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 <keiic...@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
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to