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

platform: x86: dell-laptop: Add support for keyboard backlight

This patch adds the support for the configuration of the keyboard
backlight on supported Dell laptops.

With this patch it is possible to set:
* keyboard backlight level
* timeout after which the backlight will be automatically turned off
* input activity triggers (keyboard, touchpad, mouse) that enable the backlight
* ambient light settings

The settings are exposed via /sys/class/leds/dell::kbd_backlight/

The code is based on the newly released documentation by Dell in the
libsmbios project.

Signed-off-by: Pali Rohár <pali.rohar@gmail.com>
Signed-off-by: Gabriele Mazzotta <gabriele.mzt@gmail.com>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Darren Hart <dvhart@linux.intel.com>

authored by

Gabriele Mazzotta and committed by
Darren Hart
6cff8d60 9659bee4

+1152 -6
+69
Documentation/ABI/testing/sysfs-platform-dell-laptop
··· 1 + What: /sys/class/leds/dell::kbd_backlight/als_enabled 2 + Date: December 2014 3 + KernelVersion: 3.19 4 + Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>, 5 + Pali Rohár <pali.rohar@gmail.com> 6 + Description: 7 + This file allows to control the automatic keyboard 8 + illumination mode on some systems that have an ambient 9 + light sensor. Write 1 to this file to enable the auto 10 + mode, 0 to disable it. 11 + 12 + What: /sys/class/leds/dell::kbd_backlight/als_setting 13 + Date: December 2014 14 + KernelVersion: 3.19 15 + Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>, 16 + Pali Rohár <pali.rohar@gmail.com> 17 + Description: 18 + This file allows to specifiy the on/off threshold value, 19 + as reported by the ambient light sensor. 20 + 21 + What: /sys/class/leds/dell::kbd_backlight/start_triggers 22 + Date: December 2014 23 + KernelVersion: 3.19 24 + Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>, 25 + Pali Rohár <pali.rohar@gmail.com> 26 + Description: 27 + This file allows to control the input triggers that 28 + turn on the keyboard backlight illumination that is 29 + disabled because of inactivity. 30 + Read the file to see the triggers available. The ones 31 + enabled are preceded by '+', those disabled by '-'. 32 + 33 + To enable a trigger, write its name preceded by '+' to 34 + this file. To disable a trigger, write its name preceded 35 + by '-' instead. 36 + 37 + For example, to enable the keyboard as trigger run: 38 + echo +keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers 39 + To disable it: 40 + echo -keyboard > /sys/class/leds/dell::kbd_backlight/start_triggers 41 + 42 + Note that not all the available triggers can be configured. 43 + 44 + What: /sys/class/leds/dell::kbd_backlight/stop_timeout 45 + Date: December 2014 46 + KernelVersion: 3.19 47 + Contact: Gabriele Mazzotta <gabriele.mzt@gmail.com>, 48 + Pali Rohár <pali.rohar@gmail.com> 49 + Description: 50 + This file allows to specify the interval after which the 51 + keyboard illumination is disabled because of inactivity. 52 + The timeouts are expressed in seconds, minutes, hours and 53 + days, for which the symbols are 's', 'm', 'h' and 'd' 54 + respectively. 55 + 56 + To configure the timeout, write to this file a value along 57 + with any the above units. If no unit is specified, the value 58 + is assumed to be expressed in seconds. 59 + 60 + For example, to set the timeout to 10 minutes run: 61 + echo 10m > /sys/class/leds/dell::kbd_backlight/stop_timeout 62 + 63 + Note that when this file is read, the returned value might be 64 + expressed in a different unit than the one used when the timeout 65 + was set. 66 + 67 + Also note that only some timeouts are supported and that 68 + some systems might fall back to a specific timeout in case 69 + an invalid timeout is written to this file.
+1083 -6
drivers/platform/x86/dell-laptop.c
··· 2 2 * Driver for Dell laptop extras 3 3 * 4 4 * Copyright (c) Red Hat <mjg@redhat.com> 5 + * Copyright (c) 2014 Gabriele Mazzotta <gabriele.mzt@gmail.com> 6 + * Copyright (c) 2014 Pali Rohár <pali.rohar@gmail.com> 5 7 * 6 - * Based on documentation in the libsmbios package, Copyright (C) 2005 Dell 7 - * Inc. 8 + * Based on documentation in the libsmbios package: 9 + * Copyright (C) 2005-2014 Dell Inc. 8 10 * 9 11 * This program is free software; you can redistribute it and/or modify 10 12 * it under the terms of the GNU General Public License version 2 as ··· 34 32 #include "../../firmware/dcdbas.h" 35 33 36 34 #define BRIGHTNESS_TOKEN 0x7d 35 + #define KBD_LED_OFF_TOKEN 0x01E1 36 + #define KBD_LED_ON_TOKEN 0x01E2 37 + #define KBD_LED_AUTO_TOKEN 0x01E3 38 + #define KBD_LED_AUTO_25_TOKEN 0x02EA 39 + #define KBD_LED_AUTO_50_TOKEN 0x02EB 40 + #define KBD_LED_AUTO_75_TOKEN 0x02EC 41 + #define KBD_LED_AUTO_100_TOKEN 0x02F6 37 42 38 43 /* This structure will be modified by the firmware when we enter 39 44 * system management mode, hence the volatiles */ ··· 71 62 72 63 struct quirk_entry { 73 64 u8 touchpad_led; 65 + 66 + int needs_kbd_timeouts; 67 + /* 68 + * Ordered list of timeouts expressed in seconds. 69 + * The list must end with -1 70 + */ 71 + int kbd_timeouts[]; 74 72 }; 75 73 76 74 static struct quirk_entry *quirks; ··· 91 75 quirks = dmi->driver_data; 92 76 return 1; 93 77 } 78 + 79 + /* 80 + * These values come from Windows utility provided by Dell. If any other value 81 + * is used then BIOS silently set timeout to 0 without any error message. 82 + */ 83 + static struct quirk_entry quirk_dell_xps13_9333 = { 84 + .needs_kbd_timeouts = 1, 85 + .kbd_timeouts = { 0, 5, 15, 60, 5 * 60, 15 * 60, -1 }, 86 + }; 94 87 95 88 static int da_command_address; 96 89 static int da_command_code; ··· 292 267 }, 293 268 .driver_data = &quirk_dell_vostro_v130, 294 269 }, 270 + { 271 + .callback = dmi_matched, 272 + .ident = "Dell XPS13 9333", 273 + .matches = { 274 + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 275 + DMI_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"), 276 + }, 277 + .driver_data = &quirk_dell_xps13_9333, 278 + }, 295 279 { } 296 280 }; 297 281 ··· 365 331 } 366 332 } 367 333 368 - static int find_token_location(int tokenid) 334 + static int find_token_id(int tokenid) 369 335 { 370 336 int i; 337 + 371 338 for (i = 0; i < da_num_tokens; i++) { 372 339 if (da_tokens[i].tokenID == tokenid) 373 - return da_tokens[i].location; 340 + return i; 374 341 } 375 342 376 343 return -1; 344 + } 345 + 346 + static int find_token_location(int tokenid) 347 + { 348 + int id; 349 + 350 + id = find_token_id(tokenid); 351 + if (id == -1) 352 + return -1; 353 + 354 + return da_tokens[id].location; 377 355 } 378 356 379 357 static struct calling_interface_buffer * ··· 406 360 dcdbas_smi_request(&command); 407 361 408 362 return buffer; 363 + } 364 + 365 + static inline int dell_smi_error(int value) 366 + { 367 + switch (value) { 368 + case 0: /* Completed successfully */ 369 + return 0; 370 + case -1: /* Completed with error */ 371 + return -EIO; 372 + case -2: /* Function not supported */ 373 + return -ENXIO; 374 + default: /* Unknown error */ 375 + return -EINVAL; 376 + } 409 377 } 410 378 411 379 /* Derived from information in DellWirelessCtl.cpp: ··· 776 716 else 777 717 dell_send_request(buffer, 1, 1); 778 718 779 - out: 719 + out: 780 720 release_buffer(); 781 721 return ret; 782 722 } ··· 800 740 801 741 ret = buffer->output[1]; 802 742 803 - out: 743 + out: 804 744 release_buffer(); 805 745 return ret; 806 746 } ··· 847 787 static void touchpad_led_exit(void) 848 788 { 849 789 led_classdev_unregister(&touchpad_led); 790 + } 791 + 792 + /* 793 + * Derived from information in smbios-keyboard-ctl: 794 + * 795 + * cbClass 4 796 + * cbSelect 11 797 + * Keyboard illumination 798 + * cbArg1 determines the function to be performed 799 + * 800 + * cbArg1 0x0 = Get Feature Information 801 + * cbRES1 Standard return codes (0, -1, -2) 802 + * cbRES2, word0 Bitmap of user-selectable modes 803 + * bit 0 Always off (All systems) 804 + * bit 1 Always on (Travis ATG, Siberia) 805 + * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG) 806 + * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off 807 + * bit 4 Auto: Input-activity-based On; input-activity based Off 808 + * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off 809 + * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off 810 + * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off 811 + * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off 812 + * bits 9-15 Reserved for future use 813 + * cbRES2, byte2 Reserved for future use 814 + * cbRES2, byte3 Keyboard illumination type 815 + * 0 Reserved 816 + * 1 Tasklight 817 + * 2 Backlight 818 + * 3-255 Reserved for future use 819 + * cbRES3, byte0 Supported auto keyboard illumination trigger bitmap. 820 + * bit 0 Any keystroke 821 + * bit 1 Touchpad activity 822 + * bit 2 Pointing stick 823 + * bit 3 Any mouse 824 + * bits 4-7 Reserved for future use 825 + * cbRES3, byte1 Supported timeout unit bitmap 826 + * bit 0 Seconds 827 + * bit 1 Minutes 828 + * bit 2 Hours 829 + * bit 3 Days 830 + * bits 4-7 Reserved for future use 831 + * cbRES3, byte2 Number of keyboard light brightness levels 832 + * cbRES4, byte0 Maximum acceptable seconds value (0 if seconds not supported). 833 + * cbRES4, byte1 Maximum acceptable minutes value (0 if minutes not supported). 834 + * cbRES4, byte2 Maximum acceptable hours value (0 if hours not supported). 835 + * cbRES4, byte3 Maximum acceptable days value (0 if days not supported) 836 + * 837 + * cbArg1 0x1 = Get Current State 838 + * cbRES1 Standard return codes (0, -1, -2) 839 + * cbRES2, word0 Bitmap of current mode state 840 + * bit 0 Always off (All systems) 841 + * bit 1 Always on (Travis ATG, Siberia) 842 + * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG) 843 + * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off 844 + * bit 4 Auto: Input-activity-based On; input-activity based Off 845 + * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off 846 + * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off 847 + * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off 848 + * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off 849 + * bits 9-15 Reserved for future use 850 + * Note: Only One bit can be set 851 + * cbRES2, byte2 Currently active auto keyboard illumination triggers. 852 + * bit 0 Any keystroke 853 + * bit 1 Touchpad activity 854 + * bit 2 Pointing stick 855 + * bit 3 Any mouse 856 + * bits 4-7 Reserved for future use 857 + * cbRES2, byte3 Current Timeout 858 + * bits 7:6 Timeout units indicator: 859 + * 00b Seconds 860 + * 01b Minutes 861 + * 10b Hours 862 + * 11b Days 863 + * bits 5:0 Timeout value (0-63) in sec/min/hr/day 864 + * NOTE: A value of 0 means always on (no timeout) if any bits of RES3 byte 865 + * are set upon return from the [Get feature information] call. 866 + * cbRES3, byte0 Current setting of ALS value that turns the light on or off. 867 + * cbRES3, byte1 Current ALS reading 868 + * cbRES3, byte2 Current keyboard light level. 869 + * 870 + * cbArg1 0x2 = Set New State 871 + * cbRES1 Standard return codes (0, -1, -2) 872 + * cbArg2, word0 Bitmap of current mode state 873 + * bit 0 Always off (All systems) 874 + * bit 1 Always on (Travis ATG, Siberia) 875 + * bit 2 Auto: ALS-based On; ALS-based Off (Travis ATG) 876 + * bit 3 Auto: ALS- and input-activity-based On; input-activity based Off 877 + * bit 4 Auto: Input-activity-based On; input-activity based Off 878 + * bit 5 Auto: Input-activity-based On (illumination level 25%); input-activity based Off 879 + * bit 6 Auto: Input-activity-based On (illumination level 50%); input-activity based Off 880 + * bit 7 Auto: Input-activity-based On (illumination level 75%); input-activity based Off 881 + * bit 8 Auto: Input-activity-based On (illumination level 100%); input-activity based Off 882 + * bits 9-15 Reserved for future use 883 + * Note: Only One bit can be set 884 + * cbArg2, byte2 Desired auto keyboard illumination triggers. Must remain inactive to allow 885 + * keyboard to turn off automatically. 886 + * bit 0 Any keystroke 887 + * bit 1 Touchpad activity 888 + * bit 2 Pointing stick 889 + * bit 3 Any mouse 890 + * bits 4-7 Reserved for future use 891 + * cbArg2, byte3 Desired Timeout 892 + * bits 7:6 Timeout units indicator: 893 + * 00b Seconds 894 + * 01b Minutes 895 + * 10b Hours 896 + * 11b Days 897 + * bits 5:0 Timeout value (0-63) in sec/min/hr/day 898 + * cbArg3, byte0 Desired setting of ALS value that turns the light on or off. 899 + * cbArg3, byte2 Desired keyboard light level. 900 + */ 901 + 902 + 903 + enum kbd_timeout_unit { 904 + KBD_TIMEOUT_SECONDS = 0, 905 + KBD_TIMEOUT_MINUTES, 906 + KBD_TIMEOUT_HOURS, 907 + KBD_TIMEOUT_DAYS, 908 + }; 909 + 910 + enum kbd_mode_bit { 911 + KBD_MODE_BIT_OFF = 0, 912 + KBD_MODE_BIT_ON, 913 + KBD_MODE_BIT_ALS, 914 + KBD_MODE_BIT_TRIGGER_ALS, 915 + KBD_MODE_BIT_TRIGGER, 916 + KBD_MODE_BIT_TRIGGER_25, 917 + KBD_MODE_BIT_TRIGGER_50, 918 + KBD_MODE_BIT_TRIGGER_75, 919 + KBD_MODE_BIT_TRIGGER_100, 920 + }; 921 + 922 + #define kbd_is_als_mode_bit(bit) \ 923 + ((bit) == KBD_MODE_BIT_ALS || (bit) == KBD_MODE_BIT_TRIGGER_ALS) 924 + #define kbd_is_trigger_mode_bit(bit) \ 925 + ((bit) >= KBD_MODE_BIT_TRIGGER_ALS && (bit) <= KBD_MODE_BIT_TRIGGER_100) 926 + #define kbd_is_level_mode_bit(bit) \ 927 + ((bit) >= KBD_MODE_BIT_TRIGGER_25 && (bit) <= KBD_MODE_BIT_TRIGGER_100) 928 + 929 + struct kbd_info { 930 + u16 modes; 931 + u8 type; 932 + u8 triggers; 933 + u8 levels; 934 + u8 seconds; 935 + u8 minutes; 936 + u8 hours; 937 + u8 days; 938 + }; 939 + 940 + struct kbd_state { 941 + u8 mode_bit; 942 + u8 triggers; 943 + u8 timeout_value; 944 + u8 timeout_unit; 945 + u8 als_setting; 946 + u8 als_value; 947 + u8 level; 948 + }; 949 + 950 + static const int kbd_tokens[] = { 951 + KBD_LED_OFF_TOKEN, 952 + KBD_LED_AUTO_25_TOKEN, 953 + KBD_LED_AUTO_50_TOKEN, 954 + KBD_LED_AUTO_75_TOKEN, 955 + KBD_LED_AUTO_100_TOKEN, 956 + KBD_LED_ON_TOKEN, 957 + }; 958 + 959 + static u16 kbd_token_bits; 960 + 961 + static struct kbd_info kbd_info; 962 + static bool kbd_als_supported; 963 + static bool kbd_triggers_supported; 964 + 965 + static u8 kbd_mode_levels[16]; 966 + static int kbd_mode_levels_count; 967 + 968 + static u8 kbd_previous_level; 969 + static u8 kbd_previous_mode_bit; 970 + 971 + static bool kbd_led_present; 972 + 973 + /* 974 + * NOTE: there are three ways to set the keyboard backlight level. 975 + * First, via kbd_state.mode_bit (assigning KBD_MODE_BIT_TRIGGER_* value). 976 + * Second, via kbd_state.level (assigning numerical value <= kbd_info.levels). 977 + * Third, via SMBIOS tokens (KBD_LED_* in kbd_tokens) 978 + * 979 + * There are laptops which support only one of these methods. If we want to 980 + * support as many machines as possible we need to implement all three methods. 981 + * The first two methods use the kbd_state structure. The third uses SMBIOS 982 + * tokens. If kbd_info.levels == 0, the machine does not support setting the 983 + * keyboard backlight level via kbd_state.level. 984 + */ 985 + 986 + static int kbd_get_info(struct kbd_info *info) 987 + { 988 + u8 units; 989 + int ret; 990 + 991 + get_buffer(); 992 + 993 + buffer->input[0] = 0x0; 994 + dell_send_request(buffer, 4, 11); 995 + ret = buffer->output[0]; 996 + 997 + if (ret) { 998 + ret = dell_smi_error(ret); 999 + goto out; 1000 + } 1001 + 1002 + info->modes = buffer->output[1] & 0xFFFF; 1003 + info->type = (buffer->output[1] >> 24) & 0xFF; 1004 + info->triggers = buffer->output[2] & 0xFF; 1005 + units = (buffer->output[2] >> 8) & 0xFF; 1006 + info->levels = (buffer->output[2] >> 16) & 0xFF; 1007 + 1008 + if (units & BIT(0)) 1009 + info->seconds = (buffer->output[3] >> 0) & 0xFF; 1010 + if (units & BIT(1)) 1011 + info->minutes = (buffer->output[3] >> 8) & 0xFF; 1012 + if (units & BIT(2)) 1013 + info->hours = (buffer->output[3] >> 16) & 0xFF; 1014 + if (units & BIT(3)) 1015 + info->days = (buffer->output[3] >> 24) & 0xFF; 1016 + 1017 + out: 1018 + release_buffer(); 1019 + return ret; 1020 + } 1021 + 1022 + static unsigned int kbd_get_max_level(void) 1023 + { 1024 + if (kbd_info.levels != 0) 1025 + return kbd_info.levels; 1026 + if (kbd_mode_levels_count > 0) 1027 + return kbd_mode_levels_count - 1; 1028 + return 0; 1029 + } 1030 + 1031 + static int kbd_get_level(struct kbd_state *state) 1032 + { 1033 + int i; 1034 + 1035 + if (kbd_info.levels != 0) 1036 + return state->level; 1037 + 1038 + if (kbd_mode_levels_count > 0) { 1039 + for (i = 0; i < kbd_mode_levels_count; ++i) 1040 + if (kbd_mode_levels[i] == state->mode_bit) 1041 + return i; 1042 + return 0; 1043 + } 1044 + 1045 + return -EINVAL; 1046 + } 1047 + 1048 + static int kbd_set_level(struct kbd_state *state, u8 level) 1049 + { 1050 + if (kbd_info.levels != 0) { 1051 + if (level != 0) 1052 + kbd_previous_level = level; 1053 + if (state->level == level) 1054 + return 0; 1055 + state->level = level; 1056 + if (level != 0 && state->mode_bit == KBD_MODE_BIT_OFF) 1057 + state->mode_bit = kbd_previous_mode_bit; 1058 + else if (level == 0 && state->mode_bit != KBD_MODE_BIT_OFF) { 1059 + kbd_previous_mode_bit = state->mode_bit; 1060 + state->mode_bit = KBD_MODE_BIT_OFF; 1061 + } 1062 + return 0; 1063 + } 1064 + 1065 + if (kbd_mode_levels_count > 0 && level < kbd_mode_levels_count) { 1066 + if (level != 0) 1067 + kbd_previous_level = level; 1068 + state->mode_bit = kbd_mode_levels[level]; 1069 + return 0; 1070 + } 1071 + 1072 + return -EINVAL; 1073 + } 1074 + 1075 + static int kbd_get_state(struct kbd_state *state) 1076 + { 1077 + int ret; 1078 + 1079 + get_buffer(); 1080 + 1081 + buffer->input[0] = 0x1; 1082 + dell_send_request(buffer, 4, 11); 1083 + ret = buffer->output[0]; 1084 + 1085 + if (ret) { 1086 + ret = dell_smi_error(ret); 1087 + goto out; 1088 + } 1089 + 1090 + state->mode_bit = ffs(buffer->output[1] & 0xFFFF); 1091 + if (state->mode_bit != 0) 1092 + state->mode_bit--; 1093 + 1094 + state->triggers = (buffer->output[1] >> 16) & 0xFF; 1095 + state->timeout_value = (buffer->output[1] >> 24) & 0x3F; 1096 + state->timeout_unit = (buffer->output[1] >> 30) & 0x3; 1097 + state->als_setting = buffer->output[2] & 0xFF; 1098 + state->als_value = (buffer->output[2] >> 8) & 0xFF; 1099 + state->level = (buffer->output[2] >> 16) & 0xFF; 1100 + 1101 + out: 1102 + release_buffer(); 1103 + return ret; 1104 + } 1105 + 1106 + static int kbd_set_state(struct kbd_state *state) 1107 + { 1108 + int ret; 1109 + 1110 + get_buffer(); 1111 + buffer->input[0] = 0x2; 1112 + buffer->input[1] = BIT(state->mode_bit) & 0xFFFF; 1113 + buffer->input[1] |= (state->triggers & 0xFF) << 16; 1114 + buffer->input[1] |= (state->timeout_value & 0x3F) << 24; 1115 + buffer->input[1] |= (state->timeout_unit & 0x3) << 30; 1116 + buffer->input[2] = state->als_setting & 0xFF; 1117 + buffer->input[2] |= (state->level & 0xFF) << 16; 1118 + dell_send_request(buffer, 4, 11); 1119 + ret = buffer->output[0]; 1120 + release_buffer(); 1121 + 1122 + return dell_smi_error(ret); 1123 + } 1124 + 1125 + static int kbd_set_state_safe(struct kbd_state *state, struct kbd_state *old) 1126 + { 1127 + int ret; 1128 + 1129 + ret = kbd_set_state(state); 1130 + if (ret == 0) 1131 + return 0; 1132 + 1133 + /* 1134 + * When setting the new state fails,try to restore the previous one. 1135 + * This is needed on some machines where BIOS sets a default state when 1136 + * setting a new state fails. This default state could be all off. 1137 + */ 1138 + 1139 + if (kbd_set_state(old)) 1140 + pr_err("Setting old previous keyboard state failed\n"); 1141 + 1142 + return ret; 1143 + } 1144 + 1145 + static int kbd_set_token_bit(u8 bit) 1146 + { 1147 + int id; 1148 + int ret; 1149 + 1150 + if (bit >= ARRAY_SIZE(kbd_tokens)) 1151 + return -EINVAL; 1152 + 1153 + id = find_token_id(kbd_tokens[bit]); 1154 + if (id == -1) 1155 + return -EINVAL; 1156 + 1157 + get_buffer(); 1158 + buffer->input[0] = da_tokens[id].location; 1159 + buffer->input[1] = da_tokens[id].value; 1160 + dell_send_request(buffer, 1, 0); 1161 + ret = buffer->output[0]; 1162 + release_buffer(); 1163 + 1164 + return dell_smi_error(ret); 1165 + } 1166 + 1167 + static int kbd_get_token_bit(u8 bit) 1168 + { 1169 + int id; 1170 + int ret; 1171 + int val; 1172 + 1173 + if (bit >= ARRAY_SIZE(kbd_tokens)) 1174 + return -EINVAL; 1175 + 1176 + id = find_token_id(kbd_tokens[bit]); 1177 + if (id == -1) 1178 + return -EINVAL; 1179 + 1180 + get_buffer(); 1181 + buffer->input[0] = da_tokens[id].location; 1182 + dell_send_request(buffer, 0, 0); 1183 + ret = buffer->output[0]; 1184 + val = buffer->output[1]; 1185 + release_buffer(); 1186 + 1187 + if (ret) 1188 + return dell_smi_error(ret); 1189 + 1190 + return (val == da_tokens[id].value); 1191 + } 1192 + 1193 + static int kbd_get_first_active_token_bit(void) 1194 + { 1195 + int i; 1196 + int ret; 1197 + 1198 + for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i) { 1199 + ret = kbd_get_token_bit(i); 1200 + if (ret == 1) 1201 + return i; 1202 + } 1203 + 1204 + return ret; 1205 + } 1206 + 1207 + static int kbd_get_valid_token_counts(void) 1208 + { 1209 + return hweight16(kbd_token_bits); 1210 + } 1211 + 1212 + static inline int kbd_init_info(void) 1213 + { 1214 + struct kbd_state state; 1215 + int ret; 1216 + int i; 1217 + 1218 + ret = kbd_get_info(&kbd_info); 1219 + if (ret) 1220 + return ret; 1221 + 1222 + kbd_get_state(&state); 1223 + 1224 + /* NOTE: timeout value is stored in 6 bits so max value is 63 */ 1225 + if (kbd_info.seconds > 63) 1226 + kbd_info.seconds = 63; 1227 + if (kbd_info.minutes > 63) 1228 + kbd_info.minutes = 63; 1229 + if (kbd_info.hours > 63) 1230 + kbd_info.hours = 63; 1231 + if (kbd_info.days > 63) 1232 + kbd_info.days = 63; 1233 + 1234 + /* NOTE: On tested machines ON mode did not work and caused 1235 + * problems (turned backlight off) so do not use it 1236 + */ 1237 + kbd_info.modes &= ~BIT(KBD_MODE_BIT_ON); 1238 + 1239 + kbd_previous_level = kbd_get_level(&state); 1240 + kbd_previous_mode_bit = state.mode_bit; 1241 + 1242 + if (kbd_previous_level == 0 && kbd_get_max_level() != 0) 1243 + kbd_previous_level = 1; 1244 + 1245 + if (kbd_previous_mode_bit == KBD_MODE_BIT_OFF) { 1246 + kbd_previous_mode_bit = 1247 + ffs(kbd_info.modes & ~BIT(KBD_MODE_BIT_OFF)); 1248 + if (kbd_previous_mode_bit != 0) 1249 + kbd_previous_mode_bit--; 1250 + } 1251 + 1252 + if (kbd_info.modes & (BIT(KBD_MODE_BIT_ALS) | 1253 + BIT(KBD_MODE_BIT_TRIGGER_ALS))) 1254 + kbd_als_supported = true; 1255 + 1256 + if (kbd_info.modes & ( 1257 + BIT(KBD_MODE_BIT_TRIGGER_ALS) | BIT(KBD_MODE_BIT_TRIGGER) | 1258 + BIT(KBD_MODE_BIT_TRIGGER_25) | BIT(KBD_MODE_BIT_TRIGGER_50) | 1259 + BIT(KBD_MODE_BIT_TRIGGER_75) | BIT(KBD_MODE_BIT_TRIGGER_100) 1260 + )) 1261 + kbd_triggers_supported = true; 1262 + 1263 + /* kbd_mode_levels[0] is reserved, see below */ 1264 + for (i = 0; i < 16; ++i) 1265 + if (kbd_is_level_mode_bit(i) && (BIT(i) & kbd_info.modes)) 1266 + kbd_mode_levels[1 + kbd_mode_levels_count++] = i; 1267 + 1268 + /* 1269 + * Find the first supported mode and assign to kbd_mode_levels[0]. 1270 + * This should be 0 (off), but we cannot depend on the BIOS to 1271 + * support 0. 1272 + */ 1273 + if (kbd_mode_levels_count > 0) { 1274 + for (i = 0; i < 16; ++i) { 1275 + if (BIT(i) & kbd_info.modes) { 1276 + kbd_mode_levels[0] = i; 1277 + break; 1278 + } 1279 + } 1280 + kbd_mode_levels_count++; 1281 + } 1282 + 1283 + return 0; 1284 + 1285 + } 1286 + 1287 + static inline void kbd_init_tokens(void) 1288 + { 1289 + int i; 1290 + 1291 + for (i = 0; i < ARRAY_SIZE(kbd_tokens); ++i) 1292 + if (find_token_id(kbd_tokens[i]) != -1) 1293 + kbd_token_bits |= BIT(i); 1294 + } 1295 + 1296 + static void kbd_init(void) 1297 + { 1298 + int ret; 1299 + 1300 + ret = kbd_init_info(); 1301 + kbd_init_tokens(); 1302 + 1303 + if (kbd_token_bits != 0 || ret == 0) 1304 + kbd_led_present = true; 1305 + } 1306 + 1307 + static ssize_t kbd_led_timeout_store(struct device *dev, 1308 + struct device_attribute *attr, 1309 + const char *buf, size_t count) 1310 + { 1311 + struct kbd_state new_state; 1312 + struct kbd_state state; 1313 + bool convert; 1314 + int value; 1315 + int ret; 1316 + char ch; 1317 + u8 unit; 1318 + int i; 1319 + 1320 + ret = sscanf(buf, "%d %c", &value, &ch); 1321 + if (ret < 1) 1322 + return -EINVAL; 1323 + else if (ret == 1) 1324 + ch = 's'; 1325 + 1326 + if (value < 0) 1327 + return -EINVAL; 1328 + 1329 + convert = false; 1330 + 1331 + switch (ch) { 1332 + case 's': 1333 + if (value > kbd_info.seconds) 1334 + convert = true; 1335 + unit = KBD_TIMEOUT_SECONDS; 1336 + break; 1337 + case 'm': 1338 + if (value > kbd_info.minutes) 1339 + convert = true; 1340 + unit = KBD_TIMEOUT_MINUTES; 1341 + break; 1342 + case 'h': 1343 + if (value > kbd_info.hours) 1344 + convert = true; 1345 + unit = KBD_TIMEOUT_HOURS; 1346 + break; 1347 + case 'd': 1348 + if (value > kbd_info.days) 1349 + convert = true; 1350 + unit = KBD_TIMEOUT_DAYS; 1351 + break; 1352 + default: 1353 + return -EINVAL; 1354 + } 1355 + 1356 + if (quirks && quirks->needs_kbd_timeouts) 1357 + convert = true; 1358 + 1359 + if (convert) { 1360 + /* Convert value from current units to seconds */ 1361 + switch (unit) { 1362 + case KBD_TIMEOUT_DAYS: 1363 + value *= 24; 1364 + case KBD_TIMEOUT_HOURS: 1365 + value *= 60; 1366 + case KBD_TIMEOUT_MINUTES: 1367 + value *= 60; 1368 + unit = KBD_TIMEOUT_SECONDS; 1369 + } 1370 + 1371 + if (quirks && quirks->needs_kbd_timeouts) { 1372 + for (i = 0; quirks->kbd_timeouts[i] != -1; i++) { 1373 + if (value <= quirks->kbd_timeouts[i]) { 1374 + value = quirks->kbd_timeouts[i]; 1375 + break; 1376 + } 1377 + } 1378 + } 1379 + 1380 + if (value <= kbd_info.seconds && kbd_info.seconds) { 1381 + unit = KBD_TIMEOUT_SECONDS; 1382 + } else if (value / 60 <= kbd_info.minutes && kbd_info.minutes) { 1383 + value /= 60; 1384 + unit = KBD_TIMEOUT_MINUTES; 1385 + } else if (value / (60 * 60) <= kbd_info.hours && kbd_info.hours) { 1386 + value /= (60 * 60); 1387 + unit = KBD_TIMEOUT_HOURS; 1388 + } else if (value / (60 * 60 * 24) <= kbd_info.days && kbd_info.days) { 1389 + value /= (60 * 60 * 24); 1390 + unit = KBD_TIMEOUT_DAYS; 1391 + } else { 1392 + return -EINVAL; 1393 + } 1394 + } 1395 + 1396 + ret = kbd_get_state(&state); 1397 + if (ret) 1398 + return ret; 1399 + 1400 + new_state = state; 1401 + new_state.timeout_value = value; 1402 + new_state.timeout_unit = unit; 1403 + 1404 + ret = kbd_set_state_safe(&new_state, &state); 1405 + if (ret) 1406 + return ret; 1407 + 1408 + return count; 1409 + } 1410 + 1411 + static ssize_t kbd_led_timeout_show(struct device *dev, 1412 + struct device_attribute *attr, char *buf) 1413 + { 1414 + struct kbd_state state; 1415 + int ret; 1416 + int len; 1417 + 1418 + ret = kbd_get_state(&state); 1419 + if (ret) 1420 + return ret; 1421 + 1422 + len = sprintf(buf, "%d", state.timeout_value); 1423 + 1424 + switch (state.timeout_unit) { 1425 + case KBD_TIMEOUT_SECONDS: 1426 + return len + sprintf(buf+len, "s\n"); 1427 + case KBD_TIMEOUT_MINUTES: 1428 + return len + sprintf(buf+len, "m\n"); 1429 + case KBD_TIMEOUT_HOURS: 1430 + return len + sprintf(buf+len, "h\n"); 1431 + case KBD_TIMEOUT_DAYS: 1432 + return len + sprintf(buf+len, "d\n"); 1433 + default: 1434 + return -EINVAL; 1435 + } 1436 + 1437 + return len; 1438 + } 1439 + 1440 + static DEVICE_ATTR(stop_timeout, S_IRUGO | S_IWUSR, 1441 + kbd_led_timeout_show, kbd_led_timeout_store); 1442 + 1443 + static const char * const kbd_led_triggers[] = { 1444 + "keyboard", 1445 + "touchpad", 1446 + /*"trackstick"*/ NULL, /* NOTE: trackstick is just alias for touchpad */ 1447 + "mouse", 1448 + }; 1449 + 1450 + static ssize_t kbd_led_triggers_store(struct device *dev, 1451 + struct device_attribute *attr, 1452 + const char *buf, size_t count) 1453 + { 1454 + struct kbd_state new_state; 1455 + struct kbd_state state; 1456 + bool triggers_enabled = false; 1457 + int trigger_bit = -1; 1458 + char trigger[21]; 1459 + int i, ret; 1460 + 1461 + ret = sscanf(buf, "%20s", trigger); 1462 + if (ret != 1) 1463 + return -EINVAL; 1464 + 1465 + if (trigger[0] != '+' && trigger[0] != '-') 1466 + return -EINVAL; 1467 + 1468 + ret = kbd_get_state(&state); 1469 + if (ret) 1470 + return ret; 1471 + 1472 + if (kbd_triggers_supported) 1473 + triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit); 1474 + 1475 + if (kbd_triggers_supported) { 1476 + for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) { 1477 + if (!(kbd_info.triggers & BIT(i))) 1478 + continue; 1479 + if (!kbd_led_triggers[i]) 1480 + continue; 1481 + if (strcmp(trigger+1, kbd_led_triggers[i]) != 0) 1482 + continue; 1483 + if (trigger[0] == '+' && 1484 + triggers_enabled && (state.triggers & BIT(i))) 1485 + return count; 1486 + if (trigger[0] == '-' && 1487 + (!triggers_enabled || !(state.triggers & BIT(i)))) 1488 + return count; 1489 + trigger_bit = i; 1490 + break; 1491 + } 1492 + } 1493 + 1494 + if (trigger_bit != -1) { 1495 + new_state = state; 1496 + if (trigger[0] == '+') 1497 + new_state.triggers |= BIT(trigger_bit); 1498 + else { 1499 + new_state.triggers &= ~BIT(trigger_bit); 1500 + /* NOTE: trackstick bit (2) must be disabled when 1501 + * disabling touchpad bit (1), otherwise touchpad 1502 + * bit (1) will not be disabled */ 1503 + if (trigger_bit == 1) 1504 + new_state.triggers &= ~BIT(2); 1505 + } 1506 + if ((kbd_info.triggers & new_state.triggers) != 1507 + new_state.triggers) 1508 + return -EINVAL; 1509 + if (new_state.triggers && !triggers_enabled) { 1510 + new_state.mode_bit = KBD_MODE_BIT_TRIGGER; 1511 + kbd_set_level(&new_state, kbd_previous_level); 1512 + } else if (new_state.triggers == 0) { 1513 + kbd_set_level(&new_state, 0); 1514 + } 1515 + if (!(kbd_info.modes & BIT(new_state.mode_bit))) 1516 + return -EINVAL; 1517 + ret = kbd_set_state_safe(&new_state, &state); 1518 + if (ret) 1519 + return ret; 1520 + if (new_state.mode_bit != KBD_MODE_BIT_OFF) 1521 + kbd_previous_mode_bit = new_state.mode_bit; 1522 + return count; 1523 + } 1524 + 1525 + return -EINVAL; 1526 + } 1527 + 1528 + static ssize_t kbd_led_triggers_show(struct device *dev, 1529 + struct device_attribute *attr, char *buf) 1530 + { 1531 + struct kbd_state state; 1532 + bool triggers_enabled; 1533 + int level, i, ret; 1534 + int len = 0; 1535 + 1536 + ret = kbd_get_state(&state); 1537 + if (ret) 1538 + return ret; 1539 + 1540 + len = 0; 1541 + 1542 + if (kbd_triggers_supported) { 1543 + triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit); 1544 + level = kbd_get_level(&state); 1545 + for (i = 0; i < ARRAY_SIZE(kbd_led_triggers); ++i) { 1546 + if (!(kbd_info.triggers & BIT(i))) 1547 + continue; 1548 + if (!kbd_led_triggers[i]) 1549 + continue; 1550 + if ((triggers_enabled || level <= 0) && 1551 + (state.triggers & BIT(i))) 1552 + buf[len++] = '+'; 1553 + else 1554 + buf[len++] = '-'; 1555 + len += sprintf(buf+len, "%s ", kbd_led_triggers[i]); 1556 + } 1557 + } 1558 + 1559 + if (len) 1560 + buf[len - 1] = '\n'; 1561 + 1562 + return len; 1563 + } 1564 + 1565 + static DEVICE_ATTR(start_triggers, S_IRUGO | S_IWUSR, 1566 + kbd_led_triggers_show, kbd_led_triggers_store); 1567 + 1568 + static ssize_t kbd_led_als_enabled_store(struct device *dev, 1569 + struct device_attribute *attr, 1570 + const char *buf, size_t count) 1571 + { 1572 + struct kbd_state new_state; 1573 + struct kbd_state state; 1574 + bool triggers_enabled = false; 1575 + int enable; 1576 + int ret; 1577 + 1578 + ret = kstrtoint(buf, 0, &enable); 1579 + if (ret) 1580 + return ret; 1581 + 1582 + ret = kbd_get_state(&state); 1583 + if (ret) 1584 + return ret; 1585 + 1586 + if (enable == kbd_is_als_mode_bit(state.mode_bit)) 1587 + return count; 1588 + 1589 + new_state = state; 1590 + 1591 + if (kbd_triggers_supported) 1592 + triggers_enabled = kbd_is_trigger_mode_bit(state.mode_bit); 1593 + 1594 + if (enable) { 1595 + if (triggers_enabled) 1596 + new_state.mode_bit = KBD_MODE_BIT_TRIGGER_ALS; 1597 + else 1598 + new_state.mode_bit = KBD_MODE_BIT_ALS; 1599 + } else { 1600 + if (triggers_enabled) { 1601 + new_state.mode_bit = KBD_MODE_BIT_TRIGGER; 1602 + kbd_set_level(&new_state, kbd_previous_level); 1603 + } else { 1604 + new_state.mode_bit = KBD_MODE_BIT_ON; 1605 + } 1606 + } 1607 + if (!(kbd_info.modes & BIT(new_state.mode_bit))) 1608 + return -EINVAL; 1609 + 1610 + ret = kbd_set_state_safe(&new_state, &state); 1611 + if (ret) 1612 + return ret; 1613 + kbd_previous_mode_bit = new_state.mode_bit; 1614 + 1615 + return count; 1616 + } 1617 + 1618 + static ssize_t kbd_led_als_enabled_show(struct device *dev, 1619 + struct device_attribute *attr, 1620 + char *buf) 1621 + { 1622 + struct kbd_state state; 1623 + bool enabled = false; 1624 + int ret; 1625 + 1626 + ret = kbd_get_state(&state); 1627 + if (ret) 1628 + return ret; 1629 + enabled = kbd_is_als_mode_bit(state.mode_bit); 1630 + 1631 + return sprintf(buf, "%d\n", enabled ? 1 : 0); 1632 + } 1633 + 1634 + static DEVICE_ATTR(als_enabled, S_IRUGO | S_IWUSR, 1635 + kbd_led_als_enabled_show, kbd_led_als_enabled_store); 1636 + 1637 + static ssize_t kbd_led_als_setting_store(struct device *dev, 1638 + struct device_attribute *attr, 1639 + const char *buf, size_t count) 1640 + { 1641 + struct kbd_state state; 1642 + struct kbd_state new_state; 1643 + u8 setting; 1644 + int ret; 1645 + 1646 + ret = kstrtou8(buf, 10, &setting); 1647 + if (ret) 1648 + return ret; 1649 + 1650 + ret = kbd_get_state(&state); 1651 + if (ret) 1652 + return ret; 1653 + 1654 + new_state = state; 1655 + new_state.als_setting = setting; 1656 + 1657 + ret = kbd_set_state_safe(&new_state, &state); 1658 + if (ret) 1659 + return ret; 1660 + 1661 + return count; 1662 + } 1663 + 1664 + static ssize_t kbd_led_als_setting_show(struct device *dev, 1665 + struct device_attribute *attr, 1666 + char *buf) 1667 + { 1668 + struct kbd_state state; 1669 + int ret; 1670 + 1671 + ret = kbd_get_state(&state); 1672 + if (ret) 1673 + return ret; 1674 + 1675 + return sprintf(buf, "%d\n", state.als_setting); 1676 + } 1677 + 1678 + static DEVICE_ATTR(als_setting, S_IRUGO | S_IWUSR, 1679 + kbd_led_als_setting_show, kbd_led_als_setting_store); 1680 + 1681 + static struct attribute *kbd_led_attrs[] = { 1682 + &dev_attr_stop_timeout.attr, 1683 + &dev_attr_start_triggers.attr, 1684 + NULL, 1685 + }; 1686 + 1687 + static const struct attribute_group kbd_led_group = { 1688 + .attrs = kbd_led_attrs, 1689 + }; 1690 + 1691 + static struct attribute *kbd_led_als_attrs[] = { 1692 + &dev_attr_als_enabled.attr, 1693 + &dev_attr_als_setting.attr, 1694 + NULL, 1695 + }; 1696 + 1697 + static const struct attribute_group kbd_led_als_group = { 1698 + .attrs = kbd_led_als_attrs, 1699 + }; 1700 + 1701 + static const struct attribute_group *kbd_led_groups[] = { 1702 + &kbd_led_group, 1703 + &kbd_led_als_group, 1704 + NULL, 1705 + }; 1706 + 1707 + static enum led_brightness kbd_led_level_get(struct led_classdev *led_cdev) 1708 + { 1709 + int ret; 1710 + u16 num; 1711 + struct kbd_state state; 1712 + 1713 + if (kbd_get_max_level()) { 1714 + ret = kbd_get_state(&state); 1715 + if (ret) 1716 + return 0; 1717 + ret = kbd_get_level(&state); 1718 + if (ret < 0) 1719 + return 0; 1720 + return ret; 1721 + } 1722 + 1723 + if (kbd_get_valid_token_counts()) { 1724 + ret = kbd_get_first_active_token_bit(); 1725 + if (ret < 0) 1726 + return 0; 1727 + for (num = kbd_token_bits; num != 0 && ret > 0; --ret) 1728 + num &= num - 1; /* clear the first bit set */ 1729 + if (num == 0) 1730 + return 0; 1731 + return ffs(num) - 1; 1732 + } 1733 + 1734 + pr_warn("Keyboard brightness level control not supported\n"); 1735 + return 0; 1736 + } 1737 + 1738 + static void kbd_led_level_set(struct led_classdev *led_cdev, 1739 + enum led_brightness value) 1740 + { 1741 + struct kbd_state state; 1742 + struct kbd_state new_state; 1743 + u16 num; 1744 + 1745 + if (kbd_get_max_level()) { 1746 + if (kbd_get_state(&state)) 1747 + return; 1748 + new_state = state; 1749 + if (kbd_set_level(&new_state, value)) 1750 + return; 1751 + kbd_set_state_safe(&new_state, &state); 1752 + return; 1753 + } 1754 + 1755 + if (kbd_get_valid_token_counts()) { 1756 + for (num = kbd_token_bits; num != 0 && value > 0; --value) 1757 + num &= num - 1; /* clear the first bit set */ 1758 + if (num == 0) 1759 + return; 1760 + kbd_set_token_bit(ffs(num) - 1); 1761 + return; 1762 + } 1763 + 1764 + pr_warn("Keyboard brightness level control not supported\n"); 1765 + } 1766 + 1767 + static struct led_classdev kbd_led = { 1768 + .name = "dell::kbd_backlight", 1769 + .brightness_set = kbd_led_level_set, 1770 + .brightness_get = kbd_led_level_get, 1771 + .groups = kbd_led_groups, 1772 + }; 1773 + 1774 + static int __init kbd_led_init(struct device *dev) 1775 + { 1776 + kbd_init(); 1777 + if (!kbd_led_present) 1778 + return -ENODEV; 1779 + if (!kbd_als_supported) 1780 + kbd_led_groups[1] = NULL; 1781 + kbd_led.max_brightness = kbd_get_max_level(); 1782 + if (!kbd_led.max_brightness) { 1783 + kbd_led.max_brightness = kbd_get_valid_token_counts(); 1784 + if (kbd_led.max_brightness) 1785 + kbd_led.max_brightness--; 1786 + } 1787 + return led_classdev_register(dev, &kbd_led); 1788 + } 1789 + 1790 + static void brightness_set_exit(struct led_classdev *led_cdev, 1791 + enum led_brightness value) 1792 + { 1793 + /* Don't change backlight level on exit */ 1794 + }; 1795 + 1796 + static void kbd_led_exit(void) 1797 + { 1798 + if (!kbd_led_present) 1799 + return; 1800 + kbd_led.brightness_set = brightness_set_exit; 1801 + led_classdev_unregister(&kbd_led); 850 1802 } 851 1803 852 1804 static int __init dell_init(void) ··· 1912 840 1913 841 if (quirks && quirks->touchpad_led) 1914 842 touchpad_led_init(&platform_device->dev); 843 + 844 + kbd_led_init(&platform_device->dev); 1915 845 1916 846 dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); 1917 847 if (dell_laptop_dir != NULL) ··· 1982 908 debugfs_remove_recursive(dell_laptop_dir); 1983 909 if (quirks && quirks->touchpad_led) 1984 910 touchpad_led_exit(); 911 + kbd_led_exit(); 1985 912 i8042_remove_filter(dell_laptop_i8042_filter); 1986 913 cancel_delayed_work_sync(&dell_rfkill_work); 1987 914 backlight_device_unregister(dell_backlight_device); ··· 1999 924 module_exit(dell_exit); 2000 925 2001 926 MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>"); 927 + MODULE_AUTHOR("Gabriele Mazzotta <gabriele.mzt@gmail.com>"); 928 + MODULE_AUTHOR("Pali Rohár <pali.rohar@gmail.com>"); 2002 929 MODULE_DESCRIPTION("Dell laptop driver"); 2003 930 MODULE_LICENSE("GPL");