xref: /openbsd/sys/dev/acpi/acpiasus.c (revision 404b540a)
1 /* $OpenBSD: acpiasus.c,v 1.8 2008/11/06 23:41:28 marco 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 	void 			*sc_powerhook;
60 };
61 
62 #define ASUS_NOTIFY_WIRELESSON		0x10
63 #define ASUS_NOTIFY_WIRELESSOFF		0x11
64 #define ASUS_NOTIFY_TASKSWITCH		0x12
65 #define ASUS_NOTIFY_VOLUMEMUTE		0x13
66 #define ASUS_NOTIFY_VOLUMEDOWN		0x14
67 #define ASUS_NOTIFY_VOLUMEUP		0x15
68 #define ASUS_NOTIFY_LCDSWITCHOFF0	0x16
69 #define ASUS_NOTIFY_LCDSWITCHOFF1	0x1a
70 #define ASUS_NOTIFY_LCDCHANGERES	0x1b
71 #define ASUS_NOTIFY_USERDEF0		0x1c
72 #define ASUS_NOTIFY_USERDEF1		0x1d
73 #define ASUS_NOTIFY_BRIGHTNESSLOW	0x20
74 #define ASUS_NOTIFY_BRIGHTNESSHIGH	0x2f
75 #define ASUS_NOTIFY_DISPLAYCYCLEDOWN	0x30
76 #define ASUS_NOTIFY_DISPLAYCYCLEUP	0x32
77 
78 #define ASUS_NOTIFY_POWERCONNECT	0x50
79 #define ASUS_NOTIFY_POWERDISCONNECT	0x51
80 
81 #define	ASUS_SDSP_LCD			0x01
82 #define	ASUS_SDSP_CRT			0x02
83 #define	ASUS_SDSP_TV			0x04
84 #define	ASUS_SDSP_DVI			0x08
85 #define	ASUS_SDSP_ALL \
86 	(ASUS_SDSP_LCD | ASUS_SDSP_CRT | ASUS_SDSP_TV | ASUS_SDSP_DVI)
87 
88 int	acpiasus_match(struct device *, void *, void *);
89 void	acpiasus_attach(struct device *, struct device *, void *);
90 void	acpiasus_init(struct device *);
91 int	acpiasus_notify(struct aml_node *, int, void *);
92 void	acpiasus_power(int, void *);
93 
94 #if NAUDIO > 0 && NWSKBD > 0
95 extern int wskbd_set_mixervolume(long dir);
96 #endif
97 
98 struct cfattach acpiasus_ca = {
99 	sizeof(struct acpiasus_softc), acpiasus_match, acpiasus_attach
100 };
101 
102 struct cfdriver acpiasus_cd = {
103 	NULL, "acpiasus", DV_DULL
104 };
105 
106 const char *acpiasus_hids[] = { ACPI_DEV_ASUS, 0 };
107 
108 int
109 acpiasus_match(struct device *parent, void *match, void *aux)
110 {
111 	struct acpi_attach_args *aa = aux;
112 	struct cfdata *cf = match;
113 
114 	return (acpi_matchhids(aa, acpiasus_hids, cf->cf_driver->cd_name));
115 }
116 
117 void
118 acpiasus_attach(struct device *parent, struct device *self, void *aux)
119 {
120 	struct acpiasus_softc *sc = (struct acpiasus_softc *)self;
121 	struct acpi_attach_args *aa = aux;
122 
123 	sc->sc_acpi = (struct acpi_softc *)parent;
124 	sc->sc_devnode = aa->aaa_node;
125 
126 	sc->sc_powerhook = powerhook_establish(acpiasus_power, sc);
127 
128 	printf("\n");
129 
130 	acpiasus_init(self);
131 
132 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
133 	    acpiasus_notify, sc, ACPIDEV_NOPOLL);
134 }
135 
136 void
137 acpiasus_init(struct device *self)
138 {
139 	struct acpiasus_softc *sc = (struct acpiasus_softc *)self;
140 	struct aml_value cmd;
141 	struct aml_value ret;
142 
143 	bzero(&cmd, sizeof(cmd));
144 	cmd.type = AML_OBJTYPE_INTEGER;
145 	cmd.v_integer = 0x40;		/* Disable ASL display switching. */
146 
147 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "INIT", 1, &cmd, &ret))
148 		printf("%s: no INIT\n", DEVNAME(sc));
149 	else
150 		aml_freevalue(&ret);
151 }
152 
153 int
154 acpiasus_notify(struct aml_node *node, int notify, void *arg)
155 {
156 	struct acpiasus_softc *sc = arg;
157 
158 	if (notify >= ASUS_NOTIFY_BRIGHTNESSLOW &&
159 	    notify <= ASUS_NOTIFY_BRIGHTNESSHIGH) {
160 #ifdef ACPIASUS_DEBUG
161 		printf("%s: brightness %d percent\n", DEVNAME(sc),
162 		    (notify & 0xf) * 100 / 0xf);
163 #endif
164 		return 0;
165 	}
166 
167 	switch (notify) {
168 	case ASUS_NOTIFY_WIRELESSON:	/* Handled by AML. */
169 	case ASUS_NOTIFY_WIRELESSOFF:	/* Handled by AML. */
170 		break;
171 	case ASUS_NOTIFY_TASKSWITCH:
172 		break;
173 	case ASUS_NOTIFY_DISPLAYCYCLEDOWN:
174 	case ASUS_NOTIFY_DISPLAYCYCLEUP:
175 		break;
176 #if NAUDIO > 0 && NWSKBD > 0
177 	case ASUS_NOTIFY_VOLUMEMUTE:
178 		workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
179 		    (void *)(long)0, NULL);
180 		break;
181 	case ASUS_NOTIFY_VOLUMEDOWN:
182 		workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
183 		    (void *)(long)-1, NULL);
184 		break;
185 	case ASUS_NOTIFY_VOLUMEUP:
186 		workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume,
187 		    (void *)(long)1, NULL);
188 		break;
189 #else
190 	case ASUS_NOTIFY_VOLUMEMUTE:
191 	case ASUS_NOTIFY_VOLUMEDOWN:
192 	case ASUS_NOTIFY_VOLUMEUP:
193 		break;
194 #endif
195 	case ASUS_NOTIFY_POWERCONNECT:
196 	case ASUS_NOTIFY_POWERDISCONNECT:
197 		break;
198 
199 	case ASUS_NOTIFY_LCDSWITCHOFF0:
200 	case ASUS_NOTIFY_LCDSWITCHOFF1:
201 		break;
202 
203 	case ASUS_NOTIFY_LCDCHANGERES:
204 		break;
205 
206 	case ASUS_NOTIFY_USERDEF0:
207 	case ASUS_NOTIFY_USERDEF1:
208 		break;
209 
210 	default:
211 		printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify);
212 		break;
213 	}
214 
215 	return 0;
216 }
217 
218 void
219 acpiasus_power(int why, void *arg)
220 {
221 	struct acpiasus_softc *sc = (struct acpiasus_softc *)arg;
222 	struct aml_value cmd;
223 	struct aml_value ret;
224 
225 	switch (why) {
226 	case PWR_STANDBY:
227 	case PWR_SUSPEND:
228 		break;
229 	case PWR_RESUME:
230 		acpiasus_init(arg);
231 
232 		bzero(&cmd, sizeof(cmd));
233 		cmd.type = AML_OBJTYPE_INTEGER;
234 		cmd.v_integer = ASUS_SDSP_LCD;
235 
236 		if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SDSP", 1,
237 		    &cmd, &ret))
238 			printf("%s: no SDSP\n", DEVNAME(sc));
239 		else
240 			aml_freevalue(&ret);
241 
242 		break;
243 	}
244 }
245