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