Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

samsung-laptop: enable better lid handling

Some Samsung laptops with SABI3 delay the sleep for 10 seconds after
the lid is closed and do not wake up from sleep after the lid is opened.
A SABI command is needed to enable the better behavior.

Command = 0x6e, d0 = 0x81 enables this behavior. Returns d0 = 0x01.
Command = 0x6e, d0 = 0x80 disables this behavior. Returns d0 = 0x00.

Command = 0x6d and any d0 queries the state. This returns:
d0 = 0x00000*01, d1 = 0x00, d2 = 0x00, d3 = 0x0* when it is enabled.
d0 = 0x00000*00, d1 = 0x00, d2 = 0x00, d3 = 0x0* when it is disabled.
Where * is 0 - laptop has never slept or hibernated after switch on,
1 - laptop has hibernated just before,
2 - laptop has slept just before.

Patch addresses bug https://bugzilla.kernel.org/show_bug.cgi?id=75901 .
It adds a sysfs attribute lid_handling with a description and also an
addition to the quirks structure to enable the mode by default.

A user with another laptop in the bug report says that "power button has
to be pressed twice to wake the machine" when he or she enabled the mode
manually using the SABI command. Therefore, it is enabled by default
only for the single laptop that I have tested.

Signed-off-by: Julijonas Kikutis <julijonas.kikutis@gmail.com>
Signed-off-by: Darren Hart <dvhart@linux.intel.com>

authored by

Julijonas Kikutis and committed by
Darren Hart
b0dcaf4f fa465739

+127 -1
+8
Documentation/ABI/testing/sysfs-driver-samsung-laptop
··· 35 35 Description: Use your USB ports to charge devices, even 36 36 when your laptop is powered off. 37 37 1 means enabled, 0 means disabled. 38 + 39 + What: /sys/devices/platform/samsung/lid_handling 40 + Date: December 11, 2014 41 + KernelVersion: 3.19 42 + Contact: Julijonas Kikutis <julijonas.kikutis@gmail.com> 43 + Description: Some Samsung laptops handle lid closing quicker and 44 + only handle lid opening with this mode enabled. 45 + 1 means enabled, 0 means disabled.
+119 -1
drivers/platform/x86/samsung-laptop.c
··· 124 124 u16 get_wireless_status; 125 125 u16 set_wireless_status; 126 126 127 + /* 0x80 is off, 0x81 is on */ 128 + u16 get_lid_handling; 129 + u16 set_lid_handling; 130 + 127 131 /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ 128 132 u16 kbd_backlight; 129 133 ··· 198 194 .get_wireless_status = 0xFFFF, 199 195 .set_wireless_status = 0xFFFF, 200 196 197 + .get_lid_handling = 0xFFFF, 198 + .set_lid_handling = 0xFFFF, 199 + 201 200 .kbd_backlight = 0xFFFF, 202 201 203 202 .set_linux = 0x0a, ··· 260 253 261 254 .get_wireless_status = 0x69, 262 255 .set_wireless_status = 0x6a, 256 + 257 + .get_lid_handling = 0x6d, 258 + .set_lid_handling = 0x6e, 263 259 264 260 .kbd_backlight = 0x78, 265 261 ··· 364 354 bool four_kbd_backlight_levels; 365 355 bool enable_kbd_backlight; 366 356 bool use_native_backlight; 357 + bool lid_handling; 367 358 }; 368 359 369 360 static struct samsung_quirks samsung_unknown = {}; ··· 380 369 static struct samsung_quirks samsung_np740u3e = { 381 370 .four_kbd_backlight_levels = true, 382 371 .enable_kbd_backlight = true, 372 + }; 373 + 374 + static struct samsung_quirks samsung_lid_handling = { 375 + .lid_handling = true, 383 376 }; 384 377 385 378 static bool force; ··· 850 835 static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, 851 836 get_usb_charge, set_usb_charge); 852 837 838 + static int read_lid_handling(struct samsung_laptop *samsung) 839 + { 840 + const struct sabi_commands *commands = &samsung->config->commands; 841 + struct sabi_data data; 842 + int retval; 843 + 844 + if (commands->get_lid_handling == 0xFFFF) 845 + return -ENODEV; 846 + 847 + memset(&data, 0, sizeof(data)); 848 + retval = sabi_command(samsung, commands->get_lid_handling, 849 + &data, &data); 850 + 851 + if (retval) 852 + return retval; 853 + 854 + return data.data[0] & 0x1; 855 + } 856 + 857 + static int write_lid_handling(struct samsung_laptop *samsung, 858 + int enabled) 859 + { 860 + const struct sabi_commands *commands = &samsung->config->commands; 861 + struct sabi_data data; 862 + 863 + memset(&data, 0, sizeof(data)); 864 + data.data[0] = 0x80 | enabled; 865 + return sabi_command(samsung, commands->set_lid_handling, 866 + &data, NULL); 867 + } 868 + 869 + static ssize_t get_lid_handling(struct device *dev, 870 + struct device_attribute *attr, 871 + char *buf) 872 + { 873 + struct samsung_laptop *samsung = dev_get_drvdata(dev); 874 + int ret; 875 + 876 + ret = read_lid_handling(samsung); 877 + if (ret < 0) 878 + return ret; 879 + 880 + return sprintf(buf, "%d\n", ret); 881 + } 882 + 883 + static ssize_t set_lid_handling(struct device *dev, 884 + struct device_attribute *attr, 885 + const char *buf, size_t count) 886 + { 887 + struct samsung_laptop *samsung = dev_get_drvdata(dev); 888 + int ret, value; 889 + 890 + if (!count || kstrtoint(buf, 0, &value) != 0) 891 + return -EINVAL; 892 + 893 + ret = write_lid_handling(samsung, !!value); 894 + if (ret < 0) 895 + return ret; 896 + 897 + return count; 898 + } 899 + 900 + static DEVICE_ATTR(lid_handling, S_IWUSR | S_IRUGO, 901 + get_lid_handling, set_lid_handling); 902 + 853 903 static struct attribute *platform_attributes[] = { 854 904 &dev_attr_performance_level.attr, 855 905 &dev_attr_battery_life_extender.attr, 856 906 &dev_attr_usb_charge.attr, 907 + &dev_attr_lid_handling.attr, 857 908 NULL 858 909 }; 859 910 ··· 1040 959 if (samsung->config->sabi_version == 3) 1041 960 return samsung_rfkill_init_swsmi(samsung); 1042 961 return 0; 962 + } 963 + 964 + static void samsung_lid_handling_exit(struct samsung_laptop *samsung) 965 + { 966 + if (samsung->quirks->lid_handling) 967 + write_lid_handling(samsung, 0); 968 + } 969 + 970 + static int __init samsung_lid_handling_init(struct samsung_laptop *samsung) 971 + { 972 + int retval = 0; 973 + 974 + if (samsung->quirks->lid_handling) 975 + retval = write_lid_handling(samsung, 1); 976 + 977 + return retval; 1043 978 } 1044 979 1045 980 static int kbd_backlight_enable(struct samsung_laptop *samsung) ··· 1213 1116 } 1214 1117 1215 1118 static umode_t samsung_sysfs_is_visible(struct kobject *kobj, 1216 - struct attribute *attr, int idx) 1119 + struct attribute *attr, int idx) 1217 1120 { 1218 1121 struct device *dev = container_of(kobj, struct device, kobj); 1219 1122 struct platform_device *pdev = to_platform_device(dev); ··· 1226 1129 ok = !!(read_battery_life_extender(samsung) >= 0); 1227 1130 if (attr == &dev_attr_usb_charge.attr) 1228 1131 ok = !!(read_usb_charge(samsung) >= 0); 1132 + if (attr == &dev_attr_lid_handling.attr) 1133 + ok = !!(read_lid_handling(samsung) >= 0); 1229 1134 1230 1135 return ok ? attr->mode : 0; 1231 1136 } ··· 1540 1441 samsung->quirks->enable_kbd_backlight) 1541 1442 kbd_backlight_enable(samsung); 1542 1443 1444 + if (val == PM_POST_HIBERNATION && samsung->quirks->lid_handling) 1445 + write_lid_handling(samsung, 1); 1446 + 1543 1447 return 0; 1544 1448 } 1545 1449 ··· 1685 1583 }, 1686 1584 .driver_data = &samsung_np740u3e, 1687 1585 }, 1586 + { 1587 + .callback = samsung_dmi_matched, 1588 + .ident = "300V3Z/300V4Z/300V5Z", 1589 + .matches = { 1590 + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), 1591 + DMI_MATCH(DMI_PRODUCT_NAME, "300V3Z/300V4Z/300V5Z"), 1592 + }, 1593 + .driver_data = &samsung_lid_handling, 1594 + }, 1688 1595 { }, 1689 1596 }; 1690 1597 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); ··· 1773 1662 if (ret) 1774 1663 goto error_leds; 1775 1664 1665 + ret = samsung_lid_handling_init(samsung); 1666 + if (ret) 1667 + goto error_lid_handling; 1668 + 1776 1669 ret = samsung_debugfs_init(samsung); 1777 1670 if (ret) 1778 1671 goto error_debugfs; ··· 1788 1673 return ret; 1789 1674 1790 1675 error_debugfs: 1676 + samsung_lid_handling_exit(samsung); 1677 + error_lid_handling: 1791 1678 samsung_leds_exit(samsung); 1792 1679 error_leds: 1793 1680 samsung_rfkill_exit(samsung); ··· 1814 1697 unregister_pm_notifier(&samsung->pm_nb); 1815 1698 1816 1699 samsung_debugfs_exit(samsung); 1700 + samsung_lid_handling_exit(samsung); 1817 1701 samsung_leds_exit(samsung); 1818 1702 samsung_rfkill_exit(samsung); 1819 1703 samsung_backlight_exit(samsung);