diff options
| -rw-r--r-- | linux-core/drmP.h | 91 | ||||
| -rw-r--r-- | linux-core/drm_drv.c | 4 | ||||
| -rw-r--r-- | linux-core/drm_irq.c | 356 | ||||
| -rw-r--r-- | linux-core/i915_drv.c | 8 | ||||
| -rw-r--r-- | linux-core/mach64_drv.c | 6 | ||||
| -rw-r--r-- | linux-core/mga_drv.c | 7 | ||||
| -rw-r--r-- | linux-core/r128_drv.c | 7 | ||||
| -rw-r--r-- | linux-core/radeon_drv.c | 8 | ||||
| -rw-r--r-- | shared-core/drm.h | 17 | ||||
| -rw-r--r-- | shared-core/i915_drv.h | 43 | ||||
| -rw-r--r-- | shared-core/i915_irq.c | 172 | ||||
| -rw-r--r-- | shared-core/mach64_drv.h | 13 | ||||
| -rw-r--r-- | shared-core/mach64_irq.c | 88 | ||||
| -rw-r--r-- | shared-core/mga_drv.h | 10 | ||||
| -rw-r--r-- | shared-core/mga_irq.c | 69 | ||||
| -rw-r--r-- | shared-core/nouveau_drv.h | 2 | ||||
| -rw-r--r-- | shared-core/nouveau_irq.c | 4 | ||||
| -rw-r--r-- | shared-core/r128_drv.h | 9 | ||||
| -rw-r--r-- | shared-core/r128_irq.c | 55 | ||||
| -rw-r--r-- | shared-core/radeon_drv.h | 19 | ||||
| -rw-r--r-- | shared-core/radeon_irq.c | 171 | ||||
| -rw-r--r-- | shared-core/via_drv.c | 6 | ||||
| -rw-r--r-- | shared-core/via_drv.h | 7 | ||||
| -rw-r--r-- | shared-core/via_irq.c | 71 | 
24 files changed, 854 insertions, 389 deletions
diff --git a/linux-core/drmP.h b/linux-core/drmP.h index dbf2a924..4e8b087b 100644 --- a/linux-core/drmP.h +++ b/linux-core/drmP.h @@ -104,10 +104,8 @@ struct drm_file;  #define DRIVER_HAVE_DMA    0x20  #define DRIVER_HAVE_IRQ    0x40  #define DRIVER_IRQ_SHARED  0x80 -#define DRIVER_IRQ_VBL     0x100 -#define DRIVER_DMA_QUEUE   0x200 -#define DRIVER_FB_DMA      0x400 -#define DRIVER_IRQ_VBL2    0x800 +#define DRIVER_DMA_QUEUE   0x100 +#define DRIVER_FB_DMA      0x200  /*@}*/ @@ -628,9 +626,51 @@ struct drm_driver {  	int (*context_dtor) (struct drm_device *dev, int context);  	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); +	void (*kernel_context_switch_unlock) (struct drm_device * dev); +	/** +	 * get_vblank_counter - get raw hardware vblank counter +	 * @dev: DRM device +	 * @crtc: counter to fetch +	 * +	 * Driver callback for fetching a raw hardware vblank counter +	 * for @crtc.  If a device doesn't have a hardware counter, the +	 * driver can simply return the value of drm_vblank_count and +	 * make the enable_vblank() and disable_vblank() hooks into no-ops, +	 * leaving interrupts enabled at all times. +	 * +	 * Wraparound handling and loss of events due to modesetting is dealt +	 * with in the DRM core code. +	 * +	 * RETURNS +	 * Raw vblank counter value. +	 */ +	u32 (*get_vblank_counter) (struct drm_device *dev, int crtc); + +	/** +	 * enable_vblank - enable vblank interrupt events +	 * @dev: DRM device +	 * @crtc: which irq to enable +	 * +	 * Enable vblank interrupts for @crtc.  If the device doesn't have +	 * a hardware vblank counter, this routine should be a no-op, since +	 * interrupts will have to stay on to keep the count accurate. +	 * +	 * RETURNS +	 * Zero on success, appropriate errno if the given @crtc's vblank +	 * interrupt cannot be enabled. +	 */ +	int (*enable_vblank) (struct drm_device *dev, int crtc); + +	/** +	 * disable_vblank - disable vblank interrupt events +	 * @dev: DRM device +	 * @crtc: which irq to enable +	 * +	 * Disable vblank interrupts for @crtc.  If the device doesn't have +	 * a hardware vblank counter, this routine should be a no-op, since +	 * interrupts will have to stay on to keep the count accurate. +	 */ +	void (*disable_vblank) (struct drm_device *dev, int crtc);  	int (*dri_library_name) (struct drm_device *dev, char * buf);  	/** @@ -649,7 +689,7 @@ struct drm_driver {  /* these have to be filled in */  	 irqreturn_t(*irq_handler) (DRM_IRQ_ARGS);  	void (*irq_preinstall) (struct drm_device *dev); -	void (*irq_postinstall) (struct drm_device *dev); +	int (*irq_postinstall) (struct drm_device *dev);  	void (*irq_uninstall) (struct drm_device *dev);  	void (*reclaim_buffers) (struct drm_device *dev,  				 struct drm_file *file_priv); @@ -787,13 +827,19 @@ struct drm_device {  	/** \name VBLANK IRQ support */  	/*@{ */ -	wait_queue_head_t vbl_queue;	/**< VBLANK wait queue */ -	atomic_t vbl_received; -	atomic_t vbl_received2;		/**< number of secondary VBLANK interrupts */ +	wait_queue_head_t *vbl_queue;	/**< VBLANK wait queue */ +	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; +	struct list_head *vbl_sigs;		/**< signal list to send on VBLANK */ +	atomic_t vbl_signal_pending;	/* number of signals pending on all crtcs*/ +	atomic_t *vblank_refcount;	/* number of users of vblank interrupts per crtc */ +	u32 *last_vblank;		/* protected by dev->vbl_lock, used */ +					/* for wraparound handling */ +	u32 *vblank_offset;		/* used to track how many vblanks */ +	u32 *vblank_premodeset;		/*  were lost during modeset */ +	struct timer_list vblank_disable_timer; + +	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); @@ -813,6 +859,7 @@ struct drm_device {  #ifdef __alpha__  	struct pci_controller *hose;  #endif +	int num_crtcs;			/**< Number of CRTCs on this device */  	struct drm_sg_mem *sg;		/**< Scatter gather memory */  	void *dev_private;		/**< device private data */  	struct drm_sigdata sigdata;		/**< For block_all_signals */ @@ -1084,11 +1131,19 @@ extern void drm_driver_irq_preinstall(struct drm_device *dev);  extern void drm_driver_irq_postinstall(struct drm_device *dev);  extern void drm_driver_irq_uninstall(struct drm_device *dev); -extern int drm_wait_vblank(struct drm_device *dev, void *data, -			   struct drm_file *file_priv); -extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); -extern void drm_vbl_send_signals(struct drm_device *dev); +extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); +extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp); +extern int drm_vblank_wait(struct drm_device * dev, unsigned int *vbl_seq);  extern void drm_locked_tasklet(struct drm_device *dev, void(*func)(struct drm_device*)); +extern u32 drm_vblank_count(struct drm_device *dev, int crtc); +extern void drm_update_vblank_count(struct drm_device *dev, int crtc); +extern void drm_handle_vblank(struct drm_device *dev, int crtc); +extern int drm_vblank_get(struct drm_device *dev, int crtc); +extern void drm_vblank_put(struct drm_device *dev, int crtc); + +				/* Modesetting support */ +extern int drm_modeset_ctl(struct drm_device *dev, void *data, +			   struct drm_file *file_priv);  				/* AGP/GART support (drm_agpsupport.h) */  extern struct drm_agp_head *drm_agp_init(struct drm_device *dev); diff --git a/linux-core/drm_drv.c b/linux-core/drm_drv.c index 343d5f32..3c2794d0 100644 --- a/linux-core/drm_drv.c +++ b/linux-core/drm_drv.c @@ -115,14 +115,12 @@ static struct drm_ioctl_desc drm_ioctls[] = {  	DRM_IOCTL_DEF(DRM_IOCTL_SG_ALLOC, drm_sg_alloc_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),  	DRM_IOCTL_DEF(DRM_IOCTL_SG_FREE, drm_sg_free, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), -  	DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank, 0), -	//	DRM_IOCTL_DEF(DRM_IOCTL_BUFOBJ, drm_bo_ioctl, DRM_AUTH), +	DRM_IOCTL_DEF(DRM_IOCTL_MODESET_CTL, drm_modeset_ctl, 0),  	DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_update_drawable_info, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), -  	DRM_IOCTL_DEF(DRM_IOCTL_MM_INIT, drm_mm_init_ioctl,  		      DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),  	DRM_IOCTL_DEF(DRM_IOCTL_MM_TAKEDOWN, drm_mm_takedown_ioctl, diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index 866878aa..2a5a4539 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -71,18 +71,100 @@ int drm_irq_by_busid(struct drm_device *dev, void *data,  	return 0;  } +static void vblank_disable_fn(unsigned long arg) +{ +	struct drm_device *dev = (struct drm_device *)arg; +	int i; + +	for (i = 0; i < dev->num_crtcs; i++) +		if (atomic_read(&dev->vblank_refcount[i]) == 0) +			dev->driver->disable_vblank(dev, i); +} + +int drm_vblank_init(struct drm_device *dev, int num_crtcs) +{ +	int i, ret = -ENOMEM; + +	setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, +		    (unsigned long)dev); +	spin_lock_init(&dev->vbl_lock); +	atomic_set(&dev->vbl_signal_pending, 0); +	dev->num_crtcs = num_crtcs; + +	dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs, +				   DRM_MEM_DRIVER); +	if (!dev->vbl_queue) +		goto err; + +	dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs, +				  DRM_MEM_DRIVER); +	if (!dev->vbl_sigs) +		goto err; + +	dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, +				      DRM_MEM_DRIVER); +	if (!dev->_vblank_count) +		goto err; + +	dev->vblank_refcount = drm_alloc(sizeof(atomic_t) * num_crtcs, +					 DRM_MEM_DRIVER); +	if (!dev->vblank_refcount) +		goto err; + +	dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); +	if (!dev->last_vblank) +		goto err; + +	dev->vblank_premodeset = drm_calloc(num_crtcs, sizeof(u32), +					    DRM_MEM_DRIVER); +	if (!dev->vblank_premodeset) +		goto err; + +	dev->vblank_offset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); +	if (!dev->vblank_offset) +		goto err; + +	/* Zero per-crtc vblank stuff */ +	for (i = 0; i < num_crtcs; i++) { +		init_waitqueue_head(&dev->vbl_queue[i]); +		INIT_LIST_HEAD(&dev->vbl_sigs[i]); +		atomic_set(&dev->_vblank_count[i], 0); +		atomic_set(&dev->vblank_refcount[i], 0); +	} + +	return 0; + +err: +	drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * num_crtcs, +		 DRM_MEM_DRIVER); +	drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * num_crtcs, +		 DRM_MEM_DRIVER); +	drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * num_crtcs, +		 DRM_MEM_DRIVER); +	drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * +		 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) * +		 num_crtcs, DRM_MEM_DRIVER); +	drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * num_crtcs, +		 DRM_MEM_DRIVER); +	return ret; +} +EXPORT_SYMBOL(drm_vblank_init); +  /**   * Install IRQ handler.   *   * \param dev DRM device.   * - * Initializes the IRQ related data, and setups drm_device::vbl_queue. Installs the handler, calling the driver + * Initializes the IRQ related data. Installs the handler, calling the driver   * \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions   * before and after the installation.   */  int drm_irq_install(struct drm_device * dev)  { -	int ret; +	int ret = 0;  	unsigned long sh_flags = 0;  	if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ)) @@ -108,17 +190,6 @@ int drm_irq_install(struct drm_device * dev)  	DRM_DEBUG("irq=%d\n", dev->irq); -	if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) { -		init_waitqueue_head(&dev->vbl_queue); - -		spin_lock_init(&dev->vbl_lock); - -		INIT_LIST_HEAD(&dev->vbl_sigs); -		INIT_LIST_HEAD(&dev->vbl_sigs2); - -		dev->vbl_pending = 0; -	} -  	/* Before installing handler */  	dev->driver->irq_preinstall(dev); @@ -136,9 +207,14 @@ int drm_irq_install(struct drm_device * dev)  	}  	/* After installing handler */ -	dev->driver->irq_postinstall(dev); +	ret = dev->driver->irq_postinstall(dev); +	if (ret < 0) { +		mutex_lock(&dev->struct_mutex); +		dev->irq_enabled = 0; +		mutex_unlock(&dev->struct_mutex); +	} -	return 0; +	return ret;  }  EXPORT_SYMBOL(drm_irq_install); @@ -213,6 +289,149 @@ int drm_control(struct drm_device *dev, void *data,  }  /** + * drm_vblank_count - retrieve "cooked" vblank counter value + * @dev: DRM device + * @crtc: which counter to retrieve + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. + */ +u32 drm_vblank_count(struct drm_device *dev, int crtc) +{ +	return atomic_read(&dev->_vblank_count[crtc]) + +		dev->vblank_offset[crtc]; +} +EXPORT_SYMBOL(drm_vblank_count); + +/** + * drm_update_vblank_count - update the master vblank counter + * @dev: DRM device + * @crtc: counter to update + * + * Call back into the driver to update the appropriate vblank counter + * (specified by @crtc).  Deal with wraparound, if it occurred, and + * update the last read value so we can deal with wraparound on the next + * call if necessary. + */ +void drm_update_vblank_count(struct drm_device *dev, int crtc) +{ +	unsigned long irqflags; +	u32 cur_vblank, diff; + +	/* +	 * 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 - dev->last_vblank[crtc]; +	} +	dev->last_vblank[crtc] = cur_vblank; +	spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + +	atomic_add(diff, &dev->_vblank_count[crtc]); +} +EXPORT_SYMBOL(drm_update_vblank_count); + +/** + * drm_vblank_get - get a reference count on vblank events + * @dev: DRM device + * @crtc: which CRTC to own + * + * Acquire a reference count on vblank events to avoid having them disabled + * while in use.  Note callers will probably want to update the master counter + * using drm_update_vblank_count() above before calling this routine so that + * wakeups occur on the right vblank event. + * + * RETURNS + * Zero on success, nonzero on failure. + */ +int drm_vblank_get(struct drm_device *dev, int crtc) +{ +	int ret = 0; + +	/* Going from 0->1 means we have to enable interrupts again */ +	if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1) { +		ret = dev->driver->enable_vblank(dev, crtc); +		if (ret) +			atomic_dec(&dev->vblank_refcount[crtc]); +	} + +	return ret; +} +EXPORT_SYMBOL(drm_vblank_get); + +/** + * drm_vblank_put - give up ownership of vblank events + * @dev: DRM device + * @crtc: which counter to give up + * + * Release ownership of a given vblank counter, turning off interrupts + * if possible. + */ +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)); +} +EXPORT_SYMBOL(drm_vblank_put); + +/** + * drm_modeset_ctl - handle vblank event counter changes across mode switch + * @DRM_IOCTL_ARGS: standard ioctl arguments + * + * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET + * ioctls around modesetting so that any lost vblank events are accounted for. + */ +int drm_modeset_ctl(struct drm_device *dev, void *data, +		    struct drm_file *file_priv) +{ +	drm_modeset_ctl_t __user *argp = (void __user *)data; +	drm_modeset_ctl_t modeset; +	int crtc, ret = 0; +	u32 new; + +	if (copy_from_user(&modeset, argp, sizeof(modeset))) { +		ret = -EFAULT; +		goto out; +	} + +	crtc = modeset.arg; +	if (crtc >= dev->num_crtcs) { +		ret = -EINVAL; +		goto out; +	} + +	switch (modeset.cmd) { +	case _DRM_PRE_MODESET: +		dev->vblank_premodeset[crtc] = +			dev->driver->get_vblank_counter(dev, crtc); +		break; +	case _DRM_POST_MODESET: +		new = dev->driver->get_vblank_counter(dev, crtc); +		dev->vblank_offset[crtc] = dev->vblank_premodeset[crtc] - new; +		break; +	default: +		ret = -EINVAL; +		break; +	} + +out: +	return ret; +} + +/**   * Wait for VBLANK.   *   * \param inode device inode. @@ -231,12 +450,13 @@ int drm_control(struct drm_device *dev, void *data,   *   * If a signal is not requested, then calls vblank_wait().   */ -int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) +int drm_wait_vblank(struct drm_device *dev, void *data, +		    struct drm_file *file_priv)  {  	union drm_wait_vblank *vblwait = data;  	struct timeval now;  	int ret = 0; -	unsigned int flags, seq; +	unsigned int flags, seq, crtc;  	if ((!dev->irq) || (!dev->irq_enabled))  		return -EINVAL; @@ -250,13 +470,13 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr  	}  	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)) +	if (crtc >= dev->num_crtcs)  		return -EINVAL; -	seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 -			  : &dev->vbl_received); +	drm_update_vblank_count(dev, crtc); +	seq = drm_vblank_count(dev, crtc);  	switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) {  	case _DRM_VBLANK_RELATIVE: @@ -275,8 +495,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr  	if (flags & _DRM_VBLANK_SIGNAL) {  		unsigned long irqflags; -		struct list_head *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY) -				      ? &dev->vbl_sigs2 : &dev->vbl_sigs; +		struct list_head *vbl_sigs = &dev->vbl_sigs[crtc];  		struct drm_vbl_sig *vbl_sig;  		spin_lock_irqsave(&dev->vbl_lock, irqflags); @@ -297,22 +516,26 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr  			}  		} -		if (dev->vbl_pending >= 100) { +		if (atomic_read(&dev->vbl_signal_pending) >= 100) {  			spin_unlock_irqrestore(&dev->vbl_lock, irqflags);  			return -EBUSY;  		} -		dev->vbl_pending++; -  		spin_unlock_irqrestore(&dev->vbl_lock, irqflags); -		if (! -		    (vbl_sig = -		     drm_alloc(sizeof(struct drm_vbl_sig), DRM_MEM_DRIVER))) { +		vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig), +				     DRM_MEM_DRIVER); +		if (!vbl_sig)  			return -ENOMEM; + +		ret = drm_vblank_get(dev, crtc); +		if (ret) { +			drm_free(vbl_sig, sizeof(struct drm_vbl_sig), +				 DRM_MEM_DRIVER); +			return ret;  		} -		memset((void *)vbl_sig, 0, sizeof(*vbl_sig)); +		atomic_inc(&dev->vbl_signal_pending);  		vbl_sig->sequence = vblwait->request.sequence;  		vbl_sig->info.si_signo = vblwait->request.signal; @@ -326,17 +549,20 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr  		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; + +		ret = drm_vblank_get(dev, crtc); +		if (ret) +			return ret; +		DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, +			    (((cur_vblank = drm_vblank_count(dev, crtc)) +			      - vblwait->request.sequence) <= (1 << 23))); +		drm_vblank_put(dev, crtc);  		do_gettimeofday(&now); +  		vblwait->reply.tval_sec = now.tv_sec;  		vblwait->reply.tval_usec = now.tv_usec; +		vblwait->reply.sequence = cur_vblank;  	}        done: @@ -347,43 +573,57 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr   * Send the VBLANK signals.   *   * \param dev DRM device. + * \param crtc CRTC where the vblank event occurred   *   * Sends a signal for each task in drm_device::vbl_sigs and empties the list.   *   * If a signal is not requested, then calls vblank_wait().   */ -void drm_vbl_send_signals(struct drm_device * dev) +static void drm_vbl_send_signals(struct drm_device * dev, int crtc)  { +	struct drm_vbl_sig *vbl_sig, *tmp; +	struct list_head *vbl_sigs; +	unsigned int vbl_seq;  	unsigned long flags; -	int i;  	spin_lock_irqsave(&dev->vbl_lock, flags); -	for (i = 0; i < 2; i++) { -		struct drm_vbl_sig *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); - -		list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { -			if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { -				vbl_sig->info.si_code = vbl_seq; -				send_sig_info(vbl_sig->info.si_signo, -					      &vbl_sig->info, vbl_sig->task); +	vbl_sigs = &dev->vbl_sigs[crtc]; +	vbl_seq = drm_vblank_count(dev, crtc); -				list_del(&vbl_sig->head); +	list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { +	    if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { +		vbl_sig->info.si_code = vbl_seq; +		send_sig_info(vbl_sig->info.si_signo, +			      &vbl_sig->info, vbl_sig->task); -				drm_free(vbl_sig, sizeof(*vbl_sig), -					 DRM_MEM_DRIVER); +		list_del(&vbl_sig->head); -				dev->vbl_pending--; -			} -		} +		drm_free(vbl_sig, sizeof(*vbl_sig), +			 DRM_MEM_DRIVER); +		atomic_dec(&dev->vbl_signal_pending); +		drm_vblank_put(dev, crtc); +	    }  	}  	spin_unlock_irqrestore(&dev->vbl_lock, flags);  } -EXPORT_SYMBOL(drm_vbl_send_signals); + +/** + * drm_handle_vblank - handle a vblank event + * @dev: DRM device + * @crtc: where this event occurred + * + * Drivers should call this routine in their vblank interrupt handlers to + * update the vblank counter and send any signals that may be pending. + */ +void drm_handle_vblank(struct drm_device *dev, int crtc) +{ +	drm_update_vblank_count(dev, crtc); +	DRM_WAKEUP(&dev->vbl_queue[crtc]); +	drm_vbl_send_signals(dev, crtc); +} +EXPORT_SYMBOL(drm_handle_vblank);  /**   * Tasklet wrapper function. diff --git a/linux-core/i915_drv.c b/linux-core/i915_drv.c index a8ce0f5e..a5f60ee1 100644 --- a/linux-core/i915_drv.c +++ b/linux-core/i915_drv.c @@ -536,8 +536,7 @@ static struct drm_driver driver = {  	 */  	.driver_features =  	    DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | /* DRIVER_USE_MTRR | */ -	    DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL | -	    DRIVER_IRQ_VBL2, +	    DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,  	.load = i915_driver_load,  	.unload = i915_driver_unload,  	.firstopen = i915_driver_firstopen, @@ -546,8 +545,9 @@ static struct drm_driver driver = {  	.suspend = i915_suspend,  	.resume = i915_resume,  	.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, diff --git a/linux-core/mach64_drv.c b/linux-core/mach64_drv.c index 9709934d..16bc9ff3 100644 --- a/linux-core/mach64_drv.c +++ b/linux-core/mach64_drv.c @@ -42,9 +42,11 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent);  static struct drm_driver driver = {  	.driver_features =  	    DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_HAVE_DMA -	    | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, +	    | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,  	.lastclose = mach64_driver_lastclose, -	.vblank_wait = mach64_driver_vblank_wait, +	.get_vblank_counter = mach64_get_vblank_counter, +	.enable_vblank = mach64_enable_vblank, +	.disable_vblank = mach64_disable_vblank,  	.irq_preinstall = mach64_driver_irq_preinstall,  	.irq_postinstall = mach64_driver_irq_postinstall,  	.irq_uninstall = mach64_driver_irq_uninstall, diff --git a/linux-core/mga_drv.c b/linux-core/mga_drv.c index bab90aa5..14a0be45 100644 --- a/linux-core/mga_drv.c +++ b/linux-core/mga_drv.c @@ -46,15 +46,16 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent);  static struct drm_driver driver = {  	.driver_features =  	    DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | -	    DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | -	    DRIVER_IRQ_VBL, +	    DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,  	.dev_priv_size = sizeof (drm_mga_buf_priv_t),  	.load = mga_driver_load,  	.unload = mga_driver_unload,  	.lastclose = mga_driver_lastclose,  	.dma_quiescent = mga_driver_dma_quiescent,  	.device_is_agp = mga_driver_device_is_agp, -	.vblank_wait = mga_driver_vblank_wait, +	.get_vblank_counter = mga_get_vblank_counter, +	.enable_vblank = mga_enable_vblank, +	.disable_vblank = mga_disable_vblank,  	.irq_preinstall = mga_driver_irq_preinstall,  	.irq_postinstall = mga_driver_irq_postinstall,  	.irq_uninstall = mga_driver_irq_uninstall, diff --git a/linux-core/r128_drv.c b/linux-core/r128_drv.c index ef4a5cbd..7b6159b9 100644 --- a/linux-core/r128_drv.c +++ b/linux-core/r128_drv.c @@ -44,12 +44,13 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent);  static struct drm_driver driver = {  	.driver_features =  	    DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | -	    DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | -	    DRIVER_IRQ_VBL, +	    DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED,  	.dev_priv_size = sizeof(drm_r128_buf_priv_t),  	.preclose = r128_driver_preclose,  	.lastclose = r128_driver_lastclose, -	.vblank_wait = r128_driver_vblank_wait, +	.get_vblank_counter = r128_get_vblank_counter, +	.enable_vblank = r128_enable_vblank, +	.disable_vblank = r128_disable_vblank,  	.irq_preinstall = r128_driver_irq_preinstall,  	.irq_postinstall = r128_driver_irq_postinstall,  	.irq_uninstall = r128_driver_irq_uninstall, diff --git a/linux-core/radeon_drv.c b/linux-core/radeon_drv.c index d847f3cd..f0f3320e 100644 --- a/linux-core/radeon_drv.c +++ b/linux-core/radeon_drv.c @@ -60,8 +60,7 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent);  static struct drm_driver driver = {  	.driver_features =  	    DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_PCI_DMA | DRIVER_SG | -	    DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED | -	    DRIVER_IRQ_VBL | DRIVER_IRQ_VBL2, +	    DRIVER_HAVE_IRQ | DRIVER_HAVE_DMA | DRIVER_IRQ_SHARED,  	.dev_priv_size = sizeof(drm_radeon_buf_priv_t),  	.load = radeon_driver_load,  	.firstopen = radeon_driver_firstopen, @@ -70,8 +69,9 @@ static struct drm_driver driver = {  	.postclose = radeon_driver_postclose,  	.lastclose = radeon_driver_lastclose,  	.unload = radeon_driver_unload, -	.vblank_wait = radeon_driver_vblank_wait, -	.vblank_wait2 = radeon_driver_vblank_wait2, +	.get_vblank_counter = radeon_get_vblank_counter, +	.enable_vblank = radeon_enable_vblank, +	.disable_vblank = radeon_disable_vblank,  	.dri_library_name = dri_library_name,  	.irq_preinstall = radeon_driver_irq_preinstall,  	.irq_postinstall = radeon_driver_irq_postinstall, diff --git a/shared-core/drm.h b/shared-core/drm.h index a551e4c2..52de596b 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -555,6 +555,21 @@ union drm_wait_vblank {  	struct drm_wait_vblank_reply reply;  }; +typedef enum { +	_DRM_PRE_MODESET = 1, +	_DRM_POST_MODESET = 2, +} drm_modeset_ctl_cmd_t; + +/** + * DRM_IOCTL_MODESET_CTL ioctl argument type + * + * \sa drmModesetCtl(). + */ +typedef struct drm_modeset_ctl { +	unsigned long arg; +	drm_modeset_ctl_cmd_t cmd; +} drm_modeset_ctl_t; +  /**   * DRM_IOCTL_AGP_ENABLE ioctl argument type.   * @@ -1027,6 +1042,8 @@ struct drm_mm_init_arg {  #define DRM_IOCTL_BO_VERSION          DRM_IOR(0xd6, struct drm_bo_version_arg) +#define DRM_IOCTL_MODESET_CTL           DRM_IOW(0xa0, drm_modeset_ctl_t) +  /*@}*/  /** diff --git a/shared-core/i915_drv.h b/shared-core/i915_drv.h index e8364b85..b8d027d7 100644 --- a/shared-core/i915_drv.h +++ b/shared-core/i915_drv.h @@ -258,21 +258,22 @@ extern int i915_irq_emit(struct drm_device *dev, void *data,  extern int i915_irq_wait(struct drm_device *dev, void *data,  			 struct drm_file *file_priv); -extern int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence); -extern int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence);  extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS);  extern void i915_driver_irq_preinstall(struct drm_device * dev); -extern void i915_driver_irq_postinstall(struct drm_device * dev); +extern int i915_driver_irq_postinstall(struct drm_device * dev);  extern void i915_driver_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,  				struct drm_file *file_priv); -extern int i915_emit_irq(struct drm_device *dev); -extern void i915_user_irq_on(drm_i915_private_t *dev_priv); -extern void i915_user_irq_off(drm_i915_private_t *dev_priv); +extern int i915_emit_irq(struct drm_device * dev); +extern int i915_enable_vblank(struct drm_device *dev, int crtc); +extern void i915_disable_vblank(struct drm_device *dev, int crtc); +extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc);  extern int i915_vblank_swap(struct drm_device *dev, void *data,  			    struct drm_file *file_priv); +extern void i915_user_irq_on(drm_i915_private_t *dev_priv); +extern void i915_user_irq_off(drm_i915_private_t *dev_priv);  /* i915_mem.c */  extern int i915_mem_alloc(struct drm_device *dev, void *data, @@ -472,6 +473,36 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);  #define I915REG_PIPEASTAT	0x70024  #define I915REG_PIPEBSTAT	0x71024 +/* + * The two pipe frame counter registers are not synchronized, so + * reading a stable value is somewhat tricky. The following code  + * should work: + * + *  do { + *    high1 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> + *             PIPE_FRAME_HIGH_SHIFT; + *    low1 =  ((INREG(PIPEAFRAMEPIXEL) & PIPE_FRAME_LOW_MASK) >> + *             PIPE_FRAME_LOW_SHIFT); + *    high2 = ((INREG(PIPEAFRAMEHIGH) & PIPE_FRAME_HIGH_MASK) >> + *             PIPE_FRAME_HIGH_SHIFT); + *  } while (high1 != high2); + *  frame = (high1 << 8) | low1; + */ +#define PIPEAFRAMEHIGH          0x70040 +#define PIPEBFRAMEHIGH		0x71040 +#define PIPE_FRAME_HIGH_MASK    0x0000ffff +#define PIPE_FRAME_HIGH_SHIFT   0 +#define PIPEAFRAMEPIXEL         0x70044 +#define PIPEBFRAMEPIXEL		0x71044 + +#define PIPE_FRAME_LOW_MASK     0xff000000 +#define PIPE_FRAME_LOW_SHIFT    24 +/* + * Pixel within the current frame is counted in the PIPEAFRAMEPIXEL register + * and is 24 bits wide. + */ +#define PIPE_PIXEL_MASK         0x00ffffff +#define PIPE_PIXEL_SHIFT        0  #define I915_VBLANK_INTERRUPT_ENABLE	(1UL<<17)  #define I915_VBLANK_CLEAR		(1UL<<1) diff --git a/shared-core/i915_irq.c b/shared-core/i915_irq.c index 4a968002..9b46b127 100644 --- a/shared-core/i915_irq.c +++ b/shared-core/i915_irq.c @@ -114,8 +114,7 @@ static void i915_vblank_tasklet(struct drm_device *dev)  	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;  	struct list_head *list, *tmp, hits, *hit;  	int nhits, nrects, slice[2], upper[2], lower[2], i, num_pages; -	unsigned counter[2] = { atomic_read(&dev->vbl_received), -				atomic_read(&dev->vbl_received2) }; +	unsigned counter[2];  	struct drm_drawable_info *drw;  	drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv;  	u32 cpp = dev_priv->cpp,  offsets[3]; @@ -127,6 +126,9 @@ static void i915_vblank_tasklet(struct drm_device *dev)  			  (cpp << 23) | (1 << 24);  	RING_LOCALS; +	counter[0] = drm_vblank_count(dev, 0); +	counter[1] = drm_vblank_count(dev, 1); +  	DRM_DEBUG("\n");  	INIT_LIST_HEAD(&hits); @@ -144,13 +146,14 @@ static void i915_vblank_tasklet(struct drm_device *dev)  	list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) {  		drm_i915_vbl_swap_t *vbl_swap =  			list_entry(list, drm_i915_vbl_swap_t, head); -		int pipe = i915_get_pipe(dev, vbl_swap->plane); +		int crtc = i915_get_pipe(dev, vbl_swap->plane); -		if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) +		if ((counter[crtc] - vbl_swap->sequence) > (1<<23))  			continue;  		list_del(list);  		dev_priv->swaps_pending--; +		drm_vblank_put(dev, crtc);  		DRM_SPINUNLOCK(&dev_priv->swaps_lock);  		DRM_SPINLOCK(&dev->drw_lock); @@ -301,6 +304,32 @@ static void i915_vblank_tasklet(struct drm_device *dev)  	}  } +u32 i915_get_vblank_counter(struct drm_device *dev, int crtc) +{ +	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; +	unsigned long high_frame = crtc ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; +	unsigned long low_frame = crtc ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; +	u32 high1, high2, low, count; + +	/* +	 * High & low register fields aren't synchronized, so make sure +	 * we get a low value that's stable across two reads of the high +	 * register. +	 */ +	do { +		high1 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> +			 PIPE_FRAME_HIGH_SHIFT); +		low =  ((I915_READ(low_frame) & PIPE_FRAME_LOW_MASK) >> +			PIPE_FRAME_LOW_SHIFT); +		high2 = ((I915_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >> +			 PIPE_FRAME_HIGH_SHIFT); +	} while (high1 != high2); + +	count = (high1 << 8) | low; + +	return count; +} +  irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  {  	struct drm_device *dev = (struct drm_device *) arg; @@ -312,7 +341,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  	pipeb_stats = I915_READ(I915REG_PIPEBSTAT);  	temp = I915_READ16(I915REG_INT_IDENTITY_R); -	temp &= (dev_priv->irq_enable_reg | USER_INT_FLAG);  #if 0  	DRM_DEBUG("flag=%08x\n", temp); @@ -321,8 +349,10 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  		return IRQ_NONE;  	I915_WRITE16(I915REG_INT_IDENTITY_R, temp); -	(void) I915_READ16(I915REG_INT_IDENTITY_R); -	DRM_READMEMORYBARRIER(); +	(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);  	dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); @@ -333,25 +363,16 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)  #endif  	} -	if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) { -		int vblank_pipe = dev_priv->vblank_pipe; - -		if ((vblank_pipe & -		     (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) -		    == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { -			if (temp & VSYNC_PIPEA_FLAG) -				atomic_inc(&dev->vbl_received); -			if (temp & VSYNC_PIPEB_FLAG) -				atomic_inc(&dev->vbl_received2); -		} else if (((temp & VSYNC_PIPEA_FLAG) && -			    (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) || -			   ((temp & VSYNC_PIPEB_FLAG) && -			    (vblank_pipe & DRM_I915_VBLANK_PIPE_B))) -			atomic_inc(&dev->vbl_received); - -		DRM_WAKEUP(&dev->vbl_queue); -		drm_vbl_send_signals(dev); +	/* +	 * Use drm_update_vblank_counter here to deal with potential lost +	 * interrupts +	 */ +	if (temp & VSYNC_PIPEA_FLAG) +		drm_handle_vblank(dev, 0); +	if (temp & VSYNC_PIPEB_FLAG) +		drm_handle_vblank(dev, 1); +	if (temp & (VSYNC_PIPEA_FLAG | VSYNC_PIPEB_FLAG)) {  		if (dev_priv->swaps_pending > 0)  			drm_locked_tasklet(dev, i915_vblank_tasklet);  		I915_WRITE(I915REG_PIPEASTAT, @@ -431,38 +452,6 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)  	return ret;  } -static int i915_driver_vblank_do_wait(struct drm_device *dev, -				      unsigned int *sequence, -				      atomic_t *counter) -{ -	drm_i915_private_t *dev_priv = dev->dev_private; -	unsigned int cur_vblank; -	int ret = 0; - -	if (!dev_priv) { -		DRM_ERROR("called with no initialization\n"); -		return -EINVAL; -	} - -	DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, -		    (((cur_vblank = atomic_read(counter)) -			- *sequence) <= (1<<23))); - -	*sequence = cur_vblank; - -	return ret; -} - -int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence) -{ -	return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received); -} - -int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence) -{ -	return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2); -} -  /* Needs the lock as it touches the ring.   */  int i915_irq_emit(struct drm_device *dev, void *data, @@ -505,15 +494,53 @@ int i915_irq_wait(struct drm_device *dev, void *data,  	return i915_wait_irq(dev, irqwait->irq_seq);  } -static void i915_enable_interrupt (struct drm_device *dev) +int i915_enable_vblank(struct drm_device *dev, int crtc)  {  	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; -	dev_priv->irq_enable_reg = USER_INT_FLAG; -	if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A) +	switch (crtc) { +	case 0:  		dev_priv->irq_enable_reg |= VSYNC_PIPEA_FLAG; -	if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) +		break; +	case 1:  		dev_priv->irq_enable_reg |= VSYNC_PIPEB_FLAG; +		break; +	default: +		DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", +			  crtc); +		break; +	} + +	I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); + +	return 0; +} + +void i915_disable_vblank(struct drm_device *dev, int crtc) +{ +	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + +	switch (crtc) { +	case 0: +		dev_priv->irq_enable_reg &= ~VSYNC_PIPEA_FLAG; +		break; +	case 1: +		dev_priv->irq_enable_reg &= ~VSYNC_PIPEB_FLAG; +		break; +	default: +		DRM_ERROR("tried to disable vblank on non-existent crtc %d\n", +			  crtc); +		break; +	} + +	I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg); +} + +static void i915_enable_interrupt (struct drm_device *dev) +{ +	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; +	 +	dev_priv->irq_enable_reg |= USER_INT_FLAG;  	I915_WRITE16(I915REG_INT_ENABLE_R, dev_priv->irq_enable_reg);  	dev_priv->irq_enabled = 1; @@ -539,8 +566,6 @@ int i915_vblank_pipe_set(struct drm_device *dev, void *data,  	dev_priv->vblank_pipe = pipe->pipe; -	i915_enable_interrupt (dev); -  	return 0;  } @@ -578,6 +603,7 @@ int i915_vblank_swap(struct drm_device *dev, void *data,  	unsigned int pipe, seqtype, curseq, plane;  	unsigned long irqflags;  	struct list_head *list; +	int ret;  	if (!dev_priv) {  		DRM_ERROR("%s called with no initialization\n", __func__); @@ -621,7 +647,8 @@ int i915_vblank_swap(struct drm_device *dev, void *data,  	DRM_SPINUNLOCK_IRQRESTORE(&dev->drw_lock, irqflags); -	curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); +	drm_update_vblank_count(dev, pipe); +	curseq = drm_vblank_count(dev, pipe);  	if (seqtype == _DRM_VBLANK_RELATIVE)  		swap->sequence += curseq; @@ -694,6 +721,12 @@ int i915_vblank_swap(struct drm_device *dev, void *data,  	DRM_DEBUG("\n"); +	ret = drm_vblank_get(dev, pipe); +	if (ret) { +		drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); +		return ret; +	} +  	vbl_swap->drw_id = swap->drawable;  	vbl_swap->plane = plane;  	vbl_swap->sequence = swap->sequence; @@ -723,9 +756,10 @@ void i915_driver_irq_preinstall(struct drm_device * dev)  	I915_WRITE16(I915REG_INT_ENABLE_R, 0x0);  } -void 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; +	int ret, num_pipes = 2;  	DRM_SPININIT(&dev_priv->swaps_lock, "swap");  	INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); @@ -733,6 +767,13 @@ void i915_driver_irq_postinstall(struct drm_device * dev)  	DRM_SPININIT(&dev_priv->user_irq_lock, "userirq");  	dev_priv->user_irq_refcount = 0; +	dev_priv->irq_enable_reg = 0; + +	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); @@ -742,6 +783,7 @@ void i915_driver_irq_postinstall(struct drm_device * dev)  	 */  	I915_WRITE(I915REG_INSTPM, (1 << 5) | (1 << 21)); +	return 0;  }  void i915_driver_irq_uninstall(struct drm_device * dev) diff --git a/shared-core/mach64_drv.h b/shared-core/mach64_drv.h index 91553ce6..fb8a7724 100644 --- a/shared-core/mach64_drv.h +++ b/shared-core/mach64_drv.h @@ -96,6 +96,8 @@ typedef struct drm_mach64_private {  	unsigned int depth_bpp;  	unsigned int depth_offset, depth_pitch; +	atomic_t vbl_received;          /**< Number of vblanks received. */ +  	u32 front_offset_pitch;  	u32 back_offset_pitch;  	u32 depth_offset_pitch; @@ -160,13 +162,14 @@ extern int mach64_dma_blit(struct drm_device *dev, void *data,  			   struct drm_file *file_priv);  extern int mach64_get_param(struct drm_device *dev, void *data,  			    struct drm_file *file_priv); -extern int mach64_driver_vblank_wait(struct drm_device * dev, -				     unsigned int *sequence); +extern u32 mach64_get_vblank_counter(struct drm_device *dev, int crtc); +extern int mach64_enable_vblank(struct drm_device *dev, int crtc); +extern void mach64_disable_vblank(struct drm_device *dev, int crtc);  extern irqreturn_t mach64_driver_irq_handler(DRM_IRQ_ARGS); -extern void mach64_driver_irq_preinstall(struct drm_device * dev); -extern void mach64_driver_irq_postinstall(struct drm_device * dev); -extern void mach64_driver_irq_uninstall(struct drm_device * dev); +extern void mach64_driver_irq_preinstall(struct drm_device *dev); +extern int mach64_driver_irq_postinstall(struct drm_device *dev); +extern void mach64_driver_irq_uninstall(struct drm_device *dev);  /* ================================================================   * Registers diff --git a/shared-core/mach64_irq.c b/shared-core/mach64_irq.c index 4122dd91..2d522a6c 100644 --- a/shared-core/mach64_irq.c +++ b/shared-core/mach64_irq.c @@ -42,9 +42,8 @@  irqreturn_t mach64_driver_irq_handler(DRM_IRQ_ARGS)  { -	struct drm_device *dev = (struct drm_device *) arg; -	drm_mach64_private_t *dev_priv = -	    (drm_mach64_private_t *) dev->dev_private; +	struct drm_device *dev = arg; +	drm_mach64_private_t *dev_priv = dev->dev_private;  	int status;  	status = MACH64_READ(MACH64_CRTC_INT_CNTL); @@ -62,74 +61,81 @@ irqreturn_t mach64_driver_irq_handler(DRM_IRQ_ARGS)  			     (status & ~MACH64_CRTC_INT_ACKS)  			     | MACH64_CRTC_VBLANK_INT); -		atomic_inc(&dev->vbl_received); -		DRM_WAKEUP(&dev->vbl_queue); -		drm_vbl_send_signals(dev); +		atomic_inc(&dev_priv->vbl_received); +		drm_handle_vblank(dev, 0);  		return IRQ_HANDLED;  	}  	return IRQ_NONE;  } -int mach64_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) +u32 mach64_get_vblank_counter(struct drm_device * dev, int crtc)  { -	unsigned int cur_vblank; -	int ret = 0; - -	/* Assume that the user has missed the current sequence number -	 * by about a day rather than she wants to wait for years -	 * using vertical blanks... -	 */ -	DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, -		    (((cur_vblank = atomic_read(&dev->vbl_received)) -		      - *sequence) <= (1 << 23))); +	const drm_mach64_private_t *const dev_priv = dev->dev_private; +		 +	if (crtc != 0) { +		return 0; +	} +	 +	return atomic_read(&dev_priv->vbl_received); +} -	*sequence = cur_vblank; +int mach64_enable_vblank(struct drm_device * dev, int crtc) +{ +	drm_mach64_private_t *dev_priv = dev->dev_private; +	u32 status = MACH64_READ(MACH64_CRTC_INT_CNTL); +	 +	if (crtc != 0) { +		DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", crtc); +		return 0; +	} +	 +	DRM_DEBUG("before enable vblank CRTC_INT_CTNL: 0x%08x\n", status); +	 +	/* Turn on VBLANK interrupt */ +	MACH64_WRITE(MACH64_CRTC_INT_CNTL, MACH64_READ(MACH64_CRTC_INT_CNTL) +		     | MACH64_CRTC_VBLANK_INT_EN); -	return ret; +	return 0;  } -/* drm_dma.h hooks -*/ -void mach64_driver_irq_preinstall(struct drm_device * dev) -{ -	drm_mach64_private_t *dev_priv = -	    (drm_mach64_private_t *) dev->dev_private; +void mach64_disable_vblank(struct drm_device * dev, int crtc) +{ +	drm_mach64_private_t *dev_priv = dev->dev_private;  	u32 status = MACH64_READ(MACH64_CRTC_INT_CNTL); -	DRM_DEBUG("before install CRTC_INT_CTNL: 0x%08x\n", status); +	DRM_DEBUG("before disable vblank CRTC_INT_CTNL: 0x%08x\n", status);  	/* Disable and clear VBLANK interrupt */  	MACH64_WRITE(MACH64_CRTC_INT_CNTL, (status & ~MACH64_CRTC_VBLANK_INT_EN)  		     | MACH64_CRTC_VBLANK_INT);  } -void mach64_driver_irq_postinstall(struct drm_device * dev) +/* drm_dma.h hooks +*/ +void mach64_driver_irq_preinstall(struct drm_device * dev)  { -	drm_mach64_private_t *dev_priv = -	    (drm_mach64_private_t *) dev->dev_private; +	drm_mach64_private_t *dev_priv = dev->dev_private; -	/* Turn on VBLANK interrupt */ -	MACH64_WRITE(MACH64_CRTC_INT_CNTL, MACH64_READ(MACH64_CRTC_INT_CNTL) -		     | MACH64_CRTC_VBLANK_INT_EN); +	u32 status = MACH64_READ(MACH64_CRTC_INT_CNTL); -	DRM_DEBUG("after install CRTC_INT_CTNL: 0x%08x\n", -		  MACH64_READ(MACH64_CRTC_INT_CNTL)); +	DRM_DEBUG("before install CRTC_INT_CTNL: 0x%08x\n", status); +	mach64_disable_vblank(dev,0); +} + +int mach64_driver_irq_postinstall(struct drm_device * dev) +{ +	return drm_vblank_init(dev, 1);  }  void mach64_driver_irq_uninstall(struct drm_device * dev)  { -	drm_mach64_private_t *dev_priv = -	    (drm_mach64_private_t *) dev->dev_private; +	drm_mach64_private_t *dev_priv = dev->dev_private;  	if (!dev_priv)  		return; -	/* Disable and clear VBLANK interrupt */ -	MACH64_WRITE(MACH64_CRTC_INT_CNTL, -		     (MACH64_READ(MACH64_CRTC_INT_CNTL) & -		      ~MACH64_CRTC_VBLANK_INT_EN) -		     | MACH64_CRTC_VBLANK_INT); +	mach64_disable_vblank(dev, 0);  	DRM_DEBUG("after uninstall CRTC_INT_CTNL: 0x%08x\n",  		  MACH64_READ(MACH64_CRTC_INT_CNTL)); diff --git a/shared-core/mga_drv.h b/shared-core/mga_drv.h index 9fef92d6..bf3be808 100644 --- a/shared-core/mga_drv.h +++ b/shared-core/mga_drv.h @@ -113,13 +113,14 @@ typedef struct drm_mga_private {  	 * \sa drm_mga_private_t::mmio  	 */  	/*@{*/ -	u32 mmio_base;             /**< Bus address of base of MMIO. */ -	u32 mmio_size;             /**< Size of the MMIO region. */ +	u32 mmio_base;			/**< Bus address of base of MMIO. */ +	u32 mmio_size;			/**< Size of the MMIO region. */  	/*@}*/  	u32 clear_cmd;  	u32 maccess; +	atomic_t vbl_received;		/**< Number of vblanks received. */  	wait_queue_head_t fence_queue;  	atomic_t last_fence_retired;  	u32 next_fence_to_post; @@ -181,11 +182,14 @@ extern int mga_warp_install_microcode(drm_mga_private_t * dev_priv);  extern int mga_warp_init(drm_mga_private_t * dev_priv);  				/* mga_irq.c */ +extern int mga_enable_vblank(struct drm_device *dev, int crtc); +extern void mga_disable_vblank(struct drm_device *dev, int crtc); +extern u32 mga_get_vblank_counter(struct drm_device *dev, int crtc);  extern int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence);  extern int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence);  extern irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS);  extern void mga_driver_irq_preinstall(struct drm_device * dev); -extern void mga_driver_irq_postinstall(struct drm_device * dev); +extern int mga_driver_irq_postinstall(struct drm_device * dev);  extern void mga_driver_irq_uninstall(struct drm_device * dev);  extern long mga_compat_ioctl(struct file *filp, unsigned int cmd,  			     unsigned long arg); diff --git a/shared-core/mga_irq.c b/shared-core/mga_irq.c index 0f83577f..4fe86322 100644 --- a/shared-core/mga_irq.c +++ b/shared-core/mga_irq.c @@ -36,6 +36,20 @@  #include "mga_drm.h"  #include "mga_drv.h" +u32 mga_get_vblank_counter(struct drm_device *dev, int crtc) +{ +	const drm_mga_private_t *const dev_priv =  +		(drm_mga_private_t *) dev->dev_private; + +	if (crtc != 0) { +		return 0; +	} + + +	return atomic_read(&dev_priv->vbl_received); +} + +  irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)  {  	struct drm_device *dev = (struct drm_device *) arg; @@ -48,9 +62,8 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)  	/* VBLANK interrupt */  	if (status & MGA_VLINEPEN) {  		MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR); -		atomic_inc(&dev->vbl_received); -		DRM_WAKEUP(&dev->vbl_queue); -		drm_vbl_send_signals(dev); +		atomic_inc(&dev_priv->vbl_received); +		drm_handle_vblank(dev, 0);  		handled = 1;  	} @@ -79,22 +92,34 @@ irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)  	return IRQ_NONE;  } -int mga_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) +int mga_enable_vblank(struct drm_device *dev, int crtc)  { -	unsigned int cur_vblank; -	int ret = 0; +	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; -	/* Assume that the user has missed the current sequence number -	 * by about a day rather than she wants to wait for years -	 * using vertical blanks... -	 */ -	DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, -		    (((cur_vblank = atomic_read(&dev->vbl_received)) -		      - *sequence) <= (1 << 23))); +	if (crtc != 0) { +		DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", +			  crtc); +		return 0; +	} -	*sequence = cur_vblank; +	MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); +	return 0; +} -	return ret; + +void mga_disable_vblank(struct drm_device *dev, int crtc) +{ +	if (crtc != 0) { +		DRM_ERROR("tried to disable vblank on non-existent crtc %d\n", +			  crtc); +	} + +	/* Do *NOT* disable the vertical refresh interrupt.  MGA doesn't have +	 * a nice hardware counter that tracks the number of refreshes when +	 * the interrupt is disabled, and the kernel doesn't know the refresh +	 * rate to calculate an estimate. +	 */ +	/* MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); */  }  int mga_driver_fence_wait(struct drm_device * dev, unsigned int *sequence) @@ -126,14 +151,22 @@ void mga_driver_irq_preinstall(struct drm_device * dev)  	MGA_WRITE(MGA_ICLEAR, ~0);  } -void mga_driver_irq_postinstall(struct drm_device * dev) +int mga_driver_irq_postinstall(struct drm_device * dev)  {  	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; +	int ret; + +	ret = drm_vblank_init(dev, 1); +	if (ret) +		return ret;  	DRM_INIT_WAITQUEUE(&dev_priv->fence_queue); -	/* Turn on vertical blank interrupt and soft trap interrupt. */ -	MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); +	/* Turn on soft trap interrupt.  Vertical blank interrupts are enabled +	 * in mga_enable_vblank. +	 */ +	MGA_WRITE(MGA_IEN, MGA_SOFTRAPEN); +	return 0;  }  void mga_driver_irq_uninstall(struct drm_device * dev) diff --git a/shared-core/nouveau_drv.h b/shared-core/nouveau_drv.h index 85a0d0b5..4184aa5b 100644 --- a/shared-core/nouveau_drv.h +++ b/shared-core/nouveau_drv.h @@ -432,7 +432,7 @@ extern int nouveau_ioctl_gpuobj_free(struct drm_device *, void *data,  /* nouveau_irq.c */  extern irqreturn_t nouveau_irq_handler(DRM_IRQ_ARGS);  extern void        nouveau_irq_preinstall(struct drm_device *); -extern void        nouveau_irq_postinstall(struct drm_device *); +extern int         nouveau_irq_postinstall(struct drm_device *);  extern void        nouveau_irq_uninstall(struct drm_device *);  /* nouveau_sgdma.c */ diff --git a/shared-core/nouveau_irq.c b/shared-core/nouveau_irq.c index 500fda2f..bceb81ab 100644 --- a/shared-core/nouveau_irq.c +++ b/shared-core/nouveau_irq.c @@ -46,13 +46,15 @@ nouveau_irq_preinstall(struct drm_device *dev)  	NV_WRITE(NV03_PMC_INTR_EN_0, 0);  } -void +int  nouveau_irq_postinstall(struct drm_device *dev)  {  	struct drm_nouveau_private *dev_priv = dev->dev_private;  	/* Master enable */  	NV_WRITE(NV03_PMC_INTR_EN_0, NV_PMC_INTR_EN_0_MASTER_ENABLE); + +	return 0;  }  void diff --git a/shared-core/r128_drv.h b/shared-core/r128_drv.h index 6d65f378..ab8b6297 100644 --- a/shared-core/r128_drv.h +++ b/shared-core/r128_drv.h @@ -97,6 +97,8 @@ typedef struct drm_r128_private {  	u32 crtc_offset;  	u32 crtc_offset_cntl; +	atomic_t vbl_received; +  	u32 color_fmt;  	unsigned int front_offset;  	unsigned int front_pitch; @@ -149,11 +151,12 @@ extern int r128_wait_ring(drm_r128_private_t * dev_priv, int n);  extern int r128_do_cce_idle(drm_r128_private_t * dev_priv);  extern int r128_do_cleanup_cce(struct drm_device * dev); -extern int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); - +extern int r128_enable_vblank(struct drm_device *dev, int crtc); +extern void r128_disable_vblank(struct drm_device *dev, int crtc); +extern u32 r128_get_vblank_counter(struct drm_device *dev, int crtc);  extern irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS);  extern void r128_driver_irq_preinstall(struct drm_device * dev); -extern void r128_driver_irq_postinstall(struct drm_device * dev); +extern int r128_driver_irq_postinstall(struct drm_device * dev);  extern void r128_driver_irq_uninstall(struct drm_device * dev);  extern void r128_driver_lastclose(struct drm_device * dev);  extern void r128_driver_preclose(struct drm_device * dev, diff --git a/shared-core/r128_irq.c b/shared-core/r128_irq.c index c76fdca7..5b95bd89 100644 --- a/shared-core/r128_irq.c +++ b/shared-core/r128_irq.c @@ -35,6 +35,16 @@  #include "r128_drm.h"  #include "r128_drv.h" +u32 r128_get_vblank_counter(struct drm_device *dev, int crtc) +{ +	const drm_r128_private_t *dev_priv = dev->dev_private; + +	if (crtc != 0) +		return 0; + +	return atomic_read(&dev_priv->vbl_received); +} +  irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS)  {  	struct drm_device *dev = (struct drm_device *) arg; @@ -46,30 +56,38 @@ irqreturn_t r128_driver_irq_handler(DRM_IRQ_ARGS)  	/* VBLANK interrupt */  	if (status & R128_CRTC_VBLANK_INT) {  		R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK); -		atomic_inc(&dev->vbl_received); -		DRM_WAKEUP(&dev->vbl_queue); -		drm_vbl_send_signals(dev); +		atomic_inc(&dev_priv->vbl_received); +		drm_handle_vblank(dev, 0);  		return IRQ_HANDLED;  	}  	return IRQ_NONE;  } -int r128_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) +int r128_enable_vblank(struct drm_device *dev, int crtc)  { -	unsigned int cur_vblank; -	int ret = 0; +	drm_r128_private_t *dev_priv = dev->dev_private; -	/* Assume that the user has missed the current sequence number -	 * by about a day rather than she wants to wait for years -	 * using vertical blanks... -	 */ -	DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, -		    (((cur_vblank = atomic_read(&dev->vbl_received)) -		      - *sequence) <= (1 << 23))); +	if (crtc != 0) { +		DRM_ERROR("%s:  bad crtc %d\n", __FUNCTION__, crtc); +		return -EINVAL; +	} -	*sequence = cur_vblank; +	R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN); +	return 0; +} + +void r128_disable_vblank(struct drm_device *dev, int crtc) +{ +	if (crtc != 0) +		DRM_ERROR("%s:  bad crtc %d\n", __FUNCTION__, crtc); -	return ret; +	/* +	 * FIXME: implement proper interrupt disable by using the vblank +	 * counter register (if available) +	 * +	 * R128_WRITE(R128_GEN_INT_CNTL, +	 *            R128_READ(R128_GEN_INT_CNTL) & ~R128_CRTC_VBLANK_INT_EN); +	 */  }  void r128_driver_irq_preinstall(struct drm_device * dev) @@ -82,12 +100,9 @@ void r128_driver_irq_preinstall(struct drm_device * dev)  	R128_WRITE(R128_GEN_INT_STATUS, R128_CRTC_VBLANK_INT_AK);  } -void r128_driver_irq_postinstall(struct drm_device * dev) +int r128_driver_irq_postinstall(struct drm_device * dev)  { -	drm_r128_private_t *dev_priv = (drm_r128_private_t *) dev->dev_private; - -	/* Turn on VBL interrupt */ -	R128_WRITE(R128_GEN_INT_CNTL, R128_CRTC_VBLANK_INT_EN); +	return drm_vblank_init(dev, 1);  }  void r128_driver_irq_uninstall(struct drm_device * dev) diff --git a/shared-core/radeon_drv.h b/shared-core/radeon_drv.h index c7278809..bd51de14 100644 --- a/shared-core/radeon_drv.h +++ b/shared-core/radeon_drv.h @@ -305,6 +305,9 @@ typedef struct drm_radeon_private {  	u32 scratch_ages[5]; +	unsigned int crtc_last_cnt; +	unsigned int crtc2_last_cnt; +  	/* starting from here on, data is preserved accross an open */  	uint32_t flags;		/* see radeon_chip_flags */  	unsigned long fb_aper_offset; @@ -372,13 +375,13 @@ extern int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *  extern int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv);  extern void radeon_do_release(struct drm_device * dev); -extern int radeon_driver_vblank_wait(struct drm_device * dev, -				     unsigned int *sequence); -extern int radeon_driver_vblank_wait2(struct drm_device * dev, -				      unsigned int *sequence); +extern u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc); +extern int radeon_enable_vblank(struct drm_device *dev, int crtc); +extern void radeon_disable_vblank(struct drm_device *dev, int crtc); +extern void radeon_do_release(struct drm_device * dev);  extern irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS);  extern void radeon_driver_irq_preinstall(struct drm_device * dev); -extern void radeon_driver_irq_postinstall(struct drm_device * dev); +extern int radeon_driver_irq_postinstall(struct drm_device * dev);  extern void radeon_driver_irq_uninstall(struct drm_device * dev);  extern int radeon_vblank_crtc_get(struct drm_device *dev);  extern int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value); @@ -528,6 +531,12 @@ extern int r300_do_cp_cmdbuf(struct drm_device *dev,  				? DRM_READ32( dev_priv->ring_rptr, RADEON_SCRATCHOFF(x) ) \  				: RADEON_READ( RADEON_SCRATCH_REG0 + 4*(x) ) ) +#define RADEON_CRTC_CRNT_FRAME 0x0214 +#define RADEON_CRTC2_CRNT_FRAME 0x0314 + +#define RADEON_CRTC_STATUS		0x005c +#define RADEON_CRTC2_STATUS		0x03fc +  #define RADEON_GEN_INT_CNTL		0x0040  #	define RADEON_CRTC_VBLANK_MASK		(1 << 0)  #	define RADEON_CRTC2_VBLANK_MASK		(1 << 9) diff --git a/shared-core/radeon_irq.c b/shared-core/radeon_irq.c index 7e202d23..79e4e866 100644 --- a/shared-core/radeon_irq.c +++ b/shared-core/radeon_irq.c @@ -35,12 +35,61 @@  #include "radeon_drm.h"  #include "radeon_drv.h" -static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv, -					      u32 mask) +static void radeon_irq_set_state(struct drm_device *dev, u32 mask, int state)  { -	u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS) & mask; +	drm_radeon_private_t *dev_priv = dev->dev_private; + +	if (state) +		dev_priv->irq_enable_reg |= mask; +	else +		dev_priv->irq_enable_reg &= ~mask; + +	RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); +} + +int radeon_enable_vblank(struct drm_device *dev, int crtc) +{ +	switch (crtc) { +	case 0: +		radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 1); +		break; +	case 1: +		radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 1); +		break; +	default: +		DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", +			  crtc); +		return EINVAL; +	} + +	return 0; +} + +void radeon_disable_vblank(struct drm_device *dev, int crtc) +{ +	switch (crtc) { +	case 0: +		radeon_irq_set_state(dev, RADEON_CRTC_VBLANK_MASK, 0); +		break; +	case 1: +		radeon_irq_set_state(dev, RADEON_CRTC2_VBLANK_MASK, 0); +		break; +	default: +		DRM_ERROR("tried to enable vblank on non-existent crtc %d\n", +			  crtc); +		break; +	} +} + +static __inline__ u32 radeon_acknowledge_irqs(drm_radeon_private_t * dev_priv) +{ +	u32 irqs = RADEON_READ(RADEON_GEN_INT_STATUS) & +		(RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT | +		 RADEON_CRTC2_VBLANK_STAT); +  	if (irqs)  		RADEON_WRITE(RADEON_GEN_INT_STATUS, irqs); +  	return irqs;  } @@ -72,39 +121,21 @@ irqreturn_t radeon_driver_irq_handler(DRM_IRQ_ARGS)  	/* Only consider the bits we're interested in - others could be used  	 * outside the DRM  	 */ -	stat = radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK | -						  RADEON_CRTC_VBLANK_STAT | -						  RADEON_CRTC2_VBLANK_STAT)); +	stat = radeon_acknowledge_irqs(dev_priv);  	if (!stat)  		return IRQ_NONE;  	stat &= dev_priv->irq_enable_reg;  	/* SW interrupt */ -	if (stat & RADEON_SW_INT_TEST) { +	if (stat & RADEON_SW_INT_TEST)  		DRM_WAKEUP(&dev_priv->swi_queue); -	}  	/* VBLANK interrupt */ -	if (stat & (RADEON_CRTC_VBLANK_STAT|RADEON_CRTC2_VBLANK_STAT)) { -		int vblank_crtc = dev_priv->vblank_crtc; - -		if ((vblank_crtc & -		     (DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) == -		    (DRM_RADEON_VBLANK_CRTC1 | DRM_RADEON_VBLANK_CRTC2)) { -			if (stat & RADEON_CRTC_VBLANK_STAT) -				atomic_inc(&dev->vbl_received); -			if (stat & RADEON_CRTC2_VBLANK_STAT) -				atomic_inc(&dev->vbl_received2); -		} else if (((stat & RADEON_CRTC_VBLANK_STAT) && -			   (vblank_crtc & DRM_RADEON_VBLANK_CRTC1)) || -			   ((stat & RADEON_CRTC2_VBLANK_STAT) && -			    (vblank_crtc & DRM_RADEON_VBLANK_CRTC2))) -			atomic_inc(&dev->vbl_received); - -		DRM_WAKEUP(&dev->vbl_queue); -		drm_vbl_send_signals(dev); -	} +	if (stat & RADEON_CRTC_VBLANK_STAT) +		drm_handle_vblank(dev, 0); +	if (stat & RADEON_CRTC2_VBLANK_STAT) +		drm_handle_vblank(dev, 1);  	return IRQ_HANDLED;  } @@ -144,54 +175,27 @@ static int radeon_wait_irq(struct drm_device * dev, int swi_nr)  	return ret;  } -static int radeon_driver_vblank_do_wait(struct drm_device * dev, -					unsigned int *sequence, int crtc) +u32 radeon_get_vblank_counter(struct drm_device *dev, int crtc)  { -	drm_radeon_private_t *dev_priv = -	    (drm_radeon_private_t *) dev->dev_private; -	unsigned int cur_vblank; -	int ret = 0; -	int ack = 0; -	atomic_t *counter; +	drm_radeon_private_t *dev_priv = dev->dev_private; +	u32 crtc_cnt_reg, crtc_status_reg; +  	if (!dev_priv) {  		DRM_ERROR("called with no initialization\n");  		return -EINVAL;  	} -	if (crtc == DRM_RADEON_VBLANK_CRTC1) { -		counter = &dev->vbl_received; -		ack |= RADEON_CRTC_VBLANK_STAT; -	} else if (crtc == DRM_RADEON_VBLANK_CRTC2) { -		counter = &dev->vbl_received2; -		ack |= RADEON_CRTC2_VBLANK_STAT; -	} else +	if (crtc == 0) { +		crtc_cnt_reg = RADEON_CRTC_CRNT_FRAME; +		crtc_status_reg = RADEON_CRTC_STATUS; +	} else if (crtc == 1) { +		crtc_cnt_reg = RADEON_CRTC2_CRNT_FRAME; +		crtc_status_reg = RADEON_CRTC2_STATUS; +	} else {  		return -EINVAL; +	} -	radeon_acknowledge_irqs(dev_priv, ack); - -	dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE; - -	/* Assume that the user has missed the current sequence number -	 * by about a day rather than she wants to wait for years -	 * using vertical blanks... -	 */ -	DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, -		    (((cur_vblank = atomic_read(counter)) -		      - *sequence) <= (1 << 23))); - -	*sequence = cur_vblank; - -	return ret; -} - -int radeon_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence) -{ -	return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC1); -} - -int radeon_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence) -{ -	return radeon_driver_vblank_do_wait(dev, sequence, DRM_RADEON_VBLANK_CRTC2); +	return RADEON_READ(crtc_cnt_reg) + (RADEON_READ(crtc_status_reg) & 1);  }  /* Needs the lock as it touches the ring. @@ -234,21 +238,6 @@ int radeon_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_pr  	return radeon_wait_irq(dev, irqwait->irq_seq);  } -static void radeon_enable_interrupt(struct drm_device *dev) -{ -	drm_radeon_private_t *dev_priv = (drm_radeon_private_t *) dev->dev_private; - -	dev_priv->irq_enable_reg = RADEON_SW_INT_ENABLE; -	if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC1) -		dev_priv->irq_enable_reg |= RADEON_CRTC_VBLANK_MASK; - -	if (dev_priv->vblank_crtc & DRM_RADEON_VBLANK_CRTC2) -		dev_priv->irq_enable_reg |= RADEON_CRTC2_VBLANK_MASK; - -	RADEON_WRITE(RADEON_GEN_INT_CNTL, dev_priv->irq_enable_reg); -	dev_priv->irq_enabled = 1; -} -  /* drm_dma.h hooks  */  void radeon_driver_irq_preinstall(struct drm_device * dev) @@ -260,20 +249,27 @@ void radeon_driver_irq_preinstall(struct drm_device * dev)  	RADEON_WRITE(RADEON_GEN_INT_CNTL, 0);  	/* Clear bits if they're already high */ -	radeon_acknowledge_irqs(dev_priv, (RADEON_SW_INT_TEST_ACK | -					   RADEON_CRTC_VBLANK_STAT | -					   RADEON_CRTC2_VBLANK_STAT)); +	radeon_acknowledge_irqs(dev_priv);  } -void radeon_driver_irq_postinstall(struct drm_device * dev) +int radeon_driver_irq_postinstall(struct drm_device * dev)  {  	drm_radeon_private_t *dev_priv =  	    (drm_radeon_private_t *) dev->dev_private; +	int ret;  	atomic_set(&dev_priv->swi_emitted, 0);  	DRM_INIT_WAITQUEUE(&dev_priv->swi_queue); -	radeon_enable_interrupt(dev); +	ret = drm_vblank_init(dev, 2); +	if (ret) +		return ret; + +	dev->max_vblank_count = 0x001fffff; + +	radeon_irq_set_state(dev, RADEON_SW_INT_ENABLE, 1); + +	return 0;  }  void radeon_driver_irq_uninstall(struct drm_device * dev) @@ -315,6 +311,5 @@ int radeon_vblank_crtc_set(struct drm_device *dev, int64_t value)  		return -EINVAL;  	}  	dev_priv->vblank_crtc = (unsigned int)value; -	radeon_enable_interrupt(dev);  	return 0;  } diff --git a/shared-core/via_drv.c b/shared-core/via_drv.c index 6528a7c1..a802e4ae 100644 --- a/shared-core/via_drv.c +++ b/shared-core/via_drv.c @@ -83,14 +83,16 @@ static int probe(struct pci_dev *pdev, const struct pci_device_id *ent);  static struct drm_driver driver = {  	.driver_features =  	    DRIVER_USE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | -	    DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL, +	    DRIVER_IRQ_SHARED,  	.load = via_driver_load,  	.unload = via_driver_unload,  #ifndef VIA_HAVE_CORE_MM  	.context_ctor = via_init_context,  #endif  	.context_dtor = via_final_context, -	.vblank_wait = via_driver_vblank_wait, +	.get_vblank_counter = via_get_vblank_counter, +	.enable_vblank = via_enable_vblank, +	.disable_vblank = via_disable_vblank,  	.irq_preinstall = via_driver_irq_preinstall,  	.irq_postinstall = via_driver_irq_postinstall,  	.irq_uninstall = via_driver_irq_uninstall, diff --git a/shared-core/via_drv.h b/shared-core/via_drv.h index 39aedb1d..8dd4a727 100644 --- a/shared-core/via_drv.h +++ b/shared-core/via_drv.h @@ -102,6 +102,7 @@ typedef struct drm_via_private {  	struct timeval last_vblank;  	int last_vblank_valid;  	unsigned usec_per_vblank; +	atomic_t vbl_received;  	drm_via_state_t hc_state;  	char pci_buf[VIA_PCI_BUF_SIZE];  	const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; @@ -166,11 +167,13 @@ extern int via_driver_unload(struct drm_device *dev);  extern int via_final_context(struct drm_device * dev, int context);  extern int via_do_cleanup_map(struct drm_device * dev); -extern int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence); +extern u32 via_get_vblank_counter(struct drm_device *dev, int crtc); +extern int via_enable_vblank(struct drm_device *dev, int crtc); +extern void via_disable_vblank(struct drm_device *dev, int crtc);  extern irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS);  extern void via_driver_irq_preinstall(struct drm_device * dev); -extern void via_driver_irq_postinstall(struct drm_device * dev); +extern int via_driver_irq_postinstall(struct drm_device * dev);  extern void via_driver_irq_uninstall(struct drm_device * dev);  extern int via_dma_cleanup(struct drm_device * dev); diff --git a/shared-core/via_irq.c b/shared-core/via_irq.c index 2f9b203a..b8e652e6 100644 --- a/shared-core/via_irq.c +++ b/shared-core/via_irq.c @@ -96,6 +96,15 @@ static unsigned time_diff(struct timeval *now,struct timeval *then)  		1000000 - (then->tv_usec - now->tv_usec);  } +u32 via_get_vblank_counter(struct drm_device *dev, int crtc) +{ +	drm_via_private_t *dev_priv = dev->dev_private; +	if (crtc != 0) +		return 0; + +	return atomic_read(&dev_priv->vbl_received); +} +  irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)  {  	struct drm_device *dev = (struct drm_device *) arg; @@ -108,8 +117,8 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)  	status = VIA_READ(VIA_REG_INTERRUPT);  	if (status & VIA_IRQ_VBLANK_PENDING) { -		atomic_inc(&dev->vbl_received); -		if (!(atomic_read(&dev->vbl_received) & 0x0F)) { +		atomic_inc(&dev_priv->vbl_received); +		if (!(atomic_read(&dev_priv->vbl_received) & 0x0F)) {  #ifdef __linux__  			do_gettimeofday(&cur_vblank);  #else @@ -123,12 +132,11 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)  			dev_priv->last_vblank = cur_vblank;  			dev_priv->last_vblank_valid = 1;  		} -		if (!(atomic_read(&dev->vbl_received) & 0xFF)) { +		if (!(atomic_read(&dev_priv->vbl_received) & 0xFF)) {  			DRM_DEBUG("US per vblank is: %u\n",  				  dev_priv->usec_per_vblank);  		} -		DRM_WAKEUP(&dev->vbl_queue); -		drm_vbl_send_signals(dev); +		drm_handle_vblank(dev, 0);  		handled = 1;  	} @@ -170,31 +178,25 @@ static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv)  	}  } -int via_driver_vblank_wait(struct drm_device * dev, unsigned int *sequence) +int via_enable_vblank(struct drm_device *dev, int crtc)  { -	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; -	unsigned int cur_vblank; -	int ret = 0; +	drm_via_private_t *dev_priv = dev->dev_private; +	u32 status; -	DRM_DEBUG("\n"); -	if (!dev_priv) { -		DRM_ERROR("called with no initialization\n"); +	if (crtc != 0) { +		DRM_ERROR("%s:  bad crtc %d\n", __FUNCTION__, crtc);  		return -EINVAL;  	} -	viadrv_acknowledge_irqs(dev_priv); - -	/* Assume that the user has missed the current sequence number -	 * by about a day rather than she wants to wait for years -	 * using vertical blanks... -	 */ - -	DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, -		    (((cur_vblank = atomic_read(&dev->vbl_received)) - -		      *sequence) <= (1 << 23))); +	status = VIA_READ(VIA_REG_INTERRUPT); +	VIA_WRITE(VIA_REG_INTERRUPT, status & VIA_IRQ_VBLANK_ENABLE); +	return 0; +} -	*sequence = cur_vblank; -	return ret; +void via_disable_vblank(struct drm_device *dev, int crtc) +{ +	if (crtc != 0) +		DRM_ERROR("%s:  bad crtc %d\n", __FUNCTION__, crtc);  }  static int @@ -300,23 +302,24 @@ void via_driver_irq_preinstall(struct drm_device * dev)  	}  } -void via_driver_irq_postinstall(struct drm_device * dev) +int via_driver_irq_postinstall(struct drm_device * dev)  {  	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;  	u32 status; -	DRM_DEBUG("\n"); -	if (dev_priv) { -		status = VIA_READ(VIA_REG_INTERRUPT); -		VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL -			  | dev_priv->irq_enable_mask); +	DRM_DEBUG("via_driver_irq_postinstall\n"); +	if (!dev_priv) +		return -EINVAL; -		/* Some magic, oh for some data sheets ! */ +	status = VIA_READ(VIA_REG_INTERRUPT); +	VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL +		  | dev_priv->irq_enable_mask); -		VIA_WRITE8(0x83d4, 0x11); -		VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); +	/* Some magic, oh for some data sheets ! */ +	VIA_WRITE8(0x83d4, 0x11); +	VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); -	} +	return 0;  }  void via_driver_irq_uninstall(struct drm_device * dev)  | 
