asus_acpi.c revision 1.3 1 /* $NetBSD: asus_acpi.c,v 1.3 2008/04/23 11:19:09 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2007, 2008 Jared D. McNeill <jmcneill (at) 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 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: asus_acpi.c,v 1.3 2008/04/23 11:19:09 jmcneill Exp $");
37
38 #include <sys/types.h>
39 #include <sys/param.h>
40 #include <sys/malloc.h>
41 #include <sys/buf.h>
42 #include <sys/callout.h>
43 #include <sys/kernel.h>
44 #include <sys/device.h>
45 #include <sys/pmf.h>
46
47 #include <dev/acpi/acpivar.h>
48
49 typedef struct asus_softc {
50 device_t sc_dev;
51 struct acpi_devnode *sc_node;
52
53 #define ASUS_PSW_DISPLAY_CYCLE 0
54 #define ASUS_PSW_LAST 1
55 struct sysmon_pswitch sc_smpsw[ASUS_PSW_LAST];
56 bool sc_smpsw_valid;
57 } asus_softc_t;
58
59 #define ASUS_NOTIFY_WirelessSwitch 0x10
60 #define ASUS_NOTIFY_BrightnessLow 0x20
61 #define ASUS_NOTIFY_BrightnessHigh 0x2f
62 #define ASUS_NOTIFY_DisplayCycle 0x30
63 #define ASUS_NOTIFY_WindowSwitch 0x12 /* XXXJDM ?? */
64 #define ASUS_NOTIFY_VolumeMute 0x13
65 #define ASUS_NOTIFY_VolumeDown 0x14
66 #define ASUS_NOTIFY_VolumeUp 0x15
67
68 #define ASUS_METHOD_SDSP "SDSP"
69 #define ASUS_SDSP_LCD 0x01
70 #define ASUS_SDSP_CRT 0x02
71 #define ASUS_SDSP_TV 0x04
72 #define ASUS_SDSP_DVI 0x08
73 #define ASUS_SDSP_ALL \
74 (ASUS_SDSP_LCD | ASUS_SDSP_CRT | ASUS_SDSP_TV | ASUS_SDSP_DVI)
75
76 static int asus_match(device_t, cfdata_t, void *);
77 static void asus_attach(device_t, device_t, void *);
78
79 static void asus_notify_handler(ACPI_HANDLE, UINT32, void *);
80
81 static void asus_init(device_t);
82 static bool asus_resume(device_t PMF_FN_PROTO);
83
84 CFATTACH_DECL_NEW(asus, sizeof(asus_softc_t),
85 asus_match, asus_attach, NULL, NULL);
86
87 static const char * const asus_ids[] = {
88 "ASUS010",
89 NULL
90 };
91
92 static int
93 asus_match(device_t parent, cfdata_t match, void *opaque)
94 {
95 struct acpi_attach_args *aa = opaque;
96
97 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
98 return 0;
99
100 return acpi_match_hid(aa->aa_node->ad_devinfo, asus_ids);
101 }
102
103 static void
104 asus_attach(device_t parent, device_t self, void *opaque)
105 {
106 asus_softc_t *sc = device_private(self);
107 struct acpi_attach_args *aa = opaque;
108 ACPI_STATUS rv;
109
110 sc->sc_node = aa->aa_node;
111 sc->sc_dev = self;
112
113 aprint_naive("\n");
114 aprint_normal("\n");
115
116 asus_init(self);
117
118 sc->sc_smpsw_valid = true;
119 sc->sc_smpsw[ASUS_PSW_DISPLAY_CYCLE].smpsw_name =
120 PSWITCH_HK_DISPLAY_CYCLE;
121 sc->sc_smpsw[ASUS_PSW_DISPLAY_CYCLE].smpsw_type =
122 PSWITCH_TYPE_HOTKEY;
123 if (sysmon_pswitch_register(&sc->sc_smpsw[ASUS_PSW_DISPLAY_CYCLE])) {
124 aprint_error_dev(self, "couldn't register with sysmon\n");
125 sc->sc_smpsw_valid = false;
126 }
127
128 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, ACPI_ALL_NOTIFY,
129 asus_notify_handler, sc);
130 if (ACPI_FAILURE(rv))
131 aprint_error_dev(self, "couldn't install notify handler: %s\n",
132 AcpiFormatException(rv));
133
134 if (!pmf_device_register(self, NULL, asus_resume))
135 aprint_error_dev(self, "couldn't establish power handler\n");
136 }
137
138 static void
139 asus_notify_handler(ACPI_HANDLE hdl, UINT32 notify, void *opaque)
140 {
141 asus_softc_t *sc = opaque;
142
143 if (notify >= ASUS_NOTIFY_BrightnessLow &&
144 notify <= ASUS_NOTIFY_BrightnessHigh) {
145 aprint_debug_dev(sc->sc_dev, "brightness %d percent\n",
146 (notify & 0xf) * 100 / 0xf);
147 return;
148 }
149
150 switch (notify) {
151 case ASUS_NOTIFY_WirelessSwitch: /* handled by AML */
152 case ASUS_NOTIFY_WindowSwitch: /* XXXJDM what is this? */
153 break;
154 case ASUS_NOTIFY_DisplayCycle:
155 if (sc->sc_smpsw_valid == false)
156 break;
157 sysmon_pswitch_event(&sc->sc_smpsw[ASUS_PSW_DISPLAY_CYCLE],
158 PSWITCH_EVENT_PRESSED);
159 break;
160 case ASUS_NOTIFY_VolumeMute:
161 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_TOGGLE);
162 break;
163 case ASUS_NOTIFY_VolumeDown:
164 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_DOWN);
165 break;
166 case ASUS_NOTIFY_VolumeUp:
167 pmf_event_inject(NULL, PMFE_AUDIO_VOLUME_UP);
168 break;
169 default:
170 aprint_debug_dev(sc->sc_dev, "unknown event 0x%02x\n", notify);
171 break;
172 }
173 }
174
175 static void
176 asus_init(device_t self)
177 {
178 asus_softc_t *sc = device_private(self);
179 ACPI_STATUS rv;
180 ACPI_OBJECT param;
181 ACPI_OBJECT_LIST params;
182 ACPI_BUFFER ret;
183
184 ret.Pointer = NULL;
185 ret.Length = ACPI_ALLOCATE_BUFFER;
186 param.Type = ACPI_TYPE_INTEGER;
187 param.Integer.Value = 0x40; /* disable ASL display switching */
188 params.Pointer = ¶m;
189 params.Count = 1;
190
191 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "INIT",
192 ¶ms, &ret);
193 if (ACPI_FAILURE(rv))
194 aprint_error_dev(self, "couldn't evaluate INIT: %s\n",
195 AcpiFormatException(rv));
196 }
197
198 static bool
199 asus_resume(device_t self PMF_FN_ARGS)
200 {
201 asus_softc_t *sc = device_private(self);
202 ACPI_STATUS rv;
203 ACPI_OBJECT param;
204 ACPI_OBJECT_LIST params;
205 ACPI_BUFFER ret;
206
207 asus_init(self);
208
209 ret.Pointer = NULL;
210 ret.Length = ACPI_ALLOCATE_BUFFER;
211 param.Type = ACPI_TYPE_INTEGER;
212 param.Integer.Value = ASUS_SDSP_LCD;
213 params.Pointer = ¶m;
214 params.Count = 1;
215
216 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, ASUS_METHOD_SDSP,
217 ¶ms, &ret);
218 if (ACPI_FAILURE(rv))
219 aprint_error_dev(self, "couldn't evaluate SDSP: %s\n",
220 AcpiFormatException(rv));
221
222 return true;
223 }
224