Add TV out hotplug detection
authorJesse Barnes <jbarnes@nietzche.virtuousgeek.org>
Wed, 9 Apr 2008 21:12:56 +0000 (14:12 -0700)
committerJesse Barnes <jbarnes@nietzche.virtuousgeek.org>
Wed, 9 Apr 2008 21:13:38 +0000 (14:13 -0700)
Doesn't yet work on my i915 test machine, but most of the necessary bits
should be there.

shared-core/i915_drv.h
shared-core/i915_irq.c

index 64faac9..e32c36f 100644 (file)
@@ -554,7 +554,9 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 
 #define I915_VBLANK_INTERRUPT_ENABLE   (1UL<<17)
 #define I915_HOTPLUG_INTERRUPT_ENABLE  (1UL<<26)
+#define I915_HOTPLUG_TV_INTERRUPT_ENABLE (1UL<<18)
 #define I915_HOTPLUG_CLEAR             (1UL<<10)
+#define I915_HOTPLUG_TV_CLEAR          (1UL<<2)
 #define I915_VBLANK_CLEAR              (1UL<<1)
 
 /*
@@ -669,6 +671,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 #define I915_LEGACY_BLC_EVENT_STATUS           (1UL<<6)
 #define I915_ODD_FIELD_INTERRUPT_STATUS                (1UL<<5)
 #define I915_EVEN_FIELD_INTERRUPT_STATUS       (1UL<<4)
+#define I915_HOTPLUG_TV_INTERRUPT_STATUS       (1UL<<2)
 #define I915_START_VBLANK_INTERRUPT_STATUS     (1UL<<2)        /* 965 or later */
 #define I915_VBLANK_INTERRUPT_STATUS           (1UL<<1)
 #define I915_OVERLAY_UPDATED_STATUS            (1UL<<0)
index abd8a7d..8f136c8 100644 (file)
@@ -424,11 +424,41 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane)
 #define HOTPLUG_CMD_CRT_DIS 2
 #define HOTPLUG_CMD_SDVOB 4
 #define HOTPLUG_CMD_SDVOC 8
+#define HOTPLUG_CMD_TV 16
 
 static struct drm_device *hotplug_dev;
 static int hotplug_cmd = 0;
 static spinlock_t hotplug_lock = SPIN_LOCK_UNLOCKED;
 
+static void i915_hotplug_tv(struct drm_device *dev)
+{
+       struct drm_output *output;
+       struct intel_output *iout;
+       enum drm_output_status status;
+
+       mutex_lock(&dev->mode_config.mutex);
+
+       /* find the crt output */
+       list_for_each_entry(output, &dev->mode_config.output_list, head) {
+               iout = output->driver_private;
+               if (iout->type == INTEL_OUTPUT_TVOUT)
+                       break;
+               else
+                       iout = 0;
+       }
+
+       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);
+
+unlock:
+       mutex_unlock(&dev->mode_config.mutex);
+}
+
 static void i915_hotplug_crt(struct drm_device *dev, bool isconnected)
 {
        struct drm_output *output;
@@ -493,8 +523,10 @@ static void i915_hotplug_work_func(struct work_struct *work)
        int crtDis;
        int sdvoB;
        int sdvoC;
+       int tv;
 
        spin_lock(&hotplug_lock);
+       tv = hotplug_cmd & HOTPLUG_CMD_TV;
        crt = hotplug_cmd & HOTPLUG_CMD_CRT;
        crtDis = hotplug_cmd & HOTPLUG_CMD_CRT_DIS;
        sdvoB = hotplug_cmd & HOTPLUG_CMD_SDVOB;
@@ -502,6 +534,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
        hotplug_cmd = 0;
        spin_unlock(&hotplug_lock);
 
+       if (tv)
+               i915_hotplug_tv(dev);
        if (crt)
                i915_hotplug_crt(dev, true);
        if (crtDis)
@@ -527,6 +561,14 @@ static int i915_run_hotplug_tasklet(struct drm_device *dev, uint32_t stat)
 
        hotplug_dev = dev;
 
+       if (stat & TV_HOTPLUG_INT_STATUS) {
+               DRM_DEBUG("TV event\n");
+
+               spin_lock(&hotplug_lock);
+               hotplug_cmd |= HOTPLUG_CMD_TV;
+               spin_unlock(&hotplug_lock);
+       }
+
        if (stat & CRT_HOTPLUG_INT_STATUS) {
                DRM_DEBUG("CRT event\n");
 
@@ -584,12 +626,14 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
        DRM_DEBUG("flag=%08x\n", iir);
 #endif
        if (iir == 0) {
+#if 0
                DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n",
                           iir,
                           I915_READ(I915REG_INT_MASK_R),
                           I915_READ(I915REG_INT_ENABLE_R),
                           I915_READ(I915REG_PIPEASTAT),
                           I915_READ(I915REG_PIPEBSTAT));
+#endif
                return IRQ_NONE;
        }
 
@@ -607,7 +651,8 @@ 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)
+               if ((pipea_stats & I915_HOTPLUG_INTERRUPT_STATUS) ||
+                   (pipea_stats & I915_HOTPLUG_TV_INTERRUPT_STATUS))
                        hotplug = 1;
 
                I915_WRITE(I915REG_PIPEASTAT, pipea_stats);
@@ -656,8 +701,11 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
                DRM_INFO("Hotplug event received\n");
 
                if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev)) {
-                       temp2 |= SDVOB_HOTPLUG_INT_STATUS |
-                                SDVOC_HOTPLUG_INT_STATUS;
+                       if (pipea_stats & I915_HOTPLUG_INTERRUPT_STATUS)
+                               temp2 |= SDVOB_HOTPLUG_INT_STATUS |
+                                       SDVOC_HOTPLUG_INT_STATUS;
+                       if (pipea_stats & I915_HOTPLUG_TV_INTERRUPT_STATUS)
+                               temp2 |= TV_HOTPLUG_INT_STATUS;
                } else {
                        temp2 = I915_READ(PORT_HOTPLUG_STAT);
 
@@ -898,7 +946,11 @@ void i915_enable_interrupt (struct drm_device *dev)
                        dev_priv->irq_enable_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT;
 
                /* Enable global interrupts for hotplug - not a pipeA event */
-               I915_WRITE(I915REG_PIPEASTAT, I915_READ(I915REG_PIPEASTAT) | I915_HOTPLUG_INTERRUPT_ENABLE | I915_HOTPLUG_CLEAR);
+               I915_WRITE(I915REG_PIPEASTAT, I915_READ(I915REG_PIPEASTAT) |
+                          I915_HOTPLUG_INTERRUPT_ENABLE |
+                          I915_HOTPLUG_TV_INTERRUPT_ENABLE |
+                          I915_HOTPLUG_TV_CLEAR |
+                          I915_HOTPLUG_CLEAR);
        }
 
        if (dev_priv->irq_enable_reg & (I915_DISPLAY_PORT_INTERRUPT | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)) {