diff options
| -rw-r--r-- | drivers/platform/x86/intel-hid.c | 96 |
1 files changed, 91 insertions, 5 deletions
diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index cb3ab2b212b1..bcf438f38781 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Intel HID event driver for Windows 8 | 2 | * Intel HID event & 5 button array driver |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2015 Alex Hung <alex.hung@canonical.com> | 4 | * Copyright (C) 2015 Alex Hung <alex.hung@canonical.com> |
| 5 | * Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org> | 5 | * Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org> |
| @@ -57,8 +57,24 @@ static const struct key_entry intel_hid_keymap[] = { | |||
| 57 | { KE_END }, | 57 | { KE_END }, |
| 58 | }; | 58 | }; |
| 59 | 59 | ||
| 60 | /* 5 button array notification value. */ | ||
| 61 | static const struct key_entry intel_array_keymap[] = { | ||
| 62 | { KE_KEY, 0xC2, { KEY_LEFTMETA } }, /* Press */ | ||
| 63 | { KE_IGNORE, 0xC3, { KEY_LEFTMETA } }, /* Release */ | ||
| 64 | { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* Press */ | ||
| 65 | { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* Release */ | ||
| 66 | { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* Press */ | ||
| 67 | { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } }, /* Release */ | ||
| 68 | { KE_SW, 0xC8, { .sw = { SW_ROTATE_LOCK, 1 } } }, /* Press */ | ||
| 69 | { KE_SW, 0xC9, { .sw = { SW_ROTATE_LOCK, 0 } } }, /* Release */ | ||
| 70 | { KE_KEY, 0xCE, { KEY_POWER } }, /* Press */ | ||
| 71 | { KE_IGNORE, 0xCF, { KEY_POWER } }, /* Release */ | ||
| 72 | { KE_END }, | ||
| 73 | }; | ||
| 74 | |||
| 60 | struct intel_hid_priv { | 75 | struct intel_hid_priv { |
| 61 | struct input_dev *input_dev; | 76 | struct input_dev *input_dev; |
| 77 | struct input_dev *array; | ||
| 62 | }; | 78 | }; |
| 63 | 79 | ||
| 64 | static int intel_hid_set_enable(struct device *device, int enable) | 80 | static int intel_hid_set_enable(struct device *device, int enable) |
| @@ -78,15 +94,43 @@ static int intel_hid_set_enable(struct device *device, int enable) | |||
| 78 | return 0; | 94 | return 0; |
| 79 | } | 95 | } |
| 80 | 96 | ||
| 97 | static void intel_button_array_enable(struct device *device, bool enable) | ||
| 98 | { | ||
| 99 | struct intel_hid_priv *priv = dev_get_drvdata(device); | ||
| 100 | acpi_handle handle = ACPI_HANDLE(device); | ||
| 101 | unsigned long long button_cap; | ||
| 102 | acpi_status status; | ||
| 103 | |||
| 104 | if (!priv->array) | ||
| 105 | return; | ||
| 106 | |||
| 107 | /* Query supported platform features */ | ||
| 108 | status = acpi_evaluate_integer(handle, "BTNC", NULL, &button_cap); | ||
| 109 | if (ACPI_FAILURE(status)) { | ||
| 110 | dev_warn(device, "failed to get button capability\n"); | ||
| 111 | return; | ||
| 112 | } | ||
| 113 | |||
| 114 | /* Enable|disable features - power button is always enabled */ | ||
| 115 | status = acpi_execute_simple_method(handle, "BTNE", | ||
| 116 | enable ? button_cap : 1); | ||
| 117 | if (ACPI_FAILURE(status)) | ||
| 118 | dev_warn(device, "failed to set button capability\n"); | ||
| 119 | } | ||
| 120 | |||
| 81 | static int intel_hid_pl_suspend_handler(struct device *device) | 121 | static int intel_hid_pl_suspend_handler(struct device *device) |
| 82 | { | 122 | { |
| 83 | intel_hid_set_enable(device, 0); | 123 | intel_hid_set_enable(device, 0); |
| 124 | intel_button_array_enable(device, false); | ||
| 125 | |||
| 84 | return 0; | 126 | return 0; |
| 85 | } | 127 | } |
| 86 | 128 | ||
| 87 | static int intel_hid_pl_resume_handler(struct device *device) | 129 | static int intel_hid_pl_resume_handler(struct device *device) |
| 88 | { | 130 | { |
| 89 | intel_hid_set_enable(device, 1); | 131 | intel_hid_set_enable(device, 1); |
| 132 | intel_button_array_enable(device, true); | ||
| 133 | |||
| 90 | return 0; | 134 | return 0; |
| 91 | } | 135 | } |
| 92 | 136 | ||
| @@ -126,6 +170,27 @@ err_free_device: | |||
| 126 | return ret; | 170 | return ret; |
| 127 | } | 171 | } |
| 128 | 172 | ||
| 173 | static int intel_button_array_input_setup(struct platform_device *device) | ||
| 174 | { | ||
| 175 | struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); | ||
| 176 | int ret; | ||
| 177 | |||
| 178 | /* Setup input device for 5 button array */ | ||
| 179 | priv->array = devm_input_allocate_device(&device->dev); | ||
| 180 | if (!priv->array) | ||
| 181 | return -ENOMEM; | ||
| 182 | |||
| 183 | ret = sparse_keymap_setup(priv->array, intel_array_keymap, NULL); | ||
| 184 | if (ret) | ||
| 185 | return ret; | ||
| 186 | |||
| 187 | priv->array->dev.parent = &device->dev; | ||
| 188 | priv->array->name = "Intel HID 5 button array"; | ||
| 189 | priv->array->id.bustype = BUS_HOST; | ||
| 190 | |||
| 191 | return input_register_device(priv->array); | ||
| 192 | } | ||
| 193 | |||
| 129 | static void intel_hid_input_destroy(struct platform_device *device) | 194 | static void intel_hid_input_destroy(struct platform_device *device) |
| 130 | { | 195 | { |
| 131 | struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); | 196 | struct intel_hid_priv *priv = dev_get_drvdata(&device->dev); |
| @@ -140,10 +205,11 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) | |||
| 140 | unsigned long long ev_index; | 205 | unsigned long long ev_index; |
| 141 | acpi_status status; | 206 | acpi_status status; |
| 142 | 207 | ||
| 143 | /* The platform spec only defines one event code: 0xC0. */ | 208 | /* 0xC0 is for HID events, other values are for 5 button array */ |
| 144 | if (event != 0xc0) { | 209 | if (event != 0xc0) { |
| 145 | dev_warn(&device->dev, "received unknown event (0x%x)\n", | 210 | if (!priv->array || |
| 146 | event); | 211 | !sparse_keymap_report_event(priv->array, event, 1, true)) |
| 212 | dev_info(&device->dev, "unknown event 0x%x\n", event); | ||
| 147 | return; | 213 | return; |
| 148 | } | 214 | } |
| 149 | 215 | ||
| @@ -161,8 +227,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) | |||
| 161 | static int intel_hid_probe(struct platform_device *device) | 227 | static int intel_hid_probe(struct platform_device *device) |
| 162 | { | 228 | { |
| 163 | acpi_handle handle = ACPI_HANDLE(&device->dev); | 229 | acpi_handle handle = ACPI_HANDLE(&device->dev); |
| 230 | unsigned long long event_cap, mode; | ||
| 164 | struct intel_hid_priv *priv; | 231 | struct intel_hid_priv *priv; |
| 165 | unsigned long long mode; | ||
| 166 | acpi_status status; | 232 | acpi_status status; |
| 167 | int err; | 233 | int err; |
| 168 | 234 | ||
| @@ -193,6 +259,15 @@ static int intel_hid_probe(struct platform_device *device) | |||
| 193 | return err; | 259 | return err; |
| 194 | } | 260 | } |
| 195 | 261 | ||
| 262 | /* Setup 5 button array */ | ||
| 263 | status = acpi_evaluate_integer(handle, "HEBC", NULL, &event_cap); | ||
| 264 | if (ACPI_SUCCESS(status) && (event_cap & 0x20000)) { | ||
| 265 | dev_info(&device->dev, "platform supports 5 button array\n"); | ||
| 266 | err = intel_button_array_input_setup(device); | ||
| 267 | if (err) | ||
| 268 | pr_err("Failed to setup Intel 5 button array hotkeys\n"); | ||
| 269 | } | ||
| 270 | |||
| 196 | status = acpi_install_notify_handler(handle, | 271 | status = acpi_install_notify_handler(handle, |
| 197 | ACPI_DEVICE_NOTIFY, | 272 | ACPI_DEVICE_NOTIFY, |
| 198 | notify_handler, | 273 | notify_handler, |
| @@ -206,6 +281,16 @@ static int intel_hid_probe(struct platform_device *device) | |||
| 206 | if (err) | 281 | if (err) |
| 207 | goto err_remove_notify; | 282 | goto err_remove_notify; |
| 208 | 283 | ||
| 284 | if (priv->array) { | ||
| 285 | intel_button_array_enable(&device->dev, true); | ||
| 286 | |||
| 287 | /* Call button load method to enable HID power button */ | ||
| 288 | status = acpi_evaluate_object(handle, "BTNL", NULL, NULL); | ||
| 289 | if (ACPI_FAILURE(status)) | ||
| 290 | dev_warn(&device->dev, | ||
| 291 | "failed to enable HID power button\n"); | ||
| 292 | } | ||
| 293 | |||
| 209 | return 0; | 294 | return 0; |
| 210 | 295 | ||
| 211 | err_remove_notify: | 296 | err_remove_notify: |
| @@ -224,6 +309,7 @@ static int intel_hid_remove(struct platform_device *device) | |||
| 224 | acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); | 309 | acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler); |
| 225 | intel_hid_input_destroy(device); | 310 | intel_hid_input_destroy(device); |
| 226 | intel_hid_set_enable(&device->dev, 0); | 311 | intel_hid_set_enable(&device->dev, 0); |
| 312 | intel_button_array_enable(&device->dev, false); | ||
| 227 | 313 | ||
| 228 | /* | 314 | /* |
| 229 | * Even if we failed to shut off the event stream, we can still | 315 | * Even if we failed to shut off the event stream, we can still |
