diff options
Diffstat (limited to 'drivers/platform/x86')
| -rw-r--r-- | drivers/platform/x86/Kconfig | 15 | ||||
| -rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
| -rw-r--r-- | drivers/platform/x86/alienware-wmi.c | 286 | ||||
| -rw-r--r-- | drivers/platform/x86/apple-gmux.c | 111 | ||||
| -rw-r--r-- | drivers/platform/x86/asus-nb-wmi.c | 9 | ||||
| -rw-r--r-- | drivers/platform/x86/dell-laptop.c | 370 | ||||
| -rw-r--r-- | drivers/platform/x86/dell-rbtn.c | 15 | ||||
| -rw-r--r-- | drivers/platform/x86/dell-smbios.c | 193 | ||||
| -rw-r--r-- | drivers/platform/x86/dell-smbios.h | 46 | ||||
| -rw-r--r-- | drivers/platform/x86/dell-wmi.c | 238 | ||||
| -rw-r--r-- | drivers/platform/x86/fujitsu-laptop.c | 8 | ||||
| -rw-r--r-- | drivers/platform/x86/hp-wmi.c | 46 | ||||
| -rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 14 | ||||
| -rw-r--r-- | drivers/platform/x86/intel-hid.c | 3 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_pmc_ipc.c | 8 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_scu_ipc.c | 35 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_telemetry_pltdrv.c | 13 | ||||
| -rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 4 | ||||
| -rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 115 | 
19 files changed, 1083 insertions, 447 deletions
| diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 69f93a576e45..ed2004be13cf 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -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. diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 40574e7390f3..448443c3baba 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -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 diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c index 1e1e59423889..005629447b0c 100644 --- a/drivers/platform/x86/alienware-wmi.c +++ b/drivers/platform/x86/alienware-wmi.c @@ -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 <[email protected]>");  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,17 +134,44 @@ 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, +	 .ident = "Alienware ASM100", +	 .matches = { +		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), +		     DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), +		     }, +	 .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 ASM100", -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), -			DMI_MATCH(DMI_PRODUCT_NAME, "ASM100"), -		}, -		.driver_data = &quirk_asm100, -	}, +	 .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,11 +622,144 @@ 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; +		remove_hdmi(dev); +	return ret; +} -error_create_hdmi: -	remove_hdmi(dev); +/* + * 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;  } @@ -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: diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index f236250ac106..4034d2d4c507 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -19,6 +19,7 @@  #include <linux/acpi.h>  #include <linux/pnp.h>  #include <linux/apple_bl.h> +#include <linux/apple-gmux.h>  #include <linux/slab.h>  #include <linux/delay.h>  #include <linux/pci.h> @@ -57,7 +58,9 @@ struct apple_gmux_data {  	/* switcheroo data */  	acpi_handle dhandle;  	int gpe; -	enum vga_switcheroo_client_id resume_client_id; +	enum vga_switcheroo_client_id switch_state_display; +	enum vga_switcheroo_client_id switch_state_ddc; +	enum vga_switcheroo_client_id switch_state_external;  	enum vga_switcheroo_state power_state;  	struct completion powerchange_done;  }; @@ -368,19 +371,70 @@ static const struct backlight_ops gmux_bl_ops = {   * for the selected GPU.   */ +static void gmux_read_switch_state(struct apple_gmux_data *gmux_data) +{ +	if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DDC) == 1) +		gmux_data->switch_state_ddc = VGA_SWITCHEROO_IGD; +	else +		gmux_data->switch_state_ddc = VGA_SWITCHEROO_DIS; + +	if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2) +		gmux_data->switch_state_display = VGA_SWITCHEROO_IGD; +	else +		gmux_data->switch_state_display = VGA_SWITCHEROO_DIS; + +	if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL) == 2) +		gmux_data->switch_state_external = VGA_SWITCHEROO_IGD; +	else +		gmux_data->switch_state_external = VGA_SWITCHEROO_DIS; +} + +static void gmux_write_switch_state(struct apple_gmux_data *gmux_data) +{ +	if (gmux_data->switch_state_ddc == VGA_SWITCHEROO_IGD) +		gmux_write8(gmux_data, GMUX_PORT_SWITCH_DDC, 1); +	else +		gmux_write8(gmux_data, GMUX_PORT_SWITCH_DDC, 2); + +	if (gmux_data->switch_state_display == VGA_SWITCHEROO_IGD) +		gmux_write8(gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2); +	else +		gmux_write8(gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3); + +	if (gmux_data->switch_state_external == VGA_SWITCHEROO_IGD) +		gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2); +	else +		gmux_write8(gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3); +} +  static int gmux_switchto(enum vga_switcheroo_client_id id)  { -	if (id == VGA_SWITCHEROO_IGD) { +	apple_gmux_data->switch_state_ddc = id; +	apple_gmux_data->switch_state_display = id; +	apple_gmux_data->switch_state_external = id; + +	gmux_write_switch_state(apple_gmux_data); + +	return 0; +} + +static int gmux_switch_ddc(enum vga_switcheroo_client_id id) +{ +	enum vga_switcheroo_client_id old_ddc_owner = +		apple_gmux_data->switch_state_ddc; + +	if (id == old_ddc_owner) +		return id; + +	pr_debug("Switching DDC from %d to %d\n", old_ddc_owner, id); +	apple_gmux_data->switch_state_ddc = 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 { +	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; +	return old_ddc_owner;  }  /** @@ -440,17 +494,15 @@ static int gmux_get_client_id(struct pci_dev *pdev)  		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 const struct vga_switcheroo_handler gmux_handler_indexed = { +	.switchto = gmux_switchto, +	.power_state = gmux_set_power_state, +	.get_client_id = gmux_get_client_id, +}; -static const struct vga_switcheroo_handler gmux_handler = { +static const struct vga_switcheroo_handler gmux_handler_classic = {  	.switchto = gmux_switchto, +	.switch_ddc = gmux_switch_ddc,  	.power_state = gmux_set_power_state,  	.get_client_id = gmux_get_client_id,  }; @@ -513,7 +565,6 @@ 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;  } @@ -524,7 +575,7 @@ static int gmux_resume(struct device *dev)  	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);  	gmux_enable_interrupts(gmux_data); -	gmux_switchto(gmux_data->resume_client_id); +	gmux_write_switch_state(gmux_data);  	if (gmux_data->power_state == VGA_SWITCHEROO_OFF)  		gmux_set_discrete_state(gmux_data, gmux_data->power_state);  	return 0; @@ -704,9 +755,23 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)  	apple_gmux_data = gmux_data;  	init_completion(&gmux_data->powerchange_done);  	gmux_enable_interrupts(gmux_data); +	gmux_read_switch_state(gmux_data); -	if (vga_switcheroo_register_handler(&gmux_handler)) { -		ret = -ENODEV; +	/* +	 * Retina MacBook Pros cannot switch the panel's AUX separately +	 * and need eDP pre-calibration. They are distinguishable from +	 * pre-retinas by having an "indexed" gmux. +	 * +	 * Pre-retina MacBook Pros can switch the panel's DDC separately. +	 */ +	if (gmux_data->indexed) +		ret = vga_switcheroo_register_handler(&gmux_handler_indexed, +					      VGA_SWITCHEROO_NEEDS_EDP_CONFIG); +	else +		ret = vga_switcheroo_register_handler(&gmux_handler_classic, +					      VGA_SWITCHEROO_CAN_SWITCH_DDC); +	if (ret) { +		pr_err("Failed to register vga_switcheroo handler\n");  		goto err_register_handler;  	} @@ -764,7 +829,7 @@ static void gmux_remove(struct pnp_dev *pnp)  }  static const struct pnp_device_id gmux_device_ids[] = { -	{"APP000B", 0}, +	{GMUX_ACPI_HID, 0},  	{"", 0}  }; diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 131fee2b093e..091ca7ada8fc 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -272,6 +272,15 @@ static const struct dmi_system_id asus_quirks[] = {  	},  	{  		.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",  		.matches = {  			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index aaeeae81e3a9..2c2f02b2e08a 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -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 diff --git a/drivers/platform/x86/dell-rbtn.c b/drivers/platform/x86/dell-rbtn.c index cd410e392550..b51a2008d782 100644 --- a/drivers/platform/x86/dell-rbtn.c +++ b/drivers/platform/x86/dell-rbtn.c @@ -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 },  }; diff --git a/drivers/platform/x86/dell-smbios.c b/drivers/platform/x86/dell-smbios.c new file mode 100644 index 000000000000..d2412ab097da --- /dev/null +++ b/drivers/platform/x86/dell-smbios.c @@ -0,0 +1,193 @@ +/* + *  Common functions for kernel modules using Dell SMBIOS + * + *  Copyright (c) Red Hat <[email protected]> + *  Copyright (c) 2014 Gabriele Mazzotta <[email protected]> + *  Copyright (c) 2014 Pali Rohár <[email protected]> + * + *  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 <[email protected]>"); +MODULE_AUTHOR("Gabriele Mazzotta <[email protected]>"); +MODULE_AUTHOR("Pali Rohár <[email protected]>"); +MODULE_DESCRIPTION("Common functions for kernel modules using Dell SMBIOS"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/dell-smbios.h b/drivers/platform/x86/dell-smbios.h new file mode 100644 index 000000000000..ec7d40ae5e6e --- /dev/null +++ b/drivers/platform/x86/dell-smbios.h @@ -0,0 +1,46 @@ +/* + *  Common functions for kernel modules using Dell SMBIOS + * + *  Copyright (c) Red Hat <[email protected]> + *  Copyright (c) 2014 Gabriele Mazzotta <[email protected]> + *  Copyright (c) 2014 Pali Rohár <[email protected]> + * + *  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 diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 368e193c2741..15c6f1191aec 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -37,6 +37,7 @@  #include <linux/string.h>  #include <linux/dmi.h>  #include <acpi/video.h> +#include "dell-smbios.h"  MODULE_AUTHOR("Matthew Garrett <[email protected]>");  MODULE_AUTHOR("Pali Rohár <[email protected]>"); @@ -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++; +	} + +	num_bios_keys = pos; + +	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[hotkey_num].type = KE_END; +	keymap[pos].type = KE_END; -	return keymap; +	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; -			goto err_free_dev; -		} +	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"); +	} -		err = sparse_keymap_setup(dell_wmi_input_dev, keymap, NULL); +	if (dmi_results.err) { +		err = dmi_results.err; +		goto err_free_dev; +	} + +	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();  } diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 1c62caff93fd..ffc84cc7b1c7 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -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); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index fb4dd7b3ee71..6f145f2d004d 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -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;  } diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index d78ee151c9e4..be3bc2f4edd4 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -865,6 +865,20 @@ static const struct dmi_system_id no_hw_rfkill_list[] = {  		},  	},  	{ +		.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 = {  			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), diff --git a/drivers/platform/x86/intel-hid.c b/drivers/platform/x86/intel-hid.c index e20f23e04c24..f93abc8c1424 100644 --- a/drivers/platform/x86/intel-hid.c +++ b/drivers/platform/x86/intel-hid.c @@ -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); diff --git a/drivers/platform/x86/intel_pmc_ipc.c b/drivers/platform/x86/intel_pmc_ipc.c index 092519e37de6..3fb1d85c70a8 100644 --- a/drivers/platform/x86/intel_pmc_ipc.c +++ b/drivers/platform/x86/intel_pmc_ipc.c @@ -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; diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index f94b730540e2..e81daff65f62 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -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 <[email protected]>"); -MODULE_DESCRIPTION("Intel SCU IPC driver"); -MODULE_LICENSE("GPL"); +builtin_pci_driver(ipc_driver); diff --git a/drivers/platform/x86/intel_telemetry_pltdrv.c b/drivers/platform/x86/intel_telemetry_pltdrv.c index f97019b0106f..397119f83e82 100644 --- a/drivers/platform/x86/intel_telemetry_pltdrv.c +++ b/drivers/platform/x86/intel_telemetry_pltdrv.c @@ -1030,8 +1030,19 @@ static int telemetry_plt_set_trace_verbosity(enum telemetry_unit telem_unit,  	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; diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index a268a7abf8ab..e305ab541a22 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -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) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 73833079bac8..df1f1a76a862 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -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, |