xref: /openbsd-src/sys/dev/acpi/acpithinkpad.c (revision 50b7afb2c2c0993b0894d4e34bf857cb13ed9c80)
1 /*	$OpenBSD: acpithinkpad.c,v 1.36 2014/07/08 21:32:15 miod 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 #include <sys/proc.h>
21 
22 #include <dev/acpi/acpireg.h>
23 #include <dev/acpi/acpivar.h>
24 #include <dev/acpi/acpidev.h>
25 #include <dev/acpi/amltypes.h>
26 #include <dev/acpi/dsdt.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_BRIGHTNESS_UP	0x1010
59 #define	THINKPAD_BUTTON_BRIGHTNESS_DOWN	0x1011
60 #define	THINKPAD_BUTTON_THINKLIGHT	0x1012
61 #define	THINKPAD_BUTTON_FN_SPACE	0x1014
62 #define	THINKPAD_BUTTON_VOLUME_UP	0x1015
63 #define	THINKPAD_BUTTON_VOLUME_DOWN	0x1016
64 #define	THINKPAD_BUTTON_VOLUME_MUTE	0x1017
65 #define	THINKPAD_BUTTON_THINKVANTAGE	0x1018
66 #define	THINKPAD_BUTTON_BLACK		0x101a
67 #define	THINKPAD_BUTTON_MICROPHONE_MUTE	0x101b
68 #define	THINKPAD_BUTTON_FN_F11		0x100b
69 #define	THINKPAD_BUTTON_HIBERNATE	0x100c
70 #define	THINKPAD_ADAPTIVE_NEXT		0x1101
71 #define	THINKPAD_ADAPTIVE_QUICK		0x1102
72 #define	THINKPAD_ADAPTIVE_SNIP		0x1105
73 #define	THINKPAD_ADAPTIVE_VOICE		0x1108
74 #define	THINKPAD_ADAPTIVE_GESTURES	0x110a
75 #define	THINKPAD_ADAPTIVE_SETTINGS	0x110e
76 #define	THINKPAD_ADAPTIVE_TAB		0x110f
77 #define	THINKPAD_ADAPTIVE_REFRESH	0x1110
78 #define	THINKPAD_ADAPTIVE_BACK		0x1111
79 #define THINKPAD_PORT_REPL_DOCKED	0x4010
80 #define THINKPAD_PORT_REPL_UNDOCKED	0x4011
81 #define	THINKPAD_LID_OPEN		0x5001
82 #define	THINKPAD_LID_CLOSED		0x5002
83 #define	THINKPAD_TABLET_SCREEN_NORMAL	0x500a
84 #define	THINKPAD_TABLET_SCREEN_ROTATED	0x5009
85 #define	THINKPAD_BRIGHTNESS_CHANGED	0x5010
86 #define	THINKPAD_TABLET_PEN_INSERTED	0x500b
87 #define	THINKPAD_TABLET_PEN_REMOVED	0x500c
88 #define	THINKPAD_THERMAL_TABLE_CHANGED	0x6030
89 #define	THINKPAD_POWER_CHANGED		0x6040
90 #define	THINKPAD_BACKLIGHT_CHANGED	0x6050
91 #define	THINKPAD_SWITCH_WIRELESS	0x7000
92 
93 #define THINKPAD_NSENSORS 9
94 #define THINKPAD_NTEMPSENSORS 8
95 
96 #define THINKPAD_ECOFFSET_FANLO		0x84
97 #define THINKPAD_ECOFFSET_FANHI		0x85
98 
99 #define	THINKPAD_ADAPTIVE_MODE_HOME	1
100 #define	THINKPAD_ADAPTIVE_MODE_FUNCTION	3
101 
102 struct acpithinkpad_softc {
103 	struct device		 sc_dev;
104 
105 	struct acpiec_softc     *sc_ec;
106 	struct acpi_softc	*sc_acpi;
107 	struct aml_node		*sc_devnode;
108 
109 	struct ksensor		 sc_sens[THINKPAD_NSENSORS];
110 	struct ksensordev	 sc_sensdev;
111 };
112 
113 extern void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *);
114 
115 int	thinkpad_match(struct device *, void *, void *);
116 void	thinkpad_attach(struct device *, struct device *, void *);
117 int	thinkpad_hotkey(struct aml_node *, int, void *);
118 int	thinkpad_enable_events(struct acpithinkpad_softc *);
119 int	thinkpad_toggle_bluetooth(struct acpithinkpad_softc *);
120 int	thinkpad_toggle_wan(struct acpithinkpad_softc *);
121 int	thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t);
122 int	thinkpad_volume_down(struct acpithinkpad_softc *);
123 int	thinkpad_volume_up(struct acpithinkpad_softc *);
124 int	thinkpad_volume_mute(struct acpithinkpad_softc *);
125 int	thinkpad_brightness_up(struct acpithinkpad_softc *);
126 int	thinkpad_brightness_down(struct acpithinkpad_softc *);
127 int	thinkpad_adaptive_change(struct acpithinkpad_softc *);
128 int	thinkpad_activate(struct device *, int);
129 
130 void    thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
131 void    thinkpad_sensor_refresh(void *);
132 
133 #if NAUDIO > 0 && NWSKBD > 0
134 extern int wskbd_set_mixervolume(long, long);
135 #endif
136 
137 struct cfattach acpithinkpad_ca = {
138 	sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach,
139 	NULL, thinkpad_activate
140 };
141 
142 struct cfdriver acpithinkpad_cd = {
143 	NULL, "acpithinkpad", DV_DULL
144 };
145 
146 const char *acpithinkpad_hids[] = {
147 	ACPI_DEV_IBM, ACPI_DEV_LENOVO, 0
148 };
149 
150 int
151 thinkpad_match(struct device *parent, void *match, void *aux)
152 {
153 	struct acpi_attach_args	*aa = aux;
154 	struct cfdata *cf = match;
155 	int64_t	res;
156 
157 	if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name))
158 		return (0);
159 
160 	if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node,
161 	    "MHKV", 0, NULL, &res))
162 		return (0);
163 
164 	if (!(res == THINKPAD_HKEY_VERSION1 || res == THINKPAD_HKEY_VERSION2))
165 		return (0);
166 
167 	return (1);
168 }
169 
170 void
171 thinkpad_sensor_attach(struct acpithinkpad_softc *sc)
172 {
173 	int i;
174 
175 	if (sc->sc_acpi->sc_ec == NULL)
176 		return;
177 	sc->sc_ec = sc->sc_acpi->sc_ec;
178 
179 	/* Add temperature probes */
180 	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
181 	    sizeof(sc->sc_sensdev.xname));
182 	for (i=0; i<THINKPAD_NTEMPSENSORS; i++) {
183 		sc->sc_sens[i].type = SENSOR_TEMP;
184 		sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]);
185 	}
186 
187 	/* Add fan probe */
188 	sc->sc_sens[i].type = SENSOR_FANRPM;
189 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]);
190 
191 	sensordev_install(&sc->sc_sensdev);
192 }
193 
194 void
195 thinkpad_sensor_refresh(void *arg)
196 {
197 	struct acpithinkpad_softc *sc = arg;
198 	u_int8_t lo, hi, i;
199 	int64_t tmp;
200 	char sname[5];
201 
202 	/* Refresh sensor readings */
203 	for (i=0; i<THINKPAD_NTEMPSENSORS; i++) {
204 		snprintf(sname, sizeof(sname), "TMP%d", i);
205 		aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode,
206 		    sname, 0, 0, &tmp);
207 		sc->sc_sens[i].value = (tmp * 1000000) + 273150000;
208 		if (tmp > 127 || tmp < -127)
209 			sc->sc_sens[i].flags = SENSOR_FINVALID;
210 	}
211 
212 	/* Read fan RPM */
213 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo);
214 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi);
215 	sc->sc_sens[i].value = ((hi << 8L) + lo);
216 }
217 
218 void
219 thinkpad_attach(struct device *parent, struct device *self, void *aux)
220 {
221 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
222 	struct acpi_attach_args	*aa = aux;
223 
224 	sc->sc_acpi = (struct acpi_softc *)parent;
225 	sc->sc_devnode = aa->aaa_node;
226 
227 	printf("\n");
228 
229 	/* Set event mask to receive everything */
230 	thinkpad_enable_events(sc);
231 	thinkpad_sensor_attach(sc);
232 
233 	/* Run thinkpad_hotkey on button presses */
234 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
235 	    thinkpad_hotkey, sc, ACPIDEV_POLL);
236 }
237 
238 int
239 thinkpad_enable_events(struct acpithinkpad_softc *sc)
240 {
241 	struct aml_value arg, args[2];
242 	int64_t	mask;
243 	int i;
244 
245 	/* Get the supported event mask */
246 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA",
247 	    0, NULL, &mask)) {
248 		printf("%s: no MHKA\n", DEVNAME(sc));
249 		return (1);
250 	}
251 
252 	/* Update hotkey mask */
253 	bzero(args, sizeof(args));
254 	args[0].type = args[1].type = AML_OBJTYPE_INTEGER;
255 	for (i = 0; i < 32; i++) {
256 		args[0].v_integer = i + 1;
257 		args[1].v_integer = (((1 << i) & mask) != 0);
258 
259 		if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM",
260 		    2, args, NULL)) {
261 			printf("%s: couldn't toggle MHKM\n", DEVNAME(sc));
262 			return (1);
263 		}
264 	}
265 
266 	/* Enable hotkeys */
267 	bzero(&arg, sizeof(arg));
268 	arg.type = AML_OBJTYPE_INTEGER;
269 	arg.v_integer = 1;
270 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC",
271 	    1, &arg, NULL)) {
272 		printf("%s: couldn't enable hotkeys\n", DEVNAME(sc));
273 		return (1);
274 	}
275 
276 	return (0);
277 }
278 
279 int
280 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg)
281 {
282 	struct acpithinkpad_softc *sc = arg;
283 	int handled = 0;
284 	int64_t	event;
285 
286 	if (notify_type == 0x00) {
287 		/* Poll sensors */
288 		thinkpad_sensor_refresh(sc);
289 		return (0);
290 	}
291 
292 	if (notify_type != 0x80)
293 		return (1);
294 
295 	for (;;) {
296 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP",
297 		    0, NULL, &event))
298 			break;
299 		if (event == 0)
300 			break;
301 
302 		switch (event) {
303 		case THINKPAD_BUTTON_BRIGHTNESS_UP:
304 			thinkpad_brightness_up(sc);
305 			handled = 1;
306 			break;
307 		case THINKPAD_BUTTON_BRIGHTNESS_DOWN:
308 			thinkpad_brightness_down(sc);
309 			handled = 1;
310 			break;
311 		case THINKPAD_BUTTON_WIRELESS:
312 			thinkpad_toggle_bluetooth(sc);
313 			handled = 1;
314 			break;
315 		case THINKPAD_BUTTON_SUSPEND:
316 #ifndef SMALL_KERNEL
317 			if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
318 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
319 				    sc->sc_acpi, ACPI_STATE_S3);
320 #endif
321 			handled = 1;
322 			break;
323 		case THINKPAD_BUTTON_VOLUME_MUTE:
324 			thinkpad_volume_mute(sc);
325 			handled = 1;
326 			break;
327 		case THINKPAD_BUTTON_VOLUME_DOWN:
328 			thinkpad_volume_down(sc);
329 			handled = 1;
330 			break;
331 		case THINKPAD_BUTTON_VOLUME_UP:
332 			thinkpad_volume_up(sc);
333 			handled = 1;
334 			break;
335 		case THINKPAD_BUTTON_MICROPHONE_MUTE:
336 #if NAUDIO > 0 && NWSKBD > 0
337 			wskbd_set_mixervolume(0, 0);
338 #endif
339 			handled = 1;
340 			break;
341 		case THINKPAD_BUTTON_HIBERNATE:
342 #ifndef SMALL_KERNEL
343 			acpi_addtask(sc->sc_acpi, acpi_sleep_task,
344 			    sc->sc_acpi, ACPI_STATE_S4);
345 #endif
346 			handled = 1;
347 			break;
348 		case THINKPAD_ADAPTIVE_NEXT:
349 		case THINKPAD_ADAPTIVE_QUICK:
350 			thinkpad_adaptive_change(sc);
351 			handled = 1;
352 			break;
353 		case THINKPAD_ADAPTIVE_BACK:
354 		case THINKPAD_ADAPTIVE_GESTURES:
355 		case THINKPAD_ADAPTIVE_REFRESH:
356 		case THINKPAD_ADAPTIVE_SETTINGS:
357 		case THINKPAD_ADAPTIVE_SNIP:
358 		case THINKPAD_ADAPTIVE_TAB:
359 		case THINKPAD_ADAPTIVE_VOICE:
360 		case THINKPAD_BACKLIGHT_CHANGED:
361 		case THINKPAD_BRIGHTNESS_CHANGED:
362 		case THINKPAD_BUTTON_BATTERY_INFO:
363 		case THINKPAD_BUTTON_EJECT:
364 		case THINKPAD_BUTTON_EXTERNAL_SCREEN:
365 		case THINKPAD_BUTTON_FN_F11:
366 		case THINKPAD_BUTTON_FN_F1:
367 		case THINKPAD_BUTTON_FN_F6:
368 		case THINKPAD_BUTTON_FN_SPACE:
369 		case THINKPAD_BUTTON_LOCK_SCREEN:
370 		case THINKPAD_BUTTON_POINTER_SWITCH:
371 		case THINKPAD_BUTTON_THINKLIGHT:
372 		case THINKPAD_BUTTON_THINKVANTAGE:
373 		case THINKPAD_BUTTON_BLACK:
374 		case THINKPAD_LID_CLOSED:
375 		case THINKPAD_LID_OPEN:
376 		case THINKPAD_PORT_REPL_DOCKED:
377 		case THINKPAD_PORT_REPL_UNDOCKED:
378 		case THINKPAD_POWER_CHANGED:
379 		case THINKPAD_SWITCH_WIRELESS:
380 		case THINKPAD_TABLET_PEN_INSERTED:
381 		case THINKPAD_TABLET_PEN_REMOVED:
382 		case THINKPAD_TABLET_SCREEN_NORMAL:
383 		case THINKPAD_TABLET_SCREEN_ROTATED:
384 		case THINKPAD_THERMAL_TABLE_CHANGED:
385 			handled = 1;
386 			break;
387 		default:
388 			printf("%s: unknown event 0x%03llx\n",
389 			    DEVNAME(sc), event);
390 		}
391 	}
392 
393 	return (handled);
394 }
395 
396 int
397 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc)
398 {
399 	struct aml_value arg;
400 	int64_t	bluetooth;
401 
402 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC",
403 	    0, NULL, &bluetooth))
404 		return (1);
405 
406 	if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT))
407 		return (1);
408 
409 	bzero(&arg, sizeof(arg));
410 	arg.type = AML_OBJTYPE_INTEGER;
411 	arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED;
412 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC",
413 	    1, &arg, NULL)) {
414 		printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc));
415 		return (1);
416 	}
417 
418 	return (0);
419 }
420 
421 int
422 thinkpad_toggle_wan(struct acpithinkpad_softc *sc)
423 {
424 	struct aml_value arg;
425 	int64_t wan;
426 
427 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN",
428 	    0, NULL, &wan))
429 		return (1);
430 
431 	if (!(wan & THINKPAD_WAN_PRESENT))
432 		return (1);
433 
434 	bzero(&arg, sizeof(arg));
435 	arg.type = AML_OBJTYPE_INTEGER;
436 	arg.v_integer = wan ^ THINKPAD_WAN_ENABLED;
437 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN",
438 	    1, &arg, NULL)) {
439 		printf("%s: couldn't toggle wan\n", DEVNAME(sc));
440 		return (1);
441 	}
442 
443 	return (0);
444 }
445 
446 int
447 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd)
448 {
449 	struct aml_value arg;
450 
451 	bzero(&arg, sizeof(arg));
452 	arg.type = AML_OBJTYPE_INTEGER;
453 	arg.v_integer = cmd;
454 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL);
455 	return (0);
456 }
457 
458 int
459 thinkpad_volume_down(struct acpithinkpad_softc *sc)
460 {
461 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN));
462 }
463 
464 int
465 thinkpad_volume_up(struct acpithinkpad_softc *sc)
466 {
467 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP));
468 }
469 
470 int
471 thinkpad_volume_mute(struct acpithinkpad_softc *sc)
472 {
473 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE));
474 }
475 
476 int
477 thinkpad_brightness_up(struct acpithinkpad_softc *sc)
478 {
479 	return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP));
480 }
481 
482 int
483 thinkpad_brightness_down(struct acpithinkpad_softc *sc)
484 {
485 	return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN));
486 }
487 
488 int
489 thinkpad_adaptive_change(struct acpithinkpad_softc *sc)
490 {
491 	struct aml_value arg;
492 	int64_t	mode;
493 
494 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
495 	    0, NULL, &mode)) {
496 		printf("%s: couldn't get adaptive keyboard mode\n", DEVNAME(sc));
497 		return (1);
498 	}
499 
500 	bzero(&arg, sizeof(arg));
501 	arg.type = AML_OBJTYPE_INTEGER;
502 
503 	if (mode == THINKPAD_ADAPTIVE_MODE_FUNCTION)
504 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_HOME;
505 	else
506 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_FUNCTION;
507 
508 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "STRW",
509 	    1, &arg, NULL)) {
510 		printf("%s: couldn't set adaptive keyboard mode\n", DEVNAME(sc));
511 		return (1);
512 	}
513 
514 	return (0);
515 }
516 
517 int
518 thinkpad_activate(struct device *self, int act)
519 {
520 
521 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
522 	int64_t res;
523 
524 	switch(act) {
525 	case DVACT_WAKEUP:
526 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
527 		    0, NULL, &res) == 0)
528 			thinkpad_adaptive_change(sc);
529 		break;
530 	}
531 	return (0);
532 }
533