Linux Kernel Mailing List | 21 Jul 19:33 2013

ACPI / video / i915: No ACPI backlight if firmware expects Windows 8

Gitweb:     http://git.kernel.org/linus/;a=commit;h=8c5bd7adb2ce47e6aa39d17b2375f69b0c0aa255
Commit:     8c5bd7adb2ce47e6aa39d17b2375f69b0c0aa255
Parent:     c04c697cf1fe8f0962ccd3c2392a9b637a5307aa
Author:     Rafael J. Wysocki <rafael.j.wysocki <at> intel.com>
AuthorDate: Thu Jul 18 02:08:06 2013 +0200
Committer:  Rafael J. Wysocki <rafael.j.wysocki <at> intel.com>
CommitDate: Thu Jul 18 02:08:06 2013 +0200

    ACPI / video / i915: No ACPI backlight if firmware expects Windows 8

    According to Matthew Garrett, "Windows 8 leaves backlight control up
    to individual graphics drivers rather than making ACPI calls itself.
    There's plenty of evidence to suggest that the Intel driver for
    Windows [8] doesn't use the ACPI interface, including the fact that
    it's broken on a bunch of machines when the OS claims to support
    Windows 8.  The simplest thing to do appears to be to disable the
    ACPI backlight interface on these systems".

    There's a problem with that approach, however, because simply
    avoiding to register the ACPI backlight interface if the firmware
    calls _OSI for Windows 8 may not work in the following situations:
     (1) The ACPI backlight interface actually works on the given system
         and the i915 driver is not loaded (e.g. another graphics driver
         is used).
     (2) The ACPI backlight interface doesn't work on the given system,
         but there is a vendor platform driver that will register its
         own, equally broken, backlight interface if not prevented from
         doing so by the ACPI subsystem.
    Therefore we need to allow the ACPI backlight interface to be
    registered until the i915 driver is loaded which then will unregister
    it if the firmware has called _OSI for Windows 8 (or will register
    the ACPI video driver without backlight support if not already
    present).

    For this reason, introduce an alternative function for registering
    ACPI video, acpi_video_register_with_quirks(), that will check
    whether or not the ACPI video driver has already been registered
    and whether or not the backlight Windows 8 quirk has to be applied.
    If the quirk has to be applied, it will block the ACPI backlight
    support and either unregister the backlight interface if the ACPI
    video driver has already been registered, or register the ACPI
    video driver without the backlight interface otherwise.  Make
    the i915 driver use acpi_video_register_with_quirks() instead of
    acpi_video_register() in i915_driver_load().

    This change is based on earlier patches from Matthew Garrett,
    Chun-Yi Lee and Seth Forshee and includes a fix from Aaron Lu's.

    References: https://bugzilla.kernel.org/show_bug.cgi?id=51231
    Tested-by: Aaron Lu <aaron.lu <at> intel.com>
    Tested-by: Igor Gnatenko <i.gnatenko.brain <at> gmail.com>
    Tested-by: Yves-Alexis Perez <corsac <at> debian.org>
    Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki <at> intel.com>
    Reviewed-by: Aaron Lu <aaron.lu <at> intel.com>
    Acked-by: Matthew Garrett <matthew.garrett <at> nebula.com>
---
 drivers/acpi/internal.h         |   11 ++++++
 drivers/acpi/video.c            |   69 ++++++++++++++++++++++++++++++++++----
 drivers/acpi/video_detect.c     |   21 ++++++++++++
 drivers/gpu/drm/i915/i915_dma.c |    2 +-
 include/acpi/video.h            |   11 ++++++-
 include/linux/acpi.h            |    1 +
 6 files changed, 105 insertions(+), 10 deletions(-)

diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 3a50a34..227aca7 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
 <at>  <at>  -164,4 +164,15  <at>  <at>  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_ */
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index f236e17..e9d4bb6 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
 <at>  <at>  -44,6 +44,8  <at>  <at> 
 #include <linux/suspend.h>
 #include <acpi/video.h>

+#include "internal.h"
+
 #define PREFIX "ACPI: "

 #define ACPI_VIDEO_BUS_NAME		"Video Bus"
 <at>  <at>  -901,7 +903,7  <at>  <at>  static void acpi_video_device_find_cap(struct acpi_video_device *device)
 	if (acpi_video_init_brightness(device))
 		return;

-	if (acpi_video_backlight_support()) {
+	if (acpi_video_verify_backlight_support()) {
 		struct backlight_properties props;
 		struct pci_dev *pdev;
 		acpi_handle acpi_parent;
 <at>  <at>  -1356,8 +1358,8  <at>  <at>  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)
 <at>  <at>  -1859,6 +1861,46  <at>  <at>  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)
 <at>  <at>  -1890,14 +1932,25  <at>  <at>  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;
 	}

 <at>  <at>  -1913,7 +1966,7  <at>  <at>  int acpi_video_register(void)

 	return 0;
 }
-EXPORT_SYMBOL(acpi_video_register);
+EXPORT_SYMBOL(__acpi_video_register);

 void acpi_video_unregister(void)
 {
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index e6bd910..826e52d 100644
--- a/drivers/acpi/video_detect.c
+++ b/drivers/acpi/video_detect.c
 <at>  <at>  -38,6 +38,8  <at>  <at> 
 #include <linux/dmi.h>
 #include <linux/pci.h>

+#include "internal.h"
+
 #define PREFIX "ACPI: "

 ACPI_MODULE_NAME("video");
 <at>  <at>  -234,6 +236,17  <at>  <at>  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
 <at>  <at>  -278,6 +291,14  <at>  <at>  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.
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index adb319b..cf188ab 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
 <at>  <at>  -1648,7 +1648,7  <at>  <at>  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))
diff --git a/include/acpi/video.h b/include/acpi/video.h
index 61109f2..b26dc4f 100644
--- a/include/acpi/video.h
+++ b/include/acpi/video.h
 <at>  <at>  -17,12 +17,21  <at>  <at>  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)
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 353ba25..6ad72f9 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
 <at>  <at>  -191,6 +191,7  <at>  <at>  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)

--
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to majordomo <at> vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html


Gmane