1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * apple-gmux.h - microcontroller built into dual GPU MacBook Pro & Mac Pro
4  * Copyright (C) 2015 Lukas Wunner <lukas@wunner.de>
5  */
6 
7 #ifndef LINUX_APPLE_GMUX_H
8 #define LINUX_APPLE_GMUX_H
9 
10 #include <linux/acpi.h>
11 #include <linux/io.h>
12 #include <linux/pnp.h>
13 
14 #define GMUX_ACPI_HID "APP000B"
15 
16 /*
17  * gmux port offsets. Many of these are not yet used, but may be in the
18  * future, and it's useful to have them documented here anyhow.
19  */
20 #define GMUX_PORT_VERSION_MAJOR		0x04
21 #define GMUX_PORT_VERSION_MINOR		0x05
22 #define GMUX_PORT_VERSION_RELEASE	0x06
23 #define GMUX_PORT_SWITCH_DISPLAY	0x10
24 #define GMUX_PORT_SWITCH_GET_DISPLAY	0x11
25 #define GMUX_PORT_INTERRUPT_ENABLE	0x14
26 #define GMUX_PORT_INTERRUPT_STATUS	0x16
27 #define GMUX_PORT_SWITCH_DDC		0x28
28 #define GMUX_PORT_SWITCH_EXTERNAL	0x40
29 #define GMUX_PORT_SWITCH_GET_EXTERNAL	0x41
30 #define GMUX_PORT_DISCRETE_POWER	0x50
31 #define GMUX_PORT_MAX_BRIGHTNESS	0x70
32 #define GMUX_PORT_BRIGHTNESS		0x74
33 #define GMUX_PORT_VALUE			0xc2
34 #define GMUX_PORT_READ			0xd0
35 #define GMUX_PORT_WRITE			0xd4
36 
37 #define GMUX_MIN_IO_LEN			(GMUX_PORT_BRIGHTNESS + 4)
38 
39 #if IS_ENABLED(CONFIG_APPLE_GMUX)
apple_gmux_is_indexed(unsigned long iostart)40 static inline bool apple_gmux_is_indexed(unsigned long iostart)
41 {
42 	u16 val;
43 
44 	outb(0xaa, iostart + 0xcc);
45 	outb(0x55, iostart + 0xcd);
46 	outb(0x00, iostart + 0xce);
47 
48 	val = inb(iostart + 0xcc) | (inb(iostart + 0xcd) << 8);
49 	if (val == 0x55aa)
50 		return true;
51 
52 	return false;
53 }
54 
55 /**
56  * apple_gmux_detect() - detect if gmux is built into the machine
57  *
58  * @pnp_dev:     Device to probe or NULL to use the first matching device
59  * @indexed_ret: Returns (by reference) if the gmux is indexed or not
60  *
61  * Detect if a supported gmux device is present by actually probing it.
62  * This avoids the false positives returned on some models by
63  * apple_gmux_present().
64  *
65  * Return: %true if a supported gmux ACPI device is detected and the kernel
66  * was configured with CONFIG_APPLE_GMUX, %false otherwise.
67  */
apple_gmux_detect(struct pnp_dev * pnp_dev,bool * indexed_ret)68 static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret)
69 {
70 	u8 ver_major, ver_minor, ver_release;
71 	struct device *dev = NULL;
72 	struct acpi_device *adev;
73 	struct resource *res;
74 	bool indexed = false;
75 	bool ret = false;
76 
77 	if (!pnp_dev) {
78 		adev = acpi_dev_get_first_match_dev(GMUX_ACPI_HID, NULL, -1);
79 		if (!adev)
80 			return false;
81 
82 		dev = get_device(acpi_get_first_physical_node(adev));
83 		acpi_dev_put(adev);
84 		if (!dev)
85 			return false;
86 
87 		pnp_dev = to_pnp_dev(dev);
88 	}
89 
90 	res = pnp_get_resource(pnp_dev, IORESOURCE_IO, 0);
91 	if (!res || resource_size(res) < GMUX_MIN_IO_LEN)
92 		goto out;
93 
94 	/*
95 	 * Invalid version information may indicate either that the gmux
96 	 * device isn't present or that it's a new one that uses indexed io.
97 	 */
98 	ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR);
99 	ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR);
100 	ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE);
101 	if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
102 		indexed = apple_gmux_is_indexed(res->start);
103 		if (!indexed)
104 			goto out;
105 	}
106 
107 	if (indexed_ret)
108 		*indexed_ret = indexed;
109 
110 	ret = true;
111 out:
112 	put_device(dev);
113 	return ret;
114 }
115 
116 /**
117  * apple_gmux_present() - check if gmux ACPI device is present
118  *
119  * Drivers may use this to activate quirks specific to dual GPU MacBook Pros
120  * and Mac Pros, e.g. for deferred probing, runtime pm and backlight.
121  *
122  * Return: %true if gmux ACPI device is present and the kernel was configured
123  * with CONFIG_APPLE_GMUX, %false otherwise.
124  */
apple_gmux_present(void)125 static inline bool apple_gmux_present(void)
126 {
127 	return acpi_dev_found(GMUX_ACPI_HID);
128 }
129 
130 #else  /* !CONFIG_APPLE_GMUX */
131 
apple_gmux_present(void)132 static inline bool apple_gmux_present(void)
133 {
134 	return false;
135 }
136 
apple_gmux_detect(struct pnp_dev * pnp_dev,bool * indexed_ret)137 static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret)
138 {
139 	return false;
140 }
141 
142 #endif /* !CONFIG_APPLE_GMUX */
143 
144 #endif /* LINUX_APPLE_GMUX_H */
145