diff mbox series

[v2,1/2] virtio: New virtio_gpu driver

Message ID 20240524-virtio_gpu-v2-1-b198c35b1fd2@flygoat.com
State New
Delegated to: Anatolij Gustschin
Headers show
Series virtio_gpu driver and relevant fix | expand

Commit Message

Jiaxun Yang May 24, 2024, 1:02 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>
---
v2:
	- Add big endian code path
	- Reword typical resolution for Kconfig symbol
---
 drivers/virtio/Kconfig         |  29 +++
 drivers/virtio/Makefile        |   1 +
 drivers/virtio/virtio-uclass.c |   1 +
 drivers/virtio/virtio_gpu.c    | 302 +++++++++++++++++++++++++++++
 drivers/virtio/virtio_gpu.h    | 428 +++++++++++++++++++++++++++++++++++++++++
 include/virtio.h               |   4 +-
 6 files changed, 764 insertions(+), 1 deletion(-)

Comments

Jiaxun Yang July 17, 2024, 2:33 p.m. UTC | #1
在2024年5月24日五月 下午9:02,Jiaxun Yang写道:
> 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>

Ping

> ---
> v2:
> 	- Add big endian code path
> 	- Reword typical resolution for Kconfig symbol
> ---
>  drivers/virtio/Kconfig         |  29 +++
>  drivers/virtio/Makefile        |   1 +
>  drivers/virtio/virtio-uclass.c |   1 +
>  drivers/virtio/virtio_gpu.c    | 302 +++++++++++++++++++++++++++++
>  drivers/virtio/virtio_gpu.h    | 428 +++++++++++++++++++++++++++++++++++++++++
>  include/virtio.h               |   4 +-
>  6 files changed, 764 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> index 1de68867d52e..a4838278fabc 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 size is 1280 x 1024 for compatibility.
> +
> +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 size is 1280 x 1024 for compatibility.
> +
> +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..0b306bb9d2fa
> --- /dev/null
> +++ b/drivers/virtio/virtio_gpu.c
> @@ -0,0 +1,302 @@
> +// 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);
> +#ifdef __BIG_ENDIAN
> +	res_create_2d_in->format = cpu_to_virtio32(dev, 
> VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM);
> +#else
> +	res_create_2d_in->format = cpu_to_virtio32(dev, 
> VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
> +#endif
> +	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 */
> 
>
> -- 
> 2.43.0
Simon Glass July 19, 2024, 3:05 p.m. UTC | #2
Hi Jiaxun,

On Wed, 17 Jul 2024 at 15:34, Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>
>
>
> 在2024年5月24日五月 下午9:02,Jiaxun Yang写道:
> > 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>
>
> Ping
>
> > ---
> > v2:
> >       - Add big endian code path
> >       - Reword typical resolution for Kconfig symbol
> > ---
> >  drivers/virtio/Kconfig         |  29 +++
> >  drivers/virtio/Makefile        |   1 +
> >  drivers/virtio/virtio-uclass.c |   1 +
> >  drivers/virtio/virtio_gpu.c    | 302 +++++++++++++++++++++++++++++
> >  drivers/virtio/virtio_gpu.h    | 428 +++++++++++++++++++++++++++++++++++++++++
> >  include/virtio.h               |   4 +-
> >  6 files changed, 764 insertions(+), 1 deletion(-)

Reviewed-by: Simon Glass <sjg@chromium.org>

Nits below

> >
> > diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> > index 1de68867d52e..a4838278fabc 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.

QEMU-based

Can you add a bit more info about its capabilities? Should we be using
this instead of Bochs?

> > +
> > +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 size is 1280 x 1024 for compatibility.
> > +
> > +config VIRTIO_GPU_SIZE_Y
> > +     int "High of display (Y resolution)"

Height

> > +     default 1024
> > +     help
> > +       Sets the height of the display.
> > +
> > +       These two options control the size of the display set up by QEMU.
> > +       Typical size is 1280 x 1024 for compatibility.
> > +
> > +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..0b306bb9d2fa
> > --- /dev/null
> > +++ b/drivers/virtio/virtio_gpu.c
> > @@ -0,0 +1,302 @@
> > +// 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)

Please add a function comment

> > +{
> > +     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);
> > +#ifdef __BIG_ENDIAN
> > +     res_create_2d_in->format = cpu_to_virtio32(dev,
> > VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM);
> > +#else
> > +     res_create_2d_in->format = cpu_to_virtio32(dev,
> > VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
> > +#endif
> > +     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));

Could these structs be inside priv instead of allocating each one?

> > +     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 */

But there are no comments so we don't know what these structs are for
or what the fields do. Can you add documentation, or a pointer to
somewhere where it exists?

> > +
> > +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 */
> >
> >
> > --
> > 2.43.0
>

Regards,
Simon
Jiaxun Yang July 20, 2024, 6:57 a.m. UTC | #3
在2024年7月19日七月 下午11:05,Simon Glass写道:
> Hi Jiaxun,
>
> On Wed, 17 Jul 2024 at 15:34, Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>>
>>
>>
>> 在2024年5月24日五月 下午9:02,Jiaxun Yang写道:
>> > 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>
>>
>> Ping
>>
>> > ---
>> > v2:
>> >       - Add big endian code path
>> >       - Reword typical resolution for Kconfig symbol
>> > ---
>> >  drivers/virtio/Kconfig         |  29 +++
>> >  drivers/virtio/Makefile        |   1 +
>> >  drivers/virtio/virtio-uclass.c |   1 +
>> >  drivers/virtio/virtio_gpu.c    | 302 +++++++++++++++++++++++++++++
>> >  drivers/virtio/virtio_gpu.h    | 428 +++++++++++++++++++++++++++++++++++++++++
>> >  include/virtio.h               |   4 +-
>> >  6 files changed, 764 insertions(+), 1 deletion(-)
>
> Reviewed-by: Simon Glass <sjg@chromium.org>
>
> Nits below
>
>> >
>> > diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
>> > index 1de68867d52e..a4838278fabc 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.
>
> QEMU-based
>
> Can you add a bit more info about its capabilities? Should we be using
> this instead of Bochs?

I think it’s up to users preference?
For U-Boot there is no visible benefits, for Linux you can enjoy some accel features.
I’m not really sure if it’s relevant for U-Boot Kconfig.

>
>> > +
>> > +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 size is 1280 x 1024 for compatibility.
>> > +
>> > +config VIRTIO_GPU_SIZE_Y
>> > +     int "High of display (Y resolution)"
>
> Height
>
>> > +     default 1024
>> > +     help
>> > +       Sets the height of the display.
>> > +
>> > +       These two options control the size of the display set up by QEMU.
>> > +       Typical size is 1280 x 1024 for compatibility.
>> > +
>> > +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..0b306bb9d2fa
>> > --- /dev/null
>> > +++ b/drivers/virtio/virtio_gpu.c
>> > @@ -0,0 +1,302 @@
>> > +// 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)
>
> Please add a function comment
>
>> > +{
>> > +     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);
>> > +#ifdef __BIG_ENDIAN
>> > +     res_create_2d_in->format = cpu_to_virtio32(dev,
>> > VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM);
>> > +#else
>> > +     res_create_2d_in->format = cpu_to_virtio32(dev,
>> > VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
>> > +#endif
>> > +     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));
>
> Could these structs be inside priv instead of allocating each one?

Those structs are all used only once at initialization.

We can save some runtime memory by freeing them here :-)

>
>> > +     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 */
>
> But there are no comments so we don't know what these structs are for
> or what the fields do. Can you add documentation, or a pointer to
> somewhere where it exists?

The whole header is copied from Linux kernel so I’m not really sure if we want to edit it.

Maybe I can mention about the specification at start of the driver code?

Thanks
>
>> > +
>> > +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 */
>> >
>> >
>> > --
>> > 2.43.0
>>
>
> Regards,
> Simon
Simon Glass July 21, 2024, 10:08 a.m. UTC | #4
Hi Jiaxun,

On Sat, 20 Jul 2024 at 07:58, Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
>
>
>
> 在2024年7月19日七月 下午11:05,Simon Glass写道:
> > Hi Jiaxun,
> >
> > On Wed, 17 Jul 2024 at 15:34, Jiaxun Yang <jiaxun.yang@flygoat.com> wrote:
> >>
> >>
> >>
> >> 在2024年5月24日五月 下午9:02,Jiaxun Yang写道:
> >> > 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>
> >>
> >> Ping
> >>
> >> > ---
> >> > v2:
> >> >       - Add big endian code path
> >> >       - Reword typical resolution for Kconfig symbol
> >> > ---
> >> >  drivers/virtio/Kconfig         |  29 +++
> >> >  drivers/virtio/Makefile        |   1 +
> >> >  drivers/virtio/virtio-uclass.c |   1 +
> >> >  drivers/virtio/virtio_gpu.c    | 302 +++++++++++++++++++++++++++++
> >> >  drivers/virtio/virtio_gpu.h    | 428 +++++++++++++++++++++++++++++++++++++++++
> >> >  include/virtio.h               |   4 +-
> >> >  6 files changed, 764 insertions(+), 1 deletion(-)
> >
> > Reviewed-by: Simon Glass <sjg@chromium.org>
> >
> > Nits below
> >
> >> >
> >> > diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
> >> > index 1de68867d52e..a4838278fabc 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.
> >
> > QEMU-based
> >
> > Can you add a bit more info about its capabilities? Should we be using
> > this instead of Bochs?
>
> I think it’s up to users preference?
> For U-Boot there is no visible benefits, for Linux you can enjoy some accel features.
> I’m not really sure if it’s relevant for U-Boot Kconfig.
>
> >
> >> > +
> >> > +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 size is 1280 x 1024 for compatibility.
> >> > +
> >> > +config VIRTIO_GPU_SIZE_Y
> >> > +     int "High of display (Y resolution)"
> >
> > Height
> >
> >> > +     default 1024
> >> > +     help
> >> > +       Sets the height of the display.
> >> > +
> >> > +       These two options control the size of the display set up by QEMU.
> >> > +       Typical size is 1280 x 1024 for compatibility.
> >> > +
> >> > +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..0b306bb9d2fa
> >> > --- /dev/null
> >> > +++ b/drivers/virtio/virtio_gpu.c
> >> > @@ -0,0 +1,302 @@
> >> > +// 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)
> >
> > Please add a function comment
> >
> >> > +{
> >> > +     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);
> >> > +#ifdef __BIG_ENDIAN
> >> > +     res_create_2d_in->format = cpu_to_virtio32(dev,
> >> > VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM);
> >> > +#else
> >> > +     res_create_2d_in->format = cpu_to_virtio32(dev,
> >> > VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
> >> > +#endif
> >> > +     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));
> >
> > Could these structs be inside priv instead of allocating each one?
>
> Those structs are all used only once at initialization.
>
> We can save some runtime memory by freeing them here :-)

Then you can just use a local var.

>
> >
> >> > +     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);
> >> > +

[..]

> >> > +/* 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 */
> >
> > But there are no comments so we don't know what these structs are for
> > or what the fields do. Can you add documentation, or a pointer to
> > somewhere where it exists?
>
> The whole header is copied from Linux kernel so I’m not really sure if we want to edit it.
>
> Maybe I can mention about the specification at start of the driver code?

Yes that would help.

I thought Linux's aversion to comments had softened a little?

Regards,
Simon
Jiaxun Yang July 21, 2024, 10:41 a.m. UTC | #5
在2024年7月21日七月 下午6:08,Simon Glass写道:
[...]
>> >> > +
>> >> > +     set_scanout_in = malloc(sizeof(struct virtio_gpu_set_scanout));
>> >
>> > Could these structs be inside priv instead of allocating each one?
>>
>> Those structs are all used only once at initialization.
>>
>> We can save some runtime memory by freeing them here :-)
>
> Then you can just use a local var.

That will yield a huge stack frame that breaks booting on RISC-V virt board :-(

Thanks
- Jiaxun

>
>>
>> >
>> >> > +     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);
>> >> > +
>
> [..]
>
>> >> > +/* 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 */
>> >
>> > But there are no comments so we don't know what these structs are for
>> > or what the fields do. Can you add documentation, or a pointer to
>> > somewhere where it exists?
>>
>> The whole header is copied from Linux kernel so I’m not really sure if we want to edit it.
>>
>> Maybe I can mention about the specification at start of the driver code?
>
> Yes that would help.
>
> I thought Linux's aversion to comments had softened a little?
>
> Regards,
> Simon
diff mbox series

Patch

diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index 1de68867d52e..a4838278fabc 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 size is 1280 x 1024 for compatibility.
+
+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 size is 1280 x 1024 for compatibility.
+
+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..0b306bb9d2fa
--- /dev/null
+++ b/drivers/virtio/virtio_gpu.c
@@ -0,0 +1,302 @@ 
+// 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);
+#ifdef __BIG_ENDIAN
+	res_create_2d_in->format = cpu_to_virtio32(dev, VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM);
+#else
+	res_create_2d_in->format = cpu_to_virtio32(dev, VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
+#endif
+	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 */