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 "qapi/error.h" 20a9994687SGerd Hoffmann #include "pci.h" 21aa173cb2SCédric Le Goater #include "vfio-display.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 } 10808479114SGerd Hoffmann 109362239c0SAkihiko Odaki static void vfio_display_edid_ui_info(void *opaque, uint32_t idx, 11008479114SGerd Hoffmann QemuUIInfo *info) 11108479114SGerd Hoffmann { 11208479114SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 11308479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 11408479114SGerd Hoffmann 11508479114SGerd Hoffmann if (!dpy->edid_regs) { 116362239c0SAkihiko Odaki return; 11708479114SGerd Hoffmann } 11808479114SGerd Hoffmann 11908479114SGerd Hoffmann if (info->width && info->height) { 12008479114SGerd Hoffmann vfio_display_edid_update(vdev, true, info->width, info->height); 12108479114SGerd Hoffmann } else { 12208479114SGerd Hoffmann vfio_display_edid_update(vdev, false, 0, 0); 12308479114SGerd Hoffmann } 12408479114SGerd Hoffmann } 12508479114SGerd Hoffmann 12683d90192SZhenzhong Duan static bool vfio_display_edid_init(VFIOPCIDevice *vdev, Error **errp) 12708479114SGerd Hoffmann { 12808479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 12908479114SGerd Hoffmann int fd = vdev->vbasedev.fd; 13008479114SGerd Hoffmann int ret; 13108479114SGerd Hoffmann 132e218ccf0SCédric Le Goater ret = vfio_device_get_region_info_type(&vdev->vbasedev, 13308479114SGerd Hoffmann VFIO_REGION_TYPE_GFX, 13408479114SGerd Hoffmann VFIO_REGION_SUBTYPE_GFX_EDID, 13508479114SGerd Hoffmann &dpy->edid_info); 13608479114SGerd Hoffmann if (ret) { 13783d90192SZhenzhong Duan /* Failed to get GFX edid info, allow to go through without edid. */ 13883d90192SZhenzhong Duan return true; 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); 17083d90192SZhenzhong Duan return true; 17108479114SGerd Hoffmann 17208479114SGerd Hoffmann err: 17383d90192SZhenzhong Duan error_setg(errp, "vfio: failed to read GFX edid field"); 17408479114SGerd Hoffmann trace_vfio_display_edid_write_error(); 175f15da599SZhenzhong Duan g_free(dpy->edid_info); 17608479114SGerd Hoffmann g_free(dpy->edid_regs); 177f15da599SZhenzhong Duan dpy->edid_info = NULL; 17808479114SGerd Hoffmann dpy->edid_regs = NULL; 17983d90192SZhenzhong Duan return false; 18008479114SGerd Hoffmann } 18108479114SGerd Hoffmann 18208479114SGerd Hoffmann static void vfio_display_edid_exit(VFIODisplay *dpy) 18308479114SGerd Hoffmann { 18408479114SGerd Hoffmann if (!dpy->edid_regs) { 18508479114SGerd Hoffmann return; 18608479114SGerd Hoffmann } 18708479114SGerd Hoffmann 188f15da599SZhenzhong Duan g_free(dpy->edid_info); 18908479114SGerd Hoffmann g_free(dpy->edid_regs); 19008479114SGerd Hoffmann g_free(dpy->edid_blob); 1918781c701SGerd Hoffmann timer_free(dpy->edid_link_timer); 19208479114SGerd Hoffmann } 19308479114SGerd Hoffmann 1948b818e05SGerd Hoffmann static void vfio_display_update_cursor(VFIODMABuf *dmabuf, 1958b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info *plane) 1968b818e05SGerd Hoffmann { 1978b818e05SGerd Hoffmann if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) { 1988b818e05SGerd Hoffmann dmabuf->pos_x = plane->x_pos; 1998b818e05SGerd Hoffmann dmabuf->pos_y = plane->y_pos; 2008b818e05SGerd Hoffmann dmabuf->pos_updates++; 2018b818e05SGerd Hoffmann } 2028b818e05SGerd Hoffmann if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) { 2038b818e05SGerd Hoffmann dmabuf->hot_x = plane->x_hot; 2048b818e05SGerd Hoffmann dmabuf->hot_y = plane->y_hot; 2058b818e05SGerd Hoffmann dmabuf->hot_updates++; 2068b818e05SGerd Hoffmann } 2078b818e05SGerd Hoffmann } 2088b818e05SGerd Hoffmann 2098b818e05SGerd Hoffmann static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, 2108b818e05SGerd Hoffmann uint32_t plane_type) 2118b818e05SGerd Hoffmann { 2128b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2138b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info plane; 2148b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 2158b818e05SGerd Hoffmann int fd, ret; 216*bb5101aaSQiang Yu uint32_t offset = 0; 2178b818e05SGerd Hoffmann 2188b818e05SGerd Hoffmann memset(&plane, 0, sizeof(plane)); 2198b818e05SGerd Hoffmann plane.argsz = sizeof(plane); 2208b818e05SGerd Hoffmann plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF; 2218b818e05SGerd Hoffmann plane.drm_plane_type = plane_type; 2228b818e05SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 2238b818e05SGerd Hoffmann if (ret < 0) { 2248b818e05SGerd Hoffmann return NULL; 2258b818e05SGerd Hoffmann } 2268b818e05SGerd Hoffmann if (!plane.drm_format || !plane.size) { 2278b818e05SGerd Hoffmann return NULL; 2288b818e05SGerd Hoffmann } 2298b818e05SGerd Hoffmann 2308b818e05SGerd Hoffmann QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) { 2318b818e05SGerd Hoffmann if (dmabuf->dmabuf_id == plane.dmabuf_id) { 2328b818e05SGerd Hoffmann /* found in list, move to head, return it */ 2338b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 2348b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 2358b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 2368b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 2378b818e05SGerd Hoffmann } 2388b818e05SGerd Hoffmann return dmabuf; 2398b818e05SGerd Hoffmann } 2408b818e05SGerd Hoffmann } 2418b818e05SGerd Hoffmann 2428b818e05SGerd Hoffmann fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id); 2438b818e05SGerd Hoffmann if (fd < 0) { 2448b818e05SGerd Hoffmann return NULL; 2458b818e05SGerd Hoffmann } 2468b818e05SGerd Hoffmann 2478b818e05SGerd Hoffmann dmabuf = g_new0(VFIODMABuf, 1); 2488b818e05SGerd Hoffmann dmabuf->dmabuf_id = plane.dmabuf_id; 249*bb5101aaSQiang Yu dmabuf->buf = qemu_dmabuf_new(plane.width, plane.height, &offset, 250*bb5101aaSQiang Yu &plane.stride, 0, 0, plane.width, 251c0fcd633SDongwon Kim plane.height, plane.drm_format, 252*bb5101aaSQiang Yu plane.drm_format_mod, &fd, 1, false, false); 253c0fcd633SDongwon Kim 2548b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 2558b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 2568b818e05SGerd Hoffmann } 2578b818e05SGerd Hoffmann 2588b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 2598b818e05SGerd Hoffmann return dmabuf; 2608b818e05SGerd Hoffmann } 2618b818e05SGerd Hoffmann 2628b818e05SGerd Hoffmann static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) 2638b818e05SGerd Hoffmann { 2648b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 2656779a307SDongwon Kim 266c0fcd633SDongwon Kim qemu_dmabuf_close(dmabuf->buf); 267c0fcd633SDongwon Kim dpy_gl_release_dmabuf(dpy->con, dmabuf->buf); 268c0fcd633SDongwon Kim g_clear_pointer(&dmabuf->buf, qemu_dmabuf_free); 2698b818e05SGerd Hoffmann g_free(dmabuf); 2708b818e05SGerd Hoffmann } 2718b818e05SGerd Hoffmann 2728b818e05SGerd Hoffmann static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) 2738b818e05SGerd Hoffmann { 2748b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2758b818e05SGerd Hoffmann VFIODMABuf *dmabuf, *tmp; 2768b818e05SGerd Hoffmann uint32_t keep = 5; 2778b818e05SGerd Hoffmann 2788b818e05SGerd Hoffmann QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { 2798b818e05SGerd Hoffmann if (keep > 0) { 2808b818e05SGerd Hoffmann keep--; 2818b818e05SGerd Hoffmann continue; 2828b818e05SGerd Hoffmann } 2838b818e05SGerd Hoffmann assert(dmabuf != dpy->dmabuf.primary); 2848b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 2858b818e05SGerd Hoffmann } 2868b818e05SGerd Hoffmann } 2878b818e05SGerd Hoffmann 2888b818e05SGerd Hoffmann static void vfio_display_dmabuf_update(void *opaque) 2898b818e05SGerd Hoffmann { 2908b818e05SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 2918b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2928b818e05SGerd Hoffmann VFIODMABuf *primary, *cursor; 2936779a307SDongwon Kim uint32_t width, height; 294180f3fd2SPhilippe Mathieu-Daudé bool free_bufs = false, new_cursor = false; 2958b818e05SGerd Hoffmann 2968b818e05SGerd Hoffmann primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); 2978b818e05SGerd Hoffmann if (primary == NULL) { 298b290659fSGerd Hoffmann if (dpy->ramfb) { 299b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 300b290659fSGerd Hoffmann } 3018b818e05SGerd Hoffmann return; 3028b818e05SGerd Hoffmann } 3038b818e05SGerd Hoffmann 304c0fcd633SDongwon Kim width = qemu_dmabuf_get_width(primary->buf); 305c0fcd633SDongwon Kim height = qemu_dmabuf_get_height(primary->buf); 3066779a307SDongwon Kim 3078b818e05SGerd Hoffmann if (dpy->dmabuf.primary != primary) { 3088b818e05SGerd Hoffmann dpy->dmabuf.primary = primary; 3096779a307SDongwon Kim qemu_console_resize(dpy->con, width, height); 310c0fcd633SDongwon Kim dpy_gl_scanout_dmabuf(dpy->con, primary->buf); 3118b818e05SGerd Hoffmann free_bufs = true; 3128b818e05SGerd Hoffmann } 3138b818e05SGerd Hoffmann 3148b818e05SGerd Hoffmann cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); 3158b818e05SGerd Hoffmann if (dpy->dmabuf.cursor != cursor) { 3168b818e05SGerd Hoffmann dpy->dmabuf.cursor = cursor; 3178b818e05SGerd Hoffmann new_cursor = true; 3188b818e05SGerd Hoffmann free_bufs = true; 3198b818e05SGerd Hoffmann } 3208b818e05SGerd Hoffmann 3218b818e05SGerd Hoffmann if (cursor && (new_cursor || cursor->hot_updates)) { 3228b818e05SGerd Hoffmann bool have_hot = (cursor->hot_x != 0xffffffff && 3238b818e05SGerd Hoffmann cursor->hot_y != 0xffffffff); 324c0fcd633SDongwon Kim dpy_gl_cursor_dmabuf(dpy->con, cursor->buf, have_hot, 3258b818e05SGerd Hoffmann cursor->hot_x, cursor->hot_y); 3268b818e05SGerd Hoffmann cursor->hot_updates = 0; 3278b818e05SGerd Hoffmann } else if (!cursor && new_cursor) { 3288b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); 3298b818e05SGerd Hoffmann } 3308b818e05SGerd Hoffmann 3318b818e05SGerd Hoffmann if (cursor && cursor->pos_updates) { 3328b818e05SGerd Hoffmann dpy_gl_cursor_position(dpy->con, 3338b818e05SGerd Hoffmann cursor->pos_x, 3348b818e05SGerd Hoffmann cursor->pos_y); 3358b818e05SGerd Hoffmann cursor->pos_updates = 0; 3368b818e05SGerd Hoffmann } 3378b818e05SGerd Hoffmann 3386779a307SDongwon Kim dpy_gl_update(dpy->con, 0, 0, width, height); 3398b818e05SGerd Hoffmann 3408b818e05SGerd Hoffmann if (free_bufs) { 3418b818e05SGerd Hoffmann vfio_display_free_dmabufs(vdev); 3428b818e05SGerd Hoffmann } 3438b818e05SGerd Hoffmann } 3448b818e05SGerd Hoffmann 345a7dfbe28SMarc-André Lureau static int vfio_display_get_flags(void *opaque) 346a7dfbe28SMarc-André Lureau { 347a7dfbe28SMarc-André Lureau return GRAPHIC_FLAGS_GL | GRAPHIC_FLAGS_DMABUF; 348a7dfbe28SMarc-André Lureau } 349a7dfbe28SMarc-André Lureau 3508b818e05SGerd Hoffmann static const GraphicHwOps vfio_display_dmabuf_ops = { 351a7dfbe28SMarc-André Lureau .get_flags = vfio_display_get_flags, 3528b818e05SGerd Hoffmann .gfx_update = vfio_display_dmabuf_update, 35308479114SGerd Hoffmann .ui_info = vfio_display_edid_ui_info, 3548b818e05SGerd Hoffmann }; 3558b818e05SGerd Hoffmann 356455c009dSZhenzhong Duan static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) 3578b818e05SGerd Hoffmann { 3588b818e05SGerd Hoffmann if (!display_opengl) { 3598b818e05SGerd Hoffmann error_setg(errp, "vfio-display-dmabuf: opengl not available"); 360455c009dSZhenzhong Duan return false; 3618b818e05SGerd Hoffmann } 3628b818e05SGerd Hoffmann 3638b818e05SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 3648b818e05SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 3658b818e05SGerd Hoffmann &vfio_display_dmabuf_ops, 3668b818e05SGerd Hoffmann vdev); 367b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 3682fc979cbSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 3699442d8afSZhenzhong Duan if (!vdev->dpy->ramfb) { 370455c009dSZhenzhong Duan return false; 3719442d8afSZhenzhong Duan } 372b290659fSGerd Hoffmann } 37383d90192SZhenzhong Duan return vfio_display_edid_init(vdev, errp); 3748b818e05SGerd Hoffmann } 3758b818e05SGerd Hoffmann 3768b818e05SGerd Hoffmann static void vfio_display_dmabuf_exit(VFIODisplay *dpy) 3778b818e05SGerd Hoffmann { 3788b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 3798b818e05SGerd Hoffmann 3808b818e05SGerd Hoffmann if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { 3818b818e05SGerd Hoffmann return; 3828b818e05SGerd Hoffmann } 3838b818e05SGerd Hoffmann 3848b818e05SGerd Hoffmann while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { 3858b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 3868b818e05SGerd Hoffmann } 3878b818e05SGerd Hoffmann } 3888b818e05SGerd Hoffmann 38900195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 3908983e3e3STina Zhang void vfio_display_reset(VFIOPCIDevice *vdev) 3918983e3e3STina Zhang { 3928983e3e3STina Zhang if (!vdev || !vdev->dpy || !vdev->dpy->con || 3938983e3e3STina Zhang !vdev->dpy->dmabuf.primary) { 3948983e3e3STina Zhang return; 3958983e3e3STina Zhang } 3968983e3e3STina Zhang 3978983e3e3STina Zhang dpy_gl_scanout_disable(vdev->dpy->con); 3988983e3e3STina Zhang vfio_display_dmabuf_exit(vdev->dpy); 3998983e3e3STina Zhang dpy_gfx_update_full(vdev->dpy->con); 4008983e3e3STina Zhang } 40100195ba7SGerd Hoffmann 40200195ba7SGerd Hoffmann static void vfio_display_region_update(void *opaque) 40300195ba7SGerd Hoffmann { 40400195ba7SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 40500195ba7SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 40600195ba7SGerd Hoffmann struct vfio_device_gfx_plane_info plane = { 40700195ba7SGerd Hoffmann .argsz = sizeof(plane), 40800195ba7SGerd Hoffmann .flags = VFIO_GFX_PLANE_TYPE_REGION 40900195ba7SGerd Hoffmann }; 41000195ba7SGerd Hoffmann pixman_format_code_t format; 41100195ba7SGerd Hoffmann int ret; 41200195ba7SGerd Hoffmann 41300195ba7SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 41400195ba7SGerd Hoffmann if (ret < 0) { 41500195ba7SGerd Hoffmann error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", 41600195ba7SGerd Hoffmann strerror(errno)); 41700195ba7SGerd Hoffmann return; 41800195ba7SGerd Hoffmann } 41900195ba7SGerd Hoffmann if (!plane.drm_format || !plane.size) { 420b290659fSGerd Hoffmann if (dpy->ramfb) { 421b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 4228ec14159SGerd Hoffmann dpy->region.surface = NULL; 423b290659fSGerd Hoffmann } 42400195ba7SGerd Hoffmann return; 42500195ba7SGerd Hoffmann } 42600195ba7SGerd Hoffmann format = qemu_drm_format_to_pixman(plane.drm_format); 42700195ba7SGerd Hoffmann if (!format) { 42800195ba7SGerd Hoffmann return; 42900195ba7SGerd Hoffmann } 43000195ba7SGerd Hoffmann 43100195ba7SGerd Hoffmann if (dpy->region.buffer.size && 43200195ba7SGerd Hoffmann dpy->region.buffer.nr != plane.region_index) { 43300195ba7SGerd Hoffmann /* region changed */ 43400195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 43500195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 43600195ba7SGerd Hoffmann dpy->region.surface = NULL; 43700195ba7SGerd Hoffmann } 43800195ba7SGerd Hoffmann 43900195ba7SGerd Hoffmann if (dpy->region.surface && 44000195ba7SGerd Hoffmann (surface_width(dpy->region.surface) != plane.width || 44100195ba7SGerd Hoffmann surface_height(dpy->region.surface) != plane.height || 44200195ba7SGerd Hoffmann surface_format(dpy->region.surface) != format)) { 44300195ba7SGerd Hoffmann /* size changed */ 44400195ba7SGerd Hoffmann dpy->region.surface = NULL; 44500195ba7SGerd Hoffmann } 44600195ba7SGerd Hoffmann 44700195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 44800195ba7SGerd Hoffmann /* mmap region */ 44900195ba7SGerd Hoffmann ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, 45000195ba7SGerd Hoffmann &dpy->region.buffer, 45100195ba7SGerd Hoffmann plane.region_index, 45200195ba7SGerd Hoffmann "display"); 45300195ba7SGerd Hoffmann if (ret != 0) { 45400195ba7SGerd Hoffmann error_report("%s: vfio_region_setup(%d): %s", 45500195ba7SGerd Hoffmann __func__, plane.region_index, strerror(-ret)); 45600195ba7SGerd Hoffmann goto err; 45700195ba7SGerd Hoffmann } 45800195ba7SGerd Hoffmann ret = vfio_region_mmap(&dpy->region.buffer); 45900195ba7SGerd Hoffmann if (ret != 0) { 46000195ba7SGerd Hoffmann error_report("%s: vfio_region_mmap(%d): %s", __func__, 46100195ba7SGerd Hoffmann plane.region_index, strerror(-ret)); 46200195ba7SGerd Hoffmann goto err; 46300195ba7SGerd Hoffmann } 46400195ba7SGerd Hoffmann assert(dpy->region.buffer.mmaps[0].mmap != NULL); 46500195ba7SGerd Hoffmann } 46600195ba7SGerd Hoffmann 46700195ba7SGerd Hoffmann if (dpy->region.surface == NULL) { 46800195ba7SGerd Hoffmann /* create surface */ 46900195ba7SGerd Hoffmann dpy->region.surface = qemu_create_displaysurface_from 47000195ba7SGerd Hoffmann (plane.width, plane.height, format, 47100195ba7SGerd Hoffmann plane.stride, dpy->region.buffer.mmaps[0].mmap); 47200195ba7SGerd Hoffmann dpy_gfx_replace_surface(dpy->con, dpy->region.surface); 47300195ba7SGerd Hoffmann } 47400195ba7SGerd Hoffmann 47500195ba7SGerd Hoffmann /* full screen update */ 47600195ba7SGerd Hoffmann dpy_gfx_update(dpy->con, 0, 0, 47700195ba7SGerd Hoffmann surface_width(dpy->region.surface), 47800195ba7SGerd Hoffmann surface_height(dpy->region.surface)); 47900195ba7SGerd Hoffmann return; 48000195ba7SGerd Hoffmann 48100195ba7SGerd Hoffmann err: 48200195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 48300195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 48400195ba7SGerd Hoffmann } 48500195ba7SGerd Hoffmann 48600195ba7SGerd Hoffmann static const GraphicHwOps vfio_display_region_ops = { 48700195ba7SGerd Hoffmann .gfx_update = vfio_display_region_update, 48800195ba7SGerd Hoffmann }; 48900195ba7SGerd Hoffmann 490455c009dSZhenzhong Duan static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) 49100195ba7SGerd Hoffmann { 49200195ba7SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 49300195ba7SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 49400195ba7SGerd Hoffmann &vfio_display_region_ops, 49500195ba7SGerd Hoffmann vdev); 496b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 4972fc979cbSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 4989442d8afSZhenzhong Duan if (!vdev->dpy->ramfb) { 499455c009dSZhenzhong Duan return false; 5009442d8afSZhenzhong Duan } 501b290659fSGerd Hoffmann } 502455c009dSZhenzhong Duan return true; 50300195ba7SGerd Hoffmann } 50400195ba7SGerd Hoffmann 50500195ba7SGerd Hoffmann static void vfio_display_region_exit(VFIODisplay *dpy) 50600195ba7SGerd Hoffmann { 50700195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 50800195ba7SGerd Hoffmann return; 50900195ba7SGerd Hoffmann } 51000195ba7SGerd Hoffmann 51100195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 51200195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 51300195ba7SGerd Hoffmann } 51400195ba7SGerd Hoffmann 51500195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 51600195ba7SGerd Hoffmann 517455c009dSZhenzhong Duan bool vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) 518a9994687SGerd Hoffmann { 519a9994687SGerd Hoffmann struct vfio_device_gfx_plane_info probe; 520a9994687SGerd Hoffmann int ret; 521a9994687SGerd Hoffmann 522a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 523a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 524a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; 525a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 526a9994687SGerd Hoffmann if (ret == 0) { 5278b818e05SGerd Hoffmann return vfio_display_dmabuf_init(vdev, errp); 528a9994687SGerd Hoffmann } 529a9994687SGerd Hoffmann 530a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 531a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 532a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; 533a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 534a9994687SGerd Hoffmann if (ret == 0) { 53500195ba7SGerd Hoffmann return vfio_display_region_init(vdev, errp); 536a9994687SGerd Hoffmann } 537a9994687SGerd Hoffmann 538a9994687SGerd Hoffmann if (vdev->display == ON_OFF_AUTO_AUTO) { 539a9994687SGerd Hoffmann /* not an error in automatic mode */ 540455c009dSZhenzhong Duan return true; 541a9994687SGerd Hoffmann } 542a9994687SGerd Hoffmann 543a9994687SGerd Hoffmann error_setg(errp, "vfio: device doesn't support any (known) display method"); 544455c009dSZhenzhong Duan return false; 545a9994687SGerd Hoffmann } 546a9994687SGerd Hoffmann 547a9994687SGerd Hoffmann void vfio_display_finalize(VFIOPCIDevice *vdev) 548a9994687SGerd Hoffmann { 54900195ba7SGerd Hoffmann if (!vdev->dpy) { 55000195ba7SGerd Hoffmann return; 55100195ba7SGerd Hoffmann } 55200195ba7SGerd Hoffmann 55300195ba7SGerd Hoffmann graphic_console_close(vdev->dpy->con); 5548b818e05SGerd Hoffmann vfio_display_dmabuf_exit(vdev->dpy); 55500195ba7SGerd Hoffmann vfio_display_region_exit(vdev->dpy); 55608479114SGerd Hoffmann vfio_display_edid_exit(vdev->dpy); 55700195ba7SGerd Hoffmann g_free(vdev->dpy); 558a9994687SGerd Hoffmann } 55987417811SMarc-André Lureau 56087417811SMarc-André Lureau static bool migrate_needed(void *opaque) 56187417811SMarc-André Lureau { 56287417811SMarc-André Lureau VFIODisplay *dpy = opaque; 56387417811SMarc-André Lureau bool ramfb_exists = dpy->ramfb != NULL; 56487417811SMarc-André Lureau 56587417811SMarc-André Lureau /* see vfio_display_migration_needed() */ 56687417811SMarc-André Lureau assert(ramfb_exists); 56787417811SMarc-André Lureau return ramfb_exists; 56887417811SMarc-André Lureau } 56987417811SMarc-André Lureau 57087417811SMarc-André Lureau const VMStateDescription vfio_display_vmstate = { 57187417811SMarc-André Lureau .name = "VFIODisplay", 57287417811SMarc-André Lureau .version_id = 1, 57387417811SMarc-André Lureau .minimum_version_id = 1, 57487417811SMarc-André Lureau .needed = migrate_needed, 57565bd53e8SRichard Henderson .fields = (const VMStateField[]) { 57687417811SMarc-André Lureau VMSTATE_STRUCT_POINTER(ramfb, VFIODisplay, ramfb_vmstate, RAMFBState), 57787417811SMarc-André Lureau VMSTATE_END_OF_LIST(), 57887417811SMarc-André Lureau } 57987417811SMarc-André Lureau }; 580