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);
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)
*level = device->brightness->levels[*level + 2];
}
+ *level += bqc_offset_aml_bug_workaround;
device->brightness->curr = *level;
return 0;
} else {
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))
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.
status = acpi_remove_notify_handler(device->dev->handle,
ACPI_DEVICE_NOTIFY,
acpi_video_device_notify);
- backlight_device_unregister(device->backlight);
+ if (device->backlight) {
+ sysfs_remove_link(&device->backlight->dev.kobj, "device");
+ backlight_device_unregister(device->backlight);
+ device->backlight = NULL;
+ }
if (device->cdev) {
sysfs_remove_link(&device->dev->dev.kobj,
"thermal_cooling");
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;
}
+ /*
+ * When the acpi_video_bus is loaded successfully, increase
+ * the counter reference.
+ */
+ register_count = 1;
+
return 0;
}
EXPORT_SYMBOL(acpi_video_register);
+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
static int __init acpi_video_init(void)
{
+ dmi_check_system(video_dmi_table);
+
if (intel_opregion_present())
return 0;
static void __exit acpi_video_exit(void)
{
-
- acpi_bus_unregister_driver(&acpi_video_bus);
-
- remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir);
+ acpi_video_unregister();
return;
}