diff options
Diffstat (limited to 'drivers/acpi/video.c')
-rw-r--r-- | drivers/acpi/video.c | 80 |
1 files changed, 53 insertions, 27 deletions
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 60ea984c84a0..94b1a4c5abab 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c | |||
@@ -40,10 +40,12 @@ | |||
40 | #include <linux/pci.h> | 40 | #include <linux/pci.h> |
41 | #include <linux/pci_ids.h> | 41 | #include <linux/pci_ids.h> |
42 | #include <asm/uaccess.h> | 42 | #include <asm/uaccess.h> |
43 | 43 | #include <linux/dmi.h> | |
44 | #include <acpi/acpi_bus.h> | 44 | #include <acpi/acpi_bus.h> |
45 | #include <acpi/acpi_drivers.h> | 45 | #include <acpi/acpi_drivers.h> |
46 | 46 | ||
47 | #define PREFIX "ACPI: " | ||
48 | |||
47 | #define ACPI_VIDEO_CLASS "video" | 49 | #define ACPI_VIDEO_CLASS "video" |
48 | #define ACPI_VIDEO_BUS_NAME "Video Bus" | 50 | #define ACPI_VIDEO_BUS_NAME "Video Bus" |
49 | #define ACPI_VIDEO_DEVICE_NAME "Video Device" | 51 | #define ACPI_VIDEO_DEVICE_NAME "Video Device" |
@@ -198,7 +200,7 @@ struct acpi_video_device { | |||
198 | struct acpi_device *dev; | 200 | struct acpi_device *dev; |
199 | struct acpi_video_device_brightness *brightness; | 201 | struct acpi_video_device_brightness *brightness; |
200 | struct backlight_device *backlight; | 202 | struct backlight_device *backlight; |
201 | struct thermal_cooling_device *cdev; | 203 | struct thermal_cooling_device *cooling_dev; |
202 | struct output_device *output_dev; | 204 | struct output_device *output_dev; |
203 | }; | 205 | }; |
204 | 206 | ||
@@ -387,20 +389,20 @@ static struct output_properties acpi_output_properties = { | |||
387 | 389 | ||
388 | 390 | ||
389 | /* thermal cooling device callbacks */ | 391 | /* thermal cooling device callbacks */ |
390 | static int video_get_max_state(struct thermal_cooling_device *cdev, unsigned | 392 | static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned |
391 | long *state) | 393 | long *state) |
392 | { | 394 | { |
393 | struct acpi_device *device = cdev->devdata; | 395 | struct acpi_device *device = cooling_dev->devdata; |
394 | struct acpi_video_device *video = acpi_driver_data(device); | 396 | struct acpi_video_device *video = acpi_driver_data(device); |
395 | 397 | ||
396 | *state = video->brightness->count - 3; | 398 | *state = video->brightness->count - 3; |
397 | return 0; | 399 | return 0; |
398 | } | 400 | } |
399 | 401 | ||
400 | static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned | 402 | static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned |
401 | long *state) | 403 | long *state) |
402 | { | 404 | { |
403 | struct acpi_device *device = cdev->devdata; | 405 | struct acpi_device *device = cooling_dev->devdata; |
404 | struct acpi_video_device *video = acpi_driver_data(device); | 406 | struct acpi_video_device *video = acpi_driver_data(device); |
405 | unsigned long long level; | 407 | unsigned long long level; |
406 | int offset; | 408 | int offset; |
@@ -417,9 +419,9 @@ static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned | |||
417 | } | 419 | } |
418 | 420 | ||
419 | static int | 421 | static int |
420 | video_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) | 422 | video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state) |
421 | { | 423 | { |
422 | struct acpi_device *device = cdev->devdata; | 424 | struct acpi_device *device = cooling_dev->devdata; |
423 | struct acpi_video_device *video = acpi_driver_data(device); | 425 | struct acpi_video_device *video = acpi_driver_data(device); |
424 | int level; | 426 | int level; |
425 | 427 | ||
@@ -603,6 +605,7 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, | |||
603 | unsigned long long *level) | 605 | unsigned long long *level) |
604 | { | 606 | { |
605 | acpi_status status = AE_OK; | 607 | acpi_status status = AE_OK; |
608 | int i; | ||
606 | 609 | ||
607 | if (device->cap._BQC || device->cap._BCQ) { | 610 | if (device->cap._BQC || device->cap._BCQ) { |
608 | char *buf = device->cap._BQC ? "_BQC" : "_BCQ"; | 611 | char *buf = device->cap._BQC ? "_BQC" : "_BCQ"; |
@@ -618,8 +621,15 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, | |||
618 | 621 | ||
619 | } | 622 | } |
620 | *level += bqc_offset_aml_bug_workaround; | 623 | *level += bqc_offset_aml_bug_workaround; |
621 | device->brightness->curr = *level; | 624 | for (i = 2; i < device->brightness->count; i++) |
622 | return 0; | 625 | if (device->brightness->levels[i] == *level) { |
626 | device->brightness->curr = *level; | ||
627 | return 0; | ||
628 | } | ||
629 | /* BQC returned an invalid level. Stop using it. */ | ||
630 | ACPI_WARNING((AE_INFO, "%s returned an invalid level", | ||
631 | buf)); | ||
632 | device->cap._BQC = device->cap._BCQ = 0; | ||
623 | } else { | 633 | } else { |
624 | /* Fixme: | 634 | /* Fixme: |
625 | * should we return an error or ignore this failure? | 635 | * should we return an error or ignore this failure? |
@@ -870,7 +880,7 @@ acpi_video_init_brightness(struct acpi_video_device *device) | |||
870 | br->flags._BCM_use_index = br->flags._BCL_use_index; | 880 | br->flags._BCM_use_index = br->flags._BCL_use_index; |
871 | 881 | ||
872 | /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ | 882 | /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ |
873 | br->curr = level_old = max_level; | 883 | br->curr = level = max_level; |
874 | 884 | ||
875 | if (!device->cap._BQC) | 885 | if (!device->cap._BQC) |
876 | goto set_level; | 886 | goto set_level; |
@@ -892,15 +902,25 @@ acpi_video_init_brightness(struct acpi_video_device *device) | |||
892 | 902 | ||
893 | br->flags._BQC_use_index = (level == max_level ? 0 : 1); | 903 | br->flags._BQC_use_index = (level == max_level ? 0 : 1); |
894 | 904 | ||
895 | if (!br->flags._BQC_use_index) | 905 | if (!br->flags._BQC_use_index) { |
906 | /* | ||
907 | * Set the backlight to the initial state. | ||
908 | * On some buggy laptops, _BQC returns an uninitialized value | ||
909 | * when invoked for the first time, i.e. level_old is invalid. | ||
910 | * set the backlight to max_level in this case | ||
911 | */ | ||
912 | for (i = 2; i < br->count; i++) | ||
913 | if (level_old == br->levels[i]) | ||
914 | level = level_old; | ||
896 | goto set_level; | 915 | goto set_level; |
916 | } | ||
897 | 917 | ||
898 | if (br->flags._BCL_reversed) | 918 | if (br->flags._BCL_reversed) |
899 | level_old = (br->count - 1) - level_old; | 919 | level_old = (br->count - 1) - level_old; |
900 | level_old = br->levels[level_old]; | 920 | level = br->levels[level_old]; |
901 | 921 | ||
902 | set_level: | 922 | set_level: |
903 | result = acpi_video_device_lcd_set_level(device, level_old); | 923 | result = acpi_video_device_lcd_set_level(device, level); |
904 | if (result) | 924 | if (result) |
905 | goto out_free_levels; | 925 | goto out_free_levels; |
906 | 926 | ||
@@ -934,9 +954,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) | |||
934 | { | 954 | { |
935 | acpi_handle h_dummy1; | 955 | acpi_handle h_dummy1; |
936 | 956 | ||
937 | |||
938 | memset(&device->cap, 0, sizeof(device->cap)); | ||
939 | |||
940 | if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) { | 957 | if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) { |
941 | device->cap._ADR = 1; | 958 | device->cap._ADR = 1; |
942 | } | 959 | } |
@@ -990,19 +1007,29 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) | |||
990 | if (result) | 1007 | if (result) |
991 | printk(KERN_ERR PREFIX "Create sysfs link\n"); | 1008 | printk(KERN_ERR PREFIX "Create sysfs link\n"); |
992 | 1009 | ||
993 | device->cdev = thermal_cooling_device_register("LCD", | 1010 | device->cooling_dev = thermal_cooling_device_register("LCD", |
994 | device->dev, &video_cooling_ops); | 1011 | device->dev, &video_cooling_ops); |
995 | if (IS_ERR(device->cdev)) | 1012 | if (IS_ERR(device->cooling_dev)) { |
1013 | /* | ||
1014 | * Set cooling_dev to NULL so we don't crash trying to | ||
1015 | * free it. | ||
1016 | * Also, why the hell we are returning early and | ||
1017 | * not attempt to register video output if cooling | ||
1018 | * device registration failed? | ||
1019 | * -- dtor | ||
1020 | */ | ||
1021 | device->cooling_dev = NULL; | ||
996 | return; | 1022 | return; |
1023 | } | ||
997 | 1024 | ||
998 | dev_info(&device->dev->dev, "registered as cooling_device%d\n", | 1025 | dev_info(&device->dev->dev, "registered as cooling_device%d\n", |
999 | device->cdev->id); | 1026 | device->cooling_dev->id); |
1000 | result = sysfs_create_link(&device->dev->dev.kobj, | 1027 | result = sysfs_create_link(&device->dev->dev.kobj, |
1001 | &device->cdev->device.kobj, | 1028 | &device->cooling_dev->device.kobj, |
1002 | "thermal_cooling"); | 1029 | "thermal_cooling"); |
1003 | if (result) | 1030 | if (result) |
1004 | printk(KERN_ERR PREFIX "Create sysfs link\n"); | 1031 | printk(KERN_ERR PREFIX "Create sysfs link\n"); |
1005 | result = sysfs_create_link(&device->cdev->device.kobj, | 1032 | result = sysfs_create_link(&device->cooling_dev->device.kobj, |
1006 | &device->dev->dev.kobj, "device"); | 1033 | &device->dev->dev.kobj, "device"); |
1007 | if (result) | 1034 | if (result) |
1008 | printk(KERN_ERR PREFIX "Create sysfs link\n"); | 1035 | printk(KERN_ERR PREFIX "Create sysfs link\n"); |
@@ -1039,7 +1066,6 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video) | |||
1039 | { | 1066 | { |
1040 | acpi_handle h_dummy1; | 1067 | acpi_handle h_dummy1; |
1041 | 1068 | ||
1042 | memset(&video->cap, 0, sizeof(video->cap)); | ||
1043 | if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) { | 1069 | if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) { |
1044 | video->cap._DOS = 1; | 1070 | video->cap._DOS = 1; |
1045 | } | 1071 | } |
@@ -2009,13 +2035,13 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) | |||
2009 | backlight_device_unregister(device->backlight); | 2035 | backlight_device_unregister(device->backlight); |
2010 | device->backlight = NULL; | 2036 | device->backlight = NULL; |
2011 | } | 2037 | } |
2012 | if (device->cdev) { | 2038 | if (device->cooling_dev) { |
2013 | sysfs_remove_link(&device->dev->dev.kobj, | 2039 | sysfs_remove_link(&device->dev->dev.kobj, |
2014 | "thermal_cooling"); | 2040 | "thermal_cooling"); |
2015 | sysfs_remove_link(&device->cdev->device.kobj, | 2041 | sysfs_remove_link(&device->cooling_dev->device.kobj, |
2016 | "device"); | 2042 | "device"); |
2017 | thermal_cooling_device_unregister(device->cdev); | 2043 | thermal_cooling_device_unregister(device->cooling_dev); |
2018 | device->cdev = NULL; | 2044 | device->cooling_dev = NULL; |
2019 | } | 2045 | } |
2020 | video_output_unregister(device->output_dev); | 2046 | video_output_unregister(device->output_dev); |
2021 | 2047 | ||