apple-gmux.c 17 KB

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