mirror of
				https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
				synced 2025-11-04 16:52:06 +10:00 
			
		
		
		
	Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86
Pull x86 platform driver updates from Matthew Garrett: "Support for the new keyboard features on the Thinkpad Carbon, a bunch of updates for the Sony and Toshiba drivers, a new driver for upcoming Alienware hardware and a few misc fixes. There's a couple of patches that got Acked today but aren't invasive, so I'll send a further PR for them next week" * 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86: (28 commits) alienware-wmi: cover some scenarios where memory allocations would fail Add WMI driver for controlling AlienFX features on some Alienware products fujitsu-tablet: add support for Lifebook T901 and T902 x86, platform: Make HP_WIRELESS option text more descriptive x86, acpi: LLVMLinux: Remove nested functions from Thinkpad ACPI save and restore adaptive keyboard mode for suspend and,resume support Thinkpad X1 Carbon 2nd generation's adaptive keyboard toshiba_acpi: Fix whitespace toshiba_acpi: Update version and copyright info toshiba_acpi: Add accelerometer support toshiba_acpi: Add ECO mode led support toshiba_acpi: Add touchpad enable/disable support- toshiba_acpi: Add keyboard backlight support toshiba_acpi: Adapt Illumination code to use SCI toshiba_acpi: Add System Configuration Interface thinkpad_acpi: Fix inconsistent mute LED after resume sonypi: Simplify dependencies Revert "X86 platform: New BayTrail IOSF-SB MBI driver" sony-laptop: remove useless sony-laptop versioning sony-laptop: add smart connect control function ...
This commit is contained in:
		
						commit
						4ba8526579
					
				@ -408,7 +408,7 @@ config APPLICOM
 | 
			
		||||
 | 
			
		||||
config SONYPI
 | 
			
		||||
	tristate "Sony Vaio Programmable I/O Control Device support"
 | 
			
		||||
	depends on X86 && PCI && INPUT && !64BIT
 | 
			
		||||
	depends on X86_32 && PCI && INPUT
 | 
			
		||||
	---help---
 | 
			
		||||
	  This driver enables access to the Sony Programmable I/O Control
 | 
			
		||||
	  Device which can be found in many (all ?) Sony Vaio laptops.
 | 
			
		||||
 | 
			
		||||
@ -53,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
 | 
			
		||||
@ -196,7 +208,7 @@ config HP_ACCEL
 | 
			
		||||
	  be called hp_accel.
 | 
			
		||||
 | 
			
		||||
config HP_WIRELESS
 | 
			
		||||
	tristate "HP WIRELESS"
 | 
			
		||||
	tristate "HP wireless button"
 | 
			
		||||
	depends on ACPI
 | 
			
		||||
	depends on INPUT
 | 
			
		||||
	help
 | 
			
		||||
@ -817,12 +829,4 @@ config PVPANIC
 | 
			
		||||
	  a paravirtualized device provided by QEMU; it lets a virtual machine
 | 
			
		||||
	  (guest) communicate panic events to the host.
 | 
			
		||||
 | 
			
		||||
config INTEL_BAYTRAIL_MBI
 | 
			
		||||
	tristate
 | 
			
		||||
	depends on PCI
 | 
			
		||||
	---help---
 | 
			
		||||
	  Needed on Baytrail platforms for access to the IOSF Sideband Mailbox
 | 
			
		||||
	  Interface. This is a requirement for systems that need to configure
 | 
			
		||||
	  the PUNIT for power management features such as RAPL.
 | 
			
		||||
 | 
			
		||||
endif # X86_PLATFORM_DEVICES
 | 
			
		||||
 | 
			
		||||
@ -55,4 +55,4 @@ obj-$(CONFIG_INTEL_RST)		+= intel-rst.o
 | 
			
		||||
obj-$(CONFIG_INTEL_SMARTCONNECT)	+= intel-smartconnect.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_PVPANIC)           += pvpanic.o
 | 
			
		||||
obj-$(CONFIG_INTEL_BAYTRAIL_MBI)	+= intel_baytrail.o
 | 
			
		||||
obj-$(CONFIG_ALIENWARE_WMI)	+= alienware-wmi.o
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										565
									
								
								drivers/platform/x86/alienware-wmi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										565
									
								
								drivers/platform/x86/alienware-wmi.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,565 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
		global_led.max_brightness = 100;
 | 
			
		||||
		lighting_control_state = WMAX_RUNNING;
 | 
			
		||||
	} else if (interface == LEGACY) {
 | 
			
		||||
		global_led.max_brightness = 0x0F;
 | 
			
		||||
		lighting_control_state = LEGACY_RUNNING;
 | 
			
		||||
	}
 | 
			
		||||
	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 ssize_t show_hdmi(struct device *dev, struct device_attribute *attr,
 | 
			
		||||
			 char *buf)
 | 
			
		||||
{
 | 
			
		||||
	acpi_status status;
 | 
			
		||||
	struct acpi_buffer input;
 | 
			
		||||
	union acpi_object *obj;
 | 
			
		||||
	u32 tmp = 0;
 | 
			
		||||
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
 | 
			
		||||
	struct hdmi_args in_args = {
 | 
			
		||||
		.arg = 0,
 | 
			
		||||
	};
 | 
			
		||||
	input.length = (acpi_size) sizeof(in_args);
 | 
			
		||||
	input.pointer = &in_args;
 | 
			
		||||
	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
 | 
			
		||||
				     WMAX_METHOD_HDMI_STATUS, &input, &output);
 | 
			
		||||
 | 
			
		||||
	if (ACPI_SUCCESS(status)) {
 | 
			
		||||
		obj = (union acpi_object *)output.pointer;
 | 
			
		||||
		if (obj && obj->type == ACPI_TYPE_INTEGER)
 | 
			
		||||
			tmp = (u32) obj->integer.value;
 | 
			
		||||
		if (tmp == 1)
 | 
			
		||||
			return scnprintf(buf, PAGE_SIZE,
 | 
			
		||||
					 "[input] gpu unknown\n");
 | 
			
		||||
		else if (tmp == 2)
 | 
			
		||||
			return scnprintf(buf, PAGE_SIZE,
 | 
			
		||||
					 "input [gpu] unknown\n");
 | 
			
		||||
	}
 | 
			
		||||
	pr_err("alienware-wmi: unknown HDMI status: %d\n", status);
 | 
			
		||||
	return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr,
 | 
			
		||||
			   const char *buf, size_t count)
 | 
			
		||||
{
 | 
			
		||||
	struct acpi_buffer input;
 | 
			
		||||
	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);
 | 
			
		||||
	input.length = (acpi_size) sizeof(args);
 | 
			
		||||
	input.pointer = &args;
 | 
			
		||||
	status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1,
 | 
			
		||||
				     WMAX_METHOD_HDMI_SOURCE, &input, NULL);
 | 
			
		||||
	if (ACPI_FAILURE(status))
 | 
			
		||||
		pr_err("alienware-wmi: HDMI toggle failed: results: %u\n",
 | 
			
		||||
		       status);
 | 
			
		||||
	return count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi);
 | 
			
		||||
 | 
			
		||||
static void remove_hdmi(struct platform_device *device)
 | 
			
		||||
{
 | 
			
		||||
	device_remove_file(&device->dev, &dev_attr_hdmi);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int create_hdmi(void)
 | 
			
		||||
{
 | 
			
		||||
	int ret = -ENOMEM;
 | 
			
		||||
	ret = device_create_file(&platform_device->dev, &dev_attr_hdmi);
 | 
			
		||||
	if (ret)
 | 
			
		||||
		goto error_create_hdmi;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
error_create_hdmi:
 | 
			
		||||
	remove_hdmi(platform_device);
 | 
			
		||||
	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();
 | 
			
		||||
		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);
 | 
			
		||||
@ -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,
 | 
			
		||||
@ -300,6 +338,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",
 | 
			
		||||
 | 
			
		||||
@ -1,224 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Baytrail IOSF-SB MailBox Interface Driver
 | 
			
		||||
 * Copyright (c) 2013, Intel Corporation.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify it
 | 
			
		||||
 * under the terms and conditions of the GNU General Public License,
 | 
			
		||||
 * version 2, as published by the Free Software Foundation.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope 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.
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a
 | 
			
		||||
 * mailbox interface (MBI) to communicate with mutiple devices. This
 | 
			
		||||
 * driver implements BayTrail-specific access to this interface.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/pci.h>
 | 
			
		||||
 | 
			
		||||
#include "intel_baytrail.h"
 | 
			
		||||
 | 
			
		||||
static DEFINE_SPINLOCK(iosf_mbi_lock);
 | 
			
		||||
 | 
			
		||||
static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset)
 | 
			
		||||
{
 | 
			
		||||
	return (op << 24) | (port << 16) | (offset << 8) | BT_MBI_ENABLE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct pci_dev *mbi_pdev;	/* one mbi device */
 | 
			
		||||
 | 
			
		||||
/* Hold lock before calling */
 | 
			
		||||
static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr)
 | 
			
		||||
{
 | 
			
		||||
	int result;
 | 
			
		||||
 | 
			
		||||
	if (!mbi_pdev)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (mcrx) {
 | 
			
		||||
		result = pci_write_config_dword(mbi_pdev,
 | 
			
		||||
						BT_MBI_MCRX_OFFSET, mcrx);
 | 
			
		||||
		if (result < 0)
 | 
			
		||||
			goto iosf_mbi_read_err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = pci_write_config_dword(mbi_pdev,
 | 
			
		||||
					BT_MBI_MCR_OFFSET, mcr);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		goto iosf_mbi_read_err;
 | 
			
		||||
 | 
			
		||||
	result = pci_read_config_dword(mbi_pdev,
 | 
			
		||||
				       BT_MBI_MDR_OFFSET, mdr);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		goto iosf_mbi_read_err;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
iosf_mbi_read_err:
 | 
			
		||||
	dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
 | 
			
		||||
		result);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Hold lock before calling */
 | 
			
		||||
static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr)
 | 
			
		||||
{
 | 
			
		||||
	int result;
 | 
			
		||||
 | 
			
		||||
	if (!mbi_pdev)
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	result = pci_write_config_dword(mbi_pdev,
 | 
			
		||||
					BT_MBI_MDR_OFFSET, mdr);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		goto iosf_mbi_write_err;
 | 
			
		||||
 | 
			
		||||
	if (mcrx) {
 | 
			
		||||
		result = pci_write_config_dword(mbi_pdev,
 | 
			
		||||
			 BT_MBI_MCRX_OFFSET, mcrx);
 | 
			
		||||
		if (result < 0)
 | 
			
		||||
			goto iosf_mbi_write_err;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = pci_write_config_dword(mbi_pdev,
 | 
			
		||||
					BT_MBI_MCR_OFFSET, mcr);
 | 
			
		||||
	if (result < 0)
 | 
			
		||||
		goto iosf_mbi_write_err;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
iosf_mbi_write_err:
 | 
			
		||||
	dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n",
 | 
			
		||||
		result);
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr)
 | 
			
		||||
{
 | 
			
		||||
	u32 mcr, mcrx;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/*Access to the GFX unit is handled by GPU code */
 | 
			
		||||
	BUG_ON(port == BT_MBI_UNIT_GFX);
 | 
			
		||||
 | 
			
		||||
	mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
 | 
			
		||||
	mcrx = offset & BT_MBI_MASK_HI;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&iosf_mbi_lock, flags);
 | 
			
		||||
	ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr);
 | 
			
		||||
	spin_unlock_irqrestore(&iosf_mbi_lock, flags);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(bt_mbi_read);
 | 
			
		||||
 | 
			
		||||
int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr)
 | 
			
		||||
{
 | 
			
		||||
	u32 mcr, mcrx;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/*Access to the GFX unit is handled by GPU code */
 | 
			
		||||
	BUG_ON(port == BT_MBI_UNIT_GFX);
 | 
			
		||||
 | 
			
		||||
	mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
 | 
			
		||||
	mcrx = offset & BT_MBI_MASK_HI;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&iosf_mbi_lock, flags);
 | 
			
		||||
	ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr);
 | 
			
		||||
	spin_unlock_irqrestore(&iosf_mbi_lock, flags);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(bt_mbi_write);
 | 
			
		||||
 | 
			
		||||
int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask)
 | 
			
		||||
{
 | 
			
		||||
	u32 mcr, mcrx;
 | 
			
		||||
	u32 value;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/*Access to the GFX unit is handled by GPU code */
 | 
			
		||||
	BUG_ON(port == BT_MBI_UNIT_GFX);
 | 
			
		||||
 | 
			
		||||
	mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO);
 | 
			
		||||
	mcrx = offset & BT_MBI_MASK_HI;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irqsave(&iosf_mbi_lock, flags);
 | 
			
		||||
 | 
			
		||||
	/* Read current mdr value */
 | 
			
		||||
	ret = iosf_mbi_pci_read_mdr(mcrx, mcr & BT_MBI_RD_MASK, &value);
 | 
			
		||||
	if (ret < 0) {
 | 
			
		||||
		spin_unlock_irqrestore(&iosf_mbi_lock, flags);
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Apply mask */
 | 
			
		||||
	value &= ~mask;
 | 
			
		||||
	mdr &= mask;
 | 
			
		||||
	value |= mdr;
 | 
			
		||||
 | 
			
		||||
	/* Write back */
 | 
			
		||||
	ret = iosf_mbi_pci_write_mdr(mcrx, mcr | BT_MBI_WR_MASK, value);
 | 
			
		||||
 | 
			
		||||
	spin_unlock_irqrestore(&iosf_mbi_lock, flags);
 | 
			
		||||
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(bt_mbi_modify);
 | 
			
		||||
 | 
			
		||||
static int iosf_mbi_probe(struct pci_dev *pdev,
 | 
			
		||||
			  const struct pci_device_id *unused)
 | 
			
		||||
{
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	ret = pci_enable_device(pdev);
 | 
			
		||||
	if (ret < 0) {
 | 
			
		||||
		dev_err(&pdev->dev, "error: could not enable device\n");
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mbi_pdev = pci_dev_get(pdev);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = {
 | 
			
		||||
	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F00) },
 | 
			
		||||
	{ 0, },
 | 
			
		||||
};
 | 
			
		||||
MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids);
 | 
			
		||||
 | 
			
		||||
static struct pci_driver iosf_mbi_pci_driver = {
 | 
			
		||||
	.name		= "iosf_mbi_pci",
 | 
			
		||||
	.probe		= iosf_mbi_probe,
 | 
			
		||||
	.id_table	= iosf_mbi_pci_ids,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init bt_mbi_init(void)
 | 
			
		||||
{
 | 
			
		||||
	return pci_register_driver(&iosf_mbi_pci_driver);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void __exit bt_mbi_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	pci_unregister_driver(&iosf_mbi_pci_driver);
 | 
			
		||||
	if (mbi_pdev) {
 | 
			
		||||
		pci_dev_put(mbi_pdev);
 | 
			
		||||
		mbi_pdev = NULL;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
module_init(bt_mbi_init);
 | 
			
		||||
module_exit(bt_mbi_exit);
 | 
			
		||||
 | 
			
		||||
MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
 | 
			
		||||
MODULE_DESCRIPTION("BayTrail Mailbox Interface accessor");
 | 
			
		||||
MODULE_LICENSE("GPL v2");
 | 
			
		||||
@ -1,90 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * intel_baytrail.h: MailBox access support for Intel BayTrail platforms
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef INTEL_BAYTRAIL_MBI_SYMS_H
 | 
			
		||||
#define INTEL_BAYTRAIL_MBI_SYMS_H
 | 
			
		||||
 | 
			
		||||
#define BT_MBI_MCR_OFFSET	0xD0
 | 
			
		||||
#define BT_MBI_MDR_OFFSET	0xD4
 | 
			
		||||
#define BT_MBI_MCRX_OFFSET	0xD8
 | 
			
		||||
 | 
			
		||||
#define BT_MBI_RD_MASK		0xFEFFFFFF
 | 
			
		||||
#define BT_MBI_WR_MASK		0X01000000
 | 
			
		||||
 | 
			
		||||
#define BT_MBI_MASK_HI		0xFFFFFF00
 | 
			
		||||
#define BT_MBI_MASK_LO		0x000000FF
 | 
			
		||||
#define BT_MBI_ENABLE		0xF0
 | 
			
		||||
 | 
			
		||||
/* BT-SB unit access methods */
 | 
			
		||||
#define BT_MBI_UNIT_AUNIT	0x00
 | 
			
		||||
#define BT_MBI_UNIT_SMC		0x01
 | 
			
		||||
#define BT_MBI_UNIT_CPU		0x02
 | 
			
		||||
#define BT_MBI_UNIT_BUNIT	0x03
 | 
			
		||||
#define BT_MBI_UNIT_PMC		0x04
 | 
			
		||||
#define BT_MBI_UNIT_GFX		0x06
 | 
			
		||||
#define BT_MBI_UNIT_SMI		0x0C
 | 
			
		||||
#define BT_MBI_UNIT_USB		0x43
 | 
			
		||||
#define BT_MBI_UNIT_SATA	0xA3
 | 
			
		||||
#define BT_MBI_UNIT_PCIE	0xA6
 | 
			
		||||
 | 
			
		||||
/* Read/write opcodes */
 | 
			
		||||
#define BT_MBI_AUNIT_READ	0x10
 | 
			
		||||
#define BT_MBI_AUNIT_WRITE	0x11
 | 
			
		||||
#define BT_MBI_SMC_READ		0x10
 | 
			
		||||
#define BT_MBI_SMC_WRITE	0x11
 | 
			
		||||
#define BT_MBI_CPU_READ		0x10
 | 
			
		||||
#define BT_MBI_CPU_WRITE	0x11
 | 
			
		||||
#define BT_MBI_BUNIT_READ	0x10
 | 
			
		||||
#define BT_MBI_BUNIT_WRITE	0x11
 | 
			
		||||
#define BT_MBI_PMC_READ		0x06
 | 
			
		||||
#define BT_MBI_PMC_WRITE	0x07
 | 
			
		||||
#define BT_MBI_GFX_READ		0x00
 | 
			
		||||
#define BT_MBI_GFX_WRITE	0x01
 | 
			
		||||
#define BT_MBI_SMIO_READ	0x06
 | 
			
		||||
#define BT_MBI_SMIO_WRITE	0x07
 | 
			
		||||
#define BT_MBI_USB_READ		0x06
 | 
			
		||||
#define BT_MBI_USB_WRITE	0x07
 | 
			
		||||
#define BT_MBI_SATA_READ	0x00
 | 
			
		||||
#define BT_MBI_SATA_WRITE	0x01
 | 
			
		||||
#define BT_MBI_PCIE_READ	0x00
 | 
			
		||||
#define BT_MBI_PCIE_WRITE	0x01
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * bt_mbi_read() - MailBox Interface read command
 | 
			
		||||
 * @port:	port indicating subunit being accessed
 | 
			
		||||
 * @opcode:	port specific read or write opcode
 | 
			
		||||
 * @offset:	register address offset
 | 
			
		||||
 * @mdr:	register data to be read
 | 
			
		||||
 *
 | 
			
		||||
 * Locking is handled by spinlock - cannot sleep.
 | 
			
		||||
 * Return: Nonzero on error
 | 
			
		||||
 */
 | 
			
		||||
int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * bt_mbi_write() - MailBox unmasked write command
 | 
			
		||||
 * @port:	port indicating subunit being accessed
 | 
			
		||||
 * @opcode:	port specific read or write opcode
 | 
			
		||||
 * @offset:	register address offset
 | 
			
		||||
 * @mdr:	register data to be written
 | 
			
		||||
 *
 | 
			
		||||
 * Locking is handled by spinlock - cannot sleep.
 | 
			
		||||
 * Return: Nonzero on error
 | 
			
		||||
 */
 | 
			
		||||
int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * bt_mbi_modify() - MailBox masked write command
 | 
			
		||||
 * @port:	port indicating subunit being accessed
 | 
			
		||||
 * @opcode:	port specific read or write opcode
 | 
			
		||||
 * @offset:	register address offset
 | 
			
		||||
 * @mdr:	register data being modified
 | 
			
		||||
 * @mask:	mask indicating bits in mdr to be modified
 | 
			
		||||
 *
 | 
			
		||||
 * Locking is handled by spinlock - cannot sleep.
 | 
			
		||||
 * Return: Nonzero on error
 | 
			
		||||
 */
 | 
			
		||||
int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask);
 | 
			
		||||
 | 
			
		||||
#endif /* INTEL_BAYTRAIL_MBI_SYMS_H */
 | 
			
		||||
@ -449,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;
 | 
			
		||||
@ -462,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,
 | 
			
		||||
 | 
			
		||||
@ -76,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"
 | 
			
		||||
@ -89,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);
 | 
			
		||||
@ -129,7 +126,8 @@ 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: no change from current value)");
 | 
			
		||||
		 "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 = -1;
 | 
			
		||||
module_param(kbd_backlight_timeout, int, 0444);
 | 
			
		||||
@ -152,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,
 | 
			
		||||
@ -163,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);
 | 
			
		||||
@ -1122,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 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1339,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);
 | 
			
		||||
@ -1381,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;
 | 
			
		||||
		}
 | 
			
		||||
@ -1420,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:
 | 
			
		||||
@ -1444,6 +1492,21 @@ static void sony_nc_function_cleanup(struct platform_device *pd)
 | 
			
		||||
		case 0x0163:
 | 
			
		||||
			sony_nc_kbd_backlight_cleanup(pd, handle);
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x0121:
 | 
			
		||||
			sony_nc_lowbatt_cleanup(pd);
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x0149:
 | 
			
		||||
			sony_nc_fanspeed_cleanup(pd);
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x0155:
 | 
			
		||||
			sony_nc_usb_charge_cleanup(pd);
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x011D:
 | 
			
		||||
			sony_nc_panelid_cleanup(pd);
 | 
			
		||||
			break;
 | 
			
		||||
		case 0x0168:
 | 
			
		||||
			sony_nc_smart_conn_cleanup(pd);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
@ -1719,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,
 | 
			
		||||
@ -1727,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;
 | 
			
		||||
 | 
			
		||||
@ -2221,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;
 | 
			
		||||
 | 
			
		||||
@ -2231,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;
 | 
			
		||||
 | 
			
		||||
@ -2245,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;
 | 
			
		||||
@ -2268,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);
 | 
			
		||||
@ -2298,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;
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	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[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++) {
 | 
			
		||||
		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;
 | 
			
		||||
@ -2340,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;
 | 
			
		||||
@ -2524,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;
 | 
			
		||||
@ -2726,8 +3151,6 @@ static int sony_nc_add(struct acpi_device *device)
 | 
			
		||||
	int result = 0;
 | 
			
		||||
	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");
 | 
			
		||||
 | 
			
		||||
@ -2821,6 +3244,7 @@ static int sony_nc_add(struct acpi_device *device)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pr_info("SNC setup done.\n");
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
out_sysfs:
 | 
			
		||||
@ -4259,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);
 | 
			
		||||
@ -4360,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:
 | 
			
		||||
 | 
			
		||||
@ -2321,54 +2321,56 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define TPACPI_COMPARE_KEY(__scancode, __member) \
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
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++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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++;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
 | 
			
		||||
#define TPACPI_MAY_SEND_KEY(__scancode) \
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
		while (i > newbrt) {
 | 
			
		||||
			TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND);
 | 
			
		||||
			i--;
 | 
			
		||||
		}
 | 
			
		||||
		while (i < newbrt) {
 | 
			
		||||
			TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME);
 | 
			
		||||
			i++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
 | 
			
		||||
	TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
 | 
			
		||||
	TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNF7, display_toggle);
 | 
			
		||||
@ -2402,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 {
 | 
			
		||||
@ -2412,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)
 | 
			
		||||
@ -2425,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)
 | 
			
		||||
@ -3437,6 +3441,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)
 | 
			
		||||
@ -3456,6 +3560,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;
 | 
			
		||||
}
 | 
			
		||||
@ -3728,13 +3834,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 ||
 | 
			
		||||
@ -3747,6 +3868,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 -------------------------------------------------------------- */
 | 
			
		||||
@ -8447,9 +8580,21 @@ static void mute_led_exit(void)
 | 
			
		||||
		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,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/****************************************************************************
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
@ -77,6 +78,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
 | 
			
		||||
@ -84,12 +88,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
 | 
			
		||||
@ -99,13 +114,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
 | 
			
		||||
@ -113,6 +137,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;
 | 
			
		||||
@ -122,10 +148,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;
 | 
			
		||||
@ -134,6 +164,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;
 | 
			
		||||
};
 | 
			
		||||
@ -280,21 +316,94 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg,
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Illumination support */
 | 
			
		||||
static int toshiba_illumination_available(struct toshiba_acpi_dev *dev)
 | 
			
		||||
/* common sci tasks
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static int sci_open(struct toshiba_acpi_dev *dev)
 | 
			
		||||
{
 | 
			
		||||
	u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
 | 
			
		||||
	u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 };
 | 
			
		||||
	u32 out[HCI_WORDS];
 | 
			
		||||
	acpi_status status;
 | 
			
		||||
 | 
			
		||||
	in[0] = 0xf100;
 | 
			
		||||
	status = hci_raw(dev, in, out);
 | 
			
		||||
	if (ACPI_FAILURE(status)) {
 | 
			
		||||
	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] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 };
 | 
			
		||||
	u32 out[HCI_WORDS];
 | 
			
		||||
	acpi_status status;
 | 
			
		||||
 | 
			
		||||
	if (!sci_open(dev))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	status = hci_raw(dev, in, out);
 | 
			
		||||
	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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -303,82 +412,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;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Last request : close communication. */
 | 
			
		||||
	in[0] = 0xf200;
 | 
			
		||||
	in[1] = 0;
 | 
			
		||||
	in[2] = 0;
 | 
			
		||||
	hci_raw(dev, in, out);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 };
 | 
			
		||||
	u32 out[HCI_WORDS];
 | 
			
		||||
	u32 state, result;
 | 
			
		||||
	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 (!sci_open(dev))
 | 
			
		||||
		return LED_OFF;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check the illumination */
 | 
			
		||||
	in[0] = 0xf300;
 | 
			
		||||
	in[1] = 0x14e;
 | 
			
		||||
	status = hci_raw(dev, in, out);
 | 
			
		||||
	if (ACPI_FAILURE(status)) {
 | 
			
		||||
		pr_info("ACPI call for illumination failed.\n");
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result = out[2] ? LED_FULL : LED_OFF;
 | 
			
		||||
	return state ? LED_FULL : LED_OFF;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	/* Last request : close communication. */
 | 
			
		||||
	in[0] = 0xf200;
 | 
			
		||||
	in[1] = 0;
 | 
			
		||||
	in[2] = 0;
 | 
			
		||||
	hci_raw(dev, in, out);
 | 
			
		||||
/* KBD Illumination */
 | 
			
		||||
static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time)
 | 
			
		||||
{
 | 
			
		||||
	u32 result;
 | 
			
		||||
	acpi_status status;
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
	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, 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;
 | 
			
		||||
 | 
			
		||||
	status = hci_raw(dev, in, out);
 | 
			
		||||
	if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) {
 | 
			
		||||
		pr_err("ACPI call to get ECO led failed\n");
 | 
			
		||||
		return LED_OFF;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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) || out[0] == SCI_INPUT_DATA_ERROR) {
 | 
			
		||||
		pr_err("ACPI call to set ECO led failed\n");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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;
 | 
			
		||||
 | 
			
		||||
	/* 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 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 */
 | 
			
		||||
@ -904,6 +1201,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)
 | 
			
		||||
{
 | 
			
		||||
@ -1106,6 +1574,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);
 | 
			
		||||
@ -1127,6 +1599,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;
 | 
			
		||||
 | 
			
		||||
@ -1172,6 +1650,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");
 | 
			
		||||
@ -1212,6 +1691,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);
 | 
			
		||||
@ -1220,6 +1733,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;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user