Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 205155
b: refs/heads/master
c: 49be663
h: refs/heads/master
i:
  205153: 84b7207
  205151: bce350a
v: v3
  • Loading branch information
Chris Wilson authored and Eric Anholt committed Aug 2, 2010
1 parent 483b705 commit 28eabae
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 195 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 71677043350874c55f60dce06a03ab61e3af6e93
refs/heads/master: 49be663f9952d0fc50bb0a4a75c3fd201e40ec59
299 changes: 105 additions & 194 deletions trunk/drivers/gpu/drm/i915/intel_lvds.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,31 +156,73 @@ 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_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
/*
* float point operation is not supported . So the PANEL_RATIO_FACTOR
* is defined, which can avoid the float point computation when
* calculating the panel ratio.
*/
#define PANEL_RATIO_FACTOR 8192
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct drm_encoder *tmp_encoder;
struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
struct intel_lvds_priv *lvds_priv = intel_encoder->dev_priv;
u32 pfit_control = 0, pfit_pgm_ratios = 0;
int left_border = 0, right_border = 0, top_border = 0;
int bottom_border = 0;
bool border = 0;
int panel_ratio, desired_ratio, vert_scale, horiz_scale;
int horiz_ratio, vert_ratio;
u32 hsync_width, vsync_width;
u32 hblank_width, vblank_width;
u32 hsync_pos, vsync_pos;
u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;

/* Should never happen!! */
if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
Expand Down Expand Up @@ -228,37 +270,18 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,

/* Native modes don't need fitting */
if (adjusted_mode->hdisplay == mode->hdisplay &&
adjusted_mode->vdisplay == mode->vdisplay) {
pfit_pgm_ratios = 0;
border = 0;
adjusted_mode->vdisplay == mode->vdisplay)
goto out;
}

/* full screen scale for now */
if (HAS_PCH_SPLIT(dev))
goto out;

/* 965+ wants fuzzy fitting */
if (IS_I965G(dev))
pfit_control |= (intel_crtc->pipe << PFIT_PIPE_SHIFT) |
PFIT_FILTER_FUZZY;

hsync_width = adjusted_mode->crtc_hsync_end -
adjusted_mode->crtc_hsync_start;
vsync_width = adjusted_mode->crtc_vsync_end -
adjusted_mode->crtc_vsync_start;
hblank_width = adjusted_mode->crtc_hblank_end -
adjusted_mode->crtc_hblank_start;
vblank_width = adjusted_mode->crtc_vblank_end -
adjusted_mode->crtc_vblank_start;
/*
* Deal with panel fitting options. Figure out how to stretch the
* image based on its aspect ratio & the current panel fitting mode.
*/
panel_ratio = adjusted_mode->hdisplay * PANEL_RATIO_FACTOR /
adjusted_mode->vdisplay;
desired_ratio = mode->hdisplay * PANEL_RATIO_FACTOR /
mode->vdisplay;
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
Expand All @@ -276,170 +299,63 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
* For centered modes, we have to calculate border widths &
* heights and modify the values programmed into the CRTC.
*/
left_border = (adjusted_mode->hdisplay - mode->hdisplay) / 2;
right_border = left_border;
if (mode->hdisplay & 1)
right_border++;
top_border = (adjusted_mode->vdisplay - mode->vdisplay) / 2;
bottom_border = top_border;
if (mode->vdisplay & 1)
bottom_border++;
/* Set active & border values */
adjusted_mode->crtc_hdisplay = mode->hdisplay;
/* Keep the boder be even */
if (right_border & 1)
right_border++;
/* use the border directly instead of border minuse one */
adjusted_mode->crtc_hblank_start = mode->hdisplay +
right_border;
/* keep the blank width constant */
adjusted_mode->crtc_hblank_end =
adjusted_mode->crtc_hblank_start + hblank_width;
/* get the hsync pos relative to hblank start */
hsync_pos = (hblank_width - hsync_width) / 2;
/* keep the hsync pos be even */
if (hsync_pos & 1)
hsync_pos++;
adjusted_mode->crtc_hsync_start =
adjusted_mode->crtc_hblank_start + hsync_pos;
/* keep the hsync width constant */
adjusted_mode->crtc_hsync_end =
adjusted_mode->crtc_hsync_start + hsync_width;
adjusted_mode->crtc_vdisplay = mode->vdisplay;
/* use the border instead of border minus one */
adjusted_mode->crtc_vblank_start = mode->vdisplay +
bottom_border;
/* keep the vblank width constant */
adjusted_mode->crtc_vblank_end =
adjusted_mode->crtc_vblank_start + vblank_width;
/* get the vsync start postion relative to vblank start */
vsync_pos = (vblank_width - vsync_width) / 2;
adjusted_mode->crtc_vsync_start =
adjusted_mode->crtc_vblank_start + vsync_pos;
/* keep the vsync width constant */
adjusted_mode->crtc_vsync_end =
adjusted_mode->crtc_vsync_start + vsync_width;
border = 1;
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 spect ratio */
pfit_control |= PFIT_ENABLE;
/* Scale but preserve the aspect ratio */
if (IS_I965G(dev)) {
u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;

pfit_control |= PFIT_ENABLE;
/* 965+ is easy, it does everything in hw */
if (panel_ratio > desired_ratio)
if (scaled_width > scaled_height)
pfit_control |= PFIT_SCALING_PILLAR;
else if (panel_ratio < desired_ratio)
else if (scaled_width < scaled_height)
pfit_control |= PFIT_SCALING_LETTER;
else
pfit_control |= 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
*/
u32 horiz_bits, vert_bits, bits = 12;
horiz_ratio = mode->hdisplay * PANEL_RATIO_FACTOR/
adjusted_mode->hdisplay;
vert_ratio = mode->vdisplay * PANEL_RATIO_FACTOR/
adjusted_mode->vdisplay;
horiz_scale = adjusted_mode->hdisplay *
PANEL_RATIO_FACTOR / mode->hdisplay;
vert_scale = adjusted_mode->vdisplay *
PANEL_RATIO_FACTOR / mode->vdisplay;

/* retain aspect ratio */
if (panel_ratio > desired_ratio) { /* Pillar */
u32 scaled_width;
scaled_width = mode->hdisplay * vert_scale /
PANEL_RATIO_FACTOR;
horiz_ratio = vert_ratio;
pfit_control |= (VERT_AUTO_SCALE |
VERT_INTERP_BILINEAR |
HORIZ_INTERP_BILINEAR);
/* Pillar will have left/right borders */
left_border = (adjusted_mode->hdisplay -
scaled_width) / 2;
right_border = left_border;
if (mode->hdisplay & 1) /* odd resolutions */
right_border++;
/* keep the border be even */
if (right_border & 1)
right_border++;
adjusted_mode->crtc_hdisplay = scaled_width;
/* use border instead of border minus one */
adjusted_mode->crtc_hblank_start =
scaled_width + right_border;
/* keep the hblank width constant */
adjusted_mode->crtc_hblank_end =
adjusted_mode->crtc_hblank_start +
hblank_width;
/*
* get the hsync start pos relative to
* hblank start
*/
hsync_pos = (hblank_width - hsync_width) / 2;
/* keep the hsync_pos be even */
if (hsync_pos & 1)
hsync_pos++;
adjusted_mode->crtc_hsync_start =
adjusted_mode->crtc_hblank_start +
hsync_pos;
/* keept hsync width constant */
adjusted_mode->crtc_hsync_end =
adjusted_mode->crtc_hsync_start +
hsync_width;
border = 1;
} else if (panel_ratio < desired_ratio) { /* letter */
u32 scaled_height = mode->vdisplay *
horiz_scale / PANEL_RATIO_FACTOR;
vert_ratio = horiz_ratio;
pfit_control |= (HORIZ_AUTO_SCALE |
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);
/* Letterbox will have top/bottom border */
top_border = (adjusted_mode->vdisplay -
scaled_height) / 2;
bottom_border = top_border;
if (mode->vdisplay & 1)
bottom_border++;
adjusted_mode->crtc_vdisplay = scaled_height;
/* use border instead of border minus one */
adjusted_mode->crtc_vblank_start =
scaled_height + bottom_border;
/* keep the vblank width constant */
adjusted_mode->crtc_vblank_end =
adjusted_mode->crtc_vblank_start +
vblank_width;
/*
* get the vsync start pos relative to
* vblank start
*/
vsync_pos = (vblank_width - vsync_width) / 2;
adjusted_mode->crtc_vsync_start =
adjusted_mode->crtc_vblank_start +
vsync_pos;
/* keep the vsync width constant */
adjusted_mode->crtc_vsync_end =
adjusted_mode->crtc_vsync_start +
vsync_width;
border = 1;
} else {
/* Aspects match, Let hw scale both directions */
pfit_control |= (VERT_AUTO_SCALE |
HORIZ_AUTO_SCALE |
VERT_INTERP_BILINEAR |
HORIZ_INTERP_BILINEAR);
}
horiz_bits = (1 << bits) * horiz_ratio /
PANEL_RATIO_FACTOR;
vert_bits = (1 << bits) * vert_ratio /
PANEL_RATIO_FACTOR;
pfit_pgm_ratios =
((vert_bits << PFIT_VERT_SCALE_SHIFT) &
PFIT_VERT_SCALE_MASK) |
((horiz_bits << PFIT_HORIZ_SCALE_SHIFT) &
PFIT_HORIZ_SCALE_MASK);
}
break;

Expand All @@ -456,21 +372,16 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
VERT_INTERP_BILINEAR |
HORIZ_INTERP_BILINEAR);
break;

default:
break;
}

out:
lvds_priv->pfit_control = pfit_control;
lvds_priv->pfit_pgm_ratios = pfit_pgm_ratios;
/*
* When there exists the border, it means that the LVDS_BORDR
* should be enabled.
*/
if (border)
dev_priv->lvds_border_bits |= LVDS_BORDER_ENABLE;
else
dev_priv->lvds_border_bits &= ~(LVDS_BORDER_ENABLE);
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
Expand Down

0 comments on commit 28eabae

Please sign in to comment.