diff options
Diffstat (limited to 'sys/dev/acpi_support/acpi_system76.c')
| -rw-r--r-- | sys/dev/acpi_support/acpi_system76.c | 299 |
1 files changed, 266 insertions, 33 deletions
diff --git a/sys/dev/acpi_support/acpi_system76.c b/sys/dev/acpi_support/acpi_system76.c index 916a9a61f471..1ba287ccb85d 100644 --- a/sys/dev/acpi_support/acpi_system76.c +++ b/sys/dev/acpi_support/acpi_system76.c @@ -26,15 +26,21 @@ * SUCH DAMAGE. */ +#include "opt_acpi.h" + #include <sys/param.h> -#include <sys/kernel.h> #include <sys/bus.h> +#include <sys/kernel.h> #include <sys/module.h> +#include <sys/sysctl.h> #include <contrib/dev/acpica/include/acpi.h> +#include <contrib/dev/acpica/include/accommon.h> #include <dev/acpica/acpivar.h> -#include <sys/sysctl.h> +#include <dev/backlight/backlight.h> + +#include "backlight_if.h" #define _COMPONENT ACPI_OEM ACPI_MODULE_NAME("system76") @@ -52,15 +58,22 @@ struct acpi_system76_softc { ACPI_HANDLE handle; struct acpi_ctrl kbb, /* S76_CTRL_KBB */ - kbc; /* S76_CTRL_KBC */ + kbc, /* S76_CTRL_KBC */ + bctl, /* S76_CTRL_BCTL */ + bcth; /* S76_CTRL_BCTH */ struct sysctl_ctx_list sysctl_ctx; struct sysctl_oid *sysctl_tree; + struct cdev *kbb_bkl; + uint8_t backlight_level; }; static int acpi_system76_probe(device_t); static int acpi_system76_attach(device_t); static int acpi_system76_detach(device_t); +static int acpi_system76_suspend(device_t); +static int acpi_system76_resume(device_t); +static int acpi_system76_shutdown(device_t); static void acpi_system76_init(struct acpi_system76_softc *); static struct acpi_ctrl * acpi_system76_ctrl_map(struct acpi_system76_softc *, int); @@ -68,21 +81,33 @@ static int acpi_system76_update(struct acpi_system76_softc *, int, bool); static int acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS); static void acpi_system76_notify_handler(ACPI_HANDLE, uint32_t, void *); static void acpi_system76_check(struct acpi_system76_softc *); +static int acpi_system76_backlight_update_status(device_t dev, + struct backlight_props *props); +static int acpi_system76_backlight_get_status(device_t dev, + struct backlight_props *props); +static int acpi_system76_backlight_get_info(device_t dev, + struct backlight_info *info); /* methods */ -#define S76_CTRL_KBB 1 /* Keyboard Brightness */ -#define S76_CTRL_KBC 2 /* Keyboard Color */ -#define S76_CTRL_MAX 3 +enum { + S76_CTRL_KBB = 1, /* Keyboard Brightness */ + S76_CTRL_KBC = 2, /* Keyboard Color */ + S76_CTRL_BCTL = 3, /* Battery Charging Start Thresholds */ + S76_CTRL_BCTH = 4, /* Battery Charging End Thresholds */ +}; +#define S76_CTRL_MAX 5 struct s76_ctrl_table { char *name; char *get_method; #define S76_CTRL_GKBB "\\_SB.S76D.GKBB" #define S76_CTRL_GKBC "\\_SB.S76D.GKBC" +#define S76_CTRL_GBCT "\\_SB.PCI0.LPCB.EC0.GBCT" char *set_method; #define S76_CTRL_SKBB "\\_SB.S76D.SKBB" #define S76_CTRL_SKBC "\\_SB.S76D.SKBC" +#define S76_CTRL_SBCT "\\_SB.PCI0.LPCB.EC0.SBCT" char *desc; }; @@ -100,12 +125,33 @@ static const struct s76_ctrl_table s76_sysctl_table[] = { .set_method = S76_CTRL_SKBC, .desc = "Keyboard Color", }, + [S76_CTRL_BCTL] = { + .name = "battery_charge_min", + .get_method = S76_CTRL_GBCT, + .set_method = S76_CTRL_SBCT, + .desc = "Start charging the battery when this threshold is reached (percentage)", + }, + [S76_CTRL_BCTH] = { + .name = "battery_charge_max", + .get_method = S76_CTRL_GBCT, + .set_method = S76_CTRL_SBCT, + .desc = "Stop charging the battery when this threshold is reached (percentage)", + }, }; static device_method_t acpi_system76_methods[] = { + /* Device interface */ DEVMETHOD(device_probe, acpi_system76_probe), DEVMETHOD(device_attach, acpi_system76_attach), DEVMETHOD(device_detach, acpi_system76_detach), + DEVMETHOD(device_suspend, acpi_system76_suspend), + DEVMETHOD(device_resume, acpi_system76_resume), + DEVMETHOD(device_shutdown, acpi_system76_shutdown), + + /* Backlight interface */ + DEVMETHOD(backlight_update_status, acpi_system76_backlight_update_status), + DEVMETHOD(backlight_get_status, acpi_system76_backlight_get_status), + DEVMETHOD(backlight_get_info, acpi_system76_backlight_get_info), DEVMETHOD_END }; @@ -123,6 +169,33 @@ static driver_t acpi_system76_driver = { sizeof(struct acpi_system76_softc) }; +static const uint32_t acpi_system76_backlight_levels[] = { + 0, 6, 12, 18, 24, 30, 36, 42, + 48, 54, 60, 66, 72, 78, 84, 100 +}; + +static inline uint32_t +devstate_to_backlight(uint32_t val) +{ + return (acpi_system76_backlight_levels[val >> 4 & 0xf]); +} + +static inline uint32_t +backlight_to_devstate(uint32_t bkl) +{ + int i; + uint32_t val; + + for (i = 0; i < nitems(acpi_system76_backlight_levels); i++) { + if (bkl < acpi_system76_backlight_levels[i]) + break; + } + val = (i - 1) * 16; + if (val > 224) + val = 255; + return (val); +} + /* * Returns corresponding acpi_ctrl of softc from method */ @@ -133,10 +206,12 @@ acpi_system76_ctrl_map(struct acpi_system76_softc *sc, int method) switch (method) { case S76_CTRL_KBB: return (&sc->kbb); - break; case S76_CTRL_KBC: return (&sc->kbc); - break; + case S76_CTRL_BCTL: + return (&sc->bctl); + case S76_CTRL_BCTH: + return (&sc->bcth); default: device_printf(sc->dev, "Driver received unknown method\n"); return (NULL); @@ -148,6 +223,9 @@ acpi_system76_update(struct acpi_system76_softc *sc, int method, bool set) { struct acpi_ctrl *ctrl; ACPI_STATUS status; + ACPI_BUFFER Buf; + ACPI_OBJECT Arg[2], Obj; + ACPI_OBJECT_LIST Args; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); ACPI_SERIAL_ASSERT(system76); @@ -155,12 +233,41 @@ acpi_system76_update(struct acpi_system76_softc *sc, int method, bool set) if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL) return (EINVAL); - if (set) - status = acpi_SetInteger(sc->handle, s76_sysctl_table[method].set_method, - ctrl->val); - else - status = acpi_GetInteger(sc->handle, s76_sysctl_table[method].get_method, - &ctrl->val); + switch (method) { + case S76_CTRL_BCTL: + case S76_CTRL_BCTH: + Arg[0].Type = ACPI_TYPE_INTEGER; + Arg[0].Integer.Value = method == S76_CTRL_BCTH ? 1 : 0; + Args.Count = set ? 2 : 1; + Args.Pointer = Arg; + Buf.Length = sizeof(Obj); + Buf.Pointer = &Obj; + + if (set) { + Arg[1].Type = ACPI_TYPE_INTEGER; + Arg[1].Integer.Value = ctrl->val; + + status = AcpiEvaluateObject(sc->handle, + s76_sysctl_table[method].set_method, &Args, &Buf); + } else { + status = AcpiEvaluateObject(sc->handle, + s76_sysctl_table[method].get_method, &Args, &Buf); + if (ACPI_SUCCESS(status) && + Obj.Type == ACPI_TYPE_INTEGER) + ctrl->val = Obj.Integer.Value; + } + break; + case S76_CTRL_KBB: + case S76_CTRL_KBC: + if (set) + status = acpi_SetInteger(sc->handle, s76_sysctl_table[method].set_method, + ctrl->val); + else + status = acpi_GetInteger(sc->handle, s76_sysctl_table[method].get_method, + &ctrl->val); + break; + } + if (ACPI_FAILURE(status)) { device_printf(sc->dev, "Couldn't query method (%s)\n", s76_sysctl_table[method].name); @@ -181,9 +288,16 @@ acpi_system76_notify_update(void *arg) sc = (struct acpi_system76_softc *)device_get_softc(arg); ACPI_SERIAL_BEGIN(system76); - for (method = 1; method < S76_CTRL_MAX; method++) + for (method = 1; method < S76_CTRL_MAX; method++) { + if (method == S76_CTRL_BCTL || + method == S76_CTRL_BCTH) + continue; acpi_system76_update(sc, method, false); + } ACPI_SERIAL_END(system76); + + if (sc->kbb_bkl != NULL) + sc->backlight_level = devstate_to_backlight(sc->kbb.val); } static void @@ -199,6 +313,14 @@ acpi_system76_check(struct acpi_system76_softc *sc) if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL) continue; + /* available in all models */ + if (method == S76_CTRL_BCTL || + method == S76_CTRL_BCTH) { + ctrl->exists = true; + acpi_system76_update(sc, method, false); + continue; + } + if (ACPI_FAILURE(acpi_GetInteger(sc->handle, s76_sysctl_table[method].get_method, &ctrl->val))) { ctrl->exists = false; @@ -234,9 +356,10 @@ acpi_system76_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *ctx) static int acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS) { - struct acpi_ctrl *ctrl; + struct acpi_ctrl *ctrl, *ctrl_cmp; struct acpi_system76_softc *sc; int val, method, error; + bool update; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); @@ -251,27 +374,47 @@ acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS) device_printf(sc->dev, "Driver query failed\n"); return (error); } - if (req->newptr == NULL) - return (error); - /* Input validation */ - switch (method) { - case S76_CTRL_KBB: - if (val > UINT8_MAX || val < 0) - return (EINVAL); - break; - case S76_CTRL_KBC: - if (val >= (1 << 24) || val < 0) - return (EINVAL); - break; - default: - break; + if (req->newptr == NULL) { + /* + * ACPI will not notify us if battery thresholds changes + * outside this module. Therefore, always fetch those values. + */ + if (method != S76_CTRL_BCTL && method != S76_CTRL_BCTH) + return (error); + update = false; + } else { + /* Input validation */ + switch (method) { + case S76_CTRL_KBB: + if (val > UINT8_MAX || val < 0) + return (EINVAL); + if (sc->kbb_bkl != NULL) + sc->backlight_level = devstate_to_backlight(val); + break; + case S76_CTRL_KBC: + if (val >= (1 << 24) || val < 0) + return (EINVAL); + break; + case S76_CTRL_BCTL: + if ((ctrl_cmp = acpi_system76_ctrl_map(sc, S76_CTRL_BCTH)) == NULL) + return (EINVAL); + if (val > 100 || val < 0 || val >= ctrl_cmp->val) + return (EINVAL); + break; + case S76_CTRL_BCTH: + if ((ctrl_cmp = acpi_system76_ctrl_map(sc, S76_CTRL_BCTL)) == NULL) + return (EINVAL); + if (val > 100 || val < 0 || val <= ctrl_cmp->val) + return (EINVAL); + break; + } + ctrl->val = val; + update = true; } - ctrl->val = val; - ACPI_SERIAL_BEGIN(system76); - error = acpi_system76_update(sc, method, true); + error = acpi_system76_update(sc, method, update); ACPI_SERIAL_END(system76); return (error); } @@ -299,6 +442,14 @@ acpi_system76_init(struct acpi_system76_softc *sc) if (!ctrl->exists) continue; + if (method == S76_CTRL_KBB) { + sc->kbb_bkl = backlight_register("system76_keyboard", sc->dev); + if (sc->kbb_bkl == NULL) + device_printf(sc->dev, "Can not register backlight\n"); + else + sc->backlight_level = devstate_to_backlight(sc->kbb.val); + } + SYSCTL_ADD_PROC(&sc->sysctl_ctx, SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, s76_sysctl_table[method].name, CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_ANYBODY | CTLFLAG_MPSAFE, @@ -307,6 +458,45 @@ acpi_system76_init(struct acpi_system76_softc *sc) } static int +acpi_system76_backlight_update_status(device_t dev, struct backlight_props + *props) +{ + struct acpi_system76_softc *sc; + + sc = device_get_softc(dev); + if (sc->kbb.val != backlight_to_devstate(props->brightness)) { + sc->kbb.val = backlight_to_devstate(props->brightness); + acpi_system76_update(sc, S76_CTRL_KBB, true); + } + sc->backlight_level = props->brightness; + + return (0); +} + +static int +acpi_system76_backlight_get_status(device_t dev, struct backlight_props *props) +{ + struct acpi_system76_softc *sc; + + sc = device_get_softc(dev); + props->brightness = sc->backlight_level; + props->nlevels = nitems(acpi_system76_backlight_levels); + memcpy(props->levels, acpi_system76_backlight_levels, + sizeof(acpi_system76_backlight_levels)); + + return (0); +} + +static int +acpi_system76_backlight_get_info(device_t dev, struct backlight_info *info) +{ + info->type = BACKLIGHT_TYPE_KEYBOARD; + strlcpy(info->name, "System76 Keyboard", BACKLIGHTMAXNAMELENGTH); + + return (0); +} + +static int acpi_system76_attach(device_t dev) { struct acpi_system76_softc *sc; @@ -336,10 +526,52 @@ acpi_system76_detach(device_t dev) if (sysctl_ctx_free(&sc->sysctl_ctx) != 0) return (EBUSY); + AcpiRemoveNotifyHandler(sc->handle, ACPI_SYSTEM_NOTIFY, + acpi_system76_notify_handler); + + if (sc->kbb_bkl != NULL) + backlight_destroy(sc->kbb_bkl); + return (0); } static int +acpi_system76_suspend(device_t dev) +{ + struct acpi_system76_softc *sc; + struct acpi_ctrl *ctrl; + + sc = device_get_softc(dev); + if ((ctrl = acpi_system76_ctrl_map(sc, S76_CTRL_KBB)) != NULL) { + ctrl->val = 0; + acpi_system76_update(sc, S76_CTRL_KBB, true); + } + + return (0); +} + +static int +acpi_system76_resume(device_t dev) +{ + struct acpi_system76_softc *sc; + struct acpi_ctrl *ctrl; + + sc = device_get_softc(dev); + if ((ctrl = acpi_system76_ctrl_map(sc, S76_CTRL_KBB)) != NULL) { + ctrl->val = backlight_to_devstate(sc->backlight_level); + acpi_system76_update(sc, S76_CTRL_KBB, true); + } + + return (0); +} + +static int +acpi_system76_shutdown(device_t dev) +{ + return (acpi_system76_detach(dev)); +} + +static int acpi_system76_probe(device_t dev) { int rv; @@ -357,3 +589,4 @@ acpi_system76_probe(device_t dev) DRIVER_MODULE(acpi_system76, acpi, acpi_system76_driver, 0, 0); MODULE_VERSION(acpi_system76, 1); MODULE_DEPEND(acpi_system76, acpi, 1, 1, 1); +MODULE_DEPEND(acpi_system76, backlight, 1, 1, 1); |
