diff options
author | Dave Airlie <airlied@linux.ie> | 2010-02-01 00:38:10 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-03-01 01:20:37 -0500 |
commit | 6a9ee8af344e3bd7dbd61e67037096cdf7f83289 (patch) | |
tree | 07cdb493a790cf45bc473f2fc2ea1b9a166d5191 /drivers/gpu/drm | |
parent | 9fd1de52945e06ed88a440c99ca92dab74b9b33c (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>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 35 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_fb.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_acpi.c | 160 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.c | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fbcon.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_state.c | 32 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon.h | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_atpx_handler.c | 258 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_bios.c | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_device.c | 40 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_fb.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_kms.c | 3 |
18 files changed, 559 insertions, 70 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 2307f98349f..42ca07f04a2 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 | ||
1203 | static 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 | |||
1218 | static 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 | |||
1202 | static int i915_load_modeset_init(struct drm_device *dev, | 1229 | static 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 cf4cb3e9a0c..fd739efe73c 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 | ||
204 | static int i915_suspend(struct drm_device *dev, pm_message_t state) | 204 | int 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 | ||
258 | static int i915_resume(struct drm_device *dev) | 258 | int 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 b99b6a841d9..d77e5665135 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; | |||
736 | extern unsigned int i915_powersave; | 736 | extern unsigned int i915_powersave; |
737 | extern unsigned int i915_lvds_downclock; | 737 | extern unsigned int i915_lvds_downclock; |
738 | 738 | ||
739 | extern int i915_suspend(struct drm_device *dev, pm_message_t state); | ||
740 | extern int i915_resume(struct drm_device *dev); | ||
739 | extern void i915_save_display(struct drm_device *dev); | 741 | extern void i915_save_display(struct drm_device *dev); |
740 | extern void i915_restore_display(struct drm_device *dev); | 742 | extern void i915_restore_display(struct drm_device *dev); |
741 | extern int i915_master_create(struct drm_device *dev, struct drm_master *master); | 743 | extern 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 aaabbcbe590..8cd791dc5b2 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 | ||
240 | out_unpin: | 242 | out_unpin: |
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 48227e74475..0e0730a5313 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 | ||
31 | static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) | 33 | static 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 | |
39 | static 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; | 44 | static 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 | ||
89 | int nouveau_hybrid_setup(struct drm_device *dev) | 90 | static 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)) | 95 | static 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); | 106 | static 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 | ||
114 | static 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 | |||
123 | static int nouveau_dsm_init(void) | ||
124 | { | ||
110 | return 0; | 125 | return 0; |
111 | } | 126 | } |
112 | 127 | ||
113 | bool nouveau_dsm_probe(struct drm_device *dev) | 128 | static 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 | |||
136 | static 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, | 143 | static 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 | |||
168 | static 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 | |||
191 | void 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 | |||
202 | void 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 da3b93b8450..f83ec65addb 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 | ||
138 | static int | 138 | int |
139 | nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) | 139 | nouveau_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 | ||
236 | static int | 236 | int |
237 | nouveau_pci_resume(struct pci_dev *pdev) | 237 | nouveau_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) | |||
411 | static void __exit nouveau_exit(void) | 413 | static void __exit nouveau_exit(void) |
412 | { | 414 | { |
413 | drm_exit(&driver); | 415 | drm_exit(&driver); |
416 | nouveau_unregister_dsm_handler(); | ||
414 | } | 417 | } |
415 | 418 | ||
416 | module_init(nouveau_init); | 419 | module_init(nouveau_init); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 1c15ef37b71..85c05feab4f 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; | |||
682 | extern int nouveau_nofbaccel; | 681 | extern int nouveau_nofbaccel; |
683 | extern int nouveau_noaccel; | 682 | extern int nouveau_noaccel; |
684 | 683 | ||
684 | extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state); | ||
685 | extern int nouveau_pci_resume(struct pci_dev *pdev); | ||
686 | |||
685 | /* nouveau_state.c */ | 687 | /* nouveau_state.c */ |
686 | extern void nouveau_preclose(struct drm_device *dev, struct drm_file *); | 688 | extern void nouveau_preclose(struct drm_device *dev, struct drm_file *); |
687 | extern int nouveau_load(struct drm_device *, unsigned long flags); | 689 | extern int nouveau_load(struct drm_device *, unsigned long flags); |
@@ -848,19 +850,8 @@ extern int nouveau_dma_init(struct nouveau_channel *); | |||
848 | extern int nouveau_dma_wait(struct nouveau_channel *, int size); | 850 | extern int nouveau_dma_wait(struct nouveau_channel *, int size); |
849 | 851 | ||
850 | /* nouveau_acpi.c */ | 852 | /* nouveau_acpi.c */ |
851 | #ifdef CONFIG_ACPI | 853 | void nouveau_register_dsm_handler(void); |
852 | extern int nouveau_hybrid_setup(struct drm_device *dev); | 854 | void nouveau_unregister_dsm_handler(void); |
853 | extern bool nouveau_dsm_probe(struct drm_device *dev); | ||
854 | #else | ||
855 | static inline int nouveau_hybrid_setup(struct drm_device *dev) | ||
856 | { | ||
857 | return 0; | ||
858 | } | ||
859 | static 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 ea879a2efef..1ebf22b664d 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 | ||
375 | out_unref: | 377 | out_unref: |
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index a4851af5b05..85d65b91389 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 | ||
375 | static 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 | |||
388 | static 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 | |||
374 | int | 399 | int |
375 | nouveau_card_init(struct drm_device *dev) | 400 | nouveau_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 1cc7b937b1e..8e62fe13e31 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 | ||
59 | radeon-$(CONFIG_COMPAT) += radeon_ioc32.o | 60 | radeon-$(CONFIG_COMPAT) += radeon_ioc32.o |
60 | 61 | ||
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index c0356bb193e..a5dfb1557d3 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 | |||
123 | bool radeon_atrm_supported(struct pci_dev *pdev); | ||
124 | int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len); | ||
121 | bool radeon_get_bios(struct radeon_device *rdev); | 125 | bool 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 | ||
843 | int radeon_device_init(struct radeon_device *rdev, | 849 | int radeon_device_init(struct radeon_device *rdev, |
@@ -1042,6 +1048,8 @@ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enabl | |||
1042 | extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); | 1048 | extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); |
1043 | extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); | 1049 | extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); |
1044 | extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); | 1050 | extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); |
1051 | extern int radeon_resume_kms(struct drm_device *dev); | ||
1052 | extern 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 */ |
1047 | struct r100_mc_save { | 1055 | struct 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 00000000000..0ae52f19071 --- /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 | |||
25 | static 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 */ | ||
34 | static 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 | |||
63 | bool 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 | |||
75 | int 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 | |||
80 | static 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 | |||
108 | static 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 | |||
142 | static int radeon_atpx_set_discrete_state(acpi_handle handle, int state) | ||
143 | { | ||
144 | return radeon_atpx_execute(handle, ATPX_GPU_PWR, state); | ||
145 | } | ||
146 | |||
147 | static 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 | |||
153 | static 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 | |||
162 | static 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 | |||
173 | static 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 | |||
196 | static 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 | |||
204 | static 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 | |||
212 | static 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 | |||
219 | static 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 | |||
243 | void 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 | |||
255 | void 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 906921740c6..a34b909485b 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 | */ | ||
100 | static 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 | } | ||
96 | static bool r700_read_disabled_bios(struct radeon_device *rdev) | 129 | static 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 | |||
391 | bool radeon_get_bios(struct radeon_device *rdev) | 425 | bool 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 768b1509fa0..cb8d9a1dd69 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 | ||
617 | static 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 | |||
635 | static 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 | |||
616 | int radeon_device_init(struct radeon_device *rdev, | 647 | int 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 8ba3de7994d..4ab53aa163b 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) | |||
348 | static void __exit radeon_exit(void) | 349 | static void __exit radeon_exit(void) |
349 | { | 350 | { |
350 | drm_exit(driver); | 351 | drm_exit(driver); |
352 | radeon_unregister_atpx_handler(); | ||
351 | } | 353 | } |
352 | 354 | ||
353 | module_init(radeon_init); | 355 | module_init(radeon_init); |
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index c57ad606504..73623719514 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 */ | ||
459 | void radeon_register_atpx_handler(void); | ||
460 | void 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 d71e346e9ab..56171922398 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 | |||
42 | struct radeon_fb_device { | 44 | struct 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 | ||
296 | out_unref: | 299 | out_unref: |
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index f23b05606eb..5db7af6b91f 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 | |||
33 | int radeon_driver_unload_kms(struct drm_device *dev) | 35 | int 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 | ||
137 | void radeon_driver_lastclose_kms(struct drm_device *dev) | 139 | void radeon_driver_lastclose_kms(struct drm_device *dev) |
138 | { | 140 | { |
141 | vga_switcheroo_process_delayed_switch(); | ||
139 | } | 142 | } |
140 | 143 | ||
141 | int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) | 144 | int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) |