From: Maximilian Luz Date: Fri, 9 Oct 2020 14:11:28 +0000 (+0200) Subject: platform/surface: Move Surface Pro 3 Button driver to platform/surface X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=411269babe8374b7777a0f154a2ad27c3c6dc218;p=linux.git platform/surface: Move Surface Pro 3 Button driver to platform/surface Move the Surface Pro 3 Button driver from platform/x86 to the newly created platform/surface directory. Signed-off-by: Maximilian Luz Reviewed-by: Andy Shevchenko Acked-by: Chen Yu Link: https://lore.kernel.org/r/20201009141128.683254-6-luzmaximilian@gmail.com Signed-off-by: Hans de Goede --- diff --git a/MAINTAINERS b/MAINTAINERS index e8bc7fd592359..57b07078e5b1a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11663,7 +11663,7 @@ MICROSOFT SURFACE PRO 3 BUTTON DRIVER M: Chen Yu L: platform-driver-x86@vger.kernel.org S: Supported -F: drivers/platform/x86/surfacepro3_button.c +F: drivers/platform/surface/surfacepro3_button.c MICROTEK X6 SCANNER M: Oliver Neukum diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig index ac1c749a7a2ff..10902ea438610 100644 --- a/drivers/platform/surface/Kconfig +++ b/drivers/platform/surface/Kconfig @@ -40,4 +40,10 @@ config SURFACE_3_POWER_OPREGION This driver provides support for ACPI operation region of the Surface 3 battery platform driver. +config SURFACE_PRO3_BUTTON + tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet" + depends on ACPI && INPUT + help + This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. + endif # SURFACE_PLATFORMS diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile index 4940d4db58b2e..dcb1df06d57ac 100644 --- a/drivers/platform/surface/Makefile +++ b/drivers/platform/surface/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_SURFACE3_WMI) += surface3-wmi.o obj-$(CONFIG_SURFACE_3_BUTTON) += surface3_button.o obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o +obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c new file mode 100644 index 0000000000000..d8afed5db94c5 --- /dev/null +++ b/drivers/platform/surface/surfacepro3_button.c @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * power/home/volume button support for + * Microsoft Surface Pro 3/4 tablet. + * + * Copyright (c) 2015 Intel Corporation. + * All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define SURFACE_PRO3_BUTTON_HID "MSHW0028" +#define SURFACE_PRO4_BUTTON_HID "MSHW0040" +#define SURFACE_BUTTON_OBJ_NAME "VGBI" +#define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" + +#define MSHW0040_DSM_REVISION 0x01 +#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision +static const guid_t MSHW0040_DSM_UUID = + GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, + 0x49, 0x80, 0x35); + +#define SURFACE_BUTTON_NOTIFY_TABLET_MODE 0xc8 + +#define SURFACE_BUTTON_NOTIFY_PRESS_POWER 0xc6 +#define SURFACE_BUTTON_NOTIFY_RELEASE_POWER 0xc7 + +#define SURFACE_BUTTON_NOTIFY_PRESS_HOME 0xc4 +#define SURFACE_BUTTON_NOTIFY_RELEASE_HOME 0xc5 + +#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP 0xc0 +#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP 0xc1 + +#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN 0xc2 +#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN 0xc3 + +ACPI_MODULE_NAME("surface pro 3 button"); + +MODULE_AUTHOR("Chen Yu"); +MODULE_DESCRIPTION("Surface Pro3 Button Driver"); +MODULE_LICENSE("GPL v2"); + +/* + * Power button, Home button, Volume buttons support is supposed to + * be covered by drivers/input/misc/soc_button_array.c, which is implemented + * according to "Windows ACPI Design Guide for SoC Platforms". + * However surface pro3 seems not to obey the specs, instead it uses + * device VGBI(MSHW0028) for dispatching the events. + * We choose acpi_driver rather than platform_driver/i2c_driver because + * although VGBI has an i2c resource connected to i2c controller, it + * is not embedded in any i2c controller's scope, thus neither platform_device + * will be created, nor i2c_client will be enumerated, we have to use + * acpi_driver. + */ +static const struct acpi_device_id surface_button_device_ids[] = { + {SURFACE_PRO3_BUTTON_HID, 0}, + {SURFACE_PRO4_BUTTON_HID, 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, surface_button_device_ids); + +struct surface_button { + unsigned int type; + struct input_dev *input; + char phys[32]; /* for input device */ + unsigned long pushed; + bool suspended; +}; + +static void surface_button_notify(struct acpi_device *device, u32 event) +{ + struct surface_button *button = acpi_driver_data(device); + struct input_dev *input; + int key_code = KEY_RESERVED; + bool pressed = false; + + switch (event) { + /* Power button press,release handle */ + case SURFACE_BUTTON_NOTIFY_PRESS_POWER: + pressed = true; + fallthrough; + case SURFACE_BUTTON_NOTIFY_RELEASE_POWER: + key_code = KEY_POWER; + break; + /* Home button press,release handle */ + case SURFACE_BUTTON_NOTIFY_PRESS_HOME: + pressed = true; + fallthrough; + case SURFACE_BUTTON_NOTIFY_RELEASE_HOME: + key_code = KEY_LEFTMETA; + break; + /* Volume up button press,release handle */ + case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP: + pressed = true; + fallthrough; + case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP: + key_code = KEY_VOLUMEUP; + break; + /* Volume down button press,release handle */ + case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN: + pressed = true; + fallthrough; + case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN: + key_code = KEY_VOLUMEDOWN; + break; + case SURFACE_BUTTON_NOTIFY_TABLET_MODE: + dev_warn_once(&device->dev, "Tablet mode is not supported\n"); + break; + default: + dev_info_ratelimited(&device->dev, + "Unsupported event [0x%x]\n", event); + break; + } + input = button->input; + if (key_code == KEY_RESERVED) + return; + if (pressed) + pm_wakeup_dev_event(&device->dev, 0, button->suspended); + if (button->suspended) + return; + input_report_key(input, key_code, pressed?1:0); + input_sync(input); +} + +#ifdef CONFIG_PM_SLEEP +static int surface_button_suspend(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct surface_button *button = acpi_driver_data(device); + + button->suspended = true; + return 0; +} + +static int surface_button_resume(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct surface_button *button = acpi_driver_data(device); + + button->suspended = false; + return 0; +} +#endif + +/* + * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device + * ID (MSHW0040) for the power/volume buttons. Make sure this is the right + * device by checking for the _DSM method and OEM Platform Revision. + * + * Returns true if the driver should bind to this device, i.e. the device is + * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. + */ +static bool surface_button_check_MSHW0040(struct acpi_device *dev) +{ + acpi_handle handle = dev->handle; + union acpi_object *result; + u64 oem_platform_rev = 0; // valid revisions are nonzero + + // get OEM platform revision + result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, + MSHW0040_DSM_REVISION, + MSHW0040_DSM_GET_OMPR, + NULL, ACPI_TYPE_INTEGER); + + /* + * If evaluating the _DSM fails, the method is not present. This means + * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we + * should use this driver. We use revision 0 indicating it is + * unavailable. + */ + + if (result) { + oem_platform_rev = result->integer.value; + ACPI_FREE(result); + } + + dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev); + + return oem_platform_rev == 0; +} + + +static int surface_button_add(struct acpi_device *device) +{ + struct surface_button *button; + struct input_dev *input; + const char *hid = acpi_device_hid(device); + char *name; + int error; + + if (strncmp(acpi_device_bid(device), SURFACE_BUTTON_OBJ_NAME, + strlen(SURFACE_BUTTON_OBJ_NAME))) + return -ENODEV; + + if (!surface_button_check_MSHW0040(device)) + return -ENODEV; + + button = kzalloc(sizeof(struct surface_button), GFP_KERNEL); + if (!button) + return -ENOMEM; + + device->driver_data = button; + button->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_free_button; + } + + name = acpi_device_name(device); + strcpy(name, SURFACE_BUTTON_DEVICE_NAME); + snprintf(button->phys, sizeof(button->phys), "%s/buttons", hid); + + input->name = name; + input->phys = button->phys; + input->id.bustype = BUS_HOST; + input->dev.parent = &device->dev; + input_set_capability(input, EV_KEY, KEY_POWER); + input_set_capability(input, EV_KEY, KEY_LEFTMETA); + input_set_capability(input, EV_KEY, KEY_VOLUMEUP); + input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN); + + error = input_register_device(input); + if (error) + goto err_free_input; + + device_init_wakeup(&device->dev, true); + dev_info(&device->dev, + "%s [%s]\n", name, acpi_device_bid(device)); + return 0; + + err_free_input: + input_free_device(input); + err_free_button: + kfree(button); + return error; +} + +static int surface_button_remove(struct acpi_device *device) +{ + struct surface_button *button = acpi_driver_data(device); + + input_unregister_device(button->input); + kfree(button); + return 0; +} + +static SIMPLE_DEV_PM_OPS(surface_button_pm, + surface_button_suspend, surface_button_resume); + +static struct acpi_driver surface_button_driver = { + .name = "surface_pro3_button", + .class = "SurfacePro3", + .ids = surface_button_device_ids, + .ops = { + .add = surface_button_add, + .remove = surface_button_remove, + .notify = surface_button_notify, + }, + .drv.pm = &surface_button_pm, +}; + +module_acpi_driver(surface_button_driver); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 8417ee0178d0c..6083f8241b7d0 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -870,12 +870,6 @@ config INTEL_VBTN To compile this driver as a module, choose M here: the module will be called intel_vbtn. -config SURFACE_PRO3_BUTTON - tristate "Power/home/volume buttons driver for Microsoft Surface Pro 3/4 tablet" - depends on ACPI && INPUT - help - This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3/4 tablet. - config MSI_LAPTOP tristate "MSI Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index ffa31f57d9a2a..aeff497e23a56 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -81,9 +81,6 @@ obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o obj-$(CONFIG_INTEL_VBTN) += intel-vbtn.o -# Microsoft -obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o - # MSI obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o obj-$(CONFIG_MSI_WMI) += msi-wmi.o diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c deleted file mode 100644 index d8afed5db94c5..0000000000000 --- a/drivers/platform/x86/surfacepro3_button.c +++ /dev/null @@ -1,268 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * power/home/volume button support for - * Microsoft Surface Pro 3/4 tablet. - * - * Copyright (c) 2015 Intel Corporation. - * All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include - -#define SURFACE_PRO3_BUTTON_HID "MSHW0028" -#define SURFACE_PRO4_BUTTON_HID "MSHW0040" -#define SURFACE_BUTTON_OBJ_NAME "VGBI" -#define SURFACE_BUTTON_DEVICE_NAME "Surface Pro 3/4 Buttons" - -#define MSHW0040_DSM_REVISION 0x01 -#define MSHW0040_DSM_GET_OMPR 0x02 // get OEM Platform Revision -static const guid_t MSHW0040_DSM_UUID = - GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65, - 0x49, 0x80, 0x35); - -#define SURFACE_BUTTON_NOTIFY_TABLET_MODE 0xc8 - -#define SURFACE_BUTTON_NOTIFY_PRESS_POWER 0xc6 -#define SURFACE_BUTTON_NOTIFY_RELEASE_POWER 0xc7 - -#define SURFACE_BUTTON_NOTIFY_PRESS_HOME 0xc4 -#define SURFACE_BUTTON_NOTIFY_RELEASE_HOME 0xc5 - -#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP 0xc0 -#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP 0xc1 - -#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN 0xc2 -#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN 0xc3 - -ACPI_MODULE_NAME("surface pro 3 button"); - -MODULE_AUTHOR("Chen Yu"); -MODULE_DESCRIPTION("Surface Pro3 Button Driver"); -MODULE_LICENSE("GPL v2"); - -/* - * Power button, Home button, Volume buttons support is supposed to - * be covered by drivers/input/misc/soc_button_array.c, which is implemented - * according to "Windows ACPI Design Guide for SoC Platforms". - * However surface pro3 seems not to obey the specs, instead it uses - * device VGBI(MSHW0028) for dispatching the events. - * We choose acpi_driver rather than platform_driver/i2c_driver because - * although VGBI has an i2c resource connected to i2c controller, it - * is not embedded in any i2c controller's scope, thus neither platform_device - * will be created, nor i2c_client will be enumerated, we have to use - * acpi_driver. - */ -static const struct acpi_device_id surface_button_device_ids[] = { - {SURFACE_PRO3_BUTTON_HID, 0}, - {SURFACE_PRO4_BUTTON_HID, 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, surface_button_device_ids); - -struct surface_button { - unsigned int type; - struct input_dev *input; - char phys[32]; /* for input device */ - unsigned long pushed; - bool suspended; -}; - -static void surface_button_notify(struct acpi_device *device, u32 event) -{ - struct surface_button *button = acpi_driver_data(device); - struct input_dev *input; - int key_code = KEY_RESERVED; - bool pressed = false; - - switch (event) { - /* Power button press,release handle */ - case SURFACE_BUTTON_NOTIFY_PRESS_POWER: - pressed = true; - fallthrough; - case SURFACE_BUTTON_NOTIFY_RELEASE_POWER: - key_code = KEY_POWER; - break; - /* Home button press,release handle */ - case SURFACE_BUTTON_NOTIFY_PRESS_HOME: - pressed = true; - fallthrough; - case SURFACE_BUTTON_NOTIFY_RELEASE_HOME: - key_code = KEY_LEFTMETA; - break; - /* Volume up button press,release handle */ - case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP: - pressed = true; - fallthrough; - case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP: - key_code = KEY_VOLUMEUP; - break; - /* Volume down button press,release handle */ - case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN: - pressed = true; - fallthrough; - case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN: - key_code = KEY_VOLUMEDOWN; - break; - case SURFACE_BUTTON_NOTIFY_TABLET_MODE: - dev_warn_once(&device->dev, "Tablet mode is not supported\n"); - break; - default: - dev_info_ratelimited(&device->dev, - "Unsupported event [0x%x]\n", event); - break; - } - input = button->input; - if (key_code == KEY_RESERVED) - return; - if (pressed) - pm_wakeup_dev_event(&device->dev, 0, button->suspended); - if (button->suspended) - return; - input_report_key(input, key_code, pressed?1:0); - input_sync(input); -} - -#ifdef CONFIG_PM_SLEEP -static int surface_button_suspend(struct device *dev) -{ - struct acpi_device *device = to_acpi_device(dev); - struct surface_button *button = acpi_driver_data(device); - - button->suspended = true; - return 0; -} - -static int surface_button_resume(struct device *dev) -{ - struct acpi_device *device = to_acpi_device(dev); - struct surface_button *button = acpi_driver_data(device); - - button->suspended = false; - return 0; -} -#endif - -/* - * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device - * ID (MSHW0040) for the power/volume buttons. Make sure this is the right - * device by checking for the _DSM method and OEM Platform Revision. - * - * Returns true if the driver should bind to this device, i.e. the device is - * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. - */ -static bool surface_button_check_MSHW0040(struct acpi_device *dev) -{ - acpi_handle handle = dev->handle; - union acpi_object *result; - u64 oem_platform_rev = 0; // valid revisions are nonzero - - // get OEM platform revision - result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, - MSHW0040_DSM_REVISION, - MSHW0040_DSM_GET_OMPR, - NULL, ACPI_TYPE_INTEGER); - - /* - * If evaluating the _DSM fails, the method is not present. This means - * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we - * should use this driver. We use revision 0 indicating it is - * unavailable. - */ - - if (result) { - oem_platform_rev = result->integer.value; - ACPI_FREE(result); - } - - dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev); - - return oem_platform_rev == 0; -} - - -static int surface_button_add(struct acpi_device *device) -{ - struct surface_button *button; - struct input_dev *input; - const char *hid = acpi_device_hid(device); - char *name; - int error; - - if (strncmp(acpi_device_bid(device), SURFACE_BUTTON_OBJ_NAME, - strlen(SURFACE_BUTTON_OBJ_NAME))) - return -ENODEV; - - if (!surface_button_check_MSHW0040(device)) - return -ENODEV; - - button = kzalloc(sizeof(struct surface_button), GFP_KERNEL); - if (!button) - return -ENOMEM; - - device->driver_data = button; - button->input = input = input_allocate_device(); - if (!input) { - error = -ENOMEM; - goto err_free_button; - } - - name = acpi_device_name(device); - strcpy(name, SURFACE_BUTTON_DEVICE_NAME); - snprintf(button->phys, sizeof(button->phys), "%s/buttons", hid); - - input->name = name; - input->phys = button->phys; - input->id.bustype = BUS_HOST; - input->dev.parent = &device->dev; - input_set_capability(input, EV_KEY, KEY_POWER); - input_set_capability(input, EV_KEY, KEY_LEFTMETA); - input_set_capability(input, EV_KEY, KEY_VOLUMEUP); - input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN); - - error = input_register_device(input); - if (error) - goto err_free_input; - - device_init_wakeup(&device->dev, true); - dev_info(&device->dev, - "%s [%s]\n", name, acpi_device_bid(device)); - return 0; - - err_free_input: - input_free_device(input); - err_free_button: - kfree(button); - return error; -} - -static int surface_button_remove(struct acpi_device *device) -{ - struct surface_button *button = acpi_driver_data(device); - - input_unregister_device(button->input); - kfree(button); - return 0; -} - -static SIMPLE_DEV_PM_OPS(surface_button_pm, - surface_button_suspend, surface_button_resume); - -static struct acpi_driver surface_button_driver = { - .name = "surface_pro3_button", - .class = "SurfacePro3", - .ids = surface_button_device_ids, - .ops = { - .add = surface_button_add, - .remove = surface_button_remove, - .notify = surface_button_notify, - }, - .drv.pm = &surface_button_pm, -}; - -module_acpi_driver(surface_button_driver);