1*3e37c391Sclaudio /* $OpenBSD: acpithinkpad.c,v 1.74 2023/07/07 07:37:59 claudio Exp $ */
2866be0c9Sjcs /*
3866be0c9Sjcs * Copyright (c) 2008 joshua stein <jcs@openbsd.org>
4866be0c9Sjcs *
5866be0c9Sjcs * Permission to use, copy, modify, and distribute this software for any
6866be0c9Sjcs * purpose with or without fee is hereby granted, provided that the above
7866be0c9Sjcs * copyright notice and this permission notice appear in all copies.
8866be0c9Sjcs *
9866be0c9Sjcs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10866be0c9Sjcs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11866be0c9Sjcs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12866be0c9Sjcs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13866be0c9Sjcs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14866be0c9Sjcs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15866be0c9Sjcs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16866be0c9Sjcs */
17866be0c9Sjcs
18866be0c9Sjcs #include <sys/param.h>
19866be0c9Sjcs #include <sys/systm.h>
20ec849c8aSjsg #include <sys/sensors.h>
21866be0c9Sjcs
22866be0c9Sjcs #include <dev/acpi/acpireg.h>
23866be0c9Sjcs #include <dev/acpi/acpivar.h>
24866be0c9Sjcs #include <dev/acpi/acpidev.h>
25866be0c9Sjcs #include <dev/acpi/amltypes.h>
26866be0c9Sjcs #include <dev/acpi/dsdt.h>
2723b692d1Skettenis #include <dev/wscons/wsconsio.h>
28d95a16b0Skettenis #include <dev/wscons/wsdisplayvar.h>
29866be0c9Sjcs
3030cdf572Sderaadt #include <machine/apmvar.h>
3130cdf572Sderaadt
3292a3e4ccSderaadt #include "audio.h"
3392a3e4ccSderaadt #include "wskbd.h"
3492a3e4ccSderaadt
35b96dcde9Sjcs /* #define ACPITHINKPAD_DEBUG */
36b96dcde9Sjcs
37b96dcde9Sjcs #ifdef ACPITHINKPAD_DEBUG
38b96dcde9Sjcs #define DPRINTF(x) printf x
39b96dcde9Sjcs #else
40b96dcde9Sjcs #define DPRINTF(x)
41b96dcde9Sjcs #endif
42b96dcde9Sjcs
438107bf4dSjcs #define THINKPAD_HKEY_VERSION1 0x0100
448107bf4dSjcs #define THINKPAD_HKEY_VERSION2 0x0200
45866be0c9Sjcs
463e7c1b35Sjcs #define THINKPAD_CMOS_VOLUME_DOWN 0x00
473e7c1b35Sjcs #define THINKPAD_CMOS_VOLUME_UP 0x01
483e7c1b35Sjcs #define THINKPAD_CMOS_VOLUME_MUTE 0x02
49866be0c9Sjcs #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04
50866be0c9Sjcs #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05
51866be0c9Sjcs
52866be0c9Sjcs #define THINKPAD_BLUETOOTH_PRESENT 0x01
53866be0c9Sjcs #define THINKPAD_BLUETOOTH_ENABLED 0x02
54866be0c9Sjcs
55866be0c9Sjcs /* wan (not wifi) card */
56866be0c9Sjcs #define THINKPAD_WAN_PRESENT 0x01
57866be0c9Sjcs #define THINKPAD_WAN_ENABLED 0x02
58866be0c9Sjcs
590b3884edSfgsch #define THINKPAD_BUTTON_FN_F1 0x1001
600b3884edSfgsch #define THINKPAD_BUTTON_LOCK_SCREEN 0x1002
610b3884edSfgsch #define THINKPAD_BUTTON_BATTERY_INFO 0x1003
620b3884edSfgsch #define THINKPAD_BUTTON_SUSPEND 0x1004
630b3884edSfgsch #define THINKPAD_BUTTON_WIRELESS 0x1005
640b3884edSfgsch #define THINKPAD_BUTTON_FN_F6 0x1006
650b3884edSfgsch #define THINKPAD_BUTTON_EXTERNAL_SCREEN 0x1007
660b3884edSfgsch #define THINKPAD_BUTTON_POINTER_SWITCH 0x1008
670b3884edSfgsch #define THINKPAD_BUTTON_EJECT 0x1009
6825556f88Skettenis #define THINKPAD_BUTTON_FN_F11 0x100b
6925556f88Skettenis #define THINKPAD_BUTTON_HIBERNATE 0x100c
700b3884edSfgsch #define THINKPAD_BUTTON_BRIGHTNESS_UP 0x1010
710b3884edSfgsch #define THINKPAD_BUTTON_BRIGHTNESS_DOWN 0x1011
720b3884edSfgsch #define THINKPAD_BUTTON_THINKLIGHT 0x1012
730b3884edSfgsch #define THINKPAD_BUTTON_FN_SPACE 0x1014
740b3884edSfgsch #define THINKPAD_BUTTON_VOLUME_UP 0x1015
750b3884edSfgsch #define THINKPAD_BUTTON_VOLUME_DOWN 0x1016
760b3884edSfgsch #define THINKPAD_BUTTON_VOLUME_MUTE 0x1017
770b3884edSfgsch #define THINKPAD_BUTTON_THINKVANTAGE 0x1018
78571d6818Smiod #define THINKPAD_BUTTON_BLACK 0x101a
7992a3e4ccSderaadt #define THINKPAD_BUTTON_MICROPHONE_MUTE 0x101b
8026f5b670Sderaadt #define THINKPAD_KEYLIGHT_CHANGED 0x101c
8126f5b670Sderaadt #define THINKPAD_BUTTON_CONFIG 0x101d
82b7f7f96eSphessler #define THINKPAD_BUTTON_FIND 0x101e
83b7f7f96eSphessler #define THINKPAD_BUTTON_ALL_ACTIVEPROGS 0x101f
84b7f7f96eSphessler #define THINKPAD_BUTTON_ALL_PROGS 0x1020
85b7f7f96eSphessler
868107bf4dSjcs #define THINKPAD_ADAPTIVE_NEXT 0x1101
878107bf4dSjcs #define THINKPAD_ADAPTIVE_QUICK 0x1102
888107bf4dSjcs #define THINKPAD_ADAPTIVE_SNIP 0x1105
898107bf4dSjcs #define THINKPAD_ADAPTIVE_VOICE 0x1108
908107bf4dSjcs #define THINKPAD_ADAPTIVE_GESTURES 0x110a
918107bf4dSjcs #define THINKPAD_ADAPTIVE_SETTINGS 0x110e
928107bf4dSjcs #define THINKPAD_ADAPTIVE_TAB 0x110f
938107bf4dSjcs #define THINKPAD_ADAPTIVE_REFRESH 0x1110
948107bf4dSjcs #define THINKPAD_ADAPTIVE_BACK 0x1111
95abb81af2Smlarkin #define THINKPAD_PORT_REPL_DOCKED 0x4010
96abb81af2Smlarkin #define THINKPAD_PORT_REPL_UNDOCKED 0x4011
9702830852Sstsp #define THINKPAD_TABLET_DOCKED 0x4012
9802830852Sstsp #define THINKPAD_TABLET_UNDOCKED 0x4013
990b3884edSfgsch #define THINKPAD_LID_OPEN 0x5001
1000b3884edSfgsch #define THINKPAD_LID_CLOSED 0x5002
1010b3884edSfgsch #define THINKPAD_TABLET_SCREEN_NORMAL 0x500a
1020b3884edSfgsch #define THINKPAD_TABLET_SCREEN_ROTATED 0x5009
1030b3884edSfgsch #define THINKPAD_BRIGHTNESS_CHANGED 0x5010
1040b3884edSfgsch #define THINKPAD_TABLET_PEN_INSERTED 0x500b
1050b3884edSfgsch #define THINKPAD_TABLET_PEN_REMOVED 0x500c
106f91475cfSderaadt #define THINKPAD_SWITCH_NUMLOCK 0x6000
1071862e1cbSguenther #define THINKPAD_BUTTON_ROTATION_LOCK 0x6020
10899f2e9f6Sjcs #define THINKPAD_THERMAL_TABLE_CHANGED 0x6030
10999f2e9f6Sjcs #define THINKPAD_POWER_CHANGED 0x6040
1107fcffd8bSkettenis #define THINKPAD_BACKLIGHT_CHANGED 0x6050
111f84de5a3Sderaadt #define THINKPAD_BUTTON_FN_TOGGLE 0x6060
11226f5b670Sderaadt #define THINKPAD_TABLET_SCREEN_CHANGED 0x60c0
1130b3884edSfgsch #define THINKPAD_SWITCH_WIRELESS 0x7000
1143e7c1b35Sjcs
115d91d9c7cSjcs #define THINKPAD_SENSOR_FANRPM 0
116d91d9c7cSjcs #define THINKPAD_SENSOR_PORTREPL 1
117d91d9c7cSjcs #define THINKPAD_SENSOR_TMP0 2
118306c5139Smlarkin #define THINKPAD_NSENSORS 10
119306c5139Smlarkin
120f07ad920Sderaadt #define THINKPAD_ECOFFSET_VOLUME 0x30
121f07ad920Sderaadt #define THINKPAD_ECOFFSET_VOLUME_MUTE_MASK 0x40
1221c450453Sjordan #define THINKPAD_ECOFFSET_FANLO 0x84
1231c450453Sjordan #define THINKPAD_ECOFFSET_FANHI 0x85
1241c450453Sjordan
1258107bf4dSjcs #define THINKPAD_ADAPTIVE_MODE_HOME 1
1268107bf4dSjcs #define THINKPAD_ADAPTIVE_MODE_FUNCTION 3
1278107bf4dSjcs
128171b130bSjcs #define THINKPAD_MASK_MIC_MUTE (1 << 14)
1290d574372Sjcs #define THINKPAD_MASK_BRIGHTNESS_UP (1 << 15)
1300d574372Sjcs #define THINKPAD_MASK_BRIGHTNESS_DOWN (1 << 16)
1310d574372Sjcs #define THINKPAD_MASK_KBD_BACKLIGHT (1 << 17)
1320d574372Sjcs
133a81f321cSkettenis #define THINKPAD_BATTERY_ERROR 0x80000000
134a81f321cSkettenis #define THINKPAD_BATTERY_SUPPORT 0x00000100
135a81f321cSkettenis #define THINKPAD_BATTERY_SUPPORT_BICG 0x00000020
136a81f321cSkettenis #define THINKPAD_BATTERY_SHIFT 8
137a81f321cSkettenis
138866be0c9Sjcs struct acpithinkpad_softc {
139866be0c9Sjcs struct device sc_dev;
140866be0c9Sjcs
1411c450453Sjordan struct acpiec_softc *sc_ec;
142866be0c9Sjcs struct acpi_softc *sc_acpi;
143866be0c9Sjcs struct aml_node *sc_devnode;
1441c450453Sjordan
1451c450453Sjordan struct ksensor sc_sens[THINKPAD_NSENSORS];
1461c450453Sjordan struct ksensordev sc_sensdev;
147d91d9c7cSjcs int sc_ntempsens;
14823b692d1Skettenis
1490d574372Sjcs uint64_t sc_hkey_version;
1500d574372Sjcs
15123b692d1Skettenis uint64_t sc_thinklight;
152c1535f53Skettenis const char *sc_thinklight_get;
153c1535f53Skettenis const char *sc_thinklight_set;
1544ea90f4eSkettenis
1554ea90f4eSkettenis uint64_t sc_brightness;
156866be0c9Sjcs };
157866be0c9Sjcs
158d2eaebe9Skettenis extern void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
1591c450453Sjordan
160866be0c9Sjcs int thinkpad_match(struct device *, void *, void *);
161866be0c9Sjcs void thinkpad_attach(struct device *, struct device *, void *);
162866be0c9Sjcs int thinkpad_hotkey(struct aml_node *, int, void *);
163866be0c9Sjcs int thinkpad_enable_events(struct acpithinkpad_softc *);
164866be0c9Sjcs int thinkpad_toggle_bluetooth(struct acpithinkpad_softc *);
165866be0c9Sjcs int thinkpad_toggle_wan(struct acpithinkpad_softc *);
166866be0c9Sjcs int thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t);
1673e7c1b35Sjcs int thinkpad_volume_down(struct acpithinkpad_softc *);
1683e7c1b35Sjcs int thinkpad_volume_up(struct acpithinkpad_softc *);
1693e7c1b35Sjcs int thinkpad_volume_mute(struct acpithinkpad_softc *);
170c2b1253dSjcs int thinkpad_brightness_up(struct acpithinkpad_softc *);
171e7f08eacSjsg int thinkpad_brightness_down(struct acpithinkpad_softc *);
1728107bf4dSjcs int thinkpad_adaptive_change(struct acpithinkpad_softc *);
1738107bf4dSjcs int thinkpad_activate(struct device *, int);
174866be0c9Sjcs
1754ea90f4eSkettenis /* wscons hook functions */
17623b692d1Skettenis void thinkpad_get_thinklight(struct acpithinkpad_softc *);
17723b692d1Skettenis void thinkpad_set_thinklight(void *, int);
1780dc71f27Sjcs int thinkpad_get_kbd_backlight(struct wskbd_backlight *);
1790dc71f27Sjcs int thinkpad_set_kbd_backlight(struct wskbd_backlight *);
18023b692d1Skettenis extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
18123b692d1Skettenis extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
182171b130bSjcs int thinkpad_get_brightness(struct acpithinkpad_softc *);
183171b130bSjcs int thinkpad_set_brightness(void *, int);
1844ea90f4eSkettenis int thinkpad_get_param(struct wsdisplay_param *);
1854ea90f4eSkettenis int thinkpad_set_param(struct wsdisplay_param *);
186d91d9c7cSjcs int thinkpad_get_temp(struct acpithinkpad_softc *, int, int64_t *);
18723b692d1Skettenis
1881c450453Sjordan void thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
1891c450453Sjordan void thinkpad_sensor_refresh(void *);
1901c450453Sjordan
19192a3e4ccSderaadt #if NAUDIO > 0 && NWSKBD > 0
192f07ad920Sderaadt void thinkpad_attach_deferred(void *);
193f07ad920Sderaadt int thinkpad_get_volume_mute(struct acpithinkpad_softc *);
194f07ad920Sderaadt extern int wskbd_set_mixermute(long, long);
195012023bfSmpi extern int wskbd_set_mixervolume(long, long);
19692a3e4ccSderaadt #endif
19792a3e4ccSderaadt
198a81f321cSkettenis int thinkpad_battery_setchargemode(int);
199a81f321cSkettenis int thinkpad_battery_setchargestart(int);
200a81f321cSkettenis int thinkpad_battery_setchargestop(int);
201a81f321cSkettenis
202a81f321cSkettenis extern int (*hw_battery_setchargemode)(int);
203a81f321cSkettenis extern int (*hw_battery_setchargestart)(int);
204a81f321cSkettenis extern int (*hw_battery_setchargestop)(int);
205a81f321cSkettenis extern int hw_battery_chargemode;
206a81f321cSkettenis extern int hw_battery_chargestart;
207a81f321cSkettenis extern int hw_battery_chargestop;
208a81f321cSkettenis
209471aeecfSnaddy const struct cfattach acpithinkpad_ca = {
2108107bf4dSjcs sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach,
2118107bf4dSjcs NULL, thinkpad_activate
212866be0c9Sjcs };
213866be0c9Sjcs
214866be0c9Sjcs struct cfdriver acpithinkpad_cd = {
215866be0c9Sjcs NULL, "acpithinkpad", DV_DULL
216866be0c9Sjcs };
217866be0c9Sjcs
2184adc7ca3Sjcs const char *acpithinkpad_hids[] = {
2196bf9874fSjcs "IBM0068",
2206bf9874fSjcs "LEN0068",
2216bf9874fSjcs "LEN0268",
222128e94b2Smlarkin NULL
2234adc7ca3Sjcs };
2247df90d51Smarco
225866be0c9Sjcs int
thinkpad_match(struct device * parent,void * match,void * aux)226866be0c9Sjcs thinkpad_match(struct device *parent, void *match, void *aux)
227866be0c9Sjcs {
228866be0c9Sjcs struct acpi_attach_args *aa = aux;
229866be0c9Sjcs struct cfdata *cf = match;
230a95d1b72Sjordan int64_t res;
231866be0c9Sjcs
2327df90d51Smarco if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name))
23339d6c70eSderaadt return (0);
234866be0c9Sjcs
235a95d1b72Sjordan if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node,
236866be0c9Sjcs "MHKV", 0, NULL, &res))
23739d6c70eSderaadt return (0);
238866be0c9Sjcs
2398107bf4dSjcs if (!(res == THINKPAD_HKEY_VERSION1 || res == THINKPAD_HKEY_VERSION2))
2400b3884edSfgsch return (0);
241866be0c9Sjcs
2420b3884edSfgsch return (1);
243866be0c9Sjcs }
244866be0c9Sjcs
245866be0c9Sjcs void
thinkpad_sensor_attach(struct acpithinkpad_softc * sc)2461c450453Sjordan thinkpad_sensor_attach(struct acpithinkpad_softc *sc)
2471c450453Sjordan {
248d91d9c7cSjcs int64_t tmp;
2491c450453Sjordan int i;
2501c450453Sjordan
2511c450453Sjordan if (sc->sc_acpi->sc_ec == NULL)
2521c450453Sjordan return;
2531c450453Sjordan sc->sc_ec = sc->sc_acpi->sc_ec;
2541c450453Sjordan
2551c450453Sjordan /* Add temperature probes */
2561c450453Sjordan strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
2571c450453Sjordan sizeof(sc->sc_sensdev.xname));
258d91d9c7cSjcs sc->sc_ntempsens = 0;
259d91d9c7cSjcs for (i = 0; i < THINKPAD_NSENSORS - THINKPAD_SENSOR_TMP0; i++) {
260d91d9c7cSjcs if (thinkpad_get_temp(sc, i, &tmp) != 0)
261d91d9c7cSjcs break;
262d91d9c7cSjcs
263d91d9c7cSjcs sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].type = SENSOR_TEMP;
264d91d9c7cSjcs sensor_attach(&sc->sc_sensdev,
265d91d9c7cSjcs &sc->sc_sens[THINKPAD_SENSOR_TMP0 + i]);
266d91d9c7cSjcs sc->sc_ntempsens++;
2671c450453Sjordan }
2681c450453Sjordan
2691c450453Sjordan /* Add fan probe */
270306c5139Smlarkin sc->sc_sens[THINKPAD_SENSOR_FANRPM].type = SENSOR_FANRPM;
271306c5139Smlarkin sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_FANRPM]);
272306c5139Smlarkin
273306c5139Smlarkin /* Add port replicator indicator */
274306c5139Smlarkin sc->sc_sens[THINKPAD_SENSOR_PORTREPL].type = SENSOR_INDICATOR;
275306c5139Smlarkin sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status = SENSOR_S_UNKNOWN;
276306c5139Smlarkin strlcpy(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc, "port replicator",
277306c5139Smlarkin sizeof(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc));
278306c5139Smlarkin sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_PORTREPL]);
2791c450453Sjordan
2801c450453Sjordan sensordev_install(&sc->sc_sensdev);
2811c450453Sjordan }
2821c450453Sjordan
2831c450453Sjordan void
thinkpad_sensor_refresh(void * arg)2841c450453Sjordan thinkpad_sensor_refresh(void *arg)
2851c450453Sjordan {
2861c450453Sjordan struct acpithinkpad_softc *sc = arg;
287d2eaebe9Skettenis uint8_t lo, hi, i;
2881c450453Sjordan int64_t tmp;
2891c450453Sjordan
2901c450453Sjordan /* Refresh sensor readings */
291d91d9c7cSjcs for (i = 0; i < sc->sc_ntempsens; i++) {
292d91d9c7cSjcs if (thinkpad_get_temp(sc, i, &tmp) != 0) {
2931c450453Sjordan sc->sc_sens[i].flags = SENSOR_FINVALID;
294d91d9c7cSjcs continue;
295d91d9c7cSjcs }
296d91d9c7cSjcs
297d91d9c7cSjcs sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].value =
298d91d9c7cSjcs (tmp * 1000000) + 273150000;
299d91d9c7cSjcs sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].flags =
300d91d9c7cSjcs (tmp > 127 || tmp < -127) ? SENSOR_FINVALID : 0;
3011c450453Sjordan }
3021c450453Sjordan
3031c450453Sjordan /* Read fan RPM */
3041c450453Sjordan acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo);
3051c450453Sjordan acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi);
306995549b7Smiod if (hi == 0xff && lo == 0xff) {
307995549b7Smiod sc->sc_sens[THINKPAD_SENSOR_FANRPM].flags = SENSOR_FINVALID;
308995549b7Smiod } else {
309306c5139Smlarkin sc->sc_sens[THINKPAD_SENSOR_FANRPM].value = ((hi << 8L) + lo);
310995549b7Smiod sc->sc_sens[THINKPAD_SENSOR_FANRPM].flags = 0;
311995549b7Smiod }
3121c450453Sjordan }
3131c450453Sjordan
3141c450453Sjordan void
thinkpad_attach(struct device * parent,struct device * self,void * aux)315866be0c9Sjcs thinkpad_attach(struct device *parent, struct device *self, void *aux)
316866be0c9Sjcs {
317866be0c9Sjcs struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
318866be0c9Sjcs struct acpi_attach_args *aa = aux;
319a81f321cSkettenis struct aml_value arg;
320a81f321cSkettenis uint64_t ret;
321866be0c9Sjcs
322866be0c9Sjcs sc->sc_acpi = (struct acpi_softc *)parent;
3233b455a03Smarco sc->sc_devnode = aa->aaa_node;
324866be0c9Sjcs
3250d574372Sjcs if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKV", 0, NULL,
3260d574372Sjcs &sc->sc_hkey_version))
3270d574372Sjcs sc->sc_hkey_version = THINKPAD_HKEY_VERSION1;
3280d574372Sjcs
3290ccc52bbSkettenis printf(": version %lld.%lld\n", sc->sc_hkey_version >> 8,
3300ccc52bbSkettenis sc->sc_hkey_version & 0xff);
3310ccc52bbSkettenis
332f07ad920Sderaadt #if NAUDIO > 0 && NWSKBD > 0
333f07ad920Sderaadt /* Defer speaker mute */
334f07ad920Sderaadt if (thinkpad_get_volume_mute(sc) == 1)
335f07ad920Sderaadt startuphook_establish(thinkpad_attach_deferred, sc);
336f07ad920Sderaadt #endif
337f07ad920Sderaadt
3380b3884edSfgsch /* Set event mask to receive everything */
339866be0c9Sjcs thinkpad_enable_events(sc);
3401c450453Sjordan thinkpad_sensor_attach(sc);
341866be0c9Sjcs
342c1535f53Skettenis /* Check for ThinkLight or keyboard backlight */
34323b692d1Skettenis if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "KLCG",
34423b692d1Skettenis 0, NULL, &sc->sc_thinklight) == 0) {
345c1535f53Skettenis sc->sc_thinklight_get = "KLCG";
346c1535f53Skettenis sc->sc_thinklight_set = "KLCS";
3470dc71f27Sjcs wskbd_get_backlight = thinkpad_get_kbd_backlight;
3480dc71f27Sjcs wskbd_set_backlight = thinkpad_set_kbd_backlight;
349c1535f53Skettenis } else if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MLCG",
350c1535f53Skettenis 0, NULL, &sc->sc_thinklight) == 0) {
351c1535f53Skettenis sc->sc_thinklight_get = "MLCG";
352c1535f53Skettenis sc->sc_thinklight_set = "MLCS";
3530dc71f27Sjcs wskbd_get_backlight = thinkpad_get_kbd_backlight;
3540dc71f27Sjcs wskbd_set_backlight = thinkpad_set_kbd_backlight;
35523b692d1Skettenis }
35623b692d1Skettenis
35785897bf6Sjcs /* On version 2 and newer, let *drm or acpivout control brightness */
35885897bf6Sjcs if (sc->sc_hkey_version == THINKPAD_HKEY_VERSION1 &&
35985897bf6Sjcs (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG",
36085897bf6Sjcs 0, NULL, &sc->sc_brightness) == 0)) {
3614ea90f4eSkettenis ws_get_param = thinkpad_get_param;
3624ea90f4eSkettenis ws_set_param = thinkpad_set_param;
3634ea90f4eSkettenis }
3644ea90f4eSkettenis
365a81f321cSkettenis memset(&arg, 0, sizeof(arg));
366a81f321cSkettenis arg.type = AML_OBJTYPE_INTEGER;
367a81f321cSkettenis arg.v_integer = 1;
368a81f321cSkettenis
369a81f321cSkettenis hw_battery_chargemode = 1;
370a81f321cSkettenis hw_battery_chargestart = 0;
371a81f321cSkettenis hw_battery_chargestop = 100;
372a81f321cSkettenis
373a81f321cSkettenis if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCTG",
374a81f321cSkettenis 1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
375a81f321cSkettenis if (ret & THINKPAD_BATTERY_SUPPORT) {
376a81f321cSkettenis hw_battery_chargestart = ret & 0xff;
377a81f321cSkettenis hw_battery_setchargestart =
378a81f321cSkettenis thinkpad_battery_setchargestart;
379a81f321cSkettenis }
380a81f321cSkettenis }
381a81f321cSkettenis if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSG",
382a81f321cSkettenis 1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
383a81f321cSkettenis if (ret & THINKPAD_BATTERY_SUPPORT) {
384a81f321cSkettenis if ((ret & 0xff) == 0)
385a81f321cSkettenis hw_battery_chargestop = 100;
386a81f321cSkettenis else
387a81f321cSkettenis hw_battery_chargestop = ret & 0xff;
388a81f321cSkettenis hw_battery_setchargestop =
389a81f321cSkettenis thinkpad_battery_setchargestop;
390a81f321cSkettenis }
391a81f321cSkettenis }
392a81f321cSkettenis if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSG",
393a81f321cSkettenis 1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
394a81f321cSkettenis if (ret & THINKPAD_BATTERY_SUPPORT) {
395a81f321cSkettenis if (ret & 0x1)
396a81f321cSkettenis hw_battery_chargemode = -1;
397a81f321cSkettenis hw_battery_setchargemode =
398a81f321cSkettenis thinkpad_battery_setchargemode;
399a81f321cSkettenis }
400a81f321cSkettenis }
401a81f321cSkettenis if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICG",
402a81f321cSkettenis 1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
403a81f321cSkettenis if (ret & THINKPAD_BATTERY_SUPPORT_BICG) {
404a81f321cSkettenis if (ret & 0x1)
405a81f321cSkettenis hw_battery_chargemode = 0;
406a81f321cSkettenis hw_battery_setchargemode =
407a81f321cSkettenis thinkpad_battery_setchargemode;
408a81f321cSkettenis }
409a81f321cSkettenis }
410a81f321cSkettenis
4110b3884edSfgsch /* Run thinkpad_hotkey on button presses */
4123b455a03Smarco aml_register_notify(sc->sc_devnode, aa->aaa_dev,
4131c450453Sjordan thinkpad_hotkey, sc, ACPIDEV_POLL);
414866be0c9Sjcs }
415866be0c9Sjcs
416866be0c9Sjcs int
thinkpad_enable_events(struct acpithinkpad_softc * sc)417866be0c9Sjcs thinkpad_enable_events(struct acpithinkpad_softc *sc)
418866be0c9Sjcs {
419a95d1b72Sjordan struct aml_value arg, args[2];
420866be0c9Sjcs int64_t mask;
4210b3884edSfgsch int i;
422866be0c9Sjcs
4230d574372Sjcs /* Get the default event mask */
4240b3884edSfgsch if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA",
4250b3884edSfgsch 0, NULL, &mask)) {
426866be0c9Sjcs printf("%s: no MHKA\n", DEVNAME(sc));
4270b3884edSfgsch return (1);
428866be0c9Sjcs }
429866be0c9Sjcs
4300d574372Sjcs /* Enable events we need to know about */
431171b130bSjcs mask |= (THINKPAD_MASK_MIC_MUTE |
432171b130bSjcs THINKPAD_MASK_BRIGHTNESS_UP |
433171b130bSjcs THINKPAD_MASK_BRIGHTNESS_DOWN |
4340d574372Sjcs THINKPAD_MASK_KBD_BACKLIGHT);
4350d574372Sjcs
4360d574372Sjcs DPRINTF(("%s: setting event mask to 0x%llx\n", DEVNAME(sc), mask));
4370d574372Sjcs
4380b3884edSfgsch /* Update hotkey mask */
43981ac86e5Smarco bzero(args, sizeof(args));
440866be0c9Sjcs args[0].type = args[1].type = AML_OBJTYPE_INTEGER;
441866be0c9Sjcs for (i = 0; i < 32; i++) {
442866be0c9Sjcs args[0].v_integer = i + 1;
443866be0c9Sjcs args[1].v_integer = (((1 << i) & mask) != 0);
444866be0c9Sjcs
4450b3884edSfgsch if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM",
4460b3884edSfgsch 2, args, NULL)) {
447866be0c9Sjcs printf("%s: couldn't toggle MHKM\n", DEVNAME(sc));
4480b3884edSfgsch return (1);
449866be0c9Sjcs }
450866be0c9Sjcs }
451866be0c9Sjcs
4520b3884edSfgsch /* Enable hotkeys */
45381ac86e5Smarco bzero(&arg, sizeof(arg));
454866be0c9Sjcs arg.type = AML_OBJTYPE_INTEGER;
455866be0c9Sjcs arg.v_integer = 1;
4560b3884edSfgsch if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC",
4570b3884edSfgsch 1, &arg, NULL)) {
458866be0c9Sjcs printf("%s: couldn't enable hotkeys\n", DEVNAME(sc));
4590b3884edSfgsch return (1);
460866be0c9Sjcs }
461866be0c9Sjcs
4620b3884edSfgsch return (0);
463866be0c9Sjcs }
464866be0c9Sjcs
465866be0c9Sjcs int
thinkpad_hotkey(struct aml_node * node,int notify_type,void * arg)466866be0c9Sjcs thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg)
467866be0c9Sjcs {
468866be0c9Sjcs struct acpithinkpad_softc *sc = arg;
4690b3884edSfgsch int64_t event;
47081ac86e5Smarco
4711c450453Sjordan if (notify_type == 0x00) {
4720b3884edSfgsch /* Poll sensors */
4731c450453Sjordan thinkpad_sensor_refresh(sc);
4740b3884edSfgsch return (0);
4751c450453Sjordan }
4760b3884edSfgsch
47781ac86e5Smarco if (notify_type != 0x80)
4780b3884edSfgsch return (1);
479866be0c9Sjcs
480866be0c9Sjcs for (;;) {
4810b3884edSfgsch if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP",
4820b3884edSfgsch 0, NULL, &event))
4830b3884edSfgsch break;
4840d574372Sjcs
4850d574372Sjcs DPRINTF(("%s: event 0x%03llx\n", DEVNAME(sc), event));
4860b3884edSfgsch if (event == 0)
4870b3884edSfgsch break;
488866be0c9Sjcs
489866be0c9Sjcs switch (event) {
490e7f08eacSjsg case THINKPAD_BUTTON_BRIGHTNESS_UP:
491e7f08eacSjsg thinkpad_brightness_up(sc);
492e7f08eacSjsg break;
493e7f08eacSjsg case THINKPAD_BUTTON_BRIGHTNESS_DOWN:
494e7f08eacSjsg thinkpad_brightness_down(sc);
495e7f08eacSjsg break;
496866be0c9Sjcs case THINKPAD_BUTTON_WIRELESS:
497866be0c9Sjcs thinkpad_toggle_bluetooth(sc);
498866be0c9Sjcs break;
499866be0c9Sjcs case THINKPAD_BUTTON_SUSPEND:
50030cdf572Sderaadt #ifndef SMALL_KERNEL
5017a94bfdfSderaadt if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
50290680e5dSjordan acpi_addtask(sc->sc_acpi, acpi_sleep_task,
503ad814436Sderaadt sc->sc_acpi, SLEEP_SUSPEND);
50430cdf572Sderaadt #endif
505866be0c9Sjcs break;
5060b3884edSfgsch case THINKPAD_BUTTON_VOLUME_MUTE:
5070b3884edSfgsch thinkpad_volume_mute(sc);
5080b3884edSfgsch break;
5093e7c1b35Sjcs case THINKPAD_BUTTON_VOLUME_DOWN:
5103e7c1b35Sjcs thinkpad_volume_down(sc);
5113e7c1b35Sjcs break;
5123e7c1b35Sjcs case THINKPAD_BUTTON_VOLUME_UP:
5133e7c1b35Sjcs thinkpad_volume_up(sc);
5143e7c1b35Sjcs break;
51592a3e4ccSderaadt case THINKPAD_BUTTON_MICROPHONE_MUTE:
51692a3e4ccSderaadt #if NAUDIO > 0 && NWSKBD > 0
517012023bfSmpi wskbd_set_mixervolume(0, 0);
51892a3e4ccSderaadt #endif
51992a3e4ccSderaadt break;
5202dd2a277Sderaadt case THINKPAD_BUTTON_HIBERNATE:
5219502e089Sdcoppa #if defined(HIBERNATE) && !defined(SMALL_KERNEL)
5228d4bc9d5Sdcoppa if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
5232dd2a277Sderaadt acpi_addtask(sc->sc_acpi, acpi_sleep_task,
524ad814436Sderaadt sc->sc_acpi, SLEEP_HIBERNATE);
5252dd2a277Sderaadt #endif
5262dd2a277Sderaadt break;
52723b692d1Skettenis case THINKPAD_BUTTON_THINKLIGHT:
52823b692d1Skettenis thinkpad_get_thinklight(sc);
52923b692d1Skettenis break;
5308107bf4dSjcs case THINKPAD_ADAPTIVE_NEXT:
5318107bf4dSjcs case THINKPAD_ADAPTIVE_QUICK:
5328107bf4dSjcs thinkpad_adaptive_change(sc);
5338107bf4dSjcs break;
5344ea90f4eSkettenis case THINKPAD_BACKLIGHT_CHANGED:
5354ea90f4eSkettenis thinkpad_get_brightness(sc);
5364ea90f4eSkettenis break;
537306c5139Smlarkin case THINKPAD_PORT_REPL_DOCKED:
538306c5139Smlarkin sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 1;
539306c5139Smlarkin sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
540306c5139Smlarkin SENSOR_S_OK;
541306c5139Smlarkin break;
542306c5139Smlarkin case THINKPAD_PORT_REPL_UNDOCKED:
543306c5139Smlarkin sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 0;
544306c5139Smlarkin sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
545306c5139Smlarkin SENSOR_S_OK;
546306c5139Smlarkin break;
5470b3884edSfgsch default:
548b96dcde9Sjcs /* unknown or boring event */
549b96dcde9Sjcs DPRINTF(("%s: unhandled event 0x%03llx\n", DEVNAME(sc),
550b96dcde9Sjcs event));
551b96dcde9Sjcs break;
552866be0c9Sjcs }
553866be0c9Sjcs }
554866be0c9Sjcs
555b96dcde9Sjcs return (0);
556866be0c9Sjcs }
557866be0c9Sjcs
558866be0c9Sjcs int
thinkpad_toggle_bluetooth(struct acpithinkpad_softc * sc)559866be0c9Sjcs thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc)
560866be0c9Sjcs {
561a95d1b72Sjordan struct aml_value arg;
562a95d1b72Sjordan int64_t bluetooth;
563866be0c9Sjcs
5640b3884edSfgsch if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC",
5650b3884edSfgsch 0, NULL, &bluetooth))
5660b3884edSfgsch return (1);
567866be0c9Sjcs
568866be0c9Sjcs if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT))
5690b3884edSfgsch return (1);
570866be0c9Sjcs
57181ac86e5Smarco bzero(&arg, sizeof(arg));
572866be0c9Sjcs arg.type = AML_OBJTYPE_INTEGER;
5730b3884edSfgsch arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED;
5740b3884edSfgsch if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC",
5750b3884edSfgsch 1, &arg, NULL)) {
576866be0c9Sjcs printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc));
5770b3884edSfgsch return (1);
578866be0c9Sjcs }
579866be0c9Sjcs
5800b3884edSfgsch return (0);
581866be0c9Sjcs }
582866be0c9Sjcs
583866be0c9Sjcs int
thinkpad_toggle_wan(struct acpithinkpad_softc * sc)584866be0c9Sjcs thinkpad_toggle_wan(struct acpithinkpad_softc *sc)
585866be0c9Sjcs {
586a95d1b72Sjordan struct aml_value arg;
587a95d1b72Sjordan int64_t wan;
588866be0c9Sjcs
5890b3884edSfgsch if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN",
5900b3884edSfgsch 0, NULL, &wan))
5910b3884edSfgsch return (1);
592866be0c9Sjcs
59381ac86e5Smarco if (!(wan & THINKPAD_WAN_PRESENT))
5940b3884edSfgsch return (1);
59581ac86e5Smarco
59681ac86e5Smarco bzero(&arg, sizeof(arg));
597866be0c9Sjcs arg.type = AML_OBJTYPE_INTEGER;
5980b3884edSfgsch arg.v_integer = wan ^ THINKPAD_WAN_ENABLED;
5990b3884edSfgsch if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN",
6000b3884edSfgsch 1, &arg, NULL)) {
601866be0c9Sjcs printf("%s: couldn't toggle wan\n", DEVNAME(sc));
6020b3884edSfgsch return (1);
603866be0c9Sjcs }
604866be0c9Sjcs
6050b3884edSfgsch return (0);
606866be0c9Sjcs }
607866be0c9Sjcs
608866be0c9Sjcs int
thinkpad_cmos(struct acpithinkpad_softc * sc,uint8_t cmd)609866be0c9Sjcs thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd)
610866be0c9Sjcs {
611866be0c9Sjcs struct aml_value arg;
612866be0c9Sjcs
61381ac86e5Smarco bzero(&arg, sizeof(arg));
614866be0c9Sjcs arg.type = AML_OBJTYPE_INTEGER;
615866be0c9Sjcs arg.v_integer = cmd;
61690c6566bSderaadt aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL);
617866be0c9Sjcs return (0);
618866be0c9Sjcs }
619866be0c9Sjcs
620866be0c9Sjcs int
thinkpad_volume_down(struct acpithinkpad_softc * sc)6213e7c1b35Sjcs thinkpad_volume_down(struct acpithinkpad_softc *sc)
6223e7c1b35Sjcs {
62381ac86e5Smarco return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN));
6243e7c1b35Sjcs }
6253e7c1b35Sjcs
6263e7c1b35Sjcs int
thinkpad_volume_up(struct acpithinkpad_softc * sc)6273e7c1b35Sjcs thinkpad_volume_up(struct acpithinkpad_softc *sc)
6283e7c1b35Sjcs {
62981ac86e5Smarco return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP));
6303e7c1b35Sjcs }
6313e7c1b35Sjcs
6323e7c1b35Sjcs int
thinkpad_volume_mute(struct acpithinkpad_softc * sc)6333e7c1b35Sjcs thinkpad_volume_mute(struct acpithinkpad_softc *sc)
6343e7c1b35Sjcs {
63581ac86e5Smarco return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE));
6363e7c1b35Sjcs }
6373e7c1b35Sjcs
6383e7c1b35Sjcs int
thinkpad_brightness_up(struct acpithinkpad_softc * sc)639866be0c9Sjcs thinkpad_brightness_up(struct acpithinkpad_softc *sc)
640866be0c9Sjcs {
6410d574372Sjcs int b;
6420d574372Sjcs
643171b130bSjcs if (thinkpad_get_brightness(sc) == 0) {
6440d574372Sjcs b = sc->sc_brightness & 0xff;
6450d574372Sjcs if (b < ((sc->sc_brightness >> 8) & 0xff)) {
6460d574372Sjcs sc->sc_brightness = b + 1;
6470d574372Sjcs thinkpad_set_brightness(sc, 0);
6480d574372Sjcs }
6490d574372Sjcs
6500d574372Sjcs return (0);
6510d574372Sjcs } else
65281ac86e5Smarco return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP));
653866be0c9Sjcs }
654866be0c9Sjcs
655866be0c9Sjcs int
thinkpad_brightness_down(struct acpithinkpad_softc * sc)656866be0c9Sjcs thinkpad_brightness_down(struct acpithinkpad_softc *sc)
657866be0c9Sjcs {
6580d574372Sjcs int b;
6590d574372Sjcs
660171b130bSjcs if (thinkpad_get_brightness(sc) == 0) {
6610d574372Sjcs b = sc->sc_brightness & 0xff;
6620d574372Sjcs if (b > 0) {
6630d574372Sjcs sc->sc_brightness = b - 1;
6640d574372Sjcs thinkpad_set_brightness(sc, 0);
6650d574372Sjcs }
6660d574372Sjcs
6670d574372Sjcs return (0);
6680d574372Sjcs } else
66981ac86e5Smarco return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN));
670866be0c9Sjcs }
6718107bf4dSjcs
6728107bf4dSjcs int
thinkpad_adaptive_change(struct acpithinkpad_softc * sc)6738107bf4dSjcs thinkpad_adaptive_change(struct acpithinkpad_softc *sc)
6748107bf4dSjcs {
6758107bf4dSjcs struct aml_value arg;
6768107bf4dSjcs int64_t mode;
6778107bf4dSjcs
6788107bf4dSjcs if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
6798107bf4dSjcs 0, NULL, &mode)) {
6808107bf4dSjcs printf("%s: couldn't get adaptive keyboard mode\n", DEVNAME(sc));
6818107bf4dSjcs return (1);
6828107bf4dSjcs }
6838107bf4dSjcs
6848107bf4dSjcs bzero(&arg, sizeof(arg));
6858107bf4dSjcs arg.type = AML_OBJTYPE_INTEGER;
6868107bf4dSjcs
6878107bf4dSjcs if (mode == THINKPAD_ADAPTIVE_MODE_FUNCTION)
6888107bf4dSjcs arg.v_integer = THINKPAD_ADAPTIVE_MODE_HOME;
6898107bf4dSjcs else
6908107bf4dSjcs arg.v_integer = THINKPAD_ADAPTIVE_MODE_FUNCTION;
6918107bf4dSjcs
6928107bf4dSjcs if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "STRW",
6938107bf4dSjcs 1, &arg, NULL)) {
6948107bf4dSjcs printf("%s: couldn't set adaptive keyboard mode\n", DEVNAME(sc));
6958107bf4dSjcs return (1);
6968107bf4dSjcs }
6978107bf4dSjcs
6988107bf4dSjcs return (0);
6998107bf4dSjcs }
7008107bf4dSjcs
7018107bf4dSjcs int
thinkpad_activate(struct device * self,int act)7028107bf4dSjcs thinkpad_activate(struct device *self, int act)
7038107bf4dSjcs {
7048107bf4dSjcs
7058107bf4dSjcs struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
7068107bf4dSjcs int64_t res;
707f07ad920Sderaadt #if NAUDIO > 0 && NWSKBD > 0
708f07ad920Sderaadt int mute;
709f07ad920Sderaadt #endif
7108107bf4dSjcs
7118107bf4dSjcs switch (act) {
7128107bf4dSjcs case DVACT_WAKEUP:
7138107bf4dSjcs if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
7148107bf4dSjcs 0, NULL, &res) == 0)
7158107bf4dSjcs thinkpad_adaptive_change(sc);
716f07ad920Sderaadt #if NAUDIO > 0 && NWSKBD > 0
717f07ad920Sderaadt mute = thinkpad_get_volume_mute(sc);
718f07ad920Sderaadt if (mute != -1)
719f07ad920Sderaadt wskbd_set_mixermute(mute, 1);
720f07ad920Sderaadt #endif
7218107bf4dSjcs break;
7228107bf4dSjcs }
7238107bf4dSjcs return (0);
7248107bf4dSjcs }
72523b692d1Skettenis
72623b692d1Skettenis void
thinkpad_get_thinklight(struct acpithinkpad_softc * sc)72723b692d1Skettenis thinkpad_get_thinklight(struct acpithinkpad_softc *sc)
72823b692d1Skettenis {
729a77df94aSkettenis if (sc->sc_thinklight_get)
730c1535f53Skettenis aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
731c1535f53Skettenis sc->sc_thinklight_get, 0, NULL, &sc->sc_thinklight);
73223b692d1Skettenis }
73323b692d1Skettenis
73423b692d1Skettenis void
thinkpad_set_thinklight(void * arg0,int arg1)73523b692d1Skettenis thinkpad_set_thinklight(void *arg0, int arg1)
73623b692d1Skettenis {
73723b692d1Skettenis struct acpithinkpad_softc *sc = arg0;
73823b692d1Skettenis struct aml_value arg;
73923b692d1Skettenis
74023b692d1Skettenis memset(&arg, 0, sizeof(arg));
74123b692d1Skettenis arg.type = AML_OBJTYPE_INTEGER;
742c1535f53Skettenis arg.v_integer = sc->sc_thinklight & 0x0f;
743c1535f53Skettenis aml_evalname(sc->sc_acpi, sc->sc_devnode,
744c1535f53Skettenis sc->sc_thinklight_set, 1, &arg, NULL);
74523b692d1Skettenis }
74623b692d1Skettenis
74723b692d1Skettenis int
thinkpad_get_kbd_backlight(struct wskbd_backlight * kbl)7480dc71f27Sjcs thinkpad_get_kbd_backlight(struct wskbd_backlight *kbl)
74923b692d1Skettenis {
75023b692d1Skettenis struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
75123b692d1Skettenis
75223b692d1Skettenis KASSERT(sc != NULL);
75323b692d1Skettenis
75423b692d1Skettenis kbl->min = 0;
755c1535f53Skettenis kbl->max = (sc->sc_thinklight >> 8) & 0x0f;
756c1535f53Skettenis kbl->curval = sc->sc_thinklight & 0x0f;
757ae785f32Sjsg
758ae785f32Sjsg if (kbl->max == 0)
759ae785f32Sjsg return (ENOTTY);
760ae785f32Sjsg
76123b692d1Skettenis return 0;
76223b692d1Skettenis }
76323b692d1Skettenis
76423b692d1Skettenis int
thinkpad_set_kbd_backlight(struct wskbd_backlight * kbl)7650dc71f27Sjcs thinkpad_set_kbd_backlight(struct wskbd_backlight *kbl)
76623b692d1Skettenis {
76723b692d1Skettenis struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
768aca16f73Sjsg int maxval;
76923b692d1Skettenis
77023b692d1Skettenis KASSERT(sc != NULL);
77123b692d1Skettenis
772aca16f73Sjsg maxval = (sc->sc_thinklight >> 8) & 0x0f;
773aca16f73Sjsg
774ae785f32Sjsg if (maxval == 0)
775ae785f32Sjsg return (ENOTTY);
776ae785f32Sjsg
77723b692d1Skettenis if (kbl->curval > maxval)
77823b692d1Skettenis return EINVAL;
77923b692d1Skettenis
78023b692d1Skettenis sc->sc_thinklight &= ~0xff;
78123b692d1Skettenis sc->sc_thinklight |= kbl->curval;
78223b692d1Skettenis acpi_addtask(sc->sc_acpi, thinkpad_set_thinklight, sc, 0);
78323b692d1Skettenis acpi_wakeup(sc->sc_acpi);
78423b692d1Skettenis return 0;
78523b692d1Skettenis }
7864ea90f4eSkettenis
787171b130bSjcs int
thinkpad_get_brightness(struct acpithinkpad_softc * sc)7884ea90f4eSkettenis thinkpad_get_brightness(struct acpithinkpad_softc *sc)
7894ea90f4eSkettenis {
790171b130bSjcs int ret;
791171b130bSjcs
792171b130bSjcs ret = aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG", 0, NULL,
793171b130bSjcs &sc->sc_brightness);
7940d574372Sjcs
7950d574372Sjcs DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
796171b130bSjcs
797171b130bSjcs return ret;
7984ea90f4eSkettenis }
7994ea90f4eSkettenis
800171b130bSjcs int
thinkpad_set_brightness(void * arg0,int arg1)8014ea90f4eSkettenis thinkpad_set_brightness(void *arg0, int arg1)
8024ea90f4eSkettenis {
8034ea90f4eSkettenis struct acpithinkpad_softc *sc = arg0;
8044ea90f4eSkettenis struct aml_value arg;
805171b130bSjcs int ret;
8064ea90f4eSkettenis
8070d574372Sjcs DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
8080d574372Sjcs
8094ea90f4eSkettenis memset(&arg, 0, sizeof(arg));
8104ea90f4eSkettenis arg.type = AML_OBJTYPE_INTEGER;
8114ea90f4eSkettenis arg.v_integer = sc->sc_brightness & 0xff;
812171b130bSjcs ret = aml_evalname(sc->sc_acpi, sc->sc_devnode, "PBLS", 1, &arg, NULL);
813171b130bSjcs
814171b130bSjcs if (ret)
815171b130bSjcs return ret;
8160d574372Sjcs
8170d574372Sjcs thinkpad_get_brightness(sc);
818171b130bSjcs
819171b130bSjcs return 0;
8204ea90f4eSkettenis }
8214ea90f4eSkettenis
8224ea90f4eSkettenis int
thinkpad_get_param(struct wsdisplay_param * dp)8234ea90f4eSkettenis thinkpad_get_param(struct wsdisplay_param *dp)
8244ea90f4eSkettenis {
8254ea90f4eSkettenis struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
8264ea90f4eSkettenis
8274ea90f4eSkettenis if (sc == NULL)
8284ea90f4eSkettenis return -1;
8294ea90f4eSkettenis
8304ea90f4eSkettenis switch (dp->param) {
8314ea90f4eSkettenis case WSDISPLAYIO_PARAM_BRIGHTNESS:
8324ea90f4eSkettenis dp->min = 0;
8334ea90f4eSkettenis dp->max = (sc->sc_brightness >> 8) & 0xff;
8344ea90f4eSkettenis dp->curval = sc->sc_brightness & 0xff;
8354ea90f4eSkettenis return 0;
8364ea90f4eSkettenis default:
8374ea90f4eSkettenis return -1;
8384ea90f4eSkettenis }
8394ea90f4eSkettenis }
8404ea90f4eSkettenis
8414ea90f4eSkettenis int
thinkpad_set_param(struct wsdisplay_param * dp)8424ea90f4eSkettenis thinkpad_set_param(struct wsdisplay_param *dp)
8434ea90f4eSkettenis {
8444ea90f4eSkettenis struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
845aca16f73Sjsg int maxval;
8464ea90f4eSkettenis
8474ea90f4eSkettenis if (sc == NULL)
8484ea90f4eSkettenis return -1;
8494ea90f4eSkettenis
850aca16f73Sjsg maxval = (sc->sc_brightness >> 8) & 0xff;
851aca16f73Sjsg
8524ea90f4eSkettenis switch (dp->param) {
8534ea90f4eSkettenis case WSDISPLAYIO_PARAM_BRIGHTNESS:
8544ea90f4eSkettenis if (dp->curval < 0)
8554ea90f4eSkettenis dp->curval = 0;
8564ea90f4eSkettenis if (dp->curval > maxval)
8574ea90f4eSkettenis dp->curval = maxval;
8584ea90f4eSkettenis sc->sc_brightness &= ~0xff;
8594ea90f4eSkettenis sc->sc_brightness |= dp->curval;
860171b130bSjcs acpi_addtask(sc->sc_acpi, (void *)thinkpad_set_brightness, sc,
861171b130bSjcs 0);
8624ea90f4eSkettenis acpi_wakeup(sc->sc_acpi);
8634ea90f4eSkettenis return 0;
8644ea90f4eSkettenis default:
8654ea90f4eSkettenis return -1;
8664ea90f4eSkettenis }
8674ea90f4eSkettenis }
868f07ad920Sderaadt
869d91d9c7cSjcs int
thinkpad_get_temp(struct acpithinkpad_softc * sc,int idx,int64_t * temp)870d91d9c7cSjcs thinkpad_get_temp(struct acpithinkpad_softc *sc, int idx, int64_t *temp)
871d91d9c7cSjcs {
872d91d9c7cSjcs char sname[5];
873d91d9c7cSjcs
874d91d9c7cSjcs snprintf(sname, sizeof(sname), "TMP%d", idx);
875d91d9c7cSjcs
876d91d9c7cSjcs if (aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode, sname, 0, 0,
877d91d9c7cSjcs temp) != 0)
878d91d9c7cSjcs return (1);
879d91d9c7cSjcs
880d91d9c7cSjcs return (0);
881d91d9c7cSjcs }
882d91d9c7cSjcs
883f07ad920Sderaadt #if NAUDIO > 0 && NWSKBD > 0
884f07ad920Sderaadt void
thinkpad_attach_deferred(void * v __unused)885f07ad920Sderaadt thinkpad_attach_deferred(void *v __unused)
886f07ad920Sderaadt {
887f07ad920Sderaadt wskbd_set_mixermute(1, 1);
888f07ad920Sderaadt }
889f07ad920Sderaadt
890f07ad920Sderaadt int
thinkpad_get_volume_mute(struct acpithinkpad_softc * sc)891f07ad920Sderaadt thinkpad_get_volume_mute(struct acpithinkpad_softc *sc)
892f07ad920Sderaadt {
893d2eaebe9Skettenis uint8_t vol = 0;
894f07ad920Sderaadt
895f07ad920Sderaadt if (sc->sc_acpi->sc_ec == NULL)
896f07ad920Sderaadt return (-1);
897f07ad920Sderaadt
898f07ad920Sderaadt acpiec_read(sc->sc_acpi->sc_ec, THINKPAD_ECOFFSET_VOLUME, 1, &vol);
899f07ad920Sderaadt return ((vol & THINKPAD_ECOFFSET_VOLUME_MUTE_MASK) ==
900f07ad920Sderaadt THINKPAD_ECOFFSET_VOLUME_MUTE_MASK);
901f07ad920Sderaadt }
902f07ad920Sderaadt #endif
903a81f321cSkettenis
904a81f321cSkettenis int
thinkpad_battery_inhibit_charge(int state)905a81f321cSkettenis thinkpad_battery_inhibit_charge(int state)
906a81f321cSkettenis {
907a81f321cSkettenis struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
908a81f321cSkettenis struct aml_value arg;
909*3e37c391Sclaudio int battery, count;
910a81f321cSkettenis uint64_t ret;
911a81f321cSkettenis
912*3e37c391Sclaudio count = acpi_batcount(sc->sc_acpi);
913*3e37c391Sclaudio for (battery = 1; battery <= count; battery++) {
914a81f321cSkettenis memset(&arg, 0, sizeof(arg));
915a81f321cSkettenis arg.type = AML_OBJTYPE_INTEGER;
916a81f321cSkettenis arg.v_integer = (0xffff << 8) | (battery << 4) | state;
917a81f321cSkettenis if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICS",
918a81f321cSkettenis 1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
919a81f321cSkettenis return EIO;
920*3e37c391Sclaudio }
921a81f321cSkettenis return 0;
922a81f321cSkettenis }
923a81f321cSkettenis
924a81f321cSkettenis int
thinkpad_battery_force_discharge(int state)925a81f321cSkettenis thinkpad_battery_force_discharge(int state)
926a81f321cSkettenis {
927a81f321cSkettenis struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
928a81f321cSkettenis struct aml_value arg;
929*3e37c391Sclaudio int battery, count;
930a81f321cSkettenis uint64_t ret;
931a81f321cSkettenis
932*3e37c391Sclaudio count = acpi_batcount(sc->sc_acpi);
933*3e37c391Sclaudio for (battery = 1; battery <= count; battery++) {
934a81f321cSkettenis memset(&arg, 0, sizeof(arg));
935a81f321cSkettenis arg.type = AML_OBJTYPE_INTEGER;
936a81f321cSkettenis arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | state;
937a81f321cSkettenis if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSS",
938a81f321cSkettenis 1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
939a81f321cSkettenis return EIO;
940*3e37c391Sclaudio }
941a81f321cSkettenis return 0;
942a81f321cSkettenis }
943a81f321cSkettenis
944a81f321cSkettenis int
thinkpad_battery_setchargemode(int mode)945a81f321cSkettenis thinkpad_battery_setchargemode(int mode)
946a81f321cSkettenis {
947a81f321cSkettenis int error;
948a81f321cSkettenis
949a81f321cSkettenis switch (mode) {
950a81f321cSkettenis case -1:
951a81f321cSkettenis error = thinkpad_battery_inhibit_charge(1);
952a81f321cSkettenis if (error)
953a81f321cSkettenis return error;
954a81f321cSkettenis error = thinkpad_battery_force_discharge(1);
955a81f321cSkettenis if (error)
956a81f321cSkettenis return error;
957a81f321cSkettenis break;
958a81f321cSkettenis case 0:
959a81f321cSkettenis error = thinkpad_battery_force_discharge(0);
960a81f321cSkettenis if (error)
961a81f321cSkettenis return error;
962a81f321cSkettenis error = thinkpad_battery_inhibit_charge(1);
963a81f321cSkettenis if (error)
964a81f321cSkettenis return error;
965a81f321cSkettenis break;
966a81f321cSkettenis case 1:
967a81f321cSkettenis error = thinkpad_battery_force_discharge(0);
968a81f321cSkettenis if (error)
969a81f321cSkettenis return error;
970a81f321cSkettenis error = thinkpad_battery_inhibit_charge(0);
971a81f321cSkettenis if (error)
972a81f321cSkettenis return error;
973a81f321cSkettenis break;
974a81f321cSkettenis default:
975a81f321cSkettenis return EOPNOTSUPP;
976a81f321cSkettenis }
977a81f321cSkettenis
978a81f321cSkettenis hw_battery_chargemode = mode;
979a81f321cSkettenis return 0;
980a81f321cSkettenis }
981a81f321cSkettenis
982a81f321cSkettenis int
thinkpad_battery_setchargestart(int start)983a81f321cSkettenis thinkpad_battery_setchargestart(int start)
984a81f321cSkettenis {
985a81f321cSkettenis struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
986a81f321cSkettenis struct aml_value arg;
987*3e37c391Sclaudio int battery, count;
988a81f321cSkettenis uint64_t ret;
989a81f321cSkettenis
990a81f321cSkettenis if (start >= hw_battery_chargestop)
991a81f321cSkettenis return EINVAL;
992a81f321cSkettenis
993*3e37c391Sclaudio count = acpi_batcount(sc->sc_acpi);
994*3e37c391Sclaudio for (battery = 1; battery <= count; battery++) {
995a81f321cSkettenis memset(&arg, 0, sizeof(arg));
996a81f321cSkettenis arg.type = AML_OBJTYPE_INTEGER;
997a81f321cSkettenis arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | start;
998a81f321cSkettenis if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCCS",
999a81f321cSkettenis 1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
1000a81f321cSkettenis return EIO;
1001*3e37c391Sclaudio }
1002a81f321cSkettenis hw_battery_chargestart = start;
1003a81f321cSkettenis return 0;
1004a81f321cSkettenis }
1005a81f321cSkettenis
1006a81f321cSkettenis int
thinkpad_battery_setchargestop(int stop)1007a81f321cSkettenis thinkpad_battery_setchargestop(int stop)
1008a81f321cSkettenis {
1009a81f321cSkettenis struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
1010a81f321cSkettenis struct aml_value arg;
1011*3e37c391Sclaudio int battery, count;
1012a81f321cSkettenis uint64_t ret;
1013a81f321cSkettenis
1014a81f321cSkettenis if (stop <= hw_battery_chargestart)
1015a81f321cSkettenis return EINVAL;
1016a81f321cSkettenis
1017a81f321cSkettenis if (stop == 100)
1018a81f321cSkettenis stop = 0;
1019a81f321cSkettenis
1020*3e37c391Sclaudio count = acpi_batcount(sc->sc_acpi);
1021*3e37c391Sclaudio for (battery = 1; battery <= count; battery++) {
1022a81f321cSkettenis memset(&arg, 0, sizeof(arg));
1023a81f321cSkettenis arg.type = AML_OBJTYPE_INTEGER;
1024a81f321cSkettenis arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | stop;
1025a81f321cSkettenis if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSS",
1026a81f321cSkettenis 1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
1027a81f321cSkettenis return EIO;
1028*3e37c391Sclaudio }
1029a81f321cSkettenis hw_battery_chargestop = (stop == 0) ? 100 : stop;
1030a81f321cSkettenis return 0;
1031a81f321cSkettenis }
1032