diff mbox

[1/2] drm/i915: factor out GMCH panel fitting code and use for eDP v2

Message ID 1366326349-14374-1-git-send-email-jbarnes@virtuousgeek.org (mailing list archive)
State New, archived
Headers show

Commit Message

Jesse Barnes April 18, 2013, 11:05 p.m. UTC
This gets the panel fitter working on eDP on VLV, and should also apply
to eDP panels on G4x chipsets (if we ever detect and mark an all-in-one
panel as eDP anyway).

A few cleanups are still possible on top of this, for example the LVDS
border control could be placed in the LVDS encoder structure and updated
based on the result of the panel fitter calculation.

Multi-pipe fitting isn't handled correctly either if we ever get a config
that wants to try the panel fitter on more than one output at a time.

v2: use pipe_config for storing pfit values (Daniel)
    add i9xx_pfit_enable function for use by 9xx and VLV (Daniel)

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
---
 drivers/gpu/drm/i915/intel_display.c |   33 ++++++
 drivers/gpu/drm/i915/intel_dp.c      |   11 +-
 drivers/gpu/drm/i915/intel_drv.h     |    6 +
 drivers/gpu/drm/i915/intel_lvds.c    |  208 +---------------------------------
 drivers/gpu/drm/i915/intel_panel.c   |  190 +++++++++++++++++++++++++++++++
 5 files changed, 240 insertions(+), 208 deletions(-)

Comments

Mika Kuoppala April 24, 2013, 3:05 p.m. UTC | #1
Jesse Barnes <jbarnes@virtuousgeek.org> writes:

> This gets the panel fitter working on eDP on VLV, and should also apply
> to eDP panels on G4x chipsets (if we ever detect and mark an all-in-one
> panel as eDP anyway).
>
> A few cleanups are still possible on top of this, for example the LVDS
> border control could be placed in the LVDS encoder structure and updated
> based on the result of the panel fitter calculation.
>
> Multi-pipe fitting isn't handled correctly either if we ever get a config
> that wants to try the panel fitter on more than one output at a time.
>
> v2: use pipe_config for storing pfit values (Daniel)
>     add i9xx_pfit_enable function for use by 9xx and VLV (Daniel)
>
> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
> ---
>  drivers/gpu/drm/i915/intel_display.c |   33 ++++++
>  drivers/gpu/drm/i915/intel_dp.c      |   11 +-
>  drivers/gpu/drm/i915/intel_drv.h     |    6 +
>  drivers/gpu/drm/i915/intel_lvds.c    |  208 +---------------------------------
>  drivers/gpu/drm/i915/intel_panel.c   |  190 +++++++++++++++++++++++++++++++
>  5 files changed, 240 insertions(+), 208 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 3fadd33..085a3ef 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -3692,6 +3692,33 @@ g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe)
>  	}
>  }
>  
> +static void i9xx_pfit_enable(struct intel_crtc *crtc)
> +{
> +	struct drm_device *dev = crtc->base.dev;
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	struct intel_crtc_config *pipe_config = &crtc->config;
> +
> +	if (!(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) ||
> +	      intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS)))
> +		return;
> +
> +	WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE);
> +	assert_pipe_disabled(dev_priv, crtc->pipe);
> +
> +	/*
> +	 * Enable automatic panel scaling so that non-native modes
> +	 * fill the screen.  The panel fitter should only be
> +	 * adjusted whilst the pipe is disabled, according to
> +	 * register description and PRM.
> +	 */
> +	DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
> +		      pipe_config->pfit_control,
> +		      pipe_config->pfit_pgm_ratios);
> +
> +	I915_WRITE(PFIT_PGM_RATIOS, pipe_config->pfit_pgm_ratios);
> +	I915_WRITE(PFIT_CONTROL, pipe_config->pfit_control);
> +}
> +
>  static void valleyview_crtc_enable(struct drm_crtc *crtc)
>  {
>  	struct drm_device *dev = crtc->dev;
> @@ -3725,6 +3752,9 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
>  	for_each_encoder_on_crtc(dev, crtc, encoder)
>  		encoder->enable(encoder);
>  
> +	/* Enable panel fitting for eDP */
> +	i9xx_pfit_enable(intel_crtc);
> +
>  	intel_enable_pipe(dev_priv, pipe, false);
>  	intel_enable_plane(dev_priv, plane, pipe);
>  
> @@ -3761,6 +3791,9 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
>  		if (encoder->pre_enable)
>  			encoder->pre_enable(encoder);
>  
> +	/* Enable panel fitting for LVDS */
> +	i9xx_pfit_enable(intel_crtc);
> +
>  	intel_enable_pipe(dev_priv, pipe, false);
>  	intel_enable_plane(dev_priv, plane, pipe);
>  	if (IS_G4X(dev))
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index 0580026..cb562ab 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -669,6 +669,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
>  	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
>  	struct drm_display_mode *mode = &pipe_config->requested_mode;
>  	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
> +	struct intel_crtc *intel_crtc = encoder->new_crtc;
>  	struct intel_connector *intel_connector = intel_dp->attached_connector;
>  	int lane_count, clock;
>  	int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
> @@ -685,9 +686,13 @@ intel_dp_compute_config(struct intel_encoder *encoder,
>  	if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
>  		intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
>  				       adjusted_mode);
> -		intel_pch_panel_fitting(dev,
> -					intel_connector->panel.fitting_mode,
> -					mode, adjusted_mode);
> +		if (!HAS_PCH_SPLIT(dev))
> +			intel_gmch_panel_fitting(intel_crtc, pipe_config,
> +						 intel_connector->panel.fitting_mode);
> +		else
> +			intel_pch_panel_fitting(dev,
> +						intel_connector->panel.fitting_mode,
> +						mode, adjusted_mode);
>  	}
>  	/* We need to take the panel's fixed mode into account. */
>  	target_clock = adjusted_mode->clock;
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 63264ed..8b45527 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -224,6 +224,9 @@ struct intel_crtc_config {
>  	int pixel_target_clock;
>  	/* Used by SDVO (and if we ever fix it, HDMI). */
>  	unsigned pixel_multiplier;
> +
> +	/* Panel fitter controls for gen2-gen4 + VLV */
> +	u32 pfit_control, pfit_pgm_ratios;
>  };
>  
>  struct intel_crtc {
> @@ -540,6 +543,9 @@ extern void intel_pch_panel_fitting(struct drm_device *dev,
>  				    int fitting_mode,
>  				    const struct drm_display_mode *mode,
>  				    struct drm_display_mode *adjusted_mode);
> +extern void intel_gmch_panel_fitting(struct intel_crtc *crtc,
> +				     struct intel_crtc_config *pipe_config,
> +				     int fitting_mode);
>  extern u32 intel_panel_get_max_backlight(struct drm_device *dev);
>  extern void intel_panel_set_backlight(struct drm_device *dev, u32 level);
>  extern int intel_panel_setup_backlight(struct drm_connector *connector);
> diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
> index 563f505..cb0066a 100644
> --- a/drivers/gpu/drm/i915/intel_lvds.c
> +++ b/drivers/gpu/drm/i915/intel_lvds.c
> @@ -49,8 +49,6 @@ struct intel_lvds_connector {
>  struct intel_lvds_encoder {
>  	struct intel_encoder base;
>  
> -	u32 pfit_control;
> -	u32 pfit_pgm_ratios;
>  	bool is_dual_link;
>  	u32 reg;
>  
> @@ -150,32 +148,6 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder)
>  	I915_WRITE(lvds_encoder->reg, temp);
>  }
>  
> -static void intel_pre_enable_lvds(struct intel_encoder *encoder)
> -{
> -	struct drm_device *dev = encoder->base.dev;
> -	struct intel_lvds_encoder *enc = to_lvds_encoder(&encoder->base);
> -	struct drm_i915_private *dev_priv = dev->dev_private;
> -
> -	if (HAS_PCH_SPLIT(dev) || !enc->pfit_control)
> -		return;
> -
> -	WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE);
> -	assert_pipe_disabled(dev_priv, to_intel_crtc(encoder->base.crtc)->pipe);
> -
> -	/*
> -	 * Enable automatic panel scaling so that non-native modes
> -	 * fill the screen.  The panel fitter should only be
> -	 * adjusted whilst the pipe is disabled, according to
> -	 * register description and PRM.
> -	 */
> -	DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
> -		      enc->pfit_control,
> -		      enc->pfit_pgm_ratios);
> -
> -	I915_WRITE(PFIT_PGM_RATIOS, enc->pfit_pgm_ratios);
> -	I915_WRITE(PFIT_CONTROL, enc->pfit_control);
> -}
> -
>  /**
>   * Sets the power state for the panel.
>   */
> @@ -244,62 +216,6 @@ static int intel_lvds_mode_valid(struct drm_connector *connector,
>  	return MODE_OK;
>  }
>  
> -static void
> -centre_horizontally(struct drm_display_mode *mode,
> -		    int width)
> -{
> -	u32 border, sync_pos, blank_width, sync_width;
> -
> -	/* keep the hsync and hblank widths constant */
> -	sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
> -	blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
> -	sync_pos = (blank_width - sync_width + 1) / 2;
> -
> -	border = (mode->hdisplay - width + 1) / 2;
> -	border += border & 1; /* make the border even */
> -
> -	mode->crtc_hdisplay = width;
> -	mode->crtc_hblank_start = width + border;
> -	mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
> -
> -	mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
> -	mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
> -}
> -
> -static void
> -centre_vertically(struct drm_display_mode *mode,
> -		  int height)
> -{
> -	u32 border, sync_pos, blank_width, sync_width;
> -
> -	/* keep the vsync and vblank widths constant */
> -	sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
> -	blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
> -	sync_pos = (blank_width - sync_width + 1) / 2;
> -
> -	border = (mode->vdisplay - height + 1) / 2;
> -
> -	mode->crtc_vdisplay = height;
> -	mode->crtc_vblank_start = height + border;
> -	mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
> -
> -	mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
> -	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 bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
>  				      struct intel_crtc_config *pipe_config)
>  {
> @@ -312,7 +228,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
>  	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
>  	struct drm_display_mode *mode = &pipe_config->requested_mode;
>  	struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc;
> -	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
>  	unsigned int lvds_bpp;
>  	int pipe;
>  
> @@ -352,18 +267,11 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
>  					intel_connector->panel.fitting_mode,
>  					mode, adjusted_mode);
>  		return true;
> +	} else {
> +		intel_gmch_panel_fitting(intel_crtc, pipe_config,
> +					 intel_connector->panel.fitting_mode);
>  	}
>  
> -	/* Native modes don't need fitting */
> -	if (adjusted_mode->hdisplay == mode->hdisplay &&
> -	    adjusted_mode->vdisplay == mode->vdisplay)
> -		goto out;
> -
> -	/* 965+ wants fuzzy fitting */
> -	if (INTEL_INFO(dev)->gen >= 4)
> -		pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
> -				 PFIT_FILTER_FUZZY);
> -
>  	/*
>  	 * Enable automatic panel scaling for non-native modes so that they fill
>  	 * the screen.  Should be enabled before the pipe is enabled, according
> @@ -376,111 +284,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
>  	drm_mode_set_crtcinfo(adjusted_mode, 0);
>  	pipe_config->timings_set = true;
>  
> -	switch (intel_connector->panel.fitting_mode) {
> -	case DRM_MODE_SCALE_CENTER:
> -		/*
> -		 * For centered modes, we have to calculate border widths &
> -		 * heights and modify the values programmed into the CRTC.
> -		 */
> -		centre_horizontally(adjusted_mode, mode->hdisplay);
> -		centre_vertically(adjusted_mode, mode->vdisplay);
> -		border = LVDS_BORDER_ENABLE;
> -		break;
> -
> -	case DRM_MODE_SCALE_ASPECT:
> -		/* Scale but preserve the aspect ratio */
> -		if (INTEL_INFO(dev)->gen >= 4) {
> -			u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
> -			u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
> -
> -			/* 965+ is easy, it does everything in hw */
> -			if (scaled_width > scaled_height)
> -				pfit_control |= PFIT_ENABLE | PFIT_SCALING_PILLAR;
> -			else if (scaled_width < scaled_height)
> -				pfit_control |= PFIT_ENABLE | PFIT_SCALING_LETTER;
> -			else if (adjusted_mode->hdisplay != mode->hdisplay)
> -				pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
> -		} else {
> -			u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
> -			u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
> -			/*
> -			 * For earlier chips we have to calculate the scaling
> -			 * ratio by hand and program it into the
> -			 * PFIT_PGM_RATIO register
> -			 */
> -			if (scaled_width > scaled_height) { /* pillar */
> -				centre_horizontally(adjusted_mode, scaled_height / mode->vdisplay);
> -
> -				border = LVDS_BORDER_ENABLE;
> -				if (mode->vdisplay != adjusted_mode->vdisplay) {
> -					u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
> -					pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
> -							    bits << PFIT_VERT_SCALE_SHIFT);
> -					pfit_control |= (PFIT_ENABLE |
> -							 VERT_INTERP_BILINEAR |
> -							 HORIZ_INTERP_BILINEAR);
> -				}
> -			} else if (scaled_width < scaled_height) { /* letter */
> -				centre_vertically(adjusted_mode, scaled_width / mode->hdisplay);
> -
> -				border = LVDS_BORDER_ENABLE;
> -				if (mode->hdisplay != adjusted_mode->hdisplay) {
> -					u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
> -					pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
> -							    bits << PFIT_VERT_SCALE_SHIFT);
> -					pfit_control |= (PFIT_ENABLE |
> -							 VERT_INTERP_BILINEAR |
> -							 HORIZ_INTERP_BILINEAR);
> -				}
> -			} else
> -				/* Aspects match, Let hw scale both directions */
> -				pfit_control |= (PFIT_ENABLE |
> -						 VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
> -						 VERT_INTERP_BILINEAR |
> -						 HORIZ_INTERP_BILINEAR);
> -		}
> -		break;
> -
> -	case DRM_MODE_SCALE_FULLSCREEN:
> -		/*
> -		 * Full scaling, even if it changes the aspect ratio.
> -		 * Fortunately this is all done for us in hw.
> -		 */
> -		if (mode->vdisplay != adjusted_mode->vdisplay ||
> -		    mode->hdisplay != adjusted_mode->hdisplay) {
> -			pfit_control |= PFIT_ENABLE;
> -			if (INTEL_INFO(dev)->gen >= 4)
> -				pfit_control |= PFIT_SCALING_AUTO;
> -			else
> -				pfit_control |= (VERT_AUTO_SCALE |
> -						 VERT_INTERP_BILINEAR |
> -						 HORIZ_AUTO_SCALE |
> -						 HORIZ_INTERP_BILINEAR);
> -		}
> -		break;
> -
> -	default:
> -		break;
> -	}
> -
> -out:
> -	/* If not enabling scaling, be consistent and always use 0. */
> -	if ((pfit_control & PFIT_ENABLE) == 0) {
> -		pfit_control = 0;
> -		pfit_pgm_ratios = 0;
> -	}
> -
> -	/* Make sure pre-965 set dither correctly */
> -	if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither)
> -		pfit_control |= PANEL_8TO6_DITHER_ENABLE;
> -
> -	if (pfit_control != lvds_encoder->pfit_control ||
> -	    pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) {
> -		lvds_encoder->pfit_control = pfit_control;
> -		lvds_encoder->pfit_pgm_ratios = pfit_pgm_ratios;
> -	}
> -	dev_priv->lvds_border_bits = border;
> -
>  	/*
>  	 * XXX: It would be nice to support lower refresh rates on the
>  	 * panels to reduce power consumption, and perhaps match the
> @@ -1110,10 +913,6 @@ bool intel_lvds_init(struct drm_device *dev)
>  
>  	lvds_encoder->attached_connector = lvds_connector;
>  
> -	if (!HAS_PCH_SPLIT(dev)) {
> -		lvds_encoder->pfit_control = I915_READ(PFIT_CONTROL);
> -	}
> -
>  	intel_encoder = &lvds_encoder->base;
>  	encoder = &intel_encoder->base;
>  	intel_connector = &lvds_connector->base;
> @@ -1125,7 +924,6 @@ bool intel_lvds_init(struct drm_device *dev)
>  			 DRM_MODE_ENCODER_LVDS);
>  
>  	intel_encoder->enable = intel_enable_lvds;
> -	intel_encoder->pre_enable = intel_pre_enable_lvds;
>  	intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds;
>  	intel_encoder->compute_config = intel_lvds_compute_config;
>  	intel_encoder->disable = intel_disable_lvds;
> diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
> index eb5e6e9..d434a8d 100644
> --- a/drivers/gpu/drm/i915/intel_panel.c
> +++ b/drivers/gpu/drm/i915/intel_panel.c
> @@ -117,6 +117,196 @@ done:
>  	dev_priv->pch_pf_size = (width << 16) | height;
>  }
>  
> +static void
> +centre_horizontally(struct drm_display_mode *mode,
> +		    int width)
> +{
> +	u32 border, sync_pos, blank_width, sync_width;
> +
> +	/* keep the hsync and hblank widths constant */
> +	sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
> +	blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
> +	sync_pos = (blank_width - sync_width + 1) / 2;
> +
> +	border = (mode->hdisplay - width + 1) / 2;
> +	border += border & 1; /* make the border even */
> +
> +	mode->crtc_hdisplay = width;
> +	mode->crtc_hblank_start = width + border;
> +	mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
> +
> +	mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
> +	mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
> +}
> +
> +static void
> +centre_vertically(struct drm_display_mode *mode,
> +		  int height)
> +{
> +	u32 border, sync_pos, blank_width, sync_width;
> +
> +	/* keep the vsync and vblank widths constant */
> +	sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
> +	blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
> +	sync_pos = (blank_width - sync_width + 1) / 2;
> +
> +	border = (mode->vdisplay - height + 1) / 2;
> +
> +	mode->crtc_vdisplay = height;
> +	mode->crtc_vblank_start = height + border;
> +	mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
> +
> +	mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
> +	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;
> +}
> +
> +void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
> +			      struct intel_crtc_config *pipe_config,
> +			      int fitting_mode)
> +{
> +	struct drm_device *dev = intel_crtc->base.dev;
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
> +	struct drm_display_mode *mode, *adjusted_mode;
> +
> +	mode = &pipe_config->requested_mode;
> +	adjusted_mode = &pipe_config->adjusted_mode;
> +
> +	/* Native modes don't need fitting */
> +	if (adjusted_mode->hdisplay == mode->hdisplay &&
> +	    adjusted_mode->vdisplay == mode->vdisplay)
> +		goto out;
> +
> +	switch (fitting_mode) {
> +	case DRM_MODE_SCALE_CENTER:
> +		/*
> +		 * For centered modes, we have to calculate border widths &
> +		 * heights and modify the values programmed into the CRTC.
> +		 */
> +		centre_horizontally(adjusted_mode, mode->hdisplay);
> +		centre_vertically(adjusted_mode, mode->vdisplay);
> +		break;
> +	case DRM_MODE_SCALE_ASPECT:
> +		/* Scale but preserve the aspect ratio */
> +		if (INTEL_INFO(dev)->gen >= 4) {
> +			u32 scaled_width = adjusted_mode->hdisplay *
> +				mode->vdisplay;
> +			u32 scaled_height = mode->hdisplay *
> +				adjusted_mode->vdisplay;
> +
> +			/* 965+ is easy, it does everything in hw */
> +			if (scaled_width > scaled_height)
> +				pfit_control |= PFIT_ENABLE |
> +					PFIT_SCALING_PILLAR;
> +			else if (scaled_width < scaled_height)
> +				pfit_control |= PFIT_ENABLE |
> +					PFIT_SCALING_LETTER;
> +			else if (adjusted_mode->hdisplay != mode->hdisplay)
> +				pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
> +		} else {
> +			u32 scaled_width = adjusted_mode->hdisplay *
> +				mode->vdisplay;
> +			u32 scaled_height = mode->hdisplay *
> +				adjusted_mode->vdisplay;
> +			/*
> +			 * For earlier chips we have to calculate the scaling
> +			 * ratio by hand and program it into the
> +			 * PFIT_PGM_RATIO register
> +			 */
> +			if (scaled_width > scaled_height) { /* pillar */
> +				centre_horizontally(adjusted_mode,
> +						    scaled_height /
> +						    mode->vdisplay);
> +
> +				border = LVDS_BORDER_ENABLE;
> +				if (mode->vdisplay != adjusted_mode->vdisplay) {
> +					u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
> +					pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
> +							    bits << PFIT_VERT_SCALE_SHIFT);
> +					pfit_control |= (PFIT_ENABLE |
> +							 VERT_INTERP_BILINEAR |
> +							 HORIZ_INTERP_BILINEAR);
> +				}
> +			} else if (scaled_width < scaled_height) { /* letter */
> +				centre_vertically(adjusted_mode,
> +						  scaled_width /
> +						  mode->hdisplay);
> +
> +				border = LVDS_BORDER_ENABLE;
> +				if (mode->hdisplay != adjusted_mode->hdisplay) {
> +					u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
> +					pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
> +							    bits << PFIT_VERT_SCALE_SHIFT);
> +					pfit_control |= (PFIT_ENABLE |
> +							 VERT_INTERP_BILINEAR |
> +							 HORIZ_INTERP_BILINEAR);
> +				}
> +			} else {
> +				/* Aspects match, Let hw scale both directions */
> +				pfit_control |= (PFIT_ENABLE |
> +						 VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
> +						 VERT_INTERP_BILINEAR |
> +						 HORIZ_INTERP_BILINEAR);
> +			}
> +		}
> +		break;
> +	default:
> +	case DRM_MODE_SCALE_FULLSCREEN:
        ^^

This is different than it was in lvds_compute_config. If we get called
with DRM_MODE_SCALE_NONE we go to fullscreen instead.
Is this intentional?

--Mika

> +		/*
> +		 * Full scaling, even if it changes the aspect ratio.
> +		 * Fortunately this is all done for us in hw.
> +		 */
> +		if (mode->vdisplay != adjusted_mode->vdisplay ||
> +		    mode->hdisplay != adjusted_mode->hdisplay) {
> +			pfit_control |= PFIT_ENABLE;
> +			if (INTEL_INFO(dev)->gen >= 4)
> +				pfit_control |= PFIT_SCALING_AUTO;
> +			else
> +				pfit_control |= (VERT_AUTO_SCALE |
> +						 VERT_INTERP_BILINEAR |
> +						 HORIZ_AUTO_SCALE |
> +						 HORIZ_INTERP_BILINEAR);
> +		}
> +		break;
> +	}
> +
> +	/* 965+ wants fuzzy fitting */
> +	/* FIXME: handle multiple panels by failing gracefully */
> +	if (INTEL_INFO(dev)->gen >= 4)
> +		pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
> +				 PFIT_FILTER_FUZZY);
> +
> +out:
> +	if ((pfit_control & PFIT_ENABLE) == 0) {
> +		pfit_control = 0;
> +		pfit_pgm_ratios = 0;
> +	}
> +
> +	/* Make sure pre-965 set dither correctly */
> +	if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither)
> +		pfit_control |= PANEL_8TO6_DITHER_ENABLE;
> +
> +	if (pfit_control != pipe_config->pfit_control ||
> +	    pfit_pgm_ratios != pipe_config->pfit_pgm_ratios) {
> +		pipe_config->pfit_control = pfit_control;
> +		pipe_config->pfit_pgm_ratios = pfit_pgm_ratios;
> +	}
> +	dev_priv->lvds_border_bits = border;
> +}
> +
>  static int is_backlight_combination_mode(struct drm_device *dev)
>  {
>  	struct drm_i915_private *dev_priv = dev->dev_private;
> -- 
> 1.7.10.4
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Jesse Barnes April 24, 2013, 3:29 p.m. UTC | #2
On Wed, 24 Apr 2013 18:05:54 +0300
Mika Kuoppala <mika.kuoppala@linux.intel.com> wrote:

> This is different than it was in lvds_compute_config. If we get called
> with DRM_MODE_SCALE_NONE we go to fullscreen instead.
> Is this intentional?

Yeah, figured I'd make it identical to the eDP case.  But we could
change that one instead if you have a preference.
Daniel Vetter April 24, 2013, 4:56 p.m. UTC | #3
On Wed, Apr 24, 2013 at 08:29:21AM -0700, Jesse Barnes wrote:
> On Wed, 24 Apr 2013 18:05:54 +0300
> Mika Kuoppala <mika.kuoppala@linux.intel.com> wrote:
> 
> > This is different than it was in lvds_compute_config. If we get called
> > with DRM_MODE_SCALE_NONE we go to fullscreen instead.
> > Is this intentional?
> 
> Yeah, figured I'd make it identical to the eDP case.  But we could
> change that one instead if you have a preference.

SCALE_NONE is rejected in both lvds and edp output's set_property. So not
relevant. Maybe we should smash a WARN into the default case instead?
-Daniel
Mika Kuoppala April 25, 2013, 2:38 p.m. UTC | #4
Daniel Vetter <daniel@ffwll.ch> writes:

> On Wed, Apr 24, 2013 at 08:29:21AM -0700, Jesse Barnes wrote:
>> On Wed, 24 Apr 2013 18:05:54 +0300
>> Mika Kuoppala <mika.kuoppala@linux.intel.com> wrote:
>> 
>> > This is different than it was in lvds_compute_config. If we get called
>> > with DRM_MODE_SCALE_NONE we go to fullscreen instead.
>> > Is this intentional?
>> 
>> Yeah, figured I'd make it identical to the eDP case.  But we could
>> change that one instead if you have a preference.
>
> SCALE_NONE is rejected in both lvds and edp output's set_property. So not
> relevant. Maybe we should smash a WARN into the default case instead?

I missed the SCALE_NONE. drm seems to do checking that no values
outside the enum set gets through but warn wouldn't hurt.
with or without warning added:

Reviewed-by: Mika Kuoppala <mika.kuoppala@intel.com>

> -Daniel
> -- 
> Daniel Vetter
> Software Engineer, Intel Corporation
> +41 (0) 79 365 57 48 - http://blog.ffwll.ch
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 3fadd33..085a3ef 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3692,6 +3692,33 @@  g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe)
 	}
 }
 
+static void i9xx_pfit_enable(struct intel_crtc *crtc)
+{
+	struct drm_device *dev = crtc->base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_crtc_config *pipe_config = &crtc->config;
+
+	if (!(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) ||
+	      intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS)))
+		return;
+
+	WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE);
+	assert_pipe_disabled(dev_priv, crtc->pipe);
+
+	/*
+	 * Enable automatic panel scaling so that non-native modes
+	 * fill the screen.  The panel fitter should only be
+	 * adjusted whilst the pipe is disabled, according to
+	 * register description and PRM.
+	 */
+	DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
+		      pipe_config->pfit_control,
+		      pipe_config->pfit_pgm_ratios);
+
+	I915_WRITE(PFIT_PGM_RATIOS, pipe_config->pfit_pgm_ratios);
+	I915_WRITE(PFIT_CONTROL, pipe_config->pfit_control);
+}
+
 static void valleyview_crtc_enable(struct drm_crtc *crtc)
 {
 	struct drm_device *dev = crtc->dev;
@@ -3725,6 +3752,9 @@  static void valleyview_crtc_enable(struct drm_crtc *crtc)
 	for_each_encoder_on_crtc(dev, crtc, encoder)
 		encoder->enable(encoder);
 
+	/* Enable panel fitting for eDP */
+	i9xx_pfit_enable(intel_crtc);
+
 	intel_enable_pipe(dev_priv, pipe, false);
 	intel_enable_plane(dev_priv, plane, pipe);
 
@@ -3761,6 +3791,9 @@  static void i9xx_crtc_enable(struct drm_crtc *crtc)
 		if (encoder->pre_enable)
 			encoder->pre_enable(encoder);
 
+	/* Enable panel fitting for LVDS */
+	i9xx_pfit_enable(intel_crtc);
+
 	intel_enable_pipe(dev_priv, pipe, false);
 	intel_enable_plane(dev_priv, plane, pipe);
 	if (IS_G4X(dev))
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 0580026..cb562ab 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -669,6 +669,7 @@  intel_dp_compute_config(struct intel_encoder *encoder,
 	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
 	struct drm_display_mode *mode = &pipe_config->requested_mode;
 	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
+	struct intel_crtc *intel_crtc = encoder->new_crtc;
 	struct intel_connector *intel_connector = intel_dp->attached_connector;
 	int lane_count, clock;
 	int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
@@ -685,9 +686,13 @@  intel_dp_compute_config(struct intel_encoder *encoder,
 	if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
 		intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
 				       adjusted_mode);
-		intel_pch_panel_fitting(dev,
-					intel_connector->panel.fitting_mode,
-					mode, adjusted_mode);
+		if (!HAS_PCH_SPLIT(dev))
+			intel_gmch_panel_fitting(intel_crtc, pipe_config,
+						 intel_connector->panel.fitting_mode);
+		else
+			intel_pch_panel_fitting(dev,
+						intel_connector->panel.fitting_mode,
+						mode, adjusted_mode);
 	}
 	/* We need to take the panel's fixed mode into account. */
 	target_clock = adjusted_mode->clock;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 63264ed..8b45527 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -224,6 +224,9 @@  struct intel_crtc_config {
 	int pixel_target_clock;
 	/* Used by SDVO (and if we ever fix it, HDMI). */
 	unsigned pixel_multiplier;
+
+	/* Panel fitter controls for gen2-gen4 + VLV */
+	u32 pfit_control, pfit_pgm_ratios;
 };
 
 struct intel_crtc {
@@ -540,6 +543,9 @@  extern void intel_pch_panel_fitting(struct drm_device *dev,
 				    int fitting_mode,
 				    const struct drm_display_mode *mode,
 				    struct drm_display_mode *adjusted_mode);
+extern void intel_gmch_panel_fitting(struct intel_crtc *crtc,
+				     struct intel_crtc_config *pipe_config,
+				     int fitting_mode);
 extern u32 intel_panel_get_max_backlight(struct drm_device *dev);
 extern void intel_panel_set_backlight(struct drm_device *dev, u32 level);
 extern int intel_panel_setup_backlight(struct drm_connector *connector);
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 563f505..cb0066a 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -49,8 +49,6 @@  struct intel_lvds_connector {
 struct intel_lvds_encoder {
 	struct intel_encoder base;
 
-	u32 pfit_control;
-	u32 pfit_pgm_ratios;
 	bool is_dual_link;
 	u32 reg;
 
@@ -150,32 +148,6 @@  static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder)
 	I915_WRITE(lvds_encoder->reg, temp);
 }
 
-static void intel_pre_enable_lvds(struct intel_encoder *encoder)
-{
-	struct drm_device *dev = encoder->base.dev;
-	struct intel_lvds_encoder *enc = to_lvds_encoder(&encoder->base);
-	struct drm_i915_private *dev_priv = dev->dev_private;
-
-	if (HAS_PCH_SPLIT(dev) || !enc->pfit_control)
-		return;
-
-	WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE);
-	assert_pipe_disabled(dev_priv, to_intel_crtc(encoder->base.crtc)->pipe);
-
-	/*
-	 * Enable automatic panel scaling so that non-native modes
-	 * fill the screen.  The panel fitter should only be
-	 * adjusted whilst the pipe is disabled, according to
-	 * register description and PRM.
-	 */
-	DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
-		      enc->pfit_control,
-		      enc->pfit_pgm_ratios);
-
-	I915_WRITE(PFIT_PGM_RATIOS, enc->pfit_pgm_ratios);
-	I915_WRITE(PFIT_CONTROL, enc->pfit_control);
-}
-
 /**
  * Sets the power state for the panel.
  */
@@ -244,62 +216,6 @@  static int intel_lvds_mode_valid(struct drm_connector *connector,
 	return MODE_OK;
 }
 
-static void
-centre_horizontally(struct drm_display_mode *mode,
-		    int width)
-{
-	u32 border, sync_pos, blank_width, sync_width;
-
-	/* keep the hsync and hblank widths constant */
-	sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
-	blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
-	sync_pos = (blank_width - sync_width + 1) / 2;
-
-	border = (mode->hdisplay - width + 1) / 2;
-	border += border & 1; /* make the border even */
-
-	mode->crtc_hdisplay = width;
-	mode->crtc_hblank_start = width + border;
-	mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
-
-	mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
-	mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
-}
-
-static void
-centre_vertically(struct drm_display_mode *mode,
-		  int height)
-{
-	u32 border, sync_pos, blank_width, sync_width;
-
-	/* keep the vsync and vblank widths constant */
-	sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
-	blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
-	sync_pos = (blank_width - sync_width + 1) / 2;
-
-	border = (mode->vdisplay - height + 1) / 2;
-
-	mode->crtc_vdisplay = height;
-	mode->crtc_vblank_start = height + border;
-	mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
-
-	mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
-	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 bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
 				      struct intel_crtc_config *pipe_config)
 {
@@ -312,7 +228,6 @@  static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
 	struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
 	struct drm_display_mode *mode = &pipe_config->requested_mode;
 	struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc;
-	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
 	unsigned int lvds_bpp;
 	int pipe;
 
@@ -352,18 +267,11 @@  static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
 					intel_connector->panel.fitting_mode,
 					mode, adjusted_mode);
 		return true;
+	} else {
+		intel_gmch_panel_fitting(intel_crtc, pipe_config,
+					 intel_connector->panel.fitting_mode);
 	}
 
-	/* Native modes don't need fitting */
-	if (adjusted_mode->hdisplay == mode->hdisplay &&
-	    adjusted_mode->vdisplay == mode->vdisplay)
-		goto out;
-
-	/* 965+ wants fuzzy fitting */
-	if (INTEL_INFO(dev)->gen >= 4)
-		pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
-				 PFIT_FILTER_FUZZY);
-
 	/*
 	 * Enable automatic panel scaling for non-native modes so that they fill
 	 * the screen.  Should be enabled before the pipe is enabled, according
@@ -376,111 +284,6 @@  static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
 	drm_mode_set_crtcinfo(adjusted_mode, 0);
 	pipe_config->timings_set = true;
 
-	switch (intel_connector->panel.fitting_mode) {
-	case DRM_MODE_SCALE_CENTER:
-		/*
-		 * For centered modes, we have to calculate border widths &
-		 * heights and modify the values programmed into the CRTC.
-		 */
-		centre_horizontally(adjusted_mode, mode->hdisplay);
-		centre_vertically(adjusted_mode, mode->vdisplay);
-		border = LVDS_BORDER_ENABLE;
-		break;
-
-	case DRM_MODE_SCALE_ASPECT:
-		/* Scale but preserve the aspect ratio */
-		if (INTEL_INFO(dev)->gen >= 4) {
-			u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
-			u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
-
-			/* 965+ is easy, it does everything in hw */
-			if (scaled_width > scaled_height)
-				pfit_control |= PFIT_ENABLE | PFIT_SCALING_PILLAR;
-			else if (scaled_width < scaled_height)
-				pfit_control |= PFIT_ENABLE | PFIT_SCALING_LETTER;
-			else if (adjusted_mode->hdisplay != mode->hdisplay)
-				pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
-		} else {
-			u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
-			u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
-			/*
-			 * For earlier chips we have to calculate the scaling
-			 * ratio by hand and program it into the
-			 * PFIT_PGM_RATIO register
-			 */
-			if (scaled_width > scaled_height) { /* pillar */
-				centre_horizontally(adjusted_mode, scaled_height / mode->vdisplay);
-
-				border = LVDS_BORDER_ENABLE;
-				if (mode->vdisplay != adjusted_mode->vdisplay) {
-					u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
-					pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
-							    bits << PFIT_VERT_SCALE_SHIFT);
-					pfit_control |= (PFIT_ENABLE |
-							 VERT_INTERP_BILINEAR |
-							 HORIZ_INTERP_BILINEAR);
-				}
-			} else if (scaled_width < scaled_height) { /* letter */
-				centre_vertically(adjusted_mode, scaled_width / mode->hdisplay);
-
-				border = LVDS_BORDER_ENABLE;
-				if (mode->hdisplay != adjusted_mode->hdisplay) {
-					u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
-					pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
-							    bits << PFIT_VERT_SCALE_SHIFT);
-					pfit_control |= (PFIT_ENABLE |
-							 VERT_INTERP_BILINEAR |
-							 HORIZ_INTERP_BILINEAR);
-				}
-			} else
-				/* Aspects match, Let hw scale both directions */
-				pfit_control |= (PFIT_ENABLE |
-						 VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
-						 VERT_INTERP_BILINEAR |
-						 HORIZ_INTERP_BILINEAR);
-		}
-		break;
-
-	case DRM_MODE_SCALE_FULLSCREEN:
-		/*
-		 * Full scaling, even if it changes the aspect ratio.
-		 * Fortunately this is all done for us in hw.
-		 */
-		if (mode->vdisplay != adjusted_mode->vdisplay ||
-		    mode->hdisplay != adjusted_mode->hdisplay) {
-			pfit_control |= PFIT_ENABLE;
-			if (INTEL_INFO(dev)->gen >= 4)
-				pfit_control |= PFIT_SCALING_AUTO;
-			else
-				pfit_control |= (VERT_AUTO_SCALE |
-						 VERT_INTERP_BILINEAR |
-						 HORIZ_AUTO_SCALE |
-						 HORIZ_INTERP_BILINEAR);
-		}
-		break;
-
-	default:
-		break;
-	}
-
-out:
-	/* If not enabling scaling, be consistent and always use 0. */
-	if ((pfit_control & PFIT_ENABLE) == 0) {
-		pfit_control = 0;
-		pfit_pgm_ratios = 0;
-	}
-
-	/* Make sure pre-965 set dither correctly */
-	if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither)
-		pfit_control |= PANEL_8TO6_DITHER_ENABLE;
-
-	if (pfit_control != lvds_encoder->pfit_control ||
-	    pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) {
-		lvds_encoder->pfit_control = pfit_control;
-		lvds_encoder->pfit_pgm_ratios = pfit_pgm_ratios;
-	}
-	dev_priv->lvds_border_bits = border;
-
 	/*
 	 * XXX: It would be nice to support lower refresh rates on the
 	 * panels to reduce power consumption, and perhaps match the
@@ -1110,10 +913,6 @@  bool intel_lvds_init(struct drm_device *dev)
 
 	lvds_encoder->attached_connector = lvds_connector;
 
-	if (!HAS_PCH_SPLIT(dev)) {
-		lvds_encoder->pfit_control = I915_READ(PFIT_CONTROL);
-	}
-
 	intel_encoder = &lvds_encoder->base;
 	encoder = &intel_encoder->base;
 	intel_connector = &lvds_connector->base;
@@ -1125,7 +924,6 @@  bool intel_lvds_init(struct drm_device *dev)
 			 DRM_MODE_ENCODER_LVDS);
 
 	intel_encoder->enable = intel_enable_lvds;
-	intel_encoder->pre_enable = intel_pre_enable_lvds;
 	intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds;
 	intel_encoder->compute_config = intel_lvds_compute_config;
 	intel_encoder->disable = intel_disable_lvds;
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index eb5e6e9..d434a8d 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -117,6 +117,196 @@  done:
 	dev_priv->pch_pf_size = (width << 16) | height;
 }
 
+static void
+centre_horizontally(struct drm_display_mode *mode,
+		    int width)
+{
+	u32 border, sync_pos, blank_width, sync_width;
+
+	/* keep the hsync and hblank widths constant */
+	sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
+	blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
+	sync_pos = (blank_width - sync_width + 1) / 2;
+
+	border = (mode->hdisplay - width + 1) / 2;
+	border += border & 1; /* make the border even */
+
+	mode->crtc_hdisplay = width;
+	mode->crtc_hblank_start = width + border;
+	mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
+
+	mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
+	mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
+}
+
+static void
+centre_vertically(struct drm_display_mode *mode,
+		  int height)
+{
+	u32 border, sync_pos, blank_width, sync_width;
+
+	/* keep the vsync and vblank widths constant */
+	sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
+	blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
+	sync_pos = (blank_width - sync_width + 1) / 2;
+
+	border = (mode->vdisplay - height + 1) / 2;
+
+	mode->crtc_vdisplay = height;
+	mode->crtc_vblank_start = height + border;
+	mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
+
+	mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
+	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;
+}
+
+void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
+			      struct intel_crtc_config *pipe_config,
+			      int fitting_mode)
+{
+	struct drm_device *dev = intel_crtc->base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
+	struct drm_display_mode *mode, *adjusted_mode;
+
+	mode = &pipe_config->requested_mode;
+	adjusted_mode = &pipe_config->adjusted_mode;
+
+	/* Native modes don't need fitting */
+	if (adjusted_mode->hdisplay == mode->hdisplay &&
+	    adjusted_mode->vdisplay == mode->vdisplay)
+		goto out;
+
+	switch (fitting_mode) {
+	case DRM_MODE_SCALE_CENTER:
+		/*
+		 * For centered modes, we have to calculate border widths &
+		 * heights and modify the values programmed into the CRTC.
+		 */
+		centre_horizontally(adjusted_mode, mode->hdisplay);
+		centre_vertically(adjusted_mode, mode->vdisplay);
+		break;
+	case DRM_MODE_SCALE_ASPECT:
+		/* Scale but preserve the aspect ratio */
+		if (INTEL_INFO(dev)->gen >= 4) {
+			u32 scaled_width = adjusted_mode->hdisplay *
+				mode->vdisplay;
+			u32 scaled_height = mode->hdisplay *
+				adjusted_mode->vdisplay;
+
+			/* 965+ is easy, it does everything in hw */
+			if (scaled_width > scaled_height)
+				pfit_control |= PFIT_ENABLE |
+					PFIT_SCALING_PILLAR;
+			else if (scaled_width < scaled_height)
+				pfit_control |= PFIT_ENABLE |
+					PFIT_SCALING_LETTER;
+			else if (adjusted_mode->hdisplay != mode->hdisplay)
+				pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
+		} else {
+			u32 scaled_width = adjusted_mode->hdisplay *
+				mode->vdisplay;
+			u32 scaled_height = mode->hdisplay *
+				adjusted_mode->vdisplay;
+			/*
+			 * For earlier chips we have to calculate the scaling
+			 * ratio by hand and program it into the
+			 * PFIT_PGM_RATIO register
+			 */
+			if (scaled_width > scaled_height) { /* pillar */
+				centre_horizontally(adjusted_mode,
+						    scaled_height /
+						    mode->vdisplay);
+
+				border = LVDS_BORDER_ENABLE;
+				if (mode->vdisplay != adjusted_mode->vdisplay) {
+					u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
+					pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
+							    bits << PFIT_VERT_SCALE_SHIFT);
+					pfit_control |= (PFIT_ENABLE |
+							 VERT_INTERP_BILINEAR |
+							 HORIZ_INTERP_BILINEAR);
+				}
+			} else if (scaled_width < scaled_height) { /* letter */
+				centre_vertically(adjusted_mode,
+						  scaled_width /
+						  mode->hdisplay);
+
+				border = LVDS_BORDER_ENABLE;
+				if (mode->hdisplay != adjusted_mode->hdisplay) {
+					u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
+					pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
+							    bits << PFIT_VERT_SCALE_SHIFT);
+					pfit_control |= (PFIT_ENABLE |
+							 VERT_INTERP_BILINEAR |
+							 HORIZ_INTERP_BILINEAR);
+				}
+			} else {
+				/* Aspects match, Let hw scale both directions */
+				pfit_control |= (PFIT_ENABLE |
+						 VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
+						 VERT_INTERP_BILINEAR |
+						 HORIZ_INTERP_BILINEAR);
+			}
+		}
+		break;
+	default:
+	case DRM_MODE_SCALE_FULLSCREEN:
+		/*
+		 * Full scaling, even if it changes the aspect ratio.
+		 * Fortunately this is all done for us in hw.
+		 */
+		if (mode->vdisplay != adjusted_mode->vdisplay ||
+		    mode->hdisplay != adjusted_mode->hdisplay) {
+			pfit_control |= PFIT_ENABLE;
+			if (INTEL_INFO(dev)->gen >= 4)
+				pfit_control |= PFIT_SCALING_AUTO;
+			else
+				pfit_control |= (VERT_AUTO_SCALE |
+						 VERT_INTERP_BILINEAR |
+						 HORIZ_AUTO_SCALE |
+						 HORIZ_INTERP_BILINEAR);
+		}
+		break;
+	}
+
+	/* 965+ wants fuzzy fitting */
+	/* FIXME: handle multiple panels by failing gracefully */
+	if (INTEL_INFO(dev)->gen >= 4)
+		pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
+				 PFIT_FILTER_FUZZY);
+
+out:
+	if ((pfit_control & PFIT_ENABLE) == 0) {
+		pfit_control = 0;
+		pfit_pgm_ratios = 0;
+	}
+
+	/* Make sure pre-965 set dither correctly */
+	if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither)
+		pfit_control |= PANEL_8TO6_DITHER_ENABLE;
+
+	if (pfit_control != pipe_config->pfit_control ||
+	    pfit_pgm_ratios != pipe_config->pfit_pgm_ratios) {
+		pipe_config->pfit_control = pfit_control;
+		pipe_config->pfit_pgm_ratios = pfit_pgm_ratios;
+	}
+	dev_priv->lvds_border_bits = border;
+}
+
 static int is_backlight_combination_mode(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;