diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2013-04-25 15:55:01 -0400 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2013-04-25 16:16:22 -0400 |
commit | 2dd24552cab40ea829ba3fda890eeafd2c4816d8 (patch) | |
tree | fee957bf6e4c7824ffbd82125930f316634884b0 /drivers/gpu/drm/i915/intel_lvds.c | |
parent | 198a037f02666eeaab5ba07974fa37467b1f6bd8 (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.c | 209 |
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 { | |||
49 | struct intel_lvds_encoder { | 49 | struct 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 | ||
156 | static 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 | ||
250 | static void | ||
251 | centre_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 | |||
272 | static void | ||
273 | centre_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 | |||
293 | static 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 | |||
306 | static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, | 222 | static 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 | |||
475 | out: | ||
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; |