From 14486c49a513ee95685faf74658e6d55d362eb1e Mon Sep 17 00:00:00 2001 From: Khanh Nguyen Date: Tue, 1 Jul 2025 10:32:26 +0700 Subject: [PATCH 1/8] manifest: Update hal_renesas to support CEU on RA SoCs Update hal_renesas to support CEU on RA SoCs Signed-off-by: Duy Vo Signed-off-by: Khanh Nguyen --- west.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/west.yml b/west.yml index fe8253bfa2f2..313cb769f6fd 100644 --- a/west.yml +++ b/west.yml @@ -226,7 +226,7 @@ manifest: - hal - name: hal_renesas path: modules/hal/renesas - revision: 0769fe1520f6c14e6301188588da758a609f181d + revision: 985d39b16e28d296cd1dc49b96a598c483ec8209 groups: - hal - name: hal_rpi_pico From 663c788e12d2f7843336e22992c8a57a937bf65e Mon Sep 17 00:00:00 2001 From: Khanh Nguyen Date: Tue, 1 Jul 2025 10:38:58 +0700 Subject: [PATCH 2/8] drivers: video: add support for Renesas RA CEU driver Add support for the Renesas RA Capture Engine Unit (CEU), including driver source files, Kconfig options, and DTS bindings. - Add initial implementation of the RA CEU driver - Add dedicated Kconfig and CMake integration - Provide Devicetree bindings for the RA CEU - Update module Kconfig to include the new driver This enables image capture functionality using the CEU peripheral on Renesas RA series MCUs. Signed-off-by: Duy Vo Signed-off-by: Khanh Nguyen --- drivers/video/CMakeLists.txt | 1 + drivers/video/Kconfig | 2 + drivers/video/Kconfig.renesas_ra_ceu | 12 + drivers/video/video_renesas_ra_ceu.c | 538 +++++++++++++++++++++++++ dts/bindings/video/renesas,ra-ceu.yaml | 83 ++++ modules/Kconfig.renesas | 5 + 6 files changed, 641 insertions(+) create mode 100644 drivers/video/Kconfig.renesas_ra_ceu create mode 100644 drivers/video/video_renesas_ra_ceu.c create mode 100644 dts/bindings/video/renesas,ra-ceu.yaml diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index 083df586b003..7a03156165ce 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -25,5 +25,6 @@ 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_library_sources_ifdef(CONFIG_VIDEO_RENESAS_RA_CEU video_renesas_ra_ceu.c) zephyr_linker_sources(DATA_SECTIONS video.ld) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 924e2156273c..7b1f57a83c48 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -96,4 +96,6 @@ source "drivers/video/Kconfig.st_mipid02" source "drivers/video/Kconfig.stm32_dcmipp" +source "drivers/video/Kconfig.renesas_ra_ceu" + endif # VIDEO diff --git a/drivers/video/Kconfig.renesas_ra_ceu b/drivers/video/Kconfig.renesas_ra_ceu new file mode 100644 index 000000000000..3c0d0b73bd3d --- /dev/null +++ b/drivers/video/Kconfig.renesas_ra_ceu @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config VIDEO_RENESAS_RA_CEU + bool + default y + depends on DT_HAS_RENESAS_RA_CEU_ENABLED + select PINCTRL + select CLOCK_CONTROL_PWM + select USE_RA_FSP_CEU + help + Enable driver for Renesas RA CEU. diff --git a/drivers/video/video_renesas_ra_ceu.c b/drivers/video/video_renesas_ra_ceu.c new file mode 100644 index 000000000000..596947d777bf --- /dev/null +++ b/drivers/video/video_renesas_ra_ceu.c @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2025 Renesas Electronics Co. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_ra_ceu + +#include +#include +#include +#include +#include +#include +#include + +#include "video_device.h" +#include "r_ceu.h" + +LOG_MODULE_REGISTER(renesas_ra_video_ceu, CONFIG_VIDEO_LOG_LEVEL); + +/* + * Hardware alignment constraints: + * - Width: 128–2560 pixels, must be a multiple of 8 + * - Height: 96–1920 lines, must be a multiple of 4 + */ +#define VIDEO_RA_CEU_WIDTH_MIN 128U +#define VIDEO_RA_CEU_WIDTH_MAX 2560U +#define VIDEO_RA_CEU_WIDTH_ALIGN 8U +#define VIDEO_RA_CEU_HEIGHT_MIN 96U +#define VIDEO_RA_CEU_HEIGHT_MAX 1920U +#define VIDEO_RA_CEU_HEIGHT_ALIGN 4U + +/* + * Default capture configuration: + * - Resolution: 128x96 + * - Bytes per pixel: 2 + */ +#define VIDEO_RA_CEU_DEFAULT_WIDTH VIDEO_RA_CEU_WIDTH_MIN +#define VIDEO_RA_CEU_DEFAULT_HEIGHT VIDEO_RA_CEU_HEIGHT_MIN +#define VIDEO_RA_CEU_DEFAULT_START_X 0U +#define VIDEO_RA_CEU_DEFAULT_START_Y 0U +#define VIDEO_RA_CEU_DEFAULT_BYTES_PER_PIXEL 2U + +struct video_renesas_ra_ceu_config { + void (*irq_config_func)(const struct device *dev); + const struct device *clock_dev; + const struct device *cam_xclk_dev; + const struct device *source_dev; + const struct pinctrl_dev_config *pincfg; + const struct clock_control_ra_subsys_cfg clock_subsys; +}; + +struct video_renesas_ra_ceu_data { + struct st_ceu_instance_ctrl *fsp_ctrl; + struct st_capture_cfg *fsp_cfg; + struct st_ceu_extended_cfg *fsp_extend_cfg; + struct video_format fmt; + struct k_fifo fifo_in; + struct k_fifo fifo_out; + atomic_ptr_t vbuf; + atomic_t streaming; +#ifdef CONFIG_POLL + struct k_poll_signal *signal; +#endif +}; + +extern void ceu_isr(void); + +static void video_renesas_ra_ceu_callback(capture_callback_args_t *p_args) +{ + const struct device *dev = p_args->p_context; + struct video_renesas_ra_ceu_data *data = dev->data; + struct video_buffer *curr_vbuf; + struct video_buffer *next_vbuf; + fsp_err_t err; + + if (p_args->event & CEU_EVENT_FRAME_END) { + curr_vbuf = atomic_ptr_get(&data->vbuf); + if (curr_vbuf && atomic_ptr_cas(&data->vbuf, curr_vbuf, NULL)) { + curr_vbuf->timestamp = k_uptime_get_32(); + k_fifo_put(&data->fifo_out, curr_vbuf); + } + + next_vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT); + if (next_vbuf == NULL) { + return; + } + + if (!atomic_ptr_cas(&data->vbuf, NULL, next_vbuf)) { + k_fifo_put(&data->fifo_in, next_vbuf); + return; + } + + err = R_CEU_CaptureStart(data->fsp_ctrl, next_vbuf->buffer); + if (err != FSP_SUCCESS) { + atomic_ptr_clear(&data->vbuf); + k_fifo_put(&data->fifo_out, next_vbuf); + return; + } + } +} + +static void video_renesas_ra_ceu_capture_stop(void) +{ + R_CEU->CAPSR = R_CEU_CAPSR_CPKIL_Msk; +} + +static int video_renesas_ra_ceu_get_format(const struct device *dev, struct video_format *fmt) +{ + const struct video_renesas_ra_ceu_config *config = dev->config; + int ret; + + ret = video_get_format(config->source_dev, fmt); + if (ret < 0) { + LOG_DBG("Failed to get video format from source device"); + return ret; + } + + fmt->pitch = fmt->width * video_bits_per_pixel(fmt->pixelformat) / BITS_PER_BYTE; + + return 0; +} + +static int video_renesas_ra_ceu_set_format(const struct device *dev, struct video_format *fmt) +{ + const struct video_renesas_ra_ceu_config *config = dev->config; + struct video_renesas_ra_ceu_data *data = dev->data; + fsp_err_t err; + int ret; + + if (fmt->width > VIDEO_RA_CEU_WIDTH_MAX || fmt->width < VIDEO_RA_CEU_WIDTH_MIN) { + LOG_DBG("Width %d out of supported range %d-%d", fmt->width, VIDEO_RA_CEU_WIDTH_MIN, + VIDEO_RA_CEU_WIDTH_MAX); + return -ENOTSUP; + } + if (fmt->width % VIDEO_RA_CEU_WIDTH_ALIGN != 0) { + LOG_DBG("Width %d not a multiple of %d", fmt->width, VIDEO_RA_CEU_WIDTH_ALIGN); + return -ENOTSUP; + } + + if (fmt->height > VIDEO_RA_CEU_HEIGHT_MAX || fmt->height < VIDEO_RA_CEU_HEIGHT_MIN) { + LOG_DBG("Height %d out of supported range %d-%d", fmt->height, + VIDEO_RA_CEU_HEIGHT_MIN, VIDEO_RA_CEU_HEIGHT_MAX); + return -ENOTSUP; + } + if (fmt->height % VIDEO_RA_CEU_HEIGHT_ALIGN != 0) { + LOG_DBG("Height %d not a multiple of %d", fmt->height, VIDEO_RA_CEU_HEIGHT_ALIGN); + return -ENOTSUP; + } + + ret = video_set_format(config->source_dev, fmt); + if (ret < 0) { + LOG_DBG("Failed to set format on source device"); + return ret; + } + + if (data->fsp_ctrl->open) { + R_CEU_Close(data->fsp_ctrl); + } + + data->fsp_cfg->x_capture_pixels = fmt->width; + data->fsp_cfg->y_capture_pixels = fmt->height; + data->fsp_cfg->bytes_per_pixel = video_bits_per_pixel(fmt->pixelformat) / BITS_PER_BYTE; + + err = R_CEU_Open(data->fsp_ctrl, data->fsp_cfg); + if (err != FSP_SUCCESS) { + LOG_DBG("Failed to open CEU"); + return -EIO; + } + + fmt->pitch = fmt->width * video_bits_per_pixel(fmt->pixelformat) / BITS_PER_BYTE; + + memcpy(&data->fmt, fmt, sizeof(struct video_format)); + + return 0; +} + +static int video_renesas_ra_ceu_get_caps(const struct device *dev, struct video_caps *caps) +{ + const struct video_renesas_ra_ceu_config *config = dev->config; + + caps->min_line_count = caps->max_line_count = LINE_COUNT_HEIGHT; + + return video_get_caps(config->source_dev, caps); +} + +static int video_renesas_ra_ceu_set_stream(const struct device *dev, bool enable, + enum video_buf_type type) +{ + const struct video_renesas_ra_ceu_config *config = dev->config; + struct video_renesas_ra_ceu_data *data = dev->data; + struct video_buffer *next_vbuf; + fsp_err_t err; + int ret; + + if (!enable) { + ret = video_stream_stop(config->source_dev, type); + if (ret < 0) { + LOG_DBG("Failed to stop source device stream"); + return -EIO; + } + + video_renesas_ra_ceu_capture_stop(); + atomic_clear(&data->streaming); + + return 0; + } + + if (atomic_get(&data->streaming)) { + return -EBUSY; + } + + next_vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT); + if (next_vbuf == NULL) { + LOG_DBG("No enqueued video buffers available to start streaming"); + return -EAGAIN; + } + + if (!atomic_ptr_cas(&data->vbuf, NULL, next_vbuf)) { + k_fifo_put(&data->fifo_in, next_vbuf); + return 0; + } + + err = R_CEU_CaptureStart(data->fsp_ctrl, next_vbuf->buffer); + if (err != FSP_SUCCESS) { + LOG_DBG("Failed to start CEU capture"); + atomic_ptr_clear(&data->vbuf); + k_fifo_put(&data->fifo_out, next_vbuf); + return -EIO; + } + + ret = video_stream_start(config->source_dev, type); + if (ret < 0) { + LOG_DBG("Failed to start source device stream"); + atomic_ptr_clear(&data->vbuf); + video_renesas_ra_ceu_capture_stop(); + return -EIO; + } + + atomic_set(&data->streaming, 1); + + return 0; +} + +static int video_renesas_ra_ceu_enqueue(const struct device *dev, struct video_buffer *vbuf) +{ + struct video_renesas_ra_ceu_data *data = dev->data; + struct video_buffer *next_vbuf; + const uint32_t buffer_size = data->fmt.pitch * data->fmt.height; + fsp_err_t err; + + if (buffer_size > vbuf->size) { + LOG_DBG("Enqueue buffer too small"); + return -EINVAL; + } + + vbuf->bytesused = buffer_size; + vbuf->line_offset = 0; + + k_fifo_put(&data->fifo_in, vbuf); + + if (!atomic_get(&data->streaming)) { + return 0; + } + + if (atomic_ptr_get(&data->vbuf)) { + return 0; + } + + next_vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT); + if (!next_vbuf) { + LOG_DBG("No enqueued video buffers available to restart streaming"); + return -EAGAIN; + } + + if (!atomic_ptr_cas(&data->vbuf, NULL, next_vbuf)) { + k_fifo_put(&data->fifo_in, next_vbuf); + return 0; + } + + err = R_CEU_CaptureStart(data->fsp_ctrl, next_vbuf->buffer); + if (err != FSP_SUCCESS) { + LOG_DBG("Failed to start CEU capture"); + atomic_ptr_clear(&data->vbuf); + k_fifo_put(&data->fifo_out, next_vbuf); + return -EIO; + } + + return 0; +} + +static int video_renesas_ra_ceu_dequeue(const struct device *dev, struct video_buffer **buf, + k_timeout_t timeout) +{ + struct video_renesas_ra_ceu_data *data = dev->data; + + *buf = k_fifo_get(&data->fifo_out, timeout); + if (*buf == NULL) { + LOG_DBG("Dequeue timeout or no completed buffer available"); + return -EAGAIN; + } + + return 0; +} + +static int video_renesas_ra_ceu_get_frmival(const struct device *dev, struct video_frmival *frmival) +{ + const struct video_renesas_ra_ceu_config *config = dev->config; + + return video_get_frmival(config->source_dev, frmival); +} + +static int video_renesas_ra_ceu_set_frmival(const struct device *dev, struct video_frmival *frmival) +{ + const struct video_renesas_ra_ceu_config *config = dev->config; + + return video_set_frmival(config->source_dev, frmival); +} + +static int video_renesas_ra_ceu_enum_frmival(const struct device *dev, + struct video_frmival_enum *fie) +{ + const struct video_renesas_ra_ceu_config *config = dev->config; + + return video_enum_frmival(config->source_dev, fie); +} + +static int video_renesas_ra_ceu_flush(const struct device *dev, bool cancel) +{ + struct video_renesas_ra_ceu_data *data = dev->data; + struct video_buffer *vbuf; + + if (cancel) { + if (atomic_cas(&data->streaming, 1, 0)) { + video_renesas_ra_ceu_set_stream(dev, false, VIDEO_BUF_TYPE_OUTPUT); + } + + vbuf = atomic_ptr_get(&data->vbuf); + if (vbuf && atomic_ptr_cas(&data->vbuf, vbuf, NULL)) { + k_fifo_put(&data->fifo_out, vbuf); + } + + while ((vbuf = k_fifo_get(&data->fifo_in, K_NO_WAIT)) != NULL) { + k_fifo_put(&data->fifo_out, vbuf); + } + +#ifdef CONFIG_POLL + if (data->signal) { + k_poll_signal_raise(data->signal, VIDEO_BUF_ABORTED); + } +#endif + + } else { + while (!k_fifo_is_empty(&data->fifo_in)) { + k_sleep(K_MSEC(1)); + } + } + + return 0; +} + +#ifdef CONFIG_POLL +static int video_renesas_ra_ceu_set_signal(const struct device *dev, struct k_poll_signal *sig) +{ + struct video_renesas_ra_ceu_data *data = dev->data; + + data->signal = sig; + return 0; +} +#endif + +static int video_renesas_ra_ceu_init(const struct device *dev) +{ + const struct video_renesas_ra_ceu_config *config = dev->config; + struct video_renesas_ra_ceu_data *data = dev->data; + fsp_err_t err; + int ret; + + ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_DBG("Failed to configure pinctrl"); + return ret; + } + + if (!device_is_ready(config->clock_dev)) { + LOG_DBG("Clock control device not ready"); + return -ENODEV; + } + + ret = clock_control_on(config->clock_dev, (clock_control_subsys_t)&config->clock_subsys); + if (ret < 0) { + LOG_DBG("Failed to enable clock control"); + return ret; + } + + config->irq_config_func(dev); + + err = R_CEU_Open(data->fsp_ctrl, data->fsp_cfg); + if (err != FSP_SUCCESS) { + LOG_DBG("Failed to open CEU hardware"); + return -EIO; + } + + atomic_clear(&data->streaming); + atomic_ptr_clear(&data->vbuf); + k_fifo_init(&data->fifo_in); + k_fifo_init(&data->fifo_out); + + return 0; +} + +static DEVICE_API(video, video_renesas_ra_ceu_driver_api) = { + .get_format = video_renesas_ra_ceu_get_format, + .set_format = video_renesas_ra_ceu_set_format, + .get_caps = video_renesas_ra_ceu_get_caps, + .set_stream = video_renesas_ra_ceu_set_stream, + .enqueue = video_renesas_ra_ceu_enqueue, + .dequeue = video_renesas_ra_ceu_dequeue, + .enum_frmival = video_renesas_ra_ceu_enum_frmival, + .set_frmival = video_renesas_ra_ceu_set_frmival, + .get_frmival = video_renesas_ra_ceu_get_frmival, + .flush = video_renesas_ra_ceu_flush, +#ifdef CONFIG_POLL + .set_signal = video_renesas_ra_ceu_set_signal, +#endif +}; + +#define EVENT_CEU_INT(inst) BSP_PRV_IELS_ENUM(CONCAT(EVENT_CEU, _CEUI)) + +#define EP_INST_NODE(inst) DT_INST_ENDPOINT_BY_ID(inst, 0, 0) + +#define EP_INST_PROP_SEL(inst, prop, match_val, val_if_true, val_if_false) \ + (DT_PROP(EP_INST_NODE(inst), prop) == (match_val) ? (val_if_true) : (val_if_false)) + +#define SOURCE_DEV(inst) DEVICE_DT_GET(DT_NODE_REMOTE_DEVICE(EP_INST_NODE(inst))) + +#define VIDEO_RENESAS_RA_CEU_INIT(inst) \ + static void video_renesas_ra_ceu_irq_config_func##inst(const struct device *dev) \ + { \ + R_ICU->IELSR[DT_INST_IRQ_BY_NAME(inst, ceui, irq)] = EVENT_CEU_INT(inst); \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(inst, ceui, irq), \ + DT_INST_IRQ_BY_NAME(inst, ceui, priority), ceu_isr, NULL, 0); \ + irq_enable(DT_INST_IRQ_BY_NAME(inst, ceui, irq)); \ + } \ + \ + static int video_renesas_ra_ceu_cam_clock_init##inst(void) \ + { \ + const struct device *dev = DEVICE_DT_INST_GET(inst); \ + const struct video_renesas_ra_ceu_config *config = dev->config; \ + int ret; \ + \ + if (!device_is_ready(config->cam_xclk_dev)) { \ + LOG_DBG("Camera clock control device not ready"); \ + return -ENODEV; \ + } \ + \ + ret = clock_control_on(config->cam_xclk_dev, (clock_control_subsys_t)0); \ + if (ret < 0) { \ + LOG_DBG("Failed to enable camera clock control"); \ + return ret; \ + } \ + return 0; \ + } \ + \ + PINCTRL_DT_INST_DEFINE(inst); \ + \ + static struct video_renesas_ra_ceu_config video_renesas_ra_ceu_config##inst = { \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(inst, pclk)), \ + .cam_xclk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR_BY_NAME(inst, cam_xclk)), \ + .source_dev = SOURCE_DEV(inst), \ + .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .clock_subsys = \ + { \ + .mstp = DT_INST_CLOCKS_CELL_BY_NAME(inst, pclk, mstp), \ + .stop_bit = DT_INST_CLOCKS_CELL_BY_NAME(inst, pclk, stop_bit), \ + }, \ + .irq_config_func = video_renesas_ra_ceu_irq_config_func##inst, \ + }; \ + \ + static struct st_ceu_instance_ctrl video_renesas_ra_ceu_fsp_ctrl##inst = { \ + .p_context = DEVICE_DT_INST_GET(inst), \ + .p_callback_memory = NULL, \ + }; \ + \ + static struct st_ceu_extended_cfg video_renesas_ra_ceu_fsp_extend_cfg##inst = { \ + .capture_format = CEU_CAPTURE_FORMAT_DATA_SYNCHRONOUS, \ + .data_bus_width = EP_INST_PROP_SEL(inst, bus_width, 8, 0, 1), \ + .edge_info = \ + { \ + .dsel = EP_INST_PROP_SEL(inst, pclk_sample, 1, 0, 1), \ + .hdsel = EP_INST_PROP_SEL(inst, hsync_sample, 1, 0, 1), \ + .vdsel = EP_INST_PROP_SEL(inst, vsync_sample, 1, 0, 1), \ + }, \ + .hsync_polarity = EP_INST_PROP_SEL(inst, hsync_active, 1, 0, 1), \ + .vsync_polarity = EP_INST_PROP_SEL(inst, vsync_active, 1, 0, 1), \ + .byte_swapping = \ + { \ + .swap_8bit_units = DT_INST_PROP(inst, swap_8bits), \ + .swap_16bit_units = DT_INST_PROP(inst, swap_16bits), \ + .swap_32bit_units = DT_INST_PROP(inst, swap_32bits), \ + }, \ + .burst_mode = DT_INST_ENUM_IDX(inst, burst_transfer), \ + .ceu_ipl = DT_INST_IRQ_BY_NAME(inst, ceui, priority), \ + .ceu_irq = DT_INST_IRQ_BY_NAME(inst, ceui, irq), \ + .interrupts_enabled = R_CEU_CEIER_CPEIE_Msk | R_CEU_CEIER_VDIE_Msk | \ + R_CEU_CEIER_CDTOFIE_Msk | R_CEU_CEIER_VBPIE_Msk | \ + R_CEU_CEIER_NHDIE_Msk | R_CEU_CEIER_NVDIE_Msk, \ + }; \ + \ + static struct st_capture_cfg video_renesas_ra_ceu_fsp_cfg##inst = { \ + .x_capture_pixels = VIDEO_RA_CEU_DEFAULT_WIDTH, \ + .y_capture_pixels = VIDEO_RA_CEU_DEFAULT_HEIGHT, \ + .x_capture_start_pixel = VIDEO_RA_CEU_DEFAULT_START_X, \ + .y_capture_start_pixel = VIDEO_RA_CEU_DEFAULT_START_Y, \ + .bytes_per_pixel = VIDEO_RA_CEU_DEFAULT_BYTES_PER_PIXEL, \ + .p_extend = &video_renesas_ra_ceu_fsp_extend_cfg##inst, \ + .p_callback = video_renesas_ra_ceu_callback, \ + .p_context = DEVICE_DT_INST_GET(inst), \ + }; \ + \ + static struct video_renesas_ra_ceu_data video_renesas_ra_ceu_data##inst = { \ + .fsp_ctrl = &video_renesas_ra_ceu_fsp_ctrl##inst, \ + .fsp_cfg = &video_renesas_ra_ceu_fsp_cfg##inst, \ + .fsp_extend_cfg = &video_renesas_ra_ceu_fsp_extend_cfg##inst, \ + }; \ + \ + DEVICE_DT_INST_DEFINE(inst, &video_renesas_ra_ceu_init, NULL, \ + &video_renesas_ra_ceu_data##inst, \ + &video_renesas_ra_ceu_config##inst, POST_KERNEL, \ + CONFIG_VIDEO_INIT_PRIORITY, &video_renesas_ra_ceu_driver_api); \ + \ + VIDEO_DEVICE_DEFINE(renesas_ra_ceu##inst, DEVICE_DT_INST_GET(inst), SOURCE_DEV(inst)); \ + \ + SYS_INIT(video_renesas_ra_ceu_cam_clock_init##inst, POST_KERNEL, \ + CONFIG_CLOCK_CONTROL_PWM_INIT_PRIORITY); + +DT_INST_FOREACH_STATUS_OKAY(VIDEO_RENESAS_RA_CEU_INIT) diff --git a/dts/bindings/video/renesas,ra-ceu.yaml b/dts/bindings/video/renesas,ra-ceu.yaml new file mode 100644 index 000000000000..f31cfb53b6c6 --- /dev/null +++ b/dts/bindings/video/renesas,ra-ceu.yaml @@ -0,0 +1,83 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA Capture Engine Unit Driver (ceu) + +compatible: "renesas,ra-ceu" + +include: [base.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + clocks: + required: true + + clock-names: + required: true + enum: + - "pclk" + - "cam-xclk" + + swap-8bits: + type: boolean + description: Bytes may be swapped in 8-bit units + + swap-16bits: + type: boolean + description: Bytes may be swapped in 16-bit units + + swap-32bits: + type: boolean + description: Bytes may be swapped in 32-bit units + + burst-transfer: + required: true + type: int + enum: + - 32 # Transfers data to the bus in 32-byte units + - 64 # Transfers data to the bus in 64-byte units + - 128 # Transfers data to the bus in 128-byte units + - 256 # Transfers data to the bus in 256-byte units + description: | + Specifies the data transfer unit size to the bus bridge module. + +child-binding: + child-binding: + include: video-interfaces.yaml + + properties: + hsync-active: + required: true + + vsync-active: + required: true + + pclk-sample: + required: true + + vsync-sample: + required: true + type: int + enum: + - 0 # Falling edge + - 1 # Rising edge + description: | + Sample on the falling or rising edge of the vsync signal. + + hsync-sample: + required: true + type: int + enum: + - 0 # Falling edge + - 1 # Rising edge + description: | + Sample on the falling or rising edge of the hsync signal. + + bus-width: + required: true + type: int + enum: + - 8 # Use 8 data lines for the parallel interface + - 16 # Use 16 data lines for the parallel interface diff --git a/modules/Kconfig.renesas b/modules/Kconfig.renesas index 12a898e2d8e8..5133d566a113 100644 --- a/modules/Kconfig.renesas +++ b/modules/Kconfig.renesas @@ -201,6 +201,11 @@ config USE_RA_FSP_SSI help Enable RA FSP I2S SSI driver +config USE_RA_FSP_CEU + bool + help + Enable RA FSP CEU driver + endif # HAS_RENESAS_RA_FSP if HAS_RENESAS_RZ_FSP From f23d5df6624c73ff997f1d28a6d8f4fe55d7a6a6 Mon Sep 17 00:00:00 2001 From: Khanh Nguyen Date: Tue, 1 Jul 2025 10:41:44 +0700 Subject: [PATCH 3/8] include: dt-bindings: renesas: Add RA CEU pin definitions Add pin definitions required by the RA Capture Engine Unit Signed-off-by: Duy Vo Signed-off-by: Khanh Nguyen --- include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h b/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h index a077cd226866..0808336a0a47 100644 --- a/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h +++ b/include/zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h @@ -48,6 +48,7 @@ #define RA_PSEL_ETH_RMII 0x17 #define RA_PSEL_GLCDC 0x19 #define RA_PSEL_OSPI 0x1c +#define RA_PSEL_CEU 0xf #define RA_PSEL_POS 8 #define RA_PSEL_MASK 0x1f From 38d261b1cb0cd109e4808b47c9e8bbc0d92d964a Mon Sep 17 00:00:00 2001 From: Khanh Nguyen Date: Tue, 1 Jul 2025 10:43:57 +0700 Subject: [PATCH 4/8] dts: arm: renesas: Add CEU nodes for RA8D1 and RA8M1 SoCs Add CEU to r7fa8d1xh.dtsi and r7fa8m1xh.dtsi Signed-off-by: Khanh Nguyen --- dts/arm/renesas/ra/ra8/r7fa8d1xh.dtsi | 7 +++++++ dts/arm/renesas/ra/ra8/r7fa8m1xh.dtsi | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/dts/arm/renesas/ra/ra8/r7fa8d1xh.dtsi b/dts/arm/renesas/ra/ra8/r7fa8d1xh.dtsi index e1abfa502484..fabef6762b00 100644 --- a/dts/arm/renesas/ra/ra8/r7fa8d1xh.dtsi +++ b/dts/arm/renesas/ra/ra8/r7fa8d1xh.dtsi @@ -295,6 +295,13 @@ status = "disabled"; }; }; + + ceu: ceu@40348000 { + compatible = "renesas,ra-ceu"; + reg = <0x40348000 0x8000>; + clocks = <&pclka MSTPC 16>; + status = "disabled"; + }; }; usbhs_phy: usbhs-phy { diff --git a/dts/arm/renesas/ra/ra8/r7fa8m1xh.dtsi b/dts/arm/renesas/ra/ra8/r7fa8m1xh.dtsi index 60fbd8c287ef..43c454f3b1d7 100644 --- a/dts/arm/renesas/ra/ra8/r7fa8m1xh.dtsi +++ b/dts/arm/renesas/ra/ra8/r7fa8m1xh.dtsi @@ -259,6 +259,13 @@ status = "disabled"; }; }; + + ceu: ceu@40348000 { + compatible = "renesas,ra-ceu"; + reg = <0x40348000 0x8000>; + clocks = <&pclka MSTPC 16>; + status = "disabled"; + }; }; usbhs_phy: usbhs-phy { From 09a5ef24204b12dafbacee910239c63b2b57c29f Mon Sep 17 00:00:00 2001 From: Khanh Nguyen Date: Tue, 1 Jul 2025 10:51:22 +0700 Subject: [PATCH 5/8] boards: renesas: Enable CEU video capture support on EK-RA8D1 - Add CEU pin configuration to ek_ra8d1-pinctrl.dtsi - Enable CEU node and Arducam 20-pin connector in ek_ra8d1.dts - Configure PWM3 as external XCLK via pwm-clock node - Update board YAML to declare video support Signed-off-by: Duy Vo Signed-off-by: Khanh Nguyen --- boards/renesas/ek_ra8d1/ek_ra8d1-pinctrl.dtsi | 31 ++++++++++++++ boards/renesas/ek_ra8d1/ek_ra8d1.dts | 40 +++++++++++++++++++ boards/renesas/ek_ra8d1/ek_ra8d1.yaml | 1 + 3 files changed, 72 insertions(+) diff --git a/boards/renesas/ek_ra8d1/ek_ra8d1-pinctrl.dtsi b/boards/renesas/ek_ra8d1/ek_ra8d1-pinctrl.dtsi index 9d8584ed161d..330498f75d3e 100644 --- a/boards/renesas/ek_ra8d1/ek_ra8d1-pinctrl.dtsi +++ b/boards/renesas/ek_ra8d1/ek_ra8d1-pinctrl.dtsi @@ -43,6 +43,19 @@ }; }; + pwm3_default: pwm3_default { + group1 { + /* GTIOC3A */ + psels = ; + drive-strength = "medium"; + }; + group2 { + /* GTIOC3B */ + psels = ; + drive-strength = "medium"; + }; + }; + pwm7_default: pwm7_default { group1 { /* GTIOC7A */ @@ -297,4 +310,22 @@ drive-strength = "high"; }; }; + + ceu_default: ceu_default { + group1 { + /* CEU */ + psels = , /* VIO_D0 */ + , /* VIO_D1 */ + , /* VIO_D2 */ + , /* VIO_D3 */ + , /* VIO_D4 */ + , /* VIO_D5 */ + , /* VIO_D6 */ + , /* VIO_D7 */ + , /* VIO_CLK */ + , /* VIO_HD */ + ; /* VIO_VD */ + drive-strength = "high"; + }; + }; }; diff --git a/boards/renesas/ek_ra8d1/ek_ra8d1.dts b/boards/renesas/ek_ra8d1/ek_ra8d1.dts index f91c2fa91c70..17b8d6b115ae 100644 --- a/boards/renesas/ek_ra8d1/ek_ra8d1.dts +++ b/boards/renesas/ek_ra8d1/ek_ra8d1.dts @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include "ek_ra8d1-pinctrl.dtsi" / { @@ -78,6 +80,15 @@ <18 0 &ioporta 1 0>; /* DISP_RST */ }; + dvp_20pin_connector: dvp-20pin-connector { + compatible = "arducam,dvp-20pin-connector"; + #gpio-cells = <2>; + gpio-map-mask = <0xffffffff 0xffffffc0>; + gpio-map-pass-thru = <0x0 0x3f>; + gpio-map = , + ; + }; + aliases { led0 = &led1; sw0 = &button0; @@ -255,6 +266,21 @@ }; }; +&pwm3 { + pinctrl-0 = <&pwm3_default>; + pinctrl-names = "default"; + interrupts = <51 12>, <52 12>; + interrupt-names = "gtioca", "overflow"; + + cam_clock: pwmclock { + compatible = "pwm-clock"; + status = "disabled"; + #clock-cells = <1>; + clock-frequency = <24000000>; + pwms = <&pwm3 0 PWM_KHZ(24000) PWM_POLARITY_NORMAL>; + }; +}; + &pwm7 { pinctrl-0 = <&pwm7_default>; interrupts = <40 1>, <41 1>; @@ -358,6 +384,16 @@ }; }; +&ceu { + pinctrl-0 = <&ceu_default>; + pinctrl-names = "default"; + interrupts = <53 12>; + interrupt-names = "ceui"; + clocks = <&pclka MSTPC 16>, <&cam_clock 0>; + clock-names = "pclk", "cam-xclk"; + burst-transfer = <256>; +}; + zephyr_lcdif: &lcdif {}; zephyr_mipi_dsi: &mipi_dsi {}; @@ -366,6 +402,10 @@ renesas_mipi_i2c: &iic1 {}; pmod_sd_shield: &sdhc1 {}; +dvp_20pin_i2c: &iic1 {}; + +dvp_20pin_interface: &ceu {}; + &usbfs { pinctrl-0 = <&usbfs_default>; pinctrl-names = "default"; diff --git a/boards/renesas/ek_ra8d1/ek_ra8d1.yaml b/boards/renesas/ek_ra8d1/ek_ra8d1.yaml index 11d214225c73..85a97b34816b 100644 --- a/boards/renesas/ek_ra8d1/ek_ra8d1.yaml +++ b/boards/renesas/ek_ra8d1/ek_ra8d1.yaml @@ -16,4 +16,5 @@ supported: - counter - i2s - i3c + - video vendor: renesas From 6b3b7cc0418385b7205d073973e86556703ea260 Mon Sep 17 00:00:00 2001 From: Khanh Nguyen Date: Tue, 1 Jul 2025 10:57:39 +0700 Subject: [PATCH 6/8] boards: shields: Support OV7670 DVP 20-pin shield on EK-RA8D1 board Support OV7670 DVP 20-pin shield on the EK-RA8D1 board Signed-off-by: Khanh Nguyen --- .../dvp_20pin_ov7670/boards/ek_ra8d1.overlay | 30 +++++++++++++++++++ .../dvp_20pin_ov7670/dvp_20pin_ov7670.overlay | 7 +++++ 2 files changed, 37 insertions(+) create mode 100644 boards/shields/dvp_20pin_ov7670/boards/ek_ra8d1.overlay diff --git a/boards/shields/dvp_20pin_ov7670/boards/ek_ra8d1.overlay b/boards/shields/dvp_20pin_ov7670/boards/ek_ra8d1.overlay new file mode 100644 index 000000000000..9fe1bd88ae83 --- /dev/null +++ b/boards/shields/dvp_20pin_ov7670/boards/ek_ra8d1.overlay @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&pwm3 { + status = "okay"; +}; + +&cam_clock { + status = "okay"; +}; + +&dvp_20pin_i2c { + clock-frequency = ; +}; + +&dvp_20pin_interface { + swap-8bits; + swap-16bits; + swap-32bits; + + port { + dvp_20pin_ep_in: endpoint { + hsync-sample = <1>; + vsync-sample = <1>; + }; + }; +}; diff --git a/boards/shields/dvp_20pin_ov7670/dvp_20pin_ov7670.overlay b/boards/shields/dvp_20pin_ov7670/dvp_20pin_ov7670.overlay index 234c33c2d5f8..31e4870ac02f 100644 --- a/boards/shields/dvp_20pin_ov7670/dvp_20pin_ov7670.overlay +++ b/boards/shields/dvp_20pin_ov7670/dvp_20pin_ov7670.overlay @@ -12,11 +12,14 @@ }; &dvp_20pin_i2c { + status = "okay"; + ov7670: ov7670@21 { compatible = "ovti,ov7670"; reg = <0x21>; reset-gpios = <&dvp_20pin_connector DVP_20PIN_PEN GPIO_ACTIVE_HIGH>; pwdn-gpios = <&dvp_20pin_connector DVP_20PIN_PDN GPIO_ACTIVE_HIGH>; + status = "okay"; port { ov7670_ep_out: endpoint { @@ -32,6 +35,10 @@ port { dvp_20pin_ep_in: endpoint { remote-endpoint-label = "ov7670_ep_out"; + bus-width = <8>; + hsync-active = <1>; + vsync-active = <1>; + pclk-sample = <1>; }; }; }; From 6a10a847304a15de9812085ad5fa79bc077d97a5 Mon Sep 17 00:00:00 2001 From: Khanh Nguyen Date: Tue, 1 Jul 2025 11:09:47 +0700 Subject: [PATCH 7/8] samples: video: Add CEU support for Renesas RA boards Add EK-RA8D1 board support for capture and capture_to_lvgl samples Signed-off-by: Khanh Nguyen --- .../drivers/video/capture/boards/ek_ra8d1.conf | 4 ++++ samples/drivers/video/capture/sample.yaml | 2 ++ .../video/capture_to_lvgl/boards/ek_ra8d1.conf | 1 + .../video/capture_to_lvgl/boards/ek_ra8d1.overlay | 15 +++++++++++++++ 4 files changed, 22 insertions(+) create mode 100644 samples/drivers/video/capture/boards/ek_ra8d1.conf create mode 100644 samples/drivers/video/capture_to_lvgl/boards/ek_ra8d1.conf create mode 100644 samples/drivers/video/capture_to_lvgl/boards/ek_ra8d1.overlay diff --git a/samples/drivers/video/capture/boards/ek_ra8d1.conf b/samples/drivers/video/capture/boards/ek_ra8d1.conf new file mode 100644 index 000000000000..0a93093f8923 --- /dev/null +++ b/samples/drivers/video/capture/boards/ek_ra8d1.conf @@ -0,0 +1,4 @@ +CONFIG_VIDEO_FRAME_WIDTH=320 +CONFIG_VIDEO_FRAME_HEIGHT=240 +CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=155000 +CONFIG_VIDEO_BUFFER_POOL_NUM_MAX=2 diff --git a/samples/drivers/video/capture/sample.yaml b/samples/drivers/video/capture/sample.yaml index e52f26aa7cd1..c18e69b8d430 100644 --- a/samples/drivers/video/capture/sample.yaml +++ b/samples/drivers/video/capture/sample.yaml @@ -11,6 +11,7 @@ tests: - platform:mimxrt1170_evk/mimxrt1176/cm7:SHIELD="nxp_btb44_ov5640;rk055hdmipi4ma0" - platform:mimxrt1170_evk@B/mimxrt1176/cm7:SHIELD="nxp_btb44_ov5640;rk055hdmipi4ma0" - platform:frdm_mcxn947/mcxn947/cpu0:SHIELD="dvp_20pin_ov7670;lcd_par_s035_8080" + - platform:ek_ra8d1:SHIELD="dvp_20pin_ov7670;rtkmipilcdb00000be" extra_configs: - CONFIG_TEST=y - CONFIG_FPU=y @@ -32,6 +33,7 @@ tests: - frdm_mcxn947/mcxn947/cpu0 - mm_swiftio - esp32s3_eye/esp32s3/procpu + - ek_ra8d1 depends_on: video integration_platforms: - mimxrt1064_evk/mimxrt1064 diff --git a/samples/drivers/video/capture_to_lvgl/boards/ek_ra8d1.conf b/samples/drivers/video/capture_to_lvgl/boards/ek_ra8d1.conf new file mode 100644 index 000000000000..cfb4819a8e77 --- /dev/null +++ b/samples/drivers/video/capture_to_lvgl/boards/ek_ra8d1.conf @@ -0,0 +1 @@ +CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=160000 diff --git a/samples/drivers/video/capture_to_lvgl/boards/ek_ra8d1.overlay b/samples/drivers/video/capture_to_lvgl/boards/ek_ra8d1.overlay new file mode 100644 index 000000000000..638fb1d4333a --- /dev/null +++ b/samples/drivers/video/capture_to_lvgl/boards/ek_ra8d1.overlay @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2025 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&renesas_mipi_i2c { + gt911_rtkmipilcdb00000be: gt911-rtkmipilcdb00000be@5d { + status = "disabled"; + }; +}; + +&zephyr_lcdif { + input-pixel-format = ; +}; From 03bdbce248fd39a98142e06669a092e51939f355 Mon Sep 17 00:00:00 2001 From: Thao Luong Date: Wed, 25 Jun 2025 11:26:17 +0700 Subject: [PATCH 8/8] boards: renesas: ek_ra8d1: Update document for using Camera Add SW1 configuration for using Camera Exapansion Port (J59) Signed-off-by: Thao Luong --- boards/renesas/ek_ra8d1/doc/index.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/boards/renesas/ek_ra8d1/doc/index.rst b/boards/renesas/ek_ra8d1/doc/index.rst index 6be73b0aadbb..35ee09c0c237 100644 --- a/boards/renesas/ek_ra8d1/doc/index.rst +++ b/boards/renesas/ek_ra8d1/doc/index.rst @@ -119,6 +119,14 @@ Supported Features | OFF | OFF | OFF | OFF | OFF | OFF | OFF | ON | +-------------+-------------+--------------+------------+------------+------------+-------------+-----------+ + - For using the Camera Expansion Port (J59) with the Camera, please set switch SW1 as following configuration: + + +-------------+-------------+--------------+------------+------------+------------+-------------+-----------+ + | SW1-1 PMOD1 | SW1-2 TRACE | SW1-3 CAMERA | SW1-4 ETHA | SW1-5 ETHB | SW1-6 GLCD | SW1-7 SDRAM | SW1-8 I3C | + +-------------+-------------+--------------+------------+------------+------------+-------------+-----------+ + | OFF | OFF | ON | OFF | OFF | OFF | ON | OFF | + +-------------+-------------+--------------+------------+------------+------------+-------------+-----------+ + .. warning:: Do not enable SW1-4 and SW1-5 together