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