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];
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,
.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,
#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"
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)
{
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;
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;
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.
}
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,
};
if (ret < 0)
return ret;
+ drm_plane_helper_add(&plane->plane,
+ &rcar_du_plane_helper_funcs);
+
if (type == DRM_PLANE_TYPE_PRIMARY)
continue;