aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/i915/intel_display.c33
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c11
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h6
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c209
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c191
5 files changed, 241 insertions, 209 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 2e83dbe5ecc0..fc6f76837437 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3601,6 +3601,33 @@ g4x_fixup_plane(struct drm_i915_private *dev_priv, enum pipe pipe)
3601 } 3601 }
3602} 3602}
3603 3603
3604static void i9xx_pfit_enable(struct intel_crtc *crtc)
3605{
3606 struct drm_device *dev = crtc->base.dev;
3607 struct drm_i915_private *dev_priv = dev->dev_private;
3608 struct intel_crtc_config *pipe_config = &crtc->config;
3609
3610 if (!(intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_EDP) ||
3611 intel_pipe_has_type(&crtc->base, INTEL_OUTPUT_LVDS)))
3612 return;
3613
3614 WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE);
3615 assert_pipe_disabled(dev_priv, crtc->pipe);
3616
3617 /*
3618 * Enable automatic panel scaling so that non-native modes
3619 * fill the screen. The panel fitter should only be
3620 * adjusted whilst the pipe is disabled, according to
3621 * register description and PRM.
3622 */
3623 DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
3624 pipe_config->pfit_control,
3625 pipe_config->pfit_pgm_ratios);
3626
3627 I915_WRITE(PFIT_PGM_RATIOS, pipe_config->pfit_pgm_ratios);
3628 I915_WRITE(PFIT_CONTROL, pipe_config->pfit_control);
3629}
3630
3604static void valleyview_crtc_enable(struct drm_crtc *crtc) 3631static void valleyview_crtc_enable(struct drm_crtc *crtc)
3605{ 3632{
3606 struct drm_device *dev = crtc->dev; 3633 struct drm_device *dev = crtc->dev;
@@ -3634,6 +3661,9 @@ static void valleyview_crtc_enable(struct drm_crtc *crtc)
3634 for_each_encoder_on_crtc(dev, crtc, encoder) 3661 for_each_encoder_on_crtc(dev, crtc, encoder)
3635 encoder->enable(encoder); 3662 encoder->enable(encoder);
3636 3663
3664 /* Enable panel fitting for eDP */
3665 i9xx_pfit_enable(intel_crtc);
3666
3637 intel_enable_pipe(dev_priv, pipe, false); 3667 intel_enable_pipe(dev_priv, pipe, false);
3638 intel_enable_plane(dev_priv, plane, pipe); 3668 intel_enable_plane(dev_priv, plane, pipe);
3639 3669
@@ -3670,6 +3700,9 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
3670 if (encoder->pre_enable) 3700 if (encoder->pre_enable)
3671 encoder->pre_enable(encoder); 3701 encoder->pre_enable(encoder);
3672 3702
3703 /* Enable panel fitting for LVDS */
3704 i9xx_pfit_enable(intel_crtc);
3705
3673 intel_enable_pipe(dev_priv, pipe, false); 3706 intel_enable_pipe(dev_priv, pipe, false);
3674 intel_enable_plane(dev_priv, plane, pipe); 3707 intel_enable_plane(dev_priv, plane, pipe);
3675 if (IS_G4X(dev)) 3708 if (IS_G4X(dev))
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index f63973ad33cb..9c834bc15ab7 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -712,6 +712,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
712 struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; 712 struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
713 struct drm_display_mode *mode = &pipe_config->requested_mode; 713 struct drm_display_mode *mode = &pipe_config->requested_mode;
714 struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base); 714 struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
715 struct intel_crtc *intel_crtc = encoder->new_crtc;
715 struct intel_connector *intel_connector = intel_dp->attached_connector; 716 struct intel_connector *intel_connector = intel_dp->attached_connector;
716 int lane_count, clock; 717 int lane_count, clock;
717 int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd); 718 int max_lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
@@ -728,9 +729,13 @@ intel_dp_compute_config(struct intel_encoder *encoder,
728 if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) { 729 if (is_edp(intel_dp) && intel_connector->panel.fixed_mode) {
729 intel_fixed_panel_mode(intel_connector->panel.fixed_mode, 730 intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
730 adjusted_mode); 731 adjusted_mode);
731 intel_pch_panel_fitting(dev, 732 if (!HAS_PCH_SPLIT(dev))
732 intel_connector->panel.fitting_mode, 733 intel_gmch_panel_fitting(intel_crtc, pipe_config,
733 mode, adjusted_mode); 734 intel_connector->panel.fitting_mode);
735 else
736 intel_pch_panel_fitting(dev,
737 intel_connector->panel.fitting_mode,
738 mode, adjusted_mode);
734 } 739 }
735 /* We need to take the panel's fixed mode into account. */ 740 /* We need to take the panel's fixed mode into account. */
736 target_clock = adjusted_mode->clock; 741 target_clock = adjusted_mode->clock;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index a5fe976364e1..9f3f71bc2f22 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -237,6 +237,9 @@ struct intel_crtc_config {
237 int pixel_target_clock; 237 int pixel_target_clock;
238 /* Used by SDVO (and if we ever fix it, HDMI). */ 238 /* Used by SDVO (and if we ever fix it, HDMI). */
239 unsigned pixel_multiplier; 239 unsigned pixel_multiplier;
240
241 /* Panel fitter controls for gen2-gen4 + VLV */
242 u32 pfit_control, pfit_pgm_ratios;
240}; 243};
241 244
242struct intel_crtc { 245struct intel_crtc {
@@ -558,6 +561,9 @@ extern void intel_pch_panel_fitting(struct drm_device *dev,
558 int fitting_mode, 561 int fitting_mode,
559 const struct drm_display_mode *mode, 562 const struct drm_display_mode *mode,
560 struct drm_display_mode *adjusted_mode); 563 struct drm_display_mode *adjusted_mode);
564extern void intel_gmch_panel_fitting(struct intel_crtc *crtc,
565 struct intel_crtc_config *pipe_config,
566 int fitting_mode);
561extern void intel_panel_set_backlight(struct drm_device *dev, 567extern void intel_panel_set_backlight(struct drm_device *dev,
562 u32 level, u32 max); 568 u32 level, u32 max);
563extern int intel_panel_setup_backlight(struct drm_connector *connector); 569extern 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 84085454b104..7d418818c7a8 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 {
49struct intel_lvds_encoder { 49struct intel_lvds_encoder {
50 struct intel_encoder base; 50 struct intel_encoder base;
51 51
52 u32 pfit_control;
53 u32 pfit_pgm_ratios;
54 bool is_dual_link; 52 bool is_dual_link;
55 u32 reg; 53 u32 reg;
56 54
@@ -153,32 +151,6 @@ static void intel_pre_pll_enable_lvds(struct intel_encoder *encoder)
153 I915_WRITE(lvds_encoder->reg, temp); 151 I915_WRITE(lvds_encoder->reg, temp);
154} 152}
155 153
156static void intel_pre_enable_lvds(struct intel_encoder *encoder)
157{
158 struct drm_device *dev = encoder->base.dev;
159 struct intel_lvds_encoder *enc = to_lvds_encoder(&encoder->base);
160 struct drm_i915_private *dev_priv = dev->dev_private;
161
162 if (HAS_PCH_SPLIT(dev) || !enc->pfit_control)
163 return;
164
165 WARN_ON(I915_READ(PFIT_CONTROL) & PFIT_ENABLE);
166 assert_pipe_disabled(dev_priv, to_intel_crtc(encoder->base.crtc)->pipe);
167
168 /*
169 * Enable automatic panel scaling so that non-native modes
170 * fill the screen. The panel fitter should only be
171 * adjusted whilst the pipe is disabled, according to
172 * register description and PRM.
173 */
174 DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
175 enc->pfit_control,
176 enc->pfit_pgm_ratios);
177
178 I915_WRITE(PFIT_PGM_RATIOS, enc->pfit_pgm_ratios);
179 I915_WRITE(PFIT_CONTROL, enc->pfit_control);
180}
181
182/** 154/**
183 * Sets the power state for the panel. 155 * Sets the power state for the panel.
184 */ 156 */
@@ -247,62 +219,6 @@ static int intel_lvds_mode_valid(struct drm_connector *connector,
247 return MODE_OK; 219 return MODE_OK;
248} 220}
249 221
250static void
251centre_horizontally(struct drm_display_mode *mode,
252 int width)
253{
254 u32 border, sync_pos, blank_width, sync_width;
255
256 /* keep the hsync and hblank widths constant */
257 sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
258 blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
259 sync_pos = (blank_width - sync_width + 1) / 2;
260
261 border = (mode->hdisplay - width + 1) / 2;
262 border += border & 1; /* make the border even */
263
264 mode->crtc_hdisplay = width;
265 mode->crtc_hblank_start = width + border;
266 mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
267
268 mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
269 mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
270}
271
272static void
273centre_vertically(struct drm_display_mode *mode,
274 int height)
275{
276 u32 border, sync_pos, blank_width, sync_width;
277
278 /* keep the vsync and vblank widths constant */
279 sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
280 blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
281 sync_pos = (blank_width - sync_width + 1) / 2;
282
283 border = (mode->vdisplay - height + 1) / 2;
284
285 mode->crtc_vdisplay = height;
286 mode->crtc_vblank_start = height + border;
287 mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
288
289 mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
290 mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width;
291}
292
293static inline u32 panel_fitter_scaling(u32 source, u32 target)
294{
295 /*
296 * Floating point operation is not supported. So the FACTOR
297 * is defined, which can avoid the floating point computation
298 * when calculating the panel ratio.
299 */
300#define ACCURACY 12
301#define FACTOR (1 << ACCURACY)
302 u32 ratio = source * FACTOR / target;
303 return (FACTOR * ratio + FACTOR/2) / FACTOR;
304}
305
306static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, 222static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
307 struct intel_crtc_config *pipe_config) 223 struct intel_crtc_config *pipe_config)
308{ 224{
@@ -315,7 +231,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
315 struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; 231 struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
316 struct drm_display_mode *mode = &pipe_config->requested_mode; 232 struct drm_display_mode *mode = &pipe_config->requested_mode;
317 struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc; 233 struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc;
318 u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
319 unsigned int lvds_bpp; 234 unsigned int lvds_bpp;
320 int pipe; 235 int pipe;
321 236
@@ -338,11 +253,6 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
338 DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n", 253 DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n",
339 pipe_config->pipe_bpp, lvds_bpp); 254 pipe_config->pipe_bpp, lvds_bpp);
340 pipe_config->pipe_bpp = lvds_bpp; 255 pipe_config->pipe_bpp = lvds_bpp;
341
342 /* Make sure pre-965 set dither correctly for 18bpp panels. */
343 if (INTEL_INFO(dev)->gen < 4 && lvds_bpp == 18)
344 pfit_control |= PANEL_8TO6_DITHER_ENABLE;
345
346 } 256 }
347 257
348 /* 258 /*
@@ -361,18 +271,11 @@ static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
361 intel_connector->panel.fitting_mode, 271 intel_connector->panel.fitting_mode,
362 mode, adjusted_mode); 272 mode, adjusted_mode);
363 return true; 273 return true;
274 } else {
275 intel_gmch_panel_fitting(intel_crtc, pipe_config,
276 intel_connector->panel.fitting_mode);
364 } 277 }
365 278
366 /* Native modes don't need fitting */
367 if (adjusted_mode->hdisplay == mode->hdisplay &&
368 adjusted_mode->vdisplay == mode->vdisplay)
369 goto out;
370
371 /* 965+ wants fuzzy fitting */
372 if (INTEL_INFO(dev)->gen >= 4)
373 pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
374 PFIT_FILTER_FUZZY);
375
376 /* 279 /*
377 * Enable automatic panel scaling for non-native modes so that they fill 280 * Enable automatic panel scaling for non-native modes so that they fill
378 * the screen. Should be enabled before the pipe is enabled, according 281 * 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,
385 drm_mode_set_crtcinfo(adjusted_mode, 0); 288 drm_mode_set_crtcinfo(adjusted_mode, 0);
386 pipe_config->timings_set = true; 289 pipe_config->timings_set = true;
387 290
388 switch (intel_connector->panel.fitting_mode) {
389 case DRM_MODE_SCALE_CENTER:
390 /*
391 * For centered modes, we have to calculate border widths &
392 * heights and modify the values programmed into the CRTC.
393 */
394 centre_horizontally(adjusted_mode, mode->hdisplay);
395 centre_vertically(adjusted_mode, mode->vdisplay);
396 border = LVDS_BORDER_ENABLE;
397 break;
398
399 case DRM_MODE_SCALE_ASPECT:
400 /* Scale but preserve the aspect ratio */
401 if (INTEL_INFO(dev)->gen >= 4) {
402 u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
403 u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
404
405 /* 965+ is easy, it does everything in hw */
406 if (scaled_width > scaled_height)
407 pfit_control |= PFIT_ENABLE | PFIT_SCALING_PILLAR;
408 else if (scaled_width < scaled_height)
409 pfit_control |= PFIT_ENABLE | PFIT_SCALING_LETTER;
410 else if (adjusted_mode->hdisplay != mode->hdisplay)
411 pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
412 } else {
413 u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
414 u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
415 /*
416 * For earlier chips we have to calculate the scaling
417 * ratio by hand and program it into the
418 * PFIT_PGM_RATIO register
419 */
420 if (scaled_width > scaled_height) { /* pillar */
421 centre_horizontally(adjusted_mode, scaled_height / mode->vdisplay);
422
423 border = LVDS_BORDER_ENABLE;
424 if (mode->vdisplay != adjusted_mode->vdisplay) {
425 u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
426 pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
427 bits << PFIT_VERT_SCALE_SHIFT);
428 pfit_control |= (PFIT_ENABLE |
429 VERT_INTERP_BILINEAR |
430 HORIZ_INTERP_BILINEAR);
431 }
432 } else if (scaled_width < scaled_height) { /* letter */
433 centre_vertically(adjusted_mode, scaled_width / mode->hdisplay);
434
435 border = LVDS_BORDER_ENABLE;
436 if (mode->hdisplay != adjusted_mode->hdisplay) {
437 u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
438 pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
439 bits << PFIT_VERT_SCALE_SHIFT);
440 pfit_control |= (PFIT_ENABLE |
441 VERT_INTERP_BILINEAR |
442 HORIZ_INTERP_BILINEAR);
443 }
444 } else
445 /* Aspects match, Let hw scale both directions */
446 pfit_control |= (PFIT_ENABLE |
447 VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
448 VERT_INTERP_BILINEAR |
449 HORIZ_INTERP_BILINEAR);
450 }
451 break;
452
453 case DRM_MODE_SCALE_FULLSCREEN:
454 /*
455 * Full scaling, even if it changes the aspect ratio.
456 * Fortunately this is all done for us in hw.
457 */
458 if (mode->vdisplay != adjusted_mode->vdisplay ||
459 mode->hdisplay != adjusted_mode->hdisplay) {
460 pfit_control |= PFIT_ENABLE;
461 if (INTEL_INFO(dev)->gen >= 4)
462 pfit_control |= PFIT_SCALING_AUTO;
463 else
464 pfit_control |= (VERT_AUTO_SCALE |
465 VERT_INTERP_BILINEAR |
466 HORIZ_AUTO_SCALE |
467 HORIZ_INTERP_BILINEAR);
468 }
469 break;
470
471 default:
472 break;
473 }
474
475out:
476 /* If not enabling scaling, be consistent and always use 0. */
477 if ((pfit_control & PFIT_ENABLE) == 0) {
478 pfit_control = 0;
479 pfit_pgm_ratios = 0;
480 }
481
482 if (pfit_control != lvds_encoder->pfit_control ||
483 pfit_pgm_ratios != lvds_encoder->pfit_pgm_ratios) {
484 lvds_encoder->pfit_control = pfit_control;
485 lvds_encoder->pfit_pgm_ratios = pfit_pgm_ratios;
486 }
487 dev_priv->lvds_border_bits = border;
488
489 /* 291 /*
490 * XXX: It would be nice to support lower refresh rates on the 292 * XXX: It would be nice to support lower refresh rates on the
491 * panels to reduce power consumption, and perhaps match the 293 * panels to reduce power consumption, and perhaps match the
@@ -1115,10 +917,6 @@ bool intel_lvds_init(struct drm_device *dev)
1115 917
1116 lvds_encoder->attached_connector = lvds_connector; 918 lvds_encoder->attached_connector = lvds_connector;
1117 919
1118 if (!HAS_PCH_SPLIT(dev)) {
1119 lvds_encoder->pfit_control = I915_READ(PFIT_CONTROL);
1120 }
1121
1122 intel_encoder = &lvds_encoder->base; 920 intel_encoder = &lvds_encoder->base;
1123 encoder = &intel_encoder->base; 921 encoder = &intel_encoder->base;
1124 intel_connector = &lvds_connector->base; 922 intel_connector = &lvds_connector->base;
@@ -1130,7 +928,6 @@ bool intel_lvds_init(struct drm_device *dev)
1130 DRM_MODE_ENCODER_LVDS); 928 DRM_MODE_ENCODER_LVDS);
1131 929
1132 intel_encoder->enable = intel_enable_lvds; 930 intel_encoder->enable = intel_enable_lvds;
1133 intel_encoder->pre_enable = intel_pre_enable_lvds;
1134 intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds; 931 intel_encoder->pre_pll_enable = intel_pre_pll_enable_lvds;
1135 intel_encoder->compute_config = intel_lvds_compute_config; 932 intel_encoder->compute_config = intel_lvds_compute_config;
1136 intel_encoder->disable = intel_disable_lvds; 933 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 7f6141d9a06d..0f32f6498ad3 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -117,6 +117,197 @@ done:
117 dev_priv->pch_pf_size = (width << 16) | height; 117 dev_priv->pch_pf_size = (width << 16) | height;
118} 118}
119 119
120static void
121centre_horizontally(struct drm_display_mode *mode,
122 int width)
123{
124 u32 border, sync_pos, blank_width, sync_width;
125
126 /* keep the hsync and hblank widths constant */
127 sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
128 blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
129 sync_pos = (blank_width - sync_width + 1) / 2;
130
131 border = (mode->hdisplay - width + 1) / 2;
132 border += border & 1; /* make the border even */
133
134 mode->crtc_hdisplay = width;
135 mode->crtc_hblank_start = width + border;
136 mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
137
138 mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
139 mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
140}
141
142static void
143centre_vertically(struct drm_display_mode *mode,
144 int height)
145{
146 u32 border, sync_pos, blank_width, sync_width;
147
148 /* keep the vsync and vblank widths constant */
149 sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
150 blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
151 sync_pos = (blank_width - sync_width + 1) / 2;
152
153 border = (mode->vdisplay - height + 1) / 2;
154
155 mode->crtc_vdisplay = height;
156 mode->crtc_vblank_start = height + border;
157 mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
158
159 mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
160 mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width;
161}
162
163static inline u32 panel_fitter_scaling(u32 source, u32 target)
164{
165 /*
166 * Floating point operation is not supported. So the FACTOR
167 * is defined, which can avoid the floating point computation
168 * when calculating the panel ratio.
169 */
170#define ACCURACY 12
171#define FACTOR (1 << ACCURACY)
172 u32 ratio = source * FACTOR / target;
173 return (FACTOR * ratio + FACTOR/2) / FACTOR;
174}
175
176void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
177 struct intel_crtc_config *pipe_config,
178 int fitting_mode)
179{
180 struct drm_device *dev = intel_crtc->base.dev;
181 struct drm_i915_private *dev_priv = dev->dev_private;
182 u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
183 struct drm_display_mode *mode, *adjusted_mode;
184
185 mode = &pipe_config->requested_mode;
186 adjusted_mode = &pipe_config->adjusted_mode;
187
188 /* Native modes don't need fitting */
189 if (adjusted_mode->hdisplay == mode->hdisplay &&
190 adjusted_mode->vdisplay == mode->vdisplay)
191 goto out;
192
193 switch (fitting_mode) {
194 case DRM_MODE_SCALE_CENTER:
195 /*
196 * For centered modes, we have to calculate border widths &
197 * heights and modify the values programmed into the CRTC.
198 */
199 centre_horizontally(adjusted_mode, mode->hdisplay);
200 centre_vertically(adjusted_mode, mode->vdisplay);
201 border = LVDS_BORDER_ENABLE;
202 break;
203 case DRM_MODE_SCALE_ASPECT:
204 /* Scale but preserve the aspect ratio */
205 if (INTEL_INFO(dev)->gen >= 4) {
206 u32 scaled_width = adjusted_mode->hdisplay *
207 mode->vdisplay;
208 u32 scaled_height = mode->hdisplay *
209 adjusted_mode->vdisplay;
210
211 /* 965+ is easy, it does everything in hw */
212 if (scaled_width > scaled_height)
213 pfit_control |= PFIT_ENABLE |
214 PFIT_SCALING_PILLAR;
215 else if (scaled_width < scaled_height)
216 pfit_control |= PFIT_ENABLE |
217 PFIT_SCALING_LETTER;
218 else if (adjusted_mode->hdisplay != mode->hdisplay)
219 pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
220 } else {
221 u32 scaled_width = adjusted_mode->hdisplay *
222 mode->vdisplay;
223 u32 scaled_height = mode->hdisplay *
224 adjusted_mode->vdisplay;
225 /*
226 * For earlier chips we have to calculate the scaling
227 * ratio by hand and program it into the
228 * PFIT_PGM_RATIO register
229 */
230 if (scaled_width > scaled_height) { /* pillar */
231 centre_horizontally(adjusted_mode,
232 scaled_height /
233 mode->vdisplay);
234
235 border = LVDS_BORDER_ENABLE;
236 if (mode->vdisplay != adjusted_mode->vdisplay) {
237 u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
238 pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
239 bits << PFIT_VERT_SCALE_SHIFT);
240 pfit_control |= (PFIT_ENABLE |
241 VERT_INTERP_BILINEAR |
242 HORIZ_INTERP_BILINEAR);
243 }
244 } else if (scaled_width < scaled_height) { /* letter */
245 centre_vertically(adjusted_mode,
246 scaled_width /
247 mode->hdisplay);
248
249 border = LVDS_BORDER_ENABLE;
250 if (mode->hdisplay != adjusted_mode->hdisplay) {
251 u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
252 pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
253 bits << PFIT_VERT_SCALE_SHIFT);
254 pfit_control |= (PFIT_ENABLE |
255 VERT_INTERP_BILINEAR |
256 HORIZ_INTERP_BILINEAR);
257 }
258 } else {
259 /* Aspects match, Let hw scale both directions */
260 pfit_control |= (PFIT_ENABLE |
261 VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
262 VERT_INTERP_BILINEAR |
263 HORIZ_INTERP_BILINEAR);
264 }
265 }
266 break;
267 default:
268 case DRM_MODE_SCALE_FULLSCREEN:
269 /*
270 * Full scaling, even if it changes the aspect ratio.
271 * Fortunately this is all done for us in hw.
272 */
273 if (mode->vdisplay != adjusted_mode->vdisplay ||
274 mode->hdisplay != adjusted_mode->hdisplay) {
275 pfit_control |= PFIT_ENABLE;
276 if (INTEL_INFO(dev)->gen >= 4)
277 pfit_control |= PFIT_SCALING_AUTO;
278 else
279 pfit_control |= (VERT_AUTO_SCALE |
280 VERT_INTERP_BILINEAR |
281 HORIZ_AUTO_SCALE |
282 HORIZ_INTERP_BILINEAR);
283 }
284 break;
285 }
286
287 /* 965+ wants fuzzy fitting */
288 /* FIXME: handle multiple panels by failing gracefully */
289 if (INTEL_INFO(dev)->gen >= 4)
290 pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
291 PFIT_FILTER_FUZZY);
292
293out:
294 if ((pfit_control & PFIT_ENABLE) == 0) {
295 pfit_control = 0;
296 pfit_pgm_ratios = 0;
297 }
298
299 /* Make sure pre-965 set dither correctly for 18bpp panels. */
300 if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18)
301 pfit_control |= PANEL_8TO6_DITHER_ENABLE;
302
303 if (pfit_control != pipe_config->pfit_control ||
304 pfit_pgm_ratios != pipe_config->pfit_pgm_ratios) {
305 pipe_config->pfit_control = pfit_control;
306 pipe_config->pfit_pgm_ratios = pfit_pgm_ratios;
307 }
308 dev_priv->lvds_border_bits = border;
309}
310
120static int is_backlight_combination_mode(struct drm_device *dev) 311static int is_backlight_combination_mode(struct drm_device *dev)
121{ 312{
122 struct drm_i915_private *dev_priv = dev->dev_private; 313 struct drm_i915_private *dev_priv = dev->dev_private;