diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-10 12:43:56 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-04-10 12:43:56 -0400 |
commit | 4ba85265790ba3681deeaf73f018c0eb829a7341 (patch) | |
tree | d94a9198add02cd11cac825bcad0e2858973d422 | |
parent | dd76a786af1f09e9122e150d30156e094e2a94b4 (diff) | |
parent | 562c7cec1e92be85ade6f65fff107651e10ee2ed (diff) |
Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86
Pull x86 platform driver updates from Matthew Garrett:
"Support for the new keyboard features on the Thinkpad Carbon, a bunch
of updates for the Sony and Toshiba drivers, a new driver for upcoming
Alienware hardware and a few misc fixes. There's a couple of patches
that got Acked today but aren't invasive, so I'll send a further PR
for them next week"
* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86: (28 commits)
alienware-wmi: cover some scenarios where memory allocations would fail
Add WMI driver for controlling AlienFX features on some Alienware products
fujitsu-tablet: add support for Lifebook T901 and T902
x86, platform: Make HP_WIRELESS option text more descriptive
x86, acpi: LLVMLinux: Remove nested functions from Thinkpad ACPI
save and restore adaptive keyboard mode for suspend and,resume
support Thinkpad X1 Carbon 2nd generation's adaptive keyboard
toshiba_acpi: Fix whitespace
toshiba_acpi: Update version and copyright info
toshiba_acpi: Add accelerometer support
toshiba_acpi: Add ECO mode led support
toshiba_acpi: Add touchpad enable/disable support-
toshiba_acpi: Add keyboard backlight support
toshiba_acpi: Adapt Illumination code to use SCI
toshiba_acpi: Add System Configuration Interface
thinkpad_acpi: Fix inconsistent mute LED after resume
sonypi: Simplify dependencies
Revert "X86 platform: New BayTrail IOSF-SB MBI driver"
sony-laptop: remove useless sony-laptop versioning
sony-laptop: add smart connect control function
...
-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; |