diff options
| -rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c index 7416cae60479..46fac545dc2b 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | |||
| @@ -24,6 +24,9 @@ | |||
| 24 | #include "drm_crtc_helper.h" | 24 | #include "drm_crtc_helper.h" |
| 25 | #include "drm_flip_work.h" | 25 | #include "drm_flip_work.h" |
| 26 | 26 | ||
| 27 | #define CURSOR_WIDTH 64 | ||
| 28 | #define CURSOR_HEIGHT 64 | ||
| 29 | |||
| 27 | #define SSPP_MAX (SSPP_RGB3 + 1) /* TODO: Add SSPP_MAX in mdp5.xml.h */ | 30 | #define SSPP_MAX (SSPP_RGB3 + 1) /* TODO: Add SSPP_MAX in mdp5.xml.h */ |
| 28 | 31 | ||
| 29 | struct mdp5_crtc { | 32 | struct mdp5_crtc { |
| @@ -47,8 +50,21 @@ struct mdp5_crtc { | |||
| 47 | #define PENDING_FLIP 0x2 | 50 | #define PENDING_FLIP 0x2 |
| 48 | atomic_t pending; | 51 | atomic_t pending; |
| 49 | 52 | ||
| 53 | /* for unref'ing cursor bo's after scanout completes: */ | ||
| 54 | struct drm_flip_work unref_cursor_work; | ||
| 55 | |||
| 50 | struct mdp_irq vblank; | 56 | struct mdp_irq vblank; |
| 51 | struct mdp_irq err; | 57 | struct mdp_irq err; |
| 58 | |||
| 59 | struct { | ||
| 60 | /* protect REG_MDP5_LM_CURSOR* registers and cursor scanout_bo*/ | ||
| 61 | spinlock_t lock; | ||
| 62 | |||
| 63 | /* current cursor being scanned out: */ | ||
| 64 | struct drm_gem_object *scanout_bo; | ||
| 65 | uint32_t width; | ||
| 66 | uint32_t height; | ||
| 67 | } cursor; | ||
| 52 | }; | 68 | }; |
| 53 | #define to_mdp5_crtc(x) container_of(x, struct mdp5_crtc, base) | 69 | #define to_mdp5_crtc(x) container_of(x, struct mdp5_crtc, base) |
| 54 | 70 | ||
| @@ -129,11 +145,22 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) | |||
| 129 | } | 145 | } |
| 130 | } | 146 | } |
| 131 | 147 | ||
| 148 | static void unref_cursor_worker(struct drm_flip_work *work, void *val) | ||
| 149 | { | ||
| 150 | struct mdp5_crtc *mdp5_crtc = | ||
| 151 | container_of(work, struct mdp5_crtc, unref_cursor_work); | ||
| 152 | struct mdp5_kms *mdp5_kms = get_kms(&mdp5_crtc->base); | ||
| 153 | |||
| 154 | msm_gem_put_iova(val, mdp5_kms->id); | ||
| 155 | drm_gem_object_unreference_unlocked(val); | ||
| 156 | } | ||
| 157 | |||
| 132 | static void mdp5_crtc_destroy(struct drm_crtc *crtc) | 158 | static void mdp5_crtc_destroy(struct drm_crtc *crtc) |
| 133 | { | 159 | { |
| 134 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | 160 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); |
| 135 | 161 | ||
| 136 | drm_crtc_cleanup(crtc); | 162 | drm_crtc_cleanup(crtc); |
| 163 | drm_flip_work_cleanup(&mdp5_crtc->unref_cursor_work); | ||
| 137 | 164 | ||
| 138 | kfree(mdp5_crtc); | 165 | kfree(mdp5_crtc); |
| 139 | } | 166 | } |
| @@ -376,6 +403,132 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc, | |||
| 376 | return -EINVAL; | 403 | return -EINVAL; |
| 377 | } | 404 | } |
| 378 | 405 | ||
| 406 | static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, | ||
| 407 | struct drm_file *file, uint32_t handle, | ||
| 408 | uint32_t width, uint32_t height) | ||
| 409 | { | ||
| 410 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | ||
| 411 | struct drm_device *dev = crtc->dev; | ||
| 412 | struct mdp5_kms *mdp5_kms = get_kms(crtc); | ||
| 413 | struct drm_gem_object *cursor_bo, *old_bo; | ||
| 414 | uint32_t blendcfg, cursor_addr, stride; | ||
| 415 | int ret, bpp, lm; | ||
| 416 | unsigned int depth; | ||
| 417 | enum mdp5_cursor_alpha cur_alpha = CURSOR_ALPHA_PER_PIXEL; | ||
| 418 | uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); | ||
| 419 | unsigned long flags; | ||
| 420 | |||
| 421 | if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) { | ||
| 422 | dev_err(dev->dev, "bad cursor size: %dx%d\n", width, height); | ||
| 423 | return -EINVAL; | ||
| 424 | } | ||
| 425 | |||
| 426 | if (NULL == mdp5_crtc->ctl) | ||
| 427 | return -EINVAL; | ||
| 428 | |||
| 429 | if (!handle) { | ||
| 430 | DBG("Cursor off"); | ||
| 431 | return mdp5_ctl_set_cursor(mdp5_crtc->ctl, false); | ||
| 432 | } | ||
| 433 | |||
| 434 | cursor_bo = drm_gem_object_lookup(dev, file, handle); | ||
| 435 | if (!cursor_bo) | ||
| 436 | return -ENOENT; | ||
| 437 | |||
| 438 | ret = msm_gem_get_iova(cursor_bo, mdp5_kms->id, &cursor_addr); | ||
| 439 | if (ret) | ||
| 440 | return -EINVAL; | ||
| 441 | |||
| 442 | lm = mdp5_crtc->lm; | ||
| 443 | drm_fb_get_bpp_depth(DRM_FORMAT_ARGB8888, &depth, &bpp); | ||
| 444 | stride = width * (bpp >> 3); | ||
| 445 | |||
| 446 | spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); | ||
| 447 | old_bo = mdp5_crtc->cursor.scanout_bo; | ||
| 448 | |||
| 449 | mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_STRIDE(lm), stride); | ||
| 450 | mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_FORMAT(lm), | ||
| 451 | MDP5_LM_CURSOR_FORMAT_FORMAT(CURSOR_FMT_ARGB8888)); | ||
| 452 | mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_IMG_SIZE(lm), | ||
| 453 | MDP5_LM_CURSOR_IMG_SIZE_SRC_H(height) | | ||
| 454 | MDP5_LM_CURSOR_IMG_SIZE_SRC_W(width)); | ||
| 455 | mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(lm), | ||
| 456 | MDP5_LM_CURSOR_SIZE_ROI_H(height) | | ||
| 457 | MDP5_LM_CURSOR_SIZE_ROI_W(width)); | ||
| 458 | mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_BASE_ADDR(lm), cursor_addr); | ||
| 459 | |||
| 460 | |||
| 461 | blendcfg = MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_EN; | ||
| 462 | blendcfg |= MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_TRANSP_EN; | ||
| 463 | blendcfg |= MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_ALPHA_SEL(cur_alpha); | ||
| 464 | mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_BLEND_CONFIG(lm), blendcfg); | ||
| 465 | |||
| 466 | mdp5_crtc->cursor.scanout_bo = cursor_bo; | ||
| 467 | mdp5_crtc->cursor.width = width; | ||
| 468 | mdp5_crtc->cursor.height = height; | ||
| 469 | spin_unlock_irqrestore(&mdp5_crtc->cursor.lock, flags); | ||
| 470 | |||
| 471 | ret = mdp5_ctl_set_cursor(mdp5_crtc->ctl, true); | ||
| 472 | if (ret) | ||
| 473 | goto end; | ||
| 474 | |||
| 475 | flush_mask |= mdp5_ctl_get_flush(mdp5_crtc->ctl); | ||
| 476 | crtc_flush(crtc, flush_mask); | ||
| 477 | |||
| 478 | end: | ||
| 479 | if (old_bo) { | ||
| 480 | drm_flip_work_queue(&mdp5_crtc->unref_cursor_work, old_bo); | ||
| 481 | /* enable vblank to complete cursor work: */ | ||
| 482 | request_pending(crtc, PENDING_CURSOR); | ||
| 483 | } | ||
| 484 | return ret; | ||
| 485 | } | ||
| 486 | |||
| 487 | static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) | ||
| 488 | { | ||
| 489 | struct mdp5_kms *mdp5_kms = get_kms(crtc); | ||
| 490 | struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); | ||
| 491 | uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); | ||
| 492 | uint32_t xres = crtc->mode.hdisplay; | ||
| 493 | uint32_t yres = crtc->mode.vdisplay; | ||
| 494 | uint32_t roi_w; | ||
| 495 | uint32_t roi_h; | ||
| 496 | unsigned long flags; | ||
| 497 | |||
| 498 | x = (x > 0) ? x : 0; | ||
| 499 | y = (y > 0) ? y : 0; | ||
| 500 | |||
| 501 | /* | ||
| 502 | * Cursor Region Of Interest (ROI) is a plane read from cursor | ||
| 503 | * buffer to render. The ROI region is determined by the visiblity of | ||
| 504 | * the cursor point. In the default Cursor image the cursor point will | ||
| 505 | * be at the top left of the cursor image, unless it is specified | ||
| 506 | * otherwise using hotspot feature. | ||
| 507 | * | ||
| 508 | * If the cursor point reaches the right (xres - x < cursor.width) or | ||
| 509 | * bottom (yres - y < cursor.height) boundary of the screen, then ROI | ||
| 510 | * width and ROI height need to be evaluated to crop the cursor image | ||
| 511 | * accordingly. | ||
| 512 | * (xres-x) will be new cursor width when x > (xres - cursor.width) | ||
| 513 | * (yres-y) will be new cursor height when y > (yres - cursor.height) | ||
| 514 | */ | ||
| 515 | roi_w = min(mdp5_crtc->cursor.width, xres - x); | ||
| 516 | roi_h = min(mdp5_crtc->cursor.height, yres - y); | ||
| 517 | |||
| 518 | spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); | ||
| 519 | mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(mdp5_crtc->lm), | ||
| 520 | MDP5_LM_CURSOR_SIZE_ROI_H(roi_h) | | ||
| 521 | MDP5_LM_CURSOR_SIZE_ROI_W(roi_w)); | ||
| 522 | mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_START_XY(mdp5_crtc->lm), | ||
| 523 | MDP5_LM_CURSOR_START_XY_Y_START(y) | | ||
| 524 | MDP5_LM_CURSOR_START_XY_X_START(x)); | ||
| 525 | spin_unlock_irqrestore(&mdp5_crtc->cursor.lock, flags); | ||
| 526 | |||
| 527 | crtc_flush(crtc, flush_mask); | ||
| 528 | |||
| 529 | return 0; | ||
| 530 | } | ||
| 531 | |||
| 379 | static const struct drm_crtc_funcs mdp5_crtc_funcs = { | 532 | static const struct drm_crtc_funcs mdp5_crtc_funcs = { |
| 380 | .set_config = drm_atomic_helper_set_config, | 533 | .set_config = drm_atomic_helper_set_config, |
| 381 | .destroy = mdp5_crtc_destroy, | 534 | .destroy = mdp5_crtc_destroy, |
| @@ -384,6 +537,8 @@ static const struct drm_crtc_funcs mdp5_crtc_funcs = { | |||
| 384 | .reset = drm_atomic_helper_crtc_reset, | 537 | .reset = drm_atomic_helper_crtc_reset, |
| 385 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | 538 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
| 386 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | 539 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
| 540 | .cursor_set = mdp5_crtc_cursor_set, | ||
| 541 | .cursor_move = mdp5_crtc_cursor_move, | ||
| 387 | }; | 542 | }; |
| 388 | 543 | ||
| 389 | static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = { | 544 | static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = { |
| @@ -400,6 +555,7 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) | |||
| 400 | { | 555 | { |
| 401 | struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, vblank); | 556 | struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, vblank); |
| 402 | struct drm_crtc *crtc = &mdp5_crtc->base; | 557 | struct drm_crtc *crtc = &mdp5_crtc->base; |
| 558 | struct msm_drm_private *priv = crtc->dev->dev_private; | ||
| 403 | unsigned pending; | 559 | unsigned pending; |
| 404 | 560 | ||
| 405 | mdp_irq_unregister(&get_kms(crtc)->base, &mdp5_crtc->vblank); | 561 | mdp_irq_unregister(&get_kms(crtc)->base, &mdp5_crtc->vblank); |
| @@ -409,6 +565,9 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus) | |||
| 409 | if (pending & PENDING_FLIP) { | 565 | if (pending & PENDING_FLIP) { |
| 410 | complete_flip(crtc, NULL); | 566 | complete_flip(crtc, NULL); |
| 411 | } | 567 | } |
| 568 | |||
| 569 | if (pending & PENDING_CURSOR) | ||
| 570 | drm_flip_work_commit(&mdp5_crtc->unref_cursor_work, priv->wq); | ||
| 412 | } | 571 | } |
| 413 | 572 | ||
| 414 | static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) | 573 | static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus) |
| @@ -508,6 +667,7 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, | |||
| 508 | mdp5_crtc->lm = GET_LM_ID(id); | 667 | mdp5_crtc->lm = GET_LM_ID(id); |
| 509 | 668 | ||
| 510 | spin_lock_init(&mdp5_crtc->lm_lock); | 669 | spin_lock_init(&mdp5_crtc->lm_lock); |
| 670 | spin_lock_init(&mdp5_crtc->cursor.lock); | ||
| 511 | 671 | ||
| 512 | mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq; | 672 | mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq; |
| 513 | mdp5_crtc->err.irq = mdp5_crtc_err_irq; | 673 | mdp5_crtc->err.irq = mdp5_crtc_err_irq; |
| @@ -516,6 +676,10 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, | |||
| 516 | pipe2name(mdp5_plane_pipe(plane)), id); | 676 | pipe2name(mdp5_plane_pipe(plane)), id); |
| 517 | 677 | ||
| 518 | drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs); | 678 | drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs); |
| 679 | |||
| 680 | drm_flip_work_init(&mdp5_crtc->unref_cursor_work, | ||
| 681 | "unref cursor", unref_cursor_worker); | ||
| 682 | |||
| 519 | drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs); | 683 | drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs); |
| 520 | plane->crtc = crtc; | 684 | plane->crtc = crtc; |
| 521 | 685 | ||
