150d8e25eSMarc-André Lureau /* 250d8e25eSMarc-André Lureau * Virtio GPU Device 350d8e25eSMarc-André Lureau * 450d8e25eSMarc-André Lureau * Copyright Red Hat, Inc. 2013-2014 550d8e25eSMarc-André Lureau * 650d8e25eSMarc-André Lureau * Authors: 750d8e25eSMarc-André Lureau * Dave Airlie <airlied@redhat.com> 850d8e25eSMarc-André Lureau * Gerd Hoffmann <kraxel@redhat.com> 950d8e25eSMarc-André Lureau * 1050d8e25eSMarc-André Lureau * This work is licensed under the terms of the GNU GPL, version 2 or later. 1150d8e25eSMarc-André Lureau * See the COPYING file in the top-level directory. 1250d8e25eSMarc-André Lureau */ 1350d8e25eSMarc-André Lureau 1450d8e25eSMarc-André Lureau #include "qemu/osdep.h" 1550d8e25eSMarc-André Lureau 1650d8e25eSMarc-André Lureau #include "hw/virtio/virtio-gpu.h" 1750d8e25eSMarc-André Lureau #include "migration/blocker.h" 1850d8e25eSMarc-André Lureau #include "qapi/error.h" 1950d8e25eSMarc-André Lureau #include "qemu/error-report.h" 2050d8e25eSMarc-André Lureau #include "trace.h" 2150d8e25eSMarc-André Lureau 2250d8e25eSMarc-André Lureau void 2350d8e25eSMarc-André Lureau virtio_gpu_base_reset(VirtIOGPUBase *g) 2450d8e25eSMarc-André Lureau { 2550d8e25eSMarc-André Lureau int i; 2650d8e25eSMarc-André Lureau 2750d8e25eSMarc-André Lureau g->enable = 0; 2850d8e25eSMarc-André Lureau 2950d8e25eSMarc-André Lureau for (i = 0; i < g->conf.max_outputs; i++) { 3050d8e25eSMarc-André Lureau g->scanout[i].resource_id = 0; 3150d8e25eSMarc-André Lureau g->scanout[i].width = 0; 3250d8e25eSMarc-André Lureau g->scanout[i].height = 0; 3350d8e25eSMarc-André Lureau g->scanout[i].x = 0; 3450d8e25eSMarc-André Lureau g->scanout[i].y = 0; 3550d8e25eSMarc-André Lureau g->scanout[i].ds = NULL; 3650d8e25eSMarc-André Lureau } 3750d8e25eSMarc-André Lureau } 3850d8e25eSMarc-André Lureau 3950d8e25eSMarc-André Lureau void 4050d8e25eSMarc-André Lureau virtio_gpu_base_fill_display_info(VirtIOGPUBase *g, 4150d8e25eSMarc-André Lureau struct virtio_gpu_resp_display_info *dpy_info) 4250d8e25eSMarc-André Lureau { 4350d8e25eSMarc-André Lureau int i; 4450d8e25eSMarc-André Lureau 4550d8e25eSMarc-André Lureau for (i = 0; i < g->conf.max_outputs; i++) { 4650d8e25eSMarc-André Lureau if (g->enabled_output_bitmask & (1 << i)) { 4750d8e25eSMarc-André Lureau dpy_info->pmodes[i].enabled = 1; 4850d8e25eSMarc-André Lureau dpy_info->pmodes[i].r.width = cpu_to_le32(g->req_state[i].width); 4950d8e25eSMarc-André Lureau dpy_info->pmodes[i].r.height = cpu_to_le32(g->req_state[i].height); 5050d8e25eSMarc-André Lureau } 5150d8e25eSMarc-André Lureau } 5250d8e25eSMarc-André Lureau } 5350d8e25eSMarc-André Lureau 5450d8e25eSMarc-André Lureau static void virtio_gpu_invalidate_display(void *opaque) 5550d8e25eSMarc-André Lureau { 5650d8e25eSMarc-André Lureau } 5750d8e25eSMarc-André Lureau 5850d8e25eSMarc-André Lureau static void virtio_gpu_update_display(void *opaque) 5950d8e25eSMarc-André Lureau { 6050d8e25eSMarc-André Lureau } 6150d8e25eSMarc-André Lureau 6250d8e25eSMarc-André Lureau static void virtio_gpu_text_update(void *opaque, console_ch_t *chardata) 6350d8e25eSMarc-André Lureau { 6450d8e25eSMarc-André Lureau } 6550d8e25eSMarc-André Lureau 6650d8e25eSMarc-André Lureau static void virtio_gpu_notify_event(VirtIOGPUBase *g, uint32_t event_type) 6750d8e25eSMarc-André Lureau { 6850d8e25eSMarc-André Lureau g->virtio_config.events_read |= event_type; 6950d8e25eSMarc-André Lureau virtio_notify_config(&g->parent_obj); 7050d8e25eSMarc-André Lureau } 7150d8e25eSMarc-André Lureau 7250d8e25eSMarc-André Lureau static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) 7350d8e25eSMarc-André Lureau { 7450d8e25eSMarc-André Lureau VirtIOGPUBase *g = opaque; 7550d8e25eSMarc-André Lureau 7650d8e25eSMarc-André Lureau if (idx >= g->conf.max_outputs) { 7750d8e25eSMarc-André Lureau return -1; 7850d8e25eSMarc-André Lureau } 7950d8e25eSMarc-André Lureau 8050d8e25eSMarc-André Lureau g->req_state[idx].x = info->xoff; 8150d8e25eSMarc-André Lureau g->req_state[idx].y = info->yoff; 8250d8e25eSMarc-André Lureau g->req_state[idx].width = info->width; 8350d8e25eSMarc-André Lureau g->req_state[idx].height = info->height; 844bf47f36SMarc-André Lureau g->req_state[idx].width_mm = info->width_mm; 854bf47f36SMarc-André Lureau g->req_state[idx].height_mm = info->height_mm; 8650d8e25eSMarc-André Lureau 8750d8e25eSMarc-André Lureau if (info->width && info->height) { 8850d8e25eSMarc-André Lureau g->enabled_output_bitmask |= (1 << idx); 8950d8e25eSMarc-André Lureau } else { 9050d8e25eSMarc-André Lureau g->enabled_output_bitmask &= ~(1 << idx); 9150d8e25eSMarc-André Lureau } 9250d8e25eSMarc-André Lureau 9350d8e25eSMarc-André Lureau /* send event to guest */ 9450d8e25eSMarc-André Lureau virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY); 9550d8e25eSMarc-André Lureau return 0; 9650d8e25eSMarc-André Lureau } 9750d8e25eSMarc-André Lureau 9850d8e25eSMarc-André Lureau static void 993cddb8b9SMarc-André Lureau virtio_gpu_gl_flushed(void *opaque) 10050d8e25eSMarc-André Lureau { 10150d8e25eSMarc-André Lureau VirtIOGPUBase *g = opaque; 10250d8e25eSMarc-André Lureau VirtIOGPUBaseClass *vgc = VIRTIO_GPU_BASE_GET_CLASS(g); 10350d8e25eSMarc-André Lureau 1043cddb8b9SMarc-André Lureau if (vgc->gl_flushed) { 1053cddb8b9SMarc-André Lureau vgc->gl_flushed(g); 1063cddb8b9SMarc-André Lureau } 1073cddb8b9SMarc-André Lureau } 1083cddb8b9SMarc-André Lureau 1093cddb8b9SMarc-André Lureau static void 1103cddb8b9SMarc-André Lureau virtio_gpu_gl_block(void *opaque, bool block) 1113cddb8b9SMarc-André Lureau { 1123cddb8b9SMarc-André Lureau VirtIOGPUBase *g = opaque; 1133cddb8b9SMarc-André Lureau 11450d8e25eSMarc-André Lureau if (block) { 11550d8e25eSMarc-André Lureau g->renderer_blocked++; 11650d8e25eSMarc-André Lureau } else { 11750d8e25eSMarc-André Lureau g->renderer_blocked--; 11850d8e25eSMarc-André Lureau } 11950d8e25eSMarc-André Lureau assert(g->renderer_blocked >= 0); 12050d8e25eSMarc-André Lureau } 12150d8e25eSMarc-André Lureau 122a7dfbe28SMarc-André Lureau static int 123a7dfbe28SMarc-André Lureau virtio_gpu_get_flags(void *opaque) 124a7dfbe28SMarc-André Lureau { 125a7dfbe28SMarc-André Lureau VirtIOGPUBase *g = opaque; 126a7dfbe28SMarc-André Lureau int flags = GRAPHIC_FLAGS_NONE; 127a7dfbe28SMarc-André Lureau 128a7dfbe28SMarc-André Lureau if (virtio_gpu_virgl_enabled(g->conf)) { 129a7dfbe28SMarc-André Lureau flags |= GRAPHIC_FLAGS_GL; 130a7dfbe28SMarc-André Lureau } 131a7dfbe28SMarc-André Lureau 132a7dfbe28SMarc-André Lureau if (virtio_gpu_dmabuf_enabled(g->conf)) { 133a7dfbe28SMarc-André Lureau flags |= GRAPHIC_FLAGS_DMABUF; 134a7dfbe28SMarc-André Lureau } 135a7dfbe28SMarc-André Lureau 136a7dfbe28SMarc-André Lureau return flags; 137a7dfbe28SMarc-André Lureau } 138a7dfbe28SMarc-André Lureau 1393b593b3fSGerd Hoffmann static const GraphicHwOps virtio_gpu_ops = { 140a7dfbe28SMarc-André Lureau .get_flags = virtio_gpu_get_flags, 14150d8e25eSMarc-André Lureau .invalidate = virtio_gpu_invalidate_display, 14250d8e25eSMarc-André Lureau .gfx_update = virtio_gpu_update_display, 14350d8e25eSMarc-André Lureau .text_update = virtio_gpu_text_update, 14450d8e25eSMarc-André Lureau .ui_info = virtio_gpu_ui_info, 14550d8e25eSMarc-André Lureau .gl_block = virtio_gpu_gl_block, 1463cddb8b9SMarc-André Lureau .gl_flushed = virtio_gpu_gl_flushed, 14750d8e25eSMarc-André Lureau }; 14850d8e25eSMarc-André Lureau 14950d8e25eSMarc-André Lureau bool 15050d8e25eSMarc-André Lureau virtio_gpu_base_device_realize(DeviceState *qdev, 15150d8e25eSMarc-André Lureau VirtIOHandleOutput ctrl_cb, 15250d8e25eSMarc-André Lureau VirtIOHandleOutput cursor_cb, 15350d8e25eSMarc-André Lureau Error **errp) 15450d8e25eSMarc-André Lureau { 15550d8e25eSMarc-André Lureau VirtIODevice *vdev = VIRTIO_DEVICE(qdev); 15650d8e25eSMarc-André Lureau VirtIOGPUBase *g = VIRTIO_GPU_BASE(qdev); 15750d8e25eSMarc-André Lureau int i; 15850d8e25eSMarc-André Lureau 15950d8e25eSMarc-André Lureau if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) { 16050d8e25eSMarc-André Lureau error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS); 16150d8e25eSMarc-André Lureau return false; 16250d8e25eSMarc-André Lureau } 16350d8e25eSMarc-André Lureau 16450d8e25eSMarc-André Lureau if (virtio_gpu_virgl_enabled(g->conf)) { 16550d8e25eSMarc-André Lureau error_setg(&g->migration_blocker, "virgl is not yet migratable"); 166386f6c07SMarkus Armbruster if (migrate_add_blocker(g->migration_blocker, errp) < 0) { 16750d8e25eSMarc-André Lureau error_free(g->migration_blocker); 16850d8e25eSMarc-André Lureau return false; 16950d8e25eSMarc-André Lureau } 17050d8e25eSMarc-André Lureau } 17150d8e25eSMarc-André Lureau 17250d8e25eSMarc-André Lureau g->virtio_config.num_scanouts = cpu_to_le32(g->conf.max_outputs); 17350d8e25eSMarc-André Lureau virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, 17450d8e25eSMarc-André Lureau sizeof(struct virtio_gpu_config)); 17550d8e25eSMarc-André Lureau 17650d8e25eSMarc-André Lureau if (virtio_gpu_virgl_enabled(g->conf)) { 17750d8e25eSMarc-André Lureau /* use larger control queue in 3d mode */ 17850d8e25eSMarc-André Lureau virtio_add_queue(vdev, 256, ctrl_cb); 17950d8e25eSMarc-André Lureau virtio_add_queue(vdev, 16, cursor_cb); 18050d8e25eSMarc-André Lureau } else { 18150d8e25eSMarc-André Lureau virtio_add_queue(vdev, 64, ctrl_cb); 18250d8e25eSMarc-André Lureau virtio_add_queue(vdev, 16, cursor_cb); 18350d8e25eSMarc-André Lureau } 18450d8e25eSMarc-André Lureau 18550d8e25eSMarc-André Lureau g->enabled_output_bitmask = 1; 18650d8e25eSMarc-André Lureau 18750d8e25eSMarc-André Lureau g->req_state[0].width = g->conf.xres; 18850d8e25eSMarc-André Lureau g->req_state[0].height = g->conf.yres; 18950d8e25eSMarc-André Lureau 1903b593b3fSGerd Hoffmann g->hw_ops = &virtio_gpu_ops; 19150d8e25eSMarc-André Lureau for (i = 0; i < g->conf.max_outputs; i++) { 19250d8e25eSMarc-André Lureau g->scanout[i].con = 19350d8e25eSMarc-André Lureau graphic_console_init(DEVICE(g), i, &virtio_gpu_ops, g); 19450d8e25eSMarc-André Lureau } 19550d8e25eSMarc-André Lureau 19650d8e25eSMarc-André Lureau return true; 19750d8e25eSMarc-André Lureau } 19850d8e25eSMarc-André Lureau 19950d8e25eSMarc-André Lureau static uint64_t 20050d8e25eSMarc-André Lureau virtio_gpu_base_get_features(VirtIODevice *vdev, uint64_t features, 20150d8e25eSMarc-André Lureau Error **errp) 20250d8e25eSMarc-André Lureau { 20350d8e25eSMarc-André Lureau VirtIOGPUBase *g = VIRTIO_GPU_BASE(vdev); 20450d8e25eSMarc-André Lureau 20550d8e25eSMarc-André Lureau if (virtio_gpu_virgl_enabled(g->conf)) { 20650d8e25eSMarc-André Lureau features |= (1 << VIRTIO_GPU_F_VIRGL); 20750d8e25eSMarc-André Lureau } 20850d8e25eSMarc-André Lureau if (virtio_gpu_edid_enabled(g->conf)) { 20950d8e25eSMarc-André Lureau features |= (1 << VIRTIO_GPU_F_EDID); 21050d8e25eSMarc-André Lureau } 211*cce386e1SVivek Kasireddy if (virtio_gpu_blob_enabled(g->conf)) { 212*cce386e1SVivek Kasireddy features |= (1 << VIRTIO_GPU_F_RESOURCE_BLOB); 213*cce386e1SVivek Kasireddy } 21450d8e25eSMarc-André Lureau 21550d8e25eSMarc-André Lureau return features; 21650d8e25eSMarc-André Lureau } 21750d8e25eSMarc-André Lureau 21850d8e25eSMarc-André Lureau static void 21950d8e25eSMarc-André Lureau virtio_gpu_base_set_features(VirtIODevice *vdev, uint64_t features) 22050d8e25eSMarc-André Lureau { 22150d8e25eSMarc-André Lureau static const uint32_t virgl = (1 << VIRTIO_GPU_F_VIRGL); 22250d8e25eSMarc-André Lureau 22349afbca3SGerd Hoffmann trace_virtio_gpu_features(((features & virgl) == virgl)); 22450d8e25eSMarc-André Lureau } 22550d8e25eSMarc-André Lureau 22650d8e25eSMarc-André Lureau static void 227b69c3c21SMarkus Armbruster virtio_gpu_base_device_unrealize(DeviceState *qdev) 22850d8e25eSMarc-André Lureau { 22950d8e25eSMarc-André Lureau VirtIOGPUBase *g = VIRTIO_GPU_BASE(qdev); 23050d8e25eSMarc-André Lureau 23150d8e25eSMarc-André Lureau if (g->migration_blocker) { 23250d8e25eSMarc-André Lureau migrate_del_blocker(g->migration_blocker); 23350d8e25eSMarc-André Lureau error_free(g->migration_blocker); 23450d8e25eSMarc-André Lureau } 23550d8e25eSMarc-André Lureau } 23650d8e25eSMarc-André Lureau 23750d8e25eSMarc-André Lureau static void 23850d8e25eSMarc-André Lureau virtio_gpu_base_class_init(ObjectClass *klass, void *data) 23950d8e25eSMarc-André Lureau { 24050d8e25eSMarc-André Lureau DeviceClass *dc = DEVICE_CLASS(klass); 24150d8e25eSMarc-André Lureau VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 24250d8e25eSMarc-André Lureau 24350d8e25eSMarc-André Lureau vdc->unrealize = virtio_gpu_base_device_unrealize; 24450d8e25eSMarc-André Lureau vdc->get_features = virtio_gpu_base_get_features; 24550d8e25eSMarc-André Lureau vdc->set_features = virtio_gpu_base_set_features; 24650d8e25eSMarc-André Lureau 24750d8e25eSMarc-André Lureau set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); 24850d8e25eSMarc-André Lureau dc->hotpluggable = false; 24950d8e25eSMarc-André Lureau } 25050d8e25eSMarc-André Lureau 25150d8e25eSMarc-André Lureau static const TypeInfo virtio_gpu_base_info = { 25250d8e25eSMarc-André Lureau .name = TYPE_VIRTIO_GPU_BASE, 25350d8e25eSMarc-André Lureau .parent = TYPE_VIRTIO_DEVICE, 25450d8e25eSMarc-André Lureau .instance_size = sizeof(VirtIOGPUBase), 25550d8e25eSMarc-André Lureau .class_size = sizeof(VirtIOGPUBaseClass), 25650d8e25eSMarc-André Lureau .class_init = virtio_gpu_base_class_init, 25750d8e25eSMarc-André Lureau .abstract = true 25850d8e25eSMarc-André Lureau }; 25950d8e25eSMarc-André Lureau 26050d8e25eSMarc-André Lureau static void 26150d8e25eSMarc-André Lureau virtio_register_types(void) 26250d8e25eSMarc-André Lureau { 26350d8e25eSMarc-André Lureau type_register_static(&virtio_gpu_base_info); 26450d8e25eSMarc-André Lureau } 26550d8e25eSMarc-André Lureau 26650d8e25eSMarc-André Lureau type_init(virtio_register_types) 26750d8e25eSMarc-André Lureau 26850d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctrl_hdr) != 24); 26950d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_update_cursor) != 56); 27050d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_unref) != 32); 27150d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_2d) != 40); 27250d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_set_scanout) != 48); 27350d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_flush) != 48); 27450d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_to_host_2d) != 56); 27550d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) != 16); 27650d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) != 32); 27750d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) != 32); 27850d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) != 408); 27950d8e25eSMarc-André Lureau 28050d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d) != 72); 28150d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d) != 72); 28250d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create) != 96); 28350d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy) != 24); 28450d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource) != 32); 28550d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit) != 32); 28650d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info) != 32); 28750d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info) != 40); 28850d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset) != 32); 28950d8e25eSMarc-André Lureau QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset) != 24); 290