]> rtime.felk.cvut.cz Git - linux-imx.git/commitdiff
drm/i915: factor out GMCH panel fitting code and use for eDP v3
authorJesse Barnes <jbarnes@virtuousgeek.org>
Thu, 25 Apr 2013 19:55:01 +0000 (12:55 -0700)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 25 Apr 2013 20:16:22 +0000 (22:16 +0200)
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)
v3: fixup conflicts and lvds_dither check

Reviewed-by: Mika Kuoppala <mika.kuoppala@intel.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
[danvet: fix up botched conflict resolution from Jesse:
- border = LVDS_BORDER_ENABLE was lost for CENTER scaling
- comment about gen2/3 panel fitter scaling was lost
- dev_priv->lvds_dither reintroduced.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_lvds.c
drivers/gpu/drm/i915/intel_panel.c

index 2e83dbe5ecc00082ee901434879ffe3d29aef363..fc6f7683743796a3a3841056691c07267a012f78 100644 (file)
@@ -3601,6 +3601,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;
@@ -3634,6 +3661,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);
 
@@ -3670,6 +3700,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))
index f63973ad33cb2bd1d3fdcba72cc53763cfb1bf33..9c834bc15ab7814518fe0cd8e7ab07b2b25e18db 100644 (file)
@@ -712,6 +712,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);
@@ -728,9 +729,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;
index a5fe976364e12050bc8316553964ddc8facbae5b..9f3f71bc2f22e665275958fc78c8d57675ac7489 100644 (file)
@@ -237,6 +237,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 {
@@ -558,6 +561,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 void intel_panel_set_backlight(struct drm_device *dev,
                                      u32 level, u32 max);
 extern int intel_panel_setup_backlight(struct drm_connector *connector);
index 84085454b104961a1e011a1830e0933471ee07c9..7d418818c7a8932cbc3700f35a480e6ea6838e09 100644 (file)
@@ -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;
 
@@ -153,32 +151,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.
  */
@@ -247,62 +219,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)
 {
@@ -315,7 +231,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;
 
@@ -338,11 +253,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
                DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n",
                              pipe_config->pipe_bpp, lvds_bpp);
                pipe_config->pipe_bpp = lvds_bpp;
-
-               /* Make sure pre-965 set dither correctly for 18bpp panels. */
-               if (INTEL_INFO(dev)->gen < 4 && lvds_bpp == 18)
-                       pfit_control |= PANEL_8TO6_DITHER_ENABLE;
-
        }
 
        /*
@@ -361,18 +271,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
@@ -385,107 +288,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;
-       }
-
-       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
@@ -1115,10 +917,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;
@@ -1130,7 +928,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;
index 7f6141d9a06d7e607268b5da25807bf34a087665..0f32f6498ad3d26997b8b7fcf99d0a69b9201619 100644 (file)
@@ -117,6 +117,197 @@ 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);
+               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;
+       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 for 18bpp panels. */
+       if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18)
+               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;