kern/190186: [patch] i915 driver: enable opregion handling

Henry Hu henry.hu.sh at gmail.com
Sat May 24 20:50:00 UTC 2014


>Number:         190186
>Category:       kern
>Synopsis:       [patch] i915 driver: enable opregion handling
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Sat May 24 20:50:00 UTC 2014
>Closed-Date:
>Last-Modified:
>Originator:     Henry Hu
>Release:        FreeBSD 11-CURRENT
>Organization:
Columbia University
>Environment:
FreeBSD pepsi 11.0-CURRENT FreeBSD 11.0-CURRENT #2 r265841M: Sun May 11 17:04:31 EDT 2014     root at pepsi:/usr/obj/usr/head/sys/MYKERNEL  amd64

>Description:
My laptop is ASUS UX51VZ which has an integrated graphics adapter Intel HD4000 (IvyBridge). I'm using the KMS driver with new Xorg.
Currently, opregion handling in i915 driver is disabled. In intel_opregion.c, the relevant code is surrounded by #ifdef CONFIG_ACPI which only works for linux. Related interrupt handling is also disabled.
As a result, opregion support is disabled, and status reported by ACPI video extension is wrong (for example, hw.acpi.video.lcd0.active is always 0). Brightness keys on the keyboard do not work, and setting brightness through ACPI video extension sysctls (like hw.acpi.video.lcd0.brightness) has no effect.

>How-To-Repeat:
Try FreeBSD 11-CURRENT on a recent intel laptop, and change brightness by pressing the Brightness keys on the keyboard, or adjust brightness by hw.acpi.video.lcd0.brightness.
>Fix:
The attached patch enables OpRegion handling code. It also enables opregion-related interrupts.
The Linux-specific parts are rewritten for FreeBSD. The logic is the same.
The video event handler is still disabled because it does not have any functionality. 
After applying the patch, both the brightness keys and the ACPI video extension sysctls work correctly.

Patch attached with submission follows:

Index: drm2/i915/i915_drv.h
===================================================================
--- drm2/i915/i915_drv.h	(版本 265841)
+++ drm2/i915/i915_drv.h	(工作副本)
@@ -1242,10 +1242,11 @@
 
 /* intel_opregion.c */
 int intel_opregion_setup(struct drm_device *dev);
-extern int intel_opregion_init(struct drm_device *dev);
+extern void intel_opregion_init(struct drm_device *dev);
 extern void intel_opregion_fini(struct drm_device *dev);
-extern void opregion_asle_intr(struct drm_device *dev);
-extern void opregion_enable_asle(struct drm_device *dev);
+extern void intel_opregion_asle_intr(struct drm_device *dev);
+extern void intel_opregion_gse_intr(struct drm_device *dev);
+extern void intel_opregion_enable_asle(struct drm_device *dev);
 
 /* i915_gem_gtt.c */
 int i915_gem_init_aliasing_ppgtt(struct drm_device *dev);
Index: drm2/i915/i915_irq.c
===================================================================
--- drm2/i915/i915_irq.c	(版本 265841)
+++ drm2/i915/i915_irq.c	(工作副本)
@@ -537,11 +537,7 @@
 		notify_ring(dev, &dev_priv->rings[BCS]);
 
 	if (de_iir & DE_GSE_IVB) {
-#if 1
-		KIB_NOTYET();
-#else
 		intel_opregion_gse_intr(dev);
-#endif
 	}
 
 	if (de_iir & DE_PLANEA_FLIP_DONE_IVB) {
@@ -649,11 +645,7 @@
 		notify_ring(dev, &dev_priv->rings[BCS]);
 
 	if (de_iir & DE_GSE) {
-#if 1
-		KIB_NOTYET();
-#else
 		intel_opregion_gse_intr(dev);
-#endif
 	}
 
 	if (de_iir & DE_PLANEA_FLIP_DONE) {
@@ -1055,11 +1047,7 @@
 
 
 		if (blc_event || (iir & I915_ASLE_INTERRUPT)) {
-#if 1
-			KIB_NOTYET();
-#else
 			intel_opregion_asle_intr(dev);
-#endif
 		}
 
 		/* With MSI, interrupts are only generated when iir
@@ -1781,11 +1769,7 @@
 		I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
 	}
 
-#if 1
-	KIB_NOTYET();
-#else
 	intel_opregion_enable_asle(dev);
-#endif
 
 	return 0;
 }
Index: drm2/i915/intel_opregion.c
===================================================================
--- drm2/i915/intel_opregion.c	(版本 265841)
+++ drm2/i915/intel_opregion.c	(工作副本)
@@ -32,6 +32,9 @@
 #include <dev/drm2/i915/i915_drm.h>
 #include <dev/drm2/i915/i915_drv.h>
 #include <dev/drm2/i915/intel_drv.h>
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/accommon.h>
+#include <dev/acpica/acpivar.h>
 
 #define PCI_ASLE 0xe4
 #define PCI_ASLS 0xfc
@@ -144,7 +147,7 @@
 #define ACPI_DIGITAL_OUTPUT (3<<8)
 #define ACPI_LVDS_OUTPUT (4<<8)
 
-#ifdef CONFIG_ACPI
+#if 1
 static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -289,6 +292,7 @@
 
 static struct intel_opregion *system_opregion;
 
+#if 0
 static int intel_opregion_video_event(struct notifier_block *nb,
 				      unsigned long val, void *data)
 {
@@ -319,6 +323,7 @@
 static struct notifier_block intel_opregion_notifier = {
 	.notifier_call = intel_opregion_video_event,
 };
+#endif
 
 /*
  * Initialise the DIDL field in opregion. This passes a list of devices to
@@ -326,24 +331,42 @@
  * (version 3)
  */
 
+static int acpi_is_video_device(ACPI_HANDLE devh) {
+	ACPI_HANDLE h;
+	if (ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) ||
+		ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h))) {
+		return 0;
+	}
+	return 1;
+}
+
 static void intel_didl_outputs(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_opregion *opregion = &dev_priv->opregion;
 	struct drm_connector *connector;
-	acpi_handle handle;
-	struct acpi_device *acpi_dev, *acpi_cdev, *acpi_video_bus = NULL;
-	unsigned long long device_id;
-	acpi_status status;
+	u32 device_id;
+	ACPI_HANDLE handle, acpi_video_bus, acpi_cdev;
+	ACPI_STATUS status;
 	int i = 0;
 
-	handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev)))
+	handle = acpi_get_handle(dev->device);
+	if (!handle)
 		return;
 
-	if (acpi_is_video_device(acpi_dev))
-		acpi_video_bus = acpi_dev;
+	if (acpi_is_video_device(handle))
+		acpi_video_bus = handle;
 	else {
+		acpi_cdev = NULL;
+		acpi_video_bus = NULL;
+		while (AcpiGetNextObject(ACPI_TYPE_DEVICE, handle, acpi_cdev,
+					&acpi_cdev) != AE_NOT_FOUND) {
+			if (acpi_is_video_device(acpi_cdev)) {
+				acpi_video_bus = acpi_cdev;
+				break;
+			}
+		}
+#if 0
 		list_for_each_entry(acpi_cdev, &acpi_dev->children, node) {
 			if (acpi_is_video_device(acpi_cdev)) {
 				acpi_video_bus = acpi_cdev;
@@ -350,13 +373,30 @@
 				break;
 			}
 		}
+#endif
 	}
 
 	if (!acpi_video_bus) {
-		printk(KERN_WARNING "No ACPI video bus found\n");
+		device_printf(dev->device, "No ACPI video bus found\n");
 		return;
 	}
 
+	acpi_cdev = NULL;
+	while (AcpiGetNextObject(ACPI_TYPE_DEVICE, acpi_video_bus, acpi_cdev,
+				&acpi_cdev) != AE_NOT_FOUND) {
+		if (i >= 8) {
+			device_printf(dev->device, "More than 8 outputs detected\n");
+			return;
+		}
+		status = acpi_GetInteger(acpi_cdev, "_ADR", &device_id);
+		if (ACPI_SUCCESS(status)) {
+			if (!device_id)
+				goto blind_set;
+			opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f);
+			i++;
+		}
+	}
+#if 0
 	list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
 		if (i >= 8) {
 			dev_printk(KERN_ERR, &dev->pdev->dev,
@@ -373,6 +413,7 @@
 			i++;
 		}
 	}
+#endif
 
 end:
 	/* If fewer than 8 outputs, the list must be null terminated */
@@ -417,6 +458,25 @@
 	goto end;
 }
 
+static void intel_setup_cadls(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_opregion *opregion = &dev_priv->opregion;
+	int i = 0;
+	u32 disp_id;
+
+	/* Initialize the CADL field by duplicating the DIDL values.
+	 * Technically, this is not always correct as display outputs may exist,
+	 * but not active. This initialization is necessary for some Clevo
+	 * laptops that check this field before processing the brightness and
+	 * display switching hotkeys. Just like DIDL, CADL is NULL-terminated if
+	 * there are less than eight devices. */
+	do {
+		disp_id = opregion->acpi->didl[i];
+		opregion->acpi->cadl[i] = disp_id;
+	} while (++i < 8 && disp_id != 0);
+}
+
 void intel_opregion_init(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -426,8 +486,10 @@
 		return;
 
 	if (opregion->acpi) {
-		if (drm_core_check_feature(dev, DRIVER_MODESET))
+		if (drm_core_check_feature(dev, DRIVER_MODESET)) {
 			intel_didl_outputs(dev);
+			intel_setup_cadls(dev);
+		}
 
 		/* Notify BIOS we are ready to handle ACPI video ext notifs.
 		 * Right now, all the events are handled by the ACPI video module.
@@ -436,7 +498,9 @@
 		opregion->acpi->drdy = 1;
 
 		system_opregion = opregion;
+#if 0
 		register_acpi_notifier(&intel_opregion_notifier);
+#endif
 	}
 
 	if (opregion->asle)
@@ -455,11 +519,13 @@
 		opregion->acpi->drdy = 0;
 
 		system_opregion = NULL;
+#if 0
 		unregister_acpi_notifier(&intel_opregion_notifier);
+#endif
 	}
 
 	/* just clear all opregion memory pointers now */
-	iounmap(opregion->header);
+	pmap_unmapdev((vm_offset_t)opregion->header, OPREGION_SIZE);
 	opregion->header = NULL;
 	opregion->acpi = NULL;
 	opregion->swsci = NULL;


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list