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