diff options
| -rw-r--r-- | drivers/char/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/Kconfig | 22 | ||||
| -rw-r--r-- | drivers/platform/x86/Makefile | 2 | ||||
| -rw-r--r-- | drivers/platform/x86/alienware-wmi.c | 565 | ||||
| -rw-r--r-- | drivers/platform/x86/fujitsu-tablet.c | 65 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_baytrail.c | 224 | ||||
| -rw-r--r-- | drivers/platform/x86/intel_baytrail.h | 90 | ||||
| -rw-r--r-- | drivers/platform/x86/panasonic-laptop.c | 11 | ||||
| -rw-r--r-- | drivers/platform/x86/sony-laptop.c | 539 | ||||
| -rw-r--r-- | drivers/platform/x86/thinkpad_acpi.c | 227 | ||||
| -rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 635 |
11 files changed, 1901 insertions, 481 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 1386749b48ff..fbae63e3d304 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig | |||
| @@ -408,7 +408,7 @@ config APPLICOM | |||
| 408 | 408 | ||
| 409 | config SONYPI | 409 | config SONYPI |
| 410 | tristate "Sony Vaio Programmable I/O Control Device support" | 410 | tristate "Sony Vaio Programmable I/O Control Device support" |
| 411 | depends on X86 && PCI && INPUT && !64BIT | 411 | depends on X86_32 && PCI && INPUT |
| 412 | ---help--- | 412 | ---help--- |
| 413 | This driver enables access to the Sony Programmable I/O Control | 413 | This driver enables access to the Sony Programmable I/O Control |
| 414 | Device which can be found in many (all ?) Sony Vaio laptops. | 414 | Device which can be found in many (all ?) Sony Vaio laptops. |
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 5f67843c7fb7..27df2c533b09 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
| @@ -53,6 +53,18 @@ config ACERHDF | |||
| 53 | If you have an Acer Aspire One netbook, say Y or M | 53 | If you have an Acer Aspire One netbook, say Y or M |
| 54 | here. | 54 | here. |
| 55 | 55 | ||
| 56 | config ALIENWARE_WMI | ||
| 57 | tristate "Alienware Special feature control" | ||
| 58 | depends on ACPI | ||
| 59 | depends on LEDS_CLASS | ||
| 60 | depends on NEW_LEDS | ||
| 61 | depends on ACPI_WMI | ||
| 62 | ---help--- | ||
| 63 | This is a driver for controlling Alienware BIOS driven | ||
| 64 | features. It exposes an interface for controlling the AlienFX | ||
| 65 | zones on Alienware machines that don't contain a dedicated AlienFX | ||
| 66 | USB MCU such as the X51 and X51-R2. | ||
| 67 | |||
| 56 | config ASUS_LAPTOP | 68 | config ASUS_LAPTOP |
| 57 | tristate "Asus Laptop Extras" | 69 | tristate "Asus Laptop Extras" |
| 58 | depends on ACPI | 70 | depends on ACPI |
| @@ -196,7 +208,7 @@ config HP_ACCEL | |||
| 196 | be called hp_accel. | 208 | be called hp_accel. |
| 197 | 209 | ||
| 198 | config HP_WIRELESS | 210 | config HP_WIRELESS |
| 199 | tristate "HP WIRELESS" | 211 | tristate "HP wireless button" |
| 200 | depends on ACPI | 212 | depends on ACPI |
| 201 | depends on INPUT | 213 | depends on INPUT |
| 202 | help | 214 | help |
| @@ -817,12 +829,4 @@ config PVPANIC | |||
| 817 | a paravirtualized device provided by QEMU; it lets a virtual machine | 829 | a paravirtualized device provided by QEMU; it lets a virtual machine |
| 818 | (guest) communicate panic events to the host. | 830 | (guest) communicate panic events to the host. |
| 819 | 831 | ||
| 820 | config INTEL_BAYTRAIL_MBI | ||
| 821 | tristate | ||
| 822 | depends on PCI | ||
| 823 | ---help--- | ||
| 824 | Needed on Baytrail platforms for access to the IOSF Sideband Mailbox | ||
| 825 | Interface. This is a requirement for systems that need to configure | ||
| 826 | the PUNIT for power management features such as RAPL. | ||
| 827 | |||
| 828 | endif # X86_PLATFORM_DEVICES | 832 | endif # X86_PLATFORM_DEVICES |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9b87cfc42b84..1a2eafc9d48e 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
| @@ -55,4 +55,4 @@ obj-$(CONFIG_INTEL_RST) += intel-rst.o | |||
| 55 | obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o | 55 | obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o |
| 56 | 56 | ||
| 57 | obj-$(CONFIG_PVPANIC) += pvpanic.o | 57 | obj-$(CONFIG_PVPANIC) += pvpanic.o |
| 58 | obj-$(CONFIG_INTEL_BAYTRAIL_MBI) += intel_baytrail.o | 58 | obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o |
diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c new file mode 100644 index 000000000000..541f9514f76f --- /dev/null +++ b/drivers/platform/x86/alienware-wmi.c | |||
| @@ -0,0 +1,565 @@ | |||
| 1 | /* | ||
| 2 | * Alienware AlienFX control | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.com> | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
| 19 | |||
| 20 | #include <linux/acpi.h> | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <linux/dmi.h> | ||
| 24 | #include <linux/acpi.h> | ||
| 25 | #include <linux/leds.h> | ||
| 26 | |||
| 27 | #define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" | ||
| 28 | #define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" | ||
| 29 | #define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492" | ||
| 30 | |||
| 31 | #define WMAX_METHOD_HDMI_SOURCE 0x1 | ||
| 32 | #define WMAX_METHOD_HDMI_STATUS 0x2 | ||
| 33 | #define WMAX_METHOD_BRIGHTNESS 0x3 | ||
| 34 | #define WMAX_METHOD_ZONE_CONTROL 0x4 | ||
| 35 | |||
| 36 | MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>"); | ||
| 37 | MODULE_DESCRIPTION("Alienware special feature control"); | ||
| 38 | MODULE_LICENSE("GPL"); | ||
| 39 | MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID); | ||
| 40 | MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID); | ||
| 41 | |||
| 42 | enum INTERFACE_FLAGS { | ||
| 43 | LEGACY, | ||
| 44 | WMAX, | ||
| 45 | }; | ||
| 46 | |||
| 47 | enum LEGACY_CONTROL_STATES { | ||
| 48 | LEGACY_RUNNING = 1, | ||
| 49 | LEGACY_BOOTING = 0, | ||
| 50 | LEGACY_SUSPEND = 3, | ||
| 51 | }; | ||
| 52 | |||
| 53 | enum WMAX_CONTROL_STATES { | ||
| 54 | WMAX_RUNNING = 0xFF, | ||
| 55 | WMAX_BOOTING = 0, | ||
| 56 | WMAX_SUSPEND = 3, | ||
| 57 | }; | ||
| 58 | |||
| 59 | struct quirk_entry { | ||
| 60 | u8 num_zones; | ||
| 61 | }; | ||
| 62 | |||
| 63 | static struct quirk_entry *quirks; | ||
| 64 | |||
| 65 | static struct quirk_entry quirk_unknown = { | ||
| 66 | .num_zones = 2, | ||
| 67 | }; | ||
| 68 | |||
| 69 | static struct quirk_entry quirk_x51_family = { | ||
| 70 | .num_zones = 3, | ||
| 71 | }; | ||
| 72 | |||
| 73 | static int dmi_matched(const struct dmi_system_id *dmi) | ||
| 74 | { | ||
| 75 | quirks = dmi->driver_data; | ||
| 76 | return 1; | ||
| 77 | } | ||
| 78 | |||
| 79 | static struct dmi_system_id alienware_quirks[] = { | ||
| 80 | { | ||
| 81 | .callback = dmi_matched, | ||
| 82 | .ident = "Alienware X51 R1", | ||
| 83 | .matches = { | ||
| 84 | DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), | ||
| 85 | DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), | ||
| 86 | }, | ||
| 87 | .driver_data = &quirk_x51_family, | ||
| 88 | }, | ||
| 89 | { | ||
| 90 | .callback = dmi_matched, | ||
| 91 | .ident = "Alienware X51 R2", | ||
| 92 | .matches = { | ||
| 93 | DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), | ||
| 94 | DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), | ||
| 95 | }, | ||
| 96 | .driver_data = &quirk_x51_family, | ||
| 97 | }, | ||
| 98 | {} | ||
| 99 | }; | ||
| 100 | |||
| 101 | struct color_platform { | ||
| 102 | u8 blue; | ||
| 103 | u8 green; | ||
| 104 | u8 red; | ||
| 105 | } __packed; | ||
| 106 | |||
| 107 | struct platform_zone { | ||
| 108 | u8 location; | ||
| 109 | struct device_attribute *attr; | ||
| 110 | struct color_platform colors; | ||
| 111 | }; | ||
| 112 | |||
| 113 | struct wmax_brightness_args { | ||
| 114 | u32 led_mask; | ||
| 115 | u32 percentage; | ||
| 116 | }; | ||
| 117 | |||
| 118 | struct hdmi_args { | ||
| 119 | u8 arg; | ||
| 120 | }; | ||
| 121 | |||
| 122 | struct legacy_led_args { | ||
| 123 | struct color_platform colors; | ||
| 124 | u8 brightness; | ||
| 125 | u8 state; | ||
| 126 | } __packed; | ||
| 127 | |||
| 128 | struct wmax_led_args { | ||
| 129 | u32 led_mask; | ||
| 130 | struct color_platform colors; | ||
| 131 | u8 state; | ||
| 132 | } __packed; | ||
| 133 | |||
| 134 | static struct platform_device *platform_device; | ||
| 135 | static struct device_attribute *zone_dev_attrs; | ||
| 136 | static struct attribute **zone_attrs; | ||
| 137 | static struct platform_zone *zone_data; | ||
| 138 | |||
| 139 | static struct platform_driver platform_driver = { | ||
| 140 | .driver = { | ||
| 141 | .name = "alienware-wmi", | ||
| 142 | .owner = THIS_MODULE, | ||
| 143 | } | ||
| 144 | }; | ||
| 145 | |||
| 146 | static struct attribute_group zone_attribute_group = { | ||
| 147 | .name = "rgb_zones", | ||
| 148 | }; | ||
| 149 | |||
| 150 | static u8 interface; | ||
| 151 | static u8 lighting_control_state; | ||
| 152 | static u8 global_brightness; | ||
| 153 | |||
| 154 | /* | ||
| 155 | * Helpers used for zone control | ||
| 156 | */ | ||
| 157 | static int parse_rgb(const char *buf, struct platform_zone *zone) | ||
| 158 | { | ||
| 159 | long unsigned int rgb; | ||
| 160 | int ret; | ||
| 161 | union color_union { | ||
| 162 | struct color_platform cp; | ||
| 163 | int package; | ||
| 164 | } repackager; | ||
| 165 | |||
| 166 | ret = kstrtoul(buf, 16, &rgb); | ||
| 167 | if (ret) | ||
| 168 | return ret; | ||
| 169 | |||
| 170 | /* RGB triplet notation is 24-bit hexadecimal */ | ||
| 171 | if (rgb > 0xFFFFFF) | ||
| 172 | return -EINVAL; | ||
| 173 | |||
| 174 | repackager.package = rgb & 0x0f0f0f0f; | ||
| 175 | pr_debug("alienware-wmi: r: %d g:%d b: %d\n", | ||
| 176 | repackager.cp.red, repackager.cp.green, repackager.cp.blue); | ||
| 177 | zone->colors = repackager.cp; | ||
| 178 | return 0; | ||
| 179 | } | ||
| 180 | |||
| 181 | static struct platform_zone *match_zone(struct device_attribute *attr) | ||
| 182 | { | ||
| 183 | int i; | ||
| 184 | for (i = 0; i < quirks->num_zones; i++) { | ||
| 185 | if ((struct device_attribute *)zone_data[i].attr == attr) { | ||
| 186 | pr_debug("alienware-wmi: matched zone location: %d\n", | ||
| 187 | zone_data[i].location); | ||
| 188 | return &zone_data[i]; | ||
| 189 | } | ||
| 190 | } | ||
| 191 | return NULL; | ||
| 192 | } | ||
| 193 | |||
| 194 | /* | ||
| 195 | * Individual RGB zone control | ||
| 196 | */ | ||
| 197 | static int alienware_update_led(struct platform_zone *zone) | ||
| 198 | { | ||
| 199 | int method_id; | ||
| 200 | acpi_status status; | ||
| 201 | char *guid; | ||
| 202 | struct acpi_buffer input; | ||
| 203 | struct legacy_led_args legacy_args; | ||
| 204 | struct wmax_led_args wmax_args; | ||
| 205 | if (interface == WMAX) { | ||
| 206 | wmax_args.led_mask = 1 << zone->location; | ||
| 207 | wmax_args.colors = zone->colors; | ||
| 208 | wmax_args.state = lighting_control_state; | ||
| 209 | guid = WMAX_CONTROL_GUID; | ||
| 210 | method_id = WMAX_METHOD_ZONE_CONTROL; | ||
| 211 | |||
| 212 | input.length = (acpi_size) sizeof(wmax_args); | ||
| 213 | input.pointer = &wmax_args; | ||
| 214 | } else { | ||
| 215 | legacy_args.colors = zone->colors; | ||
| 216 | legacy_args.brightness = global_brightness; | ||
| 217 | legacy_args.state = 0; | ||
| 218 | if (lighting_control_state == LEGACY_BOOTING || | ||
| 219 | lighting_control_state == LEGACY_SUSPEND) { | ||
| 220 | guid = LEGACY_POWER_CONTROL_GUID; | ||
| 221 | legacy_args.state = lighting_control_state; | ||
| 222 | } else | ||
| 223 | guid = LEGACY_CONTROL_GUID; | ||
| 224 | method_id = zone->location + 1; | ||
| 225 | |||
| 226 | input.length = (acpi_size) sizeof(legacy_args); | ||
| 227 | input.pointer = &legacy_args; | ||
| 228 | } | ||
| 229 | pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); | ||
| 230 | |||
| 231 | status = wmi_evaluate_method(guid, 1, method_id, &input, NULL); | ||
| 232 | if (ACPI_FAILURE(status)) | ||
| 233 | pr_err("alienware-wmi: zone set failure: %u\n", status); | ||
| 234 | return ACPI_FAILURE(status); | ||
| 235 | } | ||
| 236 | |||
| 237 | static ssize_t zone_show(struct device *dev, struct device_attribute *attr, | ||
| 238 | char *buf) | ||
| 239 | { | ||
| 240 | struct platform_zone *target_zone; | ||
| 241 | target_zone = match_zone(attr); | ||
| 242 | if (target_zone == NULL) | ||
| 243 | return sprintf(buf, "red: -1, green: -1, blue: -1\n"); | ||
| 244 | return sprintf(buf, "red: %d, green: %d, blue: %d\n", | ||
| 245 | target_zone->colors.red, | ||
| 246 | target_zone->colors.green, target_zone->colors.blue); | ||
| 247 | |||
| 248 | } | ||
| 249 | |||
| 250 | static ssize_t zone_set(struct device *dev, struct device_attribute *attr, | ||
| 251 | const char *buf, size_t count) | ||
| 252 | { | ||
| 253 | struct platform_zone *target_zone; | ||
| 254 | int ret; | ||
| 255 | target_zone = match_zone(attr); | ||
| 256 | if (target_zone == NULL) { | ||
| 257 | pr_err("alienware-wmi: invalid target zone\n"); | ||
| 258 | return 1; | ||
| 259 | } | ||
| 260 | ret = parse_rgb(buf, target_zone); | ||
| 261 | if (ret) | ||
| 262 | return ret; | ||
| 263 | ret = alienware_update_led(target_zone); | ||
| 264 | return ret ? ret : count; | ||
| 265 | } | ||
| 266 | |||
| 267 | /* | ||
| 268 | * LED Brightness (Global) | ||
| 269 | */ | ||
| 270 | static int wmax_brightness(int brightness) | ||
| 271 | { | ||
| 272 | acpi_status status; | ||
| 273 | struct acpi_buffer input; | ||
| 274 | struct wmax_brightness_args args = { | ||
| 275 | .led_mask = 0xFF, | ||
| 276 | .percentage = brightness, | ||
| 277 | }; | ||
| 278 | input.length = (acpi_size) sizeof(args); | ||
| 279 | input.pointer = &args; | ||
| 280 | status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, | ||
| 281 | WMAX_METHOD_BRIGHTNESS, &input, NULL); | ||
| 282 | if (ACPI_FAILURE(status)) | ||
| 283 | pr_err("alienware-wmi: brightness set failure: %u\n", status); | ||
| 284 | return ACPI_FAILURE(status); | ||
| 285 | } | ||
| 286 | |||
| 287 | static void global_led_set(struct led_classdev *led_cdev, | ||
| 288 | enum led_brightness brightness) | ||
| 289 | { | ||
| 290 | int ret; | ||
| 291 | global_brightness = brightness; | ||
| 292 | if (interface == WMAX) | ||
| 293 | ret = wmax_brightness(brightness); | ||
| 294 | else | ||
| 295 | ret = alienware_update_led(&zone_data[0]); | ||
| 296 | if (ret) | ||
| 297 | pr_err("LED brightness update failed\n"); | ||
| 298 | } | ||
| 299 | |||
| 300 | static enum led_brightness global_led_get(struct led_classdev *led_cdev) | ||
| 301 | { | ||
| 302 | return global_brightness; | ||
| 303 | } | ||
| 304 | |||
| 305 | static struct led_classdev global_led = { | ||
| 306 | .brightness_set = global_led_set, | ||
| 307 | .brightness_get = global_led_get, | ||
| 308 | .name = "alienware::global_brightness", | ||
| 309 | }; | ||
| 310 | |||
| 311 | /* | ||
| 312 | * Lighting control state device attribute (Global) | ||
| 313 | */ | ||
| 314 | static ssize_t show_control_state(struct device *dev, | ||
| 315 | struct device_attribute *attr, char *buf) | ||
| 316 | { | ||
| 317 | if (lighting_control_state == LEGACY_BOOTING) | ||
| 318 | return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n"); | ||
| 319 | else if (lighting_control_state == LEGACY_SUSPEND) | ||
| 320 | return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n"); | ||
| 321 | return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n"); | ||
| 322 | } | ||
| 323 | |||
| 324 | static ssize_t store_control_state(struct device *dev, | ||
| 325 | struct device_attribute *attr, | ||
| 326 | const char *buf, size_t count) | ||
| 327 | { | ||
| 328 | long unsigned int val; | ||
| 329 | if (strcmp(buf, "booting\n") == 0) | ||
| 330 | val = LEGACY_BOOTING; | ||
| 331 | else if (strcmp(buf, "suspend\n") == 0) | ||
| 332 | val = LEGACY_SUSPEND; | ||
| 333 | else if (interface == LEGACY) | ||
| 334 | val = LEGACY_RUNNING; | ||
| 335 | else | ||
| 336 | val = WMAX_RUNNING; | ||
| 337 | lighting_control_state = val; | ||
| 338 | pr_debug("alienware-wmi: updated control state to %d\n", | ||
| 339 | lighting_control_state); | ||
| 340 | return count; | ||
| 341 | } | ||
| 342 | |||
| 343 | static DEVICE_ATTR(lighting_control_state, 0644, show_control_state, | ||
| 344 | store_control_state); | ||
| 345 | |||
| 346 | static int alienware_zone_init(struct platform_device *dev) | ||
| 347 | { | ||
| 348 | int i; | ||
| 349 | char buffer[10]; | ||
| 350 | char *name; | ||
| 351 | |||
| 352 | if (interface == WMAX) { | ||
| 353 | global_led.max_brightness = 100; | ||
| 354 | lighting_control_state = WMAX_RUNNING; | ||
| 355 | } else if (interface == LEGACY) { | ||
| 356 | global_led.max_brightness = 0x0F; | ||
| 357 | lighting_control_state = LEGACY_RUNNING; | ||
| 358 | } | ||
| 359 | global_brightness = global_led.max_brightness; | ||
| 360 | |||
| 361 | /* | ||
| 362 | * - zone_dev_attrs num_zones + 1 is for individual zones and then | ||
| 363 | * null terminated | ||
| 364 | * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs + | ||
| 365 | * the lighting control + null terminated | ||
| 366 | * - zone_data num_zones is for the distinct zones | ||
| 367 | */ | ||
| 368 | zone_dev_attrs = | ||
| 369 | kzalloc(sizeof(struct device_attribute) * (quirks->num_zones + 1), | ||
| 370 | GFP_KERNEL); | ||
| 371 | if (!zone_dev_attrs) | ||
| 372 | return -ENOMEM; | ||
| 373 | |||
| 374 | zone_attrs = | ||
| 375 | kzalloc(sizeof(struct attribute *) * (quirks->num_zones + 2), | ||
| 376 | GFP_KERNEL); | ||
| 377 | if (!zone_attrs) | ||
| 378 | return -ENOMEM; | ||
| 379 | |||
| 380 | zone_data = | ||
| 381 | kzalloc(sizeof(struct platform_zone) * (quirks->num_zones), | ||
| 382 | GFP_KERNEL); | ||
| 383 | if (!zone_data) | ||
| 384 | return -ENOMEM; | ||
| 385 | |||
| 386 | for (i = 0; i < quirks->num_zones; i++) { | ||
| 387 | sprintf(buffer, "zone%02X", i); | ||
| 388 | name = kstrdup(buffer, GFP_KERNEL); | ||
| 389 | if (name == NULL) | ||
| 390 | return 1; | ||
| 391 | sysfs_attr_init(&zone_dev_attrs[i].attr); | ||
| 392 | zone_dev_attrs[i].attr.name = name; | ||
| 393 | zone_dev_attrs[i].attr.mode = 0644; | ||
| 394 | zone_dev_attrs[i].show = zone_show; | ||
| 395 | zone_dev_attrs[i].store = zone_set; | ||
| 396 | zone_data[i].location = i; | ||
| 397 | zone_attrs[i] = &zone_dev_attrs[i].attr; | ||
| 398 | zone_data[i].attr = &zone_dev_attrs[i]; | ||
| 399 | } | ||
| 400 | zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr; | ||
| 401 | zone_attribute_group.attrs = zone_attrs; | ||
| 402 | |||
| 403 | led_classdev_register(&dev->dev, &global_led); | ||
| 404 | |||
| 405 | return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group); | ||
| 406 | } | ||
| 407 | |||
| 408 | static void alienware_zone_exit(struct platform_device *dev) | ||
| 409 | { | ||
| 410 | sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group); | ||
| 411 | led_classdev_unregister(&global_led); | ||
| 412 | if (zone_dev_attrs) { | ||
| 413 | int i; | ||
| 414 | for (i = 0; i < quirks->num_zones; i++) | ||
| 415 | kfree(zone_dev_attrs[i].attr.name); | ||
| 416 | } | ||
| 417 | kfree(zone_dev_attrs); | ||
| 418 | kfree(zone_data); | ||
| 419 | kfree(zone_attrs); | ||
| 420 | } | ||
| 421 | |||
| 422 | /* | ||
| 423 | The HDMI mux sysfs node indicates the status of the HDMI input mux. | ||
| 424 | It can toggle between standard system GPU output and HDMI input. | ||
| 425 | */ | ||
| 426 | static ssize_t show_hdmi(struct device *dev, struct device_attribute *attr, | ||
| 427 | char *buf) | ||
| 428 | { | ||
| 429 | acpi_status status; | ||
| 430 | struct acpi_buffer input; | ||
| 431 | union acpi_object *obj; | ||
| 432 | u32 tmp = 0; | ||
| 433 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 434 | struct hdmi_args in_args = { | ||
| 435 | .arg = 0, | ||
| 436 | }; | ||
| 437 | input.length = (acpi_size) sizeof(in_args); | ||
| 438 | input.pointer = &in_args; | ||
| 439 | status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, | ||
| 440 | WMAX_METHOD_HDMI_STATUS, &input, &output); | ||
| 441 | |||
| 442 | if (ACPI_SUCCESS(status)) { | ||
| 443 | obj = (union acpi_object *)output.pointer; | ||
| 444 | if (obj && obj->type == ACPI_TYPE_INTEGER) | ||
| 445 | tmp = (u32) obj->integer.value; | ||
| 446 | if (tmp == 1) | ||
| 447 | return scnprintf(buf, PAGE_SIZE, | ||
| 448 | "[input] gpu unknown\n"); | ||
| 449 | else if (tmp == 2) | ||
| 450 | return scnprintf(buf, PAGE_SIZE, | ||
| 451 | "input [gpu] unknown\n"); | ||
| 452 | } | ||
| 453 | pr_err("alienware-wmi: unknown HDMI status: %d\n", status); | ||
| 454 | return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n"); | ||
| 455 | } | ||
| 456 | |||
| 457 | static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr, | ||
| 458 | const char *buf, size_t count) | ||
| 459 | { | ||
| 460 | struct acpi_buffer input; | ||
| 461 | acpi_status status; | ||
| 462 | struct hdmi_args args; | ||
| 463 | if (strcmp(buf, "gpu\n") == 0) | ||
| 464 | args.arg = 1; | ||
| 465 | else if (strcmp(buf, "input\n") == 0) | ||
| 466 | args.arg = 2; | ||
| 467 | else | ||
| 468 | args.arg = 3; | ||
| 469 | pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); | ||
| 470 | input.length = (acpi_size) sizeof(args); | ||
| 471 | input.pointer = &args; | ||
| 472 | status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, | ||
| 473 | WMAX_METHOD_HDMI_SOURCE, &input, NULL); | ||
| 474 | if (ACPI_FAILURE(status)) | ||
| 475 | pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", | ||
| 476 | status); | ||
| 477 | return count; | ||
| 478 | } | ||
| 479 | |||
| 480 | static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi); | ||
| 481 | |||
| 482 | static void remove_hdmi(struct platform_device *device) | ||
| 483 | { | ||
| 484 | device_remove_file(&device->dev, &dev_attr_hdmi); | ||
| 485 | } | ||
| 486 | |||
| 487 | static int create_hdmi(void) | ||
| 488 | { | ||
| 489 | int ret = -ENOMEM; | ||
| 490 | ret = device_create_file(&platform_device->dev, &dev_attr_hdmi); | ||
| 491 | if (ret) | ||
| 492 | goto error_create_hdmi; | ||
| 493 | return 0; | ||
| 494 | |||
| 495 | error_create_hdmi: | ||
| 496 | remove_hdmi(platform_device); | ||
| 497 | return ret; | ||
| 498 | } | ||
| 499 | |||
| 500 | static int __init alienware_wmi_init(void) | ||
| 501 | { | ||
| 502 | int ret; | ||
| 503 | |||
| 504 | if (wmi_has_guid(LEGACY_CONTROL_GUID)) | ||
| 505 | interface = LEGACY; | ||
| 506 | else if (wmi_has_guid(WMAX_CONTROL_GUID)) | ||
| 507 | interface = WMAX; | ||
| 508 | else { | ||
| 509 | pr_warn("alienware-wmi: No known WMI GUID found\n"); | ||
| 510 | return -ENODEV; | ||
| 511 | } | ||
| 512 | |||
| 513 | dmi_check_system(alienware_quirks); | ||
| 514 | if (quirks == NULL) | ||
| 515 | quirks = &quirk_unknown; | ||
| 516 | |||
| 517 | ret = platform_driver_register(&platform_driver); | ||
| 518 | if (ret) | ||
| 519 | goto fail_platform_driver; | ||
| 520 | platform_device = platform_device_alloc("alienware-wmi", -1); | ||
| 521 | if (!platform_device) { | ||
| 522 | ret = -ENOMEM; | ||
| 523 | goto fail_platform_device1; | ||
| 524 | } | ||
| 525 | ret = platform_device_add(platform_device); | ||
| 526 | if (ret) | ||
| 527 | goto fail_platform_device2; | ||
| 528 | |||
| 529 | if (interface == WMAX) { | ||
| 530 | ret = create_hdmi(); | ||
| 531 | if (ret) | ||
| 532 | goto fail_prep_hdmi; | ||
| 533 | } | ||
| 534 | |||
| 535 | ret = alienware_zone_init(platform_device); | ||
| 536 | if (ret) | ||
| 537 | goto fail_prep_zones; | ||
| 538 | |||
| 539 | return 0; | ||
| 540 | |||
| 541 | fail_prep_zones: | ||
| 542 | alienware_zone_exit(platform_device); | ||
| 543 | fail_prep_hdmi: | ||
| 544 | platform_device_del(platform_device); | ||
| 545 | fail_platform_device2: | ||
| 546 | platform_device_put(platform_device); | ||
| 547 | fail_platform_device1: | ||
| 548 | platform_driver_unregister(&platform_driver); | ||
| 549 | fail_platform_driver: | ||
| 550 | return ret; | ||
| 551 | } | ||
| 552 | |||
| 553 | module_init(alienware_wmi_init); | ||
| 554 | |||
| 555 | static void __exit alienware_wmi_exit(void) | ||
| 556 | { | ||
| 557 | if (platform_device) { | ||
| 558 | alienware_zone_exit(platform_device); | ||
| 559 | remove_hdmi(platform_device); | ||
| 560 | platform_device_unregister(platform_device); | ||
| 561 | platform_driver_unregister(&platform_driver); | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | module_exit(alienware_wmi_exit); | ||
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 570926c10014..c3784baceae3 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c | |||
| @@ -71,6 +71,44 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = { | |||
| 71 | KEY_LEFTALT | 71 | KEY_LEFTALT |
| 72 | }; | 72 | }; |
| 73 | 73 | ||
| 74 | static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = { | ||
| 75 | KEY_RESERVED, | ||
| 76 | KEY_RESERVED, | ||
| 77 | KEY_RESERVED, | ||
| 78 | KEY_RESERVED, | ||
| 79 | KEY_SCROLLDOWN, | ||
| 80 | KEY_SCROLLUP, | ||
| 81 | KEY_CYCLEWINDOWS, | ||
| 82 | KEY_LEFTCTRL, | ||
| 83 | KEY_RESERVED, | ||
| 84 | KEY_RESERVED, | ||
| 85 | KEY_RESERVED, | ||
| 86 | KEY_RESERVED, | ||
| 87 | KEY_RESERVED, | ||
| 88 | KEY_RESERVED, | ||
| 89 | KEY_RESERVED, | ||
| 90 | KEY_LEFTMETA | ||
| 91 | }; | ||
| 92 | |||
| 93 | static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = { | ||
| 94 | KEY_RESERVED, | ||
| 95 | KEY_VOLUMEDOWN, | ||
| 96 | KEY_VOLUMEUP, | ||
| 97 | KEY_CYCLEWINDOWS, | ||
| 98 | KEY_PROG1, | ||
| 99 | KEY_PROG2, | ||
| 100 | KEY_LEFTMETA, | ||
| 101 | KEY_RESERVED, | ||
| 102 | KEY_RESERVED, | ||
| 103 | KEY_RESERVED, | ||
| 104 | KEY_RESERVED, | ||
| 105 | KEY_RESERVED, | ||
| 106 | KEY_RESERVED, | ||
| 107 | KEY_RESERVED, | ||
| 108 | KEY_RESERVED, | ||
| 109 | KEY_RESERVED, | ||
| 110 | }; | ||
| 111 | |||
| 74 | static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = { | 112 | static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = { |
| 75 | KEY_RESERVED, | 113 | KEY_RESERVED, |
| 76 | KEY_RESERVED, | 114 | KEY_RESERVED, |
| @@ -302,6 +340,33 @@ static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) | |||
| 302 | static const struct dmi_system_id dmi_ids[] __initconst = { | 340 | static const struct dmi_system_id dmi_ids[] __initconst = { |
| 303 | { | 341 | { |
| 304 | .callback = fujitsu_dmi_lifebook, | 342 | .callback = fujitsu_dmi_lifebook, |
| 343 | .ident = "Fujitsu Lifebook T901", | ||
| 344 | .matches = { | ||
| 345 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | ||
| 346 | DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901") | ||
| 347 | }, | ||
| 348 | .driver_data = keymap_Lifebook_T901 | ||
| 349 | }, | ||
| 350 | { | ||
| 351 | .callback = fujitsu_dmi_lifebook, | ||
| 352 | .ident = "Fujitsu Lifebook T901", | ||
| 353 | .matches = { | ||
| 354 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | ||
| 355 | DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901") | ||
| 356 | }, | ||
| 357 | .driver_data = keymap_Lifebook_T901 | ||
| 358 | }, | ||
| 359 | { | ||
| 360 | .callback = fujitsu_dmi_lifebook, | ||
| 361 | .ident = "Fujitsu Lifebook T902", | ||
| 362 | .matches = { | ||
| 363 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | ||
| 364 | DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902") | ||
| 365 | }, | ||
| 366 | .driver_data = keymap_Lifebook_T902 | ||
| 367 | }, | ||
| 368 | { | ||
| 369 | .callback = fujitsu_dmi_lifebook, | ||
| 305 | .ident = "Fujitsu Siemens P/T Series", | 370 | .ident = "Fujitsu Siemens P/T Series", |
| 306 | .matches = { | 371 | .matches = { |
| 307 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | 372 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), |
diff --git a/drivers/platform/x86/intel_baytrail.c b/drivers/platform/x86/intel_baytrail.c deleted file mode 100644 index f96626b17260..000000000000 --- a/drivers/platform/x86/intel_baytrail.c +++ /dev/null | |||
| @@ -1,224 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Baytrail IOSF-SB MailBox Interface Driver | ||
| 3 | * Copyright (c) 2013, Intel Corporation. | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * | ||
| 15 | * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a | ||
| 16 | * mailbox interface (MBI) to communicate with mutiple devices. This | ||
| 17 | * driver implements BayTrail-specific access to this interface. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/init.h> | ||
| 22 | #include <linux/spinlock.h> | ||
| 23 | #include <linux/pci.h> | ||
| 24 | |||
| 25 | #include "intel_baytrail.h" | ||
| 26 | |||
| 27 | static DEFINE_SPINLOCK(iosf_mbi_lock); | ||
| 28 | |||
| 29 | static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset) | ||
| 30 | { | ||
| 31 | return (op << 24) | (port << 16) | (offset << 8) | BT_MBI_ENABLE; | ||
| 32 | } | ||
| 33 | |||
| 34 | static struct pci_dev *mbi_pdev; /* one mbi device */ | ||
| 35 | |||
| 36 | /* Hold lock before calling */ | ||
| 37 | static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr) | ||
| 38 | { | ||
| 39 | int result; | ||
| 40 | |||
| 41 | if (!mbi_pdev) | ||
| 42 | return -ENODEV; | ||
| 43 | |||
| 44 | if (mcrx) { | ||
| 45 | result = pci_write_config_dword(mbi_pdev, | ||
| 46 | BT_MBI_MCRX_OFFSET, mcrx); | ||
| 47 | if (result < 0) | ||
| 48 | goto iosf_mbi_read_err; | ||
| 49 | } | ||
| 50 | |||
| 51 | result = pci_write_config_dword(mbi_pdev, | ||
| 52 | BT_MBI_MCR_OFFSET, mcr); | ||
| 53 | if (result < 0) | ||
| 54 | goto iosf_mbi_read_err; | ||
| 55 | |||
| 56 | result = pci_read_config_dword(mbi_pdev, | ||
| 57 | BT_MBI_MDR_OFFSET, mdr); | ||
| 58 | if (result < 0) | ||
| 59 | goto iosf_mbi_read_err; | ||
| 60 | |||
| 61 | return 0; | ||
| 62 | |||
| 63 | iosf_mbi_read_err: | ||
| 64 | dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n", | ||
| 65 | result); | ||
| 66 | return result; | ||
| 67 | } | ||
| 68 | |||
| 69 | /* Hold lock before calling */ | ||
| 70 | static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr) | ||
| 71 | { | ||
| 72 | int result; | ||
| 73 | |||
| 74 | if (!mbi_pdev) | ||
| 75 | return -ENODEV; | ||
| 76 | |||
| 77 | result = pci_write_config_dword(mbi_pdev, | ||
| 78 | BT_MBI_MDR_OFFSET, mdr); | ||
| 79 | if (result < 0) | ||
| 80 | goto iosf_mbi_write_err; | ||
| 81 | |||
| 82 | if (mcrx) { | ||
| 83 | result = pci_write_config_dword(mbi_pdev, | ||
| 84 | BT_MBI_MCRX_OFFSET, mcrx); | ||
| 85 | if (result < 0) | ||
| 86 | goto iosf_mbi_write_err; | ||
| 87 | } | ||
| 88 | |||
| 89 | result = pci_write_config_dword(mbi_pdev, | ||
| 90 | BT_MBI_MCR_OFFSET, mcr); | ||
| 91 | if (result < 0) | ||
| 92 | goto iosf_mbi_write_err; | ||
| 93 | |||
| 94 | return 0; | ||
| 95 | |||
| 96 | iosf_mbi_write_err: | ||
| 97 | dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n", | ||
| 98 | result); | ||
| 99 | return result; | ||
| 100 | } | ||
| 101 | |||
| 102 | int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr) | ||
| 103 | { | ||
| 104 | u32 mcr, mcrx; | ||
| 105 | unsigned long flags; | ||
| 106 | int ret; | ||
| 107 | |||
| 108 | /*Access to the GFX unit is handled by GPU code */ | ||
| 109 | BUG_ON(port == BT_MBI_UNIT_GFX); | ||
| 110 | |||
| 111 | mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO); | ||
| 112 | mcrx = offset & BT_MBI_MASK_HI; | ||
| 113 | |||
| 114 | spin_lock_irqsave(&iosf_mbi_lock, flags); | ||
| 115 | ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr); | ||
| 116 | spin_unlock_irqrestore(&iosf_mbi_lock, flags); | ||
| 117 | |||
| 118 | return ret; | ||
| 119 | } | ||
| 120 | EXPORT_SYMBOL(bt_mbi_read); | ||
| 121 | |||
| 122 | int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr) | ||
| 123 | { | ||
| 124 | u32 mcr, mcrx; | ||
| 125 | unsigned long flags; | ||
| 126 | int ret; | ||
| 127 | |||
| 128 | /*Access to the GFX unit is handled by GPU code */ | ||
| 129 | BUG_ON(port == BT_MBI_UNIT_GFX); | ||
| 130 | |||
| 131 | mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO); | ||
| 132 | mcrx = offset & BT_MBI_MASK_HI; | ||
| 133 | |||
| 134 | spin_lock_irqsave(&iosf_mbi_lock, flags); | ||
| 135 | ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr); | ||
| 136 | spin_unlock_irqrestore(&iosf_mbi_lock, flags); | ||
| 137 | |||
| 138 | return ret; | ||
| 139 | } | ||
| 140 | EXPORT_SYMBOL(bt_mbi_write); | ||
| 141 | |||
| 142 | int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask) | ||
| 143 | { | ||
| 144 | u32 mcr, mcrx; | ||
| 145 | u32 value; | ||
| 146 | unsigned long flags; | ||
| 147 | int ret; | ||
| 148 | |||
| 149 | /*Access to the GFX unit is handled by GPU code */ | ||
| 150 | BUG_ON(port == BT_MBI_UNIT_GFX); | ||
| 151 | |||
| 152 | mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO); | ||
| 153 | mcrx = offset & BT_MBI_MASK_HI; | ||
| 154 | |||
| 155 | spin_lock_irqsave(&iosf_mbi_lock, flags); | ||
| 156 | |||
| 157 | /* Read current mdr value */ | ||
| 158 | ret = iosf_mbi_pci_read_mdr(mcrx, mcr & BT_MBI_RD_MASK, &value); | ||
| 159 | if (ret < 0) { | ||
| 160 | spin_unlock_irqrestore(&iosf_mbi_lock, flags); | ||
| 161 | return ret; | ||
| 162 | } | ||
| 163 | |||
| 164 | /* Apply mask */ | ||
| 165 | value &= ~mask; | ||
| 166 | mdr &= mask; | ||
| 167 | value |= mdr; | ||
| 168 | |||
| 169 | /* Write back */ | ||
| 170 | ret = iosf_mbi_pci_write_mdr(mcrx, mcr | BT_MBI_WR_MASK, value); | ||
| 171 | |||
| 172 | spin_unlock_irqrestore(&iosf_mbi_lock, flags); | ||
| 173 | |||
| 174 | return ret; | ||
| 175 | } | ||
| 176 | EXPORT_SYMBOL(bt_mbi_modify); | ||
| 177 | |||
| 178 | static int iosf_mbi_probe(struct pci_dev *pdev, | ||
| 179 | const struct pci_device_id *unused) | ||
| 180 | { | ||
| 181 | int ret; | ||
| 182 | |||
| 183 | ret = pci_enable_device(pdev); | ||
| 184 | if (ret < 0) { | ||
| 185 | dev_err(&pdev->dev, "error: could not enable device\n"); | ||
| 186 | return ret; | ||
| 187 | } | ||
| 188 | |||
| 189 | mbi_pdev = pci_dev_get(pdev); | ||
| 190 | return 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = { | ||
| 194 | { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F00) }, | ||
| 195 | { 0, }, | ||
| 196 | }; | ||
| 197 | MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids); | ||
| 198 | |||
| 199 | static struct pci_driver iosf_mbi_pci_driver = { | ||
| 200 | .name = "iosf_mbi_pci", | ||
| 201 | .probe = iosf_mbi_probe, | ||
| 202 | .id_table = iosf_mbi_pci_ids, | ||
| 203 | }; | ||
| 204 | |||
| 205 | static int __init bt_mbi_init(void) | ||
| 206 | { | ||
| 207 | return pci_register_driver(&iosf_mbi_pci_driver); | ||
| 208 | } | ||
| 209 | |||
| 210 | static void __exit bt_mbi_exit(void) | ||
| 211 | { | ||
| 212 | pci_unregister_driver(&iosf_mbi_pci_driver); | ||
| 213 | if (mbi_pdev) { | ||
| 214 | pci_dev_put(mbi_pdev); | ||
| 215 | mbi_pdev = NULL; | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | module_init(bt_mbi_init); | ||
| 220 | module_exit(bt_mbi_exit); | ||
| 221 | |||
| 222 | MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); | ||
| 223 | MODULE_DESCRIPTION("BayTrail Mailbox Interface accessor"); | ||
| 224 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/platform/x86/intel_baytrail.h b/drivers/platform/x86/intel_baytrail.h deleted file mode 100644 index 8bcc311262e9..000000000000 --- a/drivers/platform/x86/intel_baytrail.h +++ /dev/null | |||
| @@ -1,90 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * intel_baytrail.h: MailBox access support for Intel BayTrail platforms | ||
| 3 | */ | ||
| 4 | |||
| 5 | #ifndef INTEL_BAYTRAIL_MBI_SYMS_H | ||
| 6 | #define INTEL_BAYTRAIL_MBI_SYMS_H | ||
| 7 | |||
| 8 | #define BT_MBI_MCR_OFFSET 0xD0 | ||
| 9 | #define BT_MBI_MDR_OFFSET 0xD4 | ||
| 10 | #define BT_MBI_MCRX_OFFSET 0xD8 | ||
| 11 | |||
| 12 | #define BT_MBI_RD_MASK 0xFEFFFFFF | ||
| 13 | #define BT_MBI_WR_MASK 0X01000000 | ||
| 14 | |||
| 15 | #define BT_MBI_MASK_HI 0xFFFFFF00 | ||
| 16 | #define BT_MBI_MASK_LO 0x000000FF | ||
| 17 | #define BT_MBI_ENABLE 0xF0 | ||
| 18 | |||
| 19 | /* BT-SB unit access methods */ | ||
| 20 | #define BT_MBI_UNIT_AUNIT 0x00 | ||
| 21 | #define BT_MBI_UNIT_SMC 0x01 | ||
| 22 | #define BT_MBI_UNIT_CPU 0x02 | ||
| 23 | #define BT_MBI_UNIT_BUNIT 0x03 | ||
| 24 | #define BT_MBI_UNIT_PMC 0x04 | ||
| 25 | #define BT_MBI_UNIT_GFX 0x06 | ||
| 26 | #define BT_MBI_UNIT_SMI 0x0C | ||
| 27 | #define BT_MBI_UNIT_USB 0x43 | ||
| 28 | #define BT_MBI_UNIT_SATA 0xA3 | ||
| 29 | #define BT_MBI_UNIT_PCIE 0xA6 | ||
| 30 | |||
| 31 | /* Read/write opcodes */ | ||
| 32 | #define BT_MBI_AUNIT_READ 0x10 | ||
| 33 | #define BT_MBI_AUNIT_WRITE 0x11 | ||
| 34 | #define BT_MBI_SMC_READ 0x10 | ||
| 35 | #define BT_MBI_SMC_WRITE 0x11 | ||
| 36 | #define BT_MBI_CPU_READ 0x10 | ||
| 37 | #define BT_MBI_CPU_WRITE 0x11 | ||
| 38 | #define BT_MBI_BUNIT_READ 0x10 | ||
| 39 | #define BT_MBI_BUNIT_WRITE 0x11 | ||
| 40 | #define BT_MBI_PMC_READ 0x06 | ||
| 41 | #define BT_MBI_PMC_WRITE 0x07 | ||
| 42 | #define BT_MBI_GFX_READ 0x00 | ||
| 43 | #define BT_MBI_GFX_WRITE 0x01 | ||
| 44 | #define BT_MBI_SMIO_READ 0x06 | ||
| 45 | #define BT_MBI_SMIO_WRITE 0x07 | ||
| 46 | #define BT_MBI_USB_READ 0x06 | ||
| 47 | #define BT_MBI_USB_WRITE 0x07 | ||
| 48 | #define BT_MBI_SATA_READ 0x00 | ||
| 49 | #define BT_MBI_SATA_WRITE 0x01 | ||
| 50 | #define BT_MBI_PCIE_READ 0x00 | ||
| 51 | #define BT_MBI_PCIE_WRITE 0x01 | ||
| 52 | |||
| 53 | /** | ||
| 54 | * bt_mbi_read() - MailBox Interface read command | ||
| 55 | * @port: port indicating subunit being accessed | ||
| 56 | * @opcode: port specific read or write opcode | ||
| 57 | * @offset: register address offset | ||
| 58 | * @mdr: register data to be read | ||
| 59 | * | ||
| 60 | * Locking is handled by spinlock - cannot sleep. | ||
| 61 | * Return: Nonzero on error | ||
| 62 | */ | ||
| 63 | int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr); | ||
| 64 | |||
| 65 | /** | ||
| 66 | * bt_mbi_write() - MailBox unmasked write command | ||
| 67 | * @port: port indicating subunit being accessed | ||
| 68 | * @opcode: port specific read or write opcode | ||
| 69 | * @offset: register address offset | ||
| 70 | * @mdr: register data to be written | ||
| 71 | * | ||
| 72 | * Locking is handled by spinlock - cannot sleep. | ||
| 73 | * Return: Nonzero on error | ||
| 74 | */ | ||
| 75 | int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr); | ||
| 76 | |||
| 77 | /** | ||
| 78 | * bt_mbi_modify() - MailBox masked write command | ||
| 79 | * @port: port indicating subunit being accessed | ||
| 80 | * @opcode: port specific read or write opcode | ||
| 81 | * @offset: register address offset | ||
| 82 | * @mdr: register data being modified | ||
| 83 | * @mask: mask indicating bits in mdr to be modified | ||
| 84 | * | ||
| 85 | * Locking is handled by spinlock - cannot sleep. | ||
| 86 | * Return: Nonzero on error | ||
| 87 | */ | ||
| 88 | int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask); | ||
| 89 | |||
| 90 | #endif /* INTEL_BAYTRAIL_MBI_SYMS_H */ | ||
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 609d38779b26..3f870972247c 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c | |||
| @@ -449,6 +449,7 @@ static struct attribute_group pcc_attr_group = { | |||
| 449 | 449 | ||
| 450 | /* hotkey input device driver */ | 450 | /* hotkey input device driver */ |
| 451 | 451 | ||
| 452 | static int sleep_keydown_seen; | ||
| 452 | static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) | 453 | static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) |
| 453 | { | 454 | { |
| 454 | struct input_dev *hotk_input_dev = pcc->input_dev; | 455 | struct input_dev *hotk_input_dev = pcc->input_dev; |
| @@ -462,6 +463,16 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) | |||
| 462 | "error getting hotkey status\n")); | 463 | "error getting hotkey status\n")); |
| 463 | return; | 464 | return; |
| 464 | } | 465 | } |
| 466 | |||
| 467 | /* hack: some firmware sends no key down for sleep / hibernate */ | ||
| 468 | if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) { | ||
| 469 | if (result & 0x80) | ||
| 470 | sleep_keydown_seen = 1; | ||
| 471 | if (!sleep_keydown_seen) | ||
| 472 | sparse_keymap_report_event(hotk_input_dev, | ||
| 473 | result & 0xf, 0x80, false); | ||
| 474 | } | ||
| 475 | |||
| 465 | if (!sparse_keymap_report_event(hotk_input_dev, | 476 | if (!sparse_keymap_report_event(hotk_input_dev, |
| 466 | result & 0xf, result & 0x80, false)) | 477 | result & 0xf, result & 0x80, false)) |
| 467 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | 478 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, |
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 8f8551a63cc0..9c5a07417b2b 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
| @@ -76,8 +76,6 @@ do { \ | |||
| 76 | pr_warn(fmt, ##__VA_ARGS__); \ | 76 | pr_warn(fmt, ##__VA_ARGS__); \ |
| 77 | } while (0) | 77 | } while (0) |
| 78 | 78 | ||
| 79 | #define SONY_LAPTOP_DRIVER_VERSION "0.6" | ||
| 80 | |||
| 81 | #define SONY_NC_CLASS "sony-nc" | 79 | #define SONY_NC_CLASS "sony-nc" |
| 82 | #define SONY_NC_HID "SNY5001" | 80 | #define SONY_NC_HID "SNY5001" |
| 83 | #define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver" | 81 | #define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver" |
| @@ -89,7 +87,6 @@ do { \ | |||
| 89 | MODULE_AUTHOR("Stelian Pop, Mattia Dongili"); | 87 | MODULE_AUTHOR("Stelian Pop, Mattia Dongili"); |
| 90 | MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)"); | 88 | MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)"); |
| 91 | MODULE_LICENSE("GPL"); | 89 | MODULE_LICENSE("GPL"); |
| 92 | MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION); | ||
| 93 | 90 | ||
| 94 | static int debug; | 91 | static int debug; |
| 95 | module_param(debug, int, 0); | 92 | module_param(debug, int, 0); |
| @@ -129,7 +126,8 @@ static int kbd_backlight = -1; | |||
| 129 | module_param(kbd_backlight, int, 0444); | 126 | module_param(kbd_backlight, int, 0444); |
| 130 | MODULE_PARM_DESC(kbd_backlight, | 127 | MODULE_PARM_DESC(kbd_backlight, |
| 131 | "set this to 0 to disable keyboard backlight, " | 128 | "set this to 0 to disable keyboard backlight, " |
| 132 | "1 to enable it (default: no change from current value)"); | 129 | "1 to enable it with automatic control and 2 to have it always " |
| 130 | "on (default: no change from current value)"); | ||
| 133 | 131 | ||
| 134 | static int kbd_backlight_timeout = -1; | 132 | static int kbd_backlight_timeout = -1; |
| 135 | module_param(kbd_backlight_timeout, int, 0444); | 133 | module_param(kbd_backlight_timeout, int, 0444); |
| @@ -152,7 +150,8 @@ static void sony_nc_battery_care_cleanup(struct platform_device *pd); | |||
| 152 | static int sony_nc_thermal_setup(struct platform_device *pd); | 150 | static int sony_nc_thermal_setup(struct platform_device *pd); |
| 153 | static void sony_nc_thermal_cleanup(struct platform_device *pd); | 151 | static void sony_nc_thermal_cleanup(struct platform_device *pd); |
| 154 | 152 | ||
| 155 | static int sony_nc_lid_resume_setup(struct platform_device *pd); | 153 | static int sony_nc_lid_resume_setup(struct platform_device *pd, |
| 154 | unsigned int handle); | ||
| 156 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd); | 155 | static void sony_nc_lid_resume_cleanup(struct platform_device *pd); |
| 157 | 156 | ||
| 158 | static int sony_nc_gfx_switch_setup(struct platform_device *pd, | 157 | static int sony_nc_gfx_switch_setup(struct platform_device *pd, |
| @@ -163,6 +162,21 @@ static int __sony_nc_gfx_switch_status_get(void); | |||
| 163 | static int sony_nc_highspeed_charging_setup(struct platform_device *pd); | 162 | static int sony_nc_highspeed_charging_setup(struct platform_device *pd); |
| 164 | static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); | 163 | static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); |
| 165 | 164 | ||
| 165 | static int sony_nc_lowbatt_setup(struct platform_device *pd); | ||
| 166 | static void sony_nc_lowbatt_cleanup(struct platform_device *pd); | ||
| 167 | |||
| 168 | static int sony_nc_fanspeed_setup(struct platform_device *pd); | ||
| 169 | static void sony_nc_fanspeed_cleanup(struct platform_device *pd); | ||
| 170 | |||
| 171 | static int sony_nc_usb_charge_setup(struct platform_device *pd); | ||
| 172 | static void sony_nc_usb_charge_cleanup(struct platform_device *pd); | ||
| 173 | |||
| 174 | static int sony_nc_panelid_setup(struct platform_device *pd); | ||
| 175 | static void sony_nc_panelid_cleanup(struct platform_device *pd); | ||
| 176 | |||
| 177 | static int sony_nc_smart_conn_setup(struct platform_device *pd); | ||
| 178 | static void sony_nc_smart_conn_cleanup(struct platform_device *pd); | ||
| 179 | |||
| 166 | static int sony_nc_touchpad_setup(struct platform_device *pd, | 180 | static int sony_nc_touchpad_setup(struct platform_device *pd, |
| 167 | unsigned int handle); | 181 | unsigned int handle); |
| 168 | static void sony_nc_touchpad_cleanup(struct platform_device *pd); | 182 | static void sony_nc_touchpad_cleanup(struct platform_device *pd); |
| @@ -1122,6 +1136,8 @@ static struct sony_nc_event sony_100_events[] = { | |||
| 1122 | { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, | 1136 | { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, |
| 1123 | { 0xa6, SONYPI_EVENT_HELP_PRESSED }, | 1137 | { 0xa6, SONYPI_EVENT_HELP_PRESSED }, |
| 1124 | { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, | 1138 | { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, |
| 1139 | { 0xa8, SONYPI_EVENT_FNKEY_1 }, | ||
| 1140 | { 0x28, SONYPI_EVENT_ANYBUTTON_RELEASED }, | ||
| 1125 | { 0, 0 }, | 1141 | { 0, 0 }, |
| 1126 | }; | 1142 | }; |
| 1127 | 1143 | ||
| @@ -1339,7 +1355,8 @@ static void sony_nc_function_setup(struct acpi_device *device, | |||
| 1339 | result); | 1355 | result); |
| 1340 | break; | 1356 | break; |
| 1341 | case 0x0119: | 1357 | case 0x0119: |
| 1342 | result = sony_nc_lid_resume_setup(pf_device); | 1358 | case 0x015D: |
| 1359 | result = sony_nc_lid_resume_setup(pf_device, handle); | ||
| 1343 | if (result) | 1360 | if (result) |
| 1344 | pr_err("couldn't set up lid resume function (%d)\n", | 1361 | pr_err("couldn't set up lid resume function (%d)\n", |
| 1345 | result); | 1362 | result); |
| @@ -1381,6 +1398,36 @@ static void sony_nc_function_setup(struct acpi_device *device, | |||
| 1381 | pr_err("couldn't set up keyboard backlight function (%d)\n", | 1398 | pr_err("couldn't set up keyboard backlight function (%d)\n", |
| 1382 | result); | 1399 | result); |
| 1383 | break; | 1400 | break; |
| 1401 | case 0x0121: | ||
| 1402 | result = sony_nc_lowbatt_setup(pf_device); | ||
| 1403 | if (result) | ||
| 1404 | pr_err("couldn't set up low battery function (%d)\n", | ||
| 1405 | result); | ||
| 1406 | break; | ||
| 1407 | case 0x0149: | ||
| 1408 | result = sony_nc_fanspeed_setup(pf_device); | ||
| 1409 | if (result) | ||
| 1410 | pr_err("couldn't set up fan speed function (%d)\n", | ||
| 1411 | result); | ||
| 1412 | break; | ||
| 1413 | case 0x0155: | ||
| 1414 | result = sony_nc_usb_charge_setup(pf_device); | ||
| 1415 | if (result) | ||
| 1416 | pr_err("couldn't set up USB charge support (%d)\n", | ||
| 1417 | result); | ||
| 1418 | break; | ||
| 1419 | case 0x011D: | ||
| 1420 | result = sony_nc_panelid_setup(pf_device); | ||
| 1421 | if (result) | ||
| 1422 | pr_err("couldn't set up panel ID function (%d)\n", | ||
| 1423 | result); | ||
| 1424 | break; | ||
| 1425 | case 0x0168: | ||
| 1426 | result = sony_nc_smart_conn_setup(pf_device); | ||
| 1427 | if (result) | ||
| 1428 | pr_err("couldn't set up smart connect support (%d)\n", | ||
| 1429 | result); | ||
| 1430 | break; | ||
| 1384 | default: | 1431 | default: |
| 1385 | continue; | 1432 | continue; |
| 1386 | } | 1433 | } |
| @@ -1420,6 +1467,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd) | |||
| 1420 | sony_nc_battery_care_cleanup(pd); | 1467 | sony_nc_battery_care_cleanup(pd); |
| 1421 | break; | 1468 | break; |
| 1422 | case 0x0119: | 1469 | case 0x0119: |
| 1470 | case 0x015D: | ||
| 1423 | sony_nc_lid_resume_cleanup(pd); | 1471 | sony_nc_lid_resume_cleanup(pd); |
| 1424 | break; | 1472 | break; |
| 1425 | case 0x0122: | 1473 | case 0x0122: |
| @@ -1444,6 +1492,21 @@ static void sony_nc_function_cleanup(struct platform_device *pd) | |||
| 1444 | case 0x0163: | 1492 | case 0x0163: |
| 1445 | sony_nc_kbd_backlight_cleanup(pd, handle); | 1493 | sony_nc_kbd_backlight_cleanup(pd, handle); |
| 1446 | break; | 1494 | break; |
| 1495 | case 0x0121: | ||
| 1496 | sony_nc_lowbatt_cleanup(pd); | ||
| 1497 | break; | ||
| 1498 | case 0x0149: | ||
| 1499 | sony_nc_fanspeed_cleanup(pd); | ||
| 1500 | break; | ||
| 1501 | case 0x0155: | ||
| 1502 | sony_nc_usb_charge_cleanup(pd); | ||
| 1503 | break; | ||
| 1504 | case 0x011D: | ||
| 1505 | sony_nc_panelid_cleanup(pd); | ||
| 1506 | break; | ||
| 1507 | case 0x0168: | ||
| 1508 | sony_nc_smart_conn_cleanup(pd); | ||
| 1509 | break; | ||
| 1447 | default: | 1510 | default: |
| 1448 | continue; | 1511 | continue; |
| 1449 | } | 1512 | } |
| @@ -1719,7 +1782,7 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) | |||
| 1719 | { | 1782 | { |
| 1720 | int result; | 1783 | int result; |
| 1721 | 1784 | ||
| 1722 | if (value > 1) | 1785 | if (value > 2) |
| 1723 | return -EINVAL; | 1786 | return -EINVAL; |
| 1724 | 1787 | ||
| 1725 | if (sony_call_snc_handle(kbdbl_ctl->handle, | 1788 | if (sony_call_snc_handle(kbdbl_ctl->handle, |
| @@ -1727,8 +1790,10 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) | |||
| 1727 | return -EIO; | 1790 | return -EIO; |
| 1728 | 1791 | ||
| 1729 | /* Try to turn the light on/off immediately */ | 1792 | /* Try to turn the light on/off immediately */ |
| 1730 | sony_call_snc_handle(kbdbl_ctl->handle, | 1793 | if (value != 1) |
| 1731 | (value << 0x10) | (kbdbl_ctl->base + 0x100), &result); | 1794 | sony_call_snc_handle(kbdbl_ctl->handle, |
| 1795 | (value << 0x0f) | (kbdbl_ctl->base + 0x100), | ||
| 1796 | &result); | ||
| 1732 | 1797 | ||
| 1733 | kbdbl_ctl->mode = value; | 1798 | kbdbl_ctl->mode = value; |
| 1734 | 1799 | ||
| @@ -2221,9 +2286,14 @@ static void sony_nc_thermal_resume(void) | |||
| 2221 | #endif | 2286 | #endif |
| 2222 | 2287 | ||
| 2223 | /* resume on LID open */ | 2288 | /* resume on LID open */ |
| 2289 | #define LID_RESUME_S5 0 | ||
| 2290 | #define LID_RESUME_S4 1 | ||
| 2291 | #define LID_RESUME_S3 2 | ||
| 2292 | #define LID_RESUME_MAX 3 | ||
| 2224 | struct snc_lid_resume_control { | 2293 | struct snc_lid_resume_control { |
| 2225 | struct device_attribute attrs[3]; | 2294 | struct device_attribute attrs[LID_RESUME_MAX]; |
| 2226 | unsigned int status; | 2295 | unsigned int status; |
| 2296 | int handle; | ||
| 2227 | }; | 2297 | }; |
| 2228 | static struct snc_lid_resume_control *lid_ctl; | 2298 | static struct snc_lid_resume_control *lid_ctl; |
| 2229 | 2299 | ||
| @@ -2231,8 +2301,9 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev, | |||
| 2231 | struct device_attribute *attr, | 2301 | struct device_attribute *attr, |
| 2232 | const char *buffer, size_t count) | 2302 | const char *buffer, size_t count) |
| 2233 | { | 2303 | { |
| 2234 | unsigned int result, pos; | 2304 | unsigned int result; |
| 2235 | unsigned long value; | 2305 | unsigned long value; |
| 2306 | unsigned int pos = LID_RESUME_S5; | ||
| 2236 | if (count > 31) | 2307 | if (count > 31) |
| 2237 | return -EINVAL; | 2308 | return -EINVAL; |
| 2238 | 2309 | ||
| @@ -2245,21 +2316,21 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev, | |||
| 2245 | * +--------------+ | 2316 | * +--------------+ |
| 2246 | * 2 1 0 | 2317 | * 2 1 0 |
| 2247 | */ | 2318 | */ |
| 2248 | if (strcmp(attr->attr.name, "lid_resume_S3") == 0) | 2319 | while (pos < LID_RESUME_MAX) { |
| 2249 | pos = 2; | 2320 | if (&lid_ctl->attrs[pos].attr == &attr->attr) |
| 2250 | else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) | 2321 | break; |
| 2251 | pos = 1; | 2322 | pos++; |
| 2252 | else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) | 2323 | } |
| 2253 | pos = 0; | 2324 | if (pos == LID_RESUME_MAX) |
| 2254 | else | 2325 | return -EINVAL; |
| 2255 | return -EINVAL; | ||
| 2256 | 2326 | ||
| 2257 | if (value) | 2327 | if (value) |
| 2258 | value = lid_ctl->status | (1 << pos); | 2328 | value = lid_ctl->status | (1 << pos); |
| 2259 | else | 2329 | else |
| 2260 | value = lid_ctl->status & ~(1 << pos); | 2330 | value = lid_ctl->status & ~(1 << pos); |
| 2261 | 2331 | ||
| 2262 | if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result)) | 2332 | if (sony_call_snc_handle(lid_ctl->handle, value << 0x10 | 0x0100, |
| 2333 | &result)) | ||
| 2263 | return -EIO; | 2334 | return -EIO; |
| 2264 | 2335 | ||
| 2265 | lid_ctl->status = value; | 2336 | lid_ctl->status = value; |
| @@ -2268,29 +2339,27 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev, | |||
| 2268 | } | 2339 | } |
| 2269 | 2340 | ||
| 2270 | static ssize_t sony_nc_lid_resume_show(struct device *dev, | 2341 | static ssize_t sony_nc_lid_resume_show(struct device *dev, |
| 2271 | struct device_attribute *attr, char *buffer) | 2342 | struct device_attribute *attr, |
| 2343 | char *buffer) | ||
| 2272 | { | 2344 | { |
| 2273 | unsigned int pos; | 2345 | unsigned int pos = LID_RESUME_S5; |
| 2274 | 2346 | ||
| 2275 | if (strcmp(attr->attr.name, "lid_resume_S3") == 0) | 2347 | while (pos < LID_RESUME_MAX) { |
| 2276 | pos = 2; | 2348 | if (&lid_ctl->attrs[pos].attr == &attr->attr) |
| 2277 | else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) | 2349 | return snprintf(buffer, PAGE_SIZE, "%d\n", |
| 2278 | pos = 1; | 2350 | (lid_ctl->status >> pos) & 0x01); |
| 2279 | else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) | 2351 | pos++; |
| 2280 | pos = 0; | 2352 | } |
| 2281 | else | 2353 | return -EINVAL; |
| 2282 | return -EINVAL; | ||
| 2283 | |||
| 2284 | return snprintf(buffer, PAGE_SIZE, "%d\n", | ||
| 2285 | (lid_ctl->status >> pos) & 0x01); | ||
| 2286 | } | 2354 | } |
| 2287 | 2355 | ||
| 2288 | static int sony_nc_lid_resume_setup(struct platform_device *pd) | 2356 | static int sony_nc_lid_resume_setup(struct platform_device *pd, |
| 2357 | unsigned int handle) | ||
| 2289 | { | 2358 | { |
| 2290 | unsigned int result; | 2359 | unsigned int result; |
| 2291 | int i; | 2360 | int i; |
| 2292 | 2361 | ||
| 2293 | if (sony_call_snc_handle(0x0119, 0x0000, &result)) | 2362 | if (sony_call_snc_handle(handle, 0x0000, &result)) |
| 2294 | return -EIO; | 2363 | return -EIO; |
| 2295 | 2364 | ||
| 2296 | lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL); | 2365 | 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) | |||
| 2298 | return -ENOMEM; | 2367 | return -ENOMEM; |
| 2299 | 2368 | ||
| 2300 | lid_ctl->status = result & 0x7; | 2369 | lid_ctl->status = result & 0x7; |
| 2370 | lid_ctl->handle = handle; | ||
| 2301 | 2371 | ||
| 2302 | sysfs_attr_init(&lid_ctl->attrs[0].attr); | 2372 | sysfs_attr_init(&lid_ctl->attrs[0].attr); |
| 2303 | lid_ctl->attrs[0].attr.name = "lid_resume_S3"; | 2373 | lid_ctl->attrs[LID_RESUME_S5].attr.name = "lid_resume_S5"; |
| 2304 | lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; | 2374 | lid_ctl->attrs[LID_RESUME_S5].attr.mode = S_IRUGO | S_IWUSR; |
| 2305 | lid_ctl->attrs[0].show = sony_nc_lid_resume_show; | 2375 | lid_ctl->attrs[LID_RESUME_S5].show = sony_nc_lid_resume_show; |
| 2306 | lid_ctl->attrs[0].store = sony_nc_lid_resume_store; | 2376 | lid_ctl->attrs[LID_RESUME_S5].store = sony_nc_lid_resume_store; |
| 2307 | 2377 | ||
| 2308 | sysfs_attr_init(&lid_ctl->attrs[1].attr); | 2378 | if (handle == 0x0119) { |
| 2309 | lid_ctl->attrs[1].attr.name = "lid_resume_S4"; | 2379 | sysfs_attr_init(&lid_ctl->attrs[1].attr); |
| 2310 | lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR; | 2380 | lid_ctl->attrs[LID_RESUME_S4].attr.name = "lid_resume_S4"; |
| 2311 | lid_ctl->attrs[1].show = sony_nc_lid_resume_show; | 2381 | lid_ctl->attrs[LID_RESUME_S4].attr.mode = S_IRUGO | S_IWUSR; |
| 2312 | lid_ctl->attrs[1].store = sony_nc_lid_resume_store; | 2382 | lid_ctl->attrs[LID_RESUME_S4].show = sony_nc_lid_resume_show; |
| 2313 | 2383 | lid_ctl->attrs[LID_RESUME_S4].store = sony_nc_lid_resume_store; | |
| 2314 | sysfs_attr_init(&lid_ctl->attrs[2].attr); | 2384 | |
| 2315 | lid_ctl->attrs[2].attr.name = "lid_resume_S5"; | 2385 | sysfs_attr_init(&lid_ctl->attrs[2].attr); |
| 2316 | lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR; | 2386 | lid_ctl->attrs[LID_RESUME_S3].attr.name = "lid_resume_S3"; |
| 2317 | lid_ctl->attrs[2].show = sony_nc_lid_resume_show; | 2387 | lid_ctl->attrs[LID_RESUME_S3].attr.mode = S_IRUGO | S_IWUSR; |
| 2318 | lid_ctl->attrs[2].store = sony_nc_lid_resume_store; | 2388 | lid_ctl->attrs[LID_RESUME_S3].show = sony_nc_lid_resume_show; |
| 2319 | 2389 | lid_ctl->attrs[LID_RESUME_S3].store = sony_nc_lid_resume_store; | |
| 2320 | for (i = 0; i < 3; i++) { | 2390 | } |
| 2391 | for (i = 0; i < LID_RESUME_MAX && | ||
| 2392 | lid_ctl->attrs[LID_RESUME_S3].attr.name; i++) { | ||
| 2321 | result = device_create_file(&pd->dev, &lid_ctl->attrs[i]); | 2393 | result = device_create_file(&pd->dev, &lid_ctl->attrs[i]); |
| 2322 | if (result) | 2394 | if (result) |
| 2323 | goto liderror; | 2395 | goto liderror; |
| @@ -2340,8 +2412,12 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd) | |||
| 2340 | int i; | 2412 | int i; |
| 2341 | 2413 | ||
| 2342 | if (lid_ctl) { | 2414 | if (lid_ctl) { |
| 2343 | for (i = 0; i < 3; i++) | 2415 | for (i = 0; i < LID_RESUME_MAX; i++) { |
| 2416 | if (!lid_ctl->attrs[i].attr.name) | ||
| 2417 | break; | ||
| 2418 | |||
| 2344 | device_remove_file(&pd->dev, &lid_ctl->attrs[i]); | 2419 | device_remove_file(&pd->dev, &lid_ctl->attrs[i]); |
| 2420 | } | ||
| 2345 | 2421 | ||
| 2346 | kfree(lid_ctl); | 2422 | kfree(lid_ctl); |
| 2347 | lid_ctl = NULL; | 2423 | lid_ctl = NULL; |
| @@ -2524,6 +2600,355 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd) | |||
| 2524 | } | 2600 | } |
| 2525 | } | 2601 | } |
| 2526 | 2602 | ||
| 2603 | /* low battery function */ | ||
| 2604 | static struct device_attribute *lowbatt_handle; | ||
| 2605 | |||
| 2606 | static ssize_t sony_nc_lowbatt_store(struct device *dev, | ||
| 2607 | struct device_attribute *attr, | ||
| 2608 | const char *buffer, size_t count) | ||
| 2609 | { | ||
| 2610 | unsigned int result; | ||
| 2611 | unsigned long value; | ||
| 2612 | |||
| 2613 | if (count > 31) | ||
| 2614 | return -EINVAL; | ||
| 2615 | |||
| 2616 | if (kstrtoul(buffer, 10, &value) || value > 1) | ||
| 2617 | return -EINVAL; | ||
| 2618 | |||
| 2619 | if (sony_call_snc_handle(0x0121, value << 8, &result)) | ||
| 2620 | return -EIO; | ||
| 2621 | |||
| 2622 | return count; | ||
| 2623 | } | ||
| 2624 | |||
| 2625 | static ssize_t sony_nc_lowbatt_show(struct device *dev, | ||
| 2626 | struct device_attribute *attr, char *buffer) | ||
| 2627 | { | ||
| 2628 | unsigned int result; | ||
| 2629 | |||
| 2630 | if (sony_call_snc_handle(0x0121, 0x0200, &result)) | ||
| 2631 | return -EIO; | ||
| 2632 | |||
| 2633 | return snprintf(buffer, PAGE_SIZE, "%d\n", result & 1); | ||
| 2634 | } | ||
| 2635 | |||
| 2636 | static int sony_nc_lowbatt_setup(struct platform_device *pd) | ||
| 2637 | { | ||
| 2638 | unsigned int result; | ||
| 2639 | |||
| 2640 | lowbatt_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); | ||
| 2641 | if (!lowbatt_handle) | ||
| 2642 | return -ENOMEM; | ||
| 2643 | |||
| 2644 | sysfs_attr_init(&lowbatt_handle->attr); | ||
| 2645 | lowbatt_handle->attr.name = "lowbatt_hibernate"; | ||
| 2646 | lowbatt_handle->attr.mode = S_IRUGO | S_IWUSR; | ||
| 2647 | lowbatt_handle->show = sony_nc_lowbatt_show; | ||
| 2648 | lowbatt_handle->store = sony_nc_lowbatt_store; | ||
| 2649 | |||
| 2650 | result = device_create_file(&pd->dev, lowbatt_handle); | ||
| 2651 | if (result) { | ||
| 2652 | kfree(lowbatt_handle); | ||
| 2653 | lowbatt_handle = NULL; | ||
| 2654 | return result; | ||
| 2655 | } | ||
| 2656 | |||
| 2657 | return 0; | ||
| 2658 | } | ||
| 2659 | |||
| 2660 | static void sony_nc_lowbatt_cleanup(struct platform_device *pd) | ||
| 2661 | { | ||
| 2662 | if (lowbatt_handle) { | ||
| 2663 | device_remove_file(&pd->dev, lowbatt_handle); | ||
| 2664 | kfree(lowbatt_handle); | ||
| 2665 | lowbatt_handle = NULL; | ||
| 2666 | } | ||
| 2667 | } | ||
| 2668 | |||
| 2669 | /* fan speed function */ | ||
| 2670 | static struct device_attribute *fan_handle, *hsf_handle; | ||
| 2671 | |||
| 2672 | static ssize_t sony_nc_hsfan_store(struct device *dev, | ||
| 2673 | struct device_attribute *attr, | ||
| 2674 | const char *buffer, size_t count) | ||
| 2675 | { | ||
| 2676 | unsigned int result; | ||
| 2677 | unsigned long value; | ||
| 2678 | |||
| 2679 | if (count > 31) | ||
| 2680 | return -EINVAL; | ||
| 2681 | |||
| 2682 | if (kstrtoul(buffer, 10, &value) || value > 1) | ||
| 2683 | return -EINVAL; | ||
| 2684 | |||
| 2685 | if (sony_call_snc_handle(0x0149, value << 0x10 | 0x0200, &result)) | ||
| 2686 | return -EIO; | ||
| 2687 | |||
| 2688 | return count; | ||
| 2689 | } | ||
| 2690 | |||
| 2691 | static ssize_t sony_nc_hsfan_show(struct device *dev, | ||
| 2692 | struct device_attribute *attr, char *buffer) | ||
| 2693 | { | ||
| 2694 | unsigned int result; | ||
| 2695 | |||
| 2696 | if (sony_call_snc_handle(0x0149, 0x0100, &result)) | ||
| 2697 | return -EIO; | ||
| 2698 | |||
| 2699 | return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); | ||
| 2700 | } | ||
| 2701 | |||
| 2702 | static ssize_t sony_nc_fanspeed_show(struct device *dev, | ||
| 2703 | struct device_attribute *attr, char *buffer) | ||
| 2704 | { | ||
| 2705 | unsigned int result; | ||
| 2706 | |||
| 2707 | if (sony_call_snc_handle(0x0149, 0x0300, &result)) | ||
| 2708 | return -EIO; | ||
| 2709 | |||
| 2710 | return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff); | ||
| 2711 | } | ||
| 2712 | |||
| 2713 | static int sony_nc_fanspeed_setup(struct platform_device *pd) | ||
| 2714 | { | ||
| 2715 | unsigned int result; | ||
| 2716 | |||
| 2717 | fan_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); | ||
| 2718 | if (!fan_handle) | ||
| 2719 | return -ENOMEM; | ||
| 2720 | |||
| 2721 | hsf_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); | ||
| 2722 | if (!hsf_handle) { | ||
| 2723 | result = -ENOMEM; | ||
| 2724 | goto out_hsf_handle_alloc; | ||
| 2725 | } | ||
| 2726 | |||
| 2727 | sysfs_attr_init(&fan_handle->attr); | ||
| 2728 | fan_handle->attr.name = "fanspeed"; | ||
| 2729 | fan_handle->attr.mode = S_IRUGO; | ||
| 2730 | fan_handle->show = sony_nc_fanspeed_show; | ||
| 2731 | fan_handle->store = NULL; | ||
| 2732 | |||
| 2733 | sysfs_attr_init(&hsf_handle->attr); | ||
| 2734 | hsf_handle->attr.name = "fan_forced"; | ||
| 2735 | hsf_handle->attr.mode = S_IRUGO | S_IWUSR; | ||
| 2736 | hsf_handle->show = sony_nc_hsfan_show; | ||
| 2737 | hsf_handle->store = sony_nc_hsfan_store; | ||
| 2738 | |||
| 2739 | result = device_create_file(&pd->dev, fan_handle); | ||
| 2740 | if (result) | ||
| 2741 | goto out_fan_handle; | ||
| 2742 | |||
| 2743 | result = device_create_file(&pd->dev, hsf_handle); | ||
| 2744 | if (result) | ||
| 2745 | goto out_hsf_handle; | ||
| 2746 | |||
| 2747 | return 0; | ||
| 2748 | |||
| 2749 | out_hsf_handle: | ||
| 2750 | device_remove_file(&pd->dev, fan_handle); | ||
| 2751 | |||
| 2752 | out_fan_handle: | ||
| 2753 | kfree(hsf_handle); | ||
| 2754 | hsf_handle = NULL; | ||
| 2755 | |||
| 2756 | out_hsf_handle_alloc: | ||
| 2757 | kfree(fan_handle); | ||
| 2758 | fan_handle = NULL; | ||
| 2759 | return result; | ||
| 2760 | } | ||
| 2761 | |||
| 2762 | static void sony_nc_fanspeed_cleanup(struct platform_device *pd) | ||
| 2763 | { | ||
| 2764 | if (fan_handle) { | ||
| 2765 | device_remove_file(&pd->dev, fan_handle); | ||
| 2766 | kfree(fan_handle); | ||
| 2767 | fan_handle = NULL; | ||
| 2768 | } | ||
| 2769 | if (hsf_handle) { | ||
| 2770 | device_remove_file(&pd->dev, hsf_handle); | ||
| 2771 | kfree(hsf_handle); | ||
| 2772 | hsf_handle = NULL; | ||
| 2773 | } | ||
| 2774 | } | ||
| 2775 | |||
| 2776 | /* USB charge function */ | ||
| 2777 | static struct device_attribute *uc_handle; | ||
| 2778 | |||
| 2779 | static ssize_t sony_nc_usb_charge_store(struct device *dev, | ||
| 2780 | struct device_attribute *attr, | ||
| 2781 | const char *buffer, size_t count) | ||
| 2782 | { | ||
| 2783 | unsigned int result; | ||
| 2784 | unsigned long value; | ||
| 2785 | |||
| 2786 | if (count > 31) | ||
| 2787 | return -EINVAL; | ||
| 2788 | |||
| 2789 | if (kstrtoul(buffer, 10, &value) || value > 1) | ||
| 2790 | return -EINVAL; | ||
| 2791 | |||
| 2792 | if (sony_call_snc_handle(0x0155, value << 0x10 | 0x0100, &result)) | ||
| 2793 | return -EIO; | ||
| 2794 | |||
| 2795 | return count; | ||
| 2796 | } | ||
| 2797 | |||
| 2798 | static ssize_t sony_nc_usb_charge_show(struct device *dev, | ||
| 2799 | struct device_attribute *attr, char *buffer) | ||
| 2800 | { | ||
| 2801 | unsigned int result; | ||
| 2802 | |||
| 2803 | if (sony_call_snc_handle(0x0155, 0x0000, &result)) | ||
| 2804 | return -EIO; | ||
| 2805 | |||
| 2806 | return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); | ||
| 2807 | } | ||
| 2808 | |||
| 2809 | static int sony_nc_usb_charge_setup(struct platform_device *pd) | ||
| 2810 | { | ||
| 2811 | unsigned int result; | ||
| 2812 | |||
| 2813 | if (sony_call_snc_handle(0x0155, 0x0000, &result) || !(result & 0x01)) { | ||
| 2814 | /* some models advertise the handle but have no implementation | ||
| 2815 | * for it | ||
| 2816 | */ | ||
| 2817 | pr_info("No USB Charge capability found\n"); | ||
| 2818 | return 0; | ||
| 2819 | } | ||
| 2820 | |||
| 2821 | uc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); | ||
| 2822 | if (!uc_handle) | ||
| 2823 | return -ENOMEM; | ||
| 2824 | |||
| 2825 | sysfs_attr_init(&uc_handle->attr); | ||
| 2826 | uc_handle->attr.name = "usb_charge"; | ||
| 2827 | uc_handle->attr.mode = S_IRUGO | S_IWUSR; | ||
| 2828 | uc_handle->show = sony_nc_usb_charge_show; | ||
| 2829 | uc_handle->store = sony_nc_usb_charge_store; | ||
| 2830 | |||
| 2831 | result = device_create_file(&pd->dev, uc_handle); | ||
| 2832 | if (result) { | ||
| 2833 | kfree(uc_handle); | ||
| 2834 | uc_handle = NULL; | ||
| 2835 | return result; | ||
| 2836 | } | ||
| 2837 | |||
| 2838 | return 0; | ||
| 2839 | } | ||
| 2840 | |||
| 2841 | static void sony_nc_usb_charge_cleanup(struct platform_device *pd) | ||
| 2842 | { | ||
| 2843 | if (uc_handle) { | ||
| 2844 | device_remove_file(&pd->dev, uc_handle); | ||
| 2845 | kfree(uc_handle); | ||
| 2846 | uc_handle = NULL; | ||
| 2847 | } | ||
| 2848 | } | ||
| 2849 | |||
| 2850 | /* Panel ID function */ | ||
| 2851 | static struct device_attribute *panel_handle; | ||
| 2852 | |||
| 2853 | static ssize_t sony_nc_panelid_show(struct device *dev, | ||
| 2854 | struct device_attribute *attr, char *buffer) | ||
| 2855 | { | ||
| 2856 | unsigned int result; | ||
| 2857 | |||
| 2858 | if (sony_call_snc_handle(0x011D, 0x0000, &result)) | ||
| 2859 | return -EIO; | ||
| 2860 | |||
| 2861 | return snprintf(buffer, PAGE_SIZE, "%d\n", result); | ||
| 2862 | } | ||
| 2863 | |||
| 2864 | static int sony_nc_panelid_setup(struct platform_device *pd) | ||
| 2865 | { | ||
| 2866 | unsigned int result; | ||
| 2867 | |||
| 2868 | panel_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); | ||
| 2869 | if (!panel_handle) | ||
| 2870 | return -ENOMEM; | ||
| 2871 | |||
| 2872 | sysfs_attr_init(&panel_handle->attr); | ||
| 2873 | panel_handle->attr.name = "panel_id"; | ||
| 2874 | panel_handle->attr.mode = S_IRUGO; | ||
| 2875 | panel_handle->show = sony_nc_panelid_show; | ||
| 2876 | panel_handle->store = NULL; | ||
| 2877 | |||
| 2878 | result = device_create_file(&pd->dev, panel_handle); | ||
| 2879 | if (result) { | ||
| 2880 | kfree(panel_handle); | ||
| 2881 | panel_handle = NULL; | ||
| 2882 | return result; | ||
| 2883 | } | ||
| 2884 | |||
| 2885 | return 0; | ||
| 2886 | } | ||
| 2887 | |||
| 2888 | static void sony_nc_panelid_cleanup(struct platform_device *pd) | ||
| 2889 | { | ||
| 2890 | if (panel_handle) { | ||
| 2891 | device_remove_file(&pd->dev, panel_handle); | ||
| 2892 | kfree(panel_handle); | ||
| 2893 | panel_handle = NULL; | ||
| 2894 | } | ||
| 2895 | } | ||
| 2896 | |||
| 2897 | /* smart connect function */ | ||
| 2898 | static struct device_attribute *sc_handle; | ||
| 2899 | |||
| 2900 | static ssize_t sony_nc_smart_conn_store(struct device *dev, | ||
| 2901 | struct device_attribute *attr, | ||
| 2902 | const char *buffer, size_t count) | ||
| 2903 | { | ||
| 2904 | unsigned int result; | ||
| 2905 | unsigned long value; | ||
| 2906 | |||
| 2907 | if (count > 31) | ||
| 2908 | return -EINVAL; | ||
| 2909 | |||
| 2910 | if (kstrtoul(buffer, 10, &value) || value > 1) | ||
| 2911 | return -EINVAL; | ||
| 2912 | |||
| 2913 | if (sony_call_snc_handle(0x0168, value << 0x10, &result)) | ||
| 2914 | return -EIO; | ||
| 2915 | |||
| 2916 | return count; | ||
| 2917 | } | ||
| 2918 | |||
| 2919 | static int sony_nc_smart_conn_setup(struct platform_device *pd) | ||
| 2920 | { | ||
| 2921 | unsigned int result; | ||
| 2922 | |||
| 2923 | sc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); | ||
| 2924 | if (!sc_handle) | ||
| 2925 | return -ENOMEM; | ||
| 2926 | |||
| 2927 | sysfs_attr_init(&sc_handle->attr); | ||
| 2928 | sc_handle->attr.name = "smart_connect"; | ||
| 2929 | sc_handle->attr.mode = S_IWUSR; | ||
| 2930 | sc_handle->show = NULL; | ||
| 2931 | sc_handle->store = sony_nc_smart_conn_store; | ||
| 2932 | |||
| 2933 | result = device_create_file(&pd->dev, sc_handle); | ||
| 2934 | if (result) { | ||
| 2935 | kfree(sc_handle); | ||
| 2936 | sc_handle = NULL; | ||
| 2937 | return result; | ||
| 2938 | } | ||
| 2939 | |||
| 2940 | return 0; | ||
| 2941 | } | ||
| 2942 | |||
| 2943 | static void sony_nc_smart_conn_cleanup(struct platform_device *pd) | ||
| 2944 | { | ||
| 2945 | if (sc_handle) { | ||
| 2946 | device_remove_file(&pd->dev, sc_handle); | ||
| 2947 | kfree(sc_handle); | ||
| 2948 | sc_handle = NULL; | ||
| 2949 | } | ||
| 2950 | } | ||
| 2951 | |||
| 2527 | /* Touchpad enable/disable */ | 2952 | /* Touchpad enable/disable */ |
| 2528 | struct touchpad_control { | 2953 | struct touchpad_control { |
| 2529 | struct device_attribute attr; | 2954 | struct device_attribute attr; |
| @@ -2726,8 +3151,6 @@ static int sony_nc_add(struct acpi_device *device) | |||
| 2726 | int result = 0; | 3151 | int result = 0; |
| 2727 | struct sony_nc_value *item; | 3152 | struct sony_nc_value *item; |
| 2728 | 3153 | ||
| 2729 | pr_info("%s v%s\n", SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); | ||
| 2730 | |||
| 2731 | sony_nc_acpi_device = device; | 3154 | sony_nc_acpi_device = device; |
| 2732 | strcpy(acpi_device_class(device), "sony/hotkey"); | 3155 | strcpy(acpi_device_class(device), "sony/hotkey"); |
| 2733 | 3156 | ||
| @@ -2821,6 +3244,7 @@ static int sony_nc_add(struct acpi_device *device) | |||
| 2821 | } | 3244 | } |
| 2822 | } | 3245 | } |
| 2823 | 3246 | ||
| 3247 | pr_info("SNC setup done.\n"); | ||
| 2824 | return 0; | 3248 | return 0; |
| 2825 | 3249 | ||
| 2826 | out_sysfs: | 3250 | out_sysfs: |
| @@ -4259,8 +4683,6 @@ static int sony_pic_add(struct acpi_device *device) | |||
| 4259 | struct sony_pic_ioport *io, *tmp_io; | 4683 | struct sony_pic_ioport *io, *tmp_io; |
| 4260 | struct sony_pic_irq *irq, *tmp_irq; | 4684 | struct sony_pic_irq *irq, *tmp_irq; |
| 4261 | 4685 | ||
| 4262 | pr_info("%s v%s\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); | ||
| 4263 | |||
| 4264 | spic_dev.acpi_dev = device; | 4686 | spic_dev.acpi_dev = device; |
| 4265 | strcpy(acpi_device_class(device), "sony/hotkey"); | 4687 | strcpy(acpi_device_class(device), "sony/hotkey"); |
| 4266 | sony_pic_detect_device_type(&spic_dev); | 4688 | sony_pic_detect_device_type(&spic_dev); |
| @@ -4360,6 +4782,7 @@ static int sony_pic_add(struct acpi_device *device) | |||
| 4360 | if (result) | 4782 | if (result) |
| 4361 | goto err_remove_pf; | 4783 | goto err_remove_pf; |
| 4362 | 4784 | ||
| 4785 | pr_info("SPIC setup done.\n"); | ||
| 4363 | return 0; | 4786 | return 0; |
| 4364 | 4787 | ||
| 4365 | err_remove_pf: | 4788 | err_remove_pf: |
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 94bb6157c957..15e61c16736e 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c | |||
| @@ -2321,53 +2321,55 @@ static void hotkey_read_nvram(struct tp_nvram_state *n, const u32 m) | |||
| 2321 | } | 2321 | } |
| 2322 | } | 2322 | } |
| 2323 | 2323 | ||
| 2324 | static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, | ||
| 2325 | struct tp_nvram_state *newn, | ||
| 2326 | const u32 event_mask) | ||
| 2327 | { | ||
| 2328 | |||
| 2329 | #define TPACPI_COMPARE_KEY(__scancode, __member) \ | 2324 | #define TPACPI_COMPARE_KEY(__scancode, __member) \ |
| 2330 | do { \ | 2325 | do { \ |
| 2331 | if ((event_mask & (1 << __scancode)) && \ | 2326 | if ((event_mask & (1 << __scancode)) && \ |
| 2332 | oldn->__member != newn->__member) \ | 2327 | oldn->__member != newn->__member) \ |
| 2333 | tpacpi_hotkey_send_key(__scancode); \ | 2328 | tpacpi_hotkey_send_key(__scancode); \ |
| 2334 | } while (0) | 2329 | } while (0) |
| 2335 | 2330 | ||
| 2336 | #define TPACPI_MAY_SEND_KEY(__scancode) \ | 2331 | #define TPACPI_MAY_SEND_KEY(__scancode) \ |
| 2337 | do { \ | 2332 | do { \ |
| 2338 | if (event_mask & (1 << __scancode)) \ | 2333 | if (event_mask & (1 << __scancode)) \ |
| 2339 | tpacpi_hotkey_send_key(__scancode); \ | 2334 | tpacpi_hotkey_send_key(__scancode); \ |
| 2340 | } while (0) | 2335 | } while (0) |
| 2341 | 2336 | ||
| 2342 | void issue_volchange(const unsigned int oldvol, | 2337 | static void issue_volchange(const unsigned int oldvol, |
| 2343 | const unsigned int newvol) | 2338 | const unsigned int newvol, |
| 2344 | { | 2339 | const u32 event_mask) |
| 2345 | unsigned int i = oldvol; | 2340 | { |
| 2341 | unsigned int i = oldvol; | ||
| 2346 | 2342 | ||
| 2347 | while (i > newvol) { | 2343 | while (i > newvol) { |
| 2348 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); | 2344 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEDOWN); |
| 2349 | i--; | 2345 | i--; |
| 2350 | } | 2346 | } |
| 2351 | while (i < newvol) { | 2347 | while (i < newvol) { |
| 2352 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); | 2348 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); |
| 2353 | i++; | 2349 | i++; |
| 2354 | } | ||
| 2355 | } | 2350 | } |
| 2351 | } | ||
| 2356 | 2352 | ||
| 2357 | void issue_brightnesschange(const unsigned int oldbrt, | 2353 | static void issue_brightnesschange(const unsigned int oldbrt, |
| 2358 | const unsigned int newbrt) | 2354 | const unsigned int newbrt, |
| 2359 | { | 2355 | const u32 event_mask) |
| 2360 | unsigned int i = oldbrt; | 2356 | { |
| 2357 | unsigned int i = oldbrt; | ||
| 2361 | 2358 | ||
| 2362 | while (i > newbrt) { | 2359 | while (i > newbrt) { |
| 2363 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); | 2360 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNEND); |
| 2364 | i--; | 2361 | i--; |
| 2365 | } | ||
| 2366 | while (i < newbrt) { | ||
| 2367 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); | ||
| 2368 | i++; | ||
| 2369 | } | ||
| 2370 | } | 2362 | } |
| 2363 | while (i < newbrt) { | ||
| 2364 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_FNHOME); | ||
| 2365 | i++; | ||
| 2366 | } | ||
| 2367 | } | ||
| 2368 | |||
| 2369 | static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, | ||
| 2370 | struct tp_nvram_state *newn, | ||
| 2371 | const u32 event_mask) | ||
| 2372 | { | ||
| 2371 | 2373 | ||
| 2372 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); | 2374 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_THINKPAD, thinkpad_toggle); |
| 2373 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); | 2375 | TPACPI_COMPARE_KEY(TP_ACPI_HOTKEYSCAN_FNSPACE, zoom_toggle); |
| @@ -2402,7 +2404,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, | |||
| 2402 | oldn->volume_level != newn->volume_level) { | 2404 | oldn->volume_level != newn->volume_level) { |
| 2403 | /* recently muted, or repeated mute keypress, or | 2405 | /* recently muted, or repeated mute keypress, or |
| 2404 | * multiple presses ending in mute */ | 2406 | * multiple presses ending in mute */ |
| 2405 | issue_volchange(oldn->volume_level, newn->volume_level); | 2407 | issue_volchange(oldn->volume_level, newn->volume_level, |
| 2408 | event_mask); | ||
| 2406 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); | 2409 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_MUTE); |
| 2407 | } | 2410 | } |
| 2408 | } else { | 2411 | } else { |
| @@ -2412,7 +2415,8 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, | |||
| 2412 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); | 2415 | TPACPI_MAY_SEND_KEY(TP_ACPI_HOTKEYSCAN_VOLUMEUP); |
| 2413 | } | 2416 | } |
| 2414 | if (oldn->volume_level != newn->volume_level) { | 2417 | if (oldn->volume_level != newn->volume_level) { |
| 2415 | issue_volchange(oldn->volume_level, newn->volume_level); | 2418 | issue_volchange(oldn->volume_level, newn->volume_level, |
| 2419 | event_mask); | ||
| 2416 | } else if (oldn->volume_toggle != newn->volume_toggle) { | 2420 | } else if (oldn->volume_toggle != newn->volume_toggle) { |
| 2417 | /* repeated vol up/down keypress at end of scale ? */ | 2421 | /* repeated vol up/down keypress at end of scale ? */ |
| 2418 | if (newn->volume_level == 0) | 2422 | if (newn->volume_level == 0) |
| @@ -2425,7 +2429,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn, | |||
| 2425 | /* handle brightness */ | 2429 | /* handle brightness */ |
| 2426 | if (oldn->brightness_level != newn->brightness_level) { | 2430 | if (oldn->brightness_level != newn->brightness_level) { |
| 2427 | issue_brightnesschange(oldn->brightness_level, | 2431 | issue_brightnesschange(oldn->brightness_level, |
| 2428 | newn->brightness_level); | 2432 | newn->brightness_level, event_mask); |
| 2429 | } else if (oldn->brightness_toggle != newn->brightness_toggle) { | 2433 | } else if (oldn->brightness_toggle != newn->brightness_toggle) { |
| 2430 | /* repeated key presses that didn't change state */ | 2434 | /* repeated key presses that didn't change state */ |
| 2431 | if (newn->brightness_level == 0) | 2435 | if (newn->brightness_level == 0) |
| @@ -3437,6 +3441,106 @@ err_exit: | |||
| 3437 | return (res < 0)? res : 1; | 3441 | return (res < 0)? res : 1; |
| 3438 | } | 3442 | } |
| 3439 | 3443 | ||
| 3444 | /* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser | ||
| 3445 | * mode, Web conference mode, Function mode and Lay-flat mode. | ||
| 3446 | * We support Home mode and Function mode currently. | ||
| 3447 | * | ||
| 3448 | * Will consider support rest of modes in future. | ||
| 3449 | * | ||
| 3450 | */ | ||
| 3451 | enum ADAPTIVE_KEY_MODE { | ||
| 3452 | HOME_MODE, | ||
| 3453 | WEB_BROWSER_MODE, | ||
| 3454 | WEB_CONFERENCE_MODE, | ||
| 3455 | FUNCTION_MODE, | ||
| 3456 | LAYFLAT_MODE | ||
| 3457 | }; | ||
| 3458 | |||
| 3459 | const int adaptive_keyboard_modes[] = { | ||
| 3460 | HOME_MODE, | ||
| 3461 | /* WEB_BROWSER_MODE = 2, | ||
| 3462 | WEB_CONFERENCE_MODE = 3, */ | ||
| 3463 | FUNCTION_MODE | ||
| 3464 | }; | ||
| 3465 | |||
| 3466 | #define DFR_CHANGE_ROW 0x101 | ||
| 3467 | #define DFR_SHOW_QUICKVIEW_ROW 0x102 | ||
| 3468 | |||
| 3469 | /* press Fn key a while second, it will switch to Function Mode. Then | ||
| 3470 | * release Fn key, previous mode be restored. | ||
| 3471 | */ | ||
| 3472 | static bool adaptive_keyboard_mode_is_saved; | ||
| 3473 | static int adaptive_keyboard_prev_mode; | ||
| 3474 | |||
| 3475 | static int adaptive_keyboard_get_next_mode(int mode) | ||
| 3476 | { | ||
| 3477 | size_t i; | ||
| 3478 | size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1; | ||
| 3479 | |||
| 3480 | for (i = 0; i <= max_mode; i++) { | ||
| 3481 | if (adaptive_keyboard_modes[i] == mode) | ||
| 3482 | break; | ||
| 3483 | } | ||
| 3484 | |||
| 3485 | if (i >= max_mode) | ||
| 3486 | i = 0; | ||
| 3487 | else | ||
| 3488 | i++; | ||
| 3489 | |||
| 3490 | return adaptive_keyboard_modes[i]; | ||
| 3491 | } | ||
| 3492 | |||
| 3493 | static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) | ||
| 3494 | { | ||
| 3495 | u32 current_mode = 0; | ||
| 3496 | int new_mode = 0; | ||
| 3497 | |||
| 3498 | switch (scancode) { | ||
| 3499 | case DFR_CHANGE_ROW: | ||
| 3500 | if (adaptive_keyboard_mode_is_saved) { | ||
| 3501 | new_mode = adaptive_keyboard_prev_mode; | ||
| 3502 | adaptive_keyboard_mode_is_saved = false; | ||
| 3503 | } else { | ||
| 3504 | if (!acpi_evalf( | ||
| 3505 | hkey_handle, ¤t_mode, | ||
| 3506 | "GTRW", "dd", 0)) { | ||
| 3507 | pr_err("Cannot read adaptive keyboard mode\n"); | ||
| 3508 | return false; | ||
| 3509 | } else { | ||
| 3510 | new_mode = adaptive_keyboard_get_next_mode( | ||
| 3511 | current_mode); | ||
| 3512 | } | ||
| 3513 | } | ||
| 3514 | |||
| 3515 | if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) { | ||
| 3516 | pr_err("Cannot set adaptive keyboard mode\n"); | ||
| 3517 | return false; | ||
| 3518 | } | ||
| 3519 | |||
| 3520 | return true; | ||
| 3521 | |||
| 3522 | case DFR_SHOW_QUICKVIEW_ROW: | ||
| 3523 | if (!acpi_evalf(hkey_handle, | ||
| 3524 | &adaptive_keyboard_prev_mode, | ||
| 3525 | "GTRW", "dd", 0)) { | ||
| 3526 | pr_err("Cannot read adaptive keyboard mode\n"); | ||
| 3527 | return false; | ||
| 3528 | } else { | ||
| 3529 | adaptive_keyboard_mode_is_saved = true; | ||
| 3530 | |||
| 3531 | if (!acpi_evalf(hkey_handle, | ||
| 3532 | NULL, "STRW", "vd", FUNCTION_MODE)) { | ||
| 3533 | pr_err("Cannot set adaptive keyboard mode\n"); | ||
| 3534 | return false; | ||
| 3535 | } | ||
| 3536 | } | ||
| 3537 | return true; | ||
| 3538 | |||
| 3539 | default: | ||
| 3540 | return false; | ||
| 3541 | } | ||
| 3542 | } | ||
| 3543 | |||
| 3440 | static bool hotkey_notify_hotkey(const u32 hkey, | 3544 | static bool hotkey_notify_hotkey(const u32 hkey, |
| 3441 | bool *send_acpi_ev, | 3545 | bool *send_acpi_ev, |
| 3442 | bool *ignore_acpi_ev) | 3546 | bool *ignore_acpi_ev) |
| @@ -3456,6 +3560,8 @@ static bool hotkey_notify_hotkey(const u32 hkey, | |||
| 3456 | *ignore_acpi_ev = true; | 3560 | *ignore_acpi_ev = true; |
| 3457 | } | 3561 | } |
| 3458 | return true; | 3562 | return true; |
| 3563 | } else { | ||
| 3564 | return adaptive_keyboard_hotkey_notify_hotkey(scancode); | ||
| 3459 | } | 3565 | } |
| 3460 | return false; | 3566 | return false; |
| 3461 | } | 3567 | } |
| @@ -3728,13 +3834,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) | |||
| 3728 | 3834 | ||
| 3729 | static void hotkey_suspend(void) | 3835 | static void hotkey_suspend(void) |
| 3730 | { | 3836 | { |
| 3837 | int hkeyv; | ||
| 3838 | |||
| 3731 | /* Do these on suspend, we get the events on early resume! */ | 3839 | /* Do these on suspend, we get the events on early resume! */ |
| 3732 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; | 3840 | hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; |
| 3733 | hotkey_autosleep_ack = 0; | 3841 | hotkey_autosleep_ack = 0; |
| 3842 | |||
| 3843 | /* save previous mode of adaptive keyboard of X1 Carbon */ | ||
| 3844 | if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { | ||
| 3845 | if ((hkeyv >> 8) == 2) { | ||
| 3846 | if (!acpi_evalf(hkey_handle, | ||
| 3847 | &adaptive_keyboard_prev_mode, | ||
| 3848 | "GTRW", "dd", 0)) { | ||
| 3849 | pr_err("Cannot read adaptive keyboard mode.\n"); | ||
| 3850 | } | ||
| 3851 | } | ||
| 3852 | } | ||
| 3734 | } | 3853 | } |
| 3735 | 3854 | ||
| 3736 | static void hotkey_resume(void) | 3855 | static void hotkey_resume(void) |
| 3737 | { | 3856 | { |
| 3857 | int hkeyv; | ||
| 3858 | |||
| 3738 | tpacpi_disable_brightness_delay(); | 3859 | tpacpi_disable_brightness_delay(); |
| 3739 | 3860 | ||
| 3740 | if (hotkey_status_set(true) < 0 || | 3861 | if (hotkey_status_set(true) < 0 || |
| @@ -3747,6 +3868,18 @@ static void hotkey_resume(void) | |||
| 3747 | hotkey_wakeup_reason_notify_change(); | 3868 | hotkey_wakeup_reason_notify_change(); |
| 3748 | hotkey_wakeup_hotunplug_complete_notify_change(); | 3869 | hotkey_wakeup_hotunplug_complete_notify_change(); |
| 3749 | hotkey_poll_setup_safe(false); | 3870 | hotkey_poll_setup_safe(false); |
| 3871 | |||
| 3872 | /* restore previous mode of adapive keyboard of X1 Carbon */ | ||
| 3873 | if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { | ||
| 3874 | if ((hkeyv >> 8) == 2) { | ||
| 3875 | if (!acpi_evalf(hkey_handle, | ||
| 3876 | NULL, | ||
| 3877 | "STRW", "vd", | ||
| 3878 | adaptive_keyboard_prev_mode)) { | ||
| 3879 | pr_err("Cannot set adaptive keyboard mode.\n"); | ||
| 3880 | } | ||
| 3881 | } | ||
| 3882 | } | ||
| 3750 | } | 3883 | } |
| 3751 | 3884 | ||
| 3752 | /* procfs -------------------------------------------------------------- */ | 3885 | /* procfs -------------------------------------------------------------- */ |
| @@ -8447,9 +8580,21 @@ static void mute_led_exit(void) | |||
| 8447 | tpacpi_led_set(i, false); | 8580 | tpacpi_led_set(i, false); |
| 8448 | } | 8581 | } |
| 8449 | 8582 | ||
| 8583 | static void mute_led_resume(void) | ||
| 8584 | { | ||
| 8585 | int i; | ||
| 8586 | |||
| 8587 | for (i = 0; i < TPACPI_LED_MAX; i++) { | ||
| 8588 | struct tp_led_table *t = &led_tables[i]; | ||
| 8589 | if (t->state >= 0) | ||
| 8590 | mute_led_on_off(t, t->state); | ||
| 8591 | } | ||
| 8592 | } | ||
| 8593 | |||
| 8450 | static struct ibm_struct mute_led_driver_data = { | 8594 | static struct ibm_struct mute_led_driver_data = { |
| 8451 | .name = "mute_led", | 8595 | .name = "mute_led", |
| 8452 | .exit = mute_led_exit, | 8596 | .exit = mute_led_exit, |
| 8597 | .resume = mute_led_resume, | ||
| 8453 | }; | 8598 | }; |
| 8454 | 8599 | ||
| 8455 | /**************************************************************************** | 8600 | /**************************************************************************** |
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 90dd7645a9e5..46473ca7566b 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | * Copyright (C) 2002-2004 John Belmonte | 5 | * Copyright (C) 2002-2004 John Belmonte |
| 6 | * Copyright (C) 2008 Philip Langdale | 6 | * Copyright (C) 2008 Philip Langdale |
| 7 | * Copyright (C) 2010 Pierre Ducroquet | 7 | * Copyright (C) 2010 Pierre Ducroquet |
| 8 | * Copyright (C) 2014 Azael Avalos | ||
| 8 | * | 9 | * |
| 9 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
| 10 | * it under the terms of the GNU General Public License as published by | 11 | * it under the terms of the GNU General Public License as published by |
| @@ -37,7 +38,7 @@ | |||
| 37 | 38 | ||
| 38 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 39 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| 39 | 40 | ||
| 40 | #define TOSHIBA_ACPI_VERSION "0.19" | 41 | #define TOSHIBA_ACPI_VERSION "0.20" |
| 41 | #define PROC_INTERFACE_VERSION 1 | 42 | #define PROC_INTERFACE_VERSION 1 |
| 42 | 43 | ||
| 43 | #include <linux/kernel.h> | 44 | #include <linux/kernel.h> |
| @@ -77,6 +78,9 @@ MODULE_LICENSE("GPL"); | |||
| 77 | * However the ACPI methods seem to be incomplete in some areas (for | 78 | * However the ACPI methods seem to be incomplete in some areas (for |
| 78 | * example they allow setting, but not reading, the LCD brightness value), | 79 | * example they allow setting, but not reading, the LCD brightness value), |
| 79 | * so this is still useful. | 80 | * so this is still useful. |
| 81 | * | ||
| 82 | * SCI stands for "System Configuration Interface" which aim is to | ||
| 83 | * conceal differences in hardware between different models. | ||
| 80 | */ | 84 | */ |
| 81 | 85 | ||
| 82 | #define HCI_WORDS 6 | 86 | #define HCI_WORDS 6 |
| @@ -84,12 +88,23 @@ MODULE_LICENSE("GPL"); | |||
| 84 | /* operations */ | 88 | /* operations */ |
| 85 | #define HCI_SET 0xff00 | 89 | #define HCI_SET 0xff00 |
| 86 | #define HCI_GET 0xfe00 | 90 | #define HCI_GET 0xfe00 |
| 91 | #define SCI_OPEN 0xf100 | ||
| 92 | #define SCI_CLOSE 0xf200 | ||
| 93 | #define SCI_GET 0xf300 | ||
| 94 | #define SCI_SET 0xf400 | ||
| 87 | 95 | ||
| 88 | /* return codes */ | 96 | /* return codes */ |
| 89 | #define HCI_SUCCESS 0x0000 | 97 | #define HCI_SUCCESS 0x0000 |
| 90 | #define HCI_FAILURE 0x1000 | 98 | #define HCI_FAILURE 0x1000 |
| 91 | #define HCI_NOT_SUPPORTED 0x8000 | 99 | #define HCI_NOT_SUPPORTED 0x8000 |
| 92 | #define HCI_EMPTY 0x8c00 | 100 | #define HCI_EMPTY 0x8c00 |
| 101 | #define HCI_DATA_NOT_AVAILABLE 0x8d20 | ||
| 102 | #define HCI_NOT_INITIALIZED 0x8d50 | ||
| 103 | #define SCI_OPEN_CLOSE_OK 0x0044 | ||
| 104 | #define SCI_ALREADY_OPEN 0x8100 | ||
| 105 | #define SCI_NOT_OPENED 0x8200 | ||
| 106 | #define SCI_INPUT_DATA_ERROR 0x8300 | ||
| 107 | #define SCI_NOT_PRESENT 0x8600 | ||
| 93 | 108 | ||
| 94 | /* registers */ | 109 | /* registers */ |
| 95 | #define HCI_FAN 0x0004 | 110 | #define HCI_FAN 0x0004 |
| @@ -99,13 +114,22 @@ MODULE_LICENSE("GPL"); | |||
| 99 | #define HCI_HOTKEY_EVENT 0x001e | 114 | #define HCI_HOTKEY_EVENT 0x001e |
| 100 | #define HCI_LCD_BRIGHTNESS 0x002a | 115 | #define HCI_LCD_BRIGHTNESS 0x002a |
| 101 | #define HCI_WIRELESS 0x0056 | 116 | #define HCI_WIRELESS 0x0056 |
| 117 | #define HCI_ACCELEROMETER 0x006d | ||
| 118 | #define HCI_KBD_ILLUMINATION 0x0095 | ||
| 119 | #define HCI_ECO_MODE 0x0097 | ||
| 120 | #define HCI_ACCELEROMETER2 0x00a6 | ||
| 121 | #define SCI_ILLUMINATION 0x014e | ||
| 122 | #define SCI_KBD_ILLUM_STATUS 0x015c | ||
| 123 | #define SCI_TOUCHPAD 0x050e | ||
| 102 | 124 | ||
| 103 | /* field definitions */ | 125 | /* field definitions */ |
| 126 | #define HCI_ACCEL_MASK 0x7fff | ||
| 104 | #define HCI_HOTKEY_DISABLE 0x0b | 127 | #define HCI_HOTKEY_DISABLE 0x0b |
| 105 | #define HCI_HOTKEY_ENABLE 0x09 | 128 | #define HCI_HOTKEY_ENABLE 0x09 |
| 106 | #define HCI_LCD_BRIGHTNESS_BITS 3 | 129 | #define HCI_LCD_BRIGHTNESS_BITS 3 |
| 107 | #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) | 130 | #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) |
| 108 | #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) | 131 | #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) |
| 132 | #define HCI_MISC_SHIFT 0x10 | ||
| 109 | #define HCI_VIDEO_OUT_LCD 0x1 | 133 | #define HCI_VIDEO_OUT_LCD 0x1 |
| 110 | #define HCI_VIDEO_OUT_CRT 0x2 | 134 | #define HCI_VIDEO_OUT_CRT 0x2 |
| 111 | #define HCI_VIDEO_OUT_TV 0x4 | 135 | #define HCI_VIDEO_OUT_TV 0x4 |
| @@ -113,6 +137,8 @@ MODULE_LICENSE("GPL"); | |||
| 113 | #define HCI_WIRELESS_BT_PRESENT 0x0f | 137 | #define HCI_WIRELESS_BT_PRESENT 0x0f |
| 114 | #define HCI_WIRELESS_BT_ATTACH 0x40 | 138 | #define HCI_WIRELESS_BT_ATTACH 0x40 |
| 115 | #define HCI_WIRELESS_BT_POWER 0x80 | 139 | #define HCI_WIRELESS_BT_POWER 0x80 |
| 140 | #define SCI_KBD_MODE_FNZ 0x1 | ||
| 141 | #define SCI_KBD_MODE_AUTO 0x2 | ||
| 116 | 142 | ||
| 117 | struct toshiba_acpi_dev { | 143 | struct toshiba_acpi_dev { |
| 118 | struct acpi_device *acpi_dev; | 144 | struct acpi_device *acpi_dev; |
| @@ -122,10 +148,14 @@ struct toshiba_acpi_dev { | |||
| 122 | struct work_struct hotkey_work; | 148 | struct work_struct hotkey_work; |
| 123 | struct backlight_device *backlight_dev; | 149 | struct backlight_device *backlight_dev; |
| 124 | struct led_classdev led_dev; | 150 | struct led_classdev led_dev; |
| 151 | struct led_classdev kbd_led; | ||
| 152 | struct led_classdev eco_led; | ||
| 125 | 153 | ||
| 126 | int force_fan; | 154 | int force_fan; |
| 127 | int last_key_event; | 155 | int last_key_event; |
| 128 | int key_event_valid; | 156 | int key_event_valid; |
| 157 | int kbd_mode; | ||
| 158 | int kbd_time; | ||
| 129 | 159 | ||
| 130 | unsigned int illumination_supported:1; | 160 | unsigned int illumination_supported:1; |
| 131 | unsigned int video_supported:1; | 161 | unsigned int video_supported:1; |
| @@ -134,6 +164,12 @@ struct toshiba_acpi_dev { | |||
| 134 | unsigned int ntfy_supported:1; | 164 | unsigned int ntfy_supported:1; |
| 135 | unsigned int info_supported:1; | 165 | unsigned int info_supported:1; |
| 136 | unsigned int tr_backlight_supported:1; | 166 | unsigned int tr_backlight_supported:1; |
| 167 | unsigned int kbd_illum_supported:1; | ||
| 168 | unsigned int kbd_led_registered:1; | ||
| 169 | unsigned int touchpad_supported:1; | ||
| 170 | unsigned int eco_supported:1; | ||
| 171 | unsigned int accelerometer_supported:1; | ||
| 172 | unsigned int sysfs_created:1; | ||
| 137 | 173 | ||
| 138 | struct mutex mutex; | 174 | struct mutex mutex; |
| 139 | }; | 175 | }; |
| @@ -280,21 +316,94 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, | |||
| 280 | return status; | 316 | return status; |
| 281 | } | 317 | } |
| 282 | 318 | ||
| 319 | /* common sci tasks | ||
| 320 | */ | ||
| 321 | |||
| 322 | static int sci_open(struct toshiba_acpi_dev *dev) | ||
| 323 | { | ||
| 324 | u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; | ||
| 325 | u32 out[HCI_WORDS]; | ||
| 326 | acpi_status status; | ||
| 327 | |||
| 328 | status = hci_raw(dev, in, out); | ||
| 329 | if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { | ||
| 330 | pr_err("ACPI call to open SCI failed\n"); | ||
| 331 | return 0; | ||
| 332 | } | ||
| 333 | |||
| 334 | if (out[0] == SCI_OPEN_CLOSE_OK) { | ||
| 335 | return 1; | ||
| 336 | } else if (out[0] == SCI_ALREADY_OPEN) { | ||
| 337 | pr_info("Toshiba SCI already opened\n"); | ||
| 338 | return 1; | ||
| 339 | } else if (out[0] == SCI_NOT_PRESENT) { | ||
| 340 | pr_info("Toshiba SCI is not present\n"); | ||
| 341 | } | ||
| 342 | |||
| 343 | return 0; | ||
| 344 | } | ||
| 345 | |||
| 346 | static void sci_close(struct toshiba_acpi_dev *dev) | ||
| 347 | { | ||
| 348 | u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; | ||
| 349 | u32 out[HCI_WORDS]; | ||
| 350 | acpi_status status; | ||
| 351 | |||
| 352 | status = hci_raw(dev, in, out); | ||
| 353 | if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { | ||
| 354 | pr_err("ACPI call to close SCI failed\n"); | ||
| 355 | return; | ||
| 356 | } | ||
| 357 | |||
| 358 | if (out[0] == SCI_OPEN_CLOSE_OK) | ||
| 359 | return; | ||
| 360 | else if (out[0] == SCI_NOT_OPENED) | ||
| 361 | pr_info("Toshiba SCI not opened\n"); | ||
| 362 | else if (out[0] == SCI_NOT_PRESENT) | ||
| 363 | pr_info("Toshiba SCI is not present\n"); | ||
| 364 | } | ||
| 365 | |||
| 366 | static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg, | ||
| 367 | u32 *out1, u32 *result) | ||
| 368 | { | ||
| 369 | u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 }; | ||
| 370 | u32 out[HCI_WORDS]; | ||
| 371 | acpi_status status = hci_raw(dev, in, out); | ||
| 372 | *out1 = out[2]; | ||
| 373 | *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; | ||
| 374 | return status; | ||
| 375 | } | ||
| 376 | |||
| 377 | static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg, | ||
| 378 | u32 in1, u32 *result) | ||
| 379 | { | ||
| 380 | u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 }; | ||
| 381 | u32 out[HCI_WORDS]; | ||
| 382 | acpi_status status = hci_raw(dev, in, out); | ||
| 383 | *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; | ||
| 384 | return status; | ||
| 385 | } | ||
| 386 | |||
| 283 | /* Illumination support */ | 387 | /* Illumination support */ |
| 284 | static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) | 388 | static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) |
| 285 | { | 389 | { |
| 286 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | 390 | u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 }; |
| 287 | u32 out[HCI_WORDS]; | 391 | u32 out[HCI_WORDS]; |
| 288 | acpi_status status; | 392 | acpi_status status; |
| 289 | 393 | ||
| 290 | in[0] = 0xf100; | 394 | if (!sci_open(dev)) |
| 395 | return 0; | ||
| 396 | |||
| 291 | status = hci_raw(dev, in, out); | 397 | status = hci_raw(dev, in, out); |
| 292 | if (ACPI_FAILURE(status)) { | 398 | sci_close(dev); |
| 399 | if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { | ||
| 400 | pr_err("ACPI call to query Illumination support failed\n"); | ||
| 401 | return 0; | ||
| 402 | } else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) { | ||
| 293 | pr_info("Illumination device not available\n"); | 403 | pr_info("Illumination device not available\n"); |
| 294 | return 0; | 404 | return 0; |
| 295 | } | 405 | } |
| 296 | in[0] = 0xf400; | 406 | |
| 297 | status = hci_raw(dev, in, out); | ||
| 298 | return 1; | 407 | return 1; |
| 299 | } | 408 | } |
| 300 | 409 | ||
| @@ -303,82 +412,270 @@ static void toshiba_illumination_set(struct led_classdev *cdev, | |||
| 303 | { | 412 | { |
| 304 | struct toshiba_acpi_dev *dev = container_of(cdev, | 413 | struct toshiba_acpi_dev *dev = container_of(cdev, |
| 305 | struct toshiba_acpi_dev, led_dev); | 414 | struct toshiba_acpi_dev, led_dev); |
| 306 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | 415 | u32 state, result; |
| 307 | u32 out[HCI_WORDS]; | ||
| 308 | acpi_status status; | 416 | acpi_status status; |
| 309 | 417 | ||
| 310 | /* First request : initialize communication. */ | 418 | /* First request : initialize communication. */ |
| 311 | in[0] = 0xf100; | 419 | if (!sci_open(dev)) |
| 312 | status = hci_raw(dev, in, out); | 420 | return; |
| 421 | |||
| 422 | /* Switch the illumination on/off */ | ||
| 423 | state = brightness ? 1 : 0; | ||
| 424 | status = sci_write(dev, SCI_ILLUMINATION, state, &result); | ||
| 425 | sci_close(dev); | ||
| 313 | if (ACPI_FAILURE(status)) { | 426 | if (ACPI_FAILURE(status)) { |
| 314 | pr_info("Illumination device not available\n"); | 427 | pr_err("ACPI call for illumination failed\n"); |
| 428 | return; | ||
| 429 | } else if (result == HCI_NOT_SUPPORTED) { | ||
| 430 | pr_info("Illumination not supported\n"); | ||
| 315 | return; | 431 | return; |
| 316 | } | 432 | } |
| 433 | } | ||
| 317 | 434 | ||
| 318 | if (brightness) { | 435 | static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) |
| 319 | /* Switch the illumination on */ | 436 | { |
| 320 | in[0] = 0xf400; | 437 | struct toshiba_acpi_dev *dev = container_of(cdev, |
| 321 | in[1] = 0x14e; | 438 | struct toshiba_acpi_dev, led_dev); |
| 322 | in[2] = 1; | 439 | u32 state, result; |
| 323 | status = hci_raw(dev, in, out); | 440 | acpi_status status; |
| 324 | if (ACPI_FAILURE(status)) { | 441 | |
| 325 | pr_info("ACPI call for illumination failed\n"); | 442 | /*Â First request : initialize communication. */ |
| 326 | return; | 443 | if (!sci_open(dev)) |
| 327 | } | 444 | return LED_OFF; |
| 328 | } else { | 445 | |
| 329 | /* Switch the illumination off */ | 446 | /* Check the illumination */ |
| 330 | in[0] = 0xf400; | 447 | status = sci_read(dev, SCI_ILLUMINATION, &state, &result); |
| 331 | in[1] = 0x14e; | 448 | sci_close(dev); |
| 332 | in[2] = 0; | 449 | if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { |
| 333 | status = hci_raw(dev, in, out); | 450 | pr_err("ACPI call for illumination failed\n"); |
| 334 | if (ACPI_FAILURE(status)) { | 451 | return LED_OFF; |
| 335 | pr_info("ACPI call for illumination failed.\n"); | 452 | } else if (result == HCI_NOT_SUPPORTED) { |
| 336 | return; | 453 | pr_info("Illumination not supported\n"); |
| 337 | } | 454 | return LED_OFF; |
| 338 | } | 455 | } |
| 339 | 456 | ||
| 340 | /* Last request : close communication. */ | 457 | return state ? LED_FULL : LED_OFF; |
| 341 | in[0] = 0xf200; | ||
| 342 | in[1] = 0; | ||
| 343 | in[2] = 0; | ||
| 344 | hci_raw(dev, in, out); | ||
| 345 | } | 458 | } |
| 346 | 459 | ||
| 347 | static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | 460 | /* KBD Illumination */ |
| 461 | static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) | ||
| 462 | { | ||
| 463 | u32 result; | ||
| 464 | acpi_status status; | ||
| 465 | |||
| 466 | if (!sci_open(dev)) | ||
| 467 | return -EIO; | ||
| 468 | |||
| 469 | status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result); | ||
| 470 | sci_close(dev); | ||
| 471 | if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { | ||
| 472 | pr_err("ACPI call to set KBD backlight status failed\n"); | ||
| 473 | return -EIO; | ||
| 474 | } else if (result == HCI_NOT_SUPPORTED) { | ||
| 475 | pr_info("Keyboard backlight status not supported\n"); | ||
| 476 | return -ENODEV; | ||
| 477 | } | ||
| 478 | |||
| 479 | return 0; | ||
| 480 | } | ||
| 481 | |||
| 482 | static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time) | ||
| 483 | { | ||
| 484 | u32 result; | ||
| 485 | acpi_status status; | ||
| 486 | |||
| 487 | if (!sci_open(dev)) | ||
| 488 | return -EIO; | ||
| 489 | |||
| 490 | status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result); | ||
| 491 | sci_close(dev); | ||
| 492 | if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { | ||
| 493 | pr_err("ACPI call to get KBD backlight status failed\n"); | ||
| 494 | return -EIO; | ||
| 495 | } else if (result == HCI_NOT_SUPPORTED) { | ||
| 496 | pr_info("Keyboard backlight status not supported\n"); | ||
| 497 | return -ENODEV; | ||
| 498 | } | ||
| 499 | |||
| 500 | return 0; | ||
| 501 | } | ||
| 502 | |||
| 503 | static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev) | ||
| 348 | { | 504 | { |
| 349 | struct toshiba_acpi_dev *dev = container_of(cdev, | 505 | struct toshiba_acpi_dev *dev = container_of(cdev, |
| 350 | struct toshiba_acpi_dev, led_dev); | 506 | struct toshiba_acpi_dev, kbd_led); |
| 351 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | 507 | u32 state, result; |
| 508 | acpi_status status; | ||
| 509 | |||
| 510 | /* Check the keyboard backlight state */ | ||
| 511 | status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result); | ||
| 512 | if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { | ||
| 513 | pr_err("ACPI call to get the keyboard backlight failed\n"); | ||
| 514 | return LED_OFF; | ||
| 515 | } else if (result == HCI_NOT_SUPPORTED) { | ||
| 516 | pr_info("Keyboard backlight not supported\n"); | ||
| 517 | return LED_OFF; | ||
| 518 | } | ||
| 519 | |||
| 520 | return state ? LED_FULL : LED_OFF; | ||
| 521 | } | ||
| 522 | |||
| 523 | static void toshiba_kbd_backlight_set(struct led_classdev *cdev, | ||
| 524 | enum led_brightness brightness) | ||
| 525 | { | ||
| 526 | struct toshiba_acpi_dev *dev = container_of(cdev, | ||
| 527 | struct toshiba_acpi_dev, kbd_led); | ||
| 528 | u32 state, result; | ||
| 529 | acpi_status status; | ||
| 530 | |||
| 531 | /* Set the keyboard backlight state */ | ||
| 532 | state = brightness ? 1 : 0; | ||
| 533 | status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result); | ||
| 534 | if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { | ||
| 535 | pr_err("ACPI call to set KBD Illumination mode failed\n"); | ||
| 536 | return; | ||
| 537 | } else if (result == HCI_NOT_SUPPORTED) { | ||
| 538 | pr_info("Keyboard backlight not supported\n"); | ||
| 539 | return; | ||
| 540 | } | ||
| 541 | } | ||
| 542 | |||
| 543 | /* TouchPad support */ | ||
| 544 | static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state) | ||
| 545 | { | ||
| 546 | u32 result; | ||
| 547 | acpi_status status; | ||
| 548 | |||
| 549 | if (!sci_open(dev)) | ||
| 550 | return -EIO; | ||
| 551 | |||
| 552 | status = sci_write(dev, SCI_TOUCHPAD, state, &result); | ||
| 553 | sci_close(dev); | ||
| 554 | if (ACPI_FAILURE(status)) { | ||
| 555 | pr_err("ACPI call to set the touchpad failed\n"); | ||
| 556 | return -EIO; | ||
| 557 | } else if (result == HCI_NOT_SUPPORTED) { | ||
| 558 | return -ENODEV; | ||
| 559 | } | ||
| 560 | |||
| 561 | return 0; | ||
| 562 | } | ||
| 563 | |||
| 564 | static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) | ||
| 565 | { | ||
| 566 | u32 result; | ||
| 567 | acpi_status status; | ||
| 568 | |||
| 569 | if (!sci_open(dev)) | ||
| 570 | return -EIO; | ||
| 571 | |||
| 572 | status = sci_read(dev, SCI_TOUCHPAD, state, &result); | ||
| 573 | sci_close(dev); | ||
| 574 | if (ACPI_FAILURE(status)) { | ||
| 575 | pr_err("ACPI call to query the touchpad failed\n"); | ||
| 576 | return -EIO; | ||
| 577 | } else if (result == HCI_NOT_SUPPORTED) { | ||
| 578 | return -ENODEV; | ||
| 579 | } | ||
| 580 | |||
| 581 | return 0; | ||
| 582 | } | ||
| 583 | |||
| 584 | /* Eco Mode support */ | ||
| 585 | static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) | ||
| 586 | { | ||
| 587 | acpi_status status; | ||
| 588 | u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; | ||
| 589 | u32 out[HCI_WORDS]; | ||
| 590 | |||
| 591 | status = hci_raw(dev, in, out); | ||
| 592 | if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { | ||
| 593 | pr_info("ACPI call to get ECO led failed\n"); | ||
| 594 | return 0; | ||
| 595 | } | ||
| 596 | |||
| 597 | return 1; | ||
| 598 | } | ||
| 599 | |||
| 600 | static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev) | ||
| 601 | { | ||
| 602 | struct toshiba_acpi_dev *dev = container_of(cdev, | ||
| 603 | struct toshiba_acpi_dev, eco_led); | ||
| 604 | u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; | ||
| 352 | u32 out[HCI_WORDS]; | 605 | u32 out[HCI_WORDS]; |
| 353 | acpi_status status; | 606 | acpi_status status; |
| 354 | enum led_brightness result; | ||
| 355 | 607 | ||
| 356 | /*Â First request : initialize communication. */ | ||
| 357 | in[0] = 0xf100; | ||
| 358 | status = hci_raw(dev, in, out); | 608 | status = hci_raw(dev, in, out); |
| 359 | if (ACPI_FAILURE(status)) { | 609 | if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { |
| 360 | pr_info("Illumination device not available\n"); | 610 | pr_err("ACPI call to get ECO led failed\n"); |
| 361 | return LED_OFF; | 611 | return LED_OFF; |
| 362 | } | 612 | } |
| 363 | 613 | ||
| 364 | /* Check the illumination */ | 614 | return out[2] ? LED_FULL : LED_OFF; |
| 365 | in[0] = 0xf300; | 615 | } |
| 366 | in[1] = 0x14e; | 616 | |
| 617 | static void toshiba_eco_mode_set_status(struct led_classdev *cdev, | ||
| 618 | enum led_brightness brightness) | ||
| 619 | { | ||
| 620 | struct toshiba_acpi_dev *dev = container_of(cdev, | ||
| 621 | struct toshiba_acpi_dev, eco_led); | ||
| 622 | u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 }; | ||
| 623 | u32 out[HCI_WORDS]; | ||
| 624 | acpi_status status; | ||
| 625 | |||
| 626 | /* Switch the Eco Mode led on/off */ | ||
| 627 | in[2] = (brightness) ? 1 : 0; | ||
| 367 | status = hci_raw(dev, in, out); | 628 | status = hci_raw(dev, in, out); |
| 368 | if (ACPI_FAILURE(status)) { | 629 | if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { |
| 369 | pr_info("ACPI call for illumination failed.\n"); | 630 | pr_err("ACPI call to set ECO led failed\n"); |
| 370 | return LED_OFF; | 631 | return; |
| 371 | } | 632 | } |
| 633 | } | ||
| 372 | 634 | ||
| 373 | result = out[2] ? LED_FULL : LED_OFF; | 635 | /* Accelerometer support */ |
| 636 | static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev) | ||
| 637 | { | ||
| 638 | u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 }; | ||
| 639 | u32 out[HCI_WORDS]; | ||
| 640 | acpi_status status; | ||
| 641 | |||
| 642 | /* Check if the accelerometer call exists, | ||
| 643 | * this call also serves as initialization | ||
| 644 | */ | ||
| 645 | status = hci_raw(dev, in, out); | ||
| 646 | if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { | ||
| 647 | pr_err("ACPI call to query the accelerometer failed\n"); | ||
| 648 | return -EIO; | ||
| 649 | } else if (out[0] == HCI_DATA_NOT_AVAILABLE || | ||
| 650 | out[0] == HCI_NOT_INITIALIZED) { | ||
| 651 | pr_err("Accelerometer not initialized\n"); | ||
| 652 | return -EIO; | ||
| 653 | } else if (out[0] == HCI_NOT_SUPPORTED) { | ||
| 654 | pr_info("Accelerometer not supported\n"); | ||
| 655 | return -ENODEV; | ||
| 656 | } | ||
| 657 | |||
| 658 | return 0; | ||
| 659 | } | ||
| 660 | |||
| 661 | static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev, | ||
| 662 | u32 *xy, u32 *z) | ||
| 663 | { | ||
| 664 | u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 }; | ||
| 665 | u32 out[HCI_WORDS]; | ||
| 666 | acpi_status status; | ||
| 667 | |||
| 668 | /* Check the Accelerometer status */ | ||
| 669 | status = hci_raw(dev, in, out); | ||
| 670 | if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { | ||
| 671 | pr_err("ACPI call to query the accelerometer failed\n"); | ||
| 672 | return -EIO; | ||
| 673 | } | ||
| 374 | 674 | ||
| 375 | /* Last request : close communication. */ | 675 | *xy = out[2]; |
| 376 | in[0] = 0xf200; | 676 | *z = out[4]; |
| 377 | in[1] = 0; | ||
| 378 | in[2] = 0; | ||
| 379 | hci_raw(dev, in, out); | ||
| 380 | 677 | ||
| 381 | return result; | 678 | return 0; |
| 382 | } | 679 | } |
| 383 | 680 | ||
| 384 | /* Bluetooth rfkill handlers */ | 681 | /* Bluetooth rfkill handlers */ |
| @@ -904,6 +1201,177 @@ static const struct backlight_ops toshiba_backlight_data = { | |||
| 904 | .update_status = set_lcd_status, | 1201 | .update_status = set_lcd_status, |
| 905 | }; | 1202 | }; |
| 906 | 1203 | ||
| 1204 | /* | ||
| 1205 | * Sysfs files | ||
| 1206 | */ | ||
| 1207 | |||
| 1208 | static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, | ||
| 1209 | struct device_attribute *attr, | ||
| 1210 | const char *buf, size_t count) | ||
| 1211 | { | ||
| 1212 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
| 1213 | int mode = -1; | ||
| 1214 | int time = -1; | ||
| 1215 | |||
| 1216 | if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1)) | ||
| 1217 | return -EINVAL; | ||
| 1218 | |||
| 1219 | /* Set the Keyboard Backlight Mode where: | ||
| 1220 | * Mode - Auto (2) | FN-Z (1) | ||
| 1221 | * Auto - KBD backlight turns off automatically in given time | ||
| 1222 | * FN-Z - KBD backlight "toggles" when hotkey pressed | ||
| 1223 | */ | ||
| 1224 | if (mode != -1 && toshiba->kbd_mode != mode) { | ||
| 1225 | time = toshiba->kbd_time << HCI_MISC_SHIFT; | ||
| 1226 | time = time + toshiba->kbd_mode; | ||
| 1227 | if (toshiba_kbd_illum_status_set(toshiba, time) < 0) | ||
| 1228 | return -EIO; | ||
| 1229 | toshiba->kbd_mode = mode; | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | return count; | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | static ssize_t toshiba_kbd_bl_mode_show(struct device *dev, | ||
| 1236 | struct device_attribute *attr, | ||
| 1237 | char *buf) | ||
| 1238 | { | ||
| 1239 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
| 1240 | u32 time; | ||
| 1241 | |||
| 1242 | if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) | ||
| 1243 | return -EIO; | ||
| 1244 | |||
| 1245 | return sprintf(buf, "%i\n", time & 0x07); | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev, | ||
| 1249 | struct device_attribute *attr, | ||
| 1250 | const char *buf, size_t count) | ||
| 1251 | { | ||
| 1252 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
| 1253 | int time = -1; | ||
| 1254 | |||
| 1255 | if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60)) | ||
| 1256 | return -EINVAL; | ||
| 1257 | |||
| 1258 | /* Set the Keyboard Backlight Timeout: 0-60 seconds */ | ||
| 1259 | if (time != -1 && toshiba->kbd_time != time) { | ||
| 1260 | time = time << HCI_MISC_SHIFT; | ||
| 1261 | time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ? | ||
| 1262 | time + 1 : time + 2; | ||
| 1263 | if (toshiba_kbd_illum_status_set(toshiba, time) < 0) | ||
| 1264 | return -EIO; | ||
| 1265 | toshiba->kbd_time = time >> HCI_MISC_SHIFT; | ||
| 1266 | } | ||
| 1267 | |||
| 1268 | return count; | ||
| 1269 | } | ||
| 1270 | |||
| 1271 | static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev, | ||
| 1272 | struct device_attribute *attr, | ||
| 1273 | char *buf) | ||
| 1274 | { | ||
| 1275 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
| 1276 | u32 time; | ||
| 1277 | |||
| 1278 | if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) | ||
| 1279 | return -EIO; | ||
| 1280 | |||
| 1281 | return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT); | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | static ssize_t toshiba_touchpad_store(struct device *dev, | ||
| 1285 | struct device_attribute *attr, | ||
| 1286 | const char *buf, size_t count) | ||
| 1287 | { | ||
| 1288 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
| 1289 | int state; | ||
| 1290 | |||
| 1291 | /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */ | ||
| 1292 | if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) { | ||
| 1293 | if (toshiba_touchpad_set(toshiba, state) < 0) | ||
| 1294 | return -EIO; | ||
| 1295 | } | ||
| 1296 | |||
| 1297 | return count; | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | static ssize_t toshiba_touchpad_show(struct device *dev, | ||
| 1301 | struct device_attribute *attr, char *buf) | ||
| 1302 | { | ||
| 1303 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
| 1304 | u32 state; | ||
| 1305 | int ret; | ||
| 1306 | |||
| 1307 | ret = toshiba_touchpad_get(toshiba, &state); | ||
| 1308 | if (ret < 0) | ||
| 1309 | return ret; | ||
| 1310 | |||
| 1311 | return sprintf(buf, "%i\n", state); | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | static ssize_t toshiba_position_show(struct device *dev, | ||
| 1315 | struct device_attribute *attr, char *buf) | ||
| 1316 | { | ||
| 1317 | struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); | ||
| 1318 | u32 xyval, zval, tmp; | ||
| 1319 | u16 x, y, z; | ||
| 1320 | int ret; | ||
| 1321 | |||
| 1322 | xyval = zval = 0; | ||
| 1323 | ret = toshiba_accelerometer_get(toshiba, &xyval, &zval); | ||
| 1324 | if (ret < 0) | ||
| 1325 | return ret; | ||
| 1326 | |||
| 1327 | x = xyval & HCI_ACCEL_MASK; | ||
| 1328 | tmp = xyval >> HCI_MISC_SHIFT; | ||
| 1329 | y = tmp & HCI_ACCEL_MASK; | ||
| 1330 | z = zval & HCI_ACCEL_MASK; | ||
| 1331 | |||
| 1332 | return sprintf(buf, "%d %d %d\n", x, y, z); | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, | ||
| 1336 | toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); | ||
| 1337 | static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, | ||
| 1338 | toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); | ||
| 1339 | static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR, | ||
| 1340 | toshiba_touchpad_show, toshiba_touchpad_store); | ||
| 1341 | static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL); | ||
| 1342 | |||
| 1343 | static struct attribute *toshiba_attributes[] = { | ||
| 1344 | &dev_attr_kbd_backlight_mode.attr, | ||
| 1345 | &dev_attr_kbd_backlight_timeout.attr, | ||
| 1346 | &dev_attr_touchpad.attr, | ||
| 1347 | &dev_attr_position.attr, | ||
| 1348 | NULL, | ||
| 1349 | }; | ||
| 1350 | |||
| 1351 | static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, | ||
| 1352 | struct attribute *attr, int idx) | ||
| 1353 | { | ||
| 1354 | struct device *dev = container_of(kobj, struct device, kobj); | ||
| 1355 | struct toshiba_acpi_dev *drv = dev_get_drvdata(dev); | ||
| 1356 | bool exists = true; | ||
| 1357 | |||
| 1358 | if (attr == &dev_attr_kbd_backlight_mode.attr) | ||
| 1359 | exists = (drv->kbd_illum_supported) ? true : false; | ||
| 1360 | else if (attr == &dev_attr_kbd_backlight_timeout.attr) | ||
| 1361 | exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; | ||
| 1362 | else if (attr == &dev_attr_touchpad.attr) | ||
| 1363 | exists = (drv->touchpad_supported) ? true : false; | ||
| 1364 | else if (attr == &dev_attr_position.attr) | ||
| 1365 | exists = (drv->accelerometer_supported) ? true : false; | ||
| 1366 | |||
| 1367 | return exists ? attr->mode : 0; | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | static struct attribute_group toshiba_attr_group = { | ||
| 1371 | .is_visible = toshiba_sysfs_is_visible, | ||
| 1372 | .attrs = toshiba_attributes, | ||
| 1373 | }; | ||
| 1374 | |||
| 907 | static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, | 1375 | static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, |
| 908 | struct serio *port) | 1376 | struct serio *port) |
| 909 | { | 1377 | { |
| @@ -1106,6 +1574,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) | |||
| 1106 | 1574 | ||
| 1107 | remove_toshiba_proc_entries(dev); | 1575 | remove_toshiba_proc_entries(dev); |
| 1108 | 1576 | ||
| 1577 | if (dev->sysfs_created) | ||
| 1578 | sysfs_remove_group(&dev->acpi_dev->dev.kobj, | ||
| 1579 | &toshiba_attr_group); | ||
| 1580 | |||
| 1109 | if (dev->ntfy_supported) { | 1581 | if (dev->ntfy_supported) { |
| 1110 | i8042_remove_filter(toshiba_acpi_i8042_filter); | 1582 | i8042_remove_filter(toshiba_acpi_i8042_filter); |
| 1111 | cancel_work_sync(&dev->hotkey_work); | 1583 | cancel_work_sync(&dev->hotkey_work); |
| @@ -1127,6 +1599,12 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) | |||
| 1127 | if (dev->illumination_supported) | 1599 | if (dev->illumination_supported) |
| 1128 | led_classdev_unregister(&dev->led_dev); | 1600 | led_classdev_unregister(&dev->led_dev); |
| 1129 | 1601 | ||
| 1602 | if (dev->kbd_led_registered) | ||
| 1603 | led_classdev_unregister(&dev->kbd_led); | ||
| 1604 | |||
| 1605 | if (dev->eco_supported) | ||
| 1606 | led_classdev_unregister(&dev->eco_led); | ||
| 1607 | |||
| 1130 | if (toshiba_acpi) | 1608 | if (toshiba_acpi) |
| 1131 | toshiba_acpi = NULL; | 1609 | toshiba_acpi = NULL; |
| 1132 | 1610 | ||
| @@ -1172,6 +1650,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
| 1172 | dev->acpi_dev = acpi_dev; | 1650 | dev->acpi_dev = acpi_dev; |
| 1173 | dev->method_hci = hci_method; | 1651 | dev->method_hci = hci_method; |
| 1174 | acpi_dev->driver_data = dev; | 1652 | acpi_dev->driver_data = dev; |
| 1653 | dev_set_drvdata(&acpi_dev->dev, dev); | ||
| 1175 | 1654 | ||
| 1176 | if (toshiba_acpi_setup_keyboard(dev)) | 1655 | if (toshiba_acpi_setup_keyboard(dev)) |
| 1177 | pr_info("Unable to activate hotkeys\n"); | 1656 | pr_info("Unable to activate hotkeys\n"); |
| @@ -1212,6 +1691,40 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
| 1212 | dev->illumination_supported = 1; | 1691 | dev->illumination_supported = 1; |
| 1213 | } | 1692 | } |
| 1214 | 1693 | ||
| 1694 | if (toshiba_eco_mode_available(dev)) { | ||
| 1695 | dev->eco_led.name = "toshiba::eco_mode"; | ||
| 1696 | dev->eco_led.max_brightness = 1; | ||
| 1697 | dev->eco_led.brightness_set = toshiba_eco_mode_set_status; | ||
| 1698 | dev->eco_led.brightness_get = toshiba_eco_mode_get_status; | ||
| 1699 | if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led)) | ||
| 1700 | dev->eco_supported = 1; | ||
| 1701 | } | ||
| 1702 | |||
| 1703 | ret = toshiba_kbd_illum_status_get(dev, &dummy); | ||
| 1704 | if (!ret) { | ||
| 1705 | dev->kbd_time = dummy >> HCI_MISC_SHIFT; | ||
| 1706 | dev->kbd_mode = dummy & 0x07; | ||
| 1707 | } | ||
| 1708 | dev->kbd_illum_supported = !ret; | ||
| 1709 | /* | ||
| 1710 | * Only register the LED if KBD illumination is supported | ||
| 1711 | * and the keyboard backlight operation mode is set to FN-Z | ||
| 1712 | */ | ||
| 1713 | if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) { | ||
| 1714 | dev->kbd_led.name = "toshiba::kbd_backlight"; | ||
| 1715 | dev->kbd_led.max_brightness = 1; | ||
| 1716 | dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; | ||
| 1717 | dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; | ||
| 1718 | if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led)) | ||
| 1719 | dev->kbd_led_registered = 1; | ||
| 1720 | } | ||
| 1721 | |||
| 1722 | ret = toshiba_touchpad_get(dev, &dummy); | ||
| 1723 | dev->touchpad_supported = !ret; | ||
| 1724 | |||
| 1725 | ret = toshiba_accelerometer_supported(dev); | ||
| 1726 | dev->accelerometer_supported = !ret; | ||
| 1727 | |||
| 1215 | /* Determine whether or not BIOS supports fan and video interfaces */ | 1728 | /* Determine whether or not BIOS supports fan and video interfaces */ |
| 1216 | 1729 | ||
| 1217 | ret = get_video_status(dev, &dummy); | 1730 | ret = get_video_status(dev, &dummy); |
| @@ -1220,6 +1733,14 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) | |||
| 1220 | ret = get_fan_status(dev, &dummy); | 1733 | ret = get_fan_status(dev, &dummy); |
| 1221 | dev->fan_supported = !ret; | 1734 | dev->fan_supported = !ret; |
| 1222 | 1735 | ||
| 1736 | ret = sysfs_create_group(&dev->acpi_dev->dev.kobj, | ||
| 1737 | &toshiba_attr_group); | ||
| 1738 | if (ret) { | ||
| 1739 | dev->sysfs_created = 0; | ||
| 1740 | goto error; | ||
| 1741 | } | ||
| 1742 | dev->sysfs_created = !ret; | ||
| 1743 | |||
| 1223 | create_toshiba_proc_entries(dev); | 1744 | create_toshiba_proc_entries(dev); |
| 1224 | 1745 | ||
| 1225 | toshiba_acpi = dev; | 1746 | toshiba_acpi = dev; |
