xref: /openbsd/sys/dev/acpi/acpisony.c (revision 898184e3)
1 /* $OpenBSD: acpisony.c,v 1.4 2011/01/02 04:56:57 jordan Exp $ */
2 /*
3  * Copyright (c) 2010 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/proc.h>
21 
22 #include <dev/acpi/acpireg.h>
23 #include <dev/acpi/acpivar.h>
24 #include <dev/acpi/acpidev.h>
25 #include <dev/acpi/amltypes.h>
26 #include <dev/acpi/dsdt.h>
27 
28 #include <machine/apmvar.h>
29 
30 int	acpisony_match(struct device *, void *, void *);
31 void	acpisony_attach(struct device *, struct device *, void *);
32 int	acpisony_activate(struct device *, int);
33 int	acpisony_notify(struct aml_node *, int, void *);
34 
35 #ifdef ACPISONY_DEBUG
36 #define DPRINTF(x)	printf x
37 #else
38 #define DPRINTF(x)
39 #endif
40 
41 /* Notifications */
42 #define	SONY_NOTIFY_FN_KEY			0x90
43 
44 #define	SONY_NOTIFY_BRIGHTNESS_DOWN_PRESSED	0x85
45 #define	SONY_NOTIFY_BRIGHTNESS_DOWN_RELEASED	0x05
46 #define	SONY_NOTIFY_BRIGHTNESS_UP_PRESSED	0x86
47 #define	SONY_NOTIFY_BRIGHTNESS_UP_RELEASED	0x06
48 
49 #define	SONY_NOTIFY_DISPLAY_SWITCH_PRESSED	0x87
50 #define	SONY_NOTIFY_DISPLAY_SWITCH_RELEASED	0x07
51 
52 #define	SONY_NOTIFY_ZOOM_OUT_PRESSED		0x89
53 #define	SONY_NOTIFY_ZOOM_OUT_RELEASED		0x09
54 
55 #define	SONY_NOTIFY_ZOOM_IN_PRESSED		0x8a
56 #define	SONY_NOTIFY_ZOOM_IN_RELEASED		0x0a
57 
58 #define	SONY_NOTIFY_SUSPEND_PRESSED		0x8c
59 #define	SONY_NOTIFY_SUSPEND_RELEASED		0x0c
60 
61 struct acpisony_softc {
62 	struct device		sc_dev;
63 
64 	bus_space_tag_t		sc_iot;
65 	bus_space_handle_t	sc_ioh;
66 
67 	struct acpi_softc	*sc_acpi;
68 	struct aml_node		*sc_devnode;
69 };
70 
71 struct cfattach acpisony_ca = {
72 	sizeof(struct acpisony_softc), acpisony_match, acpisony_attach,
73 	NULL, acpisony_activate
74 };
75 
76 struct cfdriver acpisony_cd = {
77 	NULL, "acpisony", DV_DULL
78 };
79 
80 void acpisony_notify_setup(struct acpisony_softc *);
81 int acpisony_set_hotkey(struct acpisony_softc *, int, int);
82 int acpisony_find_offset(struct acpisony_softc *, int);
83 
84 void acpisony_brightness_down(struct acpisony_softc *);
85 int acpisony_get_brightness(struct acpisony_softc *);
86 void acpisony_set_brightness(struct acpisony_softc *, int);
87 
88 int
89 acpisony_match(struct device *parent, void *match, void *aux)
90 {
91 	struct acpi_attach_args *aa = aux;
92 	struct cfdata		*cf = match;
93 
94 	if (aa->aaa_name == NULL ||
95 	    strcmp(aa->aaa_name, cf->cf_driver->cd_name) != 0 ||
96 	    aa->aaa_table != NULL)
97 		return (0);
98 
99 	return (1);
100 }
101 
102 void
103 acpisony_attach(struct device *parent, struct device *self, void *aux)
104 {
105 	struct acpisony_softc	*sc = (struct acpisony_softc *)self;
106 	struct acpi_attach_args *aa = aux;
107 
108 	sc->sc_acpi = (struct acpi_softc *)parent;
109 	sc->sc_devnode = aa->aaa_node;
110 
111 	printf(": %s\n", sc->sc_devnode->name);
112 
113 	/* Setup the notification masks */
114 	acpisony_notify_setup(sc);
115 
116 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
117 	    acpisony_notify, sc, ACPIDEV_NOPOLL);
118 }
119 
120 int
121 acpisony_activate(struct device *self, int act)
122 {
123 	struct acpisony_softc *sc = (struct acpisony_softc *)self;
124 
125 	switch (act) {
126 	case DVACT_RESUME:
127 		acpisony_notify_setup(sc);
128 		break;
129 	}
130 	return 0;
131 }
132 
133 int
134 acpisony_notify(struct aml_node *node, int notify, void *arg)
135 {
136 	struct acpisony_softc *sc = arg;
137 	int val, key = 0;
138 
139 	if (notify == SONY_NOTIFY_FN_KEY) {
140 		notify -= 0x90;
141 		DPRINTF(("notify = %X", notify));
142 
143 		if (notify == acpisony_find_offset(sc, 0x100)) {
144 			DPRINTF(("key = 0x100\n"));
145 			key = 0x100;
146 		}
147 		if (notify == acpisony_find_offset(sc, 0x127)) {
148 			DPRINTF(("key = 0x127\n"));
149 			key = 0x127;
150 		}
151 
152 		if (key) {
153 			val = acpisony_set_hotkey(sc, key, 0x200);
154 			if (val < 0) {
155 				printf("returned val = %X", val);
156 				return 1;
157 			}
158 			notify = val & 0xff;
159 
160 			DPRINTF(("Treat %X events, notify %X\n", key, notify));
161 		} else
162 			DPRINTF(("rfkill update, notify %X\n", notify));
163 	}
164 
165 	switch (notify) {
166 	case SONY_NOTIFY_BRIGHTNESS_DOWN_PRESSED:
167 		DPRINTF(("br-down-pressed\n"));
168 		acpisony_brightness_down(sc);
169 		break;
170 	case SONY_NOTIFY_BRIGHTNESS_DOWN_RELEASED:
171 		DPRINTF(("br-down-released\n"));
172 		break;
173 	case SONY_NOTIFY_BRIGHTNESS_UP_PRESSED:
174 		DPRINTF(("br-up-pressed\n"));
175 		break;
176 	case SONY_NOTIFY_BRIGHTNESS_UP_RELEASED:
177 		DPRINTF(("br-up-released\n"));
178 		break;
179 	case SONY_NOTIFY_DISPLAY_SWITCH_PRESSED:
180 		DPRINTF(("display-pressed\n"));
181 		break;
182 	case SONY_NOTIFY_DISPLAY_SWITCH_RELEASED:
183 		DPRINTF(("display-released\n"));
184 		break;
185 	case SONY_NOTIFY_ZOOM_IN_PRESSED:
186 		DPRINTF(("zoom-in-pressed\n"));
187 		break;
188 	case SONY_NOTIFY_ZOOM_IN_RELEASED:
189 		DPRINTF(("zoom-in-released\n"));
190 		break;
191 	case SONY_NOTIFY_ZOOM_OUT_PRESSED:
192 		DPRINTF(("zoom-out-pressed\n"));
193 		break;
194 	case SONY_NOTIFY_ZOOM_OUT_RELEASED:
195 		DPRINTF(("zoom-out-released\n"));
196 		break;
197 	case SONY_NOTIFY_SUSPEND_PRESSED:
198 		DPRINTF(("suspend-pressed\n"));
199 #ifndef SMALL_KERNEL
200 		if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
201 			acpi_addtask(sc->sc_acpi, acpi_sleep_task,
202 			    sc->sc_acpi, ACPI_STATE_S3);
203 #endif
204 		break;
205 	case SONY_NOTIFY_SUSPEND_RELEASED:
206 		DPRINTF(("suspend-released\n"));
207 		break;
208 	default:
209 		printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify);
210 		break;
211 	}
212 
213 	return 0;
214 }
215 
216 void
217 acpisony_notify_setup(struct acpisony_softc *sc)
218 {
219 	struct aml_value arg;
220 
221 	bzero(&arg, sizeof(arg));
222 	arg.type = AML_OBJTYPE_INTEGER;
223 
224 	arg.v_integer = 1;
225 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "ECON", 1, &arg, NULL);
226 
227 	/* Enable all events */
228 	arg.v_integer = 0xffff;
229 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN02", 1, &arg, NULL);
230 
231 	/* Enable hotkeys */
232 	arg.v_integer = 0x04;
233 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN02", 1, &arg, NULL);
234 	arg.v_integer = 0x02;
235 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN07", 1, &arg, NULL);
236 	arg.v_integer = 0x10;
237 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN02", 1, &arg, NULL);
238 	arg.v_integer = 0x00;
239 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN07", 1, &arg, NULL);
240 	arg.v_integer = 0x02;
241 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN03", 1, &arg, NULL);
242 	arg.v_integer = 0x101;
243 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN07", 1, &arg, NULL);
244 }
245 
246 int
247 acpisony_find_offset(struct acpisony_softc *sc, int key)
248 {
249 	struct aml_value arg, res;
250 	int val;
251 
252 	bzero(&arg, sizeof(arg));
253 	arg.type = AML_OBJTYPE_INTEGER;
254 
255 	for (arg.v_integer = 0x20; arg.v_integer < 0x30; arg.v_integer++) {
256 		aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN00", 1, &arg, &res);
257 		val = aml_val2int(&res);
258 		aml_freevalue(&res);
259 		if (val == key) {
260 			DPRINTF(("Matched key %X\n", val));
261 			return arg.v_integer - 0x20;
262 		}
263 	}
264 
265 	return -1;
266 }
267 
268 int
269 acpisony_set_hotkey(struct acpisony_softc *sc, int key, int val)
270 {
271 	int off, rc = -1;
272 	struct aml_value res, arg;
273 
274 	bzero(&arg, sizeof(arg));
275 	arg.type = AML_OBJTYPE_INTEGER;
276 
277 	off = acpisony_find_offset(sc, key);
278 	DPRINTF(("off = %X\n", off));
279 	if (off < 0)
280 		return rc;
281 
282 	arg.v_integer = off | val;
283 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "SN07", 1, &arg, &res);
284 	rc = aml_val2int(&res);
285 	aml_freevalue(&res);
286 
287 	return rc;
288 }
289 
290 void
291 acpisony_brightness_down(struct acpisony_softc *sc)
292 {
293 	int val;
294 
295 	val = acpisony_get_brightness(sc);
296 	DPRINTF(("current value = %X", val));
297 	if (val > 0)
298 		val--;
299 	else
300 		val = 0;
301 	DPRINTF(("next value = %X", val));
302 	acpisony_set_brightness(sc, val);
303 }
304 
305 int
306 acpisony_get_brightness(struct acpisony_softc *sc)
307 {
308 	struct aml_value res;
309 	int val;
310 
311 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "GBRT", 0, NULL, &res);
312 	val = aml_val2int(&res);
313 	aml_freevalue(&res);
314 
315 	return val;
316 }
317 
318 void
319 acpisony_set_brightness(struct acpisony_softc *sc, int level)
320 {
321 	struct aml_value arg;
322 
323 	bzero(&arg, sizeof(arg));
324 	arg.type = AML_OBJTYPE_INTEGER;
325 	arg.v_integer = level;
326 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBRT", 1, &arg, NULL);
327 	aml_freevalue(&arg);
328 }
329