mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
121 lines
2.9 KiB
121 lines
2.9 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
// Driver to detect Tablet Mode for ChromeOS convertible. |
|
// |
|
// Copyright (C) 2017 Google, Inc. |
|
// Author: Gwendal Grignou <[email protected]> |
|
// |
|
// On Chromebook using ACPI, this device listens for notification |
|
// from GOOG0006 and issue method TBMC to retrieve the status. |
|
// |
|
// GOOG0006 issues the notification when it receives EC_HOST_EVENT_MODE_CHANGE |
|
// from the EC. |
|
// Method TBMC reads EC_ACPI_MEM_DEVICE_ORIENTATION byte from the shared |
|
// memory region. |
|
|
|
#include <linux/acpi.h> |
|
#include <linux/input.h> |
|
#include <linux/io.h> |
|
#include <linux/module.h> |
|
#include <linux/printk.h> |
|
|
|
#define DRV_NAME "chromeos_tbmc" |
|
#define ACPI_DRV_NAME "GOOG0006" |
|
|
|
static int chromeos_tbmc_query_switch(struct acpi_device *adev, |
|
struct input_dev *idev) |
|
{ |
|
unsigned long long state; |
|
acpi_status status; |
|
|
|
status = acpi_evaluate_integer(adev->handle, "TBMC", NULL, &state); |
|
if (ACPI_FAILURE(status)) |
|
return -ENODEV; |
|
|
|
/* input layer checks if event is redundant */ |
|
input_report_switch(idev, SW_TABLET_MODE, state); |
|
input_sync(idev); |
|
|
|
return 0; |
|
} |
|
|
|
static __maybe_unused int chromeos_tbmc_resume(struct device *dev) |
|
{ |
|
struct acpi_device *adev = to_acpi_device(dev); |
|
|
|
return chromeos_tbmc_query_switch(adev, adev->driver_data); |
|
} |
|
|
|
static void chromeos_tbmc_notify(struct acpi_device *adev, u32 event) |
|
{ |
|
acpi_pm_wakeup_event(&adev->dev); |
|
switch (event) { |
|
case 0x80: |
|
chromeos_tbmc_query_switch(adev, adev->driver_data); |
|
break; |
|
default: |
|
dev_err(&adev->dev, "Unexpected event: 0x%08X\n", event); |
|
} |
|
} |
|
|
|
static int chromeos_tbmc_open(struct input_dev *idev) |
|
{ |
|
struct acpi_device *adev = input_get_drvdata(idev); |
|
|
|
return chromeos_tbmc_query_switch(adev, idev); |
|
} |
|
|
|
static int chromeos_tbmc_add(struct acpi_device *adev) |
|
{ |
|
struct input_dev *idev; |
|
struct device *dev = &adev->dev; |
|
int ret; |
|
|
|
idev = devm_input_allocate_device(dev); |
|
if (!idev) |
|
return -ENOMEM; |
|
|
|
idev->name = "Tablet Mode Switch"; |
|
idev->phys = acpi_device_hid(adev); |
|
|
|
idev->id.bustype = BUS_HOST; |
|
idev->id.version = 1; |
|
idev->id.product = 0; |
|
idev->open = chromeos_tbmc_open; |
|
|
|
input_set_drvdata(idev, adev); |
|
adev->driver_data = idev; |
|
|
|
input_set_capability(idev, EV_SW, SW_TABLET_MODE); |
|
ret = input_register_device(idev); |
|
if (ret) { |
|
dev_err(dev, "cannot register input device\n"); |
|
return ret; |
|
} |
|
device_init_wakeup(dev, true); |
|
return 0; |
|
} |
|
|
|
static const struct acpi_device_id chromeos_tbmc_acpi_device_ids[] = { |
|
{ ACPI_DRV_NAME, 0 }, |
|
{ } |
|
}; |
|
MODULE_DEVICE_TABLE(acpi, chromeos_tbmc_acpi_device_ids); |
|
|
|
static SIMPLE_DEV_PM_OPS(chromeos_tbmc_pm_ops, NULL, |
|
chromeos_tbmc_resume); |
|
|
|
static struct acpi_driver chromeos_tbmc_driver = { |
|
.name = DRV_NAME, |
|
.class = DRV_NAME, |
|
.ids = chromeos_tbmc_acpi_device_ids, |
|
.ops = { |
|
.add = chromeos_tbmc_add, |
|
.notify = chromeos_tbmc_notify, |
|
}, |
|
.drv.pm = &chromeos_tbmc_pm_ops, |
|
}; |
|
|
|
module_acpi_driver(chromeos_tbmc_driver); |
|
|
|
MODULE_LICENSE("GPL v2"); |
|
MODULE_DESCRIPTION("ChromeOS ACPI tablet switch driver");
|
|
|