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 <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;
 
