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
> 

Attachment: signature.asc
Description: Digital signature

Reply via email to