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