diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index c771ab3..8d46686 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -276,6 +276,7 @@ static int i915_suspend(struct drm_device *dev) pci_save_state(dev->pdev); pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB); + pci_read_config_byte(dev->pdev, MGGC, &dev_priv->saveMGGC); /* Pipe & plane A info */ dev_priv->savePIPEACONF = I915_READ(PIPEACONF); @@ -397,6 +398,7 @@ static int i915_resume(struct drm_device *dev) return -1; pci_write_config_byte(dev->pdev, LBB, dev_priv->saveLBB); + pci_write_config_word(dev->pdev, MGGC, dev_priv->saveMGGC); /* Pipe & plane A info */ /* Prime the clock */ diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index d08fdf4..68584b4 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -149,6 +149,7 @@ typedef struct drm_i915_private { /* Register state */ u8 saveLBB; + u16 saveMGGC; u32 saveDSPACNTR; u32 saveDSPBCNTR; u32 savePIPEACONF; @@ -361,6 +362,7 @@ extern void intel_fini_chipset_flush_compat(struct drm_device *dev); extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); /* Extended config space */ +#define MGGC 0x52 #define LBB 0xf4 /* VGA stuff */ diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index d463f6e..9f134a3 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -32,8 +32,10 @@ #include "i915_drv.h" #define USER_INT_FLAG (1<<1) -#define VSYNC_PIPEB_FLAG (1<<5) -#define VSYNC_PIPEA_FLAG (1<<7) +#define IIR_VBLANK_A (1<<7) +#define IIR_VEVENT_A (1<<6) +#define IIR_VBLANK_B (1<<5) +#define IIR_VEVENT_B (1<<4) #define MAX_NOPID ((u32)~0) @@ -341,42 +343,19 @@ static void i915_vblank_tasklet(struct drm_device *dev) drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER); } } -#if 0 -static int i915_in_vblank(struct drm_device *dev, int pipe) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - unsigned long pipedsl, vblank, vtotal; - unsigned long vbl_start, vbl_end, cur_line; - - pipedsl = pipe ? PIPEBDSL : PIPEADSL; - vblank = pipe ? VBLANK_B : VBLANK_A; - vtotal = pipe ? VTOTAL_B : VTOTAL_A; - - vbl_start = I915_READ(vblank) & VBLANK_START_MASK; - vbl_end = (I915_READ(vblank) >> VBLANK_END_SHIFT) & VBLANK_END_MASK; - - cur_line = I915_READ(pipedsl); - if (cur_line >= vbl_start) - return 1; - - return 0; -} -#endif -u32 i915_get_vblank_counter(struct drm_device *dev, int plane) +u32 i915_read_frame_count(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long high_frame; unsigned long low_frame; u32 high1, high2, low, count; - int pipe; - pipe = i915_get_pipe(dev, plane); high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; if (!i915_pipe_enabled(dev, pipe)) { - printk(KERN_ERR "trying to get vblank count for disabled " + printk(KERN_ERR "tried to read frame count for disabled " "pipe %d\n", pipe); return 0; } @@ -397,18 +376,17 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int plane) count = (high1 << 8) | low; - /* - * If we're in the middle of the vblank period, the - * above regs won't have been updated yet, so return - * an incremented count to stay accurate - */ -#if 0 - if (i915_in_vblank(dev, pipe)) - count++; -#endif return count; } +u32 i915_get_vblank_counter(struct drm_device *dev, int plane) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int pipe = i915_get_pipe(dev, plane); + + return atomic_read(&dev_priv->vbl_count[pipe]); +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; @@ -431,24 +409,35 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) * Clear the PIPE(A|B)STAT regs before the IIR otherwise * we may get extra interrupts. */ - if (temp & VSYNC_PIPEA_FLAG) { + if (temp & IIR_VBLANK_A) { + atomic_inc(&dev_priv->vbl_count[0]); + printk(KERN_ERR "display line a: %d\n", I915_READ(PIPEADSL)); drm_handle_vblank(dev, i915_get_plane(dev, 0)); - I915_WRITE(I915REG_PIPEASTAT, - pipea_stats | I915_VBLANK_INTERRUPT_ENABLE | - I915_VBLANK_CLEAR); + I915_WRITE(I915REG_PIPEASTAT, pipea_stats); + I915_WRITE16(I915REG_INT_IDENTITY_R, temp & IIR_VBLANK_A); } - if (temp & VSYNC_PIPEB_FLAG) { + if (temp & IIR_VBLANK_B) { + I915_WRITE(I915REG_PIPEBSTAT, pipeb_stats); + I915_WRITE16(I915REG_INT_IDENTITY_R, temp & IIR_VBLANK_B); + + atomic_inc(&dev_priv->vbl_count[1]); + printk(KERN_ERR "display line b: %d\n", I915_READ(PIPEBDSL)); +// printk(KERN_ERR "iir: 0x%08x\n", +// I915_READ(I915REG_INT_IDENTITY_R)); +// printk(KERN_ERR "pipebstat: 0x%08x\n", I915_READ(PIPEBSTAT)); + drm_handle_vblank(dev, i915_get_plane(dev, 1)); - I915_WRITE(I915REG_PIPEBSTAT, - pipeb_stats | I915_VBLANK_INTERRUPT_ENABLE | - I915_VBLANK_CLEAR); +// printk(KERN_ERR "acked iir: 0x%08x\n", +// I915_READ(I915REG_INT_IDENTITY_R)); +// printk(KERN_ERR "acked pipebstat: 0x%08x\n", +// I915_READ(PIPEBSTAT)); } - +out: I915_WRITE16(I915REG_INT_IDENTITY_R, temp); (void) I915_READ16(I915REG_INT_IDENTITY_R); /* Flush posted write */ - temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG | VSYNC_PIPEA_FLAG | - VSYNC_PIPEB_FLAG); + temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG | IIR_VBLANK_A | + IIR_VBLANK_B); dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); @@ -459,7 +448,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) #endif } - if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { + if (temp & (IIR_VBLANK_A | IIR_VBLANK_B)) { if (dev_priv->swaps_pending > 0) drm_locked_tasklet(dev, i915_vblank_tasklet); } @@ -575,17 +564,51 @@ int i915_irq_wait(struct drm_device *dev, void *data, return i915_wait_irq(dev, irqwait->irq_seq); } +void i915_add_missed_vbls(struct drm_device *dev, int pipe) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + atomic_t *vbl_count; + unsigned long missed_count, last_count, cur_count, irqflags; + + vbl_count = &dev_priv->vbl_count[pipe]; + last_count = atomic_read(vbl_count); + + cur_count = i915_read_frame_count(dev, pipe); + + spin_lock_irqsave(&dev_priv->vbl_count_lock, irqflags); + if (cur_count + 1 == last_count) { + printk(KERN_ERR "frame vs. vblank count mismatch\n"); + missed_count = 0; + } else if (cur_count < last_count) { + printk(KERN_ERR "driver count wrapped\n"); + missed_count = dev->max_vblank_count - last_count; + missed_count += cur_count; + } else { + missed_count = cur_count - last_count; + } + spin_unlock_irqrestore(&dev_priv->vbl_count_lock, irqflags); + + atomic_add(missed_count, vbl_count); +} + int i915_enable_vblank(struct drm_device *dev, int plane) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int pipe = i915_get_pipe(dev, plane); + printk(KERN_ERR "vblank enable\n"); + i915_add_missed_vbls(dev, pipe); + switch (pipe) { case 0: - dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG; + dev_priv->irq_enable_reg |= IIR_VBLANK_A; + I915_WRITE(I915REG_PIPEASTAT, I915_VBLANK_INTERRUPT_ENABLE | + I915_VBLANK_CLEAR); break; case 1: - dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG; + dev_priv->irq_enable_reg |= IIR_VBLANK_B; + I915_WRITE(I915REG_PIPEBSTAT, I915_VBLANK_INTERRUPT_ENABLE | + I915_VBLANK_CLEAR); break; default: DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", @@ -593,6 +616,8 @@ int i915_enable_vblank(struct drm_device *dev, int plane) break; } + printk(KERN_ERR "enabled irqs for pipe %d\n", pipe); + I915_WRITE16(I915REG_INT_MASK_R, ~dev_priv->irq_enable_reg); I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); return 0; @@ -605,10 +630,12 @@ void i915_disable_vblank(struct drm_device *dev, int plane) switch (pipe) { case 0: - dev_priv->irq_enable_reg &= ~VSYNC_PIPEA_FLAG; + I915_WRITE(I915REG_PIPEASTAT, I915_VBLANK_CLEAR); + dev_priv->irq_enable_reg &= ~IIR_VBLANK_A; break; case 1: - dev_priv->irq_enable_reg &= ~VSYNC_PIPEB_FLAG; + I915_WRITE(I915REG_PIPEBSTAT, I915_VBLANK_CLEAR); + dev_priv->irq_enable_reg &= ~IIR_VBLANK_B; break; default: DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", @@ -616,6 +643,9 @@ void i915_disable_vblank(struct drm_device *dev, int plane) break; } + printk(KERN_ERR "disabled irqs for pipe %d\n", pipe); + + I915_WRITE16(I915REG_INT_MASK_R, ~dev_priv->irq_enable_reg); I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); } @@ -625,6 +655,7 @@ static void i915_enable_interrupt (struct drm_device *dev) dev_priv->irq_enable_reg |= USER_INT_FLAG; + I915_WRITE16(I915REG_INT_MASK_R, ~dev_priv->irq_enable_reg); I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); dev_priv->irq_enabled = 1; } @@ -666,9 +697,9 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data, flag = I915_READ(I915REG_INT_ENABLE_R); pipe->pipe = 0; - if (flag & VSYNC_PIPEA_FLAG) + if (flag & IIR_VBLANK_A) pipe->pipe |= DRM_I915_VBLANK_PIPE_A; - if (flag & VSYNC_PIPEB_FLAG) + if (flag & IIR_VBLANK_B) pipe->pipe |= DRM_I915_VBLANK_PIPE_B; return 0; @@ -834,9 +865,14 @@ void i915_driver_irq_preinstall(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + printk(KERN_ERR "irq preinstall\n"); + I915_WRITE(I915REG_PIPEASTAT, 0); + I915_WRITE(I915REG_PIPEBSTAT, 0); I915_WRITE16(I915REG_HWSTAM, 0xeffe); - I915_WRITE16(I915REG_INT_MASK_R, 0x0); + I915_WRITE16(I915REG_INT_MASK_R, 0xffff); I915_WRITE16(I915REG_INT_ENABLE_R, 0x0); + I915_WRITE16(I915REG_INT_IDENTITY_R, + I915_READ(I915REG_INT_IDENTITY_R)); } int i915_driver_irq_postinstall(struct drm_device * dev) @@ -852,12 +888,22 @@ int i915_driver_irq_postinstall(struct drm_device * dev) dev_priv->user_irq_refcount = 0; dev_priv->irq_enable_reg = 0; + + spin_lock_init(&dev_priv->vbl_count_lock); + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ + + dev_priv->vbl_count = drm_alloc(sizeof(atomic_t) * num_pipes, + GFP_KERNEL); + if (!dev_priv->vbl_count) + return -ENOMEM; + + atomic_set(&dev_priv->vbl_count[0], i915_read_frame_count(dev, 0)); + atomic_set(&dev_priv->vbl_count[1], i915_read_frame_count(dev, 1)); + ret = drm_vblank_init(dev, num_pipes); if (ret) return ret; - dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ - i915_enable_interrupt(dev); DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); @@ -882,6 +928,9 @@ void i915_driver_irq_uninstall(struct drm_device * dev) I915_WRITE16(I915REG_INT_MASK_R, 0xffff); I915_WRITE16(I915REG_INT_ENABLE_R, 0x0); + i915_disable_vblank(dev, 0); + i915_disable_vblank(dev, 1); + temp = I915_READ16(I915REG_INT_IDENTITY_R); I915_WRITE16(I915REG_INT_IDENTITY_R, temp); }