samsung-laptop: enable better lid handling
authorJulijonas Kikutis <julijonas.kikutis@gmail.com>
Thu, 29 Jan 2015 13:04:37 +0000 (13:04 +0000)
committerDarren Hart <dvhart@linux.intel.com>
Sat, 7 Feb 2015 02:33:55 +0000 (18:33 -0800)
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>
Documentation/ABI/testing/sysfs-driver-samsung-laptop
drivers/platform/x86/samsung-laptop.c

index 678819a3f8bf89e3b43e134979af2fa3f28199fd..63c1ad0212fc8624432f1691240443b9015b5d3f 100644 (file)
@@ -35,3 +35,11 @@ Contact:     Corentin Chary <corentin.chary@gmail.com>
 Description:   Use your USB ports to charge devices, even
                when your laptop is powered off.
                1 means enabled, 0 means disabled.
+
+What:          /sys/devices/platform/samsung/lid_handling
+Date:          December 11, 2014
+KernelVersion: 3.19
+Contact:       Julijonas Kikutis <julijonas.kikutis@gmail.com>
+Description:   Some Samsung laptops handle lid closing quicker and
+               only handle lid opening with this mode enabled.
+               1 means enabled, 0 means disabled.
index ce364a41842a2ad51e06c936824e3fb69a0a67b4..1743336602883ef106f7018f3f99097bdc6f566d 100644 (file)
@@ -124,6 +124,10 @@ struct sabi_commands {
        u16 get_wireless_status;
        u16 set_wireless_status;
 
+       /* 0x80 is off, 0x81 is on */
+       u16 get_lid_handling;
+       u16 set_lid_handling;
+
        /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */
        u16 kbd_backlight;
 
@@ -194,6 +198,9 @@ static const struct sabi_config sabi_configs[] = {
                        .get_wireless_status = 0xFFFF,
                        .set_wireless_status = 0xFFFF,
 
+                       .get_lid_handling = 0xFFFF,
+                       .set_lid_handling = 0xFFFF,
+
                        .kbd_backlight = 0xFFFF,
 
                        .set_linux = 0x0a,
@@ -254,6 +261,9 @@ static const struct sabi_config sabi_configs[] = {
                        .get_wireless_status = 0x69,
                        .set_wireless_status = 0x6a,
 
+                       .get_lid_handling = 0x6d,
+                       .set_lid_handling = 0x6e,
+
                        .kbd_backlight = 0x78,
 
                        .set_linux = 0xff,
@@ -354,6 +364,7 @@ struct samsung_quirks {
        bool four_kbd_backlight_levels;
        bool enable_kbd_backlight;
        bool use_native_backlight;
+       bool lid_handling;
 };
 
 static struct samsung_quirks samsung_unknown = {};
@@ -371,6 +382,10 @@ static struct samsung_quirks samsung_np740u3e = {
        .enable_kbd_backlight = true,
 };
 
+static struct samsung_quirks samsung_lid_handling = {
+       .lid_handling = true,
+};
+
 static bool force;
 module_param(force, bool, 0);
 MODULE_PARM_DESC(force,
@@ -835,10 +850,76 @@ static ssize_t set_usb_charge(struct device *dev,
 static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO,
                   get_usb_charge, set_usb_charge);
 
+static int read_lid_handling(struct samsung_laptop *samsung)
+{
+       const struct sabi_commands *commands = &samsung->config->commands;
+       struct sabi_data data;
+       int retval;
+
+       if (commands->get_lid_handling == 0xFFFF)
+               return -ENODEV;
+
+       memset(&data, 0, sizeof(data));
+       retval = sabi_command(samsung, commands->get_lid_handling,
+                             &data, &data);
+
+       if (retval)
+               return retval;
+
+       return data.data[0] & 0x1;
+}
+
+static int write_lid_handling(struct samsung_laptop *samsung,
+                             int enabled)
+{
+       const struct sabi_commands *commands = &samsung->config->commands;
+       struct sabi_data data;
+
+       memset(&data, 0, sizeof(data));
+       data.data[0] = 0x80 | enabled;
+       return sabi_command(samsung, commands->set_lid_handling,
+                           &data, NULL);
+}
+
+static ssize_t get_lid_handling(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct samsung_laptop *samsung = dev_get_drvdata(dev);
+       int ret;
+
+       ret = read_lid_handling(samsung);
+       if (ret < 0)
+               return ret;
+
+       return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t set_lid_handling(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct samsung_laptop *samsung = dev_get_drvdata(dev);
+       int ret, value;
+
+       if (!count || kstrtoint(buf, 0, &value) != 0)
+               return -EINVAL;
+
+       ret = write_lid_handling(samsung, !!value);
+       if (ret < 0)
+               return ret;
+
+       return count;
+}
+
+static DEVICE_ATTR(lid_handling, S_IWUSR | S_IRUGO,
+                  get_lid_handling, set_lid_handling);
+
 static struct attribute *platform_attributes[] = {
        &dev_attr_performance_level.attr,
        &dev_attr_battery_life_extender.attr,
        &dev_attr_usb_charge.attr,
+       &dev_attr_lid_handling.attr,
        NULL
 };
 
@@ -961,6 +1042,22 @@ static int __init samsung_rfkill_init(struct samsung_laptop *samsung)
        return 0;
 }
 
+static void samsung_lid_handling_exit(struct samsung_laptop *samsung)
+{
+       if (samsung->quirks->lid_handling)
+               write_lid_handling(samsung, 0);
+}
+
+static int __init samsung_lid_handling_init(struct samsung_laptop *samsung)
+{
+       int retval = 0;
+
+       if (samsung->quirks->lid_handling)
+               retval = write_lid_handling(samsung, 1);
+
+       return retval;
+}
+
 static int kbd_backlight_enable(struct samsung_laptop *samsung)
 {
        const struct sabi_commands *commands = &samsung->config->commands;
@@ -1116,7 +1213,7 @@ static int __init samsung_backlight_init(struct samsung_laptop *samsung)
 }
 
 static umode_t samsung_sysfs_is_visible(struct kobject *kobj,
-                                      struct attribute *attr, int idx)
+                                       struct attribute *attr, int idx)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
        struct platform_device *pdev = to_platform_device(dev);
@@ -1129,6 +1226,8 @@ static umode_t samsung_sysfs_is_visible(struct kobject *kobj,
                ok = !!(read_battery_life_extender(samsung) >= 0);
        if (attr == &dev_attr_usb_charge.attr)
                ok = !!(read_usb_charge(samsung) >= 0);
+       if (attr == &dev_attr_lid_handling.attr)
+               ok = !!(read_lid_handling(samsung) >= 0);
 
        return ok ? attr->mode : 0;
 }
@@ -1441,6 +1540,9 @@ static int samsung_pm_notification(struct notifier_block *nb,
            samsung->quirks->enable_kbd_backlight)
                kbd_backlight_enable(samsung);
 
+       if (val == PM_POST_HIBERNATION && samsung->quirks->lid_handling)
+               write_lid_handling(samsung, 1);
+
        return 0;
 }
 
@@ -1583,6 +1685,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
                },
         .driver_data = &samsung_np740u3e,
        },
+       {
+        .callback = samsung_dmi_matched,
+        .ident = "300V3Z/300V4Z/300V5Z",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+               DMI_MATCH(DMI_PRODUCT_NAME, "300V3Z/300V4Z/300V5Z"),
+               },
+        .driver_data = &samsung_lid_handling,
+       },
        { },
 };
 MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
@@ -1662,6 +1773,10 @@ static int __init samsung_init(void)
        if (ret)
                goto error_leds;
 
+       ret = samsung_lid_handling_init(samsung);
+       if (ret)
+               goto error_lid_handling;
+
        ret = samsung_debugfs_init(samsung);
        if (ret)
                goto error_debugfs;
@@ -1673,6 +1788,8 @@ static int __init samsung_init(void)
        return ret;
 
 error_debugfs:
+       samsung_lid_handling_exit(samsung);
+error_lid_handling:
        samsung_leds_exit(samsung);
 error_leds:
        samsung_rfkill_exit(samsung);
@@ -1697,6 +1814,7 @@ static void __exit samsung_exit(void)
        unregister_pm_notifier(&samsung->pm_nb);
 
        samsung_debugfs_exit(samsung);
+       samsung_lid_handling_exit(samsung);
        samsung_leds_exit(samsung);
        samsung_rfkill_exit(samsung);
        samsung_backlight_exit(samsung);