diff mbox

[v2,5/6] drm/omap: Add virtual plane support to omap_plane

Message ID 20180326162128.8740-6-bparrot@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

Benoit Parrot March 26, 2018, 4:21 p.m. UTC
Add virtual wide plane support by adding an secondary
plane_id so that an "omap_plane" can be composed of up to
two physical planes.

When at least one 'plane' child node is present in DT then
omap_plane_init will only use the plane described in DT.
Some of these nodes may be a virtual wide plane if they are defined
as two physical planes.
Planes can also be associated with various crtcs independently.
Therefore we can restrict the use of virtual plane to specific
CRTC/video port if need be, if crtc_mask is not specified then
the plane will be available to all available crtcs.
Physical planes which are not described will essentially be hidden
from the driver.

If no 'plane' child nodes exist then the normal plane
allocation will take place.

Signed-off-by: Benoit Parrot <bparrot@ti.com>
---
 drivers/gpu/drm/omapdrm/omap_drv.c   | 127 +++++++++++++++++++++++++++--
 drivers/gpu/drm/omapdrm/omap_fb.c    |  66 ++++++++++-----
 drivers/gpu/drm/omapdrm/omap_fb.h    |   4 +-
 drivers/gpu/drm/omapdrm/omap_plane.c | 151 ++++++++++++++++++++++++++---------
 drivers/gpu/drm/omapdrm/omap_plane.h |   4 +-
 5 files changed, 283 insertions(+), 69 deletions(-)

Comments

Tomi Valkeinen April 5, 2018, 11:14 a.m. UTC | #1
On 26/03/18 19:21, Benoit Parrot wrote:
> Add virtual wide plane support by adding an secondary
> plane_id so that an "omap_plane" can be composed of up to
> two physical planes.
> 
> When at least one 'plane' child node is present in DT then
> omap_plane_init will only use the plane described in DT.
> Some of these nodes may be a virtual wide plane if they are defined
> as two physical planes.
> Planes can also be associated with various crtcs independently.
> Therefore we can restrict the use of virtual plane to specific
> CRTC/video port if need be, if crtc_mask is not specified then
> the plane will be available to all available crtcs.
> Physical planes which are not described will essentially be hidden
> from the driver.
> 
> If no 'plane' child nodes exist then the normal plane
> allocation will take place.

The descs in many of the patches are a bit messy. How do you format
them? At least vim's autoformat gives a more consistent wrapping.

> Signed-off-by: Benoit Parrot <bparrot@ti.com>
> ---
>  drivers/gpu/drm/omapdrm/omap_drv.c   | 127 +++++++++++++++++++++++++++--
>  drivers/gpu/drm/omapdrm/omap_fb.c    |  66 ++++++++++-----
>  drivers/gpu/drm/omapdrm/omap_fb.h    |   4 +-
>  drivers/gpu/drm/omapdrm/omap_plane.c | 151 ++++++++++++++++++++++++++---------
>  drivers/gpu/drm/omapdrm/omap_plane.h |   4 +-
>  5 files changed, 283 insertions(+), 69 deletions(-)
> 
> diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
> index 37ee20c773c7..4c43ef239136 100644
> --- a/drivers/gpu/drm/omapdrm/omap_drv.c
> +++ b/drivers/gpu/drm/omapdrm/omap_drv.c
> @@ -16,6 +16,7 @@
>   */
>  
>  #include <linux/sys_soc.h>
> +#include <linux/sort.h>
>  
>  #include <drm/drm_atomic.h>
>  #include <drm/drm_atomic_helper.h>
> @@ -116,6 +117,112 @@ static void omap_atomic_commit_tail(struct drm_atomic_state *old_state)
>  	priv->dispc_ops->runtime_put();
>  }
>  
> +static int omap_atomic_state_zpos_cmp(const void *a, const void *b)
> +{
> +	const struct drm_plane_state *sa = *(struct drm_plane_state **)a;
> +	const struct drm_plane_state *sb = *(struct drm_plane_state **)b;
> +
> +	if (sa->zpos != sb->zpos)
> +		return sa->zpos - sb->zpos;
> +	else
> +		return sa->plane->base.id - sb->plane->base.id;
> +}
> +
> +static int omap_atomic_crtc_normalize_zpos(struct drm_crtc *crtc,
> +					   struct drm_crtc_state *crtc_state)
> +{
> +	struct drm_atomic_state *state = crtc_state->state;
> +	struct drm_device *dev = crtc->dev;
> +	int total_planes = dev->mode_config.num_total_plane;
> +	struct drm_plane_state **states;
> +	struct drm_plane *plane;
> +	int i, inc, n = 0;
> +	int ret = 0;
> +
> +	DRM_DEBUG_ATOMIC("[CRTC:%d:%s] calculating normalized zpos values\n",
> +			 crtc->base.id, crtc->name);
> +
> +	states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL);
> +	if (!states)
> +		return -ENOMEM;
> +
> +	/*
> +	 * Normalization process might create new states for planes which
> +	 * normalized_zpos has to be recalculated.
> +	 */
> +	drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) {
> +		struct drm_plane_state *plane_state =
> +			drm_atomic_get_plane_state(state, plane);
> +		if (IS_ERR(plane_state)) {
> +			ret = PTR_ERR(plane_state);
> +			goto done;
> +		}
> +		states[n++] = plane_state;
> +		DRM_DEBUG_ATOMIC("[PLANE:%d:%s] processing zpos value %d\n",
> +				 plane->base.id, plane->name,
> +				 plane_state->zpos);
> +	}
> +
> +	sort(states, n, sizeof(*states), omap_atomic_state_zpos_cmp, NULL);
> +
> +	for (inc = 0, i = 0; i < n; i++) {
> +		plane = states[i]->plane;
> +
> +		states[i]->normalized_zpos = i + inc;
> +		DRM_DEBUG_ATOMIC("[PLANE:%d:%s] normalized zpos value %d\n",
> +				 plane->base.id, plane->name, i + inc);
> +		/*
> +		 * If the current plane is virtual it uses 2 hw planes
> +		 * therefore the very next zpos is used by the secondary/aux
> +		 * plane so we need to skip one zpos from this point on.
> +		 */
> +		if (is_omap_plane_virtual(plane))
> +			inc++;
> +	}
> +	crtc_state->zpos_changed = true;
> +
> +done:
> +	kfree(states);
> +	return ret;
> +}
> +
> +static int omap_atomic_normalize_zpos(struct drm_device *dev,
> +				      struct drm_atomic_state *state)
> +{
> +	struct drm_crtc *crtc;
> +	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
> +	int i, ret = 0;
> +
> +	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
> +		if (old_crtc_state->plane_mask != new_crtc_state->plane_mask ||
> +		    new_crtc_state->zpos_changed) {
> +			ret = omap_atomic_crtc_normalize_zpos(crtc,
> +							      new_crtc_state);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int omap_atomic_check(struct drm_device *dev,
> +			     struct drm_atomic_state *state)
> +{
> +	int ret;
> +
> +	ret = drm_atomic_helper_check(dev, state);
> +	if (ret)
> +		return ret;
> +
> +	if (dev->mode_config.normalize_zpos) {
> +		ret = omap_atomic_normalize_zpos(dev, state);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
>  static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = {
>  	.atomic_commit_tail = omap_atomic_commit_tail,
>  };
> @@ -123,7 +230,7 @@ static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs =
>  static const struct drm_mode_config_funcs omap_mode_config_funcs = {
>  	.fb_create = omap_framebuffer_create,
>  	.output_poll_changed = drm_fb_helper_output_poll_changed,
> -	.atomic_check = drm_atomic_helper_check,
> +	.atomic_check = omap_atomic_check,
>  	.atomic_commit = drm_atomic_helper_commit,
>  };

I don't think it's a good idea to copypaste
drm_atomic_helper_crtc_normalize_zpos from the drm core. With Peter's
patches, the drm core does the normalizing, doesn't it? So they are
already sorted properly, and you can just go through them and adjust the
zpos if there are virtual planes.

 Tomi
diff mbox

Patch

diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 37ee20c773c7..4c43ef239136 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -16,6 +16,7 @@ 
  */
 
 #include <linux/sys_soc.h>
+#include <linux/sort.h>
 
 #include <drm/drm_atomic.h>
 #include <drm/drm_atomic_helper.h>
@@ -116,6 +117,112 @@  static void omap_atomic_commit_tail(struct drm_atomic_state *old_state)
 	priv->dispc_ops->runtime_put();
 }
 
+static int omap_atomic_state_zpos_cmp(const void *a, const void *b)
+{
+	const struct drm_plane_state *sa = *(struct drm_plane_state **)a;
+	const struct drm_plane_state *sb = *(struct drm_plane_state **)b;
+
+	if (sa->zpos != sb->zpos)
+		return sa->zpos - sb->zpos;
+	else
+		return sa->plane->base.id - sb->plane->base.id;
+}
+
+static int omap_atomic_crtc_normalize_zpos(struct drm_crtc *crtc,
+					   struct drm_crtc_state *crtc_state)
+{
+	struct drm_atomic_state *state = crtc_state->state;
+	struct drm_device *dev = crtc->dev;
+	int total_planes = dev->mode_config.num_total_plane;
+	struct drm_plane_state **states;
+	struct drm_plane *plane;
+	int i, inc, n = 0;
+	int ret = 0;
+
+	DRM_DEBUG_ATOMIC("[CRTC:%d:%s] calculating normalized zpos values\n",
+			 crtc->base.id, crtc->name);
+
+	states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL);
+	if (!states)
+		return -ENOMEM;
+
+	/*
+	 * Normalization process might create new states for planes which
+	 * normalized_zpos has to be recalculated.
+	 */
+	drm_for_each_plane_mask(plane, dev, crtc_state->plane_mask) {
+		struct drm_plane_state *plane_state =
+			drm_atomic_get_plane_state(state, plane);
+		if (IS_ERR(plane_state)) {
+			ret = PTR_ERR(plane_state);
+			goto done;
+		}
+		states[n++] = plane_state;
+		DRM_DEBUG_ATOMIC("[PLANE:%d:%s] processing zpos value %d\n",
+				 plane->base.id, plane->name,
+				 plane_state->zpos);
+	}
+
+	sort(states, n, sizeof(*states), omap_atomic_state_zpos_cmp, NULL);
+
+	for (inc = 0, i = 0; i < n; i++) {
+		plane = states[i]->plane;
+
+		states[i]->normalized_zpos = i + inc;
+		DRM_DEBUG_ATOMIC("[PLANE:%d:%s] normalized zpos value %d\n",
+				 plane->base.id, plane->name, i + inc);
+		/*
+		 * If the current plane is virtual it uses 2 hw planes
+		 * therefore the very next zpos is used by the secondary/aux
+		 * plane so we need to skip one zpos from this point on.
+		 */
+		if (is_omap_plane_virtual(plane))
+			inc++;
+	}
+	crtc_state->zpos_changed = true;
+
+done:
+	kfree(states);
+	return ret;
+}
+
+static int omap_atomic_normalize_zpos(struct drm_device *dev,
+				      struct drm_atomic_state *state)
+{
+	struct drm_crtc *crtc;
+	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
+	int i, ret = 0;
+
+	for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
+		if (old_crtc_state->plane_mask != new_crtc_state->plane_mask ||
+		    new_crtc_state->zpos_changed) {
+			ret = omap_atomic_crtc_normalize_zpos(crtc,
+							      new_crtc_state);
+			if (ret)
+				return ret;
+		}
+	}
+	return 0;
+}
+
+static int omap_atomic_check(struct drm_device *dev,
+			     struct drm_atomic_state *state)
+{
+	int ret;
+
+	ret = drm_atomic_helper_check(dev, state);
+	if (ret)
+		return ret;
+
+	if (dev->mode_config.normalize_zpos) {
+		ret = omap_atomic_normalize_zpos(dev, state);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
 static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = {
 	.atomic_commit_tail = omap_atomic_commit_tail,
 };
@@ -123,7 +230,7 @@  static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs =
 static const struct drm_mode_config_funcs omap_mode_config_funcs = {
 	.fb_create = omap_framebuffer_create,
 	.output_poll_changed = drm_fb_helper_output_poll_changed,
-	.atomic_check = drm_atomic_helper_check,
+	.atomic_check = omap_atomic_check,
 	.atomic_commit = drm_atomic_helper_commit,
 };
 
@@ -188,10 +295,9 @@  static int omap_connect_dssdevs(void)
 	return r;
 }
 
-static int omap_modeset_init_properties(struct drm_device *dev)
+static int omap_modeset_init_properties(struct drm_device *dev, u32 num_planes)
 {
 	struct omap_drm_private *priv = dev->dev_private;
-	unsigned int num_planes = priv->dispc_ops->get_num_ovls();
 
 	priv->zorder_prop = drm_property_create_range(dev, 0, "zorder", 0,
 						      num_planes - 1);
@@ -210,10 +316,19 @@  static int omap_modeset_init(struct drm_device *dev)
 	int num_crtcs, crtc_idx, plane_idx;
 	int ret;
 	u32 plane_crtc_mask;
+	struct dispc_plane_mappings plane_mappings = {0};
 
 	drm_mode_config_init(dev);
 
-	ret = omap_modeset_init_properties(dev);
+	ret = priv->dispc_ops->get_plane_mapping(&plane_mappings);
+	if (ret < 0)
+		return ret;
+
+	/* use plane mappings info */
+	if (plane_mappings.num_planes)
+		num_ovls = plane_mappings.num_planes;
+
+	ret = omap_modeset_init_properties(dev, num_ovls);
 	if (ret < 0)
 		return ret;
 
@@ -266,7 +381,7 @@  static int omap_modeset_init(struct drm_device *dev)
 			return -ENOMEM;
 
 		plane = omap_plane_init(dev, plane_idx, DRM_PLANE_TYPE_PRIMARY,
-					plane_crtc_mask);
+					plane_crtc_mask, &plane_mappings);
 		if (IS_ERR(plane))
 			return PTR_ERR(plane);
 
@@ -296,7 +411,7 @@  static int omap_modeset_init(struct drm_device *dev)
 			return -EINVAL;
 
 		plane = omap_plane_init(dev, plane_idx, DRM_PLANE_TYPE_OVERLAY,
-			plane_crtc_mask);
+					plane_crtc_mask, &plane_mappings);
 		if (IS_ERR(plane))
 			return PTR_ERR(plane);
 
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c
index b2539a90e1a4..80b29b7f5696 100644
--- a/drivers/gpu/drm/omapdrm/omap_fb.c
+++ b/drivers/gpu/drm/omapdrm/omap_fb.c
@@ -153,25 +153,27 @@  static uint32_t drm_rotation_to_tiler(unsigned int drm_rot)
 /* update ovl info for scanout, handles cases of multi-planar fb's, etc.
  */
 void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
-		struct drm_plane_state *state, struct omap_overlay_info *info)
+		struct drm_plane_state *state,
+		struct omap_overlay_info *main_info,
+		struct omap_overlay_info *aux_info)
 {
 	struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
 	const struct drm_format_info *format = omap_fb->format;
 	struct plane *plane = &omap_fb->planes[0];
 	uint32_t x, y, orient = 0;
 
-	info->fourcc = fb->format->format;
+	main_info->fourcc = fb->format->format;
 
-	info->pos_x      = state->crtc_x;
-	info->pos_y      = state->crtc_y;
-	info->out_width  = state->crtc_w;
-	info->out_height = state->crtc_h;
-	info->width      = state->src_w >> 16;
-	info->height     = state->src_h >> 16;
+	main_info->pos_x      = state->crtc_x;
+	main_info->pos_y      = state->crtc_y;
+	main_info->out_width  = state->crtc_w;
+	main_info->out_height = state->crtc_h;
+	main_info->width      = state->src_w >> 16;
+	main_info->height     = state->src_h >> 16;
 
 	/* DSS driver wants the w & h in rotated orientation */
 	if (drm_rotation_90_or_270(state->rotation))
-		swap(info->width, info->height);
+		swap(main_info->width, main_info->height);
 
 	x = state->src_x >> 16;
 	y = state->src_y >> 16;
@@ -202,11 +204,12 @@  void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
 
 		/* Note: x and y are in TILER units, not pixels */
 		omap_gem_rotated_dma_addr(plane->bo, orient, x, y,
-					  &info->paddr);
-		info->rotation_type = OMAP_DSS_ROT_TILER;
-		info->rotation = state->rotation ?: DRM_MODE_ROTATE_0;
+					  &main_info->paddr);
+		main_info->rotation_type = OMAP_DSS_ROT_TILER;
+		main_info->rotation = state->rotation ?: DRM_MODE_ROTATE_0;
 		/* Note: stride in TILER units, not pixels */
-		info->screen_width  = omap_gem_tiled_stride(plane->bo, orient);
+		main_info->screen_width  =
+				omap_gem_tiled_stride(plane->bo, orient);
 	} else {
 		switch (state->rotation & DRM_MODE_ROTATE_MASK) {
 		case 0:
@@ -221,27 +224,46 @@  void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
 			break;
 		}
 
-		info->paddr         = get_linear_addr(plane, format, 0, x, y);
-		info->rotation_type = OMAP_DSS_ROT_NONE;
-		info->rotation      = DRM_MODE_ROTATE_0;
-		info->screen_width  = plane->pitch;
+		main_info->paddr = get_linear_addr(plane, format, 0, x, y);
+		main_info->rotation_type = OMAP_DSS_ROT_NONE;
+		main_info->rotation = DRM_MODE_ROTATE_0;
+		main_info->screen_width = plane->pitch;
 	}
 
 	/* convert to pixels: */
-	info->screen_width /= format->cpp[0];
+	main_info->screen_width /= format->cpp[0];
 
 	if (fb->format->format == DRM_FORMAT_NV12) {
 		plane = &omap_fb->planes[1];
 
-		if (info->rotation_type == OMAP_DSS_ROT_TILER) {
+		if (main_info->rotation_type == OMAP_DSS_ROT_TILER) {
 			WARN_ON(!(omap_gem_flags(plane->bo) & OMAP_BO_TILED));
 			omap_gem_rotated_dma_addr(plane->bo, orient, x/2, y/2,
-						  &info->p_uv_addr);
+						  &main_info->p_uv_addr);
 		} else {
-			info->p_uv_addr = get_linear_addr(plane, format, 1, x, y);
+			main_info->p_uv_addr =
+				get_linear_addr(plane, format, 1, x, y);
 		}
 	} else {
-		info->p_uv_addr = 0;
+		main_info->p_uv_addr = 0;
+	}
+
+	if (aux_info) {
+		main_info->width /= 2;
+		main_info->out_width /= 2;
+
+		*aux_info = *main_info;
+
+		aux_info->pos_x = main_info->pos_x + main_info->out_width;
+
+		aux_info->paddr =
+				get_linear_addr(&omap_fb->planes[0], format, 0,
+						x + main_info->width, y);
+		if (fb->format->format == DRM_FORMAT_NV12) {
+			aux_info->p_uv_addr =
+				get_linear_addr(&omap_fb->planes[1], format, 1,
+						x + main_info->width, y);
+		}
 	}
 }
 
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.h b/drivers/gpu/drm/omapdrm/omap_fb.h
index 94ad5f9e4404..f68e81353288 100644
--- a/drivers/gpu/drm/omapdrm/omap_fb.h
+++ b/drivers/gpu/drm/omapdrm/omap_fb.h
@@ -37,7 +37,9 @@  struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
 int omap_framebuffer_pin(struct drm_framebuffer *fb);
 void omap_framebuffer_unpin(struct drm_framebuffer *fb);
 void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
-		struct drm_plane_state *state, struct omap_overlay_info *info);
+		struct drm_plane_state *state,
+		struct omap_overlay_info *main_info,
+		struct omap_overlay_info *aux_info);
 struct drm_connector *omap_framebuffer_get_next_connector(
 		struct drm_framebuffer *fb, struct drm_connector *from);
 bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb);
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c
index 2c19d2239bc5..21c927bbf5a7 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.c
+++ b/drivers/gpu/drm/omapdrm/omap_plane.c
@@ -30,10 +30,14 @@ 
 
 struct omap_plane {
 	struct drm_plane base;
-	enum omap_plane_id id;
+	enum omap_plane_id main_id;
+	enum omap_plane_id aux_id;
 	const char *name;
+	bool virtual_plane;
 };
 
+static const char *plane_id_to_name[];
+
 static int omap_plane_prepare_fb(struct drm_plane *plane,
 				 struct drm_plane_state *new_state)
 {
@@ -56,38 +60,75 @@  static void omap_plane_atomic_update(struct drm_plane *plane,
 	struct omap_drm_private *priv = plane->dev->dev_private;
 	struct omap_plane *omap_plane = to_omap_plane(plane);
 	struct drm_plane_state *state = plane->state;
-	struct omap_overlay_info info;
+	struct omap_overlay_info main_info, aux_info;
 	int ret;
+	bool dual_plane = omap_plane->virtual_plane;
 
 	DBG("%s, crtc=%p fb=%p", omap_plane->name, state->crtc, state->fb);
 
-	memset(&info, 0, sizeof(info));
-	info.rotation_type = OMAP_DSS_ROT_NONE;
-	info.rotation = DRM_MODE_ROTATE_0;
-	info.global_alpha = 0xff;
-	info.zorder = state->normalized_zpos;
+	memset(&main_info, 0, sizeof(main_info));
+	main_info.rotation_type = OMAP_DSS_ROT_NONE;
+	main_info.rotation = DRM_MODE_ROTATE_0;
+	main_info.global_alpha = 0xff;
+	main_info.zorder = state->normalized_zpos;
 
-	/* update scanout: */
-	omap_framebuffer_update_scanout(state->fb, state, &info);
+	aux_info = main_info;
 
-	DBG("%dx%d -> %dx%d (%d)", info.width, info.height,
-			info.out_width, info.out_height,
-			info.screen_width);
-	DBG("%d,%d %pad %pad", info.pos_x, info.pos_y,
-			&info.paddr, &info.p_uv_addr);
+	/* update scanout: */
+	omap_framebuffer_update_scanout(state->fb, state, &main_info,
+					dual_plane ? &aux_info : NULL);
+
+	DBG("%s: %dx%d -> %dx%d (%d)",
+	    plane_id_to_name[omap_plane->main_id],
+	    main_info.width, main_info.height,
+	    main_info.out_width, main_info.out_height,
+	    main_info.screen_width);
+	DBG("%d,%d %pad %pad", main_info.pos_x, main_info.pos_y,
+	    &main_info.paddr, &main_info.p_uv_addr);
+
+	if (dual_plane) {
+		/*
+		 * If the current plane is virtual it uses 2 hw planes
+		 * therefore the very next zorder is used by the aux_id
+		 * plane so we just use the main_id zorder + 1
+		 */
+		aux_info.zorder = main_info.zorder + 1;
+
+		DBG("%s: %dx%d -> %dx%d (%d)",
+		    plane_id_to_name[omap_plane->aux_id],
+		    aux_info.width, aux_info.height,
+		    aux_info.out_width, aux_info.out_height,
+		    aux_info.screen_width);
+		DBG("%d,%d %pad %pad", aux_info.pos_x, aux_info.pos_y,
+		    &aux_info.paddr, &aux_info.p_uv_addr);
+	}
 
-	/* and finally, update omapdss: */
-	ret = priv->dispc_ops->ovl_setup(omap_plane->id, &info,
+	ret = priv->dispc_ops->ovl_setup(omap_plane->main_id, &main_info,
 			      omap_crtc_timings(state->crtc), false,
 			      omap_crtc_channel(state->crtc));
 	if (ret) {
-		dev_err(plane->dev->dev, "Failed to setup plane %s\n",
+		dev_err(plane->dev->dev, "Failed to setup plane1 %s\n",
 			omap_plane->name);
-		priv->dispc_ops->ovl_enable(omap_plane->id, false);
+		priv->dispc_ops->ovl_enable(omap_plane->main_id, false);
 		return;
 	}
 
-	priv->dispc_ops->ovl_enable(omap_plane->id, true);
+	priv->dispc_ops->ovl_enable(omap_plane->main_id, true);
+
+	if (dual_plane) {
+		ret = priv->dispc_ops->ovl_setup(omap_plane->aux_id, &aux_info,
+				      omap_crtc_timings(state->crtc), false,
+				      omap_crtc_channel(state->crtc));
+		if (ret) {
+			dev_err(plane->dev->dev, "Failed to setup plane2 %s\n",
+				omap_plane->name);
+			priv->dispc_ops->ovl_enable(omap_plane->aux_id, false);
+			priv->dispc_ops->ovl_enable(omap_plane->main_id, false);
+			return;
+		}
+
+		priv->dispc_ops->ovl_enable(omap_plane->aux_id, true);
+	}
 }
 
 static void omap_plane_atomic_disable(struct drm_plane *plane,
@@ -95,12 +136,15 @@  static void omap_plane_atomic_disable(struct drm_plane *plane,
 {
 	struct omap_drm_private *priv = plane->dev->dev_private;
 	struct omap_plane *omap_plane = to_omap_plane(plane);
+	bool dual_plane = omap_plane->virtual_plane;
 
 	plane->state->rotation = DRM_MODE_ROTATE_0;
 	plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY
-			   ? 0 : omap_plane->id;
+			   ? 0 : omap_plane->main_id;
 
-	priv->dispc_ops->ovl_enable(omap_plane->id, false);
+	priv->dispc_ops->ovl_enable(omap_plane->main_id, false);
+	if (dual_plane)
+		priv->dispc_ops->ovl_enable(omap_plane->aux_id, false);
 }
 
 static int omap_plane_atomic_check(struct drm_plane *plane,
@@ -158,6 +202,13 @@  static void omap_plane_destroy(struct drm_plane *plane)
 	kfree(omap_plane);
 }
 
+bool is_omap_plane_virtual(struct drm_plane *plane)
+{
+	struct omap_plane *omap_plane = to_omap_plane(plane);
+
+	return omap_plane->virtual_plane;
+}
+
 /* helper to install properties which are common to planes and crtcs */
 void omap_plane_install_properties(struct drm_plane *plane,
 		struct drm_mode_object *obj)
@@ -195,7 +246,7 @@  static void omap_plane_reset(struct drm_plane *plane)
 	 * plane.
 	 */
 	plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY
-			   ? 0 : omap_plane->id;
+			   ? 0 : omap_plane->main_id;
 }
 
 static int omap_plane_atomic_set_property(struct drm_plane *plane,
@@ -246,43 +297,65 @@  static const char *plane_id_to_name[] = {
 	[OMAP_DSS_VIDEO3] = "vid3",
 };
 
-static const enum omap_plane_id plane_idx_to_id[] = {
-	OMAP_DSS_GFX,
-	OMAP_DSS_VIDEO1,
-	OMAP_DSS_VIDEO2,
-	OMAP_DSS_VIDEO3,
+static const char * const virtual_plane_id_to_name[] = {
+	[OMAP_DSS_GFX] = "virt-gfx",
+	[OMAP_DSS_VIDEO1] = "virt-vid1",
+	[OMAP_DSS_VIDEO2] = "virt-vid2",
+	[OMAP_DSS_VIDEO3] = "virt-vid3",
 };
 
-/* initialize plane */
 struct drm_plane *omap_plane_init(struct drm_device *dev,
 		int idx, enum drm_plane_type type,
-		u32 possible_crtcs)
+		u32 possible_crtcs,
+		struct dispc_plane_mappings *plane_mappings)
 {
 	struct omap_drm_private *priv = dev->dev_private;
 	unsigned int num_planes = priv->dispc_ops->get_num_ovls();
 	struct drm_plane *plane;
 	struct omap_plane *omap_plane;
-	enum omap_plane_id id;
 	int ret;
 	u32 nformats;
 	const u32 *formats;
 
-	if (WARN_ON(idx >= ARRAY_SIZE(plane_idx_to_id)))
-		return ERR_PTR(-EINVAL);
-
-	id = plane_idx_to_id[idx];
+	if (plane_mappings->num_planes)
+		num_planes = plane_mappings->num_planes;
 
-	DBG("%s: type=%d", plane_id_to_name[id], type);
+	if (WARN_ON(idx >= num_planes))
+		return ERR_PTR(-EINVAL);
 
 	omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
 	if (!omap_plane)
 		return ERR_PTR(-ENOMEM);
 
-	formats = priv->dispc_ops->ovl_get_color_modes(id);
+	if (plane_mappings->num_planes) {
+		/* Use plane data from DT */
+		omap_plane->main_id = plane_mappings->plane[idx].main_id;
+		if (plane_mappings->plane[idx].is_virtual) {
+			omap_plane->name =
+				virtual_plane_id_to_name[omap_plane->main_id];
+			omap_plane->aux_id = plane_mappings->plane[idx].aux_id;
+			omap_plane->virtual_plane = true;
+		} else {
+			omap_plane->name =
+				plane_id_to_name[omap_plane->main_id];
+		}
+		if (plane_mappings->plane[idx].crtc_mask)
+			possible_crtcs = plane_mappings->plane[idx].crtc_mask;
+	} else {
+		/* Use legacy plane allocation */
+		omap_plane->main_id = idx;
+		omap_plane->name = plane_id_to_name[idx];
+	}
+
+	DBG("%s: type=%d", omap_plane->name, type);
+	DBG("	omap_plane->main_id: %d", omap_plane->main_id);
+	if (omap_plane->virtual_plane)
+		DBG("	omap_plane->aux_id: %d", omap_plane->aux_id);
+	DBG("	crtc_mask: 0x%04x", possible_crtcs);
+
+	formats = priv->dispc_ops->ovl_get_color_modes(omap_plane->main_id);
 	for (nformats = 0; formats[nformats]; ++nformats)
 		;
-	omap_plane->id = id;
-	omap_plane->name = plane_id_to_name[id];
 
 	plane = &omap_plane->base;
 
@@ -301,7 +374,7 @@  struct drm_plane *omap_plane_init(struct drm_device *dev,
 
 error:
 	dev_err(dev->dev, "%s(): could not create plane: %s\n",
-		__func__, plane_id_to_name[id]);
+		__func__, omap_plane->name);
 
 	kfree(omap_plane);
 	return NULL;
diff --git a/drivers/gpu/drm/omapdrm/omap_plane.h b/drivers/gpu/drm/omapdrm/omap_plane.h
index dc5e82ad061d..48815a05f4fe 100644
--- a/drivers/gpu/drm/omapdrm/omap_plane.h
+++ b/drivers/gpu/drm/omapdrm/omap_plane.h
@@ -30,8 +30,10 @@  struct drm_plane;
 
 struct drm_plane *omap_plane_init(struct drm_device *dev,
 		int idx, enum drm_plane_type type,
-		u32 possible_crtcs);
+		u32 possible_crtcs,
+		struct dispc_plane_mappings *plane_mappings);
 void omap_plane_install_properties(struct drm_plane *plane,
 		struct drm_mode_object *obj);
+bool is_omap_plane_virtual(struct drm_plane *plane);
 
 #endif /* __OMAPDRM_PLANE_H__ */