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 |
在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
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
在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
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
在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 --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 */
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(-)