[Intel-gfx] [PATCH v4] drm/i915: Add Reenable Timer to turn Hotplug Detection back on (v4)

2013-04-11 Thread Egbert Eich
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.

v2: Reordered drm_i915_private: moved hotplug_reenable_timer to hpd state 
tracker.
v3: Clarified loop start value,
Removed superfluous test for Ivybridge and Haswell,
Restructured loop to avoid deep nesting (all suggested by Ville Syrjälä)
v4: Fixed two bugs pointed out by Jani Nikula.

Signed-off-by: Egbert Eich e...@suse.de
---
 drivers/gpu/drm/i915/i915_drv.h |  1 +
 drivers/gpu/drm/i915/i915_irq.c | 50 +
 2 files changed, 51 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 83fc1a6..195b9fe 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -938,6 +938,7 @@ typedef struct drm_i915_private {
HPD_MARK_DISABLED = 2
} hpd_mark;
} hpd_stats[HPD_NUM_PINS];
+   struct timer_list hotplug_reenable_timer;
 
int num_pch_pll;
int num_plane;
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 834c717..f60c643 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -337,6 +337,8 @@ static int i915_get_vblank_timestamp(struct drm_device 
*dev, int pipe,
 /*
  * Handle hotplug events outside the interrupt handler proper.
  */
+#define I915_REENABLE_HOTPLUG_DELAY (2*60*1000)
+
 static void i915_hotplug_work_func(struct work_struct *work)
 {
drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
@@ -377,7 +379,10 @@ static void i915_hotplug_work_func(struct work_struct 
*work)
  * some connectors */
if (hpd_disabled) {
drm_kms_helper_poll_enable(dev);
+   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(intel_encoder, mode_config-encoder_list, 
base.head)
@@ -2352,6 +2357,8 @@ static void valleyview_irq_uninstall(struct drm_device 
*dev)
if (!dev_priv)
return;
 
+   del_timer_sync(dev_priv-hotplug_reenable_timer);
+
for_each_pipe(pipe)
I915_WRITE(PIPESTAT(pipe), 0x);
 
@@ -2373,6 +2380,8 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
if (!dev_priv)
return;
 
+   del_timer_sync(dev_priv-hotplug_reenable_timer);
+
I915_WRITE(HWSTAM, 0x);
 
I915_WRITE(DEIMR, 0x);
@@ -2749,6 +2758,8 @@ static void i915_irq_uninstall(struct drm_device * dev)
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev-dev_private;
int pipe;
 
+   del_timer_sync(dev_priv-hotplug_reenable_timer);
+
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
@@ -2993,6 +3004,8 @@ static void i965_irq_uninstall(struct drm_device * dev)
if (!dev_priv)
return;
 
+   del_timer_sync(dev_priv-hotplug_reenable_timer);
+
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
 
@@ -3008,6 +3021,41 @@ static void i965_irq_uninstall(struct drm_device * dev)
I915_WRITE(IIR, I915_READ(IIR));
 }
 
+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;
+   unsigned long irqflags;
+   int i;
+
+   spin_lock_irqsave(dev_priv-irq_lock, irqflags);
+   for (i = (HPD_NONE + 1); i  HPD_NUM_PINS; i++) {
+   struct drm_connector *connector;
+
+   if (dev_priv-hpd_stats[i].hpd_mark != HPD_DISABLED)
+   continue;
+
+   dev_priv-hpd_stats[i].hpd_mark = HPD_ENABLED;
+
+   list_for_each_entry(connector, mode_config-connector_list, 
head) {
+   struct intel_connector *intel_connector = 
to_intel_connector(connector);
+
+   if (intel_connector-encoder-hpd_pin == i) {
+   if (connector-polled != 
intel_connector-polled)
+   DRM_DEBUG_DRIVER(Reenabling HPD on 
connector %s\n,
+
drm_get_connector_name(connector));
+   connector-polled = 

Re: [Intel-gfx] [PATCH v4] drm/i915: Add Reenable Timer to turn Hotplug Detection back on (v4)

2013-04-11 Thread Jani Nikula
On Thu, 11 Apr 2013, Egbert Eich e...@suse.de wrote:
 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.

 v2: Reordered drm_i915_private: moved hotplug_reenable_timer to hpd state 
 tracker.
 v3: Clarified loop start value,
 Removed superfluous test for Ivybridge and Haswell,
 Restructured loop to avoid deep nesting (all suggested by Ville Syrjälä)
 v4: Fixed two bugs pointed out by Jani Nikula.


Reviewed-by: Jani Nikula jani.nik...@intel.com

 Signed-off-by: Egbert Eich e...@suse.de
 ---
  drivers/gpu/drm/i915/i915_drv.h |  1 +
  drivers/gpu/drm/i915/i915_irq.c | 50 
 +
  2 files changed, 51 insertions(+)

 diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
 index 83fc1a6..195b9fe 100644
 --- a/drivers/gpu/drm/i915/i915_drv.h
 +++ b/drivers/gpu/drm/i915/i915_drv.h
 @@ -938,6 +938,7 @@ typedef struct drm_i915_private {
   HPD_MARK_DISABLED = 2
   } hpd_mark;
   } hpd_stats[HPD_NUM_PINS];
 + struct timer_list hotplug_reenable_timer;
  
   int num_pch_pll;
   int num_plane;
 diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
 index 834c717..f60c643 100644
 --- a/drivers/gpu/drm/i915/i915_irq.c
 +++ b/drivers/gpu/drm/i915/i915_irq.c
 @@ -337,6 +337,8 @@ static int i915_get_vblank_timestamp(struct drm_device 
 *dev, int pipe,
  /*
   * Handle hotplug events outside the interrupt handler proper.
   */
 +#define I915_REENABLE_HOTPLUG_DELAY (2*60*1000)
 +
  static void i915_hotplug_work_func(struct work_struct *work)
  {
   drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
 @@ -377,7 +379,10 @@ static void i915_hotplug_work_func(struct work_struct 
 *work)
 * some connectors */
   if (hpd_disabled) {
   drm_kms_helper_poll_enable(dev);
 + 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(intel_encoder, mode_config-encoder_list, 
 base.head)
 @@ -2352,6 +2357,8 @@ static void valleyview_irq_uninstall(struct drm_device 
 *dev)
   if (!dev_priv)
   return;
  
 + del_timer_sync(dev_priv-hotplug_reenable_timer);
 +
   for_each_pipe(pipe)
   I915_WRITE(PIPESTAT(pipe), 0x);
  
 @@ -2373,6 +2380,8 @@ static void ironlake_irq_uninstall(struct drm_device 
 *dev)
   if (!dev_priv)
   return;
  
 + del_timer_sync(dev_priv-hotplug_reenable_timer);
 +
   I915_WRITE(HWSTAM, 0x);
  
   I915_WRITE(DEIMR, 0x);
 @@ -2749,6 +2758,8 @@ static void i915_irq_uninstall(struct drm_device * dev)
   drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev-dev_private;
   int pipe;
  
 + del_timer_sync(dev_priv-hotplug_reenable_timer);
 +
   if (I915_HAS_HOTPLUG(dev)) {
   I915_WRITE(PORT_HOTPLUG_EN, 0);
   I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
 @@ -2993,6 +3004,8 @@ static void i965_irq_uninstall(struct drm_device * dev)
   if (!dev_priv)
   return;
  
 + del_timer_sync(dev_priv-hotplug_reenable_timer);
 +
   I915_WRITE(PORT_HOTPLUG_EN, 0);
   I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
  
 @@ -3008,6 +3021,41 @@ static void i965_irq_uninstall(struct drm_device * dev)
   I915_WRITE(IIR, I915_READ(IIR));
  }
  
 +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;
 + unsigned long irqflags;
 + int i;
 +
 + spin_lock_irqsave(dev_priv-irq_lock, irqflags);
 + for (i = (HPD_NONE + 1); i  HPD_NUM_PINS; i++) {
 + struct drm_connector *connector;
 +
 + if (dev_priv-hpd_stats[i].hpd_mark != HPD_DISABLED)
 + continue;
 +
 + dev_priv-hpd_stats[i].hpd_mark = HPD_ENABLED;
 +
 + list_for_each_entry(connector, mode_config-connector_list, 
 head) {
 + struct intel_connector *intel_connector = 
 to_intel_connector(connector);
 +
 + if (intel_connector-encoder-hpd_pin == i) {
 + if (connector-polled != 
 intel_connector-polled)
 + DRM_DEBUG_DRIVER(Reenabling HPD on 
 connector %s\n,
 +