1 /* $OpenBSD: acpivout.c,v 1.12 2016/03/29 17:52:04 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2009 Paul Irofti <pirofti@openbsd.org> 4 * 5 * Permission to use, copy, modify, and/or 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/device.h> 21 #include <sys/malloc.h> 22 23 #include <machine/bus.h> 24 25 #include <dev/acpi/acpivar.h> 26 #include <dev/acpi/acpidev.h> 27 #include <dev/acpi/amltypes.h> 28 #include <dev/acpi/dsdt.h> 29 30 #include <dev/wscons/wsconsio.h> 31 32 int acpivout_match(struct device *, void *, void *); 33 void acpivout_attach(struct device *, struct device *, void *); 34 int acpivout_notify(struct aml_node *, int, void *); 35 36 #ifdef ACPIVIDEO_DEBUG 37 #define DPRINTF(x) printf x 38 #else 39 #define DPRINTF(x) 40 #endif 41 42 /* Notifications for Output Devices */ 43 #define NOTIFY_BRIGHTNESS_CYCLE 0x85 44 #define NOTIFY_BRIGHTNESS_UP 0x86 45 #define NOTIFY_BRIGHTNESS_DOWN 0x87 46 #define NOTIFY_BRIGHTNESS_ZERO 0x88 47 #define NOTIFY_DISPLAY_OFF 0x89 48 49 struct acpivout_softc { 50 struct device sc_dev; 51 52 bus_space_tag_t sc_iot; 53 bus_space_handle_t sc_ioh; 54 55 struct acpi_softc *sc_acpi; 56 struct aml_node *sc_devnode; 57 58 int *sc_bcl; 59 size_t sc_bcl_len; 60 }; 61 62 void acpivout_brightness_cycle(struct acpivout_softc *); 63 void acpivout_brightness_up(struct acpivout_softc *); 64 void acpivout_brightness_down(struct acpivout_softc *); 65 void acpivout_brightness_zero(struct acpivout_softc *); 66 int acpivout_get_brightness(struct acpivout_softc *); 67 int acpivout_find_brightness(struct acpivout_softc *, int); 68 void acpivout_set_brightness(struct acpivout_softc *, int); 69 void acpivout_get_bcl(struct acpivout_softc *); 70 71 /* wconsole hook functions */ 72 int acpivout_get_param(struct wsdisplay_param *); 73 int acpivout_set_param(struct wsdisplay_param *); 74 75 extern int (*ws_get_param)(struct wsdisplay_param *); 76 extern int (*ws_set_param)(struct wsdisplay_param *); 77 78 struct cfattach acpivout_ca = { 79 sizeof(struct acpivout_softc), acpivout_match, acpivout_attach 80 }; 81 82 struct cfdriver acpivout_cd = { 83 NULL, "acpivout", DV_DULL 84 }; 85 86 int 87 acpivout_match(struct device *parent, void *match, void *aux) 88 { 89 struct acpi_attach_args *aaa = aux; 90 struct cfdata *cf = match; 91 92 if (aaa->aaa_name == NULL || 93 strcmp(aaa->aaa_name, cf->cf_driver->cd_name) != 0 || 94 aaa->aaa_table != NULL) 95 return (0); 96 97 if (ws_get_param || ws_set_param) 98 return (0); 99 100 return (1); 101 } 102 103 void 104 acpivout_attach(struct device *parent, struct device *self, void *aux) 105 { 106 struct acpivout_softc *sc = (struct acpivout_softc *)self; 107 struct acpi_attach_args *aaa = aux; 108 109 sc->sc_acpi = ((struct acpivideo_softc *)parent)->sc_acpi; 110 sc->sc_devnode = aaa->aaa_node; 111 112 printf(": %s\n", sc->sc_devnode->name); 113 114 aml_register_notify(sc->sc_devnode, aaa->aaa_dev, 115 acpivout_notify, sc, ACPIDEV_NOPOLL); 116 117 ws_get_param = acpivout_get_param; 118 ws_set_param = acpivout_set_param; 119 120 acpivout_get_bcl(sc); 121 } 122 123 int 124 acpivout_notify(struct aml_node *node, int notify, void *arg) 125 { 126 struct acpivout_softc *sc = arg; 127 128 switch (notify) { 129 case NOTIFY_BRIGHTNESS_CYCLE: 130 acpivout_brightness_cycle(sc); 131 break; 132 case NOTIFY_BRIGHTNESS_UP: 133 acpivout_brightness_up(sc); 134 break; 135 case NOTIFY_BRIGHTNESS_DOWN: 136 acpivout_brightness_down(sc); 137 break; 138 case NOTIFY_BRIGHTNESS_ZERO: 139 acpivout_brightness_zero(sc); 140 break; 141 case NOTIFY_DISPLAY_OFF: 142 /* TODO: D3 state change */ 143 break; 144 default: 145 printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify); 146 break; 147 } 148 149 return (0); 150 } 151 152 void 153 acpivout_brightness_cycle(struct acpivout_softc *sc) 154 { 155 int cur_level; 156 157 if (sc->sc_bcl_len == 0) 158 return; 159 cur_level = acpivout_get_brightness(sc); 160 if (cur_level == sc->sc_bcl[sc->sc_bcl_len - 1]) 161 acpivout_brightness_zero(sc); 162 else 163 acpivout_brightness_up(sc); 164 } 165 166 void 167 acpivout_brightness_up(struct acpivout_softc *sc) 168 { 169 int i, cur_level; 170 171 if (sc->sc_bcl_len == 0) 172 return; 173 cur_level = acpivout_get_brightness(sc); 174 if (cur_level == -1) 175 return; 176 177 /* check for max brightness level */ 178 if (cur_level == sc->sc_bcl[sc->sc_bcl_len - 1]) 179 return; 180 181 for (i = 0; i < sc->sc_bcl_len && cur_level != sc->sc_bcl[i]; i++); 182 acpivout_set_brightness(sc, sc->sc_bcl[i + 1]); 183 } 184 185 void 186 acpivout_brightness_down(struct acpivout_softc *sc) 187 { 188 int i, cur_level; 189 190 if (sc->sc_bcl_len == 0) 191 return; 192 cur_level = acpivout_get_brightness(sc); 193 if (cur_level == -1) 194 return; 195 196 /* check for min brightness level */ 197 if (cur_level == sc->sc_bcl[0]) 198 return; 199 200 for (i = 0; i < sc->sc_bcl_len && cur_level != sc->sc_bcl[i]; i++); 201 acpivout_set_brightness(sc, sc->sc_bcl[i - 1]); 202 } 203 204 void 205 acpivout_brightness_zero(struct acpivout_softc *sc) 206 { 207 if (sc->sc_bcl_len == 0) 208 return; 209 acpivout_set_brightness(sc, sc->sc_bcl[0]); 210 } 211 212 int 213 acpivout_get_brightness(struct acpivout_softc *sc) 214 { 215 struct aml_value res; 216 int level; 217 218 aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BQC", 0, NULL, &res); 219 level = aml_val2int(&res); 220 aml_freevalue(&res); 221 DPRINTF(("%s: BQC = %d\n", DEVNAME(sc), level)); 222 223 if (level < sc->sc_bcl[0] || level > sc->sc_bcl[sc->sc_bcl_len -1]) 224 level = -1; 225 226 return (level); 227 } 228 229 int 230 acpivout_find_brightness(struct acpivout_softc *sc, int level) 231 { 232 int i, mid; 233 234 for (i = 0; i < sc->sc_bcl_len - 1; i++) { 235 mid = sc->sc_bcl[i] + (sc->sc_bcl[i + 1] - sc->sc_bcl[i]) / 2; 236 if (sc->sc_bcl[i] <= level && level <= mid) 237 return sc->sc_bcl[i]; 238 if (mid < level && level <= sc->sc_bcl[i + 1]) 239 return sc->sc_bcl[i + 1]; 240 } 241 if (level < sc->sc_bcl[0]) 242 return sc->sc_bcl[0]; 243 else 244 return sc->sc_bcl[i]; 245 } 246 247 void 248 acpivout_set_brightness(struct acpivout_softc *sc, int level) 249 { 250 struct aml_value args, res; 251 252 memset(&args, 0, sizeof(args)); 253 args.v_integer = level; 254 args.type = AML_OBJTYPE_INTEGER; 255 256 DPRINTF(("%s: BCM = %d\n", DEVNAME(sc), level)); 257 aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BCM", 1, &args, &res); 258 259 aml_freevalue(&res); 260 } 261 262 void 263 acpivout_get_bcl(struct acpivout_softc *sc) 264 { 265 int i, j, value; 266 struct aml_value res; 267 268 DPRINTF(("Getting _BCL!")); 269 aml_evalname(sc->sc_acpi, sc->sc_devnode, "_BCL", 0, NULL, &res); 270 if (res.type != AML_OBJTYPE_PACKAGE) { 271 sc->sc_bcl_len = 0; 272 goto err; 273 } 274 /* 275 * Per the ACPI spec section B.6.2 the _BCL method returns a package. 276 * The first integer in the package is the brightness level 277 * when the computer has full power, and the second is the 278 * brightness level when the computer is on batteries. 279 * All other levels may be used by OSPM. 280 * So we skip the first two integers in the package. 281 */ 282 if (res.length <= 2) { 283 sc->sc_bcl_len = 0; 284 goto err; 285 } 286 sc->sc_bcl_len = res.length - 2; 287 288 sc->sc_bcl = mallocarray(sc->sc_bcl_len, sizeof(int), M_DEVBUF, 289 M_WAITOK | M_ZERO); 290 291 for (i = 0; i < sc->sc_bcl_len; i++) { 292 /* Sort darkest to brightest */ 293 value = aml_val2int(res.v_package[i + 2]); 294 for (j = i; j > 0 && sc->sc_bcl[j - 1] > value; j--) 295 sc->sc_bcl[j] = sc->sc_bcl[j - 1]; 296 sc->sc_bcl[j] = value; 297 } 298 299 err: 300 aml_freevalue(&res); 301 } 302 303 304 int 305 acpivout_get_param(struct wsdisplay_param *dp) 306 { 307 struct acpivout_softc *sc = NULL; 308 int i; 309 310 switch (dp->param) { 311 case WSDISPLAYIO_PARAM_BRIGHTNESS: 312 for (i = 0; i < acpivout_cd.cd_ndevs; i++) { 313 if (acpivout_cd.cd_devs[i] == NULL) 314 continue; 315 sc = (struct acpivout_softc *)acpivout_cd.cd_devs[i]; 316 /* Ignore device if not connected. */ 317 if (sc->sc_bcl_len != 0) 318 break; 319 } 320 if (sc != NULL && sc->sc_bcl_len != 0) { 321 dp->min = 0; 322 dp->max = sc->sc_bcl[sc->sc_bcl_len - 1]; 323 rw_enter_write(&sc->sc_acpi->sc_lck); 324 dp->curval = acpivout_get_brightness(sc); 325 rw_exit_write(&sc->sc_acpi->sc_lck); 326 if (dp->curval != -1) 327 return 0; 328 } 329 return -1; 330 default: 331 return -1; 332 } 333 } 334 335 int 336 acpivout_set_param(struct wsdisplay_param *dp) 337 { 338 struct acpivout_softc *sc = NULL; 339 int i, exact; 340 341 switch (dp->param) { 342 case WSDISPLAYIO_PARAM_BRIGHTNESS: 343 for (i = 0; i < acpivout_cd.cd_ndevs; i++) { 344 if (acpivout_cd.cd_devs[i] == NULL) 345 continue; 346 sc = (struct acpivout_softc *)acpivout_cd.cd_devs[i]; 347 /* Ignore device if not connected. */ 348 if (sc->sc_bcl_len != 0) 349 break; 350 } 351 if (sc != NULL && sc->sc_bcl_len != 0) { 352 rw_enter_write(&sc->sc_acpi->sc_lck); 353 exact = acpivout_find_brightness(sc, dp->curval); 354 acpivout_set_brightness(sc, exact); 355 rw_exit_write(&sc->sc_acpi->sc_lck); 356 return 0; 357 } 358 return -1; 359 default: 360 return -1; 361 } 362 } 363