102f27066SAndrew Turner /*- 202f27066SAndrew Turner * SPDX-License-Identifier: BSD-2-Clause 302f27066SAndrew Turner * 402f27066SAndrew Turner * Copyright (c) 2013, Bryan Venteicher <bryanv@FreeBSD.org> 502f27066SAndrew Turner * All rights reserved. 602f27066SAndrew Turner * Copyright (c) 2023, Arm Ltd 702f27066SAndrew Turner * 802f27066SAndrew Turner * Redistribution and use in source and binary forms, with or without 902f27066SAndrew Turner * modification, are permitted provided that the following conditions 1002f27066SAndrew Turner * are met: 1102f27066SAndrew Turner * 1. Redistributions of source code must retain the above copyright 1202f27066SAndrew Turner * notice unmodified, this list of conditions, and the following 1302f27066SAndrew Turner * disclaimer. 1402f27066SAndrew Turner * 2. Redistributions in binary form must reproduce the above copyright 1502f27066SAndrew Turner * notice, this list of conditions and the following disclaimer in the 1602f27066SAndrew Turner * documentation and/or other materials provided with the distribution. 1702f27066SAndrew Turner * 1802f27066SAndrew Turner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1902f27066SAndrew Turner * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2002f27066SAndrew Turner * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2102f27066SAndrew Turner * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2202f27066SAndrew Turner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2302f27066SAndrew Turner * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2402f27066SAndrew Turner * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2502f27066SAndrew Turner * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2602f27066SAndrew Turner * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2702f27066SAndrew Turner * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2802f27066SAndrew Turner */ 2902f27066SAndrew Turner 3002f27066SAndrew Turner /* Driver for VirtIO GPU device. */ 3102f27066SAndrew Turner 3202f27066SAndrew Turner #include <sys/param.h> 3302f27066SAndrew Turner #include <sys/types.h> 3402f27066SAndrew Turner #include <sys/bus.h> 3502f27066SAndrew Turner #include <sys/callout.h> 3602f27066SAndrew Turner #include <sys/fbio.h> 3702f27066SAndrew Turner #include <sys/kernel.h> 3802f27066SAndrew Turner #include <sys/malloc.h> 3902f27066SAndrew Turner #include <sys/module.h> 4002f27066SAndrew Turner #include <sys/sglist.h> 4102f27066SAndrew Turner 4202f27066SAndrew Turner #include <machine/atomic.h> 4302f27066SAndrew Turner #include <machine/bus.h> 4402f27066SAndrew Turner #include <machine/resource.h> 4502f27066SAndrew Turner 4602f27066SAndrew Turner #include <vm/vm.h> 4702f27066SAndrew Turner #include <vm/pmap.h> 4802f27066SAndrew Turner 4902f27066SAndrew Turner #include <dev/virtio/virtio.h> 5002f27066SAndrew Turner #include <dev/virtio/virtqueue.h> 5102f27066SAndrew Turner #include <dev/virtio/gpu/virtio_gpu.h> 5202f27066SAndrew Turner 5302f27066SAndrew Turner #include <dev/vt/vt.h> 5402f27066SAndrew Turner #include <dev/vt/hw/fb/vt_fb.h> 5502f27066SAndrew Turner #include <dev/vt/colors/vt_termcolors.h> 5602f27066SAndrew Turner 5702f27066SAndrew Turner #include "fb_if.h" 5802f27066SAndrew Turner 5902f27066SAndrew Turner #define VTGPU_FEATURES 0 6002f27066SAndrew Turner 6102f27066SAndrew Turner /* The guest can allocate resource IDs, we only need one */ 6202f27066SAndrew Turner #define VTGPU_RESOURCE_ID 1 6302f27066SAndrew Turner 6402f27066SAndrew Turner struct vtgpu_softc { 6502f27066SAndrew Turner /* Must be first so we can cast from info -> softc */ 6602f27066SAndrew Turner struct fb_info vtgpu_fb_info; 6702f27066SAndrew Turner struct virtio_gpu_config vtgpu_gpucfg; 6802f27066SAndrew Turner 6902f27066SAndrew Turner device_t vtgpu_dev; 7002f27066SAndrew Turner uint64_t vtgpu_features; 7102f27066SAndrew Turner 7202f27066SAndrew Turner struct virtqueue *vtgpu_ctrl_vq; 7302f27066SAndrew Turner 7402f27066SAndrew Turner uint64_t vtgpu_next_fence; 7502f27066SAndrew Turner 7602f27066SAndrew Turner bool vtgpu_have_fb_info; 7702f27066SAndrew Turner }; 7802f27066SAndrew Turner 7902f27066SAndrew Turner static int vtgpu_modevent(module_t, int, void *); 8002f27066SAndrew Turner 8102f27066SAndrew Turner static int vtgpu_probe(device_t); 8202f27066SAndrew Turner static int vtgpu_attach(device_t); 8302f27066SAndrew Turner static int vtgpu_detach(device_t); 8402f27066SAndrew Turner 8502f27066SAndrew Turner static int vtgpu_negotiate_features(struct vtgpu_softc *); 8602f27066SAndrew Turner static int vtgpu_setup_features(struct vtgpu_softc *); 8702f27066SAndrew Turner static void vtgpu_read_config(struct vtgpu_softc *, 8802f27066SAndrew Turner struct virtio_gpu_config *); 8902f27066SAndrew Turner static int vtgpu_alloc_virtqueue(struct vtgpu_softc *); 9002f27066SAndrew Turner static int vtgpu_get_display_info(struct vtgpu_softc *); 9102f27066SAndrew Turner static int vtgpu_create_2d(struct vtgpu_softc *); 9202f27066SAndrew Turner static int vtgpu_attach_backing(struct vtgpu_softc *); 9302f27066SAndrew Turner static int vtgpu_set_scanout(struct vtgpu_softc *, uint32_t, uint32_t, 9402f27066SAndrew Turner uint32_t, uint32_t); 9502f27066SAndrew Turner static int vtgpu_transfer_to_host_2d(struct vtgpu_softc *, uint32_t, 9602f27066SAndrew Turner uint32_t, uint32_t, uint32_t); 9702f27066SAndrew Turner static int vtgpu_resource_flush(struct vtgpu_softc *, uint32_t, uint32_t, 9802f27066SAndrew Turner uint32_t, uint32_t); 9902f27066SAndrew Turner 10002f27066SAndrew Turner static vd_blank_t vtgpu_fb_blank; 10102f27066SAndrew Turner static vd_bitblt_text_t vtgpu_fb_bitblt_text; 10202f27066SAndrew Turner static vd_bitblt_bmp_t vtgpu_fb_bitblt_bitmap; 10302f27066SAndrew Turner static vd_drawrect_t vtgpu_fb_drawrect; 10402f27066SAndrew Turner static vd_setpixel_t vtgpu_fb_setpixel; 105b93028d8SEmmanuel Vadot static vd_bitblt_argb_t vtgpu_fb_bitblt_argb; 10602f27066SAndrew Turner 10702f27066SAndrew Turner static struct vt_driver vtgpu_fb_driver = { 10802f27066SAndrew Turner .vd_name = "virtio_gpu", 10902f27066SAndrew Turner .vd_init = vt_fb_init, 11002f27066SAndrew Turner .vd_fini = vt_fb_fini, 11102f27066SAndrew Turner .vd_blank = vtgpu_fb_blank, 11202f27066SAndrew Turner .vd_bitblt_text = vtgpu_fb_bitblt_text, 11302f27066SAndrew Turner .vd_invalidate_text = vt_fb_invalidate_text, 11402f27066SAndrew Turner .vd_bitblt_bmp = vtgpu_fb_bitblt_bitmap, 115b93028d8SEmmanuel Vadot .vd_bitblt_argb = vtgpu_fb_bitblt_argb, 11602f27066SAndrew Turner .vd_drawrect = vtgpu_fb_drawrect, 11702f27066SAndrew Turner .vd_setpixel = vtgpu_fb_setpixel, 11802f27066SAndrew Turner .vd_postswitch = vt_fb_postswitch, 11902f27066SAndrew Turner .vd_priority = VD_PRIORITY_GENERIC+10, 12002f27066SAndrew Turner .vd_fb_ioctl = vt_fb_ioctl, 12102f27066SAndrew Turner .vd_fb_mmap = NULL, /* No mmap as we need to signal the host */ 12202f27066SAndrew Turner .vd_suspend = vt_fb_suspend, 12302f27066SAndrew Turner .vd_resume = vt_fb_resume, 12402f27066SAndrew Turner }; 12502f27066SAndrew Turner 12602f27066SAndrew Turner VT_DRIVER_DECLARE(vt_vtgpu, vtgpu_fb_driver); 12702f27066SAndrew Turner 12802f27066SAndrew Turner static void 12902f27066SAndrew Turner vtgpu_fb_blank(struct vt_device *vd, term_color_t color) 13002f27066SAndrew Turner { 13102f27066SAndrew Turner struct vtgpu_softc *sc; 13202f27066SAndrew Turner struct fb_info *info; 13302f27066SAndrew Turner 13402f27066SAndrew Turner info = vd->vd_softc; 13502f27066SAndrew Turner sc = (struct vtgpu_softc *)info; 13602f27066SAndrew Turner 13702f27066SAndrew Turner vt_fb_blank(vd, color); 13802f27066SAndrew Turner 13902f27066SAndrew Turner vtgpu_transfer_to_host_2d(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 14002f27066SAndrew Turner sc->vtgpu_fb_info.fb_height); 14102f27066SAndrew Turner vtgpu_resource_flush(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 14202f27066SAndrew Turner sc->vtgpu_fb_info.fb_height); 14302f27066SAndrew Turner } 14402f27066SAndrew Turner 14502f27066SAndrew Turner static void 14602f27066SAndrew Turner vtgpu_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw, 14702f27066SAndrew Turner const term_rect_t *area) 14802f27066SAndrew Turner { 14902f27066SAndrew Turner struct vtgpu_softc *sc; 15002f27066SAndrew Turner struct fb_info *info; 15102f27066SAndrew Turner int x, y, width, height; 15202f27066SAndrew Turner 15302f27066SAndrew Turner info = vd->vd_softc; 15402f27066SAndrew Turner sc = (struct vtgpu_softc *)info; 15502f27066SAndrew Turner 15602f27066SAndrew Turner vt_fb_bitblt_text(vd, vw, area); 15702f27066SAndrew Turner 15802f27066SAndrew Turner x = area->tr_begin.tp_col * vw->vw_font->vf_width + vw->vw_draw_area.tr_begin.tp_col; 15902f27066SAndrew Turner y = area->tr_begin.tp_row * vw->vw_font->vf_height + vw->vw_draw_area.tr_begin.tp_row; 16002f27066SAndrew Turner width = area->tr_end.tp_col * vw->vw_font->vf_width + vw->vw_draw_area.tr_begin.tp_col - x; 16102f27066SAndrew Turner height = area->tr_end.tp_row * vw->vw_font->vf_height + vw->vw_draw_area.tr_begin.tp_row - y; 16202f27066SAndrew Turner 16302f27066SAndrew Turner vtgpu_transfer_to_host_2d(sc, x, y, width, height); 16402f27066SAndrew Turner vtgpu_resource_flush(sc, x, y, width, height); 16502f27066SAndrew Turner } 16602f27066SAndrew Turner 16702f27066SAndrew Turner static void 16802f27066SAndrew Turner vtgpu_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, 16902f27066SAndrew Turner const uint8_t *pattern, const uint8_t *mask, 17002f27066SAndrew Turner unsigned int width, unsigned int height, 17102f27066SAndrew Turner unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) 17202f27066SAndrew Turner { 17302f27066SAndrew Turner struct vtgpu_softc *sc; 17402f27066SAndrew Turner struct fb_info *info; 17502f27066SAndrew Turner 17602f27066SAndrew Turner info = vd->vd_softc; 17702f27066SAndrew Turner sc = (struct vtgpu_softc *)info; 17802f27066SAndrew Turner 17902f27066SAndrew Turner vt_fb_bitblt_bitmap(vd, vw, pattern, mask, width, height, x, y, fg, bg); 18002f27066SAndrew Turner 18102f27066SAndrew Turner vtgpu_transfer_to_host_2d(sc, x, y, width, height); 18202f27066SAndrew Turner vtgpu_resource_flush(sc, x, y, width, height); 18302f27066SAndrew Turner } 18402f27066SAndrew Turner 185b93028d8SEmmanuel Vadot static int 186b93028d8SEmmanuel Vadot vtgpu_fb_bitblt_argb(struct vt_device *vd, const struct vt_window *vw, 187b93028d8SEmmanuel Vadot const uint8_t *argb, 188b93028d8SEmmanuel Vadot unsigned int width, unsigned int height, 189b93028d8SEmmanuel Vadot unsigned int x, unsigned int y) 190b93028d8SEmmanuel Vadot { 191b93028d8SEmmanuel Vadot 192b93028d8SEmmanuel Vadot return (EOPNOTSUPP); 193b93028d8SEmmanuel Vadot } 194b93028d8SEmmanuel Vadot 19502f27066SAndrew Turner static void 19602f27066SAndrew Turner vtgpu_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, 19702f27066SAndrew Turner int fill, term_color_t color) 19802f27066SAndrew Turner { 19902f27066SAndrew Turner struct vtgpu_softc *sc; 20002f27066SAndrew Turner struct fb_info *info; 20102f27066SAndrew Turner int width, height; 20202f27066SAndrew Turner 20302f27066SAndrew Turner info = vd->vd_softc; 20402f27066SAndrew Turner sc = (struct vtgpu_softc *)info; 20502f27066SAndrew Turner 20602f27066SAndrew Turner vt_fb_drawrect(vd, x1, y1, x2, y2, fill, color); 20702f27066SAndrew Turner 20802f27066SAndrew Turner width = x2 - x1 + 1; 20902f27066SAndrew Turner height = y2 - y1 + 1; 21002f27066SAndrew Turner vtgpu_transfer_to_host_2d(sc, x1, y1, width, height); 21102f27066SAndrew Turner vtgpu_resource_flush(sc, x1, y1, width, height); 21202f27066SAndrew Turner } 21302f27066SAndrew Turner 21402f27066SAndrew Turner static void 21502f27066SAndrew Turner vtgpu_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) 21602f27066SAndrew Turner { 21702f27066SAndrew Turner struct vtgpu_softc *sc; 21802f27066SAndrew Turner struct fb_info *info; 21902f27066SAndrew Turner 22002f27066SAndrew Turner info = vd->vd_softc; 22102f27066SAndrew Turner sc = (struct vtgpu_softc *)info; 22202f27066SAndrew Turner 22302f27066SAndrew Turner vt_fb_setpixel(vd, x, y, color); 22402f27066SAndrew Turner 22502f27066SAndrew Turner vtgpu_transfer_to_host_2d(sc, x, y, 1, 1); 22602f27066SAndrew Turner vtgpu_resource_flush(sc, x, y, 1, 1); 22702f27066SAndrew Turner } 22802f27066SAndrew Turner 22902f27066SAndrew Turner static struct virtio_feature_desc vtgpu_feature_desc[] = { 23002f27066SAndrew Turner { VIRTIO_GPU_F_VIRGL, "VirGL" }, 23102f27066SAndrew Turner { VIRTIO_GPU_F_EDID, "EDID" }, 23202f27066SAndrew Turner { VIRTIO_GPU_F_RESOURCE_UUID, "ResUUID" }, 23302f27066SAndrew Turner { VIRTIO_GPU_F_RESOURCE_BLOB, "ResBlob" }, 23402f27066SAndrew Turner { VIRTIO_GPU_F_CONTEXT_INIT, "ContextInit" }, 23502f27066SAndrew Turner { 0, NULL } 23602f27066SAndrew Turner }; 23702f27066SAndrew Turner 23802f27066SAndrew Turner static device_method_t vtgpu_methods[] = { 23902f27066SAndrew Turner /* Device methods. */ 24002f27066SAndrew Turner DEVMETHOD(device_probe, vtgpu_probe), 24102f27066SAndrew Turner DEVMETHOD(device_attach, vtgpu_attach), 24202f27066SAndrew Turner DEVMETHOD(device_detach, vtgpu_detach), 24302f27066SAndrew Turner 24402f27066SAndrew Turner DEVMETHOD_END 24502f27066SAndrew Turner }; 24602f27066SAndrew Turner 24702f27066SAndrew Turner static driver_t vtgpu_driver = { 24802f27066SAndrew Turner "vtgpu", 24902f27066SAndrew Turner vtgpu_methods, 25002f27066SAndrew Turner sizeof(struct vtgpu_softc) 25102f27066SAndrew Turner }; 25202f27066SAndrew Turner 25302f27066SAndrew Turner VIRTIO_DRIVER_MODULE(virtio_gpu, vtgpu_driver, vtgpu_modevent, NULL); 25402f27066SAndrew Turner MODULE_VERSION(virtio_gpu, 1); 25502f27066SAndrew Turner MODULE_DEPEND(virtio_gpu, virtio, 1, 1, 1); 25602f27066SAndrew Turner 25702f27066SAndrew Turner VIRTIO_SIMPLE_PNPINFO(virtio_gpu, VIRTIO_ID_GPU, 25802f27066SAndrew Turner "VirtIO GPU"); 25902f27066SAndrew Turner 26002f27066SAndrew Turner static int 26102f27066SAndrew Turner vtgpu_modevent(module_t mod, int type, void *unused) 26202f27066SAndrew Turner { 26302f27066SAndrew Turner int error; 26402f27066SAndrew Turner 26502f27066SAndrew Turner switch (type) { 26602f27066SAndrew Turner case MOD_LOAD: 26702f27066SAndrew Turner case MOD_QUIESCE: 26802f27066SAndrew Turner case MOD_UNLOAD: 26902f27066SAndrew Turner case MOD_SHUTDOWN: 27002f27066SAndrew Turner error = 0; 27102f27066SAndrew Turner break; 27202f27066SAndrew Turner default: 27302f27066SAndrew Turner error = EOPNOTSUPP; 27402f27066SAndrew Turner break; 27502f27066SAndrew Turner } 27602f27066SAndrew Turner 27702f27066SAndrew Turner return (error); 27802f27066SAndrew Turner } 27902f27066SAndrew Turner 28002f27066SAndrew Turner static int 28102f27066SAndrew Turner vtgpu_probe(device_t dev) 28202f27066SAndrew Turner { 28302f27066SAndrew Turner return (VIRTIO_SIMPLE_PROBE(dev, virtio_gpu)); 28402f27066SAndrew Turner } 28502f27066SAndrew Turner 28602f27066SAndrew Turner static int 28702f27066SAndrew Turner vtgpu_attach(device_t dev) 28802f27066SAndrew Turner { 28902f27066SAndrew Turner struct vtgpu_softc *sc; 29002f27066SAndrew Turner int error; 29102f27066SAndrew Turner 29202f27066SAndrew Turner sc = device_get_softc(dev); 29302f27066SAndrew Turner sc->vtgpu_have_fb_info = false; 29402f27066SAndrew Turner sc->vtgpu_dev = dev; 29502f27066SAndrew Turner sc->vtgpu_next_fence = 1; 29602f27066SAndrew Turner virtio_set_feature_desc(dev, vtgpu_feature_desc); 29702f27066SAndrew Turner 29802f27066SAndrew Turner error = vtgpu_setup_features(sc); 29902f27066SAndrew Turner if (error != 0) { 30002f27066SAndrew Turner device_printf(dev, "cannot setup features\n"); 30102f27066SAndrew Turner goto fail; 30202f27066SAndrew Turner } 30302f27066SAndrew Turner 30402f27066SAndrew Turner vtgpu_read_config(sc, &sc->vtgpu_gpucfg); 30502f27066SAndrew Turner 30602f27066SAndrew Turner error = vtgpu_alloc_virtqueue(sc); 30702f27066SAndrew Turner if (error != 0) { 30802f27066SAndrew Turner device_printf(dev, "cannot allocate virtqueue\n"); 30902f27066SAndrew Turner goto fail; 31002f27066SAndrew Turner } 31102f27066SAndrew Turner 31202f27066SAndrew Turner virtio_setup_intr(dev, INTR_TYPE_TTY); 31302f27066SAndrew Turner 31402f27066SAndrew Turner /* Read the device info to get the display size */ 31502f27066SAndrew Turner error = vtgpu_get_display_info(sc); 31602f27066SAndrew Turner if (error != 0) { 31702f27066SAndrew Turner goto fail; 31802f27066SAndrew Turner } 31902f27066SAndrew Turner 32002f27066SAndrew Turner /* 32102f27066SAndrew Turner * TODO: This doesn't need to be contigmalloc as we 32202f27066SAndrew Turner * can use scatter-gather lists. 32302f27066SAndrew Turner */ 32402f27066SAndrew Turner sc->vtgpu_fb_info.fb_vbase = (vm_offset_t)contigmalloc( 32502f27066SAndrew Turner sc->vtgpu_fb_info.fb_size, M_DEVBUF, M_WAITOK|M_ZERO, 0, ~0, 4, 0); 32602f27066SAndrew Turner sc->vtgpu_fb_info.fb_pbase = pmap_kextract(sc->vtgpu_fb_info.fb_vbase); 32702f27066SAndrew Turner 32802f27066SAndrew Turner /* Create the 2d resource */ 32902f27066SAndrew Turner error = vtgpu_create_2d(sc); 33002f27066SAndrew Turner if (error != 0) { 33102f27066SAndrew Turner goto fail; 33202f27066SAndrew Turner } 33302f27066SAndrew Turner 33402f27066SAndrew Turner /* Attach the backing memory */ 33502f27066SAndrew Turner error = vtgpu_attach_backing(sc); 33602f27066SAndrew Turner if (error != 0) { 33702f27066SAndrew Turner goto fail; 33802f27066SAndrew Turner } 33902f27066SAndrew Turner 34002f27066SAndrew Turner /* Set the scanout to link the framebuffer to the display scanout */ 34102f27066SAndrew Turner error = vtgpu_set_scanout(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 34202f27066SAndrew Turner sc->vtgpu_fb_info.fb_height); 34302f27066SAndrew Turner if (error != 0) { 34402f27066SAndrew Turner goto fail; 34502f27066SAndrew Turner } 34602f27066SAndrew Turner 34702f27066SAndrew Turner vt_allocate(&vtgpu_fb_driver, &sc->vtgpu_fb_info); 34802f27066SAndrew Turner sc->vtgpu_have_fb_info = true; 34902f27066SAndrew Turner 35002f27066SAndrew Turner error = vtgpu_transfer_to_host_2d(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 35102f27066SAndrew Turner sc->vtgpu_fb_info.fb_height); 35202f27066SAndrew Turner if (error != 0) 35302f27066SAndrew Turner goto fail; 35402f27066SAndrew Turner error = vtgpu_resource_flush(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 35502f27066SAndrew Turner sc->vtgpu_fb_info.fb_height); 35602f27066SAndrew Turner 35702f27066SAndrew Turner fail: 35802f27066SAndrew Turner if (error != 0) 35902f27066SAndrew Turner vtgpu_detach(dev); 36002f27066SAndrew Turner 36102f27066SAndrew Turner return (error); 36202f27066SAndrew Turner } 36302f27066SAndrew Turner 36402f27066SAndrew Turner static int 36502f27066SAndrew Turner vtgpu_detach(device_t dev) 36602f27066SAndrew Turner { 36702f27066SAndrew Turner struct vtgpu_softc *sc; 36802f27066SAndrew Turner 36902f27066SAndrew Turner sc = device_get_softc(dev); 37002f27066SAndrew Turner if (sc->vtgpu_have_fb_info) 37102f27066SAndrew Turner vt_deallocate(&vtgpu_fb_driver, &sc->vtgpu_fb_info); 37202f27066SAndrew Turner if (sc->vtgpu_fb_info.fb_vbase != 0) { 37302f27066SAndrew Turner MPASS(sc->vtgpu_fb_info.fb_size != 0); 374*d1bdc282SBjoern A. Zeeb free((void *)sc->vtgpu_fb_info.fb_vbase, 375*d1bdc282SBjoern A. Zeeb M_DEVBUF); 37602f27066SAndrew Turner } 37702f27066SAndrew Turner 37802f27066SAndrew Turner /* TODO: Tell the host we are detaching */ 37902f27066SAndrew Turner 38002f27066SAndrew Turner return (0); 38102f27066SAndrew Turner } 38202f27066SAndrew Turner 38302f27066SAndrew Turner static int 38402f27066SAndrew Turner vtgpu_negotiate_features(struct vtgpu_softc *sc) 38502f27066SAndrew Turner { 38602f27066SAndrew Turner device_t dev; 38702f27066SAndrew Turner uint64_t features; 38802f27066SAndrew Turner 38902f27066SAndrew Turner dev = sc->vtgpu_dev; 39002f27066SAndrew Turner features = VTGPU_FEATURES; 39102f27066SAndrew Turner 39202f27066SAndrew Turner sc->vtgpu_features = virtio_negotiate_features(dev, features); 39302f27066SAndrew Turner return (virtio_finalize_features(dev)); 39402f27066SAndrew Turner } 39502f27066SAndrew Turner 39602f27066SAndrew Turner static int 39702f27066SAndrew Turner vtgpu_setup_features(struct vtgpu_softc *sc) 39802f27066SAndrew Turner { 39902f27066SAndrew Turner int error; 40002f27066SAndrew Turner 40102f27066SAndrew Turner error = vtgpu_negotiate_features(sc); 40202f27066SAndrew Turner if (error != 0) 40302f27066SAndrew Turner return (error); 40402f27066SAndrew Turner 40502f27066SAndrew Turner return (0); 40602f27066SAndrew Turner } 40702f27066SAndrew Turner 40802f27066SAndrew Turner static void 40902f27066SAndrew Turner vtgpu_read_config(struct vtgpu_softc *sc, 41002f27066SAndrew Turner struct virtio_gpu_config *gpucfg) 41102f27066SAndrew Turner { 41202f27066SAndrew Turner device_t dev; 41302f27066SAndrew Turner 41402f27066SAndrew Turner dev = sc->vtgpu_dev; 41502f27066SAndrew Turner 41602f27066SAndrew Turner bzero(gpucfg, sizeof(struct virtio_gpu_config)); 41702f27066SAndrew Turner 41802f27066SAndrew Turner #define VTGPU_GET_CONFIG(_dev, _field, _cfg) \ 41902f27066SAndrew Turner virtio_read_device_config(_dev, \ 42002f27066SAndrew Turner offsetof(struct virtio_gpu_config, _field), \ 42102f27066SAndrew Turner &(_cfg)->_field, sizeof((_cfg)->_field)) \ 42202f27066SAndrew Turner 42302f27066SAndrew Turner VTGPU_GET_CONFIG(dev, events_read, gpucfg); 42402f27066SAndrew Turner VTGPU_GET_CONFIG(dev, events_clear, gpucfg); 42502f27066SAndrew Turner VTGPU_GET_CONFIG(dev, num_scanouts, gpucfg); 42602f27066SAndrew Turner VTGPU_GET_CONFIG(dev, num_capsets, gpucfg); 42702f27066SAndrew Turner 42802f27066SAndrew Turner #undef VTGPU_GET_CONFIG 42902f27066SAndrew Turner } 43002f27066SAndrew Turner 43102f27066SAndrew Turner static int 43202f27066SAndrew Turner vtgpu_alloc_virtqueue(struct vtgpu_softc *sc) 43302f27066SAndrew Turner { 43402f27066SAndrew Turner device_t dev; 43502f27066SAndrew Turner struct vq_alloc_info vq_info[2]; 43602f27066SAndrew Turner int nvqs; 43702f27066SAndrew Turner 43802f27066SAndrew Turner dev = sc->vtgpu_dev; 43902f27066SAndrew Turner nvqs = 1; 44002f27066SAndrew Turner 44102f27066SAndrew Turner VQ_ALLOC_INFO_INIT(&vq_info[0], 0, NULL, sc, &sc->vtgpu_ctrl_vq, 44202f27066SAndrew Turner "%s control", device_get_nameunit(dev)); 44302f27066SAndrew Turner 444180c0240SMina Galić return (virtio_alloc_virtqueues(dev, nvqs, vq_info)); 44502f27066SAndrew Turner } 44602f27066SAndrew Turner 44702f27066SAndrew Turner static int 44802f27066SAndrew Turner vtgpu_req_resp(struct vtgpu_softc *sc, void *req, size_t reqlen, 44902f27066SAndrew Turner void *resp, size_t resplen) 45002f27066SAndrew Turner { 45102f27066SAndrew Turner struct sglist sg; 45202f27066SAndrew Turner struct sglist_seg segs[2]; 45302f27066SAndrew Turner int error; 45402f27066SAndrew Turner 45502f27066SAndrew Turner sglist_init(&sg, 2, segs); 45602f27066SAndrew Turner 45702f27066SAndrew Turner error = sglist_append(&sg, req, reqlen); 45802f27066SAndrew Turner if (error != 0) { 45902f27066SAndrew Turner device_printf(sc->vtgpu_dev, 46002f27066SAndrew Turner "Unable to append the request to the sglist: %d\n", error); 46102f27066SAndrew Turner return (error); 46202f27066SAndrew Turner } 46302f27066SAndrew Turner error = sglist_append(&sg, resp, resplen); 46402f27066SAndrew Turner if (error != 0) { 46502f27066SAndrew Turner device_printf(sc->vtgpu_dev, 46602f27066SAndrew Turner "Unable to append the response buffer to the sglist: %d\n", 46702f27066SAndrew Turner error); 46802f27066SAndrew Turner return (error); 46902f27066SAndrew Turner } 47002f27066SAndrew Turner error = virtqueue_enqueue(sc->vtgpu_ctrl_vq, resp, &sg, 1, 1); 47102f27066SAndrew Turner if (error != 0) { 47202f27066SAndrew Turner device_printf(sc->vtgpu_dev, "Enqueue failed: %d\n", error); 47302f27066SAndrew Turner return (error); 47402f27066SAndrew Turner } 47502f27066SAndrew Turner 47602f27066SAndrew Turner virtqueue_notify(sc->vtgpu_ctrl_vq); 47702f27066SAndrew Turner virtqueue_poll(sc->vtgpu_ctrl_vq, NULL); 47802f27066SAndrew Turner 47902f27066SAndrew Turner return (0); 48002f27066SAndrew Turner } 48102f27066SAndrew Turner 48202f27066SAndrew Turner static int 48302f27066SAndrew Turner vtgpu_get_display_info(struct vtgpu_softc *sc) 48402f27066SAndrew Turner { 48502f27066SAndrew Turner struct { 48602f27066SAndrew Turner struct virtio_gpu_ctrl_hdr req; 48702f27066SAndrew Turner char pad; 48802f27066SAndrew Turner struct virtio_gpu_resp_display_info resp; 48902f27066SAndrew Turner } s = { 0 }; 49002f27066SAndrew Turner int error; 49102f27066SAndrew Turner 49202f27066SAndrew Turner s.req.type = htole32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO); 49302f27066SAndrew Turner s.req.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 49402f27066SAndrew Turner s.req.fence_id = htole64(atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 49502f27066SAndrew Turner 49602f27066SAndrew Turner error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 49702f27066SAndrew Turner sizeof(s.resp)); 49802f27066SAndrew Turner if (error != 0) 49902f27066SAndrew Turner return (error); 50002f27066SAndrew Turner 50102f27066SAndrew Turner for (int i = 0; i < sc->vtgpu_gpucfg.num_scanouts; i++) { 50202f27066SAndrew Turner if (s.resp.pmodes[i].enabled != 0) 50302f27066SAndrew Turner MPASS(i == 0); 50402f27066SAndrew Turner sc->vtgpu_fb_info.fb_name = 50502f27066SAndrew Turner device_get_nameunit(sc->vtgpu_dev); 50602f27066SAndrew Turner 50702f27066SAndrew Turner sc->vtgpu_fb_info.fb_width = 50802f27066SAndrew Turner le32toh(s.resp.pmodes[i].r.width); 50902f27066SAndrew Turner sc->vtgpu_fb_info.fb_height = 51002f27066SAndrew Turner le32toh(s.resp.pmodes[i].r.height); 51102f27066SAndrew Turner /* 32 bits per pixel */ 51202f27066SAndrew Turner sc->vtgpu_fb_info.fb_bpp = 32; 51302f27066SAndrew Turner sc->vtgpu_fb_info.fb_depth = 32; 51402f27066SAndrew Turner sc->vtgpu_fb_info.fb_size = sc->vtgpu_fb_info.fb_width * 51502f27066SAndrew Turner sc->vtgpu_fb_info.fb_height * 4; 51602f27066SAndrew Turner sc->vtgpu_fb_info.fb_stride = 51702f27066SAndrew Turner sc->vtgpu_fb_info.fb_width * 4; 51802f27066SAndrew Turner return (0); 51902f27066SAndrew Turner } 52002f27066SAndrew Turner 52102f27066SAndrew Turner return (ENXIO); 52202f27066SAndrew Turner } 52302f27066SAndrew Turner 52402f27066SAndrew Turner static int 52502f27066SAndrew Turner vtgpu_create_2d(struct vtgpu_softc *sc) 52602f27066SAndrew Turner { 52702f27066SAndrew Turner struct { 52802f27066SAndrew Turner struct virtio_gpu_resource_create_2d req; 52902f27066SAndrew Turner char pad; 53002f27066SAndrew Turner struct virtio_gpu_ctrl_hdr resp; 53102f27066SAndrew Turner } s = { 0 }; 53202f27066SAndrew Turner int error; 53302f27066SAndrew Turner 53402f27066SAndrew Turner s.req.hdr.type = htole32(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D); 53502f27066SAndrew Turner s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 53602f27066SAndrew Turner s.req.hdr.fence_id = htole64( 53702f27066SAndrew Turner atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 53802f27066SAndrew Turner 53902f27066SAndrew Turner s.req.resource_id = htole32(VTGPU_RESOURCE_ID); 54002f27066SAndrew Turner s.req.format = htole32(VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM); 54102f27066SAndrew Turner s.req.width = htole32(sc->vtgpu_fb_info.fb_width); 54202f27066SAndrew Turner s.req.height = htole32(sc->vtgpu_fb_info.fb_height); 54302f27066SAndrew Turner 54402f27066SAndrew Turner error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 54502f27066SAndrew Turner sizeof(s.resp)); 54602f27066SAndrew Turner if (error != 0) 54702f27066SAndrew Turner return (error); 54802f27066SAndrew Turner 54902f27066SAndrew Turner if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 55002f27066SAndrew Turner device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n", 55102f27066SAndrew Turner le32toh(s.resp.type)); 55202f27066SAndrew Turner return (EINVAL); 55302f27066SAndrew Turner } 55402f27066SAndrew Turner 55502f27066SAndrew Turner return (0); 55602f27066SAndrew Turner } 55702f27066SAndrew Turner 55802f27066SAndrew Turner static int 55902f27066SAndrew Turner vtgpu_attach_backing(struct vtgpu_softc *sc) 56002f27066SAndrew Turner { 56102f27066SAndrew Turner struct { 56202f27066SAndrew Turner struct { 56302f27066SAndrew Turner struct virtio_gpu_resource_attach_backing backing; 56402f27066SAndrew Turner struct virtio_gpu_mem_entry mem[1]; 56502f27066SAndrew Turner } req; 56602f27066SAndrew Turner char pad; 56702f27066SAndrew Turner struct virtio_gpu_ctrl_hdr resp; 56802f27066SAndrew Turner } s = { 0 }; 56902f27066SAndrew Turner int error; 57002f27066SAndrew Turner 57102f27066SAndrew Turner s.req.backing.hdr.type = 57202f27066SAndrew Turner htole32(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING); 57302f27066SAndrew Turner s.req.backing.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 57402f27066SAndrew Turner s.req.backing.hdr.fence_id = htole64( 57502f27066SAndrew Turner atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 57602f27066SAndrew Turner 57702f27066SAndrew Turner s.req.backing.resource_id = htole32(VTGPU_RESOURCE_ID); 57802f27066SAndrew Turner s.req.backing.nr_entries = htole32(1); 57902f27066SAndrew Turner 58043869351SAndrew Turner s.req.mem[0].addr = htole64(sc->vtgpu_fb_info.fb_pbase); 58102f27066SAndrew Turner s.req.mem[0].length = htole32(sc->vtgpu_fb_info.fb_size); 58202f27066SAndrew Turner 58302f27066SAndrew Turner error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 58402f27066SAndrew Turner sizeof(s.resp)); 58502f27066SAndrew Turner if (error != 0) 58602f27066SAndrew Turner return (error); 58702f27066SAndrew Turner 58802f27066SAndrew Turner if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 58902f27066SAndrew Turner device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n", 59002f27066SAndrew Turner le32toh(s.resp.type)); 59102f27066SAndrew Turner return (EINVAL); 59202f27066SAndrew Turner } 59302f27066SAndrew Turner 59402f27066SAndrew Turner return (0); 59502f27066SAndrew Turner } 59602f27066SAndrew Turner 59702f27066SAndrew Turner static int 59802f27066SAndrew Turner vtgpu_set_scanout(struct vtgpu_softc *sc, uint32_t x, uint32_t y, 59902f27066SAndrew Turner uint32_t width, uint32_t height) 60002f27066SAndrew Turner { 60102f27066SAndrew Turner struct { 60202f27066SAndrew Turner struct virtio_gpu_set_scanout req; 60302f27066SAndrew Turner char pad; 60402f27066SAndrew Turner struct virtio_gpu_ctrl_hdr resp; 60502f27066SAndrew Turner } s = { 0 }; 60602f27066SAndrew Turner int error; 60702f27066SAndrew Turner 60802f27066SAndrew Turner s.req.hdr.type = htole32(VIRTIO_GPU_CMD_SET_SCANOUT); 60902f27066SAndrew Turner s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 61002f27066SAndrew Turner s.req.hdr.fence_id = htole64( 61102f27066SAndrew Turner atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 61202f27066SAndrew Turner 61302f27066SAndrew Turner s.req.r.x = htole32(x); 61402f27066SAndrew Turner s.req.r.y = htole32(y); 61502f27066SAndrew Turner s.req.r.width = htole32(width); 61602f27066SAndrew Turner s.req.r.height = htole32(height); 61702f27066SAndrew Turner 61802f27066SAndrew Turner s.req.scanout_id = 0; 61902f27066SAndrew Turner s.req.resource_id = htole32(VTGPU_RESOURCE_ID); 62002f27066SAndrew Turner 62102f27066SAndrew Turner error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 62202f27066SAndrew Turner sizeof(s.resp)); 62302f27066SAndrew Turner if (error != 0) 62402f27066SAndrew Turner return (error); 62502f27066SAndrew Turner 62602f27066SAndrew Turner if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 62702f27066SAndrew Turner device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n", 62802f27066SAndrew Turner le32toh(s.resp.type)); 62902f27066SAndrew Turner return (EINVAL); 63002f27066SAndrew Turner } 63102f27066SAndrew Turner 63202f27066SAndrew Turner return (0); 63302f27066SAndrew Turner } 63402f27066SAndrew Turner 63502f27066SAndrew Turner static int 63602f27066SAndrew Turner vtgpu_transfer_to_host_2d(struct vtgpu_softc *sc, uint32_t x, uint32_t y, 63702f27066SAndrew Turner uint32_t width, uint32_t height) 63802f27066SAndrew Turner { 63902f27066SAndrew Turner struct { 64002f27066SAndrew Turner struct virtio_gpu_transfer_to_host_2d req; 64102f27066SAndrew Turner char pad; 64202f27066SAndrew Turner struct virtio_gpu_ctrl_hdr resp; 64302f27066SAndrew Turner } s = { 0 }; 64402f27066SAndrew Turner int error; 64502f27066SAndrew Turner 64602f27066SAndrew Turner s.req.hdr.type = htole32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D); 64702f27066SAndrew Turner s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 64802f27066SAndrew Turner s.req.hdr.fence_id = htole64( 64902f27066SAndrew Turner atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 65002f27066SAndrew Turner 65102f27066SAndrew Turner s.req.r.x = htole32(x); 65202f27066SAndrew Turner s.req.r.y = htole32(y); 65302f27066SAndrew Turner s.req.r.width = htole32(width); 65402f27066SAndrew Turner s.req.r.height = htole32(height); 65502f27066SAndrew Turner 65602f27066SAndrew Turner s.req.offset = htole64((y * sc->vtgpu_fb_info.fb_width + x) 65702f27066SAndrew Turner * (sc->vtgpu_fb_info.fb_bpp / 8)); 65802f27066SAndrew Turner s.req.resource_id = htole32(VTGPU_RESOURCE_ID); 65902f27066SAndrew Turner 66002f27066SAndrew Turner error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 66102f27066SAndrew Turner sizeof(s.resp)); 66202f27066SAndrew Turner if (error != 0) 66302f27066SAndrew Turner return (error); 66402f27066SAndrew Turner 66502f27066SAndrew Turner if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 66602f27066SAndrew Turner device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n", 66702f27066SAndrew Turner le32toh(s.resp.type)); 66802f27066SAndrew Turner return (EINVAL); 66902f27066SAndrew Turner } 67002f27066SAndrew Turner 67102f27066SAndrew Turner return (0); 67202f27066SAndrew Turner } 67302f27066SAndrew Turner 67402f27066SAndrew Turner static int 67502f27066SAndrew Turner vtgpu_resource_flush(struct vtgpu_softc *sc, uint32_t x, uint32_t y, 67602f27066SAndrew Turner uint32_t width, uint32_t height) 67702f27066SAndrew Turner { 67802f27066SAndrew Turner struct { 67902f27066SAndrew Turner struct virtio_gpu_resource_flush req; 68002f27066SAndrew Turner char pad; 68102f27066SAndrew Turner struct virtio_gpu_ctrl_hdr resp; 68202f27066SAndrew Turner } s = { 0 }; 68302f27066SAndrew Turner int error; 68402f27066SAndrew Turner 68502f27066SAndrew Turner s.req.hdr.type = htole32(VIRTIO_GPU_CMD_RESOURCE_FLUSH); 68602f27066SAndrew Turner s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 68702f27066SAndrew Turner s.req.hdr.fence_id = htole64( 68802f27066SAndrew Turner atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 68902f27066SAndrew Turner 69002f27066SAndrew Turner s.req.r.x = htole32(x); 69102f27066SAndrew Turner s.req.r.y = htole32(y); 69202f27066SAndrew Turner s.req.r.width = htole32(width); 69302f27066SAndrew Turner s.req.r.height = htole32(height); 69402f27066SAndrew Turner 69502f27066SAndrew Turner s.req.resource_id = htole32(VTGPU_RESOURCE_ID); 69602f27066SAndrew Turner 69702f27066SAndrew Turner error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 69802f27066SAndrew Turner sizeof(s.resp)); 69902f27066SAndrew Turner if (error != 0) 70002f27066SAndrew Turner return (error); 70102f27066SAndrew Turner 70202f27066SAndrew Turner if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 70302f27066SAndrew Turner device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n", 70402f27066SAndrew Turner le32toh(s.resp.type)); 70502f27066SAndrew Turner return (EINVAL); 70602f27066SAndrew Turner } 70702f27066SAndrew Turner 70802f27066SAndrew Turner return (0); 70902f27066SAndrew Turner } 710