From 78b993c4e8285f6e0fffe644523749392d3ab728 Mon Sep 17 00:00:00 2001 From: Peter Bee Date: Mon, 29 May 2023 15:02:12 +0800 Subject: [PATCH] drivers/virtio: add virtio gpu driver This patch provides basic framebuffer display support for virtio. Multiple displays are supported. Signed-off-by: Peter Bee --- drivers/virtio/Kconfig | 6 + drivers/virtio/Make.defs | 4 + drivers/virtio/virtio-gpu.c | 741 ++++++++++++++++++++++++++++++++++++ drivers/virtio/virtio-gpu.h | 530 ++++++++++++++++++++++++++ drivers/virtio/virtio.c | 9 + 5 files changed, 1290 insertions(+) create mode 100644 drivers/virtio/virtio-gpu.c create mode 100644 drivers/virtio/virtio-gpu.h diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index ef0777813f..76c24d90d6 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -27,6 +27,12 @@ config DRIVERS_VIRTIO_BLK depends on !DISABLE_MOUNTPOINT default n +config DRIVERS_VIRTIO_GPU + bool "Virtio gpu support" + default n + depends on VIDEO_FB + select FB_UPDATE + config DRIVERS_VIRTIO_NET bool "Virtio network support" depends on NETDEVICES diff --git a/drivers/virtio/Make.defs b/drivers/virtio/Make.defs index e4051eecb7..20df7ce537 100644 --- a/drivers/virtio/Make.defs +++ b/drivers/virtio/Make.defs @@ -32,6 +32,10 @@ ifeq ($(CONFIG_DRIVERS_VIRTIO_BLK),y) CSRCS += virtio-blk.c endif +ifeq ($(CONFIG_DRIVERS_VIRTIO_GPU),y) + CSRCS += virtio-gpu.c +endif + ifeq ($(CONFIG_DRIVERS_VIRTIO_NET),y) CSRCS += virtio-net.c endif diff --git a/drivers/virtio/virtio-gpu.c b/drivers/virtio/virtio-gpu.c new file mode 100644 index 0000000000..3c75cd0b22 --- /dev/null +++ b/drivers/virtio/virtio-gpu.c @@ -0,0 +1,741 @@ +/**************************************************************************** + * drivers/virtio/virtio-gpu.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include "virtio-gpu.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define VIRTIO_GPU_BPP 32 +#define VIRTIO_GPU_FB_FMT FB_FMT_RGB32 +#define VIRTIO_GPU_FMT VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM + +#define VIRTIO_GPU_CTL 0 +#define VIRTIO_GPU_NUM 1 + +#define VIRTIO_GPU_MAX_DISP 4 +#define VIRTIO_GPU_MAX_PLANE 1 +#define VIRTIO_GPU_MAX_NENTS 4 + +#define VIRTIO_GPU_MAP_ERR(e) ((e) == VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY ? \ + -ENOMEM : -EINVAL) + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct virtio_gpu_priv_s +{ + struct fb_vtable_s vtable; /* Must be cast compatible with virtio_gpu_priv_s */ + FAR struct virtio_device *vdev; /* Contained virtio device */ + FAR uint8_t *fbmem; /* Allocated framebuffer */ + size_t fblen; /* Size of the framebuffer in bytes */ + fb_coord_t xres; /* Horizontal resolution in pixel columns */ + fb_coord_t yres; /* Vertical resolution in pixel rows */ + fb_coord_t stride; /* Width of a row in bytes */ + uint8_t display; /* Display number */ +}; + +struct virtio_gpu_cookie_s +{ + bool blocking; + FAR void *p; +}; + +struct virtio_gpu_backing_s +{ + struct virtio_gpu_resource_attach_backing cmd; + struct virtio_gpu_mem_entry ents[VIRTIO_GPU_MAX_NENTS]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int virtio_gpu_send_cmd(FAR struct virtqueue *vq, + FAR struct virtqueue_buf *buf_list, + int readable, int writable, FAR void *cookie); +static void virtio_gpu_done(FAR struct virtqueue *vq); +static int virtio_gpu_init(FAR struct virtio_gpu_priv_s *priv, + FAR struct virtio_device *vdev); +static int virtio_gpu_get_display_info(FAR struct virtio_gpu_priv_s *priv); +static int virtio_gpu_create_2d(FAR struct virtio_gpu_priv_s *priv, + int resource_id, int width, int height); +static int virtio_gpu_attach_backing(FAR struct virtio_gpu_priv_s *priv, + int resource_id, + FAR struct virtio_gpu_mem_entry *ents, + uint32_t nents); +static int virtio_gpu_set_scanout(FAR struct virtio_gpu_priv_s *priv, + int scanout_id, int resource_id, + int width, int height); +static int virtio_gpu_transfer_to_host_2d(FAR struct virtio_gpu_priv_s *priv, + int resource_id, int x, int y, + int width, int height); +static int virtio_gpu_flush_resource(FAR struct virtio_gpu_priv_s *priv, + int resource_id, int x, int y, + int width, int height); +static int virtio_gpu_probe(FAR struct virtio_device *vdev); +static void virtio_gpu_remove(FAR struct virtio_device *vdev); +static int virtio_gpu_getvideoinfo(FAR struct fb_vtable_s *vtable, + FAR struct fb_videoinfo_s *vinfo); +static int virtio_gpu_getplaneinfo(FAR struct fb_vtable_s *vtable, + int planeno, + FAR struct fb_planeinfo_s *pinfo); +static int virtio_gpu_updatearea(FAR struct fb_vtable_s *vtable, + FAR const struct fb_area_s *area); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct virtio_driver g_virtio_gpu_driver = +{ + .node = LIST_INITIAL_VALUE(g_virtio_gpu_driver.node), /* node */ + .device = VIRTIO_ID_GPU, /* device id */ + .probe = virtio_gpu_probe, /* probe */ + .remove = virtio_gpu_remove, /* remove */ +}; + +static FAR struct virtio_gpu_priv_s *g_virtio_gpu[VIRTIO_GPU_MAX_DISP]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: virtio_gpu_send_cmd + ****************************************************************************/ + +static int virtio_gpu_send_cmd(FAR struct virtqueue *vq, + FAR struct virtqueue_buf *buf_list, + int readable, int writable, FAR void *buf) +{ + int ret; + + if (writable > 0) + { + sem_t sem; + struct virtio_gpu_cookie_s cookie; + + sem_init(&sem, 0, 0); + cookie.blocking = true; + cookie.p = &sem; + ret = virtqueue_add_buffer(vq, buf_list, readable, writable, &cookie); + if (ret >= 0) + { + virtqueue_kick(vq); + sem_wait(&sem); + } + + sem_destroy(&sem); + } + else + { + FAR struct virtio_gpu_cookie_s *cookie; + + cookie = kmm_malloc(sizeof(*cookie)); + if (cookie == NULL) + { + vrterr("ERROR: Failed to allocate cookie memory"); + ret = -ENOMEM; + } + else + { + cookie->blocking = false; + cookie->p = buf; + ret = virtqueue_add_buffer(vq, buf_list, readable, writable, + cookie); + if (ret >= 0) + { + virtqueue_kick(vq); + } + else + { + virtio_free_buf(vq->vq_dev, buf); + kmm_free(cookie); + } + } + } + + return ret; +} + +/**************************************************************************** + * Name: virtio_gpu_done + ****************************************************************************/ + +static void virtio_gpu_done(FAR struct virtqueue *vq) +{ + FAR struct virtio_gpu_cookie_s *cookie; + + while ((cookie = virtqueue_get_buffer(vq, NULL, NULL)) != NULL) + { + if (cookie->blocking) + { + sem_post((FAR sem_t *)cookie->p); + } + else + { + virtio_free_buf(vq->vq_dev, cookie->p); + kmm_free(cookie); + } + } +} + +/**************************************************************************** + * Name: virtio_gpu_init + ****************************************************************************/ + +static int virtio_gpu_init(FAR struct virtio_gpu_priv_s *priv, + FAR struct virtio_device *vdev) +{ + FAR const char *vqnames[VIRTIO_GPU_NUM]; + vq_callback callbacks[VIRTIO_GPU_NUM]; + int ret; + + priv->vdev = vdev; + vdev->priv = priv; + + /* Initialize the virtio device */ + + virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER); + virtio_set_features(vdev, 0); + virtio_set_status(vdev, VIRTIO_CONFIG_FEATURES_OK); + + vqnames[VIRTIO_GPU_CTL] = "virtio_gpu_ctl"; + callbacks[VIRTIO_GPU_CTL] = virtio_gpu_done; + ret = virtio_create_virtqueues(vdev, 0, VIRTIO_GPU_NUM, vqnames, + callbacks); + if (ret < 0) + { + vrterr("virtio_device_create_virtqueue failed, ret=%d", ret); + return ret; + } + + virtio_set_status(vdev, VIRTIO_CONFIG_STATUS_DRIVER_OK); + return OK; +} + +/**************************************************************************** + * Name: virtio_gpu_get_display_info + ****************************************************************************/ + +static int virtio_gpu_get_display_info(FAR struct virtio_gpu_priv_s *priv) +{ + FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_GPU_CTL].vq; + struct virtio_gpu_ctrl_hdr cmd; + struct virtio_gpu_resp_display_info info; + struct virtqueue_buf vb[2]; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO; + + vb[0].buf = &cmd; + vb[0].len = sizeof(cmd); + vb[1].buf = &info; + vb[1].len = sizeof(info); + + ret = virtio_gpu_send_cmd(vq, vb, 1, 1, NULL); + if (ret < 0) + { + return ret; + } + + if (info.hdr.type != VIRTIO_GPU_RESP_OK_DISPLAY_INFO) + { + return VIRTIO_GPU_MAP_ERR(info.hdr.type); + } + + priv->xres = info.pmodes[0].r.width; + priv->yres = info.pmodes[0].r.height; + vrtinfo("Setting resolution: (%d,%d)", priv->xres, priv->yres); + return OK; +} + +/**************************************************************************** + * Name: virtio_gpu_create_2d + ****************************************************************************/ + +static int virtio_gpu_create_2d(FAR struct virtio_gpu_priv_s *priv, + int resource_id, int width, int height) +{ + FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_GPU_CTL].vq; + struct virtio_gpu_resource_create_2d cmd; + struct virtio_gpu_ctrl_hdr resp; + struct virtqueue_buf vb[2]; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D; + cmd.resource_id = resource_id; + cmd.format = VIRTIO_GPU_FMT; + cmd.width = width; + cmd.height = height; + + vb[0].buf = &cmd; + vb[0].len = sizeof(cmd); + vb[1].buf = &resp; + vb[1].len = sizeof(resp); + + ret = virtio_gpu_send_cmd(vq, vb, 1, 1, NULL); + if (ret >= 0 && resp.type != VIRTIO_GPU_RESP_OK_NODATA) + { + ret = VIRTIO_GPU_MAP_ERR(resp.type); + } + + return ret; +} + +/**************************************************************************** + * Name: virtio_gpu_attach_backing + ****************************************************************************/ + +static int virtio_gpu_attach_backing(FAR struct virtio_gpu_priv_s *priv, + int resource_id, + FAR struct virtio_gpu_mem_entry *ents, + uint32_t nents) +{ + FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_GPU_CTL].vq; + struct virtio_gpu_backing_s backing; + struct virtio_gpu_ctrl_hdr resp; + struct virtqueue_buf vb[2]; + size_t i; + int ret; + + if (nents > VIRTIO_GPU_MAX_NENTS) + { + vrterr("ERROR: Backing memory entries count %" PRId32 "exceeds %d", + nents, VIRTIO_GPU_MAX_NENTS); + return -E2BIG; + } + + memset(&backing.cmd, 0, sizeof(backing.cmd)); + backing.cmd.hdr.type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING; + backing.cmd.resource_id = resource_id; + backing.cmd.nr_entries = nents; + + for (i = 0; i < nents; i++) + { + backing.ents[i] = ents[i]; + } + + vb[0].buf = &backing; + vb[0].len = sizeof(backing.cmd) + nents * sizeof(backing.ents[0]); + vb[1].buf = &resp; + vb[1].len = sizeof(resp); + + ret = virtio_gpu_send_cmd(vq, vb, 1, 1, NULL); + if (ret >= 0 && resp.type != VIRTIO_GPU_RESP_OK_NODATA) + { + ret = VIRTIO_GPU_MAP_ERR(resp.type); + } + + return ret; +} + +/**************************************************************************** + * Name: virtio_gpu_set_scanout + ****************************************************************************/ + +static int virtio_gpu_set_scanout(FAR struct virtio_gpu_priv_s *priv, + int scanout_id, int resource_id, + int width, int height) +{ + FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_GPU_CTL].vq; + struct virtio_gpu_set_scanout cmd; + struct virtio_gpu_ctrl_hdr resp; + struct virtqueue_buf vb[2]; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.type = VIRTIO_GPU_CMD_SET_SCANOUT; + cmd.scanout_id = scanout_id; + cmd.resource_id = resource_id; + cmd.r.width = width; + cmd.r.height = height; + + vb[0].buf = &cmd; + vb[0].len = sizeof(cmd); + vb[1].buf = &resp; + vb[1].len = sizeof(resp); + + ret = virtio_gpu_send_cmd(vq, vb, 1, 1, NULL); + if (ret >= 0 && resp.type != VIRTIO_GPU_RESP_OK_NODATA) + { + ret = VIRTIO_GPU_MAP_ERR(resp.type); + } + + return ret; +} + +/**************************************************************************** + * Name: virtio_gpu_transfer_to_host_2d + ****************************************************************************/ + +static int virtio_gpu_transfer_to_host_2d(FAR struct virtio_gpu_priv_s *priv, + int resource_id, int x, int y, + int width, int height) +{ + FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_GPU_CTL].vq; + struct virtio_gpu_transfer_to_host_2d cmd; + struct virtio_gpu_ctrl_hdr resp; + struct virtqueue_buf vb[2]; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.hdr.type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D; + cmd.resource_id = resource_id; + cmd.offset = y * priv->stride + x * (VIRTIO_GPU_BPP >> 3); + cmd.r.x = x; + cmd.r.y = y; + cmd.r.width = width; + cmd.r.height = height; + + vb[0].buf = &cmd; + vb[0].len = sizeof(cmd); + vb[1].buf = &resp; + vb[1].len = sizeof(resp); + + ret = virtio_gpu_send_cmd(vq, vb, 1, 1, NULL); + if (ret >= 0 && resp.type != VIRTIO_GPU_RESP_OK_NODATA) + { + ret = VIRTIO_GPU_MAP_ERR(resp.type); + } + + return ret; +} + +/**************************************************************************** + * Name: virtio_gpu_flush_resource + ****************************************************************************/ + +static int virtio_gpu_flush_resource(FAR struct virtio_gpu_priv_s *priv, + int resource_id, int x, int y, + int width, int height) +{ + FAR struct virtqueue *vq = priv->vdev->vrings_info[VIRTIO_GPU_CTL].vq; + FAR struct virtio_gpu_resource_flush *cmd; + struct virtqueue_buf vb; + + cmd = virtio_zalloc_buf(priv->vdev, sizeof(*cmd), 16); + if (cmd == NULL) + { + vrterr("ERROR: Failed to allocate cmd buffer"); + return -ENOMEM; + } + + cmd->hdr.type = VIRTIO_GPU_CMD_RESOURCE_FLUSH; + cmd->resource_id = resource_id; + cmd->r.x = x; + cmd->r.y = y; + cmd->r.width = width; + cmd->r.height = height; + + vb.buf = cmd; + vb.len = sizeof(*cmd); + + return virtio_gpu_send_cmd(vq, &vb, 1, 0, cmd); +} + +/**************************************************************************** + * Name: virtio_gpu_probe + ****************************************************************************/ + +static int virtio_gpu_probe(FAR struct virtio_device *vdev) +{ + FAR struct virtio_gpu_priv_s *priv; + struct virtio_gpu_mem_entry ent; + int disp; + int ret; + + for (disp = 0; disp < VIRTIO_GPU_MAX_DISP; disp++) + { + if (g_virtio_gpu[disp] == NULL) + { + break; + } + } + + if (disp == VIRTIO_GPU_MAX_DISP) + { + return -EMFILE; + } + + priv = kmm_zalloc(sizeof(*priv)); + if (priv == NULL) + { + return -ENOMEM; + } + + ret = virtio_gpu_init(priv, vdev); + if (ret < 0) + { + goto err_out; + } + + ret = virtio_gpu_get_display_info(priv); + if (ret < 0) + { + goto err_init; + } + + /* Initialize the LCD-independent fields of the state structure */ + + priv->vtable.getvideoinfo = virtio_gpu_getvideoinfo, + priv->vtable.getplaneinfo = virtio_gpu_getplaneinfo, + priv->vtable.updatearea = virtio_gpu_updatearea, + + /* Allocate (and clear) the framebuffer */ + + priv->stride = priv->xres * VIRTIO_GPU_BPP >> 3; + priv->fblen = priv->stride * priv->yres; + + priv->fbmem = (FAR uint8_t *)virtio_zalloc_buf(vdev, priv->fblen, 16); + if (priv->fbmem == NULL) + { + vrterr("ERROR: Failed to allocate frame buffer memory"); + ret = -ENOMEM; + goto err_init_fb; + } + + ret = virtio_gpu_create_2d(priv, 1, priv->xres, priv->yres); + if (ret < 0) + { + vrterr("virtio_gpu_create_2d error"); + goto err_init_fb; + } + + ent.addr = (uintptr_t)priv->fbmem; + ent.length = priv->fblen; + ret = virtio_gpu_attach_backing(priv, 1, &ent, 1); + if (ret < 0) + { + vrterr("virtio_gpu_attach_backing error"); + goto err_init_fb; + } + + ret = virtio_gpu_set_scanout(priv, 0, 1, priv->xres, priv->yres); + if (ret < 0) + { + vrterr("virtio_gpu_set_scanout error"); + goto err_init_fb; + } + + ret = virtio_gpu_transfer_to_host_2d(priv, 1, 0, 0, priv->xres, + priv->yres); + if (ret < 0) + { + vrterr("virtio_gpu_transfer_to_host_2d error"); + goto err_init_fb; + } + + ret = virtio_gpu_flush_resource(priv, 1, 0, 0, priv->xres, priv->yres); + if (ret < 0) + { + vrterr("virtio_gpu_flush_resource error"); + goto err_init_fb; + } + + g_virtio_gpu[disp] = priv; + priv->display = disp; + + ret = fb_register(disp, 0); + if (ret < 0) + { + vrterr("ERROR: Failed to initialize framebuffer driver, ret=%d", + ret); + g_virtio_gpu[disp] = NULL; + goto err_init_fb; + } + + return ret; + +err_init_fb: + virtio_free_buf(vdev, priv->fbmem); +err_init: + virtio_reset_device(vdev); + virtio_delete_virtqueues(vdev); +err_out: + kmm_free(priv); + return ret; +} + +/**************************************************************************** + * Name: virtio_gpu_remove + ****************************************************************************/ + +static void virtio_gpu_remove(FAR struct virtio_device *vdev) +{ + FAR struct virtio_gpu_priv_s *priv = vdev->priv; + + virtio_reset_device(vdev); + virtio_delete_virtqueues(vdev); + g_virtio_gpu[priv->display] = NULL; + virtio_free_buf(vdev, priv->fbmem); + kmm_free(priv); +} + +/**************************************************************************** + * Name: virtio_gpu_getvideoinfo + ****************************************************************************/ + +static int virtio_gpu_getvideoinfo(FAR struct fb_vtable_s *vtable, + FAR struct fb_videoinfo_s *vinfo) +{ + FAR struct virtio_gpu_priv_s *priv = + (FAR struct virtio_gpu_priv_s *)vtable; + + vinfo->fmt = VIRTIO_GPU_FB_FMT; + vinfo->nplanes = VIRTIO_GPU_MAX_PLANE; + vinfo->xres = priv->xres; + vinfo->yres = priv->yres; + return OK; +} + +/**************************************************************************** + * Name: virtio_gpu_getplaneinfo + ****************************************************************************/ + +static int virtio_gpu_getplaneinfo(FAR struct fb_vtable_s *vtable, + int planeno, + FAR struct fb_planeinfo_s *pinfo) +{ + FAR struct virtio_gpu_priv_s *priv = + (FAR struct virtio_gpu_priv_s *)vtable; + + if (planeno >= VIRTIO_GPU_MAX_PLANE) + { + vrterr("ERROR: plane number %d exceeds %d", + planeno, VIRTIO_GPU_MAX_PLANE - 1); + return -EINVAL; + } + + memset(pinfo, 0, sizeof(*pinfo)); + pinfo->bpp = VIRTIO_GPU_BPP; + pinfo->display = priv->display; + pinfo->fblen = priv->fblen; + pinfo->fbmem = priv->fbmem; + pinfo->stride = priv->stride; + return OK; +} + +/**************************************************************************** + * Name: virtio_gpu_updatearea + ****************************************************************************/ + +static int virtio_gpu_updatearea(FAR struct fb_vtable_s *vtable, + FAR const struct fb_area_s *area) +{ + FAR struct virtio_gpu_priv_s *priv = + (FAR struct virtio_gpu_priv_s *)vtable; + int ret = OK; + + vrtinfo("update disp %d:(%d %d)[%d %d]", priv->display, + area->x, area->y, area->w, area->h); + ret = virtio_gpu_transfer_to_host_2d(priv, 1, area->x, area->y, + area->w, area->h); + if (ret < 0) + { + vrterr("virtio_gpu_transfer_to_host_2d failed: %d", ret); + return ret; + } + + ret = virtio_gpu_flush_resource(priv, 1, area->x, area->y, + area->w, area->h); + return ret; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: virtio_register_gpu_driver + ****************************************************************************/ + +int virtio_register_gpu_driver(void) +{ + return virtio_register_driver(&g_virtio_gpu_driver); +} + +/**************************************************************************** + * Name: up_fbinitialize + * + * Description: + * Initialize the framebuffer video hardware associated with the display. + * + * Input Parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * + * Returned Value: + * Zero is returned on success; a negated errno value is returned on any + * failure. + * + ****************************************************************************/ + +int up_fbinitialize(int display) +{ + return display < VIRTIO_GPU_MAX_DISP ? OK : -EINVAL; +} + +/**************************************************************************** + * Name: up_fbgetvplane + * + * Description: + * Return a reference to the framebuffer object for the specified video + * plane of the specified plane. + * Many OSDs support multiple planes of video. + * + * Input Parameters: + * display - In the case of hardware with multiple displays, this + * specifies the display. Normally this is zero. + * vplane - Identifies the plane being queried. + * + * Returned Value: + * A non-NULL pointer to the frame buffer access structure is returned on + * success; NULL is returned on any failure. + * + ****************************************************************************/ + +FAR struct fb_vtable_s *up_fbgetvplane(int display, int vplane) +{ + if (display < 0 || display >= VIRTIO_GPU_MAX_DISP || + vplane < 0 || vplane >= VIRTIO_GPU_MAX_PLANE || !g_virtio_gpu[display]) + { + vrterr("get vplane (%d,%d) failed", display, vplane); + return NULL; + } + + return &g_virtio_gpu[display]->vtable; +} diff --git a/drivers/virtio/virtio-gpu.h b/drivers/virtio/virtio-gpu.h new file mode 100644 index 0000000000..9871bcd4aa --- /dev/null +++ b/drivers/virtio/virtio-gpu.h @@ -0,0 +1,530 @@ +/**************************************************************************** + * drivers/virtio/virtio-gpu.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __DRIVERS_VIRTIO_VIRTIO_GPU_H +#define __DRIVERS_VIRTIO_VIRTIO_GPU_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include + +#ifdef CONFIG_DRIVERS_VIRTIO_GPU + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Definitions from viogpu.h in OpenBSD src */ + +#define VIRTIO_GPU_F_VIRGL (1u << 0) +#define VIRTIO_GPU_F_EDID (1u << 1) +#define VIRTIO_GPU_F_RESOURCE_UUID (1u << 2) +#define VIRTIO_GPU_F_RESOURCE_BLOB (1u << 3) + +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_SHM_ID_HOST_VISIBLE +}; + +#define VIRTIO_GPU_FLAG_FENCE (1 << 0) +#define VIRTIO_GPU_FLAG_INFO_RING_IDX (1 << 1) + +begin_packed_struct struct virtio_gpu_ctrl_hdr +{ + uint32_t type; + uint32_t flags; + uint64_t fence_id; + uint32_t ctx_id; + uint8_t ring_idx; + uint8_t padding[3]; +} end_packed_struct; + +/* Data passed in the cursor vq */ + +begin_packed_struct struct virtio_gpu_cursor_pos +{ + uint32_t scanout_id; + uint32_t x; + uint32_t y; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */ + +begin_packed_struct struct virtio_gpu_update_cursor +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_cursor_pos pos; /* Update & move */ + uint32_t resource_id; /* Update only */ + uint32_t hot_x; /* Update only */ + uint32_t hot_y; /* Update only */ + uint32_t padding; +} end_packed_struct; + +/* Data passed in the control vq, 2d related */ + +begin_packed_struct struct virtio_gpu_rect +{ + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_RESOURCE_UNREF */ + +begin_packed_struct struct virtio_gpu_resource_unref +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */ + +begin_packed_struct struct virtio_gpu_resource_create_2d +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t format; + uint32_t width; + uint32_t height; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_SET_SCANOUT */ + +begin_packed_struct struct virtio_gpu_set_scanout +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + uint32_t scanout_id; + uint32_t resource_id; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */ + +begin_packed_struct struct virtio_gpu_resource_flush +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + uint32_t resource_id; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */ + +begin_packed_struct struct virtio_gpu_transfer_to_host_2d +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + uint64_t offset; + uint32_t resource_id; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */ + +begin_packed_struct struct virtio_gpu_mem_entry +{ + uint64_t addr; + uint32_t length; + uint32_t padding; +} end_packed_struct; + +begin_packed_struct struct virtio_gpu_resource_attach_backing +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t nr_entries; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */ + +begin_packed_struct struct virtio_gpu_resource_detach_backing +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */ + +#define VIRTIO_GPU_MAX_SCANOUTS 16 + +begin_packed_struct struct virtio_gpu_resp_display_info +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_display_one + { + struct virtio_gpu_rect r; + uint32_t enabled; + uint32_t flags; + } + pmodes[VIRTIO_GPU_MAX_SCANOUTS]; +} end_packed_struct; + +/* Data passed in the control vq, 3d related */ + +begin_packed_struct struct virtio_gpu_box +{ + uint32_t x; + uint32_t y; + uint32_t z; + uint32_t w; + uint32_t h; + uint32_t d; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */ + +begin_packed_struct struct virtio_gpu_transfer_host_3d +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_box box; + uint64_t offset; + uint32_t resource_id; + uint32_t level; + uint32_t stride; + uint32_t layer_stride; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */ + +#define VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP (1 << 0) + +begin_packed_struct struct virtio_gpu_resource_create_3d +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t target; + uint32_t format; + uint32_t bind; + uint32_t width; + uint32_t height; + uint32_t depth; + uint32_t array_size; + uint32_t last_level; + uint32_t nr_samples; + uint32_t flags; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_CTX_CREATE */ + +begin_packed_struct struct virtio_gpu_ctx_create +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t nlen; + uint32_t padding; + char debug_name[64]; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_CTX_DESTROY */ + +begin_packed_struct struct virtio_gpu_ctx_destroy +{ + struct virtio_gpu_ctrl_hdr hdr; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */ + +begin_packed_struct struct virtio_gpu_ctx_resource +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_SUBMIT_3D */ + +begin_packed_struct struct virtio_gpu_cmd_submit +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t size; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */ + +#define VIRTIO_GPU_CAPSET_VIRGL 1 +#define VIRTIO_GPU_CAPSET_VIRGL2 2 + +begin_packed_struct struct virtio_gpu_get_capset_info +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t capset_index; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */ + +begin_packed_struct struct virtio_gpu_resp_capset_info +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t capset_id; + uint32_t capset_max_version; + uint32_t capset_max_size; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_GET_CAPSET */ + +begin_packed_struct struct virtio_gpu_get_capset +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t capset_id; + uint32_t capset_version; +} end_packed_struct; + +/* VIRTIO_GPU_RESP_OK_CAPSET */ + +begin_packed_struct struct virtio_gpu_resp_capset +{ + struct virtio_gpu_ctrl_hdr hdr; + uint8_t capset_data[]; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_GET_EDID */ + +begin_packed_struct struct virtio_gpu_cmd_get_edid +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t scanout; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_RESP_OK_EDID */ + +begin_packed_struct struct virtio_gpu_resp_edid +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t size; + uint32_t padding; + uint8_t edid[1024]; +} end_packed_struct; + +#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) + +begin_packed_struct struct virtio_gpu_config +{ + uint32_t events_read; + uint32_t events_clear; + uint32_t num_scanouts; + uint32_t num_capsets; +} end_packed_struct; + +/* 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 */ + +begin_packed_struct struct virtio_gpu_resource_assign_uuid +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_RESP_OK_RESOURCE_UUID */ + +begin_packed_struct struct virtio_gpu_resp_resource_uuid +{ + struct virtio_gpu_ctrl_hdr hdr; + uint8_t uuid[16]; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB */ + +#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 + +begin_packed_struct struct virtio_gpu_resource_create_blob +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + + /* Zero is invalid blob mem */ + + uint32_t blob_mem; + uint32_t blob_flags; + uint32_t nr_entries; + uint64_t blob_id; + uint64_t size; + + /* sizeof(nr_entries * virtio_gpu_mem_entry) bytes follow */ +} end_packed_struct; + +/* VIRTIO_GPU_CMD_SET_SCANOUT_BLOB */ + +begin_packed_struct struct virtio_gpu_set_scanout_blob +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + uint32_t scanout_id; + uint32_t resource_id; + uint32_t width; + uint32_t height; + uint32_t format; + uint32_t padding; + uint32_t strides[4]; + uint32_t offsets[4]; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB */ + +begin_packed_struct struct virtio_gpu_resource_map_blob +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t padding; + uint64_t offset; +} end_packed_struct; + +/* 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 + +begin_packed_struct struct virtio_gpu_resp_map_info +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t map_info; + uint32_t padding; +} end_packed_struct; + +/* VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB */ + +begin_packed_struct struct virtio_gpu_resource_unmap_blob +{ + struct virtio_gpu_ctrl_hdr hdr; + uint32_t resource_id; + uint32_t padding; +} end_packed_struct; + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int virtio_register_gpu_driver(void); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_DRIVERS_VIRTIO_GPU */ +#endif /* __DRIVERS_VIRTIO_VIRTIO_GPU_H */ diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index ea2e07bc80..d9db6c0304 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -29,6 +29,7 @@ #include #include "virtio-blk.h" +#include "virtio-gpu.h" #include "virtio-net.h" #include "virtio-rng.h" #include "virtio-serial.h" @@ -125,6 +126,14 @@ void virtio_register_drivers(void) } #endif +#ifdef CONFIG_DRIVERS_VIRTIO_GPU + ret = virtio_register_gpu_driver(); + if (ret < 0) + { + vrterr("virtio_register_gpu_driver failed, ret=%d\n", ret); + } +#endif + #ifdef CONFIG_DRIVERS_VIRTIO_NET ret = virtio_register_net_driver(); if (ret < 0)