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(); 174*f15da599SZhenzhong Duan g_free(dpy->edid_info); 17508479114SGerd Hoffmann g_free(dpy->edid_regs); 176*f15da599SZhenzhong Duan dpy->edid_info = NULL; 17708479114SGerd Hoffmann dpy->edid_regs = NULL; 17808479114SGerd Hoffmann return; 17908479114SGerd Hoffmann } 18008479114SGerd Hoffmann 18108479114SGerd Hoffmann static void vfio_display_edid_exit(VFIODisplay *dpy) 18208479114SGerd Hoffmann { 18308479114SGerd Hoffmann if (!dpy->edid_regs) { 18408479114SGerd Hoffmann return; 18508479114SGerd Hoffmann } 18608479114SGerd Hoffmann 187*f15da599SZhenzhong Duan g_free(dpy->edid_info); 18808479114SGerd Hoffmann g_free(dpy->edid_regs); 18908479114SGerd Hoffmann g_free(dpy->edid_blob); 1908781c701SGerd Hoffmann timer_free(dpy->edid_link_timer); 19108479114SGerd Hoffmann } 19208479114SGerd Hoffmann 1938b818e05SGerd Hoffmann static void vfio_display_update_cursor(VFIODMABuf *dmabuf, 1948b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info *plane) 1958b818e05SGerd Hoffmann { 1968b818e05SGerd Hoffmann if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) { 1978b818e05SGerd Hoffmann dmabuf->pos_x = plane->x_pos; 1988b818e05SGerd Hoffmann dmabuf->pos_y = plane->y_pos; 1998b818e05SGerd Hoffmann dmabuf->pos_updates++; 2008b818e05SGerd Hoffmann } 2018b818e05SGerd Hoffmann if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) { 2028b818e05SGerd Hoffmann dmabuf->hot_x = plane->x_hot; 2038b818e05SGerd Hoffmann dmabuf->hot_y = plane->y_hot; 2048b818e05SGerd Hoffmann dmabuf->hot_updates++; 2058b818e05SGerd Hoffmann } 2068b818e05SGerd Hoffmann } 2078b818e05SGerd Hoffmann 2088b818e05SGerd Hoffmann static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, 2098b818e05SGerd Hoffmann uint32_t plane_type) 2108b818e05SGerd Hoffmann { 2118b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2128b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info plane; 2138b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 2148b818e05SGerd Hoffmann int fd, ret; 2158b818e05SGerd Hoffmann 2168b818e05SGerd Hoffmann memset(&plane, 0, sizeof(plane)); 2178b818e05SGerd Hoffmann plane.argsz = sizeof(plane); 2188b818e05SGerd Hoffmann plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF; 2198b818e05SGerd Hoffmann plane.drm_plane_type = plane_type; 2208b818e05SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 2218b818e05SGerd Hoffmann if (ret < 0) { 2228b818e05SGerd Hoffmann return NULL; 2238b818e05SGerd Hoffmann } 2248b818e05SGerd Hoffmann if (!plane.drm_format || !plane.size) { 2258b818e05SGerd Hoffmann return NULL; 2268b818e05SGerd Hoffmann } 2278b818e05SGerd Hoffmann 2288b818e05SGerd Hoffmann QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) { 2298b818e05SGerd Hoffmann if (dmabuf->dmabuf_id == plane.dmabuf_id) { 2308b818e05SGerd Hoffmann /* found in list, move to head, return it */ 2318b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 2328b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 2338b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 2348b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 2358b818e05SGerd Hoffmann } 2368b818e05SGerd Hoffmann return dmabuf; 2378b818e05SGerd Hoffmann } 2388b818e05SGerd Hoffmann } 2398b818e05SGerd Hoffmann 2408b818e05SGerd Hoffmann fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id); 2418b818e05SGerd Hoffmann if (fd < 0) { 2428b818e05SGerd Hoffmann return NULL; 2438b818e05SGerd Hoffmann } 2448b818e05SGerd Hoffmann 2458b818e05SGerd Hoffmann dmabuf = g_new0(VFIODMABuf, 1); 2468b818e05SGerd Hoffmann dmabuf->dmabuf_id = plane.dmabuf_id; 247c0fcd633SDongwon Kim dmabuf->buf = qemu_dmabuf_new(plane.width, plane.height, 248c0fcd633SDongwon Kim plane.stride, 0, 0, plane.width, 249c0fcd633SDongwon Kim plane.height, plane.drm_format, 250c0fcd633SDongwon Kim plane.drm_format_mod, fd, false, false); 251c0fcd633SDongwon Kim 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); 2636779a307SDongwon Kim 264c0fcd633SDongwon Kim qemu_dmabuf_close(dmabuf->buf); 265c0fcd633SDongwon Kim dpy_gl_release_dmabuf(dpy->con, dmabuf->buf); 266c0fcd633SDongwon Kim g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free); 2678b818e05SGerd Hoffmann g_free(dmabuf); 2688b818e05SGerd Hoffmann } 2698b818e05SGerd Hoffmann 2708b818e05SGerd Hoffmann static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) 2718b818e05SGerd Hoffmann { 2728b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2738b818e05SGerd Hoffmann VFIODMABuf *dmabuf, *tmp; 2748b818e05SGerd Hoffmann uint32_t keep = 5; 2758b818e05SGerd Hoffmann 2768b818e05SGerd Hoffmann QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { 2778b818e05SGerd Hoffmann if (keep > 0) { 2788b818e05SGerd Hoffmann keep--; 2798b818e05SGerd Hoffmann continue; 2808b818e05SGerd Hoffmann } 2818b818e05SGerd Hoffmann assert(dmabuf != dpy->dmabuf.primary); 2828b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 2838b818e05SGerd Hoffmann } 2848b818e05SGerd Hoffmann } 2858b818e05SGerd Hoffmann 2868b818e05SGerd Hoffmann static void vfio_display_dmabuf_update(void *opaque) 2878b818e05SGerd Hoffmann { 2888b818e05SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 2898b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2908b818e05SGerd Hoffmann VFIODMABuf *primary, *cursor; 2916779a307SDongwon Kim uint32_t width, height; 292180f3fd2SPhilippe Mathieu-Daudé bool free_bufs = false, new_cursor = false; 2938b818e05SGerd Hoffmann 2948b818e05SGerd Hoffmann primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); 2958b818e05SGerd Hoffmann if (primary == NULL) { 296b290659fSGerd Hoffmann if (dpy->ramfb) { 297b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 298b290659fSGerd Hoffmann } 2998b818e05SGerd Hoffmann return; 3008b818e05SGerd Hoffmann } 3018b818e05SGerd Hoffmann 302c0fcd633SDongwon Kim width = qemu_dmabuf_get_width(primary->buf); 303c0fcd633SDongwon Kim height = qemu_dmabuf_get_height(primary->buf); 3046779a307SDongwon Kim 3058b818e05SGerd Hoffmann if (dpy->dmabuf.primary != primary) { 3068b818e05SGerd Hoffmann dpy->dmabuf.primary = primary; 3076779a307SDongwon Kim qemu_console_resize(dpy->con, width, height); 308c0fcd633SDongwon Kim dpy_gl_scanout_dmabuf(dpy->con, primary->buf); 3098b818e05SGerd Hoffmann free_bufs = true; 3108b818e05SGerd Hoffmann } 3118b818e05SGerd Hoffmann 3128b818e05SGerd Hoffmann cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); 3138b818e05SGerd Hoffmann if (dpy->dmabuf.cursor != cursor) { 3148b818e05SGerd Hoffmann dpy->dmabuf.cursor = cursor; 3158b818e05SGerd Hoffmann new_cursor = true; 3168b818e05SGerd Hoffmann free_bufs = true; 3178b818e05SGerd Hoffmann } 3188b818e05SGerd Hoffmann 3198b818e05SGerd Hoffmann if (cursor && (new_cursor || cursor->hot_updates)) { 3208b818e05SGerd Hoffmann bool have_hot = (cursor->hot_x != 0xffffffff && 3218b818e05SGerd Hoffmann cursor->hot_y != 0xffffffff); 322c0fcd633SDongwon Kim dpy_gl_cursor_dmabuf(dpy->con, cursor->buf, have_hot, 3238b818e05SGerd Hoffmann cursor->hot_x, cursor->hot_y); 3248b818e05SGerd Hoffmann cursor->hot_updates = 0; 3258b818e05SGerd Hoffmann } else if (!cursor && new_cursor) { 3268b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); 3278b818e05SGerd Hoffmann } 3288b818e05SGerd Hoffmann 3298b818e05SGerd Hoffmann if (cursor && cursor->pos_updates) { 3308b818e05SGerd Hoffmann dpy_gl_cursor_position(dpy->con, 3318b818e05SGerd Hoffmann cursor->pos_x, 3328b818e05SGerd Hoffmann cursor->pos_y); 3338b818e05SGerd Hoffmann cursor->pos_updates = 0; 3348b818e05SGerd Hoffmann } 3358b818e05SGerd Hoffmann 3366779a307SDongwon Kim dpy_gl_update(dpy->con, 0, 0, width, height); 3378b818e05SGerd Hoffmann 3388b818e05SGerd Hoffmann if (free_bufs) { 3398b818e05SGerd Hoffmann vfio_display_free_dmabufs(vdev); 3408b818e05SGerd Hoffmann } 3418b818e05SGerd Hoffmann } 3428b818e05SGerd Hoffmann 343a7dfbe28SMarc-André Lureau static int vfio_display_get_flags(void *opaque) 344a7dfbe28SMarc-André Lureau { 345a7dfbe28SMarc-André Lureau return GRAPHIC_FLAGS_GL | GRAPHIC_FLAGS_DMABUF; 346a7dfbe28SMarc-André Lureau } 347a7dfbe28SMarc-André Lureau 3488b818e05SGerd Hoffmann static const GraphicHwOps vfio_display_dmabuf_ops = { 349a7dfbe28SMarc-André Lureau .get_flags = vfio_display_get_flags, 3508b818e05SGerd Hoffmann .gfx_update = vfio_display_dmabuf_update, 35108479114SGerd Hoffmann .ui_info = vfio_display_edid_ui_info, 3528b818e05SGerd Hoffmann }; 3538b818e05SGerd Hoffmann 354455c009dSZhenzhong Duan static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) 3558b818e05SGerd Hoffmann { 3568b818e05SGerd Hoffmann if (!display_opengl) { 3578b818e05SGerd Hoffmann error_setg(errp, "vfio-display-dmabuf: opengl not available"); 358455c009dSZhenzhong Duan return false; 3598b818e05SGerd Hoffmann } 3608b818e05SGerd Hoffmann 3618b818e05SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 3628b818e05SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 3638b818e05SGerd Hoffmann &vfio_display_dmabuf_ops, 3648b818e05SGerd Hoffmann vdev); 365b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 3662fc979cbSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 3679442d8afSZhenzhong Duan if (!vdev->dpy->ramfb) { 368455c009dSZhenzhong Duan return false; 3699442d8afSZhenzhong Duan } 370b290659fSGerd Hoffmann } 37108479114SGerd Hoffmann vfio_display_edid_init(vdev); 372455c009dSZhenzhong Duan return true; 3738b818e05SGerd Hoffmann } 3748b818e05SGerd Hoffmann 3758b818e05SGerd Hoffmann static void vfio_display_dmabuf_exit(VFIODisplay *dpy) 3768b818e05SGerd Hoffmann { 3778b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 3788b818e05SGerd Hoffmann 3798b818e05SGerd Hoffmann if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { 3808b818e05SGerd Hoffmann return; 3818b818e05SGerd Hoffmann } 3828b818e05SGerd Hoffmann 3838b818e05SGerd Hoffmann while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { 3848b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 3858b818e05SGerd Hoffmann } 3868b818e05SGerd Hoffmann } 3878b818e05SGerd Hoffmann 38800195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 3898983e3e3STina Zhang void vfio_display_reset(VFIOPCIDevice *vdev) 3908983e3e3STina Zhang { 3918983e3e3STina Zhang if (!vdev || !vdev->dpy || !vdev->dpy->con || 3928983e3e3STina Zhang !vdev->dpy->dmabuf.primary) { 3938983e3e3STina Zhang return; 3948983e3e3STina Zhang } 3958983e3e3STina Zhang 3968983e3e3STina Zhang dpy_gl_scanout_disable(vdev->dpy->con); 3978983e3e3STina Zhang vfio_display_dmabuf_exit(vdev->dpy); 3988983e3e3STina Zhang dpy_gfx_update_full(vdev->dpy->con); 3998983e3e3STina Zhang } 40000195ba7SGerd Hoffmann 40100195ba7SGerd Hoffmann static void vfio_display_region_update(void *opaque) 40200195ba7SGerd Hoffmann { 40300195ba7SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 40400195ba7SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 40500195ba7SGerd Hoffmann struct vfio_device_gfx_plane_info plane = { 40600195ba7SGerd Hoffmann .argsz = sizeof(plane), 40700195ba7SGerd Hoffmann .flags = VFIO_GFX_PLANE_TYPE_REGION 40800195ba7SGerd Hoffmann }; 40900195ba7SGerd Hoffmann pixman_format_code_t format; 41000195ba7SGerd Hoffmann int ret; 41100195ba7SGerd Hoffmann 41200195ba7SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 41300195ba7SGerd Hoffmann if (ret < 0) { 41400195ba7SGerd Hoffmann error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", 41500195ba7SGerd Hoffmann strerror(errno)); 41600195ba7SGerd Hoffmann return; 41700195ba7SGerd Hoffmann } 41800195ba7SGerd Hoffmann if (!plane.drm_format || !plane.size) { 419b290659fSGerd Hoffmann if (dpy->ramfb) { 420b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 4218ec14159SGerd Hoffmann dpy->region.surface = NULL; 422b290659fSGerd Hoffmann } 42300195ba7SGerd Hoffmann return; 42400195ba7SGerd Hoffmann } 42500195ba7SGerd Hoffmann format = qemu_drm_format_to_pixman(plane.drm_format); 42600195ba7SGerd Hoffmann if (!format) { 42700195ba7SGerd Hoffmann return; 42800195ba7SGerd Hoffmann } 42900195ba7SGerd Hoffmann 43000195ba7SGerd Hoffmann if (dpy->region.buffer.size && 43100195ba7SGerd Hoffmann dpy->region.buffer.nr != plane.region_index) { 43200195ba7SGerd Hoffmann /* region changed */ 43300195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 43400195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 43500195ba7SGerd Hoffmann dpy->region.surface = NULL; 43600195ba7SGerd Hoffmann } 43700195ba7SGerd Hoffmann 43800195ba7SGerd Hoffmann if (dpy->region.surface && 43900195ba7SGerd Hoffmann (surface_width(dpy->region.surface) != plane.width || 44000195ba7SGerd Hoffmann surface_height(dpy->region.surface) != plane.height || 44100195ba7SGerd Hoffmann surface_format(dpy->region.surface) != format)) { 44200195ba7SGerd Hoffmann /* size changed */ 44300195ba7SGerd Hoffmann dpy->region.surface = NULL; 44400195ba7SGerd Hoffmann } 44500195ba7SGerd Hoffmann 44600195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 44700195ba7SGerd Hoffmann /* mmap region */ 44800195ba7SGerd Hoffmann ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, 44900195ba7SGerd Hoffmann &dpy->region.buffer, 45000195ba7SGerd Hoffmann plane.region_index, 45100195ba7SGerd Hoffmann "display"); 45200195ba7SGerd Hoffmann if (ret != 0) { 45300195ba7SGerd Hoffmann error_report("%s: vfio_region_setup(%d): %s", 45400195ba7SGerd Hoffmann __func__, plane.region_index, strerror(-ret)); 45500195ba7SGerd Hoffmann goto err; 45600195ba7SGerd Hoffmann } 45700195ba7SGerd Hoffmann ret = vfio_region_mmap(&dpy->region.buffer); 45800195ba7SGerd Hoffmann if (ret != 0) { 45900195ba7SGerd Hoffmann error_report("%s: vfio_region_mmap(%d): %s", __func__, 46000195ba7SGerd Hoffmann plane.region_index, strerror(-ret)); 46100195ba7SGerd Hoffmann goto err; 46200195ba7SGerd Hoffmann } 46300195ba7SGerd Hoffmann assert(dpy->region.buffer.mmaps[0].mmap != NULL); 46400195ba7SGerd Hoffmann } 46500195ba7SGerd Hoffmann 46600195ba7SGerd Hoffmann if (dpy->region.surface == NULL) { 46700195ba7SGerd Hoffmann /* create surface */ 46800195ba7SGerd Hoffmann dpy->region.surface = qemu_create_displaysurface_from 46900195ba7SGerd Hoffmann (plane.width, plane.height, format, 47000195ba7SGerd Hoffmann plane.stride, dpy->region.buffer.mmaps[0].mmap); 47100195ba7SGerd Hoffmann dpy_gfx_replace_surface(dpy->con, dpy->region.surface); 47200195ba7SGerd Hoffmann } 47300195ba7SGerd Hoffmann 47400195ba7SGerd Hoffmann /* full screen update */ 47500195ba7SGerd Hoffmann dpy_gfx_update(dpy->con, 0, 0, 47600195ba7SGerd Hoffmann surface_width(dpy->region.surface), 47700195ba7SGerd Hoffmann surface_height(dpy->region.surface)); 47800195ba7SGerd Hoffmann return; 47900195ba7SGerd Hoffmann 48000195ba7SGerd Hoffmann err: 48100195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 48200195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 48300195ba7SGerd Hoffmann } 48400195ba7SGerd Hoffmann 48500195ba7SGerd Hoffmann static const GraphicHwOps vfio_display_region_ops = { 48600195ba7SGerd Hoffmann .gfx_update = vfio_display_region_update, 48700195ba7SGerd Hoffmann }; 48800195ba7SGerd Hoffmann 489455c009dSZhenzhong Duan static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) 49000195ba7SGerd Hoffmann { 49100195ba7SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 49200195ba7SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 49300195ba7SGerd Hoffmann &vfio_display_region_ops, 49400195ba7SGerd Hoffmann vdev); 495b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 4962fc979cbSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 4979442d8afSZhenzhong Duan if (!vdev->dpy->ramfb) { 498455c009dSZhenzhong Duan return false; 4999442d8afSZhenzhong Duan } 500b290659fSGerd Hoffmann } 501455c009dSZhenzhong Duan return true; 50200195ba7SGerd Hoffmann } 50300195ba7SGerd Hoffmann 50400195ba7SGerd Hoffmann static void vfio_display_region_exit(VFIODisplay *dpy) 50500195ba7SGerd Hoffmann { 50600195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 50700195ba7SGerd Hoffmann return; 50800195ba7SGerd Hoffmann } 50900195ba7SGerd Hoffmann 51000195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 51100195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 51200195ba7SGerd Hoffmann } 51300195ba7SGerd Hoffmann 51400195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 51500195ba7SGerd Hoffmann 516455c009dSZhenzhong Duan bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) 517a9994687SGerd Hoffmann { 518a9994687SGerd Hoffmann struct vfio_device_gfx_plane_info probe; 519a9994687SGerd Hoffmann int ret; 520a9994687SGerd Hoffmann 521a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 522a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 523a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; 524a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 525a9994687SGerd Hoffmann if (ret == 0) { 5268b818e05SGerd Hoffmann return vfio_display_dmabuf_init(vdev, errp); 527a9994687SGerd Hoffmann } 528a9994687SGerd Hoffmann 529a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 530a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 531a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; 532a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 533a9994687SGerd Hoffmann if (ret == 0) { 53400195ba7SGerd Hoffmann return vfio_display_region_init(vdev, errp); 535a9994687SGerd Hoffmann } 536a9994687SGerd Hoffmann 537a9994687SGerd Hoffmann if (vdev->display == ON_OFF_AUTO_AUTO) { 538a9994687SGerd Hoffmann /* not an error in automatic mode */ 539455c009dSZhenzhong Duan return true; 540a9994687SGerd Hoffmann } 541a9994687SGerd Hoffmann 542a9994687SGerd Hoffmann error_setg(errp, "vfio: device doesn't support any (known) display method"); 543455c009dSZhenzhong Duan return false; 544a9994687SGerd Hoffmann } 545a9994687SGerd Hoffmann 546a9994687SGerd Hoffmann void vfio_display_finalize(VFIOPCIDevice *vdev) 547a9994687SGerd Hoffmann { 54800195ba7SGerd Hoffmann if (!vdev->dpy) { 54900195ba7SGerd Hoffmann return; 55000195ba7SGerd Hoffmann } 55100195ba7SGerd Hoffmann 55200195ba7SGerd Hoffmann graphic_console_close(vdev->dpy->con); 5538b818e05SGerd Hoffmann vfio_display_dmabuf_exit(vdev->dpy); 55400195ba7SGerd Hoffmann vfio_display_region_exit(vdev->dpy); 55508479114SGerd Hoffmann vfio_display_edid_exit(vdev->dpy); 55600195ba7SGerd Hoffmann g_free(vdev->dpy); 557a9994687SGerd Hoffmann } 55887417811SMarc-André Lureau 55987417811SMarc-André Lureau static bool migrate_needed(void *opaque) 56087417811SMarc-André Lureau { 56187417811SMarc-André Lureau VFIODisplay *dpy = opaque; 56287417811SMarc-André Lureau bool ramfb_exists = dpy->ramfb != NULL; 56387417811SMarc-André Lureau 56487417811SMarc-André Lureau /* see vfio_display_migration_needed() */ 56587417811SMarc-André Lureau assert(ramfb_exists); 56687417811SMarc-André Lureau return ramfb_exists; 56787417811SMarc-André Lureau } 56887417811SMarc-André Lureau 56987417811SMarc-André Lureau const VMStateDescription vfio_display_vmstate = { 57087417811SMarc-André Lureau .name = "VFIODisplay", 57187417811SMarc-André Lureau .version_id = 1, 57287417811SMarc-André Lureau .minimum_version_id = 1, 57387417811SMarc-André Lureau .needed = migrate_needed, 57465bd53e8SRichard Henderson .fields = (const VMStateField[]) { 57587417811SMarc-André Lureau VMSTATE_STRUCT_POINTER(ramfb, VFIODisplay, ramfb_vmstate, RAMFBState), 57687417811SMarc-André Lureau VMSTATE_END_OF_LIST(), 57787417811SMarc-André Lureau } 57887417811SMarc-André Lureau }; 579