sony_acpi.c revision 1.1 1 /* $NetBSD: sony_acpi.c,v 1.1 2007/12/23 17:29:26 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: sony_acpi.c,v 1.1 2007/12/23 17:29:26 jmcneill Exp $");
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/proc.h>
45 #include <sys/kernel.h>
46 #include <sys/callout.h>
47 #include <sys/sysctl.h>
48
49 #include <machine/bus.h>
50
51 #include <dev/acpi/acpica.h>
52 #include <dev/acpi/acpivar.h>
53
54 #define SONY_NOTIFY_FnKeyEvent 0x92
55 #define SONY_NOTIFY_BrightnessDownPressed 0x85
56 #define SONY_NOTIFY_BrightnessDownReleased 0x05
57 #define SONY_NOTIFY_BrightnessUpPressed 0x86
58 #define SONY_NOTIFY_BrightnessUpReleased 0x06
59 #define SONY_NOTIFY_DisplaySwitchPressed 0x87
60 #define SONY_NOTIFY_DisplaySwitchReleased 0x07
61 #define SONY_NOTIFY_ZoomPressed 0x8a
62 #define SONY_NOTIFY_ZoomReleased 0x0a
63 #define SONY_NOTIFY_SuspendPressed 0x8c
64 #define SONY_NOTIFY_SuspendReleased 0x0c
65
66 struct sony_acpi_softc {
67 struct device sc_dev;
68 struct sysctllog *sc_log;
69 struct acpi_devnode *sc_node;
70
71 #define SONY_PSW_SLEEP 0
72 #define SONY_PSW_DISPLAY_CYCLE 1
73 #define SONY_PSW_ZOOM 2
74 #define SONY_PSW_LAST 3
75 struct sysmon_pswitch sc_smpsw[SONY_PSW_LAST];
76 int sc_smpsw_valid;
77
78 struct sony_acpi_pmstate {
79 ACPI_INTEGER brt;
80 } sc_pmstate;
81 };
82
83 static const char * const sony_acpi_ids[] = {
84 "SNY5001",
85 NULL
86 };
87
88 #define SONY_ACPI_QUIRK_FNINIT 0x01
89
90 static const struct sony_acpi_quirk_table {
91 const char * product_name;
92 int quirks;
93 } sony_acpi_quirks[] = {
94 { "VGN-N250E", SONY_ACPI_QUIRK_FNINIT },
95 { NULL, -1 }
96 };
97
98 static int sony_acpi_match(struct device *, struct cfdata *, void *);
99 static void sony_acpi_attach(struct device *, struct device *, void *);
100 static ACPI_STATUS sony_acpi_eval_set_integer(ACPI_HANDLE, const char *,
101 ACPI_INTEGER, ACPI_INTEGER *);
102 static void sony_acpi_quirk_setup(struct sony_acpi_softc *);
103 static void sony_acpi_notify_handler(ACPI_HANDLE, UINT32, void *);
104 static bool sony_acpi_suspend(device_t);
105 static bool sony_acpi_resume(device_t);
106 static void sony_acpi_brightness_down(device_t);
107 static void sony_acpi_brightness_up(device_t);
108
109 CFATTACH_DECL(sony_acpi, sizeof(struct sony_acpi_softc),
110 sony_acpi_match, sony_acpi_attach, NULL, NULL);
111
112 static int
113 sony_acpi_match(struct device *parent, struct cfdata *match,
114 void *aux)
115 {
116 struct acpi_attach_args *aa = aux;
117
118 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
119 return 0;
120
121 return acpi_match_hid(aa->aa_node->ad_devinfo, sony_acpi_ids);
122 }
123
124 static int
125 sony_sysctl_helper(SYSCTLFN_ARGS)
126 {
127 struct sysctlnode node;
128 ACPI_INTEGER acpi_val;
129 ACPI_STATUS rv;
130 int val, old_val, error;
131 char buf[SYSCTL_NAMELEN + 1], *ptr;
132 struct sony_acpi_softc *sc = rnode->sysctl_data;
133
134 (void)snprintf(buf, sizeof(buf), "G%s", rnode->sysctl_name);
135 for (ptr = buf; *ptr; ptr++)
136 *ptr = toupper(*ptr);
137
138 rv = acpi_eval_integer(sc->sc_node->ad_handle, buf, &acpi_val);
139 if (ACPI_FAILURE(rv)) {
140 #ifdef DIAGNOSTIC
141 printf("%s: couldn't get `%s'\n", device_xname(&sc->sc_dev), buf);
142 #endif
143 return EIO;
144 }
145 val = old_val = acpi_val;
146
147 node = *rnode;
148 node.sysctl_data = &val;
149
150 error = sysctl_lookup(SYSCTLFN_CALL(&node));
151 if (error || newp == NULL)
152 return error;
153
154 (void)snprintf(buf, sizeof(buf), "S%s", rnode->sysctl_name);
155 acpi_val = val;
156 rv = sony_acpi_eval_set_integer(sc->sc_node->ad_handle, buf,
157 acpi_val, NULL);
158 if (ACPI_FAILURE(rv)) {
159 #ifdef DIAGNOSTIC
160 printf("%s: couldn't set `%s' to %d\n",
161 device_xname(&sc->sc_dev), buf, val);
162 #endif
163 return EIO;
164 }
165 return 0;
166 }
167
168 static ACPI_STATUS
169 sony_walk_cb(ACPI_HANDLE hnd, UINT32 v, void *context,
170 void **status)
171 {
172 struct sony_acpi_softc *sc = (void*)context;
173 const struct sysctlnode *node, *snode;
174 const char *name = acpi_name(hnd);
175 ACPI_INTEGER acpi_val;
176 char buf[SYSCTL_NAMELEN + 1], *ptr;
177 int rv;
178
179 if ((name = strrchr(name, '.')) == NULL)
180 return AE_OK;
181
182 name++;
183 if ((*name != 'G') && (*name != 'S'))
184 return AE_OK;
185
186 (void)strlcpy(buf, name, sizeof(buf));
187 *buf = 'G';
188
189 /*
190 * We assume that if the 'get' of the name as an integer is
191 * successful it is ok.
192 */
193 if (acpi_eval_integer(sc->sc_node->ad_handle, buf, &acpi_val))
194 return AE_OK;
195
196 for (ptr = buf; *ptr; ptr++)
197 *ptr = tolower(*ptr);
198
199 if ((rv = sysctl_createv(&sc->sc_log, 0, NULL, &node, CTLFLAG_PERMANENT,
200 CTLTYPE_NODE, "hw", NULL, NULL, 0, NULL, 0, CTL_HW, CTL_EOL)) != 0)
201 goto out;
202
203 if ((rv = sysctl_createv(&sc->sc_log, 0, &node, &snode, 0,
204 CTLTYPE_NODE, device_xname(&sc->sc_dev),
205 SYSCTL_DESCR("sony controls"),
206 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
207 goto out;
208
209 if ((rv = sysctl_createv(&sc->sc_log, 0, &snode, &node,
210 CTLFLAG_READWRITE, CTLTYPE_INT, buf + 1, NULL,
211 sony_sysctl_helper, 0, sc, 0, CTL_CREATE, CTL_EOL)) != 0)
212 goto out;
213
214 out:
215 #ifdef DIAGNOSTIC
216 if (rv)
217 printf("%s: sysctl_createv failed (rv = %d)\n",
218 device_xname(&sc->sc_dev), rv);
219 #endif
220 return AE_OK;
221 }
222
223 ACPI_STATUS
224 sony_acpi_eval_set_integer(ACPI_HANDLE handle, const char *path,
225 ACPI_INTEGER val, ACPI_INTEGER *valp)
226 {
227 ACPI_STATUS rv;
228 ACPI_BUFFER buf;
229 ACPI_OBJECT param, ret_val;
230 ACPI_OBJECT_LIST params;
231
232 if (handle == NULL)
233 handle = ACPI_ROOT_OBJECT;
234
235 params.Count = 1;
236 params.Pointer = ¶m;
237
238 param.Type = ACPI_TYPE_INTEGER;
239 param.Integer.Value = val;
240
241 buf.Pointer = &ret_val;
242 buf.Length = sizeof(ret_val);
243
244 rv = AcpiEvaluateObjectTyped(handle, path, ¶ms, &buf,
245 ACPI_TYPE_INTEGER);
246
247 if (ACPI_SUCCESS(rv) && valp)
248 *valp = ret_val.Integer.Value;
249
250 return rv;
251 }
252
253 static void
254 sony_acpi_attach(struct device *parent, struct device *self, void *aux)
255 {
256 struct sony_acpi_softc *sc = (void *)self;
257 struct acpi_attach_args *aa = aux;
258 ACPI_STATUS rv;
259 int i;
260
261 aprint_naive(": Sony Miscellaneous Controller\n");
262 aprint_normal(": Sony Miscellaneous Controller\n");
263
264 sc->sc_node = aa->aa_node;
265
266 sony_acpi_quirk_setup(sc);
267
268 /* Configure suspend button and hotkeys */
269 sc->sc_smpsw[SONY_PSW_SLEEP].smpsw_name = sc->sc_dev.dv_xname;
270 sc->sc_smpsw[SONY_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP;
271 sc->sc_smpsw[SONY_PSW_DISPLAY_CYCLE].smpsw_name =
272 PSWITCH_HK_DISPLAY_CYCLE;
273 sc->sc_smpsw[SONY_PSW_DISPLAY_CYCLE].smpsw_type = PSWITCH_TYPE_HOTKEY;
274 sc->sc_smpsw[SONY_PSW_ZOOM].smpsw_name = PSWITCH_HK_ZOOM_BUTTON;
275 sc->sc_smpsw[SONY_PSW_ZOOM].smpsw_type = PSWITCH_TYPE_HOTKEY;
276 sc->sc_smpsw_valid = 1;
277
278 for (i = 0; i < SONY_PSW_LAST; i++)
279 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) {
280 aprint_error("%s: couldn't register %s with sysmon\n",
281 device_xname(self), sc->sc_smpsw[i].smpsw_name);
282 sc->sc_smpsw_valid = 0;
283 }
284
285 /* Install notify handler */
286 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle,
287 ACPI_DEVICE_NOTIFY, sony_acpi_notify_handler, self);
288 if (ACPI_FAILURE(rv))
289 aprint_error("%s: couldn't install notify handler (%d)\n",
290 device_xname(self), rv);
291
292 /* Install sysctl handler */
293 rv = AcpiWalkNamespace(ACPI_TYPE_METHOD,
294 sc->sc_node->ad_handle, 1, sony_walk_cb, sc, NULL);
295 #ifdef DIAGNOSTIC
296 if (ACPI_FAILURE(rv))
297 aprint_error("%s: Cannot walk ACPI namespace (%d)\n",
298 device_xname(self), rv);
299 #endif
300
301 if (!pmf_device_register(self, sony_acpi_suspend, sony_acpi_resume))
302 aprint_error_dev(self, "couldn't establish power handler\n");
303
304 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
305 sony_acpi_brightness_up, true))
306 aprint_error_dev(self, "couldn't register BRIGHTNESS UP handler\n");
307
308 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
309 sony_acpi_brightness_down, true))
310 aprint_error_dev(self, "couldn't register BRIGHTNESS DOWN handler\n");
311 }
312
313 static void
314 sony_acpi_quirk_setup(struct sony_acpi_softc *sc)
315 {
316 const char *product_name;
317 ACPI_HANDLE hdl;
318 int i;
319
320 hdl = sc->sc_node->ad_handle;
321
322 product_name = pmf_get_platform("system-product-name");
323 if (product_name == NULL)
324 return;
325
326 for (i = 0; sony_acpi_quirks[i].product_name != NULL; i++)
327 if (strcmp(sony_acpi_quirks[i].product_name, product_name) == 0)
328 break;
329
330 if (sony_acpi_quirks[i].product_name == NULL)
331 return;
332
333 if (sony_acpi_quirks[i].quirks & SONY_ACPI_QUIRK_FNINIT) {
334 /* Initialize extra Fn keys */
335 sony_acpi_eval_set_integer(hdl, "SN02", 0x04, NULL);
336 sony_acpi_eval_set_integer(hdl, "SN07", 0x02, NULL);
337 sony_acpi_eval_set_integer(hdl, "SN02", 0x10, NULL);
338 sony_acpi_eval_set_integer(hdl, "SN07", 0x00, NULL);
339 sony_acpi_eval_set_integer(hdl, "SN03", 0x02, NULL);
340 sony_acpi_eval_set_integer(hdl, "SN07", 0x101, NULL);
341 }
342
343 return;
344 }
345
346 static void
347 sony_acpi_notify_handler(ACPI_HANDLE hdl, UINT32 notify, void *opaque)
348 {
349 device_t dv = opaque;
350 struct sony_acpi_softc *sc = device_private(dv);
351 ACPI_STATUS rv;
352 ACPI_INTEGER arg;
353 int s;
354
355 if (notify == SONY_NOTIFY_FnKeyEvent) {
356 rv = sony_acpi_eval_set_integer(hdl, "SN07", 0x202, &arg);
357 if (ACPI_FAILURE(rv))
358 return;
359
360 notify = arg & 0xff;
361 }
362
363 s = spltty();
364 switch (notify) {
365 case SONY_NOTIFY_BrightnessDownPressed:
366 sony_acpi_brightness_down(dv);
367 break;
368 case SONY_NOTIFY_BrightnessUpPressed:
369 sony_acpi_brightness_up(dv);
370 break;
371 case SONY_NOTIFY_BrightnessDownReleased:
372 case SONY_NOTIFY_BrightnessUpReleased:
373 break;
374 case SONY_NOTIFY_SuspendPressed:
375 if (!sc->sc_smpsw_valid)
376 break;
377 sysmon_pswitch_event(&sc->sc_smpsw[SONY_PSW_SLEEP],
378 PSWITCH_EVENT_PRESSED);
379 break;
380 case SONY_NOTIFY_SuspendReleased:
381 break;
382 case SONY_NOTIFY_DisplaySwitchPressed:
383 if (!sc->sc_smpsw_valid)
384 break;
385 sysmon_pswitch_event(&sc->sc_smpsw[SONY_PSW_DISPLAY_CYCLE],
386 PSWITCH_EVENT_PRESSED);
387 break;
388 case SONY_NOTIFY_DisplaySwitchReleased:
389 break;
390 case SONY_NOTIFY_ZoomPressed:
391 if (!sc->sc_smpsw_valid)
392 break;
393 sysmon_pswitch_event(&sc->sc_smpsw[SONY_PSW_ZOOM],
394 PSWITCH_EVENT_PRESSED);
395 break;
396 case SONY_NOTIFY_ZoomReleased:
397 break;
398 default:
399 printf("%s: unknown notify event 0x%x\n",
400 device_xname(&sc->sc_dev), notify);
401 break;
402 }
403 splx(s);
404
405 return;
406 }
407
408 static bool
409 sony_acpi_suspend(device_t dv)
410 {
411 struct sony_acpi_softc *sc = device_private(dv);
412
413 acpi_eval_integer(sc->sc_node->ad_handle, "GBRT", &sc->sc_pmstate.brt);
414
415 return true;
416 }
417
418 static bool
419 sony_acpi_resume(device_t dv)
420 {
421 struct sony_acpi_softc *sc = device_private(dv);
422
423 sony_acpi_eval_set_integer(sc->sc_node->ad_handle, "SBRT",
424 sc->sc_pmstate.brt, NULL);
425 sony_acpi_quirk_setup(sc);
426
427 return true;
428 }
429
430 static void
431 sony_acpi_brightness_up(device_t dv)
432 {
433 struct sony_acpi_softc *sc = device_private(dv);
434 ACPI_INTEGER arg;
435 ACPI_STATUS rv;
436
437 rv = acpi_eval_integer(sc->sc_node->ad_handle, "GBRT", &arg);
438 if (ACPI_FAILURE(rv) || arg > 8)
439 return;
440 arg++;
441 sony_acpi_eval_set_integer(sc->sc_node->ad_handle, "SBRT", arg, NULL);
442
443 return;
444 }
445
446 static void
447 sony_acpi_brightness_down(device_t dv)
448 {
449 struct sony_acpi_softc *sc = device_private(dv);
450 ACPI_INTEGER arg;
451 ACPI_STATUS rv;
452
453 rv = acpi_eval_integer(sc->sc_node->ad_handle, "GBRT", &arg);
454 if (ACPI_FAILURE(rv) || arg == 0)
455 return;
456 arg--;
457 sony_acpi_eval_set_integer(sc->sc_node->ad_handle, "SBRT", arg, NULL);
458
459 return;
460 }
461