summaryrefslogtreecommitdiffstats
path: root/drivers/acpi/video.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/video.c')
-rw-r--r--drivers/acpi/video.c105
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)