From d32ce7f621c0d8e42cdf88ce6f1d15638a3d34b7 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 12 May 2008 15:47:19 -0700 Subject: i915: TV hotplug fixes In order to avoid recursive ->detect->interrupt->detect->interrupt->... we need to disable TV hotplug interrupts in intel_tv.c:intel_tv_detect_type. We also need to enable the TV interrupt detection and hotplug sequence properly in i915_irq.c. --- linux-core/intel_tv.c | 27 +++++++++++++++++++++++---- shared-core/i915_irq.c | 17 +++++++++++++---- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/linux-core/intel_tv.c b/linux-core/intel_tv.c index ba134d62..865e27b9 100644 --- a/linux-core/intel_tv.c +++ b/linux-core/intel_tv.c @@ -1360,11 +1360,21 @@ intel_tv_detect_type (struct drm_crtc *crtc, struct drm_output *output) struct drm_device *dev = output->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_output *intel_output = output->driver_private; + u32 pipeastat, pipeastat_save; u32 tv_ctl, save_tv_ctl; u32 tv_dac, save_tv_dac; int type = ConnectorUnknown; tv_dac = I915_READ(TV_DAC); + + /* Disable TV interrupts around load detect or we'll recurse */ + pipeastat = I915_READ(I915REG_PIPEASTAT); + pipeastat_save = pipeastat; + pipeastat &= ~I915_HOTPLUG_INTERRUPT_ENABLE; + pipeastat &= ~I915_HOTPLUG_TV_INTERRUPT_ENABLE; + I915_WRITE(I915REG_PIPEASTAT, pipeastat | I915_HOTPLUG_TV_CLEAR | + I915_HOTPLUG_CLEAR); + /* * Detect TV by polling) */ @@ -1412,6 +1422,10 @@ intel_tv_detect_type (struct drm_crtc *crtc, struct drm_output *output) type = -1; } + /* Restore interrupt config */ + I915_WRITE(I915REG_PIPEASTAT, pipeastat_save | I915_HOTPLUG_TV_CLEAR | + I915_HOTPLUG_CLEAR); + return type; } @@ -1434,10 +1448,15 @@ intel_tv_detect(struct drm_output *output) mode = reported_modes[0]; drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V); - crtc = intel_get_load_detect_pipe(output, &mode, &dpms_mode); - if (crtc) { - type = intel_tv_detect_type(crtc, output); - intel_release_load_detect_pipe(output, dpms_mode); + if (output->crtc) { + type = intel_tv_detect_type(output->crtc, output); + } else { + crtc = intel_get_load_detect_pipe(output, &mode, &dpms_mode); + if (crtc) { + type = intel_tv_detect_type(crtc, output); + intel_release_load_detect_pipe(output, dpms_mode); + } else + type = -1; } if (type != tv_priv->type) { diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 0ee0c444..4aef568e 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -471,7 +471,6 @@ static void i915_hotplug_tv(struct drm_device *dev) if (iout == 0) goto unlock; - /* may need to I915_WRITE(TVDAC, 1<<31) to ack the interrupt */ status = output->funcs->detect(output); drm_hotplug_stage_two(dev, output, status == output_status_connected ? 1 : 0); @@ -631,7 +630,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) struct drm_i915_master_private *master_priv; struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; u32 iir; - u32 pipea_stats, pipeb_stats; + u32 pipea_stats = 0, pipeb_stats, tvdac; int hotplug = 0; int vblank = 0; @@ -672,10 +671,17 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) } /* This is a global event, and not a pipe A event */ - if ((pipea_stats & I915_HOTPLUG_INTERRUPT_STATUS) || - (pipea_stats & I915_HOTPLUG_TV_INTERRUPT_STATUS)) + if (pipea_stats & I915_HOTPLUG_INTERRUPT_STATUS) hotplug = 1; + if (pipea_stats & I915_HOTPLUG_TV_INTERRUPT_STATUS) { + hotplug = 1; + /* Toggle hotplug detection to clear hotplug status */ + tvdac = I915_READ(TV_DAC); + I915_WRITE(TV_DAC, tvdac & ~TVDAC_STATE_CHG_EN); + I915_WRITE(TV_DAC, tvdac | TVDAC_STATE_CHG_EN); + } + I915_WRITE(I915REG_PIPEASTAT, pipea_stats); } @@ -1001,6 +1007,9 @@ void i915_enable_interrupt (struct drm_device *dev) I915_WRITE(SDVOB, I915_READ(SDVOB) | SDVO_INTERRUPT_ENABLE); I915_WRITE(SDVOC, I915_READ(SDVOC) | SDVO_INTERRUPT_ENABLE); + + /* TV */ + I915_WRITE(TV_DAC, I915_READ(TV_DAC) | TVDAC_STATE_CHG_EN); } else { /* DVO ???? */ } -- cgit v1.2.3