mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-10-28 11:25:21 +01:00
acpi_apple: Apple platform ACPI driver,
handles wake-on-LAN arming and lid state on the PCH PMC/GMUX platform instance, and panel backlight control on the PNLF instance, registering with backlight(9). Reviewed by: ziaee, adrian Differential Revision: https://reviews.freebsd.org/D58867
This commit is contained in:
1 parent
802f5e4d69
commit
d7d237ce73
9 files changed
+1255
No files matched your search
@@ -14,6 +14,7 @@ MANPACKAGE= kernel-man
|
||||
MAN= aac.4 \
|
||||
aacraid.4 \
|
||||
acpi.4 \
|
||||
${_acpi_apple.4} \
|
||||
${_acpi_asus.4} \
|
||||
${_acpi_asus_wmi.4} \
|
||||
${_acpi_dock.4} \
|
||||
@@ -956,6 +957,7 @@ _xnb.4= xnb.4
|
||||
.endif
|
||||
|
||||
.if ${MACHINE_CPUARCH} == "amd64"
|
||||
_acpi_apple.4= acpi_apple.4
|
||||
_ioat.4= ioat.4
|
||||
_iwx.4= iwx.4
|
||||
_nvdimm.4= nvdimm.4
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
.\"
|
||||
.\" Copyright (c) 2026 Abdelkader Boudih <[email protected]>
|
||||
.\"
|
||||
.\" SPDX-License-Identifier: BSD-2-Clause
|
||||
.\"
|
||||
.Dd August 16, 2026
|
||||
.Dt ACPI_APPLE 4 amd64
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm acpi_apple
|
||||
.Nd Apple platform ACPI driver
|
||||
.Sh SYNOPSIS
|
||||
.Cd device acpi_apple
|
||||
.Cd device backlight
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
driver attaches to Apple platform ACPI devices found on Intel-based
|
||||
Macs and provides two independent functions through a single driver,
|
||||
selected by the ACPI hardware ID it matches:
|
||||
.Bl -tag -width indent
|
||||
.It APP9876 , APP000B
|
||||
Platform instance.
|
||||
Attaches to the PCH PMC companion
|
||||
.Pq T2 Macs, 2018 and later
|
||||
or the GMUX GPU multiplexer companion
|
||||
.Pq Intel Macs with discrete graphics, 2006-2020 .
|
||||
Arms Wake-on-LAN on shutdown for any interface with wake enabled
|
||||
.Pq e.g. Va dev.bge.N.wake=1 ,
|
||||
and follows the ACPI lid switch, blanking the backlight when the lid is
|
||||
closed and restoring it when the lid is reopened.
|
||||
.It APP0002
|
||||
Backlight instance.
|
||||
Controls the panel backlight found on all Intel Macs with an internal
|
||||
display, and registers with the
|
||||
.Xr backlight 9
|
||||
framework.
|
||||
The correct control method is selected automatically.
|
||||
.El
|
||||
.Pp
|
||||
.Dv APP0001
|
||||
.Pq SMC companion
|
||||
is intentionally not claimed by this driver; it is owned by
|
||||
.Xr asmc 4 .
|
||||
.Sh HARDWARE
|
||||
The
|
||||
.Nm
|
||||
driver supports the Apple ACPI devices found in the DSDT of Intel-based
|
||||
Macs:
|
||||
.Pp
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
APP9876, the PCH PMC companion, present on T2 Macs
|
||||
.Pq 2018 and later .
|
||||
.It
|
||||
APP000B, the GMUX GPU multiplexer, present on Intel Macs with discrete
|
||||
graphics
|
||||
.Pq 2006-2020 ,
|
||||
including single-GPU Mac minis.
|
||||
.It
|
||||
APP0002, the panel backlight, present on all Intel Macs with a built-in
|
||||
display.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr acpi 4 ,
|
||||
.Xr asmc 4 ,
|
||||
.Xr backlight 9
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Nm
|
||||
driver first appeared in
|
||||
.Fx 16.0 .
|
||||
.Sh AUTHORS
|
||||
.An Abdelkader Boudih Aq Mt [email protected]
|
||||
@@ -848,6 +848,9 @@ dev/ale/if_ale.c optional ale pci
|
||||
dev/alpm/alpm.c optional alpm pci
|
||||
dev/amdpm/amdpm.c optional amdpm pci | nfpm pci
|
||||
dev/amdsmb/amdsmb.c optional amdsmb pci
|
||||
dev/apple_acpi/apple_acpi.c optional acpi_apple acpi pci backlight
|
||||
dev/apple_acpi/apple_backlight.c optional acpi_apple acpi pci backlight
|
||||
dev/apple_acpi/apple_platform.c optional acpi_apple acpi pci backlight
|
||||
#
|
||||
dev/aq/aq_main.c optional aq
|
||||
dev/aq/aq_media.c optional aq
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* acpi_apple(4) - FreeBSD driver for Apple platform ACPI devices.
|
||||
*
|
||||
* Handles multiple Apple ACPI devices with a single driver:
|
||||
*
|
||||
* APP000B (GMUX) / APP9876 (PCH PMC) - platform instance:
|
||||
* - Wake-on-LAN: walk ACPI namespace on shutdown, call _PSW(1) on
|
||||
* any device with ACPI_FLAG_WAKE_ENABLED (dev.X.Y.wake=1).
|
||||
* - Lid state: follow acpi_lid_event to blank and restore the
|
||||
* backlight.
|
||||
*
|
||||
* APP0002 (PNLF) - backlight instance:
|
||||
* - Panel backlight control via GPU PCI BAR MMIO registers.
|
||||
* - Supports Intel PCH/CPU, Nvidia NV40/NV50, AMD R600+ families.
|
||||
* - Registers with backlight(9) framework.
|
||||
*
|
||||
* OSDW() gates on OSYS==0x2710 ("Darwin"), installed via
|
||||
* hw.acpi.apple_darwin_osi=1. _PSW on GIGE only fires if the bge(4)
|
||||
* instance has ACPI_FLAG_WAKE_ENABLED (dev.bge.X.wake=1).
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include "opt_acpi.h"
|
||||
#include "apple_acpi.h"
|
||||
|
||||
#include "backlight_if.h"
|
||||
|
||||
#define _COMPONENT ACPI_OEM
|
||||
ACPI_MODULE_NAME("Apple")
|
||||
|
||||
/*
|
||||
* Attach to APP9876 (PCH PMC, T2 Macs), APP000B (GMUX, discrete GPU
|
||||
* Macs), or APP0002 (panel backlight, all Intel Macs with display).
|
||||
* APP0001 (SMC) is owned by asmc(4).
|
||||
*
|
||||
* BUS_PROBE_LOW_PRIORITY so a future apple_gmux driver can claim
|
||||
* APP000B at default priority.
|
||||
*/
|
||||
static char *acpi_apple_ids[] = { "APP9876", "APP000B", "APP0002", NULL };
|
||||
|
||||
static int
|
||||
acpi_apple_probe(device_t dev)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (acpi_disabled("apple"))
|
||||
return (ENXIO);
|
||||
rv = ACPI_ID_PROBE(device_get_parent(dev), dev, acpi_apple_ids, NULL);
|
||||
if (rv > 0)
|
||||
return (rv);
|
||||
|
||||
if (acpi_MatchHid(acpi_get_handle(dev), "APP0002"))
|
||||
device_set_desc(dev, "Apple Panel Backlight");
|
||||
else
|
||||
device_set_desc(dev, "Apple platform controller");
|
||||
|
||||
return (BUS_PROBE_LOW_PRIORITY);
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_apple_attach(device_t dev)
|
||||
{
|
||||
struct acpi_apple_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->dev = dev;
|
||||
sc->handle = acpi_get_handle(dev);
|
||||
|
||||
if (acpi_MatchHid(sc->handle, "APP0002"))
|
||||
sc->mode = ACPI_APPLE_BACKLIGHT;
|
||||
else
|
||||
sc->mode = ACPI_APPLE_PLATFORM;
|
||||
|
||||
if (sc->mode == ACPI_APPLE_PLATFORM)
|
||||
return (acpi_apple_platform_attach(sc));
|
||||
else
|
||||
return (acpi_apple_backlight_attach(sc));
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_apple_detach(device_t dev)
|
||||
{
|
||||
struct acpi_apple_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (sc->mode == ACPI_APPLE_PLATFORM)
|
||||
acpi_apple_platform_detach(sc);
|
||||
else
|
||||
acpi_apple_backlight_detach(sc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static device_method_t acpi_apple_methods[] = {
|
||||
DEVMETHOD(device_probe, acpi_apple_probe),
|
||||
DEVMETHOD(device_attach, acpi_apple_attach),
|
||||
DEVMETHOD(device_detach, acpi_apple_detach),
|
||||
|
||||
DEVMETHOD(backlight_update_status, acpi_apple_bl_update_status),
|
||||
DEVMETHOD(backlight_get_status, acpi_apple_bl_get_status),
|
||||
DEVMETHOD(backlight_get_info, acpi_apple_bl_get_info),
|
||||
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
static driver_t acpi_apple_driver = {
|
||||
"acpi_apple",
|
||||
acpi_apple_methods,
|
||||
sizeof(struct acpi_apple_softc),
|
||||
};
|
||||
|
||||
DRIVER_MODULE(acpi_apple, acpi, acpi_apple_driver, 0, 0);
|
||||
MODULE_DEPEND(acpi_apple, acpi, 1, 1, 1);
|
||||
MODULE_DEPEND(acpi_apple, backlight, 1, 1, 1);
|
||||
MODULE_VERSION(acpi_apple, 1);
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#ifndef _APPLE_ACPI_H_
|
||||
#define _APPLE_ACPI_H_
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/eventhandler.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/mutex.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
|
||||
#include <dev/acpica/acpivar.h>
|
||||
#include <dev/backlight/backlight.h>
|
||||
|
||||
#define ACPI_APPLE_PLATFORM 0x01 /* WoL + lid (APP000B/APP9876) */
|
||||
#define ACPI_APPLE_BACKLIGHT 0x02 /* panel backlight (APP0002) */
|
||||
|
||||
/*
|
||||
* Apple GMUX (GPU multiplexer) registers.
|
||||
* On dual-GPU MacBook Pros, the GMUX owns the backlight PWM.
|
||||
* Port offsets are relative to the GMUX resource base, consumed from
|
||||
* the APP000B device's _CRS.
|
||||
*
|
||||
* Three I/O protocols exist:
|
||||
* Classic (PIO) - pre-Retina Macs: direct inb/outb at base+port.
|
||||
* Indexed - Retina Macs (2012-2015): write port number to
|
||||
* GMUX_PORT_READ/WRITE, data at GMUX_PORT_VALUE.
|
||||
* MMIO - T2 Macs: memory-mapped, select port+direction via
|
||||
* GMUX_MMIO_PORT_SELECT, trigger with
|
||||
* GMUX_MMIO_COMMAND_SEND, data at the resource base
|
||||
* as a big-endian 32-bit value.
|
||||
*/
|
||||
#define GMUX_PORT_VERSION_MAJOR 0x04
|
||||
#define GMUX_PORT_VERSION_MINOR 0x05
|
||||
#define GMUX_PORT_VERSION_RELEASE 0x06
|
||||
#define GMUX_PORT_MAX_BRIGHTNESS 0x70
|
||||
#define GMUX_PORT_BRIGHTNESS 0x74
|
||||
#define GMUX_BRIGHTNESS_MASK 0x00ffffff
|
||||
|
||||
/* A version read returns major, minor and release packed into 32 bits. */
|
||||
#define GMUX_VERSION_MAJOR(v) (((v) >> 24) & 0xff)
|
||||
#define GMUX_VERSION_MINOR(v) (((v) >> 16) & 0xff)
|
||||
#define GMUX_VERSION_RELEASE(v) (((v) >> 8) & 0xff)
|
||||
|
||||
/*
|
||||
* An absent GMUX floats the bus high, so a read of all ones means the
|
||||
* protocol under test is not the one this machine speaks.
|
||||
*/
|
||||
#define GMUX_FLOATING 0xff
|
||||
|
||||
/* Handshake polling: microseconds per iteration, and iteration limits. */
|
||||
#define GMUX_POLL_DELAY 100
|
||||
#define GMUX_INDEXED_RETRIES 100
|
||||
#define GMUX_MMIO_RETRIES 200
|
||||
|
||||
/* Indexed GMUX registers */
|
||||
#define GMUX_PORT_VALUE 0xc2
|
||||
#define GMUX_PORT_READ 0xd0
|
||||
#define GMUX_PORT_WRITE 0xd4
|
||||
|
||||
/* Bit 0 of GMUX_PORT_WRITE: command outstanding, cleared once complete. */
|
||||
#define GMUX_INDEXED_BUSY 0x01
|
||||
|
||||
/*
|
||||
* Indexed-GMUX detection: write a fixed 3-byte unlock sequence to
|
||||
* PORT_INDEXED_{MAGIC1,MAGIC2,MAGIC3}, then read the same two ports
|
||||
* back and expect them to echo GMUX_INDEXED_SIGNATURE.
|
||||
*/
|
||||
#define GMUX_PORT_INDEXED_MAGIC1 0xcc
|
||||
#define GMUX_PORT_INDEXED_MAGIC2 0xcd
|
||||
#define GMUX_PORT_INDEXED_MAGIC3 0xce
|
||||
#define GMUX_INDEXED_UNLOCK1 0xaa
|
||||
#define GMUX_INDEXED_UNLOCK2 0x55
|
||||
#define GMUX_INDEXED_UNLOCK3 0x00
|
||||
#define GMUX_INDEXED_SIGNATURE 0x55aa
|
||||
|
||||
/* MMIO GMUX registers */
|
||||
#define GMUX_MMIO_PORT_SELECT 0x0e
|
||||
#define GMUX_MMIO_COMMAND_SEND 0x0f
|
||||
#define GMUX_MMIO_READ 0x00
|
||||
#define GMUX_MMIO_WRITE 0x40
|
||||
|
||||
/* Minimum resource length needed to reach GMUX_PORT_BRIGHTNESS+4. */
|
||||
#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
|
||||
|
||||
#define GMUX_TYPE_PIO 0
|
||||
#define GMUX_TYPE_INDEXED 1
|
||||
#define GMUX_TYPE_MMIO 2
|
||||
|
||||
struct apple_bl_reg {
|
||||
uint32_t offset;
|
||||
uint32_t mask;
|
||||
uint8_t shift;
|
||||
};
|
||||
|
||||
struct apple_bl_hw {
|
||||
const char *name;
|
||||
int bar;
|
||||
vm_size_t mapsize;
|
||||
struct apple_bl_reg ctrl;
|
||||
struct apple_bl_reg enable;
|
||||
uint32_t enable_val;
|
||||
struct apple_bl_reg max_reg;
|
||||
uint32_t max;
|
||||
};
|
||||
|
||||
struct acpi_apple_softc {
|
||||
device_t dev;
|
||||
ACPI_HANDLE handle;
|
||||
int mode;
|
||||
|
||||
/* Backlight */
|
||||
struct mtx bl_mtx; /* protects hw access + cache */
|
||||
struct cdev *bl_cdev;
|
||||
const struct apple_bl_hw *bl_hw;
|
||||
void *bl_mmio; /* GPU BAR MMIO (non-GMUX) */
|
||||
uint16_t bl_gmux_base; /* GMUX I/O port base, 0=none */
|
||||
void *bl_gmux_mmio; /* GMUX MMIO base, NULL=none */
|
||||
uint8_t bl_gmux_type; /* GMUX_TYPE_* */
|
||||
uint32_t bl_max;
|
||||
uint32_t bl_brightness; /* cached percent 0..100 */
|
||||
};
|
||||
|
||||
/* apple_platform.c */
|
||||
int acpi_apple_platform_attach(struct acpi_apple_softc *sc);
|
||||
void acpi_apple_platform_detach(struct acpi_apple_softc *sc);
|
||||
|
||||
/* apple_backlight.c */
|
||||
int acpi_apple_backlight_attach(struct acpi_apple_softc *sc);
|
||||
void acpi_apple_backlight_detach(struct acpi_apple_softc *sc);
|
||||
void acpi_apple_backlight_restore(void);
|
||||
void acpi_apple_backlight_off(void);
|
||||
int acpi_apple_bl_update_status(device_t dev,
|
||||
struct backlight_props *props);
|
||||
int acpi_apple_bl_get_status(device_t dev,
|
||||
struct backlight_props *props);
|
||||
int acpi_apple_bl_get_info(device_t dev,
|
||||
struct backlight_info *info);
|
||||
|
||||
#endif /* _APPLE_ACPI_H_ */
|
||||
@@ -0,0 +1,771 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* Apple panel backlight control.
|
||||
*
|
||||
* Two backends: GMUX (dual-GPU MacBook Pros, base+0x70/0x74 over PIO,
|
||||
* indexed I/O, or MMIO depending on platform) and direct GPU PCI BAR
|
||||
* MMIO (single-GPU Macs). GMUX is preferred whenever APP000B is
|
||||
* present and its _CRS yields a working protocol.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include "apple_acpi.h"
|
||||
|
||||
#include <sys/endian.h>
|
||||
#include <sys/systm.h>
|
||||
|
||||
#include <dev/backlight/backlight.h>
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
#include <machine/cpufunc.h>
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#include "backlight_if.h"
|
||||
|
||||
ACPI_SERIAL_DECL(apple, "Apple ACPI");
|
||||
static struct acpi_apple_softc *apple_backlight_sc;
|
||||
|
||||
static const struct apple_bl_hw apple_bl_intel_pch = {
|
||||
.name = "intel_pch",
|
||||
.bar = 0,
|
||||
.mapsize = 0xc8258,
|
||||
.ctrl = { 0xc8254, 0x0000FFFF, 0 },
|
||||
.enable = { 0xc8250, 0xC0000000, 0 },
|
||||
.enable_val = 0xC0000000,
|
||||
.max_reg = { 0xc8254, 0xFFFF0000, 16 },
|
||||
.max = 0xFFFF,
|
||||
};
|
||||
|
||||
static const struct apple_bl_hw apple_bl_intel_cpu = {
|
||||
.name = "intel_cpu",
|
||||
.bar = 0,
|
||||
.mapsize = 0xc8258,
|
||||
.ctrl = { 0x48254, 0x0000FFFF, 0 },
|
||||
.enable = { 0x48250, 0xC0000000, 0 },
|
||||
.enable_val = 0xC0000000,
|
||||
.max_reg = { 0xc8254, 0xFFFF0000, 16 },
|
||||
.max = 0xFFFF,
|
||||
};
|
||||
|
||||
static const struct apple_bl_hw apple_bl_nvidia_nv40 = {
|
||||
.name = "nvidia_nv40",
|
||||
.bar = 0,
|
||||
.mapsize = 0x15f4,
|
||||
.ctrl = { 0x15f0, 0x001F0000, 16 },
|
||||
.max = 31,
|
||||
};
|
||||
|
||||
static const struct apple_bl_hw apple_bl_nvidia_nv50 = {
|
||||
.name = "nvidia_nv50",
|
||||
.bar = 0,
|
||||
.mapsize = 0x61c088,
|
||||
.ctrl = { 0x61c084, 0x000007FF, 0 },
|
||||
.enable = { 0x61c084, 0x80000000, 0 },
|
||||
.enable_val = 0x80000000,
|
||||
.max = 0x7FF,
|
||||
};
|
||||
|
||||
static const struct apple_bl_hw apple_bl_amd_r600 = {
|
||||
.name = "amd_r600",
|
||||
.bar = 2,
|
||||
.mapsize = 0x1730,
|
||||
.ctrl = { 0x172c, 0x0000FF00, 8 },
|
||||
.max = 255,
|
||||
};
|
||||
|
||||
/* DCE10 (Tonga/Fiji, e.g. R9 M395/M395X, R9 Nano). */
|
||||
static const struct apple_bl_hw apple_bl_amd_dce10 = {
|
||||
.name = "amd_dce10",
|
||||
.bar = 2,
|
||||
.mapsize = 0x1208c,
|
||||
.ctrl = { 0x12080, 0x0000FFFF, 0 },
|
||||
.enable = { 0x12080, 0x80000000, 0 },
|
||||
.enable_val = 0x80000000,
|
||||
.max_reg = { 0x12088, 0x0000FFFF, 0 },
|
||||
.max = 0xFFFF,
|
||||
};
|
||||
|
||||
struct apple_bl_match {
|
||||
uint16_t vendor;
|
||||
uint16_t dev_lo;
|
||||
uint16_t dev_hi;
|
||||
const struct apple_bl_hw *hw;
|
||||
};
|
||||
|
||||
static const struct apple_bl_match apple_bl_matches[] = {
|
||||
{ 0x8086, 0x0100, 0x013f, &apple_bl_intel_cpu },
|
||||
{ 0x8086, 0x0140, 0x017f, &apple_bl_intel_cpu },
|
||||
{ 0x8086, 0, 0, &apple_bl_intel_pch },
|
||||
{ 0x10de, 0x0040, 0x03ff, &apple_bl_nvidia_nv40 },
|
||||
{ 0x10de, 0, 0, &apple_bl_nvidia_nv50 },
|
||||
{ 0x1002, 0x6920, 0x6939, &apple_bl_amd_dce10 },
|
||||
{ 0x1002, 0, 0, &apple_bl_amd_r600 },
|
||||
{ 0, 0, 0, NULL },
|
||||
};
|
||||
|
||||
/* Classic (PIO) GMUX - pre-Retina Macs. */
|
||||
static uint32_t
|
||||
gmux_pio_read32(uint16_t base, uint8_t port)
|
||||
{
|
||||
return (inl(base + port));
|
||||
}
|
||||
|
||||
/* A 32-bit GMUX write goes out as four byte writes, low byte first. */
|
||||
static void
|
||||
gmux_pio_write32_at(uint16_t addr, uint32_t val)
|
||||
{
|
||||
|
||||
for (u_int i = 0; i < sizeof(val); i++)
|
||||
outb(addr + i, (uint8_t)(val >> (i * NBBY)));
|
||||
}
|
||||
|
||||
static void
|
||||
gmux_pio_write32(uint16_t base, uint8_t port, uint32_t val)
|
||||
{
|
||||
gmux_pio_write32_at(base + port, val);
|
||||
}
|
||||
|
||||
/* Indexed GMUX - Retina Macs (2012-2015). */
|
||||
static int
|
||||
gmux_index_wait_ready(uint16_t base)
|
||||
{
|
||||
uint8_t gwr;
|
||||
|
||||
for (int i = 0; i < GMUX_INDEXED_RETRIES; i++) {
|
||||
gwr = inb(base + GMUX_PORT_WRITE);
|
||||
if ((gwr & GMUX_INDEXED_BUSY) == 0)
|
||||
return (0);
|
||||
inb(base + GMUX_PORT_READ);
|
||||
DELAY(GMUX_POLL_DELAY);
|
||||
}
|
||||
return (ETIMEDOUT);
|
||||
}
|
||||
|
||||
static int
|
||||
gmux_index_wait_complete(uint16_t base)
|
||||
{
|
||||
uint8_t gwr;
|
||||
|
||||
for (int i = 0; i < GMUX_INDEXED_RETRIES; i++) {
|
||||
gwr = inb(base + GMUX_PORT_WRITE);
|
||||
if ((gwr & GMUX_INDEXED_BUSY) != 0) {
|
||||
/* Acknowledge completion. */
|
||||
inb(base + GMUX_PORT_READ);
|
||||
return (0);
|
||||
}
|
||||
DELAY(GMUX_POLL_DELAY);
|
||||
}
|
||||
/* Clear stale status. */
|
||||
inb(base + GMUX_PORT_READ);
|
||||
return (ETIMEDOUT);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
gmux_index_read32(uint16_t base, uint8_t port)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
gmux_index_wait_ready(base);
|
||||
outb(base + GMUX_PORT_READ, port);
|
||||
gmux_index_wait_complete(base);
|
||||
val = inl(base + GMUX_PORT_VALUE);
|
||||
return (val);
|
||||
}
|
||||
|
||||
static void
|
||||
gmux_index_write32(uint16_t base, uint8_t port, uint32_t val)
|
||||
{
|
||||
gmux_pio_write32_at(base + GMUX_PORT_VALUE, val);
|
||||
gmux_index_wait_ready(base);
|
||||
outb(base + GMUX_PORT_WRITE, port);
|
||||
gmux_index_wait_complete(base);
|
||||
}
|
||||
|
||||
/* MMIO GMUX - T2 Macs. */
|
||||
static inline uint8_t
|
||||
gmux_mmio_read8(void *mmio, uint8_t port)
|
||||
{
|
||||
return (*(volatile uint8_t *)((char *)mmio + port));
|
||||
}
|
||||
|
||||
static inline void
|
||||
gmux_mmio_write8(void *mmio, uint8_t port, uint8_t val)
|
||||
{
|
||||
*(volatile uint8_t *)((char *)mmio + port) = val;
|
||||
}
|
||||
|
||||
static int
|
||||
gmux_mmio_wait(void *mmio)
|
||||
{
|
||||
|
||||
for (int i = 0; i < GMUX_MMIO_RETRIES; i++) {
|
||||
if (gmux_mmio_read8(mmio, GMUX_MMIO_COMMAND_SEND) == 0)
|
||||
return (0);
|
||||
DELAY(GMUX_POLL_DELAY);
|
||||
}
|
||||
return (ETIMEDOUT);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
gmux_mmio_read32(void *mmio, uint8_t port)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
gmux_mmio_wait(mmio);
|
||||
gmux_mmio_write8(mmio, GMUX_MMIO_PORT_SELECT, port);
|
||||
gmux_mmio_write8(mmio, GMUX_MMIO_COMMAND_SEND,
|
||||
GMUX_MMIO_READ | sizeof(val));
|
||||
gmux_mmio_wait(mmio);
|
||||
val = be32toh(*(volatile uint32_t *)mmio);
|
||||
return (val);
|
||||
}
|
||||
|
||||
static void
|
||||
gmux_mmio_write32(void *mmio, uint8_t port, uint32_t val)
|
||||
{
|
||||
gmux_mmio_wait(mmio);
|
||||
*(volatile uint32_t *)mmio = htobe32(val);
|
||||
gmux_mmio_write8(mmio, GMUX_MMIO_PORT_SELECT, port);
|
||||
gmux_mmio_write8(mmio, GMUX_MMIO_COMMAND_SEND,
|
||||
GMUX_MMIO_WRITE | sizeof(val));
|
||||
gmux_mmio_wait(mmio);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
gmux_read32(struct acpi_apple_softc *sc, uint8_t port)
|
||||
{
|
||||
if (sc->bl_gmux_type == GMUX_TYPE_MMIO)
|
||||
return (gmux_mmio_read32(sc->bl_gmux_mmio, port));
|
||||
if (sc->bl_gmux_type == GMUX_TYPE_INDEXED)
|
||||
return (gmux_index_read32(sc->bl_gmux_base, port));
|
||||
return (gmux_pio_read32(sc->bl_gmux_base, port));
|
||||
}
|
||||
|
||||
static void
|
||||
gmux_write32(struct acpi_apple_softc *sc, uint8_t port, uint32_t val)
|
||||
{
|
||||
if (sc->bl_gmux_type == GMUX_TYPE_MMIO)
|
||||
gmux_mmio_write32(sc->bl_gmux_mmio, port, val);
|
||||
else if (sc->bl_gmux_type == GMUX_TYPE_INDEXED)
|
||||
gmux_index_write32(sc->bl_gmux_base, port, val);
|
||||
else
|
||||
gmux_pio_write32(sc->bl_gmux_base, port, val);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
apple_bl_gmux_active(struct acpi_apple_softc *sc)
|
||||
{
|
||||
return (sc->bl_gmux_base != 0 || sc->bl_gmux_mmio != NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
apple_bl_gmux_teardown(struct acpi_apple_softc *sc)
|
||||
{
|
||||
if (sc->bl_gmux_mmio != NULL) {
|
||||
pmap_unmapdev(sc->bl_gmux_mmio, GMUX_MIN_IO_LEN);
|
||||
sc->bl_gmux_mmio = NULL;
|
||||
}
|
||||
sc->bl_gmux_base = 0;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
apple_bl_read(struct acpi_apple_softc *sc, uint32_t offset)
|
||||
{
|
||||
return (*(volatile uint32_t *)((char *)sc->bl_mmio + offset));
|
||||
}
|
||||
|
||||
static inline void
|
||||
apple_bl_write(struct acpi_apple_softc *sc, uint32_t offset, uint32_t val)
|
||||
{
|
||||
*(volatile uint32_t *)((char *)sc->bl_mmio + offset) = val;
|
||||
}
|
||||
|
||||
static int
|
||||
apple_bl_hw_get(struct acpi_apple_softc *sc)
|
||||
{
|
||||
if (apple_bl_gmux_active(sc))
|
||||
return (gmux_read32(sc,
|
||||
GMUX_PORT_BRIGHTNESS) & GMUX_BRIGHTNESS_MASK);
|
||||
|
||||
return ((apple_bl_read(sc, sc->bl_hw->ctrl.offset) &
|
||||
sc->bl_hw->ctrl.mask) >> sc->bl_hw->ctrl.shift);
|
||||
}
|
||||
|
||||
/* Resolve max brightness, enable the PWM if needed, cache brightness. */
|
||||
static void
|
||||
apple_bl_mmio_init(struct acpi_apple_softc *sc, const struct apple_bl_hw *hw)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
if (hw->max_reg.offset != 0) {
|
||||
sc->bl_max = (apple_bl_read(sc, hw->max_reg.offset) &
|
||||
hw->max_reg.mask) >> hw->max_reg.shift;
|
||||
if (sc->bl_max == 0)
|
||||
sc->bl_max = hw->max;
|
||||
} else {
|
||||
sc->bl_max = hw->max;
|
||||
}
|
||||
|
||||
if (hw->enable.offset != 0) {
|
||||
val = apple_bl_read(sc, hw->enable.offset);
|
||||
if ((val & hw->enable_val) != hw->enable_val) {
|
||||
val |= hw->enable_val;
|
||||
apple_bl_write(sc, hw->enable.offset, val);
|
||||
}
|
||||
}
|
||||
|
||||
if (sc->bl_max > 0)
|
||||
sc->bl_brightness = apple_bl_hw_get(sc) * 100 / sc->bl_max;
|
||||
}
|
||||
|
||||
static void
|
||||
apple_bl_hw_set(struct acpi_apple_softc *sc, int level)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
if (apple_bl_gmux_active(sc)) {
|
||||
gmux_write32(sc, GMUX_PORT_BRIGHTNESS,
|
||||
level & GMUX_BRIGHTNESS_MASK);
|
||||
return;
|
||||
}
|
||||
|
||||
val = apple_bl_read(sc, sc->bl_hw->ctrl.offset);
|
||||
val = (val & ~sc->bl_hw->ctrl.mask) |
|
||||
((level << sc->bl_hw->ctrl.shift) & sc->bl_hw->ctrl.mask);
|
||||
apple_bl_write(sc, sc->bl_hw->ctrl.offset, val);
|
||||
}
|
||||
|
||||
static const struct apple_bl_hw *
|
||||
apple_bl_find_gpu(device_t *gpu_devp)
|
||||
{
|
||||
const struct apple_bl_match *m;
|
||||
device_t gpu;
|
||||
uint16_t vendor, device;
|
||||
|
||||
gpu = pci_find_class(PCIC_DISPLAY, PCIS_DISPLAY_VGA);
|
||||
if (gpu == NULL)
|
||||
return (NULL);
|
||||
|
||||
vendor = pci_get_vendor(gpu);
|
||||
device = pci_get_device(gpu);
|
||||
|
||||
for (m = apple_bl_matches; m->vendor != 0; m++) {
|
||||
if (vendor != m->vendor)
|
||||
continue;
|
||||
if (m->dev_lo != 0 &&
|
||||
(device < m->dev_lo || device > m->dev_hi))
|
||||
continue;
|
||||
*gpu_devp = gpu;
|
||||
return (m->hw);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void *
|
||||
apple_bl_map_bar(device_t dev, device_t gpu, const struct apple_bl_hw *hw)
|
||||
{
|
||||
uint64_t bar_addr;
|
||||
uint32_t bar_lo, bar_hi;
|
||||
|
||||
bar_lo = pci_read_config(gpu, PCIR_BAR(hw->bar), 4);
|
||||
|
||||
if (PCI_BAR_IO(bar_lo)) {
|
||||
device_printf(dev, "BAR%d is I/O, expected memory\n", hw->bar);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((bar_lo & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64) {
|
||||
bar_hi = pci_read_config(gpu, PCIR_BAR(hw->bar) + 4, 4);
|
||||
bar_addr = ((uint64_t)bar_hi << 32) |
|
||||
(bar_lo & PCIM_BAR_MEM_BASE);
|
||||
} else {
|
||||
bar_addr = bar_lo & PCIM_BAR_MEM_BASE;
|
||||
}
|
||||
|
||||
if (bar_addr == 0) {
|
||||
device_printf(dev, "BAR%d address is zero\n", hw->bar);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (pmap_mapdev(bar_addr, hw->mapsize));
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe for GMUX. Locate APP000B in the ACPI namespace by HID, consume
|
||||
* its _CRS resource, and detect which I/O protocol the hardware speaks.
|
||||
*/
|
||||
#define GMUX_ACPI_HID "APP000B"
|
||||
|
||||
struct apple_bl_gmux_crs {
|
||||
uint16_t io_base;
|
||||
uint32_t io_len;
|
||||
uint64_t mem_base;
|
||||
};
|
||||
|
||||
static ACPI_STATUS
|
||||
apple_bl_gmux_found(ACPI_HANDLE handle, UINT32 level __unused, void *ctx,
|
||||
void **retval __unused)
|
||||
{
|
||||
*(ACPI_HANDLE *)ctx = handle;
|
||||
return (AE_CTRL_TERMINATE);
|
||||
}
|
||||
|
||||
static ACPI_STATUS
|
||||
apple_bl_gmux_crs_cb(ACPI_RESOURCE *res, void *ctx)
|
||||
{
|
||||
struct apple_bl_gmux_crs *crs = ctx;
|
||||
|
||||
switch (res->Type) {
|
||||
case ACPI_RESOURCE_TYPE_IO:
|
||||
crs->io_base = res->Data.Io.Minimum;
|
||||
crs->io_len = res->Data.Io.AddressLength;
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_FIXED_IO:
|
||||
crs->io_base = res->Data.FixedIo.Address;
|
||||
crs->io_len = res->Data.FixedIo.AddressLength;
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_MEMORY32:
|
||||
crs->mem_base = res->Data.Memory32.Minimum;
|
||||
break;
|
||||
case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
|
||||
crs->mem_base = res->Data.FixedMemory32.Address;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return (AE_OK);
|
||||
}
|
||||
|
||||
static bool
|
||||
apple_bl_is_indexed(uint16_t base)
|
||||
{
|
||||
uint16_t val;
|
||||
|
||||
outb(base + GMUX_PORT_INDEXED_MAGIC1, GMUX_INDEXED_UNLOCK1);
|
||||
outb(base + GMUX_PORT_INDEXED_MAGIC2, GMUX_INDEXED_UNLOCK2);
|
||||
outb(base + GMUX_PORT_INDEXED_MAGIC3, GMUX_INDEXED_UNLOCK3);
|
||||
|
||||
val = inb(base + GMUX_PORT_INDEXED_MAGIC1) |
|
||||
((uint16_t)inb(base + GMUX_PORT_INDEXED_MAGIC2) << 8);
|
||||
return (val == GMUX_INDEXED_SIGNATURE);
|
||||
}
|
||||
|
||||
static bool
|
||||
apple_bl_is_mmio(void *mmio)
|
||||
{
|
||||
return (gmux_mmio_read8(mmio, GMUX_MMIO_COMMAND_SEND) !=
|
||||
GMUX_FLOATING);
|
||||
}
|
||||
|
||||
static bool
|
||||
apple_bl_find_gmux(struct acpi_apple_softc *sc)
|
||||
{
|
||||
ACPI_HANDLE gmux_handle;
|
||||
struct apple_bl_gmux_crs crs;
|
||||
uint16_t base;
|
||||
uint8_t ver_major, ver_minor, ver_release;
|
||||
uint32_t version;
|
||||
|
||||
gmux_handle = NULL;
|
||||
AcpiGetDevices(GMUX_ACPI_HID, apple_bl_gmux_found, &gmux_handle, NULL);
|
||||
if (gmux_handle == NULL)
|
||||
return (false);
|
||||
|
||||
bzero(&crs, sizeof(crs));
|
||||
if (ACPI_FAILURE(AcpiWalkResources(gmux_handle, "_CRS",
|
||||
apple_bl_gmux_crs_cb, &crs)))
|
||||
return (false);
|
||||
|
||||
if (crs.io_base != 0 && crs.io_len >= GMUX_MIN_IO_LEN) {
|
||||
base = crs.io_base;
|
||||
|
||||
/* Try direct PIO first (classic/pre-Retina GMUX). */
|
||||
ver_major = inb(base + GMUX_PORT_VERSION_MAJOR);
|
||||
ver_minor = inb(base + GMUX_PORT_VERSION_MINOR);
|
||||
ver_release = inb(base + GMUX_PORT_VERSION_RELEASE);
|
||||
|
||||
if (ver_major != GMUX_FLOATING ||
|
||||
ver_minor != GMUX_FLOATING) {
|
||||
sc->bl_gmux_base = base;
|
||||
sc->bl_gmux_type = GMUX_TYPE_PIO;
|
||||
device_printf(sc->dev,
|
||||
"GMUX v%d.%d.%d [classic] at 0x%x\n",
|
||||
ver_major, ver_minor, ver_release, base);
|
||||
return (true);
|
||||
}
|
||||
|
||||
/* The bus floated high - check for indexed GMUX. */
|
||||
if (apple_bl_is_indexed(base)) {
|
||||
sc->bl_gmux_base = base;
|
||||
sc->bl_gmux_type = GMUX_TYPE_INDEXED;
|
||||
|
||||
version = gmux_index_read32(base,
|
||||
GMUX_PORT_VERSION_MAJOR);
|
||||
ver_major = GMUX_VERSION_MAJOR(version);
|
||||
ver_minor = GMUX_VERSION_MINOR(version);
|
||||
ver_release = GMUX_VERSION_RELEASE(version);
|
||||
|
||||
device_printf(sc->dev,
|
||||
"GMUX v%d.%d.%d [indexed] at 0x%x\n",
|
||||
ver_major, ver_minor, ver_release, base);
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
|
||||
/* No usable I/O resource - try the MMIO protocol (T2 GMUX). */
|
||||
if (crs.mem_base == 0)
|
||||
return (false);
|
||||
|
||||
sc->bl_gmux_mmio = pmap_mapdev(crs.mem_base, GMUX_MIN_IO_LEN);
|
||||
if (sc->bl_gmux_mmio == NULL)
|
||||
return (false);
|
||||
|
||||
if (!apple_bl_is_mmio(sc->bl_gmux_mmio)) {
|
||||
pmap_unmapdev(sc->bl_gmux_mmio, GMUX_MIN_IO_LEN);
|
||||
sc->bl_gmux_mmio = NULL;
|
||||
return (false);
|
||||
}
|
||||
|
||||
sc->bl_gmux_type = GMUX_TYPE_MMIO;
|
||||
|
||||
version = gmux_mmio_read32(sc->bl_gmux_mmio, GMUX_PORT_VERSION_MAJOR);
|
||||
ver_major = GMUX_VERSION_MAJOR(version);
|
||||
ver_minor = GMUX_VERSION_MINOR(version);
|
||||
ver_release = GMUX_VERSION_RELEASE(version);
|
||||
|
||||
device_printf(sc->dev, "GMUX v%d.%d.%d [mmio] at %#jx\n",
|
||||
ver_major, ver_minor, ver_release, (uintmax_t)crs.mem_base);
|
||||
return (true);
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_apple_backlight_attach_gmux(struct acpi_apple_softc *sc)
|
||||
{
|
||||
uint32_t raw_max;
|
||||
|
||||
mtx_init(&sc->bl_mtx, "apple_bl", NULL, MTX_DEF);
|
||||
|
||||
raw_max = gmux_read32(sc, GMUX_PORT_MAX_BRIGHTNESS) &
|
||||
GMUX_BRIGHTNESS_MASK;
|
||||
sc->bl_max = (raw_max > 0) ? raw_max : GMUX_BRIGHTNESS_MASK;
|
||||
|
||||
if (sc->bl_max > 0)
|
||||
sc->bl_brightness = apple_bl_hw_get(sc) * 100 / sc->bl_max;
|
||||
|
||||
sc->bl_cdev = backlight_register("apple_bl", sc->dev);
|
||||
if (sc->bl_cdev == NULL) {
|
||||
device_printf(sc->dev, "cannot register backlight\n");
|
||||
apple_bl_gmux_teardown(sc);
|
||||
mtx_destroy(&sc->bl_mtx);
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
device_printf(sc->dev, "gmux brightness %d/%d\n",
|
||||
apple_bl_hw_get(sc), sc->bl_max);
|
||||
|
||||
ACPI_SERIAL_BEGIN(apple);
|
||||
apple_backlight_sc = sc;
|
||||
ACPI_SERIAL_END(apple);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
apple_bl_attach_gpu(struct acpi_apple_softc *sc, device_t gpu,
|
||||
const struct apple_bl_hw *hw)
|
||||
{
|
||||
sc->bl_hw = hw;
|
||||
|
||||
sc->bl_mmio = apple_bl_map_bar(sc->dev, gpu, hw);
|
||||
if (sc->bl_mmio == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
mtx_init(&sc->bl_mtx, "apple_bl", NULL, MTX_DEF);
|
||||
apple_bl_mmio_init(sc, hw);
|
||||
|
||||
sc->bl_cdev = backlight_register("apple_bl", sc->dev);
|
||||
if (sc->bl_cdev == NULL) {
|
||||
device_printf(sc->dev, "cannot register backlight\n");
|
||||
mtx_destroy(&sc->bl_mtx);
|
||||
pmap_unmapdev(sc->bl_mmio, hw->mapsize);
|
||||
sc->bl_mmio = NULL;
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
device_printf(sc->dev, "%s, brightness %d/%d\n",
|
||||
hw->name, apple_bl_hw_get(sc), sc->bl_max);
|
||||
|
||||
ACPI_SERIAL_BEGIN(apple);
|
||||
apple_backlight_sc = sc;
|
||||
ACPI_SERIAL_END(apple);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_apple_backlight_deferred(void *arg)
|
||||
{
|
||||
struct acpi_apple_softc *sc = arg;
|
||||
device_t gpu;
|
||||
const struct apple_bl_hw *hw;
|
||||
|
||||
hw = apple_bl_find_gpu(&gpu);
|
||||
if (hw == NULL) {
|
||||
device_printf(sc->dev, "no supported GPU found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
(void)apple_bl_attach_gpu(sc, gpu, hw);
|
||||
}
|
||||
|
||||
static int
|
||||
acpi_apple_backlight_attach_mmio(struct acpi_apple_softc *sc)
|
||||
{
|
||||
device_t gpu;
|
||||
const struct apple_bl_hw *hw;
|
||||
|
||||
hw = apple_bl_find_gpu(&gpu);
|
||||
if (hw == NULL) {
|
||||
/*
|
||||
* GPU not yet attached (ACPI bus enumerates before PCI).
|
||||
* Defer the lookup until after all buses finish probing.
|
||||
*/
|
||||
config_intrhook_oneshot(acpi_apple_backlight_deferred, sc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (apple_bl_attach_gpu(sc, gpu, hw));
|
||||
}
|
||||
|
||||
int
|
||||
acpi_apple_backlight_attach(struct acpi_apple_softc *sc)
|
||||
{
|
||||
if (apple_bl_find_gmux(sc))
|
||||
return (acpi_apple_backlight_attach_gmux(sc));
|
||||
|
||||
return (acpi_apple_backlight_attach_mmio(sc));
|
||||
}
|
||||
|
||||
void
|
||||
acpi_apple_backlight_detach(struct acpi_apple_softc *sc)
|
||||
{
|
||||
bool had_gmux;
|
||||
|
||||
ACPI_SERIAL_BEGIN(apple);
|
||||
if (apple_backlight_sc == sc)
|
||||
apple_backlight_sc = NULL;
|
||||
ACPI_SERIAL_END(apple);
|
||||
|
||||
had_gmux = apple_bl_gmux_active(sc);
|
||||
|
||||
if (sc->bl_cdev != NULL)
|
||||
backlight_destroy(sc->bl_cdev);
|
||||
if (sc->bl_mmio != NULL)
|
||||
pmap_unmapdev(sc->bl_mmio, sc->bl_hw->mapsize);
|
||||
apple_bl_gmux_teardown(sc);
|
||||
if (sc->bl_cdev != NULL || sc->bl_mmio != NULL || had_gmux)
|
||||
mtx_destroy(&sc->bl_mtx);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
apple_bl_ready(struct acpi_apple_softc *sc)
|
||||
{
|
||||
return (sc->bl_hw != NULL || apple_bl_gmux_active(sc));
|
||||
}
|
||||
|
||||
static inline int
|
||||
apple_bl_pct_to_hw(struct acpi_apple_softc *sc, int pct)
|
||||
{
|
||||
int level;
|
||||
|
||||
level = pct * sc->bl_max / 100;
|
||||
if (level > (int)sc->bl_max)
|
||||
level = sc->bl_max;
|
||||
return (level);
|
||||
}
|
||||
|
||||
int
|
||||
acpi_apple_bl_update_status(device_t dev, struct backlight_props *props)
|
||||
{
|
||||
struct acpi_apple_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (!apple_bl_ready(sc))
|
||||
return (ENXIO);
|
||||
mtx_lock(&sc->bl_mtx);
|
||||
apple_bl_hw_set(sc, apple_bl_pct_to_hw(sc, props->brightness));
|
||||
sc->bl_brightness = props->brightness;
|
||||
mtx_unlock(&sc->bl_mtx);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
acpi_apple_bl_get_status(device_t dev, struct backlight_props *props)
|
||||
{
|
||||
struct acpi_apple_softc *sc;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
if (!apple_bl_ready(sc))
|
||||
return (ENXIO);
|
||||
props->brightness = sc->bl_brightness;
|
||||
props->nlevels = 0;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
acpi_apple_bl_get_info(device_t dev, struct backlight_info *info)
|
||||
{
|
||||
info->type = BACKLIGHT_TYPE_PANEL;
|
||||
strlcpy(info->name, "Apple Panel Backlight", BACKLIGHTMAXNAMELENGTH);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore backlight to cached brightness. Called from the platform
|
||||
* instance lid-open notify handler. Caller holds no locks.
|
||||
*/
|
||||
void
|
||||
acpi_apple_backlight_restore(void)
|
||||
{
|
||||
struct acpi_apple_softc *sc;
|
||||
|
||||
ACPI_SERIAL_BEGIN(apple);
|
||||
sc = apple_backlight_sc;
|
||||
if (sc == NULL || !apple_bl_ready(sc)) {
|
||||
ACPI_SERIAL_END(apple);
|
||||
return;
|
||||
}
|
||||
mtx_lock(&sc->bl_mtx);
|
||||
apple_bl_hw_set(sc, apple_bl_pct_to_hw(sc, sc->bl_brightness));
|
||||
mtx_unlock(&sc->bl_mtx);
|
||||
ACPI_SERIAL_END(apple);
|
||||
}
|
||||
|
||||
/*
|
||||
* Turn off backlight. Called from the platform instance lid-close
|
||||
* notify handler. Does not change the cached brightness so that
|
||||
* restore() brings it back on lid open.
|
||||
*/
|
||||
void
|
||||
acpi_apple_backlight_off(void)
|
||||
{
|
||||
struct acpi_apple_softc *sc;
|
||||
|
||||
ACPI_SERIAL_BEGIN(apple);
|
||||
sc = apple_backlight_sc;
|
||||
if (sc == NULL || !apple_bl_ready(sc)) {
|
||||
ACPI_SERIAL_END(apple);
|
||||
return;
|
||||
}
|
||||
mtx_lock(&sc->bl_mtx);
|
||||
apple_bl_hw_set(sc, 0);
|
||||
mtx_unlock(&sc->bl_mtx);
|
||||
ACPI_SERIAL_END(apple);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Abdelkader Boudih <seuros@FreeBSD.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
/*
|
||||
* Apple platform ACPI: Wake-on-LAN and lid state.
|
||||
*
|
||||
* Walks the ACPI namespace on shutdown calling _PSW(1) on any device
|
||||
* with ACPI_FLAG_WAKE_ENABLED set, arming EC.EWOL for wake on magic
|
||||
* packet. Also follows acpi_lid(4) to blank and restore the panel
|
||||
* backlight.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include "apple_acpi.h"
|
||||
|
||||
/*
|
||||
* APP9876 and APP000B attach concurrently as ACPI_APPLE_PLATFORM on T2
|
||||
* Macs with a discrete GPU. Refcount the shutdown and lid hooks here so
|
||||
* the second instance's attach/detach doesn't reinstall or rip out the
|
||||
* first instance's hooks.
|
||||
*/
|
||||
static struct mtx acpi_apple_platform_mtx;
|
||||
MTX_SYSINIT(acpi_apple_platform_mtx_init, &acpi_apple_platform_mtx,
|
||||
"apple_platform", MTX_DEF);
|
||||
static int acpi_apple_platform_refs;
|
||||
static eventhandler_tag acpi_apple_shutdown_tag;
|
||||
static eventhandler_tag acpi_apple_lid_tag;
|
||||
|
||||
static ACPI_STATUS
|
||||
acpi_apple_psw_cb(ACPI_HANDLE handle, UINT32 level __unused, void *context,
|
||||
void **retval __unused)
|
||||
{
|
||||
ACPI_HANDLE psw_handle;
|
||||
device_t dev;
|
||||
int enable;
|
||||
|
||||
enable = (int)(uintptr_t)context;
|
||||
|
||||
if (ACPI_FAILURE(AcpiGetHandle(handle, "_PSW", &psw_handle)))
|
||||
return (AE_OK);
|
||||
|
||||
dev = acpi_get_device(handle);
|
||||
if (dev == NULL)
|
||||
return (AE_OK);
|
||||
|
||||
if (enable && (acpi_get_flags(dev) & ACPI_FLAG_WAKE_ENABLED) == 0)
|
||||
return (AE_OK);
|
||||
|
||||
(void)acpi_SetInteger(handle, "_PSW", enable);
|
||||
if (bootverbose)
|
||||
device_printf(dev, "acpi_apple: _PSW(%d) on %s\n",
|
||||
enable, acpi_name(handle));
|
||||
|
||||
return (AE_OK);
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_apple_arm_wol(void *arg __unused, int howto __unused)
|
||||
{
|
||||
AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, acpi_apple_psw_cb, NULL,
|
||||
(void *)(uintptr_t)1, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* The GMUX handshake polls for milliseconds, so keep it out of the
|
||||
* acpi_lid(4) notify path, which runs with ACPI_SERIAL(lid) held.
|
||||
*/
|
||||
static void
|
||||
acpi_apple_backlight_task(void *context)
|
||||
{
|
||||
if (context != NULL)
|
||||
acpi_apple_backlight_restore();
|
||||
else
|
||||
acpi_apple_backlight_off();
|
||||
}
|
||||
|
||||
static void
|
||||
acpi_apple_lid_event(void *arg __unused, int lid_open)
|
||||
{
|
||||
AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_apple_backlight_task,
|
||||
(void *)(uintptr_t)lid_open);
|
||||
}
|
||||
|
||||
int
|
||||
acpi_apple_platform_attach(struct acpi_apple_softc *sc __unused)
|
||||
{
|
||||
mtx_lock(&acpi_apple_platform_mtx);
|
||||
if (acpi_apple_platform_refs++ == 0) {
|
||||
acpi_apple_shutdown_tag = EVENTHANDLER_REGISTER(
|
||||
shutdown_final, acpi_apple_arm_wol, NULL,
|
||||
SHUTDOWN_PRI_LAST);
|
||||
acpi_apple_lid_tag = EVENTHANDLER_REGISTER(acpi_lid_event,
|
||||
acpi_apple_lid_event, NULL, EVENTHANDLER_PRI_ANY);
|
||||
}
|
||||
mtx_unlock(&acpi_apple_platform_mtx);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
acpi_apple_platform_detach(struct acpi_apple_softc *sc __unused)
|
||||
{
|
||||
mtx_lock(&acpi_apple_platform_mtx);
|
||||
KASSERT(acpi_apple_platform_refs > 0,
|
||||
("acpi_apple: platform refcount underflow"));
|
||||
if (acpi_apple_platform_refs > 0 && --acpi_apple_platform_refs == 0) {
|
||||
if (acpi_apple_lid_tag != NULL) {
|
||||
EVENTHANDLER_DEREGISTER(acpi_lid_event,
|
||||
acpi_apple_lid_tag);
|
||||
acpi_apple_lid_tag = NULL;
|
||||
}
|
||||
if (acpi_apple_shutdown_tag != NULL) {
|
||||
EVENTHANDLER_DEREGISTER(shutdown_final,
|
||||
acpi_apple_shutdown_tag);
|
||||
acpi_apple_shutdown_tag = NULL;
|
||||
}
|
||||
}
|
||||
mtx_unlock(&acpi_apple_platform_mtx);
|
||||
}
|
||||
@@ -16,6 +16,11 @@ SUBDIR= \
|
||||
aibs
|
||||
.endif
|
||||
|
||||
.if ${MACHINE_CPUARCH} == "amd64"
|
||||
SUBDIR+= \
|
||||
acpi_apple
|
||||
.endif
|
||||
|
||||
SUBDIR+= \
|
||||
acpi_einj \
|
||||
acpi_ged
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.PATH: ${SRCTOP}/sys/dev/apple_acpi
|
||||
|
||||
KMOD= acpi_apple
|
||||
SRCS= apple_acpi.c apple_platform.c apple_backlight.c \
|
||||
opt_acpi.h acpi_if.h bus_if.h device_if.h \
|
||||
pci_if.h backlight_if.h
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
Reference in new issue
Block a user