aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorMatthew Garrett <mjg59@srcf.ucam.org>2009-03-19 17:35:39 -0400
committerLen Brown <len.brown@intel.com>2009-03-27 22:11:03 -0400
commit74a365b3f354fafc537efa5867deb7a9fadbfe27 (patch)
tree66ebff203807d2ed77e77e1ccc2f71f63e571098 /drivers/gpu
parentc60d638e29c780b75b648283a197d0226e3576c3 (diff)
ACPI: Populate DIDL before registering ACPI video device on Intel
Intel graphics hardware that implements the ACPI IGD OpRegion spec requires that the list of display devices be populated before any ACPI video methods are called. Detect when this is the case and defer registration until the opregion code calls it. Fixes crashes on HP laptops. http://bugzilla.kernel.org/show_bug.cgi?id=11259 Signed-off-by: Matthew Garrett <mjg@redhat.com> Acked-by: Eric Anholt <eric@anholt.net> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c5
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c2
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h2
-rw-r--r--drivers/gpu/drm/i915/i915_opregion.c65
4 files changed, 69 insertions, 5 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 6d21b9e48b8..638686904e0 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1144,8 +1144,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
1144 if (!IS_I945G(dev) && !IS_I945GM(dev)) 1144 if (!IS_I945G(dev) && !IS_I945GM(dev))
1145 pci_enable_msi(dev->pdev); 1145 pci_enable_msi(dev->pdev);
1146 1146
1147 intel_opregion_init(dev);
1148
1149 spin_lock_init(&dev_priv->user_irq_lock); 1147 spin_lock_init(&dev_priv->user_irq_lock);
1150 dev_priv->user_irq_refcount = 0; 1148 dev_priv->user_irq_refcount = 0;
1151 1149
@@ -1164,6 +1162,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
1164 } 1162 }
1165 } 1163 }
1166 1164
1165 /* Must be done after probing outputs */
1166 intel_opregion_init(dev, 0);
1167
1167 return 0; 1168 return 0;
1168 1169
1169out_iomapfree: 1170out_iomapfree:
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index b293ef0bae7..209592fdb7e 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -99,7 +99,7 @@ static int i915_resume(struct drm_device *dev)
99 99
100 i915_restore_state(dev); 100 i915_restore_state(dev);
101 101
102 intel_opregion_init(dev); 102 intel_opregion_init(dev, 1);
103 103
104 /* KMS EnterVT equivalent */ 104 /* KMS EnterVT equivalent */
105 if (drm_core_check_feature(dev, DRIVER_MODESET)) { 105 if (drm_core_check_feature(dev, DRIVER_MODESET)) {
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index d6cc9861e0a..1679a951aa7 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -659,7 +659,7 @@ extern int i915_restore_state(struct drm_device *dev);
659 659
660#ifdef CONFIG_ACPI 660#ifdef CONFIG_ACPI
661/* i915_opregion.c */ 661/* i915_opregion.c */
662extern int intel_opregion_init(struct drm_device *dev); 662extern int intel_opregion_init(struct drm_device *dev, int resume);
663extern void intel_opregion_free(struct drm_device *dev); 663extern void intel_opregion_free(struct drm_device *dev);
664extern void opregion_asle_intr(struct drm_device *dev); 664extern void opregion_asle_intr(struct drm_device *dev);
665extern void opregion_enable_asle(struct drm_device *dev); 665extern void opregion_enable_asle(struct drm_device *dev);
diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c
index ff012835a38..69427722d20 100644
--- a/drivers/gpu/drm/i915/i915_opregion.c
+++ b/drivers/gpu/drm/i915/i915_opregion.c
@@ -26,6 +26,7 @@
26 */ 26 */
27 27
28#include <linux/acpi.h> 28#include <linux/acpi.h>
29#include <acpi/video.h>
29 30
30#include "drmP.h" 31#include "drmP.h"
31#include "i915_drm.h" 32#include "i915_drm.h"
@@ -136,6 +137,12 @@ struct opregion_asle {
136 137
137#define ASLE_CBLV_VALID (1<<31) 138#define ASLE_CBLV_VALID (1<<31)
138 139
140#define ACPI_OTHER_OUTPUT (0<<8)
141#define ACPI_VGA_OUTPUT (1<<8)
142#define ACPI_TV_OUTPUT (2<<8)
143#define ACPI_DIGITAL_OUTPUT (3<<8)
144#define ACPI_LVDS_OUTPUT (4<<8)
145
139static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) 146static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
140{ 147{
141 struct drm_i915_private *dev_priv = dev->dev_private; 148 struct drm_i915_private *dev_priv = dev->dev_private;
@@ -282,7 +289,58 @@ static struct notifier_block intel_opregion_notifier = {
282 .notifier_call = intel_opregion_video_event, 289 .notifier_call = intel_opregion_video_event,
283}; 290};
284 291
285int intel_opregion_init(struct drm_device *dev) 292/*
293 * Initialise the DIDL field in opregion. This passes a list of devices to
294 * the firmware. Values are defined by section B.4.2 of the ACPI specification
295 * (version 3)
296 */
297
298static void intel_didl_outputs(struct drm_device *dev)
299{
300 struct drm_i915_private *dev_priv = dev->dev_private;
301 struct intel_opregion *opregion = &dev_priv->opregion;
302 struct drm_connector *connector;
303 int i = 0;
304
305 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
306 int output_type = ACPI_OTHER_OUTPUT;
307 if (i >= 8) {
308 dev_printk (KERN_ERR, &dev->pdev->dev,
309 "More than 8 outputs detected\n");
310 return;
311 }
312 switch (connector->connector_type) {
313 case DRM_MODE_CONNECTOR_VGA:
314 case DRM_MODE_CONNECTOR_DVIA:
315 output_type = ACPI_VGA_OUTPUT;
316 break;
317 case DRM_MODE_CONNECTOR_Composite:
318 case DRM_MODE_CONNECTOR_SVIDEO:
319 case DRM_MODE_CONNECTOR_Component:
320 case DRM_MODE_CONNECTOR_9PinDIN:
321 output_type = ACPI_TV_OUTPUT;
322 break;
323 case DRM_MODE_CONNECTOR_DVII:
324 case DRM_MODE_CONNECTOR_DVID:
325 case DRM_MODE_CONNECTOR_DisplayPort:
326 case DRM_MODE_CONNECTOR_HDMIA:
327 case DRM_MODE_CONNECTOR_HDMIB:
328 output_type = ACPI_DIGITAL_OUTPUT;
329 break;
330 case DRM_MODE_CONNECTOR_LVDS:
331 output_type = ACPI_LVDS_OUTPUT;
332 break;
333 }
334 opregion->acpi->didl[i] |= (1<<31) | output_type | i;
335 i++;
336 }
337
338 /* If fewer than 8 outputs, the list must be null terminated */
339 if (i < 8)
340 opregion->acpi->didl[i] = 0;
341}
342
343int intel_opregion_init(struct drm_device *dev, int resume)
286{ 344{
287 struct drm_i915_private *dev_priv = dev->dev_private; 345 struct drm_i915_private *dev_priv = dev->dev_private;
288 struct intel_opregion *opregion = &dev_priv->opregion; 346 struct intel_opregion *opregion = &dev_priv->opregion;
@@ -312,6 +370,11 @@ int intel_opregion_init(struct drm_device *dev)
312 if (mboxes & MBOX_ACPI) { 370 if (mboxes & MBOX_ACPI) {
313 DRM_DEBUG("Public ACPI methods supported\n"); 371 DRM_DEBUG("Public ACPI methods supported\n");
314 opregion->acpi = base + OPREGION_ACPI_OFFSET; 372 opregion->acpi = base + OPREGION_ACPI_OFFSET;
373 if (drm_core_check_feature(dev, DRIVER_MODESET)) {
374 intel_didl_outputs(dev);
375 if (!resume)
376 acpi_video_register();
377 }
315 } else { 378 } else {
316 DRM_DEBUG("Public ACPI methods not supported\n"); 379 DRM_DEBUG("Public ACPI methods not supported\n");
317 err = -ENOTSUPP; 380 err = -ENOTSUPP;