diff options
author | Beeresh Gopal <gbeeresh@codeaurora.org> | 2015-01-13 17:18:04 -0500 |
---|---|---|
committer | Rob Clark <robdclark@gmail.com> | 2015-02-01 15:32:46 -0500 |
commit | e172d10a9c4acc69bb07cbe9142ded2df791ff1f (patch) | |
tree | 6e3e0a70cc54b875128b834064f100d451542973 /drivers/gpu | |
parent | 5eba5d870f7234ecd25ec704dcfeb743c402dbe1 (diff) |
drm/msm/mdp5: Add hardware cursor support
This patch implements the hardware accelarated cursor
support for MDP5 platforms.
Signed-off-by: Beeresh Gopal <gbeeresh@codeaurora.org>
Signed-off-by: Wentao Xu <wentaox@codeaurora.org>
Signed-off-by: Stephane Viau <sviau@codeaurora.org>
Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu')
-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 @@ #include "drm_crtc_helper.h" #include "drm_flip_work.h" +#define CURSOR_WIDTH 64 +#define CURSOR_HEIGHT 64 + #define SSPP_MAX (SSPP_RGB3 + 1) /* TODO: Add SSPP_MAX in mdp5.xml.h */ struct mdp5_crtc { @@ -47,8 +50,21 @@ struct mdp5_crtc { #define PENDING_FLIP 0x2 atomic_t pending; + /* for unref'ing cursor bo's after scanout completes: */ + struct drm_flip_work unref_cursor_work; + struct mdp_irq vblank; struct mdp_irq err; + + struct { + /* protect REG_MDP5_LM_CURSOR* registers and cursor scanout_bo*/ + spinlock_t lock; + + /* current cursor being scanned out: */ + struct drm_gem_object *scanout_bo; + uint32_t width; + uint32_t height; + } cursor; }; #define to_mdp5_crtc(x) container_of(x, struct mdp5_crtc, base) @@ -129,11 +145,22 @@ static void complete_flip(struct drm_crtc *crtc, struct drm_file *file) } } +static void unref_cursor_worker(struct drm_flip_work *work, void *val) +{ + struct mdp5_crtc *mdp5_crtc = + container_of(work, struct mdp5_crtc, unref_cursor_work); + struct mdp5_kms *mdp5_kms = get_kms(&mdp5_crtc->base); + + msm_gem_put_iova(val, mdp5_kms->id); + drm_gem_object_unreference_unlocked(val); +} + static void mdp5_crtc_destroy(struct drm_crtc *crtc) { struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); drm_crtc_cleanup(crtc); + drm_flip_work_cleanup(&mdp5_crtc->unref_cursor_work); kfree(mdp5_crtc); } @@ -376,6 +403,132 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc, return -EINVAL; } +static int mdp5_crtc_cursor_set(struct drm_crtc *crtc, + struct drm_file *file, uint32_t handle, + uint32_t width, uint32_t height) +{ + struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct mdp5_kms *mdp5_kms = get_kms(crtc); + struct drm_gem_object *cursor_bo, *old_bo; + uint32_t blendcfg, cursor_addr, stride; + int ret, bpp, lm; + unsigned int depth; + enum mdp5_cursor_alpha cur_alpha = CURSOR_ALPHA_PER_PIXEL; + uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); + unsigned long flags; + + if ((width > CURSOR_WIDTH) || (height > CURSOR_HEIGHT)) { + dev_err(dev->dev, "bad cursor size: %dx%d\n", width, height); + return -EINVAL; + } + + if (NULL == mdp5_crtc->ctl) + return -EINVAL; + + if (!handle) { + DBG("Cursor off"); + return mdp5_ctl_set_cursor(mdp5_crtc->ctl, false); + } + + cursor_bo = drm_gem_object_lookup(dev, file, handle); + if (!cursor_bo) + return -ENOENT; + + ret = msm_gem_get_iova(cursor_bo, mdp5_kms->id, &cursor_addr); + if (ret) + return -EINVAL; + + lm = mdp5_crtc->lm; + drm_fb_get_bpp_depth(DRM_FORMAT_ARGB8888, &depth, &bpp); + stride = width * (bpp >> 3); + + spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); + old_bo = mdp5_crtc->cursor.scanout_bo; + + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_STRIDE(lm), stride); + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_FORMAT(lm), + MDP5_LM_CURSOR_FORMAT_FORMAT(CURSOR_FMT_ARGB8888)); + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_IMG_SIZE(lm), + MDP5_LM_CURSOR_IMG_SIZE_SRC_H(height) | + MDP5_LM_CURSOR_IMG_SIZE_SRC_W(width)); + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(lm), + MDP5_LM_CURSOR_SIZE_ROI_H(height) | + MDP5_LM_CURSOR_SIZE_ROI_W(width)); + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_BASE_ADDR(lm), cursor_addr); + + + blendcfg = MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_EN; + blendcfg |= MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_TRANSP_EN; + blendcfg |= MDP5_LM_CURSOR_BLEND_CONFIG_BLEND_ALPHA_SEL(cur_alpha); + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_BLEND_CONFIG(lm), blendcfg); + + mdp5_crtc->cursor.scanout_bo = cursor_bo; + mdp5_crtc->cursor.width = width; + mdp5_crtc->cursor.height = height; + spin_unlock_irqrestore(&mdp5_crtc->cursor.lock, flags); + + ret = mdp5_ctl_set_cursor(mdp5_crtc->ctl, true); + if (ret) + goto end; + + flush_mask |= mdp5_ctl_get_flush(mdp5_crtc->ctl); + crtc_flush(crtc, flush_mask); + +end: + if (old_bo) { + drm_flip_work_queue(&mdp5_crtc->unref_cursor_work, old_bo); + /* enable vblank to complete cursor work: */ + request_pending(crtc, PENDING_CURSOR); + } + return ret; +} + +static int mdp5_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct mdp5_kms *mdp5_kms = get_kms(crtc); + struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); + uint32_t flush_mask = mdp_ctl_flush_mask_cursor(0); + uint32_t xres = crtc->mode.hdisplay; + uint32_t yres = crtc->mode.vdisplay; + uint32_t roi_w; + uint32_t roi_h; + unsigned long flags; + + x = (x > 0) ? x : 0; + y = (y > 0) ? y : 0; + + /* + * Cursor Region Of Interest (ROI) is a plane read from cursor + * buffer to render. The ROI region is determined by the visiblity of + * the cursor point. In the default Cursor image the cursor point will + * be at the top left of the cursor image, unless it is specified + * otherwise using hotspot feature. + * + * If the cursor point reaches the right (xres - x < cursor.width) or + * bottom (yres - y < cursor.height) boundary of the screen, then ROI + * width and ROI height need to be evaluated to crop the cursor image + * accordingly. + * (xres-x) will be new cursor width when x > (xres - cursor.width) + * (yres-y) will be new cursor height when y > (yres - cursor.height) + */ + roi_w = min(mdp5_crtc->cursor.width, xres - x); + roi_h = min(mdp5_crtc->cursor.height, yres - y); + + spin_lock_irqsave(&mdp5_crtc->cursor.lock, flags); + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_SIZE(mdp5_crtc->lm), + MDP5_LM_CURSOR_SIZE_ROI_H(roi_h) | + MDP5_LM_CURSOR_SIZE_ROI_W(roi_w)); + mdp5_write(mdp5_kms, REG_MDP5_LM_CURSOR_START_XY(mdp5_crtc->lm), + MDP5_LM_CURSOR_START_XY_Y_START(y) | + MDP5_LM_CURSOR_START_XY_X_START(x)); + spin_unlock_irqrestore(&mdp5_crtc->cursor.lock, flags); + + crtc_flush(crtc, flush_mask); + + return 0; +} + static const struct drm_crtc_funcs mdp5_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .destroy = mdp5_crtc_destroy, @@ -384,6 +537,8 @@ static const struct drm_crtc_funcs mdp5_crtc_funcs = { .reset = drm_atomic_helper_crtc_reset, .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .cursor_set = mdp5_crtc_cursor_set, + .cursor_move = mdp5_crtc_cursor_move, }; 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) { struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, vblank); struct drm_crtc *crtc = &mdp5_crtc->base; + struct msm_drm_private *priv = crtc->dev->dev_private; unsigned pending; 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) if (pending & PENDING_FLIP) { complete_flip(crtc, NULL); } + + if (pending & PENDING_CURSOR) + drm_flip_work_commit(&mdp5_crtc->unref_cursor_work, priv->wq); } 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, mdp5_crtc->lm = GET_LM_ID(id); spin_lock_init(&mdp5_crtc->lm_lock); + spin_lock_init(&mdp5_crtc->cursor.lock); mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq; mdp5_crtc->err.irq = mdp5_crtc_err_irq; @@ -516,6 +676,10 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev, pipe2name(mdp5_plane_pipe(plane)), id); drm_crtc_init_with_planes(dev, crtc, plane, NULL, &mdp5_crtc_funcs); + + drm_flip_work_init(&mdp5_crtc->unref_cursor_work, + "unref cursor", unref_cursor_worker); + drm_crtc_helper_add(crtc, &mdp5_crtc_helper_funcs); plane->crtc = crtc; |