valz_acpi.c revision 1.1
1/*	$NetBSD: valz_acpi.c,v 1.1 2010/04/11 22:42:30 jakllsch 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.1 2010/04/11 22:42:30 jakllsch 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, void*, void **);
58static void	valz_locate_lcd(struct valz_softc *);
59static void	valz_lcd_notify_handler(ACPI_HANDLE, UINT32, 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	rv = AcpiInstallNotifyHandler(sc->sc_lcd, ACPI_DEVICE_NOTIFY,
107		valz_lcd_notify_handler, sc);
108	if (ACPI_FAILURE(rv))
109		aprint_error_dev(self, "failed to install notify handler; %s\n",
110		     AcpiFormatException(rv));
111}
112
113static void
114valz_lcd_notify_handler(ACPI_HANDLE handle, UINT32 notify, void *context)
115{
116	struct valz_softc *sc = context;
117
118	/* XXX magic */
119	switch (notify) {
120	case 0x86:
121		pmf_event_inject(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_UP);
122		break;
123	case 0x87:
124		pmf_event_inject(sc->sc_dev, PMFE_DISPLAY_BRIGHTNESS_DOWN);
125		break;
126	default:
127		log(LOG_INFO, "%s: unknown notify: 0x%x\n",
128		    device_xname(sc->sc_dev), notify);
129		break;
130	}
131}
132
133static void
134valz_locate_lcd(struct valz_softc *sc)
135{
136	ACPI_HANDLE parent;
137	ACPI_STATUS rv;
138
139	rv = AcpiGetHandle(ACPI_ROOT_OBJECT, "\\_SB_", &parent);
140	if (ACPI_FAILURE(rv))
141		return;
142
143        AcpiWalkNamespace(ACPI_TYPE_DEVICE, parent, 100,
144			  valz_check_bcl, NULL, sc, NULL);
145}
146
147static ACPI_STATUS
148valz_check_bcl(ACPI_HANDLE handle, UINT32 level, void *context, void **status)
149{
150	struct valz_softc *sc = context;
151	ACPI_STATUS rv;
152	ACPI_BUFFER buf;
153
154	rv = acpi_eval_struct(handle, "_BCL", &buf);
155	if (ACPI_FAILURE(rv))
156		return AE_OK;
157
158	sc->sc_lcd = handle;
159
160	if (buf.Pointer)
161		AcpiOsFree(buf.Pointer);
162
163	return AE_OK;
164}
165
166static void
167valz_adjust_backlight(struct valz_softc *sc, enum valz_action action)
168{
169	ACPI_STATUS rv;
170	ACPI_INTEGER old_level, new_level;
171
172	if (sc->sc_lcd == NULL)
173		return;
174
175	/* Get current backlight level. */
176	rv = acpi_eval_integer(sc->sc_lcd, "_BQC", &old_level);
177	if (ACPI_FAILURE(rv)) {
178		log(LOG_CRIT, "%s: _BQC failed evaluation: %s\n",
179		    device_xname(sc->sc_dev), AcpiFormatException(rv));
180		return;
181	}
182
183	new_level = old_level;
184
185	switch (action) {
186	case VALZ_BACKLIGHT_DECREASE:
187		if(old_level >= 10)
188			new_level -= 10;
189		break;
190	case VALZ_BACKLIGHT_INCREASE:
191		if(old_level <= 60)
192			new_level += 10;
193		break;
194	default:
195		return;
196	}
197
198	if (new_level == old_level)
199		return;
200
201	rv = acpi_eval_set_integer(sc->sc_lcd, "_BCM", new_level);
202	if (ACPI_FAILURE(rv))
203		log(LOG_CRIT, "%s: _BCM failed evaluation: %s\n",
204		    device_xname(sc->sc_dev), AcpiFormatException(rv));
205}
206
207static void
208valz_pmf_brightness_decrease(device_t self)
209{
210	struct valz_softc *sc;
211	sc = device_private(self);
212	valz_adjust_backlight(sc, VALZ_BACKLIGHT_DECREASE);
213}
214
215static void
216valz_pmf_brightness_increase(device_t self)
217{
218	struct valz_softc *sc;
219	sc = device_private(self);
220	valz_adjust_backlight(sc, VALZ_BACKLIGHT_INCREASE);
221}
222