From patchwork Sun Apr 20 10:49:00 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: akash.goel@intel.com X-Patchwork-Id: 4021011 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id CC043BFF02 for ; Sun, 20 Apr 2014 10:46:12 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 669112022D for ; Sun, 20 Apr 2014 10:46:11 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id EFE82201EF for ; Sun, 20 Apr 2014 10:46:09 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id DFE716E16A; Sun, 20 Apr 2014 03:46:08 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by gabe.freedesktop.org (Postfix) with ESMTP id 2903E6E16A for ; Sun, 20 Apr 2014 03:46:08 -0700 (PDT) Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by orsmga101.jf.intel.com with ESMTP; 20 Apr 2014 03:46:07 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.97,891,1389772800"; d="scan'208";a="516210556" Received: from akashgoe-desktop.iind.intel.com ([10.223.82.34]) by fmsmga001.fm.intel.com with ESMTP; 20 Apr 2014 03:46:05 -0700 From: akash.goel@intel.com To: intel-gfx@lists.freedesktop.org Date: Sun, 20 Apr 2014 16:19:00 +0530 Message-Id: <1397990940-24929-1-git-send-email-akash.goel@intel.com> X-Mailer: git-send-email 1.9.2 In-Reply-To: <20140411114221.GB18465@intel.com> References: <20140411114221.GB18465@intel.com> Cc: Akash Goel Subject: [Intel-gfx] [PATCH v5 3/4] drm/i915: New drm crtc property for varying the size of borders X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-4.8 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Akash Goel This patch adds a new drm crtc property for varying the size of the horizontal & vertical borers of the output/display window. This will control the output of Panel fitter. v2: Added a new check for the invalid border size input v3: Fixed bugs in output window calculation Removed superfluous checks v4: Added the capability to forecfully enable the Panel fitter. The property value is of 64 bits, first 32 bits are used for border dimensions. The 33rd bit can be used to forcefully enable the panel fitter. This is useful for Panels which do not override the User specified Pipe timings. v5: Splitted the single border property into 4 separate properties so as to allow a control on the size of each border left/top/bottom/right (Ville) Reverted the changes done in v4 of the patch, which provided forceful enabling of panel fitter. Also refactored based on latest codebase. Testcase: igt/kms_panel_fitter_test Signed-off-by: Akash Goel --- drivers/gpu/drm/i915/i915_drv.h | 10 ++ drivers/gpu/drm/i915/intel_display.c | 107 ++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 12 ++ drivers/gpu/drm/i915/intel_panel.c | 217 ++++++++++++++++++++++++++++++++--- 4 files changed, 331 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 104a232..dfe3e03 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1447,6 +1447,16 @@ struct drm_i915_private { */ struct drm_property *input_size_property; + /* + * Properties to dynamically vary the size of the + * borders. This will indirectly control the size + * of the display window i.e Panel fitter output + */ + struct drm_property *left_border_property; + struct drm_property *right_border_property; + struct drm_property *top_border_property; + struct drm_property *bottom_border_property; + uint32_t hw_context_size; struct list_head context_list; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 5484ae2..8ff99bf 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -10438,15 +10438,57 @@ static void intel_create_crtc_properties(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + /* Create properties*/ if (!dev_priv->input_size_property) dev_priv->input_size_property = drm_property_create_range(dev, 0, "input size", 0, UINT_MAX); + if (!dev_priv->left_border_property) + dev_priv->left_border_property = + drm_property_create_range(dev, 0, "left border", + 0, (uint64_t)MAX_BORDER_VALUE); + + if (!dev_priv->right_border_property) + dev_priv->right_border_property = + drm_property_create_range(dev, 0, "right border", + 0, (uint64_t)MAX_BORDER_VALUE); + + if (!dev_priv->top_border_property) + dev_priv->top_border_property = + drm_property_create_range(dev, 0, "top border", + 0, (uint64_t)MAX_BORDER_VALUE); + + if (!dev_priv->bottom_border_property) + dev_priv->bottom_border_property = + drm_property_create_range(dev, 0, "bottom border", + 0, (uint64_t)MAX_BORDER_VALUE); + + /* Attach to properties*/ if (dev_priv->input_size_property) drm_object_attach_property(&intel_crtc->base.base, dev_priv->input_size_property, 0); + + if (dev_priv->left_border_property) + drm_object_attach_property(&intel_crtc->base.base, + dev_priv->left_border_property, + 0); + + if (dev_priv->right_border_property) + drm_object_attach_property(&intel_crtc->base.base, + dev_priv->right_border_property, + 0); + + if (dev_priv->top_border_property) + drm_object_attach_property(&intel_crtc->base.base, + dev_priv->top_border_property, + 0); + + if (dev_priv->bottom_border_property) + drm_object_attach_property(&intel_crtc->base.base, + dev_priv->bottom_border_property, + 0); } static int intel_crtc_set_property(struct drm_crtc *crtc, @@ -10455,7 +10497,6 @@ static int intel_crtc_set_property(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - int ret = -ENOENT; if (property == dev_priv->input_size_property) { int new_width = (int)((val >> 16) & 0xffff); @@ -10497,7 +10538,69 @@ static int intel_crtc_set_property(struct drm_crtc *crtc, return 0; } - return ret; + if (property == dev_priv->left_border_property) { + if (val == (uint64_t)intel_crtc->border[PANEL_BORDER_LEFT]) + return 0; + if (val & 1) { + DRM_ERROR("Odd border value not supported\n"); + return -EINVAL; + } + + intel_crtc->border[PANEL_BORDER_LEFT] = (uint32_t)val; + goto done; + } + + if (property == dev_priv->right_border_property) { + if (val == (uint64_t)intel_crtc->border[PANEL_BORDER_RIGHT]) + return 0; + if (val & 1) { + DRM_ERROR("Odd border value not supported\n"); + return -EINVAL; + } + + intel_crtc->border[PANEL_BORDER_RIGHT] = (uint32_t)val; + goto done; + } + + if (property == dev_priv->top_border_property) { + if (val == (uint64_t)intel_crtc->border[PANEL_BORDER_TOP]) + return 0; + if (val & 1) { + DRM_ERROR("Odd border value not supported\n"); + return -EINVAL; + } + + intel_crtc->border[PANEL_BORDER_TOP] = (uint32_t)val; + goto done; + } + + if (property == dev_priv->bottom_border_property) { + if (val == (uint64_t)intel_crtc->border[PANEL_BORDER_BOTTOM]) + return 0; + if (val & 1) { + DRM_ERROR("Odd border value not supported\n"); + return -EINVAL; + } + + intel_crtc->border[PANEL_BORDER_BOTTOM] = (uint32_t)val; + goto done; + } + + return -EINVAL; + +done: + if (crtc) { + intel_crtc->apply_borders = + ((intel_crtc->border[PANEL_BORDER_LEFT] != 0) || + (intel_crtc->border[PANEL_BORDER_RIGHT] != 0) || + (intel_crtc->border[PANEL_BORDER_TOP] != 0) || + (intel_crtc->border[PANEL_BORDER_BOTTOM] != 0)); + + intel_crtc_restore_mode(crtc); + return 0; + } + + return -EINVAL; } static const struct drm_crtc_funcs intel_crtc_funcs = { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index c551472..f00d075 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -109,6 +109,14 @@ #define INTEL_DSI_COMMAND_MODE 0 #define INTEL_DSI_VIDEO_MODE 1 +#define MAX_BORDER_VALUE 256 +enum panel_border { + PANEL_BORDER_LEFT = 0, + PANEL_BORDER_RIGHT, + PANEL_BORDER_TOP, + PANEL_BORDER_BOTTOM, +}; + struct intel_framebuffer { struct drm_framebuffer base; struct drm_i915_gem_object *obj; @@ -395,6 +403,10 @@ struct intel_crtc { bool cpu_fifo_underrun_disabled; bool pch_fifo_underrun_disabled; + /* border info for the output/display window */ + uint32_t border[4]; + bool apply_borders; + /* per-pipe watermark state */ struct { /* watermarks currently being used */ diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 44ad415..3dbb774 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -29,10 +29,25 @@ */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +/* Max Downscale ratio of 1.125, expressed in 1.12 fixed point format */ +#define MAX_DOWNSCALE_RATIO (0x9 << 9) #include #include "intel_drv.h" +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; +} + void intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode) @@ -42,6 +57,60 @@ intel_fixed_panel_mode(const struct drm_display_mode *fixed_mode, drm_mode_set_crtcinfo(adjusted_mode, 0); } +void +intel_pch_manual_panel_fitting(struct intel_crtc *intel_crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_display_mode *adjusted_mode; + int x, y; + u32 pf_horizontal_ratio, pf_vertical_ratio; + u32 tot_width, tot_height; + u32 src_width, src_height; /* pipesrc.x, pipesrc.y */ + u32 dst_width, dst_height; + + adjusted_mode = &pipe_config->adjusted_mode; + + src_width = pipe_config->pipe_src_w; + src_height = pipe_config->pipe_src_h; + + tot_width = adjusted_mode->hdisplay; + tot_height = adjusted_mode->vdisplay; + + /* + * Having non zero borders will reduce the size of 'HACTIVE/VACTIVE' + * region. So (HACTIVE - Left border - Right Border) * + * (VACTIVE - Top Border - Bottom border) will effectively be the + * output rectangle on screen + */ + dst_width = tot_width - intel_crtc->border[PANEL_BORDER_LEFT] - + intel_crtc->border[PANEL_BORDER_RIGHT]; + dst_height = tot_height - intel_crtc->border[PANEL_BORDER_TOP] - + intel_crtc->border[PANEL_BORDER_BOTTOM]; + + if ((dst_width == 0) || (dst_height == 0)) { + DRM_ERROR("Invalid border size input\n"); + return; + } + + pf_horizontal_ratio = panel_fitter_scaling(src_width, dst_width); + pf_vertical_ratio = panel_fitter_scaling(src_height, dst_height); + + if (pf_horizontal_ratio > MAX_DOWNSCALE_RATIO) { + DRM_ERROR("width is too small\n"); + return; + } else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) { + DRM_ERROR("height is too small\n"); + return; + } + + x = intel_crtc->border[PANEL_BORDER_LEFT]; + y = intel_crtc->border[PANEL_BORDER_TOP]; + + pipe_config->pch_pfit.pos = (x << 16) | y; + pipe_config->pch_pfit.size = (dst_width << 16) | dst_height; + pipe_config->pch_pfit.enabled = pipe_config->pch_pfit.size != 0; +} + /* adjusted_mode has been preset to be the panel's fixed mode */ void intel_pch_panel_fitting(struct intel_crtc *intel_crtc, @@ -55,6 +124,12 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc, x = y = width = height = 0; + /* check if User wants to apply the borders, otherwise fall + through the regular path */ + if (intel_crtc->apply_borders) + return intel_pch_manual_panel_fitting(intel_crtc, + pipe_config); + /* Native modes don't need fitting */ if (adjusted_mode->hdisplay == pipe_config->pipe_src_w && adjusted_mode->vdisplay == pipe_config->pipe_src_h) @@ -115,6 +190,50 @@ done: } static void +apply_horizontal_borders(struct drm_display_mode *mode, + int *border) +{ + u32 width, 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; + + width = mode->hdisplay - border[PANEL_BORDER_LEFT] - + border[PANEL_BORDER_RIGHT]; + + mode->crtc_hdisplay = width; + mode->crtc_hblank_start = width + border[PANEL_BORDER_RIGHT]; + 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 +apply_vertical_borders(struct drm_display_mode *mode, + int *border) +{ + u32 height, 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; + + height = mode->vdisplay - border[PANEL_BORDER_TOP] - + border[PANEL_BORDER_BOTTOM]; + + mode->crtc_vdisplay = height; + mode->crtc_vblank_start = height + border[PANEL_BORDER_BOTTOM]; + 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 void centre_horizontally(struct drm_display_mode *mode, int width) { @@ -157,19 +276,6 @@ centre_vertically(struct drm_display_mode *mode, 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 void i965_scale_aspect(struct intel_crtc_config *pipe_config, u32 *pfit_control) { @@ -247,6 +353,85 @@ static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config, } } +void intel_gmch_manual_panel_fitting(struct intel_crtc *intel_crtc, + struct intel_crtc_config *pipe_config) +{ + struct drm_device *dev = intel_crtc->base.dev; + u32 pfit_control = 0, border = 0; + u32 pf_horizontal_ratio, pf_vertical_ratio; + struct drm_display_mode *adjusted_mode; + u32 tot_width, tot_height; + u32 src_width, src_height; /* pipesrc.x, pipesrc.y */ + u32 dst_width, dst_height; + + adjusted_mode = &pipe_config->adjusted_mode; + + src_width = pipe_config->pipe_src_w; + src_height = pipe_config->pipe_src_h; + + tot_width = adjusted_mode->hdisplay; + tot_height = adjusted_mode->vdisplay; + + /* + * Having non zero borders will reduce the size of 'HACTIVE/VACTIVE' + * region. So (HACTIVE - Left border - Right Border) * + * (VACTIVE - Top Border - Bottom border) will effectively be the + * output rectangle on screen + */ + dst_width = tot_width - intel_crtc->border[PANEL_BORDER_LEFT] - + intel_crtc->border[PANEL_BORDER_RIGHT]; + dst_height = tot_height - intel_crtc->border[PANEL_BORDER_TOP] - + intel_crtc->border[PANEL_BORDER_BOTTOM]; + + if ((dst_width == 0) || (dst_height == 0)) { + DRM_ERROR("Invalid border size input\n"); + return; + } + + pf_horizontal_ratio = panel_fitter_scaling(src_width, dst_width); + pf_vertical_ratio = panel_fitter_scaling(src_height, dst_height); + + if (pf_horizontal_ratio > MAX_DOWNSCALE_RATIO) { + DRM_ERROR("width is too small\n"); + return; + } else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) { + DRM_ERROR("height is too small\n"); + return; + } + + if (dst_width != tot_width) + apply_horizontal_borders(adjusted_mode, intel_crtc->border); + if (dst_height != tot_height) + apply_vertical_borders(adjusted_mode, intel_crtc->border); + + /* No scaling needed now, but still enable the panel fitter, + as that will allow the User to subequently do the dynamic + flipping of fbs of different resolutions */ + if (adjusted_mode->crtc_hdisplay == pipe_config->pipe_src_w && + adjusted_mode->crtc_vdisplay == pipe_config->pipe_src_h) { + DRM_DEBUG_KMS("Forcefully enabling the Panel fitter\n"); + } + + border = LVDS_BORDER_ENABLE; + + if (INTEL_INFO(dev)->gen >= 4) { + /* PFIT_SCALING_PROGRAMMED is de-featured on BYT */ + pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; + pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | PFIT_FILTER_FUZZY); + } else { + pfit_control |= (PFIT_ENABLE | + VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | + VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR); + } + + /* Make sure pre-965 set dither correctly for 18bpp panels. */ + if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) + pfit_control |= PANEL_8TO6_DITHER_ENABLE; + + pipe_config->gmch_pfit.control = pfit_control; + pipe_config->gmch_pfit.lvds_border_bits = border; +} + void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, struct intel_crtc_config *pipe_config, int fitting_mode) @@ -257,6 +442,12 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, adjusted_mode = &pipe_config->adjusted_mode; + /* check if User wants to apply the borders, otherwise fall + through the regular path */ + if (intel_crtc->apply_borders) + return intel_gmch_manual_panel_fitting(intel_crtc, + pipe_config); + /* Native modes don't need fitting */ if (adjusted_mode->hdisplay == pipe_config->pipe_src_w && adjusted_mode->vdisplay == pipe_config->pipe_src_h)