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@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