]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/acpi_support/acpi_panasonic.c
Merge OpenSSL 1.1.1a.
[FreeBSD/FreeBSD.git] / sys / dev / acpi_support / acpi_panasonic.c
1 /*-
2  * Copyright (c) 2003 OGAWA Takaya <t-ogawa@triaez.kaisei.org>
3  * Copyright (c) 2004 TAKAHASHI Yoshihiro <nyan@FreeBSD.org>
4  * All rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include "opt_acpi.h"
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/module.h>
37 #include <sys/bus.h>
38 #include <sys/power.h>
39
40 #include <contrib/dev/acpica/include/acpi.h>
41
42 #include <dev/acpica/acpivar.h>
43
44 #define _COMPONENT      ACPI_OEM
45 ACPI_MODULE_NAME("Panasonic")
46
47 /* Debug */
48 #undef  ACPI_PANASONIC_DEBUG
49
50 /* Operations */
51 #define HKEY_SET        0
52 #define HKEY_GET        1
53
54 /* Functions */
55 #define HKEY_REG_LCD_BRIGHTNESS_MAX_AC  0x02
56 #define HKEY_REG_LCD_BRIGHTNESS_MIN_AC  0x03
57 #define HKEY_REG_LCD_BRIGHTNESS_AC      0x04
58 #define HKEY_REG_LCD_BRIGHTNESS_MAX_DC  0x05
59 #define HKEY_REG_LCD_BRIGHTNESS_MIN_DC  0x06
60 #define HKEY_REG_LCD_BRIGHTNESS_DC      0x07
61 #define HKEY_REG_SOUND_MUTE             0x08
62
63 /* Field definitions */
64 #define HKEY_LCD_BRIGHTNESS_BITS        4
65 #define HKEY_LCD_BRIGHTNESS_DIV         ((1 << HKEY_LCD_BRIGHTNESS_BITS) - 1)
66
67 struct acpi_panasonic_softc {
68         device_t        dev;
69         ACPI_HANDLE     handle;
70
71         struct sysctl_ctx_list  sysctl_ctx;
72         struct sysctl_oid       *sysctl_tree;
73
74         eventhandler_tag        power_evh;
75 };
76
77 /* Prototype for HKEY functions for getting/setting a value. */
78 typedef int hkey_fn_t(ACPI_HANDLE, int, UINT32 *);
79
80 static int      acpi_panasonic_probe(device_t dev);
81 static int      acpi_panasonic_attach(device_t dev);
82 static int      acpi_panasonic_detach(device_t dev);
83 static int      acpi_panasonic_shutdown(device_t dev);
84 static int      acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS);
85 static UINT64   acpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index);
86 static void     acpi_panasonic_sset(ACPI_HANDLE h, UINT64 index,
87                     UINT64 val);
88 static int      acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc,
89                     ACPI_HANDLE h, UINT32 *arg);
90 static void     acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc,
91                     ACPI_HANDLE h, UINT32 key);
92 static void     acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify,
93                     void *context);
94 static void     acpi_panasonic_power_profile(void *arg);
95
96 static hkey_fn_t        hkey_lcd_brightness_max;
97 static hkey_fn_t        hkey_lcd_brightness_min;
98 static hkey_fn_t        hkey_lcd_brightness;
99 static hkey_fn_t        hkey_sound_mute;
100 ACPI_SERIAL_DECL(panasonic, "ACPI Panasonic extras");
101
102 /* Table of sysctl names and HKEY functions to call. */
103 static struct {
104         char            *name;
105         hkey_fn_t       *handler;
106 } sysctl_table[] = {
107         /* name,                handler */
108         {"lcd_brightness_max",  hkey_lcd_brightness_max},
109         {"lcd_brightness_min",  hkey_lcd_brightness_min},
110         {"lcd_brightness",      hkey_lcd_brightness},
111         {"sound_mute",          hkey_sound_mute},
112         {NULL, NULL}
113 };
114
115 static device_method_t acpi_panasonic_methods[] = {
116         DEVMETHOD(device_probe,         acpi_panasonic_probe),
117         DEVMETHOD(device_attach,        acpi_panasonic_attach),
118         DEVMETHOD(device_detach,        acpi_panasonic_detach),
119         DEVMETHOD(device_shutdown,      acpi_panasonic_shutdown),
120
121         DEVMETHOD_END
122 };
123
124 static driver_t acpi_panasonic_driver = {
125         "acpi_panasonic",
126         acpi_panasonic_methods,
127         sizeof(struct acpi_panasonic_softc),
128 };
129
130 static devclass_t acpi_panasonic_devclass;
131
132 DRIVER_MODULE(acpi_panasonic, acpi, acpi_panasonic_driver,
133     acpi_panasonic_devclass, 0, 0);
134 MODULE_DEPEND(acpi_panasonic, acpi, 1, 1, 1);
135
136 static int
137 acpi_panasonic_probe(device_t dev)
138 {
139         static char *mat_ids[] = { "MAT0019", NULL };
140         int rv;
141         
142         if (acpi_disabled("panasonic") ||
143             device_get_unit(dev) != 0)
144                 return (ENXIO);
145         rv = ACPI_ID_PROBE(device_get_parent(dev), dev, mat_ids, NULL);
146
147         if (rv <= 0)
148                 device_set_desc(dev, "Panasonic Notebook Hotkeys");
149         return (rv);
150 }
151
152 static int
153 acpi_panasonic_attach(device_t dev)
154 {
155         struct acpi_panasonic_softc *sc;
156         struct acpi_softc *acpi_sc;
157         ACPI_STATUS status;
158         int i;
159
160         sc = device_get_softc(dev);
161         sc->dev = dev;
162         sc->handle = acpi_get_handle(dev);
163
164         acpi_sc = acpi_device_get_parent_softc(dev);
165
166         /* Build sysctl tree */
167         sysctl_ctx_init(&sc->sysctl_ctx);
168         sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
169             SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
170             "panasonic", CTLFLAG_RD, 0, "");
171         for (i = 0; sysctl_table[i].name != NULL; i++) {
172                 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
173                     SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
174                     sysctl_table[i].name,
175                     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_ANYBODY,
176                     sc, i, acpi_panasonic_sysctl, "I", "");
177         }
178
179 #if 0
180         /* Activate hotkeys */
181         status = AcpiEvaluateObject(sc->handle, "", NULL, NULL);
182         if (ACPI_FAILURE(status)) {
183                 device_printf(dev, "enable FN keys failed\n");
184                 sysctl_ctx_free(&sc->sysctl_ctx);
185                 return (ENXIO);
186         }
187 #endif
188
189         /* Handle notifies */
190         status = AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
191             acpi_panasonic_notify, sc);
192         if (ACPI_FAILURE(status)) {
193                 device_printf(dev, "couldn't install notify handler - %s\n",
194                     AcpiFormatException(status));
195                 sysctl_ctx_free(&sc->sysctl_ctx);
196                 return (ENXIO);
197         }
198
199         /* Install power profile event handler */
200         sc->power_evh = EVENTHANDLER_REGISTER(power_profile_change,
201             acpi_panasonic_power_profile, sc->handle, 0);
202
203         return (0);
204 }
205
206 static int
207 acpi_panasonic_detach(device_t dev)
208 {
209         struct acpi_panasonic_softc *sc;
210
211         sc = device_get_softc(dev);
212
213         /* Remove power profile event handler */
214         EVENTHANDLER_DEREGISTER(power_profile_change, sc->power_evh);
215
216         /* Remove notify handler */
217         AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
218             acpi_panasonic_notify);
219
220         /* Free sysctl tree */
221         sysctl_ctx_free(&sc->sysctl_ctx);
222
223         return (0);
224 }
225
226 static int
227 acpi_panasonic_shutdown(device_t dev)
228 {
229         struct acpi_panasonic_softc *sc;
230         int mute;
231
232         /* Mute the main audio during reboot to prevent static burst to speaker. */
233         sc = device_get_softc(dev);
234         mute = 1;
235         hkey_sound_mute(sc->handle, HKEY_SET, &mute);
236         return (0);
237 }
238
239 static int
240 acpi_panasonic_sysctl(SYSCTL_HANDLER_ARGS)
241 {
242         struct acpi_panasonic_softc *sc;
243         UINT32 arg;
244         int function, error;
245         hkey_fn_t *handler;
246
247         sc = (struct acpi_panasonic_softc *)oidp->oid_arg1;
248         function = oidp->oid_arg2;
249         handler = sysctl_table[function].handler;
250
251         /* Get the current value from the appropriate function. */
252         ACPI_SERIAL_BEGIN(panasonic);
253         error = handler(sc->handle, HKEY_GET, &arg);
254         if (error != 0)
255                 goto out;
256
257         /* Send the current value to the user and return if no new value. */
258         error = sysctl_handle_int(oidp, &arg, 0, req);
259         if (error != 0 || req->newptr == NULL)
260                 goto out;
261
262         /* Set the new value via the appropriate function. */
263         error = handler(sc->handle, HKEY_SET, &arg);
264
265 out:
266         ACPI_SERIAL_END(panasonic);
267         return (error);
268 }
269
270 static UINT64
271 acpi_panasonic_sinf(ACPI_HANDLE h, UINT64 index)
272 {
273         ACPI_BUFFER buf;
274         ACPI_OBJECT *res;
275         UINT64 ret;
276
277         ACPI_SERIAL_ASSERT(panasonic);
278         ret = -1;
279         buf.Length = ACPI_ALLOCATE_BUFFER;
280         buf.Pointer = NULL;
281         AcpiEvaluateObject(h, "SINF", NULL, &buf);
282         res = (ACPI_OBJECT *)buf.Pointer;
283         if (res->Type == ACPI_TYPE_PACKAGE)
284                 ret = res->Package.Elements[index].Integer.Value;
285         AcpiOsFree(buf.Pointer);
286
287         return (ret);
288 }
289
290 static void
291 acpi_panasonic_sset(ACPI_HANDLE h, UINT64 index, UINT64 val)
292 {
293         ACPI_OBJECT_LIST args;
294         ACPI_OBJECT obj[2];
295
296         ACPI_SERIAL_ASSERT(panasonic);
297         obj[0].Type = ACPI_TYPE_INTEGER;
298         obj[0].Integer.Value = index;
299         obj[1].Type = ACPI_TYPE_INTEGER;
300         obj[1].Integer.Value = val;
301         args.Count = 2;
302         args.Pointer = obj;
303         AcpiEvaluateObject(h, "SSET", &args, NULL);
304 }
305
306 static int
307 hkey_lcd_brightness_max(ACPI_HANDLE h, int op, UINT32 *val)
308 {
309         int reg;
310
311         ACPI_SERIAL_ASSERT(panasonic);
312         reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
313             HKEY_REG_LCD_BRIGHTNESS_MAX_AC : HKEY_REG_LCD_BRIGHTNESS_MAX_DC;
314
315         switch (op) {
316         case HKEY_SET:
317                 return (EPERM);
318                 break;
319         case HKEY_GET:
320                 *val = acpi_panasonic_sinf(h, reg);
321                 break;
322         }
323
324         return (0);
325 }
326
327 static int
328 hkey_lcd_brightness_min(ACPI_HANDLE h, int op, UINT32 *val)
329 {
330         int reg;
331
332         ACPI_SERIAL_ASSERT(panasonic);
333         reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
334             HKEY_REG_LCD_BRIGHTNESS_MIN_AC : HKEY_REG_LCD_BRIGHTNESS_MIN_DC;
335
336         switch (op) {
337         case HKEY_SET:
338                 return (EPERM);
339                 break;
340         case HKEY_GET:
341                 *val = acpi_panasonic_sinf(h, reg);
342                 break;
343         }
344
345         return (0);
346 }
347
348 static int
349 hkey_lcd_brightness(ACPI_HANDLE h, int op, UINT32 *val)
350 {
351         int reg;
352         UINT32 max, min;
353
354         reg = (power_profile_get_state() == POWER_PROFILE_PERFORMANCE) ?
355             HKEY_REG_LCD_BRIGHTNESS_AC : HKEY_REG_LCD_BRIGHTNESS_DC;
356
357         ACPI_SERIAL_ASSERT(panasonic);
358         switch (op) {
359         case HKEY_SET:
360                 hkey_lcd_brightness_max(h, HKEY_GET, &max);
361                 hkey_lcd_brightness_min(h, HKEY_GET, &min);
362                 if (*val < min || *val > max)
363                         return (EINVAL);
364                 acpi_panasonic_sset(h, reg, *val);
365                 break;
366         case HKEY_GET:
367                 *val = acpi_panasonic_sinf(h, reg);
368                 break;
369         }
370
371         return (0);
372 }
373
374 static int
375 hkey_sound_mute(ACPI_HANDLE h, int op, UINT32 *val)
376 {
377
378         ACPI_SERIAL_ASSERT(panasonic);
379         switch (op) {
380         case HKEY_SET:
381                 if (*val != 0 && *val != 1)
382                         return (EINVAL);
383                 acpi_panasonic_sset(h, HKEY_REG_SOUND_MUTE, *val);
384                 break;
385         case HKEY_GET:
386                 *val = acpi_panasonic_sinf(h, HKEY_REG_SOUND_MUTE);
387                 break;
388         }
389
390         return (0);
391 }
392
393 static int
394 acpi_panasonic_hkey_event(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,
395     UINT32 *arg)
396 {
397         ACPI_BUFFER buf;
398         ACPI_OBJECT *res;
399         UINT64 val;
400         int status;
401
402         ACPI_SERIAL_ASSERT(panasonic);
403         status = ENXIO;
404
405         buf.Length = ACPI_ALLOCATE_BUFFER;
406         buf.Pointer = NULL;
407         AcpiEvaluateObject(h, "HINF", NULL, &buf);
408         res = (ACPI_OBJECT *)buf.Pointer;
409         if (res->Type != ACPI_TYPE_INTEGER) {
410                 device_printf(sc->dev, "HINF returned non-integer\n");
411                 goto end;
412         }
413         val = res->Integer.Value;
414 #ifdef ACPI_PANASONIC_DEBUG
415         device_printf(sc->dev, "%s button Fn+F%d\n",
416                       (val & 0x80) ? "Pressed" : "Released",
417                       (int)(val & 0x7f));
418 #endif
419         if ((val & 0x7f) > 0 && (val & 0x7f) < 11) {
420                 *arg = val;
421                 status = 0;
422         }
423 end:
424         if (buf.Pointer)
425                 AcpiOsFree(buf.Pointer);
426
427         return (status);
428 }
429
430 static void
431 acpi_panasonic_hkey_action(struct acpi_panasonic_softc *sc, ACPI_HANDLE h,
432     UINT32 key)
433 {
434         struct acpi_softc *acpi_sc;
435         int arg, max, min;
436
437         acpi_sc = acpi_device_get_parent_softc(sc->dev);
438
439         ACPI_SERIAL_ASSERT(panasonic);
440         switch (key) {
441         case 1:
442                 /* Decrease LCD brightness. */
443                 hkey_lcd_brightness_max(h, HKEY_GET, &max);
444                 hkey_lcd_brightness_min(h, HKEY_GET, &min);
445                 hkey_lcd_brightness(h, HKEY_GET, &arg);
446                 arg -= max / HKEY_LCD_BRIGHTNESS_DIV;
447                 if (arg < min)
448                         arg = min;
449                 else if (arg > max)
450                         arg = max;
451                 hkey_lcd_brightness(h, HKEY_SET, &arg);
452                 break;
453         case 2:
454                 /* Increase LCD brightness. */
455                 hkey_lcd_brightness_max(h, HKEY_GET, &max);
456                 hkey_lcd_brightness_min(h, HKEY_GET, &min);
457                 hkey_lcd_brightness(h, HKEY_GET, &arg);
458                 arg += max / HKEY_LCD_BRIGHTNESS_DIV;
459                 if (arg < min)
460                         arg = min;
461                 else if (arg > max)
462                         arg = max;
463                 hkey_lcd_brightness(h, HKEY_SET, &arg);
464                 break;
465         case 4:
466                 /* Toggle sound mute. */
467                 hkey_sound_mute(h, HKEY_GET, &arg);
468                 if (arg)
469                         arg = 0;
470                 else
471                         arg = 1;
472                 hkey_sound_mute(h, HKEY_SET, &arg);
473                 break;
474         case 7:
475                 /* Suspend. */
476                 acpi_event_sleep_button_sleep(acpi_sc);
477                 break;
478         }
479 }
480
481 static void
482 acpi_panasonic_notify(ACPI_HANDLE h, UINT32 notify, void *context)
483 {
484         struct acpi_panasonic_softc *sc;
485         UINT32 key = 0;
486
487         sc = (struct acpi_panasonic_softc *)context;
488
489         switch (notify) {
490         case 0x80:
491                 ACPI_SERIAL_BEGIN(panasonic);
492                 if (acpi_panasonic_hkey_event(sc, h, &key) == 0) {
493                         acpi_panasonic_hkey_action(sc, h, key);
494                         acpi_UserNotify("Panasonic", h, (uint8_t)key);
495                 }
496                 ACPI_SERIAL_END(panasonic);
497                 break;
498         case 0x81:
499                 if (!bootverbose)
500                         break;
501                 /* FALLTHROUGH */
502         default:
503                 device_printf(sc->dev, "unknown notify: %#x\n", notify);
504                 break;
505         }
506 }
507
508 static void
509 acpi_panasonic_power_profile(void *arg)
510 {
511         ACPI_HANDLE handle;
512         UINT32 brightness;
513
514         handle = (ACPI_HANDLE)arg;
515
516         /* Reset current brightness according to new power state. */
517         ACPI_SERIAL_BEGIN(panasonic);
518         hkey_lcd_brightness(handle, HKEY_GET, &brightness);
519         hkey_lcd_brightness(handle, HKEY_SET, &brightness);
520         ACPI_SERIAL_END(panasonic);
521 }