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
|
||||
|
|
|
@ -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 */
|
||||
if (value != 1)
|
||||
sony_call_snc_handle(kbdbl_ctl->handle,
|
||||
(value << 0x10) | (kbdbl_ctl->base + 0x100), &result);
|
||||
(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,13 +2316,12 @@ 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
|
||||
while (pos < LID_RESUME_MAX) {
|
||||
if (&lid_ctl->attrs[pos].attr == &attr->attr)
|
||||
break;
|
||||
pos++;
|
||||
}
|
||||
if (pos == LID_RESUME_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (value)
|
||||
|
@ -2259,7 +2329,8 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev,
|
|||
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;
|
||||
|
||||
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;
|
||||
unsigned int pos = LID_RESUME_S5;
|
||||
|
||||
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;
|
||||
|
||||
if (handle == 0x0119) {
|
||||
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;
|
||||
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++) {
|
||||
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,11 +2321,6 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m)
|
|||
}
|
||||
}
|
||||
|
||||
static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
|
||||
struct tp_nvram_state *newn,
|
||||
const u32 event_mask)
|
||||
{
|
||||
|
||||
#define TPACPI_COMPARE_KEY(__scancode, __member) \
|
||||
do { \
|
||||
if ((event_mask & (1 << __scancode)) && \
|
||||
|
@ -2339,8 +2334,9 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
|
|||
tpacpi_hotkey_send_key(__scancode); \
|
||||
} while (0)
|
||||
|
||||
void issue_volchange(const unsigned int oldvol,
|
||||
const unsigned int newvol)
|
||||
static void issue_volchange(const unsigned int oldvol,
|
||||
const unsigned int newvol,
|
||||
const u32 event_mask)
|
||||
{
|
||||
unsigned int i = oldvol;
|
||||
|
||||
|
@ -2354,8 +2350,9 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
|
|||
}
|
||||
}
|
||||
|
||||
void issue_brightnesschange(const unsigned int oldbrt,
|
||||
const unsigned int newbrt)
|
||||
static void issue_brightnesschange(const unsigned int oldbrt,
|
||||
const unsigned int newbrt,
|
||||
const u32 event_mask)
|
||||
{
|
||||
unsigned int i = oldbrt;
|
||||
|
||||
|
@ -2369,6 +2366,11 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
|
|||
}
|
||||
}
|
||||
|
||||
static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
|
||||
struct tp_nvram_state *newn,
|
||||
const u32 event_mask)
|
||||
{
|
||||
|
||||
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle);
|
||||
TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle);
|
||||
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 (ACPI_FAILURE(status)) {
|
||||
pr_info("Illumination device not available\n");
|
||||
if (!sci_open(dev))
|
||||
return;
|
||||
}
|
||||
|
||||
if (brightness) {
|
||||
/* Switch the illumination on */
|
||||
in[0] = 0xf400;
|
||||
in[1] = 0x14e;
|
||||
in[2] = 1;
|
||||
status = hci_raw(dev, in, out);
|
||||
/* 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("ACPI call for illumination failed\n");
|
||||
pr_err("ACPI call for illumination failed\n");
|
||||
return;
|
||||
} else if (result == HCI_NOT_SUPPORTED) {
|
||||
pr_info("Illumination not supported\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