cregit-Linux how code gets into the kernel

Release 4.11 drivers/acpi/acpi_video.c

Directory: drivers/acpi
/*
 *  video.c - ACPI Video Driver
 *
 *  Copyright (C) 2004 Luming Yu <luming.yu@intel.com>
 *  Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org>
 *  Copyright (C) 2006 Thomas Tuttle <linux-kernel@ttuttle.net>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/input.h>
#include <linux/backlight.h>
#include <linux/thermal.h>
#include <linux/sort.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/slab.h>
#include <linux/dmi.h>
#include <linux/suspend.h>
#include <linux/acpi.h>
#include <acpi/video.h>
#include <linux/uaccess.h>


#define PREFIX "ACPI: "


#define ACPI_VIDEO_BUS_NAME		"Video Bus"

#define ACPI_VIDEO_DEVICE_NAME		"Video Device"


#define MAX_NAME_LEN	20


#define _COMPONENT		ACPI_VIDEO_COMPONENT
ACPI_MODULE_NAME("video");

MODULE_AUTHOR("Bruno Ducrot");
MODULE_DESCRIPTION("ACPI Video Driver");
MODULE_LICENSE("GPL");


static bool brightness_switch_enabled = 1;
module_param(brightness_switch_enabled, bool, 0644);

/*
 * By default, we don't allow duplicate ACPI video bus devices
 * under the same VGA controller
 */

static bool allow_duplicates;
module_param(allow_duplicates, bool, 0644);


static int disable_backlight_sysfs_if = -1;
module_param(disable_backlight_sysfs_if, int, 0444);


#define REPORT_OUTPUT_KEY_EVENTS		0x01

#define REPORT_BRIGHTNESS_KEY_EVENTS		0x02

static int report_key_events = -1;
module_param(report_key_events, int, 0644);
MODULE_PARM_DESC(report_key_events,
	"0: none, 1: output changes, 2: brightness changes, 3: all");


static bool device_id_scheme = false;
module_param(device_id_scheme, bool, 0444);


static bool only_lcd = false;
module_param(only_lcd, bool, 0444);


static int register_count;
static DEFINE_MUTEX(register_count_mutex);
static DEFINE_MUTEX(video_list_lock);
static LIST_HEAD(video_bus_head);
static int acpi_video_bus_add(struct acpi_device *device);
static int acpi_video_bus_remove(struct acpi_device *device);
static void acpi_video_bus_notify(struct acpi_device *device, u32 event);
void acpi_video_detect_exit(void);


static const struct acpi_device_id video_device_ids[] = {
	{ACPI_VIDEO_HID, 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, video_device_ids);


static struct acpi_driver acpi_video_bus = {
	.name = "video",
	.class = ACPI_VIDEO_CLASS,
	.ids = video_device_ids,
	.ops = {
		.add = acpi_video_bus_add,
		.remove = acpi_video_bus_remove,
		.notify = acpi_video_bus_notify,
                },
};


struct acpi_video_bus_flags {
	
u8 multihead:1;		/* can switch video heads */
	
u8 rom:1;		/* can retrieve a video rom */
	
u8 post:1;		/* can configure the head to */
	
u8 reserved:5;
};


struct acpi_video_bus_cap {
	
u8 _DOS:1;		/* Enable/Disable output switching */
	
u8 _DOD:1;		/* Enumerate all devices attached to display adapter */
	
u8 _ROM:1;		/* Get ROM Data */
	
u8 _GPD:1;		/* Get POST Device */
	
u8 _SPD:1;		/* Set POST Device */
	
u8 _VPO:1;		/* Video POST Options */
	
u8 reserved:2;
};


struct acpi_video_device_attrib {
	
u32 display_index:4;	/* A zero-based instance of the Display */
	
u32 display_port_attachment:4;	/* This field differentiates the display type */
	
u32 display_type:4;	/* Describe the specific type in use */
	
u32 vendor_specific:4;	/* Chipset Vendor Specific */
	
u32 bios_can_detect:1;	/* BIOS can detect the device */
	
u32 depend_on_vga:1;	/* Non-VGA output device whose power is related to
                                   the VGA device. */
	
u32 pipe_id:3;		/* For VGA multiple-head devices. */
	
u32 reserved:10;	/* Must be 0 */
	
u32 device_id_scheme:1;	/* Device ID Scheme */
};


struct acpi_video_enumerated_device {
	union {
		
u32 int_val;
		
struct acpi_video_device_attrib attrib;
	} 
value;
	
struct acpi_video_device *bind_info;
};


struct acpi_video_bus {
	
struct acpi_device *device;
	
bool backlight_registered;
	
u8 dos_setting;
	
struct acpi_video_enumerated_device *attached_array;
	
u8 attached_count;
	
u8 child_count;
	
struct acpi_video_bus_cap cap;
	
struct acpi_video_bus_flags flags;
	
struct list_head video_device_list;
	
struct mutex device_list_lock;	/* protects video_device_list */
	
struct list_head entry;
	
struct input_dev *input;
	
char phys[32];	/* for input device */
	
struct notifier_block pm_nb;
};


struct acpi_video_device_flags {
	
u8 crt:1;
	
u8 lcd:1;
	
u8 tvout:1;
	
u8 dvi:1;
	
u8 bios:1;
	
u8 unknown:1;
	
u8 notify:1;
	
u8 reserved:1;
};


struct acpi_video_device_cap {
	
u8 _ADR:1;		/* Return the unique ID */
	
u8 _BCL:1;		/* Query list of brightness control levels supported */
	
u8 _BCM:1;		/* Set the brightness level */
	
u8 _BQC:1;		/* Get current brightness level */
	
u8 _BCQ:1;		/* Some buggy BIOS uses _BCQ instead of _BQC */
	
u8 _DDC:1;		/* Return the EDID for this device */
};


struct acpi_video_device {
	
unsigned long device_id;
	
struct acpi_video_device_flags flags;
	
struct acpi_video_device_cap cap;
	
struct list_head entry;
	
struct delayed_work switch_brightness_work;
	
int switch_brightness_event;
	
struct acpi_video_bus *video;
	
struct acpi_device *dev;
	
struct acpi_video_device_brightness *brightness;
	
struct backlight_device *backlight;
	
struct thermal_cooling_device *cooling_dev;
};

static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data);
static void acpi_video_device_rebind(struct acpi_video_bus *video);
static void acpi_video_device_bind(struct acpi_video_bus *video,
				   struct acpi_video_device *device);
static int acpi_video_device_enumerate(struct acpi_video_bus *video);
static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,
			int level);
static int acpi_video_device_lcd_get_level_current(
			struct acpi_video_device *device,
			unsigned long long *level, bool raw);
static int acpi_video_get_next_level(struct acpi_video_device *device,
				     u32 level_current, u32 event);
static void acpi_video_switch_brightness(struct work_struct *work);

/* backlight device sysfs support */

static int acpi_video_get_brightness(struct backlight_device *bd) { unsigned long long cur_level; int i; struct acpi_video_device *vd = bl_get_data(bd); if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false)) return -EINVAL; for (i = 2; i < vd->brightness->count; i++) { if (vd->brightness->levels[i] == cur_level) /* * The first two entries are special - see page 575 * of the ACPI spec 3.0 */ return i - 2; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang7688.37%120.00%
Jan Engelhardt55.81%120.00%
Len Brown33.49%120.00%
Felipe Contreras11.16%120.00%
Danny Baumann11.16%120.00%
Total86100.00%5100.00%


static int acpi_video_set_brightness(struct backlight_device *bd) { int request_level = bd->props.brightness + 2; struct acpi_video_device *vd = bl_get_data(bd); cancel_delayed_work(&vd->switch_brightness_work); return acpi_video_device_lcd_set_level(vd, vd->brightness->levels[request_level]); }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang3970.91%125.00%
Linus Torvalds814.55%125.00%
Len Brown610.91%125.00%
Jan Engelhardt23.64%125.00%
Total55100.00%4100.00%

static const struct backlight_ops acpi_backlight_ops = { .get_brightness = acpi_video_get_brightness, .update_status = acpi_video_set_brightness, }; /* thermal cooling device callbacks */
static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned long *state) { struct acpi_device *device = cooling_dev->devdata; struct acpi_video_device *video = acpi_driver_data(device); *state = video->brightness->count - 3; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang3877.55%150.00%
Len Brown1122.45%150.00%
Total49100.00%2100.00%


static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long *state) { struct acpi_device *device = cooling_dev->devdata; struct acpi_video_device *video = acpi_driver_data(device); unsigned long long level; int offset; if (acpi_video_device_lcd_get_level_current(video, &level, false)) return -EINVAL; for (offset = 2; offset < video->brightness->count; offset++) if (level == video->brightness->levels[offset]) { *state = video->brightness->count - offset - 1; return 0; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang5650.45%225.00%
Matthew Garrett2825.23%225.00%
Yu Luming1917.12%112.50%
Len Brown65.41%112.50%
Matthew Wilcox10.90%112.50%
Danny Baumann10.90%112.50%
Total111100.00%8100.00%


static int video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long state) { struct acpi_device *device = cooling_dev->devdata; struct acpi_video_device *video = acpi_driver_data(device); int level; if (state >= video->brightness->count - 2) return -EINVAL; state = video->brightness->count - state; level = video->brightness->levels[state - 1]; return acpi_video_device_lcd_set_level(video, level); }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang5869.05%250.00%
Yu Luming2023.81%125.00%
Matthew Garrett67.14%125.00%
Total84100.00%4100.00%

static const struct thermal_cooling_device_ops video_cooling_ops = { .get_max_state = video_get_max_state, .get_cur_state = video_get_cur_state, .set_cur_state = video_set_cur_state, }; /* * -------------------------------------------------------------------------- * Video Management * -------------------------------------------------------------------------- */
static int acpi_video_device_lcd_query_levels(acpi_handle handle, union acpi_object **levels) { int status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; *levels = NULL; status = acpi_evaluate_object(handle, "_BCL", NULL, &buffer); if (!ACPI_SUCCESS(status)) return status; obj = (union acpi_object *)buffer.pointer; if (!obj || (obj->type != ACPI_TYPE_PACKAGE)) { printk(KERN_ERR PREFIX "Invalid _BCL data\n"); status = -EFAULT; goto err; } *levels = obj; return 0; err: kfree(buffer.pointer); return status; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang11694.31%360.00%
Matthew Garrett54.07%120.00%
Aaron Lu21.63%120.00%
Total123100.00%5100.00%


static int acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level) { int status; int state; status = acpi_execute_simple_method(device->dev->handle, "_BCM", level); if (ACPI_FAILURE(status)) { ACPI_ERROR((AE_INFO, "Evaluating _BCM failed")); return -EIO; } device->brightness->curr = level; for (state = 2; state < device->brightness->count; state++) if (level == device->brightness->levels[state]) { if (device->backlight) device->backlight->props.brightness = state - 2; return 0; } ACPI_ERROR((AE_INFO, "Current brightness invalid")); return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang11990.84%233.33%
Len Brown75.34%116.67%
Patrick Mochel32.29%233.33%
Jiang Liu21.53%116.67%
Total131100.00%6100.00%

/* * For some buggy _BQC methods, we need to add a constant value to * the _BQC return value to get the actual current brightness level */ static int bqc_offset_aml_bug_workaround;
static int video_set_bqc_offset(const struct dmi_system_id *d) { bqc_offset_aml_bug_workaround = 9; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Len Brown1052.63%133.33%
Rui Zhang736.84%133.33%
Patrick Mochel210.53%133.33%
Total19100.00%3100.00%


static int video_disable_backlight_sysfs_if( const struct dmi_system_id *d) { if (disable_backlight_sysfs_if == -1) disable_backlight_sysfs_if = 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans de Goede26100.00%1100.00%
Total26100.00%1100.00%


static int video_set_device_id_scheme(const struct dmi_system_id *d) { device_id_scheme = true; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu19100.00%1100.00%
Total19100.00%1100.00%


static int video_enable_only_lcd(const struct dmi_system_id *d) { only_lcd = true; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu19100.00%1100.00%
Total19100.00%1100.00%


static int video_set_report_key_events(const struct dmi_system_id *id) { if (report_key_events == -1) report_key_events = (uintptr_t)id->driver_data; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Hans de Goede31100.00%1100.00%
Total31100.00%1100.00%

static struct dmi_system_id video_dmi_table[] = { /* * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 */ { .callback = video_set_bqc_offset, .ident = "Acer Aspire 5720", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"), }, }, { .callback = video_set_bqc_offset, .ident = "Acer Aspire 5710Z", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710Z"), }, }, { .callback = video_set_bqc_offset, .ident = "eMachines E510", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "EMACHINES"), DMI_MATCH(DMI_PRODUCT_NAME, "eMachines E510"), }, }, { .callback = video_set_bqc_offset, .ident = "Acer Aspire 5315", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), }, }, { .callback = video_set_bqc_offset, .ident = "Acer Aspire 7720", .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"), }, }, /* * Some machines have a broken acpi-video interface for brightness * control, but still need an acpi_video_device_lcd_set_level() call * on resume to turn the backlight power on. We Enable backlight * control on these systems, but do not register a backlight sysfs * as brightness control does not work. */ { /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */ .callback = video_disable_backlight_sysfs_if, .ident = "Toshiba Portege R700", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R700"), }, }, { /* https://bugs.freedesktop.org/show_bug.cgi?id=82634 */ .callback = video_disable_backlight_sysfs_if, .ident = "Toshiba Portege R830", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "PORTEGE R830"), }, }, { /* https://bugzilla.kernel.org/show_bug.cgi?id=21012 */ .callback = video_disable_backlight_sysfs_if, .ident = "Toshiba Satellite R830", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE R830"), }, }, /* * Some machine's _DOD IDs don't have bit 31(Device ID Scheme) set * but the IDs actually follow the Device ID Scheme. */ { /* https://bugzilla.kernel.org/show_bug.cgi?id=104121 */ .callback = video_set_device_id_scheme, .ident = "ESPRIMO Mobile M9410", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile M9410"), }, }, /* * Some machines have multiple video output devices, but only the one * that is the type of LCD can do the backlight control so we should not * register backlight interface for other video output devices. */ { /* https://bugzilla.kernel.org/show_bug.cgi?id=104121 */ .callback = video_enable_only_lcd, .ident = "ESPRIMO Mobile M9410", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile M9410"), }, }, /* * Some machines report wrong key events on the acpi-bus, suppress * key event reporting on these. Note this is only intended to work * around events which are plain wrong. In some cases we get double * events, in this case acpi-video is considered the canonical source * and the events from the other source should be filtered. E.g. * by calling acpi_video_handles_brightness_key_presses() from the * vendor acpi/wmi driver or by using /lib/udev/hwdb.d/60-keyboard.hwdb */ { .callback = video_set_report_key_events, .driver_data = (void *)((uintptr_t)REPORT_OUTPUT_KEY_EVENTS), .ident = "Dell Vostro V131", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"), }, }, {} };
static unsigned long long acpi_video_bqc_value_to_level(struct acpi_video_device *device, unsigned long long bqc_value) { unsigned long long level; if (device->brightness->flags._BQC_use_index) { /* * _BQC returns an index that doesn't account for * the first 2 items with special meaning, so we need * to compensate for that by offsetting ourselves */ if (device->brightness->flags._BCL_reversed) bqc_value = device->brightness->count - 3 - bqc_value; level = device->brightness->levels[bqc_value + 2]; } else { level = bqc_value; } level += bqc_offset_aml_bug_workaround; return level; }

Contributors

PersonTokensPropCommitsCommitProp
Danny Baumann8498.82%150.00%
Len Brown11.18%150.00%
Total85100.00%2100.00%


static int acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, unsigned long long *level, bool raw) { acpi_status status = AE_OK; int i; if (device->cap._BQC || device->cap._BCQ) { char *buf = device->cap._BQC ? "_BQC" : "_BCQ"; status = acpi_evaluate_integer(device->dev->handle, buf, NULL, level); if (ACPI_SUCCESS(status)) { if (raw) { /* * Caller has indicated he wants the raw * value returned by _BQC, so don't furtherly * mess with the value. */ return 0; } *level = acpi_video_bqc_value_to_level(device, *level); for (i = 2; i < device->brightness->count; i++) if (device->brightness->levels[i] == *level) { device->brightness->curr = *level; return 0; } /* * BQC returned an invalid level. * Stop using it. */ ACPI_WARNING((AE_INFO, "%s returned an invalid level", buf)); device->cap._BQC = device->cap._BCQ = 0; } else { /* * Fixme: * should we return an error or ignore this failure? * dev->brightness->curr is a cached value which stores * the correct current backlight level in most cases. * ACPI video backlight still works w/ buggy _BQC. * http://bugzilla.kernel.org/show_bug.cgi?id=12233 */ ACPI_WARNING((AE_INFO, "Evaluating %s failed", buf)); device->cap._BQC = device->cap._BCQ = 0; } } *level = device->brightness->curr; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang16776.96%436.36%
Len Brown2712.44%19.09%
Danny Baumann167.37%218.18%
Alexey Y. Starikovskiy31.38%19.09%
Patrick Mochel31.38%218.18%
Felipe Contreras10.46%19.09%
Total217100.00%11100.00%


static int acpi_video_device_EDID(struct acpi_video_device *device, union acpi_object **edid, ssize_t length) { int status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; union acpi_object arg0 = { ACPI_TYPE_INTEGER }; struct acpi_object_list args = { 1, &arg0 }; *edid = NULL; if (!device) return -ENODEV; if (length == 128) arg0.integer.value = 1; else if (length == 256) arg0.integer.value = 2; else return -EINVAL; status = acpi_evaluate_object(device->dev->handle, "_DDC", &args, &buffer); if (ACPI_FAILURE(status)) return -ENODEV; obj = buffer.pointer; if (obj && obj->type == ACPI_TYPE_BUFFER) *edid = obj; else { printk(KERN_ERR PREFIX "Invalid _DDC data\n"); status = -EFAULT; kfree(obj); } return status; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang17497.75%583.33%
Len Brown42.25%116.67%
Total178100.00%6100.00%

/* bus */ /* * Arg: * video : video bus device pointer * bios_flag : * 0. The system BIOS should NOT automatically switch(toggle) * the active display output. * 1. The system BIOS should automatically switch (toggle) the * active display output. No switch event. * 2. The _DGS value should be locked. * 3. The system BIOS should not automatically switch (toggle) the * active display output, but instead generate the display switch * event notify code. * lcd_flag : * 0. The system BIOS should automatically control the brightness level * of the LCD when the power changes from AC to DC * 1. The system BIOS should NOT automatically control the brightness * level of the LCD when the power changes from AC to DC. * Return Value: * -EINVAL wrong arg. */
static int acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) { acpi_status status; if (!video->cap._DOS) return 0; if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) return -EINVAL; video->dos_setting = (lcd_flag << 2) | bios_flag; status = acpi_execute_simple_method(video->device->handle, "_DOS", (lcd_flag << 2) | bios_flag); if (ACPI_FAILURE(status)) return -EIO; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang5251.49%440.00%
Igor Murzov1817.82%110.00%
Len Brown1413.86%110.00%
Jiang Liu1110.89%110.00%
Alexey Y. Starikovskiy32.97%110.00%
Matthew Garrett21.98%110.00%
Patrick Mochel10.99%110.00%
Total101100.00%10100.00%

/* * Simple comparison function used to sort backlight levels. */
static int acpi_video_cmp_level(const void *a, const void *b) { return *(int *)a - *(int *)b; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang31100.00%1100.00%
Total31100.00%1100.00%

/* * Decides if _BQC/_BCQ for this system is usable * * We do this by changing the level first and then read out the current * brightness level, if the value does not match, find out if it is using * index. If not, clear the _BQC/_BCQ capability. */
static int acpi_video_bqc_quirk(struct acpi_video_device *device, int max_level, int current_level) { struct acpi_video_device_brightness *br = device->brightness; int result; unsigned long long level; int test_level; /* don't mess with existing known broken systems */ if (bqc_offset_aml_bug_workaround) return 0; /* * Some systems always report current brightness level as maximum * through _BQC, we need to test another value for them. */ test_level = current_level == max_level ? br->levels[3] : max_level; result = acpi_video_device_lcd_set_level(device, test_level); if (result) return result; result = acpi_video_device_lcd_get_level_current(device, &level, true); if (result) return result; if (level != test_level) { /* buggy _BQC found, need to find out if it uses index */ if (level < br->count) { if (br->flags._BCL_reversed) level = br->count - 3 - level; if (br->levels[level + 2] == test_level) br->flags._BQC_use_index = 1; } if (!br->flags._BQC_use_index) device->cap._BQC = device->cap._BCQ = 0; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu17999.44%150.00%
Felipe Contreras10.56%150.00%
Total180100.00%2100.00%


int acpi_video_get_levels(struct acpi_device *device, struct acpi_video_device_brightness **dev_br, int *pmax_level) { union acpi_object *obj = NULL; int i, max_level = 0, count = 0, level_ac_battery = 0; union acpi_object *o; struct acpi_video_device_brightness *br = NULL; int result = 0; u32 value; if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device->handle, &obj))) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available " "LCD brightness level\n")); result = -ENODEV; goto out; } if (obj->package.count < 2) { result = -EINVAL; goto out; } br = kzalloc(sizeof(*br), GFP_KERNEL); if (!br) { printk(KERN_ERR "can't allocate memory\n"); result = -ENOMEM; goto out; } br->levels = kmalloc((obj->package.count + 2) * sizeof *(br->levels), GFP_KERNEL); if (!br->levels) { result = -ENOMEM; goto out_free; } for (i = 0; i < obj->package.count; i++) { o = (union acpi_object *)&obj->package.elements[i]; if (o->type != ACPI_TYPE_INTEGER) { printk(KERN_ERR PREFIX "Invalid data\n"); continue; } value = (u32) o->integer.value; /* Skip duplicate entries */ if (count > 2 && br->levels[count - 1] == value) continue; br->levels[count] = value; if (br->levels[count] > max_level) max_level = br->levels[count]; count++; } /* * some buggy BIOS don't export the levels * when machine is on AC/Battery in _BCL package. * In this case, the first two elements in _BCL packages * are also supported brightness levels that OS should take care of. */ for (i = 2; i < count; i++) { if (br->levels[i] == br->levels[0]) level_ac_battery++; if (br->levels[i] == br->levels[1]) level_ac_battery++; } if (level_ac_battery < 2) { level_ac_battery = 2 - level_ac_battery; br->flags._BCL_no_ac_battery_levels = 1; for (i = (count - 1 + level_ac_battery); i >= 2; i--) br->levels[i] = br->levels[i - level_ac_battery]; count += level_ac_battery; } else if (level_ac_battery > 2) ACPI_ERROR((AE_INFO, "Too many duplicates in _BCL package")); /* Check if the _BCL package is in a reversed order */ if (max_level == br->levels[2]) { br->flags._BCL_reversed = 1; sort(&br->levels[2], count - 2, sizeof(br->levels[2]), acpi_video_cmp_level, NULL); } else if (max_level != br->levels[count - 1]) ACPI_ERROR((AE_INFO, "Found unordered _BCL package")); br->count = count; *dev_br = br; if (pmax_level) *pmax_level = max_level; out: kfree(obj); return result; out_free: kfree(br); goto out; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang38969.96%426.67%
Aaron Lu6211.15%213.33%
Len Brown356.29%213.33%
Hans de Goede325.76%16.67%
Vladimir Serbinenko274.86%16.67%
Matthew Garrett40.72%16.67%
Dmitry Torokhov30.54%16.67%
Colin Ian King20.36%16.67%
Yakui Zhao10.18%16.67%
Arjan van de Ven10.18%16.67%
Total556100.00%15100.00%

EXPORT_SYMBOL(acpi_video_get_levels); /* * Arg: * device : video output device (LCD, CRT, ..) * * Return Value: * Maximum brightness level * * Allocate and initialize device->brightness. */
static int acpi_video_init_brightness(struct acpi_video_device *device) { int i, max_level = 0; unsigned long long level, level_old; struct acpi_video_device_brightness *br = NULL; int result = -EINVAL; result = acpi_video_get_levels(device->dev, &br, &max_level); if (result) return result; device->brightness = br; /* _BQC uses INDEX while _BCL uses VALUE in some laptops */ br->curr = level = max_level; if (!device->cap._BQC) goto set_level; result = acpi_video_device_lcd_get_level_current(device, &level_old, true); if (result) goto out_free_levels; result = acpi_video_bqc_quirk(device, max_level, level_old); if (result) goto out_free_levels; /* * cap._BQC may get cleared due to _BQC is found to be broken * in acpi_video_bqc_quirk, so check again here. */ if (!device->cap._BQC) goto set_level; level = acpi_video_bqc_value_to_level(device, level_old); /* * On some buggy laptops, _BQC returns an uninitialized * value when invoked for the first time, i.e. * level_old is invalid (no matter whether it's a level * or an index). Set the backlight to max_level in this case. */ for (i = 2; i < br->count; i++) if (level == br->levels[i]) break; if (i == br->count || !level) level = max_level; set_level: result = acpi_video_device_lcd_set_level(device, level); if (result) goto out_free_levels; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "found %d brightness levels\n", br->count - 2)); return 0; out_free_levels: kfree(br->levels); kfree(br); device->brightness = NULL; return result; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang11345.56%18.33%
Aaron Lu7831.45%650.00%
Len Brown4016.13%18.33%
Danny Baumann156.05%325.00%
Patrick Mochel20.81%18.33%
Total248100.00%12100.00%

/* * Arg: * device : video output device (LCD, CRT, ..) * * Return Value: * None * * Find out all required AML methods defined under the output * device. */
static void acpi_video_device_find_cap(struct acpi_video_device *device) { if (acpi_has_method(device->dev->handle, "_ADR")) device->cap._ADR = 1; if (acpi_has_method(device->dev->handle, "_BCL")) device->cap._BCL = 1; if (acpi_has_method(device->dev->handle, "_BCM")) device->cap._BCM = 1; if (acpi_has_method(device->dev->handle, "_BQC")) { device->cap._BQC = 1; } else if (acpi_has_method(device->dev->handle, "_BCQ")) { printk(KERN_WARNING FW_BUG "_BCQ is used instead of _BQC\n"); device->cap._BCQ = 1; } if (acpi_has_method(device->dev->handle, "_DDC")) device->cap._DDC = 1; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang11175.00%240.00%
Len Brown2818.92%120.00%
Jiang Liu85.41%120.00%
Aaron Lu10.68%120.00%
Total148100.00%5100.00%

/* * Arg: * device : video output device (VGA) * * Return Value: * None * * Find out all required AML methods defined under the video bus device. */
static void acpi_video_bus_find_cap(struct acpi_video_bus *video) { if (acpi_has_method(video->device->handle, "_DOS")) video->cap._DOS = 1; if (acpi_has_method(video->device->handle, "_DOD")) video->cap._DOD = 1; if (acpi_has_method(video->device->handle, "_ROM")) video->cap._ROM = 1; if (acpi_has_method(video->device->handle, "_GPD")) video->cap._GPD = 1; if (acpi_has_method(video->device->handle, "_SPD")) video->cap._SPD = 1; if (acpi_has_method(video->device->handle, "_VPO")) video->cap._VPO = 1; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu8259.85%116.67%
Rui Zhang2115.33%116.67%
Len Brown2014.60%116.67%
Matthew Garrett128.76%233.33%
Rafael J. Wysocki21.46%116.67%
Total137100.00%6100.00%

/* * Check whether the video bus device has required AML method to * support the desired features */
static int acpi_video_bus_check(struct acpi_video_bus *video) { acpi_status status = -ENOENT; struct pci_dev *dev; if (!video) return -EINVAL; dev = acpi_get_pci_dev(video->device->handle); if (!dev) return -ENODEV; pci_dev_put(dev); /* * Since there is no HID, CID and so on for VGA driver, we have * to check well known required nodes. */ /* Does this device support video switching? */ if (video->cap._DOS || video->cap._DOD) { if (!video->cap._DOS) { printk(KERN_WARNING FW_BUG "ACPI(%s) defines _DOD but not _DOS\n", acpi_device_bid(video->device)); } video->flags.multihead = 1; status = 0; } /* Does this device support retrieving a video ROM? */ if (video->cap._ROM) { video->flags.rom = 1; status = 0; } /* Does this device support configuring which video device to POST? */ if (video->cap._GPD && video->cap._SPD && video->cap._VPO) { video->flags.post = 1; status = 0; } return status; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang16599.40%266.67%
Felipe Contreras10.60%133.33%
Total166100.00%3100.00%

/* * -------------------------------------------------------------------------- * Driver Interface * -------------------------------------------------------------------------- */ /* device interface */
static struct acpi_video_device_attrib * acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id) { struct acpi_video_enumerated_device *ids; int i; for (i = 0; i < video->attached_count; i++) { ids = &video->attached_array[i]; if ((ids->value.int_val & 0xffff) == device_id) return &ids->value.attrib; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang5470.13%133.33%
Dmitry Torokhov2228.57%133.33%
Len Brown11.30%133.33%
Total77100.00%3100.00%


static int acpi_video_get_device_type(struct acpi_video_bus *video, unsigned long device_id) { struct acpi_video_enumerated_device *ids; int i; for (i = 0; i < video->attached_count; i++) { ids = &video->attached_array[i]; if ((ids->value.int_val & 0xffff) == device_id) return ids->value.int_val; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Garrett7297.30%133.33%
Len Brown11.35%133.33%
Rui Zhang11.35%133.33%
Total74100.00%3100.00%


static int acpi_video_bus_get_one_device(struct acpi_device *device, struct acpi_video_bus *video) { unsigned long long device_id; int status, device_type; struct acpi_video_device *data; struct acpi_video_device_attrib *attribute; status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); /* Some device omits _ADR, we skip them instead of fail */ if (ACPI_FAILURE(status)) return 0; data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); if (!data) return -ENOMEM; strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); device->driver_data = data; data->device_id = device_id; data->video = video; data->dev = device; INIT_DELAYED_WORK(&data->switch_brightness_work, acpi_video_switch_brightness); attribute = acpi_video_get_device_attr(video, device_id); if (attribute && (attribute->device_id_scheme || device_id_scheme)) { switch (attribute->display_type) { case ACPI_VIDEO_DISPLAY_CRT: data->flags.crt = 1; break; case ACPI_VIDEO_DISPLAY_TV: data->flags.tvout = 1; break; case ACPI_VIDEO_DISPLAY_DVI: data->flags.dvi = 1; break; case ACPI_VIDEO_DISPLAY_LCD: data->flags.lcd = 1; break; default: data->flags.unknown = 1; break; } if (attribute->bios_can_detect) data->flags.bios = 1; } else { /* Check for legacy IDs */ device_type = acpi_video_get_device_type(video, device_id); /* Ignore bits 16 and 18-20 */ switch (device_type & 0xffe2ffff) { case ACPI_VIDEO_DISPLAY_LEGACY_MONITOR: data->flags.crt = 1; break; case ACPI_VIDEO_DISPLAY_LEGACY_PANEL: data->flags.lcd = 1; break; case ACPI_VIDEO_DISPLAY_LEGACY_TV: data->flags.tvout = 1; break; default: data->flags.unknown = 1; } } acpi_video_device_bind(video, data); acpi_video_device_find_cap(data); mutex_lock(&video->device_list_lock); list_add_tail(&data->entry, &video->video_device_list); mutex_unlock(&video->device_list_lock); return status; }

Contributors

PersonTokensPropCommitsCommitProp
Len Brown19254.39%18.33%
Rui Zhang6718.98%18.33%
Matthew Garrett6217.56%18.33%
Aaron Lu113.12%325.00%
Linus Torvalds102.83%18.33%
Patrick Mochel41.13%18.33%
Dmitry Torokhov30.85%18.33%
Pavel Machek20.57%18.33%
Burman Yan10.28%18.33%
Matthew Wilcox10.28%18.33%
Total353100.00%12100.00%

/* * Arg: * video : video bus device * * Return: * none * * Enumerate the video device list of the video bus, * bind the ids with the corresponding video devices * under the video bus. */
static void acpi_video_device_rebind(struct acpi_video_bus *video) { struct acpi_video_device *dev; mutex_lock(&video->device_list_lock); list_for_each_entry(dev, &video->video_device_list, entry) acpi_video_device_bind(video, dev); mutex_unlock(&video->device_list_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Len Brown2757.45%133.33%
Dmitry Torokhov2042.55%266.67%
Total47100.00%3100.00%

/* * Arg: * video : video bus device * device : video output device under the video * bus * * Return: * none * * Bind the ids with the corresponding video devices * under the video bus. */
static void acpi_video_device_bind(struct acpi_video_bus *video, struct acpi_video_device *device) { struct acpi_video_enumerated_device *ids; int i; for (i = 0; i < video->attached_count; i++) { ids = &video->attached_array[i]; if (device->device_id == (ids->value.int_val & 0xffff)) { ids->bind_info = device; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i)); } } }

Contributors

PersonTokensPropCommitsCommitProp
Len Brown6373.26%150.00%
Dmitry Torokhov2326.74%150.00%
Total86100.00%2100.00%


static bool acpi_video_device_in_dod(struct acpi_video_device *device) { struct acpi_video_bus *video = device->video; int i; /* * If we have a broken _DOD or we have more than 8 output devices * under the graphics controller node that we can't proper deal with * in the operation region code currently, no need to test. */ if (!video->attached_count || video->child_count > 8) return true; for (i = 0; i < video->attached_count; i++) { if ((video->attached_array[i].value.int_val & 0xfff) == (device->device_id & 0xfff)) return true; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu88100.00%3100.00%
Total88100.00%3100.00%

/* * Arg: * video : video bus device * * Return: * < 0 : error * * Call _DOD to enumerate all devices attached to display adapter * */
static int acpi_video_device_enumerate(struct acpi_video_bus *video) { int status; int count; int i; struct acpi_video_enumerated_device *active_list; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *dod = NULL; union acpi_object *obj; if (!video->cap._DOD) return AE_NOT_EXIST; status = acpi_evaluate_object(video->device->handle, "_DOD", NULL, &buffer); if (!ACPI_SUCCESS(status)) { ACPI_EXCEPTION((AE_INFO, status, "Evaluating _DOD")); return status; } dod = buffer.pointer; if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) { ACPI_EXCEPTION((AE_INFO, status, "Invalid _DOD data")); status = -EFAULT; goto out; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n", dod->package.count)); active_list = kcalloc(1 + dod->package.count, sizeof(struct acpi_video_enumerated_device), GFP_KERNEL); if (!active_list) { status = -ENOMEM; goto out; } count = 0; for (i = 0; i < dod->package.count; i++) { obj = &dod->package.elements[i]; if (obj->type != ACPI_TYPE_INTEGER) { printk(KERN_ERR PREFIX "Invalid _DOD data in element %d\n", i); continue; } active_list[count].value.int_val = obj->integer.value; active_list[count].bind_info = NULL; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, (int)obj->integer.value)); count++; } kfree(video->attached_array); video->attached_array = active_list; video->attached_count = count; out: kfree(buffer.pointer); return status; }

Contributors

PersonTokensPropCommitsCommitProp
Len Brown28086.42%333.33%
Dmitry Torokhov144.32%111.11%
Alex Hung123.70%111.11%
Thomas Renninger103.09%111.11%
Patrick Mochel61.85%222.22%
Linus Torvalds20.62%111.11%
Total324100.00%9100.00%


static int acpi_video_get_next_level(struct acpi_video_device *device, u32 level_current, u32 event) { int min, max, min_above, max_below, i, l, delta = 255; max = max_below = 0; min = min_above = 255; /* Find closest level to level_current */ for (i = 2; i < device->brightness->count; i++) { l = device->brightness->levels[i]; if (abs(l - level_current) < abs(delta)) { delta = l - level_current; if (!delta) break; } } /* Ajust level_current to closest available level */ level_current += delta; for (i = 2; i < device->brightness->count; i++) { l = device->brightness->levels[i]; if (l < min) min = l; if (l > max) max = l; if (l < min_above && l > level_current) min_above = l; if (l > max_below && l < level_current) max_below = l; } switch (event) { case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: return (level_current < max) ? min_above : min; case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: return (level_current < max) ? min_above : max; case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: return (level_current > min) ? max_below : min; case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: return 0; default: return level_current; } }

Contributors

PersonTokensPropCommitsCommitProp
Thomas Tuttle16064.26%125.00%
Alexey Y. Starikovskiy6726.91%125.00%
Len Brown208.03%125.00%
Yakui Zhao20.80%125.00%
Total249100.00%4100.00%


static void acpi_video_switch_brightness(struct work_struct *work) { struct acpi_video_device *device = container_of(to_delayed_work(work), struct acpi_video_device, switch_brightness_work); unsigned long long level_current, level_next; int event = device->switch_brightness_event; int result = -EINVAL; /* no warning message if acpi_backlight=vendor or a quirk is used */ if (!device->backlight) return; if (!device->brightness) goto out; result = acpi_video_device_lcd_get_level_current(device, &level_current, false); if (result) goto out; level_next = acpi_video_get_next_level(device, level_current, event); result = acpi_video_device_lcd_set_level(device, level_next); if (!result) backlight_force_update(device->backlight, BACKLIGHT_UPDATE_HOTKEY); out: if (result) printk(KERN_ERR PREFIX "Failed to switch the brightness\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Len Brown4030.08%18.33%
Rui Zhang3627.07%325.00%
Linus Torvalds2921.80%18.33%
Matthew Garrett1511.28%216.67%
Julia Jomantaite75.26%18.33%
Hans de Goede32.26%18.33%
Matthew Wilcox10.75%18.33%
Aaron Lu10.75%18.33%
Danny Baumann10.75%18.33%
Total133100.00%12100.00%


int acpi_video_get_edid(struct acpi_device *device, int type, int device_id, void **edid) { struct acpi_video_bus *video; struct acpi_video_device *video_device; union acpi_object *buffer = NULL; acpi_status status; int i, length; if (!device || !acpi_driver_data(device)) return -EINVAL; video = acpi_driver_data(device); for (i = 0; i < video->attached_count; i++) { video_device = video->attached_array[i].bind_info; length = 256; if (!video_device) continue; if (!video_device->cap._DDC) continue; if (type) { switch (type) { case ACPI_VIDEO_DISPLAY_CRT: if (!video_device->flags.crt) continue; break; case ACPI_VIDEO_DISPLAY_TV: if (!video_device->flags.tvout) continue; break; case ACPI_VIDEO_DISPLAY_DVI: if (!video_device->flags.dvi) continue; break; case ACPI_VIDEO_DISPLAY_LCD: if (!video_device->flags.lcd) continue; break; } } else if (video_device->device_id != device_id) { continue; } status = acpi_video_device_EDID(video_device, &buffer, length); if (ACPI_FAILURE(status) || !buffer || buffer->type != ACPI_TYPE_BUFFER) { length = 128; status = acpi_video_device_EDID(video_device, &buffer, length); if (ACPI_FAILURE(status) || !buffer || buffer->type != ACPI_TYPE_BUFFER) { continue; } } *edid = buffer->buffer.pointer; return length; } return -ENODEV; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Garrett26796.39%150.00%
Rui Zhang103.61%150.00%
Total277100.00%2100.00%

EXPORT_SYMBOL(acpi_video_get_edid);
static int acpi_video_bus_get_devices(struct acpi_video_bus *video, struct acpi_device *device) { int status = 0; struct acpi_device *dev; /* * There are systems where video module known to work fine regardless * of broken _DOD and ignoring returned value here doesn't cause * any issues later. */ acpi_video_device_enumerate(video); list_for_each_entry(dev, &device->children, node) { status = acpi_video_bus_get_one_device(dev, video); if (status) { dev_err(&dev->dev, "Can't attach device\n"); break; } video->child_count++; } return status; }

Contributors

PersonTokensPropCommitsCommitProp
Len Brown5167.11%114.29%
Aaron Lu1317.11%228.57%
Dmitry Torokhov67.89%114.29%
Igor Murzov33.95%114.29%
Patrick Mochel22.63%114.29%
Lucas De Marchi11.32%114.29%
Total76100.00%7100.00%

/* acpi_video interface */ /* * Win8 requires setting bit2 of _DOS to let firmware know it shouldn't * preform any automatic brightness change on receiving a notification. */
static int acpi_video_bus_start_devices(struct acpi_video_bus *video) { return acpi_video_bus_DOS(video, 0, acpi_osi_is_win8() ? 1 : 0); }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu1453.85%233.33%
Len Brown726.92%116.67%
Karol Kozimor311.54%116.67%
Patrick Mochel13.85%116.67%
Andi Kleen13.85%116.67%
Total26100.00%6100.00%


static int acpi_video_bus_stop_devices(struct acpi_video_bus *video) { return acpi_video_bus_DOS(video, 0, acpi_osi_is_win8() ? 0 : 1); }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu2388.46%375.00%
Andi Kleen311.54%125.00%
Total26100.00%4100.00%


static void acpi_video_bus_notify(struct acpi_device *device, u32 event) { struct acpi_video_bus *video = acpi_driver_data(device); struct input_dev *input; int keycode = 0; if (!video || !video->input) return; input = video->input; switch (event) { case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch, * most likely via hotkey. */ keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video * connector. */ acpi_video_device_enumerate(video); acpi_video_device_rebind(video); keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed. */ keycode = KEY_SWITCHVIDEOMODE; break; case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ keycode = KEY_VIDEO_NEXT; break; case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ keycode = KEY_VIDEO_PREV; break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported event [0x%x]\n", event)); break; } if (acpi_notifier_call_chain(device, event, 0)) /* Something vetoed the keypress. */ keycode = 0; if (keycode && (report_key_events & REPORT_OUTPUT_KEY_EVENTS)) { input_report_key(input, keycode, 1); input_sync(input); input_report_key(input, keycode, 0); input_sync(input); } return; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu6234.07%17.69%
Yu Luming4625.27%215.38%
Len Brown2915.93%215.38%
Rui Zhang2010.99%215.38%
Matthew Garrett84.40%215.38%
Hans de Goede63.30%17.69%
Keith Packard52.75%17.69%
Luca Tettamanti52.75%17.69%
Patrick Mochel10.55%17.69%
Total182100.00%13100.00%


static void brightness_switch_event(struct acpi_video_device *video_device, u32 event) { if (!brightness_switch_enabled) return; video_device->switch_brightness_event = event; schedule_delayed_work(&video_device->switch_brightness_work, HZ / 10); }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds38100.00%1100.00%
Total38100.00%1100.00%


static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data) { struct acpi_video_device *video_device = data; struct acpi_device *device = NULL; struct acpi_video_bus *bus; struct input_dev *input; int keycode = 0; if (!video_device) return; device = video_device->dev; bus = video_device->video; input = bus->input; switch (event) { case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ brightness_switch_event(video_device, event); keycode = KEY_BRIGHTNESS_CYCLE; break; case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ brightness_switch_event(video_device, event); keycode = KEY_BRIGHTNESSUP; break; case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ brightness_switch_event(video_device, event); keycode = KEY_BRIGHTNESSDOWN; break; case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightness */ brightness_switch_event(video_device, event); keycode = KEY_BRIGHTNESS_ZERO; break; case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ brightness_switch_event(video_device, event); keycode = KEY_DISPLAY_OFF; break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported event [0x%x]\n", event)); break; } acpi_notifier_call_chain(device, event, 0); if (keycode && (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS)) { input_report_key(input, keycode, 1); input_sync(input); input_report_key(input, keycode, 0); input_sync(input); } return; }

Contributors

PersonTokensPropCommitsCommitProp
Yu Luming10146.33%111.11%
Len Brown8137.16%111.11%
Rui Zhang94.13%111.11%
Matthew Garrett83.67%111.11%
Patrick Mochel73.21%222.22%
Hans de Goede62.75%111.11%
Linus Torvalds52.29%111.11%
Justin P. Mattock10.46%111.11%
Total218100.00%9100.00%


static int acpi_video_resume(struct notifier_block *nb, unsigned long val, void *ign) { struct acpi_video_bus *video; struct acpi_video_device *video_device; int i; switch (val) { case PM_HIBERNATION_PREPARE: case PM_SUSPEND_PREPARE: case PM_RESTORE_PREPARE: return NOTIFY_DONE; } video = container_of(nb, struct acpi_video_bus, pm_nb); dev_info(&video->device->dev, "Restoring backlight state\n"); for (i = 0; i < video->attached_count; i++) { video_device = video->attached_array[i].bind_info; if (video_device && video_device->brightness) acpi_video_device_lcd_set_level(video_device, video_device->brightness->curr); } return NOTIFY_OK; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Garrett7560.48%133.33%
Rafael J. Wysocki4233.87%133.33%
Hans de Goede75.65%133.33%
Total124100.00%3100.00%


static acpi_status acpi_video_bus_match(acpi_handle handle, u32 level, void *context, void **return_value) { struct acpi_device *device = context; struct acpi_device *sibling; int result; if (handle == device->handle) return AE_CTRL_TERMINATE; result = acpi_bus_get_device(handle, &sibling); if (result) return AE_OK; if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) return AE_ALREADY_EXISTS; return AE_OK; }

Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang83100.00%1100.00%
Total83100.00%1100.00%


static void acpi_video_dev_register_backlight(struct acpi_video_device *device) { struct backlight_properties props; struct pci_dev *pdev; acpi_handle acpi_parent; struct device *parent = NULL; int result; static int count; char *name; result = acpi_video_init_brightness(device); if (result) return; if (disable_backlight_sysfs_if > 0) return; name = kasprintf(GFP_KERNEL, "acpi_video%d", count); if (!name) return; count++; acpi_get_parent(device->dev->handle, &acpi_parent); pdev = acpi_get_pci_dev(acpi_parent); if (pdev) { parent = &pdev->dev; pci_dev_put(pdev); } memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_FIRMWARE; props.max_brightness = device->brightness->count - 3; device->backlight = backlight_device_register(name, parent, device, &acpi_backlight_ops, &props); kfree(name); if (IS_ERR(device->backlight)) { device->backlight = NULL; return; } /* * Save current brightness level in case we have to restore it * before acpi_video_device_lcd_set_level() is called next time. */ device->backlight->props.brightness = acpi_video_get_brightness(device->backlight); device->cooling_dev = thermal_cooling_device_register("LCD", device->dev, &video_cooling_ops); if (IS_ERR(device->cooling_dev)) { /* * Set cooling_dev to NULL so we don't crash trying to free it. * Also, why the hell we are returning early and not attempt to * register video output if cooling device registration failed? * -- dtor */ device->cooling_dev = NULL; return; } dev_info(&device->dev->dev, "registered as cooling_device%d\n", device->cooling_dev->id); result = sysfs_create_link(&device->dev->dev.kobj, &device->cooling_dev->device.kobj, "thermal_cooling"); if (result) printk(KERN_ERR PREFIX "Create sysfs link\n"); result = sysfs_create_link(&device->cooling_dev->device.kobj, &device->dev->dev.kobj, "device"); if (result) printk(KERN_ERR PREFIX "Create sysfs link\n"); }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu31394.85%133.33%
Hans de Goede175.15%266.67%
Total330100.00%3100.00%


static void acpi_video_run_bcl_for_osi(struct acpi_video_bus *video) { struct acpi_video_device *dev; union acpi_object *levels; mutex_lock(&video->device_list_lock); list_for_each_entry(dev, &video->video_device_list, entry) { if (!acpi_video_device_lcd_query_levels(dev->dev->handle, &levels)) kfree(levels); } mutex_unlock(&video->device_list_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu67100.00%2100.00%
Total67100.00%2100.00%


static bool acpi_video_should_register_backlight(struct acpi_video_device *dev) { /* * Do not create backlight device for video output * device that is not in the enumerated list. */ if (!acpi_video_device_in_dod(dev)) { dev_dbg(&dev->dev->dev, "not in _DOD list, ignore\n"); return false; } if (only_lcd) return dev->flags.lcd; return true; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu51100.00%1100.00%
Total51100.00%1100.00%


static int acpi_video_bus_register_backlight(struct acpi_video_bus *video) { struct acpi_video_device *dev; if (video->backlight_registered) return 0; acpi_video_run_bcl_for_osi(video); if (acpi_video_get_backlight_type() != acpi_backlight_video) return 0; mutex_lock(&video->device_list_lock); list_for_each_entry(dev, &video->video_device_list, entry) { if (acpi_video_should_register_backlight(dev)) acpi_video_dev_register_backlight(dev); } mutex_unlock(&video->device_list_lock); video->backlight_registered = true; video->pm_nb.notifier_call = acpi_video_resume; video->pm_nb.priority = 0; return register_pm_notifier(&video->pm_nb); }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu8477.06%350.00%
Hans de Goede2522.94%350.00%
Total109100.00%6100.00%


static void acpi_video_dev_unregister_backlight(struct acpi_video_device *device) { if (device->backlight) { backlight_device_unregister(device->backlight); device->backlight = NULL; } if (device->brightness) { kfree(device->brightness->levels); kfree(device->brightness); device->brightness = NULL; } if (device->cooling_dev) { sysfs_remove_link(&device->dev->dev.kobj, "thermal_cooling"); sysfs_remove_link(&device->cooling_dev->device.kobj, "device"); thermal_cooling_device_unregister(device->cooling_dev); device->cooling_dev = NULL; } }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu111100.00%1100.00%
Total111100.00%1100.00%


static int acpi_video_bus_unregister_backlight(struct acpi_video_bus *video) { struct acpi_video_device *dev; int error; if (!video->backlight_registered) return 0; error = unregister_pm_notifier(&video->pm_nb); mutex_lock(&video->device_list_lock); list_for_each_entry(dev, &video->video_device_list, entry) acpi_video_dev_unregister_backlight(dev); mutex_unlock(&video->device_list_lock); video->backlight_registered = false; return error; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu5774.03%150.00%
Hans de Goede2025.97%150.00%
Total77100.00%2100.00%


static void acpi_video_dev_add_notify_handler(struct acpi_video_device *device) { acpi_status status; struct acpi_device *adev = device->dev; status = acpi_install_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY, acpi_video_device_notify, device); if (ACPI_FAILURE(status)) dev_err(&adev->dev, "Error installing notify handler\n"); else device->flags.notify = 1; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu64100.00%1100.00%
Total64100.00%1100.00%


static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video) { struct input_dev *input; struct acpi_video_device *dev; int error; video->input = input = input_allocate_device(); if (!input) { error = -ENOMEM; goto out; } error = acpi_video_bus_start_devices(video); if (error) goto err_free_input; snprintf(video->phys, sizeof(video->phys), "%s/video/input0", acpi_device_hid(video->device)); input->name = acpi_device_name(video->device); input->phys = video->phys; input->id.bustype = BUS_HOST; input->id.product = 0x06; input->dev.parent = &video->device->dev; input->evbit[0] = BIT(EV_KEY); set_bit(KEY_SWITCHVIDEOMODE, input->keybit); set_bit(KEY_VIDEO_NEXT, input->keybit); set_bit(KEY_VIDEO_PREV, input->keybit); set_bit(KEY_BRIGHTNESS_CYCLE, input->keybit); set_bit(KEY_BRIGHTNESSUP, input->keybit); set_bit(KEY_BRIGHTNESSDOWN, input->keybit); set_bit(KEY_BRIGHTNESS_ZERO, input->keybit); set_bit(KEY_DISPLAY_OFF, input->keybit); error = input_register_device(input); if (error) goto err_stop_dev; mutex_lock(&video->device_list_lock); list_for_each_entry(dev, &video->video_device_list, entry) acpi_video_dev_add_notify_handler(dev); mutex_unlock(&video->device_list_lock); return 0; err_stop_dev: acpi_video_bus_stop_devices(video); err_free_input: input_free_device(input); video->input = NULL; out: return error; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu288100.00%1100.00%
Total288100.00%1100.00%


static void acpi_video_dev_remove_notify_handler(struct acpi_video_device *dev) { if (dev->flags.notify) { acpi_remove_notify_handler(dev->dev->handle, ACPI_DEVICE_NOTIFY, acpi_video_device_notify); dev->flags.notify = 0; } }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu42100.00%1100.00%
Total42100.00%1100.00%


static void acpi_video_bus_remove_notify_handler(struct acpi_video_bus *video) { struct acpi_video_device *dev; mutex_lock(&video->device_list_lock); list_for_each_entry(dev, &video->video_device_list, entry) acpi_video_dev_remove_notify_handler(dev); mutex_unlock(&video->device_list_lock); acpi_video_bus_stop_devices(video); input_unregister_device(video->input); video->input = NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu63100.00%1100.00%
Total63100.00%1100.00%


static int acpi_video_bus_put_devices(struct acpi_video_bus *video) { struct acpi_video_device *dev, *next; mutex_lock(&video->device_list_lock); list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { list_del(&dev->entry); kfree(dev); } mutex_unlock(&video->device_list_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu63100.00%1100.00%
Total63100.00%1100.00%

static int instance;
static int acpi_video_bus_add(struct acpi_device *device) { struct acpi_video_bus *video; int error; acpi_status status; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, device->parent->handle, 1, acpi_video_bus_match, NULL, device, NULL); if (status == AE_ALREADY_EXISTS) { printk(KERN_WARNING FW_BUG "Duplicate ACPI video bus devices for the" " same VGA controller, please try module " "parameter \"video.allow_duplicates=1\"" "if the current driver doesn't work.\n"); if (!allow_duplicates) return -ENODEV; } video = kzalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); if (!video) return -ENOMEM; /* a hack to fix the duplicate name "VID" problem on T61 */ if (!strcmp(device->pnp.bus_id, "VID")) { if (instance) device->pnp.bus_id[3] = '0' + instance; instance++; } /* a hack to fix the duplicate name "VGA" problem on Pa 3553 */ if (!strcmp(device->pnp.bus_id, "VGA")) { if (instance) device->pnp.bus_id[3] = '0' + instance; instance++; } video->device = device; strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); device->driver_data = video; acpi_video_bus_find_cap(video); error = acpi_video_bus_check(video); if (error) goto err_free_video; mutex_init(&video->device_list_lock); INIT_LIST_HEAD(&video->video_device_list); error = acpi_video_bus_get_devices(video, device); if (error) goto err_put_video; printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), video->flags.multihead ? "yes" : "no", video->flags.rom ? "yes" : "no", video->flags.post ? "yes" : "no"); mutex_lock(&video_list_lock); list_add_tail(&video->entry, &video_bus_head); mutex_unlock(&video_list_lock); acpi_video_bus_register_backlight(video); acpi_video_bus_add_notify_handler(video); return 0; err_put_video: acpi_video_bus_put_devices(video); kfree(video->attached_array); err_free_video: kfree(video); device->driver_data = NULL; return error; }

Contributors

PersonTokensPropCommitsCommitProp
Aaron Lu9628.15%16.67%
Rui Zhang8926.10%213.33%
Yu Luming5315.54%16.67%
Len Brown4112.02%16.67%
Dmitry Torokhov4011.73%213.33%
Igor Murzov61.76%213.33%
Patrick Mochel41.17%16.67%
Rafael J. Wysocki41.17%16.67%
Dan Carpenter41.17%16.67%
Pavel Machek20.59%16.67%
Yakui Zhao10.29%16.67%
Burman Yan10.29%16.67%
Total341100.00%15100.00%


static int acpi_video_bus_remove(struct acpi_device *device) { struct acpi_video_bus *video = NULL; if (!device || !acpi_driver_data(device)) return -EINVAL; video = acpi_driver_data(device); acpi_video_bus_remove_notify_handler(video); acpi_video_bus_unregister_backlight(video); acpi_video_bus_put_devices(video); mutex_lock(&video_list_lock); list_del(&video->entry); mutex_unlock(&video_list_lock); kfree(video->attached_array); kfree(video); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Len Brown6066.67%120.00%
Aaron Lu1718.89%120.00%
Yu Luming55.56%120.00%
Rafael J. Wysocki44.44%120.00%
Patrick Mochel44.44%120.00%
Total90100.00%5100.00%


static int __init is_i740(struct pci_dev *dev) { if (dev->device == 0x00D1) return 1; if (dev->device == 0x7000) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Alan Cox37100.00%1100.00%
Total37100.00%1100.00%


static int __init intel_opregion_present(void) { int opregion = 0; struct pci_dev *dev = NULL; u32 address; for_each_pci_dev(dev) { if ((dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) continue; if (dev->vendor != PCI_VENDOR_ID_INTEL) continue; /* We don't want to poke around undefined i740 registers */ if (is_i740(dev)) continue; pci_read_config_dword(dev, 0xfc, &address); if (!address) continue; opregion = 1; } return opregion; }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Garrett6375.00%125.00%
Alan Cox1821.43%250.00%
Len Brown33.57%125.00%
Total84100.00%4100.00%


int acpi_video_register(void) { int ret = 0; mutex_lock(&register_count_mutex); if (register_count) { /* * if the function of acpi_video_register is already called, * don't register the acpi_vide_bus again and return no error. */ goto leave; } dmi_check_system(video_dmi_table); ret = acpi_bus_register_driver(&acpi_video_bus); if (ret) goto leave; /* * When the acpi_video_bus is loaded successfully, increase * the counter reference. */ register_count = 1; leave: mutex_unlock(&register_count_mutex); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Hans de Goede3250.00%333.33%
Len Brown1625.00%111.11%
Yakui Zhao710.94%111.11%
Chris Wilson34.69%111.11%
Rafael J. Wysocki34.69%111.11%
Patrick Mochel23.12%111.11%
Matthew Garrett11.56%111.11%
Total64100.00%9100.00%

EXPORT_SYMBOL(acpi_video_register);
void acpi_video_unregister(void) { mutex_lock(&register_count_mutex); if (register_count) { acpi_bus_unregister_driver(&acpi_video_bus); register_count = 0; } mutex_unlock(&register_count_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Yakui Zhao1851.43%133.33%
Hans de Goede1748.57%266.67%
Total35100.00%3100.00%

EXPORT_SYMBOL(acpi_video_unregister);
void acpi_video_unregister_backlight(void) { struct acpi_video_bus *video; mutex_lock(&register_count_mutex); if (register_count) { mutex_lock(&video_list_lock); list_for_each_entry(video, &video_bus_head, entry) acpi_video_bus_unregister_backlight(video); mutex_unlock(&video_list_lock); } mutex_unlock(&register_count_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
Hans de Goede55100.00%3100.00%
Total55100.00%3100.00%


bool acpi_video_handles_brightness_key_presses(void) { bool have_video_busses; mutex_lock(&video_list_lock); have_video_busses = !list_empty(&video_bus_head); mutex_unlock(&video_list_lock); return have_video_busses && (report_key_events & REPORT_BRIGHTNESS_KEY_EVENTS); }

Contributors

PersonTokensPropCommitsCommitProp
Hans de Goede40100.00%2100.00%
Total40100.00%2100.00%

EXPORT_SYMBOL(acpi_video_handles_brightness_key_presses); /* * This is kind of nasty. Hardware using Intel chipsets may require * the video opregion code to be run first in order to initialise * state before any ACPI video calls are made. To handle this we defer * registration of the video class until the opregion code has run. */
static int __init acpi_video_init(void) { /* * Let the module load even if ACPI is disabled (e.g. due to * a broken BIOS) so that i915.ko can still be loaded on such * old systems without an AcpiOpRegion. * * acpi_video_register() will report -ENODEV later as well due * to acpi_disabled when i915.ko tries to register itself afterwards. */ if (acpi_disabled) return 0; if (intel_opregion_present()) return 0; return acpi_video_register(); }

Contributors

PersonTokensPropCommitsCommitProp
Matthew Garrett2172.41%150.00%
Chris Wilson827.59%150.00%
Total29100.00%2100.00%


static void __exit acpi_video_exit(void) { acpi_video_detect_exit(); acpi_video_unregister(); return; }

Contributors

PersonTokensPropCommitsCommitProp
Len Brown850.00%125.00%
Yakui Zhao425.00%125.00%
Hans de Goede318.75%125.00%
Patrick Mochel16.25%125.00%
Total16100.00%4100.00%

module_init(acpi_video_init); module_exit(acpi_video_exit);

Overall Contributors

PersonTokensPropCommitsCommitProp
Rui Zhang226126.25%2215.49%
Aaron Lu218925.41%1510.56%
Len Brown156418.16%107.04%
Matthew Garrett6707.78%117.75%
Hans de Goede5806.73%1913.38%
Yu Luming2773.22%21.41%
Thomas Tuttle1601.86%10.70%
Dmitry Torokhov1401.63%74.93%
Danny Baumann1201.39%32.11%
Linus Torvalds1031.20%32.11%
Alexey Y. Starikovskiy730.85%21.41%
Rafael J. Wysocki680.79%42.82%
Alan Cox550.64%21.41%
Patrick Mochel470.55%32.11%
Yakui Zhao390.45%32.11%
Thomas Renninger390.45%21.41%
Felipe Contreras300.35%21.41%
Vladimir Serbinenko270.31%10.70%
Igor Murzov270.31%32.11%
Jan Engelhardt220.26%10.70%
Jiang Liu210.24%21.41%
Björn Helgaas150.17%10.70%
Alex Hung120.14%10.70%
Chris Wilson110.13%21.41%
Richard Purdie110.13%10.70%
Julia Jomantaite70.08%10.70%
Luca Tettamanti50.06%10.70%
Keith Packard50.06%10.70%
Dan Carpenter40.05%10.70%
Pavel Machek40.05%10.70%
Andi Kleen40.05%10.70%
Lv Zheng30.03%10.70%
Karol Kozimor30.03%10.70%
Tejun Heo30.03%10.70%
Matthew Wilcox30.03%10.70%
Colin Ian King20.02%10.70%
Burman Yan20.02%10.70%
Rusty Russell20.02%10.70%
Arjan van de Ven10.01%10.70%
Justin P. Mattock10.01%10.70%
Lucas De Marchi10.01%10.70%
Vasiliy Kulikov10.01%10.70%
Jarkko Nikula10.01%10.70%
Lionel Debroux10.01%10.70%
Total8614100.00%142100.00%
Directory: drivers/acpi
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.