| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * power/home/volume button support for |
|---|
| 3 | 4 | * Microsoft Surface Pro 3/4 tablet. |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Copyright (c) 2015 Intel Corporation. |
|---|
| 6 | 7 | * All rights reserved. |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or |
|---|
| 9 | | - * modify it under the terms of the GNU General Public License |
|---|
| 10 | | - * as published by the Free Software Foundation; version 2 |
|---|
| 11 | | - * of the License. |
|---|
| 12 | 8 | */ |
|---|
| 13 | 9 | |
|---|
| 14 | 10 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 23 | 19 | #define SURFACE_PRO4_BUTTON_HID "MSHW0040" |
|---|
| 24 | 20 | #define SURFACE_BUTTON_OBJ_NAME "VGBI" |
|---|
| 25 | 21 | #define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" |
|---|
| 22 | + |
|---|
| 23 | +#define MSHW0040_DSM_REVISION 0x01 |
|---|
| 24 | +#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision |
|---|
| 25 | +static const guid_t MSHW0040_DSM_UUID = |
|---|
| 26 | + GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, |
|---|
| 27 | + 0x49, 0x80, 0x35); |
|---|
| 26 | 28 | |
|---|
| 27 | 29 | #define SURFACE_BUTTON_NOTIFY_TABLET_MODE 0xc8 |
|---|
| 28 | 30 | |
|---|
| .. | .. |
|---|
| 82 | 84 | /* Power button press,release handle */ |
|---|
| 83 | 85 | case SURFACE_BUTTON_NOTIFY_PRESS_POWER: |
|---|
| 84 | 86 | pressed = true; |
|---|
| 85 | | - /*fall through*/ |
|---|
| 87 | + fallthrough; |
|---|
| 86 | 88 | case SURFACE_BUTTON_NOTIFY_RELEASE_POWER: |
|---|
| 87 | 89 | key_code = KEY_POWER; |
|---|
| 88 | 90 | break; |
|---|
| 89 | 91 | /* Home button press,release handle */ |
|---|
| 90 | 92 | case SURFACE_BUTTON_NOTIFY_PRESS_HOME: |
|---|
| 91 | 93 | pressed = true; |
|---|
| 92 | | - /*fall through*/ |
|---|
| 94 | + fallthrough; |
|---|
| 93 | 95 | case SURFACE_BUTTON_NOTIFY_RELEASE_HOME: |
|---|
| 94 | 96 | key_code = KEY_LEFTMETA; |
|---|
| 95 | 97 | break; |
|---|
| 96 | 98 | /* Volume up button press,release handle */ |
|---|
| 97 | 99 | case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP: |
|---|
| 98 | 100 | pressed = true; |
|---|
| 99 | | - /*fall through*/ |
|---|
| 101 | + fallthrough; |
|---|
| 100 | 102 | case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP: |
|---|
| 101 | 103 | key_code = KEY_VOLUMEUP; |
|---|
| 102 | 104 | break; |
|---|
| 103 | 105 | /* Volume down button press,release handle */ |
|---|
| 104 | 106 | case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN: |
|---|
| 105 | 107 | pressed = true; |
|---|
| 106 | | - /*fall through*/ |
|---|
| 108 | + fallthrough; |
|---|
| 107 | 109 | case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN: |
|---|
| 108 | 110 | key_code = KEY_VOLUMEDOWN; |
|---|
| 109 | 111 | break; |
|---|
| .. | .. |
|---|
| 146 | 148 | } |
|---|
| 147 | 149 | #endif |
|---|
| 148 | 150 | |
|---|
| 151 | +/* |
|---|
| 152 | + * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device |
|---|
| 153 | + * ID (MSHW0040) for the power/volume buttons. Make sure this is the right |
|---|
| 154 | + * device by checking for the _DSM method and OEM Platform Revision. |
|---|
| 155 | + * |
|---|
| 156 | + * Returns true if the driver should bind to this device, i.e. the device is |
|---|
| 157 | + * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. |
|---|
| 158 | + */ |
|---|
| 159 | +static bool surface_button_check_MSHW0040(struct acpi_device *dev) |
|---|
| 160 | +{ |
|---|
| 161 | + acpi_handle handle = dev->handle; |
|---|
| 162 | + union acpi_object *result; |
|---|
| 163 | + u64 oem_platform_rev = 0; // valid revisions are nonzero |
|---|
| 164 | + |
|---|
| 165 | + // get OEM platform revision |
|---|
| 166 | + result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, |
|---|
| 167 | + MSHW0040_DSM_REVISION, |
|---|
| 168 | + MSHW0040_DSM_GET_OMPR, |
|---|
| 169 | + NULL, ACPI_TYPE_INTEGER); |
|---|
| 170 | + |
|---|
| 171 | + /* |
|---|
| 172 | + * If evaluating the _DSM fails, the method is not present. This means |
|---|
| 173 | + * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we |
|---|
| 174 | + * should use this driver. We use revision 0 indicating it is |
|---|
| 175 | + * unavailable. |
|---|
| 176 | + */ |
|---|
| 177 | + |
|---|
| 178 | + if (result) { |
|---|
| 179 | + oem_platform_rev = result->integer.value; |
|---|
| 180 | + ACPI_FREE(result); |
|---|
| 181 | + } |
|---|
| 182 | + |
|---|
| 183 | + dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev); |
|---|
| 184 | + |
|---|
| 185 | + return oem_platform_rev == 0; |
|---|
| 186 | +} |
|---|
| 187 | + |
|---|
| 188 | + |
|---|
| 149 | 189 | static int surface_button_add(struct acpi_device *device) |
|---|
| 150 | 190 | { |
|---|
| 151 | 191 | struct surface_button *button; |
|---|
| .. | .. |
|---|
| 158 | 198 | strlen(SURFACE_BUTTON_OBJ_NAME))) |
|---|
| 159 | 199 | return -ENODEV; |
|---|
| 160 | 200 | |
|---|
| 201 | + if (!surface_button_check_MSHW0040(device)) |
|---|
| 202 | + return -ENODEV; |
|---|
| 203 | + |
|---|
| 161 | 204 | button = kzalloc(sizeof(struct surface_button), GFP_KERNEL); |
|---|
| 162 | 205 | if (!button) |
|---|
| 163 | 206 | return -ENOMEM; |
|---|