@@ -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_program_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_program_pfit_filter(crtc_state);
id = scaler_state->scaler_id;
I915_WRITE(SKL_PS_CTRL(pipe, id), PS_SCALER_EN |
@@ -620,6 +620,9 @@ struct intel_crtc_scaler_state {
/* scaler used by crtc for panel fitting purpose */
int scaler_id;
+
+ /* indicate if scaling ratio is integer, to apply scaling filters */
+ bool integer_scaling;
};
/* drm_mode->private_flags */
@@ -7114,6 +7114,7 @@ enum {
#define PS_PLANE_SEL(plane) (((plane) + 1) << 25)
#define PS_FILTER_MASK (3 << 23)
#define PS_FILTER_MEDIUM (0 << 23)
+#define PS_FILTER_PROGRAMMED (1 << 23)
#define PS_FILTER_EDGE_ENHANCE (2 << 23)
#define PS_FILTER_BILINEAR (3 << 23)
#define PS_VERT3TAP (1 << 21)
@@ -7190,6 +7191,24 @@ enum {
#define _PS_ECC_STAT_2B 0x68AD0
#define _PS_ECC_STAT_1C 0x691D0
+#define _PS_COEF_SET0_INDEX_1A 0x68198
+#define _PS_COEF_SET0_INDEX_2A 0x68298
+#define _PS_COEF_SET0_INDEX_1B 0x68998
+#define _PS_COEF_SET0_INDEX_2B 0x68A98
+#define _PS_COEF_SET1_INDEX_1A 0x681A0
+#define _PS_COEF_SET1_INDEX_2A 0x682A0
+#define _PS_COEF_SET1_INDEX_1B 0x689A0
+#define _PS_COEF_SET1_INDEX_2B 0x68AA0
+
+#define _PS_COEF_SET0_DATA_1A 0x6819C
+#define _PS_COEF_SET0_DATA_2A 0x6829C
+#define _PS_COEF_SET0_DATA_1B 0x6899C
+#define _PS_COEF_SET0_DATA_2B 0x68A9C
+#define _PS_COEF_SET1_DATA_1A 0x681A4
+#define _PS_COEF_SET1_DATA_2A 0x682A4
+#define _PS_COEF_SET1_DATA_1B 0x689A4
+#define _PS_COEF_SET1_DATA_2B 0x68AA4
+
#define _ID(id, a, b) _PICK_EVEN(id, a, b)
#define SKL_PS_CTRL(pipe, id) _MMIO_PIPE(pipe, \
_ID(id, _PS_1A_CTRL, _PS_2A_CTRL), \
@@ -7218,6 +7237,18 @@ enum {
#define SKL_PS_ECC_STAT(pipe, id) _MMIO_PIPE(pipe, \
_ID(id, _PS_ECC_STAT_1A, _PS_ECC_STAT_2A), \
_ID(id, _PS_ECC_STAT_1B, _PS_ECC_STAT_2B))
+#define SKL_PS_COEF_DATA_SET0(pipe, id) _MMIO_PIPE(pipe, \
+ _ID(id, _PS_COEF_SET0_DATA_1A, _PS_COEF_SET0_DATA_2A), \
+ _ID(id, _PS_COEF_SET0_DATA_1B, _PS_COEF_SET0_DATA_1B))
+#define SKL_PS_COEF_DATA_SET1(pipe, id) _MMIO_PIPE(pipe, \
+ _ID(id, _PS_COEF_SET1_DATA_1A, _PS_COEF_SET1_DATA_2A), \
+ _ID(id, _PS_COEF_SET1_DATA_1B, _PS_COEF_SET1_DATA_1B))
+#define SKL_PS_COEF_INDEX_SET0(pipe, id) _MMIO_PIPE(pipe, \
+ _ID(id, _PS_COEF_SET0_INDEX_1A, _PS_COEF_SET0_INDEX_2A), \
+ _ID(id, _PS_COEF_SET0_INDEX_1B, _PS_COEF_SET0_INDEX_1B))
+#define SKL_PS_COEF_INDEX_SET1(pipe, id) _MMIO_PIPE(pipe, \
+ _ID(id, _PS_COEF_SET1_INDEX_1A, _PS_COEF_SET1_INDEX_2A), \
+ _ID(id, _PS_COEF_SET1_INDEX_1B, _PS_COEF_SET1_INDEX_1B))
/* legacy palette */
#define _LGC_PALETTE_A 0x4a000
This patch adds support to handle nearest-neighbor(NN) filter option for the panel fitter / pipe scaler. 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. To enable NN: - we need to set the scaler filter select value to "programmed" - and then we need to program specific values to the scaler data registers. 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. PS: There are two 80 char warnings in this patch, which is left deliberately for the better readibility of register definision and to maintain the existing formatting of the file. Maintainers can suggest. Signed-off-by: Shashank Sharma <shashank.sharma@intel.com> --- drivers/gpu/drm/i915/display/intel_display.c | 102 ++++++++++++++++++ .../drm/i915/display/intel_display_types.h | 3 + drivers/gpu/drm/i915/i915_reg.h | 31 ++++++ 3 files changed, 136 insertions(+)