| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Supports for the button array on SoC tablets originally running |
|---|
| 3 | 4 | * Windows 8. |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * (C) Copyright 2014 Intel Corporation |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or |
|---|
| 8 | | - * modify it under the terms of the GNU General Public License |
|---|
| 9 | | - * as published by the Free Software Foundation; version 2 |
|---|
| 10 | | - * of the License. |
|---|
| 11 | 7 | */ |
|---|
| 12 | 8 | |
|---|
| 13 | 9 | #include <linux/module.h> |
|---|
| 14 | 10 | #include <linux/input.h> |
|---|
| 15 | 11 | #include <linux/init.h> |
|---|
| 12 | +#include <linux/irq.h> |
|---|
| 16 | 13 | #include <linux/kernel.h> |
|---|
| 17 | 14 | #include <linux/acpi.h> |
|---|
| 15 | +#include <linux/dmi.h> |
|---|
| 18 | 16 | #include <linux/gpio/consumer.h> |
|---|
| 19 | 17 | #include <linux/gpio_keys.h> |
|---|
| 20 | 18 | #include <linux/gpio.h> |
|---|
| 21 | 19 | #include <linux/platform_device.h> |
|---|
| 20 | + |
|---|
| 21 | +static bool use_low_level_irq; |
|---|
| 22 | +module_param(use_low_level_irq, bool, 0444); |
|---|
| 23 | +MODULE_PARM_DESC(use_low_level_irq, "Use low-level triggered IRQ instead of edge triggered"); |
|---|
| 22 | 24 | |
|---|
| 23 | 25 | struct soc_button_info { |
|---|
| 24 | 26 | const char *name; |
|---|
| .. | .. |
|---|
| 27 | 29 | unsigned int event_code; |
|---|
| 28 | 30 | bool autorepeat; |
|---|
| 29 | 31 | bool wakeup; |
|---|
| 32 | + bool active_low; |
|---|
| 33 | +}; |
|---|
| 34 | + |
|---|
| 35 | +struct soc_device_data { |
|---|
| 36 | + const struct soc_button_info *button_info; |
|---|
| 37 | + int (*check)(struct device *dev); |
|---|
| 30 | 38 | }; |
|---|
| 31 | 39 | |
|---|
| 32 | 40 | /* |
|---|
| .. | .. |
|---|
| 41 | 49 | }; |
|---|
| 42 | 50 | |
|---|
| 43 | 51 | /* |
|---|
| 52 | + * Some 2-in-1s which use the soc_button_array driver have this ugly issue in |
|---|
| 53 | + * their DSDT where the _LID method modifies the irq-type settings of the GPIOs |
|---|
| 54 | + * used for the power and home buttons. The intend of this AML code is to |
|---|
| 55 | + * disable these buttons when the lid is closed. |
|---|
| 56 | + * The AML does this by directly poking the GPIO controllers registers. This is |
|---|
| 57 | + * problematic because when re-enabling the irq, which happens whenever _LID |
|---|
| 58 | + * gets called with the lid open (e.g. on boot and on resume), it sets the |
|---|
| 59 | + * irq-type to IRQ_TYPE_LEVEL_LOW. Where as the gpio-keys driver programs the |
|---|
| 60 | + * type to, and expects it to be, IRQ_TYPE_EDGE_BOTH. |
|---|
| 61 | + * To work around this we don't set gpio_keys_button.gpio on these 2-in-1s, |
|---|
| 62 | + * instead we get the irq for the GPIO ourselves, configure it as |
|---|
| 63 | + * IRQ_TYPE_LEVEL_LOW (to match how the _LID AML code configures it) and pass |
|---|
| 64 | + * the irq in gpio_keys_button.irq. Below is a list of affected devices. |
|---|
| 65 | + */ |
|---|
| 66 | +static const struct dmi_system_id dmi_use_low_level_irq[] = { |
|---|
| 67 | + { |
|---|
| 68 | + /* |
|---|
| 69 | + * Acer Switch 10 SW5-012. _LID method messes with home- and |
|---|
| 70 | + * power-button GPIO IRQ settings. When (re-)enabling the irq |
|---|
| 71 | + * it ors in its own flags without clearing the previous set |
|---|
| 72 | + * ones, leading to an irq-type of IRQ_TYPE_LEVEL_LOW | |
|---|
| 73 | + * IRQ_TYPE_LEVEL_HIGH causing a continuous interrupt storm. |
|---|
| 74 | + */ |
|---|
| 75 | + .matches = { |
|---|
| 76 | + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), |
|---|
| 77 | + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"), |
|---|
| 78 | + }, |
|---|
| 79 | + }, |
|---|
| 80 | + { |
|---|
| 81 | + /* Acer Switch V 10 SW5-017, same issue as Acer Switch 10 SW5-012. */ |
|---|
| 82 | + .matches = { |
|---|
| 83 | + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), |
|---|
| 84 | + DMI_MATCH(DMI_PRODUCT_NAME, "SW5-017"), |
|---|
| 85 | + }, |
|---|
| 86 | + }, |
|---|
| 87 | + { |
|---|
| 88 | + /* |
|---|
| 89 | + * Acer One S1003. _LID method messes with power-button GPIO |
|---|
| 90 | + * IRQ settings, leading to a non working power-button. |
|---|
| 91 | + */ |
|---|
| 92 | + .matches = { |
|---|
| 93 | + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), |
|---|
| 94 | + DMI_MATCH(DMI_PRODUCT_NAME, "One S1003"), |
|---|
| 95 | + }, |
|---|
| 96 | + }, |
|---|
| 97 | + { |
|---|
| 98 | + /* |
|---|
| 99 | + * Lenovo Yoga Tab2 1051F/1051L, something messes with the home-button |
|---|
| 100 | + * IRQ settings, leading to a non working home-button. |
|---|
| 101 | + */ |
|---|
| 102 | + .matches = { |
|---|
| 103 | + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
|---|
| 104 | + DMI_MATCH(DMI_PRODUCT_NAME, "60073"), |
|---|
| 105 | + DMI_MATCH(DMI_PRODUCT_VERSION, "1051"), |
|---|
| 106 | + }, |
|---|
| 107 | + }, |
|---|
| 108 | + {} /* Terminating entry */ |
|---|
| 109 | +}; |
|---|
| 110 | + |
|---|
| 111 | +/* |
|---|
| 112 | + * Some devices have a wrong entry which points to a GPIO which is |
|---|
| 113 | + * required in another driver, so this driver must not claim it. |
|---|
| 114 | + */ |
|---|
| 115 | +static const struct dmi_system_id dmi_invalid_acpi_index[] = { |
|---|
| 116 | + { |
|---|
| 117 | + /* |
|---|
| 118 | + * Lenovo Yoga Book X90F / X90L, the PNP0C40 home button entry |
|---|
| 119 | + * points to a GPIO which is not a home button and which is |
|---|
| 120 | + * required by the lenovo-yogabook driver. |
|---|
| 121 | + */ |
|---|
| 122 | + .matches = { |
|---|
| 123 | + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), |
|---|
| 124 | + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"), |
|---|
| 125 | + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"), |
|---|
| 126 | + }, |
|---|
| 127 | + .driver_data = (void *)1l, |
|---|
| 128 | + }, |
|---|
| 129 | + {} /* Terminating entry */ |
|---|
| 130 | +}; |
|---|
| 131 | + |
|---|
| 132 | +/* |
|---|
| 44 | 133 | * Get the Nth GPIO number from the ACPI object. |
|---|
| 45 | 134 | */ |
|---|
| 46 | | -static int soc_button_lookup_gpio(struct device *dev, int acpi_index) |
|---|
| 135 | +static int soc_button_lookup_gpio(struct device *dev, int acpi_index, |
|---|
| 136 | + int *gpio_ret, int *irq_ret) |
|---|
| 47 | 137 | { |
|---|
| 48 | 138 | struct gpio_desc *desc; |
|---|
| 49 | | - int gpio; |
|---|
| 50 | 139 | |
|---|
| 51 | 140 | desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS); |
|---|
| 52 | 141 | if (IS_ERR(desc)) |
|---|
| 53 | 142 | return PTR_ERR(desc); |
|---|
| 54 | 143 | |
|---|
| 55 | | - gpio = desc_to_gpio(desc); |
|---|
| 144 | + *gpio_ret = desc_to_gpio(desc); |
|---|
| 145 | + *irq_ret = gpiod_to_irq(desc); |
|---|
| 56 | 146 | |
|---|
| 57 | 147 | gpiod_put(desc); |
|---|
| 58 | 148 | |
|---|
| 59 | | - return gpio; |
|---|
| 149 | + return 0; |
|---|
| 60 | 150 | } |
|---|
| 61 | 151 | |
|---|
| 62 | 152 | static struct platform_device * |
|---|
| .. | .. |
|---|
| 68 | 158 | struct platform_device *pd; |
|---|
| 69 | 159 | struct gpio_keys_button *gpio_keys; |
|---|
| 70 | 160 | struct gpio_keys_platform_data *gpio_keys_pdata; |
|---|
| 161 | + const struct dmi_system_id *dmi_id; |
|---|
| 162 | + int invalid_acpi_index = -1; |
|---|
| 163 | + int error, gpio, irq; |
|---|
| 71 | 164 | int n_buttons = 0; |
|---|
| 72 | | - int gpio; |
|---|
| 73 | | - int error; |
|---|
| 74 | 165 | |
|---|
| 75 | 166 | for (info = button_info; info->name; info++) |
|---|
| 76 | 167 | if (info->autorepeat == autorepeat) |
|---|
| .. | .. |
|---|
| 86 | 177 | gpio_keys = (void *)(gpio_keys_pdata + 1); |
|---|
| 87 | 178 | n_buttons = 0; |
|---|
| 88 | 179 | |
|---|
| 180 | + dmi_id = dmi_first_match(dmi_invalid_acpi_index); |
|---|
| 181 | + if (dmi_id) |
|---|
| 182 | + invalid_acpi_index = (long)dmi_id->driver_data; |
|---|
| 183 | + |
|---|
| 89 | 184 | for (info = button_info; info->name; info++) { |
|---|
| 90 | 185 | if (info->autorepeat != autorepeat) |
|---|
| 91 | 186 | continue; |
|---|
| 92 | 187 | |
|---|
| 93 | | - gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); |
|---|
| 94 | | - if (!gpio_is_valid(gpio)) |
|---|
| 188 | + if (info->acpi_index == invalid_acpi_index) |
|---|
| 95 | 189 | continue; |
|---|
| 190 | + |
|---|
| 191 | + error = soc_button_lookup_gpio(&pdev->dev, info->acpi_index, &gpio, &irq); |
|---|
| 192 | + if (error || irq < 0) { |
|---|
| 193 | + /* |
|---|
| 194 | + * Skip GPIO if not present. Note we deliberately |
|---|
| 195 | + * ignore -EPROBE_DEFER errors here. On some devices |
|---|
| 196 | + * Intel is using so called virtual GPIOs which are not |
|---|
| 197 | + * GPIOs at all but some way for AML code to check some |
|---|
| 198 | + * random status bits without need a custom opregion. |
|---|
| 199 | + * In some cases the resources table we parse points to |
|---|
| 200 | + * such a virtual GPIO, since these are not real GPIOs |
|---|
| 201 | + * we do not have a driver for these so they will never |
|---|
| 202 | + * show up, therefore we ignore -EPROBE_DEFER. |
|---|
| 203 | + */ |
|---|
| 204 | + continue; |
|---|
| 205 | + } |
|---|
| 206 | + |
|---|
| 207 | + /* See dmi_use_low_level_irq[] comment */ |
|---|
| 208 | + if (!autorepeat && (use_low_level_irq || |
|---|
| 209 | + dmi_check_system(dmi_use_low_level_irq))) { |
|---|
| 210 | + irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); |
|---|
| 211 | + gpio_keys[n_buttons].irq = irq; |
|---|
| 212 | + gpio_keys[n_buttons].gpio = -ENOENT; |
|---|
| 213 | + } else { |
|---|
| 214 | + gpio_keys[n_buttons].gpio = gpio; |
|---|
| 215 | + } |
|---|
| 96 | 216 | |
|---|
| 97 | 217 | gpio_keys[n_buttons].type = info->event_type; |
|---|
| 98 | 218 | gpio_keys[n_buttons].code = info->event_code; |
|---|
| 99 | | - gpio_keys[n_buttons].gpio = gpio; |
|---|
| 100 | | - gpio_keys[n_buttons].active_low = 1; |
|---|
| 219 | + gpio_keys[n_buttons].active_low = info->active_low; |
|---|
| 101 | 220 | gpio_keys[n_buttons].desc = info->name; |
|---|
| 102 | 221 | gpio_keys[n_buttons].wakeup = info->wakeup; |
|---|
| 103 | 222 | /* These devices often use cheap buttons, use 50 ms debounce */ |
|---|
| .. | .. |
|---|
| 114 | 233 | gpio_keys_pdata->nbuttons = n_buttons; |
|---|
| 115 | 234 | gpio_keys_pdata->rep = autorepeat; |
|---|
| 116 | 235 | |
|---|
| 117 | | - pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); |
|---|
| 118 | | - if (!pd) { |
|---|
| 119 | | - error = -ENOMEM; |
|---|
| 236 | + pd = platform_device_register_resndata(&pdev->dev, "gpio-keys", |
|---|
| 237 | + PLATFORM_DEVID_AUTO, NULL, 0, |
|---|
| 238 | + gpio_keys_pdata, |
|---|
| 239 | + sizeof(*gpio_keys_pdata)); |
|---|
| 240 | + error = PTR_ERR_OR_ZERO(pd); |
|---|
| 241 | + if (error) { |
|---|
| 242 | + dev_err(&pdev->dev, |
|---|
| 243 | + "failed registering gpio-keys: %d\n", error); |
|---|
| 120 | 244 | goto err_free_mem; |
|---|
| 121 | 245 | } |
|---|
| 122 | 246 | |
|---|
| 123 | | - error = platform_device_add_data(pd, gpio_keys_pdata, |
|---|
| 124 | | - sizeof(*gpio_keys_pdata)); |
|---|
| 125 | | - if (error) |
|---|
| 126 | | - goto err_free_pdev; |
|---|
| 127 | | - |
|---|
| 128 | | - error = platform_device_add(pd); |
|---|
| 129 | | - if (error) |
|---|
| 130 | | - goto err_free_pdev; |
|---|
| 131 | | - |
|---|
| 132 | 247 | return pd; |
|---|
| 133 | 248 | |
|---|
| 134 | | -err_free_pdev: |
|---|
| 135 | | - platform_device_put(pd); |
|---|
| 136 | 249 | err_free_mem: |
|---|
| 137 | 250 | devm_kfree(&pdev->dev, gpio_keys_pdata); |
|---|
| 138 | 251 | return ERR_PTR(error); |
|---|
| .. | .. |
|---|
| 166 | 279 | } |
|---|
| 167 | 280 | |
|---|
| 168 | 281 | info->event_type = EV_KEY; |
|---|
| 282 | + info->active_low = true; |
|---|
| 169 | 283 | info->acpi_index = |
|---|
| 170 | 284 | soc_button_get_acpi_object_int(&desc->package.elements[1]); |
|---|
| 171 | 285 | upage = soc_button_get_acpi_object_int(&desc->package.elements[3]); |
|---|
| .. | .. |
|---|
| 185 | 299 | info->name = "power"; |
|---|
| 186 | 300 | info->event_code = KEY_POWER; |
|---|
| 187 | 301 | info->wakeup = true; |
|---|
| 302 | + } else if (upage == 0x01 && usage == 0xca) { |
|---|
| 303 | + info->name = "rotation lock switch"; |
|---|
| 304 | + info->event_type = EV_SW; |
|---|
| 305 | + info->event_code = SW_ROTATE_LOCK; |
|---|
| 188 | 306 | } else if (upage == 0x07 && usage == 0xe3) { |
|---|
| 189 | 307 | info->name = "home"; |
|---|
| 190 | 308 | info->event_code = KEY_LEFTMETA; |
|---|
| .. | .. |
|---|
| 309 | 427 | static int soc_button_probe(struct platform_device *pdev) |
|---|
| 310 | 428 | { |
|---|
| 311 | 429 | struct device *dev = &pdev->dev; |
|---|
| 312 | | - const struct acpi_device_id *id; |
|---|
| 313 | | - struct soc_button_info *button_info; |
|---|
| 430 | + const struct soc_device_data *device_data; |
|---|
| 431 | + const struct soc_button_info *button_info; |
|---|
| 314 | 432 | struct soc_button_data *priv; |
|---|
| 315 | 433 | struct platform_device *pd; |
|---|
| 316 | 434 | int i; |
|---|
| 317 | 435 | int error; |
|---|
| 318 | 436 | |
|---|
| 319 | | - id = acpi_match_device(dev->driver->acpi_match_table, dev); |
|---|
| 320 | | - if (!id) |
|---|
| 321 | | - return -ENODEV; |
|---|
| 437 | + device_data = acpi_device_get_match_data(dev); |
|---|
| 438 | + if (device_data && device_data->check) { |
|---|
| 439 | + error = device_data->check(dev); |
|---|
| 440 | + if (error) |
|---|
| 441 | + return error; |
|---|
| 442 | + } |
|---|
| 322 | 443 | |
|---|
| 323 | | - if (!id->driver_data) { |
|---|
| 444 | + if (device_data && device_data->button_info) { |
|---|
| 445 | + button_info = device_data->button_info; |
|---|
| 446 | + } else { |
|---|
| 324 | 447 | button_info = soc_button_get_button_info(dev); |
|---|
| 325 | 448 | if (IS_ERR(button_info)) |
|---|
| 326 | 449 | return PTR_ERR(button_info); |
|---|
| 327 | | - } else { |
|---|
| 328 | | - button_info = (struct soc_button_info *)id->driver_data; |
|---|
| 329 | 450 | } |
|---|
| 330 | 451 | |
|---|
| 331 | 452 | error = gpiod_count(dev, NULL); |
|---|
| .. | .. |
|---|
| 357 | 478 | if (!priv->children[0] && !priv->children[1]) |
|---|
| 358 | 479 | return -ENODEV; |
|---|
| 359 | 480 | |
|---|
| 360 | | - if (!id->driver_data) |
|---|
| 481 | + if (!device_data || !device_data->button_info) |
|---|
| 361 | 482 | devm_kfree(dev, button_info); |
|---|
| 362 | 483 | |
|---|
| 363 | 484 | return 0; |
|---|
| .. | .. |
|---|
| 368 | 489 | * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC |
|---|
| 369 | 490 | * Platforms" |
|---|
| 370 | 491 | */ |
|---|
| 371 | | -static struct soc_button_info soc_button_PNP0C40[] = { |
|---|
| 372 | | - { "power", 0, EV_KEY, KEY_POWER, false, true }, |
|---|
| 373 | | - { "home", 1, EV_KEY, KEY_LEFTMETA, false, true }, |
|---|
| 374 | | - { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, |
|---|
| 375 | | - { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false }, |
|---|
| 376 | | - { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false }, |
|---|
| 492 | +static const struct soc_button_info soc_button_PNP0C40[] = { |
|---|
| 493 | + { "power", 0, EV_KEY, KEY_POWER, false, true, true }, |
|---|
| 494 | + { "home", 1, EV_KEY, KEY_LEFTMETA, false, true, true }, |
|---|
| 495 | + { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, |
|---|
| 496 | + { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, |
|---|
| 497 | + { "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false, true }, |
|---|
| 377 | 498 | { } |
|---|
| 378 | 499 | }; |
|---|
| 379 | 500 | |
|---|
| 501 | +static const struct soc_device_data soc_device_PNP0C40 = { |
|---|
| 502 | + .button_info = soc_button_PNP0C40, |
|---|
| 503 | +}; |
|---|
| 504 | + |
|---|
| 505 | +static const struct soc_button_info soc_button_INT33D3[] = { |
|---|
| 506 | + { "tablet_mode", 0, EV_SW, SW_TABLET_MODE, false, false, false }, |
|---|
| 507 | + { } |
|---|
| 508 | +}; |
|---|
| 509 | + |
|---|
| 510 | +static const struct soc_device_data soc_device_INT33D3 = { |
|---|
| 511 | + .button_info = soc_button_INT33D3, |
|---|
| 512 | +}; |
|---|
| 513 | + |
|---|
| 514 | +/* |
|---|
| 515 | + * Special device check for Surface Book 2 and Surface Pro (2017). |
|---|
| 516 | + * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned |
|---|
| 517 | + * devices use MSHW0040 for power and volume buttons, however the way they |
|---|
| 518 | + * have to be addressed differs. Make sure that we only load this drivers |
|---|
| 519 | + * for the correct devices by checking the OEM Platform Revision provided by |
|---|
| 520 | + * the _DSM method. |
|---|
| 521 | + */ |
|---|
| 522 | +#define MSHW0040_DSM_REVISION 0x01 |
|---|
| 523 | +#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision |
|---|
| 524 | +static const guid_t MSHW0040_DSM_UUID = |
|---|
| 525 | + GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, |
|---|
| 526 | + 0x49, 0x80, 0x35); |
|---|
| 527 | + |
|---|
| 528 | +static int soc_device_check_MSHW0040(struct device *dev) |
|---|
| 529 | +{ |
|---|
| 530 | + acpi_handle handle = ACPI_HANDLE(dev); |
|---|
| 531 | + union acpi_object *result; |
|---|
| 532 | + u64 oem_platform_rev = 0; // valid revisions are nonzero |
|---|
| 533 | + |
|---|
| 534 | + // get OEM platform revision |
|---|
| 535 | + result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, |
|---|
| 536 | + MSHW0040_DSM_REVISION, |
|---|
| 537 | + MSHW0040_DSM_GET_OMPR, NULL, |
|---|
| 538 | + ACPI_TYPE_INTEGER); |
|---|
| 539 | + |
|---|
| 540 | + if (result) { |
|---|
| 541 | + oem_platform_rev = result->integer.value; |
|---|
| 542 | + ACPI_FREE(result); |
|---|
| 543 | + } |
|---|
| 544 | + |
|---|
| 545 | + /* |
|---|
| 546 | + * If the revision is zero here, the _DSM evaluation has failed. This |
|---|
| 547 | + * indicates that we have a Pro 4 or Book 1 and this driver should not |
|---|
| 548 | + * be used. |
|---|
| 549 | + */ |
|---|
| 550 | + if (oem_platform_rev == 0) |
|---|
| 551 | + return -ENODEV; |
|---|
| 552 | + |
|---|
| 553 | + dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); |
|---|
| 554 | + |
|---|
| 555 | + return 0; |
|---|
| 556 | +} |
|---|
| 557 | + |
|---|
| 558 | +/* |
|---|
| 559 | + * Button infos for Microsoft Surface Book 2 and Surface Pro (2017). |
|---|
| 560 | + * Obtained from DSDT/testing. |
|---|
| 561 | + */ |
|---|
| 562 | +static const struct soc_button_info soc_button_MSHW0040[] = { |
|---|
| 563 | + { "power", 0, EV_KEY, KEY_POWER, false, true, true }, |
|---|
| 564 | + { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false, true }, |
|---|
| 565 | + { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false, true }, |
|---|
| 566 | + { } |
|---|
| 567 | +}; |
|---|
| 568 | + |
|---|
| 569 | +static const struct soc_device_data soc_device_MSHW0040 = { |
|---|
| 570 | + .button_info = soc_button_MSHW0040, |
|---|
| 571 | + .check = soc_device_check_MSHW0040, |
|---|
| 572 | +}; |
|---|
| 573 | + |
|---|
| 380 | 574 | static const struct acpi_device_id soc_button_acpi_match[] = { |
|---|
| 381 | | - { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, |
|---|
| 575 | + { "PNP0C40", (unsigned long)&soc_device_PNP0C40 }, |
|---|
| 576 | + { "INT33D3", (unsigned long)&soc_device_INT33D3 }, |
|---|
| 577 | + { "ID9001", (unsigned long)&soc_device_INT33D3 }, |
|---|
| 382 | 578 | { "ACPI0011", 0 }, |
|---|
| 579 | + |
|---|
| 580 | + /* Microsoft Surface Devices (5th and 6th generation) */ |
|---|
| 581 | + { "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, |
|---|
| 582 | + |
|---|
| 383 | 583 | { } |
|---|
| 384 | 584 | }; |
|---|
| 385 | 585 | |
|---|