0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/acpi.h>
0010 #include <linux/dmi.h>
0011 #include <linux/input.h>
0012 #include <linux/input/sparse-keymap.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/suspend.h>
0017 #include "../dual_accel_detect.h"
0018
0019
0020 #define VGBS_TABLET_MODE_FLAG_ALT 0x10
0021
0022 #define VGBS_TABLET_MODE_FLAG 0x40
0023 #define VGBS_DOCK_MODE_FLAG 0x80
0024
0025 #define VGBS_TABLET_MODE_FLAGS (VGBS_TABLET_MODE_FLAG | VGBS_TABLET_MODE_FLAG_ALT)
0026
0027 MODULE_LICENSE("GPL");
0028 MODULE_AUTHOR("AceLan Kao");
0029
0030 static const struct acpi_device_id intel_vbtn_ids[] = {
0031 {"INT33D6", 0},
0032 {"", 0},
0033 };
0034 MODULE_DEVICE_TABLE(acpi, intel_vbtn_ids);
0035
0036
0037 static const struct key_entry intel_vbtn_keymap[] = {
0038 { KE_KEY, 0xC0, { KEY_POWER } },
0039 { KE_IGNORE, 0xC1, { KEY_POWER } },
0040 { KE_KEY, 0xC2, { KEY_LEFTMETA } },
0041 { KE_KEY, 0xC3, { KEY_LEFTMETA } },
0042 { KE_KEY, 0xC4, { KEY_VOLUMEUP } },
0043 { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } },
0044 { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } },
0045 { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } },
0046 { KE_KEY, 0xC8, { KEY_ROTATE_LOCK_TOGGLE } },
0047 { KE_KEY, 0xC9, { KEY_ROTATE_LOCK_TOGGLE } },
0048 { KE_END }
0049 };
0050
0051 static const struct key_entry intel_vbtn_switchmap[] = {
0052
0053
0054
0055
0056
0057
0058
0059
0060 { KE_IGNORE, 0xCA, { .sw = { SW_DOCK, 1 } } },
0061 { KE_IGNORE, 0xCB, { .sw = { SW_DOCK, 0 } } },
0062 { KE_SW, 0xCC, { .sw = { SW_TABLET_MODE, 1 } } },
0063 { KE_SW, 0xCD, { .sw = { SW_TABLET_MODE, 0 } } },
0064 { KE_END }
0065 };
0066
0067 struct intel_vbtn_priv {
0068 struct input_dev *buttons_dev;
0069 struct input_dev *switches_dev;
0070 bool dual_accel;
0071 bool has_buttons;
0072 bool has_switches;
0073 bool wakeup_mode;
0074 };
0075
0076 static void detect_tablet_mode(struct platform_device *device)
0077 {
0078 struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
0079 acpi_handle handle = ACPI_HANDLE(&device->dev);
0080 unsigned long long vgbs;
0081 acpi_status status;
0082 int m;
0083
0084 status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
0085 if (ACPI_FAILURE(status))
0086 return;
0087
0088 m = !(vgbs & VGBS_TABLET_MODE_FLAGS);
0089 input_report_switch(priv->switches_dev, SW_TABLET_MODE, m);
0090 m = (vgbs & VGBS_DOCK_MODE_FLAG) ? 1 : 0;
0091 input_report_switch(priv->switches_dev, SW_DOCK, m);
0092 }
0093
0094
0095
0096
0097
0098
0099
0100
0101 static int intel_vbtn_input_setup(struct platform_device *device)
0102 {
0103 struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
0104 int ret;
0105
0106 priv->buttons_dev = devm_input_allocate_device(&device->dev);
0107 if (!priv->buttons_dev)
0108 return -ENOMEM;
0109
0110 ret = sparse_keymap_setup(priv->buttons_dev, intel_vbtn_keymap, NULL);
0111 if (ret)
0112 return ret;
0113
0114 priv->buttons_dev->dev.parent = &device->dev;
0115 priv->buttons_dev->name = "Intel Virtual Buttons";
0116 priv->buttons_dev->id.bustype = BUS_HOST;
0117
0118 if (priv->has_buttons) {
0119 ret = input_register_device(priv->buttons_dev);
0120 if (ret)
0121 return ret;
0122 }
0123
0124 priv->switches_dev = devm_input_allocate_device(&device->dev);
0125 if (!priv->switches_dev)
0126 return -ENOMEM;
0127
0128 ret = sparse_keymap_setup(priv->switches_dev, intel_vbtn_switchmap, NULL);
0129 if (ret)
0130 return ret;
0131
0132 priv->switches_dev->dev.parent = &device->dev;
0133 priv->switches_dev->name = "Intel Virtual Switches";
0134 priv->switches_dev->id.bustype = BUS_HOST;
0135
0136 if (priv->has_switches) {
0137 detect_tablet_mode(device);
0138
0139 ret = input_register_device(priv->switches_dev);
0140 if (ret)
0141 return ret;
0142 }
0143
0144 return 0;
0145 }
0146
0147 static void notify_handler(acpi_handle handle, u32 event, void *context)
0148 {
0149 struct platform_device *device = context;
0150 struct intel_vbtn_priv *priv = dev_get_drvdata(&device->dev);
0151 unsigned int val = !(event & 1);
0152 const struct key_entry *ke, *ke_rel;
0153 struct input_dev *input_dev;
0154 bool autorelease;
0155 int ret;
0156
0157 if ((ke = sparse_keymap_entry_from_scancode(priv->buttons_dev, event))) {
0158 if (!priv->has_buttons) {
0159 dev_warn(&device->dev, "Warning: received a button event on a device without buttons, please report this.\n");
0160 return;
0161 }
0162 input_dev = priv->buttons_dev;
0163 } else if ((ke = sparse_keymap_entry_from_scancode(priv->switches_dev, event))) {
0164 if (!priv->has_switches) {
0165
0166 if (priv->dual_accel)
0167 return;
0168
0169 dev_info(&device->dev, "Registering Intel Virtual Switches input-dev after receiving a switch event\n");
0170 ret = input_register_device(priv->switches_dev);
0171 if (ret)
0172 return;
0173
0174 priv->has_switches = true;
0175 }
0176 input_dev = priv->switches_dev;
0177 } else {
0178 dev_dbg(&device->dev, "unknown event index 0x%x\n", event);
0179 return;
0180 }
0181
0182 if (priv->wakeup_mode) {
0183 pm_wakeup_hard_event(&device->dev);
0184
0185
0186
0187
0188
0189 if (ke->type == KE_KEY)
0190 return;
0191 }
0192
0193
0194
0195
0196
0197 ke_rel = sparse_keymap_entry_from_scancode(input_dev, event | 1);
0198 autorelease = val && (!ke_rel || ke_rel->type == KE_IGNORE);
0199
0200 sparse_keymap_report_event(input_dev, event, val, autorelease);
0201 }
0202
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219 static const struct dmi_system_id dmi_switches_allow_list[] = {
0220 {
0221 .matches = {
0222 DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "31" ),
0223 },
0224 },
0225 {
0226 .matches = {
0227 DMI_EXACT_MATCH(DMI_CHASSIS_TYPE, "32" ),
0228 },
0229 },
0230 {
0231 .matches = {
0232 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
0233 DMI_MATCH(DMI_PRODUCT_NAME, "Venue 11 Pro 7130"),
0234 },
0235 },
0236 {
0237 .matches = {
0238 DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
0239 DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion 13 x360 PC"),
0240 },
0241 },
0242 {
0243 .matches = {
0244 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
0245 DMI_MATCH(DMI_PRODUCT_NAME, "Switch SA5-271"),
0246 },
0247 },
0248 {
0249 .matches = {
0250 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
0251 DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7352"),
0252 },
0253 },
0254 {}
0255 };
0256
0257 static bool intel_vbtn_has_switches(acpi_handle handle, bool dual_accel)
0258 {
0259 unsigned long long vgbs;
0260 acpi_status status;
0261
0262
0263 if (dual_accel)
0264 return false;
0265
0266 if (!dmi_check_system(dmi_switches_allow_list))
0267 return false;
0268
0269 status = acpi_evaluate_integer(handle, "VGBS", NULL, &vgbs);
0270 return ACPI_SUCCESS(status);
0271 }
0272
0273 static int intel_vbtn_probe(struct platform_device *device)
0274 {
0275 acpi_handle handle = ACPI_HANDLE(&device->dev);
0276 bool dual_accel, has_buttons, has_switches;
0277 struct intel_vbtn_priv *priv;
0278 acpi_status status;
0279 int err;
0280
0281 dual_accel = dual_accel_detect();
0282 has_buttons = acpi_has_method(handle, "VBDL");
0283 has_switches = intel_vbtn_has_switches(handle, dual_accel);
0284
0285 if (!has_buttons && !has_switches) {
0286 dev_warn(&device->dev, "failed to read Intel Virtual Button driver\n");
0287 return -ENODEV;
0288 }
0289
0290 priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
0291 if (!priv)
0292 return -ENOMEM;
0293 dev_set_drvdata(&device->dev, priv);
0294
0295 priv->dual_accel = dual_accel;
0296 priv->has_buttons = has_buttons;
0297 priv->has_switches = has_switches;
0298
0299 err = intel_vbtn_input_setup(device);
0300 if (err) {
0301 pr_err("Failed to setup Intel Virtual Button\n");
0302 return err;
0303 }
0304
0305 status = acpi_install_notify_handler(handle,
0306 ACPI_DEVICE_NOTIFY,
0307 notify_handler,
0308 device);
0309 if (ACPI_FAILURE(status))
0310 return -EBUSY;
0311
0312 if (has_buttons) {
0313 status = acpi_evaluate_object(handle, "VBDL", NULL, NULL);
0314 if (ACPI_FAILURE(status))
0315 dev_err(&device->dev, "Error VBDL failed with ACPI status %d\n", status);
0316 }
0317
0318 device_init_wakeup(&device->dev, true);
0319
0320
0321
0322
0323
0324 acpi_ec_mark_gpe_for_wake();
0325 return 0;
0326 }
0327
0328 static int intel_vbtn_remove(struct platform_device *device)
0329 {
0330 acpi_handle handle = ACPI_HANDLE(&device->dev);
0331
0332 device_init_wakeup(&device->dev, false);
0333 acpi_remove_notify_handler(handle, ACPI_DEVICE_NOTIFY, notify_handler);
0334
0335
0336
0337
0338
0339 return 0;
0340 }
0341
0342 static int intel_vbtn_pm_prepare(struct device *dev)
0343 {
0344 if (device_may_wakeup(dev)) {
0345 struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
0346
0347 priv->wakeup_mode = true;
0348 }
0349 return 0;
0350 }
0351
0352 static void intel_vbtn_pm_complete(struct device *dev)
0353 {
0354 struct intel_vbtn_priv *priv = dev_get_drvdata(dev);
0355
0356 priv->wakeup_mode = false;
0357 }
0358
0359 static int intel_vbtn_pm_resume(struct device *dev)
0360 {
0361 intel_vbtn_pm_complete(dev);
0362 return 0;
0363 }
0364
0365 static const struct dev_pm_ops intel_vbtn_pm_ops = {
0366 .prepare = intel_vbtn_pm_prepare,
0367 .complete = intel_vbtn_pm_complete,
0368 .resume = intel_vbtn_pm_resume,
0369 .restore = intel_vbtn_pm_resume,
0370 .thaw = intel_vbtn_pm_resume,
0371 };
0372
0373 static struct platform_driver intel_vbtn_pl_driver = {
0374 .driver = {
0375 .name = "intel-vbtn",
0376 .acpi_match_table = intel_vbtn_ids,
0377 .pm = &intel_vbtn_pm_ops,
0378 },
0379 .probe = intel_vbtn_probe,
0380 .remove = intel_vbtn_remove,
0381 };
0382
0383 static acpi_status __init
0384 check_acpi_dev(acpi_handle handle, u32 lvl, void *context, void **rv)
0385 {
0386 const struct acpi_device_id *ids = context;
0387 struct acpi_device *dev = acpi_fetch_acpi_dev(handle);
0388
0389 if (dev && acpi_match_device_ids(dev, ids) == 0)
0390 if (!IS_ERR_OR_NULL(acpi_create_platform_device(dev, NULL)))
0391 dev_info(&dev->dev,
0392 "intel-vbtn: created platform device\n");
0393
0394 return AE_OK;
0395 }
0396
0397 static int __init intel_vbtn_init(void)
0398 {
0399 acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
0400 ACPI_UINT32_MAX, check_acpi_dev, NULL,
0401 (void *)intel_vbtn_ids, NULL);
0402
0403 return platform_driver_register(&intel_vbtn_pl_driver);
0404 }
0405 module_init(intel_vbtn_init);
0406
0407 static void __exit intel_vbtn_exit(void)
0408 {
0409 platform_driver_unregister(&intel_vbtn_pl_driver);
0410 }
0411 module_exit(intel_vbtn_exit);