xref: /linux/drivers/gpu/drm/virtio/virtgpu_kms.c (revision f0c6cef7e7174bdb11bbf972cded48bbb33bfba4)
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