|
@@ -167,7 +167,8 @@ struct acpi_video_device_flags {
|
|
|
u8 dvi:1;
|
|
|
u8 bios:1;
|
|
|
u8 unknown:1;
|
|
|
- u8 reserved:2;
|
|
|
+ u8 notify:1;
|
|
|
+ u8 reserved:1;
|
|
|
};
|
|
|
|
|
|
struct acpi_video_device_cap {
|
|
@@ -222,7 +223,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, int init);
|
|
|
+ unsigned long long *level, bool raw);
|
|
|
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,
|
|
@@ -236,7 +237,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, 0))
|
|
|
+ if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false))
|
|
|
return -EINVAL;
|
|
|
for (i = 2; i < vd->brightness->count; i++) {
|
|
|
if (vd->brightness->levels[i] == cur_level)
|
|
@@ -281,7 +282,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, 0))
|
|
|
+ if (acpi_video_device_lcd_get_level_current(video, &level, false))
|
|
|
return -EINVAL;
|
|
|
for (offset = 2; offset < video->brightness->count; offset++)
|
|
|
if (level == video->brightness->levels[offset]) {
|
|
@@ -447,12 +448,45 @@ static struct dmi_system_id video_dmi_table[] __initdata = {
|
|
|
DMI_MATCH(DMI_PRODUCT_NAME, "HP Folio 13 - 2000 Notebook PC"),
|
|
|
},
|
|
|
},
|
|
|
+ {
|
|
|
+ .callback = video_ignore_initial_backlight,
|
|
|
+ .ident = "HP Pavilion dm4",
|
|
|
+ .matches = {
|
|
|
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
|
|
|
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dm4 Notebook PC"),
|
|
|
+ },
|
|
|
+ },
|
|
|
{}
|
|
|
};
|
|
|
|
|
|
+static unsigned long long
|
|
|
+acpi_video_bqc_value_to_level(struct acpi_video_device *device,
|
|
|
+ unsigned long long bqc_value)
|
|
|
+{
|
|
|
+ unsigned long long level;
|
|
|
+
|
|
|
+ if (device->brightness->flags._BQC_use_index) {
|
|
|
+ /*
|
|
|
+ * _BQC returns an index that doesn't account for
|
|
|
+ * the first 2 items with special meaning, so we need
|
|
|
+ * to compensate for that by offsetting ourselves
|
|
|
+ */
|
|
|
+ if (device->brightness->flags._BCL_reversed)
|
|
|
+ bqc_value = device->brightness->count - 3 - bqc_value;
|
|
|
+
|
|
|
+ level = device->brightness->levels[bqc_value + 2];
|
|
|
+ } else {
|
|
|
+ level = bqc_value;
|
|
|
+ }
|
|
|
+
|
|
|
+ level += bqc_offset_aml_bug_workaround;
|
|
|
+
|
|
|
+ return level;
|
|
|
+}
|
|
|
+
|
|
|
static int
|
|
|
acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
|
|
|
- unsigned long long *level, int init)
|
|
|
+ unsigned long long *level, bool raw)
|
|
|
{
|
|
|
acpi_status status = AE_OK;
|
|
|
int i;
|
|
@@ -463,29 +497,30 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,
|
|
|
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];
|
|
|
-
|
|
|
+ if (raw) {
|
|
|
+ /*
|
|
|
+ * Caller has indicated he wants the raw
|
|
|
+ * value returned by _BQC, so don't furtherly
|
|
|
+ * mess with the value.
|
|
|
+ */
|
|
|
+ return 0;
|
|
|
}
|
|
|
- *level += bqc_offset_aml_bug_workaround;
|
|
|
+
|
|
|
+ *level = acpi_video_bqc_value_to_level(device, *level);
|
|
|
+
|
|
|
for (i = 2; i < device->brightness->count; i++)
|
|
|
if (device->brightness->levels[i] == *level) {
|
|
|
device->brightness->curr = *level;
|
|
|
return 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;
|
|
|
- }
|
|
|
+ /*
|
|
|
+ * 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?
|
|
@@ -597,6 +632,56 @@ acpi_video_cmp_level(const void *a, const void *b)
|
|
|
return *(int *)a - *(int *)b;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Decides if _BQC/_BCQ for this system is usable
|
|
|
+ *
|
|
|
+ * We do this by changing the level first and then read out the current
|
|
|
+ * brightness level, if the value does not match, find out if it is using
|
|
|
+ * index. If not, clear the _BQC/_BCQ capability.
|
|
|
+ */
|
|
|
+static int acpi_video_bqc_quirk(struct acpi_video_device *device,
|
|
|
+ int max_level, int current_level)
|
|
|
+{
|
|
|
+ struct acpi_video_device_brightness *br = device->brightness;
|
|
|
+ int result;
|
|
|
+ unsigned long long level;
|
|
|
+ int test_level;
|
|
|
+
|
|
|
+ /* don't mess with existing known broken systems */
|
|
|
+ if (bqc_offset_aml_bug_workaround)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Some systems always report current brightness level as maximum
|
|
|
+ * through _BQC, we need to test another value for them.
|
|
|
+ */
|
|
|
+ test_level = current_level == max_level ? br->levels[2] : max_level;
|
|
|
+
|
|
|
+ result = acpi_video_device_lcd_set_level(device, test_level);
|
|
|
+ if (result)
|
|
|
+ return result;
|
|
|
+
|
|
|
+ result = acpi_video_device_lcd_get_level_current(device, &level, true);
|
|
|
+ if (result)
|
|
|
+ return result;
|
|
|
+
|
|
|
+ if (level != test_level) {
|
|
|
+ /* buggy _BQC found, need to find out if it uses index */
|
|
|
+ if (level < br->count) {
|
|
|
+ if (br->flags._BCL_reversed)
|
|
|
+ level = br->count - 3 - level;
|
|
|
+ if (br->levels[level + 2] == test_level)
|
|
|
+ br->flags._BQC_use_index = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!br->flags._BQC_use_index)
|
|
|
+ device->cap._BQC = device->cap._BCQ = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
/*
|
|
|
* Arg:
|
|
|
* device : video output device (LCD, CRT, ..)
|
|
@@ -703,42 +788,36 @@ 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, 1);
|
|
|
- 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);
|
|
|
+ result = acpi_video_device_lcd_get_level_current(device,
|
|
|
+ &level_old, true);
|
|
|
if (result)
|
|
|
goto out_free_levels;
|
|
|
|
|
|
- result = acpi_video_device_lcd_get_level_current(device, &level, 0);
|
|
|
+ result = acpi_video_bqc_quirk(device, max_level, level_old);
|
|
|
if (result)
|
|
|
goto out_free_levels;
|
|
|
+ /*
|
|
|
+ * cap._BQC may get cleared due to _BQC is found to be broken
|
|
|
+ * in acpi_video_bqc_quirk, so check again here.
|
|
|
+ */
|
|
|
+ if (!device->cap._BQC)
|
|
|
+ goto set_level;
|
|
|
|
|
|
- br->flags._BQC_use_index = (level == max_level ? 0 : 1);
|
|
|
-
|
|
|
- if (!br->flags._BQC_use_index) {
|
|
|
+ if (use_bios_initial_backlight) {
|
|
|
+ level = acpi_video_bqc_value_to_level(device, level_old);
|
|
|
/*
|
|
|
- * Set the backlight to the initial state.
|
|
|
- * On some buggy laptops, _BQC returns an uninitialized value
|
|
|
- * when invoked for the first time, i.e. level_old is invalid.
|
|
|
- * set the backlight to max_level in this case
|
|
|
+ * On some buggy laptops, _BQC returns an uninitialized
|
|
|
+ * value when invoked for the first time, i.e.
|
|
|
+ * level_old is invalid (no matter whether it's a level
|
|
|
+ * or an index). Set the backlight to max_level in this case.
|
|
|
*/
|
|
|
- if (use_bios_initial_backlight) {
|
|
|
- for (i = 2; i < br->count; i++)
|
|
|
- if (level_old == br->levels[i])
|
|
|
- level = level_old;
|
|
|
- }
|
|
|
- goto set_level;
|
|
|
+ for (i = 2; i < br->count; i++)
|
|
|
+ if (level_old == br->levels[i])
|
|
|
+ break;
|
|
|
+ if (i == br->count)
|
|
|
+ level = max_level;
|
|
|
}
|
|
|
|
|
|
- if (br->flags._BCL_reversed)
|
|
|
- level_old = (br->count - 1) - level_old;
|
|
|
- level = br->levels[level_old];
|
|
|
-
|
|
|
set_level:
|
|
|
result = acpi_video_device_lcd_set_level(device, level);
|
|
|
if (result)
|
|
@@ -996,53 +1075,51 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
|
|
|
struct acpi_video_device *data;
|
|
|
struct acpi_video_device_attrib* attribute;
|
|
|
|
|
|
- if (!device || !video)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
status =
|
|
|
acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id);
|
|
|
- if (ACPI_SUCCESS(status)) {
|
|
|
-
|
|
|
- data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
|
|
|
- if (!data)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
|
|
|
- strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
|
|
|
- device->driver_data = data;
|
|
|
-
|
|
|
- data->device_id = device_id;
|
|
|
- data->video = video;
|
|
|
- data->dev = device;
|
|
|
+ /* Some device omits _ADR, we skip them instead of fail */
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
+ return 0;
|
|
|
|
|
|
- attribute = acpi_video_get_device_attr(video, device_id);
|
|
|
+ data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);
|
|
|
+ if (!data)
|
|
|
+ return -ENOMEM;
|
|
|
|
|
|
- if((attribute != NULL) && attribute->device_id_scheme) {
|
|
|
- switch (attribute->display_type) {
|
|
|
- case ACPI_VIDEO_DISPLAY_CRT:
|
|
|
- data->flags.crt = 1;
|
|
|
- break;
|
|
|
- case ACPI_VIDEO_DISPLAY_TV:
|
|
|
- data->flags.tvout = 1;
|
|
|
- break;
|
|
|
- case ACPI_VIDEO_DISPLAY_DVI:
|
|
|
- data->flags.dvi = 1;
|
|
|
- break;
|
|
|
- case ACPI_VIDEO_DISPLAY_LCD:
|
|
|
- data->flags.lcd = 1;
|
|
|
- break;
|
|
|
- default:
|
|
|
- data->flags.unknown = 1;
|
|
|
- break;
|
|
|
- }
|
|
|
- if(attribute->bios_can_detect)
|
|
|
- data->flags.bios = 1;
|
|
|
- } else {
|
|
|
- /* Check for legacy IDs */
|
|
|
- device_type = acpi_video_get_device_type(video,
|
|
|
- device_id);
|
|
|
- /* Ignore bits 16 and 18-20 */
|
|
|
- switch (device_type & 0xffe2ffff) {
|
|
|
+ strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME);
|
|
|
+ strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS);
|
|
|
+ device->driver_data = data;
|
|
|
+
|
|
|
+ data->device_id = device_id;
|
|
|
+ data->video = video;
|
|
|
+ data->dev = device;
|
|
|
+
|
|
|
+ attribute = acpi_video_get_device_attr(video, device_id);
|
|
|
+
|
|
|
+ if((attribute != NULL) && attribute->device_id_scheme) {
|
|
|
+ switch (attribute->display_type) {
|
|
|
+ case ACPI_VIDEO_DISPLAY_CRT:
|
|
|
+ data->flags.crt = 1;
|
|
|
+ break;
|
|
|
+ case ACPI_VIDEO_DISPLAY_TV:
|
|
|
+ data->flags.tvout = 1;
|
|
|
+ break;
|
|
|
+ case ACPI_VIDEO_DISPLAY_DVI:
|
|
|
+ data->flags.dvi = 1;
|
|
|
+ break;
|
|
|
+ case ACPI_VIDEO_DISPLAY_LCD:
|
|
|
+ data->flags.lcd = 1;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ data->flags.unknown = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if(attribute->bios_can_detect)
|
|
|
+ data->flags.bios = 1;
|
|
|
+ } else {
|
|
|
+ /* Check for legacy IDs */
|
|
|
+ device_type = acpi_video_get_device_type(video, device_id);
|
|
|
+ /* Ignore bits 16 and 18-20 */
|
|
|
+ switch (device_type & 0xffe2ffff) {
|
|
|
case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR:
|
|
|
data->flags.crt = 1;
|
|
|
break;
|
|
@@ -1054,34 +1131,24 @@ acpi_video_bus_get_one_device(struct acpi_device *device,
|
|
|
break;
|
|
|
default:
|
|
|
data->flags.unknown = 1;
|
|
|
- }
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- acpi_video_device_bind(video, data);
|
|
|
- acpi_video_device_find_cap(data);
|
|
|
-
|
|
|
- status = acpi_install_notify_handler(device->handle,
|
|
|
- ACPI_DEVICE_NOTIFY,
|
|
|
- acpi_video_device_notify,
|
|
|
- data);
|
|
|
- if (ACPI_FAILURE(status)) {
|
|
|
- printk(KERN_ERR PREFIX
|
|
|
- "Error installing notify handler\n");
|
|
|
- if(data->brightness)
|
|
|
- kfree(data->brightness->levels);
|
|
|
- kfree(data->brightness);
|
|
|
- kfree(data);
|
|
|
- return -ENODEV;
|
|
|
- }
|
|
|
+ acpi_video_device_bind(video, data);
|
|
|
+ acpi_video_device_find_cap(data);
|
|
|
|
|
|
- mutex_lock(&video->device_list_lock);
|
|
|
- list_add_tail(&data->entry, &video->video_device_list);
|
|
|
- mutex_unlock(&video->device_list_lock);
|
|
|
+ status = acpi_install_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
|
|
|
+ acpi_video_device_notify, data);
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
+ dev_err(&device->dev, "Error installing notify handler\n");
|
|
|
+ else
|
|
|
+ data->flags.notify = 1;
|
|
|
|
|
|
- return 0;
|
|
|
- }
|
|
|
+ mutex_lock(&video->device_list_lock);
|
|
|
+ list_add_tail(&data->entry, &video->video_device_list);
|
|
|
+ mutex_unlock(&video->device_list_lock);
|
|
|
|
|
|
- return -ENOENT;
|
|
|
+ return status;
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -1268,7 +1335,8 @@ acpi_video_switch_brightness(struct acpi_video_device *device, int event)
|
|
|
goto out;
|
|
|
|
|
|
result = acpi_video_device_lcd_get_level_current(device,
|
|
|
- &level_current, 0);
|
|
|
+ &level_current,
|
|
|
+ false);
|
|
|
if (result)
|
|
|
goto out;
|
|
|
|
|
@@ -1373,9 +1441,8 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video,
|
|
|
|
|
|
status = acpi_video_bus_get_one_device(dev, video);
|
|
|
if (status) {
|
|
|
- printk(KERN_WARNING PREFIX
|
|
|
- "Can't attach device\n");
|
|
|
- continue;
|
|
|
+ dev_err(&dev->dev, "Can't attach device\n");
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
return status;
|
|
@@ -1388,13 +1455,14 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device)
|
|
|
if (!device || !device->video)
|
|
|
return -ENOENT;
|
|
|
|
|
|
- status = acpi_remove_notify_handler(device->dev->handle,
|
|
|
- ACPI_DEVICE_NOTIFY,
|
|
|
- acpi_video_device_notify);
|
|
|
- if (ACPI_FAILURE(status)) {
|
|
|
- printk(KERN_WARNING PREFIX
|
|
|
- "Can't remove video notify handler\n");
|
|
|
+ if (device->flags.notify) {
|
|
|
+ status = acpi_remove_notify_handler(device->dev->handle,
|
|
|
+ ACPI_DEVICE_NOTIFY, acpi_video_device_notify);
|
|
|
+ if (ACPI_FAILURE(status))
|
|
|
+ dev_err(&device->dev->dev,
|
|
|
+ "Can't remove video notify handler\n");
|
|
|
}
|
|
|
+
|
|
|
if (device->backlight) {
|
|
|
backlight_device_unregister(device->backlight);
|
|
|
device->backlight = NULL;
|
|
@@ -1676,7 +1744,7 @@ static int acpi_video_bus_add(struct acpi_device *device)
|
|
|
|
|
|
error = acpi_video_bus_get_devices(video, device);
|
|
|
if (error)
|
|
|
- goto err_free_video;
|
|
|
+ goto err_put_video;
|
|
|
|
|
|
video->input = input = input_allocate_device();
|
|
|
if (!input) {
|