platform/x86: intel-vbtn: Detect switch position before registering the input-device
[linux-2.6-microblaze.git] / drivers / platform / x86 / intel-vbtn.c
index b588093..ef92c1c 100644 (file)
@@ -40,28 +40,70 @@ static const struct key_entry intel_vbtn_keymap[] = {
        { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } },        /* volume-down key release */
        { KE_KEY,    0xC8, { KEY_ROTATE_LOCK_TOGGLE } },        /* rotate-lock key press */
        { KE_KEY,    0xC9, { KEY_ROTATE_LOCK_TOGGLE } },        /* rotate-lock key release */
+};
+
+static const struct key_entry intel_vbtn_switchmap[] = {
        { KE_SW,     0xCA, { .sw = { SW_DOCK, 1 } } },          /* Docked */
        { KE_SW,     0xCB, { .sw = { SW_DOCK, 0 } } },          /* Undocked */
        { KE_SW,     0xCC, { .sw = { SW_TABLET_MODE, 1 } } },   /* Tablet */
        { KE_SW,     0xCD, { .sw = { SW_TABLET_MODE, 0 } } },   /* Laptop */
-       { KE_END },
 };
 
+#define KEYMAP_LEN \
+       (ARRAY_SIZE(intel_vbtn_keymap) + ARRAY_SIZE(intel_vbtn_switchmap) + 1)
+
 struct intel_vbtn_priv {
+       struct key_entry keymap[KEYMAP_LEN];
        struct input_dev *input_dev;
+       bool has_buttons;
+       bool has_switches;
        bool wakeup_mode;
 };
 
+static void detect_tablet_mode(struct platform_device *device)
+{
+       struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
+       acpi_handle handle = ACPI_HANDLE(&device->dev);
+       unsigned long long vgbs;
+       acpi_status status;
+       int m;
+
+       status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
+       if (ACPI_FAILURE(status))
+               return;
+
+       m = !(vgbs & TABLET_MODE_FLAG);
+       input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
+       m = (vgbs & DOCK_MODE_FLAG) ? 1 : 0;
+       input_report_switch(priv->input_dev, SW_DOCK, m);
+}
+
 static int intel_vbtn_input_setup(struct platform_device *device)
 {
        struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
-       int ret;
+       int ret, keymap_len = 0;
+
+       if (priv->has_buttons) {
+               memcpy(&priv->keymap[keymap_len], intel_vbtn_keymap,
+                      ARRAY_SIZE(intel_vbtn_keymap) *
+                      sizeof(struct key_entry));
+               keymap_len += ARRAY_SIZE(intel_vbtn_keymap);
+       }
+
+       if (priv->has_switches) {
+               memcpy(&priv->keymap[keymap_len], intel_vbtn_switchmap,
+                      ARRAY_SIZE(intel_vbtn_switchmap) *
+                      sizeof(struct key_entry));
+               keymap_len += ARRAY_SIZE(intel_vbtn_switchmap);
+       }
+
+       priv->keymap[keymap_len].type = KE_END;
 
        priv->input_dev = devm_input_allocate_device(&device->dev);
        if (!priv->input_dev)
                return -ENOMEM;
 
-       ret = sparse_keymap_setup(priv->input_dev, intel_vbtn_keymap, NULL);
+       ret = sparse_keymap_setup(priv->input_dev, priv->keymap, NULL);
        if (ret)
                return ret;
 
@@ -69,6 +111,9 @@ static int intel_vbtn_input_setup(struct platform_device *device)
        priv->input_dev->name = "Intel Virtual Button driver";
        priv->input_dev->id.bustype = BUS_HOST;
 
+       if (priv->has_switches)
+               detect_tablet_mode(device);
+
        return input_register_device(priv->input_dev);
 }
 
@@ -114,44 +159,49 @@ out_unknown:
        dev_dbg(&device->dev, "unknown event index 0x%x\n", event);
 }
 
-static void detect_tablet_mode(struct platform_device *device)
+static bool intel_vbtn_has_buttons(acpi_handle handle)
 {
-       const char *chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
-       struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
-       acpi_handle handle = ACPI_HANDLE(&device->dev);
-       struct acpi_buffer vgbs_output = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *obj;
        acpi_status status;
-       int m;
 
-       if (!(chassis_type && strcmp(chassis_type, "31") == 0))
-               goto out;
+       status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
+       return ACPI_SUCCESS(status);
+}
 
-       status = acpi_evaluate_object(handle, "VGBS", NULL, &vgbs_output);
-       if (ACPI_FAILURE(status))
-               goto out;
+static bool intel_vbtn_has_switches(acpi_handle handle)
+{
+       const char *chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE);
+       unsigned long chassis_type_int;
+       unsigned long long vgbs;
+       acpi_status status;
 
-       obj = vgbs_output.pointer;
-       if (!(obj && obj->type == ACPI_TYPE_INTEGER))
-               goto out;
+       if (kstrtoul(chassis_type, 10, &chassis_type_int))
+               return false;
 
-       m = !(obj->integer.value & TABLET_MODE_FLAG);
-       input_report_switch(priv->input_dev, SW_TABLET_MODE, m);
-       m = (obj->integer.value & DOCK_MODE_FLAG) ? 1 : 0;
-       input_report_switch(priv->input_dev, SW_DOCK, m);
-out:
-       kfree(vgbs_output.pointer);
+       switch (chassis_type_int) {
+       case  8: /* Portable */
+       case 31: /* Convertible */
+       case 32: /* Detachable */
+               break;
+       default:
+               return false;
+       }
+
+       status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
+       return ACPI_SUCCESS(status);
 }
 
 static int intel_vbtn_probe(struct platform_device *device)
 {
        acpi_handle handle = ACPI_HANDLE(&device->dev);
+       bool has_buttons, has_switches;
        struct intel_vbtn_priv *priv;
        acpi_status status;
        int err;
 
-       status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
-       if (ACPI_FAILURE(status)) {
+       has_buttons = intel_vbtn_has_buttons(handle);
+       has_switches = intel_vbtn_has_switches(handle);
+
+       if (!has_buttons && !has_switches) {
                dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
                return -ENODEV;
        }
@@ -161,14 +211,15 @@ static int intel_vbtn_probe(struct platform_device *device)
                return -ENOMEM;
        dev_set_drvdata(&device->dev, priv);
 
+       priv->has_buttons = has_buttons;
+       priv->has_switches = has_switches;
+
        err = intel_vbtn_input_setup(device);
        if (err) {
                pr_err("Failed to setup Intel Virtual Button\n");
                return err;
        }
 
-       detect_tablet_mode(device);
-
        status = acpi_install_notify_handler(handle,
                                             ACPI_DEVICE_NOTIFY,
                                             notify_handler,