ACPICA: Add 64-bit support to acpi_read and acpi_write
[safe/jmp/linux-2.6] / drivers / acpi / video.c
index 9730ec1..8851315 100644 (file)
@@ -37,6 +37,8 @@
 #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>
@@ -74,9 +76,11 @@ MODULE_LICENSE("GPL");
 static int brightness_switch_enabled = 1;
 module_param(brightness_switch_enabled, bool, 0644);
 
+static int register_count = 0;
 static int acpi_video_bus_add(struct acpi_device *device);
 static int acpi_video_bus_remove(struct acpi_device *device, int type);
 static int acpi_video_resume(struct acpi_device *device);
+static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
 
 static const struct acpi_device_id video_device_ids[] = {
        {ACPI_VIDEO_HID, 0},
@@ -92,6 +96,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,
                },
 };
 
@@ -199,7 +204,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,
@@ -208,7 +213,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,
@@ -218,7 +223,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,
@@ -227,19 +232,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,
 };
@@ -247,7 +258,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,
@@ -257,27 +268,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,
@@ -285,7 +302,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",
@@ -370,33 +387,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;
 
        if (acpi_video_device_lcd_get_level_current(video, &level))
                return -EINVAL;
-       for (state = 2; state < video->brightness->count; state++)
-               if (level == video->brightness->levels[state])
-                       return sprintf(buf, "%d\n",
-                                      video->brightness->count - state - 1);
+       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);
@@ -518,6 +539,65 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)
        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"),
+               },
+       },
+       {
+        .callback = video_set_bqc_offset,
+        .ident = "eMachines E510",
+        .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"),
+               },
+       },
+       {
+        .callback = video_set_bqc_offset,
+        .ident = "Acer Aspire 5315",
+        .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"),
+               },
+       },
+       {
+        .callback = video_set_bqc_offset,
+        .ident = "Acer Aspire 7720",
+        .matches = {
+               DMI_MATCH(DMI_BOARD_VENDOR, "Acer"),
+               DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),
+               },
+       },
+       {}
+};
+
 static int
 acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
                                        unsigned long long *level)
@@ -537,6 +617,7 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
                                *level = device->brightness->levels[*level + 2];
 
                        }
+                       *level += bqc_offset_aml_bug_workaround;
                        device->brightness->curr = *level;
                        return 0;
                } else {
@@ -750,10 +831,12 @@ acpi_video_init_brightness(struct acpi_video_device *device)
         * 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] ||
-                   br->levels[i] == br->levels[1])
+       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;
@@ -787,12 +870,19 @@ acpi_video_init_brightness(struct acpi_video_device *device)
        br->flags._BCM_use_index = br->flags._BCL_use_index;
 
        /* _BQC uses INDEX while _BCL uses VALUE in some laptops */
-       br->curr = max_level;
+       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;
 
-       result = acpi_video_device_lcd_set_level(device, br->curr);
+       /*
+        * 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;
 
@@ -800,25 +890,19 @@ acpi_video_init_brightness(struct acpi_video_device *device)
        if (result)
                goto out_free_levels;
 
-       if ((level != level_old) && !br->flags._BCM_use_index) {
-               /* Note:
-                * This piece of code does not work correctly if the current
-                * brightness levels is 0.
-                * But I guess boxes that boot with such a dark screen are rare
-                * and no more code is needed to cover this specifial case.
-                */
+       br->flags._BQC_use_index = (level == max_level ? 0 : 1);
 
-               if (level_ac_battery != 2) {
-                       /*
-                        * For now, we don't support the _BCL like this:
-                        * 16, 15, 0, 1, 2, 3, ..., 14, 15, 16
-                        * because we may mess up the index returned by _BQC.
-                        * Plus: we have not got a box like this.
-                        */
-                       ACPI_ERROR((AE_INFO, "_BCL not supported\n"));
-               }
-               br->flags._BQC_use_index = 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));
@@ -901,6 +985,11 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                device->backlight->props.max_brightness = device->brightness->count-3;
                kfree(name);
 
+               result = sysfs_create_link(&device->backlight->dev.kobj,
+                                          &device->dev->dev.kobj, "device");
+               if (result)
+                       printk(KERN_ERR PREFIX "Create sysfs link\n");
+
                device->cdev = thermal_cooling_device_register("LCD",
                                        device->dev, &video_cooling_ops);
                if (IS_ERR(device->cdev))
@@ -979,15 +1068,15 @@ 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;
+       struct pci_dev *dev;
 
        if (!video)
                return -EINVAL;
 
-       dev = acpi_get_physical_pci_device(video->device->handle);
+       dev = acpi_get_pci_dev(video->device->handle);
        if (!dev)
                return -ENODEV;
-       put_device(dev);
+       pci_dev_put(dev);
 
        /* Since there is no HID, CID and so on for VGA driver, we have
         * to check well known required nodes.
@@ -1238,8 +1327,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));
@@ -1247,7 +1334,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,
@@ -1256,8 +1342,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,
@@ -1516,8 +1600,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,
@@ -1540,7 +1622,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,
@@ -1549,7 +1630,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,
@@ -1924,6 +2004,7 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
        status = acpi_remove_notify_handler(device->dev->handle,
                                            ACPI_DEVICE_NOTIFY,
                                            acpi_video_device_notify);
+       sysfs_remove_link(&device->backlight->dev.kobj, "device");
        backlight_device_unregister(device->backlight);
        if (device->cdev) {
                sysfs_remove_link(&device->dev->dev.kobj,
@@ -1977,17 +2058,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) {
@@ -2118,7 +2197,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;
@@ -2160,20 +2238,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),
@@ -2209,9 +2277,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);
@@ -2226,7 +2291,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;
 
 
@@ -2236,11 +2300,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);
 
@@ -2251,14 +2310,40 @@ 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;
+       if (register_count) {
+               /*
+                * if the function of acpi_video_register is already called,
+                * don't register the acpi_vide_bus again and return no error.
+                */
+               return 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) {
@@ -2266,16 +2351,56 @@ static int __init acpi_video_init(void)
                return -ENODEV;
        }
 
+       /*
+        * When the acpi_video_bus is loaded successfully, increase
+        * the counter reference.
+        */
+       register_count = 1;
+
        return 0;
 }
+EXPORT_SYMBOL(acpi_video_register);
 
-static void __exit acpi_video_exit(void)
+void acpi_video_unregister(void)
 {
-
+       if (!register_count) {
+               /*
+                * If the acpi video bus is already unloaded, don't
+                * unload it again and return directly.
+                */
+               return;
+       }
        acpi_bus_unregister_driver(&acpi_video_bus);
 
        remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
 
+       register_count = 0;
+
+       return;
+}
+EXPORT_SYMBOL(acpi_video_unregister);
+
+/*
+ * 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)
+{
+       acpi_video_unregister();
+
        return;
 }