18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * video.c - ACPI Video Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org> 78c2ecf20Sopenharmony_ci * Copyright (C) 2006 Thomas Tuttle <linux-kernel@ttuttle.net> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/list.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/input.h> 178c2ecf20Sopenharmony_ci#include <linux/backlight.h> 188c2ecf20Sopenharmony_ci#include <linux/thermal.h> 198c2ecf20Sopenharmony_ci#include <linux/sort.h> 208c2ecf20Sopenharmony_ci#include <linux/pci.h> 218c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci#include <linux/dmi.h> 248c2ecf20Sopenharmony_ci#include <linux/suspend.h> 258c2ecf20Sopenharmony_ci#include <linux/acpi.h> 268c2ecf20Sopenharmony_ci#include <acpi/video.h> 278c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define PREFIX "ACPI: " 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define ACPI_VIDEO_BUS_NAME "Video Bus" 328c2ecf20Sopenharmony_ci#define ACPI_VIDEO_DEVICE_NAME "Video Device" 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define MAX_NAME_LEN 20 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define _COMPONENT ACPI_VIDEO_COMPONENT 378c2ecf20Sopenharmony_ciACPI_MODULE_NAME("video"); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Bruno Ducrot"); 408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ACPI Video Driver"); 418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic bool brightness_switch_enabled = true; 448c2ecf20Sopenharmony_cimodule_param(brightness_switch_enabled, bool, 0644); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* 478c2ecf20Sopenharmony_ci * By default, we don't allow duplicate ACPI video bus devices 488c2ecf20Sopenharmony_ci * under the same VGA controller 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_cistatic bool allow_duplicates; 518c2ecf20Sopenharmony_cimodule_param(allow_duplicates, bool, 0644); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic int disable_backlight_sysfs_if = -1; 548c2ecf20Sopenharmony_cimodule_param(disable_backlight_sysfs_if, int, 0444); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define REPORT_OUTPUT_KEY_EVENTS 0x01 578c2ecf20Sopenharmony_ci#define REPORT_BRIGHTNESS_KEY_EVENTS 0x02 588c2ecf20Sopenharmony_cistatic int report_key_events = -1; 598c2ecf20Sopenharmony_cimodule_param(report_key_events, int, 0644); 608c2ecf20Sopenharmony_ciMODULE_PARM_DESC(report_key_events, 618c2ecf20Sopenharmony_ci "0: none, 1: output changes, 2: brightness changes, 3: all"); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int hw_changes_brightness = -1; 648c2ecf20Sopenharmony_cimodule_param(hw_changes_brightness, int, 0644); 658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(hw_changes_brightness, 668c2ecf20Sopenharmony_ci "Set this to 1 on buggy hw which changes the brightness itself when " 678c2ecf20Sopenharmony_ci "a hotkey is pressed: -1: auto, 0: normal 1: hw-changes-brightness"); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Whether the struct acpi_video_device_attrib::device_id_scheme bit should be 718c2ecf20Sopenharmony_ci * assumed even if not actually set. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistatic bool device_id_scheme = false; 748c2ecf20Sopenharmony_cimodule_param(device_id_scheme, bool, 0444); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int only_lcd = -1; 778c2ecf20Sopenharmony_cimodule_param(only_lcd, int, 0444); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int register_count; 808c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(register_count_mutex); 818c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(video_list_lock); 828c2ecf20Sopenharmony_cistatic LIST_HEAD(video_bus_head); 838c2ecf20Sopenharmony_cistatic int acpi_video_bus_add(struct acpi_device *device); 848c2ecf20Sopenharmony_cistatic int acpi_video_bus_remove(struct acpi_device *device); 858c2ecf20Sopenharmony_cistatic void acpi_video_bus_notify(struct acpi_device *device, u32 event); 868c2ecf20Sopenharmony_civoid acpi_video_detect_exit(void); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* 898c2ecf20Sopenharmony_ci * Indices in the _BCL method response: the first two items are special, 908c2ecf20Sopenharmony_ci * the rest are all supported levels. 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * See page 575 of the ACPI spec 3.0 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_cienum acpi_video_level_idx { 958c2ecf20Sopenharmony_ci ACPI_VIDEO_AC_LEVEL, /* level when machine has full power */ 968c2ecf20Sopenharmony_ci ACPI_VIDEO_BATTERY_LEVEL, /* level when machine is on batteries */ 978c2ecf20Sopenharmony_ci ACPI_VIDEO_FIRST_LEVEL, /* actual supported levels begin here */ 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic const struct acpi_device_id video_device_ids[] = { 1018c2ecf20Sopenharmony_ci {ACPI_VIDEO_HID, 0}, 1028c2ecf20Sopenharmony_ci {"", 0}, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, video_device_ids); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic struct acpi_driver acpi_video_bus = { 1078c2ecf20Sopenharmony_ci .name = "video", 1088c2ecf20Sopenharmony_ci .class = ACPI_VIDEO_CLASS, 1098c2ecf20Sopenharmony_ci .ids = video_device_ids, 1108c2ecf20Sopenharmony_ci .ops = { 1118c2ecf20Sopenharmony_ci .add = acpi_video_bus_add, 1128c2ecf20Sopenharmony_ci .remove = acpi_video_bus_remove, 1138c2ecf20Sopenharmony_ci .notify = acpi_video_bus_notify, 1148c2ecf20Sopenharmony_ci }, 1158c2ecf20Sopenharmony_ci}; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistruct acpi_video_bus_flags { 1188c2ecf20Sopenharmony_ci u8 multihead:1; /* can switch video heads */ 1198c2ecf20Sopenharmony_ci u8 rom:1; /* can retrieve a video rom */ 1208c2ecf20Sopenharmony_ci u8 post:1; /* can configure the head to */ 1218c2ecf20Sopenharmony_ci u8 reserved:5; 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistruct acpi_video_bus_cap { 1258c2ecf20Sopenharmony_ci u8 _DOS:1; /* Enable/Disable output switching */ 1268c2ecf20Sopenharmony_ci u8 _DOD:1; /* Enumerate all devices attached to display adapter */ 1278c2ecf20Sopenharmony_ci u8 _ROM:1; /* Get ROM Data */ 1288c2ecf20Sopenharmony_ci u8 _GPD:1; /* Get POST Device */ 1298c2ecf20Sopenharmony_ci u8 _SPD:1; /* Set POST Device */ 1308c2ecf20Sopenharmony_ci u8 _VPO:1; /* Video POST Options */ 1318c2ecf20Sopenharmony_ci u8 reserved:2; 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistruct acpi_video_device_attrib { 1358c2ecf20Sopenharmony_ci u32 display_index:4; /* A zero-based instance of the Display */ 1368c2ecf20Sopenharmony_ci u32 display_port_attachment:4; /* This field differentiates the display type */ 1378c2ecf20Sopenharmony_ci u32 display_type:4; /* Describe the specific type in use */ 1388c2ecf20Sopenharmony_ci u32 vendor_specific:4; /* Chipset Vendor Specific */ 1398c2ecf20Sopenharmony_ci u32 bios_can_detect:1; /* BIOS can detect the device */ 1408c2ecf20Sopenharmony_ci u32 depend_on_vga:1; /* Non-VGA output device whose power is related to 1418c2ecf20Sopenharmony_ci the VGA device. */ 1428c2ecf20Sopenharmony_ci u32 pipe_id:3; /* For VGA multiple-head devices. */ 1438c2ecf20Sopenharmony_ci u32 reserved:10; /* Must be 0 */ 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* 1468c2ecf20Sopenharmony_ci * The device ID might not actually follow the scheme described by this 1478c2ecf20Sopenharmony_ci * struct acpi_video_device_attrib. If it does, then this bit 1488c2ecf20Sopenharmony_ci * device_id_scheme is set; otherwise, other fields should be ignored. 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * (but also see the global flag device_id_scheme) 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ci u32 device_id_scheme:1; 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistruct acpi_video_enumerated_device { 1568c2ecf20Sopenharmony_ci union { 1578c2ecf20Sopenharmony_ci u32 int_val; 1588c2ecf20Sopenharmony_ci struct acpi_video_device_attrib attrib; 1598c2ecf20Sopenharmony_ci } value; 1608c2ecf20Sopenharmony_ci struct acpi_video_device *bind_info; 1618c2ecf20Sopenharmony_ci}; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistruct acpi_video_bus { 1648c2ecf20Sopenharmony_ci struct acpi_device *device; 1658c2ecf20Sopenharmony_ci bool backlight_registered; 1668c2ecf20Sopenharmony_ci u8 dos_setting; 1678c2ecf20Sopenharmony_ci struct acpi_video_enumerated_device *attached_array; 1688c2ecf20Sopenharmony_ci u8 attached_count; 1698c2ecf20Sopenharmony_ci u8 child_count; 1708c2ecf20Sopenharmony_ci struct acpi_video_bus_cap cap; 1718c2ecf20Sopenharmony_ci struct acpi_video_bus_flags flags; 1728c2ecf20Sopenharmony_ci struct list_head video_device_list; 1738c2ecf20Sopenharmony_ci struct mutex device_list_lock; /* protects video_device_list */ 1748c2ecf20Sopenharmony_ci struct list_head entry; 1758c2ecf20Sopenharmony_ci struct input_dev *input; 1768c2ecf20Sopenharmony_ci char phys[32]; /* for input device */ 1778c2ecf20Sopenharmony_ci struct notifier_block pm_nb; 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistruct acpi_video_device_flags { 1818c2ecf20Sopenharmony_ci u8 crt:1; 1828c2ecf20Sopenharmony_ci u8 lcd:1; 1838c2ecf20Sopenharmony_ci u8 tvout:1; 1848c2ecf20Sopenharmony_ci u8 dvi:1; 1858c2ecf20Sopenharmony_ci u8 bios:1; 1868c2ecf20Sopenharmony_ci u8 unknown:1; 1878c2ecf20Sopenharmony_ci u8 notify:1; 1888c2ecf20Sopenharmony_ci u8 reserved:1; 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistruct acpi_video_device_cap { 1928c2ecf20Sopenharmony_ci u8 _ADR:1; /* Return the unique ID */ 1938c2ecf20Sopenharmony_ci u8 _BCL:1; /* Query list of brightness control levels supported */ 1948c2ecf20Sopenharmony_ci u8 _BCM:1; /* Set the brightness level */ 1958c2ecf20Sopenharmony_ci u8 _BQC:1; /* Get current brightness level */ 1968c2ecf20Sopenharmony_ci u8 _BCQ:1; /* Some buggy BIOS uses _BCQ instead of _BQC */ 1978c2ecf20Sopenharmony_ci u8 _DDC:1; /* Return the EDID for this device */ 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistruct acpi_video_device { 2018c2ecf20Sopenharmony_ci unsigned long device_id; 2028c2ecf20Sopenharmony_ci struct acpi_video_device_flags flags; 2038c2ecf20Sopenharmony_ci struct acpi_video_device_cap cap; 2048c2ecf20Sopenharmony_ci struct list_head entry; 2058c2ecf20Sopenharmony_ci struct delayed_work switch_brightness_work; 2068c2ecf20Sopenharmony_ci int switch_brightness_event; 2078c2ecf20Sopenharmony_ci struct acpi_video_bus *video; 2088c2ecf20Sopenharmony_ci struct acpi_device *dev; 2098c2ecf20Sopenharmony_ci struct acpi_video_device_brightness *brightness; 2108c2ecf20Sopenharmony_ci struct backlight_device *backlight; 2118c2ecf20Sopenharmony_ci struct thermal_cooling_device *cooling_dev; 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic void acpi_video_device_notify(acpi_handle handle, u32 event, void *data); 2158c2ecf20Sopenharmony_cistatic void acpi_video_device_rebind(struct acpi_video_bus *video); 2168c2ecf20Sopenharmony_cistatic void acpi_video_device_bind(struct acpi_video_bus *video, 2178c2ecf20Sopenharmony_ci struct acpi_video_device *device); 2188c2ecf20Sopenharmony_cistatic int acpi_video_device_enumerate(struct acpi_video_bus *video); 2198c2ecf20Sopenharmony_cistatic int acpi_video_device_lcd_set_level(struct acpi_video_device *device, 2208c2ecf20Sopenharmony_ci int level); 2218c2ecf20Sopenharmony_cistatic int acpi_video_device_lcd_get_level_current( 2228c2ecf20Sopenharmony_ci struct acpi_video_device *device, 2238c2ecf20Sopenharmony_ci unsigned long long *level, bool raw); 2248c2ecf20Sopenharmony_cistatic int acpi_video_get_next_level(struct acpi_video_device *device, 2258c2ecf20Sopenharmony_ci u32 level_current, u32 event); 2268c2ecf20Sopenharmony_cistatic void acpi_video_switch_brightness(struct work_struct *work); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci/* backlight device sysfs support */ 2298c2ecf20Sopenharmony_cistatic int acpi_video_get_brightness(struct backlight_device *bd) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci unsigned long long cur_level; 2328c2ecf20Sopenharmony_ci int i; 2338c2ecf20Sopenharmony_ci struct acpi_video_device *vd = bl_get_data(bd); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false)) 2368c2ecf20Sopenharmony_ci return -EINVAL; 2378c2ecf20Sopenharmony_ci for (i = ACPI_VIDEO_FIRST_LEVEL; i < vd->brightness->count; i++) { 2388c2ecf20Sopenharmony_ci if (vd->brightness->levels[i] == cur_level) 2398c2ecf20Sopenharmony_ci return i - ACPI_VIDEO_FIRST_LEVEL; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int acpi_video_set_brightness(struct backlight_device *bd) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int request_level = bd->props.brightness + ACPI_VIDEO_FIRST_LEVEL; 2478c2ecf20Sopenharmony_ci struct acpi_video_device *vd = bl_get_data(bd); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci cancel_delayed_work(&vd->switch_brightness_work); 2508c2ecf20Sopenharmony_ci return acpi_video_device_lcd_set_level(vd, 2518c2ecf20Sopenharmony_ci vd->brightness->levels[request_level]); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic const struct backlight_ops acpi_backlight_ops = { 2558c2ecf20Sopenharmony_ci .get_brightness = acpi_video_get_brightness, 2568c2ecf20Sopenharmony_ci .update_status = acpi_video_set_brightness, 2578c2ecf20Sopenharmony_ci}; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* thermal cooling device callbacks */ 2608c2ecf20Sopenharmony_cistatic int video_get_max_state(struct thermal_cooling_device *cooling_dev, 2618c2ecf20Sopenharmony_ci unsigned long *state) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct acpi_device *device = cooling_dev->devdata; 2648c2ecf20Sopenharmony_ci struct acpi_video_device *video = acpi_driver_data(device); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci *state = video->brightness->count - ACPI_VIDEO_FIRST_LEVEL - 1; 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int video_get_cur_state(struct thermal_cooling_device *cooling_dev, 2718c2ecf20Sopenharmony_ci unsigned long *state) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct acpi_device *device = cooling_dev->devdata; 2748c2ecf20Sopenharmony_ci struct acpi_video_device *video = acpi_driver_data(device); 2758c2ecf20Sopenharmony_ci unsigned long long level; 2768c2ecf20Sopenharmony_ci int offset; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (acpi_video_device_lcd_get_level_current(video, &level, false)) 2798c2ecf20Sopenharmony_ci return -EINVAL; 2808c2ecf20Sopenharmony_ci for (offset = ACPI_VIDEO_FIRST_LEVEL; offset < video->brightness->count; 2818c2ecf20Sopenharmony_ci offset++) 2828c2ecf20Sopenharmony_ci if (level == video->brightness->levels[offset]) { 2838c2ecf20Sopenharmony_ci *state = video->brightness->count - offset - 1; 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return -EINVAL; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic int 2918c2ecf20Sopenharmony_civideo_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct acpi_device *device = cooling_dev->devdata; 2948c2ecf20Sopenharmony_ci struct acpi_video_device *video = acpi_driver_data(device); 2958c2ecf20Sopenharmony_ci int level; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (state >= video->brightness->count - ACPI_VIDEO_FIRST_LEVEL) 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci state = video->brightness->count - state; 3018c2ecf20Sopenharmony_ci level = video->brightness->levels[state - 1]; 3028c2ecf20Sopenharmony_ci return acpi_video_device_lcd_set_level(video, level); 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cistatic const struct thermal_cooling_device_ops video_cooling_ops = { 3068c2ecf20Sopenharmony_ci .get_max_state = video_get_max_state, 3078c2ecf20Sopenharmony_ci .get_cur_state = video_get_cur_state, 3088c2ecf20Sopenharmony_ci .set_cur_state = video_set_cur_state, 3098c2ecf20Sopenharmony_ci}; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* 3128c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- 3138c2ecf20Sopenharmony_ci * Video Management 3148c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int 3188c2ecf20Sopenharmony_ciacpi_video_device_lcd_query_levels(acpi_handle handle, 3198c2ecf20Sopenharmony_ci union acpi_object **levels) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int status; 3228c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 3238c2ecf20Sopenharmony_ci union acpi_object *obj; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci *levels = NULL; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci status = acpi_evaluate_object(handle, "_BCL", NULL, &buffer); 3298c2ecf20Sopenharmony_ci if (!ACPI_SUCCESS(status)) 3308c2ecf20Sopenharmony_ci return status; 3318c2ecf20Sopenharmony_ci obj = (union acpi_object *)buffer.pointer; 3328c2ecf20Sopenharmony_ci if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { 3338c2ecf20Sopenharmony_ci printk(KERN_ERR PREFIX "Invalid _BCL data\n"); 3348c2ecf20Sopenharmony_ci status = -EFAULT; 3358c2ecf20Sopenharmony_ci goto err; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci *levels = obj; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci return 0; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cierr: 3438c2ecf20Sopenharmony_ci kfree(buffer.pointer); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci return status; 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic int 3498c2ecf20Sopenharmony_ciacpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci int status; 3528c2ecf20Sopenharmony_ci int state; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci status = acpi_execute_simple_method(device->dev->handle, 3558c2ecf20Sopenharmony_ci "_BCM", level); 3568c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 3578c2ecf20Sopenharmony_ci ACPI_ERROR((AE_INFO, "Evaluating _BCM failed")); 3588c2ecf20Sopenharmony_ci return -EIO; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci device->brightness->curr = level; 3628c2ecf20Sopenharmony_ci for (state = ACPI_VIDEO_FIRST_LEVEL; state < device->brightness->count; 3638c2ecf20Sopenharmony_ci state++) 3648c2ecf20Sopenharmony_ci if (level == device->brightness->levels[state]) { 3658c2ecf20Sopenharmony_ci if (device->backlight) 3668c2ecf20Sopenharmony_ci device->backlight->props.brightness = 3678c2ecf20Sopenharmony_ci state - ACPI_VIDEO_FIRST_LEVEL; 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci ACPI_ERROR((AE_INFO, "Current brightness invalid")); 3728c2ecf20Sopenharmony_ci return -EINVAL; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/* 3768c2ecf20Sopenharmony_ci * For some buggy _BQC methods, we need to add a constant value to 3778c2ecf20Sopenharmony_ci * the _BQC return value to get the actual current brightness level 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic int bqc_offset_aml_bug_workaround; 3818c2ecf20Sopenharmony_cistatic int video_set_bqc_offset(const struct dmi_system_id *d) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci bqc_offset_aml_bug_workaround = 9; 3848c2ecf20Sopenharmony_ci return 0; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int video_disable_backlight_sysfs_if( 3888c2ecf20Sopenharmony_ci const struct dmi_system_id *d) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci if (disable_backlight_sysfs_if == -1) 3918c2ecf20Sopenharmony_ci disable_backlight_sysfs_if = 1; 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic int video_set_device_id_scheme(const struct dmi_system_id *d) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci device_id_scheme = true; 3988c2ecf20Sopenharmony_ci return 0; 3998c2ecf20Sopenharmony_ci} 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic int video_enable_only_lcd(const struct dmi_system_id *d) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci only_lcd = true; 4048c2ecf20Sopenharmony_ci return 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_cistatic int video_set_report_key_events(const struct dmi_system_id *id) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci if (report_key_events == -1) 4108c2ecf20Sopenharmony_ci report_key_events = (uintptr_t)id->driver_data; 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int video_hw_changes_brightness( 4158c2ecf20Sopenharmony_ci const struct dmi_system_id *d) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci if (hw_changes_brightness == -1) 4188c2ecf20Sopenharmony_ci hw_changes_brightness = 1; 4198c2ecf20Sopenharmony_ci return 0; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic const struct dmi_system_id video_dmi_table[] = { 4238c2ecf20Sopenharmony_ci /* 4248c2ecf20Sopenharmony_ci * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 4258c2ecf20Sopenharmony_ci */ 4268c2ecf20Sopenharmony_ci { 4278c2ecf20Sopenharmony_ci .callback = video_set_bqc_offset, 4288c2ecf20Sopenharmony_ci .ident = "Acer Aspire 5720", 4298c2ecf20Sopenharmony_ci .matches = { 4308c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), 4318c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), 4328c2ecf20Sopenharmony_ci }, 4338c2ecf20Sopenharmony_ci }, 4348c2ecf20Sopenharmony_ci { 4358c2ecf20Sopenharmony_ci .callback = video_set_bqc_offset, 4368c2ecf20Sopenharmony_ci .ident = "Acer Aspire 5710Z", 4378c2ecf20Sopenharmony_ci .matches = { 4388c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), 4398c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"), 4408c2ecf20Sopenharmony_ci }, 4418c2ecf20Sopenharmony_ci }, 4428c2ecf20Sopenharmony_ci { 4438c2ecf20Sopenharmony_ci .callback = video_set_bqc_offset, 4448c2ecf20Sopenharmony_ci .ident = "eMachines E510", 4458c2ecf20Sopenharmony_ci .matches = { 4468c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"), 4478c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"), 4488c2ecf20Sopenharmony_ci }, 4498c2ecf20Sopenharmony_ci }, 4508c2ecf20Sopenharmony_ci { 4518c2ecf20Sopenharmony_ci .callback = video_set_bqc_offset, 4528c2ecf20Sopenharmony_ci .ident = "Acer Aspire 5315", 4538c2ecf20Sopenharmony_ci .matches = { 4548c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), 4558c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), 4568c2ecf20Sopenharmony_ci }, 4578c2ecf20Sopenharmony_ci }, 4588c2ecf20Sopenharmony_ci { 4598c2ecf20Sopenharmony_ci .callback = video_set_bqc_offset, 4608c2ecf20Sopenharmony_ci .ident = "Acer Aspire 7720", 4618c2ecf20Sopenharmony_ci .matches = { 4628c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), 4638c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"), 4648c2ecf20Sopenharmony_ci }, 4658c2ecf20Sopenharmony_ci }, 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* 4688c2ecf20Sopenharmony_ci * Some machines have a broken acpi-video interface for brightness 4698c2ecf20Sopenharmony_ci * control, but still need an acpi_video_device_lcd_set_level() call 4708c2ecf20Sopenharmony_ci * on resume to turn the backlight power on. We Enable backlight 4718c2ecf20Sopenharmony_ci * control on these systems, but do not register a backlight sysfs 4728c2ecf20Sopenharmony_ci * as brightness control does not work. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci { 4758c2ecf20Sopenharmony_ci /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */ 4768c2ecf20Sopenharmony_ci .callback = video_disable_backlight_sysfs_if, 4778c2ecf20Sopenharmony_ci .ident = "Toshiba Portege R700", 4788c2ecf20Sopenharmony_ci .matches = { 4798c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 4808c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R700"), 4818c2ecf20Sopenharmony_ci }, 4828c2ecf20Sopenharmony_ci }, 4838c2ecf20Sopenharmony_ci { 4848c2ecf20Sopenharmony_ci /* https://bugs.freedesktop.org/show_bug.cgi?id=82634 */ 4858c2ecf20Sopenharmony_ci .callback = video_disable_backlight_sysfs_if, 4868c2ecf20Sopenharmony_ci .ident = "Toshiba Portege R830", 4878c2ecf20Sopenharmony_ci .matches = { 4888c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 4898c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R830"), 4908c2ecf20Sopenharmony_ci }, 4918c2ecf20Sopenharmony_ci }, 4928c2ecf20Sopenharmony_ci { 4938c2ecf20Sopenharmony_ci /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */ 4948c2ecf20Sopenharmony_ci .callback = video_disable_backlight_sysfs_if, 4958c2ecf20Sopenharmony_ci .ident = "Toshiba Satellite R830", 4968c2ecf20Sopenharmony_ci .matches = { 4978c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 4988c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE R830"), 4998c2ecf20Sopenharmony_ci }, 5008c2ecf20Sopenharmony_ci }, 5018c2ecf20Sopenharmony_ci { 5028c2ecf20Sopenharmony_ci .callback = video_disable_backlight_sysfs_if, 5038c2ecf20Sopenharmony_ci .ident = "Toshiba Satellite Z830", 5048c2ecf20Sopenharmony_ci .matches = { 5058c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 5068c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE Z830"), 5078c2ecf20Sopenharmony_ci }, 5088c2ecf20Sopenharmony_ci }, 5098c2ecf20Sopenharmony_ci { 5108c2ecf20Sopenharmony_ci .callback = video_disable_backlight_sysfs_if, 5118c2ecf20Sopenharmony_ci .ident = "Toshiba Portege Z830", 5128c2ecf20Sopenharmony_ci .matches = { 5138c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 5148c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE Z830"), 5158c2ecf20Sopenharmony_ci }, 5168c2ecf20Sopenharmony_ci }, 5178c2ecf20Sopenharmony_ci /* 5188c2ecf20Sopenharmony_ci * Some machine's _DOD IDs don't have bit 31(Device ID Scheme) set 5198c2ecf20Sopenharmony_ci * but the IDs actually follow the Device ID Scheme. 5208c2ecf20Sopenharmony_ci */ 5218c2ecf20Sopenharmony_ci { 5228c2ecf20Sopenharmony_ci /* https://bugzilla.kernel.org/show_bug.cgi?id=104121 */ 5238c2ecf20Sopenharmony_ci .callback = video_set_device_id_scheme, 5248c2ecf20Sopenharmony_ci .ident = "ESPRIMO Mobile M9410", 5258c2ecf20Sopenharmony_ci .matches = { 5268c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 5278c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile M9410"), 5288c2ecf20Sopenharmony_ci }, 5298c2ecf20Sopenharmony_ci }, 5308c2ecf20Sopenharmony_ci /* 5318c2ecf20Sopenharmony_ci * Some machines have multiple video output devices, but only the one 5328c2ecf20Sopenharmony_ci * that is the type of LCD can do the backlight control so we should not 5338c2ecf20Sopenharmony_ci * register backlight interface for other video output devices. 5348c2ecf20Sopenharmony_ci */ 5358c2ecf20Sopenharmony_ci { 5368c2ecf20Sopenharmony_ci /* https://bugzilla.kernel.org/show_bug.cgi?id=104121 */ 5378c2ecf20Sopenharmony_ci .callback = video_enable_only_lcd, 5388c2ecf20Sopenharmony_ci .ident = "ESPRIMO Mobile M9410", 5398c2ecf20Sopenharmony_ci .matches = { 5408c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), 5418c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile M9410"), 5428c2ecf20Sopenharmony_ci }, 5438c2ecf20Sopenharmony_ci }, 5448c2ecf20Sopenharmony_ci /* 5458c2ecf20Sopenharmony_ci * Some machines report wrong key events on the acpi-bus, suppress 5468c2ecf20Sopenharmony_ci * key event reporting on these. Note this is only intended to work 5478c2ecf20Sopenharmony_ci * around events which are plain wrong. In some cases we get double 5488c2ecf20Sopenharmony_ci * events, in this case acpi-video is considered the canonical source 5498c2ecf20Sopenharmony_ci * and the events from the other source should be filtered. E.g. 5508c2ecf20Sopenharmony_ci * by calling acpi_video_handles_brightness_key_presses() from the 5518c2ecf20Sopenharmony_ci * vendor acpi/wmi driver or by using /lib/udev/hwdb.d/60-keyboard.hwdb 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci { 5548c2ecf20Sopenharmony_ci .callback = video_set_report_key_events, 5558c2ecf20Sopenharmony_ci .driver_data = (void *)((uintptr_t)REPORT_OUTPUT_KEY_EVENTS), 5568c2ecf20Sopenharmony_ci .ident = "Dell Vostro V131", 5578c2ecf20Sopenharmony_ci .matches = { 5588c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 5598c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), 5608c2ecf20Sopenharmony_ci }, 5618c2ecf20Sopenharmony_ci }, 5628c2ecf20Sopenharmony_ci { 5638c2ecf20Sopenharmony_ci .callback = video_set_report_key_events, 5648c2ecf20Sopenharmony_ci .driver_data = (void *)((uintptr_t)REPORT_BRIGHTNESS_KEY_EVENTS), 5658c2ecf20Sopenharmony_ci .ident = "Dell Vostro 3350", 5668c2ecf20Sopenharmony_ci .matches = { 5678c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 5688c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3350"), 5698c2ecf20Sopenharmony_ci }, 5708c2ecf20Sopenharmony_ci }, 5718c2ecf20Sopenharmony_ci { 5728c2ecf20Sopenharmony_ci .callback = video_set_report_key_events, 5738c2ecf20Sopenharmony_ci .driver_data = (void *)((uintptr_t)REPORT_BRIGHTNESS_KEY_EVENTS), 5748c2ecf20Sopenharmony_ci .ident = "COLORFUL X15 AT 23", 5758c2ecf20Sopenharmony_ci .matches = { 5768c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "COLORFUL"), 5778c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "X15 AT 23"), 5788c2ecf20Sopenharmony_ci }, 5798c2ecf20Sopenharmony_ci }, 5808c2ecf20Sopenharmony_ci /* 5818c2ecf20Sopenharmony_ci * Some machines change the brightness themselves when a brightness 5828c2ecf20Sopenharmony_ci * hotkey gets pressed, despite us telling them not to. In this case 5838c2ecf20Sopenharmony_ci * acpi_video_device_notify() should only call backlight_force_update( 5848c2ecf20Sopenharmony_ci * BACKLIGHT_UPDATE_HOTKEY) and not do anything else. 5858c2ecf20Sopenharmony_ci */ 5868c2ecf20Sopenharmony_ci { 5878c2ecf20Sopenharmony_ci /* https://bugzilla.kernel.org/show_bug.cgi?id=204077 */ 5888c2ecf20Sopenharmony_ci .callback = video_hw_changes_brightness, 5898c2ecf20Sopenharmony_ci .ident = "Packard Bell EasyNote MZ35", 5908c2ecf20Sopenharmony_ci .matches = { 5918c2ecf20Sopenharmony_ci DMI_MATCH(DMI_SYS_VENDOR, "Packard Bell"), 5928c2ecf20Sopenharmony_ci DMI_MATCH(DMI_PRODUCT_NAME, "EasyNote MZ35"), 5938c2ecf20Sopenharmony_ci }, 5948c2ecf20Sopenharmony_ci }, 5958c2ecf20Sopenharmony_ci {} 5968c2ecf20Sopenharmony_ci}; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic unsigned long long 5998c2ecf20Sopenharmony_ciacpi_video_bqc_value_to_level(struct acpi_video_device *device, 6008c2ecf20Sopenharmony_ci unsigned long long bqc_value) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci unsigned long long level; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci if (device->brightness->flags._BQC_use_index) { 6058c2ecf20Sopenharmony_ci /* 6068c2ecf20Sopenharmony_ci * _BQC returns an index that doesn't account for the first 2 6078c2ecf20Sopenharmony_ci * items with special meaning (see enum acpi_video_level_idx), 6088c2ecf20Sopenharmony_ci * so we need to compensate for that by offsetting ourselves 6098c2ecf20Sopenharmony_ci */ 6108c2ecf20Sopenharmony_ci if (device->brightness->flags._BCL_reversed) 6118c2ecf20Sopenharmony_ci bqc_value = device->brightness->count - 6128c2ecf20Sopenharmony_ci ACPI_VIDEO_FIRST_LEVEL - 1 - bqc_value; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci level = device->brightness->levels[bqc_value + 6158c2ecf20Sopenharmony_ci ACPI_VIDEO_FIRST_LEVEL]; 6168c2ecf20Sopenharmony_ci } else { 6178c2ecf20Sopenharmony_ci level = bqc_value; 6188c2ecf20Sopenharmony_ci } 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci level += bqc_offset_aml_bug_workaround; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return level; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int 6268c2ecf20Sopenharmony_ciacpi_video_device_lcd_get_level_current(struct acpi_video_device *device, 6278c2ecf20Sopenharmony_ci unsigned long long *level, bool raw) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci acpi_status status = AE_OK; 6308c2ecf20Sopenharmony_ci int i; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (device->cap._BQC || device->cap._BCQ) { 6338c2ecf20Sopenharmony_ci char *buf = device->cap._BQC ? "_BQC" : "_BCQ"; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(device->dev->handle, buf, 6368c2ecf20Sopenharmony_ci NULL, level); 6378c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status)) { 6388c2ecf20Sopenharmony_ci if (raw) { 6398c2ecf20Sopenharmony_ci /* 6408c2ecf20Sopenharmony_ci * Caller has indicated he wants the raw 6418c2ecf20Sopenharmony_ci * value returned by _BQC, so don't furtherly 6428c2ecf20Sopenharmony_ci * mess with the value. 6438c2ecf20Sopenharmony_ci */ 6448c2ecf20Sopenharmony_ci return 0; 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci *level = acpi_video_bqc_value_to_level(device, *level); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci for (i = ACPI_VIDEO_FIRST_LEVEL; 6508c2ecf20Sopenharmony_ci i < device->brightness->count; i++) 6518c2ecf20Sopenharmony_ci if (device->brightness->levels[i] == *level) { 6528c2ecf20Sopenharmony_ci device->brightness->curr = *level; 6538c2ecf20Sopenharmony_ci return 0; 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci /* 6568c2ecf20Sopenharmony_ci * BQC returned an invalid level. 6578c2ecf20Sopenharmony_ci * Stop using it. 6588c2ecf20Sopenharmony_ci */ 6598c2ecf20Sopenharmony_ci ACPI_WARNING((AE_INFO, 6608c2ecf20Sopenharmony_ci "%s returned an invalid level", 6618c2ecf20Sopenharmony_ci buf)); 6628c2ecf20Sopenharmony_ci device->cap._BQC = device->cap._BCQ = 0; 6638c2ecf20Sopenharmony_ci } else { 6648c2ecf20Sopenharmony_ci /* 6658c2ecf20Sopenharmony_ci * Fixme: 6668c2ecf20Sopenharmony_ci * should we return an error or ignore this failure? 6678c2ecf20Sopenharmony_ci * dev->brightness->curr is a cached value which stores 6688c2ecf20Sopenharmony_ci * the correct current backlight level in most cases. 6698c2ecf20Sopenharmony_ci * ACPI video backlight still works w/ buggy _BQC. 6708c2ecf20Sopenharmony_ci * http://bugzilla.kernel.org/show_bug.cgi?id=12233 6718c2ecf20Sopenharmony_ci */ 6728c2ecf20Sopenharmony_ci ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf)); 6738c2ecf20Sopenharmony_ci device->cap._BQC = device->cap._BCQ = 0; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci } 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci *level = device->brightness->curr; 6788c2ecf20Sopenharmony_ci return 0; 6798c2ecf20Sopenharmony_ci} 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_cistatic int 6828c2ecf20Sopenharmony_ciacpi_video_device_EDID(struct acpi_video_device *device, 6838c2ecf20Sopenharmony_ci union acpi_object **edid, ssize_t length) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci int status; 6868c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 6878c2ecf20Sopenharmony_ci union acpi_object *obj; 6888c2ecf20Sopenharmony_ci union acpi_object arg0 = { ACPI_TYPE_INTEGER }; 6898c2ecf20Sopenharmony_ci struct acpi_object_list args = { 1, &arg0 }; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci *edid = NULL; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (!device) 6958c2ecf20Sopenharmony_ci return -ENODEV; 6968c2ecf20Sopenharmony_ci if (length == 128) 6978c2ecf20Sopenharmony_ci arg0.integer.value = 1; 6988c2ecf20Sopenharmony_ci else if (length == 256) 6998c2ecf20Sopenharmony_ci arg0.integer.value = 2; 7008c2ecf20Sopenharmony_ci else 7018c2ecf20Sopenharmony_ci return -EINVAL; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer); 7048c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 7058c2ecf20Sopenharmony_ci return -ENODEV; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci obj = buffer.pointer; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci if (obj && obj->type == ACPI_TYPE_BUFFER) 7108c2ecf20Sopenharmony_ci *edid = obj; 7118c2ecf20Sopenharmony_ci else { 7128c2ecf20Sopenharmony_ci printk(KERN_ERR PREFIX "Invalid _DDC data\n"); 7138c2ecf20Sopenharmony_ci status = -EFAULT; 7148c2ecf20Sopenharmony_ci kfree(obj); 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci return status; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/* bus */ 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci/* 7238c2ecf20Sopenharmony_ci * Arg: 7248c2ecf20Sopenharmony_ci * video : video bus device pointer 7258c2ecf20Sopenharmony_ci * bios_flag : 7268c2ecf20Sopenharmony_ci * 0. The system BIOS should NOT automatically switch(toggle) 7278c2ecf20Sopenharmony_ci * the active display output. 7288c2ecf20Sopenharmony_ci * 1. The system BIOS should automatically switch (toggle) the 7298c2ecf20Sopenharmony_ci * active display output. No switch event. 7308c2ecf20Sopenharmony_ci * 2. The _DGS value should be locked. 7318c2ecf20Sopenharmony_ci * 3. The system BIOS should not automatically switch (toggle) the 7328c2ecf20Sopenharmony_ci * active display output, but instead generate the display switch 7338c2ecf20Sopenharmony_ci * event notify code. 7348c2ecf20Sopenharmony_ci * lcd_flag : 7358c2ecf20Sopenharmony_ci * 0. The system BIOS should automatically control the brightness level 7368c2ecf20Sopenharmony_ci * of the LCD when: 7378c2ecf20Sopenharmony_ci * - the power changes from AC to DC (ACPI appendix B) 7388c2ecf20Sopenharmony_ci * - a brightness hotkey gets pressed (implied by Win7/8 backlight docs) 7398c2ecf20Sopenharmony_ci * 1. The system BIOS should NOT automatically control the brightness 7408c2ecf20Sopenharmony_ci * level of the LCD when: 7418c2ecf20Sopenharmony_ci * - the power changes from AC to DC (ACPI appendix B) 7428c2ecf20Sopenharmony_ci * - a brightness hotkey gets pressed (implied by Win7/8 backlight docs) 7438c2ecf20Sopenharmony_ci * Return Value: 7448c2ecf20Sopenharmony_ci * -EINVAL wrong arg. 7458c2ecf20Sopenharmony_ci */ 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_cistatic int 7488c2ecf20Sopenharmony_ciacpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) 7498c2ecf20Sopenharmony_ci{ 7508c2ecf20Sopenharmony_ci acpi_status status; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (!video->cap._DOS) 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) 7568c2ecf20Sopenharmony_ci return -EINVAL; 7578c2ecf20Sopenharmony_ci video->dos_setting = (lcd_flag << 2) | bios_flag; 7588c2ecf20Sopenharmony_ci status = acpi_execute_simple_method(video->device->handle, "_DOS", 7598c2ecf20Sopenharmony_ci (lcd_flag << 2) | bios_flag); 7608c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 7618c2ecf20Sopenharmony_ci return -EIO; 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return 0; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci/* 7678c2ecf20Sopenharmony_ci * Simple comparison function used to sort backlight levels. 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic int 7718c2ecf20Sopenharmony_ciacpi_video_cmp_level(const void *a, const void *b) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci return *(int *)a - *(int *)b; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci/* 7778c2ecf20Sopenharmony_ci * Decides if _BQC/_BCQ for this system is usable 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * We do this by changing the level first and then read out the current 7808c2ecf20Sopenharmony_ci * brightness level, if the value does not match, find out if it is using 7818c2ecf20Sopenharmony_ci * index. If not, clear the _BQC/_BCQ capability. 7828c2ecf20Sopenharmony_ci */ 7838c2ecf20Sopenharmony_cistatic int acpi_video_bqc_quirk(struct acpi_video_device *device, 7848c2ecf20Sopenharmony_ci int max_level, int current_level) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct acpi_video_device_brightness *br = device->brightness; 7878c2ecf20Sopenharmony_ci int result; 7888c2ecf20Sopenharmony_ci unsigned long long level; 7898c2ecf20Sopenharmony_ci int test_level; 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci /* don't mess with existing known broken systems */ 7928c2ecf20Sopenharmony_ci if (bqc_offset_aml_bug_workaround) 7938c2ecf20Sopenharmony_ci return 0; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci /* 7968c2ecf20Sopenharmony_ci * Some systems always report current brightness level as maximum 7978c2ecf20Sopenharmony_ci * through _BQC, we need to test another value for them. However, 7988c2ecf20Sopenharmony_ci * there is a subtlety: 7998c2ecf20Sopenharmony_ci * 8008c2ecf20Sopenharmony_ci * If the _BCL package ordering is descending, the first level 8018c2ecf20Sopenharmony_ci * (br->levels[2]) is likely to be 0, and if the number of levels 8028c2ecf20Sopenharmony_ci * matches the number of steps, we might confuse a returned level to 8038c2ecf20Sopenharmony_ci * mean the index. 8048c2ecf20Sopenharmony_ci * 8058c2ecf20Sopenharmony_ci * For example: 8068c2ecf20Sopenharmony_ci * 8078c2ecf20Sopenharmony_ci * current_level = max_level = 100 8088c2ecf20Sopenharmony_ci * test_level = 0 8098c2ecf20Sopenharmony_ci * returned level = 100 8108c2ecf20Sopenharmony_ci * 8118c2ecf20Sopenharmony_ci * In this case 100 means the level, not the index, and _BCM failed. 8128c2ecf20Sopenharmony_ci * Still, if the _BCL package ordering is descending, the index of 8138c2ecf20Sopenharmony_ci * level 0 is also 100, so we assume _BQC is indexed, when it's not. 8148c2ecf20Sopenharmony_ci * 8158c2ecf20Sopenharmony_ci * This causes all _BQC calls to return bogus values causing weird 8168c2ecf20Sopenharmony_ci * behavior from the user's perspective. For example: 8178c2ecf20Sopenharmony_ci * 8188c2ecf20Sopenharmony_ci * xbacklight -set 10; xbacklight -set 20; 8198c2ecf20Sopenharmony_ci * 8208c2ecf20Sopenharmony_ci * would flash to 90% and then slowly down to the desired level (20). 8218c2ecf20Sopenharmony_ci * 8228c2ecf20Sopenharmony_ci * The solution is simple; test anything other than the first level 8238c2ecf20Sopenharmony_ci * (e.g. 1). 8248c2ecf20Sopenharmony_ci */ 8258c2ecf20Sopenharmony_ci test_level = current_level == max_level 8268c2ecf20Sopenharmony_ci ? br->levels[ACPI_VIDEO_FIRST_LEVEL + 1] 8278c2ecf20Sopenharmony_ci : max_level; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci result = acpi_video_device_lcd_set_level(device, test_level); 8308c2ecf20Sopenharmony_ci if (result) 8318c2ecf20Sopenharmony_ci return result; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci result = acpi_video_device_lcd_get_level_current(device, &level, true); 8348c2ecf20Sopenharmony_ci if (result) 8358c2ecf20Sopenharmony_ci return result; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (level != test_level) { 8388c2ecf20Sopenharmony_ci /* buggy _BQC found, need to find out if it uses index */ 8398c2ecf20Sopenharmony_ci if (level < br->count) { 8408c2ecf20Sopenharmony_ci if (br->flags._BCL_reversed) 8418c2ecf20Sopenharmony_ci level = br->count - ACPI_VIDEO_FIRST_LEVEL - 1 - level; 8428c2ecf20Sopenharmony_ci if (br->levels[level + ACPI_VIDEO_FIRST_LEVEL] == test_level) 8438c2ecf20Sopenharmony_ci br->flags._BQC_use_index = 1; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (!br->flags._BQC_use_index) 8478c2ecf20Sopenharmony_ci device->cap._BQC = device->cap._BCQ = 0; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci return 0; 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ciint acpi_video_get_levels(struct acpi_device *device, 8548c2ecf20Sopenharmony_ci struct acpi_video_device_brightness **dev_br, 8558c2ecf20Sopenharmony_ci int *pmax_level) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci union acpi_object *obj = NULL; 8588c2ecf20Sopenharmony_ci int i, max_level = 0, count = 0, level_ac_battery = 0; 8598c2ecf20Sopenharmony_ci union acpi_object *o; 8608c2ecf20Sopenharmony_ci struct acpi_video_device_brightness *br = NULL; 8618c2ecf20Sopenharmony_ci int result = 0; 8628c2ecf20Sopenharmony_ci u32 value; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device->handle, 8658c2ecf20Sopenharmony_ci &obj))) { 8668c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available " 8678c2ecf20Sopenharmony_ci "LCD brightness level\n")); 8688c2ecf20Sopenharmony_ci result = -ENODEV; 8698c2ecf20Sopenharmony_ci goto out; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci if (obj->package.count < ACPI_VIDEO_FIRST_LEVEL) { 8738c2ecf20Sopenharmony_ci result = -EINVAL; 8748c2ecf20Sopenharmony_ci goto out; 8758c2ecf20Sopenharmony_ci } 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci br = kzalloc(sizeof(*br), GFP_KERNEL); 8788c2ecf20Sopenharmony_ci if (!br) { 8798c2ecf20Sopenharmony_ci printk(KERN_ERR "can't allocate memory\n"); 8808c2ecf20Sopenharmony_ci result = -ENOMEM; 8818c2ecf20Sopenharmony_ci goto out; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* 8858c2ecf20Sopenharmony_ci * Note that we have to reserve 2 extra items (ACPI_VIDEO_FIRST_LEVEL), 8868c2ecf20Sopenharmony_ci * in order to account for buggy BIOS which don't export the first two 8878c2ecf20Sopenharmony_ci * special levels (see below) 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_ci br->levels = kmalloc_array(obj->package.count + ACPI_VIDEO_FIRST_LEVEL, 8908c2ecf20Sopenharmony_ci sizeof(*br->levels), 8918c2ecf20Sopenharmony_ci GFP_KERNEL); 8928c2ecf20Sopenharmony_ci if (!br->levels) { 8938c2ecf20Sopenharmony_ci result = -ENOMEM; 8948c2ecf20Sopenharmony_ci goto out_free; 8958c2ecf20Sopenharmony_ci } 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci for (i = 0; i < obj->package.count; i++) { 8988c2ecf20Sopenharmony_ci o = (union acpi_object *)&obj->package.elements[i]; 8998c2ecf20Sopenharmony_ci if (o->type != ACPI_TYPE_INTEGER) { 9008c2ecf20Sopenharmony_ci printk(KERN_ERR PREFIX "Invalid data\n"); 9018c2ecf20Sopenharmony_ci continue; 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci value = (u32) o->integer.value; 9048c2ecf20Sopenharmony_ci /* Skip duplicate entries */ 9058c2ecf20Sopenharmony_ci if (count > ACPI_VIDEO_FIRST_LEVEL 9068c2ecf20Sopenharmony_ci && br->levels[count - 1] == value) 9078c2ecf20Sopenharmony_ci continue; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci br->levels[count] = value; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (br->levels[count] > max_level) 9128c2ecf20Sopenharmony_ci max_level = br->levels[count]; 9138c2ecf20Sopenharmony_ci count++; 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* 9178c2ecf20Sopenharmony_ci * some buggy BIOS don't export the levels 9188c2ecf20Sopenharmony_ci * when machine is on AC/Battery in _BCL package. 9198c2ecf20Sopenharmony_ci * In this case, the first two elements in _BCL packages 9208c2ecf20Sopenharmony_ci * are also supported brightness levels that OS should take care of. 9218c2ecf20Sopenharmony_ci */ 9228c2ecf20Sopenharmony_ci for (i = ACPI_VIDEO_FIRST_LEVEL; i < count; i++) { 9238c2ecf20Sopenharmony_ci if (br->levels[i] == br->levels[ACPI_VIDEO_AC_LEVEL]) 9248c2ecf20Sopenharmony_ci level_ac_battery++; 9258c2ecf20Sopenharmony_ci if (br->levels[i] == br->levels[ACPI_VIDEO_BATTERY_LEVEL]) 9268c2ecf20Sopenharmony_ci level_ac_battery++; 9278c2ecf20Sopenharmony_ci } 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (level_ac_battery < ACPI_VIDEO_FIRST_LEVEL) { 9308c2ecf20Sopenharmony_ci level_ac_battery = ACPI_VIDEO_FIRST_LEVEL - level_ac_battery; 9318c2ecf20Sopenharmony_ci br->flags._BCL_no_ac_battery_levels = 1; 9328c2ecf20Sopenharmony_ci for (i = (count - 1 + level_ac_battery); 9338c2ecf20Sopenharmony_ci i >= ACPI_VIDEO_FIRST_LEVEL; i--) 9348c2ecf20Sopenharmony_ci br->levels[i] = br->levels[i - level_ac_battery]; 9358c2ecf20Sopenharmony_ci count += level_ac_battery; 9368c2ecf20Sopenharmony_ci } else if (level_ac_battery > ACPI_VIDEO_FIRST_LEVEL) 9378c2ecf20Sopenharmony_ci ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package")); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci /* Check if the _BCL package is in a reversed order */ 9408c2ecf20Sopenharmony_ci if (max_level == br->levels[ACPI_VIDEO_FIRST_LEVEL]) { 9418c2ecf20Sopenharmony_ci br->flags._BCL_reversed = 1; 9428c2ecf20Sopenharmony_ci sort(&br->levels[ACPI_VIDEO_FIRST_LEVEL], 9438c2ecf20Sopenharmony_ci count - ACPI_VIDEO_FIRST_LEVEL, 9448c2ecf20Sopenharmony_ci sizeof(br->levels[ACPI_VIDEO_FIRST_LEVEL]), 9458c2ecf20Sopenharmony_ci acpi_video_cmp_level, NULL); 9468c2ecf20Sopenharmony_ci } else if (max_level != br->levels[count - 1]) 9478c2ecf20Sopenharmony_ci ACPI_ERROR((AE_INFO, 9488c2ecf20Sopenharmony_ci "Found unordered _BCL package")); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci br->count = count; 9518c2ecf20Sopenharmony_ci *dev_br = br; 9528c2ecf20Sopenharmony_ci if (pmax_level) 9538c2ecf20Sopenharmony_ci *pmax_level = max_level; 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_ciout: 9568c2ecf20Sopenharmony_ci kfree(obj); 9578c2ecf20Sopenharmony_ci return result; 9588c2ecf20Sopenharmony_ciout_free: 9598c2ecf20Sopenharmony_ci kfree(br); 9608c2ecf20Sopenharmony_ci goto out; 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_video_get_levels); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/* 9658c2ecf20Sopenharmony_ci * Arg: 9668c2ecf20Sopenharmony_ci * device : video output device (LCD, CRT, ..) 9678c2ecf20Sopenharmony_ci * 9688c2ecf20Sopenharmony_ci * Return Value: 9698c2ecf20Sopenharmony_ci * Maximum brightness level 9708c2ecf20Sopenharmony_ci * 9718c2ecf20Sopenharmony_ci * Allocate and initialize device->brightness. 9728c2ecf20Sopenharmony_ci */ 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_cistatic int 9758c2ecf20Sopenharmony_ciacpi_video_init_brightness(struct acpi_video_device *device) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci int i, max_level = 0; 9788c2ecf20Sopenharmony_ci unsigned long long level, level_old; 9798c2ecf20Sopenharmony_ci struct acpi_video_device_brightness *br = NULL; 9808c2ecf20Sopenharmony_ci int result; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci result = acpi_video_get_levels(device->dev, &br, &max_level); 9838c2ecf20Sopenharmony_ci if (result) 9848c2ecf20Sopenharmony_ci return result; 9858c2ecf20Sopenharmony_ci device->brightness = br; 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ 9888c2ecf20Sopenharmony_ci br->curr = level = max_level; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (!device->cap._BQC) 9918c2ecf20Sopenharmony_ci goto set_level; 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci result = acpi_video_device_lcd_get_level_current(device, 9948c2ecf20Sopenharmony_ci &level_old, true); 9958c2ecf20Sopenharmony_ci if (result) 9968c2ecf20Sopenharmony_ci goto out_free_levels; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci result = acpi_video_bqc_quirk(device, max_level, level_old); 9998c2ecf20Sopenharmony_ci if (result) 10008c2ecf20Sopenharmony_ci goto out_free_levels; 10018c2ecf20Sopenharmony_ci /* 10028c2ecf20Sopenharmony_ci * cap._BQC may get cleared due to _BQC is found to be broken 10038c2ecf20Sopenharmony_ci * in acpi_video_bqc_quirk, so check again here. 10048c2ecf20Sopenharmony_ci */ 10058c2ecf20Sopenharmony_ci if (!device->cap._BQC) 10068c2ecf20Sopenharmony_ci goto set_level; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci level = acpi_video_bqc_value_to_level(device, level_old); 10098c2ecf20Sopenharmony_ci /* 10108c2ecf20Sopenharmony_ci * On some buggy laptops, _BQC returns an uninitialized 10118c2ecf20Sopenharmony_ci * value when invoked for the first time, i.e. 10128c2ecf20Sopenharmony_ci * level_old is invalid (no matter whether it's a level 10138c2ecf20Sopenharmony_ci * or an index). Set the backlight to max_level in this case. 10148c2ecf20Sopenharmony_ci */ 10158c2ecf20Sopenharmony_ci for (i = ACPI_VIDEO_FIRST_LEVEL; i < br->count; i++) 10168c2ecf20Sopenharmony_ci if (level == br->levels[i]) 10178c2ecf20Sopenharmony_ci break; 10188c2ecf20Sopenharmony_ci if (i == br->count || !level) 10198c2ecf20Sopenharmony_ci level = max_level; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ciset_level: 10228c2ecf20Sopenharmony_ci result = acpi_video_device_lcd_set_level(device, level); 10238c2ecf20Sopenharmony_ci if (result) 10248c2ecf20Sopenharmony_ci goto out_free_levels; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, 10278c2ecf20Sopenharmony_ci "found %d brightness levels\n", 10288c2ecf20Sopenharmony_ci br->count - ACPI_VIDEO_FIRST_LEVEL)); 10298c2ecf20Sopenharmony_ci return 0; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ciout_free_levels: 10328c2ecf20Sopenharmony_ci kfree(br->levels); 10338c2ecf20Sopenharmony_ci kfree(br); 10348c2ecf20Sopenharmony_ci device->brightness = NULL; 10358c2ecf20Sopenharmony_ci return result; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci/* 10398c2ecf20Sopenharmony_ci * Arg: 10408c2ecf20Sopenharmony_ci * device : video output device (LCD, CRT, ..) 10418c2ecf20Sopenharmony_ci * 10428c2ecf20Sopenharmony_ci * Return Value: 10438c2ecf20Sopenharmony_ci * None 10448c2ecf20Sopenharmony_ci * 10458c2ecf20Sopenharmony_ci * Find out all required AML methods defined under the output 10468c2ecf20Sopenharmony_ci * device. 10478c2ecf20Sopenharmony_ci */ 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cistatic void acpi_video_device_find_cap(struct acpi_video_device *device) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci if (acpi_has_method(device->dev->handle, "_ADR")) 10528c2ecf20Sopenharmony_ci device->cap._ADR = 1; 10538c2ecf20Sopenharmony_ci if (acpi_has_method(device->dev->handle, "_BCL")) 10548c2ecf20Sopenharmony_ci device->cap._BCL = 1; 10558c2ecf20Sopenharmony_ci if (acpi_has_method(device->dev->handle, "_BCM")) 10568c2ecf20Sopenharmony_ci device->cap._BCM = 1; 10578c2ecf20Sopenharmony_ci if (acpi_has_method(device->dev->handle, "_BQC")) { 10588c2ecf20Sopenharmony_ci device->cap._BQC = 1; 10598c2ecf20Sopenharmony_ci } else if (acpi_has_method(device->dev->handle, "_BCQ")) { 10608c2ecf20Sopenharmony_ci printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n"); 10618c2ecf20Sopenharmony_ci device->cap._BCQ = 1; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci if (acpi_has_method(device->dev->handle, "_DDC")) 10658c2ecf20Sopenharmony_ci device->cap._DDC = 1; 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci/* 10698c2ecf20Sopenharmony_ci * Arg: 10708c2ecf20Sopenharmony_ci * device : video output device (VGA) 10718c2ecf20Sopenharmony_ci * 10728c2ecf20Sopenharmony_ci * Return Value: 10738c2ecf20Sopenharmony_ci * None 10748c2ecf20Sopenharmony_ci * 10758c2ecf20Sopenharmony_ci * Find out all required AML methods defined under the video bus device. 10768c2ecf20Sopenharmony_ci */ 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_cistatic void acpi_video_bus_find_cap(struct acpi_video_bus *video) 10798c2ecf20Sopenharmony_ci{ 10808c2ecf20Sopenharmony_ci if (acpi_has_method(video->device->handle, "_DOS")) 10818c2ecf20Sopenharmony_ci video->cap._DOS = 1; 10828c2ecf20Sopenharmony_ci if (acpi_has_method(video->device->handle, "_DOD")) 10838c2ecf20Sopenharmony_ci video->cap._DOD = 1; 10848c2ecf20Sopenharmony_ci if (acpi_has_method(video->device->handle, "_ROM")) 10858c2ecf20Sopenharmony_ci video->cap._ROM = 1; 10868c2ecf20Sopenharmony_ci if (acpi_has_method(video->device->handle, "_GPD")) 10878c2ecf20Sopenharmony_ci video->cap._GPD = 1; 10888c2ecf20Sopenharmony_ci if (acpi_has_method(video->device->handle, "_SPD")) 10898c2ecf20Sopenharmony_ci video->cap._SPD = 1; 10908c2ecf20Sopenharmony_ci if (acpi_has_method(video->device->handle, "_VPO")) 10918c2ecf20Sopenharmony_ci video->cap._VPO = 1; 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci/* 10958c2ecf20Sopenharmony_ci * Check whether the video bus device has required AML method to 10968c2ecf20Sopenharmony_ci * support the desired features 10978c2ecf20Sopenharmony_ci */ 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic int acpi_video_bus_check(struct acpi_video_bus *video) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci acpi_status status = -ENOENT; 11028c2ecf20Sopenharmony_ci struct pci_dev *dev; 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci if (!video) 11058c2ecf20Sopenharmony_ci return -EINVAL; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci dev = acpi_get_pci_dev(video->device->handle); 11088c2ecf20Sopenharmony_ci if (!dev) 11098c2ecf20Sopenharmony_ci return -ENODEV; 11108c2ecf20Sopenharmony_ci pci_dev_put(dev); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci /* 11138c2ecf20Sopenharmony_ci * Since there is no HID, CID and so on for VGA driver, we have 11148c2ecf20Sopenharmony_ci * to check well known required nodes. 11158c2ecf20Sopenharmony_ci */ 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci /* Does this device support video switching? */ 11188c2ecf20Sopenharmony_ci if (video->cap._DOS || video->cap._DOD) { 11198c2ecf20Sopenharmony_ci if (!video->cap._DOS) { 11208c2ecf20Sopenharmony_ci printk(KERN_WARNING FW_BUG 11218c2ecf20Sopenharmony_ci "ACPI(%s) defines _DOD but not _DOS\n", 11228c2ecf20Sopenharmony_ci acpi_device_bid(video->device)); 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci video->flags.multihead = 1; 11258c2ecf20Sopenharmony_ci status = 0; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci /* Does this device support retrieving a video ROM? */ 11298c2ecf20Sopenharmony_ci if (video->cap._ROM) { 11308c2ecf20Sopenharmony_ci video->flags.rom = 1; 11318c2ecf20Sopenharmony_ci status = 0; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci /* Does this device support configuring which video device to POST? */ 11358c2ecf20Sopenharmony_ci if (video->cap._GPD && video->cap._SPD && video->cap._VPO) { 11368c2ecf20Sopenharmony_ci video->flags.post = 1; 11378c2ecf20Sopenharmony_ci status = 0; 11388c2ecf20Sopenharmony_ci } 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci return status; 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci/* 11448c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- 11458c2ecf20Sopenharmony_ci * Driver Interface 11468c2ecf20Sopenharmony_ci * -------------------------------------------------------------------------- 11478c2ecf20Sopenharmony_ci */ 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci/* device interface */ 11508c2ecf20Sopenharmony_cistatic struct acpi_video_device_attrib * 11518c2ecf20Sopenharmony_ciacpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci struct acpi_video_enumerated_device *ids; 11548c2ecf20Sopenharmony_ci int i; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci for (i = 0; i < video->attached_count; i++) { 11578c2ecf20Sopenharmony_ci ids = &video->attached_array[i]; 11588c2ecf20Sopenharmony_ci if ((ids->value.int_val & 0xffff) == device_id) 11598c2ecf20Sopenharmony_ci return &ids->value.attrib; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci return NULL; 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic int 11668c2ecf20Sopenharmony_ciacpi_video_get_device_type(struct acpi_video_bus *video, 11678c2ecf20Sopenharmony_ci unsigned long device_id) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci struct acpi_video_enumerated_device *ids; 11708c2ecf20Sopenharmony_ci int i; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci for (i = 0; i < video->attached_count; i++) { 11738c2ecf20Sopenharmony_ci ids = &video->attached_array[i]; 11748c2ecf20Sopenharmony_ci if ((ids->value.int_val & 0xffff) == device_id) 11758c2ecf20Sopenharmony_ci return ids->value.int_val; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci return 0; 11798c2ecf20Sopenharmony_ci} 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_cistatic int 11828c2ecf20Sopenharmony_ciacpi_video_bus_get_one_device(struct acpi_device *device, 11838c2ecf20Sopenharmony_ci struct acpi_video_bus *video) 11848c2ecf20Sopenharmony_ci{ 11858c2ecf20Sopenharmony_ci unsigned long long device_id; 11868c2ecf20Sopenharmony_ci int status, device_type; 11878c2ecf20Sopenharmony_ci struct acpi_video_device *data; 11888c2ecf20Sopenharmony_ci struct acpi_video_device_attrib *attribute; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci status = 11918c2ecf20Sopenharmony_ci acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); 11928c2ecf20Sopenharmony_ci /* Some device omits _ADR, we skip them instead of fail */ 11938c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 11948c2ecf20Sopenharmony_ci return 0; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); 11978c2ecf20Sopenharmony_ci if (!data) 11988c2ecf20Sopenharmony_ci return -ENOMEM; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); 12018c2ecf20Sopenharmony_ci strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); 12028c2ecf20Sopenharmony_ci device->driver_data = data; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci data->device_id = device_id; 12058c2ecf20Sopenharmony_ci data->video = video; 12068c2ecf20Sopenharmony_ci data->dev = device; 12078c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&data->switch_brightness_work, 12088c2ecf20Sopenharmony_ci acpi_video_switch_brightness); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci attribute = acpi_video_get_device_attr(video, device_id); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (attribute && (attribute->device_id_scheme || device_id_scheme)) { 12138c2ecf20Sopenharmony_ci switch (attribute->display_type) { 12148c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_CRT: 12158c2ecf20Sopenharmony_ci data->flags.crt = 1; 12168c2ecf20Sopenharmony_ci break; 12178c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_TV: 12188c2ecf20Sopenharmony_ci data->flags.tvout = 1; 12198c2ecf20Sopenharmony_ci break; 12208c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_DVI: 12218c2ecf20Sopenharmony_ci data->flags.dvi = 1; 12228c2ecf20Sopenharmony_ci break; 12238c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_LCD: 12248c2ecf20Sopenharmony_ci data->flags.lcd = 1; 12258c2ecf20Sopenharmony_ci break; 12268c2ecf20Sopenharmony_ci default: 12278c2ecf20Sopenharmony_ci data->flags.unknown = 1; 12288c2ecf20Sopenharmony_ci break; 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci if (attribute->bios_can_detect) 12318c2ecf20Sopenharmony_ci data->flags.bios = 1; 12328c2ecf20Sopenharmony_ci } else { 12338c2ecf20Sopenharmony_ci /* Check for legacy IDs */ 12348c2ecf20Sopenharmony_ci device_type = acpi_video_get_device_type(video, device_id); 12358c2ecf20Sopenharmony_ci /* Ignore bits 16 and 18-20 */ 12368c2ecf20Sopenharmony_ci switch (device_type & 0xffe2ffff) { 12378c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR: 12388c2ecf20Sopenharmony_ci data->flags.crt = 1; 12398c2ecf20Sopenharmony_ci break; 12408c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_LEGACY_PANEL: 12418c2ecf20Sopenharmony_ci data->flags.lcd = 1; 12428c2ecf20Sopenharmony_ci break; 12438c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_LEGACY_TV: 12448c2ecf20Sopenharmony_ci data->flags.tvout = 1; 12458c2ecf20Sopenharmony_ci break; 12468c2ecf20Sopenharmony_ci default: 12478c2ecf20Sopenharmony_ci data->flags.unknown = 1; 12488c2ecf20Sopenharmony_ci } 12498c2ecf20Sopenharmony_ci } 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci acpi_video_device_bind(video, data); 12528c2ecf20Sopenharmony_ci acpi_video_device_find_cap(data); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci mutex_lock(&video->device_list_lock); 12558c2ecf20Sopenharmony_ci list_add_tail(&data->entry, &video->video_device_list); 12568c2ecf20Sopenharmony_ci mutex_unlock(&video->device_list_lock); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci return status; 12598c2ecf20Sopenharmony_ci} 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci/* 12628c2ecf20Sopenharmony_ci * Arg: 12638c2ecf20Sopenharmony_ci * video : video bus device 12648c2ecf20Sopenharmony_ci * 12658c2ecf20Sopenharmony_ci * Return: 12668c2ecf20Sopenharmony_ci * none 12678c2ecf20Sopenharmony_ci * 12688c2ecf20Sopenharmony_ci * Enumerate the video device list of the video bus, 12698c2ecf20Sopenharmony_ci * bind the ids with the corresponding video devices 12708c2ecf20Sopenharmony_ci * under the video bus. 12718c2ecf20Sopenharmony_ci */ 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic void acpi_video_device_rebind(struct acpi_video_bus *video) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct acpi_video_device *dev; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci mutex_lock(&video->device_list_lock); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci list_for_each_entry(dev, &video->video_device_list, entry) 12808c2ecf20Sopenharmony_ci acpi_video_device_bind(video, dev); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci mutex_unlock(&video->device_list_lock); 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci/* 12868c2ecf20Sopenharmony_ci * Arg: 12878c2ecf20Sopenharmony_ci * video : video bus device 12888c2ecf20Sopenharmony_ci * device : video output device under the video 12898c2ecf20Sopenharmony_ci * bus 12908c2ecf20Sopenharmony_ci * 12918c2ecf20Sopenharmony_ci * Return: 12928c2ecf20Sopenharmony_ci * none 12938c2ecf20Sopenharmony_ci * 12948c2ecf20Sopenharmony_ci * Bind the ids with the corresponding video devices 12958c2ecf20Sopenharmony_ci * under the video bus. 12968c2ecf20Sopenharmony_ci */ 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_cistatic void 12998c2ecf20Sopenharmony_ciacpi_video_device_bind(struct acpi_video_bus *video, 13008c2ecf20Sopenharmony_ci struct acpi_video_device *device) 13018c2ecf20Sopenharmony_ci{ 13028c2ecf20Sopenharmony_ci struct acpi_video_enumerated_device *ids; 13038c2ecf20Sopenharmony_ci int i; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci for (i = 0; i < video->attached_count; i++) { 13068c2ecf20Sopenharmony_ci ids = &video->attached_array[i]; 13078c2ecf20Sopenharmony_ci if (device->device_id == (ids->value.int_val & 0xffff)) { 13088c2ecf20Sopenharmony_ci ids->bind_info = device; 13098c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i)); 13108c2ecf20Sopenharmony_ci } 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistatic bool acpi_video_device_in_dod(struct acpi_video_device *device) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci struct acpi_video_bus *video = device->video; 13178c2ecf20Sopenharmony_ci int i; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci /* 13208c2ecf20Sopenharmony_ci * If we have a broken _DOD or we have more than 8 output devices 13218c2ecf20Sopenharmony_ci * under the graphics controller node that we can't proper deal with 13228c2ecf20Sopenharmony_ci * in the operation region code currently, no need to test. 13238c2ecf20Sopenharmony_ci */ 13248c2ecf20Sopenharmony_ci if (!video->attached_count || video->child_count > 8) 13258c2ecf20Sopenharmony_ci return true; 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci for (i = 0; i < video->attached_count; i++) { 13288c2ecf20Sopenharmony_ci if ((video->attached_array[i].value.int_val & 0xfff) == 13298c2ecf20Sopenharmony_ci (device->device_id & 0xfff)) 13308c2ecf20Sopenharmony_ci return true; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci return false; 13348c2ecf20Sopenharmony_ci} 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci/* 13378c2ecf20Sopenharmony_ci * Arg: 13388c2ecf20Sopenharmony_ci * video : video bus device 13398c2ecf20Sopenharmony_ci * 13408c2ecf20Sopenharmony_ci * Return: 13418c2ecf20Sopenharmony_ci * < 0 : error 13428c2ecf20Sopenharmony_ci * 13438c2ecf20Sopenharmony_ci * Call _DOD to enumerate all devices attached to display adapter 13448c2ecf20Sopenharmony_ci * 13458c2ecf20Sopenharmony_ci */ 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_cistatic int acpi_video_device_enumerate(struct acpi_video_bus *video) 13488c2ecf20Sopenharmony_ci{ 13498c2ecf20Sopenharmony_ci int status; 13508c2ecf20Sopenharmony_ci int count; 13518c2ecf20Sopenharmony_ci int i; 13528c2ecf20Sopenharmony_ci struct acpi_video_enumerated_device *active_list; 13538c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 13548c2ecf20Sopenharmony_ci union acpi_object *dod = NULL; 13558c2ecf20Sopenharmony_ci union acpi_object *obj; 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci if (!video->cap._DOD) 13588c2ecf20Sopenharmony_ci return AE_NOT_EXIST; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); 13618c2ecf20Sopenharmony_ci if (!ACPI_SUCCESS(status)) { 13628c2ecf20Sopenharmony_ci ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); 13638c2ecf20Sopenharmony_ci return status; 13648c2ecf20Sopenharmony_ci } 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci dod = buffer.pointer; 13678c2ecf20Sopenharmony_ci if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) { 13688c2ecf20Sopenharmony_ci ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data")); 13698c2ecf20Sopenharmony_ci status = -EFAULT; 13708c2ecf20Sopenharmony_ci goto out; 13718c2ecf20Sopenharmony_ci } 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n", 13748c2ecf20Sopenharmony_ci dod->package.count)); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci active_list = kcalloc(1 + dod->package.count, 13778c2ecf20Sopenharmony_ci sizeof(struct acpi_video_enumerated_device), 13788c2ecf20Sopenharmony_ci GFP_KERNEL); 13798c2ecf20Sopenharmony_ci if (!active_list) { 13808c2ecf20Sopenharmony_ci status = -ENOMEM; 13818c2ecf20Sopenharmony_ci goto out; 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci count = 0; 13858c2ecf20Sopenharmony_ci for (i = 0; i < dod->package.count; i++) { 13868c2ecf20Sopenharmony_ci obj = &dod->package.elements[i]; 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci if (obj->type != ACPI_TYPE_INTEGER) { 13898c2ecf20Sopenharmony_ci printk(KERN_ERR PREFIX 13908c2ecf20Sopenharmony_ci "Invalid _DOD data in element %d\n", i); 13918c2ecf20Sopenharmony_ci continue; 13928c2ecf20Sopenharmony_ci } 13938c2ecf20Sopenharmony_ci 13948c2ecf20Sopenharmony_ci active_list[count].value.int_val = obj->integer.value; 13958c2ecf20Sopenharmony_ci active_list[count].bind_info = NULL; 13968c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, 13978c2ecf20Sopenharmony_ci (int)obj->integer.value)); 13988c2ecf20Sopenharmony_ci count++; 13998c2ecf20Sopenharmony_ci } 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci kfree(video->attached_array); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci video->attached_array = active_list; 14048c2ecf20Sopenharmony_ci video->attached_count = count; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ciout: 14078c2ecf20Sopenharmony_ci kfree(buffer.pointer); 14088c2ecf20Sopenharmony_ci return status; 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_cistatic int 14128c2ecf20Sopenharmony_ciacpi_video_get_next_level(struct acpi_video_device *device, 14138c2ecf20Sopenharmony_ci u32 level_current, u32 event) 14148c2ecf20Sopenharmony_ci{ 14158c2ecf20Sopenharmony_ci int min, max, min_above, max_below, i, l, delta = 255; 14168c2ecf20Sopenharmony_ci max = max_below = 0; 14178c2ecf20Sopenharmony_ci min = min_above = 255; 14188c2ecf20Sopenharmony_ci /* Find closest level to level_current */ 14198c2ecf20Sopenharmony_ci for (i = ACPI_VIDEO_FIRST_LEVEL; i < device->brightness->count; i++) { 14208c2ecf20Sopenharmony_ci l = device->brightness->levels[i]; 14218c2ecf20Sopenharmony_ci if (abs(l - level_current) < abs(delta)) { 14228c2ecf20Sopenharmony_ci delta = l - level_current; 14238c2ecf20Sopenharmony_ci if (!delta) 14248c2ecf20Sopenharmony_ci break; 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci /* Ajust level_current to closest available level */ 14288c2ecf20Sopenharmony_ci level_current += delta; 14298c2ecf20Sopenharmony_ci for (i = ACPI_VIDEO_FIRST_LEVEL; i < device->brightness->count; i++) { 14308c2ecf20Sopenharmony_ci l = device->brightness->levels[i]; 14318c2ecf20Sopenharmony_ci if (l < min) 14328c2ecf20Sopenharmony_ci min = l; 14338c2ecf20Sopenharmony_ci if (l > max) 14348c2ecf20Sopenharmony_ci max = l; 14358c2ecf20Sopenharmony_ci if (l < min_above && l > level_current) 14368c2ecf20Sopenharmony_ci min_above = l; 14378c2ecf20Sopenharmony_ci if (l > max_below && l < level_current) 14388c2ecf20Sopenharmony_ci max_below = l; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci switch (event) { 14428c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: 14438c2ecf20Sopenharmony_ci return (level_current < max) ? min_above : min; 14448c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: 14458c2ecf20Sopenharmony_ci return (level_current < max) ? min_above : max; 14468c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: 14478c2ecf20Sopenharmony_ci return (level_current > min) ? max_below : min; 14488c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: 14498c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: 14508c2ecf20Sopenharmony_ci return 0; 14518c2ecf20Sopenharmony_ci default: 14528c2ecf20Sopenharmony_ci return level_current; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci} 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_cistatic void 14578c2ecf20Sopenharmony_ciacpi_video_switch_brightness(struct work_struct *work) 14588c2ecf20Sopenharmony_ci{ 14598c2ecf20Sopenharmony_ci struct acpi_video_device *device = container_of(to_delayed_work(work), 14608c2ecf20Sopenharmony_ci struct acpi_video_device, switch_brightness_work); 14618c2ecf20Sopenharmony_ci unsigned long long level_current, level_next; 14628c2ecf20Sopenharmony_ci int event = device->switch_brightness_event; 14638c2ecf20Sopenharmony_ci int result = -EINVAL; 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci /* no warning message if acpi_backlight=vendor or a quirk is used */ 14668c2ecf20Sopenharmony_ci if (!device->backlight) 14678c2ecf20Sopenharmony_ci return; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci if (!device->brightness) 14708c2ecf20Sopenharmony_ci goto out; 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci result = acpi_video_device_lcd_get_level_current(device, 14738c2ecf20Sopenharmony_ci &level_current, 14748c2ecf20Sopenharmony_ci false); 14758c2ecf20Sopenharmony_ci if (result) 14768c2ecf20Sopenharmony_ci goto out; 14778c2ecf20Sopenharmony_ci 14788c2ecf20Sopenharmony_ci level_next = acpi_video_get_next_level(device, level_current, event); 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci result = acpi_video_device_lcd_set_level(device, level_next); 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci if (!result) 14838c2ecf20Sopenharmony_ci backlight_force_update(device->backlight, 14848c2ecf20Sopenharmony_ci BACKLIGHT_UPDATE_HOTKEY); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ciout: 14878c2ecf20Sopenharmony_ci if (result) 14888c2ecf20Sopenharmony_ci printk(KERN_ERR PREFIX "Failed to switch the brightness\n"); 14898c2ecf20Sopenharmony_ci} 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ciint acpi_video_get_edid(struct acpi_device *device, int type, int device_id, 14928c2ecf20Sopenharmony_ci void **edid) 14938c2ecf20Sopenharmony_ci{ 14948c2ecf20Sopenharmony_ci struct acpi_video_bus *video; 14958c2ecf20Sopenharmony_ci struct acpi_video_device *video_device; 14968c2ecf20Sopenharmony_ci union acpi_object *buffer = NULL; 14978c2ecf20Sopenharmony_ci acpi_status status; 14988c2ecf20Sopenharmony_ci int i, length; 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci if (!device || !acpi_driver_data(device)) 15018c2ecf20Sopenharmony_ci return -EINVAL; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci video = acpi_driver_data(device); 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci for (i = 0; i < video->attached_count; i++) { 15068c2ecf20Sopenharmony_ci video_device = video->attached_array[i].bind_info; 15078c2ecf20Sopenharmony_ci length = 256; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci if (!video_device) 15108c2ecf20Sopenharmony_ci continue; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (!video_device->cap._DDC) 15138c2ecf20Sopenharmony_ci continue; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci if (type) { 15168c2ecf20Sopenharmony_ci switch (type) { 15178c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_CRT: 15188c2ecf20Sopenharmony_ci if (!video_device->flags.crt) 15198c2ecf20Sopenharmony_ci continue; 15208c2ecf20Sopenharmony_ci break; 15218c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_TV: 15228c2ecf20Sopenharmony_ci if (!video_device->flags.tvout) 15238c2ecf20Sopenharmony_ci continue; 15248c2ecf20Sopenharmony_ci break; 15258c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_DVI: 15268c2ecf20Sopenharmony_ci if (!video_device->flags.dvi) 15278c2ecf20Sopenharmony_ci continue; 15288c2ecf20Sopenharmony_ci break; 15298c2ecf20Sopenharmony_ci case ACPI_VIDEO_DISPLAY_LCD: 15308c2ecf20Sopenharmony_ci if (!video_device->flags.lcd) 15318c2ecf20Sopenharmony_ci continue; 15328c2ecf20Sopenharmony_ci break; 15338c2ecf20Sopenharmony_ci } 15348c2ecf20Sopenharmony_ci } else if (video_device->device_id != device_id) { 15358c2ecf20Sopenharmony_ci continue; 15368c2ecf20Sopenharmony_ci } 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci status = acpi_video_device_EDID(video_device, &buffer, length); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || !buffer || 15418c2ecf20Sopenharmony_ci buffer->type != ACPI_TYPE_BUFFER) { 15428c2ecf20Sopenharmony_ci length = 128; 15438c2ecf20Sopenharmony_ci status = acpi_video_device_EDID(video_device, &buffer, 15448c2ecf20Sopenharmony_ci length); 15458c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status) || !buffer || 15468c2ecf20Sopenharmony_ci buffer->type != ACPI_TYPE_BUFFER) { 15478c2ecf20Sopenharmony_ci continue; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci *edid = buffer->buffer.pointer; 15528c2ecf20Sopenharmony_ci return length; 15538c2ecf20Sopenharmony_ci } 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_ci return -ENODEV; 15568c2ecf20Sopenharmony_ci} 15578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_video_get_edid); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_cistatic int 15608c2ecf20Sopenharmony_ciacpi_video_bus_get_devices(struct acpi_video_bus *video, 15618c2ecf20Sopenharmony_ci struct acpi_device *device) 15628c2ecf20Sopenharmony_ci{ 15638c2ecf20Sopenharmony_ci int status = 0; 15648c2ecf20Sopenharmony_ci struct acpi_device *dev; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci /* 15678c2ecf20Sopenharmony_ci * There are systems where video module known to work fine regardless 15688c2ecf20Sopenharmony_ci * of broken _DOD and ignoring returned value here doesn't cause 15698c2ecf20Sopenharmony_ci * any issues later. 15708c2ecf20Sopenharmony_ci */ 15718c2ecf20Sopenharmony_ci acpi_video_device_enumerate(video); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci list_for_each_entry(dev, &device->children, node) { 15748c2ecf20Sopenharmony_ci 15758c2ecf20Sopenharmony_ci status = acpi_video_bus_get_one_device(dev, video); 15768c2ecf20Sopenharmony_ci if (status) { 15778c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Can't attach device\n"); 15788c2ecf20Sopenharmony_ci break; 15798c2ecf20Sopenharmony_ci } 15808c2ecf20Sopenharmony_ci video->child_count++; 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci return status; 15838c2ecf20Sopenharmony_ci} 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci/* acpi_video interface */ 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci/* 15888c2ecf20Sopenharmony_ci * Win8 requires setting bit2 of _DOS to let firmware know it shouldn't 15898c2ecf20Sopenharmony_ci * preform any automatic brightness change on receiving a notification. 15908c2ecf20Sopenharmony_ci */ 15918c2ecf20Sopenharmony_cistatic int acpi_video_bus_start_devices(struct acpi_video_bus *video) 15928c2ecf20Sopenharmony_ci{ 15938c2ecf20Sopenharmony_ci return acpi_video_bus_DOS(video, 0, 15948c2ecf20Sopenharmony_ci acpi_osi_is_win8() ? 1 : 0); 15958c2ecf20Sopenharmony_ci} 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_cistatic int acpi_video_bus_stop_devices(struct acpi_video_bus *video) 15988c2ecf20Sopenharmony_ci{ 15998c2ecf20Sopenharmony_ci return acpi_video_bus_DOS(video, 0, 16008c2ecf20Sopenharmony_ci acpi_osi_is_win8() ? 0 : 1); 16018c2ecf20Sopenharmony_ci} 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_cistatic void acpi_video_bus_notify(struct acpi_device *device, u32 event) 16048c2ecf20Sopenharmony_ci{ 16058c2ecf20Sopenharmony_ci struct acpi_video_bus *video = acpi_driver_data(device); 16068c2ecf20Sopenharmony_ci struct input_dev *input; 16078c2ecf20Sopenharmony_ci int keycode = 0; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci if (!video || !video->input) 16108c2ecf20Sopenharmony_ci return; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci input = video->input; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci switch (event) { 16158c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch, 16168c2ecf20Sopenharmony_ci * most likely via hotkey. */ 16178c2ecf20Sopenharmony_ci keycode = KEY_SWITCHVIDEOMODE; 16188c2ecf20Sopenharmony_ci break; 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video 16218c2ecf20Sopenharmony_ci * connector. */ 16228c2ecf20Sopenharmony_ci acpi_video_device_enumerate(video); 16238c2ecf20Sopenharmony_ci acpi_video_device_rebind(video); 16248c2ecf20Sopenharmony_ci keycode = KEY_SWITCHVIDEOMODE; 16258c2ecf20Sopenharmony_ci break; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */ 16288c2ecf20Sopenharmony_ci keycode = KEY_SWITCHVIDEOMODE; 16298c2ecf20Sopenharmony_ci break; 16308c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ 16318c2ecf20Sopenharmony_ci keycode = KEY_VIDEO_NEXT; 16328c2ecf20Sopenharmony_ci break; 16338c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ 16348c2ecf20Sopenharmony_ci keycode = KEY_VIDEO_PREV; 16358c2ecf20Sopenharmony_ci break; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci default: 16388c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, 16398c2ecf20Sopenharmony_ci "Unsupported event [0x%x]\n", event)); 16408c2ecf20Sopenharmony_ci break; 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci if (acpi_notifier_call_chain(device, event, 0)) 16448c2ecf20Sopenharmony_ci /* Something vetoed the keypress. */ 16458c2ecf20Sopenharmony_ci keycode = 0; 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci if (keycode && (report_key_events & REPORT_OUTPUT_KEY_EVENTS)) { 16488c2ecf20Sopenharmony_ci input_report_key(input, keycode, 1); 16498c2ecf20Sopenharmony_ci input_sync(input); 16508c2ecf20Sopenharmony_ci input_report_key(input, keycode, 0); 16518c2ecf20Sopenharmony_ci input_sync(input); 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci return; 16558c2ecf20Sopenharmony_ci} 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_cistatic void brightness_switch_event(struct acpi_video_device *video_device, 16588c2ecf20Sopenharmony_ci u32 event) 16598c2ecf20Sopenharmony_ci{ 16608c2ecf20Sopenharmony_ci if (!brightness_switch_enabled) 16618c2ecf20Sopenharmony_ci return; 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_ci video_device->switch_brightness_event = event; 16648c2ecf20Sopenharmony_ci schedule_delayed_work(&video_device->switch_brightness_work, HZ / 10); 16658c2ecf20Sopenharmony_ci} 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_cistatic void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) 16688c2ecf20Sopenharmony_ci{ 16698c2ecf20Sopenharmony_ci struct acpi_video_device *video_device = data; 16708c2ecf20Sopenharmony_ci struct acpi_device *device = NULL; 16718c2ecf20Sopenharmony_ci struct acpi_video_bus *bus; 16728c2ecf20Sopenharmony_ci struct input_dev *input; 16738c2ecf20Sopenharmony_ci int keycode = 0; 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (!video_device) 16768c2ecf20Sopenharmony_ci return; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci device = video_device->dev; 16798c2ecf20Sopenharmony_ci bus = video_device->video; 16808c2ecf20Sopenharmony_ci input = bus->input; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci if (hw_changes_brightness > 0) { 16838c2ecf20Sopenharmony_ci if (video_device->backlight) 16848c2ecf20Sopenharmony_ci backlight_force_update(video_device->backlight, 16858c2ecf20Sopenharmony_ci BACKLIGHT_UPDATE_HOTKEY); 16868c2ecf20Sopenharmony_ci acpi_notifier_call_chain(device, event, 0); 16878c2ecf20Sopenharmony_ci return; 16888c2ecf20Sopenharmony_ci } 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci switch (event) { 16918c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ 16928c2ecf20Sopenharmony_ci brightness_switch_event(video_device, event); 16938c2ecf20Sopenharmony_ci keycode = KEY_BRIGHTNESS_CYCLE; 16948c2ecf20Sopenharmony_ci break; 16958c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ 16968c2ecf20Sopenharmony_ci brightness_switch_event(video_device, event); 16978c2ecf20Sopenharmony_ci keycode = KEY_BRIGHTNESSUP; 16988c2ecf20Sopenharmony_ci break; 16998c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ 17008c2ecf20Sopenharmony_ci brightness_switch_event(video_device, event); 17018c2ecf20Sopenharmony_ci keycode = KEY_BRIGHTNESSDOWN; 17028c2ecf20Sopenharmony_ci break; 17038c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightness */ 17048c2ecf20Sopenharmony_ci brightness_switch_event(video_device, event); 17058c2ecf20Sopenharmony_ci keycode = KEY_BRIGHTNESS_ZERO; 17068c2ecf20Sopenharmony_ci break; 17078c2ecf20Sopenharmony_ci case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ 17088c2ecf20Sopenharmony_ci brightness_switch_event(video_device, event); 17098c2ecf20Sopenharmony_ci keycode = KEY_DISPLAY_OFF; 17108c2ecf20Sopenharmony_ci break; 17118c2ecf20Sopenharmony_ci default: 17128c2ecf20Sopenharmony_ci ACPI_DEBUG_PRINT((ACPI_DB_INFO, 17138c2ecf20Sopenharmony_ci "Unsupported event [0x%x]\n", event)); 17148c2ecf20Sopenharmony_ci break; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci acpi_notifier_call_chain(device, event, 0); 17188c2ecf20Sopenharmony_ci 17198c2ecf20Sopenharmony_ci if (keycode && (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS)) { 17208c2ecf20Sopenharmony_ci input_report_key(input, keycode, 1); 17218c2ecf20Sopenharmony_ci input_sync(input); 17228c2ecf20Sopenharmony_ci input_report_key(input, keycode, 0); 17238c2ecf20Sopenharmony_ci input_sync(input); 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci return; 17278c2ecf20Sopenharmony_ci} 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_cistatic int acpi_video_resume(struct notifier_block *nb, 17308c2ecf20Sopenharmony_ci unsigned long val, void *ign) 17318c2ecf20Sopenharmony_ci{ 17328c2ecf20Sopenharmony_ci struct acpi_video_bus *video; 17338c2ecf20Sopenharmony_ci struct acpi_video_device *video_device; 17348c2ecf20Sopenharmony_ci int i; 17358c2ecf20Sopenharmony_ci 17368c2ecf20Sopenharmony_ci switch (val) { 17378c2ecf20Sopenharmony_ci case PM_HIBERNATION_PREPARE: 17388c2ecf20Sopenharmony_ci case PM_SUSPEND_PREPARE: 17398c2ecf20Sopenharmony_ci case PM_RESTORE_PREPARE: 17408c2ecf20Sopenharmony_ci return NOTIFY_DONE; 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci video = container_of(nb, struct acpi_video_bus, pm_nb); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci dev_info(&video->device->dev, "Restoring backlight state\n"); 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci for (i = 0; i < video->attached_count; i++) { 17488c2ecf20Sopenharmony_ci video_device = video->attached_array[i].bind_info; 17498c2ecf20Sopenharmony_ci if (video_device && video_device->brightness) 17508c2ecf20Sopenharmony_ci acpi_video_device_lcd_set_level(video_device, 17518c2ecf20Sopenharmony_ci video_device->brightness->curr); 17528c2ecf20Sopenharmony_ci } 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci return NOTIFY_OK; 17558c2ecf20Sopenharmony_ci} 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_cistatic acpi_status 17588c2ecf20Sopenharmony_ciacpi_video_bus_match(acpi_handle handle, u32 level, void *context, 17598c2ecf20Sopenharmony_ci void **return_value) 17608c2ecf20Sopenharmony_ci{ 17618c2ecf20Sopenharmony_ci struct acpi_device *device = context; 17628c2ecf20Sopenharmony_ci struct acpi_device *sibling; 17638c2ecf20Sopenharmony_ci int result; 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci if (handle == device->handle) 17668c2ecf20Sopenharmony_ci return AE_CTRL_TERMINATE; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci result = acpi_bus_get_device(handle, &sibling); 17698c2ecf20Sopenharmony_ci if (result) 17708c2ecf20Sopenharmony_ci return AE_OK; 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) 17738c2ecf20Sopenharmony_ci return AE_ALREADY_EXISTS; 17748c2ecf20Sopenharmony_ci 17758c2ecf20Sopenharmony_ci return AE_OK; 17768c2ecf20Sopenharmony_ci} 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_cistatic void acpi_video_dev_register_backlight(struct acpi_video_device *device) 17798c2ecf20Sopenharmony_ci{ 17808c2ecf20Sopenharmony_ci struct backlight_properties props; 17818c2ecf20Sopenharmony_ci struct pci_dev *pdev; 17828c2ecf20Sopenharmony_ci acpi_handle acpi_parent; 17838c2ecf20Sopenharmony_ci struct device *parent = NULL; 17848c2ecf20Sopenharmony_ci int result; 17858c2ecf20Sopenharmony_ci static int count; 17868c2ecf20Sopenharmony_ci char *name; 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci result = acpi_video_init_brightness(device); 17898c2ecf20Sopenharmony_ci if (result) 17908c2ecf20Sopenharmony_ci return; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci if (disable_backlight_sysfs_if > 0) 17938c2ecf20Sopenharmony_ci return; 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "acpi_video%d", count); 17968c2ecf20Sopenharmony_ci if (!name) 17978c2ecf20Sopenharmony_ci return; 17988c2ecf20Sopenharmony_ci count++; 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(acpi_get_parent(device->dev->handle, &acpi_parent))) { 18018c2ecf20Sopenharmony_ci pdev = acpi_get_pci_dev(acpi_parent); 18028c2ecf20Sopenharmony_ci if (pdev) { 18038c2ecf20Sopenharmony_ci parent = &pdev->dev; 18048c2ecf20Sopenharmony_ci pci_dev_put(pdev); 18058c2ecf20Sopenharmony_ci } 18068c2ecf20Sopenharmony_ci } 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci memset(&props, 0, sizeof(struct backlight_properties)); 18098c2ecf20Sopenharmony_ci props.type = BACKLIGHT_FIRMWARE; 18108c2ecf20Sopenharmony_ci props.max_brightness = 18118c2ecf20Sopenharmony_ci device->brightness->count - ACPI_VIDEO_FIRST_LEVEL - 1; 18128c2ecf20Sopenharmony_ci device->backlight = backlight_device_register(name, 18138c2ecf20Sopenharmony_ci parent, 18148c2ecf20Sopenharmony_ci device, 18158c2ecf20Sopenharmony_ci &acpi_backlight_ops, 18168c2ecf20Sopenharmony_ci &props); 18178c2ecf20Sopenharmony_ci kfree(name); 18188c2ecf20Sopenharmony_ci if (IS_ERR(device->backlight)) { 18198c2ecf20Sopenharmony_ci device->backlight = NULL; 18208c2ecf20Sopenharmony_ci return; 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci /* 18248c2ecf20Sopenharmony_ci * Save current brightness level in case we have to restore it 18258c2ecf20Sopenharmony_ci * before acpi_video_device_lcd_set_level() is called next time. 18268c2ecf20Sopenharmony_ci */ 18278c2ecf20Sopenharmony_ci device->backlight->props.brightness = 18288c2ecf20Sopenharmony_ci acpi_video_get_brightness(device->backlight); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci device->cooling_dev = thermal_cooling_device_register("LCD", 18318c2ecf20Sopenharmony_ci device->dev, &video_cooling_ops); 18328c2ecf20Sopenharmony_ci if (IS_ERR(device->cooling_dev)) { 18338c2ecf20Sopenharmony_ci /* 18348c2ecf20Sopenharmony_ci * Set cooling_dev to NULL so we don't crash trying to free it. 18358c2ecf20Sopenharmony_ci * Also, why the hell we are returning early and not attempt to 18368c2ecf20Sopenharmony_ci * register video output if cooling device registration failed? 18378c2ecf20Sopenharmony_ci * -- dtor 18388c2ecf20Sopenharmony_ci */ 18398c2ecf20Sopenharmony_ci device->cooling_dev = NULL; 18408c2ecf20Sopenharmony_ci return; 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci dev_info(&device->dev->dev, "registered as cooling_device%d\n", 18448c2ecf20Sopenharmony_ci device->cooling_dev->id); 18458c2ecf20Sopenharmony_ci result = sysfs_create_link(&device->dev->dev.kobj, 18468c2ecf20Sopenharmony_ci &device->cooling_dev->device.kobj, 18478c2ecf20Sopenharmony_ci "thermal_cooling"); 18488c2ecf20Sopenharmony_ci if (result) 18498c2ecf20Sopenharmony_ci printk(KERN_ERR PREFIX "Create sysfs link\n"); 18508c2ecf20Sopenharmony_ci result = sysfs_create_link(&device->cooling_dev->device.kobj, 18518c2ecf20Sopenharmony_ci &device->dev->dev.kobj, "device"); 18528c2ecf20Sopenharmony_ci if (result) 18538c2ecf20Sopenharmony_ci printk(KERN_ERR PREFIX "Create sysfs link\n"); 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic void acpi_video_run_bcl_for_osi(struct acpi_video_bus *video) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci struct acpi_video_device *dev; 18598c2ecf20Sopenharmony_ci union acpi_object *levels; 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci mutex_lock(&video->device_list_lock); 18628c2ecf20Sopenharmony_ci list_for_each_entry(dev, &video->video_device_list, entry) { 18638c2ecf20Sopenharmony_ci if (!acpi_video_device_lcd_query_levels(dev->dev->handle, &levels)) 18648c2ecf20Sopenharmony_ci kfree(levels); 18658c2ecf20Sopenharmony_ci } 18668c2ecf20Sopenharmony_ci mutex_unlock(&video->device_list_lock); 18678c2ecf20Sopenharmony_ci} 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_cistatic bool acpi_video_should_register_backlight(struct acpi_video_device *dev) 18708c2ecf20Sopenharmony_ci{ 18718c2ecf20Sopenharmony_ci /* 18728c2ecf20Sopenharmony_ci * Do not create backlight device for video output 18738c2ecf20Sopenharmony_ci * device that is not in the enumerated list. 18748c2ecf20Sopenharmony_ci */ 18758c2ecf20Sopenharmony_ci if (!acpi_video_device_in_dod(dev)) { 18768c2ecf20Sopenharmony_ci dev_dbg(&dev->dev->dev, "not in _DOD list, ignore\n"); 18778c2ecf20Sopenharmony_ci return false; 18788c2ecf20Sopenharmony_ci } 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci if (only_lcd) 18818c2ecf20Sopenharmony_ci return dev->flags.lcd; 18828c2ecf20Sopenharmony_ci return true; 18838c2ecf20Sopenharmony_ci} 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_cistatic int acpi_video_bus_register_backlight(struct acpi_video_bus *video) 18868c2ecf20Sopenharmony_ci{ 18878c2ecf20Sopenharmony_ci struct acpi_video_device *dev; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci if (video->backlight_registered) 18908c2ecf20Sopenharmony_ci return 0; 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci acpi_video_run_bcl_for_osi(video); 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci if (acpi_video_get_backlight_type() != acpi_backlight_video) 18958c2ecf20Sopenharmony_ci return 0; 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci mutex_lock(&video->device_list_lock); 18988c2ecf20Sopenharmony_ci list_for_each_entry(dev, &video->video_device_list, entry) { 18998c2ecf20Sopenharmony_ci if (acpi_video_should_register_backlight(dev)) 19008c2ecf20Sopenharmony_ci acpi_video_dev_register_backlight(dev); 19018c2ecf20Sopenharmony_ci } 19028c2ecf20Sopenharmony_ci mutex_unlock(&video->device_list_lock); 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci video->backlight_registered = true; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci video->pm_nb.notifier_call = acpi_video_resume; 19078c2ecf20Sopenharmony_ci video->pm_nb.priority = 0; 19088c2ecf20Sopenharmony_ci return register_pm_notifier(&video->pm_nb); 19098c2ecf20Sopenharmony_ci} 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_cistatic void acpi_video_dev_unregister_backlight(struct acpi_video_device *device) 19128c2ecf20Sopenharmony_ci{ 19138c2ecf20Sopenharmony_ci if (device->backlight) { 19148c2ecf20Sopenharmony_ci backlight_device_unregister(device->backlight); 19158c2ecf20Sopenharmony_ci device->backlight = NULL; 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci if (device->brightness) { 19188c2ecf20Sopenharmony_ci kfree(device->brightness->levels); 19198c2ecf20Sopenharmony_ci kfree(device->brightness); 19208c2ecf20Sopenharmony_ci device->brightness = NULL; 19218c2ecf20Sopenharmony_ci } 19228c2ecf20Sopenharmony_ci if (device->cooling_dev) { 19238c2ecf20Sopenharmony_ci sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling"); 19248c2ecf20Sopenharmony_ci sysfs_remove_link(&device->cooling_dev->device.kobj, "device"); 19258c2ecf20Sopenharmony_ci thermal_cooling_device_unregister(device->cooling_dev); 19268c2ecf20Sopenharmony_ci device->cooling_dev = NULL; 19278c2ecf20Sopenharmony_ci } 19288c2ecf20Sopenharmony_ci} 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_cistatic int acpi_video_bus_unregister_backlight(struct acpi_video_bus *video) 19318c2ecf20Sopenharmony_ci{ 19328c2ecf20Sopenharmony_ci struct acpi_video_device *dev; 19338c2ecf20Sopenharmony_ci int error; 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci if (!video->backlight_registered) 19368c2ecf20Sopenharmony_ci return 0; 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci error = unregister_pm_notifier(&video->pm_nb); 19398c2ecf20Sopenharmony_ci 19408c2ecf20Sopenharmony_ci mutex_lock(&video->device_list_lock); 19418c2ecf20Sopenharmony_ci list_for_each_entry(dev, &video->video_device_list, entry) 19428c2ecf20Sopenharmony_ci acpi_video_dev_unregister_backlight(dev); 19438c2ecf20Sopenharmony_ci mutex_unlock(&video->device_list_lock); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci video->backlight_registered = false; 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci return error; 19488c2ecf20Sopenharmony_ci} 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_cistatic void acpi_video_dev_add_notify_handler(struct acpi_video_device *device) 19518c2ecf20Sopenharmony_ci{ 19528c2ecf20Sopenharmony_ci acpi_status status; 19538c2ecf20Sopenharmony_ci struct acpi_device *adev = device->dev; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci status = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, 19568c2ecf20Sopenharmony_ci acpi_video_device_notify, device); 19578c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) 19588c2ecf20Sopenharmony_ci dev_err(&adev->dev, "Error installing notify handler\n"); 19598c2ecf20Sopenharmony_ci else 19608c2ecf20Sopenharmony_ci device->flags.notify = 1; 19618c2ecf20Sopenharmony_ci} 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_cistatic int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video) 19648c2ecf20Sopenharmony_ci{ 19658c2ecf20Sopenharmony_ci struct input_dev *input; 19668c2ecf20Sopenharmony_ci struct acpi_video_device *dev; 19678c2ecf20Sopenharmony_ci int error; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci video->input = input = input_allocate_device(); 19708c2ecf20Sopenharmony_ci if (!input) { 19718c2ecf20Sopenharmony_ci error = -ENOMEM; 19728c2ecf20Sopenharmony_ci goto out; 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci error = acpi_video_bus_start_devices(video); 19768c2ecf20Sopenharmony_ci if (error) 19778c2ecf20Sopenharmony_ci goto err_free_input; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci snprintf(video->phys, sizeof(video->phys), 19808c2ecf20Sopenharmony_ci "%s/video/input0", acpi_device_hid(video->device)); 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci input->name = acpi_device_name(video->device); 19838c2ecf20Sopenharmony_ci input->phys = video->phys; 19848c2ecf20Sopenharmony_ci input->id.bustype = BUS_HOST; 19858c2ecf20Sopenharmony_ci input->id.product = 0x06; 19868c2ecf20Sopenharmony_ci input->dev.parent = &video->device->dev; 19878c2ecf20Sopenharmony_ci input->evbit[0] = BIT(EV_KEY); 19888c2ecf20Sopenharmony_ci set_bit(KEY_SWITCHVIDEOMODE, input->keybit); 19898c2ecf20Sopenharmony_ci set_bit(KEY_VIDEO_NEXT, input->keybit); 19908c2ecf20Sopenharmony_ci set_bit(KEY_VIDEO_PREV, input->keybit); 19918c2ecf20Sopenharmony_ci set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit); 19928c2ecf20Sopenharmony_ci set_bit(KEY_BRIGHTNESSUP, input->keybit); 19938c2ecf20Sopenharmony_ci set_bit(KEY_BRIGHTNESSDOWN, input->keybit); 19948c2ecf20Sopenharmony_ci set_bit(KEY_BRIGHTNESS_ZERO, input->keybit); 19958c2ecf20Sopenharmony_ci set_bit(KEY_DISPLAY_OFF, input->keybit); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci error = input_register_device(input); 19988c2ecf20Sopenharmony_ci if (error) 19998c2ecf20Sopenharmony_ci goto err_stop_dev; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci mutex_lock(&video->device_list_lock); 20028c2ecf20Sopenharmony_ci list_for_each_entry(dev, &video->video_device_list, entry) 20038c2ecf20Sopenharmony_ci acpi_video_dev_add_notify_handler(dev); 20048c2ecf20Sopenharmony_ci mutex_unlock(&video->device_list_lock); 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci return 0; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_cierr_stop_dev: 20098c2ecf20Sopenharmony_ci acpi_video_bus_stop_devices(video); 20108c2ecf20Sopenharmony_cierr_free_input: 20118c2ecf20Sopenharmony_ci input_free_device(input); 20128c2ecf20Sopenharmony_ci video->input = NULL; 20138c2ecf20Sopenharmony_ciout: 20148c2ecf20Sopenharmony_ci return error; 20158c2ecf20Sopenharmony_ci} 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_cistatic void acpi_video_dev_remove_notify_handler(struct acpi_video_device *dev) 20188c2ecf20Sopenharmony_ci{ 20198c2ecf20Sopenharmony_ci if (dev->flags.notify) { 20208c2ecf20Sopenharmony_ci acpi_remove_notify_handler(dev->dev->handle, ACPI_DEVICE_NOTIFY, 20218c2ecf20Sopenharmony_ci acpi_video_device_notify); 20228c2ecf20Sopenharmony_ci dev->flags.notify = 0; 20238c2ecf20Sopenharmony_ci } 20248c2ecf20Sopenharmony_ci} 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_cistatic void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video) 20278c2ecf20Sopenharmony_ci{ 20288c2ecf20Sopenharmony_ci struct acpi_video_device *dev; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci mutex_lock(&video->device_list_lock); 20318c2ecf20Sopenharmony_ci list_for_each_entry(dev, &video->video_device_list, entry) 20328c2ecf20Sopenharmony_ci acpi_video_dev_remove_notify_handler(dev); 20338c2ecf20Sopenharmony_ci mutex_unlock(&video->device_list_lock); 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci acpi_video_bus_stop_devices(video); 20368c2ecf20Sopenharmony_ci input_unregister_device(video->input); 20378c2ecf20Sopenharmony_ci video->input = NULL; 20388c2ecf20Sopenharmony_ci} 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_cistatic int acpi_video_bus_put_devices(struct acpi_video_bus *video) 20418c2ecf20Sopenharmony_ci{ 20428c2ecf20Sopenharmony_ci struct acpi_video_device *dev, *next; 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci mutex_lock(&video->device_list_lock); 20458c2ecf20Sopenharmony_ci list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { 20468c2ecf20Sopenharmony_ci list_del(&dev->entry); 20478c2ecf20Sopenharmony_ci kfree(dev); 20488c2ecf20Sopenharmony_ci } 20498c2ecf20Sopenharmony_ci mutex_unlock(&video->device_list_lock); 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci return 0; 20528c2ecf20Sopenharmony_ci} 20538c2ecf20Sopenharmony_ci 20548c2ecf20Sopenharmony_cistatic int instance; 20558c2ecf20Sopenharmony_ci 20568c2ecf20Sopenharmony_cistatic int acpi_video_bus_add(struct acpi_device *device) 20578c2ecf20Sopenharmony_ci{ 20588c2ecf20Sopenharmony_ci struct acpi_video_bus *video; 20598c2ecf20Sopenharmony_ci int error; 20608c2ecf20Sopenharmony_ci acpi_status status; 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci status = acpi_walk_namespace(ACPI_TYPE_DEVICE, 20638c2ecf20Sopenharmony_ci device->parent->handle, 1, 20648c2ecf20Sopenharmony_ci acpi_video_bus_match, NULL, 20658c2ecf20Sopenharmony_ci device, NULL); 20668c2ecf20Sopenharmony_ci if (status == AE_ALREADY_EXISTS) { 20678c2ecf20Sopenharmony_ci printk(KERN_WARNING FW_BUG 20688c2ecf20Sopenharmony_ci "Duplicate ACPI video bus devices for the" 20698c2ecf20Sopenharmony_ci " same VGA controller, please try module " 20708c2ecf20Sopenharmony_ci "parameter \"video.allow_duplicates=1\"" 20718c2ecf20Sopenharmony_ci "if the current driver doesn't work.\n"); 20728c2ecf20Sopenharmony_ci if (!allow_duplicates) 20738c2ecf20Sopenharmony_ci return -ENODEV; 20748c2ecf20Sopenharmony_ci } 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); 20778c2ecf20Sopenharmony_ci if (!video) 20788c2ecf20Sopenharmony_ci return -ENOMEM; 20798c2ecf20Sopenharmony_ci 20808c2ecf20Sopenharmony_ci /* a hack to fix the duplicate name "VID" problem on T61 */ 20818c2ecf20Sopenharmony_ci if (!strcmp(device->pnp.bus_id, "VID")) { 20828c2ecf20Sopenharmony_ci if (instance) 20838c2ecf20Sopenharmony_ci device->pnp.bus_id[3] = '0' + instance; 20848c2ecf20Sopenharmony_ci instance++; 20858c2ecf20Sopenharmony_ci } 20868c2ecf20Sopenharmony_ci /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */ 20878c2ecf20Sopenharmony_ci if (!strcmp(device->pnp.bus_id, "VGA")) { 20888c2ecf20Sopenharmony_ci if (instance) 20898c2ecf20Sopenharmony_ci device->pnp.bus_id[3] = '0' + instance; 20908c2ecf20Sopenharmony_ci instance++; 20918c2ecf20Sopenharmony_ci } 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci video->device = device; 20948c2ecf20Sopenharmony_ci strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); 20958c2ecf20Sopenharmony_ci strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); 20968c2ecf20Sopenharmony_ci device->driver_data = video; 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci acpi_video_bus_find_cap(video); 20998c2ecf20Sopenharmony_ci error = acpi_video_bus_check(video); 21008c2ecf20Sopenharmony_ci if (error) 21018c2ecf20Sopenharmony_ci goto err_free_video; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci mutex_init(&video->device_list_lock); 21048c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&video->video_device_list); 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci error = acpi_video_bus_get_devices(video, device); 21078c2ecf20Sopenharmony_ci if (error) 21088c2ecf20Sopenharmony_ci goto err_put_video; 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", 21118c2ecf20Sopenharmony_ci ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), 21128c2ecf20Sopenharmony_ci video->flags.multihead ? "yes" : "no", 21138c2ecf20Sopenharmony_ci video->flags.rom ? "yes" : "no", 21148c2ecf20Sopenharmony_ci video->flags.post ? "yes" : "no"); 21158c2ecf20Sopenharmony_ci mutex_lock(&video_list_lock); 21168c2ecf20Sopenharmony_ci list_add_tail(&video->entry, &video_bus_head); 21178c2ecf20Sopenharmony_ci mutex_unlock(&video_list_lock); 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci acpi_video_bus_register_backlight(video); 21208c2ecf20Sopenharmony_ci acpi_video_bus_add_notify_handler(video); 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci return 0; 21238c2ecf20Sopenharmony_ci 21248c2ecf20Sopenharmony_cierr_put_video: 21258c2ecf20Sopenharmony_ci acpi_video_bus_put_devices(video); 21268c2ecf20Sopenharmony_ci kfree(video->attached_array); 21278c2ecf20Sopenharmony_cierr_free_video: 21288c2ecf20Sopenharmony_ci kfree(video); 21298c2ecf20Sopenharmony_ci device->driver_data = NULL; 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci return error; 21328c2ecf20Sopenharmony_ci} 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_cistatic int acpi_video_bus_remove(struct acpi_device *device) 21358c2ecf20Sopenharmony_ci{ 21368c2ecf20Sopenharmony_ci struct acpi_video_bus *video = NULL; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci if (!device || !acpi_driver_data(device)) 21408c2ecf20Sopenharmony_ci return -EINVAL; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci video = acpi_driver_data(device); 21438c2ecf20Sopenharmony_ci 21448c2ecf20Sopenharmony_ci acpi_video_bus_remove_notify_handler(video); 21458c2ecf20Sopenharmony_ci acpi_video_bus_unregister_backlight(video); 21468c2ecf20Sopenharmony_ci acpi_video_bus_put_devices(video); 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci mutex_lock(&video_list_lock); 21498c2ecf20Sopenharmony_ci list_del(&video->entry); 21508c2ecf20Sopenharmony_ci mutex_unlock(&video_list_lock); 21518c2ecf20Sopenharmony_ci 21528c2ecf20Sopenharmony_ci kfree(video->attached_array); 21538c2ecf20Sopenharmony_ci kfree(video); 21548c2ecf20Sopenharmony_ci 21558c2ecf20Sopenharmony_ci return 0; 21568c2ecf20Sopenharmony_ci} 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_cistatic int __init is_i740(struct pci_dev *dev) 21598c2ecf20Sopenharmony_ci{ 21608c2ecf20Sopenharmony_ci if (dev->device == 0x00D1) 21618c2ecf20Sopenharmony_ci return 1; 21628c2ecf20Sopenharmony_ci if (dev->device == 0x7000) 21638c2ecf20Sopenharmony_ci return 1; 21648c2ecf20Sopenharmony_ci return 0; 21658c2ecf20Sopenharmony_ci} 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_cistatic int __init intel_opregion_present(void) 21688c2ecf20Sopenharmony_ci{ 21698c2ecf20Sopenharmony_ci int opregion = 0; 21708c2ecf20Sopenharmony_ci struct pci_dev *dev = NULL; 21718c2ecf20Sopenharmony_ci u32 address; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci for_each_pci_dev(dev) { 21748c2ecf20Sopenharmony_ci if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) 21758c2ecf20Sopenharmony_ci continue; 21768c2ecf20Sopenharmony_ci if (dev->vendor != PCI_VENDOR_ID_INTEL) 21778c2ecf20Sopenharmony_ci continue; 21788c2ecf20Sopenharmony_ci /* We don't want to poke around undefined i740 registers */ 21798c2ecf20Sopenharmony_ci if (is_i740(dev)) 21808c2ecf20Sopenharmony_ci continue; 21818c2ecf20Sopenharmony_ci pci_read_config_dword(dev, 0xfc, &address); 21828c2ecf20Sopenharmony_ci if (!address) 21838c2ecf20Sopenharmony_ci continue; 21848c2ecf20Sopenharmony_ci opregion = 1; 21858c2ecf20Sopenharmony_ci } 21868c2ecf20Sopenharmony_ci return opregion; 21878c2ecf20Sopenharmony_ci} 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci/* Check if the chassis-type indicates there is no builtin LCD panel */ 21908c2ecf20Sopenharmony_cistatic bool dmi_is_desktop(void) 21918c2ecf20Sopenharmony_ci{ 21928c2ecf20Sopenharmony_ci const char *chassis_type; 21938c2ecf20Sopenharmony_ci unsigned long type; 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci chassis_type = dmi_get_system_info(DMI_CHASSIS_TYPE); 21968c2ecf20Sopenharmony_ci if (!chassis_type) 21978c2ecf20Sopenharmony_ci return false; 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci if (kstrtoul(chassis_type, 10, &type) != 0) 22008c2ecf20Sopenharmony_ci return false; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci switch (type) { 22038c2ecf20Sopenharmony_ci case 0x03: /* Desktop */ 22048c2ecf20Sopenharmony_ci case 0x04: /* Low Profile Desktop */ 22058c2ecf20Sopenharmony_ci case 0x05: /* Pizza Box */ 22068c2ecf20Sopenharmony_ci case 0x06: /* Mini Tower */ 22078c2ecf20Sopenharmony_ci case 0x07: /* Tower */ 22088c2ecf20Sopenharmony_ci case 0x10: /* Lunch Box */ 22098c2ecf20Sopenharmony_ci case 0x11: /* Main Server Chassis */ 22108c2ecf20Sopenharmony_ci return true; 22118c2ecf20Sopenharmony_ci } 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci return false; 22148c2ecf20Sopenharmony_ci} 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ciint acpi_video_register(void) 22178c2ecf20Sopenharmony_ci{ 22188c2ecf20Sopenharmony_ci int ret = 0; 22198c2ecf20Sopenharmony_ci 22208c2ecf20Sopenharmony_ci mutex_lock(®ister_count_mutex); 22218c2ecf20Sopenharmony_ci if (register_count) { 22228c2ecf20Sopenharmony_ci /* 22238c2ecf20Sopenharmony_ci * if the function of acpi_video_register is already called, 22248c2ecf20Sopenharmony_ci * don't register the acpi_video_bus again and return no error. 22258c2ecf20Sopenharmony_ci */ 22268c2ecf20Sopenharmony_ci goto leave; 22278c2ecf20Sopenharmony_ci } 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci /* 22308c2ecf20Sopenharmony_ci * We're seeing a lot of bogus backlight interfaces on newer machines 22318c2ecf20Sopenharmony_ci * without a LCD such as desktops, servers and HDMI sticks. Checking 22328c2ecf20Sopenharmony_ci * the lcd flag fixes this, so enable this on any machines which are 22338c2ecf20Sopenharmony_ci * win8 ready (where we also prefer the native backlight driver, so 22348c2ecf20Sopenharmony_ci * normally the acpi_video code should not register there anyways). 22358c2ecf20Sopenharmony_ci */ 22368c2ecf20Sopenharmony_ci if (only_lcd == -1) { 22378c2ecf20Sopenharmony_ci if (dmi_is_desktop() && acpi_osi_is_win8()) 22388c2ecf20Sopenharmony_ci only_lcd = true; 22398c2ecf20Sopenharmony_ci else 22408c2ecf20Sopenharmony_ci only_lcd = false; 22418c2ecf20Sopenharmony_ci } 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci dmi_check_system(video_dmi_table); 22448c2ecf20Sopenharmony_ci 22458c2ecf20Sopenharmony_ci ret = acpi_bus_register_driver(&acpi_video_bus); 22468c2ecf20Sopenharmony_ci if (ret) 22478c2ecf20Sopenharmony_ci goto leave; 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci /* 22508c2ecf20Sopenharmony_ci * When the acpi_video_bus is loaded successfully, increase 22518c2ecf20Sopenharmony_ci * the counter reference. 22528c2ecf20Sopenharmony_ci */ 22538c2ecf20Sopenharmony_ci register_count = 1; 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_cileave: 22568c2ecf20Sopenharmony_ci mutex_unlock(®ister_count_mutex); 22578c2ecf20Sopenharmony_ci return ret; 22588c2ecf20Sopenharmony_ci} 22598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_video_register); 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_civoid acpi_video_unregister(void) 22628c2ecf20Sopenharmony_ci{ 22638c2ecf20Sopenharmony_ci mutex_lock(®ister_count_mutex); 22648c2ecf20Sopenharmony_ci if (register_count) { 22658c2ecf20Sopenharmony_ci acpi_bus_unregister_driver(&acpi_video_bus); 22668c2ecf20Sopenharmony_ci register_count = 0; 22678c2ecf20Sopenharmony_ci } 22688c2ecf20Sopenharmony_ci mutex_unlock(®ister_count_mutex); 22698c2ecf20Sopenharmony_ci} 22708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_video_unregister); 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_civoid acpi_video_unregister_backlight(void) 22738c2ecf20Sopenharmony_ci{ 22748c2ecf20Sopenharmony_ci struct acpi_video_bus *video; 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci mutex_lock(®ister_count_mutex); 22778c2ecf20Sopenharmony_ci if (register_count) { 22788c2ecf20Sopenharmony_ci mutex_lock(&video_list_lock); 22798c2ecf20Sopenharmony_ci list_for_each_entry(video, &video_bus_head, entry) 22808c2ecf20Sopenharmony_ci acpi_video_bus_unregister_backlight(video); 22818c2ecf20Sopenharmony_ci mutex_unlock(&video_list_lock); 22828c2ecf20Sopenharmony_ci } 22838c2ecf20Sopenharmony_ci mutex_unlock(®ister_count_mutex); 22848c2ecf20Sopenharmony_ci} 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_cibool acpi_video_handles_brightness_key_presses(void) 22878c2ecf20Sopenharmony_ci{ 22888c2ecf20Sopenharmony_ci bool have_video_busses; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci mutex_lock(&video_list_lock); 22918c2ecf20Sopenharmony_ci have_video_busses = !list_empty(&video_bus_head); 22928c2ecf20Sopenharmony_ci mutex_unlock(&video_list_lock); 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci return have_video_busses && 22958c2ecf20Sopenharmony_ci (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS); 22968c2ecf20Sopenharmony_ci} 22978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_video_handles_brightness_key_presses); 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci/* 23008c2ecf20Sopenharmony_ci * This is kind of nasty. Hardware using Intel chipsets may require 23018c2ecf20Sopenharmony_ci * the video opregion code to be run first in order to initialise 23028c2ecf20Sopenharmony_ci * state before any ACPI video calls are made. To handle this we defer 23038c2ecf20Sopenharmony_ci * registration of the video class until the opregion code has run. 23048c2ecf20Sopenharmony_ci */ 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_cistatic int __init acpi_video_init(void) 23078c2ecf20Sopenharmony_ci{ 23088c2ecf20Sopenharmony_ci /* 23098c2ecf20Sopenharmony_ci * Let the module load even if ACPI is disabled (e.g. due to 23108c2ecf20Sopenharmony_ci * a broken BIOS) so that i915.ko can still be loaded on such 23118c2ecf20Sopenharmony_ci * old systems without an AcpiOpRegion. 23128c2ecf20Sopenharmony_ci * 23138c2ecf20Sopenharmony_ci * acpi_video_register() will report -ENODEV later as well due 23148c2ecf20Sopenharmony_ci * to acpi_disabled when i915.ko tries to register itself afterwards. 23158c2ecf20Sopenharmony_ci */ 23168c2ecf20Sopenharmony_ci if (acpi_disabled) 23178c2ecf20Sopenharmony_ci return 0; 23188c2ecf20Sopenharmony_ci 23198c2ecf20Sopenharmony_ci if (intel_opregion_present()) 23208c2ecf20Sopenharmony_ci return 0; 23218c2ecf20Sopenharmony_ci 23228c2ecf20Sopenharmony_ci return acpi_video_register(); 23238c2ecf20Sopenharmony_ci} 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_cistatic void __exit acpi_video_exit(void) 23268c2ecf20Sopenharmony_ci{ 23278c2ecf20Sopenharmony_ci acpi_video_detect_exit(); 23288c2ecf20Sopenharmony_ci acpi_video_unregister(); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci return; 23318c2ecf20Sopenharmony_ci} 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_cimodule_init(acpi_video_init); 23348c2ecf20Sopenharmony_cimodule_exit(acpi_video_exit); 2335