diff mbox

[3/6] drm/i915: Added Max down-scale ratio checks when enabling Panel fitter

Message ID 1408008267-11443-4-git-send-email-akash.goel@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

akash.goel@intel.com Aug. 14, 2014, 9:24 a.m. UTC
From: Akash Goel <akash.goel@intel.com>

This patch adds a check on the Max down scale ratio supported by the
Panel fitter. If Source width/height is too big, that the downscale
ratio of more than 1.125 is needed to fit into the Output window,
then that configuration will be rejected.

Signed-off-by: Akash Goel <akash.goel@intel.com>
Signed-off-by: Pallavi G<pallavi.g@intel.com>
---
 drivers/gpu/drm/i915/intel_panel.c | 113 ++++++++++++++++++++++++++++++-------
 1 file changed, 93 insertions(+), 20 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 15f2979..350e94d 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -33,6 +33,22 @@ 
 #include <linux/moduleparam.h>
 #include "intel_drv.h"
 
+/* Max Downscale ratio of 1.125, expressed in 1.12 fixed point format */
+#define MAX_DOWNSCALE_RATIO  (0x9 << 9)
+
+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)
@@ -103,6 +119,7 @@  intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
 {
 	struct drm_display_mode *adjusted_mode;
 	int x, y, width, height;
+	u32 pf_horizontal_ratio, pf_vertical_ratio;
 
 	adjusted_mode = &pipe_config->adjusted_mode;
 
@@ -161,6 +178,19 @@  intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
 		return false;
 	}
 
+	pf_horizontal_ratio = panel_fitter_scaling(pipe_config->pipe_src_w,
+				width);
+	pf_vertical_ratio = panel_fitter_scaling(pipe_config->pipe_src_h,
+				height);
+
+	if (pf_horizontal_ratio > MAX_DOWNSCALE_RATIO) {
+		DRM_DEBUG_KMS("Src width is too big to downscale\n");
+		return false;
+	} else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) {
+		DRM_DEBUG_KMS("Src height is too big to downscale\n");
+		return false;
+	}
+
 done:
 	pipe_config->pch_pfit.pos = (x << 16) | y;
 	pipe_config->pch_pfit.size = (width << 16) | height;
@@ -211,21 +241,9 @@  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)
+			      u32 *pfit_control,
+			      u32 *pf_horizontal_ratio, u32 *pf_vertical_ratio)
 {
 	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
 	u32 scaled_width = adjusted_mode->hdisplay *
@@ -234,19 +252,39 @@  static void i965_scale_aspect(struct intel_crtc_config *pipe_config,
 		adjusted_mode->vdisplay;
 
 	/* 965+ is easy, it does everything in hw */
-	if (scaled_width > scaled_height)
+	if (scaled_width > scaled_height) {
 		*pfit_control |= PFIT_ENABLE |
 			PFIT_SCALING_PILLAR;
-	else if (scaled_width < scaled_height)
+		*pf_horizontal_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_w,
+				      scaled_height / pipe_config->pipe_src_h);
+		*pf_vertical_ratio = panel_fitter_scaling(pipe_config->pipe_src_h,
+					    adjusted_mode->vdisplay);
+	}
+	else if (scaled_width < scaled_height) {
 		*pfit_control |= PFIT_ENABLE |
 			PFIT_SCALING_LETTER;
-	else if (adjusted_mode->hdisplay != pipe_config->pipe_src_w)
+		*pf_vertical_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_h,
+				      scaled_width / pipe_config->pipe_src_w);
+		*pf_horizontal_ratio = panel_fitter_scaling(pipe_config->pipe_src_w,
+						adjusted_mode->hdisplay);
+	}
+	else if (adjusted_mode->hdisplay != pipe_config->pipe_src_w) {
 		*pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
+		*pf_horizontal_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_w,
+				adjusted_mode->hdisplay);
+		*pf_vertical_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_h,
+				adjusted_mode->vdisplay);
+	}
 }
 
 static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config,
 			      u32 *pfit_control, u32 *pfit_pgm_ratios,
-			      u32 *border)
+			      u32 *border,
+			      u32 *pf_horizontal_ratio, u32 *pf_vertical_ratio)
 {
 	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
 	u32 scaled_width = adjusted_mode->hdisplay *
@@ -264,11 +302,15 @@  static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config,
 		centre_horizontally(adjusted_mode,
 				    scaled_height /
 				    pipe_config->pipe_src_h);
+		*pf_horizontal_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_w,
+				    scaled_height / pipe_config->pipe_src_h);
 
 		*border = LVDS_BORDER_ENABLE;
 		if (pipe_config->pipe_src_h != adjusted_mode->vdisplay) {
 			bits = panel_fitter_scaling(pipe_config->pipe_src_h,
 						    adjusted_mode->vdisplay);
+			*pf_vertical_ratio = bits;
 
 			*pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
 					     bits << PFIT_VERT_SCALE_SHIFT);
@@ -280,11 +322,15 @@  static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config,
 		centre_vertically(adjusted_mode,
 				  scaled_width /
 				  pipe_config->pipe_src_w);
+		*pf_vertical_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_h,
+				    scaled_width / pipe_config->pipe_src_w);
 
 		*border = LVDS_BORDER_ENABLE;
 		if (pipe_config->pipe_src_w != adjusted_mode->hdisplay) {
 			bits = panel_fitter_scaling(pipe_config->pipe_src_w,
 						    adjusted_mode->hdisplay);
+			*pf_horizontal_ratio = bits;
 
 			*pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
 					     bits << PFIT_VERT_SCALE_SHIFT);
@@ -298,6 +344,13 @@  static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config,
 				  VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
 				  VERT_INTERP_BILINEAR |
 				  HORIZ_INTERP_BILINEAR);
+
+		*pf_horizontal_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_w,
+				adjusted_mode->hdisplay);
+		*pf_vertical_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_h,
+				adjusted_mode->vdisplay);
 	}
 }
 
@@ -308,6 +361,7 @@  bool intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
 	struct drm_device *dev = intel_crtc->base.dev;
 	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
 	struct drm_display_mode *adjusted_mode;
+	u32 pf_horizontal_ratio = 0, pf_vertical_ratio = 0;
 
 	adjusted_mode = &pipe_config->adjusted_mode;
 
@@ -325,20 +379,31 @@  bool intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
 		centre_horizontally(adjusted_mode, pipe_config->pipe_src_w);
 		centre_vertically(adjusted_mode, pipe_config->pipe_src_h);
 		border = LVDS_BORDER_ENABLE;
+		/* 1:1 scaling */
+		pf_horizontal_ratio = pf_vertical_ratio = 1;
 		break;
 	case DRM_MODE_SCALE_ASPECT:
 		/* Scale but preserve the aspect ratio */
 		if (INTEL_INFO(dev)->gen >= 4)
-			i965_scale_aspect(pipe_config, &pfit_control);
+			i965_scale_aspect(pipe_config, &pfit_control,
+				 &pf_horizontal_ratio, &pf_vertical_ratio);
 		else
 			i9xx_scale_aspect(pipe_config, &pfit_control,
-					  &pfit_pgm_ratios, &border);
+					  &pfit_pgm_ratios, &border,
+					  &pf_horizontal_ratio,
+					  &pf_vertical_ratio);
 		break;
 	case DRM_MODE_SCALE_FULLSCREEN:
 		/*
 		 * Full scaling, even if it changes the aspect ratio.
 		 * Fortunately this is all done for us in hw.
 		 */
+		pf_horizontal_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_w,
+				adjusted_mode->hdisplay);
+		pf_vertical_ratio =
+				panel_fitter_scaling(pipe_config->pipe_src_h,
+				adjusted_mode->vdisplay);
 		if (pipe_config->pipe_src_h != adjusted_mode->vdisplay ||
 		    pipe_config->pipe_src_w != adjusted_mode->hdisplay) {
 			pfit_control |= PFIT_ENABLE;
@@ -356,6 +421,14 @@  bool intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
 		return false;
 	}
 
+	if (pf_horizontal_ratio > MAX_DOWNSCALE_RATIO) {
+		DRM_DEBUG_KMS("Src width is too big to downscale\n");
+		return false;
+	} else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) {
+		DRM_DEBUG_KMS("Src height is too big to downscale\n");
+		return false;
+	}
+
 	/* 965+ wants fuzzy fitting */
 	/* FIXME: handle multiple panels by failing gracefully */
 	if (INTEL_INFO(dev)->gen >= 4)