mirror of
https://github.com/torvalds/linux.git
synced 2024-11-02 18:21:49 +00:00
drm/i915: interrupt & vblank support for Ivy Bridge
Add new interrupt handling functions for Ivy Bridge. Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org> Reviewed-by: Keith Packard <keithp@keithp.com> Signed-off-by: Keith Packard <keithp@keithp.com>
This commit is contained in:
parent
fe100d4da1
commit
b1f14ad01a
@ -1266,7 +1266,15 @@ static int i915_load_modeset_init(struct drm_device *dev)
|
||||
|
||||
intel_modeset_gem_init(dev);
|
||||
|
||||
if (HAS_PCH_SPLIT(dev)) {
|
||||
if (IS_IVYBRIDGE(dev)) {
|
||||
/* Share pre & uninstall handlers with ILK/SNB */
|
||||
dev->driver->irq_handler = ivybridge_irq_handler;
|
||||
dev->driver->irq_preinstall = ironlake_irq_preinstall;
|
||||
dev->driver->irq_postinstall = ivybridge_irq_postinstall;
|
||||
dev->driver->irq_uninstall = ironlake_irq_uninstall;
|
||||
dev->driver->enable_vblank = ivybridge_enable_vblank;
|
||||
dev->driver->disable_vblank = ivybridge_disable_vblank;
|
||||
} else if (HAS_PCH_SPLIT(dev)) {
|
||||
dev->driver->irq_handler = ironlake_irq_handler;
|
||||
dev->driver->irq_preinstall = ironlake_irq_preinstall;
|
||||
dev->driver->irq_postinstall = ironlake_irq_postinstall;
|
||||
@ -2011,7 +2019,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
dev->driver->get_vblank_counter = i915_get_vblank_counter;
|
||||
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
|
||||
if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev)) {
|
||||
if (IS_G4X(dev) || IS_GEN5(dev) || IS_GEN6(dev) || IS_IVYBRIDGE(dev)) {
|
||||
dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
|
||||
dev->driver->get_vblank_counter = gm45_get_vblank_counter;
|
||||
}
|
||||
|
@ -1043,6 +1043,11 @@ extern void ironlake_irq_preinstall(struct drm_device *dev);
|
||||
extern int ironlake_irq_postinstall(struct drm_device *dev);
|
||||
extern void ironlake_irq_uninstall(struct drm_device *dev);
|
||||
|
||||
extern irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS);
|
||||
extern void ivybridge_irq_preinstall(struct drm_device *dev);
|
||||
extern int ivybridge_irq_postinstall(struct drm_device *dev);
|
||||
extern void ivybridge_irq_uninstall(struct drm_device *dev);
|
||||
|
||||
extern int i915_vblank_pipe_set(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
extern int i915_vblank_pipe_get(struct drm_device *dev, void *data,
|
||||
@ -1051,6 +1056,8 @@ extern int i915_enable_vblank(struct drm_device *dev, int crtc);
|
||||
extern void i915_disable_vblank(struct drm_device *dev, int crtc);
|
||||
extern int ironlake_enable_vblank(struct drm_device *dev, int crtc);
|
||||
extern void ironlake_disable_vblank(struct drm_device *dev, int crtc);
|
||||
extern int ivybridge_enable_vblank(struct drm_device *dev, int crtc);
|
||||
extern void ivybridge_disable_vblank(struct drm_device *dev, int crtc);
|
||||
extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc);
|
||||
extern u32 gm45_get_vblank_counter(struct drm_device *dev, int crtc);
|
||||
extern int i915_vblank_swap(struct drm_device *dev, void *data,
|
||||
|
@ -462,6 +462,94 @@ static void pch_irq_handler(struct drm_device *dev)
|
||||
DRM_DEBUG_DRIVER("PCH transcoder A underrun interrupt\n");
|
||||
}
|
||||
|
||||
irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS)
|
||||
{
|
||||
struct drm_device *dev = (struct drm_device *) arg;
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
int ret = IRQ_NONE;
|
||||
u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir;
|
||||
struct drm_i915_master_private *master_priv;
|
||||
|
||||
atomic_inc(&dev_priv->irq_received);
|
||||
|
||||
/* disable master interrupt before clearing iir */
|
||||
de_ier = I915_READ(DEIER);
|
||||
I915_WRITE(DEIER, de_ier & ~DE_MASTER_IRQ_CONTROL);
|
||||
POSTING_READ(DEIER);
|
||||
|
||||
de_iir = I915_READ(DEIIR);
|
||||
gt_iir = I915_READ(GTIIR);
|
||||
pch_iir = I915_READ(SDEIIR);
|
||||
pm_iir = I915_READ(GEN6_PMIIR);
|
||||
|
||||
if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 && pm_iir == 0)
|
||||
goto done;
|
||||
|
||||
ret = IRQ_HANDLED;
|
||||
|
||||
if (dev->primary->master) {
|
||||
master_priv = dev->primary->master->driver_priv;
|
||||
if (master_priv->sarea_priv)
|
||||
master_priv->sarea_priv->last_dispatch =
|
||||
READ_BREADCRUMB(dev_priv);
|
||||
}
|
||||
|
||||
if (gt_iir & (GT_USER_INTERRUPT | GT_PIPE_NOTIFY))
|
||||
notify_ring(dev, &dev_priv->ring[RCS]);
|
||||
if (gt_iir & GT_GEN6_BSD_USER_INTERRUPT)
|
||||
notify_ring(dev, &dev_priv->ring[VCS]);
|
||||
if (gt_iir & GT_BLT_USER_INTERRUPT)
|
||||
notify_ring(dev, &dev_priv->ring[BCS]);
|
||||
|
||||
if (de_iir & DE_GSE_IVB)
|
||||
intel_opregion_gse_intr(dev);
|
||||
|
||||
if (de_iir & DE_PLANEA_FLIP_DONE_IVB) {
|
||||
intel_prepare_page_flip(dev, 0);
|
||||
intel_finish_page_flip_plane(dev, 0);
|
||||
}
|
||||
|
||||
if (de_iir & DE_PLANEB_FLIP_DONE_IVB) {
|
||||
intel_prepare_page_flip(dev, 1);
|
||||
intel_finish_page_flip_plane(dev, 1);
|
||||
}
|
||||
|
||||
if (de_iir & DE_PIPEA_VBLANK_IVB)
|
||||
drm_handle_vblank(dev, 0);
|
||||
|
||||
if (de_iir & DE_PIPEB_VBLANK_IVB);
|
||||
drm_handle_vblank(dev, 1);
|
||||
|
||||
/* check event from PCH */
|
||||
if (de_iir & DE_PCH_EVENT_IVB) {
|
||||
if (pch_iir & SDE_HOTPLUG_MASK_CPT)
|
||||
queue_work(dev_priv->wq, &dev_priv->hotplug_work);
|
||||
pch_irq_handler(dev);
|
||||
}
|
||||
|
||||
if (pm_iir & GEN6_PM_DEFERRED_EVENTS) {
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&dev_priv->rps_lock, flags);
|
||||
WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n");
|
||||
I915_WRITE(GEN6_PMIMR, pm_iir);
|
||||
dev_priv->pm_iir |= pm_iir;
|
||||
spin_unlock_irqrestore(&dev_priv->rps_lock, flags);
|
||||
queue_work(dev_priv->wq, &dev_priv->rps_work);
|
||||
}
|
||||
|
||||
/* should clear PCH hotplug event before clear CPU irq */
|
||||
I915_WRITE(SDEIIR, pch_iir);
|
||||
I915_WRITE(GTIIR, gt_iir);
|
||||
I915_WRITE(DEIIR, de_iir);
|
||||
I915_WRITE(GEN6_PMIIR, pm_iir);
|
||||
|
||||
done:
|
||||
I915_WRITE(DEIER, de_ier);
|
||||
POSTING_READ(DEIER);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
|
||||
{
|
||||
struct drm_device *dev = (struct drm_device *) arg;
|
||||
@ -1406,6 +1494,22 @@ int ironlake_enable_vblank(struct drm_device *dev, int pipe)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ivybridge_enable_vblank(struct drm_device *dev, int pipe)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
if (!i915_pipe_enabled(dev, pipe))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
||||
ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
|
||||
DE_PIPEA_VBLANK_IVB : DE_PIPEB_VBLANK_IVB);
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called from drm generic code, passed 'crtc' which
|
||||
* we use as a pipe index
|
||||
*/
|
||||
@ -1436,6 +1540,17 @@ void ironlake_disable_vblank(struct drm_device *dev, int pipe)
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
void ivybridge_disable_vblank(struct drm_device *dev, int pipe)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
||||
ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
|
||||
DE_PIPEA_VBLANK_IVB : DE_PIPEB_VBLANK_IVB);
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
/* Set the vblank monitor pipe
|
||||
*/
|
||||
int i915_vblank_pipe_set(struct drm_device *dev, void *data,
|
||||
@ -1713,6 +1828,56 @@ int ironlake_irq_postinstall(struct drm_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ivybridge_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
/* enable kind of interrupts always enabled */
|
||||
u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE_IVB |
|
||||
DE_PCH_EVENT_IVB | DE_PLANEA_FLIP_DONE_IVB |
|
||||
DE_PLANEB_FLIP_DONE_IVB;
|
||||
u32 render_irqs;
|
||||
u32 hotplug_mask;
|
||||
|
||||
DRM_INIT_WAITQUEUE(&dev_priv->ring[RCS].irq_queue);
|
||||
if (HAS_BSD(dev))
|
||||
DRM_INIT_WAITQUEUE(&dev_priv->ring[VCS].irq_queue);
|
||||
if (HAS_BLT(dev))
|
||||
DRM_INIT_WAITQUEUE(&dev_priv->ring[BCS].irq_queue);
|
||||
|
||||
dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
|
||||
dev_priv->irq_mask = ~display_mask;
|
||||
|
||||
/* should always can generate irq */
|
||||
I915_WRITE(DEIIR, I915_READ(DEIIR));
|
||||
I915_WRITE(DEIMR, dev_priv->irq_mask);
|
||||
I915_WRITE(DEIER, display_mask | DE_PIPEA_VBLANK_IVB |
|
||||
DE_PIPEB_VBLANK_IVB);
|
||||
POSTING_READ(DEIER);
|
||||
|
||||
dev_priv->gt_irq_mask = ~0;
|
||||
|
||||
I915_WRITE(GTIIR, I915_READ(GTIIR));
|
||||
I915_WRITE(GTIMR, dev_priv->gt_irq_mask);
|
||||
|
||||
render_irqs = GT_USER_INTERRUPT | GT_GEN6_BSD_USER_INTERRUPT |
|
||||
GT_BLT_USER_INTERRUPT;
|
||||
I915_WRITE(GTIER, render_irqs);
|
||||
POSTING_READ(GTIER);
|
||||
|
||||
hotplug_mask = (SDE_CRT_HOTPLUG_CPT |
|
||||
SDE_PORTB_HOTPLUG_CPT |
|
||||
SDE_PORTC_HOTPLUG_CPT |
|
||||
SDE_PORTD_HOTPLUG_CPT);
|
||||
dev_priv->pch_irq_mask = ~hotplug_mask;
|
||||
|
||||
I915_WRITE(SDEIIR, I915_READ(SDEIIR));
|
||||
I915_WRITE(SDEIMR, dev_priv->pch_irq_mask);
|
||||
I915_WRITE(SDEIER, hotplug_mask);
|
||||
POSTING_READ(SDEIER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i915_driver_irq_preinstall(struct drm_device * dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
|
@ -2778,6 +2778,19 @@
|
||||
#define DE_PIPEA_VSYNC (1 << 3)
|
||||
#define DE_PIPEA_FIFO_UNDERRUN (1 << 0)
|
||||
|
||||
/* More Ivybridge lolz */
|
||||
#define DE_ERR_DEBUG_IVB (1<<30)
|
||||
#define DE_GSE_IVB (1<<29)
|
||||
#define DE_PCH_EVENT_IVB (1<<28)
|
||||
#define DE_DP_A_HOTPLUG_IVB (1<<27)
|
||||
#define DE_AUX_CHANNEL_A_IVB (1<<26)
|
||||
#define DE_SPRITEB_FLIP_DONE_IVB (1<<9)
|
||||
#define DE_SPRITEA_FLIP_DONE_IVB (1<<4)
|
||||
#define DE_PLANEB_FLIP_DONE_IVB (1<<8)
|
||||
#define DE_PLANEA_FLIP_DONE_IVB (1<<3)
|
||||
#define DE_PIPEB_VBLANK_IVB (1<<5)
|
||||
#define DE_PIPEA_VBLANK_IVB (1<<0)
|
||||
|
||||
#define DEISR 0x44000
|
||||
#define DEIMR 0x44004
|
||||
#define DEIIR 0x44008
|
||||
|
Loading…
Reference in New Issue
Block a user