ACPI / PM: Move ACPI video resume to a PM notifier
authorRafael J. Wysocki <rjw@sisk.pl>
Sun, 4 Apr 2010 23:43:51 +0000 (01:43 +0200)
committerLen Brown <len.brown@intel.com>
Tue, 6 Apr 2010 21:02:00 +0000 (17:02 -0400)
There is a problem with the ACPI video resume routine that it's
executed before the GPU that may be accessed by it.  To fix this
issue, move the ACPI video resume to a power management notifier,
so that's executed after resuming all devices, including the GPU.

Fixes https://bugzilla.kernel.org/show_bug.cgi?id=15096, which is
a listed regression from 2.6.31.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Tested-by: Rafał Miłecki <zajec5@gmail.com>
Acked-by: Matthew Garrett <mjg@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>
drivers/acpi/video.c

index 6a0143796772df266d6eb5c2d8498b96330070bf..416eb0303a8337b885189b601ed52a35126c313e 100644 (file)
@@ -44,6 +44,7 @@
 #include <linux/dmi.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
+#include <linux/suspend.h>
 
 #define PREFIX "ACPI: "
 
@@ -89,7 +90,6 @@ module_param(allow_duplicates, bool, 0644);
 static int register_count = 0;
 static int acpi_video_bus_add(struct acpi_device *device);
 static int acpi_video_bus_remove(struct acpi_device *device, int type);
-static int acpi_video_resume(struct acpi_device *device);
 static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
 
 static const struct acpi_device_id video_device_ids[] = {
@@ -105,7 +105,6 @@ static struct acpi_driver acpi_video_bus = {
        .ops = {
                .add = acpi_video_bus_add,
                .remove = acpi_video_bus_remove,
-               .resume = acpi_video_resume,
                .notify = acpi_video_bus_notify,
                },
 };
@@ -160,6 +159,7 @@ struct acpi_video_bus {
        struct proc_dir_entry *dir;
        struct input_dev *input;
        char phys[32];  /* for input device */
+       struct notifier_block pm_nb;
 };
 
 struct acpi_video_device_flags {
@@ -1021,6 +1021,13 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                if (IS_ERR(device->backlight))
                        return;
 
+               /*
+                * Save current brightness level in case we have to restore it
+                * before acpi_video_device_lcd_set_level() is called next time.
+                */
+               device->backlight->props.brightness =
+                               acpi_video_get_brightness(device->backlight);
+
                result = sysfs_create_link(&device->backlight->dev.kobj,
                                           &device->dev->dev.kobj, "device");
                if (result)
@@ -2236,24 +2243,31 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
        return;
 }
 
-static int instance;
-static int acpi_video_resume(struct acpi_device *device)
+static int acpi_video_resume(struct notifier_block *nb,
+                               unsigned long val, void *ign)
 {
        struct acpi_video_bus *video;
        struct acpi_video_device *video_device;
        int i;
 
-       if (!device || !acpi_driver_data(device))
-               return -EINVAL;
+       switch (val) {
+       case PM_HIBERNATION_PREPARE:
+       case PM_SUSPEND_PREPARE:
+       case PM_RESTORE_PREPARE:
+               return NOTIFY_DONE;
+       }
 
-       video = acpi_driver_data(device);
+       video = container_of(nb, struct acpi_video_bus, pm_nb);
+
+       dev_info(&video->device->dev, "Restoring backlight state\n");
 
        for (i = 0; i < video->attached_count; i++) {
                video_device = video->attached_array[i].bind_info;
                if (video_device && video_device->backlight)
                        acpi_video_set_brightness(video_device->backlight);
        }
-       return AE_OK;
+
+       return NOTIFY_OK;
 }
 
 static acpi_status
@@ -2277,6 +2291,8 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context,
        return AE_OK;
 }
 
+static int instance;
+
 static int acpi_video_bus_add(struct acpi_device *device)
 {
        struct acpi_video_bus *video;
@@ -2370,6 +2386,10 @@ static int acpi_video_bus_add(struct acpi_device *device)
               video->flags.rom ? "yes" : "no",
               video->flags.post ? "yes" : "no");
 
+       video->pm_nb.notifier_call = acpi_video_resume;
+       video->pm_nb.priority = 0;
+       register_pm_notifier(&video->pm_nb);
+
        return 0;
 
  err_free_input_dev:
@@ -2396,6 +2416,8 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type)
 
        video = acpi_driver_data(device);
 
+       unregister_pm_notifier(&video->pm_nb);
+
        acpi_video_bus_stop_devices(video);
        acpi_video_bus_put_devices(video);
        acpi_video_bus_remove_fs(device);