[Intel-gfx] [RFC] VGA hotplug support for i915 kms
Jesse Barnes
jbarnes at virtuousgeek.org
Thu Jan 15 23:18:46 CET 2009
On Wednesday, January 14, 2009 2:14 pm Jesse Barnes wrote:
> Here's a work in progress VGA hotplug patch that I've been testing on my
> GM45. I've only tested VGA interrupts so far, but other ports may work or
> should be easy to enable. When a hotplug interrupt is received, the i915
> interrupt handler kicks off a work queue which ends up sending a uevent.
> I've deliberately done nothing else; I figure userspace should decide what
> to do with the event (ignore it, probe outputs, set up a cloned or extended
> configuration, etc.).
>
> Thoughts or comments? The passing of the DRM device struct to the work
> function is pretty ugly at this point, and could be improved, and whether
> hotplug detection is enabled should probably be configurable since it
> depends on the outputs being powered enough to detect events.
Here's a slightly less revolting version. I removed all the global variables,
and moved to using the system wide work queue rather than an i915 specific one,
so things are much cleaner & clearer.
Comments? Userspace will still need to call getresources after receiving a
hotplug event, but I think that's better than trying to do something by default
in the kernel.
--
Jesse Barnes, Intel Open Source Technology Center
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 5aa6780..3e60996 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -461,6 +461,7 @@ void drm_sysfs_hotplug_event(struct drm_device *dev)
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp);
}
+EXPORT_SYMBOL(drm_sysfs_hotplug_event);
/**
* drm_sysfs_device_add - adds a class device to sysfs for a character driver
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 0beb084..ddca00a 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -976,13 +976,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret)
goto destroy_ringbuffer;
- /* FIXME: re-add hotplug support */
-#if 0
- ret = drm_hotplug_init(dev);
- if (ret)
- goto destroy_ringbuffer;
-#endif
-
/* Always safe in the mode setting case. */
/* FIXME: do pre/post-mode set stuff in core KMS code */
dev->vblank_disable_allowed = 1;
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 563de18..d3e7201 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -147,6 +147,9 @@ typedef struct drm_i915_private {
u32 irq_mask_reg;
u32 pipestat[2];
+ u32 hotplug_supported_mask;
+ struct work_struct hotplug_work;
+
int tex_lru_log_granularity;
int allow_batchbuffer;
struct mem_block *agp_heap;
@@ -761,6 +764,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev))
#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev))
+#define I915_HAS_PORT(dev) (IS_I945G(dev) || IS_I965G(dev))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev))
#define PRIMARY_RINGBUFFER_SIZE (128*1024)
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 6290219..bc929ea 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -48,10 +48,6 @@
/** Interrupts that we mask and unmask at runtime. */
#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT)
-/** These are all of the interrupts used by the driver */
-#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \
- I915_INTERRUPT_ENABLE_VAR)
-
#define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\
PIPE_VBLANK_INTERRUPT_STATUS)
@@ -174,6 +170,19 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
return count;
}
+/*
+ * Handle hotplug events outside the interrupt handler proper.
+ */
+static void i915_hotplug_work_func(struct work_struct *work)
+{
+ drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
+ hotplug_work);
+ struct drm_device *dev = dev_priv->dev;
+
+ /* Just fire off a uevent and let userspace tell us what to do */
+ drm_sysfs_hotplug_event(dev);
+}
+
irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *) arg;
@@ -231,6 +240,20 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
ret = IRQ_HANDLED;
+ /* Consume port. Then clear IIR or we'll miss events */
+ if ((I915_HAS_PORT(dev)) &&
+ (iir & I915_DISPLAY_PORT_INTERRUPT)) {
+ u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
+
+ DRM_DEBUG("hotplug event received, stat 0x%08x\n",
+ hotplug_status);
+ if (hotplug_status & dev_priv->hotplug_supported_mask)
+ schedule_work(&dev_priv->hotplug_work);
+
+ I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
+ I915_READ(PORT_HOTPLUG_STAT);
+ }
+
I915_WRITE(IIR, iir);
new_iir = I915_READ(IIR); /* Flush posted writes */
@@ -514,17 +537,24 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
atomic_set(&dev_priv->irq_received, 0);
+ if (I915_HAS_PORT(dev)) {
+ I915_WRITE(PORT_HOTPLUG_EN, 0);
+ I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
+ }
+
I915_WRITE(HWSTAM, 0xeffe);
I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0);
I915_WRITE(IMR, 0xffffffff);
I915_WRITE(IER, 0x0);
(void) I915_READ(IER);
+ INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
}
int i915_driver_irq_postinstall(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR;
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
@@ -536,13 +566,35 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
dev_priv->pipestat[0] = 0;
dev_priv->pipestat[1] = 0;
+ if (I915_HAS_PORT(dev)) {
+ u32 hotplug_en = I915_READ(PORT_HOTPLUG_EN);
+
+ /* Leave other bits alone */
+ hotplug_en |= HOTPLUG_EN_MASK;
+ I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
+
+ dev_priv->hotplug_supported_mask = CRT_HOTPLUG_INT_STATUS |
+ TV_HOTPLUG_INT_STATUS | SDVOC_HOTPLUG_INT_STATUS |
+ SDVOB_HOTPLUG_INT_STATUS;
+ if (IS_G4X(dev)) {
+ dev_priv->hotplug_supported_mask |=
+ HDMIB_HOTPLUG_INT_STATUS |
+ HDMIC_HOTPLUG_INT_STATUS |
+ HDMID_HOTPLUG_INT_STATUS;
+ }
+ /* Enable in IER... */
+ enable_mask |= I915_DISPLAY_PORT_INTERRUPT;
+ /* and unmask in IMR */
+ i915_enable_irq(dev_priv, I915_DISPLAY_PORT_INTERRUPT);
+ }
+
/* Disable pipe interrupt enables, clear pending pipe status */
I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff);
I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff);
/* Clear pending interrupt status */
I915_WRITE(IIR, I915_READ(IIR));
- I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
+ I915_WRITE(IER, enable_mask);
I915_WRITE(IMR, dev_priv->irq_mask_reg);
(void) I915_READ(IER);
@@ -561,6 +613,11 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
dev_priv->vblank_pipe = 0;
+ if (I915_HAS_PORT(dev)) {
+ I915_WRITE(PORT_HOTPLUG_EN, 0);
+ I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
+ }
+
I915_WRITE(HWSTAM, 0xffffffff);
I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0);
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 2731625..8f632b9 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -627,7 +627,17 @@
#define SDVOC_HOTPLUG_INT_EN (1 << 25)
#define TV_HOTPLUG_INT_EN (1 << 18)
#define CRT_HOTPLUG_INT_EN (1 << 9)
+#define CRT_HOTPLUG_ACTIVE_PERIOD_64 (1 << 8)
+#define CRT_HOTPLUG_VOLTAGE_CMP_50 (1 << 5)
#define CRT_HOTPLUG_FORCE_DETECT (1 << 3)
+#define CRT_FORCE_HOTPLUG_MASK 0xfffffe1f
+#define HOTPLUG_EN_MASK (HDMIB_HOTPLUG_INT_EN | \
+ HDMIC_HOTPLUG_INT_EN | \
+ HDMID_HOTPLUG_INT_EN | \
+ SDVOB_HOTPLUG_INT_EN | \
+ SDVOC_HOTPLUG_INT_EN | \
+ TV_HOTPLUG_INT_EN | \
+ CRT_HOTPLUG_INT_EN)
#define PORT_HOTPLUG_STAT 0x61114
#define HDMIB_HOTPLUG_INT_STATUS (1 << 29)
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index dcaed34..59d0e3e 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -41,7 +41,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
temp = I915_READ(ADPA);
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
- temp &= ~ADPA_DAC_ENABLE;
+ temp |= ADPA_DAC_ENABLE;
switch(mode) {
case DRM_MODE_DPMS_ON:
@@ -134,13 +134,18 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 temp;
-
+ bool ret = false;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
temp = I915_READ(PORT_HOTPLUG_EN);
- I915_WRITE(PORT_HOTPLUG_EN,
- temp | CRT_HOTPLUG_FORCE_DETECT | (1 << 5));
+ temp &= CRT_FORCE_HOTPLUG_MASK; /* need to set 5:8 properly for force */
+
+ /* GM45 needs extra long active period */
+ if (IS_G4X(dev))
+ temp |= CRT_HOTPLUG_ACTIVE_PERIOD_64;
+ I915_WRITE(PORT_HOTPLUG_EN, temp | CRT_HOTPLUG_FORCE_DETECT |
+ CRT_HOTPLUG_VOLTAGE_CMP_50);
do {
if (!(I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT))
@@ -148,11 +153,25 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
msleep(1);
} while (time_after(timeout, jiffies));
+ /* Need to do this twice on DevELK */
+ if (IS_G4X(dev)) {
+ timeout = jiffies + msecs_to_jiffies(1000);
+ do {
+ if (!(I915_READ(PORT_HOTPLUG_EN) &
+ CRT_HOTPLUG_FORCE_DETECT))
+ break;
+ msleep(1);
+ } while (time_after(timeout, jiffies));
+ }
+
if ((I915_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) ==
CRT_HOTPLUG_MONITOR_COLOR)
- return true;
+ ret = true;
+
+ /* Clear spurious status */
+ I915_WRITE(PORT_HOTPLUG_STAT, CRT_HOTPLUG_INT_STATUS);
- return false;
+ return ret;
}
static bool intel_crt_detect_ddc(struct drm_connector *connector)
More information about the Intel-gfx
mailing list