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