1dc5698e8SDave Airlie /*
2dc5698e8SDave Airlie  * Copyright (C) 2015 Red Hat, Inc.
3dc5698e8SDave Airlie  * All Rights Reserved.
4dc5698e8SDave Airlie  *
5dc5698e8SDave Airlie  * Authors:
6dc5698e8SDave Airlie  *    Dave Airlie <airlied@redhat.com>
7dc5698e8SDave Airlie  *    Gerd Hoffmann <kraxel@redhat.com>
8dc5698e8SDave Airlie  *
9dc5698e8SDave Airlie  * Permission is hereby granted, free of charge, to any person obtaining a
10dc5698e8SDave Airlie  * copy of this software and associated documentation files (the "Software"),
11dc5698e8SDave Airlie  * to deal in the Software without restriction, including without limitation
12dc5698e8SDave Airlie  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13dc5698e8SDave Airlie  * and/or sell copies of the Software, and to permit persons to whom the
14dc5698e8SDave Airlie  * Software is furnished to do so, subject to the following conditions:
15dc5698e8SDave Airlie  *
16dc5698e8SDave Airlie  * The above copyright notice and this permission notice (including the next
17dc5698e8SDave Airlie  * paragraph) shall be included in all copies or substantial portions of the
18dc5698e8SDave Airlie  * Software.
19dc5698e8SDave Airlie  *
20dc5698e8SDave Airlie  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21dc5698e8SDave Airlie  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22dc5698e8SDave Airlie  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23dc5698e8SDave Airlie  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
24dc5698e8SDave Airlie  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
25dc5698e8SDave Airlie  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26dc5698e8SDave Airlie  * OTHER DEALINGS IN THE SOFTWARE.
27dc5698e8SDave Airlie  */
28dc5698e8SDave Airlie 
29dc5698e8SDave Airlie #include <linux/module.h>
30dc5698e8SDave Airlie #include <linux/pci.h>
31cd7f5ca3SGurchetan Singh #include <linux/poll.h>
32cd7f5ca3SGurchetan Singh #include <linux/wait.h>
33a3d63977SSam Ravnborg 
34ff67a25dSMasahiro Yamada #include <drm/drm.h>
356848c291SThomas Zimmermann #include <drm/drm_aperture.h>
36b1df3a2bSGerd Hoffmann #include <drm/drm_atomic_helper.h>
37a3d63977SSam Ravnborg #include <drm/drm_drv.h>
388ab59da2SThomas Zimmermann #include <drm/drm_fbdev_generic.h>
39a3d63977SSam Ravnborg #include <drm/drm_file.h>
40dc5698e8SDave Airlie 
41dc5698e8SDave Airlie #include "virtgpu_drv.h"
42a3d63977SSam Ravnborg 
4370a59dd8SDaniel Vetter static const struct drm_driver driver;
44dc5698e8SDave Airlie 
45dc5698e8SDave Airlie static int virtio_gpu_modeset = -1;
46dc5698e8SDave Airlie 
47dc5698e8SDave Airlie MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
48dc5698e8SDave Airlie module_param_named(modeset, virtio_gpu_modeset, int, 0400);
49dc5698e8SDave Airlie 
virtio_gpu_pci_quirk(struct drm_device * dev)50b5c9ed70SDmitry Osipenko static int virtio_gpu_pci_quirk(struct drm_device *dev)
51d516e75cSEzequiel Garcia {
52b5c9ed70SDmitry Osipenko 	struct pci_dev *pdev = to_pci_dev(dev->dev);
53d516e75cSEzequiel Garcia 	const char *pname = dev_name(&pdev->dev);
54d516e75cSEzequiel Garcia 	bool vga = (pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA;
556848c291SThomas Zimmermann 	int ret;
56d516e75cSEzequiel Garcia 
57d516e75cSEzequiel Garcia 	DRM_INFO("pci: %s detected at %s\n",
58d516e75cSEzequiel Garcia 		 vga ? "virtio-vga" : "virtio-gpu-pci",
59d516e75cSEzequiel Garcia 		 pname);
606848c291SThomas Zimmermann 	if (vga) {
6197c9bfe3SThomas Zimmermann 		ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, &driver);
626848c291SThomas Zimmermann 		if (ret)
636848c291SThomas Zimmermann 			return ret;
646848c291SThomas Zimmermann 	}
65d516e75cSEzequiel Garcia 
66b5c9ed70SDmitry Osipenko 	return 0;
67d516e75cSEzequiel Garcia }
68d516e75cSEzequiel Garcia 
virtio_gpu_probe(struct virtio_device * vdev)69dc5698e8SDave Airlie static int virtio_gpu_probe(struct virtio_device *vdev)
70dc5698e8SDave Airlie {
71d516e75cSEzequiel Garcia 	struct drm_device *dev;
7248a77d66SGerd Hoffmann 	int ret;
7348a77d66SGerd Hoffmann 
746a2d2ddfSJavier Martinez Canillas 	if (drm_firmware_drivers_only() && virtio_gpu_modeset == -1)
75dc5698e8SDave Airlie 		return -EINVAL;
76dc5698e8SDave Airlie 
77dc5698e8SDave Airlie 	if (virtio_gpu_modeset == 0)
78dc5698e8SDave Airlie 		return -EINVAL;
79dc5698e8SDave Airlie 
80b5c9ed70SDmitry Osipenko 	/*
81b5c9ed70SDmitry Osipenko 	 * The virtio-gpu device is a virtual device that doesn't have DMA
82b5c9ed70SDmitry Osipenko 	 * ops assigned to it, nor DMA mask set and etc. Its parent device
83b5c9ed70SDmitry Osipenko 	 * is actual GPU device we want to use it for the DRM's device in
84b5c9ed70SDmitry Osipenko 	 * order to benefit from using generic DRM APIs.
85b5c9ed70SDmitry Osipenko 	 */
86b5c9ed70SDmitry Osipenko 	dev = drm_dev_alloc(&driver, vdev->dev.parent);
87d516e75cSEzequiel Garcia 	if (IS_ERR(dev))
88d516e75cSEzequiel Garcia 		return PTR_ERR(dev);
89d516e75cSEzequiel Garcia 	vdev->priv = dev;
90d516e75cSEzequiel Garcia 
91a9d2e8b5SDmitry Osipenko 	if (dev_is_pci(vdev->dev.parent)) {
92b5c9ed70SDmitry Osipenko 		ret = virtio_gpu_pci_quirk(dev);
9348a77d66SGerd Hoffmann 		if (ret)
94d516e75cSEzequiel Garcia 			goto err_free;
95d516e75cSEzequiel Garcia 	}
96d516e75cSEzequiel Garcia 
97*0b1d4187SSebastian Ott 	dma_set_max_seg_size(dev->dev, dma_max_mapping_size(dev->dev) ?: UINT_MAX);
98b5c9ed70SDmitry Osipenko 	ret = virtio_gpu_init(vdev, dev);
99d516e75cSEzequiel Garcia 	if (ret)
100d516e75cSEzequiel Garcia 		goto err_free;
101d516e75cSEzequiel Garcia 
102d516e75cSEzequiel Garcia 	ret = drm_dev_register(dev, 0);
103d516e75cSEzequiel Garcia 	if (ret)
104058acb33SXie Yongji 		goto err_deinit;
10548a77d66SGerd Hoffmann 
10648a77d66SGerd Hoffmann 	drm_fbdev_generic_setup(vdev->priv, 32);
10748a77d66SGerd Hoffmann 	return 0;
108d516e75cSEzequiel Garcia 
109058acb33SXie Yongji err_deinit:
110058acb33SXie Yongji 	virtio_gpu_deinit(dev);
111d516e75cSEzequiel Garcia err_free:
112d516e75cSEzequiel Garcia 	drm_dev_put(dev);
113d516e75cSEzequiel Garcia 	return ret;
114dc5698e8SDave Airlie }
115dc5698e8SDave Airlie 
virtio_gpu_remove(struct virtio_device * vdev)116dc5698e8SDave Airlie static void virtio_gpu_remove(struct virtio_device *vdev)
117dc5698e8SDave Airlie {
118dc5698e8SDave Airlie 	struct drm_device *dev = vdev->priv;
1199d492b6bSRodrigo Siqueira 
120b1df3a2bSGerd Hoffmann 	drm_dev_unplug(dev);
121b1df3a2bSGerd Hoffmann 	drm_atomic_helper_shutdown(dev);
122d516e75cSEzequiel Garcia 	virtio_gpu_deinit(dev);
123c8f95a56SChuhong Yuan 	drm_dev_put(dev);
124dc5698e8SDave Airlie }
125dc5698e8SDave Airlie 
virtio_gpu_config_changed(struct virtio_device * vdev)126dc5698e8SDave Airlie static void virtio_gpu_config_changed(struct virtio_device *vdev)
127dc5698e8SDave Airlie {
128dc5698e8SDave Airlie 	struct drm_device *dev = vdev->priv;
129dc5698e8SDave Airlie 	struct virtio_gpu_device *vgdev = dev->dev_private;
130dc5698e8SDave Airlie 
131dc5698e8SDave Airlie 	schedule_work(&vgdev->config_changed_work);
132dc5698e8SDave Airlie }
133dc5698e8SDave Airlie 
134dc5698e8SDave Airlie static struct virtio_device_id id_table[] = {
135dc5698e8SDave Airlie 	{ VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID },
136dc5698e8SDave Airlie 	{ 0 },
137dc5698e8SDave Airlie };
138dc5698e8SDave Airlie 
139dc5698e8SDave Airlie static unsigned int features[] = {
14062fb7a5eSGerd Hoffmann #ifdef __LITTLE_ENDIAN
14162fb7a5eSGerd Hoffmann 	/*
14262fb7a5eSGerd Hoffmann 	 * Gallium command stream send by virgl is native endian.
14362fb7a5eSGerd Hoffmann 	 * Because of that we only support little endian guests on
14462fb7a5eSGerd Hoffmann 	 * little endian hosts.
14562fb7a5eSGerd Hoffmann 	 */
14662fb7a5eSGerd Hoffmann 	VIRTIO_GPU_F_VIRGL,
14762fb7a5eSGerd Hoffmann #endif
148b4b01b49SGerd Hoffmann 	VIRTIO_GPU_F_EDID,
149c84adb30SDavid Stevens 	VIRTIO_GPU_F_RESOURCE_UUID,
1506815cfe6SGerd Hoffmann 	VIRTIO_GPU_F_RESOURCE_BLOB,
1516198770aSAnthoine Bourgeois 	VIRTIO_GPU_F_CONTEXT_INIT,
152dc5698e8SDave Airlie };
153dc5698e8SDave Airlie static struct virtio_driver virtio_gpu_driver = {
154dc5698e8SDave Airlie 	.feature_table = features,
155dc5698e8SDave Airlie 	.feature_table_size = ARRAY_SIZE(features),
156dc5698e8SDave Airlie 	.driver.name = KBUILD_MODNAME,
157dc5698e8SDave Airlie 	.driver.owner = THIS_MODULE,
158dc5698e8SDave Airlie 	.id_table = id_table,
159dc5698e8SDave Airlie 	.probe = virtio_gpu_probe,
160dc5698e8SDave Airlie 	.remove = virtio_gpu_remove,
161dc5698e8SDave Airlie 	.config_changed = virtio_gpu_config_changed
162dc5698e8SDave Airlie };
163dc5698e8SDave Airlie 
164dc5698e8SDave Airlie module_virtio_driver(virtio_gpu_driver);
165dc5698e8SDave Airlie 
166dc5698e8SDave Airlie MODULE_DEVICE_TABLE(virtio, id_table);
167dc5698e8SDave Airlie MODULE_DESCRIPTION("Virtio GPU driver");
168dc5698e8SDave Airlie MODULE_LICENSE("GPL and additional rights");
169dc5698e8SDave Airlie MODULE_AUTHOR("Dave Airlie <airlied@redhat.com>");
170dc5698e8SDave Airlie MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>");
171dc5698e8SDave Airlie MODULE_AUTHOR("Alon Levy");
172dc5698e8SDave Airlie 
17342abd004SGurchetan Singh DEFINE_DRM_GEM_FOPS(virtio_gpu_driver_fops);
174dc5698e8SDave Airlie 
17570a59dd8SDaniel Vetter static const struct drm_driver driver = {
17672122c69SRob Clark 	/*
17772122c69SRob Clark 	 * If KMS is disabled DRIVER_MODESET and DRIVER_ATOMIC are masked
17872122c69SRob Clark 	 * out via drm_device::driver_features:
17972122c69SRob Clark 	 */
1807cb8d1abSDmitry Osipenko 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_RENDER | DRIVER_ATOMIC |
18187b3b45cSZack Rusin 			   DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE | DRIVER_CURSOR_HOTSPOT,
18262fb7a5eSGerd Hoffmann 	.open = virtio_gpu_driver_open,
18362fb7a5eSGerd Hoffmann 	.postclose = virtio_gpu_driver_postclose,
184dc5698e8SDave Airlie 
185dc5698e8SDave Airlie 	.dumb_create = virtio_gpu_mode_dumb_create,
186dc5698e8SDave Airlie 	.dumb_map_offset = virtio_gpu_mode_dumb_mmap,
187dc5698e8SDave Airlie 
188dc5698e8SDave Airlie #if defined(CONFIG_DEBUG_FS)
189dc5698e8SDave Airlie 	.debugfs_init = virtio_gpu_debugfs_init,
190dc5698e8SDave Airlie #endif
191c84adb30SDavid Stevens 	.gem_prime_import = virtgpu_gem_prime_import,
192a0cecc23SDave Airlie 	.gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table,
193dc5698e8SDave Airlie 
194c66df701SGerd Hoffmann 	.gem_create_object = virtio_gpu_create_object,
195dc5698e8SDave Airlie 	.fops = &virtio_gpu_driver_fops,
196dc5698e8SDave Airlie 
19762fb7a5eSGerd Hoffmann 	.ioctls = virtio_gpu_ioctls,
19862fb7a5eSGerd Hoffmann 	.num_ioctls = DRM_VIRTIO_NUM_IOCTLS,
19962fb7a5eSGerd Hoffmann 
200dc5698e8SDave Airlie 	.name = DRIVER_NAME,
201dc5698e8SDave Airlie 	.desc = DRIVER_DESC,
202dc5698e8SDave Airlie 	.date = DRIVER_DATE,
203dc5698e8SDave Airlie 	.major = DRIVER_MAJOR,
204dc5698e8SDave Airlie 	.minor = DRIVER_MINOR,
205dc5698e8SDave Airlie 	.patchlevel = DRIVER_PATCHLEVEL,
206b1df3a2bSGerd Hoffmann 
207b1df3a2bSGerd Hoffmann 	.release = virtio_gpu_release,
208dc5698e8SDave Airlie };
209