classmate-laptop: use a single MODULE_DEVICE_TABLE to get correct aliases
[cascardo/linux.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 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_device *bd;
466
467         bd = backlight_device_register("cmpc_bl", &acpi->dev,
468                                        acpi->handle, &cmpc_bl_ops);
469         bd->props.max_brightness = 7;
470         dev_set_drvdata(&acpi->dev, bd);
471         return 0;
472 }
473
474 static int cmpc_bl_remove(struct acpi_device *acpi, int type)
475 {
476         struct backlight_device *bd;
477
478         bd = dev_get_drvdata(&acpi->dev);
479         backlight_device_unregister(bd);
480         return 0;
481 }
482
483 static const struct acpi_device_id cmpc_bl_device_ids[] = {
484         {CMPC_BL_HID, 0},
485         {"", 0}
486 };
487
488 static struct acpi_driver cmpc_bl_acpi_driver = {
489         .owner = THIS_MODULE,
490         .name = "cmpc",
491         .class = "cmpc",
492         .ids = cmpc_bl_device_ids,
493         .ops = {
494                 .add = cmpc_bl_add,
495                 .remove = cmpc_bl_remove
496         }
497 };
498
499
500 /*
501  * Extra keys code.
502  */
503 static int cmpc_keys_codes[] = {
504         KEY_UNKNOWN,
505         KEY_WLAN,
506         KEY_SWITCHVIDEOMODE,
507         KEY_BRIGHTNESSDOWN,
508         KEY_BRIGHTNESSUP,
509         KEY_VENDOR,
510         KEY_MAX
511 };
512
513 static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
514 {
515         struct input_dev *inputdev;
516         int code = KEY_MAX;
517
518         if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
519                 code = cmpc_keys_codes[event & 0x0F];
520         inputdev = dev_get_drvdata(&dev->dev);;
521         input_report_key(inputdev, code, !(event & 0x10));
522 }
523
524 static void cmpc_keys_idev_init(struct input_dev *inputdev)
525 {
526         int i;
527
528         set_bit(EV_KEY, inputdev->evbit);
529         for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
530                 set_bit(cmpc_keys_codes[i], inputdev->keybit);
531 }
532
533 static int cmpc_keys_add(struct acpi_device *acpi)
534 {
535         return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
536                                            cmpc_keys_idev_init);
537 }
538
539 static int cmpc_keys_remove(struct acpi_device *acpi, int type)
540 {
541         return cmpc_remove_acpi_notify_device(acpi);
542 }
543
544 static const struct acpi_device_id cmpc_keys_device_ids[] = {
545         {CMPC_KEYS_HID, 0},
546         {"", 0}
547 };
548
549 static struct acpi_driver cmpc_keys_acpi_driver = {
550         .owner = THIS_MODULE,
551         .name = "cmpc_keys",
552         .class = "cmpc_keys",
553         .ids = cmpc_keys_device_ids,
554         .ops = {
555                 .add = cmpc_keys_add,
556                 .remove = cmpc_keys_remove,
557                 .notify = cmpc_keys_handler,
558         }
559 };
560
561
562 /*
563  * General init/exit code.
564  */
565
566 static int cmpc_init(void)
567 {
568         int r;
569
570         r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
571         if (r)
572                 goto failed_keys;
573
574         r = acpi_bus_register_driver(&cmpc_bl_acpi_driver);
575         if (r)
576                 goto failed_bl;
577
578         r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
579         if (r)
580                 goto failed_tablet;
581
582         r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
583         if (r)
584                 goto failed_accel;
585
586         return r;
587
588 failed_accel:
589         acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
590
591 failed_tablet:
592         acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
593
594 failed_bl:
595         acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
596
597 failed_keys:
598         return r;
599 }
600
601 static void cmpc_exit(void)
602 {
603         acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
604         acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
605         acpi_bus_unregister_driver(&cmpc_bl_acpi_driver);
606         acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
607 }
608
609 module_init(cmpc_init);
610 module_exit(cmpc_exit);
611
612 static const struct acpi_device_id cmpc_device_ids[] = {
613         {CMPC_ACCEL_HID, 0},
614         {CMPC_TABLET_HID, 0},
615         {CMPC_BL_HID, 0},
616         {CMPC_KEYS_HID, 0},
617         {"", 0}
618 };
619
620 MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);