18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Eee PC WMI hotkey driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright(C) 2010 Intel Corporation.
68c2ecf20Sopenharmony_ci * Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Portions based on wistron_btns.c:
98c2ecf20Sopenharmony_ci * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
108c2ecf20Sopenharmony_ci * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
118c2ecf20Sopenharmony_ci * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
128c2ecf20Sopenharmony_ci */
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/module.h>
188c2ecf20Sopenharmony_ci#include <linux/init.h>
198c2ecf20Sopenharmony_ci#include <linux/input.h>
208c2ecf20Sopenharmony_ci#include <linux/input/sparse-keymap.h>
218c2ecf20Sopenharmony_ci#include <linux/dmi.h>
228c2ecf20Sopenharmony_ci#include <linux/fb.h>
238c2ecf20Sopenharmony_ci#include <linux/acpi.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "asus-wmi.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define	EEEPC_WMI_FILE	"eeepc-wmi"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ciMODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>");
308c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
318c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define EEEPC_ACPI_HID		"ASUS010" /* old _HID used in eeepc-laptop */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define EEEPC_WMI_EVENT_GUID	"ABBC0F72-8EA1-11D1-00A0-C90629100000"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ciMODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic bool hotplug_wireless;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cimodule_param(hotplug_wireless, bool, 0444);
428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hotplug_wireless,
438c2ecf20Sopenharmony_ci		 "Enable hotplug for wireless device. "
448c2ecf20Sopenharmony_ci		 "If your laptop needs that, please report to "
458c2ecf20Sopenharmony_ci		 "acpi4asus-user@lists.sourceforge.net.");
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* Values for T101MT "Home" key */
488c2ecf20Sopenharmony_ci#define HOME_PRESS	0xe4
498c2ecf20Sopenharmony_ci#define HOME_HOLD	0xea
508c2ecf20Sopenharmony_ci#define HOME_RELEASE	0xe5
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const struct key_entry eeepc_wmi_keymap[] = {
538c2ecf20Sopenharmony_ci	{ KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },
548c2ecf20Sopenharmony_ci	{ KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },
558c2ecf20Sopenharmony_ci	/* Sleep already handled via generic ACPI code */
568c2ecf20Sopenharmony_ci	{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
578c2ecf20Sopenharmony_ci	{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
588c2ecf20Sopenharmony_ci	{ KE_KEY, 0x32, { KEY_MUTE } },
598c2ecf20Sopenharmony_ci	{ KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */
608c2ecf20Sopenharmony_ci	{ KE_KEY, 0x5d, { KEY_WLAN } },
618c2ecf20Sopenharmony_ci	{ KE_KEY, 0x6b, { KEY_TOUCHPAD_TOGGLE } }, /* Toggle Touchpad */
628c2ecf20Sopenharmony_ci	{ KE_KEY, 0x82, { KEY_CAMERA } },
638c2ecf20Sopenharmony_ci	{ KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } },
648c2ecf20Sopenharmony_ci	{ KE_KEY, 0x88, { KEY_WLAN } },
658c2ecf20Sopenharmony_ci	{ KE_KEY, 0xbd, { KEY_CAMERA } },
668c2ecf20Sopenharmony_ci	{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
678c2ecf20Sopenharmony_ci	{ KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */
688c2ecf20Sopenharmony_ci	{ KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
698c2ecf20Sopenharmony_ci	{ KE_KEY, HOME_PRESS, { KEY_CONFIG } }, /* Home/Express gate key */
708c2ecf20Sopenharmony_ci	{ KE_KEY, 0xe8, { KEY_SCREENLOCK } },
718c2ecf20Sopenharmony_ci	{ KE_KEY, 0xe9, { KEY_DISPLAYTOGGLE } },
728c2ecf20Sopenharmony_ci	{ KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
738c2ecf20Sopenharmony_ci	{ KE_KEY, 0xec, { KEY_CAMERA_UP } },
748c2ecf20Sopenharmony_ci	{ KE_KEY, 0xed, { KEY_CAMERA_DOWN } },
758c2ecf20Sopenharmony_ci	{ KE_KEY, 0xee, { KEY_CAMERA_LEFT } },
768c2ecf20Sopenharmony_ci	{ KE_KEY, 0xef, { KEY_CAMERA_RIGHT } },
778c2ecf20Sopenharmony_ci	{ KE_KEY, 0xf3, { KEY_MENU } },
788c2ecf20Sopenharmony_ci	{ KE_KEY, 0xf5, { KEY_HOMEPAGE } },
798c2ecf20Sopenharmony_ci	{ KE_KEY, 0xf6, { KEY_ESC } },
808c2ecf20Sopenharmony_ci	{ KE_END, 0},
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_unknown = {
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_1000h = {
878c2ecf20Sopenharmony_ci	.hotplug_wireless = true,
888c2ecf20Sopenharmony_ci};
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_et2012_type1 = {
918c2ecf20Sopenharmony_ci	.store_backlight_power = true,
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_et2012_type3 = {
958c2ecf20Sopenharmony_ci	.scalar_panel_brightness = true,
968c2ecf20Sopenharmony_ci	.store_backlight_power = true,
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic struct quirk_entry quirk_asus_x101ch = {
1008c2ecf20Sopenharmony_ci	/* We need this when ACPI function doesn't do this well */
1018c2ecf20Sopenharmony_ci	.wmi_backlight_power = true,
1028c2ecf20Sopenharmony_ci};
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic struct quirk_entry *quirks;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic void et2012_quirks(void)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	const struct dmi_device *dev = NULL;
1098c2ecf20Sopenharmony_ci	char oemstring[30];
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) {
1128c2ecf20Sopenharmony_ci		if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) {
1138c2ecf20Sopenharmony_ci			if (oemstring[18] == '1')
1148c2ecf20Sopenharmony_ci				quirks = &quirk_asus_et2012_type1;
1158c2ecf20Sopenharmony_ci			else if (oemstring[18] == '3')
1168c2ecf20Sopenharmony_ci				quirks = &quirk_asus_et2012_type3;
1178c2ecf20Sopenharmony_ci			break;
1188c2ecf20Sopenharmony_ci		}
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic int dmi_matched(const struct dmi_system_id *dmi)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	char *model;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	quirks = dmi->driver_data;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	model = (char *)dmi->matches[1].substr;
1298c2ecf20Sopenharmony_ci	if (unlikely(strncmp(model, "ET2012", 6) == 0))
1308c2ecf20Sopenharmony_ci		et2012_quirks();
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 1;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic const struct dmi_system_id asus_quirks[] = {
1368c2ecf20Sopenharmony_ci	{
1378c2ecf20Sopenharmony_ci		.callback = dmi_matched,
1388c2ecf20Sopenharmony_ci		.ident = "ASUSTeK Computer INC. 1000H",
1398c2ecf20Sopenharmony_ci		.matches = {
1408c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
1418c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "1000H"),
1428c2ecf20Sopenharmony_ci		},
1438c2ecf20Sopenharmony_ci		.driver_data = &quirk_asus_1000h,
1448c2ecf20Sopenharmony_ci	},
1458c2ecf20Sopenharmony_ci	{
1468c2ecf20Sopenharmony_ci		.callback = dmi_matched,
1478c2ecf20Sopenharmony_ci		.ident = "ASUSTeK Computer INC. ET2012E/I",
1488c2ecf20Sopenharmony_ci		.matches = {
1498c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
1508c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "ET2012"),
1518c2ecf20Sopenharmony_ci		},
1528c2ecf20Sopenharmony_ci		.driver_data = &quirk_asus_unknown,
1538c2ecf20Sopenharmony_ci	},
1548c2ecf20Sopenharmony_ci	{
1558c2ecf20Sopenharmony_ci		.callback = dmi_matched,
1568c2ecf20Sopenharmony_ci		.ident = "ASUSTeK Computer INC. X101CH",
1578c2ecf20Sopenharmony_ci		.matches = {
1588c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
1598c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "X101CH"),
1608c2ecf20Sopenharmony_ci		},
1618c2ecf20Sopenharmony_ci		.driver_data = &quirk_asus_x101ch,
1628c2ecf20Sopenharmony_ci	},
1638c2ecf20Sopenharmony_ci	{
1648c2ecf20Sopenharmony_ci		.callback = dmi_matched,
1658c2ecf20Sopenharmony_ci		.ident = "ASUSTeK Computer INC. 1015CX",
1668c2ecf20Sopenharmony_ci		.matches = {
1678c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
1688c2ecf20Sopenharmony_ci			DMI_MATCH(DMI_PRODUCT_NAME, "1015CX"),
1698c2ecf20Sopenharmony_ci		},
1708c2ecf20Sopenharmony_ci		.driver_data = &quirk_asus_x101ch,
1718c2ecf20Sopenharmony_ci	},
1728c2ecf20Sopenharmony_ci	{},
1738c2ecf20Sopenharmony_ci};
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_cistatic void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code,
1768c2ecf20Sopenharmony_ci				 unsigned int *value, bool *autorelease)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	switch (*code) {
1798c2ecf20Sopenharmony_ci	case HOME_PRESS:
1808c2ecf20Sopenharmony_ci		*value = 1;
1818c2ecf20Sopenharmony_ci		*autorelease = 0;
1828c2ecf20Sopenharmony_ci		break;
1838c2ecf20Sopenharmony_ci	case HOME_HOLD:
1848c2ecf20Sopenharmony_ci		*code = ASUS_WMI_KEY_IGNORE;
1858c2ecf20Sopenharmony_ci		break;
1868c2ecf20Sopenharmony_ci	case HOME_RELEASE:
1878c2ecf20Sopenharmony_ci		*code = HOME_PRESS;
1888c2ecf20Sopenharmony_ci		*value = 0;
1898c2ecf20Sopenharmony_ci		*autorelease = 0;
1908c2ecf20Sopenharmony_ci		break;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic int eeepc_wmi_probe(struct platform_device *pdev)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	if (acpi_dev_found(EEEPC_ACPI_HID)) {
1978c2ecf20Sopenharmony_ci		pr_warn("Found legacy ATKD device (%s)\n", EEEPC_ACPI_HID);
1988c2ecf20Sopenharmony_ci		pr_warn("WMI device present, but legacy ATKD device is also "
1998c2ecf20Sopenharmony_ci			"present and enabled\n");
2008c2ecf20Sopenharmony_ci		pr_warn("You probably booted with acpi_osi=\"Linux\" or "
2018c2ecf20Sopenharmony_ci			"acpi_osi=\"!Windows 2009\"\n");
2028c2ecf20Sopenharmony_ci		pr_warn("Can't load eeepc-wmi, use default acpi_osi "
2038c2ecf20Sopenharmony_ci			"(preferred) or eeepc-laptop\n");
2048c2ecf20Sopenharmony_ci		return -EBUSY;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	quirks = &quirk_asus_unknown;
2128c2ecf20Sopenharmony_ci	quirks->hotplug_wireless = hotplug_wireless;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	dmi_check_system(asus_quirks);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	driver->quirks = quirks;
2178c2ecf20Sopenharmony_ci	driver->quirks->wapf = -1;
2188c2ecf20Sopenharmony_ci	driver->panel_power = FB_BLANK_UNBLANK;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic struct asus_wmi_driver asus_wmi_driver = {
2228c2ecf20Sopenharmony_ci	.name = EEEPC_WMI_FILE,
2238c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
2248c2ecf20Sopenharmony_ci	.event_guid = EEEPC_WMI_EVENT_GUID,
2258c2ecf20Sopenharmony_ci	.keymap = eeepc_wmi_keymap,
2268c2ecf20Sopenharmony_ci	.input_name = "Eee PC WMI hotkeys",
2278c2ecf20Sopenharmony_ci	.input_phys = EEEPC_WMI_FILE "/input0",
2288c2ecf20Sopenharmony_ci	.key_filter = eeepc_wmi_key_filter,
2298c2ecf20Sopenharmony_ci	.probe = eeepc_wmi_probe,
2308c2ecf20Sopenharmony_ci	.detect_quirks = eeepc_wmi_quirks,
2318c2ecf20Sopenharmony_ci};
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic int __init eeepc_wmi_init(void)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	return asus_wmi_register_driver(&asus_wmi_driver);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic void __exit eeepc_wmi_exit(void)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	asus_wmi_unregister_driver(&asus_wmi_driver);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cimodule_init(eeepc_wmi_init);
2458c2ecf20Sopenharmony_cimodule_exit(eeepc_wmi_exit);
246