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