From patchwork Fri Jun 20 13:55:54 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: 4389681 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id C9E5C9F390 for ; Fri, 20 Jun 2014 13:53:03 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 81D8120351 for ; Fri, 20 Jun 2014 13:53:01 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id B83EB203DA for ; Fri, 20 Jun 2014 13:52:58 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 507896EA0A; Fri, 20 Jun 2014 06:52:58 -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 211476EA0A for ; Fri, 20 Jun 2014 06:52:56 -0700 (PDT) Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga101.jf.intel.com with ESMTP; 20 Jun 2014 06:52:55 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.01,514,1400050800"; d="scan'208";a="531560111" Received: from akashgoe-desktop.iind.intel.com ([10.223.82.135]) by orsmga001.jf.intel.com with ESMTP; 20 Jun 2014 06:52:52 -0700 From: akash.goel@intel.com To: intel-gfx@lists.freedesktop.org Date: Fri, 20 Jun 2014 19:25:54 +0530 Message-Id: <1403272554-20383-4-git-send-email-akash.goel@intel.com> X-Mailer: git-send-email 1.9.2 In-Reply-To: <1403272554-20383-1-git-send-email-akash.goel@intel.com> References: <1403272554-20383-1-git-send-email-akash.goel@intel.com> Cc: --cc=vijay.a.purushothaman@intel.com, Akash Goel Subject: [Intel-gfx] [PATCH 3/3] 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.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_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. There are actually 4 separate properties so as to allow a control on the size of each border left/top/bottom/right 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 | 118 ++++++++++++++++++- drivers/gpu/drm/i915/intel_drv.h | 12 ++ drivers/gpu/drm/i915/intel_panel.c | 219 ++++++++++++++++++++++++++++++++--- 4 files changed, 344 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0640071..b1d94d3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1515,6 +1515,16 @@ struct drm_i915_private { struct drm_property *broadcast_rgb_property; struct drm_property *force_audio_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 d95afb6..cf0b67f 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11064,12 +11064,124 @@ out_config: return ret; } +static void intel_create_crtc_properties(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); + + /* Create properties*/ + 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->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, struct drm_property *property, uint64_t val) { - int ret = -ENOENT; + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); - 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)); + + return intel_crtc_restore_mode(crtc); + } + + return -EINVAL; } static const struct drm_crtc_funcs intel_crtc_funcs = { @@ -11516,6 +11628,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs); + intel_create_crtc_properties(&intel_crtc->base); + WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe); return; diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 860d531..34b695f 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -109,6 +109,14 @@ #define INTEL_DSI_VIDEO_MODE 0 #define INTEL_DSI_COMMAND_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; @@ -407,6 +415,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 e605006..f459745 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) @@ -95,6 +110,61 @@ intel_find_panel_downclock(struct drm_device *dev, return NULL; } +bool +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 false; + } + + 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 false; + } else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) { + DRM_ERROR("height is too small\n"); + return false; + } + + 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; + return true; +} + /* adjusted_mode has been preset to be the panel's fixed mode */ bool intel_pch_panel_fitting(struct intel_crtc *intel_crtc, @@ -108,6 +178,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) @@ -169,6 +245,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) { @@ -211,19 +331,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) { @@ -301,6 +408,86 @@ static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config, } } +bool 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 false; + } + + 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 false; + } else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) { + DRM_ERROR("height is too small\n"); + return false; + } + + 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; + return true; +} + bool intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, struct intel_crtc_config *pipe_config, int fitting_mode) @@ -311,6 +498,12 @@ bool 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)