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