diff options
Diffstat (limited to 'drivers/misc/thinkpad_acpi.c')
-rw-r--r-- | drivers/misc/thinkpad_acpi.c | 765 |
1 files changed, 572 insertions, 193 deletions
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 6cb781262f94..3f28f6eabdbf 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c | |||
@@ -21,7 +21,7 @@ | |||
21 | * 02110-1301, USA. | 21 | * 02110-1301, USA. |
22 | */ | 22 | */ |
23 | 23 | ||
24 | #define TPACPI_VERSION "0.19" | 24 | #define TPACPI_VERSION "0.20" |
25 | #define TPACPI_SYSFS_VERSION 0x020200 | 25 | #define TPACPI_SYSFS_VERSION 0x020200 |
26 | 26 | ||
27 | /* | 27 | /* |
@@ -67,6 +67,7 @@ | |||
67 | #include <linux/hwmon.h> | 67 | #include <linux/hwmon.h> |
68 | #include <linux/hwmon-sysfs.h> | 68 | #include <linux/hwmon-sysfs.h> |
69 | #include <linux/input.h> | 69 | #include <linux/input.h> |
70 | #include <linux/leds.h> | ||
70 | #include <asm/uaccess.h> | 71 | #include <asm/uaccess.h> |
71 | 72 | ||
72 | #include <linux/dmi.h> | 73 | #include <linux/dmi.h> |
@@ -85,6 +86,8 @@ | |||
85 | #define TP_CMOS_VOLUME_MUTE 2 | 86 | #define TP_CMOS_VOLUME_MUTE 2 |
86 | #define TP_CMOS_BRIGHTNESS_UP 4 | 87 | #define TP_CMOS_BRIGHTNESS_UP 4 |
87 | #define TP_CMOS_BRIGHTNESS_DOWN 5 | 88 | #define TP_CMOS_BRIGHTNESS_DOWN 5 |
89 | #define TP_CMOS_THINKLIGHT_ON 12 | ||
90 | #define TP_CMOS_THINKLIGHT_OFF 13 | ||
88 | 91 | ||
89 | /* NVRAM Addresses */ | 92 | /* NVRAM Addresses */ |
90 | enum tp_nvram_addr { | 93 | enum tp_nvram_addr { |
@@ -133,8 +136,12 @@ enum { | |||
133 | #define TPACPI_PROC_DIR "ibm" | 136 | #define TPACPI_PROC_DIR "ibm" |
134 | #define TPACPI_ACPI_EVENT_PREFIX "ibm" | 137 | #define TPACPI_ACPI_EVENT_PREFIX "ibm" |
135 | #define TPACPI_DRVR_NAME TPACPI_FILE | 138 | #define TPACPI_DRVR_NAME TPACPI_FILE |
139 | #define TPACPI_DRVR_SHORTNAME "tpacpi" | ||
136 | #define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon" | 140 | #define TPACPI_HWMON_DRVR_NAME TPACPI_NAME "_hwmon" |
137 | 141 | ||
142 | #define TPACPI_NVRAM_KTHREAD_NAME "ktpacpi_nvramd" | ||
143 | #define TPACPI_WORKQUEUE_NAME "ktpacpid" | ||
144 | |||
138 | #define TPACPI_MAX_ACPI_ARGS 3 | 145 | #define TPACPI_MAX_ACPI_ARGS 3 |
139 | 146 | ||
140 | /* Debugging */ | 147 | /* Debugging */ |
@@ -225,6 +232,7 @@ static struct { | |||
225 | u32 light:1; | 232 | u32 light:1; |
226 | u32 light_status:1; | 233 | u32 light_status:1; |
227 | u32 bright_16levels:1; | 234 | u32 bright_16levels:1; |
235 | u32 bright_acpimode:1; | ||
228 | u32 wan:1; | 236 | u32 wan:1; |
229 | u32 fan_ctrl_status_undef:1; | 237 | u32 fan_ctrl_status_undef:1; |
230 | u32 input_device_registered:1; | 238 | u32 input_device_registered:1; |
@@ -236,6 +244,11 @@ static struct { | |||
236 | u32 hotkey_poll_active:1; | 244 | u32 hotkey_poll_active:1; |
237 | } tp_features; | 245 | } tp_features; |
238 | 246 | ||
247 | static struct { | ||
248 | u16 hotkey_mask_ff:1; | ||
249 | u16 bright_cmos_ec_unsync:1; | ||
250 | } tp_warned; | ||
251 | |||
239 | struct thinkpad_id_data { | 252 | struct thinkpad_id_data { |
240 | unsigned int vendor; /* ThinkPad vendor: | 253 | unsigned int vendor; /* ThinkPad vendor: |
241 | * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */ | 254 | * PCI_VENDOR_ID_IBM/PCI_VENDOR_ID_LENOVO */ |
@@ -246,7 +259,8 @@ struct thinkpad_id_data { | |||
246 | u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */ | 259 | u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */ |
247 | u16 ec_model; | 260 | u16 ec_model; |
248 | 261 | ||
249 | char *model_str; | 262 | char *model_str; /* ThinkPad T43 */ |
263 | char *nummodel_str; /* 9384A9C for a 9384-A9C model */ | ||
250 | }; | 264 | }; |
251 | static struct thinkpad_id_data thinkpad_id; | 265 | static struct thinkpad_id_data thinkpad_id; |
252 | 266 | ||
@@ -259,6 +273,16 @@ static enum { | |||
259 | static int experimental; | 273 | static int experimental; |
260 | static u32 dbg_level; | 274 | static u32 dbg_level; |
261 | 275 | ||
276 | static struct workqueue_struct *tpacpi_wq; | ||
277 | |||
278 | /* Special LED class that can defer work */ | ||
279 | struct tpacpi_led_classdev { | ||
280 | struct led_classdev led_classdev; | ||
281 | struct work_struct work; | ||
282 | enum led_brightness new_brightness; | ||
283 | unsigned int led; | ||
284 | }; | ||
285 | |||
262 | /**************************************************************************** | 286 | /**************************************************************************** |
263 | **************************************************************************** | 287 | **************************************************************************** |
264 | * | 288 | * |
@@ -807,6 +831,80 @@ static int parse_strtoul(const char *buf, | |||
807 | return 0; | 831 | return 0; |
808 | } | 832 | } |
809 | 833 | ||
834 | static int __init tpacpi_query_bcl_levels(acpi_handle handle) | ||
835 | { | ||
836 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
837 | union acpi_object *obj; | ||
838 | int rc; | ||
839 | |||
840 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { | ||
841 | obj = (union acpi_object *)buffer.pointer; | ||
842 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { | ||
843 | printk(TPACPI_ERR "Unknown _BCL data, " | ||
844 | "please report this to %s\n", TPACPI_MAIL); | ||
845 | rc = 0; | ||
846 | } else { | ||
847 | rc = obj->package.count; | ||
848 | } | ||
849 | } else { | ||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | kfree(buffer.pointer); | ||
854 | return rc; | ||
855 | } | ||
856 | |||
857 | static acpi_status __init tpacpi_acpi_walk_find_bcl(acpi_handle handle, | ||
858 | u32 lvl, void *context, void **rv) | ||
859 | { | ||
860 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
861 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
862 | |||
863 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | ||
864 | !strncmp("_BCL", name, sizeof(name) - 1)) { | ||
865 | BUG_ON(!rv || !*rv); | ||
866 | **(int **)rv = tpacpi_query_bcl_levels(handle); | ||
867 | return AE_CTRL_TERMINATE; | ||
868 | } else { | ||
869 | return AE_OK; | ||
870 | } | ||
871 | } | ||
872 | |||
873 | /* | ||
874 | * Returns 0 (no ACPI _BCL or _BCL invalid), or size of brightness map | ||
875 | */ | ||
876 | static int __init tpacpi_check_std_acpi_brightness_support(void) | ||
877 | { | ||
878 | int status; | ||
879 | int bcl_levels = 0; | ||
880 | void *bcl_ptr = &bcl_levels; | ||
881 | |||
882 | if (!vid_handle) { | ||
883 | TPACPI_ACPIHANDLE_INIT(vid); | ||
884 | } | ||
885 | if (!vid_handle) | ||
886 | return 0; | ||
887 | |||
888 | /* | ||
889 | * Search for a _BCL method, and execute it. This is safe on all | ||
890 | * ThinkPads, and as a side-effect, _BCL will place a Lenovo Vista | ||
891 | * BIOS in ACPI backlight control mode. We do NOT have to care | ||
892 | * about calling the _BCL method in an enabled video device, any | ||
893 | * will do for our purposes. | ||
894 | */ | ||
895 | |||
896 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | ||
897 | tpacpi_acpi_walk_find_bcl, NULL, | ||
898 | &bcl_ptr); | ||
899 | |||
900 | if (ACPI_SUCCESS(status) && bcl_levels > 2) { | ||
901 | tp_features.bright_acpimode = 1; | ||
902 | return (bcl_levels - 2); | ||
903 | } | ||
904 | |||
905 | return 0; | ||
906 | } | ||
907 | |||
810 | /************************************************************************* | 908 | /************************************************************************* |
811 | * thinkpad-acpi driver attributes | 909 | * thinkpad-acpi driver attributes |
812 | */ | 910 | */ |
@@ -909,12 +1007,14 @@ static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) | |||
909 | thinkpad_id.ec_version_str : "unknown"); | 1007 | thinkpad_id.ec_version_str : "unknown"); |
910 | 1008 | ||
911 | if (thinkpad_id.vendor && thinkpad_id.model_str) | 1009 | if (thinkpad_id.vendor && thinkpad_id.model_str) |
912 | printk(TPACPI_INFO "%s %s\n", | 1010 | printk(TPACPI_INFO "%s %s, model %s\n", |
913 | (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? | 1011 | (thinkpad_id.vendor == PCI_VENDOR_ID_IBM) ? |
914 | "IBM" : ((thinkpad_id.vendor == | 1012 | "IBM" : ((thinkpad_id.vendor == |
915 | PCI_VENDOR_ID_LENOVO) ? | 1013 | PCI_VENDOR_ID_LENOVO) ? |
916 | "Lenovo" : "Unknown vendor"), | 1014 | "Lenovo" : "Unknown vendor"), |
917 | thinkpad_id.model_str); | 1015 | thinkpad_id.model_str, |
1016 | (thinkpad_id.nummodel_str) ? | ||
1017 | thinkpad_id.nummodel_str : "unknown"); | ||
918 | 1018 | ||
919 | return 0; | 1019 | return 0; |
920 | } | 1020 | } |
@@ -1107,6 +1207,19 @@ static int hotkey_mask_set(u32 mask) | |||
1107 | int rc = 0; | 1207 | int rc = 0; |
1108 | 1208 | ||
1109 | if (tp_features.hotkey_mask) { | 1209 | if (tp_features.hotkey_mask) { |
1210 | if (!tp_warned.hotkey_mask_ff && | ||
1211 | (mask == 0xffff || mask == 0xffffff || | ||
1212 | mask == 0xffffffff)) { | ||
1213 | tp_warned.hotkey_mask_ff = 1; | ||
1214 | printk(TPACPI_NOTICE | ||
1215 | "setting the hotkey mask to 0x%08x is likely " | ||
1216 | "not the best way to go about it\n", mask); | ||
1217 | printk(TPACPI_NOTICE | ||
1218 | "please consider using the driver defaults, " | ||
1219 | "and refer to up-to-date thinkpad-acpi " | ||
1220 | "documentation\n"); | ||
1221 | } | ||
1222 | |||
1110 | HOTKEY_CONFIG_CRITICAL_START | 1223 | HOTKEY_CONFIG_CRITICAL_START |
1111 | for (i = 0; i < 32; i++) { | 1224 | for (i = 0; i < 32; i++) { |
1112 | u32 m = 1 << i; | 1225 | u32 m = 1 << i; |
@@ -1427,8 +1540,7 @@ static void hotkey_poll_setup(int may_warn) | |||
1427 | (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { | 1540 | (tpacpi_inputdev->users > 0 || hotkey_report_mode < 2)) { |
1428 | if (!tpacpi_hotkey_task) { | 1541 | if (!tpacpi_hotkey_task) { |
1429 | tpacpi_hotkey_task = kthread_run(hotkey_kthread, | 1542 | tpacpi_hotkey_task = kthread_run(hotkey_kthread, |
1430 | NULL, | 1543 | NULL, TPACPI_NVRAM_KTHREAD_NAME); |
1431 | TPACPI_FILE "d"); | ||
1432 | if (IS_ERR(tpacpi_hotkey_task)) { | 1544 | if (IS_ERR(tpacpi_hotkey_task)) { |
1433 | tpacpi_hotkey_task = NULL; | 1545 | tpacpi_hotkey_task = NULL; |
1434 | printk(TPACPI_ERR | 1546 | printk(TPACPI_ERR |
@@ -1887,6 +1999,9 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
1887 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ | 1999 | KEY_UNKNOWN, /* 0x0D: FN+INSERT */ |
1888 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ | 2000 | KEY_UNKNOWN, /* 0x0E: FN+DELETE */ |
1889 | 2001 | ||
2002 | /* These either have to go through ACPI video, or | ||
2003 | * act like in the IBM ThinkPads, so don't ever | ||
2004 | * enable them by default */ | ||
1890 | KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ | 2005 | KEY_RESERVED, /* 0x0F: FN+HOME (brightness up) */ |
1891 | KEY_RESERVED, /* 0x10: FN+END (brightness down) */ | 2006 | KEY_RESERVED, /* 0x10: FN+END (brightness down) */ |
1892 | 2007 | ||
@@ -2091,6 +2206,32 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) | |||
2091 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); | 2206 | set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit); |
2092 | } | 2207 | } |
2093 | 2208 | ||
2209 | /* Do not issue duplicate brightness change events to | ||
2210 | * userspace */ | ||
2211 | if (!tp_features.bright_acpimode) | ||
2212 | /* update bright_acpimode... */ | ||
2213 | tpacpi_check_std_acpi_brightness_support(); | ||
2214 | |||
2215 | if (tp_features.bright_acpimode) { | ||
2216 | printk(TPACPI_INFO | ||
2217 | "This ThinkPad has standard ACPI backlight " | ||
2218 | "brightness control, supported by the ACPI " | ||
2219 | "video driver\n"); | ||
2220 | printk(TPACPI_NOTICE | ||
2221 | "Disabling thinkpad-acpi brightness events " | ||
2222 | "by default...\n"); | ||
2223 | |||
2224 | /* The hotkey_reserved_mask change below is not | ||
2225 | * necessary while the keys are at KEY_RESERVED in the | ||
2226 | * default map, but better safe than sorry, leave it | ||
2227 | * here as a marker of what we have to do, especially | ||
2228 | * when we finally become able to set this at runtime | ||
2229 | * on response to X.org requests */ | ||
2230 | hotkey_reserved_mask |= | ||
2231 | (1 << TP_ACPI_HOTKEYSCAN_FNHOME) | ||
2232 | | (1 << TP_ACPI_HOTKEYSCAN_FNEND); | ||
2233 | } | ||
2234 | |||
2094 | dbg_printk(TPACPI_DBG_INIT, | 2235 | dbg_printk(TPACPI_DBG_INIT, |
2095 | "enabling hot key handling\n"); | 2236 | "enabling hot key handling\n"); |
2096 | res = hotkey_status_set(1); | 2237 | res = hotkey_status_set(1); |
@@ -3110,13 +3251,82 @@ static struct ibm_struct video_driver_data = { | |||
3110 | TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ | 3251 | TPACPI_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ |
3111 | TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */ | 3252 | TPACPI_HANDLE(ledb, ec, "LEDB"); /* G4x */ |
3112 | 3253 | ||
3254 | static int light_get_status(void) | ||
3255 | { | ||
3256 | int status = 0; | ||
3257 | |||
3258 | if (tp_features.light_status) { | ||
3259 | if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) | ||
3260 | return -EIO; | ||
3261 | return (!!status); | ||
3262 | } | ||
3263 | |||
3264 | return -ENXIO; | ||
3265 | } | ||
3266 | |||
3267 | static int light_set_status(int status) | ||
3268 | { | ||
3269 | int rc; | ||
3270 | |||
3271 | if (tp_features.light) { | ||
3272 | if (cmos_handle) { | ||
3273 | rc = acpi_evalf(cmos_handle, NULL, NULL, "vd", | ||
3274 | (status)? | ||
3275 | TP_CMOS_THINKLIGHT_ON : | ||
3276 | TP_CMOS_THINKLIGHT_OFF); | ||
3277 | } else { | ||
3278 | rc = acpi_evalf(lght_handle, NULL, NULL, "vd", | ||
3279 | (status)? 1 : 0); | ||
3280 | } | ||
3281 | return (rc)? 0 : -EIO; | ||
3282 | } | ||
3283 | |||
3284 | return -ENXIO; | ||
3285 | } | ||
3286 | |||
3287 | static void light_set_status_worker(struct work_struct *work) | ||
3288 | { | ||
3289 | struct tpacpi_led_classdev *data = | ||
3290 | container_of(work, struct tpacpi_led_classdev, work); | ||
3291 | |||
3292 | if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) | ||
3293 | light_set_status((data->new_brightness != LED_OFF)); | ||
3294 | } | ||
3295 | |||
3296 | static void light_sysfs_set(struct led_classdev *led_cdev, | ||
3297 | enum led_brightness brightness) | ||
3298 | { | ||
3299 | struct tpacpi_led_classdev *data = | ||
3300 | container_of(led_cdev, | ||
3301 | struct tpacpi_led_classdev, | ||
3302 | led_classdev); | ||
3303 | data->new_brightness = brightness; | ||
3304 | queue_work(tpacpi_wq, &data->work); | ||
3305 | } | ||
3306 | |||
3307 | static enum led_brightness light_sysfs_get(struct led_classdev *led_cdev) | ||
3308 | { | ||
3309 | return (light_get_status() == 1)? LED_FULL : LED_OFF; | ||
3310 | } | ||
3311 | |||
3312 | static struct tpacpi_led_classdev tpacpi_led_thinklight = { | ||
3313 | .led_classdev = { | ||
3314 | .name = "tpacpi::thinklight", | ||
3315 | .brightness_set = &light_sysfs_set, | ||
3316 | .brightness_get = &light_sysfs_get, | ||
3317 | } | ||
3318 | }; | ||
3319 | |||
3113 | static int __init light_init(struct ibm_init_struct *iibm) | 3320 | static int __init light_init(struct ibm_init_struct *iibm) |
3114 | { | 3321 | { |
3322 | int rc = 0; | ||
3323 | |||
3115 | vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); | 3324 | vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); |
3116 | 3325 | ||
3117 | TPACPI_ACPIHANDLE_INIT(ledb); | 3326 | TPACPI_ACPIHANDLE_INIT(ledb); |
3118 | TPACPI_ACPIHANDLE_INIT(lght); | 3327 | TPACPI_ACPIHANDLE_INIT(lght); |
3119 | TPACPI_ACPIHANDLE_INIT(cmos); | 3328 | TPACPI_ACPIHANDLE_INIT(cmos); |
3329 | INIT_WORK(&tpacpi_led_thinklight.work, light_set_status_worker); | ||
3120 | 3330 | ||
3121 | /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ | 3331 | /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ |
3122 | tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; | 3332 | tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; |
@@ -3130,13 +3340,31 @@ static int __init light_init(struct ibm_init_struct *iibm) | |||
3130 | vdbg_printk(TPACPI_DBG_INIT, "light is %s\n", | 3340 | vdbg_printk(TPACPI_DBG_INIT, "light is %s\n", |
3131 | str_supported(tp_features.light)); | 3341 | str_supported(tp_features.light)); |
3132 | 3342 | ||
3133 | return (tp_features.light)? 0 : 1; | 3343 | if (tp_features.light) { |
3344 | rc = led_classdev_register(&tpacpi_pdev->dev, | ||
3345 | &tpacpi_led_thinklight.led_classdev); | ||
3346 | } | ||
3347 | |||
3348 | if (rc < 0) { | ||
3349 | tp_features.light = 0; | ||
3350 | tp_features.light_status = 0; | ||
3351 | } else { | ||
3352 | rc = (tp_features.light)? 0 : 1; | ||
3353 | } | ||
3354 | return rc; | ||
3355 | } | ||
3356 | |||
3357 | static void light_exit(void) | ||
3358 | { | ||
3359 | led_classdev_unregister(&tpacpi_led_thinklight.led_classdev); | ||
3360 | if (work_pending(&tpacpi_led_thinklight.work)) | ||
3361 | flush_workqueue(tpacpi_wq); | ||
3134 | } | 3362 | } |
3135 | 3363 | ||
3136 | static int light_read(char *p) | 3364 | static int light_read(char *p) |
3137 | { | 3365 | { |
3138 | int len = 0; | 3366 | int len = 0; |
3139 | int status = 0; | 3367 | int status; |
3140 | 3368 | ||
3141 | if (!tp_features.light) { | 3369 | if (!tp_features.light) { |
3142 | len += sprintf(p + len, "status:\t\tnot supported\n"); | 3370 | len += sprintf(p + len, "status:\t\tnot supported\n"); |
@@ -3144,8 +3372,9 @@ static int light_read(char *p) | |||
3144 | len += sprintf(p + len, "status:\t\tunknown\n"); | 3372 | len += sprintf(p + len, "status:\t\tunknown\n"); |
3145 | len += sprintf(p + len, "commands:\ton, off\n"); | 3373 | len += sprintf(p + len, "commands:\ton, off\n"); |
3146 | } else { | 3374 | } else { |
3147 | if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) | 3375 | status = light_get_status(); |
3148 | return -EIO; | 3376 | if (status < 0) |
3377 | return status; | ||
3149 | len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); | 3378 | len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); |
3150 | len += sprintf(p + len, "commands:\ton, off\n"); | 3379 | len += sprintf(p + len, "commands:\ton, off\n"); |
3151 | } | 3380 | } |
@@ -3155,37 +3384,29 @@ static int light_read(char *p) | |||
3155 | 3384 | ||
3156 | static int light_write(char *buf) | 3385 | static int light_write(char *buf) |
3157 | { | 3386 | { |
3158 | int cmos_cmd, lght_cmd; | ||
3159 | char *cmd; | 3387 | char *cmd; |
3160 | int success; | 3388 | int newstatus = 0; |
3161 | 3389 | ||
3162 | if (!tp_features.light) | 3390 | if (!tp_features.light) |
3163 | return -ENODEV; | 3391 | return -ENODEV; |
3164 | 3392 | ||
3165 | while ((cmd = next_cmd(&buf))) { | 3393 | while ((cmd = next_cmd(&buf))) { |
3166 | if (strlencmp(cmd, "on") == 0) { | 3394 | if (strlencmp(cmd, "on") == 0) { |
3167 | cmos_cmd = 0x0c; | 3395 | newstatus = 1; |
3168 | lght_cmd = 1; | ||
3169 | } else if (strlencmp(cmd, "off") == 0) { | 3396 | } else if (strlencmp(cmd, "off") == 0) { |
3170 | cmos_cmd = 0x0d; | 3397 | newstatus = 0; |
3171 | lght_cmd = 0; | ||
3172 | } else | 3398 | } else |
3173 | return -EINVAL; | 3399 | return -EINVAL; |
3174 | |||
3175 | success = cmos_handle ? | ||
3176 | acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : | ||
3177 | acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); | ||
3178 | if (!success) | ||
3179 | return -EIO; | ||
3180 | } | 3400 | } |
3181 | 3401 | ||
3182 | return 0; | 3402 | return light_set_status(newstatus); |
3183 | } | 3403 | } |
3184 | 3404 | ||
3185 | static struct ibm_struct light_driver_data = { | 3405 | static struct ibm_struct light_driver_data = { |
3186 | .name = "light", | 3406 | .name = "light", |
3187 | .read = light_read, | 3407 | .read = light_read, |
3188 | .write = light_write, | 3408 | .write = light_write, |
3409 | .exit = light_exit, | ||
3189 | }; | 3410 | }; |
3190 | 3411 | ||
3191 | /************************************************************************* | 3412 | /************************************************************************* |
@@ -3583,6 +3804,12 @@ enum { /* For TPACPI_LED_OLD */ | |||
3583 | TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */ | 3804 | TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */ |
3584 | }; | 3805 | }; |
3585 | 3806 | ||
3807 | enum led_status_t { | ||
3808 | TPACPI_LED_OFF = 0, | ||
3809 | TPACPI_LED_ON, | ||
3810 | TPACPI_LED_BLINK, | ||
3811 | }; | ||
3812 | |||
3586 | static enum led_access_mode led_supported; | 3813 | static enum led_access_mode led_supported; |
3587 | 3814 | ||
3588 | TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | 3815 | TPACPI_HANDLE(led, ec, "SLED", /* 570 */ |
@@ -3591,8 +3818,174 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */ | |||
3591 | "LED", /* all others */ | 3818 | "LED", /* all others */ |
3592 | ); /* R30, R31 */ | 3819 | ); /* R30, R31 */ |
3593 | 3820 | ||
3821 | #define TPACPI_LED_NUMLEDS 8 | ||
3822 | static struct tpacpi_led_classdev *tpacpi_leds; | ||
3823 | static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; | ||
3824 | static const char const *tpacpi_led_names[TPACPI_LED_NUMLEDS] = { | ||
3825 | /* there's a limit of 19 chars + NULL before 2.6.26 */ | ||
3826 | "tpacpi::power", | ||
3827 | "tpacpi:orange:batt", | ||
3828 | "tpacpi:green:batt", | ||
3829 | "tpacpi::dock_active", | ||
3830 | "tpacpi::bay_active", | ||
3831 | "tpacpi::dock_batt", | ||
3832 | "tpacpi::unknown_led", | ||
3833 | "tpacpi::standby", | ||
3834 | }; | ||
3835 | |||
3836 | static int led_get_status(unsigned int led) | ||
3837 | { | ||
3838 | int status; | ||
3839 | enum led_status_t led_s; | ||
3840 | |||
3841 | switch (led_supported) { | ||
3842 | case TPACPI_LED_570: | ||
3843 | if (!acpi_evalf(ec_handle, | ||
3844 | &status, "GLED", "dd", 1 << led)) | ||
3845 | return -EIO; | ||
3846 | led_s = (status == 0)? | ||
3847 | TPACPI_LED_OFF : | ||
3848 | ((status == 1)? | ||
3849 | TPACPI_LED_ON : | ||
3850 | TPACPI_LED_BLINK); | ||
3851 | tpacpi_led_state_cache[led] = led_s; | ||
3852 | return led_s; | ||
3853 | default: | ||
3854 | return -ENXIO; | ||
3855 | } | ||
3856 | |||
3857 | /* not reached */ | ||
3858 | } | ||
3859 | |||
3860 | static int led_set_status(unsigned int led, enum led_status_t ledstatus) | ||
3861 | { | ||
3862 | /* off, on, blink. Index is led_status_t */ | ||
3863 | static const int const led_sled_arg1[] = { 0, 1, 3 }; | ||
3864 | static const int const led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ | ||
3865 | static const int const led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ | ||
3866 | static const int const led_led_arg1[] = { 0, 0x80, 0xc0 }; | ||
3867 | |||
3868 | int rc = 0; | ||
3869 | |||
3870 | switch (led_supported) { | ||
3871 | case TPACPI_LED_570: | ||
3872 | /* 570 */ | ||
3873 | led = 1 << led; | ||
3874 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | ||
3875 | led, led_sled_arg1[ledstatus])) | ||
3876 | rc = -EIO; | ||
3877 | break; | ||
3878 | case TPACPI_LED_OLD: | ||
3879 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ | ||
3880 | led = 1 << led; | ||
3881 | rc = ec_write(TPACPI_LED_EC_HLMS, led); | ||
3882 | if (rc >= 0) | ||
3883 | rc = ec_write(TPACPI_LED_EC_HLBL, | ||
3884 | led * led_exp_hlbl[ledstatus]); | ||
3885 | if (rc >= 0) | ||
3886 | rc = ec_write(TPACPI_LED_EC_HLCL, | ||
3887 | led * led_exp_hlcl[ledstatus]); | ||
3888 | break; | ||
3889 | case TPACPI_LED_NEW: | ||
3890 | /* all others */ | ||
3891 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | ||
3892 | led, led_led_arg1[ledstatus])) | ||
3893 | rc = -EIO; | ||
3894 | break; | ||
3895 | default: | ||
3896 | rc = -ENXIO; | ||
3897 | } | ||
3898 | |||
3899 | if (!rc) | ||
3900 | tpacpi_led_state_cache[led] = ledstatus; | ||
3901 | |||
3902 | return rc; | ||
3903 | } | ||
3904 | |||
3905 | static void led_sysfs_set_status(unsigned int led, | ||
3906 | enum led_brightness brightness) | ||
3907 | { | ||
3908 | led_set_status(led, | ||
3909 | (brightness == LED_OFF) ? | ||
3910 | TPACPI_LED_OFF : | ||
3911 | (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ? | ||
3912 | TPACPI_LED_BLINK : TPACPI_LED_ON); | ||
3913 | } | ||
3914 | |||
3915 | static void led_set_status_worker(struct work_struct *work) | ||
3916 | { | ||
3917 | struct tpacpi_led_classdev *data = | ||
3918 | container_of(work, struct tpacpi_led_classdev, work); | ||
3919 | |||
3920 | if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING)) | ||
3921 | led_sysfs_set_status(data->led, data->new_brightness); | ||
3922 | } | ||
3923 | |||
3924 | static void led_sysfs_set(struct led_classdev *led_cdev, | ||
3925 | enum led_brightness brightness) | ||
3926 | { | ||
3927 | struct tpacpi_led_classdev *data = container_of(led_cdev, | ||
3928 | struct tpacpi_led_classdev, led_classdev); | ||
3929 | |||
3930 | data->new_brightness = brightness; | ||
3931 | queue_work(tpacpi_wq, &data->work); | ||
3932 | } | ||
3933 | |||
3934 | static int led_sysfs_blink_set(struct led_classdev *led_cdev, | ||
3935 | unsigned long *delay_on, unsigned long *delay_off) | ||
3936 | { | ||
3937 | struct tpacpi_led_classdev *data = container_of(led_cdev, | ||
3938 | struct tpacpi_led_classdev, led_classdev); | ||
3939 | |||
3940 | /* Can we choose the flash rate? */ | ||
3941 | if (*delay_on == 0 && *delay_off == 0) { | ||
3942 | /* yes. set them to the hardware blink rate (1 Hz) */ | ||
3943 | *delay_on = 500; /* ms */ | ||
3944 | *delay_off = 500; /* ms */ | ||
3945 | } else if ((*delay_on != 500) || (*delay_off != 500)) | ||
3946 | return -EINVAL; | ||
3947 | |||
3948 | data->new_brightness = TPACPI_LED_BLINK; | ||
3949 | queue_work(tpacpi_wq, &data->work); | ||
3950 | |||
3951 | return 0; | ||
3952 | } | ||
3953 | |||
3954 | static enum led_brightness led_sysfs_get(struct led_classdev *led_cdev) | ||
3955 | { | ||
3956 | int rc; | ||
3957 | |||
3958 | struct tpacpi_led_classdev *data = container_of(led_cdev, | ||
3959 | struct tpacpi_led_classdev, led_classdev); | ||
3960 | |||
3961 | rc = led_get_status(data->led); | ||
3962 | |||
3963 | if (rc == TPACPI_LED_OFF || rc < 0) | ||
3964 | rc = LED_OFF; /* no error handling in led class :( */ | ||
3965 | else | ||
3966 | rc = LED_FULL; | ||
3967 | |||
3968 | return rc; | ||
3969 | } | ||
3970 | |||
3971 | static void led_exit(void) | ||
3972 | { | ||
3973 | unsigned int i; | ||
3974 | |||
3975 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | ||
3976 | if (tpacpi_leds[i].led_classdev.name) | ||
3977 | led_classdev_unregister(&tpacpi_leds[i].led_classdev); | ||
3978 | } | ||
3979 | |||
3980 | kfree(tpacpi_leds); | ||
3981 | tpacpi_leds = NULL; | ||
3982 | } | ||
3983 | |||
3594 | static int __init led_init(struct ibm_init_struct *iibm) | 3984 | static int __init led_init(struct ibm_init_struct *iibm) |
3595 | { | 3985 | { |
3986 | unsigned int i; | ||
3987 | int rc; | ||
3988 | |||
3596 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); | 3989 | vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); |
3597 | 3990 | ||
3598 | TPACPI_ACPIHANDLE_INIT(led); | 3991 | TPACPI_ACPIHANDLE_INIT(led); |
@@ -3613,10 +4006,41 @@ static int __init led_init(struct ibm_init_struct *iibm) | |||
3613 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", | 4006 | vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", |
3614 | str_supported(led_supported), led_supported); | 4007 | str_supported(led_supported), led_supported); |
3615 | 4008 | ||
4009 | tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, | ||
4010 | GFP_KERNEL); | ||
4011 | if (!tpacpi_leds) { | ||
4012 | printk(TPACPI_ERR "Out of memory for LED data\n"); | ||
4013 | return -ENOMEM; | ||
4014 | } | ||
4015 | |||
4016 | for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { | ||
4017 | tpacpi_leds[i].led = i; | ||
4018 | |||
4019 | tpacpi_leds[i].led_classdev.brightness_set = &led_sysfs_set; | ||
4020 | tpacpi_leds[i].led_classdev.blink_set = &led_sysfs_blink_set; | ||
4021 | if (led_supported == TPACPI_LED_570) | ||
4022 | tpacpi_leds[i].led_classdev.brightness_get = | ||
4023 | &led_sysfs_get; | ||
4024 | |||
4025 | tpacpi_leds[i].led_classdev.name = tpacpi_led_names[i]; | ||
4026 | |||
4027 | INIT_WORK(&tpacpi_leds[i].work, led_set_status_worker); | ||
4028 | |||
4029 | rc = led_classdev_register(&tpacpi_pdev->dev, | ||
4030 | &tpacpi_leds[i].led_classdev); | ||
4031 | if (rc < 0) { | ||
4032 | tpacpi_leds[i].led_classdev.name = NULL; | ||
4033 | led_exit(); | ||
4034 | return rc; | ||
4035 | } | ||
4036 | } | ||
4037 | |||
3616 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; | 4038 | return (led_supported != TPACPI_LED_NONE)? 0 : 1; |
3617 | } | 4039 | } |
3618 | 4040 | ||
3619 | #define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) | 4041 | #define str_led_status(s) \ |
4042 | ((s) == TPACPI_LED_OFF ? "off" : \ | ||
4043 | ((s) == TPACPI_LED_ON ? "on" : "blinking")) | ||
3620 | 4044 | ||
3621 | static int led_read(char *p) | 4045 | static int led_read(char *p) |
3622 | { | 4046 | { |
@@ -3632,11 +4056,11 @@ static int led_read(char *p) | |||
3632 | /* 570 */ | 4056 | /* 570 */ |
3633 | int i, status; | 4057 | int i, status; |
3634 | for (i = 0; i < 8; i++) { | 4058 | for (i = 0; i < 8; i++) { |
3635 | if (!acpi_evalf(ec_handle, | 4059 | status = led_get_status(i); |
3636 | &status, "GLED", "dd", 1 << i)) | 4060 | if (status < 0) |
3637 | return -EIO; | 4061 | return -EIO; |
3638 | len += sprintf(p + len, "%d:\t\t%s\n", | 4062 | len += sprintf(p + len, "%d:\t\t%s\n", |
3639 | i, led_status(status)); | 4063 | i, str_led_status(status)); |
3640 | } | 4064 | } |
3641 | } | 4065 | } |
3642 | 4066 | ||
@@ -3646,16 +4070,11 @@ static int led_read(char *p) | |||
3646 | return len; | 4070 | return len; |
3647 | } | 4071 | } |
3648 | 4072 | ||
3649 | /* off, on, blink */ | ||
3650 | static const int led_sled_arg1[] = { 0, 1, 3 }; | ||
3651 | static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ | ||
3652 | static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ | ||
3653 | static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; | ||
3654 | |||
3655 | static int led_write(char *buf) | 4073 | static int led_write(char *buf) |
3656 | { | 4074 | { |
3657 | char *cmd; | 4075 | char *cmd; |
3658 | int led, ind, ret; | 4076 | int led, rc; |
4077 | enum led_status_t s; | ||
3659 | 4078 | ||
3660 | if (!led_supported) | 4079 | if (!led_supported) |
3661 | return -ENODEV; | 4080 | return -ENODEV; |
@@ -3665,38 +4084,18 @@ static int led_write(char *buf) | |||
3665 | return -EINVAL; | 4084 | return -EINVAL; |
3666 | 4085 | ||
3667 | if (strstr(cmd, "off")) { | 4086 | if (strstr(cmd, "off")) { |
3668 | ind = 0; | 4087 | s = TPACPI_LED_OFF; |
3669 | } else if (strstr(cmd, "on")) { | 4088 | } else if (strstr(cmd, "on")) { |
3670 | ind = 1; | 4089 | s = TPACPI_LED_ON; |
3671 | } else if (strstr(cmd, "blink")) { | 4090 | } else if (strstr(cmd, "blink")) { |
3672 | ind = 2; | 4091 | s = TPACPI_LED_BLINK; |
3673 | } else | ||
3674 | return -EINVAL; | ||
3675 | |||
3676 | if (led_supported == TPACPI_LED_570) { | ||
3677 | /* 570 */ | ||
3678 | led = 1 << led; | ||
3679 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | ||
3680 | led, led_sled_arg1[ind])) | ||
3681 | return -EIO; | ||
3682 | } else if (led_supported == TPACPI_LED_OLD) { | ||
3683 | /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ | ||
3684 | led = 1 << led; | ||
3685 | ret = ec_write(TPACPI_LED_EC_HLMS, led); | ||
3686 | if (ret >= 0) | ||
3687 | ret = ec_write(TPACPI_LED_EC_HLBL, | ||
3688 | led * led_exp_hlbl[ind]); | ||
3689 | if (ret >= 0) | ||
3690 | ret = ec_write(TPACPI_LED_EC_HLCL, | ||
3691 | led * led_exp_hlcl[ind]); | ||
3692 | if (ret < 0) | ||
3693 | return ret; | ||
3694 | } else { | 4092 | } else { |
3695 | /* all others */ | 4093 | return -EINVAL; |
3696 | if (!acpi_evalf(led_handle, NULL, NULL, "vdd", | ||
3697 | led, led_led_arg1[ind])) | ||
3698 | return -EIO; | ||
3699 | } | 4094 | } |
4095 | |||
4096 | rc = led_set_status(led, s); | ||
4097 | if (rc < 0) | ||
4098 | return rc; | ||
3700 | } | 4099 | } |
3701 | 4100 | ||
3702 | return 0; | 4101 | return 0; |
@@ -3706,6 +4105,7 @@ static struct ibm_struct led_driver_data = { | |||
3706 | .name = "led", | 4105 | .name = "led", |
3707 | .read = led_read, | 4106 | .read = led_read, |
3708 | .write = led_write, | 4107 | .write = led_write, |
4108 | .exit = led_exit, | ||
3709 | }; | 4109 | }; |
3710 | 4110 | ||
3711 | /************************************************************************* | 4111 | /************************************************************************* |
@@ -4170,8 +4570,16 @@ static struct ibm_struct ecdump_driver_data = { | |||
4170 | 4570 | ||
4171 | #define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen" | 4571 | #define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen" |
4172 | 4572 | ||
4573 | enum { | ||
4574 | TP_EC_BACKLIGHT = 0x31, | ||
4575 | |||
4576 | /* TP_EC_BACKLIGHT bitmasks */ | ||
4577 | TP_EC_BACKLIGHT_LVLMSK = 0x1F, | ||
4578 | TP_EC_BACKLIGHT_CMDMSK = 0xE0, | ||
4579 | TP_EC_BACKLIGHT_MAPSW = 0x20, | ||
4580 | }; | ||
4581 | |||
4173 | static struct backlight_device *ibm_backlight_device; | 4582 | static struct backlight_device *ibm_backlight_device; |
4174 | static int brightness_offset = 0x31; | ||
4175 | static int brightness_mode; | 4583 | static int brightness_mode; |
4176 | static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */ | 4584 | static unsigned int brightness_enable = 2; /* 2 = auto, 0 = no, 1 = yes */ |
4177 | 4585 | ||
@@ -4180,16 +4588,24 @@ static struct mutex brightness_mutex; | |||
4180 | /* | 4588 | /* |
4181 | * ThinkPads can read brightness from two places: EC 0x31, or | 4589 | * ThinkPads can read brightness from two places: EC 0x31, or |
4182 | * CMOS NVRAM byte 0x5E, bits 0-3. | 4590 | * CMOS NVRAM byte 0x5E, bits 0-3. |
4591 | * | ||
4592 | * EC 0x31 has the following layout | ||
4593 | * Bit 7: unknown function | ||
4594 | * Bit 6: unknown function | ||
4595 | * Bit 5: Z: honour scale changes, NZ: ignore scale changes | ||
4596 | * Bit 4: must be set to zero to avoid problems | ||
4597 | * Bit 3-0: backlight brightness level | ||
4598 | * | ||
4599 | * brightness_get_raw returns status data in the EC 0x31 layout | ||
4183 | */ | 4600 | */ |
4184 | static int brightness_get(struct backlight_device *bd) | 4601 | static int brightness_get_raw(int *status) |
4185 | { | 4602 | { |
4186 | u8 lec = 0, lcmos = 0, level = 0; | 4603 | u8 lec = 0, lcmos = 0, level = 0; |
4187 | 4604 | ||
4188 | if (brightness_mode & 1) { | 4605 | if (brightness_mode & 1) { |
4189 | if (!acpi_ec_read(brightness_offset, &lec)) | 4606 | if (!acpi_ec_read(TP_EC_BACKLIGHT, &lec)) |
4190 | return -EIO; | 4607 | return -EIO; |
4191 | lec &= (tp_features.bright_16levels)? 0x0f : 0x07; | 4608 | level = lec & TP_EC_BACKLIGHT_LVLMSK; |
4192 | level = lec; | ||
4193 | }; | 4609 | }; |
4194 | if (brightness_mode & 2) { | 4610 | if (brightness_mode & 2) { |
4195 | lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) | 4611 | lcmos = (nvram_read_byte(TP_NVRAM_ADDR_BRIGHTNESS) |
@@ -4199,16 +4615,27 @@ static int brightness_get(struct backlight_device *bd) | |||
4199 | level = lcmos; | 4615 | level = lcmos; |
4200 | } | 4616 | } |
4201 | 4617 | ||
4202 | if (brightness_mode == 3 && lec != lcmos) { | 4618 | if (brightness_mode == 3) { |
4203 | printk(TPACPI_ERR | 4619 | *status = lec; /* Prefer EC, CMOS is just a backing store */ |
4204 | "CMOS NVRAM (%u) and EC (%u) do not agree " | 4620 | lec &= TP_EC_BACKLIGHT_LVLMSK; |
4205 | "on display brightness level\n", | 4621 | if (lec == lcmos) |
4206 | (unsigned int) lcmos, | 4622 | tp_warned.bright_cmos_ec_unsync = 0; |
4207 | (unsigned int) lec); | 4623 | else { |
4208 | return -EIO; | 4624 | if (!tp_warned.bright_cmos_ec_unsync) { |
4625 | printk(TPACPI_ERR | ||
4626 | "CMOS NVRAM (%u) and EC (%u) do not " | ||
4627 | "agree on display brightness level\n", | ||
4628 | (unsigned int) lcmos, | ||
4629 | (unsigned int) lec); | ||
4630 | tp_warned.bright_cmos_ec_unsync = 1; | ||
4631 | } | ||
4632 | return -EIO; | ||
4633 | } | ||
4634 | } else { | ||
4635 | *status = level; | ||
4209 | } | 4636 | } |
4210 | 4637 | ||
4211 | return level; | 4638 | return 0; |
4212 | } | 4639 | } |
4213 | 4640 | ||
4214 | /* May return EINTR which can always be mapped to ERESTARTSYS */ | 4641 | /* May return EINTR which can always be mapped to ERESTARTSYS */ |
@@ -4216,19 +4643,22 @@ static int brightness_set(int value) | |||
4216 | { | 4643 | { |
4217 | int cmos_cmd, inc, i, res; | 4644 | int cmos_cmd, inc, i, res; |
4218 | int current_value; | 4645 | int current_value; |
4646 | int command_bits; | ||
4219 | 4647 | ||
4220 | if (value > ((tp_features.bright_16levels)? 15 : 7)) | 4648 | if (value > ((tp_features.bright_16levels)? 15 : 7) || |
4649 | value < 0) | ||
4221 | return -EINVAL; | 4650 | return -EINVAL; |
4222 | 4651 | ||
4223 | res = mutex_lock_interruptible(&brightness_mutex); | 4652 | res = mutex_lock_interruptible(&brightness_mutex); |
4224 | if (res < 0) | 4653 | if (res < 0) |
4225 | return res; | 4654 | return res; |
4226 | 4655 | ||
4227 | current_value = brightness_get(NULL); | 4656 | res = brightness_get_raw(¤t_value); |
4228 | if (current_value < 0) { | 4657 | if (res < 0) |
4229 | res = current_value; | ||
4230 | goto errout; | 4658 | goto errout; |
4231 | } | 4659 | |
4660 | command_bits = current_value & TP_EC_BACKLIGHT_CMDMSK; | ||
4661 | current_value &= TP_EC_BACKLIGHT_LVLMSK; | ||
4232 | 4662 | ||
4233 | cmos_cmd = value > current_value ? | 4663 | cmos_cmd = value > current_value ? |
4234 | TP_CMOS_BRIGHTNESS_UP : | 4664 | TP_CMOS_BRIGHTNESS_UP : |
@@ -4243,7 +4673,8 @@ static int brightness_set(int value) | |||
4243 | goto errout; | 4673 | goto errout; |
4244 | } | 4674 | } |
4245 | if ((brightness_mode & 1) && | 4675 | if ((brightness_mode & 1) && |
4246 | !acpi_ec_write(brightness_offset, i + inc)) { | 4676 | !acpi_ec_write(TP_EC_BACKLIGHT, |
4677 | (i + inc) | command_bits)) { | ||
4247 | res = -EIO; | 4678 | res = -EIO; |
4248 | goto errout;; | 4679 | goto errout;; |
4249 | } | 4680 | } |
@@ -4266,106 +4697,23 @@ static int brightness_update_status(struct backlight_device *bd) | |||
4266 | bd->props.brightness : 0); | 4697 | bd->props.brightness : 0); |
4267 | } | 4698 | } |
4268 | 4699 | ||
4269 | static struct backlight_ops ibm_backlight_data = { | 4700 | static int brightness_get(struct backlight_device *bd) |
4270 | .get_brightness = brightness_get, | ||
4271 | .update_status = brightness_update_status, | ||
4272 | }; | ||
4273 | |||
4274 | /* --------------------------------------------------------------------- */ | ||
4275 | |||
4276 | static int __init tpacpi_query_bcll_levels(acpi_handle handle) | ||
4277 | { | ||
4278 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
4279 | union acpi_object *obj; | ||
4280 | int rc; | ||
4281 | |||
4282 | if (ACPI_SUCCESS(acpi_evaluate_object(handle, NULL, NULL, &buffer))) { | ||
4283 | obj = (union acpi_object *)buffer.pointer; | ||
4284 | if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { | ||
4285 | printk(TPACPI_ERR "Unknown BCLL data, " | ||
4286 | "please report this to %s\n", TPACPI_MAIL); | ||
4287 | rc = 0; | ||
4288 | } else { | ||
4289 | rc = obj->package.count; | ||
4290 | } | ||
4291 | } else { | ||
4292 | return 0; | ||
4293 | } | ||
4294 | |||
4295 | kfree(buffer.pointer); | ||
4296 | return rc; | ||
4297 | } | ||
4298 | |||
4299 | static acpi_status __init brightness_find_bcll(acpi_handle handle, u32 lvl, | ||
4300 | void *context, void **rv) | ||
4301 | { | ||
4302 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
4303 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
4304 | |||
4305 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | ||
4306 | !strncmp("BCLL", name, sizeof(name) - 1)) { | ||
4307 | if (tpacpi_query_bcll_levels(handle) == 16) { | ||
4308 | *rv = handle; | ||
4309 | return AE_CTRL_TERMINATE; | ||
4310 | } else { | ||
4311 | return AE_OK; | ||
4312 | } | ||
4313 | } else { | ||
4314 | return AE_OK; | ||
4315 | } | ||
4316 | } | ||
4317 | |||
4318 | static int __init brightness_check_levels(void) | ||
4319 | { | 4701 | { |
4320 | int status; | 4702 | int status, res; |
4321 | void *found_node = NULL; | ||
4322 | 4703 | ||
4323 | if (!vid_handle) { | 4704 | res = brightness_get_raw(&status); |
4324 | TPACPI_ACPIHANDLE_INIT(vid); | 4705 | if (res < 0) |
4325 | } | 4706 | return 0; /* FIXME: teach backlight about error handling */ |
4326 | if (!vid_handle) | ||
4327 | return 0; | ||
4328 | |||
4329 | /* Search for a BCLL package with 16 levels */ | ||
4330 | status = acpi_walk_namespace(ACPI_TYPE_PACKAGE, vid_handle, 3, | ||
4331 | brightness_find_bcll, NULL, | ||
4332 | &found_node); | ||
4333 | |||
4334 | return (ACPI_SUCCESS(status) && found_node != NULL); | ||
4335 | } | ||
4336 | |||
4337 | static acpi_status __init brightness_find_bcl(acpi_handle handle, u32 lvl, | ||
4338 | void *context, void **rv) | ||
4339 | { | ||
4340 | char name[ACPI_PATH_SEGMENT_LENGTH]; | ||
4341 | struct acpi_buffer buffer = { sizeof(name), &name }; | ||
4342 | 4707 | ||
4343 | if (ACPI_SUCCESS(acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer)) && | 4708 | return status & TP_EC_BACKLIGHT_LVLMSK; |
4344 | !strncmp("_BCL", name, sizeof(name) - 1)) { | ||
4345 | *rv = handle; | ||
4346 | return AE_CTRL_TERMINATE; | ||
4347 | } else { | ||
4348 | return AE_OK; | ||
4349 | } | ||
4350 | } | 4709 | } |
4351 | 4710 | ||
4352 | static int __init brightness_check_std_acpi_support(void) | 4711 | static struct backlight_ops ibm_backlight_data = { |
4353 | { | 4712 | .get_brightness = brightness_get, |
4354 | int status; | 4713 | .update_status = brightness_update_status, |
4355 | void *found_node = NULL; | 4714 | }; |
4356 | |||
4357 | if (!vid_handle) { | ||
4358 | TPACPI_ACPIHANDLE_INIT(vid); | ||
4359 | } | ||
4360 | if (!vid_handle) | ||
4361 | return 0; | ||
4362 | |||
4363 | /* Search for a _BCL method, but don't execute it */ | ||
4364 | status = acpi_walk_namespace(ACPI_TYPE_METHOD, vid_handle, 3, | ||
4365 | brightness_find_bcl, NULL, &found_node); | ||
4366 | 4715 | ||
4367 | return (ACPI_SUCCESS(status) && found_node != NULL); | 4716 | /* --------------------------------------------------------------------- */ |
4368 | } | ||
4369 | 4717 | ||
4370 | static int __init brightness_init(struct ibm_init_struct *iibm) | 4718 | static int __init brightness_init(struct ibm_init_struct *iibm) |
4371 | { | 4719 | { |
@@ -4375,13 +4723,19 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4375 | 4723 | ||
4376 | mutex_init(&brightness_mutex); | 4724 | mutex_init(&brightness_mutex); |
4377 | 4725 | ||
4378 | if (!brightness_enable) { | 4726 | /* |
4379 | dbg_printk(TPACPI_DBG_INIT, | 4727 | * We always attempt to detect acpi support, so as to switch |
4380 | "brightness support disabled by " | 4728 | * Lenovo Vista BIOS to ACPI brightness mode even if we are not |
4381 | "module parameter\n"); | 4729 | * going to publish a backlight interface |
4382 | return 1; | 4730 | */ |
4383 | } else if (brightness_enable > 1) { | 4731 | b = tpacpi_check_std_acpi_brightness_support(); |
4384 | if (brightness_check_std_acpi_support()) { | 4732 | if (b > 0) { |
4733 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { | ||
4734 | printk(TPACPI_NOTICE | ||
4735 | "Lenovo BIOS switched to ACPI backlight " | ||
4736 | "control mode\n"); | ||
4737 | } | ||
4738 | if (brightness_enable > 1) { | ||
4385 | printk(TPACPI_NOTICE | 4739 | printk(TPACPI_NOTICE |
4386 | "standard ACPI backlight interface " | 4740 | "standard ACPI backlight interface " |
4387 | "available, not loading native one...\n"); | 4741 | "available, not loading native one...\n"); |
@@ -4389,6 +4743,22 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4389 | } | 4743 | } |
4390 | } | 4744 | } |
4391 | 4745 | ||
4746 | if (!brightness_enable) { | ||
4747 | dbg_printk(TPACPI_DBG_INIT, | ||
4748 | "brightness support disabled by " | ||
4749 | "module parameter\n"); | ||
4750 | return 1; | ||
4751 | } | ||
4752 | |||
4753 | if (b > 16) { | ||
4754 | printk(TPACPI_ERR | ||
4755 | "Unsupported brightness interface, " | ||
4756 | "please contact %s\n", TPACPI_MAIL); | ||
4757 | return 1; | ||
4758 | } | ||
4759 | if (b == 16) | ||
4760 | tp_features.bright_16levels = 1; | ||
4761 | |||
4392 | if (!brightness_mode) { | 4762 | if (!brightness_mode) { |
4393 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) | 4763 | if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) |
4394 | brightness_mode = 2; | 4764 | brightness_mode = 2; |
@@ -4402,12 +4772,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4402 | if (brightness_mode > 3) | 4772 | if (brightness_mode > 3) |
4403 | return -EINVAL; | 4773 | return -EINVAL; |
4404 | 4774 | ||
4405 | tp_features.bright_16levels = | 4775 | if (brightness_get_raw(&b) < 0) |
4406 | thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO && | ||
4407 | brightness_check_levels(); | ||
4408 | |||
4409 | b = brightness_get(NULL); | ||
4410 | if (b < 0) | ||
4411 | return 1; | 4776 | return 1; |
4412 | 4777 | ||
4413 | if (tp_features.bright_16levels) | 4778 | if (tp_features.bright_16levels) |
@@ -4425,7 +4790,7 @@ static int __init brightness_init(struct ibm_init_struct *iibm) | |||
4425 | 4790 | ||
4426 | ibm_backlight_device->props.max_brightness = | 4791 | ibm_backlight_device->props.max_brightness = |
4427 | (tp_features.bright_16levels)? 15 : 7; | 4792 | (tp_features.bright_16levels)? 15 : 7; |
4428 | ibm_backlight_device->props.brightness = b; | 4793 | ibm_backlight_device->props.brightness = b & TP_EC_BACKLIGHT_LVLMSK; |
4429 | backlight_update_status(ibm_backlight_device); | 4794 | backlight_update_status(ibm_backlight_device); |
4430 | 4795 | ||
4431 | return 0; | 4796 | return 0; |
@@ -5046,11 +5411,11 @@ static void fan_watchdog_reset(void) | |||
5046 | if (fan_watchdog_maxinterval > 0 && | 5411 | if (fan_watchdog_maxinterval > 0 && |
5047 | tpacpi_lifecycle != TPACPI_LIFE_EXITING) { | 5412 | tpacpi_lifecycle != TPACPI_LIFE_EXITING) { |
5048 | fan_watchdog_active = 1; | 5413 | fan_watchdog_active = 1; |
5049 | if (!schedule_delayed_work(&fan_watchdog_task, | 5414 | if (!queue_delayed_work(tpacpi_wq, &fan_watchdog_task, |
5050 | msecs_to_jiffies(fan_watchdog_maxinterval | 5415 | msecs_to_jiffies(fan_watchdog_maxinterval |
5051 | * 1000))) { | 5416 | * 1000))) { |
5052 | printk(TPACPI_ERR | 5417 | printk(TPACPI_ERR |
5053 | "failed to schedule the fan watchdog, " | 5418 | "failed to queue the fan watchdog, " |
5054 | "watchdog will not trigger\n"); | 5419 | "watchdog will not trigger\n"); |
5055 | } | 5420 | } |
5056 | } else | 5421 | } else |
@@ -5420,7 +5785,7 @@ static void fan_exit(void) | |||
5420 | &driver_attr_fan_watchdog); | 5785 | &driver_attr_fan_watchdog); |
5421 | 5786 | ||
5422 | cancel_delayed_work(&fan_watchdog_task); | 5787 | cancel_delayed_work(&fan_watchdog_task); |
5423 | flush_scheduled_work(); | 5788 | flush_workqueue(tpacpi_wq); |
5424 | } | 5789 | } |
5425 | 5790 | ||
5426 | static int fan_read(char *p) | 5791 | static int fan_read(char *p) |
@@ -5826,10 +6191,13 @@ static void __init get_thinkpad_model_data(struct thinkpad_id_data *tp) | |||
5826 | 6191 | ||
5827 | tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), | 6192 | tp->model_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_VERSION), |
5828 | GFP_KERNEL); | 6193 | GFP_KERNEL); |
5829 | if (strnicmp(tp->model_str, "ThinkPad", 8) != 0) { | 6194 | if (tp->model_str && strnicmp(tp->model_str, "ThinkPad", 8) != 0) { |
5830 | kfree(tp->model_str); | 6195 | kfree(tp->model_str); |
5831 | tp->model_str = NULL; | 6196 | tp->model_str = NULL; |
5832 | } | 6197 | } |
6198 | |||
6199 | tp->nummodel_str = kstrdup(dmi_get_system_info(DMI_PRODUCT_NAME), | ||
6200 | GFP_KERNEL); | ||
5833 | } | 6201 | } |
5834 | 6202 | ||
5835 | static int __init probe_for_thinkpad(void) | 6203 | static int __init probe_for_thinkpad(void) |
@@ -6071,6 +6439,9 @@ static void thinkpad_acpi_module_exit(void) | |||
6071 | if (proc_dir) | 6439 | if (proc_dir) |
6072 | remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir); | 6440 | remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir); |
6073 | 6441 | ||
6442 | if (tpacpi_wq) | ||
6443 | destroy_workqueue(tpacpi_wq); | ||
6444 | |||
6074 | kfree(thinkpad_id.bios_version_str); | 6445 | kfree(thinkpad_id.bios_version_str); |
6075 | kfree(thinkpad_id.ec_version_str); | 6446 | kfree(thinkpad_id.ec_version_str); |
6076 | kfree(thinkpad_id.model_str); | 6447 | kfree(thinkpad_id.model_str); |
@@ -6101,6 +6472,12 @@ static int __init thinkpad_acpi_module_init(void) | |||
6101 | TPACPI_ACPIHANDLE_INIT(ecrd); | 6472 | TPACPI_ACPIHANDLE_INIT(ecrd); |
6102 | TPACPI_ACPIHANDLE_INIT(ecwr); | 6473 | TPACPI_ACPIHANDLE_INIT(ecwr); |
6103 | 6474 | ||
6475 | tpacpi_wq = create_singlethread_workqueue(TPACPI_WORKQUEUE_NAME); | ||
6476 | if (!tpacpi_wq) { | ||
6477 | thinkpad_acpi_module_exit(); | ||
6478 | return -ENOMEM; | ||
6479 | } | ||
6480 | |||
6104 | proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir); | 6481 | proc_dir = proc_mkdir(TPACPI_PROC_DIR, acpi_root_dir); |
6105 | if (!proc_dir) { | 6482 | if (!proc_dir) { |
6106 | printk(TPACPI_ERR | 6483 | printk(TPACPI_ERR |
@@ -6223,6 +6600,8 @@ static int __init thinkpad_acpi_module_init(void) | |||
6223 | /* Please remove this in year 2009 */ | 6600 | /* Please remove this in year 2009 */ |
6224 | MODULE_ALIAS("ibm_acpi"); | 6601 | MODULE_ALIAS("ibm_acpi"); |
6225 | 6602 | ||
6603 | MODULE_ALIAS(TPACPI_DRVR_SHORTNAME); | ||
6604 | |||
6226 | /* | 6605 | /* |
6227 | * DMI matching for module autoloading | 6606 | * DMI matching for module autoloading |
6228 | * | 6607 | * |