diff mbox series

[1/2] virtio: New virtio_gpu driver

Message ID 20240517-virtio_gpu-v1-1-6353b87472c7@flygoat.com
State Superseded
Delegated to: Anatolij Gustschin
Headers show
Series virtio_gpu driver and relevant fix | expand

Commit Message

Jiaxun Yang May 16, 2024, 11:03 p.m. UTC
This driver is implemened based on latest VirtIO spec.
It follows operation prodcure as defined in the spec.

It implemented multihead (mirroring) support as well.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
---
 drivers/virtio/Kconfig         |  29 +++
 drivers/virtio/Makefile        |   1 +
 drivers/virtio/virtio-uclass.c |   1 +
 drivers/virtio/virtio_gpu.c    | 298 ++++++++++++++++++++++++++++
 drivers/virtio/virtio_gpu.h    | 428 +++++++++++++++++++++++++++++++++++++++++
 include/virtio.h               |   4 +-
 6 files changed, 760 insertions(+), 1 deletion(-)

Comments

Heinrich Schuchardt May 17, 2024, 1:56 a.m. UTC | #1
Am 17. Mai 2024 01:03:24 MESZ schrieb Jiaxun Yang <jiaxun.yang@flygoat.com>:
>This driver is implemened based on latest VirtIO spec.
>It follows operation prodcure as defined in the spec.
>
>It implemented multihead (mirroring) support as well.
>
>Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
>---
> drivers/virtio/Kconfig         |  29 +++
> drivers/virtio/Makefile        |   1 +
> drivers/virtio/virtio-uclass.c |   1 +
> drivers/virtio/virtio_gpu.c    | 298 ++++++++++++++++++++++++++++
> drivers/virtio/virtio_gpu.h    | 428 +++++++++++++++++++++++++++++++++++++++++
> include/virtio.h               |   4 +-
> 6 files changed, 760 insertions(+), 1 deletion(-)
>
>diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
>index 1de68867d52e..b75572c4ad13 100644
>--- a/drivers/virtio/Kconfig
>+++ b/drivers/virtio/Kconfig
>@@ -76,4 +76,33 @@ config VIRTIO_RNG
> 	help
> 	  This is the virtual random number generator driver. It can be used
> 	  with QEMU based targets.
>+
>+ config VIRTIO_GPU
>+	bool "virtio GPU driver"
>+	depends on VIRTIO && VIDEO
>+	default y
>+	help
>+	  This is the virtual GPU display for virtio. It can be used with QEMU
>+	  based targets.
>+
>+if VIRTIO_GPU
>+config VIRTIO_GPU_SIZE_X
>+	int "Width of display (X resolution)"
>+	default 1280

1920x1080 would look like a reasonable default for me.


>+	help
>+	  Sets the width of the display.
>+
>+	  These two options control the size of the display set up by QEMU.
>+	  Typical sizes are 1024 x 768 or 1280 x 1024.
>+
>+config VIRTIO_GPU_SIZE_Y
>+	int "High of display (Y resolution)"
>+	default 1024
>+	help
>+	  Sets the height of the display.
>+
>+	  These two options control the size of the display set up by QEMU.
>+	  Typical sizes are 1024 x 768 or 1280 x 1024.

Haven't had such small monitors for a while. Why should this be typical?

Doesn't QEMU allow to read the size of the output window at runtime?

Best regards

Heinrich

>+
>+endif
> endmenu
>diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
>index 4c63a6c69043..c830fb6e6049 100644
>--- a/drivers/virtio/Makefile
>+++ b/drivers/virtio/Makefile
>@@ -11,3 +11,4 @@ obj-$(CONFIG_VIRTIO_SANDBOX) += virtio_sandbox.o
> obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
> obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
> obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
>+obj-$(CONFIG_VIRTIO_GPU) += virtio_gpu.o
>diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
>index 1dbc1a56aa21..1f3cdbf689c4 100644
>--- a/drivers/virtio/virtio-uclass.c
>+++ b/drivers/virtio/virtio-uclass.c
>@@ -30,6 +30,7 @@ static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
> 	[VIRTIO_ID_NET]		= VIRTIO_NET_DRV_NAME,
> 	[VIRTIO_ID_BLOCK]	= VIRTIO_BLK_DRV_NAME,
> 	[VIRTIO_ID_RNG]		= VIRTIO_RNG_DRV_NAME,
>+	[VIRTIO_ID_GPU]		= VIRTIO_GPU_DRV_NAME,
> };
> 
> int virtio_get_config(struct udevice *vdev, unsigned int offset,
>diff --git a/drivers/virtio/virtio_gpu.c b/drivers/virtio/virtio_gpu.c
>new file mode 100644
>index 000000000000..d798562ecba2
>--- /dev/null
>+++ b/drivers/virtio/virtio_gpu.c
>@@ -0,0 +1,298 @@
>+// SPDX-License-Identifier: GPL-2.0+
>+/*
>+ * Copyright (C) 2024, Jiaxun Yang <jiaxun.yang@flygoat.com>
>+ */
>+
>+#define pr_fmt(fmt) "virtio_gpu: " fmt
>+
>+#include <dm.h>
>+#include <log.h>
>+#include <malloc.h>
>+#include <video.h>
>+#include <virtio_types.h>
>+#include <virtio.h>
>+#include <virtio_ring.h>
>+#include "virtio_gpu.h"
>+#include <asm/io.h>
>+
>+struct virtio_gpu_priv {
>+	struct virtqueue *vq;
>+	u32 scanout_res_id;
>+	u64 fence_id;
>+	bool in_sync;
>+};
>+
>+static int virtio_gpu_do_req(struct udevice *dev,
>+			     enum virtio_gpu_ctrl_type type,
>+			     void *in, size_t in_size,
>+			     void *out, size_t out_size, bool flush)
>+{
>+	int ret;
>+	uint len;
>+	struct virtio_gpu_priv *priv = dev_get_priv(dev);
>+	struct virtio_sg in_sg;
>+	struct virtio_sg out_sg;
>+	struct virtio_sg *sgs[] = { &in_sg, &out_sg };
>+	struct virtio_gpu_ctrl_hdr *ctrl_hdr_in = in;
>+	struct virtio_gpu_ctrl_hdr *ctrl_hdr_out = out;
>+
>+	ctrl_hdr_in->type = cpu_to_virtio32(dev, (u32)type);
>+	if (flush) {
>+		ctrl_hdr_in->flags = cpu_to_virtio32(dev, VIRTIO_GPU_FLAG_FENCE);
>+		ctrl_hdr_in->fence_id = cpu_to_virtio64(dev, priv->fence_id++);
>+	} else {
>+		ctrl_hdr_in->flags = 0;
>+		ctrl_hdr_in->fence_id = 0;
>+	}
>+	ctrl_hdr_in->ctx_id = 0;
>+	ctrl_hdr_in->ring_idx = 0;
>+	in_sg.addr = in;
>+	in_sg.length = in_size;
>+	out_sg.addr = out;
>+	out_sg.length = out_size;
>+
>+	ret = virtqueue_add(priv->vq, sgs, 1, 1);
>+	if (ret) {
>+		log_debug("virtqueue_add failed %d\n", ret);
>+		return ret;
>+	}
>+	virtqueue_kick(priv->vq);
>+
>+	debug("wait...");
>+	while (!virtqueue_get_buf(priv->vq, &len))
>+		;
>+	debug("done\n");
>+
>+	if (out_size != len) {
>+		log_debug("Invalid response size %d, expected %d\n",
>+			  len, (uint)out_size);
>+	}
>+
>+	return virtio32_to_cpu(dev, ctrl_hdr_out->type);
>+}
>+
>+static int virtio_gpu_probe(struct udevice *dev)
>+{
>+	struct virtio_gpu_priv *priv = dev_get_priv(dev);
>+	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
>+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
>+	struct virtio_gpu_ctrl_hdr ctrl_hdr_in;
>+	struct virtio_gpu_ctrl_hdr ctrl_hdr_out;
>+	struct virtio_gpu_resp_display_info *disp_info_out;
>+	struct virtio_gpu_display_one *disp;
>+	struct virtio_gpu_resource_create_2d *res_create_2d_in;
>+	void *res_buf_in;
>+	struct virtio_gpu_resource_attach_backing *res_attach_backing_in;
>+	struct virtio_gpu_mem_entry *mem_entry;
>+	struct virtio_gpu_set_scanout *set_scanout_in;
>+	unsigned int scanout_mask = 0;
>+	int ret, i;
>+
>+	if (!plat->base) {
>+		log_warning("No framebuffer allocated\n");
>+		return -EINVAL;
>+	}
>+
>+	ret = virtio_find_vqs(dev, 1, &priv->vq);
>+	if (ret < 0) {
>+		log_warning("virtio_find_vqs failed\n");
>+		return ret;
>+	}
>+
>+	disp_info_out = malloc(sizeof(struct virtio_gpu_resp_display_info));
>+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, &ctrl_hdr_in,
>+				sizeof(struct virtio_gpu_ctrl_hdr), disp_info_out,
>+				sizeof(struct virtio_gpu_resp_display_info), false);
>+
>+	if (ret != VIRTIO_GPU_RESP_OK_DISPLAY_INFO) {
>+		log_warning("CMD_GET_DISPLAY_INFO failed %d\n", ret);
>+		ret = -EINVAL;
>+		goto out_free_disp;
>+	}
>+
>+	for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
>+		disp = &disp_info_out->pmodes[i];
>+		if (!disp->enabled)
>+			continue;
>+		log_debug("Found available scanout: %d\n", i);
>+		scanout_mask |= 1 << i;
>+	}
>+
>+	if (!scanout_mask) {
>+		log_warning("No active scanout found\n");
>+		ret = -EINVAL;
>+		goto out_free_disp;
>+	}
>+
>+	free(disp_info_out);
>+	disp_info_out = NULL;
>+
>+	/* TODO: We can parse EDID for those info */
>+	uc_priv->xsize = CONFIG_VAL(VIRTIO_GPU_SIZE_X);
>+	uc_priv->ysize = CONFIG_VAL(VIRTIO_GPU_SIZE_Y);
>+	uc_priv->bpix = VIDEO_BPP32;
>+
>+	priv->scanout_res_id = 1;
>+	res_create_2d_in = malloc(sizeof(struct virtio_gpu_resource_create_2d));
>+	res_create_2d_in->resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
>+	res_create_2d_in->format = cpu_to_virtio32(dev, VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
>+	res_create_2d_in->width = cpu_to_virtio32(dev, uc_priv->xsize);
>+	res_create_2d_in->height = cpu_to_virtio32(dev, uc_priv->ysize);
>+
>+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, res_create_2d_in,
>+				sizeof(struct virtio_gpu_resource_create_2d), &ctrl_hdr_out,
>+				sizeof(struct virtio_gpu_ctrl_hdr), false);
>+	if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
>+		log_warning("CMD_RESOURCE_CREATE_2D failed %d\n", ret);
>+		ret = -EINVAL;
>+		goto out_free_res_create_2d;
>+	}
>+
>+	free(res_create_2d_in);
>+	res_create_2d_in = NULL;
>+
>+	res_buf_in = malloc(sizeof(struct virtio_gpu_resource_attach_backing) +
>+			    sizeof(struct virtio_gpu_mem_entry));
>+	res_attach_backing_in = res_buf_in;
>+	mem_entry = res_buf_in + sizeof(struct virtio_gpu_resource_attach_backing);
>+	res_attach_backing_in->resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
>+	res_attach_backing_in->nr_entries = cpu_to_virtio32(dev, 1);
>+	mem_entry->addr = cpu_to_virtio64(dev, virt_to_phys((void *)plat->base));
>+	mem_entry->length = cpu_to_virtio32(dev, plat->size);
>+	mem_entry->padding = 0;
>+
>+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, res_buf_in,
>+				sizeof(struct virtio_gpu_resource_attach_backing) +
>+				sizeof(struct virtio_gpu_mem_entry), &ctrl_hdr_out,
>+				sizeof(struct virtio_gpu_ctrl_hdr), false);
>+
>+	if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
>+		log_warning("CMD_RESOURCE_ATTACH_BACKING failed %d\n", ret);
>+		ret = -EINVAL;
>+		goto out_free_res_buf;
>+	}
>+	free(res_buf_in);
>+	res_buf_in = NULL;
>+
>+	set_scanout_in = malloc(sizeof(struct virtio_gpu_set_scanout));
>+	while (scanout_mask) {
>+		u32 active_scanout = ffs(scanout_mask) - 1;
>+
>+		set_scanout_in->r.x = 0;
>+		set_scanout_in->r.y = 0;
>+		set_scanout_in->r.width = cpu_to_virtio32(dev, uc_priv->xsize);
>+		set_scanout_in->r.height = cpu_to_virtio32(dev, uc_priv->ysize);
>+		set_scanout_in->scanout_id = cpu_to_virtio32(dev, active_scanout);
>+		set_scanout_in->resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
>+
>+		ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_SET_SCANOUT, set_scanout_in,
>+					sizeof(struct virtio_gpu_set_scanout), &ctrl_hdr_out,
>+					sizeof(struct virtio_gpu_ctrl_hdr), false);
>+
>+		if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
>+			log_warning("CMD_SET_SCANOUT failed %d for scanout %d\n",
>+				    ret, active_scanout);
>+			ret = -EINVAL;
>+			goto out_free_set_scanout;
>+		}
>+		scanout_mask &= ~(1 << active_scanout);
>+	}
>+	free(set_scanout_in);
>+	set_scanout_in = NULL;
>+
>+	return 0;
>+out_free_set_scanout:
>+	if (set_scanout_in)
>+		free(set_scanout_in);
>+out_free_res_buf:
>+	if (res_buf_in)
>+		free(res_buf_in);
>+out_free_res_create_2d:
>+	if (res_create_2d_in)
>+		free(res_create_2d_in);
>+out_free_disp:
>+	if (disp_info_out)
>+		free(disp_info_out);
>+	return ret;
>+}
>+
>+static int virtio_gpu_bind(struct udevice *dev)
>+{
>+	struct virtio_dev_priv *virtio_uc_priv = dev_get_uclass_priv(dev->parent);
>+	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
>+
>+	/* Indicate what driver features we support */
>+	virtio_driver_features_init(virtio_uc_priv, NULL, 0, NULL, 0);
>+	plat->base = 0; /* Framebuffer will be allocated by the video-uclass */
>+	plat->size = CONFIG_VAL(VIRTIO_GPU_SIZE_X) *
>+		     CONFIG_VAL(VIRTIO_GPU_SIZE_X) * VNBYTES(VIDEO_BPP32);
>+
>+	return 0;
>+}
>+
>+static int virtio_gpu_video_sync(struct udevice *dev)
>+{
>+	struct virtio_gpu_priv *priv = dev_get_priv(dev);
>+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
>+	struct virtio_gpu_transfer_to_host_2d to_host_2d_in;
>+	struct virtio_gpu_resource_flush res_flush_in;
>+	struct virtio_gpu_ctrl_hdr ctrl_hdr_out;
>+	int ret;
>+
>+	/* We need to protect sync function reentrance to prevent exausting VQ */
>+	if (priv->in_sync)
>+		return 0;
>+
>+	priv->in_sync = true;
>+
>+	to_host_2d_in.r.x = 0;
>+	to_host_2d_in.r.y = 0;
>+	to_host_2d_in.r.width = cpu_to_virtio32(dev, uc_priv->xsize);
>+	to_host_2d_in.r.height = cpu_to_virtio32(dev, uc_priv->ysize);
>+	to_host_2d_in.offset = 0;
>+	to_host_2d_in.resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
>+	to_host_2d_in.padding = 0;
>+
>+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, &to_host_2d_in,
>+				sizeof(struct virtio_gpu_transfer_to_host_2d), &ctrl_hdr_out,
>+				sizeof(struct virtio_gpu_ctrl_hdr), true);
>+	if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
>+		log_debug("CMD_TRANSFER_TO_HOST_2D failed %d\n", ret);
>+		priv->in_sync = false;
>+		return -EINVAL;
>+	}
>+
>+	res_flush_in.r.x = 0;
>+	res_flush_in.r.y = 0;
>+	res_flush_in.r.width = cpu_to_virtio32(dev, uc_priv->xsize);
>+	res_flush_in.r.height = cpu_to_virtio32(dev, uc_priv->ysize);
>+	res_flush_in.resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
>+	res_flush_in.padding = 0;
>+
>+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_RESOURCE_FLUSH, &res_flush_in,
>+				sizeof(struct virtio_gpu_resource_flush), &ctrl_hdr_out,
>+				sizeof(struct virtio_gpu_ctrl_hdr), true);
>+	if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
>+		log_debug("CMD_RESOURCE_FLUSH failed %d\n", ret);
>+		priv->in_sync = false;
>+		return -EINVAL;
>+	}
>+
>+	priv->in_sync = false;
>+	return 0;
>+}
>+
>+static struct video_ops virtio_gpu_ops = {
>+	.video_sync = virtio_gpu_video_sync,
>+};
>+
>+U_BOOT_DRIVER(virtio_gpu) = {
>+	.name	= VIRTIO_GPU_DRV_NAME,
>+	.id	= UCLASS_VIDEO,
>+	.bind	= virtio_gpu_bind,
>+	.probe	= virtio_gpu_probe,
>+	.remove = virtio_reset,
>+	.ops	= &virtio_gpu_ops,
>+	.priv_auto	= sizeof(struct virtio_gpu_priv),
>+	.flags	= DM_FLAG_ACTIVE_DMA,
>+};
>diff --git a/drivers/virtio/virtio_gpu.h b/drivers/virtio/virtio_gpu.h
>new file mode 100644
>index 000000000000..d2e5c0e02f13
>--- /dev/null
>+++ b/drivers/virtio/virtio_gpu.h
>@@ -0,0 +1,428 @@
>+/* SPDX-License-Identifier: BSD-3-Clause */
>+/*
>+ * Copyright (C) 2024, Jiaxun Yang <jiaxun.yang@flygoat.com>
>+ *
>+ * From Linux kernel include/uapi/linux/virtio_gpu.h
>+ */
>+
>+#ifndef VIRTIO_GPU_HW_H
>+#define VIRTIO_GPU_HW_H
>+
>+#include <linux/types.h>
>+
>+/*
>+ * VIRTIO_GPU_CMD_CTX_*
>+ * VIRTIO_GPU_CMD_*_3D
>+ */
>+#define VIRTIO_GPU_F_VIRGL               0
>+
>+/*
>+ * VIRTIO_GPU_CMD_GET_EDID
>+ */
>+#define VIRTIO_GPU_F_EDID                1
>+/*
>+ * VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID
>+ */
>+#define VIRTIO_GPU_F_RESOURCE_UUID       2
>+
>+/*
>+ * VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB
>+ */
>+#define VIRTIO_GPU_F_RESOURCE_BLOB       3
>+/*
>+ * VIRTIO_GPU_CMD_CREATE_CONTEXT with
>+ * context_init and multiple timelines
>+ */
>+#define VIRTIO_GPU_F_CONTEXT_INIT        4
>+
>+enum virtio_gpu_ctrl_type {
>+	VIRTIO_GPU_UNDEFINED = 0,
>+
>+	/* 2d commands */
>+	VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
>+	VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
>+	VIRTIO_GPU_CMD_RESOURCE_UNREF,
>+	VIRTIO_GPU_CMD_SET_SCANOUT,
>+	VIRTIO_GPU_CMD_RESOURCE_FLUSH,
>+	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
>+	VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
>+	VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
>+	VIRTIO_GPU_CMD_GET_CAPSET_INFO,
>+	VIRTIO_GPU_CMD_GET_CAPSET,
>+	VIRTIO_GPU_CMD_GET_EDID,
>+	VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID,
>+	VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB,
>+	VIRTIO_GPU_CMD_SET_SCANOUT_BLOB,
>+
>+	/* 3d commands */
>+	VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
>+	VIRTIO_GPU_CMD_CTX_DESTROY,
>+	VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
>+	VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
>+	VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
>+	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
>+	VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
>+	VIRTIO_GPU_CMD_SUBMIT_3D,
>+	VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB,
>+	VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB,
>+
>+	/* cursor commands */
>+	VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
>+	VIRTIO_GPU_CMD_MOVE_CURSOR,
>+
>+	/* success responses */
>+	VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
>+	VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
>+	VIRTIO_GPU_RESP_OK_CAPSET_INFO,
>+	VIRTIO_GPU_RESP_OK_CAPSET,
>+	VIRTIO_GPU_RESP_OK_EDID,
>+	VIRTIO_GPU_RESP_OK_RESOURCE_UUID,
>+	VIRTIO_GPU_RESP_OK_MAP_INFO,
>+
>+	/* error responses */
>+	VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
>+	VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
>+	VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
>+	VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
>+	VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
>+	VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
>+};
>+
>+enum virtio_gpu_shm_id {
>+	VIRTIO_GPU_SHM_ID_UNDEFINED = 0,
>+	/*
>+	 * VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB
>+	 * VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB
>+	 */
>+	VIRTIO_GPU_SHM_ID_HOST_VISIBLE = 1
>+};
>+
>+#define VIRTIO_GPU_FLAG_FENCE         (1 << 0)
>+/*
>+ * If the following flag is set, then ring_idx contains the index
>+ * of the command ring that needs to used when creating the fence
>+ */
>+#define VIRTIO_GPU_FLAG_INFO_RING_IDX (1 << 1)
>+
>+struct virtio_gpu_ctrl_hdr {
>+	__le32 type;
>+	__le32 flags;
>+	__le64 fence_id;
>+	__le32 ctx_id;
>+	__u8 ring_idx;
>+	__u8 padding[3];
>+};
>+
>+/* data passed in the cursor vq */
>+
>+struct virtio_gpu_cursor_pos {
>+	__le32 scanout_id;
>+	__le32 x;
>+	__le32 y;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
>+struct virtio_gpu_update_cursor {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	struct virtio_gpu_cursor_pos pos;  /* update & move */
>+	__le32 resource_id;           /* update only */
>+	__le32 hot_x;                 /* update only */
>+	__le32 hot_y;                 /* update only */
>+	__le32 padding;
>+};
>+
>+/* data passed in the control vq, 2d related */
>+
>+struct virtio_gpu_rect {
>+	__le32 x;
>+	__le32 y;
>+	__le32 width;
>+	__le32 height;
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
>+struct virtio_gpu_resource_unref {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
>+struct virtio_gpu_resource_create_2d {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+	__le32 format;
>+	__le32 width;
>+	__le32 height;
>+};
>+
>+/* VIRTIO_GPU_CMD_SET_SCANOUT */
>+struct virtio_gpu_set_scanout {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	struct virtio_gpu_rect r;
>+	__le32 scanout_id;
>+	__le32 resource_id;
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
>+struct virtio_gpu_resource_flush {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	struct virtio_gpu_rect r;
>+	__le32 resource_id;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
>+struct virtio_gpu_transfer_to_host_2d {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	struct virtio_gpu_rect r;
>+	__le64 offset;
>+	__le32 resource_id;
>+	__le32 padding;
>+};
>+
>+struct virtio_gpu_mem_entry {
>+	__le64 addr;
>+	__le32 length;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
>+struct virtio_gpu_resource_attach_backing {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+	__le32 nr_entries;
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
>+struct virtio_gpu_resource_detach_backing {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
>+#define VIRTIO_GPU_MAX_SCANOUTS 16
>+struct virtio_gpu_resp_display_info {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	struct virtio_gpu_display_one {
>+		struct virtio_gpu_rect r;
>+		__le32 enabled;
>+		__le32 flags;
>+	} pmodes[VIRTIO_GPU_MAX_SCANOUTS];
>+};
>+
>+/* data passed in the control vq, 3d related */
>+
>+struct virtio_gpu_box {
>+	__le32 x, y, z;
>+	__le32 w, h, d;
>+};
>+
>+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */
>+struct virtio_gpu_transfer_host_3d {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	struct virtio_gpu_box box;
>+	__le64 offset;
>+	__le32 resource_id;
>+	__le32 level;
>+	__le32 stride;
>+	__le32 layer_stride;
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
>+#define VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP (1 << 0)
>+struct virtio_gpu_resource_create_3d {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+	__le32 target;
>+	__le32 format;
>+	__le32 bind;
>+	__le32 width;
>+	__le32 height;
>+	__le32 depth;
>+	__le32 array_size;
>+	__le32 last_level;
>+	__le32 nr_samples;
>+	__le32 flags;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_CMD_CTX_CREATE */
>+#define VIRTIO_GPU_CONTEXT_INIT_CAPSET_ID_MASK 0x000000ff
>+struct virtio_gpu_ctx_create {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 nlen;
>+	__le32 context_init;
>+	char debug_name[64];
>+};
>+
>+/* VIRTIO_GPU_CMD_CTX_DESTROY */
>+struct virtio_gpu_ctx_destroy {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+};
>+
>+/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */
>+struct virtio_gpu_ctx_resource {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_CMD_SUBMIT_3D */
>+struct virtio_gpu_cmd_submit {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 size;
>+	__le32 padding;
>+};
>+
>+#define VIRTIO_GPU_CAPSET_VIRGL 1
>+#define VIRTIO_GPU_CAPSET_VIRGL2 2
>+/* 3 is reserved for gfxstream */
>+#define VIRTIO_GPU_CAPSET_VENUS 4
>+
>+/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
>+struct virtio_gpu_get_capset_info {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 capset_index;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */
>+struct virtio_gpu_resp_capset_info {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 capset_id;
>+	__le32 capset_max_version;
>+	__le32 capset_max_size;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_CMD_GET_CAPSET */
>+struct virtio_gpu_get_capset {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 capset_id;
>+	__le32 capset_version;
>+};
>+
>+/* VIRTIO_GPU_RESP_OK_CAPSET */
>+struct virtio_gpu_resp_capset {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__u8 capset_data[];
>+};
>+
>+/* VIRTIO_GPU_CMD_GET_EDID */
>+struct virtio_gpu_cmd_get_edid {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 scanout;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_RESP_OK_EDID */
>+struct virtio_gpu_resp_edid {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 size;
>+	__le32 padding;
>+	__u8 edid[1024];
>+};
>+
>+#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
>+
>+struct virtio_gpu_config {
>+	__le32 events_read;
>+	__le32 events_clear;
>+	__le32 num_scanouts;
>+	__le32 num_capsets;
>+};
>+
>+/* simple formats for fbcon/X use */
>+enum virtio_gpu_formats {
>+	VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM  = 1,
>+	VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM  = 2,
>+	VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM  = 3,
>+	VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM  = 4,
>+
>+	VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM  = 67,
>+	VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM  = 68,
>+
>+	VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM  = 121,
>+	VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM  = 134,
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID */
>+struct virtio_gpu_resource_assign_uuid {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+	__le32 padding;
>+};
>+
>+/* VIRTIO_GPU_RESP_OK_RESOURCE_UUID */
>+struct virtio_gpu_resp_resource_uuid {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__u8 uuid[16];
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB */
>+struct virtio_gpu_resource_create_blob {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+#define VIRTIO_GPU_BLOB_MEM_GUEST             0x0001
>+#define VIRTIO_GPU_BLOB_MEM_HOST3D            0x0002
>+#define VIRTIO_GPU_BLOB_MEM_HOST3D_GUEST      0x0003
>+
>+#define VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE     0x0001
>+#define VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE    0x0002
>+#define VIRTIO_GPU_BLOB_FLAG_USE_CROSS_DEVICE 0x0004
>+	/* zero is invalid blob mem */
>+	__le32 blob_mem;
>+	__le32 blob_flags;
>+	__le32 nr_entries;
>+	__le64 blob_id;
>+	__le64 size;
>+	/*
>+	 * sizeof(nr_entries * virtio_gpu_mem_entry) bytes follow
>+	 */
>+};
>+
>+/* VIRTIO_GPU_CMD_SET_SCANOUT_BLOB */
>+struct virtio_gpu_set_scanout_blob {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	struct virtio_gpu_rect r;
>+	__le32 scanout_id;
>+	__le32 resource_id;
>+	__le32 width;
>+	__le32 height;
>+	__le32 format;
>+	__le32 padding;
>+	__le32 strides[4];
>+	__le32 offsets[4];
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB */
>+struct virtio_gpu_resource_map_blob {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+	__le32 padding;
>+	__le64 offset;
>+};
>+
>+/* VIRTIO_GPU_RESP_OK_MAP_INFO */
>+#define VIRTIO_GPU_MAP_CACHE_MASK     0x0f
>+#define VIRTIO_GPU_MAP_CACHE_NONE     0x00
>+#define VIRTIO_GPU_MAP_CACHE_CACHED   0x01
>+#define VIRTIO_GPU_MAP_CACHE_UNCACHED 0x02
>+#define VIRTIO_GPU_MAP_CACHE_WC       0x03
>+struct virtio_gpu_resp_map_info {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__u32 map_info;
>+	__u32 padding;
>+};
>+
>+/* VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB */
>+struct virtio_gpu_resource_unmap_blob {
>+	struct virtio_gpu_ctrl_hdr hdr;
>+	__le32 resource_id;
>+	__le32 padding;
>+};
>+
>+#endif
>diff --git a/include/virtio.h b/include/virtio.h
>index 1ab0ec5f39f5..93a6f5e92a48 100644
>--- a/include/virtio.h
>+++ b/include/virtio.h
>@@ -27,11 +27,13 @@
> #define VIRTIO_ID_NET		1 /* virtio net */
> #define VIRTIO_ID_BLOCK		2 /* virtio block */
> #define VIRTIO_ID_RNG		4 /* virtio rng */
>-#define VIRTIO_ID_MAX_NUM	5
>+#define VIRTIO_ID_GPU		16 /* virtio GPU */
>+#define VIRTIO_ID_MAX_NUM	17
> 
> #define VIRTIO_NET_DRV_NAME	"virtio-net"
> #define VIRTIO_BLK_DRV_NAME	"virtio-blk"
> #define VIRTIO_RNG_DRV_NAME	"virtio-rng"
>+#define VIRTIO_GPU_DRV_NAME	"virtio-gpu"
> 
> /* Status byte for guest to report progress, and synchronize features */
> 
>
Jiaxun Yang May 17, 2024, 2:26 a.m. UTC | #2
在2024年5月17日五月 上午2:56,Heinrich Schuchardt写道:
[...]
>>+config VIRTIO_GPU_SIZE_X
>>+	int "Width of display (X resolution)"
>>+	default 1280
>
> 1920x1080 would look like a reasonable default for me.

1280x1024 was chosen because it is the largest resolution being defined in
VESA VBE, hence guaranteed compatibility everywhere. I think it's Linux's
behaviour as well.

Carving out a huge framebuffer out of memory might be a challenge on
some platforms, so I'd like to keep it small.

>
>
>>+	help
>>+	  Sets the width of the display.
>>+
>>+	  These two options control the size of the display set up by QEMU.
>>+	  Typical sizes are 1024 x 768 or 1280 x 1024.
>>+
>>+config VIRTIO_GPU_SIZE_Y
>>+	int "High of display (Y resolution)"
>>+	default 1024
>>+	help
>>+	  Sets the height of the display.
>>+
>>+	  These two options control the size of the display set up by QEMU.
>>+	  Typical sizes are 1024 x 768 or 1280 x 1024.
>
> Haven't had such small monitors for a while. Why should this be typical?

Haha I copied this from bochs driver's help text, I can replace it with
more reasonable text.

>
> Doesn't QEMU allow to read the size of the output window at runtime?

Unfortunately, it's always guest to determine the size of display.
VIRTIO_GPU_CMD_GET_DISPLAY_INFO simply returned 0 as size of the scanout
before guest initialize it first.

It is possible to parse EDID provided by QEMU to get a resolution list,
but I found implementing the whole modesetting procdure here is a little
bit unnecessary.

I think resizing display in OS is generally done with VMM's guest agent.

>
> Best regards
>
> Heinrich
>
diff mbox series

Patch

diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 1de68867d52e..b75572c4ad13 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -76,4 +76,33 @@  config VIRTIO_RNG
 	help
 	  This is the virtual random number generator driver. It can be used
 	  with QEMU based targets.
+
+ config VIRTIO_GPU
+	bool "virtio GPU driver"
+	depends on VIRTIO && VIDEO
+	default y
+	help
+	  This is the virtual GPU display for virtio. It can be used with QEMU
+	  based targets.
+
+if VIRTIO_GPU
+config VIRTIO_GPU_SIZE_X
+	int "Width of display (X resolution)"
+	default 1280
+	help
+	  Sets the width of the display.
+
+	  These two options control the size of the display set up by QEMU.
+	  Typical sizes are 1024 x 768 or 1280 x 1024.
+
+config VIRTIO_GPU_SIZE_Y
+	int "High of display (Y resolution)"
+	default 1024
+	help
+	  Sets the height of the display.
+
+	  These two options control the size of the display set up by QEMU.
+	  Typical sizes are 1024 x 768 or 1280 x 1024.
+
+endif
 endmenu
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index 4c63a6c69043..c830fb6e6049 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -11,3 +11,4 @@  obj-$(CONFIG_VIRTIO_SANDBOX) += virtio_sandbox.o
 obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
 obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
 obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
+obj-$(CONFIG_VIRTIO_GPU) += virtio_gpu.o
diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
index 1dbc1a56aa21..1f3cdbf689c4 100644
--- a/drivers/virtio/virtio-uclass.c
+++ b/drivers/virtio/virtio-uclass.c
@@ -30,6 +30,7 @@  static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
 	[VIRTIO_ID_NET]		= VIRTIO_NET_DRV_NAME,
 	[VIRTIO_ID_BLOCK]	= VIRTIO_BLK_DRV_NAME,
 	[VIRTIO_ID_RNG]		= VIRTIO_RNG_DRV_NAME,
+	[VIRTIO_ID_GPU]		= VIRTIO_GPU_DRV_NAME,
 };
 
 int virtio_get_config(struct udevice *vdev, unsigned int offset,
diff --git a/drivers/virtio/virtio_gpu.c b/drivers/virtio/virtio_gpu.c
new file mode 100644
index 000000000000..d798562ecba2
--- /dev/null
+++ b/drivers/virtio/virtio_gpu.c
@@ -0,0 +1,298 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2024, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ */
+
+#define pr_fmt(fmt) "virtio_gpu: " fmt
+
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <video.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include "virtio_gpu.h"
+#include <asm/io.h>
+
+struct virtio_gpu_priv {
+	struct virtqueue *vq;
+	u32 scanout_res_id;
+	u64 fence_id;
+	bool in_sync;
+};
+
+static int virtio_gpu_do_req(struct udevice *dev,
+			     enum virtio_gpu_ctrl_type type,
+			     void *in, size_t in_size,
+			     void *out, size_t out_size, bool flush)
+{
+	int ret;
+	uint len;
+	struct virtio_gpu_priv *priv = dev_get_priv(dev);
+	struct virtio_sg in_sg;
+	struct virtio_sg out_sg;
+	struct virtio_sg *sgs[] = { &in_sg, &out_sg };
+	struct virtio_gpu_ctrl_hdr *ctrl_hdr_in = in;
+	struct virtio_gpu_ctrl_hdr *ctrl_hdr_out = out;
+
+	ctrl_hdr_in->type = cpu_to_virtio32(dev, (u32)type);
+	if (flush) {
+		ctrl_hdr_in->flags = cpu_to_virtio32(dev, VIRTIO_GPU_FLAG_FENCE);
+		ctrl_hdr_in->fence_id = cpu_to_virtio64(dev, priv->fence_id++);
+	} else {
+		ctrl_hdr_in->flags = 0;
+		ctrl_hdr_in->fence_id = 0;
+	}
+	ctrl_hdr_in->ctx_id = 0;
+	ctrl_hdr_in->ring_idx = 0;
+	in_sg.addr = in;
+	in_sg.length = in_size;
+	out_sg.addr = out;
+	out_sg.length = out_size;
+
+	ret = virtqueue_add(priv->vq, sgs, 1, 1);
+	if (ret) {
+		log_debug("virtqueue_add failed %d\n", ret);
+		return ret;
+	}
+	virtqueue_kick(priv->vq);
+
+	debug("wait...");
+	while (!virtqueue_get_buf(priv->vq, &len))
+		;
+	debug("done\n");
+
+	if (out_size != len) {
+		log_debug("Invalid response size %d, expected %d\n",
+			  len, (uint)out_size);
+	}
+
+	return virtio32_to_cpu(dev, ctrl_hdr_out->type);
+}
+
+static int virtio_gpu_probe(struct udevice *dev)
+{
+	struct virtio_gpu_priv *priv = dev_get_priv(dev);
+	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct virtio_gpu_ctrl_hdr ctrl_hdr_in;
+	struct virtio_gpu_ctrl_hdr ctrl_hdr_out;
+	struct virtio_gpu_resp_display_info *disp_info_out;
+	struct virtio_gpu_display_one *disp;
+	struct virtio_gpu_resource_create_2d *res_create_2d_in;
+	void *res_buf_in;
+	struct virtio_gpu_resource_attach_backing *res_attach_backing_in;
+	struct virtio_gpu_mem_entry *mem_entry;
+	struct virtio_gpu_set_scanout *set_scanout_in;
+	unsigned int scanout_mask = 0;
+	int ret, i;
+
+	if (!plat->base) {
+		log_warning("No framebuffer allocated\n");
+		return -EINVAL;
+	}
+
+	ret = virtio_find_vqs(dev, 1, &priv->vq);
+	if (ret < 0) {
+		log_warning("virtio_find_vqs failed\n");
+		return ret;
+	}
+
+	disp_info_out = malloc(sizeof(struct virtio_gpu_resp_display_info));
+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_GET_DISPLAY_INFO, &ctrl_hdr_in,
+				sizeof(struct virtio_gpu_ctrl_hdr), disp_info_out,
+				sizeof(struct virtio_gpu_resp_display_info), false);
+
+	if (ret != VIRTIO_GPU_RESP_OK_DISPLAY_INFO) {
+		log_warning("CMD_GET_DISPLAY_INFO failed %d\n", ret);
+		ret = -EINVAL;
+		goto out_free_disp;
+	}
+
+	for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
+		disp = &disp_info_out->pmodes[i];
+		if (!disp->enabled)
+			continue;
+		log_debug("Found available scanout: %d\n", i);
+		scanout_mask |= 1 << i;
+	}
+
+	if (!scanout_mask) {
+		log_warning("No active scanout found\n");
+		ret = -EINVAL;
+		goto out_free_disp;
+	}
+
+	free(disp_info_out);
+	disp_info_out = NULL;
+
+	/* TODO: We can parse EDID for those info */
+	uc_priv->xsize = CONFIG_VAL(VIRTIO_GPU_SIZE_X);
+	uc_priv->ysize = CONFIG_VAL(VIRTIO_GPU_SIZE_Y);
+	uc_priv->bpix = VIDEO_BPP32;
+
+	priv->scanout_res_id = 1;
+	res_create_2d_in = malloc(sizeof(struct virtio_gpu_resource_create_2d));
+	res_create_2d_in->resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
+	res_create_2d_in->format = cpu_to_virtio32(dev, VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
+	res_create_2d_in->width = cpu_to_virtio32(dev, uc_priv->xsize);
+	res_create_2d_in->height = cpu_to_virtio32(dev, uc_priv->ysize);
+
+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, res_create_2d_in,
+				sizeof(struct virtio_gpu_resource_create_2d), &ctrl_hdr_out,
+				sizeof(struct virtio_gpu_ctrl_hdr), false);
+	if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
+		log_warning("CMD_RESOURCE_CREATE_2D failed %d\n", ret);
+		ret = -EINVAL;
+		goto out_free_res_create_2d;
+	}
+
+	free(res_create_2d_in);
+	res_create_2d_in = NULL;
+
+	res_buf_in = malloc(sizeof(struct virtio_gpu_resource_attach_backing) +
+			    sizeof(struct virtio_gpu_mem_entry));
+	res_attach_backing_in = res_buf_in;
+	mem_entry = res_buf_in + sizeof(struct virtio_gpu_resource_attach_backing);
+	res_attach_backing_in->resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
+	res_attach_backing_in->nr_entries = cpu_to_virtio32(dev, 1);
+	mem_entry->addr = cpu_to_virtio64(dev, virt_to_phys((void *)plat->base));
+	mem_entry->length = cpu_to_virtio32(dev, plat->size);
+	mem_entry->padding = 0;
+
+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, res_buf_in,
+				sizeof(struct virtio_gpu_resource_attach_backing) +
+				sizeof(struct virtio_gpu_mem_entry), &ctrl_hdr_out,
+				sizeof(struct virtio_gpu_ctrl_hdr), false);
+
+	if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
+		log_warning("CMD_RESOURCE_ATTACH_BACKING failed %d\n", ret);
+		ret = -EINVAL;
+		goto out_free_res_buf;
+	}
+	free(res_buf_in);
+	res_buf_in = NULL;
+
+	set_scanout_in = malloc(sizeof(struct virtio_gpu_set_scanout));
+	while (scanout_mask) {
+		u32 active_scanout = ffs(scanout_mask) - 1;
+
+		set_scanout_in->r.x = 0;
+		set_scanout_in->r.y = 0;
+		set_scanout_in->r.width = cpu_to_virtio32(dev, uc_priv->xsize);
+		set_scanout_in->r.height = cpu_to_virtio32(dev, uc_priv->ysize);
+		set_scanout_in->scanout_id = cpu_to_virtio32(dev, active_scanout);
+		set_scanout_in->resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
+
+		ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_SET_SCANOUT, set_scanout_in,
+					sizeof(struct virtio_gpu_set_scanout), &ctrl_hdr_out,
+					sizeof(struct virtio_gpu_ctrl_hdr), false);
+
+		if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
+			log_warning("CMD_SET_SCANOUT failed %d for scanout %d\n",
+				    ret, active_scanout);
+			ret = -EINVAL;
+			goto out_free_set_scanout;
+		}
+		scanout_mask &= ~(1 << active_scanout);
+	}
+	free(set_scanout_in);
+	set_scanout_in = NULL;
+
+	return 0;
+out_free_set_scanout:
+	if (set_scanout_in)
+		free(set_scanout_in);
+out_free_res_buf:
+	if (res_buf_in)
+		free(res_buf_in);
+out_free_res_create_2d:
+	if (res_create_2d_in)
+		free(res_create_2d_in);
+out_free_disp:
+	if (disp_info_out)
+		free(disp_info_out);
+	return ret;
+}
+
+static int virtio_gpu_bind(struct udevice *dev)
+{
+	struct virtio_dev_priv *virtio_uc_priv = dev_get_uclass_priv(dev->parent);
+	struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+
+	/* Indicate what driver features we support */
+	virtio_driver_features_init(virtio_uc_priv, NULL, 0, NULL, 0);
+	plat->base = 0; /* Framebuffer will be allocated by the video-uclass */
+	plat->size = CONFIG_VAL(VIRTIO_GPU_SIZE_X) *
+		     CONFIG_VAL(VIRTIO_GPU_SIZE_X) * VNBYTES(VIDEO_BPP32);
+
+	return 0;
+}
+
+static int virtio_gpu_video_sync(struct udevice *dev)
+{
+	struct virtio_gpu_priv *priv = dev_get_priv(dev);
+	struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct virtio_gpu_transfer_to_host_2d to_host_2d_in;
+	struct virtio_gpu_resource_flush res_flush_in;
+	struct virtio_gpu_ctrl_hdr ctrl_hdr_out;
+	int ret;
+
+	/* We need to protect sync function reentrance to prevent exausting VQ */
+	if (priv->in_sync)
+		return 0;
+
+	priv->in_sync = true;
+
+	to_host_2d_in.r.x = 0;
+	to_host_2d_in.r.y = 0;
+	to_host_2d_in.r.width = cpu_to_virtio32(dev, uc_priv->xsize);
+	to_host_2d_in.r.height = cpu_to_virtio32(dev, uc_priv->ysize);
+	to_host_2d_in.offset = 0;
+	to_host_2d_in.resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
+	to_host_2d_in.padding = 0;
+
+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, &to_host_2d_in,
+				sizeof(struct virtio_gpu_transfer_to_host_2d), &ctrl_hdr_out,
+				sizeof(struct virtio_gpu_ctrl_hdr), true);
+	if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
+		log_debug("CMD_TRANSFER_TO_HOST_2D failed %d\n", ret);
+		priv->in_sync = false;
+		return -EINVAL;
+	}
+
+	res_flush_in.r.x = 0;
+	res_flush_in.r.y = 0;
+	res_flush_in.r.width = cpu_to_virtio32(dev, uc_priv->xsize);
+	res_flush_in.r.height = cpu_to_virtio32(dev, uc_priv->ysize);
+	res_flush_in.resource_id = cpu_to_virtio32(dev, priv->scanout_res_id);
+	res_flush_in.padding = 0;
+
+	ret = virtio_gpu_do_req(dev, VIRTIO_GPU_CMD_RESOURCE_FLUSH, &res_flush_in,
+				sizeof(struct virtio_gpu_resource_flush), &ctrl_hdr_out,
+				sizeof(struct virtio_gpu_ctrl_hdr), true);
+	if (ret != VIRTIO_GPU_RESP_OK_NODATA) {
+		log_debug("CMD_RESOURCE_FLUSH failed %d\n", ret);
+		priv->in_sync = false;
+		return -EINVAL;
+	}
+
+	priv->in_sync = false;
+	return 0;
+}
+
+static struct video_ops virtio_gpu_ops = {
+	.video_sync = virtio_gpu_video_sync,
+};
+
+U_BOOT_DRIVER(virtio_gpu) = {
+	.name	= VIRTIO_GPU_DRV_NAME,
+	.id	= UCLASS_VIDEO,
+	.bind	= virtio_gpu_bind,
+	.probe	= virtio_gpu_probe,
+	.remove = virtio_reset,
+	.ops	= &virtio_gpu_ops,
+	.priv_auto	= sizeof(struct virtio_gpu_priv),
+	.flags	= DM_FLAG_ACTIVE_DMA,
+};
diff --git a/drivers/virtio/virtio_gpu.h b/drivers/virtio/virtio_gpu.h
new file mode 100644
index 000000000000..d2e5c0e02f13
--- /dev/null
+++ b/drivers/virtio/virtio_gpu.h
@@ -0,0 +1,428 @@ 
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (C) 2024, Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *
+ * From Linux kernel include/uapi/linux/virtio_gpu.h
+ */
+
+#ifndef VIRTIO_GPU_HW_H
+#define VIRTIO_GPU_HW_H
+
+#include <linux/types.h>
+
+/*
+ * VIRTIO_GPU_CMD_CTX_*
+ * VIRTIO_GPU_CMD_*_3D
+ */
+#define VIRTIO_GPU_F_VIRGL               0
+
+/*
+ * VIRTIO_GPU_CMD_GET_EDID
+ */
+#define VIRTIO_GPU_F_EDID                1
+/*
+ * VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID
+ */
+#define VIRTIO_GPU_F_RESOURCE_UUID       2
+
+/*
+ * VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB
+ */
+#define VIRTIO_GPU_F_RESOURCE_BLOB       3
+/*
+ * VIRTIO_GPU_CMD_CREATE_CONTEXT with
+ * context_init and multiple timelines
+ */
+#define VIRTIO_GPU_F_CONTEXT_INIT        4
+
+enum virtio_gpu_ctrl_type {
+	VIRTIO_GPU_UNDEFINED = 0,
+
+	/* 2d commands */
+	VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
+	VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
+	VIRTIO_GPU_CMD_RESOURCE_UNREF,
+	VIRTIO_GPU_CMD_SET_SCANOUT,
+	VIRTIO_GPU_CMD_RESOURCE_FLUSH,
+	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
+	VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
+	VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
+	VIRTIO_GPU_CMD_GET_CAPSET_INFO,
+	VIRTIO_GPU_CMD_GET_CAPSET,
+	VIRTIO_GPU_CMD_GET_EDID,
+	VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID,
+	VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB,
+	VIRTIO_GPU_CMD_SET_SCANOUT_BLOB,
+
+	/* 3d commands */
+	VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
+	VIRTIO_GPU_CMD_CTX_DESTROY,
+	VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
+	VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
+	VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
+	VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
+	VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
+	VIRTIO_GPU_CMD_SUBMIT_3D,
+	VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB,
+	VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB,
+
+	/* cursor commands */
+	VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
+	VIRTIO_GPU_CMD_MOVE_CURSOR,
+
+	/* success responses */
+	VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
+	VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+	VIRTIO_GPU_RESP_OK_CAPSET_INFO,
+	VIRTIO_GPU_RESP_OK_CAPSET,
+	VIRTIO_GPU_RESP_OK_EDID,
+	VIRTIO_GPU_RESP_OK_RESOURCE_UUID,
+	VIRTIO_GPU_RESP_OK_MAP_INFO,
+
+	/* error responses */
+	VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
+	VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
+	VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
+	VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
+	VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
+	VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
+};
+
+enum virtio_gpu_shm_id {
+	VIRTIO_GPU_SHM_ID_UNDEFINED = 0,
+	/*
+	 * VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB
+	 * VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB
+	 */
+	VIRTIO_GPU_SHM_ID_HOST_VISIBLE = 1
+};
+
+#define VIRTIO_GPU_FLAG_FENCE         (1 << 0)
+/*
+ * If the following flag is set, then ring_idx contains the index
+ * of the command ring that needs to used when creating the fence
+ */
+#define VIRTIO_GPU_FLAG_INFO_RING_IDX (1 << 1)
+
+struct virtio_gpu_ctrl_hdr {
+	__le32 type;
+	__le32 flags;
+	__le64 fence_id;
+	__le32 ctx_id;
+	__u8 ring_idx;
+	__u8 padding[3];
+};
+
+/* data passed in the cursor vq */
+
+struct virtio_gpu_cursor_pos {
+	__le32 scanout_id;
+	__le32 x;
+	__le32 y;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
+struct virtio_gpu_update_cursor {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_cursor_pos pos;  /* update & move */
+	__le32 resource_id;           /* update only */
+	__le32 hot_x;                 /* update only */
+	__le32 hot_y;                 /* update only */
+	__le32 padding;
+};
+
+/* data passed in the control vq, 2d related */
+
+struct virtio_gpu_rect {
+	__le32 x;
+	__le32 y;
+	__le32 width;
+	__le32 height;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
+struct virtio_gpu_resource_unref {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
+struct virtio_gpu_resource_create_2d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 format;
+	__le32 width;
+	__le32 height;
+};
+
+/* VIRTIO_GPU_CMD_SET_SCANOUT */
+struct virtio_gpu_set_scanout {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_rect r;
+	__le32 scanout_id;
+	__le32 resource_id;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
+struct virtio_gpu_resource_flush {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_rect r;
+	__le32 resource_id;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
+struct virtio_gpu_transfer_to_host_2d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_rect r;
+	__le64 offset;
+	__le32 resource_id;
+	__le32 padding;
+};
+
+struct virtio_gpu_mem_entry {
+	__le64 addr;
+	__le32 length;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
+struct virtio_gpu_resource_attach_backing {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 nr_entries;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
+struct virtio_gpu_resource_detach_backing {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
+#define VIRTIO_GPU_MAX_SCANOUTS 16
+struct virtio_gpu_resp_display_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_display_one {
+		struct virtio_gpu_rect r;
+		__le32 enabled;
+		__le32 flags;
+	} pmodes[VIRTIO_GPU_MAX_SCANOUTS];
+};
+
+/* data passed in the control vq, 3d related */
+
+struct virtio_gpu_box {
+	__le32 x, y, z;
+	__le32 w, h, d;
+};
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */
+struct virtio_gpu_transfer_host_3d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_box box;
+	__le64 offset;
+	__le32 resource_id;
+	__le32 level;
+	__le32 stride;
+	__le32 layer_stride;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
+#define VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP (1 << 0)
+struct virtio_gpu_resource_create_3d {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 target;
+	__le32 format;
+	__le32 bind;
+	__le32 width;
+	__le32 height;
+	__le32 depth;
+	__le32 array_size;
+	__le32 last_level;
+	__le32 nr_samples;
+	__le32 flags;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_CTX_CREATE */
+#define VIRTIO_GPU_CONTEXT_INIT_CAPSET_ID_MASK 0x000000ff
+struct virtio_gpu_ctx_create {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 nlen;
+	__le32 context_init;
+	char debug_name[64];
+};
+
+/* VIRTIO_GPU_CMD_CTX_DESTROY */
+struct virtio_gpu_ctx_destroy {
+	struct virtio_gpu_ctrl_hdr hdr;
+};
+
+/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */
+struct virtio_gpu_ctx_resource {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_SUBMIT_3D */
+struct virtio_gpu_cmd_submit {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 size;
+	__le32 padding;
+};
+
+#define VIRTIO_GPU_CAPSET_VIRGL 1
+#define VIRTIO_GPU_CAPSET_VIRGL2 2
+/* 3 is reserved for gfxstream */
+#define VIRTIO_GPU_CAPSET_VENUS 4
+
+/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
+struct virtio_gpu_get_capset_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 capset_index;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */
+struct virtio_gpu_resp_capset_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 capset_id;
+	__le32 capset_max_version;
+	__le32 capset_max_size;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_CMD_GET_CAPSET */
+struct virtio_gpu_get_capset {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 capset_id;
+	__le32 capset_version;
+};
+
+/* VIRTIO_GPU_RESP_OK_CAPSET */
+struct virtio_gpu_resp_capset {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__u8 capset_data[];
+};
+
+/* VIRTIO_GPU_CMD_GET_EDID */
+struct virtio_gpu_cmd_get_edid {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 scanout;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_RESP_OK_EDID */
+struct virtio_gpu_resp_edid {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 size;
+	__le32 padding;
+	__u8 edid[1024];
+};
+
+#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
+
+struct virtio_gpu_config {
+	__le32 events_read;
+	__le32 events_clear;
+	__le32 num_scanouts;
+	__le32 num_capsets;
+};
+
+/* simple formats for fbcon/X use */
+enum virtio_gpu_formats {
+	VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM  = 1,
+	VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM  = 2,
+	VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM  = 3,
+	VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM  = 4,
+
+	VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM  = 67,
+	VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM  = 68,
+
+	VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM  = 121,
+	VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM  = 134,
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID */
+struct virtio_gpu_resource_assign_uuid {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+};
+
+/* VIRTIO_GPU_RESP_OK_RESOURCE_UUID */
+struct virtio_gpu_resp_resource_uuid {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__u8 uuid[16];
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB */
+struct virtio_gpu_resource_create_blob {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+#define VIRTIO_GPU_BLOB_MEM_GUEST             0x0001
+#define VIRTIO_GPU_BLOB_MEM_HOST3D            0x0002
+#define VIRTIO_GPU_BLOB_MEM_HOST3D_GUEST      0x0003
+
+#define VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE     0x0001
+#define VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE    0x0002
+#define VIRTIO_GPU_BLOB_FLAG_USE_CROSS_DEVICE 0x0004
+	/* zero is invalid blob mem */
+	__le32 blob_mem;
+	__le32 blob_flags;
+	__le32 nr_entries;
+	__le64 blob_id;
+	__le64 size;
+	/*
+	 * sizeof(nr_entries * virtio_gpu_mem_entry) bytes follow
+	 */
+};
+
+/* VIRTIO_GPU_CMD_SET_SCANOUT_BLOB */
+struct virtio_gpu_set_scanout_blob {
+	struct virtio_gpu_ctrl_hdr hdr;
+	struct virtio_gpu_rect r;
+	__le32 scanout_id;
+	__le32 resource_id;
+	__le32 width;
+	__le32 height;
+	__le32 format;
+	__le32 padding;
+	__le32 strides[4];
+	__le32 offsets[4];
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB */
+struct virtio_gpu_resource_map_blob {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+	__le64 offset;
+};
+
+/* VIRTIO_GPU_RESP_OK_MAP_INFO */
+#define VIRTIO_GPU_MAP_CACHE_MASK     0x0f
+#define VIRTIO_GPU_MAP_CACHE_NONE     0x00
+#define VIRTIO_GPU_MAP_CACHE_CACHED   0x01
+#define VIRTIO_GPU_MAP_CACHE_UNCACHED 0x02
+#define VIRTIO_GPU_MAP_CACHE_WC       0x03
+struct virtio_gpu_resp_map_info {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__u32 map_info;
+	__u32 padding;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB */
+struct virtio_gpu_resource_unmap_blob {
+	struct virtio_gpu_ctrl_hdr hdr;
+	__le32 resource_id;
+	__le32 padding;
+};
+
+#endif
diff --git a/include/virtio.h b/include/virtio.h
index 1ab0ec5f39f5..93a6f5e92a48 100644
--- a/include/virtio.h
+++ b/include/virtio.h
@@ -27,11 +27,13 @@ 
 #define VIRTIO_ID_NET		1 /* virtio net */
 #define VIRTIO_ID_BLOCK		2 /* virtio block */
 #define VIRTIO_ID_RNG		4 /* virtio rng */
-#define VIRTIO_ID_MAX_NUM	5
+#define VIRTIO_ID_GPU		16 /* virtio GPU */
+#define VIRTIO_ID_MAX_NUM	17
 
 #define VIRTIO_NET_DRV_NAME	"virtio-net"
 #define VIRTIO_BLK_DRV_NAME	"virtio-blk"
 #define VIRTIO_RNG_DRV_NAME	"virtio-rng"
+#define VIRTIO_GPU_DRV_NAME	"virtio-gpu"
 
 /* Status byte for guest to report progress, and synchronize features */