1
0
Fork 0
alistair23-linux/drivers/platform/x86/classmate-laptop.c

1147 lines
26 KiB
C
Raw Normal View History

treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 1 Based on 2 normalized pattern(s): 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 either version 2 of the license or at your option any later version this program is distributed in the hope that it will be useful but without any warranty without even the implied warranty of merchantability or fitness for a particular purpose see the gnu general public license for more details you should have received a copy of the gnu general public license along with this program if not write to the free software foundation inc 51 franklin street fifth floor boston ma 02110 1301 usa 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 either version 2 of the license or at your option [no]_[pad]_[ctrl] any later version this program is distributed in the hope that it will be useful but without any warranty without even the implied warranty of merchantability or fitness for a particular purpose see the gnu general public license for more details you should have received a copy of the gnu general public license along with this program if not write to the free software foundation inc 51 franklin street fifth floor boston ma 02110 1301 usa extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 176 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Jilayne Lovejoy <opensource@jilayne.com> Reviewed-by: Steve Winslow <swinslow@gmail.com> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190519154040.652910950@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-19 07:51:31 -06:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
*/
#include <linux/init.h>
#include <linux/module.h>
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 02:04:11 -06:00
#include <linux/slab.h>
#include <linux/workqueue.h>
ACPI: Clean up inclusions of ACPI header files Replace direct inclusions of <acpi/acpi.h>, <acpi/acpi_bus.h> and <acpi/acpi_drivers.h>, which are incorrect, with <linux/acpi.h> inclusions and remove some inclusions of those files that aren't necessary. First of all, <acpi/acpi.h>, <acpi/acpi_bus.h> and <acpi/acpi_drivers.h> should not be included directly from any files that are built for CONFIG_ACPI unset, because that generally leads to build warnings about undefined symbols in !CONFIG_ACPI builds. For CONFIG_ACPI set, <linux/acpi.h> includes those files and for CONFIG_ACPI unset it provides stub ACPI symbols to be used in that case. Second, there are ordering dependencies between those files that always have to be met. Namely, it is required that <acpi/acpi_bus.h> be included prior to <acpi/acpi_drivers.h> so that the acpi_pci_root declarations the latter depends on are always there. And <acpi/acpi.h> which provides basic ACPICA type declarations should always be included prior to any other ACPI headers in CONFIG_ACPI builds. That also is taken care of including <linux/acpi.h> as appropriate. Signed-off-by: Lv Zheng <lv.zheng@intel.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Matthew Garrett <mjg59@srcf.ucam.org> Cc: Tony Luck <tony.luck@intel.com> Cc: "H. Peter Anvin" <hpa@zytor.com> Acked-by: Bjorn Helgaas <bhelgaas@google.com> (drivers/pci stuff) Acked-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> (Xen stuff) Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-12-02 17:49:16 -07:00
#include <linux/acpi.h>
#include <linux/backlight.h>
#include <linux/input.h>
#include <linux/rfkill.h>
MODULE_LICENSE("GPL");
struct cmpc_accel {
int sensitivity;
int g_select;
int inputdev_state;
};
#define CMPC_ACCEL_DEV_STATE_CLOSED 0
#define CMPC_ACCEL_DEV_STATE_OPEN 1
#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
#define CMPC_ACCEL_G_SELECT_DEFAULT 0
#define CMPC_ACCEL_HID "ACCE0000"
#define CMPC_ACCEL_HID_V4 "ACCE0001"
#define CMPC_TABLET_HID "TBLT0000"
#define CMPC_IPML_HID "IPML200"
#define CMPC_KEYS_HID "FNBT0000"
/*
* Generic input device code.
*/
typedef void (*input_device_init)(struct input_dev *dev);
static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
input_device_init idev_init)
{
struct input_dev *inputdev;
int error;
inputdev = input_allocate_device();
if (!inputdev)
return -ENOMEM;
inputdev->name = name;
inputdev->dev.parent = &acpi->dev;
idev_init(inputdev);
error = input_register_device(inputdev);
if (error) {
input_free_device(inputdev);
return error;
}
dev_set_drvdata(&acpi->dev, inputdev);
return 0;
}
static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
{
struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
input_unregister_device(inputdev);
return 0;
}
/*
* Accelerometer code for Classmate V4
*/
static acpi_status cmpc_start_accel_v4(acpi_handle handle)
{
union acpi_object param[4];
struct acpi_object_list input;
acpi_status status;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0x3;
param[1].type = ACPI_TYPE_INTEGER;
param[1].integer.value = 0;
param[2].type = ACPI_TYPE_INTEGER;
param[2].integer.value = 0;
param[3].type = ACPI_TYPE_INTEGER;
param[3].integer.value = 0;
input.count = 4;
input.pointer = param;
status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
return status;
}
static acpi_status cmpc_stop_accel_v4(acpi_handle handle)
{
union acpi_object param[4];
struct acpi_object_list input;
acpi_status status;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0x4;
param[1].type = ACPI_TYPE_INTEGER;
param[1].integer.value = 0;
param[2].type = ACPI_TYPE_INTEGER;
param[2].integer.value = 0;
param[3].type = ACPI_TYPE_INTEGER;
param[3].integer.value = 0;
input.count = 4;
input.pointer = param;
status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
return status;
}
static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)
{
union acpi_object param[4];
struct acpi_object_list input;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0x02;
param[1].type = ACPI_TYPE_INTEGER;
param[1].integer.value = val;
param[2].type = ACPI_TYPE_INTEGER;
param[2].integer.value = 0;
param[3].type = ACPI_TYPE_INTEGER;
param[3].integer.value = 0;
input.count = 4;
input.pointer = param;
return acpi_evaluate_object(handle, "ACMD", &input, NULL);
}
static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)
{
union acpi_object param[4];
struct acpi_object_list input;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0x05;
param[1].type = ACPI_TYPE_INTEGER;
param[1].integer.value = val;
param[2].type = ACPI_TYPE_INTEGER;
param[2].integer.value = 0;
param[3].type = ACPI_TYPE_INTEGER;
param[3].integer.value = 0;
input.count = 4;
input.pointer = param;
return acpi_evaluate_object(handle, "ACMD", &input, NULL);
}
static acpi_status cmpc_get_accel_v4(acpi_handle handle,
int16_t *x,
int16_t *y,
int16_t *z)
{
union acpi_object param[4];
struct acpi_object_list input;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
int16_t *locs;
acpi_status status;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0x01;
param[1].type = ACPI_TYPE_INTEGER;
param[1].integer.value = 0;
param[2].type = ACPI_TYPE_INTEGER;
param[2].integer.value = 0;
param[3].type = ACPI_TYPE_INTEGER;
param[3].integer.value = 0;
input.count = 4;
input.pointer = param;
status = acpi_evaluate_object(handle, "ACMD", &input, &output);
if (ACPI_SUCCESS(status)) {
union acpi_object *obj;
obj = output.pointer;
locs = (int16_t *) obj->buffer.pointer;
*x = locs[0];
*y = locs[1];
*z = locs[2];
kfree(output.pointer);
}
return status;
}
static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
{
if (event == 0x81) {
int16_t x, y, z;
acpi_status status;
status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);
if (ACPI_SUCCESS(status)) {
struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
input_report_abs(inputdev, ABS_X, x);
input_report_abs(inputdev, ABS_Y, y);
input_report_abs(inputdev, ABS_Z, z);
input_sync(inputdev);
}
}
}
static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct acpi_device *acpi;
struct input_dev *inputdev;
struct cmpc_accel *accel;
acpi = to_acpi_device(dev);
inputdev = dev_get_drvdata(&acpi->dev);
accel = dev_get_drvdata(&inputdev->dev);
return sprintf(buf, "%d\n", accel->sensitivity);
}
static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct acpi_device *acpi;
struct input_dev *inputdev;
struct cmpc_accel *accel;
unsigned long sensitivity;
int r;
acpi = to_acpi_device(dev);
inputdev = dev_get_drvdata(&acpi->dev);
accel = dev_get_drvdata(&inputdev->dev);
r = kstrtoul(buf, 0, &sensitivity);
if (r)
return r;
/* sensitivity must be between 1 and 127 */
if (sensitivity < 1 || sensitivity > 127)
return -EINVAL;
accel->sensitivity = sensitivity;
cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);
return strnlen(buf, count);
}
static struct device_attribute cmpc_accel_sensitivity_attr_v4 = {
.attr = { .name = "sensitivity", .mode = 0660 },
.show = cmpc_accel_sensitivity_show_v4,
.store = cmpc_accel_sensitivity_store_v4
};
static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct acpi_device *acpi;
struct input_dev *inputdev;
struct cmpc_accel *accel;
acpi = to_acpi_device(dev);
inputdev = dev_get_drvdata(&acpi->dev);
accel = dev_get_drvdata(&inputdev->dev);
return sprintf(buf, "%d\n", accel->g_select);
}
static ssize_t cmpc_accel_g_select_store_v4(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct acpi_device *acpi;
struct input_dev *inputdev;
struct cmpc_accel *accel;
unsigned long g_select;
int r;
acpi = to_acpi_device(dev);
inputdev = dev_get_drvdata(&acpi->dev);
accel = dev_get_drvdata(&inputdev->dev);
r = kstrtoul(buf, 0, &g_select);
if (r)
return r;
/* 0 means 1.5g, 1 means 6g, everything else is wrong */
if (g_select != 0 && g_select != 1)
return -EINVAL;
accel->g_select = g_select;
cmpc_accel_set_g_select_v4(acpi->handle, g_select);
return strnlen(buf, count);
}
static struct device_attribute cmpc_accel_g_select_attr_v4 = {
.attr = { .name = "g_select", .mode = 0660 },
.show = cmpc_accel_g_select_show_v4,
.store = cmpc_accel_g_select_store_v4
};
static int cmpc_accel_open_v4(struct input_dev *input)
{
struct acpi_device *acpi;
struct cmpc_accel *accel;
acpi = to_acpi_device(input->dev.parent);
accel = dev_get_drvdata(&input->dev);
cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
return 0;
}
return -EIO;
}
static void cmpc_accel_close_v4(struct input_dev *input)
{
struct acpi_device *acpi;
struct cmpc_accel *accel;
acpi = to_acpi_device(input->dev.parent);
accel = dev_get_drvdata(&input->dev);
cmpc_stop_accel_v4(acpi->handle);
accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
}
static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
{
set_bit(EV_ABS, inputdev->evbit);
input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);
input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);
input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);
inputdev->open = cmpc_accel_open_v4;
inputdev->close = cmpc_accel_close_v4;
}
#ifdef CONFIG_PM_SLEEP
static int cmpc_accel_suspend_v4(struct device *dev)
{
struct input_dev *inputdev;
struct cmpc_accel *accel;
inputdev = dev_get_drvdata(dev);
accel = dev_get_drvdata(&inputdev->dev);
if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);
return 0;
}
static int cmpc_accel_resume_v4(struct device *dev)
{
struct input_dev *inputdev;
struct cmpc_accel *accel;
inputdev = dev_get_drvdata(dev);
accel = dev_get_drvdata(&inputdev->dev);
if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,
accel->sensitivity);
cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,
accel->g_select);
if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))
return -EIO;
}
return 0;
}
#endif
static int cmpc_accel_add_v4(struct acpi_device *acpi)
{
int error;
struct input_dev *inputdev;
struct cmpc_accel *accel;
accel = kmalloc(sizeof(*accel), GFP_KERNEL);
if (!accel)
return -ENOMEM;
accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
if (error)
goto failed_sensitivity;
accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
if (error)
goto failed_g_select;
error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
cmpc_accel_idev_init_v4);
if (error)
goto failed_input;
inputdev = dev_get_drvdata(&acpi->dev);
dev_set_drvdata(&inputdev->dev, accel);
return 0;
failed_input:
device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
failed_g_select:
device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
failed_sensitivity:
kfree(accel);
return error;
}
static int cmpc_accel_remove_v4(struct acpi_device *acpi)
{
device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
return cmpc_remove_acpi_notify_device(acpi);
}
static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,
cmpc_accel_resume_v4);
static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
{CMPC_ACCEL_HID_V4, 0},
{"", 0}
};
static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
.owner = THIS_MODULE,
.name = "cmpc_accel_v4",
.class = "cmpc_accel_v4",
.ids = cmpc_accel_device_ids_v4,
.ops = {
.add = cmpc_accel_add_v4,
.remove = cmpc_accel_remove_v4,
.notify = cmpc_accel_handler_v4,
},
.drv.pm = &cmpc_accel_pm,
};
/*
* Accelerometer code for Classmate versions prior to V4
*/
static acpi_status cmpc_start_accel(acpi_handle handle)
{
union acpi_object param[2];
struct acpi_object_list input;
acpi_status status;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0x3;
param[1].type = ACPI_TYPE_INTEGER;
input.count = 2;
input.pointer = param;
status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
return status;
}
static acpi_status cmpc_stop_accel(acpi_handle handle)
{
union acpi_object param[2];
struct acpi_object_list input;
acpi_status status;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0x4;
param[1].type = ACPI_TYPE_INTEGER;
input.count = 2;
input.pointer = param;
status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
return status;
}
static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
{
union acpi_object param[2];
struct acpi_object_list input;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0x02;
param[1].type = ACPI_TYPE_INTEGER;
param[1].integer.value = val;
input.count = 2;
input.pointer = param;
return acpi_evaluate_object(handle, "ACMD", &input, NULL);
}
static acpi_status cmpc_get_accel(acpi_handle handle,
unsigned char *x,
unsigned char *y,
unsigned char *z)
{
union acpi_object param[2];
struct acpi_object_list input;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
unsigned char *locs;
acpi_status status;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0x01;
param[1].type = ACPI_TYPE_INTEGER;
input.count = 2;
input.pointer = param;
status = acpi_evaluate_object(handle, "ACMD", &input, &output);
if (ACPI_SUCCESS(status)) {
union acpi_object *obj;
obj = output.pointer;
locs = obj->buffer.pointer;
*x = locs[0];
*y = locs[1];
*z = locs[2];
kfree(output.pointer);
}
return status;
}
static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
{
if (event == 0x81) {
unsigned char x, y, z;
acpi_status status;
status = cmpc_get_accel(dev->handle, &x, &y, &z);
if (ACPI_SUCCESS(status)) {
struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
input_report_abs(inputdev, ABS_X, x);
input_report_abs(inputdev, ABS_Y, y);
input_report_abs(inputdev, ABS_Z, z);
input_sync(inputdev);
}
}
}
static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct acpi_device *acpi;
struct input_dev *inputdev;
struct cmpc_accel *accel;
acpi = to_acpi_device(dev);
inputdev = dev_get_drvdata(&acpi->dev);
accel = dev_get_drvdata(&inputdev->dev);
return sprintf(buf, "%d\n", accel->sensitivity);
}
static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct acpi_device *acpi;
struct input_dev *inputdev;
struct cmpc_accel *accel;
unsigned long sensitivity;
int r;
acpi = to_acpi_device(dev);
inputdev = dev_get_drvdata(&acpi->dev);
accel = dev_get_drvdata(&inputdev->dev);
r = kstrtoul(buf, 0, &sensitivity);
if (r)
return r;
accel->sensitivity = sensitivity;
cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
return strnlen(buf, count);
}
static struct device_attribute cmpc_accel_sensitivity_attr = {
.attr = { .name = "sensitivity", .mode = 0660 },
.show = cmpc_accel_sensitivity_show,
.store = cmpc_accel_sensitivity_store
};
static int cmpc_accel_open(struct input_dev *input)
{
struct acpi_device *acpi;
acpi = to_acpi_device(input->dev.parent);
if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
return 0;
return -EIO;
}
static void cmpc_accel_close(struct input_dev *input)
{
struct acpi_device *acpi;
acpi = to_acpi_device(input->dev.parent);
cmpc_stop_accel(acpi->handle);
}
static void cmpc_accel_idev_init(struct input_dev *inputdev)
{
set_bit(EV_ABS, inputdev->evbit);
input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
inputdev->open = cmpc_accel_open;
inputdev->close = cmpc_accel_close;
}
static int cmpc_accel_add(struct acpi_device *acpi)
{
int error;
struct input_dev *inputdev;
struct cmpc_accel *accel;
accel = kmalloc(sizeof(*accel), GFP_KERNEL);
if (!accel)
return -ENOMEM;
accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
if (error)
goto failed_file;
error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
cmpc_accel_idev_init);
if (error)
goto failed_input;
inputdev = dev_get_drvdata(&acpi->dev);
dev_set_drvdata(&inputdev->dev, accel);
return 0;
failed_input:
device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
failed_file:
kfree(accel);
return error;
}
static int cmpc_accel_remove(struct acpi_device *acpi)
{
device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
return cmpc_remove_acpi_notify_device(acpi);
}
static const struct acpi_device_id cmpc_accel_device_ids[] = {
{CMPC_ACCEL_HID, 0},
{"", 0}
};
static struct acpi_driver cmpc_accel_acpi_driver = {
.owner = THIS_MODULE,
.name = "cmpc_accel",
.class = "cmpc_accel",
.ids = cmpc_accel_device_ids,
.ops = {
.add = cmpc_accel_add,
.remove = cmpc_accel_remove,
.notify = cmpc_accel_handler,
}
};
/*
* Tablet mode code.
*/
static acpi_status cmpc_get_tablet(acpi_handle handle,
unsigned long long *value)
{
union acpi_object param;
struct acpi_object_list input;
unsigned long long output;
acpi_status status;
param.type = ACPI_TYPE_INTEGER;
param.integer.value = 0x01;
input.count = 1;
input.pointer = &param;
status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
if (ACPI_SUCCESS(status))
*value = output;
return status;
}
static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
{
unsigned long long val = 0;
struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
if (event == 0x81) {
if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
input_sync(inputdev);
}
}
}
static void cmpc_tablet_idev_init(struct input_dev *inputdev)
{
unsigned long long val = 0;
struct acpi_device *acpi;
set_bit(EV_SW, inputdev->evbit);
set_bit(SW_TABLET_MODE, inputdev->swbit);
acpi = to_acpi_device(inputdev->dev.parent);
if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
input_sync(inputdev);
}
}
static int cmpc_tablet_add(struct acpi_device *acpi)
{
return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
cmpc_tablet_idev_init);
}
static int cmpc_tablet_remove(struct acpi_device *acpi)
{
return cmpc_remove_acpi_notify_device(acpi);
}
#ifdef CONFIG_PM_SLEEP
static int cmpc_tablet_resume(struct device *dev)
{
struct input_dev *inputdev = dev_get_drvdata(dev);
unsigned long long val = 0;
if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
input_sync(inputdev);
}
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);
static const struct acpi_device_id cmpc_tablet_device_ids[] = {
{CMPC_TABLET_HID, 0},
{"", 0}
};
static struct acpi_driver cmpc_tablet_acpi_driver = {
.owner = THIS_MODULE,
.name = "cmpc_tablet",
.class = "cmpc_tablet",
.ids = cmpc_tablet_device_ids,
.ops = {
.add = cmpc_tablet_add,
.remove = cmpc_tablet_remove,
.notify = cmpc_tablet_handler,
},
.drv.pm = &cmpc_tablet_pm,
};
/*
* Backlight code.
*/
static acpi_status cmpc_get_brightness(acpi_handle handle,
unsigned long long *value)
{
union acpi_object param;
struct acpi_object_list input;
unsigned long long output;
acpi_status status;
param.type = ACPI_TYPE_INTEGER;
param.integer.value = 0xC0;
input.count = 1;
input.pointer = &param;
status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
if (ACPI_SUCCESS(status))
*value = output;
return status;
}
static acpi_status cmpc_set_brightness(acpi_handle handle,
unsigned long long value)
{
union acpi_object param[2];
struct acpi_object_list input;
acpi_status status;
unsigned long long output;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0xC0;
param[1].type = ACPI_TYPE_INTEGER;
param[1].integer.value = value;
input.count = 2;
input.pointer = param;
status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
return status;
}
static int cmpc_bl_get_brightness(struct backlight_device *bd)
{
acpi_status status;
acpi_handle handle;
unsigned long long brightness;
handle = bl_get_data(bd);
status = cmpc_get_brightness(handle, &brightness);
if (ACPI_SUCCESS(status))
return brightness;
else
return -1;
}
static int cmpc_bl_update_status(struct backlight_device *bd)
{
acpi_status status;
acpi_handle handle;
handle = bl_get_data(bd);
status = cmpc_set_brightness(handle, bd->props.brightness);
if (ACPI_SUCCESS(status))
return 0;
else
return -1;
}
static const struct backlight_ops cmpc_bl_ops = {
.get_brightness = cmpc_bl_get_brightness,
.update_status = cmpc_bl_update_status
};
/*
* RFKILL code.
*/
static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
unsigned long long *value)
{
union acpi_object param;
struct acpi_object_list input;
unsigned long long output;
acpi_status status;
param.type = ACPI_TYPE_INTEGER;
param.integer.value = 0xC1;
input.count = 1;
input.pointer = &param;
status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
if (ACPI_SUCCESS(status))
*value = output;
return status;
}
static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
unsigned long long value)
{
union acpi_object param[2];
struct acpi_object_list input;
acpi_status status;
unsigned long long output;
param[0].type = ACPI_TYPE_INTEGER;
param[0].integer.value = 0xC1;
param[1].type = ACPI_TYPE_INTEGER;
param[1].integer.value = value;
input.count = 2;
input.pointer = param;
status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
return status;
}
static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
{
acpi_status status;
acpi_handle handle;
unsigned long long state;
bool blocked;
handle = data;
status = cmpc_get_rfkill_wlan(handle, &state);
if (ACPI_SUCCESS(status)) {
blocked = state & 1 ? false : true;
rfkill_set_sw_state(rfkill, blocked);
}
}
static int cmpc_rfkill_block(void *data, bool blocked)
{
acpi_status status;
acpi_handle handle;
unsigned long long state;
bool is_blocked;
handle = data;
status = cmpc_get_rfkill_wlan(handle, &state);
if (ACPI_FAILURE(status))
return -ENODEV;
/* Check if we really need to call cmpc_set_rfkill_wlan */
is_blocked = state & 1 ? false : true;
if (is_blocked != blocked) {
state = blocked ? 0 : 1;
status = cmpc_set_rfkill_wlan(handle, state);
if (ACPI_FAILURE(status))
return -ENODEV;
}
return 0;
}
static const struct rfkill_ops cmpc_rfkill_ops = {
.query = cmpc_rfkill_query,
.set_block = cmpc_rfkill_block,
};
/*
* Common backlight and rfkill code.
*/
struct ipml200_dev {
struct backlight_device *bd;
struct rfkill *rf;
};
static int cmpc_ipml_add(struct acpi_device *acpi)
{
int retval;
struct ipml200_dev *ipml;
struct backlight_properties props;
ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);
if (ipml == NULL)
return -ENOMEM;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = 7;
ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
acpi->handle, &cmpc_bl_ops,
&props);
if (IS_ERR(ipml->bd)) {
retval = PTR_ERR(ipml->bd);
goto out_bd;
}
ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
&cmpc_rfkill_ops, acpi->handle);
/*
* If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
* This is OK, however, since all other uses of the device will not
* derefence it.
*/
if (ipml->rf) {
retval = rfkill_register(ipml->rf);
if (retval) {
rfkill_destroy(ipml->rf);
ipml->rf = NULL;
}
}
dev_set_drvdata(&acpi->dev, ipml);
return 0;
out_bd:
kfree(ipml);
return retval;
}
static int cmpc_ipml_remove(struct acpi_device *acpi)
{
struct ipml200_dev *ipml;
ipml = dev_get_drvdata(&acpi->dev);
backlight_device_unregister(ipml->bd);
if (ipml->rf) {
rfkill_unregister(ipml->rf);
rfkill_destroy(ipml->rf);
}
kfree(ipml);
return 0;
}
static const struct acpi_device_id cmpc_ipml_device_ids[] = {
{CMPC_IPML_HID, 0},
{"", 0}
};
static struct acpi_driver cmpc_ipml_acpi_driver = {
.owner = THIS_MODULE,
.name = "cmpc",
.class = "cmpc",
.ids = cmpc_ipml_device_ids,
.ops = {
.add = cmpc_ipml_add,
.remove = cmpc_ipml_remove
}
};
/*
* Extra keys code.
*/
static int cmpc_keys_codes[] = {
KEY_UNKNOWN,
KEY_WLAN,
KEY_SWITCHVIDEOMODE,
KEY_BRIGHTNESSDOWN,
KEY_BRIGHTNESSUP,
KEY_VENDOR,
KEY_UNKNOWN,
KEY_CAMERA,
KEY_BACK,
KEY_FORWARD,
KEY_MAX
};
static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
{
struct input_dev *inputdev;
int code = KEY_MAX;
if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
code = cmpc_keys_codes[event & 0x0F];
inputdev = dev_get_drvdata(&dev->dev);
input_report_key(inputdev, code, !(event & 0x10));
input_sync(inputdev);
}
static void cmpc_keys_idev_init(struct input_dev *inputdev)
{
int i;
set_bit(EV_KEY, inputdev->evbit);
for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
set_bit(cmpc_keys_codes[i], inputdev->keybit);
}
static int cmpc_keys_add(struct acpi_device *acpi)
{
return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
cmpc_keys_idev_init);
}
static int cmpc_keys_remove(struct acpi_device *acpi)
{
return cmpc_remove_acpi_notify_device(acpi);
}
static const struct acpi_device_id cmpc_keys_device_ids[] = {
{CMPC_KEYS_HID, 0},
{"", 0}
};
static struct acpi_driver cmpc_keys_acpi_driver = {
.owner = THIS_MODULE,
.name = "cmpc_keys",
.class = "cmpc_keys",
.ids = cmpc_keys_device_ids,
.ops = {
.add = cmpc_keys_add,
.remove = cmpc_keys_remove,
.notify = cmpc_keys_handler,
}
};
/*
* General init/exit code.
*/
static int cmpc_init(void)
{
int r;
r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
if (r)
goto failed_keys;
r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
if (r)
goto failed_bl;
r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
if (r)
goto failed_tablet;
r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
if (r)
goto failed_accel;
r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
if (r)
goto failed_accel_v4;
return r;
failed_accel_v4:
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
failed_accel:
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
failed_tablet:
acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
failed_bl:
acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
failed_keys:
return r;
}
static void cmpc_exit(void)
{
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);
acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
}
module_init(cmpc_init);
module_exit(cmpc_exit);
static const struct acpi_device_id cmpc_device_ids[] = {
{CMPC_ACCEL_HID, 0},
{CMPC_ACCEL_HID_V4, 0},
{CMPC_TABLET_HID, 0},
{CMPC_IPML_HID, 0},
{CMPC_KEYS_HID, 0},
{"", 0}
};
MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);