From 8a6ddaf22fc30f97b8d47d40d610671b58158fc7 Mon Sep 17 00:00:00 2001 From: Jake Day Date: Sun, 27 Jan 2019 10:51:25 -0500 Subject: [PATCH 03/11] buttons --- drivers/input/misc/soc_button_array.c | 84 +++++++++++++++++++++-- drivers/platform/x86/surfacepro3_button.c | 36 ++++++++++ 2 files changed, 114 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c index 23520df7650f..1ea239ff426d 100644 --- a/drivers/input/misc/soc_button_array.c +++ b/drivers/input/misc/soc_button_array.c @@ -29,12 +29,24 @@ struct soc_button_info { bool wakeup; }; +struct soc_device_data { + /* Button info, may be NULL. */ + struct soc_button_info *button_info; + /* Special device check function, may be NULL. */ + int (*check)(struct device *); +}; + /* * Some of the buttons like volume up/down are auto repeat, while others * are not. To support both, we register two platform devices, and put * buttons into them based on whether the key should be auto repeat. */ -#define BUTTON_TYPES 2 +#define BUTTON_TYPES 2 + +#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); struct soc_button_data { struct platform_device *children[BUTTON_TYPES]; @@ -310,6 +322,7 @@ static int soc_button_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct acpi_device_id *id; + struct soc_device_data *device_data; struct soc_button_info *button_info; struct soc_button_data *priv; struct platform_device *pd; @@ -320,12 +333,20 @@ static int soc_button_probe(struct platform_device *pdev) if (!id) return -ENODEV; - if (!id->driver_data) { + device_data = (struct soc_device_data *)id->driver_data; + if (device_data && device_data->check) { + // device dependent check, required for MSHW0040 + error = device_data->check(dev); + if (error != 0) + return error; + } + + if (device_data && device_data->button_info) { + button_info = (struct soc_button_info *)device_data->button_info; + } else { button_info = soc_button_get_button_info(dev); if (IS_ERR(button_info)) return PTR_ERR(button_info); - } else { - button_info = (struct soc_button_info *)id->driver_data; } error = gpiod_count(dev, NULL); @@ -357,7 +378,7 @@ static int soc_button_probe(struct platform_device *pdev) if (!priv->children[0] && !priv->children[1]) return -ENODEV; - if (!id->driver_data) + if (!device_data || !device_data->button_info) devm_kfree(dev, button_info); return 0; @@ -377,9 +398,60 @@ static struct soc_button_info soc_button_PNP0C40[] = { { } }; +static struct soc_device_data soc_device_PNP0C40 = { + .button_info = soc_button_PNP0C40, + .check = NULL, +}; + +/* + * Special device check for Surface Book 2 and Surface Pro (2017). + * Both, the Surface Pro 4 (surfacepro3_button.c) and the above mentioned + * devices use MSHW0040 for power and volume buttons, however the way they + * have to be addressed differs. Make sure that we only load this drivers + * for the correct devices by checking the OEM Platform Revision provided by + * the _DSM method. + */ +static int soc_device_check_MSHW0040(struct device *dev) +{ + acpi_handle handle = ACPI_HANDLE(dev); + union acpi_object *result; + u64 oem_platform_rev = 0; + + // get OEM board revision + result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, MSHW0040_DSM_REVISION, + MSHW0040_DSM_GET_OMPR, NULL, ACPI_TYPE_INTEGER); + + if (result) { + oem_platform_rev = result->integer.value; + ACPI_FREE(result); + } + + dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); + + return oem_platform_rev > 0 ? 0 : -ENODEV; +} + +/* + * Button infos for Microsoft Surface Book 2 and Surface Pro (2017). + * Extracted from DSDT. + */ +static struct soc_button_info soc_button_MSHW0040[] = { + { "power", 0, EV_KEY, KEY_POWER, false, true }, + { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, + { "volume_down", 4, EV_KEY, KEY_VOLUMEDOWN, true, false }, + { } +}; + +static struct soc_device_data soc_device_MSHW0040 = { + .button_info = soc_button_MSHW0040, + .check = soc_device_check_MSHW0040, +}; + static const struct acpi_device_id soc_button_acpi_match[] = { - { "PNP0C40", (unsigned long)soc_button_PNP0C40 }, + { "PNP0C40", (unsigned long)&soc_device_PNP0C40 }, { "ACPI0011", 0 }, + /* Microsoft Surface Book 2 and Surface Pro (2017) */ + { "MSHW0040", (unsigned long)&soc_device_MSHW0040 }, { } }; diff --git a/drivers/platform/x86/surfacepro3_button.c b/drivers/platform/x86/surfacepro3_button.c index 1b491690ce07..b67f559ee209 100644 --- a/drivers/platform/x86/surfacepro3_button.c +++ b/drivers/platform/x86/surfacepro3_button.c @@ -24,6 +24,12 @@ #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 @@ -146,6 +152,32 @@ static int surface_button_resume(struct device *dev) } #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. + */ +static int surface_button_check_MSHW0040(struct acpi_device *dev) +{ + acpi_handle handle = dev->handle; + union acpi_object *result; + u64 oem_platform_rev = 0; + + // get OEM board revision + result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID, MSHW0040_DSM_REVISION, + MSHW0040_DSM_GET_OMPR, NULL, ACPI_TYPE_INTEGER); + + 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 ? 0 : -ENODEV; +} + + static int surface_button_add(struct acpi_device *device) { struct surface_button *button; @@ -158,6 +190,10 @@ static int surface_button_add(struct acpi_device *device) strlen(SURFACE_BUTTON_OBJ_NAME))) return -ENODEV; + error = surface_button_check_MSHW0040(device); + if (error) + return error; + button = kzalloc(sizeof(struct surface_button), GFP_KERNEL); if (!button) return -ENOMEM; -- 2.17.1