[PATCH 23/36] drm/i915: Check pipe scaler scaling factors

Ville Syrjala ville.syrjala at linux.intel.com
Wed Dec 18 16:10:40 UTC 2019


From: Ville Syrjälä <ville.syrjala at linux.intel.com>

Make sure we don't exceed the maximum scaling factors of scalers.
For plane scaling we've already done a preliminary check but that
check doesn't account for the difference between HQ vs. DYN scaler
modes (since we do the check before we know whether HQ mode can
be used or not). For pfit use cases this will be the only scaling
factor check we do.

Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
---
 drivers/gpu/drm/i915/display/intel_atomic.c  | 18 ++++
 drivers/gpu/drm/i915/display/intel_atomic.h  |  1 +
 drivers/gpu/drm/i915/display/intel_display.c | 96 ++++++++++++++++++++
 drivers/gpu/drm/i915/display/intel_sprite.c  | 10 +-
 4 files changed, 121 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c
index e1ad9f18bb49..468c76e3847d 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic.c
+++ b/drivers/gpu/drm/i915/display/intel_atomic.c
@@ -262,6 +262,24 @@ intel_crtc_destroy_state(struct drm_crtc *crtc,
 	kfree(crtc_state);
 }
 
+bool skl_can_use_hq_scaler(const struct intel_crtc_state *crtc_state)
+{
+	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+	const struct intel_crtc_scaler_state *scaler_state =
+		&crtc_state->scaler_state;
+
+	if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
+		return false;
+
+	/*
+	 * When only scaler 1 is in use on a pipe with two
+	 * scalers it can operate in high quality (HQ) mode.
+	 */
+	return crtc->num_scalers > 1 && !crtc_state->nv12_planes &&
+		is_power_of_2(scaler_state->scaler_users);
+}
+
 static void intel_atomic_setup_scaler(struct intel_crtc_scaler_state *scaler_state,
 				      int num_scalers_need, struct intel_crtc *intel_crtc,
 				      const char *name, int idx,
diff --git a/drivers/gpu/drm/i915/display/intel_atomic.h b/drivers/gpu/drm/i915/display/intel_atomic.h
index 7b49623419ba..0e0b6f6cd5f9 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic.h
+++ b/drivers/gpu/drm/i915/display/intel_atomic.h
@@ -48,6 +48,7 @@ intel_atomic_get_crtc_state(struct drm_atomic_state *state,
 int intel_atomic_setup_scalers(struct drm_i915_private *dev_priv,
 			       struct intel_crtc *intel_crtc,
 			       struct intel_crtc_state *crtc_state);
+bool skl_can_use_hq_scaler(const struct intel_crtc_state *crtc_state);
 
 int intel_atomic_lock_global_state(struct intel_atomic_state *state);
 
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 5172a80f4a79..e0df709ca800 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -5533,6 +5533,97 @@ u16 skl_scaler_calc_phase(int sub, int scale, bool chroma_cosited)
 	return ((phase >> 2) & PS_PHASE_MASK) | trip;
 }
 
+/* Common cases for both horizontal and vertical scaling */
+static int skl_scaler_max_scale(const struct intel_crtc_state *crtc_state,
+				const struct drm_format_info *format)
+{
+	struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev);
+
+	/* pipe scaler YUV 4:2:0 output */
+	if (!format && crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR420)
+		return 0x18000 - 1; /* < 1.5 */
+
+	if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
+		return 0x30000 - 1; /* < 3.0 */
+
+	/* plane scaler YUV 4:2:0 input */
+	if (format && drm_format_info_is_yuv_semiplanar(format))
+		return 0x20000 - 1; /* < 2.0 */
+
+	return 0;
+}
+
+static int skl_scaler_max_hscale(const struct intel_crtc_state *crtc_state,
+				 const struct drm_format_info *format)
+{
+	int max_hscale;
+
+	max_hscale = skl_scaler_max_scale(crtc_state, format);
+	if (max_hscale)
+		return max_hscale;
+
+	return 0x30000 - 1; /* < 3.0 */
+}
+
+static int skl_scaler_max_vscale(const struct intel_crtc_state *crtc_state,
+				 int src_w, const struct drm_format_info *format)
+{
+	int max_vscale;
+
+	max_vscale = skl_scaler_max_scale(crtc_state, format);
+	if (max_vscale)
+		return max_vscale;
+
+	if (!skl_can_use_hq_scaler(crtc_state) && src_w > 2048)
+		return 0x20000 - 1; /* < 2.0 */
+	else
+		return 0x30000 - 1; /* < 3.0 */
+}
+
+static int skl_scaler_check_scaling(const struct intel_crtc_state *crtc_state,
+				    unsigned int scaler_user,
+				    int src_w, int src_h,
+				    int dst_w, int dst_h,
+				    const struct drm_format_info *format)
+{
+	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+	struct drm_rect src = {
+		.x2 = src_w << 16,
+		.y2 = src_h << 16,
+	};
+	struct drm_rect dst = {
+		.x2 = dst_w,
+		.y2 = dst_h,
+	};
+	int ret, hscale, vscale, max_hscale, max_vscale;
+
+	max_hscale = skl_scaler_max_hscale(crtc_state, format);
+
+	ret = drm_rect_calc_hscale(&src, &dst, 0, max_hscale, &hscale);
+	if (ret) {
+		DRM_DEBUG_KMS("[CRTC:%d:%s] scaler_user %u: horizontal downscaling (" DRM_RECT_FP_FMT
+			      " -> " DRM_RECT_FMT ") (" DRM_FP_FMT ") exceeds max (" DRM_FP_FMT ")\n",
+			      crtc->base.base.id, crtc->base.name, scaler_user,
+			      DRM_RECT_FP_ARG(&src), DRM_RECT_ARG(&dst),
+			      DRM_FP_ARG(hscale), DRM_FP_ARG(max_hscale));
+		return ret;
+	}
+
+	max_vscale = skl_scaler_max_vscale(crtc_state, src_w, format);
+
+	ret = drm_rect_calc_vscale(&src, &dst, 0, max_vscale, &vscale);
+	if (ret) {
+		DRM_DEBUG_KMS("[CRTC:%d:%s] scaler_user %u: vertical downscaling (" DRM_RECT_FP_FMT
+			      " -> " DRM_RECT_FMT ") (" DRM_FP_FMT ") exceeds max (" DRM_FP_FMT ")\n",
+			      crtc->base.base.id, crtc->base.name, scaler_user,
+			      DRM_RECT_FP_ARG(&src), DRM_RECT_ARG(&dst),
+			      DRM_FP_ARG(vscale), DRM_FP_ARG(max_vscale));
+		return ret;
+	}
+
+	return 0;
+}
+
 static void skl_scaler_max_src_size(struct intel_crtc *crtc,
 				    int *max_w, int *max_h)
 {
@@ -5684,6 +5775,11 @@ skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
 	if (ret)
 		return ret;
 
+	ret = skl_scaler_check_scaling(crtc_state, scaler_user,
+				       src_w, src_h, dst_w, dst_h, format);
+	if (ret)
+		return ret;
+
 	/* mark this pipe/plane as a scaler user in crtc_state */
 	scaler_state->scaler_users |= (1 << scaler_user);
 	DRM_DEBUG_KMS("scaler_user index %u.%u: "
diff --git a/drivers/gpu/drm/i915/display/intel_sprite.c b/drivers/gpu/drm/i915/display/intel_sprite.c
index 909b196be8e4..b53035599509 100644
--- a/drivers/gpu/drm/i915/display/intel_sprite.c
+++ b/drivers/gpu/drm/i915/display/intel_sprite.c
@@ -2161,10 +2161,12 @@ static int skl_plane_max_scale(struct drm_i915_private *dev_priv,
 			       const struct drm_framebuffer *fb)
 {
 	/*
-	 * We don't yet know the final source width nor
-	 * whether we can use the HQ scaler mode. Assume
-	 * the best case.
-	 * FIXME need to properly check this later.
+	 * On pre-glk the max scale factor depends on whether
+	 * the HQ scaler can be used and/or whether the final
+	 * scaler source width exceeds 2k. We don't know the
+	 * answer to either question yet so we assume the best
+	 * case. skl_scaler_check_scaling() will double check
+	 * this for us later.
 	 */
 	if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv) ||
 	    !drm_format_info_is_yuv_semiplanar(fb->format))
-- 
2.23.0



More information about the Intel-gfx-trybot mailing list