1a9994687SGerd Hoffmann /* 2a9994687SGerd Hoffmann * display support for mdev based vgpu devices 3a9994687SGerd Hoffmann * 4a9994687SGerd Hoffmann * Copyright Red Hat, Inc. 2017 5a9994687SGerd Hoffmann * 6a9994687SGerd Hoffmann * Authors: 7a9994687SGerd Hoffmann * Gerd Hoffmann 8a9994687SGerd Hoffmann * 9a9994687SGerd Hoffmann * This work is licensed under the terms of the GNU GPL, version 2. See 10a9994687SGerd Hoffmann * the COPYING file in the top-level directory. 11a9994687SGerd Hoffmann */ 12a9994687SGerd Hoffmann 13a9994687SGerd Hoffmann #include "qemu/osdep.h" 14a9994687SGerd Hoffmann #include <linux/vfio.h> 15a9994687SGerd Hoffmann #include <sys/ioctl.h> 16a9994687SGerd Hoffmann 17a9994687SGerd Hoffmann #include "sysemu/sysemu.h" 18*08479114SGerd Hoffmann #include "hw/display/edid.h" 19a9994687SGerd Hoffmann #include "ui/console.h" 20a9994687SGerd Hoffmann #include "qapi/error.h" 21a9994687SGerd Hoffmann #include "pci.h" 22*08479114SGerd 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 29*08479114SGerd Hoffmann #define pread_field(_fd, _reg, _ptr, _fld) \ 30*08479114SGerd Hoffmann (sizeof(_ptr->_fld) != \ 31*08479114SGerd Hoffmann pread(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \ 32*08479114SGerd Hoffmann _reg->offset + offsetof(typeof(*_ptr), _fld))) 33*08479114SGerd Hoffmann 34*08479114SGerd Hoffmann #define pwrite_field(_fd, _reg, _ptr, _fld) \ 35*08479114SGerd Hoffmann (sizeof(_ptr->_fld) != \ 36*08479114SGerd Hoffmann pwrite(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \ 37*08479114SGerd Hoffmann _reg->offset + offsetof(typeof(*_ptr), _fld))) 38*08479114SGerd Hoffmann 39*08479114SGerd Hoffmann 40*08479114SGerd Hoffmann static void vfio_display_edid_update(VFIOPCIDevice *vdev, bool enabled, 41*08479114SGerd Hoffmann int prefx, int prefy) 42*08479114SGerd Hoffmann { 43*08479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 44*08479114SGerd Hoffmann int fd = vdev->vbasedev.fd; 45*08479114SGerd Hoffmann qemu_edid_info edid = { 46*08479114SGerd Hoffmann .maxx = dpy->edid_regs->max_xres, 47*08479114SGerd Hoffmann .maxy = dpy->edid_regs->max_yres, 48*08479114SGerd Hoffmann .prefx = prefx, 49*08479114SGerd Hoffmann .prefy = prefy, 50*08479114SGerd Hoffmann }; 51*08479114SGerd Hoffmann 52*08479114SGerd Hoffmann dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_DOWN; 53*08479114SGerd Hoffmann if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) { 54*08479114SGerd Hoffmann goto err; 55*08479114SGerd Hoffmann } 56*08479114SGerd Hoffmann trace_vfio_display_edid_link_down(); 57*08479114SGerd Hoffmann 58*08479114SGerd Hoffmann if (!enabled) { 59*08479114SGerd Hoffmann return; 60*08479114SGerd Hoffmann } 61*08479114SGerd Hoffmann 62*08479114SGerd Hoffmann if (edid.maxx && edid.prefx > edid.maxx) { 63*08479114SGerd Hoffmann edid.prefx = edid.maxx; 64*08479114SGerd Hoffmann } 65*08479114SGerd Hoffmann if (edid.maxy && edid.prefy > edid.maxy) { 66*08479114SGerd Hoffmann edid.prefy = edid.maxy; 67*08479114SGerd Hoffmann } 68*08479114SGerd Hoffmann qemu_edid_generate(dpy->edid_blob, 69*08479114SGerd Hoffmann dpy->edid_regs->edid_max_size, 70*08479114SGerd Hoffmann &edid); 71*08479114SGerd Hoffmann trace_vfio_display_edid_update(edid.prefx, edid.prefy); 72*08479114SGerd Hoffmann 73*08479114SGerd Hoffmann dpy->edid_regs->edid_size = qemu_edid_size(dpy->edid_blob); 74*08479114SGerd Hoffmann if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, edid_size)) { 75*08479114SGerd Hoffmann goto err; 76*08479114SGerd Hoffmann } 77*08479114SGerd Hoffmann if (pwrite(fd, dpy->edid_blob, dpy->edid_regs->edid_size, 78*08479114SGerd Hoffmann dpy->edid_info->offset + dpy->edid_regs->edid_offset) 79*08479114SGerd Hoffmann != dpy->edid_regs->edid_size) { 80*08479114SGerd Hoffmann goto err; 81*08479114SGerd Hoffmann } 82*08479114SGerd Hoffmann 83*08479114SGerd Hoffmann dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_UP; 84*08479114SGerd Hoffmann if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) { 85*08479114SGerd Hoffmann goto err; 86*08479114SGerd Hoffmann } 87*08479114SGerd Hoffmann trace_vfio_display_edid_link_up(); 88*08479114SGerd Hoffmann return; 89*08479114SGerd Hoffmann 90*08479114SGerd Hoffmann err: 91*08479114SGerd Hoffmann trace_vfio_display_edid_write_error(); 92*08479114SGerd Hoffmann return; 93*08479114SGerd Hoffmann } 94*08479114SGerd Hoffmann 95*08479114SGerd Hoffmann static int vfio_display_edid_ui_info(void *opaque, uint32_t idx, 96*08479114SGerd Hoffmann QemuUIInfo *info) 97*08479114SGerd Hoffmann { 98*08479114SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 99*08479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 100*08479114SGerd Hoffmann 101*08479114SGerd Hoffmann if (!dpy->edid_regs) { 102*08479114SGerd Hoffmann return 0; 103*08479114SGerd Hoffmann } 104*08479114SGerd Hoffmann 105*08479114SGerd Hoffmann if (info->width && info->height) { 106*08479114SGerd Hoffmann vfio_display_edid_update(vdev, true, info->width, info->height); 107*08479114SGerd Hoffmann } else { 108*08479114SGerd Hoffmann vfio_display_edid_update(vdev, false, 0, 0); 109*08479114SGerd Hoffmann } 110*08479114SGerd Hoffmann 111*08479114SGerd Hoffmann return 0; 112*08479114SGerd Hoffmann } 113*08479114SGerd Hoffmann 114*08479114SGerd Hoffmann static void vfio_display_edid_init(VFIOPCIDevice *vdev) 115*08479114SGerd Hoffmann { 116*08479114SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 117*08479114SGerd Hoffmann int fd = vdev->vbasedev.fd; 118*08479114SGerd Hoffmann int ret; 119*08479114SGerd Hoffmann 120*08479114SGerd Hoffmann ret = vfio_get_dev_region_info(&vdev->vbasedev, 121*08479114SGerd Hoffmann VFIO_REGION_TYPE_GFX, 122*08479114SGerd Hoffmann VFIO_REGION_SUBTYPE_GFX_EDID, 123*08479114SGerd Hoffmann &dpy->edid_info); 124*08479114SGerd Hoffmann if (ret) { 125*08479114SGerd Hoffmann return; 126*08479114SGerd Hoffmann } 127*08479114SGerd Hoffmann 128*08479114SGerd Hoffmann trace_vfio_display_edid_available(); 129*08479114SGerd Hoffmann dpy->edid_regs = g_new0(struct vfio_region_gfx_edid, 1); 130*08479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_offset)) { 131*08479114SGerd Hoffmann goto err; 132*08479114SGerd Hoffmann } 133*08479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_max_size)) { 134*08479114SGerd Hoffmann goto err; 135*08479114SGerd Hoffmann } 136*08479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_xres)) { 137*08479114SGerd Hoffmann goto err; 138*08479114SGerd Hoffmann } 139*08479114SGerd Hoffmann if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_yres)) { 140*08479114SGerd Hoffmann goto err; 141*08479114SGerd Hoffmann } 142*08479114SGerd Hoffmann 143*08479114SGerd Hoffmann dpy->edid_blob = g_malloc0(dpy->edid_regs->edid_max_size); 144*08479114SGerd Hoffmann 145*08479114SGerd Hoffmann vfio_display_edid_update(vdev, true, 0, 0); 146*08479114SGerd Hoffmann return; 147*08479114SGerd Hoffmann 148*08479114SGerd Hoffmann err: 149*08479114SGerd Hoffmann trace_vfio_display_edid_write_error(); 150*08479114SGerd Hoffmann g_free(dpy->edid_regs); 151*08479114SGerd Hoffmann dpy->edid_regs = NULL; 152*08479114SGerd Hoffmann return; 153*08479114SGerd Hoffmann } 154*08479114SGerd Hoffmann 155*08479114SGerd Hoffmann static void vfio_display_edid_exit(VFIODisplay *dpy) 156*08479114SGerd Hoffmann { 157*08479114SGerd Hoffmann if (!dpy->edid_regs) { 158*08479114SGerd Hoffmann return; 159*08479114SGerd Hoffmann } 160*08479114SGerd Hoffmann 161*08479114SGerd Hoffmann g_free(dpy->edid_regs); 162*08479114SGerd Hoffmann g_free(dpy->edid_blob); 163*08479114SGerd Hoffmann } 164*08479114SGerd Hoffmann 1658b818e05SGerd Hoffmann static void vfio_display_update_cursor(VFIODMABuf *dmabuf, 1668b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info *plane) 1678b818e05SGerd Hoffmann { 1688b818e05SGerd Hoffmann if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) { 1698b818e05SGerd Hoffmann dmabuf->pos_x = plane->x_pos; 1708b818e05SGerd Hoffmann dmabuf->pos_y = plane->y_pos; 1718b818e05SGerd Hoffmann dmabuf->pos_updates++; 1728b818e05SGerd Hoffmann } 1738b818e05SGerd Hoffmann if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) { 1748b818e05SGerd Hoffmann dmabuf->hot_x = plane->x_hot; 1758b818e05SGerd Hoffmann dmabuf->hot_y = plane->y_hot; 1768b818e05SGerd Hoffmann dmabuf->hot_updates++; 1778b818e05SGerd Hoffmann } 1788b818e05SGerd Hoffmann } 1798b818e05SGerd Hoffmann 1808b818e05SGerd Hoffmann static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, 1818b818e05SGerd Hoffmann uint32_t plane_type) 1828b818e05SGerd Hoffmann { 1838b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 1848b818e05SGerd Hoffmann struct vfio_device_gfx_plane_info plane; 1858b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 1868b818e05SGerd Hoffmann int fd, ret; 1878b818e05SGerd Hoffmann 1888b818e05SGerd Hoffmann memset(&plane, 0, sizeof(plane)); 1898b818e05SGerd Hoffmann plane.argsz = sizeof(plane); 1908b818e05SGerd Hoffmann plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF; 1918b818e05SGerd Hoffmann plane.drm_plane_type = plane_type; 1928b818e05SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 1938b818e05SGerd Hoffmann if (ret < 0) { 1948b818e05SGerd Hoffmann return NULL; 1958b818e05SGerd Hoffmann } 1968b818e05SGerd Hoffmann if (!plane.drm_format || !plane.size) { 1978b818e05SGerd Hoffmann return NULL; 1988b818e05SGerd Hoffmann } 1998b818e05SGerd Hoffmann 2008b818e05SGerd Hoffmann QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) { 2018b818e05SGerd Hoffmann if (dmabuf->dmabuf_id == plane.dmabuf_id) { 2028b818e05SGerd Hoffmann /* found in list, move to head, return it */ 2038b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 2048b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 2058b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 2068b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 2078b818e05SGerd Hoffmann } 2088b818e05SGerd Hoffmann return dmabuf; 2098b818e05SGerd Hoffmann } 2108b818e05SGerd Hoffmann } 2118b818e05SGerd Hoffmann 2128b818e05SGerd Hoffmann fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id); 2138b818e05SGerd Hoffmann if (fd < 0) { 2148b818e05SGerd Hoffmann return NULL; 2158b818e05SGerd Hoffmann } 2168b818e05SGerd Hoffmann 2178b818e05SGerd Hoffmann dmabuf = g_new0(VFIODMABuf, 1); 2188b818e05SGerd Hoffmann dmabuf->dmabuf_id = plane.dmabuf_id; 2198b818e05SGerd Hoffmann dmabuf->buf.width = plane.width; 2208b818e05SGerd Hoffmann dmabuf->buf.height = plane.height; 2218b818e05SGerd Hoffmann dmabuf->buf.stride = plane.stride; 2228b818e05SGerd Hoffmann dmabuf->buf.fourcc = plane.drm_format; 2238b818e05SGerd Hoffmann dmabuf->buf.fd = fd; 2248b818e05SGerd Hoffmann if (plane_type == DRM_PLANE_TYPE_CURSOR) { 2258b818e05SGerd Hoffmann vfio_display_update_cursor(dmabuf, &plane); 2268b818e05SGerd Hoffmann } 2278b818e05SGerd Hoffmann 2288b818e05SGerd Hoffmann QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next); 2298b818e05SGerd Hoffmann return dmabuf; 2308b818e05SGerd Hoffmann } 2318b818e05SGerd Hoffmann 2328b818e05SGerd Hoffmann static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf) 2338b818e05SGerd Hoffmann { 2348b818e05SGerd Hoffmann QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next); 2358b818e05SGerd Hoffmann dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf); 2368b818e05SGerd Hoffmann close(dmabuf->buf.fd); 2378b818e05SGerd Hoffmann g_free(dmabuf); 2388b818e05SGerd Hoffmann } 2398b818e05SGerd Hoffmann 2408b818e05SGerd Hoffmann static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev) 2418b818e05SGerd Hoffmann { 2428b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2438b818e05SGerd Hoffmann VFIODMABuf *dmabuf, *tmp; 2448b818e05SGerd Hoffmann uint32_t keep = 5; 2458b818e05SGerd Hoffmann 2468b818e05SGerd Hoffmann QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) { 2478b818e05SGerd Hoffmann if (keep > 0) { 2488b818e05SGerd Hoffmann keep--; 2498b818e05SGerd Hoffmann continue; 2508b818e05SGerd Hoffmann } 2518b818e05SGerd Hoffmann assert(dmabuf != dpy->dmabuf.primary); 2528b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 2538b818e05SGerd Hoffmann } 2548b818e05SGerd Hoffmann } 2558b818e05SGerd Hoffmann 2568b818e05SGerd Hoffmann static void vfio_display_dmabuf_update(void *opaque) 2578b818e05SGerd Hoffmann { 2588b818e05SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 2598b818e05SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 2608b818e05SGerd Hoffmann VFIODMABuf *primary, *cursor; 2618b818e05SGerd Hoffmann bool free_bufs = false, new_cursor = false;; 2628b818e05SGerd Hoffmann 2638b818e05SGerd Hoffmann primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); 2648b818e05SGerd Hoffmann if (primary == NULL) { 265b290659fSGerd Hoffmann if (dpy->ramfb) { 266b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 267b290659fSGerd Hoffmann } 2688b818e05SGerd Hoffmann return; 2698b818e05SGerd Hoffmann } 2708b818e05SGerd Hoffmann 2718b818e05SGerd Hoffmann if (dpy->dmabuf.primary != primary) { 2728b818e05SGerd Hoffmann dpy->dmabuf.primary = primary; 2738b818e05SGerd Hoffmann qemu_console_resize(dpy->con, 2748b818e05SGerd Hoffmann primary->buf.width, primary->buf.height); 2758b818e05SGerd Hoffmann dpy_gl_scanout_dmabuf(dpy->con, &primary->buf); 2768b818e05SGerd Hoffmann free_bufs = true; 2778b818e05SGerd Hoffmann } 2788b818e05SGerd Hoffmann 2798b818e05SGerd Hoffmann cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR); 2808b818e05SGerd Hoffmann if (dpy->dmabuf.cursor != cursor) { 2818b818e05SGerd Hoffmann dpy->dmabuf.cursor = cursor; 2828b818e05SGerd Hoffmann new_cursor = true; 2838b818e05SGerd Hoffmann free_bufs = true; 2848b818e05SGerd Hoffmann } 2858b818e05SGerd Hoffmann 2868b818e05SGerd Hoffmann if (cursor && (new_cursor || cursor->hot_updates)) { 2878b818e05SGerd Hoffmann bool have_hot = (cursor->hot_x != 0xffffffff && 2888b818e05SGerd Hoffmann cursor->hot_y != 0xffffffff); 2898b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot, 2908b818e05SGerd Hoffmann cursor->hot_x, cursor->hot_y); 2918b818e05SGerd Hoffmann cursor->hot_updates = 0; 2928b818e05SGerd Hoffmann } else if (!cursor && new_cursor) { 2938b818e05SGerd Hoffmann dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0); 2948b818e05SGerd Hoffmann } 2958b818e05SGerd Hoffmann 2968b818e05SGerd Hoffmann if (cursor && cursor->pos_updates) { 2978b818e05SGerd Hoffmann dpy_gl_cursor_position(dpy->con, 2988b818e05SGerd Hoffmann cursor->pos_x, 2998b818e05SGerd Hoffmann cursor->pos_y); 3008b818e05SGerd Hoffmann cursor->pos_updates = 0; 3018b818e05SGerd Hoffmann } 3028b818e05SGerd Hoffmann 3038b818e05SGerd Hoffmann dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height); 3048b818e05SGerd Hoffmann 3058b818e05SGerd Hoffmann if (free_bufs) { 3068b818e05SGerd Hoffmann vfio_display_free_dmabufs(vdev); 3078b818e05SGerd Hoffmann } 3088b818e05SGerd Hoffmann } 3098b818e05SGerd Hoffmann 3108b818e05SGerd Hoffmann static const GraphicHwOps vfio_display_dmabuf_ops = { 3118b818e05SGerd Hoffmann .gfx_update = vfio_display_dmabuf_update, 312*08479114SGerd Hoffmann .ui_info = vfio_display_edid_ui_info, 3138b818e05SGerd Hoffmann }; 3148b818e05SGerd Hoffmann 3158b818e05SGerd Hoffmann static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) 3168b818e05SGerd Hoffmann { 3178b818e05SGerd Hoffmann if (!display_opengl) { 3188b818e05SGerd Hoffmann error_setg(errp, "vfio-display-dmabuf: opengl not available"); 3198b818e05SGerd Hoffmann return -1; 3208b818e05SGerd Hoffmann } 3218b818e05SGerd Hoffmann 3228b818e05SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 3238b818e05SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 3248b818e05SGerd Hoffmann &vfio_display_dmabuf_ops, 3258b818e05SGerd Hoffmann vdev); 326b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 327b290659fSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 328b290659fSGerd Hoffmann } 329*08479114SGerd Hoffmann vfio_display_edid_init(vdev); 3308b818e05SGerd Hoffmann return 0; 3318b818e05SGerd Hoffmann } 3328b818e05SGerd Hoffmann 3338b818e05SGerd Hoffmann static void vfio_display_dmabuf_exit(VFIODisplay *dpy) 3348b818e05SGerd Hoffmann { 3358b818e05SGerd Hoffmann VFIODMABuf *dmabuf; 3368b818e05SGerd Hoffmann 3378b818e05SGerd Hoffmann if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) { 3388b818e05SGerd Hoffmann return; 3398b818e05SGerd Hoffmann } 3408b818e05SGerd Hoffmann 3418b818e05SGerd Hoffmann while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) { 3428b818e05SGerd Hoffmann vfio_display_free_one_dmabuf(dpy, dmabuf); 3438b818e05SGerd Hoffmann } 3448b818e05SGerd Hoffmann } 3458b818e05SGerd Hoffmann 34600195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 3478983e3e3STina Zhang void vfio_display_reset(VFIOPCIDevice *vdev) 3488983e3e3STina Zhang { 3498983e3e3STina Zhang if (!vdev || !vdev->dpy || !vdev->dpy->con || 3508983e3e3STina Zhang !vdev->dpy->dmabuf.primary) { 3518983e3e3STina Zhang return; 3528983e3e3STina Zhang } 3538983e3e3STina Zhang 3548983e3e3STina Zhang dpy_gl_scanout_disable(vdev->dpy->con); 3558983e3e3STina Zhang vfio_display_dmabuf_exit(vdev->dpy); 3568983e3e3STina Zhang dpy_gfx_update_full(vdev->dpy->con); 3578983e3e3STina Zhang } 35800195ba7SGerd Hoffmann 35900195ba7SGerd Hoffmann static void vfio_display_region_update(void *opaque) 36000195ba7SGerd Hoffmann { 36100195ba7SGerd Hoffmann VFIOPCIDevice *vdev = opaque; 36200195ba7SGerd Hoffmann VFIODisplay *dpy = vdev->dpy; 36300195ba7SGerd Hoffmann struct vfio_device_gfx_plane_info plane = { 36400195ba7SGerd Hoffmann .argsz = sizeof(plane), 36500195ba7SGerd Hoffmann .flags = VFIO_GFX_PLANE_TYPE_REGION 36600195ba7SGerd Hoffmann }; 36700195ba7SGerd Hoffmann pixman_format_code_t format; 36800195ba7SGerd Hoffmann int ret; 36900195ba7SGerd Hoffmann 37000195ba7SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane); 37100195ba7SGerd Hoffmann if (ret < 0) { 37200195ba7SGerd Hoffmann error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s", 37300195ba7SGerd Hoffmann strerror(errno)); 37400195ba7SGerd Hoffmann return; 37500195ba7SGerd Hoffmann } 37600195ba7SGerd Hoffmann if (!plane.drm_format || !plane.size) { 377b290659fSGerd Hoffmann if (dpy->ramfb) { 378b290659fSGerd Hoffmann ramfb_display_update(dpy->con, dpy->ramfb); 379b290659fSGerd Hoffmann } 38000195ba7SGerd Hoffmann return; 38100195ba7SGerd Hoffmann } 38200195ba7SGerd Hoffmann format = qemu_drm_format_to_pixman(plane.drm_format); 38300195ba7SGerd Hoffmann if (!format) { 38400195ba7SGerd Hoffmann return; 38500195ba7SGerd Hoffmann } 38600195ba7SGerd Hoffmann 38700195ba7SGerd Hoffmann if (dpy->region.buffer.size && 38800195ba7SGerd Hoffmann dpy->region.buffer.nr != plane.region_index) { 38900195ba7SGerd Hoffmann /* region changed */ 39000195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 39100195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 39200195ba7SGerd Hoffmann dpy->region.surface = NULL; 39300195ba7SGerd Hoffmann } 39400195ba7SGerd Hoffmann 39500195ba7SGerd Hoffmann if (dpy->region.surface && 39600195ba7SGerd Hoffmann (surface_width(dpy->region.surface) != plane.width || 39700195ba7SGerd Hoffmann surface_height(dpy->region.surface) != plane.height || 39800195ba7SGerd Hoffmann surface_format(dpy->region.surface) != format)) { 39900195ba7SGerd Hoffmann /* size changed */ 40000195ba7SGerd Hoffmann dpy->region.surface = NULL; 40100195ba7SGerd Hoffmann } 40200195ba7SGerd Hoffmann 40300195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 40400195ba7SGerd Hoffmann /* mmap region */ 40500195ba7SGerd Hoffmann ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev, 40600195ba7SGerd Hoffmann &dpy->region.buffer, 40700195ba7SGerd Hoffmann plane.region_index, 40800195ba7SGerd Hoffmann "display"); 40900195ba7SGerd Hoffmann if (ret != 0) { 41000195ba7SGerd Hoffmann error_report("%s: vfio_region_setup(%d): %s", 41100195ba7SGerd Hoffmann __func__, plane.region_index, strerror(-ret)); 41200195ba7SGerd Hoffmann goto err; 41300195ba7SGerd Hoffmann } 41400195ba7SGerd Hoffmann ret = vfio_region_mmap(&dpy->region.buffer); 41500195ba7SGerd Hoffmann if (ret != 0) { 41600195ba7SGerd Hoffmann error_report("%s: vfio_region_mmap(%d): %s", __func__, 41700195ba7SGerd Hoffmann plane.region_index, strerror(-ret)); 41800195ba7SGerd Hoffmann goto err; 41900195ba7SGerd Hoffmann } 42000195ba7SGerd Hoffmann assert(dpy->region.buffer.mmaps[0].mmap != NULL); 42100195ba7SGerd Hoffmann } 42200195ba7SGerd Hoffmann 42300195ba7SGerd Hoffmann if (dpy->region.surface == NULL) { 42400195ba7SGerd Hoffmann /* create surface */ 42500195ba7SGerd Hoffmann dpy->region.surface = qemu_create_displaysurface_from 42600195ba7SGerd Hoffmann (plane.width, plane.height, format, 42700195ba7SGerd Hoffmann plane.stride, dpy->region.buffer.mmaps[0].mmap); 42800195ba7SGerd Hoffmann dpy_gfx_replace_surface(dpy->con, dpy->region.surface); 42900195ba7SGerd Hoffmann } 43000195ba7SGerd Hoffmann 43100195ba7SGerd Hoffmann /* full screen update */ 43200195ba7SGerd Hoffmann dpy_gfx_update(dpy->con, 0, 0, 43300195ba7SGerd Hoffmann surface_width(dpy->region.surface), 43400195ba7SGerd Hoffmann surface_height(dpy->region.surface)); 43500195ba7SGerd Hoffmann return; 43600195ba7SGerd Hoffmann 43700195ba7SGerd Hoffmann err: 43800195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 43900195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 44000195ba7SGerd Hoffmann } 44100195ba7SGerd Hoffmann 44200195ba7SGerd Hoffmann static const GraphicHwOps vfio_display_region_ops = { 44300195ba7SGerd Hoffmann .gfx_update = vfio_display_region_update, 44400195ba7SGerd Hoffmann }; 44500195ba7SGerd Hoffmann 44600195ba7SGerd Hoffmann static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) 44700195ba7SGerd Hoffmann { 44800195ba7SGerd Hoffmann vdev->dpy = g_new0(VFIODisplay, 1); 44900195ba7SGerd Hoffmann vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, 45000195ba7SGerd Hoffmann &vfio_display_region_ops, 45100195ba7SGerd Hoffmann vdev); 452b290659fSGerd Hoffmann if (vdev->enable_ramfb) { 453b290659fSGerd Hoffmann vdev->dpy->ramfb = ramfb_setup(errp); 454b290659fSGerd Hoffmann } 45500195ba7SGerd Hoffmann return 0; 45600195ba7SGerd Hoffmann } 45700195ba7SGerd Hoffmann 45800195ba7SGerd Hoffmann static void vfio_display_region_exit(VFIODisplay *dpy) 45900195ba7SGerd Hoffmann { 46000195ba7SGerd Hoffmann if (!dpy->region.buffer.size) { 46100195ba7SGerd Hoffmann return; 46200195ba7SGerd Hoffmann } 46300195ba7SGerd Hoffmann 46400195ba7SGerd Hoffmann vfio_region_exit(&dpy->region.buffer); 46500195ba7SGerd Hoffmann vfio_region_finalize(&dpy->region.buffer); 46600195ba7SGerd Hoffmann } 46700195ba7SGerd Hoffmann 46800195ba7SGerd Hoffmann /* ---------------------------------------------------------------------- */ 46900195ba7SGerd Hoffmann 470a9994687SGerd Hoffmann int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp) 471a9994687SGerd Hoffmann { 472a9994687SGerd Hoffmann struct vfio_device_gfx_plane_info probe; 473a9994687SGerd Hoffmann int ret; 474a9994687SGerd Hoffmann 475a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 476a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 477a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF; 478a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 479a9994687SGerd Hoffmann if (ret == 0) { 4808b818e05SGerd Hoffmann return vfio_display_dmabuf_init(vdev, errp); 481a9994687SGerd Hoffmann } 482a9994687SGerd Hoffmann 483a9994687SGerd Hoffmann memset(&probe, 0, sizeof(probe)); 484a9994687SGerd Hoffmann probe.argsz = sizeof(probe); 485a9994687SGerd Hoffmann probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION; 486a9994687SGerd Hoffmann ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe); 487a9994687SGerd Hoffmann if (ret == 0) { 48800195ba7SGerd Hoffmann return vfio_display_region_init(vdev, errp); 489a9994687SGerd Hoffmann } 490a9994687SGerd Hoffmann 491a9994687SGerd Hoffmann if (vdev->display == ON_OFF_AUTO_AUTO) { 492a9994687SGerd Hoffmann /* not an error in automatic mode */ 493a9994687SGerd Hoffmann return 0; 494a9994687SGerd Hoffmann } 495a9994687SGerd Hoffmann 496a9994687SGerd Hoffmann error_setg(errp, "vfio: device doesn't support any (known) display method"); 497a9994687SGerd Hoffmann return -1; 498a9994687SGerd Hoffmann } 499a9994687SGerd Hoffmann 500a9994687SGerd Hoffmann void vfio_display_finalize(VFIOPCIDevice *vdev) 501a9994687SGerd Hoffmann { 50200195ba7SGerd Hoffmann if (!vdev->dpy) { 50300195ba7SGerd Hoffmann return; 50400195ba7SGerd Hoffmann } 50500195ba7SGerd Hoffmann 50600195ba7SGerd Hoffmann graphic_console_close(vdev->dpy->con); 5078b818e05SGerd Hoffmann vfio_display_dmabuf_exit(vdev->dpy); 50800195ba7SGerd Hoffmann vfio_display_region_exit(vdev->dpy); 509*08479114SGerd Hoffmann vfio_display_edid_exit(vdev->dpy); 51000195ba7SGerd Hoffmann g_free(vdev->dpy); 511a9994687SGerd Hoffmann } 512