From 920888a2d56f0ef7117bf3456cacb49c6801d8de Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 18 Feb 2015 12:18:05 +0200 Subject: [PATCH] drm: rcar-du: Implement planes atomic operations Implement the CRTC .atomic_begin() and .atomic_flush() operations, the plane .atomic_check(), .atomic_update() and operations, and use the transitional atomic helpers to implement the plane update and disable operations on top of the new atomic operations. The plane setup code can't be moved out of the CRTC start function completely yet, as the atomic code paths are not taken every time the CRTC needs to be started. This results in some code duplication that will be fixed after switching to atomic updates completely. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 37 +++++- drivers/gpu/drm/rcar-du/rcar_du_plane.c | 152 ++++++++++++++++-------- 2 files changed, 141 insertions(+), 48 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index c2ca2a302f44..3d862a894b17 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -357,12 +357,21 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc) rcar_du_crtc_set_display_timing(rcrtc); rcar_du_group_set_routing(rcrtc->group); + /* FIXME: Commit the planes state. This is required here as the CRTC can + * be started from the DPMS and system resume handler, which don't go + * through .atomic_plane_update() and .atomic_flush() to commit plane + * state. Similarly a mode set operation without any update to planes + * will not go through atomic plane configuration either. Additionally, + * given that the plane state atomic commit occurs between CRTC disable + * and enable, the hardware state could also be lost due to runtime PM, + * requiring a full commit here. This will be fixed later after + * switching to atomic updates completely. + */ mutex_lock(&rcrtc->group->planes.lock); rcrtc->plane->enabled = true; rcar_du_crtc_update_planes(crtc); mutex_unlock(&rcrtc->group->planes.lock); - /* Setup planes. */ for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) { struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i]; @@ -570,6 +579,30 @@ static void rcar_du_crtc_disable(struct drm_crtc *crtc) rcar_du_plane_release(rcrtc->plane); } +static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + /* We need to access the hardware during atomic update, acquire a + * reference to the CRTC. + */ + rcar_du_crtc_get(rcrtc); +} + +static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc) +{ + struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + + /* We're done, apply the configuration and drop the reference acquired + * in .atomic_begin(). + */ + mutex_lock(&rcrtc->group->planes.lock); + rcar_du_crtc_update_planes(crtc); + mutex_unlock(&rcrtc->group->planes.lock); + + rcar_du_crtc_put(rcrtc); +} + static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .dpms = rcar_du_crtc_dpms, .mode_fixup = rcar_du_crtc_mode_fixup, @@ -578,6 +611,8 @@ static const struct drm_crtc_helper_funcs crtc_helper_funcs = { .mode_set = rcar_du_crtc_mode_set, .mode_set_base = rcar_du_crtc_mode_set_base, .disable = rcar_du_crtc_disable, + .atomic_begin = rcar_du_crtc_atomic_begin, + .atomic_flush = rcar_du_crtc_atomic_flush, }; static int rcar_du_crtc_page_flip(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/rcar-du/rcar_du_plane.c b/drivers/gpu/drm/rcar-du/rcar_du_plane.c index 4a0669fd8176..03995c15bbe3 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_plane.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_plane.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "rcar_du_drv.h" #include "rcar_du_kms.h" @@ -45,6 +46,38 @@ static void rcar_du_plane_write(struct rcar_du_group *rgrp, data); } +static int rcar_du_plane_reserve_check(struct rcar_du_plane *plane, + const struct rcar_du_format_info *format) +{ + struct rcar_du_group *rgrp = plane->group; + unsigned int free; + unsigned int i; + int ret; + + mutex_lock(&rgrp->planes.lock); + + free = rgrp->planes.free; + + if (plane->hwindex != -1) { + free |= 1 << plane->hwindex; + if (plane->format->planes == 2) + free |= 1 << ((plane->hwindex + 1) % 8); + } + + for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) { + if (!(free & (1 << i))) + continue; + + if (format->planes == 1 || free & (1 << ((i + 1) % 8))) + break; + } + + ret = i == ARRAY_SIZE(rgrp->planes.planes) ? -EBUSY : 0; + + mutex_unlock(&rgrp->planes.lock); + return ret; +} + int rcar_du_plane_reserve(struct rcar_du_plane *plane, const struct rcar_du_format_info *format) { @@ -281,12 +314,8 @@ void rcar_du_plane_setup(struct rcar_du_plane *plane) rcar_du_plane_update_base(plane); } -static int -rcar_du_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 int rcar_du_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) { struct rcar_du_plane *rplane = to_rcar_plane(plane); struct rcar_du_device *rcdu = rplane->group->dev; @@ -294,63 +323,43 @@ rcar_du_plane_update(struct drm_plane *plane, struct drm_crtc *crtc, unsigned int nplanes; int ret; - if (plane->type != DRM_PLANE_TYPE_OVERLAY) - return -EINVAL; + if (!state->fb || !state->crtc) + return 0; - format = rcar_du_format_info(fb->pixel_format); - if (format == NULL) { - dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, - fb->pixel_format); + if (state->src_w >> 16 != state->crtc_w || + state->src_h >> 16 != state->crtc_h) { + dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__); return -EINVAL; } - if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) { - dev_dbg(rcdu->dev, "%s: scaling not supported\n", __func__); + format = rcar_du_format_info(state->fb->pixel_format); + if (format == NULL) { + dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__, + state->fb->pixel_format); return -EINVAL; } nplanes = rplane->format ? rplane->format->planes : 0; - /* Reallocate hardware planes if the number of required planes has - * changed. + /* If the number of required planes has changed we will need to + * reallocate hardware planes. Ensure free planes are available. */ if (format->planes != nplanes) { - rcar_du_plane_release(rplane); - ret = rcar_du_plane_reserve(rplane, format); - if (ret < 0) + ret = rcar_du_plane_reserve_check(rplane, format); + if (ret < 0) { + dev_dbg(rcdu->dev, "%s: no available hardware plane\n", + __func__); return ret; + } } - rplane->crtc = crtc; - rplane->format = format; - - rplane->src_x = src_x >> 16; - rplane->src_y = src_y >> 16; - rplane->dst_x = crtc_x; - rplane->dst_y = crtc_y; - rplane->width = crtc_w; - rplane->height = crtc_h; - - rcar_du_plane_compute_base(rplane, fb); - rcar_du_plane_setup(rplane); - - mutex_lock(&rplane->group->planes.lock); - rplane->enabled = true; - rcar_du_crtc_update_planes(rplane->crtc); - mutex_unlock(&rplane->group->planes.lock); - return 0; } -static int rcar_du_plane_disable(struct drm_plane *plane) +static void rcar_du_plane_disable(struct rcar_du_plane *rplane) { - struct rcar_du_plane *rplane = to_rcar_plane(plane); - - if (plane->type != DRM_PLANE_TYPE_OVERLAY) - return -EINVAL; - if (!rplane->enabled) - return 0; + return; mutex_lock(&rplane->group->planes.lock); rplane->enabled = false; @@ -361,10 +370,56 @@ static int rcar_du_plane_disable(struct drm_plane *plane) rplane->crtc = NULL; rplane->format = NULL; +} - return 0; +static void rcar_du_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct rcar_du_plane *rplane = to_rcar_plane(plane); + struct drm_plane_state *state = plane->state; + const struct rcar_du_format_info *format; + unsigned int nplanes; + + if (!state->crtc) { + rcar_du_plane_disable(rplane); + return; + } + + format = rcar_du_format_info(state->fb->pixel_format); + nplanes = rplane->format ? rplane->format->planes : 0; + + /* Reallocate hardware planes if the number of required planes has + * changed. + */ + if (format->planes != nplanes) { + rcar_du_plane_release(rplane); + rcar_du_plane_reserve(rplane, format); + } + + rplane->crtc = state->crtc; + rplane->format = format; + + rplane->src_x = state->src_x >> 16; + rplane->src_y = state->src_y >> 16; + rplane->dst_x = state->crtc_x; + rplane->dst_y = state->crtc_y; + rplane->width = state->crtc_w; + rplane->height = state->crtc_h; + + rcar_du_plane_compute_base(rplane, state->fb); + rcar_du_plane_setup(rplane); + + mutex_lock(&rplane->group->planes.lock); + rplane->enabled = true; + rcar_du_crtc_update_planes(rplane->crtc); + mutex_unlock(&rplane->group->planes.lock); } +static const struct drm_plane_helper_funcs rcar_du_plane_helper_funcs = { + .atomic_check = rcar_du_plane_atomic_check, + .atomic_update = rcar_du_plane_atomic_update, +}; + /* Both the .set_property and the .update_plane operations are called with the * mode_config lock held. There is this no need to explicitly protect access to * the alpha and colorkey fields and the mode register. @@ -431,8 +486,8 @@ static int rcar_du_plane_set_property(struct drm_plane *plane, } static const struct drm_plane_funcs rcar_du_plane_funcs = { - .update_plane = rcar_du_plane_update, - .disable_plane = rcar_du_plane_disable, + .update_plane = drm_plane_helper_update, + .disable_plane = drm_plane_helper_disable, .set_property = rcar_du_plane_set_property, .destroy = drm_plane_cleanup, }; @@ -509,6 +564,9 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp) if (ret < 0) return ret; + drm_plane_helper_add(&plane->plane, + &rcar_du_plane_helper_funcs); + if (type == DRM_PLANE_TYPE_PRIMARY) continue; -- 2.39.5