@@ -33,6 +33,22 @@
#include <linux/moduleparam.h>
#include "intel_drv.h"
+/* Max Downscale ratio of 1.125, expressed in 1.12 fixed point format */
+#define MAX_DOWNSCALE_RATIO (0x9 << 9)
+
+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)
@@ -103,6 +119,7 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
{
struct drm_display_mode *adjusted_mode;
int x, y, width, height;
+ u32 pf_horizontal_ratio, pf_vertical_ratio;
adjusted_mode = &pipe_config->adjusted_mode;
@@ -161,6 +178,19 @@ intel_pch_panel_fitting(struct intel_crtc *intel_crtc,
return false;
}
+ pf_horizontal_ratio = panel_fitter_scaling(pipe_config->pipe_src_w,
+ width);
+ pf_vertical_ratio = panel_fitter_scaling(pipe_config->pipe_src_h,
+ height);
+
+ if (pf_horizontal_ratio > MAX_DOWNSCALE_RATIO) {
+ DRM_DEBUG_KMS("Src width is too big to downscale\n");
+ return false;
+ } else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) {
+ DRM_DEBUG_KMS("Src height is too big to downscale\n");
+ return false;
+ }
+
done:
pipe_config->pch_pfit.pos = (x << 16) | y;
pipe_config->pch_pfit.size = (width << 16) | height;
@@ -211,21 +241,9 @@ 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)
+ u32 *pfit_control,
+ u32 *pf_horizontal_ratio, u32 *pf_vertical_ratio)
{
struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
u32 scaled_width = adjusted_mode->hdisplay *
@@ -234,19 +252,39 @@ static void i965_scale_aspect(struct intel_crtc_config *pipe_config,
adjusted_mode->vdisplay;
/* 965+ is easy, it does everything in hw */
- if (scaled_width > scaled_height)
+ if (scaled_width > scaled_height) {
*pfit_control |= PFIT_ENABLE |
PFIT_SCALING_PILLAR;
- else if (scaled_width < scaled_height)
+ *pf_horizontal_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_w,
+ scaled_height / pipe_config->pipe_src_h);
+ *pf_vertical_ratio = panel_fitter_scaling(pipe_config->pipe_src_h,
+ adjusted_mode->vdisplay);
+ }
+ else if (scaled_width < scaled_height) {
*pfit_control |= PFIT_ENABLE |
PFIT_SCALING_LETTER;
- else if (adjusted_mode->hdisplay != pipe_config->pipe_src_w)
+ *pf_vertical_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_h,
+ scaled_width / pipe_config->pipe_src_w);
+ *pf_horizontal_ratio = panel_fitter_scaling(pipe_config->pipe_src_w,
+ adjusted_mode->hdisplay);
+ }
+ else if (adjusted_mode->hdisplay != pipe_config->pipe_src_w) {
*pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
+ *pf_horizontal_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_w,
+ adjusted_mode->hdisplay);
+ *pf_vertical_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_h,
+ adjusted_mode->vdisplay);
+ }
}
static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config,
u32 *pfit_control, u32 *pfit_pgm_ratios,
- u32 *border)
+ u32 *border,
+ u32 *pf_horizontal_ratio, u32 *pf_vertical_ratio)
{
struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
u32 scaled_width = adjusted_mode->hdisplay *
@@ -264,11 +302,15 @@ static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config,
centre_horizontally(adjusted_mode,
scaled_height /
pipe_config->pipe_src_h);
+ *pf_horizontal_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_w,
+ scaled_height / pipe_config->pipe_src_h);
*border = LVDS_BORDER_ENABLE;
if (pipe_config->pipe_src_h != adjusted_mode->vdisplay) {
bits = panel_fitter_scaling(pipe_config->pipe_src_h,
adjusted_mode->vdisplay);
+ *pf_vertical_ratio = bits;
*pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
bits << PFIT_VERT_SCALE_SHIFT);
@@ -280,11 +322,15 @@ static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config,
centre_vertically(adjusted_mode,
scaled_width /
pipe_config->pipe_src_w);
+ *pf_vertical_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_h,
+ scaled_width / pipe_config->pipe_src_w);
*border = LVDS_BORDER_ENABLE;
if (pipe_config->pipe_src_w != adjusted_mode->hdisplay) {
bits = panel_fitter_scaling(pipe_config->pipe_src_w,
adjusted_mode->hdisplay);
+ *pf_horizontal_ratio = bits;
*pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
bits << PFIT_VERT_SCALE_SHIFT);
@@ -298,6 +344,13 @@ static void i9xx_scale_aspect(struct intel_crtc_config *pipe_config,
VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
VERT_INTERP_BILINEAR |
HORIZ_INTERP_BILINEAR);
+
+ *pf_horizontal_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_w,
+ adjusted_mode->hdisplay);
+ *pf_vertical_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_h,
+ adjusted_mode->vdisplay);
}
}
@@ -308,6 +361,7 @@ bool intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
struct drm_device *dev = intel_crtc->base.dev;
u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
struct drm_display_mode *adjusted_mode;
+ u32 pf_horizontal_ratio = 0, pf_vertical_ratio = 0;
adjusted_mode = &pipe_config->adjusted_mode;
@@ -325,20 +379,31 @@ bool intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
centre_horizontally(adjusted_mode, pipe_config->pipe_src_w);
centre_vertically(adjusted_mode, pipe_config->pipe_src_h);
border = LVDS_BORDER_ENABLE;
+ /* 1:1 scaling */
+ pf_horizontal_ratio = pf_vertical_ratio = 1;
break;
case DRM_MODE_SCALE_ASPECT:
/* Scale but preserve the aspect ratio */
if (INTEL_INFO(dev)->gen >= 4)
- i965_scale_aspect(pipe_config, &pfit_control);
+ i965_scale_aspect(pipe_config, &pfit_control,
+ &pf_horizontal_ratio, &pf_vertical_ratio);
else
i9xx_scale_aspect(pipe_config, &pfit_control,
- &pfit_pgm_ratios, &border);
+ &pfit_pgm_ratios, &border,
+ &pf_horizontal_ratio,
+ &pf_vertical_ratio);
break;
case DRM_MODE_SCALE_FULLSCREEN:
/*
* Full scaling, even if it changes the aspect ratio.
* Fortunately this is all done for us in hw.
*/
+ pf_horizontal_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_w,
+ adjusted_mode->hdisplay);
+ pf_vertical_ratio =
+ panel_fitter_scaling(pipe_config->pipe_src_h,
+ adjusted_mode->vdisplay);
if (pipe_config->pipe_src_h != adjusted_mode->vdisplay ||
pipe_config->pipe_src_w != adjusted_mode->hdisplay) {
pfit_control |= PFIT_ENABLE;
@@ -356,6 +421,14 @@ bool intel_gmch_panel_fitting(struct intel_crtc *intel_crtc,
return false;
}
+ if (pf_horizontal_ratio > MAX_DOWNSCALE_RATIO) {
+ DRM_DEBUG_KMS("Src width is too big to downscale\n");
+ return false;
+ } else if (pf_vertical_ratio > MAX_DOWNSCALE_RATIO) {
+ DRM_DEBUG_KMS("Src height is too big to downscale\n");
+ return false;
+ }
+
/* 965+ wants fuzzy fitting */
/* FIXME: handle multiple panels by failing gracefully */
if (INTEL_INFO(dev)->gen >= 4)