valz_acpi.c revision 1.3
1/*	$NetBSD: valz_acpi.c,v 1.3 2010/04/15 07:02:24 jruoho Exp $	*/
2
3/*
4 * Copyright (c) 2010 Jonathan A. Kollasch
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: valz_acpi.c,v 1.3 2010/04/15 07:02:24 jruoho Exp $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/syslog.h>
35
36#include <dev/acpi/acpireg.h>
37#include <dev/acpi/acpivar.h>
38
39struct valz_softc {
40	device_t sc_dev;
41	ACPI_HANDLE sc_lcd;
42};
43
44enum valz_action {
45	VALZ_BACKLIGHT_NOCHANGE,
46	VALZ_BACKLIGHT_DECREASE,
47	VALZ_BACKLIGHT_INCREASE,
48};
49
50static const char * const valz_hids[] = {
51	"TOS1900",
52	NULL
53};
54
55static int	valz_match(device_t, cfdata_t, void *);
56static void	valz_attach(device_t, device_t, void *);
57static ACPI_STATUS valz_check_bcl(ACPI_HANDLE, uint32_t, void*, void **);
58static void	valz_locate_lcd(struct valz_softc *);
59static void	valz_lcd_notify_handler(ACPI_HANDLE, uint32_t, void *);
60static void	valz_adjust_backlight(struct valz_softc *, enum valz_action);
61static void	valz_pmf_brightness_decrease(device_t);
62static void	valz_pmf_brightness_increase(device_t);
63
64CFATTACH_DECL_NEW(valz, sizeof(struct valz_softc),
65    valz_match, valz_attach, NULL, NULL);
66
67static int
68valz_match(device_t parent, cfdata_t match, void *aux)
69{
70	struct acpi_attach_args *aa = aux;
71
72	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
73		return 0;
74
75	return acpi_match_hid(aa->aa_node->ad_devinfo, valz_hids);
76}
77
78static void
79valz_attach(device_t parent, device_t self, void *aux)
80{
81	struct valz_softc *sc;
82	ACPI_STATUS rv;
83
84	aprint_naive("\n");
85	aprint_normal("\n");
86
87	sc = device_private(self);
88	sc->sc_dev = self;
89	sc->sc_lcd = NULL;
90
91	valz_locate_lcd(sc);
92
93	if (sc->sc_lcd == NULL) {
94		aprint_error_dev(self, "failed to find a usable LCD\n");
95		return;
96	}
97
98	if(!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
99	    valz_pmf_brightness_decrease, false))
100		aprint_error_dev(self, "failed to register event handler\n");
101
102	if(!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
103	    valz_pmf_brightness_increase, false))
104		aprint_error_dev(self, "failed to register event handler\n");
105
106	/*
107	 * XXX: This is broken.
108	 *
109	 *	It claims resources that do not belong to it.
110	 *
111	 *	It either prevents an ACPI video driver for
112	 *	receiving events or duplicates the notifications.
113	 */
114	rv = AcpiInstallNotifyHandler(sc->sc_lcd, ACPI_DEVICE_NOTIFY,
115		valz_lcd_notify_handler, sc);
116	if (ACPI_FAILURE(rv))
117		aprint_error_dev(self, "failed to install notify handler; %s\n",
118		     AcpiFormatException(rv));
119}
120
121static void
122valz_lcd_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *context)
123{
124	struct valz_softc *sc = context;
125
126	/* XXX magic */
127	switch (notify) {
128	case 0x86:
129		pmf_event_inject(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_UP);
130		break;
131	case 0x87:
132		pmf_event_inject(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_DOWN);
133		break;
134	default:
135		log(LOG_INFO, "%s: unknown notify: 0x%x\n",
136		    device_xname(sc->sc_dev), notify);
137		break;
138	}
139}
140
141static void
142valz_locate_lcd(struct valz_softc *sc)
143{
144	ACPI_HANDLE parent;
145	ACPI_STATUS rv;
146
147	rv = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &parent);
148	if (ACPI_FAILURE(rv))
149		return;
150
151        AcpiWalkNamespace(ACPI_TYPE_DEVICE, parent, 100,
152			  valz_check_bcl, NULL, sc, NULL);
153}
154
155static ACPI_STATUS
156valz_check_bcl(ACPI_HANDLE handle, uint32_t level,
157    void *context, void **status)
158{
159	struct valz_softc *sc = context;
160	ACPI_STATUS rv;
161	ACPI_BUFFER buf;
162
163	rv = acpi_eval_struct(handle, "_BCL", &buf);
164	if (ACPI_FAILURE(rv))
165		return AE_OK;
166
167	sc->sc_lcd = handle;
168
169	if (buf.Pointer)
170		AcpiOsFree(buf.Pointer);
171
172	return AE_OK;
173}
174
175static void
176valz_adjust_backlight(struct valz_softc *sc, enum valz_action action)
177{
178	ACPI_STATUS rv;
179	ACPI_INTEGER old_level, new_level;
180
181	if (sc->sc_lcd == NULL)
182		return;
183
184	/* Get current backlight level. */
185	rv = acpi_eval_integer(sc->sc_lcd, "_BQC", &old_level);
186	if (ACPI_FAILURE(rv)) {
187		log(LOG_CRIT, "%s: _BQC failed evaluation: %s\n",
188		    device_xname(sc->sc_dev), AcpiFormatException(rv));
189		return;
190	}
191
192	new_level = old_level;
193
194	switch (action) {
195	case VALZ_BACKLIGHT_DECREASE:
196		if(old_level >= 10)
197			new_level -= 10;
198		break;
199	case VALZ_BACKLIGHT_INCREASE:
200		if(old_level <= 60)
201			new_level += 10;
202		break;
203	default:
204		return;
205	}
206
207	if (new_level == old_level)
208		return;
209
210	rv = acpi_eval_set_integer(sc->sc_lcd, "_BCM", new_level);
211	if (ACPI_FAILURE(rv))
212		log(LOG_CRIT, "%s: _BCM failed evaluation: %s\n",
213		    device_xname(sc->sc_dev), AcpiFormatException(rv));
214}
215
216static void
217valz_pmf_brightness_decrease(device_t self)
218{
219	struct valz_softc *sc;
220	sc = device_private(self);
221	valz_adjust_backlight(sc, VALZ_BACKLIGHT_DECREASE);
222}
223
224static void
225valz_pmf_brightness_increase(device_t self)
226{
227	struct valz_softc *sc;
228	sc = device_private(self);
229	valz_adjust_backlight(sc, VALZ_BACKLIGHT_INCREASE);
230}
231