| 1 |
Return-Path: <andy.ross@windriver.com> |
| 2 |
X-Original-To: arjan@linux.intel.com |
| 3 |
Delivered-To: arjan@linux.intel.com |
| 4 |
Received: from orsmga001.jf.intel.com (orsmga001.jf.intel.com [10.7.209.18]) |
| 5 |
by linux.intel.com (Postfix) with ESMTP id 440A56A4482 |
| 6 |
for <arjan@linux.intel.com>; Thu, 24 Mar 2011 15:16:22 -0700 (PDT) |
| 7 |
X-ExtLoop1: 1 |
| 8 |
X-IronPort-AV: E=Sophos;i="4.63,239,1299484800"; |
| 9 |
d="scan'208";a="724825425" |
| 10 |
Received: from aross1x-wrs.jf.intel.com (HELO plausible.org) ([10.7.202.151]) |
| 11 |
by orsmga001.jf.intel.com with ESMTP; 24 Mar 2011 15:16:22 -0700 |
| 12 |
Received: from localhost.localdomain (unknown [192.102.209.1]) |
| 13 |
(Authenticated sender: andy-wrs) |
| 14 |
by plausible.org (Postfix) with ESMTPSA id 04F9B1194DC; |
| 15 |
Thu, 24 Mar 2011 15:16:21 -0700 (PDT) |
| 16 |
From: Andy Ross <andy.ross@windriver.com> |
| 17 |
To: Arjan van de Ven <arjan@linux.intel.com>, |
| 18 |
Prajwal Karur Mohan <prajwal.karur.mohan@intel.com> |
| 19 |
Subject: [PATCH 3/4] asus-laptop: Pegatron Lucid accelerometer |
| 20 |
Date: Thu, 24 Mar 2011 15:16:14 -0700 |
| 21 |
Message-Id: <1301004975-3656-4-git-send-email-andy.ross@windriver.com> |
| 22 |
X-Mailer: git-send-email 1.7.1 |
| 23 |
In-Reply-To: <1301004975-3656-1-git-send-email-andy.ross@windriver.com> |
| 24 |
References: <1301004975-3656-1-git-send-email-andy.ross@windriver.com> |
| 25 |
|
| 26 |
Support the built-in accelerometer on the Lucid tablets as a standard |
| 27 |
3-axis input device. |
| 28 |
|
| 29 |
Signed-off-by: Andy Ross <andy.ross@windriver.com> |
| 30 |
--- |
| 31 |
drivers/platform/x86/Kconfig | 9 ++- |
| 32 |
drivers/platform/x86/asus-laptop.c | 137 +++++++++++++++++++++++++++++++++++- |
| 33 |
2 files changed, 141 insertions(+), 5 deletions(-) |
| 34 |
|
| 35 |
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig |
| 36 |
index b6f983e..43906f5 100644 |
| 37 |
--- a/drivers/platform/x86/Kconfig |
| 38 |
+++ b/drivers/platform/x86/Kconfig |
| 39 |
@@ -67,10 +67,11 @@ config ASUS_LAPTOP |
| 40 |
This is a driver for Asus laptops and the Pegatron Lucid |
| 41 |
tablet. It may also support some MEDION, JVC or VICTOR |
| 42 |
laptops. It makes all the extra buttons generate standard |
| 43 |
- ACPI events and input events. It also adds support for video |
| 44 |
- output switching, LCD backlight control, Bluetooth and Wlan |
| 45 |
- control, and most importantly, allows you to blink those |
| 46 |
- fancy LEDs. |
| 47 |
+ ACPI events and input events, and on the Lucid the built-in |
| 48 |
+ accelerometer appears as an input device. It also adds |
| 49 |
+ support for video output switching, LCD backlight control, |
| 50 |
+ Bluetooth and Wlan control, and most importantly, allows you |
| 51 |
+ to blink those fancy LEDs. |
| 52 |
|
| 53 |
For more information and a userspace daemon for handling the extra |
| 54 |
buttons see <http://acpi4asus.sf.net>. |
| 55 |
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c |
| 56 |
index 6651d8c..9b07368 100644 |
| 57 |
--- a/drivers/platform/x86/asus-laptop.c |
| 58 |
+++ b/drivers/platform/x86/asus-laptop.c |
| 59 |
@@ -224,6 +224,14 @@ static char *display_get_paths[] = { |
| 60 |
#define PEGA_READ_ALS_H 0x02 |
| 61 |
#define PEGA_READ_ALS_L 0x03 |
| 62 |
|
| 63 |
+#define PEGA_ACCEL_NAME "pega_accel" |
| 64 |
+#define PEGA_ACCEL_DESC "Pegatron Lucid Tablet Accelerometer" |
| 65 |
+#define METHOD_XLRX "XLRX" |
| 66 |
+#define METHOD_XLRY "XLRY" |
| 67 |
+#define METHOD_XLRZ "XLRZ" |
| 68 |
+#define PEGA_ACC_CLAMP 512 /* 1G accel is reported as ~256, so clamp to 2G */ |
| 69 |
+#define PEGA_ACC_RETRIES 3 |
| 70 |
+ |
| 71 |
/* |
| 72 |
* Define a specific led structure to keep the main structure clean |
| 73 |
*/ |
| 74 |
@@ -249,6 +257,7 @@ struct asus_laptop { |
| 75 |
|
| 76 |
struct input_dev *inputdev; |
| 77 |
struct key_entry *keymap; |
| 78 |
+ struct input_polled_dev *pega_accel_poll; |
| 79 |
|
| 80 |
struct asus_led mled; |
| 81 |
struct asus_led tled; |
| 82 |
@@ -262,6 +271,10 @@ struct asus_laptop { |
| 83 |
bool have_rsts; |
| 84 |
bool have_pega_lucid; |
| 85 |
int lcd_state; |
| 86 |
+ bool pega_acc_live; |
| 87 |
+ int pega_acc_x; |
| 88 |
+ int pega_acc_y; |
| 89 |
+ int pega_acc_z; |
| 90 |
|
| 91 |
struct rfkill *gps_rfkill; |
| 92 |
|
| 93 |
@@ -390,6 +403,99 @@ static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable) |
| 94 |
return write_acpi_int(asus->handle, method, unit); |
| 95 |
} |
| 96 |
|
| 97 |
+static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method) |
| 98 |
+{ |
| 99 |
+ int i, delta; |
| 100 |
+ unsigned long long val; |
| 101 |
+ for (i = 0; i < PEGA_ACC_RETRIES; i++) { |
| 102 |
+ acpi_evaluate_integer(asus->handle, method, NULL, &val); |
| 103 |
+ |
| 104 |
+ /* The output is noisy. From reading the ASL |
| 105 |
+ * dissassembly, timeout errors are returned with 1's |
| 106 |
+ * in the high word, and the lack of locking around |
| 107 |
+ * thei hi/lo byte reads means that a transition |
| 108 |
+ * between (for example) -1 and 0 could be read as |
| 109 |
+ * 0xff00 or 0x00ff. */ |
| 110 |
+ delta = abs(curr - (short)val); |
| 111 |
+ if (delta < 128 && !(val & ~0xffff)) |
| 112 |
+ break; |
| 113 |
+ } |
| 114 |
+ return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP); |
| 115 |
+} |
| 116 |
+ |
| 117 |
+static void pega_accel_poll(struct input_polled_dev *ipd) |
| 118 |
+{ |
| 119 |
+ struct device *parent = ipd->input->dev.parent; |
| 120 |
+ struct asus_laptop *asus = dev_get_drvdata(parent); |
| 121 |
+ |
| 122 |
+ /* In some cases, the very first call to poll causes a |
| 123 |
+ * recursive fault under the polldev worker. This is |
| 124 |
+ * apparently related to very early userspace access to the |
| 125 |
+ * device, and perhaps a firmware bug. See related comments |
| 126 |
+ * in asus_platform_probe regarding the fragility of these |
| 127 |
+ * methods early in the boot. Fake the first report. */ |
| 128 |
+ if (!asus->pega_acc_live) { |
| 129 |
+ asus->pega_acc_live = true; |
| 130 |
+ input_report_abs(ipd->input, ABS_X, 0); |
| 131 |
+ input_report_abs(ipd->input, ABS_Y, 0); |
| 132 |
+ input_report_abs(ipd->input, ABS_Z, 0); |
| 133 |
+ input_sync(ipd->input); |
| 134 |
+ return; |
| 135 |
+ } |
| 136 |
+ |
| 137 |
+ asus->pega_acc_x = pega_acc_axis(asus, asus->pega_acc_x, METHOD_XLRX); |
| 138 |
+ asus->pega_acc_y = pega_acc_axis(asus, asus->pega_acc_y, METHOD_XLRY); |
| 139 |
+ asus->pega_acc_z = pega_acc_axis(asus, asus->pega_acc_z, METHOD_XLRZ); |
| 140 |
+ |
| 141 |
+ /* Note transform, convert to "right/up/out" in the native |
| 142 |
+ * landscape orientation (i.e. the vector is the direction of |
| 143 |
+ * "real up" in the device's cartiesian coordinates). */ |
| 144 |
+ input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x); |
| 145 |
+ input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y); |
| 146 |
+ input_report_abs(ipd->input, ABS_Z, asus->pega_acc_z); |
| 147 |
+ input_sync(ipd->input); |
| 148 |
+} |
| 149 |
+ |
| 150 |
+static void pega_accel_probe(struct asus_laptop *asus) |
| 151 |
+{ |
| 152 |
+ int err; |
| 153 |
+ struct input_polled_dev *ipd; |
| 154 |
+ |
| 155 |
+ if (!asus->have_pega_lucid || |
| 156 |
+ acpi_check_handle(asus->handle, METHOD_XLRX, NULL) || |
| 157 |
+ acpi_check_handle(asus->handle, METHOD_XLRY, NULL) || |
| 158 |
+ acpi_check_handle(asus->handle, METHOD_XLRZ, NULL)) |
| 159 |
+ return; |
| 160 |
+ |
| 161 |
+ ipd = input_allocate_polled_device(); |
| 162 |
+ if (!ipd) |
| 163 |
+ return; |
| 164 |
+ |
| 165 |
+ ipd->poll = pega_accel_poll; |
| 166 |
+ ipd->poll_interval = 125; |
| 167 |
+ ipd->poll_interval_min = 50; |
| 168 |
+ ipd->poll_interval_max = 2000; |
| 169 |
+ |
| 170 |
+ ipd->input->name = PEGA_ACCEL_DESC; |
| 171 |
+ ipd->input->phys = PEGA_ACCEL_NAME "/input0"; |
| 172 |
+ ipd->input->dev.parent = &asus->platform_device->dev; |
| 173 |
+ ipd->input->id.bustype = BUS_HOST; |
| 174 |
+ |
| 175 |
+ set_bit(EV_ABS, ipd->input->evbit); |
| 176 |
+ input_set_abs_params(ipd->input, ABS_X, |
| 177 |
+ -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); |
| 178 |
+ input_set_abs_params(ipd->input, ABS_Y, |
| 179 |
+ -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); |
| 180 |
+ input_set_abs_params(ipd->input, ABS_Z, |
| 181 |
+ -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); |
| 182 |
+ |
| 183 |
+ err = input_register_polled_device(ipd); |
| 184 |
+ if (err) |
| 185 |
+ input_free_polled_device(ipd); |
| 186 |
+ else |
| 187 |
+ asus->pega_accel_poll = ipd; |
| 188 |
+} |
| 189 |
+ |
| 190 |
/* Generic LED function */ |
| 191 |
static int asus_led_set(struct asus_laptop *asus, const char *method, |
| 192 |
int value) |
| 193 |
@@ -1459,11 +1565,40 @@ static void asus_platform_exit(struct asus_laptop *asus) |
| 194 |
platform_device_unregister(asus->platform_device); |
| 195 |
} |
| 196 |
|
| 197 |
+static int asus_platform_probe(struct platform_device *pd) |
| 198 |
+{ |
| 199 |
+ struct asus_laptop *asus = dev_get_drvdata(&pd->dev); |
| 200 |
+ |
| 201 |
+ /* This is instantiated during platform driver initialization |
| 202 |
+ * becuase if it's done from underneath asus_acpi_add(), the |
| 203 |
+ * resulting input device can be grabbed by an early userspace |
| 204 |
+ * reader before ACPI initialization is finished and something |
| 205 |
+ * oopses underneath the acpi_evaluate_integer() call out of |
| 206 |
+ * pega_accel_poll(). Firmware bug? */ |
| 207 |
+ pega_accel_probe(asus); |
| 208 |
+ |
| 209 |
+ return 0; |
| 210 |
+} |
| 211 |
+ |
| 212 |
+static int __devexit asus_platform_remove(struct platform_device *pd) |
| 213 |
+{ |
| 214 |
+ struct asus_laptop *asus = dev_get_drvdata(&pd->dev); |
| 215 |
+ if (asus->pega_accel_poll) { |
| 216 |
+ input_unregister_polled_device(asus->pega_accel_poll); |
| 217 |
+ input_free_polled_device(asus->pega_accel_poll); |
| 218 |
+ } |
| 219 |
+ asus->pega_accel_poll = NULL; |
| 220 |
+ return 0; |
| 221 |
+} |
| 222 |
+ |
| 223 |
+ |
| 224 |
static struct platform_driver platform_driver = { |
| 225 |
.driver = { |
| 226 |
.name = ASUS_LAPTOP_FILE, |
| 227 |
.owner = THIS_MODULE, |
| 228 |
- } |
| 229 |
+ }, |
| 230 |
+ .probe = asus_platform_probe, |
| 231 |
+ .remove = asus_platform_remove, |
| 232 |
}; |
| 233 |
|
| 234 |
static int asus_handle_init(char *name, acpi_handle * handle, |
| 235 |
-- |
| 236 |
1.7.1 |