[PATCH 3/3] drm/i915: Handle nearest-neighbor scaling filter

Shashank Sharma shashank.sharma at intel.com
Mon Oct 14 07:48:42 UTC 2019


This patch adds support to handle nearest-neighbor(NN) filter
option for the panel fitter / pipe scaler. To enable NN, we need
to set the scaler filter select value to "programmed" and then need
to program specific values to the scaler data registers.

Nearest-neighbor filter, when applied for integer upscaling ratios,
can produce sharp and blurless outputs. This is pretty useful while
upscaling the old games to higher resolution displays.

Naturally, this patch also handles the Integer scaling scenarios. If the
user selects scaler filter value as "DRM_SCALING_FILTER_NN_IS_ONLY", this
means user wants to enable NN filter only when the upscaling ratios are
complete integer.

Signed-off-by: Shashank Sharma <shashank.sharma at intel.com>
---
 drivers/gpu/drm/i915/display/intel_display.c | 102 +++++++++++++++++++
 1 file changed, 102 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 9b015b9409ff..cf68896c225a 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -5411,6 +5411,17 @@ u16 skl_scaler_calc_phase(int sub, int scale, bool chroma_cosited)
 #define SKL_MIN_YUV_420_SRC_W 16
 #define SKL_MIN_YUV_420_SRC_H 16
 
+static inline bool
+scaling_ratio_is_integer(int src_w, int dst_w, int src_h, int dst_h)
+{
+	/* Integer mode scaling is applicable only for upscaling scenarios */
+	if (dst_w < src_w || dst_h < src_h)
+		return false;
+	if (dst_w % src_w == 0 && dst_h % src_h == 0)
+		return true;
+	return false;
+}
+
 static int
 skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
 		  unsigned int scaler_user, int *scaler_id,
@@ -5445,6 +5456,15 @@ skl_update_scaler(struct intel_crtc_state *crtc_state, bool force_detach,
 		return -EINVAL;
 	}
 
+	/*
+	 * If we are upscaling, and the scaling ratios are integer, we can
+	 * pick nearest-neighbour method in HW for scaling, which produces
+	 * blurless outputs in such scenarios.
+	 */
+	if (INTEL_GEN(dev_priv) >= 11 &&
+	    scaling_ratio_is_integer(src_w, dst_w, src_h, dst_h))
+		scaler_state->integer_scaling = true;
+
 	/*
 	 * if plane is being disabled or scaler is no more required or force detach
 	 *  - free scaler binded to this plane/crtc
@@ -5615,6 +5635,86 @@ static void skylake_scaler_disable(struct intel_crtc *crtc)
 		skl_detach_scaler(crtc, i);
 }
 
+static void
+icl_setup_nearest_neighbor_filter(const struct intel_crtc_state *crtc_state)
+{
+	int count;
+	int phase;
+	struct intel_crtc *crtc = to_intel_crtc(crtc_state->base.crtc);
+	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+	int scaler_id = crtc_state->scaler_state.scaler_id;
+	enum pipe pipe = crtc->pipe;
+
+	/*
+	 * To setup nearest-neighbor integer scaling mode:
+	 * Write 60 dwords: represnting 119 coefficients.
+	 *
+	 * Seven basic Coefficients are named from An......Gn.
+	 * Value of every D'th coefficent must be 1, all others to be 0.
+	 *
+	 * 17 such phases of 7 such coefficients = 119 coefficients.
+	 * Arrange these 119 coefficients in 60 dwords, 2 coefficient
+	 * per dword, in the sequence shown below:
+	 *
+	 *+------------+--------------+
+	 *|     B0     |      A0      |
+	 *+---------------------------+
+	 *|    D0 = 1  |      C0      |
+	 *+---------------------------+
+	 *|     F0     |      E0      |
+	 *+---------------------------+
+	 *|     A1     |      G0      |
+	 *+---------------------------+
+	 *|     C1     |      B1      |
+	 *+---------------------------+
+	 *|     E1     |      D1 = 1  |
+	 *+---------------------------+
+	 *|    .....   |     .....    |
+	 *+---------------------------+
+	 *|    ......  |     ......   |
+	 *+---------------------------+
+	 *|    Res     |      G16     |
+	 *+------------+--------------+
+	 *
+	 */
+
+	for (phase = 0; phase < 17; phase++) {
+		for (count = 0; count < 7; count++) {
+			u32 val = 0;
+
+			/* Every D'th entry needs to be 1 */
+			if (count == 3)
+				(phase % 2) ? (val = 1) : (val = (1 << 16));
+
+			I915_WRITE_FW(SKL_PS_COEF_INDEX_SET0(pipe, scaler_id),
+				      phase * 17 + count);
+			I915_WRITE_FW(SKL_PS_COEF_DATA_SET0(pipe, scaler_id),
+				      val);
+
+			I915_WRITE_FW(SKL_PS_COEF_INDEX_SET1(pipe, scaler_id),
+				      phase * 17 + count);
+			I915_WRITE_FW(SKL_PS_COEF_DATA_SET1(pipe, scaler_id),
+				      val);
+		}
+	}
+}
+
+static u32 icl_prepare_pfit_filter(const struct intel_crtc_state *crtc_state)
+{
+	const struct drm_crtc_state *state = &crtc_state->base;
+	const struct intel_crtc_scaler_state *scaler_state =
+		&crtc_state->scaler_state;
+
+	if (state->scaling_filter == DRM_SCALING_FILTER_NN_IS_ONLY &&
+		!scaler_state->integer_scaling) {
+		DRM_DEBUG_DRIVER("Scaling ratio not integer, not picking NN\n");
+		return PS_FILTER_MEDIUM;
+	}
+
+	icl_setup_nearest_neighbor_filter(crtc_state);
+	return PS_FILTER_PROGRAMMED;
+}
+
 static u32
 icelake_get_scaler_filter(const struct intel_crtc_state *crtc_state)
 {
@@ -5665,6 +5765,8 @@ static void skylake_pfit_enable(const struct intel_crtc_state *crtc_state)
 		uv_rgb_vphase = skl_scaler_calc_phase(1, vscale, false);
 
 		scaler_filter = icelake_get_scaler_filter(crtc_state);
+		if (scaler_filter == PS_FILTER_PROGRAMMED)
+			scaler_filter = icl_prepare_pfit_filter(crtc_state);
 
 		id = scaler_state->scaler_id;
 		I915_WRITE(SKL_PS_CTRL(pipe, id), PS_SCALER_EN |
-- 
2.17.1



More information about the Intel-gfx-trybot mailing list