@@ -93,6 +93,79 @@ static int rkvdec_get_sps_attributes(struct rkvdec_ctx *ctx, void *sps,
return 0;
}
+static int rkvdec_set_sps(struct rkvdec_ctx *ctx, struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_pix_format_mplane *pix_mp;
+ struct sps_attributes attributes = {0};
+ void *new_sps = NULL;
+
+ /*
+ * SPS structures are not filled until the control handler is set up
+ */
+ if (!ctx->fh.ctrl_handler)
+ return 0;
+
+ switch (ctrl->id) {
+ case V4L2_CID_STATELESS_H264_SPS:
+ new_sps = (void *)ctrl->p_new.p_h264_sps;
+ break;
+ case V4L2_CID_STATELESS_HEVC_SPS:
+ new_sps = (void *)ctrl->p_new.p_hevc_sps;
+ break;
+ default:
+ dev_err(ctx->dev->dev, "Unsupported stateless control ID: %x\n", ctrl->id);
+ return -EINVAL;
+ };
+ rkvdec_get_sps_attributes(ctx, new_sps, &attributes);
+
+ /*
+ * Providing an empty SPS is valid but we do not store it.
+ */
+ if (attributes.width == 0 && attributes.height == 0)
+ return 0;
+
+ pix_mp = &ctx->decoded_fmt.fmt.pix_mp;
+
+ /*
+ * SPS must match the provided format dimension, if it doesn't userspace has to
+ * first reset the output format
+ */
+ if ((attributes.width > pix_mp->width) || (attributes.height > pix_mp->height)) {
+ dev_err(ctx->dev->dev,
+ "Dimension mismatch. [%s SPS] W: %d, H: %d, [Format] W: %d, H: %d)\n",
+ ctrl->id == V4L2_CID_STATELESS_HEVC_SPS ? "HEVC" : "H264",
+ attributes.width, attributes.height, pix_mp->width, pix_mp->height);
+ return -EINVAL;
+ }
+
+ if (ctx->sps && pix_mp->pixelformat == rkvdec_get_valid_fmt(ctx)) {
+ /*
+ * Userspace is allowed to change the SPS at any point, if the
+ * pixel format doesn't differ from the format in the context,
+ * just accept the change even if buffers are queued
+ */
+ ctx->sps = new_sps;
+ } else {
+ /*
+ * Do not accept changing the SPS, while buffers are queued,
+ * when the new SPS would cause switching the CAPTURE pixel format
+ */
+ if (pix_mp->pixelformat != rkvdec_get_valid_fmt(ctx)) {
+ if (rkvdec_queue_busy(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
+ return -EBUSY;
+ }
+ ctx->sps = new_sps;
+ /*
+ * For the initial SPS setting and when the pixel format is
+ * changed adjust the pixel format stored in the context
+ */
+ pix_mp->pixelformat = rkvdec_get_valid_fmt(ctx);
+ rkvdec_fill_decoded_pixfmt(ctx, pix_mp);
+ }
+
+ return 0;
+}
+
static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
{
struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl);
@@ -104,8 +177,16 @@ static int rkvdec_try_ctrl(struct v4l2_ctrl *ctrl)
return 0;
}
+static int rkvdec_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct rkvdec_ctx *ctx = container_of(ctrl->handler, struct rkvdec_ctx, ctrl_hdl);
+
+ return rkvdec_set_sps(ctx, ctrl);
+}
+
static const struct v4l2_ctrl_ops rkvdec_ctrl_ops = {
.try_ctrl = rkvdec_try_ctrl,
+ .s_ctrl = rkvdec_s_ctrl,
};
static const struct rkvdec_ctrl_desc rkvdec_h264_ctrl_descs[] = {