drm: sti: convert driver to atomic modeset
authorBenjamin Gaignard <benjamin.gaignard@linaro.org>
Thu, 19 Mar 2015 12:35:16 +0000 (13:35 +0100)
committerBenjamin Gaignard <benjamin.gaignard@linaro.org>
Thu, 19 Mar 2015 12:35:16 +0000 (13:35 +0100)
v1: This patch does the minimum to make sti driver use atomic helpers.
No big bang, only adapt some functions to new call order.

v2: Use dpms and page flip atomic helpers

Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
drivers/gpu/drm/sti/sti_drm_crtc.c
drivers/gpu/drm/sti/sti_drm_drv.c
drivers/gpu/drm/sti/sti_drm_drv.h
drivers/gpu/drm/sti/sti_drm_plane.c
drivers/gpu/drm/sti/sti_dvo.c
drivers/gpu/drm/sti/sti_hda.c
drivers/gpu/drm/sti/sti_hdmi.c

index e6f6ef7c4866ad94eb7bd503eabc9aee7e2b8181..6b641c5a2ec7d10609f20a38cbe6bab56c9f4c5e 100644 (file)
@@ -9,6 +9,8 @@
 #include <linux/clk.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_plane_helper.h>
 
@@ -77,22 +79,18 @@ static bool sti_drm_crtc_mode_fixup(struct drm_crtc *crtc,
 }
 
 static int
-sti_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)
+sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode)
 {
        struct sti_mixer *mixer = to_sti_mixer(crtc);
        struct device *dev = mixer->dev;
        struct sti_compositor *compo = dev_get_drvdata(dev);
-       struct sti_layer *layer;
        struct clk *clk;
        int rate = mode->clock * 1000;
        int res;
-       unsigned int w, h;
 
-       DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d mode:%d (%s)\n",
+       DRM_DEBUG_KMS("CRTC:%d (%s) mode:%d (%s)\n",
                      crtc->base.id, sti_mixer_to_str(mixer),
-                     crtc->primary->fb->base.id, mode->base.id, mode->name);
+                     mode->base.id, mode->name);
 
        DRM_DEBUG_KMS("%d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
                      mode->vrefresh, mode->clock,
@@ -122,72 +120,13 @@ sti_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
        sti_vtg_set_config(mixer->id == STI_MIXER_MAIN ?
                        compo->vtg_main : compo->vtg_aux, &crtc->mode);
 
-       /* a GDP is reserved to the CRTC FB */
-       layer = to_sti_layer(crtc->primary);
-       if (!layer) {
-               DRM_ERROR("Can not find GDP0)\n");
-               return -EINVAL;
-       }
-
-       /* copy the mode data adjusted by mode_fixup() into crtc->mode
-        * so that hardware can be set to proper mode
-        */
-       memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
-
-       res = sti_mixer_set_layer_depth(mixer, layer);
-       if (res) {
-               DRM_ERROR("Can not set layer depth\n");
-               return -EINVAL;
-       }
        res = sti_mixer_active_video_area(mixer, &crtc->mode);
        if (res) {
                DRM_ERROR("Can not set active video area\n");
                return -EINVAL;
        }
 
-       w = crtc->primary->fb->width - x;
-       h = crtc->primary->fb->height - y;
-
-       return sti_layer_prepare(layer, crtc,
-                       crtc->primary->fb, &crtc->mode,
-                       mixer->id, 0, 0, w, h, x, y, w, h);
-}
-
-static int sti_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-                                     struct drm_framebuffer *old_fb)
-{
-       struct sti_mixer *mixer = to_sti_mixer(crtc);
-       struct sti_layer *layer;
-       unsigned int w, h;
-       int ret;
-
-       DRM_DEBUG_KMS("CRTC:%d (%s) fb:%d (%d,%d)\n",
-                     crtc->base.id, sti_mixer_to_str(mixer),
-                     crtc->primary->fb->base.id, x, y);
-
-       /* GDP is reserved to the CRTC FB */
-       layer = to_sti_layer(crtc->primary);
-       if (!layer) {
-               DRM_ERROR("Can not find GDP0)\n");
-               ret = -EINVAL;
-               goto out;
-       }
-
-       w = crtc->primary->fb->width - crtc->x;
-       h = crtc->primary->fb->height - crtc->y;
-
-       ret = sti_layer_prepare(layer, crtc,
-                               crtc->primary->fb, &crtc->mode,
-                               mixer->id, 0, 0, w, h,
-                               crtc->x, crtc->y, w, h);
-       if (ret) {
-               DRM_ERROR("Can not prepare layer\n");
-               goto out;
-       }
-
-       sti_drm_crtc_commit(crtc);
-out:
-       return ret;
+       return res;
 }
 
 static void sti_drm_crtc_disable(struct drm_crtc *crtc)
@@ -195,7 +134,6 @@ static void sti_drm_crtc_disable(struct drm_crtc *crtc)
        struct sti_mixer *mixer = to_sti_mixer(crtc);
        struct device *dev = mixer->dev;
        struct sti_compositor *compo = dev_get_drvdata(dev);
-       struct sti_layer *layer;
 
        if (!mixer->enabled)
                return;
@@ -205,24 +143,6 @@ static void sti_drm_crtc_disable(struct drm_crtc *crtc)
        /* Disable Background */
        sti_mixer_set_background_status(mixer, false);
 
-       /* Disable GDP */
-       layer = to_sti_layer(crtc->primary);
-       if (!layer) {
-               DRM_ERROR("Cannot find GDP0\n");
-               return;
-       }
-
-       /* Disable layer at mixer level */
-       if (sti_mixer_set_layer_status(mixer, layer, false))
-               DRM_ERROR("Can not disable %s layer at mixer\n",
-                               sti_layer_to_str(layer));
-
-       /* Wait a while to be sure that a Vsync event is received */
-       msleep(WAIT_NEXT_VSYNC_MS);
-
-       /* Then disable layer itself */
-       sti_layer_disable(layer);
-
        drm_crtc_vblank_off(crtc);
 
        /* Disable pixel clock and compo IP clocks */
@@ -237,64 +157,44 @@ static void sti_drm_crtc_disable(struct drm_crtc *crtc)
        mixer->enabled = false;
 }
 
-static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = {
-       .dpms = sti_drm_crtc_dpms,
-       .prepare = sti_drm_crtc_prepare,
-       .commit = sti_drm_crtc_commit,
-       .mode_fixup = sti_drm_crtc_mode_fixup,
-       .mode_set = sti_drm_crtc_mode_set,
-       .mode_set_base = sti_drm_crtc_mode_set_base,
-       .disable = sti_drm_crtc_disable,
-};
+static void
+sti_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
+{
+       sti_drm_crtc_prepare(crtc);
+       sti_drm_crtc_mode_set(crtc, &crtc->state->adjusted_mode);
+}
 
-static int sti_drm_crtc_page_flip(struct drm_crtc *crtc,
-                                 struct drm_framebuffer *fb,
-                                 struct drm_pending_vblank_event *event,
-                                 uint32_t page_flip_flags)
+static void sti_drm_atomic_begin(struct drm_crtc *crtc)
 {
-       struct drm_device *drm_dev = crtc->dev;
-       struct drm_framebuffer *old_fb;
        struct sti_mixer *mixer = to_sti_mixer(crtc);
-       unsigned long flags;
-       int ret;
 
-       DRM_DEBUG_KMS("fb %d --> fb %d\n",
-                       crtc->primary->fb->base.id, fb->base.id);
+       if (crtc->state->event) {
+               crtc->state->event->pipe = drm_crtc_index(crtc);
 
-       mutex_lock(&drm_dev->struct_mutex);
+               WARN_ON(drm_crtc_vblank_get(crtc) != 0);
 
-       old_fb = crtc->primary->fb;
-       crtc->primary->fb = fb;
-       ret = sti_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, old_fb);
-       if (ret) {
-               DRM_ERROR("failed\n");
-               crtc->primary->fb = old_fb;
-               goto out;
+               mixer->pending_event = crtc->state->event;
+               crtc->state->event = NULL;
        }
+}
 
-       if (event) {
-               event->pipe = mixer->id;
-
-               ret = drm_vblank_get(drm_dev, event->pipe);
-               if (ret) {
-                       DRM_ERROR("Cannot get vblank\n");
-                       goto out;
-               }
-
-               spin_lock_irqsave(&drm_dev->event_lock, flags);
-               if (mixer->pending_event) {
-                       drm_vblank_put(drm_dev, event->pipe);
-                       ret = -EBUSY;
-               } else {
-                       mixer->pending_event = event;
-               }
-               spin_unlock_irqrestore(&drm_dev->event_lock, flags);
-       }
-out:
-       mutex_unlock(&drm_dev->struct_mutex);
-       return ret;
+static void sti_drm_atomic_flush(struct drm_crtc *crtc)
+{
 }
 
+static struct drm_crtc_helper_funcs sti_crtc_helper_funcs = {
+       .dpms = sti_drm_crtc_dpms,
+       .prepare = sti_drm_crtc_prepare,
+       .commit = sti_drm_crtc_commit,
+       .mode_fixup = sti_drm_crtc_mode_fixup,
+       .mode_set = drm_helper_crtc_mode_set,
+       .mode_set_nofb = sti_drm_crtc_mode_set_nofb,
+       .mode_set_base = drm_helper_crtc_mode_set_base,
+       .disable = sti_drm_crtc_disable,
+       .atomic_begin = sti_drm_atomic_begin,
+       .atomic_flush = sti_drm_atomic_flush,
+};
+
 static void sti_drm_crtc_destroy(struct drm_crtc *crtc)
 {
        DRM_DEBUG_KMS("\n");
@@ -380,10 +280,13 @@ void sti_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
 EXPORT_SYMBOL(sti_drm_crtc_disable_vblank);
 
 static struct drm_crtc_funcs sti_crtc_funcs = {
-       .set_config = drm_crtc_helper_set_config,
-       .page_flip = sti_drm_crtc_page_flip,
+       .set_config = drm_atomic_helper_set_config,
+       .page_flip = drm_atomic_helper_page_flip,
        .destroy = sti_drm_crtc_destroy,
        .set_property = sti_drm_crtc_set_property,
+       .reset = drm_atomic_helper_crtc_reset,
+       .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 };
 
 bool sti_drm_crtc_is_main(struct drm_crtc *crtc)
index 5239fa12172683cd2313e3863107acc26c7c08c0..59d558b400b33f390cdd6db00014ab29c94b84d4 100644 (file)
@@ -12,6 +12,8 @@
 #include <linux/module.h>
 #include <linux/of_platform.h>
 
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #define STI_MAX_FB_HEIGHT      4096
 #define STI_MAX_FB_WIDTH       4096
 
+static void sti_drm_atomic_schedule(struct sti_drm_private *private,
+                                 struct drm_atomic_state *state)
+{
+       private->commit.state = state;
+       schedule_work(&private->commit.work);
+}
+
+static void sti_drm_atomic_complete(struct sti_drm_private *private,
+                                 struct drm_atomic_state *state)
+{
+       struct drm_device *drm = private->drm_dev;
+
+       /*
+        * Everything below can be run asynchronously without the need to grab
+        * any modeset locks at all under one condition: It must be guaranteed
+        * that the asynchronous work has either been cancelled (if the driver
+        * supports it, which at least requires that the framebuffers get
+        * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
+        * before the new state gets committed on the software side with
+        * drm_atomic_helper_swap_state().
+        *
+        * This scheme allows new atomic state updates to be prepared and
+        * checked in parallel to the asynchronous completion of the previous
+        * update. Which is important since compositors need to figure out the
+        * composition of the next frame right after having submitted the
+        * current layout.
+        */
+
+       drm_atomic_helper_commit_modeset_disables(drm, state);
+       drm_atomic_helper_commit_planes(drm, state);
+       drm_atomic_helper_commit_modeset_enables(drm, state);
+
+       drm_atomic_helper_wait_for_vblanks(drm, state);
+
+       drm_atomic_helper_cleanup_planes(drm, state);
+       drm_atomic_state_free(state);
+}
+
+static void sti_drm_atomic_work(struct work_struct *work)
+{
+       struct sti_drm_private *private = container_of(work,
+                       struct sti_drm_private, commit.work);
+
+       sti_drm_atomic_complete(private, private->commit.state);
+}
+
+static int sti_drm_atomic_commit(struct drm_device *drm,
+                              struct drm_atomic_state *state, bool async)
+{
+       struct sti_drm_private *private = drm->dev_private;
+       int err;
+
+       err = drm_atomic_helper_prepare_planes(drm, state);
+       if (err)
+               return err;
+
+       /* serialize outstanding asynchronous commits */
+       mutex_lock(&private->commit.lock);
+       flush_work(&private->commit.work);
+
+       /*
+        * This is the point of no return - everything below never fails except
+        * when the hw goes bonghits. Which means we can commit the new state on
+        * the software side now.
+        */
+
+       drm_atomic_helper_swap_state(drm, state);
+
+       if (async)
+               sti_drm_atomic_schedule(private, state);
+       else
+               sti_drm_atomic_complete(private, state);
+
+       mutex_unlock(&private->commit.lock);
+       return 0;
+}
+
 static struct drm_mode_config_funcs sti_drm_mode_config_funcs = {
        .fb_create = drm_fb_cma_create,
+       .atomic_check = drm_atomic_helper_check,
+       .atomic_commit = sti_drm_atomic_commit,
 };
 
 static void sti_drm_mode_config_init(struct drm_device *dev)
@@ -61,6 +142,9 @@ static int sti_drm_load(struct drm_device *dev, unsigned long flags)
        dev->dev_private = (void *)private;
        private->drm_dev = dev;
 
+       mutex_init(&private->commit.lock);
+       INIT_WORK(&private->commit.work, sti_drm_atomic_work);
+
        drm_mode_config_init(dev);
        drm_kms_helper_poll_init(dev);
 
@@ -74,7 +158,7 @@ static int sti_drm_load(struct drm_device *dev, unsigned long flags)
                return ret;
        }
 
-       drm_helper_disable_unused_functions(dev);
+       drm_mode_config_reset(dev);
 
 #ifdef CONFIG_DRM_STI_FBDEV
        drm_fbdev_cma_init(dev, 32,
index ec5e2eb8dff91e24cc5c0a7321012b4b93d158ff..c413aa3ff4021449791f3ce73668d8beabdb94c9 100644 (file)
@@ -24,6 +24,12 @@ struct sti_drm_private {
        struct sti_compositor *compo;
        struct drm_property *plane_zorder_property;
        struct drm_device *drm_dev;
+
+       struct {
+               struct drm_atomic_state *state;
+               struct work_struct work;
+               struct mutex lock;
+       } commit;
 };
 
 #endif
index bb6a29339e1084bca0483e7ee643cfc62c06299a..64d4ed43dda3fa6e38a81a7bb4ba9bafba02287a 100644 (file)
@@ -6,6 +6,10 @@
  * License terms:  GNU General Public License (GPL), version 2
  */
 
+#include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+
 #include "sti_compositor.h"
 #include "sti_drm_drv.h"
 #include "sti_drm_plane.h"
@@ -33,9 +37,9 @@ sti_drm_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
        struct sti_mixer *mixer = to_sti_mixer(crtc);
        int res;
 
-       DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s) drm fb:%d\n",
+       DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
                      crtc->base.id, sti_mixer_to_str(mixer),
-                     plane->base.id, sti_layer_to_str(layer), fb->base.id);
+                     plane->base.id, sti_layer_to_str(layer));
        DRM_DEBUG_KMS("(%dx%d)@(%d,%d)\n", crtc_w, crtc_h, crtc_x, crtc_y);
 
        res = sti_mixer_set_layer_depth(mixer, layer);
@@ -110,7 +114,7 @@ static void sti_drm_plane_destroy(struct drm_plane *plane)
 {
        DRM_DEBUG_DRIVER("\n");
 
-       sti_drm_disable_plane(plane);
+       drm_plane_helper_disable(plane);
        drm_plane_cleanup(plane);
 }
 
@@ -133,10 +137,58 @@ static int sti_drm_plane_set_property(struct drm_plane *plane,
 }
 
 static struct drm_plane_funcs sti_drm_plane_funcs = {
-       .update_plane = sti_drm_update_plane,
-       .disable_plane = sti_drm_disable_plane,
+       .update_plane = drm_atomic_helper_update_plane,
+       .disable_plane = drm_atomic_helper_disable_plane,
        .destroy = sti_drm_plane_destroy,
        .set_property = sti_drm_plane_set_property,
+       .reset = drm_atomic_helper_plane_reset,
+       .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+};
+
+static int sti_drm_plane_prepare_fb(struct drm_plane *plane,
+                                 struct drm_framebuffer *fb,
+                                 const struct drm_plane_state *new_state)
+{
+       return 0;
+}
+
+static void sti_drm_plane_cleanup_fb(struct drm_plane *plane,
+                                  struct drm_framebuffer *fb,
+                                  const struct drm_plane_state *old_fb)
+{
+}
+
+static int sti_drm_plane_atomic_check(struct drm_plane *plane,
+                                     struct drm_plane_state *state)
+{
+       return 0;
+}
+
+static void sti_drm_plane_atomic_update(struct drm_plane *plane,
+                                       struct drm_plane_state *oldstate)
+{
+       struct drm_plane_state *state = plane->state;
+
+       sti_drm_update_plane(plane, state->crtc, state->fb,
+                           state->crtc_x, state->crtc_y,
+                           state->crtc_w, state->crtc_h,
+                           state->src_x, state->src_y,
+                           state->src_w, state->src_h);
+}
+
+static void sti_drm_plane_atomic_disable(struct drm_plane *plane,
+                                        struct drm_plane_state *oldstate)
+{
+       sti_drm_disable_plane(plane);
+}
+
+static const struct drm_plane_helper_funcs sti_drm_plane_helpers_funcs = {
+       .prepare_fb = sti_drm_plane_prepare_fb,
+       .cleanup_fb = sti_drm_plane_cleanup_fb,
+       .atomic_check = sti_drm_plane_atomic_check,
+       .atomic_update = sti_drm_plane_atomic_update,
+       .atomic_disable = sti_drm_plane_atomic_disable,
 };
 
 static void sti_drm_plane_attach_zorder_property(struct drm_plane *plane,
@@ -178,11 +230,13 @@ struct drm_plane *sti_drm_plane_init(struct drm_device *dev,
                return NULL;
        }
 
+       drm_plane_helper_add(&layer->plane, &sti_drm_plane_helpers_funcs);
+
        for (i = 0; i < ARRAY_SIZE(sti_layer_default_zorder); i++)
                if (sti_layer_default_zorder[i] == layer->desc)
                        break;
 
-       default_zorder = i;
+       default_zorder = i + 1;
 
        if (type == DRM_PLANE_TYPE_OVERLAY)
                sti_drm_plane_attach_zorder_property(&layer->plane,
index aeb5070c8363fb6307ab7c6c768dab9fefece269..a9b678af85a6f315716fa82871674519e017b9f3 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/platform_device.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_panel.h>
 
@@ -364,10 +365,13 @@ static void sti_dvo_connector_destroy(struct drm_connector *connector)
 }
 
 static struct drm_connector_funcs sti_dvo_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = sti_dvo_connector_detect,
        .destroy = sti_dvo_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static struct drm_encoder *sti_dvo_find_encoder(struct drm_device *dev)
index a9bbb081ecadc1165031b9c794b753d083b9ee2f..598cd78b0b163cd2e86f92a68e5386325a3a357f 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/platform_device.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 
 /* HDformatter registers */
@@ -611,10 +612,13 @@ static void sti_hda_connector_destroy(struct drm_connector *connector)
 }
 
 static struct drm_connector_funcs sti_hda_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = sti_hda_connector_detect,
        .destroy = sti_hda_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static struct drm_encoder *sti_hda_find_encoder(struct drm_device *dev)
index 1485ade98710a3078de14c8b577db2a5bd15cd21..ae5424bd6b4cbd00d1ffc5b4fd35f11cb56a11cd 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/reset.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 
@@ -663,10 +664,13 @@ static void sti_hdmi_connector_destroy(struct drm_connector *connector)
 }
 
 static struct drm_connector_funcs sti_hdmi_connector_funcs = {
-       .dpms = drm_helper_connector_dpms,
+       .dpms = drm_atomic_helper_connector_dpms,
        .fill_modes = drm_helper_probe_single_connector_modes,
        .detect = sti_hdmi_connector_detect,
        .destroy = sti_hdmi_connector_destroy,
+       .reset = drm_atomic_helper_connector_reset,
+       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 static struct drm_encoder *sti_hdmi_find_encoder(struct drm_device *dev)