[PATCH 8/8] drm/i915: Add Reenable Timer to turn Hotplug Detection back on.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



We disable hoptplug detection when we encounter a hotplug event
storm. Still hotplug detection is required on some outputs (like
Display Port). The interrupt storm may be only temporary (on certain
Dell Laptops for instance it happens at certain charging states of
the system). Thus we enable it after a certain grace period (2 minutes).
Should the interrupt storm persist it will be detected immediately
and it will be disabled again.

Signed-off-by: Egbert Eich <eich at suse.de>
---
 drivers/gpu/drm/i915/i915_drv.h  |    2 +
 drivers/gpu/drm/i915/i915_irq.c  |   58 ++++++++++++++++++++++++++++++++++++--
 drivers/gpu/drm/i915/intel_drv.h |    6 +++-
 3 files changed, 62 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index d7ad677..e3c3121 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -668,6 +668,8 @@ typedef struct drm_i915_private {
 
 	u32 hotplug_supported_mask;
 	struct work_struct hotplug_work;
+#define I915_REENABLE_HOTPLUG_DELAY (2*60*1000)
+	struct timer_list hotplug_reenable_timer;
 
 	int num_pipe;
 	int num_pch_pll;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index aa4986d..637f68d 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -279,6 +279,8 @@ static int i915_get_vblank_timestamp(struct drm_device *dev, int pipe,
 /*
  * Handle hotplug events outside the interrupt handler proper.
  */
+static void i915_reenable_hotplug_timer_func(unsigned long data);
+
 static void i915_hotplug_work_func(struct work_struct *work)
 {
 	drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
@@ -288,6 +290,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	struct drm_connector *connector;
 	struct intel_encoder *encoder;
 	unsigned long irqflags;
+	bool reset_timer = false;
 
 	mutex_lock(&mode_config->mutex);
 	DRM_DEBUG_KMS("running encoder hotplug functions\n");
@@ -295,12 +298,17 @@ static void i915_hotplug_work_func(struct work_struct *work)
 	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
 	list_for_each_entry(connector, &mode_config->connector_list, head) {
 		struct intel_connector *intel_connector = to_intel_connector(connector);
-		if (intel_connector->hpd_mark_disabled) {
-			intel_connector->hpd_mark_disabled = 0;
+		if (intel_connector->hpd_mark_disabled == HPD_MARKED_DISABLED) {
+			intel_connector->hpd_mark_disabled = HPD_DISABLED;
 			connector->polled = DRM_CONNECTOR_POLL_CONNECT
 				 | DRM_CONNECTOR_POLL_DISCONNECT;
+			reset_timer = true;
 		}
 	}
+	if (reset_timer)
+		mod_timer(&dev_priv->hotplug_reenable_timer,
+			  jiffies + msecs_to_jiffies(I915_REENABLE_HOTPLUG_DELAY));
+
 	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 
 	list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
@@ -333,7 +341,7 @@ static inline int hotplug_irq_storm_detect(struct drm_device *dev, u32 hotplug_t
 				intel_connector->hpd_cnt = 0;
 			} else if (intel_connector->hpd_cnt > 5) {
 				dev_priv->hotplug_supported_mask &= ~intel_connector->hpd_status_bit;
-				intel_connector->hpd_mark_disabled = 1;
+				intel_connector->hpd_mark_disabled = HPD_MARKED_DISABLED;
 				pr_warn("HPD interrupt storm on connector %s disabling\n",
 					 drm_get_connector_name(connector));
 				ret = 1;
@@ -2143,6 +2151,8 @@ static void valleyview_irq_uninstall(struct drm_device *dev)
 	I915_WRITE(VLV_IMR, 0xffffffff);
 	I915_WRITE(VLV_IER, 0x0);
 	POSTING_READ(VLV_IER);
+
+	del_timer_sync(&dev_priv->hotplug_reenable_timer);
 }
 
 static void ironlake_irq_uninstall(struct drm_device *dev)
@@ -2165,6 +2175,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
 	I915_WRITE(SDEIMR, 0xffffffff);
 	I915_WRITE(SDEIER, 0x0);
 	I915_WRITE(SDEIIR, I915_READ(SDEIIR));
+
+	del_timer_sync(&dev_priv->hotplug_reenable_timer);
 }
 
 static void i8xx_irq_preinstall(struct drm_device * dev)
@@ -2533,6 +2545,8 @@ static void i915_irq_uninstall(struct drm_device * dev)
 	I915_WRITE(IER, 0x0);
 
 	I915_WRITE(IIR, I915_READ(IIR));
+
+	del_timer_sync(&dev_priv->hotplug_reenable_timer);
 }
 
 static void i965_irq_preinstall(struct drm_device * dev)
@@ -2788,6 +2802,42 @@ static void i965_irq_uninstall(struct drm_device * dev)
 		I915_WRITE(PIPESTAT(pipe),
 			   I915_READ(PIPESTAT(pipe)) & 0x8000ffff);
 	I915_WRITE(IIR, I915_READ(IIR));
+
+	del_timer_sync(&dev_priv->hotplug_reenable_timer);
+}
+
+static void i915_reenable_hotplug_timer_func(unsigned long data)
+{
+	drm_i915_private_t *dev_priv = (drm_i915_private_t *)data;
+	struct drm_device *dev = dev_priv->dev;
+	struct drm_mode_config *mode_config = &dev->mode_config;
+	struct drm_connector *connector;
+	unsigned long irqflags;
+
+	spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
+	list_for_each_entry(connector, &mode_config->connector_list, head) {
+		struct intel_connector *intel_connector = to_intel_connector(connector);
+		if (intel_connector->hpd_mark_disabled == HPD_DISABLED) {
+			intel_connector->hpd_mark_disabled = HPD_ENABLED;
+			dev_priv->hotplug_supported_mask |= intel_connector->hpd_status_bit;
+			DRM_DEBUG_DRIVER("Reenabling HPD on connector %s\n",
+					 drm_get_connector_name(connector));
+			connector->polled = DRM_CONNECTOR_POLL_HPD;
+			if (IS_HASWELL(dev) ||
+			    IS_IVYBRIDGE(dev) ||
+			    (HAS_PCH_SPLIT(dev) && HAS_PCH_CPT(dev)))
+				cpt_enable_hotplug_irq(dev);
+			else if (HAS_PCH_SPLIT(dev))  /* ! HAS_PCH_CPT(dev) */
+				ibx_enable_hotplug_irq(dev);
+			else if (IS_VALLEYVIEW(dev))
+				valleyview_enable_hotplug_irq(dev);
+			else if (INTEL_INFO(dev)->gen == 3)
+				i915_enable_hotplug_irq(dev);
+			else
+				i965_enable_hotplug_irq(dev);
+		}
+	}
+	spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
 }
 
 void intel_irq_init(struct drm_device *dev)
@@ -2798,6 +2848,8 @@ void intel_irq_init(struct drm_device *dev)
 	INIT_WORK(&dev_priv->error_work, i915_error_work_func);
 	INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
 	INIT_WORK(&dev_priv->l3_parity.error_work, ivybridge_parity_work);
+	setup_timer(&dev_priv->hotplug_reenable_timer, i915_reenable_hotplug_timer_func,
+		    (unsigned long) dev_priv);
 
 	dev->driver->get_vblank_counter = i915_get_vblank_counter;
 	dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 2d003d9..1ca5295 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -197,7 +197,11 @@ struct intel_connector {
 	/* bookkeeping for irq storm detection */
 	int hpd_cnt;
 	unsigned long last_hpd_jiffies;
-	bool hpd_mark_disabled;
+	enum {
+		HPD_ENABLED = 0,
+		HPD_MARKED_DISABLED,
+		HPD_DISABLED,
+	} hpd_mark_disabled;
 };
 
 struct intel_crtc {
-- 
1.7.7



[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux