drm/i915: acpi/video.c fix section mismatch warning
[safe/jmp/linux-2.6] / drivers / acpi / video.c
index a3aad30..a79b885 100644 (file)
@@ -36,6 +36,9 @@
 #include <linux/backlight.h>
 #include <linux/thermal.h>
 #include <linux/video_output.h>
+#include <linux/sort.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
 #include <asm/uaccess.h>
 
 #include <acpi/acpi_bus.h>
@@ -76,6 +79,7 @@ module_param(brightness_switch_enabled, bool, 0644);
 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[] = {
        {ACPI_VIDEO_HID, 0},
@@ -91,6 +95,7 @@ static struct acpi_driver acpi_video_bus = {
                .add = acpi_video_bus_add,
                .remove = acpi_video_bus_remove,
                .resume = acpi_video_resume,
+               .notify = acpi_video_bus_notify,
                },
 };
 
@@ -161,16 +166,26 @@ struct acpi_video_device_cap {
        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 _DCS:1;              /*Return status of output device */
        u8 _DGS:1;              /*Query graphics state */
        u8 _DSS:1;              /*Device state set */
 };
 
+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 _BQC_use_index:1;            /* _BQC returns an index value */
+};
+
 struct acpi_video_device_brightness {
        int curr;
        int count;
        int *levels;
+       struct acpi_video_brightness_flags flags;
 };
 
 struct acpi_video_device {
@@ -188,7 +203,7 @@ struct acpi_video_device {
 
 /* bus */
 static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file);
-static struct file_operations acpi_video_bus_info_fops = {
+static const struct file_operations acpi_video_bus_info_fops = {
        .owner = THIS_MODULE,
        .open = acpi_video_bus_info_open_fs,
        .read = seq_read,
@@ -197,7 +212,7 @@ static struct file_operations acpi_video_bus_info_fops = {
 };
 
 static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file);
-static struct file_operations acpi_video_bus_ROM_fops = {
+static const struct file_operations acpi_video_bus_ROM_fops = {
        .owner = THIS_MODULE,
        .open = acpi_video_bus_ROM_open_fs,
        .read = seq_read,
@@ -207,7 +222,7 @@ static struct file_operations acpi_video_bus_ROM_fops = {
 
 static int acpi_video_bus_POST_info_open_fs(struct inode *inode,
                                            struct file *file);
-static struct file_operations acpi_video_bus_POST_info_fops = {
+static const struct file_operations acpi_video_bus_POST_info_fops = {
        .owner = THIS_MODULE,
        .open = acpi_video_bus_POST_info_open_fs,
        .read = seq_read,
@@ -216,19 +231,25 @@ static struct file_operations acpi_video_bus_POST_info_fops = {
 };
 
 static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file);
-static struct file_operations acpi_video_bus_POST_fops = {
+static ssize_t acpi_video_bus_write_POST(struct file *file,
+       const char __user *buffer, size_t count, loff_t *data);
+static const struct file_operations acpi_video_bus_POST_fops = {
        .owner = THIS_MODULE,
        .open = acpi_video_bus_POST_open_fs,
        .read = seq_read,
+       .write = acpi_video_bus_write_POST,
        .llseek = seq_lseek,
        .release = single_release,
 };
 
 static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file);
-static struct file_operations acpi_video_bus_DOS_fops = {
+static ssize_t acpi_video_bus_write_DOS(struct file *file,
+       const char __user *buffer, size_t count, loff_t *data);
+static const struct file_operations acpi_video_bus_DOS_fops = {
        .owner = THIS_MODULE,
        .open = acpi_video_bus_DOS_open_fs,
        .read = seq_read,
+       .write = acpi_video_bus_write_DOS,
        .llseek = seq_lseek,
        .release = single_release,
 };
@@ -236,7 +257,7 @@ static struct file_operations acpi_video_bus_DOS_fops = {
 /* device */
 static int acpi_video_device_info_open_fs(struct inode *inode,
                                          struct file *file);
-static struct file_operations acpi_video_device_info_fops = {
+static const struct file_operations acpi_video_device_info_fops = {
        .owner = THIS_MODULE,
        .open = acpi_video_device_info_open_fs,
        .read = seq_read,
@@ -246,27 +267,33 @@ static struct file_operations acpi_video_device_info_fops = {
 
 static int acpi_video_device_state_open_fs(struct inode *inode,
                                           struct file *file);
-static struct file_operations acpi_video_device_state_fops = {
+static ssize_t acpi_video_device_write_state(struct file *file,
+       const char __user *buffer, size_t count, loff_t *data);
+static const struct file_operations acpi_video_device_state_fops = {
        .owner = THIS_MODULE,
        .open = acpi_video_device_state_open_fs,
        .read = seq_read,
+       .write = acpi_video_device_write_state,
        .llseek = seq_lseek,
        .release = single_release,
 };
 
 static int acpi_video_device_brightness_open_fs(struct inode *inode,
                                                struct file *file);
+static ssize_t acpi_video_device_write_brightness(struct file *file,
+       const char __user *buffer, size_t count, loff_t *data);
 static struct file_operations acpi_video_device_brightness_fops = {
        .owner = THIS_MODULE,
        .open = acpi_video_device_brightness_open_fs,
        .read = seq_read,
+       .write = acpi_video_device_write_brightness,
        .llseek = seq_lseek,
        .release = single_release,
 };
 
 static int acpi_video_device_EDID_open_fs(struct inode *inode,
                                          struct file *file);
-static struct file_operations acpi_video_device_EDID_fops = {
+static const struct file_operations acpi_video_device_EDID_fops = {
        .owner = THIS_MODULE,
        .open = acpi_video_device_EDID_open_fs,
        .read = seq_read,
@@ -274,7 +301,7 @@ static struct file_operations acpi_video_device_EDID_fops = {
        .release = single_release,
 };
 
-static char device_decode[][30] = {
+static const char device_decode[][30] = {
        "motherboard VGA device",
        "PCI VGA device",
        "AGP VGA device",
@@ -293,7 +320,7 @@ static int acpi_video_device_lcd_get_level_current(
                        unsigned long long *level);
 static int acpi_video_get_next_level(struct acpi_video_device *device,
                                     u32 level_current, u32 event);
-static void acpi_video_switch_brightness(struct acpi_video_device *device,
+static int acpi_video_switch_brightness(struct acpi_video_device *device,
                                         int event);
 static int acpi_video_device_get_state(struct acpi_video_device *device,
                            unsigned long long *state);
@@ -307,7 +334,9 @@ static int acpi_video_get_brightness(struct backlight_device *bd)
        int i;
        struct acpi_video_device *vd =
                (struct acpi_video_device *)bl_get_data(bd);
-       acpi_video_device_lcd_get_level_current(vd, &cur_level);
+
+       if (acpi_video_device_lcd_get_level_current(vd, &cur_level))
+               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
@@ -319,12 +348,12 @@ 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;
+       int request_level = bd->props.brightness + 2;
        struct acpi_video_device *vd =
                (struct acpi_video_device *)bl_get_data(bd);
-       acpi_video_device_lcd_set_level(vd,
-                                       vd->brightness->levels[request_level]);
-       return 0;
+
+       return acpi_video_device_lcd_set_level(vd,
+                               vd->brightness->levels[request_level]);
 }
 
 static struct backlight_ops acpi_backlight_ops = {
@@ -357,32 +386,37 @@ static struct output_properties acpi_output_properties = {
 
 
 /* thermal cooling device callbacks */
-static int video_get_max_state(struct thermal_cooling_device *cdev, char *buf)
+static int video_get_max_state(struct thermal_cooling_device *cdev, unsigned
+                              long *state)
 {
        struct acpi_device *device = cdev->devdata;
        struct acpi_video_device *video = acpi_driver_data(device);
 
-       return sprintf(buf, "%d\n", video->brightness->count - 3);
+       *state = video->brightness->count - 3;
+       return 0;
 }
 
-static int video_get_cur_state(struct thermal_cooling_device *cdev, char *buf)
+static int video_get_cur_state(struct thermal_cooling_device *cdev, unsigned
+                              long *state)
 {
        struct acpi_device *device = cdev->devdata;
        struct acpi_video_device *video = acpi_driver_data(device);
        unsigned long long level;
-       int state;
+       int offset;
 
-       acpi_video_device_lcd_get_level_current(video, &level);
-       for (state = 2; state < video->brightness->count; state++)
-               if (level == video->brightness->levels[state])
-                       return sprintf(buf, "%d\n",
-                                      video->brightness->count - state - 1);
+       if (acpi_video_device_lcd_get_level_current(video, &level))
+               return -EINVAL;
+       for (offset = 2; offset < video->brightness->count; offset++)
+               if (level == video->brightness->levels[offset]) {
+                       *state = video->brightness->count - offset - 1;
+                       return 0;
+               }
 
        return -EINVAL;
 }
 
 static int
-video_set_cur_state(struct thermal_cooling_device *cdev, unsigned int state)
+video_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
 {
        struct acpi_device *device = cdev->devdata;
        struct acpi_video_device *video = acpi_driver_data(device);
@@ -478,29 +512,104 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device,
 static int
 acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
 {
-       int status = AE_OK;
+       int status;
        union acpi_object arg0 = { ACPI_TYPE_INTEGER };
        struct acpi_object_list args = { 1, &arg0 };
-
+       int state;
 
        arg0.integer.value = level;
 
-       if (device->cap._BCM)
-               status = acpi_evaluate_object(device->dev->handle, "_BCM",
-                                             &args, NULL);
+       status = acpi_evaluate_object(device->dev->handle, "_BCM",
+                                     &args, NULL);
+       if (ACPI_FAILURE(status)) {
+               ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));
+               return -EIO;
+       }
+
        device->brightness->curr = level;
-       return status;
+       for (state = 2; state < device->brightness->count; state++)
+               if (level == device->brightness->levels[state]) {
+                       if (device->backlight)
+                               device->backlight->props.brightness = state - 2;
+                       return 0;
+               }
+
+       ACPI_ERROR((AE_INFO, "Current brightness invalid"));
+       return -EINVAL;
+}
+
+/*
+ * For some buggy _BQC methods, we need to add a constant value to
+ * the _BQC return value to get the actual current brightness level
+ */
+
+static int bqc_offset_aml_bug_workaround;
+static int __init video_set_bqc_offset(const struct dmi_system_id *d)
+{
+       bqc_offset_aml_bug_workaround = 9;
+       return 0;
 }
 
+static struct dmi_system_id video_dmi_table[] __initdata = {
+       /*
+        * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121
+        */
+       {
+        .callback = video_set_bqc_offset,
+        .ident = "Acer Aspire 5720",
+        .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
+               },
+       },
+       {
+        .callback = video_set_bqc_offset,
+        .ident = "Acer Aspire 5710Z",
+        .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"),
+               },
+       },
+       {}
+};
+
 static int
 acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
                                        unsigned long long *level)
 {
-       if (device->cap._BQC)
-               return acpi_evaluate_integer(device->dev->handle, "_BQC", NULL,
-                                            level);
+       acpi_status status = AE_OK;
+
+       if (device->cap._BQC || device->cap._BCQ) {
+               char *buf = device->cap._BQC ? "_BQC" : "_BCQ";
+
+               status = acpi_evaluate_integer(device->dev->handle, buf,
+                                               NULL, level);
+               if (ACPI_SUCCESS(status)) {
+                       if (device->brightness->flags._BQC_use_index) {
+                               if (device->brightness->flags._BCL_reversed)
+                                       *level = device->brightness->count
+                                                                - 3 - (*level);
+                               *level = device->brightness->levels[*level + 2];
+
+                       }
+                       *level += bqc_offset_aml_bug_workaround;
+                       device->brightness->curr = *level;
+                       return 0;
+               } else {
+                       /* 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.
+                        * ACPI video backlight still works w/ buggy _BQC.
+                        * http://bugzilla.kernel.org/show_bug.cgi?id=12233
+                        */
+                       ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf));
+                       device->cap._BQC = device->cap._BCQ = 0;
+               }
+       }
+
        *level = device->brightness->curr;
-       return AE_OK;
+       return 0;
 }
 
 static int
@@ -626,6 +735,16 @@ acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)
 }
 
 /*
+ * Simple comparison function used to sort backlight levels.
+ */
+
+static int
+acpi_video_cmp_level(const void *a, const void *b)
+{
+       return *(int *)a - *(int *)b;
+}
+
+/*
  *  Arg:       
  *     device  : video output device (LCD, CRT, ..)
  *
@@ -639,9 +758,11 @@ static int
 acpi_video_init_brightness(struct acpi_video_device *device)
 {
        union acpi_object *obj = NULL;
-       int i, max_level = 0, count = 0;
+       int i, max_level = 0, count = 0, level_ac_battery = 0;
+       unsigned long long level, level_old;
        union acpi_object *o;
        struct acpi_video_device_brightness *br = NULL;
+       int result = -EINVAL;
 
        if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {
                ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available "
@@ -655,13 +776,16 @@ acpi_video_init_brightness(struct acpi_video_device *device)
        br = kzalloc(sizeof(*br), GFP_KERNEL);
        if (!br) {
                printk(KERN_ERR "can't allocate memory\n");
+               result = -ENOMEM;
                goto out;
        }
 
-       br->levels = kmalloc(obj->package.count * sizeof *(br->levels),
+       br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels),
                                GFP_KERNEL);
-       if (!br->levels)
+       if (!br->levels) {
+               result = -ENOMEM;
                goto out_free;
+       }
 
        for (i = 0; i < obj->package.count; i++) {
                o = (union acpi_object *)&obj->package.elements[i];
@@ -676,14 +800,89 @@ acpi_video_init_brightness(struct acpi_video_device *device)
                count++;
        }
 
-       if (count < 2)
-               goto out_free_levels;
+       /*
+        * some buggy BIOS don't export the levels
+        * when machine is on AC/Battery in _BCL package.
+        * In this case, the first two elements in _BCL packages
+        * are also supported brightness levels that OS should take care of.
+        */
+       for (i = 2; i < count; i++) {
+               if (br->levels[i] == br->levels[0])
+                       level_ac_battery++;
+               if (br->levels[i] == br->levels[1])
+                       level_ac_battery++;
+       }
+
+       if (level_ac_battery < 2) {
+               level_ac_battery = 2 - level_ac_battery;
+               br->flags._BCL_no_ac_battery_levels = 1;
+               for (i = (count - 1 + level_ac_battery); i >= 2; i--)
+                       br->levels[i] = br->levels[i - level_ac_battery];
+               count += level_ac_battery;
+       } else if (level_ac_battery > 2)
+               ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package\n"));
+
+       /* Check if the _BCL package is in a reversed order */
+       if (max_level == br->levels[2]) {
+               br->flags._BCL_reversed = 1;
+               sort(&br->levels[2], count - 2, sizeof(br->levels[2]),
+                       acpi_video_cmp_level, NULL);
+       } else if (max_level != br->levels[count - 1])
+               ACPI_ERROR((AE_INFO,
+                           "Found unordered _BCL package\n"));
 
        br->count = count;
        device->brightness = br;
-       ACPI_DEBUG_PRINT((ACPI_DB_INFO, "found %d brightness levels\n", count));
+
+       /* 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_old = max_level;
+
+       if (!device->cap._BQC)
+               goto set_level;
+
+       result = acpi_video_device_lcd_get_level_current(device, &level_old);
+       if (result)
+               goto out_free_levels;
+
+       /*
+        * Set the level to maximum and check if _BQC uses indexed value
+        */
+       result = acpi_video_device_lcd_set_level(device, max_level);
+       if (result)
+               goto out_free_levels;
+
+       result = acpi_video_device_lcd_get_level_current(device, &level);
+       if (result)
+               goto out_free_levels;
+
+       br->flags._BQC_use_index = (level == max_level ? 0 : 1);
+
+       if (!br->flags._BQC_use_index)
+               goto set_level;
+
+       if (br->flags._BCL_reversed)
+               level_old = (br->count - 1) - level_old;
+       level_old = br->levels[level_old];
+
+set_level:
+       result = acpi_video_device_lcd_set_level(device, level_old);
+       if (result)
+               goto out_free_levels;
+
+       ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+                         "found %d brightness levels\n", count - 2));
        kfree(obj);
-       return max_level;
+       return result;
 
 out_free_levels:
        kfree(br->levels);
@@ -692,7 +891,7 @@ out_free:
 out:
        device->brightness = NULL;
        kfree(obj);
-       return 0;
+       return result;
 }
 
 /*
@@ -709,7 +908,6 @@ out:
 static void acpi_video_device_find_cap(struct acpi_video_device *device)
 {
        acpi_handle h_dummy1;
-       u32 max_level = 0;
 
 
        memset(&device->cap, 0, sizeof(device->cap));
@@ -725,6 +923,12 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
        }
        if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1)))
                device->cap._BQC = 1;
+       else if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCQ",
+                               &h_dummy1))) {
+               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))) {
                device->cap._DDC = 1;
        }
@@ -738,12 +942,14 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                device->cap._DSS = 1;
        }
 
-       max_level = acpi_video_init_brightness(device);
-
-       if (device->cap._BCL && device->cap._BCM && max_level > 0) {
+       if (acpi_video_backlight_support()) {
                int result;
                static int count = 0;
                char *name;
+
+               result = acpi_video_init_brightness(device);
+               if (result)
+                       return;
                name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
                if (!name)
                        return;
@@ -752,18 +958,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                device->backlight = backlight_device_register(name,
                        NULL, device, &acpi_backlight_ops);
                device->backlight->props.max_brightness = device->brightness->count-3;
-               /*
-                * If there exists the _BQC object, the _BQC object will be
-                * called to get the current backlight brightness. Otherwise
-                * the brightness will be set to the maximum.
-                */
-               if (device->cap._BQC)
-                       device->backlight->props.brightness =
-                               acpi_video_get_brightness(device->backlight);
-               else
-                       device->backlight->props.brightness =
-                               device->backlight->props.max_brightness;
-               backlight_update_status(device->backlight);
                kfree(name);
 
                device->cdev = thermal_cooling_device_register("LCD",
@@ -784,18 +978,21 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                        printk(KERN_ERR PREFIX "Create sysfs link\n");
 
        }
-       if (device->cap._DCS && device->cap._DSS){
-               static int count = 0;
-               char *name;
-               name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
-               if (!name)
-                       return;
-               sprintf(name, "acpi_video%d", count++);
-               device->output_dev = video_output_register(name,
-                               NULL, device, &acpi_output_properties);
-               kfree(name);
+
+       if (acpi_video_display_switch_support()) {
+
+               if (device->cap._DCS && device->cap._DSS) {
+                       static int count;
+                       char *name;
+                       name = kzalloc(MAX_NAME_LEN, GFP_KERNEL);
+                       if (!name)
+                               return;
+                       sprintf(name, "acpi_video%d", count++);
+                       device->output_dev = video_output_register(name,
+                                       NULL, device, &acpi_output_properties);
+                       kfree(name);
+               }
        }
-       return;
 }
 
 /*
@@ -841,11 +1038,16 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video)
 static int acpi_video_bus_check(struct acpi_video_bus *video)
 {
        acpi_status status = -ENOENT;
-
+       struct device *dev;
 
        if (!video)
                return -EINVAL;
 
+       dev = acpi_get_physical_pci_device(video->device->handle);
+       if (!dev)
+               return -ENODEV;
+       put_device(dev);
+
        /* Since there is no HID, CID and so on for VGA driver, we have
         * to check well known required nodes.
         */
@@ -991,7 +1193,7 @@ acpi_video_device_brightness_seq_show(struct seq_file *seq, void *offset)
        }
 
        seq_printf(seq, "levels: ");
-       for (i = 0; i < dev->brightness->count; i++)
+       for (i = 2; i < dev->brightness->count; i++)
                seq_printf(seq, " %d", dev->brightness->levels[i]);
        seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr);
 
@@ -1030,15 +1232,14 @@ acpi_video_device_write_brightness(struct file *file,
                return -EFAULT;
 
        /* validate through the list of available levels */
-       for (i = 0; i < dev->brightness->count; i++)
+       for (i = 2; i < dev->brightness->count; i++)
                if (level == dev->brightness->levels[i]) {
-                       if (ACPI_SUCCESS
-                           (acpi_video_device_lcd_set_level(dev, level)))
-                               dev->brightness->curr = level;
+                       if (!acpi_video_device_lcd_set_level(dev, level))
+                               return count;
                        break;
                }
 
-       return count;
+       return -EINVAL;
 }
 
 static int acpi_video_device_EDID_seq_show(struct seq_file *seq, void *offset)
@@ -1096,8 +1297,6 @@ static int acpi_video_device_add_fs(struct acpi_device *device)
        if (!device_dir)
                return -ENOMEM;
 
-       device_dir->owner = THIS_MODULE;
-
        /* 'info' [R] */
        entry = proc_create_data("info", S_IRUGO, device_dir,
                        &acpi_video_device_info_fops, acpi_driver_data(device));
@@ -1105,7 +1304,6 @@ static int acpi_video_device_add_fs(struct acpi_device *device)
                goto err_remove_dir;
 
        /* 'state' [R/W] */
-       acpi_video_device_state_fops.write = acpi_video_device_write_state;
        entry = proc_create_data("state", S_IFREG | S_IRUGO | S_IWUSR,
                                 device_dir,
                                 &acpi_video_device_state_fops,
@@ -1114,8 +1312,6 @@ static int acpi_video_device_add_fs(struct acpi_device *device)
                goto err_remove_info;
 
        /* 'brightness' [R/W] */
-       acpi_video_device_brightness_fops.write =
-               acpi_video_device_write_brightness;
        entry = proc_create_data("brightness", S_IFREG | S_IRUGO | S_IWUSR,
                                 device_dir,
                                 &acpi_video_device_brightness_fops,
@@ -1231,7 +1427,7 @@ static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset)
                        printk(KERN_WARNING PREFIX
                               "This indicates a BIOS bug. Please contact the manufacturer.\n");
                }
-               printk("%llx\n", options);
+               printk(KERN_WARNING "%llx\n", options);
                seq_printf(seq, "can POST: <integrated video>");
                if (options & 2)
                        seq_printf(seq, " <PCI video>");
@@ -1374,8 +1570,6 @@ static int acpi_video_bus_add_fs(struct acpi_device *device)
        if (!device_dir)
                return -ENOMEM;
 
-       device_dir->owner = THIS_MODULE;
-
        /* 'info' [R] */
        entry = proc_create_data("info", S_IRUGO, device_dir,
                                 &acpi_video_bus_info_fops,
@@ -1398,7 +1592,6 @@ static int acpi_video_bus_add_fs(struct acpi_device *device)
                goto err_remove_rom;
 
        /* 'POST' [R/W] */
-       acpi_video_bus_POST_fops.write = acpi_video_bus_write_POST;
        entry = proc_create_data("POST", S_IFREG | S_IRUGO | S_IWUSR,
                                 device_dir,
                                 &acpi_video_bus_POST_fops,
@@ -1407,7 +1600,6 @@ static int acpi_video_bus_add_fs(struct acpi_device *device)
                goto err_remove_post_info;
 
        /* 'DOS' [R/W] */
-       acpi_video_bus_DOS_fops.write = acpi_video_bus_write_DOS;
        entry = proc_create_data("DOS", S_IFREG | S_IRUGO | S_IWUSR,
                                 device_dir,
                                 &acpi_video_bus_DOS_fops,
@@ -1683,7 +1875,7 @@ acpi_video_get_next_level(struct acpi_video_device *device,
        max = max_below = 0;
        min = min_above = 255;
        /* Find closest level to level_current */
-       for (i = 0; i < device->brightness->count; i++) {
+       for (i = 2; i < device->brightness->count; i++) {
                l = device->brightness->levels[i];
                if (abs(l - level_current) < abs(delta)) {
                        delta = l - level_current;
@@ -1693,7 +1885,7 @@ acpi_video_get_next_level(struct acpi_video_device *device,
        }
        /* Ajust level_current to closest available level */
        level_current += delta;
-       for (i = 0; i < device->brightness->count; i++) {
+       for (i = 2; i < device->brightness->count; i++) {
                l = device->brightness->levels[i];
                if (l < min)
                        min = l;
@@ -1720,15 +1912,29 @@ acpi_video_get_next_level(struct acpi_video_device *device,
        }
 }
 
-static void
+static int
 acpi_video_switch_brightness(struct acpi_video_device *device, int event)
 {
        unsigned long long level_current, level_next;
+       int result = -EINVAL;
+
        if (!device->brightness)
-               return;
-       acpi_video_device_lcd_get_level_current(device, &level_current);
+               goto out;
+
+       result = acpi_video_device_lcd_get_level_current(device,
+                                                        &level_current);
+       if (result)
+               goto out;
+
        level_next = acpi_video_get_next_level(device, level_current, event);
-       acpi_video_device_lcd_set_level(device, level_next);
+
+       result = acpi_video_device_lcd_set_level(device, level_next);
+
+out:
+       if (result)
+               printk(KERN_ERR PREFIX "Failed to switch the brightness\n");
+
+       return result;
 }
 
 static int
@@ -1821,17 +2027,15 @@ static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
        return acpi_video_bus_DOS(video, 0, 1);
 }
 
-static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data)
+static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
 {
-       struct acpi_video_bus *video = data;
-       struct acpi_device *device = NULL;
+       struct acpi_video_bus *video = acpi_driver_data(device);
        struct input_dev *input;
        int keycode;
 
        if (!video)
                return;
 
-       device = video->device;
        input = video->input;
 
        switch (event) {
@@ -1962,7 +2166,6 @@ static int acpi_video_resume(struct acpi_device *device)
 
 static int acpi_video_bus_add(struct acpi_device *device)
 {
-       acpi_status status;
        struct acpi_video_bus *video;
        struct input_dev *input;
        int error;
@@ -1977,6 +2180,12 @@ static int acpi_video_bus_add(struct acpi_device *device)
                        device->pnp.bus_id[3] = '0' + instance;
                instance ++;
        }
+       /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */
+       if (!strcmp(device->pnp.bus_id, "VGA")) {
+               if (instance)
+                       device->pnp.bus_id[3] = '0' + instance;
+               instance++;
+       }
 
        video->device = device;
        strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME);
@@ -1998,20 +2207,10 @@ static int acpi_video_bus_add(struct acpi_device *device)
        acpi_video_bus_get_devices(video, device);
        acpi_video_bus_start_devices(video);
 
-       status = acpi_install_notify_handler(device->handle,
-                                            ACPI_DEVICE_NOTIFY,
-                                            acpi_video_bus_notify, video);
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_ERR PREFIX
-                                 "Error installing notify handler\n");
-               error = -ENODEV;
-               goto err_stop_video;
-       }
-
        video->input = input = input_allocate_device();
        if (!input) {
                error = -ENOMEM;
-               goto err_uninstall_notify;
+               goto err_stop_video;
        }
 
        snprintf(video->phys, sizeof(video->phys),
@@ -2047,9 +2246,6 @@ static int acpi_video_bus_add(struct acpi_device *device)
 
  err_free_input_dev:
        input_free_device(input);
- err_uninstall_notify:
-       acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
-                                  acpi_video_bus_notify);
  err_stop_video:
        acpi_video_bus_stop_devices(video);
        acpi_video_bus_put_devices(video);
@@ -2064,7 +2260,6 @@ static int acpi_video_bus_add(struct acpi_device *device)
 
 static int acpi_video_bus_remove(struct acpi_device *device, int type)
 {
-       acpi_status status = 0;
        struct acpi_video_bus *video = NULL;
 
 
@@ -2074,11 +2269,6 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type)
        video = acpi_driver_data(device);
 
        acpi_video_bus_stop_devices(video);
-
-       status = acpi_remove_notify_handler(video->device->handle,
-                                           ACPI_DEVICE_NOTIFY,
-                                           acpi_video_bus_notify);
-
        acpi_video_bus_put_devices(video);
        acpi_video_bus_remove_fs(device);
 
@@ -2089,14 +2279,33 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type)
        return 0;
 }
 
-static int __init acpi_video_init(void)
+static int __init intel_opregion_present(void)
+{
+#if defined(CONFIG_DRM_I915) || defined(CONFIG_DRM_I915_MODULE)
+       struct pci_dev *dev = NULL;
+       u32 address;
+
+       for_each_pci_dev(dev) {
+               if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
+                       continue;
+               if (dev->vendor != PCI_VENDOR_ID_INTEL)
+                       continue;
+               pci_read_config_dword(dev, 0xfc, &address);
+               if (!address)
+                       continue;
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+int acpi_video_register(void)
 {
        int result = 0;
 
        acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir);
        if (!acpi_video_dir)
                return -ENODEV;
-       acpi_video_dir->owner = THIS_MODULE;
 
        result = acpi_bus_register_driver(&acpi_video_bus);
        if (result < 0) {
@@ -2106,8 +2315,26 @@ static int __init acpi_video_init(void)
 
        return 0;
 }
+EXPORT_SYMBOL(acpi_video_register);
+
+/*
+ * This is kind of nasty. Hardware using Intel chipsets may require
+ * the video opregion code to be run first in order to initialise
+ * state before any ACPI video calls are made. To handle this we defer
+ * registration of the video class until the opregion code has run.
+ */
+
+static int __init acpi_video_init(void)
+{
+       dmi_check_system(video_dmi_table);
+
+       if (intel_opregion_present())
+               return 0;
+
+       return acpi_video_register();
+}
 
-static void __exit acpi_video_exit(void)
+void acpi_video_exit(void)
 {
 
        acpi_bus_unregister_driver(&acpi_video_bus);
@@ -2116,6 +2343,7 @@ static void __exit acpi_video_exit(void)
 
        return;
 }
+EXPORT_SYMBOL(acpi_video_exit);
 
 module_init(acpi_video_init);
 module_exit(acpi_video_exit);