These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / msm / mdp / mdp5 / mdp5_plane.c
index 57b8f56..81cd490 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2014-2015 The Linux Foundation. All rights reserved.
  * Copyright (C) 2013 Red Hat
  * Author: Rob Clark <robdclark@gmail.com>
  *
@@ -26,13 +26,12 @@ struct mdp5_plane {
 
        spinlock_t pipe_lock;   /* protect REG_MDP5_PIPE_* registers */
        uint32_t reg_offset;
+       uint32_t caps;
 
        uint32_t flush_mask;    /* used to commit pipe registers */
 
        uint32_t nformats;
        uint32_t formats[32];
-
-       bool enabled;
 };
 #define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base)
 
@@ -42,6 +41,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane,
                unsigned int crtc_w, unsigned int crtc_h,
                uint32_t src_x, uint32_t src_y,
                uint32_t src_w, uint32_t src_h);
+
 static void set_scanout_locked(struct drm_plane *plane,
                struct drm_framebuffer *fb);
 
@@ -56,44 +56,132 @@ static bool plane_enabled(struct drm_plane_state *state)
        return state->fb && state->crtc;
 }
 
-static int mdp5_plane_disable(struct drm_plane *plane)
+static void mdp5_plane_destroy(struct drm_plane *plane)
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
-       struct mdp5_kms *mdp5_kms = get_kms(plane);
-       enum mdp5_pipe pipe = mdp5_plane->pipe;
-
-       DBG("%s: disable", mdp5_plane->name);
 
-       if (mdp5_kms) {
-               /* Release the memory we requested earlier from the SMP: */
-               mdp5_smp_release(mdp5_kms->smp, pipe);
-       }
+       drm_plane_helper_disable(plane);
+       drm_plane_cleanup(plane);
 
-       return 0;
+       kfree(mdp5_plane);
 }
 
-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);
 
-       drm_plane_helper_disable(plane);
-       drm_plane_cleanup(plane);
+       if (!(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP) &&
+               !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP))
+               return;
 
-       kfree(mdp5_plane);
+       if (!dev->mode_config.rotation_property)
+               dev->mode_config.rotation_property =
+                       drm_mode_create_rotation_property(dev,
+                       BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y));
+
+       if (dev->mode_config.rotation_property)
+               drm_object_attach_property(&plane->base,
+                       dev->mode_config.rotation_property,
+                       0);
 }
 
 /* helper to install properties which are common to planes and crtcs */
-void mdp5_plane_install_properties(struct drm_plane *plane,
+static void mdp5_plane_install_properties(struct drm_plane *plane,
                struct drm_mode_object *obj)
 {
-       // XXX
+       struct drm_device *dev = plane->dev;
+       struct msm_drm_private *dev_priv = dev->dev_private;
+       struct drm_property *prop;
+
+#define INSTALL_PROPERTY(name, NAME, init_val, fnc, ...) do { \
+               prop = dev_priv->plane_property[PLANE_PROP_##NAME]; \
+               if (!prop) { \
+                       prop = drm_property_##fnc(dev, 0, #name, \
+                               ##__VA_ARGS__); \
+                       if (!prop) { \
+                               dev_warn(dev->dev, \
+                                       "Create property %s failed\n", \
+                                       #name); \
+                               return; \
+                       } \
+                       dev_priv->plane_property[PLANE_PROP_##NAME] = prop; \
+               } \
+               drm_object_attach_property(&plane->base, prop, init_val); \
+       } while (0)
+
+#define INSTALL_RANGE_PROPERTY(name, NAME, min, max, init_val) \
+               INSTALL_PROPERTY(name, NAME, init_val, \
+                               create_range, min, max)
+
+#define INSTALL_ENUM_PROPERTY(name, NAME, init_val) \
+               INSTALL_PROPERTY(name, NAME, init_val, \
+                               create_enum, name##_prop_enum_list, \
+                               ARRAY_SIZE(name##_prop_enum_list))
+
+       INSTALL_RANGE_PROPERTY(zpos, ZPOS, 1, 255, 1);
+
+       mdp5_plane_install_rotation_property(dev, plane);
+
+#undef INSTALL_RANGE_PROPERTY
+#undef INSTALL_ENUM_PROPERTY
+#undef INSTALL_PROPERTY
 }
 
-int mdp5_plane_set_property(struct drm_plane *plane,
-               struct drm_property *property, uint64_t val)
+static int mdp5_plane_atomic_set_property(struct drm_plane *plane,
+               struct drm_plane_state *state, struct drm_property *property,
+               uint64_t val)
 {
-       // XXX
-       return -EINVAL;
+       struct drm_device *dev = plane->dev;
+       struct mdp5_plane_state *pstate;
+       struct msm_drm_private *dev_priv = dev->dev_private;
+       int ret = 0;
+
+       pstate = to_mdp5_plane_state(state);
+
+#define SET_PROPERTY(name, NAME, type) do { \
+               if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \
+                       pstate->name = (type)val; \
+                       DBG("Set property %s %d", #name, (type)val); \
+                       goto done; \
+               } \
+       } while (0)
+
+       SET_PROPERTY(zpos, ZPOS, uint8_t);
+
+       dev_err(dev->dev, "Invalid property\n");
+       ret = -EINVAL;
+done:
+       return ret;
+#undef SET_PROPERTY
+}
+
+static int mdp5_plane_atomic_get_property(struct drm_plane *plane,
+               const struct drm_plane_state *state,
+               struct drm_property *property, uint64_t *val)
+{
+       struct drm_device *dev = plane->dev;
+       struct mdp5_plane_state *pstate;
+       struct msm_drm_private *dev_priv = dev->dev_private;
+       int ret = 0;
+
+       pstate = to_mdp5_plane_state(state);
+
+#define GET_PROPERTY(name, NAME, type) do { \
+               if (dev_priv->plane_property[PLANE_PROP_##NAME] == property) { \
+                       *val = pstate->name; \
+                       DBG("Get property %s %lld", #name, *val); \
+                       goto done; \
+               } \
+       } while (0)
+
+       GET_PROPERTY(zpos, ZPOS, uint8_t);
+
+       dev_err(dev->dev, "Invalid property\n");
+       ret = -EINVAL;
+done:
+       return ret;
+#undef SET_PROPERTY
 }
 
 static void mdp5_plane_reset(struct drm_plane *plane)
@@ -106,11 +194,15 @@ static void mdp5_plane_reset(struct drm_plane *plane)
        kfree(to_mdp5_plane_state(plane->state));
        mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL);
 
-       if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
-               mdp5_state->zpos = 0;
-       } else {
-               mdp5_state->zpos = 1 + drm_plane_index(plane);
-       }
+       /* assign default blend parameters */
+       mdp5_state->alpha = 255;
+       mdp5_state->premultiplied = 0;
+
+       if (plane->type == DRM_PLANE_TYPE_PRIMARY)
+               mdp5_state->zpos = STAGE_BASE;
+       else
+               mdp5_state->zpos = STAGE0 + drm_plane_index(plane);
+
        mdp5_state->base.plane = plane;
 
        plane->state = &mdp5_state->base;
@@ -149,29 +241,37 @@ static const struct drm_plane_funcs mdp5_plane_funcs = {
                .update_plane = drm_atomic_helper_update_plane,
                .disable_plane = drm_atomic_helper_disable_plane,
                .destroy = mdp5_plane_destroy,
-               .set_property = mdp5_plane_set_property,
+               .set_property = drm_atomic_helper_plane_set_property,
+               .atomic_set_property = mdp5_plane_atomic_set_property,
+               .atomic_get_property = mdp5_plane_atomic_get_property,
                .reset = mdp5_plane_reset,
                .atomic_duplicate_state = mdp5_plane_duplicate_state,
                .atomic_destroy_state = mdp5_plane_destroy_state,
 };
 
 static int mdp5_plane_prepare_fb(struct drm_plane *plane,
-               struct drm_framebuffer *fb,
                const struct drm_plane_state *new_state)
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
        struct mdp5_kms *mdp5_kms = get_kms(plane);
+       struct drm_framebuffer *fb = new_state->fb;
+
+       if (!new_state->fb)
+               return 0;
 
        DBG("%s: prepare: FB[%u]", mdp5_plane->name, fb->base.id);
        return msm_framebuffer_prepare(fb, mdp5_kms->id);
 }
 
 static void mdp5_plane_cleanup_fb(struct drm_plane *plane,
-               struct drm_framebuffer *fb,
                const struct drm_plane_state *old_state)
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
        struct mdp5_kms *mdp5_kms = get_kms(plane);
+       struct drm_framebuffer *fb = old_state->fb;
+
+       if (!fb)
+               return;
 
        DBG("%s: cleanup: FB[%u]", mdp5_plane->name, fb->base.id);
        msm_framebuffer_cleanup(fb, mdp5_kms->id);
@@ -182,10 +282,44 @@ static int mdp5_plane_atomic_check(struct drm_plane *plane,
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
        struct drm_plane_state *old_state = plane->state;
+       const struct mdp_format *format;
+       bool vflip, hflip;
 
        DBG("%s: check (%d -> %d)", mdp5_plane->name,
                        plane_enabled(old_state), plane_enabled(state));
 
+       if (plane_enabled(state)) {
+               format = to_mdp_format(msm_framebuffer_format(state->fb));
+               if (MDP_FORMAT_IS_YUV(format) &&
+                       !pipe_supports_yuv(mdp5_plane->caps)) {
+                       dev_err(plane->dev->dev,
+                               "Pipe doesn't support YUV\n");
+
+                       return -EINVAL;
+               }
+
+               if (!(mdp5_plane->caps & MDP_PIPE_CAP_SCALE) &&
+                       (((state->src_w >> 16) != state->crtc_w) ||
+                       ((state->src_h >> 16) != state->crtc_h))) {
+                       dev_err(plane->dev->dev,
+                               "Pipe doesn't support scaling (%dx%d -> %dx%d)\n",
+                               state->src_w >> 16, state->src_h >> 16,
+                               state->crtc_w, state->crtc_h);
+
+                       return -EINVAL;
+               }
+
+               hflip = !!(state->rotation & BIT(DRM_REFLECT_X));
+               vflip = !!(state->rotation & BIT(DRM_REFLECT_Y));
+               if ((vflip && !(mdp5_plane->caps & MDP_PIPE_CAP_VFLIP)) ||
+                       (hflip && !(mdp5_plane->caps & MDP_PIPE_CAP_HFLIP))) {
+                       dev_err(plane->dev->dev,
+                               "Pipe doesn't support flip\n");
+
+                       return -EINVAL;
+               }
+       }
+
        if (plane_enabled(state) && plane_enabled(old_state)) {
                /* we cannot change SMP block configuration during scanout: */
                bool full_modeset = false;
@@ -224,7 +358,6 @@ static void mdp5_plane_atomic_update(struct drm_plane *plane,
 
        if (!plane_enabled(state)) {
                to_mdp5_plane_state(state)->pending = true;
-               mdp5_plane_disable(plane);
        } else if (to_mdp5_plane_state(state)->mode_changed) {
                int ret;
                to_mdp5_plane_state(state)->pending = true;
@@ -365,68 +498,170 @@ static int calc_phase_step(uint32_t src, uint32_t dst, uint32_t *out_phase)
        return 0;
 }
 
-static int calc_scalex_steps(uint32_t pixel_format, uint32_t src, uint32_t dest,
-               uint32_t phasex_steps[2])
+static int calc_scalex_steps(struct drm_plane *plane,
+               uint32_t pixel_format, uint32_t src, uint32_t dest,
+               uint32_t phasex_steps[COMP_MAX])
 {
+       struct mdp5_kms *mdp5_kms = get_kms(plane);
+       struct device *dev = mdp5_kms->dev->dev;
        uint32_t phasex_step;
        unsigned int hsub;
        int ret;
 
        ret = calc_phase_step(src, dest, &phasex_step);
-       if (ret)
+       if (ret) {
+               dev_err(dev, "X scaling (%d->%d) failed: %d\n", src, dest, ret);
                return ret;
+       }
 
        hsub = drm_format_horz_chroma_subsampling(pixel_format);
 
-       phasex_steps[0] = phasex_step;
-       phasex_steps[1] = phasex_step / hsub;
+       phasex_steps[COMP_0]   = phasex_step;
+       phasex_steps[COMP_3]   = phasex_step;
+       phasex_steps[COMP_1_2] = phasex_step / hsub;
 
        return 0;
 }
 
-static int calc_scaley_steps(uint32_t pixel_format, uint32_t src, uint32_t dest,
-               uint32_t phasey_steps[2])
+static int calc_scaley_steps(struct drm_plane *plane,
+               uint32_t pixel_format, uint32_t src, uint32_t dest,
+               uint32_t phasey_steps[COMP_MAX])
 {
+       struct mdp5_kms *mdp5_kms = get_kms(plane);
+       struct device *dev = mdp5_kms->dev->dev;
        uint32_t phasey_step;
        unsigned int vsub;
        int ret;
 
        ret = calc_phase_step(src, dest, &phasey_step);
-       if (ret)
+       if (ret) {
+               dev_err(dev, "Y scaling (%d->%d) failed: %d\n", src, dest, ret);
                return ret;
+       }
 
        vsub = drm_format_vert_chroma_subsampling(pixel_format);
 
-       phasey_steps[0] = phasey_step;
-       phasey_steps[1] = phasey_step / vsub;
+       phasey_steps[COMP_0]   = phasey_step;
+       phasey_steps[COMP_3]   = phasey_step;
+       phasey_steps[COMP_1_2] = phasey_step / vsub;
 
        return 0;
 }
 
-static uint32_t get_scalex_config(uint32_t src, uint32_t dest)
+static uint32_t get_scale_config(const struct mdp_format *format,
+               uint32_t src, uint32_t dst, bool horz)
+{
+       bool scaling = format->is_yuv ? true : (src != dst);
+       uint32_t sub, pix_fmt = format->base.pixel_format;
+       uint32_t ya_filter, uv_filter;
+       bool yuv = format->is_yuv;
+
+       if (!scaling)
+               return 0;
+
+       if (yuv) {
+               sub = horz ? drm_format_horz_chroma_subsampling(pix_fmt) :
+                            drm_format_vert_chroma_subsampling(pix_fmt);
+               uv_filter = ((src / sub) <= dst) ?
+                                  SCALE_FILTER_BIL : SCALE_FILTER_PCMN;
+       }
+       ya_filter = (src <= dst) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN;
+
+       if (horz)
+               return  MDP5_PIPE_SCALE_CONFIG_SCALEX_EN |
+                       MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0(ya_filter) |
+                       MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3(ya_filter) |
+                       COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2(uv_filter));
+       else
+               return  MDP5_PIPE_SCALE_CONFIG_SCALEY_EN |
+                       MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0(ya_filter) |
+                       MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3(ya_filter) |
+                       COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2(uv_filter));
+}
+
+static void calc_pixel_ext(const struct mdp_format *format,
+               uint32_t src, uint32_t dst, uint32_t phase_step[2],
+               int pix_ext_edge1[COMP_MAX], int pix_ext_edge2[COMP_MAX],
+               bool horz)
 {
-       uint32_t filter;
+       bool scaling = format->is_yuv ? true : (src != dst);
+       int i;
 
-       filter = (src <= dest) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN;
+       /*
+        * Note:
+        * We assume here that:
+        *     1. PCMN filter is used for downscale
+        *     2. bilinear filter is used for upscale
+        *     3. we are in a single pipe configuration
+        */
 
-       return  MDP5_PIPE_SCALE_CONFIG_SCALEX_EN |
-               MDP5_PIPE_SCALE_CONFIG_SCALEX_MIN_FILTER(filter) |
-               MDP5_PIPE_SCALE_CONFIG_SCALEX_CR_FILTER(filter)  |
-               MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(filter);
+       for (i = 0; i < COMP_MAX; i++) {
+               pix_ext_edge1[i] = 0;
+               pix_ext_edge2[i] = scaling ? 1 : 0;
+       }
 }
 
-static uint32_t get_scaley_config(uint32_t src, uint32_t dest)
+static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe,
+       const struct mdp_format *format,
+       uint32_t src_w, int pe_left[COMP_MAX], int pe_right[COMP_MAX],
+       uint32_t src_h, int pe_top[COMP_MAX], int pe_bottom[COMP_MAX])
 {
-       uint32_t filter;
+       uint32_t pix_fmt = format->base.pixel_format;
+       uint32_t lr, tb, req;
+       int i;
+
+       for (i = 0; i < COMP_MAX; i++) {
+               uint32_t roi_w = src_w;
+               uint32_t roi_h = src_h;
 
-       filter = (src <= dest) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN;
+               if (format->is_yuv && i == COMP_1_2) {
+                       roi_w /= drm_format_horz_chroma_subsampling(pix_fmt);
+                       roi_h /= drm_format_vert_chroma_subsampling(pix_fmt);
+               }
 
-       return  MDP5_PIPE_SCALE_CONFIG_SCALEY_EN |
-               MDP5_PIPE_SCALE_CONFIG_SCALEY_MIN_FILTER(filter) |
-               MDP5_PIPE_SCALE_CONFIG_SCALEY_CR_FILTER(filter)  |
-               MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(filter);
+               lr  = (pe_left[i] >= 0) ?
+                       MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT(pe_left[i]) :
+                       MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF(pe_left[i]);
+
+               lr |= (pe_right[i] >= 0) ?
+                       MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT(pe_right[i]) :
+                       MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF(pe_right[i]);
+
+               tb  = (pe_top[i] >= 0) ?
+                       MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT(pe_top[i]) :
+                       MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF(pe_top[i]);
+
+               tb |= (pe_bottom[i] >= 0) ?
+                       MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT(pe_bottom[i]) :
+                       MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF(pe_bottom[i]);
+
+               req  = MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT(roi_w +
+                               pe_left[i] + pe_right[i]);
+
+               req |= MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM(roi_h +
+                               pe_top[i] + pe_bottom[i]);
+
+               mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_LR(pipe, i), lr);
+               mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_TB(pipe, i), tb);
+               mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS(pipe, i), req);
+
+               DBG("comp-%d (L/R): rpt=%d/%d, ovf=%d/%d, req=%d", i,
+                       FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT),
+                       FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT),
+                       FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF),
+                       FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF),
+                       FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT));
+
+               DBG("comp-%d (T/B): rpt=%d/%d, ovf=%d/%d, req=%d", i,
+                       FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT),
+                       FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT),
+                       FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF),
+                       FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF),
+                       FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM));
+       }
 }
 
+
 static int mdp5_plane_mode_set(struct drm_plane *plane,
                struct drm_crtc *crtc, struct drm_framebuffer *fb,
                int crtc_x, int crtc_y,
@@ -435,15 +670,18 @@ static int mdp5_plane_mode_set(struct drm_plane *plane,
                uint32_t src_w, uint32_t src_h)
 {
        struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+       struct drm_plane_state *pstate = plane->state;
        struct mdp5_kms *mdp5_kms = get_kms(plane);
-       struct device *dev = mdp5_kms->dev->dev;
        enum mdp5_pipe pipe = mdp5_plane->pipe;
        const struct mdp_format *format;
        uint32_t nplanes, config = 0;
-       /* below array -> index 0: comp 0/3 ; index 1: comp 1/2 */
-       uint32_t phasex_step[2] = {0,}, phasey_step[2] = {0,};
+       uint32_t phasex_step[COMP_MAX] = {0,}, phasey_step[COMP_MAX] = {0,};
+       bool pe = mdp5_plane->caps & MDP_PIPE_CAP_SW_PIX_EXT;
+       int pe_left[COMP_MAX], pe_right[COMP_MAX];
+       int pe_top[COMP_MAX], pe_bottom[COMP_MAX];
        uint32_t hdecm = 0, vdecm = 0;
        uint32_t pix_format;
+       bool vflip, hflip;
        unsigned long flags;
        int ret;
 
@@ -467,10 +705,12 @@ static int mdp5_plane_mode_set(struct drm_plane *plane,
                        crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);
 
        /* Request some memory from the SMP: */
-       ret = mdp5_smp_request(mdp5_kms->smp,
-                       mdp5_plane->pipe, fb->pixel_format, src_w);
-       if (ret)
-               return ret;
+       if (mdp5_kms->smp) {
+               ret = mdp5_smp_request(mdp5_kms->smp,
+                               mdp5_plane->pipe, format, src_w, false);
+               if (ret)
+                       return ret;
+       }
 
        /*
         * Currently we update the hw for allocations/requests immediately,
@@ -478,32 +718,34 @@ static int mdp5_plane_mode_set(struct drm_plane *plane,
         * would move into atomic->check_plane_state(), while updating the
         * hw would remain here:
         */
-       mdp5_smp_configure(mdp5_kms->smp, pipe);
+       if (mdp5_kms->smp)
+               mdp5_smp_configure(mdp5_kms->smp, pipe);
 
-       /* SCALE is used to both scale and up-sample chroma components */
+       ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, phasex_step);
+       if (ret)
+               return ret;
 
-       if ((src_w != crtc_w) || MDP_FORMAT_IS_YUV(format)) {
-               /* TODO calc hdecm */
-               ret = calc_scalex_steps(pix_format, src_w, crtc_w, phasex_step);
-               if (ret) {
-                       dev_err(dev, "X scaling (%d -> %d) failed: %d\n",
-                                       src_w, crtc_w, ret);
-                       return ret;
-               }
-               config |= get_scalex_config(src_w, crtc_w);
-       }
+       ret = calc_scaley_steps(plane, pix_format, src_h, crtc_h, phasey_step);
+       if (ret)
+               return ret;
 
-       if ((src_h != crtc_h) || MDP_FORMAT_IS_YUV(format)) {
-               /* TODO calc vdecm */
-               ret = calc_scaley_steps(pix_format, src_h, crtc_h, phasey_step);
-               if (ret) {
-                       dev_err(dev, "Y scaling (%d -> %d) failed: %d\n",
-                                       src_h, crtc_h, ret);
-                       return ret;
-               }
-               config |= get_scaley_config(src_h, crtc_h);
+       if (mdp5_plane->caps & MDP_PIPE_CAP_SW_PIX_EXT) {
+               calc_pixel_ext(format, src_w, crtc_w, phasex_step,
+                                        pe_left, pe_right, true);
+               calc_pixel_ext(format, src_h, crtc_h, phasey_step,
+                                       pe_top, pe_bottom, false);
        }
 
+       /* TODO calc hdecm, vdecm */
+
+       /* SCALE is used to both scale and up-sample chroma components */
+       config |= get_scale_config(format, src_w, crtc_w, true);
+       config |= get_scale_config(format, src_h, crtc_h, false);
+       DBG("scale config = %x", config);
+
+       hflip = !!(pstate->rotation & BIT(DRM_REFLECT_X));
+       vflip = !!(pstate->rotation & BIT(DRM_REFLECT_Y));
+
        spin_lock_irqsave(&mdp5_plane->pipe_lock, flags);
 
        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe),
@@ -535,7 +777,7 @@ static int mdp5_plane_mode_set(struct drm_plane *plane,
                        MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) |
                        MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |
                        COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) |
-                       MDP5_PIPE_SRC_FORMAT_NUM_PLANES(format->fetch_type) |
+                       MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(format->fetch_type) |
                        MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample));
 
        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe),
@@ -545,29 +787,41 @@ static int mdp5_plane_mode_set(struct drm_plane *plane,
                        MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3]));
 
        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe),
+                       (hflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_LR : 0) |
+                       (vflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_UD : 0) |
+                       COND(pe, MDP5_PIPE_SRC_OP_MODE_SW_PIX_EXT_OVERRIDE) |
                        MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS));
 
        /* not using secure mode: */
        mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0);
 
-       mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe),
-                       phasex_step[0]);
-       mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe),
-                       phasey_step[0]);
-       mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe),
-                       phasex_step[1]);
-       mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe),
-                       phasey_step[1]);
-       mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe),
-                       MDP5_PIPE_DECIMATION_VERT(vdecm) |
-                       MDP5_PIPE_DECIMATION_HORZ(hdecm));
-       mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), config);
-
-       if (MDP_FORMAT_IS_YUV(format))
-               csc_enable(mdp5_kms, pipe,
-                               mdp_get_default_csc_cfg(CSC_YUV2RGB));
-       else
-               csc_disable(mdp5_kms, pipe);
+       if (mdp5_plane->caps & MDP_PIPE_CAP_SW_PIX_EXT)
+               mdp5_write_pixel_ext(mdp5_kms, pipe, format,
+                               src_w, pe_left, pe_right,
+                               src_h, pe_top, pe_bottom);
+
+       if (mdp5_plane->caps & MDP_PIPE_CAP_SCALE) {
+               mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe),
+                               phasex_step[COMP_0]);
+               mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe),
+                               phasey_step[COMP_0]);
+               mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe),
+                               phasex_step[COMP_1_2]);
+               mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe),
+                               phasey_step[COMP_1_2]);
+               mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe),
+                               MDP5_PIPE_DECIMATION_VERT(vdecm) |
+                               MDP5_PIPE_DECIMATION_HORZ(hdecm));
+               mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe), config);
+       }
+
+       if (mdp5_plane->caps & MDP_PIPE_CAP_CSC) {
+               if (MDP_FORMAT_IS_YUV(format))
+                       csc_enable(mdp5_kms, pipe,
+                                       mdp_get_default_csc_cfg(CSC_YUV2RGB));
+               else
+                       csc_disable(mdp5_kms, pipe);
+       }
 
        set_scanout_locked(plane, fb);
 
@@ -584,7 +838,8 @@ void mdp5_plane_complete_flip(struct drm_plane *plane)
 
        DBG("%s: complete flip", mdp5_plane->name);
 
-       mdp5_smp_commit(mdp5_kms->smp, pipe);
+       if (mdp5_kms->smp)
+               mdp5_smp_commit(mdp5_kms->smp, pipe);
 
        to_mdp5_plane_state(plane->state)->pending = false;
 }
@@ -602,9 +857,24 @@ uint32_t mdp5_plane_get_flush(struct drm_plane *plane)
        return mdp5_plane->flush_mask;
 }
 
+/* called after vsync in thread context */
+void mdp5_plane_complete_commit(struct drm_plane *plane,
+       struct drm_plane_state *state)
+{
+       struct mdp5_kms *mdp5_kms = get_kms(plane);
+       struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+       enum mdp5_pipe pipe = mdp5_plane->pipe;
+
+       if (!plane_enabled(plane->state) && mdp5_kms->smp) {
+               DBG("%s: free SMP", mdp5_plane->name);
+               mdp5_smp_release(mdp5_kms->smp, pipe);
+       }
+}
+
 /* initialize plane */
 struct drm_plane *mdp5_plane_init(struct drm_device *dev,
-               enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset)
+               enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset,
+               uint32_t caps)
 {
        struct drm_plane *plane = NULL;
        struct mdp5_plane *mdp5_plane;
@@ -621,9 +891,11 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev,
 
        mdp5_plane->pipe = pipe;
        mdp5_plane->name = pipe2name(pipe);
+       mdp5_plane->caps = caps;
 
-       mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats,
-                       ARRAY_SIZE(mdp5_plane->formats));
+       mdp5_plane->nformats = mdp_get_formats(mdp5_plane->formats,
+               ARRAY_SIZE(mdp5_plane->formats),
+               !pipe_supports_yuv(mdp5_plane->caps));
 
        mdp5_plane->flush_mask = mdp_ctl_flush_mask_pipe(pipe);
        mdp5_plane->reg_offset = reg_offset;