CHROMIUM: driver: media: virtio: Support prepending SPS/PPS to IDR.

This CL adds support for the V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR
control to the virtio encoder. When this control is enabled SPS and PPS
NAL units are prepended to IDR frames, to improve the resilience of
encoded video streams.

Signed-off-by: David Staessens <dstaessens@google.com>

BUG=b:161495502
TEST=tast run DUT arc.VideoEncodeAccel.h264_192p_i420_vm

Cq-Depend: chromium:3060098
Change-Id: Icfa925e9e463b75aa78c664ec52750959e5be6ea
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3058721
Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org>
Tested-by: David Staessens <dstaessens@chromium.org>
Commit-Queue: David Staessens <dstaessens@chromium.org>
(cherry picked from commit 44afe397a3fc77e313916bf940af307fab61603b)
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/3088815
diff --git a/drivers/media/virtio/virtio_video.h b/drivers/media/virtio/virtio_video.h
index 4b823a2..9e949ee 100644
--- a/drivers/media/virtio/virtio_video.h
+++ b/drivers/media/virtio/virtio_video.h
@@ -94,6 +94,7 @@ struct video_control_info {
 	uint32_t bitrate;
 	uint32_t bitrate_peak;
 	uint32_t bitrate_mode;
+	uint32_t prepend_spspps_to_idr;
 	bool is_updated;
 };
 
diff --git a/drivers/media/virtio/virtio_video_device.c b/drivers/media/virtio/virtio_video_device.c
index 1908a52..1cef520 100644
--- a/drivers/media/virtio/virtio_video_device.c
+++ b/drivers/media/virtio/virtio_video_device.c
@@ -977,6 +977,14 @@ static int virtio_video_device_open(struct file *file)
 			v4l2_err(&vv->v4l2_dev, "failed to get stream bitrate mode\n");
 			goto err_stream_get_params;
 		}
+
+		ret = virtio_video_cmd_get_control(vv, stream,
+						   VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR);
+		if (ret) {
+			v4l2_err(&vv->v4l2_dev,
+				 "failed to get stream prepend SPS/PPS to IDR control\n");
+			goto err_stream_get_params;
+		}
 	}
 
 	mutex_init(&stream->vq_mutex);
diff --git a/drivers/media/virtio/virtio_video_enc.c b/drivers/media/virtio/virtio_video_enc.c
index 9a9499c..34fd14c1 100644
--- a/drivers/media/virtio/virtio_video_enc.c
+++ b/drivers/media/virtio/virtio_video_enc.c
@@ -141,6 +141,10 @@ static int virtio_video_enc_s_ctrl(struct v4l2_ctrl *ctrl)
 		ret = virtio_video_cmd_set_control(vv, stream->stream_id,
 						   control, 1 /*ignored*/);
 		break;
+	case V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR:
+		ret = virtio_video_cmd_set_control(vv, stream->stream_id,
+						   control, ctrl->val);
+		break;
 	default:
 		ret = -EINVAL;
 		break;
@@ -248,6 +252,12 @@ int virtio_video_enc_init_ctrls(struct virtio_video_stream *stream)
 			  V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME,
 			  0, 0, 0, 0);
 
+	v4l2_ctrl_new_std(&stream->ctrl_handler,
+			  &virtio_video_enc_ctrl_ops,
+			  V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR,
+			  0, 1,
+			  1, 0);
+
 	if (stream->ctrl_handler.error)
 		return stream->ctrl_handler.error;
 
diff --git a/drivers/media/virtio/virtio_video_helpers.c b/drivers/media/virtio/virtio_video_helpers.c
index 2a7f30d3..3435ec0 100644
--- a/drivers/media/virtio/virtio_video_helpers.c
+++ b/drivers/media/virtio/virtio_video_helpers.c
@@ -191,6 +191,8 @@ static struct virtio_video_convert_table control_table[] = {
 	{ VIRTIO_VIDEO_CONTROL_LEVEL, V4L2_CID_MPEG_VIDEO_H264_LEVEL },
 	{ VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME,
 			V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME },
+	{ VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR,
+			V4L2_CID_MPEG_VIDEO_PREPEND_SPSPPS_TO_IDR },
 	{ 0 },
 };
 
diff --git a/drivers/media/virtio/virtio_video_vq.c b/drivers/media/virtio/virtio_video_vq.c
index 10194ab..f6b983d 100644
--- a/drivers/media/virtio/virtio_video_vq.c
+++ b/drivers/media/virtio/virtio_video_vq.c
@@ -1036,6 +1036,28 @@ virtio_video_cmd_get_ctrl_bitrate_mode_cb(struct virtio_video *vv,
 	wake_up(&vv->wq);
 }
 
+static void
+virtio_video_cmd_get_ctrl_prepend_spspps_to_idr(struct virtio_video *vv,
+						struct virtio_video_vbuffer *vbuf)
+{
+	struct virtio_video_get_control_resp *resp =
+		(struct virtio_video_get_control_resp *)vbuf->resp_buf;
+	struct virtio_video_control_val_prepend_spspps_to_idr *resp_ps = NULL;
+	struct virtio_video_stream *stream = vbuf->priv;
+	struct video_control_info *control = &stream->control;
+
+	if (!control)
+		return;
+
+	resp_ps = (void *)((char *) resp +
+			   sizeof(struct virtio_video_get_control_resp));
+
+	control->prepend_spspps_to_idr = le32_to_cpu(
+		resp_ps->prepend_spspps_to_idr);
+	control->is_updated = true;
+	wake_up(&vv->wq);
+}
+
 int virtio_video_cmd_get_control(struct virtio_video *vv,
 				 struct virtio_video_stream *stream,
 				 uint32_t virtio_ctrl)
@@ -1071,6 +1093,11 @@ int virtio_video_cmd_get_control(struct virtio_video *vv,
 		resp_size += sizeof(struct virtio_video_control_val_bitrate_mode);
 		cb = &virtio_video_cmd_get_ctrl_bitrate_mode_cb;
 		break;
+	case VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR:
+		resp_size += sizeof(
+			struct virtio_video_control_val_prepend_spspps_to_idr);
+		cb = &virtio_video_cmd_get_ctrl_prepend_spspps_to_idr;
+		break;
 	default:
 		return -1;
 	}
@@ -1112,6 +1139,7 @@ int virtio_video_cmd_set_control(struct virtio_video *vv, uint32_t stream_id,
 	struct virtio_video_control_val_bitrate *ctrl_b = NULL;
 	struct virtio_video_control_val_bitrate_peak *ctrl_bp = NULL;
 	struct virtio_video_control_val_bitrate_mode *ctrl_bm = NULL;
+	struct virtio_video_control_val_prepend_spspps_to_idr *ctrl_ps = NULL;
 	size_t size;
 
 	if (!vv || value == 0)
@@ -1136,6 +1164,9 @@ int virtio_video_cmd_set_control(struct virtio_video *vv, uint32_t stream_id,
 	case VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME:
 		size = 0;
 		break;
+	case VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR:
+		size = sizeof(struct virtio_video_control_val_prepend_spspps_to_idr);
+		break;
 	default:
 		return -1;
 	}
@@ -1177,6 +1208,11 @@ int virtio_video_cmd_set_control(struct virtio_video *vv, uint32_t stream_id,
 	case VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME:
 		// Button controls have no value.
 		break;
+	case VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR:
+		ctrl_ps = (void *)((char *)req_p +
+				  sizeof(struct virtio_video_set_control));
+		ctrl_ps->prepend_spspps_to_idr = cpu_to_le32(value);
+		break;
 	}
 
 	return virtio_video_queue_cmd_buffer(vv, vbuf);
diff --git a/include/uapi/linux/virtio_video.h b/include/uapi/linux/virtio_video.h
index 9a888e0..69e4c68 100644
--- a/include/uapi/linux/virtio_video.h
+++ b/include/uapi/linux/virtio_video.h
@@ -392,6 +392,7 @@ enum virtio_video_control_type {
 	VIRTIO_VIDEO_CONTROL_FORCE_KEYFRAME,
 	VIRTIO_VIDEO_CONTROL_BITRATE_MODE,
 	VIRTIO_VIDEO_CONTROL_BITRATE_PEAK,
+	VIRTIO_VIDEO_CONTROL_PREPEND_SPSPPS_TO_IDR,
 };
 
 struct virtio_video_query_control_profile {
@@ -463,6 +464,11 @@ struct virtio_video_control_val_level {
 	__u8 padding[4];
 };
 
+struct virtio_video_control_val_prepend_spspps_to_idr {
+	__le32 prepend_spspps_to_idr;
+	__u8 padding[4];
+};
+
 struct virtio_video_get_control_resp {
 	struct virtio_video_cmd_hdr hdr;
 	/* Followed by one of struct virtio_video_control_val_* */