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 17cc37d98bSRichard Henderson #include "qemu/error-report.h" 1808479114SGerd Hoffmann #include "hw/display/edid.h" 19a9994687SGerd Hoffmann #include "ui/console.h" 20a9994687SGerd Hoffmann #include "qapi/error.h" 21a9994687SGerd Hoffmann #include "pci.h" 2208479114SGerd Hoffmann #include "trace.h" 23a9994687SGerd Hoffmann 248b818e05SGerd Hoffmann #ifndef DRM_PLANE_TYPE_PRIMARY 258b818e05SGerd Hoffmann # define DRM_PLANE_TYPE_PRIMARY 1 268b818e05SGerd Hoffmann # define DRM_PLANE_TYPE_CURSOR 2 278b818e05SGerd Hoffmann #endif 288b818e05SGerd Hoffmann 2908479114SGerd Hoffmann #define pread_field(_fd, _reg, _ptr, _fld) \ 3008479114SGerd Hoffmann (sizeof(_ptr->_fld) != \ 3108479114SGerd Hoffmann pread(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \ 3208479114SGerd Hoffmann _reg->offset + offsetof(typeof(*_ptr), _fld))) 3308479114SGerd Hoffmann 3408479114SGerd Hoffmann #define pwrite_field(_fd, _reg, _ptr, _fld) \ 3508479114SGerd Hoffmann (sizeof(_ptr->_fld) != \ 3608479114SGerd Hoffmann pwrite(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \ 3708479114SGerd Hoffmann _reg->offset + offsetof(typeof(*_ptr), _fld))) 3808479114SGerd Hoffmann 3908479114SGerd Hoffmann 408781c701SGerd Hoffmann static void vfio_display_edid_link_up(void *opaque) 418781c701SGerd Hoffmann { 428781c701SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 438781c701SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 448781c701SGerd Hoffmann int fd = vdev->vbasedev.fd; 458781c701SGerd Hoffmann 468781c701SGerd Hoffmann dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_UP; 478781c701SGerd Hoffmann if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) { 488781c701SGerd Hoffmann goto err; 498781c701SGerd Hoffmann } 508781c701SGerd Hoffmann trace_vfio_display_edid_link_up(); 518781c701SGerd Hoffmann return; 528781c701SGerd Hoffmann 538781c701SGerd Hoffmann err: 548781c701SGerd Hoffmann trace_vfio_display_edid_write_error(); 558781c701SGerd Hoffmann } 568781c701SGerd Hoffmann 5708479114SGerd Hoffmann static void vfio_display_edid_update(VFIOPCIDevice *vdev, bool enabled, 5808479114SGerd Hoffmann int prefx, int prefy) 5908479114SGerd Hoffmann { 6008479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 6108479114SGerd Hoffmann int fd = vdev->vbasedev.fd; 6208479114SGerd Hoffmann qemu_edid_info edid = { 6308479114SGerd Hoffmann .maxx = dpy->edid_regs->max_xres, 6408479114SGerd Hoffmann .maxy = dpy->edid_regs->max_yres, 65c62a0c7cSGerd Hoffmann .prefx = prefx ?: vdev->display_xres, 66c62a0c7cSGerd Hoffmann .prefy = prefy ?: vdev->display_yres, 6708479114SGerd Hoffmann }; 6808479114SGerd Hoffmann 698781c701SGerd Hoffmann timer_del(dpy->edid_link_timer); 7008479114SGerd Hoffmann dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_DOWN; 7108479114SGerd Hoffmann if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) { 7208479114SGerd Hoffmann goto err; 7308479114SGerd Hoffmann } 7408479114SGerd Hoffmann trace_vfio_display_edid_link_down(); 7508479114SGerd Hoffmann 7608479114SGerd Hoffmann if (!enabled) { 7708479114SGerd Hoffmann return; 7808479114SGerd Hoffmann } 7908479114SGerd Hoffmann 8008479114SGerd Hoffmann if (edid.maxx && edid.prefx > edid.maxx) { 8108479114SGerd Hoffmann edid.prefx = edid.maxx; 8208479114SGerd Hoffmann } 8308479114SGerd Hoffmann if (edid.maxy && edid.prefy > edid.maxy) { 8408479114SGerd Hoffmann edid.prefy = edid.maxy; 8508479114SGerd Hoffmann } 8608479114SGerd Hoffmann qemu_edid_generate(dpy->edid_blob, 8708479114SGerd Hoffmann dpy->edid_regs->edid_max_size, 8808479114SGerd Hoffmann &edid); 8908479114SGerd Hoffmann trace_vfio_display_edid_update(edid.prefx, edid.prefy); 9008479114SGerd Hoffmann 9108479114SGerd Hoffmann dpy->edid_regs->edid_size = qemu_edid_size(dpy->edid_blob); 9208479114SGerd Hoffmann if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, edid_size)) { 9308479114SGerd Hoffmann goto err; 9408479114SGerd Hoffmann } 9508479114SGerd Hoffmann if (pwrite(fd, dpy->edid_blob, dpy->edid_regs->edid_size, 9608479114SGerd Hoffmann dpy->edid_info->offset + dpy->edid_regs->edid_offset) 9708479114SGerd Hoffmann != dpy->edid_regs->edid_size) { 9808479114SGerd Hoffmann goto err; 9908479114SGerd Hoffmann } 10008479114SGerd Hoffmann 1018781c701SGerd Hoffmann timer_mod(dpy->edid_link_timer, 1028781c701SGerd Hoffmann qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 100); 10308479114SGerd Hoffmann return; 10408479114SGerd Hoffmann 10508479114SGerd Hoffmann err: 10608479114SGerd Hoffmann trace_vfio_display_edid_write_error(); 10708479114SGerd Hoffmann return; 10808479114SGerd Hoffmann } 10908479114SGerd Hoffmann 110362239c0SAkihiko Odaki static void vfio_display_edid_ui_info(void *opaque, uint32_t idx, 11108479114SGerd Hoffmann QemuUIInfo *info) 11208479114SGerd Hoffmann { 11308479114SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 11408479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 11508479114SGerd Hoffmann 11608479114SGerd Hoffmann if (!dpy->edid_regs) { 117362239c0SAkihiko Odaki return; 11808479114SGerd Hoffmann } 11908479114SGerd Hoffmann 12008479114SGerd Hoffmann if (info->width && info->height) { 12108479114SGerd Hoffmann vfio_display_edid_update(vdev, true, info->width, info->height); 12208479114SGerd Hoffmann } else { 12308479114SGerd Hoffmann vfio_display_edid_update(vdev, false, 0, 0); 12408479114SGerd Hoffmann } 12508479114SGerd Hoffmann } 12608479114SGerd Hoffmann 12708479114SGerd Hoffmann static void vfio_display_edid_init(VFIOPCIDevice *vdev) 12808479114SGerd Hoffmann { 12908479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 13008479114SGerd Hoffmann int fd = vdev->vbasedev.fd; 13108479114SGerd Hoffmann int ret; 13208479114SGerd Hoffmann 13308479114SGerd Hoffmann ret = vfio_get_dev_region_info(&vdev->vbasedev, 13408479114SGerd Hoffmann VFIO_REGION_TYPE_GFX, 13508479114SGerd Hoffmann VFIO_REGION_SUBTYPE_GFX_EDID, 13608479114SGerd Hoffmann &dpy->edid_info); 13708479114SGerd Hoffmann if (ret) { 13808479114SGerd Hoffmann return; 13908479114SGerd Hoffmann } 14008479114SGerd Hoffmann 14108479114SGerd Hoffmann trace_vfio_display_edid_available(); 14208479114SGerd Hoffmann dpy->edid_regs = g_new0(struct vfio_region_gfx_edid, 1); 14308479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_offset)) { 14408479114SGerd Hoffmann goto err; 14508479114SGerd Hoffmann } 14608479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_max_size)) { 14708479114SGerd Hoffmann goto err; 14808479114SGerd Hoffmann } 14908479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_xres)) { 15008479114SGerd Hoffmann goto err; 15108479114SGerd Hoffmann } 15208479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_yres)) { 15308479114SGerd Hoffmann goto err; 15408479114SGerd Hoffmann } 15508479114SGerd Hoffmann 15608479114SGerd Hoffmann dpy->edid_blob = g_malloc0(dpy->edid_regs->edid_max_size); 15708479114SGerd Hoffmann 158c62a0c7cSGerd Hoffmann /* if xres + yres properties are unset use the maximum resolution */ 159c62a0c7cSGerd Hoffmann if (!vdev->display_xres) { 160c62a0c7cSGerd Hoffmann vdev->display_xres = dpy->edid_regs->max_xres; 161c62a0c7cSGerd Hoffmann } 162c62a0c7cSGerd Hoffmann if (!vdev->display_yres) { 163c62a0c7cSGerd Hoffmann vdev->display_yres = dpy->edid_regs->max_yres; 164c62a0c7cSGerd Hoffmann } 165c62a0c7cSGerd Hoffmann 1668781c701SGerd Hoffmann dpy->edid_link_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1678781c701SGerd Hoffmann vfio_display_edid_link_up, vdev); 1688781c701SGerd Hoffmann 16908479114SGerd Hoffmann vfio_display_edid_update(vdev, true, 0, 0); 17008479114SGerd Hoffmann return; 17108479114SGerd Hoffmann 17208479114SGerd Hoffmann err: 17308479114SGerd Hoffmann trace_vfio_display_edid_write_error(); 17408479114SGerd Hoffmann g_free(dpy->edid_regs); 17508479114SGerd Hoffmann dpy->edid_regs = NULL; 17608479114SGerd Hoffmann return; 17708479114SGerd Hoffmann } 17808479114SGerd Hoffmann 17908479114SGerd Hoffmann static void vfio_display_edid_exit(VFIODisplay *dpy) 18008479114SGerd Hoffmann { 18108479114SGerd Hoffmann if (!dpy->edid_regs) { 18208479114SGerd Hoffmann return; 18308479114SGerd Hoffmann } 18408479114SGerd Hoffmann 18508479114SGerd Hoffmann g_free(dpy->edid_regs); 18608479114SGerd Hoffmann g_free(dpy->edid_blob); 1878781c701SGerd Hoffmann timer_free(dpy->edid_link_timer); 18808479114SGerd Hoffmann } 18908479114SGerd Hoffmann 1908b818e05SGerd Hoffmann static void vfio_display_update_cursor(VFIODMABuf *dmabuf, 1918b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info *plane) 1928b818e05SGerd Hoffmann { 1938b818e05SGerd Hoffmann if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) { 1948b818e05SGerd Hoffmann dmabuf->pos_x = plane->x_pos; 1958b818e05SGerd Hoffmann dmabuf->pos_y = plane->y_pos; 1968b818e05SGerd Hoffmann dmabuf->pos_updates++; 1978b818e05SGerd Hoffmann } 1988b818e05SGerd Hoffmann if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) { 1998b818e05SGerd Hoffmann dmabuf->hot_x = plane->x_hot; 2008b818e05SGerd Hoffmann dmabuf->hot_y = plane->y_hot; 2018b818e05SGerd Hoffmann dmabuf->hot_updates++; 2028b818e05SGerd Hoffmann } 2038b818e05SGerd Hoffmann } 2048b818e05SGerd Hoffmann 2058b818e05SGerd Hoffmann static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, 2068b818e05SGerd Hoffmann uint32_t plane_type) 2078b818e05SGerd Hoffmann { 2088b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2098b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info plane; 2108b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 2118b818e05SGerd Hoffmann int fd, ret; 2128b818e05SGerd Hoffmann 2138b818e05SGerd Hoffmann memset(&plane, 0, sizeof(plane)); 2148b818e05SGerd Hoffmann plane.argsz = sizeof(plane); 2158b818e05SGerd Hoffmann plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF; 2168b818e05SGerd Hoffmann plane.drm_plane_type = plane_type; 2178b818e05SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 2188b818e05SGerd Hoffmann if (ret < 0) { 2198b818e05SGerd Hoffmann return NULL; 2208b818e05SGerd Hoffmann } 2218b818e05SGerd Hoffmann if (!plane.drm_format || !plane.size) { 2228b818e05SGerd Hoffmann return NULL; 2238b818e05SGerd Hoffmann } 2248b818e05SGerd Hoffmann 2258b818e05SGerd Hoffmann QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) { 2268b818e05SGerd Hoffmann if (dmabuf->dmabuf_id == plane.dmabuf_id) { 2278b818e05SGerd Hoffmann /* found in list, move to head, return it */ 2288b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 2298b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 2308b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 2318b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 2328b818e05SGerd Hoffmann } 2338b818e05SGerd Hoffmann return dmabuf; 2348b818e05SGerd Hoffmann } 2358b818e05SGerd Hoffmann } 2368b818e05SGerd Hoffmann 2378b818e05SGerd Hoffmann fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id); 2388b818e05SGerd Hoffmann if (fd < 0) { 2398b818e05SGerd Hoffmann return NULL; 2408b818e05SGerd Hoffmann } 2418b818e05SGerd Hoffmann 2428b818e05SGerd Hoffmann dmabuf = g_new0(VFIODMABuf, 1); 2438b818e05SGerd Hoffmann dmabuf->dmabuf_id = plane.dmabuf_id; 2448b818e05SGerd Hoffmann dmabuf->buf.width = plane.width; 2458b818e05SGerd Hoffmann dmabuf->buf.height = plane.height; 246931150e5SAlex Williamson dmabuf->buf.backing_width = plane.width; 247931150e5SAlex Williamson dmabuf->buf.backing_height = plane.height; 2488b818e05SGerd Hoffmann dmabuf->buf.stride = plane.stride; 2498b818e05SGerd Hoffmann dmabuf->buf.fourcc = plane.drm_format; 250a6c9d5daSGerd Hoffmann dmabuf->buf.modifier = plane.drm_format_mod; 2518b818e05SGerd Hoffmann dmabuf->buf.fd = fd; 2528b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 2538b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 2548b818e05SGerd Hoffmann } 2558b818e05SGerd Hoffmann 2568b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 2578b818e05SGerd Hoffmann return dmabuf; 2588b818e05SGerd Hoffmann } 2598b818e05SGerd Hoffmann 2608b818e05SGerd Hoffmann static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) 2618b818e05SGerd Hoffmann { 2628b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 2638b818e05SGerd Hoffmann dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); 2648b818e05SGerd Hoffmann close(dmabuf->buf.fd); 2658b818e05SGerd Hoffmann g_free(dmabuf); 2668b818e05SGerd Hoffmann } 2678b818e05SGerd Hoffmann 2688b818e05SGerd Hoffmann static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) 2698b818e05SGerd Hoffmann { 2708b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2718b818e05SGerd Hoffmann VFIODMABuf *dmabuf, *tmp; 2728b818e05SGerd Hoffmann uint32_t keep = 5; 2738b818e05SGerd Hoffmann 2748b818e05SGerd Hoffmann QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { 2758b818e05SGerd Hoffmann if (keep > 0) { 2768b818e05SGerd Hoffmann keep--; 2778b818e05SGerd Hoffmann continue; 2788b818e05SGerd Hoffmann } 2798b818e05SGerd Hoffmann assert(dmabuf != dpy->dmabuf.primary); 2808b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 2818b818e05SGerd Hoffmann } 2828b818e05SGerd Hoffmann } 2838b818e05SGerd Hoffmann 2848b818e05SGerd Hoffmann static void vfio_display_dmabuf_update(void *opaque) 2858b818e05SGerd Hoffmann { 2868b818e05SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 2878b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2888b818e05SGerd Hoffmann VFIODMABuf *primary, *cursor; 289180f3fd2SPhilippe Mathieu-Daudé bool free_bufs = false, new_cursor = false; 2908b818e05SGerd Hoffmann 2918b818e05SGerd Hoffmann primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); 2928b818e05SGerd Hoffmann if (primary == NULL) { 293b290659fSGerd Hoffmann if (dpy->ramfb) { 294b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 295b290659fSGerd Hoffmann } 2968b818e05SGerd Hoffmann return; 2978b818e05SGerd Hoffmann } 2988b818e05SGerd Hoffmann 2998b818e05SGerd Hoffmann if (dpy->dmabuf.primary != primary) { 3008b818e05SGerd Hoffmann dpy->dmabuf.primary = primary; 3018b818e05SGerd Hoffmann qemu_console_resize(dpy->con, 3028b818e05SGerd Hoffmann primary->buf.width, primary->buf.height); 3038b818e05SGerd Hoffmann dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); 3048b818e05SGerd Hoffmann free_bufs = true; 3058b818e05SGerd Hoffmann } 3068b818e05SGerd Hoffmann 3078b818e05SGerd Hoffmann cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); 3088b818e05SGerd Hoffmann if (dpy->dmabuf.cursor != cursor) { 3098b818e05SGerd Hoffmann dpy->dmabuf.cursor = cursor; 3108b818e05SGerd Hoffmann new_cursor = true; 3118b818e05SGerd Hoffmann free_bufs = true; 3128b818e05SGerd Hoffmann } 3138b818e05SGerd Hoffmann 3148b818e05SGerd Hoffmann if (cursor && (new_cursor || cursor->hot_updates)) { 3158b818e05SGerd Hoffmann bool have_hot = (cursor->hot_x != 0xffffffff && 3168b818e05SGerd Hoffmann cursor->hot_y != 0xffffffff); 3178b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, 3188b818e05SGerd Hoffmann cursor->hot_x, cursor->hot_y); 3198b818e05SGerd Hoffmann cursor->hot_updates = 0; 3208b818e05SGerd Hoffmann } else if (!cursor && new_cursor) { 3218b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); 3228b818e05SGerd Hoffmann } 3238b818e05SGerd Hoffmann 3248b818e05SGerd Hoffmann if (cursor && cursor->pos_updates) { 3258b818e05SGerd Hoffmann dpy_gl_cursor_position(dpy->con, 3268b818e05SGerd Hoffmann cursor->pos_x, 3278b818e05SGerd Hoffmann cursor->pos_y); 3288b818e05SGerd Hoffmann cursor->pos_updates = 0; 3298b818e05SGerd Hoffmann } 3308b818e05SGerd Hoffmann 3318b818e05SGerd Hoffmann dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); 3328b818e05SGerd Hoffmann 3338b818e05SGerd Hoffmann if (free_bufs) { 3348b818e05SGerd Hoffmann vfio_display_free_dmabufs(vdev); 3358b818e05SGerd Hoffmann } 3368b818e05SGerd Hoffmann } 3378b818e05SGerd Hoffmann 338a7dfbe28SMarc-André Lureau static int vfio_display_get_flags(void *opaque) 339a7dfbe28SMarc-André Lureau { 340a7dfbe28SMarc-André Lureau return GRAPHIC_FLAGS_GL | GRAPHIC_FLAGS_DMABUF; 341a7dfbe28SMarc-André Lureau } 342a7dfbe28SMarc-André Lureau 3438b818e05SGerd Hoffmann static const GraphicHwOps vfio_display_dmabuf_ops = { 344a7dfbe28SMarc-André Lureau .get_flags = vfio_display_get_flags, 3458b818e05SGerd Hoffmann .gfx_update = vfio_display_dmabuf_update, 34608479114SGerd Hoffmann .ui_info = vfio_display_edid_ui_info, 3478b818e05SGerd Hoffmann }; 3488b818e05SGerd Hoffmann 3498b818e05SGerd Hoffmann static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) 3508b818e05SGerd Hoffmann { 3518b818e05SGerd Hoffmann if (!display_opengl) { 3528b818e05SGerd Hoffmann error_setg(errp, "vfio-display-dmabuf: opengl not available"); 3538b818e05SGerd Hoffmann return -1; 3548b818e05SGerd Hoffmann } 3558b818e05SGerd Hoffmann 3568b818e05SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 3578b818e05SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 3588b818e05SGerd Hoffmann &vfio_display_dmabuf_ops, 3598b818e05SGerd Hoffmann vdev); 360b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 3612fc979cbSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 362b290659fSGerd Hoffmann } 36308479114SGerd Hoffmann vfio_display_edid_init(vdev); 3648b818e05SGerd Hoffmann return 0; 3658b818e05SGerd Hoffmann } 3668b818e05SGerd Hoffmann 3678b818e05SGerd Hoffmann static void vfio_display_dmabuf_exit(VFIODisplay *dpy) 3688b818e05SGerd Hoffmann { 3698b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 3708b818e05SGerd Hoffmann 3718b818e05SGerd Hoffmann if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { 3728b818e05SGerd Hoffmann return; 3738b818e05SGerd Hoffmann } 3748b818e05SGerd Hoffmann 3758b818e05SGerd Hoffmann while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { 3768b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 3778b818e05SGerd Hoffmann } 3788b818e05SGerd Hoffmann } 3798b818e05SGerd Hoffmann 38000195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 3818983e3e3STina Zhang void vfio_display_reset(VFIOPCIDevice *vdev) 3828983e3e3STina Zhang { 3838983e3e3STina Zhang if (!vdev || !vdev->dpy || !vdev->dpy->con || 3848983e3e3STina Zhang !vdev->dpy->dmabuf.primary) { 3858983e3e3STina Zhang return; 3868983e3e3STina Zhang } 3878983e3e3STina Zhang 3888983e3e3STina Zhang dpy_gl_scanout_disable(vdev->dpy->con); 3898983e3e3STina Zhang vfio_display_dmabuf_exit(vdev->dpy); 3908983e3e3STina Zhang dpy_gfx_update_full(vdev->dpy->con); 3918983e3e3STina Zhang } 39200195ba7SGerd Hoffmann 39300195ba7SGerd Hoffmann static void vfio_display_region_update(void *opaque) 39400195ba7SGerd Hoffmann { 39500195ba7SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 39600195ba7SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 39700195ba7SGerd Hoffmann struct vfio_device_gfx_plane_info plane = { 39800195ba7SGerd Hoffmann .argsz = sizeof(plane), 39900195ba7SGerd Hoffmann .flags = VFIO_GFX_PLANE_TYPE_REGION 40000195ba7SGerd Hoffmann }; 40100195ba7SGerd Hoffmann pixman_format_code_t format; 40200195ba7SGerd Hoffmann int ret; 40300195ba7SGerd Hoffmann 40400195ba7SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 40500195ba7SGerd Hoffmann if (ret < 0) { 40600195ba7SGerd Hoffmann error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", 40700195ba7SGerd Hoffmann strerror(errno)); 40800195ba7SGerd Hoffmann return; 40900195ba7SGerd Hoffmann } 41000195ba7SGerd Hoffmann if (!plane.drm_format || !plane.size) { 411b290659fSGerd Hoffmann if (dpy->ramfb) { 412b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 4138ec14159SGerd Hoffmann dpy->region.surface = NULL; 414b290659fSGerd Hoffmann } 41500195ba7SGerd Hoffmann return; 41600195ba7SGerd Hoffmann } 41700195ba7SGerd Hoffmann format = qemu_drm_format_to_pixman(plane.drm_format); 41800195ba7SGerd Hoffmann if (!format) { 41900195ba7SGerd Hoffmann return; 42000195ba7SGerd Hoffmann } 42100195ba7SGerd Hoffmann 42200195ba7SGerd Hoffmann if (dpy->region.buffer.size && 42300195ba7SGerd Hoffmann dpy->region.buffer.nr != plane.region_index) { 42400195ba7SGerd Hoffmann /* region changed */ 42500195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 42600195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 42700195ba7SGerd Hoffmann dpy->region.surface = NULL; 42800195ba7SGerd Hoffmann } 42900195ba7SGerd Hoffmann 43000195ba7SGerd Hoffmann if (dpy->region.surface && 43100195ba7SGerd Hoffmann (surface_width(dpy->region.surface) != plane.width || 43200195ba7SGerd Hoffmann surface_height(dpy->region.surface) != plane.height || 43300195ba7SGerd Hoffmann surface_format(dpy->region.surface) != format)) { 43400195ba7SGerd Hoffmann /* size changed */ 43500195ba7SGerd Hoffmann dpy->region.surface = NULL; 43600195ba7SGerd Hoffmann } 43700195ba7SGerd Hoffmann 43800195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 43900195ba7SGerd Hoffmann /* mmap region */ 44000195ba7SGerd Hoffmann ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, 44100195ba7SGerd Hoffmann &dpy->region.buffer, 44200195ba7SGerd Hoffmann plane.region_index, 44300195ba7SGerd Hoffmann "display"); 44400195ba7SGerd Hoffmann if (ret != 0) { 44500195ba7SGerd Hoffmann error_report("%s: vfio_region_setup(%d): %s", 44600195ba7SGerd Hoffmann __func__, plane.region_index, strerror(-ret)); 44700195ba7SGerd Hoffmann goto err; 44800195ba7SGerd Hoffmann } 44900195ba7SGerd Hoffmann ret = vfio_region_mmap(&dpy->region.buffer); 45000195ba7SGerd Hoffmann if (ret != 0) { 45100195ba7SGerd Hoffmann error_report("%s: vfio_region_mmap(%d): %s", __func__, 45200195ba7SGerd Hoffmann plane.region_index, strerror(-ret)); 45300195ba7SGerd Hoffmann goto err; 45400195ba7SGerd Hoffmann } 45500195ba7SGerd Hoffmann assert(dpy->region.buffer.mmaps[0].mmap != NULL); 45600195ba7SGerd Hoffmann } 45700195ba7SGerd Hoffmann 45800195ba7SGerd Hoffmann if (dpy->region.surface == NULL) { 45900195ba7SGerd Hoffmann /* create surface */ 46000195ba7SGerd Hoffmann dpy->region.surface = qemu_create_displaysurface_from 46100195ba7SGerd Hoffmann (plane.width, plane.height, format, 46200195ba7SGerd Hoffmann plane.stride, dpy->region.buffer.mmaps[0].mmap); 46300195ba7SGerd Hoffmann dpy_gfx_replace_surface(dpy->con, dpy->region.surface); 46400195ba7SGerd Hoffmann } 46500195ba7SGerd Hoffmann 46600195ba7SGerd Hoffmann /* full screen update */ 46700195ba7SGerd Hoffmann dpy_gfx_update(dpy->con, 0, 0, 46800195ba7SGerd Hoffmann surface_width(dpy->region.surface), 46900195ba7SGerd Hoffmann surface_height(dpy->region.surface)); 47000195ba7SGerd Hoffmann return; 47100195ba7SGerd Hoffmann 47200195ba7SGerd Hoffmann err: 47300195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 47400195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 47500195ba7SGerd Hoffmann } 47600195ba7SGerd Hoffmann 47700195ba7SGerd Hoffmann static const GraphicHwOps vfio_display_region_ops = { 47800195ba7SGerd Hoffmann .gfx_update = vfio_display_region_update, 47900195ba7SGerd Hoffmann }; 48000195ba7SGerd Hoffmann 48100195ba7SGerd Hoffmann static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) 48200195ba7SGerd Hoffmann { 48300195ba7SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 48400195ba7SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 48500195ba7SGerd Hoffmann &vfio_display_region_ops, 48600195ba7SGerd Hoffmann vdev); 487b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 4882fc979cbSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 489b290659fSGerd Hoffmann } 49000195ba7SGerd Hoffmann return 0; 49100195ba7SGerd Hoffmann } 49200195ba7SGerd Hoffmann 49300195ba7SGerd Hoffmann static void vfio_display_region_exit(VFIODisplay *dpy) 49400195ba7SGerd Hoffmann { 49500195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 49600195ba7SGerd Hoffmann return; 49700195ba7SGerd Hoffmann } 49800195ba7SGerd Hoffmann 49900195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 50000195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 50100195ba7SGerd Hoffmann } 50200195ba7SGerd Hoffmann 50300195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 50400195ba7SGerd Hoffmann 505a9994687SGerd Hoffmann int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) 506a9994687SGerd Hoffmann { 507a9994687SGerd Hoffmann struct vfio_device_gfx_plane_info probe; 508a9994687SGerd Hoffmann int ret; 509a9994687SGerd Hoffmann 510a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 511a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 512a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; 513a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 514a9994687SGerd Hoffmann if (ret == 0) { 5158b818e05SGerd Hoffmann return vfio_display_dmabuf_init(vdev, errp); 516a9994687SGerd Hoffmann } 517a9994687SGerd Hoffmann 518a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 519a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 520a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; 521a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 522a9994687SGerd Hoffmann if (ret == 0) { 52300195ba7SGerd Hoffmann return vfio_display_region_init(vdev, errp); 524a9994687SGerd Hoffmann } 525a9994687SGerd Hoffmann 526a9994687SGerd Hoffmann if (vdev->display == ON_OFF_AUTO_AUTO) { 527a9994687SGerd Hoffmann /* not an error in automatic mode */ 528a9994687SGerd Hoffmann return 0; 529a9994687SGerd Hoffmann } 530a9994687SGerd Hoffmann 531a9994687SGerd Hoffmann error_setg(errp, "vfio: device doesn't support any (known) display method"); 532a9994687SGerd Hoffmann return -1; 533a9994687SGerd Hoffmann } 534a9994687SGerd Hoffmann 535a9994687SGerd Hoffmann void vfio_display_finalize(VFIOPCIDevice *vdev) 536a9994687SGerd Hoffmann { 53700195ba7SGerd Hoffmann if (!vdev->dpy) { 53800195ba7SGerd Hoffmann return; 53900195ba7SGerd Hoffmann } 54000195ba7SGerd Hoffmann 54100195ba7SGerd Hoffmann graphic_console_close(vdev->dpy->con); 5428b818e05SGerd Hoffmann vfio_display_dmabuf_exit(vdev->dpy); 54300195ba7SGerd Hoffmann vfio_display_region_exit(vdev->dpy); 54408479114SGerd Hoffmann vfio_display_edid_exit(vdev->dpy); 54500195ba7SGerd Hoffmann g_free(vdev->dpy); 546a9994687SGerd Hoffmann } 54787417811SMarc-André Lureau 54887417811SMarc-André Lureau static bool migrate_needed(void *opaque) 54987417811SMarc-André Lureau { 55087417811SMarc-André Lureau VFIODisplay *dpy = opaque; 55187417811SMarc-André Lureau bool ramfb_exists = dpy->ramfb != NULL; 55287417811SMarc-André Lureau 55387417811SMarc-André Lureau /* see vfio_display_migration_needed() */ 55487417811SMarc-André Lureau assert(ramfb_exists); 55587417811SMarc-André Lureau return ramfb_exists; 55687417811SMarc-André Lureau } 55787417811SMarc-André Lureau 55887417811SMarc-André Lureau const VMStateDescription vfio_display_vmstate = { 55987417811SMarc-André Lureau .name = "VFIODisplay", 56087417811SMarc-André Lureau .version_id = 1, 56187417811SMarc-André Lureau .minimum_version_id = 1, 56287417811SMarc-André Lureau .needed = migrate_needed, 563*65bd53e8SRichard Henderson .fields = (const VMStateField[]) { 56487417811SMarc-André Lureau VMSTATE_STRUCT_POINTER(ramfb, VFIODisplay, ramfb_vmstate, RAMFBState), 56587417811SMarc-André Lureau VMSTATE_END_OF_LIST(), 56687417811SMarc-André Lureau } 56787417811SMarc-André Lureau }; 568