diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-04 10:49:37 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-04 10:49:37 -0500 |
commit | 03a2c4d76c9e99b80d74ab8a4f344e135a5ae44b (patch) | |
tree | 7fd7940a4f87dc1ace1c1bdeb1fb0d90ac3beb13 /drivers/gpu/vga | |
parent | a27341cd5fcb7cf2d2d4726e9f324009f7162c00 (diff) | |
parent | d424b925f7092b9d95e0a8556872349abe79d9b6 (diff) |
Merge branch 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied/drm-2.6
* 'drm-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied/drm-2.6: (151 commits)
vga_switcheroo: disable default y by new rules.
drm/nouveau: fix *staging* driver build with switcheroo off.
drm/radeon: fix typo in Makefile
vga_switcheroo: fix build on platforms with no ACPI
drm/radeon: Fix printf type warning in 64bit system.
drm/radeon/kms: bump the KMS version number for square tiling support.
vga_switcheroo: initial implementation (v15)
drm/radeon/kms: do not disable audio engine twice
Revert "drm/radeon/kms: disable HDMI audio for now on rv710/rv730"
drm/radeon/kms: do not preset audio stuff and start timer when not using audio
drm/radeon: r100/r200 ums: block ability for userspace app to trash 0 page and beyond
drm/ttm: fix function prototype to match implementation
drm/radeon: use ALIGN instead of open coding it
drm/radeon/kms: initialize set_surface_reg reg for rs600 asic
drm/i915: Use a dmi quirk to skip a broken SDVO TV output.
drm/i915: enable/disable LVDS port at DPMS time
drm/i915: check for multiple write domains in pin_and_relocate
drm/i915: clean-up i915_gem_flush_gpu_write_domain
drm/i915: reuse i915_gpu_idle helper
drm/i915: ensure lru ordering of fence_list
...
Fixed trivial conflicts in drivers/gpu/vga/Kconfig
Diffstat (limited to 'drivers/gpu/vga')
-rw-r--r-- | drivers/gpu/vga/Kconfig | 11 | ||||
-rw-r--r-- | drivers/gpu/vga/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/vga/vga_switcheroo.c | 450 |
3 files changed, 462 insertions, 0 deletions
diff --git a/drivers/gpu/vga/Kconfig b/drivers/gpu/vga/Kconfig index 0920492cea0a..61ab4daf0bbb 100644 --- a/drivers/gpu/vga/Kconfig +++ b/drivers/gpu/vga/Kconfig | |||
@@ -16,3 +16,14 @@ config VGA_ARB_MAX_GPUS | |||
16 | help | 16 | help |
17 | Reserves space in the kernel to maintain resource locking for | 17 | Reserves space in the kernel to maintain resource locking for |
18 | multiple GPUS. The overhead for each GPU is very small. | 18 | multiple GPUS. The overhead for each GPU is very small. |
19 | |||
20 | config VGA_SWITCHEROO | ||
21 | bool "Laptop Hybrid Grapics - GPU switching support" | ||
22 | depends on X86 | ||
23 | depends on ACPI | ||
24 | help | ||
25 | Many laptops released in 2008/9/10 have two gpus with a multiplxer | ||
26 | to switch between them. This adds support for dynamic switching when | ||
27 | X isn't running and delayed switching until the next logoff. This | ||
28 | features is called hybrid graphics, ATI PowerXpress, and Nvidia | ||
29 | HybridPower. | ||
diff --git a/drivers/gpu/vga/Makefile b/drivers/gpu/vga/Makefile index 7cc8c1ed645b..14ca30b75d0a 100644 --- a/drivers/gpu/vga/Makefile +++ b/drivers/gpu/vga/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-$(CONFIG_VGA_ARB) += vgaarb.o | 1 | obj-$(CONFIG_VGA_ARB) += vgaarb.o |
2 | obj-$(CONFIG_VGA_SWITCHEROO) += vga_switcheroo.o | ||
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c new file mode 100644 index 000000000000..d6d1149d525d --- /dev/null +++ b/drivers/gpu/vga/vga_switcheroo.c | |||
@@ -0,0 +1,450 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 Red Hat Inc. | ||
3 | * Author : Dave Airlie <airlied@redhat.com> | ||
4 | * | ||
5 | * | ||
6 | * Licensed under GPLv2 | ||
7 | * | ||
8 | * vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs | ||
9 | |||
10 | Switcher interface - methods require for ATPX and DCM | ||
11 | - switchto - this throws the output MUX switch | ||
12 | - discrete_set_power - sets the power state for the discrete card | ||
13 | |||
14 | GPU driver interface | ||
15 | - set_gpu_state - this should do the equiv of s/r for the card | ||
16 | - this should *not* set the discrete power state | ||
17 | - switch_check - check if the device is in a position to switch now | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/dmi.h> | ||
22 | #include <linux/seq_file.h> | ||
23 | #include <linux/uaccess.h> | ||
24 | #include <linux/fs.h> | ||
25 | #include <linux/debugfs.h> | ||
26 | #include <linux/fb.h> | ||
27 | |||
28 | #include <linux/pci.h> | ||
29 | #include <linux/vga_switcheroo.h> | ||
30 | |||
31 | struct vga_switcheroo_client { | ||
32 | struct pci_dev *pdev; | ||
33 | struct fb_info *fb_info; | ||
34 | int pwr_state; | ||
35 | void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state); | ||
36 | bool (*can_switch)(struct pci_dev *pdev); | ||
37 | int id; | ||
38 | bool active; | ||
39 | }; | ||
40 | |||
41 | static DEFINE_MUTEX(vgasr_mutex); | ||
42 | |||
43 | struct vgasr_priv { | ||
44 | |||
45 | bool active; | ||
46 | bool delayed_switch_active; | ||
47 | enum vga_switcheroo_client_id delayed_client_id; | ||
48 | |||
49 | struct dentry *debugfs_root; | ||
50 | struct dentry *switch_file; | ||
51 | |||
52 | int registered_clients; | ||
53 | struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS]; | ||
54 | |||
55 | struct vga_switcheroo_handler *handler; | ||
56 | }; | ||
57 | |||
58 | static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); | ||
59 | static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); | ||
60 | |||
61 | /* only one switcheroo per system */ | ||
62 | static struct vgasr_priv vgasr_priv; | ||
63 | |||
64 | int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) | ||
65 | { | ||
66 | mutex_lock(&vgasr_mutex); | ||
67 | if (vgasr_priv.handler) { | ||
68 | mutex_unlock(&vgasr_mutex); | ||
69 | return -EINVAL; | ||
70 | } | ||
71 | |||
72 | vgasr_priv.handler = handler; | ||
73 | mutex_unlock(&vgasr_mutex); | ||
74 | return 0; | ||
75 | } | ||
76 | EXPORT_SYMBOL(vga_switcheroo_register_handler); | ||
77 | |||
78 | void vga_switcheroo_unregister_handler(void) | ||
79 | { | ||
80 | mutex_lock(&vgasr_mutex); | ||
81 | vgasr_priv.handler = NULL; | ||
82 | mutex_unlock(&vgasr_mutex); | ||
83 | } | ||
84 | EXPORT_SYMBOL(vga_switcheroo_unregister_handler); | ||
85 | |||
86 | static void vga_switcheroo_enable(void) | ||
87 | { | ||
88 | int i; | ||
89 | int ret; | ||
90 | /* call the handler to init */ | ||
91 | vgasr_priv.handler->init(); | ||
92 | |||
93 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
94 | ret = vgasr_priv.handler->get_client_id(vgasr_priv.clients[i].pdev); | ||
95 | if (ret < 0) | ||
96 | return; | ||
97 | |||
98 | vgasr_priv.clients[i].id = ret; | ||
99 | } | ||
100 | vga_switcheroo_debugfs_init(&vgasr_priv); | ||
101 | vgasr_priv.active = true; | ||
102 | } | ||
103 | |||
104 | int vga_switcheroo_register_client(struct pci_dev *pdev, | ||
105 | void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state), | ||
106 | bool (*can_switch)(struct pci_dev *pdev)) | ||
107 | { | ||
108 | int index; | ||
109 | |||
110 | mutex_lock(&vgasr_mutex); | ||
111 | /* don't do IGD vs DIS here */ | ||
112 | if (vgasr_priv.registered_clients & 1) | ||
113 | index = 1; | ||
114 | else | ||
115 | index = 0; | ||
116 | |||
117 | vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON; | ||
118 | vgasr_priv.clients[index].pdev = pdev; | ||
119 | vgasr_priv.clients[index].set_gpu_state = set_gpu_state; | ||
120 | vgasr_priv.clients[index].can_switch = can_switch; | ||
121 | vgasr_priv.clients[index].id = -1; | ||
122 | if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) | ||
123 | vgasr_priv.clients[index].active = true; | ||
124 | |||
125 | vgasr_priv.registered_clients |= (1 << index); | ||
126 | |||
127 | /* if we get two clients + handler */ | ||
128 | if (vgasr_priv.registered_clients == 0x3 && vgasr_priv.handler) { | ||
129 | printk(KERN_INFO "vga_switcheroo: enabled\n"); | ||
130 | vga_switcheroo_enable(); | ||
131 | } | ||
132 | mutex_unlock(&vgasr_mutex); | ||
133 | return 0; | ||
134 | } | ||
135 | EXPORT_SYMBOL(vga_switcheroo_register_client); | ||
136 | |||
137 | void vga_switcheroo_unregister_client(struct pci_dev *pdev) | ||
138 | { | ||
139 | int i; | ||
140 | |||
141 | mutex_lock(&vgasr_mutex); | ||
142 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
143 | if (vgasr_priv.clients[i].pdev == pdev) { | ||
144 | vgasr_priv.registered_clients &= ~(1 << i); | ||
145 | break; | ||
146 | } | ||
147 | } | ||
148 | |||
149 | printk(KERN_INFO "vga_switcheroo: disabled\n"); | ||
150 | vga_switcheroo_debugfs_fini(&vgasr_priv); | ||
151 | vgasr_priv.active = false; | ||
152 | mutex_unlock(&vgasr_mutex); | ||
153 | } | ||
154 | EXPORT_SYMBOL(vga_switcheroo_unregister_client); | ||
155 | |||
156 | void vga_switcheroo_client_fb_set(struct pci_dev *pdev, | ||
157 | struct fb_info *info) | ||
158 | { | ||
159 | int i; | ||
160 | |||
161 | mutex_lock(&vgasr_mutex); | ||
162 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
163 | if (vgasr_priv.clients[i].pdev == pdev) { | ||
164 | vgasr_priv.clients[i].fb_info = info; | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | mutex_unlock(&vgasr_mutex); | ||
169 | } | ||
170 | EXPORT_SYMBOL(vga_switcheroo_client_fb_set); | ||
171 | |||
172 | static int vga_switcheroo_show(struct seq_file *m, void *v) | ||
173 | { | ||
174 | int i; | ||
175 | mutex_lock(&vgasr_mutex); | ||
176 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
177 | seq_printf(m, "%d:%c:%s:%s\n", i, | ||
178 | vgasr_priv.clients[i].active ? '+' : ' ', | ||
179 | vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off", | ||
180 | pci_name(vgasr_priv.clients[i].pdev)); | ||
181 | } | ||
182 | mutex_unlock(&vgasr_mutex); | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file) | ||
187 | { | ||
188 | return single_open(file, vga_switcheroo_show, NULL); | ||
189 | } | ||
190 | |||
191 | static int vga_switchon(struct vga_switcheroo_client *client) | ||
192 | { | ||
193 | int ret; | ||
194 | |||
195 | ret = vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); | ||
196 | /* call the driver callback to turn on device */ | ||
197 | client->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); | ||
198 | client->pwr_state = VGA_SWITCHEROO_ON; | ||
199 | return 0; | ||
200 | } | ||
201 | |||
202 | static int vga_switchoff(struct vga_switcheroo_client *client) | ||
203 | { | ||
204 | /* call the driver callback to turn off device */ | ||
205 | client->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); | ||
206 | vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); | ||
207 | client->pwr_state = VGA_SWITCHEROO_OFF; | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int vga_switchto(struct vga_switcheroo_client *new_client) | ||
212 | { | ||
213 | int ret; | ||
214 | int i; | ||
215 | struct vga_switcheroo_client *active = NULL; | ||
216 | |||
217 | if (new_client->active == true) | ||
218 | return 0; | ||
219 | |||
220 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
221 | if (vgasr_priv.clients[i].active == true) { | ||
222 | active = &vgasr_priv.clients[i]; | ||
223 | break; | ||
224 | } | ||
225 | } | ||
226 | if (!active) | ||
227 | return 0; | ||
228 | |||
229 | /* power up the first device */ | ||
230 | ret = pci_enable_device(new_client->pdev); | ||
231 | if (ret) | ||
232 | return ret; | ||
233 | |||
234 | if (new_client->pwr_state == VGA_SWITCHEROO_OFF) | ||
235 | vga_switchon(new_client); | ||
236 | |||
237 | /* swap shadow resource to denote boot VGA device has changed so X starts on new device */ | ||
238 | active->active = false; | ||
239 | |||
240 | active->pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_ROM_SHADOW; | ||
241 | new_client->pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; | ||
242 | |||
243 | if (new_client->fb_info) { | ||
244 | struct fb_event event; | ||
245 | event.info = new_client->fb_info; | ||
246 | fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); | ||
247 | } | ||
248 | |||
249 | ret = vgasr_priv.handler->switchto(new_client->id); | ||
250 | if (ret) | ||
251 | return ret; | ||
252 | |||
253 | if (active->pwr_state == VGA_SWITCHEROO_ON) | ||
254 | vga_switchoff(active); | ||
255 | |||
256 | new_client->active = true; | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | static ssize_t | ||
261 | vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | ||
262 | size_t cnt, loff_t *ppos) | ||
263 | { | ||
264 | char usercmd[64]; | ||
265 | const char *pdev_name; | ||
266 | int i, ret; | ||
267 | bool delay = false, can_switch; | ||
268 | int client_id = -1; | ||
269 | struct vga_switcheroo_client *client = NULL; | ||
270 | |||
271 | if (cnt > 63) | ||
272 | cnt = 63; | ||
273 | |||
274 | if (copy_from_user(usercmd, ubuf, cnt)) | ||
275 | return -EFAULT; | ||
276 | |||
277 | mutex_lock(&vgasr_mutex); | ||
278 | |||
279 | if (!vgasr_priv.active) | ||
280 | return -EINVAL; | ||
281 | |||
282 | /* pwr off the device not in use */ | ||
283 | if (strncmp(usercmd, "OFF", 3) == 0) { | ||
284 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
285 | if (vgasr_priv.clients[i].active) | ||
286 | continue; | ||
287 | if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_ON) | ||
288 | vga_switchoff(&vgasr_priv.clients[i]); | ||
289 | } | ||
290 | goto out; | ||
291 | } | ||
292 | /* pwr on the device not in use */ | ||
293 | if (strncmp(usercmd, "ON", 2) == 0) { | ||
294 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
295 | if (vgasr_priv.clients[i].active) | ||
296 | continue; | ||
297 | if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_OFF) | ||
298 | vga_switchon(&vgasr_priv.clients[i]); | ||
299 | } | ||
300 | goto out; | ||
301 | } | ||
302 | |||
303 | /* request a delayed switch - test can we switch now */ | ||
304 | if (strncmp(usercmd, "DIGD", 4) == 0) { | ||
305 | client_id = VGA_SWITCHEROO_IGD; | ||
306 | delay = true; | ||
307 | } | ||
308 | |||
309 | if (strncmp(usercmd, "DDIS", 4) == 0) { | ||
310 | client_id = VGA_SWITCHEROO_DIS; | ||
311 | delay = true; | ||
312 | } | ||
313 | |||
314 | if (strncmp(usercmd, "IGD", 3) == 0) | ||
315 | client_id = VGA_SWITCHEROO_IGD; | ||
316 | |||
317 | if (strncmp(usercmd, "DIS", 3) == 0) | ||
318 | client_id = VGA_SWITCHEROO_DIS; | ||
319 | |||
320 | if (client_id == -1) | ||
321 | goto out; | ||
322 | |||
323 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
324 | if (vgasr_priv.clients[i].id == client_id) { | ||
325 | client = &vgasr_priv.clients[i]; | ||
326 | break; | ||
327 | } | ||
328 | } | ||
329 | |||
330 | vgasr_priv.delayed_switch_active = false; | ||
331 | /* okay we want a switch - test if devices are willing to switch */ | ||
332 | can_switch = true; | ||
333 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
334 | can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); | ||
335 | if (can_switch == false) { | ||
336 | printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); | ||
337 | break; | ||
338 | } | ||
339 | } | ||
340 | |||
341 | if (can_switch == false && delay == false) | ||
342 | goto out; | ||
343 | |||
344 | if (can_switch == true) { | ||
345 | pdev_name = pci_name(client->pdev); | ||
346 | ret = vga_switchto(client); | ||
347 | if (ret) | ||
348 | printk(KERN_ERR "vga_switcheroo: switching failed %d\n", ret); | ||
349 | } else { | ||
350 | printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id); | ||
351 | vgasr_priv.delayed_switch_active = true; | ||
352 | vgasr_priv.delayed_client_id = client_id; | ||
353 | |||
354 | /* we should at least power up the card to | ||
355 | make the switch faster */ | ||
356 | if (client->pwr_state == VGA_SWITCHEROO_OFF) | ||
357 | vga_switchon(client); | ||
358 | } | ||
359 | |||
360 | out: | ||
361 | mutex_unlock(&vgasr_mutex); | ||
362 | return cnt; | ||
363 | } | ||
364 | |||
365 | static const struct file_operations vga_switcheroo_debugfs_fops = { | ||
366 | .owner = THIS_MODULE, | ||
367 | .open = vga_switcheroo_debugfs_open, | ||
368 | .write = vga_switcheroo_debugfs_write, | ||
369 | .read = seq_read, | ||
370 | .llseek = seq_lseek, | ||
371 | .release = single_release, | ||
372 | }; | ||
373 | |||
374 | static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv) | ||
375 | { | ||
376 | if (priv->switch_file) { | ||
377 | debugfs_remove(priv->switch_file); | ||
378 | priv->switch_file = NULL; | ||
379 | } | ||
380 | if (priv->debugfs_root) { | ||
381 | debugfs_remove(priv->debugfs_root); | ||
382 | priv->debugfs_root = NULL; | ||
383 | } | ||
384 | } | ||
385 | |||
386 | static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv) | ||
387 | { | ||
388 | /* already initialised */ | ||
389 | if (priv->debugfs_root) | ||
390 | return 0; | ||
391 | priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL); | ||
392 | |||
393 | if (!priv->debugfs_root) { | ||
394 | printk(KERN_ERR "vga_switcheroo: Cannot create /sys/kernel/debug/vgaswitcheroo\n"); | ||
395 | goto fail; | ||
396 | } | ||
397 | |||
398 | priv->switch_file = debugfs_create_file("switch", 0644, | ||
399 | priv->debugfs_root, NULL, &vga_switcheroo_debugfs_fops); | ||
400 | if (!priv->switch_file) { | ||
401 | printk(KERN_ERR "vga_switcheroo: cannot create /sys/kernel/debug/vgaswitcheroo/switch\n"); | ||
402 | goto fail; | ||
403 | } | ||
404 | return 0; | ||
405 | fail: | ||
406 | vga_switcheroo_debugfs_fini(priv); | ||
407 | return -1; | ||
408 | } | ||
409 | |||
410 | int vga_switcheroo_process_delayed_switch(void) | ||
411 | { | ||
412 | struct vga_switcheroo_client *client = NULL; | ||
413 | const char *pdev_name; | ||
414 | bool can_switch = true; | ||
415 | int i; | ||
416 | int ret; | ||
417 | int err = -EINVAL; | ||
418 | |||
419 | mutex_lock(&vgasr_mutex); | ||
420 | if (!vgasr_priv.delayed_switch_active) | ||
421 | goto err; | ||
422 | |||
423 | printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); | ||
424 | |||
425 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
426 | if (vgasr_priv.clients[i].id == vgasr_priv.delayed_client_id) | ||
427 | client = &vgasr_priv.clients[i]; | ||
428 | can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); | ||
429 | if (can_switch == false) { | ||
430 | printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); | ||
431 | break; | ||
432 | } | ||
433 | } | ||
434 | |||
435 | if (can_switch == false || client == NULL) | ||
436 | goto err; | ||
437 | |||
438 | pdev_name = pci_name(client->pdev); | ||
439 | ret = vga_switchto(client); | ||
440 | if (ret) | ||
441 | printk(KERN_ERR "vga_switcheroo: delayed switching failed %d\n", ret); | ||
442 | |||
443 | vgasr_priv.delayed_switch_active = false; | ||
444 | err = 0; | ||
445 | err: | ||
446 | mutex_unlock(&vgasr_mutex); | ||
447 | return err; | ||
448 | } | ||
449 | EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); | ||
450 | |||