]>
Commit | Line | Data |
---|---|---|
acc962c5 | 1 | // SPDX-License-Identifier: MIT |
dd55d44f HG |
2 | /* |
3 | * Copyright (C) 2013-2017 Oracle Corporation | |
4 | * This file is based on ast_drv.c | |
5 | * Copyright 2012 Red Hat Inc. | |
dd55d44f HG |
6 | * Authors: Dave Airlie <airlied@redhat.com> |
7 | * Michael Thayer <michael.thayer@oracle.com, | |
8 | * Hans de Goede <hdegoede@redhat.com> | |
9 | */ | |
10 | #include <linux/module.h> | |
11 | #include <linux/console.h> | |
12 | #include <linux/vt_kern.h> | |
13 | ||
14 | #include <drm/drmP.h> | |
15 | #include <drm/drm_crtc_helper.h> | |
16 | ||
17 | #include "vbox_drv.h" | |
18 | ||
cb67fa13 | 19 | static int vbox_modeset = -1; |
dd55d44f HG |
20 | |
21 | MODULE_PARM_DESC(modeset, "Disable/Enable modesetting"); | |
22 | module_param_named(modeset, vbox_modeset, int, 0400); | |
23 | ||
24 | static struct drm_driver driver; | |
25 | ||
26 | static const struct pci_device_id pciidlist[] = { | |
27 | { 0x80ee, 0xbeef, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, | |
28 | { 0, 0, 0}, | |
29 | }; | |
30 | MODULE_DEVICE_TABLE(pci, pciidlist); | |
31 | ||
e2c3860b HG |
32 | static struct drm_fb_helper_funcs vbox_fb_helper_funcs = { |
33 | .fb_probe = vboxfb_create, | |
34 | }; | |
35 | ||
dd55d44f HG |
36 | static int vbox_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
37 | { | |
01648890 | 38 | struct vbox_private *vbox; |
1daddbc8 FRR |
39 | int ret = 0; |
40 | ||
d4670909 HG |
41 | if (!vbox_check_supported(VBE_DISPI_ID_HGSMI)) |
42 | return -ENODEV; | |
43 | ||
01648890 HG |
44 | vbox = kzalloc(sizeof(*vbox), GFP_KERNEL); |
45 | if (!vbox) | |
46 | return -ENOMEM; | |
1ebafd15 | 47 | |
01648890 HG |
48 | ret = drm_dev_init(&vbox->ddev, &driver, &pdev->dev); |
49 | if (ret) { | |
50 | kfree(vbox); | |
51 | return ret; | |
d4670909 HG |
52 | } |
53 | ||
01648890 HG |
54 | vbox->ddev.pdev = pdev; |
55 | vbox->ddev.dev_private = vbox; | |
56 | pci_set_drvdata(pdev, vbox); | |
d4670909 HG |
57 | mutex_init(&vbox->hw_mutex); |
58 | ||
01648890 HG |
59 | ret = pci_enable_device(pdev); |
60 | if (ret) | |
61 | goto err_dev_put; | |
62 | ||
d4670909 | 63 | ret = vbox_hw_init(vbox); |
1daddbc8 | 64 | if (ret) |
d4670909 | 65 | goto err_pci_disable; |
1daddbc8 | 66 | |
d4670909 | 67 | ret = vbox_mm_init(vbox); |
1daddbc8 | 68 | if (ret) |
d4670909 | 69 | goto err_hw_fini; |
1daddbc8 | 70 | |
01648890 | 71 | ret = vbox_mode_init(vbox); |
d4670909 HG |
72 | if (ret) |
73 | goto err_mm_fini; | |
74 | ||
75 | ret = vbox_irq_init(vbox); | |
76 | if (ret) | |
77 | goto err_mode_fini; | |
78 | ||
e2c3860b HG |
79 | ret = drm_fb_helper_fbdev_setup(&vbox->ddev, &vbox->fb_helper, |
80 | &vbox_fb_helper_funcs, 32, | |
81 | vbox->num_crtcs); | |
d4670909 HG |
82 | if (ret) |
83 | goto err_irq_fini; | |
1daddbc8 | 84 | |
01648890 | 85 | ret = drm_dev_register(&vbox->ddev, 0); |
d4670909 HG |
86 | if (ret) |
87 | goto err_fbdev_fini; | |
88 | ||
89 | return 0; | |
90 | ||
91 | err_fbdev_fini: | |
01648890 | 92 | vbox_fbdev_fini(vbox); |
d4670909 HG |
93 | err_irq_fini: |
94 | vbox_irq_fini(vbox); | |
95 | err_mode_fini: | |
01648890 | 96 | vbox_mode_fini(vbox); |
d4670909 HG |
97 | err_mm_fini: |
98 | vbox_mm_fini(vbox); | |
99 | err_hw_fini: | |
100 | vbox_hw_fini(vbox); | |
101 | err_pci_disable: | |
1ebafd15 | 102 | pci_disable_device(pdev); |
d4670909 | 103 | err_dev_put: |
01648890 | 104 | drm_dev_put(&vbox->ddev); |
1daddbc8 | 105 | return ret; |
dd55d44f HG |
106 | } |
107 | ||
108 | static void vbox_pci_remove(struct pci_dev *pdev) | |
109 | { | |
01648890 | 110 | struct vbox_private *vbox = pci_get_drvdata(pdev); |
dd55d44f | 111 | |
01648890 HG |
112 | drm_dev_unregister(&vbox->ddev); |
113 | vbox_fbdev_fini(vbox); | |
d4670909 | 114 | vbox_irq_fini(vbox); |
01648890 | 115 | vbox_mode_fini(vbox); |
d4670909 HG |
116 | vbox_mm_fini(vbox); |
117 | vbox_hw_fini(vbox); | |
01648890 | 118 | drm_dev_put(&vbox->ddev); |
dd55d44f HG |
119 | } |
120 | ||
dd55d44f HG |
121 | static int vbox_pm_suspend(struct device *dev) |
122 | { | |
01648890 | 123 | struct vbox_private *vbox = dev_get_drvdata(dev); |
dd55d44f HG |
124 | int error; |
125 | ||
438340aa | 126 | error = drm_mode_config_helper_suspend(&vbox->ddev); |
dd55d44f HG |
127 | if (error) |
128 | return error; | |
129 | ||
438340aa | 130 | pci_save_state(vbox->ddev.pdev); |
01648890 HG |
131 | pci_disable_device(vbox->ddev.pdev); |
132 | pci_set_power_state(vbox->ddev.pdev, PCI_D3hot); | |
dd55d44f HG |
133 | |
134 | return 0; | |
135 | } | |
136 | ||
137 | static int vbox_pm_resume(struct device *dev) | |
138 | { | |
01648890 | 139 | struct vbox_private *vbox = dev_get_drvdata(dev); |
dd55d44f | 140 | |
3498ea8b HG |
141 | if (pci_enable_device(vbox->ddev.pdev)) |
142 | return -EIO; | |
143 | ||
438340aa | 144 | return drm_mode_config_helper_resume(&vbox->ddev); |
dd55d44f HG |
145 | } |
146 | ||
147 | static int vbox_pm_freeze(struct device *dev) | |
148 | { | |
01648890 | 149 | struct vbox_private *vbox = dev_get_drvdata(dev); |
dd55d44f | 150 | |
438340aa | 151 | return drm_mode_config_helper_suspend(&vbox->ddev); |
dd55d44f HG |
152 | } |
153 | ||
154 | static int vbox_pm_thaw(struct device *dev) | |
155 | { | |
01648890 | 156 | struct vbox_private *vbox = dev_get_drvdata(dev); |
dd55d44f | 157 | |
438340aa | 158 | return drm_mode_config_helper_resume(&vbox->ddev); |
dd55d44f HG |
159 | } |
160 | ||
161 | static int vbox_pm_poweroff(struct device *dev) | |
162 | { | |
01648890 | 163 | struct vbox_private *vbox = dev_get_drvdata(dev); |
dd55d44f | 164 | |
438340aa | 165 | return drm_mode_config_helper_suspend(&vbox->ddev); |
dd55d44f HG |
166 | } |
167 | ||
168 | static const struct dev_pm_ops vbox_pm_ops = { | |
169 | .suspend = vbox_pm_suspend, | |
170 | .resume = vbox_pm_resume, | |
171 | .freeze = vbox_pm_freeze, | |
172 | .thaw = vbox_pm_thaw, | |
173 | .poweroff = vbox_pm_poweroff, | |
174 | .restore = vbox_pm_resume, | |
175 | }; | |
176 | ||
177 | static struct pci_driver vbox_pci_driver = { | |
178 | .name = DRIVER_NAME, | |
179 | .id_table = pciidlist, | |
180 | .probe = vbox_pci_probe, | |
181 | .remove = vbox_pci_remove, | |
182 | .driver.pm = &vbox_pm_ops, | |
183 | }; | |
184 | ||
185 | static const struct file_operations vbox_fops = { | |
186 | .owner = THIS_MODULE, | |
187 | .open = drm_open, | |
188 | .release = drm_release, | |
189 | .unlocked_ioctl = drm_ioctl, | |
190 | .mmap = vbox_mmap, | |
191 | .poll = drm_poll, | |
192 | #ifdef CONFIG_COMPAT | |
193 | .compat_ioctl = drm_compat_ioctl, | |
194 | #endif | |
195 | .read = drm_read, | |
196 | }; | |
197 | ||
198 | static int vbox_master_set(struct drm_device *dev, | |
199 | struct drm_file *file_priv, bool from_open) | |
200 | { | |
201 | struct vbox_private *vbox = dev->dev_private; | |
202 | ||
203 | /* | |
204 | * We do not yet know whether the new owner can handle hotplug, so we | |
205 | * do not advertise dynamic modes on the first query and send a | |
206 | * tentative hotplug notification after that to see if they query again. | |
207 | */ | |
208 | vbox->initial_mode_queried = false; | |
209 | ||
dd55d44f HG |
210 | return 0; |
211 | } | |
212 | ||
213 | static void vbox_master_drop(struct drm_device *dev, struct drm_file *file_priv) | |
214 | { | |
215 | struct vbox_private *vbox = dev->dev_private; | |
216 | ||
217 | /* See vbox_master_set() */ | |
218 | vbox->initial_mode_queried = false; | |
dd55d44f HG |
219 | } |
220 | ||
221 | static struct drm_driver driver = { | |
222 | .driver_features = | |
223 | DRIVER_MODESET | DRIVER_GEM | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | | |
438340aa | 224 | DRIVER_PRIME | DRIVER_ATOMIC, |
dd55d44f HG |
225 | .dev_priv_size = 0, |
226 | ||
e2c3860b | 227 | .lastclose = drm_fb_helper_lastclose, |
dd55d44f HG |
228 | .master_set = vbox_master_set, |
229 | .master_drop = vbox_master_drop, | |
dd55d44f HG |
230 | |
231 | .fops = &vbox_fops, | |
232 | .irq_handler = vbox_irq_handler, | |
233 | .name = DRIVER_NAME, | |
234 | .desc = DRIVER_DESC, | |
235 | .date = DRIVER_DATE, | |
236 | .major = DRIVER_MAJOR, | |
237 | .minor = DRIVER_MINOR, | |
238 | .patchlevel = DRIVER_PATCHLEVEL, | |
239 | ||
606f1fc5 | 240 | .gem_free_object_unlocked = vbox_gem_free_object, |
dd55d44f HG |
241 | .dumb_create = vbox_dumb_create, |
242 | .dumb_map_offset = vbox_dumb_mmap_offset, | |
243 | .dumb_destroy = drm_gem_dumb_destroy, | |
244 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | |
245 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
246 | .gem_prime_export = drm_gem_prime_export, | |
247 | .gem_prime_import = drm_gem_prime_import, | |
248 | .gem_prime_pin = vbox_gem_prime_pin, | |
249 | .gem_prime_unpin = vbox_gem_prime_unpin, | |
250 | .gem_prime_get_sg_table = vbox_gem_prime_get_sg_table, | |
251 | .gem_prime_import_sg_table = vbox_gem_prime_import_sg_table, | |
252 | .gem_prime_vmap = vbox_gem_prime_vmap, | |
253 | .gem_prime_vunmap = vbox_gem_prime_vunmap, | |
254 | .gem_prime_mmap = vbox_gem_prime_mmap, | |
255 | }; | |
256 | ||
257 | static int __init vbox_init(void) | |
258 | { | |
259 | #ifdef CONFIG_VGA_CONSOLE | |
260 | if (vgacon_text_force() && vbox_modeset == -1) | |
261 | return -EINVAL; | |
262 | #endif | |
263 | ||
264 | if (vbox_modeset == 0) | |
265 | return -EINVAL; | |
266 | ||
e6742e10 | 267 | return pci_register_driver(&vbox_pci_driver); |
dd55d44f HG |
268 | } |
269 | ||
270 | static void __exit vbox_exit(void) | |
271 | { | |
e6742e10 | 272 | pci_unregister_driver(&vbox_pci_driver); |
dd55d44f HG |
273 | } |
274 | ||
275 | module_init(vbox_init); | |
276 | module_exit(vbox_exit); | |
277 | ||
278 | MODULE_AUTHOR("Oracle Corporation"); | |
279 | MODULE_DESCRIPTION(DRIVER_DESC); | |
280 | MODULE_LICENSE("GPL and additional rights"); |