aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Airlie <airlied@linux.ie>2010-02-01 00:38:10 -0500
committerDave Airlie <airlied@redhat.com>2010-03-01 01:20:37 -0500
commit6a9ee8af344e3bd7dbd61e67037096cdf7f83289 (patch)
tree07cdb493a790cf45bc473f2fc2ea1b9a166d5191
parent9fd1de52945e06ed88a440c99ca92dab74b9b33c (diff)
vga_switcheroo: initial implementation (v15)
Many new laptops now come with 2 gpus, one to be used for low power modes and one for gaming/on-ac applications. These GPUs are typically wired to the laptop panel and VGA ports via a multiplexer unit which is controlled via ACPI methods. 4 combinations of systems typically exist - with 2 ACPI methods. Intel/ATI - Lenovo W500/T500 - use ATPX ACPI method ATI/ATI - some ASUS - use ATPX ACPI Method Intel/Nvidia - - use _DSM ACPI method Nvidia/Nvidia - - use _DSM ACPI method. TODO: This patch adds support for the ATPX method and initial bits for the _DSM methods that need to written by someone with access to the hardware. Add a proper non-debugfs interface - need to get some proper testing first. v2: add power up/down support for both devices on W500 puts i915/radeon into D3 and cuts power to radeon. v3: redo probing methods, no DMI list, drm devices call to register with switcheroo, it tries to find an ATPX method on any device and once there is two devices + ATPX it inits the switcher. v4: ATPX msg handling using buffers - should work on more machines v5: rearchitect after more mjg59 discussion - move ATPX handling to radeon driver. v6: add file headers + initial nouveau bits (to be filled out). v7: merge delayed switcher code. v8: avoid suspend/resume of gpu that is off v9: rearchitect - mjg59 is always right. - move all ATPX code to radeon, should allow simpler DSM also proper ATRM handling v10: add ATRM support for radeon BIOS, add mutex to lock vgasr_priv v11: fix bug in resuming Intel for 2nd time. v12: start fixing up nvidia code blindly. v13: blindly guess at finishing nvidia code v14: remove radeon audio hacks - fix up intel resume more like upstream v15: clean up printks + remove unnecessary igd/dis pointers mount debugfs /sys/kernel/debug/vgaswitcheroo/switch - should exist if ATPX detected + 2 cards. DIS - immediate change to discrete IGD - immediate change to IGD DDIS - delayed change to discrete DIGD - delayed change to IGD ON - turn on not in use OFF - turn off not in use Tested on W500 (Intel/ATI) and T500 (Intel/ATI) Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c35
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c4
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h2
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c160
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.c9
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h19
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c32
-rw-r--r--drivers/gpu/drm/radeon/Makefile3
-rw-r--r--drivers/gpu/drm/radeon/radeon.h8
-rw-r--r--drivers/gpu/drm/radeon/radeon_atpx_handler.c258
-rw-r--r--drivers/gpu/drm/radeon/radeon_bios.c44
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c40
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.h3
-rw-r--r--drivers/gpu/drm/radeon/radeon_fb.c3
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c3
-rw-r--r--drivers/gpu/vga/Kconfig13
-rw-r--r--drivers/gpu/vga/Makefile1
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c453
-rw-r--r--drivers/video/console/fbcon.c18
-rw-r--r--include/linux/fb.h2
-rw-r--r--include/linux/vga_switcheroo.h58
24 files changed, 1104 insertions, 70 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 2307f98349f7..42ca07f04a21 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -35,6 +35,7 @@
35#include "i915_drv.h" 35#include "i915_drv.h"
36#include "i915_trace.h" 36#include "i915_trace.h"
37#include <linux/vgaarb.h> 37#include <linux/vgaarb.h>
38#include <linux/vga_switcheroo.h>
38 39
39/* Really want an OS-independent resettable timer. Would like to have 40/* Really want an OS-independent resettable timer. Would like to have
40 * this loop run for (eg) 3 sec, but have the timer reset every time 41 * this loop run for (eg) 3 sec, but have the timer reset every time
@@ -1199,6 +1200,32 @@ static unsigned int i915_vga_set_decode(void *cookie, bool state)
1199 return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; 1200 return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
1200} 1201}
1201 1202
1203static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
1204{
1205 struct drm_device *dev = pci_get_drvdata(pdev);
1206 pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
1207 if (state == VGA_SWITCHEROO_ON) {
1208 printk(KERN_INFO "i915: switched off\n");
1209 /* i915 resume handler doesn't set to D0 */
1210 pci_set_power_state(dev->pdev, PCI_D0);
1211 i915_resume(dev);
1212 } else {
1213 printk(KERN_ERR "i915: switched off\n");
1214 i915_suspend(dev, pmm);
1215 }
1216}
1217
1218static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
1219{
1220 struct drm_device *dev = pci_get_drvdata(pdev);
1221 bool can_switch;
1222
1223 spin_lock(&dev->count_lock);
1224 can_switch = (dev->open_count == 0);
1225 spin_unlock(&dev->count_lock);
1226 return can_switch;
1227}
1228
1202static int i915_load_modeset_init(struct drm_device *dev, 1229static int i915_load_modeset_init(struct drm_device *dev,
1203 unsigned long prealloc_start, 1230 unsigned long prealloc_start,
1204 unsigned long prealloc_size, 1231 unsigned long prealloc_size,
@@ -1260,6 +1287,12 @@ static int i915_load_modeset_init(struct drm_device *dev,
1260 if (ret) 1287 if (ret)
1261 goto destroy_ringbuffer; 1288 goto destroy_ringbuffer;
1262 1289
1290 ret = vga_switcheroo_register_client(dev->pdev,
1291 i915_switcheroo_set_state,
1292 i915_switcheroo_can_switch);
1293 if (ret)
1294 goto destroy_ringbuffer;
1295
1263 intel_modeset_init(dev); 1296 intel_modeset_init(dev);
1264 1297
1265 ret = drm_irq_install(dev); 1298 ret = drm_irq_install(dev);
@@ -1544,6 +1577,7 @@ int i915_driver_unload(struct drm_device *dev)
1544 dev_priv->child_dev_num = 0; 1577 dev_priv->child_dev_num = 0;
1545 } 1578 }
1546 drm_irq_uninstall(dev); 1579 drm_irq_uninstall(dev);
1580 vga_switcheroo_unregister_client(dev->pdev);
1547 vga_client_register(dev->pdev, NULL, NULL, NULL); 1581 vga_client_register(dev->pdev, NULL, NULL, NULL);
1548 } 1582 }
1549 1583
@@ -1611,6 +1645,7 @@ void i915_driver_lastclose(struct drm_device * dev)
1611 1645
1612 if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) { 1646 if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) {
1613 drm_fb_helper_restore(); 1647 drm_fb_helper_restore();
1648 vga_switcheroo_process_delayed_switch();
1614 return; 1649 return;
1615 } 1650 }
1616 1651
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index cf4cb3e9a0c2..fd739efe73ce 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -201,7 +201,7 @@ static int i915_drm_freeze(struct drm_device *dev)
201 return 0; 201 return 0;
202} 202}
203 203
204static int i915_suspend(struct drm_device *dev, pm_message_t state) 204int i915_suspend(struct drm_device *dev, pm_message_t state)
205{ 205{
206 int error; 206 int error;
207 207
@@ -255,7 +255,7 @@ static int i915_drm_thaw(struct drm_device *dev)
255 return error; 255 return error;
256} 256}
257 257
258static int i915_resume(struct drm_device *dev) 258int i915_resume(struct drm_device *dev)
259{ 259{
260 if (pci_enable_device(dev->pdev)) 260 if (pci_enable_device(dev->pdev))
261 return -EIO; 261 return -EIO;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b99b6a841d95..d77e56651352 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -736,6 +736,8 @@ extern unsigned int i915_fbpercrtc;
736extern unsigned int i915_powersave; 736extern unsigned int i915_powersave;
737extern unsigned int i915_lvds_downclock; 737extern unsigned int i915_lvds_downclock;
738 738
739extern int i915_suspend(struct drm_device *dev, pm_message_t state);
740extern int i915_resume(struct drm_device *dev);
739extern void i915_save_display(struct drm_device *dev); 741extern void i915_save_display(struct drm_device *dev);
740extern void i915_restore_display(struct drm_device *dev); 742extern void i915_restore_display(struct drm_device *dev);
741extern int i915_master_create(struct drm_device *dev, struct drm_master *master); 743extern int i915_master_create(struct drm_device *dev, struct drm_master *master);
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index aaabbcbe5905..8cd791dc5b29 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -35,6 +35,7 @@
35#include <linux/delay.h> 35#include <linux/delay.h>
36#include <linux/fb.h> 36#include <linux/fb.h>
37#include <linux/init.h> 37#include <linux/init.h>
38#include <linux/vga_switcheroo.h>
38 39
39#include "drmP.h" 40#include "drmP.h"
40#include "drm.h" 41#include "drm.h"
@@ -235,6 +236,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
235 obj_priv->gtt_offset, fbo); 236 obj_priv->gtt_offset, fbo);
236 237
237 mutex_unlock(&dev->struct_mutex); 238 mutex_unlock(&dev->struct_mutex);
239 vga_switcheroo_client_fb_set(dev->pdev, info);
238 return 0; 240 return 0;
239 241
240out_unpin: 242out_unpin:
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index 48227e744753..0e0730a53137 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -11,6 +11,8 @@
11#include "nouveau_drm.h" 11#include "nouveau_drm.h"
12#include "nv50_display.h" 12#include "nv50_display.h"
13 13
14#include <linux/vga_switcheroo.h>
15
14#define NOUVEAU_DSM_SUPPORTED 0x00 16#define NOUVEAU_DSM_SUPPORTED 0x00
15#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 17#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
16 18
@@ -28,31 +30,30 @@
28#define NOUVEAU_DSM_POWER_SPEED 0x01 30#define NOUVEAU_DSM_POWER_SPEED 0x01
29#define NOUVEAU_DSM_POWER_STAMINA 0x02 31#define NOUVEAU_DSM_POWER_STAMINA 0x02
30 32
31static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) 33static struct nouveau_dsm_priv {
32{ 34 bool dsm_detected;
33 static char muid[] = { 35 acpi_handle dhandle;
34 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 36 acpi_handle dsm_handle;
35 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 37} nouveau_dsm_priv;
36 }; 38
39static const char nouveau_dsm_muid[] = {
40 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
41 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
42};
37 43
38 struct pci_dev *pdev = dev->pdev; 44static int nouveau_dsm(acpi_handle handle, int func, int arg, int *result)
39 struct acpi_handle *handle; 45{
40 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 46 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
41 struct acpi_object_list input; 47 struct acpi_object_list input;
42 union acpi_object params[4]; 48 union acpi_object params[4];
43 union acpi_object *obj; 49 union acpi_object *obj;
44 int err; 50 int err;
45 51
46 handle = DEVICE_ACPI_HANDLE(&pdev->dev);
47
48 if (!handle)
49 return -ENODEV;
50
51 input.count = 4; 52 input.count = 4;
52 input.pointer = params; 53 input.pointer = params;
53 params[0].type = ACPI_TYPE_BUFFER; 54 params[0].type = ACPI_TYPE_BUFFER;
54 params[0].buffer.length = sizeof(muid); 55 params[0].buffer.length = sizeof(nouveau_dsm_muid);
55 params[0].buffer.pointer = (char *)muid; 56 params[0].buffer.pointer = (char *)nouveau_dsm_muid;
56 params[1].type = ACPI_TYPE_INTEGER; 57 params[1].type = ACPI_TYPE_INTEGER;
57 params[1].integer.value = 0x00000102; 58 params[1].integer.value = 0x00000102;
58 params[2].type = ACPI_TYPE_INTEGER; 59 params[2].type = ACPI_TYPE_INTEGER;
@@ -62,7 +63,7 @@ static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
62 63
63 err = acpi_evaluate_object(handle, "_DSM", &input, &output); 64 err = acpi_evaluate_object(handle, "_DSM", &input, &output);
64 if (err) { 65 if (err) {
65 NV_INFO(dev, "failed to evaluate _DSM: %d\n", err); 66 printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);
66 return err; 67 return err;
67 } 68 }
68 69
@@ -86,40 +87,119 @@ static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
86 return 0; 87 return 0;
87} 88}
88 89
89int nouveau_hybrid_setup(struct drm_device *dev) 90static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id)
90{ 91{
91 int result; 92 return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL);
92 93}
93 if (nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STATE, 94
94 &result)) 95static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state)
95 return -ENODEV; 96{
96 97 int arg;
97 NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result); 98 if (state == VGA_SWITCHEROO_ON)
98 99 arg = NOUVEAU_DSM_POWER_SPEED;
99 if (result) { /* Ensure that the external GPU is enabled */ 100 else
100 nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL); 101 arg = NOUVEAU_DSM_POWER_STAMINA;
101 nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED, 102 nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL);
102 NULL); 103 return 0;
103 } else { /* Stamina mode - disable the external GPU */ 104}
104 nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA, 105
105 NULL); 106static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id)
106 nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA, 107{
107 NULL); 108 if (id == VGA_SWITCHEROO_IGD)
108 } 109 return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_STAMINA);
110 else
111 return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_SPEED);
112}
109 113
114static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
115 enum vga_switcheroo_state state)
116{
117 if (id == VGA_SWITCHEROO_IGD)
118 return 0;
119
120 return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dsm_handle, state);
121}
122
123static int nouveau_dsm_init(void)
124{
110 return 0; 125 return 0;
111} 126}
112 127
113bool nouveau_dsm_probe(struct drm_device *dev) 128static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
114{ 129{
115 int support = 0; 130 if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
131 return VGA_SWITCHEROO_IGD;
132 else
133 return VGA_SWITCHEROO_DIS;
134}
135
136static struct vga_switcheroo_handler nouveau_dsm_handler = {
137 .switchto = nouveau_dsm_switchto,
138 .power_state = nouveau_dsm_power_state,
139 .init = nouveau_dsm_init,
140 .get_client_id = nouveau_dsm_get_client_id,
141};
116 142
117 if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED, 143static bool nouveau_dsm_pci_probe(struct pci_dev *pdev)
118 NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support)) 144{
145 acpi_handle dhandle, nvidia_handle;
146 acpi_status status;
147 int ret;
148 uint32_t result;
149
150 dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
151 if (!dhandle)
152 return false;
153 status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle);
154 if (ACPI_FAILURE(status)) {
119 return false; 155 return false;
156 }
120 157
121 if (!support) 158 ret= nouveau_dsm(nvidia_handle, NOUVEAU_DSM_SUPPORTED,
159 NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result);
160 if (ret < 0)
122 return false; 161 return false;
123 162
163 nouveau_dsm_priv.dhandle = dhandle;
164 nouveau_dsm_priv.dsm_handle = nvidia_handle;
124 return true; 165 return true;
125} 166}
167
168static bool nouveau_dsm_detect(void)
169{
170 char acpi_method_name[255] = { 0 };
171 struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
172 struct pci_dev *pdev = NULL;
173 int has_dsm = 0;
174 int vga_count = 0;
175 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
176 vga_count++;
177
178 has_dsm |= (nouveau_dsm_pci_probe(pdev) == true);
179 }
180
181 if (vga_count == 2 && has_dsm) {
182 acpi_get_name(nouveau_dsm_priv.dsm_handle, ACPI_FULL_PATHNAME, &buffer);
183 printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
184 acpi_method_name);
185 nouveau_dsm_priv.dsm_detected = true;
186 return true;
187 }
188 return false;
189}
190
191void nouveau_register_dsm_handler(void)
192{
193 bool r;
194
195 r = nouveau_dsm_detect();
196 if (!r)
197 return;
198
199 vga_switcheroo_register_handler(&nouveau_dsm_handler);
200}
201
202void nouveau_unregister_dsm_handler(void)
203{
204 vga_switcheroo_unregister_handler();
205}
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
index da3b93b84502..f83ec65addba 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
@@ -135,7 +135,7 @@ nouveau_pci_remove(struct pci_dev *pdev)
135 drm_put_dev(dev); 135 drm_put_dev(dev);
136} 136}
137 137
138static int 138int
139nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) 139nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
140{ 140{
141 struct drm_device *dev = pci_get_drvdata(pdev); 141 struct drm_device *dev = pci_get_drvdata(pdev);
@@ -233,7 +233,7 @@ out_abort:
233 return ret; 233 return ret;
234} 234}
235 235
236static int 236int
237nouveau_pci_resume(struct pci_dev *pdev) 237nouveau_pci_resume(struct pci_dev *pdev)
238{ 238{
239 struct drm_device *dev = pci_get_drvdata(pdev); 239 struct drm_device *dev = pci_get_drvdata(pdev);
@@ -402,8 +402,10 @@ static int __init nouveau_init(void)
402 nouveau_modeset = 1; 402 nouveau_modeset = 1;
403 } 403 }
404 404
405 if (nouveau_modeset == 1) 405 if (nouveau_modeset == 1) {
406 driver.driver_features |= DRIVER_MODESET; 406 driver.driver_features |= DRIVER_MODESET;
407 nouveau_register_dsm_handler();
408 }
407 409
408 return drm_init(&driver); 410 return drm_init(&driver);
409} 411}
@@ -411,6 +413,7 @@ static int __init nouveau_init(void)
411static void __exit nouveau_exit(void) 413static void __exit nouveau_exit(void)
412{ 414{
413 drm_exit(&driver); 415 drm_exit(&driver);
416 nouveau_unregister_dsm_handler();
414} 417}
415 418
416module_init(nouveau_init); 419module_init(nouveau_init);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 1c15ef37b71c..85c05feab4f0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -614,7 +614,6 @@ struct drm_nouveau_private {
614 } susres; 614 } susres;
615 615
616 struct backlight_device *backlight; 616 struct backlight_device *backlight;
617 bool acpi_dsm;
618 617
619 struct nouveau_channel *evo; 618 struct nouveau_channel *evo;
620 619
@@ -682,6 +681,9 @@ extern int nouveau_ignorelid;
682extern int nouveau_nofbaccel; 681extern int nouveau_nofbaccel;
683extern int nouveau_noaccel; 682extern int nouveau_noaccel;
684 683
684extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state);
685extern int nouveau_pci_resume(struct pci_dev *pdev);
686
685/* nouveau_state.c */ 687/* nouveau_state.c */
686extern void nouveau_preclose(struct drm_device *dev, struct drm_file *); 688extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
687extern int nouveau_load(struct drm_device *, unsigned long flags); 689extern int nouveau_load(struct drm_device *, unsigned long flags);
@@ -848,19 +850,8 @@ extern int nouveau_dma_init(struct nouveau_channel *);
848extern int nouveau_dma_wait(struct nouveau_channel *, int size); 850extern int nouveau_dma_wait(struct nouveau_channel *, int size);
849 851
850/* nouveau_acpi.c */ 852/* nouveau_acpi.c */
851#ifdef CONFIG_ACPI 853void nouveau_register_dsm_handler(void);
852extern int nouveau_hybrid_setup(struct drm_device *dev); 854void nouveau_unregister_dsm_handler(void);
853extern bool nouveau_dsm_probe(struct drm_device *dev);
854#else
855static inline int nouveau_hybrid_setup(struct drm_device *dev)
856{
857 return 0;
858}
859static inline bool nouveau_dsm_probe(struct drm_device *dev)
860{
861 return false;
862}
863#endif
864 855
865/* nouveau_backlight.c */ 856/* nouveau_backlight.c */
866#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT 857#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index ea879a2efef3..1ebf22b664dd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -36,6 +36,7 @@
36#include <linux/fb.h> 36#include <linux/fb.h>
37#include <linux/init.h> 37#include <linux/init.h>
38#include <linux/screen_info.h> 38#include <linux/screen_info.h>
39#include <linux/vga_switcheroo.h>
39 40
40#include "drmP.h" 41#include "drmP.h"
41#include "drm.h" 42#include "drm.h"
@@ -370,6 +371,7 @@ nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
370 nvbo->bo.offset, nvbo); 371 nvbo->bo.offset, nvbo);
371 372
372 mutex_unlock(&dev->struct_mutex); 373 mutex_unlock(&dev->struct_mutex);
374 vga_switcheroo_client_fb_set(dev->pdev, info);
373 return 0; 375 return 0;
374 376
375out_unref: 377out_unref:
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index a4851af5b05e..85d65b91389c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -29,6 +29,7 @@
29#include "drm_sarea.h" 29#include "drm_sarea.h"
30#include "drm_crtc_helper.h" 30#include "drm_crtc_helper.h"
31#include <linux/vgaarb.h> 31#include <linux/vgaarb.h>
32#include <linux/vga_switcheroo.h>
32 33
33#include "nouveau_drv.h" 34#include "nouveau_drv.h"
34#include "nouveau_drm.h" 35#include "nouveau_drm.h"
@@ -371,6 +372,30 @@ out_err:
371 return ret; 372 return ret;
372} 373}
373 374
375static void nouveau_switcheroo_set_state(struct pci_dev *pdev,
376 enum vga_switcheroo_state state)
377{
378 pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
379 if (state == VGA_SWITCHEROO_ON) {
380 printk(KERN_ERR "VGA switcheroo: switched nouveau on\n");
381 nouveau_pci_resume(pdev);
382 } else {
383 printk(KERN_ERR "VGA switcheroo: switched nouveau off\n");
384 nouveau_pci_suspend(pdev, pmm);
385 }
386}
387
388static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev)
389{
390 struct drm_device *dev = pci_get_drvdata(pdev);
391 bool can_switch;
392
393 spin_lock(&dev->count_lock);
394 can_switch = (dev->open_count == 0);
395 spin_unlock(&dev->count_lock);
396 return can_switch;
397}
398
374int 399int
375nouveau_card_init(struct drm_device *dev) 400nouveau_card_init(struct drm_device *dev)
376{ 401{
@@ -384,6 +409,8 @@ nouveau_card_init(struct drm_device *dev)
384 return 0; 409 return 0;
385 410
386 vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); 411 vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
412 vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state,
413 nouveau_switcheroo_can_switch);
387 414
388 /* Initialise internal driver API hooks */ 415 /* Initialise internal driver API hooks */
389 ret = nouveau_init_engine_ptrs(dev); 416 ret = nouveau_init_engine_ptrs(dev);
@@ -617,11 +644,6 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
617 NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n", 644 NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n",
618 dev->pci_vendor, dev->pci_device, dev->pdev->class); 645 dev->pci_vendor, dev->pci_device, dev->pdev->class);
619 646
620 dev_priv->acpi_dsm = nouveau_dsm_probe(dev);
621
622 if (dev_priv->acpi_dsm)
623 nouveau_hybrid_setup(dev);
624
625 dev_priv->wq = create_workqueue("nouveau"); 647 dev_priv->wq = create_workqueue("nouveau");
626 if (!dev_priv->wq) 648 if (!dev_priv->wq)
627 return -EINVAL; 649 return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index 1cc7b937b1ea..8e62fe13e31c 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -54,7 +54,8 @@ radeon-y += radeon_device.o radeon_kms.o \
54 radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \ 54 radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \
55 rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \ 55 rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \
56 r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ 56 r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \
57 r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o 57 r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \
58 radeon_atpx_handler.o
58 59
59radeon-$(CONFIG_COMPAT) += radeon_ioc32.o 60radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
60 61
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index c0356bb193e5..a5dfb1557d3e 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -118,6 +118,10 @@ struct radeon_device;
118/* 118/*
119 * BIOS. 119 * BIOS.
120 */ 120 */
121#define ATRM_BIOS_PAGE 4096
122
123bool radeon_atrm_supported(struct pci_dev *pdev);
124int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len);
121bool radeon_get_bios(struct radeon_device *rdev); 125bool radeon_get_bios(struct radeon_device *rdev);
122 126
123 127
@@ -838,6 +842,8 @@ struct radeon_device {
838 int audio_bits_per_sample; 842 int audio_bits_per_sample;
839 uint8_t audio_status_bits; 843 uint8_t audio_status_bits;
840 uint8_t audio_category_code; 844 uint8_t audio_category_code;
845
846 bool powered_down;
841}; 847};
842 848
843int radeon_device_init(struct radeon_device *rdev, 849int radeon_device_init(struct radeon_device *rdev,
@@ -1042,6 +1048,8 @@ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enabl
1042extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); 1048extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);
1043extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); 1049extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
1044extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); 1050extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
1051extern int radeon_resume_kms(struct drm_device *dev);
1052extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state);
1045 1053
1046/* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ 1054/* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */
1047struct r100_mc_save { 1055struct r100_mc_save {
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
new file mode 100644
index 000000000000..0ae52f19071d
--- /dev/null
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -0,0 +1,258 @@
1/*
2 * Copyright (c) 2010 Red Hat Inc.
3 * Author : Dave Airlie <airlied@redhat.com>
4 *
5 * Licensed under GPLv2
6 *
7 * ATPX support for both Intel/ATI
8 */
9
10#include <linux/vga_switcheroo.h>
11#include <acpi/acpi.h>
12#include <acpi/acpi_bus.h>
13#include <linux/pci.h>
14
15#define ATPX_VERSION 0
16#define ATPX_GPU_PWR 2
17#define ATPX_MUX_SELECT 3
18
19#define ATPX_INTEGRATED 0
20#define ATPX_DISCRETE 1
21
22#define ATPX_MUX_IGD 0
23#define ATPX_MUX_DISCRETE 1
24
25static struct radeon_atpx_priv {
26 bool atpx_detected;
27 /* handle for device - and atpx */
28 acpi_handle dhandle;
29 acpi_handle atpx_handle;
30 acpi_handle atrm_handle;
31} radeon_atpx_priv;
32
33/* retrieve the ROM in 4k blocks */
34static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios,
35 int offset, int len)
36{
37 acpi_status status;
38 union acpi_object atrm_arg_elements[2], *obj;
39 struct acpi_object_list atrm_arg;
40 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
41
42 atrm_arg.count = 2;
43 atrm_arg.pointer = &atrm_arg_elements[0];
44
45 atrm_arg_elements[0].type = ACPI_TYPE_INTEGER;
46 atrm_arg_elements[0].integer.value = offset;
47
48 atrm_arg_elements[1].type = ACPI_TYPE_INTEGER;
49 atrm_arg_elements[1].integer.value = len;
50
51 status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer);
52 if (ACPI_FAILURE(status)) {
53 printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status));
54 return -ENODEV;
55 }
56
57 obj = (union acpi_object *)buffer.pointer;
58 memcpy(bios+offset, obj->buffer.pointer, len);
59 kfree(buffer.pointer);
60 return len;
61}
62
63bool radeon_atrm_supported(struct pci_dev *pdev)
64{
65 /* get the discrete ROM only via ATRM */
66 if (!radeon_atpx_priv.atpx_detected)
67 return false;
68
69 if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
70 return false;
71 return true;
72}
73
74
75int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len)
76{
77 return radeon_atrm_call(radeon_atpx_priv.atrm_handle, bios, offset, len);
78}
79
80static int radeon_atpx_get_version(acpi_handle handle)
81{
82 acpi_status status;
83 union acpi_object atpx_arg_elements[2], *obj;
84 struct acpi_object_list atpx_arg;
85 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
86
87 atpx_arg.count = 2;
88 atpx_arg.pointer = &atpx_arg_elements[0];
89
90 atpx_arg_elements[0].type = ACPI_TYPE_INTEGER;
91 atpx_arg_elements[0].integer.value = ATPX_VERSION;
92
93 atpx_arg_elements[1].type = ACPI_TYPE_INTEGER;
94 atpx_arg_elements[1].integer.value = ATPX_VERSION;
95
96 status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer);
97 if (ACPI_FAILURE(status)) {
98 printk("%s: failed to call ATPX: %s\n", __func__, acpi_format_exception(status));
99 return -ENOSYS;
100 }
101 obj = (union acpi_object *)buffer.pointer;
102 if (obj && (obj->type == ACPI_TYPE_BUFFER))
103 printk(KERN_INFO "radeon atpx: version is %d\n", *((u8 *)(obj->buffer.pointer) + 2));
104 kfree(buffer.pointer);
105 return 0;
106}
107
108static int radeon_atpx_execute(acpi_handle handle, int cmd_id, u16 value)
109{
110 acpi_status status;
111 union acpi_object atpx_arg_elements[2];
112 struct acpi_object_list atpx_arg;
113 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
114 uint8_t buf[4] = {0};
115
116 if (!handle)
117 return -EINVAL;
118
119 atpx_arg.count = 2;
120 atpx_arg.pointer = &atpx_arg_elements[0];
121
122 atpx_arg_elements[0].type = ACPI_TYPE_INTEGER;
123 atpx_arg_elements[0].integer.value = cmd_id;
124
125 buf[2] = value & 0xff;
126 buf[3] = (value >> 8) & 0xff;
127
128 atpx_arg_elements[1].type = ACPI_TYPE_BUFFER;
129 atpx_arg_elements[1].buffer.length = 4;
130 atpx_arg_elements[1].buffer.pointer = buf;
131
132 status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer);
133 if (ACPI_FAILURE(status)) {
134 printk("%s: failed to call ATPX: %s\n", __func__, acpi_format_exception(status));
135 return -ENOSYS;
136 }
137 kfree(buffer.pointer);
138
139 return 0;
140}
141
142static int radeon_atpx_set_discrete_state(acpi_handle handle, int state)
143{
144 return radeon_atpx_execute(handle, ATPX_GPU_PWR, state);
145}
146
147static int radeon_atpx_switch_mux(acpi_handle handle, int mux_id)
148{
149 return radeon_atpx_execute(handle, ATPX_MUX_SELECT, mux_id);
150}
151
152
153static int radeon_atpx_switchto(enum vga_switcheroo_client_id id)
154{
155 if (id == VGA_SWITCHEROO_IGD)
156 radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 0);
157 else
158 radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 1);
159 return 0;
160}
161
162static int radeon_atpx_power_state(enum vga_switcheroo_client_id id,
163 enum vga_switcheroo_state state)
164{
165 /* on w500 ACPI can't change intel gpu state */
166 if (id == VGA_SWITCHEROO_IGD)
167 return 0;
168
169 radeon_atpx_set_discrete_state(radeon_atpx_priv.atpx_handle, state);
170 return 0;
171}
172
173static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev)
174{
175 acpi_handle dhandle, atpx_handle, atrm_handle;
176 acpi_status status;
177
178 dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
179 if (!dhandle)
180 return false;
181
182 status = acpi_get_handle(dhandle, "ATPX", &atpx_handle);
183 if (ACPI_FAILURE(status))
184 return false;
185
186 status = acpi_get_handle(dhandle, "ATRM", &atrm_handle);
187 if (ACPI_FAILURE(status))
188 return false;
189
190 radeon_atpx_priv.dhandle = dhandle;
191 radeon_atpx_priv.atpx_handle = atpx_handle;
192 radeon_atpx_priv.atrm_handle = atrm_handle;
193 return true;
194}
195
196static int radeon_atpx_init(void)
197{
198 /* set up the ATPX handle */
199
200 radeon_atpx_get_version(radeon_atpx_priv.atpx_handle);
201 return 0;
202}
203
204static int radeon_atpx_get_client_id(struct pci_dev *pdev)
205{
206 if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
207 return VGA_SWITCHEROO_IGD;
208 else
209 return VGA_SWITCHEROO_DIS;
210}
211
212static struct vga_switcheroo_handler radeon_atpx_handler = {
213 .switchto = radeon_atpx_switchto,
214 .power_state = radeon_atpx_power_state,
215 .init = radeon_atpx_init,
216 .get_client_id = radeon_atpx_get_client_id,
217};
218
219static bool radeon_atpx_detect(void)
220{
221 char acpi_method_name[255] = { 0 };
222 struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
223 struct pci_dev *pdev = NULL;
224 bool has_atpx = false;
225 int vga_count = 0;
226
227 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
228 vga_count++;
229
230 has_atpx |= (radeon_atpx_pci_probe_handle(pdev) == true);
231 }
232
233 if (has_atpx && vga_count == 2) {
234 acpi_get_name(radeon_atpx_priv.atpx_handle, ACPI_FULL_PATHNAME, &buffer);
235 printk(KERN_INFO "VGA switcheroo: detected switching method %s handle\n",
236 acpi_method_name);
237 radeon_atpx_priv.atpx_detected = true;
238 return true;
239 }
240 return false;
241}
242
243void radeon_register_atpx_handler(void)
244{
245 bool r;
246
247 /* detect if we have any ATPX + 2 VGA in the system */
248 r = radeon_atpx_detect();
249 if (!r)
250 return;
251
252 vga_switcheroo_register_handler(&radeon_atpx_handler);
253}
254
255void radeon_unregister_atpx_handler(void)
256{
257 vga_switcheroo_unregister_handler();
258}
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
index 906921740c60..a34b909485b8 100644
--- a/drivers/gpu/drm/radeon/radeon_bios.c
+++ b/drivers/gpu/drm/radeon/radeon_bios.c
@@ -30,6 +30,7 @@
30#include "radeon.h" 30#include "radeon.h"
31#include "atom.h" 31#include "atom.h"
32 32
33#include <linux/vga_switcheroo.h>
33/* 34/*
34 * BIOS. 35 * BIOS.
35 */ 36 */
@@ -62,7 +63,7 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev)
62 iounmap(bios); 63 iounmap(bios);
63 return false; 64 return false;
64 } 65 }
65 memcpy(rdev->bios, bios, size); 66 memcpy_fromio(rdev->bios, bios, size);
66 iounmap(bios); 67 iounmap(bios);
67 return true; 68 return true;
68} 69}
@@ -93,6 +94,38 @@ static bool radeon_read_bios(struct radeon_device *rdev)
93 return true; 94 return true;
94} 95}
95 96
97/* ATRM is used to get the BIOS on the discrete cards in
98 * dual-gpu systems.
99 */
100static bool radeon_atrm_get_bios(struct radeon_device *rdev)
101{
102 int ret;
103 int size = 64 * 1024;
104 int i;
105
106 if (!radeon_atrm_supported(rdev->pdev))
107 return false;
108
109 rdev->bios = kmalloc(size, GFP_KERNEL);
110 if (!rdev->bios) {
111 DRM_ERROR("Unable to allocate bios\n");
112 return false;
113 }
114
115 for (i = 0; i < size / ATRM_BIOS_PAGE; i++) {
116 ret = radeon_atrm_get_bios_chunk(rdev->bios,
117 (i * ATRM_BIOS_PAGE),
118 ATRM_BIOS_PAGE);
119 if (ret <= 0)
120 break;
121 }
122
123 if (i == 0 || rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) {
124 kfree(rdev->bios);
125 return false;
126 }
127 return true;
128}
96static bool r700_read_disabled_bios(struct radeon_device *rdev) 129static bool r700_read_disabled_bios(struct radeon_device *rdev)
97{ 130{
98 uint32_t viph_control; 131 uint32_t viph_control;
@@ -388,16 +421,16 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev)
388 return legacy_read_disabled_bios(rdev); 421 return legacy_read_disabled_bios(rdev);
389} 422}
390 423
424
391bool radeon_get_bios(struct radeon_device *rdev) 425bool radeon_get_bios(struct radeon_device *rdev)
392{ 426{
393 bool r; 427 bool r;
394 uint16_t tmp; 428 uint16_t tmp;
395 429
396 if (rdev->flags & RADEON_IS_IGP) { 430 r = radeon_atrm_get_bios(rdev);
431 if (r == false)
397 r = igp_read_bios_from_vram(rdev); 432 r = igp_read_bios_from_vram(rdev);
398 if (r == false) 433 if (r == false)
399 r = radeon_read_bios(rdev);
400 } else
401 r = radeon_read_bios(rdev); 434 r = radeon_read_bios(rdev);
402 if (r == false) { 435 if (r == false) {
403 r = radeon_read_disabled_bios(rdev); 436 r = radeon_read_disabled_bios(rdev);
@@ -408,6 +441,7 @@ bool radeon_get_bios(struct radeon_device *rdev)
408 return false; 441 return false;
409 } 442 }
410 if (rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { 443 if (rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) {
444 printk("BIOS signature incorrect %x %x\n", rdev->bios[0], rdev->bios[1]);
411 goto free_bios; 445 goto free_bios;
412 } 446 }
413 447
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 768b1509fa03..cb8d9a1dd69c 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -30,6 +30,7 @@
30#include <drm/drm_crtc_helper.h> 30#include <drm/drm_crtc_helper.h>
31#include <drm/radeon_drm.h> 31#include <drm/radeon_drm.h>
32#include <linux/vgaarb.h> 32#include <linux/vgaarb.h>
33#include <linux/vga_switcheroo.h>
33#include "radeon_reg.h" 34#include "radeon_reg.h"
34#include "radeon.h" 35#include "radeon.h"
35#include "radeon_asic.h" 36#include "radeon_asic.h"
@@ -613,6 +614,36 @@ void radeon_check_arguments(struct radeon_device *rdev)
613 } 614 }
614} 615}
615 616
617static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state)
618{
619 struct drm_device *dev = pci_get_drvdata(pdev);
620 struct radeon_device *rdev = dev->dev_private;
621 pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
622 if (state == VGA_SWITCHEROO_ON) {
623 printk(KERN_INFO "radeon: switched on\n");
624 /* don't suspend or resume card normally */
625 rdev->powered_down = false;
626 radeon_resume_kms(dev);
627 } else {
628 printk(KERN_INFO "radeon: switched off\n");
629 radeon_suspend_kms(dev, pmm);
630 /* don't suspend or resume card normally */
631 rdev->powered_down = true;
632 }
633}
634
635static bool radeon_switcheroo_can_switch(struct pci_dev *pdev)
636{
637 struct drm_device *dev = pci_get_drvdata(pdev);
638 bool can_switch;
639
640 spin_lock(&dev->count_lock);
641 can_switch = (dev->open_count == 0);
642 spin_unlock(&dev->count_lock);
643 return can_switch;
644}
645
646
616int radeon_device_init(struct radeon_device *rdev, 647int radeon_device_init(struct radeon_device *rdev,
617 struct drm_device *ddev, 648 struct drm_device *ddev,
618 struct pci_dev *pdev, 649 struct pci_dev *pdev,
@@ -692,6 +723,9 @@ int radeon_device_init(struct radeon_device *rdev,
692 /* this will fail for cards that aren't VGA class devices, just 723 /* this will fail for cards that aren't VGA class devices, just
693 * ignore it */ 724 * ignore it */
694 vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); 725 vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
726 vga_switcheroo_register_client(rdev->pdev,
727 radeon_switcheroo_set_state,
728 radeon_switcheroo_can_switch);
695 729
696 r = radeon_init(rdev); 730 r = radeon_init(rdev);
697 if (r) 731 if (r)
@@ -723,6 +757,7 @@ void radeon_device_fini(struct radeon_device *rdev)
723 rdev->shutdown = true; 757 rdev->shutdown = true;
724 radeon_fini(rdev); 758 radeon_fini(rdev);
725 destroy_workqueue(rdev->wq); 759 destroy_workqueue(rdev->wq);
760 vga_switcheroo_unregister_client(rdev->pdev);
726 vga_client_register(rdev->pdev, NULL, NULL, NULL); 761 vga_client_register(rdev->pdev, NULL, NULL, NULL);
727 iounmap(rdev->rmmio); 762 iounmap(rdev->rmmio);
728 rdev->rmmio = NULL; 763 rdev->rmmio = NULL;
@@ -746,6 +781,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
746 } 781 }
747 rdev = dev->dev_private; 782 rdev = dev->dev_private;
748 783
784 if (rdev->powered_down)
785 return 0;
749 /* unpin the front buffers */ 786 /* unpin the front buffers */
750 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 787 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
751 struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb); 788 struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb);
@@ -791,6 +828,9 @@ int radeon_resume_kms(struct drm_device *dev)
791{ 828{
792 struct radeon_device *rdev = dev->dev_private; 829 struct radeon_device *rdev = dev->dev_private;
793 830
831 if (rdev->powered_down)
832 return 0;
833
794 acquire_console_sem(); 834 acquire_console_sem();
795 pci_set_power_state(dev->pdev, PCI_D0); 835 pci_set_power_state(dev->pdev, PCI_D0);
796 pci_restore_state(dev->pdev); 836 pci_restore_state(dev->pdev);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 8ba3de7994d4..4ab53aa163b2 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -339,6 +339,7 @@ static int __init radeon_init(void)
339 driver = &kms_driver; 339 driver = &kms_driver;
340 driver->driver_features |= DRIVER_MODESET; 340 driver->driver_features |= DRIVER_MODESET;
341 driver->num_ioctls = radeon_max_kms_ioctl; 341 driver->num_ioctls = radeon_max_kms_ioctl;
342 radeon_register_atpx_handler();
342 } 343 }
343 /* if the vga console setting is enabled still 344 /* if the vga console setting is enabled still
344 * let modprobe override it */ 345 * let modprobe override it */
@@ -348,6 +349,7 @@ static int __init radeon_init(void)
348static void __exit radeon_exit(void) 349static void __exit radeon_exit(void)
349{ 350{
350 drm_exit(driver); 351 drm_exit(driver);
352 radeon_unregister_atpx_handler();
351} 353}
352 354
353module_init(radeon_init); 355module_init(radeon_init);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
index c57ad606504d..736237195143 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.h
+++ b/drivers/gpu/drm/radeon/radeon_drv.h
@@ -455,6 +455,9 @@ extern void r600_blit_swap(struct drm_device *dev,
455 int sx, int sy, int dx, int dy, 455 int sx, int sy, int dx, int dy,
456 int w, int h, int src_pitch, int dst_pitch, int cpp); 456 int w, int h, int src_pitch, int dst_pitch, int cpp);
457 457
458/* atpx handler */
459void radeon_register_atpx_handler(void);
460void radeon_unregister_atpx_handler(void);
458/* Flags for stats.boxes 461/* Flags for stats.boxes
459 */ 462 */
460#define RADEON_BOX_DMA_IDLE 0x1 463#define RADEON_BOX_DMA_IDLE 0x1
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index d71e346e9ab5..561719223988 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -39,6 +39,8 @@
39 39
40#include "drm_fb_helper.h" 40#include "drm_fb_helper.h"
41 41
42#include <linux/vga_switcheroo.h>
43
42struct radeon_fb_device { 44struct radeon_fb_device {
43 struct drm_fb_helper helper; 45 struct drm_fb_helper helper;
44 struct radeon_framebuffer *rfb; 46 struct radeon_framebuffer *rfb;
@@ -291,6 +293,7 @@ int radeonfb_create(struct drm_device *dev,
291 rfbdev->rdev = rdev; 293 rfbdev->rdev = rdev;
292 294
293 mutex_unlock(&rdev->ddev->struct_mutex); 295 mutex_unlock(&rdev->ddev->struct_mutex);
296 vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
294 return 0; 297 return 0;
295 298
296out_unref: 299out_unref:
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index f23b05606eb5..5db7af6b91f4 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -30,6 +30,8 @@
30#include "radeon.h" 30#include "radeon.h"
31#include "radeon_drm.h" 31#include "radeon_drm.h"
32 32
33#include <linux/vga_switcheroo.h>
34
33int radeon_driver_unload_kms(struct drm_device *dev) 35int radeon_driver_unload_kms(struct drm_device *dev)
34{ 36{
35 struct radeon_device *rdev = dev->dev_private; 37 struct radeon_device *rdev = dev->dev_private;
@@ -136,6 +138,7 @@ int radeon_driver_firstopen_kms(struct drm_device *dev)
136 138
137void radeon_driver_lastclose_kms(struct drm_device *dev) 139void radeon_driver_lastclose_kms(struct drm_device *dev)
138{ 140{
141 vga_switcheroo_process_delayed_switch();
139} 142}
140 143
141int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) 144int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
diff --git a/drivers/gpu/vga/Kconfig b/drivers/gpu/vga/Kconfig
index 790e675b13eb..6116a0196214 100644
--- a/drivers/gpu/vga/Kconfig
+++ b/drivers/gpu/vga/Kconfig
@@ -8,3 +8,16 @@ config VGA_ARB
8 are accessed at same time they need some kind of coordination. Please 8 are accessed at same time they need some kind of coordination. Please
9 see Documentation/vgaarbiter.txt for more details. Select this to 9 see Documentation/vgaarbiter.txt for more details. Select this to
10 enable VGA arbiter. 10 enable VGA arbiter.
11
12config VGA_SWITCHEROO
13 bool "Laptop Hybrid Grapics - GPU switching support"
14 default y
15 depends on X86
16 depends on ACPI
17 help
18 Many laptops released in 2008/9/10 have two gpus with a multiplxer
19 to switch between them. This adds support for dynamic switching when
20 X isn't running and delayed switching until the next logoff. This
21 features is called hybrid graphics, ATI PowerXpress, and Nvidia
22 HybridPower.
23
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..a3f587a0aba9
--- /dev/null
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -0,0 +1,453 @@
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 <acpi/acpi.h>
29#include <acpi/acpi_bus.h>
30
31#include <linux/pci.h>
32#include <linux/vga_switcheroo.h>
33
34struct vga_switcheroo_client {
35 struct pci_dev *pdev;
36 struct fb_info *fb_info;
37 int pwr_state;
38 void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state);
39 bool (*can_switch)(struct pci_dev *pdev);
40 int id;
41 bool active;
42};
43
44static DEFINE_MUTEX(vgasr_mutex);
45
46struct vgasr_priv {
47
48 bool active;
49 bool delayed_switch_active;
50 enum vga_switcheroo_client_id delayed_client_id;
51
52 struct dentry *debugfs_root;
53 struct dentry *switch_file;
54
55 int registered_clients;
56 struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS];
57
58 struct vga_switcheroo_handler *handler;
59};
60
61static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv);
62static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv);
63
64/* only one switcheroo per system */
65static struct vgasr_priv vgasr_priv;
66
67int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
68{
69 mutex_lock(&vgasr_mutex);
70 if (vgasr_priv.handler) {
71 mutex_unlock(&vgasr_mutex);
72 return -EINVAL;
73 }
74
75 vgasr_priv.handler = handler;
76 mutex_unlock(&vgasr_mutex);
77 return 0;
78}
79EXPORT_SYMBOL(vga_switcheroo_register_handler);
80
81void vga_switcheroo_unregister_handler(void)
82{
83 mutex_lock(&vgasr_mutex);
84 vgasr_priv.handler = NULL;
85 mutex_unlock(&vgasr_mutex);
86}
87EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
88
89static void vga_switcheroo_enable(void)
90{
91 int i;
92 int ret;
93 /* call the handler to init */
94 vgasr_priv.handler->init();
95
96 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
97 ret = vgasr_priv.handler->get_client_id(vgasr_priv.clients[i].pdev);
98 if (ret < 0)
99 return;
100
101 vgasr_priv.clients[i].id = ret;
102 }
103 vga_switcheroo_debugfs_init(&vgasr_priv);
104 vgasr_priv.active = true;
105}
106
107int vga_switcheroo_register_client(struct pci_dev *pdev,
108 void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state),
109 bool (*can_switch)(struct pci_dev *pdev))
110{
111 int index;
112
113 mutex_lock(&vgasr_mutex);
114 /* don't do IGD vs DIS here */
115 if (vgasr_priv.registered_clients & 1)
116 index = 1;
117 else
118 index = 0;
119
120 vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON;
121 vgasr_priv.clients[index].pdev = pdev;
122 vgasr_priv.clients[index].set_gpu_state = set_gpu_state;
123 vgasr_priv.clients[index].can_switch = can_switch;
124 vgasr_priv.clients[index].id = -1;
125 if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
126 vgasr_priv.clients[index].active = true;
127
128 vgasr_priv.registered_clients |= (1 << index);
129
130 /* if we get two clients + handler */
131 if (vgasr_priv.registered_clients == 0x3 && vgasr_priv.handler) {
132 printk(KERN_INFO "vga_switcheroo: enabled\n");
133 vga_switcheroo_enable();
134 }
135 mutex_unlock(&vgasr_mutex);
136 return 0;
137}
138EXPORT_SYMBOL(vga_switcheroo_register_client);
139
140void vga_switcheroo_unregister_client(struct pci_dev *pdev)
141{
142 int i;
143
144 mutex_lock(&vgasr_mutex);
145 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
146 if (vgasr_priv.clients[i].pdev == pdev) {
147 vgasr_priv.registered_clients &= ~(1 << i);
148 break;
149 }
150 }
151
152 printk(KERN_INFO "vga_switcheroo: disabled\n");
153 vga_switcheroo_debugfs_fini(&vgasr_priv);
154 vgasr_priv.active = false;
155 mutex_unlock(&vgasr_mutex);
156}
157EXPORT_SYMBOL(vga_switcheroo_unregister_client);
158
159void vga_switcheroo_client_fb_set(struct pci_dev *pdev,
160 struct fb_info *info)
161{
162 int i;
163
164 mutex_lock(&vgasr_mutex);
165 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
166 if (vgasr_priv.clients[i].pdev == pdev) {
167 vgasr_priv.clients[i].fb_info = info;
168 break;
169 }
170 }
171 mutex_unlock(&vgasr_mutex);
172}
173EXPORT_SYMBOL(vga_switcheroo_client_fb_set);
174
175static int vga_switcheroo_show(struct seq_file *m, void *v)
176{
177 int i;
178 mutex_lock(&vgasr_mutex);
179 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
180 seq_printf(m, "%d:%c:%s:%s\n", i,
181 vgasr_priv.clients[i].active ? '+' : ' ',
182 vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off",
183 pci_name(vgasr_priv.clients[i].pdev));
184 }
185 mutex_unlock(&vgasr_mutex);
186 return 0;
187}
188
189static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)
190{
191 return single_open(file, vga_switcheroo_show, NULL);
192}
193
194static int vga_switchon(struct vga_switcheroo_client *client)
195{
196 int ret;
197
198 ret = vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON);
199 /* call the driver callback to turn on device */
200 client->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON);
201 client->pwr_state = VGA_SWITCHEROO_ON;
202 return 0;
203}
204
205static int vga_switchoff(struct vga_switcheroo_client *client)
206{
207 /* call the driver callback to turn off device */
208 client->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF);
209 vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF);
210 client->pwr_state = VGA_SWITCHEROO_OFF;
211 return 0;
212}
213
214static int vga_switchto(struct vga_switcheroo_client *new_client)
215{
216 int ret;
217 int i;
218 struct vga_switcheroo_client *active = NULL;
219
220 if (new_client->active == true)
221 return 0;
222
223 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
224 if (vgasr_priv.clients[i].active == true) {
225 active = &vgasr_priv.clients[i];
226 break;
227 }
228 }
229 if (!active)
230 return 0;
231
232 /* power up the first device */
233 ret = pci_enable_device(new_client->pdev);
234 if (ret)
235 return ret;
236
237 if (new_client->pwr_state == VGA_SWITCHEROO_OFF)
238 vga_switchon(new_client);
239
240 /* swap shadow resource to denote boot VGA device has changed so X starts on new device */
241 active->active = false;
242
243 active->pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_ROM_SHADOW;
244 new_client->pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW;
245
246 if (new_client->fb_info) {
247 struct fb_event event;
248 event.info = new_client->fb_info;
249 fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event);
250 }
251
252 ret = vgasr_priv.handler->switchto(new_client->id);
253 if (ret)
254 return ret;
255
256 if (active->pwr_state == VGA_SWITCHEROO_ON)
257 vga_switchoff(active);
258
259 new_client->active = true;
260 return 0;
261}
262
263static ssize_t
264vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
265 size_t cnt, loff_t *ppos)
266{
267 char usercmd[64];
268 const char *pdev_name;
269 int i, ret;
270 bool delay = false, can_switch;
271 int client_id = -1;
272 struct vga_switcheroo_client *client = NULL;
273
274 if (cnt > 63)
275 cnt = 63;
276
277 if (copy_from_user(usercmd, ubuf, cnt))
278 return -EFAULT;
279
280 mutex_lock(&vgasr_mutex);
281
282 if (!vgasr_priv.active)
283 return -EINVAL;
284
285 /* pwr off the device not in use */
286 if (strncmp(usercmd, "OFF", 3) == 0) {
287 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
288 if (vgasr_priv.clients[i].active)
289 continue;
290 if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_ON)
291 vga_switchoff(&vgasr_priv.clients[i]);
292 }
293 goto out;
294 }
295 /* pwr on the device not in use */
296 if (strncmp(usercmd, "ON", 2) == 0) {
297 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
298 if (vgasr_priv.clients[i].active)
299 continue;
300 if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_OFF)
301 vga_switchon(&vgasr_priv.clients[i]);
302 }
303 goto out;
304 }
305
306 /* request a delayed switch - test can we switch now */
307 if (strncmp(usercmd, "DIGD", 4) == 0) {
308 client_id = VGA_SWITCHEROO_IGD;
309 delay = true;
310 }
311
312 if (strncmp(usercmd, "DDIS", 4) == 0) {
313 client_id = VGA_SWITCHEROO_DIS;
314 delay = true;
315 }
316
317 if (strncmp(usercmd, "IGD", 3) == 0)
318 client_id = VGA_SWITCHEROO_IGD;
319
320 if (strncmp(usercmd, "DIS", 3) == 0)
321 client_id = VGA_SWITCHEROO_DIS;
322
323 if (client_id == -1)
324 goto out;
325
326 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
327 if (vgasr_priv.clients[i].id == client_id) {
328 client = &vgasr_priv.clients[i];
329 break;
330 }
331 }
332
333 vgasr_priv.delayed_switch_active = false;
334 /* okay we want a switch - test if devices are willing to switch */
335 can_switch = true;
336 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
337 can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev);
338 if (can_switch == false) {
339 printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i);
340 break;
341 }
342 }
343
344 if (can_switch == false && delay == false)
345 goto out;
346
347 if (can_switch == true) {
348 pdev_name = pci_name(client->pdev);
349 ret = vga_switchto(client);
350 if (ret)
351 printk(KERN_ERR "vga_switcheroo: switching failed %d\n", ret);
352 } else {
353 printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id);
354 vgasr_priv.delayed_switch_active = true;
355 vgasr_priv.delayed_client_id = client_id;
356
357 /* we should at least power up the card to
358 make the switch faster */
359 if (client->pwr_state == VGA_SWITCHEROO_OFF)
360 vga_switchon(client);
361 }
362
363out:
364 mutex_unlock(&vgasr_mutex);
365 return cnt;
366}
367
368static const struct file_operations vga_switcheroo_debugfs_fops = {
369 .owner = THIS_MODULE,
370 .open = vga_switcheroo_debugfs_open,
371 .write = vga_switcheroo_debugfs_write,
372 .read = seq_read,
373 .llseek = seq_lseek,
374 .release = single_release,
375};
376
377static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv)
378{
379 if (priv->switch_file) {
380 debugfs_remove(priv->switch_file);
381 priv->switch_file = NULL;
382 }
383 if (priv->debugfs_root) {
384 debugfs_remove(priv->debugfs_root);
385 priv->debugfs_root = NULL;
386 }
387}
388
389static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv)
390{
391 /* already initialised */
392 if (priv->debugfs_root)
393 return 0;
394 priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL);
395
396 if (!priv->debugfs_root) {
397 printk(KERN_ERR "vga_switcheroo: Cannot create /sys/kernel/debug/vgaswitcheroo\n");
398 goto fail;
399 }
400
401 priv->switch_file = debugfs_create_file("switch", 0644,
402 priv->debugfs_root, NULL, &vga_switcheroo_debugfs_fops);
403 if (!priv->switch_file) {
404 printk(KERN_ERR "vga_switcheroo: cannot create /sys/kernel/debug/vgaswitcheroo/switch\n");
405 goto fail;
406 }
407 return 0;
408fail:
409 vga_switcheroo_debugfs_fini(priv);
410 return -1;
411}
412
413int vga_switcheroo_process_delayed_switch(void)
414{
415 struct vga_switcheroo_client *client = NULL;
416 const char *pdev_name;
417 bool can_switch = true;
418 int i;
419 int ret;
420 int err = -EINVAL;
421
422 mutex_lock(&vgasr_mutex);
423 if (!vgasr_priv.delayed_switch_active)
424 goto err;
425
426 printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id);
427
428 for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
429 if (vgasr_priv.clients[i].id == vgasr_priv.delayed_client_id)
430 client = &vgasr_priv.clients[i];
431 can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev);
432 if (can_switch == false) {
433 printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i);
434 break;
435 }
436 }
437
438 if (can_switch == false || client == NULL)
439 goto err;
440
441 pdev_name = pci_name(client->pdev);
442 ret = vga_switchto(client);
443 if (ret)
444 printk(KERN_ERR "vga_switcheroo: delayed switching failed %d\n", ret);
445
446 vgasr_priv.delayed_switch_active = false;
447 err = 0;
448err:
449 mutex_unlock(&vgasr_mutex);
450 return err;
451}
452EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
453
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 3681c6a88212..b0a3fa00706d 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -3025,6 +3025,20 @@ static int fbcon_fb_unregistered(struct fb_info *info)
3025 return 0; 3025 return 0;
3026} 3026}
3027 3027
3028static void fbcon_remap_all(int idx)
3029{
3030 int i;
3031 for (i = first_fb_vc; i <= last_fb_vc; i++)
3032 set_con2fb_map(i, idx, 0);
3033
3034 if (con_is_bound(&fb_con)) {
3035 printk(KERN_INFO "fbcon: Remapping primary device, "
3036 "fb%i, to tty %i-%i\n", idx,
3037 first_fb_vc + 1, last_fb_vc + 1);
3038 info_idx = idx;
3039 }
3040}
3041
3028#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY 3042#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY
3029static void fbcon_select_primary(struct fb_info *info) 3043static void fbcon_select_primary(struct fb_info *info)
3030{ 3044{
@@ -3225,6 +3239,10 @@ static int fbcon_event_notify(struct notifier_block *self,
3225 caps = event->data; 3239 caps = event->data;
3226 fbcon_get_requirement(info, caps); 3240 fbcon_get_requirement(info, caps);
3227 break; 3241 break;
3242 case FB_EVENT_REMAP_ALL_CONSOLE:
3243 idx = info->node;
3244 fbcon_remap_all(idx);
3245 break;
3228 } 3246 }
3229done: 3247done:
3230 return ret; 3248 return ret;
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 369767bd873e..c10163b4c40e 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -543,6 +543,8 @@ struct fb_cursor_user {
543#define FB_EVENT_GET_REQ 0x0D 543#define FB_EVENT_GET_REQ 0x0D
544/* Unbind from the console if possible */ 544/* Unbind from the console if possible */
545#define FB_EVENT_FB_UNBIND 0x0E 545#define FB_EVENT_FB_UNBIND 0x0E
546/* CONSOLE-SPECIFIC: remap all consoles to new fb - for vga switcheroo */
547#define FB_EVENT_REMAP_ALL_CONSOLE 0x0F
546 548
547struct fb_event { 549struct fb_event {
548 struct fb_info *info; 550 struct fb_info *info;
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
new file mode 100644
index 000000000000..4b58ab1e8612
--- /dev/null
+++ b/include/linux/vga_switcheroo.h
@@ -0,0 +1,58 @@
1/*
2 * Copyright (c) 2010 Red Hat Inc.
3 * Author : Dave Airlie <airlied@redhat.com>
4 *
5 * Licensed under GPLv2
6 *
7 * vga_switcheroo.h - Support for laptop with dual GPU using one set of outputs
8 */
9
10#include <acpi/acpi.h>
11#include <linux/fb.h>
12
13enum vga_switcheroo_state {
14 VGA_SWITCHEROO_OFF,
15 VGA_SWITCHEROO_ON,
16};
17
18enum vga_switcheroo_client_id {
19 VGA_SWITCHEROO_IGD,
20 VGA_SWITCHEROO_DIS,
21 VGA_SWITCHEROO_MAX_CLIENTS,
22};
23
24struct vga_switcheroo_handler {
25 int (*switchto)(enum vga_switcheroo_client_id id);
26 int (*power_state)(enum vga_switcheroo_client_id id,
27 enum vga_switcheroo_state state);
28 int (*init)(void);
29 int (*get_client_id)(struct pci_dev *pdev);
30};
31
32
33#if defined(CONFIG_VGA_SWITCHEROO)
34void vga_switcheroo_unregister_client(struct pci_dev *dev);
35int vga_switcheroo_register_client(struct pci_dev *dev,
36 void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state),
37 bool (*can_switch)(struct pci_dev *dev));
38
39void vga_switcheroo_client_fb_set(struct pci_dev *dev,
40 struct fb_info *info);
41
42int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler);
43void vga_switcheroo_unregister_handler(void);
44
45int vga_switcheroo_process_delayed_switch(void);
46
47#else
48
49static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {}
50static inline int vga_switcheroo_register_client(struct pci_dev *dev,
51 void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state),
52 bool (*can_switch)(struct pci_dev *dev)) { return 0; }
53static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {}
54static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; }
55static inline void vga_switcheroo_unregister_handler(void) {}
56static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
57
58#endif