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