1dc5698e8SDave Airlie /* 2dc5698e8SDave Airlie * Copyright (C) 2015 Red Hat, Inc. 3dc5698e8SDave Airlie * All Rights Reserved. 4dc5698e8SDave Airlie * 5dc5698e8SDave Airlie * Permission is hereby granted, free of charge, to any person obtaining 6dc5698e8SDave Airlie * a copy of this software and associated documentation files (the 7dc5698e8SDave Airlie * "Software"), to deal in the Software without restriction, including 8dc5698e8SDave Airlie * without limitation the rights to use, copy, modify, merge, publish, 9dc5698e8SDave Airlie * distribute, sublicense, and/or sell copies of the Software, and to 10dc5698e8SDave Airlie * permit persons to whom the Software is furnished to do so, subject to 11dc5698e8SDave Airlie * the following conditions: 12dc5698e8SDave Airlie * 13dc5698e8SDave Airlie * The above copyright notice and this permission notice (including the 14dc5698e8SDave Airlie * next paragraph) shall be included in all copies or substantial 15dc5698e8SDave Airlie * portions of the Software. 16dc5698e8SDave Airlie * 17dc5698e8SDave Airlie * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18dc5698e8SDave Airlie * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19dc5698e8SDave Airlie * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20dc5698e8SDave Airlie * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 21dc5698e8SDave Airlie * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22dc5698e8SDave Airlie * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23dc5698e8SDave Airlie * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24dc5698e8SDave Airlie */ 25dc5698e8SDave Airlie 26dc5698e8SDave Airlie #include <linux/virtio.h> 27dc5698e8SDave Airlie #include <linux/virtio_config.h> 28a3d63977SSam Ravnborg 29a3d63977SSam Ravnborg #include <drm/drm_file.h> 30a3d63977SSam Ravnborg 31dc5698e8SDave Airlie #include "virtgpu_drv.h" 32dc5698e8SDave Airlie 33dc5698e8SDave Airlie static void virtio_gpu_config_changed_work_func(struct work_struct *work) 34dc5698e8SDave Airlie { 35dc5698e8SDave Airlie struct virtio_gpu_device *vgdev = 36dc5698e8SDave Airlie container_of(work, struct virtio_gpu_device, 37dc5698e8SDave Airlie config_changed_work); 38dc5698e8SDave Airlie u32 events_read, events_clear = 0; 39dc5698e8SDave Airlie 40dc5698e8SDave Airlie /* read the config space */ 41dc5698e8SDave Airlie virtio_cread(vgdev->vdev, struct virtio_gpu_config, 42dc5698e8SDave Airlie events_read, &events_read); 43dc5698e8SDave Airlie if (events_read & VIRTIO_GPU_EVENT_DISPLAY) { 44b4b01b49SGerd Hoffmann if (vgdev->has_edid) 45b4b01b49SGerd Hoffmann virtio_gpu_cmd_get_edids(vgdev); 46dc5698e8SDave Airlie virtio_gpu_cmd_get_display_info(vgdev); 47dc5698e8SDave Airlie drm_helper_hpd_irq_event(vgdev->ddev); 48dc5698e8SDave Airlie events_clear |= VIRTIO_GPU_EVENT_DISPLAY; 49dc5698e8SDave Airlie } 50dc5698e8SDave Airlie virtio_cwrite(vgdev->vdev, struct virtio_gpu_config, 51dc5698e8SDave Airlie events_clear, &events_clear); 52dc5698e8SDave Airlie } 53dc5698e8SDave Airlie 546a37c49aSMatthew Wilcox static int virtio_gpu_context_create(struct virtio_gpu_device *vgdev, 556a37c49aSMatthew Wilcox uint32_t nlen, const char *name) 5662fb7a5eSGerd Hoffmann { 572ae7f165SMatthew Wilcox int handle = ida_alloc(&vgdev->ctx_id_ida, GFP_KERNEL); 5862fb7a5eSGerd Hoffmann 596a37c49aSMatthew Wilcox if (handle < 0) 606a37c49aSMatthew Wilcox return handle; 612ae7f165SMatthew Wilcox handle += 1; 626a37c49aSMatthew Wilcox virtio_gpu_cmd_context_create(vgdev, handle, nlen, name); 636a37c49aSMatthew Wilcox return handle; 6462fb7a5eSGerd Hoffmann } 6562fb7a5eSGerd Hoffmann 6662fb7a5eSGerd Hoffmann static void virtio_gpu_context_destroy(struct virtio_gpu_device *vgdev, 6762fb7a5eSGerd Hoffmann uint32_t ctx_id) 6862fb7a5eSGerd Hoffmann { 6962fb7a5eSGerd Hoffmann virtio_gpu_cmd_context_destroy(vgdev, ctx_id); 702ae7f165SMatthew Wilcox ida_free(&vgdev->ctx_id_ida, ctx_id - 1); 7162fb7a5eSGerd Hoffmann } 7262fb7a5eSGerd Hoffmann 73dc5698e8SDave Airlie static void virtio_gpu_init_vq(struct virtio_gpu_queue *vgvq, 74dc5698e8SDave Airlie void (*work_func)(struct work_struct *work)) 75dc5698e8SDave Airlie { 76dc5698e8SDave Airlie spin_lock_init(&vgvq->qlock); 77dc5698e8SDave Airlie init_waitqueue_head(&vgvq->ack_queue); 78dc5698e8SDave Airlie INIT_WORK(&vgvq->dequeue_work, work_func); 79dc5698e8SDave Airlie } 80dc5698e8SDave Airlie 8162fb7a5eSGerd Hoffmann static void virtio_gpu_get_capsets(struct virtio_gpu_device *vgdev, 8262fb7a5eSGerd Hoffmann int num_capsets) 8362fb7a5eSGerd Hoffmann { 8462fb7a5eSGerd Hoffmann int i, ret; 8562fb7a5eSGerd Hoffmann 8662fb7a5eSGerd Hoffmann vgdev->capsets = kcalloc(num_capsets, 8762fb7a5eSGerd Hoffmann sizeof(struct virtio_gpu_drv_capset), 8862fb7a5eSGerd Hoffmann GFP_KERNEL); 8962fb7a5eSGerd Hoffmann if (!vgdev->capsets) { 9062fb7a5eSGerd Hoffmann DRM_ERROR("failed to allocate cap sets\n"); 9162fb7a5eSGerd Hoffmann return; 9262fb7a5eSGerd Hoffmann } 9362fb7a5eSGerd Hoffmann for (i = 0; i < num_capsets; i++) { 9462fb7a5eSGerd Hoffmann virtio_gpu_cmd_get_capset_info(vgdev, i); 9562fb7a5eSGerd Hoffmann ret = wait_event_timeout(vgdev->resp_wq, 9662fb7a5eSGerd Hoffmann vgdev->capsets[i].id > 0, 5 * HZ); 9762fb7a5eSGerd Hoffmann if (ret == 0) { 9862fb7a5eSGerd Hoffmann DRM_ERROR("timed out waiting for cap set %d\n", i); 9962fb7a5eSGerd Hoffmann kfree(vgdev->capsets); 10062fb7a5eSGerd Hoffmann vgdev->capsets = NULL; 10162fb7a5eSGerd Hoffmann return; 10262fb7a5eSGerd Hoffmann } 10362fb7a5eSGerd Hoffmann DRM_INFO("cap set %d: id %d, max-version %d, max-size %d\n", 10462fb7a5eSGerd Hoffmann i, vgdev->capsets[i].id, 10562fb7a5eSGerd Hoffmann vgdev->capsets[i].max_version, 10662fb7a5eSGerd Hoffmann vgdev->capsets[i].max_size); 10762fb7a5eSGerd Hoffmann } 10862fb7a5eSGerd Hoffmann vgdev->num_capsets = num_capsets; 10962fb7a5eSGerd Hoffmann } 11062fb7a5eSGerd Hoffmann 111d516e75cSEzequiel Garcia int virtio_gpu_init(struct drm_device *dev) 112dc5698e8SDave Airlie { 113dc5698e8SDave Airlie static vq_callback_t *callbacks[] = { 114dc5698e8SDave Airlie virtio_gpu_ctrl_ack, virtio_gpu_cursor_ack 115dc5698e8SDave Airlie }; 116f7ad26ffSStefan Hajnoczi static const char * const names[] = { "control", "cursor" }; 117dc5698e8SDave Airlie 118dc5698e8SDave Airlie struct virtio_gpu_device *vgdev; 119dc5698e8SDave Airlie /* this will expand later */ 120dc5698e8SDave Airlie struct virtqueue *vqs[2]; 12162fb7a5eSGerd Hoffmann u32 num_scanouts, num_capsets; 122dc5698e8SDave Airlie int ret; 123dc5698e8SDave Airlie 12418e51064SDaniel Vetter if (!virtio_has_feature(dev_to_virtio(dev->dev), VIRTIO_F_VERSION_1)) 125dc5698e8SDave Airlie return -ENODEV; 126dc5698e8SDave Airlie 127dc5698e8SDave Airlie vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL); 128dc5698e8SDave Airlie if (!vgdev) 129dc5698e8SDave Airlie return -ENOMEM; 130dc5698e8SDave Airlie 131dc5698e8SDave Airlie vgdev->ddev = dev; 132dc5698e8SDave Airlie dev->dev_private = vgdev; 13318e51064SDaniel Vetter vgdev->vdev = dev_to_virtio(dev->dev); 134dc5698e8SDave Airlie vgdev->dev = dev->dev; 135dc5698e8SDave Airlie 136dc5698e8SDave Airlie spin_lock_init(&vgdev->display_info_lock); 1371938d1aeSMatthew Wilcox ida_init(&vgdev->ctx_id_ida); 1381938d1aeSMatthew Wilcox ida_init(&vgdev->resource_ida); 139dc5698e8SDave Airlie init_waitqueue_head(&vgdev->resp_wq); 140dc5698e8SDave Airlie virtio_gpu_init_vq(&vgdev->ctrlq, virtio_gpu_dequeue_ctrl_func); 141dc5698e8SDave Airlie virtio_gpu_init_vq(&vgdev->cursorq, virtio_gpu_dequeue_cursor_func); 142dc5698e8SDave Airlie 143f54d1867SChris Wilson vgdev->fence_drv.context = dma_fence_context_alloc(1); 144dc5698e8SDave Airlie spin_lock_init(&vgdev->fence_drv.lock); 145dc5698e8SDave Airlie INIT_LIST_HEAD(&vgdev->fence_drv.fences); 14662fb7a5eSGerd Hoffmann INIT_LIST_HEAD(&vgdev->cap_cache); 147dc5698e8SDave Airlie INIT_WORK(&vgdev->config_changed_work, 148dc5698e8SDave Airlie virtio_gpu_config_changed_work_func); 149dc5698e8SDave Airlie 150*f0c6cef7SGerd Hoffmann INIT_WORK(&vgdev->obj_free_work, 151*f0c6cef7SGerd Hoffmann virtio_gpu_array_put_free_work); 152*f0c6cef7SGerd Hoffmann INIT_LIST_HEAD(&vgdev->obj_free_list); 153*f0c6cef7SGerd Hoffmann spin_lock_init(&vgdev->obj_free_lock); 154*f0c6cef7SGerd Hoffmann 155ff2ac58aSLaurent Vivier #ifdef __LITTLE_ENDIAN 15662fb7a5eSGerd Hoffmann if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_VIRGL)) 15762fb7a5eSGerd Hoffmann vgdev->has_virgl_3d = true; 15862fb7a5eSGerd Hoffmann DRM_INFO("virgl 3d acceleration %s\n", 159ff2ac58aSLaurent Vivier vgdev->has_virgl_3d ? "enabled" : "not supported by host"); 160ff2ac58aSLaurent Vivier #else 161ff2ac58aSLaurent Vivier DRM_INFO("virgl 3d acceleration not supported by guest\n"); 162ff2ac58aSLaurent Vivier #endif 163b4b01b49SGerd Hoffmann if (virtio_has_feature(vgdev->vdev, VIRTIO_GPU_F_EDID)) { 164b4b01b49SGerd Hoffmann vgdev->has_edid = true; 165b4b01b49SGerd Hoffmann DRM_INFO("EDID support available.\n"); 166b4b01b49SGerd Hoffmann } 16762fb7a5eSGerd Hoffmann 1689b2bbdb2SMichael S. Tsirkin ret = virtio_find_vqs(vgdev->vdev, 2, vqs, callbacks, names, NULL); 169dc5698e8SDave Airlie if (ret) { 170dc5698e8SDave Airlie DRM_ERROR("failed to find virt queues\n"); 171dc5698e8SDave Airlie goto err_vqs; 172dc5698e8SDave Airlie } 173dc5698e8SDave Airlie vgdev->ctrlq.vq = vqs[0]; 174dc5698e8SDave Airlie vgdev->cursorq.vq = vqs[1]; 175dc5698e8SDave Airlie ret = virtio_gpu_alloc_vbufs(vgdev); 176dc5698e8SDave Airlie if (ret) { 177dc5698e8SDave Airlie DRM_ERROR("failed to alloc vbufs\n"); 178dc5698e8SDave Airlie goto err_vbufs; 179dc5698e8SDave Airlie } 180dc5698e8SDave Airlie 181dc5698e8SDave Airlie /* get display info */ 182dc5698e8SDave Airlie virtio_cread(vgdev->vdev, struct virtio_gpu_config, 183dc5698e8SDave Airlie num_scanouts, &num_scanouts); 184dc5698e8SDave Airlie vgdev->num_scanouts = min_t(uint32_t, num_scanouts, 185dc5698e8SDave Airlie VIRTIO_GPU_MAX_SCANOUTS); 186dc5698e8SDave Airlie if (!vgdev->num_scanouts) { 187dc5698e8SDave Airlie DRM_ERROR("num_scanouts is zero\n"); 188dc5698e8SDave Airlie ret = -EINVAL; 189dc5698e8SDave Airlie goto err_scanouts; 190dc5698e8SDave Airlie } 19162fb7a5eSGerd Hoffmann DRM_INFO("number of scanouts: %d\n", num_scanouts); 19262fb7a5eSGerd Hoffmann 19362fb7a5eSGerd Hoffmann virtio_cread(vgdev->vdev, struct virtio_gpu_config, 19462fb7a5eSGerd Hoffmann num_capsets, &num_capsets); 19562fb7a5eSGerd Hoffmann DRM_INFO("number of cap sets: %d\n", num_capsets); 196dc5698e8SDave Airlie 197d516e75cSEzequiel Garcia virtio_gpu_modeset_init(vgdev); 198dc5698e8SDave Airlie 199dc5698e8SDave Airlie virtio_device_ready(vgdev->vdev); 200dc5698e8SDave Airlie vgdev->vqs_ready = true; 201dc5698e8SDave Airlie 20262fb7a5eSGerd Hoffmann if (num_capsets) 20362fb7a5eSGerd Hoffmann virtio_gpu_get_capsets(vgdev, num_capsets); 204b4b01b49SGerd Hoffmann if (vgdev->has_edid) 205b4b01b49SGerd Hoffmann virtio_gpu_cmd_get_edids(vgdev); 206441012afSDave Airlie virtio_gpu_cmd_get_display_info(vgdev); 207441012afSDave Airlie wait_event_timeout(vgdev->resp_wq, !vgdev->display_info_pending, 208441012afSDave Airlie 5 * HZ); 209dc5698e8SDave Airlie return 0; 210dc5698e8SDave Airlie 211dc5698e8SDave Airlie err_scanouts: 212dc5698e8SDave Airlie virtio_gpu_free_vbufs(vgdev); 213dc5698e8SDave Airlie err_vbufs: 214dc5698e8SDave Airlie vgdev->vdev->config->del_vqs(vgdev->vdev); 215dc5698e8SDave Airlie err_vqs: 216dc5698e8SDave Airlie kfree(vgdev); 217dc5698e8SDave Airlie return ret; 218dc5698e8SDave Airlie } 219dc5698e8SDave Airlie 22062fb7a5eSGerd Hoffmann static void virtio_gpu_cleanup_cap_cache(struct virtio_gpu_device *vgdev) 22162fb7a5eSGerd Hoffmann { 22262fb7a5eSGerd Hoffmann struct virtio_gpu_drv_cap_cache *cache_ent, *tmp; 22362fb7a5eSGerd Hoffmann 22462fb7a5eSGerd Hoffmann list_for_each_entry_safe(cache_ent, tmp, &vgdev->cap_cache, head) { 22562fb7a5eSGerd Hoffmann kfree(cache_ent->caps_cache); 22662fb7a5eSGerd Hoffmann kfree(cache_ent); 22762fb7a5eSGerd Hoffmann } 22862fb7a5eSGerd Hoffmann } 22962fb7a5eSGerd Hoffmann 230d516e75cSEzequiel Garcia void virtio_gpu_deinit(struct drm_device *dev) 231dc5698e8SDave Airlie { 232dc5698e8SDave Airlie struct virtio_gpu_device *vgdev = dev->dev_private; 233dc5698e8SDave Airlie 234*f0c6cef7SGerd Hoffmann flush_work(&vgdev->obj_free_work); 235dc5698e8SDave Airlie vgdev->vqs_ready = false; 236dc5698e8SDave Airlie flush_work(&vgdev->ctrlq.dequeue_work); 237dc5698e8SDave Airlie flush_work(&vgdev->cursorq.dequeue_work); 238dc5698e8SDave Airlie flush_work(&vgdev->config_changed_work); 239edde9fc5SEzequiel Garcia vgdev->vdev->config->reset(vgdev->vdev); 240dc5698e8SDave Airlie vgdev->vdev->config->del_vqs(vgdev->vdev); 241dc5698e8SDave Airlie 242dc5698e8SDave Airlie virtio_gpu_modeset_fini(vgdev); 243dc5698e8SDave Airlie virtio_gpu_free_vbufs(vgdev); 24462fb7a5eSGerd Hoffmann virtio_gpu_cleanup_cap_cache(vgdev); 24562fb7a5eSGerd Hoffmann kfree(vgdev->capsets); 246dc5698e8SDave Airlie kfree(vgdev); 247dc5698e8SDave Airlie } 24862fb7a5eSGerd Hoffmann 24962fb7a5eSGerd Hoffmann int virtio_gpu_driver_open(struct drm_device *dev, struct drm_file *file) 25062fb7a5eSGerd Hoffmann { 25162fb7a5eSGerd Hoffmann struct virtio_gpu_device *vgdev = dev->dev_private; 25262fb7a5eSGerd Hoffmann struct virtio_gpu_fpriv *vfpriv; 2536a37c49aSMatthew Wilcox int id; 254a86f2551SMichał Mirosław char dbgname[TASK_COMM_LEN]; 25562fb7a5eSGerd Hoffmann 25662fb7a5eSGerd Hoffmann /* can't create contexts without 3d renderer */ 25762fb7a5eSGerd Hoffmann if (!vgdev->has_virgl_3d) 25862fb7a5eSGerd Hoffmann return 0; 25962fb7a5eSGerd Hoffmann 26062fb7a5eSGerd Hoffmann /* allocate a virt GPU context for this opener */ 26162fb7a5eSGerd Hoffmann vfpriv = kzalloc(sizeof(*vfpriv), GFP_KERNEL); 26262fb7a5eSGerd Hoffmann if (!vfpriv) 26362fb7a5eSGerd Hoffmann return -ENOMEM; 26462fb7a5eSGerd Hoffmann 265a86f2551SMichał Mirosław get_task_comm(dbgname, current); 2666a37c49aSMatthew Wilcox id = virtio_gpu_context_create(vgdev, strlen(dbgname), dbgname); 267040b595aSColin Ian King if (id < 0) { 268040b595aSColin Ian King kfree(vfpriv); 2696a37c49aSMatthew Wilcox return id; 270040b595aSColin Ian King } 27162fb7a5eSGerd Hoffmann 27262fb7a5eSGerd Hoffmann vfpriv->ctx_id = id; 27362fb7a5eSGerd Hoffmann file->driver_priv = vfpriv; 27462fb7a5eSGerd Hoffmann return 0; 27562fb7a5eSGerd Hoffmann } 27662fb7a5eSGerd Hoffmann 27762fb7a5eSGerd Hoffmann void virtio_gpu_driver_postclose(struct drm_device *dev, struct drm_file *file) 27862fb7a5eSGerd Hoffmann { 27962fb7a5eSGerd Hoffmann struct virtio_gpu_device *vgdev = dev->dev_private; 28062fb7a5eSGerd Hoffmann struct virtio_gpu_fpriv *vfpriv; 28162fb7a5eSGerd Hoffmann 28262fb7a5eSGerd Hoffmann if (!vgdev->has_virgl_3d) 28362fb7a5eSGerd Hoffmann return; 28462fb7a5eSGerd Hoffmann 28562fb7a5eSGerd Hoffmann vfpriv = file->driver_priv; 28662fb7a5eSGerd Hoffmann 28762fb7a5eSGerd Hoffmann virtio_gpu_context_destroy(vgdev, vfpriv->ctx_id); 28862fb7a5eSGerd Hoffmann kfree(vfpriv); 28962fb7a5eSGerd Hoffmann file->driver_priv = NULL; 29062fb7a5eSGerd Hoffmann } 291