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