xref: /freebsd/sys/dev/virtio/gpu/virtio_gpu.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2013, Bryan Venteicher <bryanv@FreeBSD.org>
5  * All rights reserved.
6  * Copyright (c) 2023, Arm Ltd
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice unmodified, this list of conditions, and the following
13  *    disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 /* Driver for VirtIO GPU device. */
31 
32 #include <sys/param.h>
33 #include <sys/types.h>
34 #include <sys/bus.h>
35 #include <sys/callout.h>
36 #include <sys/fbio.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/sglist.h>
41 
42 #include <machine/atomic.h>
43 #include <machine/bus.h>
44 #include <machine/resource.h>
45 
46 #include <vm/vm.h>
47 #include <vm/pmap.h>
48 
49 #include <dev/virtio/virtio.h>
50 #include <dev/virtio/virtqueue.h>
51 #include <dev/virtio/gpu/virtio_gpu.h>
52 
53 #include <dev/vt/vt.h>
54 #include <dev/vt/hw/fb/vt_fb.h>
55 #include <dev/vt/colors/vt_termcolors.h>
56 
57 #include "fb_if.h"
58 
59 #define VTGPU_FEATURES	0
60 
61 /* The guest can allocate resource IDs, we only need one */
62 #define	VTGPU_RESOURCE_ID	1
63 
64 struct vtgpu_softc {
65 	/* Must be first so we can cast from info -> softc */
66 	struct fb_info 		 vtgpu_fb_info;
67 	struct virtio_gpu_config vtgpu_gpucfg;
68 
69 	device_t		 vtgpu_dev;
70 	uint64_t		 vtgpu_features;
71 
72 	struct virtqueue	*vtgpu_ctrl_vq;
73 
74 	uint64_t		 vtgpu_next_fence;
75 
76 	bool			 vtgpu_have_fb_info;
77 };
78 
79 static int	vtgpu_modevent(module_t, int, void *);
80 
81 static int	vtgpu_probe(device_t);
82 static int	vtgpu_attach(device_t);
83 static int	vtgpu_detach(device_t);
84 
85 static int	vtgpu_negotiate_features(struct vtgpu_softc *);
86 static int	vtgpu_setup_features(struct vtgpu_softc *);
87 static void	vtgpu_read_config(struct vtgpu_softc *,
88 		    struct virtio_gpu_config *);
89 static int	vtgpu_alloc_virtqueue(struct vtgpu_softc *);
90 static int	vtgpu_get_display_info(struct vtgpu_softc *);
91 static int	vtgpu_create_2d(struct vtgpu_softc *);
92 static int	vtgpu_attach_backing(struct vtgpu_softc *);
93 static int	vtgpu_set_scanout(struct vtgpu_softc *, uint32_t, uint32_t,
94 		    uint32_t, uint32_t);
95 static int	vtgpu_transfer_to_host_2d(struct vtgpu_softc *, uint32_t,
96 		    uint32_t, uint32_t, uint32_t);
97 static int	vtgpu_resource_flush(struct vtgpu_softc *, uint32_t, uint32_t,
98 		    uint32_t, uint32_t);
99 
100 static vd_blank_t		vtgpu_fb_blank;
101 static vd_bitblt_text_t		vtgpu_fb_bitblt_text;
102 static vd_bitblt_bmp_t		vtgpu_fb_bitblt_bitmap;
103 static vd_drawrect_t		vtgpu_fb_drawrect;
104 static vd_setpixel_t		vtgpu_fb_setpixel;
105 
106 static struct vt_driver vtgpu_fb_driver = {
107 	.vd_name = "virtio_gpu",
108 	.vd_init = vt_fb_init,
109 	.vd_fini = vt_fb_fini,
110 	.vd_blank = vtgpu_fb_blank,
111 	.vd_bitblt_text = vtgpu_fb_bitblt_text,
112 	.vd_invalidate_text = vt_fb_invalidate_text,
113 	.vd_bitblt_bmp = vtgpu_fb_bitblt_bitmap,
114 	.vd_drawrect = vtgpu_fb_drawrect,
115 	.vd_setpixel = vtgpu_fb_setpixel,
116 	.vd_postswitch = vt_fb_postswitch,
117 	.vd_priority = VD_PRIORITY_GENERIC+10,
118 	.vd_fb_ioctl = vt_fb_ioctl,
119 	.vd_fb_mmap = NULL,	/* No mmap as we need to signal the host */
120 	.vd_suspend = vt_fb_suspend,
121 	.vd_resume = vt_fb_resume,
122 };
123 
124 VT_DRIVER_DECLARE(vt_vtgpu, vtgpu_fb_driver);
125 
126 static void
127 vtgpu_fb_blank(struct vt_device *vd, term_color_t color)
128 {
129 	struct vtgpu_softc *sc;
130 	struct fb_info *info;
131 
132 	info = vd->vd_softc;
133 	sc = (struct vtgpu_softc *)info;
134 
135 	vt_fb_blank(vd, color);
136 
137 	vtgpu_transfer_to_host_2d(sc, 0, 0, sc->vtgpu_fb_info.fb_width,
138 	    sc->vtgpu_fb_info.fb_height);
139 	vtgpu_resource_flush(sc, 0, 0, sc->vtgpu_fb_info.fb_width,
140 	    sc->vtgpu_fb_info.fb_height);
141 }
142 
143 static void
144 vtgpu_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw,
145     const term_rect_t *area)
146 {
147 	struct vtgpu_softc *sc;
148 	struct fb_info *info;
149 	int x, y, width, height;
150 
151 	info = vd->vd_softc;
152 	sc = (struct vtgpu_softc *)info;
153 
154 	vt_fb_bitblt_text(vd, vw, area);
155 
156 	x = area->tr_begin.tp_col * vw->vw_font->vf_width + vw->vw_draw_area.tr_begin.tp_col;
157 	y = area->tr_begin.tp_row * vw->vw_font->vf_height + vw->vw_draw_area.tr_begin.tp_row;
158 	width = area->tr_end.tp_col * vw->vw_font->vf_width + vw->vw_draw_area.tr_begin.tp_col - x;
159 	height = area->tr_end.tp_row * vw->vw_font->vf_height + vw->vw_draw_area.tr_begin.tp_row - y;
160 
161 	vtgpu_transfer_to_host_2d(sc, x, y, width, height);
162 	vtgpu_resource_flush(sc, x, y, width, height);
163 }
164 
165 static void
166 vtgpu_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw,
167     const uint8_t *pattern, const uint8_t *mask,
168     unsigned int width, unsigned int height,
169     unsigned int x, unsigned int y, term_color_t fg, term_color_t bg)
170 {
171 	struct vtgpu_softc *sc;
172 	struct fb_info *info;
173 
174 	info = vd->vd_softc;
175 	sc = (struct vtgpu_softc *)info;
176 
177 	vt_fb_bitblt_bitmap(vd, vw, pattern, mask, width, height, x, y, fg, bg);
178 
179 	vtgpu_transfer_to_host_2d(sc, x, y, width, height);
180 	vtgpu_resource_flush(sc, x, y, width, height);
181 }
182 
183 static void
184 vtgpu_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2,
185     int fill, term_color_t color)
186 {
187 	struct vtgpu_softc *sc;
188 	struct fb_info *info;
189 	int width, height;
190 
191 	info = vd->vd_softc;
192 	sc = (struct vtgpu_softc *)info;
193 
194 	vt_fb_drawrect(vd, x1, y1, x2, y2, fill, color);
195 
196 	width = x2 - x1 + 1;
197 	height = y2 - y1 + 1;
198 	vtgpu_transfer_to_host_2d(sc, x1, y1, width, height);
199 	vtgpu_resource_flush(sc, x1, y1, width, height);
200 }
201 
202 static void
203 vtgpu_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color)
204 {
205 	struct vtgpu_softc *sc;
206 	struct fb_info *info;
207 
208 	info = vd->vd_softc;
209 	sc = (struct vtgpu_softc *)info;
210 
211 	vt_fb_setpixel(vd, x, y, color);
212 
213 	vtgpu_transfer_to_host_2d(sc, x, y, 1, 1);
214 	vtgpu_resource_flush(sc, x, y, 1, 1);
215 }
216 
217 static struct virtio_feature_desc vtgpu_feature_desc[] = {
218 	{ VIRTIO_GPU_F_VIRGL,		"VirGL"		},
219 	{ VIRTIO_GPU_F_EDID,		"EDID"		},
220 	{ VIRTIO_GPU_F_RESOURCE_UUID,	"ResUUID"	},
221 	{ VIRTIO_GPU_F_RESOURCE_BLOB,	"ResBlob"	},
222 	{ VIRTIO_GPU_F_CONTEXT_INIT,	"ContextInit"	},
223 	{ 0, NULL }
224 };
225 
226 static device_method_t vtgpu_methods[] = {
227 	/* Device methods. */
228 	DEVMETHOD(device_probe,		vtgpu_probe),
229 	DEVMETHOD(device_attach,	vtgpu_attach),
230 	DEVMETHOD(device_detach,	vtgpu_detach),
231 
232 	DEVMETHOD_END
233 };
234 
235 static driver_t vtgpu_driver = {
236 	"vtgpu",
237 	vtgpu_methods,
238 	sizeof(struct vtgpu_softc)
239 };
240 
241 VIRTIO_DRIVER_MODULE(virtio_gpu, vtgpu_driver, vtgpu_modevent, NULL);
242 MODULE_VERSION(virtio_gpu, 1);
243 MODULE_DEPEND(virtio_gpu, virtio, 1, 1, 1);
244 
245 VIRTIO_SIMPLE_PNPINFO(virtio_gpu, VIRTIO_ID_GPU,
246     "VirtIO GPU");
247 
248 static int
249 vtgpu_modevent(module_t mod, int type, void *unused)
250 {
251 	int error;
252 
253 	switch (type) {
254 	case MOD_LOAD:
255 	case MOD_QUIESCE:
256 	case MOD_UNLOAD:
257 	case MOD_SHUTDOWN:
258 		error = 0;
259 		break;
260 	default:
261 		error = EOPNOTSUPP;
262 		break;
263 	}
264 
265 	return (error);
266 }
267 
268 static int
269 vtgpu_probe(device_t dev)
270 {
271 	return (VIRTIO_SIMPLE_PROBE(dev, virtio_gpu));
272 }
273 
274 static int
275 vtgpu_attach(device_t dev)
276 {
277 	struct vtgpu_softc *sc;
278 	int error;
279 
280 	sc = device_get_softc(dev);
281 	sc->vtgpu_have_fb_info = false;
282 	sc->vtgpu_dev = dev;
283 	sc->vtgpu_next_fence = 1;
284 	virtio_set_feature_desc(dev, vtgpu_feature_desc);
285 
286 	error = vtgpu_setup_features(sc);
287 	if (error != 0) {
288 		device_printf(dev, "cannot setup features\n");
289 		goto fail;
290 	}
291 
292 	vtgpu_read_config(sc, &sc->vtgpu_gpucfg);
293 
294 	error = vtgpu_alloc_virtqueue(sc);
295 	if (error != 0) {
296 		device_printf(dev, "cannot allocate virtqueue\n");
297 		goto fail;
298 	}
299 
300 	virtio_setup_intr(dev, INTR_TYPE_TTY);
301 
302 	/* Read the device info to get the display size */
303 	error = vtgpu_get_display_info(sc);
304 	if (error != 0) {
305 		goto fail;
306 	}
307 
308 	/*
309 	 * TODO: This doesn't need to be contigmalloc as we
310 	 * can use scatter-gather lists.
311 	 */
312 	sc->vtgpu_fb_info.fb_vbase = (vm_offset_t)contigmalloc(
313 	    sc->vtgpu_fb_info.fb_size, M_DEVBUF, M_WAITOK|M_ZERO, 0, ~0, 4, 0);
314 	sc->vtgpu_fb_info.fb_pbase = pmap_kextract(sc->vtgpu_fb_info.fb_vbase);
315 
316 	/* Create the 2d resource */
317 	error = vtgpu_create_2d(sc);
318 	if (error != 0) {
319 		goto fail;
320 	}
321 
322 	/* Attach the backing memory */
323 	error = vtgpu_attach_backing(sc);
324 	if (error != 0) {
325 		goto fail;
326 	}
327 
328 	/* Set the scanout to link the framebuffer to the display scanout */
329 	error = vtgpu_set_scanout(sc, 0, 0, sc->vtgpu_fb_info.fb_width,
330 	    sc->vtgpu_fb_info.fb_height);
331 	if (error != 0) {
332 		goto fail;
333 	}
334 
335 	vt_allocate(&vtgpu_fb_driver, &sc->vtgpu_fb_info);
336 	sc->vtgpu_have_fb_info = true;
337 
338 	error = vtgpu_transfer_to_host_2d(sc, 0, 0, sc->vtgpu_fb_info.fb_width,
339 	    sc->vtgpu_fb_info.fb_height);
340 	if (error != 0)
341 		goto fail;
342 	error = vtgpu_resource_flush(sc, 0, 0, sc->vtgpu_fb_info.fb_width,
343 	    sc->vtgpu_fb_info.fb_height);
344 
345 fail:
346 	if (error != 0)
347 		vtgpu_detach(dev);
348 
349 	return (error);
350 }
351 
352 static int
353 vtgpu_detach(device_t dev)
354 {
355 	struct vtgpu_softc *sc;
356 
357 	sc = device_get_softc(dev);
358 	if (sc->vtgpu_have_fb_info)
359 		vt_deallocate(&vtgpu_fb_driver, &sc->vtgpu_fb_info);
360 	if (sc->vtgpu_fb_info.fb_vbase != 0) {
361 		MPASS(sc->vtgpu_fb_info.fb_size != 0);
362 		contigfree((void *)sc->vtgpu_fb_info.fb_vbase,
363 		    sc->vtgpu_fb_info.fb_size, M_DEVBUF);
364 	}
365 
366 	/* TODO: Tell the host we are detaching */
367 
368 	return (0);
369 }
370 
371 static int
372 vtgpu_negotiate_features(struct vtgpu_softc *sc)
373 {
374 	device_t dev;
375 	uint64_t features;
376 
377 	dev = sc->vtgpu_dev;
378 	features = VTGPU_FEATURES;
379 
380 	sc->vtgpu_features = virtio_negotiate_features(dev, features);
381 	return (virtio_finalize_features(dev));
382 }
383 
384 static int
385 vtgpu_setup_features(struct vtgpu_softc *sc)
386 {
387 	int error;
388 
389 	error = vtgpu_negotiate_features(sc);
390 	if (error != 0)
391 		return (error);
392 
393 	return (0);
394 }
395 
396 static void
397 vtgpu_read_config(struct vtgpu_softc *sc,
398     struct virtio_gpu_config *gpucfg)
399 {
400 	device_t dev;
401 
402 	dev = sc->vtgpu_dev;
403 
404 	bzero(gpucfg, sizeof(struct virtio_gpu_config));
405 
406 #define VTGPU_GET_CONFIG(_dev, _field, _cfg)			\
407 	virtio_read_device_config(_dev,				\
408 	    offsetof(struct virtio_gpu_config, _field),	\
409 	    &(_cfg)->_field, sizeof((_cfg)->_field))		\
410 
411 	VTGPU_GET_CONFIG(dev, events_read, gpucfg);
412 	VTGPU_GET_CONFIG(dev, events_clear, gpucfg);
413 	VTGPU_GET_CONFIG(dev, num_scanouts, gpucfg);
414 	VTGPU_GET_CONFIG(dev, num_capsets, gpucfg);
415 
416 #undef VTGPU_GET_CONFIG
417 }
418 
419 static int
420 vtgpu_alloc_virtqueue(struct vtgpu_softc *sc)
421 {
422 	device_t dev;
423 	struct vq_alloc_info vq_info[2];
424 	int nvqs;
425 
426 	dev = sc->vtgpu_dev;
427 	nvqs = 1;
428 
429 	VQ_ALLOC_INFO_INIT(&vq_info[0], 0, NULL, sc, &sc->vtgpu_ctrl_vq,
430 	    "%s control", device_get_nameunit(dev));
431 
432 	return (virtio_alloc_virtqueues(dev, 0, nvqs, vq_info));
433 }
434 
435 static int
436 vtgpu_req_resp(struct vtgpu_softc *sc, void *req, size_t reqlen,
437     void *resp, size_t resplen)
438 {
439 	struct sglist sg;
440 	struct sglist_seg segs[2];
441 	int error;
442 
443 	sglist_init(&sg, 2, segs);
444 
445 	error = sglist_append(&sg, req, reqlen);
446 	if (error != 0) {
447 		device_printf(sc->vtgpu_dev,
448 		    "Unable to append the request to the sglist: %d\n", error);
449 		return (error);
450 	}
451 	error = sglist_append(&sg, resp, resplen);
452 	if (error != 0) {
453 		device_printf(sc->vtgpu_dev,
454 		    "Unable to append the response buffer to the sglist: %d\n",
455 		    error);
456 		return (error);
457 	}
458 	error = virtqueue_enqueue(sc->vtgpu_ctrl_vq, resp, &sg, 1, 1);
459 	if (error != 0) {
460 		device_printf(sc->vtgpu_dev, "Enqueue failed: %d\n", error);
461 		return (error);
462 	}
463 
464 	virtqueue_notify(sc->vtgpu_ctrl_vq);
465 	virtqueue_poll(sc->vtgpu_ctrl_vq, NULL);
466 
467 	return (0);
468 }
469 
470 static int
471 vtgpu_get_display_info(struct vtgpu_softc *sc)
472 {
473 	struct {
474 		struct virtio_gpu_ctrl_hdr req;
475 		char pad;
476 		struct virtio_gpu_resp_display_info resp;
477 	} s = { 0 };
478 	int error;
479 
480 	s.req.type = htole32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO);
481 	s.req.flags = htole32(VIRTIO_GPU_FLAG_FENCE);
482 	s.req.fence_id = htole64(atomic_fetchadd_64(&sc->vtgpu_next_fence, 1));
483 
484 	error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp,
485 	    sizeof(s.resp));
486 	if (error != 0)
487 		return (error);
488 
489 	for (int i = 0; i < sc->vtgpu_gpucfg.num_scanouts; i++) {
490 		if (s.resp.pmodes[i].enabled != 0)
491 			MPASS(i == 0);
492 			sc->vtgpu_fb_info.fb_name =
493 			    device_get_nameunit(sc->vtgpu_dev);
494 
495 			sc->vtgpu_fb_info.fb_width =
496 			    le32toh(s.resp.pmodes[i].r.width);
497 			sc->vtgpu_fb_info.fb_height =
498 			    le32toh(s.resp.pmodes[i].r.height);
499 			/* 32 bits per pixel */
500 			sc->vtgpu_fb_info.fb_bpp = 32;
501 			sc->vtgpu_fb_info.fb_depth = 32;
502 			sc->vtgpu_fb_info.fb_size = sc->vtgpu_fb_info.fb_width *
503 			    sc->vtgpu_fb_info.fb_height * 4;
504 			sc->vtgpu_fb_info.fb_stride =
505 			    sc->vtgpu_fb_info.fb_width * 4;
506 			return (0);
507 	}
508 
509 	return (ENXIO);
510 }
511 
512 static int
513 vtgpu_create_2d(struct vtgpu_softc *sc)
514 {
515 	struct {
516 		struct virtio_gpu_resource_create_2d req;
517 		char pad;
518 		struct virtio_gpu_ctrl_hdr resp;
519 	} s = { 0 };
520 	int error;
521 
522 	s.req.hdr.type = htole32(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D);
523 	s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE);
524 	s.req.hdr.fence_id = htole64(
525 	    atomic_fetchadd_64(&sc->vtgpu_next_fence, 1));
526 
527 	s.req.resource_id = htole32(VTGPU_RESOURCE_ID);
528 	s.req.format = htole32(VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM);
529 	s.req.width = htole32(sc->vtgpu_fb_info.fb_width);
530 	s.req.height = htole32(sc->vtgpu_fb_info.fb_height);
531 
532 	error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp,
533 	    sizeof(s.resp));
534 	if (error != 0)
535 		return (error);
536 
537 	if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) {
538 		device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n",
539 		    le32toh(s.resp.type));
540 		return (EINVAL);
541 	}
542 
543 	return (0);
544 }
545 
546 static int
547 vtgpu_attach_backing(struct vtgpu_softc *sc)
548 {
549 	struct {
550 		struct {
551 			struct virtio_gpu_resource_attach_backing backing;
552 			struct virtio_gpu_mem_entry mem[1];
553 		} req;
554 		char pad;
555 		struct virtio_gpu_ctrl_hdr resp;
556 	} s = { 0 };
557 	int error;
558 
559 	s.req.backing.hdr.type =
560 	    htole32(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING);
561 	s.req.backing.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE);
562 	s.req.backing.hdr.fence_id = htole64(
563 	    atomic_fetchadd_64(&sc->vtgpu_next_fence, 1));
564 
565 	s.req.backing.resource_id = htole32(VTGPU_RESOURCE_ID);
566 	s.req.backing.nr_entries = htole32(1);
567 
568 	s.req.mem[0].addr = htole32(sc->vtgpu_fb_info.fb_pbase);
569 	s.req.mem[0].length = htole32(sc->vtgpu_fb_info.fb_size);
570 
571 	error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp,
572 	    sizeof(s.resp));
573 	if (error != 0)
574 		return (error);
575 
576 	if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) {
577 		device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n",
578 		    le32toh(s.resp.type));
579 		return (EINVAL);
580 	}
581 
582 	return (0);
583 }
584 
585 static int
586 vtgpu_set_scanout(struct vtgpu_softc *sc, uint32_t x, uint32_t y,
587     uint32_t width, uint32_t height)
588 {
589 	struct {
590 		struct virtio_gpu_set_scanout req;
591 		char pad;
592 		struct virtio_gpu_ctrl_hdr resp;
593 	} s = { 0 };
594 	int error;
595 
596 	s.req.hdr.type = htole32(VIRTIO_GPU_CMD_SET_SCANOUT);
597 	s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE);
598 	s.req.hdr.fence_id = htole64(
599 	    atomic_fetchadd_64(&sc->vtgpu_next_fence, 1));
600 
601 	s.req.r.x = htole32(x);
602 	s.req.r.y = htole32(y);
603 	s.req.r.width = htole32(width);
604 	s.req.r.height = htole32(height);
605 
606 	s.req.scanout_id = 0;
607 	s.req.resource_id = htole32(VTGPU_RESOURCE_ID);
608 
609 	error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp,
610 	    sizeof(s.resp));
611 	if (error != 0)
612 		return (error);
613 
614 	if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) {
615 		device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n",
616 		    le32toh(s.resp.type));
617 		return (EINVAL);
618 	}
619 
620 	return (0);
621 }
622 
623 static int
624 vtgpu_transfer_to_host_2d(struct vtgpu_softc *sc, uint32_t x, uint32_t y,
625     uint32_t width, uint32_t height)
626 {
627 	struct {
628 		struct virtio_gpu_transfer_to_host_2d req;
629 		char pad;
630 		struct virtio_gpu_ctrl_hdr resp;
631 	} s = { 0 };
632 	int error;
633 
634 	s.req.hdr.type = htole32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D);
635 	s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE);
636 	s.req.hdr.fence_id = htole64(
637 	    atomic_fetchadd_64(&sc->vtgpu_next_fence, 1));
638 
639 	s.req.r.x = htole32(x);
640 	s.req.r.y = htole32(y);
641 	s.req.r.width = htole32(width);
642 	s.req.r.height = htole32(height);
643 
644 	s.req.offset = htole64((y * sc->vtgpu_fb_info.fb_width + x)
645 	 * (sc->vtgpu_fb_info.fb_bpp / 8));
646 	s.req.resource_id = htole32(VTGPU_RESOURCE_ID);
647 
648 	error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp,
649 	    sizeof(s.resp));
650 	if (error != 0)
651 		return (error);
652 
653 	if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) {
654 		device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n",
655 		    le32toh(s.resp.type));
656 		return (EINVAL);
657 	}
658 
659 	return (0);
660 }
661 
662 static int
663 vtgpu_resource_flush(struct vtgpu_softc *sc, uint32_t x, uint32_t y,
664     uint32_t width, uint32_t height)
665 {
666 	struct {
667 		struct virtio_gpu_resource_flush req;
668 		char pad;
669 		struct virtio_gpu_ctrl_hdr resp;
670 	} s = { 0 };
671 	int error;
672 
673 	s.req.hdr.type = htole32(VIRTIO_GPU_CMD_RESOURCE_FLUSH);
674 	s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE);
675 	s.req.hdr.fence_id = htole64(
676 	    atomic_fetchadd_64(&sc->vtgpu_next_fence, 1));
677 
678 	s.req.r.x = htole32(x);
679 	s.req.r.y = htole32(y);
680 	s.req.r.width = htole32(width);
681 	s.req.r.height = htole32(height);
682 
683 	s.req.resource_id = htole32(VTGPU_RESOURCE_ID);
684 
685 	error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp,
686 	    sizeof(s.resp));
687 	if (error != 0)
688 		return (error);
689 
690 	if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) {
691 		device_printf(sc->vtgpu_dev, "Invalid reponse type %x\n",
692 		    le32toh(s.resp.type));
693 		return (EINVAL);
694 	}
695 
696 	return (0);
697 }
698