1a9994687SGerd Hoffmann /* 2a9994687SGerd Hoffmann * display support for mdev based vgpu devices 3a9994687SGerd Hoffmann * 4a9994687SGerd Hoffmann * Copyright Red Hat, Inc. 2017 5a9994687SGerd Hoffmann * 6a9994687SGerd Hoffmann * Authors: 7a9994687SGerd Hoffmann * Gerd Hoffmann 8a9994687SGerd Hoffmann * 9a9994687SGerd Hoffmann * This work is licensed under the terms of the GNU GPL, version 2. See 10a9994687SGerd Hoffmann * the COPYING file in the top-level directory. 11a9994687SGerd Hoffmann */ 12a9994687SGerd Hoffmann 13a9994687SGerd Hoffmann #include "qemu/osdep.h" 14a9994687SGerd Hoffmann #include <linux/vfio.h> 15a9994687SGerd Hoffmann #include <sys/ioctl.h> 16a9994687SGerd Hoffmann 17a9994687SGerd Hoffmann #include "sysemu/sysemu.h" 18a9994687SGerd Hoffmann #include "ui/console.h" 19a9994687SGerd Hoffmann #include "qapi/error.h" 20a9994687SGerd Hoffmann #include "pci.h" 21a9994687SGerd Hoffmann 228b818e05SGerd Hoffmann #ifndef DRM_PLANE_TYPE_PRIMARY 238b818e05SGerd Hoffmann # define DRM_PLANE_TYPE_PRIMARY 1 248b818e05SGerd Hoffmann # define DRM_PLANE_TYPE_CURSOR 2 258b818e05SGerd Hoffmann #endif 268b818e05SGerd Hoffmann 278b818e05SGerd Hoffmann static void vfio_display_update_cursor(VFIODMABuf *dmabuf, 288b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info *plane) 298b818e05SGerd Hoffmann { 308b818e05SGerd Hoffmann if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) { 318b818e05SGerd Hoffmann dmabuf->pos_x = plane->x_pos; 328b818e05SGerd Hoffmann dmabuf->pos_y = plane->y_pos; 338b818e05SGerd Hoffmann dmabuf->pos_updates++; 348b818e05SGerd Hoffmann } 358b818e05SGerd Hoffmann if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) { 368b818e05SGerd Hoffmann dmabuf->hot_x = plane->x_hot; 378b818e05SGerd Hoffmann dmabuf->hot_y = plane->y_hot; 388b818e05SGerd Hoffmann dmabuf->hot_updates++; 398b818e05SGerd Hoffmann } 408b818e05SGerd Hoffmann } 418b818e05SGerd Hoffmann 428b818e05SGerd Hoffmann static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, 438b818e05SGerd Hoffmann uint32_t plane_type) 448b818e05SGerd Hoffmann { 458b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 468b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info plane; 478b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 488b818e05SGerd Hoffmann int fd, ret; 498b818e05SGerd Hoffmann 508b818e05SGerd Hoffmann memset(&plane, 0, sizeof(plane)); 518b818e05SGerd Hoffmann plane.argsz = sizeof(plane); 528b818e05SGerd Hoffmann plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF; 538b818e05SGerd Hoffmann plane.drm_plane_type = plane_type; 548b818e05SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 558b818e05SGerd Hoffmann if (ret < 0) { 568b818e05SGerd Hoffmann return NULL; 578b818e05SGerd Hoffmann } 588b818e05SGerd Hoffmann if (!plane.drm_format || !plane.size) { 598b818e05SGerd Hoffmann return NULL; 608b818e05SGerd Hoffmann } 618b818e05SGerd Hoffmann 628b818e05SGerd Hoffmann QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) { 638b818e05SGerd Hoffmann if (dmabuf->dmabuf_id == plane.dmabuf_id) { 648b818e05SGerd Hoffmann /* found in list, move to head, return it */ 658b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 668b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 678b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 688b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 698b818e05SGerd Hoffmann } 708b818e05SGerd Hoffmann return dmabuf; 718b818e05SGerd Hoffmann } 728b818e05SGerd Hoffmann } 738b818e05SGerd Hoffmann 748b818e05SGerd Hoffmann fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id); 758b818e05SGerd Hoffmann if (fd < 0) { 768b818e05SGerd Hoffmann return NULL; 778b818e05SGerd Hoffmann } 788b818e05SGerd Hoffmann 798b818e05SGerd Hoffmann dmabuf = g_new0(VFIODMABuf, 1); 808b818e05SGerd Hoffmann dmabuf->dmabuf_id = plane.dmabuf_id; 818b818e05SGerd Hoffmann dmabuf->buf.width = plane.width; 828b818e05SGerd Hoffmann dmabuf->buf.height = plane.height; 838b818e05SGerd Hoffmann dmabuf->buf.stride = plane.stride; 848b818e05SGerd Hoffmann dmabuf->buf.fourcc = plane.drm_format; 858b818e05SGerd Hoffmann dmabuf->buf.fd = fd; 868b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 878b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 888b818e05SGerd Hoffmann } 898b818e05SGerd Hoffmann 908b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 918b818e05SGerd Hoffmann return dmabuf; 928b818e05SGerd Hoffmann } 938b818e05SGerd Hoffmann 948b818e05SGerd Hoffmann static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) 958b818e05SGerd Hoffmann { 968b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 978b818e05SGerd Hoffmann dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); 988b818e05SGerd Hoffmann close(dmabuf->buf.fd); 998b818e05SGerd Hoffmann g_free(dmabuf); 1008b818e05SGerd Hoffmann } 1018b818e05SGerd Hoffmann 1028b818e05SGerd Hoffmann static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) 1038b818e05SGerd Hoffmann { 1048b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 1058b818e05SGerd Hoffmann VFIODMABuf *dmabuf, *tmp; 1068b818e05SGerd Hoffmann uint32_t keep = 5; 1078b818e05SGerd Hoffmann 1088b818e05SGerd Hoffmann QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { 1098b818e05SGerd Hoffmann if (keep > 0) { 1108b818e05SGerd Hoffmann keep--; 1118b818e05SGerd Hoffmann continue; 1128b818e05SGerd Hoffmann } 1138b818e05SGerd Hoffmann assert(dmabuf != dpy->dmabuf.primary); 1148b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 1158b818e05SGerd Hoffmann } 1168b818e05SGerd Hoffmann } 1178b818e05SGerd Hoffmann 1188b818e05SGerd Hoffmann static void vfio_display_dmabuf_update(void *opaque) 1198b818e05SGerd Hoffmann { 1208b818e05SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 1218b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 1228b818e05SGerd Hoffmann VFIODMABuf *primary, *cursor; 1238b818e05SGerd Hoffmann bool free_bufs = false, new_cursor = false;; 1248b818e05SGerd Hoffmann 1258b818e05SGerd Hoffmann primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); 1268b818e05SGerd Hoffmann if (primary == NULL) { 1278b818e05SGerd Hoffmann return; 1288b818e05SGerd Hoffmann } 1298b818e05SGerd Hoffmann 1308b818e05SGerd Hoffmann if (dpy->dmabuf.primary != primary) { 1318b818e05SGerd Hoffmann dpy->dmabuf.primary = primary; 1328b818e05SGerd Hoffmann qemu_console_resize(dpy->con, 1338b818e05SGerd Hoffmann primary->buf.width, primary->buf.height); 1348b818e05SGerd Hoffmann dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); 1358b818e05SGerd Hoffmann free_bufs = true; 1368b818e05SGerd Hoffmann } 1378b818e05SGerd Hoffmann 1388b818e05SGerd Hoffmann cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); 1398b818e05SGerd Hoffmann if (dpy->dmabuf.cursor != cursor) { 1408b818e05SGerd Hoffmann dpy->dmabuf.cursor = cursor; 1418b818e05SGerd Hoffmann new_cursor = true; 1428b818e05SGerd Hoffmann free_bufs = true; 1438b818e05SGerd Hoffmann } 1448b818e05SGerd Hoffmann 1458b818e05SGerd Hoffmann if (cursor && (new_cursor || cursor->hot_updates)) { 1468b818e05SGerd Hoffmann bool have_hot = (cursor->hot_x != 0xffffffff && 1478b818e05SGerd Hoffmann cursor->hot_y != 0xffffffff); 1488b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, 1498b818e05SGerd Hoffmann cursor->hot_x, cursor->hot_y); 1508b818e05SGerd Hoffmann cursor->hot_updates = 0; 1518b818e05SGerd Hoffmann } else if (!cursor && new_cursor) { 1528b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); 1538b818e05SGerd Hoffmann } 1548b818e05SGerd Hoffmann 1558b818e05SGerd Hoffmann if (cursor && cursor->pos_updates) { 1568b818e05SGerd Hoffmann dpy_gl_cursor_position(dpy->con, 1578b818e05SGerd Hoffmann cursor->pos_x, 1588b818e05SGerd Hoffmann cursor->pos_y); 1598b818e05SGerd Hoffmann cursor->pos_updates = 0; 1608b818e05SGerd Hoffmann } 1618b818e05SGerd Hoffmann 1628b818e05SGerd Hoffmann dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); 1638b818e05SGerd Hoffmann 1648b818e05SGerd Hoffmann if (free_bufs) { 1658b818e05SGerd Hoffmann vfio_display_free_dmabufs(vdev); 1668b818e05SGerd Hoffmann } 1678b818e05SGerd Hoffmann } 1688b818e05SGerd Hoffmann 1698b818e05SGerd Hoffmann static const GraphicHwOps vfio_display_dmabuf_ops = { 1708b818e05SGerd Hoffmann .gfx_update = vfio_display_dmabuf_update, 1718b818e05SGerd Hoffmann }; 1728b818e05SGerd Hoffmann 1738b818e05SGerd Hoffmann static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) 1748b818e05SGerd Hoffmann { 1758b818e05SGerd Hoffmann if (!display_opengl) { 1768b818e05SGerd Hoffmann error_setg(errp, "vfio-display-dmabuf: opengl not available"); 1778b818e05SGerd Hoffmann return -1; 1788b818e05SGerd Hoffmann } 1798b818e05SGerd Hoffmann 1808b818e05SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 1818b818e05SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 1828b818e05SGerd Hoffmann &vfio_display_dmabuf_ops, 1838b818e05SGerd Hoffmann vdev); 1848b818e05SGerd Hoffmann return 0; 1858b818e05SGerd Hoffmann } 1868b818e05SGerd Hoffmann 1878b818e05SGerd Hoffmann static void vfio_display_dmabuf_exit(VFIODisplay *dpy) 1888b818e05SGerd Hoffmann { 1898b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 1908b818e05SGerd Hoffmann 1918b818e05SGerd Hoffmann if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { 1928b818e05SGerd Hoffmann return; 1938b818e05SGerd Hoffmann } 1948b818e05SGerd Hoffmann 1958b818e05SGerd Hoffmann while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { 1968b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 1978b818e05SGerd Hoffmann } 1988b818e05SGerd Hoffmann } 1998b818e05SGerd Hoffmann 20000195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 201*8983e3e3STina Zhang void vfio_display_reset(VFIOPCIDevice *vdev) 202*8983e3e3STina Zhang { 203*8983e3e3STina Zhang if (!vdev || !vdev->dpy || !vdev->dpy->con || 204*8983e3e3STina Zhang !vdev->dpy->dmabuf.primary) { 205*8983e3e3STina Zhang return; 206*8983e3e3STina Zhang } 207*8983e3e3STina Zhang 208*8983e3e3STina Zhang dpy_gl_scanout_disable(vdev->dpy->con); 209*8983e3e3STina Zhang vfio_display_dmabuf_exit(vdev->dpy); 210*8983e3e3STina Zhang dpy_gfx_update_full(vdev->dpy->con); 211*8983e3e3STina Zhang } 21200195ba7SGerd Hoffmann 21300195ba7SGerd Hoffmann static void vfio_display_region_update(void *opaque) 21400195ba7SGerd Hoffmann { 21500195ba7SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 21600195ba7SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 21700195ba7SGerd Hoffmann struct vfio_device_gfx_plane_info plane = { 21800195ba7SGerd Hoffmann .argsz = sizeof(plane), 21900195ba7SGerd Hoffmann .flags = VFIO_GFX_PLANE_TYPE_REGION 22000195ba7SGerd Hoffmann }; 22100195ba7SGerd Hoffmann pixman_format_code_t format; 22200195ba7SGerd Hoffmann int ret; 22300195ba7SGerd Hoffmann 22400195ba7SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 22500195ba7SGerd Hoffmann if (ret < 0) { 22600195ba7SGerd Hoffmann error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", 22700195ba7SGerd Hoffmann strerror(errno)); 22800195ba7SGerd Hoffmann return; 22900195ba7SGerd Hoffmann } 23000195ba7SGerd Hoffmann if (!plane.drm_format || !plane.size) { 23100195ba7SGerd Hoffmann return; 23200195ba7SGerd Hoffmann } 23300195ba7SGerd Hoffmann format = qemu_drm_format_to_pixman(plane.drm_format); 23400195ba7SGerd Hoffmann if (!format) { 23500195ba7SGerd Hoffmann return; 23600195ba7SGerd Hoffmann } 23700195ba7SGerd Hoffmann 23800195ba7SGerd Hoffmann if (dpy->region.buffer.size && 23900195ba7SGerd Hoffmann dpy->region.buffer.nr != plane.region_index) { 24000195ba7SGerd Hoffmann /* region changed */ 24100195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 24200195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 24300195ba7SGerd Hoffmann dpy->region.surface = NULL; 24400195ba7SGerd Hoffmann } 24500195ba7SGerd Hoffmann 24600195ba7SGerd Hoffmann if (dpy->region.surface && 24700195ba7SGerd Hoffmann (surface_width(dpy->region.surface) != plane.width || 24800195ba7SGerd Hoffmann surface_height(dpy->region.surface) != plane.height || 24900195ba7SGerd Hoffmann surface_format(dpy->region.surface) != format)) { 25000195ba7SGerd Hoffmann /* size changed */ 25100195ba7SGerd Hoffmann dpy->region.surface = NULL; 25200195ba7SGerd Hoffmann } 25300195ba7SGerd Hoffmann 25400195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 25500195ba7SGerd Hoffmann /* mmap region */ 25600195ba7SGerd Hoffmann ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, 25700195ba7SGerd Hoffmann &dpy->region.buffer, 25800195ba7SGerd Hoffmann plane.region_index, 25900195ba7SGerd Hoffmann "display"); 26000195ba7SGerd Hoffmann if (ret != 0) { 26100195ba7SGerd Hoffmann error_report("%s: vfio_region_setup(%d): %s", 26200195ba7SGerd Hoffmann __func__, plane.region_index, strerror(-ret)); 26300195ba7SGerd Hoffmann goto err; 26400195ba7SGerd Hoffmann } 26500195ba7SGerd Hoffmann ret = vfio_region_mmap(&dpy->region.buffer); 26600195ba7SGerd Hoffmann if (ret != 0) { 26700195ba7SGerd Hoffmann error_report("%s: vfio_region_mmap(%d): %s", __func__, 26800195ba7SGerd Hoffmann plane.region_index, strerror(-ret)); 26900195ba7SGerd Hoffmann goto err; 27000195ba7SGerd Hoffmann } 27100195ba7SGerd Hoffmann assert(dpy->region.buffer.mmaps[0].mmap != NULL); 27200195ba7SGerd Hoffmann } 27300195ba7SGerd Hoffmann 27400195ba7SGerd Hoffmann if (dpy->region.surface == NULL) { 27500195ba7SGerd Hoffmann /* create surface */ 27600195ba7SGerd Hoffmann dpy->region.surface = qemu_create_displaysurface_from 27700195ba7SGerd Hoffmann (plane.width, plane.height, format, 27800195ba7SGerd Hoffmann plane.stride, dpy->region.buffer.mmaps[0].mmap); 27900195ba7SGerd Hoffmann dpy_gfx_replace_surface(dpy->con, dpy->region.surface); 28000195ba7SGerd Hoffmann } 28100195ba7SGerd Hoffmann 28200195ba7SGerd Hoffmann /* full screen update */ 28300195ba7SGerd Hoffmann dpy_gfx_update(dpy->con, 0, 0, 28400195ba7SGerd Hoffmann surface_width(dpy->region.surface), 28500195ba7SGerd Hoffmann surface_height(dpy->region.surface)); 28600195ba7SGerd Hoffmann return; 28700195ba7SGerd Hoffmann 28800195ba7SGerd Hoffmann err: 28900195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 29000195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 29100195ba7SGerd Hoffmann } 29200195ba7SGerd Hoffmann 29300195ba7SGerd Hoffmann static const GraphicHwOps vfio_display_region_ops = { 29400195ba7SGerd Hoffmann .gfx_update = vfio_display_region_update, 29500195ba7SGerd Hoffmann }; 29600195ba7SGerd Hoffmann 29700195ba7SGerd Hoffmann static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) 29800195ba7SGerd Hoffmann { 29900195ba7SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 30000195ba7SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 30100195ba7SGerd Hoffmann &vfio_display_region_ops, 30200195ba7SGerd Hoffmann vdev); 30300195ba7SGerd Hoffmann return 0; 30400195ba7SGerd Hoffmann } 30500195ba7SGerd Hoffmann 30600195ba7SGerd Hoffmann static void vfio_display_region_exit(VFIODisplay *dpy) 30700195ba7SGerd Hoffmann { 30800195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 30900195ba7SGerd Hoffmann return; 31000195ba7SGerd Hoffmann } 31100195ba7SGerd Hoffmann 31200195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 31300195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 31400195ba7SGerd Hoffmann } 31500195ba7SGerd Hoffmann 31600195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 31700195ba7SGerd Hoffmann 318a9994687SGerd Hoffmann int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) 319a9994687SGerd Hoffmann { 320a9994687SGerd Hoffmann struct vfio_device_gfx_plane_info probe; 321a9994687SGerd Hoffmann int ret; 322a9994687SGerd Hoffmann 323a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 324a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 325a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; 326a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 327a9994687SGerd Hoffmann if (ret == 0) { 3288b818e05SGerd Hoffmann return vfio_display_dmabuf_init(vdev, errp); 329a9994687SGerd Hoffmann } 330a9994687SGerd Hoffmann 331a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 332a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 333a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; 334a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 335a9994687SGerd Hoffmann if (ret == 0) { 33600195ba7SGerd Hoffmann return vfio_display_region_init(vdev, errp); 337a9994687SGerd Hoffmann } 338a9994687SGerd Hoffmann 339a9994687SGerd Hoffmann if (vdev->display == ON_OFF_AUTO_AUTO) { 340a9994687SGerd Hoffmann /* not an error in automatic mode */ 341a9994687SGerd Hoffmann return 0; 342a9994687SGerd Hoffmann } 343a9994687SGerd Hoffmann 344a9994687SGerd Hoffmann error_setg(errp, "vfio: device doesn't support any (known) display method"); 345a9994687SGerd Hoffmann return -1; 346a9994687SGerd Hoffmann } 347a9994687SGerd Hoffmann 348a9994687SGerd Hoffmann void vfio_display_finalize(VFIOPCIDevice *vdev) 349a9994687SGerd Hoffmann { 35000195ba7SGerd Hoffmann if (!vdev->dpy) { 35100195ba7SGerd Hoffmann return; 35200195ba7SGerd Hoffmann } 35300195ba7SGerd Hoffmann 35400195ba7SGerd Hoffmann graphic_console_close(vdev->dpy->con); 3558b818e05SGerd Hoffmann vfio_display_dmabuf_exit(vdev->dpy); 35600195ba7SGerd Hoffmann vfio_display_region_exit(vdev->dpy); 35700195ba7SGerd Hoffmann g_free(vdev->dpy); 358a9994687SGerd Hoffmann } 359