xref: /openbsd-src/sys/dev/acpi/acpithinkpad.c (revision 4b70baf6e17fc8b27fc1f7fa7929335753fa94c3)
1 /*	$OpenBSD: acpithinkpad.c,v 1.64 2019/03/08 16:33:23 jcs Exp $	*/
2 /*
3  * Copyright (c) 2008 joshua stein <jcs@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 
21 #include <dev/acpi/acpireg.h>
22 #include <dev/acpi/acpivar.h>
23 #include <dev/acpi/acpidev.h>
24 #include <dev/acpi/amltypes.h>
25 #include <dev/acpi/dsdt.h>
26 #include <dev/wscons/wsconsio.h>
27 
28 #include <machine/apmvar.h>
29 
30 #include "audio.h"
31 #include "wskbd.h"
32 
33 /* #define ACPITHINKPAD_DEBUG */
34 
35 #ifdef ACPITHINKPAD_DEBUG
36 #define DPRINTF(x) printf x
37 #else
38 #define DPRINTF(x)
39 #endif
40 
41 #define	THINKPAD_HKEY_VERSION1		0x0100
42 #define	THINKPAD_HKEY_VERSION2		0x0200
43 
44 #define	THINKPAD_CMOS_VOLUME_DOWN	0x00
45 #define	THINKPAD_CMOS_VOLUME_UP		0x01
46 #define	THINKPAD_CMOS_VOLUME_MUTE	0x02
47 #define	THINKPAD_CMOS_BRIGHTNESS_UP	0x04
48 #define	THINKPAD_CMOS_BRIGHTNESS_DOWN	0x05
49 
50 #define	THINKPAD_BLUETOOTH_PRESENT	0x01
51 #define	THINKPAD_BLUETOOTH_ENABLED	0x02
52 
53 /* wan (not wifi) card */
54 #define	THINKPAD_WAN_PRESENT		0x01
55 #define	THINKPAD_WAN_ENABLED		0x02
56 
57 #define	THINKPAD_BUTTON_FN_F1		0x1001
58 #define	THINKPAD_BUTTON_LOCK_SCREEN	0x1002
59 #define	THINKPAD_BUTTON_BATTERY_INFO	0x1003
60 #define	THINKPAD_BUTTON_SUSPEND		0x1004
61 #define	THINKPAD_BUTTON_WIRELESS	0x1005
62 #define	THINKPAD_BUTTON_FN_F6		0x1006
63 #define	THINKPAD_BUTTON_EXTERNAL_SCREEN	0x1007
64 #define	THINKPAD_BUTTON_POINTER_SWITCH	0x1008
65 #define	THINKPAD_BUTTON_EJECT		0x1009
66 #define	THINKPAD_BUTTON_FN_F11		0x100b
67 #define	THINKPAD_BUTTON_HIBERNATE	0x100c
68 #define	THINKPAD_BUTTON_BRIGHTNESS_UP	0x1010
69 #define	THINKPAD_BUTTON_BRIGHTNESS_DOWN	0x1011
70 #define	THINKPAD_BUTTON_THINKLIGHT	0x1012
71 #define	THINKPAD_BUTTON_FN_SPACE	0x1014
72 #define	THINKPAD_BUTTON_VOLUME_UP	0x1015
73 #define	THINKPAD_BUTTON_VOLUME_DOWN	0x1016
74 #define	THINKPAD_BUTTON_VOLUME_MUTE	0x1017
75 #define	THINKPAD_BUTTON_THINKVANTAGE	0x1018
76 #define	THINKPAD_BUTTON_BLACK		0x101a
77 #define	THINKPAD_BUTTON_MICROPHONE_MUTE	0x101b
78 #define	THINKPAD_KEYLIGHT_CHANGED	0x101c
79 #define	THINKPAD_BUTTON_CONFIG		0x101d
80 #define	THINKPAD_BUTTON_FIND		0x101e
81 #define	THINKPAD_BUTTON_ALL_ACTIVEPROGS	0x101f
82 #define	THINKPAD_BUTTON_ALL_PROGS	0x1020
83 
84 #define	THINKPAD_ADAPTIVE_NEXT		0x1101
85 #define	THINKPAD_ADAPTIVE_QUICK		0x1102
86 #define	THINKPAD_ADAPTIVE_SNIP		0x1105
87 #define	THINKPAD_ADAPTIVE_VOICE		0x1108
88 #define	THINKPAD_ADAPTIVE_GESTURES	0x110a
89 #define	THINKPAD_ADAPTIVE_SETTINGS	0x110e
90 #define	THINKPAD_ADAPTIVE_TAB		0x110f
91 #define	THINKPAD_ADAPTIVE_REFRESH	0x1110
92 #define	THINKPAD_ADAPTIVE_BACK		0x1111
93 #define THINKPAD_PORT_REPL_DOCKED	0x4010
94 #define THINKPAD_PORT_REPL_UNDOCKED	0x4011
95 #define	THINKPAD_TABLET_DOCKED		0x4012
96 #define	THINKPAD_TABLET_UNDOCKED	0x4013
97 #define	THINKPAD_LID_OPEN		0x5001
98 #define	THINKPAD_LID_CLOSED		0x5002
99 #define	THINKPAD_TABLET_SCREEN_NORMAL	0x500a
100 #define	THINKPAD_TABLET_SCREEN_ROTATED	0x5009
101 #define	THINKPAD_BRIGHTNESS_CHANGED	0x5010
102 #define	THINKPAD_TABLET_PEN_INSERTED	0x500b
103 #define	THINKPAD_TABLET_PEN_REMOVED	0x500c
104 #define	THINKPAD_SWITCH_NUMLOCK		0x6000
105 #define	THINKPAD_BUTTON_ROTATION_LOCK	0x6020
106 #define	THINKPAD_THERMAL_TABLE_CHANGED	0x6030
107 #define	THINKPAD_POWER_CHANGED		0x6040
108 #define	THINKPAD_BACKLIGHT_CHANGED	0x6050
109 #define	THINKPAD_BUTTON_FN_TOGGLE       0x6060
110 #define	THINKPAD_TABLET_SCREEN_CHANGED	0x60c0
111 #define	THINKPAD_SWITCH_WIRELESS	0x7000
112 
113 #define THINKPAD_NSENSORS 10
114 #define THINKPAD_NTEMPSENSORS 8
115 
116 #define THINKPAD_SENSOR_FANRPM		THINKPAD_NTEMPSENSORS
117 #define THINKPAD_SENSOR_PORTREPL	THINKPAD_NTEMPSENSORS + 1
118 
119 #define THINKPAD_ECOFFSET_VOLUME	0x30
120 #define THINKPAD_ECOFFSET_VOLUME_MUTE_MASK 0x40
121 #define THINKPAD_ECOFFSET_FANLO		0x84
122 #define THINKPAD_ECOFFSET_FANHI		0x85
123 
124 #define	THINKPAD_ADAPTIVE_MODE_HOME	1
125 #define	THINKPAD_ADAPTIVE_MODE_FUNCTION	3
126 
127 #define THINKPAD_MASK_MIC_MUTE		(1 << 14)
128 #define THINKPAD_MASK_BRIGHTNESS_UP	(1 << 15)
129 #define THINKPAD_MASK_BRIGHTNESS_DOWN	(1 << 16)
130 #define THINKPAD_MASK_KBD_BACKLIGHT	(1 << 17)
131 
132 struct acpithinkpad_softc {
133 	struct device		 sc_dev;
134 
135 	struct acpiec_softc     *sc_ec;
136 	struct acpi_softc	*sc_acpi;
137 	struct aml_node		*sc_devnode;
138 
139 	struct ksensor		 sc_sens[THINKPAD_NSENSORS];
140 	struct ksensordev	 sc_sensdev;
141 
142 	uint64_t		 sc_hkey_version;
143 
144 	uint64_t		 sc_thinklight;
145 	const char		*sc_thinklight_get;
146 	const char		*sc_thinklight_set;
147 
148 	uint64_t		 sc_brightness;
149 };
150 
151 extern void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
152 
153 int	thinkpad_match(struct device *, void *, void *);
154 void	thinkpad_attach(struct device *, struct device *, void *);
155 int	thinkpad_hotkey(struct aml_node *, int, void *);
156 int	thinkpad_enable_events(struct acpithinkpad_softc *);
157 int	thinkpad_toggle_bluetooth(struct acpithinkpad_softc *);
158 int	thinkpad_toggle_wan(struct acpithinkpad_softc *);
159 int	thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t);
160 int	thinkpad_volume_down(struct acpithinkpad_softc *);
161 int	thinkpad_volume_up(struct acpithinkpad_softc *);
162 int	thinkpad_volume_mute(struct acpithinkpad_softc *);
163 int	thinkpad_brightness_up(struct acpithinkpad_softc *);
164 int	thinkpad_brightness_down(struct acpithinkpad_softc *);
165 int	thinkpad_adaptive_change(struct acpithinkpad_softc *);
166 int	thinkpad_activate(struct device *, int);
167 
168 /* wscons hook functions */
169 void	thinkpad_get_thinklight(struct acpithinkpad_softc *);
170 void	thinkpad_set_thinklight(void *, int);
171 int	thinkpad_get_kbd_backlight(struct wskbd_backlight *);
172 int	thinkpad_set_kbd_backlight(struct wskbd_backlight *);
173 extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
174 extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
175 int	thinkpad_get_brightness(struct acpithinkpad_softc *);
176 int	thinkpad_set_brightness(void *, int);
177 int	thinkpad_get_param(struct wsdisplay_param *);
178 int	thinkpad_set_param(struct wsdisplay_param *);
179 extern int (*ws_get_param)(struct wsdisplay_param *);
180 extern int (*ws_set_param)(struct wsdisplay_param *);
181 
182 void    thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
183 void    thinkpad_sensor_refresh(void *);
184 
185 #if NAUDIO > 0 && NWSKBD > 0
186 void thinkpad_attach_deferred(void *);
187 int thinkpad_get_volume_mute(struct acpithinkpad_softc *);
188 extern int wskbd_set_mixermute(long, long);
189 extern int wskbd_set_mixervolume(long, long);
190 #endif
191 
192 struct cfattach acpithinkpad_ca = {
193 	sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach,
194 	NULL, thinkpad_activate
195 };
196 
197 struct cfdriver acpithinkpad_cd = {
198 	NULL, "acpithinkpad", DV_DULL
199 };
200 
201 const char *acpithinkpad_hids[] = {
202 	"IBM0068",
203 	"LEN0068",
204 	"LEN0268",
205 	NULL
206 };
207 
208 int
209 thinkpad_match(struct device *parent, void *match, void *aux)
210 {
211 	struct acpi_attach_args	*aa = aux;
212 	struct cfdata *cf = match;
213 	int64_t	res;
214 
215 	if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name))
216 		return (0);
217 
218 	if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node,
219 	    "MHKV", 0, NULL, &res))
220 		return (0);
221 
222 	if (!(res == THINKPAD_HKEY_VERSION1 || res == THINKPAD_HKEY_VERSION2))
223 		return (0);
224 
225 	return (1);
226 }
227 
228 void
229 thinkpad_sensor_attach(struct acpithinkpad_softc *sc)
230 {
231 	int i;
232 
233 	if (sc->sc_acpi->sc_ec == NULL)
234 		return;
235 	sc->sc_ec = sc->sc_acpi->sc_ec;
236 
237 	/* Add temperature probes */
238 	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
239 	    sizeof(sc->sc_sensdev.xname));
240 	for (i=0; i<THINKPAD_NTEMPSENSORS; i++) {
241 		sc->sc_sens[i].type = SENSOR_TEMP;
242 		sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]);
243 	}
244 
245 	/* Add fan probe */
246 	sc->sc_sens[THINKPAD_SENSOR_FANRPM].type = SENSOR_FANRPM;
247 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_FANRPM]);
248 
249 	/* Add port replicator indicator */
250 	sc->sc_sens[THINKPAD_SENSOR_PORTREPL].type = SENSOR_INDICATOR;
251 	sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status = SENSOR_S_UNKNOWN;
252 	strlcpy(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc, "port replicator",
253 	        sizeof(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc));
254 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_PORTREPL]);
255 
256 	sensordev_install(&sc->sc_sensdev);
257 }
258 
259 void
260 thinkpad_sensor_refresh(void *arg)
261 {
262 	struct acpithinkpad_softc *sc = arg;
263 	uint8_t lo, hi, i;
264 	int64_t tmp;
265 	char sname[5];
266 
267 	/* Refresh sensor readings */
268 	for (i=0; i<THINKPAD_NTEMPSENSORS; i++) {
269 		snprintf(sname, sizeof(sname), "TMP%d", i);
270 		aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode,
271 		    sname, 0, 0, &tmp);
272 		sc->sc_sens[i].value = (tmp * 1000000) + 273150000;
273 		if (tmp > 127 || tmp < -127)
274 			sc->sc_sens[i].flags = SENSOR_FINVALID;
275 	}
276 
277 	/* Read fan RPM */
278 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo);
279 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi);
280 	sc->sc_sens[THINKPAD_SENSOR_FANRPM].value = ((hi << 8L) + lo);
281 }
282 
283 void
284 thinkpad_attach(struct device *parent, struct device *self, void *aux)
285 {
286 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
287 	struct acpi_attach_args	*aa = aux;
288 
289 	sc->sc_acpi = (struct acpi_softc *)parent;
290 	sc->sc_devnode = aa->aaa_node;
291 
292 	printf("\n");
293 
294 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKV", 0, NULL,
295 	    &sc->sc_hkey_version))
296 		sc->sc_hkey_version = THINKPAD_HKEY_VERSION1;
297 
298 #if NAUDIO > 0 && NWSKBD > 0
299 	/* Defer speaker mute */
300 	if (thinkpad_get_volume_mute(sc) == 1)
301 		startuphook_establish(thinkpad_attach_deferred, sc);
302 #endif
303 
304 	/* Set event mask to receive everything */
305 	thinkpad_enable_events(sc);
306 	thinkpad_sensor_attach(sc);
307 
308 	/* Check for ThinkLight or keyboard backlight */
309 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "KLCG",
310 	    0, NULL, &sc->sc_thinklight) == 0) {
311 		sc->sc_thinklight_get = "KLCG";
312 		sc->sc_thinklight_set = "KLCS";
313 		wskbd_get_backlight = thinkpad_get_kbd_backlight;
314 		wskbd_set_backlight = thinkpad_set_kbd_backlight;
315 	} else if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MLCG",
316 	    0, NULL, &sc->sc_thinklight) == 0) {
317 		sc->sc_thinklight_get = "MLCG";
318 		sc->sc_thinklight_set = "MLCS";
319 		wskbd_get_backlight = thinkpad_get_kbd_backlight;
320 		wskbd_set_backlight = thinkpad_set_kbd_backlight;
321 	}
322 
323 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG",
324 	    0, NULL, &sc->sc_brightness) == 0) {
325 		ws_get_param = thinkpad_get_param;
326 		ws_set_param = thinkpad_set_param;
327 	}
328 
329 	/* Run thinkpad_hotkey on button presses */
330 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
331 	    thinkpad_hotkey, sc, ACPIDEV_POLL);
332 }
333 
334 int
335 thinkpad_enable_events(struct acpithinkpad_softc *sc)
336 {
337 	struct aml_value arg, args[2];
338 	int64_t	mask;
339 	int i;
340 
341 	/* Get the default event mask */
342 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA",
343 	    0, NULL, &mask)) {
344 		printf("%s: no MHKA\n", DEVNAME(sc));
345 		return (1);
346 	}
347 
348 	/* Enable events we need to know about */
349 	mask |= (THINKPAD_MASK_MIC_MUTE	|
350 	    THINKPAD_MASK_BRIGHTNESS_UP |
351 	    THINKPAD_MASK_BRIGHTNESS_DOWN |
352 	    THINKPAD_MASK_KBD_BACKLIGHT);
353 
354 	DPRINTF(("%s: setting event mask to 0x%llx\n", DEVNAME(sc), mask));
355 
356 	/* Update hotkey mask */
357 	bzero(args, sizeof(args));
358 	args[0].type = args[1].type = AML_OBJTYPE_INTEGER;
359 	for (i = 0; i < 32; i++) {
360 		args[0].v_integer = i + 1;
361 		args[1].v_integer = (((1 << i) & mask) != 0);
362 
363 		if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM",
364 		    2, args, NULL)) {
365 			printf("%s: couldn't toggle MHKM\n", DEVNAME(sc));
366 			return (1);
367 		}
368 	}
369 
370 	/* Enable hotkeys */
371 	bzero(&arg, sizeof(arg));
372 	arg.type = AML_OBJTYPE_INTEGER;
373 	arg.v_integer = 1;
374 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC",
375 	    1, &arg, NULL)) {
376 		printf("%s: couldn't enable hotkeys\n", DEVNAME(sc));
377 		return (1);
378 	}
379 
380 	return (0);
381 }
382 
383 int
384 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg)
385 {
386 	struct acpithinkpad_softc *sc = arg;
387 	int64_t	event;
388 
389 	if (notify_type == 0x00) {
390 		/* Poll sensors */
391 		thinkpad_sensor_refresh(sc);
392 		return (0);
393 	}
394 
395 	if (notify_type != 0x80)
396 		return (1);
397 
398 	for (;;) {
399 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP",
400 		    0, NULL, &event))
401 			break;
402 
403 		DPRINTF(("%s: event 0x%03llx\n", DEVNAME(sc), event));
404 		if (event == 0)
405 			break;
406 
407 		switch (event) {
408 		case THINKPAD_BUTTON_BRIGHTNESS_UP:
409 			thinkpad_brightness_up(sc);
410 			break;
411 		case THINKPAD_BUTTON_BRIGHTNESS_DOWN:
412 			thinkpad_brightness_down(sc);
413 			break;
414 		case THINKPAD_BUTTON_WIRELESS:
415 			thinkpad_toggle_bluetooth(sc);
416 			break;
417 		case THINKPAD_BUTTON_SUSPEND:
418 #ifndef SMALL_KERNEL
419 			if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
420 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
421 				    sc->sc_acpi, ACPI_SLEEP_SUSPEND);
422 #endif
423 			break;
424 		case THINKPAD_BUTTON_VOLUME_MUTE:
425 			thinkpad_volume_mute(sc);
426 			break;
427 		case THINKPAD_BUTTON_VOLUME_DOWN:
428 			thinkpad_volume_down(sc);
429 			break;
430 		case THINKPAD_BUTTON_VOLUME_UP:
431 			thinkpad_volume_up(sc);
432 			break;
433 		case THINKPAD_BUTTON_MICROPHONE_MUTE:
434 #if NAUDIO > 0 && NWSKBD > 0
435 			wskbd_set_mixervolume(0, 0);
436 #endif
437 			break;
438 		case THINKPAD_BUTTON_HIBERNATE:
439 #if defined(HIBERNATE) && !defined(SMALL_KERNEL)
440 			if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
441 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
442 				    sc->sc_acpi, ACPI_SLEEP_HIBERNATE);
443 #endif
444 			break;
445 		case THINKPAD_BUTTON_THINKLIGHT:
446 			thinkpad_get_thinklight(sc);
447 			break;
448 		case THINKPAD_ADAPTIVE_NEXT:
449 		case THINKPAD_ADAPTIVE_QUICK:
450 			thinkpad_adaptive_change(sc);
451 			break;
452 		case THINKPAD_BACKLIGHT_CHANGED:
453 			thinkpad_get_brightness(sc);
454 			break;
455 		case THINKPAD_PORT_REPL_DOCKED:
456 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 1;
457 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
458 			    SENSOR_S_OK;
459 			break;
460 		case THINKPAD_PORT_REPL_UNDOCKED:
461 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 0;
462 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
463 			    SENSOR_S_OK;
464 			break;
465 		default:
466 			/* unknown or boring event */
467 			DPRINTF(("%s: unhandled event 0x%03llx\n", DEVNAME(sc),
468 			    event));
469 			break;
470 		}
471 	}
472 
473 	return (0);
474 }
475 
476 int
477 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc)
478 {
479 	struct aml_value arg;
480 	int64_t	bluetooth;
481 
482 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC",
483 	    0, NULL, &bluetooth))
484 		return (1);
485 
486 	if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT))
487 		return (1);
488 
489 	bzero(&arg, sizeof(arg));
490 	arg.type = AML_OBJTYPE_INTEGER;
491 	arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED;
492 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC",
493 	    1, &arg, NULL)) {
494 		printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc));
495 		return (1);
496 	}
497 
498 	return (0);
499 }
500 
501 int
502 thinkpad_toggle_wan(struct acpithinkpad_softc *sc)
503 {
504 	struct aml_value arg;
505 	int64_t wan;
506 
507 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN",
508 	    0, NULL, &wan))
509 		return (1);
510 
511 	if (!(wan & THINKPAD_WAN_PRESENT))
512 		return (1);
513 
514 	bzero(&arg, sizeof(arg));
515 	arg.type = AML_OBJTYPE_INTEGER;
516 	arg.v_integer = wan ^ THINKPAD_WAN_ENABLED;
517 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN",
518 	    1, &arg, NULL)) {
519 		printf("%s: couldn't toggle wan\n", DEVNAME(sc));
520 		return (1);
521 	}
522 
523 	return (0);
524 }
525 
526 int
527 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd)
528 {
529 	struct aml_value arg;
530 
531 	bzero(&arg, sizeof(arg));
532 	arg.type = AML_OBJTYPE_INTEGER;
533 	arg.v_integer = cmd;
534 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL);
535 	return (0);
536 }
537 
538 int
539 thinkpad_volume_down(struct acpithinkpad_softc *sc)
540 {
541 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN));
542 }
543 
544 int
545 thinkpad_volume_up(struct acpithinkpad_softc *sc)
546 {
547 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP));
548 }
549 
550 int
551 thinkpad_volume_mute(struct acpithinkpad_softc *sc)
552 {
553 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE));
554 }
555 
556 int
557 thinkpad_brightness_up(struct acpithinkpad_softc *sc)
558 {
559 	int b;
560 
561 	if (thinkpad_get_brightness(sc) == 0) {
562 		b = sc->sc_brightness & 0xff;
563 		if (b < ((sc->sc_brightness >> 8) & 0xff)) {
564 			sc->sc_brightness = b + 1;
565 			thinkpad_set_brightness(sc, 0);
566 		}
567 
568 		return (0);
569 	} else
570 		return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP));
571 }
572 
573 int
574 thinkpad_brightness_down(struct acpithinkpad_softc *sc)
575 {
576 	int b;
577 
578 	if (thinkpad_get_brightness(sc) == 0) {
579 		b = sc->sc_brightness & 0xff;
580 		if (b > 0) {
581 			sc->sc_brightness = b - 1;
582 			thinkpad_set_brightness(sc, 0);
583 		}
584 
585 		return (0);
586 	} else
587 		return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN));
588 }
589 
590 int
591 thinkpad_adaptive_change(struct acpithinkpad_softc *sc)
592 {
593 	struct aml_value arg;
594 	int64_t	mode;
595 
596 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
597 	    0, NULL, &mode)) {
598 		printf("%s: couldn't get adaptive keyboard mode\n", DEVNAME(sc));
599 		return (1);
600 	}
601 
602 	bzero(&arg, sizeof(arg));
603 	arg.type = AML_OBJTYPE_INTEGER;
604 
605 	if (mode == THINKPAD_ADAPTIVE_MODE_FUNCTION)
606 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_HOME;
607 	else
608 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_FUNCTION;
609 
610 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "STRW",
611 	    1, &arg, NULL)) {
612 		printf("%s: couldn't set adaptive keyboard mode\n", DEVNAME(sc));
613 		return (1);
614 	}
615 
616 	return (0);
617 }
618 
619 int
620 thinkpad_activate(struct device *self, int act)
621 {
622 
623 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
624 	int64_t res;
625 #if NAUDIO > 0 && NWSKBD > 0
626 	int mute;
627 #endif
628 
629 	switch (act) {
630 	case DVACT_WAKEUP:
631 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
632 		    0, NULL, &res) == 0)
633 			thinkpad_adaptive_change(sc);
634 #if NAUDIO > 0 && NWSKBD > 0
635 		mute = thinkpad_get_volume_mute(sc);
636 		if (mute != -1)
637 			wskbd_set_mixermute(mute, 1);
638 #endif
639 		break;
640 	}
641 	return (0);
642 }
643 
644 void
645 thinkpad_get_thinklight(struct acpithinkpad_softc *sc)
646 {
647 	if (sc->sc_thinklight_get)
648 		aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
649 		    sc->sc_thinklight_get, 0, NULL, &sc->sc_thinklight);
650 }
651 
652 void
653 thinkpad_set_thinklight(void *arg0, int arg1)
654 {
655 	struct acpithinkpad_softc *sc = arg0;
656 	struct aml_value arg;
657 
658 	memset(&arg, 0, sizeof(arg));
659 	arg.type = AML_OBJTYPE_INTEGER;
660 	arg.v_integer = sc->sc_thinklight & 0x0f;
661 	aml_evalname(sc->sc_acpi, sc->sc_devnode,
662 	    sc->sc_thinklight_set, 1, &arg, NULL);
663 }
664 
665 int
666 thinkpad_get_kbd_backlight(struct wskbd_backlight *kbl)
667 {
668 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
669 
670 	KASSERT(sc != NULL);
671 
672 	kbl->min = 0;
673 	kbl->max = (sc->sc_thinklight >> 8) & 0x0f;
674 	kbl->curval = sc->sc_thinklight & 0x0f;
675 
676 	if (kbl->max == 0)
677 		return (ENOTTY);
678 
679 	return 0;
680 }
681 
682 int
683 thinkpad_set_kbd_backlight(struct wskbd_backlight *kbl)
684 {
685 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
686 	int maxval;
687 
688 	KASSERT(sc != NULL);
689 
690 	maxval = (sc->sc_thinklight >> 8) & 0x0f;
691 
692 	if (maxval == 0)
693 		return (ENOTTY);
694 
695 	if (kbl->curval > maxval)
696 		return EINVAL;
697 
698 	sc->sc_thinklight &= ~0xff;
699 	sc->sc_thinklight |= kbl->curval;
700 	acpi_addtask(sc->sc_acpi, thinkpad_set_thinklight, sc, 0);
701 	acpi_wakeup(sc->sc_acpi);
702 	return 0;
703 }
704 
705 int
706 thinkpad_get_brightness(struct acpithinkpad_softc *sc)
707 {
708 	int ret;
709 
710 	ret = aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG", 0, NULL,
711 	    &sc->sc_brightness);
712 
713 	DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
714 
715 	return ret;
716 }
717 
718 int
719 thinkpad_set_brightness(void *arg0, int arg1)
720 {
721 	struct acpithinkpad_softc *sc = arg0;
722 	struct aml_value arg;
723 	int ret;
724 
725 	DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
726 
727 	memset(&arg, 0, sizeof(arg));
728 	arg.type = AML_OBJTYPE_INTEGER;
729 	arg.v_integer = sc->sc_brightness & 0xff;
730 	ret = aml_evalname(sc->sc_acpi, sc->sc_devnode, "PBLS", 1, &arg, NULL);
731 
732 	if (ret)
733 		return ret;
734 
735 	thinkpad_get_brightness(sc);
736 
737 	return 0;
738 }
739 
740 int
741 thinkpad_get_param(struct wsdisplay_param *dp)
742 {
743 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
744 
745 	if (sc == NULL)
746 		return -1;
747 
748 	switch (dp->param) {
749 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
750 		dp->min = 0;
751 		dp->max = (sc->sc_brightness >> 8) & 0xff;
752 		dp->curval = sc->sc_brightness & 0xff;
753 		return 0;
754 	default:
755 		return -1;
756 	}
757 }
758 
759 int
760 thinkpad_set_param(struct wsdisplay_param *dp)
761 {
762 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
763 	int maxval;
764 
765 	if (sc == NULL)
766 		return -1;
767 
768 	maxval = (sc->sc_brightness >> 8) & 0xff;
769 
770 	switch (dp->param) {
771 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
772 		if (dp->curval < 0)
773 			dp->curval = 0;
774 		if (dp->curval > maxval)
775 			dp->curval = maxval;
776 		sc->sc_brightness &= ~0xff;
777 		sc->sc_brightness |= dp->curval;
778 		acpi_addtask(sc->sc_acpi, (void *)thinkpad_set_brightness, sc,
779 		    0);
780 		acpi_wakeup(sc->sc_acpi);
781 		return 0;
782 	default:
783 		return -1;
784 	}
785 }
786 
787 #if NAUDIO > 0 && NWSKBD > 0
788 void
789 thinkpad_attach_deferred(void *v __unused)
790 {
791 	wskbd_set_mixermute(1, 1);
792 }
793 
794 int
795 thinkpad_get_volume_mute(struct acpithinkpad_softc *sc)
796 {
797 	uint8_t vol = 0;
798 
799 	if (sc->sc_acpi->sc_ec == NULL)
800 		return (-1);
801 
802 	acpiec_read(sc->sc_acpi->sc_ec, THINKPAD_ECOFFSET_VOLUME, 1, &vol);
803 	return ((vol & THINKPAD_ECOFFSET_VOLUME_MUTE_MASK) ==
804 	    THINKPAD_ECOFFSET_VOLUME_MUTE_MASK);
805 }
806 #endif
807