1 | /* |
2 | * Copyright (C) 2015 Red Hat, Inc. |
3 | * All Rights Reserved. |
4 | * |
5 | * Authors: |
6 | * Dave Airlie <airlied@redhat.com> |
7 | * Gerd Hoffmann <kraxel@redhat.com> |
8 | * |
9 | * Permission is hereby granted, free of charge, to any person obtaining a |
10 | * copy of this software and associated documentation files (the "Software"), |
11 | * to deal in the Software without restriction, including without limitation |
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
13 | * and/or sell copies of the Software, and to permit persons to whom the |
14 | * Software is furnished to do so, subject to the following conditions: |
15 | * |
16 | * The above copyright notice and this permission notice (including the next |
17 | * paragraph) shall be included in all copies or substantial portions of the |
18 | * Software. |
19 | * |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
26 | * OTHER DEALINGS IN THE SOFTWARE. |
27 | */ |
28 | |
29 | #include <linux/module.h> |
30 | #include <linux/pci.h> |
31 | #include <linux/poll.h> |
32 | #include <linux/wait.h> |
33 | |
34 | #include <drm/drm.h> |
35 | #include <drm/drm_aperture.h> |
36 | #include <drm/drm_atomic_helper.h> |
37 | #include <drm/drm_drv.h> |
38 | #include <drm/drm_fbdev_generic.h> |
39 | #include <drm/drm_file.h> |
40 | |
41 | #include "virtgpu_drv.h" |
42 | |
43 | static const struct drm_driver driver; |
44 | |
45 | static int virtio_gpu_modeset = -1; |
46 | |
47 | MODULE_PARM_DESC(modeset, "Disable/Enable modesetting" ); |
48 | module_param_named(modeset, virtio_gpu_modeset, int, 0400); |
49 | |
50 | static int virtio_gpu_pci_quirk(struct drm_device *dev) |
51 | { |
52 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
53 | const char *pname = dev_name(dev: &pdev->dev); |
54 | bool vga = pci_is_vga(pdev); |
55 | int ret; |
56 | |
57 | DRM_INFO("pci: %s detected at %s\n" , |
58 | vga ? "virtio-vga" : "virtio-gpu-pci" , |
59 | pname); |
60 | if (vga) { |
61 | ret = drm_aperture_remove_conflicting_pci_framebuffers(pdev, req_driver: &driver); |
62 | if (ret) |
63 | return ret; |
64 | } |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | static int virtio_gpu_probe(struct virtio_device *vdev) |
70 | { |
71 | struct drm_device *dev; |
72 | int ret; |
73 | |
74 | if (drm_firmware_drivers_only() && virtio_gpu_modeset == -1) |
75 | return -EINVAL; |
76 | |
77 | if (virtio_gpu_modeset == 0) |
78 | return -EINVAL; |
79 | |
80 | /* |
81 | * The virtio-gpu device is a virtual device that doesn't have DMA |
82 | * ops assigned to it, nor DMA mask set and etc. Its parent device |
83 | * is actual GPU device we want to use it for the DRM's device in |
84 | * order to benefit from using generic DRM APIs. |
85 | */ |
86 | dev = drm_dev_alloc(driver: &driver, parent: vdev->dev.parent); |
87 | if (IS_ERR(ptr: dev)) |
88 | return PTR_ERR(ptr: dev); |
89 | vdev->priv = dev; |
90 | |
91 | if (dev_is_pci(vdev->dev.parent)) { |
92 | ret = virtio_gpu_pci_quirk(dev); |
93 | if (ret) |
94 | goto err_free; |
95 | } |
96 | |
97 | ret = virtio_gpu_init(vdev, dev); |
98 | if (ret) |
99 | goto err_free; |
100 | |
101 | ret = drm_dev_register(dev, flags: 0); |
102 | if (ret) |
103 | goto err_deinit; |
104 | |
105 | drm_fbdev_generic_setup(dev: vdev->priv, preferred_bpp: 32); |
106 | return 0; |
107 | |
108 | err_deinit: |
109 | virtio_gpu_deinit(dev); |
110 | err_free: |
111 | drm_dev_put(dev); |
112 | return ret; |
113 | } |
114 | |
115 | static void virtio_gpu_remove(struct virtio_device *vdev) |
116 | { |
117 | struct drm_device *dev = vdev->priv; |
118 | |
119 | drm_dev_unplug(dev); |
120 | drm_atomic_helper_shutdown(dev); |
121 | virtio_gpu_deinit(dev); |
122 | drm_dev_put(dev); |
123 | } |
124 | |
125 | static void virtio_gpu_config_changed(struct virtio_device *vdev) |
126 | { |
127 | struct drm_device *dev = vdev->priv; |
128 | struct virtio_gpu_device *vgdev = dev->dev_private; |
129 | |
130 | schedule_work(work: &vgdev->config_changed_work); |
131 | } |
132 | |
133 | static struct virtio_device_id id_table[] = { |
134 | { VIRTIO_ID_GPU, VIRTIO_DEV_ANY_ID }, |
135 | { 0 }, |
136 | }; |
137 | |
138 | static unsigned int features[] = { |
139 | #ifdef __LITTLE_ENDIAN |
140 | /* |
141 | * Gallium command stream send by virgl is native endian. |
142 | * Because of that we only support little endian guests on |
143 | * little endian hosts. |
144 | */ |
145 | VIRTIO_GPU_F_VIRGL, |
146 | #endif |
147 | VIRTIO_GPU_F_EDID, |
148 | VIRTIO_GPU_F_RESOURCE_UUID, |
149 | VIRTIO_GPU_F_RESOURCE_BLOB, |
150 | VIRTIO_GPU_F_CONTEXT_INIT, |
151 | }; |
152 | static struct virtio_driver virtio_gpu_driver = { |
153 | .feature_table = features, |
154 | .feature_table_size = ARRAY_SIZE(features), |
155 | .driver.name = KBUILD_MODNAME, |
156 | .driver.owner = THIS_MODULE, |
157 | .id_table = id_table, |
158 | .probe = virtio_gpu_probe, |
159 | .remove = virtio_gpu_remove, |
160 | .config_changed = virtio_gpu_config_changed |
161 | }; |
162 | |
163 | module_virtio_driver(virtio_gpu_driver); |
164 | |
165 | MODULE_DEVICE_TABLE(virtio, id_table); |
166 | MODULE_DESCRIPTION("Virtio GPU driver" ); |
167 | MODULE_LICENSE("GPL and additional rights" ); |
168 | MODULE_AUTHOR("Dave Airlie <airlied@redhat.com>" ); |
169 | MODULE_AUTHOR("Gerd Hoffmann <kraxel@redhat.com>" ); |
170 | MODULE_AUTHOR("Alon Levy" ); |
171 | |
172 | DEFINE_DRM_GEM_FOPS(virtio_gpu_driver_fops); |
173 | |
174 | static const struct drm_driver driver = { |
175 | /* |
176 | * If KMS is disabled DRIVER_MODESET and DRIVER_ATOMIC are masked |
177 | * out via drm_device::driver_features: |
178 | */ |
179 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_RENDER | DRIVER_ATOMIC | |
180 | DRIVER_SYNCOBJ | DRIVER_SYNCOBJ_TIMELINE, |
181 | .open = virtio_gpu_driver_open, |
182 | .postclose = virtio_gpu_driver_postclose, |
183 | |
184 | .dumb_create = virtio_gpu_mode_dumb_create, |
185 | .dumb_map_offset = virtio_gpu_mode_dumb_mmap, |
186 | |
187 | #if defined(CONFIG_DEBUG_FS) |
188 | .debugfs_init = virtio_gpu_debugfs_init, |
189 | #endif |
190 | .gem_prime_import = virtgpu_gem_prime_import, |
191 | .gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table, |
192 | |
193 | .gem_create_object = virtio_gpu_create_object, |
194 | .fops = &virtio_gpu_driver_fops, |
195 | |
196 | .ioctls = virtio_gpu_ioctls, |
197 | .num_ioctls = DRM_VIRTIO_NUM_IOCTLS, |
198 | |
199 | .name = DRIVER_NAME, |
200 | .desc = DRIVER_DESC, |
201 | .date = DRIVER_DATE, |
202 | .major = DRIVER_MAJOR, |
203 | .minor = DRIVER_MINOR, |
204 | .patchlevel = DRIVER_PATCHLEVEL, |
205 | |
206 | .release = virtio_gpu_release, |
207 | }; |
208 | |