1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * power/home/volume button support for 4 * Microsoft Surface Pro 3/4 tablet. 5 * 6 * Copyright (c) 2015 Intel Corporation. 7 * All rights reserved. 8 */ 9 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/init.h> 13 #include <linux/string.h> 14 #include <linux/types.h> 15 #include <linux/input.h> 16 #include <linux/acpi.h> 17 #include <linux/platform_device.h> 18 #include <acpi/button.h> 19 20 #define SURFACE_PRO3_BUTTON_HID "MSHW0028" 21 #define SURFACE_PRO4_BUTTON_HID "MSHW0040" 22 #define SURFACE_BUTTON_OBJ_NAME "VGBI" 23 24 #define MSHW0040_DSM_REVISION 0x01 25 #define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision 26 static const guid_t MSHW0040_DSM_UUID = 27 GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, 28 0x49, 0x80, 0x35); 29 30 #define SURFACE_BUTTON_NOTIFY_TABLET_MODE 0xc8 31 32 #define SURFACE_BUTTON_NOTIFY_PRESS_POWER 0xc6 33 #define SURFACE_BUTTON_NOTIFY_RELEASE_POWER 0xc7 34 35 #define SURFACE_BUTTON_NOTIFY_PRESS_HOME 0xc4 36 #define SURFACE_BUTTON_NOTIFY_RELEASE_HOME 0xc5 37 38 #define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP 0xc0 39 #define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP 0xc1 40 41 #define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN 0xc2 42 #define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN 0xc3 43 44 MODULE_AUTHOR("Chen Yu"); 45 MODULE_DESCRIPTION("Surface Pro3 Button Driver"); 46 MODULE_LICENSE("GPL v2"); 47 48 /* 49 * Power button, Home button, Volume buttons support is supposed to 50 * be covered by drivers/input/misc/soc_button_array.c, which is implemented 51 * according to "Windows ACPI Design Guide for SoC Platforms". 52 * However surface pro3 seems not to obey the specs, instead it uses 53 * device VGBI(MSHW0028) for dispatching the events. 54 * We choose acpi_driver rather than platform_driver/i2c_driver because 55 * although VGBI has an i2c resource connected to i2c controller, it 56 * is not embedded in any i2c controller's scope, thus neither platform_device 57 * will be created, nor i2c_client will be enumerated, we have to use 58 * acpi_driver. 59 */ 60 static const struct acpi_device_id surface_button_device_ids[] = { 61 {SURFACE_PRO3_BUTTON_HID, 0}, 62 {SURFACE_PRO4_BUTTON_HID, 0}, 63 {"", 0}, 64 }; 65 MODULE_DEVICE_TABLE(acpi, surface_button_device_ids); 66 67 struct surface_button { 68 unsigned int type; 69 struct input_dev *input; 70 char phys[32]; /* for input device */ 71 unsigned long pushed; 72 bool suspended; 73 }; 74 75 static void surface_button_notify(acpi_handle handle, u32 event, void *data) 76 { 77 struct device *dev = data; 78 struct surface_button *button = dev_get_drvdata(dev); 79 struct input_dev *input; 80 int key_code = KEY_RESERVED; 81 bool pressed = false; 82 83 switch (event) { 84 /* Power button press,release handle */ 85 case SURFACE_BUTTON_NOTIFY_PRESS_POWER: 86 pressed = true; 87 fallthrough; 88 case SURFACE_BUTTON_NOTIFY_RELEASE_POWER: 89 key_code = KEY_POWER; 90 break; 91 /* Home button press,release handle */ 92 case SURFACE_BUTTON_NOTIFY_PRESS_HOME: 93 pressed = true; 94 fallthrough; 95 case SURFACE_BUTTON_NOTIFY_RELEASE_HOME: 96 key_code = KEY_LEFTMETA; 97 break; 98 /* Volume up button press,release handle */ 99 case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP: 100 pressed = true; 101 fallthrough; 102 case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP: 103 key_code = KEY_VOLUMEUP; 104 break; 105 /* Volume down button press,release handle */ 106 case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN: 107 pressed = true; 108 fallthrough; 109 case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN: 110 key_code = KEY_VOLUMEDOWN; 111 break; 112 case SURFACE_BUTTON_NOTIFY_TABLET_MODE: 113 dev_warn_once(dev, "Tablet mode is not supported\n"); 114 break; 115 default: 116 dev_info_ratelimited(dev, "Unsupported event [0x%x]\n", event); 117 break; 118 } 119 input = button->input; 120 if (key_code == KEY_RESERVED) 121 return; 122 if (pressed) 123 pm_wakeup_dev_event(dev, 0, button->suspended); 124 if (button->suspended) 125 return; 126 input_report_key(input, key_code, pressed?1:0); 127 input_sync(input); 128 } 129 130 #ifdef CONFIG_PM_SLEEP 131 static int surface_button_suspend(struct device *dev) 132 { 133 struct surface_button *button = dev_get_drvdata(dev); 134 135 button->suspended = true; 136 return 0; 137 } 138 139 static int surface_button_resume(struct device *dev) 140 { 141 struct surface_button *button = dev_get_drvdata(dev); 142 143 button->suspended = false; 144 return 0; 145 } 146 #endif 147 148 /* 149 * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device 150 * ID (MSHW0040) for the power/volume buttons. Make sure this is the right 151 * device by checking for the _DSM method and OEM Platform Revision. 152 * 153 * Returns true if the driver should bind to this device, i.e. the device is 154 * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. 155 */ 156 static bool surface_button_check_MSHW0040(struct device *dev, acpi_handle handle) 157 { 158 union acpi_object *result; 159 u64 oem_platform_rev = 0; // valid revisions are nonzero 160 161 // get OEM platform revision 162 result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, 163 MSHW0040_DSM_REVISION, 164 MSHW0040_DSM_GET_OMPR, 165 NULL, ACPI_TYPE_INTEGER); 166 167 /* 168 * If evaluating the _DSM fails, the method is not present. This means 169 * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we 170 * should use this driver. We use revision 0 indicating it is 171 * unavailable. 172 */ 173 174 if (result) { 175 oem_platform_rev = result->integer.value; 176 ACPI_FREE(result); 177 } 178 179 dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); 180 181 return oem_platform_rev == 0; 182 } 183 184 185 static int surface_button_probe(struct platform_device *pdev) 186 { 187 struct surface_button *button; 188 struct acpi_device *device; 189 struct input_dev *input; 190 int error; 191 192 device = ACPI_COMPANION(&pdev->dev); 193 if (!device) 194 return -ENODEV; 195 196 if (strncmp(acpi_device_bid(device), SURFACE_BUTTON_OBJ_NAME, 197 strlen(SURFACE_BUTTON_OBJ_NAME))) 198 return -ENODEV; 199 200 if (!surface_button_check_MSHW0040(&pdev->dev, device->handle)) 201 return -ENODEV; 202 203 button = kzalloc_obj(struct surface_button); 204 if (!button) 205 return -ENOMEM; 206 207 platform_set_drvdata(pdev, button); 208 button->input = input = input_allocate_device(); 209 if (!input) { 210 error = -ENOMEM; 211 goto err_free_button; 212 } 213 214 snprintf(button->phys, sizeof(button->phys), "%s/buttons", 215 acpi_device_hid(device)); 216 217 input->name = "Surface Pro 3/4 Buttons"; 218 input->phys = button->phys; 219 input->id.bustype = BUS_HOST; 220 input->dev.parent = &pdev->dev; 221 input_set_capability(input, EV_KEY, KEY_POWER); 222 input_set_capability(input, EV_KEY, KEY_LEFTMETA); 223 input_set_capability(input, EV_KEY, KEY_VOLUMEUP); 224 input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN); 225 226 error = input_register_device(input); 227 if (error) 228 goto err_free_input; 229 230 device_init_wakeup(&pdev->dev, true); 231 232 error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, 233 surface_button_notify, &pdev->dev); 234 if (error) { 235 device_init_wakeup(&pdev->dev, false); 236 input_unregister_device(input); 237 goto err_free_button; 238 } 239 240 dev_info(&pdev->dev, "%s [%s]\n", input->name, acpi_device_bid(device)); 241 242 return 0; 243 244 err_free_input: 245 input_free_device(input); 246 err_free_button: 247 kfree(button); 248 return error; 249 } 250 251 static void surface_button_remove(struct platform_device *pdev) 252 { 253 struct surface_button *button = platform_get_drvdata(pdev); 254 255 acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), 256 ACPI_DEVICE_NOTIFY, surface_button_notify); 257 device_init_wakeup(&pdev->dev, false); 258 input_unregister_device(button->input); 259 kfree(button); 260 } 261 262 static SIMPLE_DEV_PM_OPS(surface_button_pm, 263 surface_button_suspend, surface_button_resume); 264 265 static struct platform_driver surface_button_driver = { 266 .probe = surface_button_probe, 267 .remove = surface_button_remove, 268 .driver = { 269 .name = "surface_pro3_button", 270 .acpi_match_table = surface_button_device_ids, 271 .pm = &surface_button_pm, 272 }, 273 }; 274 275 module_platform_driver(surface_button_driver); 276