[PATCH RFC 1/1] drivers: media: virtio: Support virtio objects in virtio-video driver

Keiichi Watanabe keiichiw at chromium.org
Wed Mar 25 10:40:39 UTC 2020


This patch makes the virtio-video driver use virtio objects as DMA buffers.
So, users will beable to import resources exported by other virtio devices
such as virtio-gpu.

Currently, we assumes that only one virtio object for each v4l2_buffer
format even if it's for a multiplanar format.

Signed-off-by: Keiichi Watanabe <keiichiw at chromium.org>
---
 drivers/media/virtio/virtio_video.h        |  26 +++-
 drivers/media/virtio/virtio_video_dec.c    |   1 +
 drivers/media/virtio/virtio_video_device.c | 131 ++++++++++++++++++++-
 drivers/media/virtio/virtio_video_driver.c |  25 +++-
 drivers/media/virtio/virtio_video_vq.c     |  81 ++++++++++---
 include/uapi/linux/virtio_video.h          |  15 ++-
 6 files changed, 243 insertions(+), 36 deletions(-)

diff --git a/drivers/media/virtio/virtio_video.h b/drivers/media/virtio/virtio_video.h
index c5a5704326c0..b74e5a06753f 100644
--- a/drivers/media/virtio/virtio_video.h
+++ b/drivers/media/virtio/virtio_video.h
@@ -25,6 +25,7 @@
 #include <linux/virtio_config.h>
 #include <linux/virtio_video.h>
 #include <linux/list.h>
+#include <media/videobuf2-core.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-mem2mem.h>
 #include <media/v4l2-ctrls.h>
@@ -123,10 +124,17 @@ struct virtio_video_queue {
 	struct work_struct dequeue_work;
 };

+enum virtio_video_resource_type {
+	RESOURCE_TYPE_GUEST_PAGES = 0,
+	RESOURCE_TYPE_VIRTIO_OBJECT,
+};
+
 struct virtio_video {
 	struct v4l2_device v4l2_dev;
 	int instance;

+	enum virtio_video_resource_type res_type;
+
 	struct virtio_device *vdev;
 	struct virtio_video_queue commandq;
 	struct virtio_video_queue eventq;
@@ -207,6 +215,9 @@ struct virtio_video_buffer {
 	struct v4l2_m2m_buffer v4l2_m2m_vb;
 	uint32_t resource_id;
 	bool queued;
+
+	/* Only for virtio object buffer */
+	uuid_t uuid;
 };

 static inline gfp_t
@@ -300,12 +311,14 @@ int virtio_video_cmd_stream_create(struct virtio_video *vv, uint32_t stream_id,
 int virtio_video_cmd_stream_destroy(struct virtio_video *vv,
 				    uint32_t stream_id);
 int virtio_video_cmd_stream_drain(struct virtio_video *vv, uint32_t stream_id);
-int virtio_video_cmd_resource_create(struct virtio_video *vv,
-				     uint32_t stream_id, uint32_t resource_id,
-				     uint32_t queue_type,
-				     struct virtio_video_mem_entry *ents,
-				    unsigned int num_planes,
-				     unsigned int *num_entry);
+int virtio_video_cmd_resource_create_page(
+	struct virtio_video *vv, uint32_t stream_id, uint32_t resource_id,
+	uint32_t queue_type, unsigned int num_planes, unsigned int *num_entries,
+	struct virtio_video_mem_entry *ents);
+int virtio_video_cmd_resource_create_object(
+	struct virtio_video *vv, uint32_t stream_id, uint32_t resource_id,
+	uint32_t queue_type, unsigned int num_planes, struct vb2_plane *planes,
+	struct virtio_video_object_entry *ents);
 int virtio_video_cmd_resource_destroy_all(struct virtio_video *vv,
 					  struct virtio_video_stream *stream,
 					  uint32_t queue_type);
@@ -354,6 +367,7 @@ void virtio_video_mark_drain_complete(struct virtio_video_stream *stream,
 int virtio_video_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
 			     unsigned int *num_planes, unsigned int sizes[],
 			     struct device *alloc_devs[]);
+int virtio_video_buf_prepare(struct vb2_buffer *vb);
 int virtio_video_buf_init(struct vb2_buffer *vb);
 void virtio_video_buf_cleanup(struct vb2_buffer *vb);
 int virtio_video_querycap(struct file *file, void *fh,
diff --git a/drivers/media/virtio/virtio_video_dec.c b/drivers/media/virtio/virtio_video_dec.c
index 4b85337bb9f3..5d763191f436 100644
--- a/drivers/media/virtio/virtio_video_dec.c
+++ b/drivers/media/virtio/virtio_video_dec.c
@@ -122,6 +122,7 @@ static void virtio_video_dec_stop_streaming(struct vb2_queue *vq)
 static const struct vb2_ops virtio_video_dec_qops = {
 	.queue_setup	 = virtio_video_queue_setup,
 	.buf_init	 = virtio_video_buf_init,
+	.buf_prepare	 = virtio_video_buf_prepare,
 	.buf_cleanup	 = virtio_video_buf_cleanup,
 	.buf_queue	 = virtio_video_dec_buf_queue,
 	.start_streaming = virtio_video_dec_start_streaming,
diff --git a/drivers/media/virtio/virtio_video_device.c b/drivers/media/virtio/virtio_video_device.c
index 8e6bc317339b..e91107082d97 100644
--- a/drivers/media/virtio/virtio_video_device.c
+++ b/drivers/media/virtio/virtio_video_device.c
@@ -17,6 +17,7 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */

+#include <linux/dma-buf.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-ioctl.h>
 #include <media/videobuf2-dma-sg.h>
@@ -49,7 +50,47 @@ int virtio_video_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers,
 	return 0;
 }

-int virtio_video_buf_init(struct vb2_buffer *vb)
+static int virtio_video_get_dma_buf_id(struct virtio_video_device *vvd,
+			  struct vb2_buffer *vb, uuid_t *uuid)
+{
+	/**
+	 * For multiplanar formats, we assume all planes are on one DMA buffer.
+	 */
+	struct dma_buf *dma_buf = dma_buf_get(vb->planes[0].m.fd);
+
+	return dma_buf_get_uuid(dma_buf, uuid);
+}
+
+static int virtio_video_send_resource_create_object(struct vb2_buffer *vb,
+						    uint32_t resource_id,
+						    uuid_t uuid)
+{
+	struct virtio_video_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+	struct virtio_video *vv = to_virtio_vd(stream->video_dev)->vv;
+	struct virtio_video_object_entry *ent;
+	int queue_type;
+	int ret;
+
+	if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type))
+		queue_type = VIRTIO_VIDEO_QUEUE_TYPE_INPUT;
+	else
+		queue_type = VIRTIO_VIDEO_QUEUE_TYPE_OUTPUT;
+
+	ent = kcalloc(1, sizeof(*ent), GFP_KERNEL);
+	uuid_copy((uuid_t *) &ent->uuid, &uuid);
+
+	ret = virtio_video_cmd_resource_create_object(vv, stream->stream_id,
+						      resource_id,
+						      queue_type,
+						      vb->num_planes,
+						      vb->planes, ent);
+	if (ret)
+		kfree(ent);
+
+	return ret;
+}
+
+static int virtio_video_buf_init_guest_pages(struct vb2_buffer *vb)
 {
 	int ret = 0;
 	unsigned int i, j;
@@ -109,11 +150,10 @@ int virtio_video_buf_init(struct vb2_buffer *vb)
 					ents[i].addr, ents[i].length);
 	}

-	ret = virtio_video_cmd_resource_create(vv, stream->stream_id,
-					       resource_id,
-					       to_virtio_queue_type(queue_type),
-					       ents, vb->num_planes,
-					       num_ents);
+	ret = virtio_video_cmd_resource_create_page(
+		vv, stream->stream_id, resource_id,
+		to_virtio_queue_type(queue_type), vb->num_planes, num_ents,
+		ents);
 	if (ret) {
 		virtio_video_resource_id_put(vvd->vv, resource_id);
 		kfree(ents);
@@ -127,6 +167,85 @@ int virtio_video_buf_init(struct vb2_buffer *vb)
 	return 0;
 }

+static int virtio_video_buf_init_virtio_object(struct vb2_buffer *vb)
+{
+	struct virtio_video_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+	struct virtio_video_buffer *virtio_vb = to_virtio_vb(vb);
+	struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+	struct virtio_video *vv = vvd->vv;
+	int ret;
+	uint32_t resource_id;
+	uuid_t uuid;
+
+	ret = virtio_video_get_dma_buf_id(vvd, vb, &uuid);
+	if (ret) {
+		v4l2_err(&vv->v4l2_dev, "failed to get DMA-buf handle");
+		return ret;
+	}
+	virtio_video_resource_id_get(vv, &resource_id);
+
+	ret = virtio_video_send_resource_create_object(vb, resource_id, uuid);
+	if (ret) {
+		virtio_video_resource_id_put(vv, resource_id);
+		return ret;
+	}
+
+	virtio_vb->queued = false;
+	virtio_vb->resource_id = resource_id;
+	virtio_vb->uuid = uuid;
+
+	return 0;
+}
+
+int virtio_video_buf_init(struct vb2_buffer *vb)
+{
+	struct virtio_video_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+	struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+	struct virtio_video *vv = vvd->vv;
+
+	switch (vv->res_type) {
+	case RESOURCE_TYPE_GUEST_PAGES:
+		return virtio_video_buf_init_guest_pages(vb);
+	case RESOURCE_TYPE_VIRTIO_OBJECT:
+		return virtio_video_buf_init_virtio_object(vb);
+	default:
+		return -EINVAL;
+	}
+}
+
+int virtio_video_buf_prepare(struct vb2_buffer *vb)
+{
+	struct virtio_video_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
+	struct virtio_video_buffer *virtio_vb = to_virtio_vb(vb);
+	struct virtio_video_device *vvd = to_virtio_vd(stream->video_dev);
+	struct virtio_video *vv = vvd->vv;
+	uuid_t uuid;
+	int ret;
+
+	if (vv->res_type != RESOURCE_TYPE_VIRTIO_OBJECT)
+		return 0;
+
+	ret = virtio_video_get_dma_buf_id(vvd, vb, &uuid);
+	if (ret) {
+		v4l2_err(&vv->v4l2_dev, "failed to get DMA-buf handle");
+		return ret;
+	}
+
+	/**
+	 * If a user gave a different object as a buffer from the previous
+	 * one, send RESOURCE_CREATE again to register the object.
+	 */
+	if (!uuid_equal(&uuid, &virtio_vb->uuid)) {
+		ret = virtio_video_send_resource_create_object(
+			vb, virtio_vb->resource_id, uuid);
+		if (ret)
+			return ret;
+		virtio_vb->uuid = uuid;
+	}
+
+	return ret;
+}
+
 void virtio_video_buf_cleanup(struct vb2_buffer *vb)
 {
 	struct virtio_video_stream *stream = vb2_get_drv_priv(vb->vb2_queue);
diff --git a/drivers/media/virtio/virtio_video_driver.c b/drivers/media/virtio/virtio_video_driver.c
index 2d4fd7671f4f..f01b744587c8 100644
--- a/drivers/media/virtio/virtio_video_driver.c
+++ b/drivers/media/virtio/virtio_video_driver.c
@@ -171,16 +171,27 @@ static int virtio_video_probe(struct virtio_device *vdev)
 		virtio_video_cmd_ack,
 		virtio_video_event_ack
 	};
-
-	if (!virtio_has_feature(vdev, VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES)) {
-		dev_err(dev, "device must support guest allocated buffers\n");
-		return -ENODEV;
-	}
-
 	vv = devm_kzalloc(dev, sizeof(*vv), GFP_KERNEL);
 	if (!vv)
 		return -ENOMEM;

+	/**
+	 * RESOURCE_GUEST_PAGES is prioritized when both resource type is
+	 * supported.
+	 * TODO: Can we provide users with a way of specifying a
+	 *  resource type when both are supported?
+	 */
+	if (virtio_has_feature(vdev, VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES)) {
+		vv->res_type = RESOURCE_TYPE_GUEST_PAGES;
+	} else if (virtio_has_feature(vdev,
+				      VIRTIO_VIDEO_F_RESOURCE_VIRTIO_OBJECT)) {
+		vv->res_type = RESOURCE_TYPE_VIRTIO_OBJECT;
+	} else {
+		dev_err(dev, "device must support guest allocated buffers or virtio objects\n");
+		ret = -ENODEV;
+		goto err_res_type;
+	}
+
 	vv->vdev = vdev;
 	vv->debug = debug;
 	vv->use_dma_mem = use_dma_mem;
@@ -267,6 +278,7 @@ static int virtio_video_probe(struct virtio_device *vdev)
 err_vqs:
 	v4l2_device_unregister(&vv->v4l2_dev);
 err_v4l2_reg:
+err_res_type:
 	devm_kfree(&vdev->dev, vv);

 	return ret;
@@ -292,6 +304,7 @@ static struct virtio_device_id id_table[] = {
 static unsigned int features[] = {
 	VIRTIO_VIDEO_F_RESOURCE_GUEST_PAGES,
 	VIRTIO_VIDEO_F_RESOURCE_NON_CONTIG,
+	VIRTIO_VIDEO_F_RESOURCE_VIRTIO_OBJECT,
 };

 static struct virtio_driver virtio_video_driver = {
diff --git a/drivers/media/virtio/virtio_video_vq.c b/drivers/media/virtio/virtio_video_vq.c
index 4679e6b49cf3..dc5e1b7a320e 100644
--- a/drivers/media/virtio/virtio_video_vq.c
+++ b/drivers/media/virtio/virtio_video_vq.c
@@ -411,6 +411,18 @@ int virtio_video_cmd_stream_create(struct virtio_video *vv, uint32_t stream_id,
 {
 	struct virtio_video_stream_create *req_p;
 	struct virtio_video_vbuffer *vbuf;
+	int resource_type;
+
+	switch (vv->res_type) {
+	case RESOURCE_TYPE_GUEST_PAGES:
+		resource_type = VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES;
+		break;
+	case RESOURCE_TYPE_VIRTIO_OBJECT:
+		resource_type = VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT;
+		break;
+	default:
+		return -EINVAL;
+	}

 	req_p = virtio_video_alloc_req(vv, &vbuf, sizeof(*req_p));
 	if (IS_ERR(req_p))
@@ -418,9 +430,10 @@ int virtio_video_cmd_stream_create(struct virtio_video *vv, uint32_t stream_id,

 	req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_STREAM_CREATE);
 	req_p->hdr.stream_id = cpu_to_le32(stream_id);
-	req_p->in_mem_type = cpu_to_le32(VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES);
-	req_p->out_mem_type = cpu_to_le32(VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES);
 	req_p->coded_format = cpu_to_le32(format);
+	req_p->in_mem_type = cpu_to_le32(resource_type);
+	req_p->out_mem_type = cpu_to_le32(resource_type);
+
 	strncpy(req_p->tag, tag, sizeof(req_p->tag) - 1);
 	req_p->tag[sizeof(req_p->tag) - 1] = 0;

@@ -457,30 +470,38 @@ int virtio_video_cmd_stream_drain(struct virtio_video *vv, uint32_t stream_id)
 	return virtio_video_queue_cmd_buffer(vv, vbuf);
 }

-int virtio_video_cmd_resource_create(struct virtio_video *vv,
-				     uint32_t stream_id, uint32_t resource_id,
-				     uint32_t queue_type,
-				     struct virtio_video_mem_entry *ents,
-				     unsigned int num_planes,
-				     unsigned int *num_entry)
+static void virtio_video_cmd_resource_create_core(
+	struct virtio_video *vv, struct virtio_video_resource_create *req_p,
+	uint32_t stream_id, uint32_t resource_id, uint32_t queue_type,
+	unsigned int num_planes)
+{
+	req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_RESOURCE_CREATE);
+	req_p->hdr.stream_id = cpu_to_le32(stream_id);
+	req_p->resource_id = cpu_to_le32(resource_id);
+	req_p->queue_type = cpu_to_le32(queue_type);
+	req_p->num_planes = cpu_to_le32(num_planes);
+}
+
+int virtio_video_cmd_resource_create_page(
+	struct virtio_video *vv, uint32_t stream_id, uint32_t resource_id,
+	uint32_t queue_type, unsigned int num_planes, unsigned int *num_entries,
+	struct virtio_video_mem_entry *ents)
 {
-	unsigned int i = 0, nents = 0;
 	struct virtio_video_resource_create *req_p;
 	struct virtio_video_vbuffer *vbuf;
+	unsigned int nents = 0;
+	int i;

 	req_p = virtio_video_alloc_req(vv, &vbuf, sizeof(*req_p));
 	if (IS_ERR(req_p))
 		return PTR_ERR(req_p);

-	req_p->hdr.type = cpu_to_le32(VIRTIO_VIDEO_CMD_RESOURCE_CREATE);
-	req_p->hdr.stream_id = cpu_to_le32(stream_id);
-	req_p->resource_id = cpu_to_le32(resource_id);
-	req_p->queue_type = cpu_to_le32(queue_type);
-	 req_p->num_planes = cpu_to_le32(num_planes);
+	virtio_video_cmd_resource_create_core(vv, req_p, stream_id, resource_id,
+					      queue_type, num_planes);

 	for (i = 0; i < num_planes; i++) {
-		nents += num_entry[i];
-		req_p->num_entries[i] = cpu_to_le32(num_entry[i]);
+		nents += num_entries[i];
+		req_p->num_entries[i] = cpu_to_le32(num_entries[i]);
 	}

 	vbuf->data_buf = ents;
@@ -489,6 +510,33 @@ int virtio_video_cmd_resource_create(struct virtio_video *vv,
 	return virtio_video_queue_cmd_buffer(vv, vbuf);
 }

+int virtio_video_cmd_resource_create_object(
+	struct virtio_video *vv, uint32_t stream_id, uint32_t resource_id,
+	uint32_t queue_type, unsigned int num_planes, struct vb2_plane *planes,
+	struct virtio_video_object_entry *ents)
+{
+	struct virtio_video_resource_create *req_p;
+	struct virtio_video_vbuffer *vbuf;
+	int i;
+
+	req_p = virtio_video_alloc_req(vv, &vbuf, sizeof(*req_p));
+	if (IS_ERR(req_p))
+		return PTR_ERR(req_p);
+
+	virtio_video_cmd_resource_create_core(vv, req_p, stream_id, resource_id,
+					      queue_type, num_planes);
+
+	req_p->planes_layout =
+		cpu_to_le32(VIRTIO_VIDEO_PLANES_LAYOUT_SINGLE_BUFFER);
+	for (i = 0; i < num_planes; i++)
+		req_p->plane_offsets[i] = planes[i].data_offset;
+
+	vbuf->data_buf = ents;
+	vbuf->data_size = sizeof(*ents);
+
+	return virtio_video_queue_cmd_buffer(vv, vbuf);
+}
+
 int virtio_video_cmd_resource_destroy_all(struct virtio_video *vv,
 					  struct virtio_video_stream *stream,
 					  enum virtio_video_queue_type qtype)
@@ -1009,4 +1057,3 @@ int virtio_video_cmd_set_control(struct virtio_video *vv, uint32_t stream_id,

 	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 0dd98a2237c6..f7b3f94f9dbb 100644
--- a/include/uapi/linux/virtio_video.h
+++ b/include/uapi/linux/virtio_video.h
@@ -49,6 +49,8 @@
  * scatter-gather lists.
  */
 #define VIRTIO_VIDEO_F_RESOURCE_NON_CONTIG 1
+/* Objects exported by another virtio device can be used for video buffers */
+#define VIRTIO_VIDEO_F_RESOURCE_VIRTIO_OBJECT 2

 /*
  * Image formats
@@ -240,6 +242,7 @@ struct virtio_video_query_capability_resp {
 /* VIRTIO_VIDEO_CMD_STREAM_CREATE */
 enum virtio_video_mem_type {
 	VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES,
+	VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT,
 };

 struct virtio_video_stream_create {
@@ -268,6 +271,10 @@ struct virtio_video_mem_entry {
 	__u8 padding[4];
 };

+struct virtio_video_object_entry {
+	__u8 uuid[16];
+};
+
 #define VIRTIO_VIDEO_MAX_PLANES 8

 struct virtio_video_resource_create {
@@ -278,7 +285,13 @@ struct virtio_video_resource_create {
 	__le32 num_planes;
 	__le32 plane_offsets[VIRTIO_VIDEO_MAX_PLANES];
 	__le32 num_entries[VIRTIO_VIDEO_MAX_PLANES];
-	/* Followed by struct virtio_video_mem_entry entries[] */
+	/**
+	 * Followed by either
+	 * - struct virtio_video_mem_entry entries[]
+	 *   for VIRTIO_VIDEO_MEM_TYPE_GUEST_PAGES
+	 * - struct virtio_video_object_entry entries[]
+	 *   for VIRTIO_VIDEO_MEM_TYPE_VIRTIO_OBJECT
+	 */
 };

 /* VIRTIO_VIDEO_CMD_RESOURCE_QUEUE */
--
2.25.1.696.g5e7596f4ac-goog


More information about the dri-devel mailing list