aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/vga
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-03-04 10:49:37 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2010-03-04 10:49:37 -0500
commit03a2c4d76c9e99b80d74ab8a4f344e135a5ae44b (patch)
tree7fd7940a4f87dc1ace1c1bdeb1fb0d90ac3beb13 /drivers/gpu/vga
parenta27341cd5fcb7cf2d2d4726e9f324009f7162c00 (diff)
parentd424b925f7092b9d95e0a8556872349abe79d9b6 (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/Kconfig11
-rw-r--r--drivers/gpu/vga/Makefile1
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c450
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
20config 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 @@
1obj-$(CONFIG_VGA_ARB) += vgaarb.o 1obj-$(CONFIG_VGA_ARB) += vgaarb.o
2obj-$(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
31struct 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
41static DEFINE_MUTEX(vgasr_mutex);
42
43struct 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
58static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv);
59static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv);
60
61/* only one switcheroo per system */
62static struct vgasr_priv vgasr_priv;
63
64int 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}
76EXPORT_SYMBOL(vga_switcheroo_register_handler);
77
78void vga_switcheroo_unregister_handler(void)
79{
80 mutex_lock(&vgasr_mutex);
81 vgasr_priv.handler = NULL;
82 mutex_unlock(&vgasr_mutex);
83}
84EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
85
86static 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
104int 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}
135EXPORT_SYMBOL(vga_switcheroo_register_client);
136
137void 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}
154EXPORT_SYMBOL(vga_switcheroo_unregister_client);
155
156void 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}
170EXPORT_SYMBOL(vga_switcheroo_client_fb_set);
171
172static 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
186static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)
187{
188 return single_open(file, vga_switcheroo_show, NULL);
189}
190
191static 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
202static 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
211static 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
260static ssize_t
261vga_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
360out:
361 mutex_unlock(&vgasr_mutex);
362 return cnt;
363}
364
365static 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
374static 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
386static 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;
405fail:
406 vga_switcheroo_debugfs_fini(priv);
407 return -1;
408}
409
410int 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;
445err:
446 mutex_unlock(&vgasr_mutex);
447 return err;
448}
449EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
450