blob: 1384a393f2f7a1f28523f87c344dd78b4fae6a55 [file] [log] [blame]
Seth Forshee917ee752012-03-16 14:41:22 -05001/*
2 * Gmux driver for Apple laptops
3 *
4 * Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
Andreas Heider76b487d2012-08-17 11:17:04 -05005 * Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de>
Lukas Wunner3d7b75f2016-01-11 00:08:35 +01006 * Copyright (C) 2015 Lukas Wunner <lukas@wunner.de>
Seth Forshee917ee752012-03-16 14:41:22 -05007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
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/slab.h>
Matthew Garrett96ff7052012-08-09 13:45:01 -040023#include <linux/delay.h>
Andreas Heider76b487d2012-08-17 11:17:04 -050024#include <linux/pci.h>
25#include <linux/vga_switcheroo.h>
Bruno Prémont4eebd5a2015-03-11 22:34:45 +010026#include <linux/vgaarb.h>
Seth Forshee917ee752012-03-16 14:41:22 -050027#include <acpi/video.h>
28#include <asm/io.h>
29
Lukas Wunner3d7b75f2016-01-11 00:08:35 +010030/**
31 * DOC: Overview
32 *
33 * :1: http://www.latticesemi.com/en/Products/FPGAandCPLD/LatticeXP2.aspx
34 * :2: http://www.renesas.com/products/mpumcu/h8s/h8s2100/h8s2113/index.jsp
35 *
36 * gmux is a microcontroller built into the MacBook Pro to support dual GPUs:
37 * A {1}[Lattice XP2] on pre-retinas, a {2}[Renesas R4F2113] on retinas.
38 *
39 * (The MacPro6,1 2013 also has a gmux, however it is unclear why since it has
40 * dual GPUs but no built-in display.)
41 *
42 * gmux is connected to the LPC bus of the southbridge. Its I/O ports are
43 * accessed differently depending on the microcontroller: Driver functions
44 * to access a pre-retina gmux are infixed `_pio_`, those for a retina gmux
45 * are infixed `_index_`.
46 */
47
Seth Forshee917ee752012-03-16 14:41:22 -050048struct apple_gmux_data {
49 unsigned long iostart;
50 unsigned long iolen;
Matthew Garrett96ff7052012-08-09 13:45:01 -040051 bool indexed;
52 struct mutex index_lock;
Seth Forshee917ee752012-03-16 14:41:22 -050053
Bruno Prémont4eebd5a2015-03-11 22:34:45 +010054 struct pci_dev *pdev;
Seth Forshee917ee752012-03-16 14:41:22 -050055 struct backlight_device *bdev;
Andreas Heider76b487d2012-08-17 11:17:04 -050056
57 /* switcheroo data */
58 acpi_handle dhandle;
59 int gpe;
Lukas Wunner3e463042016-01-11 20:09:20 +010060 enum vga_switcheroo_client_id switch_state_display;
61 enum vga_switcheroo_client_id switch_state_ddc;
62 enum vga_switcheroo_client_id switch_state_external;
Andreas Heider76b487d2012-08-17 11:17:04 -050063 enum vga_switcheroo_state power_state;
64 struct completion powerchange_done;
Seth Forshee917ee752012-03-16 14:41:22 -050065};
66
Andreas Heider76b487d2012-08-17 11:17:04 -050067static struct apple_gmux_data *apple_gmux_data;
68
Seth Forshee917ee752012-03-16 14:41:22 -050069/*
70 * gmux port offsets. Many of these are not yet used, but may be in the
71 * future, and it's useful to have them documented here anyhow.
72 */
73#define GMUX_PORT_VERSION_MAJOR 0x04
74#define GMUX_PORT_VERSION_MINOR 0x05
75#define GMUX_PORT_VERSION_RELEASE 0x06
76#define GMUX_PORT_SWITCH_DISPLAY 0x10
77#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
78#define GMUX_PORT_INTERRUPT_ENABLE 0x14
79#define GMUX_PORT_INTERRUPT_STATUS 0x16
80#define GMUX_PORT_SWITCH_DDC 0x28
81#define GMUX_PORT_SWITCH_EXTERNAL 0x40
82#define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
83#define GMUX_PORT_DISCRETE_POWER 0x50
84#define GMUX_PORT_MAX_BRIGHTNESS 0x70
85#define GMUX_PORT_BRIGHTNESS 0x74
Matthew Garrett96ff7052012-08-09 13:45:01 -040086#define GMUX_PORT_VALUE 0xc2
87#define GMUX_PORT_READ 0xd0
88#define GMUX_PORT_WRITE 0xd4
Seth Forshee917ee752012-03-16 14:41:22 -050089
90#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
91
92#define GMUX_INTERRUPT_ENABLE 0xff
93#define GMUX_INTERRUPT_DISABLE 0x00
94
95#define GMUX_INTERRUPT_STATUS_ACTIVE 0
96#define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0)
97#define GMUX_INTERRUPT_STATUS_POWER (1 << 2)
98#define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3)
99
100#define GMUX_BRIGHTNESS_MASK 0x00ffffff
101#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
102
Matthew Garrett96ff7052012-08-09 13:45:01 -0400103static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
Seth Forshee917ee752012-03-16 14:41:22 -0500104{
105 return inb(gmux_data->iostart + port);
106}
107
Matthew Garrett96ff7052012-08-09 13:45:01 -0400108static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port,
Seth Forshee917ee752012-03-16 14:41:22 -0500109 u8 val)
110{
111 outb(val, gmux_data->iostart + port);
112}
113
Matthew Garrett96ff7052012-08-09 13:45:01 -0400114static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port)
Seth Forshee917ee752012-03-16 14:41:22 -0500115{
116 return inl(gmux_data->iostart + port);
117}
118
Matthew Garrett96ff7052012-08-09 13:45:01 -0400119static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
120 u32 val)
Matthew Garrett7e30ed62012-08-09 12:47:00 -0400121{
122 int i;
123 u8 tmpval;
124
125 for (i = 0; i < 4; i++) {
126 tmpval = (val >> (i * 8)) & 0xff;
Seth Forsheee6d9d3d2012-08-21 21:56:49 -0500127 outb(tmpval, gmux_data->iostart + port + i);
Matthew Garrett7e30ed62012-08-09 12:47:00 -0400128 }
129}
130
Matthew Garrett96ff7052012-08-09 13:45:01 -0400131static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data)
132{
133 int i = 200;
134 u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
135
136 while (i && (gwr & 0x01)) {
137 inb(gmux_data->iostart + GMUX_PORT_READ);
138 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
139 udelay(100);
140 i--;
141 }
142
143 return !!i;
144}
145
146static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data)
147{
148 int i = 200;
149 u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
150
151 while (i && !(gwr & 0x01)) {
152 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
153 udelay(100);
154 i--;
155 }
156
157 if (gwr & 0x01)
158 inb(gmux_data->iostart + GMUX_PORT_READ);
159
160 return !!i;
161}
162
163static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
164{
165 u8 val;
166
167 mutex_lock(&gmux_data->index_lock);
Matthew Garrett96ff7052012-08-09 13:45:01 -0400168 gmux_index_wait_ready(gmux_data);
Bernhard Froemelc5a50522012-08-25 10:30:48 +0200169 outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
170 gmux_index_wait_complete(gmux_data);
Matthew Garrett96ff7052012-08-09 13:45:01 -0400171 val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
172 mutex_unlock(&gmux_data->index_lock);
173
174 return val;
175}
176
177static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port,
178 u8 val)
179{
180 mutex_lock(&gmux_data->index_lock);
181 outb(val, gmux_data->iostart + GMUX_PORT_VALUE);
182 gmux_index_wait_ready(gmux_data);
183 outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
184 gmux_index_wait_complete(gmux_data);
185 mutex_unlock(&gmux_data->index_lock);
186}
187
188static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
189{
190 u32 val;
191
192 mutex_lock(&gmux_data->index_lock);
Matthew Garrett96ff7052012-08-09 13:45:01 -0400193 gmux_index_wait_ready(gmux_data);
Bernhard Froemelc5a50522012-08-25 10:30:48 +0200194 outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
195 gmux_index_wait_complete(gmux_data);
Matthew Garrett96ff7052012-08-09 13:45:01 -0400196 val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
197 mutex_unlock(&gmux_data->index_lock);
198
199 return val;
200}
201
202static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
203 u32 val)
204{
205 int i;
206 u8 tmpval;
207
208 mutex_lock(&gmux_data->index_lock);
209
210 for (i = 0; i < 4; i++) {
211 tmpval = (val >> (i * 8)) & 0xff;
212 outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i);
213 }
214
215 gmux_index_wait_ready(gmux_data);
216 outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
217 gmux_index_wait_complete(gmux_data);
218 mutex_unlock(&gmux_data->index_lock);
219}
220
221static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
222{
223 if (gmux_data->indexed)
224 return gmux_index_read8(gmux_data, port);
225 else
226 return gmux_pio_read8(gmux_data, port);
227}
228
229static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
230{
231 if (gmux_data->indexed)
232 gmux_index_write8(gmux_data, port, val);
233 else
234 gmux_pio_write8(gmux_data, port, val);
235}
236
237static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
238{
239 if (gmux_data->indexed)
240 return gmux_index_read32(gmux_data, port);
241 else
242 return gmux_pio_read32(gmux_data, port);
243}
244
245static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
246 u32 val)
247{
248 if (gmux_data->indexed)
249 gmux_index_write32(gmux_data, port, val);
250 else
251 gmux_pio_write32(gmux_data, port, val);
252}
253
254static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
255{
256 u16 val;
257
258 outb(0xaa, gmux_data->iostart + 0xcc);
259 outb(0x55, gmux_data->iostart + 0xcd);
260 outb(0x00, gmux_data->iostart + 0xce);
261
262 val = inb(gmux_data->iostart + 0xcc) |
263 (inb(gmux_data->iostart + 0xcd) << 8);
264
265 if (val == 0x55aa)
266 return true;
267
268 return false;
269}
270
Lukas Wunner3d7b75f2016-01-11 00:08:35 +0100271/**
272 * DOC: Backlight control
273 *
274 * :3: http://www.ti.com/lit/ds/symlink/lp8543.pdf
275 * :4: http://www.ti.com/lit/ds/symlink/lp8545.pdf
276 *
277 * On single GPU MacBooks, the PWM signal for the backlight is generated by
278 * the GPU. On dual GPU MacBook Pros by contrast, either GPU may be suspended
279 * to conserve energy. Hence the PWM signal needs to be generated by a separate
280 * backlight driver which is controlled by gmux. The earliest generation
281 * MBP5 2008/09 uses a {3}[TI LP8543] backlight driver. All newer models
282 * use a {4}[TI LP8545].
283 */
284
Seth Forshee917ee752012-03-16 14:41:22 -0500285static int gmux_get_brightness(struct backlight_device *bd)
286{
287 struct apple_gmux_data *gmux_data = bl_get_data(bd);
288 return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) &
289 GMUX_BRIGHTNESS_MASK;
290}
291
292static int gmux_update_status(struct backlight_device *bd)
293{
294 struct apple_gmux_data *gmux_data = bl_get_data(bd);
295 u32 brightness = bd->props.brightness;
296
Seth Forshee96960882012-04-19 10:55:35 -0500297 if (bd->props.state & BL_CORE_SUSPENDED)
Matthew Garretta2f01a82012-06-01 15:18:52 -0400298 return 0;
Seth Forshee96960882012-04-19 10:55:35 -0500299
Matthew Garrett7e30ed62012-08-09 12:47:00 -0400300 gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
Seth Forshee917ee752012-03-16 14:41:22 -0500301
302 return 0;
303}
304
305static const struct backlight_ops gmux_bl_ops = {
Seth Forshee96960882012-04-19 10:55:35 -0500306 .options = BL_CORE_SUSPENDRESUME,
Seth Forshee917ee752012-03-16 14:41:22 -0500307 .get_brightness = gmux_get_brightness,
308 .update_status = gmux_update_status,
309};
310
Lukas Wunner3d7b75f2016-01-11 00:08:35 +0100311/**
312 * DOC: Graphics mux
313 *
314 * :5: http://pimg-fpiw.uspto.gov/fdd/07/870/086/0.pdf
315 * :6: http://www.nxp.com/documents/data_sheet/CBTL06141.pdf
316 * :7: http://www.ti.com/lit/ds/symlink/hd3ss212.pdf
317 * :8: https://www.pericom.com/assets/Datasheets/PI3VDP12412.pdf
318 * :9: http://www.ti.com/lit/ds/symlink/sn74lv4066a.pdf
319 * :10: http://pdf.datasheetarchive.com/indexerfiles/Datasheets-SW16/DSASW00308511.pdf
320 * :11: http://www.ti.com/lit/ds/symlink/ts3ds10224.pdf
321 *
322 * On pre-retinas, the LVDS outputs of both GPUs feed into gmux which muxes
323 * either of them to the panel. One of the tricks gmux has up its sleeve is
324 * to lengthen the blanking interval of its output during a switch to
325 * synchronize it with the GPU switched to. This allows for a flicker-free
326 * switch that is imperceptible by the user ({5}[US 8,687,007 B2]).
327 *
328 * On retinas, muxing is no longer done by gmux itself, but by a separate
329 * chip which is controlled by gmux. The chip is triple sourced, it is
330 * either an {6}[NXP CBTL06142], {7}[TI HD3SS212] or {8}[Pericom PI3VDP12412].
331 * The panel is driven with eDP instead of LVDS since the pixel clock
332 * required for retina resolution exceeds LVDS' limits.
333 *
334 * Pre-retinas are able to switch the panel's DDC pins separately.
335 * This is handled by a {9}[TI SN74LV4066A] which is controlled by gmux.
336 * The inactive GPU can thus probe the panel's EDID without switching over
337 * the entire panel. Retinas lack this functionality as the chips used for
338 * eDP muxing are incapable of switching the AUX channel separately (see
339 * the linked data sheets, Pericom would be capable but this is unused).
340 * However the retina panel has the NO_AUX_HANDSHAKE_LINK_TRAINING bit set
341 * in its DPCD, allowing the inactive GPU to skip the AUX handshake and
342 * set up the output with link parameters pre-calibrated by the active GPU.
343 *
344 * The external DP port is only fully switchable on the first two unibody
345 * MacBook Pro generations, MBP5 2008/09 and MBP6 2010. This is done by an
346 * {6}[NXP CBTL06141] which is controlled by gmux. It's the predecessor of the
347 * eDP mux on retinas, the difference being support for 2.7 versus 5.4 Gbit/s.
348 *
349 * The following MacBook Pro generations replaced the external DP port with a
350 * combined DP/Thunderbolt port and lost the ability to switch it between GPUs,
351 * connecting it either to the discrete GPU or the Thunderbolt controller.
352 * Oddly enough, while the full port is no longer switchable, AUX and HPD
353 * are still switchable by way of an {10}[NXP CBTL03062] (on pre-retinas
354 * MBP8 2011 and MBP9 2012) or two {11}[TI TS3DS10224] (on retinas) under the
355 * control of gmux. Since the integrated GPU is missing the main link,
356 * external displays appear to it as phantoms which fail to link-train.
357 *
358 * gmux receives the HPD signal of all display connectors and sends an
359 * interrupt on hotplug. On generations which cannot switch external ports,
360 * the discrete GPU can then be woken to drive the newly connected display.
361 * The ability to switch AUX on these generations could be used to improve
362 * reliability of hotplug detection by having the integrated GPU poll the
363 * ports while the discrete GPU is asleep, but currently we do not make use
364 * of this feature.
365 *
366 * gmux' initial switch state on bootup is user configurable via the EFI
367 * variable `gpu-power-prefs-fa4ce28d-b62f-4c99-9cc3-6815686e30f9` (5th byte,
368 * 1 = IGD, 0 = DIS). Based on this setting, the EFI firmware tells gmux to
369 * switch the panel and the external DP connector and allocates a framebuffer
370 * for the selected GPU.
371 */
372
Lukas Wunner3e463042016-01-11 20:09:20 +0100373static void gmux_read_switch_state(struct apple_gmux_data *gmux_data)
374{
375 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DDC) == 1)
376 gmux_data->switch_state_ddc = VGA_SWITCHEROO_IGD;
377 else
378 gmux_data->switch_state_ddc = VGA_SWITCHEROO_DIS;
379
380 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2)
381 gmux_data->switch_state_display = VGA_SWITCHEROO_IGD;
382 else
383 gmux_data->switch_state_display = VGA_SWITCHEROO_DIS;
384
385 if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL) == 2)
386 gmux_data->switch_state_external = VGA_SWITCHEROO_IGD;
387 else
388 gmux_data->switch_state_external = VGA_SWITCHEROO_DIS;
389}
390
391static void gmux_write_switch_state(struct apple_gmux_data *gmux_data)
392{
393 if (gmux_data->switch_state_ddc == VGA_SWITCHEROO_IGD)
394 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DDC, 1);
395 else
396 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DDC, 2);
397
398 if (gmux_data->switch_state_display == VGA_SWITCHEROO_IGD)
399 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2);
400 else
401 gmux_write8(gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3);
402
403 if (gmux_data->switch_state_external == VGA_SWITCHEROO_IGD)
404 gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2);
405 else
406 gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
407}
408
Andreas Heider76b487d2012-08-17 11:17:04 -0500409static int gmux_switchto(enum vga_switcheroo_client_id id)
410{
Lukas Wunner3e463042016-01-11 20:09:20 +0100411 apple_gmux_data->switch_state_ddc = id;
412 apple_gmux_data->switch_state_display = id;
413 apple_gmux_data->switch_state_external = id;
414
415 gmux_write_switch_state(apple_gmux_data);
Andreas Heider76b487d2012-08-17 11:17:04 -0500416
417 return 0;
418}
419
Lukas Wunnerf798d962016-01-11 20:09:20 +0100420static int gmux_switch_ddc(enum vga_switcheroo_client_id id)
421{
422 enum vga_switcheroo_client_id old_ddc_owner =
423 apple_gmux_data->switch_state_ddc;
424
425 if (id == old_ddc_owner)
426 return id;
427
428 pr_debug("Switching DDC from %d to %d\n", old_ddc_owner, id);
429 apple_gmux_data->switch_state_ddc = id;
430
431 if (id == VGA_SWITCHEROO_IGD)
432 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1);
433 else
434 gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2);
435
436 return old_ddc_owner;
437}
438
Lukas Wunner3d7b75f2016-01-11 00:08:35 +0100439/**
440 * DOC: Power control
441 *
442 * gmux is able to cut power to the discrete GPU. It automatically takes care
443 * of the correct sequence to tear down and bring up the power rails for
444 * core voltage, VRAM and PCIe.
445 */
446
Andreas Heider76b487d2012-08-17 11:17:04 -0500447static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
448 enum vga_switcheroo_state state)
449{
Wolfram Sang16735d02013-11-14 14:32:02 -0800450 reinit_completion(&gmux_data->powerchange_done);
Andreas Heider76b487d2012-08-17 11:17:04 -0500451
452 if (state == VGA_SWITCHEROO_ON) {
453 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
454 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3);
455 pr_debug("Discrete card powered up\n");
456 } else {
457 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
458 gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0);
459 pr_debug("Discrete card powered down\n");
460 }
461
462 gmux_data->power_state = state;
463
464 if (gmux_data->gpe >= 0 &&
465 !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done,
466 msecs_to_jiffies(200)))
467 pr_warn("Timeout waiting for gmux switch to complete\n");
468
469 return 0;
470}
471
472static int gmux_set_power_state(enum vga_switcheroo_client_id id,
473 enum vga_switcheroo_state state)
474{
475 if (id == VGA_SWITCHEROO_IGD)
476 return 0;
477
478 return gmux_set_discrete_state(apple_gmux_data, state);
479}
480
481static int gmux_get_client_id(struct pci_dev *pdev)
482{
483 /*
484 * Early Macbook Pros with switchable graphics use nvidia
485 * integrated graphics. Hardcode that the 9400M is integrated.
486 */
487 if (pdev->vendor == PCI_VENDOR_ID_INTEL)
488 return VGA_SWITCHEROO_IGD;
489 else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
490 pdev->device == 0x0863)
491 return VGA_SWITCHEROO_IGD;
492 else
493 return VGA_SWITCHEROO_DIS;
494}
495
Lukas Wunnerf798d962016-01-11 20:09:20 +0100496static const struct vga_switcheroo_handler gmux_handler_indexed = {
Andreas Heider76b487d2012-08-17 11:17:04 -0500497 .switchto = gmux_switchto,
498 .power_state = gmux_set_power_state,
499 .get_client_id = gmux_get_client_id,
500};
501
Lukas Wunnerf798d962016-01-11 20:09:20 +0100502static const struct vga_switcheroo_handler gmux_handler_classic = {
503 .switchto = gmux_switchto,
504 .switch_ddc = gmux_switch_ddc,
505 .power_state = gmux_set_power_state,
506 .get_client_id = gmux_get_client_id,
507};
508
Lukas Wunner3d7b75f2016-01-11 00:08:35 +0100509/**
510 * DOC: Interrupt
511 *
512 * gmux is also connected to a GPIO pin of the southbridge and thereby is able
513 * to trigger an ACPI GPE. On the MBP5 2008/09 it's GPIO pin 22 of the Nvidia
514 * MCP79, on all following generations it's GPIO pin 6 of the Intel PCH.
515 * The GPE merely signals that an interrupt occurred, the actual type of event
516 * is identified by reading a gmux register.
517 */
518
Andreas Heider76b487d2012-08-17 11:17:04 -0500519static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
520{
521 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
522 GMUX_INTERRUPT_DISABLE);
523}
524
525static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data)
526{
527 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
528 GMUX_INTERRUPT_ENABLE);
529}
530
531static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data)
532{
533 return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS);
534}
535
536static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
537{
538 u8 status;
539
540 /* to clear interrupts write back current status */
541 status = gmux_interrupt_get_status(gmux_data);
542 gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
543}
544
545static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
546{
547 u8 status;
548 struct pnp_dev *pnp = (struct pnp_dev *)context;
549 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
550
551 status = gmux_interrupt_get_status(gmux_data);
552 gmux_disable_interrupts(gmux_data);
553 pr_debug("Notify handler called: status %d\n", status);
554
555 gmux_clear_interrupts(gmux_data);
556 gmux_enable_interrupts(gmux_data);
557
558 if (status & GMUX_INTERRUPT_STATUS_POWER)
559 complete(&gmux_data->powerchange_done);
560}
561
Shuah Khan8aa6c212013-09-11 14:23:15 -0700562static int gmux_suspend(struct device *dev)
Andreas Heider76b487d2012-08-17 11:17:04 -0500563{
Shuah Khan8aa6c212013-09-11 14:23:15 -0700564 struct pnp_dev *pnp = to_pnp_dev(dev);
Andreas Heider76b487d2012-08-17 11:17:04 -0500565 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
Shuah Khan8aa6c212013-09-11 14:23:15 -0700566
Andreas Heider76b487d2012-08-17 11:17:04 -0500567 gmux_disable_interrupts(gmux_data);
568 return 0;
569}
570
Shuah Khan8aa6c212013-09-11 14:23:15 -0700571static int gmux_resume(struct device *dev)
Andreas Heider76b487d2012-08-17 11:17:04 -0500572{
Shuah Khan8aa6c212013-09-11 14:23:15 -0700573 struct pnp_dev *pnp = to_pnp_dev(dev);
Andreas Heider76b487d2012-08-17 11:17:04 -0500574 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
Shuah Khan8aa6c212013-09-11 14:23:15 -0700575
Andreas Heider76b487d2012-08-17 11:17:04 -0500576 gmux_enable_interrupts(gmux_data);
Lukas Wunner3e463042016-01-11 20:09:20 +0100577 gmux_write_switch_state(gmux_data);
Andreas Heider76b487d2012-08-17 11:17:04 -0500578 if (gmux_data->power_state == VGA_SWITCHEROO_OFF)
579 gmux_set_discrete_state(gmux_data, gmux_data->power_state);
580 return 0;
581}
582
Bruno Prémont4eebd5a2015-03-11 22:34:45 +0100583static struct pci_dev *gmux_get_io_pdev(void)
584{
585 struct pci_dev *pdev = NULL;
586
587 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev))) {
588 u16 cmd;
589
590 pci_read_config_word(pdev, PCI_COMMAND, &cmd);
591 if (!(cmd & PCI_COMMAND_IO))
592 continue;
593
594 return pdev;
595 }
596
597 return NULL;
598}
599
Greg Kroah-Hartmanb859f152012-12-21 13:18:33 -0800600static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
Seth Forshee917ee752012-03-16 14:41:22 -0500601{
602 struct apple_gmux_data *gmux_data;
603 struct resource *res;
604 struct backlight_properties props;
605 struct backlight_device *bdev;
606 u8 ver_major, ver_minor, ver_release;
607 int ret = -ENXIO;
Andreas Heider76b487d2012-08-17 11:17:04 -0500608 acpi_status status;
609 unsigned long long gpe;
Bruno Prémont4eebd5a2015-03-11 22:34:45 +0100610 struct pci_dev *pdev = NULL;
Andreas Heider76b487d2012-08-17 11:17:04 -0500611
612 if (apple_gmux_data)
613 return -EBUSY;
Seth Forshee917ee752012-03-16 14:41:22 -0500614
615 gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
616 if (!gmux_data)
617 return -ENOMEM;
618 pnp_set_drvdata(pnp, gmux_data);
619
620 res = pnp_get_resource(pnp, IORESOURCE_IO, 0);
621 if (!res) {
622 pr_err("Failed to find gmux I/O resource\n");
623 goto err_free;
624 }
625
626 gmux_data->iostart = res->start;
627 gmux_data->iolen = res->end - res->start;
628
629 if (gmux_data->iolen < GMUX_MIN_IO_LEN) {
630 pr_err("gmux I/O region too small (%lu < %u)\n",
631 gmux_data->iolen, GMUX_MIN_IO_LEN);
632 goto err_free;
633 }
634
635 if (!request_region(gmux_data->iostart, gmux_data->iolen,
636 "Apple gmux")) {
637 pr_err("gmux I/O already in use\n");
638 goto err_free;
639 }
640
641 /*
Matthew Garrett96ff7052012-08-09 13:45:01 -0400642 * Invalid version information may indicate either that the gmux
643 * device isn't present or that it's a new one that uses indexed
644 * io
Seth Forshee917ee752012-03-16 14:41:22 -0500645 */
Matthew Garrett96ff7052012-08-09 13:45:01 -0400646
Seth Forshee917ee752012-03-16 14:41:22 -0500647 ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
648 ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
649 ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
650 if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
Matthew Garrett96ff7052012-08-09 13:45:01 -0400651 if (gmux_is_indexed(gmux_data)) {
Bernhard Froemel07f377d2012-08-25 10:30:49 +0200652 u32 version;
Matthew Garrett96ff7052012-08-09 13:45:01 -0400653 mutex_init(&gmux_data->index_lock);
654 gmux_data->indexed = true;
Bernhard Froemel07f377d2012-08-25 10:30:49 +0200655 version = gmux_read32(gmux_data,
656 GMUX_PORT_VERSION_MAJOR);
657 ver_major = (version >> 24) & 0xff;
658 ver_minor = (version >> 16) & 0xff;
659 ver_release = (version >> 8) & 0xff;
Matthew Garrett96ff7052012-08-09 13:45:01 -0400660 } else {
Bruno Prémont4eebd5a2015-03-11 22:34:45 +0100661 pr_info("gmux device not present or IO disabled\n");
Matthew Garrett96ff7052012-08-09 13:45:01 -0400662 ret = -ENODEV;
663 goto err_release;
664 }
Seth Forshee917ee752012-03-16 14:41:22 -0500665 }
Bernhard Froemel07f377d2012-08-25 10:30:49 +0200666 pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
667 ver_release, (gmux_data->indexed ? "indexed" : "classic"));
Seth Forshee917ee752012-03-16 14:41:22 -0500668
Bruno Prémont4eebd5a2015-03-11 22:34:45 +0100669 /*
670 * Apple systems with gmux are EFI based and normally don't use
671 * VGA. In addition changing IO+MEM ownership between IGP and dGPU
672 * disables IO/MEM used for backlight control on some systems.
673 * Lock IO+MEM to GPU with active IO to prevent switch.
674 */
675 pdev = gmux_get_io_pdev();
676 if (pdev && vga_tryget(pdev,
677 VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM)) {
678 pr_err("IO+MEM vgaarb-locking for PCI:%s failed\n",
679 pci_name(pdev));
680 ret = -EBUSY;
681 goto err_release;
682 } else if (pdev)
683 pr_info("locked IO for PCI:%s\n", pci_name(pdev));
684 gmux_data->pdev = pdev;
685
Seth Forshee917ee752012-03-16 14:41:22 -0500686 memset(&props, 0, sizeof(props));
687 props.type = BACKLIGHT_PLATFORM;
688 props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
689
690 /*
691 * Currently it's assumed that the maximum brightness is less than
692 * 2^24 for compatibility with old gmux versions. Cap the max
693 * brightness at this value, but print a warning if the hardware
694 * reports something higher so that it can be fixed.
695 */
696 if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS))
697 props.max_brightness = GMUX_MAX_BRIGHTNESS;
698
699 bdev = backlight_device_register("gmux_backlight", &pnp->dev,
700 gmux_data, &gmux_bl_ops, &props);
701 if (IS_ERR(bdev)) {
702 ret = PTR_ERR(bdev);
703 goto err_release;
704 }
705
706 gmux_data->bdev = bdev;
707 bdev->props.brightness = gmux_get_brightness(bdev);
708 backlight_update_status(bdev);
709
710 /*
711 * The backlight situation on Macs is complicated. If the gmux is
712 * present it's the best choice, because it always works for
713 * backlight control and supports more levels than other options.
714 * Disable the other backlight choices.
715 */
Hans de Goede86ac0562015-06-16 16:27:56 +0200716 acpi_video_set_dmi_backlight_type(acpi_backlight_vendor);
Seth Forshee917ee752012-03-16 14:41:22 -0500717 apple_bl_unregister();
718
Andreas Heider76b487d2012-08-17 11:17:04 -0500719 gmux_data->power_state = VGA_SWITCHEROO_ON;
720
Rafael J. Wysocki3a83f992013-11-14 23:17:21 +0100721 gmux_data->dhandle = ACPI_HANDLE(&pnp->dev);
Andreas Heider76b487d2012-08-17 11:17:04 -0500722 if (!gmux_data->dhandle) {
723 pr_err("Cannot find acpi handle for pnp device %s\n",
724 dev_name(&pnp->dev));
725 ret = -ENODEV;
726 goto err_notify;
727 }
728
729 status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe);
730 if (ACPI_SUCCESS(status)) {
731 gmux_data->gpe = (int)gpe;
732
733 status = acpi_install_notify_handler(gmux_data->dhandle,
734 ACPI_DEVICE_NOTIFY,
735 &gmux_notify_handler, pnp);
736 if (ACPI_FAILURE(status)) {
737 pr_err("Install notify handler failed: %s\n",
738 acpi_format_exception(status));
739 ret = -ENODEV;
740 goto err_notify;
741 }
742
743 status = acpi_enable_gpe(NULL, gmux_data->gpe);
744 if (ACPI_FAILURE(status)) {
745 pr_err("Cannot enable gpe: %s\n",
746 acpi_format_exception(status));
747 goto err_enable_gpe;
748 }
749 } else {
750 pr_warn("No GPE found for gmux\n");
751 gmux_data->gpe = -1;
752 }
753
Matthew Garrettc1e16552015-11-16 21:38:40 +0100754 apple_gmux_data = gmux_data;
755 init_completion(&gmux_data->powerchange_done);
756 gmux_enable_interrupts(gmux_data);
Lukas Wunner3e463042016-01-11 20:09:20 +0100757 gmux_read_switch_state(gmux_data);
Matthew Garrettc1e16552015-11-16 21:38:40 +0100758
Lukas Wunnerf798d962016-01-11 20:09:20 +0100759 /*
760 * Retina MacBook Pros cannot switch the panel's AUX separately
761 * and need eDP pre-calibration. They are distinguishable from
762 * pre-retinas by having an "indexed" gmux.
763 *
764 * Pre-retina MacBook Pros can switch the panel's DDC separately.
765 */
766 if (gmux_data->indexed)
767 ret = vga_switcheroo_register_handler(&gmux_handler_indexed,
768 VGA_SWITCHEROO_NEEDS_EDP_CONFIG);
769 else
770 ret = vga_switcheroo_register_handler(&gmux_handler_classic,
771 VGA_SWITCHEROO_CAN_SWITCH_DDC);
772 if (ret) {
773 pr_err("Failed to register vga_switcheroo handler\n");
Andreas Heider76b487d2012-08-17 11:17:04 -0500774 goto err_register_handler;
775 }
776
Seth Forshee917ee752012-03-16 14:41:22 -0500777 return 0;
778
Andreas Heider76b487d2012-08-17 11:17:04 -0500779err_register_handler:
Matthew Garrettc1e16552015-11-16 21:38:40 +0100780 gmux_disable_interrupts(gmux_data);
781 apple_gmux_data = NULL;
Andreas Heider76b487d2012-08-17 11:17:04 -0500782 if (gmux_data->gpe >= 0)
783 acpi_disable_gpe(NULL, gmux_data->gpe);
784err_enable_gpe:
785 if (gmux_data->gpe >= 0)
786 acpi_remove_notify_handler(gmux_data->dhandle,
787 ACPI_DEVICE_NOTIFY,
788 &gmux_notify_handler);
789err_notify:
790 backlight_device_unregister(bdev);
Seth Forshee917ee752012-03-16 14:41:22 -0500791err_release:
Bruno Prémont4eebd5a2015-03-11 22:34:45 +0100792 if (gmux_data->pdev)
793 vga_put(gmux_data->pdev,
794 VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM);
795 pci_dev_put(pdev);
Seth Forshee917ee752012-03-16 14:41:22 -0500796 release_region(gmux_data->iostart, gmux_data->iolen);
797err_free:
798 kfree(gmux_data);
799 return ret;
800}
801
Greg Kroah-Hartmanb859f152012-12-21 13:18:33 -0800802static void gmux_remove(struct pnp_dev *pnp)
Seth Forshee917ee752012-03-16 14:41:22 -0500803{
804 struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
805
Andreas Heider76b487d2012-08-17 11:17:04 -0500806 vga_switcheroo_unregister_handler();
807 gmux_disable_interrupts(gmux_data);
808 if (gmux_data->gpe >= 0) {
809 acpi_disable_gpe(NULL, gmux_data->gpe);
810 acpi_remove_notify_handler(gmux_data->dhandle,
811 ACPI_DEVICE_NOTIFY,
812 &gmux_notify_handler);
813 }
814
Bruno Prémont4eebd5a2015-03-11 22:34:45 +0100815 if (gmux_data->pdev) {
816 vga_put(gmux_data->pdev,
817 VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM);
818 pci_dev_put(gmux_data->pdev);
819 }
Seth Forshee917ee752012-03-16 14:41:22 -0500820 backlight_device_unregister(gmux_data->bdev);
Andreas Heider76b487d2012-08-17 11:17:04 -0500821
Seth Forshee917ee752012-03-16 14:41:22 -0500822 release_region(gmux_data->iostart, gmux_data->iolen);
Andreas Heider76b487d2012-08-17 11:17:04 -0500823 apple_gmux_data = NULL;
Seth Forshee917ee752012-03-16 14:41:22 -0500824 kfree(gmux_data);
825
826 acpi_video_register();
Seth Forshee917ee752012-03-16 14:41:22 -0500827 apple_bl_register();
828}
829
830static const struct pnp_device_id gmux_device_ids[] = {
831 {"APP000B", 0},
832 {"", 0}
833};
834
Shuah Khan8aa6c212013-09-11 14:23:15 -0700835static const struct dev_pm_ops gmux_dev_pm_ops = {
836 .suspend = gmux_suspend,
837 .resume = gmux_resume,
838};
839
Seth Forshee917ee752012-03-16 14:41:22 -0500840static struct pnp_driver gmux_pnp_driver = {
841 .name = "apple-gmux",
842 .probe = gmux_probe,
Greg Kroah-Hartmanb859f152012-12-21 13:18:33 -0800843 .remove = gmux_remove,
Seth Forshee917ee752012-03-16 14:41:22 -0500844 .id_table = gmux_device_ids,
Shuah Khan8aa6c212013-09-11 14:23:15 -0700845 .driver = {
846 .pm = &gmux_dev_pm_ops,
847 },
Seth Forshee917ee752012-03-16 14:41:22 -0500848};
849
Peter Huewe99f74f12015-03-16 21:46:36 +0100850module_pnp_driver(gmux_pnp_driver);
Seth Forshee917ee752012-03-16 14:41:22 -0500851MODULE_AUTHOR("Seth Forshee <seth.forshee@canonical.com>");
852MODULE_DESCRIPTION("Apple Gmux Driver");
853MODULE_LICENSE("GPL");
854MODULE_DEVICE_TABLE(pnp, gmux_device_ids);