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