aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_lvds.c
diff options
context:
space:
mode:
authorJesse Barnes <jbarnes@virtuousgeek.org>2013-04-25 15:55:01 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2013-04-25 16:16:22 -0400
commit2dd24552cab40ea829ba3fda890eeafd2c4816d8 (patch)
treefee957bf6e4c7824ffbd82125930f316634884b0 /drivers/gpu/drm/i915/intel_lvds.c
parent198a037f02666eeaab5ba07974fa37467b1f6bd8 (diff)
drm/i915: factor out GMCH panel fitting code and use for eDP v3
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>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_lvds.c')
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c209
1 files changed, 3 insertions, 206 deletions
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;