Commit ea45ea70 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'acpi-video-3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull ACPI video support fixes from Rafael Wysocki:
 "I'm sending a separate pull request for this as it may be somewhat
  controversial.  The breakage addressed here is not really new and the
  fixes may not satisfy all users of the affected systems, but we've had
  so much back and forth dance in this area over the last several weeks
  that I think it's time to actually make some progress.

  The source of the problem is that about a year ago we started to tell
  BIOSes that we're compatible with Windows 8, which we really need to
  do, because some systems shipping with Windows 8 are tested with it
  and nothing else, so if we tell their BIOSes that we aren't compatible
  with Windows 8, we expose our users to untested BIOS/AML code paths.

  However, as it turns out, some Windows 8-specific AML code paths are
  not tested either, because Windows 8 actually doesn't use the ACPI
  methods containing them, so if we declare Windows 8 compatibility and
  attempt to use those ACPI methods, things break.  That occurs mostly
  in the backlight support area where in particular the _BCM and _BQC
  methods are plain unusable on some systems if the OS declares Windows
  8 compatibility.

  [ The additional twist is that they actually become usable if the OS
    says it is not compatible with Windows 8, but that may cause
    problems to show up elsewhere ]

  Investigation carried out by Matthew Garrett indicates that what
  Windows 8 does about backlight is to leave backlight control up to
  individual graphics drivers.  At least there's evidence that it does
  that if the Intel graphics driver is used, so we've decided to follow
  Windows 8 in that respect and allow i915 to control backlight (Daniel
  likes that part).

  The first commit from Aaron Lu makes ACPICA export the variable from
  which we can infer whether or not the BIOS believes that we are
  compatible with Windows 8.

  The second commit from Matthew Garrett prepares the ACPI video driver
  by making it initialize the ACPI backlight even if it is not going to
  be used afterward (that is needed for backlight control to work on
  Thinkpads).

  The third commit implements the actual workaround making i915 take
  over backlight control if the firmware thinks it's dealing with
  Windows 8 and is based on the work of multiple developers, including
  Matthew Garrett, Chun-Yi Lee, Seth Forshee, and Aaron Lu.

  The final commit from Aaron Lu makes us follow Windows 8 by informing
  the firmware through the _DOS method that it should not carry out
  automatic brightness changes, so that brightness can be controlled by
  GUI.

  Hopefully, this approach will allow us to avoid using blacklists of
  systems that should not declare Windows 8 compatibility just to avoid
  backlight control problems in the future.

   - Change from Aaron Lu makes ACPICA export a variable which can be
     used by driver code to determine whether or not the BIOS believes
     that we are compatible with Windows 8.

   - Change from Matthew Garrett makes the ACPI video driver initialize
     the ACPI backlight even if it is not going to be used afterward
     (that is needed for backlight control to work on Thinkpads).

   - Fix from Rafael J Wysocki implements Windows 8 backlight support
     workaround making i915 take over bakclight control if the firmware
     thinks it's dealing with Windows 8.  Based on the work of multiple
     developers including Matthew Garrett, Chun-Yi Lee, Seth Forshee,
     and Aaron Lu.

   - Fix from Aaron Lu makes the kernel follow Windows 8 by informing
     the firmware through the _DOS method that it should not carry out
     automatic brightness changes, so that brightness can be controlled
     by GUI"

* tag 'acpi-video-3.11' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  ACPI / video: no automatic brightness changes by win8-compatible firmware
  ACPI / video / i915: No ACPI backlight if firmware expects Windows 8
  ACPI / video: Always call acpi_video_init_brightness() on init
  ACPICA: expose OSI version
parents 90db76e8 efaa14c7
......@@ -931,19 +931,6 @@ struct acpi_bit_register_info {
/* Structs and definitions for _OSI support and I/O port validation */
#define ACPI_OSI_WIN_2000 0x01
#define ACPI_OSI_WIN_XP 0x02
#define ACPI_OSI_WIN_XP_SP1 0x03
#define ACPI_OSI_WINSRV_2003 0x04
#define ACPI_OSI_WIN_XP_SP2 0x05
#define ACPI_OSI_WINSRV_2003_SP1 0x06
#define ACPI_OSI_WIN_VISTA 0x07
#define ACPI_OSI_WINSRV_2008 0x08
#define ACPI_OSI_WIN_VISTA_SP1 0x09
#define ACPI_OSI_WIN_VISTA_SP2 0x0A
#define ACPI_OSI_WIN_7 0x0B
#define ACPI_OSI_WIN_8 0x0C
#define ACPI_ALWAYS_ILLEGAL 0x00
struct acpi_interface_info {
......
......@@ -164,4 +164,15 @@ struct platform_device;
int acpi_create_platform_device(struct acpi_device *adev,
const struct acpi_device_id *id);
/*--------------------------------------------------------------------------
Video
-------------------------------------------------------------------------- */
#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE)
bool acpi_video_backlight_quirks(void);
bool acpi_video_verify_backlight_support(void);
#else
static inline bool acpi_video_backlight_quirks(void) { return false; }
static inline bool acpi_video_verify_backlight_support(void) { return false; }
#endif
#endif /* _ACPI_INTERNAL_H_ */
......@@ -44,6 +44,8 @@
#include <linux/suspend.h>
#include <acpi/video.h>
#include "internal.h"
#define PREFIX "ACPI: "
#define ACPI_VIDEO_BUS_NAME "Video Bus"
......@@ -906,7 +908,10 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
device->cap._DDC = 1;
}
if (acpi_video_backlight_support()) {
if (acpi_video_init_brightness(device))
return;
if (acpi_video_verify_backlight_support()) {
struct backlight_properties props;
struct pci_dev *pdev;
acpi_handle acpi_parent;
......@@ -915,9 +920,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
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;
......@@ -977,6 +979,11 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
if (result)
printk(KERN_ERR PREFIX "Create sysfs link\n");
} else {
/* Remove the brightness object. */
kfree(device->brightness->levels);
kfree(device->brightness);
device->brightness = NULL;
}
}
......@@ -1359,8 +1366,8 @@ 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)
......@@ -1540,14 +1547,20 @@ static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
/* acpi_video interface */
/*
* Win8 requires setting bit2 of _DOS to let firmware know it shouldn't
* preform any automatic brightness change on receiving a notification.
*/
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_video_backlight_quirks() ? 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_video_backlight_quirks() ? 0 : 1);
}
static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
......@@ -1862,6 +1875,46 @@ static int acpi_video_bus_remove(struct acpi_device *device)
return 0;
}
static acpi_status video_unregister_backlight(acpi_handle handle, u32 lvl,
void *context, void **rv)
{
struct acpi_device *acpi_dev;
struct acpi_video_bus *video;
struct acpi_video_device *dev, *next;
if (acpi_bus_get_device(handle, &acpi_dev))
return AE_OK;
if (acpi_match_device_ids(acpi_dev, video_device_ids))
return AE_OK;
video = acpi_driver_data(acpi_dev);
if (!video)
return AE_OK;
acpi_video_bus_stop_devices(video);
mutex_lock(&video->device_list_lock);
list_for_each_entry_safe(dev, next, &video->video_device_list, entry) {
if (dev->backlight) {
backlight_device_unregister(dev->backlight);
dev->backlight = NULL;
kfree(dev->brightness->levels);
kfree(dev->brightness);
}
if (dev->cooling_dev) {
sysfs_remove_link(&dev->dev->dev.kobj,
"thermal_cooling");
sysfs_remove_link(&dev->cooling_dev->device.kobj,
"device");
thermal_cooling_device_unregister(dev->cooling_dev);
dev->cooling_dev = NULL;
}
}
mutex_unlock(&video->device_list_lock);
acpi_video_bus_start_devices(video);
return AE_OK;
}
static int __init is_i740(struct pci_dev *dev)
{
if (dev->device == 0x00D1)
......@@ -1893,14 +1946,25 @@ static int __init intel_opregion_present(void)
return opregion;
}
int acpi_video_register(void)
int __acpi_video_register(bool backlight_quirks)
{
int result = 0;
bool no_backlight;
int result;
no_backlight = backlight_quirks ? acpi_video_backlight_quirks() : false;
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.
* If acpi_video_register() has been called already, don't try
* to register acpi_video_bus, but unregister backlight devices
* if no backlight support is requested.
*/
if (no_backlight)
acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
ACPI_UINT32_MAX,
video_unregister_backlight,
NULL, NULL, NULL);
return 0;
}
......@@ -1916,7 +1980,7 @@ int acpi_video_register(void)
return 0;
}
EXPORT_SYMBOL(acpi_video_register);
EXPORT_SYMBOL(__acpi_video_register);
void acpi_video_unregister(void)
{
......
......@@ -38,6 +38,8 @@
#include <linux/dmi.h>
#include <linux/pci.h>
#include "internal.h"
#define PREFIX "ACPI: "
ACPI_MODULE_NAME("video");
......@@ -234,6 +236,17 @@ static void acpi_video_caps_check(void)
acpi_video_get_capabilities(NULL);
}
bool acpi_video_backlight_quirks(void)
{
if (acpi_gbl_osi_data >= ACPI_OSI_WIN_8) {
acpi_video_caps_check();
acpi_video_support |= ACPI_VIDEO_SKIP_BACKLIGHT;
return true;
}
return false;
}
EXPORT_SYMBOL(acpi_video_backlight_quirks);
/* Promote the vendor interface instead of the generic video module.
* This function allow DMI blacklists to be implemented by externals
* platform drivers instead of putting a big blacklist in video_detect.c
......@@ -278,6 +291,14 @@ int acpi_video_backlight_support(void)
}
EXPORT_SYMBOL(acpi_video_backlight_support);
/* For the ACPI video driver use only. */
bool acpi_video_verify_backlight_support(void)
{
return (acpi_video_support & ACPI_VIDEO_SKIP_BACKLIGHT) ?
false : acpi_video_backlight_support();
}
EXPORT_SYMBOL(acpi_video_verify_backlight_support);
/*
* Use acpi_backlight=vendor/video to force that backlight switching
* is processed by vendor specific acpi drivers or video.ko driver.
......
......@@ -1648,7 +1648,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
if (INTEL_INFO(dev)->num_pipes) {
/* Must be done after probing outputs */
intel_opregion_init(dev);
acpi_video_register();
acpi_video_register_with_quirks();
}
if (IS_GEN5(dev))
......
......@@ -62,6 +62,7 @@ extern u32 acpi_current_gpe_count;
extern struct acpi_table_fadt acpi_gbl_FADT;
extern u8 acpi_gbl_system_awake_and_running;
extern u8 acpi_gbl_reduced_hardware; /* ACPI 5.0 */
extern u8 acpi_gbl_osi_data;
/* Runtime configuration of debug print levels */
......
......@@ -1144,4 +1144,19 @@ struct acpi_memory_list {
#endif
};
/* Definitions for _OSI support */
#define ACPI_OSI_WIN_2000 0x01
#define ACPI_OSI_WIN_XP 0x02
#define ACPI_OSI_WIN_XP_SP1 0x03
#define ACPI_OSI_WINSRV_2003 0x04
#define ACPI_OSI_WIN_XP_SP2 0x05
#define ACPI_OSI_WINSRV_2003_SP1 0x06
#define ACPI_OSI_WIN_VISTA 0x07
#define ACPI_OSI_WINSRV_2008 0x08
#define ACPI_OSI_WIN_VISTA_SP1 0x09
#define ACPI_OSI_WIN_VISTA_SP2 0x0A
#define ACPI_OSI_WIN_7 0x0B
#define ACPI_OSI_WIN_8 0x0C
#endif /* __ACTYPES_H__ */
......@@ -17,12 +17,21 @@ struct acpi_device;
#define ACPI_VIDEO_DISPLAY_LEGACY_TV 0x0200
#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE)
extern int acpi_video_register(void);
extern int __acpi_video_register(bool backlight_quirks);
static inline int acpi_video_register(void)
{
return __acpi_video_register(false);
}
static inline int acpi_video_register_with_quirks(void)
{
return __acpi_video_register(true);
}
extern void acpi_video_unregister(void);
extern int acpi_video_get_edid(struct acpi_device *device, int type,
int device_id, void **edid);
#else
static inline int acpi_video_register(void) { return 0; }
static inline int acpi_video_register_with_quirks(void) { return 0; }
static inline void acpi_video_unregister(void) { return; }
static inline int acpi_video_get_edid(struct acpi_device *device, int type,
int device_id, void **edid)
......
......@@ -191,6 +191,7 @@ extern bool wmi_has_guid(const char *guid);
#define ACPI_VIDEO_BACKLIGHT_DMI_VIDEO 0x0200
#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR 0x0400
#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800
#define ACPI_VIDEO_SKIP_BACKLIGHT 0x1000
#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment