1 /* $OpenBSD: acpiasus.c,v 1.8 2008/11/06 23:41:28 marco Exp $ */ 2 /* $NetBSD: asus_acpi.c,v 1.2.2.2 2008/04/03 12:42:37 mjf Exp $ */ 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 /* 36 * ASUS ACPI hotkeys driver. 37 */ 38 39 #include <sys/param.h> 40 #include <sys/device.h> 41 #include <sys/systm.h> 42 #include <sys/workq.h> 43 44 #include <dev/acpi/acpireg.h> 45 #include <dev/acpi/acpivar.h> 46 #include <dev/acpi/acpidev.h> 47 #include <dev/acpi/amltypes.h> 48 #include <dev/acpi/dsdt.h> 49 50 #include "audio.h" 51 #include "wskbd.h" 52 53 struct acpiasus_softc { 54 struct device sc_dev; 55 56 struct acpi_softc *sc_acpi; 57 struct aml_node *sc_devnode; 58 59 void *sc_powerhook; 60 }; 61 62 #define ASUS_NOTIFY_WIRELESSON 0x10 63 #define ASUS_NOTIFY_WIRELESSOFF 0x11 64 #define ASUS_NOTIFY_TASKSWITCH 0x12 65 #define ASUS_NOTIFY_VOLUMEMUTE 0x13 66 #define ASUS_NOTIFY_VOLUMEDOWN 0x14 67 #define ASUS_NOTIFY_VOLUMEUP 0x15 68 #define ASUS_NOTIFY_LCDSWITCHOFF0 0x16 69 #define ASUS_NOTIFY_LCDSWITCHOFF1 0x1a 70 #define ASUS_NOTIFY_LCDCHANGERES 0x1b 71 #define ASUS_NOTIFY_USERDEF0 0x1c 72 #define ASUS_NOTIFY_USERDEF1 0x1d 73 #define ASUS_NOTIFY_BRIGHTNESSLOW 0x20 74 #define ASUS_NOTIFY_BRIGHTNESSHIGH 0x2f 75 #define ASUS_NOTIFY_DISPLAYCYCLEDOWN 0x30 76 #define ASUS_NOTIFY_DISPLAYCYCLEUP 0x32 77 78 #define ASUS_NOTIFY_POWERCONNECT 0x50 79 #define ASUS_NOTIFY_POWERDISCONNECT 0x51 80 81 #define ASUS_SDSP_LCD 0x01 82 #define ASUS_SDSP_CRT 0x02 83 #define ASUS_SDSP_TV 0x04 84 #define ASUS_SDSP_DVI 0x08 85 #define ASUS_SDSP_ALL \ 86 (ASUS_SDSP_LCD | ASUS_SDSP_CRT | ASUS_SDSP_TV | ASUS_SDSP_DVI) 87 88 int acpiasus_match(struct device *, void *, void *); 89 void acpiasus_attach(struct device *, struct device *, void *); 90 void acpiasus_init(struct device *); 91 int acpiasus_notify(struct aml_node *, int, void *); 92 void acpiasus_power(int, void *); 93 94 #if NAUDIO > 0 && NWSKBD > 0 95 extern int wskbd_set_mixervolume(long dir); 96 #endif 97 98 struct cfattach acpiasus_ca = { 99 sizeof(struct acpiasus_softc), acpiasus_match, acpiasus_attach 100 }; 101 102 struct cfdriver acpiasus_cd = { 103 NULL, "acpiasus", DV_DULL 104 }; 105 106 const char *acpiasus_hids[] = { ACPI_DEV_ASUS, 0 }; 107 108 int 109 acpiasus_match(struct device *parent, void *match, void *aux) 110 { 111 struct acpi_attach_args *aa = aux; 112 struct cfdata *cf = match; 113 114 return (acpi_matchhids(aa, acpiasus_hids, cf->cf_driver->cd_name)); 115 } 116 117 void 118 acpiasus_attach(struct device *parent, struct device *self, void *aux) 119 { 120 struct acpiasus_softc *sc = (struct acpiasus_softc *)self; 121 struct acpi_attach_args *aa = aux; 122 123 sc->sc_acpi = (struct acpi_softc *)parent; 124 sc->sc_devnode = aa->aaa_node; 125 126 sc->sc_powerhook = powerhook_establish(acpiasus_power, sc); 127 128 printf("\n"); 129 130 acpiasus_init(self); 131 132 aml_register_notify(sc->sc_devnode, aa->aaa_dev, 133 acpiasus_notify, sc, ACPIDEV_NOPOLL); 134 } 135 136 void 137 acpiasus_init(struct device *self) 138 { 139 struct acpiasus_softc *sc = (struct acpiasus_softc *)self; 140 struct aml_value cmd; 141 struct aml_value ret; 142 143 bzero(&cmd, sizeof(cmd)); 144 cmd.type = AML_OBJTYPE_INTEGER; 145 cmd.v_integer = 0x40; /* Disable ASL display switching. */ 146 147 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "INIT", 1, &cmd, &ret)) 148 printf("%s: no INIT\n", DEVNAME(sc)); 149 else 150 aml_freevalue(&ret); 151 } 152 153 int 154 acpiasus_notify(struct aml_node *node, int notify, void *arg) 155 { 156 struct acpiasus_softc *sc = arg; 157 158 if (notify >= ASUS_NOTIFY_BRIGHTNESSLOW && 159 notify <= ASUS_NOTIFY_BRIGHTNESSHIGH) { 160 #ifdef ACPIASUS_DEBUG 161 printf("%s: brightness %d percent\n", DEVNAME(sc), 162 (notify & 0xf) * 100 / 0xf); 163 #endif 164 return 0; 165 } 166 167 switch (notify) { 168 case ASUS_NOTIFY_WIRELESSON: /* Handled by AML. */ 169 case ASUS_NOTIFY_WIRELESSOFF: /* Handled by AML. */ 170 break; 171 case ASUS_NOTIFY_TASKSWITCH: 172 break; 173 case ASUS_NOTIFY_DISPLAYCYCLEDOWN: 174 case ASUS_NOTIFY_DISPLAYCYCLEUP: 175 break; 176 #if NAUDIO > 0 && NWSKBD > 0 177 case ASUS_NOTIFY_VOLUMEMUTE: 178 workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume, 179 (void *)(long)0, NULL); 180 break; 181 case ASUS_NOTIFY_VOLUMEDOWN: 182 workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume, 183 (void *)(long)-1, NULL); 184 break; 185 case ASUS_NOTIFY_VOLUMEUP: 186 workq_add_task(NULL, 0, (workq_fn)wskbd_set_mixervolume, 187 (void *)(long)1, NULL); 188 break; 189 #else 190 case ASUS_NOTIFY_VOLUMEMUTE: 191 case ASUS_NOTIFY_VOLUMEDOWN: 192 case ASUS_NOTIFY_VOLUMEUP: 193 break; 194 #endif 195 case ASUS_NOTIFY_POWERCONNECT: 196 case ASUS_NOTIFY_POWERDISCONNECT: 197 break; 198 199 case ASUS_NOTIFY_LCDSWITCHOFF0: 200 case ASUS_NOTIFY_LCDSWITCHOFF1: 201 break; 202 203 case ASUS_NOTIFY_LCDCHANGERES: 204 break; 205 206 case ASUS_NOTIFY_USERDEF0: 207 case ASUS_NOTIFY_USERDEF1: 208 break; 209 210 default: 211 printf("%s: unknown event 0x%02x\n", DEVNAME(sc), notify); 212 break; 213 } 214 215 return 0; 216 } 217 218 void 219 acpiasus_power(int why, void *arg) 220 { 221 struct acpiasus_softc *sc = (struct acpiasus_softc *)arg; 222 struct aml_value cmd; 223 struct aml_value ret; 224 225 switch (why) { 226 case PWR_STANDBY: 227 case PWR_SUSPEND: 228 break; 229 case PWR_RESUME: 230 acpiasus_init(arg); 231 232 bzero(&cmd, sizeof(cmd)); 233 cmd.type = AML_OBJTYPE_INTEGER; 234 cmd.v_integer = ASUS_SDSP_LCD; 235 236 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SDSP", 1, 237 &cmd, &ret)) 238 printf("%s: no SDSP\n", DEVNAME(sc)); 239 else 240 aml_freevalue(&ret); 241 242 break; 243 } 244 } 245