From 7906f4df285dc4ab0c77b06f1450d1af0feb7838 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Tue, 24 Jun 2025 15:12:26 +0200 Subject: [PATCH 1/5] drivers: display: elcdif: Decouple PxP from the display controller PxP is a distinct HW IP for image processing which is independent of the display controller. Decouple it from the elcdif driver. Signed-off-by: Phi Bang Nguyen --- drivers/display/display_mcux_elcdif.c | 110 +----------------- dts/arm/nxp/nxp_rt10xx.dtsi | 1 - dts/arm/nxp/nxp_rt11xx.dtsi | 1 - .../boards/mimxrt1170_evk_mimxrt1176_cm7.conf | 3 - 4 files changed, 1 insertion(+), 114 deletions(-) diff --git a/drivers/display/display_mcux_elcdif.c b/drivers/display/display_mcux_elcdif.c index bdde9f59ed90c..876df33802a42 100644 --- a/drivers/display/display_mcux_elcdif.c +++ b/drivers/display/display_mcux_elcdif.c @@ -17,11 +17,6 @@ #include #endif -#ifdef CONFIG_MCUX_ELCDIF_PXP -#include -#include -#endif - #include #include @@ -39,7 +34,6 @@ struct mcux_elcdif_config { elcdif_rgb_mode_config_t rgb_mode; const struct pinctrl_dev_config *pincfg; const struct gpio_dt_spec backlight_gpio; - const struct device *pxp; }; struct mcux_elcdif_data { @@ -54,22 +48,8 @@ struct mcux_elcdif_data { struct k_sem sem; /* Tracks index of next active driver framebuffer */ uint8_t next_idx; -#ifdef CONFIG_MCUX_ELCDIF_PXP - /* Given to when PXP completes operation */ - struct k_sem pxp_done; -#endif }; -#ifdef CONFIG_MCUX_ELCDIF_PXP -static void mcux_elcdif_pxp_callback(const struct device *dma_dev, void *user_data, - uint32_t channel, int ret) -{ - struct mcux_elcdif_data *data = user_data; - - k_sem_give(&data->pxp_done); -} -#endif /* CONFIG_MCUX_ELCDIF_PXP */ - static int mcux_elcdif_write(const struct device *dev, const uint16_t x, const uint16_t y, const struct display_buffer_descriptor *desc, const void *buf) { @@ -92,15 +72,6 @@ static int mcux_elcdif_write(const struct device *dev, const uint16_t x, const u LOG_DBG("Setting FB from %p->%p", (void *)dev_data->active_fb, (void *)buf); dev_data->active_fb = buf; full_fb = true; - } else if ((x == 0) && (y == 0) && (desc->width == config->rgb_mode.panelHeight) && - (desc->height == config->rgb_mode.panelWidth) && (desc->pitch == desc->width) && - IS_ENABLED(CONFIG_MCUX_ELCDIF_PXP)) { - /* With the PXP, we can rotate this display buffer to align - * with output dimensions - */ - LOG_DBG("Setting FB from %p->%p", (void *)dev_data->active_fb, (void *)buf); - dev_data->active_fb = buf; - full_fb = true; } else { /* We must use partial framebuffer copy */ if (CONFIG_MCUX_ELCDIF_FB_NUM == 0) { @@ -137,77 +108,6 @@ static int mcux_elcdif_write(const struct device *dev, const uint16_t x, const u DCACHE_CleanByRange((uint32_t)dev_data->active_fb, dev_data->fb_bytes); #endif -#ifdef CONFIG_MCUX_ELCDIF_PXP - if (full_fb) { - /* Configure PXP using DMA API, and rotate/flip frame */ - struct dma_config pxp_dma = {0}; - struct dma_block_config pxp_block = {0}; - - /* Source buffer is input to display_write, we will - * place modified output into a driver framebuffer. - */ - dev_data->active_fb = dev_data->fb[dev_data->next_idx]; - pxp_block.source_address = (uint32_t)buf; - pxp_block.dest_address = (uint32_t)dev_data->active_fb; - pxp_block.block_size = desc->buf_size; - - /* DMA slot sets pixel format and rotation angle */ - if (dev_data->pixel_format == PIXEL_FORMAT_BGR_565) { - pxp_dma.dma_slot = DMA_MCUX_PXP_FMT(DMA_MCUX_PXP_FMT_RGB565); - } else if (dev_data->pixel_format == PIXEL_FORMAT_RGB_888) { - pxp_dma.dma_slot = DMA_MCUX_PXP_FMT(DMA_MCUX_PXP_FMT_RGB888); - } else if (dev_data->pixel_format == PIXEL_FORMAT_ARGB_8888) { - pxp_dma.dma_slot = DMA_MCUX_PXP_FMT(DMA_MCUX_PXP_FMT_ARGB8888); - } else { - /* Cannot rotate */ - return -ENOTSUP; - } - if (IS_ENABLED(CONFIG_MCUX_ELCDIF_PXP_ROTATE_90)) { - pxp_dma.dma_slot |= DMA_MCUX_PXP_CMD(DMA_MCUX_PXP_CMD_ROTATE_90); - } else if (IS_ENABLED(CONFIG_MCUX_ELCDIF_PXP_ROTATE_180)) { - pxp_dma.dma_slot |= DMA_MCUX_PXP_CMD(DMA_MCUX_PXP_CMD_ROTATE_180); - } else if (IS_ENABLED(CONFIG_MCUX_ELCDIF_PXP_ROTATE_270)) { - pxp_dma.dma_slot |= DMA_MCUX_PXP_CMD(DMA_MCUX_PXP_CMD_ROTATE_270); - } else { - pxp_dma.dma_slot |= DMA_MCUX_PXP_CMD(DMA_MCUX_PXP_CMD_ROTATE_0); - } - - /* DMA linked_channel sets the flip direction */ - if (IS_ENABLED(CONFIG_MCUX_ELCDIF_PXP_FLIP_HORIZONTAL)) { - pxp_dma.linked_channel |= DMA_MCUX_PXP_FLIP(DMA_MCUX_PXP_FLIP_HORIZONTAL); - } else if (IS_ENABLED(CONFIG_MCUX_ELCDIF_PXP_FLIP_VERTICAL)) { - pxp_dma.linked_channel |= DMA_MCUX_PXP_FLIP(DMA_MCUX_PXP_FLIP_VERTICAL); - } else if (IS_ENABLED(CONFIG_MCUX_ELCDIF_PXP_FLIP_BOTH)) { - pxp_dma.linked_channel |= DMA_MCUX_PXP_FLIP(DMA_MCUX_PXP_FLIP_BOTH); - } else { - pxp_dma.linked_channel |= DMA_MCUX_PXP_FLIP(DMA_MCUX_PXP_FLIP_DISABLE); - } - - pxp_dma.channel_direction = MEMORY_TO_MEMORY; - pxp_dma.source_data_size = desc->width * dev_data->pixel_bytes; - pxp_dma.dest_data_size = config->rgb_mode.panelWidth * dev_data->pixel_bytes; - /* Burst lengths are heights of source/dest buffer in pixels */ - pxp_dma.source_burst_length = desc->height; - pxp_dma.dest_burst_length = config->rgb_mode.panelHeight; - pxp_dma.head_block = &pxp_block; - pxp_dma.dma_callback = mcux_elcdif_pxp_callback; - pxp_dma.user_data = dev_data; - - ret = dma_config(config->pxp, 0, &pxp_dma); - if (ret < 0) { - return ret; - } - ret = dma_start(config->pxp, 0); - if (ret < 0) { - return ret; - } - k_sem_take(&dev_data->pxp_done, K_FOREVER); - } else { - LOG_WRN("PXP rotation/flip will not work correctly unless a full sized " - "framebuffer is provided"); - } -#endif /* CONFIG_MCUX_ELCDIF_PXP */ - /* Queue next framebuffer */ ELCDIF_SetNextBufferAddr(config->base, (uint32_t)dev_data->active_fb); @@ -348,13 +248,6 @@ static int mcux_elcdif_init(const struct device *dev) #endif /* DT_ANY_INST_HAS_PROP_STATUS_OKAY(backlight_gpios) */ k_sem_init(&dev_data->sem, 0, 1); -#ifdef CONFIG_MCUX_ELCDIF_PXP - k_sem_init(&dev_data->pxp_done, 0, 1); - if (!device_is_ready(config->pxp)) { - LOG_ERR("PXP device is not ready"); - return -ENODEV; - } -#endif config->irq_config_func(dev); @@ -413,8 +306,7 @@ static DEVICE_API(display, mcux_elcdif_api) = { }, \ .pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(id), \ .backlight_gpio = GPIO_DT_SPEC_INST_GET_OR(id, backlight_gpios, {0}), \ - IF_ENABLED(CONFIG_MCUX_ELCDIF_PXP, \ - (.pxp = DEVICE_DT_GET(DT_INST_PHANDLE(id, nxp_pxp)),))}; \ + }; \ static struct mcux_elcdif_data mcux_elcdif_data_##id = { \ .next_idx = 0, \ .pixel_format = DT_INST_PROP(id, pixel_format), \ diff --git a/dts/arm/nxp/nxp_rt10xx.dtsi b/dts/arm/nxp/nxp_rt10xx.dtsi index 80409bcc695e6..65c511292c809 100644 --- a/dts/arm/nxp/nxp_rt10xx.dtsi +++ b/dts/arm/nxp/nxp_rt10xx.dtsi @@ -452,7 +452,6 @@ reg = <0x402b8000 0x4000>; interrupts = <42 0>; status = "disabled"; - nxp,pxp = <&pxp>; }; lpspi1: spi@40394000 { diff --git a/dts/arm/nxp/nxp_rt11xx.dtsi b/dts/arm/nxp/nxp_rt11xx.dtsi index 96daa91b6adfe..8cb9c91877aab 100644 --- a/dts/arm/nxp/nxp_rt11xx.dtsi +++ b/dts/arm/nxp/nxp_rt11xx.dtsi @@ -371,7 +371,6 @@ reg = <0x40804000 0x4000>; interrupts = <54 0>; status = "disabled"; - nxp,pxp = <&pxp>; }; mipi_dsi: mipi-dsi@4080c000 { diff --git a/samples/drivers/video/capture/boards/mimxrt1170_evk_mimxrt1176_cm7.conf b/samples/drivers/video/capture/boards/mimxrt1170_evk_mimxrt1176_cm7.conf index 7252f9e67c5cb..7ee3d1a2232bb 100644 --- a/samples/drivers/video/capture/boards/mimxrt1170_evk_mimxrt1176_cm7.conf +++ b/samples/drivers/video/capture/boards/mimxrt1170_evk_mimxrt1176_cm7.conf @@ -1,4 +1 @@ CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=3686800 -CONFIG_DMA=y -CONFIG_MCUX_ELCDIF_PXP=y -CONFIG_MCUX_ELCDIF_PXP_ROTATE_90=y From 065487a41a1c42c6a5e5f4b40d737fcce449c80d Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Thu, 26 Jun 2025 12:16:53 +0200 Subject: [PATCH 2/5] drivers: dma: Drop NXP's PxP driver Drop the old dma PxP driver to favor the new one in v4z m2m category. Signed-off-by: Phi Bang Nguyen --- drivers/display/Kconfig.mcux_elcdif | 80 ------- drivers/dma/CMakeLists.txt | 1 - drivers/dma/Kconfig | 2 - drivers/dma/Kconfig.mcux_pxp | 10 - drivers/dma/dma_mcux_pxp.c | 224 ------------------ dts/bindings/dma/nxp,pxp.yaml | 20 -- include/zephyr/drivers/dma/dma_mcux_pxp.h | 57 ----- .../lvgl/demos/boards/mimxrt1060_evk.conf | 8 - .../mimxrt1170_evk_mimxrt1176_cm7_A.conf | 8 - .../display/lvgl/boards/mimxrt1060_evk.conf | 8 - .../mimxrt1170_evk_mimxrt1176_cm7_A.conf | 8 - 11 files changed, 426 deletions(-) delete mode 100644 drivers/dma/Kconfig.mcux_pxp delete mode 100644 drivers/dma/dma_mcux_pxp.c delete mode 100644 dts/bindings/dma/nxp,pxp.yaml delete mode 100644 include/zephyr/drivers/dma/dma_mcux_pxp.h delete mode 100644 samples/modules/lvgl/demos/boards/mimxrt1060_evk.conf delete mode 100644 samples/modules/lvgl/demos/boards/mimxrt1170_evk_mimxrt1176_cm7_A.conf delete mode 100644 samples/subsys/display/lvgl/boards/mimxrt1060_evk.conf delete mode 100644 samples/subsys/display/lvgl/boards/mimxrt1170_evk_mimxrt1176_cm7_A.conf diff --git a/drivers/display/Kconfig.mcux_elcdif b/drivers/display/Kconfig.mcux_elcdif index e9b48f761449e..f0354ee3e67b0 100644 --- a/drivers/display/Kconfig.mcux_elcdif +++ b/drivers/display/Kconfig.mcux_elcdif @@ -43,84 +43,4 @@ config MCUX_ELCDIF_FB_SIZE 4-bytes pixel format, e.g. ARGB8888. Applications should change this value according to the actual used resolution and format to optimize the heap size. -config MCUX_ELCDIF_PXP - bool "Use PXP for display rotation" - depends on MCUX_PXP - depends on (MCUX_ELCDIF_FB_NUM > 0) - help - Use the PXP for display rotation. This requires the LCDIF node - have a "nxp,pxp" devicetree property pointing to the PXP device node. - The ELCDIF will only utilize the PXP to rotate frames if - display_write is called with a framebuffer equal in size to the - display. - -if MCUX_ELCDIF_PXP - -choice MCUX_ELCDIF_PXP_ROTATE_DIRECTION - default MCUX_ELCDIF_PXP_ROTATE_0 - prompt "Rotation angle of PXP" - help - Set rotation angle of PXP. The ELCDIF cannot detect the correct - rotation angle based on the call to display_write, so the user should - configure it here. In order for PXP rotation to work, calls to - display_write MUST supply a framebuffer equal in size to screen width - and height (without rotation applied). Note that the width and - height settings of the screen in devicetree should not be modified - from their values in the default screen orientation when using this - functionality. - -config MCUX_ELCDIF_PXP_ROTATE_0 - bool "Rotate display by 0 degrees" - help - Rotate display by 0 degrees. Primarily useful for testing, - production applications should simply disable the PXP. - -config MCUX_ELCDIF_PXP_ROTATE_90 - bool "Rotate display by 90 degrees" - help - Rotate display counter-clockwise by 90 degrees. - For LVGL, this corresponds to a rotation of 270 degrees - -config MCUX_ELCDIF_PXP_ROTATE_180 - bool "Rotate display by 180 degrees" - help - Rotate display counter-clockwise by 180 degrees - -config MCUX_ELCDIF_PXP_ROTATE_270 - bool "Rotate display by 270 degrees" - help - Rotate display counter-clockwise by 270 degrees - For LVGL, this corresponds to a rotation of 90 degrees - -endchoice - -choice MCUX_ELCDIF_PXP_FLIP_DIRECTION - default MCUX_ELCDIF_PXP_FLIP_DISABLE - prompt "Flip direction of PXP" - help - Set flip direction of PXP. The ELCDIF cannot detect the correct - rotation angle based on the call to display_write, so the user should - configure it here. In order for PXP flip to work, calls to - display_write MUST supply a framebuffer equal in size to screen width - and height (without flip applied). Note that the width and - height settings of the screen in devicetree should not be modified - from their values in the default screen orientation when using this - functionality. - -config MCUX_ELCDIF_PXP_FLIP_DISABLE - bool "Do not flip display" - -config MCUX_ELCDIF_PXP_FLIP_HORIZONTAL - bool "Flip display horizontally" - -config MCUX_ELCDIF_PXP_FLIP_VERTICAL - bool "Flip display vertically" - -config MCUX_ELCDIF_PXP_FLIP_BOTH - bool "Flib display both horizontally and vertically" - -endchoice - -endif # MCUX_ELCDIF_PXP - endif # DISPLAY_MCUX_ELCDIF diff --git a/drivers/dma/CMakeLists.txt b/drivers/dma/CMakeLists.txt index 0c15fffea6fc5..67d8ae2dab207 100644 --- a/drivers/dma/CMakeLists.txt +++ b/drivers/dma/CMakeLists.txt @@ -35,7 +35,6 @@ zephyr_library_sources_ifdef(CONFIG_DMA_MCHP_XEC dma_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_DMA_XMC4XXX dma_xmc4xxx.c) zephyr_library_sources_ifdef(CONFIG_DMA_RPI_PICO dma_rpi_pico.c) zephyr_library_sources_ifdef(CONFIG_DMA_RENESAS_RZ dma_renesas_rz.c) -zephyr_library_sources_ifdef(CONFIG_MCUX_PXP dma_mcux_pxp.c) zephyr_library_sources_ifdef(CONFIG_DMA_MAX32 dma_max32.c) zephyr_library_sources_ifdef(CONFIG_DMA_MCUX_SMARTDMA dma_mcux_smartdma.c) zephyr_library_sources_ifdef(CONFIG_DMA_ANDES_ATCDMAC300 dma_andes_atcdmac300.c) diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 0bba13fb04f3f..4b6470f2d48a8 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -64,8 +64,6 @@ source "drivers/dma/Kconfig.ifx_cat1" source "drivers/dma/Kconfig.intel_lpss" -source "drivers/dma/Kconfig.mcux_pxp" - source "drivers/dma/Kconfig.max32" source "drivers/dma/Kconfig.mcux_smartdma" diff --git a/drivers/dma/Kconfig.mcux_pxp b/drivers/dma/Kconfig.mcux_pxp deleted file mode 100644 index b00148304cffb..0000000000000 --- a/drivers/dma/Kconfig.mcux_pxp +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright 2023 NXP -# SPDX-License-Identifier: Apache-2.0 - -config MCUX_PXP - bool "MCUX PXP DMA driver" - default y - depends on DT_HAS_NXP_PXP_ENABLED - depends on DISPLAY - help - PXP DMA driver for NXP SOCs diff --git a/drivers/dma/dma_mcux_pxp.c b/drivers/dma/dma_mcux_pxp.c deleted file mode 100644 index b0b2df1aa68fe..0000000000000 --- a/drivers/dma/dma_mcux_pxp.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2023-2024 NXP - * All rights reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -#include -#ifdef CONFIG_HAS_MCUX_CACHE -#include -#endif - -#define DT_DRV_COMPAT nxp_pxp - -#include -LOG_MODULE_REGISTER(dma_mcux_pxp, CONFIG_DMA_LOG_LEVEL); - -struct dma_mcux_pxp_config { - PXP_Type *base; - void (*irq_config_func)(const struct device *dev); -}; - -struct dma_mcux_pxp_data { - void *user_data; - dma_callback_t dma_callback; - uint32_t ps_buf_addr; - uint32_t ps_buf_size; - uint32_t out_buf_addr; - uint32_t out_buf_size; -}; - -static void dma_mcux_pxp_irq_handler(const struct device *dev) -{ - const struct dma_mcux_pxp_config *config = dev->config; - struct dma_mcux_pxp_data *data = dev->data; - - PXP_ClearStatusFlags(config->base, kPXP_CompleteFlag); -#ifdef CONFIG_HAS_MCUX_CACHE - DCACHE_InvalidateByRange((uint32_t)data->out_buf_addr, data->out_buf_size); -#endif - if (data->dma_callback) { - data->dma_callback(dev, data->user_data, 0, 0); - } -} - -/* Configure a channel */ -static int dma_mcux_pxp_configure(const struct device *dev, uint32_t channel, - struct dma_config *config) -{ - const struct dma_mcux_pxp_config *dev_config = dev->config; - struct dma_mcux_pxp_data *dev_data = dev->data; - pxp_ps_buffer_config_t ps_buffer_cfg; - pxp_output_buffer_config_t output_buffer_cfg; - uint8_t bytes_per_pixel; - pxp_rotate_degree_t rotate; - pxp_flip_mode_t flip; - - ARG_UNUSED(channel); - if (config->channel_direction != MEMORY_TO_MEMORY) { - return -ENOTSUP; - } - /* - * Use the DMA slot value to get the pixel format and rotation - * settings - */ - switch ((config->dma_slot & DMA_MCUX_PXP_CMD_MASK) >> DMA_MCUX_PXP_CMD_SHIFT) { - case DMA_MCUX_PXP_CMD_ROTATE_0: - rotate = kPXP_Rotate0; - break; - case DMA_MCUX_PXP_CMD_ROTATE_90: - rotate = kPXP_Rotate90; - break; - case DMA_MCUX_PXP_CMD_ROTATE_180: - rotate = kPXP_Rotate180; - break; - case DMA_MCUX_PXP_CMD_ROTATE_270: - rotate = kPXP_Rotate270; - break; - default: - return -ENOTSUP; - } - switch ((config->dma_slot & DMA_MCUX_PXP_FMT_MASK) >> DMA_MCUX_PXP_FMT_SHIFT) { - case DMA_MCUX_PXP_FMT_RGB565: - ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatRGB565; - output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatRGB565; - bytes_per_pixel = 2; - break; - case DMA_MCUX_PXP_FMT_RGB888: -#if (!(defined(FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT) && \ - FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT)) && \ - (!(defined(FSL_FEATURE_PXP_V3) && FSL_FEATURE_PXP_V3)) - ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatARGB8888; -#else - ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatRGB888; -#endif - output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatRGB888; - bytes_per_pixel = 3; - break; - case DMA_MCUX_PXP_FMT_ARGB8888: - ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatARGB8888; - output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatARGB8888; - bytes_per_pixel = 4; - break; - default: - return -ENOTSUP; - } - /* - * Use the DMA linked_channel value to get the flip settings. - */ - switch ((config->linked_channel & DMA_MCUX_PXP_FLIP_MASK) >> DMA_MCUX_PXP_FLIP_SHIFT) { - case DMA_MCUX_PXP_FLIP_DISABLE: - flip = kPXP_FlipDisable; - break; - case DMA_MCUX_PXP_FLIP_HORIZONTAL: - flip = kPXP_FlipHorizontal; - break; - case DMA_MCUX_PXP_FLIP_VERTICAL: - flip = kPXP_FlipVertical; - break; - case DMA_MCUX_PXP_FLIP_BOTH: - flip = kPXP_FlipBoth; - break; - default: - return -ENOTSUP; - } - DCACHE_CleanByRange((uint32_t)config->head_block->source_address, - config->head_block->block_size); - - /* - * Some notes on how specific fields of the DMA config are used by - * the PXP: - * head block source address: PS buffer source address - * head block destination address: Output buffer address - * head block block size: size of destination and source buffer - * source data size: width of source buffer in bytes (pitch) - * source burst length: height of source buffer in pixels - * dest data size: width of destination buffer in bytes (pitch) - * dest burst length: height of destination buffer in pixels - */ - ps_buffer_cfg.swapByte = false; - ps_buffer_cfg.bufferAddr = config->head_block->source_address; - ps_buffer_cfg.bufferAddrU = 0U; - ps_buffer_cfg.bufferAddrV = 0U; - ps_buffer_cfg.pitchBytes = config->source_data_size; - PXP_SetProcessSurfaceBufferConfig(dev_config->base, &ps_buffer_cfg); - - output_buffer_cfg.interlacedMode = kPXP_OutputProgressive; - output_buffer_cfg.buffer0Addr = config->head_block->dest_address; - output_buffer_cfg.buffer1Addr = 0U; - output_buffer_cfg.pitchBytes = config->dest_data_size; - output_buffer_cfg.width = (config->dest_data_size / bytes_per_pixel); - output_buffer_cfg.height = config->dest_burst_length; - PXP_SetOutputBufferConfig(dev_config->base, &output_buffer_cfg); - /* We only support a process surface that covers the full buffer */ - PXP_SetProcessSurfacePosition(dev_config->base, 0U, 0U, output_buffer_cfg.width, - output_buffer_cfg.height); - /* Setup rotation */ - PXP_SetRotateConfig(dev_config->base, kPXP_RotateProcessSurface, rotate, flip); - - dev_data->ps_buf_addr = config->head_block->source_address; - dev_data->ps_buf_size = config->head_block->block_size; - dev_data->out_buf_addr = config->head_block->dest_address; - dev_data->out_buf_size = config->head_block->block_size; - dev_data->dma_callback = config->dma_callback; - dev_data->user_data = config->user_data; - return 0; -} - -static int dma_mcux_pxp_start(const struct device *dev, uint32_t channel) -{ - const struct dma_mcux_pxp_config *config = dev->config; - struct dma_mcux_pxp_data *data = dev->data; -#ifdef CONFIG_HAS_MCUX_CACHE - DCACHE_CleanByRange((uint32_t)data->ps_buf_addr, data->ps_buf_size); -#endif - - ARG_UNUSED(channel); - PXP_Start(config->base); - return 0; -} - -static DEVICE_API(dma, dma_mcux_pxp_api) = { - .config = dma_mcux_pxp_configure, - .start = dma_mcux_pxp_start, -}; - -static int dma_mcux_pxp_init(const struct device *dev) -{ - const struct dma_mcux_pxp_config *config = dev->config; - - PXP_Init(config->base); - PXP_SetProcessSurfaceBackGroundColor(config->base, 0U); - /* Disable alpha surface and CSC1 */ - PXP_SetAlphaSurfacePosition(config->base, 0xFFFFU, 0xFFFFU, 0U, 0U); - PXP_EnableCsc1(config->base, false); - PXP_EnableInterrupts(config->base, kPXP_CompleteInterruptEnable); - config->irq_config_func(dev); - return 0; -} - -#define DMA_INIT(n) \ - static void dma_pxp_config_func##n(const struct device *dev) \ - { \ - IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 0), \ - (IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), \ - dma_mcux_pxp_irq_handler, DEVICE_DT_INST_GET(n), 0); \ - irq_enable(DT_INST_IRQ(n, irq));)) \ - } \ - \ - static const struct dma_mcux_pxp_config dma_config_##n = { \ - .base = (PXP_Type *)DT_INST_REG_ADDR(n), \ - .irq_config_func = dma_pxp_config_func##n, \ - }; \ - \ - static struct dma_mcux_pxp_data dma_data_##n; \ - \ - DEVICE_DT_INST_DEFINE(n, dma_mcux_pxp_init, NULL, &dma_data_##n, &dma_config_##n, \ - PRE_KERNEL_1, CONFIG_DMA_INIT_PRIORITY, &dma_mcux_pxp_api); - -DT_INST_FOREACH_STATUS_OKAY(DMA_INIT) diff --git a/dts/bindings/dma/nxp,pxp.yaml b/dts/bindings/dma/nxp,pxp.yaml deleted file mode 100644 index 92fbc6be795de..0000000000000 --- a/dts/bindings/dma/nxp,pxp.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright 2023 NXP -# SPDX-License-Identifier: Apache-2.0 - -description: NXP PXP 2D DMA engine - -compatible: "nxp,pxp" - -include: dma-controller.yaml - -properties: - reg: - required: true - - interrupts: - required: true - - "#dma-cells": - type: int - required: true - const: 0 diff --git a/include/zephyr/drivers/dma/dma_mcux_pxp.h b/include/zephyr/drivers/dma/dma_mcux_pxp.h deleted file mode 100644 index 47082a947462c..0000000000000 --- a/include/zephyr/drivers/dma/dma_mcux_pxp.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2023-2024 NXP - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef ZEPHYR_INCLUDE_DRIVERS_DMA_MCUX_PXP_H_ -#define ZEPHYR_INCLUDE_DRIVERS_DMA_MCUX_PXP_H_ - -#define DMA_MCUX_PXP_CMD_MASK 0xE0 -#define DMA_MCUX_PXP_CMD_SHIFT 0x5 - -#define DMA_MCUX_PXP_FMT_MASK 0x1F -#define DMA_MCUX_PXP_FMT_SHIFT 0x0 - -/* - * In order to configure the PXP for rotation, the user should - * supply a format and command as the DMA slot parameter, like so: - * dma_slot = (DMA_MCUX_PXP_FTM(DMA_MCUX_PXP_FMT_RGB565) | - * DMA_MCUX_PXP_CMD(DMA_MCUX_PXP_CMD_ROTATE_90)) - * head block source address: input buffer address - * head block destination address: output buffer address - * source data size: input buffer size in bytes - * source burst length: height of source buffer in pixels - * dest data size: output buffer size in bytes - * dest burst length: height of destination buffer in pixels - */ - -#define DMA_MCUX_PXP_FMT(x) ((x << DMA_MCUX_PXP_FMT_SHIFT) & DMA_MCUX_PXP_FMT_MASK) -#define DMA_MCUX_PXP_CMD(x) ((x << DMA_MCUX_PXP_CMD_SHIFT) & DMA_MCUX_PXP_CMD_MASK) - -#define DMA_MCUX_PXP_CMD_ROTATE_0 0 -#define DMA_MCUX_PXP_CMD_ROTATE_90 1 -#define DMA_MCUX_PXP_CMD_ROTATE_180 2 -#define DMA_MCUX_PXP_CMD_ROTATE_270 3 - -#define DMA_MCUX_PXP_FMT_RGB565 0 -#define DMA_MCUX_PXP_FMT_RGB888 1 -#define DMA_MCUX_PXP_FMT_ARGB8888 2 - -#define DMA_MCUX_PXP_FLIP_MASK 0x3 -#define DMA_MCUX_PXP_FLIP_SHIFT 0x0 - -/* - * In order to configure the PXP to flip, the user should - * supply a flip setting as the DMA linked_channel parameter, like so: - * linked_channel |= DMA_MCUX_PXP_FLIP(DMA_MCUX_PXP_FLIP_HORIZONTAL) - */ - -#define DMA_MCUX_PXP_FLIP(x) ((x << DMA_MCUX_PXP_FLIP_SHIFT) & DMA_MCUX_PXP_FLIP_MASK) - -#define DMA_MCUX_PXP_FLIP_DISABLE 0 -#define DMA_MCUX_PXP_FLIP_HORIZONTAL 1 -#define DMA_MCUX_PXP_FLIP_VERTICAL 2 -#define DMA_MCUX_PXP_FLIP_BOTH 3 - -#endif /* ZEPHYR_INCLUDE_DRIVERS_DMA_MCUX_PXP_H_ */ diff --git a/samples/modules/lvgl/demos/boards/mimxrt1060_evk.conf b/samples/modules/lvgl/demos/boards/mimxrt1060_evk.conf deleted file mode 100644 index 30b4edca85841..0000000000000 --- a/samples/modules/lvgl/demos/boards/mimxrt1060_evk.conf +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2023 Fabian Blatz -# SPDX-License-Identifier: Apache-2.0 - -# Enable PXP DMA engine and set rotation angle to 0 degrees. -# This allows us to verify the DMA driver functions without altering -# the output image -CONFIG_DMA=y -CONFIG_MCUX_ELCDIF_PXP=y diff --git a/samples/modules/lvgl/demos/boards/mimxrt1170_evk_mimxrt1176_cm7_A.conf b/samples/modules/lvgl/demos/boards/mimxrt1170_evk_mimxrt1176_cm7_A.conf deleted file mode 100644 index 30b4edca85841..0000000000000 --- a/samples/modules/lvgl/demos/boards/mimxrt1170_evk_mimxrt1176_cm7_A.conf +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2023 Fabian Blatz -# SPDX-License-Identifier: Apache-2.0 - -# Enable PXP DMA engine and set rotation angle to 0 degrees. -# This allows us to verify the DMA driver functions without altering -# the output image -CONFIG_DMA=y -CONFIG_MCUX_ELCDIF_PXP=y diff --git a/samples/subsys/display/lvgl/boards/mimxrt1060_evk.conf b/samples/subsys/display/lvgl/boards/mimxrt1060_evk.conf deleted file mode 100644 index 33e1d5275a880..0000000000000 --- a/samples/subsys/display/lvgl/boards/mimxrt1060_evk.conf +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2023 NXP -# SPDX-License-Identifier: Apache-2.0 - -# Enable PXP DMA engine and set rotation angle to 0 degrees. -# This allows us to verify the DMA driver functions without altering -# the output image -CONFIG_DMA=y -CONFIG_MCUX_ELCDIF_PXP=y diff --git a/samples/subsys/display/lvgl/boards/mimxrt1170_evk_mimxrt1176_cm7_A.conf b/samples/subsys/display/lvgl/boards/mimxrt1170_evk_mimxrt1176_cm7_A.conf deleted file mode 100644 index 33e1d5275a880..0000000000000 --- a/samples/subsys/display/lvgl/boards/mimxrt1170_evk_mimxrt1176_cm7_A.conf +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright 2023 NXP -# SPDX-License-Identifier: Apache-2.0 - -# Enable PXP DMA engine and set rotation angle to 0 degrees. -# This allows us to verify the DMA driver functions without altering -# the output image -CONFIG_DMA=y -CONFIG_MCUX_ELCDIF_PXP=y From bad94d325627e4147738e2f68c6873907a321bdc Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Wed, 25 Jun 2025 01:44:27 +0200 Subject: [PATCH 3/5] video: controls: Add VIDEO_CID_ROTATE Add VIDEO_CID_ROTATE which is needed for some m2m devices. Signed-off-by: Phi Bang Nguyen --- drivers/video/video_ctrls.c | 2 ++ include/zephyr/drivers/video-controls.h | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/video/video_ctrls.c b/drivers/video/video_ctrls.c index cba92100967c2..93f9e94057644 100644 --- a/drivers/video/video_ctrls.c +++ b/drivers/video/video_ctrls.c @@ -488,6 +488,8 @@ static inline const char *video_get_ctrl_name(uint32_t id) return "Brightness, Automatic"; case VIDEO_CID_BAND_STOP_FILTER: return "Band-Stop Filter"; + case VIDEO_CID_ROTATE: + return "Rotate"; case VIDEO_CID_ALPHA_COMPONENT: return "Alpha Component"; diff --git a/include/zephyr/drivers/video-controls.h b/include/zephyr/drivers/video-controls.h index 21c173c45980d..abc5abdb9c784 100644 --- a/include/zephyr/drivers/video-controls.h +++ b/include/zephyr/drivers/video-controls.h @@ -134,7 +134,7 @@ enum video_colorfx { VIDEO_COLORFX_ANTIQUE = 14, }; -/* Enable Automatic Brightness. */ +/** Enable Automatic Brightness. */ #define VIDEO_CID_AUTOBRIGHTNESS (VIDEO_CID_BASE + 32) /** Switch the band-stop filter of a camera sensor on or off, or specify its strength. @@ -142,6 +142,11 @@ enum video_colorfx { */ #define VIDEO_CID_BAND_STOP_FILTER (VIDEO_CID_BASE + 33) +/** Rotate the image by a given angle, e.g. 90, 180, 270 degree. The application will be then + * responsible for setting the new width and height of the image using video_set_fmt() if needed. + */ +#define VIDEO_CID_ROTATE (VIDEO_CID_BASE + 34) + /** Sets the alpha color component. * Some devices produce data with a user-controllable alpha component. Set the value applied to * the alpha channel of every pixel produced. From 81acde236684f907e84e8b5462229675c3331944 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Sun, 29 Jun 2025 18:11:53 +0200 Subject: [PATCH 4/5] drivers: video: Add PxP m2m driver The NXP's PxP is a 2D pixel engine which is capable to do some image transformation such as rotation, flipping, color conversion, etc. Make a driver for it in the video m2m category. Signed-off-by: Phi Bang Nguyen --- drivers/video/CMakeLists.txt | 1 + drivers/video/Kconfig | 2 + drivers/video/Kconfig.mcux_pxp | 9 + drivers/video/video_mcux_pxp.c | 334 ++++++++++++++++++ dts/arm/nxp/nxp_rt10xx.dtsi | 1 - dts/arm/nxp/nxp_rt11xx.dtsi | 1 - dts/bindings/video/nxp,pxp.yaml | 18 + .../mcux/mcux-sdk-ng/drivers/drivers.cmake | 2 +- 8 files changed, 365 insertions(+), 3 deletions(-) create mode 100644 drivers/video/Kconfig.mcux_pxp create mode 100644 drivers/video/video_mcux_pxp.c create mode 100644 dts/bindings/video/nxp,pxp.yaml diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index 083df586b0033..fe8268bfa6431 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -8,6 +8,7 @@ zephyr_library_sources(video_device.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_CSI video_mcux_csi.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_MIPI_CSI2RX video_mcux_mipi_csi2rx.c) +zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_PXP video_mcux_pxp.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_SHELL video_shell.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_SW_GENERATOR video_sw_generator.c) zephyr_library_sources_ifdef(CONFIG_VIDEO_MT9M114 mt9m114.c) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 924e2156273c4..93c99e14164ac 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -64,6 +64,8 @@ source "drivers/video/Kconfig.mcux_csi" source "drivers/video/Kconfig.mcux_mipi_csi2rx" +source "drivers/video/Kconfig.mcux_pxp" + source "drivers/video/Kconfig.shell" source "drivers/video/Kconfig.sw_generator" diff --git a/drivers/video/Kconfig.mcux_pxp b/drivers/video/Kconfig.mcux_pxp new file mode 100644 index 0000000000000..c3b57ac73459d --- /dev/null +++ b/drivers/video/Kconfig.mcux_pxp @@ -0,0 +1,9 @@ +# NXP PxP driver configuration option + +# Copyright 2025 NXP +# SPDX-License-Identifier: Apache-2.0 + +config VIDEO_MCUX_PXP + bool "NXP MCUX PxP driver" + default y + depends on DT_HAS_NXP_PXP_ENABLED diff --git a/drivers/video/video_mcux_pxp.c b/drivers/video/video_mcux_pxp.c new file mode 100644 index 0000000000000..28259b0dbaa66 --- /dev/null +++ b/drivers/video/video_mcux_pxp.c @@ -0,0 +1,334 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "video_ctrls.h" +#include "video_device.h" + +#include +#ifdef CONFIG_HAS_MCUX_CACHE +#include +#endif + +#define DT_DRV_COMPAT nxp_pxp + +LOG_MODULE_REGISTER(mcux_pxp, CONFIG_VIDEO_LOG_LEVEL); + +struct mcux_pxp_config { + PXP_Type *base; + void (*irq_config_func)(void); +}; + +struct mcux_pxp_ctrls { + struct { + struct video_ctrl rotation; + struct video_ctrl hflip; + struct video_ctrl vflip; + }; +}; + +struct mcux_pxp_data { + struct mcux_pxp_ctrls ctrls; + pxp_ps_buffer_config_t ps_buffer_cfg; + pxp_output_buffer_config_t output_buffer_cfg; + struct k_fifo fifo_in_queued; + struct k_fifo fifo_in_done; + struct k_fifo fifo_out_queued; + struct k_fifo fifo_out_done; +}; + +static void mcux_pxp_isr(const struct device *const dev) +{ + const struct mcux_pxp_config *config = dev->config; + struct mcux_pxp_data *data = dev->data; + + PXP_ClearStatusFlags(config->base, kPXP_CompleteFlag); + + struct video_buffer *vbuf_in = k_fifo_get(&data->fifo_in_queued, K_NO_WAIT); + struct video_buffer *vbuf_out = k_fifo_get(&data->fifo_out_queued, K_NO_WAIT); + + /* Something went wrong, should never happen */ + if ((uint32_t)vbuf_in->buffer != data->ps_buffer_cfg.bufferAddr || + (uint32_t)vbuf_out->buffer != data->output_buffer_cfg.buffer0Addr) { + return; + } + + vbuf_out->timestamp = k_uptime_get_32(); + vbuf_out->bytesused = data->output_buffer_cfg.pitchBytes * data->output_buffer_cfg.height; + +#ifdef CONFIG_HAS_MCUX_CACHE + DCACHE_InvalidateByRange((uint32_t)vbuf_out->buffer, vbuf_out->bytesused); +#endif + + k_fifo_put(&data->fifo_in_done, vbuf_in); + k_fifo_put(&data->fifo_out_done, vbuf_out); +} + +static int mcux_pxp_get_caps(const struct device *dev, struct video_caps *caps) +{ + /* TODO */ + return 0; +} + +static int mcux_pxp_get_fmt(const struct device *dev, struct video_format *fmt) +{ + /* TODO */ + return 0; +} + +static int mcux_pxp_set_fmt(const struct device *dev, struct video_format *fmt) +{ + struct mcux_pxp_data *data = dev->data; + + fmt->pitch = fmt->width * video_bits_per_pixel(fmt->pixelformat) / BITS_PER_BYTE; + + switch (fmt->type) { + case VIDEO_BUF_TYPE_INPUT: + data->ps_buffer_cfg.pitchBytes = fmt->pitch; + switch (fmt->pixelformat) { + case VIDEO_PIX_FMT_RGB565: + data->ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatRGB565; + break; + case VIDEO_PIX_FMT_RGB24: +#if (!(defined(FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT) && \ +FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT)) && \ + (!(defined(FSL_FEATURE_PXP_V3) && FSL_FEATURE_PXP_V3)) + data->ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatARGB8888; +#else + data->ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatRGB888; +#endif + break; + case VIDEO_PIX_FMT_ARGB32: + case VIDEO_PIX_FMT_XRGB32: + data->ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatARGB8888; + break; + default: + return -ENOTSUP; + } + break; + case VIDEO_BUF_TYPE_OUTPUT: + data->output_buffer_cfg.pitchBytes = fmt->pitch; + data->output_buffer_cfg.width = fmt->width; + data->output_buffer_cfg.height = fmt->height; + switch (fmt->pixelformat) { + case VIDEO_PIX_FMT_RGB565: + data->output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatRGB565; + break; + case VIDEO_PIX_FMT_RGB24: + data->output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatRGB888; + break; + case VIDEO_PIX_FMT_XRGB32: + case VIDEO_PIX_FMT_ARGB32: + data->output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatARGB8888; + break; + default: + return -ENOTSUP; + } + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int mcux_pxp_set_ctrl(const struct device *dev, uint32_t id) +{ + const struct mcux_pxp_config *const config = dev->config; + struct mcux_pxp_data *data = dev->data; + struct mcux_pxp_ctrls *ctrls = &data->ctrls; + pxp_flip_mode_t flip = ctrls->hflip.val | ctrls->vflip.val; + pxp_rotate_degree_t rotate = kPXP_Rotate0; + + ARG_UNUSED(id); + + switch (ctrls->rotation.val) { + case 0: + rotate = kPXP_Rotate0; + break; + case 90: + rotate = kPXP_Rotate90; + break; + case 180: + rotate = kPXP_Rotate180; + break; + case 270: + rotate = kPXP_Rotate270; + break; + } + + PXP_SetRotateConfig(config->base, kPXP_RotateProcessSurface, rotate, flip); + + return 0; +} + +static int mcux_pxp_enqueue(const struct device *dev, struct video_buffer *vbuf) +{ + const struct mcux_pxp_config *const config = dev->config; + struct mcux_pxp_data *data = dev->data; + +#ifdef CONFIG_HAS_MCUX_CACHE + DCACHE_CleanByRange((uint32_t)vbuf->buffer, vbuf->size); +#endif + + switch (vbuf->type) { + case VIDEO_BUF_TYPE_INPUT: + k_fifo_put(&data->fifo_in_queued, vbuf); + + data->ps_buffer_cfg.swapByte = false; + data->ps_buffer_cfg.bufferAddr = (uint32_t)vbuf->buffer; + data->ps_buffer_cfg.bufferAddrU = 0U; + data->ps_buffer_cfg.bufferAddrV = 0U; + + PXP_SetProcessSurfaceBufferConfig(config->base, &data->ps_buffer_cfg); + + break; + case VIDEO_BUF_TYPE_OUTPUT: + k_fifo_put(&data->fifo_out_queued, vbuf); + + data->output_buffer_cfg.interlacedMode = kPXP_OutputProgressive; + data->output_buffer_cfg.buffer0Addr = (uint32_t)vbuf->buffer; + data->output_buffer_cfg.buffer1Addr = 0U; + + PXP_SetOutputBufferConfig(config->base, &data->output_buffer_cfg); + /* We only support a process surface that covers the full buffer */ + PXP_SetProcessSurfacePosition(config->base, 0U, 0U, data->output_buffer_cfg.width, + data->output_buffer_cfg.height); + + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int mcux_pxp_dequeue(const struct device *dev, struct video_buffer **vbuf, + k_timeout_t timeout) +{ + struct mcux_pxp_data *data = dev->data; + + switch ((*vbuf)->type) { + case VIDEO_BUF_TYPE_INPUT: + *vbuf = k_fifo_get(&data->fifo_in_done, timeout); + break; + case VIDEO_BUF_TYPE_OUTPUT: + *vbuf = k_fifo_get(&data->fifo_out_done, timeout); + break; + default: + return -ENOTSUP; + } + + if (*vbuf == NULL) { + return -EAGAIN; + } + + return 0; +} + +static int mcux_pxp_set_stream(const struct device *dev, bool enable, enum video_buf_type type) +{ + const struct mcux_pxp_config *const config = dev->config; + struct mcux_pxp_data *data = dev->data; + + if (enable) { +#ifdef CONFIG_HAS_MCUX_CACHE + DCACHE_CleanByRange((uint32_t)data->ps_buffer_cfg.bufferAddr, + data->output_buffer_cfg.pitchBytes * + data->output_buffer_cfg.height); +#endif + PXP_Start(config->base); + } + + return 0; +} + +static DEVICE_API(video, mcux_pxp_driver_api) = { + .get_caps = mcux_pxp_get_caps, + .get_format = mcux_pxp_get_fmt, + .set_format = mcux_pxp_set_fmt, + .set_ctrl = mcux_pxp_set_ctrl, + .enqueue = mcux_pxp_enqueue, + .dequeue = mcux_pxp_dequeue, + .set_stream = mcux_pxp_set_stream, +}; + +static int mcux_pxp_init(const struct device *const dev) +{ + const struct mcux_pxp_config *const config = dev->config; + struct mcux_pxp_data *data = dev->data; + struct mcux_pxp_ctrls *ctrls = &data->ctrls; + int ret; + + k_fifo_init(&data->fifo_in_queued); + k_fifo_init(&data->fifo_out_queued); + k_fifo_init(&data->fifo_in_done); + k_fifo_init(&data->fifo_out_done); + + PXP_Init(config->base); + + PXP_SetProcessSurfaceBackGroundColor(config->base, 0U); + /* Disable alpha surface and CSC1 */ + PXP_SetAlphaSurfacePosition(config->base, 0xFFFFU, 0xFFFFU, 0U, 0U); + PXP_EnableCsc1(config->base, false); + PXP_EnableInterrupts(config->base, kPXP_CompleteInterruptEnable); + + config->irq_config_func(); + + ret = video_init_ctrl( + &ctrls->rotation, dev, VIDEO_CID_ROTATE, + (struct video_ctrl_range){.min = 0, .max = 270, .step = 90, .def = 0}); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); + if (ret) { + return ret; + } + + ret = video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP, + (struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0}); + if (ret) { + return ret; + } + + video_auto_cluster_ctrl(&ctrls->rotation, 3, false); + + return 0; +} + +#define MCUX_PXP_INIT(inst) \ + static void mcux_pxp_irq_config_##inst(void) \ + { \ + IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), mcux_pxp_isr, \ + DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQN(inst)); \ + } \ + \ + static const struct mcux_pxp_config mcux_pxp_config_##inst = { \ + .base = (PXP_Type *)DT_INST_REG_ADDR(inst), \ + .irq_config_func = mcux_pxp_irq_config_##inst, \ + }; \ + \ + static struct mcux_pxp_data mcux_pxp_data_##inst; \ + \ + DEVICE_DT_INST_DEFINE(inst, &mcux_pxp_init, NULL, &mcux_pxp_data_##inst, \ + &mcux_pxp_config_##inst, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \ + &mcux_pxp_driver_api); \ + \ + VIDEO_DEVICE_DEFINE(pxp_##inst, DEVICE_DT_INST_GET(inst), NULL); + +DT_INST_FOREACH_STATUS_OKAY(MCUX_PXP_INIT) diff --git a/dts/arm/nxp/nxp_rt10xx.dtsi b/dts/arm/nxp/nxp_rt10xx.dtsi index 65c511292c809..c31acd6d8ec08 100644 --- a/dts/arm/nxp/nxp_rt10xx.dtsi +++ b/dts/arm/nxp/nxp_rt10xx.dtsi @@ -987,7 +987,6 @@ reg = <0x402b4000 0x4000>; interrupts = <44 0>; status = "disabled"; - #dma-cells = <0>; }; sai1: sai@40384000 { diff --git a/dts/arm/nxp/nxp_rt11xx.dtsi b/dts/arm/nxp/nxp_rt11xx.dtsi index 8cb9c91877aab..447f6961f41cf 100644 --- a/dts/arm/nxp/nxp_rt11xx.dtsi +++ b/dts/arm/nxp/nxp_rt11xx.dtsi @@ -1063,7 +1063,6 @@ reg = <0x40814000 0x4000>; interrupts = <57 0>; status = "disabled"; - #dma-cells = <0>; }; iomuxcgpr: iomuxcgpr@400e4000 { diff --git a/dts/bindings/video/nxp,pxp.yaml b/dts/bindings/video/nxp,pxp.yaml new file mode 100644 index 0000000000000..97f138215e27f --- /dev/null +++ b/dts/bindings/video/nxp,pxp.yaml @@ -0,0 +1,18 @@ +# +# Copyright 2025 NXP +# +# SPDX-License-Identifier: Apache-2.0 +# + +description: NXP MCUX PxP + +compatible: "nxp,pxp" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true diff --git a/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake b/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake index b794013ca972c..4cb9d6c185311 100644 --- a/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake +++ b/modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake @@ -91,7 +91,7 @@ set_variable_ifdef(CONFIG_WDT_MCUX_WDOG32 CONFIG_MCUX_COMPONENT_driver.wdo set_variable_ifdef(CONFIG_COUNTER_MCUX_GPT CONFIG_MCUX_COMPONENT_driver.gpt) set_variable_ifdef(CONFIG_MCUX_GPT_TIMER CONFIG_MCUX_COMPONENT_driver.gpt) set_variable_ifdef(CONFIG_DISPLAY_MCUX_ELCDIF CONFIG_MCUX_COMPONENT_driver.elcdif) -set_variable_ifdef(CONFIG_MCUX_PXP CONFIG_MCUX_COMPONENT_driver.pxp) +set_variable_ifdef(CONFIG_VIDEO_MCUX_PXP CONFIG_MCUX_COMPONENT_driver.pxp) set_variable_ifdef(CONFIG_LV_USE_GPU_NXP_PXP CONFIG_MCUX_COMPONENT_driver.pxp) set_variable_ifdef(CONFIG_GPIO_MCUX_RGPIO CONFIG_MCUX_COMPONENT_driver.rgpio) set_variable_ifdef(CONFIG_I2S_MCUX_SAI CONFIG_MCUX_COMPONENT_driver.sai) From b4c8edeeb0d0c9e80711c33746ac3a08b3954e68 Mon Sep 17 00:00:00 2001 From: Phi Bang Nguyen Date: Mon, 7 Jul 2025 01:54:45 +0200 Subject: [PATCH 5/5] samples: video: capture: Add support for m2m transformation Sometimnes, additional transforms needs to be applied on the video frame to be able to display it correctly on the LCD. This is to add support for such a transformation. This also adds a 90-degree image rotation using PxP m2m device on i.MX RT1170 as an example of such usecase. Signed-off-by: Phi Bang Nguyen --- samples/drivers/video/capture/CMakeLists.txt | 4 + samples/drivers/video/capture/Kconfig | 5 + .../capture/boards/mimxrt1170_evk/transform.c | 106 ++++++++++++++++++ .../boards/mimxrt1170_evk_mimxrt1176_cm7.conf | 4 +- samples/drivers/video/capture/src/main.c | 47 ++++++-- 5 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 samples/drivers/video/capture/boards/mimxrt1170_evk/transform.c diff --git a/samples/drivers/video/capture/CMakeLists.txt b/samples/drivers/video/capture/CMakeLists.txt index 8863a2350f460..2ccd34a2c0070 100644 --- a/samples/drivers/video/capture/CMakeLists.txt +++ b/samples/drivers/video/capture/CMakeLists.txt @@ -6,3 +6,7 @@ project(video_capture) FILE(GLOB app_sources src/*.c) target_sources(app PRIVATE ${app_sources}) + +if(CONFIG_VIDEO_TRANSFORM) + target_sources(app PRIVATE boards/${BOARD}/transform.c) +endif() diff --git a/samples/drivers/video/capture/Kconfig b/samples/drivers/video/capture/Kconfig index 362a6037a6cf1..0ae1bba09030b 100644 --- a/samples/drivers/video/capture/Kconfig +++ b/samples/drivers/video/capture/Kconfig @@ -58,6 +58,11 @@ config VIDEO_CTRL_VFLIP help If set, mirror the video frame vertically +config VIDEO_TRANSFORM + bool "Perform some m2m transforms on the video frame" + help + If set, the video frame will go through some m2m transforms before being displayed + endmenu source "Kconfig.zephyr" diff --git a/samples/drivers/video/capture/boards/mimxrt1170_evk/transform.c b/samples/drivers/video/capture/boards/mimxrt1170_evk/transform.c new file mode 100644 index 0000000000000..5a51cdb786dfb --- /dev/null +++ b/samples/drivers/video/capture/boards/mimxrt1170_evk/transform.c @@ -0,0 +1,106 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(transform, CONFIG_LOG_DEFAULT_LEVEL); + +static const struct device *transform_dev; +static struct video_buffer *transformed_buffer; + +int video_transform_setup(struct video_format in_fmt, struct video_format *out_fmt) +{ + int ret; + struct video_control ctrl = {.id = VIDEO_CID_ROTATE, .val = 90}; + + transform_dev = DEVICE_DT_GET(DT_NODELABEL(pxp)); + + out_fmt->pixelformat = in_fmt.pixelformat; + out_fmt->width = in_fmt.height; + out_fmt->height = in_fmt.width; + out_fmt->type = VIDEO_BUF_TYPE_OUTPUT; + + /* Set input and output formats */ + in_fmt.type = VIDEO_BUF_TYPE_INPUT; + ret = video_set_format(transform_dev, &in_fmt); + if (ret) { + LOG_ERR("Unable to set input format"); + return ret; + } + + ret = video_set_format(transform_dev, out_fmt); + if (ret) { + LOG_ERR("Unable to set output format"); + return ret; + } + + /* Set controls */ + video_set_ctrl(transform_dev, &ctrl); + + /* Allocate a buffer for output queue */ + transformed_buffer = video_buffer_aligned_alloc(in_fmt.pitch * in_fmt.height, + CONFIG_VIDEO_BUFFER_POOL_ALIGN, K_FOREVER); + + if (transformed_buffer == NULL) { + LOG_ERR("Unable to alloc video buffer"); + return -ENOMEM; + } + + return 0; +} + +int video_transform_process(struct video_buffer *in_buf, struct video_buffer **out_buf) +{ + int err; + struct video_buffer *transformed_buf = &(struct video_buffer){}; + /* Store the input buffer type to recover later */ + enum video_buf_type in_buf_type = in_buf->type; + + in_buf->type = VIDEO_BUF_TYPE_INPUT; + err = video_enqueue(transform_dev, in_buf); + if (err) { + LOG_ERR("Unable to enqueue input buffer"); + return err; + } + + transformed_buffer->type = VIDEO_BUF_TYPE_OUTPUT; + err = video_enqueue(transform_dev, transformed_buffer); + if (err) { + LOG_ERR("Unable to enqueue output buffer"); + return err; + } + + /* Same start for both input and output */ + if (video_stream_start(transform_dev, VIDEO_BUF_TYPE_INPUT)) { + LOG_ERR("Unable to start PxP"); + return 0; + } + + /* Dequeue input and output buffers */ + err = video_dequeue(transform_dev, &in_buf, K_FOREVER); + if (err) { + LOG_ERR("Unable to dequeue PxP video buf in"); + return err; + } + + transformed_buf->type = VIDEO_BUF_TYPE_OUTPUT; + err = video_dequeue(transform_dev, &transformed_buf, K_FOREVER); + if (err) { + LOG_ERR("Unable to dequeue PxP video buf out"); + return err; + } + + *out_buf = transformed_buf; + + /* Keep the input buffer intact */ + in_buf->type = in_buf_type; + + return 0; +} diff --git a/samples/drivers/video/capture/boards/mimxrt1170_evk_mimxrt1176_cm7.conf b/samples/drivers/video/capture/boards/mimxrt1170_evk_mimxrt1176_cm7.conf index 7ee3d1a2232bb..8953c39271efc 100644 --- a/samples/drivers/video/capture/boards/mimxrt1170_evk_mimxrt1176_cm7.conf +++ b/samples/drivers/video/capture/boards/mimxrt1170_evk_mimxrt1176_cm7.conf @@ -1 +1,3 @@ -CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=3686800 +CONFIG_VIDEO_TRANSFORM=y +CONFIG_VIDEO_BUFFER_POOL_SZ_MAX=6530200 +CONFIG_VIDEO_BUFFER_POOL_NUM_MAX=3 diff --git a/samples/drivers/video/capture/src/main.c b/samples/drivers/video/capture/src/main.c index d2131219c5647..a36f5ecf5e76d 100644 --- a/samples/drivers/video/capture/src/main.c +++ b/samples/drivers/video/capture/src/main.c @@ -26,6 +26,22 @@ LOG_MODULE_REGISTER(main, CONFIG_LOG_DEFAULT_LEVEL); #error No camera chosen in devicetree. Missing "--shield" or "--snippet video-sw-generator" flag? #endif +#define NUM_BUFS 2 + +int __weak video_transform_setup(struct video_format in_fmt, struct video_format *out_fmt) +{ + *out_fmt = in_fmt; + + return 0; +} + +int __weak video_transform_process(struct video_buffer *in_buf, struct video_buffer **out_buf) +{ + *out_buf = in_buf; + + return 0; +} + #if DT_HAS_CHOSEN(zephyr_display) static inline int display_setup(const struct device *const display_dev, const uint32_t pixfmt) { @@ -91,10 +107,11 @@ static inline void video_display_frame(const struct device *const display_dev, int main(void) { - struct video_buffer *buffers[CONFIG_VIDEO_BUFFER_POOL_NUM_MAX]; struct video_buffer *vbuf = &(struct video_buffer){}; + struct video_buffer *transformed_buf = &(struct video_buffer){}; const struct device *video_dev; struct video_format fmt; + struct video_format transformed_fmt; struct video_caps caps; struct video_frmival frmival; struct video_frmival_enum fie; @@ -262,6 +279,13 @@ int main(void) tp_set_ret = video_set_ctrl(video_dev, &ctrl); } + /* Set up transformation */ + err = video_transform_setup(fmt, &transformed_fmt); + if (err) { + LOG_ERR("Unable to set up transformation"); + return 0; + } + #if DT_HAS_CHOSEN(zephyr_display) const struct device *const display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); @@ -270,7 +294,7 @@ int main(void) return 0; } - err = display_setup(display_dev, fmt.pixelformat); + err = display_setup(display_dev, transformed_fmt.pixelformat); if (err) { LOG_ERR("Unable to set up display"); return err; @@ -285,19 +309,19 @@ int main(void) } /* Alloc video buffers and enqueue for capture */ - for (i = 0; i < ARRAY_SIZE(buffers); i++) { + for (i = 0; i < NUM_BUFS; i++) { /* * For some hardwares, such as the PxP used on i.MX RT1170 to do image rotation, * buffer alignment is needed in order to achieve the best performance */ - buffers[i] = video_buffer_aligned_alloc(bsize, CONFIG_VIDEO_BUFFER_POOL_ALIGN, + vbuf = video_buffer_aligned_alloc(bsize, CONFIG_VIDEO_BUFFER_POOL_ALIGN, K_FOREVER); - if (buffers[i] == NULL) { + if (vbuf == NULL) { LOG_ERR("Unable to alloc video buffer"); return 0; } - buffers[i]->type = type; - video_enqueue(video_dev, buffers[i]); + vbuf->type = type; + video_enqueue(video_dev, vbuf); } /* Start video capture */ @@ -328,8 +352,15 @@ int main(void) } #endif + /* Do transformation */ + err = video_transform_process(vbuf, &transformed_buf); + if (err) { + LOG_ERR("Unable to transform video frame"); + return 0; + } + #if DT_HAS_CHOSEN(zephyr_display) - video_display_frame(display_dev, vbuf, fmt); + video_display_frame(display_dev, transformed_buf, transformed_fmt); #endif err = video_enqueue(video_dev, vbuf);