Skip to content

Commit 7ab4cee

Browse files
committed
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 <phibang.nguyen@nxp.com>
1 parent 1f9ede4 commit 7ab4cee

File tree

8 files changed

+353
-3
lines changed

8 files changed

+353
-3
lines changed

drivers/video/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ zephyr_library_sources(video_device.c)
88

99
zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_CSI video_mcux_csi.c)
1010
zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_MIPI_CSI2RX video_mcux_mipi_csi2rx.c)
11+
zephyr_library_sources_ifdef(CONFIG_VIDEO_MCUX_PXP video_mcux_pxp.c)
1112
zephyr_library_sources_ifdef(CONFIG_VIDEO_SHELL video_shell.c)
1213
zephyr_library_sources_ifdef(CONFIG_VIDEO_SW_GENERATOR video_sw_generator.c)
1314
zephyr_library_sources_ifdef(CONFIG_VIDEO_MT9M114 mt9m114.c)

drivers/video/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ source "drivers/video/Kconfig.mcux_csi"
6464

6565
source "drivers/video/Kconfig.mcux_mipi_csi2rx"
6666

67+
source "drivers/video/Kconfig.mcux_pxp"
68+
6769
source "drivers/video/Kconfig.shell"
6870

6971
source "drivers/video/Kconfig.sw_generator"

drivers/video/Kconfig.mcux_pxp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# NXP PxP driver configuration option
2+
3+
# Copyright 2025 NXP
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
config VIDEO_MCUX_PXP
7+
bool "NXP MCUX PxP driver"
8+
default y
9+
depends on DT_HAS_NXP_PXP_ENABLED

drivers/video/video_mcux_pxp.c

Lines changed: 322 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
/*
2+
* Copyright 2025 NXP
3+
* All rights reserved.
4+
*
5+
* SPDX-License-Identifier: Apache-2.0
6+
*/
7+
8+
#include <errno.h>
9+
10+
#include <zephyr/drivers/video.h>
11+
#include <zephyr/drivers/video-controls.h>
12+
#include <zephyr/dt-bindings/video/video-interfaces.h>
13+
#include <zephyr/irq.h>
14+
#include <zephyr/kernel.h>
15+
#include <zephyr/logging/log.h>
16+
17+
#include "video_ctrls.h"
18+
#include "video_device.h"
19+
20+
#include <fsl_pxp.h>
21+
#ifdef CONFIG_HAS_MCUX_CACHE
22+
#include <fsl_cache.h>
23+
#endif
24+
25+
#define DT_DRV_COMPAT nxp_pxp
26+
27+
LOG_MODULE_REGISTER(mcux_pxp, CONFIG_VIDEO_LOG_LEVEL);
28+
29+
struct mcux_pxp_config {
30+
PXP_Type *base;
31+
void (*irq_config_func)(void);
32+
};
33+
34+
struct mcux_pxp_ctrls {
35+
struct {
36+
struct video_ctrl rotation;
37+
struct video_ctrl hflip;
38+
struct video_ctrl vflip;
39+
};
40+
};
41+
42+
struct mcux_pxp_data {
43+
struct mcux_pxp_ctrls ctrls;
44+
pxp_ps_buffer_config_t ps_buffer_cfg;
45+
pxp_output_buffer_config_t output_buffer_cfg;
46+
struct k_fifo fifo_in_queued;
47+
struct k_fifo fifo_in_done;
48+
struct k_fifo fifo_out_queued;
49+
struct k_fifo fifo_out_done;
50+
};
51+
52+
static void mcux_pxp_isr(const struct device *const dev)
53+
{
54+
const struct mcux_pxp_config *config = dev->config;
55+
struct mcux_pxp_data *data = dev->data;
56+
57+
PXP_ClearStatusFlags(config->base, kPXP_CompleteFlag);
58+
59+
struct video_buffer *vbuf_in = k_fifo_get(&data->fifo_in_queued, K_NO_WAIT);
60+
struct video_buffer *vbuf_out = k_fifo_get(&data->fifo_out_queued, K_NO_WAIT);
61+
62+
/* Something went wrong, should never happen */
63+
if ((uint32_t)vbuf_in->buffer != data->ps_buffer_cfg.bufferAddr ||
64+
(uint32_t)vbuf_out->buffer != data->output_buffer_cfg.buffer0Addr) {
65+
return;
66+
}
67+
68+
vbuf_out->timestamp = k_uptime_get_32();
69+
vbuf_out->bytesused = data->output_buffer_cfg.pitchBytes * data->output_buffer_cfg.height;
70+
71+
#ifdef CONFIG_HAS_MCUX_CACHE
72+
DCACHE_InvalidateByRange((uint32_t)vbuf_out->buffer, vbuf_out->bytesused);
73+
#endif
74+
75+
k_fifo_put(&data->fifo_in_done, vbuf_in);
76+
k_fifo_put(&data->fifo_out_done, vbuf_out);
77+
}
78+
79+
static int mcux_pxp_set_stream(const struct device *dev, bool enable, enum video_buf_type type)
80+
{
81+
const struct mcux_pxp_config *const config = dev->config;
82+
struct mcux_pxp_data *data = dev->data;
83+
84+
if (enable) {
85+
#ifdef CONFIG_HAS_MCUX_CACHE
86+
DCACHE_CleanByRange((uint32_t)data->ps_buffer_cfg.bufferAddr,
87+
data->output_buffer_cfg.pitchBytes *
88+
data->output_buffer_cfg.height);
89+
#endif
90+
PXP_Start(config->base);
91+
}
92+
93+
return 0;
94+
}
95+
96+
static int mcux_pxp_set_ctrl(const struct device *dev, uint32_t id)
97+
{
98+
const struct mcux_pxp_config *const config = dev->config;
99+
struct mcux_pxp_data *data = dev->data;
100+
struct mcux_pxp_ctrls *ctrls = &data->ctrls;
101+
pxp_flip_mode_t flip = ctrls->hflip.val | ctrls->vflip.val;
102+
pxp_rotate_degree_t rotate = kPXP_Rotate0;
103+
104+
ARG_UNUSED(id);
105+
106+
switch (ctrls->rotation.val) {
107+
case 0:
108+
rotate = kPXP_Rotate0;
109+
break;
110+
case 90:
111+
rotate = kPXP_Rotate90;
112+
break;
113+
case 180:
114+
rotate = kPXP_Rotate180;
115+
break;
116+
case 270:
117+
rotate = kPXP_Rotate270;
118+
break;
119+
}
120+
121+
PXP_SetRotateConfig(config->base, kPXP_RotateProcessSurface, rotate, flip);
122+
123+
return 0;
124+
}
125+
126+
static int mcux_pxp_set_fmt(const struct device *dev, struct video_format *fmt)
127+
{
128+
struct mcux_pxp_data *data = dev->data;
129+
130+
fmt->pitch = fmt->width * video_bits_per_pixel(fmt->pixelformat) / BITS_PER_BYTE;
131+
132+
switch (fmt->type) {
133+
case VIDEO_BUF_TYPE_INPUT:
134+
data->ps_buffer_cfg.pitchBytes = fmt->pitch;
135+
switch (fmt->pixelformat) {
136+
case VIDEO_PIX_FMT_RGB565:
137+
data->ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatRGB565;
138+
break;
139+
case VIDEO_PIX_FMT_RGB24:
140+
#if (!(defined(FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT) && \
141+
FSL_FEATURE_PXP_HAS_NO_EXTEND_PIXEL_FORMAT)) && \
142+
(!(defined(FSL_FEATURE_PXP_V3) && FSL_FEATURE_PXP_V3))
143+
data->ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatARGB8888;
144+
#else
145+
data->ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatRGB888;
146+
#endif
147+
break;
148+
case VIDEO_PIX_FMT_ARGB32:
149+
case VIDEO_PIX_FMT_XRGB32:
150+
data->ps_buffer_cfg.pixelFormat = kPXP_PsPixelFormatARGB8888;
151+
break;
152+
default:
153+
return -ENOTSUP;
154+
}
155+
break;
156+
case VIDEO_BUF_TYPE_OUTPUT:
157+
data->output_buffer_cfg.pitchBytes = fmt->pitch;
158+
data->output_buffer_cfg.width = fmt->width;
159+
data->output_buffer_cfg.height = fmt->height;
160+
switch (fmt->pixelformat) {
161+
case VIDEO_PIX_FMT_RGB565:
162+
data->output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatRGB565;
163+
break;
164+
case VIDEO_PIX_FMT_RGB24:
165+
data->output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatRGB888;
166+
break;
167+
case VIDEO_PIX_FMT_XRGB32:
168+
case VIDEO_PIX_FMT_ARGB32:
169+
data->output_buffer_cfg.pixelFormat = kPXP_OutputPixelFormatARGB8888;
170+
break;
171+
default:
172+
return -ENOTSUP;
173+
}
174+
break;
175+
default:
176+
return -ENOTSUP;
177+
}
178+
179+
return 0;
180+
}
181+
182+
static int mcux_pxp_enqueue(const struct device *dev, struct video_buffer *vbuf)
183+
{
184+
const struct mcux_pxp_config *const config = dev->config;
185+
struct mcux_pxp_data *data = dev->data;
186+
187+
#ifdef CONFIG_HAS_MCUX_CACHE
188+
DCACHE_CleanByRange((uint32_t)vbuf->buffer, vbuf->size);
189+
#endif
190+
191+
switch (vbuf->type) {
192+
case VIDEO_BUF_TYPE_INPUT:
193+
k_fifo_put(&data->fifo_in_queued, vbuf);
194+
195+
data->ps_buffer_cfg.swapByte = false;
196+
data->ps_buffer_cfg.bufferAddr = (uint32_t)vbuf->buffer;
197+
data->ps_buffer_cfg.bufferAddrU = 0U;
198+
data->ps_buffer_cfg.bufferAddrV = 0U;
199+
200+
PXP_SetProcessSurfaceBufferConfig(config->base, &data->ps_buffer_cfg);
201+
202+
break;
203+
case VIDEO_BUF_TYPE_OUTPUT:
204+
k_fifo_put(&data->fifo_out_queued, vbuf);
205+
206+
data->output_buffer_cfg.interlacedMode = kPXP_OutputProgressive;
207+
data->output_buffer_cfg.buffer0Addr = (uint32_t)vbuf->buffer;
208+
data->output_buffer_cfg.buffer1Addr = 0U;
209+
210+
PXP_SetOutputBufferConfig(config->base, &data->output_buffer_cfg);
211+
/* We only support a process surface that covers the full buffer */
212+
PXP_SetProcessSurfacePosition(config->base, 0U, 0U, data->output_buffer_cfg.width,
213+
data->output_buffer_cfg.height);
214+
215+
break;
216+
default:
217+
return -ENOTSUP;
218+
}
219+
220+
return 0;
221+
}
222+
223+
static int mcux_pxp_dequeue(const struct device *dev, struct video_buffer **vbuf,
224+
k_timeout_t timeout)
225+
{
226+
struct mcux_pxp_data *data = dev->data;
227+
228+
switch ((*vbuf)->type) {
229+
case VIDEO_BUF_TYPE_INPUT:
230+
*vbuf = k_fifo_get(&data->fifo_in_done, timeout);
231+
break;
232+
case VIDEO_BUF_TYPE_OUTPUT:
233+
*vbuf = k_fifo_get(&data->fifo_out_done, timeout);
234+
break;
235+
default:
236+
return -ENOTSUP;
237+
}
238+
239+
if (*vbuf == NULL) {
240+
return -EAGAIN;
241+
}
242+
243+
return 0;
244+
}
245+
246+
static DEVICE_API(video, mcux_pxp_driver_api) = {
247+
// .get_caps = mcux_pxp_get_caps,
248+
.set_stream = mcux_pxp_set_stream,
249+
.set_format = mcux_pxp_set_fmt,
250+
// .get_format = mcux_pxp_get_fmt,
251+
.set_ctrl = mcux_pxp_set_ctrl,
252+
.enqueue = mcux_pxp_enqueue,
253+
.dequeue = mcux_pxp_dequeue,
254+
};
255+
256+
static int mcux_pxp_init(const struct device *const dev)
257+
{
258+
const struct mcux_pxp_config *const config = dev->config;
259+
struct mcux_pxp_data *data = dev->data;
260+
struct mcux_pxp_ctrls *ctrls = &data->ctrls;
261+
int ret;
262+
263+
k_fifo_init(&data->fifo_in_queued);
264+
k_fifo_init(&data->fifo_out_queued);
265+
k_fifo_init(&data->fifo_in_done);
266+
k_fifo_init(&data->fifo_out_done);
267+
268+
PXP_Init(config->base);
269+
270+
PXP_SetProcessSurfaceBackGroundColor(config->base, 0U);
271+
/* Disable alpha surface and CSC1 */
272+
PXP_SetAlphaSurfacePosition(config->base, 0xFFFFU, 0xFFFFU, 0U, 0U);
273+
PXP_EnableCsc1(config->base, false);
274+
PXP_EnableInterrupts(config->base, kPXP_CompleteInterruptEnable);
275+
276+
config->irq_config_func();
277+
278+
ret = video_init_ctrl(&ctrls->rotation, dev, VIDEO_CID_ROTATE,
279+
(struct video_ctrl_range){.min = 0, .max = 270, .step = 90, .def = 0});
280+
if (ret) {
281+
return ret;
282+
}
283+
284+
ret = video_init_ctrl(&ctrls->hflip, dev, VIDEO_CID_HFLIP,
285+
(struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0});
286+
if (ret) {
287+
return ret;
288+
}
289+
290+
ret = video_init_ctrl(&ctrls->vflip, dev, VIDEO_CID_VFLIP,
291+
(struct video_ctrl_range){.min = 0, .max = 1, .step = 1, .def = 0});
292+
if (ret) {
293+
return ret;
294+
}
295+
296+
video_auto_cluster_ctrl(&ctrls->rotation, 3, false);
297+
298+
return 0;
299+
}
300+
301+
#define MCUX_PXP_INIT(inst) \
302+
static void mcux_pxp_irq_config_##inst(void) \
303+
{ \
304+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), mcux_pxp_isr, \
305+
DEVICE_DT_INST_GET(inst), 0); \
306+
irq_enable(DT_INST_IRQN(inst)); \
307+
} \
308+
\
309+
static const struct mcux_pxp_config mcux_pxp_config_##inst = { \
310+
.base = (PXP_Type *)DT_INST_REG_ADDR(inst), \
311+
.irq_config_func = mcux_pxp_irq_config_##inst, \
312+
}; \
313+
\
314+
static struct mcux_pxp_data mcux_pxp_data_##inst; \
315+
\
316+
DEVICE_DT_INST_DEFINE(inst, &mcux_pxp_init, NULL, &mcux_pxp_data_##inst, \
317+
&mcux_pxp_config_##inst, POST_KERNEL, CONFIG_VIDEO_INIT_PRIORITY, \
318+
&mcux_pxp_driver_api); \
319+
\
320+
VIDEO_DEVICE_DEFINE(pxp_##inst, DEVICE_DT_INST_GET(inst), NULL);
321+
322+
DT_INST_FOREACH_STATUS_OKAY(MCUX_PXP_INIT)

dts/arm/nxp/nxp_rt10xx.dtsi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,6 @@
987987
reg = <0x402b4000 0x4000>;
988988
interrupts = <44 0>;
989989
status = "disabled";
990-
#dma-cells = <0>;
991990
};
992991

993992
sai1: sai@40384000 {

dts/arm/nxp/nxp_rt11xx.dtsi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1063,7 +1063,6 @@
10631063
reg = <0x40814000 0x4000>;
10641064
interrupts = <57 0>;
10651065
status = "disabled";
1066-
#dma-cells = <0>;
10671066
};
10681067

10691068
iomuxcgpr: iomuxcgpr@400e4000 {

dts/bindings/video/nxp,pxp.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#
2+
# Copyright 2025 NXP
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
description: NXP MCUX PxP
8+
9+
compatible: "nxp,pxp"
10+
11+
include: base.yaml
12+
13+
properties:
14+
reg:
15+
required: true
16+
17+
interrupts:
18+
required: true

modules/hal_nxp/mcux/mcux-sdk-ng/drivers/drivers.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ set_variable_ifdef(CONFIG_WDT_MCUX_WDOG32 CONFIG_MCUX_COMPONENT_driver.wdo
9191
set_variable_ifdef(CONFIG_COUNTER_MCUX_GPT CONFIG_MCUX_COMPONENT_driver.gpt)
9292
set_variable_ifdef(CONFIG_MCUX_GPT_TIMER CONFIG_MCUX_COMPONENT_driver.gpt)
9393
set_variable_ifdef(CONFIG_DISPLAY_MCUX_ELCDIF CONFIG_MCUX_COMPONENT_driver.elcdif)
94-
set_variable_ifdef(CONFIG_MCUX_PXP CONFIG_MCUX_COMPONENT_driver.pxp)
94+
set_variable_ifdef(CONFIG_VIDEO_MCUX_PXP CONFIG_MCUX_COMPONENT_driver.pxp)
9595
set_variable_ifdef(CONFIG_LV_USE_GPU_NXP_PXP CONFIG_MCUX_COMPONENT_driver.pxp)
9696
set_variable_ifdef(CONFIG_GPIO_MCUX_RGPIO CONFIG_MCUX_COMPONENT_driver.rgpio)
9797
set_variable_ifdef(CONFIG_I2S_MCUX_SAI CONFIG_MCUX_COMPONENT_driver.sai)

0 commit comments

Comments
 (0)