aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorAndy Ross <andy.ross@windriver.com>2011-10-14 05:13:38 -0400
committerMatthew Garrett <mjg@redhat.com>2011-10-24 10:52:41 -0400
commitb23910c2194e0e0ee43e585788085f8e6dd4877e (patch)
tree5610789264266ee5235e9b03e1b4bc442e4cbaa3 /drivers/platform
parentabec04dbc3dbe7577ccd9d5d6e188aa153d464eb (diff)
asus-laptop: Pegatron Lucid accelerometer
Support the built-in accelerometer on the Lucid tablets as a standard 3-axis input device. Signed-off-by: Andy Ross <andy.ross@windriver.com> Signed-off-by: Corentin Chary <corentin.chary@gmail.com> Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/Kconfig9
-rw-r--r--drivers/platform/x86/asus-laptop.c129
2 files changed, 133 insertions, 5 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 4e5623e4fa0..0d4146ca011 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -69,10 +69,11 @@ config ASUS_LAPTOP
69 This is a driver for Asus laptops, Lenovo SL and the Pegatron 69 This is a driver for Asus laptops, Lenovo SL and the Pegatron
70 Lucid tablet. It may also support some MEDION, JVC or VICTOR 70 Lucid tablet. It may also support some MEDION, JVC or VICTOR
71 laptops. It makes all the extra buttons generate standard 71 laptops. It makes all the extra buttons generate standard
72 ACPI events and input events. It also adds support for video 72 ACPI events and input events, and on the Lucid the built-in
73 output switching, LCD backlight control, Bluetooth and Wlan 73 accelerometer appears as an input device. It also adds
74 control, and most importantly, allows you to blink those 74 support for video output switching, LCD backlight control,
75 fancy LEDs. 75 Bluetooth and Wlan control, and most importantly, allows you
76 to blink those fancy LEDs.
76 77
77 For more information see <http://acpi4asus.sf.net>. 78 For more information see <http://acpi4asus.sf.net>.
78 79
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index 8327d06b6e8..613762d825f 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -193,6 +193,14 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
193#define PEGA_READ_ALS_H 0x02 193#define PEGA_READ_ALS_H 0x02
194#define PEGA_READ_ALS_L 0x03 194#define PEGA_READ_ALS_L 0x03
195 195
196#define PEGA_ACCEL_NAME "pega_accel"
197#define PEGA_ACCEL_DESC "Pegatron Lucid Tablet Accelerometer"
198#define METHOD_XLRX "XLRX"
199#define METHOD_XLRY "XLRY"
200#define METHOD_XLRZ "XLRZ"
201#define PEGA_ACC_CLAMP 512 /* 1G accel is reported as ~256, so clamp to 2G */
202#define PEGA_ACC_RETRIES 3
203
196/* 204/*
197 * Define a specific led structure to keep the main structure clean 205 * Define a specific led structure to keep the main structure clean
198 */ 206 */
@@ -218,6 +226,7 @@ struct asus_laptop {
218 226
219 struct input_dev *inputdev; 227 struct input_dev *inputdev;
220 struct key_entry *keymap; 228 struct key_entry *keymap;
229 struct input_polled_dev *pega_accel_poll;
221 230
222 struct asus_led mled; 231 struct asus_led mled;
223 struct asus_led tled; 232 struct asus_led tled;
@@ -230,6 +239,10 @@ struct asus_laptop {
230 int wireless_status; 239 int wireless_status;
231 bool have_rsts; 240 bool have_rsts;
232 bool is_pega_lucid; 241 bool is_pega_lucid;
242 bool pega_acc_live;
243 int pega_acc_x;
244 int pega_acc_y;
245 int pega_acc_z;
233 246
234 struct rfkill *gps_rfkill; 247 struct rfkill *gps_rfkill;
235 248
@@ -358,6 +371,113 @@ static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable)
358 return write_acpi_int(asus->handle, method, unit); 371 return write_acpi_int(asus->handle, method, unit);
359} 372}
360 373
374static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method)
375{
376 int i, delta;
377 unsigned long long val;
378 for (i = 0; i < PEGA_ACC_RETRIES; i++) {
379 acpi_evaluate_integer(asus->handle, method, NULL, &val);
380
381 /* The output is noisy. From reading the ASL
382 * dissassembly, timeout errors are returned with 1's
383 * in the high word, and the lack of locking around
384 * thei hi/lo byte reads means that a transition
385 * between (for example) -1 and 0 could be read as
386 * 0xff00 or 0x00ff. */
387 delta = abs(curr - (short)val);
388 if (delta < 128 && !(val & ~0xffff))
389 break;
390 }
391 return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP);
392}
393
394static void pega_accel_poll(struct input_polled_dev *ipd)
395{
396 struct device *parent = ipd->input->dev.parent;
397 struct asus_laptop *asus = dev_get_drvdata(parent);
398
399 /* In some cases, the very first call to poll causes a
400 * recursive fault under the polldev worker. This is
401 * apparently related to very early userspace access to the
402 * device, and perhaps a firmware bug. Fake the first report. */
403 if (!asus->pega_acc_live) {
404 asus->pega_acc_live = true;
405 input_report_abs(ipd->input, ABS_X, 0);
406 input_report_abs(ipd->input, ABS_Y, 0);
407 input_report_abs(ipd->input, ABS_Z, 0);
408 input_sync(ipd->input);
409 return;
410 }
411
412 asus->pega_acc_x = pega_acc_axis(asus, asus->pega_acc_x, METHOD_XLRX);
413 asus->pega_acc_y = pega_acc_axis(asus, asus->pega_acc_y, METHOD_XLRY);
414 asus->pega_acc_z = pega_acc_axis(asus, asus->pega_acc_z, METHOD_XLRZ);
415
416 /* Note transform, convert to "right/up/out" in the native
417 * landscape orientation (i.e. the vector is the direction of
418 * "real up" in the device's cartiesian coordinates). */
419 input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x);
420 input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y);
421 input_report_abs(ipd->input, ABS_Z, asus->pega_acc_z);
422 input_sync(ipd->input);
423}
424
425static void pega_accel_exit(struct asus_laptop *asus)
426{
427 if (asus->pega_accel_poll) {
428 input_unregister_polled_device(asus->pega_accel_poll);
429 input_free_polled_device(asus->pega_accel_poll);
430 }
431 asus->pega_accel_poll = NULL;
432}
433
434static int pega_accel_init(struct asus_laptop *asus)
435{
436 int err;
437 struct input_polled_dev *ipd;
438
439 if (!asus->is_pega_lucid)
440 return -ENODEV;
441
442 if (acpi_check_handle(asus->handle, METHOD_XLRX, NULL) ||
443 acpi_check_handle(asus->handle, METHOD_XLRY, NULL) ||
444 acpi_check_handle(asus->handle, METHOD_XLRZ, NULL))
445 return -ENODEV;
446
447 ipd = input_allocate_polled_device();
448 if (!ipd)
449 return -ENOMEM;
450
451 ipd->poll = pega_accel_poll;
452 ipd->poll_interval = 125;
453 ipd->poll_interval_min = 50;
454 ipd->poll_interval_max = 2000;
455
456 ipd->input->name = PEGA_ACCEL_DESC;
457 ipd->input->phys = PEGA_ACCEL_NAME "/input0";
458 ipd->input->dev.parent = &asus->platform_device->dev;
459 ipd->input->id.bustype = BUS_HOST;
460
461 set_bit(EV_ABS, ipd->input->evbit);
462 input_set_abs_params(ipd->input, ABS_X,
463 -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
464 input_set_abs_params(ipd->input, ABS_Y,
465 -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
466 input_set_abs_params(ipd->input, ABS_Z,
467 -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
468
469 err = input_register_polled_device(ipd);
470 if (err)
471 goto exit;
472
473 asus->pega_accel_poll = ipd;
474 return 0;
475
476exit:
477 input_free_polled_device(ipd);
478 return err;
479}
480
361/* Generic LED function */ 481/* Generic LED function */
362static int asus_led_set(struct asus_laptop *asus, const char *method, 482static int asus_led_set(struct asus_laptop *asus, const char *method,
363 int value) 483 int value)
@@ -1348,7 +1468,7 @@ static struct platform_driver platform_driver = {
1348 .driver = { 1468 .driver = {
1349 .name = ASUS_LAPTOP_FILE, 1469 .name = ASUS_LAPTOP_FILE,
1350 .owner = THIS_MODULE, 1470 .owner = THIS_MODULE,
1351 } 1471 },
1352}; 1472};
1353 1473
1354/* 1474/*
@@ -1558,9 +1678,15 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
1558 if (result) 1678 if (result)
1559 goto fail_rfkill; 1679 goto fail_rfkill;
1560 1680
1681 result = pega_accel_init(asus);
1682 if (result && result != -ENODEV)
1683 goto fail_pega_accel;
1684
1561 asus_device_present = true; 1685 asus_device_present = true;
1562 return 0; 1686 return 0;
1563 1687
1688fail_pega_accel:
1689 asus_rfkill_exit(asus);
1564fail_rfkill: 1690fail_rfkill:
1565 asus_led_exit(asus); 1691 asus_led_exit(asus);
1566fail_led: 1692fail_led:
@@ -1584,6 +1710,7 @@ static int asus_acpi_remove(struct acpi_device *device, int type)
1584 asus_rfkill_exit(asus); 1710 asus_rfkill_exit(asus);
1585 asus_led_exit(asus); 1711 asus_led_exit(asus);
1586 asus_input_exit(asus); 1712 asus_input_exit(asus);
1713 pega_accel_exit(asus);
1587 asus_platform_exit(asus); 1714 asus_platform_exit(asus);
1588 1715
1589 kfree(asus->name); 1716 kfree(asus->name);