* [PATCH v6 00/28] media: platform: rga: Add RGA3 support
@ 2026-05-15 15:32 Sven Püschel
2026-05-15 15:32 ` [PATCH v6 01/28] media: dt-bindings: media: rockchip-rga: add rockchip,rk3588-rga3 Sven Püschel
` (27 more replies)
0 siblings, 28 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne, Michael Olbrich
This series adds support for the Raster Graphic Acceleration 3 (RGA3)
peripheral, which is included in the RK3588 SoC. The RK3588
contains one RGA2-Enhanced core (which is already implemented by the
rockchip rga driver) and two independent RGA3 cores. They feature
a similar functionality of scaling, cropping and rotating of up to two input
images into one output image. Key differences of the RGA3 are:
- supports 10bit YUV output formats
- supports 8x8 tiles and FBCD as inputs and outputs
- supports BT2020 color space conversion
- max output resolution of (8192-64)x(8192-64)
- MMU can map up to 32G DDR RAM
- fully planar formats (3 planes) are not supported
- max scale up/down factor of 8 (RGA2 allows up to 16)
This patch set adds support for one RGA3 core in the existing
rga m2m driver. The feature set of the PR is limited to scaling,
format and color space conversions between common 8bit RGB/YUV formats.
This already allows a practical usage of the RGA3.
During testing it has been noted that the scaling of the hardware is
slightly incorrect. A test conversion of 128x128 RGBA to 256x256 RGBA
causes a slight shift to the bottom right. The shift is suddle, as it seems
that the image is shifted by about 2px down and right and then cropped to
it's final size (probably caused by the source sampling).
The same behavior has been observed when using the vendor driver
with the librga library.
Furthermore comparing the RGA3 conversion with the GStreamer
videoconvertscale element, the chroma-site is different. A quick testing
didn't reveal a chroma-site that creates the same image with the
GStreamer Element. Also when converting from YUV to RGB the RGB values
differ by 1 or 2. This doesn't seem to be a colorspace conversion issue
but rather a slightly different precision on the calculation.
This was tested on a Radxa Rock 5T. Around 80 fps were measured when
scaling and converting from RGBA 480x360 to NV12 3840x2160 in a single
gstreamer pipeline. Format conversions were tested with a single
gstreamer pipeline converting a fixed input to a given input format.
Afterwards it's piped through the RGA3 and the result is converted back
to rgba and compared against a given hash value (generated after
comparing the output manually to the input).
The patchset also fixes the failing v4l2-compliance tests due to the
missing colorimetry propagation from output to capture:
v4l2-compliance 1.32.0, 64 bits, 64-bit time_t
...
Card type : rga2
...
Total for rockchip-rga device /dev/video0: 48, Succeeded: 48, Failed: 0, Warnings: 0
v4l2-compliance 1.32.0, 64 bits, 64-bit time_t
...
Card type : rga3
...
Total for rockchip-rga device /dev/video1: 48, Succeeded: 48, Failed: 0, Warnings: 0
To distinguish the RGA2 core from the RGA3 cores the Card type is set
accordingly. Scheduling operations between both RGA3 cores to double
the possible frame rate might be a future improvement. Until then
additional RGA3 cores are disabled to only provide one video device to
the user space. This prevents a potential ABI breakage when multi core
support is implemented.
The DTS change at the end is just as a preview, as this series targets
media/next. After it's merged the DTS change will be sent as a new
patch not targeting media.
Patch 1 updates the dtb bindings doc to support the RGA3
Patch 2-5 extend v4l2 common functionality
Patch 6-10 are general cleanups
Patch 11-26 prepare the rga driver for the RGA3
Patch 27 adds RGA3 support to the rga driver
Patch 28 dtsi additions for the RGA3
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- avoid build failure by selecting VIDEOBUF2_DMA_CONTIG
- sorted nodes in dtsi addition
- reworked cmdbuf preparation/reuse to a dirty flag instead of streamon
- fix alignment multiplication edge cases in v4l2_format_plane_stride
- fix unintended parameter change when switching to v4l2_fill_pixfmt_mp_aligned call
- adjust dma_addrs variable type to match the changed struct type change
- dropped scaling limit from s_fmt
- scaling limit considers rotation
- scaling limit is checked when updating selection/rotation while streaming
- don't prevent setting a selection for the RGA3
- fixed flipped inline if branches in rga3_adjust_and_map_format
- Link to v5: https://patch.msgid.link/20260428-spu-rga3-v5-0-eb7f5d019d86@pengutronix.de
Changes in v5:
- Fixed cmdbuf allocation size being only a quarter
- Fixed streamon cmdbuf preparation to not set
rotation/flipping which prevents changing it during streaming
- Link to v4: https://patch.msgid.link/20260325-spu-rga3-v4-0-e90ec1c61354@pengutronix.de
Changes in v4:
- Add Nicolas to Cc for potential reviews and Sebastian for the nice
RK3588 mainline status table
- Improved single memory plane y stride alignment adjustments
- Adjusted scaling inaccuracy description
- Dropped required iommu property from the binding yaml
- Fixed binding yaml indentation
- Link to v3: https://lore.kernel.org/r/20260127-spu-rga3-v3-0-77b273067beb@pengutronix.de
Changes in v3:
- Add iommus property to the dtb bindings documentation
- Drop interrupt name from the dtsi
- Added v4l2_format_info for missing 2 byte RGB formats
- Fixed incorrect dt node reference in the binding patch commit message
- Removed now unused depth member of rga_frame
- Replaced RGA3 semi planar bool with v4l2_format_info check
- Calculated x_div/y_div variables instead of storing them
- Limited width/height to even values for YUV formats
- Support all 4 CSC modes: BT601L, BT601F, BT709L, BT2020L
- Note slightly incorrect scaling by the hardware
- Fix stride alignment to bytes
- Use early returns in rga-buf init/cleanup
- Fix incorrect devm_clk_bulk_get with devm_clk_bulk_get_all
- Don't enforce max scaling factor in try_fmt (only in s_fmt)
- Merge single register editing RGA3 functions into the other functions
- Link to v2: https://lore.kernel.org/r/20251203-spu-rga3-v2-0-989a67947f71@pengutronix.de
Changes in v2:
- Removed overclocking (assigning higher clock speeds in the dts)
- Disable the second RGA3 core
- Improved RGA3 feature documentation and code comments
- Don't write the whole command buffer in each frame
- Don't announce CIDs for the RGA3 and error out on s_selection
- Check the max scaling factor of 16 (RGA2) and 8 (RGA3)
- Move stride alignment and alpha checking to v4l2 common
- Register the interrupt as shared for an external IOMMU
- Add IOMMU patch as dependency to fix sporadic hangups
- Link to v1: https://lore.kernel.org/r/20251007-spu-rga3-v1-0-36ad85570402@pengutronix.de
To: Jacob Chen <jacob-chen@iotwrt.com>
To: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
To: Mauro Carvalho Chehab <mchehab@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Heiko Stuebner <heiko@sntech.de>
To: Hans Verkuil <hverkuil@kernel.org>
Cc: linux-media@vger.kernel.org
Cc: linux-rockchip@lists.infradead.org
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
Michael Olbrich (1):
media: rockchip: rga: share the interrupt when an external iommu is used
Sven Püschel (27):
media: dt-bindings: media: rockchip-rga: add rockchip,rk3588-rga3
media: v4l2-common: sort RGB formats in v4l2_format_info
media: v4l2-common: add missing 1 and 2 byte RGB formats to v4l2_format_info
media: v4l2-common: add has_alpha to v4l2_format_info
media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper
media: rockchip: rga: fix too small buffer size
media: rockchip: rga: use clk_bulk api
media: rockchip: rga: use stride for offset calculation
media: rockchip: rga: remove redundant rga_frame variables
media: rockchip: rga: announce and sync colorimetry
media: rockchip: rga: move hw specific parts to a dedicated struct
media: rockchip: rga: avoid odd frame sizes for YUV formats
media: rockchip: rga: calculate x_div/y_div using v4l2_format_info
media: rockchip: rga: move cmdbuf to rga_ctx
media: rockchip: rga: align stride to 4 bytes
media: rockchip: rga: reuse cmdbuf contents
media: rockchip: rga: check scaling factor
media: rockchip: rga: use card type to specify rga type
media: rockchip: rga: change offset to dma_addresses
media: rockchip: rga: support external iommus
media: rockchip: rga: remove size from rga_frame
media: rockchip: rga: remove stride from rga_frame
media: rockchip: rga: move rga_fmt to rga-hw.h
media: rockchip: rga: add feature flags
media: rockchip: rga: disable multi-core support
media: rockchip: rga: add rga3 support
arm64: dts: rockchip: add rga3 dt nodes
.../devicetree/bindings/media/rockchip-rga.yaml | 10 +-
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 44 ++
drivers/media/platform/rockchip/rga/Kconfig | 1 +
drivers/media/platform/rockchip/rga/Makefile | 2 +-
drivers/media/platform/rockchip/rga/rga-buf.c | 89 +++-
drivers/media/platform/rockchip/rga/rga-hw.c | 357 +++++++++----
drivers/media/platform/rockchip/rga/rga-hw.h | 16 +-
drivers/media/platform/rockchip/rga/rga.c | 576 ++++++++++-----------
drivers/media/platform/rockchip/rga/rga.h | 88 ++--
drivers/media/platform/rockchip/rga/rga3-hw.c | 507 ++++++++++++++++++
drivers/media/platform/rockchip/rga/rga3-hw.h | 192 +++++++
drivers/media/v4l2-core/v4l2-common.c | 128 +++--
include/media/v4l2-common.h | 6 +
13 files changed, 1513 insertions(+), 503 deletions(-)
---
base-commit: bc1ba628e37c93cf2abeb2c79716f49087f8a024
change-id: 20251001-spu-rga3-8a00e018b120
prerequisite-change-id: 20251126-spu-iommudtefix-cd0c5244c74a:v2
prerequisite-patch-id: 10c6c977c0f71400931941b42da73adcaf63e810
Best regards,
--
Sven Püschel <s.pueschel@pengutronix.de>
^ permalink raw reply [flat|nested] 44+ messages in thread
* [PATCH v6 01/28] media: dt-bindings: media: rockchip-rga: add rockchip,rk3588-rga3
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 02/28] media: v4l2-common: sort RGB formats in v4l2_format_info Sven Püschel
` (26 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel
Add a new compatible for the RGA3 (Raster Graphic Acceleration 3)
peripheral found on the RK3588 SoC. Also specify an iommu property,
as the RGA3 contains the generic rockchip iommu. While other versions
also have an iommu, it's usually specific to them.
The RK3588 contains one RGA2-Enhance core (also contained on the RK3399)
and two RGA3 cores. Both feature a similar functionality of scaling,
cropping and rotating of up to two input images into one output image.
Key differences of the RGA3 are:
- supports 10bit YUV output formats
- supports 8x8 tiles and FBCD as inputs and outputs
- supports BT2020 color space conversion
- max output resolution of (8192-64)x(8192-64)
- MMU can map up to 32G DDR RAM
- fully planar formats (3 planes) are not supported
- max scale up/down factor of 8 (RGA2 allows up to 16)
Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changed in v3/v4:
- Dropped Acked-by: Krzysztof Kozlowski due to the added iommus property
and description adjustments.
---
Documentation/devicetree/bindings/media/rockchip-rga.yaml | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/media/rockchip-rga.yaml b/Documentation/devicetree/bindings/media/rockchip-rga.yaml
index ac17cda65191b..7bd92f7336664 100644
--- a/Documentation/devicetree/bindings/media/rockchip-rga.yaml
+++ b/Documentation/devicetree/bindings/media/rockchip-rga.yaml
@@ -9,7 +9,11 @@ title: Rockchip 2D raster graphic acceleration controller (RGA)
description:
RGA is a standalone 2D raster graphic acceleration unit. It accelerates 2D
graphics operations, such as point/line drawing, image scaling, rotation,
- BitBLT, alpha blending and image blur/sharpness.
+ BitBLT, alpha blending and image blur/sharpness. There exist many versions
+ of this unit that differ in the supported inputs/output formats,
+ the attached IOMMU and the supported operations on the input. As some SoCs
+ include multiple RGA units with different versions, a more specific
+ compatible name to differentiate the concrete unit is used for them.
maintainers:
- Jacob Chen <jacob-chen@iotwrt.com>
@@ -20,6 +24,7 @@ properties:
oneOf:
- const: rockchip,rk3288-rga
- const: rockchip,rk3399-rga
+ - const: rockchip,rk3588-rga3
- items:
- enum:
- rockchip,rk3228-rga
@@ -45,6 +50,9 @@ properties:
power-domains:
maxItems: 1
+ iommus:
+ maxItems: 1
+
resets:
maxItems: 3
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 02/28] media: v4l2-common: sort RGB formats in v4l2_format_info
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
2026-05-15 15:32 ` [PATCH v6 01/28] media: dt-bindings: media: rockchip-rga: add rockchip,rk3588-rga3 Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 03/28] media: v4l2-common: add missing 1 and 2 byte RGB formats to v4l2_format_info Sven Püschel
` (25 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Sort the RGB formats in v4l2_format_info to match the format definitions
in include/uapi/linux/videodev2.h . Also introduce the same sections to
partition the list of formats and align the format info in each section.
The alignment of the 1 or 2 bytes RGB formats contains an additional
space in preparation of adding the missing formats to the list, as for
V4L2_PIX_FMT_ARGB555X an additional space is necessary.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/v4l2-core/v4l2-common.c | 54 +++++++++++++++++++----------------
1 file changed, 30 insertions(+), 24 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index bceafc4e92c81..f6cb7e14f2236 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -245,33 +245,39 @@ EXPORT_SYMBOL_GPL(v4l2_s_parm_cap);
const struct v4l2_format_info *v4l2_format_info(u32 format)
{
static const struct v4l2_format_info formats[] = {
- /* RGB formats */
- { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ /* RGB formats (1 or 2 bytes per pixel) */
+ { .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+
+ /* RGB formats (3 or 4 bytes per pixel) */
+ { .format = V4L2_PIX_FMT_BGR666, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ /* RGB formats (6 or 8 bytes per pixel) */
+ { .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+
+ /* HSV formats */
+ { .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_HSV32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+
/* YUV packed formats */
{ .format = V4L2_PIX_FMT_YUYV, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_YVYU, .pixel_enc = V4L2_PIXEL_ENC_YUV, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 2, .vdiv = 1 },
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 03/28] media: v4l2-common: add missing 1 and 2 byte RGB formats to v4l2_format_info
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
2026-05-15 15:32 ` [PATCH v6 01/28] media: dt-bindings: media: rockchip-rga: add rockchip,rk3588-rga3 Sven Püschel
2026-05-15 15:32 ` [PATCH v6 02/28] media: v4l2-common: sort RGB formats in v4l2_format_info Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 04/28] media: v4l2-common: add has_alpha " Sven Püschel
` (24 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Add all missing one and two byte RGB formats to v4l2_format_info. This
allows drivers to more consistently use v4l2_format_info, as it now
covers all currently defined RGB formats.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/v4l2-core/v4l2-common.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index f6cb7e14f2236..b55ab72958eaa 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -246,8 +246,29 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{
static const struct v4l2_format_info formats[] = {
/* RGB formats (1 or 2 bytes per pixel) */
+ { .format = V4L2_PIX_FMT_RGB332, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_XRGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_XBGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_XRGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_XBGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_XRGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
/* RGB formats (3 or 4 bytes per pixel) */
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 04/28] media: v4l2-common: add has_alpha to v4l2_format_info
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (2 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 03/28] media: v4l2-common: add missing 1 and 2 byte RGB formats to v4l2_format_info Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 05/28] media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper Sven Püschel
` (23 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Add a has_alpha value to the v4l2_format_info struct to indicate if the
format contains an alpha component. This information can currently not
be queried in a generic way, but might be useful for potential drivers
to properly setup alpha blending to copy or set the alpha value.
The implementation is based on the drm_format_info implementation.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/v4l2-core/v4l2-common.c | 32 ++++++++++++++++----------------
include/media/v4l2-common.h | 2 ++
2 files changed, 18 insertions(+), 16 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index b55ab72958eaa..3cc8b04e1ea63 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -248,26 +248,26 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
/* RGB formats (1 or 2 bytes per pixel) */
{ .format = V4L2_PIX_FMT_RGB332, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 1, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_XRGB444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_RGBX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_XBGR444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRA444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_BGRX444, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_XRGB555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_RGBX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_XBGR555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRA555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_BGRX555, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB565, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_XRGB555X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB565X, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 2, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
@@ -276,24 +276,24 @@ const struct v4l2_format_info *v4l2_format_info(u32 format)
{ .format = V4L2_PIX_FMT_BGR24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_BGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_XBGR32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_BGRA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_BGRX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_RGBX32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ARGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
{ .format = V4L2_PIX_FMT_XRGB32, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGBX1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_RGBA1010102, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
+ { .format = V4L2_PIX_FMT_ARGB2101010, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 4, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
/* RGB formats (6 or 8 bytes per pixel) */
{ .format = V4L2_PIX_FMT_BGR48_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_BGR48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
{ .format = V4L2_PIX_FMT_RGB48, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 6, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
- { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
+ { .format = V4L2_PIX_FMT_ABGR64_12, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 8, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1, .has_alpha = true },
/* HSV formats */
{ .format = V4L2_PIX_FMT_HSV24, .pixel_enc = V4L2_PIXEL_ENC_RGB, .mem_planes = 1, .comp_planes = 1, .bpp = { 3, 0, 0, 0 }, .bpp_div = { 1, 1, 1, 1 }, .hdiv = 1, .vdiv = 1 },
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index f8b1faced79c8..401d8506c24b5 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -520,6 +520,7 @@ enum v4l2_pixel_encoding {
* @vdiv: Vertical chroma subsampling factor
* @block_w: Per-plane macroblock pixel width (optional)
* @block_h: Per-plane macroblock pixel height (optional)
+ * @has_alpha: Does the format embeds an alpha component?
*/
struct v4l2_format_info {
u32 format;
@@ -532,6 +533,7 @@ struct v4l2_format_info {
u8 vdiv;
u8 block_w[4];
u8 block_h[4];
+ bool has_alpha;
};
static inline bool v4l2_is_format_rgb(const struct v4l2_format_info *f)
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 05/28] media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (3 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 04/28] media: v4l2-common: add has_alpha " Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:58 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 06/28] media: rockchip: rga: fix too small buffer size Sven Püschel
` (22 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Add a v4l2_fill_pixfmt_mp_aligned helper which allows the user to
specify a custom stride alignment in bytes. This is necessary for
hardware like the Rockchip RGA3, which requires the stride value to be
aligned to a 16 bytes boundary.
The code makes some assumptions about the v4l2 format to simplify the
calculation. They currently hold for all known v4l2 formats.
v4l2_format_plane_stride uses an unsigned int as argument type to avoid
the later multiplication from overflowing the u8 value. All other places
use u8, as no practical use cases for a larger alignment are known at
the moment.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- Fixed alignment multiplication of 0 for NV24 - flagged by Sashiko:
https://sashiko.dev/#/patchset/20260428-spu-rga3-v5-0-eb7f5d019d86%40pengutronix.de?part=5
- Changed v4l2_format_plane_stride alignment parameter type to
avoid overflow for 64/128 byte alignment by multiplication.
Flagged by Sashiko URL above.
---
drivers/media/v4l2-core/v4l2-common.c | 51 ++++++++++++++++++++++++++---------
include/media/v4l2-common.h | 4 +++
2 files changed, 43 insertions(+), 12 deletions(-)
diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
index 3cc8b04e1ea63..a03e48812bcf5 100644
--- a/drivers/media/v4l2-core/v4l2-common.c
+++ b/drivers/media/v4l2-core/v4l2-common.c
@@ -432,14 +432,28 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf
}
static inline unsigned int v4l2_format_plane_stride(const struct v4l2_format_info *info, int plane,
- unsigned int width)
+ unsigned int width, unsigned int byte_alignment)
{
unsigned int hdiv = plane ? info->hdiv : 1;
unsigned int aligned_width =
ALIGN(width, v4l2_format_block_width(info, plane));
- return DIV_ROUND_UP(aligned_width, hdiv) *
- info->bpp[plane] / info->bpp_div[plane];
+ /*
+ * Formats with a single memory plane derive the stride of the
+ * other planes from the y stride. Due to chroma subsampling this
+ * may cause the other plane strides to break the byte_alignment.
+ * Increase the alignment if necessary to prevent this.
+ *
+ * It assumes the following format properties:
+ * - bpp_div[0] == bpp_div[1]
+ * - The multiplication factor doesn't differ between the non y planes
+ */
+ if (info->mem_planes == 1 && info->comp_planes > 1 && plane == 0)
+ byte_alignment *= DIV_ROUND_UP(info->hdiv * info->bpp[0], info->bpp[1]);
+
+ return ALIGN(DIV_ROUND_UP(aligned_width, hdiv) * info->bpp[plane] /
+ info->bpp_div[plane],
+ byte_alignment);
}
static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_info *info, int plane,
@@ -453,9 +467,10 @@ static inline unsigned int v4l2_format_plane_height(const struct v4l2_format_inf
}
static inline unsigned int v4l2_format_plane_size(const struct v4l2_format_info *info, int plane,
- unsigned int width, unsigned int height)
+ unsigned int width, unsigned int height,
+ u8 stride_alignment)
{
- return v4l2_format_plane_stride(info, plane, width) *
+ return v4l2_format_plane_stride(info, plane, width, stride_alignment) *
v4l2_format_plane_height(info, plane, height);
}
@@ -476,8 +491,9 @@ void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
}
EXPORT_SYMBOL_GPL(v4l2_apply_frmsize_constraints);
-int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
- u32 pixelformat, u32 width, u32 height)
+int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt,
+ u32 pixelformat, u32 width, u32 height,
+ u8 stride_alignment)
{
const struct v4l2_format_info *info;
struct v4l2_plane_pix_format *plane;
@@ -494,23 +510,34 @@ int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
if (info->mem_planes == 1) {
plane = &pixfmt->plane_fmt[0];
- plane->bytesperline = v4l2_format_plane_stride(info, 0, width);
+ plane->bytesperline = v4l2_format_plane_stride(info, 0, width,
+ stride_alignment);
plane->sizeimage = 0;
for (i = 0; i < info->comp_planes; i++)
plane->sizeimage +=
- v4l2_format_plane_size(info, i, width, height);
+ v4l2_format_plane_size(info, i, width, height,
+ stride_alignment);
} else {
for (i = 0; i < info->comp_planes; i++) {
plane = &pixfmt->plane_fmt[i];
plane->bytesperline =
- v4l2_format_plane_stride(info, i, width);
+ v4l2_format_plane_stride(info, i, width,
+ stride_alignment);
plane->sizeimage = plane->bytesperline *
v4l2_format_plane_height(info, i, height);
}
}
return 0;
}
+EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp_aligned);
+
+int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
+ u32 pixelformat, u32 width, u32 height)
+{
+ return v4l2_fill_pixfmt_mp_aligned(pixfmt, pixelformat,
+ width, height, 1);
+}
EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt_mp);
int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
@@ -530,12 +557,12 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
pixfmt->width = width;
pixfmt->height = height;
pixfmt->pixelformat = pixelformat;
- pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width);
+ pixfmt->bytesperline = v4l2_format_plane_stride(info, 0, width, 1);
pixfmt->sizeimage = 0;
for (i = 0; i < info->comp_planes; i++)
pixfmt->sizeimage +=
- v4l2_format_plane_size(info, i, width, height);
+ v4l2_format_plane_size(info, i, width, height, 1);
return 0;
}
EXPORT_SYMBOL_GPL(v4l2_fill_pixfmt);
diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
index 401d8506c24b5..edd416178c333 100644
--- a/include/media/v4l2-common.h
+++ b/include/media/v4l2-common.h
@@ -558,6 +558,10 @@ int v4l2_fill_pixfmt(struct v4l2_pix_format *pixfmt, u32 pixelformat,
u32 width, u32 height);
int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt, u32 pixelformat,
u32 width, u32 height);
+/* @stride_alignment is a power of 2 value in bytes */
+int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt,
+ u32 pixelformat, u32 width, u32 height,
+ u8 stride_alignment);
/**
* v4l2_get_link_freq - Get link rate from transmitter
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 06/28] media: rockchip: rga: fix too small buffer size
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (4 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 05/28] media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 07/28] media: rockchip: rga: use clk_bulk api Sven Püschel
` (21 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Fix the command buffer size being only a quarter of the actual size.
The RGA_CMDBUF_SIZE macro was potentially intended to specify the length
of the cmdbuf u32 array pointer. But as it's used to specify the size of
the allocation, which is counted in bytes. Therefore adjust the macro
size to bytes as it better matches the variable name and adjust it's
users accordingly.
As the command buffer is relatively small, it probably didn't caused
an issue due to being smaller than a single page.
Fixes: f7e7b48e6d79 ("[media] rockchip/rga: v4l2 m2m support")
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Flagged by Sashiko:
https://sashiko.dev/#/patchset/20260325-spu-rga3-v4-0-e90ec1c61354%40pengutronix.de?part=10
---
drivers/media/platform/rockchip/rga/rga-hw.c | 2 +-
drivers/media/platform/rockchip/rga/rga-hw.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 43ed742a16492..d1618bb247501 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -414,7 +414,7 @@ static void rga_cmd_set(struct rga_ctx *ctx,
{
struct rockchip_rga *rga = ctx->rga;
- memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE * 4);
+ memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE);
rga_cmd_set_src_addr(ctx, src->dma_desc_pa);
/*
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h
index cc6bd7f5b0300..2b8537a5fd0d7 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.h
+++ b/drivers/media/platform/rockchip/rga/rga-hw.h
@@ -6,7 +6,7 @@
#ifndef __RGA_HW_H__
#define __RGA_HW_H__
-#define RGA_CMDBUF_SIZE 0x20
+#define RGA_CMDBUF_SIZE 0x80
/* Hardware limits */
#define MAX_WIDTH 8192
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 07/28] media: rockchip: rga: use clk_bulk api
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (5 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 06/28] media: rockchip: rga: fix too small buffer size Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:54 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 08/28] media: rockchip: rga: use stride for offset calculation Sven Püschel
` (20 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Use the clk_bulk API to avoid code duplication for each of the three
clocks.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga.c | 65 ++++---------------------------
drivers/media/platform/rockchip/rga/rga.h | 6 +--
2 files changed, 11 insertions(+), 60 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index fea63b94c5f3d..4e710a050cb7c 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -698,48 +698,10 @@ static const struct video_device rga_videodev = {
.device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING,
};
-static int rga_enable_clocks(struct rockchip_rga *rga)
-{
- int ret;
-
- ret = clk_prepare_enable(rga->sclk);
- if (ret) {
- dev_err(rga->dev, "Cannot enable rga sclk: %d\n", ret);
- return ret;
- }
-
- ret = clk_prepare_enable(rga->aclk);
- if (ret) {
- dev_err(rga->dev, "Cannot enable rga aclk: %d\n", ret);
- goto err_disable_sclk;
- }
-
- ret = clk_prepare_enable(rga->hclk);
- if (ret) {
- dev_err(rga->dev, "Cannot enable rga hclk: %d\n", ret);
- goto err_disable_aclk;
- }
-
- return 0;
-
-err_disable_aclk:
- clk_disable_unprepare(rga->aclk);
-err_disable_sclk:
- clk_disable_unprepare(rga->sclk);
-
- return ret;
-}
-
-static void rga_disable_clocks(struct rockchip_rga *rga)
-{
- clk_disable_unprepare(rga->sclk);
- clk_disable_unprepare(rga->hclk);
- clk_disable_unprepare(rga->aclk);
-}
-
static int rga_parse_dt(struct rockchip_rga *rga)
{
struct reset_control *core_rst, *axi_rst, *ahb_rst;
+ int ret;
core_rst = devm_reset_control_get(rga->dev, "core");
if (IS_ERR(core_rst)) {
@@ -771,23 +733,12 @@ static int rga_parse_dt(struct rockchip_rga *rga)
udelay(1);
reset_control_deassert(ahb_rst);
- rga->sclk = devm_clk_get(rga->dev, "sclk");
- if (IS_ERR(rga->sclk)) {
- dev_err(rga->dev, "failed to get sclk clock\n");
- return PTR_ERR(rga->sclk);
- }
-
- rga->aclk = devm_clk_get(rga->dev, "aclk");
- if (IS_ERR(rga->aclk)) {
- dev_err(rga->dev, "failed to get aclk clock\n");
- return PTR_ERR(rga->aclk);
- }
-
- rga->hclk = devm_clk_get(rga->dev, "hclk");
- if (IS_ERR(rga->hclk)) {
- dev_err(rga->dev, "failed to get hclk clock\n");
- return PTR_ERR(rga->hclk);
+ ret = devm_clk_bulk_get_all(rga->dev, &rga->clks);
+ if (ret < 0) {
+ dev_err(rga->dev, "failed to get clocks\n");
+ return ret;
}
+ rga->num_clks = ret;
return 0;
}
@@ -935,7 +886,7 @@ static int __maybe_unused rga_runtime_suspend(struct device *dev)
{
struct rockchip_rga *rga = dev_get_drvdata(dev);
- rga_disable_clocks(rga);
+ clk_bulk_disable_unprepare(rga->num_clks, rga->clks);
return 0;
}
@@ -944,7 +895,7 @@ static int __maybe_unused rga_runtime_resume(struct device *dev)
{
struct rockchip_rga *rga = dev_get_drvdata(dev);
- return rga_enable_clocks(rga);
+ return clk_bulk_prepare_enable(rga->num_clks, rga->clks);
}
static const struct dev_pm_ops rga_pm = {
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 72a28b120fabf..2db10acecb405 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -6,6 +6,7 @@
#ifndef __RGA_H__
#define __RGA_H__
+#include <linux/clk.h>
#include <linux/platform_device.h>
#include <media/videobuf2-v4l2.h>
#include <media/v4l2-ctrls.h>
@@ -81,9 +82,8 @@ struct rockchip_rga {
struct device *dev;
struct regmap *grf;
void __iomem *regs;
- struct clk *sclk;
- struct clk *aclk;
- struct clk *hclk;
+ struct clk_bulk_data *clks;
+ int num_clks;
struct rockchip_rga_version version;
/* vfd lock */
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 08/28] media: rockchip: rga: use stride for offset calculation
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (6 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 07/28] media: rockchip: rga: use clk_bulk api Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 09/28] media: rockchip: rga: remove redundant rga_frame variables Sven Püschel
` (19 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Use the stride instead of the width for the offset calculation. This
ensures that the bytesperline value doesn't need to match the width
value of the image.
Furthermore this patch removes the dependency on the uv_factor property
and instead reuses the v4l2_format_info to determine the correct
division factor.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga-buf.c | 14 +++++++++-----
drivers/media/platform/rockchip/rga/rga.c | 16 ----------------
drivers/media/platform/rockchip/rga/rga.h | 1 -
3 files changed, 9 insertions(+), 22 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index bb575873f2b24..65fc0d5b4aa10 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -14,7 +14,6 @@
#include <media/videobuf2-dma-sg.h>
#include <media/videobuf2-v4l2.h>
-#include "rga-hw.h"
#include "rga.h"
static ssize_t fill_descriptors(struct rga_dma_desc *desc, size_t max_desc,
@@ -95,14 +94,19 @@ static int rga_buf_init(struct vb2_buffer *vb)
return 0;
}
-static int get_plane_offset(struct rga_frame *f, int plane)
+static int get_plane_offset(struct rga_frame *f,
+ const struct v4l2_format_info *info,
+ int plane)
{
+ u32 stride = f->pix.plane_fmt[0].bytesperline;
+
if (plane == 0)
return 0;
if (plane == 1)
- return f->width * f->height;
+ return stride * f->height;
if (plane == 2)
- return f->width * f->height + (f->width * f->height / f->fmt->uv_factor);
+ return stride * f->height +
+ (stride * f->height / info->hdiv / info->vdiv);
return -EINVAL;
}
@@ -148,7 +152,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
/* Fill the remaining planes */
info = v4l2_format_info(f->fmt->fourcc);
for (i = info->mem_planes; i < info->comp_planes; i++)
- offsets[i] = get_plane_offset(f, i);
+ offsets[i] = get_plane_offset(f, info, i);
rbuf->offset.y_off = offsets[0];
rbuf->offset.u_off = offsets[1];
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 4e710a050cb7c..c07207edffdb6 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -190,7 +190,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_ALPHA_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR8888,
.depth = 32,
- .uv_factor = 1,
.y_div = 1,
.x_div = 1,
},
@@ -199,7 +198,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR8888,
.depth = 32,
- .uv_factor = 1,
.y_div = 1,
.x_div = 1,
},
@@ -208,7 +206,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_XBGR8888,
.depth = 32,
- .uv_factor = 1,
.y_div = 1,
.x_div = 1,
},
@@ -217,7 +214,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_RGB888,
.depth = 24,
- .uv_factor = 1,
.y_div = 1,
.x_div = 1,
},
@@ -226,7 +222,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_RGB888,
.depth = 24,
- .uv_factor = 1,
.y_div = 1,
.x_div = 1,
},
@@ -235,7 +230,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR4444,
.depth = 16,
- .uv_factor = 1,
.y_div = 1,
.x_div = 1,
},
@@ -244,7 +238,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR1555,
.depth = 16,
- .uv_factor = 1,
.y_div = 1,
.x_div = 1,
},
@@ -253,7 +246,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_BGR565,
.depth = 16,
- .uv_factor = 1,
.y_div = 1,
.x_div = 1,
},
@@ -262,7 +254,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_UV_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420SP,
.depth = 12,
- .uv_factor = 4,
.y_div = 2,
.x_div = 1,
},
@@ -271,7 +262,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_UV_SWAP,
.hw_format = RGA_COLOR_FMT_YUV422SP,
.depth = 16,
- .uv_factor = 2,
.y_div = 1,
.x_div = 1,
},
@@ -280,7 +270,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420SP,
.depth = 12,
- .uv_factor = 4,
.y_div = 2,
.x_div = 1,
},
@@ -289,7 +278,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420SP,
.depth = 12,
- .uv_factor = 4,
.y_div = 2,
.x_div = 1,
},
@@ -298,7 +286,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV422SP,
.depth = 16,
- .uv_factor = 2,
.y_div = 1,
.x_div = 1,
},
@@ -307,7 +294,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420P,
.depth = 12,
- .uv_factor = 4,
.y_div = 2,
.x_div = 2,
},
@@ -316,7 +302,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV422P,
.depth = 16,
- .uv_factor = 2,
.y_div = 1,
.x_div = 2,
},
@@ -325,7 +310,6 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_UV_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420P,
.depth = 12,
- .uv_factor = 4,
.y_div = 2,
.x_div = 2,
},
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 2db10acecb405..477cf5b62bbb2 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -17,7 +17,6 @@
struct rga_fmt {
u32 fourcc;
int depth;
- u8 uv_factor;
u8 y_div;
u8 x_div;
u8 color_swap;
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 09/28] media: rockchip: rga: remove redundant rga_frame variables
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (7 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 08/28] media: rockchip: rga: use stride for offset calculation Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 10/28] media: rockchip: rga: announce and sync colorimetry Sven Püschel
` (18 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Remove the redundant rga_frame variables width, height and color space.
The value of these variables is already contained in the pix member
of rga_frame. The code also keeps these values in sync. Therefore drop
them in favor of the existing pix member.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga-buf.c | 6 ++---
drivers/media/platform/rockchip/rga/rga-hw.c | 6 ++---
drivers/media/platform/rockchip/rga/rga.c | 32 ++++++++++-----------------
drivers/media/platform/rockchip/rga/rga.h | 5 -----
4 files changed, 18 insertions(+), 31 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index 65fc0d5b4aa10..ffc6162b2e681 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -103,10 +103,10 @@ static int get_plane_offset(struct rga_frame *f,
if (plane == 0)
return 0;
if (plane == 1)
- return stride * f->height;
+ return stride * f->pix.height;
if (plane == 2)
- return stride * f->height +
- (stride * f->height / info->hdiv / info->vdiv);
+ return stride * f->pix.height +
+ (stride * f->pix.height / info->hdiv / info->vdiv);
return -EINVAL;
}
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index d1618bb247501..ec6c17504ca15 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -53,7 +53,7 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset,
x_div = frm->fmt->x_div;
y_div = frm->fmt->y_div;
uv_stride = frm->stride / x_div;
- pixel_width = frm->stride / frm->width;
+ pixel_width = frm->stride / frm->pix.width;
lt->y_off = offset->y_off + y * frm->stride + x * pixel_width;
lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div;
@@ -191,7 +191,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) &&
RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) {
- switch (ctx->in.colorspace) {
+ switch (ctx->in.pix.colorspace) {
case V4L2_COLORSPACE_REC709:
src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0;
break;
@@ -203,7 +203,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) &&
RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) {
- switch (ctx->out.colorspace) {
+ switch (ctx->out.pix.colorspace) {
case V4L2_COLORSPACE_REC709:
dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0;
break;
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index c07207edffdb6..ca8d8a53dc251 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -329,9 +329,6 @@ static struct rga_fmt *rga_fmt_find(u32 pixelformat)
}
static struct rga_frame def_frame = {
- .width = DEFAULT_WIDTH,
- .height = DEFAULT_HEIGHT,
- .colorspace = V4L2_COLORSPACE_DEFAULT,
.crop.left = 0,
.crop.top = 0,
.crop.width = DEFAULT_WIDTH,
@@ -363,9 +360,9 @@ static int rga_open(struct file *file)
ctx->out = def_frame;
v4l2_fill_pixfmt_mp(&ctx->in.pix,
- ctx->in.fmt->fourcc, ctx->out.width, ctx->out.height);
+ ctx->in.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT);
v4l2_fill_pixfmt_mp(&ctx->out.pix,
- ctx->out.fmt->fourcc, ctx->out.width, ctx->out.height);
+ ctx->out.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT);
if (mutex_lock_interruptible(&rga->mutex)) {
kfree(ctx);
@@ -453,10 +450,8 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
if (IS_ERR(frm))
return PTR_ERR(frm);
- v4l2_fill_pixfmt_mp(pix_fmt, frm->fmt->fourcc, frm->width, frm->height);
-
+ *pix_fmt = frm->pix;
pix_fmt->field = V4L2_FIELD_NONE;
- pix_fmt->colorspace = frm->colorspace;
return 0;
}
@@ -505,27 +500,24 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
frm = rga_get_frame(ctx, f->type);
if (IS_ERR(frm))
return PTR_ERR(frm);
- frm->width = pix_fmt->width;
- frm->height = pix_fmt->height;
frm->size = 0;
for (i = 0; i < pix_fmt->num_planes; i++)
frm->size += pix_fmt->plane_fmt[i].sizeimage;
frm->fmt = rga_fmt_find(pix_fmt->pixelformat);
frm->stride = pix_fmt->plane_fmt[0].bytesperline;
- frm->colorspace = pix_fmt->colorspace;
/* Reset crop settings */
frm->crop.left = 0;
frm->crop.top = 0;
- frm->crop.width = frm->width;
- frm->crop.height = frm->height;
+ frm->crop.width = pix_fmt->width;
+ frm->crop.height = pix_fmt->height;
frm->pix = *pix_fmt;
v4l2_dbg(debug, 1, &rga->v4l2_dev,
"[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n",
V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE",
- &frm->fmt->fourcc, frm->width, frm->height,
+ &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height,
frm->stride, frm->size);
for (i = 0; i < pix_fmt->num_planes; i++) {
@@ -579,8 +571,8 @@ static int vidioc_g_selection(struct file *file, void *priv,
} else {
s->r.left = 0;
s->r.top = 0;
- s->r.width = f->width;
- s->r.height = f->height;
+ s->r.width = f->pix.width;
+ s->r.height = f->pix.height;
}
return 0;
@@ -629,8 +621,8 @@ static int vidioc_s_selection(struct file *file, void *priv,
return -EINVAL;
}
- if (s->r.left + s->r.width > f->width ||
- s->r.top + s->r.height > f->height ||
+ if (s->r.left + s->r.width > f->pix.width ||
+ s->r.top + s->r.height > f->pix.height ||
s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) {
v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n");
return -EINVAL;
@@ -821,8 +813,8 @@ static int rga_probe(struct platform_device *pdev)
goto rel_m2m;
}
- def_frame.stride = (def_frame.width * def_frame.fmt->depth) >> 3;
- def_frame.size = def_frame.stride * def_frame.height;
+ def_frame.stride = (DEFAULT_WIDTH * def_frame.fmt->depth) >> 3;
+ def_frame.size = def_frame.stride * DEFAULT_HEIGHT;
ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret) {
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 477cf5b62bbb2..c4a3905a48f0d 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -24,11 +24,6 @@ struct rga_fmt {
};
struct rga_frame {
- /* Original dimensions */
- u32 width;
- u32 height;
- u32 colorspace;
-
/* Crop */
struct v4l2_rect crop;
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 10/28] media: rockchip: rga: announce and sync colorimetry
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (8 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 09/28] media: rockchip: rga: remove redundant rga_frame variables Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:14 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 11/28] media: rockchip: rga: move hw specific parts to a dedicated struct Sven Püschel
` (17 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Announce the capability to adjust the quantization and ycbcr_enc on the
capture side and check if the SET_CSC flag is set when the colorimetry
is changed. Furthermore copy the colorimetry from the output to the
capture side to fix the currently failing v4l2-compliance tests, which
expect exactly this behavior.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga.c | 37 +++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index ca8d8a53dc251..8c34f73d69764 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -437,6 +437,15 @@ static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f
fmt = &formats[f->index];
f->pixelformat = fmt->fourcc;
+ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return 0;
+
+ /* allow changing the quantization and xfer func for YUV formats */
+ if (v4l2_is_format_yuv(v4l2_format_info(f->pixelformat)))
+ f->flags |= V4L2_FMT_FLAG_CSC_QUANTIZATION |
+ V4L2_FMT_FLAG_CSC_YCBCR_ENC;
+
return 0;
}
@@ -459,8 +468,25 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp;
+ struct rga_ctx *ctx = file_to_rga_ctx(file);
struct rga_fmt *fmt;
+ if (V4L2_TYPE_IS_CAPTURE(f->type)) {
+ const struct rga_frame *frm;
+
+ frm = rga_get_frame(ctx, f->type);
+ if (IS_ERR(frm))
+ return PTR_ERR(frm);
+
+ if (!(pix_fmt->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
+ pix_fmt->quantization = frm->pix.quantization;
+ pix_fmt->ycbcr_enc = frm->pix.ycbcr_enc;
+ }
+ /* disallow values not announced in vidioc_enum_fmt */
+ pix_fmt->colorspace = frm->pix.colorspace;
+ pix_fmt->xfer_func = frm->pix.xfer_func;
+ }
+
fmt = rga_fmt_find(pix_fmt->pixelformat);
if (!fmt)
fmt = &formats[0];
@@ -506,6 +532,17 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
frm->fmt = rga_fmt_find(pix_fmt->pixelformat);
frm->stride = pix_fmt->plane_fmt[0].bytesperline;
+ /*
+ * Copy colorimetry from output to capture as required by the
+ * v4l2-compliance tests
+ */
+ if (V4L2_TYPE_IS_OUTPUT(f->type)) {
+ ctx->out.pix.colorspace = pix_fmt->colorspace;
+ ctx->out.pix.ycbcr_enc = pix_fmt->ycbcr_enc;
+ ctx->out.pix.quantization = pix_fmt->quantization;
+ ctx->out.pix.xfer_func = pix_fmt->xfer_func;
+ }
+
/* Reset crop settings */
frm->crop.left = 0;
frm->crop.top = 0;
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 11/28] media: rockchip: rga: move hw specific parts to a dedicated struct
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (9 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 10/28] media: rockchip: rga: announce and sync colorimetry Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:05 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 12/28] media: rockchip: rga: avoid odd frame sizes for YUV formats Sven Püschel
` (16 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
In preparation for the RGA3 unit, move RGA2 specific parts from rga.c
to rga-hw.c and create a struct to reference the RGA2 specific functions
and formats. This also allows to remove the rga-hw.h reference from the
include list of the rga driver.
Also document the command finish interrupt with a dedicated define.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga-hw.c | 166 ++++++++++++++++++++-
drivers/media/platform/rockchip/rga/rga-hw.h | 5 +-
drivers/media/platform/rockchip/rga/rga.c | 211 +++++----------------------
drivers/media/platform/rockchip/rga/rga.h | 23 ++-
4 files changed, 227 insertions(+), 178 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index ec6c17504ca15..40498796507e0 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -437,8 +437,8 @@ static void rga_cmd_set(struct rga_ctx *ctx,
PAGE_SIZE, DMA_BIDIRECTIONAL);
}
-void rga_hw_start(struct rockchip_rga *rga,
- struct rga_vb_buffer *src, struct rga_vb_buffer *dst)
+static void rga_hw_start(struct rockchip_rga *rga,
+ struct rga_vb_buffer *src, struct rga_vb_buffer *dst)
{
struct rga_ctx *ctx = rga->curr;
@@ -452,3 +452,165 @@ void rga_hw_start(struct rockchip_rga *rga,
rga_write(rga, RGA_CMD_CTRL, 0x1);
}
+
+static bool rga_handle_irq(struct rockchip_rga *rga)
+{
+ int intr;
+
+ intr = rga_read(rga, RGA_INT) & 0xf;
+
+ rga_mod(rga, RGA_INT, intr << 4, 0xf << 4);
+
+ return intr & RGA_INT_COMMAND_FINISHED;
+}
+
+static void rga_get_version(struct rockchip_rga *rga)
+{
+ rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF;
+ rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F;
+}
+
+static struct rga_fmt formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB32,
+ .color_swap = RGA_COLOR_ALPHA_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR8888,
+ .depth = 32,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR8888,
+ .depth = 32,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_XBGR8888,
+ .depth = 32,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_RGB888,
+ .depth = 24,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_RGB888,
+ .depth = 24,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB444,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR4444,
+ .depth = 16,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB555,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_ABGR1555,
+ .depth = 16,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .color_swap = RGA_COLOR_RB_SWAP,
+ .hw_format = RGA_COLOR_FMT_BGR565,
+ .depth = 16,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .color_swap = RGA_COLOR_UV_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420SP,
+ .depth = 12,
+ .y_div = 2,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .color_swap = RGA_COLOR_UV_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV422SP,
+ .depth = 16,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420SP,
+ .depth = 12,
+ .y_div = 2,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420SP,
+ .depth = 12,
+ .y_div = 2,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV422SP,
+ .depth = 16,
+ .y_div = 1,
+ .x_div = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420P,
+ .depth = 12,
+ .y_div = 2,
+ .x_div = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUV422P,
+ .color_swap = RGA_COLOR_NONE_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV422P,
+ .depth = 16,
+ .y_div = 1,
+ .x_div = 2,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVU420,
+ .color_swap = RGA_COLOR_UV_SWAP,
+ .hw_format = RGA_COLOR_FMT_YUV420P,
+ .depth = 12,
+ .y_div = 2,
+ .x_div = 2,
+ },
+};
+
+const struct rga_hw rga2_hw = {
+ .formats = formats,
+ .num_formats = ARRAY_SIZE(formats),
+ .cmdbuf_size = RGA_CMDBUF_SIZE,
+ .min_width = MIN_WIDTH,
+ .max_width = MAX_WIDTH,
+ .min_height = MIN_HEIGHT,
+ .max_height = MAX_HEIGHT,
+
+ .start = rga_hw_start,
+ .handle_irq = rga_handle_irq,
+ .get_version = rga_get_version,
+};
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h
index 2b8537a5fd0d7..c2e34be751939 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.h
+++ b/drivers/media/platform/rockchip/rga/rga-hw.h
@@ -15,9 +15,6 @@
#define MIN_WIDTH 34
#define MIN_HEIGHT 34
-#define DEFAULT_WIDTH 100
-#define DEFAULT_HEIGHT 100
-
#define RGA_TIMEOUT 500
/* Registers address */
@@ -178,6 +175,8 @@
#define RGA_ALPHA_COLOR_NORMAL 0
#define RGA_ALPHA_COLOR_MULTIPLY_CAL 1
+#define RGA_INT_COMMAND_FINISHED 4
+
/* Registers union */
union rga_mode_ctrl {
unsigned int val;
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 8c34f73d69764..f599c992829dd 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -25,7 +25,6 @@
#include <media/videobuf2-dma-sg.h>
#include <media/videobuf2-v4l2.h>
-#include "rga-hw.h"
#include "rga.h"
static int debug;
@@ -47,7 +46,7 @@ static void device_run(void *prv)
dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
- rga_hw_start(rga, vb_to_rga(src), vb_to_rga(dst));
+ rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst));
spin_unlock_irqrestore(&rga->ctrl_lock, flags);
}
@@ -55,13 +54,8 @@ static void device_run(void *prv)
static irqreturn_t rga_isr(int irq, void *prv)
{
struct rockchip_rga *rga = prv;
- int intr;
- intr = rga_read(rga, RGA_INT) & 0xf;
-
- rga_mod(rga, RGA_INT, intr << 4, 0xf << 4);
-
- if (intr & 0x04) {
+ if (rga->hw->handle_irq(rga)) {
struct vb2_v4l2_buffer *src, *dst;
struct rga_ctx *ctx = rga->curr;
@@ -184,158 +178,17 @@ static int rga_setup_ctrls(struct rga_ctx *ctx)
return 0;
}
-static struct rga_fmt formats[] = {
- {
- .fourcc = V4L2_PIX_FMT_ARGB32,
- .color_swap = RGA_COLOR_ALPHA_SWAP,
- .hw_format = RGA_COLOR_FMT_ABGR8888,
- .depth = 32,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ABGR32,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_ABGR8888,
- .depth = 32,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_XBGR32,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_XBGR8888,
- .depth = 32,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB24,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_RGB888,
- .depth = 24,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_BGR24,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_RGB888,
- .depth = 24,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ARGB444,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_ABGR4444,
- .depth = 16,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_ARGB555,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_ABGR1555,
- .depth = 16,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_RGB565,
- .color_swap = RGA_COLOR_RB_SWAP,
- .hw_format = RGA_COLOR_FMT_BGR565,
- .depth = 16,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV21,
- .color_swap = RGA_COLOR_UV_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420SP,
- .depth = 12,
- .y_div = 2,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV61,
- .color_swap = RGA_COLOR_UV_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV422SP,
- .depth = 16,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV12,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420SP,
- .depth = 12,
- .y_div = 2,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV12M,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420SP,
- .depth = 12,
- .y_div = 2,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_NV16,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV422SP,
- .depth = 16,
- .y_div = 1,
- .x_div = 1,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV420,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420P,
- .depth = 12,
- .y_div = 2,
- .x_div = 2,
- },
- {
- .fourcc = V4L2_PIX_FMT_YUV422P,
- .color_swap = RGA_COLOR_NONE_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV422P,
- .depth = 16,
- .y_div = 1,
- .x_div = 2,
- },
- {
- .fourcc = V4L2_PIX_FMT_YVU420,
- .color_swap = RGA_COLOR_UV_SWAP,
- .hw_format = RGA_COLOR_FMT_YUV420P,
- .depth = 12,
- .y_div = 2,
- .x_div = 2,
- },
-};
-
-#define NUM_FORMATS ARRAY_SIZE(formats)
-
-static struct rga_fmt *rga_fmt_find(u32 pixelformat)
+static struct rga_fmt *rga_fmt_find(struct rockchip_rga *rga, u32 pixelformat)
{
unsigned int i;
- for (i = 0; i < NUM_FORMATS; i++) {
- if (formats[i].fourcc == pixelformat)
- return &formats[i];
+ for (i = 0; i < rga->hw->num_formats; i++) {
+ if (rga->hw->formats[i].fourcc == pixelformat)
+ return &rga->hw->formats[i];
}
return NULL;
}
-static struct rga_frame def_frame = {
- .crop.left = 0,
- .crop.top = 0,
- .crop.width = DEFAULT_WIDTH,
- .crop.height = DEFAULT_HEIGHT,
- .fmt = &formats[0],
-};
-
struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type)
{
if (V4L2_TYPE_IS_OUTPUT(type))
@@ -350,6 +203,18 @@ static int rga_open(struct file *file)
struct rockchip_rga *rga = video_drvdata(file);
struct rga_ctx *ctx = NULL;
int ret = 0;
+ u32 def_width = clamp(DEFAULT_WIDTH, rga->hw->min_width, rga->hw->max_width);
+ u32 def_height = clamp(DEFAULT_HEIGHT, rga->hw->min_height, rga->hw->max_height);
+ struct rga_frame def_frame = {
+ .crop.left = 0,
+ .crop.top = 0,
+ .crop.width = def_width,
+ .crop.height = def_height,
+ .fmt = &rga->hw->formats[0],
+ };
+
+ def_frame.stride = (def_width * def_frame.fmt->depth) >> 3;
+ def_frame.size = def_frame.stride * def_height;
ctx = kzalloc_obj(*ctx);
if (!ctx)
@@ -360,9 +225,9 @@ static int rga_open(struct file *file)
ctx->out = def_frame;
v4l2_fill_pixfmt_mp(&ctx->in.pix,
- ctx->in.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ ctx->in.fmt->fourcc, def_width, def_height);
v4l2_fill_pixfmt_mp(&ctx->out.pix,
- ctx->out.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ ctx->out.fmt->fourcc, def_width, def_height);
if (mutex_lock_interruptible(&rga->mutex)) {
kfree(ctx);
@@ -429,12 +294,13 @@ vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)
{
+ struct rockchip_rga *rga = video_drvdata(file);
struct rga_fmt *fmt;
- if (f->index >= NUM_FORMATS)
+ if (f->index >= rga->hw->num_formats)
return -EINVAL;
- fmt = &formats[f->index];
+ fmt = &rga->hw->formats[f->index];
f->pixelformat = fmt->fourcc;
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
@@ -469,6 +335,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
{
struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp;
struct rga_ctx *ctx = file_to_rga_ctx(file);
+ const struct rga_hw *hw = ctx->rga->hw;
struct rga_fmt *fmt;
if (V4L2_TYPE_IS_CAPTURE(f->type)) {
@@ -487,14 +354,14 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
pix_fmt->xfer_func = frm->pix.xfer_func;
}
- fmt = rga_fmt_find(pix_fmt->pixelformat);
+ fmt = rga_fmt_find(ctx->rga, pix_fmt->pixelformat);
if (!fmt)
- fmt = &formats[0];
+ fmt = &hw->formats[0];
pix_fmt->width = clamp(pix_fmt->width,
- (u32)MIN_WIDTH, (u32)MAX_WIDTH);
+ hw->min_width, hw->max_width);
pix_fmt->height = clamp(pix_fmt->height,
- (u32)MIN_HEIGHT, (u32)MAX_HEIGHT);
+ hw->min_height, hw->max_height);
v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height);
pix_fmt->field = V4L2_FIELD_NONE;
@@ -529,7 +396,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
frm->size = 0;
for (i = 0; i < pix_fmt->num_planes; i++)
frm->size += pix_fmt->plane_fmt[i].sizeimage;
- frm->fmt = rga_fmt_find(pix_fmt->pixelformat);
+ frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat);
frm->stride = pix_fmt->plane_fmt[0].bytesperline;
/*
@@ -660,7 +527,7 @@ static int vidioc_s_selection(struct file *file, void *priv,
if (s->r.left + s->r.width > f->pix.width ||
s->r.top + s->r.height > f->pix.height ||
- s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) {
+ s->r.width < rga->hw->min_width || s->r.height < rga->hw->min_height) {
v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n");
return -EINVAL;
}
@@ -770,6 +637,10 @@ static int rga_probe(struct platform_device *pdev)
if (!rga)
return -ENOMEM;
+ rga->hw = of_device_get_match_data(&pdev->dev);
+ if (!rga->hw)
+ return dev_err_probe(&pdev->dev, -ENODEV, "failed to get match data\n");
+
rga->dev = &pdev->dev;
spin_lock_init(&rga->ctrl_lock);
mutex_init(&rga->mutex);
@@ -833,8 +704,7 @@ static int rga_probe(struct platform_device *pdev)
if (ret < 0)
goto rel_m2m;
- rga->version.major = (rga_read(rga, RGA_VERSION_INFO) >> 24) & 0xFF;
- rga->version.minor = (rga_read(rga, RGA_VERSION_INFO) >> 20) & 0x0F;
+ rga->hw->get_version(rga);
v4l2_info(&rga->v4l2_dev, "HW Version: 0x%02x.%02x\n",
rga->version.major, rga->version.minor);
@@ -842,7 +712,7 @@ static int rga_probe(struct platform_device *pdev)
pm_runtime_put(rga->dev);
/* Create CMD buffer */
- rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, RGA_CMDBUF_SIZE,
+ rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size,
&rga->cmdbuf_phy, GFP_KERNEL,
DMA_ATTR_WRITE_COMBINE);
if (!rga->cmdbuf_virt) {
@@ -850,9 +720,6 @@ static int rga_probe(struct platform_device *pdev)
goto rel_m2m;
}
- def_frame.stride = (DEFAULT_WIDTH * def_frame.fmt->depth) >> 3;
- def_frame.size = def_frame.stride * DEFAULT_HEIGHT;
-
ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&rga->v4l2_dev, "Failed to register video device\n");
@@ -865,7 +732,7 @@ static int rga_probe(struct platform_device *pdev)
return 0;
free_dma:
- dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt,
+ dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt,
rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
rel_m2m:
v4l2_m2m_release(rga->m2m_dev);
@@ -883,7 +750,7 @@ static void rga_remove(struct platform_device *pdev)
{
struct rockchip_rga *rga = platform_get_drvdata(pdev);
- dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt,
+ dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt,
rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
v4l2_info(&rga->v4l2_dev, "Removing\n");
@@ -919,9 +786,11 @@ static const struct dev_pm_ops rga_pm = {
static const struct of_device_id rockchip_rga_match[] = {
{
.compatible = "rockchip,rk3288-rga",
+ .data = &rga2_hw,
},
{
.compatible = "rockchip,rk3399-rga",
+ .data = &rga2_hw,
},
{},
};
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index c4a3905a48f0d..640e510285341 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -14,6 +14,9 @@
#define RGA_NAME "rockchip-rga"
+#define DEFAULT_WIDTH 100
+#define DEFAULT_HEIGHT 100
+
struct rga_fmt {
u32 fourcc;
int depth;
@@ -68,6 +71,8 @@ static inline struct rga_ctx *file_to_rga_ctx(struct file *filp)
return container_of(file_to_v4l2_fh(filp), struct rga_ctx, fh);
}
+struct rga_hw;
+
struct rockchip_rga {
struct v4l2_device v4l2_dev;
struct v4l2_m2m_dev *m2m_dev;
@@ -88,6 +93,8 @@ struct rockchip_rga {
struct rga_ctx *curr;
dma_addr_t cmdbuf_phy;
void *cmdbuf_virt;
+
+ const struct rga_hw *hw;
};
struct rga_addr_offset {
@@ -138,7 +145,19 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask)
rga_write(rga, reg, temp);
};
-void rga_hw_start(struct rockchip_rga *rga,
- struct rga_vb_buffer *src, struct rga_vb_buffer *dst);
+struct rga_hw {
+ struct rga_fmt *formats;
+ u32 num_formats;
+ size_t cmdbuf_size;
+ u32 min_width, min_height;
+ u32 max_width, max_height;
+
+ void (*start)(struct rockchip_rga *rga,
+ struct rga_vb_buffer *src, struct rga_vb_buffer *dst);
+ bool (*handle_irq)(struct rockchip_rga *rga);
+ void (*get_version)(struct rockchip_rga *rga);
+};
+
+extern const struct rga_hw rga2_hw;
#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 12/28] media: rockchip: rga: avoid odd frame sizes for YUV formats
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (10 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 11/28] media: rockchip: rga: move hw specific parts to a dedicated struct Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 13/28] media: rockchip: rga: calculate x_div/y_div using v4l2_format_info Sven Püschel
` (15 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Avoid odd frame sizes for YUV formats, as they may cause undefined
behavior. This is done in preparation for the RGA3, which hangs when the
output format is set to 129x129 pixel YUV420 SP (NV12).
This requirement is documented explicitly for the RGA3 in section 5.6.3
of the RK3588 TRM Part 2. For the RGA2 the RK3588 TRM Part 2
(section 6.1.2) and RK3568 TRM Part 2 (section 14.2) only mentions the
x/y offsets and stride aligning requirements. But the vendor driver for
the RGA2 also contains checks for the width and height to be aligned to
2 bytes.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga.c | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index f599c992829dd..77b8c7ab74274 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -337,6 +337,19 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
struct rga_ctx *ctx = file_to_rga_ctx(file);
const struct rga_hw *hw = ctx->rga->hw;
struct rga_fmt *fmt;
+ struct v4l2_frmsize_stepwise frmsize = {
+ .min_width = hw->min_width,
+ .max_width = hw->max_width,
+ .min_height = hw->min_height,
+ .max_height = hw->max_height,
+ .step_width = 1,
+ .step_height = 1,
+ };
+
+ if (v4l2_is_format_yuv(v4l2_format_info(pix_fmt->pixelformat))) {
+ frmsize.step_width = 2;
+ frmsize.step_height = 2;
+ }
if (V4L2_TYPE_IS_CAPTURE(f->type)) {
const struct rga_frame *frm;
@@ -358,11 +371,7 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
if (!fmt)
fmt = &hw->formats[0];
- pix_fmt->width = clamp(pix_fmt->width,
- hw->min_width, hw->max_width);
- pix_fmt->height = clamp(pix_fmt->height,
- hw->min_height, hw->max_height);
-
+ v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize);
v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height);
pix_fmt->field = V4L2_FIELD_NONE;
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 13/28] media: rockchip: rga: calculate x_div/y_div using v4l2_format_info
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (11 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 12/28] media: rockchip: rga: avoid odd frame sizes for YUV formats Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 14/28] media: rockchip: rga: move cmdbuf to rga_ctx Sven Püschel
` (14 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Calculate the x_div and y_div variables with the information from
v4l2_format_info instead of storing these in the rga_fmt struct.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga-hw.c | 45 +++++++---------------------
drivers/media/platform/rockchip/rga/rga.h | 2 --
2 files changed, 11 insertions(+), 36 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 40498796507e0..17f7a67c0b4bb 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -42,6 +42,7 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset,
{
struct rga_corners_addr_offset offsets;
struct rga_addr_offset *lt, *lb, *rt, *rb;
+ const struct v4l2_format_info *format_info;
unsigned int x_div = 0,
y_div = 0, uv_stride = 0, pixel_width = 0;
@@ -50,8 +51,16 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset,
rt = &offsets.right_top;
rb = &offsets.right_bottom;
- x_div = frm->fmt->x_div;
- y_div = frm->fmt->y_div;
+ format_info = v4l2_format_info(frm->pix.pixelformat);
+ /* x_div is only used for the u/v planes.
+ * When the format doesn't have these, use 1 to avoid a division by zero.
+ */
+ if (format_info->bpp[1])
+ x_div = format_info->hdiv * format_info->bpp_div[1] /
+ format_info->bpp[1];
+ else
+ x_div = 1;
+ y_div = format_info->vdiv;
uv_stride = frm->stride / x_div;
pixel_width = frm->stride / frm->pix.width;
@@ -476,128 +485,96 @@ static struct rga_fmt formats[] = {
.color_swap = RGA_COLOR_ALPHA_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR8888,
.depth = 32,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_ABGR32,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR8888,
.depth = 32,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_XBGR32,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_XBGR8888,
.depth = 32,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_RGB24,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_RGB888,
.depth = 24,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_BGR24,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_RGB888,
.depth = 24,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_ARGB444,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR4444,
.depth = 16,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_ARGB555,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR1555,
.depth = 16,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_RGB565,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_BGR565,
.depth = 16,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_NV21,
.color_swap = RGA_COLOR_UV_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420SP,
.depth = 12,
- .y_div = 2,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_NV61,
.color_swap = RGA_COLOR_UV_SWAP,
.hw_format = RGA_COLOR_FMT_YUV422SP,
.depth = 16,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_NV12,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420SP,
.depth = 12,
- .y_div = 2,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_NV12M,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420SP,
.depth = 12,
- .y_div = 2,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_NV16,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV422SP,
.depth = 16,
- .y_div = 1,
- .x_div = 1,
},
{
.fourcc = V4L2_PIX_FMT_YUV420,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420P,
.depth = 12,
- .y_div = 2,
- .x_div = 2,
},
{
.fourcc = V4L2_PIX_FMT_YUV422P,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV422P,
.depth = 16,
- .y_div = 1,
- .x_div = 2,
},
{
.fourcc = V4L2_PIX_FMT_YVU420,
.color_swap = RGA_COLOR_UV_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420P,
.depth = 12,
- .y_div = 2,
- .x_div = 2,
},
};
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 640e510285341..27b3c9b4f220c 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -20,8 +20,6 @@
struct rga_fmt {
u32 fourcc;
int depth;
- u8 y_div;
- u8 x_div;
u8 color_swap;
u8 hw_format;
};
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 14/28] media: rockchip: rga: move cmdbuf to rga_ctx
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (12 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 13/28] media: rockchip: rga: calculate x_div/y_div using v4l2_format_info Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:12 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 15/28] media: rockchip: rga: align stride to 4 bytes Sven Püschel
` (13 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Move the command buffer to the rga_ctx struct in preparation to reuse
an already prepared command buffer. This allows to split the command
buffer setup in a further commit to setup a template for the command
buffer at streamon and only update the buffer addresses in device_run
and trigger the command stream. No sync point is added, as one command
buffer should only be used for one conversion at a time.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga-hw.c | 28 +++++++---------
drivers/media/platform/rockchip/rga/rga.c | 48 ++++++++++++++++------------
drivers/media/platform/rockchip/rga/rga.h | 5 +--
3 files changed, 41 insertions(+), 40 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 17f7a67c0b4bb..9881c14f908d5 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -122,8 +122,7 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct
static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
{
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int reg;
reg = RGA_MMU_SRC_BASE - RGA_MODE_BASE_REG;
@@ -135,8 +134,7 @@ static void rga_cmd_set_src_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
{
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int reg;
reg = RGA_MMU_SRC1_BASE - RGA_MODE_BASE_REG;
@@ -148,8 +146,7 @@ static void rga_cmd_set_src1_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
{
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int reg;
reg = RGA_MMU_DST_BASE - RGA_MODE_BASE_REG;
@@ -162,7 +159,7 @@ static void rga_cmd_set_dst_addr(struct rga_ctx *ctx, dma_addr_t dma_addr)
static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
{
struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int scale_dst_w, scale_dst_h;
unsigned int src_h, src_w, dst_h, dst_w;
union rga_src_info src_info;
@@ -322,8 +319,7 @@ static void rga_cmd_set_src_info(struct rga_ctx *ctx,
struct rga_addr_offset *offset)
{
struct rga_corners_addr_offset src_offsets;
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int src_h, src_w, src_x, src_y;
src_h = ctx->in.crop.height;
@@ -350,8 +346,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
{
struct rga_addr_offset *dst_offset;
struct rga_corners_addr_offset offsets;
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
unsigned int dst_h, dst_w, dst_x, dst_y;
unsigned int mir_mode = 0;
unsigned int rot_mode = 0;
@@ -397,8 +392,7 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
static void rga_cmd_set_mode(struct rga_ctx *ctx)
{
- struct rockchip_rga *rga = ctx->rga;
- u32 *dest = rga->cmdbuf_virt;
+ u32 *dest = ctx->cmdbuf_virt;
union rga_mode_ctrl mode;
union rga_alpha_ctrl0 alpha_ctrl0;
union rga_alpha_ctrl1 alpha_ctrl1;
@@ -423,7 +417,7 @@ static void rga_cmd_set(struct rga_ctx *ctx,
{
struct rockchip_rga *rga = ctx->rga;
- memset(rga->cmdbuf_virt, 0, RGA_CMDBUF_SIZE);
+ memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE);
rga_cmd_set_src_addr(ctx, src->dma_desc_pa);
/*
@@ -439,11 +433,11 @@ static void rga_cmd_set(struct rga_ctx *ctx,
rga_cmd_set_dst_info(ctx, &dst->offset);
rga_cmd_set_trans_info(ctx);
- rga_write(rga, RGA_CMD_BASE, rga->cmdbuf_phy);
+ rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy);
/* sync CMD buf for RGA */
- dma_sync_single_for_device(rga->dev, rga->cmdbuf_phy,
- PAGE_SIZE, DMA_BIDIRECTIONAL);
+ dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
}
static void rga_hw_start(struct rockchip_rga *rga,
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 77b8c7ab74274..bf6bbcbfc869b 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -219,6 +219,16 @@ static int rga_open(struct file *file)
ctx = kzalloc_obj(*ctx);
if (!ctx)
return -ENOMEM;
+
+ /* Create CMD buffer */
+ ctx->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size,
+ &ctx->cmdbuf_phy, GFP_KERNEL,
+ DMA_ATTR_WRITE_COMBINE);
+ if (!ctx->cmdbuf_virt) {
+ ret = -ENOMEM;
+ goto rel_ctx;
+ }
+
ctx->rga = rga;
/* Set default formats */
ctx->in = def_frame;
@@ -230,15 +240,13 @@ static int rga_open(struct file *file)
ctx->out.fmt->fourcc, def_width, def_height);
if (mutex_lock_interruptible(&rga->mutex)) {
- kfree(ctx);
- return -ERESTARTSYS;
+ ret = -ERESTARTSYS;
+ goto rel_cmdbuf;
}
ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rga->m2m_dev, ctx, &queue_init);
if (IS_ERR(ctx->fh.m2m_ctx)) {
ret = PTR_ERR(ctx->fh.m2m_ctx);
- mutex_unlock(&rga->mutex);
- kfree(ctx);
- return ret;
+ goto unlock_mutex;
}
v4l2_fh_init(&ctx->fh, video_devdata(file));
v4l2_fh_add(&ctx->fh, file);
@@ -252,6 +260,15 @@ static int rga_open(struct file *file)
mutex_unlock(&rga->mutex);
return 0;
+
+unlock_mutex:
+ mutex_unlock(&rga->mutex);
+rel_cmdbuf:
+ dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt,
+ ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
+rel_ctx:
+ kfree(ctx);
+ return ret;
}
static int rga_release(struct file *file)
@@ -266,6 +283,10 @@ static int rga_release(struct file *file)
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
v4l2_fh_del(&ctx->fh, file);
v4l2_fh_exit(&ctx->fh);
+
+ dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt,
+ ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
+
kfree(ctx);
mutex_unlock(&rga->mutex);
@@ -720,19 +741,10 @@ static int rga_probe(struct platform_device *pdev)
pm_runtime_put(rga->dev);
- /* Create CMD buffer */
- rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size,
- &rga->cmdbuf_phy, GFP_KERNEL,
- DMA_ATTR_WRITE_COMBINE);
- if (!rga->cmdbuf_virt) {
- ret = -ENOMEM;
- goto rel_m2m;
- }
-
ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
if (ret) {
v4l2_err(&rga->v4l2_dev, "Failed to register video device\n");
- goto free_dma;
+ goto rel_m2m;
}
v4l2_info(&rga->v4l2_dev, "Registered %s as /dev/%s\n",
@@ -740,9 +752,6 @@ static int rga_probe(struct platform_device *pdev)
return 0;
-free_dma:
- dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt,
- rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
rel_m2m:
v4l2_m2m_release(rga->m2m_dev);
rel_vdev:
@@ -759,9 +768,6 @@ static void rga_remove(struct platform_device *pdev)
{
struct rockchip_rga *rga = platform_get_drvdata(pdev);
- dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt,
- rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
-
v4l2_info(&rga->v4l2_dev, "Removing\n");
v4l2_m2m_release(rga->m2m_dev);
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 27b3c9b4f220c..04aeb7b429523 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -53,6 +53,9 @@ struct rga_ctx {
struct rga_frame out;
struct v4l2_ctrl_handler ctrl_handler;
+ void *cmdbuf_virt;
+ dma_addr_t cmdbuf_phy;
+
int osequence;
int csequence;
@@ -89,8 +92,6 @@ struct rockchip_rga {
spinlock_t ctrl_lock;
struct rga_ctx *curr;
- dma_addr_t cmdbuf_phy;
- void *cmdbuf_virt;
const struct rga_hw *hw;
};
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 15/28] media: rockchip: rga: align stride to 4 bytes
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (13 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 14/28] media: rockchip: rga: move cmdbuf to rga_ctx Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:17 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 16/28] media: rockchip: rga: reuse cmdbuf contents Sven Püschel
` (12 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Add an alignment setting to rga_hw to set the desired stride alignment.
As the RGA2 register for the stride counts in word units, the code
already divides the bytesperline value by 4 when writing it into the
register. Therefore fix the alignment to a multiple of 4 to avoid
potential off by one errors due from the division.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changed in v6:
- Drop unintended change from fmt->fourcc to pix_fmt->pixelformat
Flagged by Sashiko:
https://sashiko.dev/#/patchset/20260428-spu-rga3-v5-0-eb7f5d019d86%40pengutronix.de?part=15
---
drivers/media/platform/rockchip/rga/rga-hw.c | 1 +
drivers/media/platform/rockchip/rga/rga.c | 11 ++++++-----
drivers/media/platform/rockchip/rga/rga.h | 1 +
3 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 9881c14f908d5..dac3cb6aa17d3 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -580,6 +580,7 @@ const struct rga_hw rga2_hw = {
.max_width = MAX_WIDTH,
.min_height = MIN_HEIGHT,
.max_height = MAX_HEIGHT,
+ .stride_alignment = 4,
.start = rga_hw_start,
.handle_irq = rga_handle_irq,
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index bf6bbcbfc869b..d080cb672740b 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -234,10 +234,10 @@ static int rga_open(struct file *file)
ctx->in = def_frame;
ctx->out = def_frame;
- v4l2_fill_pixfmt_mp(&ctx->in.pix,
- ctx->in.fmt->fourcc, def_width, def_height);
- v4l2_fill_pixfmt_mp(&ctx->out.pix,
- ctx->out.fmt->fourcc, def_width, def_height);
+ v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.fmt->fourcc,
+ def_width, def_height, rga->hw->stride_alignment);
+ v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.fmt->fourcc,
+ def_width, def_height, rga->hw->stride_alignment);
if (mutex_lock_interruptible(&rga->mutex)) {
ret = -ERESTARTSYS;
@@ -393,7 +393,8 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
fmt = &hw->formats[0];
v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize);
- v4l2_fill_pixfmt_mp(pix_fmt, fmt->fourcc, pix_fmt->width, pix_fmt->height);
+ v4l2_fill_pixfmt_mp_aligned(pix_fmt, fmt->fourcc,
+ pix_fmt->width, pix_fmt->height, hw->stride_alignment);
pix_fmt->field = V4L2_FIELD_NONE;
return 0;
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 04aeb7b429523..38518146910a6 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -150,6 +150,7 @@ struct rga_hw {
size_t cmdbuf_size;
u32 min_width, min_height;
u32 max_width, max_height;
+ u8 stride_alignment;
void (*start)(struct rockchip_rga *rga,
struct rga_vb_buffer *src, struct rga_vb_buffer *dst);
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 16/28] media: rockchip: rga: reuse cmdbuf contents
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (14 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 15/28] media: rockchip: rga: align stride to 4 bytes Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:59 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 17/28] media: rockchip: rga: check scaling factor Sven Püschel
` (11 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel
Reuse the command buffer contents instead of completely writing it
for every frame. Therefore we only need to replace the source and
destination addresses for each frame. This reduces the amount of CPU
and memory operations done in each frame. A new cmdbuf_dirty flag notes
if the cmdbuf has to be rewritten on the next frame.
The initial idea of initializing the cmdbuf on streamon broke the
ability to update controls while streaming (e.g. mirroring).
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- Reworked the commit to not setup the cmdbuf on streamon but rather
re-initialize it on the next frame when something changed.
- Sasahiko flagged the cmdbuf setup at streamon:
https://sashiko.dev/#/patchset/20260428-spu-rga3-v5-0-eb7f5d019d86%40pengutronix.de?part=17
- Dropped Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
due to the reworked patch and commit message contents
Changes in v5:
- Don't set the flipping and rotation values at streamon and preventing
the userspace from chainging them at runtime
---
drivers/media/platform/rockchip/rga/rga-hw.c | 13 +++++++++----
drivers/media/platform/rockchip/rga/rga.c | 11 +++++++++--
drivers/media/platform/rockchip/rga/rga.h | 2 ++
3 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index dac3cb6aa17d3..567d39e58d33f 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -417,8 +417,6 @@ static void rga_cmd_set(struct rga_ctx *ctx,
{
struct rockchip_rga *rga = ctx->rga;
- memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE);
-
rga_cmd_set_src_addr(ctx, src->dma_desc_pa);
/*
* Due to hardware bug,
@@ -427,11 +425,9 @@ static void rga_cmd_set(struct rga_ctx *ctx,
rga_cmd_set_src1_addr(ctx, dst->dma_desc_pa);
rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa);
- rga_cmd_set_mode(ctx);
rga_cmd_set_src_info(ctx, &src->offset);
rga_cmd_set_dst_info(ctx, &dst->offset);
- rga_cmd_set_trans_info(ctx);
rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy);
@@ -440,6 +436,14 @@ static void rga_cmd_set(struct rga_ctx *ctx,
PAGE_SIZE, DMA_BIDIRECTIONAL);
}
+static void rga_hw_setup_cmdbuf(struct rga_ctx *ctx)
+{
+ memset(ctx->cmdbuf_virt, 0, RGA_CMDBUF_SIZE);
+
+ rga_cmd_set_mode(ctx);
+ rga_cmd_set_trans_info(ctx);
+}
+
static void rga_hw_start(struct rockchip_rga *rga,
struct rga_vb_buffer *src, struct rga_vb_buffer *dst)
{
@@ -582,6 +586,7 @@ const struct rga_hw rga2_hw = {
.max_height = MAX_HEIGHT,
.stride_alignment = 4,
+ .setup_cmdbuf = rga_hw_setup_cmdbuf,
.start = rga_hw_start,
.handle_irq = rga_handle_irq,
.get_version = rga_get_version,
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index d080cb672740b..394b14b9469df 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -38,6 +38,11 @@ static void device_run(void *prv)
unsigned long flags;
spin_lock_irqsave(&rga->ctrl_lock, flags);
+ if (ctx->cmdbuf_dirty) {
+ ctx->cmdbuf_dirty = false;
+ rga->hw->setup_cmdbuf(ctx);
+ }
+ spin_unlock_irqrestore(&rga->ctrl_lock, flags);
rga->curr = ctx;
@@ -47,8 +52,6 @@ static void device_run(void *prv)
dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst));
-
- spin_unlock_irqrestore(&rga->ctrl_lock, flags);
}
static irqreturn_t rga_isr(int irq, void *prv)
@@ -141,6 +144,7 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl)
ctx->fill_color = ctrl->val;
break;
}
+ ctx->cmdbuf_dirty = true;
spin_unlock_irqrestore(&ctx->rga->ctrl_lock, flags);
return 0;
}
@@ -228,6 +232,7 @@ static int rga_open(struct file *file)
ret = -ENOMEM;
goto rel_ctx;
}
+ ctx->cmdbuf_dirty = true;
ctx->rga = rga;
/* Set default formats */
@@ -448,6 +453,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
frm->crop.height = pix_fmt->height;
frm->pix = *pix_fmt;
+ ctx->cmdbuf_dirty = true;
v4l2_dbg(debug, 1, &rga->v4l2_dev,
"[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n",
@@ -564,6 +570,7 @@ static int vidioc_s_selection(struct file *file, void *priv,
}
f->crop = s->r;
+ ctx->cmdbuf_dirty = true;
return ret;
}
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 38518146910a6..5360f092fecf0 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -55,6 +55,7 @@ struct rga_ctx {
void *cmdbuf_virt;
dma_addr_t cmdbuf_phy;
+ bool cmdbuf_dirty;
int osequence;
int csequence;
@@ -152,6 +153,7 @@ struct rga_hw {
u32 max_width, max_height;
u8 stride_alignment;
+ void (*setup_cmdbuf)(struct rga_ctx *ctx);
void (*start)(struct rockchip_rga *rga,
struct rga_vb_buffer *src, struct rga_vb_buffer *dst);
bool (*handle_irq)(struct rockchip_rga *rga);
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 17/28] media: rockchip: rga: check scaling factor
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (15 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 16/28] media: rockchip: rga: reuse cmdbuf contents Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:54 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 18/28] media: rockchip: rga: use card type to specify rga type Sven Püschel
` (10 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel
Check the scaling factor to avoid potential problems. This is relevant
for the upcoming RGA3 support, as it can hang when the scaling factor
is exceeded.
The check is done at streamon when the other side is already streaming
to avoid incorrectly failing if the application configures the other
side after calling streamon. As try_fmt shouldn't be state aware,
it cannot be used to limit the format based on the scaling factor.
Therefore the check is done just before the actual streaming would be
started.
As the driver allows changing the rotation and selection while
streaming, add additional checks to ensure these changes
don't exceed the scaling factor.
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- Dropped scaling adjustment in s_fmt, as this didn't match the try_fmt
result (which shouldn't have it to avoid making it stateful)
- Moved scaling check to the prepare_streaming callback instead of
overwriting the ioctl directly
- Consider rotation when checking the scaling
- Check scaling factor when adjusting rotation and selection while
streaming
---
drivers/media/platform/rockchip/rga/rga-buf.c | 28 ++++++++++++
drivers/media/platform/rockchip/rga/rga-hw.c | 1 +
drivers/media/platform/rockchip/rga/rga-hw.h | 1 +
drivers/media/platform/rockchip/rga/rga.c | 63 +++++++++++++++++++++++++--
drivers/media/platform/rockchip/rga/rga.h | 4 ++
5 files changed, 94 insertions(+), 3 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index ffc6162b2e681..dcaba66f5c1fc 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -197,6 +197,33 @@ static void rga_buf_return_buffers(struct vb2_queue *q,
}
}
+static int rga_buf_prepare_streaming(struct vb2_queue *q)
+{
+ struct rga_ctx *ctx = vb2_get_drv_priv(q);
+ const struct rga_hw *hw = ctx->rga->hw;
+ int ret;
+
+ /* It's safe to check the streaming state of the other queue,
+ * as the streamon ioctl's can't race due to the lock set in
+ * the queue_init function.
+ */
+ if ((V4L2_TYPE_IS_OUTPUT(q->type) &&
+ vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx))) ||
+ (V4L2_TYPE_IS_CAPTURE(q->type) &&
+ vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx)))) {
+ /*
+ * As the other side is already streaming,
+ * check that the max scaling factor isn't exceeded.
+ */
+ ret = rga_check_scaling(hw, &ctx->in.crop, &ctx->out.crop,
+ ctx->rotate);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
static int rga_buf_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct rga_ctx *ctx = vb2_get_drv_priv(q);
@@ -232,6 +259,7 @@ const struct vb2_ops rga_qops = {
.buf_prepare = rga_buf_prepare,
.buf_queue = rga_buf_queue,
.buf_cleanup = rga_buf_cleanup,
+ .prepare_streaming = rga_buf_prepare_streaming,
.start_streaming = rga_buf_start_streaming,
.stop_streaming = rga_buf_stop_streaming,
};
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 567d39e58d33f..f2900812ba76f 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -584,6 +584,7 @@ const struct rga_hw rga2_hw = {
.max_width = MAX_WIDTH,
.min_height = MIN_HEIGHT,
.max_height = MAX_HEIGHT,
+ .max_scaling_factor = MAX_SCALING_FACTOR,
.stride_alignment = 4,
.setup_cmdbuf = rga_hw_setup_cmdbuf,
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h
index c2e34be751939..805ec23e5e3f4 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.h
+++ b/drivers/media/platform/rockchip/rga/rga-hw.h
@@ -14,6 +14,7 @@
#define MIN_WIDTH 34
#define MIN_HEIGHT 34
+#define MAX_SCALING_FACTOR 16
#define RGA_TIMEOUT 500
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 394b14b9469df..22954bbae55fc 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -127,7 +127,9 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct rga_ctx *ctx = container_of(ctrl->handler, struct rga_ctx,
ctrl_handler);
+ const struct rga_hw *hw = ctx->rga->hw;
unsigned long flags;
+ int ret = 0;
spin_lock_irqsave(&ctx->rga->ctrl_lock, flags);
switch (ctrl->id) {
@@ -138,6 +140,13 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl)
ctx->vflip = ctrl->val;
break;
case V4L2_CID_ROTATE:
+ if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) &&
+ vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) {
+ ret = rga_check_scaling(hw, &ctx->in.crop,
+ &ctx->out.crop, ctrl->val);
+ if (ret < 0)
+ goto s_ctrl_done;
+ }
ctx->rotate = ctrl->val;
break;
case V4L2_CID_BG_COLOR:
@@ -145,8 +154,10 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl)
break;
}
ctx->cmdbuf_dirty = true;
+
+s_ctrl_done:
spin_unlock_irqrestore(&ctx->rga->ctrl_lock, flags);
- return 0;
+ return ret;
}
static const struct v4l2_ctrl_ops rga_ctrl_ops = {
@@ -182,6 +193,38 @@ static int rga_setup_ctrls(struct rga_ctx *ctx)
return 0;
}
+static bool check_scaling_factor(const struct rga_hw *hw, u32 src_size,
+ u32 dst_size)
+{
+ if (src_size < dst_size)
+ return src_size * hw->max_scaling_factor >= dst_size;
+ else
+ return dst_size * hw->max_scaling_factor >= src_size;
+}
+
+int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in,
+ const struct v4l2_rect *crop_out, u32 rotate)
+{
+ u32 scaled_width;
+ u32 scaled_height;
+
+ if (rotate == 90 || rotate == 270) {
+ scaled_width = crop_out->height;
+ scaled_height = crop_out->width;
+ } else {
+ scaled_width = crop_out->width;
+ scaled_height = crop_out->height;
+ }
+
+ if (!check_scaling_factor(hw, crop_in->width, scaled_width))
+ return -EINVAL;
+
+ if (!check_scaling_factor(hw, crop_in->height, scaled_height))
+ return -EINVAL;
+
+ return 0;
+}
+
static struct rga_fmt *rga_fmt_find(struct rockchip_rga *rga, u32 pixelformat)
{
unsigned int i;
@@ -525,7 +568,6 @@ static int vidioc_s_selection(struct file *file, void *priv,
struct rga_ctx *ctx = file_to_rga_ctx(file);
struct rockchip_rga *rga = ctx->rga;
struct rga_frame *f;
- int ret = 0;
f = rga_get_frame(ctx, s->type);
if (IS_ERR(f))
@@ -569,10 +611,25 @@ static int vidioc_s_selection(struct file *file, void *priv,
return -EINVAL;
}
+ if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) &&
+ vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) {
+ int ret = 0;
+
+ if (V4L2_TYPE_IS_OUTPUT(s->type))
+ ret = rga_check_scaling(rga->hw, &s->r, &ctx->out.crop,
+ ctx->rotate);
+ else
+ ret = rga_check_scaling(rga->hw, &ctx->in.crop, &s->r,
+ ctx->rotate);
+
+ if (ret < 0)
+ return ret;
+ }
+
f->crop = s->r;
ctx->cmdbuf_dirty = true;
- return ret;
+ return 0;
}
static const struct v4l2_ioctl_ops rga_ioctl_ops = {
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 5360f092fecf0..df525c6aea8b6 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -123,6 +123,9 @@ static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb)
struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type);
+int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in,
+ const struct v4l2_rect *crop_out, u32 rotate);
+
/* RGA Buffers Manage */
extern const struct vb2_ops rga_qops;
@@ -151,6 +154,7 @@ struct rga_hw {
size_t cmdbuf_size;
u32 min_width, min_height;
u32 max_width, max_height;
+ u8 max_scaling_factor;
u8 stride_alignment;
void (*setup_cmdbuf)(struct rga_ctx *ctx);
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 18/28] media: rockchip: rga: use card type to specify rga type
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (16 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 17/28] media: rockchip: rga: check scaling factor Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:00 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 19/28] media: rockchip: rga: change offset to dma_addresses Sven Püschel
` (9 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
In preparation of the RGA3 support add a filed to the rga_hw struct
to specify the desired card type value. This allows the user to
differentiate the RGA2 and RGA3 video device nodes.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga-hw.c | 1 +
drivers/media/platform/rockchip/rga/rga.c | 4 +++-
drivers/media/platform/rockchip/rga/rga.h | 1 +
3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index f2900812ba76f..43fd023b7571c 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -577,6 +577,7 @@ static struct rga_fmt formats[] = {
};
const struct rga_hw rga2_hw = {
+ .card_type = "rga2",
.formats = formats,
.num_formats = ARRAY_SIZE(formats),
.cmdbuf_size = RGA_CMDBUF_SIZE,
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 22954bbae55fc..91775b43ff617 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -354,8 +354,10 @@ static const struct v4l2_file_operations rga_fops = {
static int
vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
{
+ struct rockchip_rga *rga = video_drvdata(file);
+
strscpy(cap->driver, RGA_NAME, sizeof(cap->driver));
- strscpy(cap->card, "rockchip-rga", sizeof(cap->card));
+ strscpy(cap->card, rga->hw->card_type, sizeof(cap->card));
strscpy(cap->bus_info, "platform:rga", sizeof(cap->bus_info));
return 0;
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index df525c6aea8b6..cee2e75ea89f1 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -149,6 +149,7 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask)
};
struct rga_hw {
+ const char *card_type;
struct rga_fmt *formats;
u32 num_formats;
size_t cmdbuf_size;
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 19/28] media: rockchip: rga: change offset to dma_addresses
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (17 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 18/28] media: rockchip: rga: use card type to specify rga type Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:59 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 20/28] media: rockchip: rga: support external iommus Sven Püschel
` (8 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Change the offset to dma_addresses, as the current naming is misleading.
The offset naming comes from the fact that it references the offset in
the mapped iommu address space. But from the hardware point of view this
is an address, as also pointed out by the register naming
(e.g. RGA_DST_Y_RGB_BASE_ADDR). Therefore also change the type to
dma_addr_t, as with an external iommu driver this would also be the
correct type.
This change is a preparation for the RGA3 support, which uses an external
iommu and therefore just gets an dma_addr_t for each buffer. The field
renaming allows to reuse the existing fields of rga_vb_buffer to store
these values.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- Also changed dma_addrs variable type to dma_addr_t. Flagged by Sashiko
https://sashiko.dev/#/patchset/20260428-spu-rga3-v5-0-eb7f5d019d86%40pengutronix.de?part=20
---
drivers/media/platform/rockchip/rga/rga-buf.c | 12 +--
drivers/media/platform/rockchip/rga/rga-hw.c | 105 +++++++++++++-------------
drivers/media/platform/rockchip/rga/rga.h | 12 +--
3 files changed, 64 insertions(+), 65 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index dcaba66f5c1fc..ab9554c1c4cd9 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -121,7 +121,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
size_t curr_desc = 0;
int i;
const struct v4l2_format_info *info;
- unsigned int offsets[VIDEO_MAX_PLANES];
+ dma_addr_t dma_addrs[VIDEO_MAX_PLANES];
if (IS_ERR(f))
return PTR_ERR(f);
@@ -145,18 +145,18 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
"Failed to map video buffer to RGA\n");
return n_desc;
}
- offsets[i] = curr_desc << PAGE_SHIFT;
+ dma_addrs[i] = curr_desc << PAGE_SHIFT;
curr_desc += n_desc;
}
/* Fill the remaining planes */
info = v4l2_format_info(f->fmt->fourcc);
for (i = info->mem_planes; i < info->comp_planes; i++)
- offsets[i] = get_plane_offset(f, info, i);
+ dma_addrs[i] = dma_addrs[0] + get_plane_offset(f, info, i);
- rbuf->offset.y_off = offsets[0];
- rbuf->offset.u_off = offsets[1];
- rbuf->offset.v_off = offsets[2];
+ rbuf->dma_addrs.y_addr = dma_addrs[0];
+ rbuf->dma_addrs.u_addr = dma_addrs[1];
+ rbuf->dma_addrs.v_addr = dma_addrs[2];
return 0;
}
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 43fd023b7571c..99cf57d5ba89d 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -16,11 +16,11 @@ enum e_rga_start_pos {
RB = 3,
};
-struct rga_corners_addr_offset {
- struct rga_addr_offset left_top;
- struct rga_addr_offset right_top;
- struct rga_addr_offset left_bottom;
- struct rga_addr_offset right_bottom;
+struct rga_corners_addrs {
+ struct rga_addrs left_top;
+ struct rga_addrs right_top;
+ struct rga_addrs left_bottom;
+ struct rga_addrs right_bottom;
};
static unsigned int rga_get_scaling(unsigned int src, unsigned int dst)
@@ -36,20 +36,20 @@ static unsigned int rga_get_scaling(unsigned int src, unsigned int dst)
return (src > dst) ? ((dst << 16) / src) : ((src << 16) / dst);
}
-static struct rga_corners_addr_offset
-rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset,
- unsigned int x, unsigned int y, unsigned int w, unsigned int h)
+static struct rga_corners_addrs
+rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs,
+ unsigned int x, unsigned int y, unsigned int w, unsigned int h)
{
- struct rga_corners_addr_offset offsets;
- struct rga_addr_offset *lt, *lb, *rt, *rb;
+ struct rga_corners_addrs corner_addrs;
+ struct rga_addrs *lt, *lb, *rt, *rb;
const struct v4l2_format_info *format_info;
unsigned int x_div = 0,
y_div = 0, uv_stride = 0, pixel_width = 0;
- lt = &offsets.left_top;
- lb = &offsets.left_bottom;
- rt = &offsets.right_top;
- rb = &offsets.right_bottom;
+ lt = &corner_addrs.left_top;
+ lb = &corner_addrs.left_bottom;
+ rt = &corner_addrs.right_top;
+ rb = &corner_addrs.right_bottom;
format_info = v4l2_format_info(frm->pix.pixelformat);
/* x_div is only used for the u/v planes.
@@ -64,29 +64,28 @@ rga_get_addr_offset(struct rga_frame *frm, struct rga_addr_offset *offset,
uv_stride = frm->stride / x_div;
pixel_width = frm->stride / frm->pix.width;
- lt->y_off = offset->y_off + y * frm->stride + x * pixel_width;
- lt->u_off = offset->u_off + (y / y_div) * uv_stride + x / x_div;
- lt->v_off = offset->v_off + (y / y_div) * uv_stride + x / x_div;
+ lt->y_addr = addrs->y_addr + y * frm->stride + x * pixel_width;
+ lt->u_addr = addrs->u_addr + (y / y_div) * uv_stride + x / x_div;
+ lt->v_addr = addrs->v_addr + (y / y_div) * uv_stride + x / x_div;
- lb->y_off = lt->y_off + (h - 1) * frm->stride;
- lb->u_off = lt->u_off + (h / y_div - 1) * uv_stride;
- lb->v_off = lt->v_off + (h / y_div - 1) * uv_stride;
+ lb->y_addr = lt->y_addr + (h - 1) * frm->stride;
+ lb->u_addr = lt->u_addr + (h / y_div - 1) * uv_stride;
+ lb->v_addr = lt->v_addr + (h / y_div - 1) * uv_stride;
- rt->y_off = lt->y_off + (w - 1) * pixel_width;
- rt->u_off = lt->u_off + w / x_div - 1;
- rt->v_off = lt->v_off + w / x_div - 1;
+ rt->y_addr = lt->y_addr + (w - 1) * pixel_width;
+ rt->u_addr = lt->u_addr + w / x_div - 1;
+ rt->v_addr = lt->v_addr + w / x_div - 1;
- rb->y_off = lb->y_off + (w - 1) * pixel_width;
- rb->u_off = lb->u_off + w / x_div - 1;
- rb->v_off = lb->v_off + w / x_div - 1;
+ rb->y_addr = lb->y_addr + (w - 1) * pixel_width;
+ rb->u_addr = lb->u_addr + w / x_div - 1;
+ rb->v_addr = lb->v_addr + w / x_div - 1;
- return offsets;
+ return corner_addrs;
}
-static struct rga_addr_offset *rga_lookup_draw_pos(struct
- rga_corners_addr_offset
- * offsets, u32 rotate_mode,
- u32 mirr_mode)
+static struct rga_addrs *rga_lookup_draw_pos(struct rga_corners_addrs *corner_addrs,
+ u32 rotate_mode,
+ u32 mirr_mode)
{
static enum e_rga_start_pos rot_mir_point_matrix[4][4] = {
{
@@ -103,18 +102,18 @@ static struct rga_addr_offset *rga_lookup_draw_pos(struct
},
};
- if (!offsets)
+ if (!corner_addrs)
return NULL;
switch (rot_mir_point_matrix[rotate_mode][mirr_mode]) {
case LT:
- return &offsets->left_top;
+ return &corner_addrs->left_top;
case LB:
- return &offsets->left_bottom;
+ return &corner_addrs->left_bottom;
case RT:
- return &offsets->right_top;
+ return &corner_addrs->right_top;
case RB:
- return &offsets->right_bottom;
+ return &corner_addrs->right_bottom;
}
return NULL;
@@ -316,9 +315,9 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
}
static void rga_cmd_set_src_info(struct rga_ctx *ctx,
- struct rga_addr_offset *offset)
+ struct rga_addrs *addrs)
{
- struct rga_corners_addr_offset src_offsets;
+ struct rga_corners_addrs src_corner_addrs;
u32 *dest = ctx->cmdbuf_virt;
unsigned int src_h, src_w, src_x, src_y;
@@ -330,22 +329,22 @@ static void rga_cmd_set_src_info(struct rga_ctx *ctx,
/*
* Calculate the source framebuffer base address with offset pixel.
*/
- src_offsets = rga_get_addr_offset(&ctx->in, offset,
- src_x, src_y, src_w, src_h);
+ src_corner_addrs = rga_get_corner_addrs(&ctx->in, addrs,
+ src_x, src_y, src_w, src_h);
dest[(RGA_SRC_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- src_offsets.left_top.y_off;
+ src_corner_addrs.left_top.y_addr;
dest[(RGA_SRC_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- src_offsets.left_top.u_off;
+ src_corner_addrs.left_top.u_addr;
dest[(RGA_SRC_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- src_offsets.left_top.v_off;
+ src_corner_addrs.left_top.v_addr;
}
static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
- struct rga_addr_offset *offset)
+ struct rga_addrs *addrs)
{
- struct rga_addr_offset *dst_offset;
- struct rga_corners_addr_offset offsets;
+ struct rga_addrs *dst_addrs;
+ struct rga_corners_addrs corner_addrs;
u32 *dest = ctx->cmdbuf_virt;
unsigned int dst_h, dst_w, dst_x, dst_y;
unsigned int mir_mode = 0;
@@ -379,15 +378,15 @@ static void rga_cmd_set_dst_info(struct rga_ctx *ctx,
/*
* Configure the dest framebuffer base address with pixel offset.
*/
- offsets = rga_get_addr_offset(&ctx->out, offset, dst_x, dst_y, dst_w, dst_h);
- dst_offset = rga_lookup_draw_pos(&offsets, rot_mode, mir_mode);
+ corner_addrs = rga_get_corner_addrs(&ctx->out, addrs, dst_x, dst_y, dst_w, dst_h);
+ dst_addrs = rga_lookup_draw_pos(&corner_addrs, rot_mode, mir_mode);
dest[(RGA_DST_Y_RGB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- dst_offset->y_off;
+ dst_addrs->y_addr;
dest[(RGA_DST_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- dst_offset->u_off;
+ dst_addrs->u_addr;
dest[(RGA_DST_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
- dst_offset->v_off;
+ dst_addrs->v_addr;
}
static void rga_cmd_set_mode(struct rga_ctx *ctx)
@@ -426,8 +425,8 @@ static void rga_cmd_set(struct rga_ctx *ctx,
rga_cmd_set_dst_addr(ctx, dst->dma_desc_pa);
- rga_cmd_set_src_info(ctx, &src->offset);
- rga_cmd_set_dst_info(ctx, &dst->offset);
+ rga_cmd_set_src_info(ctx, &src->dma_addrs);
+ rga_cmd_set_dst_info(ctx, &dst->dma_addrs);
rga_write(rga, RGA_CMD_BASE, ctx->cmdbuf_phy);
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index cee2e75ea89f1..bf21a57555a59 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -97,10 +97,10 @@ struct rockchip_rga {
const struct rga_hw *hw;
};
-struct rga_addr_offset {
- unsigned int y_off;
- unsigned int u_off;
- unsigned int v_off;
+struct rga_addrs {
+ dma_addr_t y_addr;
+ dma_addr_t u_addr;
+ dma_addr_t v_addr;
};
struct rga_vb_buffer {
@@ -112,8 +112,8 @@ struct rga_vb_buffer {
dma_addr_t dma_desc_pa;
size_t n_desc;
- /* Plane offsets of this buffer into the mapping */
- struct rga_addr_offset offset;
+ /* Plane DMA addresses after the MMU mapping of the buffer */
+ struct rga_addrs dma_addrs;
};
static inline struct rga_vb_buffer *vb_to_rga(struct vb2_v4l2_buffer *vb)
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 20/28] media: rockchip: rga: support external iommus
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (18 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 19/28] media: rockchip: rga: change offset to dma_addresses Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:08 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 21/28] media: rockchip: rga: share the interrupt when an external iommu is used Sven Püschel
` (7 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
In preparation for the RGA3 add support for external iommus. This is a
transition step to just disable the RGA2 specific mmu table setup code.
Currently a simple rga_hw struct field is used to set the internal iommu.
But to handle the case of more sophisticated detection mechanisms
(e.g. check for an iommu property in the device tree), it is abstracted
by an inline function.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- Also select VIDEOBUF2_DMA_CONTIG in Kconfig, Flagged by sashiko:
https://sashiko.dev/#/patchset/20260325-spu-rga3-v4-0-e90ec1c61354%40pengutronix.de?part=10
---
drivers/media/platform/rockchip/rga/Kconfig | 1 +
drivers/media/platform/rockchip/rga/rga-buf.c | 31 ++++++++++++++++++---------
drivers/media/platform/rockchip/rga/rga-hw.c | 1 +
drivers/media/platform/rockchip/rga/rga.c | 11 ++++++++--
drivers/media/platform/rockchip/rga/rga.h | 6 ++++++
5 files changed, 38 insertions(+), 12 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/Kconfig b/drivers/media/platform/rockchip/rga/Kconfig
index 727a0f6ea4664..846e555829f38 100644
--- a/drivers/media/platform/rockchip/rga/Kconfig
+++ b/drivers/media/platform/rockchip/rga/Kconfig
@@ -3,6 +3,7 @@ config VIDEO_ROCKCHIP_RGA
depends on V4L_MEM2MEM_DRIVERS
depends on VIDEO_DEV
depends on ARCH_ROCKCHIP || COMPILE_TEST
+ select VIDEOBUF2_DMA_CONTIG
select VIDEOBUF2_DMA_SG
select V4L2_MEM2MEM_DEV
help
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index ab9554c1c4cd9..cd6904d5fe5a6 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -12,6 +12,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-v4l2.h>
#include "rga.h"
@@ -82,6 +83,9 @@ static int rga_buf_init(struct vb2_buffer *vb)
if (IS_ERR(f))
return PTR_ERR(f);
+ if (!rga_has_internal_iommu(rga))
+ return 0;
+
n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE);
rbuf->n_desc = n_desc;
@@ -136,17 +140,21 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
for (i = 0; i < vb->num_planes; i++) {
vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage);
- /* Create local MMU table for RGA */
- n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc],
- rbuf->n_desc - curr_desc,
- vb2_dma_sg_plane_desc(vb, i));
- if (n_desc < 0) {
- v4l2_err(&ctx->rga->v4l2_dev,
- "Failed to map video buffer to RGA\n");
- return n_desc;
+ if (rga_has_internal_iommu(ctx->rga)) {
+ /* Create local MMU table for RGA */
+ n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc],
+ rbuf->n_desc - curr_desc,
+ vb2_dma_sg_plane_desc(vb, i));
+ if (n_desc < 0) {
+ v4l2_err(&ctx->rga->v4l2_dev,
+ "Failed to map video buffer to RGA\n");
+ return n_desc;
+ }
+ dma_addrs[i] = curr_desc << PAGE_SHIFT;
+ curr_desc += n_desc;
+ } else {
+ dma_addrs[i] = vb2_dma_contig_plane_dma_addr(vb, i);
}
- dma_addrs[i] = curr_desc << PAGE_SHIFT;
- curr_desc += n_desc;
}
/* Fill the remaining planes */
@@ -176,6 +184,9 @@ static void rga_buf_cleanup(struct vb2_buffer *vb)
struct rga_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
struct rockchip_rga *rga = ctx->rga;
+ if (!rga_has_internal_iommu(rga))
+ return;
+
dma_free_coherent(rga->dev, rbuf->n_desc * sizeof(*rbuf->dma_desc),
rbuf->dma_desc, rbuf->dma_desc_pa);
}
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 99cf57d5ba89d..73584706a47e2 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -577,6 +577,7 @@ static struct rga_fmt formats[] = {
const struct rga_hw rga2_hw = {
.card_type = "rga2",
+ .has_internal_iommu = true,
.formats = formats,
.num_formats = ARRAY_SIZE(formats),
.cmdbuf_size = RGA_CMDBUF_SIZE,
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 91775b43ff617..e3c99c3f7c5be 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -23,6 +23,7 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-mem2mem.h>
#include <media/videobuf2-dma-sg.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/videobuf2-v4l2.h>
#include "rga.h"
@@ -98,7 +99,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
src_vq->io_modes = VB2_MMAP | VB2_DMABUF;
src_vq->drv_priv = ctx;
src_vq->ops = &rga_qops;
- src_vq->mem_ops = &vb2_dma_sg_memops;
+ if (rga_has_internal_iommu(ctx->rga))
+ src_vq->mem_ops = &vb2_dma_sg_memops;
+ else
+ src_vq->mem_ops = &vb2_dma_contig_memops;
src_vq->gfp_flags = __GFP_DMA32;
src_vq->buf_struct_size = sizeof(struct rga_vb_buffer);
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
@@ -113,7 +117,10 @@ queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
dst_vq->io_modes = VB2_MMAP | VB2_DMABUF;
dst_vq->drv_priv = ctx;
dst_vq->ops = &rga_qops;
- dst_vq->mem_ops = &vb2_dma_sg_memops;
+ if (rga_has_internal_iommu(ctx->rga))
+ dst_vq->mem_ops = &vb2_dma_sg_memops;
+ else
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
dst_vq->gfp_flags = __GFP_DMA32;
dst_vq->buf_struct_size = sizeof(struct rga_vb_buffer);
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index bf21a57555a59..b180df5c48370 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -150,6 +150,7 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask)
struct rga_hw {
const char *card_type;
+ bool has_internal_iommu;
struct rga_fmt *formats;
u32 num_formats;
size_t cmdbuf_size;
@@ -165,6 +166,11 @@ struct rga_hw {
void (*get_version)(struct rockchip_rga *rga);
};
+static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga)
+{
+ return rga->hw->has_internal_iommu;
+}
+
extern const struct rga_hw rga2_hw;
#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 21/28] media: rockchip: rga: share the interrupt when an external iommu is used
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (19 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 20/28] media: rockchip: rga: support external iommus Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:11 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 22/28] media: rockchip: rga: remove size from rga_frame Sven Püschel
` (6 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Michael Olbrich, Nicolas Dufresne
From: Michael Olbrich <m.olbrich@pengutronix.de>
The RGA3 and the corresponding iommu share the interrupt. So in that
case, request a shared interrupt so that the iommu driver can request
it as well.
Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index e3c99c3f7c5be..cda3cecb1ce8c 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -767,7 +767,8 @@ static int rga_probe(struct platform_device *pdev)
goto err_put_clk;
}
- ret = devm_request_irq(rga->dev, irq, rga_isr, 0,
+ ret = devm_request_irq(rga->dev, irq, rga_isr,
+ rga_has_internal_iommu(rga) ? 0 : IRQF_SHARED,
dev_name(rga->dev), rga);
if (ret < 0) {
dev_err(rga->dev, "failed to request irq\n");
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 22/28] media: rockchip: rga: remove size from rga_frame
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (20 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 21/28] media: rockchip: rga: share the interrupt when an external iommu is used Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:21 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 23/28] media: rockchip: rga: remove stride " Sven Püschel
` (5 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
The size member is only used for the mmu page table mapping.
Therefore avoid storing the value and instead only calculate it
in place. This also avoids the calculation entirely when an external
iommu is used.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga-buf.c | 6 +++++-
drivers/media/platform/rockchip/rga/rga.c | 8 ++------
drivers/media/platform/rockchip/rga/rga.h | 1 -
3 files changed, 7 insertions(+), 8 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index cd6904d5fe5a6..092c2bdf6b672 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -79,6 +79,8 @@ static int rga_buf_init(struct vb2_buffer *vb)
struct rockchip_rga *rga = ctx->rga;
struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type);
size_t n_desc = 0;
+ u32 size = 0;
+ u8 i;
if (IS_ERR(f))
return PTR_ERR(f);
@@ -86,7 +88,9 @@ static int rga_buf_init(struct vb2_buffer *vb)
if (!rga_has_internal_iommu(rga))
return 0;
- n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE);
+ for (i = 0; i < f->pix.num_planes; i++)
+ size += f->pix.plane_fmt[i].sizeimage;
+ n_desc = DIV_ROUND_UP(size, PAGE_SIZE);
rbuf->n_desc = n_desc;
rbuf->dma_desc = dma_alloc_coherent(rga->dev,
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index cda3cecb1ce8c..e44e81814b7cb 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -268,7 +268,6 @@ static int rga_open(struct file *file)
};
def_frame.stride = (def_width * def_frame.fmt->depth) >> 3;
- def_frame.size = def_frame.stride * def_height;
ctx = kzalloc_obj(*ctx);
if (!ctx)
@@ -481,9 +480,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
frm = rga_get_frame(ctx, f->type);
if (IS_ERR(frm))
return PTR_ERR(frm);
- frm->size = 0;
- for (i = 0; i < pix_fmt->num_planes; i++)
- frm->size += pix_fmt->plane_fmt[i].sizeimage;
frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat);
frm->stride = pix_fmt->plane_fmt[0].bytesperline;
@@ -508,10 +504,10 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
ctx->cmdbuf_dirty = true;
v4l2_dbg(debug, 1, &rga->v4l2_dev,
- "[%s] fmt - %p4cc %dx%d (stride %d, sizeimage %d)\n",
+ "[%s] fmt - %p4cc %dx%d (stride %d)\n",
V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE",
&frm->fmt->fourcc, pix_fmt->width, pix_fmt->height,
- frm->stride, frm->size);
+ frm->stride);
for (i = 0; i < pix_fmt->num_planes; i++) {
v4l2_dbg(debug, 1, &rga->v4l2_dev,
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index b180df5c48370..d47c6821ce2c9 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -34,7 +34,6 @@ struct rga_frame {
/* Variables that can calculated once and reused */
u32 stride;
- u32 size;
};
struct rga_dma_desc {
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 23/28] media: rockchip: rga: remove stride from rga_frame
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (21 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 22/28] media: rockchip: rga: remove size from rga_frame Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 24/28] media: rockchip: rga: move rga_fmt to rga-hw.h Sven Püschel
` (4 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Remove the stride variable from rga_frame. Despite the comment it
didn't involve any calculation and is just a copy of the
plane_fmt[0].bytesperline value. Therefore avoid this struct member
and use the bytesperline value directly in the places where it is
required.
Also drop the dependency on the depth format member, which was only
used to calculate the stride of the default format. This is already done
by the v4l2_fill_pixfmt_mp_aligned helper and used as stride in try_fmt.
Therefore using it's value also for the default format stride is just
more consistent.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga-hw.c | 36 ++++++++++------------------
drivers/media/platform/rockchip/rga/rga.c | 5 +---
drivers/media/platform/rockchip/rga/rga.h | 4 ----
3 files changed, 13 insertions(+), 32 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 73584706a47e2..fac3975e2d0cd 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -44,7 +44,7 @@ rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs,
struct rga_addrs *lt, *lb, *rt, *rb;
const struct v4l2_format_info *format_info;
unsigned int x_div = 0,
- y_div = 0, uv_stride = 0, pixel_width = 0;
+ y_div = 0, y_stride = 0, uv_stride = 0, pixel_width = 0;
lt = &corner_addrs.left_top;
lb = &corner_addrs.left_bottom;
@@ -61,14 +61,15 @@ rga_get_corner_addrs(struct rga_frame *frm, struct rga_addrs *addrs,
else
x_div = 1;
y_div = format_info->vdiv;
- uv_stride = frm->stride / x_div;
- pixel_width = frm->stride / frm->pix.width;
+ y_stride = frm->pix.plane_fmt[0].bytesperline;
+ uv_stride = y_stride / x_div;
+ pixel_width = y_stride / frm->pix.width;
- lt->y_addr = addrs->y_addr + y * frm->stride + x * pixel_width;
+ lt->y_addr = addrs->y_addr + y * y_stride + x * pixel_width;
lt->u_addr = addrs->u_addr + (y / y_div) * uv_stride + x / x_div;
lt->v_addr = addrs->v_addr + (y / y_div) * uv_stride + x / x_div;
- lb->y_addr = lt->y_addr + (h - 1) * frm->stride;
+ lb->y_addr = lt->y_addr + (h - 1) * y_stride;
lb->u_addr = lt->u_addr + (h / y_div - 1) * uv_stride;
lb->v_addr = lt->v_addr + (h / y_div - 1) * uv_stride;
@@ -169,6 +170,7 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
union rga_src_act_info src_act_info;
union rga_dst_vir_info dst_vir_info;
union rga_dst_act_info dst_act_info;
+ u32 in_stride, out_stride;
src_h = ctx->in.crop.height;
src_w = ctx->in.crop.width;
@@ -291,13 +293,15 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
* Calculate the framebuffer virtual strides and active size,
* note that the step of vir_stride / vir_width is 4 byte words
*/
- src_vir_info.data.vir_stride = ctx->in.stride >> 2;
- src_vir_info.data.vir_width = ctx->in.stride >> 2;
+ in_stride = ctx->in.pix.plane_fmt[0].bytesperline;
+ src_vir_info.data.vir_stride = in_stride >> 2;
+ src_vir_info.data.vir_width = in_stride >> 2;
src_act_info.data.act_height = src_h - 1;
src_act_info.data.act_width = src_w - 1;
- dst_vir_info.data.vir_stride = ctx->out.stride >> 2;
+ out_stride = ctx->out.pix.plane_fmt[0].bytesperline;
+ dst_vir_info.data.vir_stride = out_stride >> 2;
dst_act_info.data.act_height = dst_h - 1;
dst_act_info.data.act_width = dst_w - 1;
@@ -481,97 +485,81 @@ static struct rga_fmt formats[] = {
.fourcc = V4L2_PIX_FMT_ARGB32,
.color_swap = RGA_COLOR_ALPHA_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR8888,
- .depth = 32,
},
{
.fourcc = V4L2_PIX_FMT_ABGR32,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR8888,
- .depth = 32,
},
{
.fourcc = V4L2_PIX_FMT_XBGR32,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_XBGR8888,
- .depth = 32,
},
{
.fourcc = V4L2_PIX_FMT_RGB24,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_RGB888,
- .depth = 24,
},
{
.fourcc = V4L2_PIX_FMT_BGR24,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_RGB888,
- .depth = 24,
},
{
.fourcc = V4L2_PIX_FMT_ARGB444,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR4444,
- .depth = 16,
},
{
.fourcc = V4L2_PIX_FMT_ARGB555,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_ABGR1555,
- .depth = 16,
},
{
.fourcc = V4L2_PIX_FMT_RGB565,
.color_swap = RGA_COLOR_RB_SWAP,
.hw_format = RGA_COLOR_FMT_BGR565,
- .depth = 16,
},
{
.fourcc = V4L2_PIX_FMT_NV21,
.color_swap = RGA_COLOR_UV_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420SP,
- .depth = 12,
},
{
.fourcc = V4L2_PIX_FMT_NV61,
.color_swap = RGA_COLOR_UV_SWAP,
.hw_format = RGA_COLOR_FMT_YUV422SP,
- .depth = 16,
},
{
.fourcc = V4L2_PIX_FMT_NV12,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420SP,
- .depth = 12,
},
{
.fourcc = V4L2_PIX_FMT_NV12M,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420SP,
- .depth = 12,
},
{
.fourcc = V4L2_PIX_FMT_NV16,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV422SP,
- .depth = 16,
},
{
.fourcc = V4L2_PIX_FMT_YUV420,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420P,
- .depth = 12,
},
{
.fourcc = V4L2_PIX_FMT_YUV422P,
.color_swap = RGA_COLOR_NONE_SWAP,
.hw_format = RGA_COLOR_FMT_YUV422P,
- .depth = 16,
},
{
.fourcc = V4L2_PIX_FMT_YVU420,
.color_swap = RGA_COLOR_UV_SWAP,
.hw_format = RGA_COLOR_FMT_YUV420P,
- .depth = 12,
},
};
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index e44e81814b7cb..e52d0577b236c 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -267,8 +267,6 @@ static int rga_open(struct file *file)
.fmt = &rga->hw->formats[0],
};
- def_frame.stride = (def_width * def_frame.fmt->depth) >> 3;
-
ctx = kzalloc_obj(*ctx);
if (!ctx)
return -ENOMEM;
@@ -481,7 +479,6 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
if (IS_ERR(frm))
return PTR_ERR(frm);
frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat);
- frm->stride = pix_fmt->plane_fmt[0].bytesperline;
/*
* Copy colorimetry from output to capture as required by the
@@ -507,7 +504,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
"[%s] fmt - %p4cc %dx%d (stride %d)\n",
V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE",
&frm->fmt->fourcc, pix_fmt->width, pix_fmt->height,
- frm->stride);
+ pix_fmt->plane_fmt[0].bytesperline);
for (i = 0; i < pix_fmt->num_planes; i++) {
v4l2_dbg(debug, 1, &rga->v4l2_dev,
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index d47c6821ce2c9..227ab6c0532c2 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -19,7 +19,6 @@
struct rga_fmt {
u32 fourcc;
- int depth;
u8 color_swap;
u8 hw_format;
};
@@ -31,9 +30,6 @@ struct rga_frame {
/* Image format */
struct rga_fmt *fmt;
struct v4l2_pix_format_mplane pix;
-
- /* Variables that can calculated once and reused */
- u32 stride;
};
struct rga_dma_desc {
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 24/28] media: rockchip: rga: move rga_fmt to rga-hw.h
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (22 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 23/28] media: rockchip: rga: remove stride " Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 25/28] media: rockchip: rga: add feature flags Sven Püschel
` (3 subsequent siblings)
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Move rga_fmt to rga-hw in preparation of the RGA3 addition, as the struct
contains many RGA2 specific values. They are used to write the correct
register values quickly based on the chosen format. Therefore the
pointer to the rga_fmt struct is kept but changed to an opaque void
pointer outside of the rga-hw.h.
To enumerate and set the correct formats, two helper functions need to
be exposed in the rga_hw struct:
enum_format just get's the vidioc_enum_fmt format and it's return value
is also returned from vidioc_enum_fmt. This is a simple pass-through,
as the implementation is very simple.
adjust_and_map_format is a simple abstraction around the previous
rga_find_format. But unlike rga_find_format, it always returns a valid
format. Therefore the passed format value is also a pointer to update
it in case the values are not supported by the hardware.
Due to the RGA3 supporting different formats on the capture and output
side, an additional parameter is_capture has been added to support
this use-case. The additional ctx parameter is also added to allow the
RGA3 to limit the colorimetry only if an RGB<->YCrCb transformation
happens.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- adjusted v4l2_fill_pixfmt_mp_aligned argument, which previously was
incorrectly done when the function was first introduced
---
drivers/media/platform/rockchip/rga/rga-buf.c | 2 +-
drivers/media/platform/rockchip/rga/rga-hw.c | 49 +++++++++++++++++++++------
drivers/media/platform/rockchip/rga/rga-hw.h | 8 +++++
drivers/media/platform/rockchip/rga/rga.c | 41 ++++++++--------------
drivers/media/platform/rockchip/rga/rga.h | 14 +++-----
5 files changed, 67 insertions(+), 47 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
index 092c2bdf6b672..c0ea6003336bf 100644
--- a/drivers/media/platform/rockchip/rga/rga-buf.c
+++ b/drivers/media/platform/rockchip/rga/rga-buf.c
@@ -162,7 +162,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
}
/* Fill the remaining planes */
- info = v4l2_format_info(f->fmt->fourcc);
+ info = v4l2_format_info(f->pix.pixelformat);
for (i = info->mem_planes; i < info->comp_planes; i++)
dma_addrs[i] = dma_addrs[0] + get_plane_offset(f, info, i);
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index fac3975e2d0cd..616ea1bc11311 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -171,6 +171,8 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
union rga_dst_vir_info dst_vir_info;
union rga_dst_act_info dst_act_info;
u32 in_stride, out_stride;
+ struct rga_fmt *in_fmt = ctx->in.fmt;
+ struct rga_fmt *out_fmt = ctx->out.fmt;
src_h = ctx->in.crop.height;
src_w = ctx->in.crop.width;
@@ -186,18 +188,18 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
dst_vir_info.val = dest[(RGA_DST_VIR_INFO - RGA_MODE_BASE_REG) >> 2];
dst_act_info.val = dest[(RGA_DST_ACT_INFO - RGA_MODE_BASE_REG) >> 2];
- src_info.data.format = ctx->in.fmt->hw_format;
- src_info.data.swap = ctx->in.fmt->color_swap;
- dst_info.data.format = ctx->out.fmt->hw_format;
- dst_info.data.swap = ctx->out.fmt->color_swap;
+ src_info.data.format = in_fmt->hw_format;
+ src_info.data.swap = in_fmt->color_swap;
+ dst_info.data.format = out_fmt->hw_format;
+ dst_info.data.swap = out_fmt->color_swap;
/*
* CSC mode must only be set when the colorspace families differ between
* input and output. It must remain unset (zeroed) if both are the same.
*/
- if (RGA_COLOR_FMT_IS_YUV(ctx->in.fmt->hw_format) &&
- RGA_COLOR_FMT_IS_RGB(ctx->out.fmt->hw_format)) {
+ if (RGA_COLOR_FMT_IS_YUV(in_fmt->hw_format) &&
+ RGA_COLOR_FMT_IS_RGB(out_fmt->hw_format)) {
switch (ctx->in.pix.colorspace) {
case V4L2_COLORSPACE_REC709:
src_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0;
@@ -208,8 +210,8 @@ static void rga_cmd_set_trans_info(struct rga_ctx *ctx)
}
}
- if (RGA_COLOR_FMT_IS_RGB(ctx->in.fmt->hw_format) &&
- RGA_COLOR_FMT_IS_YUV(ctx->out.fmt->hw_format)) {
+ if (RGA_COLOR_FMT_IS_RGB(in_fmt->hw_format) &&
+ RGA_COLOR_FMT_IS_YUV(out_fmt->hw_format)) {
switch (ctx->out.pix.colorspace) {
case V4L2_COLORSPACE_REC709:
dst_info.data.csc_mode = RGA_SRC_CSC_MODE_BT709_R0;
@@ -563,11 +565,36 @@ static struct rga_fmt formats[] = {
},
};
+static void *rga_adjust_and_map_format(struct rga_ctx *ctx,
+ struct v4l2_pix_format_mplane *format,
+ bool is_output)
+{
+ unsigned int i;
+
+ if (!format)
+ return &formats[0];
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++) {
+ if (formats[i].fourcc == format->pixelformat)
+ return &formats[i];
+ }
+
+ format->pixelformat = formats[0].fourcc;
+ return &formats[0];
+}
+
+static int rga_enum_format(struct v4l2_fmtdesc *f)
+{
+ if (f->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ f->pixelformat = formats[f->index].fourcc;
+ return 0;
+}
+
const struct rga_hw rga2_hw = {
.card_type = "rga2",
.has_internal_iommu = true,
- .formats = formats,
- .num_formats = ARRAY_SIZE(formats),
.cmdbuf_size = RGA_CMDBUF_SIZE,
.min_width = MIN_WIDTH,
.max_width = MAX_WIDTH,
@@ -580,4 +607,6 @@ const struct rga_hw rga2_hw = {
.start = rga_hw_start,
.handle_irq = rga_handle_irq,
.get_version = rga_get_version,
+ .adjust_and_map_format = rga_adjust_and_map_format,
+ .enum_format = rga_enum_format,
};
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.h b/drivers/media/platform/rockchip/rga/rga-hw.h
index 805ec23e5e3f4..14ffa5ebd453b 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.h
+++ b/drivers/media/platform/rockchip/rga/rga-hw.h
@@ -6,6 +6,8 @@
#ifndef __RGA_HW_H__
#define __RGA_HW_H__
+#include <linux/types.h>
+
#define RGA_CMDBUF_SIZE 0x80
/* Hardware limits */
@@ -431,4 +433,10 @@ union rga_pat_con {
} data;
};
+struct rga_fmt {
+ u32 fourcc;
+ u8 color_swap;
+ u8 hw_format;
+};
+
#endif
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index e52d0577b236c..1878b4e26360b 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -232,17 +232,6 @@ int rga_check_scaling(const struct rga_hw *hw, const struct v4l2_rect *crop_in,
return 0;
}
-static struct rga_fmt *rga_fmt_find(struct rockchip_rga *rga, u32 pixelformat)
-{
- unsigned int i;
-
- for (i = 0; i < rga->hw->num_formats; i++) {
- if (rga->hw->formats[i].fourcc == pixelformat)
- return &rga->hw->formats[i];
- }
- return NULL;
-}
-
struct rga_frame *rga_get_frame(struct rga_ctx *ctx, enum v4l2_buf_type type)
{
if (V4L2_TYPE_IS_OUTPUT(type))
@@ -264,7 +253,6 @@ static int rga_open(struct file *file)
.crop.top = 0,
.crop.width = def_width,
.crop.height = def_height,
- .fmt = &rga->hw->formats[0],
};
ctx = kzalloc_obj(*ctx);
@@ -286,9 +274,12 @@ static int rga_open(struct file *file)
ctx->in = def_frame;
ctx->out = def_frame;
- v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.fmt->fourcc,
+ ctx->in.fmt = rga->hw->adjust_and_map_format(ctx, &ctx->in.pix, true);
+ v4l2_fill_pixfmt_mp_aligned(&ctx->in.pix, ctx->in.pix.pixelformat,
def_width, def_height, rga->hw->stride_alignment);
- v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.fmt->fourcc,
+ ctx->out.fmt =
+ rga->hw->adjust_and_map_format(ctx, &ctx->out.pix, false);
+ v4l2_fill_pixfmt_mp_aligned(&ctx->out.pix, ctx->out.pix.pixelformat,
def_width, def_height, rga->hw->stride_alignment);
if (mutex_lock_interruptible(&rga->mutex)) {
@@ -370,13 +361,11 @@ vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f)
{
struct rockchip_rga *rga = video_drvdata(file);
- struct rga_fmt *fmt;
-
- if (f->index >= rga->hw->num_formats)
- return -EINVAL;
+ int ret;
- fmt = &rga->hw->formats[f->index];
- f->pixelformat = fmt->fourcc;
+ ret = rga->hw->enum_format(f);
+ if (ret != 0)
+ return ret;
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
@@ -411,7 +400,6 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp;
struct rga_ctx *ctx = file_to_rga_ctx(file);
const struct rga_hw *hw = ctx->rga->hw;
- struct rga_fmt *fmt;
struct v4l2_frmsize_stepwise frmsize = {
.min_width = hw->min_width,
.max_width = hw->max_width,
@@ -442,12 +430,10 @@ static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
pix_fmt->xfer_func = frm->pix.xfer_func;
}
- fmt = rga_fmt_find(ctx->rga, pix_fmt->pixelformat);
- if (!fmt)
- fmt = &hw->formats[0];
+ hw->adjust_and_map_format(ctx, pix_fmt, V4L2_TYPE_IS_OUTPUT(f->type));
v4l2_apply_frmsize_constraints(&pix_fmt->width, &pix_fmt->height, &frmsize);
- v4l2_fill_pixfmt_mp_aligned(pix_fmt, fmt->fourcc,
+ v4l2_fill_pixfmt_mp_aligned(pix_fmt, pix_fmt->pixelformat,
pix_fmt->width, pix_fmt->height, hw->stride_alignment);
pix_fmt->field = V4L2_FIELD_NONE;
@@ -478,7 +464,8 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
frm = rga_get_frame(ctx, f->type);
if (IS_ERR(frm))
return PTR_ERR(frm);
- frm->fmt = rga_fmt_find(rga, pix_fmt->pixelformat);
+ frm->fmt = rga->hw->adjust_and_map_format(ctx, pix_fmt,
+ V4L2_TYPE_IS_OUTPUT(f->type));
/*
* Copy colorimetry from output to capture as required by the
@@ -503,7 +490,7 @@ static int vidioc_s_fmt(struct file *file, void *priv, struct v4l2_format *f)
v4l2_dbg(debug, 1, &rga->v4l2_dev,
"[%s] fmt - %p4cc %dx%d (stride %d)\n",
V4L2_TYPE_IS_OUTPUT(f->type) ? "OUTPUT" : "CAPTURE",
- &frm->fmt->fourcc, pix_fmt->width, pix_fmt->height,
+ &pix_fmt->pixelformat, pix_fmt->width, pix_fmt->height,
pix_fmt->plane_fmt[0].bytesperline);
for (i = 0; i < pix_fmt->num_planes; i++) {
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index 227ab6c0532c2..effe364a86b02 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -17,18 +17,12 @@
#define DEFAULT_WIDTH 100
#define DEFAULT_HEIGHT 100
-struct rga_fmt {
- u32 fourcc;
- u8 color_swap;
- u8 hw_format;
-};
-
struct rga_frame {
/* Crop */
struct v4l2_rect crop;
/* Image format */
- struct rga_fmt *fmt;
+ void *fmt;
struct v4l2_pix_format_mplane pix;
};
@@ -146,8 +140,6 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask)
struct rga_hw {
const char *card_type;
bool has_internal_iommu;
- struct rga_fmt *formats;
- u32 num_formats;
size_t cmdbuf_size;
u32 min_width, min_height;
u32 max_width, max_height;
@@ -159,6 +151,10 @@ struct rga_hw {
struct rga_vb_buffer *src, struct rga_vb_buffer *dst);
bool (*handle_irq)(struct rockchip_rga *rga);
void (*get_version)(struct rockchip_rga *rga);
+ void *(*adjust_and_map_format)(struct rga_ctx *ctx,
+ struct v4l2_pix_format_mplane *format,
+ bool is_output);
+ int (*enum_format)(struct v4l2_fmtdesc *f);
};
static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga)
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 25/28] media: rockchip: rga: add feature flags
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (23 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 24/28] media: rockchip: rga: move rga_fmt to rga-hw.h Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:22 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 26/28] media: rockchip: rga: disable multi-core support Sven Püschel
` (2 subsequent siblings)
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
In preparation to the RGA3 addition add feature flags, which can limit
the exposed feature set of the video device, like rotating or selection
support. This is necessary as the RGA3 doesn't initially implement the
full feature set currently exposed by the driver.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- Dropped selection feature, as the RGA3 also has basic support for the
seleciton API. Flagged by Sashiko
https://sashiko.dev/#/patchset/20260428-spu-rga3-v5-0-eb7f5d019d86%40pengutronix.de?part=28
---
drivers/media/platform/rockchip/rga/rga-hw.c | 3 +++
drivers/media/platform/rockchip/rga/rga.c | 20 ++++++++++++--------
drivers/media/platform/rockchip/rga/rga.h | 6 ++++++
3 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
index 616ea1bc11311..be1bc8ddbd03b 100644
--- a/drivers/media/platform/rockchip/rga/rga-hw.c
+++ b/drivers/media/platform/rockchip/rga/rga-hw.c
@@ -602,6 +602,9 @@ const struct rga_hw rga2_hw = {
.max_height = MAX_HEIGHT,
.max_scaling_factor = MAX_SCALING_FACTOR,
.stride_alignment = 4,
+ .features = RGA_FEATURE_FLIP
+ | RGA_FEATURE_ROTATE
+ | RGA_FEATURE_BG_COLOR,
.setup_cmdbuf = rga_hw_setup_cmdbuf,
.start = rga_hw_start,
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 1878b4e26360b..8d60e94da32d8 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -177,17 +177,21 @@ static int rga_setup_ctrls(struct rga_ctx *ctx)
v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
- V4L2_CID_HFLIP, 0, 1, 1, 0);
+ if (rga->hw->features & RGA_FEATURE_FLIP) {
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
- V4L2_CID_VFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ }
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
- V4L2_CID_ROTATE, 0, 270, 90, 0);
+ if (rga->hw->features & RGA_FEATURE_ROTATE)
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_ROTATE, 0, 270, 90, 0);
- v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
- V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0);
+ if (rga->hw->features & RGA_FEATURE_BG_COLOR)
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
+ V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0);
if (ctx->ctrl_handler.error) {
int err = ctx->ctrl_handler.error;
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index effe364a86b02..feaf40acd4ee4 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -6,6 +6,7 @@
#ifndef __RGA_H__
#define __RGA_H__
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <media/videobuf2-v4l2.h>
@@ -137,6 +138,10 @@ static inline void rga_mod(struct rockchip_rga *rga, u32 reg, u32 val, u32 mask)
rga_write(rga, reg, temp);
};
+#define RGA_FEATURE_FLIP BIT(0)
+#define RGA_FEATURE_ROTATE BIT(1)
+#define RGA_FEATURE_BG_COLOR BIT(2)
+
struct rga_hw {
const char *card_type;
bool has_internal_iommu;
@@ -145,6 +150,7 @@ struct rga_hw {
u32 max_width, max_height;
u8 max_scaling_factor;
u8 stride_alignment;
+ u8 features;
void (*setup_cmdbuf)(struct rga_ctx *ctx);
void (*start)(struct rockchip_rga *rga,
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 26/28] media: rockchip: rga: disable multi-core support
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (24 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 25/28] media: rockchip: rga: add feature flags Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 15:32 ` [PATCH v6 27/28] media: rockchip: rga: add rga3 support Sven Püschel
2026-05-15 15:32 ` [PATCH v6 28/28] arm64: dts: rockchip: add rga3 dt nodes Sven Püschel
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Disable multi-core support in preparation of the RGA3 addition. The
RK3588 SoC features two equal RGA3 cores. This allows scheduling of the
work between both cores, which is not yet implemented. Until it is
implemented avoid exposing both cores as independent video devices to
prevent an ABI breakage when multi-core support is added.
This patch is copied from the Hantro driver patch to disable multi core
support by Sebastian Reichel. See
commit ccdeb8d57f7f ("media: hantro: Disable multicore support")
Link: https://lore.kernel.org/all/20240618183816.77597-4-sebastian.reichel@collabora.com/
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
drivers/media/platform/rockchip/rga/rga.c | 47 +++++++++++++++++++++++++++++++
1 file changed, 47 insertions(+)
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 8d60e94da32d8..0152b8ef2da2f 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -711,6 +711,49 @@ static int rga_parse_dt(struct rockchip_rga *rga)
return 0;
}
+/*
+ * Some SoCs, like RK3588 have multiple identical RGA3 cores, but the
+ * kernel is currently missing support for multi-core handling. Exposing
+ * separate devices for each core to userspace is bad, since that does
+ * not allow scheduling tasks properly (and creates ABI). With this workaround
+ * the driver will only probe for the first core and early exit for the other
+ * cores. Once the driver gains multi-core support, the same technique
+ * for detecting the main core can be used to cluster all cores together.
+ */
+static int rga_disable_multicore(struct device *dev)
+{
+ struct device_node *node = NULL;
+ const char *compatible;
+ bool is_main_core;
+ int ret;
+
+ /* Intentionally ignores the fallback strings */
+ ret = of_property_read_string(dev->of_node, "compatible", &compatible);
+ if (ret)
+ return ret;
+
+ /* The first compatible and available node found is considered the main core */
+ do {
+ node = of_find_compatible_node(node, NULL, compatible);
+ if (of_device_is_available(node))
+ break;
+ } while (node);
+
+ if (!node)
+ return -EINVAL;
+
+ is_main_core = (dev->of_node == node);
+
+ of_node_put(node);
+
+ if (!is_main_core) {
+ dev_info(dev, "missing multi-core support, ignoring this instance\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
static int rga_probe(struct platform_device *pdev)
{
struct rockchip_rga *rga;
@@ -721,6 +764,10 @@ static int rga_probe(struct platform_device *pdev)
if (!pdev->dev.of_node)
return -ENODEV;
+ ret = rga_disable_multicore(&pdev->dev);
+ if (ret)
+ return ret;
+
rga = devm_kzalloc(&pdev->dev, sizeof(*rga), GFP_KERNEL);
if (!rga)
return -ENOMEM;
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 27/28] media: rockchip: rga: add rga3 support
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (25 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 26/28] media: rockchip: rga: disable multi-core support Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
2026-05-15 16:34 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 28/28] arm64: dts: rockchip: add rga3 dt nodes Sven Püschel
27 siblings, 1 reply; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel, Nicolas Dufresne
Add support for the RGA3 unit contained in the RK3588.
Only a basic feature set consisting of scaling and color conversion is
implemented. Currently unimplemented features include:
- Advanced formats like 10bit YUV, FBCE mode and Tile8x8 mode
- Background color (V4L2_CID_BG_COLOR)
- Configurable alpha value (V4L2_CID_ALPHA_COMPONENT)
- Image flipping (V4L2_CID_HFLIP and V4L2_CID_VFLIP)
- Image rotation (V4L2_CID_ROTATE)
- Image cropping/composing (VIDIOC_S_SELECTION)
- Only very basic output cropping for 1088 -> 1080 cases is implemented
The register address defines were copied from the
vendor Rockchip kernel sources and slightly adjusted to not start at 0
again for the cmd registers.
During testing it has been noted that the scaling of the hardware is
slightly incorrect. A test conversion of 128x128 RGBA to 256x256 RGBA
causes a slightly larger scaling. The scaling is suddle, as it seems
that the image is scaled to a 2px larger version and then cropped to
it's final size. Trying to use the RGA2 scaling factor calculation
didn't work. As the calculation matches the vendor kernel driver, no
further research has been utilized to check if there may be some kind of
better scaling factor calculation.
Furthermore comparing the RGA3 conversion with the GStreamer
videoconvertscale element, the chroma-site is different. A quick testing
didn't reveal a chroma-site that creates the same image with the
GStreamer Element. Also when converting from YUV to RGB the RGB values
differ by 1 or 2. This doesn't seem to be a colorspace conversion issue
but rather a slightly different precision on the calculation.
Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- Fixed other_format variable in rga3_adjust_and_map_format being
assigned the wrong way around (ctx->out is capture, not output)
Flagged by Sashiko:
https://sashiko.dev/#/patchset/20260428-spu-rga3-v5-0-eb7f5d019d86%40pengutronix.de?part=28
Changes in v5:
- Fixed cmdbuf size to not be a quarter of the actual size
---
drivers/media/platform/rockchip/rga/Makefile | 2 +-
drivers/media/platform/rockchip/rga/rga.c | 4 +
drivers/media/platform/rockchip/rga/rga.h | 1 +
drivers/media/platform/rockchip/rga/rga3-hw.c | 507 ++++++++++++++++++++++++++
drivers/media/platform/rockchip/rga/rga3-hw.h | 192 ++++++++++
5 files changed, 705 insertions(+), 1 deletion(-)
diff --git a/drivers/media/platform/rockchip/rga/Makefile b/drivers/media/platform/rockchip/rga/Makefile
index 1bbecdc3d8df2..7326a548f3dc7 100644
--- a/drivers/media/platform/rockchip/rga/Makefile
+++ b/drivers/media/platform/rockchip/rga/Makefile
@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-rockchip-rga-objs := rga.o rga-hw.o rga-buf.o
+rockchip-rga-objs := rga.o rga-hw.o rga3-hw.o rga-buf.o
obj-$(CONFIG_VIDEO_ROCKCHIP_RGA) += rockchip-rga.o
diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
index 0152b8ef2da2f..b3cb6bf8eb863 100644
--- a/drivers/media/platform/rockchip/rga/rga.c
+++ b/drivers/media/platform/rockchip/rga/rga.c
@@ -913,6 +913,10 @@ static const struct of_device_id rockchip_rga_match[] = {
.compatible = "rockchip,rk3399-rga",
.data = &rga2_hw,
},
+ {
+ .compatible = "rockchip,rk3588-rga3",
+ .data = &rga3_hw,
+ },
{},
};
diff --git a/drivers/media/platform/rockchip/rga/rga.h b/drivers/media/platform/rockchip/rga/rga.h
index feaf40acd4ee4..bd431534d0d39 100644
--- a/drivers/media/platform/rockchip/rga/rga.h
+++ b/drivers/media/platform/rockchip/rga/rga.h
@@ -169,5 +169,6 @@ static inline bool rga_has_internal_iommu(const struct rockchip_rga *rga)
}
extern const struct rga_hw rga2_hw;
+extern const struct rga_hw rga3_hw;
#endif
diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.c b/drivers/media/platform/rockchip/rga/rga3-hw.c
new file mode 100644
index 0000000000000..ca1c268303dd4
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/rga3-hw.c
@@ -0,0 +1,507 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025-2026 Pengutronix e.K.
+ * Author: Sven Püschel <s.pueschel@pengutronix.de>
+ */
+
+#include <linux/pm_runtime.h>
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/printk.h>
+
+#include <media/v4l2-common.h>
+
+#include "rga3-hw.h"
+#include "rga.h"
+
+static unsigned int rga3_get_scaling(unsigned int src, unsigned int dst)
+{
+ /*
+ * RGA3 scaling factor calculation as described in chapter 5.4.7 Resize
+ * of the TRM Part 2. The resulting scaling factor is a 16-bit value
+ * and therefore normalized with 2^16.
+ *
+ * While the TRM also mentions (dst-1)/(src-1) for the up-scaling case,
+ * it didn't work as the value always exceeds 16 bit. Flipping the
+ * factors results in a correct up-scaling. This is possible as the
+ * RGA3 has the RGA3_WIN_SCALE_XXX_UP bit to determine if it does
+ * an up or downscale.
+ *
+ * The scaling factor can potentially cause a slightly larger scaling
+ * (e.g. 1/2px larger scale and then cropped to the destination size).
+ * This can be seen when scaling 128x128px RGBA to 256x256px RGBA.
+ * The RGA2 scaling factor calculation (without the various +/-1
+ * doesn't work for the RGA3. It's assumed that this is an hardware
+ * accuracy limitation, as the vendor kernel driver uses the same
+ * scaling factor calculation.
+ *
+ * With a scaling factor of 1.0 the calculation technically also
+ * overflows 16 bit. This isn't relevant, as in this case the
+ * RGA3_WIN_SCALE_XXX_BYPASS bit completely skips the scaling operation.
+ */
+ if (dst > src) {
+ if (((src - 1) << 16) % (dst - 1) == 0)
+ return ((src - 1) << 16) / (dst - 1) - 1;
+ else
+ return ((src - 1) << 16) / (dst - 1);
+ } else {
+ return ((dst - 1) << 16) / (src - 1) + 1;
+ }
+}
+
+/*
+ * Check if the given format can be captured, as the RGA3 doesn't support all
+ * input formats also on it's output.
+ */
+static bool rga3_can_capture(const struct rga3_fmt *fmt)
+{
+ return fmt->hw_format <= RGA3_COLOR_FMT_LAST_OUTPUT;
+}
+
+/*
+ * Map the transformations to the RGA3 command buffer.
+ * Currently this is just the scaling settings and a fixed alpha value.
+ */
+static void rga3_cmd_set_trans_info(struct rga_ctx *ctx)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ unsigned int src_h, src_w, dst_h, dst_w;
+ unsigned int reg;
+ u16 hor_scl_fac, ver_scl_fac;
+ const struct rga3_fmt *in = ctx->in.fmt;
+
+ /* Support basic input cropping to support 1088px inputs */
+ src_h = ctx->in.crop.height;
+ src_w = ctx->in.crop.width;
+ dst_h = ctx->out.pix.height;
+ dst_w = ctx->out.pix.width;
+
+ reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_SCALE_HOR_UP, dst_w > src_w)
+ | FIELD_PREP(RGA3_WIN_SCALE_HOR_BYPASS, dst_w == src_w)
+ | FIELD_PREP(RGA3_WIN_SCALE_VER_UP, dst_h > src_h)
+ | FIELD_PREP(RGA3_WIN_SCALE_VER_BYPASS, dst_h == src_h);
+
+ hor_scl_fac = rga3_get_scaling(src_w, dst_w);
+ ver_scl_fac = rga3_get_scaling(src_h, dst_h);
+ reg = RGA3_WIN0_SCL_FAC - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_SCALE_HOR_FAC, hor_scl_fac)
+ | FIELD_PREP(RGA3_SCALE_VER_FAC, ver_scl_fac);
+
+ if (v4l2_format_info(in->fourcc)->has_alpha) {
+ /* copy alpha from input */
+ reg = RGA3_OVLP_TOP_ALPHA - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1)
+ | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1);
+ reg = RGA3_OVLP_BOT_ALPHA - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_ALPHA_SELECT_MODE, 1)
+ | FIELD_PREP(RGA3_ALPHA_BLEND_MODE, 1);
+ } else {
+ /* just use a 255 alpha value */
+ reg = RGA3_OVLP_TOP_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff)
+ | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1);
+ reg = RGA3_OVLP_BOT_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_OVLP_GLOBAL_ALPHA, 0xff)
+ | FIELD_PREP(RGA3_OVLP_COLOR_MODE, 1);
+ }
+}
+
+static void rga3_cmd_set_win0_addr(struct rga_ctx *ctx,
+ const struct rga_addrs *addrs)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ unsigned int reg;
+
+ reg = RGA3_WIN0_Y_BASE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = addrs->y_addr;
+ reg = RGA3_WIN0_U_BASE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = addrs->u_addr;
+}
+
+static void rga3_cmd_set_wr_addr(struct rga_ctx *ctx,
+ const struct rga_addrs *addrs)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ unsigned int reg;
+
+ reg = RGA3_WR_Y_BASE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = addrs->y_addr;
+ reg = RGA3_WR_U_BASE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = addrs->u_addr;
+}
+
+/* Map the input pixel format to win0 of the comamnd buffer. */
+static void rga3_cmd_set_win0_format(struct rga_ctx *ctx)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ const struct rga3_fmt *in = ctx->in.fmt;
+ const struct rga3_fmt *out = ctx->out.fmt;
+ const struct v4l2_format_info *in_fmt, *out_fmt;
+ unsigned int act_h, act_w, src_h, src_w;
+ bool r2y, y2r;
+ u8 rd_format;
+ const struct v4l2_pix_format_mplane *csc_pix;
+ u8 csc_mode;
+ unsigned int reg;
+
+ act_h = ctx->in.pix.height;
+ act_w = ctx->in.pix.width;
+ /* Support basic input cropping to support 1088px inputs */
+ src_h = ctx->in.crop.height;
+ src_w = ctx->in.crop.width;
+
+ in_fmt = v4l2_format_info(in->fourcc);
+ out_fmt = v4l2_format_info(out->fourcc);
+ r2y = v4l2_is_format_rgb(in_fmt) && v4l2_is_format_yuv(out_fmt);
+ y2r = v4l2_is_format_yuv(in_fmt) && v4l2_is_format_rgb(out_fmt);
+
+ /* The Hardware only supports formats with 1/2 planes */
+ if (in_fmt->comp_planes == 2)
+ rd_format = RGA3_RDWR_FORMAT_SEMI_PLANAR;
+ else
+ rd_format = RGA3_RDWR_FORMAT_INTERLEAVED;
+
+ /* set pixel format and CSC */
+ csc_pix = r2y ? &ctx->out.pix : &ctx->in.pix;
+ switch (csc_pix->ycbcr_enc) {
+ case V4L2_YCBCR_ENC_BT2020:
+ csc_mode = RGA3_WIN_CSC_MODE_BT2020_L;
+ break;
+ case V4L2_YCBCR_ENC_709:
+ csc_mode = RGA3_WIN_CSC_MODE_BT709_L;
+ break;
+ default: /* should be fixed to BT601 in adjust_and_map_format */
+ if (csc_pix->quantization == V4L2_QUANTIZATION_LIM_RANGE)
+ csc_mode = RGA3_WIN_CSC_MODE_BT601_L;
+ else
+ csc_mode = RGA3_WIN_CSC_MODE_BT601_F;
+ break;
+ }
+
+ reg = RGA3_WIN0_RD_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] |= FIELD_PREP(RGA3_WIN_ENABLE, 1)
+ | FIELD_PREP(RGA3_WIN_PIC_FORMAT, in->hw_format)
+ | FIELD_PREP(RGA3_WIN_YC_SWAP, in->yc_swap)
+ | FIELD_PREP(RGA3_WIN_RBUV_SWAP, in->rbuv_swap)
+ | FIELD_PREP(RGA3_WIN_RD_FORMAT, rd_format)
+ | FIELD_PREP(RGA3_WIN_R2Y, r2y)
+ | FIELD_PREP(RGA3_WIN_Y2R, y2r)
+ | FIELD_PREP(RGA3_WIN_CSC_MODE, csc_mode);
+
+ /* set stride */
+ reg = RGA3_WIN0_VIR_STRIDE - RGA3_FIRST_CMD_REG;
+ /* stride needs to be in words */
+ cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2;
+ reg = RGA3_WIN0_UV_VIR_STRIDE - RGA3_FIRST_CMD_REG;
+ /* The Hardware only supports formats with 1/2 planes */
+ if (ctx->in.pix.num_planes == 2)
+ cmd[reg >> 2] = ctx->in.pix.plane_fmt[1].bytesperline >> 2;
+ else
+ cmd[reg >> 2] = ctx->in.pix.plane_fmt[0].bytesperline >> 2;
+
+ /* set size */
+ reg = RGA3_WIN0_ACT_SIZE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, act_w)
+ | FIELD_PREP(RGA3_HEIGHT, act_h);
+ reg = RGA3_WIN0_SRC_SIZE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, src_w)
+ | FIELD_PREP(RGA3_HEIGHT, src_h);
+}
+
+/* Map the output pixel format to the command buffer */
+static void rga3_cmd_set_wr_format(struct rga_ctx *ctx)
+{
+ u32 *cmd = ctx->cmdbuf_virt;
+ const struct rga3_fmt *out = ctx->out.fmt;
+ const struct v4l2_format_info *out_fmt;
+ unsigned int dst_h, dst_w;
+ u8 wr_format;
+ unsigned int reg;
+
+ dst_h = ctx->out.pix.height;
+ dst_w = ctx->out.pix.width;
+
+ out_fmt = v4l2_format_info(out->fourcc);
+
+ /* The Hardware only supports formats with 1/2 planes */
+ if (out_fmt->comp_planes == 2)
+ wr_format = RGA3_RDWR_FORMAT_SEMI_PLANAR;
+ else
+ wr_format = RGA3_RDWR_FORMAT_INTERLEAVED;
+
+ /* set pixel format */
+ reg = RGA3_WR_CTRL - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_WR_PIC_FORMAT, out->hw_format)
+ | FIELD_PREP(RGA3_WR_YC_SWAP, out->yc_swap)
+ | FIELD_PREP(RGA3_WR_RBUV_SWAP, out->rbuv_swap)
+ | FIELD_PREP(RGA3_WR_FORMAT, wr_format)
+ /* Use the max value to avoid limiting the write speed */
+ | FIELD_PREP(RGA3_WR_SW_OUTSTANDING_MAX, 63);
+
+ /* set stride */
+ reg = RGA3_WR_VIR_STRIDE - RGA3_FIRST_CMD_REG;
+ /* stride needs to be in words */
+ cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2;
+ reg = RGA3_WR_PL_VIR_STRIDE - RGA3_FIRST_CMD_REG;
+ /* The Hardware only supports formats with 1/2 planes */
+ if (ctx->out.pix.num_planes == 2)
+ cmd[reg >> 2] = ctx->out.pix.plane_fmt[1].bytesperline >> 2;
+ else
+ cmd[reg >> 2] = ctx->out.pix.plane_fmt[0].bytesperline >> 2;
+
+ /* Set size.
+ * As two inputs are not supported, we don't use win1.
+ * Therefore only set the size for win0.
+ */
+ reg = RGA3_WIN0_DST_SIZE - RGA3_FIRST_CMD_REG;
+ cmd[reg >> 2] = FIELD_PREP(RGA3_WIDTH, dst_w)
+ | FIELD_PREP(RGA3_HEIGHT, dst_h);
+}
+
+static void rga3_hw_setup_cmdbuf(struct rga_ctx *ctx)
+{
+ memset(ctx->cmdbuf_virt, 0, RGA3_CMDBUF_SIZE);
+
+ rga3_cmd_set_win0_format(ctx);
+ rga3_cmd_set_trans_info(ctx);
+ rga3_cmd_set_wr_format(ctx);
+}
+
+static void rga3_hw_start(struct rockchip_rga *rga,
+ struct rga_vb_buffer *src, struct rga_vb_buffer *dst)
+{
+ struct rga_ctx *ctx = rga->curr;
+
+ rga3_cmd_set_win0_addr(ctx, &src->dma_addrs);
+ rga3_cmd_set_wr_addr(ctx, &dst->dma_addrs);
+
+ rga_write(rga, RGA3_CMD_ADDR, ctx->cmdbuf_phy);
+
+ /* sync CMD buf for RGA */
+ dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy,
+ PAGE_SIZE, DMA_BIDIRECTIONAL);
+
+ /* set to master mode and start the conversion */
+ rga_write(rga, RGA3_SYS_CTRL,
+ FIELD_PREP(RGA3_CMD_MODE, RGA3_CMD_MODE_MASTER));
+ rga_write(rga, RGA3_INT_EN, FIELD_PREP(RGA3_INT_FRM_DONE, 1));
+ rga_write(rga, RGA3_CMD_CTRL,
+ FIELD_PREP(RGA3_CMD_LINE_START_PULSE, 1));
+}
+
+static bool rga3_handle_irq(struct rockchip_rga *rga)
+{
+ u32 intr;
+
+ intr = rga_read(rga, RGA3_INT_RAW);
+ /* clear all interrupts */
+ rga_write(rga, RGA3_INT_CLR, intr);
+
+ return FIELD_GET(RGA3_INT_FRM_DONE, intr);
+}
+
+static void rga3_get_version(struct rockchip_rga *rga)
+{
+ u32 version = rga_read(rga, RGA3_VERSION_NUM);
+
+ rga->version.major = FIELD_GET(RGA3_VERSION_NUM_MAJOR, version);
+ rga->version.minor = FIELD_GET(RGA3_VERSION_NUM_MINOR, version);
+}
+
+static struct rga3_fmt rga3_formats[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_RGB24,
+ .hw_format = RGA3_COLOR_FMT_BGR888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .hw_format = RGA3_COLOR_FMT_BGR888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_ABGR32,
+ .hw_format = RGA3_COLOR_FMT_BGRA8888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGBA32,
+ .hw_format = RGA3_COLOR_FMT_BGRA8888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XBGR32,
+ .hw_format = RGA3_COLOR_FMT_BGRA8888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGBX32,
+ .hw_format = RGA3_COLOR_FMT_BGRA8888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .hw_format = RGA3_COLOR_FMT_BGR565,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12M,
+ .hw_format = RGA3_COLOR_FMT_YUV420,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .hw_format = RGA3_COLOR_FMT_YUV420,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21M,
+ .hw_format = RGA3_COLOR_FMT_YUV420,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .hw_format = RGA3_COLOR_FMT_YUV420,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16M,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61M,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .yc_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .yc_swap = 1,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .hw_format = RGA3_COLOR_FMT_YUV422,
+ .rbuv_swap = 1,
+ },
+ /* Input only formats last to keep rga3_enum_format simple */
+ {
+ .fourcc = V4L2_PIX_FMT_ARGB32,
+ .hw_format = RGA3_COLOR_FMT_ABGR8888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGRA32,
+ .hw_format = RGA3_COLOR_FMT_ABGR8888,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_XRGB32,
+ .hw_format = RGA3_COLOR_FMT_ABGR8888,
+ .rbuv_swap = 1,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_BGRX32,
+ .hw_format = RGA3_COLOR_FMT_ABGR8888,
+ },
+};
+
+static int rga3_enum_format(struct v4l2_fmtdesc *f)
+{
+ struct rga3_fmt *fmt;
+
+ if (f->index >= ARRAY_SIZE(rga3_formats))
+ return -EINVAL;
+
+ fmt = &rga3_formats[f->index];
+ if (V4L2_TYPE_IS_CAPTURE(f->type) && !rga3_can_capture(fmt))
+ return -EINVAL;
+
+ f->pixelformat = fmt->fourcc;
+ return 0;
+}
+
+static void *rga3_adjust_and_map_format(struct rga_ctx *ctx,
+ struct v4l2_pix_format_mplane *format,
+ bool is_output)
+{
+ unsigned int i;
+ const struct v4l2_format_info *format_info;
+ const struct v4l2_pix_format_mplane *other_format;
+ const struct v4l2_format_info *other_format_info;
+
+ if (!format)
+ return &rga3_formats[0];
+
+ format_info = v4l2_format_info(format->pixelformat);
+ other_format = is_output ? &ctx->out.pix : &ctx->in.pix;
+ other_format_info = v4l2_format_info(other_format->pixelformat);
+
+ if ((v4l2_is_format_rgb(format_info) &&
+ v4l2_is_format_yuv(other_format_info)) ||
+ (v4l2_is_format_yuv(format_info) &&
+ v4l2_is_format_rgb(other_format_info))) {
+ /*
+ * The RGA3 only supports BT601, BT709 and BT2020 RGB<->YUV conversions
+ * Additionally BT709 and BT2020 only support limited range YUV.
+ */
+ switch (format->ycbcr_enc) {
+ case V4L2_YCBCR_ENC_601:
+ /* supports full and limited range */
+ break;
+ case V4L2_YCBCR_ENC_709:
+ case V4L2_YCBCR_ENC_BT2020:
+ format->quantization = V4L2_QUANTIZATION_LIM_RANGE;
+ break;
+ default:
+ format->ycbcr_enc = V4L2_YCBCR_ENC_601;
+ format->quantization = V4L2_QUANTIZATION_FULL_RANGE;
+ break;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(rga3_formats); i++) {
+ if (!is_output && !rga3_can_capture(&rga3_formats[i]))
+ continue;
+
+ if (rga3_formats[i].fourcc == format->pixelformat)
+ return &rga3_formats[i];
+ }
+
+ format->pixelformat = rga3_formats[0].fourcc;
+ return &rga3_formats[0];
+}
+
+const struct rga_hw rga3_hw = {
+ .card_type = "rga3",
+ .has_internal_iommu = false,
+ .cmdbuf_size = RGA3_CMDBUF_SIZE,
+ .min_width = RGA3_MIN_WIDTH,
+ .min_height = RGA3_MIN_HEIGHT,
+ /* use output size, as it's a bit smaller than the input size */
+ .max_width = RGA3_MAX_OUTPUT_WIDTH,
+ .max_height = RGA3_MAX_OUTPUT_HEIGHT,
+ .max_scaling_factor = RGA3_MAX_SCALING_FACTOR,
+ .stride_alignment = 16,
+ .features = 0,
+
+ .setup_cmdbuf = rga3_hw_setup_cmdbuf,
+ .start = rga3_hw_start,
+ .handle_irq = rga3_handle_irq,
+ .get_version = rga3_get_version,
+ .enum_format = rga3_enum_format,
+ .adjust_and_map_format = rga3_adjust_and_map_format,
+};
diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.h b/drivers/media/platform/rockchip/rga/rga3-hw.h
new file mode 100644
index 0000000000000..85fd8ae257ecd
--- /dev/null
+++ b/drivers/media/platform/rockchip/rga/rga3-hw.h
@@ -0,0 +1,192 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) Pengutronix e.K.
+ * Author: Sven Püschel <s.pueschel@pengutronix.de>
+ */
+#ifndef __RGA3_HW_H__
+#define __RGA3_HW_H__
+
+#include <linux/bits.h>
+#include <linux/types.h>
+
+#define RGA3_CMDBUF_SIZE 0xb8
+
+#define RGA3_MIN_WIDTH 128
+#define RGA3_MIN_HEIGHT 128
+#define RGA3_MAX_INPUT_WIDTH (8192 - 16)
+#define RGA3_MAX_INPUT_HEIGHT (8192 - 16)
+#define RGA3_MAX_OUTPUT_WIDTH (8192 - 64)
+#define RGA3_MAX_OUTPUT_HEIGHT (8192 - 64)
+#define RGA3_MAX_SCALING_FACTOR 8
+#define RGA3_RESET_TIMEOUT 1000
+
+/* Registers address */
+/* sys reg */
+#define RGA3_SYS_CTRL 0x000
+#define RGA3_CMD_CTRL 0x004
+#define RGA3_CMD_ADDR 0x008
+#define RGA3_MI_GROUP_CTRL 0x00c
+#define RGA3_ARQOS_CTRL 0x010
+#define RGA3_VERSION_NUM 0x018
+#define RGA3_VERSION_TIM 0x01c
+#define RGA3_INT_EN 0x020
+#define RGA3_INT_RAW 0x024
+#define RGA3_INT_MSK 0x028
+#define RGA3_INT_CLR 0x02c
+#define RGA3_RO_SRST 0x030
+#define RGA3_STATUS0 0x034
+#define RGA3_SCAN_CNT 0x038
+#define RGA3_CMD_STATE 0x040
+
+/* cmd reg */
+#define RGA3_WIN0_RD_CTRL 0x100
+#define RGA3_FIRST_CMD_REG RGA3_WIN0_RD_CTRL
+#define RGA3_WIN0_Y_BASE 0x110
+#define RGA3_WIN0_U_BASE 0x114
+#define RGA3_WIN0_V_BASE 0x118
+#define RGA3_WIN0_VIR_STRIDE 0x11c
+#define RGA3_WIN0_FBC_OFF 0x120
+#define RGA3_WIN0_SRC_SIZE 0x124
+#define RGA3_WIN0_ACT_OFF 0x128
+#define RGA3_WIN0_ACT_SIZE 0x12c
+#define RGA3_WIN0_DST_SIZE 0x130
+#define RGA3_WIN0_SCL_FAC 0x134
+#define RGA3_WIN0_UV_VIR_STRIDE 0x138
+#define RGA3_WIN1_RD_CTRL 0x140
+#define RGA3_WIN1_Y_BASE 0x150
+#define RGA3_WIN1_U_BASE 0x154
+#define RGA3_WIN1_V_BASE 0x158
+#define RGA3_WIN1_VIR_STRIDE 0x15c
+#define RGA3_WIN1_FBC_OFF 0x160
+#define RGA3_WIN1_SRC_SIZE 0x164
+#define RGA3_WIN1_ACT_OFF 0x168
+#define RGA3_WIN1_ACT_SIZE 0x16c
+#define RGA3_WIN1_DST_SIZE 0x170
+#define RGA3_WIN1_SCL_FAC 0x174
+#define RGA3_WIN1_UV_VIR_STRIDE 0x178
+#define RGA3_OVLP_CTRL 0x180
+#define RGA3_OVLP_OFF 0x184
+#define RGA3_OVLP_TOP_KEY_MIN 0x188
+#define RGA3_OVLP_TOP_KEY_MAX 0x18c
+#define RGA3_OVLP_TOP_CTRL 0x190
+#define RGA3_OVLP_BOT_CTRL 0x194
+#define RGA3_OVLP_TOP_ALPHA 0x198
+#define RGA3_OVLP_BOT_ALPHA 0x19c
+#define RGA3_WR_CTRL 0x1a0
+#define RGA3_WR_FBCE_CTRL 0x1a4
+#define RGA3_WR_VIR_STRIDE 0x1a8
+#define RGA3_WR_PL_VIR_STRIDE 0x1ac
+#define RGA3_WR_Y_BASE 0x1b0
+#define RGA3_WR_U_BASE 0x1b4
+#define RGA3_WR_V_BASE 0x1b8
+
+/* Registers value */
+#define RGA3_COLOR_FMT_YUV420 0x0
+#define RGA3_COLOR_FMT_YUV422 0x1
+#define RGA3_COLOR_FMT_YUV420_10B 0x2
+#define RGA3_COLOR_FMT_YUV422_10B 0x3
+/*
+ * Use memory ordering names
+ * instead of the datasheet naming RGB formats in big endian order
+ */
+#define RGA3_COLOR_FMT_BGR565 0x4
+#define RGA3_COLOR_FMT_BGR888 0x5
+#define RGA3_COLOR_FMT_FIRST_HAS_ALPHA RGA3_COLOR_FMT_BGRA8888
+#define RGA3_COLOR_FMT_BGRA8888 0x6
+#define RGA3_COLOR_FMT_LAST_OUTPUT RGA3_COLOR_FMT_BGRA8888
+/* the following are only supported as inputs */
+#define RGA3_COLOR_FMT_ABGR8888 0x7
+/*
+ * the following seem to be unnecessary,
+ * as they can be achieved with RB swaps
+ */
+#define RGA3_COLOR_FMT_RGBA8888 0x8
+#define RGA3_COLOR_FMT_ARGB8888 0x9
+
+#define RGA3_RDWR_FORMAT_SEMI_PLANAR 0x1
+#define RGA3_RDWR_FORMAT_INTERLEAVED 0x2
+
+#define RGA3_CMD_MODE_MASTER 0x1
+
+#define RGA3_WIN_CSC_MODE_BT601_L 0x0
+#define RGA3_WIN_CSC_MODE_BT709_L 0x1
+#define RGA3_WIN_CSC_MODE_BT601_F 0x2
+#define RGA3_WIN_CSC_MODE_BT2020_L 0x3
+
+/* RGA masks */
+/* SYS_CTRL */
+#define RGA3_CCLK_SRESET BIT(4)
+#define RGA3_ACLK_SRESET BIT(3)
+#define RGA3_CMD_MODE BIT(1)
+
+/* CMD_CTRL */
+#define RGA3_CMD_LINE_START_PULSE BIT(0)
+
+/* VERSION_NUM */
+#define RGA3_VERSION_NUM_MAJOR GENMASK(31, 28)
+#define RGA3_VERSION_NUM_MINOR GENMASK(27, 20)
+
+/* INT_* */
+#define RGA3_INT_FRM_DONE BIT(0)
+#define RGA3_INT_DMA_READ_BUS_ERR BIT(2)
+#define RGA3_INT_WIN0_FBC_DEC_ERR BIT(5)
+#define RGA3_INT_WIN0_HOR_ERR BIT(6)
+#define RGA3_INT_WIN0_VER_ERR BIT(7)
+#define RGA3_INT_WR_VER_ERR BIT(13)
+#define RGA3_INT_WR_HOR_ERR BIT(14)
+#define RGA3_INT_WR_BUS_ERR BIT(15)
+#define RGA3_INT_WIN0_IN_FIFO_WR_ERR BIT(16)
+#define RGA3_INT_WIN0_IN_FIFO_RD_ERR BIT(17)
+#define RGA3_INT_WIN0_HOR_FIFO_WR_ERR BIT(18)
+#define RGA3_INT_WIN0_HOR_FIFO_RD_ERR BIT(19)
+#define RGA3_INT_WIN0_VER_FIFO_WR_ERR BIT(20)
+#define RGA3_INT_WIN0_VER_FIFO_RD_ERR BIT(21)
+
+/* RO_SRST */
+#define RGA3_RO_SRST_DONE GENMASK(5, 0)
+
+/* *_SIZE */
+#define RGA3_HEIGHT GENMASK(28, 16)
+#define RGA3_WIDTH GENMASK(12, 0)
+
+/* SCL_FAC */
+#define RGA3_SCALE_VER_FAC GENMASK(31, 16)
+#define RGA3_SCALE_HOR_FAC GENMASK(15, 0)
+
+/* WINx_CTRL */
+#define RGA3_WIN_CSC_MODE GENMASK(27, 26)
+#define RGA3_WIN_R2Y BIT(25)
+#define RGA3_WIN_Y2R BIT(24)
+#define RGA3_WIN_SCALE_VER_UP BIT(23)
+#define RGA3_WIN_SCALE_VER_BYPASS BIT(22)
+#define RGA3_WIN_SCALE_HOR_UP BIT(21)
+#define RGA3_WIN_SCALE_HOR_BYPASS BIT(20)
+#define RGA3_WIN_YC_SWAP BIT(13)
+#define RGA3_WIN_RBUV_SWAP BIT(12)
+#define RGA3_WIN_RD_FORMAT GENMASK(9, 8)
+#define RGA3_WIN_PIC_FORMAT GENMASK(7, 4)
+#define RGA3_WIN_ENABLE BIT(0)
+
+/* COLOR_CTRL */
+#define RGA3_OVLP_GLOBAL_ALPHA GENMASK(23, 16)
+#define RGA3_OVLP_COLOR_MODE BIT(0)
+
+/* ALPHA_CTRL */
+#define RGA3_ALPHA_SELECT_MODE BIT(4)
+#define RGA3_ALPHA_BLEND_MODE GENMASK(3, 2)
+
+/* WR_CTRL */
+#define RGA3_WR_YC_SWAP BIT(20)
+#define RGA3_WR_SW_OUTSTANDING_MAX GENMASK(18, 13)
+#define RGA3_WR_RBUV_SWAP BIT(12)
+#define RGA3_WR_FORMAT GENMASK(9, 8)
+#define RGA3_WR_PIC_FORMAT GENMASK(7, 4)
+
+struct rga3_fmt {
+ u32 fourcc;
+ u8 hw_format;
+ bool rbuv_swap;
+ bool yc_swap;
+};
+
+#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* [PATCH v6 28/28] arm64: dts: rockchip: add rga3 dt nodes
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
` (26 preceding siblings ...)
2026-05-15 15:32 ` [PATCH v6 27/28] media: rockchip: rga: add rga3 support Sven Püschel
@ 2026-05-15 15:32 ` Sven Püschel
27 siblings, 0 replies; 44+ messages in thread
From: Sven Püschel @ 2026-05-15 15:32 UTC (permalink / raw)
To: Jacob Chen, Ezequiel Garcia, Mauro Carvalho Chehab,
Heiko Stuebner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Hans Verkuil
Cc: linux-media, linux-rockchip, linux-arm-kernel, linux-kernel,
devicetree, kernel, nicolas, sebastian.reichel, m.tretter,
p.zabel, Sven Püschel
Add devicetree nodes for the RGA3 (Raster Graphics Acceleration 3)
peripheral in the RK3588.
The existing rga node refers to the RGA2-Enhanced peripheral. The RK3588
contains one RGA2-Enhanced core and two RGA3 cores. Both feature a similar
functionality of scaling, cropping and rotating of up to two input
images into one output image. Key differences of the RGA3 are:
- supports 10bit YUV output formats
- supports 8x8 tiles and FBCD as inputs and outputs
- supports BT2020 color space conversion
- max output resolution of (8192-64)x(8192-64)
- MMU can map up to 32G DDR RAM
- fully planar formats (3 planes) are not supported
- max scale up/down factor of 8 (RGA2 allows up to 16)
Signed-off-by: Sven Püschel <s.pueschel@pengutronix.de>
---
Changes in v6:
- reordered before the rga node to fix the sorting order.
Flagged by sashiko.dev:
https://sashiko.dev/#/patchset/20260325-spu-rga3-v4-0-e90ec1c61354%40pengutronix.de?part=27
---
arch/arm64/boot/dts/rockchip/rk3588-base.dtsi | 44 +++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
index 4fb8888c281c8..a4f44af512375 100644
--- a/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
+++ b/arch/arm64/boot/dts/rockchip/rk3588-base.dtsi
@@ -1262,6 +1262,50 @@ vpu121_mmu: iommu@fdb50800 {
#iommu-cells = <0>;
};
+ rga3_core0: rga@fdb60000 {
+ compatible = "rockchip,rk3588-rga3";
+ reg = <0x0 0xfdb60000 0x0 0x200>;
+ interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&cru ACLK_RGA3_0>, <&cru HCLK_RGA3_0>, <&cru CLK_RGA3_0_CORE>;
+ clock-names = "aclk", "hclk", "sclk";
+ resets = <&cru SRST_RGA3_0_CORE>, <&cru SRST_A_RGA3_0>, <&cru SRST_H_RGA3_0>;
+ reset-names = "core", "axi", "ahb";
+ power-domains = <&power RK3588_PD_RGA30>;
+ iommus = <&rga3_0_mmu>;
+ };
+
+ rga3_0_mmu: iommu@fdb60f00 {
+ compatible = "rockchip,rk3588-iommu", "rockchip,rk3568-iommu";
+ reg = <0x0 0xfdb60f00 0x0 0x100>;
+ interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&cru ACLK_RGA3_0>, <&cru HCLK_RGA3_0>;
+ clock-names = "aclk", "iface";
+ #iommu-cells = <0>;
+ power-domains = <&power RK3588_PD_RGA30>;
+ };
+
+ rga3_core1: rga@fdb70000 {
+ compatible = "rockchip,rk3588-rga3";
+ reg = <0x0 0xfdb70000 0x0 0x200>;
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&cru ACLK_RGA3_1>, <&cru HCLK_RGA3_1>, <&cru CLK_RGA3_1_CORE>;
+ clock-names = "aclk", "hclk", "sclk";
+ resets = <&cru SRST_RGA3_1_CORE>, <&cru SRST_A_RGA3_1>, <&cru SRST_H_RGA3_1>;
+ reset-names = "core", "axi", "ahb";
+ power-domains = <&power RK3588_PD_RGA31>;
+ iommus = <&rga3_1_mmu>;
+ };
+
+ rga3_1_mmu: iommu@fdb70f00 {
+ compatible = "rockchip,rk3588-iommu", "rockchip,rk3568-iommu";
+ reg = <0x0 0xfdb70f00 0x0 0x100>;
+ interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH 0>;
+ clocks = <&cru ACLK_RGA3_1>, <&cru HCLK_RGA3_1>;
+ clock-names = "aclk", "iface";
+ #iommu-cells = <0>;
+ power-domains = <&power RK3588_PD_RGA31>;
+ };
+
rga: rga@fdb80000 {
compatible = "rockchip,rk3588-rga", "rockchip,rk3288-rga";
reg = <0x0 0xfdb80000 0x0 0x180>;
--
2.54.0
^ permalink raw reply related [flat|nested] 44+ messages in thread
* Re: [PATCH v6 07/28] media: rockchip: rga: use clk_bulk api
2026-05-15 15:32 ` [PATCH v6 07/28] media: rockchip: rga: use clk_bulk api Sven Püschel
@ 2026-05-15 15:54 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 15:54 UTC (permalink / raw)
To: Sven Püschel; +Cc: robh, conor+dt, devicetree, krzk+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Switch to `devm_clk_bulk_get_all()` silently drops validation for mandatory clocks and alters the hardware initialization sequence.
--
commit 234eed9f39e78b285ced7d7570cee6af431aad44
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: use clk_bulk api
This commit replaces individual clock retrieval and enablement calls with
the clk_bulk API to reduce code duplication.
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
[ ... ]
> @@ -771,23 +733,12 @@ static int rga_parse_dt(struct rockchip_rga *rga)
> udelay(1);
> reset_control_deassert(ahb_rst);
>
> - rga->sclk = devm_clk_get(rga->dev, "sclk");
[ ... ]
> - rga->hclk = devm_clk_get(rga->dev, "hclk");
> - if (IS_ERR(rga->hclk)) {
> - dev_err(rga->dev, "failed to get hclk clock\n");
> - return PTR_ERR(rga->hclk);
> + ret = devm_clk_bulk_get_all(rga->dev, &rga->clks);
> + if (ret < 0) {
> + dev_err(rga->dev, "failed to get clocks\n");
> + return ret;
> }
> + rga->num_clks = ret;
Does this code introduce a regression when the device tree is missing
the clocks property?
Looking at devm_clk_bulk_get_all(), it returns the number of clocks found,
which is 0 if the clocks property is missing. Because the check is only for
ret < 0, a malformed device tree could cause the probe to succeed without
the required clocks. Accessing the hardware without all three clocks running
might result in an asynchronous external abort.
Also, does this change the required clock enablement sequence?
The previous code explicitly prepared and enabled the clocks in a fixed
order (sclk, aclk, hclk) and disabled them in a specific sequence. By using
devm_clk_bulk_get_all(), the order now depends strictly on the device tree
array index. If the hardware requires a specific sequence, this might cause
initialization issues.
Could devm_clk_bulk_get() with a predefined clk_bulk_data array be used
instead to explicitly define the required clock IDs? This would preserve both
validation and the enablement sequence.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=7
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 05/28] media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper
2026-05-15 15:32 ` [PATCH v6 05/28] media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper Sven Püschel
@ 2026-05-15 15:58 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 15:58 UTC (permalink / raw)
To: Sven Püschel; +Cc: krzk+dt, conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] The `sizeimage` calculation under-allocates memory for single-memory-plane formats where the chroma stride is larger than the luma stride (e.g., NV24, NV42), leading to a heap buffer overflow.
- [Medium] The `stride_alignment` parameter is typed as `u8`, which silently truncates common required alignments like 256 to 0, leading to a 0-byte allocation.
- [Low] The `ALIGN()` macro is invoked with a dynamically computed alignment value that is not structurally guaranteed to be a power of two.
--
commit 6b1d997c6e03c0072f6d0dc32b4cc844d7fbf8fe
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper
This commit introduces a helper to allow specifying a custom stride
alignment in bytes. This supports hardware requiring specific memory
boundaries.
> diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c
> index 3cc8b04e1ea63..a03e48812bcf5 100644
> --- a/drivers/media/v4l2-core/v4l2-common.c
> +++ b/drivers/media/v4l2-core/v4l2-common.c
> @@ -432,14 +432,28 @@ static inline unsigned int v4l2_format_block_height(const struct v4l2_format_inf
> }
>
> static inline unsigned int v4l2_format_plane_stride(const struct v4l2_format_info *info, int plane,
> - unsigned int width)
> + unsigned int width, unsigned int byte_alignment)
> {
> unsigned int hdiv = plane ? info->hdiv : 1;
> unsigned int aligned_width =
> ALIGN(width, v4l2_format_block_width(info, plane));
>
> - return DIV_ROUND_UP(aligned_width, hdiv) *
> - info->bpp[plane] / info->bpp_div[plane];
> + /*
> + * Formats with a single memory plane derive the stride of the
> + * other planes from the y stride. Due to chroma subsampling this
> + * may cause the other plane strides to break the byte_alignment.
> + * Increase the alignment if necessary to prevent this.
> + *
> + * It assumes the following format properties:
> + * - bpp_div[0] == bpp_div[1]
> + * - The multiplication factor doesn't differ between the non y planes
> + */
> + if (info->mem_planes == 1 && info->comp_planes > 1 && plane == 0)
> + byte_alignment *= DIV_ROUND_UP(info->hdiv * info->bpp[0], info->bpp[1]);
> +
> + return ALIGN(DIV_ROUND_UP(aligned_width, hdiv) * info->bpp[plane] /
> + info->bpp_div[plane],
> + byte_alignment);
> }
Could this calculation under-allocate the sizeimage for single-memory-plane
formats where the chroma stride is larger than the luma stride, such as
nv24 or nv42?
For nv24, the hdiv is 1, bpp[0] is 1, and bpp[1] is 2. DIV_ROUND_UP(1, 2)
evaluates to 1, which leaves the luma plane's byte_alignment unchanged.
If we have a width of 1 and a stride_alignment of 16, the software
calculates the luma stride as ALIGN(1, 16) which is 16. The hardware
mathematically derives the chroma stride from the luma stride: 16 * 2 = 32.
However, v4l2_format_plane_size() independently calculates the chroma plane
stride for sizeimage as ALIGN(1 * 2, 16) which is 16. Since the
independently calculated chroma stride (16) is smaller than the hardware's
derived chroma stride (32), does this mean the overall allocated sizeimage
will be too small and cause an out-of-bounds write during hardware
streaming?
Also related to this calculation, the ALIGN() macro requires the alignment
parameter to be a strict power of two. While the multiplication factor
evaluates to a power of two for currently supported planar formats, is there
a risk that future or custom formats might result in a non-power-of-two
factor?
If so, this would silently violate the macro's requirements. Would it be
safer to use roundup() or add a WARN_ON_ONCE(!is_power_of_2(byte_alignment))
to ensure the assumption holds?
[ ... ]
> @@ -476,8 +491,9 @@ void v4l2_apply_frmsize_constraints(u32 *width, u32 *height,
> }
> EXPORT_SYMBOL_GPL(v4l2_apply_frmsize_constraints);
>
> -int v4l2_fill_pixfmt_mp(struct v4l2_pix_format_mplane *pixfmt,
> - u32 pixelformat, u32 width, u32 height)
> +int v4l2_fill_pixfmt_mp_aligned(struct v4l2_pix_format_mplane *pixfmt,
> + u32 pixelformat, u32 width, u32 height,
> + u8 stride_alignment)
Can typing stride_alignment as a u8 cause issues for larger alignment
requirements?
If a driver needs an alignment of 256 bytes (which is common for some
modern hardware) and passes 256, the u8 type will silently truncate the
value to 0.
Passing 0 to the ALIGN() macro evaluates to (x - 1) & 0, which reliably
returns 0. Will this silently set bytesperline and sizeimage to 0, causing
a 0-byte memory allocation?
Would it be better to type this parameter as a u32?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=5
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 16/28] media: rockchip: rga: reuse cmdbuf contents
2026-05-15 15:32 ` [PATCH v6 16/28] media: rockchip: rga: reuse cmdbuf contents Sven Püschel
@ 2026-05-15 15:59 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 15:59 UTC (permalink / raw)
To: Sven Püschel; +Cc: conor+dt, krzk+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] Reducing the scope of `rga->ctrl_lock` in `device_run()` introduces a critical data race with concurrent V4L2 control updates, leading to inconsistent hardware command buffers and potential Out-Of-Bounds (OOB) DMA writes.
- [High] The newly introduced `ctx->cmdbuf_dirty` flag is updated in `vidioc_s_selection()` without holding `rga->ctrl_lock`, leading to lost updates and stale hardware configurations.
--
commit 9452d2457b5f041e524a98332bf069eeaf1ac9a6
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: reuse cmdbuf contents
This commit optimizes the command buffer programming by reusing its contents
instead of rewriting it completely for every frame. It introduces a
cmdbuf_dirty flag to track when rewriting is necessary.
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -38,6 +38,11 @@ static void device_run(void *prv)
> unsigned long flags;
>
> spin_lock_irqsave(&rga->ctrl_lock, flags);
> + if (ctx->cmdbuf_dirty) {
> + ctx->cmdbuf_dirty = false;
> + rga->hw->setup_cmdbuf(ctx);
> + }
> + spin_unlock_irqrestore(&rga->ctrl_lock, flags);
>
> rga->curr = ctx;
>
> @@ -47,8 +52,6 @@ static void device_run(void *prv)
> dst = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
>
> rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst));
> -
> - spin_unlock_irqrestore(&rga->ctrl_lock, flags);
> }
Can dropping rga->ctrl_lock before calling rga->hw->start() introduce a
data race with concurrent V4L2 control updates?
If VIDIOC_S_CTRL executes concurrently, rga_s_ctrl() modifies ctx->vflip,
ctx->hflip, and ctx->rotate under ctrl_lock.
Since rga->hw->start() calls rga_cmd_set() which then calls
rga_cmd_set_dst_info(), it reads these same control values to calculate the
destination DMA base address without holding the lock.
Could this result in setup_cmdbuf() programming the core hardware
transformation settings with old control values, while
rga_cmd_set_dst_info() uses new control values? If a flip control is
toggled during this window, might the hardware execute a normal copy but
start from a flipped destination offset, writing outside the allocated
boundaries of the DMA buffer?
[ ... ]
> @@ -564,6 +570,7 @@ static int vidioc_s_selection(struct file *file, void *priv,
> }
>
> f->crop = s->r;
> + ctx->cmdbuf_dirty = true;
>
> return ret;
> }
Does setting ctx->cmdbuf_dirty without holding rga->ctrl_lock lead to lost
updates?
Unlike vidioc_s_fmt(), vidioc_s_selection() does not appear to check if the
queue is busy, which means it could execute concurrently with active
streaming and the M2M worker thread running device_run().
If vidioc_s_selection() sets the flag at the same moment device_run()
clears it, could the update be lost?
Additionally, if f->crop is updated between device_run() calling
setup_cmdbuf() and rga_cmd_set_dst_info(), could the hardware be
programmed with mismatched active size and DMA destination addresses,
potentially causing out-of-bounds DMA writes?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=16
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 19/28] media: rockchip: rga: change offset to dma_addresses
2026-05-15 15:32 ` [PATCH v6 19/28] media: rockchip: rga: change offset to dma_addresses Sven Püschel
@ 2026-05-15 15:59 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 15:59 UTC (permalink / raw)
To: Sven Püschel; +Cc: robh, conor+dt, krzk+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Uninitialized stack memory in the `dma_addrs` array is written to the hardware's DMA-mapped command buffer for formats with fewer than 3 planes.
--
commit 9081b335b1506ccdaa6465a8ae0be247c58c4532
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: change offset to dma_addresses
This commit renames the offset fields to dma_addrs and changes their type
to dma_addr_t to better reflect that they represent addresses rather than
IOMMU offsets. It updates the buffer preparation and command generation
routines to use these newly named fields.
> diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
> index dcaba66f5c1fc..ab9554c1c4cd9 100644
> --- a/drivers/media/platform/rockchip/rga/rga-buf.c
> +++ b/drivers/media/platform/rockchip/rga/rga-buf.c
> @@ -121,7 +121,7 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
> size_t curr_desc = 0;
> int i;
> const struct v4l2_format_info *info;
> - unsigned int offsets[VIDEO_MAX_PLANES];
> + dma_addr_t dma_addrs[VIDEO_MAX_PLANES];
[ ... ]
> @@ -145,18 +145,18 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
> "Failed to map video buffer to RGA\n");
> return n_desc;
> }
> - offsets[i] = curr_desc << PAGE_SHIFT;
> + dma_addrs[i] = curr_desc << PAGE_SHIFT;
> curr_desc += n_desc;
> }
>
> /* Fill the remaining planes */
> info = v4l2_format_info(f->fmt->fourcc);
> for (i = info->mem_planes; i < info->comp_planes; i++)
> - offsets[i] = get_plane_offset(f, info, i);
> + dma_addrs[i] = dma_addrs[0] + get_plane_offset(f, info, i);
>
> - rbuf->offset.y_off = offsets[0];
> - rbuf->offset.u_off = offsets[1];
> - rbuf->offset.v_off = offsets[2];
> + rbuf->dma_addrs.y_addr = dma_addrs[0];
> + rbuf->dma_addrs.u_addr = dma_addrs[1];
> + rbuf->dma_addrs.v_addr = dma_addrs[2];
Does this code leak uninitialized stack memory into the hardware's DMA-mapped
command buffer?
If info->comp_planes is 1 (such as with RGB formats), the initialization
loops above do not populate dma_addrs[1] and dma_addrs[2]. These array
elements remain uninitialized stack memory.
However, the code unconditionally assigns these elements to
rbuf->dma_addrs.u_addr and v_addr.
Later, rga_cmd_set_src_info() and rga_cmd_set_dst_info() use these values,
apply arithmetic to them, and write the results directly into the device's
DMA command buffer:
dest[(RGA_SRC_CB_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
src_corner_addrs.left_top.u_addr;
dest[(RGA_SRC_CR_BASE_ADDR - RGA_MODE_BASE_REG) >> 2] =
src_corner_addrs.left_top.v_addr;
Could this leak stack memory to the device or trigger KMSAN warnings on
uninitialized value usage?
While this behavior existed previously with the offsets array, should
dma_addrs be zero-initialized to resolve it?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=19
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 18/28] media: rockchip: rga: use card type to specify rga type
2026-05-15 15:32 ` [PATCH v6 18/28] media: rockchip: rga: use card type to specify rga type Sven Püschel
@ 2026-05-15 16:00 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:00 UTC (permalink / raw)
To: Sven Püschel; +Cc: robh, krzk+dt, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Changing the V4L2 `card` capability string for existing devices breaks userspace backwards compatibility.
- [Medium] The commit updates `cap->card` to differentiate device nodes but fails to update `vfd->name`, leaving sysfs attributes with the generic name.
--
commit bb9e5b143d71f3baaeea92ea613c7a8d9627f5cc
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: use card type to specify rga type
In preparation of the RGA3 support add a filed to the rga_hw struct
to specify the desired card type value. This allows the user to
differentiate the RGA2 and RGA3 video device nodes.
[ ... ]
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index 22954bbae55fc..91775b43ff617 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -354,8 +354,10 @@ static const struct v4l2_file_operations rga_fops = {
> static int
> vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap)
> {
> + struct rockchip_rga *rga = video_drvdata(file);
> +
> strscpy(cap->driver, RGA_NAME, sizeof(cap->driver));
> - strscpy(cap->card, "rockchip-rga", sizeof(cap->card));
> + strscpy(cap->card, rga->hw->card_type, sizeof(cap->card));
Will changing the cap->card string from "rockchip-rga" to "rga2" break
existing userspace applications or udev rules that rely on the old string
to identify the hardware?
The cap->card field is often used by udev (which extracts it to
ID_V4L_PRODUCT) and userspace frameworks like libcamera to identify specific
video hardware, so modifying it could cause regressions for existing
configurations.
Additionally, since the goal is to differentiate RGA2 and RGA3 device nodes,
should this change also update the name field of the video_device struct
(vfd->name)?
If vfd->name remains initialized from the generic template as "rockchip-rga",
sysfs attributes like /sys/class/video4linux/videoX/name will still report
the old name. This could prevent tools relying on sysfs from differentiating
the nodes as intended.
> strscpy(cap->bus_info, "platform:rga", sizeof(cap->bus_info));
>
> return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=18
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 11/28] media: rockchip: rga: move hw specific parts to a dedicated struct
2026-05-15 15:32 ` [PATCH v6 11/28] media: rockchip: rga: move hw specific parts to a dedicated struct Sven Püschel
@ 2026-05-15 16:05 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:05 UTC (permalink / raw)
To: Sven Püschel; +Cc: conor+dt, krzk+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Lack of PM state verification in the ISR allows spurious interrupts on suspended hardware to trigger a kernel panic.
- [Critical] Integer overflow in `vidioc_s_selection` allows bypassing bounds checks with maliciously large crop dimensions.
- [High] Use-After-Free race condition during module removal due to unbalanced teardown order in `rga_remove()`.
- [High] Unhandled initialization error in `rga_open()` leads to a use-after-free of a destroyed mutex.
--
commit 0a388e94d347297bf67ec0c6a92dbb56a186e007
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: move hw specific parts to a dedicated struct
In preparation for the RGA3 unit, move RGA2 specific parts from rga.c
to rga-hw.c and create a struct to reference the RGA2 specific functions
and formats.
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index 8c34f73d69764..f599c992829dd 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -55,13 +54,8 @@ static void device_run(void *prv)
> static irqreturn_t rga_isr(int irq, void *prv)
> {
> struct rockchip_rga *rga = prv;
> - int intr;
>
> - intr = rga_read(rga, RGA_INT) & 0xf;
> -
> - rga_mod(rga, RGA_INT, intr << 4, 0xf << 4);
> -
> - if (intr & 0x04) {
> + if (rga->hw->handle_irq(rga)) {
> struct vb2_v4l2_buffer *src, *dst;
> struct rga_ctx *ctx = rga->curr;
>
> WARN_ON(!ctx);
This isn't a bug introduced by this commit, but is it possible for rga_isr
to be called when the device is suspended?
If the device is suspended, reading RGA_INT might return 0xffffffff, which
evaluates to true here. Since ctx would be NULL, the code warns but then
dereferences ctx via ctx->fh.m2m_ctx, which could cause a panic.
With the planned addition of IRQF_SHARED support for external IOMMUs
mentioned in later patches, another device on the same shared interrupt
line could trigger this when the RGA is suspended. Would we need to verify
if the device is active using pm_runtime_get_if_active() before accessing
registers?
[ ... ]
> @@ -360,9 +225,9 @@ static int rga_open(struct file *file)
> ctx->out = def_frame;
>
> v4l2_fill_pixfmt_mp(&ctx->in.pix,
> - ctx->in.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT);
> + ctx->in.fmt->fourcc, def_width, def_height);
> v4l2_fill_pixfmt_mp(&ctx->out.pix,
> - ctx->out.fmt->fourcc, DEFAULT_WIDTH, DEFAULT_HEIGHT);
> + ctx->out.fmt->fourcc, def_width, def_height);
>
> if (mutex_lock_interruptible(&rga->mutex)) {
> kfree(ctx);
This isn't a bug introduced by this commit, but looking further down in
rga_open(), is it safe to ignore the return value of rga_setup_ctrls(ctx)?
If control setup fails, rga_setup_ctrls() internally calls
v4l2_ctrl_handler_free(), which frees the internal control buckets and
destroys the mutex.
However, rga_open() blindly proceeds to call
v4l2_ctrl_handler_setup(&ctx->ctrl_handler) and assigns the freed handler
to ctx->fh.ctrl_handler. Could this lead to a use-after-free of the mutex
during subsequent V4L2 ioctl operations?
[ ... ]
> @@ -660,7 +527,7 @@ static int vidioc_s_selection(struct file *file, void *priv,
>
> if (s->r.left + s->r.width > f->pix.width ||
> s->r.top + s->r.height > f->pix.height ||
> - s->r.width < MIN_WIDTH || s->r.height < MIN_HEIGHT) {
> + s->r.width < rga->hw->min_width || s->r.height < rga->hw->min_height) {
> v4l2_dbg(debug, 1, &rga->v4l2_dev, "unsupported crop value.\n");
> return -EINVAL;
> }
This isn't a bug introduced by this commit, but can the
s->r.left + s->r.width check overflow here?
Since s->r.width is an unsigned 32-bit integer, providing a maliciously
large width (e.g., 0xFFFFFFFF) and a small left offset could cause the
addition to wrap around to 0, bypassing the bounds check.
Furthermore, 0xFFFFFFFF < rga->hw->min_width evaluates to false, which
bypasses this minimum size check as well. Could this result in out-of-bounds
memory operations when the hardware DMA engine is programmed with these
excessively large dimensions?
[ ... ]
> @@ -883,7 +750,7 @@ static void rga_remove(struct platform_device *pdev)
> {
> struct rockchip_rga *rga = platform_get_drvdata(pdev);
>
> - dma_free_attrs(rga->dev, RGA_CMDBUF_SIZE, rga->cmdbuf_virt,
> + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt,
> rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
>
> v4l2_info(&rga->v4l2_dev, "Removing\n");
This isn't a bug introduced by this commit, but is the teardown order
in rga_remove() safe?
It frees the DMA command buffer and releases the mem2mem context before
calling video_unregister_device().
Could a concurrent userspace process holding the device open continue to
issue V4L2 ioctls while the module is unloading, potentially causing a
use-after-free on m2m_dev or the newly freed DMA buffer memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=11
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 20/28] media: rockchip: rga: support external iommus
2026-05-15 15:32 ` [PATCH v6 20/28] media: rockchip: rga: support external iommus Sven Püschel
@ 2026-05-15 16:08 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:08 UTC (permalink / raw)
To: Sven Püschel; +Cc: conor+dt, robh, krzk+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] An off-by-one bounds check in `fill_descriptors` combined with unsigned integer underflow allows an attacker to perform an unbounded out-of-bounds heap write.
--
commit bf314a22b4aaca4693af88a7c738812bfaefb82d
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: support external iommus
In preparation for the RGA3 add support for external iommus. This is a
transition step to just disable the RGA2 specific mmu table setup code.
> diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
> index ab9554c1c4cd9..cd6904d5fe5a6 100644
> --- a/drivers/media/platform/rockchip/rga/rga-buf.c
> +++ b/drivers/media/platform/rockchip/rga/rga-buf.c
[ ... ]
> @@ -136,17 +140,21 @@ static int rga_buf_prepare(struct vb2_buffer *vb)
> for (i = 0; i < vb->num_planes; i++) {
> vb2_set_plane_payload(vb, i, f->pix.plane_fmt[i].sizeimage);
>
> - /* Create local MMU table for RGA */
> - n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc],
> - rbuf->n_desc - curr_desc,
> - vb2_dma_sg_plane_desc(vb, i));
> - if (n_desc < 0) {
> - v4l2_err(&ctx->rga->v4l2_dev,
> - "Failed to map video buffer to RGA\n");
> - return n_desc;
> + if (rga_has_internal_iommu(ctx->rga)) {
> + /* Create local MMU table for RGA */
> + n_desc = fill_descriptors(&rbuf->dma_desc[curr_desc],
> + rbuf->n_desc - curr_desc,
This problem wasn't introduced by this patch, but could this calculation
underflow and cause an out-of-bounds write?
In fill_descriptors(), the loop condition allows one extra iteration:
rga-buf.c:fill_descriptors() {
...
for_each_sgtable_dma_page(sgt, &iter, 0) {
if (n_desc > max_desc)
return -EINVAL;
...
}
If n_desc == max_desc, it allows an extra element to be written and returns
max_desc + 1.
If a user provides a multi-planar buffer via V4L2 where the first plane's
scatterlist contains exactly rbuf->n_desc + 1 pages, fill_descriptors()
writes one element out-of-bounds and returns rbuf->n_desc + 1 instead of an
error.
> + vb2_dma_sg_plane_desc(vb, i));
> + if (n_desc < 0) {
> + v4l2_err(&ctx->rga->v4l2_dev,
> + "Failed to map video buffer to RGA\n");
> + return n_desc;
> + }
> + dma_addrs[i] = curr_desc << PAGE_SHIFT;
> + curr_desc += n_desc;
Since curr_desc receives this return value, could rbuf->n_desc - curr_desc
underflow to SIZE_MAX during the next iteration of the loop for the second
plane?
This would pass SIZE_MAX as the max_desc argument to fill_descriptors(),
potentially allowing an unbounded out-of-bounds write to adjacent memory.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=20
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 21/28] media: rockchip: rga: share the interrupt when an external iommu is used
2026-05-15 15:32 ` [PATCH v6 21/28] media: rockchip: rga: share the interrupt when an external iommu is used Sven Püschel
@ 2026-05-15 16:11 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:11 UTC (permalink / raw)
To: Sven Püschel; +Cc: krzk+dt, robh, devicetree, conor+dt
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Accessing hardware registers without ensuring power/clock state in shared interrupt handler.
- [High] Unconditional `IRQ_HANDLED` return from shared interrupt handler defeats spurious IRQ detection.
- [High] Data race on the `RGA_INT` register due to lockless ISR execution.
- [High] Early IRQ registration introduces a NULL pointer dereference window.
--
commit 9f93e8eca57c7b5671e83f97e16709aa91786176
Author: Michael Olbrich <m.olbrich@pengutronix.de>
media: rockchip: rga: share the interrupt when an external iommu is used
The RGA3 and the corresponding iommu share the interrupt. So in that
case, request a shared interrupt so that the iommu driver can request
it as well.
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index e3c99c3f7c5be..cda3cecb1ce8c 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -767,7 +767,8 @@ static int rga_probe(struct platform_device *pdev)
> goto err_put_clk;
> }
>
> - ret = devm_request_irq(rga->dev, irq, rga_isr, 0,
> + ret = devm_request_irq(rga->dev, irq, rga_isr,
> + rga_has_internal_iommu(rga) ? 0 : IRQF_SHARED,
> dev_name(rga->dev), rga);
By enabling IRQF_SHARED, the rga_isr handler can be triggered by interrupts
from other devices (like the IOMMU) at any time. Does the current ISR safely
handle these shared interrupts?
Looking at rga_isr(), it always returns IRQ_HANDLED even when the interrupt
was not generated by this device:
drivers/media/platform/rockchip/rga/rga.c:rga_isr() {
if (rga->hw->handle_irq(rga)) {
...
}
return IRQ_HANDLED;
}
Should the handler return IRQ_NONE when it determines the interrupt wasn't
generated by its hardware, to avoid defeating the kernel's spurious IRQ
detection?
Additionally, because the IRQ is requested here before the driver is fully
initialized, could an immediate shared interrupt cause a NULL pointer
dereference?
drivers/media/platform/rockchip/rga/rga.c:rga_isr() {
...
if (rga->hw->handle_irq(rga)) {
...
struct rga_ctx *ctx = rga->curr;
WARN_ON(!ctx);
rga->curr = NULL;
src = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
...
}
If the hardware happens to have a pending interrupt bit, rga->curr will be
NULL, and the code assigns NULL to ctx, then attempts to dereference
ctx->fh.m2m_ctx.
Could this early registration also cause the ISR to attempt to access
hardware registers while the device's clocks and power domains are disabled?
Because the driver uses PM runtime, the device may be suspended when a shared
interrupt fires. Is it necessary to verify pm_runtime_active(rga->dev)
before reading registers in the ISR to avoid a synchronous external abort?
Finally, could this lead to a data race on the RGA_INT register?
With IRQF_SHARED, rga_isr() can run asynchronously on another CPU while
device_run() is executing:
drivers/media/platform/rockchip/rga/rga.c:device_run() {
...
rga->hw->start(rga, vb_to_rga(src), vb_to_rga(dst));
}
If device_run() calls rga->hw->start() to write 0x600 to the RGA_INT register
to enable interrupts, while rga_isr() locklessly performs a read-modify-write
on RGA_INT, could the ISR silently overwrite the configuration with 0,
disabling RGA interrupts mid-job?
> if (ret < 0) {
> dev_err(rga->dev, "failed to request irq\n");
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=21
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 14/28] media: rockchip: rga: move cmdbuf to rga_ctx
2026-05-15 15:32 ` [PATCH v6 14/28] media: rockchip: rga: move cmdbuf to rga_ctx Sven Püschel
@ 2026-05-15 16:12 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:12 UTC (permalink / raw)
To: Sven Püschel; +Cc: robh, krzk+dt, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Moving `dma_free_attrs()` to `rga_release()` exacerbates a Use-After-Free vulnerability.
- [High] Pre-existing Runtime PM race condition in the `rga_probe` error path.
- [High] Pre-existing Use-After-Free in `rga_open` due to ignored return value from `rga_setup_ctrls`.
- [High] Pre-existing Race Condition in `rga_remove` leading to Use-After-Free on the mem2mem device.
--
commit 4ef6c9ed68ddeb3165ce5597644374f3db2140ac
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: move cmdbuf to rga_ctx
This commit moves the command buffer to the rga_ctx struct to prepare for
reusing an already prepared command buffer. No sync point is added, as one
command buffer is only used for one conversion at a time.
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
[ ... ]
> @@ -230,15 +240,13 @@ static int rga_open(struct file *file)
> ctx->out.fmt->fourcc, def_width, def_height);
>
> if (mutex_lock_interruptible(&rga->mutex)) {
> - kfree(ctx);
> - return -ERESTARTSYS;
> + ret = -ERESTARTSYS;
> + goto rel_cmdbuf;
> }
> ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rga->m2m_dev, ctx, &queue_init);
> if (IS_ERR(ctx->fh.m2m_ctx)) {
> ret = PTR_ERR(ctx->fh.m2m_ctx);
> - mutex_unlock(&rga->mutex);
> - kfree(ctx);
> - return ret;
> + goto unlock_mutex;
> }
> v4l2_fh_init(&ctx->fh, video_devdata(file));
> v4l2_fh_add(&ctx->fh, file);
This isn't a bug introduced by this patch, but while reviewing this area I
noticed that rga_open() calls rga_setup_ctrls() just after this block:
rga_setup_ctrls(ctx);
/* Write the default values to the ctx struct */
v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
Is it safe to ignore the return value of rga_setup_ctrls() here?
If the control setup fails, rga_setup_ctrls() frees the control handler and
destroys its mutex internally. Because the failure is ignored, the code
proceeds to call v4l2_ctrl_handler_setup() on the destroyed handler, which
could trigger a use-after-free on the mutex and internal control structures.
[ ... ]
> @@ -266,6 +283,10 @@ static int rga_release(struct file *file)
> v4l2_ctrl_handler_free(&ctx->ctrl_handler);
> v4l2_fh_del(&ctx->fh, file);
> v4l2_fh_exit(&ctx->fh);
> +
> + dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, ctx->cmdbuf_virt,
> + ctx->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
> +
> kfree(ctx);
>
> mutex_unlock(&rga->mutex);
Could this trigger a use-after-free when the file descriptor is kept open
after the device unbinds?
The rga structure is allocated using devm_kzalloc() during probe, which
means it is automatically freed by devres when the device is unbound via
sysfs or module removal. If userspace keeps the V4L2 file descriptor open
after the device is removed, rga_release() will be called on close.
When that happens, dma_free_attrs() dereferences the already freed rga
pointer to access rga->dev and rga->hw. Calling the DMA API with a detached
or destroyed device structure might also trigger IOMMU faults or kernel
panics.
[ ... ]
> @@ -720,19 +741,10 @@ static int rga_probe(struct platform_device *pdev)
>
> pm_runtime_put(rga->dev);
>
> - /* Create CMD buffer */
> - rga->cmdbuf_virt = dma_alloc_attrs(rga->dev, rga->hw->cmdbuf_size,
> - &rga->cmdbuf_phy, GFP_KERNEL,
> - DMA_ATTR_WRITE_COMBINE);
> - if (!rga->cmdbuf_virt) {
> - ret = -ENOMEM;
> - goto rel_m2m;
> - }
> -
> ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1);
> if (ret) {
> v4l2_err(&rga->v4l2_dev, "Failed to register video device\n");
> - goto free_dma;
> + goto rel_m2m;
> }
This isn't a bug introduced by this patch, but could this error path leave
the hardware in an active power state indefinitely?
If video_register_device() fails, the code jumps to rel_m2m, which falls
through to the err_put_clk label:
err_put_clk:
pm_runtime_disable(rga->dev);
Because an asynchronous pm_runtime_put(rga->dev) is called just before
registration, the pending async suspend work would be abruptly canceled by
the internal barrier in pm_runtime_disable(). Should this use
pm_runtime_put_sync() instead before entering the error path?
[ ... ]
> @@ -759,9 +768,6 @@ static void rga_remove(struct platform_device *pdev)
> {
> struct rockchip_rga *rga = platform_get_drvdata(pdev);
>
> - dma_free_attrs(rga->dev, rga->hw->cmdbuf_size, rga->cmdbuf_virt,
> - rga->cmdbuf_phy, DMA_ATTR_WRITE_COMBINE);
> -
> v4l2_info(&rga->v4l2_dev, "Removing\n");
>
> v4l2_m2m_release(rga->m2m_dev);
This isn't a bug introduced by this patch, but does calling
v4l2_m2m_release() here before video_unregister_device() create a race
condition?
If v4l2_m2m_release(rga->m2m_dev) runs first, it leaves a window where the
video node is still active and accessible to userspace, but the backing
mem2mem device has already been freed.
If a user space program calls an ioctl during this window, could the file
operations access the freed rga->m2m_dev and cause a use-after-free?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=14
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 10/28] media: rockchip: rga: announce and sync colorimetry
2026-05-15 15:32 ` [PATCH v6 10/28] media: rockchip: rga: announce and sync colorimetry Sven Püschel
@ 2026-05-15 16:14 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:14 UTC (permalink / raw)
To: Sven Püschel; +Cc: krzk+dt, robh, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Falsely advertising hardware capabilities for CSC quantization and YCbCr encoding.
- [High] Missing validation and clamping of colorimetry fields in `vidioc_try_fmt`.
- [Medium] `vidioc_try_fmt` uses the CAPTURE queue's state instead of the OUTPUT queue's state as the default colorimetry.
- [Medium] `vidioc_try_fmt` accepts user-provided colorimetry for non-YUV formats, bypassing intended restrictions.
- [Low] Comment in `vidioc_enum_fmt` contradicts the implemented code.
--
commit 0b2b6b238c69f747c597a881c5d1349d57e712fe
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: announce and sync colorimetry
Announce the capability to adjust the quantization and ycbcr_enc on the
capture side and check if the SET_CSC flag is set when the colorimetry
is changed. Furthermore copy the colorimetry from the output to the
capture side to fix the currently failing v4l2-compliance tests, which
expect exactly this behavior.
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index ca8d8a53dc251..8c34f73d69764 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -437,6 +437,15 @@ static int vidioc_enum_fmt(struct file *file, void *priv, struct v4l2_fmtdesc *f
> fmt = &formats[f->index];
> f->pixelformat = fmt->fourcc;
>
> + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
> + f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
> + return 0;
> +
> + /* allow changing the quantization and xfer func for YUV formats */
This isn't a bug, but the comment mentions "xfer func" instead of
"ycbcr_enc", while the code sets V4L2_FMT_FLAG_CSC_YCBCR_ENC. Furthermore,
vidioc_try_fmt explicitly disallows modifying xfer_func.
> + if (v4l2_is_format_yuv(v4l2_format_info(f->pixelformat)))
> + f->flags |= V4L2_FMT_FLAG_CSC_QUANTIZATION |
> + V4L2_FMT_FLAG_CSC_YCBCR_ENC;
Does the hardware actually support applying custom quantization and YCbCr
encoding matrices?
Looking at rga_cmd_set_trans_info() in rga-hw.c, the hardware configuration
backend appears to ignore quantization and ycbcr_enc, hardcoding the CSC
mode to either RGA_SRC_CSC_MODE_BT709_R0 or RGA_SRC_CSC_MODE_BT601_R0 based
purely on colorspace.
If the capabilities are announced merely to pass v4l2-compliance without
applying them in hardware, could this cause the driver to silently ignore
user requests and produce visually incorrect output?
[ ... ]
> @@ -459,8 +468,25 @@ static int vidioc_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
> static int vidioc_try_fmt(struct file *file, void *priv, struct v4l2_format *f)
> {
> struct v4l2_pix_format_mplane *pix_fmt = &f->fmt.pix_mp;
> + struct rga_ctx *ctx = file_to_rga_ctx(file);
> struct rga_fmt *fmt;
>
> + if (V4L2_TYPE_IS_CAPTURE(f->type)) {
> + const struct rga_frame *frm;
> +
> + frm = rga_get_frame(ctx, f->type);
Is it correct to use the CAPTURE queue's state here instead of the OUTPUT
queue's state as the default colorimetry?
For memory-to-memory devices, when V4L2_PIX_FMT_FLAG_SET_CSC is not set on
the CAPTURE queue, it usually falls back to the colorimetry of the OUTPUT
queue (ctx->in). Fetching the frame with f->type resolves to ctx->out for
the CAPTURE queue.
If a user previously used SET_CSC to override the CAPTURE queue's
colorimetry, wouldn't a subsequent call without SET_CSC inherit this
overridden state instead of resetting to the OUTPUT queue's defaults?
> + if (IS_ERR(frm))
> + return PTR_ERR(frm);
> +
> + if (!(pix_fmt->flags & V4L2_PIX_FMT_FLAG_SET_CSC)) {
> + pix_fmt->quantization = frm->pix.quantization;
> + pix_fmt->ycbcr_enc = frm->pix.ycbcr_enc;
> + }
When V4L2_PIX_FMT_FLAG_SET_CSC is set, does this blindly accept
pix_fmt->quantization and pix_fmt->ycbcr_enc without validating if they are
supported enum values?
Additionally, does this enforce the restriction that colorimetry can only
be changed for YUV formats? vidioc_enum_fmt restricts the CSC flags to YUV
formats, but this code seems to honor SET_CSC on the CAPTURE queue
regardless of the format type, allowing arbitrary colorimetry values even
for RGB formats.
Furthermore, for the OUTPUT queue, the colorimetry fields don't seem to be
validated or resolved. Does the driver need to resolve
V4L2_COLORSPACE_DEFAULT to concrete standards and clamp unsupported values
during try_fmt as required by the V4L2 specification?
> + /* disallow values not announced in vidioc_enum_fmt */
> + pix_fmt->colorspace = frm->pix.colorspace;
> + pix_fmt->xfer_func = frm->pix.xfer_func;
> + }
> +
> fmt = rga_fmt_find(pix_fmt->pixelformat);
> if (!fmt)
> fmt = &formats[0];
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=10
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 15/28] media: rockchip: rga: align stride to 4 bytes
2026-05-15 15:32 ` [PATCH v6 15/28] media: rockchip: rga: align stride to 4 bytes Sven Püschel
@ 2026-05-15 16:17 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:17 UTC (permalink / raw)
To: Sven Püschel; +Cc: krzk+dt, conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Buffer offset mismatch and hardware stride truncation for planar YUV formats with hdiv=2.
--
commit bb91296b78fd1bbee406e680e751a44387d9a72a
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: align stride to 4 bytes
This commit adds a stride_alignment setting to rga_hw and updates the
driver to use v4l2_fill_pixfmt_mp_aligned(). This ensures the stride is
aligned to 4 bytes, avoiding off-by-one errors when the driver divides
the stride by 4 to program the word-based RGA registers.
> diff --git a/drivers/media/platform/rockchip/rga/rga-hw.c b/drivers/media/platform/rockchip/rga/rga-hw.c
> index 9881c14f908d5..dac3cb6aa17d3 100644
> --- a/drivers/media/platform/rockchip/rga/rga-hw.c
> +++ b/drivers/media/platform/rockchip/rga/rga-hw.c
> @@ -580,6 +580,7 @@ const struct rga_hw rga2_hw = {
> .max_width = MAX_WIDTH,
> .min_height = MIN_HEIGHT,
> .max_height = MAX_HEIGHT,
> + .stride_alignment = 4,
Could this cause buffer offset mismatches and hardware stride truncation
for planar YUV formats with horizontal subsampling?
For planar formats where hdiv=2, such as V4L2_PIX_FMT_YUV420, a width of
642 results in a Y stride of 644 when aligned to 4. The U/V stride is
typically half of the Y stride, which would equal 322 bytes.
If the hardware relies on word units (4 bytes), 322 is not divisible by 4.
It might be truncated to 80 words (320 bytes), causing a 2-byte drift per
line for the chrominance channels.
Additionally, v4l2_fill_pixfmt_mp_aligned() calculates sizeimage for
single-planar formats by aligning each plane's stride to the specified
alignment. For a 322-byte U stride, it aligns up to 324 bytes, introducing
padding. If the driver derives the V plane offset natively using the
unaligned bytesperline / 2, the hardware will read from a smaller,
unpadded offset than where the V4L2 user-space placed it.
Does the Y stride alignment need to be 8 bytes when hdiv=2 to ensure the
derived U/V strides are multiples of 4?
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=15
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 22/28] media: rockchip: rga: remove size from rga_frame
2026-05-15 15:32 ` [PATCH v6 22/28] media: rockchip: rga: remove size from rga_frame Sven Püschel
@ 2026-05-15 16:21 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:21 UTC (permalink / raw)
To: Sven Püschel; +Cc: devicetree, robh, krzk+dt, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] Heap buffer overflow in DMA descriptor array due to incorrect size calculation and off-by-one bounds check
--
commit 80a8417d4cdd0685a8b6425bf5882684584c9ac7
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: remove size from rga_frame
This commit removes the size member from the rga_frame structure, as it
is only used for MMU page table mapping. Instead, the size is calculated
in-place within rga_buf_init() by iterating over the planes.
> diff --git a/drivers/media/platform/rockchip/rga/rga-buf.c b/drivers/media/platform/rockchip/rga/rga-buf.c
> index cd6904d5fe5a6..092c2bdf6b672 100644
> --- a/drivers/media/platform/rockchip/rga/rga-buf.c
> +++ b/drivers/media/platform/rockchip/rga/rga-buf.c
> @@ -79,6 +79,8 @@ static int rga_buf_init(struct vb2_buffer *vb)
> struct rockchip_rga *rga = ctx->rga;
> struct rga_frame *f = rga_get_frame(ctx, vb->vb2_queue->type);
> size_t n_desc = 0;
> + u32 size = 0;
> + u8 i;
>
> if (IS_ERR(f))
> return PTR_ERR(f);
> @@ -86,7 +88,9 @@ static int rga_buf_init(struct vb2_buffer *vb)
> if (!rga_has_internal_iommu(rga))
> return 0;
>
> - n_desc = DIV_ROUND_UP(f->size, PAGE_SIZE);
> + for (i = 0; i < f->pix.num_planes; i++)
> + size += f->pix.plane_fmt[i].sizeimage;
> + n_desc = DIV_ROUND_UP(size, PAGE_SIZE);
Does this calculation under-allocate the descriptor array if userspace uses
VIDIOC_CREATE_BUFS to request buffers larger than sizeimage?
While this behavior wasn't introduced by this patch, inlining the
calculation here preserves an existing regression in the buffer allocation
logic.
Since vb2 can provision larger scatterlists than the format's sizeimage,
rbuf->n_desc might be smaller than the actual buffer length.
If fill_descriptors() uses a check like "if (n_desc > max_desc)", could a
plane sized to exactly rbuf->n_desc + 1 pages bypass the check and write
one entry out of bounds?
When the second plane is mapped, could max_desc (which would be
rbuf->n_desc - curr_desc) underflow? This might bypass remaining bounds
checks and allow the scatterlist of the second plane to sequentially
overwrite adjacent kernel heap memory.
>
> rbuf->n_desc = n_desc;
> rbuf->dma_desc = dma_alloc_coherent(rga->dev,
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=22
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 25/28] media: rockchip: rga: add feature flags
2026-05-15 15:32 ` [PATCH v6 25/28] media: rockchip: rga: add feature flags Sven Püschel
@ 2026-05-15 16:22 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:22 UTC (permalink / raw)
To: Sven Püschel; +Cc: robh, krzk+dt, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Missing return value check for `rga_setup_ctrls` in `rga_open` leads to a Use-After-Destroy of the control handler mutex.
--
commit 0e807f6179fe82fa9a8a593d5cefb491574e313b
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: add feature flags
In preparation to the RGA3 addition add feature flags, which can limit
the exposed feature set of the video device, like rotating or selection
support. This is necessary as the RGA3 doesn't initially implement the
full feature set currently exposed by the driver.
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index 1878b4e26360b..8d60e94da32d8 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -177,17 +177,21 @@ static int rga_setup_ctrls(struct rga_ctx *ctx)
>
> v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4);
>
> - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
> - V4L2_CID_HFLIP, 0, 1, 1, 0);
> + if (rga->hw->features & RGA_FEATURE_FLIP) {
> + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
> + V4L2_CID_HFLIP, 0, 1, 1, 0);
>
> - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
> - V4L2_CID_VFLIP, 0, 1, 1, 0);
> + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
> + V4L2_CID_VFLIP, 0, 1, 1, 0);
> + }
>
> - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
> - V4L2_CID_ROTATE, 0, 270, 90, 0);
> + if (rga->hw->features & RGA_FEATURE_ROTATE)
> + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
> + V4L2_CID_ROTATE, 0, 270, 90, 0);
>
> - v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
> - V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0);
> + if (rga->hw->features & RGA_FEATURE_BG_COLOR)
> + v4l2_ctrl_new_std(&ctx->ctrl_handler, &rga_ctrl_ops,
> + V4L2_CID_BG_COLOR, 0, 0xffffffff, 1, 0);
>
> if (ctx->ctrl_handler.error) {
> int err = ctx->ctrl_handler.error;
This issue wasn't introduced by this patch, but since rga_setup_ctrls() is
modified here, does the error handling in the caller expose a use-after-free
if rga_setup_ctrls() fails?
In rga_setup_ctrls(), if there is an error, it calls
v4l2_ctrl_handler_free(&ctx->ctrl_handler) and returns an error code.
However, in rga_open(), the return value of rga_setup_ctrls() is ignored:
drivers/media/platform/rockchip/rga/rga.c:rga_open() {
...
rga_setup_ctrls(ctx);
/* Write the default values to the ctx struct */
v4l2_ctrl_handler_setup(&ctx->ctrl_handler);
...
}
Since v4l2_ctrl_handler_free() destroys the handler's internal mutex, does
calling v4l2_ctrl_handler_setup() subsequently attempt to lock a destroyed
mutex?
Additionally, does rga_open() returning success to userspace leave the V4L2
context in a corrupted state if rga_setup_ctrls() failed?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=25
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 27/28] media: rockchip: rga: add rga3 support
2026-05-15 15:32 ` [PATCH v6 27/28] media: rockchip: rga: add rga3 support Sven Püschel
@ 2026-05-15 16:34 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:34 UTC (permalink / raw)
To: Sven Püschel; +Cc: robh, krzk+dt, conor+dt, devicetree
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Kernel Panic on Shared IRQ due to missing PM Runtime active check
- [High] DMA API Violation and Out-of-bounds Cache Sync on Coherent Memory
- [High] Silent API Failure of Crop Coordinates
- [Medium] Hardware Error Interrupts Masked
--
commit 71c16e32b64b60b7b83f81a3d0e8a736dfa30182
Author: Sven Püschel <s.pueschel@pengutronix.de>
Subject: media: rockchip: rga: add rga3 support
Add support for the RGA3 unit contained in the RK3588. Only a basic
feature set consisting of scaling and color conversion is implemented.
> diff --git a/drivers/media/platform/rockchip/rga/rga3-hw.c b/drivers/media/platform/rockchip/rga/rga3-hw.c
> new file mode 100644
> index 0000000000000..ca1c268303dd4
> --- /dev/null
> +++ b/drivers/media/platform/rockchip/rga/rga3-hw.c
[ ... ]
> +static void rga3_cmd_set_win0_addr(struct rga_ctx *ctx,
> + const struct rga_addrs *addrs)
> +{
> + u32 *cmd = ctx->cmdbuf_virt;
> + unsigned int reg;
> +
> + reg = RGA3_WIN0_Y_BASE - RGA3_FIRST_CMD_REG;
> + cmd[reg >> 2] = addrs->y_addr;
> + reg = RGA3_WIN0_U_BASE - RGA3_FIRST_CMD_REG;
> + cmd[reg >> 2] = addrs->u_addr;
> +}
Does this code ignore the crop coordinates (ctx->in.crop.top and
ctx->in.crop.left)?
In the existing RGA2 implementation, offsets are applied using
rga_get_corner_addrs(). Here, the unadjusted buffer base addresses are
used. If userspace sets a crop selection with non-zero top/left offsets,
the API call will succeed but the hardware will process the image starting
from (0, 0).
[ ... ]
> +static void rga3_hw_start(struct rockchip_rga *rga,
> + struct rga_vb_buffer *src, struct rga_vb_buffer *dst)
> +{
> + struct rga_ctx *ctx = rga->curr;
> +
> + rga3_cmd_set_win0_addr(ctx, &src->dma_addrs);
> + rga3_cmd_set_wr_addr(ctx, &dst->dma_addrs);
> +
> + rga_write(rga, RGA3_CMD_ADDR, ctx->cmdbuf_phy);
> +
> + /* sync CMD buf for RGA */
> + dma_sync_single_for_device(rga->dev, ctx->cmdbuf_phy,
> + PAGE_SIZE, DMA_BIDIRECTIONAL);
Is it safe to use dma_sync_single_for_device() on ctx->cmdbuf_phy?
The command buffer is allocated via dma_alloc_attrs() with
DMA_ATTR_WRITE_COMBINE, which provides coherent memory. The DMA API strictly
forbids using streaming sync functions on coherent or write-combined memory,
which can cause kernel panics or warnings when CONFIG_DMA_API_DEBUG is enabled.
Additionally, the sync size is hardcoded to PAGE_SIZE (4096 bytes), but
RGA3_CMDBUF_SIZE is only 184 bytes. Could this cause an out-of-bounds cache
sync and corrupt memory if SWIOTLB bounce buffers are used? Should a standard
memory write barrier (wmb()) be used instead?
> + /* set to master mode and start the conversion */
> + rga_write(rga, RGA3_SYS_CTRL,
> + FIELD_PREP(RGA3_CMD_MODE, RGA3_CMD_MODE_MASTER));
> + rga_write(rga, RGA3_INT_EN, FIELD_PREP(RGA3_INT_FRM_DONE, 1));
Are hardware error interrupts masked here?
RGA3_INT_EN is programmed with only RGA3_INT_FRM_DONE. If the hardware
encounters an error (like RGA3_INT_WR_BUS_ERR), it will halt but fail to
assert the interrupt. This would leave the V4L2 m2m framework waiting
indefinitely for buffer completion.
> + rga_write(rga, RGA3_CMD_CTRL,
> + FIELD_PREP(RGA3_CMD_LINE_START_PULSE, 1));
> +}
> +
> +static bool rga3_handle_irq(struct rockchip_rga *rga)
> +{
> + u32 intr;
> +
> + intr = rga_read(rga, RGA3_INT_RAW);
Is it possible for this register read to occur while the device is
runtime-suspended?
Since RGA3 sets .has_internal_iommu to false, the interrupt is requested with
IRQF_SHARED. If another device on the shared line triggers an interrupt while
the RGA3 clocks are disabled, this register read may return 0xffffffff.
Since FIELD_GET(RGA3_INT_FRM_DONE, 0xffffffff) is true, the ISR would process
a spurious frame. When no job is active, rga->curr is NULL, which could lead
to a crash when dereferencing the context.
To comply with PM Runtime guidelines, should the IRQ handler use
pm_runtime_get_if_active() before accessing registers, and should the driver
call synchronize_irq() in the runtime suspend callback?
> + /* clear all interrupts */
> + rga_write(rga, RGA3_INT_CLR, intr);
> +
> + return FIELD_GET(RGA3_INT_FRM_DONE, intr);
> +}
If error interrupts are enabled to prevent job hangs, would returning false
here cause the core ISR to ignore the error interrupt instead of aborting the
job?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=27
^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [PATCH v6 17/28] media: rockchip: rga: check scaling factor
2026-05-15 15:32 ` [PATCH v6 17/28] media: rockchip: rga: check scaling factor Sven Püschel
@ 2026-05-15 16:54 ` sashiko-bot
0 siblings, 0 replies; 44+ messages in thread
From: sashiko-bot @ 2026-05-15 16:54 UTC (permalink / raw)
To: Sven Püschel; +Cc: krzk+dt, robh, devicetree, conor+dt
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Data race and torn reads in vidioc_s_selection due to missing spinlock protection.
- [High] Concurrency between V4L2 controls and IOCTLs bypasses hardware scaling limits, allowing device hangs.
- [Medium] vidioc_s_selection violates V4L2 API contract by returning an error instead of adjusting the rectangle.
--
commit 807ac9fa5320ff60d938b4d5476ad5199ff6b979
Author: Sven Püschel <s.pueschel@pengutronix.de>
media: rockchip: rga: check scaling factor
This commit introduces checks for the scaling factor to avoid potential hangs
in the RGA hardware when the maximum scaling limit is exceeded. It adds
checks in prepare_streaming, vidioc_s_selection, and when setting the
rotation control.
[ ... ]
> diff --git a/drivers/media/platform/rockchip/rga/rga.c b/drivers/media/platform/rockchip/rga/rga.c
> index 394b14b9469df..22954bbae55fc 100644
> --- a/drivers/media/platform/rockchip/rga/rga.c
> +++ b/drivers/media/platform/rockchip/rga/rga.c
> @@ -127,7 +127,9 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl)
> {
> struct rga_ctx *ctx = container_of(ctrl->handler, struct rga_ctx,
> ctrl_handler);
> + const struct rga_hw *hw = ctx->rga->hw;
> unsigned long flags;
> + int ret = 0;
>
> spin_lock_irqsave(&ctx->rga->ctrl_lock, flags);
> switch (ctrl->id) {
> @@ -138,6 +140,13 @@ static int rga_s_ctrl(struct v4l2_ctrl *ctrl)
> ctx->vflip = ctrl->val;
> break;
> case V4L2_CID_ROTATE:
> + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) &&
> + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) {
> + ret = rga_check_scaling(hw, &ctx->in.crop,
> + &ctx->out.crop, ctrl->val);
Can this validation be bypassed due to concurrent calls?
rga_s_ctrl() operates under the V4L2 control lock and ctx->rga->ctrl_lock,
but vidioc_s_selection() operates under the global rga->mutex. If Thread A
calls VIDIOC_S_SELECTION and Thread B calls VIDIOC_S_CTRL concurrently, could
they both independently validate their new states against the old state,
resulting in a combined crop and rotation that exceeds the hardware scaling
limit?
Also, if Thread A is in the middle of preparing a stream and q->streaming is
still 0, could Thread B invoke S_CTRL, see that vb2_is_streaming() is false,
and apply an invalid rotation without validation?
> + if (ret < 0)
> + goto s_ctrl_done;
> + }
> ctx->rotate = ctrl->val;
> break;
[ ... ]
> @@ -569,10 +611,25 @@ static int vidioc_s_selection(struct file *file, void *priv,
> return -EINVAL;
> }
>
> + if (vb2_is_streaming(v4l2_m2m_get_dst_vq(ctx->fh.m2m_ctx)) &&
> + vb2_is_streaming(v4l2_m2m_get_src_vq(ctx->fh.m2m_ctx))) {
> + int ret = 0;
> +
> + if (V4L2_TYPE_IS_OUTPUT(s->type))
> + ret = rga_check_scaling(rga->hw, &s->r, &ctx->out.crop,
> + ctx->rotate);
> + else
> + ret = rga_check_scaling(rga->hw, &ctx->in.crop, &s->r,
> + ctx->rotate);
> +
> + if (ret < 0)
> + return ret;
Does this violate the V4L2 API specification for VIDIOC_S_SELECTION?
The V4L2 specification requires drivers to adjust the requested rectangle
coordinates and sizes to the closest possible valid values rather than
returning an error code when hardware constraints are violated. Could
returning -EINVAL here break userspace applications that rely on standard
format negotiation?
> + }
> +
> f->crop = s->r;
> ctx->cmdbuf_dirty = true;
Can this cause a data race resulting in torn reads?
This updates f->crop, which is a 16-byte struct v4l2_rect, and
ctx->cmdbuf_dirty without holding ctx->rga->ctrl_lock. Since the driver
permits selection changes while streaming, can the M2M worker thread read
these crop rectangles concurrently during rga_hw_setup_cmdbuf() while
holding ctrl_lock?
If the assignment is not atomic, could the worker thread read a torn struct
(such as the new width but the old height) and program the DMA engine with
invalid coordinates, potentially leading to out-of-bounds memory accesses or
device hangs?
>
> - return ret;
> + return 0;
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260515-spu-rga3-v6-0-e547152eb9c9@pengutronix.de?part=17
^ permalink raw reply [flat|nested] 44+ messages in thread
end of thread, other threads:[~2026-05-15 16:54 UTC | newest]
Thread overview: 44+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-15 15:32 [PATCH v6 00/28] media: platform: rga: Add RGA3 support Sven Püschel
2026-05-15 15:32 ` [PATCH v6 01/28] media: dt-bindings: media: rockchip-rga: add rockchip,rk3588-rga3 Sven Püschel
2026-05-15 15:32 ` [PATCH v6 02/28] media: v4l2-common: sort RGB formats in v4l2_format_info Sven Püschel
2026-05-15 15:32 ` [PATCH v6 03/28] media: v4l2-common: add missing 1 and 2 byte RGB formats to v4l2_format_info Sven Püschel
2026-05-15 15:32 ` [PATCH v6 04/28] media: v4l2-common: add has_alpha " Sven Püschel
2026-05-15 15:32 ` [PATCH v6 05/28] media: v4l2-common: add v4l2_fill_pixfmt_mp_aligned helper Sven Püschel
2026-05-15 15:58 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 06/28] media: rockchip: rga: fix too small buffer size Sven Püschel
2026-05-15 15:32 ` [PATCH v6 07/28] media: rockchip: rga: use clk_bulk api Sven Püschel
2026-05-15 15:54 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 08/28] media: rockchip: rga: use stride for offset calculation Sven Püschel
2026-05-15 15:32 ` [PATCH v6 09/28] media: rockchip: rga: remove redundant rga_frame variables Sven Püschel
2026-05-15 15:32 ` [PATCH v6 10/28] media: rockchip: rga: announce and sync colorimetry Sven Püschel
2026-05-15 16:14 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 11/28] media: rockchip: rga: move hw specific parts to a dedicated struct Sven Püschel
2026-05-15 16:05 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 12/28] media: rockchip: rga: avoid odd frame sizes for YUV formats Sven Püschel
2026-05-15 15:32 ` [PATCH v6 13/28] media: rockchip: rga: calculate x_div/y_div using v4l2_format_info Sven Püschel
2026-05-15 15:32 ` [PATCH v6 14/28] media: rockchip: rga: move cmdbuf to rga_ctx Sven Püschel
2026-05-15 16:12 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 15/28] media: rockchip: rga: align stride to 4 bytes Sven Püschel
2026-05-15 16:17 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 16/28] media: rockchip: rga: reuse cmdbuf contents Sven Püschel
2026-05-15 15:59 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 17/28] media: rockchip: rga: check scaling factor Sven Püschel
2026-05-15 16:54 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 18/28] media: rockchip: rga: use card type to specify rga type Sven Püschel
2026-05-15 16:00 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 19/28] media: rockchip: rga: change offset to dma_addresses Sven Püschel
2026-05-15 15:59 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 20/28] media: rockchip: rga: support external iommus Sven Püschel
2026-05-15 16:08 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 21/28] media: rockchip: rga: share the interrupt when an external iommu is used Sven Püschel
2026-05-15 16:11 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 22/28] media: rockchip: rga: remove size from rga_frame Sven Püschel
2026-05-15 16:21 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 23/28] media: rockchip: rga: remove stride " Sven Püschel
2026-05-15 15:32 ` [PATCH v6 24/28] media: rockchip: rga: move rga_fmt to rga-hw.h Sven Püschel
2026-05-15 15:32 ` [PATCH v6 25/28] media: rockchip: rga: add feature flags Sven Püschel
2026-05-15 16:22 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 26/28] media: rockchip: rga: disable multi-core support Sven Püschel
2026-05-15 15:32 ` [PATCH v6 27/28] media: rockchip: rga: add rga3 support Sven Püschel
2026-05-15 16:34 ` sashiko-bot
2026-05-15 15:32 ` [PATCH v6 28/28] arm64: dts: rockchip: add rga3 dt nodes Sven Püschel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox