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) { 127*b290659fSGerd Hoffmann if (dpy->ramfb) { 128*b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 129*b290659fSGerd Hoffmann } 1308b818e05SGerd Hoffmann return; 1318b818e05SGerd Hoffmann } 1328b818e05SGerd Hoffmann 1338b818e05SGerd Hoffmann if (dpy->dmabuf.primary != primary) { 1348b818e05SGerd Hoffmann dpy->dmabuf.primary = primary; 1358b818e05SGerd Hoffmann qemu_console_resize(dpy->con, 1368b818e05SGerd Hoffmann primary->buf.width, primary->buf.height); 1378b818e05SGerd Hoffmann dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); 1388b818e05SGerd Hoffmann free_bufs = true; 1398b818e05SGerd Hoffmann } 1408b818e05SGerd Hoffmann 1418b818e05SGerd Hoffmann cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); 1428b818e05SGerd Hoffmann if (dpy->dmabuf.cursor != cursor) { 1438b818e05SGerd Hoffmann dpy->dmabuf.cursor = cursor; 1448b818e05SGerd Hoffmann new_cursor = true; 1458b818e05SGerd Hoffmann free_bufs = true; 1468b818e05SGerd Hoffmann } 1478b818e05SGerd Hoffmann 1488b818e05SGerd Hoffmann if (cursor && (new_cursor || cursor->hot_updates)) { 1498b818e05SGerd Hoffmann bool have_hot = (cursor->hot_x != 0xffffffff && 1508b818e05SGerd Hoffmann cursor->hot_y != 0xffffffff); 1518b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, 1528b818e05SGerd Hoffmann cursor->hot_x, cursor->hot_y); 1538b818e05SGerd Hoffmann cursor->hot_updates = 0; 1548b818e05SGerd Hoffmann } else if (!cursor && new_cursor) { 1558b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); 1568b818e05SGerd Hoffmann } 1578b818e05SGerd Hoffmann 1588b818e05SGerd Hoffmann if (cursor && cursor->pos_updates) { 1598b818e05SGerd Hoffmann dpy_gl_cursor_position(dpy->con, 1608b818e05SGerd Hoffmann cursor->pos_x, 1618b818e05SGerd Hoffmann cursor->pos_y); 1628b818e05SGerd Hoffmann cursor->pos_updates = 0; 1638b818e05SGerd Hoffmann } 1648b818e05SGerd Hoffmann 1658b818e05SGerd Hoffmann dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); 1668b818e05SGerd Hoffmann 1678b818e05SGerd Hoffmann if (free_bufs) { 1688b818e05SGerd Hoffmann vfio_display_free_dmabufs(vdev); 1698b818e05SGerd Hoffmann } 1708b818e05SGerd Hoffmann } 1718b818e05SGerd Hoffmann 1728b818e05SGerd Hoffmann static const GraphicHwOps vfio_display_dmabuf_ops = { 1738b818e05SGerd Hoffmann .gfx_update = vfio_display_dmabuf_update, 1748b818e05SGerd Hoffmann }; 1758b818e05SGerd Hoffmann 1768b818e05SGerd Hoffmann static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) 1778b818e05SGerd Hoffmann { 1788b818e05SGerd Hoffmann if (!display_opengl) { 1798b818e05SGerd Hoffmann error_setg(errp, "vfio-display-dmabuf: opengl not available"); 1808b818e05SGerd Hoffmann return -1; 1818b818e05SGerd Hoffmann } 1828b818e05SGerd Hoffmann 1838b818e05SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 1848b818e05SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 1858b818e05SGerd Hoffmann &vfio_display_dmabuf_ops, 1868b818e05SGerd Hoffmann vdev); 187*b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 188*b290659fSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 189*b290659fSGerd Hoffmann } 1908b818e05SGerd Hoffmann return 0; 1918b818e05SGerd Hoffmann } 1928b818e05SGerd Hoffmann 1938b818e05SGerd Hoffmann static void vfio_display_dmabuf_exit(VFIODisplay *dpy) 1948b818e05SGerd Hoffmann { 1958b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 1968b818e05SGerd Hoffmann 1978b818e05SGerd Hoffmann if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { 1988b818e05SGerd Hoffmann return; 1998b818e05SGerd Hoffmann } 2008b818e05SGerd Hoffmann 2018b818e05SGerd Hoffmann while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { 2028b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 2038b818e05SGerd Hoffmann } 2048b818e05SGerd Hoffmann } 2058b818e05SGerd Hoffmann 20600195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 2078983e3e3STina Zhang void vfio_display_reset(VFIOPCIDevice *vdev) 2088983e3e3STina Zhang { 2098983e3e3STina Zhang if (!vdev || !vdev->dpy || !vdev->dpy->con || 2108983e3e3STina Zhang !vdev->dpy->dmabuf.primary) { 2118983e3e3STina Zhang return; 2128983e3e3STina Zhang } 2138983e3e3STina Zhang 2148983e3e3STina Zhang dpy_gl_scanout_disable(vdev->dpy->con); 2158983e3e3STina Zhang vfio_display_dmabuf_exit(vdev->dpy); 2168983e3e3STina Zhang dpy_gfx_update_full(vdev->dpy->con); 2178983e3e3STina Zhang } 21800195ba7SGerd Hoffmann 21900195ba7SGerd Hoffmann static void vfio_display_region_update(void *opaque) 22000195ba7SGerd Hoffmann { 22100195ba7SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 22200195ba7SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 22300195ba7SGerd Hoffmann struct vfio_device_gfx_plane_info plane = { 22400195ba7SGerd Hoffmann .argsz = sizeof(plane), 22500195ba7SGerd Hoffmann .flags = VFIO_GFX_PLANE_TYPE_REGION 22600195ba7SGerd Hoffmann }; 22700195ba7SGerd Hoffmann pixman_format_code_t format; 22800195ba7SGerd Hoffmann int ret; 22900195ba7SGerd Hoffmann 23000195ba7SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 23100195ba7SGerd Hoffmann if (ret < 0) { 23200195ba7SGerd Hoffmann error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", 23300195ba7SGerd Hoffmann strerror(errno)); 23400195ba7SGerd Hoffmann return; 23500195ba7SGerd Hoffmann } 23600195ba7SGerd Hoffmann if (!plane.drm_format || !plane.size) { 237*b290659fSGerd Hoffmann if (dpy->ramfb) { 238*b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 239*b290659fSGerd Hoffmann } 24000195ba7SGerd Hoffmann return; 24100195ba7SGerd Hoffmann } 24200195ba7SGerd Hoffmann format = qemu_drm_format_to_pixman(plane.drm_format); 24300195ba7SGerd Hoffmann if (!format) { 24400195ba7SGerd Hoffmann return; 24500195ba7SGerd Hoffmann } 24600195ba7SGerd Hoffmann 24700195ba7SGerd Hoffmann if (dpy->region.buffer.size && 24800195ba7SGerd Hoffmann dpy->region.buffer.nr != plane.region_index) { 24900195ba7SGerd Hoffmann /* region changed */ 25000195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 25100195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 25200195ba7SGerd Hoffmann dpy->region.surface = NULL; 25300195ba7SGerd Hoffmann } 25400195ba7SGerd Hoffmann 25500195ba7SGerd Hoffmann if (dpy->region.surface && 25600195ba7SGerd Hoffmann (surface_width(dpy->region.surface) != plane.width || 25700195ba7SGerd Hoffmann surface_height(dpy->region.surface) != plane.height || 25800195ba7SGerd Hoffmann surface_format(dpy->region.surface) != format)) { 25900195ba7SGerd Hoffmann /* size changed */ 26000195ba7SGerd Hoffmann dpy->region.surface = NULL; 26100195ba7SGerd Hoffmann } 26200195ba7SGerd Hoffmann 26300195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 26400195ba7SGerd Hoffmann /* mmap region */ 26500195ba7SGerd Hoffmann ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, 26600195ba7SGerd Hoffmann &dpy->region.buffer, 26700195ba7SGerd Hoffmann plane.region_index, 26800195ba7SGerd Hoffmann "display"); 26900195ba7SGerd Hoffmann if (ret != 0) { 27000195ba7SGerd Hoffmann error_report("%s: vfio_region_setup(%d): %s", 27100195ba7SGerd Hoffmann __func__, plane.region_index, strerror(-ret)); 27200195ba7SGerd Hoffmann goto err; 27300195ba7SGerd Hoffmann } 27400195ba7SGerd Hoffmann ret = vfio_region_mmap(&dpy->region.buffer); 27500195ba7SGerd Hoffmann if (ret != 0) { 27600195ba7SGerd Hoffmann error_report("%s: vfio_region_mmap(%d): %s", __func__, 27700195ba7SGerd Hoffmann plane.region_index, strerror(-ret)); 27800195ba7SGerd Hoffmann goto err; 27900195ba7SGerd Hoffmann } 28000195ba7SGerd Hoffmann assert(dpy->region.buffer.mmaps[0].mmap != NULL); 28100195ba7SGerd Hoffmann } 28200195ba7SGerd Hoffmann 28300195ba7SGerd Hoffmann if (dpy->region.surface == NULL) { 28400195ba7SGerd Hoffmann /* create surface */ 28500195ba7SGerd Hoffmann dpy->region.surface = qemu_create_displaysurface_from 28600195ba7SGerd Hoffmann (plane.width, plane.height, format, 28700195ba7SGerd Hoffmann plane.stride, dpy->region.buffer.mmaps[0].mmap); 28800195ba7SGerd Hoffmann dpy_gfx_replace_surface(dpy->con, dpy->region.surface); 28900195ba7SGerd Hoffmann } 29000195ba7SGerd Hoffmann 29100195ba7SGerd Hoffmann /* full screen update */ 29200195ba7SGerd Hoffmann dpy_gfx_update(dpy->con, 0, 0, 29300195ba7SGerd Hoffmann surface_width(dpy->region.surface), 29400195ba7SGerd Hoffmann surface_height(dpy->region.surface)); 29500195ba7SGerd Hoffmann return; 29600195ba7SGerd Hoffmann 29700195ba7SGerd Hoffmann err: 29800195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 29900195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 30000195ba7SGerd Hoffmann } 30100195ba7SGerd Hoffmann 30200195ba7SGerd Hoffmann static const GraphicHwOps vfio_display_region_ops = { 30300195ba7SGerd Hoffmann .gfx_update = vfio_display_region_update, 30400195ba7SGerd Hoffmann }; 30500195ba7SGerd Hoffmann 30600195ba7SGerd Hoffmann static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) 30700195ba7SGerd Hoffmann { 30800195ba7SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 30900195ba7SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 31000195ba7SGerd Hoffmann &vfio_display_region_ops, 31100195ba7SGerd Hoffmann vdev); 312*b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 313*b290659fSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 314*b290659fSGerd Hoffmann } 31500195ba7SGerd Hoffmann return 0; 31600195ba7SGerd Hoffmann } 31700195ba7SGerd Hoffmann 31800195ba7SGerd Hoffmann static void vfio_display_region_exit(VFIODisplay *dpy) 31900195ba7SGerd Hoffmann { 32000195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 32100195ba7SGerd Hoffmann return; 32200195ba7SGerd Hoffmann } 32300195ba7SGerd Hoffmann 32400195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 32500195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 32600195ba7SGerd Hoffmann } 32700195ba7SGerd Hoffmann 32800195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 32900195ba7SGerd Hoffmann 330a9994687SGerd Hoffmann int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) 331a9994687SGerd Hoffmann { 332a9994687SGerd Hoffmann struct vfio_device_gfx_plane_info probe; 333a9994687SGerd Hoffmann int ret; 334a9994687SGerd Hoffmann 335a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 336a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 337a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; 338a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 339a9994687SGerd Hoffmann if (ret == 0) { 3408b818e05SGerd Hoffmann return vfio_display_dmabuf_init(vdev, errp); 341a9994687SGerd Hoffmann } 342a9994687SGerd Hoffmann 343a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 344a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 345a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; 346a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 347a9994687SGerd Hoffmann if (ret == 0) { 34800195ba7SGerd Hoffmann return vfio_display_region_init(vdev, errp); 349a9994687SGerd Hoffmann } 350a9994687SGerd Hoffmann 351a9994687SGerd Hoffmann if (vdev->display == ON_OFF_AUTO_AUTO) { 352a9994687SGerd Hoffmann /* not an error in automatic mode */ 353a9994687SGerd Hoffmann return 0; 354a9994687SGerd Hoffmann } 355a9994687SGerd Hoffmann 356a9994687SGerd Hoffmann error_setg(errp, "vfio: device doesn't support any (known) display method"); 357a9994687SGerd Hoffmann return -1; 358a9994687SGerd Hoffmann } 359a9994687SGerd Hoffmann 360a9994687SGerd Hoffmann void vfio_display_finalize(VFIOPCIDevice *vdev) 361a9994687SGerd Hoffmann { 36200195ba7SGerd Hoffmann if (!vdev->dpy) { 36300195ba7SGerd Hoffmann return; 36400195ba7SGerd Hoffmann } 36500195ba7SGerd Hoffmann 36600195ba7SGerd Hoffmann graphic_console_close(vdev->dpy->con); 3678b818e05SGerd Hoffmann vfio_display_dmabuf_exit(vdev->dpy); 36800195ba7SGerd Hoffmann vfio_display_region_exit(vdev->dpy); 36900195ba7SGerd Hoffmann g_free(vdev->dpy); 370a9994687SGerd Hoffmann } 371