VirtualBox

source: vbox/trunk/src/VBox/Additions/linux/drm/vbox_drv.c@ 79449

Last change on this file since 79449 was 79025, checked in by vboxsync, 6 years ago

Linux/host and guest drivers: support more openSUSE 15.0 and 15.1 kernels.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.1 KB
Line 
1/* $Id: vbox_drv.c 79025 2019-06-06 14:33:47Z vboxsync $ */
2/** @file
3 * VirtualBox Additions Linux kernel video driver
4 */
5
6/*
7 * Copyright (C) 2013-2019 Oracle Corporation
8 * This file is based on ast_drv.c
9 * Copyright 2012 Red Hat Inc.
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the
13 * "Software"), to deal in the Software without restriction, including
14 * without limitation the rights to use, copy, modify, merge, publish,
15 * distribute, sub license, and/or sell copies of the Software, and to
16 * permit persons to whom the Software is furnished to do so, subject to
17 * the following conditions:
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25 * USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 * The above copyright notice and this permission notice (including the
28 * next paragraph) shall be included in all copies or substantial portions
29 * of the Software.
30 *
31 * Authors: Dave Airlie <airlied@redhat.com>
32 * Michael Thayer <michael.thayer@oracle.com,
33 * Hans de Goede <hdegoede@redhat.com>
34 */
35#include <linux/module.h>
36#include <linux/console.h>
37#include <linux/vt_kern.h>
38
39#include <drm/drmP.h>
40#include <drm/drm_crtc_helper.h>
41
42#include "vbox_drv.h"
43
44#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
45#include <drm/drm_probe_helper.h>
46#endif
47
48#include "version-generated.h"
49#include "revision-generated.h"
50
51static int vbox_modeset = -1;
52
53MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
54module_param_named(modeset, vbox_modeset, int, 0400);
55
56static struct drm_driver driver;
57
58static const struct pci_device_id pciidlist[] = {
59 { 0x80ee, 0xbeef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
60 { 0, 0, 0},
61};
62MODULE_DEVICE_TABLE(pci, pciidlist);
63
64static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
65{
66#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
67 return drm_get_pci_dev(pdev, ent, &driver);
68#else
69 struct drm_device *dev = NULL;
70 int ret = 0;
71
72 dev = drm_dev_alloc(&driver, &pdev->dev);
73 if (IS_ERR(dev)) {
74 ret = PTR_ERR(dev);
75 goto err_drv_alloc;
76 }
77 dev->pdev = pdev;
78 pci_set_drvdata(pdev, dev);
79
80 ret = vbox_driver_load(dev);
81 if (ret)
82 goto err_vbox_driver_load;
83
84 ret = drm_dev_register(dev, 0);
85 if (ret)
86 goto err_drv_dev_register;
87 return ret;
88
89err_drv_dev_register:
90 vbox_driver_unload(dev);
91err_vbox_driver_load:
92 drm_dev_put(dev);
93err_drv_alloc:
94 return ret;
95#endif
96}
97
98static void vbox_pci_remove(struct pci_dev *pdev)
99{
100 struct drm_device *dev = pci_get_drvdata(pdev);
101
102#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
103 drm_put_dev(dev);
104#else
105 drm_dev_unregister(dev);
106 vbox_driver_unload(dev);
107 drm_dev_put(dev);
108#endif
109}
110
111#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 9, 0) && !defined(RHEL_74)
112static void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
113 bool suspend)
114{
115 if (!fb_helper || !fb_helper->fbdev)
116 return;
117
118 console_lock();
119 fb_set_suspend(fb_helper->fbdev, suspend);
120 console_unlock();
121}
122#endif
123
124static int vbox_drm_freeze(struct drm_device *dev)
125{
126 struct vbox_private *vbox = dev->dev_private;
127
128 drm_kms_helper_poll_disable(dev);
129
130 pci_save_state(dev->pdev);
131
132 drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, true);
133
134 return 0;
135}
136
137static int vbox_drm_thaw(struct drm_device *dev)
138{
139 struct vbox_private *vbox = dev->dev_private;
140
141 drm_mode_config_reset(dev);
142 drm_helper_resume_force_mode(dev);
143 drm_fb_helper_set_suspend_unlocked(&vbox->fbdev->helper, false);
144
145 return 0;
146}
147
148static int vbox_drm_resume(struct drm_device *dev)
149{
150 int ret;
151
152 if (pci_enable_device(dev->pdev))
153 return -EIO;
154
155 ret = vbox_drm_thaw(dev);
156 if (ret)
157 return ret;
158
159 drm_kms_helper_poll_enable(dev);
160
161 return 0;
162}
163
164static int vbox_pm_suspend(struct device *dev)
165{
166 struct pci_dev *pdev = to_pci_dev(dev);
167 struct drm_device *ddev = pci_get_drvdata(pdev);
168 int error;
169
170 error = vbox_drm_freeze(ddev);
171 if (error)
172 return error;
173
174 pci_disable_device(pdev);
175 pci_set_power_state(pdev, PCI_D3hot);
176
177 return 0;
178}
179
180static int vbox_pm_resume(struct device *dev)
181{
182 struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
183
184 return vbox_drm_resume(ddev);
185}
186
187static int vbox_pm_freeze(struct device *dev)
188{
189 struct pci_dev *pdev = to_pci_dev(dev);
190 struct drm_device *ddev = pci_get_drvdata(pdev);
191
192 if (!ddev || !ddev->dev_private)
193 return -ENODEV;
194
195 return vbox_drm_freeze(ddev);
196}
197
198static int vbox_pm_thaw(struct device *dev)
199{
200 struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
201
202 return vbox_drm_thaw(ddev);
203}
204
205static int vbox_pm_poweroff(struct device *dev)
206{
207 struct drm_device *ddev = pci_get_drvdata(to_pci_dev(dev));
208
209 return vbox_drm_freeze(ddev);
210}
211
212static const struct dev_pm_ops vbox_pm_ops = {
213 .suspend = vbox_pm_suspend,
214 .resume = vbox_pm_resume,
215 .freeze = vbox_pm_freeze,
216 .thaw = vbox_pm_thaw,
217 .poweroff = vbox_pm_poweroff,
218 .restore = vbox_pm_resume,
219};
220
221static struct pci_driver vbox_pci_driver = {
222 .name = DRIVER_NAME,
223 .id_table = pciidlist,
224 .probe = vbox_pci_probe,
225 .remove = vbox_pci_remove,
226 .driver.pm = &vbox_pm_ops,
227};
228
229#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) && !defined(RHEL_74)
230/* This works around a bug in X servers prior to 1.18.4, which sometimes
231 * submit more dirty rectangles than the kernel is willing to handle and
232 * then disable dirty rectangle handling altogether when they see the
233 * EINVAL error. I do not want the code to hang around forever, which is
234 * why I am limiting it to certain kernel versions. We can increase the
235 * limit if some distributions uses old X servers with new kernels. */
236long vbox_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
237{
238 long rc = drm_ioctl(filp, cmd, arg);
239
240 if (cmd == DRM_IOCTL_MODE_DIRTYFB && rc == -EINVAL)
241 return -EOVERFLOW;
242
243 return rc;
244}
245#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) && !RHEL_74 */
246
247static const struct file_operations vbox_fops = {
248 .owner = THIS_MODULE,
249 .open = drm_open,
250 .release = drm_release,
251#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) && !defined(RHEL_74)
252 .unlocked_ioctl = vbox_ioctl,
253#else
254 .unlocked_ioctl = drm_ioctl,
255#endif
256 .mmap = vbox_mmap,
257 .poll = drm_poll,
258#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && !defined(RHEL_70)
259 .fasync = drm_fasync,
260#endif
261#ifdef CONFIG_COMPAT
262 .compat_ioctl = drm_compat_ioctl,
263#endif
264 .read = drm_read,
265};
266
267static int vbox_master_set(struct drm_device *dev,
268 struct drm_file *file_priv, bool from_open)
269{
270 struct vbox_private *vbox = dev->dev_private;
271
272 /*
273 * We do not yet know whether the new owner can handle hotplug, so we
274 * do not advertise dynamic modes on the first query and send a
275 * tentative hotplug notification after that to see if they query again.
276 */
277 vbox->initial_mode_queried = false;
278
279 mutex_lock(&vbox->hw_mutex);
280 /* Start the refresh timer in case the user does not provide dirty
281 * rectangles. */
282 vbox->need_refresh_timer = true;
283 schedule_delayed_work(&vbox->refresh_work, VBOX_REFRESH_PERIOD);
284 mutex_unlock(&vbox->hw_mutex);
285
286 return 0;
287}
288
289#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) && !defined(RHEL_74)
290static void vbox_master_drop(struct drm_device *dev,
291 struct drm_file *file_priv, bool from_release)
292#else
293static void vbox_master_drop(struct drm_device *dev, struct drm_file *file_priv)
294#endif
295{
296 struct vbox_private *vbox = dev->dev_private;
297
298 /* See vbox_master_set() */
299 vbox->initial_mode_queried = false;
300 vbox_report_caps(vbox);
301
302 mutex_lock(&vbox->hw_mutex);
303 vbox->need_refresh_timer = false;
304 mutex_unlock(&vbox->hw_mutex);
305}
306
307static struct drm_driver driver = {
308 .driver_features =
309 DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ |
310#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)
311 DRIVER_IRQ_SHARED |
312#endif
313 DRIVER_PRIME,
314 .dev_priv_size = 0,
315
316#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
317 /* Legacy hooks, but still supported. */
318 .load = vbox_driver_load,
319 .unload = vbox_driver_unload,
320#endif
321 .lastclose = vbox_driver_lastclose,
322 .master_set = vbox_master_set,
323 .master_drop = vbox_master_drop,
324#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) || defined(RHEL_73)
325# if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) && !defined(RHEL_75) \
326 && !defined(OPENSUSE_151)
327 .set_busid = drm_pci_set_busid,
328# endif
329#endif
330
331 .fops = &vbox_fops,
332 .irq_handler = vbox_irq_handler,
333 .name = DRIVER_NAME,
334 .desc = DRIVER_DESC,
335 .date = DRIVER_DATE,
336 .major = DRIVER_MAJOR,
337 .minor = DRIVER_MINOR,
338 .patchlevel = DRIVER_PATCHLEVEL,
339
340#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
341 .gem_free_object = vbox_gem_free_object,
342#else
343 .gem_free_object_unlocked = vbox_gem_free_object,
344#endif
345 .dumb_create = vbox_dumb_create,
346 .dumb_map_offset = vbox_dumb_mmap_offset,
347#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && !defined(RHEL_73)
348 .dumb_destroy = vbox_dumb_destroy,
349#else
350 .dumb_destroy = drm_gem_dumb_destroy,
351#endif
352 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
353 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
354 .gem_prime_export = drm_gem_prime_export,
355 .gem_prime_import = drm_gem_prime_import,
356 .gem_prime_pin = vbox_gem_prime_pin,
357 .gem_prime_unpin = vbox_gem_prime_unpin,
358 .gem_prime_get_sg_table = vbox_gem_prime_get_sg_table,
359 .gem_prime_import_sg_table = vbox_gem_prime_import_sg_table,
360 .gem_prime_vmap = vbox_gem_prime_vmap,
361 .gem_prime_vunmap = vbox_gem_prime_vunmap,
362 .gem_prime_mmap = vbox_gem_prime_mmap,
363};
364
365static int __init vbox_init(void)
366{
367#if defined(CONFIG_VGA_CONSOLE) || LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
368 if (vgacon_text_force() && vbox_modeset == -1)
369 return -EINVAL;
370#endif
371
372 if (vbox_modeset == 0)
373 return -EINVAL;
374
375 return pci_register_driver(&vbox_pci_driver);
376}
377
378static void __exit vbox_exit(void)
379{
380 pci_unregister_driver(&vbox_pci_driver);
381}
382
383module_init(vbox_init);
384module_exit(vbox_exit);
385
386MODULE_AUTHOR(DRIVER_AUTHOR);
387MODULE_DESCRIPTION(DRIVER_DESC);
388MODULE_LICENSE("GPL and additional rights");
389#ifdef MODULE_VERSION
390MODULE_VERSION(VBOX_VERSION_STRING " r" __stringify(VBOX_SVN_REV));
391#endif
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette