0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0011
0012 #include <linux/module.h>
0013 #include <linux/kernel.h>
0014 #include <linux/init.h>
0015 #include <linux/backlight.h>
0016 #include <linux/acpi.h>
0017 #include <linux/pnp.h>
0018 #include <linux/apple_bl.h>
0019 #include <linux/apple-gmux.h>
0020 #include <linux/slab.h>
0021 #include <linux/delay.h>
0022 #include <linux/pci.h>
0023 #include <linux/vga_switcheroo.h>
0024 #include <acpi/video.h>
0025 #include <asm/io.h>
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047 struct apple_gmux_data {
0048 unsigned long iostart;
0049 unsigned long iolen;
0050 bool indexed;
0051 struct mutex index_lock;
0052
0053 struct backlight_device *bdev;
0054
0055
0056 acpi_handle dhandle;
0057 int gpe;
0058 bool external_switchable;
0059 enum vga_switcheroo_client_id switch_state_display;
0060 enum vga_switcheroo_client_id switch_state_ddc;
0061 enum vga_switcheroo_client_id switch_state_external;
0062 enum vga_switcheroo_state power_state;
0063 struct completion powerchange_done;
0064 };
0065
0066 static struct apple_gmux_data *apple_gmux_data;
0067
0068
0069
0070
0071
0072 #define GMUX_PORT_VERSION_MAJOR 0x04
0073 #define GMUX_PORT_VERSION_MINOR 0x05
0074 #define GMUX_PORT_VERSION_RELEASE 0x06
0075 #define GMUX_PORT_SWITCH_DISPLAY 0x10
0076 #define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
0077 #define GMUX_PORT_INTERRUPT_ENABLE 0x14
0078 #define GMUX_PORT_INTERRUPT_STATUS 0x16
0079 #define GMUX_PORT_SWITCH_DDC 0x28
0080 #define GMUX_PORT_SWITCH_EXTERNAL 0x40
0081 #define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
0082 #define GMUX_PORT_DISCRETE_POWER 0x50
0083 #define GMUX_PORT_MAX_BRIGHTNESS 0x70
0084 #define GMUX_PORT_BRIGHTNESS 0x74
0085 #define GMUX_PORT_VALUE 0xc2
0086 #define GMUX_PORT_READ 0xd0
0087 #define GMUX_PORT_WRITE 0xd4
0088
0089 #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
0090
0091 #define GMUX_INTERRUPT_ENABLE 0xff
0092 #define GMUX_INTERRUPT_DISABLE 0x00
0093
0094 #define GMUX_INTERRUPT_STATUS_ACTIVE 0
0095 #define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0)
0096 #define GMUX_INTERRUPT_STATUS_POWER (1 << 2)
0097 #define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3)
0098
0099 #define GMUX_BRIGHTNESS_MASK 0x00ffffff
0100 #define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
0101
0102 static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
0103 {
0104 return inb(gmux_data->iostart + port);
0105 }
0106
0107 static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port,
0108 u8 val)
0109 {
0110 outb(val, gmux_data->iostart + port);
0111 }
0112
0113 static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port)
0114 {
0115 return inl(gmux_data->iostart + port);
0116 }
0117
0118 static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
0119 u32 val)
0120 {
0121 int i;
0122 u8 tmpval;
0123
0124 for (i = 0; i < 4; i++) {
0125 tmpval = (val >> (i * 8)) & 0xff;
0126 outb(tmpval, gmux_data->iostart + port + i);
0127 }
0128 }
0129
0130 static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data)
0131 {
0132 int i = 200;
0133 u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
0134
0135 while (i && (gwr & 0x01)) {
0136 inb(gmux_data->iostart + GMUX_PORT_READ);
0137 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
0138 udelay(100);
0139 i--;
0140 }
0141
0142 return !!i;
0143 }
0144
0145 static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data)
0146 {
0147 int i = 200;
0148 u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
0149
0150 while (i && !(gwr & 0x01)) {
0151 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
0152 udelay(100);
0153 i--;
0154 }
0155
0156 if (gwr & 0x01)
0157 inb(gmux_data->iostart + GMUX_PORT_READ);
0158
0159 return !!i;
0160 }
0161
0162 static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
0163 {
0164 u8 val;
0165
0166 mutex_lock(&gmux_data->index_lock);
0167 gmux_index_wait_ready(gmux_data);
0168 outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
0169 gmux_index_wait_complete(gmux_data);
0170 val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
0171 mutex_unlock(&gmux_data->index_lock);
0172
0173 return val;
0174 }
0175
0176 static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port,
0177 u8 val)
0178 {
0179 mutex_lock(&gmux_data->index_lock);
0180 outb(val, gmux_data->iostart + GMUX_PORT_VALUE);
0181 gmux_index_wait_ready(gmux_data);
0182 outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
0183 gmux_index_wait_complete(gmux_data);
0184 mutex_unlock(&gmux_data->index_lock);
0185 }
0186
0187 static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
0188 {
0189 u32 val;
0190
0191 mutex_lock(&gmux_data->index_lock);
0192 gmux_index_wait_ready(gmux_data);
0193 outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
0194 gmux_index_wait_complete(gmux_data);
0195 val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
0196 mutex_unlock(&gmux_data->index_lock);
0197
0198 return val;
0199 }
0200
0201 static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
0202 u32 val)
0203 {
0204 int i;
0205 u8 tmpval;
0206
0207 mutex_lock(&gmux_data->index_lock);
0208
0209 for (i = 0; i < 4; i++) {
0210 tmpval = (val >> (i * 8)) & 0xff;
0211 outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i);
0212 }
0213
0214 gmux_index_wait_ready(gmux_data);
0215 outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
0216 gmux_index_wait_complete(gmux_data);
0217 mutex_unlock(&gmux_data->index_lock);
0218 }
0219
0220 static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
0221 {
0222 if (gmux_data->indexed)
0223 return gmux_index_read8(gmux_data, port);
0224 else
0225 return gmux_pio_read8(gmux_data, port);
0226 }
0227
0228 static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
0229 {
0230 if (gmux_data->indexed)
0231 gmux_index_write8(gmux_data, port, val);
0232 else
0233 gmux_pio_write8(gmux_data, port, val);
0234 }
0235
0236 static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
0237 {
0238 if (gmux_data->indexed)
0239 return gmux_index_read32(gmux_data, port);
0240 else
0241 return gmux_pio_read32(gmux_data, port);
0242 }
0243
0244 static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
0245 u32 val)
0246 {
0247 if (gmux_data->indexed)
0248 gmux_index_write32(gmux_data, port, val);
0249 else
0250 gmux_pio_write32(gmux_data, port, val);
0251 }
0252
0253 static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
0254 {
0255 u16 val;
0256
0257 outb(0xaa, gmux_data->iostart + 0xcc);
0258 outb(0x55, gmux_data->iostart + 0xcd);
0259 outb(0x00, gmux_data->iostart + 0xce);
0260
0261 val = inb(gmux_data->iostart + 0xcc) |
0262 (inb(gmux_data->iostart + 0xcd) << 8);
0263
0264 if (val == 0x55aa)
0265 return true;
0266
0267 return false;
0268 }
0269
0270
0271
0272
0273
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283
0284 static int gmux_get_brightness(struct backlight_device *bd)
0285 {
0286 struct apple_gmux_data *gmux_data = bl_get_data(bd);
0287 return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) &
0288 GMUX_BRIGHTNESS_MASK;
0289 }
0290
0291 static int gmux_update_status(struct backlight_device *bd)
0292 {
0293 struct apple_gmux_data *gmux_data = bl_get_data(bd);
0294 u32 brightness = backlight_get_brightness(bd);
0295
0296 gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
0297
0298 return 0;
0299 }
0300
0301 static const struct backlight_ops gmux_bl_ops = {
0302 .options = BL_CORE_SUSPENDRESUME,
0303 .get_brightness = gmux_get_brightness,
0304 .update_status = gmux_update_status,
0305 };
0306
0307
0308
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
0327
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
0346
0347
0348
0349
0350
0351
0352
0353
0354
0355
0356
0357
0358
0359
0360
0361
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383 static void gmux_read_switch_state(struct apple_gmux_data *gmux_data)
0384 {
0385 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DDC) == 1)
0386 gmux_data->switch_state_ddc = VGA_SWITCHEROO_IGD;
0387 else
0388 gmux_data->switch_state_ddc = VGA_SWITCHEROO_DIS;
0389
0390 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2)
0391 gmux_data->switch_state_display = VGA_SWITCHEROO_IGD;
0392 else
0393 gmux_data->switch_state_display = VGA_SWITCHEROO_DIS;
0394
0395 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL) == 2)
0396 gmux_data->switch_state_external = VGA_SWITCHEROO_IGD;
0397 else
0398 gmux_data->switch_state_external = VGA_SWITCHEROO_DIS;
0399 }
0400
0401 static void gmux_write_switch_state(struct apple_gmux_data *gmux_data)
0402 {
0403 if (gmux_data->switch_state_ddc == VGA_SWITCHEROO_IGD)
0404 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DDC, 1);
0405 else
0406 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DDC, 2);
0407
0408 if (gmux_data->switch_state_display == VGA_SWITCHEROO_IGD)
0409 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2);
0410 else
0411 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3);
0412
0413 if (gmux_data->switch_state_external == VGA_SWITCHEROO_IGD)
0414 gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2);
0415 else
0416 gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
0417 }
0418
0419 static int gmux_switchto(enum vga_switcheroo_client_id id)
0420 {
0421 apple_gmux_data->switch_state_ddc = id;
0422 apple_gmux_data->switch_state_display = id;
0423 if (apple_gmux_data->external_switchable)
0424 apple_gmux_data->switch_state_external = id;
0425
0426 gmux_write_switch_state(apple_gmux_data);
0427
0428 return 0;
0429 }
0430
0431 static int gmux_switch_ddc(enum vga_switcheroo_client_id id)
0432 {
0433 enum vga_switcheroo_client_id old_ddc_owner =
0434 apple_gmux_data->switch_state_ddc;
0435
0436 if (id == old_ddc_owner)
0437 return id;
0438
0439 pr_debug("Switching DDC from %d to %d\n", old_ddc_owner, id);
0440 apple_gmux_data->switch_state_ddc = id;
0441
0442 if (id == VGA_SWITCHEROO_IGD)
0443 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1);
0444 else
0445 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2);
0446
0447 return old_ddc_owner;
0448 }
0449
0450
0451
0452
0453
0454
0455
0456
0457
0458 static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
0459 enum vga_switcheroo_state state)
0460 {
0461 reinit_completion(&gmux_data->powerchange_done);
0462
0463 if (state == VGA_SWITCHEROO_ON) {
0464 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
0465 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3);
0466 pr_debug("Discrete card powered up\n");
0467 } else {
0468 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
0469 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0);
0470 pr_debug("Discrete card powered down\n");
0471 }
0472
0473 gmux_data->power_state = state;
0474
0475 if (gmux_data->gpe >= 0 &&
0476 !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done,
0477 msecs_to_jiffies(200)))
0478 pr_warn("Timeout waiting for gmux switch to complete\n");
0479
0480 return 0;
0481 }
0482
0483 static int gmux_set_power_state(enum vga_switcheroo_client_id id,
0484 enum vga_switcheroo_state state)
0485 {
0486 if (id == VGA_SWITCHEROO_IGD)
0487 return 0;
0488
0489 return gmux_set_discrete_state(apple_gmux_data, state);
0490 }
0491
0492 static enum vga_switcheroo_client_id gmux_get_client_id(struct pci_dev *pdev)
0493 {
0494
0495
0496
0497
0498 if (pdev->vendor == PCI_VENDOR_ID_INTEL)
0499 return VGA_SWITCHEROO_IGD;
0500 else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
0501 pdev->device == 0x0863)
0502 return VGA_SWITCHEROO_IGD;
0503 else
0504 return VGA_SWITCHEROO_DIS;
0505 }
0506
0507 static const struct vga_switcheroo_handler gmux_handler_indexed = {
0508 .switchto = gmux_switchto,
0509 .power_state = gmux_set_power_state,
0510 .get_client_id = gmux_get_client_id,
0511 };
0512
0513 static const struct vga_switcheroo_handler gmux_handler_classic = {
0514 .switchto = gmux_switchto,
0515 .switch_ddc = gmux_switch_ddc,
0516 .power_state = gmux_set_power_state,
0517 .get_client_id = gmux_get_client_id,
0518 };
0519
0520
0521
0522
0523
0524
0525
0526
0527
0528
0529
0530 static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
0531 {
0532 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
0533 GMUX_INTERRUPT_DISABLE);
0534 }
0535
0536 static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data)
0537 {
0538 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
0539 GMUX_INTERRUPT_ENABLE);
0540 }
0541
0542 static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data)
0543 {
0544 return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS);
0545 }
0546
0547 static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
0548 {
0549 u8 status;
0550
0551
0552 status = gmux_interrupt_get_status(gmux_data);
0553 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
0554 }
0555
0556 static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
0557 {
0558 u8 status;
0559 struct pnp_dev *pnp = (struct pnp_dev *)context;
0560 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
0561
0562 status = gmux_interrupt_get_status(gmux_data);
0563 gmux_disable_interrupts(gmux_data);
0564 pr_debug("Notify handler called: status %d\n", status);
0565
0566 gmux_clear_interrupts(gmux_data);
0567 gmux_enable_interrupts(gmux_data);
0568
0569 if (status & GMUX_INTERRUPT_STATUS_POWER)
0570 complete(&gmux_data->powerchange_done);
0571 }
0572
0573 static int gmux_suspend(struct device *dev)
0574 {
0575 struct pnp_dev *pnp = to_pnp_dev(dev);
0576 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
0577
0578 gmux_disable_interrupts(gmux_data);
0579 return 0;
0580 }
0581
0582 static int gmux_resume(struct device *dev)
0583 {
0584 struct pnp_dev *pnp = to_pnp_dev(dev);
0585 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
0586
0587 gmux_enable_interrupts(gmux_data);
0588 gmux_write_switch_state(gmux_data);
0589 if (gmux_data->power_state == VGA_SWITCHEROO_OFF)
0590 gmux_set_discrete_state(gmux_data, gmux_data->power_state);
0591 return 0;
0592 }
0593
0594 static int is_thunderbolt(struct device *dev, void *data)
0595 {
0596 return to_pci_dev(dev)->is_thunderbolt;
0597 }
0598
0599 static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
0600 {
0601 struct apple_gmux_data *gmux_data;
0602 struct resource *res;
0603 struct backlight_properties props;
0604 struct backlight_device *bdev;
0605 u8 ver_major, ver_minor, ver_release;
0606 int ret = -ENXIO;
0607 acpi_status status;
0608 unsigned long long gpe;
0609
0610 if (apple_gmux_data)
0611 return -EBUSY;
0612
0613 gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
0614 if (!gmux_data)
0615 return -ENOMEM;
0616 pnp_set_drvdata(pnp, gmux_data);
0617
0618 res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
0619 if (!res) {
0620 pr_err("Failed to find gmux I/O resource\n");
0621 goto err_free;
0622 }
0623
0624 gmux_data->iostart = res->start;
0625 gmux_data->iolen = resource_size(res);
0626
0627 if (gmux_data->iolen < GMUX_MIN_IO_LEN) {
0628 pr_err("gmux I/O region too small (%lu < %u)\n",
0629 gmux_data->iolen, GMUX_MIN_IO_LEN);
0630 goto err_free;
0631 }
0632
0633 if (!request_region(gmux_data->iostart, gmux_data->iolen,
0634 "Apple gmux")) {
0635 pr_err("gmux I/O already in use\n");
0636 goto err_free;
0637 }
0638
0639
0640
0641
0642
0643
0644
0645 ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
0646 ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
0647 ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
0648 if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
0649 if (gmux_is_indexed(gmux_data)) {
0650 u32 version;
0651 mutex_init(&gmux_data->index_lock);
0652 gmux_data->indexed = true;
0653 version = gmux_read32(gmux_data,
0654 GMUX_PORT_VERSION_MAJOR);
0655 ver_major = (version >> 24) & 0xff;
0656 ver_minor = (version >> 16) & 0xff;
0657 ver_release = (version >> 8) & 0xff;
0658 } else {
0659 pr_info("gmux device not present\n");
0660 ret = -ENODEV;
0661 goto err_release;
0662 }
0663 }
0664 pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
0665 ver_release, (gmux_data->indexed ? "indexed" : "classic"));
0666
0667 memset(&props, 0, sizeof(props));
0668 props.type = BACKLIGHT_PLATFORM;
0669 props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
0670
0671
0672
0673
0674
0675
0676
0677 if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
0678 props.max_brightness = GMUX_MAX_BRIGHTNESS;
0679
0680 bdev = backlight_device_register("gmux_backlight", &pnp->dev,
0681 gmux_data, &gmux_bl_ops, &props);
0682 if (IS_ERR(bdev)) {
0683 ret = PTR_ERR(bdev);
0684 goto err_release;
0685 }
0686
0687 gmux_data->bdev = bdev;
0688 bdev->props.brightness = gmux_get_brightness(bdev);
0689 backlight_update_status(bdev);
0690
0691
0692
0693
0694
0695
0696
0697 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
0698 apple_bl_unregister();
0699
0700 gmux_data->power_state = VGA_SWITCHEROO_ON;
0701
0702 gmux_data->dhandle = ACPI_HANDLE(&pnp->dev);
0703 if (!gmux_data->dhandle) {
0704 pr_err("Cannot find acpi handle for pnp device %s\n",
0705 dev_name(&pnp->dev));
0706 ret = -ENODEV;
0707 goto err_notify;
0708 }
0709
0710 status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe);
0711 if (ACPI_SUCCESS(status)) {
0712 gmux_data->gpe = (int)gpe;
0713
0714 status = acpi_install_notify_handler(gmux_data->dhandle,
0715 ACPI_DEVICE_NOTIFY,
0716 &gmux_notify_handler, pnp);
0717 if (ACPI_FAILURE(status)) {
0718 pr_err("Install notify handler failed: %s\n",
0719 acpi_format_exception(status));
0720 ret = -ENODEV;
0721 goto err_notify;
0722 }
0723
0724 status = acpi_enable_gpe(NULL, gmux_data->gpe);
0725 if (ACPI_FAILURE(status)) {
0726 pr_err("Cannot enable gpe: %s\n",
0727 acpi_format_exception(status));
0728 goto err_enable_gpe;
0729 }
0730 } else {
0731 pr_warn("No GPE found for gmux\n");
0732 gmux_data->gpe = -1;
0733 }
0734
0735
0736
0737
0738
0739 gmux_data->external_switchable =
0740 !bus_for_each_dev(&pci_bus_type, NULL, NULL, is_thunderbolt);
0741 if (!gmux_data->external_switchable)
0742 gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
0743
0744 apple_gmux_data = gmux_data;
0745 init_completion(&gmux_data->powerchange_done);
0746 gmux_enable_interrupts(gmux_data);
0747 gmux_read_switch_state(gmux_data);
0748
0749
0750
0751
0752
0753
0754
0755
0756 if (gmux_data->indexed)
0757 ret = vga_switcheroo_register_handler(&gmux_handler_indexed,
0758 VGA_SWITCHEROO_NEEDS_EDP_CONFIG);
0759 else
0760 ret = vga_switcheroo_register_handler(&gmux_handler_classic,
0761 VGA_SWITCHEROO_CAN_SWITCH_DDC);
0762 if (ret) {
0763 pr_err("Failed to register vga_switcheroo handler\n");
0764 goto err_register_handler;
0765 }
0766
0767 return 0;
0768
0769 err_register_handler:
0770 gmux_disable_interrupts(gmux_data);
0771 apple_gmux_data = NULL;
0772 if (gmux_data->gpe >= 0)
0773 acpi_disable_gpe(NULL, gmux_data->gpe);
0774 err_enable_gpe:
0775 if (gmux_data->gpe >= 0)
0776 acpi_remove_notify_handler(gmux_data->dhandle,
0777 ACPI_DEVICE_NOTIFY,
0778 &gmux_notify_handler);
0779 err_notify:
0780 backlight_device_unregister(bdev);
0781 err_release:
0782 release_region(gmux_data->iostart, gmux_data->iolen);
0783 err_free:
0784 kfree(gmux_data);
0785 return ret;
0786 }
0787
0788 static void gmux_remove(struct pnp_dev *pnp)
0789 {
0790 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
0791
0792 vga_switcheroo_unregister_handler();
0793 gmux_disable_interrupts(gmux_data);
0794 if (gmux_data->gpe >= 0) {
0795 acpi_disable_gpe(NULL, gmux_data->gpe);
0796 acpi_remove_notify_handler(gmux_data->dhandle,
0797 ACPI_DEVICE_NOTIFY,
0798 &gmux_notify_handler);
0799 }
0800
0801 backlight_device_unregister(gmux_data->bdev);
0802
0803 release_region(gmux_data->iostart, gmux_data->iolen);
0804 apple_gmux_data = NULL;
0805 kfree(gmux_data);
0806
0807 acpi_video_register();
0808 apple_bl_register();
0809 }
0810
0811 static const struct pnp_device_id gmux_device_ids[] = {
0812 {GMUX_ACPI_HID, 0},
0813 {"", 0}
0814 };
0815
0816 static const struct dev_pm_ops gmux_dev_pm_ops = {
0817 .suspend = gmux_suspend,
0818 .resume = gmux_resume,
0819 };
0820
0821 static struct pnp_driver gmux_pnp_driver = {
0822 .name = "apple-gmux",
0823 .probe = gmux_probe,
0824 .remove = gmux_remove,
0825 .id_table = gmux_device_ids,
0826 .driver = {
0827 .pm = &gmux_dev_pm_ops,
0828 },
0829 };
0830
0831 module_pnp_driver(gmux_pnp_driver);
0832 MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
0833 MODULE_DESCRIPTION("Apple Gmux Driver");
0834 MODULE_LICENSE("GPL");
0835 MODULE_DEVICE_TABLE(pnp, gmux_device_ids);