Merge remote-tracking branches 'regulator/fix/as3722', 'regulator/fix/ltc3589' and...
[cascardo/linux.git] / drivers / acpi / video.c
index f8bc5a7..fb9ffe9 100644 (file)
@@ -68,7 +68,7 @@ MODULE_AUTHOR("Bruno Ducrot");
 MODULE_DESCRIPTION("ACPI Video Driver");
 MODULE_LICENSE("GPL");
 
-static bool brightness_switch_enabled = 1;
+static bool brightness_switch_enabled;
 module_param(brightness_switch_enabled, bool, 0644);
 
 /*
@@ -82,7 +82,7 @@ module_param(allow_duplicates, bool, 0644);
  * For Windows 8 systems: used to decide if video module
  * should skip registering backlight interface of its own.
  */
-static int use_native_backlight_param = -1;
+static int use_native_backlight_param = 1;
 module_param_named(use_native_backlight, use_native_backlight_param, int, 0444);
 static bool use_native_backlight_dmi = false;
 
@@ -150,6 +150,8 @@ struct acpi_video_enumerated_device {
 
 struct acpi_video_bus {
        struct acpi_device *device;
+       bool backlight_registered;
+       bool backlight_notifier_registered;
        u8 dos_setting;
        struct acpi_video_enumerated_device *attached_array;
        u8 attached_count;
@@ -161,6 +163,7 @@ struct acpi_video_bus {
        struct input_dev *input;
        char phys[32];  /* for input device */
        struct notifier_block pm_nb;
+       struct notifier_block backlight_nb;
 };
 
 struct acpi_video_device_flags {
@@ -471,6 +474,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X230"),
                },
        },
+       {
+        .callback = video_set_use_native_backlight,
+        .ident = "ThinkPad W530",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W530"),
+               },
+       },
        {
         .callback = video_set_use_native_backlight,
        .ident = "ThinkPad X1 Carbon",
@@ -487,6 +498,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Yoga 13"),
                },
        },
+       {
+        .callback = video_set_use_native_backlight,
+        .ident = "Lenovo Yoga 2 11",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+               DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"),
+               },
+       },
        {
        .callback = video_set_use_native_backlight,
        .ident = "Thinkpad Helix",
@@ -519,6 +538,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5742G"),
                },
        },
+       {
+        .callback = video_set_use_native_backlight,
+        .ident = "Acer Aspire V5-171",
+        .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "V5-171"),
+               },
+       },
        {
         .callback = video_set_use_native_backlight,
         .ident = "Acer Aspire V5-431",
@@ -527,6 +554,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
                DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-431"),
                },
        },
+       {
+        .callback = video_set_use_native_backlight,
+        .ident = "Acer Aspire V5-471G",
+        .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-471G"),
+               },
+       },
        {
        .callback = video_set_use_native_backlight,
        .ident = "HP ProBook 4340s",
@@ -579,6 +614,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
        },
        {
        .callback = video_set_use_native_backlight,
+       .ident = "HP EliteBook 8470p",
+       .matches = {
+               DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8470p"),
+               },
+       },
+       {
+       .callback = video_set_use_native_backlight,
        .ident = "HP EliteBook 8780w",
        .matches = {
                DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
@@ -1658,88 +1701,92 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context,
 
 static void acpi_video_dev_register_backlight(struct acpi_video_device *device)
 {
-       if (acpi_video_verify_backlight_support()) {
-               struct backlight_properties props;
-               struct pci_dev *pdev;
-               acpi_handle acpi_parent;
-               struct device *parent = NULL;
-               int result;
-               static int count;
-               char *name;
-
-               result = acpi_video_init_brightness(device);
-               if (result)
-                       return;
-               name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
-               if (!name)
-                       return;
-               count++;
+       struct backlight_properties props;
+       struct pci_dev *pdev;
+       acpi_handle acpi_parent;
+       struct device *parent = NULL;
+       int result;
+       static int count;
+       char *name;
 
-               acpi_get_parent(device->dev->handle, &acpi_parent);
+       result = acpi_video_init_brightness(device);
+       if (result)
+               return;
+       name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
+       if (!name)
+               return;
+       count++;
 
-               pdev = acpi_get_pci_dev(acpi_parent);
-               if (pdev) {
-                       parent = &pdev->dev;
-                       pci_dev_put(pdev);
-               }
+       acpi_get_parent(device->dev->handle, &acpi_parent);
 
-               memset(&props, 0, sizeof(struct backlight_properties));
-               props.type = BACKLIGHT_FIRMWARE;
-               props.max_brightness = device->brightness->count - 3;
-               device->backlight = backlight_device_register(name,
-                                                             parent,
-                                                             device,
-                                                             &acpi_backlight_ops,
-                                                             &props);
-               kfree(name);
-               if (IS_ERR(device->backlight))
-                       return;
+       pdev = acpi_get_pci_dev(acpi_parent);
+       if (pdev) {
+               parent = &pdev->dev;
+               pci_dev_put(pdev);
+       }
 
-               /*
-                * 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);
+       memset(&props, 0, sizeof(struct backlight_properties));
+       props.type = BACKLIGHT_FIRMWARE;
+       props.max_brightness = device->brightness->count - 3;
+       device->backlight = backlight_device_register(name,
+                                                     parent,
+                                                     device,
+                                                     &acpi_backlight_ops,
+                                                     &props);
+       kfree(name);
+       if (IS_ERR(device->backlight))
+               return;
 
-               device->cooling_dev = thermal_cooling_device_register("LCD",
-                                       device->dev, &video_cooling_ops);
-               if (IS_ERR(device->cooling_dev)) {
-                       /*
-                        * Set cooling_dev to NULL so we don't crash trying to
-                        * free it.
-                        * Also, why the hell we are returning early and
-                        * not attempt to register video output if cooling
-                        * device registration failed?
-                        * -- dtor
-                        */
-                       device->cooling_dev = NULL;
-                       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);
 
-               dev_info(&device->dev->dev, "registered as cooling_device%d\n",
-                        device->cooling_dev->id);
-               result = sysfs_create_link(&device->dev->dev.kobj,
-                               &device->cooling_dev->device.kobj,
-                               "thermal_cooling");
-               if (result)
-                       printk(KERN_ERR PREFIX "Create sysfs link\n");
-               result = sysfs_create_link(&device->cooling_dev->device.kobj,
-                               &device->dev->dev.kobj, "device");
-               if (result)
-                       printk(KERN_ERR PREFIX "Create sysfs link\n");
+       device->cooling_dev = thermal_cooling_device_register("LCD",
+                               device->dev, &video_cooling_ops);
+       if (IS_ERR(device->cooling_dev)) {
+               /*
+                * Set cooling_dev to NULL so we don't crash trying to free it.
+                * Also, why the hell we are returning early and not attempt to
+                * register video output if cooling device registration failed?
+                * -- dtor
+                */
+               device->cooling_dev = NULL;
+               return;
        }
+
+       dev_info(&device->dev->dev, "registered as cooling_device%d\n",
+                device->cooling_dev->id);
+       result = sysfs_create_link(&device->dev->dev.kobj,
+                       &device->cooling_dev->device.kobj,
+                       "thermal_cooling");
+       if (result)
+               printk(KERN_ERR PREFIX "Create sysfs link\n");
+       result = sysfs_create_link(&device->cooling_dev->device.kobj,
+                       &device->dev->dev.kobj, "device");
+       if (result)
+               printk(KERN_ERR PREFIX "Create sysfs link\n");
 }
 
 static int acpi_video_bus_register_backlight(struct acpi_video_bus *video)
 {
        struct acpi_video_device *dev;
 
+       if (video->backlight_registered)
+               return 0;
+
+       if (!acpi_video_verify_backlight_support())
+               return 0;
+
        mutex_lock(&video->device_list_lock);
        list_for_each_entry(dev, &video->video_device_list, entry)
                acpi_video_dev_register_backlight(dev);
        mutex_unlock(&video->device_list_lock);
 
+       video->backlight_registered = true;
+
        video->pm_nb.notifier_call = acpi_video_resume;
        video->pm_nb.priority = 0;
        return register_pm_notifier(&video->pm_nb);
@@ -1767,13 +1814,20 @@ static void acpi_video_dev_unregister_backlight(struct acpi_video_device *device
 static int acpi_video_bus_unregister_backlight(struct acpi_video_bus *video)
 {
        struct acpi_video_device *dev;
-       int error = unregister_pm_notifier(&video->pm_nb);
+       int error;
+
+       if (!video->backlight_registered)
+               return 0;
+
+       error = unregister_pm_notifier(&video->pm_nb);
 
        mutex_lock(&video->device_list_lock);
        list_for_each_entry(dev, &video->video_device_list, entry)
                acpi_video_dev_unregister_backlight(dev);
        mutex_unlock(&video->device_list_lock);
 
+       video->backlight_registered = false;
+
        return error;
 }
 
@@ -1867,6 +1921,56 @@ static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video)
        video->input = NULL;
 }
 
+static int acpi_video_backlight_notify(struct notifier_block *nb,
+                                       unsigned long val, void *bd)
+{
+       struct backlight_device *backlight = bd;
+       struct acpi_video_bus *video;
+
+       /* acpi_video_verify_backlight_support only cares about raw devices */
+       if (backlight->props.type != BACKLIGHT_RAW)
+               return NOTIFY_DONE;
+
+       video = container_of(nb, struct acpi_video_bus, backlight_nb);
+
+       switch (val) {
+       case BACKLIGHT_REGISTERED:
+               if (!acpi_video_verify_backlight_support())
+                       acpi_video_bus_unregister_backlight(video);
+               break;
+       case BACKLIGHT_UNREGISTERED:
+               acpi_video_bus_register_backlight(video);
+               break;
+       }
+
+       return NOTIFY_OK;
+}
+
+static int acpi_video_bus_add_backlight_notify_handler(
+                                               struct acpi_video_bus *video)
+{
+       int error;
+
+       video->backlight_nb.notifier_call = acpi_video_backlight_notify;
+       video->backlight_nb.priority = 0;
+       error = backlight_register_notifier(&video->backlight_nb);
+       if (error == 0)
+               video->backlight_notifier_registered = true;
+
+       return error;
+}
+
+static int acpi_video_bus_remove_backlight_notify_handler(
+                                               struct acpi_video_bus *video)
+{
+       if (!video->backlight_notifier_registered)
+               return 0;
+
+       video->backlight_notifier_registered = false;
+
+       return backlight_unregister_notifier(&video->backlight_nb);
+}
+
 static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
 {
        struct acpi_video_device *dev, *next;
@@ -1948,6 +2052,7 @@ static int acpi_video_bus_add(struct acpi_device *device)
 
        acpi_video_bus_register_backlight(video);
        acpi_video_bus_add_notify_handler(video);
+       acpi_video_bus_add_backlight_notify_handler(video);
 
        return 0;
 
@@ -1971,6 +2076,7 @@ static int acpi_video_bus_remove(struct acpi_device *device)
 
        video = acpi_driver_data(device);
 
+       acpi_video_bus_remove_backlight_notify_handler(video);
        acpi_video_bus_remove_notify_handler(video);
        acpi_video_bus_unregister_backlight(video);
        acpi_video_bus_put_devices(video);
@@ -2061,6 +2167,20 @@ void acpi_video_unregister(void)
 }
 EXPORT_SYMBOL(acpi_video_unregister);
 
+void acpi_video_unregister_backlight(void)
+{
+       struct acpi_video_bus *video;
+
+       if (!register_count)
+               return;
+
+       mutex_lock(&video_list_lock);
+       list_for_each_entry(video, &video_bus_head, entry)
+               acpi_video_bus_unregister_backlight(video);
+       mutex_unlock(&video_list_lock);
+}
+EXPORT_SYMBOL(acpi_video_unregister_backlight);
+
 /*
  * This is kind of nasty. Hardware using Intel chipsets may require
  * the video opregion code to be run first in order to initialise