1 // SPDX-License-Identifier: GPL-2.0+ 2 // Driver to detect Tablet Mode for ChromeOS convertible. 3 // 4 // Copyright (C) 2017 Google, Inc. 5 // Author: Gwendal Grignou <gwendal@chromium.org> 6 7 #include <linux/acpi.h> 8 #include <linux/input.h> 9 #include <linux/io.h> 10 #include <linux/module.h> 11 #include <linux/printk.h> 12 13 #define DRV_NAME "chromeos_tbmc" 14 #define ACPI_DRV_NAME "GOOG0006" 15 16 static int chromeos_tbmc_query_switch(struct acpi_device *adev, 17 struct input_dev *idev) 18 { 19 unsigned long long state; 20 acpi_status status; 21 22 status = acpi_evaluate_integer(adev->handle, "TBMC", NULL, &state); 23 if (ACPI_FAILURE(status)) 24 return -ENODEV; 25 26 /* input layer checks if event is redundant */ 27 input_report_switch(idev, SW_TABLET_MODE, state); 28 input_sync(idev); 29 30 return 0; 31 } 32 33 static __maybe_unused int chromeos_tbmc_resume(struct device *dev) 34 { 35 struct acpi_device *adev = to_acpi_device(dev); 36 37 return chromeos_tbmc_query_switch(adev, adev->driver_data); 38 } 39 40 static void chromeos_tbmc_notify(struct acpi_device *adev, u32 event) 41 { 42 switch (event) { 43 case 0x80: 44 chromeos_tbmc_query_switch(adev, adev->driver_data); 45 break; 46 default: 47 dev_err(&adev->dev, "Unexpected event: 0x%08X\n", event); 48 } 49 } 50 51 static int chromeos_tbmc_open(struct input_dev *idev) 52 { 53 struct acpi_device *adev = input_get_drvdata(idev); 54 55 return chromeos_tbmc_query_switch(adev, idev); 56 } 57 58 static int chromeos_tbmc_add(struct acpi_device *adev) 59 { 60 struct input_dev *idev; 61 struct device *dev = &adev->dev; 62 int ret; 63 64 idev = devm_input_allocate_device(dev); 65 if (!idev) 66 return -ENOMEM; 67 68 idev->name = "Tablet Mode Switch"; 69 idev->phys = acpi_device_hid(adev); 70 71 idev->id.bustype = BUS_HOST; 72 idev->id.version = 1; 73 idev->id.product = 0; 74 idev->open = chromeos_tbmc_open; 75 76 input_set_drvdata(idev, adev); 77 adev->driver_data = idev; 78 79 input_set_capability(idev, EV_SW, SW_TABLET_MODE); 80 ret = input_register_device(idev); 81 if (ret) { 82 dev_err(dev, "cannot register input device\n"); 83 return ret; 84 } 85 return 0; 86 } 87 88 static const struct acpi_device_id chromeos_tbmc_acpi_device_ids[] = { 89 { ACPI_DRV_NAME, 0 }, 90 { } 91 }; 92 MODULE_DEVICE_TABLE(acpi, chromeos_tbmc_acpi_device_ids); 93 94 static const SIMPLE_DEV_PM_OPS(chromeos_tbmc_pm_ops, NULL, 95 chromeos_tbmc_resume); 96 97 static struct acpi_driver chromeos_tbmc_driver = { 98 .name = DRV_NAME, 99 .class = DRV_NAME, 100 .ids = chromeos_tbmc_acpi_device_ids, 101 .ops = { 102 .add = chromeos_tbmc_add, 103 .notify = chromeos_tbmc_notify, 104 }, 105 .drv.pm = &chromeos_tbmc_pm_ops, 106 }; 107 108 module_acpi_driver(chromeos_tbmc_driver); 109 110 MODULE_LICENSE("GPL v2"); 111 MODULE_DESCRIPTION("ChromeOS ACPI tablet switch driver"); 112