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