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