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
vfio_display_edid_link_up(void * opaque)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
vfio_display_edid_update(VFIOPCIDevice * vdev,bool enabled,int prefx,int prefy)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
vfio_display_edid_ui_info(void * opaque,uint32_t idx,QemuUIInfo * info)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
vfio_display_edid_init(VFIOPCIDevice * vdev,Error ** errp)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
vfio_display_edid_exit(VFIODisplay * dpy)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
vfio_display_update_cursor(VFIODMABuf * dmabuf,struct vfio_device_gfx_plane_info * plane)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
vfio_display_get_dmabuf(VFIOPCIDevice * vdev,uint32_t plane_type)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
vfio_display_free_one_dmabuf(VFIODisplay * dpy,VFIODMABuf * dmabuf)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
vfio_display_free_dmabufs(VFIOPCIDevice * vdev)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
vfio_display_dmabuf_update(void * opaque)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
vfio_display_get_flags(void * opaque)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
vfio_display_dmabuf_init(VFIOPCIDevice * vdev,Error ** errp)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
vfio_display_dmabuf_exit(VFIODisplay * dpy)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 /* ---------------------------------------------------------------------- */
vfio_display_reset(VFIOPCIDevice * vdev)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
vfio_display_region_update(void * opaque)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
vfio_display_region_init(VFIOPCIDevice * vdev,Error ** errp)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
vfio_display_region_exit(VFIODisplay * dpy)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
vfio_display_probe(VFIOPCIDevice * vdev,Error ** errp)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
vfio_display_finalize(VFIOPCIDevice * vdev)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
migrate_needed(void * opaque)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