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