diff --git a/linux-core/drmP.h b/linux-core/drmP.h index 4e8b087..efd046b 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -836,6 +836,7 @@ struct drm_device { u32 *last_vblank; /* protected by dev->vbl_lock, used */ /* for wraparound handling */ u32 *vblank_offset; /* used to track how many vblanks */ + int *vblank_enabled; u32 *vblank_premodeset; /* were lost during modeset */ struct timer_list vblank_disable_timer; diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 367d2dd..e4940bb 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -74,11 +74,18 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, static void vblank_disable_fn(unsigned long arg) { struct drm_device *dev = (struct drm_device *)arg; + unsigned long irqflags; int i; - for (i = 0; i < dev->num_crtcs; i++) - if (atomic_read(&dev->vblank_refcount[i]) == 0) + for (i = 0; i < dev->num_crtcs; i++) { + spin_lock_irqsave(&dev->vbl_lock, irqflags); + if (atomic_read(&dev->vblank_refcount[i]) == 0 && + dev->vblank_enabled[i]) { dev->driver->disable_vblank(dev, i); + dev->vblank_enabled[i] = 0; + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + } } int drm_vblank_init(struct drm_device *dev, int num_crtcs) @@ -111,6 +118,11 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) if (!dev->vblank_refcount) goto err; + dev->vblank_enabled = drm_calloc(num_crtcs, sizeof(int), + DRM_MEM_DRIVER); + if (!dev->vblank_enabled) + goto err; + dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); if (!dev->last_vblank) goto err; @@ -143,6 +155,8 @@ err: DRM_MEM_DRIVER); drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * num_crtcs, + DRM_MEM_DRIVER); drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * num_crtcs, DRM_MEM_DRIVER); drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * @@ -357,14 +371,20 @@ EXPORT_SYMBOL(drm_update_vblank_count); */ int drm_vblank_get(struct drm_device *dev, int crtc) { + unsigned long irqflags; int ret = 0; + spin_lock_irqsave(&dev->vbl_lock, irqflags); /* Going from 0->1 means we have to enable interrupts again */ - if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) { + if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1 && + !dev->vblank_enabled[crtc]) { ret = dev->driver->enable_vblank(dev, crtc); if (ret) atomic_dec(&dev->vblank_refcount[crtc]); + else + dev->vblank_enabled[crtc] = 1; } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); return ret; } @@ -382,8 +402,7 @@ void drm_vblank_put(struct drm_device *dev, int crtc) { /* Last user schedules interrupt disable */ if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) - mod_timer(&dev->vblank_disable_timer, - round_jiffies_relative(DRM_HZ)); + mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ); } EXPORT_SYMBOL(drm_vblank_put); diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index 5f2e6ad..84ad7d0 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -310,6 +310,7 @@ static int i915_suspend(struct drm_device *dev) dev_priv->saveDSPATILEOFF = I915_READ(DSPATILEOFF); } i915_save_palette(dev, PIPE_A); + dev_priv->savePIPEASTAT = I915_READ(I915REG_PIPEASTAT); /* Pipe & plane B info */ dev_priv->savePIPEBCONF = I915_READ(PIPEBCONF); @@ -337,6 +338,7 @@ static int i915_suspend(struct drm_device *dev) dev_priv->saveDSPBTILEOFF = I915_READ(DSPBTILEOFF); } i915_save_palette(dev, PIPE_B); + dev_priv->savePIPEBSTAT = I915_READ(I915REG_PIPEBSTAT); /* CRT state */ dev_priv->saveADPA = I915_READ(ADPA); @@ -363,6 +365,11 @@ static int i915_suspend(struct drm_device *dev) dev_priv->saveFBC_CONTROL2 = I915_READ(FBC_CONTROL2); dev_priv->saveFBC_CONTROL = I915_READ(FBC_CONTROL); + /* Interrupt state */ + dev_priv->saveIIR = I915_READ(I915REG_INT_IDENTITY_R); + dev_priv->saveIER = I915_READ(I915REG_INT_ENABLE_R); + dev_priv->saveIMR = I915_READ(I915REG_INT_MASK_R); + /* VGA state */ dev_priv->saveVCLK_DIVISOR_VGA0 = I915_READ(VCLK_DIVISOR_VGA0); dev_priv->saveVCLK_DIVISOR_VGA1 = I915_READ(VCLK_DIVISOR_VGA1); diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index c92758f..da97381 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -147,6 +147,9 @@ typedef struct drm_i915_private { drm_i915_vbl_swap_t vbl_swaps; unsigned int swaps_pending; + spinlock_t vbl_count_lock; + atomic_t *vbl_count; + /* Register state */ u8 saveLBB; u32 saveDSPACNTR; @@ -166,6 +169,7 @@ typedef struct drm_i915_private { u32 saveVBLANK_A; u32 saveVSYNC_A; u32 saveBCLRPAT_A; + u32 savePIPEASTAT; u32 saveDSPASTRIDE; u32 saveDSPASIZE; u32 saveDSPAPOS; @@ -186,6 +190,7 @@ typedef struct drm_i915_private { u32 saveVBLANK_B; u32 saveVSYNC_B; u32 saveBCLRPAT_B; + u32 savePIPEBSTAT; u32 saveDSPBSTRIDE; u32 saveDSPBSIZE; u32 saveDSPBPOS; @@ -214,6 +219,9 @@ typedef struct drm_i915_private { u32 saveFBC_LL_BASE; u32 saveFBC_CONTROL; u32 saveFBC_CONTROL2; + u32 saveIER; + u32 saveIIR; + u32 saveIMR; u32 saveSWF0[16]; u32 saveSWF1[16]; u32 saveSWF2[3]; @@ -472,7 +480,12 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define I915REG_INSTPM 0x020c0 #define PIPEADSL 0x70000 +#define PIPEASLC 0x70004 +//#define SLC_INCLUSIVE (1UL<<31) +//#define SLC_END_SHIFT 16 +//#define SLC_MASK 0x1fff #define PIPEBDSL 0x71000 +#define PIPEBSLC 0x71004 #define I915REG_PIPEASTAT 0x70024 #define I915REG_PIPEBSTAT 0x71024 @@ -507,8 +520,11 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define PIPE_PIXEL_MASK 0x00ffffff #define PIPE_PIXEL_SHIFT 0 +//#define I915_DLC_ENABLE (1UL<<24) #define I915_VBLANK_INTERRUPT_ENABLE (1UL<<17) +//#define I915_VSYNC_CLEAR (1UL<<9) #define I915_VBLANK_CLEAR (1UL<<1) +//#define I915_DLC_CLEAR (1UL<<8) #define SRX_INDEX 0x3c4 #define SRX_DATA 0x3c5 @@ -797,9 +813,8 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define VTOTAL_MASK 0x00001fff #define VTOTAL_SHIFT 16 #define VACTIVE_MASK 0x00000fff -#define VBLANK_END_MASK 0x00001fff #define VBLANK_END_SHIFT 16 -#define VBLANK_START_MASK 0x00001fff +#define VBLANK_MASK 0x00001fff #define PP_STATUS 0x61200 # define PP_ON (1 << 31) @@ -1099,7 +1114,6 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller); #define PIPEBGCMAXRED 0x71010 #define PIPEBGCMAXGREEN 0x71014 #define PIPEBGCMAXBLUE 0x71018 -#define PIPEBSTAT 0x71024 #define PIPEBFRAMEHIGH 0x71040 #define PIPEBFRAMEPIXEL 0x71044 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); }