dispc_runtime_put(priv->dispc);
 }
 
+static int drm_atomic_state_normalized_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->normalized_zpos != sb->normalized_zpos)
+               return sa->normalized_zpos - sb->normalized_zpos;
+       else
+               return sa->plane->base.id - sb->plane->base.id;
+}
+
+/*
+ * This replaces the drm_atomic_normalize_zpos to handle the dual overlay case.
+ *
+ * Since both halves need to be 'appear' side by side the zpos is
+ * recalculated when dealing with dual overlay cases so that the other
+ * planes zpos is consistent.
+ */
+static int omap_atomic_update_normalize_zpos(struct drm_device *dev,
+                                            struct drm_atomic_state *state)
+{
+       struct drm_crtc *crtc;
+       struct drm_crtc_state *old_state, *new_state;
+       struct drm_plane *plane;
+       int c, i, n, inc;
+       int total_planes = dev->mode_config.num_total_plane;
+       struct drm_plane_state **states;
+       int ret = 0;
+
+       states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL);
+       if (!states)
+               return -ENOMEM;
+
+       for_each_oldnew_crtc_in_state(state, crtc, old_state, new_state, c) {
+               if (old_state->plane_mask == new_state->plane_mask &&
+                   !new_state->zpos_changed)
+                       continue;
+
+               /* Reset plane increment and index value for every crtc */
+               n = 0;
+
+               /*
+                * Normalization process might create new states for planes
+                * which normalized_zpos has to be recalculated.
+                */
+               drm_for_each_plane_mask(plane, dev, new_state->plane_mask) {
+                       struct drm_plane_state *plane_state =
+                               drm_atomic_get_plane_state(new_state->state,
+                                                          plane);
+                       if (IS_ERR(plane_state)) {
+                               ret = PTR_ERR(plane_state);
+                               goto done;
+                       }
+                       states[n++] = plane_state;
+               }
+
+               sort(states, n, sizeof(*states),
+                    drm_atomic_state_normalized_zpos_cmp, NULL);
+
+               for (i = 0, inc = 0; i < n; i++) {
+                       plane = states[i]->plane;
+
+                       states[i]->normalized_zpos = i + inc;
+                       DRM_DEBUG_ATOMIC("[PLANE:%d:%s] updated normalized zpos value %d\n",
+                                        plane->base.id, plane->name,
+                                        states[i]->normalized_zpos);
+
+                       if (is_omap_plane_dual_overlay(states[i]))
+                               inc++;
+               }
+               new_state->zpos_changed = true;
+       }
+
+done:
+       kfree(states);
+       return ret;
+}
+
+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_update_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,
 };
 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,
 };
 
 
        struct drm_plane_state base;
 
        struct omap_hw_overlay *overlay;
+       struct omap_hw_overlay *r_overlay;  /* right overlay */
 };
 
 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
        enum omap_plane_id id;
 };
 
+bool is_omap_plane_dual_overlay(struct drm_plane_state *state)
+{
+       struct omap_plane_state *omap_state = to_omap_plane_state(state);
+
+       return !!omap_state->r_overlay;
+}
+
 static int omap_plane_prepare_fb(struct drm_plane *plane,
                                 struct drm_plane_state *new_state)
 {
                                                                           plane);
        struct omap_plane_state *new_omap_state;
        struct omap_plane_state *old_omap_state;
-       struct omap_overlay_info info;
-       enum omap_plane_id ovl_id;
+       struct omap_overlay_info info, r_info;
+       enum omap_plane_id ovl_id, r_ovl_id;
        int ret;
+       bool dual_ovl;
 
        new_omap_state = to_omap_plane_state(new_state);
        old_omap_state = to_omap_plane_state(old_state);
 
+       dual_ovl = is_omap_plane_dual_overlay(new_state);
+
        /* Cleanup previously held overlay if needed */
        if (old_omap_state->overlay)
                omap_overlay_update_state(priv, old_omap_state->overlay);
+       if (old_omap_state->r_overlay)
+               omap_overlay_update_state(priv, old_omap_state->r_overlay);
 
        if (!new_omap_state->overlay) {
                DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name);
        info.color_encoding = new_state->color_encoding;
        info.color_range = new_state->color_range;
 
+       r_info = info;
+
        /* update scanout: */
-       omap_framebuffer_update_scanout(new_state->fb, new_state, &info);
+       omap_framebuffer_update_scanout(new_state->fb, new_state, &info,
+                                       dual_ovl ? &r_info : NULL);
 
        DBG("%s: %dx%d -> %dx%d (%d)",
                        new_omap_state->overlay->name, info.width, info.height,
        DBG("%d,%d %pad %pad", info.pos_x, info.pos_y,
                        &info.paddr, &info.p_uv_addr);
 
+       if (dual_ovl) {
+               r_ovl_id = new_omap_state->r_overlay->id;
+               /*
+                * If the current plane uses 2 hw planes the very next
+                * zorder is used by the r_overlay so we just use the
+                * main overlay zorder + 1
+                */
+               r_info.zorder = info.zorder + 1;
+
+               DBG("%s: %dx%d -> %dx%d (%d)",
+                   new_omap_state->r_overlay->name,
+                   r_info.width, r_info.height,
+                   r_info.out_width, r_info.out_height, r_info.screen_width);
+               DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y,
+                   &r_info.paddr, &r_info.p_uv_addr);
+       }
+
        /* and finally, update omapdss: */
        ret = dispc_ovl_setup(priv->dispc, ovl_id, &info,
                              omap_crtc_timings(new_state->crtc), false,
        }
 
        dispc_ovl_enable(priv->dispc, ovl_id, true);
+
+       if (dual_ovl) {
+               ret = dispc_ovl_setup(priv->dispc, r_ovl_id, &r_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 right-overlay %s\n",
+                               plane->name);
+                       dispc_ovl_enable(priv->dispc, r_ovl_id, false);
+                       dispc_ovl_enable(priv->dispc, ovl_id, false);
+                       return;
+               }
+
+               dispc_ovl_enable(priv->dispc, r_ovl_id, true);
+       }
 }
 
 static void omap_plane_atomic_disable(struct drm_plane *plane,
 
        omap_overlay_update_state(priv, old_omap_state->overlay);
        new_omap_state->overlay = NULL;
+
+       if (is_omap_plane_dual_overlay(old_state)) {
+               omap_overlay_update_state(priv, old_omap_state->r_overlay);
+               new_omap_state->r_overlay = NULL;
+       }
 }
 
 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
        struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state);
        struct omap_global_state *omap_overlay_global_state;
        struct drm_crtc_state *crtc_state;
+       bool new_r_hw_overlay = false;
        bool new_hw_overlay = false;
        u32 max_width, max_height;
        struct drm_crtc *crtc;
 
        if (!new_plane_state->visible) {
                omap_overlay_release(state, omap_state->overlay);
+               omap_overlay_release(state, omap_state->r_overlay);
                omap_state->overlay = NULL;
+               omap_state->r_overlay = NULL;
                return 0;
        }
 
        if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height)
                return -EINVAL;
 
-       if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width)
-               return -EINVAL;
+
+       if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width) {
+               bool is_fourcc_yuv = new_plane_state->fb->format->is_yuv;
+
+               if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) ||
+                                     new_plane_state->crtc_w / 2 & 1)) {
+                       /*
+                        * When calculating the split overlay width
+                        * and it yield an odd value we will need to adjust
+                        * the indivual width +/- 1. So make sure it fits
+                        */
+                       if (new_plane_state->src_w <= ((2 * width - 1) << 16) &&
+                           new_plane_state->crtc_w <= (2 * width - 1))
+                               new_r_hw_overlay = true;
+                       else
+                               return -EINVAL;
+               } else {
+                       if (new_plane_state->src_w <= (2 * max_width) &&
+                           new_plane_state->crtc_w <= (2 * width))
+                               new_r_hw_overlay = true;
+                       else
+                               return -EINVAL;
+               }
+       }
 
        if (new_plane_state->rotation != DRM_MODE_ROTATE_0 &&
            !omap_framebuffer_supports_rotation(new_plane_state->fb))
                        new_hw_overlay = true;
        }
 
+       /*
+        * check if we need two overlays and only have 1 or
+        * if we had 2 overlays but will only need 1
+        */
+       if ((new_r_hw_overlay && !omap_state->r_overlay) ||
+           (!new_r_hw_overlay && omap_state->r_overlay))
+               new_hw_overlay = true;
+
        if (new_hw_overlay) {
                struct omap_hw_overlay *old_ovl = omap_state->overlay;
+               struct omap_hw_overlay *old_r_ovl = omap_state->r_overlay;
                struct omap_hw_overlay *new_ovl = NULL;
+               struct omap_hw_overlay *new_r_ovl = NULL;
 
                omap_overlay_release(state, old_ovl);
+               omap_overlay_release(state, old_r_ovl);
 
-               ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl);
+               ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl,
+                                         new_r_hw_overlay ? &new_r_ovl : NULL);
                if (ret) {
                        DBG("%s: failed to assign hw_overlay", plane->name);
                        omap_state->overlay = NULL;
+                       omap_state->r_overlay = NULL;
                        return ret;
                }
 
                omap_state->overlay = new_ovl;
+               if (new_r_hw_overlay)
+                       omap_state->r_overlay = new_r_ovl;
+               else
+                       omap_state->r_overlay = NULL;
        }
 
        DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id);
 
+       if (omap_state->r_overlay)
+               DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id);
+
        return 0;
 }
 
        __drm_atomic_helper_plane_duplicate_state(plane, &state->base);
 
        state->overlay = current_state->overlay;
+       state->r_overlay = current_state->r_overlay;
 
        return &state->base;
 }
                           omap_state->overlay->caps);
        else
                drm_printf(p, "\toverlay=None\n");
+       if (omap_state->r_overlay)
+               drm_printf(p, "\tr_overlay=%s (caps=0x%x)\n",
+                          omap_state->r_overlay->name,
+                          omap_state->r_overlay->caps);
+       else
+               drm_printf(p, "\tr_overlay=None\n");
 }
 
 static int omap_plane_atomic_set_property(struct drm_plane *plane,