]> bbs.cooldavid.org Git - net-next-2.6.git/blob - drivers/platform/x86/classmate-laptop.c
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[net-next-2.6.git] / drivers / platform / x86 / classmate-laptop.c
1 /*
2  *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19
20 #include <linux/init.h>
21 #include <linux/module.h>
22 #include <linux/slab.h>
23 #include <linux/workqueue.h>
24 #include <acpi/acpi_drivers.h>
25 #include <linux/backlight.h>
26 #include <linux/input.h>
27
28 MODULE_LICENSE("GPL");
29
30
31 struct cmpc_accel {
32         int sensitivity;
33 };
34
35 #define CMPC_ACCEL_SENSITIVITY_DEFAULT          5
36
37
38 #define CMPC_ACCEL_HID          "ACCE0000"
39 #define CMPC_TABLET_HID         "TBLT0000"
40 #define CMPC_BL_HID             "IPML200"
41 #define CMPC_KEYS_HID           "FnBT0000"
42
43 /*
44  * Generic input device code.
45  */
46
47 typedef void (*input_device_init)(struct input_dev *dev);
48
49 static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
50                                        input_device_init idev_init)
51 {
52         struct input_dev *inputdev;
53         int error;
54
55         inputdev = input_allocate_device();
56         if (!inputdev)
57                 return -ENOMEM;
58         inputdev->name = name;
59         inputdev->dev.parent = &acpi->dev;
60         idev_init(inputdev);
61         error = input_register_device(inputdev);
62         if (error) {
63                 input_free_device(inputdev);
64                 return error;
65         }
66         dev_set_drvdata(&acpi->dev, inputdev);
67         return 0;
68 }
69
70 static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
71 {
72         struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
73         input_unregister_device(inputdev);
74         return 0;
75 }
76
77 /*
78  * Accelerometer code.
79  */
80 static acpi_status cmpc_start_accel(acpi_handle handle)
81 {
82         union acpi_object param[2];
83         struct acpi_object_list input;
84         acpi_status status;
85
86         param[0].type = ACPI_TYPE_INTEGER;
87         param[0].integer.value = 0x3;
88         param[1].type = ACPI_TYPE_INTEGER;
89         input.count = 2;
90         input.pointer = param;
91         status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
92         return status;
93 }
94
95 static acpi_status cmpc_stop_accel(acpi_handle handle)
96 {
97         union acpi_object param[2];
98         struct acpi_object_list input;
99         acpi_status status;
100
101         param[0].type = ACPI_TYPE_INTEGER;
102         param[0].integer.value = 0x4;
103         param[1].type = ACPI_TYPE_INTEGER;
104         input.count = 2;
105         input.pointer = param;
106         status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
107         return status;
108 }
109
110 static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
111 {
112         union acpi_object param[2];
113         struct acpi_object_list input;
114
115         param[0].type = ACPI_TYPE_INTEGER;
116         param[0].integer.value = 0x02;
117         param[1].type = ACPI_TYPE_INTEGER;
118         param[1].integer.value = val;
119         input.count = 2;
120         input.pointer = param;
121         return acpi_evaluate_object(handle, "ACMD", &input, NULL);
122 }
123
124 static acpi_status cmpc_get_accel(acpi_handle handle,
125                                   unsigned char *x,
126                                   unsigned char *y,
127                                   unsigned char *z)
128 {
129         union acpi_object param[2];
130         struct acpi_object_list input;
131         struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 };
132         unsigned char *locs;
133         acpi_status status;
134
135         param[0].type = ACPI_TYPE_INTEGER;
136         param[0].integer.value = 0x01;
137         param[1].type = ACPI_TYPE_INTEGER;
138         input.count = 2;
139         input.pointer = param;
140         status = acpi_evaluate_object(handle, "ACMD", &input, &output);
141         if (ACPI_SUCCESS(status)) {
142                 union acpi_object *obj;
143                 obj = output.pointer;
144                 locs = obj->buffer.pointer;
145                 *x = locs[0];
146                 *y = locs[1];
147                 *z = locs[2];
148                 kfree(output.pointer);
149         }
150         return status;
151 }
152
153 static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
154 {
155         if (event == 0x81) {
156                 unsigned char x, y, z;
157                 acpi_status status;
158
159                 status = cmpc_get_accel(dev->handle, &x, &y, &z);
160                 if (ACPI_SUCCESS(status)) {
161                         struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
162
163                         input_report_abs(inputdev, ABS_X, x);
164                         input_report_abs(inputdev, ABS_Y, y);
165                         input_report_abs(inputdev, ABS_Z, z);
166                         input_sync(inputdev);
167                 }
168         }
169 }
170
171 static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
172                                            struct device_attribute *attr,
173                                            char *buf)
174 {
175         struct acpi_device *acpi;
176         struct input_dev *inputdev;
177         struct cmpc_accel *accel;
178
179         acpi = to_acpi_device(dev);
180         inputdev = dev_get_drvdata(&acpi->dev);
181         accel = dev_get_drvdata(&inputdev->dev);
182
183         return sprintf(buf, "%d\n", accel->sensitivity);
184 }
185
186 static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
187                                             struct device_attribute *attr,
188                                             const char *buf, size_t count)
189 {
190         struct acpi_device *acpi;
191         struct input_dev *inputdev;
192         struct cmpc_accel *accel;
193         unsigned long sensitivity;
194         int r;
195
196         acpi = to_acpi_device(dev);
197         inputdev = dev_get_drvdata(&acpi->dev);
198         accel = dev_get_drvdata(&inputdev->dev);
199
200         r = strict_strtoul(buf, 0, &sensitivity);
201         if (r)
202                 return r;
203
204         accel->sensitivity = sensitivity;
205         cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
206
207         return strnlen(buf, count);
208 }
209
210 struct device_attribute cmpc_accel_sensitivity_attr = {
211         .attr = { .name = "sensitivity", .mode = 0660 },
212         .show = cmpc_accel_sensitivity_show,
213         .store = cmpc_accel_sensitivity_store
214 };
215
216 static int cmpc_accel_open(struct input_dev *input)
217 {
218         struct acpi_device *acpi;
219
220         acpi = to_acpi_device(input->dev.parent);
221         if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
222                 return 0;
223         return -EIO;
224 }
225
226 static void cmpc_accel_close(struct input_dev *input)
227 {
228         struct acpi_device *acpi;
229
230         acpi = to_acpi_device(input->dev.parent);
231         cmpc_stop_accel(acpi->handle);
232 }
233
234 static void cmpc_accel_idev_init(struct input_dev *inputdev)
235 {
236         set_bit(EV_ABS, inputdev->evbit);
237         input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
238         input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
239         input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
240         inputdev->open = cmpc_accel_open;
241         inputdev->close = cmpc_accel_close;
242 }
243
244 static int cmpc_accel_add(struct acpi_device *acpi)
245 {
246         int error;
247         struct input_dev *inputdev;
248         struct cmpc_accel *accel;
249
250         accel = kmalloc(sizeof(*accel), GFP_KERNEL);
251         if (!accel)
252                 return -ENOMEM;
253
254         accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
255         cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
256
257         error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
258         if (error)
259                 goto failed_file;
260
261         error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
262                                             cmpc_accel_idev_init);
263         if (error)
264                 goto failed_input;
265
266         inputdev = dev_get_drvdata(&acpi->dev);
267         dev_set_drvdata(&inputdev->dev, accel);
268
269         return 0;
270
271 failed_input:
272         device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
273 failed_file:
274         kfree(accel);
275         return error;
276 }
277
278 static int cmpc_accel_remove(struct acpi_device *acpi, int type)
279 {
280         struct input_dev *inputdev;
281         struct cmpc_accel *accel;
282
283         inputdev = dev_get_drvdata(&acpi->dev);
284         accel = dev_get_drvdata(&inputdev->dev);
285
286         device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
287         return cmpc_remove_acpi_notify_device(acpi);
288 }
289
290 static const struct acpi_device_id cmpc_accel_device_ids[] = {
291         {CMPC_ACCEL_HID, 0},
292         {"", 0}
293 };
294
295 static struct acpi_driver cmpc_accel_acpi_driver = {
296         .owner = THIS_MODULE,
297         .name = "cmpc_accel",
298         .class = "cmpc_accel",
299         .ids = cmpc_accel_device_ids,
300         .ops = {
301                 .add = cmpc_accel_add,
302                 .remove = cmpc_accel_remove,
303                 .notify = cmpc_accel_handler,
304         }
305 };
306
307
308 /*
309  * Tablet mode code.
310  */
311 static acpi_status cmpc_get_tablet(acpi_handle handle,
312                                    unsigned long long *value)
313 {
314         union acpi_object param;
315         struct acpi_object_list input;
316         unsigned long long output;
317         acpi_status status;
318
319         param.type = ACPI_TYPE_INTEGER;
320         param.integer.value = 0x01;
321         input.count = 1;
322         input.pointer = &param;
323         status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
324         if (ACPI_SUCCESS(status))
325                 *value = output;
326         return status;
327 }
328
329 static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
330 {
331         unsigned long long val = 0;
332         struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
333
334         if (event == 0x81) {
335                 if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
336                         input_report_switch(inputdev, SW_TABLET_MODE, !val);
337         }
338 }
339
340 static void cmpc_tablet_idev_init(struct input_dev *inputdev)
341 {
342         unsigned long long val = 0;
343         struct acpi_device *acpi;
344
345         set_bit(EV_SW, inputdev->evbit);
346         set_bit(SW_TABLET_MODE, inputdev->swbit);
347
348         acpi = to_acpi_device(inputdev->dev.parent);
349         if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
350                 input_report_switch(inputdev, SW_TABLET_MODE, !val);
351 }
352
353 static int cmpc_tablet_add(struct acpi_device *acpi)
354 {
355         return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
356                                            cmpc_tablet_idev_init);
357 }
358
359 static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
360 {
361         return cmpc_remove_acpi_notify_device(acpi);
362 }
363
364 static int cmpc_tablet_resume(struct acpi_device *acpi)
365 {
366         struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
367         unsigned long long val = 0;
368         if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
369                 input_report_switch(inputdev, SW_TABLET_MODE, !val);
370         return 0;
371 }
372
373 static const struct acpi_device_id cmpc_tablet_device_ids[] = {
374         {CMPC_TABLET_HID, 0},
375         {"", 0}
376 };
377
378 static struct acpi_driver cmpc_tablet_acpi_driver = {
379         .owner = THIS_MODULE,
380         .name = "cmpc_tablet",
381         .class = "cmpc_tablet",
382         .ids = cmpc_tablet_device_ids,
383         .ops = {
384                 .add = cmpc_tablet_add,
385                 .remove = cmpc_tablet_remove,
386                 .resume = cmpc_tablet_resume,
387                 .notify = cmpc_tablet_handler,
388         }
389 };
390
391
392 /*
393  * Backlight code.
394  */
395
396 static acpi_status cmpc_get_brightness(acpi_handle handle,
397                                        unsigned long long *value)
398 {
399         union acpi_object param;
400         struct acpi_object_list input;
401         unsigned long long output;
402         acpi_status status;
403
404         param.type = ACPI_TYPE_INTEGER;
405         param.integer.value = 0xC0;
406         input.count = 1;
407         input.pointer = &param;
408         status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
409         if (ACPI_SUCCESS(status))
410                 *value = output;
411         return status;
412 }
413
414 static acpi_status cmpc_set_brightness(acpi_handle handle,
415                                        unsigned long long value)
416 {
417         union acpi_object param[2];
418         struct acpi_object_list input;
419         acpi_status status;
420         unsigned long long output;
421
422         param[0].type = ACPI_TYPE_INTEGER;
423         param[0].integer.value = 0xC0;
424         param[1].type = ACPI_TYPE_INTEGER;
425         param[1].integer.value = value;
426         input.count = 2;
427         input.pointer = param;
428         status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
429         return status;
430 }
431
432 static int cmpc_bl_get_brightness(struct backlight_device *bd)
433 {
434         acpi_status status;
435         acpi_handle handle;
436         unsigned long long brightness;
437
438         handle = bl_get_data(bd);
439         status = cmpc_get_brightness(handle, &brightness);
440         if (ACPI_SUCCESS(status))
441                 return brightness;
442         else
443                 return -1;
444 }
445
446 static int cmpc_bl_update_status(struct backlight_device *bd)
447 {
448         acpi_status status;
449         acpi_handle handle;
450
451         handle = bl_get_data(bd);
452         status = cmpc_set_brightness(handle, bd->props.brightness);
453         if (ACPI_SUCCESS(status))
454                 return 0;
455         else
456                 return -1;
457 }
458
459 static const struct backlight_ops cmpc_bl_ops = {
460         .get_brightness = cmpc_bl_get_brightness,
461         .update_status = cmpc_bl_update_status
462 };
463
464 static int cmpc_bl_add(struct acpi_device *acpi)
465 {
466         struct backlight_properties props;
467         struct backlight_device *bd;
468
469         memset(&props, 0, sizeof(struct backlight_properties));
470         props.max_brightness = 7;
471         bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle,
472                                        &cmpc_bl_ops, &props);
473         if (IS_ERR(bd))
474                 return PTR_ERR(bd);
475         dev_set_drvdata(&acpi->dev, bd);
476         return 0;
477 }
478
479 static int cmpc_bl_remove(struct acpi_device *acpi, int type)
480 {
481         struct backlight_device *bd;
482
483         bd = dev_get_drvdata(&acpi->dev);
484         backlight_device_unregister(bd);
485         return 0;
486 }
487
488 static const struct acpi_device_id cmpc_bl_device_ids[] = {
489         {CMPC_BL_HID, 0},
490         {"", 0}
491 };
492
493 static struct acpi_driver cmpc_bl_acpi_driver = {
494         .owner = THIS_MODULE,
495         .name = "cmpc",
496         .class = "cmpc",
497         .ids = cmpc_bl_device_ids,
498         .ops = {
499                 .add = cmpc_bl_add,
500                 .remove = cmpc_bl_remove
501         }
502 };
503
504
505 /*
506  * Extra keys code.
507  */
508 static int cmpc_keys_codes[] = {
509         KEY_UNKNOWN,
510         KEY_WLAN,
511         KEY_SWITCHVIDEOMODE,
512         KEY_BRIGHTNESSDOWN,
513         KEY_BRIGHTNESSUP,
514         KEY_VENDOR,
515         KEY_UNKNOWN,
516         KEY_CAMERA,
517         KEY_BACK,
518         KEY_FORWARD,
519         KEY_MAX
520 };
521
522 static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
523 {
524         struct input_dev *inputdev;
525         int code = KEY_MAX;
526
527         if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
528                 code = cmpc_keys_codes[event & 0x0F];
529         inputdev = dev_get_drvdata(&dev->dev);;
530         input_report_key(inputdev, code, !(event & 0x10));
531 }
532
533 static void cmpc_keys_idev_init(struct input_dev *inputdev)
534 {
535         int i;
536
537         set_bit(EV_KEY, inputdev->evbit);
538         for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
539                 set_bit(cmpc_keys_codes[i], inputdev->keybit);
540 }
541
542 static int cmpc_keys_add(struct acpi_device *acpi)
543 {
544         return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
545                                            cmpc_keys_idev_init);
546 }
547
548 static int cmpc_keys_remove(struct acpi_device *acpi, int type)
549 {
550         return cmpc_remove_acpi_notify_device(acpi);
551 }
552
553 static const struct acpi_device_id cmpc_keys_device_ids[] = {
554         {CMPC_KEYS_HID, 0},
555         {"", 0}
556 };
557
558 static struct acpi_driver cmpc_keys_acpi_driver = {
559         .owner = THIS_MODULE,
560         .name = "cmpc_keys",
561         .class = "cmpc_keys",
562         .ids = cmpc_keys_device_ids,
563         .ops = {
564                 .add = cmpc_keys_add,
565                 .remove = cmpc_keys_remove,
566                 .notify = cmpc_keys_handler,
567         }
568 };
569
570
571 /*
572  * General init/exit code.
573  */
574
575 static int cmpc_init(void)
576 {
577         int r;
578
579         r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
580         if (r)
581                 goto failed_keys;
582
583         r = acpi_bus_register_driver(&cmpc_bl_acpi_driver);
584         if (r)
585                 goto failed_bl;
586
587         r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
588         if (r)
589                 goto failed_tablet;
590
591         r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
592         if (r)
593                 goto failed_accel;
594
595         return r;
596
597 failed_accel:
598         acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
599
600 failed_tablet:
601         acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
602
603 failed_bl:
604         acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
605
606 failed_keys:
607         return r;
608 }
609
610 static void cmpc_exit(void)
611 {
612         acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
613         acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
614         acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
615         acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
616 }
617
618 module_init(cmpc_init);
619 module_exit(cmpc_exit);
620
621 static const struct acpi_device_id cmpc_device_ids[] = {
622         {CMPC_ACCEL_HID, 0},
623         {CMPC_TABLET_HID, 0},
624         {CMPC_BL_HID, 0},
625         {CMPC_KEYS_HID, 0},
626         {"", 0}
627 };
628
629 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);