These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / msm / msm_drv.c
index c80a6be..b88ce51 100644 (file)
 
 static void msm_fb_output_poll_changed(struct drm_device *dev)
 {
-#ifdef CONFIG_DRM_MSM_FBDEV
        struct msm_drm_private *priv = dev->dev_private;
        if (priv->fbdev)
                drm_fb_helper_hotplug_event(priv->fbdev);
-#endif
 }
 
 static const struct drm_mode_config_funcs mode_config_funcs = {
@@ -56,7 +54,7 @@ module_param(reglog, bool, 0600);
 #define reglog 0
 #endif
 
-#ifdef CONFIG_DRM_MSM_FBDEV
+#ifdef CONFIG_DRM_FBDEV_EMULATION
 static bool fbdev = true;
 MODULE_PARM_DESC(fbdev, "Enable fbdev compat layer");
 module_param(fbdev, bool, 0600);
@@ -116,6 +114,65 @@ u32 msm_readl(const void __iomem *addr)
        return val;
 }
 
+struct vblank_event {
+       struct list_head node;
+       int crtc_id;
+       bool enable;
+};
+
+static void vblank_ctrl_worker(struct work_struct *work)
+{
+       struct msm_vblank_ctrl *vbl_ctrl = container_of(work,
+                                               struct msm_vblank_ctrl, work);
+       struct msm_drm_private *priv = container_of(vbl_ctrl,
+                                       struct msm_drm_private, vblank_ctrl);
+       struct msm_kms *kms = priv->kms;
+       struct vblank_event *vbl_ev, *tmp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vbl_ctrl->lock, flags);
+       list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) {
+               list_del(&vbl_ev->node);
+               spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
+
+               if (vbl_ev->enable)
+                       kms->funcs->enable_vblank(kms,
+                                               priv->crtcs[vbl_ev->crtc_id]);
+               else
+                       kms->funcs->disable_vblank(kms,
+                                               priv->crtcs[vbl_ev->crtc_id]);
+
+               kfree(vbl_ev);
+
+               spin_lock_irqsave(&vbl_ctrl->lock, flags);
+       }
+
+       spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
+}
+
+static int vblank_ctrl_queue_work(struct msm_drm_private *priv,
+                                       int crtc_id, bool enable)
+{
+       struct msm_vblank_ctrl *vbl_ctrl = &priv->vblank_ctrl;
+       struct vblank_event *vbl_ev;
+       unsigned long flags;
+
+       vbl_ev = kzalloc(sizeof(*vbl_ev), GFP_ATOMIC);
+       if (!vbl_ev)
+               return -ENOMEM;
+
+       vbl_ev->crtc_id = crtc_id;
+       vbl_ev->enable = enable;
+
+       spin_lock_irqsave(&vbl_ctrl->lock, flags);
+       list_add_tail(&vbl_ev->node, &vbl_ctrl->event_list);
+       spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
+
+       queue_work(priv->wq, &vbl_ctrl->work);
+
+       return 0;
+}
+
 /*
  * DRM operations:
  */
@@ -125,6 +182,18 @@ static int msm_unload(struct drm_device *dev)
        struct msm_drm_private *priv = dev->dev_private;
        struct msm_kms *kms = priv->kms;
        struct msm_gpu *gpu = priv->gpu;
+       struct msm_vblank_ctrl *vbl_ctrl = &priv->vblank_ctrl;
+       struct vblank_event *vbl_ev, *tmp;
+
+       /* We must cancel and cleanup any pending vblank enable/disable
+        * work before drm_irq_uninstall() to avoid work re-enabling an
+        * irq after uninstall has disabled it.
+        */
+       cancel_work_sync(&vbl_ctrl->work);
+       list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) {
+               list_del(&vbl_ev->node);
+               kfree(vbl_ev);
+       }
 
        drm_kms_helper_poll_fini(dev);
        drm_mode_config_cleanup(dev);
@@ -282,6 +351,9 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
 
        INIT_LIST_HEAD(&priv->inactive_list);
        INIT_LIST_HEAD(&priv->fence_cbs);
+       INIT_LIST_HEAD(&priv->vblank_ctrl.event_list);
+       INIT_WORK(&priv->vblank_ctrl.work, vblank_ctrl_worker);
+       spin_lock_init(&priv->vblank_ctrl.lock);
 
        drm_mode_config_init(dev);
 
@@ -331,10 +403,6 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
                }
        }
 
-       dev->mode_config.min_width = 0;
-       dev->mode_config.min_height = 0;
-       dev->mode_config.max_width = 2048;
-       dev->mode_config.max_height = 2048;
        dev->mode_config.funcs = &mode_config_funcs;
 
        ret = drm_vblank_init(dev, priv->num_crtcs);
@@ -353,7 +421,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
 
        drm_mode_config_reset(dev);
 
-#ifdef CONFIG_DRM_MSM_FBDEV
+#ifdef CONFIG_DRM_FBDEV_EMULATION
        if (fbdev)
                priv->fbdev = msm_fbdev_init(dev);
 #endif
@@ -421,11 +489,9 @@ static void msm_preclose(struct drm_device *dev, struct drm_file *file)
 
 static void msm_lastclose(struct drm_device *dev)
 {
-#ifdef CONFIG_DRM_MSM_FBDEV
        struct msm_drm_private *priv = dev->dev_private;
        if (priv->fbdev)
                drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev);
-#endif
 }
 
 static irqreturn_t msm_irq(int irq, void *arg)
@@ -461,24 +527,24 @@ static void msm_irq_uninstall(struct drm_device *dev)
        kms->funcs->irq_uninstall(kms);
 }
 
-static int msm_enable_vblank(struct drm_device *dev, int crtc_id)
+static int msm_enable_vblank(struct drm_device *dev, unsigned int pipe)
 {
        struct msm_drm_private *priv = dev->dev_private;
        struct msm_kms *kms = priv->kms;
        if (!kms)
                return -ENXIO;
-       DBG("dev=%p, crtc=%d", dev, crtc_id);
-       return kms->funcs->enable_vblank(kms, priv->crtcs[crtc_id]);
+       DBG("dev=%p, crtc=%u", dev, pipe);
+       return vblank_ctrl_queue_work(priv, pipe, true);
 }
 
-static void msm_disable_vblank(struct drm_device *dev, int crtc_id)
+static void msm_disable_vblank(struct drm_device *dev, unsigned int pipe)
 {
        struct msm_drm_private *priv = dev->dev_private;
        struct msm_kms *kms = priv->kms;
        if (!kms)
                return;
-       DBG("dev=%p, crtc=%d", dev, crtc_id);
-       kms->funcs->disable_vblank(kms, priv->crtcs[crtc_id]);
+       DBG("dev=%p, crtc=%u", dev, pipe);
+       vblank_ctrl_queue_work(priv, pipe, false);
 }
 
 /*
@@ -637,8 +703,8 @@ static void msm_debugfs_cleanup(struct drm_minor *minor)
  * Fences:
  */
 
-int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
-               struct timespec *timeout)
+int msm_wait_fence(struct drm_device *dev, uint32_t fence,
+               ktime_t *timeout , bool interruptible)
 {
        struct msm_drm_private *priv = dev->dev_private;
        int ret;
@@ -656,16 +722,23 @@ int msm_wait_fence_interruptable(struct drm_device *dev, uint32_t fence,
                /* no-wait: */
                ret = fence_completed(dev, fence) ? 0 : -EBUSY;
        } else {
-               unsigned long timeout_jiffies = timespec_to_jiffies(timeout);
-               unsigned long start_jiffies = jiffies;
+               ktime_t now = ktime_get();
                unsigned long remaining_jiffies;
 
-               if (time_after(start_jiffies, timeout_jiffies))
+               if (ktime_compare(*timeout, now) < 0) {
                        remaining_jiffies = 0;
-               else
-                       remaining_jiffies = timeout_jiffies - start_jiffies;
+               } else {
+                       ktime_t rem = ktime_sub(*timeout, now);
+                       struct timespec ts = ktime_to_timespec(rem);
+                       remaining_jiffies = timespec_to_jiffies(&ts);
+               }
 
-               ret = wait_event_interruptible_timeout(priv->fence_event,
+               if (interruptible)
+                       ret = wait_event_interruptible_timeout(priv->fence_event,
+                               fence_completed(dev, fence),
+                               remaining_jiffies);
+               else
+                       ret = wait_event_timeout(priv->fence_event,
                                fence_completed(dev, fence),
                                remaining_jiffies);
 
@@ -772,13 +845,17 @@ static int msm_ioctl_gem_new(struct drm_device *dev, void *data,
                        args->flags, &args->handle);
 }
 
-#define TS(t) ((struct timespec){ .tv_sec = (t).tv_sec, .tv_nsec = (t).tv_nsec })
+static inline ktime_t to_ktime(struct drm_msm_timespec timeout)
+{
+       return ktime_set(timeout.tv_sec, timeout.tv_nsec);
+}
 
 static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
                struct drm_file *file)
 {
        struct drm_msm_gem_cpu_prep *args = data;
        struct drm_gem_object *obj;
+       ktime_t timeout = to_ktime(args->timeout);
        int ret;
 
        if (args->op & ~MSM_PREP_FLAGS) {
@@ -790,7 +867,7 @@ static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
        if (!obj)
                return -ENOENT;
 
-       ret = msm_gem_cpu_prep(obj, args->op, &TS(args->timeout));
+       ret = msm_gem_cpu_prep(obj, args->op, &timeout);
 
        drm_gem_object_unreference_unlocked(obj);
 
@@ -840,24 +917,24 @@ static int msm_ioctl_wait_fence(struct drm_device *dev, void *data,
                struct drm_file *file)
 {
        struct drm_msm_wait_fence *args = data;
+       ktime_t timeout = to_ktime(args->timeout);
 
        if (args->pad) {
                DRM_ERROR("invalid pad: %08x\n", args->pad);
                return -EINVAL;
        }
 
-       return msm_wait_fence_interruptable(dev, args->fence,
-                       &TS(args->timeout));
+       return msm_wait_fence(dev, args->fence, &timeout, true);
 }
 
 static const struct drm_ioctl_desc msm_ioctls[] = {
-       DRM_IOCTL_DEF_DRV(MSM_GET_PARAM,    msm_ioctl_get_param,    DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_NEW,      msm_ioctl_gem_new,      DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_INFO,     msm_ioctl_gem_info,     DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT,   msm_ioctl_gem_submit,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
-       DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE,   msm_ioctl_wait_fence,   DRM_UNLOCKED|DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GET_PARAM,    msm_ioctl_get_param,    DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_NEW,      msm_ioctl_gem_new,      DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_INFO,     msm_ioctl_gem_info,     DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT,   msm_ioctl_gem_submit,   DRM_AUTH|DRM_RENDER_ALLOW),
+       DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE,   msm_ioctl_wait_fence,   DRM_AUTH|DRM_RENDER_ALLOW),
 };
 
 static const struct vm_operations_struct vm_ops = {
@@ -885,6 +962,7 @@ static struct drm_driver msm_driver = {
                                DRIVER_GEM |
                                DRIVER_PRIME |
                                DRIVER_RENDER |
+                               DRIVER_ATOMIC |
                                DRIVER_MODESET,
        .load               = msm_load,
        .unload             = msm_unload,
@@ -896,7 +974,7 @@ static struct drm_driver msm_driver = {
        .irq_preinstall     = msm_irq_preinstall,
        .irq_postinstall    = msm_irq_postinstall,
        .irq_uninstall      = msm_irq_uninstall,
-       .get_vblank_counter = drm_vblank_count,
+       .get_vblank_counter = drm_vblank_no_hw_counter,
        .enable_vblank      = msm_enable_vblank,
        .disable_vblank     = msm_disable_vblank,
        .gem_free_object    = msm_gem_free_object,