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 #if _BYTE_ORDER == _BIG_ENDIAN 65 #define VTGPU_FORMAT VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM 66 #else 67 #define VTGPU_FORMAT VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM 68 #endif 69 70 struct vtgpu_softc { 71 /* Must be first so we can cast from info -> softc */ 72 struct fb_info vtgpu_fb_info; 73 struct virtio_gpu_config vtgpu_gpucfg; 74 75 device_t vtgpu_dev; 76 uint64_t vtgpu_features; 77 78 struct virtqueue *vtgpu_ctrl_vq; 79 80 uint64_t vtgpu_next_fence; 81 82 bool vtgpu_have_fb_info; 83 }; 84 85 static int vtgpu_modevent(module_t, int, void *); 86 87 static int vtgpu_probe(device_t); 88 static int vtgpu_attach(device_t); 89 static int vtgpu_detach(device_t); 90 91 static int vtgpu_negotiate_features(struct vtgpu_softc *); 92 static int vtgpu_setup_features(struct vtgpu_softc *); 93 static void vtgpu_read_config(struct vtgpu_softc *, 94 struct virtio_gpu_config *); 95 static int vtgpu_alloc_virtqueue(struct vtgpu_softc *); 96 static int vtgpu_get_display_info(struct vtgpu_softc *); 97 static int vtgpu_create_2d(struct vtgpu_softc *); 98 static int vtgpu_attach_backing(struct vtgpu_softc *); 99 static int vtgpu_set_scanout(struct vtgpu_softc *, uint32_t, uint32_t, 100 uint32_t, uint32_t); 101 static int vtgpu_transfer_to_host_2d(struct vtgpu_softc *, uint32_t, 102 uint32_t, uint32_t, uint32_t); 103 static int vtgpu_resource_flush(struct vtgpu_softc *, uint32_t, uint32_t, 104 uint32_t, uint32_t); 105 106 static vd_blank_t vtgpu_fb_blank; 107 static vd_bitblt_text_t vtgpu_fb_bitblt_text; 108 static vd_bitblt_bmp_t vtgpu_fb_bitblt_bitmap; 109 static vd_drawrect_t vtgpu_fb_drawrect; 110 static vd_setpixel_t vtgpu_fb_setpixel; 111 static vd_bitblt_argb_t vtgpu_fb_bitblt_argb; 112 113 static struct vt_driver vtgpu_fb_driver = { 114 .vd_name = "virtio_gpu", 115 .vd_init = vt_fb_init, 116 .vd_fini = vt_fb_fini, 117 .vd_blank = vtgpu_fb_blank, 118 .vd_bitblt_text = vtgpu_fb_bitblt_text, 119 .vd_invalidate_text = vt_fb_invalidate_text, 120 .vd_bitblt_bmp = vtgpu_fb_bitblt_bitmap, 121 .vd_bitblt_argb = vtgpu_fb_bitblt_argb, 122 .vd_drawrect = vtgpu_fb_drawrect, 123 .vd_setpixel = vtgpu_fb_setpixel, 124 .vd_postswitch = vt_fb_postswitch, 125 .vd_priority = VD_PRIORITY_GENERIC+10, 126 .vd_fb_ioctl = vt_fb_ioctl, 127 .vd_fb_mmap = NULL, /* No mmap as we need to signal the host */ 128 .vd_suspend = vt_fb_suspend, 129 .vd_resume = vt_fb_resume, 130 }; 131 132 VT_DRIVER_DECLARE(vt_vtgpu, vtgpu_fb_driver); 133 134 static void 135 vtgpu_fb_blank(struct vt_device *vd, term_color_t color) 136 { 137 struct vtgpu_softc *sc; 138 struct fb_info *info; 139 140 info = vd->vd_softc; 141 sc = (struct vtgpu_softc *)info; 142 143 vt_fb_blank(vd, color); 144 145 vtgpu_transfer_to_host_2d(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 146 sc->vtgpu_fb_info.fb_height); 147 vtgpu_resource_flush(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 148 sc->vtgpu_fb_info.fb_height); 149 } 150 151 static void 152 vtgpu_fb_bitblt_text(struct vt_device *vd, const struct vt_window *vw, 153 const term_rect_t *area) 154 { 155 struct vtgpu_softc *sc; 156 struct fb_info *info; 157 int x, y, width, height; 158 159 info = vd->vd_softc; 160 sc = (struct vtgpu_softc *)info; 161 162 vt_fb_bitblt_text(vd, vw, area); 163 164 x = area->tr_begin.tp_col * vw->vw_font->vf_width + vw->vw_draw_area.tr_begin.tp_col; 165 y = area->tr_begin.tp_row * vw->vw_font->vf_height + vw->vw_draw_area.tr_begin.tp_row; 166 width = area->tr_end.tp_col * vw->vw_font->vf_width + vw->vw_draw_area.tr_begin.tp_col - x; 167 height = area->tr_end.tp_row * vw->vw_font->vf_height + vw->vw_draw_area.tr_begin.tp_row - y; 168 169 vtgpu_transfer_to_host_2d(sc, x, y, width, height); 170 vtgpu_resource_flush(sc, x, y, width, height); 171 } 172 173 static void 174 vtgpu_fb_bitblt_bitmap(struct vt_device *vd, const struct vt_window *vw, 175 const uint8_t *pattern, const uint8_t *mask, 176 unsigned int width, unsigned int height, 177 unsigned int x, unsigned int y, term_color_t fg, term_color_t bg) 178 { 179 struct vtgpu_softc *sc; 180 struct fb_info *info; 181 182 info = vd->vd_softc; 183 sc = (struct vtgpu_softc *)info; 184 185 vt_fb_bitblt_bitmap(vd, vw, pattern, mask, width, height, x, y, fg, bg); 186 187 vtgpu_transfer_to_host_2d(sc, x, y, width, height); 188 vtgpu_resource_flush(sc, x, y, width, height); 189 } 190 191 static int 192 vtgpu_fb_bitblt_argb(struct vt_device *vd, const struct vt_window *vw, 193 const uint8_t *argb, 194 unsigned int width, unsigned int height, 195 unsigned int x, unsigned int y) 196 { 197 198 return (EOPNOTSUPP); 199 } 200 201 static void 202 vtgpu_fb_drawrect(struct vt_device *vd, int x1, int y1, int x2, int y2, 203 int fill, term_color_t color) 204 { 205 struct vtgpu_softc *sc; 206 struct fb_info *info; 207 int width, height; 208 209 info = vd->vd_softc; 210 sc = (struct vtgpu_softc *)info; 211 212 vt_fb_drawrect(vd, x1, y1, x2, y2, fill, color); 213 214 width = x2 - x1 + 1; 215 height = y2 - y1 + 1; 216 vtgpu_transfer_to_host_2d(sc, x1, y1, width, height); 217 vtgpu_resource_flush(sc, x1, y1, width, height); 218 } 219 220 static void 221 vtgpu_fb_setpixel(struct vt_device *vd, int x, int y, term_color_t color) 222 { 223 struct vtgpu_softc *sc; 224 struct fb_info *info; 225 226 info = vd->vd_softc; 227 sc = (struct vtgpu_softc *)info; 228 229 vt_fb_setpixel(vd, x, y, color); 230 231 vtgpu_transfer_to_host_2d(sc, x, y, 1, 1); 232 vtgpu_resource_flush(sc, x, y, 1, 1); 233 } 234 235 static struct virtio_feature_desc vtgpu_feature_desc[] = { 236 { VIRTIO_GPU_F_VIRGL, "VirGL" }, 237 { VIRTIO_GPU_F_EDID, "EDID" }, 238 { VIRTIO_GPU_F_RESOURCE_UUID, "ResUUID" }, 239 { VIRTIO_GPU_F_RESOURCE_BLOB, "ResBlob" }, 240 { VIRTIO_GPU_F_CONTEXT_INIT, "ContextInit" }, 241 { 0, NULL } 242 }; 243 244 static device_method_t vtgpu_methods[] = { 245 /* Device methods. */ 246 DEVMETHOD(device_probe, vtgpu_probe), 247 DEVMETHOD(device_attach, vtgpu_attach), 248 DEVMETHOD(device_detach, vtgpu_detach), 249 250 DEVMETHOD_END 251 }; 252 253 static driver_t vtgpu_driver = { 254 "vtgpu", 255 vtgpu_methods, 256 sizeof(struct vtgpu_softc) 257 }; 258 259 VIRTIO_DRIVER_MODULE(virtio_gpu, vtgpu_driver, vtgpu_modevent, NULL); 260 MODULE_VERSION(virtio_gpu, 1); 261 MODULE_DEPEND(virtio_gpu, virtio, 1, 1, 1); 262 263 VIRTIO_SIMPLE_PNPINFO(virtio_gpu, VIRTIO_ID_GPU, 264 "VirtIO GPU"); 265 266 static int 267 vtgpu_modevent(module_t mod, int type, void *unused) 268 { 269 int error; 270 271 switch (type) { 272 case MOD_LOAD: 273 case MOD_QUIESCE: 274 case MOD_UNLOAD: 275 case MOD_SHUTDOWN: 276 error = 0; 277 break; 278 default: 279 error = EOPNOTSUPP; 280 break; 281 } 282 283 return (error); 284 } 285 286 static int 287 vtgpu_probe(device_t dev) 288 { 289 return (VIRTIO_SIMPLE_PROBE(dev, virtio_gpu)); 290 } 291 292 static int 293 vtgpu_attach(device_t dev) 294 { 295 struct vtgpu_softc *sc; 296 int error; 297 298 sc = device_get_softc(dev); 299 sc->vtgpu_have_fb_info = false; 300 sc->vtgpu_dev = dev; 301 sc->vtgpu_next_fence = 1; 302 virtio_set_feature_desc(dev, vtgpu_feature_desc); 303 304 error = vtgpu_setup_features(sc); 305 if (error != 0) { 306 device_printf(dev, "cannot setup features\n"); 307 goto fail; 308 } 309 310 vtgpu_read_config(sc, &sc->vtgpu_gpucfg); 311 312 error = vtgpu_alloc_virtqueue(sc); 313 if (error != 0) { 314 device_printf(dev, "cannot allocate virtqueue\n"); 315 goto fail; 316 } 317 318 virtio_setup_intr(dev, INTR_TYPE_TTY); 319 320 /* Read the device info to get the display size */ 321 error = vtgpu_get_display_info(sc); 322 if (error != 0) { 323 goto fail; 324 } 325 326 /* 327 * TODO: This doesn't need to be contigmalloc as we 328 * can use scatter-gather lists. 329 */ 330 sc->vtgpu_fb_info.fb_vbase = (vm_offset_t)contigmalloc( 331 sc->vtgpu_fb_info.fb_size, M_DEVBUF, M_WAITOK|M_ZERO, 0, ~0, 4, 0); 332 sc->vtgpu_fb_info.fb_pbase = pmap_kextract(sc->vtgpu_fb_info.fb_vbase); 333 334 /* Create the 2d resource */ 335 error = vtgpu_create_2d(sc); 336 if (error != 0) { 337 goto fail; 338 } 339 340 /* Attach the backing memory */ 341 error = vtgpu_attach_backing(sc); 342 if (error != 0) { 343 goto fail; 344 } 345 346 /* Set the scanout to link the framebuffer to the display scanout */ 347 error = vtgpu_set_scanout(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 348 sc->vtgpu_fb_info.fb_height); 349 if (error != 0) { 350 goto fail; 351 } 352 353 vt_allocate(&vtgpu_fb_driver, &sc->vtgpu_fb_info); 354 sc->vtgpu_have_fb_info = true; 355 356 error = vtgpu_transfer_to_host_2d(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 357 sc->vtgpu_fb_info.fb_height); 358 if (error != 0) 359 goto fail; 360 error = vtgpu_resource_flush(sc, 0, 0, sc->vtgpu_fb_info.fb_width, 361 sc->vtgpu_fb_info.fb_height); 362 363 fail: 364 if (error != 0) 365 vtgpu_detach(dev); 366 367 return (error); 368 } 369 370 static int 371 vtgpu_detach(device_t dev) 372 { 373 struct vtgpu_softc *sc; 374 375 sc = device_get_softc(dev); 376 if (sc->vtgpu_have_fb_info) 377 vt_deallocate(&vtgpu_fb_driver, &sc->vtgpu_fb_info); 378 if (sc->vtgpu_fb_info.fb_vbase != 0) { 379 MPASS(sc->vtgpu_fb_info.fb_size != 0); 380 free((void *)sc->vtgpu_fb_info.fb_vbase, 381 M_DEVBUF); 382 } 383 384 /* TODO: Tell the host we are detaching */ 385 386 return (0); 387 } 388 389 static int 390 vtgpu_negotiate_features(struct vtgpu_softc *sc) 391 { 392 device_t dev; 393 uint64_t features; 394 395 dev = sc->vtgpu_dev; 396 features = VTGPU_FEATURES; 397 398 sc->vtgpu_features = virtio_negotiate_features(dev, features); 399 return (virtio_finalize_features(dev)); 400 } 401 402 static int 403 vtgpu_setup_features(struct vtgpu_softc *sc) 404 { 405 int error; 406 407 error = vtgpu_negotiate_features(sc); 408 if (error != 0) 409 return (error); 410 411 return (0); 412 } 413 414 static void 415 vtgpu_read_config(struct vtgpu_softc *sc, 416 struct virtio_gpu_config *gpucfg) 417 { 418 device_t dev; 419 420 dev = sc->vtgpu_dev; 421 422 bzero(gpucfg, sizeof(struct virtio_gpu_config)); 423 424 #define VTGPU_GET_CONFIG(_dev, _field, _cfg) \ 425 virtio_read_device_config(_dev, \ 426 offsetof(struct virtio_gpu_config, _field), \ 427 &(_cfg)->_field, sizeof((_cfg)->_field)) \ 428 429 VTGPU_GET_CONFIG(dev, events_read, gpucfg); 430 VTGPU_GET_CONFIG(dev, events_clear, gpucfg); 431 VTGPU_GET_CONFIG(dev, num_scanouts, gpucfg); 432 VTGPU_GET_CONFIG(dev, num_capsets, gpucfg); 433 434 #undef VTGPU_GET_CONFIG 435 } 436 437 static int 438 vtgpu_alloc_virtqueue(struct vtgpu_softc *sc) 439 { 440 device_t dev; 441 struct vq_alloc_info vq_info[2]; 442 int nvqs; 443 444 dev = sc->vtgpu_dev; 445 nvqs = 1; 446 447 VQ_ALLOC_INFO_INIT(&vq_info[0], 0, NULL, sc, &sc->vtgpu_ctrl_vq, 448 "%s control", device_get_nameunit(dev)); 449 450 return (virtio_alloc_virtqueues(dev, nvqs, vq_info)); 451 } 452 453 static int 454 vtgpu_req_resp2(struct vtgpu_softc *sc, void *req1, size_t req1len, 455 void *req2, size_t req2len, void *resp, size_t resplen) 456 { 457 struct sglist sg; 458 struct sglist_seg segs[3]; 459 int error, rcount; 460 461 sglist_init(&sg, 3, segs); 462 463 rcount = 1; 464 error = sglist_append(&sg, req1, req1len); 465 if (error != 0) { 466 device_printf(sc->vtgpu_dev, 467 "Unable to append the request to the sglist: %d\n", 468 error); 469 return (error); 470 } 471 if (req2 != NULL) { 472 error = sglist_append(&sg, req2, req2len); 473 if (error != 0) { 474 device_printf(sc->vtgpu_dev, 475 "Unable to append the request to the sglist: %d\n", 476 error); 477 return (error); 478 } 479 rcount++; 480 } 481 error = sglist_append(&sg, resp, resplen); 482 if (error != 0) { 483 device_printf(sc->vtgpu_dev, 484 "Unable to append the response buffer to the sglist: %d\n", 485 error); 486 return (error); 487 } 488 error = virtqueue_enqueue(sc->vtgpu_ctrl_vq, resp, &sg, rcount, 1); 489 if (error != 0) { 490 device_printf(sc->vtgpu_dev, "Enqueue failed: %d\n", error); 491 return (error); 492 } 493 494 virtqueue_notify(sc->vtgpu_ctrl_vq); 495 virtqueue_poll(sc->vtgpu_ctrl_vq, NULL); 496 497 return (0); 498 } 499 500 static int 501 vtgpu_req_resp(struct vtgpu_softc *sc, void *req, size_t reqlen, 502 void *resp, size_t resplen) 503 { 504 return (vtgpu_req_resp2(sc, req, reqlen, NULL, 0, resp, resplen)); 505 } 506 507 static int 508 vtgpu_get_display_info(struct vtgpu_softc *sc) 509 { 510 struct { 511 struct virtio_gpu_ctrl_hdr req; 512 char pad; 513 struct virtio_gpu_resp_display_info resp; 514 } s = { 0 }; 515 int error; 516 517 s.req.type = htole32(VIRTIO_GPU_CMD_GET_DISPLAY_INFO); 518 s.req.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 519 s.req.fence_id = htole64(atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 520 521 error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 522 sizeof(s.resp)); 523 if (error != 0) 524 return (error); 525 526 for (int i = 0; i < sc->vtgpu_gpucfg.num_scanouts; i++) { 527 if (s.resp.pmodes[i].enabled != 0) 528 MPASS(i == 0); 529 sc->vtgpu_fb_info.fb_name = 530 device_get_nameunit(sc->vtgpu_dev); 531 532 sc->vtgpu_fb_info.fb_width = 533 le32toh(s.resp.pmodes[i].r.width); 534 sc->vtgpu_fb_info.fb_height = 535 le32toh(s.resp.pmodes[i].r.height); 536 /* 32 bits per pixel */ 537 sc->vtgpu_fb_info.fb_bpp = 32; 538 sc->vtgpu_fb_info.fb_depth = 32; 539 sc->vtgpu_fb_info.fb_size = sc->vtgpu_fb_info.fb_width * 540 sc->vtgpu_fb_info.fb_height * 4; 541 sc->vtgpu_fb_info.fb_stride = 542 sc->vtgpu_fb_info.fb_width * 4; 543 return (0); 544 } 545 546 return (ENXIO); 547 } 548 549 static int 550 vtgpu_create_2d(struct vtgpu_softc *sc) 551 { 552 struct { 553 struct virtio_gpu_resource_create_2d req; 554 char pad; 555 struct virtio_gpu_ctrl_hdr resp; 556 } s = { 0 }; 557 int error; 558 559 s.req.hdr.type = htole32(VIRTIO_GPU_CMD_RESOURCE_CREATE_2D); 560 s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 561 s.req.hdr.fence_id = htole64( 562 atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 563 564 s.req.resource_id = htole32(VTGPU_RESOURCE_ID); 565 s.req.format = htole32(VTGPU_FORMAT); 566 s.req.width = htole32(sc->vtgpu_fb_info.fb_width); 567 s.req.height = htole32(sc->vtgpu_fb_info.fb_height); 568 569 error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 570 sizeof(s.resp)); 571 if (error != 0) 572 return (error); 573 574 if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 575 device_printf(sc->vtgpu_dev, "Invalid response type %x\n", 576 le32toh(s.resp.type)); 577 return (EINVAL); 578 } 579 580 return (0); 581 } 582 583 static int 584 vtgpu_attach_backing(struct vtgpu_softc *sc) 585 { 586 struct { 587 /* 588 * Split the backing and mem request arguments as some 589 * hypervisors, e.g. Parallels Desktop, don't work when 590 * they are enqueued together. 591 */ 592 struct { 593 struct virtio_gpu_resource_attach_backing backing; 594 char pad; 595 struct virtio_gpu_mem_entry mem; 596 } req; 597 char pad; 598 struct virtio_gpu_ctrl_hdr resp; 599 } s = { 0 }; 600 int error; 601 602 s.req.backing.hdr.type = 603 htole32(VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING); 604 s.req.backing.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 605 s.req.backing.hdr.fence_id = htole64( 606 atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 607 608 s.req.backing.resource_id = htole32(VTGPU_RESOURCE_ID); 609 s.req.backing.nr_entries = htole32(1); 610 611 s.req.mem.addr = htole64(sc->vtgpu_fb_info.fb_pbase); 612 s.req.mem.length = htole32(sc->vtgpu_fb_info.fb_size); 613 614 error = vtgpu_req_resp2(sc, &s.req.backing, sizeof(s.req.backing), 615 &s.req.mem, sizeof(s.req.mem), &s.resp, sizeof(s.resp)); 616 if (error != 0) 617 return (error); 618 619 if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 620 device_printf(sc->vtgpu_dev, "Invalid response type %x\n", 621 le32toh(s.resp.type)); 622 return (EINVAL); 623 } 624 625 return (0); 626 } 627 628 static int 629 vtgpu_set_scanout(struct vtgpu_softc *sc, uint32_t x, uint32_t y, 630 uint32_t width, uint32_t height) 631 { 632 struct { 633 struct virtio_gpu_set_scanout req; 634 char pad; 635 struct virtio_gpu_ctrl_hdr resp; 636 } s = { 0 }; 637 int error; 638 639 s.req.hdr.type = htole32(VIRTIO_GPU_CMD_SET_SCANOUT); 640 s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 641 s.req.hdr.fence_id = htole64( 642 atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 643 644 s.req.r.x = htole32(x); 645 s.req.r.y = htole32(y); 646 s.req.r.width = htole32(width); 647 s.req.r.height = htole32(height); 648 649 s.req.scanout_id = 0; 650 s.req.resource_id = htole32(VTGPU_RESOURCE_ID); 651 652 error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 653 sizeof(s.resp)); 654 if (error != 0) 655 return (error); 656 657 if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 658 device_printf(sc->vtgpu_dev, "Invalid response type %x\n", 659 le32toh(s.resp.type)); 660 return (EINVAL); 661 } 662 663 return (0); 664 } 665 666 static int 667 vtgpu_transfer_to_host_2d(struct vtgpu_softc *sc, uint32_t x, uint32_t y, 668 uint32_t width, uint32_t height) 669 { 670 struct { 671 struct virtio_gpu_transfer_to_host_2d req; 672 char pad; 673 struct virtio_gpu_ctrl_hdr resp; 674 } s = { 0 }; 675 int error; 676 677 s.req.hdr.type = htole32(VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D); 678 s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 679 s.req.hdr.fence_id = htole64( 680 atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 681 682 s.req.r.x = htole32(x); 683 s.req.r.y = htole32(y); 684 s.req.r.width = htole32(width); 685 s.req.r.height = htole32(height); 686 687 s.req.offset = htole64((y * sc->vtgpu_fb_info.fb_width + x) 688 * (sc->vtgpu_fb_info.fb_bpp / 8)); 689 s.req.resource_id = htole32(VTGPU_RESOURCE_ID); 690 691 error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 692 sizeof(s.resp)); 693 if (error != 0) 694 return (error); 695 696 if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 697 device_printf(sc->vtgpu_dev, "Invalid response type %x\n", 698 le32toh(s.resp.type)); 699 return (EINVAL); 700 } 701 702 return (0); 703 } 704 705 static int 706 vtgpu_resource_flush(struct vtgpu_softc *sc, uint32_t x, uint32_t y, 707 uint32_t width, uint32_t height) 708 { 709 struct { 710 struct virtio_gpu_resource_flush req; 711 char pad; 712 struct virtio_gpu_ctrl_hdr resp; 713 } s = { 0 }; 714 int error; 715 716 s.req.hdr.type = htole32(VIRTIO_GPU_CMD_RESOURCE_FLUSH); 717 s.req.hdr.flags = htole32(VIRTIO_GPU_FLAG_FENCE); 718 s.req.hdr.fence_id = htole64( 719 atomic_fetchadd_64(&sc->vtgpu_next_fence, 1)); 720 721 s.req.r.x = htole32(x); 722 s.req.r.y = htole32(y); 723 s.req.r.width = htole32(width); 724 s.req.r.height = htole32(height); 725 726 s.req.resource_id = htole32(VTGPU_RESOURCE_ID); 727 728 error = vtgpu_req_resp(sc, &s.req, sizeof(s.req), &s.resp, 729 sizeof(s.resp)); 730 if (error != 0) 731 return (error); 732 733 if (s.resp.type != htole32(VIRTIO_GPU_RESP_OK_NODATA)) { 734 device_printf(sc->vtgpu_dev, "Invalid response type %x\n", 735 le32toh(s.resp.type)); 736 return (EINVAL); 737 } 738 739 return (0); 740 } 741