@@ -1614,6 +1614,13 @@ typedef struct drm_i915_private {
*/
struct drm_property *input_size_property;
+ /*
+ * Property 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 *output_border_property;
+
uint32_t hw_context_size;
struct list_head context_list;
@@ -10437,7 +10437,16 @@ 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->output_border_property) {
+ if (val == (uint64_t)intel_crtc->border_size)
+ return 0;
+
+ intel_crtc->border_size = (uint32_t)val;
+ intel_crtc->border_size_changed = 1;
+
+ goto done;
+ }
if (property == dev_priv->input_size_property) {
int new_width = (int)((val >> 16) & 0xffff);
@@ -10466,7 +10475,13 @@ static int intel_crtc_set_property(struct drm_crtc *crtc,
return 0;
}
- return ret;
+ return -EINVAL;
+
+done:
+ if (crtc)
+ intel_crtc_restore_mode(crtc);
+
+ return 0;
}
static const struct drm_crtc_funcs intel_crtc_funcs = {
@@ -10625,6 +10640,15 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
drm_object_attach_property(&intel_crtc->base.base,
dev_priv->input_size_property,
0);
+
+ if (!dev_priv->output_border_property)
+ dev_priv->output_border_property =
+ drm_property_create_range(dev, 0, "border size", 0, 0xFFFFFFFF);
+
+ if (dev_priv->output_border_property)
+ drm_object_attach_property(&intel_crtc->base.base,
+ dev_priv->output_border_property,
+ 0);
}
enum pipe intel_get_pipe_from_connector(struct intel_connector *connector)
@@ -387,6 +387,10 @@ struct intel_crtc {
bool cpu_fifo_underrun_disabled;
bool pch_fifo_underrun_disabled;
+ /* border info for the output/display window */
+ bool border_size_changed;
+ uint32_t border_size;
+
/* per-pipe watermark state */
struct {
/* watermarks currently being used */
@@ -42,6 +42,57 @@ 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 tot_width, tot_height;
+ u32 dst_width, dst_height;
+
+ adjusted_mode = &pipe_config->adjusted_mode;
+
+ tot_width = adjusted_mode->crtc_hdisplay;
+ tot_height = adjusted_mode->crtc_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_size >> 16) & 0xffff) * 2);
+ dst_height = tot_height -
+ ((intel_crtc->border_size & 0xffff) * 2);
+
+ x = y = 0;
+
+ if (tot_width < dst_width) {
+ DRM_ERROR("width is too big\n");
+ return;
+ } else if (dst_width & 1) {
+ DRM_ERROR("width must be even\n");
+ return;
+ } else if (tot_height < dst_height) {
+ DRM_ERROR("height is too big\n");
+ return;
+ } else if (dst_height & 1) {
+ DRM_ERROR("height must be even\n");
+ return;
+ }
+
+ x = (adjusted_mode->hdisplay - dst_width + 1)/2;
+ y = (adjusted_mode->vdisplay - dst_height + 1)/2;
+
+ 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;
+
+ intel_crtc->border_size_changed = 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 +106,13 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
x = y = width = height = 0;
+ /* check if size of borders has changed and border size is
+ not zero, otherwise fall through the regular path */
+ if (intel_crtc->border_size_changed &&
+ intel_crtc->border_size)
+ 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)
@@ -247,6 +305,88 @@ 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->crtc_hdisplay;
+ tot_height = adjusted_mode->crtc_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_size >> 16) & 0xffff) * 2);
+ dst_height = tot_height -
+ ((intel_crtc->border_size & 0xffff) * 2);
+
+ pf_horizontal_ratio = panel_fitter_scaling(src_width, dst_width);
+ pf_vertical_ratio = panel_fitter_scaling(src_height, dst_height);
+
+/* Max Downscale ratio of 1.125, expressed in 1.12 fixed point format */
+#define MAX_DOWNSCALE_RATIO (0x9 << 9)
+
+ if (pf_horizontal_ratio > MAX_DOWNSCALE_RATIO) {
+ DRM_ERROR("width is too small\n");
+ return;
+ } else if (tot_width < dst_width) {
+ DRM_ERROR("width is too big\n");
+ return;
+ } else if (dst_width & 1) {
+ DRM_ERROR("width must be even\n");
+ return;
+ } else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) {
+ DRM_ERROR("height is too small\n");
+ return;
+ } else if (tot_height < dst_height) {
+ DRM_ERROR("height is too big\n");
+ return;
+ } else if (dst_height & 1) {
+ DRM_ERROR("height must be even\n");
+ return;
+ }
+
+ if (dst_width != tot_width)
+ centre_horizontally(adjusted_mode, dst_width);
+ if (dst_height != tot_height)
+ centre_vertically(adjusted_mode, dst_height);
+ 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;
+
+ intel_crtc->border_size_changed = 0;
+}
+
void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
struct intel_crtc_config *pipe_config,
int fitting_mode)
@@ -257,6 +397,13 @@ void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
adjusted_mode = &pipe_config->adjusted_mode;
+ /* check if size of borders has changed and border size is
+ not zero, otherwise fall through the regular path */
+ if (intel_crtc->border_size_changed &&
+ intel_crtc->border_size)
+ 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)