forked from Minki/linux
51fac8388a
The second argument of ACPI driver .remove() operation is only used by the ACPI processor driver and the value passed to that driver through it is always available from the given struct acpi_device object's removal_type field. For this reason, the second ACPI driver .remove() argument is in fact useless, so drop it. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Jiang Liu <jiang.liu@huawei.com> Acked-by: Toshi Kani <toshi.kani@hp.com> Acked-by: Yinghai Lu <yinghai@kernel.org>
194 lines
4.7 KiB
C
194 lines
4.7 KiB
C
/*
|
|
* ACPI driver for Topstar notebooks (hotkeys support only)
|
|
*
|
|
* Copyright (c) 2009 Herton Ronaldo Krzesinski <herton@mandriva.com.br>
|
|
*
|
|
* Implementation inspired by existing x86 platform drivers, in special
|
|
* asus/eepc/fujitsu-laptop, thanks to their authors
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/acpi.h>
|
|
#include <linux/input.h>
|
|
#include <linux/input/sparse-keymap.h>
|
|
|
|
#define ACPI_TOPSTAR_CLASS "topstar"
|
|
|
|
struct topstar_hkey {
|
|
struct input_dev *inputdev;
|
|
};
|
|
|
|
static const struct key_entry topstar_keymap[] = {
|
|
{ KE_KEY, 0x80, { KEY_BRIGHTNESSUP } },
|
|
{ KE_KEY, 0x81, { KEY_BRIGHTNESSDOWN } },
|
|
{ KE_KEY, 0x83, { KEY_VOLUMEUP } },
|
|
{ KE_KEY, 0x84, { KEY_VOLUMEDOWN } },
|
|
{ KE_KEY, 0x85, { KEY_MUTE } },
|
|
{ KE_KEY, 0x86, { KEY_SWITCHVIDEOMODE } },
|
|
{ KE_KEY, 0x87, { KEY_F13 } }, /* touchpad enable/disable key */
|
|
{ KE_KEY, 0x88, { KEY_WLAN } },
|
|
{ KE_KEY, 0x8a, { KEY_WWW } },
|
|
{ KE_KEY, 0x8b, { KEY_MAIL } },
|
|
{ KE_KEY, 0x8c, { KEY_MEDIA } },
|
|
|
|
/* Known non hotkey events don't handled or that we don't care yet */
|
|
{ KE_IGNORE, 0x82, }, /* backlight event */
|
|
{ KE_IGNORE, 0x8e, },
|
|
{ KE_IGNORE, 0x8f, },
|
|
{ KE_IGNORE, 0x90, },
|
|
|
|
/*
|
|
* 'G key' generate two event codes, convert to only
|
|
* one event/key code for now, consider replacing by
|
|
* a switch (3G switch - SW_3G?)
|
|
*/
|
|
{ KE_KEY, 0x96, { KEY_F14 } },
|
|
{ KE_KEY, 0x97, { KEY_F14 } },
|
|
|
|
{ KE_END, 0 }
|
|
};
|
|
|
|
static void acpi_topstar_notify(struct acpi_device *device, u32 event)
|
|
{
|
|
static bool dup_evnt[2];
|
|
bool *dup;
|
|
struct topstar_hkey *hkey = acpi_driver_data(device);
|
|
|
|
/* 0x83 and 0x84 key events comes duplicated... */
|
|
if (event == 0x83 || event == 0x84) {
|
|
dup = &dup_evnt[event - 0x83];
|
|
if (*dup) {
|
|
*dup = false;
|
|
return;
|
|
}
|
|
*dup = true;
|
|
}
|
|
|
|
if (!sparse_keymap_report_event(hkey->inputdev, event, 1, true))
|
|
pr_info("unknown event = 0x%02x\n", event);
|
|
}
|
|
|
|
static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)
|
|
{
|
|
acpi_status status;
|
|
union acpi_object fncx_params[1] = {
|
|
{ .type = ACPI_TYPE_INTEGER }
|
|
};
|
|
struct acpi_object_list fncx_arg_list = { 1, &fncx_params[0] };
|
|
|
|
fncx_params[0].integer.value = state ? 0x86 : 0x87;
|
|
status = acpi_evaluate_object(device->handle, "FNCX", &fncx_arg_list, NULL);
|
|
if (ACPI_FAILURE(status)) {
|
|
pr_err("Unable to switch FNCX notifications\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
|
|
{
|
|
struct input_dev *input;
|
|
int error;
|
|
|
|
input = input_allocate_device();
|
|
if (!input) {
|
|
pr_err("Unable to allocate input device\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
input->name = "Topstar Laptop extra buttons";
|
|
input->phys = "topstar/input0";
|
|
input->id.bustype = BUS_HOST;
|
|
|
|
error = sparse_keymap_setup(input, topstar_keymap, NULL);
|
|
if (error) {
|
|
pr_err("Unable to setup input device keymap\n");
|
|
goto err_free_dev;
|
|
}
|
|
|
|
error = input_register_device(input);
|
|
if (error) {
|
|
pr_err("Unable to register input device\n");
|
|
goto err_free_keymap;
|
|
}
|
|
|
|
hkey->inputdev = input;
|
|
return 0;
|
|
|
|
err_free_keymap:
|
|
sparse_keymap_free(input);
|
|
err_free_dev:
|
|
input_free_device(input);
|
|
return error;
|
|
}
|
|
|
|
static int acpi_topstar_add(struct acpi_device *device)
|
|
{
|
|
struct topstar_hkey *tps_hkey;
|
|
|
|
tps_hkey = kzalloc(sizeof(struct topstar_hkey), GFP_KERNEL);
|
|
if (!tps_hkey)
|
|
return -ENOMEM;
|
|
|
|
strcpy(acpi_device_name(device), "Topstar TPSACPI");
|
|
strcpy(acpi_device_class(device), ACPI_TOPSTAR_CLASS);
|
|
|
|
if (acpi_topstar_fncx_switch(device, true))
|
|
goto add_err;
|
|
|
|
if (acpi_topstar_init_hkey(tps_hkey))
|
|
goto add_err;
|
|
|
|
device->driver_data = tps_hkey;
|
|
return 0;
|
|
|
|
add_err:
|
|
kfree(tps_hkey);
|
|
return -ENODEV;
|
|
}
|
|
|
|
static int acpi_topstar_remove(struct acpi_device *device)
|
|
{
|
|
struct topstar_hkey *tps_hkey = acpi_driver_data(device);
|
|
|
|
acpi_topstar_fncx_switch(device, false);
|
|
|
|
sparse_keymap_free(tps_hkey->inputdev);
|
|
input_unregister_device(tps_hkey->inputdev);
|
|
kfree(tps_hkey);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct acpi_device_id topstar_device_ids[] = {
|
|
{ "TPSACPI01", 0 },
|
|
{ "", 0 },
|
|
};
|
|
MODULE_DEVICE_TABLE(acpi, topstar_device_ids);
|
|
|
|
static struct acpi_driver acpi_topstar_driver = {
|
|
.name = "Topstar laptop ACPI driver",
|
|
.class = ACPI_TOPSTAR_CLASS,
|
|
.ids = topstar_device_ids,
|
|
.ops = {
|
|
.add = acpi_topstar_add,
|
|
.remove = acpi_topstar_remove,
|
|
.notify = acpi_topstar_notify,
|
|
},
|
|
};
|
|
module_acpi_driver(acpi_topstar_driver);
|
|
|
|
MODULE_AUTHOR("Herton Ronaldo Krzesinski");
|
|
MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver");
|
|
MODULE_LICENSE("GPL");
|