1 /* $NetBSD: sony_acpi.c,v 1.11 2009/09/16 10:47:55 mlelstv Exp $ */
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: sony_acpi.c,v 1.11 2009/09/16 10:47:55 mlelstv Exp $");
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/device.h>
38 #include <sys/kernel.h>
39 #include <sys/callout.h>
40 #include <sys/sysctl.h>
42 #include <machine/bus.h>
44 #include <dev/acpi/acpica.h>
45 #include <dev/acpi/acpivar.h>
46 #include <dev/acpi/acpireg.h>
48 #define _COMPONENT ACPI_RESOURCE_COMPONENT
49 ACPI_MODULE_NAME ("sony_acpi")
51 #define SONY_NOTIFY_FnKeyEvent 0x92
52 #define SONY_NOTIFY_BrightnessDownPressed 0x85
53 #define SONY_NOTIFY_BrightnessDownReleased 0x05
54 #define SONY_NOTIFY_BrightnessUpPressed 0x86
55 #define SONY_NOTIFY_BrightnessUpReleased 0x06
56 #define SONY_NOTIFY_DisplaySwitchPressed 0x87
57 #define SONY_NOTIFY_DisplaySwitchReleased 0x07
58 #define SONY_NOTIFY_ZoomPressed 0x8a
59 #define SONY_NOTIFY_ZoomReleased 0x0a
60 #define SONY_NOTIFY_SuspendPressed 0x8c
61 #define SONY_NOTIFY_SuspendReleased 0x0c
63 struct sony_acpi_softc
{
65 struct sysctllog
*sc_log
;
66 struct acpi_devnode
*sc_node
;
68 #define SONY_PSW_SLEEP 0
69 #define SONY_PSW_DISPLAY_CYCLE 1
70 #define SONY_PSW_ZOOM 2
71 #define SONY_PSW_LAST 3
72 struct sysmon_pswitch sc_smpsw
[SONY_PSW_LAST
];
75 #define SONY_ACPI_QUIRK_FNINIT 0x01
79 struct sony_acpi_pmstate
{
84 static const char * const sony_acpi_ids
[] = {
89 static int sony_acpi_match(device_t
, cfdata_t
, void *);
90 static void sony_acpi_attach(device_t
, device_t
, void *);
91 static ACPI_STATUS
sony_acpi_eval_set_integer(ACPI_HANDLE
, const char *,
92 ACPI_INTEGER
, ACPI_INTEGER
*);
93 static void sony_acpi_quirk_setup(struct sony_acpi_softc
*);
94 static void sony_acpi_notify_handler(ACPI_HANDLE
, UINT32
, void *);
95 static bool sony_acpi_suspend(device_t
, pmf_qual_t
);
96 static bool sony_acpi_resume(device_t
, pmf_qual_t
);
97 static void sony_acpi_brightness_down(device_t
);
98 static void sony_acpi_brightness_up(device_t
);
99 static ACPI_STATUS
sony_acpi_find_pic(ACPI_HANDLE
, UINT32
, void *, void **);
101 CFATTACH_DECL_NEW(sony_acpi
, sizeof(struct sony_acpi_softc
),
102 sony_acpi_match
, sony_acpi_attach
, NULL
, NULL
);
105 sony_acpi_match(device_t parent
, cfdata_t match
, void *aux
)
107 struct acpi_attach_args
*aa
= aux
;
109 if (aa
->aa_node
->ad_type
!= ACPI_TYPE_DEVICE
)
112 return acpi_match_hid(aa
->aa_node
->ad_devinfo
, sony_acpi_ids
);
116 sony_sysctl_helper(SYSCTLFN_ARGS
)
118 struct sysctlnode node
;
119 ACPI_INTEGER acpi_val
;
121 int val
, old_val
, error
;
122 char buf
[SYSCTL_NAMELEN
+ 1], *ptr
;
123 struct sony_acpi_softc
*sc
= rnode
->sysctl_data
;
125 (void)snprintf(buf
, sizeof(buf
), "G%s", rnode
->sysctl_name
);
126 for (ptr
= buf
; *ptr
; ptr
++)
127 *ptr
= toupper((unsigned char)*ptr
);
129 rv
= acpi_eval_integer(sc
->sc_node
->ad_handle
, buf
, &acpi_val
);
130 if (ACPI_FAILURE(rv
)) {
132 printf("%s: couldn't get `%s'\n", device_xname(sc
->sc_dev
), buf
);
136 val
= old_val
= acpi_val
;
139 node
.sysctl_data
= &val
;
141 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
142 if (error
|| newp
== NULL
)
147 rv
= sony_acpi_eval_set_integer(sc
->sc_node
->ad_handle
, buf
,
149 if (ACPI_FAILURE(rv
)) {
151 printf("%s: couldn't set `%s' to %d\n",
152 device_xname(sc
->sc_dev
), buf
, val
);
160 sony_walk_cb(ACPI_HANDLE hnd
, UINT32 v
, void *context
, void **status
)
162 struct sony_acpi_softc
*sc
= (void *)context
;
163 const struct sysctlnode
*node
, *snode
;
164 const char *name
= acpi_name(hnd
);
165 ACPI_INTEGER acpi_val
;
166 char buf
[SYSCTL_NAMELEN
+ 1], *ptr
;
169 if ((name
= strrchr(name
, '.')) == NULL
)
173 if ((*name
!= 'G') && (*name
!= 'S'))
176 (void)strlcpy(buf
, name
, sizeof(buf
));
180 * We assume that if the 'get' of the name as an integer is
181 * successful it is ok.
183 if (acpi_eval_integer(sc
->sc_node
->ad_handle
, buf
, &acpi_val
))
186 for (ptr
= buf
; *ptr
; ptr
++)
187 *ptr
= tolower(*ptr
);
189 if ((rv
= sysctl_createv(&sc
->sc_log
, 0, NULL
, &node
, CTLFLAG_PERMANENT
,
190 CTLTYPE_NODE
, "hw", NULL
, NULL
, 0, NULL
, 0, CTL_HW
, CTL_EOL
)) != 0)
193 if ((rv
= sysctl_createv(&sc
->sc_log
, 0, &node
, &snode
, 0,
194 CTLTYPE_NODE
, device_xname(sc
->sc_dev
),
195 SYSCTL_DESCR("sony controls"),
196 NULL
, 0, NULL
, 0, CTL_CREATE
, CTL_EOL
)) != 0)
199 if ((rv
= sysctl_createv(&sc
->sc_log
, 0, &snode
, &node
,
200 CTLFLAG_READWRITE
, CTLTYPE_INT
, buf
+ 1, NULL
,
201 sony_sysctl_helper
, 0, sc
, 0, CTL_CREATE
, CTL_EOL
)) != 0)
207 printf("%s: sysctl_createv failed (rv = %d)\n",
208 device_xname(sc
->sc_dev
), rv
);
214 sony_acpi_eval_set_integer(ACPI_HANDLE handle
, const char *path
,
215 ACPI_INTEGER val
, ACPI_INTEGER
*valp
)
219 ACPI_OBJECT param
, ret_val
;
220 ACPI_OBJECT_LIST params
;
223 handle
= ACPI_ROOT_OBJECT
;
226 params
.Pointer
= ¶m
;
228 param
.Type
= ACPI_TYPE_INTEGER
;
229 param
.Integer
.Value
= val
;
231 buf
.Pointer
= &ret_val
;
232 buf
.Length
= sizeof(ret_val
);
234 rv
= AcpiEvaluateObjectTyped(handle
, path
, ¶ms
, &buf
,
237 if (ACPI_SUCCESS(rv
) && valp
)
238 *valp
= ret_val
.Integer
.Value
;
244 sony_acpi_attach(device_t parent
, device_t self
, void *aux
)
246 struct sony_acpi_softc
*sc
= device_private(self
);
247 struct acpi_attach_args
*aa
= aux
;
251 aprint_naive(": Sony Miscellaneous Controller\n");
252 aprint_normal(": Sony Miscellaneous Controller\n");
254 sc
->sc_node
= aa
->aa_node
;
257 rv
= AcpiWalkNamespace(ACPI_TYPE_DEVICE
, ACPI_ROOT_OBJECT
, 100,
258 sony_acpi_find_pic
, sc
, NULL
);
259 if (ACPI_FAILURE(rv
))
260 aprint_error_dev(self
, "couldn't walk namespace: %s\n",
261 AcpiFormatException(rv
));
264 * If we don't find an SNY6001 device, assume that we need the
265 * Fn key initialization sequence.
267 if (sc
->sc_has_pic
== false)
268 sc
->sc_quirks
|= SONY_ACPI_QUIRK_FNINIT
;
270 sony_acpi_quirk_setup(sc
);
272 /* Configure suspend button and hotkeys */
273 sc
->sc_smpsw
[SONY_PSW_SLEEP
].smpsw_name
= device_xname(self
);
274 sc
->sc_smpsw
[SONY_PSW_SLEEP
].smpsw_type
= PSWITCH_TYPE_SLEEP
;
275 sc
->sc_smpsw
[SONY_PSW_DISPLAY_CYCLE
].smpsw_name
=
276 PSWITCH_HK_DISPLAY_CYCLE
;
277 sc
->sc_smpsw
[SONY_PSW_DISPLAY_CYCLE
].smpsw_type
= PSWITCH_TYPE_HOTKEY
;
278 sc
->sc_smpsw
[SONY_PSW_ZOOM
].smpsw_name
= PSWITCH_HK_ZOOM_BUTTON
;
279 sc
->sc_smpsw
[SONY_PSW_ZOOM
].smpsw_type
= PSWITCH_TYPE_HOTKEY
;
280 sc
->sc_smpsw_valid
= 1;
282 for (i
= 0; i
< SONY_PSW_LAST
; i
++)
283 if (sysmon_pswitch_register(&sc
->sc_smpsw
[i
]) != 0) {
284 aprint_error_dev(self
,
285 "couldn't register %s with sysmon\n",
286 sc
->sc_smpsw
[i
].smpsw_name
);
287 sc
->sc_smpsw_valid
= 0;
290 /* Install notify handler */
291 rv
= AcpiInstallNotifyHandler(sc
->sc_node
->ad_handle
,
292 ACPI_DEVICE_NOTIFY
, sony_acpi_notify_handler
, self
);
293 if (ACPI_FAILURE(rv
))
294 aprint_error_dev(self
,
295 "couldn't install notify handler (%d)\n", rv
);
297 /* Install sysctl handler */
298 rv
= AcpiWalkNamespace(ACPI_TYPE_METHOD
,
299 sc
->sc_node
->ad_handle
, 1, sony_walk_cb
, sc
, NULL
);
301 if (ACPI_FAILURE(rv
))
302 aprint_error_dev(self
, "Cannot walk ACPI namespace (%d)\n",
306 if (!pmf_device_register(self
, sony_acpi_suspend
, sony_acpi_resume
))
307 aprint_error_dev(self
, "couldn't establish power handler\n");
309 if (!pmf_event_register(self
, PMFE_DISPLAY_BRIGHTNESS_UP
,
310 sony_acpi_brightness_up
, true))
311 aprint_error_dev(self
, "couldn't register BRIGHTNESS UP handler\n");
313 if (!pmf_event_register(self
, PMFE_DISPLAY_BRIGHTNESS_DOWN
,
314 sony_acpi_brightness_down
, true))
315 aprint_error_dev(self
, "couldn't register BRIGHTNESS DOWN handler\n");
319 sony_acpi_quirk_setup(struct sony_acpi_softc
*sc
)
321 ACPI_HANDLE hdl
= sc
->sc_node
->ad_handle
;
323 if (sc
->sc_quirks
& SONY_ACPI_QUIRK_FNINIT
) {
324 /* Initialize extra Fn keys */
325 sony_acpi_eval_set_integer(hdl
, "SN02", 0x04, NULL
);
326 sony_acpi_eval_set_integer(hdl
, "SN07", 0x02, NULL
);
327 sony_acpi_eval_set_integer(hdl
, "SN02", 0x10, NULL
);
328 sony_acpi_eval_set_integer(hdl
, "SN07", 0x00, NULL
);
329 sony_acpi_eval_set_integer(hdl
, "SN03", 0x02, NULL
);
330 sony_acpi_eval_set_integer(hdl
, "SN07", 0x101, NULL
);
335 sony_acpi_notify_handler(ACPI_HANDLE hdl
, UINT32 notify
, void *opaque
)
337 device_t dv
= opaque
;
338 struct sony_acpi_softc
*sc
= device_private(dv
);
343 if (notify
== SONY_NOTIFY_FnKeyEvent
) {
344 rv
= sony_acpi_eval_set_integer(hdl
, "SN07", 0x202, &arg
);
345 if (ACPI_FAILURE(rv
))
353 case SONY_NOTIFY_BrightnessDownPressed
:
354 sony_acpi_brightness_down(dv
);
356 case SONY_NOTIFY_BrightnessUpPressed
:
357 sony_acpi_brightness_up(dv
);
359 case SONY_NOTIFY_BrightnessDownReleased
:
360 case SONY_NOTIFY_BrightnessUpReleased
:
362 case SONY_NOTIFY_SuspendPressed
:
363 if (!sc
->sc_smpsw_valid
)
365 sysmon_pswitch_event(&sc
->sc_smpsw
[SONY_PSW_SLEEP
],
366 PSWITCH_EVENT_PRESSED
);
368 case SONY_NOTIFY_SuspendReleased
:
370 case SONY_NOTIFY_DisplaySwitchPressed
:
371 if (!sc
->sc_smpsw_valid
)
373 sysmon_pswitch_event(&sc
->sc_smpsw
[SONY_PSW_DISPLAY_CYCLE
],
374 PSWITCH_EVENT_PRESSED
);
376 case SONY_NOTIFY_DisplaySwitchReleased
:
378 case SONY_NOTIFY_ZoomPressed
:
379 if (!sc
->sc_smpsw_valid
)
381 sysmon_pswitch_event(&sc
->sc_smpsw
[SONY_PSW_ZOOM
],
382 PSWITCH_EVENT_PRESSED
);
384 case SONY_NOTIFY_ZoomReleased
:
387 aprint_debug_dev(dv
, "unknown notify event 0x%x\n",
395 sony_acpi_suspend(device_t dv
, pmf_qual_t qual
)
397 struct sony_acpi_softc
*sc
= device_private(dv
);
399 acpi_eval_integer(sc
->sc_node
->ad_handle
, "GBRT", &sc
->sc_pmstate
.brt
);
405 sony_acpi_resume(device_t dv
, pmf_qual_t qual
)
407 struct sony_acpi_softc
*sc
= device_private(dv
);
409 sony_acpi_eval_set_integer(sc
->sc_node
->ad_handle
, "SBRT",
410 sc
->sc_pmstate
.brt
, NULL
);
411 sony_acpi_quirk_setup(sc
);
417 sony_acpi_brightness_up(device_t dv
)
419 struct sony_acpi_softc
*sc
= device_private(dv
);
423 rv
= acpi_eval_integer(sc
->sc_node
->ad_handle
, "GBRT", &arg
);
424 if (ACPI_FAILURE(rv
))
430 sony_acpi_eval_set_integer(sc
->sc_node
->ad_handle
, "SBRT", arg
, NULL
);
434 sony_acpi_brightness_down(device_t dv
)
436 struct sony_acpi_softc
*sc
= device_private(dv
);
440 rv
= acpi_eval_integer(sc
->sc_node
->ad_handle
, "GBRT", &arg
);
441 if (ACPI_FAILURE(rv
))
447 sony_acpi_eval_set_integer(sc
->sc_node
->ad_handle
, "SBRT", arg
, NULL
);
451 sony_acpi_find_pic(ACPI_HANDLE hdl
, UINT32 level
, void *opaque
, void **status
)
453 struct sony_acpi_softc
*sc
= opaque
;
455 ACPI_DEVICE_INFO
*devinfo
;
457 rv
= AcpiGetObjectInfo(hdl
, &devinfo
);
458 if (ACPI_FAILURE(rv
) || devinfo
== NULL
)
459 return AE_OK
; /* we don't want to stop searching */
461 if (devinfo
->HardwareId
.String
&&
462 strncmp(devinfo
->HardwareId
.String
, "SNY6001", 7) == 0)
463 sc
->sc_has_pic
= true;