From: Akash Goel <akash.g...@intel.com>

This patch adds a new drm crtc property for varying the size of
the horizontal & vertical borers of the output/display window.
This will control the output of Panel fitter.

v2: Added a new check for the invalid border size input

v3: Fixed bugs in output window calculation
Removed superfluous checks

Testcase: igt/kms_panel_fitter_test

Signed-off-by: Akash Goel <akash.g...@intel.com>
---
 drivers/gpu/drm/i915/i915_drv.h      |   7 ++
 drivers/gpu/drm/i915/intel_display.c |  28 +++++-
 drivers/gpu/drm/i915/intel_drv.h     |   4 +
 drivers/gpu/drm/i915/intel_panel.c   | 180 ++++++++++++++++++++++++++++++++---
 4 files changed, 204 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 6f3af15..eec32ed 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1614,6 +1614,13 @@ typedef struct drm_i915_private {
         */
        struct drm_property *input_size_property;
 
+       /*
+        * Property to dynamically vary the size of the
+        * borders. This will indirectly control the size
+        * of the display window i.e Panel fitter output
+        */
+       struct drm_property *output_border_property;
+
        uint32_t hw_context_size;
        struct list_head context_list;
 
diff --git a/drivers/gpu/drm/i915/intel_display.c 
b/drivers/gpu/drm/i915/intel_display.c
index 8772390..68ec5b5 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -10447,7 +10447,16 @@ static int intel_crtc_set_property(struct drm_crtc 
*crtc,
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       int ret = -ENOENT;
+
+       if (property == dev_priv->output_border_property) {
+               if (val == (uint64_t)intel_crtc->border_size)
+                       return 0;
+
+               intel_crtc->border_size = (uint32_t)val;
+               intel_crtc->border_size_changed = 1;
+
+               goto done;
+       }
 
        if (property == dev_priv->input_size_property) {
                int new_width = (int)((val >> 16) & 0xffff);
@@ -10483,7 +10492,13 @@ static int intel_crtc_set_property(struct drm_crtc 
*crtc,
                return 0;
        }
 
-       return ret;
+       return -EINVAL;
+
+done:
+       if (crtc)
+               intel_crtc_restore_mode(crtc);
+
+       return 0;
 }
 
 static const struct drm_crtc_funcs intel_crtc_funcs = {
@@ -10642,6 +10657,15 @@ static void intel_crtc_init(struct drm_device *dev, 
int pipe)
                drm_object_attach_property(&intel_crtc->base.base,
                                           dev_priv->input_size_property,
                                           0);
+
+       if (!dev_priv->output_border_property)
+               dev_priv->output_border_property =
+                       drm_property_create_range(dev, 0, "border size", 0, 
0xFFFFFFFF);
+
+       if (dev_priv->output_border_property)
+               drm_object_attach_property(&intel_crtc->base.base,
+                                          dev_priv->output_border_property,
+                                          0);
 }
 
 enum pipe intel_get_pipe_from_connector(struct intel_connector *connector)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 5360d16..68eec38 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -387,6 +387,10 @@ struct intel_crtc {
        bool cpu_fifo_underrun_disabled;
        bool pch_fifo_underrun_disabled;
 
+       /* border info for the output/display window */
+       bool border_size_changed;
+       uint32_t border_size;
+
        /* per-pipe watermark state */
        struct {
                /* watermarks currently being used  */
diff --git a/drivers/gpu/drm/i915/intel_panel.c 
b/drivers/gpu/drm/i915/intel_panel.c
index cb05840..ca61a17 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -29,10 +29,25 @@
  */
 
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+/* Max Downscale ratio of 1.125, expressed in 1.12 fixed point format */
+#define MAX_DOWNSCALE_RATIO  (0x9 << 9)
 
 #include <linux/moduleparam.h>
 #include "intel_drv.h"
 
+static inline u32 panel_fitter_scaling(u32 source, u32 target)
+{
+       /*
+        * Floating point operation is not supported. So the FACTOR
+        * is defined, which can avoid the floating point computation
+        * when calculating the panel ratio.
+        */
+#define ACCURACY 12
+#define FACTOR (1 << ACCURACY)
+       u32 ratio = source * FACTOR / target;
+       return (FACTOR * ratio + FACTOR/2) / FACTOR;
+}
+
 void
 intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode,
                       struct drm_display_mode *adjusted_mode)
@@ -42,6 +57,63 @@ intel_fixed_panel_mode(const struct drm_display_mode 
*fixed_mode,
        drm_mode_set_crtcinfo(adjusted_mode, 0);
 }
 
+void
+intel_pch_manual_panel_fitting(struct intel_crtc *intel_crtc,
+                       struct intel_crtc_config *pipe_config)
+{
+       struct drm_display_mode *adjusted_mode;
+       int x, y;
+       u32 pf_horizontal_ratio, pf_vertical_ratio;
+       u32 tot_width, tot_height;
+       u32 src_width, src_height; /* pipesrc.x, pipesrc.y */
+       u32 dst_width, dst_height;
+
+       adjusted_mode = &pipe_config->adjusted_mode;
+
+       src_width = pipe_config->pipe_src_w;
+       src_height = pipe_config->pipe_src_h;
+
+       tot_width  = adjusted_mode->hdisplay;
+       tot_height = adjusted_mode->vdisplay;
+
+       /*
+        * Having non zero borders will reduce the size of 'HACTIVE/VACTIVE'
+        * region. So (HACTIVE - Left border - Right Border) *
+        * (VACTIVE  - Top Border  - Bottom border) will effectively be the
+        * output rectangle on screen
+        */
+       dst_width = tot_width -
+                       (((intel_crtc->border_size >> 16) & 0xffff) * 2);
+       dst_height = tot_height -
+                       ((intel_crtc->border_size & 0xffff) * 2);
+
+       if ((dst_width == 0) || (dst_height == 0)) {
+               DRM_ERROR("Invalid border size input\n");
+               goto out;
+       }
+
+       pf_horizontal_ratio = panel_fitter_scaling(src_width, dst_width);
+       pf_vertical_ratio   = panel_fitter_scaling(src_height, dst_height);
+
+       if (pf_horizontal_ratio > MAX_DOWNSCALE_RATIO) {
+               DRM_ERROR("width is too small\n");
+               goto out;
+       } else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) {
+               DRM_ERROR("height is too small\n");
+               goto out;
+       }
+
+       x = (adjusted_mode->hdisplay - dst_width + 1)/2;
+       y = (adjusted_mode->vdisplay - dst_height + 1)/2;
+
+       pipe_config->pch_pfit.pos = (x << 16) | y;
+       pipe_config->pch_pfit.size = (dst_width << 16) | dst_height;
+       pipe_config->pch_pfit.enabled = pipe_config->pch_pfit.size != 0;
+
+out:
+       intel_crtc->border_size_changed = 0;
+}
+
 /* adjusted_mode has been preset to be the panel's fixed mode */
 void
 intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
@@ -55,6 +127,13 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
 
        x = y = width = height = 0;
 
+       /* check if size of borders has changed and border size is
+          not zero, otherwise fall through the regular path */
+       if (intel_crtc->border_size_changed ||
+                       intel_crtc->border_size)
+               return intel_pch_manual_panel_fitting(intel_crtc,
+                                                     pipe_config);
+
        /* Native modes don't need fitting */
        if (adjusted_mode->hdisplay == pipe_config->pipe_src_w &&
            adjusted_mode->vdisplay == pipe_config->pipe_src_h)
@@ -157,19 +236,6 @@ centre_vertically(struct drm_display_mode *mode,
        mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width;
 }
 
-static inline u32 panel_fitter_scaling(u32 source, u32 target)
-{
-       /*
-        * Floating point operation is not supported. So the FACTOR
-        * is defined, which can avoid the floating point computation
-        * when calculating the panel ratio.
-        */
-#define ACCURACY 12
-#define FACTOR (1 << ACCURACY)
-       u32 ratio = source * FACTOR / target;
-       return (FACTOR * ratio + FACTOR/2) / FACTOR;
-}
-
 static void i965_scale_aspect(struct intel_crtc_config *pipe_config,
                              u32 *pfit_control)
 {
@@ -247,6 +313,87 @@ static void i9xx_scale_aspect(struct intel_crtc_config 
*pipe_config,
        }
 }
 
+void intel_gmch_manual_panel_fitting(struct intel_crtc *intel_crtc,
+                                    struct intel_crtc_config *pipe_config)
+{
+       struct drm_device *dev = intel_crtc->base.dev;
+       u32 pfit_control = 0, border = 0;
+       u32 pf_horizontal_ratio, pf_vertical_ratio;
+       struct drm_display_mode *adjusted_mode;
+       u32 tot_width, tot_height;
+       u32 src_width, src_height; /* pipesrc.x, pipesrc.y */
+       u32 dst_width, dst_height;
+
+       adjusted_mode = &pipe_config->adjusted_mode;
+
+       src_width = pipe_config->pipe_src_w;
+       src_height = pipe_config->pipe_src_h;
+
+       tot_width = adjusted_mode->hdisplay;
+       tot_height = adjusted_mode->vdisplay;
+
+       /*
+        * Having non zero borders will reduce the size of 'HACTIVE/VACTIVE'
+        * region. So (HACTIVE - Left border - Right Border) *
+        * (VACTIVE  - Top Border  - Bottom border) will effectively be the
+        * output rectangle on screen
+        */
+       dst_width = tot_width -
+                       (((intel_crtc->border_size >> 16) & 0xffff) * 2);
+       dst_height = tot_height -
+                       ((intel_crtc->border_size & 0xffff) * 2);
+
+       if ((dst_width == 0) || (dst_height == 0)) {
+               DRM_ERROR("Invalid border size input\n");
+               goto out;
+       }
+
+       pf_horizontal_ratio = panel_fitter_scaling(src_width, dst_width);
+       pf_vertical_ratio   = panel_fitter_scaling(src_height, dst_height);
+
+       if (pf_horizontal_ratio > MAX_DOWNSCALE_RATIO) {
+               DRM_ERROR("width is too small\n");
+               goto out;
+       } else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) {
+               DRM_ERROR("height is too small\n");
+               goto out;
+       }
+
+       if (dst_width != tot_width)
+               centre_horizontally(adjusted_mode, dst_width);
+       if (dst_height != tot_height)
+               centre_vertically(adjusted_mode, dst_height);
+
+       /* Don't enable the Panel fitter, if no scaling is needed */
+       if (adjusted_mode->crtc_hdisplay == pipe_config->pipe_src_w &&
+           adjusted_mode->crtc_vdisplay == pipe_config->pipe_src_h) {
+               DRM_DEBUG_KMS("Skipping enabling of Panel fitter\n");
+               goto out;
+       }
+
+       border = LVDS_BORDER_ENABLE;
+
+       if (INTEL_INFO(dev)->gen >= 4) {
+               /* PFIT_SCALING_PROGRAMMED is de-featured on BYT */
+               pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
+               pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | 
PFIT_FILTER_FUZZY);
+       } else {
+               pfit_control |= (PFIT_ENABLE |
+                                VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
+                                VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR);
+       }
+
+       /* Make sure pre-965 set dither correctly for 18bpp panels. */
+       if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18)
+               pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+
+       pipe_config->gmch_pfit.control = pfit_control;
+       pipe_config->gmch_pfit.lvds_border_bits = border;
+
+out:
+       intel_crtc->border_size_changed = 0;
+}
+
 void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
                              struct intel_crtc_config *pipe_config,
                              int fitting_mode)
@@ -257,6 +404,13 @@ void intel_gmch_panel_fitting(struct intel_crtc 
*intel_crtc,
 
        adjusted_mode = &pipe_config->adjusted_mode;
 
+       /* check if size of borders has changed or border size is
+          not zero, otherwise fall through the regular path */
+       if (intel_crtc->border_size_changed ||
+                       intel_crtc->border_size)
+               return intel_gmch_manual_panel_fitting(intel_crtc,
+                                                      pipe_config);
+
        /* Native modes don't need fitting */
        if (adjusted_mode->hdisplay == pipe_config->pipe_src_w &&
            adjusted_mode->vdisplay == pipe_config->pipe_src_h)
-- 
1.8.5.2

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

Reply via email to