diff options
Diffstat (limited to 'drivers/acpi/acpi_video.c')
| -rw-r--r-- | drivers/acpi/acpi_video.c | 547 |
1 files changed, 284 insertions, 263 deletions
diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index e88fe3632dd6..be8e7e18abca 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1,25 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * video.c - ACPI Video Driver * * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> * Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org> * Copyright (C) 2006 Thomas Tuttle <linux-kernel@ttuttle.net> - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +#define pr_fmt(fmt) "ACPI: video: " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -38,22 +27,18 @@ #include <linux/acpi.h> #include <acpi/video.h> #include <linux/uaccess.h> - -#define PREFIX "ACPI: " +#include <linux/string_choices.h> #define ACPI_VIDEO_BUS_NAME "Video Bus" #define ACPI_VIDEO_DEVICE_NAME "Video Device" #define MAX_NAME_LEN 20 -#define _COMPONENT ACPI_VIDEO_COMPONENT -ACPI_MODULE_NAME("video"); - MODULE_AUTHOR("Bruno Ducrot"); MODULE_DESCRIPTION("ACPI Video Driver"); MODULE_LICENSE("GPL"); -static bool brightness_switch_enabled = 1; +static bool brightness_switch_enabled = true; module_param(brightness_switch_enabled, bool, 0644); /* @@ -63,9 +48,6 @@ module_param(brightness_switch_enabled, bool, 0644); static bool allow_duplicates; module_param(allow_duplicates, bool, 0644); -static int disable_backlight_sysfs_if = -1; -module_param(disable_backlight_sysfs_if, int, 0444); - #define REPORT_OUTPUT_KEY_EVENTS 0x01 #define REPORT_BRIGHTNESS_KEY_EVENTS 0x02 static int report_key_events = -1; @@ -73,6 +55,12 @@ module_param(report_key_events, int, 0644); MODULE_PARM_DESC(report_key_events, "0: none, 1: output changes, 2: brightness changes, 3: all"); +static int hw_changes_brightness = -1; +module_param(hw_changes_brightness, int, 0644); +MODULE_PARM_DESC(hw_changes_brightness, + "Set this to 1 on buggy hw which changes the brightness itself when " + "a hotkey is pressed: -1: auto, 0: normal 1: hw-changes-brightness"); + /* * Whether the struct acpi_video_device_attrib::device_id_scheme bit should be * assumed even if not actually set. @@ -80,17 +68,17 @@ MODULE_PARM_DESC(report_key_events, static bool device_id_scheme = false; module_param(device_id_scheme, bool, 0444); -static bool only_lcd = false; -module_param(only_lcd, bool, 0444); +static int only_lcd; +module_param(only_lcd, int, 0444); +static bool may_report_brightness_keys; static int register_count; static DEFINE_MUTEX(register_count_mutex); static DEFINE_MUTEX(video_list_lock); static LIST_HEAD(video_bus_head); static int acpi_video_bus_add(struct acpi_device *device); -static int acpi_video_bus_remove(struct acpi_device *device); -static void acpi_video_bus_notify(struct acpi_device *device, u32 event); -void acpi_video_detect_exit(void); +static void acpi_video_bus_remove(struct acpi_device *device); +static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data); /* * Indices in the _BCL method response: the first two items are special, @@ -117,7 +105,6 @@ static struct acpi_driver acpi_video_bus = { .ops = { .add = acpi_video_bus_add, .remove = acpi_video_bus_remove, - .notify = acpi_video_bus_notify, }, }; @@ -267,8 +254,7 @@ static const struct backlight_ops acpi_backlight_ops = { static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned long *state) { - struct acpi_device *device = cooling_dev->devdata; - struct acpi_video_device *video = acpi_driver_data(device); + struct acpi_video_device *video = cooling_dev->devdata; *state = video->brightness->count - ACPI_VIDEO_FIRST_LEVEL - 1; return 0; @@ -277,8 +263,7 @@ static int video_get_max_state(struct thermal_cooling_device *cooling_dev, static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long *state) { - struct acpi_device *device = cooling_dev->devdata; - struct acpi_video_device *video = acpi_driver_data(device); + struct acpi_video_device *video = cooling_dev->devdata; unsigned long long level; int offset; @@ -297,8 +282,7 @@ static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, static int video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state) { - struct acpi_device *device = cooling_dev->devdata; - struct acpi_video_device *video = acpi_driver_data(device); + struct acpi_video_device *video = cooling_dev->devdata; int level; if (state >= video->brightness->count - ACPI_VIDEO_FIRST_LEVEL) @@ -333,11 +317,11 @@ acpi_video_device_lcd_query_levels(acpi_handle handle, *levels = NULL; status = acpi_evaluate_object(handle, "_BCL", NULL, &buffer); - if (!ACPI_SUCCESS(status)) + if (ACPI_FAILURE(status)) return status; obj = (union acpi_object *)buffer.pointer; if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { - printk(KERN_ERR PREFIX "Invalid _BCL data\n"); + acpi_handle_info(handle, "Invalid _BCL data\n"); status = -EFAULT; goto err; } @@ -361,7 +345,7 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) status = acpi_execute_simple_method(device->dev->handle, "_BCM", level); if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, "Evaluating _BCM failed")); + acpi_handle_info(device->dev->handle, "_BCM evaluation failed\n"); return -EIO; } @@ -375,7 +359,7 @@ acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) return 0; } - ACPI_ERROR((AE_INFO, "Current brightness invalid")); + acpi_handle_info(device->dev->handle, "Current brightness invalid\n"); return -EINVAL; } @@ -391,14 +375,6 @@ static int video_set_bqc_offset(const struct dmi_system_id *d) return 0; } -static int video_disable_backlight_sysfs_if( - const struct dmi_system_id *d) -{ - if (disable_backlight_sysfs_if == -1) - disable_backlight_sysfs_if = 1; - return 0; -} - static int video_set_device_id_scheme(const struct dmi_system_id *d) { device_id_scheme = true; @@ -418,7 +394,15 @@ static int video_set_report_key_events(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id video_dmi_table[] = { +static int video_hw_changes_brightness( + const struct dmi_system_id *d) +{ + if (hw_changes_brightness == -1) + hw_changes_brightness = 1; + return 0; +} + +static const struct dmi_system_id video_dmi_table[] = { /* * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 */ @@ -464,40 +448,6 @@ static struct dmi_system_id video_dmi_table[] = { }, /* - * Some machines have a broken acpi-video interface for brightness - * control, but still need an acpi_video_device_lcd_set_level() call - * on resume to turn the backlight power on. We Enable backlight - * control on these systems, but do not register a backlight sysfs - * as brightness control does not work. - */ - { - /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */ - .callback = video_disable_backlight_sysfs_if, - .ident = "Toshiba Portege R700", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R700"), - }, - }, - { - /* https://bugs.freedesktop.org/show_bug.cgi?id=82634 */ - .callback = video_disable_backlight_sysfs_if, - .ident = "Toshiba Portege R830", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R830"), - }, - }, - { - /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */ - .callback = video_disable_backlight_sysfs_if, - .ident = "Toshiba Satellite R830", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), - DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE R830"), - }, - }, - /* * Some machine's _DOD IDs don't have bit 31(Device ID Scheme) set * but the IDs actually follow the Device ID Scheme. */ @@ -542,6 +492,39 @@ static struct dmi_system_id video_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), }, }, + { + .callback = video_set_report_key_events, + .driver_data = (void *)((uintptr_t)REPORT_BRIGHTNESS_KEY_EVENTS), + .ident = "Dell Vostro 3350", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"), + }, + }, + { + .callback = video_set_report_key_events, + .driver_data = (void *)((uintptr_t)REPORT_BRIGHTNESS_KEY_EVENTS), + .ident = "COLORFUL X15 AT 23", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "COLORFUL"), + DMI_MATCH(DMI_PRODUCT_NAME, "X15 AT 23"), + }, + }, + /* + * Some machines change the brightness themselves when a brightness + * hotkey gets pressed, despite us telling them not to. In this case + * acpi_video_device_notify() should only call backlight_force_update( + * BACKLIGHT_UPDATE_HOTKEY) and not do anything else. + */ + { + /* https://bugzilla.kernel.org/show_bug.cgi?id=204077 */ + .callback = video_hw_changes_brightness, + .ident = "Packard Bell EasyNote MZ35", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Packard Bell"), + DMI_MATCH(DMI_PRODUCT_NAME, "EasyNote MZ35"), + }, + }, {} }; @@ -562,7 +545,7 @@ acpi_video_bqc_value_to_level(struct acpi_video_device *device, ACPI_VIDEO_FIRST_LEVEL - 1 - bqc_value; level = device->brightness->levels[bqc_value + - ACPI_VIDEO_FIRST_LEVEL]; + ACPI_VIDEO_FIRST_LEVEL]; } else { level = bqc_value; } @@ -606,9 +589,8 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, * BQC returned an invalid level. * Stop using it. */ - ACPI_WARNING((AE_INFO, - "%s returned an invalid level", - buf)); + acpi_handle_info(device->dev->handle, + "%s returned an invalid level", buf); device->cap._BQC = device->cap._BCQ = 0; } else { /* @@ -619,7 +601,8 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, * ACPI video backlight still works w/ buggy _BQC. * http://bugzilla.kernel.org/show_bug.cgi?id=12233 */ - ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf)); + acpi_handle_info(device->dev->handle, + "%s evaluation failed", buf); device->cap._BQC = device->cap._BCQ = 0; } } @@ -628,43 +611,62 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, return 0; } +/** + * acpi_video_device_EDID() - Get EDID from ACPI _DDC + * @device: video output device (LCD, CRT, ..) + * @edid: address for returned EDID pointer + * @length: _DDC length to request (must be a multiple of 128) + * + * Get EDID from ACPI _DDC. On success, a pointer to the EDID data is written + * to the @edid address, and the length of the EDID is returned. The caller is + * responsible for freeing the edid pointer. + * + * Return the length of EDID (positive value) on success or error (negative + * value). + */ static int -acpi_video_device_EDID(struct acpi_video_device *device, - union acpi_object **edid, ssize_t length) +acpi_video_device_EDID(struct acpi_video_device *device, void **edid, int length) { - int status; + acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list args = { 1, &arg0 }; - + int ret; *edid = NULL; if (!device) return -ENODEV; - if (length == 128) - arg0.integer.value = 1; - else if (length == 256) - arg0.integer.value = 2; - else + if (!length || (length % 128)) return -EINVAL; + arg0.integer.value = length / 128; + status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer); if (ACPI_FAILURE(status)) return -ENODEV; obj = buffer.pointer; - if (obj && obj->type == ACPI_TYPE_BUFFER) - *edid = obj; - else { - printk(KERN_ERR PREFIX "Invalid _DDC data\n"); - status = -EFAULT; - kfree(obj); + /* + * Some buggy implementations incorrectly return the EDID buffer in an ACPI package. + * In this case, extract the buffer from the package. + */ + if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 1) + obj = &obj->package.elements[0]; + + if (obj && obj->type == ACPI_TYPE_BUFFER) { + *edid = kmemdup(obj->buffer.pointer, obj->buffer.length, GFP_KERNEL); + ret = *edid ? obj->buffer.length : -ENOMEM; + } else { + acpi_handle_debug(device->dev->handle, + "Invalid _DDC data for length %d\n", length); + ret = -EFAULT; } - return status; + kfree(buffer.pointer); + return ret; } /* bus */ @@ -683,9 +685,13 @@ acpi_video_device_EDID(struct acpi_video_device *device, * event notify code. * lcd_flag : * 0. The system BIOS should automatically control the brightness level - * of the LCD when the power changes from AC to DC + * of the LCD when: + * - the power changes from AC to DC (ACPI appendix B) + * - a brightness hotkey gets pressed (implied by Win7/8 backlight docs) * 1. The system BIOS should NOT automatically control the brightness - * level of the LCD when the power changes from AC to DC. + * level of the LCD when: + * - the power changes from AC to DC (ACPI appendix B) + * - a brightness hotkey gets pressed (implied by Win7/8 backlight docs) * Return Value: * -EINVAL wrong arg. */ @@ -807,10 +813,9 @@ int acpi_video_get_levels(struct acpi_device *device, int result = 0; u32 value; - if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device->handle, - &obj))) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available " - "LCD brightness level\n")); + if (ACPI_FAILURE(acpi_video_device_lcd_query_levels(device->handle, &obj))) { + acpi_handle_debug(device->handle, + "Could not query available LCD brightness level\n"); result = -ENODEV; goto out; } @@ -822,7 +827,6 @@ int acpi_video_get_levels(struct acpi_device *device, br = kzalloc(sizeof(*br), GFP_KERNEL); if (!br) { - printk(KERN_ERR "can't allocate memory\n"); result = -ENOMEM; goto out; } @@ -832,8 +836,9 @@ int acpi_video_get_levels(struct acpi_device *device, * in order to account for buggy BIOS which don't export the first two * special levels (see below) */ - br->levels = kmalloc((obj->package.count + ACPI_VIDEO_FIRST_LEVEL) * - sizeof(*br->levels), GFP_KERNEL); + br->levels = kmalloc_array(obj->package.count + ACPI_VIDEO_FIRST_LEVEL, + sizeof(*br->levels), + GFP_KERNEL); if (!br->levels) { result = -ENOMEM; goto out_free; @@ -842,7 +847,7 @@ int acpi_video_get_levels(struct acpi_device *device, for (i = 0; i < obj->package.count; i++) { o = (union acpi_object *)&obj->package.elements[i]; if (o->type != ACPI_TYPE_INTEGER) { - printk(KERN_ERR PREFIX "Invalid data\n"); + acpi_handle_info(device->handle, "Invalid data\n"); continue; } value = (u32) o->integer.value; @@ -879,7 +884,8 @@ int acpi_video_get_levels(struct acpi_device *device, br->levels[i] = br->levels[i - level_ac_battery]; count += level_ac_battery; } else if (level_ac_battery > ACPI_VIDEO_FIRST_LEVEL) - ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package")); + acpi_handle_info(device->handle, + "Too many duplicates in _BCL package"); /* Check if the _BCL package is in a reversed order */ if (max_level == br->levels[ACPI_VIDEO_FIRST_LEVEL]) { @@ -889,8 +895,8 @@ int acpi_video_get_levels(struct acpi_device *device, sizeof(br->levels[ACPI_VIDEO_FIRST_LEVEL]), acpi_video_cmp_level, NULL); } else if (max_level != br->levels[count - 1]) - ACPI_ERROR((AE_INFO, - "Found unordered _BCL package")); + acpi_handle_info(device->handle, + "Found unordered _BCL package"); br->count = count; *dev_br = br; @@ -922,7 +928,7 @@ acpi_video_init_brightness(struct acpi_video_device *device) int i, max_level = 0; unsigned long long level, level_old; struct acpi_video_device_brightness *br = NULL; - int result = -EINVAL; + int result; result = acpi_video_get_levels(device->dev, &br, &max_level); if (result) @@ -968,9 +974,9 @@ set_level: if (result) goto out_free_levels; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "found %d brightness levels\n", - br->count - ACPI_VIDEO_FIRST_LEVEL)); + acpi_handle_debug(device->dev->handle, "found %d brightness levels\n", + br->count - ACPI_VIDEO_FIRST_LEVEL); + return 0; out_free_levels: @@ -1002,7 +1008,8 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) if (acpi_has_method(device->dev->handle, "_BQC")) { device->cap._BQC = 1; } else if (acpi_has_method(device->dev->handle, "_BCQ")) { - printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n"); + acpi_handle_info(device->dev->handle, + "_BCQ is used instead of _BQC\n"); device->cap._BCQ = 1; } @@ -1062,8 +1069,7 @@ static int acpi_video_bus_check(struct acpi_video_bus *video) /* Does this device support video switching? */ if (video->cap._DOS || video->cap._DOD) { if (!video->cap._DOS) { - printk(KERN_WARNING FW_BUG - "ACPI(%s) defines _DOD but not _DOS\n", + pr_info(FW_BUG "ACPI(%s) defines _DOD but not _DOS\n", acpi_device_bid(video->device)); } video->flags.multihead = 1; @@ -1123,28 +1129,28 @@ acpi_video_get_device_type(struct acpi_video_bus *video, return 0; } -static int -acpi_video_bus_get_one_device(struct acpi_device *device, - struct acpi_video_bus *video) +static int acpi_video_bus_get_one_device(struct acpi_device *device, void *arg) { - unsigned long long device_id; - int status, device_type; - struct acpi_video_device *data; + struct acpi_video_bus *video = arg; struct acpi_video_device_attrib *attribute; + struct acpi_video_device *data; + unsigned long long device_id; + acpi_status status; + int device_type; - status = - acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); - /* Some device omits _ADR, we skip them instead of fail */ + status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); + /* Skip devices without _ADR instead of failing. */ if (ACPI_FAILURE(status)) - return 0; + goto exit; data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); - if (!data) + if (!data) { + dev_dbg(&device->dev, "Cannot attach\n"); return -ENOMEM; + } - strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); - device->driver_data = data; + strscpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); + strscpy(acpi_device_class(device), ACPI_VIDEO_CLASS); data->device_id = device_id; data->video = video; @@ -1196,11 +1202,16 @@ acpi_video_bus_get_one_device(struct acpi_device *device, acpi_video_device_bind(video, data); acpi_video_device_find_cap(data); + if (data->cap._BCM && data->cap._BCL) + may_report_brightness_keys = true; + mutex_lock(&video->device_list_lock); list_add_tail(&data->entry, &video->video_device_list); mutex_unlock(&video->device_list_lock); - return status; +exit: + video->child_count++; + return 0; } /* @@ -1251,7 +1262,8 @@ acpi_video_device_bind(struct acpi_video_bus *video, ids = &video->attached_array[i]; if (device->device_id == (ids->value.int_val & 0xffff)) { ids->bind_info = device; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i)); + acpi_handle_debug(video->device->handle, "%s: %d\n", + __func__, i); } } } @@ -1303,20 +1315,22 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) return AE_NOT_EXIST; status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); - if (!ACPI_SUCCESS(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); + if (ACPI_FAILURE(status)) { + acpi_handle_info(video->device->handle, + "_DOD evaluation failed: %s\n", + acpi_format_exception(status)); return status; } dod = buffer.pointer; if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) { - ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data")); + acpi_handle_info(video->device->handle, "Invalid _DOD data\n"); status = -EFAULT; goto out; } - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n", - dod->package.count)); + acpi_handle_debug(video->device->handle, "Found %d video heads in _DOD\n", + dod->package.count); active_list = kcalloc(1 + dod->package.count, sizeof(struct acpi_video_enumerated_device), @@ -1331,15 +1345,18 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) obj = &dod->package.elements[i]; if (obj->type != ACPI_TYPE_INTEGER) { - printk(KERN_ERR PREFIX - "Invalid _DOD data in element %d\n", i); + acpi_handle_info(video->device->handle, + "Invalid _DOD data in element %d\n", i); continue; } active_list[count].value.int_val = obj->integer.value; active_list[count].bind_info = NULL; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, - (int)obj->integer.value)); + + acpi_handle_debug(video->device->handle, + "_DOD element[%d] = %d\n", i, + (int)obj->integer.value); + count++; } @@ -1369,7 +1386,7 @@ acpi_video_get_next_level(struct acpi_video_device *device, break; } } - /* Ajust level_current to closest available level */ + /* Adjust level_current to closest available level */ level_current += delta; for (i = ACPI_VIDEO_FIRST_LEVEL; i < device->brightness->count; i++) { l = device->brightness->levels[i]; @@ -1430,7 +1447,8 @@ acpi_video_switch_brightness(struct work_struct *work) out: if (result) - printk(KERN_ERR PREFIX "Failed to switch the brightness\n"); + acpi_handle_info(device->dev->handle, + "Failed to switch brightness\n"); } int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, @@ -1438,9 +1456,7 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, { struct acpi_video_bus *video; struct acpi_video_device *video_device; - union acpi_object *buffer = NULL; - acpi_status status; - int i, length; + int i, length, ret; if (!device || !acpi_driver_data(device)) return -EINVAL; @@ -1449,7 +1465,6 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, for (i = 0; i < video->attached_count; i++) { video_device = video->attached_array[i].bind_info; - length = 256; if (!video_device) continue; @@ -1480,21 +1495,11 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, continue; } - status = acpi_video_device_EDID(video_device, &buffer, length); - - if (ACPI_FAILURE(status) || !buffer || - buffer->type != ACPI_TYPE_BUFFER) { - length = 128; - status = acpi_video_device_EDID(video_device, &buffer, - length); - if (ACPI_FAILURE(status) || !buffer || - buffer->type != ACPI_TYPE_BUFFER) { - continue; - } + for (length = 512; length > 0; length -= 128) { + ret = acpi_video_device_EDID(video_device, edid, length); + if (ret > 0) + return ret; } - - *edid = buffer->buffer.pointer; - return length; } return -ENODEV; @@ -1505,9 +1510,6 @@ static int acpi_video_bus_get_devices(struct acpi_video_bus *video, struct acpi_device *device) { - int status = 0; - struct acpi_device *dev; - /* * There are systems where video module known to work fine regardless * of broken _DOD and ignoring returned value here doesn't cause @@ -1515,23 +1517,14 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video, */ acpi_video_device_enumerate(video); - list_for_each_entry(dev, &device->children, node) { - - status = acpi_video_bus_get_one_device(dev, video); - if (status) { - dev_err(&dev->dev, "Can't attach device\n"); - break; - } - video->child_count++; - } - return status; + return acpi_dev_for_each_child(device, acpi_video_bus_get_one_device, video); } /* acpi_video interface */ /* * Win8 requires setting bit2 of _DOS to let firmware know it shouldn't - * preform any automatic brightness change on receiving a notification. + * perform any automatic brightness change on receiving a notification. */ static int acpi_video_bus_start_devices(struct acpi_video_bus *video) { @@ -1545,8 +1538,9 @@ static int acpi_video_bus_stop_devices(struct acpi_video_bus *video) acpi_osi_is_win8() ? 0 : 1); } -static void acpi_video_bus_notify(struct acpi_device *device, u32 event) +static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) { + struct acpi_device *device = data; struct acpi_video_bus *video = acpi_driver_data(device); struct input_dev *input; int keycode = 0; @@ -1580,8 +1574,8 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event) break; default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); + acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n", + event); break; } @@ -1595,8 +1589,6 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event) input_report_key(input, keycode, 0); input_sync(input); } - - return; } static void brightness_switch_event(struct acpi_video_device *video_device, @@ -1624,6 +1616,14 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) bus = video_device->video; input = bus->input; + if (hw_changes_brightness > 0) { + if (video_device->backlight) + backlight_force_update(video_device->backlight, + BACKLIGHT_UPDATE_HOTKEY); + acpi_notifier_call_chain(device, event, 0); + return; + } + switch (event) { case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ brightness_switch_event(video_device, event); @@ -1646,11 +1646,13 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) keycode = KEY_DISPLAY_OFF; break; default: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Unsupported event [0x%x]\n", event)); + acpi_handle_debug(handle, "Unsupported event [0x%x]\n", event); break; } + if (keycode) + may_report_brightness_keys = true; + acpi_notifier_call_chain(device, event, 0); if (keycode && (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS)) { @@ -1659,8 +1661,6 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) input_report_key(input, keycode, 0); input_sync(input); } - - return; } static int acpi_video_resume(struct notifier_block *nb, @@ -1671,24 +1671,23 @@ static int acpi_video_resume(struct notifier_block *nb, int i; switch (val) { - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - case PM_RESTORE_PREPARE: - return NOTIFY_DONE; - } - - video = container_of(nb, struct acpi_video_bus, pm_nb); - - dev_info(&video->device->dev, "Restoring backlight state\n"); + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + case PM_POST_RESTORE: + video = container_of(nb, struct acpi_video_bus, pm_nb); + + dev_info(&video->device->dev, "Restoring backlight state\n"); + + for (i = 0; i < video->attached_count; i++) { + video_device = video->attached_array[i].bind_info; + if (video_device && video_device->brightness) + acpi_video_device_lcd_set_level(video_device, + video_device->brightness->curr); + } - for (i = 0; i < video->attached_count; i++) { - video_device = video->attached_array[i].bind_info; - if (video_device && video_device->brightness) - acpi_video_device_lcd_set_level(video_device, - video_device->brightness->curr); + return NOTIFY_OK; } - - return NOTIFY_OK; + return NOTIFY_DONE; } static acpi_status @@ -1697,13 +1696,12 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context, { 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) + sibling = acpi_fetch_acpi_dev(handle); + if (!sibling) return AE_OK; if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) @@ -1726,20 +1724,17 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) if (result) return; - if (disable_backlight_sysfs_if > 0) - return; - name = kasprintf(GFP_KERNEL, "acpi_video%d", count); if (!name) return; count++; - acpi_get_parent(device->dev->handle, &acpi_parent); - - pdev = acpi_get_pci_dev(acpi_parent); - if (pdev) { - parent = &pdev->dev; - pci_dev_put(pdev); + if (ACPI_SUCCESS(acpi_get_parent(device->dev->handle, &acpi_parent))) { + pdev = acpi_get_pci_dev(acpi_parent); + if (pdev) { + parent = &pdev->dev; + pci_dev_put(pdev); + } } memset(&props, 0, sizeof(struct backlight_properties)); @@ -1764,8 +1759,8 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) device->backlight->props.brightness = acpi_video_get_brightness(device->backlight); - device->cooling_dev = thermal_cooling_device_register("LCD", - device->dev, &video_cooling_ops); + device->cooling_dev = thermal_cooling_device_register("LCD", device, + &video_cooling_ops); if (IS_ERR(device->cooling_dev)) { /* * Set cooling_dev to NULL so we don't crash trying to free it. @@ -1783,11 +1778,12 @@ static void acpi_video_dev_register_backlight(struct acpi_video_device *device) &device->cooling_dev->device.kobj, "thermal_cooling"); if (result) - printk(KERN_ERR PREFIX "Create sysfs link\n"); + pr_info("sysfs link creation failed\n"); + result = sysfs_create_link(&device->cooling_dev->device.kobj, &device->dev->dev.kobj, "device"); if (result) - printk(KERN_ERR PREFIX "Create sysfs link\n"); + pr_info("Reverse sysfs link creation failed\n"); } static void acpi_video_run_bcl_for_osi(struct acpi_video_bus *video) @@ -1826,8 +1822,6 @@ static int acpi_video_bus_register_backlight(struct acpi_video_bus *video) if (video->backlight_registered) return 0; - acpi_video_run_bcl_for_osi(video); - if (acpi_video_get_backlight_type() != acpi_backlight_video) return 0; @@ -1965,8 +1959,10 @@ static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video) struct acpi_video_device *dev; mutex_lock(&video->device_list_lock); - list_for_each_entry(dev, &video->video_device_list, entry) + list_for_each_entry(dev, &video->video_device_list, entry) { acpi_video_dev_remove_notify_handler(dev); + cancel_delayed_work_sync(&dev->switch_brightness_work); + } mutex_unlock(&video->device_list_lock); acpi_video_bus_stop_devices(video); @@ -1993,15 +1989,16 @@ static int instance; static int acpi_video_bus_add(struct acpi_device *device) { struct acpi_video_bus *video; + bool auto_detect; int error; acpi_status status; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, - device->parent->handle, 1, + acpi_dev_parent(device)->handle, 1, acpi_video_bus_match, NULL, device, NULL); if (status == AE_ALREADY_EXISTS) { - printk(KERN_WARNING FW_BUG + pr_info(FW_BUG "Duplicate ACPI video bus devices for the" " same VGA controller, please try module " "parameter \"video.allow_duplicates=1\"" @@ -2028,8 +2025,8 @@ static int acpi_video_bus_add(struct acpi_device *device) } video->device = device; - strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); - strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); + strscpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); + strscpy(acpi_device_class(device), ACPI_VIDEO_CLASS); device->driver_data = video; acpi_video_bus_find_cap(video); @@ -2044,20 +2041,54 @@ static int acpi_video_bus_add(struct acpi_device *device) if (error) goto err_put_video; - printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", + /* + * HP ZBook Fury 16 G10 requires ACPI video's child devices have _PS0 + * evaluated to have functional panel brightness control. + */ + acpi_device_fix_up_power_children(device); + + pr_info("%s [%s] (multi-head: %s rom: %s post: %s)\n", ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), - video->flags.multihead ? "yes" : "no", - video->flags.rom ? "yes" : "no", - video->flags.post ? "yes" : "no"); + str_yes_no(video->flags.multihead), + str_yes_no(video->flags.rom), + str_yes_no(video->flags.post)); mutex_lock(&video_list_lock); list_add_tail(&video->entry, &video_bus_head); mutex_unlock(&video_list_lock); - acpi_video_bus_register_backlight(video); - acpi_video_bus_add_notify_handler(video); + /* + * If backlight-type auto-detection is used then a native backlight may + * show up later and this may change the result from video to native. + * Therefor normally the userspace visible /sys/class/backlight device + * gets registered separately by the GPU driver calling + * acpi_video_register_backlight() when an internal panel is detected. + * Register the backlight now when not using auto-detection, so that + * when the kernel cmdline or DMI-quirks are used the backlight will + * get registered even if acpi_video_register_backlight() is not called. + */ + acpi_video_run_bcl_for_osi(video); + if (__acpi_video_get_backlight_type(false, &auto_detect) == acpi_backlight_video && + !auto_detect) + acpi_video_bus_register_backlight(video); + + error = acpi_video_bus_add_notify_handler(video); + if (error) + goto err_del; + + error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_video_bus_notify, device); + if (error) + goto err_remove; return 0; +err_remove: + acpi_video_bus_remove_notify_handler(video); +err_del: + mutex_lock(&video_list_lock); + list_del(&video->entry); + mutex_unlock(&video_list_lock); + acpi_video_bus_unregister_backlight(video); err_put_video: acpi_video_bus_put_devices(video); kfree(video->attached_array); @@ -2068,28 +2099,29 @@ err_free_video: return error; } -static int acpi_video_bus_remove(struct acpi_device *device) +static void acpi_video_bus_remove(struct acpi_device *device) { struct acpi_video_bus *video = NULL; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; video = acpi_driver_data(device); - acpi_video_bus_remove_notify_handler(video); - acpi_video_bus_unregister_backlight(video); - acpi_video_bus_put_devices(video); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_video_bus_notify); mutex_lock(&video_list_lock); list_del(&video->entry); mutex_unlock(&video_list_lock); + acpi_video_bus_remove_notify_handler(video); + acpi_video_bus_unregister_backlight(video); + acpi_video_bus_put_devices(video); + kfree(video->attached_array); kfree(video); - - return 0; } static int __init is_i740(struct pci_dev *dev) @@ -2131,7 +2163,7 @@ int acpi_video_register(void) 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. + * don't register the acpi_video_bus again and return no error. */ goto leave; } @@ -2160,34 +2192,26 @@ void acpi_video_unregister(void) if (register_count) { acpi_bus_unregister_driver(&acpi_video_bus); register_count = 0; + may_report_brightness_keys = false; } mutex_unlock(®ister_count_mutex); } EXPORT_SYMBOL(acpi_video_unregister); -void acpi_video_unregister_backlight(void) +void acpi_video_register_backlight(void) { struct acpi_video_bus *video; - mutex_lock(®ister_count_mutex); - if (register_count) { - mutex_lock(&video_list_lock); - list_for_each_entry(video, &video_bus_head, entry) - acpi_video_bus_unregister_backlight(video); - mutex_unlock(&video_list_lock); - } - mutex_unlock(®ister_count_mutex); + mutex_lock(&video_list_lock); + list_for_each_entry(video, &video_bus_head, entry) + acpi_video_bus_register_backlight(video); + mutex_unlock(&video_list_lock); } +EXPORT_SYMBOL(acpi_video_register_backlight); bool acpi_video_handles_brightness_key_presses(void) { - bool have_video_busses; - - mutex_lock(&video_list_lock); - have_video_busses = !list_empty(&video_bus_head); - mutex_unlock(&video_list_lock); - - return have_video_busses && + return may_report_brightness_keys && (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS); } EXPORT_SYMBOL(acpi_video_handles_brightness_key_presses); @@ -2220,10 +2244,7 @@ static int __init acpi_video_init(void) static void __exit acpi_video_exit(void) { - acpi_video_detect_exit(); acpi_video_unregister(); - - return; } module_init(acpi_video_init); |
