[Intel-gfx] [Patch 2/2 _v2] DRM/I915: Sync the panel fitting property with 2D driver
yakui_zhao
yakui.zhao at intel.com
Thu Apr 2 07:32:25 CEST 2009
On Thu, 2009-04-02 at 13:22 +0800, Zhao, Yakui wrote:
Hi, Eric
this time the email is sent by using git-send-email. But after
saving the message from email and running checkpatch.pl, it seems that
the patch is still mangled.
I don't know what I should do.
thanks.
> This covers:
> a. create the scaling mode property and attach it to LVDS
> b. add the support of panel fitting property for LVDS
> c. update the display mode according to the panel fitting mode
>
> v2: the drm_calloc/drm_free is replaced by kzalloc/kfree based
> on Eric's suggestion.
>
> Signed-off-by: Zhao Yakui <yakui.zhao at intel.com>
> ---
> drivers/gpu/drm/i915/i915_reg.h | 16 ++
> drivers/gpu/drm/i915/intel_lvds.c | 282 +++++++++++++++++++++++++++++++++++---
> 2 files changed, 277 insertions(+), 21 deletions(-)
>
> Index: linux-2.6/drivers/gpu/drm/i915/i915_reg.h
> ===================================================================
> --- linux-2.6.orig/drivers/gpu/drm/i915/i915_reg.h 2009-04-02 11:05:28.000000000 +0800
> +++ linux-2.6/drivers/gpu/drm/i915/i915_reg.h 2009-04-02 11:11:36.000000000 +0800
> @@ -819,9 +819,25 @@
> #define HORIZ_INTERP_MASK (3 << 6)
> #define HORIZ_AUTO_SCALE (1 << 5)
> #define PANEL_8TO6_DITHER_ENABLE (1 << 3)
> +#define PFIT_FILTER_FUZZY (0 << 24)
> +#define PFIT_SCALING_AUTO (0 << 26)
> +#define PFIT_SCALING_PROGRAMMED (1 << 26)
> +#define PFIT_SCALING_PILLAR (2 << 26)
> +#define PFIT_SCALING_LETTER (3 << 26)
> #define PFIT_PGM_RATIOS 0x61234
> #define PFIT_VERT_SCALE_MASK 0xfff00000
> #define PFIT_HORIZ_SCALE_MASK 0x0000fff0
> +/* Pre-965 */
> +#define PFIT_VERT_SCALE_SHIFT 20
> +#define PFIT_VERT_SCALE_MASK 0xfff00000
> +#define PFIT_HORIZ_SCALE_SHIFT 4
> +#define PFIT_HORIZ_SCALE_MASK 0x0000fff0
> +/* 965+ */
> +#define PFIT_VERT_SCALE_SHIFT_965 16
> +#define PFIT_VERT_SCALE_MASK_965 0x1fff0000
> +#define PFIT_HORIZ_SCALE_SHIFT_965 0
> +#define PFIT_HORIZ_SCALE_MASK_965 0x00001fff
> +
> #define PFIT_AUTO_RATIOS 0x61238
>
> /* Backlight control */
> Index: linux-2.6/drivers/gpu/drm/i915/intel_lvds.c
> ===================================================================
> --- linux-2.6.orig/drivers/gpu/drm/i915/intel_lvds.c 2009-03-30 10:08:58.000000000 +0800
> +++ linux-2.6/drivers/gpu/drm/i915/intel_lvds.c 2009-04-02 11:40:13.000000000 +0800
> @@ -37,6 +37,21 @@
> #include "i915_drm.h"
> #include "i915_drv.h"
>
> +/*
> + * the following four scaling options are defined.
> + * #define DRM_MODE_SCALE_NON_GPU 0
> + * #define DRM_MODE_SCALE_FULLSCREEN 1
> + * #define DRM_MODE_SCALE_NO_SCALE 2
> + * #define DRM_MODE_SCALE_ASPECT 3
> + */
> +
> +/* Private structure for the integrated LVDS support */
> +struct intel_lvds_priv {
> + int fitting_mode;
> + u32 pfit_control;
> + u32 pfit_pgm_ratios;
> +};
> +
> /**
> * Sets the backlight level.
> *
> @@ -160,10 +175,24 @@
> 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_output *intel_output = enc_to_intel_output(encoder);
> + struct intel_lvds_priv *lvds_priv = intel_output->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;
>
> /* Should never happen!! */
> if (!IS_I965G(dev) && intel_crtc->pipe == 0) {
> @@ -179,7 +208,9 @@
> return false;
> }
> }
> -
> + /* If we don't have a panel mode, there is nothing we can do */
> + if (dev_priv->panel_fixed_mode == NULL)
> + return true;
> /*
> * If we have timings from the BIOS for the panel, put them in
> * to the adjusted mode. The CRTC will be set up for this mode,
> @@ -203,6 +234,191 @@
> drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
> }
>
> + /* Make sure pre-965s set dither correctly */
> + if (!IS_I965G(dev)) {
> + if (dev_priv->panel_wants_dither || dev_priv->lvds_dither)
> + pfit_control |= PANEL_8TO6_DITHER_ENABLE;
> + }
> +
> + /* Native modes don't need fitting */
> + if (adjusted_mode->hdisplay == mode->hdisplay &&
> + adjusted_mode->vdisplay == mode->vdisplay) {
> + pfit_pgm_ratios = 0;
> + border = 0;
> + goto out;
> + }
> +
> + /* 965+ wants fuzzy fitting */
> + if (IS_I965G(dev))
> + pfit_control |= (intel_crtc->pipe << PFIT_PIPE_SHIFT) |
> + PFIT_FILTER_FUZZY;
> +
> + /*
> + * 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;
> + /*
> + * Enable automatic panel scaling for non-native modes so that they fill
> + * the screen. Should be enabled before the pipe is enabled, according
> + * to register description and PRM.
> + * Change the value here to see the borders for debugging
> + */
> + I915_WRITE(BCLRPAT_A, 0);
> + I915_WRITE(BCLRPAT_B, 0);
> +
> + switch (lvds_priv->fitting_mode) {
> + case DRM_MODE_SCALE_NO_SCALE:
> + /*
> + * 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;
> + adjusted_mode->crtc_hblank_start = mode->hdisplay +
> + right_border - 1;
> + adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_htotal -
> + left_border - 1;
> + adjusted_mode->crtc_hsync_start =
> + adjusted_mode->crtc_hblank_start;
> + adjusted_mode->crtc_hsync_end =
> + adjusted_mode->crtc_hblank_end;
> + adjusted_mode->crtc_vdisplay = mode->vdisplay;
> + adjusted_mode->crtc_vblank_start = mode->vdisplay +
> + bottom_border - 1;
> + adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vtotal -
> + top_border - 1;
> + adjusted_mode->crtc_vsync_start =
> + adjusted_mode->crtc_vblank_start;
> + adjusted_mode->crtc_vsync_end =
> + adjusted_mode->crtc_vblank_end;
> + border = 1;
> + break;
> + case DRM_MODE_SCALE_ASPECT:
> + /* Scale but preserve the spect ratio */
> + pfit_control |= PFIT_ENABLE;
> + if (IS_I965G(dev)) {
> + /* 965+ is easy, it does everything in hw */
> + if (panel_ratio > desired_ratio)
> + pfit_control |= PFIT_SCALING_PILLAR;
> + else if (panel_ratio < desired_ratio)
> + pfit_control |= PFIT_SCALING_LETTER;
> + else
> + pfit_control |= PFIT_SCALING_AUTO;
> + } else {
> + /*
> + * 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++;
> + adjusted_mode->crtc_hdisplay = scaled_width;
> + adjusted_mode->crtc_hblank_start =
> + scaled_width + right_border - 1;
> + adjusted_mode->crtc_hblank_end =
> + adjusted_mode->crtc_htotal - left_border - 1;
> + adjusted_mode->crtc_hsync_start =
> + adjusted_mode->crtc_hblank_start;
> + adjusted_mode->crtc_hsync_end =
> + adjusted_mode->crtc_hblank_end;
> + 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 |
> + 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;
> + adjusted_mode->crtc_vblank_start =
> + scaled_height + bottom_border - 1;
> + adjusted_mode->crtc_vblank_end =
> + adjusted_mode->crtc_vtotal - top_border - 1;
> + adjusted_mode->crtc_vsync_start =
> + adjusted_mode->crtc_vblank_start;
> + adjusted_mode->crtc_vsync_end =
> + adjusted_mode->crtc_vblank_end;
> + 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;
> +
> + case DRM_MODE_SCALE_FULLSCREEN:
> + /*
> + * Full scaling, even if it changes the aspect ratio.
> + * Fortunately this is all done for us in hw.
> + */
> + pfit_control |= PFIT_ENABLE;
> + if (IS_I965G(dev))
> + pfit_control |= PFIT_SCALING_AUTO;
> + else
> + pfit_control |= (VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
> + VERT_INTERP_BILINEAR |
> + HORIZ_INTERP_BILINEAR);
> + break;
> + default:
> + break;
> + }
> +
> +out:
> + lvds_priv->pfit_control = pfit_control;
> + lvds_priv->pfit_pgm_ratios = pfit_pgm_ratios;
> /*
> * XXX: It would be nice to support lower refresh rates on the
> * panels to reduce power consumption, and perhaps match the
> @@ -242,8 +458,8 @@
> {
> 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);
> - u32 pfit_control;
> + struct intel_output *intel_output = enc_to_intel_output(encoder);
> + struct intel_lvds_priv *lvds_priv = intel_output->dev_priv;
>
> /*
> * The LVDS pin pair will already have been turned on in the
> @@ -256,22 +472,8 @@
> * screen. Should be enabled before the pipe is enabled, according to
> * register description and PRM.
> */
> - if (mode->hdisplay != adjusted_mode->hdisplay ||
> - mode->vdisplay != adjusted_mode->vdisplay)
> - pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
> - HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
> - HORIZ_INTERP_BILINEAR);
> - else
> - pfit_control = 0;
> -
> - if (!IS_I965G(dev)) {
> - if (dev_priv->panel_wants_dither || dev_priv->lvds_dither)
> - pfit_control |= PANEL_8TO6_DITHER_ENABLE;
> - }
> - else
> - pfit_control |= intel_crtc->pipe << PFIT_PIPE_SHIFT;
> -
> - I915_WRITE(PFIT_CONTROL, pfit_control);
> + I915_WRITE(PFIT_PGM_RATIOS, lvds_priv->pfit_pgm_ratios);
> + I915_WRITE(PFIT_CONTROL, lvds_priv->pfit_control);
> }
>
> /**
> @@ -344,10 +546,35 @@
> uint64_t value)
> {
> struct drm_device *dev = connector->dev;
> + struct intel_output *intel_output =
> + to_intel_output(connector);
>
> if (property == dev->mode_config.dpms_property && connector->encoder)
> intel_lvds_dpms(connector->encoder, (uint32_t)(value & 0xf));
>
> + if (property == dev->mode_config.scaling_mode_property &&
> + connector->encoder) {
> + struct drm_crtc *crtc = connector->encoder->crtc;
> + struct intel_lvds_priv *lvds_priv = intel_output->dev_priv;
> +
> + if (value == DRM_MODE_SCALE_NON_GPU) {
> + DRM_DEBUG("non_GPU property is unsupported.\n");
> + return 0;
> + }
> +
> + if (lvds_priv->fitting_mode == value)
> + return 0;
> +
> + lvds_priv->fitting_mode = value;
> + if (crtc && crtc->enabled) {
> + /*
> + * If the CRTC is enabled, the display will be changed
> + * according to the new panel fitting mode.
> + */
> + drm_crtc_helper_set_mode(crtc, &crtc->mode,
> + crtc->x, crtc->y, crtc->fb);
> + }
> + }
> return 0;
> }
>
> @@ -401,6 +628,7 @@
> struct drm_encoder *encoder;
> struct drm_display_mode *scan; /* *modes, *bios_mode; */
> struct drm_crtc *crtc;
> + struct intel_lvds_priv *lvds_priv;
> u32 lvds;
> int pipe;
>
> @@ -414,7 +642,8 @@
> return;
> }
>
> - intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
> + intel_output = kzalloc(sizeof(struct intel_output) +
> + sizeof(struct intel_lvds_priv), GFP_KERNEL);
> if (!intel_output) {
> return;
> }
> @@ -436,7 +665,18 @@
> connector->interlace_allowed = false;
> connector->doublescan_allowed = false;
>
> + lvds_priv = (struct intel_lvds_priv *)(intel_output + 1);
> + intel_output->dev_priv = lvds_priv;
> + /* create the scaling mode property */
> + drm_mode_create_scaling_mode_property(dev);
> + /*
> + * the initial panel fitting mode will be FULL_ASPECT
> + */
>
> + drm_connector_attach_property(&intel_output->base,
> + dev->mode_config.scaling_mode_property,
> + DRM_MODE_SCALE_ASPECT);
> + lvds_priv->fitting_mode = DRM_MODE_SCALE_ASPECT;
> /*
> * LVDS discovery:
> * 1) check for EDID on DDC
> @@ -516,5 +756,5 @@
> if (intel_output->ddc_bus)
> intel_i2c_destroy(intel_output->ddc_bus);
> drm_connector_cleanup(connector);
> - kfree(connector);
> + kfree(intel_output);
> }
More information about the Intel-gfx
mailing list