aboutsummaryrefslogtreecommitdiff
path: root/drivers/platform/x86
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/platform/x86')
-rw-r--r--drivers/platform/x86/Kconfig112
-rw-r--r--drivers/platform/x86/Makefile7
-rw-r--r--drivers/platform/x86/acer-wmi.c260
-rw-r--r--drivers/platform/x86/acerhdf.c9
-rw-r--r--drivers/platform/x86/alienware-wmi.c622
-rw-r--r--drivers/platform/x86/amilo-rfkill.c11
-rw-r--r--drivers/platform/x86/apple-gmux.c447
-rw-r--r--drivers/platform/x86/asus-laptop.c128
-rw-r--r--drivers/platform/x86/asus-nb-wmi.c221
-rw-r--r--drivers/platform/x86/asus-wmi.c227
-rw-r--r--drivers/platform/x86/asus-wmi.h11
-rw-r--r--drivers/platform/x86/classmate-laptop.c445
-rw-r--r--drivers/platform/x86/compal-laptop.c138
-rw-r--r--drivers/platform/x86/dell-laptop.c367
-rw-r--r--drivers/platform/x86/dell-smo8800.c233
-rw-r--r--drivers/platform/x86/dell-wmi-aio.c54
-rw-r--r--drivers/platform/x86/dell-wmi.c8
-rw-r--r--drivers/platform/x86/eeepc-laptop.c93
-rw-r--r--drivers/platform/x86/eeepc-wmi.c31
-rw-r--r--drivers/platform/x86/fujitsu-laptop.c68
-rw-r--r--drivers/platform/x86/fujitsu-tablet.c102
-rw-r--r--drivers/platform/x86/hdaps.c8
-rw-r--r--drivers/platform/x86/hp-wireless.c132
-rw-r--r--drivers/platform/x86/hp-wmi.c226
-rw-r--r--drivers/platform/x86/hp_accel.c56
-rw-r--r--drivers/platform/x86/ibm_rtl.c2
-rw-r--r--drivers/platform/x86/ideapad-laptop.c453
-rw-r--r--drivers/platform/x86/intel-rst.c166
-rw-r--r--drivers/platform/x86/intel-smartconnect.c60
-rw-r--r--drivers/platform/x86/intel_ips.c52
-rw-r--r--drivers/platform/x86/intel_menlow.c14
-rw-r--r--drivers/platform/x86/intel_mid_powerbtn.c11
-rw-r--r--drivers/platform/x86/intel_mid_thermal.c28
-rw-r--r--drivers/platform/x86/intel_oaktrail.c9
-rw-r--r--drivers/platform/x86/intel_pmic_gpio.c12
-rw-r--r--drivers/platform/x86/intel_scu_ipc.c122
-rw-r--r--drivers/platform/x86/msi-laptop.c407
-rw-r--r--drivers/platform/x86/msi-wmi.c224
-rw-r--r--drivers/platform/x86/mxm-wmi.c4
-rw-r--r--drivers/platform/x86/panasonic-laptop.c68
-rw-r--r--drivers/platform/x86/pvpanic.c124
-rw-r--r--drivers/platform/x86/samsung-laptop.c91
-rw-r--r--drivers/platform/x86/samsung-q10.c76
-rw-r--r--drivers/platform/x86/sony-laptop.c981
-rw-r--r--drivers/platform/x86/tc1100-wmi.c8
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c553
-rw-r--r--drivers/platform/x86/topstar-laptop.c36
-rw-r--r--drivers/platform/x86/toshiba_acpi.c770
-rw-r--r--drivers/platform/x86/toshiba_bluetooth.c44
-rw-r--r--drivers/platform/x86/wmi.c58
-rw-r--r--drivers/platform/x86/xo1-rfkill.c9
-rw-r--r--drivers/platform/x86/xo15-ebook.c29
52 files changed, 6715 insertions, 1712 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 2a262f5c5c0..172f26ce59a 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -27,8 +27,6 @@ config ACER_WMI
depends on ACPI_WMI
select INPUT_SPARSEKMAP
# Acer WMI depends on ACPI_VIDEO when ACPI is enabled
- # but for select to work, need to select ACPI_VIDEO's dependencies, ick
- select VIDEO_OUTPUT_CONTROL if ACPI
select ACPI_VIDEO if ACPI
---help---
This is a driver for newer Acer (and Wistron) laptops. It adds
@@ -55,6 +53,18 @@ config ACERHDF
If you have an Acer Aspire One netbook, say Y or M
here.
+config ALIENWARE_WMI
+ tristate "Alienware Special feature control"
+ depends on ACPI
+ depends on LEDS_CLASS
+ depends on NEW_LEDS
+ depends on ACPI_WMI
+ ---help---
+ This is a driver for controlling Alienware BIOS driven
+ features. It exposes an interface for controlling the AlienFX
+ zones on Alienware machines that don't contain a dedicated AlienFX
+ USB MCU such as the X51 and X51-R2.
+
config ASUS_LAPTOP
tristate "Asus Laptop Extras"
depends on ACPI
@@ -80,10 +90,9 @@ config ASUS_LAPTOP
If you have an ACPI-compatible ASUS laptop, say Y or M here.
config DELL_LAPTOP
- tristate "Dell Laptop Extras (EXPERIMENTAL)"
+ tristate "Dell Laptop Extras"
depends on X86
depends on DCDBAS
- depends on EXPERIMENTAL
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL || RFKILL = n
depends on SERIO_I8042
@@ -93,7 +102,7 @@ config DELL_LAPTOP
default n
---help---
This driver adds support for rfkill and backlight control to Dell
- laptops.
+ laptops (except for some models covered by the Compal driver).
config DELL_WMI
tristate "Dell WMI extras"
@@ -118,6 +127,16 @@ config DELL_WMI_AIO
To compile this driver as a module, choose M here: the module will
be called dell-wmi-aio.
+config DELL_SMO8800
+ tristate "Dell Latitude freefall driver (ACPI SMO8800/SMO8810)"
+ depends on ACPI
+ ---help---
+ Say Y here if you want to support SMO8800/SMO8810 freefall device
+ on Dell Latitude laptops.
+
+ To compile this driver as a module, choose M here: the module will
+ be called dell-smo8800.
+
config FUJITSU_LAPTOP
tristate "Fujitsu Laptop Extras"
@@ -166,14 +185,14 @@ config FUJITSU_TABLET
config AMILO_RFKILL
tristate "Fujitsu-Siemens Amilo rfkill support"
depends on RFKILL
+ depends on SERIO_I8042
---help---
This is a driver for enabling wifi on some Fujitsu-Siemens Amilo
laptops.
config TC1100_WMI
- tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)"
+ tristate "HP Compaq TC1100 Tablet WMI Extras"
depends on !X86_64
- depends on EXPERIMENTAL
depends on ACPI
depends on ACPI_WMI
---help---
@@ -198,6 +217,17 @@ config HP_ACCEL
To compile this driver as a module, choose M here: the module will
be called hp_accel.
+config HP_WIRELESS
+ tristate "HP wireless button"
+ depends on ACPI
+ depends on INPUT
+ help
+ This driver provides supports for new HP wireless button for Windows 8.
+ On such systems the driver should load automatically (via ACPI alias).
+
+ To compile this driver as a module, choose M here: the module will
+ be called hp-wireless.
+
config HP_WMI
tristate "HP WMI extras"
depends on ACPI_WMI
@@ -245,23 +275,21 @@ config PANASONIC_LAPTOP
R2, R3, R5, T2, W2 and Y2 series), say Y.
config COMPAL_LAPTOP
- tristate "Compal Laptop Extras"
+ tristate "Compal (and others) Laptop Extras"
depends on ACPI
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL
depends on HWMON
depends on POWER_SUPPLY
---help---
- This is a driver for laptops built by Compal:
+ This is a driver for laptops built by Compal, and some models by
+ other brands (e.g. Dell, Toshiba).
- Compal FL90/IFL90
- Compal FL91/IFL91
- Compal FL92/JFL92
- Compal FT00/IFT00
-
- It adds support for Bluetooth, WLAN and LCD brightness control.
+ It adds support for rfkill, Bluetooth, WLAN and LCD brightness
+ control.
- If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here.
+ For a (possibly incomplete) list of supported laptops, please refer
+ to: Documentation/platform/x86-laptop-drivers.txt
config SONY_LAPTOP
tristate "Sony Laptop Extras"
@@ -289,9 +317,12 @@ config IDEAPAD_LAPTOP
tristate "Lenovo IdeaPad Laptop Extras"
depends on ACPI
depends on RFKILL && INPUT
+ depends on SERIO_I8042
+ depends on BACKLIGHT_CLASS_DEVICE
select INPUT_SPARSEKMAP
help
- This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
+ This is a driver for Lenovo IdeaPad netbooks contains drivers for
+ rfkill switch, hotkey, fan control and backlight control.
config THINKPAD_ACPI
tristate "ThinkPad ACPI Laptop Extras"
@@ -491,6 +522,7 @@ config ASUS_WMI
depends on BACKLIGHT_CLASS_DEVICE
depends on RFKILL || RFKILL = n
depends on HOTPLUG_PCI
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
select INPUT_SPARSEKMAP
select LEDS_CLASS
select NEW_LEDS
@@ -579,6 +611,7 @@ config ACPI_TOSHIBA
depends on BACKLIGHT_CLASS_DEVICE
depends on INPUT
depends on RFKILL || RFKILL = n
+ depends on SERIO_I8042 || SERIO_I8042 = n
select INPUT_POLLDEV
select INPUT_SPARSEKMAP
---help---
@@ -699,7 +732,7 @@ config IBM_RTL
config XO1_RFKILL
tristate "OLPC XO-1 software RF kill switch"
- depends on OLPC
+ depends on OLPC || COMPILE_TEST
depends on RFKILL
---help---
Support for enabling/disabling the WLAN interface on the OLPC XO-1
@@ -707,6 +740,7 @@ config XO1_RFKILL
config XO15_EBOOK
tristate "OLPC XO-1.5 ebook switch"
+ depends on OLPC || COMPILE_TEST
depends on ACPI && INPUT
---help---
Support for the ebook switch on the OLPC XO-1.5 laptop.
@@ -718,6 +752,7 @@ config SAMSUNG_LAPTOP
tristate "Samsung Laptop driver"
depends on X86
depends on RFKILL || RFKILL = n
+ depends on ACPI_VIDEO || ACPI_VIDEO = n
depends on BACKLIGHT_CLASS_DEVICE
select LEDS_CLASS
select NEW_LEDS
@@ -750,7 +785,7 @@ config INTEL_OAKTRAIL
config SAMSUNG_Q10
tristate "Samsung Q10 Extras"
- depends on SERIO_I8042
+ depends on ACPI
select BACKLIGHT_CLASS_DEVICE
---help---
This driver provides support for backlight control on Samsung Q10
@@ -758,12 +793,49 @@ config SAMSUNG_Q10
config APPLE_GMUX
tristate "Apple Gmux Driver"
+ depends on ACPI
depends on PNP
- select BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE
+ depends on ACPI_VIDEO=n || ACPI_VIDEO
---help---
This driver provides support for the gmux device found on many
Apple laptops, which controls the display mux for the hybrid
graphics as well as the backlight. Currently only backlight
control is supported by the driver.
+config INTEL_RST
+ tristate "Intel Rapid Start Technology Driver"
+ depends on ACPI
+ ---help---
+ This driver provides support for modifying paramaters on systems
+ equipped with Intel's Rapid Start Technology. When put in an ACPI
+ sleep state, these devices will wake after either a configured
+ timeout or when the system battery reaches a critical state,
+ automatically copying memory contents to disk. On resume, the
+ firmware will copy the memory contents back to RAM and resume the OS
+ as usual.
+
+config INTEL_SMARTCONNECT
+ tristate "Intel Smart Connect disabling driver"
+ depends on ACPI
+ ---help---
+ Intel Smart Connect is a technology intended to permit devices to
+ update state by resuming for a short period of time at regular
+ intervals. If a user enables this functionality under Windows and
+ then reboots into Linux, the system may remain configured to resume
+ on suspend. In the absence of any userspace to support it, the system
+ will then remain awake until something triggers another suspend.
+
+ This driver checks to determine whether the device has Intel Smart
+ Connect enabled, and if so disables it.
+
+config PVPANIC
+ tristate "pvpanic device support"
+ depends on ACPI
+ ---help---
+ This driver provides support for the pvpanic device. pvpanic is
+ a paravirtualized device provided by QEMU; it lets a virtual machine
+ (guest) communicate panic events to the host.
+
endif # X86_PLATFORM_DEVICES
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index bf7e4f935b1..c4ca428fd3d 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -13,9 +13,11 @@ obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
+obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_ACERHDF) += acerhdf.o
obj-$(CONFIG_HP_ACCEL) += hp_accel.o
+obj-$(CONFIG_HP_WIRELESS) += hp-wireless.o
obj-$(CONFIG_HP_WMI) += hp-wmi.o
obj-$(CONFIG_AMILO_RFKILL) += amilo-rfkill.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
@@ -50,3 +52,8 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o
+obj-$(CONFIG_INTEL_RST) += intel-rst.o
+obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o
+
+obj-$(CONFIG_PVPANIC) += pvpanic.o
+obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index ce875dc365e..bbf78b2d6d9 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -41,8 +41,6 @@
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
-
-#include <acpi/acpi_drivers.h>
#include <acpi/video.h>
MODULE_AUTHOR("Carlos Corbacho");
@@ -95,6 +93,7 @@ MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
enum acer_wmi_event_ids {
WMID_HOTKEY_EVENT = 0x1,
+ WMID_ACCEL_EVENT = 0x5,
};
static const struct key_entry acer_wmi_keymap[] = {
@@ -124,12 +123,16 @@ static const struct key_entry acer_wmi_keymap[] = {
{KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} },
{KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */
{KE_IGNORE, 0x81, {KEY_SLEEP} },
- {KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad On/Off */
+ {KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad Toggle */
+ {KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} },
+ {KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },
{KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} },
+ {KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} },
{KE_END, 0}
};
static struct input_dev *acer_wmi_input_dev;
+static struct input_dev *acer_wmi_accel_dev;
struct event_return_value {
u8 function;
@@ -145,6 +148,7 @@ struct event_return_value {
#define ACER_WMID3_GDS_THREEG (1<<6) /* 3G */
#define ACER_WMID3_GDS_WIMAX (1<<7) /* WiMAX */
#define ACER_WMID3_GDS_BLUETOOTH (1<<11) /* BT */
+#define ACER_WMID3_GDS_TOUCHPAD (1<<1) /* Touchpad */
struct lm_input_params {
u8 function_num; /* Function Number */
@@ -200,6 +204,7 @@ struct hotkey_function_type_aa {
#define ACER_CAP_BLUETOOTH (1<<2)
#define ACER_CAP_BRIGHTNESS (1<<3)
#define ACER_CAP_THREEG (1<<4)
+#define ACER_CAP_ACCEL (1<<5)
#define ACER_CAP_ANY (0xFFFFFFFF)
/*
@@ -332,7 +337,7 @@ static struct quirk_entry quirk_lenovo_ideapad_s205 = {
};
/* The Aspire One has a dummy ACPI-WMI interface - disable it */
-static struct dmi_system_id __devinitdata acer_blacklist[] = {
+static struct dmi_system_id acer_blacklist[] = {
{
.ident = "Acer Aspire One (SSD)",
.matches = {
@@ -504,6 +509,24 @@ static struct dmi_system_id acer_quirks[] = {
},
.driver_data = &quirk_fujitsu_amilo_li_1718,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Lenovo Ideapad S205-10382JG",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "10382JG"),
+ },
+ .driver_data = &quirk_lenovo_ideapad_s205,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Lenovo Ideapad S205-1038DPG",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1038DPG"),
+ },
+ .driver_data = &quirk_lenovo_ideapad_s205,
+ },
{}
};
@@ -547,6 +570,14 @@ static const struct dmi_system_id video_vendor_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5750"),
},
},
+ {
+ .callback = video_set_backlight_video_vendor,
+ .ident = "Acer Aspire 5741",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5741"),
+ },
+ },
{}
};
@@ -872,7 +903,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out)
struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };
struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
- u32 tmp;
+ u32 tmp = 0;
acpi_status status;
status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result);
@@ -881,14 +912,14 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out)
return status;
obj = (union acpi_object *) result.pointer;
- if (obj && obj->type == ACPI_TYPE_BUFFER &&
- (obj->buffer.length == sizeof(u32) ||
- obj->buffer.length == sizeof(u64))) {
- tmp = *((u32 *) obj->buffer.pointer);
- } else if (obj->type == ACPI_TYPE_INTEGER) {
- tmp = (u32) obj->integer.value;
- } else {
- tmp = 0;
+ if (obj) {
+ if (obj->type == ACPI_TYPE_BUFFER &&
+ (obj->buffer.length == sizeof(u32) ||
+ obj->buffer.length == sizeof(u64))) {
+ tmp = *((u32 *) obj->buffer.pointer);
+ } else if (obj->type == ACPI_TYPE_INTEGER) {
+ tmp = (u32) obj->integer.value;
+ }
}
if (out)
@@ -1190,12 +1221,17 @@ static acpi_status WMID_set_capabilities(void)
return status;
obj = (union acpi_object *) out.pointer;
- if (obj && obj->type == ACPI_TYPE_BUFFER &&
- (obj->buffer.length == sizeof(u32) ||
- obj->buffer.length == sizeof(u64))) {
- devices = *((u32 *) obj->buffer.pointer);
- } else if (obj->type == ACPI_TYPE_INTEGER) {
- devices = (u32) obj->integer.value;
+ if (obj) {
+ if (obj->type == ACPI_TYPE_BUFFER &&
+ (obj->buffer.length == sizeof(u32) ||
+ obj->buffer.length == sizeof(u64))) {
+ devices = *((u32 *) obj->buffer.pointer);
+ } else if (obj->type == ACPI_TYPE_INTEGER) {
+ devices = (u32) obj->integer.value;
+ } else {
+ kfree(out.pointer);
+ return AE_ERROR;
+ }
} else {
kfree(out.pointer);
return AE_ERROR;
@@ -1327,7 +1363,7 @@ static struct led_classdev mail_led = {
.brightness_set = mail_led_set,
};
-static int __devinit acer_led_init(struct device *dev)
+static int acer_led_init(struct device *dev)
{
return led_classdev_register(dev, &mail_led);
}
@@ -1369,7 +1405,7 @@ static const struct backlight_ops acer_bl_ops = {
.update_status = update_bl_status,
};
-static int __devinit acer_backlight_init(struct device *dev)
+static int acer_backlight_init(struct device *dev)
{
struct backlight_properties props;
struct backlight_device *bd;
@@ -1399,6 +1435,60 @@ static void acer_backlight_exit(void)
}
/*
+ * Accelerometer device
+ */
+static acpi_handle gsensor_handle;
+
+static int acer_gsensor_init(void)
+{
+ acpi_status status;
+ struct acpi_buffer output;
+ union acpi_object out_obj;
+
+ output.length = sizeof(out_obj);
+ output.pointer = &out_obj;
+ status = acpi_evaluate_object(gsensor_handle, "_INI", NULL, &output);
+ if (ACPI_FAILURE(status))
+ return -1;
+
+ return 0;
+}
+
+static int acer_gsensor_open(struct input_dev *input)
+{
+ return acer_gsensor_init();
+}
+
+static int acer_gsensor_event(void)
+{
+ acpi_status status;
+ struct acpi_buffer output;
+ union acpi_object out_obj[5];
+
+ if (!has_cap(ACER_CAP_ACCEL))
+ return -1;
+
+ output.length = sizeof(out_obj);
+ output.pointer = out_obj;
+
+ status = acpi_evaluate_object(gsensor_handle, "RDVL", NULL, &output);
+ if (ACPI_FAILURE(status))
+ return -1;
+
+ if (out_obj->package.count != 4)
+ return -1;
+
+ input_report_abs(acer_wmi_accel_dev, ABS_X,
+ (s16)out_obj->package.elements[0].integer.value);
+ input_report_abs(acer_wmi_accel_dev, ABS_Y,
+ (s16)out_obj->package.elements[1].integer.value);
+ input_report_abs(acer_wmi_accel_dev, ABS_Z,
+ (s16)out_obj->package.elements[2].integer.value);
+ input_sync(acer_wmi_accel_dev);
+ return 0;
+}
+
+/*
* Rfkill devices
*/
static void acer_rfkill_update(struct work_struct *ignored);
@@ -1619,6 +1709,7 @@ static void acer_wmi_notify(u32 value, void *context)
acpi_status status;
u16 device_state;
const struct key_entry *key;
+ u32 scancode;
status = wmi_get_event_data(value, &response);
if (status != AE_OK) {
@@ -1655,6 +1746,7 @@ static void acer_wmi_notify(u32 value, void *context)
pr_warn("Unknown key number - 0x%x\n",
return_value.key_num);
} else {
+ scancode = return_value.key_num;
switch (key->keycode) {
case KEY_WLAN:
case KEY_BLUETOOTH:
@@ -1668,11 +1760,16 @@ static void acer_wmi_notify(u32 value, void *context)
rfkill_set_sw_state(bluetooth_rfkill,
!(device_state & ACER_WMID3_GDS_BLUETOOTH));
break;
+ case KEY_TOUCHPAD_TOGGLE:
+ scancode = (device_state & ACER_WMID3_GDS_TOUCHPAD) ?
+ KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF;
}
- sparse_keymap_report_entry(acer_wmi_input_dev, key,
- 1, true);
+ sparse_keymap_report_event(acer_wmi_input_dev, scancode, 1, true);
}
break;
+ case WMID_ACCEL_EVENT:
+ acer_gsensor_event();
+ break;
default:
pr_warn("Unknown function number - %d - %d\n",
return_value.function, return_value.key_num);
@@ -1758,6 +1855,73 @@ static int acer_wmi_enable_lm(void)
return status;
}
+static acpi_status __init acer_wmi_get_handle_cb(acpi_handle ah, u32 level,
+ void *ctx, void **retval)
+{
+ *(acpi_handle *)retval = ah;
+ return AE_OK;
+}
+
+static int __init acer_wmi_get_handle(const char *name, const char *prop,
+ acpi_handle *ah)
+{
+ acpi_status status;
+ acpi_handle handle;
+
+ BUG_ON(!name || !ah);
+
+ handle = NULL;
+ status = acpi_get_devices(prop, acer_wmi_get_handle_cb,
+ (void *)name, &handle);
+
+ if (ACPI_SUCCESS(status)) {
+ *ah = handle;
+ return 0;
+ } else {
+ return -ENODEV;
+ }
+}
+
+static int __init acer_wmi_accel_setup(void)
+{
+ int err;
+
+ err = acer_wmi_get_handle("SENR", "BST0001", &gsensor_handle);
+ if (err)
+ return err;
+
+ interface->capability |= ACER_CAP_ACCEL;
+
+ acer_wmi_accel_dev = input_allocate_device();
+ if (!acer_wmi_accel_dev)
+ return -ENOMEM;
+
+ acer_wmi_accel_dev->open = acer_gsensor_open;
+
+ acer_wmi_accel_dev->name = "Acer BMA150 accelerometer";
+ acer_wmi_accel_dev->phys = "wmi/input1";
+ acer_wmi_accel_dev->id.bustype = BUS_HOST;
+ acer_wmi_accel_dev->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(acer_wmi_accel_dev, ABS_X, -16384, 16384, 0, 0);
+ input_set_abs_params(acer_wmi_accel_dev, ABS_Y, -16384, 16384, 0, 0);
+ input_set_abs_params(acer_wmi_accel_dev, ABS_Z, -16384, 16384, 0, 0);
+
+ err = input_register_device(acer_wmi_accel_dev);
+ if (err)
+ goto err_free_dev;
+
+ return 0;
+
+err_free_dev:
+ input_free_device(acer_wmi_accel_dev);
+ return err;
+}
+
+static void acer_wmi_accel_destroy(void)
+{
+ input_unregister_device(acer_wmi_accel_dev);
+}
+
static int __init acer_wmi_input_setup(void)
{
acpi_status status;
@@ -1819,12 +1983,14 @@ static u32 get_wmid_devices(void)
return 0;
obj = (union acpi_object *) out.pointer;
- if (obj && obj->type == ACPI_TYPE_BUFFER &&
- (obj->buffer.length == sizeof(u32) ||
- obj->buffer.length == sizeof(u64))) {
- devices = *((u32 *) obj->buffer.pointer);
- } else if (obj->type == ACPI_TYPE_INTEGER) {
- devices = (u32) obj->integer.value;
+ if (obj) {
+ if (obj->type == ACPI_TYPE_BUFFER &&
+ (obj->buffer.length == sizeof(u32) ||
+ obj->buffer.length == sizeof(u64))) {
+ devices = *((u32 *) obj->buffer.pointer);
+ } else if (obj->type == ACPI_TYPE_INTEGER) {
+ devices = (u32) obj->integer.value;
+ }
}
kfree(out.pointer);
@@ -1834,7 +2000,7 @@ static u32 get_wmid_devices(void)
/*
* Platform device
*/
-static int __devinit acer_platform_probe(struct platform_device *device)
+static int acer_platform_probe(struct platform_device *device)
{
int err;
@@ -1877,8 +2043,7 @@ static int acer_platform_remove(struct platform_device *device)
return 0;
}
-static int acer_platform_suspend(struct platform_device *dev,
-pm_message_t state)
+static int acer_suspend(struct device *dev)
{
u32 value;
struct acer_data *data = &interface->data;
@@ -1900,7 +2065,7 @@ pm_message_t state)
return 0;
}
-static int acer_platform_resume(struct platform_device *device)
+static int acer_resume(struct device *dev)
{
struct acer_data *data = &interface->data;
@@ -1913,9 +2078,14 @@ static int acer_platform_resume(struct platform_device *device)
if (has_cap(ACER_CAP_BRIGHTNESS))
set_u32(data->brightness, ACER_CAP_BRIGHTNESS);
+ if (has_cap(ACER_CAP_ACCEL))
+ acer_gsensor_init();
+
return 0;
}
+static SIMPLE_DEV_PM_OPS(acer_pm, acer_suspend, acer_resume);
+
static void acer_platform_shutdown(struct platform_device *device)
{
struct acer_data *data = &interface->data;
@@ -1931,11 +2101,10 @@ static struct platform_driver acer_platform_driver = {
.driver = {
.name = "acer-wmi",
.owner = THIS_MODULE,
+ .pm = &acer_pm,
},
.probe = acer_platform_probe,
.remove = acer_platform_remove,
- .suspend = acer_platform_suspend,
- .resume = acer_platform_resume,
.shutdown = acer_platform_shutdown,
};
@@ -2060,14 +2229,14 @@ static int __init acer_wmi_init(void)
set_quirks();
+ if (dmi_check_system(video_vendor_dmi_table))
+ acpi_video_dmi_promote_vendor();
if (acpi_video_backlight_support()) {
- if (dmi_check_system(video_vendor_dmi_table)) {
- acpi_video_unregister();
- } else {
- interface->capability &= ~ACER_CAP_BRIGHTNESS;
- pr_info("Brightness must be controlled by "
- "acpi video driver\n");
- }
+ interface->capability &= ~ACER_CAP_BRIGHTNESS;
+ pr_info("Brightness must be controlled by acpi video driver\n");
+ } else {
+ pr_info("Disabling ACPI video driver\n");
+ acpi_video_unregister_backlight();
}
if (wmi_has_guid(WMID_GUID3)) {
@@ -2090,6 +2259,8 @@ static int __init acer_wmi_init(void)
return err;
}
+ acer_wmi_accel_setup();
+
err = platform_driver_register(&acer_platform_driver);
if (err) {
pr_err("Unable to register platform driver\n");
@@ -2133,6 +2304,8 @@ error_device_alloc:
error_platform_register:
if (wmi_has_guid(ACERWMID_EVENT_GUID))
acer_wmi_input_destroy();
+ if (has_cap(ACER_CAP_ACCEL))
+ acer_wmi_accel_destroy();
return err;
}
@@ -2142,6 +2315,9 @@ static void __exit acer_wmi_exit(void)
if (wmi_has_guid(ACERWMID_EVENT_GUID))
acer_wmi_input_destroy();
+ if (has_cap(ACER_CAP_ACCEL))
+ acer_wmi_accel_destroy();
+
remove_sysfs(acer_platform_device);
remove_debugfs();
platform_device_unregister(acer_platform_device);
diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c
index 2fd9d36acd1..f94467c0522 100644
--- a/drivers/platform/x86/acerhdf.c
+++ b/drivers/platform/x86/acerhdf.c
@@ -329,7 +329,8 @@ static int acerhdf_bind(struct thermal_zone_device *thermal,
if (cdev != cl_dev)
return 0;
- if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) {
+ if (thermal_zone_bind_cooling_device(thermal, 0, cdev,
+ THERMAL_NO_LIMIT, THERMAL_NO_LIMIT)) {
pr_err("error binding cooling dev\n");
return -EINVAL;
}
@@ -514,7 +515,7 @@ static int acerhdf_suspend(struct device *dev)
return 0;
}
-static int __devinit acerhdf_probe(struct platform_device *device)
+static int acerhdf_probe(struct platform_device *device)
{
return 0;
}
@@ -660,8 +661,8 @@ static int acerhdf_register_thermal(void)
if (IS_ERR(cl_dev))
return -EINVAL;
- thz_dev = thermal_zone_device_register("acerhdf", 1, NULL,
- &acerhdf_dev_ops, 0, 0, 0,
+ thz_dev = thermal_zone_device_register("acerhdf", 1, 0, NULL,
+ &acerhdf_dev_ops, NULL, 0,
(kernelmode) ? interval*1000 : 0);
if (IS_ERR(thz_dev))
return -EINVAL;
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c
new file mode 100644
index 00000000000..297b6640213
--- /dev/null
+++ b/drivers/platform/x86/alienware-wmi.c
@@ -0,0 +1,622 @@
+/*
+ * Alienware AlienFX control
+ *
+ * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.com>
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include <linux/leds.h>
+
+#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492"
+#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
+#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492"
+
+#define WMAX_METHOD_HDMI_SOURCE 0x1
+#define WMAX_METHOD_HDMI_STATUS 0x2
+#define WMAX_METHOD_BRIGHTNESS 0x3
+#define WMAX_METHOD_ZONE_CONTROL 0x4
+#define WMAX_METHOD_HDMI_CABLE 0x5
+
+MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
+MODULE_DESCRIPTION("Alienware special feature control");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID);
+MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID);
+
+enum INTERFACE_FLAGS {
+ LEGACY,
+ WMAX,
+};
+
+enum LEGACY_CONTROL_STATES {
+ LEGACY_RUNNING = 1,
+ LEGACY_BOOTING = 0,
+ LEGACY_SUSPEND = 3,
+};
+
+enum WMAX_CONTROL_STATES {
+ WMAX_RUNNING = 0xFF,
+ WMAX_BOOTING = 0,
+ WMAX_SUSPEND = 3,
+};
+
+struct quirk_entry {
+ u8 num_zones;
+};
+
+static struct quirk_entry *quirks;
+
+static struct quirk_entry quirk_unknown = {
+ .num_zones = 2,
+};
+
+static struct quirk_entry quirk_x51_family = {
+ .num_zones = 3,
+};
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+ quirks = dmi->driver_data;
+ return 1;
+}
+
+static struct dmi_system_id alienware_quirks[] = {
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
+ },
+ .driver_data = &quirk_x51_family,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Alienware X51 R2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
+ },
+ .driver_data = &quirk_x51_family,
+ },
+ {}
+};
+
+struct color_platform {
+ u8 blue;
+ u8 green;
+ u8 red;
+} __packed;
+
+struct platform_zone {
+ u8 location;
+ struct device_attribute *attr;
+ struct color_platform colors;
+};
+
+struct wmax_brightness_args {
+ u32 led_mask;
+ u32 percentage;
+};
+
+struct hdmi_args {
+ u8 arg;
+};
+
+struct legacy_led_args {
+ struct color_platform colors;
+ u8 brightness;
+ u8 state;
+} __packed;
+
+struct wmax_led_args {
+ u32 led_mask;
+ struct color_platform colors;
+ u8 state;
+} __packed;
+
+static struct platform_device *platform_device;
+static struct device_attribute *zone_dev_attrs;
+static struct attribute **zone_attrs;
+static struct platform_zone *zone_data;
+
+static struct platform_driver platform_driver = {
+ .driver = {
+ .name = "alienware-wmi",
+ .owner = THIS_MODULE,
+ }
+};
+
+static struct attribute_group zone_attribute_group = {
+ .name = "rgb_zones",
+};
+
+static u8 interface;
+static u8 lighting_control_state;
+static u8 global_brightness;
+
+/*
+ * Helpers used for zone control
+*/
+static int parse_rgb(const char *buf, struct platform_zone *zone)
+{
+ long unsigned int rgb;
+ int ret;
+ union color_union {
+ struct color_platform cp;
+ int package;
+ } repackager;
+
+ ret = kstrtoul(buf, 16, &rgb);
+ if (ret)
+ return ret;
+
+ /* RGB triplet notation is 24-bit hexadecimal */
+ if (rgb > 0xFFFFFF)
+ return -EINVAL;
+
+ repackager.package = rgb & 0x0f0f0f0f;
+ pr_debug("alienware-wmi: r: %d g:%d b: %d\n",
+ repackager.cp.red, repackager.cp.green, repackager.cp.blue);
+ zone->colors = repackager.cp;
+ return 0;
+}
+
+static struct platform_zone *match_zone(struct device_attribute *attr)
+{
+ int i;
+ for (i = 0; i < quirks->num_zones; i++) {
+ if ((struct device_attribute *)zone_data[i].attr == attr) {
+ pr_debug("alienware-wmi: matched zone location: %d\n",
+ zone_data[i].location);
+ return &zone_data[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Individual RGB zone control
+*/
+static int alienware_update_led(struct platform_zone *zone)
+{
+ int method_id;
+ acpi_status status;
+ char *guid;
+ struct acpi_buffer input;
+ struct legacy_led_args legacy_args;
+ struct wmax_led_args wmax_args;
+ if (interface == WMAX) {
+ wmax_args.led_mask = 1 << zone->location;
+ wmax_args.colors = zone->colors;
+ wmax_args.state = lighting_control_state;
+ guid = WMAX_CONTROL_GUID;
+ method_id = WMAX_METHOD_ZONE_CONTROL;
+
+ input.length = (acpi_size) sizeof(wmax_args);
+ input.pointer = &wmax_args;
+ } else {
+ legacy_args.colors = zone->colors;
+ legacy_args.brightness = global_brightness;
+ legacy_args.state = 0;
+ if (lighting_control_state == LEGACY_BOOTING ||
+ lighting_control_state == LEGACY_SUSPEND) {
+ guid = LEGACY_POWER_CONTROL_GUID;
+ legacy_args.state = lighting_control_state;
+ } else
+ guid = LEGACY_CONTROL_GUID;
+ method_id = zone->location + 1;
+
+ input.length = (acpi_size) sizeof(legacy_args);
+ input.pointer = &legacy_args;
+ }
+ pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id);
+
+ status = wmi_evaluate_method(guid, 1, method_id, &input, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: zone set failure: %u\n", status);
+ return ACPI_FAILURE(status);
+}
+
+static ssize_t zone_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_zone *target_zone;
+ target_zone = match_zone(attr);
+ if (target_zone == NULL)
+ return sprintf(buf, "red: -1, green: -1, blue: -1\n");
+ return sprintf(buf, "red: %d, green: %d, blue: %d\n",
+ target_zone->colors.red,
+ target_zone->colors.green, target_zone->colors.blue);
+
+}
+
+static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct platform_zone *target_zone;
+ int ret;
+ target_zone = match_zone(attr);
+ if (target_zone == NULL) {
+ pr_err("alienware-wmi: invalid target zone\n");
+ return 1;
+ }
+ ret = parse_rgb(buf, target_zone);
+ if (ret)
+ return ret;
+ ret = alienware_update_led(target_zone);
+ return ret ? ret : count;
+}
+
+/*
+ * LED Brightness (Global)
+*/
+static int wmax_brightness(int brightness)
+{
+ acpi_status status;
+ struct acpi_buffer input;
+ struct wmax_brightness_args args = {
+ .led_mask = 0xFF,
+ .percentage = brightness,
+ };
+ input.length = (acpi_size) sizeof(args);
+ input.pointer = &args;
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ WMAX_METHOD_BRIGHTNESS, &input, NULL);
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: brightness set failure: %u\n", status);
+ return ACPI_FAILURE(status);
+}
+
+static void global_led_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ int ret;
+ global_brightness = brightness;
+ if (interface == WMAX)
+ ret = wmax_brightness(brightness);
+ else
+ ret = alienware_update_led(&zone_data[0]);
+ if (ret)
+ pr_err("LED brightness update failed\n");
+}
+
+static enum led_brightness global_led_get(struct led_classdev *led_cdev)
+{
+ return global_brightness;
+}
+
+static struct led_classdev global_led = {
+ .brightness_set = global_led_set,
+ .brightness_get = global_led_get,
+ .name = "alienware::global_brightness",
+};
+
+/*
+ * Lighting control state device attribute (Global)
+*/
+static ssize_t show_control_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (lighting_control_state == LEGACY_BOOTING)
+ return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n");
+ else if (lighting_control_state == LEGACY_SUSPEND)
+ return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n");
+ return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n");
+}
+
+static ssize_t store_control_state(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ long unsigned int val;
+ if (strcmp(buf, "booting\n") == 0)
+ val = LEGACY_BOOTING;
+ else if (strcmp(buf, "suspend\n") == 0)
+ val = LEGACY_SUSPEND;
+ else if (interface == LEGACY)
+ val = LEGACY_RUNNING;
+ else
+ val = WMAX_RUNNING;
+ lighting_control_state = val;
+ pr_debug("alienware-wmi: updated control state to %d\n",
+ lighting_control_state);
+ return count;
+}
+
+static DEVICE_ATTR(lighting_control_state, 0644, show_control_state,
+ store_control_state);
+
+static int alienware_zone_init(struct platform_device *dev)
+{
+ int i;
+ char buffer[10];
+ char *name;
+
+ if (interface == WMAX) {
+ lighting_control_state = WMAX_RUNNING;
+ } else if (interface == LEGACY) {
+ lighting_control_state = LEGACY_RUNNING;
+ }
+ global_led.max_brightness = 0x0F;
+ global_brightness = global_led.max_brightness;
+
+ /*
+ * - zone_dev_attrs num_zones + 1 is for individual zones and then
+ * null terminated
+ * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs +
+ * the lighting control + null terminated
+ * - zone_data num_zones is for the distinct zones
+ */
+ zone_dev_attrs =
+ kzalloc(sizeof(struct device_attribute) * (quirks->num_zones + 1),
+ GFP_KERNEL);
+ if (!zone_dev_attrs)
+ return -ENOMEM;
+
+ zone_attrs =
+ kzalloc(sizeof(struct attribute *) * (quirks->num_zones + 2),
+ GFP_KERNEL);
+ if (!zone_attrs)
+ return -ENOMEM;
+
+ zone_data =
+ kzalloc(sizeof(struct platform_zone) * (quirks->num_zones),
+ GFP_KERNEL);
+ if (!zone_data)
+ return -ENOMEM;
+
+ for (i = 0; i < quirks->num_zones; i++) {
+ sprintf(buffer, "zone%02X", i);
+ name = kstrdup(buffer, GFP_KERNEL);
+ if (name == NULL)
+ return 1;
+ sysfs_attr_init(&zone_dev_attrs[i].attr);
+ zone_dev_attrs[i].attr.name = name;
+ zone_dev_attrs[i].attr.mode = 0644;
+ zone_dev_attrs[i].show = zone_show;
+ zone_dev_attrs[i].store = zone_set;
+ zone_data[i].location = i;
+ zone_attrs[i] = &zone_dev_attrs[i].attr;
+ zone_data[i].attr = &zone_dev_attrs[i];
+ }
+ zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr;
+ zone_attribute_group.attrs = zone_attrs;
+
+ led_classdev_register(&dev->dev, &global_led);
+
+ return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group);
+}
+
+static void alienware_zone_exit(struct platform_device *dev)
+{
+ sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group);
+ led_classdev_unregister(&global_led);
+ if (zone_dev_attrs) {
+ int i;
+ for (i = 0; i < quirks->num_zones; i++)
+ kfree(zone_dev_attrs[i].attr.name);
+ }
+ kfree(zone_dev_attrs);
+ kfree(zone_data);
+ kfree(zone_attrs);
+}
+
+/*
+ The HDMI mux sysfs node indicates the status of the HDMI input mux.
+ It can toggle between standard system GPU output and HDMI input.
+*/
+static acpi_status alienware_hdmi_command(struct hdmi_args *in_args,
+ u32 command, int *out_data)
+{
+ acpi_status status;
+ union acpi_object *obj;
+ struct acpi_buffer input;
+ struct acpi_buffer output;
+
+ input.length = (acpi_size) sizeof(*in_args);
+ input.pointer = in_args;
+ if (out_data != NULL) {
+ output.length = ACPI_ALLOCATE_BUFFER;
+ output.pointer = NULL;
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ command, &input, &output);
+ } else
+ status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
+ command, &input, NULL);
+
+ if (ACPI_SUCCESS(status) && out_data != NULL) {
+ obj = (union acpi_object *)output.pointer;
+ if (obj && obj->type == ACPI_TYPE_INTEGER)
+ *out_data = (u32) obj->integer.value;
+ }
+ return status;
+
+}
+
+static ssize_t show_hdmi_cable(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ acpi_status status;
+ u32 out_data;
+ struct hdmi_args in_args = {
+ .arg = 0,
+ };
+ status =
+ alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_CABLE,
+ (u32 *) &out_data);
+ if (ACPI_SUCCESS(status)) {
+ if (out_data == 0)
+ return scnprintf(buf, PAGE_SIZE,
+ "[unconnected] connected unknown\n");
+ else if (out_data == 1)
+ return scnprintf(buf, PAGE_SIZE,
+ "unconnected [connected] unknown\n");
+ }
+ pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status);
+ return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n");
+}
+
+static ssize_t show_hdmi_source(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ acpi_status status;
+ u32 out_data;
+ struct hdmi_args in_args = {
+ .arg = 0,
+ };
+ status =
+ alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_STATUS,
+ (u32 *) &out_data);
+
+ if (ACPI_SUCCESS(status)) {
+ if (out_data == 1)
+ return scnprintf(buf, PAGE_SIZE,
+ "[input] gpu unknown\n");
+ else if (out_data == 2)
+ return scnprintf(buf, PAGE_SIZE,
+ "input [gpu] unknown\n");
+ }
+ pr_err("alienware-wmi: unknown HDMI source status: %d\n", out_data);
+ return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n");
+}
+
+static ssize_t toggle_hdmi_source(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ acpi_status status;
+ struct hdmi_args args;
+ if (strcmp(buf, "gpu\n") == 0)
+ args.arg = 1;
+ else if (strcmp(buf, "input\n") == 0)
+ args.arg = 2;
+ else
+ args.arg = 3;
+ pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf);
+
+ status = alienware_hdmi_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
+
+ if (ACPI_FAILURE(status))
+ pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
+ status);
+ return count;
+}
+
+static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL);
+static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source,
+ toggle_hdmi_source);
+
+static struct attribute *hdmi_attrs[] = {
+ &dev_attr_cable.attr,
+ &dev_attr_source.attr,
+ NULL,
+};
+
+static struct attribute_group hdmi_attribute_group = {
+ .name = "hdmi",
+ .attrs = hdmi_attrs,
+};
+
+static void remove_hdmi(struct platform_device *dev)
+{
+ sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group);
+}
+
+static int create_hdmi(struct platform_device *dev)
+{
+ int ret;
+
+ ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group);
+ if (ret)
+ goto error_create_hdmi;
+ return 0;
+
+error_create_hdmi:
+ remove_hdmi(dev);
+ return ret;
+}
+
+static int __init alienware_wmi_init(void)
+{
+ int ret;
+
+ if (wmi_has_guid(LEGACY_CONTROL_GUID))
+ interface = LEGACY;
+ else if (wmi_has_guid(WMAX_CONTROL_GUID))
+ interface = WMAX;
+ else {
+ pr_warn("alienware-wmi: No known WMI GUID found\n");
+ return -ENODEV;
+ }
+
+ dmi_check_system(alienware_quirks);
+ if (quirks == NULL)
+ quirks = &quirk_unknown;
+
+ ret = platform_driver_register(&platform_driver);
+ if (ret)
+ goto fail_platform_driver;
+ platform_device = platform_device_alloc("alienware-wmi", -1);
+ if (!platform_device) {
+ ret = -ENOMEM;
+ goto fail_platform_device1;
+ }
+ ret = platform_device_add(platform_device);
+ if (ret)
+ goto fail_platform_device2;
+
+ if (interface == WMAX) {
+ ret = create_hdmi(platform_device);
+ if (ret)
+ goto fail_prep_hdmi;
+ }
+
+ ret = alienware_zone_init(platform_device);
+ if (ret)
+ goto fail_prep_zones;
+
+ return 0;
+
+fail_prep_zones:
+ alienware_zone_exit(platform_device);
+fail_prep_hdmi:
+ platform_device_del(platform_device);
+fail_platform_device2:
+ platform_device_put(platform_device);
+fail_platform_device1:
+ platform_driver_unregister(&platform_driver);
+fail_platform_driver:
+ return ret;
+}
+
+module_init(alienware_wmi_init);
+
+static void __exit alienware_wmi_exit(void)
+{
+ if (platform_device) {
+ alienware_zone_exit(platform_device);
+ remove_hdmi(platform_device);
+ platform_device_unregister(platform_device);
+ platform_driver_unregister(&platform_driver);
+ }
+}
+
+module_exit(alienware_wmi_exit);
diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c
index a514bf66fdd..da36b5e824d 100644
--- a/drivers/platform/x86/amilo-rfkill.c
+++ b/drivers/platform/x86/amilo-rfkill.c
@@ -74,7 +74,7 @@ static const struct rfkill_ops amilo_m7440_rfkill_ops = {
.set_block = amilo_m7440_rfkill_set_block
};
-static const struct dmi_system_id __devinitdata amilo_rfkill_id_table[] = {
+static const struct dmi_system_id amilo_rfkill_id_table[] = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
@@ -85,6 +85,13 @@ static const struct dmi_system_id __devinitdata amilo_rfkill_id_table[] = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_BOARD_NAME, "AMILO L1310"),
+ },
+ .driver_data = (void *)&amilo_a1655_rfkill_ops
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
DMI_MATCH(DMI_BOARD_NAME, "AMILO M7440"),
},
.driver_data = (void *)&amilo_m7440_rfkill_ops
@@ -95,7 +102,7 @@ static const struct dmi_system_id __devinitdata amilo_rfkill_id_table[] = {
static struct platform_device *amilo_rfkill_pdev;
static struct rfkill *amilo_rfkill_dev;
-static int __devinit amilo_rfkill_probe(struct platform_device *device)
+static int amilo_rfkill_probe(struct platform_device *device)
{
int rc;
const struct dmi_system_id *system_id =
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index 694a15a5623..b9429fbf1cd 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -2,6 +2,7 @@
* Gmux driver for Apple laptops
*
* Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
+ * Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de>
*
* 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
@@ -18,16 +19,30 @@
#include <linux/pnp.h>
#include <linux/apple_bl.h>
#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/vga_switcheroo.h>
#include <acpi/video.h>
#include <asm/io.h>
struct apple_gmux_data {
unsigned long iostart;
unsigned long iolen;
+ bool indexed;
+ struct mutex index_lock;
struct backlight_device *bdev;
+
+ /* switcheroo data */
+ acpi_handle dhandle;
+ int gpe;
+ enum vga_switcheroo_client_id resume_client_id;
+ enum vga_switcheroo_state power_state;
+ struct completion powerchange_done;
};
+static struct apple_gmux_data *apple_gmux_data;
+
/*
* gmux port offsets. Many of these are not yet used, but may be in the
* future, and it's useful to have them documented here anyhow.
@@ -45,6 +60,9 @@ struct apple_gmux_data {
#define GMUX_PORT_DISCRETE_POWER 0x50
#define GMUX_PORT_MAX_BRIGHTNESS 0x70
#define GMUX_PORT_BRIGHTNESS 0x74
+#define GMUX_PORT_VALUE 0xc2
+#define GMUX_PORT_READ 0xd0
+#define GMUX_PORT_WRITE 0xd4
#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
@@ -59,22 +77,174 @@ struct apple_gmux_data {
#define GMUX_BRIGHTNESS_MASK 0x00ffffff
#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
-static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
+static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
{
return inb(gmux_data->iostart + port);
}
-static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port,
+static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port,
u8 val)
{
outb(val, gmux_data->iostart + port);
}
-static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
+static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port)
{
return inl(gmux_data->iostart + port);
}
+static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
+ u32 val)
+{
+ int i;
+ u8 tmpval;
+
+ for (i = 0; i < 4; i++) {
+ tmpval = (val >> (i * 8)) & 0xff;
+ outb(tmpval, gmux_data->iostart + port + i);
+ }
+}
+
+static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data)
+{
+ int i = 200;
+ u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+
+ while (i && (gwr & 0x01)) {
+ inb(gmux_data->iostart + GMUX_PORT_READ);
+ gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+ udelay(100);
+ i--;
+ }
+
+ return !!i;
+}
+
+static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data)
+{
+ int i = 200;
+ u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+
+ while (i && !(gwr & 0x01)) {
+ gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+ udelay(100);
+ i--;
+ }
+
+ if (gwr & 0x01)
+ inb(gmux_data->iostart + GMUX_PORT_READ);
+
+ return !!i;
+}
+
+static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
+{
+ u8 val;
+
+ mutex_lock(&gmux_data->index_lock);
+ gmux_index_wait_ready(gmux_data);
+ outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
+ gmux_index_wait_complete(gmux_data);
+ val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
+ mutex_unlock(&gmux_data->index_lock);
+
+ return val;
+}
+
+static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port,
+ u8 val)
+{
+ mutex_lock(&gmux_data->index_lock);
+ outb(val, gmux_data->iostart + GMUX_PORT_VALUE);
+ gmux_index_wait_ready(gmux_data);
+ outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
+ gmux_index_wait_complete(gmux_data);
+ mutex_unlock(&gmux_data->index_lock);
+}
+
+static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
+{
+ u32 val;
+
+ mutex_lock(&gmux_data->index_lock);
+ gmux_index_wait_ready(gmux_data);
+ outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
+ gmux_index_wait_complete(gmux_data);
+ val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
+ mutex_unlock(&gmux_data->index_lock);
+
+ return val;
+}
+
+static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
+ u32 val)
+{
+ int i;
+ u8 tmpval;
+
+ mutex_lock(&gmux_data->index_lock);
+
+ for (i = 0; i < 4; i++) {
+ tmpval = (val >> (i * 8)) & 0xff;
+ outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i);
+ }
+
+ gmux_index_wait_ready(gmux_data);
+ outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
+ gmux_index_wait_complete(gmux_data);
+ mutex_unlock(&gmux_data->index_lock);
+}
+
+static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
+{
+ if (gmux_data->indexed)
+ return gmux_index_read8(gmux_data, port);
+ else
+ return gmux_pio_read8(gmux_data, port);
+}
+
+static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
+{
+ if (gmux_data->indexed)
+ gmux_index_write8(gmux_data, port, val);
+ else
+ gmux_pio_write8(gmux_data, port, val);
+}
+
+static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
+{
+ if (gmux_data->indexed)
+ return gmux_index_read32(gmux_data, port);
+ else
+ return gmux_pio_read32(gmux_data, port);
+}
+
+static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
+ u32 val)
+{
+ if (gmux_data->indexed)
+ gmux_index_write32(gmux_data, port, val);
+ else
+ gmux_pio_write32(gmux_data, port, val);
+}
+
+static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
+{
+ u16 val;
+
+ outb(0xaa, gmux_data->iostart + 0xcc);
+ outb(0x55, gmux_data->iostart + 0xcd);
+ outb(0x00, gmux_data->iostart + 0xce);
+
+ val = inb(gmux_data->iostart + 0xcc) |
+ (inb(gmux_data->iostart + 0xcd) << 8);
+
+ if (val == 0x55aa)
+ return true;
+
+ return false;
+}
+
static int gmux_get_brightness(struct backlight_device *bd)
{
struct apple_gmux_data *gmux_data = bl_get_data(bd);
@@ -90,16 +260,7 @@ static int gmux_update_status(struct backlight_device *bd)
if (bd->props.state & BL_CORE_SUSPENDED)
return 0;
- /*
- * Older gmux versions require writing out lower bytes first then
- * setting the upper byte to 0 to flush the values. Newer versions
- * accept a single u32 write, but the old method also works, so we
- * just use the old method for all gmux versions.
- */
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8);
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16);
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0);
+ gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
return 0;
}
@@ -110,8 +271,151 @@ static const struct backlight_ops gmux_bl_ops = {
.update_status = gmux_update_status,
};
-static int __devinit gmux_probe(struct pnp_dev *pnp,
- const struct pnp_device_id *id)
+static int gmux_switchto(enum vga_switcheroo_client_id id)
+{
+ if (id == VGA_SWITCHEROO_IGD) {
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2);
+ } else {
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
+ }
+
+ return 0;
+}
+
+static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
+ enum vga_switcheroo_state state)
+{
+ reinit_completion(&gmux_data->powerchange_done);
+
+ if (state == VGA_SWITCHEROO_ON) {
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3);
+ pr_debug("Discrete card powered up\n");
+ } else {
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0);
+ pr_debug("Discrete card powered down\n");
+ }
+
+ gmux_data->power_state = state;
+
+ if (gmux_data->gpe >= 0 &&
+ !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done,
+ msecs_to_jiffies(200)))
+ pr_warn("Timeout waiting for gmux switch to complete\n");
+
+ return 0;
+}
+
+static int gmux_set_power_state(enum vga_switcheroo_client_id id,
+ enum vga_switcheroo_state state)
+{
+ if (id == VGA_SWITCHEROO_IGD)
+ return 0;
+
+ return gmux_set_discrete_state(apple_gmux_data, state);
+}
+
+static int gmux_get_client_id(struct pci_dev *pdev)
+{
+ /*
+ * Early Macbook Pros with switchable graphics use nvidia
+ * integrated graphics. Hardcode that the 9400M is integrated.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ return VGA_SWITCHEROO_IGD;
+ else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
+ pdev->device == 0x0863)
+ return VGA_SWITCHEROO_IGD;
+ else
+ return VGA_SWITCHEROO_DIS;
+}
+
+static enum vga_switcheroo_client_id
+gmux_active_client(struct apple_gmux_data *gmux_data)
+{
+ if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2)
+ return VGA_SWITCHEROO_IGD;
+
+ return VGA_SWITCHEROO_DIS;
+}
+
+static struct vga_switcheroo_handler gmux_handler = {
+ .switchto = gmux_switchto,
+ .power_state = gmux_set_power_state,
+ .get_client_id = gmux_get_client_id,
+};
+
+static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
+{
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
+ GMUX_INTERRUPT_DISABLE);
+}
+
+static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data)
+{
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
+ GMUX_INTERRUPT_ENABLE);
+}
+
+static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data)
+{
+ return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS);
+}
+
+static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
+{
+ u8 status;
+
+ /* to clear interrupts write back current status */
+ status = gmux_interrupt_get_status(gmux_data);
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
+}
+
+static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
+{
+ u8 status;
+ struct pnp_dev *pnp = (struct pnp_dev *)context;
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+
+ status = gmux_interrupt_get_status(gmux_data);
+ gmux_disable_interrupts(gmux_data);
+ pr_debug("Notify handler called: status %d\n", status);
+
+ gmux_clear_interrupts(gmux_data);
+ gmux_enable_interrupts(gmux_data);
+
+ if (status & GMUX_INTERRUPT_STATUS_POWER)
+ complete(&gmux_data->powerchange_done);
+}
+
+static int gmux_suspend(struct device *dev)
+{
+ struct pnp_dev *pnp = to_pnp_dev(dev);
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+
+ gmux_data->resume_client_id = gmux_active_client(gmux_data);
+ gmux_disable_interrupts(gmux_data);
+ return 0;
+}
+
+static int gmux_resume(struct device *dev)
+{
+ struct pnp_dev *pnp = to_pnp_dev(dev);
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+
+ gmux_enable_interrupts(gmux_data);
+ gmux_switchto(gmux_data->resume_client_id);
+ if (gmux_data->power_state == VGA_SWITCHEROO_OFF)
+ gmux_set_discrete_state(gmux_data, gmux_data->power_state);
+ return 0;
+}
+
+static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
{
struct apple_gmux_data *gmux_data;
struct resource *res;
@@ -119,6 +423,11 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
struct backlight_device *bdev;
u8 ver_major, ver_minor, ver_release;
int ret = -ENXIO;
+ acpi_status status;
+ unsigned long long gpe;
+
+ if (apple_gmux_data)
+ return -EBUSY;
gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
if (!gmux_data)
@@ -147,21 +456,32 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
}
/*
- * On some machines the gmux is in ACPI even thought the machine
- * doesn't really have a gmux. Check for invalid version information
- * to detect this.
+ * Invalid version information may indicate either that the gmux
+ * device isn't present or that it's a new one that uses indexed
+ * io
*/
+
ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
- pr_info("gmux device not present\n");
- ret = -ENODEV;
- goto err_release;
+ if (gmux_is_indexed(gmux_data)) {
+ u32 version;
+ mutex_init(&gmux_data->index_lock);
+ gmux_data->indexed = true;
+ version = gmux_read32(gmux_data,
+ GMUX_PORT_VERSION_MAJOR);
+ ver_major = (version >> 24) & 0xff;
+ ver_minor = (version >> 16) & 0xff;
+ ver_release = (version >> 8) & 0xff;
+ } else {
+ pr_info("gmux device not present\n");
+ ret = -ENODEV;
+ goto err_release;
+ }
}
-
- pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
- ver_release);
+ pr_info("Found gmux version %d.%d.%d [%s]\n", ver_major, ver_minor,
+ ver_release, (gmux_data->indexed ? "indexed" : "classic"));
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_PLATFORM;
@@ -193,11 +513,66 @@ static int __devinit gmux_probe(struct pnp_dev *pnp,
* backlight control and supports more levels than other options.
* Disable the other backlight choices.
*/
+ acpi_video_dmi_promote_vendor();
acpi_video_unregister();
apple_bl_unregister();
+ gmux_data->power_state = VGA_SWITCHEROO_ON;
+
+ gmux_data->dhandle = ACPI_HANDLE(&pnp->dev);
+ if (!gmux_data->dhandle) {
+ pr_err("Cannot find acpi handle for pnp device %s\n",
+ dev_name(&pnp->dev));
+ ret = -ENODEV;
+ goto err_notify;
+ }
+
+ status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe);
+ if (ACPI_SUCCESS(status)) {
+ gmux_data->gpe = (int)gpe;
+
+ status = acpi_install_notify_handler(gmux_data->dhandle,
+ ACPI_DEVICE_NOTIFY,
+ &gmux_notify_handler, pnp);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Install notify handler failed: %s\n",
+ acpi_format_exception(status));
+ ret = -ENODEV;
+ goto err_notify;
+ }
+
+ status = acpi_enable_gpe(NULL, gmux_data->gpe);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Cannot enable gpe: %s\n",
+ acpi_format_exception(status));
+ goto err_enable_gpe;
+ }
+ } else {
+ pr_warn("No GPE found for gmux\n");
+ gmux_data->gpe = -1;
+ }
+
+ if (vga_switcheroo_register_handler(&gmux_handler)) {
+ ret = -ENODEV;
+ goto err_register_handler;
+ }
+
+ init_completion(&gmux_data->powerchange_done);
+ apple_gmux_data = gmux_data;
+ gmux_enable_interrupts(gmux_data);
+
return 0;
+err_register_handler:
+ if (gmux_data->gpe >= 0)
+ acpi_disable_gpe(NULL, gmux_data->gpe);
+err_enable_gpe:
+ if (gmux_data->gpe >= 0)
+ acpi_remove_notify_handler(gmux_data->dhandle,
+ ACPI_DEVICE_NOTIFY,
+ &gmux_notify_handler);
+err_notify:
+ backlight_device_unregister(bdev);
err_release:
release_region(gmux_data->iostart, gmux_data->iolen);
err_free:
@@ -205,14 +580,26 @@ err_free:
return ret;
}
-static void __devexit gmux_remove(struct pnp_dev *pnp)
+static void gmux_remove(struct pnp_dev *pnp)
{
struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+ vga_switcheroo_unregister_handler();
+ gmux_disable_interrupts(gmux_data);
+ if (gmux_data->gpe >= 0) {
+ acpi_disable_gpe(NULL, gmux_data->gpe);
+ acpi_remove_notify_handler(gmux_data->dhandle,
+ ACPI_DEVICE_NOTIFY,
+ &gmux_notify_handler);
+ }
+
backlight_device_unregister(gmux_data->bdev);
+
release_region(gmux_data->iostart, gmux_data->iolen);
+ apple_gmux_data = NULL;
kfree(gmux_data);
+ acpi_video_dmi_demote_vendor();
acpi_video_register();
apple_bl_register();
}
@@ -222,11 +609,19 @@ static const struct pnp_device_id gmux_device_ids[] = {
{"", 0}
};
+static const struct dev_pm_ops gmux_dev_pm_ops = {
+ .suspend = gmux_suspend,
+ .resume = gmux_resume,
+};
+
static struct pnp_driver gmux_pnp_driver = {
.name = "apple-gmux",
.probe = gmux_probe,
- .remove = __devexit_p(gmux_remove),
+ .remove = gmux_remove,
.id_table = gmux_device_ids,
+ .driver = {
+ .pm = &gmux_dev_pm_ops,
+ },
};
static int __init apple_gmux_init(void)
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c
index e38f91be0b1..7f4dc6f51f8 100644
--- a/drivers/platform/x86/asus-laptop.c
+++ b/drivers/platform/x86/asus-laptop.c
@@ -53,8 +53,7 @@
#include <linux/rfkill.h>
#include <linux/slab.h>
#include <linux/dmi.h>
-#include <acpi/acpi_drivers.h>
-#include <acpi/acpi_bus.h>
+#include <linux/acpi.h>
#define ASUS_LAPTOP_VERSION "0.42"
@@ -85,7 +84,7 @@ static char *wled_type = "unknown";
static char *bled_type = "unknown";
module_param(wled_type, charp, 0444);
-MODULE_PARM_DESC(wlan_status, "Set the wled type on boot "
+MODULE_PARM_DESC(wled_type, "Set the wled type on boot "
"(unknown, led or rfkill). "
"default is unknown");
@@ -128,10 +127,12 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
/*
* Some events we use, same for all Asus
*/
-#define ATKD_BR_UP 0x10 /* (event & ~ATKD_BR_UP) = brightness level */
-#define ATKD_BR_DOWN 0x20 /* (event & ~ATKD_BR_DOWN) = britghness level */
-#define ATKD_BR_MIN ATKD_BR_UP
-#define ATKD_BR_MAX (ATKD_BR_DOWN | 0xF) /* 0x2f */
+#define ATKD_BRNUP_MIN 0x10
+#define ATKD_BRNUP_MAX 0x1f
+#define ATKD_BRNDOWN_MIN 0x20
+#define ATKD_BRNDOWN_MAX 0x2f
+#define ATKD_BRNDOWN 0x20
+#define ATKD_BRNUP 0x2f
#define ATKD_LCD_ON 0x33
#define ATKD_LCD_OFF 0x34
@@ -301,40 +302,65 @@ static const struct key_entry asus_keymap[] = {
{KE_KEY, 0x17, { KEY_ZOOM } },
{KE_KEY, 0x1f, { KEY_BATTERY } },
/* End of Lenovo SL Specific keycodes */
+ {KE_KEY, ATKD_BRNDOWN, { KEY_BRIGHTNESSDOWN } },
+ {KE_KEY, ATKD_BRNUP, { KEY_BRIGHTNESSUP } },
{KE_KEY, 0x30, { KEY_VOLUMEUP } },
{KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
{KE_KEY, 0x32, { KEY_MUTE } },
- {KE_KEY, 0x33, { KEY_SWITCHVIDEOMODE } },
- {KE_KEY, 0x34, { KEY_SWITCHVIDEOMODE } },
+ {KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */
+ {KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
{KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
{KE_KEY, 0x41, { KEY_NEXTSONG } },
- {KE_KEY, 0x43, { KEY_STOPCD } },
+ {KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */
{KE_KEY, 0x45, { KEY_PLAYPAUSE } },
- {KE_KEY, 0x4c, { KEY_MEDIA } },
+ {KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */
{KE_KEY, 0x50, { KEY_EMAIL } },
{KE_KEY, 0x51, { KEY_WWW } },
{KE_KEY, 0x55, { KEY_CALC } },
+ {KE_IGNORE, 0x57, }, /* Battery mode */
+ {KE_IGNORE, 0x58, }, /* AC mode */
{KE_KEY, 0x5C, { KEY_SCREENLOCK } }, /* Screenlock */
- {KE_KEY, 0x5D, { KEY_WLAN } },
- {KE_KEY, 0x5E, { KEY_WLAN } },
- {KE_KEY, 0x5F, { KEY_WLAN } },
- {KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
- {KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
- {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
- {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
- {KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */
+ {KE_KEY, 0x5D, { KEY_WLAN } }, /* WLAN Toggle */
+ {KE_KEY, 0x5E, { KEY_WLAN } }, /* WLAN Enable */
+ {KE_KEY, 0x5F, { KEY_WLAN } }, /* WLAN Disable */
+ {KE_KEY, 0x60, { KEY_TOUCHPAD_ON } },
+ {KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */
+ {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */
+ {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */
+ {KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */
+ {KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */
+ {KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */
+ {KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
+ {KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, /* Lock Touchpad */
{KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */
{KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */
- {KE_KEY, 0x7E, { KEY_BLUETOOTH } },
- {KE_KEY, 0x7D, { KEY_BLUETOOTH } },
+ {KE_IGNORE, 0x6E, }, /* Low Battery notification */
+ {KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
+ {KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{KE_KEY, 0x82, { KEY_CAMERA } },
- {KE_KEY, 0x88, { KEY_WLAN } },
- {KE_KEY, 0x8A, { KEY_PROG1 } },
+ {KE_KEY, 0x88, { KEY_RFKILL } }, /* Radio Toggle Key */
+ {KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */
+ {KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */
+ {KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */
+ {KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */
+ {KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */
+ {KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */
+ {KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */
+ {KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
+ {KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
{KE_KEY, 0x95, { KEY_MEDIA } },
{KE_KEY, 0x99, { KEY_PHONE } },
- {KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
- {KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
- {KE_KEY, 0xb5, { KEY_CALC } },
+ {KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
+ {KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
+ {KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
+ {KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
+ {KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */
+ {KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */
+ {KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */
+ {KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */
+ {KE_KEY, 0xB5, { KEY_CALC } },
+ {KE_KEY, 0xC4, { KEY_KBDILLUMUP } },
+ {KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },
{KE_END, 0},
};
@@ -860,12 +886,14 @@ static ssize_t show_infos(struct device *dev,
/*
* The HWRS method return informations about the hardware.
* 0x80 bit is for WLAN, 0x100 for Bluetooth.
+ * 0x40 for WWAN, 0x10 for WIMAX.
* The significance of others is yet to be found.
- * If we don't find the method, we assume the device are present.
+ * We don't currently use this for device detection, and it
+ * takes several seconds to run on some systems.
*/
- rv = acpi_evaluate_integer(asus->handle, "HRWS", NULL, &temp);
+ rv = acpi_evaluate_integer(asus->handle, "HWRS", NULL, &temp);
if (!ACPI_FAILURE(rv))
- len += sprintf(page + len, "HRWS value : %#x\n",
+ len += sprintf(page + len, "HWRS value : %#x\n",
(uint) temp);
/*
* Another value for userspace: the ASYM method returns 0x02 for
@@ -1465,10 +1493,9 @@ static int asus_input_init(struct asus_laptop *asus)
int error;
input = input_allocate_device();
- if (!input) {
- pr_warn("Unable to allocate input device\n");
+ if (!input)
return -ENOMEM;
- }
+
input->name = "Asus Laptop extra buttons";
input->phys = ASUS_LAPTOP_FILE "/input0";
input->id.bustype = BUS_HOST;
@@ -1514,20 +1541,23 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
/* TODO Find a better way to handle events count. */
count = asus->event_count[event % 128]++;
- acpi_bus_generate_proc_event(asus->device, event, count);
acpi_bus_generate_netlink_event(asus->device->pnp.device_class,
dev_name(&asus->device->dev), event,
count);
- /* Brightness events are special */
- if (event >= ATKD_BR_MIN && event <= ATKD_BR_MAX) {
+ if (event >= ATKD_BRNUP_MIN && event <= ATKD_BRNUP_MAX)
+ event = ATKD_BRNUP;
+ else if (event >= ATKD_BRNDOWN_MIN &&
+ event <= ATKD_BRNDOWN_MAX)
+ event = ATKD_BRNDOWN;
- /* Ignore them completely if the acpi video driver is used */
+ /* Brightness events are special */
+ if (event == ATKD_BRNDOWN || event == ATKD_BRNUP) {
if (asus->backlight_device != NULL) {
/* Update the backlight device. */
asus_backlight_notify(asus);
+ return ;
}
- return ;
}
/* Accelerometer "coarse orientation change" event */
@@ -1682,7 +1712,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *model = NULL;
- unsigned long long bsts_result, hwrs_result;
+ unsigned long long bsts_result;
char *string = NULL;
acpi_status status;
@@ -1741,20 +1771,9 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
return -ENOMEM;
}
- if (*string)
+ if (string)
pr_notice(" %s model detected\n", string);
- /*
- * The HWRS method return informations about the hardware.
- * 0x80 bit is for WLAN, 0x100 for Bluetooth,
- * 0x40 for WWAN, 0x10 for WIMAX.
- * The significance of others is yet to be found.
- */
- status =
- acpi_evaluate_integer(asus->handle, "HRWS", NULL, &hwrs_result);
- if (!ACPI_FAILURE(status))
- pr_notice(" HRWS returned %x", (int)hwrs_result);
-
if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))
asus->have_rsts = true;
@@ -1763,7 +1782,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
return AE_OK;
}
-static int __devinit asus_acpi_init(struct asus_laptop *asus)
+static int asus_acpi_init(struct asus_laptop *asus)
{
int result = 0;
@@ -1823,7 +1842,7 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
return result;
}
-static void __devinit asus_dmi_check(void)
+static void asus_dmi_check(void)
{
const char *model;
@@ -1839,7 +1858,7 @@ static void __devinit asus_dmi_check(void)
static bool asus_device_present;
-static int __devinit asus_acpi_add(struct acpi_device *device)
+static int asus_acpi_add(struct acpi_device *device)
{
struct asus_laptop *asus;
int result;
@@ -1913,13 +1932,12 @@ fail_input:
fail_backlight:
asus_platform_exit(asus);
fail_platform:
- kfree(asus->name);
kfree(asus);
return result;
}
-static int asus_acpi_remove(struct acpi_device *device, int type)
+static int asus_acpi_remove(struct acpi_device *device)
{
struct asus_laptop *asus = acpi_driver_data(device);
diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c
index 99a30b51313..ddf0eefd862 100644
--- a/drivers/platform/x86/asus-nb-wmi.c
+++ b/drivers/platform/x86/asus-nb-wmi.c
@@ -26,12 +26,13 @@
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
#include <linux/fb.h>
+#include <linux/dmi.h>
#include "asus-wmi.h"
#define ASUS_NB_WMI_FILE "asus-nb-wmi"
-MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>");
+MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>");
MODULE_DESCRIPTION("Asus Notebooks WMI Hotkey Driver");
MODULE_LICENSE("GPL");
@@ -48,21 +49,185 @@ MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
* 1 | Hardware | Software
* 4 | Software | Software
*/
-static uint wapf;
+static int wapf = -1;
module_param(wapf, uint, 0444);
MODULE_PARM_DESC(wapf, "WAPF value");
+static struct quirk_entry *quirks;
+
static struct quirk_entry quirk_asus_unknown = {
+ .wapf = 0,
+};
+
+/*
+ * For those machines that need software to control bt/wifi status
+ * and can't adjust brightness through ACPI interface
+ * and have duplicate events(ACPI and WMI) for display toggle
+ */
+static struct quirk_entry quirk_asus_x55u = {
+ .wapf = 4,
+ .wmi_backlight_power = true,
+ .no_display_toggle = true,
+};
+
+static struct quirk_entry quirk_asus_x401u = {
+ .wapf = 4,
+};
+
+static int dmi_matched(const struct dmi_system_id *dmi)
+{
+ quirks = dmi->driver_data;
+ return 1;
+}
+
+static struct dmi_system_id asus_quirks[] = {
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X401U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X401U"),
+ },
+ .driver_data = &quirk_asus_x55u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X401A",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X401A"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X401A1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X401A1"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X501U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X501U"),
+ },
+ .driver_data = &quirk_asus_x55u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X501A",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X501A"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X501A1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X501A1"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X550CA",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X550CA"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X55A",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X55A"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X55C",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X55C"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X55U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X55U"),
+ },
+ .driver_data = &quirk_asus_x55u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X55VD",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X55VD"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. X75A",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X75A"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. 1015E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1015E"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK COMPUTER INC. 1015U",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1015U"),
+ },
+ .driver_data = &quirk_asus_x401u,
+ },
+ {},
};
static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)
{
- driver->quirks = &quirk_asus_unknown;
- driver->quirks->wapf = wapf;
+ quirks = &quirk_asus_unknown;
+ dmi_check_system(asus_quirks);
+
+ driver->quirks = quirks;
driver->panel_power = FB_BLANK_UNBLANK;
+
+ /* overwrite the wapf setting if the wapf paramater is specified */
+ if (wapf != -1)
+ quirks->wapf = wapf;
+ else
+ wapf = quirks->wapf;
}
static const struct key_entry asus_nb_wmi_keymap[] = {
+ { KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },
+ { KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
{ KE_KEY, 0x32, { KEY_MUTE } },
@@ -70,9 +235,9 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
{ KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
{ KE_KEY, 0x41, { KEY_NEXTSONG } },
- { KE_KEY, 0x43, { KEY_STOPCD } },
+ { KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */
{ KE_KEY, 0x45, { KEY_PLAYPAUSE } },
- { KE_KEY, 0x4c, { KEY_MEDIA } },
+ { KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */
{ KE_KEY, 0x50, { KEY_EMAIL } },
{ KE_KEY, 0x51, { KEY_WWW } },
{ KE_KEY, 0x55, { KEY_CALC } },
@@ -82,21 +247,43 @@ static const struct key_entry asus_nb_wmi_keymap[] = {
{ KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */
{ KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */
{ KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */
- { KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
- { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
- { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
- { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, 0x60, { KEY_TOUCHPAD_ON } },
+ { KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */
+ { KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */
+ { KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */
+ { KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */
+ { KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */
+ { KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */
+ { KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
- { KE_KEY, 0x7D, { KEY_BLUETOOTH } },
- { KE_KEY, 0x7E, { KEY_BLUETOOTH } },
+ { KE_IGNORE, 0x6E, }, /* Low Battery notification */
+ { KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */
+ { KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */
{ KE_KEY, 0x82, { KEY_CAMERA } },
- { KE_KEY, 0x88, { KEY_RFKILL } },
- { KE_KEY, 0x8A, { KEY_PROG1 } },
+ { KE_KEY, 0x88, { KEY_RFKILL } }, /* Radio Toggle Key */
+ { KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */
+ { KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */
+ { KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */
+ { KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */
+ { KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */
+ { KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */
+ { KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */
+ { KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */
+ { KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */
{ KE_KEY, 0x95, { KEY_MEDIA } },
{ KE_KEY, 0x99, { KEY_PHONE } },
- { KE_KEY, 0xb5, { KEY_CALC } },
- { KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
- { KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
+ { KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */
+ { KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */
+ { KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */
+ { KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */
+ { KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */
+ { KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */
+ { KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */
+ { KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */
+ { KE_KEY, 0xB5, { KEY_CALC } },
+ { KE_KEY, 0xC4, { KEY_KBDILLUMUP } },
+ { KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },
+ { KE_IGNORE, 0xC6, }, /* Ambient Light Sensor notification */
{ KE_END, 0},
};
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 77aadde5281..3c6ccedc82b 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -45,12 +45,12 @@
#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/thermal.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
+#include <linux/acpi.h>
+#include <acpi/video.h>
#include "asus-wmi.h"
-MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>, "
+MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>, "
"Yong Wang <yong.y.wang@intel.com>");
MODULE_DESCRIPTION("Asus Generic WMI Driver");
MODULE_LICENSE("GPL");
@@ -98,6 +98,7 @@ MODULE_LICENSE("GPL");
#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002
#define ASUS_WMI_DEVID_CWAP 0x00010003
#define ASUS_WMI_DEVID_WLAN 0x00010011
+#define ASUS_WMI_DEVID_WLAN_LED 0x00010012
#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013
#define ASUS_WMI_DEVID_GPS 0x00010015
#define ASUS_WMI_DEVID_WIMAX 0x00010017
@@ -136,6 +137,9 @@ MODULE_LICENSE("GPL");
/* Power */
#define ASUS_WMI_DEVID_PROCESSOR_STATE 0x00120012
+/* Deep S3 / Resume on LID open */
+#define ASUS_WMI_DEVID_LID_RESUME 0x00120031
+
/* DSTS masks */
#define ASUS_WMI_DSTS_STATUS_BIT 0x00000001
#define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002
@@ -179,9 +183,10 @@ struct asus_wmi {
struct input_dev *inputdev;
struct backlight_device *backlight_device;
- struct device *hwmon_device;
struct platform_device *platform_device;
+ struct led_classdev wlan_led;
+ int wlan_led_wk;
struct led_classdev tpd_led;
int tpd_led_wk;
struct led_classdev kbd_led;
@@ -189,6 +194,7 @@ struct asus_wmi {
struct workqueue_struct *led_workqueue;
struct work_struct tpd_led_work;
struct work_struct kbd_led_work;
+ struct work_struct wlan_led_work;
struct asus_rfkill wlan;
struct asus_rfkill bluetooth;
@@ -260,7 +266,7 @@ static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
acpi_status status;
union acpi_object *obj;
- u32 tmp;
+ u32 tmp = 0;
status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id,
&input, &output);
@@ -271,8 +277,6 @@ static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,
obj = (union acpi_object *)output.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
tmp = (u32) obj->integer.value;
- else
- tmp = 0;
if (retval)
*retval = tmp;
@@ -451,12 +455,65 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
return value;
}
+static int wlan_led_unknown_state(struct asus_wmi *asus)
+{
+ u32 result;
+
+ asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
+
+ return result & ASUS_WMI_DSTS_UNKNOWN_BIT;
+}
+
+static int wlan_led_presence(struct asus_wmi *asus)
+{
+ u32 result;
+
+ asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
+
+ return result & ASUS_WMI_DSTS_PRESENCE_BIT;
+}
+
+static void wlan_led_update(struct work_struct *work)
+{
+ int ctrl_param;
+ struct asus_wmi *asus;
+
+ asus = container_of(work, struct asus_wmi, wlan_led_work);
+
+ ctrl_param = asus->wlan_led_wk;
+ asus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL);
+}
+
+static void wlan_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct asus_wmi *asus;
+
+ asus = container_of(led_cdev, struct asus_wmi, wlan_led);
+
+ asus->wlan_led_wk = !!value;
+ queue_work(asus->led_workqueue, &asus->wlan_led_work);
+}
+
+static enum led_brightness wlan_led_get(struct led_classdev *led_cdev)
+{
+ struct asus_wmi *asus;
+ u32 result;
+
+ asus = container_of(led_cdev, struct asus_wmi, wlan_led);
+ asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result);
+
+ return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK;
+}
+
static void asus_wmi_led_exit(struct asus_wmi *asus)
{
if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
led_classdev_unregister(&asus->kbd_led);
if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
led_classdev_unregister(&asus->tpd_led);
+ if (!IS_ERR_OR_NULL(asus->wlan_led.dev))
+ led_classdev_unregister(&asus->wlan_led);
if (asus->led_workqueue)
destroy_workqueue(asus->led_workqueue);
}
@@ -493,6 +550,23 @@ static int asus_wmi_led_init(struct asus_wmi *asus)
rv = led_classdev_register(&asus->platform_device->dev,
&asus->kbd_led);
+ if (rv)
+ goto error;
+ }
+
+ if (wlan_led_presence(asus) && (asus->driver->quirks->wapf == 4)) {
+ INIT_WORK(&asus->wlan_led_work, wlan_led_update);
+
+ asus->wlan_led.name = "asus::wlan";
+ asus->wlan_led.brightness_set = wlan_led_set;
+ if (!wlan_led_unknown_state(asus))
+ asus->wlan_led.brightness_get = wlan_led_get;
+ asus->wlan_led.flags = LED_CORE_SUSPENDRESUME;
+ asus->wlan_led.max_brightness = 1;
+ asus->wlan_led.default_trigger = "asus-wlan";
+
+ rv = led_classdev_register(&asus->platform_device->dev,
+ &asus->wlan_led);
}
error:
@@ -528,6 +602,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)
mutex_unlock(&asus->wmi_lock);
mutex_lock(&asus->hotplug_lock);
+ pci_lock_rescan_remove();
if (asus->wlan.rfkill)
rfkill_set_sw_state(asus->wlan.rfkill, blocked);
@@ -565,8 +640,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)
dev = pci_scan_single_device(bus, 0);
if (dev) {
pci_bus_assign_resources(bus);
- if (pci_bus_add_device(dev))
- pr_err("Unable to hotplug wifi\n");
+ pci_bus_add_device(dev);
}
} else {
dev = pci_get_slot(bus, 0);
@@ -578,6 +652,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)
}
out_unlock:
+ pci_unlock_rescan_remove();
mutex_unlock(&asus->hotplug_lock);
}
@@ -725,8 +800,21 @@ static int asus_rfkill_set(void *data, bool blocked)
{
struct asus_rfkill *priv = data;
u32 ctrl_param = !blocked;
+ u32 dev_id = priv->dev_id;
- return asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL);
+ /*
+ * If the user bit is set, BIOS can't set and record the wlan status,
+ * it will report the value read from id ASUS_WMI_DEVID_WLAN_LED
+ * while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN).
+ * So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED
+ * while setting the wlan status through WMI.
+ * This is also the behavior that windows app will do.
+ */
+ if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
+ priv->asus->driver->wlan_ctrl_by_user)
+ dev_id = ASUS_WMI_DEVID_WLAN_LED;
+
+ return asus_wmi_set_devstate(dev_id, ctrl_param, NULL);
}
static void asus_rfkill_query(struct rfkill *rfkill, void *data)
@@ -795,6 +883,10 @@ static int asus_new_rfkill(struct asus_wmi *asus,
if (!*rfkill)
return -EINVAL;
+ if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
+ (asus->driver->quirks->wapf == 4))
+ rfkill_set_led_trigger_name(*rfkill, "asus-wlan");
+
rfkill_init_sw_state(*rfkill, !result);
result = rfkill_register(*rfkill);
if (result) {
@@ -951,7 +1043,7 @@ static ssize_t asus_hwmon_pwm1(struct device *dev,
else if (value == 3)
value = 255;
else if (value != 0) {
- pr_err("Unknown fan speed %#x", value);
+ pr_err("Unknown fan speed %#x\n", value);
value = -1;
}
@@ -976,20 +1068,12 @@ static ssize_t asus_hwmon_temp1(struct device *dev,
return sprintf(buf, "%d\n", value);
}
-static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL, 0);
-
-static ssize_t
-show_name(struct device *dev, struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "asus\n");
-}
-static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+static DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL);
+static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);
static struct attribute *hwmon_attributes[] = {
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_name.dev_attr.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_temp1_input.attr,
NULL
};
@@ -1003,9 +1087,9 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,
int dev_id = -1;
u32 value = ASUS_WMI_UNSUPPORTED_METHOD;
- if (attr == &sensor_dev_attr_pwm1.dev_attr.attr)
+ if (attr == &dev_attr_pwm1.attr)
dev_id = ASUS_WMI_DEVID_FAN_CTRL;
- else if (attr == &sensor_dev_attr_temp1_input.dev_attr.attr)
+ else if (attr == &dev_attr_temp1_input.attr)
dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;
if (dev_id != -1) {
@@ -1040,35 +1124,20 @@ static struct attribute_group hwmon_attribute_group = {
.is_visible = asus_hwmon_sysfs_is_visible,
.attrs = hwmon_attributes
};
-
-static void asus_wmi_hwmon_exit(struct asus_wmi *asus)
-{
- struct device *hwmon;
-
- hwmon = asus->hwmon_device;
- if (!hwmon)
- return;
- sysfs_remove_group(&hwmon->kobj, &hwmon_attribute_group);
- hwmon_device_unregister(hwmon);
- asus->hwmon_device = NULL;
-}
+__ATTRIBUTE_GROUPS(hwmon_attribute);
static int asus_wmi_hwmon_init(struct asus_wmi *asus)
{
struct device *hwmon;
- int result;
- hwmon = hwmon_device_register(&asus->platform_device->dev);
+ hwmon = hwmon_device_register_with_groups(&asus->platform_device->dev,
+ "asus", asus,
+ hwmon_attribute_groups);
if (IS_ERR(hwmon)) {
pr_err("Could not register asus hwmon device\n");
return PTR_ERR(hwmon);
}
- dev_set_drvdata(hwmon, asus);
- asus->hwmon_device = hwmon;
- result = sysfs_create_group(&hwmon->kobj, &hwmon_attribute_group);
- if (result)
- asus_wmi_hwmon_exit(asus);
- return result;
+ return 0;
}
/*
@@ -1247,6 +1316,18 @@ static void asus_wmi_backlight_exit(struct asus_wmi *asus)
asus->backlight_device = NULL;
}
+static int is_display_toggle(int code)
+{
+ /* display toggle keys */
+ if ((code >= 0x61 && code <= 0x67) ||
+ (code >= 0x8c && code <= 0x93) ||
+ (code >= 0xa0 && code <= 0xa7) ||
+ (code >= 0xd0 && code <= 0xd5))
+ return 1;
+
+ return 0;
+}
+
static void asus_wmi_notify(u32 value, void *context)
{
struct asus_wmi *asus = context;
@@ -1280,16 +1361,24 @@ static void asus_wmi_notify(u32 value, void *context)
}
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
- code = NOTIFY_BRNUP_MIN;
+ code = ASUS_WMI_BRN_UP;
else if (code >= NOTIFY_BRNDOWN_MIN &&
code <= NOTIFY_BRNDOWN_MAX)
- code = NOTIFY_BRNDOWN_MIN;
+ code = ASUS_WMI_BRN_DOWN;
- if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
- if (!acpi_video_backlight_support())
+ if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) {
+ if (!acpi_video_backlight_support()) {
asus_wmi_backlight_notify(asus, orig_code);
- } else if (!sparse_keymap_report_event(asus->inputdev, code,
- key_value, autorelease))
+ goto exit;
+ }
+ }
+
+ if (is_display_toggle(code) &&
+ asus->driver->quirks->no_display_toggle)
+ goto exit;
+
+ if (!sparse_keymap_report_event(asus->inputdev, code,
+ key_value, autorelease))
pr_info("Unknown key %x pressed\n", code);
exit:
@@ -1365,6 +1454,7 @@ static ssize_t show_sys_wmi(struct asus_wmi *asus, int devid, char *buf)
ASUS_WMI_CREATE_DEVICE_ATTR(touchpad, 0644, ASUS_WMI_DEVID_TOUCHPAD);
ASUS_WMI_CREATE_DEVICE_ATTR(camera, 0644, ASUS_WMI_DEVID_CAMERA);
ASUS_WMI_CREATE_DEVICE_ATTR(cardr, 0644, ASUS_WMI_DEVID_CARDREADER);
+ASUS_WMI_CREATE_DEVICE_ATTR(lid_resume, 0644, ASUS_WMI_DEVID_LID_RESUME);
static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
@@ -1390,6 +1480,7 @@ static struct attribute *platform_attributes[] = {
&dev_attr_camera.attr,
&dev_attr_cardr.attr,
&dev_attr_touchpad.attr,
+ &dev_attr_lid_resume.attr,
NULL
};
@@ -1408,6 +1499,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
devid = ASUS_WMI_DEVID_CARDREADER;
else if (attr == &dev_attr_touchpad.attr)
devid = ASUS_WMI_DEVID_TOUCHPAD;
+ else if (attr == &dev_attr_lid_resume.attr)
+ devid = ASUS_WMI_DEVID_LID_RESUME;
if (devid != -1)
ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0);
@@ -1439,11 +1532,11 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
/* INIT enable hotkeys on some models */
if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_INIT, 0, 0, &rv))
- pr_info("Initialization: %#x", rv);
+ pr_info("Initialization: %#x\n", rv);
/* We don't know yet what to do with this version... */
if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SPEC, 0, 0x9, &rv)) {
- pr_info("BIOS WMI version: %d.%d", rv >> 16, rv & 0xFF);
+ pr_info("BIOS WMI version: %d.%d\n", rv >> 16, rv & 0xFF);
asus->spec = rv;
}
@@ -1454,7 +1547,7 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
* The significance of others is yet to be found.
*/
if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_SFUN, 0, 0, &rv)) {
- pr_info("SFUN value: %#x", rv);
+ pr_info("SFUN value: %#x\n", rv);
asus->sfun = rv;
}
@@ -1467,14 +1560,9 @@ static int asus_wmi_platform_init(struct asus_wmi *asus)
*/
if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS, 0, 0, NULL))
asus->dsts_id = ASUS_WMI_METHODID_DSTS;
- else if (!asus_wmi_evaluate_method(ASUS_WMI_METHODID_DSTS2, 0, 0, NULL))
+ else
asus->dsts_id = ASUS_WMI_METHODID_DSTS2;
- if (!asus->dsts_id) {
- pr_err("Can't find DSTS");
- return -ENODEV;
- }
-
/* CWAP allow to define the behavior of the Fn+F2 key,
* this method doesn't seems to be present on Eee PCs */
if (asus->driver->quirks->wapf >= 0)
@@ -1599,7 +1687,7 @@ static int asus_wmi_debugfs_init(struct asus_wmi *asus)
asus->debug.root = debugfs_create_dir(asus->driver->name, NULL);
if (!asus->debug.root) {
- pr_err("failed to create debugfs directory");
+ pr_err("failed to create debugfs directory\n");
goto error_debugfs;
}
@@ -1648,6 +1736,7 @@ static int asus_wmi_add(struct platform_device *pdev)
struct asus_wmi *asus;
acpi_status status;
int err;
+ u32 result;
asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
if (!asus)
@@ -1681,7 +1770,11 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_rfkill;
+ if (asus->driver->quirks->wmi_backlight_power)
+ acpi_video_dmi_promote_vendor();
if (!acpi_video_backlight_support()) {
+ pr_info("Disabling ACPI video driver\n");
+ acpi_video_unregister();
err = asus_wmi_backlight_init(asus);
if (err && err != -ENODEV)
goto fail_backlight;
@@ -1700,6 +1793,10 @@ static int asus_wmi_add(struct platform_device *pdev)
if (err)
goto fail_debugfs;
+ asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
+ if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
+ asus->driver->wlan_ctrl_by_user = 1;
+
return 0;
fail_debugfs:
@@ -1711,7 +1808,6 @@ fail_backlight:
fail_rfkill:
asus_wmi_led_exit(asus);
fail_leds:
- asus_wmi_hwmon_exit(asus);
fail_hwmon:
asus_wmi_input_exit(asus);
fail_input:
@@ -1729,7 +1825,6 @@ static int asus_wmi_remove(struct platform_device *device)
wmi_remove_notify_handler(asus->driver->event_guid);
asus_wmi_backlight_exit(asus);
asus_wmi_input_exit(asus);
- asus_wmi_hwmon_exit(asus);
asus_wmi_led_exit(asus);
asus_wmi_rfkill_exit(asus);
asus_wmi_debugfs_exit(asus);
@@ -1863,17 +1958,17 @@ EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver);
static int __init asus_wmi_init(void)
{
if (!wmi_has_guid(ASUS_WMI_MGMT_GUID)) {
- pr_info("Asus Management GUID not found");
+ pr_info("Asus Management GUID not found\n");
return -ENODEV;
}
- pr_info("ASUS WMI generic driver loaded");
+ pr_info("ASUS WMI generic driver loaded\n");
return 0;
}
static void __exit asus_wmi_exit(void)
{
- pr_info("ASUS WMI generic driver unloaded");
+ pr_info("ASUS WMI generic driver unloaded\n");
}
module_init(asus_wmi_init);
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index d43b6674200..4da4c8bafe7 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -30,6 +30,8 @@
#include <linux/platform_device.h>
#define ASUS_WMI_KEY_IGNORE (-1)
+#define ASUS_WMI_BRN_DOWN 0x20
+#define ASUS_WMI_BRN_UP 0x2f
struct module;
struct key_entry;
@@ -39,12 +41,21 @@ struct quirk_entry {
bool hotplug_wireless;
bool scalar_panel_brightness;
bool store_backlight_power;
+ bool wmi_backlight_power;
int wapf;
+ /*
+ * For machines with AMD graphic chips, it will send out WMI event
+ * and ACPI interrupt at the same time while hitting the hotkey.
+ * To simplify the problem, we just have to ignore the WMI event,
+ * and let the ACPI interrupt to send out the key event.
+ */
+ int no_display_toggle;
};
struct asus_wmi_driver {
int brightness;
int panel_power;
+ int wlan_ctrl_by_user;
const char *name;
struct module *owner;
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index 94f93b621d7..70d355a9ae2 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -21,25 +21,30 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <acpi/acpi_drivers.h>
+#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_SENSITIVITY_DEFAULT 5
+#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"
+#define CMPC_KEYS_HID "FNBT0000"
/*
* Generic input device code.
@@ -76,7 +81,393 @@ static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
}
/*
- * Accelerometer code.
+ * 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)
+{
+ struct input_dev *inputdev;
+ struct cmpc_accel *accel;
+
+ inputdev = dev_get_drvdata(&acpi->dev);
+ accel = dev_get_drvdata(&inputdev->dev);
+
+ 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)
{
@@ -198,7 +589,7 @@ static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
inputdev = dev_get_drvdata(&acpi->dev);
accel = dev_get_drvdata(&inputdev->dev);
- r = strict_strtoul(buf, 0, &sensitivity);
+ r = kstrtoul(buf, 0, &sensitivity);
if (r)
return r;
@@ -276,7 +667,7 @@ failed_file:
return error;
}
-static int cmpc_accel_remove(struct acpi_device *acpi, int type)
+static int cmpc_accel_remove(struct acpi_device *acpi)
{
struct input_dev *inputdev;
struct cmpc_accel *accel;
@@ -333,8 +724,10 @@ static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
if (event == 0x81) {
- if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
+ if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
+ input_sync(inputdev);
+ }
}
}
@@ -347,8 +740,10 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev)
set_bit(SW_TABLET_MODE, inputdev->swbit);
acpi = to_acpi_device(inputdev->dev.parent);
- if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
+ 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)
@@ -357,19 +752,26 @@ static int cmpc_tablet_add(struct acpi_device *acpi)
cmpc_tablet_idev_init);
}
-static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
+static int cmpc_tablet_remove(struct acpi_device *acpi)
{
return cmpc_remove_acpi_notify_device(acpi);
}
-static int cmpc_tablet_resume(struct acpi_device *acpi)
+#ifdef CONFIG_PM_SLEEP
+static int cmpc_tablet_resume(struct device *dev)
{
- struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
+ struct input_dev *inputdev = dev_get_drvdata(dev);
+
unsigned long long val = 0;
- if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
+ 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},
@@ -384,9 +786,9 @@ static struct acpi_driver cmpc_tablet_acpi_driver = {
.ops = {
.add = cmpc_tablet_add,
.remove = cmpc_tablet_remove,
- .resume = cmpc_tablet_resume,
.notify = cmpc_tablet_handler,
- }
+ },
+ .drv.pm = &cmpc_tablet_pm,
};
@@ -597,7 +999,7 @@ out_bd:
return retval;
}
-static int cmpc_ipml_remove(struct acpi_device *acpi, int type)
+static int cmpc_ipml_remove(struct acpi_device *acpi)
{
struct ipml200_dev *ipml;
@@ -676,7 +1078,7 @@ static int cmpc_keys_add(struct acpi_device *acpi)
cmpc_keys_idev_init);
}
-static int cmpc_keys_remove(struct acpi_device *acpi, int type)
+static int cmpc_keys_remove(struct acpi_device *acpi)
{
return cmpc_remove_acpi_notify_device(acpi);
}
@@ -723,8 +1125,15 @@ static int cmpc_init(void)
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);
@@ -740,6 +1149,7 @@ failed_keys:
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);
@@ -751,6 +1161,7 @@ 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},
diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c
index 1887e2f166a..7297df2ebf5 100644
--- a/drivers/platform/x86/compal-laptop.c
+++ b/drivers/platform/x86/compal-laptop.c
@@ -173,8 +173,7 @@
/* ======= */
struct compal_data{
/* Fan control */
- struct device *hwmon_dev;
- int pwm_enable; /* 0:full on, 1:set by pwm1, 2:control by moterboard */
+ int pwm_enable; /* 0:full on, 1:set by pwm1, 2:control by motherboard */
unsigned char curr_pwm;
/* Power supply */
@@ -402,15 +401,6 @@ SIMPLE_MASKED_STORE_SHOW(wake_up_wlan, WAKE_UP_ADDR, WAKE_UP_WLAN)
SIMPLE_MASKED_STORE_SHOW(wake_up_key, WAKE_UP_ADDR, WAKE_UP_KEY)
SIMPLE_MASKED_STORE_SHOW(wake_up_mouse, WAKE_UP_ADDR, WAKE_UP_MOUSE)
-
-/* General hwmon interface */
-static ssize_t hwmon_name_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "%s\n", DRIVER_NAME);
-}
-
-
/* Fan control interface */
static ssize_t pwm_enable_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -425,7 +415,8 @@ static ssize_t pwm_enable_store(struct device *dev,
struct compal_data *data = dev_get_drvdata(dev);
long val;
int err;
- err = strict_strtol(buf, 10, &val);
+
+ err = kstrtol(buf, 10, &val);
if (err)
return err;
if (val < 0)
@@ -463,7 +454,8 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *attr,
struct compal_data *data = dev_get_drvdata(dev);
long val;
int err;
- err = strict_strtol(buf, 10, &val);
+
+ err = kstrtol(buf, 10, &val);
if (err)
return err;
if (val < 0 || val > 255)
@@ -663,65 +655,65 @@ static DEVICE_ATTR(wake_up_key,
static DEVICE_ATTR(wake_up_mouse,
0644, wake_up_mouse_show, wake_up_mouse_store);
-static SENSOR_DEVICE_ATTR(name, S_IRUGO, hwmon_name_show, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, fan_show, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO, label_vga, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN, NULL, 1);
-static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store, 1);
-static SENSOR_DEVICE_ATTR(pwm1_enable,
- S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store, 0);
-
-static struct attribute *compal_attributes[] = {
+static DEVICE_ATTR(fan1_input, S_IRUGO, fan_show, NULL);
+static DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu, NULL);
+static DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local, NULL);
+static DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS, NULL);
+static DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge, NULL);
+static DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga, NULL);
+static DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN, NULL);
+static DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu, NULL);
+static DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local, NULL);
+static DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS, NULL);
+static DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL);
+static DEVICE_ATTR(temp5_label, S_IRUGO, label_vga, NULL);
+static DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN, NULL);
+static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store);
+static DEVICE_ATTR(pwm1_enable,
+ S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store);
+
+static struct attribute *compal_platform_attrs[] = {
&dev_attr_wake_up_pme.attr,
&dev_attr_wake_up_modem.attr,
&dev_attr_wake_up_lan.attr,
&dev_attr_wake_up_wlan.attr,
&dev_attr_wake_up_key.attr,
&dev_attr_wake_up_mouse.attr,
- /* Maybe put the sensor-stuff in a separate hwmon-driver? That way,
- * the hwmon sysfs won't be cluttered with the above files. */
- &sensor_dev_attr_name.dev_attr.attr,
- &sensor_dev_attr_pwm1_enable.dev_attr.attr,
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_fan1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp5_input.dev_attr.attr,
- &sensor_dev_attr_temp6_input.dev_attr.attr,
- &sensor_dev_attr_temp1_label.dev_attr.attr,
- &sensor_dev_attr_temp2_label.dev_attr.attr,
- &sensor_dev_attr_temp3_label.dev_attr.attr,
- &sensor_dev_attr_temp4_label.dev_attr.attr,
- &sensor_dev_attr_temp5_label.dev_attr.attr,
- &sensor_dev_attr_temp6_label.dev_attr.attr,
NULL
};
+static struct attribute_group compal_platform_attr_group = {
+ .attrs = compal_platform_attrs
+};
-static struct attribute_group compal_attribute_group = {
- .attrs = compal_attributes
+static struct attribute *compal_hwmon_attrs[] = {
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp4_input.attr,
+ &dev_attr_temp5_input.attr,
+ &dev_attr_temp6_input.attr,
+ &dev_attr_temp1_label.attr,
+ &dev_attr_temp2_label.attr,
+ &dev_attr_temp3_label.attr,
+ &dev_attr_temp4_label.attr,
+ &dev_attr_temp5_label.attr,
+ &dev_attr_temp6_label.attr,
+ NULL
};
+ATTRIBUTE_GROUPS(compal_hwmon);
-static int __devinit compal_probe(struct platform_device *);
-static int __devexit compal_remove(struct platform_device *);
+static int compal_probe(struct platform_device *);
+static int compal_remove(struct platform_device *);
static struct platform_driver compal_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.probe = compal_probe,
- .remove = __devexit_p(compal_remove)
+ .remove = compal_remove,
};
static enum power_supply_property compal_bat_properties[] = {
@@ -1015,34 +1007,32 @@ err_backlight:
return ret;
}
-static int __devinit compal_probe(struct platform_device *pdev)
+static int compal_probe(struct platform_device *pdev)
{
int err;
struct compal_data *data;
+ struct device *hwmon_dev;
if (!extra_features)
return 0;
/* Fan control */
- data = kzalloc(sizeof(struct compal_data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(struct compal_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
initialize_fan_control_data(data);
- err = sysfs_create_group(&pdev->dev.kobj, &compal_attribute_group);
- if (err) {
- kfree(data);
+ err = sysfs_create_group(&pdev->dev.kobj, &compal_platform_attr_group);
+ if (err)
return err;
- }
- data->hwmon_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- sysfs_remove_group(&pdev->dev.kobj,
- &compal_attribute_group);
- kfree(data);
- return err;
+ hwmon_dev = hwmon_device_register_with_groups(&pdev->dev,
+ DRIVER_NAME, data,
+ compal_hwmon_groups);
+ if (IS_ERR(hwmon_dev)) {
+ err = PTR_ERR(hwmon_dev);
+ goto remove;
}
/* Power supply */
@@ -1052,6 +1042,10 @@ static int __devinit compal_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
return 0;
+
+remove:
+ sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
+ return err;
}
static void __exit compal_cleanup(void)
@@ -1067,7 +1061,7 @@ static void __exit compal_cleanup(void)
pr_info("Driver unloaded\n");
}
-static int __devexit compal_remove(struct platform_device *pdev)
+static int compal_remove(struct platform_device *pdev)
{
struct compal_data *data;
@@ -1078,13 +1072,9 @@ static int __devexit compal_remove(struct platform_device *pdev)
pwm_disable_control();
data = platform_get_drvdata(pdev);
- hwmon_device_unregister(data->hwmon_dev);
power_supply_unregister(&data->psy);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
-
- sysfs_remove_group(&pdev->dev.kobj, &compal_attribute_group);
+ sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);
return 0;
}
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 5f78aac9b16..fed4111ac31 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -21,6 +21,7 @@
#include <linux/err.h>
#include <linux/dmi.h>
#include <linux/io.h>
+#include <linux/rfkill.h>
#include <linux/power_supply.h>
#include <linux/acpi.h>
#include <linux/mm.h>
@@ -89,6 +90,13 @@ static struct platform_driver platform_driver = {
static struct platform_device *platform_device;
static struct backlight_device *dell_backlight_device;
+static struct rfkill *wifi_rfkill;
+static struct rfkill *bluetooth_rfkill;
+static struct rfkill *wwan_rfkill;
+static bool force_rfkill;
+
+module_param(force_rfkill, bool, 0444);
+MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");
static const struct dmi_system_id dell_device_table[] __initconst = {
{
@@ -115,7 +123,7 @@ static const struct dmi_system_id dell_device_table[] __initconst = {
};
MODULE_DEVICE_TABLE(dmi, dell_device_table);
-static struct dmi_system_id __devinitdata dell_quirks[] = {
+static struct dmi_system_id dell_quirks[] = {
{
.callback = dmi_matched,
.ident = "Dell Vostro V130",
@@ -206,6 +214,60 @@ static struct dmi_system_id __devinitdata dell_quirks[] = {
},
.driver_data = &quirk_dell_vostro_v130,
},
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 5420",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5420"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 5520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5520"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 5720",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5720"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 7420",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7420"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 7520",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "Dell Inspiron 7720",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
+ },
+ .driver_data = &quirk_dell_vostro_v130,
+ },
{ }
};
@@ -230,6 +292,7 @@ static void __init parse_da_table(const struct dmi_header *dm)
{
/* Final token is a terminator, so we don't want to copy it */
int tokens = (dm->length-11)/sizeof(struct calling_interface_token)-1;
+ struct calling_interface_token *new_da_tokens;
struct calling_interface_structure *table =
container_of(dm, struct calling_interface_structure, header);
@@ -242,12 +305,13 @@ static void __init parse_da_table(const struct dmi_header *dm)
da_command_address = table->cmdIOAddress;
da_command_code = table->cmdIOCode;
- da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
- sizeof(struct calling_interface_token),
- GFP_KERNEL);
+ new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
+ sizeof(struct calling_interface_token),
+ GFP_KERNEL);
- if (!da_tokens)
+ if (!new_da_tokens)
return;
+ da_tokens = new_da_tokens;
memcpy(da_tokens+da_num_tokens, table->tokens,
sizeof(struct calling_interface_token) * tokens);
@@ -299,6 +363,108 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,
return buffer;
}
+/* Derived from information in DellWirelessCtl.cpp:
+ Class 17, select 11 is radio control. It returns an array of 32-bit values.
+
+ Input byte 0 = 0: Wireless information
+
+ result[0]: return code
+ result[1]:
+ Bit 0: Hardware switch supported
+ Bit 1: Wifi locator supported
+ Bit 2: Wifi is supported
+ Bit 3: Bluetooth is supported
+ Bit 4: WWAN is supported
+ Bit 5: Wireless keyboard supported
+ Bits 6-7: Reserved
+ Bit 8: Wifi is installed
+ Bit 9: Bluetooth is installed
+ Bit 10: WWAN is installed
+ Bits 11-15: Reserved
+ Bit 16: Hardware switch is on
+ Bit 17: Wifi is blocked
+ Bit 18: Bluetooth is blocked
+ Bit 19: WWAN is blocked
+ Bits 20-31: Reserved
+ result[2]: NVRAM size in bytes
+ result[3]: NVRAM format version number
+
+ Input byte 0 = 2: Wireless switch configuration
+ result[0]: return code
+ result[1]:
+ Bit 0: Wifi controlled by switch
+ Bit 1: Bluetooth controlled by switch
+ Bit 2: WWAN controlled by switch
+ Bits 3-6: Reserved
+ Bit 7: Wireless switch config locked
+ Bit 8: Wifi locator enabled
+ Bits 9-14: Reserved
+ Bit 15: Wifi locator setting locked
+ Bits 16-31: Reserved
+*/
+
+static int dell_rfkill_set(void *data, bool blocked)
+{
+ int disable = blocked ? 1 : 0;
+ unsigned long radio = (unsigned long)data;
+ int hwswitch_bit = (unsigned long)data - 1;
+
+ get_buffer();
+ dell_send_request(buffer, 17, 11);
+
+ /* If the hardware switch controls this radio, and the hardware
+ switch is disabled, always disable the radio */
+ if ((hwswitch_state & BIT(hwswitch_bit)) &&
+ !(buffer->output[1] & BIT(16)))
+ disable = 1;
+
+ buffer->input[0] = (1 | (radio<<8) | (disable << 16));
+ dell_send_request(buffer, 17, 11);
+
+ release_buffer();
+ return 0;
+}
+
+/* Must be called with the buffer held */
+static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
+ int status)
+{
+ if (status & BIT(0)) {
+ /* Has hw-switch, sync sw_state to BIOS */
+ int block = rfkill_blocked(rfkill);
+ buffer->input[0] = (1 | (radio << 8) | (block << 16));
+ dell_send_request(buffer, 17, 11);
+ } else {
+ /* No hw-switch, sync BIOS state to sw_state */
+ rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
+ }
+}
+
+static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
+ int status)
+{
+ if (hwswitch_state & (BIT(radio - 1)))
+ rfkill_set_hw_state(rfkill, !(status & BIT(16)));
+}
+
+static void dell_rfkill_query(struct rfkill *rfkill, void *data)
+{
+ int status;
+
+ get_buffer();
+ dell_send_request(buffer, 17, 11);
+ status = buffer->output[1];
+
+ dell_rfkill_update_hw_state(rfkill, (unsigned long)data, status);
+
+ release_buffer();
+}
+
+static const struct rfkill_ops dell_rfkill_ops = {
+ .set_block = dell_rfkill_set,
+ .query = dell_rfkill_query,
+};
+
static struct dentry *dell_laptop_dir;
static int dell_debugfs_show(struct seq_file *s, void *data)
@@ -368,6 +534,171 @@ static const struct file_operations dell_debugfs_fops = {
.release = single_release,
};
+static void dell_update_rfkill(struct work_struct *ignored)
+{
+ int status;
+
+ get_buffer();
+ dell_send_request(buffer, 17, 11);
+ status = buffer->output[1];
+
+ if (wifi_rfkill) {
+ dell_rfkill_update_hw_state(wifi_rfkill, 1, status);
+ dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
+ }
+ if (bluetooth_rfkill) {
+ dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status);
+ dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
+ }
+ if (wwan_rfkill) {
+ dell_rfkill_update_hw_state(wwan_rfkill, 3, status);
+ dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
+ }
+
+ release_buffer();
+}
+static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
+
+static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
+ struct serio *port)
+{
+ static bool extended;
+
+ if (str & 0x20)
+ return false;
+
+ if (unlikely(data == 0xe0)) {
+ extended = true;
+ return false;
+ } else if (unlikely(extended)) {
+ switch (data) {
+ case 0x8:
+ schedule_delayed_work(&dell_rfkill_work,
+ round_jiffies_relative(HZ / 4));
+ break;
+ }
+ extended = false;
+ }
+
+ return false;
+}
+
+static int __init dell_setup_rfkill(void)
+{
+ int status, ret, whitelisted;
+ const char *product;
+
+ /*
+ * rfkill support causes trouble on various models, mostly Inspirons.
+ * So we whitelist certain series, and don't support rfkill on others.
+ */
+ whitelisted = 0;
+ product = dmi_get_system_info(DMI_PRODUCT_NAME);
+ if (product && (strncmp(product, "Latitude", 8) == 0 ||
+ strncmp(product, "Precision", 9) == 0))
+ whitelisted = 1;
+ if (!force_rfkill && !whitelisted)
+ return 0;
+
+ get_buffer();
+ dell_send_request(buffer, 17, 11);
+ status = buffer->output[1];
+ buffer->input[0] = 0x2;
+ dell_send_request(buffer, 17, 11);
+ hwswitch_state = buffer->output[1];
+ release_buffer();
+
+ if (!(status & BIT(0))) {
+ if (force_rfkill) {
+ /* No hwsitch, clear all hw-controlled bits */
+ hwswitch_state &= ~7;
+ } else {
+ /* rfkill is only tested on laptops with a hwswitch */
+ return 0;
+ }
+ }
+
+ if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) {
+ wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev,
+ RFKILL_TYPE_WLAN,
+ &dell_rfkill_ops, (void *) 1);
+ if (!wifi_rfkill) {
+ ret = -ENOMEM;
+ goto err_wifi;
+ }
+ ret = rfkill_register(wifi_rfkill);
+ if (ret)
+ goto err_wifi;
+ }
+
+ if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) {
+ bluetooth_rfkill = rfkill_alloc("dell-bluetooth",
+ &platform_device->dev,
+ RFKILL_TYPE_BLUETOOTH,
+ &dell_rfkill_ops, (void *) 2);
+ if (!bluetooth_rfkill) {
+ ret = -ENOMEM;
+ goto err_bluetooth;
+ }
+ ret = rfkill_register(bluetooth_rfkill);
+ if (ret)
+ goto err_bluetooth;
+ }
+
+ if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) {
+ wwan_rfkill = rfkill_alloc("dell-wwan",
+ &platform_device->dev,
+ RFKILL_TYPE_WWAN,
+ &dell_rfkill_ops, (void *) 3);
+ if (!wwan_rfkill) {
+ ret = -ENOMEM;
+ goto err_wwan;
+ }
+ ret = rfkill_register(wwan_rfkill);
+ if (ret)
+ goto err_wwan;
+ }
+
+ ret = i8042_install_filter(dell_laptop_i8042_filter);
+ if (ret) {
+ pr_warn("Unable to install key filter\n");
+ goto err_filter;
+ }
+
+ return 0;
+err_filter:
+ if (wwan_rfkill)
+ rfkill_unregister(wwan_rfkill);
+err_wwan:
+ rfkill_destroy(wwan_rfkill);
+ if (bluetooth_rfkill)
+ rfkill_unregister(bluetooth_rfkill);
+err_bluetooth:
+ rfkill_destroy(bluetooth_rfkill);
+ if (wifi_rfkill)
+ rfkill_unregister(wifi_rfkill);
+err_wifi:
+ rfkill_destroy(wifi_rfkill);
+
+ return ret;
+}
+
+static void dell_cleanup_rfkill(void)
+{
+ if (wifi_rfkill) {
+ rfkill_unregister(wifi_rfkill);
+ rfkill_destroy(wifi_rfkill);
+ }
+ if (bluetooth_rfkill) {
+ rfkill_unregister(bluetooth_rfkill);
+ rfkill_destroy(bluetooth_rfkill);
+ }
+ if (wwan_rfkill) {
+ rfkill_unregister(wwan_rfkill);
+ rfkill_destroy(wwan_rfkill);
+ }
+}
+
static int dell_send_intensity(struct backlight_device *bd)
{
int ret = 0;
@@ -388,7 +719,7 @@ static int dell_send_intensity(struct backlight_device *bd)
out:
release_buffer();
- return 0;
+ return ret;
}
static int dell_get_intensity(struct backlight_device *bd)
@@ -449,7 +780,7 @@ static struct led_classdev touchpad_led = {
.flags = LED_CORE_SUSPENDRESUME,
};
-static int __devinit touchpad_led_init(struct device *dev)
+static int touchpad_led_init(struct device *dev)
{
return led_classdev_register(dev, &touchpad_led);
}
@@ -495,15 +826,26 @@ static int __init dell_init(void)
* is passed to SMI handler.
*/
bufferpage = alloc_page(GFP_KERNEL | GFP_DMA32);
-
- if (!bufferpage)
+ if (!bufferpage) {
+ ret = -ENOMEM;
goto fail_buffer;
+ }
buffer = page_address(bufferpage);
+ ret = dell_setup_rfkill();
+
+ if (ret) {
+ pr_warn("Unable to setup rfkill\n");
+ goto fail_rfkill;
+ }
+
if (quirks && quirks->touchpad_led)
touchpad_led_init(&platform_device->dev);
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
+ if (dell_laptop_dir != NULL)
+ debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
+ &dell_debugfs_fops);
#ifdef CONFIG_ACPI
/* In the event of an ACPI backlight being available, don't
@@ -546,6 +888,10 @@ static int __init dell_init(void)
return 0;
fail_backlight:
+ i8042_remove_filter(dell_laptop_i8042_filter);
+ cancel_delayed_work_sync(&dell_rfkill_work);
+ dell_cleanup_rfkill();
+fail_rfkill:
free_page((unsigned long)bufferpage);
fail_buffer:
platform_device_del(platform_device);
@@ -563,7 +909,10 @@ static void __exit dell_exit(void)
debugfs_remove_recursive(dell_laptop_dir);
if (quirks && quirks->touchpad_led)
touchpad_led_exit();
+ i8042_remove_filter(dell_laptop_i8042_filter);
+ cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device);
+ dell_cleanup_rfkill();
if (platform_device) {
platform_device_unregister(platform_device);
platform_driver_unregister(&platform_driver);
diff --git a/drivers/platform/x86/dell-smo8800.c b/drivers/platform/x86/dell-smo8800.c
new file mode 100644
index 00000000000..a653716055d
--- /dev/null
+++ b/drivers/platform/x86/dell-smo8800.c
@@ -0,0 +1,233 @@
+/*
+ * dell-smo8800.c - Dell Latitude ACPI SMO8800/SMO8810 freefall sensor driver
+ *
+ * Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com>
+ * Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * This is loosely based on lis3lv02d driver.
+ *
+ * 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.
+ */
+
+#define DRIVER_NAME "smo8800"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/interrupt.h>
+#include <linux/miscdevice.h>
+
+struct smo8800_device {
+ u32 irq; /* acpi device irq */
+ atomic_t counter; /* count after last read */
+ struct miscdevice miscdev; /* for /dev/freefall */
+ unsigned long misc_opened; /* whether the device is open */
+ wait_queue_head_t misc_wait; /* Wait queue for the misc dev */
+ struct device *dev; /* acpi device */
+};
+
+static irqreturn_t smo8800_interrupt_quick(int irq, void *data)
+{
+ struct smo8800_device *smo8800 = data;
+
+ atomic_inc(&smo8800->counter);
+ wake_up_interruptible(&smo8800->misc_wait);
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t smo8800_interrupt_thread(int irq, void *data)
+{
+ struct smo8800_device *smo8800 = data;
+
+ dev_info(smo8800->dev, "detected free fall\n");
+ return IRQ_HANDLED;
+}
+
+static acpi_status smo8800_get_resource(struct acpi_resource *resource,
+ void *context)
+{
+ struct acpi_resource_extended_irq *irq;
+
+ if (resource->type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ)
+ return AE_OK;
+
+ irq = &resource->data.extended_irq;
+ if (!irq || !irq->interrupt_count)
+ return AE_OK;
+
+ *((u32 *)context) = irq->interrupts[0];
+ return AE_CTRL_TERMINATE;
+}
+
+static u32 smo8800_get_irq(struct acpi_device *device)
+{
+ u32 irq = 0;
+ acpi_status status;
+
+ status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
+ smo8800_get_resource, &irq);
+ if (ACPI_FAILURE(status)) {
+ dev_err(&device->dev, "acpi_walk_resources failed\n");
+ return 0;
+ }
+
+ return irq;
+}
+
+static ssize_t smo8800_misc_read(struct file *file, char __user *buf,
+ size_t count, loff_t *pos)
+{
+ struct smo8800_device *smo8800 = container_of(file->private_data,
+ struct smo8800_device, miscdev);
+
+ u32 data = 0;
+ unsigned char byte_data = 0;
+ ssize_t retval = 1;
+
+ if (count < 1)
+ return -EINVAL;
+
+ atomic_set(&smo8800->counter, 0);
+ retval = wait_event_interruptible(smo8800->misc_wait,
+ (data = atomic_xchg(&smo8800->counter, 0)));
+
+ if (retval)
+ return retval;
+
+ byte_data = 1;
+ retval = 1;
+
+ if (data < 255)
+ byte_data = data;
+ else
+ byte_data = 255;
+
+ if (put_user(byte_data, buf))
+ retval = -EFAULT;
+
+ return retval;
+}
+
+static int smo8800_misc_open(struct inode *inode, struct file *file)
+{
+ struct smo8800_device *smo8800 = container_of(file->private_data,
+ struct smo8800_device, miscdev);
+
+ if (test_and_set_bit(0, &smo8800->misc_opened))
+ return -EBUSY; /* already open */
+
+ atomic_set(&smo8800->counter, 0);
+ return 0;
+}
+
+static int smo8800_misc_release(struct inode *inode, struct file *file)
+{
+ struct smo8800_device *smo8800 = container_of(file->private_data,
+ struct smo8800_device, miscdev);
+
+ clear_bit(0, &smo8800->misc_opened); /* release the device */
+ return 0;
+}
+
+static const struct file_operations smo8800_misc_fops = {
+ .owner = THIS_MODULE,
+ .read = smo8800_misc_read,
+ .open = smo8800_misc_open,
+ .release = smo8800_misc_release,
+};
+
+static int smo8800_add(struct acpi_device *device)
+{
+ int err;
+ struct smo8800_device *smo8800;
+
+ smo8800 = devm_kzalloc(&device->dev, sizeof(*smo8800), GFP_KERNEL);
+ if (!smo8800) {
+ dev_err(&device->dev, "failed to allocate device data\n");
+ return -ENOMEM;
+ }
+
+ smo8800->dev = &device->dev;
+ smo8800->miscdev.minor = MISC_DYNAMIC_MINOR;
+ smo8800->miscdev.name = "freefall";
+ smo8800->miscdev.fops = &smo8800_misc_fops;
+
+ init_waitqueue_head(&smo8800->misc_wait);
+
+ err = misc_register(&smo8800->miscdev);
+ if (err) {
+ dev_err(&device->dev, "failed to register misc dev: %d\n", err);
+ return err;
+ }
+
+ device->driver_data = smo8800;
+
+ smo8800->irq = smo8800_get_irq(device);
+ if (!smo8800->irq) {
+ dev_err(&device->dev, "failed to obtain IRQ\n");
+ err = -EINVAL;
+ goto error;
+ }
+
+ err = request_threaded_irq(smo8800->irq, smo8800_interrupt_quick,
+ smo8800_interrupt_thread,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ DRIVER_NAME, smo8800);
+ if (err) {
+ dev_err(&device->dev,
+ "failed to request thread for IRQ %d: %d\n",
+ smo8800->irq, err);
+ goto error;
+ }
+
+ dev_dbg(&device->dev, "device /dev/freefall registered with IRQ %d\n",
+ smo8800->irq);
+ return 0;
+
+error:
+ misc_deregister(&smo8800->miscdev);
+ return err;
+}
+
+static int smo8800_remove(struct acpi_device *device)
+{
+ struct smo8800_device *smo8800 = device->driver_data;
+
+ free_irq(smo8800->irq, smo8800);
+ misc_deregister(&smo8800->miscdev);
+ dev_dbg(&device->dev, "device /dev/freefall unregistered\n");
+ return 0;
+}
+
+static const struct acpi_device_id smo8800_ids[] = {
+ { "SMO8800", 0 },
+ { "SMO8810", 0 },
+ { "", 0 },
+};
+
+MODULE_DEVICE_TABLE(acpi, smo8800_ids);
+
+static struct acpi_driver smo8800_driver = {
+ .name = DRIVER_NAME,
+ .class = "Latitude",
+ .ids = smo8800_ids,
+ .ops = {
+ .add = smo8800_add,
+ .remove = smo8800_remove,
+ },
+ .owner = THIS_MODULE,
+};
+
+module_acpi_driver(smo8800_driver);
+
+MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO8800/SMO8810)");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sonal Santan, Pali Rohár");
diff --git a/drivers/platform/x86/dell-wmi-aio.c b/drivers/platform/x86/dell-wmi-aio.c
index 3f945457f71..dbc97a33bbc 100644
--- a/drivers/platform/x86/dell-wmi-aio.c
+++ b/drivers/platform/x86/dell-wmi-aio.c
@@ -24,7 +24,6 @@
#include <linux/types.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
-#include <acpi/acpi_drivers.h>
#include <linux/acpi.h>
#include <linux/string.h>
@@ -34,6 +33,14 @@ MODULE_LICENSE("GPL");
#define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4"
#define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8"
+struct dell_wmi_event {
+ u16 length;
+ /* 0x000: A hot key pressed or an event occurred
+ * 0x00F: A sequence of hot keys are pressed */
+ u16 type;
+ u16 event[];
+};
+
static const char *dell_wmi_aio_guids[] = {
EVENT_GUID1,
EVENT_GUID2,
@@ -46,15 +53,41 @@ MODULE_ALIAS("wmi:"EVENT_GUID2);
static const struct key_entry dell_wmi_aio_keymap[] = {
{ KE_KEY, 0xc0, { KEY_VOLUMEUP } },
{ KE_KEY, 0xc1, { KEY_VOLUMEDOWN } },
+ { KE_KEY, 0xe030, { KEY_VOLUMEUP } },
+ { KE_KEY, 0xe02e, { KEY_VOLUMEDOWN } },
+ { KE_KEY, 0xe020, { KEY_MUTE } },
+ { KE_KEY, 0xe027, { KEY_DISPLAYTOGGLE } },
+ { KE_KEY, 0xe006, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, 0xe005, { KEY_BRIGHTNESSDOWN } },
+ { KE_KEY, 0xe00b, { KEY_SWITCHVIDEOMODE } },
{ KE_END, 0 }
};
static struct input_dev *dell_wmi_aio_input_dev;
+/*
+ * The new WMI event data format will follow the dell_wmi_event structure
+ * So, we will check if the buffer matches the format
+ */
+static bool dell_wmi_aio_event_check(u8 *buffer, int length)
+{
+ struct dell_wmi_event *event = (struct dell_wmi_event *)buffer;
+
+ if (event == NULL || length < 6)
+ return false;
+
+ if ((event->type == 0 || event->type == 0xf) &&
+ event->length >= 2)
+ return true;
+
+ return false;
+}
+
static void dell_wmi_aio_notify(u32 value, void *context)
{
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
+ struct dell_wmi_event *event;
acpi_status status;
status = wmi_get_event_data(value, &response);
@@ -65,7 +98,7 @@ static void dell_wmi_aio_notify(u32 value, void *context)
obj = (union acpi_object *)response.pointer;
if (obj) {
- unsigned int scancode;
+ unsigned int scancode = 0;
switch (obj->type) {
case ACPI_TYPE_INTEGER:
@@ -75,13 +108,22 @@ static void dell_wmi_aio_notify(u32 value, void *context)
scancode, 1, true);
break;
case ACPI_TYPE_BUFFER:
- /* Broken machines return the scancode in a buffer */
- if (obj->buffer.pointer && obj->buffer.length > 0) {
- scancode = obj->buffer.pointer[0];
+ if (dell_wmi_aio_event_check(obj->buffer.pointer,
+ obj->buffer.length)) {
+ event = (struct dell_wmi_event *)
+ obj->buffer.pointer;
+ scancode = event->event[0];
+ } else {
+ /* Broken machines return the scancode in a
+ buffer */
+ if (obj->buffer.pointer &&
+ obj->buffer.length > 0)
+ scancode = obj->buffer.pointer[0];
+ }
+ if (scancode)
sparse_keymap_report_event(
dell_wmi_aio_input_dev,
scancode, 1, true);
- }
break;
}
}
diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c
index fa9a2171cc1..390e8e33d5e 100644
--- a/drivers/platform/x86/dell-wmi.c
+++ b/drivers/platform/x86/dell-wmi.c
@@ -32,7 +32,6 @@
#include <linux/types.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
-#include <acpi/acpi_drivers.h>
#include <linux/acpi.h>
#include <linux/string.h>
#include <linux/dmi.h>
@@ -130,7 +129,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
KEY_BRIGHTNESSUP, KEY_UNKNOWN, KEY_KBDILLUMTOGGLE,
KEY_UNKNOWN, KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_SWITCHVIDEOMODE, KEY_UNKNOWN, KEY_UNKNOWN, KEY_PROG2,
- KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
+ KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_MICMUTE,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -139,8 +139,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- KEY_PROG3
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PROG3
};
static struct input_dev *dell_wmi_input_dev;
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c
index dab91b48d22..9b0c57cd1d4 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -28,8 +28,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/slab.h>
-#include <acpi/acpi_drivers.h>
-#include <acpi/acpi_bus.h>
+#include <linux/acpi.h>
#include <linux/uaccess.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
@@ -166,7 +165,6 @@ struct eeepc_laptop {
struct platform_device *platform_device;
struct acpi_device *device; /* the device we are in */
- struct device *hwmon_device;
struct backlight_device *backlight_device;
struct input_dev *inputdev;
@@ -190,16 +188,10 @@ struct eeepc_laptop {
*/
static int write_acpi_int(acpi_handle handle, const char *method, int val)
{
- struct acpi_object_list params;
- union acpi_object in_obj;
acpi_status status;
- params.count = 1;
- params.pointer = &in_obj;
- in_obj.type = ACPI_TYPE_INTEGER;
- in_obj.integer.value = val;
+ status = acpi_execute_simple_method(handle, (char *)method, val);
- status = acpi_evaluate_object(handle, (char *)method, &params, NULL);
return (status == AE_OK ? 0 : -1);
}
@@ -598,6 +590,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);
mutex_lock(&eeepc->hotplug_lock);
+ pci_lock_rescan_remove();
if (eeepc->hotplug_slot) {
port = acpi_get_pci_dev(handle);
@@ -610,12 +603,12 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
if (!bus) {
pr_warn("Unable to find PCI bus 1?\n");
- goto out_unlock;
+ goto out_put_dev;
}
if (pci_bus_read_config_dword(bus, 0, PCI_VENDOR_ID, &l)) {
pr_err("Unable to read PCI config space?\n");
- goto out_unlock;
+ goto out_put_dev;
}
absent = (l == 0xffffffff);
@@ -627,7 +620,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
absent ? "absent" : "present");
pr_warn("skipped wireless hotplug as probably "
"inappropriate for this model\n");
- goto out_unlock;
+ goto out_put_dev;
}
if (!blocked) {
@@ -635,13 +628,12 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
if (dev) {
/* Device already present */
pci_dev_put(dev);
- goto out_unlock;
+ goto out_put_dev;
}
dev = pci_scan_single_device(bus, 0);
if (dev) {
pci_bus_assign_resources(bus);
- if (pci_bus_add_device(dev))
- pr_err("Unable to hotplug wifi\n");
+ pci_bus_add_device(dev);
}
} else {
dev = pci_get_slot(bus, 0);
@@ -650,9 +642,12 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
pci_dev_put(dev);
}
}
+out_put_dev:
+ pci_dev_put(port);
}
out_unlock:
+ pci_unlock_rescan_remove();
mutex_unlock(&eeepc->hotplug_lock);
}
@@ -1005,7 +1000,7 @@ static int eeepc_get_fan_pwm(void)
static void eeepc_set_fan_pwm(int value)
{
- value = SENSORS_LIMIT(value, 0, 255);
+ value = clamp_val(value, 0, 255);
value = value * 100 / 255;
ec_write(EEEPC_EC_FAN_PWM, value);
}
@@ -1071,7 +1066,7 @@ static ssize_t show_sys_hwmon(int (*get)(void), char *buf)
{ \
return store_sys_hwmon(_get, buf, count); \
} \
- static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0);
+ static DEVICE_ATTR(_name, _mode, show_##_name, store_##_name);
EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);
EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
@@ -1079,55 +1074,26 @@ EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,
EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
eeepc_get_fan_ctrl, eeepc_set_fan_ctrl);
-static ssize_t
-show_name(struct device *dev, struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "eeepc\n");
-}
-static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
-
-static struct attribute *hwmon_attributes[] = {
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_fan1_input.dev_attr.attr,
- &sensor_dev_attr_pwm1_enable.dev_attr.attr,
- &sensor_dev_attr_name.dev_attr.attr,
+static struct attribute *hwmon_attrs[] = {
+ &dev_attr_pwm1.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_pwm1_enable.attr,
NULL
};
-
-static struct attribute_group hwmon_attribute_group = {
- .attrs = hwmon_attributes
-};
-
-static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc)
-{
- struct device *hwmon;
-
- hwmon = eeepc->hwmon_device;
- if (!hwmon)
- return;
- sysfs_remove_group(&hwmon->kobj,
- &hwmon_attribute_group);
- hwmon_device_unregister(hwmon);
- eeepc->hwmon_device = NULL;
-}
+ATTRIBUTE_GROUPS(hwmon);
static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)
{
+ struct device *dev = &eeepc->platform_device->dev;
struct device *hwmon;
- int result;
- hwmon = hwmon_device_register(&eeepc->platform_device->dev);
+ hwmon = devm_hwmon_device_register_with_groups(dev, "eeepc", NULL,
+ hwmon_groups);
if (IS_ERR(hwmon)) {
pr_err("Could not register eeepc hwmon device\n");
- eeepc->hwmon_device = NULL;
return PTR_ERR(hwmon);
}
- eeepc->hwmon_device = hwmon;
- result = sysfs_create_group(&hwmon->kobj,
- &hwmon_attribute_group);
- if (result)
- eeepc_hwmon_exit(eeepc);
- return result;
+ return 0;
}
/*
@@ -1207,10 +1173,8 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)
int error;
input = input_allocate_device();
- if (!input) {
- pr_info("Unable to allocate input device\n");
+ if (!input)
return -ENOMEM;
- }
input->name = "Asus EeePC extra buttons";
input->phys = EEEPC_LAPTOP_FILE "/input0";
@@ -1267,7 +1231,6 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event)
if (event > ACPI_MAX_SYS_NOTIFY)
return;
count = eeepc->event_count[event % 128]++;
- acpi_bus_generate_proc_event(device, event, count);
acpi_bus_generate_netlink_event(device->pnp.device_class,
dev_name(&device->dev), event,
count);
@@ -1373,7 +1336,7 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc)
cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");
}
-static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
+static int eeepc_acpi_init(struct eeepc_laptop *eeepc)
{
unsigned int init_flags;
int result;
@@ -1405,7 +1368,7 @@ static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)
return 0;
}
-static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc)
+static void eeepc_enable_camera(struct eeepc_laptop *eeepc)
{
/*
* If the following call to set_acpi() fails, it's because there's no
@@ -1417,7 +1380,7 @@ static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc)
static bool eeepc_device_present;
-static int __devinit eeepc_acpi_add(struct acpi_device *device)
+static int eeepc_acpi_add(struct acpi_device *device)
{
struct eeepc_laptop *eeepc;
int result;
@@ -1486,7 +1449,6 @@ static int __devinit eeepc_acpi_add(struct acpi_device *device)
fail_rfkill:
eeepc_led_exit(eeepc);
fail_led:
- eeepc_hwmon_exit(eeepc);
fail_hwmon:
eeepc_input_exit(eeepc);
fail_input:
@@ -1499,14 +1461,13 @@ fail_platform:
return result;
}
-static int eeepc_acpi_remove(struct acpi_device *device, int type)
+static int eeepc_acpi_remove(struct acpi_device *device)
{
struct eeepc_laptop *eeepc = acpi_driver_data(device);
eeepc_backlight_exit(eeepc);
eeepc_rfkill_exit(eeepc);
eeepc_input_exit(eeepc);
- eeepc_hwmon_exit(eeepc);
eeepc_led_exit(eeepc);
eeepc_platform_exit(eeepc);
diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c
index 65676138034..6112933f627 100644
--- a/drivers/platform/x86/eeepc-wmi.c
+++ b/drivers/platform/x86/eeepc-wmi.c
@@ -33,13 +33,13 @@
#include <linux/input/sparse-keymap.h>
#include <linux/dmi.h>
#include <linux/fb.h>
-#include <acpi/acpi_bus.h>
+#include <linux/acpi.h>
#include "asus-wmi.h"
#define EEEPC_WMI_FILE "eeepc-wmi"
-MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>");
+MODULE_AUTHOR("Corentin Chary <corentin.chary@gmail.com>");
MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
MODULE_LICENSE("GPL");
@@ -63,6 +63,8 @@ MODULE_PARM_DESC(hotplug_wireless,
#define HOME_RELEASE 0xe5
static const struct key_entry eeepc_wmi_keymap[] = {
+ { KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } },
+ { KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },
/* Sleep already handled via generic ACPI code */
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
@@ -79,7 +81,7 @@ static const struct key_entry eeepc_wmi_keymap[] = {
{ KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
{ KE_KEY, HOME_PRESS, { KEY_CONFIG } }, /* Home/Express gate key */
{ KE_KEY, 0xe8, { KEY_SCREENLOCK } },
- { KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
+ { KE_KEY, 0xe9, { KEY_DISPLAYTOGGLE } },
{ KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
{ KE_KEY, 0xec, { KEY_CAMERA_UP } },
{ KE_KEY, 0xed, { KEY_CAMERA_DOWN } },
@@ -107,6 +109,11 @@ static struct quirk_entry quirk_asus_et2012_type3 = {
.store_backlight_power = true,
};
+static struct quirk_entry quirk_asus_x101ch = {
+ /* We need this when ACPI function doesn't do this well */
+ .wmi_backlight_power = true,
+};
+
static struct quirk_entry *quirks;
static void et2012_quirks(void)
@@ -157,6 +164,24 @@ static struct dmi_system_id asus_quirks[] = {
},
.driver_data = &quirk_asus_unknown,
},
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK Computer INC. X101CH",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X101CH"),
+ },
+ .driver_data = &quirk_asus_x101ch,
+ },
+ {
+ .callback = dmi_matched,
+ .ident = "ASUSTeK Computer INC. 1015CX",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "1015CX"),
+ },
+ .driver_data = &quirk_asus_x101ch,
+ },
{},
};
diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c
index c4c1a5444b3..e6f336270c2 100644
--- a/drivers/platform/x86/fujitsu-laptop.c
+++ b/drivers/platform/x86/fujitsu-laptop.c
@@ -66,7 +66,6 @@
#include <linux/backlight.h>
#include <linux/input.h>
#include <linux/kfifo.h>
-#include <linux/video_output.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
@@ -219,8 +218,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
{ .type = ACPI_TYPE_INTEGER }
};
struct acpi_object_list arg_list = { 4, &params[0] };
- struct acpi_buffer output;
- union acpi_object out_obj;
+ unsigned long long value;
acpi_handle handle = NULL;
status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle);
@@ -235,10 +233,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
params[2].integer.value = arg1;
params[3].integer.value = arg2;
- output.length = sizeof(out_obj);
- output.pointer = &out_obj;
-
- status = acpi_evaluate_object(handle, NULL, &arg_list, &output);
+ status = acpi_evaluate_integer(handle, NULL, &arg_list, &value);
if (ACPI_FAILURE(status)) {
vdbg_printk(FUJLAPTOP_DBG_WARN,
"FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n",
@@ -246,18 +241,10 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)
return -ENODEV;
}
- if (out_obj.type != ACPI_TYPE_INTEGER) {
- vdbg_printk(FUJLAPTOP_DBG_WARN,
- "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not "
- "return an integer\n",
- cmd, arg0, arg1, arg2);
- return -ENODEV;
- }
-
vdbg_printk(FUJLAPTOP_DBG_TRACE,
"FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n",
- cmd, arg0, arg1, arg2, (int)out_obj.integer.value);
- return out_obj.integer.value;
+ cmd, arg0, arg1, arg2, (int)value);
+ return value;
}
#if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE)
@@ -317,8 +304,6 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev)
static int set_lcd_level(int level)
{
acpi_status status = AE_OK;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list arg_list = { 1, &arg0 };
acpi_handle handle = NULL;
vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n",
@@ -333,9 +318,8 @@ static int set_lcd_level(int level)
return -ENODEV;
}
- arg0.integer.value = level;
- status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
+ status = acpi_execute_simple_method(handle, NULL, level);
if (ACPI_FAILURE(status))
return -ENODEV;
@@ -345,8 +329,6 @@ static int set_lcd_level(int level)
static int set_lcd_level_alt(int level)
{
acpi_status status = AE_OK;
- union acpi_object arg0 = { ACPI_TYPE_INTEGER };
- struct acpi_object_list arg_list = { 1, &arg0 };
acpi_handle handle = NULL;
vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n",
@@ -361,9 +343,7 @@ static int set_lcd_level_alt(int level)
return -ENODEV;
}
- arg0.integer.value = level;
-
- status = acpi_evaluate_object(handle, NULL, &arg_list, NULL);
+ status = acpi_execute_simple_method(handle, NULL, level);
if (ACPI_FAILURE(status))
return -ENODEV;
@@ -586,11 +566,10 @@ static struct platform_driver fujitsupf_driver = {
static void dmi_check_cb_common(const struct dmi_system_id *id)
{
- acpi_handle handle;
pr_info("Identified laptop model '%s'\n", id->ident);
if (use_alt_lcd_levels == -1) {
- if (ACPI_SUCCESS(acpi_get_handle(NULL,
- "\\_SB.PCI0.LPCB.FJEX.SBL2", &handle)))
+ if (acpi_has_method(NULL,
+ "\\_SB.PCI0.LPCB.FJEX.SBL2"))
use_alt_lcd_levels = 1;
else
use_alt_lcd_levels = 0;
@@ -653,8 +632,6 @@ static struct dmi_system_id fujitsu_dmi_table[] = {
static int acpi_fujitsu_add(struct acpi_device *device)
{
- acpi_handle handle;
- int result = 0;
int state = 0;
struct input_dev *input;
int error;
@@ -690,8 +667,8 @@ static int acpi_fujitsu_add(struct acpi_device *device)
if (error)
goto err_free_input_dev;
- result = acpi_bus_update_power(fujitsu->acpi_handle, &state);
- if (result) {
+ error = acpi_bus_update_power(fujitsu->acpi_handle, &state);
+ if (error) {
pr_err("Error reading power state\n");
goto err_unregister_input_dev;
}
@@ -702,8 +679,7 @@ static int acpi_fujitsu_add(struct acpi_device *device)
fujitsu->dev = device;
- if (ACPI_SUCCESS
- (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
+ if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
if (ACPI_FAILURE
(acpi_evaluate_object
@@ -722,7 +698,7 @@ static int acpi_fujitsu_add(struct acpi_device *device)
fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;
get_lcd_level();
- return result;
+ return 0;
err_unregister_input_dev:
input_unregister_device(input);
@@ -730,10 +706,10 @@ err_unregister_input_dev:
err_free_input_dev:
input_free_device(input);
err_stop:
- return result;
+ return error;
}
-static int acpi_fujitsu_remove(struct acpi_device *device, int type)
+static int acpi_fujitsu_remove(struct acpi_device *device)
{
struct fujitsu_t *fujitsu = acpi_driver_data(device);
struct input_dev *input = fujitsu->input;
@@ -773,8 +749,6 @@ static void acpi_fujitsu_notify(struct acpi_device *device, u32 event)
else
set_lcd_level(newb);
}
- acpi_bus_generate_proc_event(fujitsu->dev,
- ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS, 0);
keycode = KEY_BRIGHTNESSUP;
} else if (oldb > newb) {
if (disable_brightness_adjust != 1) {
@@ -783,8 +757,6 @@ static void acpi_fujitsu_notify(struct acpi_device *device, u32 event)
else
set_lcd_level(newb);
}
- acpi_bus_generate_proc_event(fujitsu->dev,
- ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS, 0);
keycode = KEY_BRIGHTNESSDOWN;
}
break;
@@ -807,7 +779,6 @@ static void acpi_fujitsu_notify(struct acpi_device *device, u32 event)
static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
{
- acpi_handle handle;
int result = 0;
int state = 0;
struct input_dev *input;
@@ -858,8 +829,8 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
if (error)
goto err_free_input_dev;
- result = acpi_bus_update_power(fujitsu_hotkey->acpi_handle, &state);
- if (result) {
+ error = acpi_bus_update_power(fujitsu_hotkey->acpi_handle, &state);
+ if (error) {
pr_err("Error reading power state\n");
goto err_unregister_input_dev;
}
@@ -870,8 +841,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
fujitsu_hotkey->dev = device;
- if (ACPI_SUCCESS
- (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) {
+ if (acpi_has_method(device->handle, METHOD_NAME__INI)) {
vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");
if (ACPI_FAILURE
(acpi_evaluate_object
@@ -935,10 +905,10 @@ err_free_input_dev:
err_free_fifo:
kfifo_free(&fujitsu_hotkey->fifo);
err_stop:
- return result;
+ return error;
}
-static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type)
+static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)
{
struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device);
struct input_dev *input = fujitsu_hotkey->input;
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c
index da267eae8ba..c3784baceae 100644
--- a/drivers/platform/x86/fujitsu-tablet.c
+++ b/drivers/platform/x86/fujitsu-tablet.c
@@ -52,7 +52,7 @@ struct fujitsu_config {
unsigned int quirks;
};
-static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initconst = {
+static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
@@ -71,7 +71,45 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initconst = {
KEY_LEFTALT
};
-static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initconst = {
+static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_SCROLLDOWN,
+ KEY_SCROLLUP,
+ KEY_CYCLEWINDOWS,
+ KEY_LEFTCTRL,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_LEFTMETA
+};
+
+static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = {
+ KEY_RESERVED,
+ KEY_VOLUMEDOWN,
+ KEY_VOLUMEUP,
+ KEY_CYCLEWINDOWS,
+ KEY_PROG1,
+ KEY_PROG2,
+ KEY_LEFTMETA,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+};
+
+static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
@@ -90,7 +128,7 @@ static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initconst = {
KEY_LEFTALT
};
-static unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initconst = {
+static unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initdata = {
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
@@ -109,7 +147,7 @@ static unsigned short keymap_Stylistic_Tseries[KEYMAP_LEN] __initconst = {
KEY_LEFTALT
};
-static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initconst = {
+static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initdata = {
KEY_RESERVED,
KEY_RESERVED,
KEY_RESERVED,
@@ -192,8 +230,8 @@ static void fujitsu_reset(void)
fujitsu_send_state();
}
-static int __devinit input_fujitsu_setup(struct device *parent,
- const char *name, const char *phys)
+static int input_fujitsu_setup(struct device *parent, const char *name,
+ const char *phys)
{
struct input_dev *idev;
int error;
@@ -277,21 +315,21 @@ static irqreturn_t fujitsu_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static void __devinit fujitsu_dmi_common(const struct dmi_system_id *dmi)
+static void fujitsu_dmi_common(const struct dmi_system_id *dmi)
{
pr_info("%s\n", dmi->ident);
memcpy(fujitsu.config.keymap, dmi->driver_data,
sizeof(fujitsu.config.keymap));
}
-static int __devinit fujitsu_dmi_lifebook(const struct dmi_system_id *dmi)
+static int fujitsu_dmi_lifebook(const struct dmi_system_id *dmi)
{
fujitsu_dmi_common(dmi);
fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT;
return 1;
}
-static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
+static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
{
fujitsu_dmi_common(dmi);
fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK;
@@ -299,7 +337,34 @@ static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)
return 1;
}
-static struct dmi_system_id dmi_ids[] __initconst = {
+static const struct dmi_system_id dmi_ids[] __initconst = {
+ {
+ .callback = fujitsu_dmi_lifebook,
+ .ident = "Fujitsu Lifebook T901",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901")
+ },
+ .driver_data = keymap_Lifebook_T901
+ },
+ {
+ .callback = fujitsu_dmi_lifebook,
+ .ident = "Fujitsu Lifebook T901",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901")
+ },
+ .driver_data = keymap_Lifebook_T901
+ },
+ {
+ .callback = fujitsu_dmi_lifebook,
+ .ident = "Fujitsu Lifebook T902",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902")
+ },
+ .driver_data = keymap_Lifebook_T902
+ },
{
.callback = fujitsu_dmi_lifebook,
.ident = "Fujitsu Siemens P/T Series",
@@ -366,8 +431,7 @@ static struct dmi_system_id dmi_ids[] __initconst = {
{ NULL }
};
-static acpi_status __devinit
-fujitsu_walk_resources(struct acpi_resource *res, void *data)
+static acpi_status fujitsu_walk_resources(struct acpi_resource *res, void *data)
{
switch (res->type) {
case ACPI_RESOURCE_TYPE_IRQ:
@@ -390,7 +454,7 @@ fujitsu_walk_resources(struct acpi_resource *res, void *data)
}
}
-static int __devinit acpi_fujitsu_add(struct acpi_device *adev)
+static int acpi_fujitsu_add(struct acpi_device *adev)
{
acpi_status status;
int error;
@@ -432,7 +496,7 @@ static int __devinit acpi_fujitsu_add(struct acpi_device *adev)
return 0;
}
-static int __devexit acpi_fujitsu_remove(struct acpi_device *adev, int type)
+static int acpi_fujitsu_remove(struct acpi_device *adev)
{
free_irq(fujitsu.irq, fujitsu_interrupt);
release_region(fujitsu.io_base, fujitsu.io_length);
@@ -440,11 +504,15 @@ static int __devexit acpi_fujitsu_remove(struct acpi_device *adev, int type)
return 0;
}
-static int acpi_fujitsu_resume(struct acpi_device *adev)
+#ifdef CONFIG_PM_SLEEP
+static int acpi_fujitsu_resume(struct device *dev)
{
fujitsu_reset();
return 0;
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume);
static struct acpi_driver acpi_fujitsu_driver = {
.name = MODULENAME,
@@ -453,8 +521,8 @@ static struct acpi_driver acpi_fujitsu_driver = {
.ops = {
.add = acpi_fujitsu_add,
.remove = acpi_fujitsu_remove,
- .resume = acpi_fujitsu_resume,
- }
+ },
+ .drv.pm = &acpi_fujitsu_pm,
};
static int __init fujitsu_module_init(void)
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c
index 24a3ae065f1..777c7e3dda5 100644
--- a/drivers/platform/x86/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -305,17 +305,21 @@ static int hdaps_probe(struct platform_device *dev)
return 0;
}
-static int hdaps_resume(struct platform_device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int hdaps_resume(struct device *dev)
{
return hdaps_device_init();
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(hdaps_pm, NULL, hdaps_resume);
static struct platform_driver hdaps_driver = {
.probe = hdaps_probe,
- .resume = hdaps_resume,
.driver = {
.name = "hdaps",
.owner = THIS_MODULE,
+ .pm = &hdaps_pm,
},
};
diff --git a/drivers/platform/x86/hp-wireless.c b/drivers/platform/x86/hp-wireless.c
new file mode 100644
index 00000000000..415348fc121
--- /dev/null
+++ b/drivers/platform/x86/hp-wireless.c
@@ -0,0 +1,132 @@
+/*
+ * hp-wireless button for Windows 8
+ *
+ * Copyright (C) 2014 Alex Hung <alex.hung@canonical.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alex Hung");
+MODULE_ALIAS("acpi*:HPQ6001:*");
+
+static struct input_dev *hpwl_input_dev;
+
+static const struct acpi_device_id hpwl_ids[] = {
+ {"HPQ6001", 0},
+ {"", 0},
+};
+
+static int hp_wireless_input_setup(void)
+{
+ int err;
+
+ hpwl_input_dev = input_allocate_device();
+ if (!hpwl_input_dev)
+ return -ENOMEM;
+
+ hpwl_input_dev->name = "HP Wireless hotkeys";
+ hpwl_input_dev->phys = "hpq6001/input0";
+ hpwl_input_dev->id.bustype = BUS_HOST;
+ hpwl_input_dev->evbit[0] = BIT(EV_KEY);
+ set_bit(KEY_RFKILL, hpwl_input_dev->keybit);
+
+ err = input_register_device(hpwl_input_dev);
+ if (err)
+ goto err_free_dev;
+
+ return 0;
+
+err_free_dev:
+ input_free_device(hpwl_input_dev);
+ return err;
+}
+
+static void hp_wireless_input_destroy(void)
+{
+ input_unregister_device(hpwl_input_dev);
+}
+
+static void hpwl_notify(struct acpi_device *acpi_dev, u32 event)
+{
+ if (event != 0x80) {
+ pr_info("Received unknown event (0x%x)\n", event);
+ return;
+ }
+
+ input_report_key(hpwl_input_dev, KEY_RFKILL, 1);
+ input_sync(hpwl_input_dev);
+ input_report_key(hpwl_input_dev, KEY_RFKILL, 0);
+ input_sync(hpwl_input_dev);
+}
+
+static int hpwl_add(struct acpi_device *device)
+{
+ int err;
+
+ err = hp_wireless_input_setup();
+ return err;
+}
+
+static int hpwl_remove(struct acpi_device *device)
+{
+ hp_wireless_input_destroy();
+ return 0;
+}
+
+static struct acpi_driver hpwl_driver = {
+ .name = "hp-wireless",
+ .owner = THIS_MODULE,
+ .ids = hpwl_ids,
+ .ops = {
+ .add = hpwl_add,
+ .remove = hpwl_remove,
+ .notify = hpwl_notify,
+ },
+};
+
+static int __init hpwl_init(void)
+{
+ int err;
+
+ pr_info("Initializing HPQ6001 module\n");
+ err = acpi_bus_register_driver(&hpwl_driver);
+ if (err) {
+ pr_err("Unable to register HP wireless control driver.\n");
+ goto error_acpi_register;
+ }
+
+ return 0;
+
+error_acpi_register:
+ return err;
+}
+
+static void __exit hpwl_exit(void)
+{
+ pr_info("Exiting HPQ6001 module\n");
+ acpi_bus_unregister_driver(&hpwl_driver);
+}
+
+module_init(hpwl_init);
+module_exit(hpwl_exit);
diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c
index 387183a2d6d..484a8673b83 100644
--- a/drivers/platform/x86/hp-wmi.c
+++ b/drivers/platform/x86/hp-wmi.c
@@ -53,13 +53,17 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
#define HPWMI_ALS_QUERY 0x3
#define HPWMI_HARDWARE_QUERY 0x4
#define HPWMI_WIRELESS_QUERY 0x5
+#define HPWMI_BIOS_QUERY 0x9
#define HPWMI_HOTKEY_QUERY 0xc
+#define HPWMI_FEATURE_QUERY 0xd
#define HPWMI_WIRELESS2_QUERY 0x1b
+#define HPWMI_POSTCODEERROR_QUERY 0x2a
enum hp_wmi_radio {
HPWMI_WIFI = 0,
HPWMI_BLUETOOTH = 1,
HPWMI_WWAN = 2,
+ HPWMI_GPS = 3,
};
enum hp_wmi_event_ids {
@@ -70,12 +74,16 @@ enum hp_wmi_event_ids {
HPWMI_WIRELESS = 5,
HPWMI_CPU_BATTERY_THROTTLE = 6,
HPWMI_LOCK_SWITCH = 7,
+ HPWMI_LID_SWITCH = 8,
+ HPWMI_SCREEN_ROTATION = 9,
+ HPWMI_COOLSENSE_SYSTEM_MOBILE = 0x0A,
+ HPWMI_COOLSENSE_SYSTEM_HOT = 0x0B,
+ HPWMI_PROXIMITY_SENSOR = 0x0C,
+ HPWMI_BACKLIT_KB_BRIGHTNESS = 0x0D,
+ HPWMI_PEAKSHIFT_PERIOD = 0x0F,
+ HPWMI_BATTERY_CHARGE_PERIOD = 0x10,
};
-static int __devinit hp_wmi_bios_setup(struct platform_device *device);
-static int __exit hp_wmi_bios_remove(struct platform_device *device);
-static int hp_wmi_resume_handler(struct device *device);
-
struct bios_args {
u32 signature;
u32 command;
@@ -137,6 +145,7 @@ static const struct key_entry hp_wmi_keymap[] = {
{ KE_KEY, 0x2142, { KEY_MEDIA } },
{ KE_KEY, 0x213b, { KEY_INFO } },
{ KE_KEY, 0x2169, { KEY_DIRECTION } },
+ { KE_KEY, 0x216a, { KEY_SETUP } },
{ KE_KEY, 0x231b, { KEY_HELP } },
{ KE_END, 0 }
};
@@ -147,6 +156,7 @@ static struct platform_device *hp_wmi_platform_dev;
static struct rfkill *wifi_rfkill;
static struct rfkill *bluetooth_rfkill;
static struct rfkill *wwan_rfkill;
+static struct rfkill *gps_rfkill;
struct rfkill2_device {
u8 id;
@@ -157,21 +167,6 @@ struct rfkill2_device {
static int rfkill2_count;
static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
-static const struct dev_pm_ops hp_wmi_pm_ops = {
- .resume = hp_wmi_resume_handler,
- .restore = hp_wmi_resume_handler,
-};
-
-static struct platform_driver hp_wmi_driver = {
- .driver = {
- .name = "hp-wmi",
- .owner = THIS_MODULE,
- .pm = &hp_wmi_pm_ops,
- },
- .probe = hp_wmi_bios_setup,
- .remove = hp_wmi_bios_remove,
-};
-
/*
* hp_wmi_perform_query
*
@@ -300,6 +295,30 @@ static int hp_wmi_tablet_state(void)
return (state & 0x4) ? 1 : 0;
}
+static int hp_wmi_bios_2009_later(void)
+{
+ int state = 0;
+ int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state,
+ sizeof(state), sizeof(state));
+ if (ret)
+ return ret;
+
+ return (state & 0x10) ? 1 : 0;
+}
+
+static int hp_wmi_enable_hotkeys(void)
+{
+ int ret;
+ int query = 0x6e;
+
+ ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query),
+ 0);
+
+ if (ret)
+ return -EINVAL;
+ return 0;
+}
+
static int hp_wmi_set_block(void *data, bool blocked)
{
enum hp_wmi_radio r = (enum hp_wmi_radio) data;
@@ -395,6 +414,16 @@ static int hp_wmi_rfkill2_refresh(void)
return 0;
}
+static int hp_wmi_post_code_state(void)
+{
+ int state = 0;
+ int ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, 0, &state,
+ sizeof(state), sizeof(state));
+ if (ret)
+ return -EINVAL;
+ return state;
+}
+
static ssize_t show_display(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -440,6 +469,16 @@ static ssize_t show_tablet(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%d\n", value);
}
+static ssize_t show_postcode(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ /* Get the POST error code of previous boot failure. */
+ int value = hp_wmi_post_code_state();
+ if (value < 0)
+ return -EINVAL;
+ return sprintf(buf, "0x%x\n", value);
+}
+
static ssize_t set_als(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -452,11 +491,33 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr,
return count;
}
+static ssize_t set_postcode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+ u32 tmp;
+ long unsigned int tmp2;
+
+ ret = kstrtoul(buf, 10, &tmp2);
+ if (ret || tmp2 != 1)
+ return -EINVAL;
+
+ /* Clear the POST error code. It is kept until until cleared. */
+ tmp = (u32) tmp2;
+ ret = hp_wmi_perform_query(HPWMI_POSTCODEERROR_QUERY, 1, &tmp,
+ sizeof(tmp), sizeof(tmp));
+ if (ret)
+ return -EINVAL;
+
+ return count;
+}
+
static DEVICE_ATTR(display, S_IRUGO, show_display, NULL);
static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL);
static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als);
static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL);
static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL);
+static DEVICE_ATTR(postcode, S_IRUGO | S_IWUSR, show_postcode, set_postcode);
static void hp_wmi_notify(u32 value, void *context)
{
@@ -543,12 +604,32 @@ static void hp_wmi_notify(u32 value, void *context)
rfkill_set_states(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN),
hp_wmi_get_hw_state(HPWMI_WWAN));
+ if (gps_rfkill)
+ rfkill_set_states(gps_rfkill,
+ hp_wmi_get_sw_state(HPWMI_GPS),
+ hp_wmi_get_hw_state(HPWMI_GPS));
break;
case HPWMI_CPU_BATTERY_THROTTLE:
pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n");
break;
case HPWMI_LOCK_SWITCH:
break;
+ case HPWMI_LID_SWITCH:
+ break;
+ case HPWMI_SCREEN_ROTATION:
+ break;
+ case HPWMI_COOLSENSE_SYSTEM_MOBILE:
+ break;
+ case HPWMI_COOLSENSE_SYSTEM_HOT:
+ break;
+ case HPWMI_PROXIMITY_SENSOR:
+ break;
+ case HPWMI_BACKLIT_KB_BRIGHTNESS:
+ break;
+ case HPWMI_PEAKSHIFT_PERIOD:
+ break;
+ case HPWMI_BATTERY_CHARGE_PERIOD:
+ break;
default:
pr_info("Unknown event_id - %d - 0x%x\n", event_id, event_data);
break;
@@ -582,6 +663,9 @@ static int __init hp_wmi_input_setup(void)
hp_wmi_tablet_state());
input_sync(hp_wmi_input_dev);
+ if (hp_wmi_bios_2009_later() == 4)
+ hp_wmi_enable_hotkeys();
+
status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);
if (ACPI_FAILURE(status)) {
err = -EIO;
@@ -617,9 +701,10 @@ static void cleanup_sysfs(struct platform_device *device)
device_remove_file(&device->dev, &dev_attr_als);
device_remove_file(&device->dev, &dev_attr_dock);
device_remove_file(&device->dev, &dev_attr_tablet);
+ device_remove_file(&device->dev, &dev_attr_postcode);
}
-static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
+static int hp_wmi_rfkill_setup(struct platform_device *device)
{
int err;
int wireless = 0;
@@ -678,15 +763,38 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
hp_wmi_get_hw_state(HPWMI_WWAN));
err = rfkill_register(wwan_rfkill);
if (err)
- goto register_wwan_err;
+ goto register_wwan_error;
+ }
+
+ if (wireless & 0x8) {
+ gps_rfkill = rfkill_alloc("hp-gps", &device->dev,
+ RFKILL_TYPE_GPS,
+ &hp_wmi_rfkill_ops,
+ (void *) HPWMI_GPS);
+ if (!gps_rfkill) {
+ err = -ENOMEM;
+ goto register_wwan_error;
+ }
+ rfkill_init_sw_state(gps_rfkill,
+ hp_wmi_get_sw_state(HPWMI_GPS));
+ rfkill_set_hw_state(gps_rfkill,
+ hp_wmi_get_hw_state(HPWMI_GPS));
+ err = rfkill_register(gps_rfkill);
+ if (err)
+ goto register_gps_error;
}
return 0;
-register_wwan_err:
- rfkill_destroy(wwan_rfkill);
- wwan_rfkill = NULL;
+register_gps_error:
+ rfkill_destroy(gps_rfkill);
+ gps_rfkill = NULL;
if (bluetooth_rfkill)
rfkill_unregister(bluetooth_rfkill);
+register_wwan_error:
+ rfkill_destroy(wwan_rfkill);
+ wwan_rfkill = NULL;
+ if (gps_rfkill)
+ rfkill_unregister(gps_rfkill);
register_bluetooth_error:
rfkill_destroy(bluetooth_rfkill);
bluetooth_rfkill = NULL;
@@ -698,7 +806,7 @@ register_wifi_error:
return err;
}
-static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
+static int hp_wmi_rfkill2_setup(struct platform_device *device)
{
int err, i;
struct bios_rfkill2_state state;
@@ -729,6 +837,10 @@ static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
type = RFKILL_TYPE_WWAN;
name = "hp-wwan";
break;
+ case HPWMI_GPS:
+ type = RFKILL_TYPE_GPS;
+ name = "hp-gps";
+ break;
default:
pr_warn("unknown device type 0x%x\n",
state.device[i].radio_type);
@@ -778,7 +890,7 @@ fail:
return err;
}
-static int __devinit hp_wmi_bios_setup(struct platform_device *device)
+static int __init hp_wmi_bios_setup(struct platform_device *device)
{
int err;
@@ -786,9 +898,10 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device)
wifi_rfkill = NULL;
bluetooth_rfkill = NULL;
wwan_rfkill = NULL;
+ gps_rfkill = NULL;
rfkill2_count = 0;
- if (hp_wmi_rfkill_setup(device))
+ if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))
hp_wmi_rfkill2_setup(device);
err = device_create_file(&device->dev, &dev_attr_display);
@@ -806,6 +919,9 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device)
err = device_create_file(&device->dev, &dev_attr_tablet);
if (err)
goto add_sysfs_error;
+ err = device_create_file(&device->dev, &dev_attr_postcode);
+ if (err)
+ goto add_sysfs_error;
return 0;
add_sysfs_error:
@@ -835,6 +951,10 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
rfkill_unregister(wwan_rfkill);
rfkill_destroy(wwan_rfkill);
}
+ if (gps_rfkill) {
+ rfkill_unregister(gps_rfkill);
+ rfkill_destroy(gps_rfkill);
+ }
return 0;
}
@@ -870,16 +990,37 @@ static int hp_wmi_resume_handler(struct device *device)
rfkill_set_states(wwan_rfkill,
hp_wmi_get_sw_state(HPWMI_WWAN),
hp_wmi_get_hw_state(HPWMI_WWAN));
+ if (gps_rfkill)
+ rfkill_set_states(gps_rfkill,
+ hp_wmi_get_sw_state(HPWMI_GPS),
+ hp_wmi_get_hw_state(HPWMI_GPS));
return 0;
}
+static const struct dev_pm_ops hp_wmi_pm_ops = {
+ .resume = hp_wmi_resume_handler,
+ .restore = hp_wmi_resume_handler,
+};
+
+static struct platform_driver hp_wmi_driver = {
+ .driver = {
+ .name = "hp-wmi",
+ .owner = THIS_MODULE,
+ .pm = &hp_wmi_pm_ops,
+ },
+ .remove = __exit_p(hp_wmi_bios_remove),
+};
+
static int __init hp_wmi_init(void)
{
int err;
int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);
int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID);
+ if (!bios_capable && !event_capable)
+ return -ENODEV;
+
if (event_capable) {
err = hp_wmi_input_setup();
if (err)
@@ -887,34 +1028,29 @@ static int __init hp_wmi_init(void)
}
if (bios_capable) {
- err = platform_driver_register(&hp_wmi_driver);
- if (err)
- goto err_driver_reg;
- hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1);
- if (!hp_wmi_platform_dev) {
- err = -ENOMEM;
- goto err_device_alloc;
+ hp_wmi_platform_dev =
+ platform_device_register_simple("hp-wmi", -1, NULL, 0);
+ if (IS_ERR(hp_wmi_platform_dev)) {
+ err = PTR_ERR(hp_wmi_platform_dev);
+ goto err_destroy_input;
}
- err = platform_device_add(hp_wmi_platform_dev);
+
+ err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);
if (err)
- goto err_device_add;
+ goto err_unregister_device;
}
- if (!bios_capable && !event_capable)
- return -ENODEV;
-
return 0;
-err_device_add:
- platform_device_put(hp_wmi_platform_dev);
-err_device_alloc:
- platform_driver_unregister(&hp_wmi_driver);
-err_driver_reg:
+err_unregister_device:
+ platform_device_unregister(hp_wmi_platform_dev);
+err_destroy_input:
if (event_capable)
hp_wmi_input_destroy();
return err;
}
+module_init(hp_wmi_init);
static void __exit hp_wmi_exit(void)
{
@@ -926,6 +1062,4 @@ static void __exit hp_wmi_exit(void)
platform_driver_unregister(&hp_wmi_driver);
}
}
-
-module_init(hp_wmi_init);
module_exit(hp_wmi_exit);
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c
index 22b2dfa7314..3dc934438c2 100644
--- a/drivers/platform/x86/hp_accel.c
+++ b/drivers/platform/x86/hp_accel.c
@@ -36,7 +36,7 @@
#include <linux/uaccess.h>
#include <linux/leds.h>
#include <linux/atomic.h>
-#include <acpi/acpi_drivers.h>
+#include <linux/acpi.h>
#include "../../misc/lis3lv02d/lis3lv02d.h"
#define DRIVER_NAME "hp_accel"
@@ -77,6 +77,7 @@ static inline void delayed_sysfs_set(struct led_classdev *led_cdev,
static struct acpi_device_id lis3lv02d_device_ids[] = {
{"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */
{"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */
+ {"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids);
@@ -88,7 +89,7 @@ MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids);
*
* Returns 0 on success.
*/
-int lis3lv02d_acpi_init(struct lis3lv02d *lis3)
+static int lis3lv02d_acpi_init(struct lis3lv02d *lis3)
{
struct acpi_device *dev = lis3->bus_priv;
if (acpi_evaluate_object(dev->handle, METHOD_NAME__INI,
@@ -106,7 +107,7 @@ int lis3lv02d_acpi_init(struct lis3lv02d *lis3)
*
* Returns 0 on success.
*/
-int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret)
+static int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret)
{
struct acpi_device *dev = lis3->bus_priv;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
@@ -129,7 +130,7 @@ int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret)
*
* Returns 0 on success.
*/
-int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val)
+static int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val)
{
struct acpi_device *dev = lis3->bus_priv;
unsigned long long ret; /* Not used when writting */
@@ -337,7 +338,7 @@ static int lis3lv02d_add(struct acpi_device *device)
return ret;
}
-static int lis3lv02d_remove(struct acpi_device *device, int type)
+static int lis3lv02d_remove(struct acpi_device *device)
{
if (!device)
return -EINVAL;
@@ -352,21 +353,24 @@ static int lis3lv02d_remove(struct acpi_device *device, int type)
}
-#ifdef CONFIG_PM
-static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int lis3lv02d_suspend(struct device *dev)
{
/* make sure the device is off when we suspend */
lis3lv02d_poweroff(&lis3_dev);
return 0;
}
-static int lis3lv02d_resume(struct acpi_device *device)
+static int lis3lv02d_resume(struct device *dev)
{
- return lis3lv02d_poweron(&lis3_dev);
+ lis3lv02d_poweron(&lis3_dev);
+ return 0;
}
+
+static SIMPLE_DEV_PM_OPS(hp_accel_pm, lis3lv02d_suspend, lis3lv02d_resume);
+#define HP_ACCEL_PM (&hp_accel_pm)
#else
-#define lis3lv02d_suspend NULL
-#define lis3lv02d_resume NULL
+#define HP_ACCEL_PM NULL
#endif
/* For the HP MDPS aka 3D Driveguard */
@@ -377,35 +381,11 @@ static struct acpi_driver lis3lv02d_driver = {
.ops = {
.add = lis3lv02d_add,
.remove = lis3lv02d_remove,
- .suspend = lis3lv02d_suspend,
- .resume = lis3lv02d_resume,
- }
+ },
+ .drv.pm = HP_ACCEL_PM,
};
-
-static int __init lis3lv02d_init_module(void)
-{
- int ret;
-
- if (acpi_disabled)
- return -ENODEV;
-
- ret = acpi_bus_register_driver(&lis3lv02d_driver);
- if (ret < 0)
- return ret;
-
- pr_info("driver loaded\n");
-
- return 0;
-}
-
-static void __exit lis3lv02d_exit_module(void)
-{
- acpi_bus_unregister_driver(&lis3lv02d_driver);
-}
+module_acpi_driver(lis3lv02d_driver);
MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED.");
MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek");
MODULE_LICENSE("GPL");
-
-module_init(lis3lv02d_init_module);
-module_exit(lis3lv02d_exit_module);
diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c
index 7481146a5b4..97c2be195ef 100644
--- a/drivers/platform/x86/ibm_rtl.c
+++ b/drivers/platform/x86/ibm_rtl.c
@@ -244,7 +244,7 @@ static int __init ibm_rtl_init(void) {
if (force)
pr_warn("module loaded by force\n");
/* first ensure that we are running on IBM HW */
- else if (efi_enabled || !dmi_check_system(ibm_rtl_dmi_table))
+ else if (efi_enabled(EFI_BOOT) || !dmi_check_system(ibm_rtl_dmi_table))
return -ENODEV;
/* Get the address for the Extended BIOS Data Area */
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 4f20f8dd3d7..b4c495a62ee 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -26,8 +26,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
+#include <linux/acpi.h>
#include <linux/rfkill.h>
#include <linux/platform_device.h>
#include <linux/input.h>
@@ -36,6 +35,9 @@
#include <linux/fb.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/i8042.h>
+#include <linux/dmi.h>
+#include <linux/device.h>
#define IDEAPAD_RFKILL_DEV_NUM (3)
@@ -63,13 +65,23 @@ enum {
VPCCMD_R_3G,
VPCCMD_W_3G,
VPCCMD_R_ODD, /* 0x21 */
- VPCCMD_R_RF = 0x23,
+ VPCCMD_W_FAN,
+ VPCCMD_R_RF,
VPCCMD_W_RF,
+ VPCCMD_R_FAN = 0x2B,
+ VPCCMD_R_SPECIAL_BUTTONS = 0x31,
VPCCMD_W_BL_POWER = 0x33,
};
+struct ideapad_rfk_priv {
+ int dev;
+ struct ideapad_private *priv;
+};
+
struct ideapad_private {
+ struct acpi_device *adev;
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
+ struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device;
struct input_dev *inputdev;
struct backlight_device *blightdev;
@@ -77,8 +89,6 @@ struct ideapad_private {
unsigned long cfg;
};
-static acpi_handle ideapad_handle;
-static struct ideapad_private *ideapad_priv;
static bool no_bt_rfkill;
module_param(no_bt_rfkill, bool, 0444);
MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
@@ -196,34 +206,38 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
*/
static int debugfs_status_show(struct seq_file *s, void *data)
{
+ struct ideapad_private *priv = s->private;
unsigned long value;
- if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &value))
+ if (!priv)
+ return -EINVAL;
+
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))
seq_printf(s, "Backlight max:\t%lu\n", value);
- if (!read_ec_data(ideapad_handle, VPCCMD_R_BL, &value))
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))
seq_printf(s, "Backlight now:\t%lu\n", value);
- if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &value))
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))
seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
seq_printf(s, "=====================\n");
- if (!read_ec_data(ideapad_handle, VPCCMD_R_RF, &value))
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))
seq_printf(s, "Radio status:\t%s(%lu)\n",
value ? "On" : "Off", value);
- if (!read_ec_data(ideapad_handle, VPCCMD_R_WIFI, &value))
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))
seq_printf(s, "Wifi status:\t%s(%lu)\n",
value ? "On" : "Off", value);
- if (!read_ec_data(ideapad_handle, VPCCMD_R_BT, &value))
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))
seq_printf(s, "BT status:\t%s(%lu)\n",
value ? "On" : "Off", value);
- if (!read_ec_data(ideapad_handle, VPCCMD_R_3G, &value))
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))
seq_printf(s, "3G status:\t%s(%lu)\n",
value ? "On" : "Off", value);
seq_printf(s, "=====================\n");
- if (!read_ec_data(ideapad_handle, VPCCMD_R_TOUCHPAD, &value))
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))
seq_printf(s, "Touchpad status:%s(%lu)\n",
value ? "On" : "Off", value);
- if (!read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &value))
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))
seq_printf(s, "Camera status:\t%s(%lu)\n",
value ? "On" : "Off", value);
@@ -232,7 +246,7 @@ static int debugfs_status_show(struct seq_file *s, void *data)
static int debugfs_status_open(struct inode *inode, struct file *file)
{
- return single_open(file, debugfs_status_show, NULL);
+ return single_open(file, debugfs_status_show, inode->i_private);
}
static const struct file_operations debugfs_status_fops = {
@@ -245,21 +259,23 @@ static const struct file_operations debugfs_status_fops = {
static int debugfs_cfg_show(struct seq_file *s, void *data)
{
- if (!ideapad_priv) {
+ struct ideapad_private *priv = s->private;
+
+ if (!priv) {
seq_printf(s, "cfg: N/A\n");
} else {
seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
- ideapad_priv->cfg);
- if (test_bit(CFG_BT_BIT, &ideapad_priv->cfg))
+ priv->cfg);
+ if (test_bit(CFG_BT_BIT, &priv->cfg))
seq_printf(s, "Bluetooth ");
- if (test_bit(CFG_3G_BIT, &ideapad_priv->cfg))
+ if (test_bit(CFG_3G_BIT, &priv->cfg))
seq_printf(s, "3G ");
- if (test_bit(CFG_WIFI_BIT, &ideapad_priv->cfg))
+ if (test_bit(CFG_WIFI_BIT, &priv->cfg))
seq_printf(s, "Wireless ");
- if (test_bit(CFG_CAMERA_BIT, &ideapad_priv->cfg))
+ if (test_bit(CFG_CAMERA_BIT, &priv->cfg))
seq_printf(s, "Camera ");
seq_printf(s, "\nGraphic: ");
- switch ((ideapad_priv->cfg)&0x700) {
+ switch ((priv->cfg)&0x700) {
case 0x100:
seq_printf(s, "Intel");
break;
@@ -283,7 +299,7 @@ static int debugfs_cfg_show(struct seq_file *s, void *data)
static int debugfs_cfg_open(struct inode *inode, struct file *file)
{
- return single_open(file, debugfs_cfg_show, NULL);
+ return single_open(file, debugfs_cfg_show, inode->i_private);
}
static const struct file_operations debugfs_cfg_fops = {
@@ -294,7 +310,7 @@ static const struct file_operations debugfs_cfg_fops = {
.release = single_release,
};
-static int __devinit ideapad_debugfs_init(struct ideapad_private *priv)
+static int ideapad_debugfs_init(struct ideapad_private *priv)
{
struct dentry *node;
@@ -304,14 +320,14 @@ static int __devinit ideapad_debugfs_init(struct ideapad_private *priv)
goto errout;
}
- node = debugfs_create_file("cfg", S_IRUGO, priv->debug, NULL,
+ node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv,
&debugfs_cfg_fops);
if (!node) {
pr_err("failed to create cfg in debugfs");
goto errout;
}
- node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL,
+ node = debugfs_create_file("status", S_IRUGO, priv->debug, priv,
&debugfs_status_fops);
if (!node) {
pr_err("failed to create status in debugfs");
@@ -338,8 +354,9 @@ static ssize_t show_ideapad_cam(struct device *dev,
char *buf)
{
unsigned long result;
+ struct ideapad_private *priv = dev_get_drvdata(dev);
- if (read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &result))
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))
return sprintf(buf, "-1\n");
return sprintf(buf, "%lu\n", result);
}
@@ -349,21 +366,56 @@ static ssize_t store_ideapad_cam(struct device *dev,
const char *buf, size_t count)
{
int ret, state;
+ struct ideapad_private *priv = dev_get_drvdata(dev);
if (!count)
return 0;
if (sscanf(buf, "%i", &state) != 1)
return -EINVAL;
- ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state);
+ ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);
if (ret < 0)
- return ret;
+ return -EIO;
return count;
}
static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
+static ssize_t show_ideapad_fan(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned long result;
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))
+ return sprintf(buf, "-1\n");
+ return sprintf(buf, "%lu\n", result);
+}
+
+static ssize_t store_ideapad_fan(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret, state;
+ struct ideapad_private *priv = dev_get_drvdata(dev);
+
+ if (!count)
+ return 0;
+ if (sscanf(buf, "%i", &state) != 1)
+ return -EINVAL;
+ if (state < 0 || state > 4 || state == 3)
+ return -EINVAL;
+ ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);
+ if (ret < 0)
+ return -EIO;
+ return count;
+}
+
+static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
+
static struct attribute *ideapad_attributes[] = {
&dev_attr_camera_power.attr,
+ &dev_attr_fan_mode.attr,
NULL
};
@@ -377,7 +429,11 @@ static umode_t ideapad_is_visible(struct kobject *kobj,
if (attr == &dev_attr_camera_power.attr)
supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
- else
+ else if (attr == &dev_attr_fan_mode.attr) {
+ unsigned long value;
+ supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN,
+ &value);
+ } else
supported = true;
return supported ? attr->mode : 0;
@@ -406,9 +462,9 @@ const struct ideapad_rfk_data ideapad_rfk_data[] = {
static int ideapad_rfk_set(void *data, bool blocked)
{
- unsigned long opcode = (unsigned long)data;
+ struct ideapad_rfk_priv *priv = data;
- return write_ec_cmd(ideapad_handle, opcode, !blocked);
+ return write_ec_cmd(priv->priv->adev->handle, priv->dev, !blocked);
}
static struct rfkill_ops ideapad_rfk_ops = {
@@ -420,7 +476,7 @@ static void ideapad_sync_rfk_state(struct ideapad_private *priv)
unsigned long hw_blocked;
int i;
- if (read_ec_data(ideapad_handle, VPCCMD_R_RF, &hw_blocked))
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))
return;
hw_blocked = !hw_blocked;
@@ -429,28 +485,30 @@ static void ideapad_sync_rfk_state(struct ideapad_private *priv)
rfkill_set_hw_state(priv->rfk[i], hw_blocked);
}
-static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
- int dev)
+static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)
{
- struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
int ret;
unsigned long sw_blocked;
if (no_bt_rfkill &&
(ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {
/* Force to enable bluetooth when no_bt_rfkill=1 */
- write_ec_cmd(ideapad_handle,
+ write_ec_cmd(priv->adev->handle,
ideapad_rfk_data[dev].opcode, 1);
return 0;
}
-
- priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev,
- ideapad_rfk_data[dev].type, &ideapad_rfk_ops,
- (void *)(long)dev);
+ priv->rfk_priv[dev].dev = dev;
+ priv->rfk_priv[dev].priv = priv;
+
+ priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name,
+ &priv->platform_device->dev,
+ ideapad_rfk_data[dev].type,
+ &ideapad_rfk_ops,
+ &priv->rfk_priv[dev]);
if (!priv->rfk[dev])
return -ENOMEM;
- if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1,
+ if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,
&sw_blocked)) {
rfkill_init_sw_state(priv->rfk[dev], 0);
} else {
@@ -466,10 +524,8 @@ static int __devinit ideapad_register_rfkill(struct acpi_device *adevice,
return 0;
}
-static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
+static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)
{
- struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
-
if (!priv->rfk[dev])
return;
@@ -480,37 +536,16 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)
/*
* Platform device
*/
-static int __devinit ideapad_platform_init(struct ideapad_private *priv)
+static int ideapad_sysfs_init(struct ideapad_private *priv)
{
- int result;
-
- priv->platform_device = platform_device_alloc("ideapad", -1);
- if (!priv->platform_device)
- return -ENOMEM;
- platform_set_drvdata(priv->platform_device, priv);
-
- result = platform_device_add(priv->platform_device);
- if (result)
- goto fail_platform_device;
-
- result = sysfs_create_group(&priv->platform_device->dev.kobj,
+ return sysfs_create_group(&priv->platform_device->dev.kobj,
&ideapad_attribute_group);
- if (result)
- goto fail_sysfs;
- return 0;
-
-fail_sysfs:
- platform_device_del(priv->platform_device);
-fail_platform_device:
- platform_device_put(priv->platform_device);
- return result;
}
-static void ideapad_platform_exit(struct ideapad_private *priv)
+static void ideapad_sysfs_exit(struct ideapad_private *priv)
{
sysfs_remove_group(&priv->platform_device->dev.kobj,
&ideapad_attribute_group);
- platform_device_unregister(priv->platform_device);
}
/*
@@ -518,22 +553,26 @@ static void ideapad_platform_exit(struct ideapad_private *priv)
*/
static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, 7, { KEY_CAMERA } },
+ { KE_KEY, 11, { KEY_F16 } },
{ KE_KEY, 13, { KEY_WLAN } },
{ KE_KEY, 16, { KEY_PROG1 } },
{ KE_KEY, 17, { KEY_PROG2 } },
+ { KE_KEY, 64, { KEY_PROG3 } },
+ { KE_KEY, 65, { KEY_PROG4 } },
+ { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
+ { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
{ KE_END, 0 },
};
-static int __devinit ideapad_input_init(struct ideapad_private *priv)
+static int ideapad_input_init(struct ideapad_private *priv)
{
struct input_dev *inputdev;
int error;
inputdev = input_allocate_device();
- if (!inputdev) {
- pr_info("Unable to allocate input device\n");
+ if (!inputdev)
return -ENOMEM;
- }
inputdev->name = "Ideapad extra buttons";
inputdev->phys = "ideapad/input0";
@@ -579,7 +618,7 @@ static void ideapad_input_novokey(struct ideapad_private *priv)
{
unsigned long long_pressed;
- if (read_ec_data(ideapad_handle, VPCCMD_R_NOVO, &long_pressed))
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))
return;
if (long_pressed)
ideapad_input_report(priv, 17);
@@ -587,24 +626,59 @@ static void ideapad_input_novokey(struct ideapad_private *priv)
ideapad_input_report(priv, 16);
}
+static void ideapad_check_special_buttons(struct ideapad_private *priv)
+{
+ unsigned long bit, value;
+
+ read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
+
+ for (bit = 0; bit < 16; bit++) {
+ if (test_bit(bit, &value)) {
+ switch (bit) {
+ case 0: /* Z580 */
+ case 6: /* Z570 */
+ /* Thermal Management button */
+ ideapad_input_report(priv, 65);
+ break;
+ case 1:
+ /* OneKey Theater button */
+ ideapad_input_report(priv, 64);
+ break;
+ default:
+ pr_info("Unknown special button: %lu\n", bit);
+ break;
+ }
+ }
+ }
+}
+
/*
* backlight
*/
static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
{
+ struct ideapad_private *priv = bl_get_data(blightdev);
unsigned long now;
- if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
+ if (!priv)
+ return -EINVAL;
+
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
return -EIO;
return now;
}
static int ideapad_backlight_update_status(struct backlight_device *blightdev)
{
- if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL,
+ struct ideapad_private *priv = bl_get_data(blightdev);
+
+ if (!priv)
+ return -EINVAL;
+
+ if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,
blightdev->props.brightness))
return -EIO;
- if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL_POWER,
+ if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,
blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
return -EIO;
@@ -622,11 +696,11 @@ static int ideapad_backlight_init(struct ideapad_private *priv)
struct backlight_properties props;
unsigned long max, now, power;
- if (read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &max))
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))
return -EIO;
- if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))
return -EIO;
- if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
return -EIO;
memset(&props, 0, sizeof(struct backlight_properties));
@@ -664,7 +738,7 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv)
if (!blightdev)
return;
- if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
+ if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))
return;
blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
}
@@ -675,7 +749,7 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
/* if we control brightness via acpi video driver */
if (priv->blightdev == NULL) {
- read_ec_data(ideapad_handle, VPCCMD_R_BL, &now);
+ read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);
return;
}
@@ -685,32 +759,107 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
/*
* module init/exit
*/
-static const struct acpi_device_id ideapad_device_ids[] = {
- { "VPC2004", 0},
- { "", 0},
+static void ideapad_sync_touchpad_state(struct ideapad_private *priv)
+{
+ unsigned long value;
+
+ /* Without reading from EC touchpad LED doesn't switch state */
+ if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {
+ /* Some IdeaPads don't really turn off touchpad - they only
+ * switch the LED state. We (de)activate KBC AUX port to turn
+ * touchpad off and on. We send KEY_TOUCHPAD_OFF and
+ * KEY_TOUCHPAD_ON to not to get out of sync with LED */
+ unsigned char param;
+ i8042_command(&param, value ? I8042_CMD_AUX_ENABLE :
+ I8042_CMD_AUX_DISABLE);
+ ideapad_input_report(priv, value ? 67 : 66);
+ }
+}
+
+static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data)
+{
+ struct ideapad_private *priv = data;
+ unsigned long vpc1, vpc2, vpc_bit;
+
+ if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
+ return;
+ if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
+ return;
+
+ vpc1 = (vpc2 << 8) | vpc1;
+ for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
+ if (test_bit(vpc_bit, &vpc1)) {
+ switch (vpc_bit) {
+ case 9:
+ ideapad_sync_rfk_state(priv);
+ break;
+ case 13:
+ case 11:
+ case 7:
+ case 6:
+ ideapad_input_report(priv, vpc_bit);
+ break;
+ case 5:
+ ideapad_sync_touchpad_state(priv);
+ break;
+ case 4:
+ ideapad_backlight_notify_brightness(priv);
+ break;
+ case 3:
+ ideapad_input_novokey(priv);
+ break;
+ case 2:
+ ideapad_backlight_notify_power(priv);
+ break;
+ case 0:
+ ideapad_check_special_buttons(priv);
+ break;
+ default:
+ pr_info("Unknown event: %lu\n", vpc_bit);
+ }
+ }
+ }
+}
+
+/* Blacklist for devices where the ideapad rfkill interface does not work */
+static struct dmi_system_id rfkill_blacklist[] = {
+ /* The Lenovo Yoga 2 11 always reports everything as blocked */
+ {
+ .ident = "Lenovo Yoga 2 11",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"),
+ },
+ },
+ {}
};
-MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
-static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
+static int ideapad_acpi_add(struct platform_device *pdev)
{
int ret, i;
- unsigned long cfg;
+ int cfg;
struct ideapad_private *priv;
+ struct acpi_device *adev;
- if (read_method_int(adevice->handle, "_CFG", (int *)&cfg))
+ ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev);
+ if (ret)
return -ENODEV;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (read_method_int(adev->handle, "_CFG", &cfg))
+ return -ENODEV;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- dev_set_drvdata(&adevice->dev, priv);
- ideapad_priv = priv;
- ideapad_handle = adevice->handle;
+
+ dev_set_drvdata(&pdev->dev, priv);
priv->cfg = cfg;
+ priv->adev = adev;
+ priv->platform_device = pdev;
- ret = ideapad_platform_init(priv);
+ ret = ideapad_sysfs_init(priv);
if (ret)
- goto platform_failed;
+ return ret;
ret = ideapad_debugfs_init(priv);
if (ret)
@@ -720,113 +869,91 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
if (ret)
goto input_failed;
- for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) {
- if (test_bit(ideapad_rfk_data[i].cfgbit, &cfg))
- ideapad_register_rfkill(adevice, i);
- else
- priv->rfk[i] = NULL;
+ if (!dmi_check_system(rfkill_blacklist)) {
+ for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
+ if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg))
+ ideapad_register_rfkill(priv, i);
}
ideapad_sync_rfk_state(priv);
+ ideapad_sync_touchpad_state(priv);
if (!acpi_video_backlight_support()) {
ret = ideapad_backlight_init(priv);
if (ret && ret != -ENODEV)
goto backlight_failed;
}
+ ret = acpi_install_notify_handler(adev->handle,
+ ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv);
+ if (ret)
+ goto notification_failed;
return 0;
-
+notification_failed:
+ ideapad_backlight_exit(priv);
backlight_failed:
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
- ideapad_unregister_rfkill(adevice, i);
+ ideapad_unregister_rfkill(priv, i);
ideapad_input_exit(priv);
input_failed:
ideapad_debugfs_exit(priv);
debugfs_failed:
- ideapad_platform_exit(priv);
-platform_failed:
- kfree(priv);
+ ideapad_sysfs_exit(priv);
return ret;
}
-static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
+static int ideapad_acpi_remove(struct platform_device *pdev)
{
- struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+ struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);
int i;
+ acpi_remove_notify_handler(priv->adev->handle,
+ ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);
ideapad_backlight_exit(priv);
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
- ideapad_unregister_rfkill(adevice, i);
+ ideapad_unregister_rfkill(priv, i);
ideapad_input_exit(priv);
ideapad_debugfs_exit(priv);
- ideapad_platform_exit(priv);
- dev_set_drvdata(&adevice->dev, NULL);
- kfree(priv);
+ ideapad_sysfs_exit(priv);
+ dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
-static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
+#ifdef CONFIG_PM_SLEEP
+static int ideapad_acpi_resume(struct device *device)
{
- struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
- acpi_handle handle = adevice->handle;
- unsigned long vpc1, vpc2, vpc_bit;
+ struct ideapad_private *priv;
- if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
- return;
- if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
- return;
+ if (!device)
+ return -EINVAL;
+ priv = dev_get_drvdata(device);
- vpc1 = (vpc2 << 8) | vpc1;
- for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) {
- if (test_bit(vpc_bit, &vpc1)) {
- switch (vpc_bit) {
- case 9:
- ideapad_sync_rfk_state(priv);
- break;
- case 13:
- case 6:
- ideapad_input_report(priv, vpc_bit);
- break;
- case 4:
- ideapad_backlight_notify_brightness(priv);
- break;
- case 3:
- ideapad_input_novokey(priv);
- break;
- case 2:
- ideapad_backlight_notify_power(priv);
- break;
- default:
- pr_info("Unknown event: %lu\n", vpc_bit);
- }
- }
- }
+ ideapad_sync_rfk_state(priv);
+ ideapad_sync_touchpad_state(priv);
+ return 0;
}
+#endif
+static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
-static struct acpi_driver ideapad_acpi_driver = {
- .name = "ideapad_acpi",
- .class = "IdeaPad",
- .ids = ideapad_device_ids,
- .ops.add = ideapad_acpi_add,
- .ops.remove = ideapad_acpi_remove,
- .ops.notify = ideapad_acpi_notify,
- .owner = THIS_MODULE,
+static const struct acpi_device_id ideapad_device_ids[] = {
+ { "VPC2004", 0},
+ { "", 0},
};
+MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
-static int __init ideapad_acpi_module_init(void)
-{
- return acpi_bus_register_driver(&ideapad_acpi_driver);
-}
+static struct platform_driver ideapad_acpi_driver = {
+ .probe = ideapad_acpi_add,
+ .remove = ideapad_acpi_remove,
+ .driver = {
+ .name = "ideapad_acpi",
+ .owner = THIS_MODULE,
+ .pm = &ideapad_pm,
+ .acpi_match_table = ACPI_PTR(ideapad_device_ids),
+ },
+};
-static void __exit ideapad_acpi_module_exit(void)
-{
- acpi_bus_unregister_driver(&ideapad_acpi_driver);
-}
+module_platform_driver(ideapad_acpi_driver);
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("IdeaPad ACPI Extras");
MODULE_LICENSE("GPL");
-
-module_init(ideapad_acpi_module_init);
-module_exit(ideapad_acpi_module_exit);
diff --git a/drivers/platform/x86/intel-rst.c b/drivers/platform/x86/intel-rst.c
new file mode 100644
index 00000000000..d45bca34bf1
--- /dev/null
+++ b/drivers/platform/x86/intel-rst.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2013 Matthew Garrett <mjg59@srcf.ucam.org>
+ *
+ * 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.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+
+MODULE_LICENSE("GPL");
+
+static ssize_t irst_show_wakeup_events(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct acpi_device *acpi;
+ unsigned long long value;
+ acpi_status status;
+
+ acpi = to_acpi_device(dev);
+
+ status = acpi_evaluate_integer(acpi->handle, "GFFS", NULL, &value);
+ if (!ACPI_SUCCESS(status))
+ return -EINVAL;
+
+ return sprintf(buf, "%lld\n", value);
+}
+
+static ssize_t irst_store_wakeup_events(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi;
+ acpi_status status;
+ unsigned long value;
+ int error;
+
+ acpi = to_acpi_device(dev);
+
+ error = kstrtoul(buf, 0, &value);
+
+ if (error)
+ return error;
+
+ status = acpi_execute_simple_method(acpi->handle, "SFFS", value);
+
+ if (!ACPI_SUCCESS(status))
+ return -EINVAL;
+
+ return count;
+}
+
+static struct device_attribute irst_wakeup_attr = {
+ .attr = { .name = "wakeup_events", .mode = 0600 },
+ .show = irst_show_wakeup_events,
+ .store = irst_store_wakeup_events
+};
+
+static ssize_t irst_show_wakeup_time(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct acpi_device *acpi;
+ unsigned long long value;
+ acpi_status status;
+
+ acpi = to_acpi_device(dev);
+
+ status = acpi_evaluate_integer(acpi->handle, "GFTV", NULL, &value);
+ if (!ACPI_SUCCESS(status))
+ return -EINVAL;
+
+ return sprintf(buf, "%lld\n", value);
+}
+
+static ssize_t irst_store_wakeup_time(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct acpi_device *acpi;
+ acpi_status status;
+ unsigned long value;
+ int error;
+
+ acpi = to_acpi_device(dev);
+
+ error = kstrtoul(buf, 0, &value);
+
+ if (error)
+ return error;
+
+ status = acpi_execute_simple_method(acpi->handle, "SFTV", value);
+
+ if (!ACPI_SUCCESS(status))
+ return -EINVAL;
+
+ return count;
+}
+
+static struct device_attribute irst_timeout_attr = {
+ .attr = { .name = "wakeup_time", .mode = 0600 },
+ .show = irst_show_wakeup_time,
+ .store = irst_store_wakeup_time
+};
+
+static int irst_add(struct acpi_device *acpi)
+{
+ int error = 0;
+
+ error = device_create_file(&acpi->dev, &irst_timeout_attr);
+ if (error)
+ goto out;
+
+ error = device_create_file(&acpi->dev, &irst_wakeup_attr);
+ if (error)
+ goto out_timeout;
+
+ return 0;
+
+out_timeout:
+ device_remove_file(&acpi->dev, &irst_timeout_attr);
+out:
+ return error;
+}
+
+static int irst_remove(struct acpi_device *acpi)
+{
+ device_remove_file(&acpi->dev, &irst_wakeup_attr);
+ device_remove_file(&acpi->dev, &irst_timeout_attr);
+
+ return 0;
+}
+
+static const struct acpi_device_id irst_ids[] = {
+ {"INT3392", 0},
+ {"", 0}
+};
+
+static struct acpi_driver irst_driver = {
+ .owner = THIS_MODULE,
+ .name = "intel_rapid_start",
+ .class = "intel_rapid_start",
+ .ids = irst_ids,
+ .ops = {
+ .add = irst_add,
+ .remove = irst_remove,
+ },
+};
+
+module_acpi_driver(irst_driver);
+
+MODULE_DEVICE_TABLE(acpi, irst_ids);
diff --git a/drivers/platform/x86/intel-smartconnect.c b/drivers/platform/x86/intel-smartconnect.c
new file mode 100644
index 00000000000..04cf5dffdfd
--- /dev/null
+++ b/drivers/platform/x86/intel-smartconnect.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013 Matthew Garrett <mjg59@srcf.ucam.org>
+ *
+ * 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.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+
+MODULE_LICENSE("GPL");
+
+static int smartconnect_acpi_init(struct acpi_device *acpi)
+{
+ unsigned long long value;
+ acpi_status status;
+
+ status = acpi_evaluate_integer(acpi->handle, "GAOS", NULL, &value);
+ if (!ACPI_SUCCESS(status))
+ return -EINVAL;
+
+ if (value & 0x1) {
+ dev_info(&acpi->dev, "Disabling Intel Smart Connect\n");
+ status = acpi_execute_simple_method(acpi->handle, "SAOS", 0);
+ }
+
+ return 0;
+}
+
+static const struct acpi_device_id smartconnect_ids[] = {
+ {"INT33A0", 0},
+ {"", 0}
+};
+
+static struct acpi_driver smartconnect_driver = {
+ .owner = THIS_MODULE,
+ .name = "intel_smart_connect",
+ .class = "intel_smart_connect",
+ .ids = smartconnect_ids,
+ .ops = {
+ .add = smartconnect_acpi_init,
+ },
+};
+
+module_acpi_driver(smartconnect_driver);
+
+MODULE_DEVICE_TABLE(acpi, smartconnect_ids);
diff --git a/drivers/platform/x86/intel_ips.c b/drivers/platform/x86/intel_ips.c
index 0ffdb3cde2b..18dcb58ba96 100644
--- a/drivers/platform/x86/intel_ips.c
+++ b/drivers/platform/x86/intel_ips.c
@@ -72,6 +72,7 @@
#include <linux/string.h>
#include <linux/tick.h>
#include <linux/timer.h>
+#include <linux/dmi.h>
#include <drm/i915_drm.h>
#include <asm/msr.h>
#include <asm/processor.h>
@@ -1485,6 +1486,24 @@ static DEFINE_PCI_DEVICE_TABLE(ips_id_table) = {
MODULE_DEVICE_TABLE(pci, ips_id_table);
+static int ips_blacklist_callback(const struct dmi_system_id *id)
+{
+ pr_info("Blacklisted intel_ips for %s\n", id->ident);
+ return 1;
+}
+
+static const struct dmi_system_id ips_blacklist[] = {
+ {
+ .callback = ips_blacklist_callback,
+ .ident = "HP ProBook",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook"),
+ },
+ },
+ { } /* terminating entry */
+};
+
static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
u64 platform_info;
@@ -1494,6 +1513,9 @@ static int ips_probe(struct pci_dev *dev, const struct pci_device_id *id)
u16 htshi, trc, trc_required_mask;
u8 tse;
+ if (dmi_check_system(ips_blacklist))
+ return -ENODEV;
+
ips = kzalloc(sizeof(struct ips_driver), GFP_KERNEL);
if (!ips)
return -ENOMEM;
@@ -1697,21 +1719,6 @@ static void ips_remove(struct pci_dev *dev)
dev_dbg(&dev->dev, "IPS driver removed\n");
}
-#ifdef CONFIG_PM
-static int ips_suspend(struct pci_dev *dev, pm_message_t state)
-{
- return 0;
-}
-
-static int ips_resume(struct pci_dev *dev)
-{
- return 0;
-}
-#else
-#define ips_suspend NULL
-#define ips_resume NULL
-#endif /* CONFIG_PM */
-
static void ips_shutdown(struct pci_dev *dev)
{
}
@@ -1721,23 +1728,10 @@ static struct pci_driver ips_pci_driver = {
.id_table = ips_id_table,
.probe = ips_probe,
.remove = ips_remove,
- .suspend = ips_suspend,
- .resume = ips_resume,
.shutdown = ips_shutdown,
};
-static int __init ips_init(void)
-{
- return pci_register_driver(&ips_pci_driver);
-}
-module_init(ips_init);
-
-static void ips_exit(void)
-{
- pci_unregister_driver(&ips_pci_driver);
- return;
-}
-module_exit(ips_exit);
+module_pci_driver(ips_pci_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jesse Barnes <jbarnes@virtuousgeek.org>");
diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c
index 3271ac85115..e8b46d2c468 100644
--- a/drivers/platform/x86/intel_menlow.c
+++ b/drivers/platform/x86/intel_menlow.c
@@ -36,10 +36,8 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pm.h>
-
#include <linux/thermal.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
+#include <linux/acpi.h>
MODULE_AUTHOR("Thomas Sujith");
MODULE_AUTHOR("Zhang Rui");
@@ -156,19 +154,15 @@ static struct thermal_cooling_device_ops memory_cooling_ops = {
static int intel_menlow_memory_add(struct acpi_device *device)
{
int result = -ENODEV;
- acpi_status status = AE_OK;
- acpi_handle dummy;
struct thermal_cooling_device *cdev;
if (!device)
return -EINVAL;
- status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy);
- if (ACPI_FAILURE(status))
+ if (!acpi_has_method(device->handle, MEMORY_GET_BANDWIDTH))
goto end;
- status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy);
- if (ACPI_FAILURE(status))
+ if (!acpi_has_method(device->handle, MEMORY_SET_BANDWIDTH))
goto end;
cdev = thermal_cooling_device_register("Memory controller", device,
@@ -200,7 +194,7 @@ static int intel_menlow_memory_add(struct acpi_device *device)
}
-static int intel_menlow_memory_remove(struct acpi_device *device, int type)
+static int intel_menlow_memory_remove(struct acpi_device *device)
{
struct thermal_cooling_device *cdev = acpi_driver_data(device);
diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index bcbad8452a6..8d6775266d6 100644
--- a/drivers/platform/x86/intel_mid_powerbtn.c
+++ b/drivers/platform/x86/intel_mid_powerbtn.c
@@ -56,7 +56,7 @@ static irqreturn_t mfld_pb_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit mfld_pb_probe(struct platform_device *pdev)
+static int mfld_pb_probe(struct platform_device *pdev)
{
struct input_dev *input;
int irq = platform_get_irq(pdev, 0);
@@ -66,10 +66,8 @@ static int __devinit mfld_pb_probe(struct platform_device *pdev)
return -EINVAL;
input = input_allocate_device();
- if (!input) {
- dev_err(&pdev->dev, "Input device allocation error\n");
+ if (!input)
return -ENOMEM;
- }
input->name = pdev->name;
input->phys = "power-button/input0";
@@ -121,14 +119,13 @@ err_free_input:
return error;
}
-static int __devexit mfld_pb_remove(struct platform_device *pdev)
+static int mfld_pb_remove(struct platform_device *pdev)
{
struct input_dev *input = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
free_irq(irq, input);
input_unregister_device(input);
- platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -139,7 +136,7 @@ static struct platform_driver mfld_pb_driver = {
.owner = THIS_MODULE,
},
.probe = mfld_pb_probe,
- .remove = __devexit_p(mfld_pb_remove),
+ .remove = mfld_pb_remove,
};
module_platform_driver(mfld_pb_driver);
diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c
index 5ae9cd9c7e6..ab7860a21a2 100644
--- a/drivers/platform/x86/intel_mid_thermal.c
+++ b/drivers/platform/x86/intel_mid_thermal.c
@@ -418,23 +418,23 @@ static struct thermal_device_info *initialize_sensor(int index)
/**
* mid_thermal_resume - resume routine
- * @pdev: platform device structure
+ * @dev: device structure
*
* mid thermal resume: re-initializes the adc. Can sleep.
*/
-static int mid_thermal_resume(struct platform_device *pdev)
+static int mid_thermal_resume(struct device *dev)
{
- return mid_initialize_adc(&pdev->dev);
+ return mid_initialize_adc(dev);
}
/**
* mid_thermal_suspend - suspend routine
- * @pdev: platform device structure
+ * @dev: device structure
*
* mid thermal suspend implements the suspend functionality
* by stopping the ADC. Can sleep.
*/
-static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg)
+static int mid_thermal_suspend(struct device *dev)
{
/*
* This just stops the ADC and does not disable it.
@@ -444,6 +444,9 @@ static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg)
return configure_adc(0);
}
+static SIMPLE_DEV_PM_OPS(mid_thermal_pm,
+ mid_thermal_suspend, mid_thermal_resume);
+
/**
* read_curr_temp - reads the current temperature and stores in temp
* @temp: holds the current temperature value after reading
@@ -478,7 +481,8 @@ static int mid_thermal_probe(struct platform_device *pdev)
int i;
struct platform_info *pinfo;
- pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL);
+ pinfo = devm_kzalloc(&pdev->dev, sizeof(struct platform_info),
+ GFP_KERNEL);
if (!pinfo)
return -ENOMEM;
@@ -486,7 +490,6 @@ static int mid_thermal_probe(struct platform_device *pdev)
ret = mid_initialize_adc(&pdev->dev);
if (ret) {
dev_err(&pdev->dev, "ADC init failed");
- kfree(pinfo);
return ret;
}
@@ -499,7 +502,7 @@ static int mid_thermal_probe(struct platform_device *pdev)
goto err;
}
pinfo->tzd[i] = thermal_zone_device_register(name[i],
- 0, td_info, &tzd_ops, 0, 0, 0, 0);
+ 0, 0, td_info, &tzd_ops, NULL, 0, 0);
if (IS_ERR(pinfo->tzd[i])) {
kfree(td_info);
ret = PTR_ERR(pinfo->tzd[i]);
@@ -517,7 +520,6 @@ err:
thermal_zone_device_unregister(pinfo->tzd[i]);
}
configure_adc(0);
- kfree(pinfo);
return ret;
}
@@ -538,9 +540,6 @@ static int mid_thermal_remove(struct platform_device *pdev)
thermal_zone_device_unregister(pinfo->tzd[i]);
}
- kfree(pinfo);
- platform_set_drvdata(pdev, NULL);
-
/* Stop the ADC */
return configure_adc(0);
}
@@ -557,11 +556,10 @@ static struct platform_driver mid_thermal_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
+ .pm = &mid_thermal_pm,
},
.probe = mid_thermal_probe,
- .suspend = mid_thermal_suspend,
- .resume = mid_thermal_resume,
- .remove = __devexit_p(mid_thermal_remove),
+ .remove = mid_thermal_remove,
.id_table = therm_id_table,
};
diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c
index 79a0c2f6be5..4bc96041678 100644
--- a/drivers/platform/x86/intel_oaktrail.c
+++ b/drivers/platform/x86/intel_oaktrail.c
@@ -50,9 +50,6 @@
#include <linux/platform_device.h>
#include <linux/dmi.h>
#include <linux/rfkill.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
-
#define DRIVER_NAME "intel_oaktrail"
#define DRIVER_VERSION "0.4ac1"
@@ -278,12 +275,12 @@ static void oaktrail_backlight_exit(void)
backlight_device_unregister(oaktrail_bl_device);
}
-static int __devinit oaktrail_probe(struct platform_device *pdev)
+static int oaktrail_probe(struct platform_device *pdev)
{
return 0;
}
-static int __devexit oaktrail_remove(struct platform_device *pdev)
+static int oaktrail_remove(struct platform_device *pdev)
{
return 0;
}
@@ -294,7 +291,7 @@ static struct platform_driver oaktrail_driver = {
.owner = THIS_MODULE,
},
.probe = oaktrail_probe,
- .remove = __devexit_p(oaktrail_remove)
+ .remove = oaktrail_remove,
};
static int dmi_check_cb(const struct dmi_system_id *id)
diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c
index 1686c1e07d5..40929e4f7ad 100644
--- a/drivers/platform/x86/intel_pmic_gpio.c
+++ b/drivers/platform/x86/intel_pmic_gpio.c
@@ -91,7 +91,7 @@ static void pmic_program_irqtype(int gpio, int type)
static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
- if (offset > 8) {
+ if (offset >= 8) {
pr_err("only pin 0-7 support input\n");
return -1;/* we only have 8 GPIO can use as input */
}
@@ -130,7 +130,7 @@ static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset)
int ret;
/* we only have 8 GPIO pins we can use as input */
- if (offset > 8)
+ if (offset >= 8)
return -EOPNOTSUPP;
ret = intel_scu_ipc_ioread8(GPIO0 + offset, &r);
if (ret < 0)
@@ -230,7 +230,7 @@ static irqreturn_t pmic_irq_handler(int irq, void *data)
return ret;
}
-static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev)
+static int platform_pmic_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
int irq = platform_get_irq(pdev, 0);
@@ -288,7 +288,7 @@ static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev)
retval = request_irq(pg->irq, pmic_irq_handler, 0, "pmic", pg);
if (retval) {
pr_warn("Interrupt request failed\n");
- goto err;
+ goto fail_request_irq;
}
for (i = 0; i < 8; i++) {
@@ -299,6 +299,10 @@ static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev)
irq_set_chip_data(i + pg->irq_base, pg);
}
return 0;
+
+fail_request_irq:
+ if (gpiochip_remove(&pg->chip))
+ pr_err("gpiochip_remove failed\n");
err:
iounmap(pg->gpiointr);
err2:
diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c
index 9215ed72bec..76ca094ed01 100644
--- a/drivers/platform/x86/intel_scu_ipc.c
+++ b/drivers/platform/x86/intel_scu_ipc.c
@@ -25,7 +25,7 @@
#include <linux/interrupt.h>
#include <linux/sfi.h>
#include <linux/module.h>
-#include <asm/mrst.h>
+#include <asm/intel-mid.h>
#include <asm/intel_scu_ipc.h>
/* IPC defines the following message types */
@@ -58,12 +58,48 @@
* message handler is called within firmware.
*/
-#define IPC_BASE_ADDR 0xFF11C000 /* IPC1 base register address */
-#define IPC_MAX_ADDR 0x100 /* Maximum IPC regisers */
#define IPC_WWBUF_SIZE 20 /* IPC Write buffer Size */
#define IPC_RWBUF_SIZE 20 /* IPC Read buffer Size */
-#define IPC_I2C_BASE 0xFF12B000 /* I2C control register base address */
-#define IPC_I2C_MAX_ADDR 0x10 /* Maximum I2C regisers */
+#define IPC_IOC 0x100 /* IPC command register IOC bit */
+
+#define PCI_DEVICE_ID_LINCROFT 0x082a
+#define PCI_DEVICE_ID_PENWELL 0x080e
+#define PCI_DEVICE_ID_CLOVERVIEW 0x08ea
+#define PCI_DEVICE_ID_TANGIER 0x11a0
+
+/* intel scu ipc driver data*/
+struct intel_scu_ipc_pdata_t {
+ u32 ipc_base;
+ u32 i2c_base;
+ u32 ipc_len;
+ u32 i2c_len;
+ u8 irq_mode;
+};
+
+static struct intel_scu_ipc_pdata_t intel_scu_ipc_lincroft_pdata = {
+ .ipc_base = 0xff11c000,
+ .i2c_base = 0xff12b000,
+ .ipc_len = 0x100,
+ .i2c_len = 0x10,
+ .irq_mode = 0,
+};
+
+/* Penwell and Cloverview */
+static struct intel_scu_ipc_pdata_t intel_scu_ipc_penwell_pdata = {
+ .ipc_base = 0xff11c000,
+ .i2c_base = 0xff12b000,
+ .ipc_len = 0x100,
+ .i2c_len = 0x10,
+ .irq_mode = 1,
+};
+
+static struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = {
+ .ipc_base = 0xff009000,
+ .i2c_base = 0xff00d000,
+ .ipc_len = 0x100,
+ .i2c_len = 0x10,
+ .irq_mode = 0,
+};
static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id);
static void ipc_remove(struct pci_dev *pdev);
@@ -72,6 +108,8 @@ struct intel_scu_ipc_dev {
struct pci_dev *pdev;
void __iomem *ipc_base;
void __iomem *i2c_base;
+ struct completion cmd_complete;
+ u8 irq_mode;
};
static struct intel_scu_ipc_dev ipcdev; /* Only one for now */
@@ -98,6 +136,10 @@ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */
*/
static inline void ipc_command(u32 cmd) /* Send ipc command */
{
+ if (ipcdev.irq_mode) {
+ reinit_completion(&ipcdev.cmd_complete);
+ writel(cmd | IPC_IOC, ipcdev.ipc_base);
+ }
writel(cmd, ipcdev.ipc_base);
}
@@ -156,6 +198,30 @@ static inline int busy_loop(void) /* Wait till scu status is busy */
return 0;
}
+/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */
+static inline int ipc_wait_for_interrupt(void)
+{
+ int status;
+
+ if (!wait_for_completion_timeout(&ipcdev.cmd_complete, 3 * HZ)) {
+ struct device *dev = &ipcdev.pdev->dev;
+ dev_err(dev, "IPC timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ status = ipc_read_status();
+
+ if ((status >> 1) & 1)
+ return -EIO;
+
+ return 0;
+}
+
+int intel_scu_ipc_check_status(void)
+{
+ return ipcdev.irq_mode ? ipc_wait_for_interrupt() : busy_loop();
+}
+
/* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */
static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
{
@@ -196,8 +262,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)
ipc_command(4 << 16 | id << 12 | 0 << 8 | op);
}
- err = busy_loop();
- if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
+ err = intel_scu_ipc_check_status();
+ if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */
/* Workaround: values are read as 0 without memcpy_fromio */
memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16);
for (nc = 0; nc < count; nc++)
@@ -391,7 +457,7 @@ int intel_scu_ipc_simple_command(int cmd, int sub)
return -ENODEV;
}
ipc_command(sub << 12 | cmd);
- err = busy_loop();
+ err = intel_scu_ipc_check_status();
mutex_unlock(&ipclock);
return err;
}
@@ -425,10 +491,12 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,
ipc_data_writel(*in++, 4 * i);
ipc_command((inlen << 16) | (sub << 12) | cmd);
- err = busy_loop();
+ err = intel_scu_ipc_check_status();
- for (i = 0; i < outlen; i++)
- *out++ = ipc_data_readl(4 * i);
+ if (!err) {
+ for (i = 0; i < outlen; i++)
+ *out++ = ipc_data_readl(4 * i);
+ }
mutex_unlock(&ipclock);
return err;
@@ -491,6 +559,9 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);
*/
static irqreturn_t ioc(int irq, void *dev_id)
{
+ if (ipcdev.irq_mode)
+ complete(&ipcdev.cmd_complete);
+
return IRQ_HANDLED;
}
@@ -505,12 +576,16 @@ static irqreturn_t ioc(int irq, void *dev_id)
static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
int err;
+ struct intel_scu_ipc_pdata_t *pdata;
resource_size_t pci_resource;
if (ipcdev.pdev) /* We support only one SCU */
return -EBUSY;
+ pdata = (struct intel_scu_ipc_pdata_t *)id->driver_data;
+
ipcdev.pdev = pci_dev_get(dev);
+ ipcdev.irq_mode = pdata->irq_mode;
err = pci_enable_device(dev);
if (err)
@@ -524,14 +599,16 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (!pci_resource)
return -ENOMEM;
+ init_completion(&ipcdev.cmd_complete);
+
if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev))
return -EBUSY;
- ipcdev.ipc_base = ioremap_nocache(IPC_BASE_ADDR, IPC_MAX_ADDR);
+ ipcdev.ipc_base = ioremap_nocache(pdata->ipc_base, pdata->ipc_len);
if (!ipcdev.ipc_base)
return -ENOMEM;
- ipcdev.i2c_base = ioremap_nocache(IPC_I2C_BASE, IPC_I2C_MAX_ADDR);
+ ipcdev.i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);
if (!ipcdev.i2c_base) {
iounmap(ipcdev.ipc_base);
return -ENOMEM;
@@ -564,8 +641,21 @@ static void ipc_remove(struct pci_dev *pdev)
}
static DEFINE_PCI_DEVICE_TABLE(pci_ids) = {
- {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)},
- { 0,}
+ {
+ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_LINCROFT),
+ (kernel_ulong_t)&intel_scu_ipc_lincroft_pdata,
+ }, {
+ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PENWELL),
+ (kernel_ulong_t)&intel_scu_ipc_penwell_pdata,
+ }, {
+ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CLOVERVIEW),
+ (kernel_ulong_t)&intel_scu_ipc_penwell_pdata,
+ }, {
+ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_TANGIER),
+ (kernel_ulong_t)&intel_scu_ipc_tangier_pdata,
+ }, {
+ 0,
+ }
};
MODULE_DEVICE_TABLE(pci, pci_ids);
@@ -579,7 +669,7 @@ static struct pci_driver ipc_driver = {
static int __init intel_scu_ipc_init(void)
{
- platform = mrst_identify_cpu();
+ platform = intel_mid_identify_cpu();
if (platform == 0)
return -ENODEV;
return pci_register_driver(&ipc_driver);
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index bb5132128b3..62f8030b9e7 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -82,10 +82,24 @@
#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
-#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4
+#define MSI_STANDARD_EC_FUNCTIONS_ADDRESS 0xe4
+/* Power LED is orange - Turbo mode */
+#define MSI_STANDARD_EC_TURBO_MASK (1 << 1)
+/* Power LED is green - ECO mode */
+#define MSI_STANDARD_EC_ECO_MASK (1 << 3)
+/* Touchpad is turned on */
#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
+/* If this bit != bit 1, turbo mode can't be toggled */
+#define MSI_STANDARD_EC_TURBO_COOLDOWN_MASK (1 << 7)
-static int msi_laptop_resume(struct platform_device *device);
+#define MSI_STANDARD_EC_FAN_ADDRESS 0x33
+/* If zero, fan rotates at maximal speed */
+#define MSI_STANDARD_EC_AUTOFAN_MASK (1 << 0)
+
+#ifdef CONFIG_PM_SLEEP
+static int msi_laptop_resume(struct device *device);
+#endif
+static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume);
#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
@@ -105,23 +119,38 @@ static const struct key_entry msi_laptop_keymap[] = {
static struct input_dev *msi_laptop_input_dev;
-static bool old_ec_model;
static int wlan_s, bluetooth_s, threeg_s;
static int threeg_exists;
-
-/* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G,
- * those netbook will load the SCM (windows app) to disable the original
- * Wlan/Bluetooth control by BIOS when user press fn key, then control
- * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user
- * cann't on/off 3G module on those 3G netbook.
- * On Linux, msi-laptop driver will do the same thing to disable the
- * original BIOS control, then might need use HAL or other userland
- * application to do the software control that simulate with SCM.
- * e.g. MSI N034 netbook
- */
-static bool load_scm_model;
static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg;
+/* MSI laptop quirks */
+struct quirk_entry {
+ bool old_ec_model;
+
+ /* Some MSI 3G netbook only have one fn key to control
+ * Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to
+ * disable the original Wlan/Bluetooth control by BIOS when user press
+ * fn key, then control Wlan/Bluetooth/3G by SCM (software control by
+ * OS). Without SCM, user cann't on/off 3G module on those 3G netbook.
+ * On Linux, msi-laptop driver will do the same thing to disable the
+ * original BIOS control, then might need use HAL or other userland
+ * application to do the software control that simulate with SCM.
+ * e.g. MSI N034 netbook
+ */
+ bool load_scm_model;
+
+ /* Some MSI laptops need delay before reading from EC */
+ bool ec_delay;
+
+ /* Some MSI Wind netbooks (e.g. MSI Wind U100) need loading SCM to get
+ * some features working (e.g. ECO mode), but we cannot change
+ * Wlan/Bluetooth state in software and we can only read its state.
+ */
+ bool ec_read_only;
+};
+
+static struct quirk_entry *quirks;
+
/* Hardware access */
static int set_lcd_level(int level)
@@ -192,10 +221,13 @@ static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))
return -EINVAL;
+ if (quirks->ec_read_only)
+ return -EOPNOTSUPP;
+
/* read current device state */
result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
if (result < 0)
- return -EINVAL;
+ return result;
if (!!(rdata & mask) != status) {
/* reverse device bit */
@@ -206,7 +238,7 @@ static ssize_t set_device_state(const char *buf, size_t count, u8 mask)
result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);
if (result < 0)
- return -EINVAL;
+ return result;
}
return count;
@@ -219,7 +251,7 @@ static int get_wireless_state(int *wlan, int *bluetooth)
result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);
if (result < 0)
- return -1;
+ return result;
if (wlan)
*wlan = !!(rdata & 8);
@@ -237,7 +269,7 @@ static int get_wireless_state_ec_standard(void)
result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);
if (result < 0)
- return -1;
+ return result;
wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK);
@@ -255,7 +287,7 @@ static int get_threeg_exists(void)
result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);
if (result < 0)
- return -1;
+ return result;
threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK);
@@ -288,9 +320,9 @@ static ssize_t show_wlan(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int ret, enabled;
+ int ret, enabled = 0;
- if (old_ec_model) {
+ if (quirks->old_ec_model) {
ret = get_wireless_state(&enabled, NULL);
} else {
ret = get_wireless_state_ec_standard();
@@ -312,9 +344,9 @@ static ssize_t show_bluetooth(struct device *dev,
struct device_attribute *attr, char *buf)
{
- int ret, enabled;
+ int ret, enabled = 0;
- if (old_ec_model) {
+ if (quirks->old_ec_model) {
ret = get_wireless_state(NULL, &enabled);
} else {
ret = get_wireless_state_ec_standard();
@@ -339,8 +371,8 @@ static ssize_t show_threeg(struct device *dev,
int ret;
/* old msi ec not support 3G */
- if (old_ec_model)
- return -1;
+ if (quirks->old_ec_model)
+ return -ENODEV;
ret = get_wireless_state_ec_standard();
if (ret < 0)
@@ -414,18 +446,119 @@ static ssize_t store_auto_brightness(struct device *dev,
return count;
}
+static ssize_t show_touchpad(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ u8 rdata;
+ int result;
+
+ result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
+ if (result < 0)
+ return result;
+
+ return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK));
+}
+
+static ssize_t show_turbo(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ u8 rdata;
+ int result;
+
+ result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
+ if (result < 0)
+ return result;
+
+ return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK));
+}
+
+static ssize_t show_eco(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ u8 rdata;
+ int result;
+
+ result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
+ if (result < 0)
+ return result;
+
+ return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK));
+}
+
+static ssize_t show_turbo_cooldown(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ u8 rdata;
+ int result;
+
+ result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
+ if (result < 0)
+ return result;
+
+ return sprintf(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) |
+ (!!(rdata & MSI_STANDARD_EC_TURBO_COOLDOWN_MASK) << 1));
+}
+
+static ssize_t show_auto_fan(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ u8 rdata;
+ int result;
+
+ result = ec_read(MSI_STANDARD_EC_FAN_ADDRESS, &rdata);
+ if (result < 0)
+ return result;
+
+ return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK));
+}
+
+static ssize_t store_auto_fan(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+
+ int enable, result;
+
+ if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1)))
+ return -EINVAL;
+
+ result = ec_write(MSI_STANDARD_EC_FAN_ADDRESS, enable);
+ if (result < 0)
+ return result;
+
+ return count;
+}
+
static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,
store_auto_brightness);
static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);
static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);
static DEVICE_ATTR(threeg, 0444, show_threeg, NULL);
+static DEVICE_ATTR(touchpad, 0444, show_touchpad, NULL);
+static DEVICE_ATTR(turbo_mode, 0444, show_turbo, NULL);
+static DEVICE_ATTR(eco_mode, 0444, show_eco, NULL);
+static DEVICE_ATTR(turbo_cooldown, 0444, show_turbo_cooldown, NULL);
+static DEVICE_ATTR(auto_fan, 0644, show_auto_fan, store_auto_fan);
static struct attribute *msipf_attributes[] = {
- &dev_attr_lcd_level.attr,
- &dev_attr_auto_brightness.attr,
&dev_attr_bluetooth.attr,
&dev_attr_wlan.attr,
+ &dev_attr_touchpad.attr,
+ &dev_attr_turbo_mode.attr,
+ &dev_attr_eco_mode.attr,
+ &dev_attr_turbo_cooldown.attr,
+ &dev_attr_auto_fan.attr,
+ NULL
+};
+
+static struct attribute *msipf_old_attributes[] = {
+ &dev_attr_lcd_level.attr,
+ &dev_attr_auto_brightness.attr,
NULL
};
@@ -433,21 +566,42 @@ static struct attribute_group msipf_attribute_group = {
.attrs = msipf_attributes
};
+static struct attribute_group msipf_old_attribute_group = {
+ .attrs = msipf_old_attributes
+};
+
static struct platform_driver msipf_driver = {
.driver = {
.name = "msi-laptop-pf",
.owner = THIS_MODULE,
+ .pm = &msi_laptop_pm,
},
- .resume = msi_laptop_resume,
};
static struct platform_device *msipf_device;
/* Initialization */
-static int dmi_check_cb(const struct dmi_system_id *id)
+static struct quirk_entry quirk_old_ec_model = {
+ .old_ec_model = true,
+};
+
+static struct quirk_entry quirk_load_scm_model = {
+ .load_scm_model = true,
+ .ec_delay = true,
+};
+
+static struct quirk_entry quirk_load_scm_ro_model = {
+ .load_scm_model = true,
+ .ec_read_only = true,
+};
+
+static int dmi_check_cb(const struct dmi_system_id *dmi)
{
- pr_info("Identified laptop model '%s'\n", id->ident);
+ pr_info("Identified laptop model '%s'\n", dmi->ident);
+
+ quirks = dmi->driver_data;
+
return 1;
}
@@ -461,6 +615,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
DMI_MATCH(DMI_CHASSIS_VENDOR,
"MICRO-STAR INT'L CO.,LTD")
},
+ .driver_data = &quirk_old_ec_model,
.callback = dmi_check_cb
},
{
@@ -471,6 +626,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),
DMI_MATCH(DMI_BOARD_NAME, "MS-1058")
},
+ .driver_data = &quirk_old_ec_model,
.callback = dmi_check_cb
},
{
@@ -481,6 +637,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),
DMI_MATCH(DMI_BOARD_NAME, "MS-1412")
},
+ .driver_data = &quirk_old_ec_model,
.callback = dmi_check_cb
},
{
@@ -492,12 +649,9 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {
DMI_MATCH(DMI_CHASSIS_VENDOR,
"MICRO-STAR INT'L CO.,LTD")
},
+ .driver_data = &quirk_old_ec_model,
.callback = dmi_check_cb
},
- { }
-};
-
-static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
{
.ident = "MSI N034",
.matches = {
@@ -507,6 +661,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
DMI_MATCH(DMI_CHASSIS_VENDOR,
"MICRO-STAR INTERNATIONAL CO., LTD")
},
+ .driver_data = &quirk_load_scm_model,
.callback = dmi_check_cb
},
{
@@ -518,6 +673,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
DMI_MATCH(DMI_CHASSIS_VENDOR,
"MICRO-STAR INTERNATIONAL CO., LTD")
},
+ .driver_data = &quirk_load_scm_model,
.callback = dmi_check_cb
},
{
@@ -527,6 +683,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
"MICRO-STAR INTERNATIONAL CO., LTD"),
DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),
},
+ .driver_data = &quirk_load_scm_model,
.callback = dmi_check_cb
},
{
@@ -536,6 +693,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
"Micro-Star International"),
DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),
},
+ .driver_data = &quirk_load_scm_model,
.callback = dmi_check_cb
},
{
@@ -545,6 +703,17 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {
"Micro-Star International Co., Ltd."),
DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),
},
+ .driver_data = &quirk_load_scm_model,
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "MSI U90/U100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR,
+ "MICRO-STAR INTERNATIONAL CO., LTD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "U90/U100"),
+ },
+ .driver_data = &quirk_load_scm_ro_model,
.callback = dmi_check_cb
},
{ }
@@ -557,32 +726,26 @@ static int rfkill_bluetooth_set(void *data, bool blocked)
* blocked == false is on
* blocked == true is off
*/
- if (blocked)
- set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
- else
- set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK);
+ int result = set_device_state(blocked ? "0" : "1", 0,
+ MSI_STANDARD_EC_BLUETOOTH_MASK);
- return 0;
+ return min(result, 0);
}
static int rfkill_wlan_set(void *data, bool blocked)
{
- if (blocked)
- set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK);
- else
- set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK);
+ int result = set_device_state(blocked ? "0" : "1", 0,
+ MSI_STANDARD_EC_WLAN_MASK);
- return 0;
+ return min(result, 0);
}
static int rfkill_threeg_set(void *data, bool blocked)
{
- if (blocked)
- set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK);
- else
- set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK);
+ int result = set_device_state(blocked ? "0" : "1", 0,
+ MSI_STANDARD_EC_3G_MASK);
- return 0;
+ return min(result, 0);
}
static const struct rfkill_ops rfkill_bluetooth_ops = {
@@ -615,25 +778,34 @@ static void rfkill_cleanup(void)
}
}
+static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked)
+{
+ if (quirks->ec_read_only)
+ return rfkill_set_hw_state(rfkill, blocked);
+ else
+ return rfkill_set_sw_state(rfkill, blocked);
+}
+
static void msi_update_rfkill(struct work_struct *ignored)
{
get_wireless_state_ec_standard();
if (rfk_wlan)
- rfkill_set_sw_state(rfk_wlan, !wlan_s);
+ msi_rfkill_set_state(rfk_wlan, !wlan_s);
if (rfk_bluetooth)
- rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s);
+ msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s);
if (rfk_threeg)
- rfkill_set_sw_state(rfk_threeg, !threeg_s);
+ msi_rfkill_set_state(rfk_threeg, !threeg_s);
}
-static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
+static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill);
+static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill);
static void msi_send_touchpad_key(struct work_struct *ignored)
{
u8 rdata;
int result;
- result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata);
+ result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);
if (result < 0)
return;
@@ -641,7 +813,8 @@ static void msi_send_touchpad_key(struct work_struct *ignored)
(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
}
-static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key);
+static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key);
+static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key);
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
@@ -659,14 +832,20 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
extended = false;
switch (data) {
case 0xE4:
- schedule_delayed_work(&msi_touchpad_work,
- round_jiffies_relative(0.5 * HZ));
+ if (quirks->ec_delay) {
+ schedule_delayed_work(&msi_touchpad_dwork,
+ round_jiffies_relative(0.5 * HZ));
+ } else
+ schedule_work(&msi_touchpad_work);
break;
case 0x54:
case 0x62:
case 0x76:
- schedule_delayed_work(&msi_rfkill_work,
- round_jiffies_relative(0.5 * HZ));
+ if (quirks->ec_delay) {
+ schedule_delayed_work(&msi_rfkill_dwork,
+ round_jiffies_relative(0.5 * HZ));
+ } else
+ schedule_work(&msi_rfkill_work);
break;
}
}
@@ -733,8 +912,11 @@ static int rfkill_init(struct platform_device *sdev)
}
/* schedule to run rfkill state initial */
- schedule_delayed_work(&msi_rfkill_init,
- round_jiffies_relative(1 * HZ));
+ if (quirks->ec_delay) {
+ schedule_delayed_work(&msi_rfkill_init,
+ round_jiffies_relative(1 * HZ));
+ } else
+ schedule_work(&msi_rfkill_work);
return 0;
@@ -752,12 +934,13 @@ err_bluetooth:
return retval;
}
-static int msi_laptop_resume(struct platform_device *device)
+#ifdef CONFIG_PM_SLEEP
+static int msi_laptop_resume(struct device *device)
{
u8 data;
int result;
- if (!load_scm_model)
+ if (!quirks->load_scm_model)
return 0;
/* set load SCM to disable hardware control by fn key */
@@ -772,6 +955,7 @@ static int msi_laptop_resume(struct platform_device *device)
return 0;
}
+#endif
static int __init msi_laptop_input_setup(void)
{
@@ -814,13 +998,15 @@ static int __init load_scm_model_init(struct platform_device *sdev)
u8 data;
int result;
- /* allow userland write sysfs file */
- dev_attr_bluetooth.store = store_bluetooth;
- dev_attr_wlan.store = store_wlan;
- dev_attr_threeg.store = store_threeg;
- dev_attr_bluetooth.attr.mode |= S_IWUSR;
- dev_attr_wlan.attr.mode |= S_IWUSR;
- dev_attr_threeg.attr.mode |= S_IWUSR;
+ if (!quirks->ec_read_only) {
+ /* allow userland write sysfs file */
+ dev_attr_bluetooth.store = store_bluetooth;
+ dev_attr_wlan.store = store_wlan;
+ dev_attr_threeg.store = store_threeg;
+ dev_attr_bluetooth.attr.mode |= S_IWUSR;
+ dev_attr_wlan.attr.mode |= S_IWUSR;
+ dev_attr_threeg.attr.mode |= S_IWUSR;
+ }
/* disable hardware control by fn key */
result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data);
@@ -869,21 +1055,22 @@ static int __init msi_init(void)
if (acpi_disabled)
return -ENODEV;
- if (force || dmi_check_system(msi_dmi_table))
- old_ec_model = 1;
+ dmi_check_system(msi_dmi_table);
+ if (!quirks)
+ /* quirks may be NULL if no match in DMI table */
+ quirks = &quirk_load_scm_model;
+ if (force)
+ quirks = &quirk_old_ec_model;
- if (!old_ec_model)
+ if (!quirks->old_ec_model)
get_threeg_exists();
- if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table))
- load_scm_model = 1;
-
if (auto_brightness < 0 || auto_brightness > 2)
return -EINVAL;
/* Register backlight stuff */
- if (acpi_video_backlight_support()) {
+ if (!quirks->old_ec_model || acpi_video_backlight_support()) {
pr_info("Brightness ignored, must be controlled by ACPI video driver\n");
} else {
struct backlight_properties props;
@@ -911,56 +1098,58 @@ static int __init msi_init(void)
ret = platform_device_add(msipf_device);
if (ret)
- goto fail_platform_device1;
+ goto fail_device_add;
- if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
+ if (quirks->load_scm_model && (load_scm_model_init(msipf_device) < 0)) {
ret = -EINVAL;
- goto fail_platform_device1;
+ goto fail_scm_model_init;
}
ret = sysfs_create_group(&msipf_device->dev.kobj,
&msipf_attribute_group);
if (ret)
- goto fail_platform_device2;
+ goto fail_create_group;
- if (!old_ec_model) {
+ if (!quirks->old_ec_model) {
if (threeg_exists)
ret = device_create_file(&msipf_device->dev,
&dev_attr_threeg);
if (ret)
- goto fail_platform_device2;
- }
+ goto fail_create_attr;
+ } else {
+ ret = sysfs_create_group(&msipf_device->dev.kobj,
+ &msipf_old_attribute_group);
+ if (ret)
+ goto fail_create_attr;
- /* Disable automatic brightness control by default because
- * this module was probably loaded to do brightness control in
- * software. */
+ /* Disable automatic brightness control by default because
+ * this module was probably loaded to do brightness control in
+ * software. */
- if (auto_brightness != 2)
- set_auto_brightness(auto_brightness);
+ if (auto_brightness != 2)
+ set_auto_brightness(auto_brightness);
+ }
pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n");
return 0;
-fail_platform_device2:
-
- if (load_scm_model) {
+fail_create_attr:
+ sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
+fail_create_group:
+ if (quirks->load_scm_model) {
i8042_remove_filter(msi_laptop_i8042_filter);
- cancel_delayed_work_sync(&msi_rfkill_work);
+ cancel_delayed_work_sync(&msi_rfkill_dwork);
+ cancel_work_sync(&msi_rfkill_work);
rfkill_cleanup();
}
+fail_scm_model_init:
platform_device_del(msipf_device);
-
-fail_platform_device1:
-
+fail_device_add:
platform_device_put(msipf_device);
-
fail_platform_driver:
-
platform_driver_unregister(&msipf_driver);
-
fail_backlight:
-
backlight_device_unregister(msibl_device);
return ret;
@@ -968,23 +1157,26 @@ fail_backlight:
static void __exit msi_cleanup(void)
{
- if (load_scm_model) {
+ if (quirks->load_scm_model) {
i8042_remove_filter(msi_laptop_i8042_filter);
msi_laptop_input_destroy();
- cancel_delayed_work_sync(&msi_rfkill_work);
+ cancel_delayed_work_sync(&msi_rfkill_dwork);
+ cancel_work_sync(&msi_rfkill_work);
rfkill_cleanup();
}
sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group);
- if (!old_ec_model && threeg_exists)
+ if (!quirks->old_ec_model && threeg_exists)
device_remove_file(&msipf_device->dev, &dev_attr_threeg);
platform_device_unregister(msipf_device);
platform_driver_unregister(&msipf_driver);
backlight_device_unregister(msibl_device);
- /* Enable automatic brightness control again */
- if (auto_brightness != 2)
- set_auto_brightness(1);
+ if (quirks->old_ec_model) {
+ /* Enable automatic brightness control again */
+ if (auto_brightness != 2)
+ set_auto_brightness(1);
+ }
pr_info("driver unloaded\n");
}
@@ -1006,3 +1198,4 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");
MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");
MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");
MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*");
+MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnU90/U100:*");
diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c
index 2264331bd48..70222f265f6 100644
--- a/drivers/platform/x86/msi-wmi.c
+++ b/drivers/platform/x86/msi-wmi.c
@@ -34,29 +34,65 @@ MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");
MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45");
-MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2");
-
#define DRV_NAME "msi-wmi"
#define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45"
-#define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
-
-#define SCANCODE_BASE 0xD0
-#define MSI_WMI_BRIGHTNESSUP SCANCODE_BASE
-#define MSI_WMI_BRIGHTNESSDOWN (SCANCODE_BASE + 1)
-#define MSI_WMI_VOLUMEUP (SCANCODE_BASE + 2)
-#define MSI_WMI_VOLUMEDOWN (SCANCODE_BASE + 3)
-#define MSI_WMI_MUTE (SCANCODE_BASE + 4)
+#define MSIWMI_MSI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"
+#define MSIWMI_WIND_EVENT_GUID "5B3CC38A-40D9-7245-8AE6-1145B751BE3F"
+
+MODULE_ALIAS("wmi:" MSIWMI_BIOS_GUID);
+MODULE_ALIAS("wmi:" MSIWMI_MSI_EVENT_GUID);
+MODULE_ALIAS("wmi:" MSIWMI_WIND_EVENT_GUID);
+
+enum msi_scancodes {
+ /* Generic MSI keys (not present on MSI Wind) */
+ MSI_KEY_BRIGHTNESSUP = 0xD0,
+ MSI_KEY_BRIGHTNESSDOWN,
+ MSI_KEY_VOLUMEUP,
+ MSI_KEY_VOLUMEDOWN,
+ MSI_KEY_MUTE,
+ /* MSI Wind keys */
+ WIND_KEY_TOUCHPAD = 0x08, /* Fn+F3 touchpad toggle */
+ WIND_KEY_BLUETOOTH = 0x56, /* Fn+F11 Bluetooth toggle */
+ WIND_KEY_CAMERA, /* Fn+F6 webcam toggle */
+ WIND_KEY_WLAN = 0x5f, /* Fn+F11 Wi-Fi toggle */
+ WIND_KEY_TURBO, /* Fn+F10 turbo mode toggle */
+ WIND_KEY_ECO = 0x69, /* Fn+F10 ECO mode toggle */
+};
static struct key_entry msi_wmi_keymap[] = {
- { KE_KEY, MSI_WMI_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} },
- { KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} },
- { KE_KEY, MSI_WMI_VOLUMEUP, {KEY_VOLUMEUP} },
- { KE_KEY, MSI_WMI_VOLUMEDOWN, {KEY_VOLUMEDOWN} },
- { KE_KEY, MSI_WMI_MUTE, {KEY_MUTE} },
- { KE_END, 0}
+ { KE_KEY, MSI_KEY_BRIGHTNESSUP, {KEY_BRIGHTNESSUP} },
+ { KE_KEY, MSI_KEY_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} },
+ { KE_KEY, MSI_KEY_VOLUMEUP, {KEY_VOLUMEUP} },
+ { KE_KEY, MSI_KEY_VOLUMEDOWN, {KEY_VOLUMEDOWN} },
+ { KE_KEY, MSI_KEY_MUTE, {KEY_MUTE} },
+
+ /* These keys work without WMI. Ignore them to avoid double keycodes */
+ { KE_IGNORE, WIND_KEY_TOUCHPAD, {KEY_TOUCHPAD_TOGGLE} },
+ { KE_IGNORE, WIND_KEY_BLUETOOTH, {KEY_BLUETOOTH} },
+ { KE_IGNORE, WIND_KEY_CAMERA, {KEY_CAMERA} },
+ { KE_IGNORE, WIND_KEY_WLAN, {KEY_WLAN} },
+
+ /* These are unknown WMI events found on MSI Wind */
+ { KE_IGNORE, 0x00 },
+ { KE_IGNORE, 0x62 },
+ { KE_IGNORE, 0x63 },
+
+ /* These are MSI Wind keys that should be handled via WMI */
+ { KE_KEY, WIND_KEY_TURBO, {KEY_PROG1} },
+ { KE_KEY, WIND_KEY_ECO, {KEY_PROG2} },
+
+ { KE_END, 0 }
+};
+
+static ktime_t last_pressed;
+
+static const struct {
+ const char *guid;
+ bool quirk_last_pressed;
+} *event_wmi, event_wmis[] = {
+ { MSIWMI_MSI_EVENT_GUID, true },
+ { MSIWMI_WIND_EVENT_GUID, false },
};
-static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1];
static struct backlight_device *backlight;
@@ -149,7 +185,6 @@ static void msi_wmi_notify(u32 value, void *context)
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
static struct key_entry *key;
union acpi_object *obj;
- ktime_t cur;
acpi_status status;
status = wmi_get_event_data(value, &response);
@@ -165,39 +200,67 @@ static void msi_wmi_notify(u32 value, void *context)
pr_debug("Eventcode: 0x%x\n", eventcode);
key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev,
eventcode);
- if (key) {
- ktime_t diff;
- cur = ktime_get_real();
- diff = ktime_sub(cur, last_pressed[key->code -
- SCANCODE_BASE]);
- /* Ignore event if the same event happened in a 50 ms
+ if (!key) {
+ pr_info("Unknown key pressed - %x\n", eventcode);
+ goto msi_wmi_notify_exit;
+ }
+
+ if (event_wmi->quirk_last_pressed) {
+ ktime_t cur = ktime_get_real();
+ ktime_t diff = ktime_sub(cur, last_pressed);
+ /* Ignore event if any event happened in a 50 ms
timeframe -> Key press may result in 10-20 GPEs */
if (ktime_to_us(diff) < 1000 * 50) {
pr_debug("Suppressed key event 0x%X - "
"Last press was %lld us ago\n",
key->code, ktime_to_us(diff));
- return;
- }
- last_pressed[key->code - SCANCODE_BASE] = cur;
-
- if (key->type == KE_KEY &&
- /* Brightness is served via acpi video driver */
- (!acpi_video_backlight_support() ||
- (key->code != MSI_WMI_BRIGHTNESSUP &&
- key->code != MSI_WMI_BRIGHTNESSDOWN))) {
- pr_debug("Send key: 0x%X - "
- "Input layer keycode: %d\n",
- key->code, key->keycode);
- sparse_keymap_report_entry(msi_wmi_input_dev,
- key, 1, true);
+ goto msi_wmi_notify_exit;
}
- } else
- pr_info("Unknown key pressed - %x\n", eventcode);
+ last_pressed = cur;
+ }
+
+ if (key->type == KE_KEY &&
+ /* Brightness is served via acpi video driver */
+ (backlight ||
+ (key->code != MSI_KEY_BRIGHTNESSUP &&
+ key->code != MSI_KEY_BRIGHTNESSDOWN))) {
+ pr_debug("Send key: 0x%X - Input layer keycode: %d\n",
+ key->code, key->keycode);
+ sparse_keymap_report_entry(msi_wmi_input_dev, key, 1,
+ true);
+ }
} else
pr_info("Unknown event received\n");
+
+msi_wmi_notify_exit:
kfree(response.pointer);
}
+static int __init msi_wmi_backlight_setup(void)
+{
+ int err;
+ struct backlight_properties props;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = ARRAY_SIZE(backlight_map) - 1;
+ backlight = backlight_device_register(DRV_NAME, NULL, NULL,
+ &msi_backlight_ops,
+ &props);
+ if (IS_ERR(backlight))
+ return PTR_ERR(backlight);
+
+ err = bl_get(NULL);
+ if (err < 0) {
+ backlight_device_unregister(backlight);
+ return err;
+ }
+
+ backlight->props.brightness = err;
+
+ return 0;
+}
+
static int __init msi_wmi_input_setup(void)
{
int err;
@@ -219,7 +282,7 @@ static int __init msi_wmi_input_setup(void)
if (err)
goto err_free_keymap;
- memset(last_pressed, 0, sizeof(last_pressed));
+ last_pressed = ktime_set(0, 0);
return 0;
@@ -233,61 +296,66 @@ err_free_dev:
static int __init msi_wmi_init(void)
{
int err;
+ int i;
- if (!wmi_has_guid(MSIWMI_EVENT_GUID)) {
- pr_err("This machine doesn't have MSI-hotkeys through WMI\n");
- return -ENODEV;
- }
- err = wmi_install_notify_handler(MSIWMI_EVENT_GUID,
- msi_wmi_notify, NULL);
- if (ACPI_FAILURE(err))
- return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(event_wmis); i++) {
+ if (!wmi_has_guid(event_wmis[i].guid))
+ continue;
- err = msi_wmi_input_setup();
- if (err)
- goto err_uninstall_notifier;
-
- if (!acpi_video_backlight_support()) {
- struct backlight_properties props;
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_PLATFORM;
- props.max_brightness = ARRAY_SIZE(backlight_map) - 1;
- backlight = backlight_device_register(DRV_NAME, NULL, NULL,
- &msi_backlight_ops,
- &props);
- if (IS_ERR(backlight)) {
- err = PTR_ERR(backlight);
+ err = msi_wmi_input_setup();
+ if (err) {
+ pr_err("Unable to setup input device\n");
+ return err;
+ }
+
+ err = wmi_install_notify_handler(event_wmis[i].guid,
+ msi_wmi_notify, NULL);
+ if (ACPI_FAILURE(err)) {
+ pr_err("Unable to setup WMI notify handler\n");
goto err_free_input;
}
- err = bl_get(NULL);
- if (err < 0)
- goto err_free_backlight;
+ pr_debug("Event handler installed\n");
+ event_wmi = &event_wmis[i];
+ break;
+ }
- backlight->props.brightness = err;
+ if (wmi_has_guid(MSIWMI_BIOS_GUID) && !acpi_video_backlight_support()) {
+ err = msi_wmi_backlight_setup();
+ if (err) {
+ pr_err("Unable to setup backlight device\n");
+ goto err_uninstall_handler;
+ }
+ pr_debug("Backlight device created\n");
+ }
+
+ if (!event_wmi && !backlight) {
+ pr_err("This machine doesn't have neither MSI-hotkeys nor backlight through WMI\n");
+ return -ENODEV;
}
- pr_debug("Event handler installed\n");
return 0;
-err_free_backlight:
- backlight_device_unregister(backlight);
+err_uninstall_handler:
+ if (event_wmi)
+ wmi_remove_notify_handler(event_wmi->guid);
err_free_input:
- sparse_keymap_free(msi_wmi_input_dev);
- input_unregister_device(msi_wmi_input_dev);
-err_uninstall_notifier:
- wmi_remove_notify_handler(MSIWMI_EVENT_GUID);
+ if (event_wmi) {
+ sparse_keymap_free(msi_wmi_input_dev);
+ input_unregister_device(msi_wmi_input_dev);
+ }
return err;
}
static void __exit msi_wmi_exit(void)
{
- if (wmi_has_guid(MSIWMI_EVENT_GUID)) {
- wmi_remove_notify_handler(MSIWMI_EVENT_GUID);
+ if (event_wmi) {
+ wmi_remove_notify_handler(event_wmi->guid);
sparse_keymap_free(msi_wmi_input_dev);
input_unregister_device(msi_wmi_input_dev);
- backlight_device_unregister(backlight);
}
+ if (backlight)
+ backlight_device_unregister(backlight);
}
module_init(msi_wmi_init);
diff --git a/drivers/platform/x86/mxm-wmi.c b/drivers/platform/x86/mxm-wmi.c
index 0aea63b3729..f4bad83053a 100644
--- a/drivers/platform/x86/mxm-wmi.c
+++ b/drivers/platform/x86/mxm-wmi.c
@@ -20,8 +20,8 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
+#include <linux/mxm-wmi.h>
+#include <linux/acpi.h>
MODULE_AUTHOR("Dave Airlie");
MODULE_DESCRIPTION("MXM WMI Driver");
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index ffff8b4b494..3f870972247 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -125,12 +125,10 @@
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
+#include <linux/acpi.h>
#include <linux/input.h>
#include <linux/input/sparse-keymap.h>
-
#ifndef ACPI_HOTKEY_COMPONENT
#define ACPI_HOTKEY_COMPONENT 0x10000000
#endif
@@ -176,8 +174,7 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0,
/* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */
static int acpi_pcc_hotkey_add(struct acpi_device *device);
-static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type);
-static int acpi_pcc_hotkey_resume(struct acpi_device *device);
+static int acpi_pcc_hotkey_remove(struct acpi_device *device);
static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event);
static const struct acpi_device_id pcc_device_ids[] = {
@@ -189,6 +186,11 @@ static const struct acpi_device_id pcc_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, pcc_device_ids);
+#ifdef CONFIG_PM_SLEEP
+static int acpi_pcc_hotkey_resume(struct device *dev);
+#endif
+static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume);
+
static struct acpi_driver acpi_pcc_driver = {
.name = ACPI_PCC_DRIVER_NAME,
.class = ACPI_PCC_CLASS,
@@ -196,9 +198,9 @@ static struct acpi_driver acpi_pcc_driver = {
.ops = {
.add = acpi_pcc_hotkey_add,
.remove = acpi_pcc_hotkey_remove,
- .resume = acpi_pcc_hotkey_resume,
.notify = acpi_pcc_hotkey_notify,
},
+ .drv.pm = &acpi_pcc_hotkey_pm,
};
static const struct key_entry panasonic_keymap[] = {
@@ -447,6 +449,7 @@ static struct attribute_group pcc_attr_group = {
/* hotkey input device driver */
+static int sleep_keydown_seen;
static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
{
struct input_dev *hotk_input_dev = pcc->input_dev;
@@ -461,7 +464,14 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)
return;
}
- acpi_bus_generate_proc_event(pcc->device, HKEY_NOTIFY, result);
+ /* hack: some firmware sends no key down for sleep / hibernate */
+ if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) {
+ if (result & 0x80)
+ sleep_keydown_seen = 1;
+ if (!sleep_keydown_seen)
+ sparse_keymap_report_event(hotk_input_dev,
+ result & 0xf, 0x80, false);
+ }
if (!sparse_keymap_report_event(hotk_input_dev,
result & 0xf, result & 0x80, false))
@@ -489,11 +499,8 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc)
int error;
input_dev = input_allocate_device();
- if (!input_dev) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
- "Couldn't allocate input device for hotkey"));
+ if (!input_dev)
return -ENOMEM;
- }
input_dev->name = ACPI_PCC_DRIVER_NAME;
input_dev->phys = ACPI_PCC_INPUT_PHYS;
@@ -538,11 +545,16 @@ static void acpi_pcc_destroy_input(struct pcc_acpi *pcc)
/* kernel module interface */
-static int acpi_pcc_hotkey_resume(struct acpi_device *device)
+#ifdef CONFIG_PM_SLEEP
+static int acpi_pcc_hotkey_resume(struct device *dev)
{
- struct pcc_acpi *pcc = acpi_driver_data(device);
+ struct pcc_acpi *pcc;
+
+ if (!dev)
+ return -EINVAL;
- if (device == NULL || pcc == NULL)
+ pcc = acpi_driver_data(to_acpi_device(dev));
+ if (!pcc)
return -EINVAL;
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Sticky mode restore: %d\n",
@@ -550,6 +562,7 @@ static int acpi_pcc_hotkey_resume(struct acpi_device *device)
return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
}
+#endif
static int acpi_pcc_hotkey_add(struct acpi_device *device)
{
@@ -636,24 +649,7 @@ out_hotkey:
return result;
}
-static int __init acpi_pcc_init(void)
-{
- int result = 0;
-
- if (acpi_disabled)
- return -ENODEV;
-
- result = acpi_bus_register_driver(&acpi_pcc_driver);
- if (result < 0) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
- "Error registering hotkey driver\n"));
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
+static int acpi_pcc_hotkey_remove(struct acpi_device *device)
{
struct pcc_acpi *pcc = acpi_driver_data(device);
@@ -672,10 +668,4 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type)
return 0;
}
-static void __exit acpi_pcc_exit(void)
-{
- acpi_bus_unregister_driver(&acpi_pcc_driver);
-}
-
-module_init(acpi_pcc_init);
-module_exit(acpi_pcc_exit);
+module_acpi_driver(acpi_pcc_driver);
diff --git a/drivers/platform/x86/pvpanic.c b/drivers/platform/x86/pvpanic.c
new file mode 100644
index 00000000000..073a90a63db
--- /dev/null
+++ b/drivers/platform/x86/pvpanic.c
@@ -0,0 +1,124 @@
+/*
+ * pvpanic.c - pvpanic Device Support
+ *
+ * Copyright (C) 2013 Fujitsu.
+ *
+ * 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
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/acpi.h>
+
+MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
+MODULE_DESCRIPTION("pvpanic device driver");
+MODULE_LICENSE("GPL");
+
+static int pvpanic_add(struct acpi_device *device);
+static int pvpanic_remove(struct acpi_device *device);
+
+static const struct acpi_device_id pvpanic_device_ids[] = {
+ { "QEMU0001", 0 },
+ { "", 0 },
+};
+MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
+
+#define PVPANIC_PANICKED (1 << 0)
+
+static u16 port;
+
+static struct acpi_driver pvpanic_driver = {
+ .name = "pvpanic",
+ .class = "QEMU",
+ .ids = pvpanic_device_ids,
+ .ops = {
+ .add = pvpanic_add,
+ .remove = pvpanic_remove,
+ },
+ .owner = THIS_MODULE,
+};
+
+static void
+pvpanic_send_event(unsigned int event)
+{
+ outb(event, port);
+}
+
+static int
+pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
+ void *unused)
+{
+ pvpanic_send_event(PVPANIC_PANICKED);
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block pvpanic_panic_nb = {
+ .notifier_call = pvpanic_panic_notify,
+ .priority = 1, /* let this called before broken drm_fb_helper */
+};
+
+
+static acpi_status
+pvpanic_walk_resources(struct acpi_resource *res, void *context)
+{
+ switch (res->type) {
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ return AE_OK;
+
+ case ACPI_RESOURCE_TYPE_IO:
+ port = res->data.io.minimum;
+ return AE_OK;
+
+ default:
+ return AE_ERROR;
+ }
+}
+
+static int pvpanic_add(struct acpi_device *device)
+{
+ acpi_status status;
+ u64 ret;
+
+ status = acpi_evaluate_integer(device->handle, "_STA", NULL,
+ &ret);
+
+ if (ACPI_FAILURE(status) || (ret & 0x0B) != 0x0B)
+ return -ENODEV;
+
+ acpi_walk_resources(device->handle, METHOD_NAME__CRS,
+ pvpanic_walk_resources, NULL);
+
+ if (!port)
+ return -ENODEV;
+
+ atomic_notifier_chain_register(&panic_notifier_list,
+ &pvpanic_panic_nb);
+
+ return 0;
+}
+
+static int pvpanic_remove(struct acpi_device *device)
+{
+
+ atomic_notifier_chain_unregister(&panic_notifier_list,
+ &pvpanic_panic_nb);
+ return 0;
+}
+
+module_acpi_driver(pvpanic_driver);
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c
index e2a34b42ddc..5a596651227 100644
--- a/drivers/platform/x86/samsung-laptop.c
+++ b/drivers/platform/x86/samsung-laptop.c
@@ -26,9 +26,9 @@
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/ctype.h>
-#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+#include <linux/efi.h>
+#include <linux/suspend.h>
#include <acpi/video.h>
-#endif
/*
* This driver is needed because a number of Samsung laptops do not hook
@@ -341,6 +341,8 @@ struct samsung_laptop {
struct samsung_laptop_debug debug;
struct samsung_quirks *quirks;
+ struct notifier_block pm_nb;
+
bool handle_backlight;
bool has_stepping_quirk;
@@ -349,6 +351,8 @@ struct samsung_laptop {
struct samsung_quirks {
bool broken_acpi_video;
+ bool four_kbd_backlight_levels;
+ bool enable_kbd_backlight;
};
static struct samsung_quirks samsung_unknown = {};
@@ -357,6 +361,11 @@ static struct samsung_quirks samsung_broken_acpi_video = {
.broken_acpi_video = true,
};
+static struct samsung_quirks samsung_np740u3e = {
+ .four_kbd_backlight_levels = true,
+ .enable_kbd_backlight = true,
+};
+
static bool force;
module_param(force, bool, 0);
MODULE_PARM_DESC(force,
@@ -1052,6 +1061,8 @@ static int __init samsung_leds_init(struct samsung_laptop *samsung)
samsung->kbd_led.brightness_set = kbd_led_set;
samsung->kbd_led.brightness_get = kbd_led_get;
samsung->kbd_led.max_brightness = 8;
+ if (samsung->quirks->four_kbd_backlight_levels)
+ samsung->kbd_led.max_brightness = 4;
ret = led_classdev_register(&samsung->platform_device->dev,
&samsung->kbd_led);
@@ -1415,6 +1426,19 @@ static void samsung_platform_exit(struct samsung_laptop *samsung)
}
}
+static int samsung_pm_notification(struct notifier_block *nb,
+ unsigned long val, void *ptr)
+{
+ struct samsung_laptop *samsung;
+
+ samsung = container_of(nb, struct samsung_laptop, pm_nb);
+ if (val == PM_POST_HIBERNATION &&
+ samsung->quirks->enable_kbd_backlight)
+ kbd_backlight_enable(samsung);
+
+ return 0;
+}
+
static int __init samsung_platform_init(struct samsung_laptop *samsung)
{
struct platform_device *pdev;
@@ -1465,6 +1489,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
},
},
+ /* DMI ids for laptops with bad Chassis Type */
+ {
+ .ident = "R40/R41",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "R40/R41"),
+ DMI_MATCH(DMI_BOARD_NAME, "R40/R41"),
+ },
+ },
/* Specific DMI ids for laptop with quirks */
{
.callback = samsung_dmi_matched,
@@ -1506,6 +1539,35 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
},
.driver_data = &samsung_broken_acpi_video,
},
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "X360",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
+ DMI_MATCH(DMI_BOARD_NAME, "X360"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "N250P",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N250P"),
+ DMI_MATCH(DMI_BOARD_NAME, "N250P"),
+ },
+ .driver_data = &samsung_broken_acpi_video,
+ },
+ {
+ .callback = samsung_dmi_matched,
+ .ident = "730U3E/740U3E",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"),
+ },
+ .driver_data = &samsung_np740u3e,
+ },
{ },
};
MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
@@ -1517,6 +1579,9 @@ static int __init samsung_init(void)
struct samsung_laptop *samsung;
int ret;
+ if (efi_enabled(EFI_BOOT))
+ return -ENODEV;
+
quirks = &samsung_unknown;
if (!force && !dmi_check_system(samsung_dmi_table))
return -ENODEV;
@@ -1530,15 +1595,16 @@ static int __init samsung_init(void)
samsung->quirks = quirks;
-#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
+#ifdef CONFIG_ACPI
+ if (samsung->quirks->broken_acpi_video)
+ acpi_video_dmi_promote_vendor();
+
/* Don't handle backlight here if the acpi video already handle it */
if (acpi_video_backlight_support()) {
- if (samsung->quirks->broken_acpi_video) {
- pr_info("Disabling ACPI video driver\n");
- acpi_video_unregister();
- } else {
- samsung->handle_backlight = false;
- }
+ samsung->handle_backlight = false;
+ } else if (samsung->quirks->broken_acpi_video) {
+ pr_info("Disabling ACPI video driver\n");
+ acpi_video_unregister();
}
#endif
@@ -1552,8 +1618,7 @@ static int __init samsung_init(void)
#ifdef CONFIG_ACPI
/* Only log that if we are really on a sabi platform */
- if (acpi_video_backlight_support() &&
- !samsung->quirks->broken_acpi_video)
+ if (acpi_video_backlight_support())
pr_info("Backlight controlled by ACPI video driver\n");
#endif
@@ -1577,6 +1642,9 @@ static int __init samsung_init(void)
if (ret)
goto error_debugfs;
+ samsung->pm_nb.notifier_call = samsung_pm_notification;
+ register_pm_notifier(&samsung->pm_nb);
+
samsung_platform_device = samsung->platform_device;
return ret;
@@ -1602,6 +1670,7 @@ static void __exit samsung_exit(void)
struct samsung_laptop *samsung;
samsung = platform_get_drvdata(samsung_platform_device);
+ unregister_pm_notifier(&samsung->pm_nb);
samsung_debugfs_exit(samsung);
samsung_leds_exit(samsung);
diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c
index 1e54ae74274..5413f62d2e6 100644
--- a/drivers/platform/x86/samsung-q10.c
+++ b/drivers/platform/x86/samsung-q10.c
@@ -14,16 +14,12 @@
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/backlight.h>
-#include <linux/i8042.h>
#include <linux/dmi.h>
+#include <linux/acpi.h>
-#define SAMSUNGQ10_BL_MAX_INTENSITY 255
-#define SAMSUNGQ10_BL_DEFAULT_INTENSITY 185
+#define SAMSUNGQ10_BL_MAX_INTENSITY 7
-#define SAMSUNGQ10_BL_8042_CMD 0xbe
-#define SAMSUNGQ10_BL_8042_DATA { 0x89, 0x91 }
-
-static int samsungq10_bl_brightness;
+static acpi_handle ec_handle;
static bool force;
module_param(force, bool, 0);
@@ -33,21 +29,26 @@ MODULE_PARM_DESC(force,
static int samsungq10_bl_set_intensity(struct backlight_device *bd)
{
- int brightness = bd->props.brightness;
- unsigned char c[3] = SAMSUNGQ10_BL_8042_DATA;
+ acpi_status status;
+ int i;
- c[2] = (unsigned char)brightness;
- i8042_lock_chip();
- i8042_command(c, (0x30 << 8) | SAMSUNGQ10_BL_8042_CMD);
- i8042_unlock_chip();
- samsungq10_bl_brightness = brightness;
+ for (i = 0; i < SAMSUNGQ10_BL_MAX_INTENSITY; i++) {
+ status = acpi_evaluate_object(ec_handle, "_Q63", NULL, NULL);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ }
+ for (i = 0; i < bd->props.brightness; i++) {
+ status = acpi_evaluate_object(ec_handle, "_Q64", NULL, NULL);
+ if (ACPI_FAILURE(status))
+ return -EIO;
+ }
return 0;
}
static int samsungq10_bl_get_intensity(struct backlight_device *bd)
{
- return samsungq10_bl_brightness;
+ return bd->props.brightness;
}
static const struct backlight_ops samsungq10_bl_ops = {
@@ -55,29 +56,7 @@ static const struct backlight_ops samsungq10_bl_ops = {
.update_status = samsungq10_bl_set_intensity,
};
-#ifdef CONFIG_PM_SLEEP
-static int samsungq10_suspend(struct device *dev)
-{
- return 0;
-}
-
-static int samsungq10_resume(struct device *dev)
-{
-
- struct backlight_device *bd = dev_get_drvdata(dev);
-
- samsungq10_bl_set_intensity(bd);
- return 0;
-}
-#else
-#define samsungq10_suspend NULL
-#define samsungq10_resume NULL
-#endif
-
-static SIMPLE_DEV_PM_OPS(samsungq10_pm_ops,
- samsungq10_suspend, samsungq10_resume);
-
-static int __devinit samsungq10_probe(struct platform_device *pdev)
+static int samsungq10_probe(struct platform_device *pdev)
{
struct backlight_properties props;
@@ -93,20 +72,14 @@ static int __devinit samsungq10_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, bd);
- bd->props.brightness = SAMSUNGQ10_BL_DEFAULT_INTENSITY;
- samsungq10_bl_set_intensity(bd);
-
return 0;
}
-static int __devexit samsungq10_remove(struct platform_device *pdev)
+static int samsungq10_remove(struct platform_device *pdev)
{
struct backlight_device *bd = platform_get_drvdata(pdev);
- bd->props.brightness = SAMSUNGQ10_BL_DEFAULT_INTENSITY;
- samsungq10_bl_set_intensity(bd);
-
backlight_device_unregister(bd);
return 0;
@@ -116,10 +89,9 @@ static struct platform_driver samsungq10_driver = {
.driver = {
.name = KBUILD_MODNAME,
.owner = THIS_MODULE,
- .pm = &samsungq10_pm_ops,
},
.probe = samsungq10_probe,
- .remove = __devexit_p(samsungq10_remove),
+ .remove = samsungq10_remove,
};
static struct platform_device *samsungq10_device;
@@ -172,14 +144,16 @@ static int __init samsungq10_init(void)
if (!force && !dmi_check_system(samsungq10_dmi_table))
return -ENODEV;
+ ec_handle = ec_get_handle();
+
+ if (!ec_handle)
+ return -ENODEV;
+
samsungq10_device = platform_create_bundle(&samsungq10_driver,
samsungq10_probe,
NULL, 0, NULL, 0);
- if (IS_ERR(samsungq10_device))
- return PTR_ERR(samsungq10_device);
-
- return 0;
+ return PTR_ERR_OR_ZERO(samsungq10_device);
}
static void __exit samsungq10_exit(void)
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 210d4ae547c..9c5a07417b2 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -61,9 +61,6 @@
#include <linux/workqueue.h>
#include <linux/acpi.h>
#include <linux/slab.h>
-#include <acpi/acpi_drivers.h>
-#include <acpi/acpi_bus.h>
-#include <asm/uaccess.h>
#include <linux/sonypi.h>
#include <linux/sony-laptop.h>
#include <linux/rfkill.h>
@@ -71,6 +68,7 @@
#include <linux/poll.h>
#include <linux/miscdevice.h>
#endif
+#include <asm/uaccess.h>
#define dprintk(fmt, ...) \
do { \
@@ -78,8 +76,6 @@ do { \
pr_warn(fmt, ##__VA_ARGS__); \
} while (0)
-#define SONY_LAPTOP_DRIVER_VERSION "0.6"
-
#define SONY_NC_CLASS "sony-nc"
#define SONY_NC_HID "SNY5001"
#define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver"
@@ -91,7 +87,6 @@ do { \
MODULE_AUTHOR("Stelian Pop, Mattia Dongili");
MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");
MODULE_LICENSE("GPL");
-MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);
static int debug;
module_param(debug, int, 0);
@@ -127,23 +122,26 @@ MODULE_PARM_DESC(minor,
"default is -1 (automatic)");
#endif
-static int kbd_backlight = 1;
+static int kbd_backlight = -1;
module_param(kbd_backlight, int, 0444);
MODULE_PARM_DESC(kbd_backlight,
"set this to 0 to disable keyboard backlight, "
- "1 to enable it (default: 0)");
+ "1 to enable it with automatic control and 2 to have it always "
+ "on (default: no change from current value)");
-static int kbd_backlight_timeout; /* = 0 */
+static int kbd_backlight_timeout = -1;
module_param(kbd_backlight_timeout, int, 0444);
MODULE_PARM_DESC(kbd_backlight_timeout,
- "set this to 0 to set the default 10 seconds timeout, "
- "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout "
- "(default: 0)");
+ "meaningful values vary from 0 to 3 and their meaning depends "
+ "on the model (default: no change from current value)");
-static void sony_nc_kbd_backlight_resume(void);
+#ifdef CONFIG_PM_SLEEP
+static void sony_nc_thermal_resume(void);
+#endif
static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
unsigned int handle);
-static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd);
+static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd,
+ unsigned int handle);
static int sony_nc_battery_care_setup(struct platform_device *pd,
unsigned int handle);
@@ -151,14 +149,34 @@ static void sony_nc_battery_care_cleanup(struct platform_device *pd);
static int sony_nc_thermal_setup(struct platform_device *pd);
static void sony_nc_thermal_cleanup(struct platform_device *pd);
-static void sony_nc_thermal_resume(void);
-static int sony_nc_lid_resume_setup(struct platform_device *pd);
+static int sony_nc_lid_resume_setup(struct platform_device *pd,
+ unsigned int handle);
static void sony_nc_lid_resume_cleanup(struct platform_device *pd);
+static int sony_nc_gfx_switch_setup(struct platform_device *pd,
+ unsigned int handle);
+static void sony_nc_gfx_switch_cleanup(struct platform_device *pd);
+static int __sony_nc_gfx_switch_status_get(void);
+
static int sony_nc_highspeed_charging_setup(struct platform_device *pd);
static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd);
+static int sony_nc_lowbatt_setup(struct platform_device *pd);
+static void sony_nc_lowbatt_cleanup(struct platform_device *pd);
+
+static int sony_nc_fanspeed_setup(struct platform_device *pd);
+static void sony_nc_fanspeed_cleanup(struct platform_device *pd);
+
+static int sony_nc_usb_charge_setup(struct platform_device *pd);
+static void sony_nc_usb_charge_cleanup(struct platform_device *pd);
+
+static int sony_nc_panelid_setup(struct platform_device *pd);
+static void sony_nc_panelid_cleanup(struct platform_device *pd);
+
+static int sony_nc_smart_conn_setup(struct platform_device *pd);
+static void sony_nc_smart_conn_cleanup(struct platform_device *pd);
+
static int sony_nc_touchpad_setup(struct platform_device *pd,
unsigned int handle);
static void sony_nc_touchpad_cleanup(struct platform_device *pd);
@@ -298,8 +316,8 @@ static int sony_laptop_input_keycode_map[] = {
KEY_FN_F10, /* 14 SONYPI_EVENT_FNKEY_F10 */
KEY_FN_F11, /* 15 SONYPI_EVENT_FNKEY_F11 */
KEY_FN_F12, /* 16 SONYPI_EVENT_FNKEY_F12 */
- KEY_FN_F1, /* 17 SONYPI_EVENT_FNKEY_1 */
- KEY_FN_F2, /* 18 SONYPI_EVENT_FNKEY_2 */
+ KEY_FN_1, /* 17 SONYPI_EVENT_FNKEY_1 */
+ KEY_FN_2, /* 18 SONYPI_EVENT_FNKEY_2 */
KEY_FN_D, /* 19 SONYPI_EVENT_FNKEY_D */
KEY_FN_E, /* 20 SONYPI_EVENT_FNKEY_E */
KEY_FN_F, /* 21 SONYPI_EVENT_FNKEY_F */
@@ -784,28 +802,29 @@ static int sony_nc_int_call(acpi_handle handle, char *name, int *value,
static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,
void *buffer, size_t buflen)
{
- size_t len = len;
+ int ret = 0;
+ size_t len;
union acpi_object *object = __call_snc_method(handle, name, value);
if (!object)
return -EINVAL;
- if (object->type == ACPI_TYPE_BUFFER)
+ if (object->type == ACPI_TYPE_BUFFER) {
len = MIN(buflen, object->buffer.length);
+ memcpy(buffer, object->buffer.pointer, len);
- else if (object->type == ACPI_TYPE_INTEGER)
+ } else if (object->type == ACPI_TYPE_INTEGER) {
len = MIN(buflen, sizeof(object->integer.value));
+ memcpy(buffer, &object->integer.value, len);
- else {
+ } else {
pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",
ACPI_TYPE_BUFFER, object->type);
- kfree(object);
- return -EINVAL;
+ ret = -EINVAL;
}
- memcpy(buffer, object->buffer.pointer, len);
kfree(object);
- return 0;
+ return ret;
}
struct sony_nc_handles {
@@ -973,7 +992,7 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
- unsigned long value = 0;
+ int value;
int ret = 0;
struct sony_nc_value *item =
container_of(attr, struct sony_nc_value, devattr);
@@ -984,7 +1003,7 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
if (count > 31)
return -EINVAL;
- if (kstrtoul(buffer, 10, &value))
+ if (kstrtoint(buffer, 10, &value))
return -EINVAL;
if (item->validate)
@@ -994,7 +1013,7 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
return value;
ret = sony_nc_int_call(sony_nc_acpi_handle, *item->acpiset,
- (int *)&value, NULL);
+ &value, NULL);
if (ret < 0)
return -EIO;
@@ -1010,6 +1029,7 @@ static ssize_t sony_nc_sysfs_store(struct device *dev,
struct sony_backlight_props {
struct backlight_device *dev;
int handle;
+ int cmd_base;
u8 offset;
u8 maxlvl;
};
@@ -1037,7 +1057,7 @@ static int sony_nc_get_brightness_ng(struct backlight_device *bd)
struct sony_backlight_props *sdev =
(struct sony_backlight_props *)bl_get_data(bd);
- sony_call_snc_handle(sdev->handle, 0x0200, &result);
+ sony_call_snc_handle(sdev->handle, sdev->cmd_base + 0x100, &result);
return (result & 0xff) - sdev->offset;
}
@@ -1049,7 +1069,8 @@ static int sony_nc_update_status_ng(struct backlight_device *bd)
(struct sony_backlight_props *)bl_get_data(bd);
value = bd->props.brightness + sdev->offset;
- if (sony_call_snc_handle(sdev->handle, 0x0100 | (value << 16), &result))
+ if (sony_call_snc_handle(sdev->handle, sdev->cmd_base | (value << 0x10),
+ &result))
return -EIO;
return value;
@@ -1115,6 +1136,8 @@ static struct sony_nc_event sony_100_events[] = {
{ 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0xa6, SONYPI_EVENT_HELP_PRESSED },
{ 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED },
+ { 0xa8, SONYPI_EVENT_FNKEY_1 },
+ { 0x28, SONYPI_EVENT_ANYBUTTON_RELEASED },
{ 0, 0 },
};
@@ -1172,6 +1195,11 @@ static int sony_nc_hotkeys_decode(u32 event, unsigned int handle)
/*
* ACPI callbacks
*/
+enum event_types {
+ HOTKEY = 1,
+ KILLSWITCH,
+ GFX_SWITCH
+};
static void sony_nc_notify(struct acpi_device *device, u32 event)
{
u32 real_ev = event;
@@ -1196,7 +1224,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
/* hotkey event */
case 0x0100:
case 0x0127:
- ev_type = 1;
+ ev_type = HOTKEY;
real_ev = sony_nc_hotkeys_decode(event, handle);
if (real_ev > 0)
@@ -1216,7 +1244,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
* update the rfkill device status when the
* switch is moved.
*/
- ev_type = 2;
+ ev_type = KILLSWITCH;
sony_call_snc_handle(handle, 0x0100, &result);
real_ev = result & 0x03;
@@ -1226,6 +1254,25 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
break;
+ case 0x0128:
+ case 0x0146:
+ /* Hybrid GFX switching */
+ sony_call_snc_handle(handle, 0x0000, &result);
+ dprintk("GFX switch event received (reason: %s)\n",
+ (result == 0x1) ? "switch change" :
+ (result == 0x2) ? "output switch" :
+ (result == 0x3) ? "output switch" :
+ "");
+
+ ev_type = GFX_SWITCH;
+ real_ev = __sony_nc_gfx_switch_status_get();
+ break;
+
+ case 0x015B:
+ /* Hybrid GFX switching SVS151290S */
+ ev_type = GFX_SWITCH;
+ real_ev = __sony_nc_gfx_switch_status_get();
+ break;
default:
dprintk("Unknown event 0x%x for handle 0x%x\n",
event, handle);
@@ -1238,12 +1285,9 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
} else {
/* old style event */
- ev_type = 1;
+ ev_type = HOTKEY;
sony_laptop_report_input_event(real_ev);
}
-
- acpi_bus_generate_proc_event(sony_nc_acpi_device, ev_type, real_ev);
-
acpi_bus_generate_netlink_event(sony_nc_acpi_device->pnp.device_class,
dev_name(&sony_nc_acpi_device->dev), ev_type, real_ev);
}
@@ -1311,7 +1355,8 @@ static void sony_nc_function_setup(struct acpi_device *device,
result);
break;
case 0x0119:
- result = sony_nc_lid_resume_setup(pf_device);
+ case 0x015D:
+ result = sony_nc_lid_resume_setup(pf_device, handle);
if (result)
pr_err("couldn't set up lid resume function (%d)\n",
result);
@@ -1322,6 +1367,14 @@ static void sony_nc_function_setup(struct acpi_device *device,
pr_err("couldn't set up thermal profile function (%d)\n",
result);
break;
+ case 0x0128:
+ case 0x0146:
+ case 0x015B:
+ result = sony_nc_gfx_switch_setup(pf_device, handle);
+ if (result)
+ pr_err("couldn't set up GFX Switch status (%d)\n",
+ result);
+ break;
case 0x0131:
result = sony_nc_highspeed_charging_setup(pf_device);
if (result)
@@ -1337,11 +1390,44 @@ static void sony_nc_function_setup(struct acpi_device *device,
break;
case 0x0137:
case 0x0143:
+ case 0x014b:
+ case 0x014c:
+ case 0x0163:
result = sony_nc_kbd_backlight_setup(pf_device, handle);
if (result)
pr_err("couldn't set up keyboard backlight function (%d)\n",
result);
break;
+ case 0x0121:
+ result = sony_nc_lowbatt_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up low battery function (%d)\n",
+ result);
+ break;
+ case 0x0149:
+ result = sony_nc_fanspeed_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up fan speed function (%d)\n",
+ result);
+ break;
+ case 0x0155:
+ result = sony_nc_usb_charge_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up USB charge support (%d)\n",
+ result);
+ break;
+ case 0x011D:
+ result = sony_nc_panelid_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up panel ID function (%d)\n",
+ result);
+ break;
+ case 0x0168:
+ result = sony_nc_smart_conn_setup(pf_device);
+ if (result)
+ pr_err("couldn't set up smart connect support (%d)\n",
+ result);
+ break;
default:
continue;
}
@@ -1381,11 +1467,17 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
sony_nc_battery_care_cleanup(pd);
break;
case 0x0119:
+ case 0x015D:
sony_nc_lid_resume_cleanup(pd);
break;
case 0x0122:
sony_nc_thermal_cleanup(pd);
break;
+ case 0x0128:
+ case 0x0146:
+ case 0x015B:
+ sony_nc_gfx_switch_cleanup(pd);
+ break;
case 0x0131:
sony_nc_highspeed_charging_cleanup(pd);
break;
@@ -1395,7 +1487,25 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
break;
case 0x0137:
case 0x0143:
- sony_nc_kbd_backlight_cleanup(pd);
+ case 0x014b:
+ case 0x014c:
+ case 0x0163:
+ sony_nc_kbd_backlight_cleanup(pd, handle);
+ break;
+ case 0x0121:
+ sony_nc_lowbatt_cleanup(pd);
+ break;
+ case 0x0149:
+ sony_nc_fanspeed_cleanup(pd);
+ break;
+ case 0x0155:
+ sony_nc_usb_charge_cleanup(pd);
+ break;
+ case 0x011D:
+ sony_nc_panelid_cleanup(pd);
+ break;
+ case 0x0168:
+ sony_nc_smart_conn_cleanup(pd);
break;
default:
continue;
@@ -1406,6 +1516,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
sony_nc_handles_cleanup(pd);
}
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_function_resume(void)
{
unsigned int i, result, bitmask, arg;
@@ -1436,10 +1547,6 @@ static void sony_nc_function_resume(void)
case 0x0135:
sony_nc_rfkill_update();
break;
- case 0x0137:
- case 0x0143:
- sony_nc_kbd_backlight_resume();
- break;
default:
continue;
}
@@ -1452,10 +1559,9 @@ static void sony_nc_function_resume(void)
&result);
}
-static int sony_nc_resume(struct acpi_device *device)
+static int sony_nc_resume(struct device *dev)
{
struct sony_nc_value *item;
- acpi_handle handle;
for (item = sony_nc_values; item->name; item++) {
int ret;
@@ -1470,19 +1576,20 @@ static int sony_nc_resume(struct acpi_device *device)
}
}
- if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON",
- &handle))) {
+ if (acpi_has_method(sony_nc_acpi_handle, "ECON")) {
int arg = 1;
if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))
dprintk("ECON Method failed\n");
}
- if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
- &handle)))
+ if (acpi_has_method(sony_nc_acpi_handle, "SN00"))
sony_nc_function_resume();
return 0;
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sony_nc_pm, NULL, sony_nc_resume);
static void sony_nc_rfkill_cleanup(void)
{
@@ -1502,7 +1609,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked)
int argument = sony_rfkill_address[(long) data] + 0x100;
if (!blocked)
- argument |= 0x030000;
+ argument |= 0x070000;
return sony_call_snc_handle(sony_rfkill_handle, argument, &result);
}
@@ -1675,7 +1782,7 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
{
int result;
- if (value > 1)
+ if (value > 2)
return -EINVAL;
if (sony_call_snc_handle(kbdbl_ctl->handle,
@@ -1683,8 +1790,10 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
return -EIO;
/* Try to turn the light on/off immediately */
- sony_call_snc_handle(kbdbl_ctl->handle,
- (value << 0x10) | (kbdbl_ctl->base + 0x100), &result);
+ if (value != 1)
+ sony_call_snc_handle(kbdbl_ctl->handle,
+ (value << 0x0f) | (kbdbl_ctl->base + 0x100),
+ &result);
kbdbl_ctl->mode = value;
@@ -1769,6 +1878,12 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
int result;
int ret = 0;
+ if (kbdbl_ctl) {
+ pr_warn("handle 0x%.4x: keyboard backlight setup already done for 0x%.4x\n",
+ handle, kbdbl_ctl->handle);
+ return -EBUSY;
+ }
+
/* verify the kbd backlight presence, these handles are not used for
* keyboard backlight only
*/
@@ -1787,6 +1902,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
if (!kbdbl_ctl)
return -ENOMEM;
+ kbdbl_ctl->mode = kbd_backlight;
+ kbdbl_ctl->timeout = kbd_backlight_timeout;
kbdbl_ctl->handle = handle;
if (handle == 0x0137)
kbdbl_ctl->base = 0x0C00;
@@ -1813,8 +1930,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
if (ret)
goto outmode;
- __sony_nc_kbd_backlight_mode_set(kbd_backlight);
- __sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout);
+ __sony_nc_kbd_backlight_mode_set(kbdbl_ctl->mode);
+ __sony_nc_kbd_backlight_timeout_set(kbdbl_ctl->timeout);
return 0;
@@ -1826,42 +1943,17 @@ outkzalloc:
return ret;
}
-static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
+static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd,
+ unsigned int handle)
{
- if (kbdbl_ctl) {
- int result;
-
+ if (kbdbl_ctl && handle == kbdbl_ctl->handle) {
device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);
device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr);
-
- /* restore the default hw behaviour */
- sony_call_snc_handle(kbdbl_ctl->handle,
- kbdbl_ctl->base | 0x10000, &result);
- sony_call_snc_handle(kbdbl_ctl->handle,
- kbdbl_ctl->base + 0x200, &result);
-
kfree(kbdbl_ctl);
kbdbl_ctl = NULL;
}
}
-static void sony_nc_kbd_backlight_resume(void)
-{
- int ignore = 0;
-
- if (!kbdbl_ctl)
- return;
-
- if (kbdbl_ctl->mode == 0)
- sony_call_snc_handle(kbdbl_ctl->handle, kbdbl_ctl->base,
- &ignore);
-
- if (kbdbl_ctl->timeout != 0)
- sony_call_snc_handle(kbdbl_ctl->handle,
- (kbdbl_ctl->base + 0x200) |
- (kbdbl_ctl->timeout << 0x10), &ignore);
-}
-
struct battery_care_control {
struct device_attribute attrs[2];
unsigned int handle;
@@ -1893,32 +1985,33 @@ static ssize_t sony_nc_battery_care_limit_store(struct device *dev,
* bits 4,5: store the limit into the EC
* bits 6,7: store the limit into the battery
*/
+ cmd = 0;
- /*
- * handle 0x0115 should allow storing on battery too;
- * handle 0x0136 same as 0x0115 + health status;
- * handle 0x013f, same as 0x0136 but no storing on the battery
- *
- * Store only inside the EC for now, regardless the handle number
- */
- if (value == 0)
- /* disable limits */
- cmd = 0x0;
+ if (value > 0) {
+ if (value <= 50)
+ cmd = 0x20;
- else if (value <= 50)
- cmd = 0x21;
+ else if (value <= 80)
+ cmd = 0x10;
- else if (value <= 80)
- cmd = 0x11;
+ else if (value <= 100)
+ cmd = 0x30;
- else if (value <= 100)
- cmd = 0x31;
+ else
+ return -EINVAL;
- else
- return -EINVAL;
+ /*
+ * handle 0x0115 should allow storing on battery too;
+ * handle 0x0136 same as 0x0115 + health status;
+ * handle 0x013f, same as 0x0136 but no storing on the battery
+ */
+ if (bcare_ctl->handle != 0x013f)
+ cmd = cmd | (cmd << 2);
- if (sony_call_snc_handle(bcare_ctl->handle, (cmd << 0x10) | 0x0100,
- &result))
+ cmd = (cmd | 0x1) << 0x10;
+ }
+
+ if (sony_call_snc_handle(bcare_ctl->handle, cmd | 0x0100, &result))
return -EIO;
return count;
@@ -2113,7 +2206,7 @@ static ssize_t sony_nc_thermal_mode_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
ssize_t count = 0;
- unsigned int mode = sony_nc_thermal_mode_get();
+ int mode = sony_nc_thermal_mode_get();
if (mode < 0)
return mode;
@@ -2182,6 +2275,7 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd)
}
}
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_thermal_resume(void)
{
unsigned int status = sony_nc_thermal_mode_get();
@@ -2189,11 +2283,17 @@ static void sony_nc_thermal_resume(void)
if (status != th_handle->mode)
sony_nc_thermal_mode_set(th_handle->mode);
}
+#endif
/* resume on LID open */
+#define LID_RESUME_S5 0
+#define LID_RESUME_S4 1
+#define LID_RESUME_S3 2
+#define LID_RESUME_MAX 3
struct snc_lid_resume_control {
- struct device_attribute attrs[3];
+ struct device_attribute attrs[LID_RESUME_MAX];
unsigned int status;
+ int handle;
};
static struct snc_lid_resume_control *lid_ctl;
@@ -2201,8 +2301,9 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
- unsigned int result, pos;
+ unsigned int result;
unsigned long value;
+ unsigned int pos = LID_RESUME_S5;
if (count > 31)
return -EINVAL;
@@ -2215,21 +2316,21 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
* +--------------+
* 2 1 0
*/
- if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
- pos = 2;
- else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
- pos = 1;
- else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
- pos = 0;
- else
- return -EINVAL;
+ while (pos < LID_RESUME_MAX) {
+ if (&lid_ctl->attrs[pos].attr == &attr->attr)
+ break;
+ pos++;
+ }
+ if (pos == LID_RESUME_MAX)
+ return -EINVAL;
if (value)
value = lid_ctl->status | (1 << pos);
else
value = lid_ctl->status & ~(1 << pos);
- if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result))
+ if (sony_call_snc_handle(lid_ctl->handle, value << 0x10 | 0x0100,
+ &result))
return -EIO;
lid_ctl->status = value;
@@ -2238,29 +2339,27 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
}
static ssize_t sony_nc_lid_resume_show(struct device *dev,
- struct device_attribute *attr, char *buffer)
+ struct device_attribute *attr,
+ char *buffer)
{
- unsigned int pos;
+ unsigned int pos = LID_RESUME_S5;
- if (strcmp(attr->attr.name, "lid_resume_S3") == 0)
- pos = 2;
- else if (strcmp(attr->attr.name, "lid_resume_S4") == 0)
- pos = 1;
- else if (strcmp(attr->attr.name, "lid_resume_S5") == 0)
- pos = 0;
- else
- return -EINVAL;
-
- return snprintf(buffer, PAGE_SIZE, "%d\n",
- (lid_ctl->status >> pos) & 0x01);
+ while (pos < LID_RESUME_MAX) {
+ if (&lid_ctl->attrs[pos].attr == &attr->attr)
+ return snprintf(buffer, PAGE_SIZE, "%d\n",
+ (lid_ctl->status >> pos) & 0x01);
+ pos++;
+ }
+ return -EINVAL;
}
-static int sony_nc_lid_resume_setup(struct platform_device *pd)
+static int sony_nc_lid_resume_setup(struct platform_device *pd,
+ unsigned int handle)
{
unsigned int result;
int i;
- if (sony_call_snc_handle(0x0119, 0x0000, &result))
+ if (sony_call_snc_handle(handle, 0x0000, &result))
return -EIO;
lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL);
@@ -2268,26 +2367,29 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd)
return -ENOMEM;
lid_ctl->status = result & 0x7;
+ lid_ctl->handle = handle;
sysfs_attr_init(&lid_ctl->attrs[0].attr);
- lid_ctl->attrs[0].attr.name = "lid_resume_S3";
- lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR;
- lid_ctl->attrs[0].show = sony_nc_lid_resume_show;
- lid_ctl->attrs[0].store = sony_nc_lid_resume_store;
-
- sysfs_attr_init(&lid_ctl->attrs[1].attr);
- lid_ctl->attrs[1].attr.name = "lid_resume_S4";
- lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR;
- lid_ctl->attrs[1].show = sony_nc_lid_resume_show;
- lid_ctl->attrs[1].store = sony_nc_lid_resume_store;
-
- sysfs_attr_init(&lid_ctl->attrs[2].attr);
- lid_ctl->attrs[2].attr.name = "lid_resume_S5";
- lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR;
- lid_ctl->attrs[2].show = sony_nc_lid_resume_show;
- lid_ctl->attrs[2].store = sony_nc_lid_resume_store;
-
- for (i = 0; i < 3; i++) {
+ lid_ctl->attrs[LID_RESUME_S5].attr.name = "lid_resume_S5";
+ lid_ctl->attrs[LID_RESUME_S5].attr.mode = S_IRUGO | S_IWUSR;
+ lid_ctl->attrs[LID_RESUME_S5].show = sony_nc_lid_resume_show;
+ lid_ctl->attrs[LID_RESUME_S5].store = sony_nc_lid_resume_store;
+
+ if (handle == 0x0119) {
+ sysfs_attr_init(&lid_ctl->attrs[1].attr);
+ lid_ctl->attrs[LID_RESUME_S4].attr.name = "lid_resume_S4";
+ lid_ctl->attrs[LID_RESUME_S4].attr.mode = S_IRUGO | S_IWUSR;
+ lid_ctl->attrs[LID_RESUME_S4].show = sony_nc_lid_resume_show;
+ lid_ctl->attrs[LID_RESUME_S4].store = sony_nc_lid_resume_store;
+
+ sysfs_attr_init(&lid_ctl->attrs[2].attr);
+ lid_ctl->attrs[LID_RESUME_S3].attr.name = "lid_resume_S3";
+ lid_ctl->attrs[LID_RESUME_S3].attr.mode = S_IRUGO | S_IWUSR;
+ lid_ctl->attrs[LID_RESUME_S3].show = sony_nc_lid_resume_show;
+ lid_ctl->attrs[LID_RESUME_S3].store = sony_nc_lid_resume_store;
+ }
+ for (i = 0; i < LID_RESUME_MAX &&
+ lid_ctl->attrs[LID_RESUME_S3].attr.name; i++) {
result = device_create_file(&pd->dev, &lid_ctl->attrs[i]);
if (result)
goto liderror;
@@ -2296,7 +2398,7 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd)
return 0;
liderror:
- for (; i > 0; i--)
+ for (i--; i >= 0; i--)
device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
kfree(lid_ctl);
@@ -2310,14 +2412,120 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd)
int i;
if (lid_ctl) {
- for (i = 0; i < 3; i++)
+ for (i = 0; i < LID_RESUME_MAX; i++) {
+ if (!lid_ctl->attrs[i].attr.name)
+ break;
+
device_remove_file(&pd->dev, &lid_ctl->attrs[i]);
+ }
kfree(lid_ctl);
lid_ctl = NULL;
}
}
+/* GFX Switch position */
+enum gfx_switch {
+ SPEED,
+ STAMINA,
+ AUTO
+};
+struct snc_gfx_switch_control {
+ struct device_attribute attr;
+ unsigned int handle;
+};
+static struct snc_gfx_switch_control *gfxs_ctl;
+
+/* returns 0 for speed, 1 for stamina */
+static int __sony_nc_gfx_switch_status_get(void)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(gfxs_ctl->handle,
+ gfxs_ctl->handle == 0x015B ? 0x0000 : 0x0100,
+ &result))
+ return -EIO;
+
+ switch (gfxs_ctl->handle) {
+ case 0x0146:
+ /* 1: discrete GFX (speed)
+ * 0: integrated GFX (stamina)
+ */
+ return result & 0x1 ? SPEED : STAMINA;
+ break;
+ case 0x015B:
+ /* 0: discrete GFX (speed)
+ * 1: integrated GFX (stamina)
+ */
+ return result & 0x1 ? STAMINA : SPEED;
+ break;
+ case 0x0128:
+ /* it's a more elaborated bitmask, for now:
+ * 2: integrated GFX (stamina)
+ * 0: discrete GFX (speed)
+ */
+ dprintk("GFX Status: 0x%x\n", result);
+ return result & 0x80 ? AUTO :
+ result & 0x02 ? STAMINA : SPEED;
+ break;
+ }
+ return -EINVAL;
+}
+
+static ssize_t sony_nc_gfx_switch_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buffer)
+{
+ int pos = __sony_nc_gfx_switch_status_get();
+
+ if (pos < 0)
+ return pos;
+
+ return snprintf(buffer, PAGE_SIZE, "%s\n",
+ pos == SPEED ? "speed" :
+ pos == STAMINA ? "stamina" :
+ pos == AUTO ? "auto" : "unknown");
+}
+
+static int sony_nc_gfx_switch_setup(struct platform_device *pd,
+ unsigned int handle)
+{
+ unsigned int result;
+
+ gfxs_ctl = kzalloc(sizeof(struct snc_gfx_switch_control), GFP_KERNEL);
+ if (!gfxs_ctl)
+ return -ENOMEM;
+
+ gfxs_ctl->handle = handle;
+
+ sysfs_attr_init(&gfxs_ctl->attr.attr);
+ gfxs_ctl->attr.attr.name = "gfx_switch_status";
+ gfxs_ctl->attr.attr.mode = S_IRUGO;
+ gfxs_ctl->attr.show = sony_nc_gfx_switch_status_show;
+
+ result = device_create_file(&pd->dev, &gfxs_ctl->attr);
+ if (result)
+ goto gfxerror;
+
+ return 0;
+
+gfxerror:
+ kfree(gfxs_ctl);
+ gfxs_ctl = NULL;
+
+ return result;
+}
+
+static void sony_nc_gfx_switch_cleanup(struct platform_device *pd)
+{
+ if (gfxs_ctl) {
+ device_remove_file(&pd->dev, &gfxs_ctl->attr);
+
+ kfree(gfxs_ctl);
+ gfxs_ctl = NULL;
+ }
+}
+
/* High speed charging function */
static struct device_attribute *hsc_handle;
@@ -2392,6 +2600,355 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd)
}
}
+/* low battery function */
+static struct device_attribute *lowbatt_handle;
+
+static ssize_t sony_nc_lowbatt_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0121, value << 8, &result))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_nc_lowbatt_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0121, 0x0200, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 1);
+}
+
+static int sony_nc_lowbatt_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ lowbatt_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!lowbatt_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&lowbatt_handle->attr);
+ lowbatt_handle->attr.name = "lowbatt_hibernate";
+ lowbatt_handle->attr.mode = S_IRUGO | S_IWUSR;
+ lowbatt_handle->show = sony_nc_lowbatt_show;
+ lowbatt_handle->store = sony_nc_lowbatt_store;
+
+ result = device_create_file(&pd->dev, lowbatt_handle);
+ if (result) {
+ kfree(lowbatt_handle);
+ lowbatt_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_lowbatt_cleanup(struct platform_device *pd)
+{
+ if (lowbatt_handle) {
+ device_remove_file(&pd->dev, lowbatt_handle);
+ kfree(lowbatt_handle);
+ lowbatt_handle = NULL;
+ }
+}
+
+/* fan speed function */
+static struct device_attribute *fan_handle, *hsf_handle;
+
+static ssize_t sony_nc_hsfan_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0149, value << 0x10 | 0x0200, &result))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_nc_hsfan_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0149, 0x0100, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
+}
+
+static ssize_t sony_nc_fanspeed_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0149, 0x0300, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff);
+}
+
+static int sony_nc_fanspeed_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ fan_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!fan_handle)
+ return -ENOMEM;
+
+ hsf_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!hsf_handle) {
+ result = -ENOMEM;
+ goto out_hsf_handle_alloc;
+ }
+
+ sysfs_attr_init(&fan_handle->attr);
+ fan_handle->attr.name = "fanspeed";
+ fan_handle->attr.mode = S_IRUGO;
+ fan_handle->show = sony_nc_fanspeed_show;
+ fan_handle->store = NULL;
+
+ sysfs_attr_init(&hsf_handle->attr);
+ hsf_handle->attr.name = "fan_forced";
+ hsf_handle->attr.mode = S_IRUGO | S_IWUSR;
+ hsf_handle->show = sony_nc_hsfan_show;
+ hsf_handle->store = sony_nc_hsfan_store;
+
+ result = device_create_file(&pd->dev, fan_handle);
+ if (result)
+ goto out_fan_handle;
+
+ result = device_create_file(&pd->dev, hsf_handle);
+ if (result)
+ goto out_hsf_handle;
+
+ return 0;
+
+out_hsf_handle:
+ device_remove_file(&pd->dev, fan_handle);
+
+out_fan_handle:
+ kfree(hsf_handle);
+ hsf_handle = NULL;
+
+out_hsf_handle_alloc:
+ kfree(fan_handle);
+ fan_handle = NULL;
+ return result;
+}
+
+static void sony_nc_fanspeed_cleanup(struct platform_device *pd)
+{
+ if (fan_handle) {
+ device_remove_file(&pd->dev, fan_handle);
+ kfree(fan_handle);
+ fan_handle = NULL;
+ }
+ if (hsf_handle) {
+ device_remove_file(&pd->dev, hsf_handle);
+ kfree(hsf_handle);
+ hsf_handle = NULL;
+ }
+}
+
+/* USB charge function */
+static struct device_attribute *uc_handle;
+
+static ssize_t sony_nc_usb_charge_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0155, value << 0x10 | 0x0100, &result))
+ return -EIO;
+
+ return count;
+}
+
+static ssize_t sony_nc_usb_charge_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0155, 0x0000, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01);
+}
+
+static int sony_nc_usb_charge_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x0155, 0x0000, &result) || !(result & 0x01)) {
+ /* some models advertise the handle but have no implementation
+ * for it
+ */
+ pr_info("No USB Charge capability found\n");
+ return 0;
+ }
+
+ uc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!uc_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&uc_handle->attr);
+ uc_handle->attr.name = "usb_charge";
+ uc_handle->attr.mode = S_IRUGO | S_IWUSR;
+ uc_handle->show = sony_nc_usb_charge_show;
+ uc_handle->store = sony_nc_usb_charge_store;
+
+ result = device_create_file(&pd->dev, uc_handle);
+ if (result) {
+ kfree(uc_handle);
+ uc_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_usb_charge_cleanup(struct platform_device *pd)
+{
+ if (uc_handle) {
+ device_remove_file(&pd->dev, uc_handle);
+ kfree(uc_handle);
+ uc_handle = NULL;
+ }
+}
+
+/* Panel ID function */
+static struct device_attribute *panel_handle;
+
+static ssize_t sony_nc_panelid_show(struct device *dev,
+ struct device_attribute *attr, char *buffer)
+{
+ unsigned int result;
+
+ if (sony_call_snc_handle(0x011D, 0x0000, &result))
+ return -EIO;
+
+ return snprintf(buffer, PAGE_SIZE, "%d\n", result);
+}
+
+static int sony_nc_panelid_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ panel_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!panel_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&panel_handle->attr);
+ panel_handle->attr.name = "panel_id";
+ panel_handle->attr.mode = S_IRUGO;
+ panel_handle->show = sony_nc_panelid_show;
+ panel_handle->store = NULL;
+
+ result = device_create_file(&pd->dev, panel_handle);
+ if (result) {
+ kfree(panel_handle);
+ panel_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_panelid_cleanup(struct platform_device *pd)
+{
+ if (panel_handle) {
+ device_remove_file(&pd->dev, panel_handle);
+ kfree(panel_handle);
+ panel_handle = NULL;
+ }
+}
+
+/* smart connect function */
+static struct device_attribute *sc_handle;
+
+static ssize_t sony_nc_smart_conn_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count)
+{
+ unsigned int result;
+ unsigned long value;
+
+ if (count > 31)
+ return -EINVAL;
+
+ if (kstrtoul(buffer, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (sony_call_snc_handle(0x0168, value << 0x10, &result))
+ return -EIO;
+
+ return count;
+}
+
+static int sony_nc_smart_conn_setup(struct platform_device *pd)
+{
+ unsigned int result;
+
+ sc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL);
+ if (!sc_handle)
+ return -ENOMEM;
+
+ sysfs_attr_init(&sc_handle->attr);
+ sc_handle->attr.name = "smart_connect";
+ sc_handle->attr.mode = S_IWUSR;
+ sc_handle->show = NULL;
+ sc_handle->store = sony_nc_smart_conn_store;
+
+ result = device_create_file(&pd->dev, sc_handle);
+ if (result) {
+ kfree(sc_handle);
+ sc_handle = NULL;
+ return result;
+ }
+
+ return 0;
+}
+
+static void sony_nc_smart_conn_cleanup(struct platform_device *pd)
+{
+ if (sc_handle) {
+ device_remove_file(&pd->dev, sc_handle);
+ kfree(sc_handle);
+ sc_handle = NULL;
+ }
+}
+
/* Touchpad enable/disable */
struct touchpad_control {
struct device_attribute attr;
@@ -2472,6 +3029,7 @@ static void sony_nc_backlight_ng_read_limits(int handle,
{
u64 offset;
int i;
+ int lvl_table_len = 0;
u8 min = 0xff, max = 0x00;
unsigned char buffer[32] = { 0 };
@@ -2480,8 +3038,6 @@ static void sony_nc_backlight_ng_read_limits(int handle,
props->maxlvl = 0xff;
offset = sony_find_snc_handle(handle);
- if (offset < 0)
- return;
/* try to read the boundaries from ACPI tables, if we fail the above
* defaults should be reasonable
@@ -2491,11 +3047,23 @@ static void sony_nc_backlight_ng_read_limits(int handle,
if (i < 0)
return;
+ switch (handle) {
+ case 0x012f:
+ case 0x0137:
+ lvl_table_len = 9;
+ break;
+ case 0x143:
+ case 0x14b:
+ case 0x14c:
+ lvl_table_len = 16;
+ break;
+ }
+
/* the buffer lists brightness levels available, brightness levels are
* from position 0 to 8 in the array, other values are used by ALS
* control.
*/
- for (i = 0; i < 9 && i < ARRAY_SIZE(buffer); i++) {
+ for (i = 0; i < lvl_table_len && i < ARRAY_SIZE(buffer); i++) {
dprintk("Brightness level: %d\n", buffer[i]);
@@ -2515,23 +3083,41 @@ static void sony_nc_backlight_ng_read_limits(int handle,
static void sony_nc_backlight_setup(void)
{
- acpi_handle unused;
int max_brightness = 0;
const struct backlight_ops *ops = NULL;
struct backlight_properties props;
- if (sony_find_snc_handle(0x12f) != -1) {
+ if (sony_find_snc_handle(0x12f) >= 0) {
ops = &sony_backlight_ng_ops;
+ sony_bl_props.cmd_base = 0x0100;
sony_nc_backlight_ng_read_limits(0x12f, &sony_bl_props);
max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
- } else if (sony_find_snc_handle(0x137) != -1) {
+ } else if (sony_find_snc_handle(0x137) >= 0) {
ops = &sony_backlight_ng_ops;
+ sony_bl_props.cmd_base = 0x0100;
sony_nc_backlight_ng_read_limits(0x137, &sony_bl_props);
max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
- } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
- &unused))) {
+ } else if (sony_find_snc_handle(0x143) >= 0) {
+ ops = &sony_backlight_ng_ops;
+ sony_bl_props.cmd_base = 0x3000;
+ sony_nc_backlight_ng_read_limits(0x143, &sony_bl_props);
+ max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
+
+ } else if (sony_find_snc_handle(0x14b) >= 0) {
+ ops = &sony_backlight_ng_ops;
+ sony_bl_props.cmd_base = 0x3000;
+ sony_nc_backlight_ng_read_limits(0x14b, &sony_bl_props);
+ max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
+
+ } else if (sony_find_snc_handle(0x14c) >= 0) {
+ ops = &sony_backlight_ng_ops;
+ sony_bl_props.cmd_base = 0x3000;
+ sony_nc_backlight_ng_read_limits(0x14c, &sony_bl_props);
+ max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset;
+
+ } else if (acpi_has_method(sony_nc_acpi_handle, "GBRT")) {
ops = &sony_backlight_ops;
max_brightness = SONY_MAX_BRIGHTNESS - 1;
@@ -2563,11 +3149,8 @@ static int sony_nc_add(struct acpi_device *device)
{
acpi_status status;
int result = 0;
- acpi_handle handle;
struct sony_nc_value *item;
- pr_info("%s v%s\n", SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
-
sony_nc_acpi_device = device;
strcpy(acpi_device_class(device), "sony/hotkey");
@@ -2597,15 +3180,19 @@ static int sony_nc_add(struct acpi_device *device)
}
}
- if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON",
- &handle))) {
+ result = sony_laptop_setup_input(device);
+ if (result) {
+ pr_err("Unable to create input devices\n");
+ goto outplatform;
+ }
+
+ if (acpi_has_method(sony_nc_acpi_handle, "ECON")) {
int arg = 1;
if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))
dprintk("ECON Method failed\n");
}
- if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
- &handle))) {
+ if (acpi_has_method(sony_nc_acpi_handle, "SN00")) {
dprintk("Doing SNC setup\n");
/* retrieve the available handles */
result = sony_nc_handles_setup(sony_pf_device);
@@ -2614,12 +3201,6 @@ static int sony_nc_add(struct acpi_device *device)
}
/* setup input devices and helper fifo */
- result = sony_laptop_setup_input(device);
- if (result) {
- pr_err("Unable to create input devices\n");
- goto outsnc;
- }
-
if (acpi_video_backlight_support()) {
pr_info("brightness ignored, must be controlled by ACPI video driver\n");
} else {
@@ -2634,9 +3215,8 @@ static int sony_nc_add(struct acpi_device *device)
/* find the available acpiget as described in the DSDT */
for (; item->acpiget && *item->acpiget; ++item->acpiget) {
- if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
- *item->acpiget,
- &handle))) {
+ if (acpi_has_method(sony_nc_acpi_handle,
+ *item->acpiget)) {
dprintk("Found %s getter: %s\n",
item->name, *item->acpiget);
item->devattr.attr.mode |= S_IRUGO;
@@ -2646,9 +3226,8 @@ static int sony_nc_add(struct acpi_device *device)
/* find the available acpiset as described in the DSDT */
for (; item->acpiset && *item->acpiset; ++item->acpiset) {
- if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle,
- *item->acpiset,
- &handle))) {
+ if (acpi_has_method(sony_nc_acpi_handle,
+ *item->acpiset)) {
dprintk("Found %s setter: %s\n",
item->name, *item->acpiset);
item->devattr.attr.mode |= S_IWUSR;
@@ -2665,29 +3244,29 @@ static int sony_nc_add(struct acpi_device *device)
}
}
+ pr_info("SNC setup done.\n");
return 0;
- out_sysfs:
+out_sysfs:
for (item = sony_nc_values; item->name; ++item) {
device_remove_file(&sony_pf_device->dev, &item->devattr);
}
sony_nc_backlight_cleanup();
-
- sony_laptop_remove_input();
-
- outsnc:
sony_nc_function_cleanup(sony_pf_device);
sony_nc_handles_cleanup(sony_pf_device);
- outpresent:
+outplatform:
+ sony_laptop_remove_input();
+
+outpresent:
sony_pf_remove();
- outwalk:
+outwalk:
sony_nc_rfkill_cleanup();
return result;
}
-static int sony_nc_remove(struct acpi_device *device, int type)
+static int sony_nc_remove(struct acpi_device *device)
{
struct sony_nc_value *item;
@@ -2728,9 +3307,9 @@ static struct acpi_driver sony_nc_driver = {
.ops = {
.add = sony_nc_add,
.remove = sony_nc_remove,
- .resume = sony_nc_resume,
.notify = sony_nc_notify,
},
+ .drv.pm = &sony_nc_pm,
};
/*********** SPIC (SNY6001) Device ***********/
@@ -3513,7 +4092,7 @@ static ssize_t sonypi_misc_read(struct file *file, char __user *buf,
}
if (ret > 0) {
- struct inode *inode = file->f_path.dentry->d_inode;
+ struct inode *inode = file_inode(file);
inode->i_atime = current_fs_time(inode->i_sb);
}
@@ -3945,7 +4524,7 @@ static int sony_pic_enable(struct acpi_device *device,
resource->res3.data.irq.sharable = ACPI_SHARED;
resource->res4.type = ACPI_RESOURCE_TYPE_END_TAG;
-
+ resource->res4.length = sizeof(struct acpi_resource);
}
/* setup Type 2/3 resources */
else {
@@ -3964,6 +4543,7 @@ static int sony_pic_enable(struct acpi_device *device,
resource->res2.data.irq.sharable = ACPI_SHARED;
resource->res3.type = ACPI_RESOURCE_TYPE_END_TAG;
+ resource->res3.length = sizeof(struct acpi_resource);
}
/* Attempt to set the resource */
@@ -4048,7 +4628,6 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id)
found:
sony_laptop_report_input_event(device_event);
- acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event);
sonypi_compat_report_event(device_event);
return IRQ_HANDLED;
}
@@ -4058,7 +4637,7 @@ found:
* ACPI driver
*
*****************/
-static int sony_pic_remove(struct acpi_device *device, int type)
+static int sony_pic_remove(struct acpi_device *device)
{
struct sony_pic_ioport *io, *tmp_io;
struct sony_pic_irq *irq, *tmp_irq;
@@ -4104,8 +4683,6 @@ static int sony_pic_add(struct acpi_device *device)
struct sony_pic_ioport *io, *tmp_io;
struct sony_pic_irq *irq, *tmp_irq;
- pr_info("%s v%s\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
-
spic_dev.acpi_dev = device;
strcpy(acpi_device_class(device), "sony/hotkey");
sony_pic_detect_device_type(&spic_dev);
@@ -4125,7 +4702,8 @@ static int sony_pic_add(struct acpi_device *device)
goto err_free_resources;
}
- if (sonypi_compat_init())
+ result = sonypi_compat_init();
+ if (result)
goto err_remove_input;
/* request io port */
@@ -4204,6 +4782,7 @@ static int sony_pic_add(struct acpi_device *device)
if (result)
goto err_remove_pf;
+ pr_info("SPIC setup done.\n");
return 0;
err_remove_pf:
@@ -4243,18 +4822,23 @@ err_free_resources:
return result;
}
-static int sony_pic_suspend(struct acpi_device *device, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int sony_pic_suspend(struct device *dev)
{
- if (sony_pic_disable(device))
+ if (sony_pic_disable(to_acpi_device(dev)))
return -ENXIO;
return 0;
}
-static int sony_pic_resume(struct acpi_device *device)
+static int sony_pic_resume(struct device *dev)
{
- sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
+ sony_pic_enable(to_acpi_device(dev),
+ spic_dev.cur_ioport, spic_dev.cur_irq);
return 0;
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sony_pic_pm, sony_pic_suspend, sony_pic_resume);
static const struct acpi_device_id sony_pic_device_ids[] = {
{SONY_PIC_HID, 0},
@@ -4269,9 +4853,8 @@ static struct acpi_driver sony_pic_driver = {
.ops = {
.add = sony_pic_add,
.remove = sony_pic_remove,
- .suspend = sony_pic_suspend,
- .resume = sony_pic_resume,
},
+ .drv.pm = &sony_pic_pm,
};
static struct dmi_system_id __initdata sonypi_dmi_table[] = {
diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c
index e24f5ae475a..6a6ea28a7e5 100644
--- a/drivers/platform/x86/tc1100-wmi.c
+++ b/drivers/platform/x86/tc1100-wmi.c
@@ -32,9 +32,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <acpi/acpi.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
+#include <linux/acpi.h>
#include <linux/platform_device.h>
#define GUID "C364AC71-36DB-495A-8494-B439D472A505"
@@ -187,7 +185,7 @@ static int __init tc1100_probe(struct platform_device *device)
}
-static int __devexit tc1100_remove(struct platform_device *device)
+static int tc1100_remove(struct platform_device *device)
{
sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group);
@@ -241,7 +239,7 @@ static struct platform_driver tc1100_driver = {
.pm = &tc1100_pm_ops,
#endif
},
- .remove = __devexit_p(tc1100_remove),
+ .remove = tc1100_remove,
};
static int __init tc1100_init(void)
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 8b5610d8841..d82f196e3cf 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -23,7 +23,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define TPACPI_VERSION "0.24"
+#define TPACPI_VERSION "0.25"
#define TPACPI_SYSFS_VERSION 0x020700
/*
@@ -61,7 +61,6 @@
#include <linux/freezer.h>
#include <linux/delay.h>
#include <linux/slab.h>
-
#include <linux/nvram.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
@@ -74,20 +73,16 @@
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/rfkill.h>
-#include <asm/uaccess.h>
-
#include <linux/dmi.h>
#include <linux/jiffies.h>
#include <linux/workqueue.h>
-
+#include <linux/acpi.h>
+#include <linux/pci_ids.h>
+#include <linux/thinkpad_acpi.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
-
-#include <acpi/acpi_drivers.h>
-
-#include <linux/pci_ids.h>
-
+#include <asm/uaccess.h>
/* ThinkPad CMOS commands */
#define TP_CMOS_VOLUME_DOWN 0
@@ -209,9 +204,8 @@ enum tpacpi_hkey_event_t {
TP_HKEY_EV_ALARM_SENSOR_XHOT = 0x6022, /* sensor critically hot */
TP_HKEY_EV_THM_TABLE_CHANGED = 0x6030, /* thermal table changed */
- TP_HKEY_EV_UNK_6040 = 0x6040, /* Related to AC change?
- some sort of APM hint,
- W520 */
+ /* AC-related events */
+ TP_HKEY_EV_AC_CHANGED = 0x6040, /* AC status changed */
/* Misc */
TP_HKEY_EV_RFKILL_CHANGED = 0x7000, /* rfkill switch changed */
@@ -277,7 +271,7 @@ struct ibm_struct {
int (*write) (char *);
void (*exit) (void);
void (*resume) (void);
- void (*suspend) (pm_message_t state);
+ void (*suspend) (void);
void (*shutdown) (void);
struct list_head all_drivers;
@@ -370,7 +364,7 @@ struct tpacpi_led_classdev {
struct led_classdev led_classdev;
struct work_struct work;
enum led_status_t new_state;
- unsigned int led;
+ int led;
};
/* brightness level capabilities */
@@ -522,7 +516,7 @@ static acpi_handle ec_handle;
#define TPACPI_HANDLE(object, parent, paths...) \
static acpi_handle object##_handle; \
- static const acpi_handle *object##_parent __initdata = \
+ static const acpi_handle * const object##_parent __initconst = \
&parent##_handle; \
static char *object##_paths[] __initdata = { paths }
@@ -545,7 +539,7 @@ TPACPI_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */
*/
static int acpi_evalf(acpi_handle handle,
- void *res, char *method, char *fmt, ...)
+ int *res, char *method, char *fmt, ...)
{
char *fmt0 = fmt;
struct acpi_object_list params;
@@ -606,7 +600,7 @@ static int acpi_evalf(acpi_handle handle,
success = (status == AE_OK &&
out_obj.type == ACPI_TYPE_INTEGER);
if (success && res)
- *(int *)res = out_obj.integer.value;
+ *res = out_obj.integer.value;
break;
case 'v': /* void */
success = status == AE_OK;
@@ -701,6 +695,14 @@ static void __init drv_acpi_handle_init(const char *name,
static acpi_status __init tpacpi_acpi_handle_locate_callback(acpi_handle handle,
u32 level, void *context, void **return_value)
{
+ struct acpi_device *dev;
+ if (!strcmp(context, "video")) {
+ if (acpi_bus_get_device(handle, &dev))
+ return AE_OK;
+ if (strcmp(ACPI_VIDEO_HID, acpi_device_hid(dev)))
+ return AE_OK;
+ }
+
*(acpi_handle *)return_value = handle;
return AE_CTRL_TERMINATE;
@@ -713,10 +715,10 @@ static void __init tpacpi_acpi_handle_locate(const char *name,
acpi_status status;
acpi_handle device_found;
- BUG_ON(!name || !hid || !handle);
+ BUG_ON(!name || !handle);
vdbg_printk(TPACPI_DBG_INIT,
"trying to locate ACPI handle for %s, using HID %s\n",
- name, hid);
+ name, hid ? hid : "NULL");
memset(&device_found, 0, sizeof(device_found));
status = acpi_get_devices(hid, tpacpi_acpi_handle_locate_callback,
@@ -845,14 +847,14 @@ static int dispatch_proc_show(struct seq_file *m, void *v)
static int dispatch_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, dispatch_proc_show, PDE(inode)->data);
+ return single_open(file, dispatch_proc_show, PDE_DATA(inode));
}
static ssize_t dispatch_proc_write(struct file *file,
const char __user *userbuf,
size_t count, loff_t *pos)
{
- struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data;
+ struct ibm_struct *ibm = PDE_DATA(file_inode(file));
char *kernbuf;
int ret;
@@ -922,8 +924,8 @@ static struct input_dev *tpacpi_inputdev;
static struct mutex tpacpi_inputdev_send_mutex;
static LIST_HEAD(tpacpi_all_drivers);
-static int tpacpi_suspend_handler(struct platform_device *pdev,
- pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int tpacpi_suspend_handler(struct device *dev)
{
struct ibm_struct *ibm, *itmp;
@@ -931,13 +933,13 @@ static int tpacpi_suspend_handler(struct platform_device *pdev,
&tpacpi_all_drivers,
all_drivers) {
if (ibm->suspend)
- (ibm->suspend)(state);
+ (ibm->suspend)();
}
return 0;
}
-static int tpacpi_resume_handler(struct platform_device *pdev)
+static int tpacpi_resume_handler(struct device *dev)
{
struct ibm_struct *ibm, *itmp;
@@ -950,6 +952,10 @@ static int tpacpi_resume_handler(struct platform_device *pdev)
return 0;
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tpacpi_pm,
+ tpacpi_suspend_handler, tpacpi_resume_handler);
static void tpacpi_shutdown_handler(struct platform_device *pdev)
{
@@ -967,9 +973,8 @@ static struct platform_driver tpacpi_pdriver = {
.driver = {
.name = TPACPI_DRVR_NAME,
.owner = THIS_MODULE,
+ .pm = &tpacpi_pm,
},
- .suspend = tpacpi_suspend_handler,
- .resume = tpacpi_resume_handler,
.shutdown = tpacpi_shutdown_handler,
};
@@ -1962,9 +1967,6 @@ struct tp_nvram_state {
/* kthread for the hotkey poller */
static struct task_struct *tpacpi_hotkey_task;
-/* Acquired while the poller kthread is running, use to sync start/stop */
-static struct mutex hotkey_thread_mutex;
-
/*
* Acquire mutex to write poller control variables as an
* atomic block.
@@ -2023,8 +2025,6 @@ static u32 hotkey_driver_mask; /* events needed by the driver */
static u32 hotkey_user_mask; /* events visible to userspace */
static u32 hotkey_acpi_mask; /* events enabled in firmware */
-static unsigned int hotkey_report_mode;
-
static u16 *hotkey_keycode_map;
static struct attribute_set *hotkey_dev_attributes;
@@ -2283,10 +2283,6 @@ static struct tp_acpi_drv_struct ibm_hotkey_acpidriver;
static void tpacpi_hotkey_send_key(unsigned int scancode)
{
tpacpi_input_send_key_masked(scancode);
- if (hotkey_report_mode < 2) {
- acpi_bus_generate_proc_event(ibm_hotkey_acpidriver.device,
- 0x80, TP_HKEY_EV_HOTKEY_BASE + scancode);
- }
}
static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
@@ -2325,53 +2321,55 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
}
}
-static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
- struct tp_nvram_state *newn,
- const u32 event_mask)
-{
-
#define TPACPI_COMPARE_KEY(__scancode, __member) \
- do { \
- if ((event_mask & (1 << __scancode)) && \
- oldn->__member != newn->__member) \
- tpacpi_hotkey_send_key(__scancode); \
- } while (0)
+do { \
+ if ((event_mask & (1 << __scancode)) && \
+ oldn->__member != newn->__member) \
+ tpacpi_hotkey_send_key(__scancode); \
+} while (0)
#define TPACPI_MAY_SEND_KEY(__scancode) \
- do { \
- if (event_mask & (1 << __scancode)) \
- tpacpi_hotkey_send_key(__scancode); \
- } while (0)
+do { \
+ if (event_mask & (1 << __scancode)) \
+ tpacpi_hotkey_send_key(__scancode); \
+} while (0)
- void issue_volchange(const unsigned int oldvol,
- const unsigned int newvol)
- {
- unsigned int i = oldvol;
+static void issue_volchange(const unsigned int oldvol,
+ const unsigned int newvol,
+ const u32 event_mask)
+{
+ unsigned int i = oldvol;
- while (i > newvol) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
- i--;
- }
- while (i < newvol) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
- i++;
- }
+ while (i > newvol) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN);
+ i--;
}
+ while (i < newvol) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
+ i++;
+ }
+}
- void issue_brightnesschange(const unsigned int oldbrt,
- const unsigned int newbrt)
- {
- unsigned int i = oldbrt;
+static void issue_brightnesschange(const unsigned int oldbrt,
+ const unsigned int newbrt,
+ const u32 event_mask)
+{
+ unsigned int i = oldbrt;
- while (i > newbrt) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
- i--;
- }
- while (i < newbrt) {
- TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
- i++;
- }
+ while (i > newbrt) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
+ i--;
}
+ while (i < newbrt) {
+ TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
+ i++;
+ }
+}
+
+static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
+ struct tp_nvram_state *newn,
+ const u32 event_mask)
+{
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
@@ -2406,7 +2404,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
oldn->volume_level != newn->volume_level) {
/* recently muted, or repeated mute keypress, or
* multiple presses ending in mute */
- issue_volchange(oldn->volume_level, newn->volume_level);
+ issue_volchange(oldn->volume_level, newn->volume_level,
+ event_mask);
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);
}
} else {
@@ -2416,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);
}
if (oldn->volume_level != newn->volume_level) {
- issue_volchange(oldn->volume_level, newn->volume_level);
+ issue_volchange(oldn->volume_level, newn->volume_level,
+ event_mask);
} else if (oldn->volume_toggle != newn->volume_toggle) {
/* repeated vol up/down keypress at end of scale ? */
if (newn->volume_level == 0)
@@ -2429,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
/* handle brightness */
if (oldn->brightness_level != newn->brightness_level) {
issue_brightnesschange(oldn->brightness_level,
- newn->brightness_level);
+ newn->brightness_level, event_mask);
} else if (oldn->brightness_toggle != newn->brightness_toggle) {
/* repeated key presses that didn't change state */
if (newn->brightness_level == 0)
@@ -2460,8 +2460,6 @@ static int hotkey_kthread(void *data)
unsigned int poll_freq;
bool was_frozen;
- mutex_lock(&hotkey_thread_mutex);
-
if (tpacpi_lifecycle == TPACPI_LIFE_EXITING)
goto exit;
@@ -2521,7 +2519,6 @@ static int hotkey_kthread(void *data)
}
exit:
- mutex_unlock(&hotkey_thread_mutex);
return 0;
}
@@ -2531,9 +2528,6 @@ static void hotkey_poll_stop_sync(void)
if (tpacpi_hotkey_task) {
kthread_stop(tpacpi_hotkey_task);
tpacpi_hotkey_task = NULL;
- mutex_lock(&hotkey_thread_mutex);
- /* at this point, the thread did exit */
- mutex_unlock(&hotkey_thread_mutex);
}
}
@@ -2889,18 +2883,6 @@ static void hotkey_tablet_mode_notify_change(void)
"hotkey_tablet_mode");
}
-/* sysfs hotkey report_mode -------------------------------------------- */
-static ssize_t hotkey_report_mode_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return snprintf(buf, PAGE_SIZE, "%d\n",
- (hotkey_report_mode != 0) ? hotkey_report_mode : 1);
-}
-
-static struct device_attribute dev_attr_hotkey_report_mode =
- __ATTR(hotkey_report_mode, S_IRUGO, hotkey_report_mode_show, NULL);
-
/* sysfs wakeup reason (pollable) -------------------------------------- */
static ssize_t hotkey_wakeup_reason_show(struct device *dev,
struct device_attribute *attr,
@@ -2942,7 +2924,6 @@ static struct attribute *hotkey_attributes[] __initdata = {
&dev_attr_hotkey_enable.attr,
&dev_attr_hotkey_bios_enabled.attr,
&dev_attr_hotkey_bios_mask.attr,
- &dev_attr_hotkey_report_mode.attr,
&dev_attr_hotkey_wakeup_reason.attr,
&dev_attr_hotkey_wakeup_hotunplug_complete.attr,
&dev_attr_hotkey_mask.attr,
@@ -3014,8 +2995,6 @@ static void hotkey_exit(void)
if (hotkey_dev_attributes)
delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
- kfree(hotkey_keycode_map);
-
dbg_printk(TPACPI_DBG_EXIT | TPACPI_DBG_HKEY,
"restoring original HKEY status and mask\n");
/* yes, there is a bitwise or below, we want the
@@ -3192,8 +3171,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
KEY_MICMUTE, /* 0x1a: Mic mute (since ?400 or so) */
/* (assignments unknown, please report if found) */
- KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,
KEY_UNKNOWN,
+
+ /* Extra keys in use since the X240 / T440 / T540 */
+ KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_COMPUTER,
},
};
@@ -3234,7 +3215,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
mutex_init(&hotkey_mutex);
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
- mutex_init(&hotkey_thread_mutex);
mutex_init(&hotkey_thread_data_mutex);
#endif
@@ -3449,11 +3429,6 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
"initial masks: user=0x%08x, fw=0x%08x, poll=0x%08x\n",
hotkey_user_mask, hotkey_acpi_mask, hotkey_source_mask);
- dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY,
- "legacy ibm/hotkey event reporting over procfs %s\n",
- (hotkey_report_mode < 2) ?
- "enabled" : "disabled");
-
tpacpi_inputdev->open = &hotkey_inputdev_open;
tpacpi_inputdev->close = &hotkey_inputdev_close;
@@ -3468,6 +3443,106 @@ err_exit:
return (res < 0)? res : 1;
}
+/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser
+ * mode, Web conference mode, Function mode and Lay-flat mode.
+ * We support Home mode and Function mode currently.
+ *
+ * Will consider support rest of modes in future.
+ *
+ */
+enum ADAPTIVE_KEY_MODE {
+ HOME_MODE,
+ WEB_BROWSER_MODE,
+ WEB_CONFERENCE_MODE,
+ FUNCTION_MODE,
+ LAYFLAT_MODE
+};
+
+const int adaptive_keyboard_modes[] = {
+ HOME_MODE,
+/* WEB_BROWSER_MODE = 2,
+ WEB_CONFERENCE_MODE = 3, */
+ FUNCTION_MODE
+};
+
+#define DFR_CHANGE_ROW 0x101
+#define DFR_SHOW_QUICKVIEW_ROW 0x102
+
+/* press Fn key a while second, it will switch to Function Mode. Then
+ * release Fn key, previous mode be restored.
+ */
+static bool adaptive_keyboard_mode_is_saved;
+static int adaptive_keyboard_prev_mode;
+
+static int adaptive_keyboard_get_next_mode(int mode)
+{
+ size_t i;
+ size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1;
+
+ for (i = 0; i <= max_mode; i++) {
+ if (adaptive_keyboard_modes[i] == mode)
+ break;
+ }
+
+ if (i >= max_mode)
+ i = 0;
+ else
+ i++;
+
+ return adaptive_keyboard_modes[i];
+}
+
+static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode)
+{
+ u32 current_mode = 0;
+ int new_mode = 0;
+
+ switch (scancode) {
+ case DFR_CHANGE_ROW:
+ if (adaptive_keyboard_mode_is_saved) {
+ new_mode = adaptive_keyboard_prev_mode;
+ adaptive_keyboard_mode_is_saved = false;
+ } else {
+ if (!acpi_evalf(
+ hkey_handle, &current_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode\n");
+ return false;
+ } else {
+ new_mode = adaptive_keyboard_get_next_mode(
+ current_mode);
+ }
+ }
+
+ if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) {
+ pr_err("Cannot set adaptive keyboard mode\n");
+ return false;
+ }
+
+ return true;
+
+ case DFR_SHOW_QUICKVIEW_ROW:
+ if (!acpi_evalf(hkey_handle,
+ &adaptive_keyboard_prev_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode\n");
+ return false;
+ } else {
+ adaptive_keyboard_mode_is_saved = true;
+
+ if (!acpi_evalf(hkey_handle,
+ NULL, "STRW", "vd", FUNCTION_MODE)) {
+ pr_err("Cannot set adaptive keyboard mode\n");
+ return false;
+ }
+ }
+ return true;
+
+ default:
+ return false;
+ }
+}
+
static bool hotkey_notify_hotkey(const u32 hkey,
bool *send_acpi_ev,
bool *ignore_acpi_ev)
@@ -3487,6 +3562,8 @@ static bool hotkey_notify_hotkey(const u32 hkey,
*ignore_acpi_ev = true;
}
return true;
+ } else {
+ return adaptive_keyboard_hotkey_notify_hotkey(scancode);
}
return false;
}
@@ -3628,6 +3705,12 @@ static bool hotkey_notify_6xxx(const u32 hkey,
"a sensor reports something is extremely hot!\n");
/* recommended action: immediate sleep/hibernate */
break;
+ case TP_HKEY_EV_AC_CHANGED:
+ /* X120e, X121e, X220, X220i, X220t, X230, T420, T420s, W520:
+ * AC status changed; can be triggered by plugging or
+ * unplugging AC adapter, docking or undocking. */
+
+ /* fallthrough */
case TP_HKEY_EV_KEY_NUMLOCK:
case TP_HKEY_EV_KEY_FN:
@@ -3741,13 +3824,6 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
"event happened to %s\n", TPACPI_MAIL);
}
- /* Legacy events */
- if (!ignore_acpi_ev &&
- (send_acpi_ev || hotkey_report_mode < 2)) {
- acpi_bus_generate_proc_event(ibm->acpi->device,
- event, hkey);
- }
-
/* netlink events */
if (!ignore_acpi_ev && send_acpi_ev) {
acpi_bus_generate_netlink_event(
@@ -3758,15 +3834,30 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)
}
}
-static void hotkey_suspend(pm_message_t state)
+static void hotkey_suspend(void)
{
+ int hkeyv;
+
/* Do these on suspend, we get the events on early resume! */
hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;
hotkey_autosleep_ack = 0;
+
+ /* save previous mode of adaptive keyboard of X1 Carbon */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) == 2) {
+ if (!acpi_evalf(hkey_handle,
+ &adaptive_keyboard_prev_mode,
+ "GTRW", "dd", 0)) {
+ pr_err("Cannot read adaptive keyboard mode.\n");
+ }
+ }
+ }
}
static void hotkey_resume(void)
{
+ int hkeyv;
+
tpacpi_disable_brightness_delay();
if (hotkey_status_set(true) < 0 ||
@@ -3779,6 +3870,18 @@ static void hotkey_resume(void)
hotkey_wakeup_reason_notify_change();
hotkey_wakeup_hotunplug_complete_notify_change();
hotkey_poll_setup_safe(false);
+
+ /* restore previous mode of adapive keyboard of X1 Carbon */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) == 2) {
+ if (!acpi_evalf(hkey_handle,
+ NULL,
+ "STRW", "vd",
+ adaptive_keyboard_prev_mode)) {
+ pr_err("Cannot set adaptive keyboard mode.\n");
+ }
+ }
+ }
}
/* procfs -------------------------------------------------------------- */
@@ -4876,8 +4979,7 @@ static int __init light_init(struct ibm_init_struct *iibm)
static void light_exit(void)
{
led_classdev_unregister(&tpacpi_led_thinklight.led_classdev);
- if (work_pending(&tpacpi_led_thinklight.work))
- flush_workqueue(tpacpi_wq);
+ flush_workqueue(tpacpi_wq);
}
static int light_read(struct seq_file *m)
@@ -5216,6 +5318,7 @@ static void led_exit(void)
led_classdev_unregister(&tpacpi_leds[i].led_classdev);
}
+ flush_workqueue(tpacpi_wq);
kfree(tpacpi_leds);
}
@@ -5331,6 +5434,16 @@ static int __init led_init(struct ibm_init_struct *iibm)
led_supported = led_init_detect_mode();
+ if (led_supported != TPACPI_LED_NONE) {
+ useful_leds = tpacpi_check_quirks(led_useful_qtable,
+ ARRAY_SIZE(led_useful_qtable));
+
+ if (!useful_leds) {
+ led_handle = NULL;
+ led_supported = TPACPI_LED_NONE;
+ }
+ }
+
vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n",
str_supported(led_supported), led_supported);
@@ -5344,10 +5457,9 @@ static int __init led_init(struct ibm_init_struct *iibm)
return -ENOMEM;
}
- useful_leds = tpacpi_check_quirks(led_useful_qtable,
- ARRAY_SIZE(led_useful_qtable));
-
for (i = 0; i < TPACPI_LED_NUMLEDS; i++) {
+ tpacpi_leds[i].led = -1;
+
if (!tpacpi_is_led_restricted(i) &&
test_bit(i, &useful_leds)) {
rc = tpacpi_init_led(i);
@@ -5405,9 +5517,13 @@ static int led_write(char *buf)
return -ENODEV;
while ((cmd = next_cmd(&buf))) {
- if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15)
+ if (sscanf(cmd, "%d", &led) != 1)
return -EINVAL;
+ if (led < 0 || led > (TPACPI_LED_NUMLEDS - 1) ||
+ tpacpi_leds[led].led < 0)
+ return -ENODEV;
+
if (strstr(cmd, "off")) {
s = TPACPI_LED_OFF;
} else if (strstr(cmd, "on")) {
@@ -6112,19 +6228,28 @@ static int __init tpacpi_query_bcl_levels(acpi_handle handle)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
+ struct acpi_device *device, *child;
int rc;
- if (ACPI_SUCCESS(acpi_evaluate_object(handle, "_BCL", NULL, &buffer))) {
+ if (acpi_bus_get_device(handle, &device))
+ return 0;
+
+ rc = 0;
+ list_for_each_entry(child, &device->children, node) {
+ acpi_status status = acpi_evaluate_object(child->handle, "_BCL",
+ NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ continue;
+
obj = (union acpi_object *)buffer.pointer;
if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {
pr_err("Unknown _BCL data, please report this to %s\n",
- TPACPI_MAIL);
+ TPACPI_MAIL);
rc = 0;
} else {
rc = obj->package.count;
}
- } else {
- return 0;
+ break;
}
kfree(buffer.pointer);
@@ -6140,7 +6265,7 @@ static unsigned int __init tpacpi_check_std_acpi_brightness_support(void)
acpi_handle video_device;
int bcl_levels = 0;
- tpacpi_acpi_handle_locate("video", ACPI_VIDEO_HID, &video_device);
+ tpacpi_acpi_handle_locate("video", NULL, &video_device);
if (video_device)
bcl_levels = tpacpi_query_bcl_levels(video_device);
@@ -6329,7 +6454,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm)
return 0;
}
-static void brightness_suspend(pm_message_t state)
+static void brightness_suspend(void)
{
tpacpi_brightness_checkpoint_nvram();
}
@@ -6442,7 +6567,12 @@ static struct ibm_struct brightness_driver_data = {
#define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control"
#define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME
-static int alsa_index = ~((1 << (SNDRV_CARDS - 3)) - 1); /* last three slots */
+#if SNDRV_CARDS <= 32
+#define DEFAULT_ALSA_IDX ~((1 << (SNDRV_CARDS - 3)) - 1)
+#else
+#define DEFAULT_ALSA_IDX ~((1 << (32 - 3)) - 1)
+#endif
+static int alsa_index = DEFAULT_ALSA_IDX; /* last three slots */
static char *alsa_id = "ThinkPadEC";
static bool alsa_enable = SNDRV_DEFAULT_ENABLE1;
@@ -6730,7 +6860,7 @@ static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol,
return volume_alsa_set_mute(!ucontrol->value.integer.value[0]);
}
-static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = {
+static struct snd_kcontrol_new volume_alsa_control_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Console Playback Volume",
.index = 0,
@@ -6739,7 +6869,7 @@ static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = {
.get = volume_alsa_vol_get,
};
-static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = {
+static struct snd_kcontrol_new volume_alsa_control_mute = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Console Playback Switch",
.index = 0,
@@ -6748,7 +6878,7 @@ static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = {
.get = volume_alsa_mute_get,
};
-static void volume_suspend(pm_message_t state)
+static void volume_suspend(void)
{
tpacpi_volume_checkpoint_nvram();
}
@@ -6781,8 +6911,9 @@ static int __init volume_create_alsa_mixer(void)
struct snd_kcontrol *ctl_mute;
int rc;
- rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE,
- sizeof(struct tpacpi_alsa_data), &card);
+ rc = snd_card_new(&tpacpi_pdev->dev,
+ alsa_index, alsa_id, THIS_MODULE,
+ sizeof(struct tpacpi_alsa_data), &card);
if (rc < 0 || !card) {
pr_err("Failed to create ALSA card structures: %d\n", rc);
return 1;
@@ -6833,7 +6964,6 @@ static int __init volume_create_alsa_mixer(void)
}
data->ctl_mute_id = &ctl_mute->id;
- snd_card_set_dev(card, &tpacpi_pdev->dev);
rc = snd_card_register(card);
if (rc < 0) {
pr_err("Failed to register ALSA card: %d\n", rc);
@@ -7384,17 +7514,18 @@ static int fan_get_status(u8 *status)
* Add TPACPI_FAN_RD_ACPI_FANS ? */
switch (fan_status_access_mode) {
- case TPACPI_FAN_RD_ACPI_GFAN:
+ case TPACPI_FAN_RD_ACPI_GFAN: {
/* 570, 600e/x, 770e, 770x */
+ int res;
- if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d")))
+ if (unlikely(!acpi_evalf(gfan_handle, &res, NULL, "d")))
return -EIO;
if (likely(status))
- *status = s & 0x07;
+ *status = res & 0x07;
break;
-
+ }
case TPACPI_FAN_RD_TPEC:
/* all except 570, 600e/x, 770e, 770x */
if (unlikely(!acpi_ec_read(fan_status_offset, &s)))
@@ -7682,25 +7813,15 @@ static int fan_set_speed(int speed)
static void fan_watchdog_reset(void)
{
- static int fan_watchdog_active;
-
if (fan_control_access_mode == TPACPI_FAN_WR_NONE)
return;
- if (fan_watchdog_active)
- cancel_delayed_work(&fan_watchdog_task);
-
if (fan_watchdog_maxinterval > 0 &&
- tpacpi_lifecycle != TPACPI_LIFE_EXITING) {
- fan_watchdog_active = 1;
- if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task,
- msecs_to_jiffies(fan_watchdog_maxinterval
- * 1000))) {
- pr_err("failed to queue the fan watchdog, "
- "watchdog will not trigger\n");
- }
- } else
- fan_watchdog_active = 0;
+ tpacpi_lifecycle != TPACPI_LIFE_EXITING)
+ mod_delayed_work(tpacpi_wq, &fan_watchdog_task,
+ msecs_to_jiffies(fan_watchdog_maxinterval * 1000));
+ else
+ cancel_delayed_work(&fan_watchdog_task);
}
static void fan_watchdog_fire(struct work_struct *ignored)
@@ -8107,7 +8228,7 @@ static void fan_exit(void)
flush_workqueue(tpacpi_wq);
}
-static void fan_suspend(pm_message_t state)
+static void fan_suspend(void)
{
int rc;
@@ -8381,6 +8502,103 @@ static struct ibm_struct fan_driver_data = {
.resume = fan_resume,
};
+/*************************************************************************
+ * Mute LED subdriver
+ */
+
+
+struct tp_led_table {
+ acpi_string name;
+ int on_value;
+ int off_value;
+ int state;
+};
+
+static struct tp_led_table led_tables[] = {
+ [TPACPI_LED_MUTE] = {
+ .name = "SSMS",
+ .on_value = 1,
+ .off_value = 0,
+ },
+ [TPACPI_LED_MICMUTE] = {
+ .name = "MMTS",
+ .on_value = 2,
+ .off_value = 0,
+ },
+};
+
+static int mute_led_on_off(struct tp_led_table *t, bool state)
+{
+ acpi_handle temp;
+ int output;
+
+ if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) {
+ pr_warn("Thinkpad ACPI has no %s interface.\n", t->name);
+ return -EIO;
+ }
+
+ if (!acpi_evalf(hkey_handle, &output, t->name, "dd",
+ state ? t->on_value : t->off_value))
+ return -EIO;
+
+ t->state = state;
+ return state;
+}
+
+int tpacpi_led_set(int whichled, bool on)
+{
+ struct tp_led_table *t;
+
+ if (whichled < 0 || whichled >= TPACPI_LED_MAX)
+ return -EINVAL;
+
+ t = &led_tables[whichled];
+ if (t->state < 0 || t->state == on)
+ return t->state;
+ return mute_led_on_off(t, on);
+}
+EXPORT_SYMBOL_GPL(tpacpi_led_set);
+
+static int mute_led_init(struct ibm_init_struct *iibm)
+{
+ acpi_handle temp;
+ int i;
+
+ for (i = 0; i < TPACPI_LED_MAX; i++) {
+ struct tp_led_table *t = &led_tables[i];
+ if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp)))
+ mute_led_on_off(t, false);
+ else
+ t->state = -ENODEV;
+ }
+ return 0;
+}
+
+static void mute_led_exit(void)
+{
+ int i;
+
+ for (i = 0; i < TPACPI_LED_MAX; i++)
+ tpacpi_led_set(i, false);
+}
+
+static void mute_led_resume(void)
+{
+ int i;
+
+ for (i = 0; i < TPACPI_LED_MAX; i++) {
+ struct tp_led_table *t = &led_tables[i];
+ if (t->state >= 0)
+ mute_led_on_off(t, t->state);
+ }
+}
+
+static struct ibm_struct mute_led_driver_data = {
+ .name = "mute_led",
+ .exit = mute_led_exit,
+ .resume = mute_led_resume,
+};
+
/****************************************************************************
****************************************************************************
*
@@ -8582,7 +8800,8 @@ static bool __pure __init tpacpi_is_valid_fw_id(const char* const s,
return s && strlen(s) >= 8 &&
tpacpi_is_fw_digit(s[0]) &&
tpacpi_is_fw_digit(s[1]) &&
- s[2] == t && s[3] == 'T' &&
+ s[2] == t &&
+ (s[3] == 'T' || s[3] == 'N') &&
tpacpi_is_fw_digit(s[4]) &&
tpacpi_is_fw_digit(s[5]);
}
@@ -8615,7 +8834,8 @@ static int __must_check __init get_thinkpad_model_data(
return -ENOMEM;
/* Really ancient ThinkPad 240X will fail this, which is fine */
- if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E'))
+ if (!(tpacpi_is_valid_fw_id(tp->bios_version_str, 'E') ||
+ tpacpi_is_valid_fw_id(tp->bios_version_str, 'C')))
return 0;
tp->bios_model = tp->bios_version_str[0]
@@ -8662,6 +8882,13 @@ static int __must_check __init get_thinkpad_model_data(
tp->model_str = kstrdup(s, GFP_KERNEL);
if (!tp->model_str)
return -ENOMEM;
+ } else {
+ s = dmi_get_system_info(DMI_BIOS_VENDOR);
+ if (s && !(strnicmp(s, "Lenovo", 6))) {
+ tp->model_str = kstrdup(s, GFP_KERNEL);
+ if (!tp->model_str)
+ return -ENOMEM;
+ }
}
s = dmi_get_system_info(DMI_PRODUCT_NAME);
@@ -8790,6 +9017,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {
.init = fan_init,
.data = &fan_driver_data,
},
+ {
+ .init = mute_led_init,
+ .data = &mute_led_driver_data,
+ },
};
static int __init set_ibm_param(const char *val, struct kernel_param *kp)
@@ -8844,11 +9075,6 @@ module_param(brightness_enable, uint, 0444);
MODULE_PARM_DESC(brightness_enable,
"Enables backlight control when 1, disables when 0");
-module_param(hotkey_report_mode, uint, 0444);
-MODULE_PARM_DESC(hotkey_report_mode,
- "used for backwards compatibility with userspace, "
- "see documentation");
-
#ifdef CONFIG_THINKPAD_ACPI_ALSA_SUPPORT
module_param_named(volume_mode, volume_mode, uint, 0444);
MODULE_PARM_DESC(volume_mode,
@@ -8935,6 +9161,7 @@ static void thinkpad_acpi_module_exit(void)
input_unregister_device(tpacpi_inputdev);
else
input_free_device(tpacpi_inputdev);
+ kfree(hotkey_keycode_map);
}
if (tpacpi_hwmon)
@@ -8968,6 +9195,7 @@ static void thinkpad_acpi_module_exit(void)
kfree(thinkpad_id.bios_version_str);
kfree(thinkpad_id.ec_version_str);
kfree(thinkpad_id.model_str);
+ kfree(thinkpad_id.nummodel_str);
}
@@ -8977,10 +9205,6 @@ static int __init thinkpad_acpi_module_init(void)
tpacpi_lifecycle = TPACPI_LIFE_INIT;
- /* Parameter checking */
- if (hotkey_report_mode > 2)
- return -EINVAL;
-
/* Driver-level probe */
ret = get_thinkpad_model_data(&thinkpad_id);
@@ -9085,7 +9309,6 @@ static int __init thinkpad_acpi_module_init(void)
mutex_init(&tpacpi_inputdev_send_mutex);
tpacpi_inputdev = input_allocate_device();
if (!tpacpi_inputdev) {
- pr_err("unable to allocate input device\n");
thinkpad_acpi_module_exit();
return -ENOMEM;
} else {
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c
index d528daa0e81..e597de05e6c 100644
--- a/drivers/platform/x86/topstar-laptop.c
+++ b/drivers/platform/x86/topstar-laptop.c
@@ -80,13 +80,9 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 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);
+ status = acpi_execute_simple_method(device->handle, "FNCX",
+ state ? 0x86 : 0x87);
if (ACPI_FAILURE(status)) {
pr_err("Unable to switch FNCX notifications\n");
return -ENODEV;
@@ -101,10 +97,8 @@ static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)
int error;
input = input_allocate_device();
- if (!input) {
- pr_err("Unable to allocate input device\n");
+ if (!input)
return -ENOMEM;
- }
input->name = "Topstar Laptop extra buttons";
input->phys = "topstar/input0";
@@ -157,7 +151,7 @@ add_err:
return -ENODEV;
}
-static int acpi_topstar_remove(struct acpi_device *device, int type)
+static int acpi_topstar_remove(struct acpi_device *device)
{
struct topstar_hkey *tps_hkey = acpi_driver_data(device);
@@ -186,27 +180,7 @@ static struct acpi_driver acpi_topstar_driver = {
.notify = acpi_topstar_notify,
},
};
-
-static int __init topstar_laptop_init(void)
-{
- int ret;
-
- ret = acpi_bus_register_driver(&acpi_topstar_driver);
- if (ret < 0)
- return ret;
-
- pr_info("ACPI extras driver loaded\n");
-
- return 0;
-}
-
-static void __exit topstar_laptop_exit(void)
-{
- acpi_bus_unregister_driver(&acpi_topstar_driver);
-}
-
-module_init(topstar_laptop_init);
-module_exit(topstar_laptop_exit);
+module_acpi_driver(acpi_topstar_driver);
MODULE_AUTHOR("Herton Ronaldo Krzesinski");
MODULE_DESCRIPTION("Topstar Laptop ACPI Extras driver");
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index dab10f6edcd..76441dcbe5f 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -5,6 +5,7 @@
* Copyright (C) 2002-2004 John Belmonte
* Copyright (C) 2008 Philip Langdale
* Copyright (C) 2010 Pierre Ducroquet
+ * Copyright (C) 2014 Azael Avalos
*
* 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
@@ -37,7 +38,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define TOSHIBA_ACPI_VERSION "0.19"
+#define TOSHIBA_ACPI_VERSION "0.20"
#define PROC_INTERFACE_VERSION 1
#include <linux/kernel.h>
@@ -54,11 +55,10 @@
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/i8042.h>
-
+#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <asm/uaccess.h>
-#include <acpi/acpi_drivers.h>
-
MODULE_AUTHOR("John Belmonte");
MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");
MODULE_LICENSE("GPL");
@@ -79,6 +79,9 @@ MODULE_LICENSE("GPL");
* However the ACPI methods seem to be incomplete in some areas (for
* example they allow setting, but not reading, the LCD brightness value),
* so this is still useful.
+ *
+ * SCI stands for "System Configuration Interface" which aim is to
+ * conceal differences in hardware between different models.
*/
#define HCI_WORDS 6
@@ -86,12 +89,23 @@ MODULE_LICENSE("GPL");
/* operations */
#define HCI_SET 0xff00
#define HCI_GET 0xfe00
+#define SCI_OPEN 0xf100
+#define SCI_CLOSE 0xf200
+#define SCI_GET 0xf300
+#define SCI_SET 0xf400
/* return codes */
#define HCI_SUCCESS 0x0000
#define HCI_FAILURE 0x1000
#define HCI_NOT_SUPPORTED 0x8000
#define HCI_EMPTY 0x8c00
+#define HCI_DATA_NOT_AVAILABLE 0x8d20
+#define HCI_NOT_INITIALIZED 0x8d50
+#define SCI_OPEN_CLOSE_OK 0x0044
+#define SCI_ALREADY_OPEN 0x8100
+#define SCI_NOT_OPENED 0x8200
+#define SCI_INPUT_DATA_ERROR 0x8300
+#define SCI_NOT_PRESENT 0x8600
/* registers */
#define HCI_FAN 0x0004
@@ -101,13 +115,22 @@ MODULE_LICENSE("GPL");
#define HCI_HOTKEY_EVENT 0x001e
#define HCI_LCD_BRIGHTNESS 0x002a
#define HCI_WIRELESS 0x0056
+#define HCI_ACCELEROMETER 0x006d
+#define HCI_KBD_ILLUMINATION 0x0095
+#define HCI_ECO_MODE 0x0097
+#define HCI_ACCELEROMETER2 0x00a6
+#define SCI_ILLUMINATION 0x014e
+#define SCI_KBD_ILLUM_STATUS 0x015c
+#define SCI_TOUCHPAD 0x050e
/* field definitions */
+#define HCI_ACCEL_MASK 0x7fff
#define HCI_HOTKEY_DISABLE 0x0b
#define HCI_HOTKEY_ENABLE 0x09
#define HCI_LCD_BRIGHTNESS_BITS 3
#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS)
#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS)
+#define HCI_MISC_SHIFT 0x10
#define HCI_VIDEO_OUT_LCD 0x1
#define HCI_VIDEO_OUT_CRT 0x2
#define HCI_VIDEO_OUT_TV 0x4
@@ -115,6 +138,8 @@ MODULE_LICENSE("GPL");
#define HCI_WIRELESS_BT_PRESENT 0x0f
#define HCI_WIRELESS_BT_ATTACH 0x40
#define HCI_WIRELESS_BT_POWER 0x80
+#define SCI_KBD_MODE_FNZ 0x1
+#define SCI_KBD_MODE_AUTO 0x2
struct toshiba_acpi_dev {
struct acpi_device *acpi_dev;
@@ -124,10 +149,14 @@ struct toshiba_acpi_dev {
struct work_struct hotkey_work;
struct backlight_device *backlight_dev;
struct led_classdev led_dev;
+ struct led_classdev kbd_led;
+ struct led_classdev eco_led;
int force_fan;
int last_key_event;
int key_event_valid;
+ int kbd_mode;
+ int kbd_time;
unsigned int illumination_supported:1;
unsigned int video_supported:1;
@@ -136,6 +165,12 @@ struct toshiba_acpi_dev {
unsigned int ntfy_supported:1;
unsigned int info_supported:1;
unsigned int tr_backlight_supported:1;
+ unsigned int kbd_illum_supported:1;
+ unsigned int kbd_led_registered:1;
+ unsigned int touchpad_supported:1;
+ unsigned int eco_supported:1;
+ unsigned int accelerometer_supported:1;
+ unsigned int sysfs_created:1;
struct mutex mutex;
};
@@ -150,7 +185,8 @@ static const struct acpi_device_id toshiba_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
-static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
+static const struct key_entry toshiba_acpi_keymap[] = {
+ { KE_KEY, 0x9e, { KEY_RFKILL } },
{ KE_KEY, 0x101, { KEY_MUTE } },
{ KE_KEY, 0x102, { KEY_ZOOMOUT } },
{ KE_KEY, 0x103, { KEY_ZOOMIN } },
@@ -178,6 +214,30 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = {
{ KE_END, 0 },
};
+/* alternative keymap */
+static const struct dmi_system_id toshiba_alt_keymap_dmi[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Satellite M840"),
+ },
+ },
+ {}
+};
+
+static const struct key_entry toshiba_acpi_alt_keymap[] = {
+ { KE_KEY, 0x157, { KEY_MUTE } },
+ { KE_KEY, 0x102, { KEY_ZOOMOUT } },
+ { KE_KEY, 0x103, { KEY_ZOOMIN } },
+ { KE_KEY, 0x139, { KEY_ZOOMRESET } },
+ { KE_KEY, 0x13e, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, 0x13c, { KEY_BRIGHTNESSDOWN } },
+ { KE_KEY, 0x13d, { KEY_BRIGHTNESSUP } },
+ { KE_KEY, 0x158, { KEY_WLAN } },
+ { KE_KEY, 0x13f, { KEY_TOUCHPAD_TOGGLE } },
+ { KE_END, 0 },
+};
+
/* utility
*/
@@ -191,16 +251,9 @@ static __inline__ void _set_bit(u32 * word, u32 mask, int value)
static int write_acpi_int(const char *methodName, int val)
{
- struct acpi_object_list params;
- union acpi_object in_objs[1];
acpi_status status;
- params.count = ARRAY_SIZE(in_objs);
- params.pointer = in_objs;
- in_objs[0].type = ACPI_TYPE_INTEGER;
- in_objs[0].integer.value = val;
-
- status = acpi_evaluate_object(NULL, (char *)methodName, &params, NULL);
+ status = acpi_execute_simple_method(NULL, (char *)methodName, val);
return (status == AE_OK) ? 0 : -EIO;
}
@@ -288,21 +341,94 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
return status;
}
+/* common sci tasks
+ */
+
+static int sci_open(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to open SCI failed\n");
+ return 0;
+ }
+
+ if (out[0] == SCI_OPEN_CLOSE_OK) {
+ return 1;
+ } else if (out[0] == SCI_ALREADY_OPEN) {
+ pr_info("Toshiba SCI already opened\n");
+ return 1;
+ } else if (out[0] == SCI_NOT_PRESENT) {
+ pr_info("Toshiba SCI is not present\n");
+ }
+
+ return 0;
+}
+
+static void sci_close(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to close SCI failed\n");
+ return;
+ }
+
+ if (out[0] == SCI_OPEN_CLOSE_OK)
+ return;
+ else if (out[0] == SCI_NOT_OPENED)
+ pr_info("Toshiba SCI not opened\n");
+ else if (out[0] == SCI_NOT_PRESENT)
+ pr_info("Toshiba SCI is not present\n");
+}
+
+static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 *out1, u32 *result)
+{
+ u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status = hci_raw(dev, in, out);
+ *out1 = out[2];
+ *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
+ return status;
+}
+
+static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg,
+ u32 in1, u32 *result)
+{
+ u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status = hci_raw(dev, in, out);
+ *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE;
+ return status;
+}
+
/* Illumination support */
static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
{
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+ u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
- in[0] = 0xf100;
+ if (!sci_open(dev))
+ return 0;
+
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) {
+ pr_err("ACPI call to query Illumination support failed\n");
+ return 0;
+ } else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) {
pr_info("Illumination device not available\n");
return 0;
}
- in[0] = 0xf400;
- status = hci_raw(dev, in, out);
+
return 1;
}
@@ -311,82 +437,270 @@ static void toshiba_illumination_set(struct led_classdev *cdev,
{
struct toshiba_acpi_dev *dev = container_of(cdev,
struct toshiba_acpi_dev, led_dev);
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
- u32 out[HCI_WORDS];
+ u32 state, result;
acpi_status status;
/* First request : initialize communication. */
- in[0] = 0xf100;
- status = hci_raw(dev, in, out);
+ if (!sci_open(dev))
+ return;
+
+ /* Switch the illumination on/off */
+ state = brightness ? 1 : 0;
+ status = sci_write(dev, SCI_ILLUMINATION, state, &result);
+ sci_close(dev);
if (ACPI_FAILURE(status)) {
- pr_info("Illumination device not available\n");
+ pr_err("ACPI call for illumination failed\n");
+ return;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Illumination not supported\n");
return;
}
+}
- if (brightness) {
- /* Switch the illumination on */
- in[0] = 0xf400;
- in[1] = 0x14e;
- in[2] = 1;
- status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed\n");
- return;
- }
- } else {
- /* Switch the illumination off */
- in[0] = 0xf400;
- in[1] = 0x14e;
- in[2] = 0;
- status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed.\n");
- return;
- }
+static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, led_dev);
+ u32 state, result;
+ acpi_status status;
+
+ /* First request : initialize communication. */
+ if (!sci_open(dev))
+ return LED_OFF;
+
+ /* Check the illumination */
+ status = sci_read(dev, SCI_ILLUMINATION, &state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call for illumination failed\n");
+ return LED_OFF;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Illumination not supported\n");
+ return LED_OFF;
}
- /* Last request : close communication. */
- in[0] = 0xf200;
- in[1] = 0;
- in[2] = 0;
- hci_raw(dev, in, out);
+ return state ? LED_FULL : LED_OFF;
}
-static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev)
+/* KBD Illumination */
+static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set KBD backlight status failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight status not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get KBD backlight status failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight status not supported\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev)
{
struct toshiba_acpi_dev *dev = container_of(cdev,
- struct toshiba_acpi_dev, led_dev);
- u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
+ struct toshiba_acpi_dev, kbd_led);
+ u32 state, result;
+ acpi_status status;
+
+ /* Check the keyboard backlight state */
+ status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get the keyboard backlight failed\n");
+ return LED_OFF;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight not supported\n");
+ return LED_OFF;
+ }
+
+ return state ? LED_FULL : LED_OFF;
+}
+
+static void toshiba_kbd_backlight_set(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, kbd_led);
+ u32 state, result;
+ acpi_status status;
+
+ /* Set the keyboard backlight state */
+ state = brightness ? 1 : 0;
+ status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result);
+ if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set KBD Illumination mode failed\n");
+ return;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ pr_info("Keyboard backlight not supported\n");
+ return;
+ }
+}
+
+/* TouchPad support */
+static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_write(dev, SCI_TOUCHPAD, state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status)) {
+ pr_err("ACPI call to set the touchpad failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state)
+{
+ u32 result;
+ acpi_status status;
+
+ if (!sci_open(dev))
+ return -EIO;
+
+ status = sci_read(dev, SCI_TOUCHPAD, state, &result);
+ sci_close(dev);
+ if (ACPI_FAILURE(status)) {
+ pr_err("ACPI call to query the touchpad failed\n");
+ return -EIO;
+ } else if (result == HCI_NOT_SUPPORTED) {
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* Eco Mode support */
+static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev)
+{
+ acpi_status status;
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_info("ACPI call to get ECO led failed\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, eco_led);
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 };
u32 out[HCI_WORDS];
acpi_status status;
- enum led_brightness result;
- /* First request : initialize communication. */
- in[0] = 0xf100;
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("Illumination device not available\n");
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to get ECO led failed\n");
return LED_OFF;
}
- /* Check the illumination */
- in[0] = 0xf300;
- in[1] = 0x14e;
+ return out[2] ? LED_FULL : LED_OFF;
+}
+
+static void toshiba_eco_mode_set_status(struct led_classdev *cdev,
+ enum led_brightness brightness)
+{
+ struct toshiba_acpi_dev *dev = container_of(cdev,
+ struct toshiba_acpi_dev, eco_led);
+ u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Switch the Eco Mode led on/off */
+ in[2] = (brightness) ? 1 : 0;
status = hci_raw(dev, in, out);
- if (ACPI_FAILURE(status)) {
- pr_info("ACPI call for illumination failed.\n");
- return LED_OFF;
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to set ECO led failed\n");
+ return;
}
+}
- result = out[2] ? LED_FULL : LED_OFF;
+/* Accelerometer support */
+static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev)
+{
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
- /* Last request : close communication. */
- in[0] = 0xf200;
- in[1] = 0;
- in[2] = 0;
- hci_raw(dev, in, out);
+ /* Check if the accelerometer call exists,
+ * this call also serves as initialization
+ */
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to query the accelerometer failed\n");
+ return -EIO;
+ } else if (out[0] == HCI_DATA_NOT_AVAILABLE ||
+ out[0] == HCI_NOT_INITIALIZED) {
+ pr_err("Accelerometer not initialized\n");
+ return -EIO;
+ } else if (out[0] == HCI_NOT_SUPPORTED) {
+ pr_info("Accelerometer not supported\n");
+ return -ENODEV;
+ }
- return result;
+ return 0;
+}
+
+static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev,
+ u32 *xy, u32 *z)
+{
+ u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 };
+ u32 out[HCI_WORDS];
+ acpi_status status;
+
+ /* Check the Accelerometer status */
+ status = hci_raw(dev, in, out);
+ if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
+ pr_err("ACPI call to query the accelerometer failed\n");
+ return -EIO;
+ }
+
+ *xy = out[2];
+ *z = out[4];
+
+ return 0;
}
/* Bluetooth rfkill handlers */
@@ -553,7 +867,7 @@ static int lcd_proc_show(struct seq_file *m, void *v)
static int lcd_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, lcd_proc_show, PDE(inode)->data);
+ return single_open(file, lcd_proc_show, PDE_DATA(inode));
}
static int set_lcd_brightness(struct toshiba_acpi_dev *dev, int value)
@@ -583,7 +897,7 @@ static int set_lcd_status(struct backlight_device *bd)
static ssize_t lcd_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
- struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
+ struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
char cmd[42];
size_t len;
int value;
@@ -644,13 +958,13 @@ static int video_proc_show(struct seq_file *m, void *v)
static int video_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, video_proc_show, PDE(inode)->data);
+ return single_open(file, video_proc_show, PDE_DATA(inode));
}
static ssize_t video_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
- struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
+ struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
char *cmd, *buffer;
int ret;
int value;
@@ -744,13 +1058,13 @@ static int fan_proc_show(struct seq_file *m, void *v)
static int fan_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, fan_proc_show, PDE(inode)->data);
+ return single_open(file, fan_proc_show, PDE_DATA(inode));
}
static ssize_t fan_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
- struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
+ struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
char cmd[42];
size_t len;
int value;
@@ -816,13 +1130,13 @@ static int keys_proc_show(struct seq_file *m, void *v)
static int keys_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, keys_proc_show, PDE(inode)->data);
+ return single_open(file, keys_proc_show, PDE_DATA(inode));
}
static ssize_t keys_proc_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
- struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data;
+ struct toshiba_acpi_dev *dev = PDE_DATA(file_inode(file));
char cmd[42];
size_t len;
int value;
@@ -859,7 +1173,7 @@ static int version_proc_show(struct seq_file *m, void *v)
static int version_proc_open(struct inode *inode, struct file *file)
{
- return single_open(file, version_proc_show, PDE(inode)->data);
+ return single_open(file, version_proc_show, PDE_DATA(inode));
}
static const struct file_operations version_proc_fops = {
@@ -875,8 +1189,7 @@ static const struct file_operations version_proc_fops = {
#define PROC_TOSHIBA "toshiba"
-static void __devinit
-create_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
+static void create_toshiba_proc_entries(struct toshiba_acpi_dev *dev)
{
if (dev->backlight_dev)
proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir,
@@ -913,6 +1226,177 @@ static const struct backlight_ops toshiba_backlight_data = {
.update_status = set_lcd_status,
};
+/*
+ * Sysfs files
+ */
+
+static ssize_t toshiba_kbd_bl_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int mode = -1;
+ int time = -1;
+
+ if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1))
+ return -EINVAL;
+
+ /* Set the Keyboard Backlight Mode where:
+ * Mode - Auto (2) | FN-Z (1)
+ * Auto - KBD backlight turns off automatically in given time
+ * FN-Z - KBD backlight "toggles" when hotkey pressed
+ */
+ if (mode != -1 && toshiba->kbd_mode != mode) {
+ time = toshiba->kbd_time << HCI_MISC_SHIFT;
+ time = time + toshiba->kbd_mode;
+ if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
+ return -EIO;
+ toshiba->kbd_mode = mode;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_kbd_bl_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 time;
+
+ if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
+ return -EIO;
+
+ return sprintf(buf, "%i\n", time & 0x07);
+}
+
+static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int time = -1;
+
+ if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60))
+ return -EINVAL;
+
+ /* Set the Keyboard Backlight Timeout: 0-60 seconds */
+ if (time != -1 && toshiba->kbd_time != time) {
+ time = time << HCI_MISC_SHIFT;
+ time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ?
+ time + 1 : time + 2;
+ if (toshiba_kbd_illum_status_set(toshiba, time) < 0)
+ return -EIO;
+ toshiba->kbd_time = time >> HCI_MISC_SHIFT;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 time;
+
+ if (toshiba_kbd_illum_status_get(toshiba, &time) < 0)
+ return -EIO;
+
+ return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT);
+}
+
+static ssize_t toshiba_touchpad_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ int state;
+
+ /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */
+ if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) {
+ if (toshiba_touchpad_set(toshiba, state) < 0)
+ return -EIO;
+ }
+
+ return count;
+}
+
+static ssize_t toshiba_touchpad_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 state;
+ int ret;
+
+ ret = toshiba_touchpad_get(toshiba, &state);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%i\n", state);
+}
+
+static ssize_t toshiba_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
+ u32 xyval, zval, tmp;
+ u16 x, y, z;
+ int ret;
+
+ xyval = zval = 0;
+ ret = toshiba_accelerometer_get(toshiba, &xyval, &zval);
+ if (ret < 0)
+ return ret;
+
+ x = xyval & HCI_ACCEL_MASK;
+ tmp = xyval >> HCI_MISC_SHIFT;
+ y = tmp & HCI_ACCEL_MASK;
+ z = zval & HCI_ACCEL_MASK;
+
+ return sprintf(buf, "%d %d %d\n", x, y, z);
+}
+
+static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR,
+ toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store);
+static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR,
+ toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store);
+static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR,
+ toshiba_touchpad_show, toshiba_touchpad_store);
+static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL);
+
+static struct attribute *toshiba_attributes[] = {
+ &dev_attr_kbd_backlight_mode.attr,
+ &dev_attr_kbd_backlight_timeout.attr,
+ &dev_attr_touchpad.attr,
+ &dev_attr_position.attr,
+ NULL,
+};
+
+static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
+ struct attribute *attr, int idx)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct toshiba_acpi_dev *drv = dev_get_drvdata(dev);
+ bool exists = true;
+
+ if (attr == &dev_attr_kbd_backlight_mode.attr)
+ exists = (drv->kbd_illum_supported) ? true : false;
+ else if (attr == &dev_attr_kbd_backlight_timeout.attr)
+ exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false;
+ else if (attr == &dev_attr_touchpad.attr)
+ exists = (drv->touchpad_supported) ? true : false;
+ else if (attr == &dev_attr_position.attr)
+ exists = (drv->accelerometer_supported) ? true : false;
+
+ return exists ? attr->mode : 0;
+}
+
+static struct attribute_group toshiba_attr_group = {
+ .is_visible = toshiba_sysfs_is_visible,
+ .attrs = toshiba_attributes,
+};
+
static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,
struct serio *port)
{
@@ -948,21 +1432,17 @@ static void toshiba_acpi_hotkey_work(struct work_struct *work)
*/
static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev)
{
- struct acpi_buffer buf;
- union acpi_object out_obj;
+ unsigned long long value;
acpi_status status;
- buf.pointer = &out_obj;
- buf.length = sizeof(out_obj);
-
- status = acpi_evaluate_object(dev->acpi_dev->handle, "INFO",
- NULL, &buf);
- if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) {
+ status = acpi_evaluate_integer(dev->acpi_dev->handle, "INFO",
+ NULL, &value);
+ if (ACPI_FAILURE(status)) {
pr_err("ACPI INFO method execution failed\n");
return -EIO;
}
- return out_obj.integer.value;
+ return value;
}
static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
@@ -979,24 +1459,25 @@ static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,
pr_info("Unknown key %x\n", scancode);
}
-static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
+static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
{
acpi_status status;
- acpi_handle ec_handle, handle;
+ acpi_handle ec_handle;
int error;
u32 hci_result;
+ const struct key_entry *keymap = toshiba_acpi_keymap;
dev->hotkey_dev = input_allocate_device();
- if (!dev->hotkey_dev) {
- pr_info("Unable to register input device\n");
+ if (!dev->hotkey_dev)
return -ENOMEM;
- }
dev->hotkey_dev->name = "Toshiba input device";
dev->hotkey_dev->phys = "toshiba_acpi/input0";
dev->hotkey_dev->id.bustype = BUS_HOST;
- error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL);
+ if (dmi_check_system(toshiba_alt_keymap_dmi))
+ keymap = toshiba_acpi_alt_keymap;
+ error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL);
if (error)
goto err_free_dev;
@@ -1009,10 +1490,7 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
*/
status = AE_ERROR;
ec_handle = ec_get_handle();
- if (ec_handle)
- status = acpi_get_handle(ec_handle, "NTFY", &handle);
-
- if (ACPI_SUCCESS(status)) {
+ if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {
INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);
error = i8042_install_filter(toshiba_acpi_i8042_filter);
@@ -1028,10 +1506,9 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
* Determine hotkey query interface. Prefer using the INFO
* method when it is available.
*/
- status = acpi_get_handle(dev->acpi_dev->handle, "INFO", &handle);
- if (ACPI_SUCCESS(status)) {
+ if (acpi_has_method(dev->acpi_dev->handle, "INFO"))
dev->info_supported = 1;
- } else {
+ else {
hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);
if (hci_result == HCI_SUCCESS)
dev->system_event_supported = 1;
@@ -1069,7 +1546,7 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
return error;
}
-static int __devinit toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
+static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
{
struct backlight_properties props;
int brightness;
@@ -1119,12 +1596,16 @@ static int __devinit toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)
return 0;
}
-static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
+static int toshiba_acpi_remove(struct acpi_device *acpi_dev)
{
struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
remove_toshiba_proc_entries(dev);
+ if (dev->sysfs_created)
+ sysfs_remove_group(&dev->acpi_dev->dev.kobj,
+ &toshiba_attr_group);
+
if (dev->ntfy_supported) {
i8042_remove_filter(toshiba_acpi_i8042_filter);
cancel_work_sync(&dev->hotkey_work);
@@ -1146,6 +1627,12 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
if (dev->illumination_supported)
led_classdev_unregister(&dev->led_dev);
+ if (dev->kbd_led_registered)
+ led_classdev_unregister(&dev->kbd_led);
+
+ if (dev->eco_supported)
+ led_classdev_unregister(&dev->eco_led);
+
if (toshiba_acpi)
toshiba_acpi = NULL;
@@ -1154,23 +1641,18 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)
return 0;
}
-static const char * __devinit find_hci_method(acpi_handle handle)
+static const char *find_hci_method(acpi_handle handle)
{
- acpi_status status;
- acpi_handle hci_handle;
-
- status = acpi_get_handle(handle, "GHCI", &hci_handle);
- if (ACPI_SUCCESS(status))
+ if (acpi_has_method(handle, "GHCI"))
return "GHCI";
- status = acpi_get_handle(handle, "SPFC", &hci_handle);
- if (ACPI_SUCCESS(status))
+ if (acpi_has_method(handle, "SPFC"))
return "SPFC";
return NULL;
}
-static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
+static int toshiba_acpi_add(struct acpi_device *acpi_dev)
{
struct toshiba_acpi_dev *dev;
const char *hci_method;
@@ -1196,6 +1678,7 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->acpi_dev = acpi_dev;
dev->method_hci = hci_method;
acpi_dev->driver_data = dev;
+ dev_set_drvdata(&acpi_dev->dev, dev);
if (toshiba_acpi_setup_keyboard(dev))
pr_info("Unable to activate hotkeys\n");
@@ -1236,6 +1719,40 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
dev->illumination_supported = 1;
}
+ if (toshiba_eco_mode_available(dev)) {
+ dev->eco_led.name = "toshiba::eco_mode";
+ dev->eco_led.max_brightness = 1;
+ dev->eco_led.brightness_set = toshiba_eco_mode_set_status;
+ dev->eco_led.brightness_get = toshiba_eco_mode_get_status;
+ if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led))
+ dev->eco_supported = 1;
+ }
+
+ ret = toshiba_kbd_illum_status_get(dev, &dummy);
+ if (!ret) {
+ dev->kbd_time = dummy >> HCI_MISC_SHIFT;
+ dev->kbd_mode = dummy & 0x07;
+ }
+ dev->kbd_illum_supported = !ret;
+ /*
+ * Only register the LED if KBD illumination is supported
+ * and the keyboard backlight operation mode is set to FN-Z
+ */
+ if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) {
+ dev->kbd_led.name = "toshiba::kbd_backlight";
+ dev->kbd_led.max_brightness = 1;
+ dev->kbd_led.brightness_set = toshiba_kbd_backlight_set;
+ dev->kbd_led.brightness_get = toshiba_kbd_backlight_get;
+ if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led))
+ dev->kbd_led_registered = 1;
+ }
+
+ ret = toshiba_touchpad_get(dev, &dummy);
+ dev->touchpad_supported = !ret;
+
+ ret = toshiba_accelerometer_supported(dev);
+ dev->accelerometer_supported = !ret;
+
/* Determine whether or not BIOS supports fan and video interfaces */
ret = get_video_status(dev, &dummy);
@@ -1244,6 +1761,14 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
ret = get_fan_status(dev, &dummy);
dev->fan_supported = !ret;
+ ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
+ &toshiba_attr_group);
+ if (ret) {
+ dev->sysfs_created = 0;
+ goto error;
+ }
+ dev->sysfs_created = !ret;
+
create_toshiba_proc_entries(dev);
toshiba_acpi = dev;
@@ -1251,7 +1776,7 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)
return 0;
error:
- toshiba_acpi_remove(acpi_dev, 0);
+ toshiba_acpi_remove(acpi_dev);
return ret;
}
@@ -1296,10 +1821,10 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event)
}
}
-static int toshiba_acpi_suspend(struct acpi_device *acpi_dev,
- pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int toshiba_acpi_suspend(struct device *device)
{
- struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
+ struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
u32 result;
if (dev->hotkey_dev)
@@ -1308,9 +1833,9 @@ static int toshiba_acpi_suspend(struct acpi_device *acpi_dev,
return 0;
}
-static int toshiba_acpi_resume(struct acpi_device *acpi_dev)
+static int toshiba_acpi_resume(struct device *device)
{
- struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev);
+ struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
u32 result;
if (dev->hotkey_dev)
@@ -1318,6 +1843,10 @@ static int toshiba_acpi_resume(struct acpi_device *acpi_dev)
return 0;
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm,
+ toshiba_acpi_suspend, toshiba_acpi_resume);
static struct acpi_driver toshiba_acpi_driver = {
.name = "Toshiba ACPI driver",
@@ -1328,9 +1857,8 @@ static struct acpi_driver toshiba_acpi_driver = {
.add = toshiba_acpi_add,
.remove = toshiba_acpi_remove,
.notify = toshiba_acpi_notify,
- .suspend = toshiba_acpi_suspend,
- .resume = toshiba_acpi_resume,
},
+ .drv.pm = &toshiba_acpi_pm,
};
static int __init toshiba_acpi_init(void)
diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c
index 5fb7186694d..2cb1ea62b4a 100644
--- a/drivers/platform/x86/toshiba_bluetooth.c
+++ b/drivers/platform/x86/toshiba_bluetooth.c
@@ -23,18 +23,15 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
+#include <linux/acpi.h>
MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>");
MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver");
MODULE_LICENSE("GPL");
-
static int toshiba_bt_rfkill_add(struct acpi_device *device);
-static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type);
+static int toshiba_bt_rfkill_remove(struct acpi_device *device);
static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event);
-static int toshiba_bt_resume(struct acpi_device *device);
static const struct acpi_device_id bt_device_ids[] = {
{ "TOS6205", 0},
@@ -42,6 +39,11 @@ static const struct acpi_device_id bt_device_ids[] = {
};
MODULE_DEVICE_TABLE(acpi, bt_device_ids);
+#ifdef CONFIG_PM_SLEEP
+static int toshiba_bt_resume(struct device *dev);
+#endif
+static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume);
+
static struct acpi_driver toshiba_bt_rfkill_driver = {
.name = "Toshiba BT",
.class = "Toshiba",
@@ -50,9 +52,9 @@ static struct acpi_driver toshiba_bt_rfkill_driver = {
.add = toshiba_bt_rfkill_add,
.remove = toshiba_bt_rfkill_remove,
.notify = toshiba_bt_rfkill_notify,
- .resume = toshiba_bt_resume,
},
.owner = THIS_MODULE,
+ .drv.pm = &toshiba_bt_pm,
};
@@ -88,10 +90,12 @@ static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event)
toshiba_bluetooth_enable(device->handle);
}
-static int toshiba_bt_resume(struct acpi_device *device)
+#ifdef CONFIG_PM_SLEEP
+static int toshiba_bt_resume(struct device *dev)
{
- return toshiba_bluetooth_enable(device->handle);
+ return toshiba_bluetooth_enable(to_acpi_device(dev)->handle);
}
+#endif
static int toshiba_bt_rfkill_add(struct acpi_device *device)
{
@@ -116,30 +120,10 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device)
return result;
}
-static int __init toshiba_bt_rfkill_init(void)
-{
- int result;
-
- result = acpi_bus_register_driver(&toshiba_bt_rfkill_driver);
- if (result < 0) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
- "Error registering driver\n"));
- return result;
- }
-
- return 0;
-}
-
-static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type)
+static int toshiba_bt_rfkill_remove(struct acpi_device *device)
{
/* clean up */
return 0;
}
-static void __exit toshiba_bt_rfkill_exit(void)
-{
- acpi_bus_unregister_driver(&toshiba_bt_rfkill_driver);
-}
-
-module_init(toshiba_bt_rfkill_init);
-module_exit(toshiba_bt_rfkill_exit);
+module_acpi_driver(toshiba_bt_rfkill_driver);
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
index 42a4dcc25f9..43d13295e63 100644
--- a/drivers/platform/x86/wmi.c
+++ b/drivers/platform/x86/wmi.c
@@ -37,8 +37,6 @@
#include <linux/acpi.h>
#include <linux/slab.h>
#include <linux/module.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
ACPI_MODULE_NAME("wmi");
MODULE_AUTHOR("Carlos Corbacho");
@@ -92,7 +90,7 @@ module_param(debug_dump_wdg, bool, 0444);
MODULE_PARM_DESC(debug_dump_wdg,
"Dump available WMI interfaces [0/1]");
-static int acpi_wmi_remove(struct acpi_device *device, int type);
+static int acpi_wmi_remove(struct acpi_device *device);
static int acpi_wmi_add(struct acpi_device *device);
static void acpi_wmi_notify(struct acpi_device *device, u32 event);
@@ -252,8 +250,6 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
{
struct guid_block *block = NULL;
char method[5];
- struct acpi_object_list input;
- union acpi_object params[1];
acpi_status status;
acpi_handle handle;
@@ -263,13 +259,9 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)
if (!block)
return AE_NOT_EXIST;
- input.count = 1;
- input.pointer = params;
- params[0].type = ACPI_TYPE_INTEGER;
- params[0].integer.value = enable;
snprintf(method, 5, "WE%02X", block->notify_id);
- status = acpi_evaluate_object(handle, method, &input, NULL);
+ status = acpi_execute_simple_method(handle, method, enable);
if (status != AE_OK && status != AE_NOT_FOUND)
return status;
@@ -353,10 +345,10 @@ struct acpi_buffer *out)
{
struct guid_block *block = NULL;
struct wmi_block *wblock = NULL;
- acpi_handle handle, wc_handle;
+ acpi_handle handle;
acpi_status status, wc_status = AE_ERROR;
- struct acpi_object_list input, wc_input;
- union acpi_object wc_params[1], wq_params[1];
+ struct acpi_object_list input;
+ union acpi_object wq_params[1];
char method[5];
char wc_method[5] = "WC";
@@ -386,11 +378,6 @@ struct acpi_buffer *out)
* enable collection.
*/
if (block->flags & ACPI_WMI_EXPENSIVE) {
- wc_input.count = 1;
- wc_input.pointer = wc_params;
- wc_params[0].type = ACPI_TYPE_INTEGER;
- wc_params[0].integer.value = 1;
-
strncat(wc_method, block->object_id, 2);
/*
@@ -398,10 +385,9 @@ struct acpi_buffer *out)
* expensive, but have no corresponding WCxx method. So we
* should not fail if this happens.
*/
- wc_status = acpi_get_handle(handle, wc_method, &wc_handle);
- if (ACPI_SUCCESS(wc_status))
- wc_status = acpi_evaluate_object(handle, wc_method,
- &wc_input, NULL);
+ if (acpi_has_method(handle, wc_method))
+ wc_status = acpi_execute_simple_method(handle,
+ wc_method, 1);
}
strcpy(method, "WQ");
@@ -414,9 +400,7 @@ struct acpi_buffer *out)
* the WQxx method failed - we should disable collection anyway.
*/
if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) {
- wc_params[0].integer.value = 0;
- status = acpi_evaluate_object(handle,
- wc_method, &wc_input, NULL);
+ status = acpi_execute_simple_method(handle, wc_method, 0);
}
return status;
@@ -686,18 +670,22 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
struct wmi_block *wblock;
wblock = dev_get_drvdata(dev);
- if (!wblock)
- return -ENOMEM;
+ if (!wblock) {
+ strcat(buf, "\n");
+ return strlen(buf);
+ }
wmi_gtoa(wblock->gblock.guid, guid_string);
return sprintf(buf, "wmi:%s\n", guid_string);
}
+static DEVICE_ATTR_RO(modalias);
-static struct device_attribute wmi_dev_attrs[] = {
- __ATTR_RO(modalias),
- __ATTR_NULL
+static struct attribute *wmi_attrs[] = {
+ &dev_attr_modalias.attr,
+ NULL,
};
+ATTRIBUTE_GROUPS(wmi);
static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
{
@@ -732,7 +720,7 @@ static struct class wmi_class = {
.name = "wmi",
.dev_release = wmi_dev_free,
.dev_uevent = wmi_dev_uevent,
- .dev_attrs = wmi_dev_attrs,
+ .dev_groups = wmi_groups,
};
static int wmi_create_device(const struct guid_block *gblock,
@@ -743,7 +731,7 @@ static int wmi_create_device(const struct guid_block *gblock,
wblock->dev.class = &wmi_class;
wmi_gtoa(gblock->guid, guid_string);
- dev_set_name(&wblock->dev, guid_string);
+ dev_set_name(&wblock->dev, "%s", guid_string);
dev_set_drvdata(&wblock->dev, wblock);
@@ -778,7 +766,7 @@ static bool guid_already_parsed(const char *guid_string)
/*
* Parse the _WDG method for the GUID data blocks
*/
-static acpi_status parse_wdg(acpi_handle handle)
+static int parse_wdg(acpi_handle handle)
{
struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj;
@@ -810,7 +798,7 @@ static acpi_status parse_wdg(acpi_handle handle)
wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
if (!wblock)
- return AE_NO_MEMORY;
+ return -ENOMEM;
wblock->handle = handle;
wblock->gblock = gblock[i];
@@ -917,7 +905,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)
}
}
-static int acpi_wmi_remove(struct acpi_device *device, int type)
+static int acpi_wmi_remove(struct acpi_device *device)
{
acpi_remove_address_space_handler(device->handle,
ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler);
diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c
index b57ad864148..4bd17248dfc 100644
--- a/drivers/platform/x86/xo1-rfkill.c
+++ b/drivers/platform/x86/xo1-rfkill.c
@@ -12,8 +12,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rfkill.h>
-
-#include <asm/olpc.h>
+#include <linux/olpc-ec.h>
static bool card_blocked;
@@ -41,7 +40,7 @@ static const struct rfkill_ops rfkill_ops = {
.set_block = rfkill_set_block,
};
-static int __devinit xo1_rfkill_probe(struct platform_device *pdev)
+static int xo1_rfkill_probe(struct platform_device *pdev)
{
struct rfkill *rfk;
int r;
@@ -61,7 +60,7 @@ static int __devinit xo1_rfkill_probe(struct platform_device *pdev)
return 0;
}
-static int __devexit xo1_rfkill_remove(struct platform_device *pdev)
+static int xo1_rfkill_remove(struct platform_device *pdev)
{
struct rfkill *rfk = platform_get_drvdata(pdev);
rfkill_unregister(rfk);
@@ -75,7 +74,7 @@ static struct platform_driver xo1_rfkill_driver = {
.owner = THIS_MODULE,
},
.probe = xo1_rfkill_probe,
- .remove = __devexit_p(xo1_rfkill_remove),
+ .remove = xo1_rfkill_remove,
};
module_platform_driver(xo1_rfkill_driver);
diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c
index fad153dc035..49cbccec6e2 100644
--- a/drivers/platform/x86/xo15-ebook.c
+++ b/drivers/platform/x86/xo15-ebook.c
@@ -18,8 +18,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/input.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
+#include <linux/acpi.h>
#define MODULE_NAME "xo15-ebook"
@@ -77,10 +76,14 @@ static void ebook_switch_notify(struct acpi_device *device, u32 event)
}
}
-static int ebook_switch_resume(struct acpi_device *device)
+#ifdef CONFIG_PM_SLEEP
+static int ebook_switch_resume(struct device *dev)
{
- return ebook_send_state(device);
+ return ebook_send_state(to_acpi_device(dev));
}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume);
static int ebook_switch_add(struct acpi_device *device)
{
@@ -146,7 +149,7 @@ static int ebook_switch_add(struct acpi_device *device)
return error;
}
-static int ebook_switch_remove(struct acpi_device *device, int type)
+static int ebook_switch_remove(struct acpi_device *device)
{
struct ebook_switch *button = acpi_driver_data(device);
@@ -161,21 +164,9 @@ static struct acpi_driver xo15_ebook_driver = {
.ids = ebook_device_ids,
.ops = {
.add = ebook_switch_add,
- .resume = ebook_switch_resume,
.remove = ebook_switch_remove,
.notify = ebook_switch_notify,
},
+ .drv.pm = &ebook_switch_pm,
};
-
-static int __init xo15_ebook_init(void)
-{
- return acpi_bus_register_driver(&xo15_ebook_driver);
-}
-
-static void __exit xo15_ebook_exit(void)
-{
- acpi_bus_unregister_driver(&xo15_ebook_driver);
-}
-
-module_init(xo15_ebook_init);
-module_exit(xo15_ebook_exit);
+module_acpi_driver(xo15_ebook_driver);