diff options
Diffstat (limited to 'drivers/gpu/drm/drm_irq.c')
| -rw-r--r-- | drivers/gpu/drm/drm_irq.c | 61 | 
1 files changed, 45 insertions, 16 deletions
| diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 77f357b2c386..b969a64a1514 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -482,7 +482,7 @@ int drm_irq_install(struct drm_device *dev, int irq)  		return ret;  	} -	if (!drm_core_check_feature(dev, DRIVER_MODESET)) +	if (drm_core_check_feature(dev, DRIVER_LEGACY))  		vga_client_register(dev->pdev, (void *)dev, drm_irq_vgaarb_nokms, NULL);  	/* After installing handler */ @@ -491,7 +491,7 @@ int drm_irq_install(struct drm_device *dev, int irq)  	if (ret < 0) {  		dev->irq_enabled = false; -		if (!drm_core_check_feature(dev, DRIVER_MODESET)) +		if (drm_core_check_feature(dev, DRIVER_LEGACY))  			vga_client_register(dev->pdev, NULL, NULL, NULL);  		free_irq(irq, dev);  	} else { @@ -557,7 +557,7 @@ int drm_irq_uninstall(struct drm_device *dev)  	DRM_DEBUG("irq=%d\n", dev->irq); -	if (!drm_core_check_feature(dev, DRIVER_MODESET)) +	if (drm_core_check_feature(dev, DRIVER_LEGACY))  		vga_client_register(dev->pdev, NULL, NULL, NULL);  	if (dev->driver->irq_uninstall) @@ -592,7 +592,7 @@ int drm_control(struct drm_device *dev, void *data,  	if (!drm_core_check_feature(dev, DRIVER_HAVE_IRQ))  		return 0; -	if (drm_core_check_feature(dev, DRIVER_MODESET)) +	if (!drm_core_check_feature(dev, DRIVER_LEGACY))  		return 0;  	/* UMS was only ever supported on pci devices. */  	if (WARN_ON(!dev->pdev)) @@ -713,10 +713,10 @@ EXPORT_SYMBOL(drm_calc_timestamping_constants);   * Negative value on error, failure or if not supported in current   * video mode:   * - * -EINVAL   - Invalid CRTC. - * -EAGAIN   - Temporary unavailable, e.g., called before initial modeset. - * -ENOTSUPP - Function not supported in current display mode. - * -EIO      - Failed, e.g., due to failed scanout position query. + * -EINVAL    Invalid CRTC. + * -EAGAIN    Temporary unavailable, e.g., called before initial modeset. + * -ENOTSUPP  Function not supported in current display mode. + * -EIO       Failed, e.g., due to failed scanout position query.   *   * Returns or'ed positive status flags on success:   * @@ -1008,6 +1008,31 @@ static void send_vblank_event(struct drm_device *dev,   * period. This helper function implements exactly the required vblank arming   * behaviour.   * + * NOTE: Drivers using this to send out the event in struct &drm_crtc_state + * as part of an atomic commit must ensure that the next vblank happens at + * exactly the same time as the atomic commit is committed to the hardware. This + * function itself does **not** protect again the next vblank interrupt racing + * with either this function call or the atomic commit operation. A possible + * sequence could be: + * + * 1. Driver commits new hardware state into vblank-synchronized registers. + * 2. A vblank happens, committing the hardware state. Also the corresponding + *    vblank interrupt is fired off and fully processed by the interrupt + *    handler. + * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event(). + * 4. The event is only send out for the next vblank, which is wrong. + * + * An equivalent race can happen when the driver calls + * drm_crtc_arm_vblank_event() before writing out the new hardware state. + * + * The only way to make this work safely is to prevent the vblank from firing + * (and the hardware from committing anything else) until the entire atomic + * commit sequence has run to completion. If the hardware does not have such a + * feature (e.g. using a "go" bit), then it is unsafe to use this functions. + * Instead drivers need to manually send out the event from their interrupt + * handler by calling drm_crtc_send_vblank_event() and make sure that there's no + * possible race with the hardware committing the atomic update. + *   * Caller must hold event lock. Caller must also hold a vblank reference for   * the event @e, which will be dropped when the next vblank arrives.   */ @@ -1030,8 +1055,11 @@ EXPORT_SYMBOL(drm_crtc_arm_vblank_event);   * @crtc: the source CRTC of the vblank event   * @e: the event to send   * - * Updates sequence # and timestamp on event, and sends it to userspace. - * Caller must hold event lock. + * Updates sequence # and timestamp on event for the most recently processed + * vblank, and sends it to userspace.  Caller must hold event lock. + * + * See drm_crtc_arm_vblank_event() for a helper which can be used in certain + * situation, especially to send out events for atomic commit operations.   */  void drm_crtc_send_vblank_event(struct drm_crtc *crtc,  				struct drm_pending_vblank_event *e) @@ -1295,7 +1323,7 @@ void drm_vblank_off(struct drm_device *dev, unsigned int pipe)  		if (e->pipe != pipe)  			continue;  		DRM_DEBUG("Sending premature vblank event on disable: " -			  "wanted %d, current %d\n", +			  "wanted %u, current %u\n",  			  e->event.sequence, seq);  		list_del(&e->base.link);  		drm_vblank_put(dev, pipe); @@ -1519,7 +1547,7 @@ int drm_modeset_ctl(struct drm_device *dev, void *data,  		return 0;  	/* KMS drivers handle this internally */ -	if (drm_core_check_feature(dev, DRIVER_MODESET)) +	if (!drm_core_check_feature(dev, DRIVER_LEGACY))  		return 0;  	pipe = modeset->crtc; @@ -1585,7 +1613,7 @@ static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe,  	seq = drm_vblank_count_and_time(dev, pipe, &now); -	DRM_DEBUG("event on vblank count %d, current %d, crtc %u\n", +	DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n",  		  vblwait->request.sequence, seq, pipe);  	trace_drm_vblank_event_queued(current->pid, pipe, @@ -1693,7 +1721,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,  		return drm_queue_vblank_event(dev, pipe, vblwait, file_priv);  	} -	DRM_DEBUG("waiting on vblank count %d, crtc %u\n", +	DRM_DEBUG("waiting on vblank count %u, crtc %u\n",  		  vblwait->request.sequence, pipe);  	DRM_WAIT_ON(ret, vblank->queue, 3 * HZ,  		    (((drm_vblank_count(dev, pipe) - @@ -1708,7 +1736,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data,  		vblwait->reply.tval_sec = now.tv_sec;  		vblwait->reply.tval_usec = now.tv_usec; -		DRM_DEBUG("returning %d to client\n", +		DRM_DEBUG("returning %u to client\n",  			  vblwait->reply.sequence);  	} else {  		DRM_DEBUG("vblank wait interrupted by signal\n"); @@ -1735,7 +1763,7 @@ static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe)  		if ((seq - e->event.sequence) > (1<<23))  			continue; -		DRM_DEBUG("vblank event on %d, current %d\n", +		DRM_DEBUG("vblank event on %u, current %u\n",  			  e->event.sequence, seq);  		list_del(&e->base.link); @@ -1826,6 +1854,7 @@ EXPORT_SYMBOL(drm_crtc_handle_vblank);   */  u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe)  { +	WARN_ON_ONCE(dev->max_vblank_count != 0);  	return 0;  }  EXPORT_SYMBOL(drm_vblank_no_hw_counter); | 
