1048 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014-2015 The Linux Foundation. All rights reserved.4 * Copyright (C) 2013 Red Hat5 * Author: Rob Clark <robdclark@gmail.com>6 */7 8#include <drm/drm_atomic.h>9#include <drm/drm_blend.h>10#include <drm/drm_damage_helper.h>11#include <drm/drm_fourcc.h>12#include <drm/drm_framebuffer.h>13#include <drm/drm_gem_atomic_helper.h>14#include <drm/drm_print.h>15 16#include "mdp5_kms.h"17 18struct mdp5_plane {19 struct drm_plane base;20};21#define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base)22 23static int mdp5_plane_mode_set(struct drm_plane *plane,24 struct drm_crtc *crtc, struct drm_framebuffer *fb,25 struct drm_rect *src, struct drm_rect *dest);26 27static struct mdp5_kms *get_kms(struct drm_plane *plane)28{29 struct msm_drm_private *priv = plane->dev->dev_private;30 return to_mdp5_kms(to_mdp_kms(priv->kms));31}32 33static bool plane_enabled(struct drm_plane_state *state)34{35 return state->visible;36}37 38/* helper to install properties which are common to planes and crtcs */39static void mdp5_plane_install_properties(struct drm_plane *plane,40 struct drm_mode_object *obj)41{42 unsigned int zpos;43 44 drm_plane_create_rotation_property(plane,45 DRM_MODE_ROTATE_0,46 DRM_MODE_ROTATE_0 |47 DRM_MODE_ROTATE_180 |48 DRM_MODE_REFLECT_X |49 DRM_MODE_REFLECT_Y);50 drm_plane_create_alpha_property(plane);51 drm_plane_create_blend_mode_property(plane,52 BIT(DRM_MODE_BLEND_PIXEL_NONE) |53 BIT(DRM_MODE_BLEND_PREMULTI) |54 BIT(DRM_MODE_BLEND_COVERAGE));55 56 if (plane->type == DRM_PLANE_TYPE_PRIMARY)57 zpos = STAGE_BASE;58 else59 zpos = STAGE0 + drm_plane_index(plane);60 drm_plane_create_zpos_property(plane, zpos, 1, 255);61}62 63static void64mdp5_plane_atomic_print_state(struct drm_printer *p,65 const struct drm_plane_state *state)66{67 struct mdp5_plane_state *pstate = to_mdp5_plane_state(state);68 struct mdp5_kms *mdp5_kms = get_kms(state->plane);69 70 drm_printf(p, "\thwpipe=%s\n", pstate->hwpipe ?71 pstate->hwpipe->name : "(null)");72 if (mdp5_kms->caps & MDP_CAP_SRC_SPLIT)73 drm_printf(p, "\tright-hwpipe=%s\n",74 pstate->r_hwpipe ? pstate->r_hwpipe->name :75 "(null)");76 drm_printf(p, "\tblend_mode=%u\n", pstate->base.pixel_blend_mode);77 drm_printf(p, "\tzpos=%u\n", pstate->base.zpos);78 drm_printf(p, "\tnormalized_zpos=%u\n", pstate->base.normalized_zpos);79 drm_printf(p, "\talpha=%u\n", pstate->base.alpha);80 drm_printf(p, "\tstage=%s\n", stage2name(pstate->stage));81}82 83static void mdp5_plane_reset(struct drm_plane *plane)84{85 struct mdp5_plane_state *mdp5_state;86 87 if (plane->state)88 __drm_atomic_helper_plane_destroy_state(plane->state);89 90 kfree(to_mdp5_plane_state(plane->state));91 plane->state = NULL;92 mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL);93 if (!mdp5_state)94 return;95 __drm_atomic_helper_plane_reset(plane, &mdp5_state->base);96}97 98static struct drm_plane_state *99mdp5_plane_duplicate_state(struct drm_plane *plane)100{101 struct mdp5_plane_state *mdp5_state;102 103 if (WARN_ON(!plane->state))104 return NULL;105 106 mdp5_state = kmemdup(to_mdp5_plane_state(plane->state),107 sizeof(*mdp5_state), GFP_KERNEL);108 if (!mdp5_state)109 return NULL;110 111 __drm_atomic_helper_plane_duplicate_state(plane, &mdp5_state->base);112 113 return &mdp5_state->base;114}115 116static void mdp5_plane_destroy_state(struct drm_plane *plane,117 struct drm_plane_state *state)118{119 struct mdp5_plane_state *pstate = to_mdp5_plane_state(state);120 121 __drm_atomic_helper_plane_destroy_state(state);122 123 kfree(pstate);124}125 126static const struct drm_plane_funcs mdp5_plane_funcs = {127 .update_plane = drm_atomic_helper_update_plane,128 .disable_plane = drm_atomic_helper_disable_plane,129 .reset = mdp5_plane_reset,130 .atomic_duplicate_state = mdp5_plane_duplicate_state,131 .atomic_destroy_state = mdp5_plane_destroy_state,132 .atomic_print_state = mdp5_plane_atomic_print_state,133};134 135static int mdp5_plane_prepare_fb(struct drm_plane *plane,136 struct drm_plane_state *new_state)137{138 struct msm_drm_private *priv = plane->dev->dev_private;139 struct msm_kms *kms = priv->kms;140 bool needs_dirtyfb = to_mdp5_plane_state(new_state)->needs_dirtyfb;141 142 if (!new_state->fb)143 return 0;144 145 drm_gem_plane_helper_prepare_fb(plane, new_state);146 147 return msm_framebuffer_prepare(new_state->fb, kms->aspace, needs_dirtyfb);148}149 150static void mdp5_plane_cleanup_fb(struct drm_plane *plane,151 struct drm_plane_state *old_state)152{153 struct mdp5_kms *mdp5_kms = get_kms(plane);154 struct msm_kms *kms = &mdp5_kms->base.base;155 struct drm_framebuffer *fb = old_state->fb;156 bool needed_dirtyfb = to_mdp5_plane_state(old_state)->needs_dirtyfb;157 158 if (!fb)159 return;160 161 DBG("%s: cleanup: FB[%u]", plane->name, fb->base.id);162 msm_framebuffer_cleanup(fb, kms->aspace, needed_dirtyfb);163}164 165static int mdp5_plane_atomic_check_with_state(struct drm_crtc_state *crtc_state,166 struct drm_plane_state *state)167{168 struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(state);169 struct drm_plane *plane = state->plane;170 struct drm_plane_state *old_state = plane->state;171 struct mdp5_cfg *config = mdp5_cfg_get_config(get_kms(plane)->cfg);172 bool new_hwpipe = false;173 bool need_right_hwpipe = false;174 uint32_t max_width, max_height;175 bool out_of_bounds = false;176 uint32_t caps = 0;177 int min_scale, max_scale;178 int ret;179 180 DBG("%s: check (%d -> %d)", plane->name,181 plane_enabled(old_state), plane_enabled(state));182 183 max_width = config->hw->lm.max_width << 16;184 max_height = config->hw->lm.max_height << 16;185 186 /* Make sure source dimensions are within bounds. */187 if (state->src_h > max_height)188 out_of_bounds = true;189 190 if (state->src_w > max_width) {191 /* If source split is supported, we can go up to 2x192 * the max LM width, but we'd need to stage another193 * hwpipe to the right LM. So, the drm_plane would194 * consist of 2 hwpipes.195 */196 if (config->hw->mdp.caps & MDP_CAP_SRC_SPLIT &&197 (state->src_w <= 2 * max_width))198 need_right_hwpipe = true;199 else200 out_of_bounds = true;201 }202 203 if (out_of_bounds) {204 struct drm_rect src = drm_plane_state_src(state);205 DBG("Invalid source size "DRM_RECT_FP_FMT,206 DRM_RECT_FP_ARG(&src));207 return -ERANGE;208 }209 210 min_scale = FRAC_16_16(1, 8);211 max_scale = FRAC_16_16(8, 1);212 213 ret = drm_atomic_helper_check_plane_state(state, crtc_state,214 min_scale, max_scale,215 true, true);216 if (ret)217 return ret;218 219 if (plane_enabled(state)) {220 unsigned int rotation;221 const struct msm_format *format;222 struct mdp5_kms *mdp5_kms = get_kms(plane);223 uint32_t blkcfg = 0;224 225 format = msm_framebuffer_format(state->fb);226 if (MSM_FORMAT_IS_YUV(format))227 caps |= MDP_PIPE_CAP_SCALE | MDP_PIPE_CAP_CSC;228 229 if (((state->src_w >> 16) != state->crtc_w) ||230 ((state->src_h >> 16) != state->crtc_h))231 caps |= MDP_PIPE_CAP_SCALE;232 233 rotation = drm_rotation_simplify(state->rotation,234 DRM_MODE_ROTATE_0 |235 DRM_MODE_REFLECT_X |236 DRM_MODE_REFLECT_Y);237 238 if (rotation & DRM_MODE_REFLECT_X)239 caps |= MDP_PIPE_CAP_HFLIP;240 241 if (rotation & DRM_MODE_REFLECT_Y)242 caps |= MDP_PIPE_CAP_VFLIP;243 244 if (plane->type == DRM_PLANE_TYPE_CURSOR)245 caps |= MDP_PIPE_CAP_CURSOR;246 247 /* (re)allocate hw pipe if we don't have one or caps-mismatch: */248 if (!mdp5_state->hwpipe || (caps & ~mdp5_state->hwpipe->caps))249 new_hwpipe = true;250 251 /*252 * (re)allocte hw pipe if we're either requesting for 2 hw pipes253 * or we're switching from 2 hw pipes to 1 hw pipe because the254 * new src_w can be supported by 1 hw pipe itself.255 */256 if ((need_right_hwpipe && !mdp5_state->r_hwpipe) ||257 (!need_right_hwpipe && mdp5_state->r_hwpipe))258 new_hwpipe = true;259 260 if (mdp5_kms->smp) {261 const struct msm_format *format =262 msm_framebuffer_format(state->fb);263 264 blkcfg = mdp5_smp_calculate(mdp5_kms->smp, format,265 state->src_w >> 16, false);266 267 if (mdp5_state->hwpipe && (mdp5_state->hwpipe->blkcfg != blkcfg))268 new_hwpipe = true;269 }270 271 /* (re)assign hwpipe if needed, otherwise keep old one: */272 if (new_hwpipe) {273 /* TODO maybe we want to re-assign hwpipe sometimes274 * in cases when we no-longer need some caps to make275 * it available for other planes?276 */277 struct mdp5_hw_pipe *old_hwpipe = mdp5_state->hwpipe;278 struct mdp5_hw_pipe *old_right_hwpipe =279 mdp5_state->r_hwpipe;280 struct mdp5_hw_pipe *new_hwpipe = NULL;281 struct mdp5_hw_pipe *new_right_hwpipe = NULL;282 283 ret = mdp5_pipe_assign(state->state, plane, caps,284 blkcfg, &new_hwpipe,285 need_right_hwpipe ?286 &new_right_hwpipe : NULL);287 if (ret) {288 DBG("%s: failed to assign hwpipe(s)!",289 plane->name);290 return ret;291 }292 293 mdp5_state->hwpipe = new_hwpipe;294 if (need_right_hwpipe)295 mdp5_state->r_hwpipe = new_right_hwpipe;296 else297 /*298 * set it to NULL so that the driver knows we299 * don't have a right hwpipe when committing a300 * new state301 */302 mdp5_state->r_hwpipe = NULL;303 304 305 ret = mdp5_pipe_release(state->state, old_hwpipe);306 if (ret)307 return ret;308 309 ret = mdp5_pipe_release(state->state, old_right_hwpipe);310 if (ret)311 return ret;312 313 }314 } else {315 ret = mdp5_pipe_release(state->state, mdp5_state->hwpipe);316 if (ret)317 return ret;318 319 ret = mdp5_pipe_release(state->state, mdp5_state->r_hwpipe);320 if (ret)321 return ret;322 323 mdp5_state->hwpipe = mdp5_state->r_hwpipe = NULL;324 }325 326 return 0;327}328 329static int mdp5_plane_atomic_check(struct drm_plane *plane,330 struct drm_atomic_state *state)331{332 struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,333 plane);334 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,335 plane);336 struct drm_crtc *crtc;337 struct drm_crtc_state *crtc_state;338 339 crtc = new_plane_state->crtc ? new_plane_state->crtc : old_plane_state->crtc;340 if (!crtc)341 return 0;342 343 crtc_state = drm_atomic_get_existing_crtc_state(state,344 crtc);345 if (WARN_ON(!crtc_state))346 return -EINVAL;347 348 return mdp5_plane_atomic_check_with_state(crtc_state, new_plane_state);349}350 351static void mdp5_plane_atomic_update(struct drm_plane *plane,352 struct drm_atomic_state *state)353{354 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,355 plane);356 357 DBG("%s: update", plane->name);358 359 if (plane_enabled(new_state)) {360 int ret;361 362 ret = mdp5_plane_mode_set(plane,363 new_state->crtc, new_state->fb,364 &new_state->src, &new_state->dst);365 /* atomic_check should have ensured that this doesn't fail */366 WARN_ON(ret < 0);367 }368}369 370static int mdp5_plane_atomic_async_check(struct drm_plane *plane,371 struct drm_atomic_state *state)372{373 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,374 plane);375 struct mdp5_plane_state *mdp5_state = to_mdp5_plane_state(new_plane_state);376 struct drm_crtc_state *crtc_state;377 int min_scale, max_scale;378 int ret;379 380 crtc_state = drm_atomic_get_existing_crtc_state(state,381 new_plane_state->crtc);382 if (WARN_ON(!crtc_state))383 return -EINVAL;384 385 if (!crtc_state->active)386 return -EINVAL;387 388 /* don't use fast path if we don't have a hwpipe allocated yet */389 if (!mdp5_state->hwpipe)390 return -EINVAL;391 392 /* only allow changing of position(crtc x/y or src x/y) in fast path */393 if (plane->state->crtc != new_plane_state->crtc ||394 plane->state->src_w != new_plane_state->src_w ||395 plane->state->src_h != new_plane_state->src_h ||396 plane->state->crtc_w != new_plane_state->crtc_w ||397 plane->state->crtc_h != new_plane_state->crtc_h ||398 !plane->state->fb ||399 plane->state->fb != new_plane_state->fb)400 return -EINVAL;401 402 min_scale = FRAC_16_16(1, 8);403 max_scale = FRAC_16_16(8, 1);404 405 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,406 min_scale, max_scale,407 true, true);408 if (ret)409 return ret;410 411 /*412 * if the visibility of the plane changes (i.e, if the cursor is413 * clipped out completely, we can't take the async path because414 * we need to stage/unstage the plane from the Layer Mixer(s). We415 * also assign/unassign the hwpipe(s) tied to the plane. We avoid416 * taking the fast path for both these reasons.417 */418 if (new_plane_state->visible != plane->state->visible)419 return -EINVAL;420 421 return 0;422}423 424static void mdp5_plane_atomic_async_update(struct drm_plane *plane,425 struct drm_atomic_state *state)426{427 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,428 plane);429 struct drm_framebuffer *old_fb = plane->state->fb;430 431 plane->state->src_x = new_state->src_x;432 plane->state->src_y = new_state->src_y;433 plane->state->crtc_x = new_state->crtc_x;434 plane->state->crtc_y = new_state->crtc_y;435 436 if (plane_enabled(new_state)) {437 struct mdp5_ctl *ctl;438 struct mdp5_pipeline *pipeline =439 mdp5_crtc_get_pipeline(new_state->crtc);440 int ret;441 442 ret = mdp5_plane_mode_set(plane, new_state->crtc, new_state->fb,443 &new_state->src, &new_state->dst);444 WARN_ON(ret < 0);445 446 ctl = mdp5_crtc_get_ctl(new_state->crtc);447 448 mdp5_ctl_commit(ctl, pipeline, mdp5_plane_get_flush(plane), true);449 }450 451 *to_mdp5_plane_state(plane->state) =452 *to_mdp5_plane_state(new_state);453 454 new_state->fb = old_fb;455}456 457static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = {458 .prepare_fb = mdp5_plane_prepare_fb,459 .cleanup_fb = mdp5_plane_cleanup_fb,460 .atomic_check = mdp5_plane_atomic_check,461 .atomic_update = mdp5_plane_atomic_update,462 .atomic_async_check = mdp5_plane_atomic_async_check,463 .atomic_async_update = mdp5_plane_atomic_async_update,464};465 466static void set_scanout_locked(struct mdp5_kms *mdp5_kms,467 enum mdp5_pipe pipe,468 struct drm_framebuffer *fb)469{470 struct msm_kms *kms = &mdp5_kms->base.base;471 472 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe),473 MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |474 MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));475 476 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe),477 MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |478 MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));479 480 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe),481 msm_framebuffer_iova(fb, kms->aspace, 0));482 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe),483 msm_framebuffer_iova(fb, kms->aspace, 1));484 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe),485 msm_framebuffer_iova(fb, kms->aspace, 2));486 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe),487 msm_framebuffer_iova(fb, kms->aspace, 3));488}489 490/* Note: mdp5_plane->pipe_lock must be locked */491static void csc_disable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe)492{493 uint32_t value = mdp5_read(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe)) &494 ~MDP5_PIPE_OP_MODE_CSC_1_EN;495 496 mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), value);497}498 499/* Note: mdp5_plane->pipe_lock must be locked */500static void csc_enable(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe,501 struct csc_cfg *csc)502{503 uint32_t i, mode = 0; /* RGB, no CSC */504 uint32_t *matrix;505 506 if (unlikely(!csc))507 return;508 509 if ((csc->type == CSC_YUV2RGB) || (CSC_YUV2YUV == csc->type))510 mode |= MDP5_PIPE_OP_MODE_CSC_SRC_DATA_FORMAT(DATA_FORMAT_YUV);511 if ((csc->type == CSC_RGB2YUV) || (CSC_YUV2YUV == csc->type))512 mode |= MDP5_PIPE_OP_MODE_CSC_DST_DATA_FORMAT(DATA_FORMAT_YUV);513 mode |= MDP5_PIPE_OP_MODE_CSC_1_EN;514 mdp5_write(mdp5_kms, REG_MDP5_PIPE_OP_MODE(pipe), mode);515 516 matrix = csc->matrix;517 mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_0(pipe),518 MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_11(matrix[0]) |519 MDP5_PIPE_CSC_1_MATRIX_COEFF_0_COEFF_12(matrix[1]));520 mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_1(pipe),521 MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_13(matrix[2]) |522 MDP5_PIPE_CSC_1_MATRIX_COEFF_1_COEFF_21(matrix[3]));523 mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_2(pipe),524 MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_22(matrix[4]) |525 MDP5_PIPE_CSC_1_MATRIX_COEFF_2_COEFF_23(matrix[5]));526 mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_3(pipe),527 MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_31(matrix[6]) |528 MDP5_PIPE_CSC_1_MATRIX_COEFF_3_COEFF_32(matrix[7]));529 mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_MATRIX_COEFF_4(pipe),530 MDP5_PIPE_CSC_1_MATRIX_COEFF_4_COEFF_33(matrix[8]));531 532 for (i = 0; i < ARRAY_SIZE(csc->pre_bias); i++) {533 uint32_t *pre_clamp = csc->pre_clamp;534 uint32_t *post_clamp = csc->post_clamp;535 536 mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_CLAMP(pipe, i),537 MDP5_PIPE_CSC_1_PRE_CLAMP_REG_HIGH(pre_clamp[2*i+1]) |538 MDP5_PIPE_CSC_1_PRE_CLAMP_REG_LOW(pre_clamp[2*i]));539 540 mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_CLAMP(pipe, i),541 MDP5_PIPE_CSC_1_POST_CLAMP_REG_HIGH(post_clamp[2*i+1]) |542 MDP5_PIPE_CSC_1_POST_CLAMP_REG_LOW(post_clamp[2*i]));543 544 mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_PRE_BIAS(pipe, i),545 MDP5_PIPE_CSC_1_PRE_BIAS_REG_VALUE(csc->pre_bias[i]));546 547 mdp5_write(mdp5_kms, REG_MDP5_PIPE_CSC_1_POST_BIAS(pipe, i),548 MDP5_PIPE_CSC_1_POST_BIAS_REG_VALUE(csc->post_bias[i]));549 }550}551 552#define PHASE_STEP_SHIFT 21553#define DOWN_SCALE_RATIO_MAX 32 /* 2^(26-21) */554 555static int calc_phase_step(uint32_t src, uint32_t dst, uint32_t *out_phase)556{557 uint32_t unit;558 559 if (src == 0 || dst == 0)560 return -EINVAL;561 562 /*563 * PHASE_STEP_X/Y is coded on 26 bits (25:0),564 * where 2^21 represents the unity "1" in fixed-point hardware design.565 * This leaves 5 bits for the integer part (downscale case):566 * -> maximum downscale ratio = 0b1_1111 = 31567 */568 if (src > (dst * DOWN_SCALE_RATIO_MAX))569 return -EOVERFLOW;570 571 unit = 1 << PHASE_STEP_SHIFT;572 *out_phase = mult_frac(unit, src, dst);573 574 return 0;575}576 577static int calc_scalex_steps(struct drm_plane *plane,578 uint32_t pixel_format, uint32_t src, uint32_t dest,579 uint32_t phasex_steps[COMP_MAX])580{581 const struct drm_format_info *info = drm_format_info(pixel_format);582 struct mdp5_kms *mdp5_kms = get_kms(plane);583 struct device *dev = mdp5_kms->dev->dev;584 uint32_t phasex_step;585 int ret;586 587 ret = calc_phase_step(src, dest, &phasex_step);588 if (ret) {589 DRM_DEV_ERROR(dev, "X scaling (%d->%d) failed: %d\n", src, dest, ret);590 return ret;591 }592 593 phasex_steps[COMP_0] = phasex_step;594 phasex_steps[COMP_3] = phasex_step;595 phasex_steps[COMP_1_2] = phasex_step / info->hsub;596 597 return 0;598}599 600static int calc_scaley_steps(struct drm_plane *plane,601 uint32_t pixel_format, uint32_t src, uint32_t dest,602 uint32_t phasey_steps[COMP_MAX])603{604 const struct drm_format_info *info = drm_format_info(pixel_format);605 struct mdp5_kms *mdp5_kms = get_kms(plane);606 struct device *dev = mdp5_kms->dev->dev;607 uint32_t phasey_step;608 int ret;609 610 ret = calc_phase_step(src, dest, &phasey_step);611 if (ret) {612 DRM_DEV_ERROR(dev, "Y scaling (%d->%d) failed: %d\n", src, dest, ret);613 return ret;614 }615 616 phasey_steps[COMP_0] = phasey_step;617 phasey_steps[COMP_3] = phasey_step;618 phasey_steps[COMP_1_2] = phasey_step / info->vsub;619 620 return 0;621}622 623static uint32_t get_scale_config(const struct msm_format *format,624 uint32_t src, uint32_t dst, bool horz)625{626 const struct drm_format_info *info = drm_format_info(format->pixel_format);627 bool yuv = MSM_FORMAT_IS_YUV(format);628 bool scaling = yuv ? true : (src != dst);629 uint32_t sub;630 uint32_t ya_filter, uv_filter;631 632 if (!scaling)633 return 0;634 635 if (yuv) {636 sub = horz ? info->hsub : info->vsub;637 uv_filter = ((src / sub) <= dst) ?638 SCALE_FILTER_BIL : SCALE_FILTER_PCMN;639 }640 ya_filter = (src <= dst) ? SCALE_FILTER_BIL : SCALE_FILTER_PCMN;641 642 if (horz)643 return MDP5_PIPE_SCALE_CONFIG_SCALEX_EN |644 MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_0(ya_filter) |645 MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_3(ya_filter) |646 COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEX_FILTER_COMP_1_2(uv_filter));647 else648 return MDP5_PIPE_SCALE_CONFIG_SCALEY_EN |649 MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_0(ya_filter) |650 MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_3(ya_filter) |651 COND(yuv, MDP5_PIPE_SCALE_CONFIG_SCALEY_FILTER_COMP_1_2(uv_filter));652}653 654static void calc_pixel_ext(const struct msm_format *format,655 uint32_t src, uint32_t dst, uint32_t phase_step[2],656 int pix_ext_edge1[COMP_MAX], int pix_ext_edge2[COMP_MAX],657 bool horz)658{659 bool scaling = MSM_FORMAT_IS_YUV(format) ? true : (src != dst);660 int i;661 662 /*663 * Note:664 * We assume here that:665 * 1. PCMN filter is used for downscale666 * 2. bilinear filter is used for upscale667 * 3. we are in a single pipe configuration668 */669 670 for (i = 0; i < COMP_MAX; i++) {671 pix_ext_edge1[i] = 0;672 pix_ext_edge2[i] = scaling ? 1 : 0;673 }674}675 676static void mdp5_write_pixel_ext(struct mdp5_kms *mdp5_kms, enum mdp5_pipe pipe,677 const struct msm_format *format,678 uint32_t src_w, int pe_left[COMP_MAX], int pe_right[COMP_MAX],679 uint32_t src_h, int pe_top[COMP_MAX], int pe_bottom[COMP_MAX])680{681 const struct drm_format_info *info = drm_format_info(format->pixel_format);682 uint32_t lr, tb, req;683 int i;684 685 for (i = 0; i < COMP_MAX; i++) {686 uint32_t roi_w = src_w;687 uint32_t roi_h = src_h;688 689 if (MSM_FORMAT_IS_YUV(format) && i == COMP_1_2) {690 roi_w /= info->hsub;691 roi_h /= info->vsub;692 }693 694 lr = (pe_left[i] >= 0) ?695 MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT(pe_left[i]) :696 MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF(pe_left[i]);697 698 lr |= (pe_right[i] >= 0) ?699 MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT(pe_right[i]) :700 MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF(pe_right[i]);701 702 tb = (pe_top[i] >= 0) ?703 MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT(pe_top[i]) :704 MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF(pe_top[i]);705 706 tb |= (pe_bottom[i] >= 0) ?707 MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT(pe_bottom[i]) :708 MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF(pe_bottom[i]);709 710 req = MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT(roi_w +711 pe_left[i] + pe_right[i]);712 713 req |= MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM(roi_h +714 pe_top[i] + pe_bottom[i]);715 716 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_LR(pipe, i), lr);717 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_TB(pipe, i), tb);718 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS(pipe, i), req);719 720 DBG("comp-%d (L/R): rpt=%d/%d, ovf=%d/%d, req=%d", i,721 FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_LEFT_RPT),722 FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_RPT),723 FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_LEFT_OVF),724 FIELD(lr, MDP5_PIPE_SW_PIX_EXT_LR_RIGHT_OVF),725 FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_LEFT_RIGHT));726 727 DBG("comp-%d (T/B): rpt=%d/%d, ovf=%d/%d, req=%d", i,728 FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_TOP_RPT),729 FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_RPT),730 FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_TOP_OVF),731 FIELD(tb, MDP5_PIPE_SW_PIX_EXT_TB_BOTTOM_OVF),732 FIELD(req, MDP5_PIPE_SW_PIX_EXT_REQ_PIXELS_TOP_BOTTOM));733 }734}735 736struct pixel_ext {737 int left[COMP_MAX];738 int right[COMP_MAX];739 int top[COMP_MAX];740 int bottom[COMP_MAX];741};742 743struct phase_step {744 u32 x[COMP_MAX];745 u32 y[COMP_MAX];746};747 748static void mdp5_hwpipe_mode_set(struct mdp5_kms *mdp5_kms,749 struct mdp5_hw_pipe *hwpipe,750 struct drm_framebuffer *fb,751 struct phase_step *step,752 struct pixel_ext *pe,753 u32 scale_config, u32 hdecm, u32 vdecm,754 bool hflip, bool vflip,755 int crtc_x, int crtc_y,756 unsigned int crtc_w, unsigned int crtc_h,757 u32 src_img_w, u32 src_img_h,758 u32 src_x, u32 src_y,759 u32 src_w, u32 src_h)760{761 enum mdp5_pipe pipe = hwpipe->pipe;762 bool has_pe = hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT;763 const struct msm_format *format =764 msm_framebuffer_format(fb);765 766 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe),767 MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_img_w) |768 MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_img_h));769 770 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_SIZE(pipe),771 MDP5_PIPE_SRC_SIZE_WIDTH(src_w) |772 MDP5_PIPE_SRC_SIZE_HEIGHT(src_h));773 774 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_XY(pipe),775 MDP5_PIPE_SRC_XY_X(src_x) |776 MDP5_PIPE_SRC_XY_Y(src_y));777 778 mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_SIZE(pipe),779 MDP5_PIPE_OUT_SIZE_WIDTH(crtc_w) |780 MDP5_PIPE_OUT_SIZE_HEIGHT(crtc_h));781 782 mdp5_write(mdp5_kms, REG_MDP5_PIPE_OUT_XY(pipe),783 MDP5_PIPE_OUT_XY_X(crtc_x) |784 MDP5_PIPE_OUT_XY_Y(crtc_y));785 786 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe),787 MDP5_PIPE_SRC_FORMAT_A_BPC(format->bpc_a) |788 MDP5_PIPE_SRC_FORMAT_R_BPC(format->bpc_r_cr) |789 MDP5_PIPE_SRC_FORMAT_G_BPC(format->bpc_g_y) |790 MDP5_PIPE_SRC_FORMAT_B_BPC(format->bpc_b_cb) |791 COND(format->alpha_enable, MDP5_PIPE_SRC_FORMAT_ALPHA_ENABLE) |792 MDP5_PIPE_SRC_FORMAT_CPP(format->bpp - 1) |793 MDP5_PIPE_SRC_FORMAT_UNPACK_COUNT(format->unpack_count - 1) |794 COND(format->flags & MSM_FORMAT_FLAG_UNPACK_TIGHT,795 MDP5_PIPE_SRC_FORMAT_UNPACK_TIGHT) |796 MDP5_PIPE_SRC_FORMAT_FETCH_TYPE(format->fetch_type) |797 MDP5_PIPE_SRC_FORMAT_CHROMA_SAMP(format->chroma_sample));798 799 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_UNPACK(pipe),800 MDP5_PIPE_SRC_UNPACK_ELEM0(format->element[0]) |801 MDP5_PIPE_SRC_UNPACK_ELEM1(format->element[1]) |802 MDP5_PIPE_SRC_UNPACK_ELEM2(format->element[2]) |803 MDP5_PIPE_SRC_UNPACK_ELEM3(format->element[3]));804 805 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_OP_MODE(pipe),806 (hflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_LR : 0) |807 (vflip ? MDP5_PIPE_SRC_OP_MODE_FLIP_UD : 0) |808 COND(has_pe, MDP5_PIPE_SRC_OP_MODE_SW_PIX_EXT_OVERRIDE) |809 MDP5_PIPE_SRC_OP_MODE_BWC(BWC_LOSSLESS));810 811 /* not using secure mode: */812 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_ADDR_SW_STATUS(pipe), 0);813 814 if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT)815 mdp5_write_pixel_ext(mdp5_kms, pipe, format,816 src_w, pe->left, pe->right,817 src_h, pe->top, pe->bottom);818 819 if (hwpipe->caps & MDP_PIPE_CAP_SCALE) {820 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_X(pipe),821 step->x[COMP_0]);822 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_PHASE_STEP_Y(pipe),823 step->y[COMP_0]);824 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_X(pipe),825 step->x[COMP_1_2]);826 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CR_PHASE_STEP_Y(pipe),827 step->y[COMP_1_2]);828 mdp5_write(mdp5_kms, REG_MDP5_PIPE_DECIMATION(pipe),829 MDP5_PIPE_DECIMATION_VERT(vdecm) |830 MDP5_PIPE_DECIMATION_HORZ(hdecm));831 mdp5_write(mdp5_kms, REG_MDP5_PIPE_SCALE_CONFIG(pipe),832 scale_config);833 }834 835 if (hwpipe->caps & MDP_PIPE_CAP_CSC) {836 if (MSM_FORMAT_IS_YUV(format))837 csc_enable(mdp5_kms, pipe,838 mdp_get_default_csc_cfg(CSC_YUV2RGB));839 else840 csc_disable(mdp5_kms, pipe);841 }842 843 set_scanout_locked(mdp5_kms, pipe, fb);844}845 846static int mdp5_plane_mode_set(struct drm_plane *plane,847 struct drm_crtc *crtc, struct drm_framebuffer *fb,848 struct drm_rect *src, struct drm_rect *dest)849{850 struct drm_plane_state *pstate = plane->state;851 struct mdp5_hw_pipe *hwpipe = to_mdp5_plane_state(pstate)->hwpipe;852 struct mdp5_kms *mdp5_kms = get_kms(plane);853 enum mdp5_pipe pipe = hwpipe->pipe;854 struct mdp5_hw_pipe *right_hwpipe;855 const struct msm_format *format;856 uint32_t nplanes, config = 0;857 struct phase_step step = { { 0 } };858 struct pixel_ext pe = { { 0 } };859 uint32_t hdecm = 0, vdecm = 0;860 uint32_t pix_format;861 unsigned int rotation;862 bool vflip, hflip;863 int crtc_x, crtc_y;864 unsigned int crtc_w, crtc_h;865 uint32_t src_x, src_y;866 uint32_t src_w, src_h;867 uint32_t src_img_w, src_img_h;868 int ret;869 870 nplanes = fb->format->num_planes;871 872 /* bad formats should already be rejected: */873 if (WARN_ON(nplanes > pipe2nclients(pipe)))874 return -EINVAL;875 876 format = msm_framebuffer_format(fb);877 pix_format = format->pixel_format;878 879 src_x = src->x1;880 src_y = src->y1;881 src_w = drm_rect_width(src);882 src_h = drm_rect_height(src);883 884 crtc_x = dest->x1;885 crtc_y = dest->y1;886 crtc_w = drm_rect_width(dest);887 crtc_h = drm_rect_height(dest);888 889 /* src values are in Q16 fixed point, convert to integer: */890 src_x = src_x >> 16;891 src_y = src_y >> 16;892 src_w = src_w >> 16;893 src_h = src_h >> 16;894 895 src_img_w = min(fb->width, src_w);896 src_img_h = min(fb->height, src_h);897 898 DBG("%s: FB[%u] %u,%u,%u,%u -> CRTC[%u] %d,%d,%u,%u", plane->name,899 fb->base.id, src_x, src_y, src_w, src_h,900 crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);901 902 right_hwpipe = to_mdp5_plane_state(pstate)->r_hwpipe;903 if (right_hwpipe) {904 /*905 * if the plane comprises of 2 hw pipes, assume that the width906 * is split equally across them. The only parameters that varies907 * between the 2 pipes are src_x and crtc_x908 */909 crtc_w /= 2;910 src_w /= 2;911 src_img_w /= 2;912 }913 914 ret = calc_scalex_steps(plane, pix_format, src_w, crtc_w, step.x);915 if (ret)916 return ret;917 918 ret = calc_scaley_steps(plane, pix_format, src_h, crtc_h, step.y);919 if (ret)920 return ret;921 922 if (hwpipe->caps & MDP_PIPE_CAP_SW_PIX_EXT) {923 calc_pixel_ext(format, src_w, crtc_w, step.x,924 pe.left, pe.right, true);925 calc_pixel_ext(format, src_h, crtc_h, step.y,926 pe.top, pe.bottom, false);927 }928 929 /* TODO calc hdecm, vdecm */930 931 /* SCALE is used to both scale and up-sample chroma components */932 config |= get_scale_config(format, src_w, crtc_w, true);933 config |= get_scale_config(format, src_h, crtc_h, false);934 DBG("scale config = %x", config);935 936 rotation = drm_rotation_simplify(pstate->rotation,937 DRM_MODE_ROTATE_0 |938 DRM_MODE_REFLECT_X |939 DRM_MODE_REFLECT_Y);940 hflip = !!(rotation & DRM_MODE_REFLECT_X);941 vflip = !!(rotation & DRM_MODE_REFLECT_Y);942 943 mdp5_hwpipe_mode_set(mdp5_kms, hwpipe, fb, &step, &pe,944 config, hdecm, vdecm, hflip, vflip,945 crtc_x, crtc_y, crtc_w, crtc_h,946 src_img_w, src_img_h,947 src_x, src_y, src_w, src_h);948 if (right_hwpipe)949 mdp5_hwpipe_mode_set(mdp5_kms, right_hwpipe, fb, &step, &pe,950 config, hdecm, vdecm, hflip, vflip,951 crtc_x + crtc_w, crtc_y, crtc_w, crtc_h,952 src_img_w, src_img_h,953 src_x + src_w, src_y, src_w, src_h);954 955 return ret;956}957 958/*959 * Use this func and the one below only after the atomic state has been960 * successfully swapped961 */962enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane)963{964 struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state);965 966 if (WARN_ON(!pstate->hwpipe))967 return SSPP_NONE;968 969 return pstate->hwpipe->pipe;970}971 972enum mdp5_pipe mdp5_plane_right_pipe(struct drm_plane *plane)973{974 struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state);975 976 if (!pstate->r_hwpipe)977 return SSPP_NONE;978 979 return pstate->r_hwpipe->pipe;980}981 982uint32_t mdp5_plane_get_flush(struct drm_plane *plane)983{984 struct mdp5_plane_state *pstate = to_mdp5_plane_state(plane->state);985 u32 mask;986 987 if (WARN_ON(!pstate->hwpipe))988 return 0;989 990 mask = pstate->hwpipe->flush_mask;991 992 if (pstate->r_hwpipe)993 mask |= pstate->r_hwpipe->flush_mask;994 995 return mask;996}997 998static const uint32_t mdp5_plane_formats[] = {999 DRM_FORMAT_ARGB8888,1000 DRM_FORMAT_ABGR8888,1001 DRM_FORMAT_RGBA8888,1002 DRM_FORMAT_BGRA8888,1003 DRM_FORMAT_XRGB8888,1004 DRM_FORMAT_XBGR8888,1005 DRM_FORMAT_RGBX8888,1006 DRM_FORMAT_BGRX8888,1007 DRM_FORMAT_RGB888,1008 DRM_FORMAT_BGR888,1009 DRM_FORMAT_RGB565,1010 DRM_FORMAT_BGR565,1011 1012 DRM_FORMAT_NV12,1013 DRM_FORMAT_NV21,1014 DRM_FORMAT_NV16,1015 DRM_FORMAT_NV61,1016 DRM_FORMAT_VYUY,1017 DRM_FORMAT_UYVY,1018 DRM_FORMAT_YUYV,1019 DRM_FORMAT_YVYU,1020 DRM_FORMAT_YUV420,1021 DRM_FORMAT_YVU420,1022};1023 1024/* initialize plane */1025struct drm_plane *mdp5_plane_init(struct drm_device *dev,1026 enum drm_plane_type type)1027{1028 struct drm_plane *plane = NULL;1029 struct mdp5_plane *mdp5_plane;1030 1031 mdp5_plane = drmm_universal_plane_alloc(dev, struct mdp5_plane, base,1032 0xff, &mdp5_plane_funcs,1033 mdp5_plane_formats, ARRAY_SIZE(mdp5_plane_formats),1034 NULL, type, NULL);1035 if (IS_ERR(mdp5_plane))1036 return ERR_CAST(mdp5_plane);1037 1038 plane = &mdp5_plane->base;1039 1040 drm_plane_helper_add(plane, &mdp5_plane_helper_funcs);1041 1042 mdp5_plane_install_properties(plane, &plane->base);1043 1044 drm_plane_enable_fb_damage_clips(plane);1045 1046 return plane;1047}1048