mirror of https://gitee.com/openkylin/linux.git
platform-drivers-x86 for 4.6-1
Significant refactoring of Dell laptop drivers, modularizing the smbios code. Multiple new platforms added for ideapad, asus, dell, and alienware using existing quirks. A few fixes and cleanups. hp-wmi: - Remove GPS rfkill support via pre-2009 interface - fix unregister order in hp_wmi_rfkill_setup() once again ideapad-laptop: - Add ideapad Y700 (15) to the no_hw_rfkill DMI list fujitsu-laptop: - Support radio toggle button intel-hid: - allocate correct amount of memory for private struct platform/x86: - Make intel_scu_ipc explicitly non-modular intel_pmc_ipc: - Avoid pending IPC1 command during legacy suspend - Fix GCR register base address and length asus-nb-wmi: - add wapf=4 quirk for ASUS X75VD intel_telemetry_pltdrv: - Change verbosity control bits dell-rbtn: - Add a comment about the XPS 13 9350 dell-wmi, dell-laptop: - depends DMI dell-wmi: - support Dell Inspiron M5110 - properly process Dell Instant Launch hotkey - enable receiving WMI events on Dell Vostro V131 - Support new hotkeys on the XPS 13 9350 (Skylake) - Clean up hotkey table size check - Stop storing pointers to DMI tables dell-laptop: - move dell_smi_error() to dell-smbios - use dell_smbios_find_token() instead of find_token_location() - use dell_smbios_find_token() instead of find_token_id() - extract SMBIOS-related code to a separate module dell-smbios: - rename dell_smi_error() to dell_smbios_error() - make da_tokens static - remove find_token_{id,location}() - implement new function for finding DMI table 0xDA tokens - make the SMBIOS buffer static - return the SMBIOS buffer from dell_smbios_get_buffer() - don't return an SMBIOS buffer from dell_smbios_send_request() - don't pass an SMBIOS buffer to dell_smbios_send_request() - rename dell_send_request() to dell_smbios_send_request() - rename release_buffer() to dell_smbios_release_buffer() - rename clear_buffer() to dell_smbios_clear_buffer() - rename get_buffer() to dell_smbios_get_buffer() dell-led: - use dell_smbios_send_request() for performing SMBIOS calls - use dell_smbios_find_token() for finding mic DMI tokens toshiba_acpi: - Add a module parameter to disable hotkeys registration - Add sysfs entries for the Cooling Method feature - Add support for cooling method feature Documentation/ABI: - Update sysfs-driver-toshiba_acpi file thinkpad_acpi: - Remove ambiguous logging for "Unsupported brightness interface" alienware-wmi: - whitespace improvements - Add support for two new systems: ASM200 and ASM201. - Add support for deep sleep control. - Add initial support for alienware graphics amplifier. - Add support for new platform: X51-R3 - Clean up whitespace for ASM100 platform -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQEcBAABAgAGBQJW8s77AAoJEKbMaAwKp364iVgH/18Tpefbc7uOrNak4zfTmvVO CQjLdbnbCYoeNjwgdYSZNlaR+E2TuMdqmBXXLEr3kqicpmX15l+V5y8xJkDmGpxw 0RmwJnkdIpmkZcjb3/2abOtIuOj1Y/tXCp5mY1FoaDYBQVzO+ZsPEGNQcMu5uJpa qZTw9UwiSPUspTrHHbS7/Bzv7yFnxlWC1nmt6G5zL4OMEv2AGbuVSstXVGySIML5 TfFdisUBIXQplzwpDytHGqtGUJMc0NnT1s66beuo1oBdEFTFtBNpFySW3goV2nrZ JPbvyaV3tD24NYNmU9WH653InX4KuaGiJRWVT31a/I5HVVlx7mZdUYRI+EYAnmE= =e0E4 -----END PGP SIGNATURE----- Merge tag 'platform-drivers-x86-v4.6-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86 Pull x86 platform driver updates from Darren Hart: "Significant refactoring of Dell laptop drivers, modularizing the smbios code. Multiple new platforms added for ideapad, asus, dell, and alienware using existing quirks. A few fixes and cleanups. hp-wmi: - Remove GPS rfkill support via pre-2009 interface - fix unregister order in hp_wmi_rfkill_setup() once again ideapad-laptop: - Add ideapad Y700 (15) to the no_hw_rfkill DMI list fujitsu-laptop: - Support radio toggle button intel-hid: - allocate correct amount of memory for private struct platform/x86: - Make intel_scu_ipc explicitly non-modular intel_pmc_ipc: - Avoid pending IPC1 command during legacy suspend - Fix GCR register base address and length asus-nb-wmi: - add wapf=4 quirk for ASUS X75VD intel_telemetry_pltdrv: - Change verbosity control bits dell-rbtn: - Add a comment about the XPS 13 9350 dell-wmi, dell-laptop: - depends DMI dell-wmi: - support Dell Inspiron M5110 - properly process Dell Instant Launch hotkey - enable receiving WMI events on Dell Vostro V131 - Support new hotkeys on the XPS 13 9350 (Skylake) - Clean up hotkey table size check - Stop storing pointers to DMI tables dell-laptop: - move dell_smi_error() to dell-smbios - use dell_smbios_find_token() instead of find_token_location() - use dell_smbios_find_token() instead of find_token_id() - extract SMBIOS-related code to a separate module dell-smbios: - rename dell_smi_error() to dell_smbios_error() - make da_tokens static - remove find_token_{id,location}() - implement new function for finding DMI table 0xDA tokens - make the SMBIOS buffer static - return the SMBIOS buffer from dell_smbios_get_buffer() - don't return an SMBIOS buffer from dell_smbios_send_request() - don't pass an SMBIOS buffer to dell_smbios_send_request() - rename dell_send_request() to dell_smbios_send_request() - rename release_buffer() to dell_smbios_release_buffer() - rename clear_buffer() to dell_smbios_clear_buffer() - rename get_buffer() to dell_smbios_get_buffer() dell-led: - use dell_smbios_send_request() for performing SMBIOS calls - use dell_smbios_find_token() for finding mic DMI tokens toshiba_acpi: - Add a module parameter to disable hotkeys registration - Add sysfs entries for the Cooling Method feature - Add support for cooling method feature Documentation/ABI: - Update sysfs-driver-toshiba_acpi file thinkpad_acpi: - Remove ambiguous logging for "Unsupported brightness interface" alienware-wmi: - whitespace improvements - Add support for two new systems: ASM200 and ASM201. - Add support for deep sleep control. - Add initial support for alienware graphics amplifier. - Add support for new platform: X51-R3 - Clean up whitespace for ASM100 platform" * tag 'platform-drivers-x86-v4.6-1' of git://git.infradead.org/users/dvhart/linux-platform-drivers-x86: (47 commits) hp-wmi: Remove GPS rfkill support via pre-2009 interface hp-wmi: fix unregister order in hp_wmi_rfkill_setup() once again dell-wmi: support Dell Inspiron M5110 dell-wmi: properly process Dell Instant Launch hotkey dell-wmi: enable receiving WMI events on Dell Vostro V131 dell-smbios: rename dell_smi_error() to dell_smbios_error() dell-laptop: move dell_smi_error() to dell-smbios ideapad-laptop: Add ideapad Y700 (15) to the no_hw_rfkill DMI list fujitsu-laptop: Support radio toggle button intel-hid: allocate correct amount of memory for private struct platform/x86: Make intel_scu_ipc explicitly non-modular intel_pmc_ipc: Avoid pending IPC1 command during legacy suspend intel_pmc_ipc: Fix GCR register base address and length asus-nb-wmi: add wapf=4 quirk for ASUS X75VD intel_telemetry_pltdrv: Change verbosity control bits dell-rbtn: Add a comment about the XPS 13 9350 dell-wmi: Support new hotkeys on the XPS 13 9350 (Skylake) dell-wmi: Clean up hotkey table size check dell-wmi, dell-laptop: depends DMI dell-wmi: Stop storing pointers to DMI tables ...
This commit is contained in:
commit
5a010c73cd
|
@ -179,3 +179,19 @@ Description: This file controls the USB 3 functionality, valid values are:
|
|||
Note that toggling this value requires a reboot for changes to
|
||||
take effect.
|
||||
Users: KToshiba
|
||||
|
||||
What: /sys/devices/LNXSYSTM:00/LNXSYBUS:00/TOS{1900,620{0,7,8}}:00/cooling_method
|
||||
Date: 2016
|
||||
KernelVersion: 4.6
|
||||
Contact: Azael Avalos <coproscefalo@gmail.com>
|
||||
Description: This file controls the Cooling Method feature.
|
||||
Reading this file prints two values, the first is the actual cooling method
|
||||
and the second is the maximum cooling method supported.
|
||||
When the maximum cooling method is ONE, valid values are:
|
||||
* 0 -> Maximum Performance
|
||||
* 1 -> Battery Optimized
|
||||
When the maximum cooling method is TWO, valid values are:
|
||||
* 0 -> Maximum Performance
|
||||
* 1 -> Performance
|
||||
* 2 -> Battery Optimized
|
||||
Users: KToshiba
|
||||
|
|
|
@ -443,6 +443,7 @@ config LEDS_DELL_NETBOOKS
|
|||
tristate "External LED on Dell Business Netbooks"
|
||||
depends on LEDS_CLASS
|
||||
depends on X86 && ACPI_WMI
|
||||
depends on DELL_SMBIOS
|
||||
help
|
||||
This adds support for the Latitude 2100 and similar
|
||||
notebooks that have an external LED.
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/dell-led.h>
|
||||
#include "../platform/x86/dell-smbios.h"
|
||||
|
||||
MODULE_AUTHOR("Louis Davis/Jim Dailey");
|
||||
MODULE_DESCRIPTION("Dell LED Control Driver");
|
||||
|
@ -42,120 +43,32 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
|
|||
#define CMD_LED_OFF 17
|
||||
#define CMD_LED_BLINK 18
|
||||
|
||||
struct app_wmi_args {
|
||||
u16 class;
|
||||
u16 selector;
|
||||
u32 arg1;
|
||||
u32 arg2;
|
||||
u32 arg3;
|
||||
u32 arg4;
|
||||
u32 res1;
|
||||
u32 res2;
|
||||
u32 res3;
|
||||
u32 res4;
|
||||
char dummy[92];
|
||||
};
|
||||
|
||||
#define GLOBAL_MIC_MUTE_ENABLE 0x364
|
||||
#define GLOBAL_MIC_MUTE_DISABLE 0x365
|
||||
|
||||
struct dell_bios_data_token {
|
||||
u16 tokenid;
|
||||
u16 location;
|
||||
u16 value;
|
||||
};
|
||||
|
||||
struct __attribute__ ((__packed__)) dell_bios_calling_interface {
|
||||
struct dmi_header header;
|
||||
u16 cmd_io_addr;
|
||||
u8 cmd_io_code;
|
||||
u32 supported_cmds;
|
||||
struct dell_bios_data_token damap[];
|
||||
};
|
||||
|
||||
static struct dell_bios_data_token dell_mic_tokens[2];
|
||||
|
||||
static int dell_wmi_perform_query(struct app_wmi_args *args)
|
||||
{
|
||||
struct app_wmi_args *bios_return;
|
||||
union acpi_object *obj;
|
||||
struct acpi_buffer input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
acpi_status status;
|
||||
u32 rc = -EINVAL;
|
||||
|
||||
input.length = 128;
|
||||
input.pointer = args;
|
||||
|
||||
status = wmi_evaluate_method(DELL_APP_GUID, 0, 1, &input, &output);
|
||||
if (!ACPI_SUCCESS(status))
|
||||
goto err_out0;
|
||||
|
||||
obj = output.pointer;
|
||||
if (!obj)
|
||||
goto err_out0;
|
||||
|
||||
if (obj->type != ACPI_TYPE_BUFFER)
|
||||
goto err_out1;
|
||||
|
||||
bios_return = (struct app_wmi_args *)obj->buffer.pointer;
|
||||
rc = bios_return->res1;
|
||||
if (rc)
|
||||
goto err_out1;
|
||||
|
||||
memcpy(args, bios_return, sizeof(struct app_wmi_args));
|
||||
rc = 0;
|
||||
|
||||
err_out1:
|
||||
kfree(obj);
|
||||
err_out0:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __init find_micmute_tokens(const struct dmi_header *dm, void *dummy)
|
||||
{
|
||||
struct dell_bios_calling_interface *calling_interface;
|
||||
struct dell_bios_data_token *token;
|
||||
int token_size = sizeof(struct dell_bios_data_token);
|
||||
int i = 0;
|
||||
|
||||
if (dm->type == 0xda && dm->length > 17) {
|
||||
calling_interface = container_of(dm,
|
||||
struct dell_bios_calling_interface, header);
|
||||
|
||||
token = &calling_interface->damap[i];
|
||||
while (token->tokenid != 0xffff) {
|
||||
if (token->tokenid == GLOBAL_MIC_MUTE_DISABLE)
|
||||
memcpy(&dell_mic_tokens[0], token, token_size);
|
||||
else if (token->tokenid == GLOBAL_MIC_MUTE_ENABLE)
|
||||
memcpy(&dell_mic_tokens[1], token, token_size);
|
||||
|
||||
i++;
|
||||
token = &calling_interface->damap[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int dell_micmute_led_set(int state)
|
||||
{
|
||||
struct app_wmi_args args;
|
||||
struct dell_bios_data_token *token;
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
|
||||
if (!wmi_has_guid(DELL_APP_GUID))
|
||||
return -ENODEV;
|
||||
|
||||
if (state == 0 || state == 1)
|
||||
token = &dell_mic_tokens[state];
|
||||
if (state == 0)
|
||||
token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE);
|
||||
else if (state == 1)
|
||||
token = dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
memset(&args, 0, sizeof(struct app_wmi_args));
|
||||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
args.class = 1;
|
||||
args.arg1 = token->location;
|
||||
args.arg2 = token->value;
|
||||
|
||||
dell_wmi_perform_query(&args);
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
buffer->input[1] = token->value;
|
||||
dell_smbios_send_request(1, 0);
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
return state;
|
||||
}
|
||||
|
@ -177,14 +90,6 @@ int dell_app_wmi_led_set(int whichled, int on)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(dell_app_wmi_led_set);
|
||||
|
||||
static int __init dell_micmute_led_init(void)
|
||||
{
|
||||
memset(dell_mic_tokens, 0, sizeof(struct dell_bios_data_token) * 2);
|
||||
dmi_walk(find_micmute_tokens, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bios_args {
|
||||
unsigned char length;
|
||||
unsigned char result_code;
|
||||
|
@ -330,9 +235,6 @@ static int __init dell_led_init(void)
|
|||
if (!wmi_has_guid(DELL_LED_BIOS_GUID) && !wmi_has_guid(DELL_APP_GUID))
|
||||
return -ENODEV;
|
||||
|
||||
if (wmi_has_guid(DELL_APP_GUID))
|
||||
error = dell_micmute_led_init();
|
||||
|
||||
if (wmi_has_guid(DELL_LED_BIOS_GUID)) {
|
||||
error = led_off();
|
||||
if (error != 0)
|
||||
|
|
|
@ -91,10 +91,21 @@ config ASUS_LAPTOP
|
|||
|
||||
If you have an ACPI-compatible ASUS laptop, say Y or M here.
|
||||
|
||||
config DELL_SMBIOS
|
||||
tristate "Dell SMBIOS Support"
|
||||
depends on DCDBAS
|
||||
default n
|
||||
---help---
|
||||
This module provides common functions for kernel modules using
|
||||
Dell SMBIOS.
|
||||
|
||||
If you have a Dell laptop, say Y or M here.
|
||||
|
||||
config DELL_LAPTOP
|
||||
tristate "Dell Laptop Extras"
|
||||
depends on X86
|
||||
depends on DCDBAS
|
||||
depends on DELL_SMBIOS
|
||||
depends on DMI
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
||||
depends on RFKILL || RFKILL = n
|
||||
|
@ -110,8 +121,10 @@ config DELL_LAPTOP
|
|||
config DELL_WMI
|
||||
tristate "Dell WMI extras"
|
||||
depends on ACPI_WMI
|
||||
depends on DMI
|
||||
depends on INPUT
|
||||
depends on ACPI_VIDEO || ACPI_VIDEO = n
|
||||
depends on DELL_SMBIOS
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
Say Y here if you want to support WMI-based hotkeys on Dell laptops.
|
||||
|
|
|
@ -11,6 +11,7 @@ obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
|
|||
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
|
||||
obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o
|
||||
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
|
||||
obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
|
||||
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
|
||||
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
|
||||
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
#define WMAX_METHOD_BRIGHTNESS 0x3
|
||||
#define WMAX_METHOD_ZONE_CONTROL 0x4
|
||||
#define WMAX_METHOD_HDMI_CABLE 0x5
|
||||
#define WMAX_METHOD_AMPLIFIER_CABLE 0x6
|
||||
#define WMAX_METHOD_DEEP_SLEEP_CONTROL 0x0B
|
||||
#define WMAX_METHOD_DEEP_SLEEP_STATUS 0x0C
|
||||
|
||||
MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>");
|
||||
MODULE_DESCRIPTION("Alienware special feature control");
|
||||
|
@ -60,6 +63,8 @@ enum WMAX_CONTROL_STATES {
|
|||
struct quirk_entry {
|
||||
u8 num_zones;
|
||||
u8 hdmi_mux;
|
||||
u8 amplifier;
|
||||
u8 deepslp;
|
||||
};
|
||||
|
||||
static struct quirk_entry *quirks;
|
||||
|
@ -67,16 +72,43 @@ static struct quirk_entry *quirks;
|
|||
static struct quirk_entry quirk_unknown = {
|
||||
.num_zones = 2,
|
||||
.hdmi_mux = 0,
|
||||
.amplifier = 0,
|
||||
.deepslp = 0,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_x51_family = {
|
||||
static struct quirk_entry quirk_x51_r1_r2 = {
|
||||
.num_zones = 3,
|
||||
.hdmi_mux = 0.
|
||||
.hdmi_mux = 0,
|
||||
.amplifier = 0,
|
||||
.deepslp = 0,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_x51_r3 = {
|
||||
.num_zones = 4,
|
||||
.hdmi_mux = 0,
|
||||
.amplifier = 1,
|
||||
.deepslp = 0,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_asm100 = {
|
||||
.num_zones = 2,
|
||||
.hdmi_mux = 1,
|
||||
.amplifier = 0,
|
||||
.deepslp = 0,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_asm200 = {
|
||||
.num_zones = 2,
|
||||
.hdmi_mux = 1,
|
||||
.amplifier = 0,
|
||||
.deepslp = 1,
|
||||
};
|
||||
|
||||
static struct quirk_entry quirk_asm201 = {
|
||||
.num_zones = 2,
|
||||
.hdmi_mux = 1,
|
||||
.amplifier = 1,
|
||||
.deepslp = 1,
|
||||
};
|
||||
|
||||
static int __init dmi_matched(const struct dmi_system_id *dmi)
|
||||
|
@ -88,12 +120,12 @@ static int __init dmi_matched(const struct dmi_system_id *dmi)
|
|||
static const struct dmi_system_id alienware_quirks[] __initconst = {
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Alienware X51 R1",
|
||||
.ident = "Alienware X51 R3",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R3"),
|
||||
},
|
||||
.driver_data = &quirk_x51_family,
|
||||
.driver_data = &quirk_x51_r3,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
|
@ -102,7 +134,16 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"),
|
||||
},
|
||||
.driver_data = &quirk_x51_family,
|
||||
.driver_data = &quirk_x51_r1_r2,
|
||||
},
|
||||
{
|
||||
.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_r1_r2,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
|
@ -113,6 +154,24 @@ static const struct dmi_system_id alienware_quirks[] __initconst = {
|
|||
},
|
||||
.driver_data = &quirk_asm100,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Alienware ASM200",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ASM200"),
|
||||
},
|
||||
.driver_data = &quirk_asm200,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Alienware ASM201",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Alienware"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ASM201"),
|
||||
},
|
||||
.driver_data = &quirk_asm201,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -133,7 +192,7 @@ struct wmax_brightness_args {
|
|||
u32 percentage;
|
||||
};
|
||||
|
||||
struct hdmi_args {
|
||||
struct wmax_basic_args {
|
||||
u8 arg;
|
||||
};
|
||||
|
||||
|
@ -170,7 +229,7 @@ static u8 global_brightness;
|
|||
|
||||
/*
|
||||
* Helpers used for zone control
|
||||
*/
|
||||
*/
|
||||
static int parse_rgb(const char *buf, struct platform_zone *zone)
|
||||
{
|
||||
long unsigned int rgb;
|
||||
|
@ -210,7 +269,7 @@ static struct platform_zone *match_zone(struct device_attribute *attr)
|
|||
|
||||
/*
|
||||
* Individual RGB zone control
|
||||
*/
|
||||
*/
|
||||
static int alienware_update_led(struct platform_zone *zone)
|
||||
{
|
||||
int method_id;
|
||||
|
@ -218,16 +277,16 @@ static int alienware_update_led(struct platform_zone *zone)
|
|||
char *guid;
|
||||
struct acpi_buffer input;
|
||||
struct legacy_led_args legacy_args;
|
||||
struct wmax_led_args wmax_args;
|
||||
struct wmax_led_args wmax_basic_args;
|
||||
if (interface == WMAX) {
|
||||
wmax_args.led_mask = 1 << zone->location;
|
||||
wmax_args.colors = zone->colors;
|
||||
wmax_args.state = lighting_control_state;
|
||||
wmax_basic_args.led_mask = 1 << zone->location;
|
||||
wmax_basic_args.colors = zone->colors;
|
||||
wmax_basic_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;
|
||||
input.length = (acpi_size) sizeof(wmax_basic_args);
|
||||
input.pointer = &wmax_basic_args;
|
||||
} else {
|
||||
legacy_args.colors = zone->colors;
|
||||
legacy_args.brightness = global_brightness;
|
||||
|
@ -283,7 +342,7 @@ static ssize_t zone_set(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
/*
|
||||
* LED Brightness (Global)
|
||||
*/
|
||||
*/
|
||||
static int wmax_brightness(int brightness)
|
||||
{
|
||||
acpi_status status;
|
||||
|
@ -327,7 +386,7 @@ static struct led_classdev global_led = {
|
|||
|
||||
/*
|
||||
* Lighting control state device attribute (Global)
|
||||
*/
|
||||
*/
|
||||
static ssize_t show_control_state(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -435,11 +494,7 @@ static void alienware_zone_exit(struct platform_device *dev)
|
|||
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,
|
||||
static acpi_status alienware_wmax_command(struct wmax_basic_args *in_args,
|
||||
u32 command, int *out_data)
|
||||
{
|
||||
acpi_status status;
|
||||
|
@ -467,16 +522,20 @@ static acpi_status alienware_hdmi_command(struct hdmi_args *in_args,
|
|||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 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 = {
|
||||
struct wmax_basic_args in_args = {
|
||||
.arg = 0,
|
||||
};
|
||||
status =
|
||||
alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_CABLE,
|
||||
alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_CABLE,
|
||||
(u32 *) &out_data);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
if (out_data == 0)
|
||||
|
@ -495,11 +554,11 @@ static ssize_t show_hdmi_source(struct device *dev,
|
|||
{
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
struct hdmi_args in_args = {
|
||||
struct wmax_basic_args in_args = {
|
||||
.arg = 0,
|
||||
};
|
||||
status =
|
||||
alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_STATUS,
|
||||
alienware_wmax_command(&in_args, WMAX_METHOD_HDMI_STATUS,
|
||||
(u32 *) &out_data);
|
||||
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
|
@ -519,7 +578,7 @@ static ssize_t toggle_hdmi_source(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
acpi_status status;
|
||||
struct hdmi_args args;
|
||||
struct wmax_basic_args args;
|
||||
if (strcmp(buf, "gpu\n") == 0)
|
||||
args.arg = 1;
|
||||
else if (strcmp(buf, "input\n") == 0)
|
||||
|
@ -528,7 +587,7 @@ static ssize_t toggle_hdmi_source(struct device *dev,
|
|||
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);
|
||||
status = alienware_wmax_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
|
||||
|
@ -563,14 +622,147 @@ static int create_hdmi(struct platform_device *dev)
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Alienware GFX amplifier support
|
||||
* - Currently supports reading cable status
|
||||
* - Leaving expansion room to possibly support dock/undock events later
|
||||
*/
|
||||
static ssize_t show_amplifier_status(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
struct wmax_basic_args in_args = {
|
||||
.arg = 0,
|
||||
};
|
||||
status =
|
||||
alienware_wmax_command(&in_args, WMAX_METHOD_AMPLIFIER_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 amplifier cable status: %d\n", status);
|
||||
return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(status, S_IRUGO, show_amplifier_status, NULL);
|
||||
|
||||
static struct attribute *amplifier_attrs[] = {
|
||||
&dev_attr_status.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group amplifier_attribute_group = {
|
||||
.name = "amplifier",
|
||||
.attrs = amplifier_attrs,
|
||||
};
|
||||
|
||||
static void remove_amplifier(struct platform_device *dev)
|
||||
{
|
||||
if (quirks->amplifier > 0)
|
||||
sysfs_remove_group(&dev->dev.kobj, &lifier_attribute_group);
|
||||
}
|
||||
|
||||
static int create_amplifier(struct platform_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sysfs_create_group(&dev->dev.kobj, &lifier_attribute_group);
|
||||
if (ret)
|
||||
remove_amplifier(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Deep Sleep Control support
|
||||
* - Modifies BIOS setting for deep sleep control allowing extra wakeup events
|
||||
*/
|
||||
static ssize_t show_deepsleep_status(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
acpi_status status;
|
||||
u32 out_data;
|
||||
struct wmax_basic_args in_args = {
|
||||
.arg = 0,
|
||||
};
|
||||
status = alienware_wmax_command(&in_args, WMAX_METHOD_DEEP_SLEEP_STATUS,
|
||||
(u32 *) &out_data);
|
||||
if (ACPI_SUCCESS(status)) {
|
||||
if (out_data == 0)
|
||||
return scnprintf(buf, PAGE_SIZE,
|
||||
"[disabled] s5 s5_s4\n");
|
||||
else if (out_data == 1)
|
||||
return scnprintf(buf, PAGE_SIZE,
|
||||
"disabled [s5] s5_s4\n");
|
||||
else if (out_data == 2)
|
||||
return scnprintf(buf, PAGE_SIZE,
|
||||
"disabled s5 [s5_s4]\n");
|
||||
}
|
||||
pr_err("alienware-wmi: unknown deep sleep status: %d\n", status);
|
||||
return scnprintf(buf, PAGE_SIZE, "disabled s5 s5_s4 [unknown]\n");
|
||||
}
|
||||
|
||||
static ssize_t toggle_deepsleep(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
acpi_status status;
|
||||
struct wmax_basic_args args;
|
||||
|
||||
if (strcmp(buf, "disabled\n") == 0)
|
||||
args.arg = 0;
|
||||
else if (strcmp(buf, "s5\n") == 0)
|
||||
args.arg = 1;
|
||||
else
|
||||
args.arg = 2;
|
||||
pr_debug("alienware-wmi: setting deep sleep to %d : %s", args.arg, buf);
|
||||
|
||||
status = alienware_wmax_command(&args, WMAX_METHOD_DEEP_SLEEP_CONTROL,
|
||||
NULL);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
pr_err("alienware-wmi: deep sleep control failed: results: %u\n",
|
||||
status);
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(deepsleep, S_IRUGO | S_IWUSR, show_deepsleep_status, toggle_deepsleep);
|
||||
|
||||
static struct attribute *deepsleep_attrs[] = {
|
||||
&dev_attr_deepsleep.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group deepsleep_attribute_group = {
|
||||
.name = "deepsleep",
|
||||
.attrs = deepsleep_attrs,
|
||||
};
|
||||
|
||||
static void remove_deepsleep(struct platform_device *dev)
|
||||
{
|
||||
if (quirks->deepslp > 0)
|
||||
sysfs_remove_group(&dev->dev.kobj, &deepsleep_attribute_group);
|
||||
}
|
||||
|
||||
static int create_deepsleep(struct platform_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sysfs_create_group(&dev->dev.kobj, &deepsleep_attribute_group);
|
||||
if (ret)
|
||||
remove_deepsleep(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init alienware_wmi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
@ -606,6 +798,18 @@ static int __init alienware_wmi_init(void)
|
|||
goto fail_prep_hdmi;
|
||||
}
|
||||
|
||||
if (quirks->amplifier > 0) {
|
||||
ret = create_amplifier(platform_device);
|
||||
if (ret)
|
||||
goto fail_prep_amplifier;
|
||||
}
|
||||
|
||||
if (quirks->deepslp > 0) {
|
||||
ret = create_deepsleep(platform_device);
|
||||
if (ret)
|
||||
goto fail_prep_deepsleep;
|
||||
}
|
||||
|
||||
ret = alienware_zone_init(platform_device);
|
||||
if (ret)
|
||||
goto fail_prep_zones;
|
||||
|
@ -614,6 +818,8 @@ static int __init alienware_wmi_init(void)
|
|||
|
||||
fail_prep_zones:
|
||||
alienware_zone_exit(platform_device);
|
||||
fail_prep_deepsleep:
|
||||
fail_prep_amplifier:
|
||||
fail_prep_hdmi:
|
||||
platform_device_del(platform_device);
|
||||
fail_platform_device2:
|
||||
|
|
|
@ -270,6 +270,15 @@ static const struct dmi_system_id asus_quirks[] = {
|
|||
},
|
||||
.driver_data = &quirk_asus_wapf4,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUSTeK COMPUTER INC. X75VD",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X75VD"),
|
||||
},
|
||||
.driver_data = &quirk_asus_wapf4,
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "ASUSTeK COMPUTER INC. 1015E",
|
||||
|
|
|
@ -28,12 +28,11 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/i8042.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <acpi/video.h>
|
||||
#include "../../firmware/dcdbas.h"
|
||||
#include "dell-rbtn.h"
|
||||
#include "dell-smbios.h"
|
||||
|
||||
#define BRIGHTNESS_TOKEN 0x7d
|
||||
#define KBD_LED_OFF_TOKEN 0x01E1
|
||||
|
@ -44,33 +43,6 @@
|
|||
#define KBD_LED_AUTO_75_TOKEN 0x02EC
|
||||
#define KBD_LED_AUTO_100_TOKEN 0x02F6
|
||||
|
||||
/* This structure will be modified by the firmware when we enter
|
||||
* system management mode, hence the volatiles */
|
||||
|
||||
struct calling_interface_buffer {
|
||||
u16 class;
|
||||
u16 select;
|
||||
volatile u32 input[4];
|
||||
volatile u32 output[4];
|
||||
} __packed;
|
||||
|
||||
struct calling_interface_token {
|
||||
u16 tokenID;
|
||||
u16 location;
|
||||
union {
|
||||
u16 value;
|
||||
u16 stringlength;
|
||||
};
|
||||
};
|
||||
|
||||
struct calling_interface_structure {
|
||||
struct dmi_header header;
|
||||
u16 cmdIOAddress;
|
||||
u8 cmdIOCode;
|
||||
u32 supportedCmds;
|
||||
struct calling_interface_token tokens[];
|
||||
} __packed;
|
||||
|
||||
struct quirk_entry {
|
||||
u8 touchpad_led;
|
||||
|
||||
|
@ -103,11 +75,6 @@ static struct quirk_entry quirk_dell_xps13_9333 = {
|
|||
.kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 },
|
||||
};
|
||||
|
||||
static int da_command_address;
|
||||
static int da_command_code;
|
||||
static int da_num_tokens;
|
||||
static struct calling_interface_token *da_tokens;
|
||||
|
||||
static struct platform_driver platform_driver = {
|
||||
.driver = {
|
||||
.name = "dell-laptop",
|
||||
|
@ -306,126 +273,6 @@ static const struct dmi_system_id dell_quirks[] __initconst = {
|
|||
{ }
|
||||
};
|
||||
|
||||
static struct calling_interface_buffer *buffer;
|
||||
static DEFINE_MUTEX(buffer_mutex);
|
||||
|
||||
static void clear_buffer(void)
|
||||
{
|
||||
memset(buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
}
|
||||
|
||||
static void get_buffer(void)
|
||||
{
|
||||
mutex_lock(&buffer_mutex);
|
||||
clear_buffer();
|
||||
}
|
||||
|
||||
static void release_buffer(void)
|
||||
{
|
||||
mutex_unlock(&buffer_mutex);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
|
||||
6 bytes of entry */
|
||||
|
||||
if (dm->length < 17)
|
||||
return;
|
||||
|
||||
da_command_address = table->cmdIOAddress;
|
||||
da_command_code = table->cmdIOCode;
|
||||
|
||||
new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
|
||||
sizeof(struct calling_interface_token),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!new_da_tokens)
|
||||
return;
|
||||
da_tokens = new_da_tokens;
|
||||
|
||||
memcpy(da_tokens+da_num_tokens, table->tokens,
|
||||
sizeof(struct calling_interface_token) * tokens);
|
||||
|
||||
da_num_tokens += tokens;
|
||||
}
|
||||
|
||||
static void __init find_tokens(const struct dmi_header *dm, void *dummy)
|
||||
{
|
||||
switch (dm->type) {
|
||||
case 0xd4: /* Indexed IO */
|
||||
case 0xd5: /* Protected Area Type 1 */
|
||||
case 0xd6: /* Protected Area Type 2 */
|
||||
break;
|
||||
case 0xda: /* Calling interface */
|
||||
parse_da_table(dm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int find_token_id(int tokenid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < da_num_tokens; i++) {
|
||||
if (da_tokens[i].tokenID == tokenid)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_token_location(int tokenid)
|
||||
{
|
||||
int id;
|
||||
|
||||
id = find_token_id(tokenid);
|
||||
if (id == -1)
|
||||
return -1;
|
||||
|
||||
return da_tokens[id].location;
|
||||
}
|
||||
|
||||
static struct calling_interface_buffer *
|
||||
dell_send_request(struct calling_interface_buffer *buffer, int class,
|
||||
int select)
|
||||
{
|
||||
struct smi_cmd command;
|
||||
|
||||
command.magic = SMI_CMD_MAGIC;
|
||||
command.command_address = da_command_address;
|
||||
command.command_code = da_command_code;
|
||||
command.ebx = virt_to_phys(buffer);
|
||||
command.ecx = 0x42534931;
|
||||
|
||||
buffer->class = class;
|
||||
buffer->select = select;
|
||||
|
||||
dcdbas_smi_request(&command);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static inline int dell_smi_error(int value)
|
||||
{
|
||||
switch (value) {
|
||||
case 0: /* Completed successfully */
|
||||
return 0;
|
||||
case -1: /* Completed with error */
|
||||
return -EIO;
|
||||
case -2: /* Function not supported */
|
||||
return -ENXIO;
|
||||
default: /* Unknown error */
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Derived from information in smbios-wireless-ctl:
|
||||
*
|
||||
|
@ -548,6 +395,7 @@ static inline int dell_smi_error(int value)
|
|||
|
||||
static int dell_rfkill_set(void *data, bool blocked)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int disable = blocked ? 1 : 0;
|
||||
unsigned long radio = (unsigned long)data;
|
||||
int hwswitch_bit = (unsigned long)data - 1;
|
||||
|
@ -555,19 +403,19 @@ static int dell_rfkill_set(void *data, bool blocked)
|
|||
int status;
|
||||
int ret;
|
||||
|
||||
get_buffer();
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
status = buffer->output[1];
|
||||
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
clear_buffer();
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = 0x2;
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
hwswitch = buffer->output[1];
|
||||
|
||||
|
@ -577,27 +425,28 @@ static int dell_rfkill_set(void *data, bool blocked)
|
|||
(status & BIT(0)) && !(status & BIT(16)))
|
||||
disable = 1;
|
||||
|
||||
clear_buffer();
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = (1 | (radio<<8) | (disable << 16));
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
|
||||
out:
|
||||
release_buffer();
|
||||
return dell_smi_error(ret);
|
||||
dell_smbios_release_buffer();
|
||||
return dell_smbios_error(ret);
|
||||
}
|
||||
|
||||
/* Must be called with the buffer held */
|
||||
static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio,
|
||||
int status)
|
||||
int status,
|
||||
struct calling_interface_buffer *buffer)
|
||||
{
|
||||
if (status & BIT(0)) {
|
||||
/* Has hw-switch, sync sw_state to BIOS */
|
||||
int block = rfkill_blocked(rfkill);
|
||||
clear_buffer();
|
||||
dell_smbios_clear_buffer();
|
||||
buffer->input[0] = (1 | (radio << 8) | (block << 16));
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
} else {
|
||||
/* No hw-switch, sync BIOS state to sw_state */
|
||||
rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16)));
|
||||
|
@ -613,30 +462,31 @@ static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio,
|
|||
|
||||
static void dell_rfkill_query(struct rfkill *rfkill, void *data)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int radio = ((unsigned long)data & 0xF);
|
||||
int hwswitch;
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
get_buffer();
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
status = buffer->output[1];
|
||||
|
||||
if (ret != 0 || !(status & BIT(0))) {
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
return;
|
||||
}
|
||||
|
||||
clear_buffer();
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = 0x2;
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
hwswitch = buffer->output[1];
|
||||
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
if (ret != 0)
|
||||
return;
|
||||
|
@ -653,25 +503,26 @@ static struct dentry *dell_laptop_dir;
|
|||
|
||||
static int dell_debugfs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int hwswitch_state;
|
||||
int hwswitch_ret;
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
get_buffer();
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
status = buffer->output[1];
|
||||
|
||||
clear_buffer();
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = 0x2;
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
hwswitch_ret = buffer->output[0];
|
||||
hwswitch_state = buffer->output[1];
|
||||
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
seq_printf(s, "return:\t%d\n", ret);
|
||||
seq_printf(s, "status:\t0x%X\n", status);
|
||||
|
@ -752,23 +603,24 @@ static const struct file_operations dell_debugfs_fops = {
|
|||
|
||||
static void dell_update_rfkill(struct work_struct *ignored)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int hwswitch = 0;
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
get_buffer();
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
status = buffer->output[1];
|
||||
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
|
||||
clear_buffer();
|
||||
dell_smbios_clear_buffer();
|
||||
|
||||
buffer->input[0] = 0x2;
|
||||
dell_send_request(buffer, 17, 11);
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
|
||||
if (ret == 0 && (status & BIT(0)))
|
||||
|
@ -776,20 +628,21 @@ static void dell_update_rfkill(struct work_struct *ignored)
|
|||
|
||||
if (wifi_rfkill) {
|
||||
dell_rfkill_update_hw_state(wifi_rfkill, 1, status, hwswitch);
|
||||
dell_rfkill_update_sw_state(wifi_rfkill, 1, status);
|
||||
dell_rfkill_update_sw_state(wifi_rfkill, 1, status, buffer);
|
||||
}
|
||||
if (bluetooth_rfkill) {
|
||||
dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status,
|
||||
hwswitch);
|
||||
dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status);
|
||||
dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status,
|
||||
buffer);
|
||||
}
|
||||
if (wwan_rfkill) {
|
||||
dell_rfkill_update_hw_state(wwan_rfkill, 3, status, hwswitch);
|
||||
dell_rfkill_update_sw_state(wwan_rfkill, 3, status);
|
||||
dell_rfkill_update_sw_state(wwan_rfkill, 3, status, buffer);
|
||||
}
|
||||
|
||||
out:
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
}
|
||||
static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill);
|
||||
|
||||
|
@ -833,6 +686,7 @@ static struct notifier_block dell_laptop_rbtn_notifier = {
|
|||
|
||||
static int __init dell_setup_rfkill(void)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int status, ret, whitelisted;
|
||||
const char *product;
|
||||
|
||||
|
@ -848,11 +702,11 @@ static int __init dell_setup_rfkill(void)
|
|||
if (!force_rfkill && !whitelisted)
|
||||
return 0;
|
||||
|
||||
get_buffer();
|
||||
dell_send_request(buffer, 17, 11);
|
||||
buffer = dell_smbios_get_buffer();
|
||||
dell_smbios_send_request(17, 11);
|
||||
ret = buffer->output[0];
|
||||
status = buffer->output[1];
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
/* dell wireless info smbios call is not supported */
|
||||
if (ret != 0)
|
||||
|
@ -1005,51 +859,53 @@ static void dell_cleanup_rfkill(void)
|
|||
|
||||
static int dell_send_intensity(struct backlight_device *bd)
|
||||
{
|
||||
int token;
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
|
||||
token = find_token_location(BRIGHTNESS_TOKEN);
|
||||
if (token == -1)
|
||||
token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
|
||||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
get_buffer();
|
||||
buffer->input[0] = token;
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
buffer->input[1] = bd->props.brightness;
|
||||
|
||||
if (power_supply_is_system_supplied() > 0)
|
||||
dell_send_request(buffer, 1, 2);
|
||||
dell_smbios_send_request(1, 2);
|
||||
else
|
||||
dell_send_request(buffer, 1, 1);
|
||||
dell_smbios_send_request(1, 1);
|
||||
|
||||
ret = dell_smi_error(buffer->output[0]);
|
||||
ret = dell_smbios_error(buffer->output[0]);
|
||||
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dell_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
int token;
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
|
||||
token = find_token_location(BRIGHTNESS_TOKEN);
|
||||
if (token == -1)
|
||||
token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
|
||||
if (!token)
|
||||
return -ENODEV;
|
||||
|
||||
get_buffer();
|
||||
buffer->input[0] = token;
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
|
||||
if (power_supply_is_system_supplied() > 0)
|
||||
dell_send_request(buffer, 0, 2);
|
||||
dell_smbios_send_request(0, 2);
|
||||
else
|
||||
dell_send_request(buffer, 0, 1);
|
||||
dell_smbios_send_request(0, 1);
|
||||
|
||||
if (buffer->output[0])
|
||||
ret = dell_smi_error(buffer->output[0]);
|
||||
ret = dell_smbios_error(buffer->output[0]);
|
||||
else
|
||||
ret = buffer->output[1];
|
||||
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1293,17 +1149,18 @@ static bool kbd_led_present;
|
|||
|
||||
static int kbd_get_info(struct kbd_info *info)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
u8 units;
|
||||
int ret;
|
||||
|
||||
get_buffer();
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
buffer->input[0] = 0x0;
|
||||
dell_send_request(buffer, 4, 11);
|
||||
dell_smbios_send_request(4, 11);
|
||||
ret = buffer->output[0];
|
||||
|
||||
if (ret) {
|
||||
ret = dell_smi_error(ret);
|
||||
ret = dell_smbios_error(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1323,7 +1180,7 @@ static int kbd_get_info(struct kbd_info *info)
|
|||
info->days = (buffer->output[3] >> 24) & 0xFF;
|
||||
|
||||
out:
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1382,16 +1239,17 @@ static int kbd_set_level(struct kbd_state *state, u8 level)
|
|||
|
||||
static int kbd_get_state(struct kbd_state *state)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
get_buffer();
|
||||
buffer = dell_smbios_get_buffer();
|
||||
|
||||
buffer->input[0] = 0x1;
|
||||
dell_send_request(buffer, 4, 11);
|
||||
dell_smbios_send_request(4, 11);
|
||||
ret = buffer->output[0];
|
||||
|
||||
if (ret) {
|
||||
ret = dell_smi_error(ret);
|
||||
ret = dell_smbios_error(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -1407,15 +1265,16 @@ static int kbd_get_state(struct kbd_state *state)
|
|||
state->level = (buffer->output[2] >> 16) & 0xFF;
|
||||
|
||||
out:
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kbd_set_state(struct kbd_state *state)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
get_buffer();
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = 0x2;
|
||||
buffer->input[1] = BIT(state->mode_bit) & 0xFFFF;
|
||||
buffer->input[1] |= (state->triggers & 0xFF) << 16;
|
||||
|
@ -1423,11 +1282,11 @@ static int kbd_set_state(struct kbd_state *state)
|
|||
buffer->input[1] |= (state->timeout_unit & 0x3) << 30;
|
||||
buffer->input[2] = state->als_setting & 0xFF;
|
||||
buffer->input[2] |= (state->level & 0xFF) << 16;
|
||||
dell_send_request(buffer, 4, 11);
|
||||
dell_smbios_send_request(4, 11);
|
||||
ret = buffer->output[0];
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
return dell_smi_error(ret);
|
||||
return dell_smbios_error(ret);
|
||||
}
|
||||
|
||||
static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
|
||||
|
@ -1452,50 +1311,52 @@ static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old)
|
|||
|
||||
static int kbd_set_token_bit(u8 bit)
|
||||
{
|
||||
int id;
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
|
||||
if (bit >= ARRAY_SIZE(kbd_tokens))
|
||||
return -EINVAL;
|
||||
|
||||
id = find_token_id(kbd_tokens[bit]);
|
||||
if (id == -1)
|
||||
token = dell_smbios_find_token(kbd_tokens[bit]);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
get_buffer();
|
||||
buffer->input[0] = da_tokens[id].location;
|
||||
buffer->input[1] = da_tokens[id].value;
|
||||
dell_send_request(buffer, 1, 0);
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
buffer->input[1] = token->value;
|
||||
dell_smbios_send_request(1, 0);
|
||||
ret = buffer->output[0];
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
return dell_smi_error(ret);
|
||||
return dell_smbios_error(ret);
|
||||
}
|
||||
|
||||
static int kbd_get_token_bit(u8 bit)
|
||||
{
|
||||
int id;
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int ret;
|
||||
int val;
|
||||
|
||||
if (bit >= ARRAY_SIZE(kbd_tokens))
|
||||
return -EINVAL;
|
||||
|
||||
id = find_token_id(kbd_tokens[bit]);
|
||||
if (id == -1)
|
||||
token = dell_smbios_find_token(kbd_tokens[bit]);
|
||||
if (!token)
|
||||
return -EINVAL;
|
||||
|
||||
get_buffer();
|
||||
buffer->input[0] = da_tokens[id].location;
|
||||
dell_send_request(buffer, 0, 0);
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
dell_smbios_send_request(0, 0);
|
||||
ret = buffer->output[0];
|
||||
val = buffer->output[1];
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
if (ret)
|
||||
return dell_smi_error(ret);
|
||||
return dell_smbios_error(ret);
|
||||
|
||||
return (val == da_tokens[id].value);
|
||||
return (val == token->value);
|
||||
}
|
||||
|
||||
static int kbd_get_first_active_token_bit(void)
|
||||
|
@ -1597,7 +1458,7 @@ static inline void kbd_init_tokens(void)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i)
|
||||
if (find_token_id(kbd_tokens[i]) != -1)
|
||||
if (dell_smbios_find_token(kbd_tokens[i]))
|
||||
kbd_token_bits |= BIT(i);
|
||||
}
|
||||
|
||||
|
@ -2111,8 +1972,9 @@ static void kbd_led_exit(void)
|
|||
|
||||
static int __init dell_init(void)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
struct calling_interface_token *token;
|
||||
int max_intensity = 0;
|
||||
int token;
|
||||
int ret;
|
||||
|
||||
if (!dmi_check_system(dell_device_table))
|
||||
|
@ -2122,13 +1984,6 @@ static int __init dell_init(void)
|
|||
/* find if this machine support other functions */
|
||||
dmi_check_system(dell_quirks);
|
||||
|
||||
dmi_walk(find_tokens, NULL);
|
||||
|
||||
if (!da_tokens) {
|
||||
pr_info("Unable to find dmi tokens\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = platform_driver_register(&platform_driver);
|
||||
if (ret)
|
||||
goto fail_platform_driver;
|
||||
|
@ -2141,16 +1996,6 @@ static int __init dell_init(void)
|
|||
if (ret)
|
||||
goto fail_platform_device2;
|
||||
|
||||
/*
|
||||
* Allocate buffer below 4GB for SMI data--only 32-bit physical addr
|
||||
* is passed to SMI handler.
|
||||
*/
|
||||
buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_buffer;
|
||||
}
|
||||
|
||||
ret = dell_setup_rfkill();
|
||||
|
||||
if (ret) {
|
||||
|
@ -2171,14 +2016,14 @@ static int __init dell_init(void)
|
|||
if (acpi_video_get_backlight_type() != acpi_backlight_vendor)
|
||||
return 0;
|
||||
|
||||
token = find_token_location(BRIGHTNESS_TOKEN);
|
||||
if (token != -1) {
|
||||
get_buffer();
|
||||
buffer->input[0] = token;
|
||||
dell_send_request(buffer, 0, 2);
|
||||
token = dell_smbios_find_token(BRIGHTNESS_TOKEN);
|
||||
if (token) {
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = token->location;
|
||||
dell_smbios_send_request(0, 2);
|
||||
if (buffer->output[0] == 0)
|
||||
max_intensity = buffer->output[3];
|
||||
release_buffer();
|
||||
dell_smbios_release_buffer();
|
||||
}
|
||||
|
||||
if (max_intensity) {
|
||||
|
@ -2208,15 +2053,12 @@ static int __init dell_init(void)
|
|||
fail_backlight:
|
||||
dell_cleanup_rfkill();
|
||||
fail_rfkill:
|
||||
free_page((unsigned long)buffer);
|
||||
fail_buffer:
|
||||
platform_device_del(platform_device);
|
||||
fail_platform_device2:
|
||||
platform_device_put(platform_device);
|
||||
fail_platform_device1:
|
||||
platform_driver_unregister(&platform_driver);
|
||||
fail_platform_driver:
|
||||
kfree(da_tokens);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -2232,8 +2074,6 @@ static void __exit dell_exit(void)
|
|||
platform_device_unregister(platform_device);
|
||||
platform_driver_unregister(&platform_driver);
|
||||
}
|
||||
kfree(da_tokens);
|
||||
free_page((unsigned long)buffer);
|
||||
}
|
||||
|
||||
/* dell-rbtn.c driver export functions which will not work correctly (and could
|
||||
|
|
|
@ -217,6 +217,21 @@ static void rbtn_notify(struct acpi_device *device, u32 event);
|
|||
static const struct acpi_device_id rbtn_ids[] = {
|
||||
{ "DELRBTN", 0 },
|
||||
{ "DELLABCE", 0 },
|
||||
|
||||
/*
|
||||
* This driver can also handle the "DELLABC6" device that
|
||||
* appears on the XPS 13 9350, but that device is disabled
|
||||
* by the DSDT unless booted with acpi_osi="!Windows 2012"
|
||||
* acpi_osi="!Windows 2013". Even if we boot that and bind
|
||||
* the driver, we seem to have inconsistent behavior in
|
||||
* which NetworkManager can get out of sync with the rfkill
|
||||
* state.
|
||||
*
|
||||
* On the XPS 13 9350 and similar laptops, we're not supposed to
|
||||
* use DELLABC6 at all. Instead, we handle the rfkill button
|
||||
* via the intel-hid driver.
|
||||
*/
|
||||
|
||||
{ "", 0 },
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Common functions for kernel modules using Dell SMBIOS
|
||||
*
|
||||
* Copyright (c) Red Hat <mjg@redhat.com>
|
||||
* Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
|
||||
* Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
|
||||
*
|
||||
* Based on documentation in the libsmbios package:
|
||||
* Copyright (C) 2005-2014 Dell Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include "../../firmware/dcdbas.h"
|
||||
#include "dell-smbios.h"
|
||||
|
||||
struct calling_interface_structure {
|
||||
struct dmi_header header;
|
||||
u16 cmdIOAddress;
|
||||
u8 cmdIOCode;
|
||||
u32 supportedCmds;
|
||||
struct calling_interface_token tokens[];
|
||||
} __packed;
|
||||
|
||||
static struct calling_interface_buffer *buffer;
|
||||
static DEFINE_MUTEX(buffer_mutex);
|
||||
|
||||
static int da_command_address;
|
||||
static int da_command_code;
|
||||
static int da_num_tokens;
|
||||
static struct calling_interface_token *da_tokens;
|
||||
|
||||
int dell_smbios_error(int value)
|
||||
{
|
||||
switch (value) {
|
||||
case 0: /* Completed successfully */
|
||||
return 0;
|
||||
case -1: /* Completed with error */
|
||||
return -EIO;
|
||||
case -2: /* Function not supported */
|
||||
return -ENXIO;
|
||||
default: /* Unknown error */
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_error);
|
||||
|
||||
struct calling_interface_buffer *dell_smbios_get_buffer(void)
|
||||
{
|
||||
mutex_lock(&buffer_mutex);
|
||||
dell_smbios_clear_buffer();
|
||||
return buffer;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_get_buffer);
|
||||
|
||||
void dell_smbios_clear_buffer(void)
|
||||
{
|
||||
memset(buffer, 0, sizeof(struct calling_interface_buffer));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_clear_buffer);
|
||||
|
||||
void dell_smbios_release_buffer(void)
|
||||
{
|
||||
mutex_unlock(&buffer_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_release_buffer);
|
||||
|
||||
void dell_smbios_send_request(int class, int select)
|
||||
{
|
||||
struct smi_cmd command;
|
||||
|
||||
command.magic = SMI_CMD_MAGIC;
|
||||
command.command_address = da_command_address;
|
||||
command.command_code = da_command_code;
|
||||
command.ebx = virt_to_phys(buffer);
|
||||
command.ecx = 0x42534931;
|
||||
|
||||
buffer->class = class;
|
||||
buffer->select = select;
|
||||
|
||||
dcdbas_smi_request(&command);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_send_request);
|
||||
|
||||
struct calling_interface_token *dell_smbios_find_token(int tokenid)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < da_num_tokens; i++) {
|
||||
if (da_tokens[i].tokenID == tokenid)
|
||||
return &da_tokens[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dell_smbios_find_token);
|
||||
|
||||
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);
|
||||
|
||||
/* 4 bytes of table header, plus 7 bytes of Dell header, plus at least
|
||||
6 bytes of entry */
|
||||
|
||||
if (dm->length < 17)
|
||||
return;
|
||||
|
||||
da_command_address = table->cmdIOAddress;
|
||||
da_command_code = table->cmdIOCode;
|
||||
|
||||
new_da_tokens = krealloc(da_tokens, (da_num_tokens + tokens) *
|
||||
sizeof(struct calling_interface_token),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!new_da_tokens)
|
||||
return;
|
||||
da_tokens = new_da_tokens;
|
||||
|
||||
memcpy(da_tokens+da_num_tokens, table->tokens,
|
||||
sizeof(struct calling_interface_token) * tokens);
|
||||
|
||||
da_num_tokens += tokens;
|
||||
}
|
||||
|
||||
static void __init find_tokens(const struct dmi_header *dm, void *dummy)
|
||||
{
|
||||
switch (dm->type) {
|
||||
case 0xd4: /* Indexed IO */
|
||||
case 0xd5: /* Protected Area Type 1 */
|
||||
case 0xd6: /* Protected Area Type 2 */
|
||||
break;
|
||||
case 0xda: /* Calling interface */
|
||||
parse_da_table(dm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init dell_smbios_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
dmi_walk(find_tokens, NULL);
|
||||
|
||||
if (!da_tokens) {
|
||||
pr_info("Unable to find dmi tokens\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate buffer below 4GB for SMI data--only 32-bit physical addr
|
||||
* is passed to SMI handler.
|
||||
*/
|
||||
buffer = (void *)__get_free_page(GFP_KERNEL | GFP_DMA32);
|
||||
if (!buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_buffer;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_buffer:
|
||||
kfree(da_tokens);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit dell_smbios_exit(void)
|
||||
{
|
||||
kfree(da_tokens);
|
||||
free_page((unsigned long)buffer);
|
||||
}
|
||||
|
||||
subsys_initcall(dell_smbios_init);
|
||||
module_exit(dell_smbios_exit);
|
||||
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
|
||||
MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>");
|
||||
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
|
||||
MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Common functions for kernel modules using Dell SMBIOS
|
||||
*
|
||||
* Copyright (c) Red Hat <mjg@redhat.com>
|
||||
* Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com>
|
||||
* Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com>
|
||||
*
|
||||
* Based on documentation in the libsmbios package:
|
||||
* Copyright (C) 2005-2014 Dell Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _DELL_SMBIOS_H_
|
||||
#define _DELL_SMBIOS_H_
|
||||
|
||||
/* This structure will be modified by the firmware when we enter
|
||||
* system management mode, hence the volatiles */
|
||||
|
||||
struct calling_interface_buffer {
|
||||
u16 class;
|
||||
u16 select;
|
||||
volatile u32 input[4];
|
||||
volatile u32 output[4];
|
||||
} __packed;
|
||||
|
||||
struct calling_interface_token {
|
||||
u16 tokenID;
|
||||
u16 location;
|
||||
union {
|
||||
u16 value;
|
||||
u16 stringlength;
|
||||
};
|
||||
};
|
||||
|
||||
int dell_smbios_error(int value);
|
||||
|
||||
struct calling_interface_buffer *dell_smbios_get_buffer(void);
|
||||
void dell_smbios_clear_buffer(void);
|
||||
void dell_smbios_release_buffer(void);
|
||||
void dell_smbios_send_request(int class, int select);
|
||||
|
||||
struct calling_interface_token *dell_smbios_find_token(int tokenid);
|
||||
#endif
|
|
@ -37,6 +37,7 @@
|
|||
#include <linux/string.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <acpi/video.h>
|
||||
#include "dell-smbios.h"
|
||||
|
||||
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
|
||||
MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>");
|
||||
|
@ -47,10 +48,37 @@ MODULE_LICENSE("GPL");
|
|||
#define DELL_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492"
|
||||
|
||||
static u32 dell_wmi_interface_version;
|
||||
static bool wmi_requires_smbios_request;
|
||||
|
||||
MODULE_ALIAS("wmi:"DELL_EVENT_GUID);
|
||||
MODULE_ALIAS("wmi:"DELL_DESCRIPTOR_GUID);
|
||||
|
||||
static int __init dmi_matched(const struct dmi_system_id *dmi)
|
||||
{
|
||||
wmi_requires_smbios_request = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct dmi_system_id dell_wmi_smbios_list[] __initconst = {
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Dell Inspiron M5110",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.callback = dmi_matched,
|
||||
.ident = "Dell Vostro V131",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
* Certain keys are flagged as KE_IGNORE. All of these are either
|
||||
* notifications (rather than requests for change) or are also sent
|
||||
|
@ -90,8 +118,11 @@ static const struct key_entry dell_wmi_legacy_keymap[] __initconst = {
|
|||
|
||||
{ KE_IGNORE, 0xe020, { KEY_MUTE } },
|
||||
|
||||
/* Shortcut and audio panel keys */
|
||||
{ KE_IGNORE, 0xe025, { KEY_RESERVED } },
|
||||
/* Dell Instant Launch key */
|
||||
{ KE_KEY, 0xe025, { KEY_PROG4 } },
|
||||
{ KE_KEY, 0xe029, { KEY_PROG4 } },
|
||||
|
||||
/* Audio panel key */
|
||||
{ KE_IGNORE, 0xe026, { KEY_RESERVED } },
|
||||
|
||||
{ KE_IGNORE, 0xe02e, { KEY_VOLUMEDOWN } },
|
||||
|
@ -120,7 +151,10 @@ struct dell_bios_hotkey_table {
|
|||
|
||||
};
|
||||
|
||||
static const struct dell_bios_hotkey_table *dell_bios_hotkey_table;
|
||||
struct dell_dmi_results {
|
||||
int err;
|
||||
struct key_entry *keymap;
|
||||
};
|
||||
|
||||
/* Uninitialized entries here are KEY_RESERVED == 0. */
|
||||
static const u16 bios_to_linux_keycode[256] __initconst = {
|
||||
|
@ -166,6 +200,30 @@ static const u16 bios_to_linux_keycode[256] __initconst = {
|
|||
[255] = KEY_PROG3,
|
||||
};
|
||||
|
||||
/*
|
||||
* These are applied if the 0xB2 DMI hotkey table is present and doesn't
|
||||
* override them.
|
||||
*/
|
||||
static const struct key_entry dell_wmi_extra_keymap[] __initconst = {
|
||||
/* Fn-lock */
|
||||
{ KE_IGNORE, 0x151, { KEY_RESERVED } },
|
||||
|
||||
/* Change keyboard illumination */
|
||||
{ KE_IGNORE, 0x152, { KEY_KBDILLUMTOGGLE } },
|
||||
|
||||
/*
|
||||
* Radio disable (notify only -- there is no model for which the
|
||||
* WMI event is supposed to trigger an action).
|
||||
*/
|
||||
{ KE_IGNORE, 0x153, { KEY_RFKILL } },
|
||||
|
||||
/* RGB keyboard backlight control */
|
||||
{ KE_IGNORE, 0x154, { KEY_RESERVED } },
|
||||
|
||||
/* Stealth mode toggle */
|
||||
{ KE_IGNORE, 0x155, { KEY_RESERVED } },
|
||||
};
|
||||
|
||||
static struct input_dev *dell_wmi_input_dev;
|
||||
|
||||
static void dell_wmi_process_key(int reported_key)
|
||||
|
@ -188,6 +246,9 @@ static void dell_wmi_process_key(int reported_key)
|
|||
acpi_video_handles_brightness_key_presses())
|
||||
return;
|
||||
|
||||
if (reported_key == 0xe025 && !wmi_requires_smbios_request)
|
||||
return;
|
||||
|
||||
sparse_keymap_report_entry(dell_wmi_input_dev, key, 1, true);
|
||||
}
|
||||
|
||||
|
@ -337,20 +398,60 @@ static void dell_wmi_notify(u32 value, void *context)
|
|||
kfree(obj);
|
||||
}
|
||||
|
||||
static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
|
||||
static bool have_scancode(u32 scancode, const struct key_entry *keymap, int len)
|
||||
{
|
||||
int hotkey_num = (dell_bios_hotkey_table->header.length - 4) /
|
||||
sizeof(struct dell_bios_keymap_entry);
|
||||
struct key_entry *keymap;
|
||||
int i;
|
||||
|
||||
keymap = kcalloc(hotkey_num + 1, sizeof(struct key_entry), GFP_KERNEL);
|
||||
if (!keymap)
|
||||
return NULL;
|
||||
for (i = 0; i < len; i++)
|
||||
if (keymap[i].code == scancode)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void __init handle_dmi_entry(const struct dmi_header *dm,
|
||||
|
||||
void *opaque)
|
||||
|
||||
{
|
||||
struct dell_dmi_results *results = opaque;
|
||||
struct dell_bios_hotkey_table *table;
|
||||
int hotkey_num, i, pos = 0;
|
||||
struct key_entry *keymap;
|
||||
int num_bios_keys;
|
||||
|
||||
if (results->err || results->keymap)
|
||||
return; /* We already found the hotkey table. */
|
||||
|
||||
if (dm->type != 0xb2)
|
||||
return;
|
||||
|
||||
table = container_of(dm, struct dell_bios_hotkey_table, header);
|
||||
|
||||
hotkey_num = (table->header.length -
|
||||
sizeof(struct dell_bios_hotkey_table)) /
|
||||
sizeof(struct dell_bios_keymap_entry);
|
||||
if (hotkey_num < 1) {
|
||||
/*
|
||||
* Historically, dell-wmi would ignore a DMI entry of
|
||||
* fewer than 7 bytes. Sizes between 4 and 8 bytes are
|
||||
* nonsensical (both the header and all entries are 4
|
||||
* bytes), so we approximate the old behavior by
|
||||
* ignoring tables with fewer than one entry.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
keymap = kcalloc(hotkey_num + ARRAY_SIZE(dell_wmi_extra_keymap) + 1,
|
||||
sizeof(struct key_entry), GFP_KERNEL);
|
||||
if (!keymap) {
|
||||
results->err = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < hotkey_num; i++) {
|
||||
const struct dell_bios_keymap_entry *bios_entry =
|
||||
&dell_bios_hotkey_table->keymap[i];
|
||||
&table->keymap[i];
|
||||
|
||||
/* Uninitialized entries are 0 aka KEY_RESERVED. */
|
||||
u16 keycode = (bios_entry->keycode <
|
||||
|
@ -370,20 +471,39 @@ static const struct key_entry * __init dell_wmi_prepare_new_keymap(void)
|
|||
}
|
||||
|
||||
if (keycode == KEY_KBDILLUMTOGGLE)
|
||||
keymap[i].type = KE_IGNORE;
|
||||
keymap[pos].type = KE_IGNORE;
|
||||
else
|
||||
keymap[i].type = KE_KEY;
|
||||
keymap[i].code = bios_entry->scancode;
|
||||
keymap[i].keycode = keycode;
|
||||
keymap[pos].type = KE_KEY;
|
||||
keymap[pos].code = bios_entry->scancode;
|
||||
keymap[pos].keycode = keycode;
|
||||
|
||||
pos++;
|
||||
}
|
||||
|
||||
keymap[hotkey_num].type = KE_END;
|
||||
num_bios_keys = pos;
|
||||
|
||||
return keymap;
|
||||
for (i = 0; i < ARRAY_SIZE(dell_wmi_extra_keymap); i++) {
|
||||
const struct key_entry *entry = &dell_wmi_extra_keymap[i];
|
||||
|
||||
/*
|
||||
* Check if we've already found this scancode. This takes
|
||||
* quadratic time, but it doesn't matter unless the list
|
||||
* of extra keys gets very long.
|
||||
*/
|
||||
if (!have_scancode(entry->code, keymap, num_bios_keys)) {
|
||||
keymap[pos] = *entry;
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
keymap[pos].type = KE_END;
|
||||
|
||||
results->keymap = keymap;
|
||||
}
|
||||
|
||||
static int __init dell_wmi_input_setup(void)
|
||||
{
|
||||
struct dell_dmi_results dmi_results = {};
|
||||
int err;
|
||||
|
||||
dell_wmi_input_dev = input_allocate_device();
|
||||
|
@ -394,20 +514,31 @@ static int __init dell_wmi_input_setup(void)
|
|||
dell_wmi_input_dev->phys = "wmi/input0";
|
||||
dell_wmi_input_dev->id.bustype = BUS_HOST;
|
||||
|
||||
if (dell_new_hk_type) {
|
||||
const struct key_entry *keymap = dell_wmi_prepare_new_keymap();
|
||||
if (!keymap) {
|
||||
err = -ENOMEM;
|
||||
if (dmi_walk(handle_dmi_entry, &dmi_results)) {
|
||||
/*
|
||||
* Historically, dell-wmi ignored dmi_walk errors. A failure
|
||||
* is certainly surprising, but it probably just indicates
|
||||
* a very old laptop.
|
||||
*/
|
||||
pr_warn("no DMI; using the old-style hotkey interface\n");
|
||||
}
|
||||
|
||||
if (dmi_results.err) {
|
||||
err = dmi_results.err;
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL);
|
||||
if (dmi_results.keymap) {
|
||||
dell_new_hk_type = true;
|
||||
|
||||
err = sparse_keymap_setup(dell_wmi_input_dev,
|
||||
dmi_results.keymap, NULL);
|
||||
|
||||
/*
|
||||
* Sparse keymap library makes a copy of keymap so we
|
||||
* don't need the original one that was allocated.
|
||||
*/
|
||||
kfree(keymap);
|
||||
kfree(dmi_results.keymap);
|
||||
} else {
|
||||
err = sparse_keymap_setup(dell_wmi_input_dev,
|
||||
dell_wmi_legacy_keymap, NULL);
|
||||
|
@ -434,15 +565,6 @@ static void dell_wmi_input_destroy(void)
|
|||
input_unregister_device(dell_wmi_input_dev);
|
||||
}
|
||||
|
||||
static void __init find_hk_type(const struct dmi_header *dm, void *dummy)
|
||||
{
|
||||
if (dm->type == 0xb2 && dm->length > 6) {
|
||||
dell_new_hk_type = true;
|
||||
dell_bios_hotkey_table =
|
||||
container_of(dm, struct dell_bios_hotkey_table, header);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Descriptor buffer is 128 byte long and contains:
|
||||
*
|
||||
|
@ -509,6 +631,38 @@ static int __init dell_wmi_check_descriptor_buffer(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to Dell SMBIOS documentation:
|
||||
*
|
||||
* 17 3 Application Program Registration
|
||||
*
|
||||
* cbArg1 Application ID 1 = 0x00010000
|
||||
* cbArg2 Application ID 2
|
||||
* QUICKSET/DCP = 0x51534554 "QSET"
|
||||
* ALS Driver = 0x416c7353 "AlsS"
|
||||
* Latitude ON = 0x4c6f6e52 "LonR"
|
||||
* cbArg3 Application version or revision number
|
||||
* cbArg4 0 = Unregister application
|
||||
* 1 = Register application
|
||||
* cbRes1 Standard return codes (0, -1, -2)
|
||||
*/
|
||||
|
||||
static int dell_wmi_events_set_enabled(bool enable)
|
||||
{
|
||||
struct calling_interface_buffer *buffer;
|
||||
int ret;
|
||||
|
||||
buffer = dell_smbios_get_buffer();
|
||||
buffer->input[0] = 0x10000;
|
||||
buffer->input[1] = 0x51534554;
|
||||
buffer->input[3] = enable;
|
||||
dell_smbios_send_request(17, 3);
|
||||
ret = buffer->output[0];
|
||||
dell_smbios_release_buffer();
|
||||
|
||||
return dell_smbios_error(ret);
|
||||
}
|
||||
|
||||
static int __init dell_wmi_init(void)
|
||||
{
|
||||
int err;
|
||||
|
@ -524,8 +678,6 @@ static int __init dell_wmi_init(void)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
dmi_walk(find_hk_type, NULL);
|
||||
|
||||
err = dell_wmi_input_setup();
|
||||
if (err)
|
||||
return err;
|
||||
|
@ -538,12 +690,26 @@ static int __init dell_wmi_init(void)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
dmi_check_system(dell_wmi_smbios_list);
|
||||
|
||||
if (wmi_requires_smbios_request) {
|
||||
err = dell_wmi_events_set_enabled(true);
|
||||
if (err) {
|
||||
pr_err("Failed to enable WMI events\n");
|
||||
wmi_remove_notify_handler(DELL_EVENT_GUID);
|
||||
dell_wmi_input_destroy();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(dell_wmi_init);
|
||||
|
||||
static void __exit dell_wmi_exit(void)
|
||||
{
|
||||
if (wmi_requires_smbios_request)
|
||||
dell_wmi_events_set_enabled(false);
|
||||
wmi_remove_notify_handler(DELL_EVENT_GUID);
|
||||
dell_wmi_input_destroy();
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@
|
|||
#define KEY2_CODE 0x411
|
||||
#define KEY3_CODE 0x412
|
||||
#define KEY4_CODE 0x413
|
||||
#define KEY5_CODE 0x420
|
||||
|
||||
#define MAX_HOTKEY_RINGBUFFER_SIZE 100
|
||||
#define RINGBUFFERSIZE 40
|
||||
|
@ -149,7 +150,7 @@ struct fujitsu_t {
|
|||
char phys[32];
|
||||
struct backlight_device *bl_device;
|
||||
struct platform_device *pf_device;
|
||||
int keycode1, keycode2, keycode3, keycode4;
|
||||
int keycode1, keycode2, keycode3, keycode4, keycode5;
|
||||
|
||||
unsigned int max_brightness;
|
||||
unsigned int brightness_changed;
|
||||
|
@ -823,6 +824,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)
|
|||
set_bit(fujitsu->keycode2, input->keybit);
|
||||
set_bit(fujitsu->keycode3, input->keybit);
|
||||
set_bit(fujitsu->keycode4, input->keybit);
|
||||
set_bit(fujitsu->keycode5, input->keybit);
|
||||
set_bit(KEY_UNKNOWN, input->keybit);
|
||||
|
||||
error = input_register_device(input);
|
||||
|
@ -962,6 +964,9 @@ static void acpi_fujitsu_hotkey_notify(struct acpi_device *device, u32 event)
|
|||
case KEY4_CODE:
|
||||
keycode = fujitsu->keycode4;
|
||||
break;
|
||||
case KEY5_CODE:
|
||||
keycode = fujitsu->keycode5;
|
||||
break;
|
||||
case 0:
|
||||
keycode = 0;
|
||||
break;
|
||||
|
@ -1072,6 +1077,7 @@ static int __init fujitsu_init(void)
|
|||
fujitsu->keycode2 = KEY_PROG2;
|
||||
fujitsu->keycode3 = KEY_PROG3;
|
||||
fujitsu->keycode4 = KEY_PROG4;
|
||||
fujitsu->keycode5 = KEY_RFKILL;
|
||||
dmi_check_system(fujitsu_dmi_table);
|
||||
|
||||
result = acpi_bus_register_driver(&acpi_fujitsu_driver);
|
||||
|
|
|
@ -157,7 +157,6 @@ 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;
|
||||
|
@ -613,10 +612,6 @@ 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");
|
||||
|
@ -746,7 +741,7 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
|
|||
(void *) HPWMI_BLUETOOTH);
|
||||
if (!bluetooth_rfkill) {
|
||||
err = -ENOMEM;
|
||||
goto register_wifi_error;
|
||||
goto register_bluetooth_error;
|
||||
}
|
||||
rfkill_init_sw_state(bluetooth_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_BLUETOOTH));
|
||||
|
@ -764,7 +759,7 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
|
|||
(void *) HPWMI_WWAN);
|
||||
if (!wwan_rfkill) {
|
||||
err = -ENOMEM;
|
||||
goto register_bluetooth_error;
|
||||
goto register_wwan_error;
|
||||
}
|
||||
rfkill_init_sw_state(wwan_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_WWAN));
|
||||
|
@ -775,35 +770,13 @@ static int __init hp_wmi_rfkill_setup(struct platform_device *device)
|
|||
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_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);
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
register_bluetooth_error:
|
||||
rfkill_destroy(bluetooth_rfkill);
|
||||
bluetooth_rfkill = NULL;
|
||||
|
@ -907,7 +880,6 @@ static int __init 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_bios_2009_later() || hp_wmi_rfkill_setup(device))
|
||||
|
@ -960,10 +932,6 @@ 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;
|
||||
}
|
||||
|
@ -999,10 +967,6 @@ 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;
|
||||
}
|
||||
|
|
|
@ -864,6 +864,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {
|
|||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo G50-30"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lenovo ideapad Y700-15ISK",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700-15ISK"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lenovo ideapad Y700 Touch-15ISK",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad Y700 Touch-15ISK"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Lenovo ideapad Y700-17ISK",
|
||||
.matches = {
|
||||
|
|
|
@ -180,8 +180,7 @@ static int intel_hid_probe(struct platform_device *device)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&device->dev,
|
||||
sizeof(struct intel_hid_priv *), GFP_KERNEL);
|
||||
priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(&device->dev, priv);
|
||||
|
|
|
@ -67,7 +67,8 @@
|
|||
/* exported resources from IFWI */
|
||||
#define PLAT_RESOURCE_IPC_INDEX 0
|
||||
#define PLAT_RESOURCE_IPC_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_GCR_SIZE 0x1000
|
||||
#define PLAT_RESOURCE_GCR_OFFSET 0x1008
|
||||
#define PLAT_RESOURCE_GCR_SIZE 0x4
|
||||
#define PLAT_RESOURCE_BIOS_DATA_INDEX 1
|
||||
#define PLAT_RESOURCE_BIOS_IFACE_INDEX 2
|
||||
#define PLAT_RESOURCE_TELEM_SSRAM_INDEX 3
|
||||
|
@ -766,7 +767,7 @@ static int ipc_plat_get_res(struct platform_device *pdev)
|
|||
}
|
||||
ipcdev.ipc_base = addr;
|
||||
|
||||
ipcdev.gcr_base = res->start + size;
|
||||
ipcdev.gcr_base = res->start + PLAT_RESOURCE_GCR_OFFSET;
|
||||
ipcdev.gcr_size = PLAT_RESOURCE_GCR_SIZE;
|
||||
dev_info(&pdev->dev, "ipc res: %pR\n", res);
|
||||
|
||||
|
@ -824,7 +825,8 @@ static int ipc_plat_probe(struct platform_device *pdev)
|
|||
goto err_device;
|
||||
}
|
||||
|
||||
if (request_irq(ipcdev.irq, ioc, 0, "intel_pmc_ipc", &ipcdev)) {
|
||||
if (request_irq(ipcdev.irq, ioc, IRQF_NO_SUSPEND,
|
||||
"intel_pmc_ipc", &ipcdev)) {
|
||||
dev_err(&pdev->dev, "Failed to request irq\n");
|
||||
ret = -EBUSY;
|
||||
goto err_irq;
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/sfi.h>
|
||||
#include <linux/module.h>
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
|
@ -611,28 +610,6 @@ static int ipc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipc_remove - remove a bound IPC device
|
||||
* @pdev: PCI device
|
||||
*
|
||||
* In practice the SCU is not removable but this function is also
|
||||
* called for each device on a module unload or cleanup which is the
|
||||
* path that will get used.
|
||||
*
|
||||
* Free up the mappings and release the PCI resources
|
||||
*/
|
||||
static void ipc_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct intel_scu_ipc_dev *scu = pci_get_drvdata(pdev);
|
||||
|
||||
mutex_lock(&ipclock);
|
||||
scu->dev = NULL;
|
||||
mutex_unlock(&ipclock);
|
||||
|
||||
iounmap(scu->i2c_base);
|
||||
intel_scu_devices_destroy();
|
||||
}
|
||||
|
||||
static const struct pci_device_id pci_ids[] = {
|
||||
{
|
||||
PCI_VDEVICE(INTEL, PCI_DEVICE_ID_LINCROFT),
|
||||
|
@ -650,17 +627,13 @@ static const struct pci_device_id pci_ids[] = {
|
|||
0,
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pci_ids);
|
||||
|
||||
static struct pci_driver ipc_driver = {
|
||||
.driver = {
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
.name = "intel_scu_ipc",
|
||||
.id_table = pci_ids,
|
||||
.probe = ipc_probe,
|
||||
.remove = ipc_remove,
|
||||
};
|
||||
|
||||
module_pci_driver(ipc_driver);
|
||||
|
||||
MODULE_AUTHOR("Sreedhara DS <sreedhara.ds@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel SCU IPC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
builtin_pci_driver(ipc_driver);
|
||||
|
|
|
@ -1029,9 +1029,20 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,
|
|||
mutex_lock(&(telm_conf->telem_trace_lock));
|
||||
switch (telem_unit) {
|
||||
case TELEM_PSS:
|
||||
ret = intel_punit_ipc_command(
|
||||
IPC_PUNIT_BIOS_READ_TELE_TRACE_CTRL,
|
||||
0, 0, NULL, &temp);
|
||||
if (ret) {
|
||||
pr_err("PSS TRACE_CTRL Read Failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
TELEM_CLEAR_VERBOSITY_BITS(temp);
|
||||
TELEM_SET_VERBOSITY_BITS(temp, verbosity);
|
||||
|
||||
ret = intel_punit_ipc_command(
|
||||
IPC_PUNIT_BIOS_WRITE_TELE_TRACE_CTRL,
|
||||
0, 0, &verbosity, NULL);
|
||||
0, 0, &temp, NULL);
|
||||
if (ret) {
|
||||
pr_err("PSS TRACE_CTRL Verbosity Set Failed\n");
|
||||
goto out;
|
||||
|
|
|
@ -6653,18 +6653,16 @@ static void __init tpacpi_detect_brightness_capabilities(void)
|
|||
switch (b) {
|
||||
case 16:
|
||||
bright_maxlvl = 15;
|
||||
pr_info("detected a 16-level brightness capable ThinkPad\n");
|
||||
break;
|
||||
case 8:
|
||||
case 0:
|
||||
bright_maxlvl = 7;
|
||||
pr_info("detected a 8-level brightness capable ThinkPad\n");
|
||||
break;
|
||||
default:
|
||||
pr_info("Unsupported brightness interface\n");
|
||||
tp_features.bright_unkfw = 1;
|
||||
bright_maxlvl = b - 1;
|
||||
}
|
||||
pr_debug("detected %u brightness levels\n", bright_maxlvl + 1);
|
||||
}
|
||||
|
||||
static int __init brightness_init(struct ibm_init_struct *iibm)
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
@ -117,6 +118,7 @@ MODULE_LICENSE("GPL");
|
|||
#define HCI_LCD_BRIGHTNESS 0x002a
|
||||
#define HCI_WIRELESS 0x0056
|
||||
#define HCI_ACCELEROMETER 0x006d
|
||||
#define HCI_COOLING_METHOD 0x007f
|
||||
#define HCI_KBD_ILLUMINATION 0x0095
|
||||
#define HCI_ECO_MODE 0x0097
|
||||
#define HCI_ACCELEROMETER2 0x00a6
|
||||
|
@ -186,6 +188,7 @@ struct toshiba_acpi_dev {
|
|||
int usbsc_bat_level;
|
||||
int usbsc_mode_base;
|
||||
int hotkey_event_type;
|
||||
int max_cooling_method;
|
||||
|
||||
unsigned int illumination_supported:1;
|
||||
unsigned int video_supported:1;
|
||||
|
@ -205,6 +208,7 @@ struct toshiba_acpi_dev {
|
|||
unsigned int panel_power_on_supported:1;
|
||||
unsigned int usb_three_supported:1;
|
||||
unsigned int wwan_supported:1;
|
||||
unsigned int cooling_method_supported:1;
|
||||
unsigned int sysfs_created:1;
|
||||
unsigned int special_functions;
|
||||
|
||||
|
@ -217,6 +221,10 @@ struct toshiba_acpi_dev {
|
|||
|
||||
static struct toshiba_acpi_dev *toshiba_acpi;
|
||||
|
||||
static bool disable_hotkeys;
|
||||
module_param(disable_hotkeys, bool, 0444);
|
||||
MODULE_PARM_DESC(disable_hotkeys, "Disables the hotkeys activation");
|
||||
|
||||
static const struct acpi_device_id toshiba_device_ids[] = {
|
||||
{"TOS6200", 0},
|
||||
{"TOS6207", 0},
|
||||
|
@ -1194,6 +1202,53 @@ static int toshiba_wwan_set(struct toshiba_acpi_dev *dev, u32 state)
|
|||
return out[0] == TOS_SUCCESS ? 0 : -EIO;
|
||||
}
|
||||
|
||||
/* Cooling Method */
|
||||
static void toshiba_cooling_method_available(struct toshiba_acpi_dev *dev)
|
||||
{
|
||||
u32 in[TCI_WORDS] = { HCI_GET, HCI_COOLING_METHOD, 0, 0, 0, 0 };
|
||||
u32 out[TCI_WORDS];
|
||||
acpi_status status;
|
||||
|
||||
dev->cooling_method_supported = 0;
|
||||
dev->max_cooling_method = 0;
|
||||
|
||||
status = tci_raw(dev, in, out);
|
||||
if (ACPI_FAILURE(status))
|
||||
pr_err("ACPI call to get Cooling Method failed\n");
|
||||
|
||||
if (out[0] != TOS_SUCCESS && out[0] != TOS_SUCCESS2)
|
||||
return;
|
||||
|
||||
dev->cooling_method_supported = 1;
|
||||
dev->max_cooling_method = out[3];
|
||||
}
|
||||
|
||||
static int toshiba_cooling_method_get(struct toshiba_acpi_dev *dev, u32 *state)
|
||||
{
|
||||
u32 result = hci_read(dev, HCI_COOLING_METHOD, state);
|
||||
|
||||
if (result == TOS_FAILURE)
|
||||
pr_err("ACPI call to get Cooling Method failed\n");
|
||||
|
||||
if (result == TOS_NOT_SUPPORTED)
|
||||
return -ENODEV;
|
||||
|
||||
return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int toshiba_cooling_method_set(struct toshiba_acpi_dev *dev, u32 state)
|
||||
{
|
||||
u32 result = hci_write(dev, HCI_COOLING_METHOD, state);
|
||||
|
||||
if (result == TOS_FAILURE)
|
||||
pr_err("ACPI call to get Cooling Method failed\n");
|
||||
|
||||
if (result == TOS_NOT_SUPPORTED)
|
||||
return -ENODEV;
|
||||
|
||||
return (result == TOS_SUCCESS || result == TOS_SUCCESS2) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
/* Transflective Backlight */
|
||||
static int get_tr_backlight_status(struct toshiba_acpi_dev *dev, u32 *status)
|
||||
{
|
||||
|
@ -2239,6 +2294,54 @@ static ssize_t usb_three_store(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR_RW(usb_three);
|
||||
|
||||
static ssize_t cooling_method_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev);
|
||||
int state;
|
||||
int ret;
|
||||
|
||||
ret = toshiba_cooling_method_get(toshiba, &state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d %d\n", state, toshiba->max_cooling_method);
|
||||
}
|
||||
|
||||
static ssize_t cooling_method_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;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoint(buf, 0, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Check for supported values
|
||||
* Depending on the laptop model, some only support these two:
|
||||
* 0 - Maximum Performance
|
||||
* 1 - Battery Optimized
|
||||
*
|
||||
* While some others support all three methods:
|
||||
* 0 - Maximum Performance
|
||||
* 1 - Performance
|
||||
* 2 - Battery Optimized
|
||||
*/
|
||||
if (state < 0 || state > toshiba->max_cooling_method)
|
||||
return -EINVAL;
|
||||
|
||||
ret = toshiba_cooling_method_set(toshiba, state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(cooling_method);
|
||||
|
||||
static struct attribute *toshiba_attributes[] = {
|
||||
&dev_attr_version.attr,
|
||||
&dev_attr_fan.attr,
|
||||
|
@ -2255,6 +2358,7 @@ static struct attribute *toshiba_attributes[] = {
|
|||
&dev_attr_kbd_function_keys.attr,
|
||||
&dev_attr_panel_power_on.attr,
|
||||
&dev_attr_usb_three.attr,
|
||||
&dev_attr_cooling_method.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
@ -2289,6 +2393,8 @@ static umode_t toshiba_sysfs_is_visible(struct kobject *kobj,
|
|||
exists = (drv->panel_power_on_supported) ? true : false;
|
||||
else if (attr == &dev_attr_usb_three.attr)
|
||||
exists = (drv->usb_three_supported) ? true : false;
|
||||
else if (attr == &dev_attr_cooling_method.attr)
|
||||
exists = (drv->cooling_method_supported) ? true : false;
|
||||
|
||||
return exists ? attr->mode : 0;
|
||||
}
|
||||
|
@ -2591,6 +2697,11 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)
|
|||
acpi_handle ec_handle;
|
||||
int error;
|
||||
|
||||
if (disable_hotkeys) {
|
||||
pr_info("Hotkeys disabled by module parameter\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) {
|
||||
pr_info("WMI event detected, hotkeys will not be monitored\n");
|
||||
return 0;
|
||||
|
@ -2779,6 +2890,8 @@ static void print_supported_features(struct toshiba_acpi_dev *dev)
|
|||
pr_cont(" usb3");
|
||||
if (dev->wwan_supported)
|
||||
pr_cont(" wwan");
|
||||
if (dev->cooling_method_supported)
|
||||
pr_cont(" cooling-method");
|
||||
|
||||
pr_cont("\n");
|
||||
}
|
||||
|
@ -2963,6 +3076,8 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)
|
|||
if (dev->wwan_supported)
|
||||
toshiba_acpi_setup_wwan_rfkill(dev);
|
||||
|
||||
toshiba_cooling_method_available(dev);
|
||||
|
||||
print_supported_features(dev);
|
||||
|
||||
ret = sysfs_create_group(&dev->acpi_dev->dev.kobj,
|
||||
|
|
Loading…
Reference in New Issue