acer-wmi: Respect framebuffer blanking in backlight
[deliverable/linux.git] / drivers / misc / acer-wmi.c
CommitLineData
745a5d21
CC
1/*
2 * Acer WMI Laptop Extras
3 *
4 * Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
5 *
6 * Based on acer_acpi:
7 * Copyright (C) 2005-2007 E.M. Smith
8 * Copyright (C) 2007-2008 Carlos Corbacho <cathectic@gmail.com>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25#define ACER_WMI_VERSION "0.1"
26
27#include <linux/kernel.h>
28#include <linux/module.h>
29#include <linux/init.h>
30#include <linux/types.h>
31#include <linux/dmi.h>
f2b585b4 32#include <linux/fb.h>
745a5d21
CC
33#include <linux/backlight.h>
34#include <linux/leds.h>
35#include <linux/platform_device.h>
36#include <linux/acpi.h>
37#include <linux/i8042.h>
38
39#include <acpi/acpi_drivers.h>
40
41MODULE_AUTHOR("Carlos Corbacho");
42MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
43MODULE_LICENSE("GPL");
44
45#define ACER_LOGPREFIX "acer-wmi: "
46#define ACER_ERR KERN_ERR ACER_LOGPREFIX
47#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
48#define ACER_INFO KERN_INFO ACER_LOGPREFIX
49
50/*
51 * The following defines quirks to get some specific functions to work
52 * which are known to not be supported over ACPI-WMI (such as the mail LED
53 * on WMID based Acer's)
54 */
55struct acer_quirks {
56 const char *vendor;
57 const char *model;
58 u16 quirks;
59};
60
61/*
62 * Magic Number
63 * Meaning is unknown - this number is required for writing to ACPI for AMW0
64 * (it's also used in acerhk when directly accessing the BIOS)
65 */
66#define ACER_AMW0_WRITE 0x9610
67
68/*
69 * Bit masks for the AMW0 interface
70 */
71#define ACER_AMW0_WIRELESS_MASK 0x35
72#define ACER_AMW0_BLUETOOTH_MASK 0x34
73#define ACER_AMW0_MAILLED_MASK 0x31
74
75/*
76 * Method IDs for WMID interface
77 */
78#define ACER_WMID_GET_WIRELESS_METHODID 1
79#define ACER_WMID_GET_BLUETOOTH_METHODID 2
80#define ACER_WMID_GET_BRIGHTNESS_METHODID 3
81#define ACER_WMID_SET_WIRELESS_METHODID 4
82#define ACER_WMID_SET_BLUETOOTH_METHODID 5
83#define ACER_WMID_SET_BRIGHTNESS_METHODID 6
84#define ACER_WMID_GET_THREEG_METHODID 10
85#define ACER_WMID_SET_THREEG_METHODID 11
86
87/*
88 * Acer ACPI method GUIDs
89 */
90#define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
91#define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3"
92#define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A"
93
94MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
95MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
96
97/* Temporary workaround until the WMI sysfs interface goes in */
98MODULE_ALIAS("dmi:*:*Acer*:*:");
99
100/*
101 * Interface capability flags
102 */
103#define ACER_CAP_MAILLED (1<<0)
104#define ACER_CAP_WIRELESS (1<<1)
105#define ACER_CAP_BLUETOOTH (1<<2)
106#define ACER_CAP_BRIGHTNESS (1<<3)
107#define ACER_CAP_THREEG (1<<4)
108#define ACER_CAP_ANY (0xFFFFFFFF)
109
110/*
111 * Interface type flags
112 */
113enum interface_flags {
114 ACER_AMW0,
115 ACER_AMW0_V2,
116 ACER_WMID,
117};
118
119#define ACER_DEFAULT_WIRELESS 0
120#define ACER_DEFAULT_BLUETOOTH 0
121#define ACER_DEFAULT_MAILLED 0
122#define ACER_DEFAULT_THREEG 0
123
124static int max_brightness = 0xF;
125
126static int wireless = -1;
127static int bluetooth = -1;
128static int mailled = -1;
129static int brightness = -1;
130static int threeg = -1;
131static int force_series;
132
133module_param(mailled, int, 0444);
134module_param(wireless, int, 0444);
135module_param(bluetooth, int, 0444);
136module_param(brightness, int, 0444);
137module_param(threeg, int, 0444);
138module_param(force_series, int, 0444);
139MODULE_PARM_DESC(wireless, "Set initial state of Wireless hardware");
140MODULE_PARM_DESC(bluetooth, "Set initial state of Bluetooth hardware");
141MODULE_PARM_DESC(mailled, "Set initial state of Mail LED");
142MODULE_PARM_DESC(brightness, "Set initial LCD backlight brightness");
143MODULE_PARM_DESC(threeg, "Set initial state of 3G hardware");
144MODULE_PARM_DESC(force_series, "Force a different laptop series");
145
146struct acer_data {
147 int mailled;
148 int wireless;
149 int bluetooth;
150 int threeg;
151 int brightness;
152};
153
154/* Each low-level interface must define at least some of the following */
155struct wmi_interface {
156 /* The WMI device type */
157 u32 type;
158
159 /* The capabilities this interface provides */
160 u32 capability;
161
162 /* Private data for the current interface */
163 struct acer_data data;
164};
165
166/* The static interface pointer, points to the currently detected interface */
167static struct wmi_interface *interface;
168
169/*
170 * Embedded Controller quirks
171 * Some laptops require us to directly access the EC to either enable or query
172 * features that are not available through WMI.
173 */
174
175struct quirk_entry {
176 u8 wireless;
177 u8 mailled;
9991d9f2 178 s8 brightness;
745a5d21
CC
179 u8 bluetooth;
180};
181
182static struct quirk_entry *quirks;
183
184static void set_quirks(void)
185{
186 if (quirks->mailled)
187 interface->capability |= ACER_CAP_MAILLED;
188
189 if (quirks->brightness)
190 interface->capability |= ACER_CAP_BRIGHTNESS;
191}
192
193static int dmi_matched(const struct dmi_system_id *dmi)
194{
195 quirks = dmi->driver_data;
196 return 0;
197}
198
199static struct quirk_entry quirk_unknown = {
200};
201
9991d9f2
CC
202static struct quirk_entry quirk_acer_aspire_1520 = {
203 .brightness = -1,
204};
205
745a5d21
CC
206static struct quirk_entry quirk_acer_travelmate_2490 = {
207 .mailled = 1,
208};
209
210/* This AMW0 laptop has no bluetooth */
211static struct quirk_entry quirk_medion_md_98300 = {
212 .wireless = 1,
213};
214
215static struct dmi_system_id acer_quirks[] = {
9991d9f2
CC
216 {
217 .callback = dmi_matched,
218 .ident = "Acer Aspire 1360",
219 .matches = {
220 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
221 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
222 },
223 .driver_data = &quirk_acer_aspire_1520,
224 },
225 {
226 .callback = dmi_matched,
227 .ident = "Acer Aspire 1520",
228 .matches = {
229 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
230 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1520"),
231 },
232 .driver_data = &quirk_acer_aspire_1520,
233 },
745a5d21
CC
234 {
235 .callback = dmi_matched,
236 .ident = "Acer Aspire 3100",
237 .matches = {
238 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
239 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3100"),
240 },
241 .driver_data = &quirk_acer_travelmate_2490,
242 },
ed9cfe98
CC
243 {
244 .callback = dmi_matched,
245 .ident = "Acer Aspire 3610",
246 .matches = {
247 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
248 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3610"),
249 },
250 .driver_data = &quirk_acer_travelmate_2490,
251 },
745a5d21
CC
252 {
253 .callback = dmi_matched,
254 .ident = "Acer Aspire 5100",
255 .matches = {
256 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
257 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
258 },
259 .driver_data = &quirk_acer_travelmate_2490,
260 },
ed9cfe98
CC
261 {
262 .callback = dmi_matched,
263 .ident = "Acer Aspire 5610",
264 .matches = {
265 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
266 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
267 },
268 .driver_data = &quirk_acer_travelmate_2490,
269 },
745a5d21
CC
270 {
271 .callback = dmi_matched,
272 .ident = "Acer Aspire 5630",
273 .matches = {
274 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
275 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
276 },
277 .driver_data = &quirk_acer_travelmate_2490,
278 },
279 {
280 .callback = dmi_matched,
281 .ident = "Acer Aspire 5650",
282 .matches = {
283 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
284 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
285 },
286 .driver_data = &quirk_acer_travelmate_2490,
287 },
288 {
289 .callback = dmi_matched,
290 .ident = "Acer Aspire 5680",
291 .matches = {
292 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
293 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
294 },
295 .driver_data = &quirk_acer_travelmate_2490,
296 },
297 {
298 .callback = dmi_matched,
299 .ident = "Acer Aspire 9110",
300 .matches = {
301 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
302 DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
303 },
304 .driver_data = &quirk_acer_travelmate_2490,
305 },
306 {
307 .callback = dmi_matched,
308 .ident = "Acer TravelMate 2490",
309 .matches = {
310 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
311 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
312 },
313 .driver_data = &quirk_acer_travelmate_2490,
314 },
262ee35b
CC
315 {
316 .callback = dmi_matched,
317 .ident = "Acer TravelMate 4200",
318 .matches = {
319 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
320 DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4200"),
321 },
322 .driver_data = &quirk_acer_travelmate_2490,
323 },
745a5d21
CC
324 {
325 .callback = dmi_matched,
326 .ident = "Medion MD 98300",
327 .matches = {
328 DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
329 DMI_MATCH(DMI_PRODUCT_NAME, "WAM2030"),
330 },
331 .driver_data = &quirk_medion_md_98300,
332 },
333 {}
334};
335
336/* Find which quirks are needed for a particular vendor/ model pair */
337static void find_quirks(void)
338{
339 if (!force_series) {
340 dmi_check_system(acer_quirks);
341 } else if (force_series == 2490) {
342 quirks = &quirk_acer_travelmate_2490;
343 }
344
345 if (quirks == NULL)
346 quirks = &quirk_unknown;
347
348 set_quirks();
349}
350
351/*
352 * General interface convenience methods
353 */
354
355static bool has_cap(u32 cap)
356{
357 if ((interface->capability & cap) != 0)
358 return 1;
359
360 return 0;
361}
362
363/*
364 * AMW0 (V1) interface
365 */
366struct wmab_args {
367 u32 eax;
368 u32 ebx;
369 u32 ecx;
370 u32 edx;
371};
372
373struct wmab_ret {
374 u32 eax;
375 u32 ebx;
376 u32 ecx;
377 u32 edx;
378 u32 eex;
379};
380
381static acpi_status wmab_execute(struct wmab_args *regbuf,
382struct acpi_buffer *result)
383{
384 struct acpi_buffer input;
385 acpi_status status;
386 input.length = sizeof(struct wmab_args);
387 input.pointer = (u8 *)regbuf;
388
389 status = wmi_evaluate_method(AMW0_GUID1, 1, 1, &input, result);
390
391 return status;
392}
393
394static acpi_status AMW0_get_u32(u32 *value, u32 cap,
395struct wmi_interface *iface)
396{
397 int err;
398 u8 result;
399
400 switch (cap) {
401 case ACER_CAP_MAILLED:
402 switch (quirks->mailled) {
403 default:
404 err = ec_read(0xA, &result);
405 if (err)
406 return AE_ERROR;
407 *value = (result >> 7) & 0x1;
408 return AE_OK;
409 }
410 break;
411 case ACER_CAP_WIRELESS:
412 switch (quirks->wireless) {
413 case 1:
414 err = ec_read(0x7B, &result);
415 if (err)
416 return AE_ERROR;
417 *value = result & 0x1;
418 return AE_OK;
419 default:
420 err = ec_read(0xA, &result);
421 if (err)
422 return AE_ERROR;
423 *value = (result >> 2) & 0x1;
424 return AE_OK;
425 }
426 break;
427 case ACER_CAP_BLUETOOTH:
428 switch (quirks->bluetooth) {
429 default:
430 err = ec_read(0xA, &result);
431 if (err)
432 return AE_ERROR;
433 *value = (result >> 4) & 0x1;
434 return AE_OK;
435 }
436 break;
437 case ACER_CAP_BRIGHTNESS:
438 switch (quirks->brightness) {
439 default:
440 err = ec_read(0x83, &result);
441 if (err)
442 return AE_ERROR;
443 *value = result;
444 return AE_OK;
445 }
446 break;
447 default:
448 return AE_BAD_ADDRESS;
449 }
450 return AE_OK;
451}
452
453static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
454{
455 struct wmab_args args;
456
457 args.eax = ACER_AMW0_WRITE;
458 args.ebx = value ? (1<<8) : 0;
459 args.ecx = args.edx = 0;
460
461 switch (cap) {
462 case ACER_CAP_MAILLED:
463 if (value > 1)
464 return AE_BAD_PARAMETER;
465 args.ebx |= ACER_AMW0_MAILLED_MASK;
466 break;
467 case ACER_CAP_WIRELESS:
468 if (value > 1)
469 return AE_BAD_PARAMETER;
470 args.ebx |= ACER_AMW0_WIRELESS_MASK;
471 break;
472 case ACER_CAP_BLUETOOTH:
473 if (value > 1)
474 return AE_BAD_PARAMETER;
475 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
476 break;
477 case ACER_CAP_BRIGHTNESS:
478 if (value > max_brightness)
479 return AE_BAD_PARAMETER;
480 switch (quirks->brightness) {
745a5d21 481 default:
4609d029
CC
482 return ec_write(0x83, value);
483 break;
745a5d21
CC
484 }
485 default:
486 return AE_BAD_ADDRESS;
487 }
488
489 /* Actually do the set */
490 return wmab_execute(&args, NULL);
491}
492
493static acpi_status AMW0_find_mailled(void)
494{
495 struct wmab_args args;
496 struct wmab_ret ret;
497 acpi_status status = AE_OK;
498 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
499 union acpi_object *obj;
500
501 args.eax = 0x86;
502 args.ebx = args.ecx = args.edx = 0;
503
504 status = wmab_execute(&args, &out);
505 if (ACPI_FAILURE(status))
506 return status;
507
508 obj = (union acpi_object *) out.pointer;
509 if (obj && obj->type == ACPI_TYPE_BUFFER &&
510 obj->buffer.length == sizeof(struct wmab_ret)) {
511 ret = *((struct wmab_ret *) obj->buffer.pointer);
512 } else {
513 return AE_ERROR;
514 }
515
516 if (ret.eex & 0x1)
517 interface->capability |= ACER_CAP_MAILLED;
518
519 kfree(out.pointer);
520
521 return AE_OK;
522}
523
524static acpi_status AMW0_set_capabilities(void)
525{
526 struct wmab_args args;
527 struct wmab_ret ret;
528 acpi_status status = AE_OK;
529 struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
530 union acpi_object *obj;
531
532 args.eax = ACER_AMW0_WRITE;
533 args.ecx = args.edx = 0;
534
535 args.ebx = 0xa2 << 8;
536 args.ebx |= ACER_AMW0_WIRELESS_MASK;
537
538 status = wmab_execute(&args, &out);
539 if (ACPI_FAILURE(status))
540 return status;
541
542 obj = (union acpi_object *) out.pointer;
543 if (obj && obj->type == ACPI_TYPE_BUFFER &&
544 obj->buffer.length == sizeof(struct wmab_ret)) {
545 ret = *((struct wmab_ret *) obj->buffer.pointer);
546 } else {
547 return AE_ERROR;
548 }
549
550 if (ret.eax & 0x1)
551 interface->capability |= ACER_CAP_WIRELESS;
552
553 args.ebx = 2 << 8;
554 args.ebx |= ACER_AMW0_BLUETOOTH_MASK;
555
556 status = wmab_execute(&args, &out);
557 if (ACPI_FAILURE(status))
558 return status;
559
560 obj = (union acpi_object *) out.pointer;
561 if (obj && obj->type == ACPI_TYPE_BUFFER
562 && obj->buffer.length == sizeof(struct wmab_ret)) {
563 ret = *((struct wmab_ret *) obj->buffer.pointer);
564 } else {
565 return AE_ERROR;
566 }
567
568 if (ret.eax & 0x1)
569 interface->capability |= ACER_CAP_BLUETOOTH;
570
571 kfree(out.pointer);
572
573 /*
574 * This appears to be safe to enable, since all Wistron based laptops
575 * appear to use the same EC register for brightness, even if they
576 * differ for wireless, etc
577 */
9991d9f2
CC
578 if (quirks->brightness >= 0)
579 interface->capability |= ACER_CAP_BRIGHTNESS;
745a5d21
CC
580
581 return AE_OK;
582}
583
584static struct wmi_interface AMW0_interface = {
585 .type = ACER_AMW0,
586};
587
588static struct wmi_interface AMW0_V2_interface = {
589 .type = ACER_AMW0_V2,
590};
591
592/*
593 * New interface (The WMID interface)
594 */
595static acpi_status
596WMI_execute_u32(u32 method_id, u32 in, u32 *out)
597{
598 struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
599 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
600 union acpi_object *obj;
601 u32 tmp;
602 acpi_status status;
603
604 status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
605
606 if (ACPI_FAILURE(status))
607 return status;
608
609 obj = (union acpi_object *) result.pointer;
610 if (obj && obj->type == ACPI_TYPE_BUFFER &&
611 obj->buffer.length == sizeof(u32)) {
612 tmp = *((u32 *) obj->buffer.pointer);
613 } else {
614 tmp = 0;
615 }
616
617 if (out)
618 *out = tmp;
619
620 kfree(result.pointer);
621
622 return status;
623}
624
625static acpi_status WMID_get_u32(u32 *value, u32 cap,
626struct wmi_interface *iface)
627{
628 acpi_status status;
629 u8 tmp;
630 u32 result, method_id = 0;
631
632 switch (cap) {
633 case ACER_CAP_WIRELESS:
634 method_id = ACER_WMID_GET_WIRELESS_METHODID;
635 break;
636 case ACER_CAP_BLUETOOTH:
637 method_id = ACER_WMID_GET_BLUETOOTH_METHODID;
638 break;
639 case ACER_CAP_BRIGHTNESS:
640 method_id = ACER_WMID_GET_BRIGHTNESS_METHODID;
641 break;
642 case ACER_CAP_THREEG:
643 method_id = ACER_WMID_GET_THREEG_METHODID;
644 break;
645 case ACER_CAP_MAILLED:
646 if (quirks->mailled == 1) {
647 ec_read(0x9f, &tmp);
648 *value = tmp & 0x1;
649 return 0;
650 }
651 default:
652 return AE_BAD_ADDRESS;
653 }
654 status = WMI_execute_u32(method_id, 0, &result);
655
656 if (ACPI_SUCCESS(status))
657 *value = (u8)result;
658
659 return status;
660}
661
662static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
663{
664 u32 method_id = 0;
665 char param;
666
667 switch (cap) {
668 case ACER_CAP_BRIGHTNESS:
669 if (value > max_brightness)
670 return AE_BAD_PARAMETER;
671 method_id = ACER_WMID_SET_BRIGHTNESS_METHODID;
672 break;
673 case ACER_CAP_WIRELESS:
674 if (value > 1)
675 return AE_BAD_PARAMETER;
676 method_id = ACER_WMID_SET_WIRELESS_METHODID;
677 break;
678 case ACER_CAP_BLUETOOTH:
679 if (value > 1)
680 return AE_BAD_PARAMETER;
681 method_id = ACER_WMID_SET_BLUETOOTH_METHODID;
682 break;
683 case ACER_CAP_THREEG:
684 if (value > 1)
685 return AE_BAD_PARAMETER;
686 method_id = ACER_WMID_SET_THREEG_METHODID;
687 break;
688 case ACER_CAP_MAILLED:
689 if (value > 1)
690 return AE_BAD_PARAMETER;
691 if (quirks->mailled == 1) {
692 param = value ? 0x92 : 0x93;
693 i8042_command(&param, 0x1059);
694 return 0;
695 }
696 break;
697 default:
698 return AE_BAD_ADDRESS;
699 }
700 return WMI_execute_u32(method_id, (u32)value, NULL);
701}
702
703static acpi_status WMID_set_capabilities(void)
704{
705 struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
706 union acpi_object *obj;
707 acpi_status status;
708 u32 devices;
709
710 status = wmi_query_block(WMID_GUID2, 1, &out);
711 if (ACPI_FAILURE(status))
712 return status;
713
714 obj = (union acpi_object *) out.pointer;
715 if (obj && obj->type == ACPI_TYPE_BUFFER &&
716 obj->buffer.length == sizeof(u32)) {
717 devices = *((u32 *) obj->buffer.pointer);
718 } else {
719 return AE_ERROR;
720 }
721
722 /* Not sure on the meaning of the relevant bits yet to detect these */
723 interface->capability |= ACER_CAP_WIRELESS;
724 interface->capability |= ACER_CAP_THREEG;
725
726 /* WMID always provides brightness methods */
727 interface->capability |= ACER_CAP_BRIGHTNESS;
728
729 if (devices & 0x10)
730 interface->capability |= ACER_CAP_BLUETOOTH;
731
732 if (!(devices & 0x20))
733 max_brightness = 0x9;
734
735 return status;
736}
737
738static struct wmi_interface wmid_interface = {
739 .type = ACER_WMID,
740};
741
742/*
743 * Generic Device (interface-independent)
744 */
745
746static acpi_status get_u32(u32 *value, u32 cap)
747{
748 acpi_status status = AE_BAD_ADDRESS;
749
750 switch (interface->type) {
751 case ACER_AMW0:
752 status = AMW0_get_u32(value, cap, interface);
753 break;
754 case ACER_AMW0_V2:
755 if (cap == ACER_CAP_MAILLED) {
756 status = AMW0_get_u32(value, cap, interface);
757 break;
758 }
759 case ACER_WMID:
760 status = WMID_get_u32(value, cap, interface);
761 break;
762 }
763
764 return status;
765}
766
767static acpi_status set_u32(u32 value, u32 cap)
768{
769 if (interface->capability & cap) {
770 switch (interface->type) {
771 case ACER_AMW0:
772 return AMW0_set_u32(value, cap, interface);
773 case ACER_AMW0_V2:
774 case ACER_WMID:
775 return WMID_set_u32(value, cap, interface);
776 default:
777 return AE_BAD_PARAMETER;
778 }
779 }
780 return AE_BAD_PARAMETER;
781}
782
783static void __init acer_commandline_init(void)
784{
785 /*
786 * These will all fail silently if the value given is invalid, or the
787 * capability isn't available on the given interface
788 */
789 set_u32(mailled, ACER_CAP_MAILLED);
790 set_u32(wireless, ACER_CAP_WIRELESS);
791 set_u32(bluetooth, ACER_CAP_BLUETOOTH);
792 set_u32(threeg, ACER_CAP_THREEG);
793 set_u32(brightness, ACER_CAP_BRIGHTNESS);
794}
795
796/*
797 * LED device (Mail LED only, no other LEDs known yet)
798 */
799static void mail_led_set(struct led_classdev *led_cdev,
800enum led_brightness value)
801{
802 set_u32(value, ACER_CAP_MAILLED);
803}
804
805static struct led_classdev mail_led = {
343c0042 806 .name = "acer-wmi::mail",
745a5d21
CC
807 .brightness_set = mail_led_set,
808};
809
7560e385 810static int __devinit acer_led_init(struct device *dev)
745a5d21
CC
811{
812 return led_classdev_register(dev, &mail_led);
813}
814
815static void acer_led_exit(void)
816{
817 led_classdev_unregister(&mail_led);
818}
819
820/*
821 * Backlight device
822 */
823static struct backlight_device *acer_backlight_device;
824
825static int read_brightness(struct backlight_device *bd)
826{
827 u32 value;
828 get_u32(&value, ACER_CAP_BRIGHTNESS);
829 return value;
830}
831
832static int update_bl_status(struct backlight_device *bd)
833{
f2b585b4
CC
834 int intensity = bd->props.brightness;
835
836 if (bd->props.power != FB_BLANK_UNBLANK)
837 intensity = 0;
838 if (bd->props.fb_blank != FB_BLANK_UNBLANK)
839 intensity = 0;
840
841 set_u32(intensity, ACER_CAP_BRIGHTNESS);
842
745a5d21
CC
843 return 0;
844}
845
846static struct backlight_ops acer_bl_ops = {
847 .get_brightness = read_brightness,
848 .update_status = update_bl_status,
849};
850
7560e385 851static int __devinit acer_backlight_init(struct device *dev)
745a5d21
CC
852{
853 struct backlight_device *bd;
854
855 bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops);
856 if (IS_ERR(bd)) {
857 printk(ACER_ERR "Could not register Acer backlight device\n");
858 acer_backlight_device = NULL;
859 return PTR_ERR(bd);
860 }
861
862 acer_backlight_device = bd;
863
f2b585b4
CC
864 bd->props.power = FB_BLANK_UNBLANK;
865 bd->props.brightness = max_brightness;
745a5d21 866 bd->props.max_brightness = max_brightness;
745a5d21
CC
867 backlight_update_status(bd);
868 return 0;
869}
870
7560e385 871static void acer_backlight_exit(void)
745a5d21
CC
872{
873 backlight_device_unregister(acer_backlight_device);
874}
875
876/*
877 * Read/ write bool sysfs macro
878 */
879#define show_set_bool(value, cap) \
880static ssize_t \
881show_bool_##value(struct device *dev, struct device_attribute *attr, \
882 char *buf) \
883{ \
884 u32 result; \
885 acpi_status status = get_u32(&result, cap); \
886 if (ACPI_SUCCESS(status)) \
887 return sprintf(buf, "%u\n", result); \
888 return sprintf(buf, "Read error\n"); \
889} \
890\
891static ssize_t \
892set_bool_##value(struct device *dev, struct device_attribute *attr, \
893 const char *buf, size_t count) \
894{ \
895 u32 tmp = simple_strtoul(buf, NULL, 10); \
896 acpi_status status = set_u32(tmp, cap); \
897 if (ACPI_FAILURE(status)) \
898 return -EINVAL; \
899 return count; \
900} \
901static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \
902 show_bool_##value, set_bool_##value);
903
904show_set_bool(wireless, ACER_CAP_WIRELESS);
905show_set_bool(bluetooth, ACER_CAP_BLUETOOTH);
906show_set_bool(threeg, ACER_CAP_THREEG);
907
908/*
909 * Read interface sysfs macro
910 */
911static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
912 char *buf)
913{
914 switch (interface->type) {
915 case ACER_AMW0:
916 return sprintf(buf, "AMW0\n");
917 case ACER_AMW0_V2:
918 return sprintf(buf, "AMW0 v2\n");
919 case ACER_WMID:
920 return sprintf(buf, "WMID\n");
921 default:
922 return sprintf(buf, "Error!\n");
923 }
924}
925
926static DEVICE_ATTR(interface, S_IWUGO | S_IRUGO | S_IWUSR,
927 show_interface, NULL);
928
929/*
930 * Platform device
931 */
932static int __devinit acer_platform_probe(struct platform_device *device)
933{
934 int err;
935
936 if (has_cap(ACER_CAP_MAILLED)) {
937 err = acer_led_init(&device->dev);
938 if (err)
939 goto error_mailled;
940 }
941
942 if (has_cap(ACER_CAP_BRIGHTNESS)) {
943 err = acer_backlight_init(&device->dev);
944 if (err)
945 goto error_brightness;
946 }
947
948 return 0;
949
950error_brightness:
951 acer_led_exit();
952error_mailled:
953 return err;
954}
955
956static int acer_platform_remove(struct platform_device *device)
957{
958 if (has_cap(ACER_CAP_MAILLED))
959 acer_led_exit();
960 if (has_cap(ACER_CAP_BRIGHTNESS))
961 acer_backlight_exit();
962 return 0;
963}
964
965static int acer_platform_suspend(struct platform_device *dev,
966pm_message_t state)
967{
968 u32 value;
969 struct acer_data *data = &interface->data;
970
971 if (!data)
972 return -ENOMEM;
973
974 if (has_cap(ACER_CAP_WIRELESS)) {
975 get_u32(&value, ACER_CAP_WIRELESS);
976 data->wireless = value;
977 }
978
979 if (has_cap(ACER_CAP_BLUETOOTH)) {
980 get_u32(&value, ACER_CAP_BLUETOOTH);
981 data->bluetooth = value;
982 }
983
984 if (has_cap(ACER_CAP_MAILLED)) {
985 get_u32(&value, ACER_CAP_MAILLED);
986 data->mailled = value;
987 }
988
989 if (has_cap(ACER_CAP_BRIGHTNESS)) {
990 get_u32(&value, ACER_CAP_BRIGHTNESS);
991 data->brightness = value;
992 }
993
994 return 0;
995}
996
997static int acer_platform_resume(struct platform_device *device)
998{
999 struct acer_data *data = &interface->data;
1000
1001 if (!data)
1002 return -ENOMEM;
1003
1004 if (has_cap(ACER_CAP_WIRELESS))
1005 set_u32(data->wireless, ACER_CAP_WIRELESS);
1006
1007 if (has_cap(ACER_CAP_BLUETOOTH))
1008 set_u32(data->bluetooth, ACER_CAP_BLUETOOTH);
1009
1010 if (has_cap(ACER_CAP_THREEG))
1011 set_u32(data->threeg, ACER_CAP_THREEG);
1012
1013 if (has_cap(ACER_CAP_MAILLED))
1014 set_u32(data->mailled, ACER_CAP_MAILLED);
1015
1016 if (has_cap(ACER_CAP_BRIGHTNESS))
1017 set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
1018
1019 return 0;
1020}
1021
1022static struct platform_driver acer_platform_driver = {
1023 .driver = {
1024 .name = "acer-wmi",
1025 .owner = THIS_MODULE,
1026 },
1027 .probe = acer_platform_probe,
1028 .remove = acer_platform_remove,
1029 .suspend = acer_platform_suspend,
1030 .resume = acer_platform_resume,
1031};
1032
1033static struct platform_device *acer_platform_device;
1034
1035static int remove_sysfs(struct platform_device *device)
1036{
1037 if (has_cap(ACER_CAP_WIRELESS))
1038 device_remove_file(&device->dev, &dev_attr_wireless);
1039
1040 if (has_cap(ACER_CAP_BLUETOOTH))
1041 device_remove_file(&device->dev, &dev_attr_bluetooth);
1042
1043 if (has_cap(ACER_CAP_THREEG))
1044 device_remove_file(&device->dev, &dev_attr_threeg);
1045
1046 device_remove_file(&device->dev, &dev_attr_interface);
1047
1048 return 0;
1049}
1050
1051static int create_sysfs(void)
1052{
1053 int retval = -ENOMEM;
1054
1055 if (has_cap(ACER_CAP_WIRELESS)) {
1056 retval = device_create_file(&acer_platform_device->dev,
1057 &dev_attr_wireless);
1058 if (retval)
1059 goto error_sysfs;
1060 }
1061
1062 if (has_cap(ACER_CAP_BLUETOOTH)) {
1063 retval = device_create_file(&acer_platform_device->dev,
1064 &dev_attr_bluetooth);
1065 if (retval)
1066 goto error_sysfs;
1067 }
1068
1069 if (has_cap(ACER_CAP_THREEG)) {
1070 retval = device_create_file(&acer_platform_device->dev,
1071 &dev_attr_threeg);
1072 if (retval)
1073 goto error_sysfs;
1074 }
1075
1076 retval = device_create_file(&acer_platform_device->dev,
1077 &dev_attr_interface);
1078 if (retval)
1079 goto error_sysfs;
1080
1081 return 0;
1082
1083error_sysfs:
1084 remove_sysfs(acer_platform_device);
1085 return retval;
1086}
1087
1088static int __init acer_wmi_init(void)
1089{
1090 int err;
1091
1092 printk(ACER_INFO "Acer Laptop ACPI-WMI Extras version %s\n",
1093 ACER_WMI_VERSION);
1094
9991d9f2
CC
1095 find_quirks();
1096
745a5d21
CC
1097 /*
1098 * Detect which ACPI-WMI interface we're using.
1099 */
1100 if (wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1101 interface = &AMW0_V2_interface;
1102
1103 if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
1104 interface = &wmid_interface;
1105
1106 if (wmi_has_guid(WMID_GUID2) && interface) {
1107 if (ACPI_FAILURE(WMID_set_capabilities())) {
8d039bc7
CC
1108 printk(ACER_ERR "Unable to detect available WMID "
1109 "devices\n");
745a5d21
CC
1110 return -ENODEV;
1111 }
1112 } else if (!wmi_has_guid(WMID_GUID2) && interface) {
8d039bc7 1113 printk(ACER_ERR "No WMID device detection method found\n");
745a5d21
CC
1114 return -ENODEV;
1115 }
1116
1117 if (wmi_has_guid(AMW0_GUID1) && !wmi_has_guid(WMID_GUID1)) {
1118 interface = &AMW0_interface;
1119
1120 if (ACPI_FAILURE(AMW0_set_capabilities())) {
8d039bc7
CC
1121 printk(ACER_ERR "Unable to detect available AMW0 "
1122 "devices\n");
745a5d21
CC
1123 return -ENODEV;
1124 }
1125 }
1126
9b963c40
CC
1127 if (wmi_has_guid(AMW0_GUID1))
1128 AMW0_find_mailled();
745a5d21 1129
745a5d21 1130 if (!interface) {
8d039bc7
CC
1131 printk(ACER_ERR "No or unsupported WMI interface, unable to "
1132 "load\n");
745a5d21
CC
1133 return -ENODEV;
1134 }
1135
1136 if (platform_driver_register(&acer_platform_driver)) {
1137 printk(ACER_ERR "Unable to register platform driver.\n");
1138 goto error_platform_register;
1139 }
1140 acer_platform_device = platform_device_alloc("acer-wmi", -1);
1141 platform_device_add(acer_platform_device);
1142
1143 err = create_sysfs();
1144 if (err)
1145 return err;
1146
1147 /* Override any initial settings with values from the commandline */
1148 acer_commandline_init();
1149
1150 return 0;
1151
1152error_platform_register:
1153 return -ENODEV;
1154}
1155
1156static void __exit acer_wmi_exit(void)
1157{
1158 remove_sysfs(acer_platform_device);
1159 platform_device_del(acer_platform_device);
1160 platform_driver_unregister(&acer_platform_driver);
1161
1162 printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
1163 return;
1164}
1165
1166module_init(acer_wmi_init);
1167module_exit(acer_wmi_exit);
This page took 0.137602 seconds and 5 git commands to generate.