2 * display support for mdev based vgpu devices
4 * Copyright Red Hat, Inc. 2017
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
13 #include "qemu/osdep.h"
14 #include <linux/vfio.h>
15 #include <sys/ioctl.h>
17 #include "sysemu/sysemu.h"
18 #include "ui/console.h"
19 #include "qapi/error.h"
22 #ifndef DRM_PLANE_TYPE_PRIMARY
23 # define DRM_PLANE_TYPE_PRIMARY 1
24 # define DRM_PLANE_TYPE_CURSOR 2
27 static void vfio_display_update_cursor(VFIODMABuf
*dmabuf
,
28 struct vfio_device_gfx_plane_info
*plane
)
30 if (dmabuf
->pos_x
!= plane
->x_pos
|| dmabuf
->pos_y
!= plane
->y_pos
) {
31 dmabuf
->pos_x
= plane
->x_pos
;
32 dmabuf
->pos_y
= plane
->y_pos
;
33 dmabuf
->pos_updates
++;
35 if (dmabuf
->hot_x
!= plane
->x_hot
|| dmabuf
->hot_y
!= plane
->y_hot
) {
36 dmabuf
->hot_x
= plane
->x_hot
;
37 dmabuf
->hot_y
= plane
->y_hot
;
38 dmabuf
->hot_updates
++;
42 static VFIODMABuf
*vfio_display_get_dmabuf(VFIOPCIDevice
*vdev
,
45 VFIODisplay
*dpy
= vdev
->dpy
;
46 struct vfio_device_gfx_plane_info plane
;
50 memset(&plane
, 0, sizeof(plane
));
51 plane
.argsz
= sizeof(plane
);
52 plane
.flags
= VFIO_GFX_PLANE_TYPE_DMABUF
;
53 plane
.drm_plane_type
= plane_type
;
54 ret
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_QUERY_GFX_PLANE
, &plane
);
58 if (!plane
.drm_format
|| !plane
.size
) {
62 QTAILQ_FOREACH(dmabuf
, &dpy
->dmabuf
.bufs
, next
) {
63 if (dmabuf
->dmabuf_id
== plane
.dmabuf_id
) {
64 /* found in list, move to head, return it */
65 QTAILQ_REMOVE(&dpy
->dmabuf
.bufs
, dmabuf
, next
);
66 QTAILQ_INSERT_HEAD(&dpy
->dmabuf
.bufs
, dmabuf
, next
);
67 if (plane_type
== DRM_PLANE_TYPE_CURSOR
) {
68 vfio_display_update_cursor(dmabuf
, &plane
);
74 fd
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_GET_GFX_DMABUF
, &plane
.dmabuf_id
);
79 dmabuf
= g_new0(VFIODMABuf
, 1);
80 dmabuf
->dmabuf_id
= plane
.dmabuf_id
;
81 dmabuf
->buf
.width
= plane
.width
;
82 dmabuf
->buf
.height
= plane
.height
;
83 dmabuf
->buf
.stride
= plane
.stride
;
84 dmabuf
->buf
.fourcc
= plane
.drm_format
;
86 if (plane_type
== DRM_PLANE_TYPE_CURSOR
) {
87 vfio_display_update_cursor(dmabuf
, &plane
);
90 QTAILQ_INSERT_HEAD(&dpy
->dmabuf
.bufs
, dmabuf
, next
);
94 static void vfio_display_free_one_dmabuf(VFIODisplay
*dpy
, VFIODMABuf
*dmabuf
)
96 QTAILQ_REMOVE(&dpy
->dmabuf
.bufs
, dmabuf
, next
);
97 dpy_gl_release_dmabuf(dpy
->con
, &dmabuf
->buf
);
98 close(dmabuf
->buf
.fd
);
102 static void vfio_display_free_dmabufs(VFIOPCIDevice
*vdev
)
104 VFIODisplay
*dpy
= vdev
->dpy
;
105 VFIODMABuf
*dmabuf
, *tmp
;
108 QTAILQ_FOREACH_SAFE(dmabuf
, &dpy
->dmabuf
.bufs
, next
, tmp
) {
113 assert(dmabuf
!= dpy
->dmabuf
.primary
);
114 vfio_display_free_one_dmabuf(dpy
, dmabuf
);
118 static void vfio_display_dmabuf_update(void *opaque
)
120 VFIOPCIDevice
*vdev
= opaque
;
121 VFIODisplay
*dpy
= vdev
->dpy
;
122 VFIODMABuf
*primary
, *cursor
;
123 bool free_bufs
= false, new_cursor
= false;;
125 primary
= vfio_display_get_dmabuf(vdev
, DRM_PLANE_TYPE_PRIMARY
);
126 if (primary
== NULL
) {
128 ramfb_display_update(dpy
->con
, dpy
->ramfb
);
133 if (dpy
->dmabuf
.primary
!= primary
) {
134 dpy
->dmabuf
.primary
= primary
;
135 qemu_console_resize(dpy
->con
,
136 primary
->buf
.width
, primary
->buf
.height
);
137 dpy_gl_scanout_dmabuf(dpy
->con
, &primary
->buf
);
141 cursor
= vfio_display_get_dmabuf(vdev
, DRM_PLANE_TYPE_CURSOR
);
142 if (dpy
->dmabuf
.cursor
!= cursor
) {
143 dpy
->dmabuf
.cursor
= cursor
;
148 if (cursor
&& (new_cursor
|| cursor
->hot_updates
)) {
149 bool have_hot
= (cursor
->hot_x
!= 0xffffffff &&
150 cursor
->hot_y
!= 0xffffffff);
151 dpy_gl_cursor_dmabuf(dpy
->con
, &cursor
->buf
, have_hot
,
152 cursor
->hot_x
, cursor
->hot_y
);
153 cursor
->hot_updates
= 0;
154 } else if (!cursor
&& new_cursor
) {
155 dpy_gl_cursor_dmabuf(dpy
->con
, NULL
, false, 0, 0);
158 if (cursor
&& cursor
->pos_updates
) {
159 dpy_gl_cursor_position(dpy
->con
,
162 cursor
->pos_updates
= 0;
165 dpy_gl_update(dpy
->con
, 0, 0, primary
->buf
.width
, primary
->buf
.height
);
168 vfio_display_free_dmabufs(vdev
);
172 static const GraphicHwOps vfio_display_dmabuf_ops
= {
173 .gfx_update
= vfio_display_dmabuf_update
,
176 static int vfio_display_dmabuf_init(VFIOPCIDevice
*vdev
, Error
**errp
)
178 if (!display_opengl
) {
179 error_setg(errp
, "vfio-display-dmabuf: opengl not available");
183 vdev
->dpy
= g_new0(VFIODisplay
, 1);
184 vdev
->dpy
->con
= graphic_console_init(DEVICE(vdev
), 0,
185 &vfio_display_dmabuf_ops
,
187 if (vdev
->enable_ramfb
) {
188 vdev
->dpy
->ramfb
= ramfb_setup(errp
);
193 static void vfio_display_dmabuf_exit(VFIODisplay
*dpy
)
197 if (QTAILQ_EMPTY(&dpy
->dmabuf
.bufs
)) {
201 while ((dmabuf
= QTAILQ_FIRST(&dpy
->dmabuf
.bufs
)) != NULL
) {
202 vfio_display_free_one_dmabuf(dpy
, dmabuf
);
206 /* ---------------------------------------------------------------------- */
207 void vfio_display_reset(VFIOPCIDevice
*vdev
)
209 if (!vdev
|| !vdev
->dpy
|| !vdev
->dpy
->con
||
210 !vdev
->dpy
->dmabuf
.primary
) {
214 dpy_gl_scanout_disable(vdev
->dpy
->con
);
215 vfio_display_dmabuf_exit(vdev
->dpy
);
216 dpy_gfx_update_full(vdev
->dpy
->con
);
219 static void vfio_display_region_update(void *opaque
)
221 VFIOPCIDevice
*vdev
= opaque
;
222 VFIODisplay
*dpy
= vdev
->dpy
;
223 struct vfio_device_gfx_plane_info plane
= {
224 .argsz
= sizeof(plane
),
225 .flags
= VFIO_GFX_PLANE_TYPE_REGION
227 pixman_format_code_t format
;
230 ret
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_QUERY_GFX_PLANE
, &plane
);
232 error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s",
236 if (!plane
.drm_format
|| !plane
.size
) {
238 ramfb_display_update(dpy
->con
, dpy
->ramfb
);
242 format
= qemu_drm_format_to_pixman(plane
.drm_format
);
247 if (dpy
->region
.buffer
.size
&&
248 dpy
->region
.buffer
.nr
!= plane
.region_index
) {
250 vfio_region_exit(&dpy
->region
.buffer
);
251 vfio_region_finalize(&dpy
->region
.buffer
);
252 dpy
->region
.surface
= NULL
;
255 if (dpy
->region
.surface
&&
256 (surface_width(dpy
->region
.surface
) != plane
.width
||
257 surface_height(dpy
->region
.surface
) != plane
.height
||
258 surface_format(dpy
->region
.surface
) != format
)) {
260 dpy
->region
.surface
= NULL
;
263 if (!dpy
->region
.buffer
.size
) {
265 ret
= vfio_region_setup(OBJECT(vdev
), &vdev
->vbasedev
,
270 error_report("%s: vfio_region_setup(%d): %s",
271 __func__
, plane
.region_index
, strerror(-ret
));
274 ret
= vfio_region_mmap(&dpy
->region
.buffer
);
276 error_report("%s: vfio_region_mmap(%d): %s", __func__
,
277 plane
.region_index
, strerror(-ret
));
280 assert(dpy
->region
.buffer
.mmaps
[0].mmap
!= NULL
);
283 if (dpy
->region
.surface
== NULL
) {
285 dpy
->region
.surface
= qemu_create_displaysurface_from
286 (plane
.width
, plane
.height
, format
,
287 plane
.stride
, dpy
->region
.buffer
.mmaps
[0].mmap
);
288 dpy_gfx_replace_surface(dpy
->con
, dpy
->region
.surface
);
291 /* full screen update */
292 dpy_gfx_update(dpy
->con
, 0, 0,
293 surface_width(dpy
->region
.surface
),
294 surface_height(dpy
->region
.surface
));
298 vfio_region_exit(&dpy
->region
.buffer
);
299 vfio_region_finalize(&dpy
->region
.buffer
);
302 static const GraphicHwOps vfio_display_region_ops
= {
303 .gfx_update
= vfio_display_region_update
,
306 static int vfio_display_region_init(VFIOPCIDevice
*vdev
, Error
**errp
)
308 vdev
->dpy
= g_new0(VFIODisplay
, 1);
309 vdev
->dpy
->con
= graphic_console_init(DEVICE(vdev
), 0,
310 &vfio_display_region_ops
,
312 if (vdev
->enable_ramfb
) {
313 vdev
->dpy
->ramfb
= ramfb_setup(errp
);
318 static void vfio_display_region_exit(VFIODisplay
*dpy
)
320 if (!dpy
->region
.buffer
.size
) {
324 vfio_region_exit(&dpy
->region
.buffer
);
325 vfio_region_finalize(&dpy
->region
.buffer
);
328 /* ---------------------------------------------------------------------- */
330 int vfio_display_probe(VFIOPCIDevice
*vdev
, Error
**errp
)
332 struct vfio_device_gfx_plane_info probe
;
335 memset(&probe
, 0, sizeof(probe
));
336 probe
.argsz
= sizeof(probe
);
337 probe
.flags
= VFIO_GFX_PLANE_TYPE_PROBE
| VFIO_GFX_PLANE_TYPE_DMABUF
;
338 ret
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_QUERY_GFX_PLANE
, &probe
);
340 return vfio_display_dmabuf_init(vdev
, errp
);
343 memset(&probe
, 0, sizeof(probe
));
344 probe
.argsz
= sizeof(probe
);
345 probe
.flags
= VFIO_GFX_PLANE_TYPE_PROBE
| VFIO_GFX_PLANE_TYPE_REGION
;
346 ret
= ioctl(vdev
->vbasedev
.fd
, VFIO_DEVICE_QUERY_GFX_PLANE
, &probe
);
348 return vfio_display_region_init(vdev
, errp
);
351 if (vdev
->display
== ON_OFF_AUTO_AUTO
) {
352 /* not an error in automatic mode */
356 error_setg(errp
, "vfio: device doesn't support any (known) display method");
360 void vfio_display_finalize(VFIOPCIDevice
*vdev
)
366 graphic_console_close(vdev
->dpy
->con
);
367 vfio_display_dmabuf_exit(vdev
->dpy
);
368 vfio_display_region_exit(vdev
->dpy
);