--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated -  http://www.ti.com/
+ * Author: Benoit Parrot <bparrot@ti.com>
+ */
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_plane_helper.h>
+
+#include "omap_dmm_tiler.h"
+#include "omap_drv.h"
+
+/*
+ * overlay funcs
+ */
+static const char * const overlay_id_to_name[] = {
+       [OMAP_DSS_GFX] = "gfx",
+       [OMAP_DSS_VIDEO1] = "vid1",
+       [OMAP_DSS_VIDEO2] = "vid2",
+       [OMAP_DSS_VIDEO3] = "vid3",
+};
+
+static void omap_overlay_destroy(struct omap_hw_overlay *overlay)
+{
+       kfree(overlay);
+}
+
+static struct omap_hw_overlay *omap_overlay_init(enum omap_plane_id overlay_id,
+                                                enum omap_overlay_caps caps)
+{
+       struct omap_hw_overlay *overlay;
+
+       overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
+       if (!overlay)
+               return ERR_PTR(-ENOMEM);
+
+       overlay->name = overlay_id_to_name[overlay_id];
+       overlay->id = overlay_id;
+       overlay->caps = caps;
+
+       return overlay;
+}
+
+int omap_hwoverlays_init(struct omap_drm_private *priv)
+{
+       static const enum omap_plane_id hw_plane_ids[] = {
+                       OMAP_DSS_GFX, OMAP_DSS_VIDEO1,
+                       OMAP_DSS_VIDEO2, OMAP_DSS_VIDEO3,
+       };
+       u32 num_overlays = dispc_get_num_ovls(priv->dispc);
+       enum omap_overlay_caps caps;
+       int i, ret;
+
+       for (i = 0; i < num_overlays; i++) {
+               struct omap_hw_overlay *overlay;
+
+               caps = dispc_ovl_get_caps(priv->dispc, hw_plane_ids[i]);
+               overlay = omap_overlay_init(hw_plane_ids[i], caps);
+               if (IS_ERR(overlay)) {
+                       ret = PTR_ERR(overlay);
+                       dev_err(priv->dev, "failed to construct overlay for %s (%d)\n",
+                               overlay_id_to_name[i], ret);
+                       omap_hwoverlays_destroy(priv);
+                       return ret;
+               }
+               overlay->idx = priv->num_ovls;
+               priv->overlays[priv->num_ovls++] = overlay;
+       }
+
+       return 0;
+}
+
+void omap_hwoverlays_destroy(struct omap_drm_private *priv)
+{
+       int i;
+
+       for (i = 0; i < priv->num_ovls; i++) {
+               omap_overlay_destroy(priv->overlays[i]);
+               priv->overlays[i] = NULL;
+       }
+
+       priv->num_ovls = 0;
+}
 
 struct omap_plane {
        struct drm_plane base;
        enum omap_plane_id id;
-       const char *name;
+
+       struct omap_hw_overlay *overlay;
 };
 
 static int omap_plane_prepare_fb(struct drm_plane *plane,
        struct omap_plane *omap_plane = to_omap_plane(plane);
        struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
                                                                           plane);
+       enum omap_plane_id ovl_id = omap_plane->overlay->id;
        struct omap_overlay_info info;
        int ret;
 
-       DBG("%s, crtc=%p fb=%p", omap_plane->name, new_state->crtc,
+       DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc,
            new_state->fb);
 
        memset(&info, 0, sizeof(info));
                        &info.paddr, &info.p_uv_addr);
 
        /* and finally, update omapdss: */
-       ret = dispc_ovl_setup(priv->dispc, omap_plane->id, &info,
+       ret = dispc_ovl_setup(priv->dispc, ovl_id, &info,
                              omap_crtc_timings(new_state->crtc), false,
                              omap_crtc_channel(new_state->crtc));
        if (ret) {
                dev_err(plane->dev->dev, "Failed to setup plane %s\n",
-                       omap_plane->name);
-               dispc_ovl_enable(priv->dispc, omap_plane->id, false);
+                       plane->name);
+               dispc_ovl_enable(priv->dispc, ovl_id, false);
                return;
        }
 
-       dispc_ovl_enable(priv->dispc, omap_plane->id, true);
+       dispc_ovl_enable(priv->dispc, ovl_id, true);
 }
 
 static void omap_plane_atomic_disable(struct drm_plane *plane,
                                                                           plane);
        struct omap_drm_private *priv = plane->dev->dev_private;
        struct omap_plane *omap_plane = to_omap_plane(plane);
+       enum omap_plane_id ovl_id = omap_plane->overlay->id;
 
        new_state->rotation = DRM_MODE_ROTATE_0;
        new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id;
 
-       dispc_ovl_enable(priv->dispc, omap_plane->id, false);
+       dispc_ovl_enable(priv->dispc, ovl_id, false);
 }
 
 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
 {
        struct omap_plane *omap_plane = to_omap_plane(plane);
 
-       DBG("%s", omap_plane->name);
+       DBG("%s", plane->name);
 
        drm_plane_cleanup(plane);
 
        return false;
 }
 
-static const char *plane_id_to_name[] = {
-       [OMAP_DSS_GFX] = "gfx",
-       [OMAP_DSS_VIDEO1] = "vid1",
-       [OMAP_DSS_VIDEO2] = "vid2",
-       [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,
-};
-
 /* initialize plane */
 struct drm_plane *omap_plane_init(struct drm_device *dev,
                int idx, enum drm_plane_type type,
        unsigned int num_planes = dispc_get_num_ovls(priv->dispc);
        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)))
+       if (WARN_ON(idx >= num_planes))
                return ERR_PTR(-EINVAL);
 
-       id = plane_idx_to_id[idx];
-
-       DBG("%s: type=%d", plane_id_to_name[id], type);
-
        omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
        if (!omap_plane)
                return ERR_PTR(-ENOMEM);
 
-       formats = dispc_ovl_get_color_modes(priv->dispc, id);
+       omap_plane->id = idx;
+       omap_plane->overlay = priv->overlays[idx];
+
+       DBG("%d: type=%d", omap_plane->id, type);
+       DBG("   crtc_mask: 0x%04x", possible_crtcs);
+
+       formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->overlay->id);
        for (nformats = 0; formats[nformats]; ++nformats)
                ;
-       omap_plane->id = id;
-       omap_plane->name = plane_id_to_name[id];
 
        plane = &omap_plane->base;
 
        return plane;
 
 error:
-       dev_err(dev->dev, "%s(): could not create plane: %s\n",
-               __func__, plane_id_to_name[id]);
+       dev_err(dev->dev, "%s(): could not create plane: %d\n",
+               __func__, omap_plane->id);
 
        kfree(omap_plane);
        return NULL;