On Thu May 21 00:44:22 2026 +0200, Sven Püschel wrote:
> 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 <[email protected]>
> Reviewed-by: Nicolas Dufresne <[email protected]>
> Signed-off-by: Nicolas Dufresne <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>
Patch committed.
Thanks,
Hans Verkuil
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 ffc6162b2e68..dcaba66f5c1f 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 567d39e58d33..f2900812ba76 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 c2e34be75193..805ec23e5e3f 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 394b14b9469d..22954bbae55f 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 5360f092fecf..df525c6aea8b 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);
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]