Skip to content

Commit 81acde2

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 bad94d3 commit 81acde2

File tree

8 files changed

+365
-3
lines changed

8 files changed

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

0 commit comments

Comments
 (0)