1
2
3
4
5
6
7
8
9
10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11
12#include <linux/module.h>
13#include <linux/kernel.h>
14#include <linux/init.h>
15#include <linux/backlight.h>
16#include <linux/acpi.h>
17#include <linux/pnp.h>
18#include <linux/apple_bl.h>
19#include <linux/apple-gmux.h>
20#include <linux/slab.h>
21#include <linux/delay.h>
22#include <linux/pci.h>
23#include <linux/vga_switcheroo.h>
24#include <acpi/video.h>
25#include <asm/io.h>
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47struct apple_gmux_data {
48 unsigned long iostart;
49 unsigned long iolen;
50 bool indexed;
51 struct mutex index_lock;
52
53 struct backlight_device *bdev;
54
55
56 acpi_handle dhandle;
57 int gpe;
58 bool external_switchable;
59 enum vga_switcheroo_client_id switch_state_display;
60 enum vga_switcheroo_client_id switch_state_ddc;
61 enum vga_switcheroo_client_id switch_state_external;
62 enum vga_switcheroo_state power_state;
63 struct completion powerchange_done;
64};
65
66static struct apple_gmux_data *apple_gmux_data;
67
68
69
70
71
72#define GMUX_PORT_VERSION_MAJOR 0x04
73#define GMUX_PORT_VERSION_MINOR 0x05
74#define GMUX_PORT_VERSION_RELEASE 0x06
75#define GMUX_PORT_SWITCH_DISPLAY 0x10
76#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
77#define GMUX_PORT_INTERRUPT_ENABLE 0x14
78#define GMUX_PORT_INTERRUPT_STATUS 0x16
79#define GMUX_PORT_SWITCH_DDC 0x28
80#define GMUX_PORT_SWITCH_EXTERNAL 0x40
81#define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
82#define GMUX_PORT_DISCRETE_POWER 0x50
83#define GMUX_PORT_MAX_BRIGHTNESS 0x70
84#define GMUX_PORT_BRIGHTNESS 0x74
85#define GMUX_PORT_VALUE 0xc2
86#define GMUX_PORT_READ 0xd0
87#define GMUX_PORT_WRITE 0xd4
88
89#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
90
91#define GMUX_INTERRUPT_ENABLE 0xff
92#define GMUX_INTERRUPT_DISABLE 0x00
93
94#define GMUX_INTERRUPT_STATUS_ACTIVE 0
95#define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0)
96#define GMUX_INTERRUPT_STATUS_POWER (1 << 2)
97#define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3)
98
99#define GMUX_BRIGHTNESS_MASK 0x00ffffff
100#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
101
102static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
103{
104 return inb(gmux_data->iostart + port);
105}
106
107static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port,
108 u8 val)
109{
110 outb(val, gmux_data->iostart + port);
111}
112
113static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port)
114{
115 return inl(gmux_data->iostart + port);
116}
117
118static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
119 u32 val)
120{
121 int i;
122 u8 tmpval;
123
124 for (i = 0; i < 4; i++) {
125 tmpval = (val >> (i * 8)) & 0xff;
126 outb(tmpval, gmux_data->iostart + port + i);
127 }
128}
129
130static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data)
131{
132 int i = 200;
133 u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
134
135 while (i && (gwr & 0x01)) {
136 inb(gmux_data->iostart + GMUX_PORT_READ);
137 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
138 udelay(100);
139 i--;
140 }
141
142 return !!i;
143}
144
145static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data)
146{
147 int i = 200;
148 u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
149
150 while (i && !(gwr & 0x01)) {
151 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
152 udelay(100);
153 i--;
154 }
155
156 if (gwr & 0x01)
157 inb(gmux_data->iostart + GMUX_PORT_READ);
158
159 return !!i;
160}
161
162static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
163{
164 u8 val;
165
166 mutex_lock(&gmux_data->index_lock);
167 gmux_index_wait_ready(gmux_data);
168 outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
169 gmux_index_wait_complete(gmux_data);
170 val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
171 mutex_unlock(&gmux_data->index_lock);
172
173 return val;
174}
175
176static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port,
177 u8 val)
178{
179 mutex_lock(&gmux_data->index_lock);
180 outb(val, gmux_data->iostart + GMUX_PORT_VALUE);
181 gmux_index_wait_ready(gmux_data);
182 outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
183 gmux_index_wait_complete(gmux_data);
184 mutex_unlock(&gmux_data->index_lock);
185}
186
187static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
188{
189 u32 val;
190
191 mutex_lock(&gmux_data->index_lock);
192 gmux_index_wait_ready(gmux_data);
193 outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
194 gmux_index_wait_complete(gmux_data);
195 val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
196 mutex_unlock(&gmux_data->index_lock);
197
198 return val;
199}
200
201static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
202 u32 val)
203{
204 int i;
205 u8 tmpval;
206
207 mutex_lock(&gmux_data->index_lock);
208
209 for (i = 0; i < 4; i++) {
210 tmpval = (val >> (i * 8)) & 0xff;
211 outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i);
212 }
213
214 gmux_index_wait_ready(gmux_data);
215 outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
216 gmux_index_wait_complete(gmux_data);
217 mutex_unlock(&gmux_data->index_lock);
218}
219
220static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
221{
222 if (gmux_data->indexed)
223 return gmux_index_read8(gmux_data, port);
224 else
225 return gmux_pio_read8(gmux_data, port);
226}
227
228static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
229{
230 if (gmux_data->indexed)
231 gmux_index_write8(gmux_data, port, val);
232 else
233 gmux_pio_write8(gmux_data, port, val);
234}
235
236static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
237{
238 if (gmux_data->indexed)
239 return gmux_index_read32(gmux_data, port);
240 else
241 return gmux_pio_read32(gmux_data, port);
242}
243
244static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
245 u32 val)
246{
247 if (gmux_data->indexed)
248 gmux_index_write32(gmux_data, port, val);
249 else
250 gmux_pio_write32(gmux_data, port, val);
251}
252
253static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
254{
255 u16 val;
256
257 outb(0xaa, gmux_data->iostart + 0xcc);
258 outb(0x55, gmux_data->iostart + 0xcd);
259 outb(0x00, gmux_data->iostart + 0xce);
260
261 val = inb(gmux_data->iostart + 0xcc) |
262 (inb(gmux_data->iostart + 0xcd) << 8);
263
264 if (val == 0x55aa)
265 return true;
266
267 return false;
268}
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284static int gmux_get_brightness(struct backlight_device *bd)
285{
286 struct apple_gmux_data *gmux_data = bl_get_data(bd);
287 return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) &
288 GMUX_BRIGHTNESS_MASK;
289}
290
291static int gmux_update_status(struct backlight_device *bd)
292{
293 struct apple_gmux_data *gmux_data = bl_get_data(bd);
294 u32 brightness = bd->props.brightness;
295
296 if (bd->props.state & BL_CORE_SUSPENDED)
297 return 0;
298
299 gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
300
301 return 0;
302}
303
304static const struct backlight_ops gmux_bl_ops = {
305 .options = BL_CORE_SUSPENDRESUME,
306 .get_brightness = gmux_get_brightness,
307 .update_status = gmux_update_status,
308};
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386static void gmux_read_switch_state(struct apple_gmux_data *gmux_data)
387{
388 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DDC) == 1)
389 gmux_data->switch_state_ddc = VGA_SWITCHEROO_IGD;
390 else
391 gmux_data->switch_state_ddc = VGA_SWITCHEROO_DIS;
392
393 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2)
394 gmux_data->switch_state_display = VGA_SWITCHEROO_IGD;
395 else
396 gmux_data->switch_state_display = VGA_SWITCHEROO_DIS;
397
398 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL) == 2)
399 gmux_data->switch_state_external = VGA_SWITCHEROO_IGD;
400 else
401 gmux_data->switch_state_external = VGA_SWITCHEROO_DIS;
402}
403
404static void gmux_write_switch_state(struct apple_gmux_data *gmux_data)
405{
406 if (gmux_data->switch_state_ddc == VGA_SWITCHEROO_IGD)
407 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DDC, 1);
408 else
409 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DDC, 2);
410
411 if (gmux_data->switch_state_display == VGA_SWITCHEROO_IGD)
412 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2);
413 else
414 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3);
415
416 if (gmux_data->switch_state_external == VGA_SWITCHEROO_IGD)
417 gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2);
418 else
419 gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
420}
421
422static int gmux_switchto(enum vga_switcheroo_client_id id)
423{
424 apple_gmux_data->switch_state_ddc = id;
425 apple_gmux_data->switch_state_display = id;
426 if (apple_gmux_data->external_switchable)
427 apple_gmux_data->switch_state_external = id;
428
429 gmux_write_switch_state(apple_gmux_data);
430
431 return 0;
432}
433
434static int gmux_switch_ddc(enum vga_switcheroo_client_id id)
435{
436 enum vga_switcheroo_client_id old_ddc_owner =
437 apple_gmux_data->switch_state_ddc;
438
439 if (id == old_ddc_owner)
440 return id;
441
442 pr_debug("Switching DDC from %d to %d\n", old_ddc_owner, id);
443 apple_gmux_data->switch_state_ddc = id;
444
445 if (id == VGA_SWITCHEROO_IGD)
446 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1);
447 else
448 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2);
449
450 return old_ddc_owner;
451}
452
453
454
455
456
457
458
459
460
461static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
462 enum vga_switcheroo_state state)
463{
464 reinit_completion(&gmux_data->powerchange_done);
465
466 if (state == VGA_SWITCHEROO_ON) {
467 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
468 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3);
469 pr_debug("Discrete card powered up\n");
470 } else {
471 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
472 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0);
473 pr_debug("Discrete card powered down\n");
474 }
475
476 gmux_data->power_state = state;
477
478 if (gmux_data->gpe >= 0 &&
479 !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done,
480 msecs_to_jiffies(200)))
481 pr_warn("Timeout waiting for gmux switch to complete\n");
482
483 return 0;
484}
485
486static int gmux_set_power_state(enum vga_switcheroo_client_id id,
487 enum vga_switcheroo_state state)
488{
489 if (id == VGA_SWITCHEROO_IGD)
490 return 0;
491
492 return gmux_set_discrete_state(apple_gmux_data, state);
493}
494
495static enum vga_switcheroo_client_id gmux_get_client_id(struct pci_dev *pdev)
496{
497
498
499
500
501 if (pdev->vendor == PCI_VENDOR_ID_INTEL)
502 return VGA_SWITCHEROO_IGD;
503 else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
504 pdev->device == 0x0863)
505 return VGA_SWITCHEROO_IGD;
506 else
507 return VGA_SWITCHEROO_DIS;
508}
509
510static const struct vga_switcheroo_handler gmux_handler_indexed = {
511 .switchto = gmux_switchto,
512 .power_state = gmux_set_power_state,
513 .get_client_id = gmux_get_client_id,
514};
515
516static const struct vga_switcheroo_handler gmux_handler_classic = {
517 .switchto = gmux_switchto,
518 .switch_ddc = gmux_switch_ddc,
519 .power_state = gmux_set_power_state,
520 .get_client_id = gmux_get_client_id,
521};
522
523
524
525
526
527
528
529
530
531
532
533static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
534{
535 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
536 GMUX_INTERRUPT_DISABLE);
537}
538
539static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data)
540{
541 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
542 GMUX_INTERRUPT_ENABLE);
543}
544
545static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data)
546{
547 return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS);
548}
549
550static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
551{
552 u8 status;
553
554
555 status = gmux_interrupt_get_status(gmux_data);
556 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
557}
558
559static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
560{
561 u8 status;
562 struct pnp_dev *pnp = (struct pnp_dev *)context;
563 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
564
565 status = gmux_interrupt_get_status(gmux_data);
566 gmux_disable_interrupts(gmux_data);
567 pr_debug("Notify handler called: status %d\n", status);
568
569 gmux_clear_interrupts(gmux_data);
570 gmux_enable_interrupts(gmux_data);
571
572 if (status & GMUX_INTERRUPT_STATUS_POWER)
573 complete(&gmux_data->powerchange_done);
574}
575
576static int gmux_suspend(struct device *dev)
577{
578 struct pnp_dev *pnp = to_pnp_dev(dev);
579 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
580
581 gmux_disable_interrupts(gmux_data);
582 return 0;
583}
584
585static int gmux_resume(struct device *dev)
586{
587 struct pnp_dev *pnp = to_pnp_dev(dev);
588 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
589
590 gmux_enable_interrupts(gmux_data);
591 gmux_write_switch_state(gmux_data);
592 if (gmux_data->power_state == VGA_SWITCHEROO_OFF)
593 gmux_set_discrete_state(gmux_data, gmux_data->power_state);
594 return 0;
595}
596
597static int is_thunderbolt(struct device *dev, void *data)
598{
599 return to_pci_dev(dev)->is_thunderbolt;
600}
601
602static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
603{
604 struct apple_gmux_data *gmux_data;
605 struct resource *res;
606 struct backlight_properties props;
607 struct backlight_device *bdev;
608 u8 ver_major, ver_minor, ver_release;
609 int ret = -ENXIO;
610 acpi_status status;
611 unsigned long long gpe;
612
613 if (apple_gmux_data)
614 return -EBUSY;
615
616 gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
617 if (!gmux_data)
618 return -ENOMEM;
619 pnp_set_drvdata(pnp, gmux_data);
620
621 res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
622 if (!res) {
623 pr_err("Failed to find gmux I/O resource\n");
624 goto err_free;
625 }
626
627 gmux_data->iostart = res->start;
628 gmux_data->iolen = res->end - res->start;
629
630 if (gmux_data->iolen < GMUX_MIN_IO_LEN) {
631 pr_err("gmux I/O region too small (%lu < %u)\n",
632 gmux_data->iolen, GMUX_MIN_IO_LEN);
633 goto err_free;
634 }
635
636 if (!request_region(gmux_data->iostart, gmux_data->iolen,
637 "Apple gmux")) {
638 pr_err("gmux I/O already in use\n");
639 goto err_free;
640 }
641
642
643
644
645
646
647
648 ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
649 ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
650 ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
651 if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
652 if (gmux_is_indexed(gmux_data)) {
653 u32 version;
654 mutex_init(&gmux_data->index_lock);
655 gmux_data->indexed = true;
656 version = gmux_read32(gmux_data,
657 GMUX_PORT_VERSION_MAJOR);
658 ver_major = (version >> 24) & 0xff;
659 ver_minor = (version >> 16) & 0xff;
660 ver_release = (version >> 8) & 0xff;
661 } else {
662 pr_info("gmux device not present\n");
663 ret = -ENODEV;
664 goto err_release;
665 }
666 }
667 pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
668 ver_release, (gmux_data->indexed ? "indexed" : "classic"));
669
670 memset(&props, 0, sizeof(props));
671 props.type = BACKLIGHT_PLATFORM;
672 props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
673
674
675
676
677
678
679
680 if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
681 props.max_brightness = GMUX_MAX_BRIGHTNESS;
682
683 bdev = backlight_device_register("gmux_backlight", &pnp->dev,
684 gmux_data, &gmux_bl_ops, &props);
685 if (IS_ERR(bdev)) {
686 ret = PTR_ERR(bdev);
687 goto err_release;
688 }
689
690 gmux_data->bdev = bdev;
691 bdev->props.brightness = gmux_get_brightness(bdev);
692 backlight_update_status(bdev);
693
694
695
696
697
698
699
700 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
701 apple_bl_unregister();
702
703 gmux_data->power_state = VGA_SWITCHEROO_ON;
704
705 gmux_data->dhandle = ACPI_HANDLE(&pnp->dev);
706 if (!gmux_data->dhandle) {
707 pr_err("Cannot find acpi handle for pnp device %s\n",
708 dev_name(&pnp->dev));
709 ret = -ENODEV;
710 goto err_notify;
711 }
712
713 status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe);
714 if (ACPI_SUCCESS(status)) {
715 gmux_data->gpe = (int)gpe;
716
717 status = acpi_install_notify_handler(gmux_data->dhandle,
718 ACPI_DEVICE_NOTIFY,
719 &gmux_notify_handler, pnp);
720 if (ACPI_FAILURE(status)) {
721 pr_err("Install notify handler failed: %s\n",
722 acpi_format_exception(status));
723 ret = -ENODEV;
724 goto err_notify;
725 }
726
727 status = acpi_enable_gpe(NULL, gmux_data->gpe);
728 if (ACPI_FAILURE(status)) {
729 pr_err("Cannot enable gpe: %s\n",
730 acpi_format_exception(status));
731 goto err_enable_gpe;
732 }
733 } else {
734 pr_warn("No GPE found for gmux\n");
735 gmux_data->gpe = -1;
736 }
737
738
739
740
741
742 gmux_data->external_switchable =
743 !bus_for_each_dev(&pci_bus_type, NULL, NULL, is_thunderbolt);
744 if (!gmux_data->external_switchable)
745 gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
746
747 apple_gmux_data = gmux_data;
748 init_completion(&gmux_data->powerchange_done);
749 gmux_enable_interrupts(gmux_data);
750 gmux_read_switch_state(gmux_data);
751
752
753
754
755
756
757
758
759 if (gmux_data->indexed)
760 ret = vga_switcheroo_register_handler(&gmux_handler_indexed,
761 VGA_SWITCHEROO_NEEDS_EDP_CONFIG);
762 else
763 ret = vga_switcheroo_register_handler(&gmux_handler_classic,
764 VGA_SWITCHEROO_CAN_SWITCH_DDC);
765 if (ret) {
766 pr_err("Failed to register vga_switcheroo handler\n");
767 goto err_register_handler;
768 }
769
770 return 0;
771
772err_register_handler:
773 gmux_disable_interrupts(gmux_data);
774 apple_gmux_data = NULL;
775 if (gmux_data->gpe >= 0)
776 acpi_disable_gpe(NULL, gmux_data->gpe);
777err_enable_gpe:
778 if (gmux_data->gpe >= 0)
779 acpi_remove_notify_handler(gmux_data->dhandle,
780 ACPI_DEVICE_NOTIFY,
781 &gmux_notify_handler);
782err_notify:
783 backlight_device_unregister(bdev);
784err_release:
785 release_region(gmux_data->iostart, gmux_data->iolen);
786err_free:
787 kfree(gmux_data);
788 return ret;
789}
790
791static void gmux_remove(struct pnp_dev *pnp)
792{
793 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
794
795 vga_switcheroo_unregister_handler();
796 gmux_disable_interrupts(gmux_data);
797 if (gmux_data->gpe >= 0) {
798 acpi_disable_gpe(NULL, gmux_data->gpe);
799 acpi_remove_notify_handler(gmux_data->dhandle,
800 ACPI_DEVICE_NOTIFY,
801 &gmux_notify_handler);
802 }
803
804 backlight_device_unregister(gmux_data->bdev);
805
806 release_region(gmux_data->iostart, gmux_data->iolen);
807 apple_gmux_data = NULL;
808 kfree(gmux_data);
809
810 acpi_video_register();
811 apple_bl_register();
812}
813
814static const struct pnp_device_id gmux_device_ids[] = {
815 {GMUX_ACPI_HID, 0},
816 {"", 0}
817};
818
819static const struct dev_pm_ops gmux_dev_pm_ops = {
820 .suspend = gmux_suspend,
821 .resume = gmux_resume,
822};
823
824static struct pnp_driver gmux_pnp_driver = {
825 .name = "apple-gmux",
826 .probe = gmux_probe,
827 .remove = gmux_remove,
828 .id_table = gmux_device_ids,
829 .driver = {
830 .pm = &gmux_dev_pm_ops,
831 },
832};
833
834module_pnp_driver(gmux_pnp_driver);
835MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
836MODULE_DESCRIPTION("Apple Gmux Driver");
837MODULE_LICENSE("GPL");
838MODULE_DEVICE_TABLE(pnp, gmux_device_ids);
839