xref: /freebsd/sys/dev/acpi_apple/acpi_apple_bl.c (revision 44ceda0a53bfe0c9b343f1e94a4f6ce67f427be6)
1*44ceda0aSAbdelkader Boudih /*
2*44ceda0aSAbdelkader Boudih  * Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
3*44ceda0aSAbdelkader Boudih  *
4*44ceda0aSAbdelkader Boudih  * SPDX-License-Identifier: BSD-2-Clause
5*44ceda0aSAbdelkader Boudih  */
6*44ceda0aSAbdelkader Boudih 
7*44ceda0aSAbdelkader Boudih /*
8*44ceda0aSAbdelkader Boudih  * Apple panel backlight control.
9*44ceda0aSAbdelkader Boudih  *
10*44ceda0aSAbdelkader Boudih  * Two backends: GMUX (dual-GPU MacBook Pros, base+0x70/0x74 over PIO,
11*44ceda0aSAbdelkader Boudih  * indexed I/O, or MMIO depending on platform) and direct GPU PCI BAR
12*44ceda0aSAbdelkader Boudih  * MMIO (single-GPU Macs).  GMUX is preferred whenever APP000B is
13*44ceda0aSAbdelkader Boudih  * present and its _CRS yields a working protocol.
14*44ceda0aSAbdelkader Boudih  */
15*44ceda0aSAbdelkader Boudih 
16*44ceda0aSAbdelkader Boudih #include <sys/cdefs.h>
17*44ceda0aSAbdelkader Boudih #include "acpi_apple.h"
18*44ceda0aSAbdelkader Boudih 
19*44ceda0aSAbdelkader Boudih #include <sys/endian.h>
20*44ceda0aSAbdelkader Boudih #include <sys/systm.h>
21*44ceda0aSAbdelkader Boudih 
22*44ceda0aSAbdelkader Boudih #include <dev/backlight/backlight.h>
23*44ceda0aSAbdelkader Boudih #include <dev/pci/pcireg.h>
24*44ceda0aSAbdelkader Boudih #include <dev/pci/pcivar.h>
25*44ceda0aSAbdelkader Boudih #include <machine/cpufunc.h>
26*44ceda0aSAbdelkader Boudih #include <vm/vm.h>
27*44ceda0aSAbdelkader Boudih #include <vm/pmap.h>
28*44ceda0aSAbdelkader Boudih 
29*44ceda0aSAbdelkader Boudih #include "backlight_if.h"
30*44ceda0aSAbdelkader Boudih 
31*44ceda0aSAbdelkader Boudih ACPI_SERIAL_DECL(apple, "Apple ACPI");
32*44ceda0aSAbdelkader Boudih static struct acpi_apple_softc *apple_backlight_sc;
33*44ceda0aSAbdelkader Boudih 
34*44ceda0aSAbdelkader Boudih static const struct apple_bl_hw apple_bl_intel_pch = {
35*44ceda0aSAbdelkader Boudih 	.name		= "intel_pch",
36*44ceda0aSAbdelkader Boudih 	.bar		= 0,
37*44ceda0aSAbdelkader Boudih 	.mapsize	= 0xc8258,
38*44ceda0aSAbdelkader Boudih 	.ctrl		= { 0xc8254, 0x0000FFFF, 0 },
39*44ceda0aSAbdelkader Boudih 	.enable		= { 0xc8250, 0xC0000000, 0 },
40*44ceda0aSAbdelkader Boudih 	.enable_val	= 0xC0000000,
41*44ceda0aSAbdelkader Boudih 	.max_reg	= { 0xc8254, 0xFFFF0000, 16 },
42*44ceda0aSAbdelkader Boudih 	.max		= 0xFFFF,
43*44ceda0aSAbdelkader Boudih };
44*44ceda0aSAbdelkader Boudih 
45*44ceda0aSAbdelkader Boudih static const struct apple_bl_hw apple_bl_intel_cpu = {
46*44ceda0aSAbdelkader Boudih 	.name		= "intel_cpu",
47*44ceda0aSAbdelkader Boudih 	.bar		= 0,
48*44ceda0aSAbdelkader Boudih 	.mapsize	= 0xc8258,
49*44ceda0aSAbdelkader Boudih 	.ctrl		= { 0x48254, 0x0000FFFF, 0 },
50*44ceda0aSAbdelkader Boudih 	.enable		= { 0x48250, 0xC0000000, 0 },
51*44ceda0aSAbdelkader Boudih 	.enable_val	= 0xC0000000,
52*44ceda0aSAbdelkader Boudih 	.max_reg	= { 0xc8254, 0xFFFF0000, 16 },
53*44ceda0aSAbdelkader Boudih 	.max		= 0xFFFF,
54*44ceda0aSAbdelkader Boudih };
55*44ceda0aSAbdelkader Boudih 
56*44ceda0aSAbdelkader Boudih static const struct apple_bl_hw apple_bl_nvidia_nv40 = {
57*44ceda0aSAbdelkader Boudih 	.name		= "nvidia_nv40",
58*44ceda0aSAbdelkader Boudih 	.bar		= 0,
59*44ceda0aSAbdelkader Boudih 	.mapsize	= 0x15f4,
60*44ceda0aSAbdelkader Boudih 	.ctrl		= { 0x15f0, 0x001F0000, 16 },
61*44ceda0aSAbdelkader Boudih 	.max		= 31,
62*44ceda0aSAbdelkader Boudih };
63*44ceda0aSAbdelkader Boudih 
64*44ceda0aSAbdelkader Boudih static const struct apple_bl_hw apple_bl_nvidia_nv50 = {
65*44ceda0aSAbdelkader Boudih 	.name		= "nvidia_nv50",
66*44ceda0aSAbdelkader Boudih 	.bar		= 0,
67*44ceda0aSAbdelkader Boudih 	.mapsize	= 0x61c088,
68*44ceda0aSAbdelkader Boudih 	.ctrl		= { 0x61c084, 0x000007FF, 0 },
69*44ceda0aSAbdelkader Boudih 	.enable		= { 0x61c084, 0x80000000, 0 },
70*44ceda0aSAbdelkader Boudih 	.enable_val	= 0x80000000,
71*44ceda0aSAbdelkader Boudih 	.max		= 0x7FF,
72*44ceda0aSAbdelkader Boudih };
73*44ceda0aSAbdelkader Boudih 
74*44ceda0aSAbdelkader Boudih static const struct apple_bl_hw apple_bl_amd_r600 = {
75*44ceda0aSAbdelkader Boudih 	.name		= "amd_r600",
76*44ceda0aSAbdelkader Boudih 	.bar		= 2,
77*44ceda0aSAbdelkader Boudih 	.mapsize	= 0x1730,
78*44ceda0aSAbdelkader Boudih 	.ctrl		= { 0x172c, 0x0000FF00, 8 },
79*44ceda0aSAbdelkader Boudih 	.max		= 255,
80*44ceda0aSAbdelkader Boudih };
81*44ceda0aSAbdelkader Boudih 
82*44ceda0aSAbdelkader Boudih /* DCE10 (Tonga/Fiji, e.g. R9 M395/M395X, R9 Nano). */
83*44ceda0aSAbdelkader Boudih static const struct apple_bl_hw apple_bl_amd_dce10 = {
84*44ceda0aSAbdelkader Boudih 	.name		= "amd_dce10",
85*44ceda0aSAbdelkader Boudih 	.bar		= 2,
86*44ceda0aSAbdelkader Boudih 	.mapsize	= 0x1208c,
87*44ceda0aSAbdelkader Boudih 	.ctrl		= { 0x12080, 0x0000FFFF, 0 },
88*44ceda0aSAbdelkader Boudih 	.enable		= { 0x12080, 0x80000000, 0 },
89*44ceda0aSAbdelkader Boudih 	.enable_val	= 0x80000000,
90*44ceda0aSAbdelkader Boudih 	.max_reg	= { 0x12088, 0x0000FFFF, 0 },
91*44ceda0aSAbdelkader Boudih 	.max		= 0xFFFF,
92*44ceda0aSAbdelkader Boudih };
93*44ceda0aSAbdelkader Boudih 
94*44ceda0aSAbdelkader Boudih struct apple_bl_match {
95*44ceda0aSAbdelkader Boudih 	uint16_t		vendor;
96*44ceda0aSAbdelkader Boudih 	uint16_t		dev_lo;
97*44ceda0aSAbdelkader Boudih 	uint16_t		dev_hi;
98*44ceda0aSAbdelkader Boudih 	const struct apple_bl_hw *hw;
99*44ceda0aSAbdelkader Boudih };
100*44ceda0aSAbdelkader Boudih 
101*44ceda0aSAbdelkader Boudih static const struct apple_bl_match apple_bl_matches[] = {
102*44ceda0aSAbdelkader Boudih 	{ 0x8086, 0x0100, 0x013f, &apple_bl_intel_cpu },
103*44ceda0aSAbdelkader Boudih 	{ 0x8086, 0x0140, 0x017f, &apple_bl_intel_cpu },
104*44ceda0aSAbdelkader Boudih 	{ 0x8086, 0, 0, &apple_bl_intel_pch },
105*44ceda0aSAbdelkader Boudih 	{ 0x10de, 0x0040, 0x03ff, &apple_bl_nvidia_nv40 },
106*44ceda0aSAbdelkader Boudih 	{ 0x10de, 0, 0, &apple_bl_nvidia_nv50 },
107*44ceda0aSAbdelkader Boudih 	{ 0x1002, 0x6920, 0x6939, &apple_bl_amd_dce10 },
108*44ceda0aSAbdelkader Boudih 	{ 0x1002, 0, 0, &apple_bl_amd_r600 },
109*44ceda0aSAbdelkader Boudih 	{ 0, 0, 0, NULL },
110*44ceda0aSAbdelkader Boudih };
111*44ceda0aSAbdelkader Boudih 
112*44ceda0aSAbdelkader Boudih /* Classic (PIO) GMUX - pre-Retina Macs. */
113*44ceda0aSAbdelkader Boudih static uint32_t
gmux_pio_read32(uint16_t base,uint8_t port)114*44ceda0aSAbdelkader Boudih gmux_pio_read32(uint16_t base, uint8_t port)
115*44ceda0aSAbdelkader Boudih {
116*44ceda0aSAbdelkader Boudih 	return (inl(base + port));
117*44ceda0aSAbdelkader Boudih }
118*44ceda0aSAbdelkader Boudih 
119*44ceda0aSAbdelkader Boudih /* A 32-bit GMUX write goes out as four byte writes, low byte first. */
120*44ceda0aSAbdelkader Boudih static void
gmux_pio_write32_at(uint16_t addr,uint32_t val)121*44ceda0aSAbdelkader Boudih gmux_pio_write32_at(uint16_t addr, uint32_t val)
122*44ceda0aSAbdelkader Boudih {
123*44ceda0aSAbdelkader Boudih 
124*44ceda0aSAbdelkader Boudih 	for (u_int i = 0; i < sizeof(val); i++)
125*44ceda0aSAbdelkader Boudih 		outb(addr + i, (uint8_t)(val >> (i * NBBY)));
126*44ceda0aSAbdelkader Boudih }
127*44ceda0aSAbdelkader Boudih 
128*44ceda0aSAbdelkader Boudih static void
gmux_pio_write32(uint16_t base,uint8_t port,uint32_t val)129*44ceda0aSAbdelkader Boudih gmux_pio_write32(uint16_t base, uint8_t port, uint32_t val)
130*44ceda0aSAbdelkader Boudih {
131*44ceda0aSAbdelkader Boudih 	gmux_pio_write32_at(base + port, val);
132*44ceda0aSAbdelkader Boudih }
133*44ceda0aSAbdelkader Boudih 
134*44ceda0aSAbdelkader Boudih /* Indexed GMUX - Retina Macs (2012-2015). */
135*44ceda0aSAbdelkader Boudih static int
gmux_index_wait_ready(uint16_t base)136*44ceda0aSAbdelkader Boudih gmux_index_wait_ready(uint16_t base)
137*44ceda0aSAbdelkader Boudih {
138*44ceda0aSAbdelkader Boudih 	uint8_t gwr;
139*44ceda0aSAbdelkader Boudih 
140*44ceda0aSAbdelkader Boudih 	for (int i = 0; i < GMUX_INDEXED_RETRIES; i++) {
141*44ceda0aSAbdelkader Boudih 		gwr = inb(base + GMUX_PORT_WRITE);
142*44ceda0aSAbdelkader Boudih 		if ((gwr & GMUX_INDEXED_BUSY) == 0)
143*44ceda0aSAbdelkader Boudih 			return (0);
144*44ceda0aSAbdelkader Boudih 		inb(base + GMUX_PORT_READ);
145*44ceda0aSAbdelkader Boudih 		DELAY(GMUX_POLL_DELAY);
146*44ceda0aSAbdelkader Boudih 	}
147*44ceda0aSAbdelkader Boudih 	return (ETIMEDOUT);
148*44ceda0aSAbdelkader Boudih }
149*44ceda0aSAbdelkader Boudih 
150*44ceda0aSAbdelkader Boudih static int
gmux_index_wait_complete(uint16_t base)151*44ceda0aSAbdelkader Boudih gmux_index_wait_complete(uint16_t base)
152*44ceda0aSAbdelkader Boudih {
153*44ceda0aSAbdelkader Boudih 	uint8_t gwr;
154*44ceda0aSAbdelkader Boudih 
155*44ceda0aSAbdelkader Boudih 	for (int i = 0; i < GMUX_INDEXED_RETRIES; i++) {
156*44ceda0aSAbdelkader Boudih 		gwr = inb(base + GMUX_PORT_WRITE);
157*44ceda0aSAbdelkader Boudih 		if ((gwr & GMUX_INDEXED_BUSY) != 0) {
158*44ceda0aSAbdelkader Boudih 			/* Acknowledge completion. */
159*44ceda0aSAbdelkader Boudih 			inb(base + GMUX_PORT_READ);
160*44ceda0aSAbdelkader Boudih 			return (0);
161*44ceda0aSAbdelkader Boudih 		}
162*44ceda0aSAbdelkader Boudih 		DELAY(GMUX_POLL_DELAY);
163*44ceda0aSAbdelkader Boudih 	}
164*44ceda0aSAbdelkader Boudih 	/* Clear stale status. */
165*44ceda0aSAbdelkader Boudih 	inb(base + GMUX_PORT_READ);
166*44ceda0aSAbdelkader Boudih 	return (ETIMEDOUT);
167*44ceda0aSAbdelkader Boudih }
168*44ceda0aSAbdelkader Boudih 
169*44ceda0aSAbdelkader Boudih static uint32_t
gmux_index_read32(uint16_t base,uint8_t port)170*44ceda0aSAbdelkader Boudih gmux_index_read32(uint16_t base, uint8_t port)
171*44ceda0aSAbdelkader Boudih {
172*44ceda0aSAbdelkader Boudih 	uint32_t val;
173*44ceda0aSAbdelkader Boudih 
174*44ceda0aSAbdelkader Boudih 	gmux_index_wait_ready(base);
175*44ceda0aSAbdelkader Boudih 	outb(base + GMUX_PORT_READ, port);
176*44ceda0aSAbdelkader Boudih 	gmux_index_wait_complete(base);
177*44ceda0aSAbdelkader Boudih 	val = inl(base + GMUX_PORT_VALUE);
178*44ceda0aSAbdelkader Boudih 	return (val);
179*44ceda0aSAbdelkader Boudih }
180*44ceda0aSAbdelkader Boudih 
181*44ceda0aSAbdelkader Boudih static void
gmux_index_write32(uint16_t base,uint8_t port,uint32_t val)182*44ceda0aSAbdelkader Boudih gmux_index_write32(uint16_t base, uint8_t port, uint32_t val)
183*44ceda0aSAbdelkader Boudih {
184*44ceda0aSAbdelkader Boudih 	gmux_pio_write32_at(base + GMUX_PORT_VALUE, val);
185*44ceda0aSAbdelkader Boudih 	gmux_index_wait_ready(base);
186*44ceda0aSAbdelkader Boudih 	outb(base + GMUX_PORT_WRITE, port);
187*44ceda0aSAbdelkader Boudih 	gmux_index_wait_complete(base);
188*44ceda0aSAbdelkader Boudih }
189*44ceda0aSAbdelkader Boudih 
190*44ceda0aSAbdelkader Boudih /* MMIO GMUX - T2 Macs. */
191*44ceda0aSAbdelkader Boudih static inline uint8_t
gmux_mmio_read8(void * mmio,uint8_t port)192*44ceda0aSAbdelkader Boudih gmux_mmio_read8(void *mmio, uint8_t port)
193*44ceda0aSAbdelkader Boudih {
194*44ceda0aSAbdelkader Boudih 	return (*(volatile uint8_t *)((char *)mmio + port));
195*44ceda0aSAbdelkader Boudih }
196*44ceda0aSAbdelkader Boudih 
197*44ceda0aSAbdelkader Boudih static inline void
gmux_mmio_write8(void * mmio,uint8_t port,uint8_t val)198*44ceda0aSAbdelkader Boudih gmux_mmio_write8(void *mmio, uint8_t port, uint8_t val)
199*44ceda0aSAbdelkader Boudih {
200*44ceda0aSAbdelkader Boudih 	*(volatile uint8_t *)((char *)mmio + port) = val;
201*44ceda0aSAbdelkader Boudih }
202*44ceda0aSAbdelkader Boudih 
203*44ceda0aSAbdelkader Boudih static int
gmux_mmio_wait(void * mmio)204*44ceda0aSAbdelkader Boudih gmux_mmio_wait(void *mmio)
205*44ceda0aSAbdelkader Boudih {
206*44ceda0aSAbdelkader Boudih 
207*44ceda0aSAbdelkader Boudih 	for (int i = 0; i < GMUX_MMIO_RETRIES; i++) {
208*44ceda0aSAbdelkader Boudih 		if (gmux_mmio_read8(mmio, GMUX_MMIO_COMMAND_SEND) == 0)
209*44ceda0aSAbdelkader Boudih 			return (0);
210*44ceda0aSAbdelkader Boudih 		DELAY(GMUX_POLL_DELAY);
211*44ceda0aSAbdelkader Boudih 	}
212*44ceda0aSAbdelkader Boudih 	return (ETIMEDOUT);
213*44ceda0aSAbdelkader Boudih }
214*44ceda0aSAbdelkader Boudih 
215*44ceda0aSAbdelkader Boudih static uint32_t
gmux_mmio_read32(void * mmio,uint8_t port)216*44ceda0aSAbdelkader Boudih gmux_mmio_read32(void *mmio, uint8_t port)
217*44ceda0aSAbdelkader Boudih {
218*44ceda0aSAbdelkader Boudih 	uint32_t val;
219*44ceda0aSAbdelkader Boudih 
220*44ceda0aSAbdelkader Boudih 	gmux_mmio_wait(mmio);
221*44ceda0aSAbdelkader Boudih 	gmux_mmio_write8(mmio, GMUX_MMIO_PORT_SELECT, port);
222*44ceda0aSAbdelkader Boudih 	gmux_mmio_write8(mmio, GMUX_MMIO_COMMAND_SEND,
223*44ceda0aSAbdelkader Boudih 	    GMUX_MMIO_READ | sizeof(val));
224*44ceda0aSAbdelkader Boudih 	gmux_mmio_wait(mmio);
225*44ceda0aSAbdelkader Boudih 	val = be32toh(*(volatile uint32_t *)mmio);
226*44ceda0aSAbdelkader Boudih 	return (val);
227*44ceda0aSAbdelkader Boudih }
228*44ceda0aSAbdelkader Boudih 
229*44ceda0aSAbdelkader Boudih static void
gmux_mmio_write32(void * mmio,uint8_t port,uint32_t val)230*44ceda0aSAbdelkader Boudih gmux_mmio_write32(void *mmio, uint8_t port, uint32_t val)
231*44ceda0aSAbdelkader Boudih {
232*44ceda0aSAbdelkader Boudih 	gmux_mmio_wait(mmio);
233*44ceda0aSAbdelkader Boudih 	*(volatile uint32_t *)mmio = htobe32(val);
234*44ceda0aSAbdelkader Boudih 	gmux_mmio_write8(mmio, GMUX_MMIO_PORT_SELECT, port);
235*44ceda0aSAbdelkader Boudih 	gmux_mmio_write8(mmio, GMUX_MMIO_COMMAND_SEND,
236*44ceda0aSAbdelkader Boudih 	    GMUX_MMIO_WRITE | sizeof(val));
237*44ceda0aSAbdelkader Boudih 	gmux_mmio_wait(mmio);
238*44ceda0aSAbdelkader Boudih }
239*44ceda0aSAbdelkader Boudih 
240*44ceda0aSAbdelkader Boudih static uint32_t
gmux_read32(struct acpi_apple_softc * sc,uint8_t port)241*44ceda0aSAbdelkader Boudih gmux_read32(struct acpi_apple_softc *sc, uint8_t port)
242*44ceda0aSAbdelkader Boudih {
243*44ceda0aSAbdelkader Boudih 	if (sc->bl_gmux_type == GMUX_TYPE_MMIO)
244*44ceda0aSAbdelkader Boudih 		return (gmux_mmio_read32(sc->bl_gmux_mmio, port));
245*44ceda0aSAbdelkader Boudih 	if (sc->bl_gmux_type == GMUX_TYPE_INDEXED)
246*44ceda0aSAbdelkader Boudih 		return (gmux_index_read32(sc->bl_gmux_base, port));
247*44ceda0aSAbdelkader Boudih 	return (gmux_pio_read32(sc->bl_gmux_base, port));
248*44ceda0aSAbdelkader Boudih }
249*44ceda0aSAbdelkader Boudih 
250*44ceda0aSAbdelkader Boudih static void
gmux_write32(struct acpi_apple_softc * sc,uint8_t port,uint32_t val)251*44ceda0aSAbdelkader Boudih gmux_write32(struct acpi_apple_softc *sc, uint8_t port, uint32_t val)
252*44ceda0aSAbdelkader Boudih {
253*44ceda0aSAbdelkader Boudih 	if (sc->bl_gmux_type == GMUX_TYPE_MMIO)
254*44ceda0aSAbdelkader Boudih 		gmux_mmio_write32(sc->bl_gmux_mmio, port, val);
255*44ceda0aSAbdelkader Boudih 	else if (sc->bl_gmux_type == GMUX_TYPE_INDEXED)
256*44ceda0aSAbdelkader Boudih 		gmux_index_write32(sc->bl_gmux_base, port, val);
257*44ceda0aSAbdelkader Boudih 	else
258*44ceda0aSAbdelkader Boudih 		gmux_pio_write32(sc->bl_gmux_base, port, val);
259*44ceda0aSAbdelkader Boudih }
260*44ceda0aSAbdelkader Boudih 
261*44ceda0aSAbdelkader Boudih static inline bool
apple_bl_gmux_active(struct acpi_apple_softc * sc)262*44ceda0aSAbdelkader Boudih apple_bl_gmux_active(struct acpi_apple_softc *sc)
263*44ceda0aSAbdelkader Boudih {
264*44ceda0aSAbdelkader Boudih 	return (sc->bl_gmux_base != 0 || sc->bl_gmux_mmio != NULL);
265*44ceda0aSAbdelkader Boudih }
266*44ceda0aSAbdelkader Boudih 
267*44ceda0aSAbdelkader Boudih static void
apple_bl_gmux_teardown(struct acpi_apple_softc * sc)268*44ceda0aSAbdelkader Boudih apple_bl_gmux_teardown(struct acpi_apple_softc *sc)
269*44ceda0aSAbdelkader Boudih {
270*44ceda0aSAbdelkader Boudih 	if (sc->bl_gmux_mmio != NULL) {
271*44ceda0aSAbdelkader Boudih 		pmap_unmapdev(sc->bl_gmux_mmio, GMUX_MIN_IO_LEN);
272*44ceda0aSAbdelkader Boudih 		sc->bl_gmux_mmio = NULL;
273*44ceda0aSAbdelkader Boudih 	}
274*44ceda0aSAbdelkader Boudih 	sc->bl_gmux_base = 0;
275*44ceda0aSAbdelkader Boudih }
276*44ceda0aSAbdelkader Boudih 
277*44ceda0aSAbdelkader Boudih static inline uint32_t
apple_bl_read(struct acpi_apple_softc * sc,uint32_t offset)278*44ceda0aSAbdelkader Boudih apple_bl_read(struct acpi_apple_softc *sc, uint32_t offset)
279*44ceda0aSAbdelkader Boudih {
280*44ceda0aSAbdelkader Boudih 	return (*(volatile uint32_t *)((char *)sc->bl_mmio + offset));
281*44ceda0aSAbdelkader Boudih }
282*44ceda0aSAbdelkader Boudih 
283*44ceda0aSAbdelkader Boudih static inline void
apple_bl_write(struct acpi_apple_softc * sc,uint32_t offset,uint32_t val)284*44ceda0aSAbdelkader Boudih apple_bl_write(struct acpi_apple_softc *sc, uint32_t offset, uint32_t val)
285*44ceda0aSAbdelkader Boudih {
286*44ceda0aSAbdelkader Boudih 	*(volatile uint32_t *)((char *)sc->bl_mmio + offset) = val;
287*44ceda0aSAbdelkader Boudih }
288*44ceda0aSAbdelkader Boudih 
289*44ceda0aSAbdelkader Boudih static int
apple_bl_hw_get(struct acpi_apple_softc * sc)290*44ceda0aSAbdelkader Boudih apple_bl_hw_get(struct acpi_apple_softc *sc)
291*44ceda0aSAbdelkader Boudih {
292*44ceda0aSAbdelkader Boudih 	if (apple_bl_gmux_active(sc))
293*44ceda0aSAbdelkader Boudih 		return (gmux_read32(sc,
294*44ceda0aSAbdelkader Boudih 		    GMUX_PORT_BRIGHTNESS) & GMUX_BRIGHTNESS_MASK);
295*44ceda0aSAbdelkader Boudih 
296*44ceda0aSAbdelkader Boudih 	return ((apple_bl_read(sc, sc->bl_hw->ctrl.offset) &
297*44ceda0aSAbdelkader Boudih 	    sc->bl_hw->ctrl.mask) >> sc->bl_hw->ctrl.shift);
298*44ceda0aSAbdelkader Boudih }
299*44ceda0aSAbdelkader Boudih 
300*44ceda0aSAbdelkader Boudih /* Resolve max brightness, enable the PWM if needed, cache brightness. */
301*44ceda0aSAbdelkader Boudih static void
apple_bl_mmio_init(struct acpi_apple_softc * sc,const struct apple_bl_hw * hw)302*44ceda0aSAbdelkader Boudih apple_bl_mmio_init(struct acpi_apple_softc *sc, const struct apple_bl_hw *hw)
303*44ceda0aSAbdelkader Boudih {
304*44ceda0aSAbdelkader Boudih 	uint32_t val;
305*44ceda0aSAbdelkader Boudih 
306*44ceda0aSAbdelkader Boudih 	if (hw->max_reg.offset != 0) {
307*44ceda0aSAbdelkader Boudih 		sc->bl_max = (apple_bl_read(sc, hw->max_reg.offset) &
308*44ceda0aSAbdelkader Boudih 		    hw->max_reg.mask) >> hw->max_reg.shift;
309*44ceda0aSAbdelkader Boudih 		if (sc->bl_max == 0)
310*44ceda0aSAbdelkader Boudih 			sc->bl_max = hw->max;
311*44ceda0aSAbdelkader Boudih 	} else {
312*44ceda0aSAbdelkader Boudih 		sc->bl_max = hw->max;
313*44ceda0aSAbdelkader Boudih 	}
314*44ceda0aSAbdelkader Boudih 
315*44ceda0aSAbdelkader Boudih 	if (hw->enable.offset != 0) {
316*44ceda0aSAbdelkader Boudih 		val = apple_bl_read(sc, hw->enable.offset);
317*44ceda0aSAbdelkader Boudih 		if ((val & hw->enable_val) != hw->enable_val) {
318*44ceda0aSAbdelkader Boudih 			val |= hw->enable_val;
319*44ceda0aSAbdelkader Boudih 			apple_bl_write(sc, hw->enable.offset, val);
320*44ceda0aSAbdelkader Boudih 		}
321*44ceda0aSAbdelkader Boudih 	}
322*44ceda0aSAbdelkader Boudih 
323*44ceda0aSAbdelkader Boudih 	if (sc->bl_max > 0)
324*44ceda0aSAbdelkader Boudih 		sc->bl_brightness = apple_bl_hw_get(sc) * 100 / sc->bl_max;
325*44ceda0aSAbdelkader Boudih }
326*44ceda0aSAbdelkader Boudih 
327*44ceda0aSAbdelkader Boudih static void
apple_bl_hw_set(struct acpi_apple_softc * sc,int level)328*44ceda0aSAbdelkader Boudih apple_bl_hw_set(struct acpi_apple_softc *sc, int level)
329*44ceda0aSAbdelkader Boudih {
330*44ceda0aSAbdelkader Boudih 	uint32_t val;
331*44ceda0aSAbdelkader Boudih 
332*44ceda0aSAbdelkader Boudih 	if (apple_bl_gmux_active(sc)) {
333*44ceda0aSAbdelkader Boudih 		gmux_write32(sc, GMUX_PORT_BRIGHTNESS,
334*44ceda0aSAbdelkader Boudih 		    level & GMUX_BRIGHTNESS_MASK);
335*44ceda0aSAbdelkader Boudih 		return;
336*44ceda0aSAbdelkader Boudih 	}
337*44ceda0aSAbdelkader Boudih 
338*44ceda0aSAbdelkader Boudih 	val = apple_bl_read(sc, sc->bl_hw->ctrl.offset);
339*44ceda0aSAbdelkader Boudih 	val = (val & ~sc->bl_hw->ctrl.mask) |
340*44ceda0aSAbdelkader Boudih 	    ((level << sc->bl_hw->ctrl.shift) & sc->bl_hw->ctrl.mask);
341*44ceda0aSAbdelkader Boudih 	apple_bl_write(sc, sc->bl_hw->ctrl.offset, val);
342*44ceda0aSAbdelkader Boudih }
343*44ceda0aSAbdelkader Boudih 
344*44ceda0aSAbdelkader Boudih static const struct apple_bl_hw *
apple_bl_find_gpu(device_t * gpu_devp)345*44ceda0aSAbdelkader Boudih apple_bl_find_gpu(device_t *gpu_devp)
346*44ceda0aSAbdelkader Boudih {
347*44ceda0aSAbdelkader Boudih 	const struct apple_bl_match *m;
348*44ceda0aSAbdelkader Boudih 	device_t gpu;
349*44ceda0aSAbdelkader Boudih 	uint16_t vendor, device;
350*44ceda0aSAbdelkader Boudih 
351*44ceda0aSAbdelkader Boudih 	gpu = pci_find_class(PCIC_DISPLAY, PCIS_DISPLAY_VGA);
352*44ceda0aSAbdelkader Boudih 	if (gpu == NULL)
353*44ceda0aSAbdelkader Boudih 		return (NULL);
354*44ceda0aSAbdelkader Boudih 
355*44ceda0aSAbdelkader Boudih 	vendor = pci_get_vendor(gpu);
356*44ceda0aSAbdelkader Boudih 	device = pci_get_device(gpu);
357*44ceda0aSAbdelkader Boudih 
358*44ceda0aSAbdelkader Boudih 	for (m = apple_bl_matches; m->vendor != 0; m++) {
359*44ceda0aSAbdelkader Boudih 		if (vendor != m->vendor)
360*44ceda0aSAbdelkader Boudih 			continue;
361*44ceda0aSAbdelkader Boudih 		if (m->dev_lo != 0 &&
362*44ceda0aSAbdelkader Boudih 		    (device < m->dev_lo || device > m->dev_hi))
363*44ceda0aSAbdelkader Boudih 			continue;
364*44ceda0aSAbdelkader Boudih 		*gpu_devp = gpu;
365*44ceda0aSAbdelkader Boudih 		return (m->hw);
366*44ceda0aSAbdelkader Boudih 	}
367*44ceda0aSAbdelkader Boudih 
368*44ceda0aSAbdelkader Boudih 	return (NULL);
369*44ceda0aSAbdelkader Boudih }
370*44ceda0aSAbdelkader Boudih 
371*44ceda0aSAbdelkader Boudih static void *
apple_bl_map_bar(device_t dev,device_t gpu,const struct apple_bl_hw * hw)372*44ceda0aSAbdelkader Boudih apple_bl_map_bar(device_t dev, device_t gpu, const struct apple_bl_hw *hw)
373*44ceda0aSAbdelkader Boudih {
374*44ceda0aSAbdelkader Boudih 	uint64_t bar_addr;
375*44ceda0aSAbdelkader Boudih 	uint32_t bar_lo, bar_hi;
376*44ceda0aSAbdelkader Boudih 
377*44ceda0aSAbdelkader Boudih 	bar_lo = pci_read_config(gpu, PCIR_BAR(hw->bar), 4);
378*44ceda0aSAbdelkader Boudih 
379*44ceda0aSAbdelkader Boudih 	if (PCI_BAR_IO(bar_lo)) {
380*44ceda0aSAbdelkader Boudih 		device_printf(dev, "BAR%d is I/O, expected memory\n", hw->bar);
381*44ceda0aSAbdelkader Boudih 		return (NULL);
382*44ceda0aSAbdelkader Boudih 	}
383*44ceda0aSAbdelkader Boudih 
384*44ceda0aSAbdelkader Boudih 	if ((bar_lo & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) {
385*44ceda0aSAbdelkader Boudih 		bar_hi = pci_read_config(gpu, PCIR_BAR(hw->bar) + 4, 4);
386*44ceda0aSAbdelkader Boudih 		bar_addr = ((uint64_t)bar_hi << 32) |
387*44ceda0aSAbdelkader Boudih 		    (bar_lo & PCIM_BAR_MEM_BASE);
388*44ceda0aSAbdelkader Boudih 	} else {
389*44ceda0aSAbdelkader Boudih 		bar_addr = bar_lo & PCIM_BAR_MEM_BASE;
390*44ceda0aSAbdelkader Boudih 	}
391*44ceda0aSAbdelkader Boudih 
392*44ceda0aSAbdelkader Boudih 	if (bar_addr == 0) {
393*44ceda0aSAbdelkader Boudih 		device_printf(dev, "BAR%d address is zero\n", hw->bar);
394*44ceda0aSAbdelkader Boudih 		return (NULL);
395*44ceda0aSAbdelkader Boudih 	}
396*44ceda0aSAbdelkader Boudih 
397*44ceda0aSAbdelkader Boudih 	return (pmap_mapdev(bar_addr, hw->mapsize));
398*44ceda0aSAbdelkader Boudih }
399*44ceda0aSAbdelkader Boudih 
400*44ceda0aSAbdelkader Boudih /*
401*44ceda0aSAbdelkader Boudih  * Probe for GMUX.  Locate APP000B in the ACPI namespace by HID, consume
402*44ceda0aSAbdelkader Boudih  * its _CRS resource, and detect which I/O protocol the hardware speaks.
403*44ceda0aSAbdelkader Boudih  */
404*44ceda0aSAbdelkader Boudih #define	GMUX_ACPI_HID	"APP000B"
405*44ceda0aSAbdelkader Boudih 
406*44ceda0aSAbdelkader Boudih struct apple_bl_gmux_crs {
407*44ceda0aSAbdelkader Boudih 	uint16_t	io_base;
408*44ceda0aSAbdelkader Boudih 	uint32_t	io_len;
409*44ceda0aSAbdelkader Boudih 	uint64_t	mem_base;
410*44ceda0aSAbdelkader Boudih };
411*44ceda0aSAbdelkader Boudih 
412*44ceda0aSAbdelkader Boudih static ACPI_STATUS
apple_bl_gmux_found(ACPI_HANDLE handle,UINT32 level __unused,void * ctx,void ** retval __unused)413*44ceda0aSAbdelkader Boudih apple_bl_gmux_found(ACPI_HANDLE handle, UINT32 level __unused, void *ctx,
414*44ceda0aSAbdelkader Boudih     void **retval __unused)
415*44ceda0aSAbdelkader Boudih {
416*44ceda0aSAbdelkader Boudih 	*(ACPI_HANDLE *)ctx = handle;
417*44ceda0aSAbdelkader Boudih 	return (AE_CTRL_TERMINATE);
418*44ceda0aSAbdelkader Boudih }
419*44ceda0aSAbdelkader Boudih 
420*44ceda0aSAbdelkader Boudih static ACPI_STATUS
apple_bl_gmux_crs_cb(ACPI_RESOURCE * res,void * ctx)421*44ceda0aSAbdelkader Boudih apple_bl_gmux_crs_cb(ACPI_RESOURCE *res, void *ctx)
422*44ceda0aSAbdelkader Boudih {
423*44ceda0aSAbdelkader Boudih 	struct apple_bl_gmux_crs *crs = ctx;
424*44ceda0aSAbdelkader Boudih 
425*44ceda0aSAbdelkader Boudih 	switch (res->Type) {
426*44ceda0aSAbdelkader Boudih 	case ACPI_RESOURCE_TYPE_IO:
427*44ceda0aSAbdelkader Boudih 		crs->io_base = res->Data.Io.Minimum;
428*44ceda0aSAbdelkader Boudih 		crs->io_len = res->Data.Io.AddressLength;
429*44ceda0aSAbdelkader Boudih 		break;
430*44ceda0aSAbdelkader Boudih 	case ACPI_RESOURCE_TYPE_FIXED_IO:
431*44ceda0aSAbdelkader Boudih 		crs->io_base = res->Data.FixedIo.Address;
432*44ceda0aSAbdelkader Boudih 		crs->io_len = res->Data.FixedIo.AddressLength;
433*44ceda0aSAbdelkader Boudih 		break;
434*44ceda0aSAbdelkader Boudih 	case ACPI_RESOURCE_TYPE_MEMORY32:
435*44ceda0aSAbdelkader Boudih 		crs->mem_base = res->Data.Memory32.Minimum;
436*44ceda0aSAbdelkader Boudih 		break;
437*44ceda0aSAbdelkader Boudih 	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
438*44ceda0aSAbdelkader Boudih 		crs->mem_base = res->Data.FixedMemory32.Address;
439*44ceda0aSAbdelkader Boudih 		break;
440*44ceda0aSAbdelkader Boudih 	default:
441*44ceda0aSAbdelkader Boudih 		break;
442*44ceda0aSAbdelkader Boudih 	}
443*44ceda0aSAbdelkader Boudih 	return (AE_OK);
444*44ceda0aSAbdelkader Boudih }
445*44ceda0aSAbdelkader Boudih 
446*44ceda0aSAbdelkader Boudih static bool
apple_bl_is_indexed(uint16_t base)447*44ceda0aSAbdelkader Boudih apple_bl_is_indexed(uint16_t base)
448*44ceda0aSAbdelkader Boudih {
449*44ceda0aSAbdelkader Boudih 	uint16_t val;
450*44ceda0aSAbdelkader Boudih 
451*44ceda0aSAbdelkader Boudih 	outb(base + GMUX_PORT_INDEXED_MAGIC1, GMUX_INDEXED_UNLOCK1);
452*44ceda0aSAbdelkader Boudih 	outb(base + GMUX_PORT_INDEXED_MAGIC2, GMUX_INDEXED_UNLOCK2);
453*44ceda0aSAbdelkader Boudih 	outb(base + GMUX_PORT_INDEXED_MAGIC3, GMUX_INDEXED_UNLOCK3);
454*44ceda0aSAbdelkader Boudih 
455*44ceda0aSAbdelkader Boudih 	val = inb(base + GMUX_PORT_INDEXED_MAGIC1) |
456*44ceda0aSAbdelkader Boudih 	    ((uint16_t)inb(base + GMUX_PORT_INDEXED_MAGIC2) << 8);
457*44ceda0aSAbdelkader Boudih 	return (val == GMUX_INDEXED_SIGNATURE);
458*44ceda0aSAbdelkader Boudih }
459*44ceda0aSAbdelkader Boudih 
460*44ceda0aSAbdelkader Boudih static bool
apple_bl_is_mmio(void * mmio)461*44ceda0aSAbdelkader Boudih apple_bl_is_mmio(void *mmio)
462*44ceda0aSAbdelkader Boudih {
463*44ceda0aSAbdelkader Boudih 	return (gmux_mmio_read8(mmio, GMUX_MMIO_COMMAND_SEND) !=
464*44ceda0aSAbdelkader Boudih 	    GMUX_FLOATING);
465*44ceda0aSAbdelkader Boudih }
466*44ceda0aSAbdelkader Boudih 
467*44ceda0aSAbdelkader Boudih static bool
apple_bl_find_gmux(struct acpi_apple_softc * sc)468*44ceda0aSAbdelkader Boudih apple_bl_find_gmux(struct acpi_apple_softc *sc)
469*44ceda0aSAbdelkader Boudih {
470*44ceda0aSAbdelkader Boudih 	ACPI_HANDLE gmux_handle;
471*44ceda0aSAbdelkader Boudih 	struct apple_bl_gmux_crs crs;
472*44ceda0aSAbdelkader Boudih 	uint16_t base;
473*44ceda0aSAbdelkader Boudih 	uint8_t ver_major, ver_minor, ver_release;
474*44ceda0aSAbdelkader Boudih 	uint32_t version;
475*44ceda0aSAbdelkader Boudih 
476*44ceda0aSAbdelkader Boudih 	gmux_handle = NULL;
477*44ceda0aSAbdelkader Boudih 	AcpiGetDevices(GMUX_ACPI_HID, apple_bl_gmux_found, &gmux_handle, NULL);
478*44ceda0aSAbdelkader Boudih 	if (gmux_handle == NULL)
479*44ceda0aSAbdelkader Boudih 		return (false);
480*44ceda0aSAbdelkader Boudih 
481*44ceda0aSAbdelkader Boudih 	bzero(&crs, sizeof(crs));
482*44ceda0aSAbdelkader Boudih 	if (ACPI_FAILURE(AcpiWalkResources(gmux_handle, "_CRS",
483*44ceda0aSAbdelkader Boudih 	    apple_bl_gmux_crs_cb, &crs)))
484*44ceda0aSAbdelkader Boudih 		return (false);
485*44ceda0aSAbdelkader Boudih 
486*44ceda0aSAbdelkader Boudih 	if (crs.io_base != 0 && crs.io_len >= GMUX_MIN_IO_LEN) {
487*44ceda0aSAbdelkader Boudih 		base = crs.io_base;
488*44ceda0aSAbdelkader Boudih 
489*44ceda0aSAbdelkader Boudih 		/* Try direct PIO first (classic/pre-Retina GMUX). */
490*44ceda0aSAbdelkader Boudih 		ver_major = inb(base + GMUX_PORT_VERSION_MAJOR);
491*44ceda0aSAbdelkader Boudih 		ver_minor = inb(base + GMUX_PORT_VERSION_MINOR);
492*44ceda0aSAbdelkader Boudih 		ver_release = inb(base + GMUX_PORT_VERSION_RELEASE);
493*44ceda0aSAbdelkader Boudih 
494*44ceda0aSAbdelkader Boudih 		if (ver_major != GMUX_FLOATING ||
495*44ceda0aSAbdelkader Boudih 		    ver_minor != GMUX_FLOATING) {
496*44ceda0aSAbdelkader Boudih 			sc->bl_gmux_base = base;
497*44ceda0aSAbdelkader Boudih 			sc->bl_gmux_type = GMUX_TYPE_PIO;
498*44ceda0aSAbdelkader Boudih 			device_printf(sc->dev,
499*44ceda0aSAbdelkader Boudih 			    "GMUX v%d.%d.%d [classic] at 0x%x\n",
500*44ceda0aSAbdelkader Boudih 			    ver_major, ver_minor, ver_release, base);
501*44ceda0aSAbdelkader Boudih 			return (true);
502*44ceda0aSAbdelkader Boudih 		}
503*44ceda0aSAbdelkader Boudih 
504*44ceda0aSAbdelkader Boudih 		/* The bus floated high - check for indexed GMUX. */
505*44ceda0aSAbdelkader Boudih 		if (apple_bl_is_indexed(base)) {
506*44ceda0aSAbdelkader Boudih 			sc->bl_gmux_base = base;
507*44ceda0aSAbdelkader Boudih 			sc->bl_gmux_type = GMUX_TYPE_INDEXED;
508*44ceda0aSAbdelkader Boudih 
509*44ceda0aSAbdelkader Boudih 			version = gmux_index_read32(base,
510*44ceda0aSAbdelkader Boudih 			    GMUX_PORT_VERSION_MAJOR);
511*44ceda0aSAbdelkader Boudih 			ver_major = GMUX_VERSION_MAJOR(version);
512*44ceda0aSAbdelkader Boudih 			ver_minor = GMUX_VERSION_MINOR(version);
513*44ceda0aSAbdelkader Boudih 			ver_release = GMUX_VERSION_RELEASE(version);
514*44ceda0aSAbdelkader Boudih 
515*44ceda0aSAbdelkader Boudih 			device_printf(sc->dev,
516*44ceda0aSAbdelkader Boudih 			    "GMUX v%d.%d.%d [indexed] at 0x%x\n",
517*44ceda0aSAbdelkader Boudih 			    ver_major, ver_minor, ver_release, base);
518*44ceda0aSAbdelkader Boudih 			return (true);
519*44ceda0aSAbdelkader Boudih 		}
520*44ceda0aSAbdelkader Boudih 	}
521*44ceda0aSAbdelkader Boudih 
522*44ceda0aSAbdelkader Boudih 	/* No usable I/O resource - try the MMIO protocol (T2 GMUX). */
523*44ceda0aSAbdelkader Boudih 	if (crs.mem_base == 0)
524*44ceda0aSAbdelkader Boudih 		return (false);
525*44ceda0aSAbdelkader Boudih 
526*44ceda0aSAbdelkader Boudih 	sc->bl_gmux_mmio = pmap_mapdev(crs.mem_base, GMUX_MIN_IO_LEN);
527*44ceda0aSAbdelkader Boudih 	if (sc->bl_gmux_mmio == NULL)
528*44ceda0aSAbdelkader Boudih 		return (false);
529*44ceda0aSAbdelkader Boudih 
530*44ceda0aSAbdelkader Boudih 	if (!apple_bl_is_mmio(sc->bl_gmux_mmio)) {
531*44ceda0aSAbdelkader Boudih 		pmap_unmapdev(sc->bl_gmux_mmio, GMUX_MIN_IO_LEN);
532*44ceda0aSAbdelkader Boudih 		sc->bl_gmux_mmio = NULL;
533*44ceda0aSAbdelkader Boudih 		return (false);
534*44ceda0aSAbdelkader Boudih 	}
535*44ceda0aSAbdelkader Boudih 
536*44ceda0aSAbdelkader Boudih 	sc->bl_gmux_type = GMUX_TYPE_MMIO;
537*44ceda0aSAbdelkader Boudih 
538*44ceda0aSAbdelkader Boudih 	version = gmux_mmio_read32(sc->bl_gmux_mmio, GMUX_PORT_VERSION_MAJOR);
539*44ceda0aSAbdelkader Boudih 	ver_major = GMUX_VERSION_MAJOR(version);
540*44ceda0aSAbdelkader Boudih 	ver_minor = GMUX_VERSION_MINOR(version);
541*44ceda0aSAbdelkader Boudih 	ver_release = GMUX_VERSION_RELEASE(version);
542*44ceda0aSAbdelkader Boudih 
543*44ceda0aSAbdelkader Boudih 	device_printf(sc->dev, "GMUX v%d.%d.%d [mmio] at %#jx\n",
544*44ceda0aSAbdelkader Boudih 	    ver_major, ver_minor, ver_release, (uintmax_t)crs.mem_base);
545*44ceda0aSAbdelkader Boudih 	return (true);
546*44ceda0aSAbdelkader Boudih }
547*44ceda0aSAbdelkader Boudih 
548*44ceda0aSAbdelkader Boudih static int
acpi_apple_backlight_attach_gmux(struct acpi_apple_softc * sc)549*44ceda0aSAbdelkader Boudih acpi_apple_backlight_attach_gmux(struct acpi_apple_softc *sc)
550*44ceda0aSAbdelkader Boudih {
551*44ceda0aSAbdelkader Boudih 	uint32_t raw_max;
552*44ceda0aSAbdelkader Boudih 
553*44ceda0aSAbdelkader Boudih 	mtx_init(&sc->bl_mtx, "apple_bl", NULL, MTX_DEF);
554*44ceda0aSAbdelkader Boudih 
555*44ceda0aSAbdelkader Boudih 	raw_max = gmux_read32(sc, GMUX_PORT_MAX_BRIGHTNESS) &
556*44ceda0aSAbdelkader Boudih 	    GMUX_BRIGHTNESS_MASK;
557*44ceda0aSAbdelkader Boudih 	sc->bl_max = (raw_max > 0) ? raw_max : GMUX_BRIGHTNESS_MASK;
558*44ceda0aSAbdelkader Boudih 
559*44ceda0aSAbdelkader Boudih 	if (sc->bl_max > 0)
560*44ceda0aSAbdelkader Boudih 		sc->bl_brightness = apple_bl_hw_get(sc) * 100 / sc->bl_max;
561*44ceda0aSAbdelkader Boudih 
562*44ceda0aSAbdelkader Boudih 	sc->bl_cdev = backlight_register("apple_bl", sc->dev);
563*44ceda0aSAbdelkader Boudih 	if (sc->bl_cdev == NULL) {
564*44ceda0aSAbdelkader Boudih 		device_printf(sc->dev, "cannot register backlight\n");
565*44ceda0aSAbdelkader Boudih 		apple_bl_gmux_teardown(sc);
566*44ceda0aSAbdelkader Boudih 		mtx_destroy(&sc->bl_mtx);
567*44ceda0aSAbdelkader Boudih 		return (ENXIO);
568*44ceda0aSAbdelkader Boudih 	}
569*44ceda0aSAbdelkader Boudih 
570*44ceda0aSAbdelkader Boudih 	device_printf(sc->dev, "gmux brightness %d/%d\n",
571*44ceda0aSAbdelkader Boudih 	    apple_bl_hw_get(sc), sc->bl_max);
572*44ceda0aSAbdelkader Boudih 
573*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_BEGIN(apple);
574*44ceda0aSAbdelkader Boudih 	apple_backlight_sc = sc;
575*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_END(apple);
576*44ceda0aSAbdelkader Boudih 	return (0);
577*44ceda0aSAbdelkader Boudih }
578*44ceda0aSAbdelkader Boudih 
579*44ceda0aSAbdelkader Boudih static int
apple_bl_attach_gpu(struct acpi_apple_softc * sc,device_t gpu,const struct apple_bl_hw * hw)580*44ceda0aSAbdelkader Boudih apple_bl_attach_gpu(struct acpi_apple_softc *sc, device_t gpu,
581*44ceda0aSAbdelkader Boudih     const struct apple_bl_hw *hw)
582*44ceda0aSAbdelkader Boudih {
583*44ceda0aSAbdelkader Boudih 	sc->bl_hw = hw;
584*44ceda0aSAbdelkader Boudih 
585*44ceda0aSAbdelkader Boudih 	sc->bl_mmio = apple_bl_map_bar(sc->dev, gpu, hw);
586*44ceda0aSAbdelkader Boudih 	if (sc->bl_mmio == NULL)
587*44ceda0aSAbdelkader Boudih 		return (ENXIO);
588*44ceda0aSAbdelkader Boudih 
589*44ceda0aSAbdelkader Boudih 	mtx_init(&sc->bl_mtx, "apple_bl", NULL, MTX_DEF);
590*44ceda0aSAbdelkader Boudih 	apple_bl_mmio_init(sc, hw);
591*44ceda0aSAbdelkader Boudih 
592*44ceda0aSAbdelkader Boudih 	sc->bl_cdev = backlight_register("apple_bl", sc->dev);
593*44ceda0aSAbdelkader Boudih 	if (sc->bl_cdev == NULL) {
594*44ceda0aSAbdelkader Boudih 		device_printf(sc->dev, "cannot register backlight\n");
595*44ceda0aSAbdelkader Boudih 		mtx_destroy(&sc->bl_mtx);
596*44ceda0aSAbdelkader Boudih 		pmap_unmapdev(sc->bl_mmio, hw->mapsize);
597*44ceda0aSAbdelkader Boudih 		sc->bl_mmio = NULL;
598*44ceda0aSAbdelkader Boudih 		return (ENXIO);
599*44ceda0aSAbdelkader Boudih 	}
600*44ceda0aSAbdelkader Boudih 
601*44ceda0aSAbdelkader Boudih 	device_printf(sc->dev, "%s, brightness %d/%d\n",
602*44ceda0aSAbdelkader Boudih 	    hw->name, apple_bl_hw_get(sc), sc->bl_max);
603*44ceda0aSAbdelkader Boudih 
604*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_BEGIN(apple);
605*44ceda0aSAbdelkader Boudih 	apple_backlight_sc = sc;
606*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_END(apple);
607*44ceda0aSAbdelkader Boudih 	return (0);
608*44ceda0aSAbdelkader Boudih }
609*44ceda0aSAbdelkader Boudih 
610*44ceda0aSAbdelkader Boudih static void
acpi_apple_backlight_deferred(void * arg)611*44ceda0aSAbdelkader Boudih acpi_apple_backlight_deferred(void *arg)
612*44ceda0aSAbdelkader Boudih {
613*44ceda0aSAbdelkader Boudih 	struct acpi_apple_softc *sc = arg;
614*44ceda0aSAbdelkader Boudih 	device_t gpu;
615*44ceda0aSAbdelkader Boudih 	const struct apple_bl_hw *hw;
616*44ceda0aSAbdelkader Boudih 
617*44ceda0aSAbdelkader Boudih 	hw = apple_bl_find_gpu(&gpu);
618*44ceda0aSAbdelkader Boudih 	if (hw == NULL) {
619*44ceda0aSAbdelkader Boudih 		device_printf(sc->dev, "no supported GPU found\n");
620*44ceda0aSAbdelkader Boudih 		return;
621*44ceda0aSAbdelkader Boudih 	}
622*44ceda0aSAbdelkader Boudih 
623*44ceda0aSAbdelkader Boudih 	(void)apple_bl_attach_gpu(sc, gpu, hw);
624*44ceda0aSAbdelkader Boudih }
625*44ceda0aSAbdelkader Boudih 
626*44ceda0aSAbdelkader Boudih static int
acpi_apple_backlight_attach_mmio(struct acpi_apple_softc * sc)627*44ceda0aSAbdelkader Boudih acpi_apple_backlight_attach_mmio(struct acpi_apple_softc *sc)
628*44ceda0aSAbdelkader Boudih {
629*44ceda0aSAbdelkader Boudih 	device_t gpu;
630*44ceda0aSAbdelkader Boudih 	const struct apple_bl_hw *hw;
631*44ceda0aSAbdelkader Boudih 
632*44ceda0aSAbdelkader Boudih 	hw = apple_bl_find_gpu(&gpu);
633*44ceda0aSAbdelkader Boudih 	if (hw == NULL) {
634*44ceda0aSAbdelkader Boudih 		/*
635*44ceda0aSAbdelkader Boudih 		 * GPU not yet attached (ACPI bus enumerates before PCI).
636*44ceda0aSAbdelkader Boudih 		 * Defer the lookup until after all buses finish probing.
637*44ceda0aSAbdelkader Boudih 		 */
638*44ceda0aSAbdelkader Boudih 		config_intrhook_oneshot(acpi_apple_backlight_deferred, sc);
639*44ceda0aSAbdelkader Boudih 		return (0);
640*44ceda0aSAbdelkader Boudih 	}
641*44ceda0aSAbdelkader Boudih 
642*44ceda0aSAbdelkader Boudih 	return (apple_bl_attach_gpu(sc, gpu, hw));
643*44ceda0aSAbdelkader Boudih }
644*44ceda0aSAbdelkader Boudih 
645*44ceda0aSAbdelkader Boudih int
acpi_apple_backlight_attach(struct acpi_apple_softc * sc)646*44ceda0aSAbdelkader Boudih acpi_apple_backlight_attach(struct acpi_apple_softc *sc)
647*44ceda0aSAbdelkader Boudih {
648*44ceda0aSAbdelkader Boudih 	if (apple_bl_find_gmux(sc))
649*44ceda0aSAbdelkader Boudih 		return (acpi_apple_backlight_attach_gmux(sc));
650*44ceda0aSAbdelkader Boudih 
651*44ceda0aSAbdelkader Boudih 	return (acpi_apple_backlight_attach_mmio(sc));
652*44ceda0aSAbdelkader Boudih }
653*44ceda0aSAbdelkader Boudih 
654*44ceda0aSAbdelkader Boudih void
acpi_apple_backlight_detach(struct acpi_apple_softc * sc)655*44ceda0aSAbdelkader Boudih acpi_apple_backlight_detach(struct acpi_apple_softc *sc)
656*44ceda0aSAbdelkader Boudih {
657*44ceda0aSAbdelkader Boudih 	bool had_gmux;
658*44ceda0aSAbdelkader Boudih 
659*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_BEGIN(apple);
660*44ceda0aSAbdelkader Boudih 	if (apple_backlight_sc == sc)
661*44ceda0aSAbdelkader Boudih 		apple_backlight_sc = NULL;
662*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_END(apple);
663*44ceda0aSAbdelkader Boudih 
664*44ceda0aSAbdelkader Boudih 	had_gmux = apple_bl_gmux_active(sc);
665*44ceda0aSAbdelkader Boudih 
666*44ceda0aSAbdelkader Boudih 	if (sc->bl_cdev != NULL)
667*44ceda0aSAbdelkader Boudih 		backlight_destroy(sc->bl_cdev);
668*44ceda0aSAbdelkader Boudih 	if (sc->bl_mmio != NULL)
669*44ceda0aSAbdelkader Boudih 		pmap_unmapdev(sc->bl_mmio, sc->bl_hw->mapsize);
670*44ceda0aSAbdelkader Boudih 	apple_bl_gmux_teardown(sc);
671*44ceda0aSAbdelkader Boudih 	if (sc->bl_cdev != NULL || sc->bl_mmio != NULL || had_gmux)
672*44ceda0aSAbdelkader Boudih 		mtx_destroy(&sc->bl_mtx);
673*44ceda0aSAbdelkader Boudih }
674*44ceda0aSAbdelkader Boudih 
675*44ceda0aSAbdelkader Boudih static inline bool
apple_bl_ready(struct acpi_apple_softc * sc)676*44ceda0aSAbdelkader Boudih apple_bl_ready(struct acpi_apple_softc *sc)
677*44ceda0aSAbdelkader Boudih {
678*44ceda0aSAbdelkader Boudih 	return (sc->bl_hw != NULL || apple_bl_gmux_active(sc));
679*44ceda0aSAbdelkader Boudih }
680*44ceda0aSAbdelkader Boudih 
681*44ceda0aSAbdelkader Boudih static inline int
apple_bl_pct_to_hw(struct acpi_apple_softc * sc,int pct)682*44ceda0aSAbdelkader Boudih apple_bl_pct_to_hw(struct acpi_apple_softc *sc, int pct)
683*44ceda0aSAbdelkader Boudih {
684*44ceda0aSAbdelkader Boudih 	int level;
685*44ceda0aSAbdelkader Boudih 
686*44ceda0aSAbdelkader Boudih 	level = pct * sc->bl_max / 100;
687*44ceda0aSAbdelkader Boudih 	if (level > (int)sc->bl_max)
688*44ceda0aSAbdelkader Boudih 		level = sc->bl_max;
689*44ceda0aSAbdelkader Boudih 	return (level);
690*44ceda0aSAbdelkader Boudih }
691*44ceda0aSAbdelkader Boudih 
692*44ceda0aSAbdelkader Boudih int
acpi_apple_bl_update_status(device_t dev,struct backlight_props * props)693*44ceda0aSAbdelkader Boudih acpi_apple_bl_update_status(device_t dev, struct backlight_props *props)
694*44ceda0aSAbdelkader Boudih {
695*44ceda0aSAbdelkader Boudih 	struct acpi_apple_softc *sc;
696*44ceda0aSAbdelkader Boudih 
697*44ceda0aSAbdelkader Boudih 	sc = device_get_softc(dev);
698*44ceda0aSAbdelkader Boudih 	if (!apple_bl_ready(sc))
699*44ceda0aSAbdelkader Boudih 		return (ENXIO);
700*44ceda0aSAbdelkader Boudih 	mtx_lock(&sc->bl_mtx);
701*44ceda0aSAbdelkader Boudih 	apple_bl_hw_set(sc, apple_bl_pct_to_hw(sc, props->brightness));
702*44ceda0aSAbdelkader Boudih 	sc->bl_brightness = props->brightness;
703*44ceda0aSAbdelkader Boudih 	mtx_unlock(&sc->bl_mtx);
704*44ceda0aSAbdelkader Boudih 
705*44ceda0aSAbdelkader Boudih 	return (0);
706*44ceda0aSAbdelkader Boudih }
707*44ceda0aSAbdelkader Boudih 
708*44ceda0aSAbdelkader Boudih int
acpi_apple_bl_get_status(device_t dev,struct backlight_props * props)709*44ceda0aSAbdelkader Boudih acpi_apple_bl_get_status(device_t dev, struct backlight_props *props)
710*44ceda0aSAbdelkader Boudih {
711*44ceda0aSAbdelkader Boudih 	struct acpi_apple_softc *sc;
712*44ceda0aSAbdelkader Boudih 
713*44ceda0aSAbdelkader Boudih 	sc = device_get_softc(dev);
714*44ceda0aSAbdelkader Boudih 	if (!apple_bl_ready(sc))
715*44ceda0aSAbdelkader Boudih 		return (ENXIO);
716*44ceda0aSAbdelkader Boudih 	props->brightness = sc->bl_brightness;
717*44ceda0aSAbdelkader Boudih 	props->nlevels = 0;
718*44ceda0aSAbdelkader Boudih 
719*44ceda0aSAbdelkader Boudih 	return (0);
720*44ceda0aSAbdelkader Boudih }
721*44ceda0aSAbdelkader Boudih 
722*44ceda0aSAbdelkader Boudih int
acpi_apple_bl_get_info(device_t dev,struct backlight_info * info)723*44ceda0aSAbdelkader Boudih acpi_apple_bl_get_info(device_t dev, struct backlight_info *info)
724*44ceda0aSAbdelkader Boudih {
725*44ceda0aSAbdelkader Boudih 	info->type = BACKLIGHT_TYPE_PANEL;
726*44ceda0aSAbdelkader Boudih 	strlcpy(info->name, "Apple Panel Backlight", BACKLIGHTMAXNAMELENGTH);
727*44ceda0aSAbdelkader Boudih 	return (0);
728*44ceda0aSAbdelkader Boudih }
729*44ceda0aSAbdelkader Boudih 
730*44ceda0aSAbdelkader Boudih /*
731*44ceda0aSAbdelkader Boudih  * Restore backlight to cached brightness.  Called from the platform
732*44ceda0aSAbdelkader Boudih  * instance lid-open notify handler.  Caller holds no locks.
733*44ceda0aSAbdelkader Boudih  */
734*44ceda0aSAbdelkader Boudih void
acpi_apple_backlight_restore(void)735*44ceda0aSAbdelkader Boudih acpi_apple_backlight_restore(void)
736*44ceda0aSAbdelkader Boudih {
737*44ceda0aSAbdelkader Boudih 	struct acpi_apple_softc *sc;
738*44ceda0aSAbdelkader Boudih 
739*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_BEGIN(apple);
740*44ceda0aSAbdelkader Boudih 	sc = apple_backlight_sc;
741*44ceda0aSAbdelkader Boudih 	if (sc == NULL || !apple_bl_ready(sc)) {
742*44ceda0aSAbdelkader Boudih 		ACPI_SERIAL_END(apple);
743*44ceda0aSAbdelkader Boudih 		return;
744*44ceda0aSAbdelkader Boudih 	}
745*44ceda0aSAbdelkader Boudih 	mtx_lock(&sc->bl_mtx);
746*44ceda0aSAbdelkader Boudih 	apple_bl_hw_set(sc, apple_bl_pct_to_hw(sc, sc->bl_brightness));
747*44ceda0aSAbdelkader Boudih 	mtx_unlock(&sc->bl_mtx);
748*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_END(apple);
749*44ceda0aSAbdelkader Boudih }
750*44ceda0aSAbdelkader Boudih 
751*44ceda0aSAbdelkader Boudih /*
752*44ceda0aSAbdelkader Boudih  * Turn off backlight.  Called from the platform instance lid-close
753*44ceda0aSAbdelkader Boudih  * notify handler.  Does not change the cached brightness so that
754*44ceda0aSAbdelkader Boudih  * restore() brings it back on lid open.
755*44ceda0aSAbdelkader Boudih  */
756*44ceda0aSAbdelkader Boudih void
acpi_apple_backlight_off(void)757*44ceda0aSAbdelkader Boudih acpi_apple_backlight_off(void)
758*44ceda0aSAbdelkader Boudih {
759*44ceda0aSAbdelkader Boudih 	struct acpi_apple_softc *sc;
760*44ceda0aSAbdelkader Boudih 
761*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_BEGIN(apple);
762*44ceda0aSAbdelkader Boudih 	sc = apple_backlight_sc;
763*44ceda0aSAbdelkader Boudih 	if (sc == NULL || !apple_bl_ready(sc)) {
764*44ceda0aSAbdelkader Boudih 		ACPI_SERIAL_END(apple);
765*44ceda0aSAbdelkader Boudih 		return;
766*44ceda0aSAbdelkader Boudih 	}
767*44ceda0aSAbdelkader Boudih 	mtx_lock(&sc->bl_mtx);
768*44ceda0aSAbdelkader Boudih 	apple_bl_hw_set(sc, 0);
769*44ceda0aSAbdelkader Boudih 	mtx_unlock(&sc->bl_mtx);
770*44ceda0aSAbdelkader Boudih 	ACPI_SERIAL_END(apple);
771*44ceda0aSAbdelkader Boudih }
772