1 /* 2 * Gmux driver for Apple laptops 3 * 4 * Copyright (C) Canonical Ltd. <seth.forshee@canonical.com> 5 * Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13 14 #include <linux/module.h> 15 #include <linux/kernel.h> 16 #include <linux/init.h> 17 #include <linux/backlight.h> 18 #include <linux/acpi.h> 19 #include <linux/pnp.h> 20 #include <linux/apple_bl.h> 21 #include <linux/slab.h> 22 #include <linux/delay.h> 23 #include <linux/pci.h> 24 #include <linux/vga_switcheroo.h> 25 #include <linux/vgaarb.h> 26 #include <acpi/video.h> 27 #include <asm/io.h> 28 29 struct apple_gmux_data { 30 unsigned long iostart; 31 unsigned long iolen; 32 bool indexed; 33 struct mutex index_lock; 34 35 struct pci_dev *pdev; 36 struct backlight_device *bdev; 37 38 /* switcheroo data */ 39 acpi_handle dhandle; 40 int gpe; 41 enum vga_switcheroo_client_id resume_client_id; 42 enum vga_switcheroo_state power_state; 43 struct completion powerchange_done; 44 }; 45 46 static struct apple_gmux_data *apple_gmux_data; 47 48 /* 49 * gmux port offsets. Many of these are not yet used, but may be in the 50 * future, and it's useful to have them documented here anyhow. 51 */ 52 #define GMUX_PORT_VERSION_MAJOR 0x04 53 #define GMUX_PORT_VERSION_MINOR 0x05 54 #define GMUX_PORT_VERSION_RELEASE 0x06 55 #define GMUX_PORT_SWITCH_DISPLAY 0x10 56 #define GMUX_PORT_SWITCH_GET_DISPLAY 0x11 57 #define GMUX_PORT_INTERRUPT_ENABLE 0x14 58 #define GMUX_PORT_INTERRUPT_STATUS 0x16 59 #define GMUX_PORT_SWITCH_DDC 0x28 60 #define GMUX_PORT_SWITCH_EXTERNAL 0x40 61 #define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41 62 #define GMUX_PORT_DISCRETE_POWER 0x50 63 #define GMUX_PORT_MAX_BRIGHTNESS 0x70 64 #define GMUX_PORT_BRIGHTNESS 0x74 65 #define GMUX_PORT_VALUE 0xc2 66 #define GMUX_PORT_READ 0xd0 67 #define GMUX_PORT_WRITE 0xd4 68 69 #define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) 70 71 #define GMUX_INTERRUPT_ENABLE 0xff 72 #define GMUX_INTERRUPT_DISABLE 0x00 73 74 #define GMUX_INTERRUPT_STATUS_ACTIVE 0 75 #define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0) 76 #define GMUX_INTERRUPT_STATUS_POWER (1 << 2) 77 #define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3) 78 79 #define GMUX_BRIGHTNESS_MASK 0x00ffffff 80 #define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK 81 82 static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port) 83 { 84 return inb(gmux_data->iostart + port); 85 } 86 87 static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port, 88 u8 val) 89 { 90 outb(val, gmux_data->iostart + port); 91 } 92 93 static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port) 94 { 95 return inl(gmux_data->iostart + port); 96 } 97 98 static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port, 99 u32 val) 100 { 101 int i; 102 u8 tmpval; 103 104 for (i = 0; i < 4; i++) { 105 tmpval = (val >> (i * 8)) & 0xff; 106 outb(tmpval, gmux_data->iostart + port + i); 107 } 108 } 109 110 static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data) 111 { 112 int i = 200; 113 u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE); 114 115 while (i && (gwr & 0x01)) { 116 inb(gmux_data->iostart + GMUX_PORT_READ); 117 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE); 118 udelay(100); 119 i--; 120 } 121 122 return !!i; 123 } 124 125 static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data) 126 { 127 int i = 200; 128 u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE); 129 130 while (i && !(gwr & 0x01)) { 131 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE); 132 udelay(100); 133 i--; 134 } 135 136 if (gwr & 0x01) 137 inb(gmux_data->iostart + GMUX_PORT_READ); 138 139 return !!i; 140 } 141 142 static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port) 143 { 144 u8 val; 145 146 mutex_lock(&gmux_data->index_lock); 147 gmux_index_wait_ready(gmux_data); 148 outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ); 149 gmux_index_wait_complete(gmux_data); 150 val = inb(gmux_data->iostart + GMUX_PORT_VALUE); 151 mutex_unlock(&gmux_data->index_lock); 152 153 return val; 154 } 155 156 static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port, 157 u8 val) 158 { 159 mutex_lock(&gmux_data->index_lock); 160 outb(val, gmux_data->iostart + GMUX_PORT_VALUE); 161 gmux_index_wait_ready(gmux_data); 162 outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE); 163 gmux_index_wait_complete(gmux_data); 164 mutex_unlock(&gmux_data->index_lock); 165 } 166 167 static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port) 168 { 169 u32 val; 170 171 mutex_lock(&gmux_data->index_lock); 172 gmux_index_wait_ready(gmux_data); 173 outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ); 174 gmux_index_wait_complete(gmux_data); 175 val = inl(gmux_data->iostart + GMUX_PORT_VALUE); 176 mutex_unlock(&gmux_data->index_lock); 177 178 return val; 179 } 180 181 static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port, 182 u32 val) 183 { 184 int i; 185 u8 tmpval; 186 187 mutex_lock(&gmux_data->index_lock); 188 189 for (i = 0; i < 4; i++) { 190 tmpval = (val >> (i * 8)) & 0xff; 191 outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i); 192 } 193 194 gmux_index_wait_ready(gmux_data); 195 outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE); 196 gmux_index_wait_complete(gmux_data); 197 mutex_unlock(&gmux_data->index_lock); 198 } 199 200 static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port) 201 { 202 if (gmux_data->indexed) 203 return gmux_index_read8(gmux_data, port); 204 else 205 return gmux_pio_read8(gmux_data, port); 206 } 207 208 static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val) 209 { 210 if (gmux_data->indexed) 211 gmux_index_write8(gmux_data, port, val); 212 else 213 gmux_pio_write8(gmux_data, port, val); 214 } 215 216 static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port) 217 { 218 if (gmux_data->indexed) 219 return gmux_index_read32(gmux_data, port); 220 else 221 return gmux_pio_read32(gmux_data, port); 222 } 223 224 static void gmux_write32(struct apple_gmux_data *gmux_data, int port, 225 u32 val) 226 { 227 if (gmux_data->indexed) 228 gmux_index_write32(gmux_data, port, val); 229 else 230 gmux_pio_write32(gmux_data, port, val); 231 } 232 233 static bool gmux_is_indexed(struct apple_gmux_data *gmux_data) 234 { 235 u16 val; 236 237 outb(0xaa, gmux_data->iostart + 0xcc); 238 outb(0x55, gmux_data->iostart + 0xcd); 239 outb(0x00, gmux_data->iostart + 0xce); 240 241 val = inb(gmux_data->iostart + 0xcc) | 242 (inb(gmux_data->iostart + 0xcd) << 8); 243 244 if (val == 0x55aa) 245 return true; 246 247 return false; 248 } 249 250 static int gmux_get_brightness(struct backlight_device *bd) 251 { 252 struct apple_gmux_data *gmux_data = bl_get_data(bd); 253 return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) & 254 GMUX_BRIGHTNESS_MASK; 255 } 256 257 static int gmux_update_status(struct backlight_device *bd) 258 { 259 struct apple_gmux_data *gmux_data = bl_get_data(bd); 260 u32 brightness = bd->props.brightness; 261 262 if (bd->props.state & BL_CORE_SUSPENDED) 263 return 0; 264 265 gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness); 266 267 return 0; 268 } 269 270 static const struct backlight_ops gmux_bl_ops = { 271 .options = BL_CORE_SUSPENDRESUME, 272 .get_brightness = gmux_get_brightness, 273 .update_status = gmux_update_status, 274 }; 275 276 static int gmux_switchto(enum vga_switcheroo_client_id id) 277 { 278 if (id == VGA_SWITCHEROO_IGD) { 279 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1); 280 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); 281 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); 282 } else { 283 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2); 284 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); 285 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); 286 } 287 288 return 0; 289 } 290 291 static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data, 292 enum vga_switcheroo_state state) 293 { 294 reinit_completion(&gmux_data->powerchange_done); 295 296 if (state == VGA_SWITCHEROO_ON) { 297 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); 298 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3); 299 pr_debug("Discrete card powered up\n"); 300 } else { 301 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); 302 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0); 303 pr_debug("Discrete card powered down\n"); 304 } 305 306 gmux_data->power_state = state; 307 308 if (gmux_data->gpe >= 0 && 309 !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done, 310 msecs_to_jiffies(200))) 311 pr_warn("Timeout waiting for gmux switch to complete\n"); 312 313 return 0; 314 } 315 316 static int gmux_set_power_state(enum vga_switcheroo_client_id id, 317 enum vga_switcheroo_state state) 318 { 319 if (id == VGA_SWITCHEROO_IGD) 320 return 0; 321 322 return gmux_set_discrete_state(apple_gmux_data, state); 323 } 324 325 static int gmux_get_client_id(struct pci_dev *pdev) 326 { 327 /* 328 * Early Macbook Pros with switchable graphics use nvidia 329 * integrated graphics. Hardcode that the 9400M is integrated. 330 */ 331 if (pdev->vendor == PCI_VENDOR_ID_INTEL) 332 return VGA_SWITCHEROO_IGD; 333 else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA && 334 pdev->device == 0x0863) 335 return VGA_SWITCHEROO_IGD; 336 else 337 return VGA_SWITCHEROO_DIS; 338 } 339 340 static enum vga_switcheroo_client_id 341 gmux_active_client(struct apple_gmux_data *gmux_data) 342 { 343 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2) 344 return VGA_SWITCHEROO_IGD; 345 346 return VGA_SWITCHEROO_DIS; 347 } 348 349 static struct vga_switcheroo_handler gmux_handler = { 350 .switchto = gmux_switchto, 351 .power_state = gmux_set_power_state, 352 .get_client_id = gmux_get_client_id, 353 }; 354 355 static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data) 356 { 357 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, 358 GMUX_INTERRUPT_DISABLE); 359 } 360 361 static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data) 362 { 363 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE, 364 GMUX_INTERRUPT_ENABLE); 365 } 366 367 static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data) 368 { 369 return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS); 370 } 371 372 static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data) 373 { 374 u8 status; 375 376 /* to clear interrupts write back current status */ 377 status = gmux_interrupt_get_status(gmux_data); 378 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status); 379 } 380 381 static void gmux_notify_handler(acpi_handle device, u32 value, void *context) 382 { 383 u8 status; 384 struct pnp_dev *pnp = (struct pnp_dev *)context; 385 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); 386 387 status = gmux_interrupt_get_status(gmux_data); 388 gmux_disable_interrupts(gmux_data); 389 pr_debug("Notify handler called: status %d\n", status); 390 391 gmux_clear_interrupts(gmux_data); 392 gmux_enable_interrupts(gmux_data); 393 394 if (status & GMUX_INTERRUPT_STATUS_POWER) 395 complete(&gmux_data->powerchange_done); 396 } 397 398 static int gmux_suspend(struct device *dev) 399 { 400 struct pnp_dev *pnp = to_pnp_dev(dev); 401 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); 402 403 gmux_data->resume_client_id = gmux_active_client(gmux_data); 404 gmux_disable_interrupts(gmux_data); 405 return 0; 406 } 407 408 static int gmux_resume(struct device *dev) 409 { 410 struct pnp_dev *pnp = to_pnp_dev(dev); 411 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); 412 413 gmux_enable_interrupts(gmux_data); 414 gmux_switchto(gmux_data->resume_client_id); 415 if (gmux_data->power_state == VGA_SWITCHEROO_OFF) 416 gmux_set_discrete_state(gmux_data, gmux_data->power_state); 417 return 0; 418 } 419 420 static struct pci_dev *gmux_get_io_pdev(void) 421 { 422 struct pci_dev *pdev = NULL; 423 424 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev))) { 425 u16 cmd; 426 427 pci_read_config_word(pdev, PCI_COMMAND, &cmd); 428 if (!(cmd & PCI_COMMAND_IO)) 429 continue; 430 431 return pdev; 432 } 433 434 return NULL; 435 } 436 437 static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) 438 { 439 struct apple_gmux_data *gmux_data; 440 struct resource *res; 441 struct backlight_properties props; 442 struct backlight_device *bdev; 443 u8 ver_major, ver_minor, ver_release; 444 int ret = -ENXIO; 445 acpi_status status; 446 unsigned long long gpe; 447 struct pci_dev *pdev = NULL; 448 449 if (apple_gmux_data) 450 return -EBUSY; 451 452 gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL); 453 if (!gmux_data) 454 return -ENOMEM; 455 pnp_set_drvdata(pnp, gmux_data); 456 457 res = pnp_get_resource(pnp, IORESOURCE_IO, 0); 458 if (!res) { 459 pr_err("Failed to find gmux I/O resource\n"); 460 goto err_free; 461 } 462 463 gmux_data->iostart = res->start; 464 gmux_data->iolen = res->end - res->start; 465 466 if (gmux_data->iolen < GMUX_MIN_IO_LEN) { 467 pr_err("gmux I/O region too small (%lu < %u)\n", 468 gmux_data->iolen, GMUX_MIN_IO_LEN); 469 goto err_free; 470 } 471 472 if (!request_region(gmux_data->iostart, gmux_data->iolen, 473 "Apple gmux")) { 474 pr_err("gmux I/O already in use\n"); 475 goto err_free; 476 } 477 478 /* 479 * Invalid version information may indicate either that the gmux 480 * device isn't present or that it's a new one that uses indexed 481 * io 482 */ 483 484 ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR); 485 ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR); 486 ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE); 487 if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { 488 if (gmux_is_indexed(gmux_data)) { 489 u32 version; 490 mutex_init(&gmux_data->index_lock); 491 gmux_data->indexed = true; 492 version = gmux_read32(gmux_data, 493 GMUX_PORT_VERSION_MAJOR); 494 ver_major = (version >> 24) & 0xff; 495 ver_minor = (version >> 16) & 0xff; 496 ver_release = (version >> 8) & 0xff; 497 } else { 498 pr_info("gmux device not present or IO disabled\n"); 499 ret = -ENODEV; 500 goto err_release; 501 } 502 } 503 pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor, 504 ver_release, (gmux_data->indexed ? "indexed" : "classic")); 505 506 /* 507 * Apple systems with gmux are EFI based and normally don't use 508 * VGA. In addition changing IO+MEM ownership between IGP and dGPU 509 * disables IO/MEM used for backlight control on some systems. 510 * Lock IO+MEM to GPU with active IO to prevent switch. 511 */ 512 pdev = gmux_get_io_pdev(); 513 if (pdev && vga_tryget(pdev, 514 VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM)) { 515 pr_err("IO+MEM vgaarb-locking for PCI:%s failed\n", 516 pci_name(pdev)); 517 ret = -EBUSY; 518 goto err_release; 519 } else if (pdev) 520 pr_info("locked IO for PCI:%s\n", pci_name(pdev)); 521 gmux_data->pdev = pdev; 522 523 memset(&props, 0, sizeof(props)); 524 props.type = BACKLIGHT_PLATFORM; 525 props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS); 526 527 /* 528 * Currently it's assumed that the maximum brightness is less than 529 * 2^24 for compatibility with old gmux versions. Cap the max 530 * brightness at this value, but print a warning if the hardware 531 * reports something higher so that it can be fixed. 532 */ 533 if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS)) 534 props.max_brightness = GMUX_MAX_BRIGHTNESS; 535 536 bdev = backlight_device_register("gmux_backlight", &pnp->dev, 537 gmux_data, &gmux_bl_ops, &props); 538 if (IS_ERR(bdev)) { 539 ret = PTR_ERR(bdev); 540 goto err_release; 541 } 542 543 gmux_data->bdev = bdev; 544 bdev->props.brightness = gmux_get_brightness(bdev); 545 backlight_update_status(bdev); 546 547 /* 548 * The backlight situation on Macs is complicated. If the gmux is 549 * present it's the best choice, because it always works for 550 * backlight control and supports more levels than other options. 551 * Disable the other backlight choices. 552 */ 553 acpi_video_dmi_promote_vendor(); 554 acpi_video_unregister(); 555 apple_bl_unregister(); 556 557 gmux_data->power_state = VGA_SWITCHEROO_ON; 558 559 gmux_data->dhandle = ACPI_HANDLE(&pnp->dev); 560 if (!gmux_data->dhandle) { 561 pr_err("Cannot find acpi handle for pnp device %s\n", 562 dev_name(&pnp->dev)); 563 ret = -ENODEV; 564 goto err_notify; 565 } 566 567 status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe); 568 if (ACPI_SUCCESS(status)) { 569 gmux_data->gpe = (int)gpe; 570 571 status = acpi_install_notify_handler(gmux_data->dhandle, 572 ACPI_DEVICE_NOTIFY, 573 &gmux_notify_handler, pnp); 574 if (ACPI_FAILURE(status)) { 575 pr_err("Install notify handler failed: %s\n", 576 acpi_format_exception(status)); 577 ret = -ENODEV; 578 goto err_notify; 579 } 580 581 status = acpi_enable_gpe(NULL, gmux_data->gpe); 582 if (ACPI_FAILURE(status)) { 583 pr_err("Cannot enable gpe: %s\n", 584 acpi_format_exception(status)); 585 goto err_enable_gpe; 586 } 587 } else { 588 pr_warn("No GPE found for gmux\n"); 589 gmux_data->gpe = -1; 590 } 591 592 if (vga_switcheroo_register_handler(&gmux_handler)) { 593 ret = -ENODEV; 594 goto err_register_handler; 595 } 596 597 init_completion(&gmux_data->powerchange_done); 598 apple_gmux_data = gmux_data; 599 gmux_enable_interrupts(gmux_data); 600 601 return 0; 602 603 err_register_handler: 604 if (gmux_data->gpe >= 0) 605 acpi_disable_gpe(NULL, gmux_data->gpe); 606 err_enable_gpe: 607 if (gmux_data->gpe >= 0) 608 acpi_remove_notify_handler(gmux_data->dhandle, 609 ACPI_DEVICE_NOTIFY, 610 &gmux_notify_handler); 611 err_notify: 612 backlight_device_unregister(bdev); 613 err_release: 614 if (gmux_data->pdev) 615 vga_put(gmux_data->pdev, 616 VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM); 617 pci_dev_put(pdev); 618 release_region(gmux_data->iostart, gmux_data->iolen); 619 err_free: 620 kfree(gmux_data); 621 return ret; 622 } 623 624 static void gmux_remove(struct pnp_dev *pnp) 625 { 626 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); 627 628 vga_switcheroo_unregister_handler(); 629 gmux_disable_interrupts(gmux_data); 630 if (gmux_data->gpe >= 0) { 631 acpi_disable_gpe(NULL, gmux_data->gpe); 632 acpi_remove_notify_handler(gmux_data->dhandle, 633 ACPI_DEVICE_NOTIFY, 634 &gmux_notify_handler); 635 } 636 637 if (gmux_data->pdev) { 638 vga_put(gmux_data->pdev, 639 VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM); 640 pci_dev_put(gmux_data->pdev); 641 } 642 backlight_device_unregister(gmux_data->bdev); 643 644 release_region(gmux_data->iostart, gmux_data->iolen); 645 apple_gmux_data = NULL; 646 kfree(gmux_data); 647 648 acpi_video_dmi_demote_vendor(); 649 acpi_video_register(); 650 apple_bl_register(); 651 } 652 653 static const struct pnp_device_id gmux_device_ids[] = { 654 {"APP000B", 0}, 655 {"", 0} 656 }; 657 658 static const struct dev_pm_ops gmux_dev_pm_ops = { 659 .suspend = gmux_suspend, 660 .resume = gmux_resume, 661 }; 662 663 static struct pnp_driver gmux_pnp_driver = { 664 .name = "apple-gmux", 665 .probe = gmux_probe, 666 .remove = gmux_remove, 667 .id_table = gmux_device_ids, 668 .driver = { 669 .pm = &gmux_dev_pm_ops, 670 }, 671 }; 672 673 module_pnp_driver(gmux_pnp_driver); 674 MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>"); 675 MODULE_DESCRIPTION("Apple Gmux Driver"); 676 MODULE_LICENSE("GPL"); 677 MODULE_DEVICE_TABLE(pnp, gmux_device_ids); 678