diff options
Diffstat (limited to 'drivers/platform/x86')
44 files changed, 3419 insertions, 1286 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 96d6b2eef4f..172f26ce59a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -27,8 +27,6 @@ config ACER_WMI  	depends on ACPI_WMI  	select INPUT_SPARSEKMAP  	# Acer WMI depends on ACPI_VIDEO when ACPI is enabled -	# but for select to work, need to select ACPI_VIDEO's dependencies, ick -        select VIDEO_OUTPUT_CONTROL if ACPI          select ACPI_VIDEO if ACPI  	---help---  	  This is a driver for newer Acer (and Wistron) laptops. It adds @@ -55,6 +53,18 @@ config ACERHDF  	  If you have an Acer Aspire One netbook, say Y or M  	  here. +config ALIENWARE_WMI +	tristate "Alienware Special feature control" +	depends on ACPI +	depends on LEDS_CLASS +	depends on NEW_LEDS +	depends on ACPI_WMI +	---help--- +	 This is a driver for controlling Alienware BIOS driven +	 features.  It exposes an interface for controlling the AlienFX +	 zones on Alienware machines that don't contain a dedicated AlienFX +	 USB MCU such as the X51 and X51-R2. +  config ASUS_LAPTOP  	tristate "Asus Laptop Extras"  	depends on ACPI @@ -79,17 +89,6 @@ config ASUS_LAPTOP  	  If you have an ACPI-compatible ASUS laptop, say Y or M here. -config CHROMEOS_LAPTOP -	tristate "Chrome OS Laptop" -	depends on I2C -	depends on DMI -	---help--- -	  This driver instantiates i2c and smbus devices such as -	  light sensors and touchpads. - -	  If you have a supported Chromebook, choose Y or M here. -	  The module will be called chromeos_laptop. -  config DELL_LAPTOP  	tristate "Dell Laptop Extras"  	depends on X86 @@ -103,7 +102,7 @@ config DELL_LAPTOP  	default n  	---help---  	This driver adds support for rfkill and backlight control to Dell -	laptops. +	laptops (except for some models covered by the Compal driver).  config DELL_WMI  	tristate "Dell WMI extras" @@ -128,6 +127,16 @@ config DELL_WMI_AIO  	  To compile this driver as a module, choose M here: the module will  	  be called dell-wmi-aio. +config DELL_SMO8800 +	tristate "Dell Latitude freefall driver (ACPI SMO8800/SMO8810)" +	depends on ACPI +	---help--- +	  Say Y here if you want to support SMO8800/SMO8810 freefall device +	  on Dell Latitude laptops. + +	  To compile this driver as a module, choose M here: the module will +	  be called dell-smo8800. +  config FUJITSU_LAPTOP  	tristate "Fujitsu Laptop Extras" @@ -208,6 +217,17 @@ config HP_ACCEL  	  To compile this driver as a module, choose M here: the module will  	  be called hp_accel. +config HP_WIRELESS +	tristate "HP wireless button" +	depends on ACPI +	depends on INPUT +	help +	 This driver provides supports for new HP wireless button for Windows 8. +	 On such systems the driver should load automatically (via ACPI alias). + +	 To compile this driver as a module, choose M here: the module will +	 be called hp-wireless. +  config HP_WMI  	tristate "HP WMI extras"  	depends on ACPI_WMI @@ -255,23 +275,21 @@ config PANASONIC_LAPTOP  	  R2, R3, R5, T2, W2 and Y2 series), say Y.  config COMPAL_LAPTOP -	tristate "Compal Laptop Extras" +	tristate "Compal (and others) Laptop Extras"  	depends on ACPI  	depends on BACKLIGHT_CLASS_DEVICE  	depends on RFKILL  	depends on HWMON  	depends on POWER_SUPPLY  	---help--- -	  This is a driver for laptops built by Compal: +	  This is a driver for laptops built by Compal, and some models by +	  other brands (e.g. Dell, Toshiba). -	  Compal FL90/IFL90 -	  Compal FL91/IFL91 -	  Compal FL92/JFL92 -	  Compal FT00/IFT00 +	  It adds support for rfkill, Bluetooth, WLAN and LCD brightness +	  control. -	  It adds support for Bluetooth, WLAN and LCD brightness control. - -	  If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here. +	  For a (possibly incomplete) list of supported laptops, please refer +	  to: Documentation/platform/x86-laptop-drivers.txt  config SONY_LAPTOP  	tristate "Sony Laptop Extras" @@ -504,6 +522,7 @@ config ASUS_WMI  	depends on BACKLIGHT_CLASS_DEVICE  	depends on RFKILL || RFKILL = n  	depends on HOTPLUG_PCI +	depends on ACPI_VIDEO || ACPI_VIDEO = n  	select INPUT_SPARSEKMAP  	select LEDS_CLASS  	select NEW_LEDS @@ -713,7 +732,7 @@ config IBM_RTL  config XO1_RFKILL  	tristate "OLPC XO-1 software RF kill switch" -	depends on OLPC +	depends on OLPC || COMPILE_TEST  	depends on RFKILL  	---help---  	  Support for enabling/disabling the WLAN interface on the OLPC XO-1 @@ -721,6 +740,7 @@ config XO1_RFKILL  config XO15_EBOOK  	tristate "OLPC XO-1.5 ebook switch" +	depends on OLPC || COMPILE_TEST  	depends on ACPI && INPUT  	---help---  	  Support for the ebook switch on the OLPC XO-1.5 laptop. diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 5dbe1932435..c4ca428fd3d 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -13,9 +13,11 @@ obj-$(CONFIG_COMPAL_LAPTOP)	+= compal-laptop.o  obj-$(CONFIG_DELL_LAPTOP)	+= dell-laptop.o  obj-$(CONFIG_DELL_WMI)		+= dell-wmi.o  obj-$(CONFIG_DELL_WMI_AIO)	+= dell-wmi-aio.o +obj-$(CONFIG_DELL_SMO8800)	+= dell-smo8800.o  obj-$(CONFIG_ACER_WMI)		+= acer-wmi.o  obj-$(CONFIG_ACERHDF)		+= acerhdf.o  obj-$(CONFIG_HP_ACCEL)		+= hp_accel.o +obj-$(CONFIG_HP_WIRELESS)	+= hp-wireless.o  obj-$(CONFIG_HP_WMI)		+= hp-wmi.o  obj-$(CONFIG_AMILO_RFKILL)	+= amilo-rfkill.o  obj-$(CONFIG_TC1100_WMI)	+= tc1100-wmi.o @@ -50,8 +52,8 @@ obj-$(CONFIG_INTEL_MID_POWER_BUTTON)	+= intel_mid_powerbtn.o  obj-$(CONFIG_INTEL_OAKTRAIL)	+= intel_oaktrail.o  obj-$(CONFIG_SAMSUNG_Q10)	+= samsung-q10.o  obj-$(CONFIG_APPLE_GMUX)	+= apple-gmux.o -obj-$(CONFIG_CHROMEOS_LAPTOP)	+= chromeos_laptop.o  obj-$(CONFIG_INTEL_RST)		+= intel-rst.o  obj-$(CONFIG_INTEL_SMARTCONNECT)	+= intel-smartconnect.o  obj-$(CONFIG_PVPANIC)           += pvpanic.o +obj-$(CONFIG_ALIENWARE_WMI)	+= alienware-wmi.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c9076bdaf2c..bbf78b2d6d9 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -41,8 +41,6 @@  #include <linux/slab.h>  #include <linux/input.h>  #include <linux/input/sparse-keymap.h> - -#include <acpi/acpi_drivers.h>  #include <acpi/video.h>  MODULE_AUTHOR("Carlos Corbacho"); @@ -572,6 +570,14 @@ static const struct dmi_system_id video_vendor_dmi_table[] = {  			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5750"),  		},  	}, +	{ +		.callback = video_set_backlight_video_vendor, +		.ident = "Acer Aspire 5741", +		.matches = { +			DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5741"), +		}, +	},  	{}  }; @@ -2230,7 +2236,7 @@ static int __init acer_wmi_init(void)  		pr_info("Brightness must be controlled by acpi video driver\n");  	} else {  		pr_info("Disabling ACPI video driver\n"); -		acpi_video_unregister(); +		acpi_video_unregister_backlight();  	}  	if (wmi_has_guid(WMID_GUID3)) { diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c new file mode 100644 index 00000000000..297b6640213 --- /dev/null +++ b/drivers/platform/x86/alienware-wmi.c @@ -0,0 +1,622 @@ +/* + * Alienware AlienFX control + * + * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.com> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/dmi.h> +#include <linux/acpi.h> +#include <linux/leds.h> + +#define LEGACY_CONTROL_GUID		"A90597CE-A997-11DA-B012-B622A1EF5492" +#define LEGACY_POWER_CONTROL_GUID	"A80593CE-A997-11DA-B012-B622A1EF5492" +#define WMAX_CONTROL_GUID		"A70591CE-A997-11DA-B012-B622A1EF5492" + +#define WMAX_METHOD_HDMI_SOURCE		0x1 +#define WMAX_METHOD_HDMI_STATUS		0x2 +#define WMAX_METHOD_BRIGHTNESS		0x3 +#define WMAX_METHOD_ZONE_CONTROL	0x4 +#define WMAX_METHOD_HDMI_CABLE		0x5 + +MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>"); +MODULE_DESCRIPTION("Alienware special feature control"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID); +MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID); + +enum INTERFACE_FLAGS { +	LEGACY, +	WMAX, +}; + +enum LEGACY_CONTROL_STATES { +	LEGACY_RUNNING = 1, +	LEGACY_BOOTING = 0, +	LEGACY_SUSPEND = 3, +}; + +enum WMAX_CONTROL_STATES { +	WMAX_RUNNING = 0xFF, +	WMAX_BOOTING = 0, +	WMAX_SUSPEND = 3, +}; + +struct quirk_entry { +	u8 num_zones; +}; + +static struct quirk_entry *quirks; + +static struct quirk_entry quirk_unknown = { +	.num_zones = 2, +}; + +static struct quirk_entry quirk_x51_family = { +	.num_zones = 3, +}; + +static int dmi_matched(const struct dmi_system_id *dmi) +{ +	quirks = dmi->driver_data; +	return 1; +} + +static struct dmi_system_id alienware_quirks[] = { +	{ +	 .callback = dmi_matched, +	 .ident = "Alienware X51 R1", +	 .matches = { +		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), +		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), +		     }, +	 .driver_data = &quirk_x51_family, +	 }, +	{ +	 .callback = dmi_matched, +	 .ident = "Alienware X51 R2", +	 .matches = { +		     DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), +		     DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), +		     }, +	 .driver_data = &quirk_x51_family, +	 }, +	{} +}; + +struct color_platform { +	u8 blue; +	u8 green; +	u8 red; +} __packed; + +struct platform_zone { +	u8 location; +	struct device_attribute *attr; +	struct color_platform colors; +}; + +struct wmax_brightness_args { +	u32 led_mask; +	u32 percentage; +}; + +struct hdmi_args { +	u8 arg; +}; + +struct legacy_led_args { +	struct color_platform colors; +	u8 brightness; +	u8 state; +} __packed; + +struct wmax_led_args { +	u32 led_mask; +	struct color_platform colors; +	u8 state; +} __packed; + +static struct platform_device *platform_device; +static struct device_attribute *zone_dev_attrs; +static struct attribute **zone_attrs; +static struct platform_zone *zone_data; + +static struct platform_driver platform_driver = { +	.driver = { +		   .name = "alienware-wmi", +		   .owner = THIS_MODULE, +		   } +}; + +static struct attribute_group zone_attribute_group = { +	.name = "rgb_zones", +}; + +static u8 interface; +static u8 lighting_control_state; +static u8 global_brightness; + +/* + * Helpers used for zone control +*/ +static int parse_rgb(const char *buf, struct platform_zone *zone) +{ +	long unsigned int rgb; +	int ret; +	union color_union { +		struct color_platform cp; +		int package; +	} repackager; + +	ret = kstrtoul(buf, 16, &rgb); +	if (ret) +		return ret; + +	/* RGB triplet notation is 24-bit hexadecimal */ +	if (rgb > 0xFFFFFF) +		return -EINVAL; + +	repackager.package = rgb & 0x0f0f0f0f; +	pr_debug("alienware-wmi: r: %d g:%d b: %d\n", +		 repackager.cp.red, repackager.cp.green, repackager.cp.blue); +	zone->colors = repackager.cp; +	return 0; +} + +static struct platform_zone *match_zone(struct device_attribute *attr) +{ +	int i; +	for (i = 0; i < quirks->num_zones; i++) { +		if ((struct device_attribute *)zone_data[i].attr == attr) { +			pr_debug("alienware-wmi: matched zone location: %d\n", +				 zone_data[i].location); +			return &zone_data[i]; +		} +	} +	return NULL; +} + +/* + * Individual RGB zone control +*/ +static int alienware_update_led(struct platform_zone *zone) +{ +	int method_id; +	acpi_status status; +	char *guid; +	struct acpi_buffer input; +	struct legacy_led_args legacy_args; +	struct wmax_led_args wmax_args; +	if (interface == WMAX) { +		wmax_args.led_mask = 1 << zone->location; +		wmax_args.colors = zone->colors; +		wmax_args.state = lighting_control_state; +		guid = WMAX_CONTROL_GUID; +		method_id = WMAX_METHOD_ZONE_CONTROL; + +		input.length = (acpi_size) sizeof(wmax_args); +		input.pointer = &wmax_args; +	} else { +		legacy_args.colors = zone->colors; +		legacy_args.brightness = global_brightness; +		legacy_args.state = 0; +		if (lighting_control_state == LEGACY_BOOTING || +		    lighting_control_state == LEGACY_SUSPEND) { +			guid = LEGACY_POWER_CONTROL_GUID; +			legacy_args.state = lighting_control_state; +		} else +			guid = LEGACY_CONTROL_GUID; +		method_id = zone->location + 1; + +		input.length = (acpi_size) sizeof(legacy_args); +		input.pointer = &legacy_args; +	} +	pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); + +	status = wmi_evaluate_method(guid, 1, method_id, &input, NULL); +	if (ACPI_FAILURE(status)) +		pr_err("alienware-wmi: zone set failure: %u\n", status); +	return ACPI_FAILURE(status); +} + +static ssize_t zone_show(struct device *dev, struct device_attribute *attr, +			 char *buf) +{ +	struct platform_zone *target_zone; +	target_zone = match_zone(attr); +	if (target_zone == NULL) +		return sprintf(buf, "red: -1, green: -1, blue: -1\n"); +	return sprintf(buf, "red: %d, green: %d, blue: %d\n", +		       target_zone->colors.red, +		       target_zone->colors.green, target_zone->colors.blue); + +} + +static ssize_t zone_set(struct device *dev, struct device_attribute *attr, +			const char *buf, size_t count) +{ +	struct platform_zone *target_zone; +	int ret; +	target_zone = match_zone(attr); +	if (target_zone == NULL) { +		pr_err("alienware-wmi: invalid target zone\n"); +		return 1; +	} +	ret = parse_rgb(buf, target_zone); +	if (ret) +		return ret; +	ret = alienware_update_led(target_zone); +	return ret ? ret : count; +} + +/* + * LED Brightness (Global) +*/ +static int wmax_brightness(int brightness) +{ +	acpi_status status; +	struct acpi_buffer input; +	struct wmax_brightness_args args = { +		.led_mask = 0xFF, +		.percentage = brightness, +	}; +	input.length = (acpi_size) sizeof(args); +	input.pointer = &args; +	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, +				     WMAX_METHOD_BRIGHTNESS, &input, NULL); +	if (ACPI_FAILURE(status)) +		pr_err("alienware-wmi: brightness set failure: %u\n", status); +	return ACPI_FAILURE(status); +} + +static void global_led_set(struct led_classdev *led_cdev, +			   enum led_brightness brightness) +{ +	int ret; +	global_brightness = brightness; +	if (interface == WMAX) +		ret = wmax_brightness(brightness); +	else +		ret = alienware_update_led(&zone_data[0]); +	if (ret) +		pr_err("LED brightness update failed\n"); +} + +static enum led_brightness global_led_get(struct led_classdev *led_cdev) +{ +	return global_brightness; +} + +static struct led_classdev global_led = { +	.brightness_set = global_led_set, +	.brightness_get = global_led_get, +	.name = "alienware::global_brightness", +}; + +/* + * Lighting control state device attribute (Global) +*/ +static ssize_t show_control_state(struct device *dev, +				  struct device_attribute *attr, char *buf) +{ +	if (lighting_control_state == LEGACY_BOOTING) +		return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n"); +	else if (lighting_control_state == LEGACY_SUSPEND) +		return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n"); +	return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n"); +} + +static ssize_t store_control_state(struct device *dev, +				   struct device_attribute *attr, +				   const char *buf, size_t count) +{ +	long unsigned int val; +	if (strcmp(buf, "booting\n") == 0) +		val = LEGACY_BOOTING; +	else if (strcmp(buf, "suspend\n") == 0) +		val = LEGACY_SUSPEND; +	else if (interface == LEGACY) +		val = LEGACY_RUNNING; +	else +		val = WMAX_RUNNING; +	lighting_control_state = val; +	pr_debug("alienware-wmi: updated control state to %d\n", +		 lighting_control_state); +	return count; +} + +static DEVICE_ATTR(lighting_control_state, 0644, show_control_state, +		   store_control_state); + +static int alienware_zone_init(struct platform_device *dev) +{ +	int i; +	char buffer[10]; +	char *name; + +	if (interface == WMAX) { +		lighting_control_state = WMAX_RUNNING; +	} else if (interface == LEGACY) { +		lighting_control_state = LEGACY_RUNNING; +	} +	global_led.max_brightness = 0x0F; +	global_brightness = global_led.max_brightness; + +	/* +	 *      - zone_dev_attrs num_zones + 1 is for individual zones and then +	 *        null terminated +	 *      - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs + +	 *        the lighting control + null terminated +	 *      - zone_data num_zones is for the distinct zones +	 */ +	zone_dev_attrs = +	    kzalloc(sizeof(struct device_attribute) * (quirks->num_zones + 1), +		    GFP_KERNEL); +	if (!zone_dev_attrs) +		return -ENOMEM; + +	zone_attrs = +	    kzalloc(sizeof(struct attribute *) * (quirks->num_zones + 2), +		    GFP_KERNEL); +	if (!zone_attrs) +		return -ENOMEM; + +	zone_data = +	    kzalloc(sizeof(struct platform_zone) * (quirks->num_zones), +		    GFP_KERNEL); +	if (!zone_data) +		return -ENOMEM; + +	for (i = 0; i < quirks->num_zones; i++) { +		sprintf(buffer, "zone%02X", i); +		name = kstrdup(buffer, GFP_KERNEL); +		if (name == NULL) +			return 1; +		sysfs_attr_init(&zone_dev_attrs[i].attr); +		zone_dev_attrs[i].attr.name = name; +		zone_dev_attrs[i].attr.mode = 0644; +		zone_dev_attrs[i].show = zone_show; +		zone_dev_attrs[i].store = zone_set; +		zone_data[i].location = i; +		zone_attrs[i] = &zone_dev_attrs[i].attr; +		zone_data[i].attr = &zone_dev_attrs[i]; +	} +	zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr; +	zone_attribute_group.attrs = zone_attrs; + +	led_classdev_register(&dev->dev, &global_led); + +	return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group); +} + +static void alienware_zone_exit(struct platform_device *dev) +{ +	sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group); +	led_classdev_unregister(&global_led); +	if (zone_dev_attrs) { +		int i; +		for (i = 0; i < quirks->num_zones; i++) +			kfree(zone_dev_attrs[i].attr.name); +	} +	kfree(zone_dev_attrs); +	kfree(zone_data); +	kfree(zone_attrs); +} + +/* +	The HDMI mux sysfs node indicates the status of the HDMI input mux. +	It can toggle between standard system GPU output and HDMI input. +*/ +static acpi_status alienware_hdmi_command(struct hdmi_args *in_args, +					  u32 command, int *out_data) +{ +	acpi_status status; +	union acpi_object *obj; +	struct acpi_buffer input; +	struct acpi_buffer output; + +	input.length = (acpi_size) sizeof(*in_args); +	input.pointer = in_args; +	if (out_data != NULL) { +		output.length = ACPI_ALLOCATE_BUFFER; +		output.pointer = NULL; +		status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, +					     command, &input, &output); +	} else +		status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, +					     command, &input, NULL); + +	if (ACPI_SUCCESS(status) && out_data != NULL) { +		obj = (union acpi_object *)output.pointer; +		if (obj && obj->type == ACPI_TYPE_INTEGER) +			*out_data = (u32) obj->integer.value; +	} +	return status; + +} + +static ssize_t show_hdmi_cable(struct device *dev, +			       struct device_attribute *attr, char *buf) +{ +	acpi_status status; +	u32 out_data; +	struct hdmi_args in_args = { +		.arg = 0, +	}; +	status = +	    alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_CABLE, +				   (u32 *) &out_data); +	if (ACPI_SUCCESS(status)) { +		if (out_data == 0) +			return scnprintf(buf, PAGE_SIZE, +					 "[unconnected] connected unknown\n"); +		else if (out_data == 1) +			return scnprintf(buf, PAGE_SIZE, +					 "unconnected [connected] unknown\n"); +	} +	pr_err("alienware-wmi: unknown HDMI cable status: %d\n", status); +	return scnprintf(buf, PAGE_SIZE, "unconnected connected [unknown]\n"); +} + +static ssize_t show_hdmi_source(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	acpi_status status; +	u32 out_data; +	struct hdmi_args in_args = { +		.arg = 0, +	}; +	status = +	    alienware_hdmi_command(&in_args, WMAX_METHOD_HDMI_STATUS, +				   (u32 *) &out_data); + +	if (ACPI_SUCCESS(status)) { +		if (out_data == 1) +			return scnprintf(buf, PAGE_SIZE, +					 "[input] gpu unknown\n"); +		else if (out_data == 2) +			return scnprintf(buf, PAGE_SIZE, +					 "input [gpu] unknown\n"); +	} +	pr_err("alienware-wmi: unknown HDMI source status: %d\n", out_data); +	return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n"); +} + +static ssize_t toggle_hdmi_source(struct device *dev, +				  struct device_attribute *attr, +				  const char *buf, size_t count) +{ +	acpi_status status; +	struct hdmi_args args; +	if (strcmp(buf, "gpu\n") == 0) +		args.arg = 1; +	else if (strcmp(buf, "input\n") == 0) +		args.arg = 2; +	else +		args.arg = 3; +	pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); + +	status = alienware_hdmi_command(&args, WMAX_METHOD_HDMI_SOURCE, NULL); + +	if (ACPI_FAILURE(status)) +		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", +		       status); +	return count; +} + +static DEVICE_ATTR(cable, S_IRUGO, show_hdmi_cable, NULL); +static DEVICE_ATTR(source, S_IRUGO | S_IWUSR, show_hdmi_source, +		   toggle_hdmi_source); + +static struct attribute *hdmi_attrs[] = { +	&dev_attr_cable.attr, +	&dev_attr_source.attr, +	NULL, +}; + +static struct attribute_group hdmi_attribute_group = { +	.name = "hdmi", +	.attrs = hdmi_attrs, +}; + +static void remove_hdmi(struct platform_device *dev) +{ +	sysfs_remove_group(&dev->dev.kobj, &hdmi_attribute_group); +} + +static int create_hdmi(struct platform_device *dev) +{ +	int ret; + +	ret = sysfs_create_group(&dev->dev.kobj, &hdmi_attribute_group); +	if (ret) +		goto error_create_hdmi; +	return 0; + +error_create_hdmi: +	remove_hdmi(dev); +	return ret; +} + +static int __init alienware_wmi_init(void) +{ +	int ret; + +	if (wmi_has_guid(LEGACY_CONTROL_GUID)) +		interface = LEGACY; +	else if (wmi_has_guid(WMAX_CONTROL_GUID)) +		interface = WMAX; +	else { +		pr_warn("alienware-wmi: No known WMI GUID found\n"); +		return -ENODEV; +	} + +	dmi_check_system(alienware_quirks); +	if (quirks == NULL) +		quirks = &quirk_unknown; + +	ret = platform_driver_register(&platform_driver); +	if (ret) +		goto fail_platform_driver; +	platform_device = platform_device_alloc("alienware-wmi", -1); +	if (!platform_device) { +		ret = -ENOMEM; +		goto fail_platform_device1; +	} +	ret = platform_device_add(platform_device); +	if (ret) +		goto fail_platform_device2; + +	if (interface == WMAX) { +		ret = create_hdmi(platform_device); +		if (ret) +			goto fail_prep_hdmi; +	} + +	ret = alienware_zone_init(platform_device); +	if (ret) +		goto fail_prep_zones; + +	return 0; + +fail_prep_zones: +	alienware_zone_exit(platform_device); +fail_prep_hdmi: +	platform_device_del(platform_device); +fail_platform_device2: +	platform_device_put(platform_device); +fail_platform_device1: +	platform_driver_unregister(&platform_driver); +fail_platform_driver: +	return ret; +} + +module_init(alienware_wmi_init); + +static void __exit alienware_wmi_exit(void) +{ +	if (platform_device) { +		alienware_zone_exit(platform_device); +		remove_hdmi(platform_device); +		platform_device_unregister(platform_device); +		platform_driver_unregister(&platform_driver); +	} +} + +module_exit(alienware_wmi_exit); diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 8eea2efbbb6..b9429fbf1cd 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -289,7 +289,7 @@ static int gmux_switchto(enum vga_switcheroo_client_id id)  static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,  				   enum vga_switcheroo_state state)  { -	INIT_COMPLETION(gmux_data->powerchange_done); +	reinit_completion(&gmux_data->powerchange_done);  	if (state == VGA_SWITCHEROO_ON) {  		gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1); @@ -519,7 +519,7 @@ static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)  	gmux_data->power_state = VGA_SWITCHEROO_ON; -	gmux_data->dhandle = DEVICE_ACPI_HANDLE(&pnp->dev); +	gmux_data->dhandle = ACPI_HANDLE(&pnp->dev);  	if (!gmux_data->dhandle) {  		pr_err("Cannot find acpi handle for pnp device %s\n",  		       dev_name(&pnp->dev)); diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 0e9c169b42f..7f4dc6f51f8 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -53,8 +53,7 @@  #include <linux/rfkill.h>  #include <linux/slab.h>  #include <linux/dmi.h> -#include <acpi/acpi_drivers.h> -#include <acpi/acpi_bus.h> +#include <linux/acpi.h>  #define ASUS_LAPTOP_VERSION	"0.42" @@ -1494,10 +1493,9 @@ static int asus_input_init(struct asus_laptop *asus)  	int error;  	input = input_allocate_device(); -	if (!input) { -		pr_warn("Unable to allocate input device\n"); +	if (!input)  		return -ENOMEM; -	} +  	input->name = "Asus Laptop extra buttons";  	input->phys = ASUS_LAPTOP_FILE "/input0";  	input->id.bustype = BUS_HOST; diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 563f59efa66..ddf0eefd862 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -137,6 +137,15 @@ static struct dmi_system_id asus_quirks[] = {  	},  	{  		.callback = dmi_matched, +		.ident = "ASUSTeK COMPUTER INC. X550CA", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), +			DMI_MATCH(DMI_PRODUCT_NAME, "X550CA"), +		}, +		.driver_data = &quirk_asus_x401u, +	}, +	{ +		.callback = dmi_matched,  		.ident = "ASUSTeK COMPUTER INC. X55A",  		.matches = {  			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 19c313b056c..3c6ccedc82b 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -45,8 +45,7 @@  #include <linux/seq_file.h>  #include <linux/platform_device.h>  #include <linux/thermal.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  #include <acpi/video.h>  #include "asus-wmi.h" @@ -184,7 +183,6 @@ struct asus_wmi {  	struct input_dev *inputdev;  	struct backlight_device *backlight_device; -	struct device *hwmon_device;  	struct platform_device *platform_device;  	struct led_classdev wlan_led; @@ -268,7 +266,7 @@ static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,  	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };  	acpi_status status;  	union acpi_object *obj; -	u32 tmp; +	u32 tmp = 0;  	status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, method_id,  				     &input, &output); @@ -279,8 +277,6 @@ static int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1,  	obj = (union acpi_object *)output.pointer;  	if (obj && obj->type == ACPI_TYPE_INTEGER)  		tmp = (u32) obj->integer.value; -	else -		tmp = 0;  	if (retval)  		*retval = tmp; @@ -606,6 +602,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)  	mutex_unlock(&asus->wmi_lock);  	mutex_lock(&asus->hotplug_lock); +	pci_lock_rescan_remove();  	if (asus->wlan.rfkill)  		rfkill_set_sw_state(asus->wlan.rfkill, blocked); @@ -643,8 +640,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)  			dev = pci_scan_single_device(bus, 0);  			if (dev) {  				pci_bus_assign_resources(bus); -				if (pci_bus_add_device(dev)) -					pr_err("Unable to hotplug wifi\n"); +				pci_bus_add_device(dev);  			}  		} else {  			dev = pci_get_slot(bus, 0); @@ -656,6 +652,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)  	}  out_unlock: +	pci_unlock_rescan_remove();  	mutex_unlock(&asus->hotplug_lock);  } @@ -1071,20 +1068,12 @@ static ssize_t asus_hwmon_temp1(struct device *dev,  	return sprintf(buf, "%d\n", value);  } -static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL, 0); -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL, 0); - -static ssize_t -show_name(struct device *dev, struct device_attribute *attr, char *buf) -{ -	return sprintf(buf, "asus\n"); -} -static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); +static DEVICE_ATTR(pwm1, S_IRUGO, asus_hwmon_pwm1, NULL); +static DEVICE_ATTR(temp1_input, S_IRUGO, asus_hwmon_temp1, NULL);  static struct attribute *hwmon_attributes[] = { -	&sensor_dev_attr_pwm1.dev_attr.attr, -	&sensor_dev_attr_temp1_input.dev_attr.attr, -	&sensor_dev_attr_name.dev_attr.attr, +	&dev_attr_pwm1.attr, +	&dev_attr_temp1_input.attr,  	NULL  }; @@ -1098,9 +1087,9 @@ static umode_t asus_hwmon_sysfs_is_visible(struct kobject *kobj,  	int dev_id = -1;  	u32 value = ASUS_WMI_UNSUPPORTED_METHOD; -	if (attr == &sensor_dev_attr_pwm1.dev_attr.attr) +	if (attr == &dev_attr_pwm1.attr)  		dev_id = ASUS_WMI_DEVID_FAN_CTRL; -	else if (attr == &sensor_dev_attr_temp1_input.dev_attr.attr) +	else if (attr == &dev_attr_temp1_input.attr)  		dev_id = ASUS_WMI_DEVID_THERMAL_CTRL;  	if (dev_id != -1) { @@ -1135,35 +1124,20 @@ static struct attribute_group hwmon_attribute_group = {  	.is_visible = asus_hwmon_sysfs_is_visible,  	.attrs = hwmon_attributes  }; - -static void asus_wmi_hwmon_exit(struct asus_wmi *asus) -{ -	struct device *hwmon; - -	hwmon = asus->hwmon_device; -	if (!hwmon) -		return; -	sysfs_remove_group(&hwmon->kobj, &hwmon_attribute_group); -	hwmon_device_unregister(hwmon); -	asus->hwmon_device = NULL; -} +__ATTRIBUTE_GROUPS(hwmon_attribute);  static int asus_wmi_hwmon_init(struct asus_wmi *asus)  {  	struct device *hwmon; -	int result; -	hwmon = hwmon_device_register(&asus->platform_device->dev); +	hwmon = hwmon_device_register_with_groups(&asus->platform_device->dev, +						  "asus", asus, +						  hwmon_attribute_groups);  	if (IS_ERR(hwmon)) {  		pr_err("Could not register asus hwmon device\n");  		return PTR_ERR(hwmon);  	} -	dev_set_drvdata(hwmon, asus); -	asus->hwmon_device = hwmon; -	result = sysfs_create_group(&hwmon->kobj, &hwmon_attribute_group); -	if (result) -		asus_wmi_hwmon_exit(asus); -	return result; +	return 0;  }  /* @@ -1834,7 +1808,6 @@ fail_backlight:  fail_rfkill:  	asus_wmi_led_exit(asus);  fail_leds: -	asus_wmi_hwmon_exit(asus);  fail_hwmon:  	asus_wmi_input_exit(asus);  fail_input: @@ -1852,7 +1825,6 @@ static int asus_wmi_remove(struct platform_device *device)  	wmi_remove_notify_handler(asus->driver->event_guid);  	asus_wmi_backlight_exit(asus);  	asus_wmi_input_exit(asus); -	asus_wmi_hwmon_exit(asus);  	asus_wmi_led_exit(asus);  	asus_wmi_rfkill_exit(asus);  	asus_wmi_debugfs_exit(asus); diff --git a/drivers/platform/x86/chromeos_laptop.c b/drivers/platform/x86/chromeos_laptop.c deleted file mode 100644 index 3e5b4497a1d..00000000000 --- a/drivers/platform/x86/chromeos_laptop.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - *  chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus devices. - * - *  Author : Benson Leung <bleung@chromium.org> - * - *  Copyright (C) 2012 Google, Inc. - * - *  This program is free software; you can redistribute it and/or modify - *  it under the terms of the GNU General Public License as published by - *  the Free Software Foundation; either version 2 of the License, or - *  (at your option) any later version. - * - *  This program is distributed in the hope that it will be useful, - *  but WITHOUT ANY WARRANTY; without even the implied warranty of - *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the - *  GNU General Public License for more details. - * - *  You should have received a copy of the GNU General Public License - *  along with this program; if not, write to the Free Software - *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA - * - */ - -#include <linux/dmi.h> -#include <linux/i2c.h> -#include <linux/i2c/atmel_mxt_ts.h> -#include <linux/input.h> -#include <linux/interrupt.h> -#include <linux/module.h> - -#define ATMEL_TP_I2C_ADDR	0x4b -#define ATMEL_TP_I2C_BL_ADDR	0x25 -#define ATMEL_TS_I2C_ADDR	0x4a -#define ATMEL_TS_I2C_BL_ADDR	0x26 -#define CYAPA_TP_I2C_ADDR	0x67 -#define ISL_ALS_I2C_ADDR	0x44 -#define TAOS_ALS_I2C_ADDR	0x29 - -static struct i2c_client *als; -static struct i2c_client *tp; -static struct i2c_client *ts; - -const char *i2c_adapter_names[] = { -	"SMBus I801 adapter", -	"i915 gmbus vga", -	"i915 gmbus panel", -}; - -/* Keep this enum consistent with i2c_adapter_names */ -enum i2c_adapter_type { -	I2C_ADAPTER_SMBUS = 0, -	I2C_ADAPTER_VGADDC, -	I2C_ADAPTER_PANEL, -}; - -static struct i2c_board_info __initdata cyapa_device = { -	I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), -	.flags		= I2C_CLIENT_WAKE, -}; - -static struct i2c_board_info __initdata isl_als_device = { -	I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), -}; - -static struct i2c_board_info __initdata tsl2583_als_device = { -	I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), -}; - -static struct i2c_board_info __initdata tsl2563_als_device = { -	I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), -}; - -static struct mxt_platform_data atmel_224s_tp_platform_data = { -	.x_line			= 18, -	.y_line			= 12, -	.x_size			= 102*20, -	.y_size			= 68*20, -	.blen			= 0x80,	/* Gain setting is in upper 4 bits */ -	.threshold		= 0x32, -	.voltage		= 0,	/* 3.3V */ -	.orient			= MXT_VERTICAL_FLIP, -	.irqflags		= IRQF_TRIGGER_FALLING, -	.is_tp			= true, -	.key_map		= { KEY_RESERVED, -				    KEY_RESERVED, -				    KEY_RESERVED, -				    BTN_LEFT }, -	.config			= NULL, -	.config_length		= 0, -}; - -static struct i2c_board_info __initdata atmel_224s_tp_device = { -	I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR), -	.platform_data = &atmel_224s_tp_platform_data, -	.flags		= I2C_CLIENT_WAKE, -}; - -static struct mxt_platform_data atmel_1664s_platform_data = { -	.x_line			= 32, -	.y_line			= 50, -	.x_size			= 1700, -	.y_size			= 2560, -	.blen			= 0x89,	/* Gain setting is in upper 4 bits */ -	.threshold		= 0x28, -	.voltage		= 0,	/* 3.3V */ -	.orient			= MXT_ROTATED_90_COUNTER, -	.irqflags		= IRQF_TRIGGER_FALLING, -	.is_tp			= false, -	.config			= NULL, -	.config_length		= 0, -}; - -static struct i2c_board_info __initdata atmel_1664s_device = { -	I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), -	.platform_data = &atmel_1664s_platform_data, -	.flags		= I2C_CLIENT_WAKE, -}; - -static struct i2c_client __init *__add_probed_i2c_device( -		const char *name, -		int bus, -		struct i2c_board_info *info, -		const unsigned short *addrs) -{ -	const struct dmi_device *dmi_dev; -	const struct dmi_dev_onboard *dev_data; -	struct i2c_adapter *adapter; -	struct i2c_client *client; - -	if (bus < 0) -		return NULL; -	/* -	 * If a name is specified, look for irq platform information stashed -	 * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware. -	 */ -	if (name) { -		dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL); -		if (!dmi_dev) { -			pr_err("%s failed to dmi find device %s.\n", -			       __func__, -			       name); -			return NULL; -		} -		dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data; -		if (!dev_data) { -			pr_err("%s failed to get data from dmi for %s.\n", -			       __func__, name); -			return NULL; -		} -		info->irq = dev_data->instance; -	} - -	adapter = i2c_get_adapter(bus); -	if (!adapter) { -		pr_err("%s failed to get i2c adapter %d.\n", __func__, bus); -		return NULL; -	} - -	/* add the i2c device */ -	client = i2c_new_probed_device(adapter, info, addrs, NULL); -	if (!client) -		pr_err("%s failed to register device %d-%02x\n", -		       __func__, bus, info->addr); -	else -		pr_debug("%s added i2c device %d-%02x\n", -			 __func__, bus, info->addr); - -	i2c_put_adapter(adapter); -	return client; -} - -static int __init __find_i2c_adap(struct device *dev, void *data) -{ -	const char *name = data; -	static const char *prefix = "i2c-"; -	struct i2c_adapter *adapter; -	if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) -		return 0; -	adapter = to_i2c_adapter(dev); -	return (strncmp(adapter->name, name, strlen(name)) == 0); -} - -static int __init find_i2c_adapter_num(enum i2c_adapter_type type) -{ -	struct device *dev = NULL; -	struct i2c_adapter *adapter; -	const char *name = i2c_adapter_names[type]; -	/* find the adapter by name */ -	dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, -			      __find_i2c_adap); -	if (!dev) { -		pr_err("%s: i2c adapter %s not found on system.\n", __func__, -		       name); -		return -ENODEV; -	} -	adapter = to_i2c_adapter(dev); -	return adapter->nr; -} - -/* - * Takes a list of addresses in addrs as such : - * { addr1, ... , addrn, I2C_CLIENT_END }; - * add_probed_i2c_device will use i2c_new_probed_device - * and probe for devices at all of the addresses listed. - * Returns NULL if no devices found. - * See Documentation/i2c/instantiating-devices for more information. - */ -static __init struct i2c_client *add_probed_i2c_device( -		const char *name, -		enum i2c_adapter_type type, -		struct i2c_board_info *info, -		const unsigned short *addrs) -{ -	return __add_probed_i2c_device(name, -				       find_i2c_adapter_num(type), -				       info, -				       addrs); -} - -/* - * Probes for a device at a single address, the one provided by - * info->addr. - * Returns NULL if no device found. - */ -static __init struct i2c_client *add_i2c_device(const char *name, -						enum i2c_adapter_type type, -						struct i2c_board_info *info) -{ -	const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; -	return __add_probed_i2c_device(name, -				       find_i2c_adapter_num(type), -				       info, -				       addr_list); -} - - -static struct i2c_client __init *add_smbus_device(const char *name, -						  struct i2c_board_info *info) -{ -	return add_i2c_device(name, I2C_ADAPTER_SMBUS, info); -} - -static int __init setup_cyapa_smbus_tp(const struct dmi_system_id *id) -{ -	/* add cyapa touchpad on smbus */ -	tp = add_smbus_device("trackpad", &cyapa_device); -	return 0; -} - -static int __init setup_atmel_224s_tp(const struct dmi_system_id *id) -{ -	const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR, -					     ATMEL_TP_I2C_ADDR, -					     I2C_CLIENT_END }; - -	/* add atmel mxt touchpad on VGA DDC GMBus */ -	tp = add_probed_i2c_device("trackpad", I2C_ADAPTER_VGADDC, -				   &atmel_224s_tp_device, addr_list); -	return 0; -} - -static int __init setup_atmel_1664s_ts(const struct dmi_system_id *id) -{ -	const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR, -					     ATMEL_TS_I2C_ADDR, -					     I2C_CLIENT_END }; - -	/* add atmel mxt touch device on PANEL GMBus */ -	ts = add_probed_i2c_device("touchscreen", I2C_ADAPTER_PANEL, -				   &atmel_1664s_device, addr_list); -	return 0; -} - - -static int __init setup_isl29018_als(const struct dmi_system_id *id) -{ -	/* add isl29018 light sensor */ -	als = add_smbus_device("lightsensor", &isl_als_device); -	return 0; -} - -static int __init setup_isl29023_als(const struct dmi_system_id *id) -{ -	/* add isl29023 light sensor on Panel GMBus */ -	als = add_i2c_device("lightsensor", I2C_ADAPTER_PANEL, -			     &isl_als_device); -	return 0; -} - -static int __init setup_tsl2583_als(const struct dmi_system_id *id) -{ -	/* add tsl2583 light sensor on smbus */ -	als = add_smbus_device(NULL, &tsl2583_als_device); -	return 0; -} - -static int __init setup_tsl2563_als(const struct dmi_system_id *id) -{ -	/* add tsl2563 light sensor on smbus */ -	als = add_smbus_device(NULL, &tsl2563_als_device); -	return 0; -} - -static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = { -	{ -		.ident = "Samsung Series 5 550 - Touchpad", -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"), -			DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"), -		}, -		.callback = setup_cyapa_smbus_tp, -	}, -	{ -		.ident = "Chromebook Pixel - Touchscreen", -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), -			DMI_MATCH(DMI_PRODUCT_NAME, "Link"), -		}, -		.callback = setup_atmel_1664s_ts, -	}, -	{ -		.ident = "Chromebook Pixel - Touchpad", -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), -			DMI_MATCH(DMI_PRODUCT_NAME, "Link"), -		}, -		.callback = setup_atmel_224s_tp, -	}, -	{ -		.ident = "Samsung Series 5 550 - Light Sensor", -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"), -			DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"), -		}, -		.callback = setup_isl29018_als, -	}, -	{ -		.ident = "Chromebook Pixel - Light Sensor", -		.matches = { -			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), -			DMI_MATCH(DMI_PRODUCT_NAME, "Link"), -		}, -		.callback = setup_isl29023_als, -	}, -	{ -		.ident = "Acer C7 Chromebook - Touchpad", -		.matches = { -			DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"), -		}, -		.callback = setup_cyapa_smbus_tp, -	}, -	{ -		.ident = "HP Pavilion 14 Chromebook - Touchpad", -		.matches = { -			DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"), -		}, -		.callback = setup_cyapa_smbus_tp, -	}, -	{ -		.ident = "Samsung Series 5 - Light Sensor", -		.matches = { -			DMI_MATCH(DMI_PRODUCT_NAME, "Alex"), -		}, -		.callback = setup_tsl2583_als, -	}, -	{ -		.ident = "Cr-48 - Light Sensor", -		.matches = { -			DMI_MATCH(DMI_PRODUCT_NAME, "Mario"), -		}, -		.callback = setup_tsl2563_als, -	}, -	{ -		.ident = "Acer AC700 - Light Sensor", -		.matches = { -			DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), -		}, -		.callback = setup_tsl2563_als, -	}, -	{ } -}; -MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); - -static int __init chromeos_laptop_init(void) -{ -	if (!dmi_check_system(chromeos_laptop_dmi_table)) { -		pr_debug("%s unsupported system.\n", __func__); -		return -ENODEV; -	} -	return 0; -} - -static void __exit chromeos_laptop_exit(void) -{ -	if (als) -		i2c_unregister_device(als); -	if (tp) -		i2c_unregister_device(tp); -	if (ts) -		i2c_unregister_device(ts); -} - -module_init(chromeos_laptop_init); -module_exit(chromeos_laptop_exit); - -MODULE_DESCRIPTION("Chrome OS Laptop driver"); -MODULE_AUTHOR("Benson Leung <bleung@chromium.org>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 6dfa8d3b4ee..70d355a9ae2 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -21,14 +21,13 @@  #include <linux/module.h>  #include <linux/slab.h>  #include <linux/workqueue.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  #include <linux/backlight.h>  #include <linux/input.h>  #include <linux/rfkill.h>  MODULE_LICENSE("GPL"); -  struct cmpc_accel {  	int sensitivity;  	int g_select; diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index eaa78edb1f4..7297df2ebf5 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -173,8 +173,7 @@  /* ======= */  struct compal_data{  	/* Fan control */ -	struct device *hwmon_dev; -	int pwm_enable; /* 0:full on, 1:set by pwm1, 2:control by moterboard */ +	int pwm_enable; /* 0:full on, 1:set by pwm1, 2:control by motherboard */  	unsigned char curr_pwm;  	/* Power supply */ @@ -402,15 +401,6 @@ SIMPLE_MASKED_STORE_SHOW(wake_up_wlan,	WAKE_UP_ADDR, WAKE_UP_WLAN)  SIMPLE_MASKED_STORE_SHOW(wake_up_key,	WAKE_UP_ADDR, WAKE_UP_KEY)  SIMPLE_MASKED_STORE_SHOW(wake_up_mouse,	WAKE_UP_ADDR, WAKE_UP_MOUSE) - -/* General hwmon interface */ -static ssize_t hwmon_name_show(struct device *dev, -		struct device_attribute *attr, char *buf) -{ -	return sprintf(buf, "%s\n", DRIVER_NAME); -} - -  /* Fan control interface */  static ssize_t pwm_enable_show(struct device *dev,  		struct device_attribute *attr, char *buf) @@ -665,55 +655,55 @@ static DEVICE_ATTR(wake_up_key,  static DEVICE_ATTR(wake_up_mouse,  		0644, wake_up_mouse_show,	wake_up_mouse_store); -static SENSOR_DEVICE_ATTR(name,        S_IRUGO, hwmon_name_show,   NULL, 1); -static SENSOR_DEVICE_ATTR(fan1_input,  S_IRUGO, fan_show,          NULL, 1); -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu,          NULL, 1); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local,    NULL, 1); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS,      NULL, 1); -static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge,  NULL, 1); -static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga,          NULL, 1); -static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN,         NULL, 1); -static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu,         NULL, 1); -static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local,   NULL, 1); -static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS,     NULL, 1); -static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL, 1); -static SENSOR_DEVICE_ATTR(temp5_label, S_IRUGO, label_vga,         NULL, 1); -static SENSOR_DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN,        NULL, 1); -static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store, 1); -static SENSOR_DEVICE_ATTR(pwm1_enable, -		S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store, 0); - -static struct attribute *compal_attributes[] = { +static DEVICE_ATTR(fan1_input,  S_IRUGO, fan_show,          NULL); +static DEVICE_ATTR(temp1_input, S_IRUGO, temp_cpu,          NULL); +static DEVICE_ATTR(temp2_input, S_IRUGO, temp_cpu_local,    NULL); +static DEVICE_ATTR(temp3_input, S_IRUGO, temp_cpu_DTS,      NULL); +static DEVICE_ATTR(temp4_input, S_IRUGO, temp_northbridge,  NULL); +static DEVICE_ATTR(temp5_input, S_IRUGO, temp_vga,          NULL); +static DEVICE_ATTR(temp6_input, S_IRUGO, temp_SKIN,         NULL); +static DEVICE_ATTR(temp1_label, S_IRUGO, label_cpu,         NULL); +static DEVICE_ATTR(temp2_label, S_IRUGO, label_cpu_local,   NULL); +static DEVICE_ATTR(temp3_label, S_IRUGO, label_cpu_DTS,     NULL); +static DEVICE_ATTR(temp4_label, S_IRUGO, label_northbridge, NULL); +static DEVICE_ATTR(temp5_label, S_IRUGO, label_vga,         NULL); +static DEVICE_ATTR(temp6_label, S_IRUGO, label_SKIN,        NULL); +static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, pwm_show, pwm_store); +static DEVICE_ATTR(pwm1_enable, +		   S_IRUGO | S_IWUSR, pwm_enable_show, pwm_enable_store); + +static struct attribute *compal_platform_attrs[] = {  	&dev_attr_wake_up_pme.attr,  	&dev_attr_wake_up_modem.attr,  	&dev_attr_wake_up_lan.attr,  	&dev_attr_wake_up_wlan.attr,  	&dev_attr_wake_up_key.attr,  	&dev_attr_wake_up_mouse.attr, -	/* Maybe put the sensor-stuff in a separate hwmon-driver? That way, -	 * the hwmon sysfs won't be cluttered with the above files. */ -	&sensor_dev_attr_name.dev_attr.attr, -	&sensor_dev_attr_pwm1_enable.dev_attr.attr, -	&sensor_dev_attr_pwm1.dev_attr.attr, -	&sensor_dev_attr_fan1_input.dev_attr.attr, -	&sensor_dev_attr_temp1_input.dev_attr.attr, -	&sensor_dev_attr_temp2_input.dev_attr.attr, -	&sensor_dev_attr_temp3_input.dev_attr.attr, -	&sensor_dev_attr_temp4_input.dev_attr.attr, -	&sensor_dev_attr_temp5_input.dev_attr.attr, -	&sensor_dev_attr_temp6_input.dev_attr.attr, -	&sensor_dev_attr_temp1_label.dev_attr.attr, -	&sensor_dev_attr_temp2_label.dev_attr.attr, -	&sensor_dev_attr_temp3_label.dev_attr.attr, -	&sensor_dev_attr_temp4_label.dev_attr.attr, -	&sensor_dev_attr_temp5_label.dev_attr.attr, -	&sensor_dev_attr_temp6_label.dev_attr.attr,  	NULL  }; +static struct attribute_group compal_platform_attr_group = { +	.attrs = compal_platform_attrs +}; -static struct attribute_group compal_attribute_group = { -	.attrs = compal_attributes +static struct attribute *compal_hwmon_attrs[] = { +	&dev_attr_pwm1_enable.attr, +	&dev_attr_pwm1.attr, +	&dev_attr_fan1_input.attr, +	&dev_attr_temp1_input.attr, +	&dev_attr_temp2_input.attr, +	&dev_attr_temp3_input.attr, +	&dev_attr_temp4_input.attr, +	&dev_attr_temp5_input.attr, +	&dev_attr_temp6_input.attr, +	&dev_attr_temp1_label.attr, +	&dev_attr_temp2_label.attr, +	&dev_attr_temp3_label.attr, +	&dev_attr_temp4_label.attr, +	&dev_attr_temp5_label.attr, +	&dev_attr_temp6_label.attr, +	NULL  }; +ATTRIBUTE_GROUPS(compal_hwmon);  static int compal_probe(struct platform_device *);  static int compal_remove(struct platform_device *); @@ -1021,30 +1011,28 @@ static int compal_probe(struct platform_device *pdev)  {  	int err;  	struct compal_data *data; +	struct device *hwmon_dev;  	if (!extra_features)  		return 0;  	/* Fan control */ -	data = kzalloc(sizeof(struct compal_data), GFP_KERNEL); +	data = devm_kzalloc(&pdev->dev, sizeof(struct compal_data), GFP_KERNEL);  	if (!data)  		return -ENOMEM;  	initialize_fan_control_data(data); -	err = sysfs_create_group(&pdev->dev.kobj, &compal_attribute_group); -	if (err) { -		kfree(data); +	err = sysfs_create_group(&pdev->dev.kobj, &compal_platform_attr_group); +	if (err)  		return err; -	} -	data->hwmon_dev = hwmon_device_register(&pdev->dev); -	if (IS_ERR(data->hwmon_dev)) { -		err = PTR_ERR(data->hwmon_dev); -		sysfs_remove_group(&pdev->dev.kobj, -				&compal_attribute_group); -		kfree(data); -		return err; +	hwmon_dev = hwmon_device_register_with_groups(&pdev->dev, +						      DRIVER_NAME, data, +						      compal_hwmon_groups); +	if (IS_ERR(hwmon_dev)) { +		err = PTR_ERR(hwmon_dev); +		goto remove;  	}  	/* Power supply */ @@ -1054,6 +1042,10 @@ static int compal_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, data);  	return 0; + +remove: +	sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group); +	return err;  }  static void __exit compal_cleanup(void) @@ -1080,12 +1072,9 @@ static int compal_remove(struct platform_device *pdev)  	pwm_disable_control();  	data = platform_get_drvdata(pdev); -	hwmon_device_unregister(data->hwmon_dev);  	power_supply_unregister(&data->psy); -	kfree(data); - -	sysfs_remove_group(&pdev->dev.kobj, &compal_attribute_group); +	sysfs_remove_group(&pdev->dev.kobj, &compal_platform_attr_group);  	return 0;  } diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index bb77e18b3dd..fed4111ac31 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -21,6 +21,7 @@  #include <linux/err.h>  #include <linux/dmi.h>  #include <linux/io.h> +#include <linux/rfkill.h>  #include <linux/power_supply.h>  #include <linux/acpi.h>  #include <linux/mm.h> @@ -89,6 +90,13 @@ static struct platform_driver platform_driver = {  static struct platform_device *platform_device;  static struct backlight_device *dell_backlight_device; +static struct rfkill *wifi_rfkill; +static struct rfkill *bluetooth_rfkill; +static struct rfkill *wwan_rfkill; +static bool force_rfkill; + +module_param(force_rfkill, bool, 0444); +MODULE_PARM_DESC(force_rfkill, "enable rfkill on non whitelisted models");  static const struct dmi_system_id dell_device_table[] __initconst = {  	{ @@ -355,6 +363,108 @@ dell_send_request(struct calling_interface_buffer *buffer, int class,  	return buffer;  } +/* Derived from information in DellWirelessCtl.cpp: +   Class 17, select 11 is radio control. It returns an array of 32-bit values. + +   Input byte 0 = 0: Wireless information + +   result[0]: return code +   result[1]: +     Bit 0:      Hardware switch supported +     Bit 1:      Wifi locator supported +     Bit 2:      Wifi is supported +     Bit 3:      Bluetooth is supported +     Bit 4:      WWAN is supported +     Bit 5:      Wireless keyboard supported +     Bits 6-7:   Reserved +     Bit 8:      Wifi is installed +     Bit 9:      Bluetooth is installed +     Bit 10:     WWAN is installed +     Bits 11-15: Reserved +     Bit 16:     Hardware switch is on +     Bit 17:     Wifi is blocked +     Bit 18:     Bluetooth is blocked +     Bit 19:     WWAN is blocked +     Bits 20-31: Reserved +   result[2]: NVRAM size in bytes +   result[3]: NVRAM format version number + +   Input byte 0 = 2: Wireless switch configuration +   result[0]: return code +   result[1]: +     Bit 0:      Wifi controlled by switch +     Bit 1:      Bluetooth controlled by switch +     Bit 2:      WWAN controlled by switch +     Bits 3-6:   Reserved +     Bit 7:      Wireless switch config locked +     Bit 8:      Wifi locator enabled +     Bits 9-14:  Reserved +     Bit 15:     Wifi locator setting locked +     Bits 16-31: Reserved +*/ + +static int dell_rfkill_set(void *data, bool blocked) +{ +	int disable = blocked ? 1 : 0; +	unsigned long radio = (unsigned long)data; +	int hwswitch_bit = (unsigned long)data - 1; + +	get_buffer(); +	dell_send_request(buffer, 17, 11); + +	/* If the hardware switch controls this radio, and the hardware +	   switch is disabled, always disable the radio */ +	if ((hwswitch_state & BIT(hwswitch_bit)) && +	    !(buffer->output[1] & BIT(16))) +		disable = 1; + +	buffer->input[0] = (1 | (radio<<8) | (disable << 16)); +	dell_send_request(buffer, 17, 11); + +	release_buffer(); +	return 0; +} + +/* Must be called with the buffer held */ +static void dell_rfkill_update_sw_state(struct rfkill *rfkill, int radio, +					int status) +{ +	if (status & BIT(0)) { +		/* Has hw-switch, sync sw_state to BIOS */ +		int block = rfkill_blocked(rfkill); +		buffer->input[0] = (1 | (radio << 8) | (block << 16)); +		dell_send_request(buffer, 17, 11); +	} else { +		/* No hw-switch, sync BIOS state to sw_state */ +		rfkill_set_sw_state(rfkill, !!(status & BIT(radio + 16))); +	} +} + +static void dell_rfkill_update_hw_state(struct rfkill *rfkill, int radio, +					int status) +{ +	if (hwswitch_state & (BIT(radio - 1))) +		rfkill_set_hw_state(rfkill, !(status & BIT(16))); +} + +static void dell_rfkill_query(struct rfkill *rfkill, void *data) +{ +	int status; + +	get_buffer(); +	dell_send_request(buffer, 17, 11); +	status = buffer->output[1]; + +	dell_rfkill_update_hw_state(rfkill, (unsigned long)data, status); + +	release_buffer(); +} + +static const struct rfkill_ops dell_rfkill_ops = { +	.set_block = dell_rfkill_set, +	.query = dell_rfkill_query, +}; +  static struct dentry *dell_laptop_dir;  static int dell_debugfs_show(struct seq_file *s, void *data) @@ -424,6 +534,171 @@ static const struct file_operations dell_debugfs_fops = {  	.release = single_release,  }; +static void dell_update_rfkill(struct work_struct *ignored) +{ +	int status; + +	get_buffer(); +	dell_send_request(buffer, 17, 11); +	status = buffer->output[1]; + +	if (wifi_rfkill) { +		dell_rfkill_update_hw_state(wifi_rfkill, 1, status); +		dell_rfkill_update_sw_state(wifi_rfkill, 1, status); +	} +	if (bluetooth_rfkill) { +		dell_rfkill_update_hw_state(bluetooth_rfkill, 2, status); +		dell_rfkill_update_sw_state(bluetooth_rfkill, 2, status); +	} +	if (wwan_rfkill) { +		dell_rfkill_update_hw_state(wwan_rfkill, 3, status); +		dell_rfkill_update_sw_state(wwan_rfkill, 3, status); +	} + +	release_buffer(); +} +static DECLARE_DELAYED_WORK(dell_rfkill_work, dell_update_rfkill); + +static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, +			      struct serio *port) +{ +	static bool extended; + +	if (str & 0x20) +		return false; + +	if (unlikely(data == 0xe0)) { +		extended = true; +		return false; +	} else if (unlikely(extended)) { +		switch (data) { +		case 0x8: +			schedule_delayed_work(&dell_rfkill_work, +					      round_jiffies_relative(HZ / 4)); +			break; +		} +		extended = false; +	} + +	return false; +} + +static int __init dell_setup_rfkill(void) +{ +	int status, ret, whitelisted; +	const char *product; + +	/* +	 * rfkill support causes trouble on various models, mostly Inspirons. +	 * So we whitelist certain series, and don't support rfkill on others. +	 */ +	whitelisted = 0; +	product = dmi_get_system_info(DMI_PRODUCT_NAME); +	if (product &&  (strncmp(product, "Latitude", 8) == 0 || +			 strncmp(product, "Precision", 9) == 0)) +		whitelisted = 1; +	if (!force_rfkill && !whitelisted) +		return 0; + +	get_buffer(); +	dell_send_request(buffer, 17, 11); +	status = buffer->output[1]; +	buffer->input[0] = 0x2; +	dell_send_request(buffer, 17, 11); +	hwswitch_state = buffer->output[1]; +	release_buffer(); + +	if (!(status & BIT(0))) { +		if (force_rfkill) { +			/* No hwsitch, clear all hw-controlled bits */ +			hwswitch_state &= ~7; +		} else { +			/* rfkill is only tested on laptops with a hwswitch */ +			return 0; +		} +	} + +	if ((status & (1<<2|1<<8)) == (1<<2|1<<8)) { +		wifi_rfkill = rfkill_alloc("dell-wifi", &platform_device->dev, +					   RFKILL_TYPE_WLAN, +					   &dell_rfkill_ops, (void *) 1); +		if (!wifi_rfkill) { +			ret = -ENOMEM; +			goto err_wifi; +		} +		ret = rfkill_register(wifi_rfkill); +		if (ret) +			goto err_wifi; +	} + +	if ((status & (1<<3|1<<9)) == (1<<3|1<<9)) { +		bluetooth_rfkill = rfkill_alloc("dell-bluetooth", +						&platform_device->dev, +						RFKILL_TYPE_BLUETOOTH, +						&dell_rfkill_ops, (void *) 2); +		if (!bluetooth_rfkill) { +			ret = -ENOMEM; +			goto err_bluetooth; +		} +		ret = rfkill_register(bluetooth_rfkill); +		if (ret) +			goto err_bluetooth; +	} + +	if ((status & (1<<4|1<<10)) == (1<<4|1<<10)) { +		wwan_rfkill = rfkill_alloc("dell-wwan", +					   &platform_device->dev, +					   RFKILL_TYPE_WWAN, +					   &dell_rfkill_ops, (void *) 3); +		if (!wwan_rfkill) { +			ret = -ENOMEM; +			goto err_wwan; +		} +		ret = rfkill_register(wwan_rfkill); +		if (ret) +			goto err_wwan; +	} + +	ret = i8042_install_filter(dell_laptop_i8042_filter); +	if (ret) { +		pr_warn("Unable to install key filter\n"); +		goto err_filter; +	} + +	return 0; +err_filter: +	if (wwan_rfkill) +		rfkill_unregister(wwan_rfkill); +err_wwan: +	rfkill_destroy(wwan_rfkill); +	if (bluetooth_rfkill) +		rfkill_unregister(bluetooth_rfkill); +err_bluetooth: +	rfkill_destroy(bluetooth_rfkill); +	if (wifi_rfkill) +		rfkill_unregister(wifi_rfkill); +err_wifi: +	rfkill_destroy(wifi_rfkill); + +	return ret; +} + +static void dell_cleanup_rfkill(void) +{ +	if (wifi_rfkill) { +		rfkill_unregister(wifi_rfkill); +		rfkill_destroy(wifi_rfkill); +	} +	if (bluetooth_rfkill) { +		rfkill_unregister(bluetooth_rfkill); +		rfkill_destroy(bluetooth_rfkill); +	} +	if (wwan_rfkill) { +		rfkill_unregister(wwan_rfkill); +		rfkill_destroy(wwan_rfkill); +	} +} +  static int dell_send_intensity(struct backlight_device *bd)  {  	int ret = 0; @@ -444,7 +719,7 @@ static int dell_send_intensity(struct backlight_device *bd)  out:  	release_buffer(); -	return 0; +	return ret;  }  static int dell_get_intensity(struct backlight_device *bd) @@ -557,10 +832,20 @@ static int __init dell_init(void)  	}  	buffer = page_address(bufferpage); +	ret = dell_setup_rfkill(); + +	if (ret) { +		pr_warn("Unable to setup rfkill\n"); +		goto fail_rfkill; +	} +  	if (quirks && quirks->touchpad_led)  		touchpad_led_init(&platform_device->dev);  	dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); +	if (dell_laptop_dir != NULL) +		debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, +				    &dell_debugfs_fops);  #ifdef CONFIG_ACPI  	/* In the event of an ACPI backlight being available, don't @@ -603,6 +888,10 @@ static int __init dell_init(void)  	return 0;  fail_backlight: +	i8042_remove_filter(dell_laptop_i8042_filter); +	cancel_delayed_work_sync(&dell_rfkill_work); +	dell_cleanup_rfkill(); +fail_rfkill:  	free_page((unsigned long)bufferpage);  fail_buffer:  	platform_device_del(platform_device); @@ -620,7 +909,10 @@ static void __exit dell_exit(void)  	debugfs_remove_recursive(dell_laptop_dir);  	if (quirks && quirks->touchpad_led)  		touchpad_led_exit(); +	i8042_remove_filter(dell_laptop_i8042_filter); +	cancel_delayed_work_sync(&dell_rfkill_work);  	backlight_device_unregister(dell_backlight_device); +	dell_cleanup_rfkill();  	if (platform_device) {  		platform_device_unregister(platform_device);  		platform_driver_unregister(&platform_driver); diff --git a/drivers/platform/x86/dell-smo8800.c b/drivers/platform/x86/dell-smo8800.c new file mode 100644 index 00000000000..a653716055d --- /dev/null +++ b/drivers/platform/x86/dell-smo8800.c @@ -0,0 +1,233 @@ +/* + *  dell-smo8800.c - Dell Latitude ACPI SMO8800/SMO8810 freefall sensor driver + * + *  Copyright (C) 2012 Sonal Santan <sonal.santan@gmail.com> + *  Copyright (C) 2014 Pali Rohár <pali.rohar@gmail.com> + * + *  This is loosely based on lis3lv02d driver. + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + */ + +#define DRIVER_NAME "smo8800" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/acpi.h> +#include <linux/interrupt.h> +#include <linux/miscdevice.h> + +struct smo8800_device { +	u32 irq;                     /* acpi device irq */ +	atomic_t counter;            /* count after last read */ +	struct miscdevice miscdev;   /* for /dev/freefall */ +	unsigned long misc_opened;   /* whether the device is open */ +	wait_queue_head_t misc_wait; /* Wait queue for the misc dev */ +	struct device *dev;          /* acpi device */ +}; + +static irqreturn_t smo8800_interrupt_quick(int irq, void *data) +{ +	struct smo8800_device *smo8800 = data; + +	atomic_inc(&smo8800->counter); +	wake_up_interruptible(&smo8800->misc_wait); +	return IRQ_WAKE_THREAD; +} + +static irqreturn_t smo8800_interrupt_thread(int irq, void *data) +{ +	struct smo8800_device *smo8800 = data; + +	dev_info(smo8800->dev, "detected free fall\n"); +	return IRQ_HANDLED; +} + +static acpi_status smo8800_get_resource(struct acpi_resource *resource, +					void *context) +{ +	struct acpi_resource_extended_irq *irq; + +	if (resource->type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ) +		return AE_OK; + +	irq = &resource->data.extended_irq; +	if (!irq || !irq->interrupt_count) +		return AE_OK; + +	*((u32 *)context) = irq->interrupts[0]; +	return AE_CTRL_TERMINATE; +} + +static u32 smo8800_get_irq(struct acpi_device *device) +{ +	u32 irq = 0; +	acpi_status status; + +	status = acpi_walk_resources(device->handle, METHOD_NAME__CRS, +				     smo8800_get_resource, &irq); +	if (ACPI_FAILURE(status)) { +		dev_err(&device->dev, "acpi_walk_resources failed\n"); +		return 0; +	} + +	return irq; +} + +static ssize_t smo8800_misc_read(struct file *file, char __user *buf, +				 size_t count, loff_t *pos) +{ +	struct smo8800_device *smo8800 = container_of(file->private_data, +					 struct smo8800_device, miscdev); + +	u32 data = 0; +	unsigned char byte_data = 0; +	ssize_t retval = 1; + +	if (count < 1) +		return -EINVAL; + +	atomic_set(&smo8800->counter, 0); +	retval = wait_event_interruptible(smo8800->misc_wait, +				(data = atomic_xchg(&smo8800->counter, 0))); + +	if (retval) +		return retval; + +	byte_data = 1; +	retval = 1; + +	if (data < 255) +		byte_data = data; +	else +		byte_data = 255; + +	if (put_user(byte_data, buf)) +		retval = -EFAULT; + +	return retval; +} + +static int smo8800_misc_open(struct inode *inode, struct file *file) +{ +	struct smo8800_device *smo8800 = container_of(file->private_data, +					 struct smo8800_device, miscdev); + +	if (test_and_set_bit(0, &smo8800->misc_opened)) +		return -EBUSY; /* already open */ + +	atomic_set(&smo8800->counter, 0); +	return 0; +} + +static int smo8800_misc_release(struct inode *inode, struct file *file) +{ +	struct smo8800_device *smo8800 = container_of(file->private_data, +					 struct smo8800_device, miscdev); + +	clear_bit(0, &smo8800->misc_opened); /* release the device */ +	return 0; +} + +static const struct file_operations smo8800_misc_fops = { +	.owner = THIS_MODULE, +	.read = smo8800_misc_read, +	.open = smo8800_misc_open, +	.release = smo8800_misc_release, +}; + +static int smo8800_add(struct acpi_device *device) +{ +	int err; +	struct smo8800_device *smo8800; + +	smo8800 = devm_kzalloc(&device->dev, sizeof(*smo8800), GFP_KERNEL); +	if (!smo8800) { +		dev_err(&device->dev, "failed to allocate device data\n"); +		return -ENOMEM; +	} + +	smo8800->dev = &device->dev; +	smo8800->miscdev.minor = MISC_DYNAMIC_MINOR; +	smo8800->miscdev.name = "freefall"; +	smo8800->miscdev.fops = &smo8800_misc_fops; + +	init_waitqueue_head(&smo8800->misc_wait); + +	err = misc_register(&smo8800->miscdev); +	if (err) { +		dev_err(&device->dev, "failed to register misc dev: %d\n", err); +		return err; +	} + +	device->driver_data = smo8800; + +	smo8800->irq = smo8800_get_irq(device); +	if (!smo8800->irq) { +		dev_err(&device->dev, "failed to obtain IRQ\n"); +		err = -EINVAL; +		goto error; +	} + +	err = request_threaded_irq(smo8800->irq, smo8800_interrupt_quick, +				   smo8800_interrupt_thread, +				   IRQF_TRIGGER_RISING | IRQF_ONESHOT, +				   DRIVER_NAME, smo8800); +	if (err) { +		dev_err(&device->dev, +			"failed to request thread for IRQ %d: %d\n", +			smo8800->irq, err); +		goto error; +	} + +	dev_dbg(&device->dev, "device /dev/freefall registered with IRQ %d\n", +		 smo8800->irq); +	return 0; + +error: +	misc_deregister(&smo8800->miscdev); +	return err; +} + +static int smo8800_remove(struct acpi_device *device) +{ +	struct smo8800_device *smo8800 = device->driver_data; + +	free_irq(smo8800->irq, smo8800); +	misc_deregister(&smo8800->miscdev); +	dev_dbg(&device->dev, "device /dev/freefall unregistered\n"); +	return 0; +} + +static const struct acpi_device_id smo8800_ids[] = { +	{ "SMO8800", 0 }, +	{ "SMO8810", 0 }, +	{ "", 0 }, +}; + +MODULE_DEVICE_TABLE(acpi, smo8800_ids); + +static struct acpi_driver smo8800_driver = { +	.name = DRIVER_NAME, +	.class = "Latitude", +	.ids = smo8800_ids, +	.ops = { +		.add = smo8800_add, +		.remove = smo8800_remove, +	}, +	.owner = THIS_MODULE, +}; + +module_acpi_driver(smo8800_driver); + +MODULE_DESCRIPTION("Dell Latitude freefall driver (ACPI SMO8800/SMO8810)"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sonal Santan, Pali Rohár"); diff --git a/drivers/platform/x86/dell-wmi-aio.c b/drivers/platform/x86/dell-wmi-aio.c index bcf8cc6b553..dbc97a33bbc 100644 --- a/drivers/platform/x86/dell-wmi-aio.c +++ b/drivers/platform/x86/dell-wmi-aio.c @@ -24,7 +24,6 @@  #include <linux/types.h>  #include <linux/input.h>  #include <linux/input/sparse-keymap.h> -#include <acpi/acpi_drivers.h>  #include <linux/acpi.h>  #include <linux/string.h> diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index fa9a2171cc1..390e8e33d5e 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -32,7 +32,6 @@  #include <linux/types.h>  #include <linux/input.h>  #include <linux/input/sparse-keymap.h> -#include <acpi/acpi_drivers.h>  #include <linux/acpi.h>  #include <linux/string.h>  #include <linux/dmi.h> @@ -130,7 +129,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = {  	KEY_BRIGHTNESSUP,	KEY_UNKNOWN,	KEY_KBDILLUMTOGGLE,  	KEY_UNKNOWN,	KEY_SWITCHVIDEOMODE,	KEY_UNKNOWN, KEY_UNKNOWN,  	KEY_SWITCHVIDEOMODE,	KEY_UNKNOWN,	KEY_UNKNOWN, KEY_PROG2, -	KEY_UNKNOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	KEY_UNKNOWN,	KEY_UNKNOWN,	KEY_UNKNOWN,	KEY_UNKNOWN, +	KEY_UNKNOWN,	KEY_UNKNOWN,	KEY_UNKNOWN,	KEY_MICMUTE,  	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -139,8 +139,8 @@ static const u16 bios_to_linux_keycode[256] __initconst = {  	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	KEY_PROG3 +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PROG3  };  static struct input_dev *dell_wmi_input_dev; diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index a6afd4108be..9b0c57cd1d4 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -28,8 +28,7 @@  #include <linux/hwmon.h>  #include <linux/hwmon-sysfs.h>  #include <linux/slab.h> -#include <acpi/acpi_drivers.h> -#include <acpi/acpi_bus.h> +#include <linux/acpi.h>  #include <linux/uaccess.h>  #include <linux/input.h>  #include <linux/input/sparse-keymap.h> @@ -166,7 +165,6 @@ struct eeepc_laptop {  	struct platform_device *platform_device;  	struct acpi_device *device;		/* the device we are in */ -	struct device *hwmon_device;  	struct backlight_device *backlight_device;  	struct input_dev *inputdev; @@ -190,16 +188,10 @@ struct eeepc_laptop {   */  static int write_acpi_int(acpi_handle handle, const char *method, int val)  { -	struct acpi_object_list params; -	union acpi_object in_obj;  	acpi_status status; -	params.count = 1; -	params.pointer = &in_obj; -	in_obj.type = ACPI_TYPE_INTEGER; -	in_obj.integer.value = val; +	status = acpi_execute_simple_method(handle, (char *)method, val); -	status = acpi_evaluate_object(handle, (char *)method, ¶ms, NULL);  	return (status == AE_OK ? 0 : -1);  } @@ -598,6 +590,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)  		rfkill_set_sw_state(eeepc->wlan_rfkill, blocked);  	mutex_lock(&eeepc->hotplug_lock); +	pci_lock_rescan_remove();  	if (eeepc->hotplug_slot) {  		port = acpi_get_pci_dev(handle); @@ -640,8 +633,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)  			dev = pci_scan_single_device(bus, 0);  			if (dev) {  				pci_bus_assign_resources(bus); -				if (pci_bus_add_device(dev)) -					pr_err("Unable to hotplug wifi\n"); +				pci_bus_add_device(dev);  			}  		} else {  			dev = pci_get_slot(bus, 0); @@ -655,6 +647,7 @@ out_put_dev:  	}  out_unlock: +	pci_unlock_rescan_remove();  	mutex_unlock(&eeepc->hotplug_lock);  } @@ -1073,7 +1066,7 @@ static ssize_t show_sys_hwmon(int (*get)(void), char *buf)  	{								\  		return store_sys_hwmon(_get, buf, count);		\  	}								\ -	static SENSOR_DEVICE_ATTR(_name, _mode, show_##_name, store_##_name, 0); +	static DEVICE_ATTR(_name, _mode, show_##_name, store_##_name);  EEEPC_CREATE_SENSOR_ATTR(fan1_input, S_IRUGO, eeepc_get_fan_rpm, NULL);  EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR, @@ -1081,55 +1074,26 @@ EEEPC_CREATE_SENSOR_ATTR(pwm1, S_IRUGO | S_IWUSR,  EEEPC_CREATE_SENSOR_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,  			 eeepc_get_fan_ctrl, eeepc_set_fan_ctrl); -static ssize_t -show_name(struct device *dev, struct device_attribute *attr, char *buf) -{ -	return sprintf(buf, "eeepc\n"); -} -static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0); - -static struct attribute *hwmon_attributes[] = { -	&sensor_dev_attr_pwm1.dev_attr.attr, -	&sensor_dev_attr_fan1_input.dev_attr.attr, -	&sensor_dev_attr_pwm1_enable.dev_attr.attr, -	&sensor_dev_attr_name.dev_attr.attr, +static struct attribute *hwmon_attrs[] = { +	&dev_attr_pwm1.attr, +	&dev_attr_fan1_input.attr, +	&dev_attr_pwm1_enable.attr,  	NULL  }; - -static struct attribute_group hwmon_attribute_group = { -	.attrs = hwmon_attributes -}; - -static void eeepc_hwmon_exit(struct eeepc_laptop *eeepc) -{ -	struct device *hwmon; - -	hwmon = eeepc->hwmon_device; -	if (!hwmon) -		return; -	sysfs_remove_group(&hwmon->kobj, -			   &hwmon_attribute_group); -	hwmon_device_unregister(hwmon); -	eeepc->hwmon_device = NULL; -} +ATTRIBUTE_GROUPS(hwmon);  static int eeepc_hwmon_init(struct eeepc_laptop *eeepc)  { +	struct device *dev = &eeepc->platform_device->dev;  	struct device *hwmon; -	int result; -	hwmon = hwmon_device_register(&eeepc->platform_device->dev); +	hwmon = devm_hwmon_device_register_with_groups(dev, "eeepc", NULL, +						       hwmon_groups);  	if (IS_ERR(hwmon)) {  		pr_err("Could not register eeepc hwmon device\n"); -		eeepc->hwmon_device = NULL;  		return PTR_ERR(hwmon);  	} -	eeepc->hwmon_device = hwmon; -	result = sysfs_create_group(&hwmon->kobj, -				    &hwmon_attribute_group); -	if (result) -		eeepc_hwmon_exit(eeepc); -	return result; +	return 0;  }  /* @@ -1209,10 +1173,8 @@ static int eeepc_input_init(struct eeepc_laptop *eeepc)  	int error;  	input = input_allocate_device(); -	if (!input) { -		pr_info("Unable to allocate input device\n"); +	if (!input)  		return -ENOMEM; -	}  	input->name = "Asus EeePC extra buttons";  	input->phys = EEEPC_LAPTOP_FILE "/input0"; @@ -1487,7 +1449,6 @@ static int eeepc_acpi_add(struct acpi_device *device)  fail_rfkill:  	eeepc_led_exit(eeepc);  fail_led: -	eeepc_hwmon_exit(eeepc);  fail_hwmon:  	eeepc_input_exit(eeepc);  fail_input: @@ -1507,7 +1468,6 @@ static int eeepc_acpi_remove(struct acpi_device *device)  	eeepc_backlight_exit(eeepc);  	eeepc_rfkill_exit(eeepc);  	eeepc_input_exit(eeepc); -	eeepc_hwmon_exit(eeepc);  	eeepc_led_exit(eeepc);  	eeepc_platform_exit(eeepc); diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index af67e6e56eb..6112933f627 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -33,7 +33,7 @@  #include <linux/input/sparse-keymap.h>  #include <linux/dmi.h>  #include <linux/fb.h> -#include <acpi/acpi_bus.h> +#include <linux/acpi.h>  #include "asus-wmi.h" diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 52b8a97efde..e6f336270c2 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -66,7 +66,6 @@  #include <linux/backlight.h>  #include <linux/input.h>  #include <linux/kfifo.h> -#include <linux/video_output.h>  #include <linux/platform_device.h>  #include <linux/slab.h>  #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) @@ -219,8 +218,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)  	{ .type = ACPI_TYPE_INTEGER }  	};  	struct acpi_object_list arg_list = { 4, ¶ms[0] }; -	struct acpi_buffer output; -	union acpi_object out_obj; +	unsigned long long value;  	acpi_handle handle = NULL;  	status = acpi_get_handle(fujitsu_hotkey->acpi_handle, "FUNC", &handle); @@ -235,10 +233,7 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)  	params[2].integer.value = arg1;  	params[3].integer.value = arg2; -	output.length = sizeof(out_obj); -	output.pointer = &out_obj; - -	status = acpi_evaluate_object(handle, NULL, &arg_list, &output); +	status = acpi_evaluate_integer(handle, NULL, &arg_list, &value);  	if (ACPI_FAILURE(status)) {  		vdbg_printk(FUJLAPTOP_DBG_WARN,  			"FUNC 0x%x (args 0x%x, 0x%x, 0x%x) call failed\n", @@ -246,18 +241,10 @@ static int call_fext_func(int cmd, int arg0, int arg1, int arg2)  		return -ENODEV;  	} -	if (out_obj.type != ACPI_TYPE_INTEGER) { -		vdbg_printk(FUJLAPTOP_DBG_WARN, -			"FUNC 0x%x (args 0x%x, 0x%x, 0x%x) did not " -			"return an integer\n", -			cmd, arg0, arg1, arg2); -		return -ENODEV; -	} -  	vdbg_printk(FUJLAPTOP_DBG_TRACE,  		"FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", -			cmd, arg0, arg1, arg2, (int)out_obj.integer.value); -	return out_obj.integer.value; +			cmd, arg0, arg1, arg2, (int)value); +	return value;  }  #if defined(CONFIG_LEDS_CLASS) || defined(CONFIG_LEDS_CLASS_MODULE) @@ -317,8 +304,6 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev)  static int set_lcd_level(int level)  {  	acpi_status status = AE_OK; -	union acpi_object arg0 = { ACPI_TYPE_INTEGER }; -	struct acpi_object_list arg_list = { 1, &arg0 };  	acpi_handle handle = NULL;  	vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBLL [%d]\n", @@ -333,9 +318,8 @@ static int set_lcd_level(int level)  		return -ENODEV;  	} -	arg0.integer.value = level; -	status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); +	status = acpi_execute_simple_method(handle, NULL, level);  	if (ACPI_FAILURE(status))  		return -ENODEV; @@ -345,8 +329,6 @@ static int set_lcd_level(int level)  static int set_lcd_level_alt(int level)  {  	acpi_status status = AE_OK; -	union acpi_object arg0 = { ACPI_TYPE_INTEGER }; -	struct acpi_object_list arg_list = { 1, &arg0 };  	acpi_handle handle = NULL;  	vdbg_printk(FUJLAPTOP_DBG_TRACE, "set lcd level via SBL2 [%d]\n", @@ -361,9 +343,7 @@ static int set_lcd_level_alt(int level)  		return -ENODEV;  	} -	arg0.integer.value = level; - -	status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); +	status = acpi_execute_simple_method(handle, NULL, level);  	if (ACPI_FAILURE(status))  		return -ENODEV; @@ -586,11 +566,10 @@ static struct platform_driver fujitsupf_driver = {  static void dmi_check_cb_common(const struct dmi_system_id *id)  { -	acpi_handle handle;  	pr_info("Identified laptop model '%s'\n", id->ident);  	if (use_alt_lcd_levels == -1) { -		if (ACPI_SUCCESS(acpi_get_handle(NULL, -				"\\_SB.PCI0.LPCB.FJEX.SBL2", &handle))) +		if (acpi_has_method(NULL, +				"\\_SB.PCI0.LPCB.FJEX.SBL2"))  			use_alt_lcd_levels = 1;  		else  			use_alt_lcd_levels = 0; @@ -653,8 +632,6 @@ static struct dmi_system_id fujitsu_dmi_table[] = {  static int acpi_fujitsu_add(struct acpi_device *device)  { -	acpi_handle handle; -	int result = 0;  	int state = 0;  	struct input_dev *input;  	int error; @@ -690,8 +667,8 @@ static int acpi_fujitsu_add(struct acpi_device *device)  	if (error)  		goto err_free_input_dev; -	result = acpi_bus_update_power(fujitsu->acpi_handle, &state); -	if (result) { +	error = acpi_bus_update_power(fujitsu->acpi_handle, &state); +	if (error) {  		pr_err("Error reading power state\n");  		goto err_unregister_input_dev;  	} @@ -702,8 +679,7 @@ static int acpi_fujitsu_add(struct acpi_device *device)  	fujitsu->dev = device; -	if (ACPI_SUCCESS -	    (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { +	if (acpi_has_method(device->handle, METHOD_NAME__INI)) {  		vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");  		if (ACPI_FAILURE  		    (acpi_evaluate_object @@ -722,7 +698,7 @@ static int acpi_fujitsu_add(struct acpi_device *device)  		fujitsu->max_brightness = FUJITSU_LCD_N_LEVELS;  	get_lcd_level(); -	return result; +	return 0;  err_unregister_input_dev:  	input_unregister_device(input); @@ -730,7 +706,7 @@ err_unregister_input_dev:  err_free_input_dev:  	input_free_device(input);  err_stop: -	return result; +	return error;  }  static int acpi_fujitsu_remove(struct acpi_device *device) @@ -803,7 +779,6 @@ static void acpi_fujitsu_notify(struct acpi_device *device, u32 event)  static int acpi_fujitsu_hotkey_add(struct acpi_device *device)  { -	acpi_handle handle;  	int result = 0;  	int state = 0;  	struct input_dev *input; @@ -854,8 +829,8 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)  	if (error)  		goto err_free_input_dev; -	result = acpi_bus_update_power(fujitsu_hotkey->acpi_handle, &state); -	if (result) { +	error = acpi_bus_update_power(fujitsu_hotkey->acpi_handle, &state); +	if (error) {  		pr_err("Error reading power state\n");  		goto err_unregister_input_dev;  	} @@ -866,8 +841,7 @@ static int acpi_fujitsu_hotkey_add(struct acpi_device *device)  	fujitsu_hotkey->dev = device; -	if (ACPI_SUCCESS -	    (acpi_get_handle(device->handle, METHOD_NAME__INI, &handle))) { +	if (acpi_has_method(device->handle, METHOD_NAME__INI)) {  		vdbg_printk(FUJLAPTOP_DBG_INFO, "Invoking _INI\n");  		if (ACPI_FAILURE  		    (acpi_evaluate_object @@ -931,7 +905,7 @@ err_free_input_dev:  err_free_fifo:  	kfifo_free(&fujitsu_hotkey->fifo);  err_stop: -	return result; +	return error;  }  static int acpi_fujitsu_hotkey_remove(struct acpi_device *device) diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 570926c1001..c3784baceae 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -71,6 +71,44 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = {  	KEY_LEFTALT  }; +static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = { +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_SCROLLDOWN, +	KEY_SCROLLUP, +	KEY_CYCLEWINDOWS, +	KEY_LEFTCTRL, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_LEFTMETA +}; + +static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = { +	KEY_RESERVED, +	KEY_VOLUMEDOWN, +	KEY_VOLUMEUP, +	KEY_CYCLEWINDOWS, +	KEY_PROG1, +	KEY_PROG2, +	KEY_LEFTMETA, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +	KEY_RESERVED, +}; +  static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = {  	KEY_RESERVED,  	KEY_RESERVED, @@ -302,6 +340,33 @@ static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)  static const struct dmi_system_id dmi_ids[] __initconst = {  	{  		.callback = fujitsu_dmi_lifebook, +		.ident = "Fujitsu Lifebook T901", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), +			DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901") +		}, +		.driver_data = keymap_Lifebook_T901 +	}, +	{ +		.callback = fujitsu_dmi_lifebook, +		.ident = "Fujitsu Lifebook T901", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), +			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901") +		}, +		.driver_data = keymap_Lifebook_T901 +	}, +	{ +		.callback = fujitsu_dmi_lifebook, +		.ident = "Fujitsu Lifebook T902", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), +			DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902") +		}, +		.driver_data = keymap_Lifebook_T902 +	}, +	{ +		.callback = fujitsu_dmi_lifebook,  		.ident = "Fujitsu Siemens P/T Series",  		.matches = {  			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/platform/x86/hp-wireless.c b/drivers/platform/x86/hp-wireless.c new file mode 100644 index 00000000000..415348fc121 --- /dev/null +++ b/drivers/platform/x86/hp-wireless.c @@ -0,0 +1,132 @@ +/* + *  hp-wireless button for Windows 8 + * + *  Copyright (C) 2014 Alex Hung <alex.hung@canonical.com> + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License along + *  with this program; if not, write to the Free Software Foundation, Inc., + *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alex Hung"); +MODULE_ALIAS("acpi*:HPQ6001:*"); + +static struct input_dev *hpwl_input_dev; + +static const struct acpi_device_id hpwl_ids[] = { +	{"HPQ6001", 0}, +	{"", 0}, +}; + +static int hp_wireless_input_setup(void) +{ +	int err; + +	hpwl_input_dev = input_allocate_device(); +	if (!hpwl_input_dev) +		return -ENOMEM; + +	hpwl_input_dev->name = "HP Wireless hotkeys"; +	hpwl_input_dev->phys = "hpq6001/input0"; +	hpwl_input_dev->id.bustype = BUS_HOST; +	hpwl_input_dev->evbit[0] = BIT(EV_KEY); +	set_bit(KEY_RFKILL, hpwl_input_dev->keybit); + +	err = input_register_device(hpwl_input_dev); +	if (err) +		goto err_free_dev; + +	return 0; + +err_free_dev: +	input_free_device(hpwl_input_dev); +	return err; +} + +static void hp_wireless_input_destroy(void) +{ +	input_unregister_device(hpwl_input_dev); +} + +static void hpwl_notify(struct acpi_device *acpi_dev, u32 event) +{ +	if (event != 0x80) { +		pr_info("Received unknown event (0x%x)\n", event); +		return; +	} + +	input_report_key(hpwl_input_dev, KEY_RFKILL, 1); +	input_sync(hpwl_input_dev); +	input_report_key(hpwl_input_dev, KEY_RFKILL, 0); +	input_sync(hpwl_input_dev); +} + +static int hpwl_add(struct acpi_device *device) +{ +	int err; + +	err = hp_wireless_input_setup(); +	return err; +} + +static int hpwl_remove(struct acpi_device *device) +{ +	hp_wireless_input_destroy(); +	return 0; +} + +static struct acpi_driver hpwl_driver = { +	.name	= "hp-wireless", +	.owner	= THIS_MODULE, +	.ids	= hpwl_ids, +	.ops	= { +		.add	= hpwl_add, +		.remove	= hpwl_remove, +		.notify	= hpwl_notify, +	}, +}; + +static int __init hpwl_init(void) +{ +	int err; + +	pr_info("Initializing HPQ6001 module\n"); +	err = acpi_bus_register_driver(&hpwl_driver); +	if (err) { +		pr_err("Unable to register HP wireless control driver.\n"); +		goto error_acpi_register; +	} + +	return 0; + +error_acpi_register: +	return err; +} + +static void __exit hpwl_exit(void) +{ +	pr_info("Exiting HPQ6001 module\n"); +	acpi_bus_unregister_driver(&hpwl_driver); +} + +module_init(hpwl_init); +module_exit(hpwl_exit); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 1c86fa0857c..484a8673b83 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -53,7 +53,9 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");  #define HPWMI_ALS_QUERY 0x3  #define HPWMI_HARDWARE_QUERY 0x4  #define HPWMI_WIRELESS_QUERY 0x5 +#define HPWMI_BIOS_QUERY 0x9  #define HPWMI_HOTKEY_QUERY 0xc +#define HPWMI_FEATURE_QUERY 0xd  #define HPWMI_WIRELESS2_QUERY 0x1b  #define HPWMI_POSTCODEERROR_QUERY 0x2a @@ -143,6 +145,7 @@ static const struct key_entry hp_wmi_keymap[] = {  	{ KE_KEY, 0x2142, { KEY_MEDIA } },  	{ KE_KEY, 0x213b, { KEY_INFO } },  	{ KE_KEY, 0x2169, { KEY_DIRECTION } }, +	{ KE_KEY, 0x216a, { KEY_SETUP } },  	{ KE_KEY, 0x231b, { KEY_HELP } },  	{ KE_END, 0 }  }; @@ -292,6 +295,30 @@ static int hp_wmi_tablet_state(void)  	return (state & 0x4) ? 1 : 0;  } +static int hp_wmi_bios_2009_later(void) +{ +	int state = 0; +	int ret = hp_wmi_perform_query(HPWMI_FEATURE_QUERY, 0, &state, +				       sizeof(state), sizeof(state)); +	if (ret) +		return ret; + +	return (state & 0x10) ? 1 : 0; +} + +static int hp_wmi_enable_hotkeys(void) +{ +	int ret; +	int query = 0x6e; + +	ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query), +				   0); + +	if (ret) +		return -EINVAL; +	return 0; +} +  static int hp_wmi_set_block(void *data, bool blocked)  {  	enum hp_wmi_radio r = (enum hp_wmi_radio) data; @@ -636,6 +663,9 @@ static int __init hp_wmi_input_setup(void)  			    hp_wmi_tablet_state());  	input_sync(hp_wmi_input_dev); +	if (hp_wmi_bios_2009_later() == 4) +		hp_wmi_enable_hotkeys(); +  	status = wmi_install_notify_handler(HPWMI_EVENT_GUID, hp_wmi_notify, NULL);  	if (ACPI_FAILURE(status)) {  		err = -EIO; @@ -871,7 +901,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)  	gps_rfkill = NULL;  	rfkill2_count = 0; -	if (hp_wmi_rfkill_setup(device)) +	if (hp_wmi_bios_2009_later() || hp_wmi_rfkill_setup(device))  		hp_wmi_rfkill2_setup(device);  	err = device_create_file(&device->dev, &dev_attr_display); diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index a8e43cf70fa..3dc934438c2 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -36,7 +36,7 @@  #include <linux/uaccess.h>  #include <linux/leds.h>  #include <linux/atomic.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  #include "../../misc/lis3lv02d/lis3lv02d.h"  #define DRIVER_NAME     "hp_accel" @@ -77,6 +77,7 @@ static inline void delayed_sysfs_set(struct led_classdev *led_cdev,  static struct acpi_device_id lis3lv02d_device_ids[] = {  	{"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */  	{"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ +	{"HPQ6007", 0}, /* HP Mobile Data Protection System PNP */  	{"", 0},  };  MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); @@ -88,7 +89,7 @@ MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids);   *   * Returns 0 on success.   */ -int lis3lv02d_acpi_init(struct lis3lv02d *lis3) +static int lis3lv02d_acpi_init(struct lis3lv02d *lis3)  {  	struct acpi_device *dev = lis3->bus_priv;  	if (acpi_evaluate_object(dev->handle, METHOD_NAME__INI, @@ -106,7 +107,7 @@ int lis3lv02d_acpi_init(struct lis3lv02d *lis3)   *   * Returns 0 on success.   */ -int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret) +static int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret)  {  	struct acpi_device *dev = lis3->bus_priv;  	union acpi_object arg0 = { ACPI_TYPE_INTEGER }; @@ -129,7 +130,7 @@ int lis3lv02d_acpi_read(struct lis3lv02d *lis3, int reg, u8 *ret)   *   * Returns 0 on success.   */ -int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val) +static int lis3lv02d_acpi_write(struct lis3lv02d *lis3, int reg, u8 val)  {  	struct acpi_device *dev = lis3->bus_priv;  	unsigned long long ret; /* Not used when writting */ diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 89c4519d48a..b4c495a62ee 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -26,8 +26,7 @@  #include <linux/module.h>  #include <linux/init.h>  #include <linux/types.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  #include <linux/rfkill.h>  #include <linux/platform_device.h>  #include <linux/input.h> @@ -37,6 +36,8 @@  #include <linux/debugfs.h>  #include <linux/seq_file.h>  #include <linux/i8042.h> +#include <linux/dmi.h> +#include <linux/device.h>  #define IDEAPAD_RFKILL_DEV_NUM	(3) @@ -72,8 +73,15 @@ enum {  	VPCCMD_W_BL_POWER = 0x33,  }; +struct ideapad_rfk_priv { +	int dev; +	struct ideapad_private *priv; +}; +  struct ideapad_private { +	struct acpi_device *adev;  	struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; +	struct ideapad_rfk_priv rfk_priv[IDEAPAD_RFKILL_DEV_NUM];  	struct platform_device *platform_device;  	struct input_dev *inputdev;  	struct backlight_device *blightdev; @@ -81,8 +89,6 @@ struct ideapad_private {  	unsigned long cfg;  }; -static acpi_handle ideapad_handle; -static struct ideapad_private *ideapad_priv;  static bool no_bt_rfkill;  module_param(no_bt_rfkill, bool, 0444);  MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); @@ -200,34 +206,38 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)   */  static int debugfs_status_show(struct seq_file *s, void *data)  { +	struct ideapad_private *priv = s->private;  	unsigned long value; -	if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &value)) +	if (!priv) +		return -EINVAL; + +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &value))  		seq_printf(s, "Backlight max:\t%lu\n", value); -	if (!read_ec_data(ideapad_handle, VPCCMD_R_BL, &value)) +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL, &value))  		seq_printf(s, "Backlight now:\t%lu\n", value); -	if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &value)) +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &value))  		seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");  	seq_printf(s, "=====================\n"); -	if (!read_ec_data(ideapad_handle, VPCCMD_R_RF, &value)) +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_RF, &value))  		seq_printf(s, "Radio status:\t%s(%lu)\n",  			   value ? "On" : "Off", value); -	if (!read_ec_data(ideapad_handle, VPCCMD_R_WIFI, &value)) +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_WIFI, &value))  		seq_printf(s, "Wifi status:\t%s(%lu)\n",  			   value ? "On" : "Off", value); -	if (!read_ec_data(ideapad_handle, VPCCMD_R_BT, &value)) +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_BT, &value))  		seq_printf(s, "BT status:\t%s(%lu)\n",  			   value ? "On" : "Off", value); -	if (!read_ec_data(ideapad_handle, VPCCMD_R_3G, &value)) +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_3G, &value))  		seq_printf(s, "3G status:\t%s(%lu)\n",  			   value ? "On" : "Off", value);  	seq_printf(s, "=====================\n"); -	if (!read_ec_data(ideapad_handle, VPCCMD_R_TOUCHPAD, &value)) +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value))  		seq_printf(s, "Touchpad status:%s(%lu)\n",  			   value ? "On" : "Off", value); -	if (!read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &value)) +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &value))  		seq_printf(s, "Camera status:\t%s(%lu)\n",  			   value ? "On" : "Off", value); @@ -236,7 +246,7 @@ static int debugfs_status_show(struct seq_file *s, void *data)  static int debugfs_status_open(struct inode *inode, struct file *file)  { -	return single_open(file, debugfs_status_show, NULL); +	return single_open(file, debugfs_status_show, inode->i_private);  }  static const struct file_operations debugfs_status_fops = { @@ -249,21 +259,23 @@ static const struct file_operations debugfs_status_fops = {  static int debugfs_cfg_show(struct seq_file *s, void *data)  { -	if (!ideapad_priv) { +	struct ideapad_private *priv = s->private; + +	if (!priv) {  		seq_printf(s, "cfg: N/A\n");  	} else {  		seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ", -			   ideapad_priv->cfg); -		if (test_bit(CFG_BT_BIT, &ideapad_priv->cfg)) +			   priv->cfg); +		if (test_bit(CFG_BT_BIT, &priv->cfg))  			seq_printf(s, "Bluetooth "); -		if (test_bit(CFG_3G_BIT, &ideapad_priv->cfg)) +		if (test_bit(CFG_3G_BIT, &priv->cfg))  			seq_printf(s, "3G "); -		if (test_bit(CFG_WIFI_BIT, &ideapad_priv->cfg)) +		if (test_bit(CFG_WIFI_BIT, &priv->cfg))  			seq_printf(s, "Wireless "); -		if (test_bit(CFG_CAMERA_BIT, &ideapad_priv->cfg)) +		if (test_bit(CFG_CAMERA_BIT, &priv->cfg))  			seq_printf(s, "Camera ");  		seq_printf(s, "\nGraphic: "); -		switch ((ideapad_priv->cfg)&0x700) { +		switch ((priv->cfg)&0x700) {  		case 0x100:  			seq_printf(s, "Intel");  			break; @@ -287,7 +299,7 @@ static int debugfs_cfg_show(struct seq_file *s, void *data)  static int debugfs_cfg_open(struct inode *inode, struct file *file)  { -	return single_open(file, debugfs_cfg_show, NULL); +	return single_open(file, debugfs_cfg_show, inode->i_private);  }  static const struct file_operations debugfs_cfg_fops = { @@ -308,14 +320,14 @@ static int ideapad_debugfs_init(struct ideapad_private *priv)  		goto errout;  	} -	node = debugfs_create_file("cfg", S_IRUGO, priv->debug, NULL, +	node = debugfs_create_file("cfg", S_IRUGO, priv->debug, priv,  				   &debugfs_cfg_fops);  	if (!node) {  		pr_err("failed to create cfg in debugfs");  		goto errout;  	} -	node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL, +	node = debugfs_create_file("status", S_IRUGO, priv->debug, priv,  				   &debugfs_status_fops);  	if (!node) {  		pr_err("failed to create status in debugfs"); @@ -342,8 +354,9 @@ static ssize_t show_ideapad_cam(struct device *dev,  				char *buf)  {  	unsigned long result; +	struct ideapad_private *priv = dev_get_drvdata(dev); -	if (read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &result)) +	if (read_ec_data(priv->adev->handle, VPCCMD_R_CAMERA, &result))  		return sprintf(buf, "-1\n");  	return sprintf(buf, "%lu\n", result);  } @@ -353,12 +366,13 @@ static ssize_t store_ideapad_cam(struct device *dev,  				 const char *buf, size_t count)  {  	int ret, state; +	struct ideapad_private *priv = dev_get_drvdata(dev);  	if (!count)  		return 0;  	if (sscanf(buf, "%i", &state) != 1)  		return -EINVAL; -	ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state); +	ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_CAMERA, state);  	if (ret < 0)  		return -EIO;  	return count; @@ -371,8 +385,9 @@ static ssize_t show_ideapad_fan(struct device *dev,  				char *buf)  {  	unsigned long result; +	struct ideapad_private *priv = dev_get_drvdata(dev); -	if (read_ec_data(ideapad_handle, VPCCMD_R_FAN, &result)) +	if (read_ec_data(priv->adev->handle, VPCCMD_R_FAN, &result))  		return sprintf(buf, "-1\n");  	return sprintf(buf, "%lu\n", result);  } @@ -382,6 +397,7 @@ static ssize_t store_ideapad_fan(struct device *dev,  				 const char *buf, size_t count)  {  	int ret, state; +	struct ideapad_private *priv = dev_get_drvdata(dev);  	if (!count)  		return 0; @@ -389,7 +405,7 @@ static ssize_t store_ideapad_fan(struct device *dev,  		return -EINVAL;  	if (state < 0 || state > 4 || state == 3)  		return -EINVAL; -	ret = write_ec_cmd(ideapad_handle, VPCCMD_W_FAN, state); +	ret = write_ec_cmd(priv->adev->handle, VPCCMD_W_FAN, state);  	if (ret < 0)  		return -EIO;  	return count; @@ -415,7 +431,8 @@ static umode_t ideapad_is_visible(struct kobject *kobj,  		supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));  	else if (attr == &dev_attr_fan_mode.attr) {  		unsigned long value; -		supported = !read_ec_data(ideapad_handle, VPCCMD_R_FAN, &value); +		supported = !read_ec_data(priv->adev->handle, VPCCMD_R_FAN, +					  &value);  	} else  		supported = true; @@ -445,9 +462,9 @@ const struct ideapad_rfk_data ideapad_rfk_data[] = {  static int ideapad_rfk_set(void *data, bool blocked)  { -	unsigned long opcode = (unsigned long)data; +	struct ideapad_rfk_priv *priv = data; -	return write_ec_cmd(ideapad_handle, opcode, !blocked); +	return write_ec_cmd(priv->priv->adev->handle, priv->dev, !blocked);  }  static struct rfkill_ops ideapad_rfk_ops = { @@ -459,7 +476,7 @@ static void ideapad_sync_rfk_state(struct ideapad_private *priv)  	unsigned long hw_blocked;  	int i; -	if (read_ec_data(ideapad_handle, VPCCMD_R_RF, &hw_blocked)) +	if (read_ec_data(priv->adev->handle, VPCCMD_R_RF, &hw_blocked))  		return;  	hw_blocked = !hw_blocked; @@ -468,27 +485,30 @@ static void ideapad_sync_rfk_state(struct ideapad_private *priv)  			rfkill_set_hw_state(priv->rfk[i], hw_blocked);  } -static int ideapad_register_rfkill(struct acpi_device *adevice, int dev) +static int ideapad_register_rfkill(struct ideapad_private *priv, int dev)  { -	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);  	int ret;  	unsigned long sw_blocked;  	if (no_bt_rfkill &&  	    (ideapad_rfk_data[dev].type == RFKILL_TYPE_BLUETOOTH)) {  		/* Force to enable bluetooth when no_bt_rfkill=1 */ -		write_ec_cmd(ideapad_handle, +		write_ec_cmd(priv->adev->handle,  			     ideapad_rfk_data[dev].opcode, 1);  		return 0;  	} - -	priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, &adevice->dev, -				      ideapad_rfk_data[dev].type, &ideapad_rfk_ops, -				      (void *)(long)dev); +	priv->rfk_priv[dev].dev = dev; +	priv->rfk_priv[dev].priv = priv; + +	priv->rfk[dev] = rfkill_alloc(ideapad_rfk_data[dev].name, +				      &priv->platform_device->dev, +				      ideapad_rfk_data[dev].type, +				      &ideapad_rfk_ops, +				      &priv->rfk_priv[dev]);  	if (!priv->rfk[dev])  		return -ENOMEM; -	if (read_ec_data(ideapad_handle, ideapad_rfk_data[dev].opcode-1, +	if (read_ec_data(priv->adev->handle, ideapad_rfk_data[dev].opcode-1,  			 &sw_blocked)) {  		rfkill_init_sw_state(priv->rfk[dev], 0);  	} else { @@ -504,10 +524,8 @@ static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)  	return 0;  } -static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev) +static void ideapad_unregister_rfkill(struct ideapad_private *priv, int dev)  { -	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); -  	if (!priv->rfk[dev])  		return; @@ -518,37 +536,16 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)  /*   * Platform device   */ -static int ideapad_platform_init(struct ideapad_private *priv) +static int ideapad_sysfs_init(struct ideapad_private *priv)  { -	int result; - -	priv->platform_device = platform_device_alloc("ideapad", -1); -	if (!priv->platform_device) -		return -ENOMEM; -	platform_set_drvdata(priv->platform_device, priv); - -	result = platform_device_add(priv->platform_device); -	if (result) -		goto fail_platform_device; - -	result = sysfs_create_group(&priv->platform_device->dev.kobj, +	return sysfs_create_group(&priv->platform_device->dev.kobj,  				    &ideapad_attribute_group); -	if (result) -		goto fail_sysfs; -	return 0; - -fail_sysfs: -	platform_device_del(priv->platform_device); -fail_platform_device: -	platform_device_put(priv->platform_device); -	return result;  } -static void ideapad_platform_exit(struct ideapad_private *priv) +static void ideapad_sysfs_exit(struct ideapad_private *priv)  {  	sysfs_remove_group(&priv->platform_device->dev.kobj,  			   &ideapad_attribute_group); -	platform_device_unregister(priv->platform_device);  }  /* @@ -574,10 +571,8 @@ static int ideapad_input_init(struct ideapad_private *priv)  	int error;  	inputdev = input_allocate_device(); -	if (!inputdev) { -		pr_info("Unable to allocate input device\n"); +	if (!inputdev)  		return -ENOMEM; -	}  	inputdev->name = "Ideapad extra buttons";  	inputdev->phys = "ideapad/input0"; @@ -623,7 +618,7 @@ static void ideapad_input_novokey(struct ideapad_private *priv)  {  	unsigned long long_pressed; -	if (read_ec_data(ideapad_handle, VPCCMD_R_NOVO, &long_pressed)) +	if (read_ec_data(priv->adev->handle, VPCCMD_R_NOVO, &long_pressed))  		return;  	if (long_pressed)  		ideapad_input_report(priv, 17); @@ -635,7 +630,7 @@ static void ideapad_check_special_buttons(struct ideapad_private *priv)  {  	unsigned long bit, value; -	read_ec_data(ideapad_handle, VPCCMD_R_SPECIAL_BUTTONS, &value); +	read_ec_data(priv->adev->handle, VPCCMD_R_SPECIAL_BUTTONS, &value);  	for (bit = 0; bit < 16; bit++) {  		if (test_bit(bit, &value)) { @@ -662,19 +657,28 @@ static void ideapad_check_special_buttons(struct ideapad_private *priv)   */  static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)  { +	struct ideapad_private *priv = bl_get_data(blightdev);  	unsigned long now; -	if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now)) +	if (!priv) +		return -EINVAL; + +	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))  		return -EIO;  	return now;  }  static int ideapad_backlight_update_status(struct backlight_device *blightdev)  { -	if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL, +	struct ideapad_private *priv = bl_get_data(blightdev); + +	if (!priv) +		return -EINVAL; + +	if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL,  			 blightdev->props.brightness))  		return -EIO; -	if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL_POWER, +	if (write_ec_cmd(priv->adev->handle, VPCCMD_W_BL_POWER,  			 blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))  		return -EIO; @@ -692,11 +696,11 @@ static int ideapad_backlight_init(struct ideapad_private *priv)  	struct backlight_properties props;  	unsigned long max, now, power; -	if (read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &max)) +	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_MAX, &max))  		return -EIO; -	if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now)) +	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now))  		return -EIO; -	if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power)) +	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))  		return -EIO;  	memset(&props, 0, sizeof(struct backlight_properties)); @@ -734,7 +738,7 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv)  	if (!blightdev)  		return; -	if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power)) +	if (read_ec_data(priv->adev->handle, VPCCMD_R_BL_POWER, &power))  		return;  	blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;  } @@ -745,7 +749,7 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)  	/* if we control brightness via acpi video driver */  	if (priv->blightdev == NULL) { -		read_ec_data(ideapad_handle, VPCCMD_R_BL, &now); +		read_ec_data(priv->adev->handle, VPCCMD_R_BL, &now);  		return;  	} @@ -755,19 +759,12 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)  /*   * module init/exit   */ -static const struct acpi_device_id ideapad_device_ids[] = { -	{ "VPC2004", 0}, -	{ "", 0}, -}; -MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); - -static void ideapad_sync_touchpad_state(struct acpi_device *adevice) +static void ideapad_sync_touchpad_state(struct ideapad_private *priv)  { -	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);  	unsigned long value;  	/* Without reading from EC touchpad LED doesn't switch state */ -	if (!read_ec_data(adevice->handle, VPCCMD_R_TOUCHPAD, &value)) { +	if (!read_ec_data(priv->adev->handle, VPCCMD_R_TOUCHPAD, &value)) {  		/* Some IdeaPads don't really turn off touchpad - they only  		 * switch the LED state. We (de)activate KBC AUX port to turn  		 * touchpad off and on. We send KEY_TOUCHPAD_OFF and @@ -779,26 +776,90 @@ static void ideapad_sync_touchpad_state(struct acpi_device *adevice)  	}  } -static int ideapad_acpi_add(struct acpi_device *adevice) +static void ideapad_acpi_notify(acpi_handle handle, u32 event, void *data) +{ +	struct ideapad_private *priv = data; +	unsigned long vpc1, vpc2, vpc_bit; + +	if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1)) +		return; +	if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2)) +		return; + +	vpc1 = (vpc2 << 8) | vpc1; +	for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { +		if (test_bit(vpc_bit, &vpc1)) { +			switch (vpc_bit) { +			case 9: +				ideapad_sync_rfk_state(priv); +				break; +			case 13: +			case 11: +			case 7: +			case 6: +				ideapad_input_report(priv, vpc_bit); +				break; +			case 5: +				ideapad_sync_touchpad_state(priv); +				break; +			case 4: +				ideapad_backlight_notify_brightness(priv); +				break; +			case 3: +				ideapad_input_novokey(priv); +				break; +			case 2: +				ideapad_backlight_notify_power(priv); +				break; +			case 0: +				ideapad_check_special_buttons(priv); +				break; +			default: +				pr_info("Unknown event: %lu\n", vpc_bit); +			} +		} +	} +} + +/* Blacklist for devices where the ideapad rfkill interface does not work */ +static struct dmi_system_id rfkill_blacklist[] = { +	/* The Lenovo Yoga 2 11 always reports everything as blocked */ +	{ +		.ident = "Lenovo Yoga 2 11", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"), +		}, +	}, +	{} +}; + +static int ideapad_acpi_add(struct platform_device *pdev)  {  	int ret, i;  	int cfg;  	struct ideapad_private *priv; +	struct acpi_device *adev; + +	ret = acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev); +	if (ret) +		return -ENODEV; -	if (read_method_int(adevice->handle, "_CFG", &cfg)) +	if (read_method_int(adev->handle, "_CFG", &cfg))  		return -ENODEV; -	priv = kzalloc(sizeof(*priv), GFP_KERNEL); +	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);  	if (!priv)  		return -ENOMEM; -	dev_set_drvdata(&adevice->dev, priv); -	ideapad_priv = priv; -	ideapad_handle = adevice->handle; + +	dev_set_drvdata(&pdev->dev, priv);  	priv->cfg = cfg; +	priv->adev = adev; +	priv->platform_device = pdev; -	ret = ideapad_platform_init(priv); +	ret = ideapad_sysfs_init(priv);  	if (ret) -		goto platform_failed; +		return ret;  	ret = ideapad_debugfs_init(priv);  	if (ret) @@ -808,119 +869,90 @@ static int ideapad_acpi_add(struct acpi_device *adevice)  	if (ret)  		goto input_failed; -	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) { -		if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) -			ideapad_register_rfkill(adevice, i); -		else -			priv->rfk[i] = NULL; +	if (!dmi_check_system(rfkill_blacklist)) { +		for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) +			if (test_bit(ideapad_rfk_data[i].cfgbit, &priv->cfg)) +				ideapad_register_rfkill(priv, i);  	}  	ideapad_sync_rfk_state(priv); -	ideapad_sync_touchpad_state(adevice); +	ideapad_sync_touchpad_state(priv);  	if (!acpi_video_backlight_support()) {  		ret = ideapad_backlight_init(priv);  		if (ret && ret != -ENODEV)  			goto backlight_failed;  	} +	ret = acpi_install_notify_handler(adev->handle, +		ACPI_DEVICE_NOTIFY, ideapad_acpi_notify, priv); +	if (ret) +		goto notification_failed;  	return 0; - +notification_failed: +	ideapad_backlight_exit(priv);  backlight_failed:  	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) -		ideapad_unregister_rfkill(adevice, i); +		ideapad_unregister_rfkill(priv, i);  	ideapad_input_exit(priv);  input_failed:  	ideapad_debugfs_exit(priv);  debugfs_failed: -	ideapad_platform_exit(priv); -platform_failed: -	kfree(priv); +	ideapad_sysfs_exit(priv);  	return ret;  } -static int ideapad_acpi_remove(struct acpi_device *adevice) +static int ideapad_acpi_remove(struct platform_device *pdev)  { -	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); +	struct ideapad_private *priv = dev_get_drvdata(&pdev->dev);  	int i; +	acpi_remove_notify_handler(priv->adev->handle, +		ACPI_DEVICE_NOTIFY, ideapad_acpi_notify);  	ideapad_backlight_exit(priv);  	for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) -		ideapad_unregister_rfkill(adevice, i); +		ideapad_unregister_rfkill(priv, i);  	ideapad_input_exit(priv);  	ideapad_debugfs_exit(priv); -	ideapad_platform_exit(priv); -	dev_set_drvdata(&adevice->dev, NULL); -	kfree(priv); +	ideapad_sysfs_exit(priv); +	dev_set_drvdata(&pdev->dev, NULL);  	return 0;  } -static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) +#ifdef CONFIG_PM_SLEEP +static int ideapad_acpi_resume(struct device *device)  { -	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); -	acpi_handle handle = adevice->handle; -	unsigned long vpc1, vpc2, vpc_bit; - -	if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1)) -		return; -	if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2)) -		return; +	struct ideapad_private *priv; -	vpc1 = (vpc2 << 8) | vpc1; -	for (vpc_bit = 0; vpc_bit < 16; vpc_bit++) { -		if (test_bit(vpc_bit, &vpc1)) { -			switch (vpc_bit) { -			case 9: -				ideapad_sync_rfk_state(priv); -				break; -			case 13: -			case 11: -			case 7: -			case 6: -				ideapad_input_report(priv, vpc_bit); -				break; -			case 5: -				ideapad_sync_touchpad_state(adevice); -				break; -			case 4: -				ideapad_backlight_notify_brightness(priv); -				break; -			case 3: -				ideapad_input_novokey(priv); -				break; -			case 2: -				ideapad_backlight_notify_power(priv); -				break; -			case 0: -				ideapad_check_special_buttons(priv); -				break; -			default: -				pr_info("Unknown event: %lu\n", vpc_bit); -			} -		} -	} -} +	if (!device) +		return -EINVAL; +	priv = dev_get_drvdata(device); -static int ideapad_acpi_resume(struct device *device) -{ -	ideapad_sync_rfk_state(ideapad_priv); -	ideapad_sync_touchpad_state(to_acpi_device(device)); +	ideapad_sync_rfk_state(priv); +	ideapad_sync_touchpad_state(priv);  	return 0;  } - +#endif  static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume); -static struct acpi_driver ideapad_acpi_driver = { -	.name = "ideapad_acpi", -	.class = "IdeaPad", -	.ids = ideapad_device_ids, -	.ops.add = ideapad_acpi_add, -	.ops.remove = ideapad_acpi_remove, -	.ops.notify = ideapad_acpi_notify, -	.drv.pm = &ideapad_pm, -	.owner = THIS_MODULE, +static const struct acpi_device_id ideapad_device_ids[] = { +	{ "VPC2004", 0}, +	{ "", 0},  }; -module_acpi_driver(ideapad_acpi_driver); +MODULE_DEVICE_TABLE(acpi, ideapad_device_ids); + +static struct platform_driver ideapad_acpi_driver = { +	.probe = ideapad_acpi_add, +	.remove = ideapad_acpi_remove, +	.driver = { +		.name   = "ideapad_acpi", +		.owner  = THIS_MODULE, +		.pm     = &ideapad_pm, +		.acpi_match_table = ACPI_PTR(ideapad_device_ids), +	}, +}; + +module_platform_driver(ideapad_acpi_driver);  MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");  MODULE_DESCRIPTION("IdeaPad ACPI Extras"); diff --git a/drivers/platform/x86/intel-rst.c b/drivers/platform/x86/intel-rst.c index 41b740cb28b..d45bca34bf1 100644 --- a/drivers/platform/x86/intel-rst.c +++ b/drivers/platform/x86/intel-rst.c @@ -20,7 +20,7 @@  #include <linux/init.h>  #include <linux/module.h>  #include <linux/slab.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  MODULE_LICENSE("GPL"); @@ -29,24 +29,16 @@ static ssize_t irst_show_wakeup_events(struct device *dev,  				       char *buf)  {  	struct acpi_device *acpi; -	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; -	union acpi_object *result; +	unsigned long long value;  	acpi_status status;  	acpi = to_acpi_device(dev); -	status = acpi_evaluate_object(acpi->handle, "GFFS", NULL, &output); +	status = acpi_evaluate_integer(acpi->handle, "GFFS", NULL, &value);  	if (!ACPI_SUCCESS(status))  		return -EINVAL; -	result = output.pointer; - -	if (result->type != ACPI_TYPE_INTEGER) { -		kfree(result); -		return -EINVAL; -	} - -	return sprintf(buf, "%lld\n", result->integer.value); +	return sprintf(buf, "%lld\n", value);  }  static ssize_t irst_store_wakeup_events(struct device *dev, @@ -54,8 +46,6 @@ static ssize_t irst_store_wakeup_events(struct device *dev,  					const char *buf, size_t count)  {  	struct acpi_device *acpi; -	struct acpi_object_list input; -	union acpi_object param;  	acpi_status status;  	unsigned long value;  	int error; @@ -67,13 +57,7 @@ static ssize_t irst_store_wakeup_events(struct device *dev,  	if (error)  		return error; -	param.type = ACPI_TYPE_INTEGER; -	param.integer.value = value; - -	input.count = 1; -	input.pointer = ¶m; - -	status = acpi_evaluate_object(acpi->handle, "SFFS", &input, NULL); +	status = acpi_execute_simple_method(acpi->handle, "SFFS", value);  	if (!ACPI_SUCCESS(status))  		return -EINVAL; @@ -91,24 +75,16 @@ static ssize_t irst_show_wakeup_time(struct device *dev,  				     struct device_attribute *attr, char *buf)  {  	struct acpi_device *acpi; -	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; -	union acpi_object *result; +	unsigned long long value;  	acpi_status status;  	acpi = to_acpi_device(dev); -	status = acpi_evaluate_object(acpi->handle, "GFTV", NULL, &output); +	status = acpi_evaluate_integer(acpi->handle, "GFTV", NULL, &value);  	if (!ACPI_SUCCESS(status))  		return -EINVAL; -	result = output.pointer; - -	if (result->type != ACPI_TYPE_INTEGER) { -		kfree(result); -		return -EINVAL; -	} - -	return sprintf(buf, "%lld\n", result->integer.value); +	return sprintf(buf, "%lld\n", value);  }  static ssize_t irst_store_wakeup_time(struct device *dev, @@ -116,8 +92,6 @@ static ssize_t irst_store_wakeup_time(struct device *dev,  				      const char *buf, size_t count)  {  	struct acpi_device *acpi; -	struct acpi_object_list input; -	union acpi_object param;  	acpi_status status;  	unsigned long value;  	int error; @@ -129,13 +103,7 @@ static ssize_t irst_store_wakeup_time(struct device *dev,  	if (error)  		return error; -	param.type = ACPI_TYPE_INTEGER; -	param.integer.value = value; - -	input.count = 1; -	input.pointer = ¶m; - -	status = acpi_evaluate_object(acpi->handle, "SFTV", &input, NULL); +	status = acpi_execute_simple_method(acpi->handle, "SFTV", value);  	if (!ACPI_SUCCESS(status))  		return -EINVAL; diff --git a/drivers/platform/x86/intel-smartconnect.c b/drivers/platform/x86/intel-smartconnect.c index 52259dcabec..04cf5dffdfd 100644 --- a/drivers/platform/x86/intel-smartconnect.c +++ b/drivers/platform/x86/intel-smartconnect.c @@ -19,43 +19,24 @@  #include <linux/init.h>  #include <linux/module.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  MODULE_LICENSE("GPL");  static int smartconnect_acpi_init(struct acpi_device *acpi)  { -	struct acpi_object_list input; -	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; -	union acpi_object *result; -	union acpi_object param; +	unsigned long long value;  	acpi_status status; -	status = acpi_evaluate_object(acpi->handle, "GAOS", NULL, &output); +	status = acpi_evaluate_integer(acpi->handle, "GAOS", NULL, &value);  	if (!ACPI_SUCCESS(status))  		return -EINVAL; -	result = output.pointer; - -	if (result->type != ACPI_TYPE_INTEGER) { -		kfree(result); -		return -EINVAL; -	} - -	if (result->integer.value & 0x1) { -		param.type = ACPI_TYPE_INTEGER; -		param.integer.value = 0; - -		input.count = 1; -		input.pointer = ¶m; - +	if (value & 0x1) {  		dev_info(&acpi->dev, "Disabling Intel Smart Connect\n"); -		status = acpi_evaluate_object(acpi->handle, "SAOS", &input, -					      NULL); +		status = acpi_execute_simple_method(acpi->handle, "SAOS", 0);  	} -	kfree(result); -  	return 0;  } diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index d6cfc1558c2..e8b46d2c468 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -36,10 +36,8 @@  #include <linux/types.h>  #include <linux/pci.h>  #include <linux/pm.h> -  #include <linux/thermal.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  MODULE_AUTHOR("Thomas Sujith");  MODULE_AUTHOR("Zhang Rui"); @@ -156,19 +154,15 @@ static struct thermal_cooling_device_ops memory_cooling_ops = {  static int intel_menlow_memory_add(struct acpi_device *device)  {  	int result = -ENODEV; -	acpi_status status = AE_OK; -	acpi_handle dummy;  	struct thermal_cooling_device *cdev;  	if (!device)  		return -EINVAL; -	status = acpi_get_handle(device->handle, MEMORY_GET_BANDWIDTH, &dummy); -	if (ACPI_FAILURE(status)) +	if (!acpi_has_method(device->handle, MEMORY_GET_BANDWIDTH))  		goto end; -	status = acpi_get_handle(device->handle, MEMORY_SET_BANDWIDTH, &dummy); -	if (ACPI_FAILURE(status)) +	if (!acpi_has_method(device->handle, MEMORY_SET_BANDWIDTH))  		goto end;  	cdev = thermal_cooling_device_register("Memory controller", device, diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index 6b18aba82cf..8d6775266d6 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -66,10 +66,8 @@ static int mfld_pb_probe(struct platform_device *pdev)  		return -EINVAL;  	input = input_allocate_device(); -	if (!input) { -		dev_err(&pdev->dev, "Input device allocation error\n"); +	if (!input)  		return -ENOMEM; -	}  	input->name = pdev->name;  	input->phys = "power-button/input0"; diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 93fab8b70ce..ab7860a21a2 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -481,7 +481,8 @@ static int mid_thermal_probe(struct platform_device *pdev)  	int i;  	struct platform_info *pinfo; -	pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL); +	pinfo = devm_kzalloc(&pdev->dev, sizeof(struct platform_info), +			     GFP_KERNEL);  	if (!pinfo)  		return -ENOMEM; @@ -489,7 +490,6 @@ static int mid_thermal_probe(struct platform_device *pdev)  	ret = mid_initialize_adc(&pdev->dev);  	if (ret) {  		dev_err(&pdev->dev, "ADC init failed"); -		kfree(pinfo);  		return ret;  	} @@ -520,7 +520,6 @@ err:  		thermal_zone_device_unregister(pinfo->tzd[i]);  	}  	configure_adc(0); -	kfree(pinfo);  	return ret;  } @@ -541,8 +540,6 @@ static int mid_thermal_remove(struct platform_device *pdev)  		thermal_zone_device_unregister(pinfo->tzd[i]);  	} -	kfree(pinfo); -  	/* Stop the ADC */  	return configure_adc(0);  } diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c index f6f18cde0f1..4bc96041678 100644 --- a/drivers/platform/x86/intel_oaktrail.c +++ b/drivers/platform/x86/intel_oaktrail.c @@ -50,9 +50,6 @@  #include <linux/platform_device.h>  #include <linux/dmi.h>  #include <linux/rfkill.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> -  #define DRIVER_NAME	"intel_oaktrail"  #define DRIVER_VERSION	"0.4ac1" diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 2805988485f..40929e4f7ad 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -91,7 +91,7 @@ static void pmic_program_irqtype(int gpio, int type)  static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)  { -	if (offset > 8) { +	if (offset >= 8) {  		pr_err("only pin 0-7 support input\n");  		return -1;/* we only have 8 GPIO can use as input */  	} @@ -130,7 +130,7 @@ static int pmic_gpio_get(struct gpio_chip *chip, unsigned offset)  	int ret;  	/* we only have 8 GPIO pins we can use as input */ -	if (offset > 8) +	if (offset >= 8)  		return -EOPNOTSUPP;  	ret = intel_scu_ipc_ioread8(GPIO0 + offset, &r);  	if (ret < 0) diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index 9215ed72bec..76ca094ed01 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -25,7 +25,7 @@  #include <linux/interrupt.h>  #include <linux/sfi.h>  #include <linux/module.h> -#include <asm/mrst.h> +#include <asm/intel-mid.h>  #include <asm/intel_scu_ipc.h>  /* IPC defines the following message types */ @@ -58,12 +58,48 @@   *    message handler is called within firmware.   */ -#define IPC_BASE_ADDR     0xFF11C000	/* IPC1 base register address */ -#define IPC_MAX_ADDR      0x100		/* Maximum IPC regisers */  #define IPC_WWBUF_SIZE    20		/* IPC Write buffer Size */  #define IPC_RWBUF_SIZE    20		/* IPC Read buffer Size */ -#define IPC_I2C_BASE      0xFF12B000	/* I2C control register base address */ -#define IPC_I2C_MAX_ADDR  0x10		/* Maximum I2C regisers */ +#define IPC_IOC	          0x100		/* IPC command register IOC bit */ + +#define PCI_DEVICE_ID_LINCROFT		0x082a +#define PCI_DEVICE_ID_PENWELL		0x080e +#define PCI_DEVICE_ID_CLOVERVIEW	0x08ea +#define PCI_DEVICE_ID_TANGIER		0x11a0 + +/* intel scu ipc driver data*/ +struct intel_scu_ipc_pdata_t { +	u32 ipc_base; +	u32 i2c_base; +	u32 ipc_len; +	u32 i2c_len; +	u8 irq_mode; +}; + +static struct intel_scu_ipc_pdata_t intel_scu_ipc_lincroft_pdata = { +	.ipc_base = 0xff11c000, +	.i2c_base = 0xff12b000, +	.ipc_len = 0x100, +	.i2c_len = 0x10, +	.irq_mode = 0, +}; + +/* Penwell and Cloverview */ +static struct intel_scu_ipc_pdata_t intel_scu_ipc_penwell_pdata = { +	.ipc_base = 0xff11c000, +	.i2c_base = 0xff12b000, +	.ipc_len = 0x100, +	.i2c_len = 0x10, +	.irq_mode = 1, +}; + +static struct intel_scu_ipc_pdata_t intel_scu_ipc_tangier_pdata = { +	.ipc_base = 0xff009000, +	.i2c_base  = 0xff00d000, +	.ipc_len  = 0x100, +	.i2c_len = 0x10, +	.irq_mode = 0, +};  static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id);  static void ipc_remove(struct pci_dev *pdev); @@ -72,6 +108,8 @@ struct intel_scu_ipc_dev {  	struct pci_dev *pdev;  	void __iomem *ipc_base;  	void __iomem *i2c_base; +	struct completion cmd_complete; +	u8 irq_mode;  };  static struct intel_scu_ipc_dev  ipcdev; /* Only one for now */ @@ -98,6 +136,10 @@ static DEFINE_MUTEX(ipclock); /* lock used to prevent multiple call to SCU */   */  static inline void ipc_command(u32 cmd) /* Send ipc command */  { +	if (ipcdev.irq_mode) { +		reinit_completion(&ipcdev.cmd_complete); +		writel(cmd | IPC_IOC, ipcdev.ipc_base); +	}  	writel(cmd, ipcdev.ipc_base);  } @@ -156,6 +198,30 @@ static inline int busy_loop(void) /* Wait till scu status is busy */  	return 0;  } +/* Wait till ipc ioc interrupt is received or timeout in 3 HZ */ +static inline int ipc_wait_for_interrupt(void) +{ +	int status; + +	if (!wait_for_completion_timeout(&ipcdev.cmd_complete, 3 * HZ)) { +		struct device *dev = &ipcdev.pdev->dev; +		dev_err(dev, "IPC timed out\n"); +		return -ETIMEDOUT; +	} + +	status = ipc_read_status(); + +	if ((status >> 1) & 1) +		return -EIO; + +	return 0; +} + +int intel_scu_ipc_check_status(void) +{ +	return ipcdev.irq_mode ? ipc_wait_for_interrupt() : busy_loop(); +} +  /* Read/Write power control(PMIC in Langwell, MSIC in PenWell) registers */  static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)  { @@ -196,8 +262,8 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id)  		ipc_command(4 << 16 |  id << 12 | 0 << 8 | op);  	} -	err = busy_loop(); -	if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ +	err = intel_scu_ipc_check_status(); +	if (!err && id == IPC_CMD_PCNTRL_R) { /* Read rbuf */  		/* Workaround: values are read as 0 without memcpy_fromio */  		memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16);  		for (nc = 0; nc < count; nc++) @@ -391,7 +457,7 @@ int intel_scu_ipc_simple_command(int cmd, int sub)  		return -ENODEV;  	}  	ipc_command(sub << 12 | cmd); -	err = busy_loop(); +	err = intel_scu_ipc_check_status();  	mutex_unlock(&ipclock);  	return err;  } @@ -425,10 +491,12 @@ int intel_scu_ipc_command(int cmd, int sub, u32 *in, int inlen,  		ipc_data_writel(*in++, 4 * i);  	ipc_command((inlen << 16) | (sub << 12) | cmd); -	err = busy_loop(); +	err = intel_scu_ipc_check_status(); -	for (i = 0; i < outlen; i++) -		*out++ = ipc_data_readl(4 * i); +	if (!err) { +		for (i = 0; i < outlen; i++) +			*out++ = ipc_data_readl(4 * i); +	}  	mutex_unlock(&ipclock);  	return err; @@ -491,6 +559,9 @@ EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl);   */  static irqreturn_t ioc(int irq, void *dev_id)  { +	if (ipcdev.irq_mode) +		complete(&ipcdev.cmd_complete); +  	return IRQ_HANDLED;  } @@ -505,12 +576,16 @@ static irqreturn_t ioc(int irq, void *dev_id)  static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)  {  	int err; +	struct intel_scu_ipc_pdata_t *pdata;  	resource_size_t pci_resource;  	if (ipcdev.pdev)		/* We support only one SCU */  		return -EBUSY; +	pdata = (struct intel_scu_ipc_pdata_t *)id->driver_data; +  	ipcdev.pdev = pci_dev_get(dev); +	ipcdev.irq_mode = pdata->irq_mode;  	err = pci_enable_device(dev);  	if (err) @@ -524,14 +599,16 @@ static int ipc_probe(struct pci_dev *dev, const struct pci_device_id *id)  	if (!pci_resource)  		return -ENOMEM; +	init_completion(&ipcdev.cmd_complete); +  	if (request_irq(dev->irq, ioc, 0, "intel_scu_ipc", &ipcdev))  		return -EBUSY; -	ipcdev.ipc_base = ioremap_nocache(IPC_BASE_ADDR, IPC_MAX_ADDR); +	ipcdev.ipc_base = ioremap_nocache(pdata->ipc_base, pdata->ipc_len);  	if (!ipcdev.ipc_base)  		return -ENOMEM; -	ipcdev.i2c_base = ioremap_nocache(IPC_I2C_BASE, IPC_I2C_MAX_ADDR); +	ipcdev.i2c_base = ioremap_nocache(pdata->i2c_base, pdata->i2c_len);  	if (!ipcdev.i2c_base) {  		iounmap(ipcdev.ipc_base);  		return -ENOMEM; @@ -564,8 +641,21 @@ static void ipc_remove(struct pci_dev *pdev)  }  static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { -	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)}, -	{ 0,} +	{ +		PCI_VDEVICE(INTEL, PCI_DEVICE_ID_LINCROFT), +		(kernel_ulong_t)&intel_scu_ipc_lincroft_pdata, +	}, { +		PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PENWELL), +		(kernel_ulong_t)&intel_scu_ipc_penwell_pdata, +	}, { +		PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CLOVERVIEW), +		(kernel_ulong_t)&intel_scu_ipc_penwell_pdata, +	}, { +		PCI_VDEVICE(INTEL, PCI_DEVICE_ID_TANGIER), +		(kernel_ulong_t)&intel_scu_ipc_tangier_pdata, +	}, { +		0, +	}  };  MODULE_DEVICE_TABLE(pci, pci_ids); @@ -579,7 +669,7 @@ static struct pci_driver ipc_driver = {  static int __init intel_scu_ipc_init(void)  { -	platform = mrst_identify_cpu(); +	platform = intel_mid_identify_cpu();  	if (platform == 0)  		return -ENODEV;  	return  pci_register_driver(&ipc_driver); diff --git a/drivers/platform/x86/mxm-wmi.c b/drivers/platform/x86/mxm-wmi.c index 0aea63b3729..f4bad83053a 100644 --- a/drivers/platform/x86/mxm-wmi.c +++ b/drivers/platform/x86/mxm-wmi.c @@ -20,8 +20,8 @@  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/init.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/mxm-wmi.h> +#include <linux/acpi.h>  MODULE_AUTHOR("Dave Airlie");  MODULE_DESCRIPTION("MXM WMI Driver"); diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 10d12b22160..3f870972247 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -125,12 +125,10 @@  #include <linux/seq_file.h>  #include <linux/uaccess.h>  #include <linux/slab.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  #include <linux/input.h>  #include <linux/input/sparse-keymap.h> -  #ifndef ACPI_HOTKEY_COMPONENT  #define ACPI_HOTKEY_COMPONENT	0x10000000  #endif @@ -451,6 +449,7 @@ static struct attribute_group pcc_attr_group = {  /* hotkey input device driver */ +static int sleep_keydown_seen;  static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)  {  	struct input_dev *hotk_input_dev = pcc->input_dev; @@ -464,6 +463,16 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc)  				 "error getting hotkey status\n"));  		return;  	} + +	/* hack: some firmware sends no key down for sleep / hibernate */ +	if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) { +		if (result & 0x80) +			sleep_keydown_seen = 1; +		if (!sleep_keydown_seen) +			sparse_keymap_report_event(hotk_input_dev, +					result & 0xf, 0x80, false); +	} +  	if (!sparse_keymap_report_event(hotk_input_dev,  					result & 0xf, result & 0x80, false))  		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, @@ -490,11 +499,8 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc)  	int error;  	input_dev = input_allocate_device(); -	if (!input_dev) { -		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, -				  "Couldn't allocate input device for hotkey")); +	if (!input_dev)  		return -ENOMEM; -	}  	input_dev->name = ACPI_PCC_DRIVER_NAME;  	input_dev->phys = ACPI_PCC_INPUT_PHYS; diff --git a/drivers/platform/x86/pvpanic.c b/drivers/platform/x86/pvpanic.c index 47ae0c47d4b..073a90a63db 100644 --- a/drivers/platform/x86/pvpanic.c +++ b/drivers/platform/x86/pvpanic.c @@ -24,8 +24,7 @@  #include <linux/module.h>  #include <linux/init.h>  #include <linux/types.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");  MODULE_DESCRIPTION("pvpanic device driver"); @@ -71,6 +70,7 @@ pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,  static struct notifier_block pvpanic_panic_nb = {  	.notifier_call = pvpanic_panic_notify, +	.priority = 1, /* let this called before broken drm_fb_helper */  }; diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index d1f03005317..5a596651227 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -27,6 +27,7 @@  #include <linux/debugfs.h>  #include <linux/ctype.h>  #include <linux/efi.h> +#include <linux/suspend.h>  #include <acpi/video.h>  /* @@ -340,6 +341,8 @@ struct samsung_laptop {  	struct samsung_laptop_debug debug;  	struct samsung_quirks *quirks; +	struct notifier_block pm_nb; +  	bool handle_backlight;  	bool has_stepping_quirk; @@ -348,6 +351,8 @@ struct samsung_laptop {  struct samsung_quirks {  	bool broken_acpi_video; +	bool four_kbd_backlight_levels; +	bool enable_kbd_backlight;  };  static struct samsung_quirks samsung_unknown = {}; @@ -356,6 +361,11 @@ static struct samsung_quirks samsung_broken_acpi_video = {  	.broken_acpi_video = true,  }; +static struct samsung_quirks samsung_np740u3e = { +	.four_kbd_backlight_levels = true, +	.enable_kbd_backlight = true, +}; +  static bool force;  module_param(force, bool, 0);  MODULE_PARM_DESC(force, @@ -1051,6 +1061,8 @@ static int __init samsung_leds_init(struct samsung_laptop *samsung)  		samsung->kbd_led.brightness_set = kbd_led_set;  		samsung->kbd_led.brightness_get = kbd_led_get;  		samsung->kbd_led.max_brightness = 8; +		if (samsung->quirks->four_kbd_backlight_levels) +			samsung->kbd_led.max_brightness = 4;  		ret = led_classdev_register(&samsung->platform_device->dev,  					   &samsung->kbd_led); @@ -1414,6 +1426,19 @@ static void samsung_platform_exit(struct samsung_laptop *samsung)  	}  } +static int samsung_pm_notification(struct notifier_block *nb, +				   unsigned long val, void *ptr) +{ +	struct samsung_laptop *samsung; + +	samsung = container_of(nb, struct samsung_laptop, pm_nb); +	if (val == PM_POST_HIBERNATION && +	    samsung->quirks->enable_kbd_backlight) +		kbd_backlight_enable(samsung); + +	return 0; +} +  static int __init samsung_platform_init(struct samsung_laptop *samsung)  {  	struct platform_device *pdev; @@ -1534,6 +1559,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {  		},  	 .driver_data = &samsung_broken_acpi_video,  	}, +	{ +	 .callback = samsung_dmi_matched, +	 .ident = "730U3E/740U3E", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), +		DMI_MATCH(DMI_PRODUCT_NAME, "730U3E/740U3E"), +		}, +	 .driver_data = &samsung_np740u3e, +	},  	{ },  };  MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); @@ -1608,6 +1642,9 @@ static int __init samsung_init(void)  	if (ret)  		goto error_debugfs; +	samsung->pm_nb.notifier_call = samsung_pm_notification; +	register_pm_notifier(&samsung->pm_nb); +  	samsung_platform_device = samsung->platform_device;  	return ret; @@ -1633,6 +1670,7 @@ static void __exit samsung_exit(void)  	struct samsung_laptop *samsung;  	samsung = platform_get_drvdata(samsung_platform_device); +	unregister_pm_notifier(&samsung->pm_nb);  	samsung_debugfs_exit(samsung);  	samsung_leds_exit(samsung); diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c index cae7098e9b0..5413f62d2e6 100644 --- a/drivers/platform/x86/samsung-q10.c +++ b/drivers/platform/x86/samsung-q10.c @@ -15,7 +15,7 @@  #include <linux/platform_device.h>  #include <linux/backlight.h>  #include <linux/dmi.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  #define SAMSUNGQ10_BL_MAX_INTENSITY 7 diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index d3fd52036fd..9c5a07417b2 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -61,9 +61,6 @@  #include <linux/workqueue.h>  #include <linux/acpi.h>  #include <linux/slab.h> -#include <acpi/acpi_drivers.h> -#include <acpi/acpi_bus.h> -#include <asm/uaccess.h>  #include <linux/sonypi.h>  #include <linux/sony-laptop.h>  #include <linux/rfkill.h> @@ -71,6 +68,7 @@  #include <linux/poll.h>  #include <linux/miscdevice.h>  #endif +#include <asm/uaccess.h>  #define dprintk(fmt, ...)			\  do {						\ @@ -78,8 +76,6 @@ do {						\  		pr_warn(fmt, ##__VA_ARGS__);	\  } while (0) -#define SONY_LAPTOP_DRIVER_VERSION	"0.6" -  #define SONY_NC_CLASS		"sony-nc"  #define SONY_NC_HID		"SNY5001"  #define SONY_NC_DRIVER_NAME	"Sony Notebook Control Driver" @@ -91,7 +87,6 @@ do {						\  MODULE_AUTHOR("Stelian Pop, Mattia Dongili");  MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)");  MODULE_LICENSE("GPL"); -MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION);  static int debug;  module_param(debug, int, 0); @@ -127,26 +122,26 @@ MODULE_PARM_DESC(minor,  		 "default is -1 (automatic)");  #endif -static int kbd_backlight = 1; +static int kbd_backlight = -1;  module_param(kbd_backlight, int, 0444);  MODULE_PARM_DESC(kbd_backlight,  		 "set this to 0 to disable keyboard backlight, " -		 "1 to enable it (default: 0)"); +		 "1 to enable it with automatic control and 2 to have it always " +		 "on (default: no change from current value)"); -static int kbd_backlight_timeout;	/* = 0 */ +static int kbd_backlight_timeout = -1;  module_param(kbd_backlight_timeout, int, 0444);  MODULE_PARM_DESC(kbd_backlight_timeout, -		 "set this to 0 to set the default 10 seconds timeout, " -		 "1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout " -		 "(default: 0)"); +		 "meaningful values vary from 0 to 3 and their meaning depends " +		 "on the model (default: no change from current value)");  #ifdef CONFIG_PM_SLEEP -static void sony_nc_kbd_backlight_resume(void);  static void sony_nc_thermal_resume(void);  #endif  static int sony_nc_kbd_backlight_setup(struct platform_device *pd,  		unsigned int handle); -static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd); +static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd, +		unsigned int handle);  static int sony_nc_battery_care_setup(struct platform_device *pd,  		unsigned int handle); @@ -155,7 +150,8 @@ static void sony_nc_battery_care_cleanup(struct platform_device *pd);  static int sony_nc_thermal_setup(struct platform_device *pd);  static void sony_nc_thermal_cleanup(struct platform_device *pd); -static int sony_nc_lid_resume_setup(struct platform_device *pd); +static int sony_nc_lid_resume_setup(struct platform_device *pd, +				    unsigned int handle);  static void sony_nc_lid_resume_cleanup(struct platform_device *pd);  static int sony_nc_gfx_switch_setup(struct platform_device *pd, @@ -166,6 +162,21 @@ static int __sony_nc_gfx_switch_status_get(void);  static int sony_nc_highspeed_charging_setup(struct platform_device *pd);  static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); +static int sony_nc_lowbatt_setup(struct platform_device *pd); +static void sony_nc_lowbatt_cleanup(struct platform_device *pd); + +static int sony_nc_fanspeed_setup(struct platform_device *pd); +static void sony_nc_fanspeed_cleanup(struct platform_device *pd); + +static int sony_nc_usb_charge_setup(struct platform_device *pd); +static void sony_nc_usb_charge_cleanup(struct platform_device *pd); + +static int sony_nc_panelid_setup(struct platform_device *pd); +static void sony_nc_panelid_cleanup(struct platform_device *pd); + +static int sony_nc_smart_conn_setup(struct platform_device *pd); +static void sony_nc_smart_conn_cleanup(struct platform_device *pd); +  static int sony_nc_touchpad_setup(struct platform_device *pd,  				  unsigned int handle);  static void sony_nc_touchpad_cleanup(struct platform_device *pd); @@ -305,8 +316,8 @@ static int sony_laptop_input_keycode_map[] = {  	KEY_FN_F10,	/* 14 SONYPI_EVENT_FNKEY_F10 */  	KEY_FN_F11,	/* 15 SONYPI_EVENT_FNKEY_F11 */  	KEY_FN_F12,	/* 16 SONYPI_EVENT_FNKEY_F12 */ -	KEY_FN_F1,	/* 17 SONYPI_EVENT_FNKEY_1 */ -	KEY_FN_F2,	/* 18 SONYPI_EVENT_FNKEY_2 */ +	KEY_FN_1,	/* 17 SONYPI_EVENT_FNKEY_1 */ +	KEY_FN_2,	/* 18 SONYPI_EVENT_FNKEY_2 */  	KEY_FN_D,	/* 19 SONYPI_EVENT_FNKEY_D */  	KEY_FN_E,	/* 20 SONYPI_EVENT_FNKEY_E */  	KEY_FN_F,	/* 21 SONYPI_EVENT_FNKEY_F */ @@ -792,7 +803,7 @@ static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,  		void *buffer, size_t buflen)  {  	int ret = 0; -	size_t len = len; +	size_t len;  	union acpi_object *object = __call_snc_method(handle, name, value);  	if (!object) @@ -1125,6 +1136,8 @@ static struct sony_nc_event sony_100_events[] = {  	{ 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED },  	{ 0xa6, SONYPI_EVENT_HELP_PRESSED },  	{ 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, +	{ 0xa8, SONYPI_EVENT_FNKEY_1 }, +	{ 0x28, SONYPI_EVENT_ANYBUTTON_RELEASED },  	{ 0, 0 },  }; @@ -1342,7 +1355,8 @@ static void sony_nc_function_setup(struct acpi_device *device,  						result);  			break;  		case 0x0119: -			result = sony_nc_lid_resume_setup(pf_device); +		case 0x015D: +			result = sony_nc_lid_resume_setup(pf_device, handle);  			if (result)  				pr_err("couldn't set up lid resume function (%d)\n",  						result); @@ -1384,6 +1398,36 @@ static void sony_nc_function_setup(struct acpi_device *device,  				pr_err("couldn't set up keyboard backlight function (%d)\n",  						result);  			break; +		case 0x0121: +			result = sony_nc_lowbatt_setup(pf_device); +			if (result) +				pr_err("couldn't set up low battery function (%d)\n", +				       result); +			break; +		case 0x0149: +			result = sony_nc_fanspeed_setup(pf_device); +			if (result) +				pr_err("couldn't set up fan speed function (%d)\n", +				       result); +			break; +		case 0x0155: +			result = sony_nc_usb_charge_setup(pf_device); +			if (result) +				pr_err("couldn't set up USB charge support (%d)\n", +						result); +			break; +		case 0x011D: +			result = sony_nc_panelid_setup(pf_device); +			if (result) +				pr_err("couldn't set up panel ID function (%d)\n", +				       result); +			break; +		case 0x0168: +			result = sony_nc_smart_conn_setup(pf_device); +			if (result) +				pr_err("couldn't set up smart connect support (%d)\n", +						result); +			break;  		default:  			continue;  		} @@ -1423,6 +1467,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd)  			sony_nc_battery_care_cleanup(pd);  			break;  		case 0x0119: +		case 0x015D:  			sony_nc_lid_resume_cleanup(pd);  			break;  		case 0x0122: @@ -1445,7 +1490,22 @@ static void sony_nc_function_cleanup(struct platform_device *pd)  		case 0x014b:  		case 0x014c:  		case 0x0163: -			sony_nc_kbd_backlight_cleanup(pd); +			sony_nc_kbd_backlight_cleanup(pd, handle); +			break; +		case 0x0121: +			sony_nc_lowbatt_cleanup(pd); +			break; +		case 0x0149: +			sony_nc_fanspeed_cleanup(pd); +			break; +		case 0x0155: +			sony_nc_usb_charge_cleanup(pd); +			break; +		case 0x011D: +			sony_nc_panelid_cleanup(pd); +			break; +		case 0x0168: +			sony_nc_smart_conn_cleanup(pd);  			break;  		default:  			continue; @@ -1487,13 +1547,6 @@ static void sony_nc_function_resume(void)  		case 0x0135:  			sony_nc_rfkill_update();  			break; -		case 0x0137: -		case 0x0143: -		case 0x014b: -		case 0x014c: -		case 0x0163: -			sony_nc_kbd_backlight_resume(); -			break;  		default:  			continue;  		} @@ -1509,7 +1562,6 @@ static void sony_nc_function_resume(void)  static int sony_nc_resume(struct device *dev)  {  	struct sony_nc_value *item; -	acpi_handle handle;  	for (item = sony_nc_values; item->name; item++) {  		int ret; @@ -1524,15 +1576,13 @@ static int sony_nc_resume(struct device *dev)  		}  	} -	if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", -					 &handle))) { +	if (acpi_has_method(sony_nc_acpi_handle, "ECON")) {  		int arg = 1;  		if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))  			dprintk("ECON Method failed\n");  	} -	if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", -					 &handle))) +	if (acpi_has_method(sony_nc_acpi_handle, "SN00"))  		sony_nc_function_resume();  	return 0; @@ -1732,7 +1782,7 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)  {  	int result; -	if (value > 1) +	if (value > 2)  		return -EINVAL;  	if (sony_call_snc_handle(kbdbl_ctl->handle, @@ -1740,8 +1790,10 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)  		return -EIO;  	/* Try to turn the light on/off immediately */ -	sony_call_snc_handle(kbdbl_ctl->handle, -			(value << 0x10) | (kbdbl_ctl->base + 0x100), &result); +	if (value != 1) +		sony_call_snc_handle(kbdbl_ctl->handle, +				(value << 0x0f) | (kbdbl_ctl->base + 0x100), +				&result);  	kbdbl_ctl->mode = value; @@ -1826,6 +1878,12 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,  	int result;  	int ret = 0; +	if (kbdbl_ctl) { +		pr_warn("handle 0x%.4x: keyboard backlight setup already done for 0x%.4x\n", +				handle, kbdbl_ctl->handle); +		return -EBUSY; +	} +  	/* verify the kbd backlight presence, these handles are not used for  	 * keyboard backlight only  	 */ @@ -1844,6 +1902,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,  	if (!kbdbl_ctl)  		return -ENOMEM; +	kbdbl_ctl->mode = kbd_backlight; +	kbdbl_ctl->timeout = kbd_backlight_timeout;  	kbdbl_ctl->handle = handle;  	if (handle == 0x0137)  		kbdbl_ctl->base = 0x0C00; @@ -1870,8 +1930,8 @@ static int sony_nc_kbd_backlight_setup(struct platform_device *pd,  	if (ret)  		goto outmode; -	__sony_nc_kbd_backlight_mode_set(kbd_backlight); -	__sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout); +	__sony_nc_kbd_backlight_mode_set(kbdbl_ctl->mode); +	__sony_nc_kbd_backlight_timeout_set(kbdbl_ctl->timeout);  	return 0; @@ -1883,44 +1943,17 @@ outkzalloc:  	return ret;  } -static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd) +static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd, +		unsigned int handle)  { -	if (kbdbl_ctl) { -		int result; - +	if (kbdbl_ctl && handle == kbdbl_ctl->handle) {  		device_remove_file(&pd->dev, &kbdbl_ctl->mode_attr);  		device_remove_file(&pd->dev, &kbdbl_ctl->timeout_attr); - -		/* restore the default hw behaviour */ -		sony_call_snc_handle(kbdbl_ctl->handle, -				kbdbl_ctl->base | 0x10000, &result); -		sony_call_snc_handle(kbdbl_ctl->handle, -				kbdbl_ctl->base + 0x200, &result); -  		kfree(kbdbl_ctl);  		kbdbl_ctl = NULL;  	}  } -#ifdef CONFIG_PM_SLEEP -static void sony_nc_kbd_backlight_resume(void) -{ -	int ignore = 0; - -	if (!kbdbl_ctl) -		return; - -	if (kbdbl_ctl->mode == 0) -		sony_call_snc_handle(kbdbl_ctl->handle, kbdbl_ctl->base, -				&ignore); - -	if (kbdbl_ctl->timeout != 0) -		sony_call_snc_handle(kbdbl_ctl->handle, -				(kbdbl_ctl->base + 0x200) | -				(kbdbl_ctl->timeout << 0x10), &ignore); -} -#endif -  struct battery_care_control {  	struct device_attribute attrs[2];  	unsigned int handle; @@ -2253,9 +2286,14 @@ static void sony_nc_thermal_resume(void)  #endif  /* resume on LID open */ +#define LID_RESUME_S5	0 +#define LID_RESUME_S4	1 +#define LID_RESUME_S3	2 +#define LID_RESUME_MAX	3  struct snc_lid_resume_control { -	struct device_attribute attrs[3]; +	struct device_attribute attrs[LID_RESUME_MAX];  	unsigned int status; +	int handle;  };  static struct snc_lid_resume_control *lid_ctl; @@ -2263,8 +2301,9 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,  					struct device_attribute *attr,  					const char *buffer, size_t count)  { -	unsigned int result, pos; +	unsigned int result;  	unsigned long value; +	unsigned int pos = LID_RESUME_S5;  	if (count > 31)  		return -EINVAL; @@ -2277,21 +2316,21 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,  	 * +--------------+  	 *   2    1    0  	 */ -	if (strcmp(attr->attr.name, "lid_resume_S3") == 0) -		pos = 2; -	else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) -		pos = 1; -	else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) -		pos = 0; -	else -               return -EINVAL; +	while (pos < LID_RESUME_MAX) { +		if (&lid_ctl->attrs[pos].attr == &attr->attr) +			break; +		pos++; +	} +	if (pos == LID_RESUME_MAX) +		return -EINVAL;  	if (value)  		value = lid_ctl->status | (1 << pos);  	else  		value = lid_ctl->status & ~(1 << pos); -	if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result)) +	if (sony_call_snc_handle(lid_ctl->handle, value << 0x10 | 0x0100, +				&result))  		return -EIO;  	lid_ctl->status = value; @@ -2300,29 +2339,27 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,  }  static ssize_t sony_nc_lid_resume_show(struct device *dev, -				       struct device_attribute *attr, char *buffer) +					struct device_attribute *attr, +					char *buffer)  { -	unsigned int pos; +	unsigned int pos = LID_RESUME_S5; -	if (strcmp(attr->attr.name, "lid_resume_S3") == 0) -		pos = 2; -	else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) -		pos = 1; -	else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) -		pos = 0; -	else -		return -EINVAL; -	        -	return snprintf(buffer, PAGE_SIZE, "%d\n", -			(lid_ctl->status >> pos) & 0x01); +	while (pos < LID_RESUME_MAX) { +		if (&lid_ctl->attrs[pos].attr == &attr->attr) +			return snprintf(buffer, PAGE_SIZE, "%d\n", +					(lid_ctl->status >> pos) & 0x01); +		pos++; +	} +	return -EINVAL;  } -static int sony_nc_lid_resume_setup(struct platform_device *pd) +static int sony_nc_lid_resume_setup(struct platform_device *pd, +					unsigned int handle)  {  	unsigned int result;  	int i; -	if (sony_call_snc_handle(0x0119, 0x0000, &result)) +	if (sony_call_snc_handle(handle, 0x0000, &result))  		return -EIO;  	lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL); @@ -2330,26 +2367,29 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd)  		return -ENOMEM;  	lid_ctl->status = result & 0x7; +	lid_ctl->handle = handle;  	sysfs_attr_init(&lid_ctl->attrs[0].attr); -	lid_ctl->attrs[0].attr.name = "lid_resume_S3"; -	lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; -	lid_ctl->attrs[0].show = sony_nc_lid_resume_show; -	lid_ctl->attrs[0].store = sony_nc_lid_resume_store; - -	sysfs_attr_init(&lid_ctl->attrs[1].attr); -	lid_ctl->attrs[1].attr.name = "lid_resume_S4"; -	lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR; -	lid_ctl->attrs[1].show = sony_nc_lid_resume_show; -	lid_ctl->attrs[1].store = sony_nc_lid_resume_store; - -	sysfs_attr_init(&lid_ctl->attrs[2].attr); -	lid_ctl->attrs[2].attr.name = "lid_resume_S5"; -	lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR; -	lid_ctl->attrs[2].show = sony_nc_lid_resume_show; -	lid_ctl->attrs[2].store = sony_nc_lid_resume_store; - -	for (i = 0; i < 3; i++) { +	lid_ctl->attrs[LID_RESUME_S5].attr.name = "lid_resume_S5"; +	lid_ctl->attrs[LID_RESUME_S5].attr.mode = S_IRUGO | S_IWUSR; +	lid_ctl->attrs[LID_RESUME_S5].show = sony_nc_lid_resume_show; +	lid_ctl->attrs[LID_RESUME_S5].store = sony_nc_lid_resume_store; + +	if (handle == 0x0119) { +		sysfs_attr_init(&lid_ctl->attrs[1].attr); +		lid_ctl->attrs[LID_RESUME_S4].attr.name = "lid_resume_S4"; +		lid_ctl->attrs[LID_RESUME_S4].attr.mode = S_IRUGO | S_IWUSR; +		lid_ctl->attrs[LID_RESUME_S4].show = sony_nc_lid_resume_show; +		lid_ctl->attrs[LID_RESUME_S4].store = sony_nc_lid_resume_store; + +		sysfs_attr_init(&lid_ctl->attrs[2].attr); +		lid_ctl->attrs[LID_RESUME_S3].attr.name = "lid_resume_S3"; +		lid_ctl->attrs[LID_RESUME_S3].attr.mode = S_IRUGO | S_IWUSR; +		lid_ctl->attrs[LID_RESUME_S3].show = sony_nc_lid_resume_show; +		lid_ctl->attrs[LID_RESUME_S3].store = sony_nc_lid_resume_store; +	} +	for (i = 0; i < LID_RESUME_MAX && +			lid_ctl->attrs[LID_RESUME_S3].attr.name; i++) {  		result = device_create_file(&pd->dev, &lid_ctl->attrs[i]);  		if (result)  			goto liderror; @@ -2372,8 +2412,12 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd)  	int i;  	if (lid_ctl) { -		for (i = 0; i < 3; i++) +		for (i = 0; i < LID_RESUME_MAX; i++) { +			if (!lid_ctl->attrs[i].attr.name) +				break; +  			device_remove_file(&pd->dev, &lid_ctl->attrs[i]); +		}  		kfree(lid_ctl);  		lid_ctl = NULL; @@ -2556,6 +2600,355 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd)  	}  } +/* low battery function */ +static struct device_attribute *lowbatt_handle; + +static ssize_t sony_nc_lowbatt_store(struct device *dev, +		struct device_attribute *attr, +		const char *buffer, size_t count) +{ +	unsigned int result; +	unsigned long value; + +	if (count > 31) +		return -EINVAL; + +	if (kstrtoul(buffer, 10, &value) || value > 1) +		return -EINVAL; + +	if (sony_call_snc_handle(0x0121, value << 8, &result)) +		return -EIO; + +	return count; +} + +static ssize_t sony_nc_lowbatt_show(struct device *dev, +		struct device_attribute *attr, char *buffer) +{ +	unsigned int result; + +	if (sony_call_snc_handle(0x0121, 0x0200, &result)) +		return -EIO; + +	return snprintf(buffer, PAGE_SIZE, "%d\n", result & 1); +} + +static int sony_nc_lowbatt_setup(struct platform_device *pd) +{ +	unsigned int result; + +	lowbatt_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); +	if (!lowbatt_handle) +		return -ENOMEM; + +	sysfs_attr_init(&lowbatt_handle->attr); +	lowbatt_handle->attr.name = "lowbatt_hibernate"; +	lowbatt_handle->attr.mode = S_IRUGO | S_IWUSR; +	lowbatt_handle->show = sony_nc_lowbatt_show; +	lowbatt_handle->store = sony_nc_lowbatt_store; + +	result = device_create_file(&pd->dev, lowbatt_handle); +	if (result) { +		kfree(lowbatt_handle); +		lowbatt_handle = NULL; +		return result; +	} + +	return 0; +} + +static void sony_nc_lowbatt_cleanup(struct platform_device *pd) +{ +	if (lowbatt_handle) { +		device_remove_file(&pd->dev, lowbatt_handle); +		kfree(lowbatt_handle); +		lowbatt_handle = NULL; +	} +} + +/* fan speed function */ +static struct device_attribute *fan_handle, *hsf_handle; + +static ssize_t sony_nc_hsfan_store(struct device *dev, +		struct device_attribute *attr, +		const char *buffer, size_t count) +{ +	unsigned int result; +	unsigned long value; + +	if (count > 31) +		return -EINVAL; + +	if (kstrtoul(buffer, 10, &value) || value > 1) +		return -EINVAL; + +	if (sony_call_snc_handle(0x0149, value << 0x10 | 0x0200, &result)) +		return -EIO; + +	return count; +} + +static ssize_t sony_nc_hsfan_show(struct device *dev, +		struct device_attribute *attr, char *buffer) +{ +	unsigned int result; + +	if (sony_call_snc_handle(0x0149, 0x0100, &result)) +		return -EIO; + +	return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); +} + +static ssize_t sony_nc_fanspeed_show(struct device *dev, +		struct device_attribute *attr, char *buffer) +{ +	unsigned int result; + +	if (sony_call_snc_handle(0x0149, 0x0300, &result)) +		return -EIO; + +	return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff); +} + +static int sony_nc_fanspeed_setup(struct platform_device *pd) +{ +	unsigned int result; + +	fan_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); +	if (!fan_handle) +		return -ENOMEM; + +	hsf_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); +	if (!hsf_handle) { +		result = -ENOMEM; +		goto out_hsf_handle_alloc; +	} + +	sysfs_attr_init(&fan_handle->attr); +	fan_handle->attr.name = "fanspeed"; +	fan_handle->attr.mode = S_IRUGO; +	fan_handle->show = sony_nc_fanspeed_show; +	fan_handle->store = NULL; + +	sysfs_attr_init(&hsf_handle->attr); +	hsf_handle->attr.name = "fan_forced"; +	hsf_handle->attr.mode = S_IRUGO | S_IWUSR; +	hsf_handle->show = sony_nc_hsfan_show; +	hsf_handle->store = sony_nc_hsfan_store; + +	result = device_create_file(&pd->dev, fan_handle); +	if (result) +		goto out_fan_handle; + +	result = device_create_file(&pd->dev, hsf_handle); +	if (result) +		goto out_hsf_handle; + +	return 0; + +out_hsf_handle: +	device_remove_file(&pd->dev, fan_handle); + +out_fan_handle: +	kfree(hsf_handle); +	hsf_handle = NULL; + +out_hsf_handle_alloc: +	kfree(fan_handle); +	fan_handle = NULL; +	return result; +} + +static void sony_nc_fanspeed_cleanup(struct platform_device *pd) +{ +	if (fan_handle) { +		device_remove_file(&pd->dev, fan_handle); +		kfree(fan_handle); +		fan_handle = NULL; +	} +	if (hsf_handle) { +		device_remove_file(&pd->dev, hsf_handle); +		kfree(hsf_handle); +		hsf_handle = NULL; +	} +} + +/* USB charge function */ +static struct device_attribute *uc_handle; + +static ssize_t sony_nc_usb_charge_store(struct device *dev, +		struct device_attribute *attr, +		const char *buffer, size_t count) +{ +	unsigned int result; +	unsigned long value; + +	if (count > 31) +		return -EINVAL; + +	if (kstrtoul(buffer, 10, &value) || value > 1) +		return -EINVAL; + +	if (sony_call_snc_handle(0x0155, value << 0x10 | 0x0100, &result)) +		return -EIO; + +	return count; +} + +static ssize_t sony_nc_usb_charge_show(struct device *dev, +		struct device_attribute *attr, char *buffer) +{ +	unsigned int result; + +	if (sony_call_snc_handle(0x0155, 0x0000, &result)) +		return -EIO; + +	return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); +} + +static int sony_nc_usb_charge_setup(struct platform_device *pd) +{ +	unsigned int result; + +	if (sony_call_snc_handle(0x0155, 0x0000, &result) || !(result & 0x01)) { +		/* some models advertise the handle but have no implementation +		 * for it +		 */ +		pr_info("No USB Charge capability found\n"); +		return 0; +	} + +	uc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); +	if (!uc_handle) +		return -ENOMEM; + +	sysfs_attr_init(&uc_handle->attr); +	uc_handle->attr.name = "usb_charge"; +	uc_handle->attr.mode = S_IRUGO | S_IWUSR; +	uc_handle->show = sony_nc_usb_charge_show; +	uc_handle->store = sony_nc_usb_charge_store; + +	result = device_create_file(&pd->dev, uc_handle); +	if (result) { +		kfree(uc_handle); +		uc_handle = NULL; +		return result; +	} + +	return 0; +} + +static void sony_nc_usb_charge_cleanup(struct platform_device *pd) +{ +	if (uc_handle) { +		device_remove_file(&pd->dev, uc_handle); +		kfree(uc_handle); +		uc_handle = NULL; +	} +} + +/* Panel ID function */ +static struct device_attribute *panel_handle; + +static ssize_t sony_nc_panelid_show(struct device *dev, +		struct device_attribute *attr, char *buffer) +{ +	unsigned int result; + +	if (sony_call_snc_handle(0x011D, 0x0000, &result)) +		return -EIO; + +	return snprintf(buffer, PAGE_SIZE, "%d\n", result); +} + +static int sony_nc_panelid_setup(struct platform_device *pd) +{ +	unsigned int result; + +	panel_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); +	if (!panel_handle) +		return -ENOMEM; + +	sysfs_attr_init(&panel_handle->attr); +	panel_handle->attr.name = "panel_id"; +	panel_handle->attr.mode = S_IRUGO; +	panel_handle->show = sony_nc_panelid_show; +	panel_handle->store = NULL; + +	result = device_create_file(&pd->dev, panel_handle); +	if (result) { +		kfree(panel_handle); +		panel_handle = NULL; +		return result; +	} + +	return 0; +} + +static void sony_nc_panelid_cleanup(struct platform_device *pd) +{ +	if (panel_handle) { +		device_remove_file(&pd->dev, panel_handle); +		kfree(panel_handle); +		panel_handle = NULL; +	} +} + +/* smart connect function */ +static struct device_attribute *sc_handle; + +static ssize_t sony_nc_smart_conn_store(struct device *dev, +		struct device_attribute *attr, +		const char *buffer, size_t count) +{ +	unsigned int result; +	unsigned long value; + +	if (count > 31) +		return -EINVAL; + +	if (kstrtoul(buffer, 10, &value) || value > 1) +		return -EINVAL; + +	if (sony_call_snc_handle(0x0168, value << 0x10, &result)) +		return -EIO; + +	return count; +} + +static int sony_nc_smart_conn_setup(struct platform_device *pd) +{ +	unsigned int result; + +	sc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); +	if (!sc_handle) +		return -ENOMEM; + +	sysfs_attr_init(&sc_handle->attr); +	sc_handle->attr.name = "smart_connect"; +	sc_handle->attr.mode = S_IWUSR; +	sc_handle->show = NULL; +	sc_handle->store = sony_nc_smart_conn_store; + +	result = device_create_file(&pd->dev, sc_handle); +	if (result) { +		kfree(sc_handle); +		sc_handle = NULL; +		return result; +	} + +	return 0; +} + +static void sony_nc_smart_conn_cleanup(struct platform_device *pd) +{ +	if (sc_handle) { +		device_remove_file(&pd->dev, sc_handle); +		kfree(sc_handle); +		sc_handle = NULL; +	} +} +  /* Touchpad enable/disable */  struct touchpad_control {  	struct device_attribute attr; @@ -2690,7 +3083,6 @@ static void sony_nc_backlight_ng_read_limits(int handle,  static void sony_nc_backlight_setup(void)  { -	acpi_handle unused;  	int max_brightness = 0;  	const struct backlight_ops *ops = NULL;  	struct backlight_properties props; @@ -2725,8 +3117,7 @@ static void sony_nc_backlight_setup(void)  		sony_nc_backlight_ng_read_limits(0x14c, &sony_bl_props);  		max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; -	} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", -						&unused))) { +	} else if (acpi_has_method(sony_nc_acpi_handle, "GBRT")) {  		ops = &sony_backlight_ops;  		max_brightness = SONY_MAX_BRIGHTNESS - 1; @@ -2758,11 +3149,8 @@ static int sony_nc_add(struct acpi_device *device)  {  	acpi_status status;  	int result = 0; -	acpi_handle handle;  	struct sony_nc_value *item; -	pr_info("%s v%s\n", SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); -  	sony_nc_acpi_device = device;  	strcpy(acpi_device_class(device), "sony/hotkey"); @@ -2798,15 +3186,13 @@ static int sony_nc_add(struct acpi_device *device)  		goto outplatform;  	} -	if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "ECON", -					 &handle))) { +	if (acpi_has_method(sony_nc_acpi_handle, "ECON")) {  		int arg = 1;  		if (sony_nc_int_call(sony_nc_acpi_handle, "ECON", &arg, NULL))  			dprintk("ECON Method failed\n");  	} -	if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00", -					 &handle))) { +	if (acpi_has_method(sony_nc_acpi_handle, "SN00")) {  		dprintk("Doing SNC setup\n");  		/* retrieve the available handles */  		result = sony_nc_handles_setup(sony_pf_device); @@ -2829,9 +3215,8 @@ static int sony_nc_add(struct acpi_device *device)  		/* find the available acpiget as described in the DSDT */  		for (; item->acpiget && *item->acpiget; ++item->acpiget) { -			if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, -							 *item->acpiget, -							 &handle))) { +			if (acpi_has_method(sony_nc_acpi_handle, +							*item->acpiget)) {  				dprintk("Found %s getter: %s\n",  						item->name, *item->acpiget);  				item->devattr.attr.mode |= S_IRUGO; @@ -2841,9 +3226,8 @@ static int sony_nc_add(struct acpi_device *device)  		/* find the available acpiset as described in the DSDT */  		for (; item->acpiset && *item->acpiset; ++item->acpiset) { -			if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, -							 *item->acpiset, -							 &handle))) { +			if (acpi_has_method(sony_nc_acpi_handle, +							*item->acpiset)) {  				dprintk("Found %s setter: %s\n",  						item->name, *item->acpiset);  				item->devattr.attr.mode |= S_IWUSR; @@ -2860,6 +3244,7 @@ static int sony_nc_add(struct acpi_device *device)  		}  	} +	pr_info("SNC setup done.\n");  	return 0;  out_sysfs: @@ -4298,8 +4683,6 @@ static int sony_pic_add(struct acpi_device *device)  	struct sony_pic_ioport *io, *tmp_io;  	struct sony_pic_irq *irq, *tmp_irq; -	pr_info("%s v%s\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); -  	spic_dev.acpi_dev = device;  	strcpy(acpi_device_class(device), "sony/hotkey");  	sony_pic_detect_device_type(&spic_dev); @@ -4399,6 +4782,7 @@ static int sony_pic_add(struct acpi_device *device)  	if (result)  		goto err_remove_pf; +	pr_info("SPIC setup done.\n");  	return 0;  err_remove_pf: diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c index 9b93fdb61ed..6a6ea28a7e5 100644 --- a/drivers/platform/x86/tc1100-wmi.c +++ b/drivers/platform/x86/tc1100-wmi.c @@ -32,9 +32,7 @@  #include <linux/slab.h>  #include <linux/init.h>  #include <linux/types.h> -#include <acpi/acpi.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  #include <linux/platform_device.h>  #define GUID "C364AC71-36DB-495A-8494-B439D472A505" diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 03ca6c139f1..d82f196e3cf 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -23,7 +23,7 @@  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define TPACPI_VERSION "0.24" +#define TPACPI_VERSION "0.25"  #define TPACPI_SYSFS_VERSION 0x020700  /* @@ -61,7 +61,6 @@  #include <linux/freezer.h>  #include <linux/delay.h>  #include <linux/slab.h> -  #include <linux/nvram.h>  #include <linux/proc_fs.h>  #include <linux/seq_file.h> @@ -74,20 +73,16 @@  #include <linux/input.h>  #include <linux/leds.h>  #include <linux/rfkill.h> -#include <asm/uaccess.h> -  #include <linux/dmi.h>  #include <linux/jiffies.h>  #include <linux/workqueue.h> - +#include <linux/acpi.h> +#include <linux/pci_ids.h> +#include <linux/thinkpad_acpi.h>  #include <sound/core.h>  #include <sound/control.h>  #include <sound/initval.h> - -#include <acpi/acpi_drivers.h> - -#include <linux/pci_ids.h> - +#include <asm/uaccess.h>  /* ThinkPad CMOS commands */  #define TP_CMOS_VOLUME_DOWN	0 @@ -700,6 +695,14 @@ static void __init drv_acpi_handle_init(const char *name,  static acpi_status __init tpacpi_acpi_handle_locate_callback(acpi_handle handle,  			u32 level, void *context, void **return_value)  { +	struct acpi_device *dev; +	if (!strcmp(context, "video")) { +		if (acpi_bus_get_device(handle, &dev)) +			return AE_OK; +		if (strcmp(ACPI_VIDEO_HID, acpi_device_hid(dev))) +			return AE_OK; +	} +  	*(acpi_handle *)return_value = handle;  	return AE_CTRL_TERMINATE; @@ -712,10 +715,10 @@ static void __init tpacpi_acpi_handle_locate(const char *name,  	acpi_status status;  	acpi_handle device_found; -	BUG_ON(!name || !hid || !handle); +	BUG_ON(!name || !handle);  	vdbg_printk(TPACPI_DBG_INIT,  			"trying to locate ACPI handle for %s, using HID %s\n", -			name, hid); +			name, hid ? hid : "NULL");  	memset(&device_found, 0, sizeof(device_found));  	status = acpi_get_devices(hid, tpacpi_acpi_handle_locate_callback, @@ -2318,53 +2321,55 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)  	}  } -static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, -					   struct tp_nvram_state *newn, -					   const u32 event_mask) -{ -  #define TPACPI_COMPARE_KEY(__scancode, __member) \ -	do { \ -		if ((event_mask & (1 << __scancode)) && \ -		    oldn->__member != newn->__member) \ -			tpacpi_hotkey_send_key(__scancode); \ -	} while (0) +do { \ +	if ((event_mask & (1 << __scancode)) && \ +	    oldn->__member != newn->__member) \ +		tpacpi_hotkey_send_key(__scancode); \ +} while (0)  #define TPACPI_MAY_SEND_KEY(__scancode) \ -	do { \ -		if (event_mask & (1 << __scancode)) \ -			tpacpi_hotkey_send_key(__scancode); \ -	} while (0) +do { \ +	if (event_mask & (1 << __scancode)) \ +		tpacpi_hotkey_send_key(__scancode); \ +} while (0) -	void issue_volchange(const unsigned int oldvol, -			     const unsigned int newvol) -	{ -		unsigned int i = oldvol; +static void issue_volchange(const unsigned int oldvol, +			    const unsigned int newvol, +			    const u32 event_mask) +{ +	unsigned int i = oldvol; -		while (i > newvol) { -			TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); -			i--; -		} -		while (i < newvol) { -			TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); -			i++; -		} +	while (i > newvol) { +		TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); +		i--;  	} +	while (i < newvol) { +		TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); +		i++; +	} +} -	void issue_brightnesschange(const unsigned int oldbrt, -				    const unsigned int newbrt) -	{ -		unsigned int i = oldbrt; +static void issue_brightnesschange(const unsigned int oldbrt, +				   const unsigned int newbrt, +				   const u32 event_mask) +{ +	unsigned int i = oldbrt; -		while (i > newbrt) { -			TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); -			i--; -		} -		while (i < newbrt) { -			TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); -			i++; -		} +	while (i > newbrt) { +		TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); +		i--; +	} +	while (i < newbrt) { +		TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); +		i++;  	} +} + +static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, +					   struct tp_nvram_state *newn, +					   const u32 event_mask) +{  	TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);  	TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); @@ -2399,7 +2404,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,  		    oldn->volume_level != newn->volume_level) {  			/* recently muted, or repeated mute keypress, or  			 * multiple presses ending in mute */ -			issue_volchange(oldn->volume_level, newn->volume_level); +			issue_volchange(oldn->volume_level, newn->volume_level, +				event_mask);  			TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE);  		}  	} else { @@ -2409,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,  			TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP);  		}  		if (oldn->volume_level != newn->volume_level) { -			issue_volchange(oldn->volume_level, newn->volume_level); +			issue_volchange(oldn->volume_level, newn->volume_level, +				event_mask);  		} else if (oldn->volume_toggle != newn->volume_toggle) {  			/* repeated vol up/down keypress at end of scale ? */  			if (newn->volume_level == 0) @@ -2422,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,  	/* handle brightness */  	if (oldn->brightness_level != newn->brightness_level) {  		issue_brightnesschange(oldn->brightness_level, -				       newn->brightness_level); +				       newn->brightness_level, event_mask);  	} else if (oldn->brightness_toggle != newn->brightness_toggle) {  		/* repeated key presses that didn't change state */  		if (newn->brightness_level == 0) @@ -3164,8 +3171,10 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)  		KEY_MICMUTE,	/* 0x1a: Mic mute (since ?400 or so) */  		/* (assignments unknown, please report if found) */ -		KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN, KEY_UNKNOWN,  		KEY_UNKNOWN, + +		/* Extra keys in use since the X240 / T440 / T540 */ +		KEY_CONFIG, KEY_SEARCH, KEY_SCALE, KEY_COMPUTER,  		},  	}; @@ -3434,6 +3443,106 @@ err_exit:  	return (res < 0)? res : 1;  } +/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser + * mode, Web conference mode, Function mode and Lay-flat mode. + * We support Home mode and Function mode currently. + * + * Will consider support rest of modes in future. + * + */ +enum ADAPTIVE_KEY_MODE { +	HOME_MODE, +	WEB_BROWSER_MODE, +	WEB_CONFERENCE_MODE, +	FUNCTION_MODE, +	LAYFLAT_MODE +}; + +const int adaptive_keyboard_modes[] = { +	HOME_MODE, +/*	WEB_BROWSER_MODE = 2, +	WEB_CONFERENCE_MODE = 3, */ +	FUNCTION_MODE +}; + +#define DFR_CHANGE_ROW			0x101 +#define DFR_SHOW_QUICKVIEW_ROW		0x102 + +/* press Fn key a while second, it will switch to Function Mode. Then + * release Fn key, previous mode be restored. + */ +static bool adaptive_keyboard_mode_is_saved; +static int adaptive_keyboard_prev_mode; + +static int adaptive_keyboard_get_next_mode(int mode) +{ +	size_t i; +	size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1; + +	for (i = 0; i <= max_mode; i++) { +		if (adaptive_keyboard_modes[i] == mode) +			break; +	} + +	if (i >= max_mode) +		i = 0; +	else +		i++; + +	return adaptive_keyboard_modes[i]; +} + +static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) +{ +	u32 current_mode = 0; +	int new_mode = 0; + +	switch (scancode) { +	case DFR_CHANGE_ROW: +		if (adaptive_keyboard_mode_is_saved) { +			new_mode = adaptive_keyboard_prev_mode; +			adaptive_keyboard_mode_is_saved = false; +		} else { +			if (!acpi_evalf( +					hkey_handle, ¤t_mode, +					"GTRW", "dd", 0)) { +				pr_err("Cannot read adaptive keyboard mode\n"); +				return false; +			} else { +				new_mode = adaptive_keyboard_get_next_mode( +						current_mode); +			} +		} + +		if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) { +			pr_err("Cannot set adaptive keyboard mode\n"); +			return false; +		} + +		return true; + +	case DFR_SHOW_QUICKVIEW_ROW: +		if (!acpi_evalf(hkey_handle, +				&adaptive_keyboard_prev_mode, +				"GTRW", "dd", 0)) { +			pr_err("Cannot read adaptive keyboard mode\n"); +			return false; +		} else { +			adaptive_keyboard_mode_is_saved = true; + +			if (!acpi_evalf(hkey_handle, +					NULL, "STRW", "vd", FUNCTION_MODE)) { +				pr_err("Cannot set adaptive keyboard mode\n"); +				return false; +			} +		} +		return true; + +	default: +		return false; +	} +} +  static bool hotkey_notify_hotkey(const u32 hkey,  				 bool *send_acpi_ev,  				 bool *ignore_acpi_ev) @@ -3453,6 +3562,8 @@ static bool hotkey_notify_hotkey(const u32 hkey,  			*ignore_acpi_ev = true;  		}  		return true; +	} else { +		return adaptive_keyboard_hotkey_notify_hotkey(scancode);  	}  	return false;  } @@ -3725,13 +3836,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event)  static void hotkey_suspend(void)  { +	int hkeyv; +  	/* Do these on suspend, we get the events on early resume! */  	hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE;  	hotkey_autosleep_ack = 0; + +	/* save previous mode of adaptive keyboard of X1 Carbon */ +	if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { +		if ((hkeyv >> 8) == 2) { +			if (!acpi_evalf(hkey_handle, +						&adaptive_keyboard_prev_mode, +						"GTRW", "dd", 0)) { +				pr_err("Cannot read adaptive keyboard mode.\n"); +			} +		} +	}  }  static void hotkey_resume(void)  { +	int hkeyv; +  	tpacpi_disable_brightness_delay();  	if (hotkey_status_set(true) < 0 || @@ -3744,6 +3870,18 @@ static void hotkey_resume(void)  	hotkey_wakeup_reason_notify_change();  	hotkey_wakeup_hotunplug_complete_notify_change();  	hotkey_poll_setup_safe(false); + +	/* restore previous mode of adapive keyboard of X1 Carbon */ +	if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { +		if ((hkeyv >> 8) == 2) { +			if (!acpi_evalf(hkey_handle, +						NULL, +						"STRW", "vd", +						adaptive_keyboard_prev_mode)) { +				pr_err("Cannot set adaptive keyboard mode.\n"); +			} +		} +	}  }  /* procfs -------------------------------------------------------------- */ @@ -6090,19 +6228,28 @@ static int __init tpacpi_query_bcl_levels(acpi_handle handle)  {  	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };  	union acpi_object *obj; +	struct acpi_device *device, *child;  	int rc; -	if (ACPI_SUCCESS(acpi_evaluate_object(handle, "_BCL", NULL, &buffer))) { +	if (acpi_bus_get_device(handle, &device)) +		return 0; + +	rc = 0; +	list_for_each_entry(child, &device->children, node) { +		acpi_status status = acpi_evaluate_object(child->handle, "_BCL", +							  NULL, &buffer); +		if (ACPI_FAILURE(status)) +			continue; +  		obj = (union acpi_object *)buffer.pointer;  		if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) {  			pr_err("Unknown _BCL data, please report this to %s\n", -			       TPACPI_MAIL); +				TPACPI_MAIL);  			rc = 0;  		} else {  			rc = obj->package.count;  		} -	} else { -		return 0; +		break;  	}  	kfree(buffer.pointer); @@ -6118,7 +6265,7 @@ static unsigned int __init tpacpi_check_std_acpi_brightness_support(void)  	acpi_handle video_device;  	int bcl_levels = 0; -	tpacpi_acpi_handle_locate("video", ACPI_VIDEO_HID, &video_device); +	tpacpi_acpi_handle_locate("video", NULL, &video_device);  	if (video_device)  		bcl_levels = tpacpi_query_bcl_levels(video_device); @@ -6420,7 +6567,12 @@ static struct ibm_struct brightness_driver_data = {  #define TPACPI_ALSA_SHRTNAME "ThinkPad Console Audio Control"  #define TPACPI_ALSA_MIXERNAME TPACPI_ALSA_SHRTNAME -static int alsa_index = ~((1 << (SNDRV_CARDS - 3)) - 1); /* last three slots */ +#if SNDRV_CARDS <= 32 +#define DEFAULT_ALSA_IDX		~((1 << (SNDRV_CARDS - 3)) - 1) +#else +#define DEFAULT_ALSA_IDX		~((1 << (32 - 3)) - 1) +#endif +static int alsa_index = DEFAULT_ALSA_IDX; /* last three slots */  static char *alsa_id = "ThinkPadEC";  static bool alsa_enable = SNDRV_DEFAULT_ENABLE1; @@ -6759,8 +6911,9 @@ static int __init volume_create_alsa_mixer(void)  	struct snd_kcontrol *ctl_mute;  	int rc; -	rc = snd_card_create(alsa_index, alsa_id, THIS_MODULE, -			    sizeof(struct tpacpi_alsa_data), &card); +	rc = snd_card_new(&tpacpi_pdev->dev, +			  alsa_index, alsa_id, THIS_MODULE, +			  sizeof(struct tpacpi_alsa_data), &card);  	if (rc < 0 || !card) {  		pr_err("Failed to create ALSA card structures: %d\n", rc);  		return 1; @@ -6811,7 +6964,6 @@ static int __init volume_create_alsa_mixer(void)  	}  	data->ctl_mute_id = &ctl_mute->id; -	snd_card_set_dev(card, &tpacpi_pdev->dev);  	rc = snd_card_register(card);  	if (rc < 0) {  		pr_err("Failed to register ALSA card: %d\n", rc); @@ -8350,6 +8502,103 @@ static struct ibm_struct fan_driver_data = {  	.resume = fan_resume,  }; +/************************************************************************* + * Mute LED subdriver + */ + + +struct tp_led_table { +	acpi_string name; +	int on_value; +	int off_value; +	int state; +}; + +static struct tp_led_table led_tables[] = { +	[TPACPI_LED_MUTE] = { +		.name = "SSMS", +		.on_value = 1, +		.off_value = 0, +	}, +	[TPACPI_LED_MICMUTE] = { +		.name = "MMTS", +		.on_value = 2, +		.off_value = 0, +	}, +}; + +static int mute_led_on_off(struct tp_led_table *t, bool state) +{ +	acpi_handle temp; +	int output; + +	if (!ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) { +		pr_warn("Thinkpad ACPI has no %s interface.\n", t->name); +		return -EIO; +	} + +	if (!acpi_evalf(hkey_handle, &output, t->name, "dd", +			state ? t->on_value : t->off_value)) +		return -EIO; + +	t->state = state; +	return state; +} + +int tpacpi_led_set(int whichled, bool on) +{ +	struct tp_led_table *t; + +	if (whichled < 0 || whichled >= TPACPI_LED_MAX) +		return -EINVAL; + +	t = &led_tables[whichled]; +	if (t->state < 0 || t->state == on) +		return t->state; +	return mute_led_on_off(t, on); +} +EXPORT_SYMBOL_GPL(tpacpi_led_set); + +static int mute_led_init(struct ibm_init_struct *iibm) +{ +	acpi_handle temp; +	int i; + +	for (i = 0; i < TPACPI_LED_MAX; i++) { +		struct tp_led_table *t = &led_tables[i]; +		if (ACPI_SUCCESS(acpi_get_handle(hkey_handle, t->name, &temp))) +			mute_led_on_off(t, false); +		else +			t->state = -ENODEV; +	} +	return 0; +} + +static void mute_led_exit(void) +{ +	int i; + +	for (i = 0; i < TPACPI_LED_MAX; i++) +		tpacpi_led_set(i, false); +} + +static void mute_led_resume(void) +{ +	int i; + +	for (i = 0; i < TPACPI_LED_MAX; i++) { +		struct tp_led_table *t = &led_tables[i]; +		if (t->state >= 0) +			mute_led_on_off(t, t->state); +	} +} + +static struct ibm_struct mute_led_driver_data = { +	.name = "mute_led", +	.exit = mute_led_exit, +	.resume = mute_led_resume, +}; +  /****************************************************************************   ****************************************************************************   * @@ -8768,6 +9017,10 @@ static struct ibm_init_struct ibms_init[] __initdata = {  		.init = fan_init,  		.data = &fan_driver_data,  	}, +	{ +		.init = mute_led_init, +		.data = &mute_led_driver_data, +	},  };  static int __init set_ibm_param(const char *val, struct kernel_param *kp) @@ -9056,7 +9309,6 @@ static int __init thinkpad_acpi_module_init(void)  	mutex_init(&tpacpi_inputdev_send_mutex);  	tpacpi_inputdev = input_allocate_device();  	if (!tpacpi_inputdev) { -		pr_err("unable to allocate input device\n");  		thinkpad_acpi_module_exit();  		return -ENOMEM;  	} else { diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 4ab618c63b4..e597de05e6c 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -80,13 +80,9 @@ static void acpi_topstar_notify(struct acpi_device *device, u32 event)  static int acpi_topstar_fncx_switch(struct acpi_device *device, bool state)  {  	acpi_status status; -	union acpi_object fncx_params[1] = { -		{ .type = ACPI_TYPE_INTEGER } -	}; -	struct acpi_object_list fncx_arg_list = { 1, &fncx_params[0] }; -	fncx_params[0].integer.value = state ? 0x86 : 0x87; -	status = acpi_evaluate_object(device->handle, "FNCX", &fncx_arg_list, NULL); +	status = acpi_execute_simple_method(device->handle, "FNCX", +						state ? 0x86 : 0x87);  	if (ACPI_FAILURE(status)) {  		pr_err("Unable to switch FNCX notifications\n");  		return -ENODEV; @@ -101,10 +97,8 @@ static int acpi_topstar_init_hkey(struct topstar_hkey *hkey)  	int error;  	input = input_allocate_device(); -	if (!input) { -		pr_err("Unable to allocate input device\n"); +	if (!input)  		return -ENOMEM; -	}  	input->name = "Topstar Laptop extra buttons";  	input->phys = "topstar/input0"; diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index eb3467ea6d8..76441dcbe5f 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -5,6 +5,7 @@   *  Copyright (C) 2002-2004 John Belmonte   *  Copyright (C) 2008 Philip Langdale   *  Copyright (C) 2010 Pierre Ducroquet + *  Copyright (C) 2014 Azael Avalos   *   *  This program is free software; you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -37,7 +38,7 @@  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define TOSHIBA_ACPI_VERSION	"0.19" +#define TOSHIBA_ACPI_VERSION	"0.20"  #define PROC_INTERFACE_VERSION	1  #include <linux/kernel.h> @@ -54,11 +55,10 @@  #include <linux/slab.h>  #include <linux/workqueue.h>  #include <linux/i8042.h> - +#include <linux/acpi.h> +#include <linux/dmi.h>  #include <asm/uaccess.h> -#include <acpi/acpi_drivers.h> -  MODULE_AUTHOR("John Belmonte");  MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver");  MODULE_LICENSE("GPL"); @@ -79,6 +79,9 @@ MODULE_LICENSE("GPL");   * However the ACPI methods seem to be incomplete in some areas (for   * example they allow setting, but not reading, the LCD brightness value),   * so this is still useful. + * + * SCI stands for "System Configuration Interface" which aim is to + * conceal differences in hardware between different models.   */  #define HCI_WORDS			6 @@ -86,12 +89,23 @@ MODULE_LICENSE("GPL");  /* operations */  #define HCI_SET				0xff00  #define HCI_GET				0xfe00 +#define SCI_OPEN			0xf100 +#define SCI_CLOSE			0xf200 +#define SCI_GET				0xf300 +#define SCI_SET				0xf400  /* return codes */  #define HCI_SUCCESS			0x0000  #define HCI_FAILURE			0x1000  #define HCI_NOT_SUPPORTED		0x8000  #define HCI_EMPTY			0x8c00 +#define HCI_DATA_NOT_AVAILABLE		0x8d20 +#define HCI_NOT_INITIALIZED		0x8d50 +#define SCI_OPEN_CLOSE_OK		0x0044 +#define SCI_ALREADY_OPEN		0x8100 +#define SCI_NOT_OPENED			0x8200 +#define SCI_INPUT_DATA_ERROR		0x8300 +#define SCI_NOT_PRESENT			0x8600  /* registers */  #define HCI_FAN				0x0004 @@ -101,13 +115,22 @@ MODULE_LICENSE("GPL");  #define HCI_HOTKEY_EVENT		0x001e  #define HCI_LCD_BRIGHTNESS		0x002a  #define HCI_WIRELESS			0x0056 +#define HCI_ACCELEROMETER		0x006d +#define HCI_KBD_ILLUMINATION		0x0095 +#define HCI_ECO_MODE			0x0097 +#define HCI_ACCELEROMETER2		0x00a6 +#define SCI_ILLUMINATION		0x014e +#define SCI_KBD_ILLUM_STATUS		0x015c +#define SCI_TOUCHPAD			0x050e  /* field definitions */ +#define HCI_ACCEL_MASK			0x7fff  #define HCI_HOTKEY_DISABLE		0x0b  #define HCI_HOTKEY_ENABLE		0x09  #define HCI_LCD_BRIGHTNESS_BITS		3  #define HCI_LCD_BRIGHTNESS_SHIFT	(16-HCI_LCD_BRIGHTNESS_BITS)  #define HCI_LCD_BRIGHTNESS_LEVELS	(1 << HCI_LCD_BRIGHTNESS_BITS) +#define HCI_MISC_SHIFT			0x10  #define HCI_VIDEO_OUT_LCD		0x1  #define HCI_VIDEO_OUT_CRT		0x2  #define HCI_VIDEO_OUT_TV		0x4 @@ -115,6 +138,8 @@ MODULE_LICENSE("GPL");  #define HCI_WIRELESS_BT_PRESENT		0x0f  #define HCI_WIRELESS_BT_ATTACH		0x40  #define HCI_WIRELESS_BT_POWER		0x80 +#define SCI_KBD_MODE_FNZ		0x1 +#define SCI_KBD_MODE_AUTO		0x2  struct toshiba_acpi_dev {  	struct acpi_device *acpi_dev; @@ -124,10 +149,14 @@ struct toshiba_acpi_dev {  	struct work_struct hotkey_work;  	struct backlight_device *backlight_dev;  	struct led_classdev led_dev; +	struct led_classdev kbd_led; +	struct led_classdev eco_led;  	int force_fan;  	int last_key_event;  	int key_event_valid; +	int kbd_mode; +	int kbd_time;  	unsigned int illumination_supported:1;  	unsigned int video_supported:1; @@ -136,6 +165,12 @@ struct toshiba_acpi_dev {  	unsigned int ntfy_supported:1;  	unsigned int info_supported:1;  	unsigned int tr_backlight_supported:1; +	unsigned int kbd_illum_supported:1; +	unsigned int kbd_led_registered:1; +	unsigned int touchpad_supported:1; +	unsigned int eco_supported:1; +	unsigned int accelerometer_supported:1; +	unsigned int sysfs_created:1;  	struct mutex mutex;  }; @@ -151,6 +186,7 @@ static const struct acpi_device_id toshiba_device_ids[] = {  MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);  static const struct key_entry toshiba_acpi_keymap[] = { +	{ KE_KEY, 0x9e, { KEY_RFKILL } },  	{ KE_KEY, 0x101, { KEY_MUTE } },  	{ KE_KEY, 0x102, { KEY_ZOOMOUT } },  	{ KE_KEY, 0x103, { KEY_ZOOMIN } }, @@ -178,6 +214,30 @@ static const struct key_entry toshiba_acpi_keymap[] = {  	{ KE_END, 0 },  }; +/* alternative keymap */ +static const struct dmi_system_id toshiba_alt_keymap_dmi[] = { +	{ +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Satellite M840"), +		}, +	}, +	{} +}; + +static const struct key_entry toshiba_acpi_alt_keymap[] = { +	{ KE_KEY, 0x157, { KEY_MUTE } }, +	{ KE_KEY, 0x102, { KEY_ZOOMOUT } }, +	{ KE_KEY, 0x103, { KEY_ZOOMIN } }, +	{ KE_KEY, 0x139, { KEY_ZOOMRESET } }, +	{ KE_KEY, 0x13e, { KEY_SWITCHVIDEOMODE } }, +	{ KE_KEY, 0x13c, { KEY_BRIGHTNESSDOWN } }, +	{ KE_KEY, 0x13d, { KEY_BRIGHTNESSUP } }, +	{ KE_KEY, 0x158, { KEY_WLAN } }, +	{ KE_KEY, 0x13f, { KEY_TOUCHPAD_TOGGLE } }, +	{ KE_END, 0 }, +}; +  /* utility   */ @@ -191,16 +251,9 @@ static __inline__ void _set_bit(u32 * word, u32 mask, int value)  static int write_acpi_int(const char *methodName, int val)  { -	struct acpi_object_list params; -	union acpi_object in_objs[1];  	acpi_status status; -	params.count = ARRAY_SIZE(in_objs); -	params.pointer = in_objs; -	in_objs[0].type = ACPI_TYPE_INTEGER; -	in_objs[0].integer.value = val; - -	status = acpi_evaluate_object(NULL, (char *)methodName, ¶ms, NULL); +	status = acpi_execute_simple_method(NULL, (char *)methodName, val);  	return (status == AE_OK) ? 0 : -EIO;  } @@ -288,21 +341,94 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,  	return status;  } +/* common sci tasks + */ + +static int sci_open(struct toshiba_acpi_dev *dev) +{ +	u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; +	u32 out[HCI_WORDS]; +	acpi_status status; + +	status = hci_raw(dev, in, out); +	if  (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { +		pr_err("ACPI call to open SCI failed\n"); +		return 0; +	} + +	if (out[0] == SCI_OPEN_CLOSE_OK) { +		return 1; +	} else if (out[0] == SCI_ALREADY_OPEN) { +		pr_info("Toshiba SCI already opened\n"); +		return 1; +	} else if (out[0] == SCI_NOT_PRESENT) { +		pr_info("Toshiba SCI is not present\n"); +	} + +	return 0; +} + +static void sci_close(struct toshiba_acpi_dev *dev) +{ +	u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; +	u32 out[HCI_WORDS]; +	acpi_status status; + +	status = hci_raw(dev, in, out); +	if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { +		pr_err("ACPI call to close SCI failed\n"); +		return; +	} + +	if (out[0] == SCI_OPEN_CLOSE_OK) +		return; +	else if (out[0] == SCI_NOT_OPENED) +		pr_info("Toshiba SCI not opened\n"); +	else if (out[0] == SCI_NOT_PRESENT) +		pr_info("Toshiba SCI is not present\n"); +} + +static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg, +			    u32 *out1, u32 *result) +{ +	u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 }; +	u32 out[HCI_WORDS]; +	acpi_status status = hci_raw(dev, in, out); +	*out1 = out[2]; +	*result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; +	return status; +} + +static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg, +			     u32 in1, u32 *result) +{ +	u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 }; +	u32 out[HCI_WORDS]; +	acpi_status status = hci_raw(dev, in, out); +	*result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; +	return status; +} +  /* Illumination support */  static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)  { -	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; +	u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 };  	u32 out[HCI_WORDS];  	acpi_status status; -	in[0] = 0xf100; +	if (!sci_open(dev)) +		return 0; +  	status = hci_raw(dev, in, out); -	if (ACPI_FAILURE(status)) { +	sci_close(dev); +	if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { +		pr_err("ACPI call to query Illumination support failed\n"); +		return 0; +	} else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) {  		pr_info("Illumination device not available\n");  		return 0;  	} -	in[0] = 0xf400; -	status = hci_raw(dev, in, out); +  	return 1;  } @@ -311,82 +437,270 @@ static void toshiba_illumination_set(struct led_classdev *cdev,  {  	struct toshiba_acpi_dev *dev = container_of(cdev,  			struct toshiba_acpi_dev, led_dev); -	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; -	u32 out[HCI_WORDS]; +	u32 state, result;  	acpi_status status;  	/* First request : initialize communication. */ -	in[0] = 0xf100; -	status = hci_raw(dev, in, out); +	if (!sci_open(dev)) +		return; + +	/* Switch the illumination on/off */ +	state = brightness ? 1 : 0; +	status = sci_write(dev, SCI_ILLUMINATION, state, &result); +	sci_close(dev);  	if (ACPI_FAILURE(status)) { -		pr_info("Illumination device not available\n"); +		pr_err("ACPI call for illumination failed\n"); +		return; +	} else if (result == HCI_NOT_SUPPORTED) { +		pr_info("Illumination not supported\n");  		return;  	} +} -	if (brightness) { -		/* Switch the illumination on */ -		in[0] = 0xf400; -		in[1] = 0x14e; -		in[2] = 1; -		status = hci_raw(dev, in, out); -		if (ACPI_FAILURE(status)) { -			pr_info("ACPI call for illumination failed\n"); -			return; -		} -	} else { -		/* Switch the illumination off */ -		in[0] = 0xf400; -		in[1] = 0x14e; -		in[2] = 0; -		status = hci_raw(dev, in, out); -		if (ACPI_FAILURE(status)) { -			pr_info("ACPI call for illumination failed.\n"); -			return; -		} +static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) +{ +	struct toshiba_acpi_dev *dev = container_of(cdev, +			struct toshiba_acpi_dev, led_dev); +	u32 state, result; +	acpi_status status; + +	/* First request : initialize communication. */ +	if (!sci_open(dev)) +		return LED_OFF; + +	/* Check the illumination */ +	status = sci_read(dev, SCI_ILLUMINATION, &state, &result); +	sci_close(dev); +	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { +		pr_err("ACPI call for illumination failed\n"); +		return LED_OFF; +	} else if (result == HCI_NOT_SUPPORTED) { +		pr_info("Illumination not supported\n"); +		return LED_OFF;  	} -	/* Last request : close communication. */ -	in[0] = 0xf200; -	in[1] = 0; -	in[2] = 0; -	hci_raw(dev, in, out); +	return state ? LED_FULL : LED_OFF;  } -static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) +/* KBD Illumination */ +static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) +{ +	u32 result; +	acpi_status status; + +	if (!sci_open(dev)) +		return -EIO; + +	status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result); +	sci_close(dev); +	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { +		pr_err("ACPI call to set KBD backlight status failed\n"); +		return -EIO; +	} else if (result == HCI_NOT_SUPPORTED) { +		pr_info("Keyboard backlight status not supported\n"); +		return -ENODEV; +	} + +	return 0; +} + +static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time) +{ +	u32 result; +	acpi_status status; + +	if (!sci_open(dev)) +		return -EIO; + +	status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result); +	sci_close(dev); +	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { +		pr_err("ACPI call to get KBD backlight status failed\n"); +		return -EIO; +	} else if (result == HCI_NOT_SUPPORTED) { +		pr_info("Keyboard backlight status not supported\n"); +		return -ENODEV; +	} + +	return 0; +} + +static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev)  {  	struct toshiba_acpi_dev *dev = container_of(cdev, -			struct toshiba_acpi_dev, led_dev); -	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; +			struct toshiba_acpi_dev, kbd_led); +	u32 state, result; +	acpi_status status; + +	/* Check the keyboard backlight state */ +	status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result); +	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { +		pr_err("ACPI call to get the keyboard backlight failed\n"); +		return LED_OFF; +	} else if (result == HCI_NOT_SUPPORTED) { +		pr_info("Keyboard backlight not supported\n"); +		return LED_OFF; +	} + +	return state ? LED_FULL : LED_OFF; +} + +static void toshiba_kbd_backlight_set(struct led_classdev *cdev, +				     enum led_brightness brightness) +{ +	struct toshiba_acpi_dev *dev = container_of(cdev, +			struct toshiba_acpi_dev, kbd_led); +	u32 state, result; +	acpi_status status; + +	/* Set the keyboard backlight state */ +	state = brightness ? 1 : 0; +	status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result); +	if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { +		pr_err("ACPI call to set KBD Illumination mode failed\n"); +		return; +	} else if (result == HCI_NOT_SUPPORTED) { +		pr_info("Keyboard backlight not supported\n"); +		return; +	} +} + +/* TouchPad support */ +static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state) +{ +	u32 result; +	acpi_status status; + +	if (!sci_open(dev)) +		return -EIO; + +	status = sci_write(dev, SCI_TOUCHPAD, state, &result); +	sci_close(dev); +	if (ACPI_FAILURE(status)) { +		pr_err("ACPI call to set the touchpad failed\n"); +		return -EIO; +	} else if (result == HCI_NOT_SUPPORTED) { +		return -ENODEV; +	} + +	return 0; +} + +static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) +{ +	u32 result; +	acpi_status status; + +	if (!sci_open(dev)) +		return -EIO; + +	status = sci_read(dev, SCI_TOUCHPAD, state, &result); +	sci_close(dev); +	if (ACPI_FAILURE(status)) { +		pr_err("ACPI call to query the touchpad failed\n"); +		return -EIO; +	} else if (result == HCI_NOT_SUPPORTED) { +		return -ENODEV; +	} + +	return 0; +} + +/* Eco Mode support */ +static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) +{ +	acpi_status status; +	u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; +	u32 out[HCI_WORDS]; + +	status = hci_raw(dev, in, out); +	if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { +		pr_info("ACPI call to get ECO led failed\n"); +		return 0; +	} + +	return 1; +} + +static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev) +{ +	struct toshiba_acpi_dev *dev = container_of(cdev, +			struct toshiba_acpi_dev, eco_led); +	u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 };  	u32 out[HCI_WORDS];  	acpi_status status; -	enum led_brightness result; -	/* First request : initialize communication. */ -	in[0] = 0xf100;  	status = hci_raw(dev, in, out); -	if (ACPI_FAILURE(status)) { -		pr_info("Illumination device not available\n"); +	if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { +		pr_err("ACPI call to get ECO led failed\n");  		return LED_OFF;  	} -	/* Check the illumination */ -	in[0] = 0xf300; -	in[1] = 0x14e; +	return out[2] ? LED_FULL : LED_OFF; +} + +static void toshiba_eco_mode_set_status(struct led_classdev *cdev, +				     enum led_brightness brightness) +{ +	struct toshiba_acpi_dev *dev = container_of(cdev, +			struct toshiba_acpi_dev, eco_led); +	u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 }; +	u32 out[HCI_WORDS]; +	acpi_status status; + +	/* Switch the Eco Mode led on/off */ +	in[2] = (brightness) ? 1 : 0;  	status = hci_raw(dev, in, out); -	if (ACPI_FAILURE(status)) { -		pr_info("ACPI call for illumination failed.\n"); -		return LED_OFF; +	if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { +		pr_err("ACPI call to set ECO led failed\n"); +		return;  	} +} -	result = out[2] ? LED_FULL : LED_OFF; +/* Accelerometer support */ +static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev) +{ +	u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 }; +	u32 out[HCI_WORDS]; +	acpi_status status; -	/* Last request : close communication. */ -	in[0] = 0xf200; -	in[1] = 0; -	in[2] = 0; -	hci_raw(dev, in, out); +	/* Check if the accelerometer call exists, +	 * this call also serves as initialization +	 */ +	status = hci_raw(dev, in, out); +	if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { +		pr_err("ACPI call to query the accelerometer failed\n"); +		return -EIO; +	} else if (out[0] == HCI_DATA_NOT_AVAILABLE || +		   out[0] == HCI_NOT_INITIALIZED) { +		pr_err("Accelerometer not initialized\n"); +		return -EIO; +	} else if (out[0] == HCI_NOT_SUPPORTED) { +		pr_info("Accelerometer not supported\n"); +		return -ENODEV; +	} -	return result; +	return 0; +} + +static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev, +				      u32 *xy, u32 *z) +{ +	u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 }; +	u32 out[HCI_WORDS]; +	acpi_status status; + +	/* Check the Accelerometer status */ +	status = hci_raw(dev, in, out); +	if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { +		pr_err("ACPI call to query the accelerometer failed\n"); +		return -EIO; +	} + +	*xy = out[2]; +	*z = out[4]; + +	return 0;  }  /* Bluetooth rfkill handlers */ @@ -912,6 +1226,177 @@ static const struct backlight_ops toshiba_backlight_data = {  	.update_status  = set_lcd_status,  }; +/* + * Sysfs files + */ + +static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, +					 struct device_attribute *attr, +					 const char *buf, size_t count) +{ +	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); +	int mode = -1; +	int time = -1; + +	if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1)) +		return -EINVAL; + +	/* Set the Keyboard Backlight Mode where: +	 * Mode - Auto (2) | FN-Z (1) +	 *	Auto - KBD backlight turns off automatically in given time +	 *	FN-Z - KBD backlight "toggles" when hotkey pressed +	 */ +	if (mode != -1 && toshiba->kbd_mode != mode) { +		time = toshiba->kbd_time << HCI_MISC_SHIFT; +		time = time + toshiba->kbd_mode; +		if (toshiba_kbd_illum_status_set(toshiba, time) < 0) +			return -EIO; +		toshiba->kbd_mode = mode; +	} + +	return count; +} + +static ssize_t toshiba_kbd_bl_mode_show(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); +	u32 time; + +	if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) +		return -EIO; + +	return sprintf(buf, "%i\n", time & 0x07); +} + +static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev, +					    struct device_attribute *attr, +					    const char *buf, size_t count) +{ +	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); +	int time = -1; + +	if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60)) +		return -EINVAL; + +	/* Set the Keyboard Backlight Timeout: 0-60 seconds */ +	if (time != -1 && toshiba->kbd_time != time) { +		time = time << HCI_MISC_SHIFT; +		time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ? +							time + 1 : time + 2; +		if (toshiba_kbd_illum_status_set(toshiba, time) < 0) +			return -EIO; +		toshiba->kbd_time = time >> HCI_MISC_SHIFT; +	} + +	return count; +} + +static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev, +					   struct device_attribute *attr, +					   char *buf) +{ +	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); +	u32 time; + +	if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) +		return -EIO; + +	return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT); +} + +static ssize_t toshiba_touchpad_store(struct device *dev, +				      struct device_attribute *attr, +				      const char *buf, size_t count) +{ +	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); +	int state; + +	/* Set the TouchPad on/off, 0 - Disable | 1 - Enable */ +	if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) { +		if (toshiba_touchpad_set(toshiba, state) < 0) +			return -EIO; +	} + +	return count; +} + +static ssize_t toshiba_touchpad_show(struct device *dev, +				     struct device_attribute *attr, char *buf) +{ +	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); +	u32 state; +	int ret; + +	ret = toshiba_touchpad_get(toshiba, &state); +	if (ret < 0) +		return ret; + +	return sprintf(buf, "%i\n", state); +} + +static ssize_t toshiba_position_show(struct device *dev, +				     struct device_attribute *attr, char *buf) +{ +	struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); +	u32 xyval, zval, tmp; +	u16 x, y, z; +	int ret; + +	xyval = zval = 0; +	ret = toshiba_accelerometer_get(toshiba, &xyval, &zval); +	if (ret < 0) +		return ret; + +	x = xyval & HCI_ACCEL_MASK; +	tmp = xyval >> HCI_MISC_SHIFT; +	y = tmp & HCI_ACCEL_MASK; +	z = zval & HCI_ACCEL_MASK; + +	return sprintf(buf, "%d %d %d\n", x, y, z); +} + +static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, +		   toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); +static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, +		   toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); +static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR, +		   toshiba_touchpad_show, toshiba_touchpad_store); +static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL); + +static struct attribute *toshiba_attributes[] = { +	&dev_attr_kbd_backlight_mode.attr, +	&dev_attr_kbd_backlight_timeout.attr, +	&dev_attr_touchpad.attr, +	&dev_attr_position.attr, +	NULL, +}; + +static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, +					struct attribute *attr, int idx) +{ +	struct device *dev = container_of(kobj, struct device, kobj); +	struct toshiba_acpi_dev *drv = dev_get_drvdata(dev); +	bool exists = true; + +	if (attr == &dev_attr_kbd_backlight_mode.attr) +		exists = (drv->kbd_illum_supported) ? true : false; +	else if (attr == &dev_attr_kbd_backlight_timeout.attr) +		exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; +	else if (attr == &dev_attr_touchpad.attr) +		exists = (drv->touchpad_supported) ? true : false; +	else if (attr == &dev_attr_position.attr) +		exists = (drv->accelerometer_supported) ? true : false; + +	return exists ? attr->mode : 0; +} + +static struct attribute_group toshiba_attr_group = { +	.is_visible = toshiba_sysfs_is_visible, +	.attrs = toshiba_attributes, +}; +  static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str,  				      struct serio *port)  { @@ -947,21 +1432,17 @@ static void toshiba_acpi_hotkey_work(struct work_struct *work)   */  static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev)  { -	struct acpi_buffer buf; -	union acpi_object out_obj; +	unsigned long long value;  	acpi_status status; -	buf.pointer = &out_obj; -	buf.length = sizeof(out_obj); - -	status = acpi_evaluate_object(dev->acpi_dev->handle, "INFO", -				      NULL, &buf); -	if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) { +	status = acpi_evaluate_integer(dev->acpi_dev->handle, "INFO", +				      NULL, &value); +	if (ACPI_FAILURE(status)) {  		pr_err("ACPI INFO method execution failed\n");  		return -EIO;  	} -	return out_obj.integer.value; +	return value;  }  static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev, @@ -981,21 +1462,22 @@ static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,  static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)  {  	acpi_status status; -	acpi_handle ec_handle, handle; +	acpi_handle ec_handle;  	int error;  	u32 hci_result; +	const struct key_entry *keymap = toshiba_acpi_keymap;  	dev->hotkey_dev = input_allocate_device(); -	if (!dev->hotkey_dev) { -		pr_info("Unable to register input device\n"); +	if (!dev->hotkey_dev)  		return -ENOMEM; -	}  	dev->hotkey_dev->name = "Toshiba input device";  	dev->hotkey_dev->phys = "toshiba_acpi/input0";  	dev->hotkey_dev->id.bustype = BUS_HOST; -	error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL); +	if (dmi_check_system(toshiba_alt_keymap_dmi)) +		keymap = toshiba_acpi_alt_keymap; +	error = sparse_keymap_setup(dev->hotkey_dev, keymap, NULL);  	if (error)  		goto err_free_dev; @@ -1008,10 +1490,7 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)  	 */  	status = AE_ERROR;  	ec_handle = ec_get_handle(); -	if (ec_handle) -		status = acpi_get_handle(ec_handle, "NTFY", &handle); - -	if (ACPI_SUCCESS(status)) { +	if (ec_handle && acpi_has_method(ec_handle, "NTFY")) {  		INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work);  		error = i8042_install_filter(toshiba_acpi_i8042_filter); @@ -1027,10 +1506,9 @@ static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)  	 * Determine hotkey query interface. Prefer using the INFO  	 * method when it is available.  	 */ -	status = acpi_get_handle(dev->acpi_dev->handle, "INFO", &handle); -	if (ACPI_SUCCESS(status)) { +	if (acpi_has_method(dev->acpi_dev->handle, "INFO"))  		dev->info_supported = 1; -	} else { +	else {  		hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result);  		if (hci_result == HCI_SUCCESS)  			dev->system_event_supported = 1; @@ -1124,6 +1602,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)  	remove_toshiba_proc_entries(dev); +	if (dev->sysfs_created) +		sysfs_remove_group(&dev->acpi_dev->dev.kobj, +				   &toshiba_attr_group); +  	if (dev->ntfy_supported) {  		i8042_remove_filter(toshiba_acpi_i8042_filter);  		cancel_work_sync(&dev->hotkey_work); @@ -1145,6 +1627,12 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)  	if (dev->illumination_supported)  		led_classdev_unregister(&dev->led_dev); +	if (dev->kbd_led_registered) +		led_classdev_unregister(&dev->kbd_led); + +	if (dev->eco_supported) +		led_classdev_unregister(&dev->eco_led); +  	if (toshiba_acpi)  		toshiba_acpi = NULL; @@ -1155,15 +1643,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev)  static const char *find_hci_method(acpi_handle handle)  { -	acpi_status status; -	acpi_handle hci_handle; - -	status = acpi_get_handle(handle, "GHCI", &hci_handle); -	if (ACPI_SUCCESS(status)) +	if (acpi_has_method(handle, "GHCI"))  		return "GHCI"; -	status = acpi_get_handle(handle, "SPFC", &hci_handle); -	if (ACPI_SUCCESS(status)) +	if (acpi_has_method(handle, "SPFC"))  		return "SPFC";  	return NULL; @@ -1195,6 +1678,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)  	dev->acpi_dev = acpi_dev;  	dev->method_hci = hci_method;  	acpi_dev->driver_data = dev; +	dev_set_drvdata(&acpi_dev->dev, dev);  	if (toshiba_acpi_setup_keyboard(dev))  		pr_info("Unable to activate hotkeys\n"); @@ -1235,6 +1719,40 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)  			dev->illumination_supported = 1;  	} +	if (toshiba_eco_mode_available(dev)) { +		dev->eco_led.name = "toshiba::eco_mode"; +		dev->eco_led.max_brightness = 1; +		dev->eco_led.brightness_set = toshiba_eco_mode_set_status; +		dev->eco_led.brightness_get = toshiba_eco_mode_get_status; +		if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led)) +			dev->eco_supported = 1; +	} + +	ret = toshiba_kbd_illum_status_get(dev, &dummy); +	if (!ret) { +		dev->kbd_time = dummy >> HCI_MISC_SHIFT; +		dev->kbd_mode = dummy & 0x07; +	} +	dev->kbd_illum_supported = !ret; +	/* +	 * Only register the LED if KBD illumination is supported +	 * and the keyboard backlight operation mode is set to FN-Z +	 */ +	if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) { +		dev->kbd_led.name = "toshiba::kbd_backlight"; +		dev->kbd_led.max_brightness = 1; +		dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; +		dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; +		if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led)) +			dev->kbd_led_registered = 1; +	} + +	ret = toshiba_touchpad_get(dev, &dummy); +	dev->touchpad_supported = !ret; + +	ret = toshiba_accelerometer_supported(dev); +	dev->accelerometer_supported = !ret; +  	/* Determine whether or not BIOS supports fan and video interfaces */  	ret = get_video_status(dev, &dummy); @@ -1243,6 +1761,14 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev)  	ret = get_fan_status(dev, &dummy);  	dev->fan_supported = !ret; +	ret = sysfs_create_group(&dev->acpi_dev->dev.kobj, +				 &toshiba_attr_group); +	if (ret) { +		dev->sysfs_created = 0; +		goto error; +	} +	dev->sysfs_created = !ret; +  	create_toshiba_proc_entries(dev);  	toshiba_acpi = dev; diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index 74dd01ae343..2cb1ea62b4a 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -23,14 +23,12 @@  #include <linux/module.h>  #include <linux/init.h>  #include <linux/types.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  MODULE_AUTHOR("Jes Sorensen <Jes.Sorensen@gmail.com>");  MODULE_DESCRIPTION("Toshiba Laptop ACPI Bluetooth Enable Driver");  MODULE_LICENSE("GPL"); -  static int toshiba_bt_rfkill_add(struct acpi_device *device);  static int toshiba_bt_rfkill_remove(struct acpi_device *device);  static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 601ea951224..43d13295e63 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -37,8 +37,6 @@  #include <linux/acpi.h>  #include <linux/slab.h>  #include <linux/module.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h>  ACPI_MODULE_NAME("wmi");  MODULE_AUTHOR("Carlos Corbacho"); @@ -252,8 +250,6 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)  {  	struct guid_block *block = NULL;  	char method[5]; -	struct acpi_object_list input; -	union acpi_object params[1];  	acpi_status status;  	acpi_handle handle; @@ -263,13 +259,9 @@ static acpi_status wmi_method_enable(struct wmi_block *wblock, int enable)  	if (!block)  		return AE_NOT_EXIST; -	input.count = 1; -	input.pointer = params; -	params[0].type = ACPI_TYPE_INTEGER; -	params[0].integer.value = enable;  	snprintf(method, 5, "WE%02X", block->notify_id); -	status = acpi_evaluate_object(handle, method, &input, NULL); +	status = acpi_execute_simple_method(handle, method, enable);  	if (status != AE_OK && status != AE_NOT_FOUND)  		return status; @@ -353,10 +345,10 @@ struct acpi_buffer *out)  {  	struct guid_block *block = NULL;  	struct wmi_block *wblock = NULL; -	acpi_handle handle, wc_handle; +	acpi_handle handle;  	acpi_status status, wc_status = AE_ERROR; -	struct acpi_object_list input, wc_input; -	union acpi_object wc_params[1], wq_params[1]; +	struct acpi_object_list input; +	union acpi_object wq_params[1];  	char method[5];  	char wc_method[5] = "WC"; @@ -386,11 +378,6 @@ struct acpi_buffer *out)  	 * enable collection.  	 */  	if (block->flags & ACPI_WMI_EXPENSIVE) { -		wc_input.count = 1; -		wc_input.pointer = wc_params; -		wc_params[0].type = ACPI_TYPE_INTEGER; -		wc_params[0].integer.value = 1; -  		strncat(wc_method, block->object_id, 2);  		/* @@ -398,10 +385,9 @@ struct acpi_buffer *out)  		 * expensive, but have no corresponding WCxx method. So we  		 * should not fail if this happens.  		 */ -		wc_status = acpi_get_handle(handle, wc_method, &wc_handle); -		if (ACPI_SUCCESS(wc_status)) -			wc_status = acpi_evaluate_object(handle, wc_method, -				&wc_input, NULL); +		if (acpi_has_method(handle, wc_method)) +			wc_status = acpi_execute_simple_method(handle, +								wc_method, 1);  	}  	strcpy(method, "WQ"); @@ -414,9 +400,7 @@ struct acpi_buffer *out)  	 * the WQxx method failed - we should disable collection anyway.  	 */  	if ((block->flags & ACPI_WMI_EXPENSIVE) && ACPI_SUCCESS(wc_status)) { -		wc_params[0].integer.value = 0; -		status = acpi_evaluate_object(handle, -		wc_method, &wc_input, NULL); +		status = acpi_execute_simple_method(handle, wc_method, 0);  	}  	return status; @@ -686,8 +670,10 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,  	struct wmi_block *wblock;  	wblock = dev_get_drvdata(dev); -	if (!wblock) -		return -ENOMEM; +	if (!wblock) { +		strcat(buf, "\n"); +		return strlen(buf); +	}  	wmi_gtoa(wblock->gblock.guid, guid_string); diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 4b1377bd594..49cbccec6e2 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -18,8 +18,7 @@  #include <linux/init.h>  #include <linux/types.h>  #include <linux/input.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  #define MODULE_NAME "xo15-ebook"  | 
