1 /* $OpenBSD: acpithinkpad.c,v 1.68 2019/12/31 01:38:33 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 #include <sys/sensors.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 #include <dev/wscons/wsconsio.h> 28 #include <dev/wscons/wsdisplayvar.h> 29 30 #include <machine/apmvar.h> 31 32 #include "audio.h" 33 #include "wskbd.h" 34 35 /* #define ACPITHINKPAD_DEBUG */ 36 37 #ifdef ACPITHINKPAD_DEBUG 38 #define DPRINTF(x) printf x 39 #else 40 #define DPRINTF(x) 41 #endif 42 43 #define THINKPAD_HKEY_VERSION1 0x0100 44 #define THINKPAD_HKEY_VERSION2 0x0200 45 46 #define THINKPAD_CMOS_VOLUME_DOWN 0x00 47 #define THINKPAD_CMOS_VOLUME_UP 0x01 48 #define THINKPAD_CMOS_VOLUME_MUTE 0x02 49 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 50 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 51 52 #define THINKPAD_BLUETOOTH_PRESENT 0x01 53 #define THINKPAD_BLUETOOTH_ENABLED 0x02 54 55 /* wan (not wifi) card */ 56 #define THINKPAD_WAN_PRESENT 0x01 57 #define THINKPAD_WAN_ENABLED 0x02 58 59 #define THINKPAD_BUTTON_FN_F1 0x1001 60 #define THINKPAD_BUTTON_LOCK_SCREEN 0x1002 61 #define THINKPAD_BUTTON_BATTERY_INFO 0x1003 62 #define THINKPAD_BUTTON_SUSPEND 0x1004 63 #define THINKPAD_BUTTON_WIRELESS 0x1005 64 #define THINKPAD_BUTTON_FN_F6 0x1006 65 #define THINKPAD_BUTTON_EXTERNAL_SCREEN 0x1007 66 #define THINKPAD_BUTTON_POINTER_SWITCH 0x1008 67 #define THINKPAD_BUTTON_EJECT 0x1009 68 #define THINKPAD_BUTTON_FN_F11 0x100b 69 #define THINKPAD_BUTTON_HIBERNATE 0x100c 70 #define THINKPAD_BUTTON_BRIGHTNESS_UP 0x1010 71 #define THINKPAD_BUTTON_BRIGHTNESS_DOWN 0x1011 72 #define THINKPAD_BUTTON_THINKLIGHT 0x1012 73 #define THINKPAD_BUTTON_FN_SPACE 0x1014 74 #define THINKPAD_BUTTON_VOLUME_UP 0x1015 75 #define THINKPAD_BUTTON_VOLUME_DOWN 0x1016 76 #define THINKPAD_BUTTON_VOLUME_MUTE 0x1017 77 #define THINKPAD_BUTTON_THINKVANTAGE 0x1018 78 #define THINKPAD_BUTTON_BLACK 0x101a 79 #define THINKPAD_BUTTON_MICROPHONE_MUTE 0x101b 80 #define THINKPAD_KEYLIGHT_CHANGED 0x101c 81 #define THINKPAD_BUTTON_CONFIG 0x101d 82 #define THINKPAD_BUTTON_FIND 0x101e 83 #define THINKPAD_BUTTON_ALL_ACTIVEPROGS 0x101f 84 #define THINKPAD_BUTTON_ALL_PROGS 0x1020 85 86 #define THINKPAD_ADAPTIVE_NEXT 0x1101 87 #define THINKPAD_ADAPTIVE_QUICK 0x1102 88 #define THINKPAD_ADAPTIVE_SNIP 0x1105 89 #define THINKPAD_ADAPTIVE_VOICE 0x1108 90 #define THINKPAD_ADAPTIVE_GESTURES 0x110a 91 #define THINKPAD_ADAPTIVE_SETTINGS 0x110e 92 #define THINKPAD_ADAPTIVE_TAB 0x110f 93 #define THINKPAD_ADAPTIVE_REFRESH 0x1110 94 #define THINKPAD_ADAPTIVE_BACK 0x1111 95 #define THINKPAD_PORT_REPL_DOCKED 0x4010 96 #define THINKPAD_PORT_REPL_UNDOCKED 0x4011 97 #define THINKPAD_TABLET_DOCKED 0x4012 98 #define THINKPAD_TABLET_UNDOCKED 0x4013 99 #define THINKPAD_LID_OPEN 0x5001 100 #define THINKPAD_LID_CLOSED 0x5002 101 #define THINKPAD_TABLET_SCREEN_NORMAL 0x500a 102 #define THINKPAD_TABLET_SCREEN_ROTATED 0x5009 103 #define THINKPAD_BRIGHTNESS_CHANGED 0x5010 104 #define THINKPAD_TABLET_PEN_INSERTED 0x500b 105 #define THINKPAD_TABLET_PEN_REMOVED 0x500c 106 #define THINKPAD_SWITCH_NUMLOCK 0x6000 107 #define THINKPAD_BUTTON_ROTATION_LOCK 0x6020 108 #define THINKPAD_THERMAL_TABLE_CHANGED 0x6030 109 #define THINKPAD_POWER_CHANGED 0x6040 110 #define THINKPAD_BACKLIGHT_CHANGED 0x6050 111 #define THINKPAD_BUTTON_FN_TOGGLE 0x6060 112 #define THINKPAD_TABLET_SCREEN_CHANGED 0x60c0 113 #define THINKPAD_SWITCH_WIRELESS 0x7000 114 115 #define THINKPAD_NSENSORS 10 116 #define THINKPAD_NTEMPSENSORS 8 117 118 #define THINKPAD_SENSOR_FANRPM THINKPAD_NTEMPSENSORS 119 #define THINKPAD_SENSOR_PORTREPL THINKPAD_NTEMPSENSORS + 1 120 121 #define THINKPAD_ECOFFSET_VOLUME 0x30 122 #define THINKPAD_ECOFFSET_VOLUME_MUTE_MASK 0x40 123 #define THINKPAD_ECOFFSET_FANLO 0x84 124 #define THINKPAD_ECOFFSET_FANHI 0x85 125 126 #define THINKPAD_ADAPTIVE_MODE_HOME 1 127 #define THINKPAD_ADAPTIVE_MODE_FUNCTION 3 128 129 #define THINKPAD_MASK_MIC_MUTE (1 << 14) 130 #define THINKPAD_MASK_BRIGHTNESS_UP (1 << 15) 131 #define THINKPAD_MASK_BRIGHTNESS_DOWN (1 << 16) 132 #define THINKPAD_MASK_KBD_BACKLIGHT (1 << 17) 133 134 struct acpithinkpad_softc { 135 struct device sc_dev; 136 137 struct acpiec_softc *sc_ec; 138 struct acpi_softc *sc_acpi; 139 struct aml_node *sc_devnode; 140 141 struct ksensor sc_sens[THINKPAD_NSENSORS]; 142 struct ksensordev sc_sensdev; 143 144 uint64_t sc_hkey_version; 145 146 uint64_t sc_thinklight; 147 const char *sc_thinklight_get; 148 const char *sc_thinklight_set; 149 150 uint64_t sc_brightness; 151 }; 152 153 extern void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *); 154 155 int thinkpad_match(struct device *, void *, void *); 156 void thinkpad_attach(struct device *, struct device *, void *); 157 int thinkpad_hotkey(struct aml_node *, int, void *); 158 int thinkpad_enable_events(struct acpithinkpad_softc *); 159 int thinkpad_toggle_bluetooth(struct acpithinkpad_softc *); 160 int thinkpad_toggle_wan(struct acpithinkpad_softc *); 161 int thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t); 162 int thinkpad_volume_down(struct acpithinkpad_softc *); 163 int thinkpad_volume_up(struct acpithinkpad_softc *); 164 int thinkpad_volume_mute(struct acpithinkpad_softc *); 165 int thinkpad_brightness_up(struct acpithinkpad_softc *); 166 int thinkpad_brightness_down(struct acpithinkpad_softc *); 167 int thinkpad_adaptive_change(struct acpithinkpad_softc *); 168 int thinkpad_activate(struct device *, int); 169 170 /* wscons hook functions */ 171 void thinkpad_get_thinklight(struct acpithinkpad_softc *); 172 void thinkpad_set_thinklight(void *, int); 173 int thinkpad_get_kbd_backlight(struct wskbd_backlight *); 174 int thinkpad_set_kbd_backlight(struct wskbd_backlight *); 175 extern int (*wskbd_get_backlight)(struct wskbd_backlight *); 176 extern int (*wskbd_set_backlight)(struct wskbd_backlight *); 177 int thinkpad_get_brightness(struct acpithinkpad_softc *); 178 int thinkpad_set_brightness(void *, int); 179 int thinkpad_get_param(struct wsdisplay_param *); 180 int thinkpad_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 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKV", 0, NULL, 293 &sc->sc_hkey_version)) 294 sc->sc_hkey_version = THINKPAD_HKEY_VERSION1; 295 296 printf(": version %lld.%lld\n", sc->sc_hkey_version >> 8, 297 sc->sc_hkey_version & 0xff); 298 299 #if NAUDIO > 0 && NWSKBD > 0 300 /* Defer speaker mute */ 301 if (thinkpad_get_volume_mute(sc) == 1) 302 startuphook_establish(thinkpad_attach_deferred, sc); 303 #endif 304 305 /* Set event mask to receive everything */ 306 thinkpad_enable_events(sc); 307 thinkpad_sensor_attach(sc); 308 309 /* Check for ThinkLight or keyboard backlight */ 310 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "KLCG", 311 0, NULL, &sc->sc_thinklight) == 0) { 312 sc->sc_thinklight_get = "KLCG"; 313 sc->sc_thinklight_set = "KLCS"; 314 wskbd_get_backlight = thinkpad_get_kbd_backlight; 315 wskbd_set_backlight = thinkpad_set_kbd_backlight; 316 } else if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MLCG", 317 0, NULL, &sc->sc_thinklight) == 0) { 318 sc->sc_thinklight_get = "MLCG"; 319 sc->sc_thinklight_set = "MLCS"; 320 wskbd_get_backlight = thinkpad_get_kbd_backlight; 321 wskbd_set_backlight = thinkpad_set_kbd_backlight; 322 } 323 324 /* On version 2 and newer, let *drm or acpivout control brightness */ 325 if (sc->sc_hkey_version == THINKPAD_HKEY_VERSION1 && 326 (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG", 327 0, NULL, &sc->sc_brightness) == 0)) { 328 ws_get_param = thinkpad_get_param; 329 ws_set_param = thinkpad_set_param; 330 } 331 332 /* Run thinkpad_hotkey on button presses */ 333 aml_register_notify(sc->sc_devnode, aa->aaa_dev, 334 thinkpad_hotkey, sc, ACPIDEV_POLL); 335 } 336 337 int 338 thinkpad_enable_events(struct acpithinkpad_softc *sc) 339 { 340 struct aml_value arg, args[2]; 341 int64_t mask; 342 int i; 343 344 /* Get the default event mask */ 345 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA", 346 0, NULL, &mask)) { 347 printf("%s: no MHKA\n", DEVNAME(sc)); 348 return (1); 349 } 350 351 /* Enable events we need to know about */ 352 mask |= (THINKPAD_MASK_MIC_MUTE | 353 THINKPAD_MASK_BRIGHTNESS_UP | 354 THINKPAD_MASK_BRIGHTNESS_DOWN | 355 THINKPAD_MASK_KBD_BACKLIGHT); 356 357 DPRINTF(("%s: setting event mask to 0x%llx\n", DEVNAME(sc), mask)); 358 359 /* Update hotkey mask */ 360 bzero(args, sizeof(args)); 361 args[0].type = args[1].type = AML_OBJTYPE_INTEGER; 362 for (i = 0; i < 32; i++) { 363 args[0].v_integer = i + 1; 364 args[1].v_integer = (((1 << i) & mask) != 0); 365 366 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM", 367 2, args, NULL)) { 368 printf("%s: couldn't toggle MHKM\n", DEVNAME(sc)); 369 return (1); 370 } 371 } 372 373 /* Enable hotkeys */ 374 bzero(&arg, sizeof(arg)); 375 arg.type = AML_OBJTYPE_INTEGER; 376 arg.v_integer = 1; 377 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC", 378 1, &arg, NULL)) { 379 printf("%s: couldn't enable hotkeys\n", DEVNAME(sc)); 380 return (1); 381 } 382 383 return (0); 384 } 385 386 int 387 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg) 388 { 389 struct acpithinkpad_softc *sc = arg; 390 int64_t event; 391 392 if (notify_type == 0x00) { 393 /* Poll sensors */ 394 thinkpad_sensor_refresh(sc); 395 return (0); 396 } 397 398 if (notify_type != 0x80) 399 return (1); 400 401 for (;;) { 402 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP", 403 0, NULL, &event)) 404 break; 405 406 DPRINTF(("%s: event 0x%03llx\n", DEVNAME(sc), event)); 407 if (event == 0) 408 break; 409 410 switch (event) { 411 case THINKPAD_BUTTON_BRIGHTNESS_UP: 412 thinkpad_brightness_up(sc); 413 break; 414 case THINKPAD_BUTTON_BRIGHTNESS_DOWN: 415 thinkpad_brightness_down(sc); 416 break; 417 case THINKPAD_BUTTON_WIRELESS: 418 thinkpad_toggle_bluetooth(sc); 419 break; 420 case THINKPAD_BUTTON_SUSPEND: 421 #ifndef SMALL_KERNEL 422 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) 423 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 424 sc->sc_acpi, ACPI_SLEEP_SUSPEND); 425 #endif 426 break; 427 case THINKPAD_BUTTON_VOLUME_MUTE: 428 thinkpad_volume_mute(sc); 429 break; 430 case THINKPAD_BUTTON_VOLUME_DOWN: 431 thinkpad_volume_down(sc); 432 break; 433 case THINKPAD_BUTTON_VOLUME_UP: 434 thinkpad_volume_up(sc); 435 break; 436 case THINKPAD_BUTTON_MICROPHONE_MUTE: 437 #if NAUDIO > 0 && NWSKBD > 0 438 wskbd_set_mixervolume(0, 0); 439 #endif 440 break; 441 case THINKPAD_BUTTON_HIBERNATE: 442 #if defined(HIBERNATE) && !defined(SMALL_KERNEL) 443 if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ)) 444 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 445 sc->sc_acpi, ACPI_SLEEP_HIBERNATE); 446 #endif 447 break; 448 case THINKPAD_BUTTON_THINKLIGHT: 449 thinkpad_get_thinklight(sc); 450 break; 451 case THINKPAD_ADAPTIVE_NEXT: 452 case THINKPAD_ADAPTIVE_QUICK: 453 thinkpad_adaptive_change(sc); 454 break; 455 case THINKPAD_BACKLIGHT_CHANGED: 456 thinkpad_get_brightness(sc); 457 break; 458 case THINKPAD_PORT_REPL_DOCKED: 459 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 1; 460 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status = 461 SENSOR_S_OK; 462 break; 463 case THINKPAD_PORT_REPL_UNDOCKED: 464 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 0; 465 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status = 466 SENSOR_S_OK; 467 break; 468 default: 469 /* unknown or boring event */ 470 DPRINTF(("%s: unhandled event 0x%03llx\n", DEVNAME(sc), 471 event)); 472 break; 473 } 474 } 475 476 return (0); 477 } 478 479 int 480 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc) 481 { 482 struct aml_value arg; 483 int64_t bluetooth; 484 485 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC", 486 0, NULL, &bluetooth)) 487 return (1); 488 489 if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT)) 490 return (1); 491 492 bzero(&arg, sizeof(arg)); 493 arg.type = AML_OBJTYPE_INTEGER; 494 arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED; 495 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC", 496 1, &arg, NULL)) { 497 printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc)); 498 return (1); 499 } 500 501 return (0); 502 } 503 504 int 505 thinkpad_toggle_wan(struct acpithinkpad_softc *sc) 506 { 507 struct aml_value arg; 508 int64_t wan; 509 510 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN", 511 0, NULL, &wan)) 512 return (1); 513 514 if (!(wan & THINKPAD_WAN_PRESENT)) 515 return (1); 516 517 bzero(&arg, sizeof(arg)); 518 arg.type = AML_OBJTYPE_INTEGER; 519 arg.v_integer = wan ^ THINKPAD_WAN_ENABLED; 520 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN", 521 1, &arg, NULL)) { 522 printf("%s: couldn't toggle wan\n", DEVNAME(sc)); 523 return (1); 524 } 525 526 return (0); 527 } 528 529 int 530 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd) 531 { 532 struct aml_value arg; 533 534 bzero(&arg, sizeof(arg)); 535 arg.type = AML_OBJTYPE_INTEGER; 536 arg.v_integer = cmd; 537 aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL); 538 return (0); 539 } 540 541 int 542 thinkpad_volume_down(struct acpithinkpad_softc *sc) 543 { 544 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN)); 545 } 546 547 int 548 thinkpad_volume_up(struct acpithinkpad_softc *sc) 549 { 550 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP)); 551 } 552 553 int 554 thinkpad_volume_mute(struct acpithinkpad_softc *sc) 555 { 556 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE)); 557 } 558 559 int 560 thinkpad_brightness_up(struct acpithinkpad_softc *sc) 561 { 562 int b; 563 564 if (thinkpad_get_brightness(sc) == 0) { 565 b = sc->sc_brightness & 0xff; 566 if (b < ((sc->sc_brightness >> 8) & 0xff)) { 567 sc->sc_brightness = b + 1; 568 thinkpad_set_brightness(sc, 0); 569 } 570 571 return (0); 572 } else 573 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP)); 574 } 575 576 int 577 thinkpad_brightness_down(struct acpithinkpad_softc *sc) 578 { 579 int b; 580 581 if (thinkpad_get_brightness(sc) == 0) { 582 b = sc->sc_brightness & 0xff; 583 if (b > 0) { 584 sc->sc_brightness = b - 1; 585 thinkpad_set_brightness(sc, 0); 586 } 587 588 return (0); 589 } else 590 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN)); 591 } 592 593 int 594 thinkpad_adaptive_change(struct acpithinkpad_softc *sc) 595 { 596 struct aml_value arg; 597 int64_t mode; 598 599 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW", 600 0, NULL, &mode)) { 601 printf("%s: couldn't get adaptive keyboard mode\n", DEVNAME(sc)); 602 return (1); 603 } 604 605 bzero(&arg, sizeof(arg)); 606 arg.type = AML_OBJTYPE_INTEGER; 607 608 if (mode == THINKPAD_ADAPTIVE_MODE_FUNCTION) 609 arg.v_integer = THINKPAD_ADAPTIVE_MODE_HOME; 610 else 611 arg.v_integer = THINKPAD_ADAPTIVE_MODE_FUNCTION; 612 613 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "STRW", 614 1, &arg, NULL)) { 615 printf("%s: couldn't set adaptive keyboard mode\n", DEVNAME(sc)); 616 return (1); 617 } 618 619 return (0); 620 } 621 622 int 623 thinkpad_activate(struct device *self, int act) 624 { 625 626 struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self; 627 int64_t res; 628 #if NAUDIO > 0 && NWSKBD > 0 629 int mute; 630 #endif 631 632 switch (act) { 633 case DVACT_WAKEUP: 634 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW", 635 0, NULL, &res) == 0) 636 thinkpad_adaptive_change(sc); 637 #if NAUDIO > 0 && NWSKBD > 0 638 mute = thinkpad_get_volume_mute(sc); 639 if (mute != -1) 640 wskbd_set_mixermute(mute, 1); 641 #endif 642 break; 643 } 644 return (0); 645 } 646 647 void 648 thinkpad_get_thinklight(struct acpithinkpad_softc *sc) 649 { 650 if (sc->sc_thinklight_get) 651 aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 652 sc->sc_thinklight_get, 0, NULL, &sc->sc_thinklight); 653 } 654 655 void 656 thinkpad_set_thinklight(void *arg0, int arg1) 657 { 658 struct acpithinkpad_softc *sc = arg0; 659 struct aml_value arg; 660 661 memset(&arg, 0, sizeof(arg)); 662 arg.type = AML_OBJTYPE_INTEGER; 663 arg.v_integer = sc->sc_thinklight & 0x0f; 664 aml_evalname(sc->sc_acpi, sc->sc_devnode, 665 sc->sc_thinklight_set, 1, &arg, NULL); 666 } 667 668 int 669 thinkpad_get_kbd_backlight(struct wskbd_backlight *kbl) 670 { 671 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0]; 672 673 KASSERT(sc != NULL); 674 675 kbl->min = 0; 676 kbl->max = (sc->sc_thinklight >> 8) & 0x0f; 677 kbl->curval = sc->sc_thinklight & 0x0f; 678 679 if (kbl->max == 0) 680 return (ENOTTY); 681 682 return 0; 683 } 684 685 int 686 thinkpad_set_kbd_backlight(struct wskbd_backlight *kbl) 687 { 688 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0]; 689 int maxval; 690 691 KASSERT(sc != NULL); 692 693 maxval = (sc->sc_thinklight >> 8) & 0x0f; 694 695 if (maxval == 0) 696 return (ENOTTY); 697 698 if (kbl->curval > maxval) 699 return EINVAL; 700 701 sc->sc_thinklight &= ~0xff; 702 sc->sc_thinklight |= kbl->curval; 703 acpi_addtask(sc->sc_acpi, thinkpad_set_thinklight, sc, 0); 704 acpi_wakeup(sc->sc_acpi); 705 return 0; 706 } 707 708 int 709 thinkpad_get_brightness(struct acpithinkpad_softc *sc) 710 { 711 int ret; 712 713 ret = aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG", 0, NULL, 714 &sc->sc_brightness); 715 716 DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness)); 717 718 return ret; 719 } 720 721 int 722 thinkpad_set_brightness(void *arg0, int arg1) 723 { 724 struct acpithinkpad_softc *sc = arg0; 725 struct aml_value arg; 726 int ret; 727 728 DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness)); 729 730 memset(&arg, 0, sizeof(arg)); 731 arg.type = AML_OBJTYPE_INTEGER; 732 arg.v_integer = sc->sc_brightness & 0xff; 733 ret = aml_evalname(sc->sc_acpi, sc->sc_devnode, "PBLS", 1, &arg, NULL); 734 735 if (ret) 736 return ret; 737 738 thinkpad_get_brightness(sc); 739 740 return 0; 741 } 742 743 int 744 thinkpad_get_param(struct wsdisplay_param *dp) 745 { 746 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0]; 747 748 if (sc == NULL) 749 return -1; 750 751 switch (dp->param) { 752 case WSDISPLAYIO_PARAM_BRIGHTNESS: 753 dp->min = 0; 754 dp->max = (sc->sc_brightness >> 8) & 0xff; 755 dp->curval = sc->sc_brightness & 0xff; 756 return 0; 757 default: 758 return -1; 759 } 760 } 761 762 int 763 thinkpad_set_param(struct wsdisplay_param *dp) 764 { 765 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0]; 766 int maxval; 767 768 if (sc == NULL) 769 return -1; 770 771 maxval = (sc->sc_brightness >> 8) & 0xff; 772 773 switch (dp->param) { 774 case WSDISPLAYIO_PARAM_BRIGHTNESS: 775 if (dp->curval < 0) 776 dp->curval = 0; 777 if (dp->curval > maxval) 778 dp->curval = maxval; 779 sc->sc_brightness &= ~0xff; 780 sc->sc_brightness |= dp->curval; 781 acpi_addtask(sc->sc_acpi, (void *)thinkpad_set_brightness, sc, 782 0); 783 acpi_wakeup(sc->sc_acpi); 784 return 0; 785 default: 786 return -1; 787 } 788 } 789 790 #if NAUDIO > 0 && NWSKBD > 0 791 void 792 thinkpad_attach_deferred(void *v __unused) 793 { 794 wskbd_set_mixermute(1, 1); 795 } 796 797 int 798 thinkpad_get_volume_mute(struct acpithinkpad_softc *sc) 799 { 800 uint8_t vol = 0; 801 802 if (sc->sc_acpi->sc_ec == NULL) 803 return (-1); 804 805 acpiec_read(sc->sc_acpi->sc_ec, THINKPAD_ECOFFSET_VOLUME, 1, &vol); 806 return ((vol & THINKPAD_ECOFFSET_VOLUME_MUTE_MASK) == 807 THINKPAD_ECOFFSET_VOLUME_MUTE_MASK); 808 } 809 #endif 810