diff options
Diffstat (limited to 'drivers/platform/x86/samsung-laptop.c')
-rw-r--r-- | drivers/platform/x86/samsung-laptop.c | 146 |
1 files changed, 139 insertions, 7 deletions
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index ff765d8e1a09..9e701b2256f9 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c | |||
@@ -124,6 +124,10 @@ struct sabi_commands { | |||
124 | u16 get_wireless_status; | 124 | u16 get_wireless_status; |
125 | u16 set_wireless_status; | 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 | /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ | 131 | /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ |
128 | u16 kbd_backlight; | 132 | u16 kbd_backlight; |
129 | 133 | ||
@@ -194,6 +198,9 @@ static const struct sabi_config sabi_configs[] = { | |||
194 | .get_wireless_status = 0xFFFF, | 198 | .get_wireless_status = 0xFFFF, |
195 | .set_wireless_status = 0xFFFF, | 199 | .set_wireless_status = 0xFFFF, |
196 | 200 | ||
201 | .get_lid_handling = 0xFFFF, | ||
202 | .set_lid_handling = 0xFFFF, | ||
203 | |||
197 | .kbd_backlight = 0xFFFF, | 204 | .kbd_backlight = 0xFFFF, |
198 | 205 | ||
199 | .set_linux = 0x0a, | 206 | .set_linux = 0x0a, |
@@ -254,6 +261,9 @@ static const struct sabi_config sabi_configs[] = { | |||
254 | .get_wireless_status = 0x69, | 261 | .get_wireless_status = 0x69, |
255 | .set_wireless_status = 0x6a, | 262 | .set_wireless_status = 0x6a, |
256 | 263 | ||
264 | .get_lid_handling = 0x6d, | ||
265 | .set_lid_handling = 0x6e, | ||
266 | |||
257 | .kbd_backlight = 0x78, | 267 | .kbd_backlight = 0x78, |
258 | 268 | ||
259 | .set_linux = 0xff, | 269 | .set_linux = 0xff, |
@@ -353,6 +363,8 @@ struct samsung_quirks { | |||
353 | bool broken_acpi_video; | 363 | bool broken_acpi_video; |
354 | bool four_kbd_backlight_levels; | 364 | bool four_kbd_backlight_levels; |
355 | bool enable_kbd_backlight; | 365 | bool enable_kbd_backlight; |
366 | bool use_native_backlight; | ||
367 | bool lid_handling; | ||
356 | }; | 368 | }; |
357 | 369 | ||
358 | static struct samsung_quirks samsung_unknown = {}; | 370 | static struct samsung_quirks samsung_unknown = {}; |
@@ -361,11 +373,19 @@ static struct samsung_quirks samsung_broken_acpi_video = { | |||
361 | .broken_acpi_video = true, | 373 | .broken_acpi_video = true, |
362 | }; | 374 | }; |
363 | 375 | ||
376 | static struct samsung_quirks samsung_use_native_backlight = { | ||
377 | .use_native_backlight = true, | ||
378 | }; | ||
379 | |||
364 | static struct samsung_quirks samsung_np740u3e = { | 380 | static struct samsung_quirks samsung_np740u3e = { |
365 | .four_kbd_backlight_levels = true, | 381 | .four_kbd_backlight_levels = true, |
366 | .enable_kbd_backlight = true, | 382 | .enable_kbd_backlight = true, |
367 | }; | 383 | }; |
368 | 384 | ||
385 | static struct samsung_quirks samsung_lid_handling = { | ||
386 | .lid_handling = true, | ||
387 | }; | ||
388 | |||
369 | static bool force; | 389 | static bool force; |
370 | module_param(force, bool, 0); | 390 | module_param(force, bool, 0); |
371 | MODULE_PARM_DESC(force, | 391 | MODULE_PARM_DESC(force, |
@@ -748,7 +768,7 @@ static ssize_t set_battery_life_extender(struct device *dev, | |||
748 | struct samsung_laptop *samsung = dev_get_drvdata(dev); | 768 | struct samsung_laptop *samsung = dev_get_drvdata(dev); |
749 | int ret, value; | 769 | int ret, value; |
750 | 770 | ||
751 | if (!count || sscanf(buf, "%i", &value) != 1) | 771 | if (!count || kstrtoint(buf, 0, &value) != 0) |
752 | return -EINVAL; | 772 | return -EINVAL; |
753 | 773 | ||
754 | ret = write_battery_life_extender(samsung, !!value); | 774 | ret = write_battery_life_extender(samsung, !!value); |
@@ -817,7 +837,7 @@ static ssize_t set_usb_charge(struct device *dev, | |||
817 | struct samsung_laptop *samsung = dev_get_drvdata(dev); | 837 | struct samsung_laptop *samsung = dev_get_drvdata(dev); |
818 | int ret, value; | 838 | int ret, value; |
819 | 839 | ||
820 | if (!count || sscanf(buf, "%i", &value) != 1) | 840 | if (!count || kstrtoint(buf, 0, &value) != 0) |
821 | return -EINVAL; | 841 | return -EINVAL; |
822 | 842 | ||
823 | ret = write_usb_charge(samsung, !!value); | 843 | ret = write_usb_charge(samsung, !!value); |
@@ -830,10 +850,76 @@ static ssize_t set_usb_charge(struct device *dev, | |||
830 | static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, | 850 | static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, |
831 | get_usb_charge, set_usb_charge); | 851 | get_usb_charge, set_usb_charge); |
832 | 852 | ||
853 | static int read_lid_handling(struct samsung_laptop *samsung) | ||
854 | { | ||
855 | const struct sabi_commands *commands = &samsung->config->commands; | ||
856 | struct sabi_data data; | ||
857 | int retval; | ||
858 | |||
859 | if (commands->get_lid_handling == 0xFFFF) | ||
860 | return -ENODEV; | ||
861 | |||
862 | memset(&data, 0, sizeof(data)); | ||
863 | retval = sabi_command(samsung, commands->get_lid_handling, | ||
864 | &data, &data); | ||
865 | |||
866 | if (retval) | ||
867 | return retval; | ||
868 | |||
869 | return data.data[0] & 0x1; | ||
870 | } | ||
871 | |||
872 | static int write_lid_handling(struct samsung_laptop *samsung, | ||
873 | int enabled) | ||
874 | { | ||
875 | const struct sabi_commands *commands = &samsung->config->commands; | ||
876 | struct sabi_data data; | ||
877 | |||
878 | memset(&data, 0, sizeof(data)); | ||
879 | data.data[0] = 0x80 | enabled; | ||
880 | return sabi_command(samsung, commands->set_lid_handling, | ||
881 | &data, NULL); | ||
882 | } | ||
883 | |||
884 | static ssize_t get_lid_handling(struct device *dev, | ||
885 | struct device_attribute *attr, | ||
886 | char *buf) | ||
887 | { | ||
888 | struct samsung_laptop *samsung = dev_get_drvdata(dev); | ||
889 | int ret; | ||
890 | |||
891 | ret = read_lid_handling(samsung); | ||
892 | if (ret < 0) | ||
893 | return ret; | ||
894 | |||
895 | return sprintf(buf, "%d\n", ret); | ||
896 | } | ||
897 | |||
898 | static ssize_t set_lid_handling(struct device *dev, | ||
899 | struct device_attribute *attr, | ||
900 | const char *buf, size_t count) | ||
901 | { | ||
902 | struct samsung_laptop *samsung = dev_get_drvdata(dev); | ||
903 | int ret, value; | ||
904 | |||
905 | if (!count || kstrtoint(buf, 0, &value) != 0) | ||
906 | return -EINVAL; | ||
907 | |||
908 | ret = write_lid_handling(samsung, !!value); | ||
909 | if (ret < 0) | ||
910 | return ret; | ||
911 | |||
912 | return count; | ||
913 | } | ||
914 | |||
915 | static DEVICE_ATTR(lid_handling, S_IWUSR | S_IRUGO, | ||
916 | get_lid_handling, set_lid_handling); | ||
917 | |||
833 | static struct attribute *platform_attributes[] = { | 918 | static struct attribute *platform_attributes[] = { |
834 | &dev_attr_performance_level.attr, | 919 | &dev_attr_performance_level.attr, |
835 | &dev_attr_battery_life_extender.attr, | 920 | &dev_attr_battery_life_extender.attr, |
836 | &dev_attr_usb_charge.attr, | 921 | &dev_attr_usb_charge.attr, |
922 | &dev_attr_lid_handling.attr, | ||
837 | NULL | 923 | NULL |
838 | }; | 924 | }; |
839 | 925 | ||
@@ -956,6 +1042,22 @@ static int __init samsung_rfkill_init(struct samsung_laptop *samsung) | |||
956 | return 0; | 1042 | return 0; |
957 | } | 1043 | } |
958 | 1044 | ||
1045 | static void samsung_lid_handling_exit(struct samsung_laptop *samsung) | ||
1046 | { | ||
1047 | if (samsung->quirks->lid_handling) | ||
1048 | write_lid_handling(samsung, 0); | ||
1049 | } | ||
1050 | |||
1051 | static int __init samsung_lid_handling_init(struct samsung_laptop *samsung) | ||
1052 | { | ||
1053 | int retval = 0; | ||
1054 | |||
1055 | if (samsung->quirks->lid_handling) | ||
1056 | retval = write_lid_handling(samsung, 1); | ||
1057 | |||
1058 | return retval; | ||
1059 | } | ||
1060 | |||
959 | static int kbd_backlight_enable(struct samsung_laptop *samsung) | 1061 | static int kbd_backlight_enable(struct samsung_laptop *samsung) |
960 | { | 1062 | { |
961 | const struct sabi_commands *commands = &samsung->config->commands; | 1063 | const struct sabi_commands *commands = &samsung->config->commands; |
@@ -1111,7 +1213,7 @@ static int __init samsung_backlight_init(struct samsung_laptop *samsung) | |||
1111 | } | 1213 | } |
1112 | 1214 | ||
1113 | static umode_t samsung_sysfs_is_visible(struct kobject *kobj, | 1215 | static umode_t samsung_sysfs_is_visible(struct kobject *kobj, |
1114 | struct attribute *attr, int idx) | 1216 | struct attribute *attr, int idx) |
1115 | { | 1217 | { |
1116 | struct device *dev = container_of(kobj, struct device, kobj); | 1218 | struct device *dev = container_of(kobj, struct device, kobj); |
1117 | struct platform_device *pdev = to_platform_device(dev); | 1219 | struct platform_device *pdev = to_platform_device(dev); |
@@ -1124,6 +1226,8 @@ static umode_t samsung_sysfs_is_visible(struct kobject *kobj, | |||
1124 | ok = !!(read_battery_life_extender(samsung) >= 0); | 1226 | ok = !!(read_battery_life_extender(samsung) >= 0); |
1125 | if (attr == &dev_attr_usb_charge.attr) | 1227 | if (attr == &dev_attr_usb_charge.attr) |
1126 | ok = !!(read_usb_charge(samsung) >= 0); | 1228 | ok = !!(read_usb_charge(samsung) >= 0); |
1229 | if (attr == &dev_attr_lid_handling.attr) | ||
1230 | ok = !!(read_lid_handling(samsung) >= 0); | ||
1127 | 1231 | ||
1128 | return ok ? attr->mode : 0; | 1232 | return ok ? attr->mode : 0; |
1129 | } | 1233 | } |
@@ -1357,7 +1461,7 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) | |||
1357 | samsung_sabi_diag(samsung); | 1461 | samsung_sabi_diag(samsung); |
1358 | 1462 | ||
1359 | /* Try to find one of the signatures in memory to find the header */ | 1463 | /* Try to find one of the signatures in memory to find the header */ |
1360 | for (i = 0; sabi_configs[i].test_string != 0; ++i) { | 1464 | for (i = 0; sabi_configs[i].test_string != NULL; ++i) { |
1361 | samsung->config = &sabi_configs[i]; | 1465 | samsung->config = &sabi_configs[i]; |
1362 | loca = find_signature(samsung->f0000_segment, | 1466 | loca = find_signature(samsung->f0000_segment, |
1363 | samsung->config->test_string); | 1467 | samsung->config->test_string); |
@@ -1436,6 +1540,9 @@ static int samsung_pm_notification(struct notifier_block *nb, | |||
1436 | samsung->quirks->enable_kbd_backlight) | 1540 | samsung->quirks->enable_kbd_backlight) |
1437 | kbd_backlight_enable(samsung); | 1541 | kbd_backlight_enable(samsung); |
1438 | 1542 | ||
1543 | if (val == PM_POST_HIBERNATION && samsung->quirks->lid_handling) | ||
1544 | write_lid_handling(samsung, 1); | ||
1545 | |||
1439 | return 0; | 1546 | return 0; |
1440 | } | 1547 | } |
1441 | 1548 | ||
@@ -1507,7 +1614,7 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { | |||
1507 | DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), | 1614 | DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), |
1508 | DMI_MATCH(DMI_BOARD_NAME, "N150P"), | 1615 | DMI_MATCH(DMI_BOARD_NAME, "N150P"), |
1509 | }, | 1616 | }, |
1510 | .driver_data = &samsung_broken_acpi_video, | 1617 | .driver_data = &samsung_use_native_backlight, |
1511 | }, | 1618 | }, |
1512 | { | 1619 | { |
1513 | .callback = samsung_dmi_matched, | 1620 | .callback = samsung_dmi_matched, |
@@ -1517,7 +1624,7 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { | |||
1517 | DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), | 1624 | DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"), |
1518 | DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), | 1625 | DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"), |
1519 | }, | 1626 | }, |
1520 | .driver_data = &samsung_broken_acpi_video, | 1627 | .driver_data = &samsung_use_native_backlight, |
1521 | }, | 1628 | }, |
1522 | { | 1629 | { |
1523 | .callback = samsung_dmi_matched, | 1630 | .callback = samsung_dmi_matched, |
@@ -1557,7 +1664,7 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { | |||
1557 | DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), | 1664 | DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), |
1558 | DMI_MATCH(DMI_BOARD_NAME, "N250P"), | 1665 | DMI_MATCH(DMI_BOARD_NAME, "N250P"), |
1559 | }, | 1666 | }, |
1560 | .driver_data = &samsung_broken_acpi_video, | 1667 | .driver_data = &samsung_use_native_backlight, |
1561 | }, | 1668 | }, |
1562 | { | 1669 | { |
1563 | .callback = samsung_dmi_matched, | 1670 | .callback = samsung_dmi_matched, |
@@ -1578,6 +1685,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { | |||
1578 | }, | 1685 | }, |
1579 | .driver_data = &samsung_np740u3e, | 1686 | .driver_data = &samsung_np740u3e, |
1580 | }, | 1687 | }, |
1688 | { | ||
1689 | .callback = samsung_dmi_matched, | ||
1690 | .ident = "300V3Z/300V4Z/300V5Z", | ||
1691 | .matches = { | ||
1692 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
1693 | DMI_MATCH(DMI_PRODUCT_NAME, "300V3Z/300V4Z/300V5Z"), | ||
1694 | }, | ||
1695 | .driver_data = &samsung_lid_handling, | ||
1696 | }, | ||
1581 | { }, | 1697 | { }, |
1582 | }; | 1698 | }; |
1583 | MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); | 1699 | MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); |
@@ -1616,6 +1732,15 @@ static int __init samsung_init(void) | |||
1616 | pr_info("Disabling ACPI video driver\n"); | 1732 | pr_info("Disabling ACPI video driver\n"); |
1617 | acpi_video_unregister(); | 1733 | acpi_video_unregister(); |
1618 | } | 1734 | } |
1735 | |||
1736 | if (samsung->quirks->use_native_backlight) { | ||
1737 | pr_info("Using native backlight driver\n"); | ||
1738 | /* Tell acpi-video to not handle the backlight */ | ||
1739 | acpi_video_dmi_promote_vendor(); | ||
1740 | acpi_video_unregister(); | ||
1741 | /* And also do not handle it ourselves */ | ||
1742 | samsung->handle_backlight = false; | ||
1743 | } | ||
1619 | #endif | 1744 | #endif |
1620 | 1745 | ||
1621 | ret = samsung_platform_init(samsung); | 1746 | ret = samsung_platform_init(samsung); |
@@ -1648,6 +1773,10 @@ static int __init samsung_init(void) | |||
1648 | if (ret) | 1773 | if (ret) |
1649 | goto error_leds; | 1774 | goto error_leds; |
1650 | 1775 | ||
1776 | ret = samsung_lid_handling_init(samsung); | ||
1777 | if (ret) | ||
1778 | goto error_lid_handling; | ||
1779 | |||
1651 | ret = samsung_debugfs_init(samsung); | 1780 | ret = samsung_debugfs_init(samsung); |
1652 | if (ret) | 1781 | if (ret) |
1653 | goto error_debugfs; | 1782 | goto error_debugfs; |
@@ -1659,6 +1788,8 @@ static int __init samsung_init(void) | |||
1659 | return ret; | 1788 | return ret; |
1660 | 1789 | ||
1661 | error_debugfs: | 1790 | error_debugfs: |
1791 | samsung_lid_handling_exit(samsung); | ||
1792 | error_lid_handling: | ||
1662 | samsung_leds_exit(samsung); | 1793 | samsung_leds_exit(samsung); |
1663 | error_leds: | 1794 | error_leds: |
1664 | samsung_rfkill_exit(samsung); | 1795 | samsung_rfkill_exit(samsung); |
@@ -1683,6 +1814,7 @@ static void __exit samsung_exit(void) | |||
1683 | unregister_pm_notifier(&samsung->pm_nb); | 1814 | unregister_pm_notifier(&samsung->pm_nb); |
1684 | 1815 | ||
1685 | samsung_debugfs_exit(samsung); | 1816 | samsung_debugfs_exit(samsung); |
1817 | samsung_lid_handling_exit(samsung); | ||
1686 | samsung_leds_exit(samsung); | 1818 | samsung_leds_exit(samsung); |
1687 | samsung_rfkill_exit(samsung); | 1819 | samsung_rfkill_exit(samsung); |
1688 | samsung_backlight_exit(samsung); | 1820 | samsung_backlight_exit(samsung); |