aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorZhao Yakui <yakui.zhao@intel.com>2009-06-22 03:31:25 -0400
committerEric Anholt <eric@anholt.net>2009-06-22 22:31:05 -0400
commit3fbe18d65d66054667aaee849bed74674bb50062 (patch)
treec80b52cd5ca66ee8d4542e9237292e7f39794fbd /drivers
parent9e06dd39f2b6d7e35981e0d7aded618686b32ccb (diff)
drm/i915: Add support for changing LVDS panel fitting using an output property.
Previously the driver would always scale the chosen video mode to fill the panel. This adds 1:1 and maintain-aspect-ratio scaling modes. v2: the drm_calloc/drm_free is replaced by kzalloc/kfree based on Eric's suggestion. Signed-off-by: Zhao Yakui <yakui.zhao@intel.com> Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h16
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c285
2 files changed, 280 insertions, 21 deletions
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 544d5677a2fa..88bf7521405f 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -847,9 +847,25 @@
847#define HORIZ_INTERP_MASK (3 << 6) 847#define HORIZ_INTERP_MASK (3 << 6)
848#define HORIZ_AUTO_SCALE (1 << 5) 848#define HORIZ_AUTO_SCALE (1 << 5)
849#define PANEL_8TO6_DITHER_ENABLE (1 << 3) 849#define PANEL_8TO6_DITHER_ENABLE (1 << 3)
850#define PFIT_FILTER_FUZZY (0 << 24)
851#define PFIT_SCALING_AUTO (0 << 26)
852#define PFIT_SCALING_PROGRAMMED (1 << 26)
853#define PFIT_SCALING_PILLAR (2 << 26)
854#define PFIT_SCALING_LETTER (3 << 26)
850#define PFIT_PGM_RATIOS 0x61234 855#define PFIT_PGM_RATIOS 0x61234
851#define PFIT_VERT_SCALE_MASK 0xfff00000 856#define PFIT_VERT_SCALE_MASK 0xfff00000
852#define PFIT_HORIZ_SCALE_MASK 0x0000fff0 857#define PFIT_HORIZ_SCALE_MASK 0x0000fff0
858/* Pre-965 */
859#define PFIT_VERT_SCALE_SHIFT 20
860#define PFIT_VERT_SCALE_MASK 0xfff00000
861#define PFIT_HORIZ_SCALE_SHIFT 4
862#define PFIT_HORIZ_SCALE_MASK 0x0000fff0
863/* 965+ */
864#define PFIT_VERT_SCALE_SHIFT_965 16
865#define PFIT_VERT_SCALE_MASK_965 0x1fff0000
866#define PFIT_HORIZ_SCALE_SHIFT_965 0
867#define PFIT_HORIZ_SCALE_MASK_965 0x00001fff
868
853#define PFIT_AUTO_RATIOS 0x61238 869#define PFIT_AUTO_RATIOS 0x61238
854 870
855/* Backlight control */ 871/* Backlight control */
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index 345e5055f1c0..f416ead71204 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -39,6 +39,21 @@
39 39
40#define I915_LVDS "i915_lvds" 40#define I915_LVDS "i915_lvds"
41 41
42/*
43 * the following four scaling options are defined.
44 * #define DRM_MODE_SCALE_NON_GPU 0
45 * #define DRM_MODE_SCALE_FULLSCREEN 1
46 * #define DRM_MODE_SCALE_NO_SCALE 2
47 * #define DRM_MODE_SCALE_ASPECT 3
48 */
49
50/* Private structure for the integrated LVDS support */
51struct intel_lvds_priv {
52 int fitting_mode;
53 u32 pfit_control;
54 u32 pfit_pgm_ratios;
55};
56
42/** 57/**
43 * Sets the backlight level. 58 * Sets the backlight level.
44 * 59 *
@@ -213,10 +228,24 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
213 struct drm_display_mode *mode, 228 struct drm_display_mode *mode,
214 struct drm_display_mode *adjusted_mode) 229 struct drm_display_mode *adjusted_mode)
215{ 230{
231 /*
232 * float point operation is not supported . So the PANEL_RATIO_FACTOR
233 * is defined, which can avoid the float point computation when
234 * calculating the panel ratio.
235 */
236#define PANEL_RATIO_FACTOR 8192
216 struct drm_device *dev = encoder->dev; 237 struct drm_device *dev = encoder->dev;
217 struct drm_i915_private *dev_priv = dev->dev_private; 238 struct drm_i915_private *dev_priv = dev->dev_private;
218 struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 239 struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
219 struct drm_encoder *tmp_encoder; 240 struct drm_encoder *tmp_encoder;
241 struct intel_output *intel_output = enc_to_intel_output(encoder);
242 struct intel_lvds_priv *lvds_priv = intel_output->dev_priv;
243 u32 pfit_control = 0, pfit_pgm_ratios = 0;
244 int left_border = 0, right_border = 0, top_border = 0;
245 int bottom_border = 0;
246 bool border = 0;
247 int panel_ratio, desired_ratio, vert_scale, horiz_scale;
248 int horiz_ratio, vert_ratio;
220 249
221 /* Should never happen!! */ 250 /* Should never happen!! */
222 if (!IS_I965G(dev) && intel_crtc->pipe == 0) { 251 if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
@@ -232,7 +261,9 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
232 return false; 261 return false;
233 } 262 }
234 } 263 }
235 264 /* If we don't have a panel mode, there is nothing we can do */
265 if (dev_priv->panel_fixed_mode == NULL)
266 return true;
236 /* 267 /*
237 * If we have timings from the BIOS for the panel, put them in 268 * If we have timings from the BIOS for the panel, put them in
238 * to the adjusted mode. The CRTC will be set up for this mode, 269 * to the adjusted mode. The CRTC will be set up for this mode,
@@ -256,6 +287,191 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
256 drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); 287 drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
257 } 288 }
258 289
290 /* Make sure pre-965s set dither correctly */
291 if (!IS_I965G(dev)) {
292 if (dev_priv->panel_wants_dither || dev_priv->lvds_dither)
293 pfit_control |= PANEL_8TO6_DITHER_ENABLE;
294 }
295
296 /* Native modes don't need fitting */
297 if (adjusted_mode->hdisplay == mode->hdisplay &&
298 adjusted_mode->vdisplay == mode->vdisplay) {
299 pfit_pgm_ratios = 0;
300 border = 0;
301 goto out;
302 }
303
304 /* 965+ wants fuzzy fitting */
305 if (IS_I965G(dev))
306 pfit_control |= (intel_crtc->pipe << PFIT_PIPE_SHIFT) |
307 PFIT_FILTER_FUZZY;
308
309 /*
310 * Deal with panel fitting options. Figure out how to stretch the
311 * image based on its aspect ratio & the current panel fitting mode.
312 */
313 panel_ratio = adjusted_mode->hdisplay * PANEL_RATIO_FACTOR /
314 adjusted_mode->vdisplay;
315 desired_ratio = mode->hdisplay * PANEL_RATIO_FACTOR /
316 mode->vdisplay;
317 /*
318 * Enable automatic panel scaling for non-native modes so that they fill
319 * the screen. Should be enabled before the pipe is enabled, according
320 * to register description and PRM.
321 * Change the value here to see the borders for debugging
322 */
323 I915_WRITE(BCLRPAT_A, 0);
324 I915_WRITE(BCLRPAT_B, 0);
325
326 switch (lvds_priv->fitting_mode) {
327 case DRM_MODE_SCALE_NO_SCALE:
328 /*
329 * For centered modes, we have to calculate border widths &
330 * heights and modify the values programmed into the CRTC.
331 */
332 left_border = (adjusted_mode->hdisplay - mode->hdisplay) / 2;
333 right_border = left_border;
334 if (mode->hdisplay & 1)
335 right_border++;
336 top_border = (adjusted_mode->vdisplay - mode->vdisplay) / 2;
337 bottom_border = top_border;
338 if (mode->vdisplay & 1)
339 bottom_border++;
340 /* Set active & border values */
341 adjusted_mode->crtc_hdisplay = mode->hdisplay;
342 adjusted_mode->crtc_hblank_start = mode->hdisplay +
343 right_border - 1;
344 adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_htotal -
345 left_border - 1;
346 adjusted_mode->crtc_hsync_start =
347 adjusted_mode->crtc_hblank_start;
348 adjusted_mode->crtc_hsync_end =
349 adjusted_mode->crtc_hblank_end;
350 adjusted_mode->crtc_vdisplay = mode->vdisplay;
351 adjusted_mode->crtc_vblank_start = mode->vdisplay +
352 bottom_border - 1;
353 adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vtotal -
354 top_border - 1;
355 adjusted_mode->crtc_vsync_start =
356 adjusted_mode->crtc_vblank_start;
357 adjusted_mode->crtc_vsync_end =
358 adjusted_mode->crtc_vblank_end;
359 border = 1;
360 break;
361 case DRM_MODE_SCALE_ASPECT:
362 /* Scale but preserve the spect ratio */
363 pfit_control |= PFIT_ENABLE;
364 if (IS_I965G(dev)) {
365 /* 965+ is easy, it does everything in hw */
366 if (panel_ratio > desired_ratio)
367 pfit_control |= PFIT_SCALING_PILLAR;
368 else if (panel_ratio < desired_ratio)
369 pfit_control |= PFIT_SCALING_LETTER;
370 else
371 pfit_control |= PFIT_SCALING_AUTO;
372 } else {
373 /*
374 * For earlier chips we have to calculate the scaling
375 * ratio by hand and program it into the
376 * PFIT_PGM_RATIO register
377 */
378 u32 horiz_bits, vert_bits, bits = 12;
379 horiz_ratio = mode->hdisplay * PANEL_RATIO_FACTOR/
380 adjusted_mode->hdisplay;
381 vert_ratio = mode->vdisplay * PANEL_RATIO_FACTOR/
382 adjusted_mode->vdisplay;
383 horiz_scale = adjusted_mode->hdisplay *
384 PANEL_RATIO_FACTOR / mode->hdisplay;
385 vert_scale = adjusted_mode->vdisplay *
386 PANEL_RATIO_FACTOR / mode->vdisplay;
387
388 /* retain aspect ratio */
389 if (panel_ratio > desired_ratio) { /* Pillar */
390 u32 scaled_width;
391 scaled_width = mode->hdisplay * vert_scale /
392 PANEL_RATIO_FACTOR;
393 horiz_ratio = vert_ratio;
394 pfit_control |= (VERT_AUTO_SCALE |
395 VERT_INTERP_BILINEAR |
396 HORIZ_INTERP_BILINEAR);
397 /* Pillar will have left/right borders */
398 left_border = (adjusted_mode->hdisplay -
399 scaled_width) / 2;
400 right_border = left_border;
401 if (mode->hdisplay & 1) /* odd resolutions */
402 right_border++;
403 adjusted_mode->crtc_hdisplay = scaled_width;
404 adjusted_mode->crtc_hblank_start =
405 scaled_width + right_border - 1;
406 adjusted_mode->crtc_hblank_end =
407 adjusted_mode->crtc_htotal - left_border - 1;
408 adjusted_mode->crtc_hsync_start =
409 adjusted_mode->crtc_hblank_start;
410 adjusted_mode->crtc_hsync_end =
411 adjusted_mode->crtc_hblank_end;
412 border = 1;
413 } else if (panel_ratio < desired_ratio) { /* letter */
414 u32 scaled_height = mode->vdisplay *
415 horiz_scale / PANEL_RATIO_FACTOR;
416 vert_ratio = horiz_ratio;
417 pfit_control |= (HORIZ_AUTO_SCALE |
418 VERT_INTERP_BILINEAR |
419 HORIZ_INTERP_BILINEAR);
420 /* Letterbox will have top/bottom border */
421 top_border = (adjusted_mode->vdisplay -
422 scaled_height) / 2;
423 bottom_border = top_border;
424 if (mode->vdisplay & 1)
425 bottom_border++;
426 adjusted_mode->crtc_vdisplay = scaled_height;
427 adjusted_mode->crtc_vblank_start =
428 scaled_height + bottom_border - 1;
429 adjusted_mode->crtc_vblank_end =
430 adjusted_mode->crtc_vtotal - top_border - 1;
431 adjusted_mode->crtc_vsync_start =
432 adjusted_mode->crtc_vblank_start;
433 adjusted_mode->crtc_vsync_end =
434 adjusted_mode->crtc_vblank_end;
435 border = 1;
436 } else {
437 /* Aspects match, Let hw scale both directions */
438 pfit_control |= (VERT_AUTO_SCALE |
439 HORIZ_AUTO_SCALE |
440 VERT_INTERP_BILINEAR |
441 HORIZ_INTERP_BILINEAR);
442 }
443 horiz_bits = (1 << bits) * horiz_ratio /
444 PANEL_RATIO_FACTOR;
445 vert_bits = (1 << bits) * vert_ratio /
446 PANEL_RATIO_FACTOR;
447 pfit_pgm_ratios =
448 ((vert_bits << PFIT_VERT_SCALE_SHIFT) &
449 PFIT_VERT_SCALE_MASK) |
450 ((horiz_bits << PFIT_HORIZ_SCALE_SHIFT) &
451 PFIT_HORIZ_SCALE_MASK);
452 }
453 break;
454
455 case DRM_MODE_SCALE_FULLSCREEN:
456 /*
457 * Full scaling, even if it changes the aspect ratio.
458 * Fortunately this is all done for us in hw.
459 */
460 pfit_control |= PFIT_ENABLE;
461 if (IS_I965G(dev))
462 pfit_control |= PFIT_SCALING_AUTO;
463 else
464 pfit_control |= (VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
465 VERT_INTERP_BILINEAR |
466 HORIZ_INTERP_BILINEAR);
467 break;
468 default:
469 break;
470 }
471
472out:
473 lvds_priv->pfit_control = pfit_control;
474 lvds_priv->pfit_pgm_ratios = pfit_pgm_ratios;
259 /* 475 /*
260 * XXX: It would be nice to support lower refresh rates on the 476 * XXX: It would be nice to support lower refresh rates on the
261 * panels to reduce power consumption, and perhaps match the 477 * panels to reduce power consumption, and perhaps match the
@@ -301,8 +517,8 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
301{ 517{
302 struct drm_device *dev = encoder->dev; 518 struct drm_device *dev = encoder->dev;
303 struct drm_i915_private *dev_priv = dev->dev_private; 519 struct drm_i915_private *dev_priv = dev->dev_private;
304 struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 520 struct intel_output *intel_output = enc_to_intel_output(encoder);
305 u32 pfit_control; 521 struct intel_lvds_priv *lvds_priv = intel_output->dev_priv;
306 522
307 /* 523 /*
308 * The LVDS pin pair will already have been turned on in the 524 * The LVDS pin pair will already have been turned on in the
@@ -319,22 +535,8 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
319 * screen. Should be enabled before the pipe is enabled, according to 535 * screen. Should be enabled before the pipe is enabled, according to
320 * register description and PRM. 536 * register description and PRM.
321 */ 537 */
322 if (mode->hdisplay != adjusted_mode->hdisplay || 538 I915_WRITE(PFIT_PGM_RATIOS, lvds_priv->pfit_pgm_ratios);
323 mode->vdisplay != adjusted_mode->vdisplay) 539 I915_WRITE(PFIT_CONTROL, lvds_priv->pfit_control);
324 pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
325 HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
326 HORIZ_INTERP_BILINEAR);
327 else
328 pfit_control = 0;
329
330 if (!IS_I965G(dev)) {
331 if (dev_priv->panel_wants_dither || dev_priv->lvds_dither)
332 pfit_control |= PANEL_8TO6_DITHER_ENABLE;
333 }
334 else
335 pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
336
337 I915_WRITE(PFIT_CONTROL, pfit_control);
338} 540}
339 541
340/** 542/**
@@ -406,6 +608,34 @@ static int intel_lvds_set_property(struct drm_connector *connector,
406 struct drm_property *property, 608 struct drm_property *property,
407 uint64_t value) 609 uint64_t value)
408{ 610{
611 struct drm_device *dev = connector->dev;
612 struct intel_output *intel_output =
613 to_intel_output(connector);
614
615 if (property == dev->mode_config.scaling_mode_property &&
616 connector->encoder) {
617 struct drm_crtc *crtc = connector->encoder->crtc;
618 struct intel_lvds_priv *lvds_priv = intel_output->dev_priv;
619 if (value == DRM_MODE_SCALE_NON_GPU) {
620 DRM_DEBUG_KMS(I915_LVDS,
621 "non_GPU property is unsupported\n");
622 return 0;
623 }
624 if (lvds_priv->fitting_mode == value) {
625 /* the LVDS scaling property is not changed */
626 return 0;
627 }
628 lvds_priv->fitting_mode = value;
629 if (crtc && crtc->enabled) {
630 /*
631 * If the CRTC is enabled, the display will be changed
632 * according to the new panel fitting mode.
633 */
634 drm_crtc_helper_set_mode(crtc, &crtc->mode,
635 crtc->x, crtc->y, crtc->fb);
636 }
637 }
638
409 return 0; 639 return 0;
410} 640}
411 641
@@ -518,6 +748,7 @@ void intel_lvds_init(struct drm_device *dev)
518 struct drm_encoder *encoder; 748 struct drm_encoder *encoder;
519 struct drm_display_mode *scan; /* *modes, *bios_mode; */ 749 struct drm_display_mode *scan; /* *modes, *bios_mode; */
520 struct drm_crtc *crtc; 750 struct drm_crtc *crtc;
751 struct intel_lvds_priv *lvds_priv;
521 u32 lvds; 752 u32 lvds;
522 int pipe, gpio = GPIOC; 753 int pipe, gpio = GPIOC;
523 754
@@ -531,7 +762,8 @@ void intel_lvds_init(struct drm_device *dev)
531 gpio = PCH_GPIOC; 762 gpio = PCH_GPIOC;
532 } 763 }
533 764
534 intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL); 765 intel_output = kzalloc(sizeof(struct intel_output) +
766 sizeof(struct intel_lvds_priv), GFP_KERNEL);
535 if (!intel_output) { 767 if (!intel_output) {
536 return; 768 return;
537 } 769 }
@@ -553,7 +785,18 @@ void intel_lvds_init(struct drm_device *dev)
553 connector->interlace_allowed = false; 785 connector->interlace_allowed = false;
554 connector->doublescan_allowed = false; 786 connector->doublescan_allowed = false;
555 787
788 lvds_priv = (struct intel_lvds_priv *)(intel_output + 1);
789 intel_output->dev_priv = lvds_priv;
790 /* create the scaling mode property */
791 drm_mode_create_scaling_mode_property(dev);
792 /*
793 * the initial panel fitting mode will be FULL_SCREEN.
794 */
556 795
796 drm_connector_attach_property(&intel_output->base,
797 dev->mode_config.scaling_mode_property,
798 DRM_MODE_SCALE_FULLSCREEN);
799 lvds_priv->fitting_mode = DRM_MODE_SCALE_FULLSCREEN;
557 /* 800 /*
558 * LVDS discovery: 801 * LVDS discovery:
559 * 1) check for EDID on DDC 802 * 1) check for EDID on DDC
@@ -649,5 +892,5 @@ failed:
649 if (intel_output->ddc_bus) 892 if (intel_output->ddc_bus)
650 intel_i2c_destroy(intel_output->ddc_bus); 893 intel_i2c_destroy(intel_output->ddc_bus);
651 drm_connector_cleanup(connector); 894 drm_connector_cleanup(connector);
652 kfree(connector); 895 kfree(intel_output);
653} 896}