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 1708479114SGerd Hoffmann #include "hw/display/edid.h" 18a9994687SGerd Hoffmann #include "ui/console.h" 19a9994687SGerd Hoffmann #include "qapi/error.h" 20a9994687SGerd Hoffmann #include "pci.h" 2108479114SGerd Hoffmann #include "trace.h" 22a9994687SGerd Hoffmann 238b818e05SGerd Hoffmann #ifndef DRM_PLANE_TYPE_PRIMARY 248b818e05SGerd Hoffmann # define DRM_PLANE_TYPE_PRIMARY 1 258b818e05SGerd Hoffmann # define DRM_PLANE_TYPE_CURSOR 2 268b818e05SGerd Hoffmann #endif 278b818e05SGerd Hoffmann 2808479114SGerd Hoffmann #define pread_field(_fd, _reg, _ptr, _fld) \ 2908479114SGerd Hoffmann (sizeof(_ptr->_fld) != \ 3008479114SGerd Hoffmann pread(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \ 3108479114SGerd Hoffmann _reg->offset + offsetof(typeof(*_ptr), _fld))) 3208479114SGerd Hoffmann 3308479114SGerd Hoffmann #define pwrite_field(_fd, _reg, _ptr, _fld) \ 3408479114SGerd Hoffmann (sizeof(_ptr->_fld) != \ 3508479114SGerd Hoffmann pwrite(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \ 3608479114SGerd Hoffmann _reg->offset + offsetof(typeof(*_ptr), _fld))) 3708479114SGerd Hoffmann 3808479114SGerd Hoffmann 398781c701SGerd Hoffmann static void vfio_display_edid_link_up(void *opaque) 408781c701SGerd Hoffmann { 418781c701SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 428781c701SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 438781c701SGerd Hoffmann int fd = vdev->vbasedev.fd; 448781c701SGerd Hoffmann 458781c701SGerd Hoffmann dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_UP; 468781c701SGerd Hoffmann if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) { 478781c701SGerd Hoffmann goto err; 488781c701SGerd Hoffmann } 498781c701SGerd Hoffmann trace_vfio_display_edid_link_up(); 508781c701SGerd Hoffmann return; 518781c701SGerd Hoffmann 528781c701SGerd Hoffmann err: 538781c701SGerd Hoffmann trace_vfio_display_edid_write_error(); 548781c701SGerd Hoffmann } 558781c701SGerd Hoffmann 5608479114SGerd Hoffmann static void vfio_display_edid_update(VFIOPCIDevice *vdev, bool enabled, 5708479114SGerd Hoffmann int prefx, int prefy) 5808479114SGerd Hoffmann { 5908479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 6008479114SGerd Hoffmann int fd = vdev->vbasedev.fd; 6108479114SGerd Hoffmann qemu_edid_info edid = { 6208479114SGerd Hoffmann .maxx = dpy->edid_regs->max_xres, 6308479114SGerd Hoffmann .maxy = dpy->edid_regs->max_yres, 64c62a0c7cSGerd Hoffmann .prefx = prefx ?: vdev->display_xres, 65c62a0c7cSGerd Hoffmann .prefy = prefy ?: vdev->display_yres, 6608479114SGerd Hoffmann }; 6708479114SGerd Hoffmann 688781c701SGerd Hoffmann timer_del(dpy->edid_link_timer); 6908479114SGerd Hoffmann dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_DOWN; 7008479114SGerd Hoffmann if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) { 7108479114SGerd Hoffmann goto err; 7208479114SGerd Hoffmann } 7308479114SGerd Hoffmann trace_vfio_display_edid_link_down(); 7408479114SGerd Hoffmann 7508479114SGerd Hoffmann if (!enabled) { 7608479114SGerd Hoffmann return; 7708479114SGerd Hoffmann } 7808479114SGerd Hoffmann 7908479114SGerd Hoffmann if (edid.maxx && edid.prefx > edid.maxx) { 8008479114SGerd Hoffmann edid.prefx = edid.maxx; 8108479114SGerd Hoffmann } 8208479114SGerd Hoffmann if (edid.maxy && edid.prefy > edid.maxy) { 8308479114SGerd Hoffmann edid.prefy = edid.maxy; 8408479114SGerd Hoffmann } 8508479114SGerd Hoffmann qemu_edid_generate(dpy->edid_blob, 8608479114SGerd Hoffmann dpy->edid_regs->edid_max_size, 8708479114SGerd Hoffmann &edid); 8808479114SGerd Hoffmann trace_vfio_display_edid_update(edid.prefx, edid.prefy); 8908479114SGerd Hoffmann 9008479114SGerd Hoffmann dpy->edid_regs->edid_size = qemu_edid_size(dpy->edid_blob); 9108479114SGerd Hoffmann if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, edid_size)) { 9208479114SGerd Hoffmann goto err; 9308479114SGerd Hoffmann } 9408479114SGerd Hoffmann if (pwrite(fd, dpy->edid_blob, dpy->edid_regs->edid_size, 9508479114SGerd Hoffmann dpy->edid_info->offset + dpy->edid_regs->edid_offset) 9608479114SGerd Hoffmann != dpy->edid_regs->edid_size) { 9708479114SGerd Hoffmann goto err; 9808479114SGerd Hoffmann } 9908479114SGerd Hoffmann 1008781c701SGerd Hoffmann timer_mod(dpy->edid_link_timer, 1018781c701SGerd Hoffmann qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 100); 10208479114SGerd Hoffmann return; 10308479114SGerd Hoffmann 10408479114SGerd Hoffmann err: 10508479114SGerd Hoffmann trace_vfio_display_edid_write_error(); 10608479114SGerd Hoffmann return; 10708479114SGerd Hoffmann } 10808479114SGerd Hoffmann 109*362239c0SAkihiko 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) { 116*362239c0SAkihiko 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 12608479114SGerd Hoffmann static void vfio_display_edid_init(VFIOPCIDevice *vdev) 12708479114SGerd Hoffmann { 12808479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 12908479114SGerd Hoffmann int fd = vdev->vbasedev.fd; 13008479114SGerd Hoffmann int ret; 13108479114SGerd Hoffmann 13208479114SGerd Hoffmann ret = vfio_get_dev_region_info(&vdev->vbasedev, 13308479114SGerd Hoffmann VFIO_REGION_TYPE_GFX, 13408479114SGerd Hoffmann VFIO_REGION_SUBTYPE_GFX_EDID, 13508479114SGerd Hoffmann &dpy->edid_info); 13608479114SGerd Hoffmann if (ret) { 13708479114SGerd Hoffmann return; 13808479114SGerd Hoffmann } 13908479114SGerd Hoffmann 14008479114SGerd Hoffmann trace_vfio_display_edid_available(); 14108479114SGerd Hoffmann dpy->edid_regs = g_new0(struct vfio_region_gfx_edid, 1); 14208479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_offset)) { 14308479114SGerd Hoffmann goto err; 14408479114SGerd Hoffmann } 14508479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_max_size)) { 14608479114SGerd Hoffmann goto err; 14708479114SGerd Hoffmann } 14808479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_xres)) { 14908479114SGerd Hoffmann goto err; 15008479114SGerd Hoffmann } 15108479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_yres)) { 15208479114SGerd Hoffmann goto err; 15308479114SGerd Hoffmann } 15408479114SGerd Hoffmann 15508479114SGerd Hoffmann dpy->edid_blob = g_malloc0(dpy->edid_regs->edid_max_size); 15608479114SGerd Hoffmann 157c62a0c7cSGerd Hoffmann /* if xres + yres properties are unset use the maximum resolution */ 158c62a0c7cSGerd Hoffmann if (!vdev->display_xres) { 159c62a0c7cSGerd Hoffmann vdev->display_xres = dpy->edid_regs->max_xres; 160c62a0c7cSGerd Hoffmann } 161c62a0c7cSGerd Hoffmann if (!vdev->display_yres) { 162c62a0c7cSGerd Hoffmann vdev->display_yres = dpy->edid_regs->max_yres; 163c62a0c7cSGerd Hoffmann } 164c62a0c7cSGerd Hoffmann 1658781c701SGerd Hoffmann dpy->edid_link_timer = timer_new_ms(QEMU_CLOCK_REALTIME, 1668781c701SGerd Hoffmann vfio_display_edid_link_up, vdev); 1678781c701SGerd Hoffmann 16808479114SGerd Hoffmann vfio_display_edid_update(vdev, true, 0, 0); 16908479114SGerd Hoffmann return; 17008479114SGerd Hoffmann 17108479114SGerd Hoffmann err: 17208479114SGerd Hoffmann trace_vfio_display_edid_write_error(); 17308479114SGerd Hoffmann g_free(dpy->edid_regs); 17408479114SGerd Hoffmann dpy->edid_regs = NULL; 17508479114SGerd Hoffmann return; 17608479114SGerd Hoffmann } 17708479114SGerd Hoffmann 17808479114SGerd Hoffmann static void vfio_display_edid_exit(VFIODisplay *dpy) 17908479114SGerd Hoffmann { 18008479114SGerd Hoffmann if (!dpy->edid_regs) { 18108479114SGerd Hoffmann return; 18208479114SGerd Hoffmann } 18308479114SGerd Hoffmann 18408479114SGerd Hoffmann g_free(dpy->edid_regs); 18508479114SGerd Hoffmann g_free(dpy->edid_blob); 1868781c701SGerd Hoffmann timer_free(dpy->edid_link_timer); 18708479114SGerd Hoffmann } 18808479114SGerd Hoffmann 1898b818e05SGerd Hoffmann static void vfio_display_update_cursor(VFIODMABuf *dmabuf, 1908b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info *plane) 1918b818e05SGerd Hoffmann { 1928b818e05SGerd Hoffmann if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) { 1938b818e05SGerd Hoffmann dmabuf->pos_x = plane->x_pos; 1948b818e05SGerd Hoffmann dmabuf->pos_y = plane->y_pos; 1958b818e05SGerd Hoffmann dmabuf->pos_updates++; 1968b818e05SGerd Hoffmann } 1978b818e05SGerd Hoffmann if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) { 1988b818e05SGerd Hoffmann dmabuf->hot_x = plane->x_hot; 1998b818e05SGerd Hoffmann dmabuf->hot_y = plane->y_hot; 2008b818e05SGerd Hoffmann dmabuf->hot_updates++; 2018b818e05SGerd Hoffmann } 2028b818e05SGerd Hoffmann } 2038b818e05SGerd Hoffmann 2048b818e05SGerd Hoffmann static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, 2058b818e05SGerd Hoffmann uint32_t plane_type) 2068b818e05SGerd Hoffmann { 2078b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2088b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info plane; 2098b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 2108b818e05SGerd Hoffmann int fd, ret; 2118b818e05SGerd Hoffmann 2128b818e05SGerd Hoffmann memset(&plane, 0, sizeof(plane)); 2138b818e05SGerd Hoffmann plane.argsz = sizeof(plane); 2148b818e05SGerd Hoffmann plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF; 2158b818e05SGerd Hoffmann plane.drm_plane_type = plane_type; 2168b818e05SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 2178b818e05SGerd Hoffmann if (ret < 0) { 2188b818e05SGerd Hoffmann return NULL; 2198b818e05SGerd Hoffmann } 2208b818e05SGerd Hoffmann if (!plane.drm_format || !plane.size) { 2218b818e05SGerd Hoffmann return NULL; 2228b818e05SGerd Hoffmann } 2238b818e05SGerd Hoffmann 2248b818e05SGerd Hoffmann QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) { 2258b818e05SGerd Hoffmann if (dmabuf->dmabuf_id == plane.dmabuf_id) { 2268b818e05SGerd Hoffmann /* found in list, move to head, return it */ 2278b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 2288b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 2298b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 2308b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 2318b818e05SGerd Hoffmann } 2328b818e05SGerd Hoffmann return dmabuf; 2338b818e05SGerd Hoffmann } 2348b818e05SGerd Hoffmann } 2358b818e05SGerd Hoffmann 2368b818e05SGerd Hoffmann fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id); 2378b818e05SGerd Hoffmann if (fd < 0) { 2388b818e05SGerd Hoffmann return NULL; 2398b818e05SGerd Hoffmann } 2408b818e05SGerd Hoffmann 2418b818e05SGerd Hoffmann dmabuf = g_new0(VFIODMABuf, 1); 2428b818e05SGerd Hoffmann dmabuf->dmabuf_id = plane.dmabuf_id; 2438b818e05SGerd Hoffmann dmabuf->buf.width = plane.width; 2448b818e05SGerd Hoffmann dmabuf->buf.height = plane.height; 2458b818e05SGerd Hoffmann dmabuf->buf.stride = plane.stride; 2468b818e05SGerd Hoffmann dmabuf->buf.fourcc = plane.drm_format; 247a6c9d5daSGerd Hoffmann dmabuf->buf.modifier = plane.drm_format_mod; 2488b818e05SGerd Hoffmann dmabuf->buf.fd = fd; 2498b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 2508b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 2518b818e05SGerd Hoffmann } 2528b818e05SGerd Hoffmann 2538b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 2548b818e05SGerd Hoffmann return dmabuf; 2558b818e05SGerd Hoffmann } 2568b818e05SGerd Hoffmann 2578b818e05SGerd Hoffmann static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) 2588b818e05SGerd Hoffmann { 2598b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 2608b818e05SGerd Hoffmann dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); 2618b818e05SGerd Hoffmann close(dmabuf->buf.fd); 2628b818e05SGerd Hoffmann g_free(dmabuf); 2638b818e05SGerd Hoffmann } 2648b818e05SGerd Hoffmann 2658b818e05SGerd Hoffmann static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) 2668b818e05SGerd Hoffmann { 2678b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2688b818e05SGerd Hoffmann VFIODMABuf *dmabuf, *tmp; 2698b818e05SGerd Hoffmann uint32_t keep = 5; 2708b818e05SGerd Hoffmann 2718b818e05SGerd Hoffmann QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { 2728b818e05SGerd Hoffmann if (keep > 0) { 2738b818e05SGerd Hoffmann keep--; 2748b818e05SGerd Hoffmann continue; 2758b818e05SGerd Hoffmann } 2768b818e05SGerd Hoffmann assert(dmabuf != dpy->dmabuf.primary); 2778b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 2788b818e05SGerd Hoffmann } 2798b818e05SGerd Hoffmann } 2808b818e05SGerd Hoffmann 2818b818e05SGerd Hoffmann static void vfio_display_dmabuf_update(void *opaque) 2828b818e05SGerd Hoffmann { 2838b818e05SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 2848b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2858b818e05SGerd Hoffmann VFIODMABuf *primary, *cursor; 286180f3fd2SPhilippe Mathieu-Daudé bool free_bufs = false, new_cursor = false; 2878b818e05SGerd Hoffmann 2888b818e05SGerd Hoffmann primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); 2898b818e05SGerd Hoffmann if (primary == NULL) { 290b290659fSGerd Hoffmann if (dpy->ramfb) { 291b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 292b290659fSGerd Hoffmann } 2938b818e05SGerd Hoffmann return; 2948b818e05SGerd Hoffmann } 2958b818e05SGerd Hoffmann 2968b818e05SGerd Hoffmann if (dpy->dmabuf.primary != primary) { 2978b818e05SGerd Hoffmann dpy->dmabuf.primary = primary; 2988b818e05SGerd Hoffmann qemu_console_resize(dpy->con, 2998b818e05SGerd Hoffmann primary->buf.width, primary->buf.height); 3008b818e05SGerd Hoffmann dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); 3018b818e05SGerd Hoffmann free_bufs = true; 3028b818e05SGerd Hoffmann } 3038b818e05SGerd Hoffmann 3048b818e05SGerd Hoffmann cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); 3058b818e05SGerd Hoffmann if (dpy->dmabuf.cursor != cursor) { 3068b818e05SGerd Hoffmann dpy->dmabuf.cursor = cursor; 3078b818e05SGerd Hoffmann new_cursor = true; 3088b818e05SGerd Hoffmann free_bufs = true; 3098b818e05SGerd Hoffmann } 3108b818e05SGerd Hoffmann 3118b818e05SGerd Hoffmann if (cursor && (new_cursor || cursor->hot_updates)) { 3128b818e05SGerd Hoffmann bool have_hot = (cursor->hot_x != 0xffffffff && 3138b818e05SGerd Hoffmann cursor->hot_y != 0xffffffff); 3148b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, 3158b818e05SGerd Hoffmann cursor->hot_x, cursor->hot_y); 3168b818e05SGerd Hoffmann cursor->hot_updates = 0; 3178b818e05SGerd Hoffmann } else if (!cursor && new_cursor) { 3188b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); 3198b818e05SGerd Hoffmann } 3208b818e05SGerd Hoffmann 3218b818e05SGerd Hoffmann if (cursor && cursor->pos_updates) { 3228b818e05SGerd Hoffmann dpy_gl_cursor_position(dpy->con, 3238b818e05SGerd Hoffmann cursor->pos_x, 3248b818e05SGerd Hoffmann cursor->pos_y); 3258b818e05SGerd Hoffmann cursor->pos_updates = 0; 3268b818e05SGerd Hoffmann } 3278b818e05SGerd Hoffmann 3288b818e05SGerd Hoffmann dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); 3298b818e05SGerd Hoffmann 3308b818e05SGerd Hoffmann if (free_bufs) { 3318b818e05SGerd Hoffmann vfio_display_free_dmabufs(vdev); 3328b818e05SGerd Hoffmann } 3338b818e05SGerd Hoffmann } 3348b818e05SGerd Hoffmann 335a7dfbe28SMarc-André Lureau static int vfio_display_get_flags(void *opaque) 336a7dfbe28SMarc-André Lureau { 337a7dfbe28SMarc-André Lureau return GRAPHIC_FLAGS_GL | GRAPHIC_FLAGS_DMABUF; 338a7dfbe28SMarc-André Lureau } 339a7dfbe28SMarc-André Lureau 3408b818e05SGerd Hoffmann static const GraphicHwOps vfio_display_dmabuf_ops = { 341a7dfbe28SMarc-André Lureau .get_flags = vfio_display_get_flags, 3428b818e05SGerd Hoffmann .gfx_update = vfio_display_dmabuf_update, 34308479114SGerd Hoffmann .ui_info = vfio_display_edid_ui_info, 3448b818e05SGerd Hoffmann }; 3458b818e05SGerd Hoffmann 3468b818e05SGerd Hoffmann static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) 3478b818e05SGerd Hoffmann { 3488b818e05SGerd Hoffmann if (!display_opengl) { 3498b818e05SGerd Hoffmann error_setg(errp, "vfio-display-dmabuf: opengl not available"); 3508b818e05SGerd Hoffmann return -1; 3518b818e05SGerd Hoffmann } 3528b818e05SGerd Hoffmann 3538b818e05SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 3548b818e05SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 3558b818e05SGerd Hoffmann &vfio_display_dmabuf_ops, 3568b818e05SGerd Hoffmann vdev); 357b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 3582fc979cbSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 359b290659fSGerd Hoffmann } 36008479114SGerd Hoffmann vfio_display_edid_init(vdev); 3618b818e05SGerd Hoffmann return 0; 3628b818e05SGerd Hoffmann } 3638b818e05SGerd Hoffmann 3648b818e05SGerd Hoffmann static void vfio_display_dmabuf_exit(VFIODisplay *dpy) 3658b818e05SGerd Hoffmann { 3668b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 3678b818e05SGerd Hoffmann 3688b818e05SGerd Hoffmann if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { 3698b818e05SGerd Hoffmann return; 3708b818e05SGerd Hoffmann } 3718b818e05SGerd Hoffmann 3728b818e05SGerd Hoffmann while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { 3738b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 3748b818e05SGerd Hoffmann } 3758b818e05SGerd Hoffmann } 3768b818e05SGerd Hoffmann 37700195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 3788983e3e3STina Zhang void vfio_display_reset(VFIOPCIDevice *vdev) 3798983e3e3STina Zhang { 3808983e3e3STina Zhang if (!vdev || !vdev->dpy || !vdev->dpy->con || 3818983e3e3STina Zhang !vdev->dpy->dmabuf.primary) { 3828983e3e3STina Zhang return; 3838983e3e3STina Zhang } 3848983e3e3STina Zhang 3858983e3e3STina Zhang dpy_gl_scanout_disable(vdev->dpy->con); 3868983e3e3STina Zhang vfio_display_dmabuf_exit(vdev->dpy); 3878983e3e3STina Zhang dpy_gfx_update_full(vdev->dpy->con); 3888983e3e3STina Zhang } 38900195ba7SGerd Hoffmann 39000195ba7SGerd Hoffmann static void vfio_display_region_update(void *opaque) 39100195ba7SGerd Hoffmann { 39200195ba7SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 39300195ba7SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 39400195ba7SGerd Hoffmann struct vfio_device_gfx_plane_info plane = { 39500195ba7SGerd Hoffmann .argsz = sizeof(plane), 39600195ba7SGerd Hoffmann .flags = VFIO_GFX_PLANE_TYPE_REGION 39700195ba7SGerd Hoffmann }; 39800195ba7SGerd Hoffmann pixman_format_code_t format; 39900195ba7SGerd Hoffmann int ret; 40000195ba7SGerd Hoffmann 40100195ba7SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 40200195ba7SGerd Hoffmann if (ret < 0) { 40300195ba7SGerd Hoffmann error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", 40400195ba7SGerd Hoffmann strerror(errno)); 40500195ba7SGerd Hoffmann return; 40600195ba7SGerd Hoffmann } 40700195ba7SGerd Hoffmann if (!plane.drm_format || !plane.size) { 408b290659fSGerd Hoffmann if (dpy->ramfb) { 409b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 4108ec14159SGerd Hoffmann dpy->region.surface = NULL; 411b290659fSGerd Hoffmann } 41200195ba7SGerd Hoffmann return; 41300195ba7SGerd Hoffmann } 41400195ba7SGerd Hoffmann format = qemu_drm_format_to_pixman(plane.drm_format); 41500195ba7SGerd Hoffmann if (!format) { 41600195ba7SGerd Hoffmann return; 41700195ba7SGerd Hoffmann } 41800195ba7SGerd Hoffmann 41900195ba7SGerd Hoffmann if (dpy->region.buffer.size && 42000195ba7SGerd Hoffmann dpy->region.buffer.nr != plane.region_index) { 42100195ba7SGerd Hoffmann /* region changed */ 42200195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 42300195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 42400195ba7SGerd Hoffmann dpy->region.surface = NULL; 42500195ba7SGerd Hoffmann } 42600195ba7SGerd Hoffmann 42700195ba7SGerd Hoffmann if (dpy->region.surface && 42800195ba7SGerd Hoffmann (surface_width(dpy->region.surface) != plane.width || 42900195ba7SGerd Hoffmann surface_height(dpy->region.surface) != plane.height || 43000195ba7SGerd Hoffmann surface_format(dpy->region.surface) != format)) { 43100195ba7SGerd Hoffmann /* size changed */ 43200195ba7SGerd Hoffmann dpy->region.surface = NULL; 43300195ba7SGerd Hoffmann } 43400195ba7SGerd Hoffmann 43500195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 43600195ba7SGerd Hoffmann /* mmap region */ 43700195ba7SGerd Hoffmann ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, 43800195ba7SGerd Hoffmann &dpy->region.buffer, 43900195ba7SGerd Hoffmann plane.region_index, 44000195ba7SGerd Hoffmann "display"); 44100195ba7SGerd Hoffmann if (ret != 0) { 44200195ba7SGerd Hoffmann error_report("%s: vfio_region_setup(%d): %s", 44300195ba7SGerd Hoffmann __func__, plane.region_index, strerror(-ret)); 44400195ba7SGerd Hoffmann goto err; 44500195ba7SGerd Hoffmann } 44600195ba7SGerd Hoffmann ret = vfio_region_mmap(&dpy->region.buffer); 44700195ba7SGerd Hoffmann if (ret != 0) { 44800195ba7SGerd Hoffmann error_report("%s: vfio_region_mmap(%d): %s", __func__, 44900195ba7SGerd Hoffmann plane.region_index, strerror(-ret)); 45000195ba7SGerd Hoffmann goto err; 45100195ba7SGerd Hoffmann } 45200195ba7SGerd Hoffmann assert(dpy->region.buffer.mmaps[0].mmap != NULL); 45300195ba7SGerd Hoffmann } 45400195ba7SGerd Hoffmann 45500195ba7SGerd Hoffmann if (dpy->region.surface == NULL) { 45600195ba7SGerd Hoffmann /* create surface */ 45700195ba7SGerd Hoffmann dpy->region.surface = qemu_create_displaysurface_from 45800195ba7SGerd Hoffmann (plane.width, plane.height, format, 45900195ba7SGerd Hoffmann plane.stride, dpy->region.buffer.mmaps[0].mmap); 46000195ba7SGerd Hoffmann dpy_gfx_replace_surface(dpy->con, dpy->region.surface); 46100195ba7SGerd Hoffmann } 46200195ba7SGerd Hoffmann 46300195ba7SGerd Hoffmann /* full screen update */ 46400195ba7SGerd Hoffmann dpy_gfx_update(dpy->con, 0, 0, 46500195ba7SGerd Hoffmann surface_width(dpy->region.surface), 46600195ba7SGerd Hoffmann surface_height(dpy->region.surface)); 46700195ba7SGerd Hoffmann return; 46800195ba7SGerd Hoffmann 46900195ba7SGerd Hoffmann err: 47000195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 47100195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 47200195ba7SGerd Hoffmann } 47300195ba7SGerd Hoffmann 47400195ba7SGerd Hoffmann static const GraphicHwOps vfio_display_region_ops = { 47500195ba7SGerd Hoffmann .gfx_update = vfio_display_region_update, 47600195ba7SGerd Hoffmann }; 47700195ba7SGerd Hoffmann 47800195ba7SGerd Hoffmann static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) 47900195ba7SGerd Hoffmann { 48000195ba7SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 48100195ba7SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 48200195ba7SGerd Hoffmann &vfio_display_region_ops, 48300195ba7SGerd Hoffmann vdev); 484b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 4852fc979cbSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 486b290659fSGerd Hoffmann } 48700195ba7SGerd Hoffmann return 0; 48800195ba7SGerd Hoffmann } 48900195ba7SGerd Hoffmann 49000195ba7SGerd Hoffmann static void vfio_display_region_exit(VFIODisplay *dpy) 49100195ba7SGerd Hoffmann { 49200195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 49300195ba7SGerd Hoffmann return; 49400195ba7SGerd Hoffmann } 49500195ba7SGerd Hoffmann 49600195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 49700195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 49800195ba7SGerd Hoffmann } 49900195ba7SGerd Hoffmann 50000195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 50100195ba7SGerd Hoffmann 502a9994687SGerd Hoffmann int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) 503a9994687SGerd Hoffmann { 504a9994687SGerd Hoffmann struct vfio_device_gfx_plane_info probe; 505a9994687SGerd Hoffmann int ret; 506a9994687SGerd Hoffmann 507a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 508a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 509a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; 510a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 511a9994687SGerd Hoffmann if (ret == 0) { 5128b818e05SGerd Hoffmann return vfio_display_dmabuf_init(vdev, errp); 513a9994687SGerd Hoffmann } 514a9994687SGerd Hoffmann 515a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 516a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 517a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; 518a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 519a9994687SGerd Hoffmann if (ret == 0) { 52000195ba7SGerd Hoffmann return vfio_display_region_init(vdev, errp); 521a9994687SGerd Hoffmann } 522a9994687SGerd Hoffmann 523a9994687SGerd Hoffmann if (vdev->display == ON_OFF_AUTO_AUTO) { 524a9994687SGerd Hoffmann /* not an error in automatic mode */ 525a9994687SGerd Hoffmann return 0; 526a9994687SGerd Hoffmann } 527a9994687SGerd Hoffmann 528a9994687SGerd Hoffmann error_setg(errp, "vfio: device doesn't support any (known) display method"); 529a9994687SGerd Hoffmann return -1; 530a9994687SGerd Hoffmann } 531a9994687SGerd Hoffmann 532a9994687SGerd Hoffmann void vfio_display_finalize(VFIOPCIDevice *vdev) 533a9994687SGerd Hoffmann { 53400195ba7SGerd Hoffmann if (!vdev->dpy) { 53500195ba7SGerd Hoffmann return; 53600195ba7SGerd Hoffmann } 53700195ba7SGerd Hoffmann 53800195ba7SGerd Hoffmann graphic_console_close(vdev->dpy->con); 5398b818e05SGerd Hoffmann vfio_display_dmabuf_exit(vdev->dpy); 54000195ba7SGerd Hoffmann vfio_display_region_exit(vdev->dpy); 54108479114SGerd Hoffmann vfio_display_edid_exit(vdev->dpy); 54200195ba7SGerd Hoffmann g_free(vdev->dpy); 543a9994687SGerd Hoffmann } 544