Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 346 lines (287 sloc) 8.588 kb
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
1 /*
2d5f4a2 @awilliam asus-switcheroo: Misc configs and printk changes
authored
2 * VGA switcheroo driver for ASUS laptops
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
3 *
4 * Based on drivers/gpu/drm/nouveau/nouveau_acpi.c
5 *
6 * Copyright 2011 Red Hat, Inc
7 *
c3457f8 @awilliam asus-switcheroo: rename
authored
8 * Author: Alex Williamson <alex.williamson@redhat.com>
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
9 *
10 * This work is licensed under the terms of the GNU GPL, version 2. See
11 * the COPYING file in the top-level directory.
12 */
13
723ae8b compile against 3.2 kernels
ibloat authored
14 #include <linux/moduleparam.h>
15 #include <linux/module.h>
e005845 @awilliam asus-switcheroo: Add delay after powering on device
authored
16 #include <linux/delay.h>
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
17 #include <linux/pci.h>
18 #include <linux/acpi.h>
19 #include <linux/slab.h>
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
20 #include <linux/version.h>
100f1f8 @awilliam asus-switcheroo: Make nouveau reprobe
authored
21 #include <linux/kallsyms.h>
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
22 #include <linux/vga_switcheroo.h>
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
23 #include <acpi/acpi_bus.h>
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
24 #include <acpi/acpi_drivers.h>
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
25 #include <acpi/video.h>
26
27 #define DSM_SUPPORTED 0x00
28 #define DSM_SUPPORTED_FUNCTIONS 0x00
29
30 #define DSM_LED 0x02
31 #define DSM_LED_STATE 0x00
32 #define DSM_LED_OFF 0x10
33 #define DSM_LED_STAMINA 0x11
34 #define DSM_LED_SPEED 0x12
35
36 #define DSM_POWER 0x03
37 #define DSM_POWER_STATE 0x00
38 #define DSM_POWER_SPEED 0x01
39 #define DSM_POWER_STAMINA 0x02
40
41 static acpi_handle dsm_handle;
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
42 static acpi_handle discrete_handle;
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
43 static acpi_handle igd_handle;
44
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
45 static struct pci_dev *discrete_dev;
46 static bool dummy_client;
47 static bool dummy_client_switched;
48
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
49 static const char dsm_uuid[] = {
50 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
51 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
52 };
53
c3457f8 @awilliam asus-switcheroo: rename
authored
54 static int asus_switcheroo_dsm_call(acpi_handle handle, int func, int arg)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
55 {
56 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
57 struct acpi_object_list input;
58 union acpi_object params[4], elements[4];
59 union acpi_object *obj;
60 int i, err;
61
62 input.count = 4;
63 input.pointer = params;
64 params[0].type = ACPI_TYPE_BUFFER;
65 params[0].buffer.length = sizeof(dsm_uuid);
66 params[0].buffer.pointer = (char *)dsm_uuid;
67 params[1].type = ACPI_TYPE_INTEGER;
68 params[1].integer.value = 0; /* revision ID - unused */
69 params[2].type = ACPI_TYPE_INTEGER;
70 params[2].integer.value = func;
71 params[3].type = ACPI_TYPE_PACKAGE;
72 params[3].package.count = 4;
73 params[3].package.elements = elements;
74 for (i = 0; i < 4; i++) {
75 elements[i].type = ACPI_TYPE_INTEGER;
76 elements[i].integer.value = (arg >> (i * 8)) & 0xff;
77 }
78
79 err = acpi_evaluate_object(handle, "_DSM", &input, &output);
80 if (err) {
81 printk(KERN_INFO "failed to evaluate _DSM: %d\n", err);
82 return err;
83 }
84
85 obj = (union acpi_object *)output.pointer;
86
87 if (obj->type == ACPI_TYPE_INTEGER)
88 if (obj->integer.value == 0x80000002)
89 return -ENODEV;
90
91 kfree(output.pointer);
92 return 0;
93 }
94
c3457f8 @awilliam asus-switcheroo: rename
authored
95 static int asus_switcheroo_acpi_mux(acpi_handle handle)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
96 {
97 struct acpi_object_list input;
98 union acpi_object param;
99 int err;
100
101 input.count = 1;
102 input.pointer = &param;
103 param.type = ACPI_TYPE_INTEGER;
104 param.integer.value = 1;
105
106 /* I don't really know what these do, but it seems to work */
107 err = acpi_evaluate_object(handle, "MXMX", &input, NULL);
108 if (err) {
109 printk(KERN_INFO "failed to evaluate MXMX: %d\n", err);
110 return err;
111 }
112
113 err = acpi_evaluate_object(handle, "MXDS", &input, NULL);
114 if (err) {
115 printk(KERN_INFO "failed to evaluate MXMX: %d\n", err);
116 return err;
117 }
118 return 0;
119 }
120
bca53f1 @awilliam asus-switcher: Fix client register compile
authored
121 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
100f1f8 @awilliam asus-switcheroo: Make nouveau reprobe
authored
122 static void asus_switcheroo_force_nouveau_reprobe(void)
123 {
124 void *dev = pci_get_drvdata(discrete_dev);
125 void (*nouveau_fbcon_hook)(void *);
126
127 nouveau_fbcon_hook =
128 (void *)kallsyms_lookup_name("nouveau_fbcon_output_poll_changed");
129
130 if (!nouveau_fbcon_hook) {
131 printk("Can't hook to nouveau_fbcon_output_poll_changed\n");
132 return;
133 }
134 nouveau_fbcon_hook(dev);
135 }
136 #endif
137
c3457f8 @awilliam asus-switcheroo: rename
authored
138 static int asus_switcheroo_switchto(enum vga_switcheroo_client_id id)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
139 {
100f1f8 @awilliam asus-switcheroo: Make nouveau reprobe
authored
140 int ret, dsm_arg;
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
141
142 if (id == VGA_SWITCHEROO_IGD) {
c3457f8 @awilliam asus-switcheroo: rename
authored
143 asus_switcheroo_acpi_mux(igd_handle);
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
144 dsm_arg = DSM_LED_STAMINA;
145 } else {
c3457f8 @awilliam asus-switcheroo: rename
authored
146 asus_switcheroo_acpi_mux(discrete_handle);
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
147 dsm_arg = DSM_LED_SPEED;
148 }
149
100f1f8 @awilliam asus-switcheroo: Make nouveau reprobe
authored
150 ret = asus_switcheroo_dsm_call(dsm_handle, DSM_LED, dsm_arg);
bca53f1 @awilliam asus-switcher: Fix client register compile
authored
151 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,38)
100f1f8 @awilliam asus-switcheroo: Make nouveau reprobe
authored
152 if (id == VGA_SWITCHEROO_DIS && !dummy_client)
153 asus_switcheroo_force_nouveau_reprobe();
154 #endif
155 return ret;
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
156 }
157
c3457f8 @awilliam asus-switcheroo: rename
authored
158 static int asus_switcheroo_power_state(enum vga_switcheroo_client_id id,
159 enum vga_switcheroo_state state)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
160 {
e005845 @awilliam asus-switcheroo: Add delay after powering on device
authored
161 int ret, dsm_arg;
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
162
163 if (id == VGA_SWITCHEROO_IGD)
164 return 0;
165
166 if (state == VGA_SWITCHEROO_ON)
167 dsm_arg = DSM_POWER_SPEED;
168 else
169 dsm_arg = DSM_POWER_STAMINA;
170
e005845 @awilliam asus-switcheroo: Add delay after powering on device
authored
171 ret = asus_switcheroo_dsm_call(dsm_handle, DSM_POWER, dsm_arg);
172
173 if (state == VGA_SWITCHEROO_ON)
174 msleep(10);
175
176 return ret;
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
177 }
178
c3457f8 @awilliam asus-switcheroo: rename
authored
179 static int asus_switcheroo_handler_init(void)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
180 {
181 return 0;
182 }
183
c3457f8 @awilliam asus-switcheroo: rename
authored
184 static int asus_switcheroo_get_client_id(struct pci_dev *pdev)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
185 {
186 if (pdev->vendor == PCI_VENDOR_ID_INTEL)
187 return VGA_SWITCHEROO_IGD;
188
189 return VGA_SWITCHEROO_DIS;
190 }
191
c3457f8 @awilliam asus-switcheroo: rename
authored
192 static struct vga_switcheroo_handler asus_dsm_handler = {
193 .switchto = asus_switcheroo_switchto,
194 .power_state = asus_switcheroo_power_state,
195 .init = asus_switcheroo_handler_init,
196 .get_client_id = asus_switcheroo_get_client_id,
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
197 };
198
c3457f8 @awilliam asus-switcheroo: rename
authored
199 static void asus_switcheroo_set_state(struct pci_dev *pdev,
200 enum vga_switcheroo_state state)
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
201 {
202 if (state == VGA_SWITCHEROO_ON) {
203 printk(KERN_INFO
2d5f4a2 @awilliam asus-switcheroo: Misc configs and printk changes
authored
204 "Asus switcheroo: turning on discrete graphics\n");
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
205 pci_set_power_state(pdev, PCI_D0);
206 pci_restore_state(pdev);
207 if (pci_enable_device(pdev))
2d5f4a2 @awilliam asus-switcheroo: Misc configs and printk changes
authored
208 printk(KERN_WARNING
209 "Asus switcher: failed to enable %s\n",
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
210 dev_name(&pdev->dev));
211 pci_set_master(pdev);
212 dummy_client_switched = true;
213 } else {
214 printk(KERN_INFO
2d5f4a2 @awilliam asus-switcheroo: Misc configs and printk changes
authored
215 "Asus switcheroo: turning off discrete graphics\n");
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
216 pci_save_state(pdev);
217 pci_clear_master(pdev);
218 pci_disable_device(pdev);
219 pci_set_power_state(pdev, PCI_D3hot);
220 }
221 }
222
c3457f8 @awilliam asus-switcheroo: rename
authored
223 static bool asus_switcheroo_can_switch(struct pci_dev *pdev)
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
224 {
225 return !dummy_client_switched;
226 }
227
c3457f8 @awilliam asus-switcheroo: rename
authored
228 static bool asus_switcheroo_dsm_pci_probe(struct pci_dev *pdev)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
229 {
230 acpi_handle handle, test_handle;
231 acpi_status status;
232 int ret;
233
234 handle = DEVICE_ACPI_HANDLE(&pdev->dev);
235 if (!handle)
236 return false;
237
238 status = acpi_get_handle(handle, "_DSM", &test_handle);
239 if (ACPI_FAILURE(status)) {
240 return false;
241 }
242
c3457f8 @awilliam asus-switcheroo: rename
authored
243 ret = asus_switcheroo_dsm_call(handle, DSM_SUPPORTED,
244 DSM_SUPPORTED_FUNCTIONS);
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
245 if (ret < 0)
246 return false;
247
248 status = acpi_get_handle(handle, "MXMX", &test_handle);
249 if (ACPI_FAILURE(status)) {
250 return false;
251 }
252
253 status = acpi_get_handle(handle, "MXDS", &test_handle);
254 if (ACPI_FAILURE(status)) {
255 return false;
256 }
257
258 dsm_handle = handle;
259 return true;
260 }
261
c3457f8 @awilliam asus-switcheroo: rename
authored
262 static bool asus_switcheroo_dsm_detect(void)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
263 {
264 struct pci_dev *pdev = NULL;
265 int class = PCI_CLASS_DISPLAY_VGA << 8;
266 int vga_count = 0;
267
268 while ((pdev = pci_get_class(class, pdev)) != NULL) {
269 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
270 acpi_handle handle;
271
272 vga_count++;
273
274 /* The _DSM actually exists on both devices on this system */
c3457f8 @awilliam asus-switcheroo: rename
authored
275 if (!asus_switcheroo_dsm_pci_probe(pdev))
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
276 return false;
277
278 handle = DEVICE_ACPI_HANDLE(&pdev->dev);
279 if (!handle)
280 return false;
281
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
282 if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
283 igd_handle = handle;
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
284 } else {
285 discrete_handle = handle;
286 discrete_dev = pdev;
287 }
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
288
289 acpi_get_name(handle, ACPI_FULL_PATHNAME, &buf);
290 printk(KERN_INFO "Found VGA device %s (%s): %s\n",
291 dev_name(&pdev->dev), (char *)buf.pointer,
292 pdev->vendor == PCI_VENDOR_ID_INTEL ? "IGD" : "DIS");
293 kfree(buf.pointer);
294 }
295
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
296 if (vga_count == 2 && dsm_handle && igd_handle && discrete_handle) {
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
297 struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
298
299 acpi_get_name(dsm_handle, ACPI_FULL_PATHNAME, &buf);
300 printk(KERN_INFO
2d5f4a2 @awilliam asus-switcheroo: Misc configs and printk changes
authored
301 "Asus switcheroo: detected DSM switching method "
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
302 "%s handle\n", (char *)buf.pointer);
303 kfree(buf.pointer);
304 return true;
305 }
306 return false;
307 }
308
c3457f8 @awilliam asus-switcheroo: rename
authored
309 static int __init asus_switcheroo_init(void)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
310 {
c3457f8 @awilliam asus-switcheroo: rename
authored
311 if (!asus_switcheroo_dsm_detect())
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
312 return 0;
313
c3457f8 @awilliam asus-switcheroo: rename
authored
314 vga_switcheroo_register_handler(&asus_dsm_handler);
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
315
316 if (dummy_client)
bca53f1 @awilliam asus-switcher: Fix client register compile
authored
317 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
318 vga_switcheroo_register_client(discrete_dev,
c3457f8 @awilliam asus-switcheroo: rename
authored
319 asus_switcheroo_set_state, NULL,
320 asus_switcheroo_can_switch);
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
321 #else
322 vga_switcheroo_register_client(discrete_dev,
c3457f8 @awilliam asus-switcheroo: rename
authored
323 asus_switcheroo_set_state,
324 asus_switcheroo_can_switch);
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
325 #endif
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
326 return 0;
327 }
328
c3457f8 @awilliam asus-switcheroo: rename
authored
329 static void __exit asus_switcheroo_exit(void)
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
330 {
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
331 if (dummy_client)
332 vga_switcheroo_unregister_client(discrete_dev);
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
333 vga_switcheroo_unregister_handler();
334 }
335
c3457f8 @awilliam asus-switcheroo: rename
authored
336 module_init(asus_switcheroo_init);
337 module_exit(asus_switcheroo_exit);
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
338
339 module_param(dummy_client, bool, 0444);
340 MODULE_PARM_DESC(dummy_client, "Enable dummy VGA switcheroo client support");
6007331 @awilliam Initial, working VGA switcheroo for Asus UL30VT
authored
341
342 MODULE_AUTHOR("Alex Williamson <alex.williamson@redhat.com>");
2d5f4a2 @awilliam asus-switcheroo: Misc configs and printk changes
authored
343 MODULE_DESCRIPTION("Experimental Asus hybrid graphics switcheroo");
fbe683a @awilliam ul30vt switcheroo: first pass at dummy client support
authored
344 MODULE_LICENSE("GPL v2");
8b5fa56 @awilliam asus-switcher: update to deploy i915-jprobe
authored
345 MODULE_VERSION("0.2");
Something went wrong with that request. Please try again.