vfio/display: add xres + yres properties
[qemu/ar7.git] / hw / vfio / display.c
blob212ad1674ec1259d218729bd6719f571d38ebefe
1 /*
2 * display support for mdev based vgpu devices
4 * Copyright Red Hat, Inc. 2017
6 * Authors:
7 * Gerd Hoffmann
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 "hw/display/edid.h"
19 #include "ui/console.h"
20 #include "qapi/error.h"
21 #include "pci.h"
22 #include "trace.h"
24 #ifndef DRM_PLANE_TYPE_PRIMARY
25 # define DRM_PLANE_TYPE_PRIMARY 1
26 # define DRM_PLANE_TYPE_CURSOR 2
27 #endif
29 #define pread_field(_fd, _reg, _ptr, _fld) \
30 (sizeof(_ptr->_fld) != \
31 pread(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \
32 _reg->offset + offsetof(typeof(*_ptr), _fld)))
34 #define pwrite_field(_fd, _reg, _ptr, _fld) \
35 (sizeof(_ptr->_fld) != \
36 pwrite(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \
37 _reg->offset + offsetof(typeof(*_ptr), _fld)))
40 static void vfio_display_edid_update(VFIOPCIDevice *vdev, bool enabled,
41 int prefx, int prefy)
43 VFIODisplay *dpy = vdev->dpy;
44 int fd = vdev->vbasedev.fd;
45 qemu_edid_info edid = {
46 .maxx = dpy->edid_regs->max_xres,
47 .maxy = dpy->edid_regs->max_yres,
48 .prefx = prefx ?: vdev->display_xres,
49 .prefy = prefy ?: vdev->display_yres,
52 dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_DOWN;
53 if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) {
54 goto err;
56 trace_vfio_display_edid_link_down();
58 if (!enabled) {
59 return;
62 if (edid.maxx && edid.prefx > edid.maxx) {
63 edid.prefx = edid.maxx;
65 if (edid.maxy && edid.prefy > edid.maxy) {
66 edid.prefy = edid.maxy;
68 qemu_edid_generate(dpy->edid_blob,
69 dpy->edid_regs->edid_max_size,
70 &edid);
71 trace_vfio_display_edid_update(edid.prefx, edid.prefy);
73 dpy->edid_regs->edid_size = qemu_edid_size(dpy->edid_blob);
74 if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, edid_size)) {
75 goto err;
77 if (pwrite(fd, dpy->edid_blob, dpy->edid_regs->edid_size,
78 dpy->edid_info->offset + dpy->edid_regs->edid_offset)
79 != dpy->edid_regs->edid_size) {
80 goto err;
83 dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_UP;
84 if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) {
85 goto err;
87 trace_vfio_display_edid_link_up();
88 return;
90 err:
91 trace_vfio_display_edid_write_error();
92 return;
95 static int vfio_display_edid_ui_info(void *opaque, uint32_t idx,
96 QemuUIInfo *info)
98 VFIOPCIDevice *vdev = opaque;
99 VFIODisplay *dpy = vdev->dpy;
101 if (!dpy->edid_regs) {
102 return 0;
105 if (info->width && info->height) {
106 vfio_display_edid_update(vdev, true, info->width, info->height);
107 } else {
108 vfio_display_edid_update(vdev, false, 0, 0);
111 return 0;
114 static void vfio_display_edid_init(VFIOPCIDevice *vdev)
116 VFIODisplay *dpy = vdev->dpy;
117 int fd = vdev->vbasedev.fd;
118 int ret;
120 ret = vfio_get_dev_region_info(&vdev->vbasedev,
121 VFIO_REGION_TYPE_GFX,
122 VFIO_REGION_SUBTYPE_GFX_EDID,
123 &dpy->edid_info);
124 if (ret) {
125 return;
128 trace_vfio_display_edid_available();
129 dpy->edid_regs = g_new0(struct vfio_region_gfx_edid, 1);
130 if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_offset)) {
131 goto err;
133 if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_max_size)) {
134 goto err;
136 if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_xres)) {
137 goto err;
139 if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_yres)) {
140 goto err;
143 dpy->edid_blob = g_malloc0(dpy->edid_regs->edid_max_size);
145 /* if xres + yres properties are unset use the maximum resolution */
146 if (!vdev->display_xres) {
147 vdev->display_xres = dpy->edid_regs->max_xres;
149 if (!vdev->display_yres) {
150 vdev->display_yres = dpy->edid_regs->max_yres;
153 vfio_display_edid_update(vdev, true, 0, 0);
154 return;
156 err:
157 trace_vfio_display_edid_write_error();
158 g_free(dpy->edid_regs);
159 dpy->edid_regs = NULL;
160 return;
163 static void vfio_display_edid_exit(VFIODisplay *dpy)
165 if (!dpy->edid_regs) {
166 return;
169 g_free(dpy->edid_regs);
170 g_free(dpy->edid_blob);
173 static void vfio_display_update_cursor(VFIODMABuf *dmabuf,
174 struct vfio_device_gfx_plane_info *plane)
176 if (dmabuf->pos_x != plane->x_pos || dmabuf->pos_y != plane->y_pos) {
177 dmabuf->pos_x = plane->x_pos;
178 dmabuf->pos_y = plane->y_pos;
179 dmabuf->pos_updates++;
181 if (dmabuf->hot_x != plane->x_hot || dmabuf->hot_y != plane->y_hot) {
182 dmabuf->hot_x = plane->x_hot;
183 dmabuf->hot_y = plane->y_hot;
184 dmabuf->hot_updates++;
188 static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev,
189 uint32_t plane_type)
191 VFIODisplay *dpy = vdev->dpy;
192 struct vfio_device_gfx_plane_info plane;
193 VFIODMABuf *dmabuf;
194 int fd, ret;
196 memset(&plane, 0, sizeof(plane));
197 plane.argsz = sizeof(plane);
198 plane.flags = VFIO_GFX_PLANE_TYPE_DMABUF;
199 plane.drm_plane_type = plane_type;
200 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane);
201 if (ret < 0) {
202 return NULL;
204 if (!plane.drm_format || !plane.size) {
205 return NULL;
208 QTAILQ_FOREACH(dmabuf, &dpy->dmabuf.bufs, next) {
209 if (dmabuf->dmabuf_id == plane.dmabuf_id) {
210 /* found in list, move to head, return it */
211 QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
212 QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next);
213 if (plane_type == DRM_PLANE_TYPE_CURSOR) {
214 vfio_display_update_cursor(dmabuf, &plane);
216 return dmabuf;
220 fd = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_GFX_DMABUF, &plane.dmabuf_id);
221 if (fd < 0) {
222 return NULL;
225 dmabuf = g_new0(VFIODMABuf, 1);
226 dmabuf->dmabuf_id = plane.dmabuf_id;
227 dmabuf->buf.width = plane.width;
228 dmabuf->buf.height = plane.height;
229 dmabuf->buf.stride = plane.stride;
230 dmabuf->buf.fourcc = plane.drm_format;
231 dmabuf->buf.fd = fd;
232 if (plane_type == DRM_PLANE_TYPE_CURSOR) {
233 vfio_display_update_cursor(dmabuf, &plane);
236 QTAILQ_INSERT_HEAD(&dpy->dmabuf.bufs, dmabuf, next);
237 return dmabuf;
240 static void vfio_display_free_one_dmabuf(VFIODisplay *dpy, VFIODMABuf *dmabuf)
242 QTAILQ_REMOVE(&dpy->dmabuf.bufs, dmabuf, next);
243 dpy_gl_release_dmabuf(dpy->con, &dmabuf->buf);
244 close(dmabuf->buf.fd);
245 g_free(dmabuf);
248 static void vfio_display_free_dmabufs(VFIOPCIDevice *vdev)
250 VFIODisplay *dpy = vdev->dpy;
251 VFIODMABuf *dmabuf, *tmp;
252 uint32_t keep = 5;
254 QTAILQ_FOREACH_SAFE(dmabuf, &dpy->dmabuf.bufs, next, tmp) {
255 if (keep > 0) {
256 keep--;
257 continue;
259 assert(dmabuf != dpy->dmabuf.primary);
260 vfio_display_free_one_dmabuf(dpy, dmabuf);
264 static void vfio_display_dmabuf_update(void *opaque)
266 VFIOPCIDevice *vdev = opaque;
267 VFIODisplay *dpy = vdev->dpy;
268 VFIODMABuf *primary, *cursor;
269 bool free_bufs = false, new_cursor = false;;
271 primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY);
272 if (primary == NULL) {
273 if (dpy->ramfb) {
274 ramfb_display_update(dpy->con, dpy->ramfb);
276 return;
279 if (dpy->dmabuf.primary != primary) {
280 dpy->dmabuf.primary = primary;
281 qemu_console_resize(dpy->con,
282 primary->buf.width, primary->buf.height);
283 dpy_gl_scanout_dmabuf(dpy->con, &primary->buf);
284 free_bufs = true;
287 cursor = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_CURSOR);
288 if (dpy->dmabuf.cursor != cursor) {
289 dpy->dmabuf.cursor = cursor;
290 new_cursor = true;
291 free_bufs = true;
294 if (cursor && (new_cursor || cursor->hot_updates)) {
295 bool have_hot = (cursor->hot_x != 0xffffffff &&
296 cursor->hot_y != 0xffffffff);
297 dpy_gl_cursor_dmabuf(dpy->con, &cursor->buf, have_hot,
298 cursor->hot_x, cursor->hot_y);
299 cursor->hot_updates = 0;
300 } else if (!cursor && new_cursor) {
301 dpy_gl_cursor_dmabuf(dpy->con, NULL, false, 0, 0);
304 if (cursor && cursor->pos_updates) {
305 dpy_gl_cursor_position(dpy->con,
306 cursor->pos_x,
307 cursor->pos_y);
308 cursor->pos_updates = 0;
311 dpy_gl_update(dpy->con, 0, 0, primary->buf.width, primary->buf.height);
313 if (free_bufs) {
314 vfio_display_free_dmabufs(vdev);
318 static const GraphicHwOps vfio_display_dmabuf_ops = {
319 .gfx_update = vfio_display_dmabuf_update,
320 .ui_info = vfio_display_edid_ui_info,
323 static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp)
325 if (!display_opengl) {
326 error_setg(errp, "vfio-display-dmabuf: opengl not available");
327 return -1;
330 vdev->dpy = g_new0(VFIODisplay, 1);
331 vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
332 &vfio_display_dmabuf_ops,
333 vdev);
334 if (vdev->enable_ramfb) {
335 vdev->dpy->ramfb = ramfb_setup(errp);
337 vfio_display_edid_init(vdev);
338 return 0;
341 static void vfio_display_dmabuf_exit(VFIODisplay *dpy)
343 VFIODMABuf *dmabuf;
345 if (QTAILQ_EMPTY(&dpy->dmabuf.bufs)) {
346 return;
349 while ((dmabuf = QTAILQ_FIRST(&dpy->dmabuf.bufs)) != NULL) {
350 vfio_display_free_one_dmabuf(dpy, dmabuf);
354 /* ---------------------------------------------------------------------- */
355 void vfio_display_reset(VFIOPCIDevice *vdev)
357 if (!vdev || !vdev->dpy || !vdev->dpy->con ||
358 !vdev->dpy->dmabuf.primary) {
359 return;
362 dpy_gl_scanout_disable(vdev->dpy->con);
363 vfio_display_dmabuf_exit(vdev->dpy);
364 dpy_gfx_update_full(vdev->dpy->con);
367 static void vfio_display_region_update(void *opaque)
369 VFIOPCIDevice *vdev = opaque;
370 VFIODisplay *dpy = vdev->dpy;
371 struct vfio_device_gfx_plane_info plane = {
372 .argsz = sizeof(plane),
373 .flags = VFIO_GFX_PLANE_TYPE_REGION
375 pixman_format_code_t format;
376 int ret;
378 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &plane);
379 if (ret < 0) {
380 error_report("ioctl VFIO_DEVICE_QUERY_GFX_PLANE: %s",
381 strerror(errno));
382 return;
384 if (!plane.drm_format || !plane.size) {
385 if (dpy->ramfb) {
386 ramfb_display_update(dpy->con, dpy->ramfb);
388 return;
390 format = qemu_drm_format_to_pixman(plane.drm_format);
391 if (!format) {
392 return;
395 if (dpy->region.buffer.size &&
396 dpy->region.buffer.nr != plane.region_index) {
397 /* region changed */
398 vfio_region_exit(&dpy->region.buffer);
399 vfio_region_finalize(&dpy->region.buffer);
400 dpy->region.surface = NULL;
403 if (dpy->region.surface &&
404 (surface_width(dpy->region.surface) != plane.width ||
405 surface_height(dpy->region.surface) != plane.height ||
406 surface_format(dpy->region.surface) != format)) {
407 /* size changed */
408 dpy->region.surface = NULL;
411 if (!dpy->region.buffer.size) {
412 /* mmap region */
413 ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev,
414 &dpy->region.buffer,
415 plane.region_index,
416 "display");
417 if (ret != 0) {
418 error_report("%s: vfio_region_setup(%d): %s",
419 __func__, plane.region_index, strerror(-ret));
420 goto err;
422 ret = vfio_region_mmap(&dpy->region.buffer);
423 if (ret != 0) {
424 error_report("%s: vfio_region_mmap(%d): %s", __func__,
425 plane.region_index, strerror(-ret));
426 goto err;
428 assert(dpy->region.buffer.mmaps[0].mmap != NULL);
431 if (dpy->region.surface == NULL) {
432 /* create surface */
433 dpy->region.surface = qemu_create_displaysurface_from
434 (plane.width, plane.height, format,
435 plane.stride, dpy->region.buffer.mmaps[0].mmap);
436 dpy_gfx_replace_surface(dpy->con, dpy->region.surface);
439 /* full screen update */
440 dpy_gfx_update(dpy->con, 0, 0,
441 surface_width(dpy->region.surface),
442 surface_height(dpy->region.surface));
443 return;
445 err:
446 vfio_region_exit(&dpy->region.buffer);
447 vfio_region_finalize(&dpy->region.buffer);
450 static const GraphicHwOps vfio_display_region_ops = {
451 .gfx_update = vfio_display_region_update,
454 static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp)
456 vdev->dpy = g_new0(VFIODisplay, 1);
457 vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0,
458 &vfio_display_region_ops,
459 vdev);
460 if (vdev->enable_ramfb) {
461 vdev->dpy->ramfb = ramfb_setup(errp);
463 return 0;
466 static void vfio_display_region_exit(VFIODisplay *dpy)
468 if (!dpy->region.buffer.size) {
469 return;
472 vfio_region_exit(&dpy->region.buffer);
473 vfio_region_finalize(&dpy->region.buffer);
476 /* ---------------------------------------------------------------------- */
478 int vfio_display_probe(VFIOPCIDevice *vdev, Error **errp)
480 struct vfio_device_gfx_plane_info probe;
481 int ret;
483 memset(&probe, 0, sizeof(probe));
484 probe.argsz = sizeof(probe);
485 probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_DMABUF;
486 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe);
487 if (ret == 0) {
488 return vfio_display_dmabuf_init(vdev, errp);
491 memset(&probe, 0, sizeof(probe));
492 probe.argsz = sizeof(probe);
493 probe.flags = VFIO_GFX_PLANE_TYPE_PROBE | VFIO_GFX_PLANE_TYPE_REGION;
494 ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_QUERY_GFX_PLANE, &probe);
495 if (ret == 0) {
496 return vfio_display_region_init(vdev, errp);
499 if (vdev->display == ON_OFF_AUTO_AUTO) {
500 /* not an error in automatic mode */
501 return 0;
504 error_setg(errp, "vfio: device doesn't support any (known) display method");
505 return -1;
508 void vfio_display_finalize(VFIOPCIDevice *vdev)
510 if (!vdev->dpy) {
511 return;
514 graphic_console_close(vdev->dpy->con);
515 vfio_display_dmabuf_exit(vdev->dpy);
516 vfio_display_region_exit(vdev->dpy);
517 vfio_display_edid_exit(vdev->dpy);
518 g_free(vdev->dpy);