Commit 5ca58282 authored by Jesse Barnes's avatar Jesse Barnes Committed by Eric Anholt

drm/i915: add VGA hotplug support for 945+

Add VGA port hotplug detection to the i915 driver.  When KMS is enabled,
plugging in or removing a VGA cable from the VGA connector will
generate a uevent, which indicates to userspace that it should re-probe
outputs on this device (to determine modes, etc.).
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
[anholt: dropped extra PORT_HOTPLUG_STAT clear with ack from jbarnes]
Signed-off-by: default avatarEric Anholt <eric@anholt.net>
parent 4a8df458
...@@ -451,6 +451,7 @@ void drm_sysfs_hotplug_event(struct drm_device *dev) ...@@ -451,6 +451,7 @@ void drm_sysfs_hotplug_event(struct drm_device *dev)
kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); 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 * drm_sysfs_device_add - adds a class device to sysfs for a character driver
......
...@@ -1030,13 +1030,6 @@ static int i915_load_modeset_init(struct drm_device *dev) ...@@ -1030,13 +1030,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
if (ret) if (ret)
goto destroy_ringbuffer; 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. */ /* Always safe in the mode setting case. */
/* FIXME: do pre/post-mode set stuff in core KMS code */ /* FIXME: do pre/post-mode set stuff in core KMS code */
dev->vblank_disable_allowed = 1; dev->vblank_disable_allowed = 1;
......
...@@ -159,6 +159,9 @@ typedef struct drm_i915_private { ...@@ -159,6 +159,9 @@ typedef struct drm_i915_private {
u32 irq_mask_reg; u32 irq_mask_reg;
u32 pipestat[2]; u32 pipestat[2];
u32 hotplug_supported_mask;
struct work_struct hotplug_work;
int tex_lru_log_granularity; int tex_lru_log_granularity;
int allow_batchbuffer; int allow_batchbuffer;
struct mem_block *agp_heap; struct mem_block *agp_heap;
...@@ -810,6 +813,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); ...@@ -810,6 +813,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \ #define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
IS_I915GM(dev))) IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev)) #define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
#define PRIMARY_RINGBUFFER_SIZE (128*1024) #define PRIMARY_RINGBUFFER_SIZE (128*1024)
......
...@@ -48,10 +48,6 @@ ...@@ -48,10 +48,6 @@
/** Interrupts that we mask and unmask at runtime. */ /** Interrupts that we mask and unmask at runtime. */
#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) #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 |\ #define I915_PIPE_VBLANK_STATUS (PIPE_START_VBLANK_INTERRUPT_STATUS |\
PIPE_VBLANK_INTERRUPT_STATUS) PIPE_VBLANK_INTERRUPT_STATUS)
...@@ -187,6 +183,19 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) ...@@ -187,6 +183,19 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
return I915_READ(reg); return I915_READ(reg);
} }
/*
* 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) irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
{ {
struct drm_device *dev = (struct drm_device *) arg; struct drm_device *dev = (struct drm_device *) arg;
...@@ -244,6 +253,20 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) ...@@ -244,6 +253,20 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
ret = IRQ_HANDLED; ret = IRQ_HANDLED;
/* Consume port. Then clear IIR or we'll miss events */
if ((I915_HAS_HOTPLUG(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); I915_WRITE(IIR, iir);
new_iir = I915_READ(IIR); /* Flush posted writes */ new_iir = I915_READ(IIR); /* Flush posted writes */
...@@ -528,17 +551,24 @@ void i915_driver_irq_preinstall(struct drm_device * dev) ...@@ -528,17 +551,24 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
atomic_set(&dev_priv->irq_received, 0); atomic_set(&dev_priv->irq_received, 0);
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
}
I915_WRITE(HWSTAM, 0xeffe); I915_WRITE(HWSTAM, 0xeffe);
I915_WRITE(PIPEASTAT, 0); I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0); I915_WRITE(PIPEBSTAT, 0);
I915_WRITE(IMR, 0xffffffff); I915_WRITE(IMR, 0xffffffff);
I915_WRITE(IER, 0x0); I915_WRITE(IER, 0x0);
(void) I915_READ(IER); (void) I915_READ(IER);
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
} }
int i915_driver_irq_postinstall(struct drm_device *dev) int i915_driver_irq_postinstall(struct drm_device *dev)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; 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; dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
...@@ -550,13 +580,35 @@ int i915_driver_irq_postinstall(struct drm_device *dev) ...@@ -550,13 +580,35 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
dev_priv->pipestat[0] = 0; dev_priv->pipestat[0] = 0;
dev_priv->pipestat[1] = 0; dev_priv->pipestat[1] = 0;
if (I915_HAS_HOTPLUG(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 */ /* Disable pipe interrupt enables, clear pending pipe status */
I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff);
I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff);
/* Clear pending interrupt status */ /* Clear pending interrupt status */
I915_WRITE(IIR, I915_READ(IIR)); 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); I915_WRITE(IMR, dev_priv->irq_mask_reg);
(void) I915_READ(IER); (void) I915_READ(IER);
...@@ -575,6 +627,11 @@ void i915_driver_irq_uninstall(struct drm_device * dev) ...@@ -575,6 +627,11 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
dev_priv->vblank_pipe = 0; dev_priv->vblank_pipe = 0;
if (I915_HAS_HOTPLUG(dev)) {
I915_WRITE(PORT_HOTPLUG_EN, 0);
I915_WRITE(PORT_HOTPLUG_STAT, I915_READ(PORT_HOTPLUG_STAT));
}
I915_WRITE(HWSTAM, 0xffffffff); I915_WRITE(HWSTAM, 0xffffffff);
I915_WRITE(PIPEASTAT, 0); I915_WRITE(PIPEASTAT, 0);
I915_WRITE(PIPEBSTAT, 0); I915_WRITE(PIPEBSTAT, 0);
......
...@@ -651,6 +651,14 @@ ...@@ -651,6 +651,14 @@
#define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2) #define CRT_HOTPLUG_DETECT_VOLTAGE_325MV (0 << 2)
#define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2) #define CRT_HOTPLUG_DETECT_VOLTAGE_475MV (1 << 2)
#define CRT_HOTPLUG_MASK (0x3fc) /* Bits 9-2 */ #define CRT_HOTPLUG_MASK (0x3fc) /* Bits 9-2 */
#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 PORT_HOTPLUG_STAT 0x61114
......
...@@ -41,7 +41,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode) ...@@ -41,7 +41,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
temp = I915_READ(ADPA); temp = I915_READ(ADPA);
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp &= ~ADPA_DAC_ENABLE; temp |= ADPA_DAC_ENABLE;
switch(mode) { switch(mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
...@@ -158,7 +158,7 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector) ...@@ -158,7 +158,7 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
else else
tries = 1; tries = 1;
hotplug_en = I915_READ(PORT_HOTPLUG_EN); hotplug_en = I915_READ(PORT_HOTPLUG_EN);
hotplug_en &= ~(CRT_HOTPLUG_MASK); hotplug_en &= CRT_FORCE_HOTPLUG_MASK;
hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; hotplug_en |= CRT_HOTPLUG_FORCE_DETECT;
if (IS_GM45(dev)) if (IS_GM45(dev))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment