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