[PATCH v2 0/2] virtio_gpu driver and relevant fix

Hi all,
This series added support for virtio-gpu device, it has been tested on QEMU riscv64 and aarch64.
I also fixed a problem in EFI GOP so it works with EFI applications as well.
There is a problem remaining. virtio_init() is being called too late to allow virtio-gpu to be selected as a vidconsole. My current appraoch is to register virtio_init in init_sequence_r, just after pci_init. Is this a viable approach? Should I introduce a config like CONFIG_VIRTIO_INIT_R to allow it to be selected by boards?
Thanks
Signed-off-by: Jiaxun Yang jiaxun.yang@flygoat.com --- Changes in v2: - Fix left over in previous version (endian, unused variable) - Fix Kconfig text - Link to v1: https://lore.kernel.org/r/20240517-virtio_gpu-v1-0-6353b87472c7@flygoat.com
--- Jiaxun Yang (2): virtio: New virtio_gpu driver efi: gop: Mark pixel_format as BLTONLY if we have sync hook
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/efi_api.h | 1 + include/virtio.h | 4 +- lib/efi_loader/efi_gop.c | 8 +- 8 files changed, 772 insertions(+), 2 deletions(-) --- base-commit: 21aa37ec1cd4a12e01e0e863ec9e99d703ce4d52 change-id: 20240513-virtio_gpu-abb4de685808
Best regards,

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

在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

If a video device has a video_sync hook, it means some software intervene is required to scanout framebuffer up on change.
That means EFI application can't just use it as raw framebuffer, it should call BLT operation to let U-Boot help with scanout.
Mark pixel format as BLTONLY as per UEFI spec to reflect this nature.
Signed-off-by: Jiaxun Yang jiaxun.yang@flygoat.com --- v2: - Remove an unused variable --- include/efi_api.h | 1 + lib/efi_loader/efi_gop.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/include/efi_api.h b/include/efi_api.h index ab40b1b5ddf6..3eaefb322878 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -1399,6 +1399,7 @@ struct efi_hii_config_access_protocol { #define EFI_GOT_RGBA8 0 #define EFI_GOT_BGRA8 1 #define EFI_GOT_BITMASK 2 +#define EFI_GOT_BLTONLY 3
struct efi_gop_mode_info { u32 version; diff --git a/lib/efi_loader/efi_gop.c b/lib/efi_loader/efi_gop.c index 41e12fa72460..c9244376c09a 100644 --- a/lib/efi_loader/efi_gop.c +++ b/lib/efi_loader/efi_gop.c @@ -471,6 +471,7 @@ efi_status_t efi_gop_register(void) struct udevice *vdev; struct video_priv *priv; struct video_uc_plat *plat; + struct video_ops *ops;
/* We only support a single video output device for now */ if (uclass_first_device_err(UCLASS_VIDEO, &vdev)) { @@ -485,6 +486,7 @@ efi_status_t efi_gop_register(void) row = video_get_ysize(vdev);
plat = dev_get_uclass_plat(vdev); + ops = video_get_ops(vdev); fb_base = IS_ENABLED(CONFIG_VIDEO_COPY) ? plat->copy_base : plat->base; fb_size = plat->size;
@@ -529,7 +531,11 @@ efi_status_t efi_gop_register(void) gopobj->info.version = 0; gopobj->info.width = col; gopobj->info.height = row; - if (bpix == VIDEO_BPP32) + + if (ops && ops->video_sync) { + /* Applications can't really use it as framebuffer */ + gopobj->info.pixel_format = EFI_GOT_BLTONLY; + } else if (bpix == VIDEO_BPP32) { if (format == VIDEO_X2R10G10B10) { gopobj->info.pixel_format = EFI_GOT_BITMASK;
participants (2)
-
Jiaxun Yang
-
Simon Glass