These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / exynos / exynos_drm_crtc.c
index 9006b94..e693571 100644 (file)
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
 
 #include "exynos_drm_crtc.h"
 #include "exynos_drm_drv.h"
-#include "exynos_drm_encoder.h"
 #include "exynos_drm_plane.h"
 
-static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
+static void exynos_drm_crtc_enable(struct drm_crtc *crtc)
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
-       DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
+       if (exynos_crtc->ops->enable)
+               exynos_crtc->ops->enable(exynos_crtc);
 
-       if (exynos_crtc->dpms == mode) {
-               DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
-               return;
-       }
-
-       if (mode > DRM_MODE_DPMS_ON) {
-               /* wait for the completion of page flip. */
-               if (!wait_event_timeout(exynos_crtc->pending_flip_queue,
-                               (exynos_crtc->event == NULL), HZ/20))
-                       exynos_crtc->event = NULL;
-               drm_crtc_vblank_off(crtc);
-       }
-
-       if (exynos_crtc->ops->dpms)
-               exynos_crtc->ops->dpms(exynos_crtc, mode);
-
-       exynos_crtc->dpms = mode;
-
-       if (mode == DRM_MODE_DPMS_ON)
-               drm_crtc_vblank_on(crtc);
+       drm_crtc_vblank_on(crtc);
 }
 
-static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
-{
-       /* drm framework doesn't check NULL. */
-}
-
-static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
+static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
-       struct exynos_drm_plane *exynos_plane = to_exynos_plane(crtc->primary);
 
-       exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+       drm_crtc_vblank_off(crtc);
 
-       if (exynos_crtc->ops->win_commit)
-               exynos_crtc->ops->win_commit(exynos_crtc, exynos_plane->zpos);
-
-       if (exynos_crtc->ops->commit)
-               exynos_crtc->ops->commit(exynos_crtc);
+       if (exynos_crtc->ops->disable)
+               exynos_crtc->ops->disable(exynos_crtc);
 }
 
-static bool
-exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
-                           const struct drm_display_mode *mode,
-                           struct drm_display_mode *adjusted_mode)
+static void
+exynos_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
-       if (exynos_crtc->ops->mode_fixup)
-               return exynos_crtc->ops->mode_fixup(exynos_crtc, mode,
-                                                   adjusted_mode);
-
-       return true;
+       if (exynos_crtc->ops->commit)
+               exynos_crtc->ops->commit(exynos_crtc);
 }
 
-static int
-exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
-                         struct drm_display_mode *adjusted_mode, int x, int y,
-                         struct drm_framebuffer *old_fb)
+static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
+                                    struct drm_crtc_state *state)
 {
-       struct drm_framebuffer *fb = crtc->primary->fb;
-       unsigned int crtc_w;
-       unsigned int crtc_h;
-       int ret;
-
-       /*
-        * copy the mode data adjusted by mode_fixup() into crtc->mode
-        * so that hardware can be seet to proper mode.
-        */
-       memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
+       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
 
-       ret = exynos_check_plane(crtc->primary, fb);
-       if (ret < 0)
-               return ret;
+       if (!state->enable)
+               return 0;
 
-       crtc_w = fb->width - x;
-       crtc_h = fb->height - y;
-       exynos_plane_mode_set(crtc->primary, crtc, fb, 0, 0,
-                             crtc_w, crtc_h, x, y, crtc_w, crtc_h);
+       if (exynos_crtc->ops->atomic_check)
+               return exynos_crtc->ops->atomic_check(exynos_crtc, state);
 
        return 0;
 }
 
-static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-                                         struct drm_framebuffer *old_fb)
+static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
+                                    struct drm_crtc_state *old_crtc_state)
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
-       struct drm_framebuffer *fb = crtc->primary->fb;
-       unsigned int crtc_w;
-       unsigned int crtc_h;
-
-       /* when framebuffer changing is requested, crtc's dpms should be on */
-       if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
-               DRM_ERROR("failed framebuffer changing request.\n");
-               return -EPERM;
-       }
+       struct drm_plane *plane;
 
-       crtc_w = fb->width - x;
-       crtc_h = fb->height - y;
+       exynos_crtc->event = crtc->state->event;
 
-       return exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
-                                  crtc_w, crtc_h, x, y, crtc_w, crtc_h);
+       drm_atomic_crtc_for_each_plane(plane, crtc) {
+               struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
+
+               if (exynos_crtc->ops->atomic_begin)
+                       exynos_crtc->ops->atomic_begin(exynos_crtc,
+                                                       exynos_plane);
+       }
 }
 
-static void exynos_drm_crtc_disable(struct drm_crtc *crtc)
+static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
+                                    struct drm_crtc_state *old_crtc_state)
 {
+       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
        struct drm_plane *plane;
-       int ret;
 
-       exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+       drm_atomic_crtc_for_each_plane(plane, crtc) {
+               struct exynos_drm_plane *exynos_plane = to_exynos_plane(plane);
 
-       drm_for_each_legacy_plane(plane, &crtc->dev->mode_config.plane_list) {
-               if (plane->crtc != crtc)
-                       continue;
-
-               ret = plane->funcs->disable_plane(plane);
-               if (ret)
-                       DRM_ERROR("Failed to disable plane %d\n", ret);
+               if (exynos_crtc->ops->atomic_flush)
+                       exynos_crtc->ops->atomic_flush(exynos_crtc,
+                                                       exynos_plane);
        }
 }
 
 static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
-       .dpms           = exynos_drm_crtc_dpms,
-       .prepare        = exynos_drm_crtc_prepare,
-       .commit         = exynos_drm_crtc_commit,
-       .mode_fixup     = exynos_drm_crtc_mode_fixup,
-       .mode_set       = exynos_drm_crtc_mode_set,
-       .mode_set_base  = exynos_drm_crtc_mode_set_base,
+       .enable         = exynos_drm_crtc_enable,
        .disable        = exynos_drm_crtc_disable,
+       .mode_set_nofb  = exynos_drm_crtc_mode_set_nofb,
+       .atomic_check   = exynos_crtc_atomic_check,
+       .atomic_begin   = exynos_crtc_atomic_begin,
+       .atomic_flush   = exynos_crtc_atomic_flush,
 };
 
-static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
-                                    struct drm_framebuffer *fb,
-                                    struct drm_pending_vblank_event *event,
-                                    uint32_t page_flip_flags)
-{
-       struct drm_device *dev = crtc->dev;
-       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
-       struct drm_framebuffer *old_fb = crtc->primary->fb;
-       unsigned int crtc_w, crtc_h;
-       int ret;
-
-       /* when the page flip is requested, crtc's dpms should be on */
-       if (exynos_crtc->dpms > DRM_MODE_DPMS_ON) {
-               DRM_ERROR("failed page flip request.\n");
-               return -EINVAL;
-       }
-
-       if (!event)
-               return -EINVAL;
-
-       spin_lock_irq(&dev->event_lock);
-       if (exynos_crtc->event) {
-               ret = -EBUSY;
-               goto out;
-       }
-
-       ret = drm_vblank_get(dev, exynos_crtc->pipe);
-       if (ret) {
-               DRM_DEBUG("failed to acquire vblank counter\n");
-               goto out;
-       }
-
-       exynos_crtc->event = event;
-       spin_unlock_irq(&dev->event_lock);
-
-       /*
-        * the pipe from user always is 0 so we can set pipe number
-        * of current owner to event.
-        */
-       event->pipe = exynos_crtc->pipe;
-
-       crtc->primary->fb = fb;
-       crtc_w = fb->width - crtc->x;
-       crtc_h = fb->height - crtc->y;
-       ret = exynos_update_plane(crtc->primary, crtc, fb, 0, 0,
-                                 crtc_w, crtc_h, crtc->x, crtc->y,
-                                 crtc_w, crtc_h);
-       if (ret) {
-               crtc->primary->fb = old_fb;
-               spin_lock_irq(&dev->event_lock);
-               exynos_crtc->event = NULL;
-               drm_vblank_put(dev, exynos_crtc->pipe);
-               spin_unlock_irq(&dev->event_lock);
-               return ret;
-       }
-
-       return 0;
-
-out:
-       spin_unlock_irq(&dev->event_lock);
-       return ret;
-}
-
 static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
 {
        struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
@@ -232,9 +117,12 @@ static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
 }
 
 static struct drm_crtc_funcs exynos_crtc_funcs = {
-       .set_config     = drm_crtc_helper_set_config,
-       .page_flip      = exynos_drm_crtc_page_flip,
+       .set_config     = drm_atomic_helper_set_config,
+       .page_flip      = drm_atomic_helper_page_flip,
        .destroy        = exynos_drm_crtc_destroy,
+       .reset = drm_atomic_helper_crtc_reset,
+       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 };
 
 struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
@@ -253,14 +141,13 @@ struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
        if (!exynos_crtc)
                return ERR_PTR(-ENOMEM);
 
-       init_waitqueue_head(&exynos_crtc->pending_flip_queue);
-
-       exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
        exynos_crtc->pipe = pipe;
        exynos_crtc->type = type;
        exynos_crtc->ops = ops;
        exynos_crtc->ctx = ctx;
 
+       init_waitqueue_head(&exynos_crtc->wait_update);
+
        crtc = &exynos_crtc->base;
 
        private->crtc[pipe] = crtc;
@@ -280,52 +167,52 @@ err_crtc:
        return ERR_PTR(ret);
 }
 
-int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int pipe)
+int exynos_drm_crtc_enable_vblank(struct drm_device *dev, unsigned int pipe)
 {
        struct exynos_drm_private *private = dev->dev_private;
        struct exynos_drm_crtc *exynos_crtc =
                to_exynos_crtc(private->crtc[pipe]);
 
-       if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
-               return -EPERM;
-
        if (exynos_crtc->ops->enable_vblank)
-               exynos_crtc->ops->enable_vblank(exynos_crtc);
+               return exynos_crtc->ops->enable_vblank(exynos_crtc);
 
        return 0;
 }
 
-void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int pipe)
+void exynos_drm_crtc_disable_vblank(struct drm_device *dev, unsigned int pipe)
 {
        struct exynos_drm_private *private = dev->dev_private;
        struct exynos_drm_crtc *exynos_crtc =
                to_exynos_crtc(private->crtc[pipe]);
 
-       if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
-               return;
-
        if (exynos_crtc->ops->disable_vblank)
                exynos_crtc->ops->disable_vblank(exynos_crtc);
 }
 
-void exynos_drm_crtc_finish_pageflip(struct drm_device *dev, int pipe)
+void exynos_drm_crtc_wait_pending_update(struct exynos_drm_crtc *exynos_crtc)
 {
-       struct exynos_drm_private *dev_priv = dev->dev_private;
-       struct drm_crtc *drm_crtc = dev_priv->crtc[pipe];
-       struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(drm_crtc);
+       wait_event_timeout(exynos_crtc->wait_update,
+                          (atomic_read(&exynos_crtc->pending_update) == 0),
+                          msecs_to_jiffies(50));
+}
+
+void exynos_drm_crtc_finish_update(struct exynos_drm_crtc *exynos_crtc,
+                               struct exynos_drm_plane *exynos_plane)
+{
+       struct drm_crtc *crtc = &exynos_crtc->base;
        unsigned long flags;
 
-       spin_lock_irqsave(&dev->event_lock, flags);
-       if (exynos_crtc->event) {
+       exynos_plane->pending_fb = NULL;
 
-               drm_send_vblank_event(dev, -1, exynos_crtc->event);
-               drm_vblank_put(dev, pipe);
-               wake_up(&exynos_crtc->pending_flip_queue);
+       if (atomic_dec_and_test(&exynos_crtc->pending_update))
+               wake_up(&exynos_crtc->wait_update);
 
-       }
+       spin_lock_irqsave(&crtc->dev->event_lock, flags);
+       if (exynos_crtc->event)
+               drm_crtc_send_vblank_event(crtc, exynos_crtc->event);
 
        exynos_crtc->event = NULL;
-       spin_unlock_irqrestore(&dev->event_lock, flags);
+       spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
 }
 
 void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
@@ -352,7 +239,7 @@ void exynos_drm_crtc_complete_scanout(struct drm_framebuffer *fb)
 }
 
 int exynos_drm_crtc_get_pipe_from_type(struct drm_device *drm_dev,
-                                       unsigned int out_type)
+                                      enum exynos_drm_output_type out_type)
 {
        struct drm_crtc *crtc;