Path: blob/main/sys/dev/acpi_support/acpi_panasonic.c
39535 views
/*-1* Copyright (c) 2003 OGAWA Takaya <[email protected]>2* Copyright (c) 2004 TAKAHASHI Yoshihiro <[email protected]>3* All rights Reserved.4*5* Redistribution and use in source and binary forms, with or without6* modification, are permitted provided that the following conditions7* are met:8* 1. Redistributions of source code must retain the above copyright9* notice, this list of conditions and the following disclaimer.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND15* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE16* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE17* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE18* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL19* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS20* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)21* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT22* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY23* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF24* SUCH DAMAGE.25*26*/2728#include <sys/cdefs.h>29#include "opt_acpi.h"30#include <sys/param.h>31#include <sys/bus.h>32#include <sys/eventhandler.h>33#include <sys/kernel.h>34#include <sys/malloc.h>35#include <sys/module.h>36#include <sys/power.h>3738#include <contrib/dev/acpica/include/acpi.h>3940#include <dev/acpica/acpivar.h>4142#define _COMPONENT ACPI_OEM43ACPI_MODULE_NAME("Panasonic")4445/* Debug */46#undef ACPI_PANASONIC_DEBUG4748/* Operations */49#define HKEY_SET 050#define HKEY_GET 15152/* Functions */53#define HKEY_REG_LCD_BRIGHTNESS_MAX_AC 0x0254#define HKEY_REG_LCD_BRIGHTNESS_MIN_AC 0x0355#define HKEY_REG_LCD_BRIGHTNESS_AC 0x0456#define HKEY_REG_LCD_BRIGHTNESS_MAX_DC 0x0557#define HKEY_REG_LCD_BRIGHTNESS_MIN_DC 0x0658#define HKEY_REG_LCD_BRIGHTNESS_DC 0x0759#define HKEY_REG_SOUND_MUTE 0x086061/* Field definitions */62#define HKEY_LCD_BRIGHTNESS_BITS 463#define HKEY_LCD_BRIGHTNESS_DIV ((1 << HKEY_LCD_BRIGHTNESS_BITS) - 1)6465struct acpi_panasonic_softc {66device_t dev;67ACPI_HANDLE handle;6869struct sysctl_ctx_list sysctl_ctx;70struct sysctl_oid *sysctl_tree;7172eventhandler_tag power_evh;73};7475/* Prototype for HKEY functions for getting/setting a value. */76typedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *);7778static int acpi_panasonic_probe(device_t dev);79static int acpi_panasonic_attach(device_t dev);80static int acpi_panasonic_detach(device_t dev);81static int acpi_panasonic_shutdown(device_t dev);82static int acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS);83static UINT64 acpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index);84static void acpi_panasonic_sset(ACPI_HANDLE h, UINT64 index,85UINT64 val);86static int acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc,87ACPI_HANDLE h, UINT32 *arg);88static void acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc,89ACPI_HANDLE h, UINT32 key);90static void acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify,91void *context);92static void acpi_panasonic_power_profile(void *arg);9394static hkey_fn_t hkey_lcd_brightness_max;95static hkey_fn_t hkey_lcd_brightness_min;96static hkey_fn_t hkey_lcd_brightness;97static hkey_fn_t hkey_sound_mute;98ACPI_SERIAL_DECL(panasonic, "ACPI Panasonic extras");99100/* Table of sysctl names and HKEY functions to call. */101static struct {102char *name;103hkey_fn_t *handler;104} sysctl_table[] = {105/* name, handler */106{"lcd_brightness_max", hkey_lcd_brightness_max},107{"lcd_brightness_min", hkey_lcd_brightness_min},108{"lcd_brightness", hkey_lcd_brightness},109{"sound_mute", hkey_sound_mute},110{NULL, NULL}111};112113static device_method_t acpi_panasonic_methods[] = {114DEVMETHOD(device_probe, acpi_panasonic_probe),115DEVMETHOD(device_attach, acpi_panasonic_attach),116DEVMETHOD(device_detach, acpi_panasonic_detach),117DEVMETHOD(device_shutdown, acpi_panasonic_shutdown),118119DEVMETHOD_END120};121122static driver_t acpi_panasonic_driver = {123"acpi_panasonic",124acpi_panasonic_methods,125sizeof(struct acpi_panasonic_softc),126};127128DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver, 0, 0);129MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1);130131static int132acpi_panasonic_probe(device_t dev)133{134static char *mat_ids[] = { "MAT0019", NULL };135int rv;136137if (acpi_disabled("panasonic") ||138device_get_unit(dev) != 0)139return (ENXIO);140rv = ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids, NULL);141142if (rv <= 0)143device_set_desc(dev, "Panasonic Notebook Hotkeys");144return (rv);145}146147static int148acpi_panasonic_attach(device_t dev)149{150struct acpi_panasonic_softc *sc;151struct acpi_softc *acpi_sc;152ACPI_STATUS status;153int i;154155sc = device_get_softc(dev);156sc->dev = dev;157sc->handle = acpi_get_handle(dev);158159acpi_sc = acpi_device_get_parent_softc(dev);160161/* Build sysctl tree */162sysctl_ctx_init(&sc->sysctl_ctx);163sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,164SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,165"panasonic", CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");166for (i = 0; sysctl_table[i].name != NULL; i++) {167SYSCTL_ADD_PROC(&sc->sysctl_ctx,168SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,169sysctl_table[i].name,170CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY |171CTLFLAG_MPSAFE, sc, i, acpi_panasonic_sysctl, "I", "");172}173174#if 0175/* Activate hotkeys */176status = AcpiEvaluateObject(sc->handle, "", NULL, NULL);177if (ACPI_FAILURE(status)) {178device_printf(dev, "enable FN keys failed\n");179sysctl_ctx_free(&sc->sysctl_ctx);180return (ENXIO);181}182#endif183184/* Handle notifies */185status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,186acpi_panasonic_notify, sc);187if (ACPI_FAILURE(status)) {188device_printf(dev, "couldn't install notify handler - %s\n",189AcpiFormatException(status));190sysctl_ctx_free(&sc->sysctl_ctx);191return (ENXIO);192}193194/* Install power profile event handler */195sc->power_evh = EVENTHANDLER_REGISTER(power_profile_change,196acpi_panasonic_power_profile, sc->handle, 0);197198return (0);199}200201static int202acpi_panasonic_detach(device_t dev)203{204struct acpi_panasonic_softc *sc;205206sc = device_get_softc(dev);207208/* Remove power profile event handler */209EVENTHANDLER_DEREGISTER(power_profile_change, sc->power_evh);210211/* Remove notify handler */212AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,213acpi_panasonic_notify);214215/* Free sysctl tree */216sysctl_ctx_free(&sc->sysctl_ctx);217218return (0);219}220221static int222acpi_panasonic_shutdown(device_t dev)223{224struct acpi_panasonic_softc *sc;225int mute;226227/* Mute the main audio during reboot to prevent static burst to speaker. */228sc = device_get_softc(dev);229mute = 1;230hkey_sound_mute(sc->handle, HKEY_SET, &mute);231return (0);232}233234static int235acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS)236{237struct acpi_panasonic_softc *sc;238UINT32 arg;239int function, error;240hkey_fn_t *handler;241242sc = (struct acpi_panasonic_softc *)oidp->oid_arg1;243function = oidp->oid_arg2;244handler = sysctl_table[function].handler;245246/* Get the current value from the appropriate function. */247ACPI_SERIAL_BEGIN(panasonic);248error = handler(sc->handle, HKEY_GET, &arg);249if (error != 0)250goto out;251252/* Send the current value to the user and return if no new value. */253error = sysctl_handle_int(oidp, &arg, 0, req);254if (error != 0 || req->newptr == NULL)255goto out;256257/* Set the new value via the appropriate function. */258error = handler(sc->handle, HKEY_SET, &arg);259260out:261ACPI_SERIAL_END(panasonic);262return (error);263}264265static UINT64266acpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index)267{268ACPI_BUFFER buf;269ACPI_OBJECT *res;270UINT64 ret;271272ACPI_SERIAL_ASSERT(panasonic);273ret = -1;274buf.Length = ACPI_ALLOCATE_BUFFER;275buf.Pointer = NULL;276AcpiEvaluateObject(h, "SINF", NULL, &buf);277res = (ACPI_OBJECT *)buf.Pointer;278if (res->Type == ACPI_TYPE_PACKAGE)279ret = res->Package.Elements[index].Integer.Value;280AcpiOsFree(buf.Pointer);281282return (ret);283}284285static void286acpi_panasonic_sset(ACPI_HANDLE h, UINT64 index, UINT64 val)287{288ACPI_OBJECT_LIST args;289ACPI_OBJECT obj[2];290291ACPI_SERIAL_ASSERT(panasonic);292obj[0].Type = ACPI_TYPE_INTEGER;293obj[0].Integer.Value = index;294obj[1].Type = ACPI_TYPE_INTEGER;295obj[1].Integer.Value = val;296args.Count = 2;297args.Pointer = obj;298AcpiEvaluateObject(h, "SSET", &args, NULL);299}300301static int302hkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val)303{304int reg;305306ACPI_SERIAL_ASSERT(panasonic);307reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?308HKEY_REG_LCD_BRIGHTNESS_MAX_AC : HKEY_REG_LCD_BRIGHTNESS_MAX_DC;309310switch (op) {311case HKEY_SET:312return (EPERM);313break;314case HKEY_GET:315*val = acpi_panasonic_sinf(h, reg);316break;317}318319return (0);320}321322static int323hkey_lcd_brightness_min(ACPI_HANDLE h, int op, UINT32 *val)324{325int reg;326327ACPI_SERIAL_ASSERT(panasonic);328reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?329HKEY_REG_LCD_BRIGHTNESS_MIN_AC : HKEY_REG_LCD_BRIGHTNESS_MIN_DC;330331switch (op) {332case HKEY_SET:333return (EPERM);334break;335case HKEY_GET:336*val = acpi_panasonic_sinf(h, reg);337break;338}339340return (0);341}342343static int344hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val)345{346int reg;347UINT32 max, min;348349reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?350HKEY_REG_LCD_BRIGHTNESS_AC : HKEY_REG_LCD_BRIGHTNESS_DC;351352ACPI_SERIAL_ASSERT(panasonic);353switch (op) {354case HKEY_SET:355hkey_lcd_brightness_max(h, HKEY_GET, &max);356hkey_lcd_brightness_min(h, HKEY_GET, &min);357if (*val < min || *val > max)358return (EINVAL);359acpi_panasonic_sset(h, reg, *val);360break;361case HKEY_GET:362*val = acpi_panasonic_sinf(h, reg);363break;364}365366return (0);367}368369static int370hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val)371{372373ACPI_SERIAL_ASSERT(panasonic);374switch (op) {375case HKEY_SET:376if (*val != 0 && *val != 1)377return (EINVAL);378acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val);379break;380case HKEY_GET:381*val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE);382break;383}384385return (0);386}387388static int389acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,390UINT32 *arg)391{392ACPI_BUFFER buf;393ACPI_OBJECT *res;394UINT64 val;395int status;396397ACPI_SERIAL_ASSERT(panasonic);398status = ENXIO;399400buf.Length = ACPI_ALLOCATE_BUFFER;401buf.Pointer = NULL;402AcpiEvaluateObject(h, "HINF", NULL, &buf);403res = (ACPI_OBJECT *)buf.Pointer;404if (res->Type != ACPI_TYPE_INTEGER) {405device_printf(sc->dev, "HINF returned non-integer\n");406goto end;407}408val = res->Integer.Value;409#ifdef ACPI_PANASONIC_DEBUG410device_printf(sc->dev, "%s button Fn+F%d\n",411(val & 0x80) ? "Pressed" : "Released",412(int)(val & 0x7f));413#endif414if ((val & 0x7f) > 0 && (val & 0x7f) < 11) {415*arg = val;416status = 0;417}418end:419if (buf.Pointer)420AcpiOsFree(buf.Pointer);421422return (status);423}424425static void426acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,427UINT32 key)428{429struct acpi_softc *acpi_sc;430int arg, max, min;431432acpi_sc = acpi_device_get_parent_softc(sc->dev);433434ACPI_SERIAL_ASSERT(panasonic);435switch (key) {436case 1:437/* Decrease LCD brightness. */438hkey_lcd_brightness_max(h, HKEY_GET, &max);439hkey_lcd_brightness_min(h, HKEY_GET, &min);440hkey_lcd_brightness(h, HKEY_GET, &arg);441arg -= max / HKEY_LCD_BRIGHTNESS_DIV;442if (arg < min)443arg = min;444else if (arg > max)445arg = max;446hkey_lcd_brightness(h, HKEY_SET, &arg);447break;448case 2:449/* Increase LCD brightness. */450hkey_lcd_brightness_max(h, HKEY_GET, &max);451hkey_lcd_brightness_min(h, HKEY_GET, &min);452hkey_lcd_brightness(h, HKEY_GET, &arg);453arg += max / HKEY_LCD_BRIGHTNESS_DIV;454if (arg < min)455arg = min;456else if (arg > max)457arg = max;458hkey_lcd_brightness(h, HKEY_SET, &arg);459break;460case 4:461/* Toggle sound mute. */462hkey_sound_mute(h, HKEY_GET, &arg);463if (arg)464arg = 0;465else466arg = 1;467hkey_sound_mute(h, HKEY_SET, &arg);468break;469case 7:470/* Suspend. */471acpi_event_sleep_button_sleep(acpi_sc);472break;473}474}475476static void477acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context)478{479struct acpi_panasonic_softc *sc;480UINT32 key = 0;481482sc = (struct acpi_panasonic_softc *)context;483484switch (notify) {485case 0x80:486ACPI_SERIAL_BEGIN(panasonic);487if (acpi_panasonic_hkey_event(sc, h, &key) == 0) {488acpi_panasonic_hkey_action(sc, h, key);489acpi_UserNotify("Panasonic", h, (uint8_t)key);490}491ACPI_SERIAL_END(panasonic);492break;493case 0x81:494if (!bootverbose)495break;496/* FALLTHROUGH */497default:498device_printf(sc->dev, "unknown notify: %#x\n", notify);499break;500}501}502503static void504acpi_panasonic_power_profile(void *arg)505{506ACPI_HANDLE handle;507UINT32 brightness;508509handle = (ACPI_HANDLE)arg;510511/* Reset current brightness according to new power state. */512ACPI_SERIAL_BEGIN(panasonic);513hkey_lcd_brightness(handle, HKEY_GET, &brightness);514hkey_lcd_brightness(handle, HKEY_SET, &brightness);515ACPI_SERIAL_END(panasonic);516}517518519