From ba0b1935f1e00ad01ef4a6747b920a622197fcc1 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 2 Apr 2025 22:06:21 +0200 Subject: [PATCH 1/8] dts-bindings: video: addition of stm32 dcmipp description Addition of description for the STM32 Digital Camera Memory Interface Pixel Processor (DCMIPP). Signed-off-by: Alain Volmat --- dts/bindings/video/st,stm32-dcmipp.yaml | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 dts/bindings/video/st,stm32-dcmipp.yaml diff --git a/dts/bindings/video/st,stm32-dcmipp.yaml b/dts/bindings/video/st,stm32-dcmipp.yaml new file mode 100644 index 000000000000..125554dad72e --- /dev/null +++ b/dts/bindings/video/st,stm32-dcmipp.yaml @@ -0,0 +1,56 @@ +# +# Copyright (c) 2025 STMicroelectronics. +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: | + STM32 Digital Camera Memory Interface Pixel Processor (DCMIPP). + + Example of node configuration at board level: + + &dcmipp { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + dcmipp_ep_in: endpoint { + remote-endpoint-label = "imx335_ep_out"; + data-lanes = <1 2>; + }; + }; + }; + }; + +compatible: "st,stm32-dcmipp" + +include: [base.yaml, pinctrl-device.yaml, reset-device.yaml] + +properties: + reg: + required: true + + clocks: + required: true + + clock-names: + required: true + + interrupts: + required: true + + resets: + required: true + +child-binding: + child-binding: + child-binding: + include: video-interfaces.yaml + + properties: + bus-type: + required: true From 3ed8c2312f410817a4e1ff207f83fe7794f43fd3 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Thu, 24 Apr 2025 14:05:19 +0200 Subject: [PATCH 2/8] drivers: video: introduction of the stm32 DCMIPP driver The STM32 Digital Camera Memory Interface Pixel Processor (DCMIPP) is a multi-pipeline camera interface allowing to capture and process frames from parallel or CSI interfaces depending on its version. Signed-off-by: Alain Volmat --- drivers/video/CMakeLists.txt | 1 + drivers/video/Kconfig | 2 + drivers/video/Kconfig.stm32_dcmipp | 36 + drivers/video/video_stm32_dcmipp.c | 1321 +++++++++++++++++++ tests/drivers/build_all/video/testcase.yaml | 5 + 5 files changed, 1365 insertions(+) create mode 100644 drivers/video/Kconfig.stm32_dcmipp create mode 100644 drivers/video/video_stm32_dcmipp.c diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index 1e672a9bbfb5..ed9c5993a599 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -23,5 +23,6 @@ zephyr_library_sources_ifdef(CONFIG_VIDEO_EMUL_IMAGER video_emul_imager.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_EMUL_RX video_emul_rx.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_IMX335 imx335.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_ST_MIPID02 video_st_mipid02.c) +zephyr_library_sources_ifdef(CONFIG_VIDEO_STM32_DCMIPP video_stm32_dcmipp.c) zephyr_linker_sources(DATA_SECTIONS video.ld) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 17b251d102c8..c5d8ccca266d 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -92,4 +92,6 @@ source "drivers/video/Kconfig.imx335" source "drivers/video/Kconfig.st_mipid02" +source "drivers/video/Kconfig.stm32_dcmipp" + endif # VIDEO diff --git a/drivers/video/Kconfig.stm32_dcmipp b/drivers/video/Kconfig.stm32_dcmipp new file mode 100644 index 000000000000..329babde6e69 --- /dev/null +++ b/drivers/video/Kconfig.stm32_dcmipp @@ -0,0 +1,36 @@ +# STM32 DCMIPP driver configuration options + +# Copyright (c) 2025 STMicroelectronics. +# SPDX-License-Identifier: Apache-2.0 + +config VIDEO_STM32_DCMIPP + bool "STM32 Digital Camera Memory Interface Pixel Processor (DCMIPP) driver" + default y + depends on DT_HAS_ST_STM32_DCMIPP_ENABLED + select USE_STM32_HAL_DCMIPP + select USE_STM32_HAL_RIF if SOC_SERIES_STM32N6X + help + Enable driver for STM32 Digital Camera Memory Interface Pixel Processor + (DCMIPP) peripheral + +if VIDEO_STM32_DCMIPP + +config VIDEO_STM32_DCMIPP_SENSOR_WIDTH + int "Width of the sensor frame" + default 2592 + help + Width of the sensor video frame. + +config VIDEO_STM32_DCMIPP_SENSOR_HEIGHT + int "Height of the sensor frame" + default 1944 + help + Height of the sensor video frame. + +config VIDEO_STM32_DCMIPP_SENSOR_PIXEL_FORMAT + string "Pixel format of the sensor frame" + default "RG12" + help + Pixel format of the sensor video frame. + +endif diff --git a/drivers/video/video_stm32_dcmipp.c b/drivers/video/video_stm32_dcmipp.c new file mode 100644 index 000000000000..e04723fdb1ef --- /dev/null +++ b/drivers/video/video_stm32_dcmipp.c @@ -0,0 +1,1321 @@ +/* + * Copyright (c) 2025 STMicroelectronics. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "video_ctrls.h" +#include "video_device.h" + +#define DT_DRV_COMPAT st_stm32_dcmipp + +/* On STM32MP13X HAL, below two functions have different names */ +#if defined(CONFIG_SOC_SERIES_STM32MP13X) +#define HAL_DCMIPP_PIPE_SetConfig HAL_DCMIPP_PIPE_Config +#define HAL_DCMIPP_PARALLEL_SetConfig HAL_DCMIPP_SetParallelConfig +#endif + +#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32n6_dcmipp) +#define STM32_DCMIPP_HAS_CSI +#define STM32_DCMIPP_HAS_PIXEL_PIPES +#endif + +LOG_MODULE_REGISTER(stm32_dcmipp, CONFIG_VIDEO_LOG_LEVEL); + +typedef void (*irq_config_func_t)(const struct device *dev); + +enum stm32_dcmipp_state { + STM32_DCMIPP_STOPPED = 0, + STM32_DCMIPP_WAIT_FOR_BUFFER, + STM32_DCMIPP_RUNNING, +}; + +#define STM32_DCMIPP_MAX_PIPE_NB 3 + +#define STM32_DCMIPP_MAX_ISP_DEC_FACTOR 8 +#define STM32_DCMIPP_MAX_PIPE_DEC_FACTOR 8 +#define STM32_DCMIPP_MAX_PIPE_DSIZE_FACTOR 8 +#define STM32_DCMIPP_MAX_PIPE_SCALE_FACTOR (STM32_DCMIPP_MAX_PIPE_DEC_FACTOR * \ + STM32_DCMIPP_MAX_PIPE_DSIZE_FACTOR) +struct stm32_dcmipp_pipe_data { + const struct device *dev; + uint32_t id; + struct stm32_dcmipp_data *dcmipp; + struct k_mutex lock; + struct video_format fmt; + struct k_fifo fifo_in; + struct k_fifo fifo_out; + struct video_buffer *next; + struct video_buffer *active; + enum stm32_dcmipp_state state; + bool is_streaming; +}; + +struct stm32_dcmipp_data { + const struct device *dev; + DCMIPP_HandleTypeDef hdcmipp; + + /* FIXME - there should be a mutex to protect enabled_pipe */ + unsigned int enabled_pipe; + struct video_format source_fmt; + +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + /* Store the ISP decimation block ratio */ + int32_t isp_dec_hratio; + int32_t isp_dec_vratio; +#endif + + struct stm32_dcmipp_pipe_data *pipe[STM32_DCMIPP_MAX_PIPE_NB]; +}; + +struct stm32_dcmipp_config { + const struct stm32_pclken dcmipp_pclken; + const struct stm32_pclken dcmipp_pclken_ker; + irq_config_func_t irq_config; + const struct pinctrl_dev_config *pctrl; + const struct device *source_dev; + const struct reset_dt_spec reset_dcmipp; + int bus_type; +#if defined(STM32_DCMIPP_HAS_CSI) + const struct stm32_pclken csi_pclken; + const struct reset_dt_spec reset_csi; + struct { + uint32_t nb_lanes; + uint8_t lanes[2]; + } csi; +#endif + struct { + uint32_t vs_polarity; + uint32_t hs_polarity; + uint32_t pck_polarity; + } parallel; +}; + +#define STM32_DCMIPP_WIDTH_MIN 16 +#define STM32_DCMIPP_HEIGHT_MIN 2 +#define STM32_DCMIPP_WIDTH_MAX 4094 +#define STM32_DCMIPP_HEIGHT_MAX 4094 + +/* Callback getting called for each frame written into memory */ +void HAL_DCMIPP_PIPE_FrameEventCallback(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe) +{ + struct stm32_dcmipp_data *dcmipp = + CONTAINER_OF(hdcmipp, struct stm32_dcmipp_data, hdcmipp); + struct stm32_dcmipp_pipe_data *pipe = dcmipp->pipe[Pipe]; + uint32_t bytesused; + int ret; + + __ASSERT(pipe->active, "Unexpected behavior, active_buf must not be NULL"); + + /* Counter is only available on Pipe0 */ + if (Pipe == DCMIPP_PIPE0) { + ret = HAL_DCMIPP_PIPE_GetDataCounter(hdcmipp, Pipe, &bytesused); + if (ret != HAL_OK) { + LOG_WRN("Failed to read counter - buffer in error"); + pipe->active->bytesused = 0; + } else { + pipe->active->bytesused = bytesused; + } + } else { + pipe->active->bytesused = pipe->fmt.height * pipe->fmt.pitch; + } + + pipe->active->timestamp = k_uptime_get_32(); + pipe->active->line_offset = 0; + + k_fifo_put(&pipe->fifo_out, pipe->active); + pipe->active = NULL; +} + +/* Callback getting called for each vsync */ +void HAL_DCMIPP_PIPE_VsyncEventCallback(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe) +{ + struct stm32_dcmipp_data *dcmipp = + CONTAINER_OF(hdcmipp, struct stm32_dcmipp_data, hdcmipp); + struct stm32_dcmipp_pipe_data *pipe = dcmipp->pipe[Pipe]; + int ret; + + if (pipe->state != STM32_DCMIPP_RUNNING) { + return; + } + + pipe->active = pipe->next; + + pipe->next = k_fifo_get(&pipe->fifo_in, K_NO_WAIT); + if (pipe->next == NULL) { + LOG_DBG("No buffer available, pause streaming"); + /* + * Disable Capture Request + * Use direct register access here since we don't want to wait until + * getting CPTACT down here since we still need to handle other stuff + */ + pipe->state = STM32_DCMIPP_WAIT_FOR_BUFFER; + if (Pipe == DCMIPP_PIPE0) { + CLEAR_BIT(hdcmipp->Instance->P0FCTCR, DCMIPP_P0FCTCR_CPTREQ); + } +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + else if (Pipe == DCMIPP_PIPE1) { + CLEAR_BIT(hdcmipp->Instance->P1FCTCR, DCMIPP_P1FCTCR_CPTREQ); + } else if (Pipe == DCMIPP_PIPE2) { + CLEAR_BIT(hdcmipp->Instance->P2FCTCR, DCMIPP_P2FCTCR_CPTREQ); + } +#endif + return; + } + + /* + * TODO - we only support 1 buffer formats for the time being, setting of + * MEMORY_ADDRESS_1 and MEMORY_ADDRESS_2 required depending on the pixelformat + * for Pipe1 + */ + ret = HAL_DCMIPP_PIPE_SetMemoryAddress(&dcmipp->hdcmipp, Pipe, DCMIPP_MEMORY_ADDRESS_0, + (uint32_t)pipe->next->buffer); + if (ret != HAL_OK) { + LOG_ERR("Failed to update memory address"); + return; + } +} + +#if defined(STM32_DCMIPP_HAS_CSI) +#define INPUT_FMT(fmt, dcmipp_fmt, bpp, dt) \ + { \ + .pixelformat = VIDEO_PIX_FMT_##fmt, \ + .dcmipp_format = DCMIPP_FORMAT_##dcmipp_fmt, \ + .dcmipp_csi_bpp = DCMIPP_CSI_DT_BPP##bpp, \ + .dcmipp_csi_dt = DCMIPP_DT_##dt, \ + } +#else +#define INPUT_FMT(fmt, dcmipp_fmt, bpp, dt) \ + { \ + .pixelformat = VIDEO_PIX_FMT_##fmt, \ + .dcmipp_format = DCMIPP_FORMAT_##dcmipp_fmt, \ + } +#endif + +/* DCMIPP input format descriptions */ +static const struct stm32_dcmipp_input_fmt { + uint32_t pixelformat; + uint32_t dcmipp_format; +#if defined(STM32_DCMIPP_HAS_CSI) + uint32_t dcmipp_csi_bpp; + uint32_t dcmipp_csi_dt; +#endif +} stm32_dcmipp_input_fmt_desc[] = { + INPUT_FMT(SBGGR8, RAW8, 8, RAW8), INPUT_FMT(SGBRG8, RAW8, 8, RAW8), + INPUT_FMT(SGRBG8, RAW8, 8, RAW8), INPUT_FMT(SRGGB8, RAW8, 8, RAW8), + INPUT_FMT(SBGGR10P, RAW10, 10, RAW10), INPUT_FMT(SGBRG10P, RAW10, 10, RAW10), + INPUT_FMT(SGRBG10P, RAW10, 10, RAW10), INPUT_FMT(SRGGB10P, RAW10, 10, RAW10), + INPUT_FMT(SBGGR12P, RAW12, 12, RAW12), INPUT_FMT(SGBRG12P, RAW12, 12, RAW12), + INPUT_FMT(SGRBG12P, RAW12, 12, RAW12), INPUT_FMT(SRGGB12P, RAW12, 12, RAW12), + INPUT_FMT(SBGGR14P, RAW14, 14, RAW14), INPUT_FMT(SGBRG14P, RAW14, 14, RAW14), + INPUT_FMT(SGRBG14P, RAW14, 14, RAW14), INPUT_FMT(SRGGB14P, RAW14, 14, RAW14), + INPUT_FMT(RGB565, RGB565, 8, RGB565), + INPUT_FMT(YUYV, YUV422, 8, YUV422_8), +}; + +static const struct stm32_dcmipp_input_fmt *stm32_dcmipp_get_input_info(uint32_t pixelformat) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(stm32_dcmipp_input_fmt_desc); i++) { + if (pixelformat == stm32_dcmipp_input_fmt_desc[i].pixelformat) { + return &stm32_dcmipp_input_fmt_desc[i]; + } + } + + return NULL; +} + +static int stm32_dcmipp_conf_parallel(const struct device *dev, + const struct stm32_dcmipp_input_fmt *input_fmt) +{ + struct stm32_dcmipp_data *dcmipp = dev->data; + const struct stm32_dcmipp_config *config = dev->config; + DCMIPP_ParallelConfTypeDef parallel_cfg = { 0 }; + int ret; + + parallel_cfg.Format = input_fmt->dcmipp_format; + parallel_cfg.SwapCycles = DCMIPP_SWAPCYCLES_DISABLE; + parallel_cfg.VSPolarity = config->parallel.vs_polarity; + parallel_cfg.HSPolarity = config->parallel.hs_polarity; + parallel_cfg.PCKPolarity = config->parallel.pck_polarity; + parallel_cfg.ExtendedDataMode = DCMIPP_INTERFACE_8BITS; + parallel_cfg.SynchroMode = DCMIPP_SYNCHRO_HARDWARE; + + ret = HAL_DCMIPP_PARALLEL_SetConfig(&dcmipp->hdcmipp, ¶llel_cfg); + if (ret != HAL_OK) { + LOG_ERR("Failed to configure DCMIPP Parallel interface"); + return -EIO; + } + + return 0; +} + +#if defined(STM32_DCMIPP_HAS_CSI) +#define DCMIPP_PHY_BITRATE(val) \ + { \ + .rate = val, \ + .PHYBitrate = DCMIPP_CSI_PHY_BT_##val, \ + } +static const struct { + uint16_t rate; + uint16_t PHYBitrate; +} stm32_dcmipp_bitrate[] = { + DCMIPP_PHY_BITRATE(80), DCMIPP_PHY_BITRATE(90), DCMIPP_PHY_BITRATE(100), + DCMIPP_PHY_BITRATE(110), DCMIPP_PHY_BITRATE(120), DCMIPP_PHY_BITRATE(130), + DCMIPP_PHY_BITRATE(140), DCMIPP_PHY_BITRATE(150), DCMIPP_PHY_BITRATE(160), + DCMIPP_PHY_BITRATE(170), DCMIPP_PHY_BITRATE(180), DCMIPP_PHY_BITRATE(190), + DCMIPP_PHY_BITRATE(205), DCMIPP_PHY_BITRATE(220), DCMIPP_PHY_BITRATE(235), + DCMIPP_PHY_BITRATE(250), DCMIPP_PHY_BITRATE(275), DCMIPP_PHY_BITRATE(300), + DCMIPP_PHY_BITRATE(325), DCMIPP_PHY_BITRATE(350), DCMIPP_PHY_BITRATE(400), + DCMIPP_PHY_BITRATE(450), DCMIPP_PHY_BITRATE(500), DCMIPP_PHY_BITRATE(550), + DCMIPP_PHY_BITRATE(600), DCMIPP_PHY_BITRATE(650), DCMIPP_PHY_BITRATE(700), + DCMIPP_PHY_BITRATE(750), DCMIPP_PHY_BITRATE(800), DCMIPP_PHY_BITRATE(850), + DCMIPP_PHY_BITRATE(900), DCMIPP_PHY_BITRATE(950), DCMIPP_PHY_BITRATE(1000), + DCMIPP_PHY_BITRATE(1050), DCMIPP_PHY_BITRATE(1100), DCMIPP_PHY_BITRATE(1150), + DCMIPP_PHY_BITRATE(1200), DCMIPP_PHY_BITRATE(1250), DCMIPP_PHY_BITRATE(1300), + DCMIPP_PHY_BITRATE(1350), DCMIPP_PHY_BITRATE(1400), DCMIPP_PHY_BITRATE(1450), + DCMIPP_PHY_BITRATE(1500), DCMIPP_PHY_BITRATE(1550), DCMIPP_PHY_BITRATE(1600), + DCMIPP_PHY_BITRATE(1650), DCMIPP_PHY_BITRATE(1700), DCMIPP_PHY_BITRATE(1750), + DCMIPP_PHY_BITRATE(1800), DCMIPP_PHY_BITRATE(1850), DCMIPP_PHY_BITRATE(1900), + DCMIPP_PHY_BITRATE(1950), DCMIPP_PHY_BITRATE(2000), DCMIPP_PHY_BITRATE(2050), + DCMIPP_PHY_BITRATE(2100), DCMIPP_PHY_BITRATE(2150), DCMIPP_PHY_BITRATE(2200), + DCMIPP_PHY_BITRATE(2250), DCMIPP_PHY_BITRATE(2300), DCMIPP_PHY_BITRATE(2350), + DCMIPP_PHY_BITRATE(2400), DCMIPP_PHY_BITRATE(2450), DCMIPP_PHY_BITRATE(2500), +}; + +static int stm32_dcmipp_conf_csi(const struct device *dev, uint32_t dcmipp_csi_bpp) +{ + const struct stm32_dcmipp_config *config = dev->config; + struct stm32_dcmipp_data *dcmipp = dev->data; + DCMIPP_CSI_ConfTypeDef csiconf = { 0 }; + uint64_t phy_bitrate; + struct video_control ctrl = { + .id = VIDEO_CID_PIXEL_RATE, + }; + int err, i; + + csiconf.NumberOfLanes = config->csi.nb_lanes == 2 ? DCMIPP_CSI_TWO_DATA_LANES : + DCMIPP_CSI_ONE_DATA_LANE; + csiconf.DataLaneMapping = config->csi.lanes[0] == 1 ? DCMIPP_CSI_PHYSICAL_DATA_LANES : + DCMIPP_CSI_INVERTED_DATA_LANES; + + /* Get the pixel_rate from the source to guess the bitrate */ + err = video_get_ctrl(config->source_dev, &ctrl); + if (err < 0) { + LOG_ERR("Can not get source_dev pixel rate"); + return err; + } + phy_bitrate = ctrl.val64 * video_bits_per_pixel(dcmipp->source_fmt.pixelformat) / + (2 * config->csi.nb_lanes); + phy_bitrate /= MHZ(1); + LOG_DBG("Sensor PixelRate = %lld, PHY Bitrate computed = %lld MHz", + ctrl.val64, phy_bitrate); + + for (i = 0; i < ARRAY_SIZE(stm32_dcmipp_bitrate); i++) { + if (stm32_dcmipp_bitrate[i].rate >= phy_bitrate) { + break; + } + } + if (i == ARRAY_SIZE(stm32_dcmipp_bitrate)) { + LOG_ERR("Couldn't find proper CSI PHY settings for bitrate %lld", phy_bitrate); + return -EINVAL; + } + csiconf.PHYBitrate = stm32_dcmipp_bitrate[i].PHYBitrate; + + err = HAL_DCMIPP_CSI_SetConfig(&dcmipp->hdcmipp, &csiconf); + if (err != HAL_OK) { + LOG_ERR("Failed to configure DCMIPP CSI"); + return -EIO; + } + + /* Set Virtual Channel config */ + /* TODO - need to be able to use an alternate VC, info coming from the source */ + err = HAL_DCMIPP_CSI_SetVCConfig(&dcmipp->hdcmipp, DCMIPP_VIRTUAL_CHANNEL0, + dcmipp_csi_bpp); + if (err != HAL_OK) { + LOG_ERR("Failed to set CSI configuration"); + return -EIO; + } + + return 0; +} +#endif + +/* Description of DCMIPP inputs */ +#define DUMP_PIPE_FMT(fmt, input_fmt) \ + { \ + .pixelformat = VIDEO_PIX_FMT_##fmt, \ + .pipes = BIT(0), \ + .dump.input_pixelformat = VIDEO_PIX_FMT_##input_fmt, \ + } +#define RAW_BAYER_UNPACKED(size) \ + DUMP_PIPE_FMT(SBGGR##size, SBGGR##size), \ + DUMP_PIPE_FMT(SGBRG##size, SGBRG##size), \ + DUMP_PIPE_FMT(SGRBG##size, SGRBG##size), \ + DUMP_PIPE_FMT(SRGGB##size, SRGGB##size) + +#define RAW_BAYER_PACKED(size) \ + DUMP_PIPE_FMT(SBGGR##size##P, SBGGR##size), \ + DUMP_PIPE_FMT(SGBRG##size##P, SGBRG##size), \ + DUMP_PIPE_FMT(SGRBG##size##P, SGRBG##size), \ + DUMP_PIPE_FMT(SRGGB##size##P, SRGGB##size) + +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) +/* Description of Pixel Pipes formats */ +#define PIXEL_PIPE_FMT(fmt, dcmipp, swap, valid_pipes) \ + { \ + .pixelformat = VIDEO_PIX_FMT_##fmt, \ + .pipes = valid_pipes, \ + .pixels.dcmipp_format = DCMIPP_PIXEL_PACKER_FORMAT_##dcmipp, \ + .pixels.swap_uv = swap, \ + } +#endif +static const struct stm32_dcmipp_mapping { + uint32_t pixelformat; + uint32_t pipes; + union { + struct { + uint32_t input_pixelformat; + } dump; + struct { + uint32_t dcmipp_format; + uint32_t swap_uv; + } pixels; + }; +} stm32_dcmipp_format_support[] = { + /* Dump pipe format descriptions */ + RAW_BAYER_UNPACKED(8), + RAW_BAYER_PACKED(10), RAW_BAYER_PACKED(12), RAW_BAYER_PACKED(14), + DUMP_PIPE_FMT(RGB565, RGB565), + DUMP_PIPE_FMT(YUYV, YUYV), +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + /* Pixel pipes format descriptions */ + PIXEL_PIPE_FMT(RGB565, RGB565_1, 0, (BIT(1) | BIT(2))), + PIXEL_PIPE_FMT(YUYV, YUV422_1, 0, (BIT(1) | BIT(2))), + PIXEL_PIPE_FMT(YVYU, YUV422_1, 1, (BIT(1) | BIT(2))), + PIXEL_PIPE_FMT(GREY, MONO_Y8_G8_1, 0, (BIT(1) | BIT(2))), + PIXEL_PIPE_FMT(RGB24, RGB888_YUV444_1, 1, (BIT(1) | BIT(2))), + PIXEL_PIPE_FMT(BGR24, RGB888_YUV444_1, 0, (BIT(1) | BIT(2))), + PIXEL_PIPE_FMT(ARGB32, RGBA888, 1, (BIT(1) | BIT(2))), + PIXEL_PIPE_FMT(ABGR32, ARGB8888, 0, (BIT(1) | BIT(2))), + PIXEL_PIPE_FMT(RGBA32, ARGB8888, 1, (BIT(1) | BIT(2))), + PIXEL_PIPE_FMT(BGRA32, RGBA888, 0, (BIT(1) | BIT(2))), + /* TODO - need to add the semiplanar & planar formats */ +#endif +}; + +/* + * FIXME: Helper to know colorspace of a format + * This below to the video_common part and should be moved there + */ +#define VIDEO_COLORSPACE_RAW 0 +#define VIDEO_COLORSPACE_RGB 1 +#define VIDEO_COLORSPACE_YUV 2 +#define VIDEO_COLORSPACE(fmt) \ + (((fmt) == VIDEO_PIX_FMT_RGB565X || (fmt) == VIDEO_PIX_FMT_RGB565 || \ + (fmt) == VIDEO_PIX_FMT_BGR24 || (fmt) == VIDEO_PIX_FMT_RGB24 || \ + (fmt) == VIDEO_PIX_FMT_ARGB32 || (fmt) == VIDEO_PIX_FMT_ABGR32 || \ + (fmt) == VIDEO_PIX_FMT_RGBA32 || (fmt) == VIDEO_PIX_FMT_BGRA32 || \ + (fmt) == VIDEO_PIX_FMT_XRGB32) ? VIDEO_COLORSPACE_RGB : \ + \ + ((fmt) == VIDEO_PIX_FMT_GREY || \ + (fmt) == VIDEO_PIX_FMT_YUYV || (fmt) == VIDEO_PIX_FMT_YVYU || \ + (fmt) == VIDEO_PIX_FMT_VYUY || (fmt) == VIDEO_PIX_FMT_UYVY || \ + (fmt) == VIDEO_PIX_FMT_XYUV32) ? VIDEO_COLORSPACE_YUV : \ + \ + VIDEO_COLORSPACE_RAW) + +static const struct stm32_dcmipp_mapping *stm32_dcmipp_get_mapping(uint32_t pixelformat, + uint32_t pipe) +{ + for (int i = 0; i < ARRAY_SIZE(stm32_dcmipp_format_support); i++) { + if (pixelformat == stm32_dcmipp_format_support[i].pixelformat && + BIT(pipe) & stm32_dcmipp_format_support[i].pipes) { + return &stm32_dcmipp_format_support[i]; + } + } + + return NULL; +} + +static int stm32_dcmipp_set_fmt(const struct device *dev, struct video_format *fmt) +{ + struct stm32_dcmipp_pipe_data *pipe = dev->data; + struct stm32_dcmipp_data *dcmipp = pipe->dcmipp; + const struct stm32_dcmipp_mapping *mapping; + int ret = 0; + + /* Sanitize format given */ + mapping = stm32_dcmipp_get_mapping(fmt->pixelformat, pipe->id); + if (mapping == NULL) { + LOG_ERR("Pixelformat %s/0x%x is not supported", + VIDEO_FOURCC_TO_STR(fmt->pixelformat), fmt->pixelformat); + return -EINVAL; + } + + /* In case of DUMP pipe, verify that requested format matched with input format */ + if (pipe->id == DCMIPP_PIPE0 && + mapping->dump.input_pixelformat != dcmipp->source_fmt.pixelformat) { + LOG_ERR("Dump pipe output format (%s) incompatible with source format (%s)", + VIDEO_FOURCC_TO_STR(fmt->pixelformat), + VIDEO_FOURCC_TO_STR(dcmipp->source_fmt.pixelformat)); + return -EINVAL; + } + + if (!IN_RANGE(fmt->width, STM32_DCMIPP_WIDTH_MIN, STM32_DCMIPP_WIDTH_MAX) || + !IN_RANGE(fmt->height, STM32_DCMIPP_HEIGHT_MIN, STM32_DCMIPP_HEIGHT_MAX)) { + LOG_ERR("Format width/height (%d x %d) does not match caps", + fmt->width, fmt->height); + return -EINVAL; + } + + fmt->pitch = fmt->width * video_bits_per_pixel(fmt->pixelformat) / BITS_PER_BYTE; +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + if (pipe->id == DCMIPP_PIPE1 || pipe->id == DCMIPP_PIPE2) { + /* On Pipe1 and Pipe2, the pitch must be multiple of 16 bytes */ + fmt->pitch = ROUND_UP(fmt->pitch, 16); + } +#endif + + k_mutex_lock(&pipe->lock, K_FOREVER); + + if (pipe->is_streaming) { + ret = -EBUSY; + goto out; + } + +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + if (pipe->id == DCMIPP_PIPE1 || pipe->id == DCMIPP_PIPE2) { + uint32_t post_isp_decimate_width = dcmipp->source_fmt.width / + dcmipp->isp_dec_hratio; + uint32_t post_isp_decimate_height = dcmipp->source_fmt.height / + dcmipp->isp_dec_vratio; + + if (!IN_RANGE(fmt->width, + post_isp_decimate_width / STM32_DCMIPP_MAX_PIPE_SCALE_FACTOR, + post_isp_decimate_width) || + !IN_RANGE(fmt->height, + post_isp_decimate_height / STM32_DCMIPP_MAX_PIPE_SCALE_FACTOR, + post_isp_decimate_height)) { + LOG_ERR("Requested resolution cannot be achieved"); + ret = -EINVAL; + goto out; + } + } +#endif + + pipe->fmt = *fmt; + +out: + k_mutex_unlock(&pipe->lock); + + return ret; +} + +static int stm32_dcmipp_get_fmt(const struct device *dev, struct video_format *fmt) +{ + struct stm32_dcmipp_pipe_data *pipe = dev->data; + + *fmt = pipe->fmt; + + return 0; +} + +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) +#define STM32_DCMIPP_DSIZE_HVRATIO_CONS 8192 +#define STM32_DCMIPP_DSIZE_HVRATIO_MAX 65535 +#define STM32_DCMIPP_DSIZE_HVDIV_CONS 1024 +#define STM32_DCMIPP_DSIZE_HVDIV_MAX 1023 +static int stm32_dcmipp_set_downscale(struct stm32_dcmipp_pipe_data *pipe) +{ + struct stm32_dcmipp_data *dcmipp = pipe->dcmipp; + uint32_t post_decimate_width = dcmipp->source_fmt.width / dcmipp->isp_dec_hratio; + uint32_t post_decimate_height = dcmipp->source_fmt.height / dcmipp->isp_dec_vratio; + DCMIPP_DecimationConfTypeDef dec_cfg; + DCMIPP_DownsizeTypeDef downsize_cfg; + struct video_format *fmt = &pipe->fmt; + uint32_t hdec = 1, vdec = 1; + int ret; + + if (fmt->width == post_decimate_width && fmt->height == post_decimate_height) { + ret = HAL_DCMIPP_PIPE_DisableDecimation(&dcmipp->hdcmipp, pipe->id); + if (ret != HAL_OK) { + LOG_ERR("Failed to disable the pipe decimation"); + return -EIO; + } + + ret = HAL_DCMIPP_PIPE_DisableDownsize(&dcmipp->hdcmipp, pipe->id); + if (ret != HAL_OK) { + LOG_ERR("Failed to disable the pipe downsize"); + return -EIO; + } + + return 0; + } + + /* Compute decimation factors (HDEC/VDEC) */ + while (fmt->width * STM32_DCMIPP_MAX_PIPE_DSIZE_FACTOR < post_decimate_width) { + hdec *= 2; + post_decimate_width /= 2; + } + while (fmt->height * STM32_DCMIPP_MAX_PIPE_DSIZE_FACTOR < post_decimate_height) { + vdec *= 2; + post_decimate_height /= 2; + } + + if (hdec == 1 && vdec == 1) { + ret = HAL_DCMIPP_PIPE_DisableDecimation(&dcmipp->hdcmipp, pipe->id); + if (ret != HAL_OK) { + LOG_ERR("Failed to disable the pipe decimation"); + return -EIO; + } + } else { + /* Use same decimation factor in width / height to keep aspect ratio */ + dec_cfg.HRatio = __builtin_ctz(hdec) << DCMIPP_P1DECR_HDEC_Pos; + dec_cfg.VRatio = __builtin_ctz(vdec) << DCMIPP_P1DECR_VDEC_Pos; + + ret = HAL_DCMIPP_PIPE_SetDecimationConfig(&dcmipp->hdcmipp, pipe->id, &dec_cfg); + if (ret != HAL_OK) { + LOG_ERR("Failed to disable the pipe decimation"); + return -EIO; + } + ret = HAL_DCMIPP_PIPE_EnableDecimation(&dcmipp->hdcmipp, pipe->id); + if (ret != HAL_OK) { + LOG_ERR("Failed to enable the pipe decimation"); + return -EIO; + } + } + + /* Compute downsize factor */ + downsize_cfg.HRatio = post_decimate_width * STM32_DCMIPP_DSIZE_HVRATIO_CONS / fmt->width; + if (downsize_cfg.HRatio > STM32_DCMIPP_DSIZE_HVRATIO_MAX) { + downsize_cfg.HRatio = STM32_DCMIPP_DSIZE_HVRATIO_MAX; + } + downsize_cfg.VRatio = post_decimate_height * STM32_DCMIPP_DSIZE_HVRATIO_CONS / fmt->height; + if (downsize_cfg.VRatio > STM32_DCMIPP_DSIZE_HVRATIO_MAX) { + downsize_cfg.VRatio = STM32_DCMIPP_DSIZE_HVRATIO_MAX; + } + downsize_cfg.HDivFactor = STM32_DCMIPP_DSIZE_HVDIV_CONS * fmt->width / post_decimate_width; + if (downsize_cfg.HDivFactor > STM32_DCMIPP_DSIZE_HVDIV_MAX) { + downsize_cfg.HDivFactor = STM32_DCMIPP_DSIZE_HVDIV_MAX; + } + downsize_cfg.VDivFactor = + STM32_DCMIPP_DSIZE_HVDIV_CONS * fmt->height / post_decimate_height; + if (downsize_cfg.VDivFactor > STM32_DCMIPP_DSIZE_HVDIV_MAX) { + downsize_cfg.VDivFactor = STM32_DCMIPP_DSIZE_HVDIV_MAX; + } + downsize_cfg.HSize = fmt->width; + downsize_cfg.VSize = fmt->height; + + ret = HAL_DCMIPP_PIPE_SetDownsizeConfig(&dcmipp->hdcmipp, pipe->id, &downsize_cfg); + if (ret != HAL_OK) { + LOG_ERR("Failed to configure the pipe downsize"); + return -EIO; + } + + ret = HAL_DCMIPP_PIPE_EnableDownsize(&dcmipp->hdcmipp, pipe->id); + if (ret != HAL_OK) { + LOG_ERR("Failed to enable the pipe downsize"); + return -EIO; + } + + return 0; +} + +/* No clamping enabled */ +/* RGB Full to YUV 601 Full */ +const DCMIPP_ColorConversionConfTypeDef stm32_dcmipp_rgb_to_yuv = { + .RR = 131, .RG = -110, .RB = -21, .RA = 128, + .GR = 77, .GG = 150, .GB = 29, .GA = 0, + .BR = -44, .BG = -87, .BB = 131, .BA = 128, +}; + +/* YUV 601 Full to RGB Full */ +const DCMIPP_ColorConversionConfTypeDef stm32_dcmipp_yuv_to_rgb = { + .RR = 351, .RG = 256, .RB = 0, .RA = -175, + .GR = -179, .GG = 256, .GB = -86, .GA = 132, + .BR = 0, .BG = 256, .BB = 443, .BA = -222, +}; + +static int stm32_dcmipp_set_yuv_conversion(struct stm32_dcmipp_pipe_data *pipe, + uint32_t source_colorspace, + uint32_t output_colorspace) +{ + struct stm32_dcmipp_data *dcmipp = pipe->dcmipp; + const DCMIPP_ColorConversionConfTypeDef *cfg = NULL; + int ret; + + /* No YUV conversion on pipe 2 */ + if (pipe->id == DCMIPP_PIPE2) { + return 0; + } + + /* Perform YUV conversion if necessary and possible */ + if ((source_colorspace == VIDEO_COLORSPACE_RAW || + source_colorspace == VIDEO_COLORSPACE_RGB) && + output_colorspace == VIDEO_COLORSPACE_YUV) { + /* Need to perform RGB to YUV conversion */ + cfg = &stm32_dcmipp_rgb_to_yuv; + } else if (source_colorspace == VIDEO_COLORSPACE_YUV && + output_colorspace == VIDEO_COLORSPACE_RGB) { + /* Need to perform YUV to RGB conversion */ + cfg = &stm32_dcmipp_yuv_to_rgb; + } else { + ret = HAL_DCMIPP_PIPE_DisableYUVConversion(&dcmipp->hdcmipp, pipe->id); + if (ret != HAL_OK) { + LOG_ERR("Failed to disable YUV conversion"); + return -EIO; + } + + return 0; + } + + ret = HAL_DCMIPP_PIPE_SetYUVConversionConfig(&dcmipp->hdcmipp, pipe->id, cfg); + if (ret != HAL_OK) { + LOG_ERR("Failed to setup YUV conversion"); + return -EIO; + } + + ret = HAL_DCMIPP_PIPE_EnableYUVConversion(&dcmipp->hdcmipp, pipe->id); + if (ret != HAL_OK) { + LOG_ERR("Failed to disable YUV conversion"); + return -EIO; + } + + return 0; +} +#endif + +static int stm32_dcmipp_stream_enable(const struct device *dev) +{ + struct stm32_dcmipp_pipe_data *pipe = dev->data; + struct stm32_dcmipp_data *dcmipp = pipe->dcmipp; + const struct stm32_dcmipp_config *config = dev->config; + struct video_format *fmt = &pipe->fmt; + const struct stm32_dcmipp_mapping *mapping; + const struct stm32_dcmipp_input_fmt *input_fmt; +#if defined(STM32_DCMIPP_HAS_CSI) + DCMIPP_CSI_PIPE_ConfTypeDef csi_pipe_cfg = { 0 }; +#endif + DCMIPP_PipeConfTypeDef pipe_cfg = { 0 }; + int ret; + + k_mutex_lock(&pipe->lock, K_FOREVER); + + if (pipe->is_streaming) { + ret = -EALREADY; + goto out; + } + + input_fmt = stm32_dcmipp_get_input_info(dcmipp->source_fmt.pixelformat); + if (input_fmt == NULL) { + LOG_ERR("Unsupported input format"); + ret = -EINVAL; + goto out; + } + + /* Configure the source if nothing is already running */ + if (dcmipp->enabled_pipe == 0) { + ret = video_set_format(config->source_dev, &dcmipp->source_fmt); + if (ret < 0) { + goto out; + } + + if (config->bus_type == VIDEO_BUS_TYPE_PARALLEL) { + ret = stm32_dcmipp_conf_parallel(dcmipp->dev, input_fmt); + } +#if defined(STM32_DCMIPP_HAS_CSI) + else if (config->bus_type == VIDEO_BUS_TYPE_CSI2_DPHY) { + ret = stm32_dcmipp_conf_csi(dcmipp->dev, input_fmt->dcmipp_csi_bpp); + } +#endif + else { + LOG_ERR("Invalid bus_type"); + ret = -EINVAL; + goto out; + } + if (ret < 0) { + LOG_ERR("Failed to configure input stage"); + goto out; + } + } + + pipe->active = NULL; + pipe->next = k_fifo_get(&pipe->fifo_in, K_NO_WAIT); + if (pipe->next == NULL) { + LOG_ERR("No buffer available to start streaming"); + ret = -ENOMEM; + goto out; + } + +#if defined(STM32_DCMIPP_HAS_CSI) + /* Configure the Pipe input */ + csi_pipe_cfg.DataTypeMode = DCMIPP_DTMODE_DTIDA; + csi_pipe_cfg.DataTypeIDA = input_fmt->dcmipp_csi_dt; + ret = HAL_DCMIPP_CSI_PIPE_SetConfig(&dcmipp->hdcmipp, + pipe->id == DCMIPP_PIPE2 ? DCMIPP_PIPE1 : pipe->id, + &csi_pipe_cfg); + if (ret != HAL_OK) { + LOG_ERR("Failed to configure pipe #%d input", pipe->id); + ret = -EIO; + goto out; + } +#endif + + /* Configure Pipe */ + mapping = stm32_dcmipp_get_mapping(fmt->pixelformat, pipe->id); + if (mapping == NULL) { + LOG_ERR("Failed to find pipe format mapping"); + ret = -EIO; + goto out; + } + + pipe_cfg.FrameRate = DCMIPP_FRAME_RATE_ALL; +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + if (pipe->id == DCMIPP_PIPE1 || pipe->id == DCMIPP_PIPE2) { + pipe_cfg.PixelPipePitch = fmt->pitch; + pipe_cfg.PixelPackerFormat = mapping->pixels.dcmipp_format; + } +#endif + ret = HAL_DCMIPP_PIPE_SetConfig(&dcmipp->hdcmipp, pipe->id, &pipe_cfg); + if (ret != HAL_OK) { + LOG_ERR("Failed to configure pipe #%d", pipe->id); + ret = -EIO; + goto out; + } + + if (pipe->id == DCMIPP_PIPE0) { + /* Only the PIPE0 has a limiter */ + /* Set Limiter to avoid buffer overflow, in number of 32 bits words */ + ret = HAL_DCMIPP_PIPE_EnableLimitEvent(&dcmipp->hdcmipp, DCMIPP_PIPE0, + (fmt->pitch * fmt->height) / 4); + if (ret != HAL_OK) { + LOG_ERR("Failed to set limiter"); + ret = -EIO; + goto out; + } + } +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + else if (pipe->id == DCMIPP_PIPE1 || pipe->id == DCMIPP_PIPE2) { + uint32_t source_colorspace = VIDEO_COLORSPACE(dcmipp->source_fmt.pixelformat); + uint32_t output_colorspace = VIDEO_COLORSPACE(fmt->pixelformat); + + /* Enable / disable SWAPRB if necessary */ + if (mapping->pixels.swap_uv) { + ret = HAL_DCMIPP_PIPE_EnableRedBlueSwap(&dcmipp->hdcmipp, pipe->id); + if (ret != HAL_OK) { + LOG_ERR("Failed to enable Red-Blue swap"); + ret = -EIO; + goto out; + } + } else { + ret = HAL_DCMIPP_PIPE_DisableRedBlueSwap(&dcmipp->hdcmipp, pipe->id); + if (ret != HAL_OK) { + LOG_ERR("Failed to disable Red-Blue swap"); + ret = -EIO; + goto out; + } + } + + if (source_colorspace == VIDEO_COLORSPACE_RAW) { + /* Enable demosaicing if input format is Bayer */ + ret = HAL_DCMIPP_PIPE_EnableISPRawBayer2RGB(&dcmipp->hdcmipp, DCMIPP_PIPE1); + if (ret != HAL_OK) { + LOG_ERR("Failed to enable demosaicing"); + ret = -EIO; + goto out; + } + } else { + /* Disable demosaicing */ + ret = HAL_DCMIPP_PIPE_DisableISPRawBayer2RGB(&dcmipp->hdcmipp, + DCMIPP_PIPE1); + if (ret != HAL_OK) { + LOG_ERR("Failed to disable demosaicing"); + ret = -EIO; + goto out; + } + } + + /* Configure the Pipe decimation / downsize */ + ret = stm32_dcmipp_set_downscale(pipe); + if (ret < 0) { + goto out; + } + + /* Configure YUV conversion */ + ret = stm32_dcmipp_set_yuv_conversion(pipe, source_colorspace, output_colorspace); + if (ret < 0) { + goto out; + } + } +#endif + + /* Enable the DCMIPP Pipeline */ + if (config->bus_type == VIDEO_BUS_TYPE_PARALLEL) { + ret = HAL_DCMIPP_PIPE_Start(&dcmipp->hdcmipp, pipe->id, + (uint32_t)pipe->next->buffer, DCMIPP_MODE_CONTINUOUS); + } +#if defined(STM32_DCMIPP_HAS_CSI) + else if (config->bus_type == VIDEO_BUS_TYPE_CSI2_DPHY) { + ret = HAL_DCMIPP_CSI_PIPE_Start(&dcmipp->hdcmipp, pipe->id, DCMIPP_VIRTUAL_CHANNEL0, + (uint32_t)pipe->next->buffer, + DCMIPP_MODE_CONTINUOUS); + } +#endif + else { + LOG_ERR("Invalid bus_type"); + ret = -EINVAL; + goto out; + } + if (ret != HAL_OK) { + LOG_ERR("Failed to start the pipeline"); + ret = -EIO; + goto out; + } + + /* It is necessary to start the source as well if nothing else is running */ + if (dcmipp->enabled_pipe == 0) { + ret = video_stream_start(config->source_dev, VIDEO_BUF_TYPE_OUTPUT); + if (ret < 0) { + LOG_ERR("Failed to start the source"); + if (config->bus_type == VIDEO_BUS_TYPE_PARALLEL) { + HAL_DCMIPP_PIPE_Stop(&dcmipp->hdcmipp, pipe->id); + } +#if defined(STM32_DCMIPP_HAS_CSI) + else if (config->bus_type == VIDEO_BUS_TYPE_CSI2_DPHY) { + HAL_DCMIPP_CSI_PIPE_Stop(&dcmipp->hdcmipp, pipe->id, + DCMIPP_VIRTUAL_CHANNEL0); + } +#endif + else { + LOG_ERR("Invalid bus_type"); + } + ret = -EIO; + goto out; + } + } + + pipe->state = STM32_DCMIPP_RUNNING; + pipe->is_streaming = true; + dcmipp->enabled_pipe++; + +out: + k_mutex_unlock(&pipe->lock); + + return ret; +} + +static int stm32_dcmipp_stream_disable(const struct device *dev) +{ + struct stm32_dcmipp_pipe_data *pipe = dev->data; + struct stm32_dcmipp_data *dcmipp = pipe->dcmipp; + const struct stm32_dcmipp_config *config = dev->config; + int ret; + + k_mutex_lock(&pipe->lock, K_FOREVER); + + if (!pipe->is_streaming) { + ret = -EINVAL; + goto out; + } + + /* Disable the DCMIPP Pipeline */ + if (config->bus_type == VIDEO_BUS_TYPE_PARALLEL) { + ret = HAL_DCMIPP_PIPE_Stop(&dcmipp->hdcmipp, pipe->id); + } +#if defined(STM32_DCMIPP_HAS_CSI) + else if (config->bus_type == VIDEO_BUS_TYPE_CSI2_DPHY) { + ret = HAL_DCMIPP_CSI_PIPE_Stop(&dcmipp->hdcmipp, pipe->id, DCMIPP_VIRTUAL_CHANNEL0); + } +#endif + else { + LOG_ERR("Invalid bus_type"); + ret = -EIO; + goto out; + } + if (ret != HAL_OK) { + LOG_ERR("Failed to stop the pipeline"); + ret = -EIO; + goto out; + } + + /* Turn off the source nobody else is using the DCMIPP */ + if (dcmipp->enabled_pipe == 1) { + ret = video_stream_stop(config->source_dev, VIDEO_BUF_TYPE_OUTPUT); + if (ret < 0) { + LOG_ERR("Failed to stop the source"); + ret = -EIO; + goto out; + } + } + + /* Release the video buffer allocated when start streaming */ + if (pipe->next != NULL) { + k_fifo_put(&pipe->fifo_in, pipe->next); + } + if (pipe->active != NULL) { + k_fifo_put(&pipe->fifo_in, pipe->active); + } + + pipe->state = STM32_DCMIPP_STOPPED; + pipe->is_streaming = false; + dcmipp->enabled_pipe--; + +out: + k_mutex_unlock(&pipe->lock); + + return ret; +} + +static int stm32_dcmipp_set_stream(const struct device *dev, bool enable, enum video_buf_type type) +{ + return enable ? stm32_dcmipp_stream_enable(dev) : stm32_dcmipp_stream_disable(dev); +} + +static int stm32_dcmipp_enqueue(const struct device *dev, struct video_buffer *vbuf) +{ + struct stm32_dcmipp_pipe_data *pipe = dev->data; + struct stm32_dcmipp_data *dcmipp = pipe->dcmipp; + int ret; + + k_mutex_lock(&pipe->lock, K_FOREVER); + + if (pipe->fmt.pitch * pipe->fmt.height > vbuf->size) { + return -EINVAL; + } + + if (pipe->state == STM32_DCMIPP_WAIT_FOR_BUFFER) { + LOG_DBG("Restart CPTREQ after wait for buffer"); + pipe->next = vbuf; + ret = HAL_DCMIPP_PIPE_SetMemoryAddress(&dcmipp->hdcmipp, pipe->id, + DCMIPP_MEMORY_ADDRESS_0, + (uint32_t)pipe->next->buffer); + if (ret != HAL_OK) { + LOG_ERR("Failed to update memory address"); + return -EIO; + } + if (pipe->id == DCMIPP_PIPE0) { + SET_BIT(dcmipp->hdcmipp.Instance->P0FCTCR, DCMIPP_P0FCTCR_CPTREQ); + } +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + else if (pipe->id == DCMIPP_PIPE1) { + SET_BIT(dcmipp->hdcmipp.Instance->P1FCTCR, DCMIPP_P1FCTCR_CPTREQ); + } else if (pipe->id == DCMIPP_PIPE2) { + SET_BIT(dcmipp->hdcmipp.Instance->P2FCTCR, DCMIPP_P2FCTCR_CPTREQ); + } +#endif + pipe->state = STM32_DCMIPP_RUNNING; + } else { + k_fifo_put(&pipe->fifo_in, vbuf); + } + + k_mutex_unlock(&pipe->lock); + + return 0; +} + +static int stm32_dcmipp_dequeue(const struct device *dev, struct video_buffer **vbuf, + k_timeout_t timeout) +{ + struct stm32_dcmipp_pipe_data *pipe = dev->data; + + *vbuf = k_fifo_get(&pipe->fifo_out, timeout); + if (*vbuf == NULL) { + return -EAGAIN; + } + + return 0; +} + +/* + * TODO: caps aren't yet handled hence give back straight the caps given by the + * source. Normally this should be the intersection of what the source produces + * vs what the DCMIPP can input (for pipe0) and, for pipe 1 and 2, for a given + * input format, generate caps based on capabilities, color conversion, decimation + * etc + */ +static int stm32_dcmipp_get_caps(const struct device *dev, struct video_caps *caps) +{ + const struct stm32_dcmipp_config *config = dev->config; + int ret; + + ret = video_get_caps(config->source_dev, caps); + + caps->min_vbuf_count = 1; + caps->min_line_count = LINE_COUNT_HEIGHT; + caps->max_line_count = LINE_COUNT_HEIGHT; + + return ret; +} + +static int stm32_dcmipp_get_frmival(const struct device *dev, struct video_frmival *frmival) +{ + const struct stm32_dcmipp_config *config = dev->config; + + return video_get_frmival(config->source_dev, frmival); +} + +static int stm32_dcmipp_set_frmival(const struct device *dev, struct video_frmival *frmival) +{ + const struct stm32_dcmipp_config *config = dev->config; + + return video_set_frmival(config->source_dev, frmival); +} + +static int stm32_dcmipp_enum_frmival(const struct device *dev, struct video_frmival_enum *fie) +{ + const struct stm32_dcmipp_config *config = dev->config; + + return video_enum_frmival(config->source_dev, fie); +} + +static DEVICE_API(video, stm32_dcmipp_driver_api) = { + .set_format = stm32_dcmipp_set_fmt, + .get_format = stm32_dcmipp_get_fmt, + .set_stream = stm32_dcmipp_set_stream, + .enqueue = stm32_dcmipp_enqueue, + .dequeue = stm32_dcmipp_dequeue, + .get_caps = stm32_dcmipp_get_caps, + .get_frmival = stm32_dcmipp_get_frmival, + .set_frmival = stm32_dcmipp_set_frmival, + .enum_frmival = stm32_dcmipp_enum_frmival, +}; + +static int stm32_dcmipp_enable_clock(const struct device *dev) +{ + const struct stm32_dcmipp_config *config = dev->config; + const struct device *cc_node = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE); + int err; + + if (!device_is_ready(cc_node)) { + LOG_ERR("clock control device not ready"); + return -ENODEV; + } + + /* Turn on DCMIPP peripheral clock */ + err = clock_control_configure(cc_node, (clock_control_subsys_t)&config->dcmipp_pclken_ker, + NULL); + if (err < 0) { + LOG_ERR("Failed to configure DCMIPP clock. Error %d", err); + return err; + } + + err = clock_control_on(cc_node, (clock_control_subsys_t *)&config->dcmipp_pclken); + if (err < 0) { + LOG_ERR("Failed to enable DCMIPP clock. Error %d", err); + return err; + } + +#if defined(STM32_DCMIPP_HAS_CSI) + /* Turn on CSI peripheral clock */ + err = clock_control_on(cc_node, (clock_control_subsys_t *)&config->csi_pclken); + if (err < 0) { + LOG_ERR("Failed to enable CSI clock. Error %d", err); + return err; + } +#endif + + return 0; +} + +static int stm32_dcmipp_init(const struct device *dev) +{ + const struct stm32_dcmipp_config *cfg = dev->config; + struct stm32_dcmipp_data *dcmipp = dev->data; + + int err; + +#if defined(CONFIG_SOC_SERIES_STM32N6X) + RIMC_MasterConfig_t rimc = {0}; +#endif + + dcmipp->enabled_pipe = 0; + +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + dcmipp->isp_dec_hratio = 1; + dcmipp->isp_dec_vratio = 1; +#endif + + /* Configure DT provided pins */ + if (cfg->bus_type == VIDEO_BUS_TYPE_PARALLEL) { + err = pinctrl_apply_state(cfg->pctrl, PINCTRL_STATE_DEFAULT); + if (err < 0) { + LOG_ERR("pinctrl setup failed. Error %d.", err); + return err; + } + } + + /* Enable DCMIPP / CSI clocks */ + err = stm32_dcmipp_enable_clock(dev); + if (err < 0) { + LOG_ERR("Clock enabling failed."); + return err; + } + + /* Reset DCMIPP & CSI */ + if (!device_is_ready(cfg->reset_dcmipp.dev)) { + LOG_ERR("reset controller not ready"); + return -ENODEV; + } + reset_line_toggle_dt(&cfg->reset_dcmipp); +#if defined(STM32_DCMIPP_HAS_CSI) + reset_line_toggle_dt(&cfg->reset_csi); +#endif + + dcmipp->dev = dev; + + /* Run IRQ init */ + cfg->irq_config(dev); + +#if defined(CONFIG_SOC_SERIES_STM32N6X) + rimc.MasterCID = RIF_CID_1; + rimc.SecPriv = RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV; + HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_DCMIPP, &rimc); + HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_DCMIPP, + RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); +#endif + + /* Initialize DCMI peripheral */ + err = HAL_DCMIPP_Init(&dcmipp->hdcmipp); + if (err != HAL_OK) { + LOG_ERR("DCMIPP initialization failed."); + return -EIO; + } + + LOG_DBG("%s initialized", dev->name); + + return 0; +} + +static int stm32_dcmipp_pipe_init(const struct device *dev) +{ + struct stm32_dcmipp_pipe_data *pipe = dev->data; + struct stm32_dcmipp_data *dcmipp = pipe->dcmipp; + + k_mutex_init(&pipe->lock); + k_fifo_init(&pipe->fifo_in); + k_fifo_init(&pipe->fifo_out); + + /* TODO - need to init formats to valid values */ + + /* Store the pipe data pointer into dcmipp data structure */ + dcmipp->pipe[pipe->id] = pipe; + + return 0; +} + +static void stm32_dcmipp_isr(const struct device *dev) +{ + struct stm32_dcmipp_data *dcmipp = dev->data; + + HAL_DCMIPP_IRQHandler(&dcmipp->hdcmipp); +} + +#define DCMIPP_PIPE_INIT_DEFINE(node_id, inst) \ + static struct stm32_dcmipp_pipe_data stm32_dcmipp_pipe_##node_id = { \ + .id = DT_NODE_CHILD_IDX(node_id), \ + .dcmipp = &stm32_dcmipp_data_##inst, \ + }; \ + \ + DEVICE_DT_DEFINE(node_id, &stm32_dcmipp_pipe_init, NULL, \ + &stm32_dcmipp_pipe_##node_id, \ + &stm32_dcmipp_config_##inst, \ + POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \ + &stm32_dcmipp_driver_api); + +#define SOURCE_DEV(inst) DEVICE_DT_GET(DT_NODE_REMOTE_DEVICE(DT_INST_ENDPOINT_BY_ID(inst, 0, 0))) + +#if defined(STM32_DCMIPP_HAS_CSI) +#define STM32_DCMIPP_CSI_DT_PARAMS(inst) \ + .csi_pclken = \ + {.bus = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(inst), csi, bus), \ + .enr = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(inst), csi, bits)}, \ + .reset_csi = RESET_DT_SPEC_INST_GET_BY_IDX(inst, 1), \ + .csi.nb_lanes = DT_PROP_LEN(DT_INST_ENDPOINT_BY_ID(inst, 0, 0), data_lanes), \ + .csi.lanes[0] = DT_PROP_BY_IDX(DT_INST_ENDPOINT_BY_ID(inst, 0, 0), \ + data_lanes, 0), \ + .csi.lanes[1] = COND_CODE_1(DT_PROP_LEN(DT_INST_ENDPOINT_BY_ID(inst, 0, 0), \ + data_lanes), \ + (0), \ + (DT_PROP_BY_IDX(DT_INST_ENDPOINT_BY_ID(inst, 0, 0), \ + data_lanes, 1))), +#else +#define STM32_DCMIPP_CSI_DT_PARAMS(inst) +#endif + +#define STM32_DCMIPP_INIT(inst) \ + static void stm32_dcmipp_irq_config_##inst(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \ + stm32_dcmipp_isr, DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQN(inst)); \ + } \ + \ + static struct stm32_dcmipp_data stm32_dcmipp_data_##inst = { \ + .hdcmipp = { \ + .Instance = (DCMIPP_TypeDef *)DT_INST_REG_ADDR(inst), \ + }, \ + .source_fmt = { \ + .pixelformat = \ + VIDEO_FOURCC_FROM_STR( \ + CONFIG_VIDEO_STM32_DCMIPP_SENSOR_PIXEL_FORMAT), \ + .width = CONFIG_VIDEO_STM32_DCMIPP_SENSOR_WIDTH, \ + .height = CONFIG_VIDEO_STM32_DCMIPP_SENSOR_HEIGHT, \ + }, \ + }; \ + \ + PINCTRL_DT_INST_DEFINE(inst); \ + \ + static const struct stm32_dcmipp_config stm32_dcmipp_config_##inst = { \ + .dcmipp_pclken = \ + {.bus = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(inst), dcmipp, bus), \ + .enr = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(inst), dcmipp, bits)}, \ + .dcmipp_pclken_ker = \ + {.bus = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(inst), dcmipp_ker, bus), \ + .enr = DT_CLOCKS_CELL_BY_NAME(DT_DRV_INST(inst), dcmipp_ker, bits)}, \ + .irq_config = stm32_dcmipp_irq_config_##inst, \ + .pctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .source_dev = SOURCE_DEV(inst), \ + .reset_dcmipp = RESET_DT_SPEC_INST_GET_BY_IDX(inst, 0), \ + .bus_type = DT_PROP_OR(DT_INST_ENDPOINT_BY_ID(inst, 0, 0), bus_type, \ + VIDEO_BUS_TYPE_PARALLEL), \ + STM32_DCMIPP_CSI_DT_PARAMS(inst) \ + .parallel.vs_polarity = DT_PROP_OR(DT_INST_ENDPOINT_BY_ID(inst, 0, 0), \ + vsync_active, 0) ? \ + DCMIPP_VSPOLARITY_HIGH : \ + DCMIPP_VSPOLARITY_LOW, \ + .parallel.hs_polarity = DT_PROP_OR(DT_INST_ENDPOINT_BY_ID(n, 0, 0), \ + hsync_active, 0) ? \ + DCMIPP_HSPOLARITY_HIGH : \ + DCMIPP_HSPOLARITY_LOW, \ + .parallel.pck_polarity = DT_PROP_OR(DT_INST_ENDPOINT_BY_ID(inst, 0, 0), \ + pclk_sample, 0) ? \ + DCMIPP_PCKPOLARITY_RISING : \ + DCMIPP_PCKPOLARITY_FALLING, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, &stm32_dcmipp_init, \ + NULL, &stm32_dcmipp_data_##inst, \ + &stm32_dcmipp_config_##inst, \ + POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \ + NULL); \ + \ + DT_FOREACH_CHILD_VARGS(DT_INST_PORT_BY_ID(inst, 1), DCMIPP_PIPE_INIT_DEFINE, inst); \ + \ + VIDEO_DEVICE_DEFINE(dcmipp_##inst, DEVICE_DT_INST_GET(inst), SOURCE_DEV(inst)); + +DT_INST_FOREACH_STATUS_OKAY(STM32_DCMIPP_INIT) diff --git a/tests/drivers/build_all/video/testcase.yaml b/tests/drivers/build_all/video/testcase.yaml index 519966a846a3..c7a5e07161ec 100644 --- a/tests/drivers/build_all/video/testcase.yaml +++ b/tests/drivers/build_all/video/testcase.yaml @@ -31,3 +31,8 @@ tests: drivers.video.esp32_dvp.build: platform_allow: - esp32s3_eye/esp32s3/procpu + drivers.video.stm32_dcmipp.build: + platform_allow: + - stm32n6570_dk/stm32n657xx/sb + extra_args: + - platform:stm32n6570_dk/stm32n657xx/sb:SHIELD=st_b_cams_imx_mb1854 From 7f2803b12b6adc60be27802bd35b167abd28ef1a Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Tue, 13 May 2025 14:07:55 +0200 Subject: [PATCH 3/8] drivers: video: dcmipp: add functions for external ISP functions usage Add weak functions and their call within the dcmipp driver so that externally provided ISP control functions can be called by the driver at right timing in order to perform the control of the ISP part of the DCMIPP. Signed-off-by: Alain Volmat --- drivers/video/video_stm32_dcmipp.c | 100 ++++++++++++++++++++++++++++ include/zephyr/video/stm32_dcmipp.h | 16 +++++ 2 files changed, 116 insertions(+) create mode 100644 include/zephyr/video/stm32_dcmipp.h diff --git a/drivers/video/video_stm32_dcmipp.c b/drivers/video/video_stm32_dcmipp.c index e04723fdb1ef..37d63fc46b5c 100644 --- a/drivers/video/video_stm32_dcmipp.c +++ b/drivers/video/video_stm32_dcmipp.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "video_ctrls.h" #include "video_device.h" @@ -33,6 +34,26 @@ #define STM32_DCMIPP_HAS_PIXEL_PIPES #endif +/* Weak function declaration in order to interface with external ISP handler */ +void __weak stm32_dcmipp_isp_vsync_update(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe) +{ +} + +int __weak stm32_dcmipp_isp_init(DCMIPP_HandleTypeDef *hdcmipp, const struct device *source) +{ + return 0; +} + +int __weak stm32_dcmipp_isp_start(void) +{ + return 0; +} + +int __weak stm32_dcmipp_isp_stop(void) +{ + return 0; +} + LOG_MODULE_REGISTER(stm32_dcmipp, CONFIG_VIDEO_LOG_LEVEL); typedef void (*irq_config_func_t)(const struct device *dev); @@ -148,6 +169,12 @@ void HAL_DCMIPP_PIPE_VsyncEventCallback(DCMIPP_HandleTypeDef *hdcmipp, uint32_t struct stm32_dcmipp_pipe_data *pipe = dcmipp->pipe[Pipe]; int ret; + /* + * Let the external ISP handler know that a VSYNC happened a new statistics are + * thus available + */ + stm32_dcmipp_isp_vsync_update(hdcmipp, Pipe); + if (pipe->state != STM32_DCMIPP_RUNNING) { return; } @@ -526,9 +553,64 @@ static int stm32_dcmipp_set_fmt(const struct device *dev, struct video_format *f return ret; } +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) +static void stm32_dcmipp_get_isp_decimation(struct stm32_dcmipp_data *dcmipp) +{ + DCMIPP_DecimationConfTypeDef ispdec_cfg; + uint32_t is_enabled; + + is_enabled = HAL_DCMIPP_PIPE_IsEnabledISPDecimation(&dcmipp->hdcmipp, DCMIPP_PIPE1); + if (is_enabled == 0) { + dcmipp->isp_dec_hratio = 1; + dcmipp->isp_dec_vratio = 1; + } else { + HAL_DCMIPP_PIPE_GetISPDecimationConfig(&dcmipp->hdcmipp, DCMIPP_PIPE1, &ispdec_cfg); + dcmipp->isp_dec_hratio = 1 << (ispdec_cfg.HRatio >> DCMIPP_P1DECR_HDEC_Pos); + dcmipp->isp_dec_vratio = 1 << (ispdec_cfg.VRatio >> DCMIPP_P1DECR_VDEC_Pos); + } +} +#endif + static int stm32_dcmipp_get_fmt(const struct device *dev, struct video_format *fmt) { struct stm32_dcmipp_pipe_data *pipe = dev->data; +#if defined(STM32_DCMIPP_HAS_PIXEL_PIPES) + struct stm32_dcmipp_data *dcmipp = pipe->dcmipp; + const struct stm32_dcmipp_config *config = dev->config; + static atomic_t isp_init_once; + int ret; + + /* Initialize the external ISP handling stack */ + /* + * TODO - this is not the right place to do that, however we need to know + * the source format before calling the isp_init handler hence can't + * do that within the stm32_dcmipp_init function due to unknown + * driver initialization order + * + * Would need an ops that get called when both side of an endpoint get + * initiialized + */ + if (atomic_cas(&isp_init_once, 0, 1) && + (pipe->id == DCMIPP_PIPE1 || pipe->id == DCMIPP_PIPE2)) { + /* + * It is necessary to perform a dummy configuration here otherwise any + * ISP related configuration done by the stm32_dcmipp_isp_init will + * fail due to the HAL DCMIPP driver not being in READY state + */ + ret = stm32_dcmipp_conf_parallel(dcmipp->dev, &stm32_dcmipp_input_fmt_desc[0]); + if (ret < 0) { + LOG_ERR("Failed to perform dummy parallel configuration"); + return ret; + } + + ret = stm32_dcmipp_isp_init(&dcmipp->hdcmipp, config->source_dev); + if (ret < 0) { + LOG_ERR("Failed to initialize the ISP"); + return ret; + } + stm32_dcmipp_get_isp_decimation(dcmipp); + } +#endif *fmt = pipe->fmt; @@ -864,6 +946,12 @@ static int stm32_dcmipp_stream_enable(const struct device *dev) } #endif + /* Initialize the external ISP handling stack */ + ret = stm32_dcmipp_isp_init(&dcmipp->hdcmipp, config->source_dev); + if (ret < 0) { + goto out; + } + /* Enable the DCMIPP Pipeline */ if (config->bus_type == VIDEO_BUS_TYPE_PARALLEL) { ret = HAL_DCMIPP_PIPE_Start(&dcmipp->hdcmipp, pipe->id, @@ -909,6 +997,12 @@ static int stm32_dcmipp_stream_enable(const struct device *dev) } } + /* Start the external ISP handling */ + ret = stm32_dcmipp_isp_start(); + if (ret < 0) { + goto out; + } + pipe->state = STM32_DCMIPP_RUNNING; pipe->is_streaming = true; dcmipp->enabled_pipe++; @@ -933,6 +1027,12 @@ static int stm32_dcmipp_stream_disable(const struct device *dev) goto out; } + /* Stop the external ISP handling */ + ret = stm32_dcmipp_isp_stop(); + if (ret < 0) { + goto out; + } + /* Disable the DCMIPP Pipeline */ if (config->bus_type == VIDEO_BUS_TYPE_PARALLEL) { ret = HAL_DCMIPP_PIPE_Stop(&dcmipp->hdcmipp, pipe->id); diff --git a/include/zephyr/video/stm32_dcmipp.h b/include/zephyr/video/stm32_dcmipp.h new file mode 100644 index 000000000000..f8c4d713b766 --- /dev/null +++ b/include/zephyr/video/stm32_dcmipp.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2025 STMicroelectronics. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_VIDEO_STM32_DCMIPP_H_ +#define ZEPHYR_INCLUDE_VIDEO_STM32_DCMIPP_H_ + +/* Prototypes of ISP external handler weak functions */ +void stm32_dcmipp_isp_vsync_update(DCMIPP_HandleTypeDef *hdcmipp, uint32_t Pipe); +int stm32_dcmipp_isp_init(DCMIPP_HandleTypeDef *hdcmipp, const struct device *source); +int stm32_dcmipp_isp_start(void); +int stm32_dcmipp_isp_stop(void); + +#endif /* ZEPHYR_INCLUDE_VIDEO_STM32_DCMIPP_H_ */ From 94cfcbfd70446b4de144d8edc760f1137ef0478c Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Wed, 2 Apr 2025 22:25:38 +0200 Subject: [PATCH 4/8] dts: arm: st: n6: add dcmipp node Add node describing the dcmipp in stm32n6.dtsi Signed-off-by: Alain Volmat --- dts/arm/st/n6/stm32n6.dtsi | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/dts/arm/st/n6/stm32n6.dtsi b/dts/arm/st/n6/stm32n6.dtsi index e779082794c9..16e2acb6ed24 100644 --- a/dts/arm/st/n6/stm32n6.dtsi +++ b/dts/arm/st/n6/stm32n6.dtsi @@ -15,6 +15,7 @@ #include #include #include +#include #include / { @@ -643,6 +644,55 @@ status = "disabled"; }; + dcmipp: dcmipp@58002000 { + compatible = "st,stm32n6-dcmipp", "st,stm32-dcmipp"; + reg = <0x58002000 0x1000>; + clock-names = "dcmipp", "dcmipp-ker", "csi"; + clocks = <&rcc STM32_CLOCK(APB5, 2)>, + <&rcc STM32_SRC_IC17 DCMIPP_SEL(2)>, + <&rcc STM32_CLOCK(APB5, 6)>; + interrupts = <48 0>; + resets = <&rctl STM32_RESET(APB5, 2)>, + <&rctl STM32_RESET(APB5, 6)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + endpoint { + remote-endpoint-label = ""; + bus-type = ; + }; + }; + + port@1 { + #address-cells = <1>; + #size-cells = <0>; + + reg = <1>; + + dcmipp_pipe_dump: endpoint@0 { + compatible = "st,stm32-dcmipp-pipe"; + reg = <0>; + }; + + dcmipp_pipe_main: endpoint@1 { + compatible = "st,stm32-dcmipp-pipe"; + reg = <1>; + }; + + dcmipp_pipe_aux: endpoint@2 { + compatible = "st,stm32-dcmipp-pipe"; + reg = <2>; + }; + }; + }; + }; + ethernet@58036000 { reg = <0x58036000 0x8000>; compatible = "st,stm32-ethernet-controller"; From 437d853a911a9c76ce08254b82edbe15bd44d2ce Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Tue, 8 Apr 2025 10:25:41 +0200 Subject: [PATCH 5/8] boards: st: stm32n6570_dk: Add IC17/IC18 settings for DCMIPP/CSI Add configuration of the IC17 and IC18 clock dividers, used by the dcmipp and csi IPs in the stm32n6. Signed-off-by: Alain Volmat --- boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi b/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi index 0d65e4431ed6..0097db0b6f41 100644 --- a/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi +++ b/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi @@ -160,6 +160,18 @@ status = "okay"; }; +&ic17 { + pll-src = <1>; + ic-div = <4>; + status = "okay"; +}; + +&ic18 { + pll-src = <1>; + ic-div = <60>; + status = "okay"; +}; + &perck { clocks = <&rcc STM32_SRC_HSI PER_SEL(0)>; status = "okay"; From b7c02d4d5f7344c6c7d22f6682eb744bba19606f Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Tue, 8 Apr 2025 07:43:35 +0200 Subject: [PATCH 6/8] boards: st: stm32n6570_dk: csi_22pins connector related labels Add csi_22pins_connector and related label in order to use shields relying on csi_pins_connector. Signed-off-by: Alain Volmat --- .../stm32n6570_dk/stm32n6570_dk_common.dtsi | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi b/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi index 0097db0b6f41..1b22ee0db8dc 100644 --- a/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi +++ b/boards/st/stm32n6570_dk/stm32n6570_dk_common.dtsi @@ -8,6 +8,7 @@ #include #include "zephyr/dt-bindings/display/panel.h" #include +#include #include #include #include "arduino_r3_connector.dtsi" @@ -54,6 +55,15 @@ label = "User LD2"; }; }; + + csi_22pins_connector: connector_csi_22pins { + compatible = "raspberrypi,csi-22pins-connector"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0 0x3f>; + gpio-map = , + ; + }; }; &i2c2 { @@ -208,7 +218,7 @@ status = "okay"; }; -&i2c1 { +csi_22pins_i2c: &i2c1 { clocks = <&rcc STM32_CLOCK(APB1, 21)>, <&rcc STM32_SRC_CKPER I2C1_SEL(1)>; pinctrl-0 = <&i2c1_scl_ph9 &i2c1_sda_pc1>; @@ -405,3 +415,15 @@ zephyr_udc0: &usbotg_hs1 { def-back-color-green = <0xFF>; def-back-color-blue = <0xFF>; }; + +csi_22pins_interface: &dcmipp { + ports { + port@0 { + csi_22pins_ep_in: endpoint { }; + }; + + port@1 { + csi_22pins_capture_port: endpoint@1 { }; + }; + }; +}; From 9b4e507888d18cad7ff5e529032f770c1bfe5520 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Sun, 4 May 2025 21:40:31 +0200 Subject: [PATCH 7/8] dts: arm: st: mp13: add dcmipp node in stm32mp135.dtsi Add node describing the DCMIPP available on stm32mp135. Signed-off-by: Alain Volmat --- dts/arm/st/mp13/stm32mp135.dtsi | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/dts/arm/st/mp13/stm32mp135.dtsi b/dts/arm/st/mp13/stm32mp135.dtsi index fddcc419f2d7..61a92c628164 100644 --- a/dts/arm/st/mp13/stm32mp135.dtsi +++ b/dts/arm/st/mp13/stm32mp135.dtsi @@ -5,11 +5,45 @@ */ #include +#include / { soc { compatible = "st,stm32mp135", "st,stm32mp13", "simple-bus"; + dcmipp: dcmipp@5a000000 { + compatible = "st,stm32mp13-dcmipp", "st,stm32-dcmipp"; + reg = <0x5a000000 0x400>; + interrupts = ; + clocks = <&rcc STM32_CLOCK(APB4, 1)>, + <&rcc STM32_SRC_PLL2_Q DCMIPP_SEL(1)>; + clock-names = "dcmipp", "dcmipp-ker"; + resets = <&rctl STM32_RESET(APB4, 1)>; + status = "disabled"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + + endpoint { + remote-endpoint-label = ""; + bus-type = ; + }; + }; + + port@1 { + reg = <1>; + + dcmipp_pipe_dump: endpoint { + compatible = "st,stm32-dcmipp-pipe"; + }; + }; + }; + }; + ltdc: display-controller@5a001000 { compatible = "st,stm32-ltdc"; reg = <0x5a001000 0x1000>; From 1b54d250795949818b5718565f62c835ca3abe88 Mon Sep 17 00:00:00 2001 From: Alain Volmat Date: Tue, 3 Jun 2025 19:21:34 +0200 Subject: [PATCH 8/8] shields: st_b_cams_imx_mb1854: add stm32n6 specifc confs Add configuration for the STM32N6 DCMIPP driver which currently requires to have the sensor pixel format and resolutions set via KConfig Signed-off-by: Alain Volmat --- boards/shields/st_b_cams_imx_mb1854/boards/stm32n6570_dk.conf | 3 +++ .../boards/stm32n6570_dk_stm32n657xx_sb.conf | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 boards/shields/st_b_cams_imx_mb1854/boards/stm32n6570_dk.conf create mode 100644 boards/shields/st_b_cams_imx_mb1854/boards/stm32n6570_dk_stm32n657xx_sb.conf diff --git a/boards/shields/st_b_cams_imx_mb1854/boards/stm32n6570_dk.conf b/boards/shields/st_b_cams_imx_mb1854/boards/stm32n6570_dk.conf new file mode 100644 index 000000000000..f89907f91723 --- /dev/null +++ b/boards/shields/st_b_cams_imx_mb1854/boards/stm32n6570_dk.conf @@ -0,0 +1,3 @@ +CONFIG_VIDEO_STM32_DCMIPP_SENSOR_PIXEL_FORMAT="pRAA" +CONFIG_VIDEO_STM32_DCMIPP_SENSOR_WIDTH=2592 +CONFIG_VIDEO_STM32_DCMIPP_SENSOR_HEIGHT=1944 diff --git a/boards/shields/st_b_cams_imx_mb1854/boards/stm32n6570_dk_stm32n657xx_sb.conf b/boards/shields/st_b_cams_imx_mb1854/boards/stm32n6570_dk_stm32n657xx_sb.conf new file mode 100644 index 000000000000..f89907f91723 --- /dev/null +++ b/boards/shields/st_b_cams_imx_mb1854/boards/stm32n6570_dk_stm32n657xx_sb.conf @@ -0,0 +1,3 @@ +CONFIG_VIDEO_STM32_DCMIPP_SENSOR_PIXEL_FORMAT="pRAA" +CONFIG_VIDEO_STM32_DCMIPP_SENSOR_WIDTH=2592 +CONFIG_VIDEO_STM32_DCMIPP_SENSOR_HEIGHT=1944