diff options
| author | Jesse Barnes <jbarnes@hobbes.virtuousgeek.org> | 2007-06-12 10:44:21 -0700 | 
|---|---|---|
| committer | Jesse Barnes <jbarnes@hobbes.virtuousgeek.org> | 2007-06-12 10:44:21 -0700 | 
| commit | db689c7b95613237cec904c3f6ee27e8c2bf7ce0 (patch) | |
| tree | 9e8b5377207f3d2a89759f189b1be611f6e85859 /linux-core | |
| parent | 280083d4a2a12a1ff6dc1b068553a4ae8960200c (diff) | |
Initial checkin of vblank rework.  Code attempts to reduce the number
of vblank interrupt in order to save power.
Diffstat (limited to 'linux-core')
| -rw-r--r-- | linux-core/drmP.h | 12 | ||||
| -rw-r--r-- | linux-core/drm_irq.c | 69 | ||||
| -rw-r--r-- | linux-core/i915_drv.c | 5 | 
3 files changed, 63 insertions, 23 deletions
| diff --git a/linux-core/drmP.h b/linux-core/drmP.h index dd3a69df..c3f20311 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -627,8 +627,9 @@ struct drm_driver {  	int (*kernel_context_switch) (struct drm_device * dev, int old,  				      int new);  	void (*kernel_context_switch_unlock) (struct drm_device * dev); -	int (*vblank_wait) (struct drm_device * dev, unsigned int *sequence); -	int (*vblank_wait2) (struct drm_device * dev, unsigned int *sequence); +	u32 (*get_vblank_counter) (struct drm_device *dev, int crtc); +	void (*enable_vblank) (struct drm_device *dev, int crtc); +	void (*disable_vblank) (struct drm_device *dev, int crtc);  	int (*dri_library_name) (struct drm_device * dev, char * buf);  	/** @@ -783,12 +784,13 @@ typedef struct drm_device {  	/*@{ */  	wait_queue_head_t vbl_queue;	/**< VBLANK wait queue */ -	atomic_t vbl_received; -	atomic_t vbl_received2;		/**< number of secondary VBLANK interrupts */ +	atomic_t *vblank_count;		/**< number of VBLANK interrupts (driver must alloc the right number of counters) */  	spinlock_t vbl_lock;  	struct list_head vbl_sigs;		/**< signal list to send on VBLANK */  	struct list_head vbl_sigs2;	/**< signals to send on secondary VBLANK */ -	unsigned int vbl_pending; +	atomic_t *vbl_pending; +	u32 *last_vblank; /* protected by dev->vbl_lock */ +	unsigned long max_vblank_count; /**< size of vblank counter register */  	spinlock_t tasklet_lock;	/**< For drm_locked_tasklet */  	void (*locked_tasklet_func)(struct drm_device *dev); diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 88716712..f229f778 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -221,6 +221,44 @@ int drm_control(struct inode *inode, struct file *filp,  	}  } +static void drm_vblank_get(drm_device_t *dev, int crtc) +{ +	unsigned long irqflags; +	u32 cur_vblank, diff; + +	if (atomic_add_return(1, &dev->vbl_pending[crtc]) != 1) +		return; + +	/* +	 * Interrupts were disabled prior to this call, so deal with counter +	 * wrap if needed. +	 * NOTE!  It's possible we lost a full dev->max_vblank_count events +	 * here if the register is small or we had vblank interrupts off for +	 * a long time. +	 */ +	cur_vblank = dev->driver->get_vblank_counter(dev, crtc); +	spin_lock_irqsave(&dev->vbl_lock, irqflags); +	if (cur_vblank < dev->last_vblank[crtc]) { +		diff = dev->max_vblank_count - +			dev->last_vblank[crtc]; +		diff += cur_vblank; +	} else { +		diff = cur_vblank - last_vblank; +	} +	dev->last_vblank[crtc] = cur_vblank; +	spin_lock_irqrestore(&dev->vbl_lock, irqflags); + +	atomic_add(diff, &dev->vblank_count[crtc]); +	dev->driver->enable_vblank(dev, crtc); +} + +static void drm_vblank_put(drm_device_t *dev, int crtc) +{ +	if (atomic_dec_and_test(&dev->vbl_pending[crtc])) +		dev->driver->disable_vblank(dev, crtc); +} + +  /**   * Wait for VBLANK.   * @@ -248,7 +286,7 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)  	drm_wait_vblank_t vblwait;  	struct timeval now;  	int ret = 0; -	unsigned int flags, seq; +	unsigned int flags, seq, crtc;  	if ((!dev->irq) || (!dev->irq_enabled))  		return -EINVAL; @@ -265,13 +303,14 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)  	}  	flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; +	crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0;  	if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ?  				    DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL))  		return -EINVAL; -	seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 -			  : &dev->vbl_received); +	drm_vblank_get(dev, crtc); +	seq = atomic_read(&dev->vblank_count[crtc]);  	switch (vblwait.request.type & _DRM_VBLANK_TYPES_MASK) {  	case _DRM_VBLANK_RELATIVE: @@ -307,22 +346,23 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)  				spin_unlock_irqrestore(&dev->vbl_lock,  						       irqflags);  				vblwait.reply.sequence = seq; +				drm_vblank_put(dev, crtc);  				goto done;  			}  		} -		if (dev->vbl_pending >= 100) { +		if (atomic_read(&dev->vbl_pending[crtc]) >= 100) {  			spin_unlock_irqrestore(&dev->vbl_lock, irqflags); +			drm_vblank_put(dev, crtc);  			return -EBUSY;  		} -		dev->vbl_pending++; -  		spin_unlock_irqrestore(&dev->vbl_lock, irqflags);  		if (!  		    (vbl_sig =  		     drm_alloc(sizeof(drm_vbl_sig_t), DRM_MEM_DRIVER))) { +			drm_vblank_put(dev, crtc);  			return -ENOMEM;  		} @@ -340,14 +380,12 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)  		vblwait.reply.sequence = seq;  	} else { -		if (flags & _DRM_VBLANK_SECONDARY) { -			if (dev->driver->vblank_wait2) -				ret = dev->driver->vblank_wait2(dev, &vblwait.request.sequence); -		} else if (dev->driver->vblank_wait) -			ret = -			    dev->driver->vblank_wait(dev, -						     &vblwait.request.sequence); +		unsigned long cur_vblank; +		DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, +			    (((cur_vblank = atomic_read(&dev->vblank_count[crtc])) +			      - seq) <= (1 << 23))); +		drm_vblank_put(dev, crtc);  		do_gettimeofday(&now);  		vblwait.reply.tval_sec = now.tv_sec;  		vblwait.reply.tval_usec = now.tv_usec; @@ -379,8 +417,7 @@ void drm_vbl_send_signals(drm_device_t * dev)  	for (i = 0; i < 2; i++) {  		drm_vbl_sig_t *vbl_sig, *tmp;  		struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs; -		unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 : -						   &dev->vbl_received); +		unsigned int vbl_seq = atomic_read(&dev->vblank_count[i]);  		list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) {  			if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { @@ -393,7 +430,7 @@ void drm_vbl_send_signals(drm_device_t * dev)  				drm_free(vbl_sig, sizeof(*vbl_sig),  					 DRM_MEM_DRIVER); -				dev->vbl_pending--; +				drm_vblank_put(dev, i);  			}  		}  	} diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index 7fdb0839..e7a9bfdc 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -83,8 +83,9 @@ static struct drm_driver driver = {  	.lastclose = i915_driver_lastclose,  	.preclose = i915_driver_preclose,  	.device_is_agp = i915_driver_device_is_agp, -	.vblank_wait = i915_driver_vblank_wait, -	.vblank_wait2 = i915_driver_vblank_wait2, +	.get_vblank_counter = i915_get_vblank_counter, +	.enable_vblank = i915_enable_vblank, +	.disable_vblank = i915_disable_vblank,  	.irq_preinstall = i915_driver_irq_preinstall,  	.irq_postinstall = i915_driver_irq_postinstall,  	.irq_uninstall = i915_driver_irq_uninstall, | 
