]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/drm_irq.c
Merge branch 'for-4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/tj/libata
[karo-tx-linux.git] / drivers / gpu / drm / drm_irq.c
index 77f357b2c3869a8bb13eea58c8c76819df34e70a..b969a64a15140ca6a4a5b19bb441a24945827784 100644 (file)
@@ -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);