xref: /freebsd/sys/dev/virtio/gpu/virtio_gpu.c (revision d1bdc2821fcd416ab9b238580386eb605a6128d0)
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