summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--shared-core/i915_drv.h3
-rw-r--r--shared-core/i915_irq.c60
2 files changed, 59 insertions, 4 deletions
diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h
index 64faac9b..e32c36f1 100644
--- a/shared-core/i915_drv.h
+++ b/shared-core/i915_drv.h
@@ -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)
diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c
index abd8a7d3..8f136c8f 100644
--- a/shared-core/i915_irq.c
+++ b/shared-core/i915_irq.c
@@ -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)) {