| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0+ |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Intel HID event & 5 button array driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 2015 Alex Hung <alex.hung@canonical.com> |
|---|
| 5 | 6 | * Copyright (C) 2015 Andrew Lutomirski <luto@kernel.org> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 9 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 10 | | - * (at your option) any later version. |
|---|
| 11 | | - * |
|---|
| 12 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 15 | | - * GNU General Public License for more details. |
|---|
| 16 | | - * |
|---|
| 17 | 7 | */ |
|---|
| 18 | 8 | |
|---|
| 19 | 9 | #include <linux/acpi.h> |
|---|
| .. | .. |
|---|
| 30 | 20 | |
|---|
| 31 | 21 | static const struct acpi_device_id intel_hid_ids[] = { |
|---|
| 32 | 22 | {"INT33D5", 0}, |
|---|
| 23 | + {"INTC1051", 0}, |
|---|
| 33 | 24 | {"", 0}, |
|---|
| 34 | 25 | }; |
|---|
| 26 | +MODULE_DEVICE_TABLE(acpi, intel_hid_ids); |
|---|
| 35 | 27 | |
|---|
| 36 | 28 | /* In theory, these are HID usages. */ |
|---|
| 37 | 29 | static const struct key_entry intel_hid_keymap[] = { |
|---|
| .. | .. |
|---|
| 99 | 91 | .matches = { |
|---|
| 100 | 92 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), |
|---|
| 101 | 93 | DMI_MATCH(DMI_PRODUCT_FAMILY, "ThinkPad X1 Tablet Gen 2"), |
|---|
| 94 | + }, |
|---|
| 95 | + }, |
|---|
| 96 | + { |
|---|
| 97 | + .ident = "Microsoft Surface Go 3", |
|---|
| 98 | + .matches = { |
|---|
| 99 | + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), |
|---|
| 100 | + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"), |
|---|
| 102 | 101 | }, |
|---|
| 103 | 102 | }, |
|---|
| 104 | 103 | { } |
|---|
| .. | .. |
|---|
| 277 | 276 | |
|---|
| 278 | 277 | static int intel_hid_pm_prepare(struct device *device) |
|---|
| 279 | 278 | { |
|---|
| 279 | + if (device_may_wakeup(device)) { |
|---|
| 280 | + struct intel_hid_priv *priv = dev_get_drvdata(device); |
|---|
| 281 | + |
|---|
| 282 | + priv->wakeup_mode = true; |
|---|
| 283 | + } |
|---|
| 284 | + return 0; |
|---|
| 285 | +} |
|---|
| 286 | + |
|---|
| 287 | +static void intel_hid_pm_complete(struct device *device) |
|---|
| 288 | +{ |
|---|
| 280 | 289 | struct intel_hid_priv *priv = dev_get_drvdata(device); |
|---|
| 281 | 290 | |
|---|
| 282 | | - priv->wakeup_mode = true; |
|---|
| 283 | | - return 0; |
|---|
| 291 | + priv->wakeup_mode = false; |
|---|
| 284 | 292 | } |
|---|
| 285 | 293 | |
|---|
| 286 | 294 | static int intel_hid_pl_suspend_handler(struct device *device) |
|---|
| 287 | 295 | { |
|---|
| 288 | | - if (pm_suspend_via_firmware()) { |
|---|
| 296 | + intel_button_array_enable(device, false); |
|---|
| 297 | + |
|---|
| 298 | + if (!pm_suspend_no_platform()) |
|---|
| 289 | 299 | intel_hid_set_enable(device, false); |
|---|
| 290 | | - intel_button_array_enable(device, false); |
|---|
| 291 | | - } |
|---|
| 300 | + |
|---|
| 292 | 301 | return 0; |
|---|
| 293 | 302 | } |
|---|
| 294 | 303 | |
|---|
| 295 | 304 | static int intel_hid_pl_resume_handler(struct device *device) |
|---|
| 296 | 305 | { |
|---|
| 297 | | - struct intel_hid_priv *priv = dev_get_drvdata(device); |
|---|
| 306 | + intel_hid_pm_complete(device); |
|---|
| 298 | 307 | |
|---|
| 299 | | - priv->wakeup_mode = false; |
|---|
| 300 | | - if (pm_resume_via_firmware()) { |
|---|
| 308 | + if (!pm_suspend_no_platform()) |
|---|
| 301 | 309 | intel_hid_set_enable(device, true); |
|---|
| 302 | | - intel_button_array_enable(device, true); |
|---|
| 303 | | - } |
|---|
| 310 | + |
|---|
| 311 | + intel_button_array_enable(device, true); |
|---|
| 304 | 312 | return 0; |
|---|
| 305 | 313 | } |
|---|
| 306 | 314 | |
|---|
| 307 | 315 | static const struct dev_pm_ops intel_hid_pl_pm_ops = { |
|---|
| 308 | 316 | .prepare = intel_hid_pm_prepare, |
|---|
| 317 | + .complete = intel_hid_pm_complete, |
|---|
| 309 | 318 | .freeze = intel_hid_pl_suspend_handler, |
|---|
| 310 | 319 | .thaw = intel_hid_pl_resume_handler, |
|---|
| 311 | 320 | .restore = intel_hid_pl_resume_handler, |
|---|
| .. | .. |
|---|
| 449 | 458 | static int intel_hid_probe(struct platform_device *device) |
|---|
| 450 | 459 | { |
|---|
| 451 | 460 | acpi_handle handle = ACPI_HANDLE(&device->dev); |
|---|
| 452 | | - unsigned long long mode; |
|---|
| 461 | + unsigned long long mode, dummy; |
|---|
| 453 | 462 | struct intel_hid_priv *priv; |
|---|
| 454 | 463 | acpi_status status; |
|---|
| 455 | 464 | int err; |
|---|
| .. | .. |
|---|
| 501 | 510 | if (err) |
|---|
| 502 | 511 | goto err_remove_notify; |
|---|
| 503 | 512 | |
|---|
| 504 | | - if (priv->array) { |
|---|
| 505 | | - unsigned long long dummy; |
|---|
| 513 | + intel_button_array_enable(&device->dev, true); |
|---|
| 506 | 514 | |
|---|
| 507 | | - intel_button_array_enable(&device->dev, true); |
|---|
| 508 | | - |
|---|
| 509 | | - /* Call button load method to enable HID power button */ |
|---|
| 510 | | - if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_BTNL_FN, |
|---|
| 511 | | - &dummy)) { |
|---|
| 512 | | - dev_warn(&device->dev, |
|---|
| 513 | | - "failed to enable HID power button\n"); |
|---|
| 514 | | - } |
|---|
| 515 | | - } |
|---|
| 515 | + /* |
|---|
| 516 | + * Call button load method to enable HID power button |
|---|
| 517 | + * Always do this since it activates events on some devices without |
|---|
| 518 | + * a button array too. |
|---|
| 519 | + */ |
|---|
| 520 | + if (!intel_hid_evaluate_method(handle, INTEL_HID_DSM_BTNL_FN, &dummy)) |
|---|
| 521 | + dev_warn(&device->dev, "failed to enable HID power button\n"); |
|---|
| 516 | 522 | |
|---|
| 517 | 523 | device_init_wakeup(&device->dev, true); |
|---|
| 524 | + /* |
|---|
| 525 | + * In order for system wakeup to work, the EC GPE has to be marked as |
|---|
| 526 | + * a wakeup one, so do that here (this setting will persist, but it has |
|---|
| 527 | + * no effect until the wakeup mask is set for the EC GPE). |
|---|
| 528 | + */ |
|---|
| 529 | + acpi_ec_mark_gpe_for_wake(); |
|---|
| 518 | 530 | return 0; |
|---|
| 519 | 531 | |
|---|
| 520 | 532 | err_remove_notify: |
|---|
| .. | .. |
|---|
| 548 | 560 | .probe = intel_hid_probe, |
|---|
| 549 | 561 | .remove = intel_hid_remove, |
|---|
| 550 | 562 | }; |
|---|
| 551 | | -MODULE_DEVICE_TABLE(acpi, intel_hid_ids); |
|---|
| 552 | 563 | |
|---|
| 553 | 564 | /* |
|---|
| 554 | 565 | * Unfortunately, some laptops provide a _HID="INT33D5" device with |
|---|