[Intel-gfx] [PATCH v3] drm/i915: Disable HPD interrupt on pin when irq storm is detected (v3)

2013-04-11 Thread Egbert Eich
This patch disables hotplug interrupts if an 'interrupt storm'
has been detected.
Noise on the interrupt line renders the hotplug interrupt useless:
each hotplug event causes the devices to be rescanned which will
will only increase the system load.
Thus disable the hotplug interrupts and fall back to periodic
device polling.

v2: Fixed cleanup typo.
v3: Fixed format issues, clarified a variable name,
changed pr_warn() to DRM_INFO() as suggested by
Jani Nikula jani.nik...@linux.intel.com.

Signed-off-by: Egbert Eich e...@suse.de
---
 drivers/gpu/drm/i915/i915_irq.c | 75 +++--
 1 file changed, 58 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index a3f1ac4..834c717 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -88,7 +88,8 @@ static const u32 hpd_status_i915[] = { /* i915 and valleyview 
are the same */
[HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
 };
 
-
+static void ibx_hpd_irq_setup(struct drm_device *dev);
+static void i915_hpd_irq_setup(struct drm_device *dev);
 
 /* For display hotplug interrupt */
 static void
@@ -342,7 +343,11 @@ static void i915_hotplug_work_func(struct work_struct 
*work)
hotplug_work);
struct drm_device *dev = dev_priv-dev;
struct drm_mode_config *mode_config = dev-mode_config;
-   struct intel_encoder *encoder;
+   struct intel_connector *intel_connector;
+   struct intel_encoder *intel_encoder;
+   struct drm_connector *connector;
+   unsigned long irqflags;
+   bool hpd_disabled = false;
 
/* HPD irq before everything is fully set up. */
if (!dev_priv-enable_hotplug_processing)
@@ -351,9 +356,33 @@ static void i915_hotplug_work_func(struct work_struct 
*work)
mutex_lock(mode_config-mutex);
DRM_DEBUG_KMS(running encoder hotplug functions\n);
 
-   list_for_each_entry(encoder, mode_config-encoder_list, base.head)
-   if (encoder-hot_plug)
-   encoder-hot_plug(encoder);
+   spin_lock_irqsave(dev_priv-irq_lock, irqflags);
+   list_for_each_entry(connector, mode_config-connector_list, head) {
+   intel_connector = to_intel_connector(connector);
+   intel_encoder = intel_connector-encoder;
+   if (intel_encoder-hpd_pin  HPD_NONE 
+   dev_priv-hpd_stats[intel_encoder-hpd_pin].hpd_mark == 
HPD_MARK_DISABLED 
+   connector-polled == DRM_CONNECTOR_POLL_HPD) {
+   DRM_INFO(HPD interrupt storm detected on connector %s: 

+switching from hotplug detection to 
polling\n,
+   drm_get_connector_name(connector));
+   dev_priv-hpd_stats[intel_encoder-hpd_pin].hpd_mark = 
HPD_DISABLED;
+   connector-polled = DRM_CONNECTOR_POLL_CONNECT
+   | DRM_CONNECTOR_POLL_DISCONNECT;
+   hpd_disabled = true;
+   }
+   }
+/* if there were no outputs to poll, poll was disabled,
+ * therefore make sure it's enabled when disabling HPD on
+ * some connectors */
+   if (hpd_disabled) {
+   drm_kms_helper_poll_enable(dev);
+   }
+   spin_unlock_irqrestore(dev_priv-irq_lock, irqflags);
+
+   list_for_each_entry(intel_encoder, mode_config-encoder_list, 
base.head)
+   if (intel_encoder-hot_plug)
+   intel_encoder-hot_plug(intel_encoder);
 
mutex_unlock(mode_config-mutex);
 
@@ -584,13 +613,14 @@ static void gen6_queue_rps_work(struct drm_i915_private 
*dev_priv,
 
 #define HPD_STORM_DETECT_PERIOD 1000
 
-static inline void hotplug_irq_storm_detect(struct drm_device *dev,
+static inline bool hotplug_irq_storm_detect(struct drm_device *dev,
u32 hotplug_trigger,
const u32 *hpd)
 {
drm_i915_private_t *dev_priv = dev-dev_private;
unsigned long irqflags;
int i;
+   bool ret = false;
 
spin_lock_irqsave(dev_priv-irq_lock, irqflags);
 
@@ -605,12 +635,15 @@ static inline void hotplug_irq_storm_detect(struct 
drm_device *dev,
} else if (dev_priv-hpd_stats[i].hpd_cnt  5) {
dev_priv-hpd_stats[i].hpd_mark = 
HPD_MARK_DISABLED;
DRM_DEBUG_KMS(HPD interrupt storm detected on  
PIN %d\n, i);
+   ret = true;
} else
dev_priv-hpd_stats[i].hpd_cnt++;
}
}
 
spin_unlock_irqrestore(dev_priv-irq_lock, irqflags);
+
+   return ret;
 }
 
 static void gmbus_irq_handler(struct drm_device *dev)
@@ -686,7 +719,8 @@ static irqreturn_t valleyview_irq_handler(int irq, void 

Re: [Intel-gfx] [PATCH v3] drm/i915: Disable HPD interrupt on pin when irq storm is detected (v3)

2013-04-11 Thread Jani Nikula
On Thu, 11 Apr 2013, Egbert Eich e...@suse.de wrote:
 This patch disables hotplug interrupts if an 'interrupt storm'
 has been detected.
 Noise on the interrupt line renders the hotplug interrupt useless:
 each hotplug event causes the devices to be rescanned which will
 will only increase the system load.
 Thus disable the hotplug interrupts and fall back to periodic
 device polling.

 v2: Fixed cleanup typo.
 v3: Fixed format issues, clarified a variable name,
 changed pr_warn() to DRM_INFO() as suggested by
 Jani Nikula jani.nik...@linux.intel.com.

 Signed-off-by: Egbert Eich e...@suse.de
 ---
  drivers/gpu/drm/i915/i915_irq.c | 75 
 +++--
  1 file changed, 58 insertions(+), 17 deletions(-)

 diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
 index a3f1ac4..834c717 100644
 --- a/drivers/gpu/drm/i915/i915_irq.c
 +++ b/drivers/gpu/drm/i915/i915_irq.c
 @@ -88,7 +88,8 @@ static const u32 hpd_status_i915[] = { /* i915 and 
 valleyview are the same */
   [HPD_PORT_D] = PORTD_HOTPLUG_INT_STATUS
  };
  
 -
 +static void ibx_hpd_irq_setup(struct drm_device *dev);
 +static void i915_hpd_irq_setup(struct drm_device *dev);
  
  /* For display hotplug interrupt */
  static void
 @@ -342,7 +343,11 @@ static void i915_hotplug_work_func(struct work_struct 
 *work)
   hotplug_work);
   struct drm_device *dev = dev_priv-dev;
   struct drm_mode_config *mode_config = dev-mode_config;
 - struct intel_encoder *encoder;
 + struct intel_connector *intel_connector;
 + struct intel_encoder *intel_encoder;
 + struct drm_connector *connector;
 + unsigned long irqflags;
 + bool hpd_disabled = false;
  
   /* HPD irq before everything is fully set up. */
   if (!dev_priv-enable_hotplug_processing)
 @@ -351,9 +356,33 @@ static void i915_hotplug_work_func(struct work_struct 
 *work)
   mutex_lock(mode_config-mutex);
   DRM_DEBUG_KMS(running encoder hotplug functions\n);
  
 - list_for_each_entry(encoder, mode_config-encoder_list, base.head)
 - if (encoder-hot_plug)
 - encoder-hot_plug(encoder);
 + spin_lock_irqsave(dev_priv-irq_lock, irqflags);
 + list_for_each_entry(connector, mode_config-connector_list, head) {
 + intel_connector = to_intel_connector(connector);
 + intel_encoder = intel_connector-encoder;
 + if (intel_encoder-hpd_pin  HPD_NONE 
 + dev_priv-hpd_stats[intel_encoder-hpd_pin].hpd_mark == 
 HPD_MARK_DISABLED 
 + connector-polled == DRM_CONNECTOR_POLL_HPD) {
 + DRM_INFO(HPD interrupt storm detected on connector %s: 
 
 +  switching from hotplug detection to 
 polling\n,
 + drm_get_connector_name(connector));
 + dev_priv-hpd_stats[intel_encoder-hpd_pin].hpd_mark = 
 HPD_DISABLED;
 + connector-polled = DRM_CONNECTOR_POLL_CONNECT
 + | DRM_CONNECTOR_POLL_DISCONNECT;
 + hpd_disabled = true;
 + }
 + }
 +  /* if there were no outputs to poll, poll was disabled,
 +   * therefore make sure it's enabled when disabling HPD on
 +   * some connectors */
 + if (hpd_disabled) {
 + drm_kms_helper_poll_enable(dev);
 + }
 + spin_unlock_irqrestore(dev_priv-irq_lock, irqflags);
 +
 + list_for_each_entry(intel_encoder, mode_config-encoder_list, 
 base.head)
 + if (intel_encoder-hot_plug)
 + intel_encoder-hot_plug(intel_encoder);
  
   mutex_unlock(mode_config-mutex);
  
 @@ -584,13 +613,14 @@ static void gen6_queue_rps_work(struct drm_i915_private 
 *dev_priv,
  
  #define HPD_STORM_DETECT_PERIOD 1000
  
 -static inline void hotplug_irq_storm_detect(struct drm_device *dev,
 +static inline bool hotplug_irq_storm_detect(struct drm_device *dev,
   u32 hotplug_trigger,
   const u32 *hpd)
  {
   drm_i915_private_t *dev_priv = dev-dev_private;
   unsigned long irqflags;
   int i;
 + bool ret = false;
  
   spin_lock_irqsave(dev_priv-irq_lock, irqflags);
  
 @@ -605,12 +635,15 @@ static inline void hotplug_irq_storm_detect(struct 
 drm_device *dev,
   } else if (dev_priv-hpd_stats[i].hpd_cnt  5) {
   dev_priv-hpd_stats[i].hpd_mark = 
 HPD_MARK_DISABLED;
   DRM_DEBUG_KMS(HPD interrupt storm detected on  
 PIN %d\n, i);
 + ret = true;
   } else
   dev_priv-hpd_stats[i].hpd_cnt++;
   }
   }
  
   spin_unlock_irqrestore(dev_priv-irq_lock, irqflags);
 +
 + return ret;
  }
  
  static void gmbus_irq_handler(struct drm_device *dev)
 @@ -686,7 +719,8 @@