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 <laurent.pinchart+renesas@ideasonboard.com>
This commit is contained in:
Laurent Pinchart 2015-02-18 12:18:05 +02:00
parent 3053460482
commit 920888a2d5
2 changed files with 142 additions and 49 deletions

View File

@ -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,

View File

@ -16,6 +16,7 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_plane_helper.h>
#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;