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);