diff options
Diffstat (limited to 'drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c')
-rw-r--r-- | drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 328 |
1 files changed, 205 insertions, 123 deletions
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c index f3daec4412ad..26e5fdea6594 100644 --- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2014 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * @@ -17,6 +18,7 @@ #include "mdp5_kms.h" +#define MAX_PLANE 4 struct mdp5_plane { struct drm_plane base; @@ -24,6 +26,11 @@ struct mdp5_plane { enum mdp5_pipe pipe; + spinlock_t pipe_lock; /* protect REG_MDP5_PIPE_* registers */ + uint32_t reg_offset; + + uint32_t flush_mask; /* used to commit pipe registers */ + uint32_t nformats; uint32_t formats[32]; @@ -31,31 +38,24 @@ struct mdp5_plane { }; #define to_mdp5_plane(x) container_of(x, struct mdp5_plane, base) +static int mdp5_plane_mode_set(struct drm_plane *plane, + struct drm_crtc *crtc, struct drm_framebuffer *fb, + int crtc_x, int crtc_y, + unsigned int crtc_w, unsigned int crtc_h, + uint32_t src_x, uint32_t src_y, + uint32_t src_w, uint32_t src_h); +static void set_scanout_locked(struct drm_plane *plane, + struct drm_framebuffer *fb); + static struct mdp5_kms *get_kms(struct drm_plane *plane) { struct msm_drm_private *priv = plane->dev->dev_private; return to_mdp5_kms(to_mdp_kms(priv->kms)); } -static int mdp5_plane_update(struct drm_plane *plane, - struct drm_crtc *crtc, struct drm_framebuffer *fb, - int crtc_x, int crtc_y, - unsigned int crtc_w, unsigned int crtc_h, - uint32_t src_x, uint32_t src_y, - uint32_t src_w, uint32_t src_h) +static bool plane_enabled(struct drm_plane_state *state) { - struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); - - mdp5_plane->enabled = true; - - if (plane->fb) - drm_framebuffer_unreference(plane->fb); - - drm_framebuffer_reference(fb); - - return mdp5_plane_mode_set(plane, crtc, fb, - crtc_x, crtc_y, crtc_w, crtc_h, - src_x, src_y, src_w, src_h); + return state->fb && state->crtc; } static int mdp5_plane_disable(struct drm_plane *plane) @@ -63,21 +63,13 @@ static int mdp5_plane_disable(struct drm_plane *plane) struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); enum mdp5_pipe pipe = mdp5_plane->pipe; - int i; DBG("%s: disable", mdp5_plane->name); - /* update our SMP request to zero (release all our blks): */ - for (i = 0; i < pipe2nclients(pipe); i++) - mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), 0); - - /* TODO detaching now will cause us not to get the last - * vblank and mdp5_smp_commit().. so other planes will - * still see smp blocks previously allocated to us as - * in-use.. - */ - if (plane->crtc) - mdp5_crtc_detach(plane->crtc, plane); + if (mdp5_kms) { + /* Release the memory we requested earlier from the SMP: */ + mdp5_smp_release(mdp5_kms->smp, pipe); + } return 0; } @@ -85,11 +77,8 @@ static int mdp5_plane_disable(struct drm_plane *plane) static void mdp5_plane_destroy(struct drm_plane *plane) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); - struct msm_drm_private *priv = plane->dev->dev_private; - - if (priv->kms) - mdp5_plane_disable(plane); + drm_plane_helper_disable(plane); drm_plane_cleanup(plane); kfree(mdp5_plane); @@ -109,109 +98,186 @@ int mdp5_plane_set_property(struct drm_plane *plane, return -EINVAL; } +static void mdp5_plane_reset(struct drm_plane *plane) +{ + struct mdp5_plane_state *mdp5_state; + + if (plane->state && plane->state->fb) + drm_framebuffer_unreference(plane->state->fb); + + kfree(to_mdp5_plane_state(plane->state)); + mdp5_state = kzalloc(sizeof(*mdp5_state), GFP_KERNEL); + + if (plane->type == DRM_PLANE_TYPE_PRIMARY) { + mdp5_state->zpos = 0; + } else { + mdp5_state->zpos = 1 + drm_plane_index(plane); + } + + plane->state = &mdp5_state->base; +} + +static struct drm_plane_state * +mdp5_plane_duplicate_state(struct drm_plane *plane) +{ + struct mdp5_plane_state *mdp5_state; + + if (WARN_ON(!plane->state)) + return NULL; + + mdp5_state = kmemdup(to_mdp5_plane_state(plane->state), + sizeof(*mdp5_state), GFP_KERNEL); + + if (mdp5_state && mdp5_state->base.fb) + drm_framebuffer_reference(mdp5_state->base.fb); + + mdp5_state->mode_changed = false; + mdp5_state->pending = false; + + return &mdp5_state->base; +} + +static void mdp5_plane_destroy_state(struct drm_plane *plane, + struct drm_plane_state *state) +{ + if (state->fb) + drm_framebuffer_unreference(state->fb); + + kfree(to_mdp5_plane_state(state)); +} + static const struct drm_plane_funcs mdp5_plane_funcs = { - .update_plane = mdp5_plane_update, - .disable_plane = mdp5_plane_disable, + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, .destroy = mdp5_plane_destroy, .set_property = mdp5_plane_set_property, + .reset = mdp5_plane_reset, + .atomic_duplicate_state = mdp5_plane_duplicate_state, + .atomic_destroy_state = mdp5_plane_destroy_state, }; -void mdp5_plane_set_scanout(struct drm_plane *plane, +static int mdp5_plane_prepare_fb(struct drm_plane *plane, struct drm_framebuffer *fb) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); - enum mdp5_pipe pipe = mdp5_plane->pipe; - uint32_t nplanes = drm_format_num_planes(fb->pixel_format); - uint32_t iova[4]; - int i; - - for (i = 0; i < nplanes; i++) { - struct drm_gem_object *bo = msm_framebuffer_bo(fb, i); - msm_gem_get_iova(bo, mdp5_kms->id, &iova[i]); - } - for (; i < 4; i++) - iova[i] = 0; - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), - MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | - MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); - - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe), - MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | - MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); - - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), iova[0]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), iova[1]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), iova[2]); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), iova[3]); - - plane->fb = fb; + DBG("%s: prepare: FB[%u]", mdp5_plane->name, fb->base.id); + return msm_framebuffer_prepare(fb, mdp5_kms->id); } -/* NOTE: looks like if horizontal decimation is used (if we supported that) - * then the width used to calculate SMP block requirements is the post- - * decimated width. Ie. SMP buffering sits downstream of decimation (which - * presumably happens during the dma from scanout buffer). - */ -static int request_smp_blocks(struct drm_plane *plane, uint32_t format, - uint32_t nplanes, uint32_t width) +static void mdp5_plane_cleanup_fb(struct drm_plane *plane, + struct drm_framebuffer *fb) { - struct drm_device *dev = plane->dev; struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); - enum mdp5_pipe pipe = mdp5_plane->pipe; - int i, hsub, nlines, nblks, ret; - hsub = drm_format_horz_chroma_subsampling(format); + DBG("%s: cleanup: FB[%u]", mdp5_plane->name, fb->base.id); + msm_framebuffer_cleanup(fb, mdp5_kms->id); +} - /* different if BWC (compressed framebuffer?) enabled: */ - nlines = 2; +static int mdp5_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); + struct drm_plane_state *old_state = plane->state; - for (i = 0, nblks = 0; i < nplanes; i++) { - int n, fetch_stride, cpp; + DBG("%s: check (%d -> %d)", mdp5_plane->name, + plane_enabled(old_state), plane_enabled(state)); - cpp = drm_format_plane_cpp(format, i); - fetch_stride = width * cpp / (i ? hsub : 1); + if (plane_enabled(state) && plane_enabled(old_state)) { + /* we cannot change SMP block configuration during scanout: */ + bool full_modeset = false; + if (state->fb->pixel_format != old_state->fb->pixel_format) { + DBG("%s: pixel_format change!", mdp5_plane->name); + full_modeset = true; + } + if (state->src_w != old_state->src_w) { + DBG("%s: src_w change!", mdp5_plane->name); + full_modeset = true; + } + if (to_mdp5_plane_state(old_state)->pending) { + DBG("%s: still pending!", mdp5_plane->name); + full_modeset = true; + } + if (full_modeset) { + struct drm_crtc_state *crtc_state = + drm_atomic_get_crtc_state(state->state, state->crtc); + crtc_state->mode_changed = true; + to_mdp5_plane_state(state)->mode_changed = true; + } + } else { + to_mdp5_plane_state(state)->mode_changed = true; + } - n = DIV_ROUND_UP(fetch_stride * nlines, SMP_BLK_SIZE); + return 0; +} - /* for hw rev v1.00 */ - if (mdp5_kms->rev == 0) - n = roundup_pow_of_two(n); +static void mdp5_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); + struct drm_plane_state *state = plane->state; - DBG("%s[%d]: request %d SMP blocks", mdp5_plane->name, i, n); - ret = mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), n); - if (ret) { - dev_err(dev->dev, "Could not allocate %d SMP blocks: %d\n", - n, ret); - return ret; - } + DBG("%s: update", mdp5_plane->name); - nblks += n; + if (!plane_enabled(state)) { + to_mdp5_plane_state(state)->pending = true; + mdp5_plane_disable(plane); + } else if (to_mdp5_plane_state(state)->mode_changed) { + int ret; + to_mdp5_plane_state(state)->pending = true; + ret = mdp5_plane_mode_set(plane, + state->crtc, state->fb, + state->crtc_x, state->crtc_y, + state->crtc_w, state->crtc_h, + state->src_x, state->src_y, + state->src_w, state->src_h); + /* atomic_check should have ensured that this doesn't fail */ + WARN_ON(ret < 0); + } else { + unsigned long flags; + spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); + set_scanout_locked(plane, state->fb); + spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); } - - /* in success case, return total # of blocks allocated: */ - return nblks; } -static void set_fifo_thresholds(struct drm_plane *plane, int nblks) +static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = { + .prepare_fb = mdp5_plane_prepare_fb, + .cleanup_fb = mdp5_plane_cleanup_fb, + .atomic_check = mdp5_plane_atomic_check, + .atomic_update = mdp5_plane_atomic_update, +}; + +static void set_scanout_locked(struct drm_plane *plane, + struct drm_framebuffer *fb) { struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); struct mdp5_kms *mdp5_kms = get_kms(plane); enum mdp5_pipe pipe = mdp5_plane->pipe; - uint32_t val; - /* 1/4 of SMP pool that is being fetched */ - val = (nblks * SMP_ENTRIES_PER_BLK) / 4; + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe), + MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) | + MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1])); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(pipe), val * 1); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(pipe), val * 2); - mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(pipe), val * 3); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe), + MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) | + MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3])); + + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), + msm_framebuffer_iova(fb, mdp5_kms->id, 0)); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), + msm_framebuffer_iova(fb, mdp5_kms->id, 1)); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), + msm_framebuffer_iova(fb, mdp5_kms->id, 2)); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), + msm_framebuffer_iova(fb, mdp5_kms->id, 4)); + plane->fb = fb; } -int mdp5_plane_mode_set(struct drm_plane *plane, +static int mdp5_plane_mode_set(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, @@ -225,7 +291,8 @@ int mdp5_plane_mode_set(struct drm_plane *plane, uint32_t nplanes, config = 0; uint32_t phasex_step = 0, phasey_step = 0; uint32_t hdecm = 0, vdecm = 0; - int i, nblks; + unsigned long flags; + int ret; nplanes = drm_format_num_planes(fb->pixel_format); @@ -243,12 +310,11 @@ int mdp5_plane_mode_set(struct drm_plane *plane, fb->base.id, src_x, src_y, src_w, src_h, crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h); - /* - * Calculate and request required # of smp blocks: - */ - nblks = request_smp_blocks(plane, fb->pixel_format, nplanes, src_w); - if (nblks < 0) - return nblks; + /* Request some memory from the SMP: */ + ret = mdp5_smp_request(mdp5_kms->smp, + mdp5_plane->pipe, fb->pixel_format, src_w); + if (ret) + return ret; /* * Currently we update the hw for allocations/requests immediately, @@ -256,8 +322,7 @@ int mdp5_plane_mode_set(struct drm_plane *plane, * would move into atomic->check_plane_state(), while updating the * hw would remain here: */ - for (i = 0; i < pipe2nclients(pipe); i++) - mdp5_smp_configure(mdp5_kms, pipe2client(pipe, i)); + mdp5_smp_configure(mdp5_kms->smp, pipe); if (src_w != crtc_w) { config |= MDP5_PIPE_SCALE_CONFIG_SCALEX_EN; @@ -269,6 +334,8 @@ int mdp5_plane_mode_set(struct drm_plane *plane, /* TODO calc phasey_step, vdecm */ } + spin_lock_irqsave(&mdp5_plane->pipe_lock, flags); + mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe), MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_w) | MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_h)); @@ -289,8 +356,6 @@ int mdp5_plane_mode_set(struct drm_plane *plane, MDP5_PIPE_OUT_XY_X(crtc_x) | MDP5_PIPE_OUT_XY_Y(crtc_y)); - mdp5_plane_set_scanout(plane, fb); - format = to_mdp_format(msm_framebuffer_format(fb)); mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe), @@ -330,22 +395,24 @@ int mdp5_plane_mode_set(struct drm_plane *plane, MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(SCALE_FILTER_NEAREST) | MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(SCALE_FILTER_NEAREST)); - set_fifo_thresholds(plane, nblks); + set_scanout_locked(plane, fb); - /* TODO detach from old crtc (if we had more than one) */ - mdp5_crtc_attach(crtc, plane); + spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags); - return 0; + return ret; } void mdp5_plane_complete_flip(struct drm_plane *plane) { struct mdp5_kms *mdp5_kms = get_kms(plane); - enum mdp5_pipe pipe = to_mdp5_plane(plane)->pipe; - int i; + struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); + enum mdp5_pipe pipe = mdp5_plane->pipe; + + DBG("%s: complete flip", mdp5_plane->name); - for (i = 0; i < pipe2nclients(pipe); i++) - mdp5_smp_commit(mdp5_kms, pipe2client(pipe, i)); + mdp5_smp_commit(mdp5_kms->smp, pipe); + + to_mdp5_plane_state(plane->state)->pending = false; } enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) @@ -354,9 +421,16 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane) return mdp5_plane->pipe; } +uint32_t mdp5_plane_get_flush(struct drm_plane *plane) +{ + struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane); + + return mdp5_plane->flush_mask; +} + /* initialize plane */ struct drm_plane *mdp5_plane_init(struct drm_device *dev, - enum mdp5_pipe pipe, bool private_plane) + enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset) { struct drm_plane *plane = NULL; struct mdp5_plane *mdp5_plane; @@ -377,10 +451,18 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev, mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats, ARRAY_SIZE(mdp5_plane->formats)); + mdp5_plane->flush_mask = mdp_ctl_flush_mask_pipe(pipe); + mdp5_plane->reg_offset = reg_offset; + spin_lock_init(&mdp5_plane->pipe_lock); + type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; - drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs, + ret = drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs, mdp5_plane->formats, mdp5_plane->nformats, type); + if (ret) + goto fail; + + drm_plane_helper_add(plane, &mdp5_plane_helper_funcs); mdp5_plane_install_properties(plane, &plane->base); |