diff options
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/Kconfig | 17 | ||||
-rw-r--r-- | drivers/platform/x86/acer-wmi.c | 488 | ||||
-rw-r--r-- | drivers/platform/x86/asus-laptop.c | 378 | ||||
-rw-r--r-- | drivers/platform/x86/asus-wmi.c | 4 | ||||
-rw-r--r-- | drivers/platform/x86/dell-laptop.c | 84 | ||||
-rw-r--r-- | drivers/platform/x86/eeepc-laptop.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/hp_accel.c | 15 | ||||
-rw-r--r-- | drivers/platform/x86/ideapad-laptop.c | 251 | ||||
-rw-r--r-- | drivers/platform/x86/intel_scu_ipcutil.c | 8 | ||||
-rw-r--r-- | drivers/platform/x86/samsung-laptop.c | 107 | ||||
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 2 | ||||
-rw-r--r-- | drivers/platform/x86/topstar-laptop.c | 1 | ||||
-rw-r--r-- | drivers/platform/x86/toshiba_acpi.c | 641 | ||||
-rw-r--r-- | drivers/platform/x86/wmi.c | 6 |
14 files changed, 1401 insertions, 603 deletions
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 10cf2500522b..f4e3d82379d7 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
@@ -61,15 +61,18 @@ config ASUS_LAPTOP | |||
61 | depends on INPUT | 61 | depends on INPUT |
62 | depends on RFKILL || RFKILL = n | 62 | depends on RFKILL || RFKILL = n |
63 | select INPUT_SPARSEKMAP | 63 | select INPUT_SPARSEKMAP |
64 | select INPUT_POLLDEV | ||
64 | ---help--- | 65 | ---help--- |
65 | This is the new Linux driver for Asus laptops. It may also support some | 66 | This is a driver for Asus laptops, Lenovo SL and the Pegatron |
66 | MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate | 67 | Lucid tablet. It may also support some MEDION, JVC or VICTOR |
67 | standard ACPI events and input events. It also adds | 68 | laptops. It makes all the extra buttons generate standard |
68 | support for video output switching, LCD backlight control, Bluetooth and | 69 | ACPI events and input events, and on the Lucid the built-in |
69 | Wlan control, and most importantly, allows you to blink those fancy LEDs. | 70 | accelerometer appears as an input device. It also adds |
71 | support for video output switching, LCD backlight control, | ||
72 | Bluetooth and Wlan control, and most importantly, allows you | ||
73 | to blink those fancy LEDs. | ||
70 | 74 | ||
71 | For more information and a userspace daemon for handling the extra | 75 | For more information see <http://acpi4asus.sf.net>. |
72 | buttons see <http://acpi4asus.sf.net>. | ||
73 | 76 | ||
74 | If you have an ACPI-compatible ASUS laptop, say Y or M here. | 77 | If you have an ACPI-compatible ASUS laptop, say Y or M here. |
75 | 78 | ||
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index af2bb20cb2fb..b848277171a4 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c | |||
@@ -190,6 +190,7 @@ enum interface_flags { | |||
190 | ACER_AMW0, | 190 | ACER_AMW0, |
191 | ACER_AMW0_V2, | 191 | ACER_AMW0_V2, |
192 | ACER_WMID, | 192 | ACER_WMID, |
193 | ACER_WMID_v2, | ||
193 | }; | 194 | }; |
194 | 195 | ||
195 | #define ACER_DEFAULT_WIRELESS 0 | 196 | #define ACER_DEFAULT_WIRELESS 0 |
@@ -205,6 +206,7 @@ static int threeg = -1; | |||
205 | static int force_series; | 206 | static int force_series; |
206 | static bool ec_raw_mode; | 207 | static bool ec_raw_mode; |
207 | static bool has_type_aa; | 208 | static bool has_type_aa; |
209 | static u16 commun_func_bitmap; | ||
208 | 210 | ||
209 | module_param(mailled, int, 0444); | 211 | module_param(mailled, int, 0444); |
210 | module_param(brightness, int, 0444); | 212 | module_param(brightness, int, 0444); |
@@ -464,6 +466,15 @@ static struct dmi_system_id acer_quirks[] = { | |||
464 | }, | 466 | }, |
465 | .driver_data = &quirk_lenovo_ideapad_s205, | 467 | .driver_data = &quirk_lenovo_ideapad_s205, |
466 | }, | 468 | }, |
469 | { | ||
470 | .callback = dmi_matched, | ||
471 | .ident = "Lenovo 3000 N200", | ||
472 | .matches = { | ||
473 | DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), | ||
474 | DMI_MATCH(DMI_PRODUCT_NAME, "0687A31"), | ||
475 | }, | ||
476 | .driver_data = &quirk_fujitsu_amilo_li_1718, | ||
477 | }, | ||
467 | {} | 478 | {} |
468 | }; | 479 | }; |
469 | 480 | ||
@@ -868,6 +879,174 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) | |||
868 | return WMI_execute_u32(method_id, (u32)value, NULL); | 879 | return WMI_execute_u32(method_id, (u32)value, NULL); |
869 | } | 880 | } |
870 | 881 | ||
882 | static acpi_status wmid3_get_device_status(u32 *value, u16 device) | ||
883 | { | ||
884 | struct wmid3_gds_return_value return_value; | ||
885 | acpi_status status; | ||
886 | union acpi_object *obj; | ||
887 | struct wmid3_gds_input_param params = { | ||
888 | .function_num = 0x1, | ||
889 | .hotkey_number = 0x01, | ||
890 | .devices = device, | ||
891 | }; | ||
892 | struct acpi_buffer input = { | ||
893 | sizeof(struct wmid3_gds_input_param), | ||
894 | ¶ms | ||
895 | }; | ||
896 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
897 | |||
898 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); | ||
899 | if (ACPI_FAILURE(status)) | ||
900 | return status; | ||
901 | |||
902 | obj = output.pointer; | ||
903 | |||
904 | if (!obj) | ||
905 | return AE_ERROR; | ||
906 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
907 | kfree(obj); | ||
908 | return AE_ERROR; | ||
909 | } | ||
910 | if (obj->buffer.length != 8) { | ||
911 | pr_warn("Unknown buffer length %d\n", obj->buffer.length); | ||
912 | kfree(obj); | ||
913 | return AE_ERROR; | ||
914 | } | ||
915 | |||
916 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
917 | kfree(obj); | ||
918 | |||
919 | if (return_value.error_code || return_value.ec_return_value) | ||
920 | pr_warn("Get 0x%x Device Status failed: 0x%x - 0x%x\n", | ||
921 | device, | ||
922 | return_value.error_code, | ||
923 | return_value.ec_return_value); | ||
924 | else | ||
925 | *value = !!(return_value.devices & device); | ||
926 | |||
927 | return status; | ||
928 | } | ||
929 | |||
930 | static acpi_status wmid_v2_get_u32(u32 *value, u32 cap) | ||
931 | { | ||
932 | u16 device; | ||
933 | |||
934 | switch (cap) { | ||
935 | case ACER_CAP_WIRELESS: | ||
936 | device = ACER_WMID3_GDS_WIRELESS; | ||
937 | break; | ||
938 | case ACER_CAP_BLUETOOTH: | ||
939 | device = ACER_WMID3_GDS_BLUETOOTH; | ||
940 | break; | ||
941 | case ACER_CAP_THREEG: | ||
942 | device = ACER_WMID3_GDS_THREEG; | ||
943 | break; | ||
944 | default: | ||
945 | return AE_ERROR; | ||
946 | } | ||
947 | return wmid3_get_device_status(value, device); | ||
948 | } | ||
949 | |||
950 | static acpi_status wmid3_set_device_status(u32 value, u16 device) | ||
951 | { | ||
952 | struct wmid3_gds_return_value return_value; | ||
953 | acpi_status status; | ||
954 | union acpi_object *obj; | ||
955 | u16 devices; | ||
956 | struct wmid3_gds_input_param params = { | ||
957 | .function_num = 0x1, | ||
958 | .hotkey_number = 0x01, | ||
959 | .devices = commun_func_bitmap, | ||
960 | }; | ||
961 | struct acpi_buffer input = { | ||
962 | sizeof(struct wmid3_gds_input_param), | ||
963 | ¶ms | ||
964 | }; | ||
965 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
966 | struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
967 | |||
968 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); | ||
969 | if (ACPI_FAILURE(status)) | ||
970 | return status; | ||
971 | |||
972 | obj = output.pointer; | ||
973 | |||
974 | if (!obj) | ||
975 | return AE_ERROR; | ||
976 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
977 | kfree(obj); | ||
978 | return AE_ERROR; | ||
979 | } | ||
980 | if (obj->buffer.length != 8) { | ||
981 | pr_warning("Unknown buffer length %d\n", obj->buffer.length); | ||
982 | kfree(obj); | ||
983 | return AE_ERROR; | ||
984 | } | ||
985 | |||
986 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
987 | kfree(obj); | ||
988 | |||
989 | if (return_value.error_code || return_value.ec_return_value) { | ||
990 | pr_warning("Get Current Device Status failed: " | ||
991 | "0x%x - 0x%x\n", return_value.error_code, | ||
992 | return_value.ec_return_value); | ||
993 | return status; | ||
994 | } | ||
995 | |||
996 | devices = return_value.devices; | ||
997 | params.function_num = 0x2; | ||
998 | params.hotkey_number = 0x01; | ||
999 | params.devices = (value) ? (devices | device) : (devices & ~device); | ||
1000 | |||
1001 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2); | ||
1002 | if (ACPI_FAILURE(status)) | ||
1003 | return status; | ||
1004 | |||
1005 | obj = output2.pointer; | ||
1006 | |||
1007 | if (!obj) | ||
1008 | return AE_ERROR; | ||
1009 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1010 | kfree(obj); | ||
1011 | return AE_ERROR; | ||
1012 | } | ||
1013 | if (obj->buffer.length != 4) { | ||
1014 | pr_warning("Unknown buffer length %d\n", obj->buffer.length); | ||
1015 | kfree(obj); | ||
1016 | return AE_ERROR; | ||
1017 | } | ||
1018 | |||
1019 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
1020 | kfree(obj); | ||
1021 | |||
1022 | if (return_value.error_code || return_value.ec_return_value) | ||
1023 | pr_warning("Set Device Status failed: " | ||
1024 | "0x%x - 0x%x\n", return_value.error_code, | ||
1025 | return_value.ec_return_value); | ||
1026 | |||
1027 | return status; | ||
1028 | } | ||
1029 | |||
1030 | static acpi_status wmid_v2_set_u32(u32 value, u32 cap) | ||
1031 | { | ||
1032 | u16 device; | ||
1033 | |||
1034 | switch (cap) { | ||
1035 | case ACER_CAP_WIRELESS: | ||
1036 | device = ACER_WMID3_GDS_WIRELESS; | ||
1037 | break; | ||
1038 | case ACER_CAP_BLUETOOTH: | ||
1039 | device = ACER_WMID3_GDS_BLUETOOTH; | ||
1040 | break; | ||
1041 | case ACER_CAP_THREEG: | ||
1042 | device = ACER_WMID3_GDS_THREEG; | ||
1043 | break; | ||
1044 | default: | ||
1045 | return AE_ERROR; | ||
1046 | } | ||
1047 | return wmid3_set_device_status(value, device); | ||
1048 | } | ||
1049 | |||
871 | static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) | 1050 | static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) |
872 | { | 1051 | { |
873 | struct hotkey_function_type_aa *type_aa; | 1052 | struct hotkey_function_type_aa *type_aa; |
@@ -881,6 +1060,7 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) | |||
881 | 1060 | ||
882 | pr_info("Function bitmap for Communication Button: 0x%x\n", | 1061 | pr_info("Function bitmap for Communication Button: 0x%x\n", |
883 | type_aa->commun_func_bitmap); | 1062 | type_aa->commun_func_bitmap); |
1063 | commun_func_bitmap = type_aa->commun_func_bitmap; | ||
884 | 1064 | ||
885 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) | 1065 | if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) |
886 | interface->capability |= ACER_CAP_WIRELESS; | 1066 | interface->capability |= ACER_CAP_WIRELESS; |
@@ -913,17 +1093,13 @@ static acpi_status WMID_set_capabilities(void) | |||
913 | return AE_ERROR; | 1093 | return AE_ERROR; |
914 | } | 1094 | } |
915 | 1095 | ||
916 | dmi_walk(type_aa_dmi_decode, NULL); | 1096 | pr_info("Function bitmap for Communication Device: 0x%x\n", devices); |
917 | if (!has_type_aa) { | 1097 | if (devices & 0x07) |
918 | interface->capability |= ACER_CAP_WIRELESS; | 1098 | interface->capability |= ACER_CAP_WIRELESS; |
919 | if (devices & 0x40) | 1099 | if (devices & 0x40) |
920 | interface->capability |= ACER_CAP_THREEG; | 1100 | interface->capability |= ACER_CAP_THREEG; |
921 | if (devices & 0x10) | 1101 | if (devices & 0x10) |
922 | interface->capability |= ACER_CAP_BLUETOOTH; | 1102 | interface->capability |= ACER_CAP_BLUETOOTH; |
923 | } | ||
924 | |||
925 | /* WMID always provides brightness methods */ | ||
926 | interface->capability |= ACER_CAP_BRIGHTNESS; | ||
927 | 1103 | ||
928 | if (!(devices & 0x20)) | 1104 | if (!(devices & 0x20)) |
929 | max_brightness = 0x9; | 1105 | max_brightness = 0x9; |
@@ -936,6 +1112,10 @@ static struct wmi_interface wmid_interface = { | |||
936 | .type = ACER_WMID, | 1112 | .type = ACER_WMID, |
937 | }; | 1113 | }; |
938 | 1114 | ||
1115 | static struct wmi_interface wmid_v2_interface = { | ||
1116 | .type = ACER_WMID_v2, | ||
1117 | }; | ||
1118 | |||
939 | /* | 1119 | /* |
940 | * Generic Device (interface-independent) | 1120 | * Generic Device (interface-independent) |
941 | */ | 1121 | */ |
@@ -956,6 +1136,14 @@ static acpi_status get_u32(u32 *value, u32 cap) | |||
956 | case ACER_WMID: | 1136 | case ACER_WMID: |
957 | status = WMID_get_u32(value, cap, interface); | 1137 | status = WMID_get_u32(value, cap, interface); |
958 | break; | 1138 | break; |
1139 | case ACER_WMID_v2: | ||
1140 | if (cap & (ACER_CAP_WIRELESS | | ||
1141 | ACER_CAP_BLUETOOTH | | ||
1142 | ACER_CAP_THREEG)) | ||
1143 | status = wmid_v2_get_u32(value, cap); | ||
1144 | else if (wmi_has_guid(WMID_GUID2)) | ||
1145 | status = WMID_get_u32(value, cap, interface); | ||
1146 | break; | ||
959 | } | 1147 | } |
960 | 1148 | ||
961 | return status; | 1149 | return status; |
@@ -989,6 +1177,13 @@ static acpi_status set_u32(u32 value, u32 cap) | |||
989 | } | 1177 | } |
990 | case ACER_WMID: | 1178 | case ACER_WMID: |
991 | return WMID_set_u32(value, cap, interface); | 1179 | return WMID_set_u32(value, cap, interface); |
1180 | case ACER_WMID_v2: | ||
1181 | if (cap & (ACER_CAP_WIRELESS | | ||
1182 | ACER_CAP_BLUETOOTH | | ||
1183 | ACER_CAP_THREEG)) | ||
1184 | return wmid_v2_set_u32(value, cap); | ||
1185 | else if (wmi_has_guid(WMID_GUID2)) | ||
1186 | return WMID_set_u32(value, cap, interface); | ||
992 | default: | 1187 | default: |
993 | return AE_BAD_PARAMETER; | 1188 | return AE_BAD_PARAMETER; |
994 | } | 1189 | } |
@@ -1095,186 +1290,6 @@ static void acer_backlight_exit(void) | |||
1095 | backlight_device_unregister(acer_backlight_device); | 1290 | backlight_device_unregister(acer_backlight_device); |
1096 | } | 1291 | } |
1097 | 1292 | ||
1098 | static acpi_status wmid3_get_device_status(u32 *value, u16 device) | ||
1099 | { | ||
1100 | struct wmid3_gds_return_value return_value; | ||
1101 | acpi_status status; | ||
1102 | union acpi_object *obj; | ||
1103 | struct wmid3_gds_input_param params = { | ||
1104 | .function_num = 0x1, | ||
1105 | .hotkey_number = 0x01, | ||
1106 | .devices = device, | ||
1107 | }; | ||
1108 | struct acpi_buffer input = { | ||
1109 | sizeof(struct wmid3_gds_input_param), | ||
1110 | ¶ms | ||
1111 | }; | ||
1112 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1113 | |||
1114 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); | ||
1115 | if (ACPI_FAILURE(status)) | ||
1116 | return status; | ||
1117 | |||
1118 | obj = output.pointer; | ||
1119 | |||
1120 | if (!obj) | ||
1121 | return AE_ERROR; | ||
1122 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1123 | kfree(obj); | ||
1124 | return AE_ERROR; | ||
1125 | } | ||
1126 | if (obj->buffer.length != 8) { | ||
1127 | pr_warn("Unknown buffer length %d\n", obj->buffer.length); | ||
1128 | kfree(obj); | ||
1129 | return AE_ERROR; | ||
1130 | } | ||
1131 | |||
1132 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
1133 | kfree(obj); | ||
1134 | |||
1135 | if (return_value.error_code || return_value.ec_return_value) | ||
1136 | pr_warn("Get Device Status failed: 0x%x - 0x%x\n", | ||
1137 | return_value.error_code, | ||
1138 | return_value.ec_return_value); | ||
1139 | else | ||
1140 | *value = !!(return_value.devices & device); | ||
1141 | |||
1142 | return status; | ||
1143 | } | ||
1144 | |||
1145 | static acpi_status get_device_status(u32 *value, u32 cap) | ||
1146 | { | ||
1147 | if (wmi_has_guid(WMID_GUID3)) { | ||
1148 | u16 device; | ||
1149 | |||
1150 | switch (cap) { | ||
1151 | case ACER_CAP_WIRELESS: | ||
1152 | device = ACER_WMID3_GDS_WIRELESS; | ||
1153 | break; | ||
1154 | case ACER_CAP_BLUETOOTH: | ||
1155 | device = ACER_WMID3_GDS_BLUETOOTH; | ||
1156 | break; | ||
1157 | case ACER_CAP_THREEG: | ||
1158 | device = ACER_WMID3_GDS_THREEG; | ||
1159 | break; | ||
1160 | default: | ||
1161 | return AE_ERROR; | ||
1162 | } | ||
1163 | return wmid3_get_device_status(value, device); | ||
1164 | |||
1165 | } else { | ||
1166 | return get_u32(value, cap); | ||
1167 | } | ||
1168 | } | ||
1169 | |||
1170 | static acpi_status wmid3_set_device_status(u32 value, u16 device) | ||
1171 | { | ||
1172 | struct wmid3_gds_return_value return_value; | ||
1173 | acpi_status status; | ||
1174 | union acpi_object *obj; | ||
1175 | u16 devices; | ||
1176 | struct wmid3_gds_input_param params = { | ||
1177 | .function_num = 0x1, | ||
1178 | .hotkey_number = 0x01, | ||
1179 | .devices = ACER_WMID3_GDS_WIRELESS | | ||
1180 | ACER_WMID3_GDS_THREEG | | ||
1181 | ACER_WMID3_GDS_WIMAX | | ||
1182 | ACER_WMID3_GDS_BLUETOOTH, | ||
1183 | }; | ||
1184 | struct acpi_buffer input = { | ||
1185 | sizeof(struct wmid3_gds_input_param), | ||
1186 | ¶ms | ||
1187 | }; | ||
1188 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1189 | struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1190 | |||
1191 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output); | ||
1192 | if (ACPI_FAILURE(status)) | ||
1193 | return status; | ||
1194 | |||
1195 | obj = output.pointer; | ||
1196 | |||
1197 | if (!obj) | ||
1198 | return AE_ERROR; | ||
1199 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1200 | kfree(obj); | ||
1201 | return AE_ERROR; | ||
1202 | } | ||
1203 | if (obj->buffer.length != 8) { | ||
1204 | pr_warning("Unknown buffer length %d\n", obj->buffer.length); | ||
1205 | kfree(obj); | ||
1206 | return AE_ERROR; | ||
1207 | } | ||
1208 | |||
1209 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
1210 | kfree(obj); | ||
1211 | |||
1212 | if (return_value.error_code || return_value.ec_return_value) { | ||
1213 | pr_warning("Get Current Device Status failed: " | ||
1214 | "0x%x - 0x%x\n", return_value.error_code, | ||
1215 | return_value.ec_return_value); | ||
1216 | return status; | ||
1217 | } | ||
1218 | |||
1219 | devices = return_value.devices; | ||
1220 | params.function_num = 0x2; | ||
1221 | params.hotkey_number = 0x01; | ||
1222 | params.devices = (value) ? (devices | device) : (devices & ~device); | ||
1223 | |||
1224 | status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2); | ||
1225 | if (ACPI_FAILURE(status)) | ||
1226 | return status; | ||
1227 | |||
1228 | obj = output2.pointer; | ||
1229 | |||
1230 | if (!obj) | ||
1231 | return AE_ERROR; | ||
1232 | else if (obj->type != ACPI_TYPE_BUFFER) { | ||
1233 | kfree(obj); | ||
1234 | return AE_ERROR; | ||
1235 | } | ||
1236 | if (obj->buffer.length != 4) { | ||
1237 | pr_warning("Unknown buffer length %d\n", obj->buffer.length); | ||
1238 | kfree(obj); | ||
1239 | return AE_ERROR; | ||
1240 | } | ||
1241 | |||
1242 | return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer); | ||
1243 | kfree(obj); | ||
1244 | |||
1245 | if (return_value.error_code || return_value.ec_return_value) | ||
1246 | pr_warning("Set Device Status failed: " | ||
1247 | "0x%x - 0x%x\n", return_value.error_code, | ||
1248 | return_value.ec_return_value); | ||
1249 | |||
1250 | return status; | ||
1251 | } | ||
1252 | |||
1253 | static acpi_status set_device_status(u32 value, u32 cap) | ||
1254 | { | ||
1255 | if (wmi_has_guid(WMID_GUID3)) { | ||
1256 | u16 device; | ||
1257 | |||
1258 | switch (cap) { | ||
1259 | case ACER_CAP_WIRELESS: | ||
1260 | device = ACER_WMID3_GDS_WIRELESS; | ||
1261 | break; | ||
1262 | case ACER_CAP_BLUETOOTH: | ||
1263 | device = ACER_WMID3_GDS_BLUETOOTH; | ||
1264 | break; | ||
1265 | case ACER_CAP_THREEG: | ||
1266 | device = ACER_WMID3_GDS_THREEG; | ||
1267 | break; | ||
1268 | default: | ||
1269 | return AE_ERROR; | ||
1270 | } | ||
1271 | return wmid3_set_device_status(value, device); | ||
1272 | |||
1273 | } else { | ||
1274 | return set_u32(value, cap); | ||
1275 | } | ||
1276 | } | ||
1277 | |||
1278 | /* | 1293 | /* |
1279 | * Rfkill devices | 1294 | * Rfkill devices |
1280 | */ | 1295 | */ |
@@ -1285,12 +1300,13 @@ static void acer_rfkill_update(struct work_struct *ignored) | |||
1285 | u32 state; | 1300 | u32 state; |
1286 | acpi_status status; | 1301 | acpi_status status; |
1287 | 1302 | ||
1288 | status = get_u32(&state, ACER_CAP_WIRELESS); | 1303 | if (has_cap(ACER_CAP_WIRELESS)) { |
1289 | if (ACPI_SUCCESS(status)) { | 1304 | status = get_u32(&state, ACER_CAP_WIRELESS); |
1290 | if (quirks->wireless == 3) { | 1305 | if (ACPI_SUCCESS(status)) { |
1291 | rfkill_set_hw_state(wireless_rfkill, !state); | 1306 | if (quirks->wireless == 3) |
1292 | } else { | 1307 | rfkill_set_hw_state(wireless_rfkill, !state); |
1293 | rfkill_set_sw_state(wireless_rfkill, !state); | 1308 | else |
1309 | rfkill_set_sw_state(wireless_rfkill, !state); | ||
1294 | } | 1310 | } |
1295 | } | 1311 | } |
1296 | 1312 | ||
@@ -1301,8 +1317,7 @@ static void acer_rfkill_update(struct work_struct *ignored) | |||
1301 | } | 1317 | } |
1302 | 1318 | ||
1303 | if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { | 1319 | if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { |
1304 | status = wmid3_get_device_status(&state, | 1320 | status = get_u32(&state, ACER_WMID3_GDS_THREEG); |
1305 | ACER_WMID3_GDS_THREEG); | ||
1306 | if (ACPI_SUCCESS(status)) | 1321 | if (ACPI_SUCCESS(status)) |
1307 | rfkill_set_sw_state(threeg_rfkill, !state); | 1322 | rfkill_set_sw_state(threeg_rfkill, !state); |
1308 | } | 1323 | } |
@@ -1316,7 +1331,7 @@ static int acer_rfkill_set(void *data, bool blocked) | |||
1316 | u32 cap = (unsigned long)data; | 1331 | u32 cap = (unsigned long)data; |
1317 | 1332 | ||
1318 | if (rfkill_inited) { | 1333 | if (rfkill_inited) { |
1319 | status = set_device_status(!blocked, cap); | 1334 | status = set_u32(!blocked, cap); |
1320 | if (ACPI_FAILURE(status)) | 1335 | if (ACPI_FAILURE(status)) |
1321 | return -ENODEV; | 1336 | return -ENODEV; |
1322 | } | 1337 | } |
@@ -1343,7 +1358,7 @@ static struct rfkill *acer_rfkill_register(struct device *dev, | |||
1343 | if (!rfkill_dev) | 1358 | if (!rfkill_dev) |
1344 | return ERR_PTR(-ENOMEM); | 1359 | return ERR_PTR(-ENOMEM); |
1345 | 1360 | ||
1346 | status = get_device_status(&state, cap); | 1361 | status = get_u32(&state, cap); |
1347 | 1362 | ||
1348 | err = rfkill_register(rfkill_dev); | 1363 | err = rfkill_register(rfkill_dev); |
1349 | if (err) { | 1364 | if (err) { |
@@ -1359,19 +1374,24 @@ static struct rfkill *acer_rfkill_register(struct device *dev, | |||
1359 | 1374 | ||
1360 | static int acer_rfkill_init(struct device *dev) | 1375 | static int acer_rfkill_init(struct device *dev) |
1361 | { | 1376 | { |
1362 | wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN, | 1377 | int err; |
1363 | "acer-wireless", ACER_CAP_WIRELESS); | 1378 | |
1364 | if (IS_ERR(wireless_rfkill)) | 1379 | if (has_cap(ACER_CAP_WIRELESS)) { |
1365 | return PTR_ERR(wireless_rfkill); | 1380 | wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN, |
1381 | "acer-wireless", ACER_CAP_WIRELESS); | ||
1382 | if (IS_ERR(wireless_rfkill)) { | ||
1383 | err = PTR_ERR(wireless_rfkill); | ||
1384 | goto error_wireless; | ||
1385 | } | ||
1386 | } | ||
1366 | 1387 | ||
1367 | if (has_cap(ACER_CAP_BLUETOOTH)) { | 1388 | if (has_cap(ACER_CAP_BLUETOOTH)) { |
1368 | bluetooth_rfkill = acer_rfkill_register(dev, | 1389 | bluetooth_rfkill = acer_rfkill_register(dev, |
1369 | RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", | 1390 | RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", |
1370 | ACER_CAP_BLUETOOTH); | 1391 | ACER_CAP_BLUETOOTH); |
1371 | if (IS_ERR(bluetooth_rfkill)) { | 1392 | if (IS_ERR(bluetooth_rfkill)) { |
1372 | rfkill_unregister(wireless_rfkill); | 1393 | err = PTR_ERR(bluetooth_rfkill); |
1373 | rfkill_destroy(wireless_rfkill); | 1394 | goto error_bluetooth; |
1374 | return PTR_ERR(bluetooth_rfkill); | ||
1375 | } | 1395 | } |
1376 | } | 1396 | } |
1377 | 1397 | ||
@@ -1380,30 +1400,44 @@ static int acer_rfkill_init(struct device *dev) | |||
1380 | RFKILL_TYPE_WWAN, "acer-threeg", | 1400 | RFKILL_TYPE_WWAN, "acer-threeg", |
1381 | ACER_CAP_THREEG); | 1401 | ACER_CAP_THREEG); |
1382 | if (IS_ERR(threeg_rfkill)) { | 1402 | if (IS_ERR(threeg_rfkill)) { |
1383 | rfkill_unregister(wireless_rfkill); | 1403 | err = PTR_ERR(threeg_rfkill); |
1384 | rfkill_destroy(wireless_rfkill); | 1404 | goto error_threeg; |
1385 | rfkill_unregister(bluetooth_rfkill); | ||
1386 | rfkill_destroy(bluetooth_rfkill); | ||
1387 | return PTR_ERR(threeg_rfkill); | ||
1388 | } | 1405 | } |
1389 | } | 1406 | } |
1390 | 1407 | ||
1391 | rfkill_inited = true; | 1408 | rfkill_inited = true; |
1392 | 1409 | ||
1393 | if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) | 1410 | if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) && |
1411 | has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG)) | ||
1394 | schedule_delayed_work(&acer_rfkill_work, | 1412 | schedule_delayed_work(&acer_rfkill_work, |
1395 | round_jiffies_relative(HZ)); | 1413 | round_jiffies_relative(HZ)); |
1396 | 1414 | ||
1397 | return 0; | 1415 | return 0; |
1416 | |||
1417 | error_threeg: | ||
1418 | if (has_cap(ACER_CAP_BLUETOOTH)) { | ||
1419 | rfkill_unregister(bluetooth_rfkill); | ||
1420 | rfkill_destroy(bluetooth_rfkill); | ||
1421 | } | ||
1422 | error_bluetooth: | ||
1423 | if (has_cap(ACER_CAP_WIRELESS)) { | ||
1424 | rfkill_unregister(wireless_rfkill); | ||
1425 | rfkill_destroy(wireless_rfkill); | ||
1426 | } | ||
1427 | error_wireless: | ||
1428 | return err; | ||
1398 | } | 1429 | } |
1399 | 1430 | ||
1400 | static void acer_rfkill_exit(void) | 1431 | static void acer_rfkill_exit(void) |
1401 | { | 1432 | { |
1402 | if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) | 1433 | if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) && |
1434 | has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG)) | ||
1403 | cancel_delayed_work_sync(&acer_rfkill_work); | 1435 | cancel_delayed_work_sync(&acer_rfkill_work); |
1404 | 1436 | ||
1405 | rfkill_unregister(wireless_rfkill); | 1437 | if (has_cap(ACER_CAP_WIRELESS)) { |
1406 | rfkill_destroy(wireless_rfkill); | 1438 | rfkill_unregister(wireless_rfkill); |
1439 | rfkill_destroy(wireless_rfkill); | ||
1440 | } | ||
1407 | 1441 | ||
1408 | if (has_cap(ACER_CAP_BLUETOOTH)) { | 1442 | if (has_cap(ACER_CAP_BLUETOOTH)) { |
1409 | rfkill_unregister(bluetooth_rfkill); | 1443 | rfkill_unregister(bluetooth_rfkill); |
@@ -1428,11 +1462,7 @@ static ssize_t show_bool_threeg(struct device *dev, | |||
1428 | 1462 | ||
1429 | pr_info("This threeg sysfs will be removed in 2012" | 1463 | pr_info("This threeg sysfs will be removed in 2012" |
1430 | " - used by: %s\n", current->comm); | 1464 | " - used by: %s\n", current->comm); |
1431 | if (wmi_has_guid(WMID_GUID3)) | 1465 | status = get_u32(&result, ACER_CAP_THREEG); |
1432 | status = wmid3_get_device_status(&result, | ||
1433 | ACER_WMID3_GDS_THREEG); | ||
1434 | else | ||
1435 | status = get_u32(&result, ACER_CAP_THREEG); | ||
1436 | if (ACPI_SUCCESS(status)) | 1466 | if (ACPI_SUCCESS(status)) |
1437 | return sprintf(buf, "%u\n", result); | 1467 | return sprintf(buf, "%u\n", result); |
1438 | return sprintf(buf, "Read error\n"); | 1468 | return sprintf(buf, "Read error\n"); |
@@ -1464,6 +1494,8 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr, | |||
1464 | return sprintf(buf, "AMW0 v2\n"); | 1494 | return sprintf(buf, "AMW0 v2\n"); |
1465 | case ACER_WMID: | 1495 | case ACER_WMID: |
1466 | return sprintf(buf, "WMID\n"); | 1496 | return sprintf(buf, "WMID\n"); |
1497 | case ACER_WMID_v2: | ||
1498 | return sprintf(buf, "WMID v2\n"); | ||
1467 | default: | 1499 | default: |
1468 | return sprintf(buf, "Error!\n"); | 1500 | return sprintf(buf, "Error!\n"); |
1469 | } | 1501 | } |
@@ -1883,12 +1915,20 @@ static int __init acer_wmi_init(void) | |||
1883 | if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1)) | 1915 | if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1)) |
1884 | interface = &wmid_interface; | 1916 | interface = &wmid_interface; |
1885 | 1917 | ||
1918 | if (wmi_has_guid(WMID_GUID3)) | ||
1919 | interface = &wmid_v2_interface; | ||
1920 | |||
1921 | if (interface) | ||
1922 | dmi_walk(type_aa_dmi_decode, NULL); | ||
1923 | |||
1886 | if (wmi_has_guid(WMID_GUID2) && interface) { | 1924 | if (wmi_has_guid(WMID_GUID2) && interface) { |
1887 | if (ACPI_FAILURE(WMID_set_capabilities())) { | 1925 | if (!has_type_aa && ACPI_FAILURE(WMID_set_capabilities())) { |
1888 | pr_err("Unable to detect available WMID devices\n"); | 1926 | pr_err("Unable to detect available WMID devices\n"); |
1889 | return -ENODEV; | 1927 | return -ENODEV; |
1890 | } | 1928 | } |
1891 | } else if (!wmi_has_guid(WMID_GUID2) && interface) { | 1929 | /* WMID always provides brightness methods */ |
1930 | interface->capability |= ACER_CAP_BRIGHTNESS; | ||
1931 | } else if (!wmi_has_guid(WMID_GUID2) && interface && !has_type_aa) { | ||
1892 | pr_err("No WMID device detection method found\n"); | 1932 | pr_err("No WMID device detection method found\n"); |
1893 | return -ENODEV; | 1933 | return -ENODEV; |
1894 | } | 1934 | } |
@@ -1912,7 +1952,7 @@ static int __init acer_wmi_init(void) | |||
1912 | 1952 | ||
1913 | set_quirks(); | 1953 | set_quirks(); |
1914 | 1954 | ||
1915 | if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { | 1955 | if (acpi_video_backlight_support()) { |
1916 | interface->capability &= ~ACER_CAP_BRIGHTNESS; | 1956 | interface->capability &= ~ACER_CAP_BRIGHTNESS; |
1917 | pr_info("Brightness must be controlled by " | 1957 | pr_info("Brightness must be controlled by " |
1918 | "generic video driver\n"); | 1958 | "generic video driver\n"); |
diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index fa6d7ec68b26..edaccad9b5bf 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * | 4 | * |
5 | * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor | 5 | * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor |
6 | * Copyright (C) 2006-2007 Corentin Chary | 6 | * Copyright (C) 2006-2007 Corentin Chary |
7 | * Copyright (C) 2011 Wind River Systems | ||
7 | * | 8 | * |
8 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by | 10 | * it under the terms of the GNU General Public License as published by |
@@ -48,6 +49,7 @@ | |||
48 | #include <linux/uaccess.h> | 49 | #include <linux/uaccess.h> |
49 | #include <linux/input.h> | 50 | #include <linux/input.h> |
50 | #include <linux/input/sparse-keymap.h> | 51 | #include <linux/input/sparse-keymap.h> |
52 | #include <linux/input-polldev.h> | ||
51 | #include <linux/rfkill.h> | 53 | #include <linux/rfkill.h> |
52 | #include <linux/slab.h> | 54 | #include <linux/slab.h> |
53 | #include <linux/dmi.h> | 55 | #include <linux/dmi.h> |
@@ -83,26 +85,32 @@ static int wlan_status = 1; | |||
83 | static int bluetooth_status = 1; | 85 | static int bluetooth_status = 1; |
84 | static int wimax_status = -1; | 86 | static int wimax_status = -1; |
85 | static int wwan_status = -1; | 87 | static int wwan_status = -1; |
88 | static int als_status; | ||
86 | 89 | ||
87 | module_param(wlan_status, int, 0444); | 90 | module_param(wlan_status, int, 0444); |
88 | MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " | 91 | MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " |
89 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | 92 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " |
90 | "default is 1"); | 93 | "default is -1"); |
91 | 94 | ||
92 | module_param(bluetooth_status, int, 0444); | 95 | module_param(bluetooth_status, int, 0444); |
93 | MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " | 96 | MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " |
94 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | 97 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " |
95 | "default is 1"); | 98 | "default is -1"); |
96 | 99 | ||
97 | module_param(wimax_status, int, 0444); | 100 | module_param(wimax_status, int, 0444); |
98 | MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot " | 101 | MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot " |
99 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | 102 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " |
100 | "default is 1"); | 103 | "default is -1"); |
101 | 104 | ||
102 | module_param(wwan_status, int, 0444); | 105 | module_param(wwan_status, int, 0444); |
103 | MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " | 106 | MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " |
104 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " | 107 | "(0 = disabled, 1 = enabled, -1 = don't do anything). " |
105 | "default is 1"); | 108 | "default is -1"); |
109 | |||
110 | module_param(als_status, int, 0444); | ||
111 | MODULE_PARM_DESC(als_status, "Set the ALS status on boot " | ||
112 | "(0 = disabled, 1 = enabled). " | ||
113 | "default is 0"); | ||
106 | 114 | ||
107 | /* | 115 | /* |
108 | * Some events we use, same for all Asus | 116 | * Some events we use, same for all Asus |
@@ -173,6 +181,29 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " | |||
173 | #define METHOD_KBD_LIGHT_SET "SLKB" | 181 | #define METHOD_KBD_LIGHT_SET "SLKB" |
174 | #define METHOD_KBD_LIGHT_GET "GLKB" | 182 | #define METHOD_KBD_LIGHT_GET "GLKB" |
175 | 183 | ||
184 | /* For Pegatron Lucid tablet */ | ||
185 | #define DEVICE_NAME_PEGA "Lucid" | ||
186 | |||
187 | #define METHOD_PEGA_ENABLE "ENPR" | ||
188 | #define METHOD_PEGA_DISABLE "DAPR" | ||
189 | #define PEGA_WLAN 0x00 | ||
190 | #define PEGA_BLUETOOTH 0x01 | ||
191 | #define PEGA_WWAN 0x02 | ||
192 | #define PEGA_ALS 0x04 | ||
193 | #define PEGA_ALS_POWER 0x05 | ||
194 | |||
195 | #define METHOD_PEGA_READ "RDLN" | ||
196 | #define PEGA_READ_ALS_H 0x02 | ||
197 | #define PEGA_READ_ALS_L 0x03 | ||
198 | |||
199 | #define PEGA_ACCEL_NAME "pega_accel" | ||
200 | #define PEGA_ACCEL_DESC "Pegatron Lucid Tablet Accelerometer" | ||
201 | #define METHOD_XLRX "XLRX" | ||
202 | #define METHOD_XLRY "XLRY" | ||
203 | #define METHOD_XLRZ "XLRZ" | ||
204 | #define PEGA_ACC_CLAMP 512 /* 1G accel is reported as ~256, so clamp to 2G */ | ||
205 | #define PEGA_ACC_RETRIES 3 | ||
206 | |||
176 | /* | 207 | /* |
177 | * Define a specific led structure to keep the main structure clean | 208 | * Define a specific led structure to keep the main structure clean |
178 | */ | 209 | */ |
@@ -185,6 +216,15 @@ struct asus_led { | |||
185 | }; | 216 | }; |
186 | 217 | ||
187 | /* | 218 | /* |
219 | * Same thing for rfkill | ||
220 | */ | ||
221 | struct asus_pega_rfkill { | ||
222 | int control_id; /* type of control. Maps to PEGA_* values */ | ||
223 | struct rfkill *rfkill; | ||
224 | struct asus_laptop *asus; | ||
225 | }; | ||
226 | |||
227 | /* | ||
188 | * This is the main structure, we can use it to store anything interesting | 228 | * This is the main structure, we can use it to store anything interesting |
189 | * about the hotk device | 229 | * about the hotk device |
190 | */ | 230 | */ |
@@ -198,6 +238,7 @@ struct asus_laptop { | |||
198 | 238 | ||
199 | struct input_dev *inputdev; | 239 | struct input_dev *inputdev; |
200 | struct key_entry *keymap; | 240 | struct key_entry *keymap; |
241 | struct input_polled_dev *pega_accel_poll; | ||
201 | 242 | ||
202 | struct asus_led mled; | 243 | struct asus_led mled; |
203 | struct asus_led tled; | 244 | struct asus_led tled; |
@@ -209,9 +250,18 @@ struct asus_laptop { | |||
209 | 250 | ||
210 | int wireless_status; | 251 | int wireless_status; |
211 | bool have_rsts; | 252 | bool have_rsts; |
253 | bool is_pega_lucid; | ||
254 | bool pega_acc_live; | ||
255 | int pega_acc_x; | ||
256 | int pega_acc_y; | ||
257 | int pega_acc_z; | ||
212 | 258 | ||
213 | struct rfkill *gps_rfkill; | 259 | struct rfkill *gps_rfkill; |
214 | 260 | ||
261 | struct asus_pega_rfkill wlanrfk; | ||
262 | struct asus_pega_rfkill btrfk; | ||
263 | struct asus_pega_rfkill wwanrfk; | ||
264 | |||
215 | acpi_handle handle; /* the handle of the hotk device */ | 265 | acpi_handle handle; /* the handle of the hotk device */ |
216 | u32 ledd_status; /* status of the LED display */ | 266 | u32 ledd_status; /* status of the LED display */ |
217 | u8 light_level; /* light sensor level */ | 267 | u8 light_level; /* light sensor level */ |
@@ -323,6 +373,127 @@ static int acpi_check_handle(acpi_handle handle, const char *method, | |||
323 | return 0; | 373 | return 0; |
324 | } | 374 | } |
325 | 375 | ||
376 | static bool asus_check_pega_lucid(struct asus_laptop *asus) | ||
377 | { | ||
378 | return !strcmp(asus->name, DEVICE_NAME_PEGA) && | ||
379 | !acpi_check_handle(asus->handle, METHOD_PEGA_ENABLE, NULL) && | ||
380 | !acpi_check_handle(asus->handle, METHOD_PEGA_DISABLE, NULL) && | ||
381 | !acpi_check_handle(asus->handle, METHOD_PEGA_READ, NULL); | ||
382 | } | ||
383 | |||
384 | static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable) | ||
385 | { | ||
386 | char *method = enable ? METHOD_PEGA_ENABLE : METHOD_PEGA_DISABLE; | ||
387 | return write_acpi_int(asus->handle, method, unit); | ||
388 | } | ||
389 | |||
390 | static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method) | ||
391 | { | ||
392 | int i, delta; | ||
393 | unsigned long long val; | ||
394 | for (i = 0; i < PEGA_ACC_RETRIES; i++) { | ||
395 | acpi_evaluate_integer(asus->handle, method, NULL, &val); | ||
396 | |||
397 | /* The output is noisy. From reading the ASL | ||
398 | * dissassembly, timeout errors are returned with 1's | ||
399 | * in the high word, and the lack of locking around | ||
400 | * thei hi/lo byte reads means that a transition | ||
401 | * between (for example) -1 and 0 could be read as | ||
402 | * 0xff00 or 0x00ff. */ | ||
403 | delta = abs(curr - (short)val); | ||
404 | if (delta < 128 && !(val & ~0xffff)) | ||
405 | break; | ||
406 | } | ||
407 | return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP); | ||
408 | } | ||
409 | |||
410 | static void pega_accel_poll(struct input_polled_dev *ipd) | ||
411 | { | ||
412 | struct device *parent = ipd->input->dev.parent; | ||
413 | struct asus_laptop *asus = dev_get_drvdata(parent); | ||
414 | |||
415 | /* In some cases, the very first call to poll causes a | ||
416 | * recursive fault under the polldev worker. This is | ||
417 | * apparently related to very early userspace access to the | ||
418 | * device, and perhaps a firmware bug. Fake the first report. */ | ||
419 | if (!asus->pega_acc_live) { | ||
420 | asus->pega_acc_live = true; | ||
421 | input_report_abs(ipd->input, ABS_X, 0); | ||
422 | input_report_abs(ipd->input, ABS_Y, 0); | ||
423 | input_report_abs(ipd->input, ABS_Z, 0); | ||
424 | input_sync(ipd->input); | ||
425 | return; | ||
426 | } | ||
427 | |||
428 | asus->pega_acc_x = pega_acc_axis(asus, asus->pega_acc_x, METHOD_XLRX); | ||
429 | asus->pega_acc_y = pega_acc_axis(asus, asus->pega_acc_y, METHOD_XLRY); | ||
430 | asus->pega_acc_z = pega_acc_axis(asus, asus->pega_acc_z, METHOD_XLRZ); | ||
431 | |||
432 | /* Note transform, convert to "right/up/out" in the native | ||
433 | * landscape orientation (i.e. the vector is the direction of | ||
434 | * "real up" in the device's cartiesian coordinates). */ | ||
435 | input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x); | ||
436 | input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y); | ||
437 | input_report_abs(ipd->input, ABS_Z, asus->pega_acc_z); | ||
438 | input_sync(ipd->input); | ||
439 | } | ||
440 | |||
441 | static void pega_accel_exit(struct asus_laptop *asus) | ||
442 | { | ||
443 | if (asus->pega_accel_poll) { | ||
444 | input_unregister_polled_device(asus->pega_accel_poll); | ||
445 | input_free_polled_device(asus->pega_accel_poll); | ||
446 | } | ||
447 | asus->pega_accel_poll = NULL; | ||
448 | } | ||
449 | |||
450 | static int pega_accel_init(struct asus_laptop *asus) | ||
451 | { | ||
452 | int err; | ||
453 | struct input_polled_dev *ipd; | ||
454 | |||
455 | if (!asus->is_pega_lucid) | ||
456 | return -ENODEV; | ||
457 | |||
458 | if (acpi_check_handle(asus->handle, METHOD_XLRX, NULL) || | ||
459 | acpi_check_handle(asus->handle, METHOD_XLRY, NULL) || | ||
460 | acpi_check_handle(asus->handle, METHOD_XLRZ, NULL)) | ||
461 | return -ENODEV; | ||
462 | |||
463 | ipd = input_allocate_polled_device(); | ||
464 | if (!ipd) | ||
465 | return -ENOMEM; | ||
466 | |||
467 | ipd->poll = pega_accel_poll; | ||
468 | ipd->poll_interval = 125; | ||
469 | ipd->poll_interval_min = 50; | ||
470 | ipd->poll_interval_max = 2000; | ||
471 | |||
472 | ipd->input->name = PEGA_ACCEL_DESC; | ||
473 | ipd->input->phys = PEGA_ACCEL_NAME "/input0"; | ||
474 | ipd->input->dev.parent = &asus->platform_device->dev; | ||
475 | ipd->input->id.bustype = BUS_HOST; | ||
476 | |||
477 | set_bit(EV_ABS, ipd->input->evbit); | ||
478 | input_set_abs_params(ipd->input, ABS_X, | ||
479 | -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); | ||
480 | input_set_abs_params(ipd->input, ABS_Y, | ||
481 | -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); | ||
482 | input_set_abs_params(ipd->input, ABS_Z, | ||
483 | -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0); | ||
484 | |||
485 | err = input_register_polled_device(ipd); | ||
486 | if (err) | ||
487 | goto exit; | ||
488 | |||
489 | asus->pega_accel_poll = ipd; | ||
490 | return 0; | ||
491 | |||
492 | exit: | ||
493 | input_free_polled_device(ipd); | ||
494 | return err; | ||
495 | } | ||
496 | |||
326 | /* Generic LED function */ | 497 | /* Generic LED function */ |
327 | static int asus_led_set(struct asus_laptop *asus, const char *method, | 498 | static int asus_led_set(struct asus_laptop *asus, const char *method, |
328 | int value) | 499 | int value) |
@@ -430,17 +601,17 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev) | |||
430 | 601 | ||
431 | static void asus_led_exit(struct asus_laptop *asus) | 602 | static void asus_led_exit(struct asus_laptop *asus) |
432 | { | 603 | { |
433 | if (asus->mled.led.dev) | 604 | if (!IS_ERR_OR_NULL(asus->mled.led.dev)) |
434 | led_classdev_unregister(&asus->mled.led); | 605 | led_classdev_unregister(&asus->mled.led); |
435 | if (asus->tled.led.dev) | 606 | if (!IS_ERR_OR_NULL(asus->tled.led.dev)) |
436 | led_classdev_unregister(&asus->tled.led); | 607 | led_classdev_unregister(&asus->tled.led); |
437 | if (asus->pled.led.dev) | 608 | if (!IS_ERR_OR_NULL(asus->pled.led.dev)) |
438 | led_classdev_unregister(&asus->pled.led); | 609 | led_classdev_unregister(&asus->pled.led); |
439 | if (asus->rled.led.dev) | 610 | if (!IS_ERR_OR_NULL(asus->rled.led.dev)) |
440 | led_classdev_unregister(&asus->rled.led); | 611 | led_classdev_unregister(&asus->rled.led); |
441 | if (asus->gled.led.dev) | 612 | if (!IS_ERR_OR_NULL(asus->gled.led.dev)) |
442 | led_classdev_unregister(&asus->gled.led); | 613 | led_classdev_unregister(&asus->gled.led); |
443 | if (asus->kled.led.dev) | 614 | if (!IS_ERR_OR_NULL(asus->kled.led.dev)) |
444 | led_classdev_unregister(&asus->kled.led); | 615 | led_classdev_unregister(&asus->kled.led); |
445 | if (asus->led_workqueue) { | 616 | if (asus->led_workqueue) { |
446 | destroy_workqueue(asus->led_workqueue); | 617 | destroy_workqueue(asus->led_workqueue); |
@@ -474,6 +645,13 @@ static int asus_led_init(struct asus_laptop *asus) | |||
474 | int r; | 645 | int r; |
475 | 646 | ||
476 | /* | 647 | /* |
648 | * The Pegatron Lucid has no physical leds, but all methods are | ||
649 | * available in the DSDT... | ||
650 | */ | ||
651 | if (asus->is_pega_lucid) | ||
652 | return 0; | ||
653 | |||
654 | /* | ||
477 | * Functions that actually update the LED's are called from a | 655 | * Functions that actually update the LED's are called from a |
478 | * workqueue. By doing this as separate work rather than when the LED | 656 | * workqueue. By doing this as separate work rather than when the LED |
479 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a | 657 | * subsystem asks, we avoid messing with the Asus ACPI stuff during a |
@@ -907,8 +1085,18 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr, | |||
907 | */ | 1085 | */ |
908 | static void asus_als_switch(struct asus_laptop *asus, int value) | 1086 | static void asus_als_switch(struct asus_laptop *asus, int value) |
909 | { | 1087 | { |
910 | if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value)) | 1088 | int ret; |
911 | pr_warn("Error setting light sensor switch\n"); | 1089 | |
1090 | if (asus->is_pega_lucid) { | ||
1091 | ret = asus_pega_lucid_set(asus, PEGA_ALS, value); | ||
1092 | if (!ret) | ||
1093 | ret = asus_pega_lucid_set(asus, PEGA_ALS_POWER, value); | ||
1094 | } else { | ||
1095 | ret = write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value); | ||
1096 | } | ||
1097 | if (ret) | ||
1098 | pr_warning("Error setting light sensor switch\n"); | ||
1099 | |||
912 | asus->light_switch = value; | 1100 | asus->light_switch = value; |
913 | } | 1101 | } |
914 | 1102 | ||
@@ -964,6 +1152,35 @@ static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr, | |||
964 | return rv; | 1152 | return rv; |
965 | } | 1153 | } |
966 | 1154 | ||
1155 | static int pega_int_read(struct asus_laptop *asus, int arg, int *result) | ||
1156 | { | ||
1157 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1158 | int err = write_acpi_int_ret(asus->handle, METHOD_PEGA_READ, arg, | ||
1159 | &buffer); | ||
1160 | if (!err) { | ||
1161 | union acpi_object *obj = buffer.pointer; | ||
1162 | if (obj && obj->type == ACPI_TYPE_INTEGER) | ||
1163 | *result = obj->integer.value; | ||
1164 | else | ||
1165 | err = -EIO; | ||
1166 | } | ||
1167 | return err; | ||
1168 | } | ||
1169 | |||
1170 | static ssize_t show_lsvalue(struct device *dev, | ||
1171 | struct device_attribute *attr, char *buf) | ||
1172 | { | ||
1173 | struct asus_laptop *asus = dev_get_drvdata(dev); | ||
1174 | int err, hi, lo; | ||
1175 | |||
1176 | err = pega_int_read(asus, PEGA_READ_ALS_H, &hi); | ||
1177 | if (!err) | ||
1178 | err = pega_int_read(asus, PEGA_READ_ALS_L, &lo); | ||
1179 | if (!err) | ||
1180 | return sprintf(buf, "%d\n", 10 * hi + lo); | ||
1181 | return err; | ||
1182 | } | ||
1183 | |||
967 | /* | 1184 | /* |
968 | * GPS | 1185 | * GPS |
969 | */ | 1186 | */ |
@@ -1062,6 +1279,86 @@ static int asus_rfkill_init(struct asus_laptop *asus) | |||
1062 | return result; | 1279 | return result; |
1063 | } | 1280 | } |
1064 | 1281 | ||
1282 | static int pega_rfkill_set(void *data, bool blocked) | ||
1283 | { | ||
1284 | struct asus_pega_rfkill *pega_rfk = data; | ||
1285 | |||
1286 | int ret = asus_pega_lucid_set(pega_rfk->asus, pega_rfk->control_id, !blocked); | ||
1287 | pr_warn("Setting rfkill %d, to %d; returned %d\n", pega_rfk->control_id, !blocked, ret); | ||
1288 | |||
1289 | return ret; | ||
1290 | } | ||
1291 | |||
1292 | static const struct rfkill_ops pega_rfkill_ops = { | ||
1293 | .set_block = pega_rfkill_set, | ||
1294 | }; | ||
1295 | |||
1296 | static void pega_rfkill_terminate(struct asus_pega_rfkill *pega_rfk) | ||
1297 | { | ||
1298 | pr_warn("Terminating %d\n", pega_rfk->control_id); | ||
1299 | if (pega_rfk->rfkill) { | ||
1300 | rfkill_unregister(pega_rfk->rfkill); | ||
1301 | rfkill_destroy(pega_rfk->rfkill); | ||
1302 | pega_rfk->rfkill = NULL; | ||
1303 | } | ||
1304 | } | ||
1305 | |||
1306 | static void pega_rfkill_exit(struct asus_laptop *asus) | ||
1307 | { | ||
1308 | pega_rfkill_terminate(&asus->wwanrfk); | ||
1309 | pega_rfkill_terminate(&asus->btrfk); | ||
1310 | pega_rfkill_terminate(&asus->wlanrfk); | ||
1311 | } | ||
1312 | |||
1313 | static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_pega_rfkill *pega_rfk, | ||
1314 | const char *name, int controlid, int rfkill_type) | ||
1315 | { | ||
1316 | int result; | ||
1317 | |||
1318 | pr_warn("Setting up rfk %s, control %d, type %d\n", name, controlid, rfkill_type); | ||
1319 | pega_rfk->control_id = controlid; | ||
1320 | pega_rfk->asus = asus; | ||
1321 | pega_rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev, | ||
1322 | rfkill_type, &pega_rfkill_ops, pega_rfk); | ||
1323 | if (!pega_rfk->rfkill) | ||
1324 | return -EINVAL; | ||
1325 | |||
1326 | result = rfkill_register(pega_rfk->rfkill); | ||
1327 | if (result) { | ||
1328 | rfkill_destroy(pega_rfk->rfkill); | ||
1329 | pega_rfk->rfkill = NULL; | ||
1330 | } | ||
1331 | |||
1332 | return result; | ||
1333 | } | ||
1334 | |||
1335 | static int pega_rfkill_init(struct asus_laptop *asus) | ||
1336 | { | ||
1337 | int ret = 0; | ||
1338 | |||
1339 | if(!asus->is_pega_lucid) | ||
1340 | return -ENODEV; | ||
1341 | |||
1342 | ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN); | ||
1343 | if(ret) | ||
1344 | return ret; | ||
1345 | ret = pega_rfkill_setup(asus, &asus->btrfk, "pega-bt", PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH); | ||
1346 | if(ret) | ||
1347 | goto err_btrfk; | ||
1348 | ret = pega_rfkill_setup(asus, &asus->wwanrfk, "pega-wwan", PEGA_WWAN, RFKILL_TYPE_WWAN); | ||
1349 | if(ret) | ||
1350 | goto err_wwanrfk; | ||
1351 | |||
1352 | pr_warn("Pega rfkill init succeeded\n"); | ||
1353 | return 0; | ||
1354 | err_wwanrfk: | ||
1355 | pega_rfkill_terminate(&asus->btrfk); | ||
1356 | err_btrfk: | ||
1357 | pega_rfkill_terminate(&asus->wlanrfk); | ||
1358 | |||
1359 | return ret; | ||
1360 | } | ||
1361 | |||
1065 | /* | 1362 | /* |
1066 | * Input device (i.e. hotkeys) | 1363 | * Input device (i.e. hotkeys) |
1067 | */ | 1364 | */ |
@@ -1141,6 +1438,14 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event) | |||
1141 | } | 1438 | } |
1142 | return ; | 1439 | return ; |
1143 | } | 1440 | } |
1441 | |||
1442 | /* Accelerometer "coarse orientation change" event */ | ||
1443 | if (asus->pega_accel_poll && event == 0xEA) { | ||
1444 | kobject_uevent(&asus->pega_accel_poll->input->dev.kobj, | ||
1445 | KOBJ_CHANGE); | ||
1446 | return ; | ||
1447 | } | ||
1448 | |||
1144 | asus_input_notify(asus, event); | 1449 | asus_input_notify(asus, event); |
1145 | } | 1450 | } |
1146 | 1451 | ||
@@ -1152,6 +1457,7 @@ static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax); | |||
1152 | static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan); | 1457 | static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan); |
1153 | static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp); | 1458 | static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp); |
1154 | static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); | 1459 | static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd); |
1460 | static DEVICE_ATTR(ls_value, S_IRUGO, show_lsvalue, NULL); | ||
1155 | static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); | 1461 | static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); |
1156 | static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); | 1462 | static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw); |
1157 | static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); | 1463 | static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); |
@@ -1164,6 +1470,7 @@ static struct attribute *asus_attributes[] = { | |||
1164 | &dev_attr_wwan.attr, | 1470 | &dev_attr_wwan.attr, |
1165 | &dev_attr_display.attr, | 1471 | &dev_attr_display.attr, |
1166 | &dev_attr_ledd.attr, | 1472 | &dev_attr_ledd.attr, |
1473 | &dev_attr_ls_value.attr, | ||
1167 | &dev_attr_ls_level.attr, | 1474 | &dev_attr_ls_level.attr, |
1168 | &dev_attr_ls_switch.attr, | 1475 | &dev_attr_ls_switch.attr, |
1169 | &dev_attr_gps.attr, | 1476 | &dev_attr_gps.attr, |
@@ -1180,6 +1487,19 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj, | |||
1180 | acpi_handle handle = asus->handle; | 1487 | acpi_handle handle = asus->handle; |
1181 | bool supported; | 1488 | bool supported; |
1182 | 1489 | ||
1490 | if (asus->is_pega_lucid) { | ||
1491 | /* no ls_level interface on the Lucid */ | ||
1492 | if (attr == &dev_attr_ls_switch.attr) | ||
1493 | supported = true; | ||
1494 | else if (attr == &dev_attr_ls_level.attr) | ||
1495 | supported = false; | ||
1496 | else | ||
1497 | goto normal; | ||
1498 | |||
1499 | return supported; | ||
1500 | } | ||
1501 | |||
1502 | normal: | ||
1183 | if (attr == &dev_attr_wlan.attr) { | 1503 | if (attr == &dev_attr_wlan.attr) { |
1184 | supported = !acpi_check_handle(handle, METHOD_WLAN, NULL); | 1504 | supported = !acpi_check_handle(handle, METHOD_WLAN, NULL); |
1185 | 1505 | ||
@@ -1202,8 +1522,9 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj, | |||
1202 | } else if (attr == &dev_attr_ls_switch.attr || | 1522 | } else if (attr == &dev_attr_ls_switch.attr || |
1203 | attr == &dev_attr_ls_level.attr) { | 1523 | attr == &dev_attr_ls_level.attr) { |
1204 | supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && | 1524 | supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && |
1205 | !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); | 1525 | !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); |
1206 | 1526 | } else if (attr == &dev_attr_ls_value.attr) { | |
1527 | supported = asus->is_pega_lucid; | ||
1207 | } else if (attr == &dev_attr_gps.attr) { | 1528 | } else if (attr == &dev_attr_gps.attr) { |
1208 | supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) && | 1529 | supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) && |
1209 | !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && | 1530 | !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && |
@@ -1258,7 +1579,7 @@ static struct platform_driver platform_driver = { | |||
1258 | .driver = { | 1579 | .driver = { |
1259 | .name = ASUS_LAPTOP_FILE, | 1580 | .name = ASUS_LAPTOP_FILE, |
1260 | .owner = THIS_MODULE, | 1581 | .owner = THIS_MODULE, |
1261 | } | 1582 | }, |
1262 | }; | 1583 | }; |
1263 | 1584 | ||
1264 | /* | 1585 | /* |
@@ -1388,11 +1709,13 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) | |||
1388 | asus->ledd_status = 0xFFF; | 1709 | asus->ledd_status = 0xFFF; |
1389 | 1710 | ||
1390 | /* Set initial values of light sensor and level */ | 1711 | /* Set initial values of light sensor and level */ |
1391 | asus->light_switch = 0; /* Default to light sensor disabled */ | 1712 | asus->light_switch = !!als_status; |
1392 | asus->light_level = 5; /* level 5 for sensor sensitivity */ | 1713 | asus->light_level = 5; /* level 5 for sensor sensitivity */ |
1393 | 1714 | ||
1394 | if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && | 1715 | if (asus->is_pega_lucid) { |
1395 | !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { | 1716 | asus_als_switch(asus, asus->light_switch); |
1717 | } else if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && | ||
1718 | !acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { | ||
1396 | asus_als_switch(asus, asus->light_switch); | 1719 | asus_als_switch(asus, asus->light_switch); |
1397 | asus_als_level(asus, asus->light_level); | 1720 | asus_als_level(asus, asus->light_level); |
1398 | } | 1721 | } |
@@ -1439,9 +1762,10 @@ static int __devinit asus_acpi_add(struct acpi_device *device) | |||
1439 | goto fail_platform; | 1762 | goto fail_platform; |
1440 | 1763 | ||
1441 | /* | 1764 | /* |
1442 | * Register the platform device first. It is used as a parent for the | 1765 | * Need platform type detection first, then the platform |
1443 | * sub-devices below. | 1766 | * device. It is used as a parent for the sub-devices below. |
1444 | */ | 1767 | */ |
1768 | asus->is_pega_lucid = asus_check_pega_lucid(asus); | ||
1445 | result = asus_platform_init(asus); | 1769 | result = asus_platform_init(asus); |
1446 | if (result) | 1770 | if (result) |
1447 | goto fail_platform; | 1771 | goto fail_platform; |
@@ -1465,9 +1789,21 @@ static int __devinit asus_acpi_add(struct acpi_device *device) | |||
1465 | if (result) | 1789 | if (result) |
1466 | goto fail_rfkill; | 1790 | goto fail_rfkill; |
1467 | 1791 | ||
1792 | result = pega_accel_init(asus); | ||
1793 | if (result && result != -ENODEV) | ||
1794 | goto fail_pega_accel; | ||
1795 | |||
1796 | result = pega_rfkill_init(asus); | ||
1797 | if (result && result != -ENODEV) | ||
1798 | goto fail_pega_rfkill; | ||
1799 | |||
1468 | asus_device_present = true; | 1800 | asus_device_present = true; |
1469 | return 0; | 1801 | return 0; |
1470 | 1802 | ||
1803 | fail_pega_rfkill: | ||
1804 | pega_accel_exit(asus); | ||
1805 | fail_pega_accel: | ||
1806 | asus_rfkill_exit(asus); | ||
1471 | fail_rfkill: | 1807 | fail_rfkill: |
1472 | asus_led_exit(asus); | 1808 | asus_led_exit(asus); |
1473 | fail_led: | 1809 | fail_led: |
@@ -1491,6 +1827,8 @@ static int asus_acpi_remove(struct acpi_device *device, int type) | |||
1491 | asus_rfkill_exit(asus); | 1827 | asus_rfkill_exit(asus); |
1492 | asus_led_exit(asus); | 1828 | asus_led_exit(asus); |
1493 | asus_input_exit(asus); | 1829 | asus_input_exit(asus); |
1830 | pega_accel_exit(asus); | ||
1831 | pega_rfkill_exit(asus); | ||
1494 | asus_platform_exit(asus); | 1832 | asus_platform_exit(asus); |
1495 | 1833 | ||
1496 | kfree(asus->name); | 1834 | kfree(asus->name); |
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 95cba9ebf6c0..d1049ee3c9e8 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c | |||
@@ -453,7 +453,9 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) | |||
453 | 453 | ||
454 | static void asus_wmi_led_exit(struct asus_wmi *asus) | 454 | static void asus_wmi_led_exit(struct asus_wmi *asus) |
455 | { | 455 | { |
456 | if (asus->tpd_led.dev) | 456 | if (!IS_ERR_OR_NULL(asus->kbd_led.dev)) |
457 | led_classdev_unregister(&asus->kbd_led); | ||
458 | if (!IS_ERR_OR_NULL(asus->tpd_led.dev)) | ||
457 | led_classdev_unregister(&asus->tpd_led); | 459 | led_classdev_unregister(&asus->tpd_led); |
458 | if (asus->led_workqueue) | 460 | if (asus->led_workqueue) |
459 | destroy_workqueue(asus->led_workqueue); | 461 | destroy_workqueue(asus->led_workqueue); |
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index f31fa4efa725..a43cfd906c6d 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c | |||
@@ -60,6 +60,22 @@ struct calling_interface_structure { | |||
60 | struct calling_interface_token tokens[]; | 60 | struct calling_interface_token tokens[]; |
61 | } __packed; | 61 | } __packed; |
62 | 62 | ||
63 | struct quirk_entry { | ||
64 | u8 touchpad_led; | ||
65 | }; | ||
66 | |||
67 | static struct quirk_entry *quirks; | ||
68 | |||
69 | static struct quirk_entry quirk_dell_vostro_v130 = { | ||
70 | .touchpad_led = 1, | ||
71 | }; | ||
72 | |||
73 | static int dmi_matched(const struct dmi_system_id *dmi) | ||
74 | { | ||
75 | quirks = dmi->driver_data; | ||
76 | return 1; | ||
77 | } | ||
78 | |||
63 | static int da_command_address; | 79 | static int da_command_address; |
64 | static int da_command_code; | 80 | static int da_command_code; |
65 | static int da_num_tokens; | 81 | static int da_num_tokens; |
@@ -149,6 +165,27 @@ static struct dmi_system_id __devinitdata dell_blacklist[] = { | |||
149 | {} | 165 | {} |
150 | }; | 166 | }; |
151 | 167 | ||
168 | static struct dmi_system_id __devinitdata dell_quirks[] = { | ||
169 | { | ||
170 | .callback = dmi_matched, | ||
171 | .ident = "Dell Vostro V130", | ||
172 | .matches = { | ||
173 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
174 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"), | ||
175 | }, | ||
176 | .driver_data = &quirk_dell_vostro_v130, | ||
177 | }, | ||
178 | { | ||
179 | .callback = dmi_matched, | ||
180 | .ident = "Dell Vostro V131", | ||
181 | .matches = { | ||
182 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | ||
183 | DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), | ||
184 | }, | ||
185 | .driver_data = &quirk_dell_vostro_v130, | ||
186 | }, | ||
187 | }; | ||
188 | |||
152 | static struct calling_interface_buffer *buffer; | 189 | static struct calling_interface_buffer *buffer; |
153 | static struct page *bufferpage; | 190 | static struct page *bufferpage; |
154 | static DEFINE_MUTEX(buffer_mutex); | 191 | static DEFINE_MUTEX(buffer_mutex); |
@@ -552,6 +589,44 @@ static const struct backlight_ops dell_ops = { | |||
552 | .update_status = dell_send_intensity, | 589 | .update_status = dell_send_intensity, |
553 | }; | 590 | }; |
554 | 591 | ||
592 | static void touchpad_led_on() | ||
593 | { | ||
594 | int command = 0x97; | ||
595 | char data = 1; | ||
596 | i8042_command(&data, command | 1 << 12); | ||
597 | } | ||
598 | |||
599 | static void touchpad_led_off() | ||
600 | { | ||
601 | int command = 0x97; | ||
602 | char data = 2; | ||
603 | i8042_command(&data, command | 1 << 12); | ||
604 | } | ||
605 | |||
606 | static void touchpad_led_set(struct led_classdev *led_cdev, | ||
607 | enum led_brightness value) | ||
608 | { | ||
609 | if (value > 0) | ||
610 | touchpad_led_on(); | ||
611 | else | ||
612 | touchpad_led_off(); | ||
613 | } | ||
614 | |||
615 | static struct led_classdev touchpad_led = { | ||
616 | .name = "dell-laptop::touchpad", | ||
617 | .brightness_set = touchpad_led_set, | ||
618 | }; | ||
619 | |||
620 | static int __devinit touchpad_led_init(struct device *dev) | ||
621 | { | ||
622 | return led_classdev_register(dev, &touchpad_led); | ||
623 | } | ||
624 | |||
625 | static void touchpad_led_exit(void) | ||
626 | { | ||
627 | led_classdev_unregister(&touchpad_led); | ||
628 | } | ||
629 | |||
555 | static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, | 630 | static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, |
556 | struct serio *port) | 631 | struct serio *port) |
557 | { | 632 | { |
@@ -584,6 +659,10 @@ static int __init dell_init(void) | |||
584 | if (!dmi_check_system(dell_device_table)) | 659 | if (!dmi_check_system(dell_device_table)) |
585 | return -ENODEV; | 660 | return -ENODEV; |
586 | 661 | ||
662 | quirks = NULL; | ||
663 | /* find if this machine support other functions */ | ||
664 | dmi_check_system(dell_quirks); | ||
665 | |||
587 | dmi_walk(find_tokens, NULL); | 666 | dmi_walk(find_tokens, NULL); |
588 | 667 | ||
589 | if (!da_tokens) { | 668 | if (!da_tokens) { |
@@ -626,6 +705,9 @@ static int __init dell_init(void) | |||
626 | goto fail_filter; | 705 | goto fail_filter; |
627 | } | 706 | } |
628 | 707 | ||
708 | if (quirks && quirks->touchpad_led) | ||
709 | touchpad_led_init(&platform_device->dev); | ||
710 | |||
629 | dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); | 711 | dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); |
630 | if (dell_laptop_dir != NULL) | 712 | if (dell_laptop_dir != NULL) |
631 | debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, | 713 | debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, |
@@ -692,6 +774,8 @@ fail_platform_driver: | |||
692 | static void __exit dell_exit(void) | 774 | static void __exit dell_exit(void) |
693 | { | 775 | { |
694 | debugfs_remove_recursive(dell_laptop_dir); | 776 | debugfs_remove_recursive(dell_laptop_dir); |
777 | if (quirks && quirks->touchpad_led) | ||
778 | touchpad_led_exit(); | ||
695 | i8042_remove_filter(dell_laptop_i8042_filter); | 779 | i8042_remove_filter(dell_laptop_i8042_filter); |
696 | cancel_delayed_work_sync(&dell_rfkill_work); | 780 | cancel_delayed_work_sync(&dell_rfkill_work); |
697 | backlight_device_unregister(dell_backlight_device); | 781 | backlight_device_unregister(dell_backlight_device); |
diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 1c45d92e2163..ea44abd8df48 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c | |||
@@ -568,7 +568,7 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc) | |||
568 | 568 | ||
569 | static void eeepc_led_exit(struct eeepc_laptop *eeepc) | 569 | static void eeepc_led_exit(struct eeepc_laptop *eeepc) |
570 | { | 570 | { |
571 | if (eeepc->tpd_led.dev) | 571 | if (!IS_ERR_OR_NULL(eeepc->tpd_led.dev)) |
572 | led_classdev_unregister(&eeepc->tpd_led); | 572 | led_classdev_unregister(&eeepc->tpd_led); |
573 | if (eeepc->led_workqueue) | 573 | if (eeepc->led_workqueue) |
574 | destroy_workqueue(eeepc->led_workqueue); | 574 | destroy_workqueue(eeepc->led_workqueue); |
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 1b52d00e2f90..22b2dfa73148 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c | |||
@@ -76,6 +76,7 @@ static inline void delayed_sysfs_set(struct led_classdev *led_cdev, | |||
76 | /* For automatic insertion of the module */ | 76 | /* For automatic insertion of the module */ |
77 | static struct acpi_device_id lis3lv02d_device_ids[] = { | 77 | static struct acpi_device_id lis3lv02d_device_ids[] = { |
78 | {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ | 78 | {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ |
79 | {"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */ | ||
79 | {"", 0}, | 80 | {"", 0}, |
80 | }; | 81 | }; |
81 | MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); | 82 | MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); |
@@ -209,6 +210,8 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { | |||
209 | AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), | 210 | AXIS_DMI_MATCH("NC6715x", "HP Compaq 6715", y_inverted), |
210 | AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), | 211 | AXIS_DMI_MATCH("NC693xx", "HP EliteBook 693", xy_rotated_right), |
211 | AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), | 212 | AXIS_DMI_MATCH("NC693xx", "HP EliteBook 853", xy_swap), |
213 | AXIS_DMI_MATCH("NC854xx", "HP EliteBook 854", y_inverted), | ||
214 | AXIS_DMI_MATCH("NC273xx", "HP EliteBook 273", y_inverted), | ||
212 | /* Intel-based HP Pavilion dv5 */ | 215 | /* Intel-based HP Pavilion dv5 */ |
213 | AXIS_DMI_MATCH2("HPDV5_I", | 216 | AXIS_DMI_MATCH2("HPDV5_I", |
214 | PRODUCT_NAME, "HP Pavilion dv5", | 217 | PRODUCT_NAME, "HP Pavilion dv5", |
@@ -227,7 +230,12 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { | |||
227 | AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), | 230 | AXIS_DMI_MATCH("HPB452x", "HP ProBook 452", y_inverted), |
228 | AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), | 231 | AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), |
229 | AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), | 232 | AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), |
233 | AXIS_DMI_MATCH("HPB655x", "HP ProBook 655", xy_swap_inverted), | ||
230 | AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), | 234 | AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), |
235 | AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap), | ||
236 | AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap), | ||
237 | AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap), | ||
238 | AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted), | ||
231 | { NULL, } | 239 | { NULL, } |
232 | /* Laptop models without axis info (yet): | 240 | /* Laptop models without axis info (yet): |
233 | * "NC6910" "HP Compaq 6910" | 241 | * "NC6910" "HP Compaq 6910" |
@@ -320,7 +328,7 @@ static int lis3lv02d_add(struct acpi_device *device) | |||
320 | INIT_WORK(&hpled_led.work, delayed_set_status_worker); | 328 | INIT_WORK(&hpled_led.work, delayed_set_status_worker); |
321 | ret = led_classdev_register(NULL, &hpled_led.led_classdev); | 329 | ret = led_classdev_register(NULL, &hpled_led.led_classdev); |
322 | if (ret) { | 330 | if (ret) { |
323 | lis3lv02d_joystick_disable(); | 331 | lis3lv02d_joystick_disable(&lis3_dev); |
324 | lis3lv02d_poweroff(&lis3_dev); | 332 | lis3lv02d_poweroff(&lis3_dev); |
325 | flush_work(&hpled_led.work); | 333 | flush_work(&hpled_led.work); |
326 | return ret; | 334 | return ret; |
@@ -334,7 +342,7 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) | |||
334 | if (!device) | 342 | if (!device) |
335 | return -EINVAL; | 343 | return -EINVAL; |
336 | 344 | ||
337 | lis3lv02d_joystick_disable(); | 345 | lis3lv02d_joystick_disable(&lis3_dev); |
338 | lis3lv02d_poweroff(&lis3_dev); | 346 | lis3lv02d_poweroff(&lis3_dev); |
339 | 347 | ||
340 | led_classdev_unregister(&hpled_led.led_classdev); | 348 | led_classdev_unregister(&hpled_led.led_classdev); |
@@ -354,8 +362,7 @@ static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) | |||
354 | 362 | ||
355 | static int lis3lv02d_resume(struct acpi_device *device) | 363 | static int lis3lv02d_resume(struct acpi_device *device) |
356 | { | 364 | { |
357 | lis3lv02d_poweron(&lis3_dev); | 365 | return lis3lv02d_poweron(&lis3_dev); |
358 | return 0; | ||
359 | } | 366 | } |
360 | #else | 367 | #else |
361 | #define lis3lv02d_suspend NULL | 368 | #define lis3lv02d_suspend NULL |
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 0c595410e788..a36addf106a0 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c | |||
@@ -34,6 +34,8 @@ | |||
34 | #include <linux/input/sparse-keymap.h> | 34 | #include <linux/input/sparse-keymap.h> |
35 | #include <linux/backlight.h> | 35 | #include <linux/backlight.h> |
36 | #include <linux/fb.h> | 36 | #include <linux/fb.h> |
37 | #include <linux/debugfs.h> | ||
38 | #include <linux/seq_file.h> | ||
37 | 39 | ||
38 | #define IDEAPAD_RFKILL_DEV_NUM (3) | 40 | #define IDEAPAD_RFKILL_DEV_NUM (3) |
39 | 41 | ||
@@ -42,15 +44,41 @@ | |||
42 | #define CFG_WIFI_BIT (18) | 44 | #define CFG_WIFI_BIT (18) |
43 | #define CFG_CAMERA_BIT (19) | 45 | #define CFG_CAMERA_BIT (19) |
44 | 46 | ||
47 | enum { | ||
48 | VPCCMD_R_VPC1 = 0x10, | ||
49 | VPCCMD_R_BL_MAX, | ||
50 | VPCCMD_R_BL, | ||
51 | VPCCMD_W_BL, | ||
52 | VPCCMD_R_WIFI, | ||
53 | VPCCMD_W_WIFI, | ||
54 | VPCCMD_R_BT, | ||
55 | VPCCMD_W_BT, | ||
56 | VPCCMD_R_BL_POWER, | ||
57 | VPCCMD_R_NOVO, | ||
58 | VPCCMD_R_VPC2, | ||
59 | VPCCMD_R_TOUCHPAD, | ||
60 | VPCCMD_W_TOUCHPAD, | ||
61 | VPCCMD_R_CAMERA, | ||
62 | VPCCMD_W_CAMERA, | ||
63 | VPCCMD_R_3G, | ||
64 | VPCCMD_W_3G, | ||
65 | VPCCMD_R_ODD, /* 0x21 */ | ||
66 | VPCCMD_R_RF = 0x23, | ||
67 | VPCCMD_W_RF, | ||
68 | VPCCMD_W_BL_POWER = 0x33, | ||
69 | }; | ||
70 | |||
45 | struct ideapad_private { | 71 | struct ideapad_private { |
46 | struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; | 72 | struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; |
47 | struct platform_device *platform_device; | 73 | struct platform_device *platform_device; |
48 | struct input_dev *inputdev; | 74 | struct input_dev *inputdev; |
49 | struct backlight_device *blightdev; | 75 | struct backlight_device *blightdev; |
76 | struct dentry *debug; | ||
50 | unsigned long cfg; | 77 | unsigned long cfg; |
51 | }; | 78 | }; |
52 | 79 | ||
53 | static acpi_handle ideapad_handle; | 80 | static acpi_handle ideapad_handle; |
81 | static struct ideapad_private *ideapad_priv; | ||
54 | static bool no_bt_rfkill; | 82 | static bool no_bt_rfkill; |
55 | module_param(no_bt_rfkill, bool, 0444); | 83 | module_param(no_bt_rfkill, bool, 0444); |
56 | MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); | 84 | MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); |
@@ -164,6 +192,146 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data) | |||
164 | } | 192 | } |
165 | 193 | ||
166 | /* | 194 | /* |
195 | * debugfs | ||
196 | */ | ||
197 | #define DEBUGFS_EVENT_LEN (4096) | ||
198 | static int debugfs_status_show(struct seq_file *s, void *data) | ||
199 | { | ||
200 | unsigned long value; | ||
201 | |||
202 | if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &value)) | ||
203 | seq_printf(s, "Backlight max:\t%lu\n", value); | ||
204 | if (!read_ec_data(ideapad_handle, VPCCMD_R_BL, &value)) | ||
205 | seq_printf(s, "Backlight now:\t%lu\n", value); | ||
206 | if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &value)) | ||
207 | seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off"); | ||
208 | seq_printf(s, "=====================\n"); | ||
209 | |||
210 | if (!read_ec_data(ideapad_handle, VPCCMD_R_RF, &value)) | ||
211 | seq_printf(s, "Radio status:\t%s(%lu)\n", | ||
212 | value ? "On" : "Off", value); | ||
213 | if (!read_ec_data(ideapad_handle, VPCCMD_R_WIFI, &value)) | ||
214 | seq_printf(s, "Wifi status:\t%s(%lu)\n", | ||
215 | value ? "On" : "Off", value); | ||
216 | if (!read_ec_data(ideapad_handle, VPCCMD_R_BT, &value)) | ||
217 | seq_printf(s, "BT status:\t%s(%lu)\n", | ||
218 | value ? "On" : "Off", value); | ||
219 | if (!read_ec_data(ideapad_handle, VPCCMD_R_3G, &value)) | ||
220 | seq_printf(s, "3G status:\t%s(%lu)\n", | ||
221 | value ? "On" : "Off", value); | ||
222 | seq_printf(s, "=====================\n"); | ||
223 | |||
224 | if (!read_ec_data(ideapad_handle, VPCCMD_R_TOUCHPAD, &value)) | ||
225 | seq_printf(s, "Touchpad status:%s(%lu)\n", | ||
226 | value ? "On" : "Off", value); | ||
227 | if (!read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &value)) | ||
228 | seq_printf(s, "Camera status:\t%s(%lu)\n", | ||
229 | value ? "On" : "Off", value); | ||
230 | |||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | static int debugfs_status_open(struct inode *inode, struct file *file) | ||
235 | { | ||
236 | return single_open(file, debugfs_status_show, NULL); | ||
237 | } | ||
238 | |||
239 | static const struct file_operations debugfs_status_fops = { | ||
240 | .owner = THIS_MODULE, | ||
241 | .open = debugfs_status_open, | ||
242 | .read = seq_read, | ||
243 | .llseek = seq_lseek, | ||
244 | .release = single_release, | ||
245 | }; | ||
246 | |||
247 | static int debugfs_cfg_show(struct seq_file *s, void *data) | ||
248 | { | ||
249 | if (!ideapad_priv) { | ||
250 | seq_printf(s, "cfg: N/A\n"); | ||
251 | } else { | ||
252 | seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ", | ||
253 | ideapad_priv->cfg); | ||
254 | if (test_bit(CFG_BT_BIT, &ideapad_priv->cfg)) | ||
255 | seq_printf(s, "Bluetooth "); | ||
256 | if (test_bit(CFG_3G_BIT, &ideapad_priv->cfg)) | ||
257 | seq_printf(s, "3G "); | ||
258 | if (test_bit(CFG_WIFI_BIT, &ideapad_priv->cfg)) | ||
259 | seq_printf(s, "Wireless "); | ||
260 | if (test_bit(CFG_CAMERA_BIT, &ideapad_priv->cfg)) | ||
261 | seq_printf(s, "Camera "); | ||
262 | seq_printf(s, "\nGraphic: "); | ||
263 | switch ((ideapad_priv->cfg)&0x700) { | ||
264 | case 0x100: | ||
265 | seq_printf(s, "Intel"); | ||
266 | break; | ||
267 | case 0x200: | ||
268 | seq_printf(s, "ATI"); | ||
269 | break; | ||
270 | case 0x300: | ||
271 | seq_printf(s, "Nvidia"); | ||
272 | break; | ||
273 | case 0x400: | ||
274 | seq_printf(s, "Intel and ATI"); | ||
275 | break; | ||
276 | case 0x500: | ||
277 | seq_printf(s, "Intel and Nvidia"); | ||
278 | break; | ||
279 | } | ||
280 | seq_printf(s, "\n"); | ||
281 | } | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int debugfs_cfg_open(struct inode *inode, struct file *file) | ||
286 | { | ||
287 | return single_open(file, debugfs_cfg_show, NULL); | ||
288 | } | ||
289 | |||
290 | static const struct file_operations debugfs_cfg_fops = { | ||
291 | .owner = THIS_MODULE, | ||
292 | .open = debugfs_cfg_open, | ||
293 | .read = seq_read, | ||
294 | .llseek = seq_lseek, | ||
295 | .release = single_release, | ||
296 | }; | ||
297 | |||
298 | static int __devinit ideapad_debugfs_init(struct ideapad_private *priv) | ||
299 | { | ||
300 | struct dentry *node; | ||
301 | |||
302 | priv->debug = debugfs_create_dir("ideapad", NULL); | ||
303 | if (priv->debug == NULL) { | ||
304 | pr_err("failed to create debugfs directory"); | ||
305 | goto errout; | ||
306 | } | ||
307 | |||
308 | node = debugfs_create_file("cfg", S_IRUGO, priv->debug, NULL, | ||
309 | &debugfs_cfg_fops); | ||
310 | if (!node) { | ||
311 | pr_err("failed to create cfg in debugfs"); | ||
312 | goto errout; | ||
313 | } | ||
314 | |||
315 | node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL, | ||
316 | &debugfs_status_fops); | ||
317 | if (!node) { | ||
318 | pr_err("failed to create event in debugfs"); | ||
319 | goto errout; | ||
320 | } | ||
321 | |||
322 | return 0; | ||
323 | |||
324 | errout: | ||
325 | return -ENOMEM; | ||
326 | } | ||
327 | |||
328 | static void ideapad_debugfs_exit(struct ideapad_private *priv) | ||
329 | { | ||
330 | debugfs_remove_recursive(priv->debug); | ||
331 | priv->debug = NULL; | ||
332 | } | ||
333 | |||
334 | /* | ||
167 | * sysfs | 335 | * sysfs |
168 | */ | 336 | */ |
169 | static ssize_t show_ideapad_cam(struct device *dev, | 337 | static ssize_t show_ideapad_cam(struct device *dev, |
@@ -172,7 +340,7 @@ static ssize_t show_ideapad_cam(struct device *dev, | |||
172 | { | 340 | { |
173 | unsigned long result; | 341 | unsigned long result; |
174 | 342 | ||
175 | if (read_ec_data(ideapad_handle, 0x1D, &result)) | 343 | if (read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &result)) |
176 | return sprintf(buf, "-1\n"); | 344 | return sprintf(buf, "-1\n"); |
177 | return sprintf(buf, "%lu\n", result); | 345 | return sprintf(buf, "%lu\n", result); |
178 | } | 346 | } |
@@ -187,7 +355,7 @@ static ssize_t store_ideapad_cam(struct device *dev, | |||
187 | return 0; | 355 | return 0; |
188 | if (sscanf(buf, "%i", &state) != 1) | 356 | if (sscanf(buf, "%i", &state) != 1) |
189 | return -EINVAL; | 357 | return -EINVAL; |
190 | ret = write_ec_cmd(ideapad_handle, 0x1E, state); | 358 | ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state); |
191 | if (ret < 0) | 359 | if (ret < 0) |
192 | return ret; | 360 | return ret; |
193 | return count; | 361 | return count; |
@@ -195,20 +363,8 @@ static ssize_t store_ideapad_cam(struct device *dev, | |||
195 | 363 | ||
196 | static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); | 364 | static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); |
197 | 365 | ||
198 | static ssize_t show_ideapad_cfg(struct device *dev, | ||
199 | struct device_attribute *attr, | ||
200 | char *buf) | ||
201 | { | ||
202 | struct ideapad_private *priv = dev_get_drvdata(dev); | ||
203 | |||
204 | return sprintf(buf, "0x%.8lX\n", priv->cfg); | ||
205 | } | ||
206 | |||
207 | static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL); | ||
208 | |||
209 | static struct attribute *ideapad_attributes[] = { | 366 | static struct attribute *ideapad_attributes[] = { |
210 | &dev_attr_camera_power.attr, | 367 | &dev_attr_camera_power.attr, |
211 | &dev_attr_cfg.attr, | ||
212 | NULL | 368 | NULL |
213 | }; | 369 | }; |
214 | 370 | ||
@@ -244,9 +400,9 @@ struct ideapad_rfk_data { | |||
244 | }; | 400 | }; |
245 | 401 | ||
246 | const struct ideapad_rfk_data ideapad_rfk_data[] = { | 402 | const struct ideapad_rfk_data ideapad_rfk_data[] = { |
247 | { "ideapad_wlan", CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN }, | 403 | { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN }, |
248 | { "ideapad_bluetooth", CFG_BT_BIT, 0x17, RFKILL_TYPE_BLUETOOTH }, | 404 | { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH }, |
249 | { "ideapad_3g", CFG_3G_BIT, 0x20, RFKILL_TYPE_WWAN }, | 405 | { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN }, |
250 | }; | 406 | }; |
251 | 407 | ||
252 | static int ideapad_rfk_set(void *data, bool blocked) | 408 | static int ideapad_rfk_set(void *data, bool blocked) |
@@ -260,13 +416,12 @@ static struct rfkill_ops ideapad_rfk_ops = { | |||
260 | .set_block = ideapad_rfk_set, | 416 | .set_block = ideapad_rfk_set, |
261 | }; | 417 | }; |
262 | 418 | ||
263 | static void ideapad_sync_rfk_state(struct acpi_device *adevice) | 419 | static void ideapad_sync_rfk_state(struct ideapad_private *priv) |
264 | { | 420 | { |
265 | struct ideapad_private *priv = dev_get_drvdata(&adevice->dev); | ||
266 | unsigned long hw_blocked; | 421 | unsigned long hw_blocked; |
267 | int i; | 422 | int i; |
268 | 423 | ||
269 | if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) | 424 | if (read_ec_data(ideapad_handle, VPCCMD_R_RF, &hw_blocked)) |
270 | return; | 425 | return; |
271 | hw_blocked = !hw_blocked; | 426 | hw_blocked = !hw_blocked; |
272 | 427 | ||
@@ -363,8 +518,10 @@ static void ideapad_platform_exit(struct ideapad_private *priv) | |||
363 | * input device | 518 | * input device |
364 | */ | 519 | */ |
365 | static const struct key_entry ideapad_keymap[] = { | 520 | static const struct key_entry ideapad_keymap[] = { |
366 | { KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, | 521 | { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } }, |
367 | { KE_KEY, 0x0D, { KEY_WLAN } }, | 522 | { KE_KEY, 13, { KEY_WLAN } }, |
523 | { KE_KEY, 16, { KEY_PROG1 } }, | ||
524 | { KE_KEY, 17, { KEY_PROG2 } }, | ||
368 | { KE_END, 0 }, | 525 | { KE_END, 0 }, |
369 | }; | 526 | }; |
370 | 527 | ||
@@ -419,6 +576,18 @@ static void ideapad_input_report(struct ideapad_private *priv, | |||
419 | sparse_keymap_report_event(priv->inputdev, scancode, 1, true); | 576 | sparse_keymap_report_event(priv->inputdev, scancode, 1, true); |
420 | } | 577 | } |
421 | 578 | ||
579 | static void ideapad_input_novokey(struct ideapad_private *priv) | ||
580 | { | ||
581 | unsigned long long_pressed; | ||
582 | |||
583 | if (read_ec_data(ideapad_handle, VPCCMD_R_NOVO, &long_pressed)) | ||
584 | return; | ||
585 | if (long_pressed) | ||
586 | ideapad_input_report(priv, 17); | ||
587 | else | ||
588 | ideapad_input_report(priv, 16); | ||
589 | } | ||
590 | |||
422 | /* | 591 | /* |
423 | * backlight | 592 | * backlight |
424 | */ | 593 | */ |
@@ -426,16 +595,17 @@ static int ideapad_backlight_get_brightness(struct backlight_device *blightdev) | |||
426 | { | 595 | { |
427 | unsigned long now; | 596 | unsigned long now; |
428 | 597 | ||
429 | if (read_ec_data(ideapad_handle, 0x12, &now)) | 598 | if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now)) |
430 | return -EIO; | 599 | return -EIO; |
431 | return now; | 600 | return now; |
432 | } | 601 | } |
433 | 602 | ||
434 | static int ideapad_backlight_update_status(struct backlight_device *blightdev) | 603 | static int ideapad_backlight_update_status(struct backlight_device *blightdev) |
435 | { | 604 | { |
436 | if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness)) | 605 | if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL, |
606 | blightdev->props.brightness)) | ||
437 | return -EIO; | 607 | return -EIO; |
438 | if (write_ec_cmd(ideapad_handle, 0x33, | 608 | if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL_POWER, |
439 | blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1)) | 609 | blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1)) |
440 | return -EIO; | 610 | return -EIO; |
441 | 611 | ||
@@ -453,11 +623,11 @@ static int ideapad_backlight_init(struct ideapad_private *priv) | |||
453 | struct backlight_properties props; | 623 | struct backlight_properties props; |
454 | unsigned long max, now, power; | 624 | unsigned long max, now, power; |
455 | 625 | ||
456 | if (read_ec_data(ideapad_handle, 0x11, &max)) | 626 | if (read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &max)) |
457 | return -EIO; | 627 | return -EIO; |
458 | if (read_ec_data(ideapad_handle, 0x12, &now)) | 628 | if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now)) |
459 | return -EIO; | 629 | return -EIO; |
460 | if (read_ec_data(ideapad_handle, 0x18, &power)) | 630 | if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power)) |
461 | return -EIO; | 631 | return -EIO; |
462 | 632 | ||
463 | memset(&props, 0, sizeof(struct backlight_properties)); | 633 | memset(&props, 0, sizeof(struct backlight_properties)); |
@@ -493,7 +663,9 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv) | |||
493 | unsigned long power; | 663 | unsigned long power; |
494 | struct backlight_device *blightdev = priv->blightdev; | 664 | struct backlight_device *blightdev = priv->blightdev; |
495 | 665 | ||
496 | if (read_ec_data(ideapad_handle, 0x18, &power)) | 666 | if (!blightdev) |
667 | return; | ||
668 | if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power)) | ||
497 | return; | 669 | return; |
498 | blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; | 670 | blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; |
499 | } | 671 | } |
@@ -504,7 +676,7 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv) | |||
504 | 676 | ||
505 | /* if we control brightness via acpi video driver */ | 677 | /* if we control brightness via acpi video driver */ |
506 | if (priv->blightdev == NULL) { | 678 | if (priv->blightdev == NULL) { |
507 | read_ec_data(ideapad_handle, 0x12, &now); | 679 | read_ec_data(ideapad_handle, VPCCMD_R_BL, &now); |
508 | return; | 680 | return; |
509 | } | 681 | } |
510 | 682 | ||
@@ -533,6 +705,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) | |||
533 | if (!priv) | 705 | if (!priv) |
534 | return -ENOMEM; | 706 | return -ENOMEM; |
535 | dev_set_drvdata(&adevice->dev, priv); | 707 | dev_set_drvdata(&adevice->dev, priv); |
708 | ideapad_priv = priv; | ||
536 | ideapad_handle = adevice->handle; | 709 | ideapad_handle = adevice->handle; |
537 | priv->cfg = cfg; | 710 | priv->cfg = cfg; |
538 | 711 | ||
@@ -540,6 +713,10 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) | |||
540 | if (ret) | 713 | if (ret) |
541 | goto platform_failed; | 714 | goto platform_failed; |
542 | 715 | ||
716 | ret = ideapad_debugfs_init(priv); | ||
717 | if (ret) | ||
718 | goto debugfs_failed; | ||
719 | |||
543 | ret = ideapad_input_init(priv); | 720 | ret = ideapad_input_init(priv); |
544 | if (ret) | 721 | if (ret) |
545 | goto input_failed; | 722 | goto input_failed; |
@@ -550,7 +727,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice) | |||
550 | else | 727 | else |
551 | priv->rfk[i] = NULL; | 728 | priv->rfk[i] = NULL; |
552 | } | 729 | } |
553 | ideapad_sync_rfk_state(adevice); | 730 | ideapad_sync_rfk_state(priv); |
554 | 731 | ||
555 | if (!acpi_video_backlight_support()) { | 732 | if (!acpi_video_backlight_support()) { |
556 | ret = ideapad_backlight_init(priv); | 733 | ret = ideapad_backlight_init(priv); |
@@ -565,6 +742,8 @@ backlight_failed: | |||
565 | ideapad_unregister_rfkill(adevice, i); | 742 | ideapad_unregister_rfkill(adevice, i); |
566 | ideapad_input_exit(priv); | 743 | ideapad_input_exit(priv); |
567 | input_failed: | 744 | input_failed: |
745 | ideapad_debugfs_exit(priv); | ||
746 | debugfs_failed: | ||
568 | ideapad_platform_exit(priv); | 747 | ideapad_platform_exit(priv); |
569 | platform_failed: | 748 | platform_failed: |
570 | kfree(priv); | 749 | kfree(priv); |
@@ -580,6 +759,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) | |||
580 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) | 759 | for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) |
581 | ideapad_unregister_rfkill(adevice, i); | 760 | ideapad_unregister_rfkill(adevice, i); |
582 | ideapad_input_exit(priv); | 761 | ideapad_input_exit(priv); |
762 | ideapad_debugfs_exit(priv); | ||
583 | ideapad_platform_exit(priv); | 763 | ideapad_platform_exit(priv); |
584 | dev_set_drvdata(&adevice->dev, NULL); | 764 | dev_set_drvdata(&adevice->dev, NULL); |
585 | kfree(priv); | 765 | kfree(priv); |
@@ -593,9 +773,9 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | |||
593 | acpi_handle handle = adevice->handle; | 773 | acpi_handle handle = adevice->handle; |
594 | unsigned long vpc1, vpc2, vpc_bit; | 774 | unsigned long vpc1, vpc2, vpc_bit; |
595 | 775 | ||
596 | if (read_ec_data(handle, 0x10, &vpc1)) | 776 | if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1)) |
597 | return; | 777 | return; |
598 | if (read_ec_data(handle, 0x1A, &vpc2)) | 778 | if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2)) |
599 | return; | 779 | return; |
600 | 780 | ||
601 | vpc1 = (vpc2 << 8) | vpc1; | 781 | vpc1 = (vpc2 << 8) | vpc1; |
@@ -603,11 +783,14 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event) | |||
603 | if (test_bit(vpc_bit, &vpc1)) { | 783 | if (test_bit(vpc_bit, &vpc1)) { |
604 | switch (vpc_bit) { | 784 | switch (vpc_bit) { |
605 | case 9: | 785 | case 9: |
606 | ideapad_sync_rfk_state(adevice); | 786 | ideapad_sync_rfk_state(priv); |
607 | break; | 787 | break; |
608 | case 4: | 788 | case 4: |
609 | ideapad_backlight_notify_brightness(priv); | 789 | ideapad_backlight_notify_brightness(priv); |
610 | break; | 790 | break; |
791 | case 3: | ||
792 | ideapad_input_novokey(priv); | ||
793 | break; | ||
611 | case 2: | 794 | case 2: |
612 | ideapad_backlight_notify_power(priv); | 795 | ideapad_backlight_notify_power(priv); |
613 | break; | 796 | break; |
diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index b93a03259c16..2d0f9136ea9a 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c | |||
@@ -24,7 +24,7 @@ | |||
24 | #include <linux/init.h> | 24 | #include <linux/init.h> |
25 | #include <asm/intel_scu_ipc.h> | 25 | #include <asm/intel_scu_ipc.h> |
26 | 26 | ||
27 | static u32 major; | 27 | static int major; |
28 | 28 | ||
29 | #define MAX_FW_SIZE 264192 | 29 | #define MAX_FW_SIZE 264192 |
30 | 30 | ||
@@ -117,7 +117,11 @@ static const struct file_operations scu_ipc_fops = { | |||
117 | 117 | ||
118 | static int __init ipc_module_init(void) | 118 | static int __init ipc_module_init(void) |
119 | { | 119 | { |
120 | return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); | 120 | major = register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); |
121 | if (major < 0) | ||
122 | return major; | ||
123 | |||
124 | return 0; | ||
121 | } | 125 | } |
122 | 126 | ||
123 | static void __exit ipc_module_exit(void) | 127 | static void __exit ipc_module_exit(void) |
diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 359163011044..09e26bfd4643 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c | |||
@@ -226,6 +226,7 @@ static struct backlight_device *backlight_device; | |||
226 | static struct mutex sabi_mutex; | 226 | static struct mutex sabi_mutex; |
227 | static struct platform_device *sdev; | 227 | static struct platform_device *sdev; |
228 | static struct rfkill *rfk; | 228 | static struct rfkill *rfk; |
229 | static bool has_stepping_quirk; | ||
229 | 230 | ||
230 | static int force; | 231 | static int force; |
231 | module_param(force, bool, 0); | 232 | module_param(force, bool, 0); |
@@ -370,15 +371,28 @@ static u8 read_brightness(void) | |||
370 | &sretval); | 371 | &sretval); |
371 | if (!retval) { | 372 | if (!retval) { |
372 | user_brightness = sretval.retval[0]; | 373 | user_brightness = sretval.retval[0]; |
373 | if (user_brightness != 0) | 374 | if (user_brightness > sabi_config->min_brightness) |
374 | user_brightness -= sabi_config->min_brightness; | 375 | user_brightness -= sabi_config->min_brightness; |
376 | else | ||
377 | user_brightness = 0; | ||
375 | } | 378 | } |
376 | return user_brightness; | 379 | return user_brightness; |
377 | } | 380 | } |
378 | 381 | ||
379 | static void set_brightness(u8 user_brightness) | 382 | static void set_brightness(u8 user_brightness) |
380 | { | 383 | { |
381 | u8 user_level = user_brightness - sabi_config->min_brightness; | 384 | u8 user_level = user_brightness + sabi_config->min_brightness; |
385 | |||
386 | if (has_stepping_quirk && user_level != 0) { | ||
387 | /* | ||
388 | * short circuit if the specified level is what's already set | ||
389 | * to prevent the screen from flickering needlessly | ||
390 | */ | ||
391 | if (user_brightness == read_brightness()) | ||
392 | return; | ||
393 | |||
394 | sabi_set_command(sabi_config->commands.set_brightness, 0); | ||
395 | } | ||
382 | 396 | ||
383 | sabi_set_command(sabi_config->commands.set_brightness, user_level); | 397 | sabi_set_command(sabi_config->commands.set_brightness, user_level); |
384 | } | 398 | } |
@@ -388,6 +402,40 @@ static int get_brightness(struct backlight_device *bd) | |||
388 | return (int)read_brightness(); | 402 | return (int)read_brightness(); |
389 | } | 403 | } |
390 | 404 | ||
405 | static void check_for_stepping_quirk(void) | ||
406 | { | ||
407 | u8 initial_level; | ||
408 | u8 check_level; | ||
409 | u8 orig_level = read_brightness(); | ||
410 | |||
411 | /* | ||
412 | * Some laptops exhibit the strange behaviour of stepping toward | ||
413 | * (rather than setting) the brightness except when changing to/from | ||
414 | * brightness level 0. This behaviour is checked for here and worked | ||
415 | * around in set_brightness. | ||
416 | */ | ||
417 | |||
418 | if (orig_level == 0) | ||
419 | set_brightness(1); | ||
420 | |||
421 | initial_level = read_brightness(); | ||
422 | |||
423 | if (initial_level <= 2) | ||
424 | check_level = initial_level + 2; | ||
425 | else | ||
426 | check_level = initial_level - 2; | ||
427 | |||
428 | has_stepping_quirk = false; | ||
429 | set_brightness(check_level); | ||
430 | |||
431 | if (read_brightness() != check_level) { | ||
432 | has_stepping_quirk = true; | ||
433 | pr_info("enabled workaround for brightness stepping quirk\n"); | ||
434 | } | ||
435 | |||
436 | set_brightness(orig_level); | ||
437 | } | ||
438 | |||
391 | static int update_status(struct backlight_device *bd) | 439 | static int update_status(struct backlight_device *bd) |
392 | { | 440 | { |
393 | set_brightness(bd->props.brightness); | 441 | set_brightness(bd->props.brightness); |
@@ -621,6 +669,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { | |||
621 | .callback = dmi_check_cb, | 669 | .callback = dmi_check_cb, |
622 | }, | 670 | }, |
623 | { | 671 | { |
672 | .ident = "N220", | ||
673 | .matches = { | ||
674 | DMI_MATCH(DMI_SYS_VENDOR, | ||
675 | "SAMSUNG ELECTRONICS CO., LTD."), | ||
676 | DMI_MATCH(DMI_PRODUCT_NAME, "N220"), | ||
677 | DMI_MATCH(DMI_BOARD_NAME, "N220"), | ||
678 | }, | ||
679 | .callback = dmi_check_cb, | ||
680 | }, | ||
681 | { | ||
624 | .ident = "N150/N210/N220/N230", | 682 | .ident = "N150/N210/N220/N230", |
625 | .matches = { | 683 | .matches = { |
626 | DMI_MATCH(DMI_SYS_VENDOR, | 684 | DMI_MATCH(DMI_SYS_VENDOR, |
@@ -641,6 +699,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { | |||
641 | .callback = dmi_check_cb, | 699 | .callback = dmi_check_cb, |
642 | }, | 700 | }, |
643 | { | 701 | { |
702 | .ident = "R700", | ||
703 | .matches = { | ||
704 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
705 | DMI_MATCH(DMI_PRODUCT_NAME, "SR700"), | ||
706 | DMI_MATCH(DMI_BOARD_NAME, "SR700"), | ||
707 | }, | ||
708 | .callback = dmi_check_cb, | ||
709 | }, | ||
710 | { | ||
644 | .ident = "R530/R730", | 711 | .ident = "R530/R730", |
645 | .matches = { | 712 | .matches = { |
646 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | 713 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), |
@@ -686,6 +753,33 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { | |||
686 | }, | 753 | }, |
687 | .callback = dmi_check_cb, | 754 | .callback = dmi_check_cb, |
688 | }, | 755 | }, |
756 | { | ||
757 | .ident = "R528/R728", | ||
758 | .matches = { | ||
759 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
760 | DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"), | ||
761 | DMI_MATCH(DMI_BOARD_NAME, "R528/R728"), | ||
762 | }, | ||
763 | .callback = dmi_check_cb, | ||
764 | }, | ||
765 | { | ||
766 | .ident = "NC210/NC110", | ||
767 | .matches = { | ||
768 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
769 | DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"), | ||
770 | DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"), | ||
771 | }, | ||
772 | .callback = dmi_check_cb, | ||
773 | }, | ||
774 | { | ||
775 | .ident = "X520", | ||
776 | .matches = { | ||
777 | DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), | ||
778 | DMI_MATCH(DMI_PRODUCT_NAME, "X520"), | ||
779 | DMI_MATCH(DMI_BOARD_NAME, "X520"), | ||
780 | }, | ||
781 | .callback = dmi_check_cb, | ||
782 | }, | ||
689 | { }, | 783 | { }, |
690 | }; | 784 | }; |
691 | MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); | 785 | MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); |
@@ -770,7 +864,7 @@ static int __init samsung_init(void) | |||
770 | sabi_iface = ioremap_nocache(ifaceP, 16); | 864 | sabi_iface = ioremap_nocache(ifaceP, 16); |
771 | if (!sabi_iface) { | 865 | if (!sabi_iface) { |
772 | pr_err("Can't remap %x\n", ifaceP); | 866 | pr_err("Can't remap %x\n", ifaceP); |
773 | goto exit; | 867 | goto error_no_signature; |
774 | } | 868 | } |
775 | if (debug) { | 869 | if (debug) { |
776 | printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); | 870 | printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); |
@@ -794,6 +888,9 @@ static int __init samsung_init(void) | |||
794 | } | 888 | } |
795 | } | 889 | } |
796 | 890 | ||
891 | /* Check for stepping quirk */ | ||
892 | check_for_stepping_quirk(); | ||
893 | |||
797 | /* knock up a platform device to hang stuff off of */ | 894 | /* knock up a platform device to hang stuff off of */ |
798 | sdev = platform_device_register_simple("samsung", -1, NULL, 0); | 895 | sdev = platform_device_register_simple("samsung", -1, NULL, 0); |
799 | if (IS_ERR(sdev)) | 896 | if (IS_ERR(sdev)) |
@@ -802,7 +899,8 @@ static int __init samsung_init(void) | |||
802 | /* create a backlight device to talk to this one */ | 899 | /* create a backlight device to talk to this one */ |
803 | memset(&props, 0, sizeof(struct backlight_properties)); | 900 | memset(&props, 0, sizeof(struct backlight_properties)); |
804 | props.type = BACKLIGHT_PLATFORM; | 901 | props.type = BACKLIGHT_PLATFORM; |
805 | props.max_brightness = sabi_config->max_brightness; | 902 | props.max_brightness = sabi_config->max_brightness - |
903 | sabi_config->min_brightness; | ||
806 | backlight_device = backlight_device_register("samsung", &sdev->dev, | 904 | backlight_device = backlight_device_register("samsung", &sdev->dev, |
807 | NULL, &backlight_ops, | 905 | NULL, &backlight_ops, |
808 | &props); | 906 | &props); |
@@ -821,7 +919,6 @@ static int __init samsung_init(void) | |||
821 | if (retval) | 919 | if (retval) |
822 | goto error_file_create; | 920 | goto error_file_create; |
823 | 921 | ||
824 | exit: | ||
825 | return 0; | 922 | return 0; |
826 | 923 | ||
827 | error_file_create: | 924 | error_file_create: |
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index bbd182e178cb..c006dee5ebfe 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -3281,7 +3281,7 @@ static int sony_pic_add(struct acpi_device *device) | |||
3281 | /* request IRQ */ | 3281 | /* request IRQ */ |
3282 | list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) { | 3282 | list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) { |
3283 | if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, | 3283 | if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, |
3284 | IRQF_DISABLED, "sony-laptop", &spic_dev)) { | 3284 | 0, "sony-laptop", &spic_dev)) { |
3285 | dprintk("IRQ: %d - triggering: %d - " | 3285 | dprintk("IRQ: %d - triggering: %d - " |
3286 | "polarity: %d - shr: %d\n", | 3286 | "polarity: %d - shr: %d\n", |
3287 | irq->irq.interrupts[0], | 3287 | irq->irq.interrupts[0], |
diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 4c20447ddbb7..d528daa0e81c 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c | |||
@@ -41,6 +41,7 @@ static const struct key_entry topstar_keymap[] = { | |||
41 | { KE_KEY, 0x8c, { KEY_MEDIA } }, | 41 | { KE_KEY, 0x8c, { KEY_MEDIA } }, |
42 | 42 | ||
43 | /* Known non hotkey events don't handled or that we don't care yet */ | 43 | /* Known non hotkey events don't handled or that we don't care yet */ |
44 | { KE_IGNORE, 0x82, }, /* backlight event */ | ||
44 | { KE_IGNORE, 0x8e, }, | 45 | { KE_IGNORE, 0x8e, }, |
45 | { KE_IGNORE, 0x8f, }, | 46 | { KE_IGNORE, 0x8f, }, |
46 | { KE_IGNORE, 0x90, }, | 47 | { KE_IGNORE, 0x90, }, |
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index cb009b2629ee..13ef8c37471d 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c | |||
@@ -47,7 +47,6 @@ | |||
47 | #include <linux/proc_fs.h> | 47 | #include <linux/proc_fs.h> |
48 | #include <linux/seq_file.h> | 48 | #include <linux/seq_file.h> |
49 | #include <linux/backlight.h> | 49 | #include <linux/backlight.h> |
50 | #include <linux/platform_device.h> | ||
51 | #include <linux/rfkill.h> | 50 | #include <linux/rfkill.h> |
52 | #include <linux/input.h> | 51 | #include <linux/input.h> |
53 | #include <linux/input/sparse-keymap.h> | 52 | #include <linux/input/sparse-keymap.h> |
@@ -63,11 +62,7 @@ MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); | |||
63 | MODULE_LICENSE("GPL"); | 62 | MODULE_LICENSE("GPL"); |
64 | 63 | ||
65 | /* Toshiba ACPI method paths */ | 64 | /* Toshiba ACPI method paths */ |
66 | #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" | ||
67 | #define TOSH_INTERFACE_1 "\\_SB_.VALD" | ||
68 | #define TOSH_INTERFACE_2 "\\_SB_.VALZ" | ||
69 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" | 65 | #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" |
70 | #define GHCI_METHOD ".GHCI" | ||
71 | 66 | ||
72 | /* Toshiba HCI interface definitions | 67 | /* Toshiba HCI interface definitions |
73 | * | 68 | * |
@@ -111,6 +106,25 @@ MODULE_LICENSE("GPL"); | |||
111 | #define HCI_WIRELESS_BT_ATTACH 0x40 | 106 | #define HCI_WIRELESS_BT_ATTACH 0x40 |
112 | #define HCI_WIRELESS_BT_POWER 0x80 | 107 | #define HCI_WIRELESS_BT_POWER 0x80 |
113 | 108 | ||
109 | struct toshiba_acpi_dev { | ||
110 | struct acpi_device *acpi_dev; | ||
111 | const char *method_hci; | ||
112 | struct rfkill *bt_rfk; | ||
113 | struct input_dev *hotkey_dev; | ||
114 | struct backlight_device *backlight_dev; | ||
115 | struct led_classdev led_dev; | ||
116 | |||
117 | int force_fan; | ||
118 | int last_key_event; | ||
119 | int key_event_valid; | ||
120 | |||
121 | int illumination_supported:1; | ||
122 | int video_supported:1; | ||
123 | int fan_supported:1; | ||
124 | |||
125 | struct mutex mutex; | ||
126 | }; | ||
127 | |||
114 | static const struct acpi_device_id toshiba_device_ids[] = { | 128 | static const struct acpi_device_id toshiba_device_ids[] = { |
115 | {"TOS6200", 0}, | 129 | {"TOS6200", 0}, |
116 | {"TOS6208", 0}, | 130 | {"TOS6208", 0}, |
@@ -119,7 +133,7 @@ static const struct acpi_device_id toshiba_device_ids[] = { | |||
119 | }; | 133 | }; |
120 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); | 134 | MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); |
121 | 135 | ||
122 | static const struct key_entry toshiba_acpi_keymap[] __initconst = { | 136 | static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { |
123 | { KE_KEY, 0x101, { KEY_MUTE } }, | 137 | { KE_KEY, 0x101, { KEY_MUTE } }, |
124 | { KE_KEY, 0x102, { KEY_ZOOMOUT } }, | 138 | { KE_KEY, 0x102, { KEY_ZOOMOUT } }, |
125 | { KE_KEY, 0x103, { KEY_ZOOMIN } }, | 139 | { KE_KEY, 0x103, { KEY_ZOOMIN } }, |
@@ -155,15 +169,6 @@ static __inline__ void _set_bit(u32 * word, u32 mask, int value) | |||
155 | /* acpi interface wrappers | 169 | /* acpi interface wrappers |
156 | */ | 170 | */ |
157 | 171 | ||
158 | static int is_valid_acpi_path(const char *methodName) | ||
159 | { | ||
160 | acpi_handle handle; | ||
161 | acpi_status status; | ||
162 | |||
163 | status = acpi_get_handle(NULL, (char *)methodName, &handle); | ||
164 | return !ACPI_FAILURE(status); | ||
165 | } | ||
166 | |||
167 | static int write_acpi_int(const char *methodName, int val) | 172 | static int write_acpi_int(const char *methodName, int val) |
168 | { | 173 | { |
169 | struct acpi_object_list params; | 174 | struct acpi_object_list params; |
@@ -176,32 +181,14 @@ static int write_acpi_int(const char *methodName, int val) | |||
176 | in_objs[0].integer.value = val; | 181 | in_objs[0].integer.value = val; |
177 | 182 | ||
178 | status = acpi_evaluate_object(NULL, (char *)methodName, ¶ms, NULL); | 183 | status = acpi_evaluate_object(NULL, (char *)methodName, ¶ms, NULL); |
179 | return (status == AE_OK); | 184 | return (status == AE_OK) ? 0 : -EIO; |
180 | } | ||
181 | |||
182 | #if 0 | ||
183 | static int read_acpi_int(const char *methodName, int *pVal) | ||
184 | { | ||
185 | struct acpi_buffer results; | ||
186 | union acpi_object out_objs[1]; | ||
187 | acpi_status status; | ||
188 | |||
189 | results.length = sizeof(out_objs); | ||
190 | results.pointer = out_objs; | ||
191 | |||
192 | status = acpi_evaluate_object(0, (char *)methodName, 0, &results); | ||
193 | *pVal = out_objs[0].integer.value; | ||
194 | |||
195 | return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER); | ||
196 | } | 185 | } |
197 | #endif | ||
198 | |||
199 | static const char *method_hci /*= 0*/ ; | ||
200 | 186 | ||
201 | /* Perform a raw HCI call. Here we don't care about input or output buffer | 187 | /* Perform a raw HCI call. Here we don't care about input or output buffer |
202 | * format. | 188 | * format. |
203 | */ | 189 | */ |
204 | static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) | 190 | static acpi_status hci_raw(struct toshiba_acpi_dev *dev, |
191 | const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) | ||
205 | { | 192 | { |
206 | struct acpi_object_list params; | 193 | struct acpi_object_list params; |
207 | union acpi_object in_objs[HCI_WORDS]; | 194 | union acpi_object in_objs[HCI_WORDS]; |
@@ -220,7 +207,8 @@ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) | |||
220 | results.length = sizeof(out_objs); | 207 | results.length = sizeof(out_objs); |
221 | results.pointer = out_objs; | 208 | results.pointer = out_objs; |
222 | 209 | ||
223 | status = acpi_evaluate_object(NULL, (char *)method_hci, ¶ms, | 210 | status = acpi_evaluate_object(dev->acpi_dev->handle, |
211 | (char *)dev->method_hci, ¶ms, | ||
224 | &results); | 212 | &results); |
225 | if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { | 213 | if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { |
226 | for (i = 0; i < out_objs->package.count; ++i) { | 214 | for (i = 0; i < out_objs->package.count; ++i) { |
@@ -237,85 +225,79 @@ static acpi_status hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) | |||
237 | * may be useful (such as "not supported"). | 225 | * may be useful (such as "not supported"). |
238 | */ | 226 | */ |
239 | 227 | ||
240 | static acpi_status hci_write1(u32 reg, u32 in1, u32 * result) | 228 | static acpi_status hci_write1(struct toshiba_acpi_dev *dev, u32 reg, |
229 | u32 in1, u32 *result) | ||
241 | { | 230 | { |
242 | u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; | 231 | u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; |
243 | u32 out[HCI_WORDS]; | 232 | u32 out[HCI_WORDS]; |
244 | acpi_status status = hci_raw(in, out); | 233 | acpi_status status = hci_raw(dev, in, out); |
245 | *result = (status == AE_OK) ? out[0] : HCI_FAILURE; | 234 | *result = (status == AE_OK) ? out[0] : HCI_FAILURE; |
246 | return status; | 235 | return status; |
247 | } | 236 | } |
248 | 237 | ||
249 | static acpi_status hci_read1(u32 reg, u32 * out1, u32 * result) | 238 | static acpi_status hci_read1(struct toshiba_acpi_dev *dev, u32 reg, |
239 | u32 *out1, u32 *result) | ||
250 | { | 240 | { |
251 | u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; | 241 | u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; |
252 | u32 out[HCI_WORDS]; | 242 | u32 out[HCI_WORDS]; |
253 | acpi_status status = hci_raw(in, out); | 243 | acpi_status status = hci_raw(dev, in, out); |
254 | *out1 = out[2]; | 244 | *out1 = out[2]; |
255 | *result = (status == AE_OK) ? out[0] : HCI_FAILURE; | 245 | *result = (status == AE_OK) ? out[0] : HCI_FAILURE; |
256 | return status; | 246 | return status; |
257 | } | 247 | } |
258 | 248 | ||
259 | static acpi_status hci_write2(u32 reg, u32 in1, u32 in2, u32 *result) | 249 | static acpi_status hci_write2(struct toshiba_acpi_dev *dev, u32 reg, |
250 | u32 in1, u32 in2, u32 *result) | ||
260 | { | 251 | { |
261 | u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 }; | 252 | u32 in[HCI_WORDS] = { HCI_SET, reg, in1, in2, 0, 0 }; |
262 | u32 out[HCI_WORDS]; | 253 | u32 out[HCI_WORDS]; |
263 | acpi_status status = hci_raw(in, out); | 254 | acpi_status status = hci_raw(dev, in, out); |
264 | *result = (status == AE_OK) ? out[0] : HCI_FAILURE; | 255 | *result = (status == AE_OK) ? out[0] : HCI_FAILURE; |
265 | return status; | 256 | return status; |
266 | } | 257 | } |
267 | 258 | ||
268 | static acpi_status hci_read2(u32 reg, u32 *out1, u32 *out2, u32 *result) | 259 | static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, |
260 | u32 *out1, u32 *out2, u32 *result) | ||
269 | { | 261 | { |
270 | u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 }; | 262 | u32 in[HCI_WORDS] = { HCI_GET, reg, *out1, *out2, 0, 0 }; |
271 | u32 out[HCI_WORDS]; | 263 | u32 out[HCI_WORDS]; |
272 | acpi_status status = hci_raw(in, out); | 264 | acpi_status status = hci_raw(dev, in, out); |
273 | *out1 = out[2]; | 265 | *out1 = out[2]; |
274 | *out2 = out[3]; | 266 | *out2 = out[3]; |
275 | *result = (status == AE_OK) ? out[0] : HCI_FAILURE; | 267 | *result = (status == AE_OK) ? out[0] : HCI_FAILURE; |
276 | return status; | 268 | return status; |
277 | } | 269 | } |
278 | 270 | ||
279 | struct toshiba_acpi_dev { | ||
280 | struct platform_device *p_dev; | ||
281 | struct rfkill *bt_rfk; | ||
282 | struct input_dev *hotkey_dev; | ||
283 | int illumination_installed; | ||
284 | acpi_handle handle; | ||
285 | |||
286 | const char *bt_name; | ||
287 | |||
288 | struct mutex mutex; | ||
289 | }; | ||
290 | |||
291 | /* Illumination support */ | 271 | /* Illumination support */ |
292 | static int toshiba_illumination_available(void) | 272 | static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) |
293 | { | 273 | { |
294 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | 274 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; |
295 | u32 out[HCI_WORDS]; | 275 | u32 out[HCI_WORDS]; |
296 | acpi_status status; | 276 | acpi_status status; |
297 | 277 | ||
298 | in[0] = 0xf100; | 278 | in[0] = 0xf100; |
299 | status = hci_raw(in, out); | 279 | status = hci_raw(dev, in, out); |
300 | if (ACPI_FAILURE(status)) { | 280 | if (ACPI_FAILURE(status)) { |
301 | pr_info("Illumination device not available\n"); | 281 | pr_info("Illumination device not available\n"); |
302 | return 0; | 282 | return 0; |
303 | } | 283 | } |
304 | in[0] = 0xf400; | 284 | in[0] = 0xf400; |
305 | status = hci_raw(in, out); | 285 | status = hci_raw(dev, in, out); |
306 | return 1; | 286 | return 1; |
307 | } | 287 | } |
308 | 288 | ||
309 | static void toshiba_illumination_set(struct led_classdev *cdev, | 289 | static void toshiba_illumination_set(struct led_classdev *cdev, |
310 | enum led_brightness brightness) | 290 | enum led_brightness brightness) |
311 | { | 291 | { |
292 | struct toshiba_acpi_dev *dev = container_of(cdev, | ||
293 | struct toshiba_acpi_dev, led_dev); | ||
312 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | 294 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; |
313 | u32 out[HCI_WORDS]; | 295 | u32 out[HCI_WORDS]; |
314 | acpi_status status; | 296 | acpi_status status; |
315 | 297 | ||
316 | /* First request : initialize communication. */ | 298 | /* First request : initialize communication. */ |
317 | in[0] = 0xf100; | 299 | in[0] = 0xf100; |
318 | status = hci_raw(in, out); | 300 | status = hci_raw(dev, in, out); |
319 | if (ACPI_FAILURE(status)) { | 301 | if (ACPI_FAILURE(status)) { |
320 | pr_info("Illumination device not available\n"); | 302 | pr_info("Illumination device not available\n"); |
321 | return; | 303 | return; |
@@ -326,7 +308,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev, | |||
326 | in[0] = 0xf400; | 308 | in[0] = 0xf400; |
327 | in[1] = 0x14e; | 309 | in[1] = 0x14e; |
328 | in[2] = 1; | 310 | in[2] = 1; |
329 | status = hci_raw(in, out); | 311 | status = hci_raw(dev, in, out); |
330 | if (ACPI_FAILURE(status)) { | 312 | if (ACPI_FAILURE(status)) { |
331 | pr_info("ACPI call for illumination failed\n"); | 313 | pr_info("ACPI call for illumination failed\n"); |
332 | return; | 314 | return; |
@@ -336,7 +318,7 @@ static void toshiba_illumination_set(struct led_classdev *cdev, | |||
336 | in[0] = 0xf400; | 318 | in[0] = 0xf400; |
337 | in[1] = 0x14e; | 319 | in[1] = 0x14e; |
338 | in[2] = 0; | 320 | in[2] = 0; |
339 | status = hci_raw(in, out); | 321 | status = hci_raw(dev, in, out); |
340 | if (ACPI_FAILURE(status)) { | 322 | if (ACPI_FAILURE(status)) { |
341 | pr_info("ACPI call for illumination failed.\n"); | 323 | pr_info("ACPI call for illumination failed.\n"); |
342 | return; | 324 | return; |
@@ -347,11 +329,13 @@ static void toshiba_illumination_set(struct led_classdev *cdev, | |||
347 | in[0] = 0xf200; | 329 | in[0] = 0xf200; |
348 | in[1] = 0; | 330 | in[1] = 0; |
349 | in[2] = 0; | 331 | in[2] = 0; |
350 | hci_raw(in, out); | 332 | hci_raw(dev, in, out); |
351 | } | 333 | } |
352 | 334 | ||
353 | static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | 335 | static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) |
354 | { | 336 | { |
337 | struct toshiba_acpi_dev *dev = container_of(cdev, | ||
338 | struct toshiba_acpi_dev, led_dev); | ||
355 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; | 339 | u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; |
356 | u32 out[HCI_WORDS]; | 340 | u32 out[HCI_WORDS]; |
357 | acpi_status status; | 341 | acpi_status status; |
@@ -359,7 +343,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | |||
359 | 343 | ||
360 | /*Â First request : initialize communication. */ | 344 | /*Â First request : initialize communication. */ |
361 | in[0] = 0xf100; | 345 | in[0] = 0xf100; |
362 | status = hci_raw(in, out); | 346 | status = hci_raw(dev, in, out); |
363 | if (ACPI_FAILURE(status)) { | 347 | if (ACPI_FAILURE(status)) { |
364 | pr_info("Illumination device not available\n"); | 348 | pr_info("Illumination device not available\n"); |
365 | return LED_OFF; | 349 | return LED_OFF; |
@@ -368,7 +352,7 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | |||
368 | /* Check the illumination */ | 352 | /* Check the illumination */ |
369 | in[0] = 0xf300; | 353 | in[0] = 0xf300; |
370 | in[1] = 0x14e; | 354 | in[1] = 0x14e; |
371 | status = hci_raw(in, out); | 355 | status = hci_raw(dev, in, out); |
372 | if (ACPI_FAILURE(status)) { | 356 | if (ACPI_FAILURE(status)) { |
373 | pr_info("ACPI call for illumination failed.\n"); | 357 | pr_info("ACPI call for illumination failed.\n"); |
374 | return LED_OFF; | 358 | return LED_OFF; |
@@ -380,46 +364,35 @@ static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) | |||
380 | in[0] = 0xf200; | 364 | in[0] = 0xf200; |
381 | in[1] = 0; | 365 | in[1] = 0; |
382 | in[2] = 0; | 366 | in[2] = 0; |
383 | hci_raw(in, out); | 367 | hci_raw(dev, in, out); |
384 | 368 | ||
385 | return result; | 369 | return result; |
386 | } | 370 | } |
387 | 371 | ||
388 | static struct led_classdev toshiba_led = { | ||
389 | .name = "toshiba::illumination", | ||
390 | .max_brightness = 1, | ||
391 | .brightness_set = toshiba_illumination_set, | ||
392 | .brightness_get = toshiba_illumination_get, | ||
393 | }; | ||
394 | |||
395 | static struct toshiba_acpi_dev toshiba_acpi = { | ||
396 | .bt_name = "Toshiba Bluetooth", | ||
397 | }; | ||
398 | |||
399 | /* Bluetooth rfkill handlers */ | 372 | /* Bluetooth rfkill handlers */ |
400 | 373 | ||
401 | static u32 hci_get_bt_present(bool *present) | 374 | static u32 hci_get_bt_present(struct toshiba_acpi_dev *dev, bool *present) |
402 | { | 375 | { |
403 | u32 hci_result; | 376 | u32 hci_result; |
404 | u32 value, value2; | 377 | u32 value, value2; |
405 | 378 | ||
406 | value = 0; | 379 | value = 0; |
407 | value2 = 0; | 380 | value2 = 0; |
408 | hci_read2(HCI_WIRELESS, &value, &value2, &hci_result); | 381 | hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); |
409 | if (hci_result == HCI_SUCCESS) | 382 | if (hci_result == HCI_SUCCESS) |
410 | *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false; | 383 | *present = (value & HCI_WIRELESS_BT_PRESENT) ? true : false; |
411 | 384 | ||
412 | return hci_result; | 385 | return hci_result; |
413 | } | 386 | } |
414 | 387 | ||
415 | static u32 hci_get_radio_state(bool *radio_state) | 388 | static u32 hci_get_radio_state(struct toshiba_acpi_dev *dev, bool *radio_state) |
416 | { | 389 | { |
417 | u32 hci_result; | 390 | u32 hci_result; |
418 | u32 value, value2; | 391 | u32 value, value2; |
419 | 392 | ||
420 | value = 0; | 393 | value = 0; |
421 | value2 = 0x0001; | 394 | value2 = 0x0001; |
422 | hci_read2(HCI_WIRELESS, &value, &value2, &hci_result); | 395 | hci_read2(dev, HCI_WIRELESS, &value, &value2, &hci_result); |
423 | 396 | ||
424 | *radio_state = value & HCI_WIRELESS_KILL_SWITCH; | 397 | *radio_state = value & HCI_WIRELESS_KILL_SWITCH; |
425 | return hci_result; | 398 | return hci_result; |
@@ -436,8 +409,8 @@ static int bt_rfkill_set_block(void *data, bool blocked) | |||
436 | value = (blocked == false); | 409 | value = (blocked == false); |
437 | 410 | ||
438 | mutex_lock(&dev->mutex); | 411 | mutex_lock(&dev->mutex); |
439 | if (hci_get_radio_state(&radio_state) != HCI_SUCCESS) { | 412 | if (hci_get_radio_state(dev, &radio_state) != HCI_SUCCESS) { |
440 | err = -EBUSY; | 413 | err = -EIO; |
441 | goto out; | 414 | goto out; |
442 | } | 415 | } |
443 | 416 | ||
@@ -446,11 +419,11 @@ static int bt_rfkill_set_block(void *data, bool blocked) | |||
446 | goto out; | 419 | goto out; |
447 | } | 420 | } |
448 | 421 | ||
449 | hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); | 422 | hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_POWER, &result1); |
450 | hci_write2(HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); | 423 | hci_write2(dev, HCI_WIRELESS, value, HCI_WIRELESS_BT_ATTACH, &result2); |
451 | 424 | ||
452 | if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) | 425 | if (result1 != HCI_SUCCESS || result2 != HCI_SUCCESS) |
453 | err = -EBUSY; | 426 | err = -EIO; |
454 | else | 427 | else |
455 | err = 0; | 428 | err = 0; |
456 | out: | 429 | out: |
@@ -467,7 +440,7 @@ static void bt_rfkill_poll(struct rfkill *rfkill, void *data) | |||
467 | 440 | ||
468 | mutex_lock(&dev->mutex); | 441 | mutex_lock(&dev->mutex); |
469 | 442 | ||
470 | hci_result = hci_get_radio_state(&value); | 443 | hci_result = hci_get_radio_state(dev, &value); |
471 | if (hci_result != HCI_SUCCESS) { | 444 | if (hci_result != HCI_SUCCESS) { |
472 | /* Can't do anything useful */ | 445 | /* Can't do anything useful */ |
473 | mutex_unlock(&dev->mutex); | 446 | mutex_unlock(&dev->mutex); |
@@ -488,63 +461,64 @@ static const struct rfkill_ops toshiba_rfk_ops = { | |||
488 | }; | 461 | }; |
489 | 462 | ||
490 | static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; | 463 | static struct proc_dir_entry *toshiba_proc_dir /*= 0*/ ; |
491 | static struct backlight_device *toshiba_backlight_device; | ||
492 | static int force_fan; | ||
493 | static int last_key_event; | ||
494 | static int key_event_valid; | ||
495 | 464 | ||
496 | static int get_lcd(struct backlight_device *bd) | 465 | static int get_lcd(struct backlight_device *bd) |
497 | { | 466 | { |
467 | struct toshiba_acpi_dev *dev = bl_get_data(bd); | ||
498 | u32 hci_result; | 468 | u32 hci_result; |
499 | u32 value; | 469 | u32 value; |
500 | 470 | ||
501 | hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result); | 471 | hci_read1(dev, HCI_LCD_BRIGHTNESS, &value, &hci_result); |
502 | if (hci_result == HCI_SUCCESS) { | 472 | if (hci_result == HCI_SUCCESS) |
503 | return (value >> HCI_LCD_BRIGHTNESS_SHIFT); | 473 | return (value >> HCI_LCD_BRIGHTNESS_SHIFT); |
504 | } else | 474 | |
505 | return -EFAULT; | 475 | return -EIO; |
506 | } | 476 | } |
507 | 477 | ||
508 | static int lcd_proc_show(struct seq_file *m, void *v) | 478 | static int lcd_proc_show(struct seq_file *m, void *v) |
509 | { | 479 | { |
510 | int value = get_lcd(NULL); | 480 | struct toshiba_acpi_dev *dev = m->private; |
481 | int value; | ||
482 | |||
483 | if (!dev->backlight_dev) | ||
484 | return -ENODEV; | ||
511 | 485 | ||
486 | value = get_lcd(dev->backlight_dev); | ||
512 | if (value >= 0) { | 487 | if (value >= 0) { |
513 | seq_printf(m, "brightness: %d\n", value); | 488 | seq_printf(m, "brightness: %d\n", value); |
514 | seq_printf(m, "brightness_levels: %d\n", | 489 | seq_printf(m, "brightness_levels: %d\n", |
515 | HCI_LCD_BRIGHTNESS_LEVELS); | 490 | HCI_LCD_BRIGHTNESS_LEVELS); |
516 | } else { | 491 | return 0; |
517 | pr_err("Error reading LCD brightness\n"); | ||
518 | } | 492 | } |
519 | 493 | ||
520 | return 0; | 494 | pr_err("Error reading LCD brightness\n"); |
495 | return -EIO; | ||
521 | } | 496 | } |
522 | 497 | ||
523 | static int lcd_proc_open(struct inode *inode, struct file *file) | 498 | static int lcd_proc_open(struct inode *inode, struct file *file) |
524 | { | 499 | { |
525 | return single_open(file, lcd_proc_show, NULL); | 500 | return single_open(file, lcd_proc_show, PDE(inode)->data); |
526 | } | 501 | } |
527 | 502 | ||
528 | static int set_lcd(int value) | 503 | static int set_lcd(struct toshiba_acpi_dev *dev, int value) |
529 | { | 504 | { |
530 | u32 hci_result; | 505 | u32 hci_result; |
531 | 506 | ||
532 | value = value << HCI_LCD_BRIGHTNESS_SHIFT; | 507 | value = value << HCI_LCD_BRIGHTNESS_SHIFT; |
533 | hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); | 508 | hci_write1(dev, HCI_LCD_BRIGHTNESS, value, &hci_result); |
534 | if (hci_result != HCI_SUCCESS) | 509 | return hci_result == HCI_SUCCESS ? 0 : -EIO; |
535 | return -EFAULT; | ||
536 | |||
537 | return 0; | ||
538 | } | 510 | } |
539 | 511 | ||
540 | static int set_lcd_status(struct backlight_device *bd) | 512 | static int set_lcd_status(struct backlight_device *bd) |
541 | { | 513 | { |
542 | return set_lcd(bd->props.brightness); | 514 | struct toshiba_acpi_dev *dev = bl_get_data(bd); |
515 | return set_lcd(dev, bd->props.brightness); | ||
543 | } | 516 | } |
544 | 517 | ||
545 | static ssize_t lcd_proc_write(struct file *file, const char __user *buf, | 518 | static ssize_t lcd_proc_write(struct file *file, const char __user *buf, |
546 | size_t count, loff_t *pos) | 519 | size_t count, loff_t *pos) |
547 | { | 520 | { |
521 | struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; | ||
548 | char cmd[42]; | 522 | char cmd[42]; |
549 | size_t len; | 523 | size_t len; |
550 | int value; | 524 | int value; |
@@ -557,7 +531,7 @@ static ssize_t lcd_proc_write(struct file *file, const char __user *buf, | |||
557 | 531 | ||
558 | if (sscanf(cmd, " brightness : %i", &value) == 1 && | 532 | if (sscanf(cmd, " brightness : %i", &value) == 1 && |
559 | value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { | 533 | value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { |
560 | ret = set_lcd(value); | 534 | ret = set_lcd(dev, value); |
561 | if (ret == 0) | 535 | if (ret == 0) |
562 | ret = count; | 536 | ret = count; |
563 | } else { | 537 | } else { |
@@ -575,41 +549,49 @@ static const struct file_operations lcd_proc_fops = { | |||
575 | .write = lcd_proc_write, | 549 | .write = lcd_proc_write, |
576 | }; | 550 | }; |
577 | 551 | ||
578 | static int video_proc_show(struct seq_file *m, void *v) | 552 | static int get_video_status(struct toshiba_acpi_dev *dev, u32 *status) |
579 | { | 553 | { |
580 | u32 hci_result; | 554 | u32 hci_result; |
555 | |||
556 | hci_read1(dev, HCI_VIDEO_OUT, status, &hci_result); | ||
557 | return hci_result == HCI_SUCCESS ? 0 : -EIO; | ||
558 | } | ||
559 | |||
560 | static int video_proc_show(struct seq_file *m, void *v) | ||
561 | { | ||
562 | struct toshiba_acpi_dev *dev = m->private; | ||
581 | u32 value; | 563 | u32 value; |
564 | int ret; | ||
582 | 565 | ||
583 | hci_read1(HCI_VIDEO_OUT, &value, &hci_result); | 566 | ret = get_video_status(dev, &value); |
584 | if (hci_result == HCI_SUCCESS) { | 567 | if (!ret) { |
585 | int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; | 568 | int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; |
586 | int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; | 569 | int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; |
587 | int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; | 570 | int is_tv = (value & HCI_VIDEO_OUT_TV) ? 1 : 0; |
588 | seq_printf(m, "lcd_out: %d\n", is_lcd); | 571 | seq_printf(m, "lcd_out: %d\n", is_lcd); |
589 | seq_printf(m, "crt_out: %d\n", is_crt); | 572 | seq_printf(m, "crt_out: %d\n", is_crt); |
590 | seq_printf(m, "tv_out: %d\n", is_tv); | 573 | seq_printf(m, "tv_out: %d\n", is_tv); |
591 | } else { | ||
592 | pr_err("Error reading video out status\n"); | ||
593 | } | 574 | } |
594 | 575 | ||
595 | return 0; | 576 | return ret; |
596 | } | 577 | } |
597 | 578 | ||
598 | static int video_proc_open(struct inode *inode, struct file *file) | 579 | static int video_proc_open(struct inode *inode, struct file *file) |
599 | { | 580 | { |
600 | return single_open(file, video_proc_show, NULL); | 581 | return single_open(file, video_proc_show, PDE(inode)->data); |
601 | } | 582 | } |
602 | 583 | ||
603 | static ssize_t video_proc_write(struct file *file, const char __user *buf, | 584 | static ssize_t video_proc_write(struct file *file, const char __user *buf, |
604 | size_t count, loff_t *pos) | 585 | size_t count, loff_t *pos) |
605 | { | 586 | { |
587 | struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; | ||
606 | char *cmd, *buffer; | 588 | char *cmd, *buffer; |
589 | int ret; | ||
607 | int value; | 590 | int value; |
608 | int remain = count; | 591 | int remain = count; |
609 | int lcd_out = -1; | 592 | int lcd_out = -1; |
610 | int crt_out = -1; | 593 | int crt_out = -1; |
611 | int tv_out = -1; | 594 | int tv_out = -1; |
612 | u32 hci_result; | ||
613 | u32 video_out; | 595 | u32 video_out; |
614 | 596 | ||
615 | cmd = kmalloc(count + 1, GFP_KERNEL); | 597 | cmd = kmalloc(count + 1, GFP_KERNEL); |
@@ -644,8 +626,8 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, | |||
644 | 626 | ||
645 | kfree(cmd); | 627 | kfree(cmd); |
646 | 628 | ||
647 | hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result); | 629 | ret = get_video_status(dev, &video_out); |
648 | if (hci_result == HCI_SUCCESS) { | 630 | if (!ret) { |
649 | unsigned int new_video_out = video_out; | 631 | unsigned int new_video_out = video_out; |
650 | if (lcd_out != -1) | 632 | if (lcd_out != -1) |
651 | _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); | 633 | _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); |
@@ -656,12 +638,10 @@ static ssize_t video_proc_write(struct file *file, const char __user *buf, | |||
656 | /* To avoid unnecessary video disruption, only write the new | 638 | /* To avoid unnecessary video disruption, only write the new |
657 | * video setting if something changed. */ | 639 | * video setting if something changed. */ |
658 | if (new_video_out != video_out) | 640 | if (new_video_out != video_out) |
659 | write_acpi_int(METHOD_VIDEO_OUT, new_video_out); | 641 | ret = write_acpi_int(METHOD_VIDEO_OUT, new_video_out); |
660 | } else { | ||
661 | return -EFAULT; | ||
662 | } | 642 | } |
663 | 643 | ||
664 | return count; | 644 | return ret ? ret : count; |
665 | } | 645 | } |
666 | 646 | ||
667 | static const struct file_operations video_proc_fops = { | 647 | static const struct file_operations video_proc_fops = { |
@@ -673,30 +653,38 @@ static const struct file_operations video_proc_fops = { | |||
673 | .write = video_proc_write, | 653 | .write = video_proc_write, |
674 | }; | 654 | }; |
675 | 655 | ||
676 | static int fan_proc_show(struct seq_file *m, void *v) | 656 | static int get_fan_status(struct toshiba_acpi_dev *dev, u32 *status) |
677 | { | 657 | { |
678 | u32 hci_result; | 658 | u32 hci_result; |
659 | |||
660 | hci_read1(dev, HCI_FAN, status, &hci_result); | ||
661 | return hci_result == HCI_SUCCESS ? 0 : -EIO; | ||
662 | } | ||
663 | |||
664 | static int fan_proc_show(struct seq_file *m, void *v) | ||
665 | { | ||
666 | struct toshiba_acpi_dev *dev = m->private; | ||
667 | int ret; | ||
679 | u32 value; | 668 | u32 value; |
680 | 669 | ||
681 | hci_read1(HCI_FAN, &value, &hci_result); | 670 | ret = get_fan_status(dev, &value); |
682 | if (hci_result == HCI_SUCCESS) { | 671 | if (!ret) { |
683 | seq_printf(m, "running: %d\n", (value > 0)); | 672 | seq_printf(m, "running: %d\n", (value > 0)); |
684 | seq_printf(m, "force_on: %d\n", force_fan); | 673 | seq_printf(m, "force_on: %d\n", dev->force_fan); |
685 | } else { | ||
686 | pr_err("Error reading fan status\n"); | ||
687 | } | 674 | } |
688 | 675 | ||
689 | return 0; | 676 | return ret; |
690 | } | 677 | } |
691 | 678 | ||
692 | static int fan_proc_open(struct inode *inode, struct file *file) | 679 | static int fan_proc_open(struct inode *inode, struct file *file) |
693 | { | 680 | { |
694 | return single_open(file, fan_proc_show, NULL); | 681 | return single_open(file, fan_proc_show, PDE(inode)->data); |
695 | } | 682 | } |
696 | 683 | ||
697 | static ssize_t fan_proc_write(struct file *file, const char __user *buf, | 684 | static ssize_t fan_proc_write(struct file *file, const char __user *buf, |
698 | size_t count, loff_t *pos) | 685 | size_t count, loff_t *pos) |
699 | { | 686 | { |
687 | struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; | ||
700 | char cmd[42]; | 688 | char cmd[42]; |
701 | size_t len; | 689 | size_t len; |
702 | int value; | 690 | int value; |
@@ -709,11 +697,11 @@ static ssize_t fan_proc_write(struct file *file, const char __user *buf, | |||
709 | 697 | ||
710 | if (sscanf(cmd, " force_on : %i", &value) == 1 && | 698 | if (sscanf(cmd, " force_on : %i", &value) == 1 && |
711 | value >= 0 && value <= 1) { | 699 | value >= 0 && value <= 1) { |
712 | hci_write1(HCI_FAN, value, &hci_result); | 700 | hci_write1(dev, HCI_FAN, value, &hci_result); |
713 | if (hci_result != HCI_SUCCESS) | 701 | if (hci_result != HCI_SUCCESS) |
714 | return -EFAULT; | 702 | return -EIO; |
715 | else | 703 | else |
716 | force_fan = value; | 704 | dev->force_fan = value; |
717 | } else { | 705 | } else { |
718 | return -EINVAL; | 706 | return -EINVAL; |
719 | } | 707 | } |
@@ -732,42 +720,43 @@ static const struct file_operations fan_proc_fops = { | |||
732 | 720 | ||
733 | static int keys_proc_show(struct seq_file *m, void *v) | 721 | static int keys_proc_show(struct seq_file *m, void *v) |
734 | { | 722 | { |
723 | struct toshiba_acpi_dev *dev = m->private; | ||
735 | u32 hci_result; | 724 | u32 hci_result; |
736 | u32 value; | 725 | u32 value; |
737 | 726 | ||
738 | if (!key_event_valid) { | 727 | if (!dev->key_event_valid) { |
739 | hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); | 728 | hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); |
740 | if (hci_result == HCI_SUCCESS) { | 729 | if (hci_result == HCI_SUCCESS) { |
741 | key_event_valid = 1; | 730 | dev->key_event_valid = 1; |
742 | last_key_event = value; | 731 | dev->last_key_event = value; |
743 | } else if (hci_result == HCI_EMPTY) { | 732 | } else if (hci_result == HCI_EMPTY) { |
744 | /* better luck next time */ | 733 | /* better luck next time */ |
745 | } else if (hci_result == HCI_NOT_SUPPORTED) { | 734 | } else if (hci_result == HCI_NOT_SUPPORTED) { |
746 | /* This is a workaround for an unresolved issue on | 735 | /* This is a workaround for an unresolved issue on |
747 | * some machines where system events sporadically | 736 | * some machines where system events sporadically |
748 | * become disabled. */ | 737 | * become disabled. */ |
749 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); | 738 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); |
750 | pr_notice("Re-enabled hotkeys\n"); | 739 | pr_notice("Re-enabled hotkeys\n"); |
751 | } else { | 740 | } else { |
752 | pr_err("Error reading hotkey status\n"); | 741 | pr_err("Error reading hotkey status\n"); |
753 | goto end; | 742 | return -EIO; |
754 | } | 743 | } |
755 | } | 744 | } |
756 | 745 | ||
757 | seq_printf(m, "hotkey_ready: %d\n", key_event_valid); | 746 | seq_printf(m, "hotkey_ready: %d\n", dev->key_event_valid); |
758 | seq_printf(m, "hotkey: 0x%04x\n", last_key_event); | 747 | seq_printf(m, "hotkey: 0x%04x\n", dev->last_key_event); |
759 | end: | ||
760 | return 0; | 748 | return 0; |
761 | } | 749 | } |
762 | 750 | ||
763 | static int keys_proc_open(struct inode *inode, struct file *file) | 751 | static int keys_proc_open(struct inode *inode, struct file *file) |
764 | { | 752 | { |
765 | return single_open(file, keys_proc_show, NULL); | 753 | return single_open(file, keys_proc_show, PDE(inode)->data); |
766 | } | 754 | } |
767 | 755 | ||
768 | static ssize_t keys_proc_write(struct file *file, const char __user *buf, | 756 | static ssize_t keys_proc_write(struct file *file, const char __user *buf, |
769 | size_t count, loff_t *pos) | 757 | size_t count, loff_t *pos) |
770 | { | 758 | { |
759 | struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; | ||
771 | char cmd[42]; | 760 | char cmd[42]; |
772 | size_t len; | 761 | size_t len; |
773 | int value; | 762 | int value; |
@@ -778,7 +767,7 @@ static ssize_t keys_proc_write(struct file *file, const char __user *buf, | |||
778 | cmd[len] = '\0'; | 767 | cmd[len] = '\0'; |
779 | 768 | ||
780 | if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) { | 769 | if (sscanf(cmd, " hotkey_ready : %i", &value) == 1 && value == 0) { |
781 | key_event_valid = 0; | 770 | dev->key_event_valid = 0; |
782 | } else { | 771 | } else { |
783 | return -EINVAL; | 772 | return -EINVAL; |
784 | } | 773 | } |
@@ -820,21 +809,35 @@ static const struct file_operations version_proc_fops = { | |||
820 | 809 | ||
821 | #define PROC_TOSHIBA "toshiba" | 810 | #define PROC_TOSHIBA "toshiba" |
822 | 811 | ||
823 | static void __init create_toshiba_proc_entries(void) | 812 | static void __devinit |
813 | create_toshiba_proc_entries(struct toshiba_acpi_dev *dev) | ||
824 | { | 814 | { |
825 | proc_create("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, &lcd_proc_fops); | 815 | if (dev->backlight_dev) |
826 | proc_create("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, &video_proc_fops); | 816 | proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, |
827 | proc_create("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, &fan_proc_fops); | 817 | &lcd_proc_fops, dev); |
828 | proc_create("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, &keys_proc_fops); | 818 | if (dev->video_supported) |
829 | proc_create("version", S_IRUGO, toshiba_proc_dir, &version_proc_fops); | 819 | proc_create_data("video", S_IRUGO | S_IWUSR, toshiba_proc_dir, |
820 | &video_proc_fops, dev); | ||
821 | if (dev->fan_supported) | ||
822 | proc_create_data("fan", S_IRUGO | S_IWUSR, toshiba_proc_dir, | ||
823 | &fan_proc_fops, dev); | ||
824 | if (dev->hotkey_dev) | ||
825 | proc_create_data("keys", S_IRUGO | S_IWUSR, toshiba_proc_dir, | ||
826 | &keys_proc_fops, dev); | ||
827 | proc_create_data("version", S_IRUGO, toshiba_proc_dir, | ||
828 | &version_proc_fops, dev); | ||
830 | } | 829 | } |
831 | 830 | ||
832 | static void remove_toshiba_proc_entries(void) | 831 | static void remove_toshiba_proc_entries(struct toshiba_acpi_dev *dev) |
833 | { | 832 | { |
834 | remove_proc_entry("lcd", toshiba_proc_dir); | 833 | if (dev->backlight_dev) |
835 | remove_proc_entry("video", toshiba_proc_dir); | 834 | remove_proc_entry("lcd", toshiba_proc_dir); |
836 | remove_proc_entry("fan", toshiba_proc_dir); | 835 | if (dev->video_supported) |
837 | remove_proc_entry("keys", toshiba_proc_dir); | 836 | remove_proc_entry("video", toshiba_proc_dir); |
837 | if (dev->fan_supported) | ||
838 | remove_proc_entry("fan", toshiba_proc_dir); | ||
839 | if (dev->hotkey_dev) | ||
840 | remove_proc_entry("keys", toshiba_proc_dir); | ||
838 | remove_proc_entry("version", toshiba_proc_dir); | 841 | remove_proc_entry("version", toshiba_proc_dir); |
839 | } | 842 | } |
840 | 843 | ||
@@ -843,224 +846,256 @@ static const struct backlight_ops toshiba_backlight_data = { | |||
843 | .update_status = set_lcd_status, | 846 | .update_status = set_lcd_status, |
844 | }; | 847 | }; |
845 | 848 | ||
846 | static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *context) | 849 | static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) |
847 | { | ||
848 | u32 hci_result, value; | ||
849 | |||
850 | if (event != 0x80) | ||
851 | return; | ||
852 | do { | ||
853 | hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); | ||
854 | if (hci_result == HCI_SUCCESS) { | ||
855 | if (value == 0x100) | ||
856 | continue; | ||
857 | /* act on key press; ignore key release */ | ||
858 | if (value & 0x80) | ||
859 | continue; | ||
860 | |||
861 | if (!sparse_keymap_report_event(toshiba_acpi.hotkey_dev, | ||
862 | value, 1, true)) { | ||
863 | pr_info("Unknown key %x\n", | ||
864 | value); | ||
865 | } | ||
866 | } else if (hci_result == HCI_NOT_SUPPORTED) { | ||
867 | /* This is a workaround for an unresolved issue on | ||
868 | * some machines where system events sporadically | ||
869 | * become disabled. */ | ||
870 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); | ||
871 | pr_notice("Re-enabled hotkeys\n"); | ||
872 | } | ||
873 | } while (hci_result != HCI_EMPTY); | ||
874 | } | ||
875 | |||
876 | static int __init toshiba_acpi_setup_keyboard(char *device) | ||
877 | { | 850 | { |
878 | acpi_status status; | 851 | acpi_status status; |
879 | int error; | 852 | int error; |
880 | 853 | ||
881 | status = acpi_get_handle(NULL, device, &toshiba_acpi.handle); | 854 | dev->hotkey_dev = input_allocate_device(); |
882 | if (ACPI_FAILURE(status)) { | 855 | if (!dev->hotkey_dev) { |
883 | pr_info("Unable to get notification device\n"); | ||
884 | return -ENODEV; | ||
885 | } | ||
886 | |||
887 | toshiba_acpi.hotkey_dev = input_allocate_device(); | ||
888 | if (!toshiba_acpi.hotkey_dev) { | ||
889 | pr_info("Unable to register input device\n"); | 856 | pr_info("Unable to register input device\n"); |
890 | return -ENOMEM; | 857 | return -ENOMEM; |
891 | } | 858 | } |
892 | 859 | ||
893 | toshiba_acpi.hotkey_dev->name = "Toshiba input device"; | 860 | dev->hotkey_dev->name = "Toshiba input device"; |
894 | toshiba_acpi.hotkey_dev->phys = device; | 861 | dev->hotkey_dev->phys = "toshiba_acpi/input0"; |
895 | toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST; | 862 | dev->hotkey_dev->id.bustype = BUS_HOST; |
896 | 863 | ||
897 | error = sparse_keymap_setup(toshiba_acpi.hotkey_dev, | 864 | error = sparse_keymap_setup(dev->hotkey_dev, toshiba_acpi_keymap, NULL); |
898 | toshiba_acpi_keymap, NULL); | ||
899 | if (error) | 865 | if (error) |
900 | goto err_free_dev; | 866 | goto err_free_dev; |
901 | 867 | ||
902 | status = acpi_install_notify_handler(toshiba_acpi.handle, | 868 | status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); |
903 | ACPI_DEVICE_NOTIFY, toshiba_acpi_notify, NULL); | ||
904 | if (ACPI_FAILURE(status)) { | ||
905 | pr_info("Unable to install hotkey notification\n"); | ||
906 | error = -ENODEV; | ||
907 | goto err_free_keymap; | ||
908 | } | ||
909 | |||
910 | status = acpi_evaluate_object(toshiba_acpi.handle, "ENAB", NULL, NULL); | ||
911 | if (ACPI_FAILURE(status)) { | 869 | if (ACPI_FAILURE(status)) { |
912 | pr_info("Unable to enable hotkeys\n"); | 870 | pr_info("Unable to enable hotkeys\n"); |
913 | error = -ENODEV; | 871 | error = -ENODEV; |
914 | goto err_remove_notify; | 872 | goto err_free_keymap; |
915 | } | 873 | } |
916 | 874 | ||
917 | error = input_register_device(toshiba_acpi.hotkey_dev); | 875 | error = input_register_device(dev->hotkey_dev); |
918 | if (error) { | 876 | if (error) { |
919 | pr_info("Unable to register input device\n"); | 877 | pr_info("Unable to register input device\n"); |
920 | goto err_remove_notify; | 878 | goto err_free_keymap; |
921 | } | 879 | } |
922 | 880 | ||
923 | return 0; | 881 | return 0; |
924 | 882 | ||
925 | err_remove_notify: | ||
926 | acpi_remove_notify_handler(toshiba_acpi.handle, | ||
927 | ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); | ||
928 | err_free_keymap: | 883 | err_free_keymap: |
929 | sparse_keymap_free(toshiba_acpi.hotkey_dev); | 884 | sparse_keymap_free(dev->hotkey_dev); |
930 | err_free_dev: | 885 | err_free_dev: |
931 | input_free_device(toshiba_acpi.hotkey_dev); | 886 | input_free_device(dev->hotkey_dev); |
932 | toshiba_acpi.hotkey_dev = NULL; | 887 | dev->hotkey_dev = NULL; |
933 | return error; | 888 | return error; |
934 | } | 889 | } |
935 | 890 | ||
936 | static void toshiba_acpi_exit(void) | 891 | static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) |
937 | { | 892 | { |
938 | if (toshiba_acpi.hotkey_dev) { | 893 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); |
939 | acpi_remove_notify_handler(toshiba_acpi.handle, | 894 | |
940 | ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); | 895 | remove_toshiba_proc_entries(dev); |
941 | sparse_keymap_free(toshiba_acpi.hotkey_dev); | 896 | |
942 | input_unregister_device(toshiba_acpi.hotkey_dev); | 897 | if (dev->hotkey_dev) { |
898 | input_unregister_device(dev->hotkey_dev); | ||
899 | sparse_keymap_free(dev->hotkey_dev); | ||
943 | } | 900 | } |
944 | 901 | ||
945 | if (toshiba_acpi.bt_rfk) { | 902 | if (dev->bt_rfk) { |
946 | rfkill_unregister(toshiba_acpi.bt_rfk); | 903 | rfkill_unregister(dev->bt_rfk); |
947 | rfkill_destroy(toshiba_acpi.bt_rfk); | 904 | rfkill_destroy(dev->bt_rfk); |
948 | } | 905 | } |
949 | 906 | ||
950 | if (toshiba_backlight_device) | 907 | if (dev->backlight_dev) |
951 | backlight_device_unregister(toshiba_backlight_device); | 908 | backlight_device_unregister(dev->backlight_dev); |
952 | 909 | ||
953 | remove_toshiba_proc_entries(); | 910 | if (dev->illumination_supported) |
911 | led_classdev_unregister(&dev->led_dev); | ||
954 | 912 | ||
955 | if (toshiba_proc_dir) | 913 | kfree(dev); |
956 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | 914 | |
915 | return 0; | ||
916 | } | ||
917 | |||
918 | static const char * __devinit find_hci_method(acpi_handle handle) | ||
919 | { | ||
920 | acpi_status status; | ||
921 | acpi_handle hci_handle; | ||
957 | 922 | ||
958 | if (toshiba_acpi.illumination_installed) | 923 | status = acpi_get_handle(handle, "GHCI", &hci_handle); |
959 | led_classdev_unregister(&toshiba_led); | 924 | if (ACPI_SUCCESS(status)) |
925 | return "GHCI"; | ||
960 | 926 | ||
961 | platform_device_unregister(toshiba_acpi.p_dev); | 927 | status = acpi_get_handle(handle, "SPFC", &hci_handle); |
928 | if (ACPI_SUCCESS(status)) | ||
929 | return "SPFC"; | ||
962 | 930 | ||
963 | return; | 931 | return NULL; |
964 | } | 932 | } |
965 | 933 | ||
966 | static int __init toshiba_acpi_init(void) | 934 | static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) |
967 | { | 935 | { |
936 | struct toshiba_acpi_dev *dev; | ||
937 | const char *hci_method; | ||
968 | u32 hci_result; | 938 | u32 hci_result; |
939 | u32 dummy; | ||
969 | bool bt_present; | 940 | bool bt_present; |
970 | int ret = 0; | 941 | int ret = 0; |
971 | struct backlight_properties props; | 942 | struct backlight_properties props; |
972 | 943 | ||
973 | if (acpi_disabled) | ||
974 | return -ENODEV; | ||
975 | |||
976 | /* simple device detection: look for HCI method */ | ||
977 | if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) { | ||
978 | method_hci = TOSH_INTERFACE_1 GHCI_METHOD; | ||
979 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1)) | ||
980 | pr_info("Unable to activate hotkeys\n"); | ||
981 | } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) { | ||
982 | method_hci = TOSH_INTERFACE_2 GHCI_METHOD; | ||
983 | if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2)) | ||
984 | pr_info("Unable to activate hotkeys\n"); | ||
985 | } else | ||
986 | return -ENODEV; | ||
987 | |||
988 | pr_info("Toshiba Laptop ACPI Extras version %s\n", | 944 | pr_info("Toshiba Laptop ACPI Extras version %s\n", |
989 | TOSHIBA_ACPI_VERSION); | 945 | TOSHIBA_ACPI_VERSION); |
990 | pr_info(" HCI method: %s\n", method_hci); | 946 | |
991 | 947 | hci_method = find_hci_method(acpi_dev->handle); | |
992 | mutex_init(&toshiba_acpi.mutex); | 948 | if (!hci_method) { |
993 | 949 | pr_err("HCI interface not found\n"); | |
994 | toshiba_acpi.p_dev = platform_device_register_simple("toshiba_acpi", | 950 | return -ENODEV; |
995 | -1, NULL, 0); | ||
996 | if (IS_ERR(toshiba_acpi.p_dev)) { | ||
997 | ret = PTR_ERR(toshiba_acpi.p_dev); | ||
998 | pr_err("unable to register platform device\n"); | ||
999 | toshiba_acpi.p_dev = NULL; | ||
1000 | toshiba_acpi_exit(); | ||
1001 | return ret; | ||
1002 | } | 951 | } |
1003 | 952 | ||
1004 | force_fan = 0; | 953 | dev = kzalloc(sizeof(*dev), GFP_KERNEL); |
1005 | key_event_valid = 0; | 954 | if (!dev) |
955 | return -ENOMEM; | ||
956 | dev->acpi_dev = acpi_dev; | ||
957 | dev->method_hci = hci_method; | ||
958 | acpi_dev->driver_data = dev; | ||
1006 | 959 | ||
1007 | /* enable event fifo */ | 960 | if (toshiba_acpi_setup_keyboard(dev)) |
1008 | hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); | 961 | pr_info("Unable to activate hotkeys\n"); |
1009 | 962 | ||
1010 | toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); | 963 | mutex_init(&dev->mutex); |
1011 | if (!toshiba_proc_dir) { | 964 | |
1012 | toshiba_acpi_exit(); | 965 | /* enable event fifo */ |
1013 | return -ENODEV; | 966 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); |
1014 | } else { | ||
1015 | create_toshiba_proc_entries(); | ||
1016 | } | ||
1017 | 967 | ||
1018 | props.type = BACKLIGHT_PLATFORM; | 968 | props.type = BACKLIGHT_PLATFORM; |
1019 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; | 969 | props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; |
1020 | toshiba_backlight_device = backlight_device_register("toshiba", | 970 | dev->backlight_dev = backlight_device_register("toshiba", |
1021 | &toshiba_acpi.p_dev->dev, | 971 | &acpi_dev->dev, |
1022 | NULL, | 972 | dev, |
1023 | &toshiba_backlight_data, | 973 | &toshiba_backlight_data, |
1024 | &props); | 974 | &props); |
1025 | if (IS_ERR(toshiba_backlight_device)) { | 975 | if (IS_ERR(dev->backlight_dev)) { |
1026 | ret = PTR_ERR(toshiba_backlight_device); | 976 | ret = PTR_ERR(dev->backlight_dev); |
1027 | 977 | ||
1028 | pr_err("Could not register toshiba backlight device\n"); | 978 | pr_err("Could not register toshiba backlight device\n"); |
1029 | toshiba_backlight_device = NULL; | 979 | dev->backlight_dev = NULL; |
1030 | toshiba_acpi_exit(); | 980 | goto error; |
1031 | return ret; | ||
1032 | } | 981 | } |
982 | dev->backlight_dev->props.brightness = get_lcd(dev->backlight_dev); | ||
1033 | 983 | ||
1034 | /* Register rfkill switch for Bluetooth */ | 984 | /* Register rfkill switch for Bluetooth */ |
1035 | if (hci_get_bt_present(&bt_present) == HCI_SUCCESS && bt_present) { | 985 | if (hci_get_bt_present(dev, &bt_present) == HCI_SUCCESS && bt_present) { |
1036 | toshiba_acpi.bt_rfk = rfkill_alloc(toshiba_acpi.bt_name, | 986 | dev->bt_rfk = rfkill_alloc("Toshiba Bluetooth", |
1037 | &toshiba_acpi.p_dev->dev, | 987 | &acpi_dev->dev, |
1038 | RFKILL_TYPE_BLUETOOTH, | 988 | RFKILL_TYPE_BLUETOOTH, |
1039 | &toshiba_rfk_ops, | 989 | &toshiba_rfk_ops, |
1040 | &toshiba_acpi); | 990 | dev); |
1041 | if (!toshiba_acpi.bt_rfk) { | 991 | if (!dev->bt_rfk) { |
1042 | pr_err("unable to allocate rfkill device\n"); | 992 | pr_err("unable to allocate rfkill device\n"); |
1043 | toshiba_acpi_exit(); | 993 | ret = -ENOMEM; |
1044 | return -ENOMEM; | 994 | goto error; |
1045 | } | 995 | } |
1046 | 996 | ||
1047 | ret = rfkill_register(toshiba_acpi.bt_rfk); | 997 | ret = rfkill_register(dev->bt_rfk); |
1048 | if (ret) { | 998 | if (ret) { |
1049 | pr_err("unable to register rfkill device\n"); | 999 | pr_err("unable to register rfkill device\n"); |
1050 | rfkill_destroy(toshiba_acpi.bt_rfk); | 1000 | rfkill_destroy(dev->bt_rfk); |
1051 | toshiba_acpi_exit(); | 1001 | goto error; |
1052 | return ret; | ||
1053 | } | 1002 | } |
1054 | } | 1003 | } |
1055 | 1004 | ||
1056 | toshiba_acpi.illumination_installed = 0; | 1005 | if (toshiba_illumination_available(dev)) { |
1057 | if (toshiba_illumination_available()) { | 1006 | dev->led_dev.name = "toshiba::illumination"; |
1058 | if (!led_classdev_register(&(toshiba_acpi.p_dev->dev), | 1007 | dev->led_dev.max_brightness = 1; |
1059 | &toshiba_led)) | 1008 | dev->led_dev.brightness_set = toshiba_illumination_set; |
1060 | toshiba_acpi.illumination_installed = 1; | 1009 | dev->led_dev.brightness_get = toshiba_illumination_get; |
1010 | if (!led_classdev_register(&acpi_dev->dev, &dev->led_dev)) | ||
1011 | dev->illumination_supported = 1; | ||
1061 | } | 1012 | } |
1062 | 1013 | ||
1014 | /* Determine whether or not BIOS supports fan and video interfaces */ | ||
1015 | |||
1016 | ret = get_video_status(dev, &dummy); | ||
1017 | dev->video_supported = !ret; | ||
1018 | |||
1019 | ret = get_fan_status(dev, &dummy); | ||
1020 | dev->fan_supported = !ret; | ||
1021 | |||
1022 | create_toshiba_proc_entries(dev); | ||
1023 | |||
1063 | return 0; | 1024 | return 0; |
1025 | |||
1026 | error: | ||
1027 | toshiba_acpi_remove(acpi_dev, 0); | ||
1028 | return ret; | ||
1029 | } | ||
1030 | |||
1031 | static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) | ||
1032 | { | ||
1033 | struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); | ||
1034 | u32 hci_result, value; | ||
1035 | |||
1036 | if (event != 0x80) | ||
1037 | return; | ||
1038 | do { | ||
1039 | hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); | ||
1040 | if (hci_result == HCI_SUCCESS) { | ||
1041 | if (value == 0x100) | ||
1042 | continue; | ||
1043 | /* act on key press; ignore key release */ | ||
1044 | if (value & 0x80) | ||
1045 | continue; | ||
1046 | |||
1047 | if (!sparse_keymap_report_event(dev->hotkey_dev, | ||
1048 | value, 1, true)) { | ||
1049 | pr_info("Unknown key %x\n", | ||
1050 | value); | ||
1051 | } | ||
1052 | } else if (hci_result == HCI_NOT_SUPPORTED) { | ||
1053 | /* This is a workaround for an unresolved issue on | ||
1054 | * some machines where system events sporadically | ||
1055 | * become disabled. */ | ||
1056 | hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); | ||
1057 | pr_notice("Re-enabled hotkeys\n"); | ||
1058 | } | ||
1059 | } while (hci_result != HCI_EMPTY); | ||
1060 | } | ||
1061 | |||
1062 | |||
1063 | static struct acpi_driver toshiba_acpi_driver = { | ||
1064 | .name = "Toshiba ACPI driver", | ||
1065 | .owner = THIS_MODULE, | ||
1066 | .ids = toshiba_device_ids, | ||
1067 | .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, | ||
1068 | .ops = { | ||
1069 | .add = toshiba_acpi_add, | ||
1070 | .remove = toshiba_acpi_remove, | ||
1071 | .notify = toshiba_acpi_notify, | ||
1072 | }, | ||
1073 | }; | ||
1074 | |||
1075 | static int __init toshiba_acpi_init(void) | ||
1076 | { | ||
1077 | int ret; | ||
1078 | |||
1079 | toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); | ||
1080 | if (!toshiba_proc_dir) { | ||
1081 | pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); | ||
1082 | return -ENODEV; | ||
1083 | } | ||
1084 | |||
1085 | ret = acpi_bus_register_driver(&toshiba_acpi_driver); | ||
1086 | if (ret) { | ||
1087 | pr_err("Failed to register ACPI driver: %d\n", ret); | ||
1088 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | ||
1089 | } | ||
1090 | |||
1091 | return ret; | ||
1092 | } | ||
1093 | |||
1094 | static void __exit toshiba_acpi_exit(void) | ||
1095 | { | ||
1096 | acpi_bus_unregister_driver(&toshiba_acpi_driver); | ||
1097 | if (toshiba_proc_dir) | ||
1098 | remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); | ||
1064 | } | 1099 | } |
1065 | 1100 | ||
1066 | module_init(toshiba_acpi_init); | 1101 | module_init(toshiba_acpi_init); |
diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index c10546c7e17e..a134c26870b0 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c | |||
@@ -755,9 +755,13 @@ static void wmi_free_devices(void) | |||
755 | struct wmi_block *wblock, *next; | 755 | struct wmi_block *wblock, *next; |
756 | 756 | ||
757 | /* Delete devices for all the GUIDs */ | 757 | /* Delete devices for all the GUIDs */ |
758 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) | 758 | list_for_each_entry_safe(wblock, next, &wmi_block_list, list) { |
759 | list_del(&wblock->list); | ||
759 | if (wblock->dev.class) | 760 | if (wblock->dev.class) |
760 | device_unregister(&wblock->dev); | 761 | device_unregister(&wblock->dev); |
762 | else | ||
763 | kfree(wblock); | ||
764 | } | ||
761 | } | 765 | } |
762 | 766 | ||
763 | static bool guid_already_parsed(const char *guid_string) | 767 | static bool guid_already_parsed(const char *guid_string) |