diff options
Diffstat (limited to 'drivers/acpi/video.c')
-rw-r--r-- | drivers/acpi/video.c | 105 |
1 files changed, 84 insertions, 21 deletions
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index a4fddb24476f..cbe6f3924a10 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -78,6 +78,13 @@ MODULE_LICENSE("GPL"); static int brightness_switch_enabled = 1; module_param(brightness_switch_enabled, bool, 0644); +/* + * By default, we don't allow duplicate ACPI video bus devices + * under the same VGA controller + */ +static int allow_duplicates; +module_param(allow_duplicates, 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); @@ -285,7 +292,7 @@ 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 = { +static const struct file_operations acpi_video_device_brightness_fops = { .owner = THIS_MODULE, .open = acpi_video_device_brightness_open_fs, .read = seq_read, @@ -320,7 +327,7 @@ static int acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level); static int acpi_video_device_lcd_get_level_current( struct acpi_video_device *device, - unsigned long long *level); + unsigned long long *level, int init); static int acpi_video_get_next_level(struct acpi_video_device *device, u32 level_current, u32 event); static int acpi_video_switch_brightness(struct acpi_video_device *device, @@ -338,7 +345,7 @@ static int acpi_video_get_brightness(struct backlight_device *bd) struct acpi_video_device *vd = (struct acpi_video_device *)bl_get_data(bd); - if (acpi_video_device_lcd_get_level_current(vd, &cur_level)) + if (acpi_video_device_lcd_get_level_current(vd, &cur_level, 0)) return -EINVAL; for (i = 2; i < vd->brightness->count; i++) { if (vd->brightness->levels[i] == cur_level) @@ -407,7 +414,7 @@ static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsig unsigned long long level; int offset; - if (acpi_video_device_lcd_get_level_current(video, &level)) + if (acpi_video_device_lcd_get_level_current(video, &level, 0)) return -EINVAL; for (offset = 2; offset < video->brightness->count; offset++) if (level == video->brightness->levels[offset]) { @@ -602,7 +609,7 @@ static struct dmi_system_id video_dmi_table[] __initdata = { static int acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, - unsigned long long *level) + unsigned long long *level, int init) { acpi_status status = AE_OK; int i; @@ -626,10 +633,16 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, device->brightness->curr = *level; return 0; } - /* BQC returned an invalid level. Stop using it. */ - ACPI_WARNING((AE_INFO, "%s returned an invalid level", - buf)); - device->cap._BQC = device->cap._BCQ = 0; + if (!init) { + /* + * BQC returned an invalid level. + * Stop using it. + */ + ACPI_WARNING((AE_INFO, + "%s returned an invalid level", + buf)); + device->cap._BQC = device->cap._BCQ = 0; + } } else { /* Fixme: * should we return an error or ignore this failure? @@ -752,7 +765,7 @@ acpi_video_bus_POST_options(struct acpi_video_bus *video, static int acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) { - acpi_integer status = 0; + u64 status = 0; union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list args = { 1, &arg0 }; @@ -885,7 +898,7 @@ acpi_video_init_brightness(struct acpi_video_device *device) if (!device->cap._BQC) goto set_level; - result = acpi_video_device_lcd_get_level_current(device, &level_old); + result = acpi_video_device_lcd_get_level_current(device, &level_old, 1); if (result) goto out_free_levels; @@ -896,7 +909,7 @@ acpi_video_init_brightness(struct acpi_video_device *device) if (result) goto out_free_levels; - result = acpi_video_device_lcd_get_level_current(device, &level); + result = acpi_video_device_lcd_get_level_current(device, &level, 0); if (result) goto out_free_levels; @@ -985,6 +998,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) } if (acpi_video_backlight_support()) { + struct backlight_properties props; int result; static int count = 0; char *name; @@ -997,10 +1011,14 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) return; sprintf(name, "acpi_video%d", count++); - device->backlight = backlight_device_register(name, - NULL, device, &acpi_backlight_ops); - device->backlight->props.max_brightness = device->brightness->count-3; + memset(&props, 0, sizeof(struct backlight_properties)); + props.max_brightness = device->brightness->count - 3; + device->backlight = backlight_device_register(name, NULL, device, + &acpi_backlight_ops, + &props); kfree(name); + if (IS_ERR(device->backlight)) + return; result = sysfs_create_link(&device->backlight->dev.kobj, &device->dev->dev.kobj, "device"); @@ -1109,7 +1127,12 @@ static int acpi_video_bus_check(struct acpi_video_bus *video) */ /* Does this device support video switching? */ - if (video->cap._DOS) { + if (video->cap._DOS || video->cap._DOD) { + if (!video->cap._DOS) { + printk(KERN_WARNING FW_BUG + "ACPI(%s) defines _DOD but not _DOS\n", + acpi_device_bid(video->device)); + } video->flags.multihead = 1; status = 0; } @@ -1218,7 +1241,7 @@ acpi_video_device_write_state(struct file *file, u32 state = 0; - if (!dev || count + 1 > sizeof str) + if (!dev || count >= sizeof(str)) return -EINVAL; if (copy_from_user(str, buffer, count)) @@ -1275,7 +1298,7 @@ acpi_video_device_write_brightness(struct file *file, int i; - if (!dev || !dev->brightness || count + 1 > sizeof str) + if (!dev || !dev->brightness || count >= sizeof(str)) return -EINVAL; if (copy_from_user(str, buffer, count)) @@ -1557,7 +1580,7 @@ acpi_video_bus_write_POST(struct file *file, unsigned long long opt, options; - if (!video || count + 1 > sizeof str) + if (!video || count >= sizeof(str)) return -EINVAL; status = acpi_video_bus_POST_options(video, &options); @@ -1597,7 +1620,7 @@ acpi_video_bus_write_DOS(struct file *file, unsigned long opt; - if (!video || count + 1 > sizeof str) + if (!video || count >= sizeof(str)) return -EINVAL; if (copy_from_user(str, buffer, count)) @@ -1974,11 +1997,15 @@ 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()) + return 0; + if (!device->brightness) goto out; result = acpi_video_device_lcd_get_level_current(device, - &level_current); + &level_current, 0); if (result) goto out; @@ -2228,11 +2255,47 @@ static int acpi_video_resume(struct acpi_device *device) return AE_OK; } +static acpi_status +acpi_video_bus_match(acpi_handle handle, u32 level, void *context, + void **return_value) +{ + struct acpi_device *device = context; + struct acpi_device *sibling; + int result; + + if (handle == device->handle) + return AE_CTRL_TERMINATE; + + result = acpi_bus_get_device(handle, &sibling); + if (result) + return AE_OK; + + if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) + return AE_ALREADY_EXISTS; + + return AE_OK; +} + static int acpi_video_bus_add(struct acpi_device *device) { struct acpi_video_bus *video; struct input_dev *input; int error; + acpi_status status; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, + device->parent->handle, 1, + acpi_video_bus_match, NULL, + device, NULL); + if (status == AE_ALREADY_EXISTS) { + printk(KERN_WARNING FW_BUG + "Duplicate ACPI video bus devices for the" + " same VGA controller, please try module " + "parameter \"video.allow_duplicates=1\"" + "if the current driver doesn't work.\n"); + if (!allow_duplicates) + return -ENODEV; + } video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); if (!video) |