On Fri, Jun 29, 2012 at 03:39:48PM +0200, Miguel Gómez wrote: > Classmate V4 laptop includes a new accelerometer that can't be handled by > previous driver. This patch adds a new driver to handle it. > > Signed-off-by: Miguel Gómez <mago...@igalia.com>
Hi, Miguel. This seems OK to me. Thanks for this work. Do you have the other functionality of the driver working? I am interested to know if you have the function buttons working. Regards. Cascardo. Acked-by: Thadeu Lima de Souza Cascardo <casca...@holoscopio.com> > --- > drivers/platform/x86/classmate-laptop.c | 400 > ++++++++++++++++++++++++++++++- > 1 file changed, 398 insertions(+), 2 deletions(-) > > diff --git a/drivers/platform/x86/classmate-laptop.c > b/drivers/platform/x86/classmate-laptop.c > index 94f93b6..5de01bc 100644 > --- a/drivers/platform/x86/classmate-laptop.c > +++ b/drivers/platform/x86/classmate-laptop.c > @@ -31,12 +31,18 @@ MODULE_LICENSE("GPL"); > > struct cmpc_accel { > int sensitivity; > + int g_select; > + int inputdev_state; > }; > > -#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 > +#define CMPC_ACCEL_DEV_STATE_CLOSED 0 > +#define CMPC_ACCEL_DEV_STATE_OPEN 1 > > +#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 > +#define CMPC_ACCEL_G_SELECT_DEFAULT 0 > > #define CMPC_ACCEL_HID "ACCE0000" > +#define CMPC_ACCEL_HID_V4 "ACCE0001" > #define CMPC_TABLET_HID "TBLT0000" > #define CMPC_IPML_HID "IPML200" > #define CMPC_KEYS_HID "FnBT0000" > @@ -76,7 +82,388 @@ static int cmpc_remove_acpi_notify_device(struct > acpi_device *acpi) > } > > /* > - * Accelerometer code. > + * Accelerometer code for Classmate V4 > + */ > +static acpi_status cmpc_start_accel_v4(acpi_handle handle) > +{ > + union acpi_object param[4]; > + struct acpi_object_list input; > + acpi_status status; > + > + param[0].type = ACPI_TYPE_INTEGER; > + param[0].integer.value = 0x3; > + param[1].type = ACPI_TYPE_INTEGER; > + param[1].integer.value = 0; > + param[2].type = ACPI_TYPE_INTEGER; > + param[2].integer.value = 0; > + param[3].type = ACPI_TYPE_INTEGER; > + param[3].integer.value = 0; > + input.count = 4; > + input.pointer = param; > + status = acpi_evaluate_object(handle, "ACMD", &input, NULL); > + return status; > +} > + > +static acpi_status cmpc_stop_accel_v4(acpi_handle handle) > +{ > + union acpi_object param[4]; > + struct acpi_object_list input; > + acpi_status status; > + > + param[0].type = ACPI_TYPE_INTEGER; > + param[0].integer.value = 0x4; > + param[1].type = ACPI_TYPE_INTEGER; > + param[1].integer.value = 0; > + param[2].type = ACPI_TYPE_INTEGER; > + param[2].integer.value = 0; > + param[3].type = ACPI_TYPE_INTEGER; > + param[3].integer.value = 0; > + input.count = 4; > + input.pointer = param; > + status = acpi_evaluate_object(handle, "ACMD", &input, NULL); > + return status; > +} > + > +static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val) > +{ > + union acpi_object param[4]; > + struct acpi_object_list input; > + > + param[0].type = ACPI_TYPE_INTEGER; > + param[0].integer.value = 0x02; > + param[1].type = ACPI_TYPE_INTEGER; > + param[1].integer.value = val; > + param[2].type = ACPI_TYPE_INTEGER; > + param[2].integer.value = 0; > + param[3].type = ACPI_TYPE_INTEGER; > + param[3].integer.value = 0; > + input.count = 4; > + input.pointer = param; > + return acpi_evaluate_object(handle, "ACMD", &input, NULL); > +} > + > +static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val) > +{ > + union acpi_object param[4]; > + struct acpi_object_list input; > + > + param[0].type = ACPI_TYPE_INTEGER; > + param[0].integer.value = 0x05; > + param[1].type = ACPI_TYPE_INTEGER; > + param[1].integer.value = val; > + param[2].type = ACPI_TYPE_INTEGER; > + param[2].integer.value = 0; > + param[3].type = ACPI_TYPE_INTEGER; > + param[3].integer.value = 0; > + input.count = 4; > + input.pointer = param; > + return acpi_evaluate_object(handle, "ACMD", &input, NULL); > +} > + > +static acpi_status cmpc_get_accel_v4(acpi_handle handle, > + int16_t *x, > + int16_t *y, > + int16_t *z) > +{ > + union acpi_object param[4]; > + struct acpi_object_list input; > + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; > + int16_t *locs; > + acpi_status status; > + > + param[0].type = ACPI_TYPE_INTEGER; > + param[0].integer.value = 0x01; > + param[1].type = ACPI_TYPE_INTEGER; > + param[1].integer.value = 0; > + param[2].type = ACPI_TYPE_INTEGER; > + param[2].integer.value = 0; > + param[3].type = ACPI_TYPE_INTEGER; > + param[3].integer.value = 0; > + input.count = 4; > + input.pointer = param; > + status = acpi_evaluate_object(handle, "ACMD", &input, &output); > + if (ACPI_SUCCESS(status)) { > + union acpi_object *obj; > + obj = output.pointer; > + locs = (int16_t *) obj->buffer.pointer; > + *x = locs[0]; > + *y = locs[1]; > + *z = locs[2]; > + kfree(output.pointer); > + } > + return status; > +} > + > +static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event) > +{ > + if (event == 0x81) { > + int16_t x, y, z; > + acpi_status status; > + > + status = cmpc_get_accel_v4(dev->handle, &x, &y, &z); > + if (ACPI_SUCCESS(status)) { > + struct input_dev *inputdev = dev_get_drvdata(&dev->dev); > + > + input_report_abs(inputdev, ABS_X, x); > + input_report_abs(inputdev, ABS_Y, y); > + input_report_abs(inputdev, ABS_Z, z); > + input_sync(inputdev); > + } > + } > +} > + > +static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct acpi_device *acpi; > + struct input_dev *inputdev; > + struct cmpc_accel *accel; > + > + acpi = to_acpi_device(dev); > + inputdev = dev_get_drvdata(&acpi->dev); > + accel = dev_get_drvdata(&inputdev->dev); > + > + return sprintf(buf, "%d\n", accel->sensitivity); > +} > + > +static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct acpi_device *acpi; > + struct input_dev *inputdev; > + struct cmpc_accel *accel; > + unsigned long sensitivity; > + int r; > + > + acpi = to_acpi_device(dev); > + inputdev = dev_get_drvdata(&acpi->dev); > + accel = dev_get_drvdata(&inputdev->dev); > + > + r = kstrtoul(buf, 0, &sensitivity); > + if (r) > + return r; > + > + /* sensitivity must be between 1 and 127 */ > + if (sensitivity < 1 || sensitivity > 127) > + return -EINVAL; > + > + accel->sensitivity = sensitivity; > + cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity); > + > + return strnlen(buf, count); > +} > + > +static struct device_attribute cmpc_accel_sensitivity_attr_v4 = { > + .attr = { .name = "sensitivity", .mode = 0660 }, > + .show = cmpc_accel_sensitivity_show_v4, > + .store = cmpc_accel_sensitivity_store_v4 > +}; > + > +static ssize_t cmpc_accel_g_select_show_v4(struct device *dev, > + struct device_attribute *attr, > + char *buf) > +{ > + struct acpi_device *acpi; > + struct input_dev *inputdev; > + struct cmpc_accel *accel; > + > + acpi = to_acpi_device(dev); > + inputdev = dev_get_drvdata(&acpi->dev); > + accel = dev_get_drvdata(&inputdev->dev); > + > + return sprintf(buf, "%d\n", accel->g_select); > +} > + > +static ssize_t cmpc_accel_g_select_store_v4(struct device *dev, > + struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct acpi_device *acpi; > + struct input_dev *inputdev; > + struct cmpc_accel *accel; > + unsigned long g_select; > + int r; > + > + acpi = to_acpi_device(dev); > + inputdev = dev_get_drvdata(&acpi->dev); > + accel = dev_get_drvdata(&inputdev->dev); > + > + r = kstrtoul(buf, 0, &g_select); > + if (r) > + return r; > + > + /* 0 means 1.5g, 1 means 6g, everything else is wrong */ > + if (g_select != 0 && g_select != 1) > + return -EINVAL; > + > + accel->g_select = g_select; > + cmpc_accel_set_g_select_v4(acpi->handle, g_select); > + > + return strnlen(buf, count); > +} > + > +static struct device_attribute cmpc_accel_g_select_attr_v4 = { > + .attr = { .name = "g_select", .mode = 0660 }, > + .show = cmpc_accel_g_select_show_v4, > + .store = cmpc_accel_g_select_store_v4 > +}; > + > +static int cmpc_accel_open_v4(struct input_dev *input) > +{ > + struct acpi_device *acpi; > + struct cmpc_accel *accel; > + > + acpi = to_acpi_device(input->dev.parent); > + accel = dev_get_drvdata(&input->dev); > + > + cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); > + cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); > + > + if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) { > + accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN; > + return 0; > + } > + return -EIO; > +} > + > +static void cmpc_accel_close_v4(struct input_dev *input) > +{ > + struct acpi_device *acpi; > + struct cmpc_accel *accel; > + > + acpi = to_acpi_device(input->dev.parent); > + accel = dev_get_drvdata(&input->dev); > + > + cmpc_stop_accel_v4(acpi->handle); > + accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; > +} > + > +static void cmpc_accel_idev_init_v4(struct input_dev *inputdev) > +{ > + set_bit(EV_ABS, inputdev->evbit); > + input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0); > + input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0); > + input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0); > + inputdev->open = cmpc_accel_open_v4; > + inputdev->close = cmpc_accel_close_v4; > +} > + > +static int cmpc_accel_suspend_v4(struct acpi_device *acpi, pm_message_t > state) > +{ > + struct input_dev *inputdev; > + struct cmpc_accel *accel; > + > + inputdev = dev_get_drvdata(&acpi->dev); > + accel = dev_get_drvdata(&inputdev->dev); > + > + if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) > + return cmpc_stop_accel_v4(acpi->handle); > + > + return 0; > +} > + > +static int cmpc_accel_resume_v4(struct acpi_device *acpi) > +{ > + > + struct input_dev *inputdev; > + struct cmpc_accel *accel; > + > + inputdev = dev_get_drvdata(&acpi->dev); > + accel = dev_get_drvdata(&inputdev->dev); > + > + if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) { > + cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); > + cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); > + > + if (ACPI_FAILURE(cmpc_start_accel_v4(acpi->handle))) > + return -EIO; > + } > + > + return 0; > +} > + > +static int cmpc_accel_add_v4(struct acpi_device *acpi) > +{ > + int error; > + struct input_dev *inputdev; > + struct cmpc_accel *accel; > + > + accel = kmalloc(sizeof(*accel), GFP_KERNEL); > + if (!accel) > + return -ENOMEM; > + > + accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; > + > + accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; > + cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); > + > + error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); > + if (error) > + goto failed_sensitivity; > + > + accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT; > + cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); > + > + error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); > + if (error) > + goto failed_g_select; > + > + error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4", > + cmpc_accel_idev_init_v4); > + if (error) > + goto failed_input; > + > + inputdev = dev_get_drvdata(&acpi->dev); > + dev_set_drvdata(&inputdev->dev, accel); > + > + return 0; > + > +failed_input: > + device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); > +failed_g_select: > + device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); > +failed_sensitivity: > + kfree(accel); > + return error; > +} > + > +static int cmpc_accel_remove_v4(struct acpi_device *acpi, int type) > +{ > + struct input_dev *inputdev; > + struct cmpc_accel *accel; > + > + inputdev = dev_get_drvdata(&acpi->dev); > + accel = dev_get_drvdata(&inputdev->dev); > + > + device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); > + device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); > + return cmpc_remove_acpi_notify_device(acpi); > +} > + > +static const struct acpi_device_id cmpc_accel_device_ids_v4[] = { > + {CMPC_ACCEL_HID_V4, 0}, > + {"", 0} > +}; > + > +static struct acpi_driver cmpc_accel_acpi_driver_v4 = { > + .owner = THIS_MODULE, > + .name = "cmpc_accel_v4", > + .class = "cmpc_accel_v4", > + .ids = cmpc_accel_device_ids_v4, > + .ops = { > + .add = cmpc_accel_add_v4, > + .remove = cmpc_accel_remove_v4, > + .notify = cmpc_accel_handler_v4, > + .suspend = cmpc_accel_suspend_v4, > + .resume = cmpc_accel_resume_v4, > + } > +}; > + > + > +/* > + * Accelerometer code for Classmate versions prior to V4 > */ > static acpi_status cmpc_start_accel(acpi_handle handle) > { > @@ -723,8 +1110,15 @@ static int cmpc_init(void) > if (r) > goto failed_accel; > > + r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4); > + if (r) > + goto failed_accel_v4; > + > return r; > > +failed_accel_v4: > + acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); > + > failed_accel: > acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); > > @@ -740,6 +1134,7 @@ failed_keys: > > static void cmpc_exit(void) > { > + acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4); > acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); > acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); > acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); > @@ -751,6 +1146,7 @@ module_exit(cmpc_exit); > > static const struct acpi_device_id cmpc_device_ids[] = { > {CMPC_ACCEL_HID, 0}, > + {CMPC_ACCEL_HID_V4, 0}, > {CMPC_TABLET_HID, 0}, > {CMPC_IPML_HID, 0}, > {CMPC_KEYS_HID, 0}, > -- > 1.7.9.5 >
signature.asc
Description: Digital signature