Hi Jiaxun, On Sat, 20 Jul 2024 at 07:58, Jiaxun Yang <jiaxun.y...@flygoat.com> wrote: > > > > 在2024年7月19日七月 下午11:05,Simon Glass写道: > > Hi Jiaxun, > > > > On Wed, 17 Jul 2024 at 15:34, Jiaxun Yang <jiaxun.y...@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.y...@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 <s...@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.y...@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