aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/platform
diff options
context:
space:
mode:
authorMiguel Gómez <magomez@igalia.com>2012-06-29 09:39:48 -0400
committerMatthew Garrett <mjg@redhat.com>2012-07-28 00:28:27 -0400
commit7125587df4e87224dbd3b90ddf6f23e83044ae30 (patch)
treedac9ab910929ef0904eb4b16bd5f811b00b1039e /drivers/platform
parentc0b91b6d5226247fa4fe894eb592bcc56bc7e9fd (diff)
classmate-laptop: Add support for Classmate V4 accelerometer.
Classmate V4 laptop includes a new accelerometer that can't be handled by previous driver. This patch adds a new driver to handle it. [mjg: Fixed up the driver pm stuff] Signed-off-by: Miguel Gómez <magomez@igalia.com> Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/x86/classmate-laptop.c403
1 files changed, 401 insertions, 2 deletions
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index e2230a2b2f8e..33f4d2c72c03 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -31,12 +31,18 @@ MODULE_LICENSE("GPL");
31 31
32struct cmpc_accel { 32struct cmpc_accel {
33 int sensitivity; 33 int sensitivity;
34 int g_select;
35 int inputdev_state;
34}; 36};
35 37
36#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 38#define CMPC_ACCEL_DEV_STATE_CLOSED 0
39#define CMPC_ACCEL_DEV_STATE_OPEN 1
37 40
41#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
42#define CMPC_ACCEL_G_SELECT_DEFAULT 0
38 43
39#define CMPC_ACCEL_HID "ACCE0000" 44#define CMPC_ACCEL_HID "ACCE0000"
45#define CMPC_ACCEL_HID_V4 "ACCE0001"
40#define CMPC_TABLET_HID "TBLT0000" 46#define CMPC_TABLET_HID "TBLT0000"
41#define CMPC_IPML_HID "IPML200" 47#define CMPC_IPML_HID "IPML200"
42#define CMPC_KEYS_HID "FnBT0000" 48#define CMPC_KEYS_HID "FnBT0000"
@@ -76,7 +82,391 @@ static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
76} 82}
77 83
78/* 84/*
79 * Accelerometer code. 85 * Accelerometer code for Classmate V4
86 */
87static acpi_status cmpc_start_accel_v4(acpi_handle handle)
88{
89 union acpi_object param[4];
90 struct acpi_object_list input;
91 acpi_status status;
92
93 param[0].type = ACPI_TYPE_INTEGER;
94 param[0].integer.value = 0x3;
95 param[1].type = ACPI_TYPE_INTEGER;
96 param[1].integer.value = 0;
97 param[2].type = ACPI_TYPE_INTEGER;
98 param[2].integer.value = 0;
99 param[3].type = ACPI_TYPE_INTEGER;
100 param[3].integer.value = 0;
101 input.count = 4;
102 input.pointer = param;
103 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
104 return status;
105}
106
107static acpi_status cmpc_stop_accel_v4(acpi_handle handle)
108{
109 union acpi_object param[4];
110 struct acpi_object_list input;
111 acpi_status status;
112
113 param[0].type = ACPI_TYPE_INTEGER;
114 param[0].integer.value = 0x4;
115 param[1].type = ACPI_TYPE_INTEGER;
116 param[1].integer.value = 0;
117 param[2].type = ACPI_TYPE_INTEGER;
118 param[2].integer.value = 0;
119 param[3].type = ACPI_TYPE_INTEGER;
120 param[3].integer.value = 0;
121 input.count = 4;
122 input.pointer = param;
123 status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
124 return status;
125}
126
127static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)
128{
129 union acpi_object param[4];
130 struct acpi_object_list input;
131
132 param[0].type = ACPI_TYPE_INTEGER;
133 param[0].integer.value = 0x02;
134 param[1].type = ACPI_TYPE_INTEGER;
135 param[1].integer.value = val;
136 param[2].type = ACPI_TYPE_INTEGER;
137 param[2].integer.value = 0;
138 param[3].type = ACPI_TYPE_INTEGER;
139 param[3].integer.value = 0;
140 input.count = 4;
141 input.pointer = param;
142 return acpi_evaluate_object(handle, "ACMD", &input, NULL);
143}
144
145static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)
146{
147 union acpi_object param[4];
148 struct acpi_object_list input;
149
150 param[0].type = ACPI_TYPE_INTEGER;
151 param[0].integer.value = 0x05;
152 param[1].type = ACPI_TYPE_INTEGER;
153 param[1].integer.value = val;
154 param[2].type = ACPI_TYPE_INTEGER;
155 param[2].integer.value = 0;
156 param[3].type = ACPI_TYPE_INTEGER;
157 param[3].integer.value = 0;
158 input.count = 4;
159 input.pointer = param;
160 return acpi_evaluate_object(handle, "ACMD", &input, NULL);
161}
162
163static acpi_status cmpc_get_accel_v4(acpi_handle handle,
164 int16_t *x,
165 int16_t *y,
166 int16_t *z)
167{
168 union acpi_object param[4];
169 struct acpi_object_list input;
170 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
171 int16_t *locs;
172 acpi_status status;
173
174 param[0].type = ACPI_TYPE_INTEGER;
175 param[0].integer.value = 0x01;
176 param[1].type = ACPI_TYPE_INTEGER;
177 param[1].integer.value = 0;
178 param[2].type = ACPI_TYPE_INTEGER;
179 param[2].integer.value = 0;
180 param[3].type = ACPI_TYPE_INTEGER;
181 param[3].integer.value = 0;
182 input.count = 4;
183 input.pointer = param;
184 status = acpi_evaluate_object(handle, "ACMD", &input, &output);
185 if (ACPI_SUCCESS(status)) {
186 union acpi_object *obj;
187 obj = output.pointer;
188 locs = (int16_t *) obj->buffer.pointer;
189 *x = locs[0];
190 *y = locs[1];
191 *z = locs[2];
192 kfree(output.pointer);
193 }
194 return status;
195}
196
197static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
198{
199 if (event == 0x81) {
200 int16_t x, y, z;
201 acpi_status status;
202
203 status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);
204 if (ACPI_SUCCESS(status)) {
205 struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
206
207 input_report_abs(inputdev, ABS_X, x);
208 input_report_abs(inputdev, ABS_Y, y);
209 input_report_abs(inputdev, ABS_Z, z);
210 input_sync(inputdev);
211 }
212 }
213}
214
215static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,
216 struct device_attribute *attr,
217 char *buf)
218{
219 struct acpi_device *acpi;
220 struct input_dev *inputdev;
221 struct cmpc_accel *accel;
222
223 acpi = to_acpi_device(dev);
224 inputdev = dev_get_drvdata(&acpi->dev);
225 accel = dev_get_drvdata(&inputdev->dev);
226
227 return sprintf(buf, "%d\n", accel->sensitivity);
228}
229
230static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,
231 struct device_attribute *attr,
232 const char *buf, size_t count)
233{
234 struct acpi_device *acpi;
235 struct input_dev *inputdev;
236 struct cmpc_accel *accel;
237 unsigned long sensitivity;
238 int r;
239
240 acpi = to_acpi_device(dev);
241 inputdev = dev_get_drvdata(&acpi->dev);
242 accel = dev_get_drvdata(&inputdev->dev);
243
244 r = kstrtoul(buf, 0, &sensitivity);
245 if (r)
246 return r;
247
248 /* sensitivity must be between 1 and 127 */
249 if (sensitivity < 1 || sensitivity > 127)
250 return -EINVAL;
251
252 accel->sensitivity = sensitivity;
253 cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);
254
255 return strnlen(buf, count);
256}
257
258static struct device_attribute cmpc_accel_sensitivity_attr_v4 = {
259 .attr = { .name = "sensitivity", .mode = 0660 },
260 .show = cmpc_accel_sensitivity_show_v4,
261 .store = cmpc_accel_sensitivity_store_v4
262};
263
264static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,
265 struct device_attribute *attr,
266 char *buf)
267{
268 struct acpi_device *acpi;
269 struct input_dev *inputdev;
270 struct cmpc_accel *accel;
271
272 acpi = to_acpi_device(dev);
273 inputdev = dev_get_drvdata(&acpi->dev);
274 accel = dev_get_drvdata(&inputdev->dev);
275
276 return sprintf(buf, "%d\n", accel->g_select);
277}
278
279static ssize_t cmpc_accel_g_select_store_v4(struct device *dev,
280 struct device_attribute *attr,
281 const char *buf, size_t count)
282{
283 struct acpi_device *acpi;
284 struct input_dev *inputdev;
285 struct cmpc_accel *accel;
286 unsigned long g_select;
287 int r;
288
289 acpi = to_acpi_device(dev);
290 inputdev = dev_get_drvdata(&acpi->dev);
291 accel = dev_get_drvdata(&inputdev->dev);
292
293 r = kstrtoul(buf, 0, &g_select);
294 if (r)
295 return r;
296
297 /* 0 means 1.5g, 1 means 6g, everything else is wrong */
298 if (g_select != 0 && g_select != 1)
299 return -EINVAL;
300
301 accel->g_select = g_select;
302 cmpc_accel_set_g_select_v4(acpi->handle, g_select);
303
304 return strnlen(buf, count);
305}
306
307static struct device_attribute cmpc_accel_g_select_attr_v4 = {
308 .attr = { .name = "g_select", .mode = 0660 },
309 .show = cmpc_accel_g_select_show_v4,
310 .store = cmpc_accel_g_select_store_v4
311};
312
313static int cmpc_accel_open_v4(struct input_dev *input)
314{
315 struct acpi_device *acpi;
316 struct cmpc_accel *accel;
317
318 acpi = to_acpi_device(input->dev.parent);
319 accel = dev_get_drvdata(&input->dev);
320
321 cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
322 cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
323
324 if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
325 accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
326 return 0;
327 }
328 return -EIO;
329}
330
331static void cmpc_accel_close_v4(struct input_dev *input)
332{
333 struct acpi_device *acpi;
334 struct cmpc_accel *accel;
335
336 acpi = to_acpi_device(input->dev.parent);
337 accel = dev_get_drvdata(&input->dev);
338
339 cmpc_stop_accel_v4(acpi->handle);
340 accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
341}
342
343static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
344{
345 set_bit(EV_ABS, inputdev->evbit);
346 input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);
347 input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);
348 input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);
349 inputdev->open = cmpc_accel_open_v4;
350 inputdev->close = cmpc_accel_close_v4;
351}
352
353static int cmpc_accel_suspend_v4(struct device *dev)
354{
355 struct input_dev *inputdev;
356 struct cmpc_accel *accel;
357
358 inputdev = dev_get_drvdata(dev);
359 accel = dev_get_drvdata(&inputdev->dev);
360
361 if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
362 return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);
363
364 return 0;
365}
366
367static int cmpc_accel_resume_v4(struct device *dev)
368{
369 struct input_dev *inputdev;
370 struct cmpc_accel *accel;
371
372 inputdev = dev_get_drvdata(dev);
373 accel = dev_get_drvdata(&inputdev->dev);
374
375 if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
376 cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,
377 accel->sensitivity);
378 cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,
379 accel->g_select);
380
381 if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))
382 return -EIO;
383 }
384
385 return 0;
386}
387
388static int cmpc_accel_add_v4(struct acpi_device *acpi)
389{
390 int error;
391 struct input_dev *inputdev;
392 struct cmpc_accel *accel;
393
394 accel = kmalloc(sizeof(*accel), GFP_KERNEL);
395 if (!accel)
396 return -ENOMEM;
397
398 accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
399
400 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
401 cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
402
403 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
404 if (error)
405 goto failed_sensitivity;
406
407 accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
408 cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
409
410 error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
411 if (error)
412 goto failed_g_select;
413
414 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
415 cmpc_accel_idev_init_v4);
416 if (error)
417 goto failed_input;
418
419 inputdev = dev_get_drvdata(&acpi->dev);
420 dev_set_drvdata(&inputdev->dev, accel);
421
422 return 0;
423
424failed_input:
425 device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
426failed_g_select:
427 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
428failed_sensitivity:
429 kfree(accel);
430 return error;
431}
432
433static int cmpc_accel_remove_v4(struct acpi_device *acpi, int type)
434{
435 struct input_dev *inputdev;
436 struct cmpc_accel *accel;
437
438 inputdev = dev_get_drvdata(&acpi->dev);
439 accel = dev_get_drvdata(&inputdev->dev);
440
441 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
442 device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
443 return cmpc_remove_acpi_notify_device(acpi);
444}
445
446static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,
447 cmpc_accel_resume_v4);
448
449static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
450 {CMPC_ACCEL_HID_V4, 0},
451 {"", 0}
452};
453
454static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
455 .owner = THIS_MODULE,
456 .name = "cmpc_accel_v4",
457 .class = "cmpc_accel_v4",
458 .ids = cmpc_accel_device_ids_v4,
459 .ops = {
460 .add = cmpc_accel_add_v4,
461 .remove = cmpc_accel_remove_v4,
462 .notify = cmpc_accel_handler_v4,
463 },
464 .drv.pm = &cmpc_accel_pm,
465};
466
467
468/*
469 * Accelerometer code for Classmate versions prior to V4
80 */ 470 */
81static acpi_status cmpc_start_accel(acpi_handle handle) 471static acpi_status cmpc_start_accel(acpi_handle handle)
82{ 472{
@@ -726,8 +1116,15 @@ static int cmpc_init(void)
726 if (r) 1116 if (r)
727 goto failed_accel; 1117 goto failed_accel;
728 1118
1119 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
1120 if (r)
1121 goto failed_accel_v4;
1122
729 return r; 1123 return r;
730 1124
1125failed_accel_v4:
1126 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
1127
731failed_accel: 1128failed_accel:
732 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 1129 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
733 1130
@@ -743,6 +1140,7 @@ failed_keys:
743 1140
744static void cmpc_exit(void) 1141static void cmpc_exit(void)
745{ 1142{
1143 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);
746 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); 1144 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
747 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 1145 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
748 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); 1146 acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
@@ -754,6 +1152,7 @@ module_exit(cmpc_exit);
754 1152
755static const struct acpi_device_id cmpc_device_ids[] = { 1153static const struct acpi_device_id cmpc_device_ids[] = {
756 {CMPC_ACCEL_HID, 0}, 1154 {CMPC_ACCEL_HID, 0},
1155 {CMPC_ACCEL_HID_V4, 0},
757 {CMPC_TABLET_HID, 0}, 1156 {CMPC_TABLET_HID, 0},
758 {CMPC_IPML_HID, 0}, 1157 {CMPC_IPML_HID, 0},
759 {CMPC_KEYS_HID, 0}, 1158 {CMPC_KEYS_HID, 0},