|
|
@@ -32,6 +32,7 @@ struct omap_plane_state {
|
|
|
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)
|
|
|
@@ -42,6 +43,13 @@ struct omap_plane {
|
|
|
const char *name;
|
|
|
};
|
|
|
|
|
|
+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)
|
|
|
{
|
|
|
@@ -66,15 +74,20 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
|
|
|
struct drm_plane_state *state = plane->state;
|
|
|
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(state);
|
|
|
old_omap_state = to_omap_plane_state(old_state);
|
|
|
|
|
|
+ dual_ovl = is_omap_plane_dual_overlay(state);
|
|
|
+
|
|
|
/* Cleanup previously held overlay if needed */
|
|
|
omap_overlay_disable(old_state->state, plane, old_omap_state->overlay);
|
|
|
+ omap_overlay_disable(old_state->state, plane,
|
|
|
+ old_omap_state->r_overlay);
|
|
|
|
|
|
if (!new_omap_state->overlay) {
|
|
|
DBG("[PLANE:%d:%s] overlay_id: ??? (%p)", plane->base.id, plane->name,
|
|
|
@@ -93,8 +106,11 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
|
|
|
info.global_alpha = 0xff;
|
|
|
info.zorder = state->normalized_zpos;
|
|
|
|
|
|
+ r_info = info;
|
|
|
+
|
|
|
/* update scanout: */
|
|
|
- omap_framebuffer_update_scanout(state->fb, state, &info);
|
|
|
+ omap_framebuffer_update_scanout(state->fb, state, &info,
|
|
|
+ dual_ovl ? &r_info : NULL);
|
|
|
|
|
|
DBG("%s: %dx%d -> %dx%d (%d)",
|
|
|
new_omap_state->overlay->name, info.width, info.height,
|
|
|
@@ -102,18 +118,50 @@ static void omap_plane_atomic_update(struct drm_plane *plane,
|
|
|
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->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 = priv->dispc_ops->ovl_setup(priv->dispc, ovl_id, &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(priv->dispc, ovl_id, false);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
priv->dispc_ops->ovl_enable(priv->dispc, ovl_id, true);
|
|
|
+
|
|
|
+ if (dual_ovl) {
|
|
|
+ ret = priv->dispc_ops->ovl_setup(priv->dispc, r_ovl_id, &r_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(priv->dispc, r_ovl_id, false);
|
|
|
+ priv->dispc_ops->ovl_enable(priv->dispc, ovl_id, false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ priv->dispc_ops->ovl_enable(priv->dispc, r_ovl_id, true);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void omap_plane_atomic_disable(struct drm_plane *plane,
|
|
|
@@ -135,6 +183,11 @@ static void omap_plane_atomic_disable(struct drm_plane *plane,
|
|
|
|
|
|
omap_overlay_disable(old_state->state, plane, old_omap_state->overlay);
|
|
|
new_omap_state->overlay = NULL;
|
|
|
+ if (is_omap_plane_dual_overlay(old_state)) {
|
|
|
+ omap_overlay_disable(old_state->state, plane,
|
|
|
+ old_omap_state->r_overlay);
|
|
|
+ new_omap_state->r_overlay = NULL;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#define FRAC_16_16(mult, div) (((mult) << 16) / (div))
|
|
|
@@ -153,6 +206,8 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
|
|
|
u32 fourcc;
|
|
|
u32 caps = 0;
|
|
|
bool new_hw_overlay = false;
|
|
|
+ bool new_r_hw_overlay = false;
|
|
|
+ bool is_fourcc_yuv = false;
|
|
|
int min_scale, max_scale;
|
|
|
int ret;
|
|
|
|
|
|
@@ -179,8 +234,31 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
|
|
|
if (state->src_h > height_fp || state->crtc_h > height)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (state->src_w > width_fp || state->crtc_w > width)
|
|
|
- return -EINVAL;
|
|
|
+ if (state->fb)
|
|
|
+ is_fourcc_yuv = state->fb->format->is_yuv;
|
|
|
+
|
|
|
+ if (state->src_w > width_fp || state->crtc_w > width) {
|
|
|
+ if (is_fourcc_yuv &&
|
|
|
+ (((state->src_w >> 16) / 2 & 1) ||
|
|
|
+ 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 (state->src_w <= ((2 * width - 1) << 16) &&
|
|
|
+ state->crtc_w <= (2 * width - 1))
|
|
|
+ new_r_hw_overlay = true;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+ } else {
|
|
|
+ if (state->src_w <= (2 * width_fp) &&
|
|
|
+ state->crtc_w <= (2 * width))
|
|
|
+ new_r_hw_overlay = true;
|
|
|
+ else
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
min_scale = FRAC_16_16(1, 4);
|
|
|
max_scale = FRAC_16_16(8, 1);
|
|
|
@@ -224,33 +302,57 @@ static int omap_plane_atomic_check(struct drm_plane *plane,
|
|
|
fourcc))
|
|
|
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->state, plane, old_ovl);
|
|
|
+ omap_overlay_release(state->state, plane, old_r_ovl);
|
|
|
|
|
|
ret = omap_overlay_assign(state->state, plane, caps,
|
|
|
- fourcc, crtc_mask, &new_ovl);
|
|
|
+ fourcc, crtc_mask, &new_ovl,
|
|
|
+ new_r_hw_overlay ?
|
|
|
+ &new_r_ovl : NULL);
|
|
|
if (ret) {
|
|
|
DBG("%s: failed to assign hw_overlay(s)!",
|
|
|
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;
|
|
|
}
|
|
|
} else {
|
|
|
omap_overlay_release(state->state, plane, omap_state->overlay);
|
|
|
+ omap_overlay_release(state->state, plane,
|
|
|
+ omap_state->r_overlay);
|
|
|
omap_state->overlay = NULL;
|
|
|
+ omap_state->r_overlay = NULL;
|
|
|
}
|
|
|
|
|
|
if (omap_state->overlay)
|
|
|
DBG("plane: %s overlay_id: %d", plane->name,
|
|
|
omap_state->overlay->overlay_id);
|
|
|
+ if (omap_state->r_overlay)
|
|
|
+ DBG("plane: %s r_overlay_id: %d", plane->name,
|
|
|
+ omap_state->r_overlay->overlay_id);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
@@ -363,6 +465,18 @@ static void omap_plane_atomic_print_state(struct drm_printer *p,
|
|
|
drm_printf(p, "\t\tpossible_crtcs=0x%x\n",
|
|
|
omap_state->overlay->possible_crtcs);
|
|
|
}
|
|
|
+
|
|
|
+ drm_printf(p, "\tr_overlay=%s\n", omap_state->r_overlay ?
|
|
|
+ omap_state->r_overlay->name :
|
|
|
+ "(null)");
|
|
|
+ if (omap_state->r_overlay) {
|
|
|
+ drm_printf(p, "\t\tidx=%d\n", omap_state->r_overlay->idx);
|
|
|
+ drm_printf(p, "\t\toverlay_id=%d\n",
|
|
|
+ omap_state->r_overlay->overlay_id);
|
|
|
+ drm_printf(p, "\t\tcaps=0x%x\n", omap_state->r_overlay->caps);
|
|
|
+ drm_printf(p, "\t\tpossible_crtcs=0x%x\n",
|
|
|
+ omap_state->r_overlay->possible_crtcs);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static int omap_plane_atomic_set_property(struct drm_plane *plane,
|