Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2014-2015 The Linux Foundation. All rights reserved.
0004  * Copyright (C) 2013 Red Hat
0005  * Author: Rob Clark <robdclark@gmail.com>
0006  */
0007 
0008 #include <drm/drm_atomic.h>
0009 #include <drm/drm_blend.h>
0010 #include <drm/drm_damage_helper.h>
0011 #include <drm/drm_fourcc.h>
0012 #include <drm/drm_framebuffer.h>
0013 #include <drm/drm_gem_atomic_helper.h>
0014 #include <drm/drm_print.h>
0015 
0016 #include "mdp5_kms.h"
0017 
0018 struct mdp5_plane {
0019     struct drm_plane base;
0020 
0021     uint32_t nformats;
0022     uint32_t formats[32];
0023 };
0024 #define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base)
0025 
0026 static int mdp5_plane_mode_set(struct drm_plane *plane,
0027         struct drm_crtc *crtc, struct drm_framebuffer *fb,
0028         struct drm_rect *src, struct drm_rect *dest);
0029 
0030 static struct mdp5_kms *get_kms(struct drm_plane *plane)
0031 {
0032     struct msm_drm_private *priv = plane->dev->dev_private;
0033     return to_mdp5_kms(to_mdp_kms(priv->kms));
0034 }
0035 
0036 static bool plane_enabled(struct drm_plane_state *state)
0037 {
0038     return state->visible;
0039 }
0040 
0041 static void mdp5_plane_destroy(struct drm_plane *plane)
0042 {
0043     struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
0044 
0045     drm_plane_cleanup(plane);
0046 
0047     kfree(mdp5_plane);
0048 }
0049 
0050 /* helper to install properties which are common to planes and crtcs */
0051 static void mdp5_plane_install_properties(struct drm_plane *plane,
0052         struct drm_mode_object *obj)
0053 {
0054     unsigned int zpos;
0055 
0056     drm_plane_create_rotation_property(plane,
0057                        DRM_MODE_ROTATE_0,
0058                        DRM_MODE_ROTATE_0 |
0059                        DRM_MODE_ROTATE_180 |
0060                        DRM_MODE_REFLECT_X |
0061                        DRM_MODE_REFLECT_Y);
0062     drm_plane_create_alpha_property(plane);
0063     drm_plane_create_blend_mode_property(plane,
0064             BIT(DRM_MODE_BLEND_PIXEL_NONE) |
0065             BIT(DRM_MODE_BLEND_PREMULTI) |
0066             BIT(DRM_MODE_BLEND_COVERAGE));
0067 
0068     if (plane->type == DRM_PLANE_TYPE_PRIMARY)
0069         zpos = STAGE_BASE;
0070     else
0071         zpos = STAGE0 + drm_plane_index(plane);
0072     drm_plane_create_zpos_property(plane, zpos, 1, 255);
0073 }
0074 
0075 static void
0076 mdp5_plane_atomic_print_state(struct drm_printer *p,
0077         const struct drm_plane_state *state)
0078 {
0079     struct mdp5_plane_state *pstate = to_mdp5_plane_state(state);
0080     struct mdp5_kms *mdp5_kms = get_kms(state->plane);
0081 
0082     drm_printf(p, "\thwpipe=%s\n", pstate->hwpipe ?
0083             pstate->hwpipe->name : "(null)");
0084     if (mdp5_kms->caps & MDP_CAP_SRC_SPLIT)
0085         drm_printf(p, "\tright-hwpipe=%s\n",
0086                pstate->r_hwpipe ? pstate->r_hwpipe->name :
0087                           "(null)");
0088     drm_printf(p, "\tblend_mode=%u\n", pstate->base.pixel_blend_mode);
0089     drm_printf(p, "\tzpos=%u\n", pstate->base.zpos);
0090     drm_printf(p, "\tnormalized_zpos=%u\n", pstate->base.normalized_zpos);
0091     drm_printf(p, "\talpha=%u\n", pstate->base.alpha);
0092     drm_printf(p, "\tstage=%s\n", stage2name(pstate->stage));
0093 }
0094 
0095 static void mdp5_plane_reset(struct drm_plane *plane)
0096 {
0097     struct mdp5_plane_state *mdp5_state;
0098 
0099     if (plane->state)
0100         __drm_atomic_helper_plane_destroy_state(plane->state);
0101 
0102     kfree(to_mdp5_plane_state(plane->state));
0103     plane->state = NULL;
0104     mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL);
0105     if (!mdp5_state)
0106         return;
0107     __drm_atomic_helper_plane_reset(plane, &mdp5_state->base);
0108 }
0109 
0110 static struct drm_plane_state *
0111 mdp5_plane_duplicate_state(struct drm_plane *plane)
0112 {
0113     struct mdp5_plane_state *mdp5_state;
0114 
0115     if (WARN_ON(!plane->state))
0116         return NULL;
0117 
0118     mdp5_state = kmemdup(to_mdp5_plane_state(plane->state),
0119             sizeof(*mdp5_state), GFP_KERNEL);
0120     if (!mdp5_state)
0121         return NULL;
0122 
0123     __drm_atomic_helper_plane_duplicate_state(plane, &mdp5_state->base);
0124 
0125     return &mdp5_state->base;
0126 }
0127 
0128 static void mdp5_plane_destroy_state(struct drm_plane *plane,
0129         struct drm_plane_state *state)
0130 {
0131     struct mdp5_plane_state *pstate = to_mdp5_plane_state(state);
0132 
0133     if (state->fb)
0134         drm_framebuffer_put(state->fb);
0135 
0136     kfree(pstate);
0137 }
0138 
0139 static const struct drm_plane_funcs mdp5_plane_funcs = {
0140         .update_plane = drm_atomic_helper_update_plane,
0141         .disable_plane = drm_atomic_helper_disable_plane,
0142         .destroy = mdp5_plane_destroy,
0143         .reset = mdp5_plane_reset,
0144         .atomic_duplicate_state = mdp5_plane_duplicate_state,
0145         .atomic_destroy_state = mdp5_plane_destroy_state,
0146         .atomic_print_state = mdp5_plane_atomic_print_state,
0147 };
0148 
0149 static int mdp5_plane_prepare_fb(struct drm_plane *plane,
0150                  struct drm_plane_state *new_state)
0151 {
0152     struct msm_drm_private *priv = plane->dev->dev_private;
0153     struct msm_kms *kms = priv->kms;
0154     bool needs_dirtyfb = to_mdp5_plane_state(new_state)->needs_dirtyfb;
0155 
0156     if (!new_state->fb)
0157         return 0;
0158 
0159     drm_gem_plane_helper_prepare_fb(plane, new_state);
0160 
0161     return msm_framebuffer_prepare(new_state->fb, kms->aspace, needs_dirtyfb);
0162 }
0163 
0164 static void mdp5_plane_cleanup_fb(struct drm_plane *plane,
0165                   struct drm_plane_state *old_state)
0166 {
0167     struct mdp5_kms *mdp5_kms = get_kms(plane);
0168     struct msm_kms *kms = &mdp5_kms->base.base;
0169     struct drm_framebuffer *fb = old_state->fb;
0170     bool needed_dirtyfb = to_mdp5_plane_state(old_state)->needs_dirtyfb;
0171 
0172     if (!fb)
0173         return;
0174 
0175     DBG("%s: cleanup: FB[%u]", plane->name, fb->base.id);
0176     msm_framebuffer_cleanup(fb, kms->aspace, needed_dirtyfb);
0177 }
0178 
0179 static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state,
0180                           struct drm_plane_state *state)
0181 {
0182     struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(state);
0183     struct drm_plane *plane = state->plane;
0184     struct drm_plane_state *old_state = plane->state;
0185     struct mdp5_cfg *config = mdp5_cfg_get_config(get_kms(plane)->cfg);
0186     bool new_hwpipe = false;
0187     bool need_right_hwpipe = false;
0188     uint32_t max_width, max_height;
0189     bool out_of_bounds = false;
0190     uint32_t caps = 0;
0191     int min_scale, max_scale;
0192     int ret;
0193 
0194     DBG("%s: check (%d -> %d)", plane->name,
0195             plane_enabled(old_state), plane_enabled(state));
0196 
0197     max_width = config->hw->lm.max_width << 16;
0198     max_height = config->hw->lm.max_height << 16;
0199 
0200     /* Make sure source dimensions are within bounds. */
0201     if (state->src_h > max_height)
0202         out_of_bounds = true;
0203 
0204     if (state->src_w > max_width) {
0205         /* If source split is supported, we can go up to 2x
0206          * the max LM width, but we'd need to stage another
0207          * hwpipe to the right LM. So, the drm_plane would
0208          * consist of 2 hwpipes.
0209          */
0210         if (config->hw->mdp.caps & MDP_CAP_SRC_SPLIT &&
0211             (state->src_w <= 2 * max_width))
0212             need_right_hwpipe = true;
0213         else
0214             out_of_bounds = true;
0215     }
0216 
0217     if (out_of_bounds) {
0218         struct drm_rect src = drm_plane_state_src(state);
0219         DBG("Invalid source size "DRM_RECT_FP_FMT,
0220                 DRM_RECT_FP_ARG(&src));
0221         return -ERANGE;
0222     }
0223 
0224     min_scale = FRAC_16_16(1, 8);
0225     max_scale = FRAC_16_16(8, 1);
0226 
0227     ret = drm_atomic_helper_check_plane_state(state, crtc_state,
0228                           min_scale, max_scale,
0229                           true, true);
0230     if (ret)
0231         return ret;
0232 
0233     if (plane_enabled(state)) {
0234         unsigned int rotation;
0235         const struct mdp_format *format;
0236         struct mdp5_kms *mdp5_kms = get_kms(plane);
0237         uint32_t blkcfg = 0;
0238 
0239         format = to_mdp_format(msm_framebuffer_format(state->fb));
0240         if (MDP_FORMAT_IS_YUV(format))
0241             caps |= MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC;
0242 
0243         if (((state->src_w >> 16) != state->crtc_w) ||
0244                 ((state->src_h >> 16) != state->crtc_h))
0245             caps |= MDP_PIPE_CAP_SCALE;
0246 
0247         rotation = drm_rotation_simplify(state->rotation,
0248                          DRM_MODE_ROTATE_0 |
0249                          DRM_MODE_REFLECT_X |
0250                          DRM_MODE_REFLECT_Y);
0251 
0252         if (rotation & DRM_MODE_REFLECT_X)
0253             caps |= MDP_PIPE_CAP_HFLIP;
0254 
0255         if (rotation & DRM_MODE_REFLECT_Y)
0256             caps |= MDP_PIPE_CAP_VFLIP;
0257 
0258         if (plane->type == DRM_PLANE_TYPE_CURSOR)
0259             caps |= MDP_PIPE_CAP_CURSOR;
0260 
0261         /* (re)allocate hw pipe if we don't have one or caps-mismatch: */
0262         if (!mdp5_state->hwpipe || (caps & ~mdp5_state->hwpipe->caps))
0263             new_hwpipe = true;
0264 
0265         /*
0266          * (re)allocte hw pipe if we're either requesting for 2 hw pipes
0267          * or we're switching from 2 hw pipes to 1 hw pipe because the
0268          * new src_w can be supported by 1 hw pipe itself.
0269          */
0270         if ((need_right_hwpipe && !mdp5_state->r_hwpipe) ||
0271             (!need_right_hwpipe && mdp5_state->r_hwpipe))
0272             new_hwpipe = true;
0273 
0274         if (mdp5_kms->smp) {
0275             const struct mdp_format *format =
0276                 to_mdp_format(msm_framebuffer_format(state->fb));
0277 
0278             blkcfg = mdp5_smp_calculate(mdp5_kms->smp, format,
0279                     state->src_w >> 16, false);
0280 
0281             if (mdp5_state->hwpipe && (mdp5_state->hwpipe->blkcfg != blkcfg))
0282                 new_hwpipe = true;
0283         }
0284 
0285         /* (re)assign hwpipe if needed, otherwise keep old one: */
0286         if (new_hwpipe) {
0287             /* TODO maybe we want to re-assign hwpipe sometimes
0288              * in cases when we no-longer need some caps to make
0289              * it available for other planes?
0290              */
0291             struct mdp5_hw_pipe *old_hwpipe = mdp5_state->hwpipe;
0292             struct mdp5_hw_pipe *old_right_hwpipe =
0293                               mdp5_state->r_hwpipe;
0294             struct mdp5_hw_pipe *new_hwpipe = NULL;
0295             struct mdp5_hw_pipe *new_right_hwpipe = NULL;
0296 
0297             ret = mdp5_pipe_assign(state->state, plane, caps,
0298                            blkcfg, &new_hwpipe,
0299                            need_right_hwpipe ?
0300                            &new_right_hwpipe : NULL);
0301             if (ret) {
0302                 DBG("%s: failed to assign hwpipe(s)!",
0303                     plane->name);
0304                 return ret;
0305             }
0306 
0307             mdp5_state->hwpipe = new_hwpipe;
0308             if (need_right_hwpipe)
0309                 mdp5_state->r_hwpipe = new_right_hwpipe;
0310             else
0311                 /*
0312                  * set it to NULL so that the driver knows we
0313                  * don't have a right hwpipe when committing a
0314                  * new state
0315                  */
0316                 mdp5_state->r_hwpipe = NULL;
0317 
0318 
0319             ret = mdp5_pipe_release(state->state, old_hwpipe);
0320             if (ret)
0321                 return ret;
0322 
0323             ret = mdp5_pipe_release(state->state, old_right_hwpipe);
0324             if (ret)
0325                 return ret;
0326 
0327         }
0328     } else {
0329         ret = mdp5_pipe_release(state->state, mdp5_state->hwpipe);
0330         if (ret)
0331             return ret;
0332 
0333         ret = mdp5_pipe_release(state->state, mdp5_state->r_hwpipe);
0334         if (ret)
0335             return ret;
0336 
0337         mdp5_state->hwpipe = mdp5_state->r_hwpipe = NULL;
0338     }
0339 
0340     return 0;
0341 }
0342 
0343 static int mdp5_plane_atomic_check(struct drm_plane *plane,
0344                    struct drm_atomic_state *state)
0345 {
0346     struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,
0347                                          plane);
0348     struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
0349                                          plane);
0350     struct drm_crtc *crtc;
0351     struct drm_crtc_state *crtc_state;
0352 
0353     crtc = new_plane_state->crtc ? new_plane_state->crtc : old_plane_state->crtc;
0354     if (!crtc)
0355         return 0;
0356 
0357     crtc_state = drm_atomic_get_existing_crtc_state(state,
0358                             crtc);
0359     if (WARN_ON(!crtc_state))
0360         return -EINVAL;
0361 
0362     return mdp5_plane_atomic_check_with_state(crtc_state, new_plane_state);
0363 }
0364 
0365 static void mdp5_plane_atomic_update(struct drm_plane *plane,
0366                      struct drm_atomic_state *state)
0367 {
0368     struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
0369                                        plane);
0370 
0371     DBG("%s: update", plane->name);
0372 
0373     if (plane_enabled(new_state)) {
0374         int ret;
0375 
0376         ret = mdp5_plane_mode_set(plane,
0377                 new_state->crtc, new_state->fb,
0378                 &new_state->src, &new_state->dst);
0379         /* atomic_check should have ensured that this doesn't fail */
0380         WARN_ON(ret < 0);
0381     }
0382 }
0383 
0384 static int mdp5_plane_atomic_async_check(struct drm_plane *plane,
0385                      struct drm_atomic_state *state)
0386 {
0387     struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
0388                                          plane);
0389     struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(new_plane_state);
0390     struct drm_crtc_state *crtc_state;
0391     int min_scale, max_scale;
0392     int ret;
0393 
0394     crtc_state = drm_atomic_get_existing_crtc_state(state,
0395                             new_plane_state->crtc);
0396     if (WARN_ON(!crtc_state))
0397         return -EINVAL;
0398 
0399     if (!crtc_state->active)
0400         return -EINVAL;
0401 
0402     /* don't use fast path if we don't have a hwpipe allocated yet */
0403     if (!mdp5_state->hwpipe)
0404         return -EINVAL;
0405 
0406     /* only allow changing of position(crtc x/y or src x/y) in fast path */
0407     if (plane->state->crtc != new_plane_state->crtc ||
0408         plane->state->src_w != new_plane_state->src_w ||
0409         plane->state->src_h != new_plane_state->src_h ||
0410         plane->state->crtc_w != new_plane_state->crtc_w ||
0411         plane->state->crtc_h != new_plane_state->crtc_h ||
0412         !plane->state->fb ||
0413         plane->state->fb != new_plane_state->fb)
0414         return -EINVAL;
0415 
0416     min_scale = FRAC_16_16(1, 8);
0417     max_scale = FRAC_16_16(8, 1);
0418 
0419     ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
0420                           min_scale, max_scale,
0421                           true, true);
0422     if (ret)
0423         return ret;
0424 
0425     /*
0426      * if the visibility of the plane changes (i.e, if the cursor is
0427      * clipped out completely, we can't take the async path because
0428      * we need to stage/unstage the plane from the Layer Mixer(s). We
0429      * also assign/unassign the hwpipe(s) tied to the plane. We avoid
0430      * taking the fast path for both these reasons.
0431      */
0432     if (new_plane_state->visible != plane->state->visible)
0433         return -EINVAL;
0434 
0435     return 0;
0436 }
0437 
0438 static void mdp5_plane_atomic_async_update(struct drm_plane *plane,
0439                        struct drm_atomic_state *state)
0440 {
0441     struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
0442                                        plane);
0443     struct drm_framebuffer *old_fb = plane->state->fb;
0444 
0445     plane->state->src_x = new_state->src_x;
0446     plane->state->src_y = new_state->src_y;
0447     plane->state->crtc_x = new_state->crtc_x;
0448     plane->state->crtc_y = new_state->crtc_y;
0449 
0450     if (plane_enabled(new_state)) {
0451         struct mdp5_ctl *ctl;
0452         struct mdp5_pipeline *pipeline =
0453                     mdp5_crtc_get_pipeline(new_state->crtc);
0454         int ret;
0455 
0456         ret = mdp5_plane_mode_set(plane, new_state->crtc, new_state->fb,
0457                 &new_state->src, &new_state->dst);
0458         WARN_ON(ret < 0);
0459 
0460         ctl = mdp5_crtc_get_ctl(new_state->crtc);
0461 
0462         mdp5_ctl_commit(ctl, pipeline, mdp5_plane_get_flush(plane), true);
0463     }
0464 
0465     *to_mdp5_plane_state(plane->state) =
0466         *to_mdp5_plane_state(new_state);
0467 
0468     new_state->fb = old_fb;
0469 }
0470 
0471 static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = {
0472         .prepare_fb = mdp5_plane_prepare_fb,
0473         .cleanup_fb = mdp5_plane_cleanup_fb,
0474         .atomic_check = mdp5_plane_atomic_check,
0475         .atomic_update = mdp5_plane_atomic_update,
0476         .atomic_async_check = mdp5_plane_atomic_async_check,
0477         .atomic_async_update = mdp5_plane_atomic_async_update,
0478 };
0479 
0480 static void set_scanout_locked(struct mdp5_kms *mdp5_kms,
0481                    enum mdp5_pipe pipe,
0482                    struct drm_framebuffer *fb)
0483 {
0484     struct msm_kms *kms = &mdp5_kms->base.base;
0485 
0486     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe),
0487             MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |
0488             MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));
0489 
0490     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe),
0491             MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |
0492             MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));
0493 
0494     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe),
0495             msm_framebuffer_iova(fb, kms->aspace, 0));
0496     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe),
0497             msm_framebuffer_iova(fb, kms->aspace, 1));
0498     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe),
0499             msm_framebuffer_iova(fb, kms->aspace, 2));
0500     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe),
0501             msm_framebuffer_iova(fb, kms->aspace, 3));
0502 }
0503 
0504 /* Note: mdp5_plane->pipe_lock must be locked */
0505 static void csc_disable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe)
0506 {
0507     uint32_t value = mdp5_read(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe)) &
0508              ~MDP5_PIPE_OP_MODE_CSC_1_EN;
0509 
0510     mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), value);
0511 }
0512 
0513 /* Note: mdp5_plane->pipe_lock must be locked */
0514 static void csc_enable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe,
0515         struct csc_cfg *csc)
0516 {
0517     uint32_t  i, mode = 0; /* RGB, no CSC */
0518     uint32_t *matrix;
0519 
0520     if (unlikely(!csc))
0521         return;
0522 
0523     if ((csc->type == CSC_YUV2RGB) || (CSC_YUV2YUV == csc->type))
0524         mode |= MDP5_PIPE_OP_MODE_CSC_SRC_DATA_FORMAT(DATA_FORMAT_YUV);
0525     if ((csc->type == CSC_RGB2YUV) || (CSC_YUV2YUV == csc->type))
0526         mode |= MDP5_PIPE_OP_MODE_CSC_DST_DATA_FORMAT(DATA_FORMAT_YUV);
0527     mode |= MDP5_PIPE_OP_MODE_CSC_1_EN;
0528     mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), mode);
0529 
0530     matrix = csc->matrix;
0531     mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_0(pipe),
0532             MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_11(matrix[0]) |
0533             MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_12(matrix[1]));
0534     mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_1(pipe),
0535             MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_13(matrix[2]) |
0536             MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_21(matrix[3]));
0537     mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_2(pipe),
0538             MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_22(matrix[4]) |
0539             MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_23(matrix[5]));
0540     mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_3(pipe),
0541             MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_31(matrix[6]) |
0542             MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_32(matrix[7]));
0543     mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_4(pipe),
0544             MDP5_PIPE_CSC_1_MATRIX_COEFF_4_COEFF_33(matrix[8]));
0545 
0546     for (i = 0; i < ARRAY_SIZE(csc->pre_bias); i++) {
0547         uint32_t *pre_clamp = csc->pre_clamp;
0548         uint32_t *post_clamp = csc->post_clamp;
0549 
0550         mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_CLAMP(pipe, i),
0551             MDP5_PIPE_CSC_1_PRE_CLAMP_REG_HIGH(pre_clamp[2*i+1]) |
0552             MDP5_PIPE_CSC_1_PRE_CLAMP_REG_LOW(pre_clamp[2*i]));
0553 
0554         mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_CLAMP(pipe, i),
0555             MDP5_PIPE_CSC_1_POST_CLAMP_REG_HIGH(post_clamp[2*i+1]) |
0556             MDP5_PIPE_CSC_1_POST_CLAMP_REG_LOW(post_clamp[2*i]));
0557 
0558         mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_BIAS(pipe, i),
0559             MDP5_PIPE_CSC_1_PRE_BIAS_REG_VALUE(csc->pre_bias[i]));
0560 
0561         mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_BIAS(pipe, i),
0562             MDP5_PIPE_CSC_1_POST_BIAS_REG_VALUE(csc->post_bias[i]));
0563     }
0564 }
0565 
0566 #define PHASE_STEP_SHIFT    21
0567 #define DOWN_SCALE_RATIO_MAX    32  /* 2^(26-21) */
0568 
0569 static int calc_phase_step(uint32_t src, uint32_t dst, uint32_t *out_phase)
0570 {
0571     uint32_t unit;
0572 
0573     if (src == 0 || dst == 0)
0574         return -EINVAL;
0575 
0576     /*
0577      * PHASE_STEP_X/Y is coded on 26 bits (25:0),
0578      * where 2^21 represents the unity "1" in fixed-point hardware design.
0579      * This leaves 5 bits for the integer part (downscale case):
0580      *  -> maximum downscale ratio = 0b1_1111 = 31
0581      */
0582     if (src > (dst * DOWN_SCALE_RATIO_MAX))
0583         return -EOVERFLOW;
0584 
0585     unit = 1 << PHASE_STEP_SHIFT;
0586     *out_phase = mult_frac(unit, src, dst);
0587 
0588     return 0;
0589 }
0590 
0591 static int calc_scalex_steps(struct drm_plane *plane,
0592         uint32_t pixel_format, uint32_t src, uint32_t dest,
0593         uint32_t phasex_steps[COMP_MAX])
0594 {
0595     const struct drm_format_info *info = drm_format_info(pixel_format);
0596     struct mdp5_kms *mdp5_kms = get_kms(plane);
0597     struct device *dev = mdp5_kms->dev->dev;
0598     uint32_t phasex_step;
0599     int ret;
0600 
0601     ret = calc_phase_step(src, dest, &phasex_step);
0602     if (ret) {
0603         DRM_DEV_ERROR(dev, "X scaling (%d->%d) failed: %d\n", src, dest, ret);
0604         return ret;
0605     }
0606 
0607     phasex_steps[COMP_0]   = phasex_step;
0608     phasex_steps[COMP_3]   = phasex_step;
0609     phasex_steps[COMP_1_2] = phasex_step / info->hsub;
0610 
0611     return 0;
0612 }
0613 
0614 static int calc_scaley_steps(struct drm_plane *plane,
0615         uint32_t pixel_format, uint32_t src, uint32_t dest,
0616         uint32_t phasey_steps[COMP_MAX])
0617 {
0618     const struct drm_format_info *info = drm_format_info(pixel_format);
0619     struct mdp5_kms *mdp5_kms = get_kms(plane);
0620     struct device *dev = mdp5_kms->dev->dev;
0621     uint32_t phasey_step;
0622     int ret;
0623 
0624     ret = calc_phase_step(src, dest, &phasey_step);
0625     if (ret) {
0626         DRM_DEV_ERROR(dev, "Y scaling (%d->%d) failed: %d\n", src, dest, ret);
0627         return ret;
0628     }
0629 
0630     phasey_steps[COMP_0]   = phasey_step;
0631     phasey_steps[COMP_3]   = phasey_step;
0632     phasey_steps[COMP_1_2] = phasey_step / info->vsub;
0633 
0634     return 0;
0635 }
0636 
0637 static uint32_t get_scale_config(const struct mdp_format *format,
0638         uint32_t src, uint32_t dst, bool horz)
0639 {
0640     const struct drm_format_info *info = drm_format_info(format->base.pixel_format);
0641     bool scaling = format->is_yuv ? true : (src != dst);
0642     uint32_t sub;
0643     uint32_t ya_filter, uv_filter;
0644     bool yuv = format->is_yuv;
0645 
0646     if (!scaling)
0647         return 0;
0648 
0649     if (yuv) {
0650         sub = horz ? info->hsub : info->vsub;
0651         uv_filter = ((src / sub) <= dst) ?
0652                    SCALE_FILTER_BIL : SCALE_FILTER_PCMN;
0653     }
0654     ya_filter = (src <= dst) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN;
0655 
0656     if (horz)
0657         return  MDP5_PIPE_SCALE_CONFIG_SCALEX_EN |
0658             MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0(ya_filter) |
0659             MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3(ya_filter) |
0660             COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2(uv_filter));
0661     else
0662         return  MDP5_PIPE_SCALE_CONFIG_SCALEY_EN |
0663             MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0(ya_filter) |
0664             MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3(ya_filter) |
0665             COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2(uv_filter));
0666 }
0667 
0668 static void calc_pixel_ext(const struct mdp_format *format,
0669         uint32_t src, uint32_t dst, uint32_t phase_step[2],
0670         int pix_ext_edge1[COMP_MAX], int pix_ext_edge2[COMP_MAX],
0671         bool horz)
0672 {
0673     bool scaling = format->is_yuv ? true : (src != dst);
0674     int i;
0675 
0676     /*
0677      * Note:
0678      * We assume here that:
0679      *     1. PCMN filter is used for downscale
0680      *     2. bilinear filter is used for upscale
0681      *     3. we are in a single pipe configuration
0682      */
0683 
0684     for (i = 0; i < COMP_MAX; i++) {
0685         pix_ext_edge1[i] = 0;
0686         pix_ext_edge2[i] = scaling ? 1 : 0;
0687     }
0688 }
0689 
0690 static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe,
0691     const struct mdp_format *format,
0692     uint32_t src_w, int pe_left[COMP_MAX], int pe_right[COMP_MAX],
0693     uint32_t src_h, int pe_top[COMP_MAX], int pe_bottom[COMP_MAX])
0694 {
0695     const struct drm_format_info *info = drm_format_info(format->base.pixel_format);
0696     uint32_t lr, tb, req;
0697     int i;
0698 
0699     for (i = 0; i < COMP_MAX; i++) {
0700         uint32_t roi_w = src_w;
0701         uint32_t roi_h = src_h;
0702 
0703         if (format->is_yuv && i == COMP_1_2) {
0704             roi_w /= info->hsub;
0705             roi_h /= info->vsub;
0706         }
0707 
0708         lr  = (pe_left[i] >= 0) ?
0709             MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT(pe_left[i]) :
0710             MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF(pe_left[i]);
0711 
0712         lr |= (pe_right[i] >= 0) ?
0713             MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT(pe_right[i]) :
0714             MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF(pe_right[i]);
0715 
0716         tb  = (pe_top[i] >= 0) ?
0717             MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT(pe_top[i]) :
0718             MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF(pe_top[i]);
0719 
0720         tb |= (pe_bottom[i] >= 0) ?
0721             MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT(pe_bottom[i]) :
0722             MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF(pe_bottom[i]);
0723 
0724         req  = MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT(roi_w +
0725                 pe_left[i] + pe_right[i]);
0726 
0727         req |= MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM(roi_h +
0728                 pe_top[i] + pe_bottom[i]);
0729 
0730         mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_LR(pipe, i), lr);
0731         mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_TB(pipe, i), tb);
0732         mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS(pipe, i), req);
0733 
0734         DBG("comp-%d (L/R): rpt=%d/%d, ovf=%d/%d, req=%d", i,
0735             FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT),
0736             FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT),
0737             FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF),
0738             FIELD(lr,  MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF),
0739             FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT));
0740 
0741         DBG("comp-%d (T/B): rpt=%d/%d, ovf=%d/%d, req=%d", i,
0742             FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT),
0743             FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT),
0744             FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF),
0745             FIELD(tb,  MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF),
0746             FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM));
0747     }
0748 }
0749 
0750 struct pixel_ext {
0751     int left[COMP_MAX];
0752     int right[COMP_MAX];
0753     int top[COMP_MAX];
0754     int bottom[COMP_MAX];
0755 };
0756 
0757 struct phase_step {
0758     u32 x[COMP_MAX];
0759     u32 y[COMP_MAX];
0760 };
0761 
0762 static void mdp5_hwpipe_mode_set(struct mdp5_kms *mdp5_kms,
0763                  struct mdp5_hw_pipe *hwpipe,
0764                  struct drm_framebuffer *fb,
0765                  struct phase_step *step,
0766                  struct pixel_ext *pe,
0767                  u32 scale_config, u32 hdecm, u32 vdecm,
0768                  bool hflip, bool vflip,
0769                  int crtc_x, int crtc_y,
0770                  unsigned int crtc_w, unsigned int crtc_h,
0771                  u32 src_img_w, u32 src_img_h,
0772                  u32 src_x, u32 src_y,
0773                  u32 src_w, u32 src_h)
0774 {
0775     enum mdp5_pipe pipe = hwpipe->pipe;
0776     bool has_pe = hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT;
0777     const struct mdp_format *format =
0778             to_mdp_format(msm_framebuffer_format(fb));
0779 
0780     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe),
0781             MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_img_w) |
0782             MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_img_h));
0783 
0784     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe),
0785             MDP5_PIPE_SRC_SIZE_WIDTH(src_w) |
0786             MDP5_PIPE_SRC_SIZE_HEIGHT(src_h));
0787 
0788     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe),
0789             MDP5_PIPE_SRC_XY_X(src_x) |
0790             MDP5_PIPE_SRC_XY_Y(src_y));
0791 
0792     mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe),
0793             MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) |
0794             MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h));
0795 
0796     mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe),
0797             MDP5_PIPE_OUT_XY_X(crtc_x) |
0798             MDP5_PIPE_OUT_XY_Y(crtc_y));
0799 
0800     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe),
0801             MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) |
0802             MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r) |
0803             MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g) |
0804             MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b) |
0805             COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) |
0806             MDP5_PIPE_SRC_FORMAT_CPP(format->cpp - 1) |
0807             MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |
0808             COND(format->unpack_tight, MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) |
0809             MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(format->fetch_type) |
0810             MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample));
0811 
0812     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe),
0813             MDP5_PIPE_SRC_UNPACK_ELEM0(format->unpack[0]) |
0814             MDP5_PIPE_SRC_UNPACK_ELEM1(format->unpack[1]) |
0815             MDP5_PIPE_SRC_UNPACK_ELEM2(format->unpack[2]) |
0816             MDP5_PIPE_SRC_UNPACK_ELEM3(format->unpack[3]));
0817 
0818     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe),
0819             (hflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_LR : 0) |
0820             (vflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_UD : 0) |
0821             COND(has_pe, MDP5_PIPE_SRC_OP_MODE_SW_PIX_EXT_OVERRIDE) |
0822             MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS));
0823 
0824     /* not using secure mode: */
0825     mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0);
0826 
0827     if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT)
0828         mdp5_write_pixel_ext(mdp5_kms, pipe, format,
0829                 src_w, pe->left, pe->right,
0830                 src_h, pe->top, pe->bottom);
0831 
0832     if (hwpipe->caps & MDP_PIPE_CAP_SCALE) {
0833         mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe),
0834                 step->x[COMP_0]);
0835         mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe),
0836                 step->y[COMP_0]);
0837         mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe),
0838                 step->x[COMP_1_2]);
0839         mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe),
0840                 step->y[COMP_1_2]);
0841         mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe),
0842                 MDP5_PIPE_DECIMATION_VERT(vdecm) |
0843                 MDP5_PIPE_DECIMATION_HORZ(hdecm));
0844         mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe),
0845                scale_config);
0846     }
0847 
0848     if (hwpipe->caps & MDP_PIPE_CAP_CSC) {
0849         if (MDP_FORMAT_IS_YUV(format))
0850             csc_enable(mdp5_kms, pipe,
0851                     mdp_get_default_csc_cfg(CSC_YUV2RGB));
0852         else
0853             csc_disable(mdp5_kms, pipe);
0854     }
0855 
0856     set_scanout_locked(mdp5_kms, pipe, fb);
0857 }
0858 
0859 static int mdp5_plane_mode_set(struct drm_plane *plane,
0860         struct drm_crtc *crtc, struct drm_framebuffer *fb,
0861         struct drm_rect *src, struct drm_rect *dest)
0862 {
0863     struct drm_plane_state *pstate = plane->state;
0864     struct mdp5_hw_pipe *hwpipe = to_mdp5_plane_state(pstate)->hwpipe;
0865     struct mdp5_kms *mdp5_kms = get_kms(plane);
0866     enum mdp5_pipe pipe = hwpipe->pipe;
0867     struct mdp5_hw_pipe *right_hwpipe;
0868     const struct mdp_format *format;
0869     uint32_t nplanes, config = 0;
0870     struct phase_step step = { { 0 } };
0871     struct pixel_ext pe = { { 0 } };
0872     uint32_t hdecm = 0, vdecm = 0;
0873     uint32_t pix_format;
0874     unsigned int rotation;
0875     bool vflip, hflip;
0876     int crtc_x, crtc_y;
0877     unsigned int crtc_w, crtc_h;
0878     uint32_t src_x, src_y;
0879     uint32_t src_w, src_h;
0880     uint32_t src_img_w, src_img_h;
0881     int ret;
0882 
0883     nplanes = fb->format->num_planes;
0884 
0885     /* bad formats should already be rejected: */
0886     if (WARN_ON(nplanes > pipe2nclients(pipe)))
0887         return -EINVAL;
0888 
0889     format = to_mdp_format(msm_framebuffer_format(fb));
0890     pix_format = format->base.pixel_format;
0891 
0892     src_x = src->x1;
0893     src_y = src->y1;
0894     src_w = drm_rect_width(src);
0895     src_h = drm_rect_height(src);
0896 
0897     crtc_x = dest->x1;
0898     crtc_y = dest->y1;
0899     crtc_w = drm_rect_width(dest);
0900     crtc_h = drm_rect_height(dest);
0901 
0902     /* src values are in Q16 fixed point, convert to integer: */
0903     src_x = src_x >> 16;
0904     src_y = src_y >> 16;
0905     src_w = src_w >> 16;
0906     src_h = src_h >> 16;
0907 
0908     src_img_w = min(fb->width, src_w);
0909     src_img_h = min(fb->height, src_h);
0910 
0911     DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", plane->name,
0912             fb->base.id, src_x, src_y, src_w, src_h,
0913             crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);
0914 
0915     right_hwpipe = to_mdp5_plane_state(pstate)->r_hwpipe;
0916     if (right_hwpipe) {
0917         /*
0918          * if the plane comprises of 2 hw pipes, assume that the width
0919          * is split equally across them. The only parameters that varies
0920          * between the 2 pipes are src_x and crtc_x
0921          */
0922         crtc_w /= 2;
0923         src_w /= 2;
0924         src_img_w /= 2;
0925     }
0926 
0927     ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, step.x);
0928     if (ret)
0929         return ret;
0930 
0931     ret = calc_scaley_steps(plane, pix_format, src_h, crtc_h, step.y);
0932     if (ret)
0933         return ret;
0934 
0935     if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) {
0936         calc_pixel_ext(format, src_w, crtc_w, step.x,
0937                    pe.left, pe.right, true);
0938         calc_pixel_ext(format, src_h, crtc_h, step.y,
0939                    pe.top, pe.bottom, false);
0940     }
0941 
0942     /* TODO calc hdecm, vdecm */
0943 
0944     /* SCALE is used to both scale and up-sample chroma components */
0945     config |= get_scale_config(format, src_w, crtc_w, true);
0946     config |= get_scale_config(format, src_h, crtc_h, false);
0947     DBG("scale config = %x", config);
0948 
0949     rotation = drm_rotation_simplify(pstate->rotation,
0950                      DRM_MODE_ROTATE_0 |
0951                      DRM_MODE_REFLECT_X |
0952                      DRM_MODE_REFLECT_Y);
0953     hflip = !!(rotation & DRM_MODE_REFLECT_X);
0954     vflip = !!(rotation & DRM_MODE_REFLECT_Y);
0955 
0956     mdp5_hwpipe_mode_set(mdp5_kms, hwpipe, fb, &step, &pe,
0957                  config, hdecm, vdecm, hflip, vflip,
0958                  crtc_x, crtc_y, crtc_w, crtc_h,
0959                  src_img_w, src_img_h,
0960                  src_x, src_y, src_w, src_h);
0961     if (right_hwpipe)
0962         mdp5_hwpipe_mode_set(mdp5_kms, right_hwpipe, fb, &step, &pe,
0963                      config, hdecm, vdecm, hflip, vflip,
0964                      crtc_x + crtc_w, crtc_y, crtc_w, crtc_h,
0965                      src_img_w, src_img_h,
0966                      src_x + src_w, src_y, src_w, src_h);
0967 
0968     return ret;
0969 }
0970 
0971 /*
0972  * Use this func and the one below only after the atomic state has been
0973  * successfully swapped
0974  */
0975 enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane)
0976 {
0977     struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state);
0978 
0979     if (WARN_ON(!pstate->hwpipe))
0980         return SSPP_NONE;
0981 
0982     return pstate->hwpipe->pipe;
0983 }
0984 
0985 enum mdp5_pipe mdp5_plane_right_pipe(struct drm_plane *plane)
0986 {
0987     struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state);
0988 
0989     if (!pstate->r_hwpipe)
0990         return SSPP_NONE;
0991 
0992     return pstate->r_hwpipe->pipe;
0993 }
0994 
0995 uint32_t mdp5_plane_get_flush(struct drm_plane *plane)
0996 {
0997     struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state);
0998     u32 mask;
0999 
1000     if (WARN_ON(!pstate->hwpipe))
1001         return 0;
1002 
1003     mask = pstate->hwpipe->flush_mask;
1004 
1005     if (pstate->r_hwpipe)
1006         mask |= pstate->r_hwpipe->flush_mask;
1007 
1008     return mask;
1009 }
1010 
1011 /* initialize plane */
1012 struct drm_plane *mdp5_plane_init(struct drm_device *dev,
1013                   enum drm_plane_type type)
1014 {
1015     struct drm_plane *plane = NULL;
1016     struct mdp5_plane *mdp5_plane;
1017     int ret;
1018 
1019     mdp5_plane = kzalloc(sizeof(*mdp5_plane), GFP_KERNEL);
1020     if (!mdp5_plane) {
1021         ret = -ENOMEM;
1022         goto fail;
1023     }
1024 
1025     plane = &mdp5_plane->base;
1026 
1027     mdp5_plane->nformats = mdp_get_formats(mdp5_plane->formats,
1028         ARRAY_SIZE(mdp5_plane->formats), false);
1029 
1030     ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs,
1031             mdp5_plane->formats, mdp5_plane->nformats,
1032             NULL, type, NULL);
1033     if (ret)
1034         goto fail;
1035 
1036     drm_plane_helper_add(plane, &mdp5_plane_helper_funcs);
1037 
1038     mdp5_plane_install_properties(plane, &plane->base);
1039 
1040     drm_plane_enable_fb_damage_clips(plane);
1041 
1042     return plane;
1043 
1044 fail:
1045     if (plane)
1046         mdp5_plane_destroy(plane);
1047 
1048     return ERR_PTR(ret);
1049 }