Today I have modified the patch slightly to apply to 4.4.1 kernel and built a kernel with it applied - power button now works for me. The patch is attached. I can probably make a similar one for master branch and for earlier releases too, let me know what I should do. Thanks! 2016-02-03 19:04 GMT+03:00 Dmitry Sutyagin <f3flight@xxxxxxxxx>: > Dear all, > > I am writing to ask for the attached patch to be reviewed and merged > into linux kernel (preferably starting from 3.18 but at least for 4.4 > and up). > The patch was created by Jan-Michael Brummer, I took it from here - > https://bugzilla.kernel.org/show_bug.cgi?id=102281 > > Please let me know if I can help in any way.
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1089eaa..c519d2e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -944,4 +944,10 @@ config SURFACE_PRO3_BUTTON depends on ACPI && INPUT ---help--- This driver handles the power/home/volume buttons on the Microsoft Surface Pro 3 tablet. + +config DV11P_POWER_BUTTON + tristate "Dell Venue 11 Pro Power Button Driver" + depends on ACPI + ---help--- + This driver provides power button support for the Dell Venue 11 Pro tablet. endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 3ca78a3..338b0fc 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -62,3 +62,4 @@ obj-$(CONFIG_PVPANIC) += pvpanic.o obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o obj-$(CONFIG_INTEL_PMC_IPC) += intel_pmc_ipc.o obj-$(CONFIG_SURFACE_PRO3_BUTTON) += surfacepro3_button.o +obj-$(CONFIG_DV11P_POWER_BUTTON) += dv11p_power_button.o diff --git a/drivers/platform/x86/dv11p_power_button.c b/drivers/platform/x86/dv11p_power_button.c new file mode 100644 index 0000000..03250ae --- /dev/null +++ b/drivers/platform/x86/dv11p_power_button.c @@ -0,0 +1,196 @@ +/* + * Power button support for Dell Venue 11 Pro + * + * (C) Copyright 2015 Jan-Michael Brummer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/input.h> +#include <linux/acpi.h> +#include <acpi/button.h> + +#define DV11P_BUTTON_HID "INT33D6" +#define DV11P_BUTTON_CLASS "button" +#define DV11P_BUTTON_SUB_CLASS "ec" +#define DV11P_BUTTON_DEVICE_NAME "EC" + +#define DV11P_BUTTON_NOTIFY_PRESS_POWER 0xc0 +#define DV11P_BUTTON_NOTIFY_RELEASE_POWER 0xc1 + +ACPI_MODULE_NAME("dv11p power button"); + +#define _COMPONENT ACPI_BUTTON_COMPONENT + +MODULE_AUTHOR("Jan-Michael Brummer"); +MODULE_DESCRIPTION("Dell Venue 11 Pro Power Button Driver"); +MODULE_LICENSE("GPL"); + +/* + * Power button are handled by EC + */ +static const struct acpi_device_id dv11p_power_button_device_ids[] = { + {DV11P_BUTTON_HID, 0}, + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, dv11p_power_button_device_ids); + +static int dv11p_power_button_add(struct acpi_device *device); +static int dv11p_power_button_remove(struct acpi_device *device); +static void dv11p_power_button_notify(struct acpi_device *device, u32 event); + +#ifdef CONFIG_PM_SLEEP +static int dv11p_power_button_suspend(struct device *dev); +static int dv11p_power_button_resume(struct device *dev); +#else +#define dv11p_power_button_suspend NULL +#define dv11p_power_button_resume NULL +#endif + +static SIMPLE_DEV_PM_OPS(dv11p_power_button_pm, + dv11p_power_button_suspend, dv11p_power_button_resume); + +static struct acpi_driver dv11p_power_button_driver = { + .name = "dell venue 11 pro button", + .class = DV11P_BUTTON_CLASS, + .ids = dv11p_power_button_device_ids, + .ops = { + .add = dv11p_power_button_add, + .remove = dv11p_power_button_remove, + .notify = dv11p_power_button_notify, + }, + .drv.pm = &dv11p_power_button_pm, +}; + +struct dv11p_power_button { + unsigned int type; + struct input_dev *input; + char phys[32]; /* for input device */ + unsigned long pushed; + bool suspended; +}; + +static void dv11p_power_button_notify(struct acpi_device *device, u32 event) +{ + struct dv11p_power_button *button = acpi_driver_data(device); + struct input_dev *input; + int key_code = KEY_RESERVED, pressed = 0; + + switch (event) { + case DV11P_BUTTON_NOTIFY_PRESS_POWER: + pressed = 1; + case DV11P_BUTTON_NOTIFY_RELEASE_POWER: + key_code = KEY_POWER; + break; + + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + input = button->input; + + if (KEY_RESERVED == key_code) + return; + + if (pressed) + pm_wakeup_event(&device->dev, 0); + + if (button->suspended) + return; + + input_report_key(input, key_code, pressed); + input_sync(input); + +} + +#ifdef CONFIG_PM_SLEEP +static int dv11p_power_button_suspend(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct dv11p_power_button *button = acpi_driver_data(device); + + button->suspended = true; + return 0; +} + +static int dv11p_power_button_resume(struct device *dev) +{ + struct acpi_device *device = to_acpi_device(dev); + struct dv11p_power_button *button = acpi_driver_data(device); + + button->suspended = false; + return 0; +} +#endif + +static int dv11p_power_button_add(struct acpi_device *device) +{ + struct dv11p_power_button *button; + struct input_dev *input; + const char *hid = acpi_device_hid(device); + char *name, *class; + int error; + + button = kzalloc(sizeof(struct dv11p_power_button), GFP_KERNEL); + if (!button) + return -ENOMEM; + + device->driver_data = button; + + button->input = input = input_allocate_device(); + if (!input) { + error = -ENOMEM; + goto err_free_power_button; + } + + name = acpi_device_name(device); + class = acpi_device_class(device); + + strcpy(name, DV11P_BUTTON_DEVICE_NAME); + sprintf(class, "%s/%s", + DV11P_BUTTON_CLASS, DV11P_BUTTON_SUB_CLASS); + + snprintf(button->phys, sizeof(button->phys), "%s/button/input0", 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); + + + error = input_register_device(input); + if (error) + goto err_free_input; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "%s [%s]\n", name, acpi_device_bid(device))); + return 0; + + err_free_input: + input_free_device(input); + err_free_power_button: + kfree(button); + return error; +} + +static int dv11p_power_button_remove(struct acpi_device *device) +{ + struct dv11p_power_button *button = acpi_driver_data(device); + + input_unregister_device(button->input); + kfree(button); + return 0; +} + +module_acpi_driver(dv11p_power_button_driver);