瀏覽代碼

Merge branch 'drm/next/atomic' of git://linuxtv.org/pinchartl/fbdev into drm-next

rcar-du atomic modesetting support
* 'drm/next/atomic' of git://linuxtv.org/pinchartl/fbdev: (32 commits)
  drm: rcar-du: Fix race condition in hardware plane allocator
  drm: rcar-du: Move group locking inside rcar_du_crtc_update_planes()
  drm: rcar-du: Move plane commit code from CRTC start to CRTC resume
  drm: rcar-du: Move plane format to plane state
  drm: rcar-du: Remove unneeded rcar_du_crtc plane field
  drm: rcar-du: Replace plane crtc and enabled fields by plane state
  drm: rcar-du: Rework plane setup code
  drm: rcar-du: Switch plane set_property to atomic helpers
  drm: rcar-du: Switch page flip to atomic helpers
  drm: rcar-du: Implement asynchronous commit support
  drm: rcar-du: Replace encoder mode_fixup with atomic_check
  drm: rcar-du: Switch connector DPMS to atomic helpers
  drm: rcar-du: Switch mode config to atomic helpers
  drm: rcar-du: Switch plane update to atomic helpers
  drm: rcar-du: Rework CRTC enable/disable for atomic updates
  drm: rcar-du: Rework HDMI encoder enable/disable for atomic updates
  drm: rcar-du: Rework encoder enable/disable for atomic updates
  drm: rcar-du: Replace LVDS encoder DPMS by enable/disable
  drm: rcar-du: Remove private copy of plane size and position
  drm: rcar-du: Wire up atomic state object scaffolding
  ...
Dave Airlie 10 年之前
父節點
當前提交
7e4774843b

+ 185 - 215
drivers/gpu/drm/rcar-du/rcar_du_crtc.c

@@ -15,6 +15,8 @@
 #include <linux/mutex.h>
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
@@ -99,9 +101,13 @@ static void rcar_du_crtc_put(struct rcar_du_crtc *rcrtc)
 	clk_disable_unprepare(rcrtc->clock);
 }
 
+/* -----------------------------------------------------------------------------
+ * Hardware Setup
+ */
+
 static void rcar_du_crtc_set_display_timing(struct rcar_du_crtc *rcrtc)
 {
-	const struct drm_display_mode *mode = &rcrtc->crtc.mode;
+	const struct drm_display_mode *mode = &rcrtc->crtc.state->adjusted_mode;
 	unsigned long mode_clock = mode->clock * 1000;
 	unsigned long clk;
 	u32 value;
@@ -187,9 +193,19 @@ void rcar_du_crtc_route_output(struct drm_crtc *crtc,
 		rcdu->dpad0_source = rcrtc->index;
 }
 
-void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
+static unsigned int plane_zpos(struct rcar_du_plane *plane)
+{
+	return to_rcar_du_plane_state(plane->plane.state)->zpos;
+}
+
+static const struct rcar_du_format_info *
+plane_format(struct rcar_du_plane *plane)
+{
+	return to_rcar_du_plane_state(plane->plane.state)->format;
+}
+
+static void rcar_du_crtc_update_planes(struct rcar_du_crtc *rcrtc)
 {
-	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
 	struct rcar_du_plane *planes[RCAR_DU_NUM_HW_PLANES];
 	unsigned int num_planes = 0;
 	unsigned int prio = 0;
@@ -201,29 +217,30 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
 		struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i];
 		unsigned int j;
 
-		if (plane->crtc != &rcrtc->crtc || !plane->enabled)
+		if (plane->plane.state->crtc != &rcrtc->crtc)
 			continue;
 
 		/* Insert the plane in the sorted planes array. */
 		for (j = num_planes++; j > 0; --j) {
-			if (planes[j-1]->zpos <= plane->zpos)
+			if (plane_zpos(planes[j-1]) <= plane_zpos(plane))
 				break;
 			planes[j] = planes[j-1];
 		}
 
 		planes[j] = plane;
-		prio += plane->format->planes * 4;
+		prio += plane_format(plane)->planes * 4;
 	}
 
 	for (i = 0; i < num_planes; ++i) {
 		struct rcar_du_plane *plane = planes[i];
-		unsigned int index = plane->hwindex;
+		struct drm_plane_state *state = plane->plane.state;
+		unsigned int index = to_rcar_du_plane_state(state)->hwindex;
 
 		prio -= 4;
 		dspr |= (index + 1) << prio;
 		dptsr |= DPTSR_PnDK(index) |  DPTSR_PnTS(index);
 
-		if (plane->format->planes == 2) {
+		if (plane_format(plane)->planes == 2) {
 			index = (index + 1) % 8;
 
 			prio -= 4;
@@ -236,8 +253,6 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
 	 * with superposition controller 2.
 	 */
 	if (rcrtc->index % 2) {
-		u32 value = rcar_du_group_read(rcrtc->group, DPTSR);
-
 		/* The DPTSR register is updated when the display controller is
 		 * stopped. We thus need to restart the DU. Once again, sorry
 		 * for the flicker. One way to mitigate the issue would be to
@@ -245,29 +260,104 @@ void rcar_du_crtc_update_planes(struct drm_crtc *crtc)
 		 * split, or through a module parameter). Flicker would then
 		 * occur only if we need to break the pre-association.
 		 */
-		if (value != dptsr) {
+		mutex_lock(&rcrtc->group->lock);
+		if (rcar_du_group_read(rcrtc->group, DPTSR) != dptsr) {
 			rcar_du_group_write(rcrtc->group, DPTSR, dptsr);
 			if (rcrtc->group->used_crtcs)
 				rcar_du_group_restart(rcrtc->group);
 		}
+		mutex_unlock(&rcrtc->group->lock);
 	}
 
 	rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR,
 			    dspr);
 }
 
+/* -----------------------------------------------------------------------------
+ * Page Flip
+ */
+
+void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
+				   struct drm_file *file)
+{
+	struct drm_pending_vblank_event *event;
+	struct drm_device *dev = rcrtc->crtc.dev;
+	unsigned long flags;
+
+	/* Destroy the pending vertical blanking event associated with the
+	 * pending page flip, if any, and disable vertical blanking interrupts.
+	 */
+	spin_lock_irqsave(&dev->event_lock, flags);
+	event = rcrtc->event;
+	if (event && event->base.file_priv == file) {
+		rcrtc->event = NULL;
+		event->base.destroy(&event->base);
+		drm_crtc_vblank_put(&rcrtc->crtc);
+	}
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+}
+
+static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
+{
+	struct drm_pending_vblank_event *event;
+	struct drm_device *dev = rcrtc->crtc.dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	event = rcrtc->event;
+	rcrtc->event = NULL;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	if (event == NULL)
+		return;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	drm_send_vblank_event(dev, rcrtc->index, event);
+	wake_up(&rcrtc->flip_wait);
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	drm_crtc_vblank_put(&rcrtc->crtc);
+}
+
+static bool rcar_du_crtc_page_flip_pending(struct rcar_du_crtc *rcrtc)
+{
+	struct drm_device *dev = rcrtc->crtc.dev;
+	unsigned long flags;
+	bool pending;
+
+	spin_lock_irqsave(&dev->event_lock, flags);
+	pending = rcrtc->event != NULL;
+	spin_unlock_irqrestore(&dev->event_lock, flags);
+
+	return pending;
+}
+
+static void rcar_du_crtc_wait_page_flip(struct rcar_du_crtc *rcrtc)
+{
+	struct rcar_du_device *rcdu = rcrtc->group->dev;
+
+	if (wait_event_timeout(rcrtc->flip_wait,
+			       !rcar_du_crtc_page_flip_pending(rcrtc),
+			       msecs_to_jiffies(50)))
+		return;
+
+	dev_warn(rcdu->dev, "page flip timeout\n");
+
+	rcar_du_crtc_finish_page_flip(rcrtc);
+}
+
+/* -----------------------------------------------------------------------------
+ * Start/Stop and Suspend/Resume
+ */
+
 static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
 {
 	struct drm_crtc *crtc = &rcrtc->crtc;
 	bool interlaced;
-	unsigned int i;
 
 	if (rcrtc->started)
 		return;
 
-	if (WARN_ON(rcrtc->plane->format == NULL))
-		return;
-
 	/* Set display off and background to black */
 	rcar_du_crtc_write(rcrtc, DOOR, DOOR_RGB(0, 0, 0));
 	rcar_du_crtc_write(rcrtc, BPOR, BPOR_RGB(0, 0, 0));
@@ -276,20 +366,8 @@ 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);
 
-	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];
-
-		if (plane->crtc != crtc || !plane->enabled)
-			continue;
-
-		rcar_du_plane_setup(plane);
-	}
+	/* Start with all planes disabled. */
+	rcar_du_group_write(rcrtc->group, rcrtc->index % 2 ? DS2PR : DS1PR, 0);
 
 	/* Select master sync mode. This enables display operation in master
 	 * sync mode (with the HSYNC and VSYNC signals configured as outputs and
@@ -302,6 +380,9 @@ static void rcar_du_crtc_start(struct rcar_du_crtc *rcrtc)
 
 	rcar_du_group_start_stop(rcrtc->group, true);
 
+	/* Turn vertical blanking interrupt reporting back on. */
+	drm_crtc_vblank_on(crtc);
+
 	rcrtc->started = true;
 }
 
@@ -312,10 +393,12 @@ static void rcar_du_crtc_stop(struct rcar_du_crtc *rcrtc)
 	if (!rcrtc->started)
 		return;
 
-	mutex_lock(&rcrtc->group->planes.lock);
-	rcrtc->plane->enabled = false;
-	rcar_du_crtc_update_planes(crtc);
-	mutex_unlock(&rcrtc->group->planes.lock);
+	/* Disable vertical blanking interrupt reporting. We first need to wait
+	 * for page flip completion before stopping the CRTC as userspace
+	 * expects page flips to eventually complete.
+	 */
+	rcar_du_crtc_wait_page_flip(rcrtc);
+	drm_crtc_vblank_off(crtc);
 
 	/* Select switch sync mode. This stops display operation and configures
 	 * the HSYNC and VSYNC signals as inputs.
@@ -335,196 +418,111 @@ void rcar_du_crtc_suspend(struct rcar_du_crtc *rcrtc)
 
 void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc)
 {
-	if (rcrtc->dpms != DRM_MODE_DPMS_ON)
+	unsigned int i;
+
+	if (!rcrtc->enabled)
 		return;
 
 	rcar_du_crtc_get(rcrtc);
 	rcar_du_crtc_start(rcrtc);
-}
-
-static void rcar_du_crtc_update_base(struct rcar_du_crtc *rcrtc)
-{
-	struct drm_crtc *crtc = &rcrtc->crtc;
-
-	rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb);
-	rcar_du_plane_update_base(rcrtc->plane);
-}
-
-static void rcar_du_crtc_dpms(struct drm_crtc *crtc, int mode)
-{
-	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
 
-	if (mode != DRM_MODE_DPMS_ON)
-		mode = DRM_MODE_DPMS_OFF;
+	/* Commit the planes state. */
+	for (i = 0; i < ARRAY_SIZE(rcrtc->group->planes.planes); ++i) {
+		struct rcar_du_plane *plane = &rcrtc->group->planes.planes[i];
 
-	if (rcrtc->dpms == mode)
-		return;
+		if (plane->plane.state->crtc != &rcrtc->crtc)
+			continue;
 
-	if (mode == DRM_MODE_DPMS_ON) {
-		rcar_du_crtc_get(rcrtc);
-		rcar_du_crtc_start(rcrtc);
-	} else {
-		rcar_du_crtc_stop(rcrtc);
-		rcar_du_crtc_put(rcrtc);
+		rcar_du_plane_setup(plane);
 	}
 
-	rcrtc->dpms = mode;
+	rcar_du_crtc_update_planes(rcrtc);
 }
 
-static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
-				    const struct drm_display_mode *mode,
-				    struct drm_display_mode *adjusted_mode)
-{
-	/* TODO Fixup modes */
-	return true;
-}
+/* -----------------------------------------------------------------------------
+ * CRTC Functions
+ */
 
-static void rcar_du_crtc_mode_prepare(struct drm_crtc *crtc)
+static void rcar_du_crtc_enable(struct drm_crtc *crtc)
 {
 	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
 
-	/* We need to access the hardware during mode set, acquire a reference
-	 * to the CRTC.
-	 */
-	rcar_du_crtc_get(rcrtc);
+	if (rcrtc->enabled)
+		return;
 
-	/* Stop the CRTC and release the plane. Force the DPMS mode to off as a
-	 * result.
-	 */
-	rcar_du_crtc_stop(rcrtc);
-	rcar_du_plane_release(rcrtc->plane);
+	rcar_du_crtc_get(rcrtc);
+	rcar_du_crtc_start(rcrtc);
 
-	rcrtc->dpms = DRM_MODE_DPMS_OFF;
+	rcrtc->enabled = true;
 }
 
-static int rcar_du_crtc_mode_set(struct drm_crtc *crtc,
-				 struct drm_display_mode *mode,
-				 struct drm_display_mode *adjusted_mode,
-				 int x, int y,
-				 struct drm_framebuffer *old_fb)
+static void rcar_du_crtc_disable(struct drm_crtc *crtc)
 {
 	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-	struct rcar_du_device *rcdu = rcrtc->group->dev;
-	const struct rcar_du_format_info *format;
-	int ret;
-
-	format = rcar_du_format_info(crtc->primary->fb->pixel_format);
-	if (format == NULL) {
-		dev_dbg(rcdu->dev, "mode_set: unsupported format %08x\n",
-			crtc->primary->fb->pixel_format);
-		ret = -EINVAL;
-		goto error;
-	}
 
-	ret = rcar_du_plane_reserve(rcrtc->plane, format);
-	if (ret < 0)
-		goto error;
-
-	rcrtc->plane->format = format;
-
-	rcrtc->plane->src_x = x;
-	rcrtc->plane->src_y = y;
-	rcrtc->plane->width = mode->hdisplay;
-	rcrtc->plane->height = mode->vdisplay;
+	if (!rcrtc->enabled)
+		return;
 
-	rcar_du_plane_compute_base(rcrtc->plane, crtc->primary->fb);
+	rcar_du_crtc_stop(rcrtc);
+	rcar_du_crtc_put(rcrtc);
 
+	rcrtc->enabled = false;
 	rcrtc->outputs = 0;
-
-	return 0;
-
-error:
-	/* There's no rollback/abort operation to clean up in case of error. We
-	 * thus need to release the reference to the CRTC acquired in prepare()
-	 * here.
-	 */
-	rcar_du_crtc_put(rcrtc);
-	return ret;
 }
 
-static void rcar_du_crtc_mode_commit(struct drm_crtc *crtc)
+static bool rcar_du_crtc_mode_fixup(struct drm_crtc *crtc,
+				    const struct drm_display_mode *mode,
+				    struct drm_display_mode *adjusted_mode)
 {
-	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-
-	/* We're done, restart the CRTC and set the DPMS mode to on. The
-	 * reference to the DU acquired at prepare() time will thus be released
-	 * by the DPMS handler (possibly called by the disable() handler).
-	 */
-	rcar_du_crtc_start(rcrtc);
-	rcrtc->dpms = DRM_MODE_DPMS_ON;
+	/* TODO Fixup modes */
+	return true;
 }
 
-static int rcar_du_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
-				      struct drm_framebuffer *old_fb)
+static void rcar_du_crtc_atomic_begin(struct drm_crtc *crtc)
 {
+	struct drm_pending_vblank_event *event = crtc->state->event;
 	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
+	struct drm_device *dev = rcrtc->crtc.dev;
+	unsigned long flags;
 
-	rcrtc->plane->src_x = x;
-	rcrtc->plane->src_y = y;
+	if (event) {
+		event->pipe = rcrtc->index;
 
-	rcar_du_crtc_update_base(rcrtc);
+		WARN_ON(drm_crtc_vblank_get(crtc) != 0);
 
-	return 0;
+		spin_lock_irqsave(&dev->event_lock, flags);
+		rcrtc->event = event;
+		spin_unlock_irqrestore(&dev->event_lock, flags);
+	}
 }
 
-static void rcar_du_crtc_disable(struct drm_crtc *crtc)
+static void rcar_du_crtc_atomic_flush(struct drm_crtc *crtc)
 {
 	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
 
-	rcar_du_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
-	rcar_du_plane_release(rcrtc->plane);
+	rcar_du_crtc_update_planes(rcrtc);
 }
 
 static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
-	.dpms = rcar_du_crtc_dpms,
 	.mode_fixup = rcar_du_crtc_mode_fixup,
-	.prepare = rcar_du_crtc_mode_prepare,
-	.commit = rcar_du_crtc_mode_commit,
-	.mode_set = rcar_du_crtc_mode_set,
-	.mode_set_base = rcar_du_crtc_mode_set_base,
 	.disable = rcar_du_crtc_disable,
+	.enable = rcar_du_crtc_enable,
+	.atomic_begin = rcar_du_crtc_atomic_begin,
+	.atomic_flush = rcar_du_crtc_atomic_flush,
 };
 
-void rcar_du_crtc_cancel_page_flip(struct rcar_du_crtc *rcrtc,
-				   struct drm_file *file)
-{
-	struct drm_pending_vblank_event *event;
-	struct drm_device *dev = rcrtc->crtc.dev;
-	unsigned long flags;
-
-	/* Destroy the pending vertical blanking event associated with the
-	 * pending page flip, if any, and disable vertical blanking interrupts.
-	 */
-	spin_lock_irqsave(&dev->event_lock, flags);
-	event = rcrtc->event;
-	if (event && event->base.file_priv == file) {
-		rcrtc->event = NULL;
-		event->base.destroy(&event->base);
-		drm_vblank_put(dev, rcrtc->index);
-	}
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-}
-
-static void rcar_du_crtc_finish_page_flip(struct rcar_du_crtc *rcrtc)
-{
-	struct drm_pending_vblank_event *event;
-	struct drm_device *dev = rcrtc->crtc.dev;
-	unsigned long flags;
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-	event = rcrtc->event;
-	rcrtc->event = NULL;
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-
-	if (event == NULL)
-		return;
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-	drm_send_vblank_event(dev, rcrtc->index, event);
-	spin_unlock_irqrestore(&dev->event_lock, flags);
+static const struct drm_crtc_funcs crtc_funcs = {
+	.reset = drm_atomic_helper_crtc_reset,
+	.destroy = drm_crtc_cleanup,
+	.set_config = drm_atomic_helper_set_config,
+	.page_flip = drm_atomic_helper_page_flip,
+	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
 
-	drm_vblank_put(dev, rcrtc->index);
-}
+/* -----------------------------------------------------------------------------
+ * Interrupt Handling
+ */
 
 static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
 {
@@ -544,41 +542,9 @@ static irqreturn_t rcar_du_crtc_irq(int irq, void *arg)
 	return ret;
 }
 
-static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
-				  struct drm_framebuffer *fb,
-				  struct drm_pending_vblank_event *event,
-				  uint32_t page_flip_flags)
-{
-	struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
-	struct drm_device *dev = rcrtc->crtc.dev;
-	unsigned long flags;
-
-	spin_lock_irqsave(&dev->event_lock, flags);
-	if (rcrtc->event != NULL) {
-		spin_unlock_irqrestore(&dev->event_lock, flags);
-		return -EBUSY;
-	}
-	spin_unlock_irqrestore(&dev->event_lock, flags);
-
-	crtc->primary->fb = fb;
-	rcar_du_crtc_update_base(rcrtc);
-
-	if (event) {
-		event->pipe = rcrtc->index;
-		drm_vblank_get(dev, rcrtc->index);
-		spin_lock_irqsave(&dev->event_lock, flags);
-		rcrtc->event = event;
-		spin_unlock_irqrestore(&dev->event_lock, flags);
-	}
-
-	return 0;
-}
-
-static const struct drm_crtc_funcs crtc_funcs = {
-	.destroy = drm_crtc_cleanup,
-	.set_config = drm_crtc_helper_set_config,
-	.page_flip = rcar_du_crtc_page_flip,
-};
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
 
 int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
 {
@@ -620,20 +586,24 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
 		return -EPROBE_DEFER;
 	}
 
+	init_waitqueue_head(&rcrtc->flip_wait);
+
 	rcrtc->group = rgrp;
 	rcrtc->mmio_offset = mmio_offsets[index];
 	rcrtc->index = index;
-	rcrtc->dpms = DRM_MODE_DPMS_OFF;
-	rcrtc->plane = &rgrp->planes.planes[index % 2];
-
-	rcrtc->plane->crtc = crtc;
+	rcrtc->enabled = false;
 
-	ret = drm_crtc_init(rcdu->ddev, crtc, &crtc_funcs);
+	ret = drm_crtc_init_with_planes(rcdu->ddev, crtc,
+					&rgrp->planes.planes[index % 2].plane,
+					NULL, &crtc_funcs);
 	if (ret < 0)
 		return ret;
 
 	drm_crtc_helper_add(crtc, &crtc_helper_funcs);
 
+	/* Start with vertical blanking interrupt reporting disabled. */
+	drm_crtc_vblank_off(crtc);
+
 	/* Register the interrupt handler. */
 	if (rcar_du_has(rcdu, RCAR_DU_FEATURE_CRTC_IRQ_CLOCK)) {
 		irq = platform_get_irq(pdev, index);

+ 4 - 4
drivers/gpu/drm/rcar-du/rcar_du_crtc.h

@@ -15,12 +15,12 @@
 #define __RCAR_DU_CRTC_H__
 
 #include <linux/mutex.h>
+#include <linux/wait.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 
 struct rcar_du_group;
-struct rcar_du_plane;
 
 struct rcar_du_crtc {
 	struct drm_crtc crtc;
@@ -32,11 +32,12 @@ struct rcar_du_crtc {
 	bool started;
 
 	struct drm_pending_vblank_event *event;
+	wait_queue_head_t flip_wait;
+
 	unsigned int outputs;
-	int dpms;
+	bool enabled;
 
 	struct rcar_du_group *group;
-	struct rcar_du_plane *plane;
 };
 
 #define to_rcar_crtc(c)	container_of(c, struct rcar_du_crtc, crtc)
@@ -59,6 +60,5 @@ void rcar_du_crtc_resume(struct rcar_du_crtc *rcrtc);
 
 void rcar_du_crtc_route_output(struct drm_crtc *crtc,
 			       enum rcar_du_output output);
-void rcar_du_crtc_update_planes(struct drm_crtc *crtc);
 
 #endif /* __RCAR_DU_CRTC_H__ */

+ 11 - 6
drivers/gpu/drm/rcar-du/rcar_du_drv.c

@@ -19,6 +19,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm.h>
 #include <linux/slab.h>
+#include <linux/wait.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
@@ -163,6 +164,8 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
 		return -ENOMEM;
 	}
 
+	init_waitqueue_head(&rcdu->commit.wait);
+
 	rcdu->dev = &pdev->dev;
 	rcdu->info = np ? of_match_device(rcar_du_of_table, rcdu->dev)->data
 		   : (void *)platform_get_device_id(pdev)->driver_data;
@@ -175,17 +178,19 @@ static int rcar_du_load(struct drm_device *dev, unsigned long flags)
 	if (IS_ERR(rcdu->mmio))
 		return PTR_ERR(rcdu->mmio);
 
-	/* DRM/KMS objects */
-	ret = rcar_du_modeset_init(rcdu);
+	/* Initialize vertical blanking interrupts handling. Start with vblank
+	 * disabled for all CRTCs.
+	 */
+	ret = drm_vblank_init(dev, (1 << rcdu->info->num_crtcs) - 1);
 	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
+		dev_err(&pdev->dev, "failed to initialize vblank\n");
 		goto done;
 	}
 
-	/* vblank handling */
-	ret = drm_vblank_init(dev, (1 << rcdu->num_crtcs) - 1);
+	/* DRM/KMS objects */
+	ret = rcar_du_modeset_init(rcdu);
 	if (ret < 0) {
-		dev_err(&pdev->dev, "failed to initialize vblank\n");
+		dev_err(&pdev->dev, "failed to initialize DRM/KMS\n");
 		goto done;
 	}
 

+ 13 - 3
drivers/gpu/drm/rcar-du/rcar_du_drv.h

@@ -15,6 +15,7 @@
 #define __RCAR_DU_DRV_H__
 
 #include <linux/kernel.h>
+#include <linux/wait.h>
 
 #include "rcar_du_crtc.h"
 #include "rcar_du_group.h"
@@ -64,6 +65,10 @@ struct rcar_du_device_info {
 	unsigned int num_lvds;
 };
 
+#define RCAR_DU_MAX_CRTCS		3
+#define RCAR_DU_MAX_GROUPS		DIV_ROUND_UP(RCAR_DU_MAX_CRTCS, 2)
+#define RCAR_DU_MAX_LVDS		2
+
 struct rcar_du_device {
 	struct device *dev;
 	const struct rcar_du_device_info *info;
@@ -73,13 +78,18 @@ struct rcar_du_device {
 	struct drm_device *ddev;
 	struct drm_fbdev_cma *fbdev;
 
-	struct rcar_du_crtc crtcs[3];
+	struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS];
 	unsigned int num_crtcs;
 
-	struct rcar_du_group groups[2];
+	struct rcar_du_group groups[RCAR_DU_MAX_GROUPS];
 
 	unsigned int dpad0_source;
-	struct rcar_du_lvdsenc *lvds[2];
+	struct rcar_du_lvdsenc *lvds[RCAR_DU_MAX_LVDS];
+
+	struct {
+		wait_queue_head_t wait;
+		u32 pending;
+	} commit;
 };
 
 static inline bool rcar_du_has(struct rcar_du_device *rcdu,

+ 23 - 48
drivers/gpu/drm/rcar-du/rcar_du_encoder.c

@@ -42,46 +42,40 @@ rcar_du_connector_best_encoder(struct drm_connector *connector)
  * Encoder
  */
 
-static void rcar_du_encoder_dpms(struct drm_encoder *encoder, int mode)
+static void rcar_du_encoder_disable(struct drm_encoder *encoder)
 {
 	struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
 
-	if (mode != DRM_MODE_DPMS_ON)
-		mode = DRM_MODE_DPMS_OFF;
+	if (renc->lvds)
+		rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, false);
+}
+
+static void rcar_du_encoder_enable(struct drm_encoder *encoder)
+{
+	struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
 
 	if (renc->lvds)
-		rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc, mode);
+		rcar_du_lvdsenc_enable(renc->lvds, encoder->crtc, true);
 }
 
-static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder,
-				       const struct drm_display_mode *mode,
-				       struct drm_display_mode *adjusted_mode)
+static int rcar_du_encoder_atomic_check(struct drm_encoder *encoder,
+					struct drm_crtc_state *crtc_state,
+					struct drm_connector_state *conn_state)
 {
 	struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
+	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+	const struct drm_display_mode *mode = &crtc_state->mode;
 	const struct drm_display_mode *panel_mode;
+	struct drm_connector *connector = conn_state->connector;
 	struct drm_device *dev = encoder->dev;
-	struct drm_connector *connector;
-	bool found = false;
 
 	/* DAC encoders have currently no restriction on the mode. */
 	if (encoder->encoder_type == DRM_MODE_ENCODER_DAC)
-		return true;
-
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		if (connector->encoder == encoder) {
-			found = true;
-			break;
-		}
-	}
-
-	if (!found) {
-		dev_dbg(dev->dev, "mode_fixup: no connector found\n");
-		return false;
-	}
+		return 0;
 
 	if (list_empty(&connector->modes)) {
-		dev_dbg(dev->dev, "mode_fixup: empty modes list\n");
-		return false;
+		dev_dbg(dev->dev, "encoder: empty modes list\n");
+		return -EINVAL;
 	}
 
 	panel_mode = list_first_entry(&connector->modes,
@@ -90,7 +84,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder,
 	/* We're not allowed to modify the resolution. */
 	if (mode->hdisplay != panel_mode->hdisplay ||
 	    mode->vdisplay != panel_mode->vdisplay)
-		return false;
+		return -EINVAL;
 
 	/* The flat panel mode is fixed, just copy it to the adjusted mode. */
 	drm_mode_copy(adjusted_mode, panel_mode);
@@ -102,25 +96,7 @@ static bool rcar_du_encoder_mode_fixup(struct drm_encoder *encoder,
 		adjusted_mode->clock = clamp(adjusted_mode->clock,
 					     30000, 150000);
 
-	return true;
-}
-
-static void rcar_du_encoder_mode_prepare(struct drm_encoder *encoder)
-{
-	struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
-
-	if (renc->lvds)
-		rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc,
-				     DRM_MODE_DPMS_OFF);
-}
-
-static void rcar_du_encoder_mode_commit(struct drm_encoder *encoder)
-{
-	struct rcar_du_encoder *renc = to_rcar_encoder(encoder);
-
-	if (renc->lvds)
-		rcar_du_lvdsenc_dpms(renc->lvds, encoder->crtc,
-				     DRM_MODE_DPMS_ON);
+	return 0;
 }
 
 static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
@@ -133,11 +109,10 @@ static void rcar_du_encoder_mode_set(struct drm_encoder *encoder,
 }
 
 static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
-	.dpms = rcar_du_encoder_dpms,
-	.mode_fixup = rcar_du_encoder_mode_fixup,
-	.prepare = rcar_du_encoder_mode_prepare,
-	.commit = rcar_du_encoder_mode_commit,
 	.mode_set = rcar_du_encoder_mode_set,
+	.disable = rcar_du_encoder_disable,
+	.enable = rcar_du_encoder_enable,
+	.atomic_check = rcar_du_encoder_atomic_check,
 };
 
 static const struct drm_encoder_funcs encoder_funcs = {

+ 5 - 0
drivers/gpu/drm/rcar-du/rcar_du_group.h

@@ -14,6 +14,8 @@
 #ifndef __RCAR_DU_GROUP_H__
 #define __RCAR_DU_GROUP_H__
 
+#include <linux/mutex.h>
+
 #include "rcar_du_plane.h"
 
 struct rcar_du_device;
@@ -25,6 +27,7 @@ struct rcar_du_device;
  * @index: group index
  * @use_count: number of users of the group (rcar_du_group_(get|put))
  * @used_crtcs: number of CRTCs currently in use
+ * @lock: protects the DPTSR register
  * @planes: planes handled by the group
  */
 struct rcar_du_group {
@@ -35,6 +38,8 @@ struct rcar_du_group {
 	unsigned int use_count;
 	unsigned int used_crtcs;
 
+	struct mutex lock;
+
 	struct rcar_du_planes planes;
 };
 

+ 6 - 3
drivers/gpu/drm/rcar-du/rcar_du_hdmicon.c

@@ -12,6 +12,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_encoder_slave.h>
@@ -74,10 +75,13 @@ rcar_du_hdmi_connector_detect(struct drm_connector *connector, bool force)
 }
 
 static const struct drm_connector_funcs connector_funcs = {
-	.dpms = drm_helper_connector_dpms,
+	.dpms = drm_atomic_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = rcar_du_hdmi_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = rcar_du_hdmi_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
@@ -108,7 +112,7 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
 	if (ret < 0)
 		return ret;
 
-	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+	connector->dpms = DRM_MODE_DPMS_OFF;
 	drm_object_property_set_value(&connector->base,
 		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
 
@@ -116,7 +120,6 @@ int rcar_du_hdmi_connector_init(struct rcar_du_device *rcdu,
 	if (ret < 0)
 		return ret;
 
-	connector->encoder = encoder;
 	rcon->encoder = renc;
 
 	return 0;

+ 32 - 33
drivers/gpu/drm/rcar-du/rcar_du_hdmienc.c

@@ -26,41 +26,50 @@
 struct rcar_du_hdmienc {
 	struct rcar_du_encoder *renc;
 	struct device *dev;
-	int dpms;
+	bool enabled;
 };
 
 #define to_rcar_hdmienc(e)	(to_rcar_encoder(e)->hdmi)
 #define to_slave_funcs(e)	(to_rcar_encoder(e)->slave.slave_funcs)
 
-static void rcar_du_hdmienc_dpms(struct drm_encoder *encoder, int mode)
+static void rcar_du_hdmienc_disable(struct drm_encoder *encoder)
 {
 	struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
 	struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
 
-	if (mode != DRM_MODE_DPMS_ON)
-		mode = DRM_MODE_DPMS_OFF;
+	if (sfuncs->dpms)
+		sfuncs->dpms(encoder, DRM_MODE_DPMS_OFF);
 
-	if (hdmienc->dpms == mode)
-		return;
+	if (hdmienc->renc->lvds)
+		rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
+				       false);
 
-	if (mode == DRM_MODE_DPMS_ON && hdmienc->renc->lvds)
-		rcar_du_lvdsenc_dpms(hdmienc->renc->lvds, encoder->crtc, mode);
+	hdmienc->enabled = false;
+}
 
-	if (sfuncs->dpms)
-		sfuncs->dpms(encoder, mode);
+static void rcar_du_hdmienc_enable(struct drm_encoder *encoder)
+{
+	struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
+	struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
+
+	if (hdmienc->renc->lvds)
+		rcar_du_lvdsenc_enable(hdmienc->renc->lvds, encoder->crtc,
+				       true);
 
-	if (mode != DRM_MODE_DPMS_ON && hdmienc->renc->lvds)
-		rcar_du_lvdsenc_dpms(hdmienc->renc->lvds, encoder->crtc, mode);
+	if (sfuncs->dpms)
+		sfuncs->dpms(encoder, DRM_MODE_DPMS_ON);
 
-	hdmienc->dpms = mode;
+	hdmienc->enabled = true;
 }
 
-static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder,
-				       const struct drm_display_mode *mode,
-				       struct drm_display_mode *adjusted_mode)
+static int rcar_du_hdmienc_atomic_check(struct drm_encoder *encoder,
+					struct drm_crtc_state *crtc_state,
+					struct drm_connector_state *conn_state)
 {
 	struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
 	struct drm_encoder_slave_funcs *sfuncs = to_slave_funcs(encoder);
+	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
+	const struct drm_display_mode *mode = &crtc_state->mode;
 
 	/* The internal LVDS encoder has a clock frequency operating range of
 	 * 30MHz to 150MHz. Clamp the clock accordingly.
@@ -70,19 +79,9 @@ static bool rcar_du_hdmienc_mode_fixup(struct drm_encoder *encoder,
 					     30000, 150000);
 
 	if (sfuncs->mode_fixup == NULL)
-		return true;
-
-	return sfuncs->mode_fixup(encoder, mode, adjusted_mode);
-}
+		return 0;
 
-static void rcar_du_hdmienc_mode_prepare(struct drm_encoder *encoder)
-{
-	rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF);
-}
-
-static void rcar_du_hdmienc_mode_commit(struct drm_encoder *encoder)
-{
-	rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_ON);
+	return sfuncs->mode_fixup(encoder, mode, adjusted_mode) ? 0 : -EINVAL;
 }
 
 static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder,
@@ -99,18 +98,18 @@ static void rcar_du_hdmienc_mode_set(struct drm_encoder *encoder,
 }
 
 static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
-	.dpms = rcar_du_hdmienc_dpms,
-	.mode_fixup = rcar_du_hdmienc_mode_fixup,
-	.prepare = rcar_du_hdmienc_mode_prepare,
-	.commit = rcar_du_hdmienc_mode_commit,
 	.mode_set = rcar_du_hdmienc_mode_set,
+	.disable = rcar_du_hdmienc_disable,
+	.enable = rcar_du_hdmienc_enable,
+	.atomic_check = rcar_du_hdmienc_atomic_check,
 };
 
 static void rcar_du_hdmienc_cleanup(struct drm_encoder *encoder)
 {
 	struct rcar_du_hdmienc *hdmienc = to_rcar_hdmienc(encoder);
 
-	rcar_du_hdmienc_dpms(encoder, DRM_MODE_DPMS_OFF);
+	if (hdmienc->enabled)
+		rcar_du_hdmienc_disable(encoder);
 
 	drm_encoder_cleanup(encoder);
 	put_device(hdmienc->dev);

+ 316 - 17
drivers/gpu/drm/rcar-du/rcar_du_kms.c

@@ -12,12 +12,15 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_fb_cma_helper.h>
 #include <drm/drm_gem_cma_helper.h>
 
 #include <linux/of_graph.h>
+#include <linux/wait.h>
 
 #include "rcar_du_crtc.h"
 #include "rcar_du_drv.h"
@@ -185,9 +188,309 @@ static void rcar_du_output_poll_changed(struct drm_device *dev)
 	drm_fbdev_cma_hotplug_event(rcdu->fbdev);
 }
 
+/* -----------------------------------------------------------------------------
+ * Atomic Check and Update
+ */
+
+/*
+ * Atomic hardware plane allocator
+ *
+ * The hardware plane allocator is solely based on the atomic plane states
+ * without keeping any external state to avoid races between .atomic_check()
+ * and .atomic_commit().
+ *
+ * The core idea is to avoid using a free planes bitmask that would need to be
+ * shared between check and commit handlers with a collective knowledge based on
+ * the allocated hardware plane(s) for each KMS plane. The allocator then loops
+ * over all plane states to compute the free planes bitmask, allocates hardware
+ * planes based on that bitmask, and stores the result back in the plane states.
+ *
+ * For this to work we need to access the current state of planes not touched by
+ * the atomic update. To ensure that it won't be modified, we need to lock all
+ * planes using drm_atomic_get_plane_state(). This effectively serializes atomic
+ * updates from .atomic_check() up to completion (when swapping the states if
+ * the check step has succeeded) or rollback (when freeing the states if the
+ * check step has failed).
+ *
+ * Allocation is performed in the .atomic_check() handler and applied
+ * automatically when the core swaps the old and new states.
+ */
+
+static bool rcar_du_plane_needs_realloc(struct rcar_du_plane *plane,
+					struct rcar_du_plane_state *state)
+{
+	const struct rcar_du_format_info *cur_format;
+
+	cur_format = to_rcar_du_plane_state(plane->plane.state)->format;
+
+	/* Lowering the number of planes doesn't strictly require reallocation
+	 * as the extra hardware plane will be freed when committing, but doing
+	 * so could lead to more fragmentation.
+	 */
+	return !cur_format || cur_format->planes != state->format->planes;
+}
+
+static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
+{
+	unsigned int mask;
+
+	if (state->hwindex == -1)
+		return 0;
+
+	mask = 1 << state->hwindex;
+	if (state->format->planes == 2)
+		mask |= 1 << ((state->hwindex + 1) % 8);
+
+	return mask;
+}
+
+static int rcar_du_plane_hwalloc(unsigned int num_planes, unsigned int free)
+{
+	unsigned int i;
+
+	for (i = 0; i < RCAR_DU_NUM_HW_PLANES; ++i) {
+		if (!(free & (1 << i)))
+			continue;
+
+		if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
+			break;
+	}
+
+	return i == RCAR_DU_NUM_HW_PLANES ? -EBUSY : i;
+}
+
+static int rcar_du_atomic_check(struct drm_device *dev,
+				struct drm_atomic_state *state)
+{
+	struct rcar_du_device *rcdu = dev->dev_private;
+	unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
+	unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
+	bool needs_realloc = false;
+	unsigned int groups = 0;
+	unsigned int i;
+	int ret;
+
+	ret = drm_atomic_helper_check(dev, state);
+	if (ret < 0)
+		return ret;
+
+	/* Check if hardware planes need to be reallocated. */
+	for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
+		struct rcar_du_plane_state *plane_state;
+		struct rcar_du_plane *plane;
+		unsigned int index;
+
+		if (!state->planes[i])
+			continue;
+
+		plane = to_rcar_plane(state->planes[i]);
+		plane_state = to_rcar_du_plane_state(state->plane_states[i]);
+
+		/* If the plane is being disabled we don't need to go through
+		 * the full reallocation procedure. Just mark the hardware
+		 * plane(s) as freed.
+		 */
+		if (!plane_state->format) {
+			index = plane - plane->group->planes.planes;
+			group_freed_planes[plane->group->index] |= 1 << index;
+			plane_state->hwindex = -1;
+			continue;
+		}
+
+		/* If the plane needs to be reallocated mark it as such, and
+		 * mark the hardware plane(s) as free.
+		 */
+		if (rcar_du_plane_needs_realloc(plane, plane_state)) {
+			groups |= 1 << plane->group->index;
+			needs_realloc = true;
+
+			index = plane - plane->group->planes.planes;
+			group_freed_planes[plane->group->index] |= 1 << index;
+			plane_state->hwindex = -1;
+		}
+	}
+
+	if (!needs_realloc)
+		return 0;
+
+	/* Grab all plane states for the groups that need reallocation to ensure
+	 * locking and avoid racy updates. This serializes the update operation,
+	 * but there's not much we can do about it as that's the hardware
+	 * design.
+	 *
+	 * Compute the used planes mask for each group at the same time to avoid
+	 * looping over the planes separately later.
+	 */
+	while (groups) {
+		unsigned int index = ffs(groups) - 1;
+		struct rcar_du_group *group = &rcdu->groups[index];
+		unsigned int used_planes = 0;
+
+		for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) {
+			struct rcar_du_plane *plane = &group->planes.planes[i];
+			struct rcar_du_plane_state *plane_state;
+			struct drm_plane_state *s;
+
+			s = drm_atomic_get_plane_state(state, &plane->plane);
+			if (IS_ERR(s))
+				return PTR_ERR(s);
+
+			/* If the plane has been freed in the above loop its
+			 * hardware planes must not be added to the used planes
+			 * bitmask. However, the current state doesn't reflect
+			 * the free state yet, as we've modified the new state
+			 * above. Use the local freed planes list to check for
+			 * that condition instead.
+			 */
+			if (group_freed_planes[index] & (1 << i))
+				continue;
+
+			plane_state = to_rcar_du_plane_state(plane->plane.state);
+			used_planes |= rcar_du_plane_hwmask(plane_state);
+		}
+
+		group_free_planes[index] = 0xff & ~used_planes;
+		groups &= ~(1 << index);
+	}
+
+	/* Reallocate hardware planes for each plane that needs it. */
+	for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
+		struct rcar_du_plane_state *plane_state;
+		struct rcar_du_plane *plane;
+		int idx;
+
+		if (!state->planes[i])
+			continue;
+
+		plane = to_rcar_plane(state->planes[i]);
+		plane_state = to_rcar_du_plane_state(state->plane_states[i]);
+
+		/* Skip planes that are being disabled or don't need to be
+		 * reallocated.
+		 */
+		if (!plane_state->format ||
+		    !rcar_du_plane_needs_realloc(plane, plane_state))
+			continue;
+
+		idx = rcar_du_plane_hwalloc(plane_state->format->planes,
+					group_free_planes[plane->group->index]);
+		if (idx < 0) {
+			dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
+				__func__);
+			return idx;
+		}
+
+		plane_state->hwindex = idx;
+
+		group_free_planes[plane->group->index] &=
+			~rcar_du_plane_hwmask(plane_state);
+	}
+
+	return 0;
+}
+
+struct rcar_du_commit {
+	struct work_struct work;
+	struct drm_device *dev;
+	struct drm_atomic_state *state;
+	u32 crtcs;
+};
+
+static void rcar_du_atomic_complete(struct rcar_du_commit *commit)
+{
+	struct drm_device *dev = commit->dev;
+	struct rcar_du_device *rcdu = dev->dev_private;
+	struct drm_atomic_state *old_state = commit->state;
+
+	/* Apply the atomic update. */
+	drm_atomic_helper_commit_modeset_disables(dev, old_state);
+	drm_atomic_helper_commit_modeset_enables(dev, old_state);
+	drm_atomic_helper_commit_planes(dev, old_state);
+
+	drm_atomic_helper_wait_for_vblanks(dev, old_state);
+
+	drm_atomic_helper_cleanup_planes(dev, old_state);
+
+	drm_atomic_state_free(old_state);
+
+	/* Complete the commit, wake up any waiter. */
+	spin_lock(&rcdu->commit.wait.lock);
+	rcdu->commit.pending &= ~commit->crtcs;
+	wake_up_all_locked(&rcdu->commit.wait);
+	spin_unlock(&rcdu->commit.wait.lock);
+
+	kfree(commit);
+}
+
+static void rcar_du_atomic_work(struct work_struct *work)
+{
+	struct rcar_du_commit *commit =
+		container_of(work, struct rcar_du_commit, work);
+
+	rcar_du_atomic_complete(commit);
+}
+
+static int rcar_du_atomic_commit(struct drm_device *dev,
+				 struct drm_atomic_state *state, bool async)
+{
+	struct rcar_du_device *rcdu = dev->dev_private;
+	struct rcar_du_commit *commit;
+	unsigned int i;
+	int ret;
+
+	ret = drm_atomic_helper_prepare_planes(dev, state);
+	if (ret)
+		return ret;
+
+	/* Allocate the commit object. */
+	commit = kzalloc(sizeof(*commit), GFP_KERNEL);
+	if (commit == NULL)
+		return -ENOMEM;
+
+	INIT_WORK(&commit->work, rcar_du_atomic_work);
+	commit->dev = dev;
+	commit->state = state;
+
+	/* Wait until all affected CRTCs have completed previous commits and
+	 * mark them as pending.
+	 */
+	for (i = 0; i < dev->mode_config.num_crtc; ++i) {
+		if (state->crtcs[i])
+			commit->crtcs |= 1 << drm_crtc_index(state->crtcs[i]);
+	}
+
+	spin_lock(&rcdu->commit.wait.lock);
+	ret = wait_event_interruptible_locked(rcdu->commit.wait,
+			!(rcdu->commit.pending & commit->crtcs));
+	if (ret == 0)
+		rcdu->commit.pending |= commit->crtcs;
+	spin_unlock(&rcdu->commit.wait.lock);
+
+	if (ret) {
+		kfree(commit);
+		return ret;
+	}
+
+	/* Swap the state, this is the point of no return. */
+	drm_atomic_helper_swap_state(dev, state);
+
+	if (async)
+		schedule_work(&commit->work);
+	else
+		rcar_du_atomic_complete(commit);
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
 static const struct drm_mode_config_funcs rcar_du_mode_config_funcs = {
 	.fb_create = rcar_du_fb_create,
 	.output_poll_changed = rcar_du_output_poll_changed,
+	.atomic_check = rcar_du_atomic_check,
+	.atomic_commit = rcar_du_atomic_commit,
 };
 
 static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu,
@@ -392,6 +695,8 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 	for (i = 0; i < num_groups; ++i) {
 		struct rcar_du_group *rgrp = &rcdu->groups[i];
 
+		mutex_init(&rgrp->lock);
+
 		rgrp->dev = rcdu;
 		rgrp->mmio_offset = mmio_offsets[i];
 		rgrp->index = i;
@@ -439,27 +744,21 @@ int rcar_du_modeset_init(struct rcar_du_device *rcdu)
 		encoder->possible_clones = (1 << num_encoders) - 1;
 	}
 
-	/* Now that the CRTCs have been initialized register the planes. */
-	for (i = 0; i < num_groups; ++i) {
-		ret = rcar_du_planes_register(&rcdu->groups[i]);
-		if (ret < 0)
-			return ret;
-	}
+	drm_mode_config_reset(dev);
 
 	drm_kms_helper_poll_init(dev);
 
-	drm_helper_disable_unused_functions(dev);
-
-	fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc,
-				   dev->mode_config.num_connector);
-	if (IS_ERR(fbdev))
-		return PTR_ERR(fbdev);
+	if (dev->mode_config.num_connector) {
+		fbdev = drm_fbdev_cma_init(dev, 32, dev->mode_config.num_crtc,
+					   dev->mode_config.num_connector);
+		if (IS_ERR(fbdev))
+			return PTR_ERR(fbdev);
 
-#ifndef CONFIG_FRAMEBUFFER_CONSOLE
-	drm_fbdev_cma_restore_mode(fbdev);
-#endif
-
-	rcdu->fbdev = fbdev;
+		rcdu->fbdev = fbdev;
+	} else {
+		dev_info(rcdu->dev,
+			 "no connector found, disabling fbdev emulation\n");
+	}
 
 	return 0;
 }

+ 6 - 3
drivers/gpu/drm/rcar-du/rcar_du_lvdscon.c

@@ -12,6 +12,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 
@@ -74,10 +75,13 @@ rcar_du_lvds_connector_detect(struct drm_connector *connector, bool force)
 }
 
 static const struct drm_connector_funcs connector_funcs = {
-	.dpms = drm_helper_connector_dpms,
+	.dpms = drm_atomic_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = rcar_du_lvds_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = rcar_du_lvds_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
@@ -117,7 +121,7 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
 	if (ret < 0)
 		return ret;
 
-	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+	connector->dpms = DRM_MODE_DPMS_OFF;
 	drm_object_property_set_value(&connector->base,
 		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
 
@@ -125,7 +129,6 @@ int rcar_du_lvds_connector_init(struct rcar_du_device *rcdu,
 	if (ret < 0)
 		return ret;
 
-	connector->encoder = encoder;
 	lvdscon->connector.encoder = renc;
 
 	return 0;

+ 9 - 9
drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.c

@@ -28,7 +28,7 @@ struct rcar_du_lvdsenc {
 	unsigned int index;
 	void __iomem *mmio;
 	struct clk *clock;
-	int dpms;
+	bool enabled;
 
 	enum rcar_lvds_input input;
 };
@@ -48,7 +48,7 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
 	u32 pllcr;
 	int ret;
 
-	if (lvds->dpms == DRM_MODE_DPMS_ON)
+	if (lvds->enabled)
 		return 0;
 
 	ret = clk_prepare_enable(lvds->clock);
@@ -110,13 +110,13 @@ static int rcar_du_lvdsenc_start(struct rcar_du_lvdsenc *lvds,
 	lvdcr0 |= LVDCR0_LVRES;
 	rcar_lvds_write(lvds, LVDCR0, lvdcr0);
 
-	lvds->dpms = DRM_MODE_DPMS_ON;
+	lvds->enabled = true;
 	return 0;
 }
 
 static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds)
 {
-	if (lvds->dpms == DRM_MODE_DPMS_OFF)
+	if (!lvds->enabled)
 		return;
 
 	rcar_lvds_write(lvds, LVDCR0, 0);
@@ -124,13 +124,13 @@ static void rcar_du_lvdsenc_stop(struct rcar_du_lvdsenc *lvds)
 
 	clk_disable_unprepare(lvds->clock);
 
-	lvds->dpms = DRM_MODE_DPMS_OFF;
+	lvds->enabled = false;
 }
 
-int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds,
-			 struct drm_crtc *crtc, int mode)
+int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds, struct drm_crtc *crtc,
+			   bool enable)
 {
-	if (mode == DRM_MODE_DPMS_OFF) {
+	if (!enable) {
 		rcar_du_lvdsenc_stop(lvds);
 		return 0;
 	} else if (crtc) {
@@ -179,7 +179,7 @@ int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
 		lvds->dev = rcdu;
 		lvds->index = i;
 		lvds->input = i ? RCAR_LVDS_INPUT_DU1 : RCAR_LVDS_INPUT_DU0;
-		lvds->dpms = DRM_MODE_DPMS_OFF;
+		lvds->enabled = false;
 
 		ret = rcar_du_lvdsenc_get_resources(lvds, pdev);
 		if (ret < 0)

+ 4 - 4
drivers/gpu/drm/rcar-du/rcar_du_lvdsenc.h

@@ -28,15 +28,15 @@ enum rcar_lvds_input {
 
 #if IS_ENABLED(CONFIG_DRM_RCAR_LVDS)
 int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu);
-int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds,
-			 struct drm_crtc *crtc, int mode);
+int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
+			   struct drm_crtc *crtc, bool enable);
 #else
 static inline int rcar_du_lvdsenc_init(struct rcar_du_device *rcdu)
 {
 	return 0;
 }
-static inline int rcar_du_lvdsenc_dpms(struct rcar_du_lvdsenc *lvds,
-				       struct drm_crtc *crtc, int mode)
+static inline int rcar_du_lvdsenc_enable(struct rcar_du_lvdsenc *lvds,
+					 struct drm_crtc *crtc, bool enable)
 {
 	return 0;
 }

+ 173 - 246
drivers/gpu/drm/rcar-du/rcar_du_plane.c

@@ -12,10 +12,12 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #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"
@@ -26,16 +28,6 @@
 #define RCAR_DU_COLORKEY_SOURCE		(1 << 24)
 #define RCAR_DU_COLORKEY_MASK		(1 << 24)
 
-struct rcar_du_kms_plane {
-	struct drm_plane plane;
-	struct rcar_du_plane *hwplane;
-};
-
-static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
-{
-	return container_of(plane, struct rcar_du_kms_plane, plane)->hwplane;
-}
-
 static u32 rcar_du_plane_read(struct rcar_du_group *rgrp,
 			      unsigned int index, u32 reg)
 {
@@ -50,74 +42,31 @@ static void rcar_du_plane_write(struct rcar_du_group *rgrp,
 		      data);
 }
 
-int rcar_du_plane_reserve(struct rcar_du_plane *plane,
-			  const struct rcar_du_format_info *format)
-{
-	struct rcar_du_group *rgrp = plane->group;
-	unsigned int i;
-	int ret = -EBUSY;
-
-	mutex_lock(&rgrp->planes.lock);
-
-	for (i = 0; i < ARRAY_SIZE(rgrp->planes.planes); ++i) {
-		if (!(rgrp->planes.free & (1 << i)))
-			continue;
-
-		if (format->planes == 1 ||
-		    rgrp->planes.free & (1 << ((i + 1) % 8)))
-			break;
-	}
-
-	if (i == ARRAY_SIZE(rgrp->planes.planes))
-		goto done;
-
-	rgrp->planes.free &= ~(1 << i);
-	if (format->planes == 2)
-		rgrp->planes.free &= ~(1 << ((i + 1) % 8));
-
-	plane->hwindex = i;
-
-	ret = 0;
-
-done:
-	mutex_unlock(&rgrp->planes.lock);
-	return ret;
-}
-
-void rcar_du_plane_release(struct rcar_du_plane *plane)
-{
-	struct rcar_du_group *rgrp = plane->group;
-
-	if (plane->hwindex == -1)
-		return;
-
-	mutex_lock(&rgrp->planes.lock);
-	rgrp->planes.free |= 1 << plane->hwindex;
-	if (plane->format->planes == 2)
-		rgrp->planes.free |= 1 << ((plane->hwindex + 1) % 8);
-	mutex_unlock(&rgrp->planes.lock);
-
-	plane->hwindex = -1;
-}
-
-void rcar_du_plane_update_base(struct rcar_du_plane *plane)
+static void rcar_du_plane_setup_fb(struct rcar_du_plane *plane)
 {
+	struct rcar_du_plane_state *state =
+		to_rcar_du_plane_state(plane->plane.state);
+	struct drm_framebuffer *fb = plane->plane.state->fb;
 	struct rcar_du_group *rgrp = plane->group;
-	unsigned int index = plane->hwindex;
+	unsigned int src_x = state->state.src_x >> 16;
+	unsigned int src_y = state->state.src_y >> 16;
+	unsigned int index = state->hwindex;
+	struct drm_gem_cma_object *gem;
 	bool interlaced;
 	u32 mwr;
 
-	interlaced = plane->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE;
+	interlaced = state->state.crtc->state->adjusted_mode.flags
+		   & DRM_MODE_FLAG_INTERLACE;
 
 	/* Memory pitch (expressed in pixels). Must be doubled for interlaced
 	 * operation with 32bpp formats.
 	 */
-	if (plane->format->planes == 2)
-		mwr = plane->pitch;
+	if (state->format->planes == 2)
+		mwr = fb->pitches[0];
 	else
-		mwr = plane->pitch * 8 / plane->format->bpp;
+		mwr = fb->pitches[0] * 8 / state->format->bpp;
 
-	if (interlaced && plane->format->bpp == 32)
+	if (interlaced && state->format->bpp == 32)
 		mwr *= 2;
 
 	rcar_du_plane_write(rgrp, index, PnMWR, mwr);
@@ -134,42 +83,33 @@ void rcar_du_plane_update_base(struct rcar_du_plane *plane)
 	 * require a halved Y position value, in both progressive and interlaced
 	 * modes.
 	 */
-	rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x);
-	rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y *
-			    (!interlaced && plane->format->bpp == 32 ? 2 : 1));
-	rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[0]);
+	rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
+	rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
+			    (!interlaced && state->format->bpp == 32 ? 2 : 1));
 
-	if (plane->format->planes == 2) {
-		index = (index + 1) % 8;
-
-		rcar_du_plane_write(rgrp, index, PnMWR, plane->pitch);
+	gem = drm_fb_cma_get_gem_obj(fb, 0);
+	rcar_du_plane_write(rgrp, index, PnDSA0R, gem->paddr + fb->offsets[0]);
 
-		rcar_du_plane_write(rgrp, index, PnSPXR, plane->src_x);
-		rcar_du_plane_write(rgrp, index, PnSPYR, plane->src_y *
-				    (plane->format->bpp == 16 ? 2 : 1) / 2);
-		rcar_du_plane_write(rgrp, index, PnDSA0R, plane->dma[1]);
-	}
-}
+	if (state->format->planes == 2) {
+		index = (index + 1) % 8;
 
-void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
-				struct drm_framebuffer *fb)
-{
-	struct drm_gem_cma_object *gem;
+		rcar_du_plane_write(rgrp, index, PnMWR, fb->pitches[0]);
 
-	plane->pitch = fb->pitches[0];
+		rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
+		rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
+				    (state->format->bpp == 16 ? 2 : 1) / 2);
 
-	gem = drm_fb_cma_get_gem_obj(fb, 0);
-	plane->dma[0] = gem->paddr + fb->offsets[0];
-
-	if (plane->format->planes == 2) {
 		gem = drm_fb_cma_get_gem_obj(fb, 1);
-		plane->dma[1] = gem->paddr + fb->offsets[1];
+		rcar_du_plane_write(rgrp, index, PnDSA0R,
+				    gem->paddr + fb->offsets[1]);
 	}
 }
 
 static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
 				     unsigned int index)
 {
+	struct rcar_du_plane_state *state =
+		to_rcar_du_plane_state(plane->plane.state);
 	struct rcar_du_group *rgrp = plane->group;
 	u32 colorkey;
 	u32 pnmr;
@@ -183,47 +123,47 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
 	 * For XRGB, set the alpha value to the plane-wide alpha value and
 	 * enable alpha-blending regardless of the X bit value.
 	 */
-	if (plane->format->fourcc != DRM_FORMAT_XRGB1555)
+	if (state->format->fourcc != DRM_FORMAT_XRGB1555)
 		rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0);
 	else
 		rcar_du_plane_write(rgrp, index, PnALPHAR,
-				    PnALPHAR_ABIT_X | plane->alpha);
+				    PnALPHAR_ABIT_X | state->alpha);
 
-	pnmr = PnMR_BM_MD | plane->format->pnmr;
+	pnmr = PnMR_BM_MD | state->format->pnmr;
 
 	/* Disable color keying when requested. YUV formats have the
 	 * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying
 	 * automatically.
 	 */
-	if ((plane->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
+	if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
 		pnmr |= PnMR_SPIM_TP_OFF;
 
 	/* For packed YUV formats we need to select the U/V order. */
-	if (plane->format->fourcc == DRM_FORMAT_YUYV)
+	if (state->format->fourcc == DRM_FORMAT_YUYV)
 		pnmr |= PnMR_YCDF_YUYV;
 
 	rcar_du_plane_write(rgrp, index, PnMR, pnmr);
 
-	switch (plane->format->fourcc) {
+	switch (state->format->fourcc) {
 	case DRM_FORMAT_RGB565:
-		colorkey = ((plane->colorkey & 0xf80000) >> 8)
-			 | ((plane->colorkey & 0x00fc00) >> 5)
-			 | ((plane->colorkey & 0x0000f8) >> 3);
+		colorkey = ((state->colorkey & 0xf80000) >> 8)
+			 | ((state->colorkey & 0x00fc00) >> 5)
+			 | ((state->colorkey & 0x0000f8) >> 3);
 		rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
 		break;
 
 	case DRM_FORMAT_ARGB1555:
 	case DRM_FORMAT_XRGB1555:
-		colorkey = ((plane->colorkey & 0xf80000) >> 9)
-			 | ((plane->colorkey & 0x00f800) >> 6)
-			 | ((plane->colorkey & 0x0000f8) >> 3);
+		colorkey = ((state->colorkey & 0xf80000) >> 9)
+			 | ((state->colorkey & 0x00f800) >> 6)
+			 | ((state->colorkey & 0x0000f8) >> 3);
 		rcar_du_plane_write(rgrp, index, PnTC2R, colorkey);
 		break;
 
 	case DRM_FORMAT_XRGB8888:
 	case DRM_FORMAT_ARGB8888:
 		rcar_du_plane_write(rgrp, index, PnTC3R,
-				    PnTC3R_CODE | (plane->colorkey & 0xffffff));
+				    PnTC3R_CODE | (state->colorkey & 0xffffff));
 		break;
 	}
 }
@@ -231,6 +171,8 @@ static void rcar_du_plane_setup_mode(struct rcar_du_plane *plane,
 static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
 				  unsigned int index)
 {
+	struct rcar_du_plane_state *state =
+		to_rcar_du_plane_state(plane->plane.state);
 	struct rcar_du_group *rgrp = plane->group;
 	u32 ddcr2 = PnDDCR2_CODE;
 	u32 ddcr4;
@@ -242,17 +184,17 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
 	 */
 	ddcr4 = rcar_du_plane_read(rgrp, index, PnDDCR4);
 	ddcr4 &= ~PnDDCR4_EDF_MASK;
-	ddcr4 |= plane->format->edf | PnDDCR4_CODE;
+	ddcr4 |= state->format->edf | PnDDCR4_CODE;
 
 	rcar_du_plane_setup_mode(plane, index);
 
-	if (plane->format->planes == 2) {
-		if (plane->hwindex != index) {
-			if (plane->format->fourcc == DRM_FORMAT_NV12 ||
-			    plane->format->fourcc == DRM_FORMAT_NV21)
+	if (state->format->planes == 2) {
+		if (state->hwindex != index) {
+			if (state->format->fourcc == DRM_FORMAT_NV12 ||
+			    state->format->fourcc == DRM_FORMAT_NV21)
 				ddcr2 |= PnDDCR2_Y420;
 
-			if (plane->format->fourcc == DRM_FORMAT_NV21)
+			if (state->format->fourcc == DRM_FORMAT_NV21)
 				ddcr2 |= PnDDCR2_NV21;
 
 			ddcr2 |= PnDDCR2_DIVU;
@@ -265,10 +207,10 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
 	rcar_du_plane_write(rgrp, index, PnDDCR4, ddcr4);
 
 	/* Destination position and size */
-	rcar_du_plane_write(rgrp, index, PnDSXR, plane->width);
-	rcar_du_plane_write(rgrp, index, PnDSYR, plane->height);
-	rcar_du_plane_write(rgrp, index, PnDPXR, plane->dst_x);
-	rcar_du_plane_write(rgrp, index, PnDPYR, plane->dst_y);
+	rcar_du_plane_write(rgrp, index, PnDSXR, plane->plane.state->crtc_w);
+	rcar_du_plane_write(rgrp, index, PnDSYR, plane->plane.state->crtc_h);
+	rcar_du_plane_write(rgrp, index, PnDPXR, plane->plane.state->crtc_x);
+	rcar_du_plane_write(rgrp, index, PnDPYR, plane->plane.state->crtc_y);
 
 	/* Wrap-around and blinking, disabled */
 	rcar_du_plane_write(rgrp, index, PnWASPR, 0);
@@ -279,150 +221,140 @@ static void __rcar_du_plane_setup(struct rcar_du_plane *plane,
 
 void rcar_du_plane_setup(struct rcar_du_plane *plane)
 {
-	__rcar_du_plane_setup(plane, plane->hwindex);
-	if (plane->format->planes == 2)
-		__rcar_du_plane_setup(plane, (plane->hwindex + 1) % 8);
+	struct rcar_du_plane_state *state =
+		to_rcar_du_plane_state(plane->plane.state);
+
+	__rcar_du_plane_setup(plane, state->hwindex);
+	if (state->format->planes == 2)
+		__rcar_du_plane_setup(plane, (state->hwindex + 1) % 8);
 
-	rcar_du_plane_update_base(plane);
+	rcar_du_plane_setup_fb(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_state *rstate = to_rcar_du_plane_state(state);
 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
 	struct rcar_du_device *rcdu = rplane->group->dev;
-	const struct rcar_du_format_info *format;
-	unsigned int nplanes;
-	int ret;
 
-	format = rcar_du_format_info(fb->pixel_format);
-	if (format == NULL) {
-		dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
-			fb->pixel_format);
-		return -EINVAL;
+	if (!state->fb || !state->crtc) {
+		rstate->format = NULL;
+		return 0;
 	}
 
-	if (src_w >> 16 != crtc_w || src_h >> 16 != crtc_h) {
+	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;
 	}
 
-	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);
-		ret = rcar_du_plane_reserve(rplane, format);
-		if (ret < 0)
-			return ret;
+	rstate->format = rcar_du_format_info(state->fb->pixel_format);
+	if (rstate->format == NULL) {
+		dev_dbg(rcdu->dev, "%s: unsupported format %08x\n", __func__,
+			state->fb->pixel_format);
+		return -EINVAL;
 	}
 
-	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_atomic_update(struct drm_plane *plane,
+					struct drm_plane_state *old_state)
 {
 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
 
-	if (!rplane->enabled)
-		return 0;
+	if (plane->state->crtc)
+		rcar_du_plane_setup(rplane);
+}
 
-	mutex_lock(&rplane->group->planes.lock);
-	rplane->enabled = false;
-	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,
+};
 
-	rcar_du_plane_release(rplane);
+static void rcar_du_plane_reset(struct drm_plane *plane)
+{
+	struct rcar_du_plane_state *state;
 
-	rplane->crtc = NULL;
-	rplane->format = NULL;
+	if (plane->state && plane->state->fb)
+		drm_framebuffer_unreference(plane->state->fb);
 
-	return 0;
-}
+	kfree(plane->state);
+	plane->state = NULL;
 
-/* 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 void rcar_du_plane_set_alpha(struct rcar_du_plane *plane, u32 alpha)
-{
-	if (plane->alpha == alpha)
+	state = kzalloc(sizeof(*state), GFP_KERNEL);
+	if (state == NULL)
 		return;
 
-	plane->alpha = alpha;
-	if (!plane->enabled || plane->format->fourcc != DRM_FORMAT_XRGB1555)
-		return;
+	state->hwindex = -1;
+	state->alpha = 255;
+	state->colorkey = RCAR_DU_COLORKEY_NONE;
+	state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : 1;
 
-	rcar_du_plane_setup_mode(plane, plane->hwindex);
+	plane->state = &state->state;
+	plane->state->plane = plane;
 }
 
-static void rcar_du_plane_set_colorkey(struct rcar_du_plane *plane,
-				       u32 colorkey)
+static struct drm_plane_state *
+rcar_du_plane_atomic_duplicate_state(struct drm_plane *plane)
 {
-	if (plane->colorkey == colorkey)
-		return;
+	struct rcar_du_plane_state *state;
+	struct rcar_du_plane_state *copy;
 
-	plane->colorkey = colorkey;
-	if (!plane->enabled)
-		return;
+	state = to_rcar_du_plane_state(plane->state);
+	copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
+	if (copy == NULL)
+		return NULL;
+
+	if (copy->state.fb)
+		drm_framebuffer_reference(copy->state.fb);
 
-	rcar_du_plane_setup_mode(plane, plane->hwindex);
+	return &copy->state;
 }
 
-static void rcar_du_plane_set_zpos(struct rcar_du_plane *plane,
-				   unsigned int zpos)
+static void rcar_du_plane_atomic_destroy_state(struct drm_plane *plane,
+					       struct drm_plane_state *state)
 {
-	mutex_lock(&plane->group->planes.lock);
-	if (plane->zpos == zpos)
-		goto done;
+	kfree(to_rcar_du_plane_state(state));
+}
 
-	plane->zpos = zpos;
-	if (!plane->enabled)
-		goto done;
+static int rcar_du_plane_atomic_set_property(struct drm_plane *plane,
+					     struct drm_plane_state *state,
+					     struct drm_property *property,
+					     uint64_t val)
+{
+	struct rcar_du_plane_state *rstate = to_rcar_du_plane_state(state);
+	struct rcar_du_plane *rplane = to_rcar_plane(plane);
+	struct rcar_du_group *rgrp = rplane->group;
 
-	rcar_du_crtc_update_planes(plane->crtc);
+	if (property == rgrp->planes.alpha)
+		rstate->alpha = val;
+	else if (property == rgrp->planes.colorkey)
+		rstate->colorkey = val;
+	else if (property == rgrp->planes.zpos)
+		rstate->zpos = val;
+	else
+		return -EINVAL;
 
-done:
-	mutex_unlock(&plane->group->planes.lock);
+	return 0;
 }
 
-static int rcar_du_plane_set_property(struct drm_plane *plane,
-				      struct drm_property *property,
-				      uint64_t value)
+static int rcar_du_plane_atomic_get_property(struct drm_plane *plane,
+	const struct drm_plane_state *state, struct drm_property *property,
+	uint64_t *val)
 {
+	const struct rcar_du_plane_state *rstate =
+		container_of(state, const struct rcar_du_plane_state, state);
 	struct rcar_du_plane *rplane = to_rcar_plane(plane);
 	struct rcar_du_group *rgrp = rplane->group;
 
 	if (property == rgrp->planes.alpha)
-		rcar_du_plane_set_alpha(rplane, value);
+		*val = rstate->alpha;
 	else if (property == rgrp->planes.colorkey)
-		rcar_du_plane_set_colorkey(rplane, value);
+		*val = rstate->colorkey;
 	else if (property == rgrp->planes.zpos)
-		rcar_du_plane_set_zpos(rplane, value);
+		*val = rstate->zpos;
 	else
 		return -EINVAL;
 
@@ -430,10 +362,15 @@ 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,
-	.set_property = rcar_du_plane_set_property,
+	.update_plane = drm_atomic_helper_update_plane,
+	.disable_plane = drm_atomic_helper_disable_plane,
+	.reset = rcar_du_plane_reset,
+	.set_property = drm_atomic_helper_plane_set_property,
 	.destroy = drm_plane_cleanup,
+	.atomic_duplicate_state = rcar_du_plane_atomic_duplicate_state,
+	.atomic_destroy_state = rcar_du_plane_atomic_destroy_state,
+	.atomic_set_property = rcar_du_plane_atomic_set_property,
+	.atomic_get_property = rcar_du_plane_atomic_get_property,
 };
 
 static const uint32_t formats[] = {
@@ -453,10 +390,11 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp)
 {
 	struct rcar_du_planes *planes = &rgrp->planes;
 	struct rcar_du_device *rcdu = rgrp->dev;
+	unsigned int num_planes;
+	unsigned int num_crtcs;
+	unsigned int crtcs;
 	unsigned int i;
-
-	mutex_init(&planes->lock);
-	planes->free = 0xff;
+	int ret;
 
 	planes->alpha =
 		drm_property_create_range(rcdu->ddev, 0, "alpha", 0, 255);
@@ -478,45 +416,34 @@ int rcar_du_planes_init(struct rcar_du_group *rgrp)
 	if (planes->zpos == NULL)
 		return -ENOMEM;
 
-	for (i = 0; i < ARRAY_SIZE(planes->planes); ++i) {
-		struct rcar_du_plane *plane = &planes->planes[i];
-
-		plane->group = rgrp;
-		plane->hwindex = -1;
-		plane->alpha = 255;
-		plane->colorkey = RCAR_DU_COLORKEY_NONE;
-		plane->zpos = 0;
-	}
-
-	return 0;
-}
-
-int rcar_du_planes_register(struct rcar_du_group *rgrp)
-{
-	struct rcar_du_planes *planes = &rgrp->planes;
-	struct rcar_du_device *rcdu = rgrp->dev;
-	unsigned int crtcs;
-	unsigned int i;
-	int ret;
+	 /* Create one primary plane per in this group CRTC and seven overlay
+	  * planes.
+	  */
+	num_crtcs = min(rcdu->num_crtcs - 2 * rgrp->index, 2U);
+	num_planes = num_crtcs + 7;
 
 	crtcs = ((1 << rcdu->num_crtcs) - 1) & (3 << (2 * rgrp->index));
 
-	for (i = 0; i < RCAR_DU_NUM_KMS_PLANES; ++i) {
-		struct rcar_du_kms_plane *plane;
-
-		plane = devm_kzalloc(rcdu->dev, sizeof(*plane), GFP_KERNEL);
-		if (plane == NULL)
-			return -ENOMEM;
+	for (i = 0; i < num_planes; ++i) {
+		enum drm_plane_type type = i < num_crtcs
+					 ? DRM_PLANE_TYPE_PRIMARY
+					 : DRM_PLANE_TYPE_OVERLAY;
+		struct rcar_du_plane *plane = &planes->planes[i];
 
-		plane->hwplane = &planes->planes[i + 2];
-		plane->hwplane->zpos = 1;
+		plane->group = rgrp;
 
-		ret = drm_plane_init(rcdu->ddev, &plane->plane, crtcs,
-				     &rcar_du_plane_funcs, formats,
-				     ARRAY_SIZE(formats), false);
+		ret = drm_universal_plane_init(rcdu->ddev, &plane->plane, crtcs,
+					       &rcar_du_plane_funcs, formats,
+					       ARRAY_SIZE(formats), type);
 		if (ret < 0)
 			return ret;
 
+		drm_plane_helper_add(&plane->plane,
+				     &rcar_du_plane_helper_funcs);
+
+		if (type == DRM_PLANE_TYPE_PRIMARY)
+			continue;
+
 		drm_object_attach_property(&plane->plane.base,
 					   planes->alpha, 255);
 		drm_object_attach_property(&plane->plane.base,

+ 29 - 40
drivers/gpu/drm/rcar-du/rcar_du_plane.h

@@ -14,68 +14,57 @@
 #ifndef __RCAR_DU_PLANE_H__
 #define __RCAR_DU_PLANE_H__
 
-#include <linux/mutex.h>
-
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 
 struct rcar_du_format_info;
 struct rcar_du_group;
 
-/* The RCAR DU has 8 hardware planes, shared between KMS planes and CRTCs. As
- * using KMS planes requires at least one of the CRTCs being enabled, no more
- * than 7 KMS planes can be available. We thus create 7 KMS planes and
- * 9 software planes (one for each KMS planes and one for each CRTC).
+/* The RCAR DU has 8 hardware planes, shared between primary and overlay planes.
+ * As using overlay planes requires at least one of the CRTCs being enabled, no
+ * more than 7 overlay planes can be available. We thus create 1 primary plane
+ * per CRTC and 7 overlay planes, for a total of up to 9 KMS planes.
  */
-
-#define RCAR_DU_NUM_KMS_PLANES		7
+#define RCAR_DU_NUM_KMS_PLANES		9
 #define RCAR_DU_NUM_HW_PLANES		8
-#define RCAR_DU_NUM_SW_PLANES		9
 
 struct rcar_du_plane {
+	struct drm_plane plane;
 	struct rcar_du_group *group;
-	struct drm_crtc *crtc;
-
-	bool enabled;
-
-	int hwindex;		/* 0-based, -1 means unused */
-	unsigned int alpha;
-	unsigned int colorkey;
-	unsigned int zpos;
-
-	const struct rcar_du_format_info *format;
-
-	unsigned long dma[2];
-	unsigned int pitch;
-
-	unsigned int width;
-	unsigned int height;
-
-	unsigned int src_x;
-	unsigned int src_y;
-	unsigned int dst_x;
-	unsigned int dst_y;
 };
 
+static inline struct rcar_du_plane *to_rcar_plane(struct drm_plane *plane)
+{
+	return container_of(plane, struct rcar_du_plane, plane);
+}
+
 struct rcar_du_planes {
-	struct rcar_du_plane planes[RCAR_DU_NUM_SW_PLANES];
-	unsigned int free;
-	struct mutex lock;
+	struct rcar_du_plane planes[RCAR_DU_NUM_KMS_PLANES];
 
 	struct drm_property *alpha;
 	struct drm_property *colorkey;
 	struct drm_property *zpos;
 };
 
+struct rcar_du_plane_state {
+	struct drm_plane_state state;
+
+	const struct rcar_du_format_info *format;
+	int hwindex;		/* 0-based, -1 means unused */
+
+	unsigned int alpha;
+	unsigned int colorkey;
+	unsigned int zpos;
+};
+
+static inline struct rcar_du_plane_state *
+to_rcar_du_plane_state(struct drm_plane_state *state)
+{
+	return container_of(state, struct rcar_du_plane_state, state);
+}
+
 int rcar_du_planes_init(struct rcar_du_group *rgrp);
-int rcar_du_planes_register(struct rcar_du_group *rgrp);
 
 void rcar_du_plane_setup(struct rcar_du_plane *plane);
-void rcar_du_plane_update_base(struct rcar_du_plane *plane);
-void rcar_du_plane_compute_base(struct rcar_du_plane *plane,
-				struct drm_framebuffer *fb);
-int rcar_du_plane_reserve(struct rcar_du_plane *plane,
-			  const struct rcar_du_format_info *format);
-void rcar_du_plane_release(struct rcar_du_plane *plane);
 
 #endif /* __RCAR_DU_PLANE_H__ */

+ 6 - 3
drivers/gpu/drm/rcar-du/rcar_du_vgacon.c

@@ -12,6 +12,7 @@
  */
 
 #include <drm/drmP.h>
+#include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
 
@@ -43,10 +44,13 @@ rcar_du_vga_connector_detect(struct drm_connector *connector, bool force)
 }
 
 static const struct drm_connector_funcs connector_funcs = {
-	.dpms = drm_helper_connector_dpms,
+	.dpms = drm_atomic_helper_connector_dpms,
+	.reset = drm_atomic_helper_connector_reset,
 	.detect = rcar_du_vga_connector_detect,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.destroy = rcar_du_vga_connector_destroy,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };
 
 int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
@@ -76,7 +80,7 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
 	if (ret < 0)
 		return ret;
 
-	drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
+	connector->dpms = DRM_MODE_DPMS_OFF;
 	drm_object_property_set_value(&connector->base,
 		rcdu->ddev->mode_config.dpms_property, DRM_MODE_DPMS_OFF);
 
@@ -84,7 +88,6 @@ int rcar_du_vga_connector_init(struct rcar_du_device *rcdu,
 	if (ret < 0)
 		return ret;
 
-	connector->encoder = encoder;
 	rcon->encoder = renc;
 
 	return 0;