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