@@ -193,6 +193,14 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
193
193
#define PEGA_READ_ALS_H 0x02
194
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
205
* Define a specific led structure to keep the main structure clean
198
206
*/
@@ -218,6 +226,7 @@ struct asus_laptop {
218
226
219
227
struct input_dev * inputdev ;
220
228
struct key_entry * keymap ;
229
+ struct input_polled_dev * pega_accel_poll ;
221
230
222
231
struct asus_led mled ;
223
232
struct asus_led tled ;
@@ -230,6 +239,10 @@ struct asus_laptop {
230
239
int wireless_status ;
231
240
bool have_rsts ;
232
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
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
371
return write_acpi_int (asus -> handle , method , unit );
359
372
}
360
373
374
+ static 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
+
394
+ static 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
+
425
+ static 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
+
434
+ static 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
+
476
+ exit :
477
+ input_free_polled_device (ipd );
478
+ return err ;
479
+ }
480
+
361
481
/* Generic LED function */
362
482
static int asus_led_set (struct asus_laptop * asus , const char * method ,
363
483
int value )
@@ -1348,7 +1468,7 @@ static struct platform_driver platform_driver = {
1348
1468
.driver = {
1349
1469
.name = ASUS_LAPTOP_FILE ,
1350
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
1678
if (result )
1559
1679
goto fail_rfkill ;
1560
1680
1681
+ result = pega_accel_init (asus );
1682
+ if (result && result != - ENODEV )
1683
+ goto fail_pega_accel ;
1684
+
1561
1685
asus_device_present = true;
1562
1686
return 0 ;
1563
1687
1688
+ fail_pega_accel :
1689
+ asus_rfkill_exit (asus );
1564
1690
fail_rfkill :
1565
1691
asus_led_exit (asus );
1566
1692
fail_led :
@@ -1584,6 +1710,7 @@ static int asus_acpi_remove(struct acpi_device *device, int type)
1584
1710
asus_rfkill_exit (asus );
1585
1711
asus_led_exit (asus );
1586
1712
asus_input_exit (asus );
1713
+ pega_accel_exit (asus );
1587
1714
asus_platform_exit (asus );
1588
1715
1589
1716
kfree (asus -> name );
0 commit comments