Hi,

Minor comments below. LGTM otherwise.

On 11/05/2016 09:56 PM, Rob Clark wrote:
(re)assign the hw pipes to planes based on required caps, and to handle
situations where we could not modify an in-use plane (ie. SMP block
reallocation).

This means all planes advertise the superset of formats and properties.
Userspace must (as always) use atomic TEST_ONLY step for atomic updates,
as not all planes may be available for use on every frame.

The mapping of hwpipe to plane is stored in mdp5_state, so that state
updates are atomically committed in the same way that plane/etc state
updates are managed.  This is needed because the mdp5_plane_state keeps
a pointer to the hwpipe, and we don't want global state to become out
of sync with the plane state if an atomic update fails, we hit deadlock/
backoff scenario, etc.  The use of state_lock keeps multiple parallel
updates which both re-assign hwpipes properly serialized.

Signed-off-by: Rob Clark <robdcl...@gmail.com>
---
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c  |   2 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c   |   4 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h   |   7 +-
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c  |  70 +++++++++++++
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h  |  10 ++
 drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 158 ++++++++++++++++--------------
 6 files changed, 171 insertions(+), 80 deletions(-)

diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c 
b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index 6213f51..99958f7 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -407,7 +407,7 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,
        for (i = 0; i < cnt; i++) {
                pstates[i].state->stage = STAGE_BASE + i + base;
                DBG("%s: assign pipe %s on stage=%d", crtc->name,
-                               pipe2name(mdp5_plane_pipe(pstates[i].plane)),
+                               pstates[i].plane->name,
                                pstates[i].state->stage);
        }

diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c 
b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index ca6dfeb..3542adf 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -92,7 +92,7 @@ struct mdp5_state *mdp5_get_state(struct drm_atomic_state *s)
                return ERR_PTR(-ENOMEM);

        /* Copy state: */
-       /* TODO */
+       new_state->hwpipe = mdp5_kms->state->hwpipe;

        state->state = new_state;

@@ -377,7 +377,7 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
                struct drm_plane *plane;
                struct drm_crtc *crtc;

-               plane = mdp5_plane_init(dev, mdp5_kms->hwpipes[i], primary);
+               plane = mdp5_plane_init(dev, primary);
                if (IS_ERR(plane)) {
                        ret = PTR_ERR(plane);
                        dev_err(dev->dev, "failed to construct plane %d 
(%d)\n", i, ret);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h 
b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index 52914ec..4475090 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -82,7 +82,7 @@ struct mdp5_kms {
  * For atomic updates which require modifying global state,
  */
 struct mdp5_state {
-       uint32_t dummy;
+       struct mdp5_hw_pipe_state hwpipe;

I was wondering if we could rename the above as hwpipes/hwpipe_state/hwpipe_map,
because it gets a confusing since variables of the type mdp5_hw_pipe are
also named hwpipe.

 };

 struct mdp5_state *__must_check
@@ -94,6 +94,8 @@ mdp5_get_state(struct drm_atomic_state *s);
 struct mdp5_plane_state {
        struct drm_plane_state base;

+       struct mdp5_hw_pipe *hwpipe;
+
        /* aligned with property */
        uint8_t premultiplied;
        uint8_t zpos;
@@ -232,8 +234,7 @@ uint32_t mdp5_plane_get_flush(struct drm_plane *plane);
 void mdp5_plane_complete_commit(struct drm_plane *plane,
        struct drm_plane_state *state);
 enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane);
-struct drm_plane *mdp5_plane_init(struct drm_device *dev,
-               struct mdp5_hw_pipe *hwpipe, bool primary);
+struct drm_plane *mdp5_plane_init(struct drm_device *dev, bool primary);

 uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);

diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c 
b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c
index 7f3e8e50..115de7d 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.c
@@ -17,6 +17,76 @@

 #include "mdp5_kms.h"

+struct mdp5_hw_pipe *mdp5_pipe_assign(struct drm_atomic_state *s,
+               struct drm_plane *plane, uint32_t caps)
+{
+       struct msm_drm_private *priv = s->dev->dev_private;
+       struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(priv->kms));
+       struct mdp5_state *state = mdp5_get_state(s);
+       struct mdp5_hw_pipe_state *old_state, *new_state;
+       struct mdp5_hw_pipe *hwpipe = NULL;
+       int i;
+

Can we assign "state = mdp5_get_state(s);" here instead for clarity? Since the 
func
does more than just getting the mdp5_state pointer?

+       if (IS_ERR(state))
+               return ERR_CAST(state);
+
+       /* grab old_state after mdp5_get_state(), since now we hold lock: */
+       old_state = &mdp5_kms->state->hwpipe;
+       new_state = &state->hwpipe;
+
+       for (i = 0; i < mdp5_kms->num_hwpipes; i++) {
+               struct mdp5_hw_pipe *cur = mdp5_kms->hwpipes[i];
+
+               /* skip if already in-use.. check both new and old state,
+                * since we cannot immediately re-use a pipe that is
+                * released in the current update in some cases:
+                *  (1) mdp5 has SMP (non-double-buffered)
+                *  (2) hw pipe previously assigned to different CRTC
+                *      (vblanks might not be aligned)

For 1), we have non-SMP SoCs (a.k.a 8996), and for 2) we would have
the info whether the hwpipe is changing crtcs in old and new states.
I guess we could leave this as an optimization for the future.


+                */
+               if (new_state->hwpipe_to_plane[cur->idx] ||
+                               old_state->hwpipe_to_plane[cur->idx])
+                       continue;
+
+               /* skip if doesn't support some required caps: */
+               if (caps & ~cur->caps)
+                       continue;
+
+               /* possible candidate, take the one with the
+                * fewest unneeded caps bits set:
+                */
+               if (!hwpipe || (hweight_long(cur->caps & ~caps) <
+                               hweight_long(hwpipe->caps & ~caps)))
+                       hwpipe = cur;
+       }
+
+       if (!hwpipe)
+               return ERR_PTR(-ENOMEM);
+
+       DBG("%s: assign to plane %s for caps %x",
+                       hwpipe->name, plane->name, caps);
+       new_state->hwpipe_to_plane[hwpipe->idx] = plane;
+
+       return hwpipe;
+}
+
+void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe *hwpipe)
+{
+       struct mdp5_state *state = mdp5_get_state(s);
+       struct mdp5_hw_pipe_state *new_state = &state->hwpipe;
+
+       if (!hwpipe)
+               return;
+
+       if (WARN_ON(!new_state->hwpipe_to_plane[hwpipe->idx]))
+               return;
+
+       DBG("%s: release from plane %s", hwpipe->name,
+               new_state->hwpipe_to_plane[hwpipe->idx]->name);
+
+       new_state->hwpipe_to_plane[hwpipe->idx] = NULL;
+}
+
 void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe)
 {
        kfree(hwpipe);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h 
b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h
index dcfa0e0..bac5900 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_pipe.h
@@ -32,6 +32,16 @@ struct mdp5_hw_pipe {
        uint32_t flush_mask;      /* used to commit pipe registers */
 };

+/* global atomic state of assignment between pipes and planes: */
+struct mdp5_hw_pipe_state {
+       struct drm_plane *hwpipe_to_plane[16];

We could use SSPP_MAX here instead of 16. We would need to move its
definition from mdp5_crtc.c to mdp5_kms.h, though.

+};
+
+struct mdp5_hw_pipe *__must_check
+mdp5_pipe_assign(struct drm_atomic_state *s, struct drm_plane *plane,
+               uint32_t caps);
+void mdp5_pipe_release(struct drm_atomic_state *s, struct mdp5_hw_pipe 
*hwpipe);
+
 struct mdp5_hw_pipe *mdp5_pipe_init(enum mdp5_pipe pipe,
                uint32_t reg_offset, uint32_t caps);
 void mdp5_pipe_destroy(struct mdp5_hw_pipe *hwpipe);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c 
b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index e4ecfeb..64e97ef 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -22,8 +22,6 @@
 struct mdp5_plane {
        struct drm_plane base;

-       struct mdp5_hw_pipe *hwpipe;
-
        uint32_t nformats;
        uint32_t formats[32];
 };
@@ -63,12 +61,6 @@ static void mdp5_plane_destroy(struct drm_plane *plane)
 static void mdp5_plane_install_rotation_property(struct drm_device *dev,
                struct drm_plane *plane)
 {
-       struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
-
-       if (!(mdp5_plane->hwpipe->caps & MDP_PIPE_CAP_HFLIP) &&
-               !(mdp5_plane->hwpipe->caps & MDP_PIPE_CAP_VFLIP))
-               return;
-
        drm_plane_create_rotation_property(plane,
                                           DRM_ROTATE_0,
                                           DRM_ROTATE_0 |
@@ -184,6 +176,8 @@ mdp5_plane_atomic_print_state(struct drm_printer *p,
 {
        struct mdp5_plane_state *pstate = to_mdp5_plane_state(state);

+       drm_printf(p, "\thwpipe=%s\n", pstate->hwpipe ?
+                       pstate->hwpipe->name : "(null)");
        drm_printf(p, "\tpremultiplied=%u\n", pstate->premultiplied);
        drm_printf(p, "\tzpos=%u\n", pstate->zpos);
        drm_printf(p, "\talpha=%u\n", pstate->alpha);
@@ -237,10 +231,12 @@ mdp5_plane_duplicate_state(struct drm_plane *plane)
 static void mdp5_plane_destroy_state(struct drm_plane *plane,
                struct drm_plane_state *state)
 {
+       struct mdp5_plane_state *pstate = to_mdp5_plane_state(state);
+
        if (state->fb)
                drm_framebuffer_unreference(state->fb);

-       kfree(to_mdp5_plane_state(state));
+       kfree(pstate);
 }

 static const struct drm_plane_funcs mdp5_plane_funcs = {
@@ -285,70 +281,81 @@ static void mdp5_plane_cleanup_fb(struct drm_plane *plane,
 static int mdp5_plane_atomic_check(struct drm_plane *plane,
                struct drm_plane_state *state)
 {
-       struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+       struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(state);
        struct drm_plane_state *old_state = plane->state;
-       const struct mdp_format *format;
-       bool vflip, hflip;
+       bool new_hwpipe = false;
+       uint32_t caps = 0;

        DBG("%s: check (%d -> %d)", plane->name,
                        plane_enabled(old_state), plane_enabled(state));

+       /* We don't allow faster-than-vblank updates.. if we did add this
+        * some day, we would need to disallow in cases where hwpipe
+        * changes
+        */
+       if (WARN_ON(to_mdp5_plane_state(old_state)->pending))
+               return -EBUSY;
+
        if (plane_enabled(state)) {
                unsigned int rotation;
+               const struct mdp_format *format;

                format = to_mdp_format(msm_framebuffer_format(state->fb));
-               if (MDP_FORMAT_IS_YUV(format) &&
-                       !pipe_supports_yuv(mdp5_plane->hwpipe->caps)) {
-                       DBG("Pipe doesn't support YUV\n");
-
-                       return -EINVAL;
-               }
-
-               if (!(mdp5_plane->hwpipe->caps & MDP_PIPE_CAP_SCALE) &&
-                       (((state->src_w >> 16) != state->crtc_w) ||
-                       ((state->src_h >> 16) != state->crtc_h))) {
-                       DBG("Pipe doesn't support scaling (%dx%d -> %dx%d)\n",
-                               state->src_w >> 16, state->src_h >> 16,
-                               state->crtc_w, state->crtc_h);
+               if (MDP_FORMAT_IS_YUV(format))
+                       caps |= MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC;

-                       return -EINVAL;
-               }
+               if (((state->src_w >> 16) != state->crtc_w) ||
+                               ((state->src_h >> 16) != state->crtc_h))
+                       caps |= MDP_PIPE_CAP_SCALE;

                rotation = drm_rotation_simplify(state->rotation,
                                                 DRM_ROTATE_0 |
                                                 DRM_REFLECT_X |
                                                 DRM_REFLECT_Y);

-               hflip = !!(rotation & DRM_REFLECT_X);
-               vflip = !!(rotation & DRM_REFLECT_Y);
-
-               if ((vflip && !(mdp5_plane->hwpipe->caps & MDP_PIPE_CAP_VFLIP)) 
||
-                       (hflip && !(mdp5_plane->hwpipe->caps & 
MDP_PIPE_CAP_HFLIP))) {
-                       DBG("Pipe doesn't support flip\n");
-
-                       return -EINVAL;
+               if (rotation & DRM_REFLECT_X)
+                       caps |= MDP_PIPE_CAP_HFLIP;
+
+               if (rotation & DRM_REFLECT_Y)
+                       caps |= MDP_PIPE_CAP_VFLIP;
+
+               /* (re)allocate hw pipe if we don't have one or caps-mismatch: 
*/
+               if (!mdp5_state->hwpipe || (caps & ~mdp5_state->hwpipe->caps))
+                       new_hwpipe = true;
+
+               if (plane_enabled(old_state)) {
+                       bool full_modeset = false;
+                       if (state->fb->pixel_format != 
old_state->fb->pixel_format) {
+                               DBG("%s: pixel_format change!", plane->name);
+                               full_modeset = true;
+                       }
+                       if (state->src_w != old_state->src_w) {
+                               DBG("%s: src_w change!", plane->name);
+                               full_modeset = true;
+                       }
+                       if (full_modeset) {
+                               /* cannot change SMP block allocation during
+                                * scanout:
+                                */
+                               if (get_kms(plane)->smp)
+                                       new_hwpipe = true;
+                       }
                }
-       }

-       if (plane_enabled(state) && plane_enabled(old_state)) {
-               /* we cannot change SMP block configuration during scanout: */
-               bool full_modeset = false;
-               if (state->fb->pixel_format != old_state->fb->pixel_format) {
-                       DBG("%s: pixel_format change!", plane->name);
-                       full_modeset = true;
-               }
-               if (state->src_w != old_state->src_w) {
-                       DBG("%s: src_w change!", plane->name);
-                       full_modeset = true;
-               }
-               if (to_mdp5_plane_state(old_state)->pending) {
-                       DBG("%s: still pending!", plane->name);
-                       full_modeset = true;
-               }
-               if (full_modeset) {
-                       struct drm_crtc_state *crtc_state =
-                                       drm_atomic_get_crtc_state(state->state, 
state->crtc);
-                       crtc_state->mode_changed = true;
+               /* (re)assign hwpipe if needed, otherwise keep old one: */
+               if (new_hwpipe) {
+                       /* TODO maybe we want to re-assign hwpipe sometimes
+                        * in cases when we no-longer need some caps to make
+                        * it available for other planes?
+                        */
+                       struct mdp5_hw_pipe *hwpipe = mdp5_state->hwpipe;

Maybe hwpipe above could be curr_hwpipe or old_hwpipe?

+                       mdp5_state->hwpipe =
+                               mdp5_pipe_assign(state->state, plane, caps);
+                       if (IS_ERR(mdp5_state->hwpipe)) {
+                               DBG("%s: failed to assign hwpipe!", 
plane->name);
+                               return PTR_ERR(mdp5_state->hwpipe);
+                       }
+                       mdp5_pipe_release(state->state, hwpipe);
                }
        }

Thanks,
Archit

--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
_______________________________________________
Freedreno mailing list
Freedreno@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/freedreno

Reply via email to