drm/i915: implement ibx_hpd_irq_setup

This fixes a regression introduced in

commit e5868a318d
Author: Egbert Eich <eich@suse.de>
Date:   Thu Feb 28 04:17:12 2013 -0500

    DRM/i915: Convert HPD interrupts to make use of HPD pin assignment in encode

Due to the irq setup rework in 3.9, see

commit 20afbda209
Author: Daniel Vetter <daniel.vetter@ffwll.ch>
Date:   Tue Dec 11 14:05:07 2012 +0100

    drm/i915: Fixup hpd irq register setup ordering

Egbert Eich's hpd rework blows up on pch-split platforms - it walks
the encoder list before that has been set up completely. The new init
sequence is:

1. irq enabling
2. modeset init
3. hpd setup

We need to move around the ibx setup a bit to fix this.

Ville Syrjälä pointed out in his review that we can't touch SDEIER
after the interrupt handler is set up, since that'll race with Paulo
Zanoni's PCH interrupt race fix:

commit 44498aea29
Author: Paulo Zanoni <paulo.r.zanoni@intel.com>
Date:   Fri Feb 22 17:05:28 2013 -0300

    drm/i915: also disable south interrupts when handling them

We fix that by unconditionally enabling all interrupts in SDEIER, but
masking them as-needed in SDEIMR. Since only the single-threaded
setup/teardown (or suspend/resume) code touches that, no further
locking is required.

While at it also simplify the mask handling - we start out with all
interrupts cleared in the postinstall hook, and never enable a hpd
interrupt before hpd_irq_setup is called.

And finally, for consistency rename the ibx hpd setup function to
ibx_hpd_irq_setup.

v2: Fix race around SDEIER writes (Ville).

v3: Remove the superflous posting read for SDEIER, spotted by Ville.

Ville also wondered whether we shouldn't clear SDEIIR, since now
SDE interrupts are enabled before we have an irq handler installed.
But the master interrupt control bit in DEIER is still cleared, so we
should be fine.

Cc: Egbert Eich <eich@suse.de>
Cc: Jesse Barnes <jbarnes@virtuousgeek.org>
Cc: Paulo Zanoni <paulo.r.zanoni@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=62798
Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
Daniel Vetter 2013-03-27 15:55:01 +01:00
parent e5868a318d
commit 82a28bcf56

View File

@ -2028,7 +2028,13 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
/* south display irq */ /* south display irq */
I915_WRITE(SDEIMR, 0xffffffff); I915_WRITE(SDEIMR, 0xffffffff);
I915_WRITE(SDEIER, 0x0); /*
* SDEIER is also touched by the interrupt handler to work around missed
* PCH interrupts. Hence we can't update it after the interrupt handler
* is enabled - instead we unconditionally enable all PCH interrupt
* sources here, but then only unmask them as needed with SDEIMR.
*/
I915_WRITE(SDEIER, 0xffffffff);
POSTING_READ(SDEIER); POSTING_READ(SDEIER);
} }
@ -2064,18 +2070,30 @@ static void valleyview_irq_preinstall(struct drm_device *dev)
POSTING_READ(VLV_IER); POSTING_READ(VLV_IER);
} }
static void ibx_hpd_irq_setup(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *intel_encoder;
u32 mask = ~I915_READ(SDEIMR);
u32 hotplug;
if (HAS_PCH_IBX(dev)) {
list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
mask |= hpd_ibx[intel_encoder->hpd_pin];
} else {
list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
mask |= hpd_cpt[intel_encoder->hpd_pin];
}
I915_WRITE(SDEIMR, ~mask);
/* /*
* Enable digital hotplug on the PCH, and configure the DP short pulse * Enable digital hotplug on the PCH, and configure the DP short pulse
* duration to 2ms (which is the minimum in the Display Port spec) * duration to 2ms (which is the minimum in the Display Port spec)
* *
* This register is the same on all known PCH chips. * This register is the same on all known PCH chips.
*/ */
static void ibx_enable_hotplug(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 hotplug;
hotplug = I915_READ(PCH_PORT_HOTPLUG); hotplug = I915_READ(PCH_PORT_HOTPLUG);
hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK); hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK);
hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms; hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms;
@ -2087,27 +2105,14 @@ static void ibx_enable_hotplug(struct drm_device *dev)
static void ibx_irq_postinstall(struct drm_device *dev) static void ibx_irq_postinstall(struct drm_device *dev)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
struct drm_mode_config *mode_config = &dev->mode_config; u32 mask;
struct intel_encoder *intel_encoder;
u32 mask = I915_READ(SDEIER);
if (HAS_PCH_IBX(dev)) { if (HAS_PCH_IBX(dev))
mask &= ~SDE_HOTPLUG_MASK; mask = SDE_GMBUS | SDE_AUX_MASK;
list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head) else
mask |= hpd_ibx[intel_encoder->hpd_pin]; mask = SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
mask |= SDE_GMBUS | SDE_AUX_MASK;
} else {
mask &= ~SDE_HOTPLUG_MASK_CPT;
list_for_each_entry(intel_encoder, &mode_config->encoder_list, base.head)
mask |= hpd_cpt[intel_encoder->hpd_pin];
mask |= SDE_GMBUS_CPT | SDE_AUX_MASK_CPT;
}
I915_WRITE(SDEIIR, I915_READ(SDEIIR)); I915_WRITE(SDEIIR, I915_READ(SDEIIR));
I915_WRITE(SDEIMR, ~mask); I915_WRITE(SDEIMR, ~mask);
I915_WRITE(SDEIER, mask);
POSTING_READ(SDEIER);
ibx_enable_hotplug(dev);
} }
static int ironlake_irq_postinstall(struct drm_device *dev) static int ironlake_irq_postinstall(struct drm_device *dev)
@ -2977,6 +2982,7 @@ void intel_irq_init(struct drm_device *dev)
dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->irq_uninstall = ironlake_irq_uninstall;
dev->driver->enable_vblank = ivybridge_enable_vblank; dev->driver->enable_vblank = ivybridge_enable_vblank;
dev->driver->disable_vblank = ivybridge_disable_vblank; dev->driver->disable_vblank = ivybridge_disable_vblank;
dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
} else if (HAS_PCH_SPLIT(dev)) { } else if (HAS_PCH_SPLIT(dev)) {
dev->driver->irq_handler = ironlake_irq_handler; dev->driver->irq_handler = ironlake_irq_handler;
dev->driver->irq_preinstall = ironlake_irq_preinstall; dev->driver->irq_preinstall = ironlake_irq_preinstall;
@ -2984,6 +2990,7 @@ void intel_irq_init(struct drm_device *dev)
dev->driver->irq_uninstall = ironlake_irq_uninstall; dev->driver->irq_uninstall = ironlake_irq_uninstall;
dev->driver->enable_vblank = ironlake_enable_vblank; dev->driver->enable_vblank = ironlake_enable_vblank;
dev->driver->disable_vblank = ironlake_disable_vblank; dev->driver->disable_vblank = ironlake_disable_vblank;
dev_priv->display.hpd_irq_setup = ibx_hpd_irq_setup;
} else { } else {
if (INTEL_INFO(dev)->gen == 2) { if (INTEL_INFO(dev)->gen == 2) {
dev->driver->irq_preinstall = i8xx_irq_preinstall; dev->driver->irq_preinstall = i8xx_irq_preinstall;