]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/acpi/video.c
Merge remote-tracking branch 'net-next/master'
[karo-tx-linux.git] / drivers / acpi / video.c
index 3270d3c8ba4ed239d25b0507882fa50df3abb5e0..d020df5a732ab1109d9fd3ddcaa9c7ff871b192b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  video.c - ACPI Video Driver ($Revision:$)
+ *  video.c - ACPI Video Driver
  *
  *  Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
  *  Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
@@ -88,7 +88,16 @@ module_param(allow_duplicates, bool, 0644);
 static bool use_bios_initial_backlight = 1;
 module_param(use_bios_initial_backlight, bool, 0644);
 
-static int register_count = 0;
+/*
+ * For Windows 8 systems: if set ture and the GPU driver has
+ * registered a backlight interface, skip registering ACPI video's.
+ */
+static bool use_native_backlight = false;
+module_param(use_native_backlight, bool, 0644);
+
+static int register_count;
+static struct mutex video_list_lock;
+static struct list_head video_bus_head;
 static int acpi_video_bus_add(struct acpi_device *device);
 static int acpi_video_bus_remove(struct acpi_device *device);
 static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
@@ -118,26 +127,26 @@ struct acpi_video_bus_flags {
 };
 
 struct acpi_video_bus_cap {
-       u8 _DOS:1;              /*Enable/Disable output switching */
-       u8 _DOD:1;              /*Enumerate all devices attached to display adapter */
-       u8 _ROM:1;              /*Get ROM Data */
-       u8 _GPD:1;              /*Get POST Device */
-       u8 _SPD:1;              /*Set POST Device */
-       u8 _VPO:1;              /*Video POST Options */
+       u8 _DOS:1;              /* Enable/Disable output switching */
+       u8 _DOD:1;              /* Enumerate all devices attached to display adapter */
+       u8 _ROM:1;              /* Get ROM Data */
+       u8 _GPD:1;              /* Get POST Device */
+       u8 _SPD:1;              /* Set POST Device */
+       u8 _VPO:1;              /* Video POST Options */
        u8 reserved:2;
 };
 
 struct acpi_video_device_attrib {
        u32 display_index:4;    /* A zero-based instance of the Display */
-       u32 display_port_attachment:4;  /*This field differentiates the display type */
-       u32 display_type:4;     /*Describe the specific type in use */
-       u32 vendor_specific:4;  /*Chipset Vendor Specific */
-       u32 bios_can_detect:1;  /*BIOS can detect the device */
-       u32 depend_on_vga:1;    /*Non-VGA output device whose power is related to 
+       u32 display_port_attachment:4;  /* This field differentiates the display type */
+       u32 display_type:4;     /* Describe the specific type in use */
+       u32 vendor_specific:4;  /* Chipset Vendor Specific */
+       u32 bios_can_detect:1;  /* BIOS can detect the device */
+       u32 depend_on_vga:1;    /* Non-VGA output device whose power is related to
                                   the VGA device. */
-       u32 pipe_id:3;          /*For VGA multiple-head devices. */
-       u32 reserved:10;        /*Must be 0 */
-       u32 device_id_scheme:1; /*Device ID Scheme */
+       u32 pipe_id:3;          /* For VGA multiple-head devices. */
+       u32 reserved:10;        /* Must be 0 */
+       u32 device_id_scheme:1; /* Device ID Scheme */
 };
 
 struct acpi_video_enumerated_device {
@@ -157,6 +166,7 @@ struct acpi_video_bus {
        struct acpi_video_bus_flags flags;
        struct list_head video_device_list;
        struct mutex device_list_lock;  /* protects video_device_list */
+       struct list_head entry;
        struct input_dev *input;
        char phys[32];  /* for input device */
        struct notifier_block pm_nb;
@@ -174,19 +184,17 @@ struct acpi_video_device_flags {
 };
 
 struct acpi_video_device_cap {
-       u8 _ADR:1;              /*Return the unique ID */
-       u8 _BCL:1;              /*Query list of brightness control levels supported */
-       u8 _BCM:1;              /*Set the brightness level */
+       u8 _ADR:1;              /* Return the unique ID */
+       u8 _BCL:1;              /* Query list of brightness control levels supported */
+       u8 _BCM:1;              /* Set the brightness level */
        u8 _BQC:1;              /* Get current brightness level */
        u8 _BCQ:1;              /* Some buggy BIOS uses _BCQ instead of _BQC */
-       u8 _DDC:1;              /*Return the EDID for this device */
+       u8 _DDC:1;              /* Return the EDID for this device */
 };
 
 struct acpi_video_brightness_flags {
        u8 _BCL_no_ac_battery_levels:1; /* no AC/Battery levels in _BCL */
-       u8 _BCL_reversed:1;             /* _BCL package is in a reversed order*/
-       u8 _BCL_use_index:1;            /* levels in _BCL are index values */
-       u8 _BCM_use_index:1;            /* input of _BCM is an index value */
+       u8 _BCL_reversed:1;             /* _BCL package is in a reversed order */
        u8 _BQC_use_index:1;            /* _BQC returns an index value */
 };
 
@@ -231,21 +239,30 @@ static int acpi_video_get_next_level(struct acpi_video_device *device,
 static int acpi_video_switch_brightness(struct acpi_video_device *device,
                                         int event);
 
-/*backlight device sysfs support*/
+static bool acpi_video_verify_backlight_support(void)
+{
+       if (acpi_osi_is_win8() && use_native_backlight &&
+           backlight_device_registered(BACKLIGHT_RAW))
+               return false;
+       return acpi_video_backlight_support();
+}
+
+/* backlight device sysfs support */
 static int acpi_video_get_brightness(struct backlight_device *bd)
 {
        unsigned long long cur_level;
        int i;
-       struct acpi_video_device *vd =
-               (struct acpi_video_device *)bl_get_data(bd);
+       struct acpi_video_device *vd = bl_get_data(bd);
 
        if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false))
                return -EINVAL;
        for (i = 2; i < vd->brightness->count; i++) {
                if (vd->brightness->levels[i] == cur_level)
-                       /* The first two entries are special - see page 575
-                          of the ACPI spec 3.0 */
-                       return i-2;
+                       /*
+                        * The first two entries are special - see page 575
+                        * of the ACPI spec 3.0
+                        */
+                       return i - 2;
        }
        return 0;
 }
@@ -253,8 +270,7 @@ static int acpi_video_get_brightness(struct backlight_device *bd)
 static int acpi_video_set_brightness(struct backlight_device *bd)
 {
        int request_level = bd->props.brightness + 2;
-       struct acpi_video_device *vd =
-               (struct acpi_video_device *)bl_get_data(bd);
+       struct acpi_video_device *vd = bl_get_data(bd);
 
        return acpi_video_device_lcd_set_level(vd,
                                vd->brightness->levels[request_level]);
@@ -302,11 +318,11 @@ video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long st
        struct acpi_video_device *video = acpi_driver_data(device);
        int level;
 
-       if ( state >= video->brightness->count - 2)
+       if (state >= video->brightness->count - 2)
                return -EINVAL;
 
        state = video->brightness->count - state;
-       level = video->brightness->levels[state -1];
+       level = video->brightness->levels[state - 1];
        return acpi_video_device_lcd_set_level(video, level);
 }
 
@@ -316,9 +332,11 @@ static const struct thermal_cooling_device_ops video_cooling_ops = {
        .set_cur_state = video_set_cur_state,
 };
 
-/* --------------------------------------------------------------------------
-                               Video Management
-   -------------------------------------------------------------------------- */
+/*
+ * --------------------------------------------------------------------------
+ *                             Video Management
+ * --------------------------------------------------------------------------
+ */
 
 static int
 acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
@@ -345,7 +363,7 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
 
        return 0;
 
-      err:
+err:
        kfree(buffer.pointer);
 
        return status;
@@ -355,14 +373,10 @@ static int
 acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
 {
        int status;
-       union acpi_object arg0 = { ACPI_TYPE_INTEGER };
-       struct acpi_object_list args = { 1, &arg0 };
        int state;
 
-       arg0.integer.value = level;
-
-       status = acpi_evaluate_object(device->dev->handle, "_BCM",
-                                     &args, NULL);
+       status = acpi_execute_simple_method(device->dev->handle,
+                                           "_BCM", level);
        if (ACPI_FAILURE(status)) {
                ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
                return -EIO;
@@ -546,7 +560,7 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
                                if (device->brightness->levels[i] == *level) {
                                        device->brightness->curr = *level;
                                        return 0;
-                       }
+                               }
                        /*
                         * BQC returned an invalid level.
                         * Stop using it.
@@ -556,7 +570,8 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
                                      buf));
                        device->cap._BQC = device->cap._BCQ = 0;
                } else {
-                       /* Fixme:
+                       /*
+                        * Fixme:
                         * should we return an error or ignore this failure?
                         * dev->brightness->curr is a cached value which stores
                         * the correct current backlight level in most cases.
@@ -615,8 +630,8 @@ acpi_video_device_EDID(struct acpi_video_device *device,
 
 /*
  *  Arg:
- *     video           : video bus device pointer
- *     bios_flag       : 
+ *     video           : video bus device pointer
+ *     bios_flag       :
  *             0.      The system BIOS should NOT automatically switch(toggle)
  *                     the active display output.
  *             1.      The system BIOS should automatically switch (toggle) the
@@ -628,9 +643,9 @@ acpi_video_device_EDID(struct acpi_video_device *device,
  *     lcd_flag        :
  *             0.      The system BIOS should automatically control the brightness level
  *                     of the LCD when the power changes from AC to DC
- *             1.      The system BIOS should NOT automatically control the brightness 
+ *             1.      The system BIOS should NOT automatically control the brightness
  *                     level of the LCD when the power changes from AC to DC.
- * Return Value:
+ *  Return Value:
  *             -EINVAL wrong arg.
  */
 
@@ -638,18 +653,15 @@ static int
 acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
 {
        acpi_status status;
-       union acpi_object arg0 = { ACPI_TYPE_INTEGER };
-       struct acpi_object_list args = { 1, &arg0 };
 
        if (!video->cap._DOS)
                return 0;
 
        if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1)
                return -EINVAL;
-       arg0.integer.value = (lcd_flag << 2) | bios_flag;
-       video->dos_setting = arg0.integer.value;
-       status = acpi_evaluate_object(video->device->handle, "_DOS",
-               &args, NULL);
+       video->dos_setting = (lcd_flag << 2) | bios_flag;
+       status = acpi_execute_simple_method(video->device->handle, "_DOS",
+                                           (lcd_flag << 2) | bios_flag);
        if (ACPI_FAILURE(status))
                return -EIO;
 
@@ -717,8 +729,8 @@ static int acpi_video_bqc_quirk(struct acpi_video_device *device,
 
 
 /*
- *  Arg:       
- *     device  : video output device (LCD, CRT, ..)
+ *  Arg:
+ *     device  : video output device (LCD, CRT, ..)
  *
  *  Return Value:
  *     Maximum brightness level
@@ -806,16 +818,6 @@ acpi_video_init_brightness(struct acpi_video_device *device)
        br->count = count;
        device->brightness = br;
 
-       /* Check the input/output of _BQC/_BCL/_BCM */
-       if ((max_level < 100) && (max_level <= (count - 2)))
-               br->flags._BCL_use_index = 1;
-
-       /*
-        * _BCM is always consistent with _BCL,
-        * at least for all the laptops we have ever seen.
-        */
-       br->flags._BCM_use_index = br->flags._BCL_use_index;
-
        /* _BQC uses INDEX while _BCL uses VALUE in some laptops */
        br->curr = level = max_level;
 
@@ -877,7 +879,7 @@ out:
  *     device  : video output device (LCD, CRT, ..)
  *
  *  Return Value:
- *     None
+ *     None
  *
  *  Find out all required AML methods defined under the output
  *  device.
@@ -885,135 +887,47 @@ out:
 
 static void acpi_video_device_find_cap(struct acpi_video_device *device)
 {
-       acpi_handle h_dummy1;
-
-       if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) {
+       if (acpi_has_method(device->dev->handle, "_ADR"))
                device->cap._ADR = 1;
-       }
-       if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) {
+       if (acpi_has_method(device->dev->handle, "_BCL"))
                device->cap._BCL = 1;
-       }
-       if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) {
+       if (acpi_has_method(device->dev->handle, "_BCM"))
                device->cap._BCM = 1;
-       }
-       if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
+       if (acpi_has_method(device->dev->handle, "_BQC")) {
                device->cap._BQC = 1;
-       else if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCQ",
-                               &h_dummy1))) {
+       } else if (acpi_has_method(device->dev->handle, "_BCQ")) {
                printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n");
                device->cap._BCQ = 1;
        }
 
-       if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) {
+       if (acpi_has_method(device->dev->handle, "_DDC"))
                device->cap._DDC = 1;
-       }
-
-       if (acpi_video_backlight_support()) {
-               struct backlight_properties props;
-               struct pci_dev *pdev;
-               acpi_handle acpi_parent;
-               struct device *parent = NULL;
-               int result;
-               static int count = 0;
-               char *name;
-
-               result = acpi_video_init_brightness(device);
-               if (result)
-                       return;
-               name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
-               if (!name)
-                       return;
-               count++;
-
-               acpi_get_parent(device->dev->handle, &acpi_parent);
-
-               pdev = acpi_get_pci_dev(acpi_parent);
-               if (pdev) {
-                       parent = &pdev->dev;
-                       pci_dev_put(pdev);
-               }
-
-               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;
-
-               /*
-                * 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);
-
-               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");
-
-       }
 }
 
 /*
- *  Arg:       
- *     device  : video output device (VGA)
+ *  Arg:
+ *     device  : video output device (VGA)
  *
  *  Return Value:
- *     None
+ *     None
  *
  *  Find out all required AML methods defined under the video bus device.
  */
 
 static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
 {
-       acpi_handle h_dummy1;
-
-       if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) {
+       if (acpi_has_method(video->device->handle, "_DOS"))
                video->cap._DOS = 1;
-       }
-       if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) {
+       if (acpi_has_method(video->device->handle, "_DOD"))
                video->cap._DOD = 1;
-       }
-       if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) {
+       if (acpi_has_method(video->device->handle, "_ROM"))
                video->cap._ROM = 1;
-       }
-       if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) {
+       if (acpi_has_method(video->device->handle, "_GPD"))
                video->cap._GPD = 1;
-       }
-       if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) {
+       if (acpi_has_method(video->device->handle, "_SPD"))
                video->cap._SPD = 1;
-       }
-       if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) {
+       if (acpi_has_method(video->device->handle, "_VPO"))
                video->cap._VPO = 1;
-       }
 }
 
 /*
@@ -1034,7 +948,8 @@ static int acpi_video_bus_check(struct acpi_video_bus *video)
                return -ENODEV;
        pci_dev_put(dev);
 
-       /* Since there is no HID, CID and so on for VGA driver, we have
+       /*
+        * Since there is no HID, CID and so on for VGA driver, we have
         * to check well known required nodes.
         */
 
@@ -1064,12 +979,14 @@ static int acpi_video_bus_check(struct acpi_video_bus *video)
        return status;
 }
 
-/* --------------------------------------------------------------------------
-                                 Driver Interface
-   -------------------------------------------------------------------------- */
+/*
+ * --------------------------------------------------------------------------
+ *                               Driver Interface
+ * --------------------------------------------------------------------------
+ */
 
 /* device interface */
-static struct acpi_video_device_attrib*
+static struct acpi_video_device_attrib *
 acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id)
 {
        struct acpi_video_enumerated_device *ids;
@@ -1107,7 +1024,7 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
        unsigned long long device_id;
        int status, device_type;
        struct acpi_video_device *data;
-       struct acpi_video_device_attribattribute;
+       struct acpi_video_device_attrib *attribute;
 
        status =
            acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
@@ -1129,7 +1046,7 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
 
        attribute = acpi_video_get_device_attr(video, device_id);
 
-       if((attribute != NULL) && attribute->device_id_scheme) {
+       if (attribute && attribute->device_id_scheme) {
                switch (attribute->display_type) {
                case ACPI_VIDEO_DISPLAY_CRT:
                        data->flags.crt = 1;
@@ -1147,37 +1064,30 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
                        data->flags.unknown = 1;
                        break;
                }
-               if(attribute->bios_can_detect)
+               if (attribute->bios_can_detect)
                        data->flags.bios = 1;
        } else {
                /* Check for legacy IDs */
                device_type = acpi_video_get_device_type(video, device_id);
                /* Ignore bits 16 and 18-20 */
                switch (device_type & 0xffe2ffff) {
-                       case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
-                               data->flags.crt = 1;
-                               break;
-                       case ACPI_VIDEO_DISPLAY_LEGACY_PANEL:
-                               data->flags.lcd = 1;
-                               break;
-                       case ACPI_VIDEO_DISPLAY_LEGACY_TV:
-                               data->flags.tvout = 1;
-                               break;
-                       default:
-                               data->flags.unknown = 1;
+               case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
+                       data->flags.crt = 1;
+                       break;
+               case ACPI_VIDEO_DISPLAY_LEGACY_PANEL:
+                       data->flags.lcd = 1;
+                       break;
+               case ACPI_VIDEO_DISPLAY_LEGACY_TV:
+                       data->flags.tvout = 1;
+                       break;
+               default:
+                       data->flags.unknown = 1;
                }
        }
 
        acpi_video_device_bind(video, data);
        acpi_video_device_find_cap(data);
 
-       status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
-                                            acpi_video_device_notify, data);
-       if (ACPI_FAILURE(status))
-               dev_err(&device->dev, "Error installing notify handler\n");
-       else
-               data->flags.notify = 1;
-
        mutex_lock(&video->device_list_lock);
        list_add_tail(&data->entry, &video->video_device_list);
        mutex_unlock(&video->device_list_lock);
@@ -1187,12 +1097,12 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
 
 /*
  *  Arg:
- *     video   : video bus device 
+ *     video   : video bus device
  *
  *  Return:
- *     none
- *  
- *  Enumerate the video device list of the video bus, 
+ *     none
+ *
+ *  Enumerate the video device list of the video bus,
  *  bind the ids with the corresponding video devices
  *  under the video bus.
  */
@@ -1211,13 +1121,13 @@ static void acpi_video_device_rebind(struct acpi_video_bus *video)
 
 /*
  *  Arg:
- *     video   : video bus device 
- *     device  : video output device under the video 
- *             bus
+ *     video   : video bus device
+ *     device  : video output device under the video
+ *             bus
  *
  *  Return:
- *     none
- *  
+ *     none
+ *
  *  Bind the ids with the corresponding video devices
  *  under the video bus.
  */
@@ -1240,11 +1150,11 @@ acpi_video_device_bind(struct acpi_video_bus *video,
 
 /*
  *  Arg:
- *     video   : video bus device 
+ *     video   : video bus device
  *
  *  Return:
- *     < 0     : error
- *  
+ *     < 0     : error
+ *
  *  Call _DOD to enumerate all devices attached to display adapter
  *
  */
@@ -1305,7 +1215,7 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video)
        video->attached_array = active_list;
        video->attached_count = count;
 
- out:
+out:
        kfree(buffer.pointer);
        return status;
 }
@@ -1361,8 +1271,8 @@ acpi_video_switch_brightness(struct acpi_video_device *device, int event)
        unsigned long long level_current, level_next;
        int result = -EINVAL;
 
-       /* no warning message if acpi_backlight=vendor is used */
-       if (!acpi_video_backlight_support())
+       /* no warning message if acpi_backlight=vendor or a quirk is used */
+       if (!acpi_video_verify_backlight_support())
                return 0;
 
        if (!device->brightness)
@@ -1482,64 +1392,6 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video,
        return status;
 }
 
-static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
-{
-       acpi_status status;
-
-       if (!device || !device->video)
-               return -ENOENT;
-
-       if (device->flags.notify) {
-               status = acpi_remove_notify_handler(device->dev->handle,
-                               ACPI_DEVICE_NOTIFY, acpi_video_device_notify);
-               if (ACPI_FAILURE(status))
-                       dev_err(&device->dev->dev,
-                                       "Can't remove video notify handler\n");
-       }
-
-       if (device->backlight) {
-               backlight_device_unregister(device->backlight);
-               device->backlight = NULL;
-       }
-       if (device->cooling_dev) {
-               sysfs_remove_link(&device->dev->dev.kobj,
-                                 "thermal_cooling");
-               sysfs_remove_link(&device->cooling_dev->device.kobj,
-                                 "device");
-               thermal_cooling_device_unregister(device->cooling_dev);
-               device->cooling_dev = NULL;
-       }
-
-       return 0;
-}
-
-static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
-{
-       int status;
-       struct acpi_video_device *dev, *next;
-
-       mutex_lock(&video->device_list_lock);
-
-       list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
-
-               status = acpi_video_bus_put_one_device(dev);
-               if (ACPI_FAILURE(status))
-                       printk(KERN_WARNING PREFIX
-                              "hhuuhhuu bug in acpi video driver.\n");
-
-               if (dev->brightness) {
-                       kfree(dev->brightness->levels);
-                       kfree(dev->brightness);
-               }
-               list_del(&dev->entry);
-               kfree(dev);
-       }
-
-       mutex_unlock(&video->device_list_lock);
-
-       return 0;
-}
-
 /* acpi_video interface */
 
 /*
@@ -1549,13 +1401,13 @@ static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
 static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
 {
        return acpi_video_bus_DOS(video, 0,
-                                 acpi_video_backlight_quirks() ? 1 : 0);
+                                 acpi_osi_is_win8() ? 1 : 0);
 }
 
 static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
 {
        return acpi_video_bus_DOS(video, 0,
-                                 acpi_video_backlight_quirks() ? 0 : 1);
+                                 acpi_osi_is_win8() ? 0 : 1);
 }
 
 static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
@@ -1564,7 +1416,7 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
        struct input_dev *input;
        int keycode = 0;
 
-       if (!video)
+       if (!video || !video->input)
                return;
 
        input = video->input;
@@ -1572,7 +1424,6 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
        switch (event) {
        case ACPI_VIDEO_NOTIFY_SWITCH:  /* User requested a switch,
                                         * most likely via hotkey. */
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_SWITCHVIDEOMODE;
                break;
 
@@ -1580,20 +1431,16 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
                                         * connector. */
                acpi_video_device_enumerate(video);
                acpi_video_device_rebind(video);
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_SWITCHVIDEOMODE;
                break;
 
        case ACPI_VIDEO_NOTIFY_CYCLE:   /* Cycle Display output hotkey pressed. */
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_SWITCHVIDEOMODE;
                break;
        case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:     /* Next Display output hotkey pressed. */
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_VIDEO_NEXT;
                break;
        case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:     /* previous Display output hotkey pressed. */
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_VIDEO_PREV;
                break;
 
@@ -1636,31 +1483,26 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)
        case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:        /* Cycle brightness */
                if (brightness_switch_enabled)
                        acpi_video_switch_brightness(video_device, event);
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_BRIGHTNESS_CYCLE;
                break;
        case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:  /* Increase brightness */
                if (brightness_switch_enabled)
                        acpi_video_switch_brightness(video_device, event);
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_BRIGHTNESSUP;
                break;
        case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:  /* Decrease brightness */
                if (brightness_switch_enabled)
                        acpi_video_switch_brightness(video_device, event);
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_BRIGHTNESSDOWN;
                break;
        case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightness */
                if (brightness_switch_enabled)
                        acpi_video_switch_brightness(video_device, event);
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_BRIGHTNESS_ZERO;
                break;
        case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:     /* display device off */
                if (brightness_switch_enabled)
                        acpi_video_switch_brightness(video_device, event);
-               acpi_bus_generate_proc_event(device, event, 0);
                keycode = KEY_DISPLAY_OFF;
                break;
        default:
@@ -1729,12 +1571,236 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context,
        return AE_OK;
 }
 
+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++;
+
+               acpi_get_parent(device->dev->handle, &acpi_parent);
+
+               pdev = acpi_get_pci_dev(acpi_parent);
+               if (pdev) {
+                       parent = &pdev->dev;
+                       pci_dev_put(pdev);
+               }
+
+               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;
+
+               /*
+                * 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);
+
+               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;
+
+       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->pm_nb.notifier_call = acpi_video_resume;
+       video->pm_nb.priority = 0;
+       return register_pm_notifier(&video->pm_nb);
+}
+
+static void acpi_video_dev_unregister_backlight(struct acpi_video_device *device)
+{
+       if (device->backlight) {
+               backlight_device_unregister(device->backlight);
+               device->backlight = NULL;
+       }
+       if (device->brightness) {
+               kfree(device->brightness->levels);
+               kfree(device->brightness);
+               device->brightness = NULL;
+       }
+       if (device->cooling_dev) {
+               sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling");
+               sysfs_remove_link(&device->cooling_dev->device.kobj, "device");
+               thermal_cooling_device_unregister(device->cooling_dev);
+               device->cooling_dev = NULL;
+       }
+}
+
+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);
+
+       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);
+
+       return error;
+}
+
+static void acpi_video_dev_add_notify_handler(struct acpi_video_device *device)
+{
+       acpi_status status;
+       struct acpi_device *adev = device->dev;
+
+       status = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
+                                            acpi_video_device_notify, device);
+       if (ACPI_FAILURE(status))
+               dev_err(&adev->dev, "Error installing notify handler\n");
+       else
+               device->flags.notify = 1;
+}
+
+static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video)
+{
+       struct input_dev *input;
+       struct acpi_video_device *dev;
+       int error;
+
+       video->input = input = input_allocate_device();
+       if (!input) {
+               error = -ENOMEM;
+               goto out;
+       }
+
+       error = acpi_video_bus_start_devices(video);
+       if (error)
+               goto err_free_input;
+
+       snprintf(video->phys, sizeof(video->phys),
+                       "%s/video/input0", acpi_device_hid(video->device));
+
+       input->name = acpi_device_name(video->device);
+       input->phys = video->phys;
+       input->id.bustype = BUS_HOST;
+       input->id.product = 0x06;
+       input->dev.parent = &video->device->dev;
+       input->evbit[0] = BIT(EV_KEY);
+       set_bit(KEY_SWITCHVIDEOMODE, input->keybit);
+       set_bit(KEY_VIDEO_NEXT, input->keybit);
+       set_bit(KEY_VIDEO_PREV, input->keybit);
+       set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit);
+       set_bit(KEY_BRIGHTNESSUP, input->keybit);
+       set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
+       set_bit(KEY_BRIGHTNESS_ZERO, input->keybit);
+       set_bit(KEY_DISPLAY_OFF, input->keybit);
+
+       error = input_register_device(input);
+       if (error)
+               goto err_stop_dev;
+
+       mutex_lock(&video->device_list_lock);
+       list_for_each_entry(dev, &video->video_device_list, entry)
+               acpi_video_dev_add_notify_handler(dev);
+       mutex_unlock(&video->device_list_lock);
+
+       return 0;
+
+err_stop_dev:
+       acpi_video_bus_stop_devices(video);
+err_free_input:
+       input_free_device(input);
+       video->input = NULL;
+out:
+       return error;
+}
+
+static void acpi_video_dev_remove_notify_handler(struct acpi_video_device *dev)
+{
+       if (dev->flags.notify) {
+               acpi_remove_notify_handler(dev->dev->handle, ACPI_DEVICE_NOTIFY,
+                                          acpi_video_device_notify);
+               dev->flags.notify = 0;
+       }
+}
+
+static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video)
+{
+       struct acpi_video_device *dev;
+
+       mutex_lock(&video->device_list_lock);
+       list_for_each_entry(dev, &video->video_device_list, entry)
+               acpi_video_dev_remove_notify_handler(dev);
+       mutex_unlock(&video->device_list_lock);
+
+       acpi_video_bus_stop_devices(video);
+       input_unregister_device(video->input);
+       video->input = NULL;
+}
+
+static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
+{
+       struct acpi_video_device *dev, *next;
+
+       mutex_lock(&video->device_list_lock);
+       list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
+               list_del(&dev->entry);
+               kfree(dev);
+       }
+       mutex_unlock(&video->device_list_lock);
+
+       return 0;
+}
+
 static int instance;
 
 static int acpi_video_bus_add(struct acpi_device *device)
 {
        struct acpi_video_bus *video;
-       struct input_dev *input;
        int error;
        acpi_status status;
 
@@ -1760,7 +1826,7 @@ static int acpi_video_bus_add(struct acpi_device *device)
        if (!strcmp(device->pnp.bus_id, "VID")) {
                if (instance)
                        device->pnp.bus_id[3] = '0' + instance;
-               instance ++;
+               instance++;
        }
        /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */
        if (!strcmp(device->pnp.bus_id, "VGA")) {
@@ -1786,62 +1852,24 @@ static int acpi_video_bus_add(struct acpi_device *device)
        if (error)
                goto err_put_video;
 
-       video->input = input = input_allocate_device();
-       if (!input) {
-               error = -ENOMEM;
-               goto err_put_video;
-       }
-
-       error = acpi_video_bus_start_devices(video);
-       if (error)
-               goto err_free_input_dev;
-
-       snprintf(video->phys, sizeof(video->phys),
-               "%s/video/input0", acpi_device_hid(video->device));
-
-       input->name = acpi_device_name(video->device);
-       input->phys = video->phys;
-       input->id.bustype = BUS_HOST;
-       input->id.product = 0x06;
-       input->dev.parent = &device->dev;
-       input->evbit[0] = BIT(EV_KEY);
-       set_bit(KEY_SWITCHVIDEOMODE, input->keybit);
-       set_bit(KEY_VIDEO_NEXT, input->keybit);
-       set_bit(KEY_VIDEO_PREV, input->keybit);
-       set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit);
-       set_bit(KEY_BRIGHTNESSUP, input->keybit);
-       set_bit(KEY_BRIGHTNESSDOWN, input->keybit);
-       set_bit(KEY_BRIGHTNESS_ZERO, input->keybit);
-       set_bit(KEY_DISPLAY_OFF, input->keybit);
-
        printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s  rom: %s  post: %s)\n",
               ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device),
               video->flags.multihead ? "yes" : "no",
               video->flags.rom ? "yes" : "no",
               video->flags.post ? "yes" : "no");
+       mutex_lock(&video_list_lock);
+       list_add_tail(&video->entry, &video_bus_head);
+       mutex_unlock(&video_list_lock);
 
-       video->pm_nb.notifier_call = acpi_video_resume;
-       video->pm_nb.priority = 0;
-       error = register_pm_notifier(&video->pm_nb);
-       if (error)
-               goto err_stop_video;
-
-       error = input_register_device(input);
-       if (error)
-               goto err_unregister_pm_notifier;
+       acpi_video_bus_register_backlight(video);
+       acpi_video_bus_add_notify_handler(video);
 
        return 0;
 
- err_unregister_pm_notifier:
-       unregister_pm_notifier(&video->pm_nb);
- err_stop_video:
-       acpi_video_bus_stop_devices(video);
- err_free_input_dev:
-       input_free_device(input);
- err_put_video:
+err_put_video:
        acpi_video_bus_put_devices(video);
        kfree(video->attached_array);
- err_free_video:
+err_free_video:
        kfree(video);
        device->driver_data = NULL;
 
@@ -1858,12 +1886,14 @@ static int acpi_video_bus_remove(struct acpi_device *device)
 
        video = acpi_driver_data(device);
 
-       unregister_pm_notifier(&video->pm_nb);
-
-       acpi_video_bus_stop_devices(video);
+       acpi_video_bus_remove_notify_handler(video);
+       acpi_video_bus_unregister_backlight(video);
        acpi_video_bus_put_devices(video);
 
-       input_unregister_device(video->input);
+       mutex_lock(&video_list_lock);
+       list_del(&video->entry);
+       mutex_unlock(&video_list_lock);
+
        kfree(video->attached_array);
        kfree(video);
 
@@ -1912,6 +1942,9 @@ int acpi_video_register(void)
                return 0;
        }
 
+       mutex_init(&video_list_lock);
+       INIT_LIST_HEAD(&video_bus_head);
+
        result = acpi_bus_register_driver(&acpi_video_bus);
        if (result < 0)
                return -ENODEV;