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