[Intel-gfx] [Patch 2/3]: [DRM/I915] : Sync the panel fitting property with 2D driver

yakui_zhao yakui.zhao at intel.com
Fri Mar 20 07:11:25 CET 2009


Subject: DRM/I915: Sync the panel fitting property with 2D driver
From: Zhao Yakui <yakui.zhao at intel.com>

     Sync the panel fitting property with 2D driver
     This covers:
     a. create the scaling mode property and attach it to LVDS
     b. add the support of panel fitting property for LVDS
     c. update the display mode according to the panel fitting mode

Signed-off-by: Zhao Yakui <yakui.zhao at intel.com>
---
 drivers/gpu/drm/i915/i915_reg.h   |   17 ++
 drivers/gpu/drm/i915/intel_lvds.c |  273 +++++++++++++++++++++++++++++++++++---
 2 files changed, 269 insertions(+), 21 deletions(-)

Index: linux-2.6/drivers/gpu/drm/i915/intel_lvds.c
===================================================================
--- linux-2.6.orig/drivers/gpu/drm/i915/intel_lvds.c	2009-03-20 11:39:38.000000000 +0800
+++ linux-2.6/drivers/gpu/drm/i915/intel_lvds.c	2009-03-20 11:51:53.000000000 +0800
@@ -37,6 +37,21 @@
 #include "i915_drm.h"
 #include "i915_drv.h"
 
+/*
+ * the following four scaling options are defined.
+ * #define DRM_MODE_SCALE_NON_GPU	0
+ * #define DRM_MODE_SCALE_FULLSCREEN	1
+ * #define DRM_MODE_SCALE_NO_SCALE	2
+ * #define DRM_MODE_SCALE_ASPECT	3
+ */
+
+/* Private structure for the integrated LVDS support */
+struct intel_lvds_priv {
+	int fitting_mode;
+	u32 pfit_control;
+	u32 pfit_pgm_ratios;
+};
+
 /**
  * Sets the backlight level.
  *
@@ -160,10 +175,24 @@
 				  struct drm_display_mode *mode,
 				  struct drm_display_mode *adjusted_mode)
 {
+	/*
+	 * float point operation is not supported . So the PANEL_RATIO_FACTOR
+	 * is defined, which can avoid the float point computation when
+	 * calculating the panel ratio.
+	 */
+#define PANEL_RATIO_FACTOR 8192
 	struct drm_device *dev = encoder->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
 	struct drm_encoder *tmp_encoder;
+	struct intel_output *intel_output = enc_to_intel_output(encoder);
+	struct intel_lvds_priv *lvds_priv = intel_output->dev_priv;
+	u32 pfit_control = 0, pfit_pgm_ratios = 0;
+	int left_border = 0, right_border = 0, top_border = 0;
+	int bottom_border = 0;
+	bool border = 0;
+	int panel_ratio, desired_ratio, vert_scale, horiz_scale;
+	int horiz_ratio, vert_ratio;
 
 	/* Should never happen!! */
 	if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
@@ -179,7 +208,9 @@
 			return false;
 		}
 	}
-
+	/* If we don't have a panel mode, there is nothing we can do */
+	if (dev_priv->panel_fixed_mode == NULL)
+		return true;
 	/*
 	 * If we have timings from the BIOS for the panel, put them in
 	 * to the adjusted mode.  The CRTC will be set up for this mode,
@@ -203,6 +234,184 @@
 		drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
 	}
 
+	/* Make sure pre-965s set dither correctly */
+	if (!IS_I965G(dev) && dev_priv->panel_wants_dither)
+		pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+
+	/* Native modes don't need fitting */
+	if (adjusted_mode->hdisplay == mode->hdisplay &&
+			adjusted_mode->vdisplay == mode->vdisplay) {
+		pfit_pgm_ratios = 0;
+		border = 0;
+		goto out;
+	}
+
+	/* 965+ wants fuzzy fitting */
+	if (IS_I965G(dev))
+		pfit_control |= (intel_crtc->pipe << PFIT_PIPE_SHIFT) |
+					PFIT_FILTER_FUZZY;
+
+	/*
+	 * Deal with panel fitting options. Figure out how to stretch the
+	 * image based on its aspect ratio & the current panel fitting mode.
+	 */
+	panel_ratio = adjusted_mode->hdisplay * PANEL_RATIO_FACTOR /
+				adjusted_mode->vdisplay;
+	desired_ratio = mode->hdisplay * PANEL_RATIO_FACTOR /
+				mode->vdisplay;
+	/*
+	 * Enable automatic panel scaling for non-native modes so that they fill
+	 * the screen.  Should be enabled before the pipe is enabled, according
+	 * to register description and PRM.
+	 * Change the value here to see the borders for debugging
+	 */
+	I915_WRITE(BCLRPAT_A, 0);
+	I915_WRITE(BCLRPAT_B, 0);
+
+	switch (lvds_priv->fitting_mode) {
+	case DRM_MODE_SCALE_NO_SCALE:
+		/*
+		 * For centered modes, we have to calculate border widths &
+		 * heights and modify the values programmed into the CRTC.
+		 */
+		left_border = (adjusted_mode->hdisplay - mode->hdisplay) / 2;
+		right_border = left_border;
+		if (mode->hdisplay & 1)
+			right_border++;
+		top_border = (adjusted_mode->vdisplay - mode->vdisplay) / 2;
+		bottom_border = top_border;
+		if (mode->vdisplay & 1)
+			bottom_border++;
+		/* Set active & border values */
+		adjusted_mode->crtc_hdisplay = mode->hdisplay;
+		adjusted_mode->crtc_hblank_start = mode->hdisplay +
+						right_border - 1;
+		adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_htotal -
+						left_border - 1;
+		adjusted_mode->crtc_hsync_start =
+				adjusted_mode->crtc_hblank_start;
+		adjusted_mode->crtc_hsync_end =
+				adjusted_mode->crtc_hblank_end;
+		adjusted_mode->crtc_vdisplay = mode->vdisplay;
+		adjusted_mode->crtc_vblank_start = mode->vdisplay +
+						bottom_border - 1;
+		adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vtotal -
+						top_border - 1;
+		adjusted_mode->crtc_vsync_start =
+				adjusted_mode->crtc_vblank_start;
+		adjusted_mode->crtc_vsync_end =
+				adjusted_mode->crtc_vblank_end;
+		border = 1;
+		break;
+	case DRM_MODE_SCALE_ASPECT:
+		/* Scale but preserve the spect ratio */
+		pfit_control |= PFIT_ENABLE;
+		if (IS_I965G(dev)) {
+			/* 965+ is easy, it does everything in hw */
+			if (panel_ratio > desired_ratio)
+				pfit_control |= PFIT_SCALING_PILLAR;
+			else if (panel_ratio < desired_ratio)
+				pfit_control |= PFIT_SCALING_LETTER;
+			else
+				pfit_control |= PFIT_SCALING_AUTO;
+		} else {
+			/*
+			 * For earlier chips we have to calculate the scaling
+			 * ratio by hand and program it into the
+			 * PFIT_PGM_RATIO register
+			 */
+			u32 horiz_bits, vert_bits, bits = 12;
+			horiz_ratio = mode->hdisplay * PANEL_RATIO_FACTOR/
+						adjusted_mode->hdisplay;
+			vert_ratio = mode->vdisplay * PANEL_RATIO_FACTOR/
+						adjusted_mode->vdisplay;
+			horiz_scale = adjusted_mode->hdisplay *
+					PANEL_RATIO_FACTOR / mode->hdisplay;
+			vert_scale = adjusted_mode->vdisplay *
+					PANEL_RATIO_FACTOR / mode->vdisplay;
+
+			/* retain aspect ratio */
+			if (panel_ratio > desired_ratio) { /* Pillar */
+				u32 scaled_width;
+				scaled_width = mode->hdisplay * vert_scale /
+						PANEL_RATIO_FACTOR;
+				horiz_ratio = vert_ratio;
+				pfit_control |= (VERT_AUTO_SCALE |
+				VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR);
+				/* Pillar will have left/right borders */
+				left_border = (adjusted_mode->hdisplay -
+						scaled_width) / 2;
+				right_border = left_border;
+				if (mode->hdisplay & 1) /* odd resolutions */
+					right_border++;
+				adjusted_mode->crtc_hdisplay = scaled_width;
+				adjusted_mode->crtc_hblank_start =
+					scaled_width + right_border - 1;
+				adjusted_mode->crtc_hblank_end =
+				 adjusted_mode->crtc_htotal - left_border - 1;
+				adjusted_mode->crtc_hsync_start =
+					adjusted_mode->crtc_hblank_start;
+				adjusted_mode->crtc_hsync_end =
+					adjusted_mode->crtc_hblank_end;
+				border = 1;
+			} else if (panel_ratio < desired_ratio) { /* letter */
+				u32 scaled_height = mode->vdisplay *
+					horiz_scale / PANEL_RATIO_FACTOR;
+				vert_ratio = horiz_ratio;
+				pfit_control |= (HORIZ_AUTO_SCALE |
+				VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR);
+				/* Letterbox will have top/bottom border */
+				top_border = (adjusted_mode->vdisplay -
+					scaled_height) / 2;
+				bottom_border = top_border;
+				if (mode->vdisplay & 1)
+					bottom_border++;
+				adjusted_mode->crtc_vdisplay = scaled_height;
+				adjusted_mode->crtc_vblank_start =
+					scaled_height + bottom_border - 1;
+				adjusted_mode->crtc_vblank_end =
+				 adjusted_mode->crtc_vtotal - top_border - 1;
+				adjusted_mode->crtc_vsync_start =
+					adjusted_mode->crtc_vblank_start;
+				adjusted_mode->crtc_vsync_end =
+					adjusted_mode->crtc_vblank_end;
+				border = 1;
+			} else {
+			/* Aspects match, Let hw scale both directions */
+			pfit_control |= (VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
+				VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR);
+			}
+			horiz_bits = (1 << bits) * horiz_ratio /
+					PANEL_RATIO_FACTOR;
+			vert_bits = (1 << bits) * vert_ratio /
+					PANEL_RATIO_FACTOR;
+			pfit_pgm_ratios =
+					((vert_bits << PFIT_VERT_SCALE_SHIFT) &
+						PFIT_VERT_SCALE_MASK) |
+				((horiz_bits << PFIT_HORIZ_SCALE_SHIFT) &
+						PFIT_HORIZ_SCALE_MASK);
+		}
+		break;
+
+	case DRM_MODE_SCALE_FULLSCREEN:
+		/*
+		 * Full scaling, even if it changes the aspect ratio.
+		 * Fortunately this is all done for us in hw.
+		 */
+		pfit_control |= PFIT_ENABLE;
+		if (IS_I965G(dev))
+			pfit_control |= PFIT_SCALING_AUTO;
+		else
+			pfit_control |= (VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
+				VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR);
+		break;
+	default:
+		break;
+	}
+
+out:
+	lvds_priv->pfit_control = pfit_control;
+	lvds_priv->pfit_pgm_ratios = pfit_pgm_ratios;
 	/*
 	 * XXX: It would be nice to support lower refresh rates on the
 	 * panels to reduce power consumption, and perhaps match the
@@ -242,8 +451,8 @@
 {
 	struct drm_device *dev = encoder->dev;
 	struct drm_i915_private *dev_priv = dev->dev_private;
-	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
-	u32 pfit_control;
+	struct intel_output *intel_output = enc_to_intel_output(encoder);
+	struct intel_lvds_priv *lvds_priv = intel_output->dev_priv;
 
 	/*
 	 * The LVDS pin pair will already have been turned on in the
@@ -256,22 +465,8 @@
 	 * screen.  Should be enabled before the pipe is enabled, according to
 	 * register description and PRM.
 	 */
-	if (mode->hdisplay != adjusted_mode->hdisplay ||
-	    mode->vdisplay != adjusted_mode->vdisplay)
-		pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
-				HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
-				HORIZ_INTERP_BILINEAR);
-	else
-		pfit_control = 0;
-
-	if (!IS_I965G(dev)) {
-		if (dev_priv->panel_wants_dither)
-			pfit_control |= PANEL_8TO6_DITHER_ENABLE;
-	}
-	else
-		pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
-
-	I915_WRITE(PFIT_CONTROL, pfit_control);
+	I915_WRITE(PFIT_PGM_RATIOS, lvds_priv->pfit_pgm_ratios);
+	I915_WRITE(PFIT_CONTROL, lvds_priv->pfit_control);
 }
 
 /**
@@ -344,10 +539,32 @@
 				   uint64_t value)
 {
 	struct drm_device *dev = connector->dev;
+	struct intel_output *intel_output =
+			to_intel_output(connector);
 
 	if (property == dev->mode_config.dpms_property && connector->encoder)
 		intel_lvds_dpms(connector->encoder, (uint32_t)(value & 0xf));
 
+	if (property == dev->mode_config.scaling_mode_property &&
+			connector->encoder) {
+		struct drm_crtc *crtc = connector->encoder->crtc;
+		struct intel_lvds_priv *lvds_priv = intel_output->dev_priv;
+		if (value == DRM_MODE_SCALE_NON_GPU) {
+			DRM_DEBUG("non_GPU property is unsupported.\n");
+			return 0;
+		}
+		if (lvds_priv->fitting_mode == value)
+			return 0;
+		lvds_priv->fitting_mode = value;
+		if (crtc && crtc->enabled) {
+			/*
+			 * If the CRTC is enabled, the display will be changed
+			 * according to the new panel fitting mode.
+			 */
+			drm_crtc_helper_set_mode(crtc, &crtc->mode,
+					crtc->x, crtc->y, crtc->fb);
+		}
+	}
 	return 0;
 }
 
@@ -401,6 +618,7 @@
 	struct drm_encoder *encoder;
 	struct drm_display_mode *scan; /* *modes, *bios_mode; */
 	struct drm_crtc *crtc;
+	struct intel_lvds_priv *lvds_priv;
 	u32 lvds;
 	int pipe;
 
@@ -419,7 +637,8 @@
 		return;
 	}
 
-	intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
+	intel_output = drm_calloc(1, sizeof(struct intel_output) +
+				sizeof(struct intel_lvds_priv), DRM_MEM_DRIVER);
 	if (!intel_output) {
 		return;
 	}
@@ -441,7 +660,18 @@
 	connector->interlace_allowed = false;
 	connector->doublescan_allowed = false;
 
+	lvds_priv = (struct intel_lvds_priv *)(intel_output + 1);
+	intel_output->dev_priv = lvds_priv;
+	/* create the scaling mode property */
+	drm_mode_create_scaling_mode_property(dev);
+	/*
+	 * the initial panel fitting mode will be FULL_ASPECT
+	 */
 
+	drm_connector_attach_property(&intel_output->base,
+				dev->mode_config.scaling_mode_property,
+				DRM_MODE_SCALE_ASPECT);
+	lvds_priv->fitting_mode = DRM_MODE_SCALE_ASPECT;
 	/*
 	 * LVDS discovery:
 	 * 1) check for EDID on DDC
@@ -521,5 +751,6 @@
 	if (intel_output->ddc_bus)
 		intel_i2c_destroy(intel_output->ddc_bus);
 	drm_connector_cleanup(connector);
-	kfree(connector);
+	drm_free(intel_output, sizeof(struct intel_output) +
+				sizeof(struct intel_lvds_priv), DRM_MEM_DRIVER);
 }
Index: linux-2.6/drivers/gpu/drm/i915/i915_reg.h
===================================================================
--- linux-2.6.orig/drivers/gpu/drm/i915/i915_reg.h	2009-03-20 11:39:38.000000000 +0800
+++ linux-2.6/drivers/gpu/drm/i915/i915_reg.h	2009-03-20 11:39:42.000000000 +0800
@@ -807,9 +807,26 @@
 #define   HORIZ_INTERP_MASK	(3 << 6)
 #define   HORIZ_AUTO_SCALE	(1 << 5)
 #define   PANEL_8TO6_DITHER_ENABLE (1 << 3)
+#define		PFIT_FILTER_FUZZY	(0 << 24)
+#define		PFIT_SCALING_AUTO	(0 << 26)
+#define		PFIT_SCALING_PROGRAMMED	(1 << 26)
+#define		PFIT_SCALING_PILLAR	(2 << 26)
+#define		PFIT_SCALING_LETTER	(3 << 26)
+
 #define PFIT_PGM_RATIOS	0x61234
 #define   PFIT_VERT_SCALE_MASK			0xfff00000
 #define   PFIT_HORIZ_SCALE_MASK			0x0000fff0
+/* Pre-965 */
+#define		PFIT_VERT_SCALE_SHIFT		20
+#define		PFIT_VERT_SCALE_MASK		0xfff00000
+#define		PFIT_HORIZ_SCALE_SHIFT		4
+#define		PFIT_HORIZ_SCALE_MASK		0x0000fff0
+/* 965+ */
+#define		PFIT_VERT_SCALE_SHIFT_965	16
+#define		PFIT_VERT_SCALE_MASK_965	0x1fff0000
+#define		PFIT_HORIZ_SCALE_SHIFT_965	0
+#define		PFIT_HORIZ_SCALE_MASK_965	0x00001fff
+
 #define PFIT_AUTO_RATIOS 0x61238
 
 /* Backlight control */





More information about the Intel-gfx mailing list