162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * power/home/volume button support for
462306a36Sopenharmony_ci * Microsoft Surface Pro 3/4 tablet.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2015 Intel Corporation.
762306a36Sopenharmony_ci * All rights reserved.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/types.h>
1462306a36Sopenharmony_ci#include <linux/input.h>
1562306a36Sopenharmony_ci#include <linux/acpi.h>
1662306a36Sopenharmony_ci#include <acpi/button.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define SURFACE_PRO3_BUTTON_HID		"MSHW0028"
1962306a36Sopenharmony_ci#define SURFACE_PRO4_BUTTON_HID		"MSHW0040"
2062306a36Sopenharmony_ci#define SURFACE_BUTTON_OBJ_NAME		"VGBI"
2162306a36Sopenharmony_ci#define SURFACE_BUTTON_DEVICE_NAME	"Surface Pro 3/4 Buttons"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define MSHW0040_DSM_REVISION		0x01
2462306a36Sopenharmony_ci#define MSHW0040_DSM_GET_OMPR		0x02	// get OEM Platform Revision
2562306a36Sopenharmony_cistatic const guid_t MSHW0040_DSM_UUID =
2662306a36Sopenharmony_ci	GUID_INIT(0x6fd05c69, 0xcde3, 0x49f4, 0x95, 0xed, 0xab, 0x16, 0x65,
2762306a36Sopenharmony_ci		  0x49, 0x80, 0x35);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define SURFACE_BUTTON_NOTIFY_TABLET_MODE	0xc8
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define SURFACE_BUTTON_NOTIFY_PRESS_POWER	0xc6
3262306a36Sopenharmony_ci#define SURFACE_BUTTON_NOTIFY_RELEASE_POWER	0xc7
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#define SURFACE_BUTTON_NOTIFY_PRESS_HOME	0xc4
3562306a36Sopenharmony_ci#define SURFACE_BUTTON_NOTIFY_RELEASE_HOME	0xc5
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP	0xc0
3862306a36Sopenharmony_ci#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP	0xc1
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN		0xc2
4162306a36Sopenharmony_ci#define SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN	0xc3
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ciMODULE_AUTHOR("Chen Yu");
4462306a36Sopenharmony_ciMODULE_DESCRIPTION("Surface Pro3 Button Driver");
4562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci/*
4862306a36Sopenharmony_ci * Power button, Home button, Volume buttons support is supposed to
4962306a36Sopenharmony_ci * be covered by drivers/input/misc/soc_button_array.c, which is implemented
5062306a36Sopenharmony_ci * according to "Windows ACPI Design Guide for SoC Platforms".
5162306a36Sopenharmony_ci * However surface pro3 seems not to obey the specs, instead it uses
5262306a36Sopenharmony_ci * device VGBI(MSHW0028) for dispatching the events.
5362306a36Sopenharmony_ci * We choose acpi_driver rather than platform_driver/i2c_driver because
5462306a36Sopenharmony_ci * although VGBI has an i2c resource connected to i2c controller, it
5562306a36Sopenharmony_ci * is not embedded in any i2c controller's scope, thus neither platform_device
5662306a36Sopenharmony_ci * will be created, nor i2c_client will be enumerated, we have to use
5762306a36Sopenharmony_ci * acpi_driver.
5862306a36Sopenharmony_ci */
5962306a36Sopenharmony_cistatic const struct acpi_device_id surface_button_device_ids[] = {
6062306a36Sopenharmony_ci	{SURFACE_PRO3_BUTTON_HID,    0},
6162306a36Sopenharmony_ci	{SURFACE_PRO4_BUTTON_HID,    0},
6262306a36Sopenharmony_ci	{"", 0},
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, surface_button_device_ids);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct surface_button {
6762306a36Sopenharmony_ci	unsigned int type;
6862306a36Sopenharmony_ci	struct input_dev *input;
6962306a36Sopenharmony_ci	char phys[32];			/* for input device */
7062306a36Sopenharmony_ci	unsigned long pushed;
7162306a36Sopenharmony_ci	bool suspended;
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void surface_button_notify(struct acpi_device *device, u32 event)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	struct surface_button *button = acpi_driver_data(device);
7762306a36Sopenharmony_ci	struct input_dev *input;
7862306a36Sopenharmony_ci	int key_code = KEY_RESERVED;
7962306a36Sopenharmony_ci	bool pressed = false;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	switch (event) {
8262306a36Sopenharmony_ci	/* Power button press,release handle */
8362306a36Sopenharmony_ci	case SURFACE_BUTTON_NOTIFY_PRESS_POWER:
8462306a36Sopenharmony_ci		pressed = true;
8562306a36Sopenharmony_ci		fallthrough;
8662306a36Sopenharmony_ci	case SURFACE_BUTTON_NOTIFY_RELEASE_POWER:
8762306a36Sopenharmony_ci		key_code = KEY_POWER;
8862306a36Sopenharmony_ci		break;
8962306a36Sopenharmony_ci	/* Home button press,release handle */
9062306a36Sopenharmony_ci	case SURFACE_BUTTON_NOTIFY_PRESS_HOME:
9162306a36Sopenharmony_ci		pressed = true;
9262306a36Sopenharmony_ci		fallthrough;
9362306a36Sopenharmony_ci	case SURFACE_BUTTON_NOTIFY_RELEASE_HOME:
9462306a36Sopenharmony_ci		key_code = KEY_LEFTMETA;
9562306a36Sopenharmony_ci		break;
9662306a36Sopenharmony_ci	/* Volume up button press,release handle */
9762306a36Sopenharmony_ci	case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_UP:
9862306a36Sopenharmony_ci		pressed = true;
9962306a36Sopenharmony_ci		fallthrough;
10062306a36Sopenharmony_ci	case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_UP:
10162306a36Sopenharmony_ci		key_code = KEY_VOLUMEUP;
10262306a36Sopenharmony_ci		break;
10362306a36Sopenharmony_ci	/* Volume down button press,release handle */
10462306a36Sopenharmony_ci	case SURFACE_BUTTON_NOTIFY_PRESS_VOLUME_DOWN:
10562306a36Sopenharmony_ci		pressed = true;
10662306a36Sopenharmony_ci		fallthrough;
10762306a36Sopenharmony_ci	case SURFACE_BUTTON_NOTIFY_RELEASE_VOLUME_DOWN:
10862306a36Sopenharmony_ci		key_code = KEY_VOLUMEDOWN;
10962306a36Sopenharmony_ci		break;
11062306a36Sopenharmony_ci	case SURFACE_BUTTON_NOTIFY_TABLET_MODE:
11162306a36Sopenharmony_ci		dev_warn_once(&device->dev, "Tablet mode is not supported\n");
11262306a36Sopenharmony_ci		break;
11362306a36Sopenharmony_ci	default:
11462306a36Sopenharmony_ci		dev_info_ratelimited(&device->dev,
11562306a36Sopenharmony_ci				     "Unsupported event [0x%x]\n", event);
11662306a36Sopenharmony_ci		break;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	input = button->input;
11962306a36Sopenharmony_ci	if (key_code == KEY_RESERVED)
12062306a36Sopenharmony_ci		return;
12162306a36Sopenharmony_ci	if (pressed)
12262306a36Sopenharmony_ci		pm_wakeup_dev_event(&device->dev, 0, button->suspended);
12362306a36Sopenharmony_ci	if (button->suspended)
12462306a36Sopenharmony_ci		return;
12562306a36Sopenharmony_ci	input_report_key(input, key_code, pressed?1:0);
12662306a36Sopenharmony_ci	input_sync(input);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
13062306a36Sopenharmony_cistatic int surface_button_suspend(struct device *dev)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct acpi_device *device = to_acpi_device(dev);
13362306a36Sopenharmony_ci	struct surface_button *button = acpi_driver_data(device);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	button->suspended = true;
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cistatic int surface_button_resume(struct device *dev)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct acpi_device *device = to_acpi_device(dev);
14262306a36Sopenharmony_ci	struct surface_button *button = acpi_driver_data(device);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	button->suspended = false;
14562306a36Sopenharmony_ci	return 0;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci#endif
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/*
15062306a36Sopenharmony_ci * Surface Pro 4 and Surface Book 2 / Surface Pro 2017 use the same device
15162306a36Sopenharmony_ci * ID (MSHW0040) for the power/volume buttons. Make sure this is the right
15262306a36Sopenharmony_ci * device by checking for the _DSM method and OEM Platform Revision.
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * Returns true if the driver should bind to this device, i.e. the device is
15562306a36Sopenharmony_ci * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1.
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistatic bool surface_button_check_MSHW0040(struct acpi_device *dev)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	acpi_handle handle = dev->handle;
16062306a36Sopenharmony_ci	union acpi_object *result;
16162306a36Sopenharmony_ci	u64 oem_platform_rev = 0;	// valid revisions are nonzero
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	// get OEM platform revision
16462306a36Sopenharmony_ci	result = acpi_evaluate_dsm_typed(handle, &MSHW0040_DSM_UUID,
16562306a36Sopenharmony_ci					 MSHW0040_DSM_REVISION,
16662306a36Sopenharmony_ci					 MSHW0040_DSM_GET_OMPR,
16762306a36Sopenharmony_ci					 NULL, ACPI_TYPE_INTEGER);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/*
17062306a36Sopenharmony_ci	 * If evaluating the _DSM fails, the method is not present. This means
17162306a36Sopenharmony_ci	 * that we have either MSHW0028 or MSHW0040 on Pro 4 or Book 1, so we
17262306a36Sopenharmony_ci	 * should use this driver. We use revision 0 indicating it is
17362306a36Sopenharmony_ci	 * unavailable.
17462306a36Sopenharmony_ci	 */
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (result) {
17762306a36Sopenharmony_ci		oem_platform_rev = result->integer.value;
17862306a36Sopenharmony_ci		ACPI_FREE(result);
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return oem_platform_rev == 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic int surface_button_add(struct acpi_device *device)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct surface_button *button;
19062306a36Sopenharmony_ci	struct input_dev *input;
19162306a36Sopenharmony_ci	const char *hid = acpi_device_hid(device);
19262306a36Sopenharmony_ci	char *name;
19362306a36Sopenharmony_ci	int error;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (strncmp(acpi_device_bid(device), SURFACE_BUTTON_OBJ_NAME,
19662306a36Sopenharmony_ci	    strlen(SURFACE_BUTTON_OBJ_NAME)))
19762306a36Sopenharmony_ci		return -ENODEV;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!surface_button_check_MSHW0040(device))
20062306a36Sopenharmony_ci		return -ENODEV;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	button = kzalloc(sizeof(struct surface_button), GFP_KERNEL);
20362306a36Sopenharmony_ci	if (!button)
20462306a36Sopenharmony_ci		return -ENOMEM;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	device->driver_data = button;
20762306a36Sopenharmony_ci	button->input = input = input_allocate_device();
20862306a36Sopenharmony_ci	if (!input) {
20962306a36Sopenharmony_ci		error = -ENOMEM;
21062306a36Sopenharmony_ci		goto err_free_button;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	name = acpi_device_name(device);
21462306a36Sopenharmony_ci	strcpy(name, SURFACE_BUTTON_DEVICE_NAME);
21562306a36Sopenharmony_ci	snprintf(button->phys, sizeof(button->phys), "%s/buttons", hid);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	input->name = name;
21862306a36Sopenharmony_ci	input->phys = button->phys;
21962306a36Sopenharmony_ci	input->id.bustype = BUS_HOST;
22062306a36Sopenharmony_ci	input->dev.parent = &device->dev;
22162306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, KEY_POWER);
22262306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, KEY_LEFTMETA);
22362306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, KEY_VOLUMEUP);
22462306a36Sopenharmony_ci	input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	error = input_register_device(input);
22762306a36Sopenharmony_ci	if (error)
22862306a36Sopenharmony_ci		goto err_free_input;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	device_init_wakeup(&device->dev, true);
23162306a36Sopenharmony_ci	dev_info(&device->dev,
23262306a36Sopenharmony_ci			"%s [%s]\n", name, acpi_device_bid(device));
23362306a36Sopenharmony_ci	return 0;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci err_free_input:
23662306a36Sopenharmony_ci	input_free_device(input);
23762306a36Sopenharmony_ci err_free_button:
23862306a36Sopenharmony_ci	kfree(button);
23962306a36Sopenharmony_ci	return error;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic void surface_button_remove(struct acpi_device *device)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct surface_button *button = acpi_driver_data(device);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	input_unregister_device(button->input);
24762306a36Sopenharmony_ci	kfree(button);
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(surface_button_pm,
25162306a36Sopenharmony_ci		surface_button_suspend, surface_button_resume);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_cistatic struct acpi_driver surface_button_driver = {
25462306a36Sopenharmony_ci	.name = "surface_pro3_button",
25562306a36Sopenharmony_ci	.class = "SurfacePro3",
25662306a36Sopenharmony_ci	.ids = surface_button_device_ids,
25762306a36Sopenharmony_ci	.ops = {
25862306a36Sopenharmony_ci		.add = surface_button_add,
25962306a36Sopenharmony_ci		.remove = surface_button_remove,
26062306a36Sopenharmony_ci		.notify = surface_button_notify,
26162306a36Sopenharmony_ci	},
26262306a36Sopenharmony_ci	.drv.pm = &surface_button_pm,
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cimodule_acpi_driver(surface_button_driver);
266