diff options
Diffstat (limited to 'drivers/acpi/video.c')
| -rw-r--r-- | drivers/acpi/video.c | 1420 | 
1 files changed, 880 insertions, 540 deletions
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 5cd0228d2da..350d52a8f78 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -1,5 +1,5 @@  /* - *  video.c - ACPI Video Driver ($Revision:$) + *  video.c - ACPI Video Driver   *   *  Copyright (C) 2004 Luming Yu <luming.yu@intel.com>   *  Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org> @@ -33,21 +33,18 @@  #include <linux/input.h>  #include <linux/backlight.h>  #include <linux/thermal.h> -#include <linux/video_output.h>  #include <linux/sort.h>  #include <linux/pci.h>  #include <linux/pci_ids.h>  #include <linux/slab.h> -#include <asm/uaccess.h>  #include <linux/dmi.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h>  #include <linux/suspend.h> +#include <linux/acpi.h>  #include <acpi/video.h> +#include <asm/uaccess.h> -#define PREFIX "ACPI: " +#include "internal.h" -#define ACPI_VIDEO_CLASS		"video"  #define ACPI_VIDEO_BUS_NAME		"Video Bus"  #define ACPI_VIDEO_DEVICE_NAME		"Video Device"  #define ACPI_VIDEO_NOTIFY_SWITCH	0x80 @@ -71,19 +68,29 @@ MODULE_AUTHOR("Bruno Ducrot");  MODULE_DESCRIPTION("ACPI Video Driver");  MODULE_LICENSE("GPL"); -static int brightness_switch_enabled = 1; +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 int allow_duplicates; +static bool allow_duplicates;  module_param(allow_duplicates, bool, 0644); -static int register_count = 0; +/* + * For Windows 8 systems: used to decide if video module + * should skip registering backlight interface of its own. + */ +static int use_native_backlight_param = 1; +module_param_named(use_native_backlight, use_native_backlight_param, int, 0444); +static bool use_native_backlight_dmi = false; + +static int register_count; +static struct mutex video_list_lock; +static struct 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, int type); +static int acpi_video_bus_remove(struct acpi_device *device);  static void acpi_video_bus_notify(struct acpi_device *device, u32 event);  static const struct acpi_device_id video_device_ids[] = { @@ -111,26 +118,26 @@ struct acpi_video_bus_flags {  };  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 _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  +	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 */ +	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 { @@ -143,6 +150,8 @@ struct acpi_video_enumerated_device {  struct acpi_video_bus {  	struct acpi_device *device; +	bool backlight_registered; +	bool backlight_notifier_registered;  	u8 dos_setting;  	struct acpi_video_enumerated_device *attached_array;  	u8 attached_count; @@ -150,9 +159,11 @@ struct acpi_video_bus {  	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 notifier_block backlight_nb;  };  struct acpi_video_device_flags { @@ -162,26 +173,22 @@ struct acpi_video_device_flags {  	u8 dvi:1;  	u8 bios:1;  	u8 unknown:1; -	u8 reserved:2; +	u8 notify:1; +	u8 reserved:1;  };  struct acpi_video_device_cap { -	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 _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 */ -	u8 _DCS:1;		/*Return status of output device */ -	u8 _DGS:1;		/*Query graphics state */ -	u8 _DSS:1;		/*Device state set */ +	u8 _DDC:1;		/* Return the EDID for this device */  };  struct acpi_video_brightness_flags {  	u8 _BCL_no_ac_battery_levels:1;	/* no AC/Battery levels in _BCL */ -	u8 _BCL_reversed:1;		/* _BCL package is in a reversed order*/ -	u8 _BCL_use_index:1;		/* levels in _BCL are index values */ -	u8 _BCM_use_index:1;		/* input of _BCM is an index value */ +	u8 _BCL_reversed:1;		/* _BCL package is in a reversed order */  	u8 _BQC_use_index:1;		/* _BQC returns an index value */  }; @@ -202,7 +209,6 @@ struct acpi_video_device {  	struct acpi_video_device_brightness *brightness;  	struct backlight_device *backlight;  	struct thermal_cooling_device *cooling_dev; -	struct output_device *output_dev;  };  static const char device_decode[][30] = { @@ -221,31 +227,45 @@ static int acpi_video_device_lcd_set_level(struct acpi_video_device *device,  			int level);  static int acpi_video_device_lcd_get_level_current(  			struct acpi_video_device *device, -			unsigned long long *level, int init); +			unsigned long long *level, bool raw);  static int acpi_video_get_next_level(struct acpi_video_device *device,  				     u32 level_current, u32 event);  static int acpi_video_switch_brightness(struct acpi_video_device *device,  					 int event); -static int acpi_video_device_get_state(struct acpi_video_device *device, -			    unsigned long long *state); -static int acpi_video_output_get(struct output_device *od); -static int acpi_video_device_set_state(struct acpi_video_device *device, int state); -/*backlight device sysfs support*/ +static bool acpi_video_use_native_backlight(void) +{ +	if (use_native_backlight_param != -1) +		return use_native_backlight_param; +	else +		return use_native_backlight_dmi; +} + +bool acpi_video_verify_backlight_support(void) +{ +	if (acpi_osi_is_win8() && acpi_video_use_native_backlight() && +	    backlight_device_registered(BACKLIGHT_RAW)) +		return false; +	return acpi_video_backlight_support(); +} +EXPORT_SYMBOL_GPL(acpi_video_verify_backlight_support); + +/* 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 = -		(struct acpi_video_device *)bl_get_data(bd); +	struct acpi_video_device *vd = bl_get_data(bd); -	if (acpi_video_device_lcd_get_level_current(vd, &cur_level, 0)) +	if (acpi_video_device_lcd_get_level_current(vd, &cur_level, false))  		return -EINVAL;  	for (i = 2; i < vd->brightness->count; i++) {  		if (vd->brightness->levels[i] == cur_level) -			/* The first two entries are special - see page 575 -			   of the ACPI spec 3.0 */ -			return i-2; +			/* +			 * The first two entries are special - see page 575 +			 * of the ACPI spec 3.0 +			 */ +			return i - 2;  	}  	return 0;  } @@ -253,42 +273,17 @@ static int acpi_video_get_brightness(struct backlight_device *bd)  static int acpi_video_set_brightness(struct backlight_device *bd)  {  	int request_level = bd->props.brightness + 2; -	struct acpi_video_device *vd = -		(struct acpi_video_device *)bl_get_data(bd); +	struct acpi_video_device *vd = bl_get_data(bd);  	return acpi_video_device_lcd_set_level(vd,  				vd->brightness->levels[request_level]);  } -static struct backlight_ops acpi_backlight_ops = { +static const struct backlight_ops acpi_backlight_ops = {  	.get_brightness = acpi_video_get_brightness,  	.update_status  = acpi_video_set_brightness,  }; -/*video output device sysfs support*/ -static int acpi_video_output_get(struct output_device *od) -{ -	unsigned long long state; -	struct acpi_video_device *vd = -		(struct acpi_video_device *)dev_get_drvdata(&od->dev); -	acpi_video_device_get_state(vd, &state); -	return (int)state; -} - -static int acpi_video_output_set(struct output_device *od) -{ -	unsigned long state = od->request_state; -	struct acpi_video_device *vd= -		(struct acpi_video_device *)dev_get_drvdata(&od->dev); -	return acpi_video_device_set_state(vd, state); -} - -static struct output_properties acpi_output_properties = { -	.set_state = acpi_video_output_set, -	.get_status = acpi_video_output_get, -}; - -  /* thermal cooling device callbacks */  static int video_get_max_state(struct thermal_cooling_device *cooling_dev, unsigned  			       long *state) @@ -308,7 +303,7 @@ static int video_get_cur_state(struct thermal_cooling_device *cooling_dev, unsig  	unsigned long long level;  	int offset; -	if (acpi_video_device_lcd_get_level_current(video, &level, 0)) +	if (acpi_video_device_lcd_get_level_current(video, &level, false))  		return -EINVAL;  	for (offset = 2; offset < video->brightness->count; offset++)  		if (level == video->brightness->levels[offset]) { @@ -326,51 +321,25 @@ video_set_cur_state(struct thermal_cooling_device *cooling_dev, unsigned long st  	struct acpi_video_device *video = acpi_driver_data(device);  	int level; -	if ( state >= video->brightness->count - 2) +	if (state >= video->brightness->count - 2)  		return -EINVAL;  	state = video->brightness->count - state; -	level = video->brightness->levels[state -1]; +	level = video->brightness->levels[state - 1];  	return acpi_video_device_lcd_set_level(video, level);  } -static struct thermal_cooling_device_ops video_cooling_ops = { +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 -   -------------------------------------------------------------------------- */ - -/* device */ - -static int -acpi_video_device_get_state(struct acpi_video_device *device, -			    unsigned long long *state) -{ -	int status; - -	status = acpi_evaluate_integer(device->dev->handle, "_DCS", NULL, state); - -	return status; -} - -static int -acpi_video_device_set_state(struct acpi_video_device *device, int state) -{ -	int status; -	union acpi_object arg0 = { ACPI_TYPE_INTEGER }; -	struct acpi_object_list args = { 1, &arg0 }; -	unsigned long long ret; - - -	arg0.integer.value = state; -	status = acpi_evaluate_integer(device->dev->handle, "_DSS", &args, &ret); - -	return status; -} +/* + * -------------------------------------------------------------------------- + *                             Video Management + * -------------------------------------------------------------------------- + */  static int  acpi_video_device_lcd_query_levels(struct acpi_video_device *device, @@ -397,7 +366,7 @@ acpi_video_device_lcd_query_levels(struct acpi_video_device *device,  	return 0; -      err: +err:  	kfree(buffer.pointer);  	return status; @@ -407,14 +376,10 @@ static int  acpi_video_device_lcd_set_level(struct acpi_video_device *device, int level)  {  	int status; -	union acpi_object arg0 = { ACPI_TYPE_INTEGER }; -	struct acpi_object_list args = { 1, &arg0 };  	int state; -	arg0.integer.value = level; - -	status = acpi_evaluate_object(device->dev->handle, "_BCM", -				      &args, NULL); +	status = acpi_execute_simple_method(device->dev->handle, +					    "_BCM", level);  	if (ACPI_FAILURE(status)) {  		ACPI_ERROR((AE_INFO, "Evaluating _BCM failed"));  		return -EIO; @@ -444,6 +409,12 @@ static int __init video_set_bqc_offset(const struct dmi_system_id *d)  	return 0;  } +static int __init video_set_use_native_backlight(const struct dmi_system_id *d) +{ +	use_native_backlight_dmi = true; +	return 0; +} +  static struct dmi_system_id video_dmi_table[] __initdata = {  	/*  	 * Broken _BQC workaround http://bugzilla.kernel.org/show_bug.cgi?id=13121 @@ -488,12 +459,223 @@ static struct dmi_system_id video_dmi_table[] __initdata = {  		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"),  		},  	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "ThinkPad T430 and T430s", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +		DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad T430"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "ThinkPad X230", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +		DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X230"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "ThinkPad W530", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +		DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad W530"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	.ident = "ThinkPad X1 Carbon", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +		DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad X1 Carbon"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "Lenovo Yoga 13", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +		DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo IdeaPad Yoga 13"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "Lenovo Yoga 2 11", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +		DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Yoga 2 11"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "Thinkpad Helix", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +		DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "Dell Inspiron 7520", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), +		DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "Acer Aspire 5733Z", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Acer"), +		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5733Z"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "Acer Aspire 5742G", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Acer"), +		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5742G"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "Acer Aspire V5-171", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Acer"), +		DMI_MATCH(DMI_PRODUCT_NAME, "V5-171"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "Acer Aspire V5-431", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Acer"), +		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-431"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "Acer Aspire V5-471G", +	 .matches = { +		DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), +		DMI_MATCH(DMI_PRODUCT_NAME, "Aspire V5-471G"), +		}, +	}, +	{ +	 .callback = video_set_use_native_backlight, +	 .ident = "Acer TravelMate B113", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Acer"), +		DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate B113"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "HP ProBook 4340s", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +		DMI_MATCH(DMI_PRODUCT_VERSION, "HP ProBook 4340s"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "HP ProBook 4540s", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +		DMI_MATCH(DMI_PRODUCT_VERSION, "HP ProBook 4540s"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "HP ProBook 2013 models", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +		DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook "), +		DMI_MATCH(DMI_PRODUCT_NAME, " G1"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "HP EliteBook 2013 models", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +		DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook "), +		DMI_MATCH(DMI_PRODUCT_NAME, " G1"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "HP ZBook 14", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +		DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 14"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "HP ZBook 15", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +		DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 15"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "HP ZBook 17", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +		DMI_MATCH(DMI_PRODUCT_NAME, "HP ZBook 17"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "HP EliteBook 8470p", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +		DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8470p"), +		}, +	}, +	{ +	.callback = video_set_use_native_backlight, +	.ident = "HP EliteBook 8780w", +	.matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), +		DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook 8780w"), +		}, +	},  	{}  }; +static unsigned long long +acpi_video_bqc_value_to_level(struct acpi_video_device *device, +			      unsigned long long bqc_value) +{ +	unsigned long long level; + +	if (device->brightness->flags._BQC_use_index) { +		/* +		 * _BQC returns an index that doesn't account for +		 * the first 2 items with special meaning, so we need +		 * to compensate for that by offsetting ourselves +		 */ +		if (device->brightness->flags._BCL_reversed) +			bqc_value = device->brightness->count - 3 - bqc_value; + +		level = device->brightness->levels[bqc_value + 2]; +	} else { +		level = bqc_value; +	} + +	level += bqc_offset_aml_bug_workaround; + +	return level; +} +  static int  acpi_video_device_lcd_get_level_current(struct acpi_video_device *device, -					unsigned long long *level, int init) +					unsigned long long *level, bool raw)  {  	acpi_status status = AE_OK;  	int i; @@ -504,31 +686,33 @@ acpi_video_device_lcd_get_level_current(struct acpi_video_device *device,  		status = acpi_evaluate_integer(device->dev->handle, buf,  						NULL, level);  		if (ACPI_SUCCESS(status)) { -			if (device->brightness->flags._BQC_use_index) { -				if (device->brightness->flags._BCL_reversed) -					*level = device->brightness->count -								 - 3 - (*level); -				*level = device->brightness->levels[*level + 2]; - +			if (raw) { +				/* +				 * Caller has indicated he wants the raw +				 * value returned by _BQC, so don't furtherly +				 * mess with the value. +				 */ +				return 0;  			} -			*level += bqc_offset_aml_bug_workaround; + +			*level = acpi_video_bqc_value_to_level(device, *level); +  			for (i = 2; i < device->brightness->count; i++)  				if (device->brightness->levels[i] == *level) {  					device->brightness->curr = *level;  					return 0; -			} -			if (!init) { -				/* -				 * BQC returned an invalid level. -				 * Stop using it. -				 */ -				ACPI_WARNING((AE_INFO, -					      "%s returned an invalid level", -					      buf)); -				device->cap._BQC = device->cap._BCQ = 0; -			} +				} +			/* +			 * BQC returned an invalid level. +			 * Stop using it. +			 */ +			ACPI_WARNING((AE_INFO, +				      "%s returned an invalid level", +				      buf)); +			device->cap._BQC = device->cap._BCQ = 0;  		} else { -			/* Fixme: +			/* +			 * 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. @@ -587,8 +771,8 @@ acpi_video_device_EDID(struct acpi_video_device *device,  /*   *  Arg: - *  	video		: video bus device pointer - *	bios_flag	:  + *	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 @@ -600,30 +784,29 @@ acpi_video_device_EDID(struct acpi_video_device *device,   *	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  + *		1.	The system BIOS should NOT automatically control the brightness   *			level of the LCD when the power changes from AC to DC. - * Return Value: - * 		-1	wrong arg. + *  Return Value: + *		-EINVAL	wrong arg.   */  static int  acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag)  { -	u64 status = 0; -	union acpi_object arg0 = { ACPI_TYPE_INTEGER }; -	struct acpi_object_list args = { 1, &arg0 }; +	acpi_status status; +	if (!video->cap._DOS) +		return 0; -	if (bios_flag < 0 || bios_flag > 3 || lcd_flag < 0 || lcd_flag > 1) { -		status = -1; -		goto Failed; -	} -	arg0.integer.value = (lcd_flag << 2) | bios_flag; -	video->dos_setting = arg0.integer.value; -	acpi_evaluate_object(video->device->handle, "_DOS", &args, NULL); +	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; -      Failed: -	return status; +	return 0;  }  /* @@ -637,8 +820,58 @@ acpi_video_cmp_level(const void *a, const void *b)  }  /* - *  Arg:	 - *  	device	: video output device (LCD, CRT, ..) + * 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; +} + + +/* + *  Arg: + *	device	: video output device (LCD, CRT, ..)   *   *  Return Value:   *	Maximum brightness level @@ -655,6 +888,7 @@ acpi_video_init_brightness(struct acpi_video_device *device)  	union acpi_object *o;  	struct acpi_video_device_brightness *br = NULL;  	int result = -EINVAL; +	u32 value;  	if (!ACPI_SUCCESS(acpi_video_device_lcd_query_levels(device, &obj))) {  		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Could not query available " @@ -685,7 +919,12 @@ acpi_video_init_brightness(struct acpi_video_device *device)  			printk(KERN_ERR PREFIX "Invalid data\n");  			continue;  		} -		br->levels[count] = (u32) o->integer.value; +		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]; @@ -712,7 +951,7 @@ acpi_video_init_brightness(struct acpi_video_device *device)  			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\n")); +		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]) { @@ -721,60 +960,44 @@ acpi_video_init_brightness(struct acpi_video_device *device)  			acpi_video_cmp_level, NULL);  	} else if (max_level != br->levels[count - 1])  		ACPI_ERROR((AE_INFO, -			    "Found unordered _BCL package\n")); +			    "Found unordered _BCL package"));  	br->count = count;  	device->brightness = br; -	/* Check the input/output of _BQC/_BCL/_BCM */ -	if ((max_level < 100) && (max_level <= (count - 2))) -		br->flags._BCL_use_index = 1; - -	/* -	 * _BCM is always consistent with _BCL, -	 * at least for all the laptops we have ever seen. -	 */ -	br->flags._BCM_use_index = br->flags._BCL_use_index; -  	/* _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, 1); -	if (result) -		goto out_free_levels; - -	/* -	 * Set the level to maximum and check if _BQC uses indexed value -	 */ -	result = acpi_video_device_lcd_set_level(device, max_level); +	result = acpi_video_device_lcd_get_level_current(device, +							 &level_old, true);  	if (result)  		goto out_free_levels; -	result = acpi_video_device_lcd_get_level_current(device, &level, 0); +	result = acpi_video_bqc_quirk(device, max_level, level_old);  	if (result)  		goto out_free_levels; - -	br->flags._BQC_use_index = (level == max_level ? 0 : 1); - -	if (!br->flags._BQC_use_index) { -		/* -		 * Set the backlight to the initial state. -		 * On some buggy laptops, _BQC returns an uninitialized value -		 * when invoked for the first time, i.e. level_old is invalid. -		 * set the backlight to max_level in this case -		 */ -		for (i = 2; i < br->count; i++) -			if (level_old == br->levels[i]) -				level = level_old; +	/* +	 * 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; -	} -	if (br->flags._BCL_reversed) -		level_old = (br->count - 1) - level_old; -	level = br->levels[level_old]; +	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); @@ -801,7 +1024,7 @@ out:   *	device	: video output device (LCD, CRT, ..)   *   *  Return Value: - *  	None + *	None   *   *  Find out all required AML methods defined under the output   *  device. @@ -809,150 +1032,47 @@ out:  static void acpi_video_device_find_cap(struct acpi_video_device *device)  { -	acpi_handle h_dummy1; - -	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_ADR", &h_dummy1))) { +	if (acpi_has_method(device->dev->handle, "_ADR"))  		device->cap._ADR = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCL", &h_dummy1))) { +	if (acpi_has_method(device->dev->handle, "_BCL"))  		device->cap._BCL = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) { +	if (acpi_has_method(device->dev->handle, "_BCM"))  		device->cap._BCM = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1))) +	if (acpi_has_method(device->dev->handle, "_BQC")) {  		device->cap._BQC = 1; -	else if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCQ", -				&h_dummy1))) { +	} 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_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) { +	if (acpi_has_method(device->dev->handle, "_DDC"))  		device->cap._DDC = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DCS", &h_dummy1))) { -		device->cap._DCS = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DGS", &h_dummy1))) { -		device->cap._DGS = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DSS", &h_dummy1))) { -		device->cap._DSS = 1; -	} - -	if (acpi_video_backlight_support()) { -		struct backlight_properties props; -		int result; -		static int count = 0; -		char *name; - -		result = acpi_video_init_brightness(device); -		if (result) -			return; -		name = kasprintf(GFP_KERNEL, "acpi_video%d", count); -		if (!name) -			return; -		count++; - -		memset(&props, 0, sizeof(struct backlight_properties)); -		props.max_brightness = device->brightness->count - 3; -		device->backlight = backlight_device_register(name, NULL, device, -							      &acpi_backlight_ops, -							      &props); -		kfree(name); -		if (IS_ERR(device->backlight)) -			return; - -		/* -		 * 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); - -		result = sysfs_create_link(&device->backlight->dev.kobj, -					   &device->dev->dev.kobj, "device"); -		if (result) -			printk(KERN_ERR PREFIX "Create sysfs link\n"); - -		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"); - -	} - -	if (acpi_video_display_switch_support()) { - -		if (device->cap._DCS && device->cap._DSS) { -			static int count; -			char *name; -			name = kasprintf(GFP_KERNEL, "acpi_video%d", count); -			if (!name) -				return; -			count++; -			device->output_dev = video_output_register(name, -					NULL, device, &acpi_output_properties); -			kfree(name); -		} -	}  }  /* - *  Arg:	 - *  	device	: video output device (VGA) + *  Arg: + *	device	: video output device (VGA)   *   *  Return Value: - *  	None + *	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)  { -	acpi_handle h_dummy1; - -	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOS", &h_dummy1))) { +	if (acpi_has_method(video->device->handle, "_DOS"))  		video->cap._DOS = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_DOD", &h_dummy1))) { +	if (acpi_has_method(video->device->handle, "_DOD"))  		video->cap._DOD = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_ROM", &h_dummy1))) { +	if (acpi_has_method(video->device->handle, "_ROM"))  		video->cap._ROM = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_GPD", &h_dummy1))) { +	if (acpi_has_method(video->device->handle, "_GPD"))  		video->cap._GPD = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_SPD", &h_dummy1))) { +	if (acpi_has_method(video->device->handle, "_SPD"))  		video->cap._SPD = 1; -	} -	if (ACPI_SUCCESS(acpi_get_handle(video->device->handle, "_VPO", &h_dummy1))) { +	if (acpi_has_method(video->device->handle, "_VPO"))  		video->cap._VPO = 1; -	}  }  /* @@ -973,7 +1093,8 @@ static int acpi_video_bus_check(struct acpi_video_bus *video)  		return -ENODEV;  	pci_dev_put(dev); -	/* Since there is no HID, CID and so on for VGA driver, we have +	/* +	 * Since there is no HID, CID and so on for VGA driver, we have  	 * to check well known required nodes.  	 */ @@ -1003,12 +1124,14 @@ static int acpi_video_bus_check(struct acpi_video_bus *video)  	return status;  } -/* -------------------------------------------------------------------------- -                                 Driver Interface -   -------------------------------------------------------------------------- */ +/* + * -------------------------------------------------------------------------- + *                               Driver Interface + * -------------------------------------------------------------------------- + */  /* device interface */ -static struct acpi_video_device_attrib* +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; @@ -1046,104 +1169,85 @@ acpi_video_bus_get_one_device(struct acpi_device *device,  	unsigned long long device_id;  	int status, device_type;  	struct acpi_video_device *data; -	struct acpi_video_device_attrib* attribute; - -	if (!device || !video) -		return -EINVAL; +	struct acpi_video_device_attrib *attribute;  	status =  	    acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); -	if (ACPI_SUCCESS(status)) { - -		data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); -		if (!data) -			return -ENOMEM; - -		strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); -		strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); -		device->driver_data = data; - -		data->device_id = device_id; -		data->video = video; -		data->dev = device; +	/* Some device omits _ADR, we skip them instead of fail */ +	if (ACPI_FAILURE(status)) +		return 0; -		attribute = acpi_video_get_device_attr(video, device_id); +	data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL); +	if (!data) +		return -ENOMEM; -		if((attribute != NULL) && attribute->device_id_scheme) { -			switch (attribute->display_type) { -			case ACPI_VIDEO_DISPLAY_CRT: -				data->flags.crt = 1; -				break; -			case ACPI_VIDEO_DISPLAY_TV: -				data->flags.tvout = 1; -				break; -			case ACPI_VIDEO_DISPLAY_DVI: -				data->flags.dvi = 1; -				break; -			case ACPI_VIDEO_DISPLAY_LCD: -				data->flags.lcd = 1; -				break; -			default: -				data->flags.unknown = 1; -				break; -			} -			if(attribute->bios_can_detect) -				data->flags.bios = 1; -		} else { -			/* Check for legacy IDs */ -			device_type = acpi_video_get_device_type(video, -								 device_id); -			/* Ignore bits 16 and 18-20 */ -			switch (device_type & 0xffe2ffff) { -			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; -			} +	strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); +	strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); +	device->driver_data = data; + +	data->device_id = device_id; +	data->video = video; +	data->dev = device; + +	attribute = acpi_video_get_device_attr(video, device_id); + +	if (attribute && attribute->device_id_scheme) { +		switch (attribute->display_type) { +		case ACPI_VIDEO_DISPLAY_CRT: +			data->flags.crt = 1; +			break; +		case ACPI_VIDEO_DISPLAY_TV: +			data->flags.tvout = 1; +			break; +		case ACPI_VIDEO_DISPLAY_DVI: +			data->flags.dvi = 1; +			break; +		case ACPI_VIDEO_DISPLAY_LCD: +			data->flags.lcd = 1; +			break; +		default: +			data->flags.unknown = 1; +			break;  		} - -		acpi_video_device_bind(video, data); -		acpi_video_device_find_cap(data); - -		status = acpi_install_notify_handler(device->handle, -						     ACPI_DEVICE_NOTIFY, -						     acpi_video_device_notify, -						     data); -		if (ACPI_FAILURE(status)) { -			printk(KERN_ERR PREFIX -					  "Error installing notify handler\n"); -			if(data->brightness) -				kfree(data->brightness->levels); -			kfree(data->brightness); -			kfree(data); -			return -ENODEV; +		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;  		} +	} -		mutex_lock(&video->device_list_lock); -		list_add_tail(&data->entry, &video->video_device_list); -		mutex_unlock(&video->device_list_lock); +	acpi_video_device_bind(video, data); +	acpi_video_device_find_cap(data); -		return 0; -	} +	mutex_lock(&video->device_list_lock); +	list_add_tail(&data->entry, &video->video_device_list); +	mutex_unlock(&video->device_list_lock); -	return -ENOENT; +	return status;  }  /*   *  Arg: - *  	video	: video bus device  + *	video	: video bus device   *   *  Return: - *  	none - *   - *  Enumerate the video device list of the video bus,  + *	none + * + *  Enumerate the video device list of the video bus,   *  bind the ids with the corresponding video devices   *  under the video bus.   */ @@ -1162,13 +1266,13 @@ static void acpi_video_device_rebind(struct acpi_video_bus *video)  /*   *  Arg: - *  	video	: video bus device  - *  	device	: video output device under the video  - *  		bus + *	video	: video bus device + *	device	: video output device under the video + *		bus   *   *  Return: - *  	none - *   + *	none + *   *  Bind the ids with the corresponding video devices   *  under the video bus.   */ @@ -1191,11 +1295,11 @@ acpi_video_device_bind(struct acpi_video_bus *video,  /*   *  Arg: - *  	video	: video bus device  + *	video	: video bus device   *   *  Return: - *  	< 0	: error - *   + *	< 0	: error + *   *  Call _DOD to enumerate all devices attached to display adapter   *   */ @@ -1256,7 +1360,7 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video)  	video->attached_array = active_list;  	video->attached_count = count; - out: +out:  	kfree(buffer.pointer);  	return status;  } @@ -1312,15 +1416,16 @@ acpi_video_switch_brightness(struct acpi_video_device *device, int event)  	unsigned long long level_current, level_next;  	int result = -EINVAL; -	/* no warning message if acpi_backlight=vendor is used */ -	if (!acpi_video_backlight_support()) +	/* no warning message if acpi_backlight=vendor or a quirk is used */ +	if (!acpi_video_verify_backlight_support())  		return 0;  	if (!device->brightness)  		goto out;  	result = acpi_video_device_lcd_get_level_current(device, -							 &level_current, 0); +							 &level_current, +							 false);  	if (result)  		goto out; @@ -1360,6 +1465,9 @@ int acpi_video_get_edid(struct acpi_device *device, int type, int device_id,  		if (!video_device)  			continue; +		if (!video_device->cap._DDC) +			continue; +  		if (type) {  			switch (type) {  			case ACPI_VIDEO_DISPLAY_CRT: @@ -1411,89 +1519,40 @@ acpi_video_bus_get_devices(struct acpi_video_bus *video,  	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 (ACPI_FAILURE(status)) { -			printk(KERN_WARNING PREFIX -					"Cant attach device\n"); -			continue; +		if (status) { +			dev_err(&dev->dev, "Can't attach device\n"); +			break;  		}  	}  	return status;  } -static int acpi_video_bus_put_one_device(struct acpi_video_device *device) -{ -	acpi_status status; - -	if (!device || !device->video) -		return -ENOENT; - -	status = acpi_remove_notify_handler(device->dev->handle, -					    ACPI_DEVICE_NOTIFY, -					    acpi_video_device_notify); -	if (ACPI_FAILURE(status)) { -		printk(KERN_WARNING PREFIX -		       "Cant remove video notify handler\n"); -	} -	if (device->backlight) { -		sysfs_remove_link(&device->backlight->dev.kobj, "device"); -		backlight_device_unregister(device->backlight); -		device->backlight = 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; -	} -	video_output_unregister(device->output_dev); - -	return 0; -} - -static int acpi_video_bus_put_devices(struct acpi_video_bus *video) -{ -	int status; -	struct acpi_video_device *dev, *next; - -	mutex_lock(&video->device_list_lock); - -	list_for_each_entry_safe(dev, next, &video->video_device_list, entry) { - -		status = acpi_video_bus_put_one_device(dev); -		if (ACPI_FAILURE(status)) -			printk(KERN_WARNING PREFIX -			       "hhuuhhuu bug in acpi video driver.\n"); - -		if (dev->brightness) { -			kfree(dev->brightness->levels); -			kfree(dev->brightness); -		} -		list_del(&dev->entry); -		kfree(dev); -	} - -	mutex_unlock(&video->device_list_lock); - -	return 0; -} -  /* 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, 0); +	return acpi_video_bus_DOS(video, 0, +				  acpi_osi_is_win8() ? 1 : 0);  }  static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)  { -	return acpi_video_bus_DOS(video, 0, 1); +	return acpi_video_bus_DOS(video, 0, +				  acpi_osi_is_win8() ? 0 : 1);  }  static void acpi_video_bus_notify(struct acpi_device *device, u32 event) @@ -1502,7 +1561,7 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)  	struct input_dev *input;  	int keycode = 0; -	if (!video) +	if (!video || !video->input)  		return;  	input = video->input; @@ -1510,7 +1569,6 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)  	switch (event) {  	case ACPI_VIDEO_NOTIFY_SWITCH:	/* User requested a switch,  					 * most likely via hotkey. */ -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_SWITCHVIDEOMODE;  		break; @@ -1518,20 +1576,16 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)  					 * connector. */  		acpi_video_device_enumerate(video);  		acpi_video_device_rebind(video); -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_SWITCHVIDEOMODE;  		break;  	case ACPI_VIDEO_NOTIFY_CYCLE:	/* Cycle Display output hotkey pressed. */ -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_SWITCHVIDEOMODE;  		break;  	case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT:	/* Next Display output hotkey pressed. */ -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_VIDEO_NEXT;  		break;  	case ACPI_VIDEO_NOTIFY_PREV_OUTPUT:	/* previous Display output hotkey pressed. */ -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_VIDEO_PREV;  		break; @@ -1541,7 +1595,9 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)  		break;  	} -	acpi_notifier_call_chain(device, event, 0); +	if (acpi_notifier_call_chain(device, event, 0)) +		/* Something vetoed the keypress. */ +		keycode = 0;  	if (keycode) {  		input_report_key(input, keycode, 1); @@ -1572,31 +1628,26 @@ static void acpi_video_device_notify(acpi_handle handle, u32 event, void *data)  	case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS:	/* Cycle brightness */  		if (brightness_switch_enabled)  			acpi_video_switch_brightness(video_device, event); -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_BRIGHTNESS_CYCLE;  		break;  	case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS:	/* Increase brightness */  		if (brightness_switch_enabled)  			acpi_video_switch_brightness(video_device, event); -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_BRIGHTNESSUP;  		break;  	case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS:	/* Decrease brightness */  		if (brightness_switch_enabled)  			acpi_video_switch_brightness(video_device, event); -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_BRIGHTNESSDOWN;  		break; -	case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:	/* zero brightnesss */ +	case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS:	/* zero brightness */  		if (brightness_switch_enabled)  			acpi_video_switch_brightness(video_device, event); -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_BRIGHTNESS_ZERO;  		break;  	case ACPI_VIDEO_NOTIFY_DISPLAY_OFF:	/* display device off */  		if (brightness_switch_enabled)  			acpi_video_switch_brightness(video_device, event); -		acpi_bus_generate_proc_event(device, event, 0);  		keycode = KEY_DISPLAY_OFF;  		break;  	default: @@ -1665,12 +1716,297 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context,  	return AE_OK;  } +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; +	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)) +		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"); +} + +static int acpi_video_bus_register_backlight(struct acpi_video_bus *video) +{ +	struct acpi_video_device *dev; + +	if (video->backlight_registered) +		return 0; + +	if (!acpi_video_verify_backlight_support()) +		return 0; + +	mutex_lock(&video->device_list_lock); +	list_for_each_entry(dev, &video->video_device_list, entry) +		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); +} + +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; +	} +} + +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; +} + +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; +} + +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; +} + +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; +	} +} + +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; +} + +static int acpi_video_backlight_notify(struct notifier_block *nb, +					unsigned long val, void *bd) +{ +	struct backlight_device *backlight = bd; +	struct acpi_video_bus *video; + +	/* acpi_video_verify_backlight_support only cares about raw devices */ +	if (backlight->props.type != BACKLIGHT_RAW) +		return NOTIFY_DONE; + +	video = container_of(nb, struct acpi_video_bus, backlight_nb); + +	switch (val) { +	case BACKLIGHT_REGISTERED: +		if (!acpi_video_verify_backlight_support()) +			acpi_video_bus_unregister_backlight(video); +		break; +	case BACKLIGHT_UNREGISTERED: +		acpi_video_bus_register_backlight(video); +		break; +	} + +	return NOTIFY_OK; +} + +static int acpi_video_bus_add_backlight_notify_handler( +						struct acpi_video_bus *video) +{ +	int error; + +	video->backlight_nb.notifier_call = acpi_video_backlight_notify; +	video->backlight_nb.priority = 0; +	error = backlight_register_notifier(&video->backlight_nb); +	if (error == 0) +		video->backlight_notifier_registered = true; + +	return error; +} + +static int acpi_video_bus_remove_backlight_notify_handler( +						struct acpi_video_bus *video) +{ +	if (!video->backlight_notifier_registered) +		return 0; + +	video->backlight_notifier_registered = false; + +	return backlight_unregister_notifier(&video->backlight_nb); +} + +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; +} +  static int instance;  static int acpi_video_bus_add(struct acpi_device *device)  {  	struct acpi_video_bus *video; -	struct input_dev *input;  	int error;  	acpi_status status; @@ -1696,7 +2032,7 @@ static int acpi_video_bus_add(struct acpi_device *device)  	if (!strcmp(device->pnp.bus_id, "VID")) {  		if (instance)  			device->pnp.bus_id[3] = '0' + instance; -		instance ++; +		instance++;  	}  	/* a hack to fix the duplicate name "VGA" problem on Pa 3553 */  	if (!strcmp(device->pnp.bus_id, "VGA")) { @@ -1718,63 +2054,36 @@ static int acpi_video_bus_add(struct acpi_device *device)  	mutex_init(&video->device_list_lock);  	INIT_LIST_HEAD(&video->video_device_list); -	acpi_video_bus_get_devices(video, device); -	acpi_video_bus_start_devices(video); - -	video->input = input = input_allocate_device(); -	if (!input) { -		error = -ENOMEM; -		goto err_stop_video; -	} - -	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 = &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); +	error = acpi_video_bus_get_devices(video, device);  	if (error) -		goto err_free_input_dev; +		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); -	video->pm_nb.notifier_call = acpi_video_resume; -	video->pm_nb.priority = 0; -	register_pm_notifier(&video->pm_nb); +	acpi_video_bus_register_backlight(video); +	acpi_video_bus_add_notify_handler(video); +	acpi_video_bus_add_backlight_notify_handler(video);  	return 0; - err_free_input_dev: -	input_free_device(input); - err_stop_video: -	acpi_video_bus_stop_devices(video); +err_put_video:  	acpi_video_bus_put_devices(video);  	kfree(video->attached_array); - err_free_video: +err_free_video:  	kfree(video);  	device->driver_data = NULL;  	return error;  } -static int acpi_video_bus_remove(struct acpi_device *device, int type) +static int acpi_video_bus_remove(struct acpi_device *device)  {  	struct acpi_video_bus *video = NULL; @@ -1784,21 +2093,33 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type)  	video = acpi_driver_data(device); -	unregister_pm_notifier(&video->pm_nb); - -	acpi_video_bus_stop_devices(video); +	acpi_video_bus_remove_backlight_notify_handler(video); +	acpi_video_bus_remove_notify_handler(video); +	acpi_video_bus_unregister_backlight(video);  	acpi_video_bus_put_devices(video); -	input_unregister_device(video->input); +	mutex_lock(&video_list_lock); +	list_del(&video->entry); +	mutex_unlock(&video_list_lock); +  	kfree(video->attached_array);  	kfree(video);  	return 0;  } +static int __init is_i740(struct pci_dev *dev) +{ +	if (dev->device == 0x00D1) +		return 1; +	if (dev->device == 0x7000) +		return 1; +	return 0; +} +  static int __init intel_opregion_present(void)  { -#if defined(CONFIG_DRM_I915) || defined(CONFIG_DRM_I915_MODULE) +	int opregion = 0;  	struct pci_dev *dev = NULL;  	u32 address; @@ -1807,13 +2128,15 @@ static int __init intel_opregion_present(void)  			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; -		return 1; +		opregion = 1;  	} -#endif -	return 0; +	return opregion;  }  int acpi_video_register(void) @@ -1827,6 +2150,9 @@ int acpi_video_register(void)  		return 0;  	} +	mutex_init(&video_list_lock); +	INIT_LIST_HEAD(&video_bus_head); +  	result = acpi_bus_register_driver(&acpi_video_bus);  	if (result < 0)  		return -ENODEV; @@ -1858,6 +2184,20 @@ void acpi_video_unregister(void)  }  EXPORT_SYMBOL(acpi_video_unregister); +void acpi_video_unregister_backlight(void) +{ +	struct acpi_video_bus *video; + +	if (!register_count) +		return; + +	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); +} +EXPORT_SYMBOL(acpi_video_unregister_backlight); +  /*   * This is kind of nasty. Hardware using Intel chipsets may require   * the video opregion code to be run first in order to initialise  | 
