xref: /openbsd-src/sys/dev/acpi/acpiasus.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /* $OpenBSD: acpiasus.c,v 1.12 2011/06/06 06:13:46 deraadt Exp $ */
2 /* $NetBSD: asus_acpi.c,v 1.2.2.2 2008/04/03 12:42:37 mjf Exp $ */
3 /*
4  * Copyright (c) 2007, 2008 Jared D. McNeill <jmcneill@invisible.ca>
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  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *        This product includes software developed by Jared D. McNeill.
18  * 4. Neither the name of The NetBSD Foundation nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 /*
36  * ASUS ACPI hotkeys driver.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/device.h>
41 #include <sys/systm.h>
42 #include <sys/workq.h>
43 
44 #include <dev/acpi/acpireg.h>
45 #include <dev/acpi/acpivar.h>
46 #include <dev/acpi/acpidev.h>
47 #include <dev/acpi/amltypes.h>
48 #include <dev/acpi/dsdt.h>
49 
50 #include "audio.h"
51 #include "wskbd.h"
52 
53 struct acpiasus_softc {
54 	struct device		sc_dev;
55 
56 	struct acpi_softc	*sc_acpi;
57 	struct aml_node		*sc_devnode;
58 };
59 
60 #define ASUS_NOTIFY_WIRELESSON		0x10
61 #define ASUS_NOTIFY_WIRELESSOFF		0x11
62 #define ASUS_NOTIFY_TASKSWITCH		0x12
63 #define ASUS_NOTIFY_VOLUMEMUTE		0x13
64 #define ASUS_NOTIFY_VOLUMEDOWN		0x14
65 #define ASUS_NOTIFY_VOLUMEUP		0x15
66 #define ASUS_NOTIFY_LCDSWITCHOFF0	0x16
67 #define ASUS_NOTIFY_LCDSWITCHOFF1	0x1a
68 #define ASUS_NOTIFY_LCDCHANGERES	0x1b
69 #define ASUS_NOTIFY_USERDEF0		0x1c
70 #define ASUS_NOTIFY_USERDEF1		0x1d
71 #define ASUS_NOTIFY_BRIGHTNESSLOW	0x20
72 #define ASUS_NOTIFY_BRIGHTNESSHIGH	0x2f
73 #define ASUS_NOTIFY_DISPLAYCYCLEDOWN	0x30
74 #define ASUS_NOTIFY_DISPLAYCYCLEUP	0x32
75 
76 #define ASUS_NOTIFY_POWERCONNECT	0x50
77 #define ASUS_NOTIFY_POWERDISCONNECT	0x51
78 
79 #define	ASUS_SDSP_LCD			0x01
80 #define	ASUS_SDSP_CRT			0x02
81 #define	ASUS_SDSP_TV			0x04
82 #define	ASUS_SDSP_DVI			0x08
83 #define	ASUS_SDSP_ALL \
84 	(ASUS_SDSP_LCD | ASUS_SDSP_CRT | ASUS_SDSP_TV | ASUS_SDSP_DVI)
85 
86 int	acpiasus_match(struct device *, void *, void *);
87 void	acpiasus_attach(struct device *, struct device *, void *);
88 void	acpiasus_init(struct device *);
89 int	acpiasus_notify(struct aml_node *, int, void *);
90 int	acpiasus_activate(struct device *, int);
91 
92 #if NAUDIO > 0 && NWSKBD > 0
93 extern int wskbd_set_mixervolume(long dir, int out);
94 #endif
95 
96 struct cfattach acpiasus_ca = {
97 	sizeof(struct acpiasus_softc), acpiasus_match, acpiasus_attach,
98 	acpiasus_activate
99 };
100 
101 struct cfdriver acpiasus_cd = {
102 	NULL, "acpiasus", DV_DULL
103 };
104 
105 const char *acpiasus_hids[] = { ACPI_DEV_ASUS, 0 };
106 
107 int
108 acpiasus_match(struct device *parent, void *match, void *aux)
109 {
110 	struct acpi_attach_args *aa = aux;
111 	struct cfdata *cf = match;
112 
113 	return (acpi_matchhids(aa, acpiasus_hids, cf->cf_driver->cd_name));
114 }
115 
116 void
117 acpiasus_attach(struct device *parent, struct device *self, void *aux)
118 {
119 	struct acpiasus_softc *sc = (struct acpiasus_softc *)self;
120 	struct acpi_attach_args *aa = aux;
121 
122 	sc->sc_acpi = (struct acpi_softc *)parent;
123 	sc->sc_devnode = aa->aaa_node;
124 
125 	printf("\n");
126 
127 	acpiasus_init(self);
128 
129 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
130 	    acpiasus_notify, sc, ACPIDEV_NOPOLL);
131 }
132 
133 void
134 acpiasus_init(struct device *self)
135 {
136 	struct acpiasus_softc *sc = (struct acpiasus_softc *)self;
137 	struct aml_value cmd;
138 	struct aml_value ret;
139 
140 	bzero(&cmd, sizeof(cmd));
141 	cmd.type = AML_OBJTYPE_INTEGER;
142 	cmd.v_integer = 0x40;		/* Disable ASL display switching. */
143 
144 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "INIT", 1, &cmd, &ret))
145 		printf("%s: no INIT\n", DEVNAME(sc));
146 	else
147 		aml_freevalue(&ret);
148 }
149 
150 int
151 acpiasus_notify(struct aml_node *node, int notify, void *arg)
152 {
153 	struct acpiasus_softc *sc = arg;
154 
155 	if (notify >= ASUS_NOTIFY_BRIGHTNESSLOW &&
156 	    notify <= ASUS_NOTIFY_BRIGHTNESSHIGH) {
157 #ifdef ACPIASUS_DEBUG
158 		printf("%s: brightness %d percent\n", DEVNAME(sc),
159 		    (notify & 0xf) * 100 / 0xf);
160 #endif
161 		return 0;
162 	}
163 
164 	switch (notify) {
165 	case ASUS_NOTIFY_WIRELESSON:	/* Handled by AML. */
166 	case ASUS_NOTIFY_WIRELESSOFF:	/* Handled by AML. */
167 		break;
168 	case ASUS_NOTIFY_TASKSWITCH:
169 		break;
170 	case ASUS_NOTIFY_DISPLAYCYCLEDOWN:
171 	case ASUS_NOTIFY_DISPLAYCYCLEUP:
172 		break;
173 #if NAUDIO > 0 && NWSKBD > 0
174 	case ASUS_NOTIFY_VOLUMEMUTE:
175 		workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
176 		    (void *)(long)0, (void *)(int)1);
177 		break;
178 	case ASUS_NOTIFY_VOLUMEDOWN:
179 		workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
180 		    (void *)(long)-1, (void *)(int)1);
181 		break;
182 	case ASUS_NOTIFY_VOLUMEUP:
183 		workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
184 		    (void *)(long)1, (void *)(int)1);
185 		break;
186 #else
187 	case ASUS_NOTIFY_VOLUMEMUTE:
188 	case ASUS_NOTIFY_VOLUMEDOWN:
189 	case ASUS_NOTIFY_VOLUMEUP:
190 		break;
191 #endif
192 	case ASUS_NOTIFY_POWERCONNECT:
193 	case ASUS_NOTIFY_POWERDISCONNECT:
194 		break;
195 
196 	case ASUS_NOTIFY_LCDSWITCHOFF0:
197 	case ASUS_NOTIFY_LCDSWITCHOFF1:
198 		break;
199 
200 	case ASUS_NOTIFY_LCDCHANGERES:
201 		break;
202 
203 	case ASUS_NOTIFY_USERDEF0:
204 	case ASUS_NOTIFY_USERDEF1:
205 		break;
206 
207 	default:
208 		printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify);
209 		break;
210 	}
211 
212 	return 0;
213 }
214 
215 int
216 acpiasus_activate(struct device *self, int act)
217 {
218 	struct acpiasus_softc *sc = (struct acpiasus_softc *)self;
219 	struct aml_value cmd;
220 	struct aml_value ret;
221 
222 	switch (act) {
223 	case DVACT_SUSPEND:
224 		break;
225 	case DVACT_RESUME:
226 		acpiasus_init(self);
227 
228 		bzero(&cmd, sizeof(cmd));
229 		cmd.type = AML_OBJTYPE_INTEGER;
230 		cmd.v_integer = ASUS_SDSP_LCD;
231 
232 		if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SDSP", 1,
233 		    &cmd, &ret))
234 			printf("%s: no SDSP\n", DEVNAME(sc));
235 		else
236 			aml_freevalue(&ret);
237 		break;
238 	}
239 	return (0);
240 }
241