xref: /openbsd-src/sys/dev/acpi/acpivout.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
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