diff options
Diffstat (limited to 'drivers/platform/x86/apple-gmux.c')
-rw-r--r-- | drivers/platform/x86/apple-gmux.c | 432 |
1 files changed, 406 insertions, 26 deletions
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 905fa01ac8df..db8f63841b42 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Gmux driver for Apple laptops | 2 | * Gmux driver for Apple laptops |
3 | * | 3 | * |
4 | * Copyright (C) Canonical Ltd. <seth.forshee@canonical.com> | 4 | * Copyright (C) Canonical Ltd. <seth.forshee@canonical.com> |
5 | * Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de> | ||
5 | * | 6 | * |
6 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 8 | * it under the terms of the GNU General Public License version 2 as |
@@ -18,16 +19,30 @@ | |||
18 | #include <linux/pnp.h> | 19 | #include <linux/pnp.h> |
19 | #include <linux/apple_bl.h> | 20 | #include <linux/apple_bl.h> |
20 | #include <linux/slab.h> | 21 | #include <linux/slab.h> |
22 | #include <linux/delay.h> | ||
23 | #include <linux/pci.h> | ||
24 | #include <linux/vga_switcheroo.h> | ||
21 | #include <acpi/video.h> | 25 | #include <acpi/video.h> |
22 | #include <asm/io.h> | 26 | #include <asm/io.h> |
23 | 27 | ||
24 | struct apple_gmux_data { | 28 | struct apple_gmux_data { |
25 | unsigned long iostart; | 29 | unsigned long iostart; |
26 | unsigned long iolen; | 30 | unsigned long iolen; |
31 | bool indexed; | ||
32 | struct mutex index_lock; | ||
27 | 33 | ||
28 | struct backlight_device *bdev; | 34 | struct backlight_device *bdev; |
35 | |||
36 | /* switcheroo data */ | ||
37 | acpi_handle dhandle; | ||
38 | int gpe; | ||
39 | enum vga_switcheroo_client_id resume_client_id; | ||
40 | enum vga_switcheroo_state power_state; | ||
41 | struct completion powerchange_done; | ||
29 | }; | 42 | }; |
30 | 43 | ||
44 | static struct apple_gmux_data *apple_gmux_data; | ||
45 | |||
31 | /* | 46 | /* |
32 | * gmux port offsets. Many of these are not yet used, but may be in the | 47 | * gmux port offsets. Many of these are not yet used, but may be in the |
33 | * future, and it's useful to have them documented here anyhow. | 48 | * future, and it's useful to have them documented here anyhow. |
@@ -45,6 +60,9 @@ struct apple_gmux_data { | |||
45 | #define GMUX_PORT_DISCRETE_POWER 0x50 | 60 | #define GMUX_PORT_DISCRETE_POWER 0x50 |
46 | #define GMUX_PORT_MAX_BRIGHTNESS 0x70 | 61 | #define GMUX_PORT_MAX_BRIGHTNESS 0x70 |
47 | #define GMUX_PORT_BRIGHTNESS 0x74 | 62 | #define GMUX_PORT_BRIGHTNESS 0x74 |
63 | #define GMUX_PORT_VALUE 0xc2 | ||
64 | #define GMUX_PORT_READ 0xd0 | ||
65 | #define GMUX_PORT_WRITE 0xd4 | ||
48 | 66 | ||
49 | #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) | 67 | #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) |
50 | 68 | ||
@@ -59,22 +77,174 @@ struct apple_gmux_data { | |||
59 | #define GMUX_BRIGHTNESS_MASK 0x00ffffff | 77 | #define GMUX_BRIGHTNESS_MASK 0x00ffffff |
60 | #define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK | 78 | #define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK |
61 | 79 | ||
62 | static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port) | 80 | static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port) |
63 | { | 81 | { |
64 | return inb(gmux_data->iostart + port); | 82 | return inb(gmux_data->iostart + port); |
65 | } | 83 | } |
66 | 84 | ||
67 | static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port, | 85 | static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port, |
68 | u8 val) | 86 | u8 val) |
69 | { | 87 | { |
70 | outb(val, gmux_data->iostart + port); | 88 | outb(val, gmux_data->iostart + port); |
71 | } | 89 | } |
72 | 90 | ||
73 | static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port) | 91 | static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port) |
74 | { | 92 | { |
75 | return inl(gmux_data->iostart + port); | 93 | return inl(gmux_data->iostart + port); |
76 | } | 94 | } |
77 | 95 | ||
96 | static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port, | ||
97 | u32 val) | ||
98 | { | ||
99 | int i; | ||
100 | u8 tmpval; | ||
101 | |||
102 | for (i = 0; i < 4; i++) { | ||
103 | tmpval = (val >> (i * 8)) & 0xff; | ||
104 | outb(tmpval, gmux_data->iostart + port + i); | ||
105 | } | ||
106 | } | ||
107 | |||
108 | static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data) | ||
109 | { | ||
110 | int i = 200; | ||
111 | u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE); | ||
112 | |||
113 | while (i && (gwr & 0x01)) { | ||
114 | inb(gmux_data->iostart + GMUX_PORT_READ); | ||
115 | gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE); | ||
116 | udelay(100); | ||
117 | i--; | ||
118 | } | ||
119 | |||
120 | return !!i; | ||
121 | } | ||
122 | |||
123 | static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data) | ||
124 | { | ||
125 | int i = 200; | ||
126 | u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE); | ||
127 | |||
128 | while (i && !(gwr & 0x01)) { | ||
129 | gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE); | ||
130 | udelay(100); | ||
131 | i--; | ||
132 | } | ||
133 | |||
134 | if (gwr & 0x01) | ||
135 | inb(gmux_data->iostart + GMUX_PORT_READ); | ||
136 | |||
137 | return !!i; | ||
138 | } | ||
139 | |||
140 | static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port) | ||
141 | { | ||
142 | u8 val; | ||
143 | |||
144 | mutex_lock(&gmux_data->index_lock); | ||
145 | gmux_index_wait_ready(gmux_data); | ||
146 | outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ); | ||
147 | gmux_index_wait_complete(gmux_data); | ||
148 | val = inb(gmux_data->iostart + GMUX_PORT_VALUE); | ||
149 | mutex_unlock(&gmux_data->index_lock); | ||
150 | |||
151 | return val; | ||
152 | } | ||
153 | |||
154 | static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port, | ||
155 | u8 val) | ||
156 | { | ||
157 | mutex_lock(&gmux_data->index_lock); | ||
158 | outb(val, gmux_data->iostart + GMUX_PORT_VALUE); | ||
159 | gmux_index_wait_ready(gmux_data); | ||
160 | outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE); | ||
161 | gmux_index_wait_complete(gmux_data); | ||
162 | mutex_unlock(&gmux_data->index_lock); | ||
163 | } | ||
164 | |||
165 | static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port) | ||
166 | { | ||
167 | u32 val; | ||
168 | |||
169 | mutex_lock(&gmux_data->index_lock); | ||
170 | gmux_index_wait_ready(gmux_data); | ||
171 | outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ); | ||
172 | gmux_index_wait_complete(gmux_data); | ||
173 | val = inl(gmux_data->iostart + GMUX_PORT_VALUE); | ||
174 | mutex_unlock(&gmux_data->index_lock); | ||
175 | |||
176 | return val; | ||
177 | } | ||
178 | |||
179 | static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port, | ||
180 | u32 val) | ||
181 | { | ||
182 | int i; | ||
183 | u8 tmpval; | ||
184 | |||
185 | mutex_lock(&gmux_data->index_lock); | ||
186 | |||
187 | for (i = 0; i < 4; i++) { | ||
188 | tmpval = (val >> (i * 8)) & 0xff; | ||
189 | outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i); | ||
190 | } | ||
191 | |||
192 | gmux_index_wait_ready(gmux_data); | ||
193 | outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE); | ||
194 | gmux_index_wait_complete(gmux_data); | ||
195 | mutex_unlock(&gmux_data->index_lock); | ||
196 | } | ||
197 | |||
198 | static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port) | ||
199 | { | ||
200 | if (gmux_data->indexed) | ||
201 | return gmux_index_read8(gmux_data, port); | ||
202 | else | ||
203 | return gmux_pio_read8(gmux_data, port); | ||
204 | } | ||
205 | |||
206 | static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val) | ||
207 | { | ||
208 | if (gmux_data->indexed) | ||
209 | gmux_index_write8(gmux_data, port, val); | ||
210 | else | ||
211 | gmux_pio_write8(gmux_data, port, val); | ||
212 | } | ||
213 | |||
214 | static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port) | ||
215 | { | ||
216 | if (gmux_data->indexed) | ||
217 | return gmux_index_read32(gmux_data, port); | ||
218 | else | ||
219 | return gmux_pio_read32(gmux_data, port); | ||
220 | } | ||
221 | |||
222 | static void gmux_write32(struct apple_gmux_data *gmux_data, int port, | ||
223 | u32 val) | ||
224 | { | ||
225 | if (gmux_data->indexed) | ||
226 | gmux_index_write32(gmux_data, port, val); | ||
227 | else | ||
228 | gmux_pio_write32(gmux_data, port, val); | ||
229 | } | ||
230 | |||
231 | static bool gmux_is_indexed(struct apple_gmux_data *gmux_data) | ||
232 | { | ||
233 | u16 val; | ||
234 | |||
235 | outb(0xaa, gmux_data->iostart + 0xcc); | ||
236 | outb(0x55, gmux_data->iostart + 0xcd); | ||
237 | outb(0x00, gmux_data->iostart + 0xce); | ||
238 | |||
239 | val = inb(gmux_data->iostart + 0xcc) | | ||
240 | (inb(gmux_data->iostart + 0xcd) << 8); | ||
241 | |||
242 | if (val == 0x55aa) | ||
243 | return true; | ||
244 | |||
245 | return false; | ||
246 | } | ||
247 | |||
78 | static int gmux_get_brightness(struct backlight_device *bd) | 248 | static int gmux_get_brightness(struct backlight_device *bd) |
79 | { | 249 | { |
80 | struct apple_gmux_data *gmux_data = bl_get_data(bd); | 250 | struct apple_gmux_data *gmux_data = bl_get_data(bd); |
@@ -90,16 +260,7 @@ static int gmux_update_status(struct backlight_device *bd) | |||
90 | if (bd->props.state & BL_CORE_SUSPENDED) | 260 | if (bd->props.state & BL_CORE_SUSPENDED) |
91 | return 0; | 261 | return 0; |
92 | 262 | ||
93 | /* | 263 | gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness); |
94 | * Older gmux versions require writing out lower bytes first then | ||
95 | * setting the upper byte to 0 to flush the values. Newer versions | ||
96 | * accept a single u32 write, but the old method also works, so we | ||
97 | * just use the old method for all gmux versions. | ||
98 | */ | ||
99 | gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness); | ||
100 | gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8); | ||
101 | gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16); | ||
102 | gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0); | ||
103 | 264 | ||
104 | return 0; | 265 | return 0; |
105 | } | 266 | } |
@@ -110,6 +271,146 @@ static const struct backlight_ops gmux_bl_ops = { | |||
110 | .update_status = gmux_update_status, | 271 | .update_status = gmux_update_status, |
111 | }; | 272 | }; |
112 | 273 | ||
274 | static int gmux_switchto(enum vga_switcheroo_client_id id) | ||
275 | { | ||
276 | if (id == VGA_SWITCHEROO_IGD) { | ||
277 | gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); | ||
278 | gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); | ||
279 | gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); | ||
280 | } else { | ||
281 | gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); | ||
282 | gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); | ||
283 | gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); | ||
284 | } | ||
285 | |||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data, | ||
290 | enum vga_switcheroo_state state) | ||
291 | { | ||
292 | INIT_COMPLETION(gmux_data->powerchange_done); | ||
293 | |||
294 | if (state == VGA_SWITCHEROO_ON) { | ||
295 | gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); | ||
296 | gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); | ||
297 | pr_debug("Discrete card powered up\n"); | ||
298 | } else { | ||
299 | gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); | ||
300 | gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); | ||
301 | pr_debug("Discrete card powered down\n"); | ||
302 | } | ||
303 | |||
304 | gmux_data->power_state = state; | ||
305 | |||
306 | if (gmux_data->gpe >= 0 && | ||
307 | !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done, | ||
308 | msecs_to_jiffies(200))) | ||
309 | pr_warn("Timeout waiting for gmux switch to complete\n"); | ||
310 | |||
311 | return 0; | ||
312 | } | ||
313 | |||
314 | static int gmux_set_power_state(enum vga_switcheroo_client_id id, | ||
315 | enum vga_switcheroo_state state) | ||
316 | { | ||
317 | if (id == VGA_SWITCHEROO_IGD) | ||
318 | return 0; | ||
319 | |||
320 | return gmux_set_discrete_state(apple_gmux_data, state); | ||
321 | } | ||
322 | |||
323 | static int gmux_get_client_id(struct pci_dev *pdev) | ||
324 | { | ||
325 | /* | ||
326 | * Early Macbook Pros with switchable graphics use nvidia | ||
327 | * integrated graphics. Hardcode that the 9400M is integrated. | ||
328 | */ | ||
329 | if (pdev->vendor == PCI_VENDOR_ID_INTEL) | ||
330 | return VGA_SWITCHEROO_IGD; | ||
331 | else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && | ||
332 | pdev->device == 0x0863) | ||
333 | return VGA_SWITCHEROO_IGD; | ||
334 | else | ||
335 | return VGA_SWITCHEROO_DIS; | ||
336 | } | ||
337 | |||
338 | static enum vga_switcheroo_client_id | ||
339 | gmux_active_client(struct apple_gmux_data *gmux_data) | ||
340 | { | ||
341 | if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2) | ||
342 | return VGA_SWITCHEROO_IGD; | ||
343 | |||
344 | return VGA_SWITCHEROO_DIS; | ||
345 | } | ||
346 | |||
347 | static struct vga_switcheroo_handler gmux_handler = { | ||
348 | .switchto = gmux_switchto, | ||
349 | .power_state = gmux_set_power_state, | ||
350 | .get_client_id = gmux_get_client_id, | ||
351 | }; | ||
352 | |||
353 | static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data) | ||
354 | { | ||
355 | gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, | ||
356 | GMUX_INTERRUPT_DISABLE); | ||
357 | } | ||
358 | |||
359 | static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data) | ||
360 | { | ||
361 | gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, | ||
362 | GMUX_INTERRUPT_ENABLE); | ||
363 | } | ||
364 | |||
365 | static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data) | ||
366 | { | ||
367 | return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS); | ||
368 | } | ||
369 | |||
370 | static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data) | ||
371 | { | ||
372 | u8 status; | ||
373 | |||
374 | /* to clear interrupts write back current status */ | ||
375 | status = gmux_interrupt_get_status(gmux_data); | ||
376 | gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status); | ||
377 | } | ||
378 | |||
379 | static void gmux_notify_handler(acpi_handle device, u32 value, void *context) | ||
380 | { | ||
381 | u8 status; | ||
382 | struct pnp_dev *pnp = (struct pnp_dev *)context; | ||
383 | struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); | ||
384 | |||
385 | status = gmux_interrupt_get_status(gmux_data); | ||
386 | gmux_disable_interrupts(gmux_data); | ||
387 | pr_debug("Notify handler called: status %d\n", status); | ||
388 | |||
389 | gmux_clear_interrupts(gmux_data); | ||
390 | gmux_enable_interrupts(gmux_data); | ||
391 | |||
392 | if (status & GMUX_INTERRUPT_STATUS_POWER) | ||
393 | complete(&gmux_data->powerchange_done); | ||
394 | } | ||
395 | |||
396 | static int gmux_suspend(struct pnp_dev *pnp, pm_message_t state) | ||
397 | { | ||
398 | struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); | ||
399 | gmux_data->resume_client_id = gmux_active_client(gmux_data); | ||
400 | gmux_disable_interrupts(gmux_data); | ||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static int gmux_resume(struct pnp_dev *pnp) | ||
405 | { | ||
406 | struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); | ||
407 | gmux_enable_interrupts(gmux_data); | ||
408 | gmux_switchto(gmux_data->resume_client_id); | ||
409 | if (gmux_data->power_state == VGA_SWITCHEROO_OFF) | ||
410 | gmux_set_discrete_state(gmux_data, gmux_data->power_state); | ||
411 | return 0; | ||
412 | } | ||
413 | |||
113 | static int __devinit gmux_probe(struct pnp_dev *pnp, | 414 | static int __devinit gmux_probe(struct pnp_dev *pnp, |
114 | const struct pnp_device_id *id) | 415 | const struct pnp_device_id *id) |
115 | { | 416 | { |
@@ -119,6 +420,11 @@ static int __devinit gmux_probe(struct pnp_dev *pnp, | |||
119 | struct backlight_device *bdev; | 420 | struct backlight_device *bdev; |
120 | u8 ver_major, ver_minor, ver_release; | 421 | u8 ver_major, ver_minor, ver_release; |
121 | int ret = -ENXIO; | 422 | int ret = -ENXIO; |
423 | acpi_status status; | ||
424 | unsigned long long gpe; | ||
425 | |||
426 | if (apple_gmux_data) | ||
427 | return -EBUSY; | ||
122 | 428 | ||
123 | gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL); | 429 | gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL); |
124 | if (!gmux_data) | 430 | if (!gmux_data) |
@@ -147,21 +453,32 @@ static int __devinit gmux_probe(struct pnp_dev *pnp, | |||
147 | } | 453 | } |
148 | 454 | ||
149 | /* | 455 | /* |
150 | * On some machines the gmux is in ACPI even thought the machine | 456 | * Invalid version information may indicate either that the gmux |
151 | * doesn't really have a gmux. Check for invalid version information | 457 | * device isn't present or that it's a new one that uses indexed |
152 | * to detect this. | 458 | * io |
153 | */ | 459 | */ |
460 | |||
154 | ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR); | 461 | ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR); |
155 | ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR); | 462 | ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR); |
156 | ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE); | 463 | ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE); |
157 | if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { | 464 | if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { |
158 | pr_info("gmux device not present\n"); | 465 | if (gmux_is_indexed(gmux_data)) { |
159 | ret = -ENODEV; | 466 | u32 version; |
160 | goto err_release; | 467 | mutex_init(&gmux_data->index_lock); |
468 | gmux_data->indexed = true; | ||
469 | version = gmux_read32(gmux_data, | ||
470 | GMUX_PORT_VERSION_MAJOR); | ||
471 | ver_major = (version >> 24) & 0xff; | ||
472 | ver_minor = (version >> 16) & 0xff; | ||
473 | ver_release = (version >> 8) & 0xff; | ||
474 | } else { | ||
475 | pr_info("gmux device not present\n"); | ||
476 | ret = -ENODEV; | ||
477 | goto err_release; | ||
478 | } | ||
161 | } | 479 | } |
162 | 480 | pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor, | |
163 | pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor, | 481 | ver_release, (gmux_data->indexed ? "indexed" : "classic")); |
164 | ver_release); | ||
165 | 482 | ||
166 | memset(&props, 0, sizeof(props)); | 483 | memset(&props, 0, sizeof(props)); |
167 | props.type = BACKLIGHT_PLATFORM; | 484 | props.type = BACKLIGHT_PLATFORM; |
@@ -194,13 +511,65 @@ static int __devinit gmux_probe(struct pnp_dev *pnp, | |||
194 | * Disable the other backlight choices. | 511 | * Disable the other backlight choices. |
195 | */ | 512 | */ |
196 | acpi_video_dmi_promote_vendor(); | 513 | acpi_video_dmi_promote_vendor(); |
197 | #ifdef CONFIG_ACPI_VIDEO | ||
198 | acpi_video_unregister(); | 514 | acpi_video_unregister(); |
199 | #endif | ||
200 | apple_bl_unregister(); | 515 | apple_bl_unregister(); |
201 | 516 | ||
517 | gmux_data->power_state = VGA_SWITCHEROO_ON; | ||
518 | |||
519 | gmux_data->dhandle = DEVICE_ACPI_HANDLE(&pnp->dev); | ||
520 | if (!gmux_data->dhandle) { | ||
521 | pr_err("Cannot find acpi handle for pnp device %s\n", | ||
522 | dev_name(&pnp->dev)); | ||
523 | ret = -ENODEV; | ||
524 | goto err_notify; | ||
525 | } | ||
526 | |||
527 | status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe); | ||
528 | if (ACPI_SUCCESS(status)) { | ||
529 | gmux_data->gpe = (int)gpe; | ||
530 | |||
531 | status = acpi_install_notify_handler(gmux_data->dhandle, | ||
532 | ACPI_DEVICE_NOTIFY, | ||
533 | &gmux_notify_handler, pnp); | ||
534 | if (ACPI_FAILURE(status)) { | ||
535 | pr_err("Install notify handler failed: %s\n", | ||
536 | acpi_format_exception(status)); | ||
537 | ret = -ENODEV; | ||
538 | goto err_notify; | ||
539 | } | ||
540 | |||
541 | status = acpi_enable_gpe(NULL, gmux_data->gpe); | ||
542 | if (ACPI_FAILURE(status)) { | ||
543 | pr_err("Cannot enable gpe: %s\n", | ||
544 | acpi_format_exception(status)); | ||
545 | goto err_enable_gpe; | ||
546 | } | ||
547 | } else { | ||
548 | pr_warn("No GPE found for gmux\n"); | ||
549 | gmux_data->gpe = -1; | ||
550 | } | ||
551 | |||
552 | if (vga_switcheroo_register_handler(&gmux_handler)) { | ||
553 | ret = -ENODEV; | ||
554 | goto err_register_handler; | ||
555 | } | ||
556 | |||
557 | init_completion(&gmux_data->powerchange_done); | ||
558 | apple_gmux_data = gmux_data; | ||
559 | gmux_enable_interrupts(gmux_data); | ||
560 | |||
202 | return 0; | 561 | return 0; |
203 | 562 | ||
563 | err_register_handler: | ||
564 | if (gmux_data->gpe >= 0) | ||
565 | acpi_disable_gpe(NULL, gmux_data->gpe); | ||
566 | err_enable_gpe: | ||
567 | if (gmux_data->gpe >= 0) | ||
568 | acpi_remove_notify_handler(gmux_data->dhandle, | ||
569 | ACPI_DEVICE_NOTIFY, | ||
570 | &gmux_notify_handler); | ||
571 | err_notify: | ||
572 | backlight_device_unregister(bdev); | ||
204 | err_release: | 573 | err_release: |
205 | release_region(gmux_data->iostart, gmux_data->iolen); | 574 | release_region(gmux_data->iostart, gmux_data->iolen); |
206 | err_free: | 575 | err_free: |
@@ -212,14 +581,23 @@ static void __devexit gmux_remove(struct pnp_dev *pnp) | |||
212 | { | 581 | { |
213 | struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); | 582 | struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); |
214 | 583 | ||
584 | vga_switcheroo_unregister_handler(); | ||
585 | gmux_disable_interrupts(gmux_data); | ||
586 | if (gmux_data->gpe >= 0) { | ||
587 | acpi_disable_gpe(NULL, gmux_data->gpe); | ||
588 | acpi_remove_notify_handler(gmux_data->dhandle, | ||
589 | ACPI_DEVICE_NOTIFY, | ||
590 | &gmux_notify_handler); | ||
591 | } | ||
592 | |||
215 | backlight_device_unregister(gmux_data->bdev); | 593 | backlight_device_unregister(gmux_data->bdev); |
594 | |||
216 | release_region(gmux_data->iostart, gmux_data->iolen); | 595 | release_region(gmux_data->iostart, gmux_data->iolen); |
596 | apple_gmux_data = NULL; | ||
217 | kfree(gmux_data); | 597 | kfree(gmux_data); |
218 | 598 | ||
219 | acpi_video_dmi_demote_vendor(); | 599 | acpi_video_dmi_demote_vendor(); |
220 | #ifdef CONFIG_ACPI_VIDEO | ||
221 | acpi_video_register(); | 600 | acpi_video_register(); |
222 | #endif | ||
223 | apple_bl_register(); | 601 | apple_bl_register(); |
224 | } | 602 | } |
225 | 603 | ||
@@ -233,6 +611,8 @@ static struct pnp_driver gmux_pnp_driver = { | |||
233 | .probe = gmux_probe, | 611 | .probe = gmux_probe, |
234 | .remove = __devexit_p(gmux_remove), | 612 | .remove = __devexit_p(gmux_remove), |
235 | .id_table = gmux_device_ids, | 613 | .id_table = gmux_device_ids, |
614 | .suspend = gmux_suspend, | ||
615 | .resume = gmux_resume | ||
236 | }; | 616 | }; |
237 | 617 | ||
238 | static int __init apple_gmux_init(void) | 618 | static int __init apple_gmux_init(void) |