OSDN Git Service

i915: TV hotplug fixes
authorJesse Barnes <jbarnes@nietzche.virtuousgeek.org>
Mon, 12 May 2008 22:47:19 +0000 (15:47 -0700)
committerJesse Barnes <jbarnes@nietzche.virtuousgeek.org>
Mon, 12 May 2008 22:47:19 +0000 (15:47 -0700)
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
shared-core/i915_irq.c

index ba134d6..865e27b 100644 (file)
@@ -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) {
index 0ee0c44..4aef568 100644 (file)
@@ -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 ???? */
                }