diff mbox

[4/5] drm/i915: Make primary/sprite zpos configurable on VLV/CHV

Message ID 20180529182804.8571-4-ville.syrjala@linux.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ville Syrjälä May 29, 2018, 6:28 p.m. UTC
From: Ville Syrjälä <ville.syrjala@linux.intel.com>

VLV/CHV can change the z order between the primary and sprite planes
freely. Let's expose that capability by making the zpos property
mutable. The cursor plane is always on top, so the cursor zpos is
left as an immutable property.

The way the hardware is configured is a bit awkward. Both sprite planes
have a "bottom" and "zorder" bits, but the way they interact betweens
the sprites doesn't seem entirely logical. The spec doesn't even list
all 16 possible combinations (just 10 are listed), but I've gone through
them all the discovered the following relationship:

sprite 0	sprite 1	resulting plane order (bottom to top)
B Z		B Z
0 0		0 0		P 0 1	  d0(see note),d1,d01,d01p
0 0		0 1		P 0 1	*
0 0		1 0		1 P 0	* d0
0 0		1 1		1 P 0
0 1		0 0		P 1 0	*
0 1		0 1		P 0 1
0 1		1 0		1 0 P	* dp
0 1		1 1		1 0 P
1 0		0 0		0 P 1	* d1
1 0		0 1		0 1 P	* dp
1 0		1 0		P 0 1
1 0		1 1		P 0 1
1 1		0 0		0 P 1
1 1		0 1		0 1 P
1 1		1 0		P 0 1
1 1		1 1		P 0 1

[B=bottom bit Z=zorder bit, 0=sprite 0, 1=sprite 1, P=primary,
 d0=sprite 0 disabled, d1=sprite 1 disabled, dp=primary disabled,
 d01=both sprites, d01p=both sprites and primary disabled]

Disabling any one or two planes doesn't seem to interfere with the
resulting plane order in an unexpected way. The bottom and zorder
bits of the disabled planes are still effective though, so we must be
careful what we leave in those bits when the plane is disabled.

What we do for enabled sprite planes is set bottom=1 if the plane is
at the bottom, else we set zorder=1 if the plane is just above
the other sprite plane. This will end up picking the configurations
marked with * from the table when both sprite planes are enabled.

For the disabled plane cases we just leave bottom=0 and zorder=0
for any disabled sprite plane. Any enabled sprite plane will still
follow the rule laid out before. For the purposes of that rule,
we'll consider any disabled plane to be stacked on top of the
enabled planes. This will end up picking the configurations marked
d0 when sprite 0 is disabled, d1 when sprite 1 is disabled,
dp when the primary is disabled, and d01 when both sprites are
disabled (and d01p is actually the same thing with just primary
disabled as well).

The use of the 'P 0 1' configuration for the first d0 case doesn't
really make sense based on the above, but given the way the zorder
bits work, there's no nice way to pick the 'P 1 0' (which would be
the logical thing) when sprite 0 is disabled as that would require
setting the zorder bit in the sprite 0 control register, but as
stated we always set both bits to 0 for disabled planes. That way
we don't have to reconfigure already disabled planes to change the
zorder between the currently active planes, and more imporantly we
don't need to consult any plane/crtc state in the .disable_plane()
hook. This slight irregularity doesn't matter in the end as both
of these configuratios will give us the expected visible 'P 1'
result.

v2: Redo the final zpos computation to not require every plane
    on the crtc to be part of the state
v3: More polish

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
---
 drivers/gpu/drm/i915/i915_reg.h           |   2 +
 drivers/gpu/drm/i915/intel_atomic_plane.c |   8 ++
 drivers/gpu/drm/i915/intel_display.c      | 146 +++++++++++++++++++++++++++++-
 drivers/gpu/drm/i915/intel_display.h      |   9 ++
 drivers/gpu/drm/i915/intel_drv.h          |   7 ++
 drivers/gpu/drm/i915/intel_sprite.c       |  24 ++++-
 6 files changed, 192 insertions(+), 4 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index f238b7b33cd9..44eac5ffc2ce 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -6265,6 +6265,8 @@  enum {
 #define   SP_ROTATE_180			(1<<15)
 #define   SP_TILED			(1<<10)
 #define   SP_MIRROR			(1<<8) /* CHV pipe B */
+#define   SP_BOTTOM			(1<<2)
+#define   SP_ZORDER			(1<<0)
 #define _SPALINOFF		(VLV_DISPLAY_BASE + 0x72184)
 #define _SPASTRIDE		(VLV_DISPLAY_BASE + 0x72188)
 #define _SPAPOS			(VLV_DISPLAY_BASE + 0x7218c)
diff --git a/drivers/gpu/drm/i915/intel_atomic_plane.c b/drivers/gpu/drm/i915/intel_atomic_plane.c
index 6d068786eb41..a5a3123589e7 100644
--- a/drivers/gpu/drm/i915/intel_atomic_plane.c
+++ b/drivers/gpu/drm/i915/intel_atomic_plane.c
@@ -193,6 +193,14 @@  int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_
 	else
 		crtc_state->nv12_planes &= ~BIT(intel_plane->id);
 
+	/*
+	 * Copy over the zpos to the crtc state so that we don't
+	 * need to add every plane on the crtc to the state to
+	 * recompute the final zpos.
+	 */
+	crtc_state->raw_zpos[intel_plane->id] =
+		intel_plane_raw_zpos(crtc_state, intel_state);
+
 	return intel_plane_atomic_calc_changes(old_crtc_state,
 					       &crtc_state->base,
 					       old_plane_state,
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index bc3de1b5bb89..111cb0c1da54 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -30,6 +30,7 @@ 
 #include <linux/i2c.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/sort.h>
 #include <linux/vgaarb.h>
 #include <drm/drm_edid.h>
 #include <drm/drmP.h>
@@ -2760,6 +2761,8 @@  intel_set_plane_visible(struct intel_crtc_state *crtc_state,
 		crtc_state->active_planes &= ~BIT(plane->id);
 	}
 
+	crtc_state->raw_zpos[plane->id] = intel_plane_raw_zpos(crtc_state, plane_state);
+
 	DRM_DEBUG_KMS("%s active planes 0x%x\n",
 		      crtc_state->base.crtc->name,
 		      crtc_state->active_planes);
@@ -3208,8 +3211,8 @@  int skl_check_plane_surface(const struct intel_crtc_state *crtc_state,
 static u32 i9xx_plane_ctl(const struct intel_crtc_state *crtc_state,
 			  const struct intel_plane_state *plane_state)
 {
-	struct drm_i915_private *dev_priv =
-		to_i915(plane_state->base.plane->dev);
+	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
+	struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
 	const struct drm_framebuffer *fb = plane_state->base.fb;
 	unsigned int rotation = plane_state->base.rotation;
@@ -10932,6 +10935,7 @@  clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
 	struct intel_dpll_hw_state dpll_hw_state;
 	struct intel_shared_dpll *shared_dpll;
 	struct intel_crtc_wm_state wm_state;
+	u8 raw_zpos[I915_MAX_PLANES];
 	bool force_thru, ips_force_disable;
 
 	/* FIXME: before the switch to atomic started, a new pipe_config was
@@ -10947,6 +10951,7 @@  clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
 	if (IS_G4X(dev_priv) ||
 	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
 		wm_state = crtc_state->wm;
+	memcpy(raw_zpos, crtc_state->raw_zpos, sizeof(raw_zpos));
 
 	/* Keep base drm_crtc_state intact, only clear our extended struct */
 	BUILD_BUG_ON(offsetof(struct intel_crtc_state, base));
@@ -10961,6 +10966,7 @@  clear_intel_crtc_state(struct intel_crtc_state *crtc_state)
 	if (IS_G4X(dev_priv) ||
 	    IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
 		crtc_state->wm = wm_state;
+	memcpy(crtc_state->raw_zpos, raw_zpos, sizeof(raw_zpos));
 }
 
 static int
@@ -12161,6 +12167,130 @@  static int calc_watermark_data(struct drm_atomic_state *state)
 	return 0;
 }
 
+int intel_plane_raw_zpos(const struct intel_crtc_state *crtc_state,
+			 const struct intel_plane_state *plane_state)
+{
+	struct intel_plane *plane = to_intel_plane(plane_state->base.plane);
+
+	/*
+	 * We'll keep the cursor always at the top regardless of
+	 * whether it's enabled or not. This avoids changes to the
+	 * sorted zpos of other planes when turning the cursor on/off,
+	 * and thus we can easily avoid having to add the other
+	 * planes to the atomic state.
+	 */
+	if (plane->id == PLANE_CURSOR)
+		return 0x20;
+
+	/*
+	 * For other planes we stack the disabled planes on top
+	 * of any enabled planes. This simplifies our life by
+	 * allowing us to keep the SP_BOTTOM and SP_ZORDER bits
+	 * cleared for any disabled planes on VLV/CHV. Thus we
+	 * don't have to worry about zpos in .disable_plane().
+	 */
+	if (plane_state->base.visible)
+		return plane_state->base.zpos;
+	else
+		return 0x10;
+}
+
+struct intel_plane_zpos {
+	u32 obj_id;
+	enum plane_id plane_id;
+	u8 zpos;
+};
+
+static int intel_zpos_cmp(const void *a, const void *b)
+{
+	const struct intel_plane_zpos *za = (const struct intel_plane_zpos *)a;
+	const struct intel_plane_zpos *zb = (const struct intel_plane_zpos *)b;
+
+	if (za->zpos != zb->zpos)
+		return za->zpos - zb->zpos;
+	else
+		return za->obj_id - zb->obj_id;
+}
+
+static void vlv_normalize_zpos(struct intel_crtc_state *crtc_state)
+{
+	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+	struct intel_plane_zpos zpos[I915_MAX_PLANES] = {};
+	struct intel_plane *plane;
+	int i, num_planes;
+
+	i = 0;
+	for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) {
+		zpos[i].obj_id = plane->base.base.id;
+		zpos[i].plane_id = plane->id;
+		zpos[i].zpos = crtc_state->raw_zpos[plane->id];
+		i++;
+	}
+
+	num_planes = i;
+
+	/* sort according to zpos/plane obj id */
+	sort(zpos, num_planes, sizeof(zpos[0]),
+	     intel_zpos_cmp, NULL);
+
+	/* copy over sorted zpos */
+	for (i = 0; i < num_planes; i++) {
+		enum plane_id plane_id = zpos[i].plane_id;
+
+		crtc_state->zpos[plane_id] = i;
+	}
+}
+
+static int vlv_update_zpos(struct drm_i915_private *dev_priv,
+			   struct intel_atomic_state *state)
+{
+	struct intel_crtc *crtc;
+	struct intel_crtc_state *old_crtc_state, *new_crtc_state;
+	int i;
+
+	for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) {
+		struct drm_plane *plane;
+		bool sprites_changed;
+
+		vlv_normalize_zpos(new_crtc_state);
+
+		sprites_changed =
+			memcmp(new_crtc_state->zpos,
+			       old_crtc_state->zpos,
+			       sizeof(new_crtc_state->zpos)) != 0;
+
+		if (!sprites_changed)
+			continue;
+
+		drm_for_each_plane_mask(plane, &dev_priv->drm,
+					new_crtc_state->base.plane_mask) {
+			struct drm_plane_state *plane_state;
+			enum plane_id plane_id = to_intel_plane(plane)->id;
+
+			if (plane_id == PLANE_CURSOR ||
+			    plane_id == PLANE_PRIMARY)
+				continue;
+
+			/*
+			 * zpos is configured via sprite registers, so must
+			 * add them to the state if anything significant
+			 * changed.
+			 */
+			plane_state = drm_atomic_get_plane_state(&state->base, plane);
+			if (IS_ERR(plane_state))
+				return PTR_ERR(plane_state);
+		}
+
+		DRM_DEBUG_KMS("pipe %c zpos sprite %c: %d, sprite %c: %d\n",
+			      pipe_name(crtc->pipe),
+			      sprite_name(crtc->pipe, 0), new_crtc_state->zpos[PLANE_SPRITE0],
+			      sprite_name(crtc->pipe, 1), new_crtc_state->zpos[PLANE_SPRITE1]);
+	}
+
+	return 0;
+}
+
 /**
  * intel_atomic_check - validate state object
  * @dev: drm device
@@ -12236,7 +12366,14 @@  static int intel_atomic_check(struct drm_device *dev,
 	if (ret)
 		return ret;
 
+	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) {
+		ret = vlv_update_zpos(dev_priv, intel_state);
+		if (ret)
+			return ret;
+	}
+
 	intel_fbc_choose_crtc(dev_priv, intel_state);
+
 	return calc_watermark_data(state);
 }
 
@@ -13592,7 +13729,10 @@  intel_primary_plane_create(struct drm_i915_private *dev_priv, enum pipe pipe)
 						  DRM_COLOR_YCBCR_LIMITED_RANGE);
 
 	zpos = 0;
-	drm_plane_create_zpos_immutable_property(&primary->base, zpos);
+	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+		drm_plane_create_zpos_property(&primary->base, zpos, 0, 2);
+	else
+		drm_plane_create_zpos_immutable_property(&primary->base, zpos);
 
 	drm_plane_helper_add(&primary->base, &intel_plane_helper_funcs);
 
diff --git a/drivers/gpu/drm/i915/intel_display.h b/drivers/gpu/drm/i915/intel_display.h
index 2ef31617614a..09bbd9a3efd6 100644
--- a/drivers/gpu/drm/i915/intel_display.h
+++ b/drivers/gpu/drm/i915/intel_display.h
@@ -333,6 +333,15 @@  struct intel_link_m_n {
 	     (__i)++) \
 		for_each_if(plane)
 
+#define for_each_oldnew_intel_crtc_in_state(__state, crtc, old_crtc_state, new_crtc_state, __i) \
+	for ((__i) = 0; \
+	     (__i) < (__state)->base.dev->mode_config.num_crtc && \
+		     ((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \
+		      (old_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].old_state), \
+		      (new_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].new_state), 1); \
+	     (__i)++) \
+		for_each_if(crtc)
+
 void intel_link_compute_m_n(int bpp, int nlanes,
 			    int pixel_clock, int link_clock,
 			    struct intel_link_m_n *m_n,
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index fd6256632482..994b9473b817 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -890,6 +890,10 @@  struct intel_crtc_state {
 	u8 active_planes;
 	u8 nv12_planes;
 
+	/* VLV/CHV zpos for each plane */
+	u8 raw_zpos[I915_MAX_PLANES]; /* raw property value (or 0xff for disabled planes) */
+	u8 zpos[I915_MAX_PLANES]; /* final zpos for each plane */
+
 	/* HDMI scrambling status */
 	bool hdmi_scrambling;
 
@@ -1457,6 +1461,9 @@  void intel_dump_cdclk_state(const struct intel_cdclk_state *cdclk_state,
 			    const char *context);
 
 /* intel_display.c */
+int intel_plane_raw_zpos(const struct intel_crtc_state *crtc_state,
+			 const struct intel_plane_state *plane_state);
+void intel__enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe);
 void i830_enable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe);
 void i830_disable_pipe(struct drm_i915_private *dev_priv, enum pipe pipe);
 enum pipe intel_crtc_pch_transcoder(struct intel_crtc *crtc);
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index a6f4524adff4..228eba582d44 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -511,6 +511,22 @@  static u32 vlv_sprite_ctl(const struct intel_crtc_state *crtc_state,
 	return sprctl;
 }
 
+static u32 vlv_sprctl_zpos(struct intel_plane *plane,
+			   const struct intel_crtc_state *crtc_state)
+{
+	enum plane_id plane_id = plane->id;
+	enum plane_id other_plane_id = plane_id == PLANE_SPRITE0 ?
+		PLANE_SPRITE1 : PLANE_SPRITE0;
+
+	if (crtc_state->zpos[plane_id] == 0)
+		return SP_BOTTOM;
+	else if (crtc_state->zpos[plane_id] ==
+		 crtc_state->zpos[other_plane_id] + 1)
+		return SP_ZORDER;
+
+	return 0;
+}
+
 static void
 vlv_update_plane(struct intel_plane *plane,
 		 const struct intel_crtc_state *crtc_state,
@@ -538,6 +554,9 @@  vlv_update_plane(struct intel_plane *plane,
 
 	linear_offset = intel_fb_xy_to_linear(x, y, plane_state, 0);
 
+	/* final zpos value is computed after vlv_sprite_ctl() */
+	sprctl |= vlv_sprctl_zpos(plane, crtc_state);
+
 	spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
 
 	vlv_update_clrc(plane_state);
@@ -1553,7 +1572,10 @@  intel_sprite_plane_create(struct drm_i915_private *dev_priv,
 					  DRM_COLOR_YCBCR_LIMITED_RANGE);
 
 	zpos = plane + 1;
-	drm_plane_create_zpos_immutable_property(&intel_plane->base, zpos);
+	if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
+		drm_plane_create_zpos_property(&intel_plane->base, zpos, 0, 2);
+	else
+		drm_plane_create_zpos_immutable_property(&intel_plane->base, zpos);
 
 	drm_plane_helper_add(&intel_plane->base, &intel_plane_helper_funcs);