xref: /linux/drivers/gpu/drm/i915/display/intel_pfit.c (revision 2c1ed907520c50326b8f604907a8478b27881a2e)
10f16cd2aSVille Syrjälä // SPDX-License-Identifier: MIT
20f16cd2aSVille Syrjälä /*
30f16cd2aSVille Syrjälä  * Copyright © 2024 Intel Corporation
40f16cd2aSVille Syrjälä  */
50f16cd2aSVille Syrjälä 
60f16cd2aSVille Syrjälä #include "i915_reg.h"
7*96bd1d50SJani Nikula #include "i915_utils.h"
80f16cd2aSVille Syrjälä #include "intel_display_core.h"
90f16cd2aSVille Syrjälä #include "intel_display_driver.h"
100f16cd2aSVille Syrjälä #include "intel_display_types.h"
110f16cd2aSVille Syrjälä #include "intel_lvds_regs.h"
120f16cd2aSVille Syrjälä #include "intel_pfit.h"
130f16cd2aSVille Syrjälä 
intel_pch_pfit_check_dst_window(const struct intel_crtc_state * crtc_state)140f16cd2aSVille Syrjälä static int intel_pch_pfit_check_dst_window(const struct intel_crtc_state *crtc_state)
150f16cd2aSVille Syrjälä {
160f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
170f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
180f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
190f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
200f16cd2aSVille Syrjälä 	const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
210f16cd2aSVille Syrjälä 	int width = drm_rect_width(dst);
220f16cd2aSVille Syrjälä 	int height = drm_rect_height(dst);
230f16cd2aSVille Syrjälä 	int x = dst->x1;
240f16cd2aSVille Syrjälä 	int y = dst->y1;
250f16cd2aSVille Syrjälä 
260f16cd2aSVille Syrjälä 	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE &&
270f16cd2aSVille Syrjälä 	    (y & 1 || height & 1)) {
280f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
290f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") misaligned for interlaced output\n",
300f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
310f16cd2aSVille Syrjälä 		return -EINVAL;
320f16cd2aSVille Syrjälä 	}
330f16cd2aSVille Syrjälä 
340f16cd2aSVille Syrjälä 	/*
350f16cd2aSVille Syrjälä 	 * "Restriction : When pipe scaling is enabled, the scaled
360f16cd2aSVille Syrjälä 	 *  output must equal the pipe active area, so Pipe active
370f16cd2aSVille Syrjälä 	 *  size = (2 * PF window position) + PF window size."
380f16cd2aSVille Syrjälä 	 *
390f16cd2aSVille Syrjälä 	 * The vertical direction seems more forgiving than the
400f16cd2aSVille Syrjälä 	 * horizontal direction, but still has some issues so
410f16cd2aSVille Syrjälä 	 * let's follow the same hard rule for both.
420f16cd2aSVille Syrjälä 	 */
430f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay != 2 * x + width ||
440f16cd2aSVille Syrjälä 	    adjusted_mode->crtc_vdisplay != 2 * y + height) {
450f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
460f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") not centered\n",
470f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
480f16cd2aSVille Syrjälä 		return -EINVAL;
490f16cd2aSVille Syrjälä 	}
500f16cd2aSVille Syrjälä 
510f16cd2aSVille Syrjälä 	/*
520f16cd2aSVille Syrjälä 	 * "Restriction : The X position must not be programmed
530f16cd2aSVille Syrjälä 	 *  to be 1 (28:16=0 0000 0000 0001b)."
540f16cd2aSVille Syrjälä 	 */
550f16cd2aSVille Syrjälä 	if (x == 1) {
560f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
570f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") badly positioned\n",
580f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
590f16cd2aSVille Syrjälä 		return -EINVAL;
600f16cd2aSVille Syrjälä 	}
610f16cd2aSVille Syrjälä 
620f16cd2aSVille Syrjälä 	return 0;
630f16cd2aSVille Syrjälä }
640f16cd2aSVille Syrjälä 
intel_pch_pfit_check_src_size(const struct intel_crtc_state * crtc_state)650f16cd2aSVille Syrjälä static int intel_pch_pfit_check_src_size(const struct intel_crtc_state *crtc_state)
660f16cd2aSVille Syrjälä {
670f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
680f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
690f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
700f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
710f16cd2aSVille Syrjälä 	int max_src_w, max_src_h;
720f16cd2aSVille Syrjälä 
730f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) >= 8) {
740f16cd2aSVille Syrjälä 		max_src_w = 4096;
750f16cd2aSVille Syrjälä 		max_src_h = 4096;
760f16cd2aSVille Syrjälä 	} else if (DISPLAY_VER(display) >= 7) {
770f16cd2aSVille Syrjälä 		/*
780f16cd2aSVille Syrjälä 		 * PF0 7x5 capable
790f16cd2aSVille Syrjälä 		 * PF1 3x3 capable (could be switched to 7x5
800f16cd2aSVille Syrjälä 		 *                  mode on HSW when PF2 unused)
810f16cd2aSVille Syrjälä 		 * PF2 3x3 capable
820f16cd2aSVille Syrjälä 		 *
830f16cd2aSVille Syrjälä 		 * This assumes we use a 1:1 mapping between pipe and PF.
840f16cd2aSVille Syrjälä 		 */
850f16cd2aSVille Syrjälä 		max_src_w = crtc->pipe == PIPE_A ? 4096 : 2048;
860f16cd2aSVille Syrjälä 		max_src_h = 4096;
870f16cd2aSVille Syrjälä 	} else {
880f16cd2aSVille Syrjälä 		max_src_w = 4096;
890f16cd2aSVille Syrjälä 		max_src_h = 4096;
900f16cd2aSVille Syrjälä 	}
910f16cd2aSVille Syrjälä 
920f16cd2aSVille Syrjälä 	if (pipe_src_w > max_src_w || pipe_src_h > max_src_h) {
930f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
940f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] source size (%dx%d) exceeds pfit max (%dx%d)\n",
950f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
960f16cd2aSVille Syrjälä 			    pipe_src_w, pipe_src_h, max_src_w, max_src_h);
970f16cd2aSVille Syrjälä 		return -EINVAL;
980f16cd2aSVille Syrjälä 	}
990f16cd2aSVille Syrjälä 
1000f16cd2aSVille Syrjälä 	return 0;
1010f16cd2aSVille Syrjälä }
1020f16cd2aSVille Syrjälä 
intel_pch_pfit_check_scaling(const struct intel_crtc_state * crtc_state)1030f16cd2aSVille Syrjälä static int intel_pch_pfit_check_scaling(const struct intel_crtc_state *crtc_state)
1040f16cd2aSVille Syrjälä {
1050f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
1060f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
1070f16cd2aSVille Syrjälä 	const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
1080f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
1090f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
1100f16cd2aSVille Syrjälä 	int hscale, vscale, max_scale = 0x12000; /* 1.125 */
1110f16cd2aSVille Syrjälä 	struct drm_rect src;
1120f16cd2aSVille Syrjälä 
1130f16cd2aSVille Syrjälä 	drm_rect_init(&src, 0, 0, pipe_src_w << 16, pipe_src_h << 16);
1140f16cd2aSVille Syrjälä 
1150f16cd2aSVille Syrjälä 	hscale = drm_rect_calc_hscale(&src, dst, 0, max_scale);
1160f16cd2aSVille Syrjälä 	if (hscale < 0) {
1170f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
1180f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit horizontal downscaling (%d->%d) exceeds max (0x%x)\n",
1190f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
1200f16cd2aSVille Syrjälä 			    pipe_src_w, drm_rect_width(dst),
1210f16cd2aSVille Syrjälä 			    max_scale);
1220f16cd2aSVille Syrjälä 		return hscale;
1230f16cd2aSVille Syrjälä 	}
1240f16cd2aSVille Syrjälä 
1250f16cd2aSVille Syrjälä 	vscale = drm_rect_calc_vscale(&src, dst, 0, max_scale);
1260f16cd2aSVille Syrjälä 	if (vscale < 0) {
1270f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
1280f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit vertical downscaling (%d->%d) exceeds max (0x%x)\n",
1290f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
1300f16cd2aSVille Syrjälä 			    pipe_src_h, drm_rect_height(dst),
1310f16cd2aSVille Syrjälä 			    max_scale);
1320f16cd2aSVille Syrjälä 		return vscale;
1330f16cd2aSVille Syrjälä 	}
1340f16cd2aSVille Syrjälä 
1350f16cd2aSVille Syrjälä 	return 0;
1360f16cd2aSVille Syrjälä }
1370f16cd2aSVille Syrjälä 
intel_pch_pfit_check_timings(const struct intel_crtc_state * crtc_state)1380f16cd2aSVille Syrjälä static int intel_pch_pfit_check_timings(const struct intel_crtc_state *crtc_state)
1390f16cd2aSVille Syrjälä {
1400f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
1410f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
1420f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
1430f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
1440f16cd2aSVille Syrjälä 
1450f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_vdisplay < 7) {
1460f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
1470f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] vertical active (%d) below minimum (%d) for pfit\n",
1480f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
1490f16cd2aSVille Syrjälä 			    adjusted_mode->crtc_vdisplay, 7);
1500f16cd2aSVille Syrjälä 		return -EINVAL;
1510f16cd2aSVille Syrjälä 	}
1520f16cd2aSVille Syrjälä 
1530f16cd2aSVille Syrjälä 	return 0;
1540f16cd2aSVille Syrjälä }
1550f16cd2aSVille Syrjälä 
intel_pch_pfit_check_cloning(const struct intel_crtc_state * crtc_state)1560f16cd2aSVille Syrjälä static int intel_pch_pfit_check_cloning(const struct intel_crtc_state *crtc_state)
1570f16cd2aSVille Syrjälä {
1580f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
1590f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
1600f16cd2aSVille Syrjälä 
1610f16cd2aSVille Syrjälä 	/*
1620f16cd2aSVille Syrjälä 	 * The panel fitter is in the pipe and thus would affect every
1630f16cd2aSVille Syrjälä 	 * cloned output. The relevant properties (scaling mode, TV
1640f16cd2aSVille Syrjälä 	 * margins) are per-connector so we'd have to make sure each
1650f16cd2aSVille Syrjälä 	 * output sets them up identically. Seems like a very niche use
1660f16cd2aSVille Syrjälä 	 * case so let's just reject cloning entirely when pfit is used.
1670f16cd2aSVille Syrjälä 	 */
1680f16cd2aSVille Syrjälä 	if (crtc_state->uapi.encoder_mask &&
1690f16cd2aSVille Syrjälä 	    !is_power_of_2(crtc_state->uapi.encoder_mask)) {
1700f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
1710f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] no pfit when cloning\n",
1720f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name);
1730f16cd2aSVille Syrjälä 		return -EINVAL;
1740f16cd2aSVille Syrjälä 	}
1750f16cd2aSVille Syrjälä 
1760f16cd2aSVille Syrjälä 	return 0;
1770f16cd2aSVille Syrjälä }
1780f16cd2aSVille Syrjälä 
1790f16cd2aSVille Syrjälä /* adjusted_mode has been preset to be the panel's fixed mode */
pch_panel_fitting(struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)1800f16cd2aSVille Syrjälä static int pch_panel_fitting(struct intel_crtc_state *crtc_state,
1810f16cd2aSVille Syrjälä 			     const struct drm_connector_state *conn_state)
1820f16cd2aSVille Syrjälä {
1830f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
1840f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
1850f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
1860f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
1870f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
1880f16cd2aSVille Syrjälä 	int ret, x, y, width, height;
1890f16cd2aSVille Syrjälä 
1900f16cd2aSVille Syrjälä 	/* Native modes don't need fitting */
1910f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
1920f16cd2aSVille Syrjälä 	    adjusted_mode->crtc_vdisplay == pipe_src_h &&
1930f16cd2aSVille Syrjälä 	    crtc_state->output_format != INTEL_OUTPUT_FORMAT_YCBCR420)
1940f16cd2aSVille Syrjälä 		return 0;
1950f16cd2aSVille Syrjälä 
1960f16cd2aSVille Syrjälä 	switch (conn_state->scaling_mode) {
1970f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_CENTER:
1980f16cd2aSVille Syrjälä 		width = pipe_src_w;
1990f16cd2aSVille Syrjälä 		height = pipe_src_h;
2000f16cd2aSVille Syrjälä 		x = (adjusted_mode->crtc_hdisplay - width + 1)/2;
2010f16cd2aSVille Syrjälä 		y = (adjusted_mode->crtc_vdisplay - height + 1)/2;
2020f16cd2aSVille Syrjälä 		break;
2030f16cd2aSVille Syrjälä 
2040f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_ASPECT:
2050f16cd2aSVille Syrjälä 		/* Scale but preserve the aspect ratio */
2060f16cd2aSVille Syrjälä 		{
2070f16cd2aSVille Syrjälä 			u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
2080f16cd2aSVille Syrjälä 			u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
2090f16cd2aSVille Syrjälä 
2100f16cd2aSVille Syrjälä 			if (scaled_width > scaled_height) { /* pillar */
2110f16cd2aSVille Syrjälä 				width = scaled_height / pipe_src_h;
2120f16cd2aSVille Syrjälä 				if (width & 1)
2130f16cd2aSVille Syrjälä 					width++;
2140f16cd2aSVille Syrjälä 				x = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
2150f16cd2aSVille Syrjälä 				y = 0;
2160f16cd2aSVille Syrjälä 				height = adjusted_mode->crtc_vdisplay;
2170f16cd2aSVille Syrjälä 			} else if (scaled_width < scaled_height) { /* letter */
2180f16cd2aSVille Syrjälä 				height = scaled_width / pipe_src_w;
2190f16cd2aSVille Syrjälä 				if (height & 1)
2200f16cd2aSVille Syrjälä 					height++;
2210f16cd2aSVille Syrjälä 				y = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
2220f16cd2aSVille Syrjälä 				x = 0;
2230f16cd2aSVille Syrjälä 				width = adjusted_mode->crtc_hdisplay;
2240f16cd2aSVille Syrjälä 			} else {
2250f16cd2aSVille Syrjälä 				x = y = 0;
2260f16cd2aSVille Syrjälä 				width = adjusted_mode->crtc_hdisplay;
2270f16cd2aSVille Syrjälä 				height = adjusted_mode->crtc_vdisplay;
2280f16cd2aSVille Syrjälä 			}
2290f16cd2aSVille Syrjälä 		}
2300f16cd2aSVille Syrjälä 		break;
2310f16cd2aSVille Syrjälä 
2320f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_NONE:
2330f16cd2aSVille Syrjälä 		WARN_ON(adjusted_mode->crtc_hdisplay != pipe_src_w);
2340f16cd2aSVille Syrjälä 		WARN_ON(adjusted_mode->crtc_vdisplay != pipe_src_h);
2350f16cd2aSVille Syrjälä 		fallthrough;
2360f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_FULLSCREEN:
2370f16cd2aSVille Syrjälä 		x = y = 0;
2380f16cd2aSVille Syrjälä 		width = adjusted_mode->crtc_hdisplay;
2390f16cd2aSVille Syrjälä 		height = adjusted_mode->crtc_vdisplay;
2400f16cd2aSVille Syrjälä 		break;
2410f16cd2aSVille Syrjälä 
2420f16cd2aSVille Syrjälä 	default:
2430f16cd2aSVille Syrjälä 		MISSING_CASE(conn_state->scaling_mode);
2440f16cd2aSVille Syrjälä 		return -EINVAL;
2450f16cd2aSVille Syrjälä 	}
2460f16cd2aSVille Syrjälä 
2470f16cd2aSVille Syrjälä 	drm_rect_init(&crtc_state->pch_pfit.dst,
2480f16cd2aSVille Syrjälä 		      x, y, width, height);
2490f16cd2aSVille Syrjälä 	crtc_state->pch_pfit.enabled = true;
2500f16cd2aSVille Syrjälä 
2510f16cd2aSVille Syrjälä 	/*
2520f16cd2aSVille Syrjälä 	 * SKL+ have unified scalers for pipes/planes so the
2530f16cd2aSVille Syrjälä 	 * checks are done in a single place for all scalers.
2540f16cd2aSVille Syrjälä 	 */
2550f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) >= 9)
2560f16cd2aSVille Syrjälä 		return 0;
2570f16cd2aSVille Syrjälä 
2580f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_dst_window(crtc_state);
2590f16cd2aSVille Syrjälä 	if (ret)
2600f16cd2aSVille Syrjälä 		return ret;
2610f16cd2aSVille Syrjälä 
2620f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_src_size(crtc_state);
2630f16cd2aSVille Syrjälä 	if (ret)
2640f16cd2aSVille Syrjälä 		return ret;
2650f16cd2aSVille Syrjälä 
2660f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_scaling(crtc_state);
2670f16cd2aSVille Syrjälä 	if (ret)
2680f16cd2aSVille Syrjälä 		return ret;
2690f16cd2aSVille Syrjälä 
2700f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_timings(crtc_state);
2710f16cd2aSVille Syrjälä 	if (ret)
2720f16cd2aSVille Syrjälä 		return ret;
2730f16cd2aSVille Syrjälä 
2740f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_cloning(crtc_state);
2750f16cd2aSVille Syrjälä 	if (ret)
2760f16cd2aSVille Syrjälä 		return ret;
2770f16cd2aSVille Syrjälä 
2780f16cd2aSVille Syrjälä 	return 0;
2790f16cd2aSVille Syrjälä }
2800f16cd2aSVille Syrjälä 
2810f16cd2aSVille Syrjälä static void
centre_horizontally(struct drm_display_mode * adjusted_mode,int width)2820f16cd2aSVille Syrjälä centre_horizontally(struct drm_display_mode *adjusted_mode,
2830f16cd2aSVille Syrjälä 		    int width)
2840f16cd2aSVille Syrjälä {
2850f16cd2aSVille Syrjälä 	u32 border, sync_pos, blank_width, sync_width;
2860f16cd2aSVille Syrjälä 
2870f16cd2aSVille Syrjälä 	/* keep the hsync and hblank widths constant */
2880f16cd2aSVille Syrjälä 	sync_width = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
2890f16cd2aSVille Syrjälä 	blank_width = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
2900f16cd2aSVille Syrjälä 	sync_pos = (blank_width - sync_width + 1) / 2;
2910f16cd2aSVille Syrjälä 
2920f16cd2aSVille Syrjälä 	border = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
2930f16cd2aSVille Syrjälä 	border += border & 1; /* make the border even */
2940f16cd2aSVille Syrjälä 
2950f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hdisplay = width;
2960f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hblank_start = width + border;
2970f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_hblank_start + blank_width;
2980f16cd2aSVille Syrjälä 
2990f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hblank_start + sync_pos;
3000f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + sync_width;
3010f16cd2aSVille Syrjälä }
3020f16cd2aSVille Syrjälä 
3030f16cd2aSVille Syrjälä static void
centre_vertically(struct drm_display_mode * adjusted_mode,int height)3040f16cd2aSVille Syrjälä centre_vertically(struct drm_display_mode *adjusted_mode,
3050f16cd2aSVille Syrjälä 		  int height)
3060f16cd2aSVille Syrjälä {
3070f16cd2aSVille Syrjälä 	u32 border, sync_pos, blank_width, sync_width;
3080f16cd2aSVille Syrjälä 
3090f16cd2aSVille Syrjälä 	/* keep the vsync and vblank widths constant */
3100f16cd2aSVille Syrjälä 	sync_width = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
3110f16cd2aSVille Syrjälä 	blank_width = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vblank_start;
3120f16cd2aSVille Syrjälä 	sync_pos = (blank_width - sync_width + 1) / 2;
3130f16cd2aSVille Syrjälä 
3140f16cd2aSVille Syrjälä 	border = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
3150f16cd2aSVille Syrjälä 
3160f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vdisplay = height;
3170f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vblank_start = height + border;
3180f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vblank_start + blank_width;
3190f16cd2aSVille Syrjälä 
3200f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vblank_start + sync_pos;
3210f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + sync_width;
3220f16cd2aSVille Syrjälä }
3230f16cd2aSVille Syrjälä 
panel_fitter_scaling(u32 source,u32 target)3240f16cd2aSVille Syrjälä static u32 panel_fitter_scaling(u32 source, u32 target)
3250f16cd2aSVille Syrjälä {
3260f16cd2aSVille Syrjälä 	/*
3270f16cd2aSVille Syrjälä 	 * Floating point operation is not supported. So the FACTOR
3280f16cd2aSVille Syrjälä 	 * is defined, which can avoid the floating point computation
3290f16cd2aSVille Syrjälä 	 * when calculating the panel ratio.
3300f16cd2aSVille Syrjälä 	 */
3310f16cd2aSVille Syrjälä #define ACCURACY 12
3320f16cd2aSVille Syrjälä #define FACTOR (1 << ACCURACY)
3330f16cd2aSVille Syrjälä 	u32 ratio = source * FACTOR / target;
3340f16cd2aSVille Syrjälä 	return (FACTOR * ratio + FACTOR/2) / FACTOR;
3350f16cd2aSVille Syrjälä }
3360f16cd2aSVille Syrjälä 
i965_scale_aspect(struct intel_crtc_state * crtc_state,u32 * pfit_control)3370f16cd2aSVille Syrjälä static void i965_scale_aspect(struct intel_crtc_state *crtc_state,
3380f16cd2aSVille Syrjälä 			      u32 *pfit_control)
3390f16cd2aSVille Syrjälä {
3400f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
3410f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
3420f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
3430f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
3440f16cd2aSVille Syrjälä 	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
3450f16cd2aSVille Syrjälä 	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
3460f16cd2aSVille Syrjälä 
3470f16cd2aSVille Syrjälä 	/* 965+ is easy, it does everything in hw */
3480f16cd2aSVille Syrjälä 	if (scaled_width > scaled_height)
3490f16cd2aSVille Syrjälä 		*pfit_control |= PFIT_ENABLE |
3500f16cd2aSVille Syrjälä 			PFIT_SCALING_PILLAR;
3510f16cd2aSVille Syrjälä 	else if (scaled_width < scaled_height)
3520f16cd2aSVille Syrjälä 		*pfit_control |= PFIT_ENABLE |
3530f16cd2aSVille Syrjälä 			PFIT_SCALING_LETTER;
3540f16cd2aSVille Syrjälä 	else if (adjusted_mode->crtc_hdisplay != pipe_src_w)
3550f16cd2aSVille Syrjälä 		*pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
3560f16cd2aSVille Syrjälä }
3570f16cd2aSVille Syrjälä 
i9xx_scale_aspect(struct intel_crtc_state * crtc_state,u32 * pfit_control,u32 * pfit_pgm_ratios,u32 * border)3580f16cd2aSVille Syrjälä static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state,
3590f16cd2aSVille Syrjälä 			      u32 *pfit_control, u32 *pfit_pgm_ratios,
3600f16cd2aSVille Syrjälä 			      u32 *border)
3610f16cd2aSVille Syrjälä {
3620f16cd2aSVille Syrjälä 	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
3630f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
3640f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
3650f16cd2aSVille Syrjälä 	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
3660f16cd2aSVille Syrjälä 	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
3670f16cd2aSVille Syrjälä 	u32 bits;
3680f16cd2aSVille Syrjälä 
3690f16cd2aSVille Syrjälä 	/*
3700f16cd2aSVille Syrjälä 	 * For earlier chips we have to calculate the scaling
3710f16cd2aSVille Syrjälä 	 * ratio by hand and program it into the
3720f16cd2aSVille Syrjälä 	 * PFIT_PGM_RATIO register
3730f16cd2aSVille Syrjälä 	 */
3740f16cd2aSVille Syrjälä 	if (scaled_width > scaled_height) { /* pillar */
3750f16cd2aSVille Syrjälä 		centre_horizontally(adjusted_mode,
3760f16cd2aSVille Syrjälä 				    scaled_height / pipe_src_h);
3770f16cd2aSVille Syrjälä 
3780f16cd2aSVille Syrjälä 		*border = LVDS_BORDER_ENABLE;
3790f16cd2aSVille Syrjälä 		if (pipe_src_h != adjusted_mode->crtc_vdisplay) {
3800f16cd2aSVille Syrjälä 			bits = panel_fitter_scaling(pipe_src_h,
3810f16cd2aSVille Syrjälä 						    adjusted_mode->crtc_vdisplay);
3820f16cd2aSVille Syrjälä 
3830f16cd2aSVille Syrjälä 			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
3840f16cd2aSVille Syrjälä 					     PFIT_VERT_SCALE(bits));
3850f16cd2aSVille Syrjälä 			*pfit_control |= (PFIT_ENABLE |
3860f16cd2aSVille Syrjälä 					  PFIT_VERT_INTERP_BILINEAR |
3870f16cd2aSVille Syrjälä 					  PFIT_HORIZ_INTERP_BILINEAR);
3880f16cd2aSVille Syrjälä 		}
3890f16cd2aSVille Syrjälä 	} else if (scaled_width < scaled_height) { /* letter */
3900f16cd2aSVille Syrjälä 		centre_vertically(adjusted_mode,
3910f16cd2aSVille Syrjälä 				  scaled_width / pipe_src_w);
3920f16cd2aSVille Syrjälä 
3930f16cd2aSVille Syrjälä 		*border = LVDS_BORDER_ENABLE;
3940f16cd2aSVille Syrjälä 		if (pipe_src_w != adjusted_mode->crtc_hdisplay) {
3950f16cd2aSVille Syrjälä 			bits = panel_fitter_scaling(pipe_src_w,
3960f16cd2aSVille Syrjälä 						    adjusted_mode->crtc_hdisplay);
3970f16cd2aSVille Syrjälä 
3980f16cd2aSVille Syrjälä 			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
3990f16cd2aSVille Syrjälä 					     PFIT_VERT_SCALE(bits));
4000f16cd2aSVille Syrjälä 			*pfit_control |= (PFIT_ENABLE |
4010f16cd2aSVille Syrjälä 					  PFIT_VERT_INTERP_BILINEAR |
4020f16cd2aSVille Syrjälä 					  PFIT_HORIZ_INTERP_BILINEAR);
4030f16cd2aSVille Syrjälä 		}
4040f16cd2aSVille Syrjälä 	} else {
4050f16cd2aSVille Syrjälä 		/* Aspects match, Let hw scale both directions */
4060f16cd2aSVille Syrjälä 		*pfit_control |= (PFIT_ENABLE |
4070f16cd2aSVille Syrjälä 				  PFIT_VERT_AUTO_SCALE |
4080f16cd2aSVille Syrjälä 				  PFIT_HORIZ_AUTO_SCALE |
4090f16cd2aSVille Syrjälä 				  PFIT_VERT_INTERP_BILINEAR |
4100f16cd2aSVille Syrjälä 				  PFIT_HORIZ_INTERP_BILINEAR);
4110f16cd2aSVille Syrjälä 	}
4120f16cd2aSVille Syrjälä }
4130f16cd2aSVille Syrjälä 
intel_gmch_pfit_check_timings(const struct intel_crtc_state * crtc_state)4140f16cd2aSVille Syrjälä static int intel_gmch_pfit_check_timings(const struct intel_crtc_state *crtc_state)
4150f16cd2aSVille Syrjälä {
4160f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
4170f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
4180f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
4190f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
4200f16cd2aSVille Syrjälä 	int min;
4210f16cd2aSVille Syrjälä 
4220f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) >= 4)
4230f16cd2aSVille Syrjälä 		min = 3;
4240f16cd2aSVille Syrjälä 	else
4250f16cd2aSVille Syrjälä 		min = 2;
4260f16cd2aSVille Syrjälä 
4270f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay < min) {
4280f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
4290f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] horizontal active (%d) below minimum (%d) for pfit\n",
4300f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
4310f16cd2aSVille Syrjälä 			    adjusted_mode->crtc_hdisplay, min);
4320f16cd2aSVille Syrjälä 		return -EINVAL;
4330f16cd2aSVille Syrjälä 	}
4340f16cd2aSVille Syrjälä 
4350f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_vdisplay < min) {
4360f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
4370f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] vertical active (%d) below minimum (%d) for pfit\n",
4380f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
4390f16cd2aSVille Syrjälä 			    adjusted_mode->crtc_vdisplay, min);
4400f16cd2aSVille Syrjälä 		return -EINVAL;
4410f16cd2aSVille Syrjälä 	}
4420f16cd2aSVille Syrjälä 
4430f16cd2aSVille Syrjälä 	return 0;
4440f16cd2aSVille Syrjälä }
4450f16cd2aSVille Syrjälä 
gmch_panel_fitting(struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)4460f16cd2aSVille Syrjälä static int gmch_panel_fitting(struct intel_crtc_state *crtc_state,
4470f16cd2aSVille Syrjälä 			      const struct drm_connector_state *conn_state)
4480f16cd2aSVille Syrjälä {
4490f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
4500f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
4510f16cd2aSVille Syrjälä 	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
4520f16cd2aSVille Syrjälä 	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
4530f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
4540f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
4550f16cd2aSVille Syrjälä 
4560f16cd2aSVille Syrjälä 	/* Native modes don't need fitting */
4570f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
4580f16cd2aSVille Syrjälä 	    adjusted_mode->crtc_vdisplay == pipe_src_h)
4590f16cd2aSVille Syrjälä 		goto out;
4600f16cd2aSVille Syrjälä 
4610f16cd2aSVille Syrjälä 	/*
4620f16cd2aSVille Syrjälä 	 * TODO: implement downscaling for i965+. Need to account
4630f16cd2aSVille Syrjälä 	 * for downscaling in intel_crtc_compute_pixel_rate().
4640f16cd2aSVille Syrjälä 	 */
4650f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay < pipe_src_w) {
4660f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
4670f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit horizontal downscaling (%d->%d) not supported\n",
4680f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
4690f16cd2aSVille Syrjälä 			    pipe_src_w, adjusted_mode->crtc_hdisplay);
4700f16cd2aSVille Syrjälä 		return -EINVAL;
4710f16cd2aSVille Syrjälä 	}
4720f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_vdisplay < pipe_src_h) {
4730f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
4740f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit vertical downscaling (%d->%d) not supported\n",
4750f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
4760f16cd2aSVille Syrjälä 			    pipe_src_h, adjusted_mode->crtc_vdisplay);
4770f16cd2aSVille Syrjälä 		return -EINVAL;
4780f16cd2aSVille Syrjälä 	}
4790f16cd2aSVille Syrjälä 
4800f16cd2aSVille Syrjälä 	switch (conn_state->scaling_mode) {
4810f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_CENTER:
4820f16cd2aSVille Syrjälä 		/*
4830f16cd2aSVille Syrjälä 		 * For centered modes, we have to calculate border widths &
4840f16cd2aSVille Syrjälä 		 * heights and modify the values programmed into the CRTC.
4850f16cd2aSVille Syrjälä 		 */
4860f16cd2aSVille Syrjälä 		centre_horizontally(adjusted_mode, pipe_src_w);
4870f16cd2aSVille Syrjälä 		centre_vertically(adjusted_mode, pipe_src_h);
4880f16cd2aSVille Syrjälä 		border = LVDS_BORDER_ENABLE;
4890f16cd2aSVille Syrjälä 		break;
4900f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_ASPECT:
4910f16cd2aSVille Syrjälä 		/* Scale but preserve the aspect ratio */
4920f16cd2aSVille Syrjälä 		if (DISPLAY_VER(display) >= 4)
4930f16cd2aSVille Syrjälä 			i965_scale_aspect(crtc_state, &pfit_control);
4940f16cd2aSVille Syrjälä 		else
4950f16cd2aSVille Syrjälä 			i9xx_scale_aspect(crtc_state, &pfit_control,
4960f16cd2aSVille Syrjälä 					  &pfit_pgm_ratios, &border);
4970f16cd2aSVille Syrjälä 		break;
4980f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_FULLSCREEN:
4990f16cd2aSVille Syrjälä 		/*
5000f16cd2aSVille Syrjälä 		 * Full scaling, even if it changes the aspect ratio.
5010f16cd2aSVille Syrjälä 		 * Fortunately this is all done for us in hw.
5020f16cd2aSVille Syrjälä 		 */
5030f16cd2aSVille Syrjälä 		if (pipe_src_h != adjusted_mode->crtc_vdisplay ||
5040f16cd2aSVille Syrjälä 		    pipe_src_w != adjusted_mode->crtc_hdisplay) {
5050f16cd2aSVille Syrjälä 			pfit_control |= PFIT_ENABLE;
5060f16cd2aSVille Syrjälä 			if (DISPLAY_VER(display) >= 4)
5070f16cd2aSVille Syrjälä 				pfit_control |= PFIT_SCALING_AUTO;
5080f16cd2aSVille Syrjälä 			else
5090f16cd2aSVille Syrjälä 				pfit_control |= (PFIT_VERT_AUTO_SCALE |
5100f16cd2aSVille Syrjälä 						 PFIT_VERT_INTERP_BILINEAR |
5110f16cd2aSVille Syrjälä 						 PFIT_HORIZ_AUTO_SCALE |
5120f16cd2aSVille Syrjälä 						 PFIT_HORIZ_INTERP_BILINEAR);
5130f16cd2aSVille Syrjälä 		}
5140f16cd2aSVille Syrjälä 		break;
5150f16cd2aSVille Syrjälä 	default:
5160f16cd2aSVille Syrjälä 		MISSING_CASE(conn_state->scaling_mode);
5170f16cd2aSVille Syrjälä 		return -EINVAL;
5180f16cd2aSVille Syrjälä 	}
5190f16cd2aSVille Syrjälä 
5200f16cd2aSVille Syrjälä 	/* 965+ wants fuzzy fitting */
5210f16cd2aSVille Syrjälä 	/* FIXME: handle multiple panels by failing gracefully */
5220f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) >= 4)
5230f16cd2aSVille Syrjälä 		pfit_control |= PFIT_PIPE(crtc->pipe) | PFIT_FILTER_FUZZY;
5240f16cd2aSVille Syrjälä 
5250f16cd2aSVille Syrjälä out:
5260f16cd2aSVille Syrjälä 	if ((pfit_control & PFIT_ENABLE) == 0) {
5270f16cd2aSVille Syrjälä 		pfit_control = 0;
5280f16cd2aSVille Syrjälä 		pfit_pgm_ratios = 0;
5290f16cd2aSVille Syrjälä 	}
5300f16cd2aSVille Syrjälä 
5310f16cd2aSVille Syrjälä 	/* Make sure pre-965 set dither correctly for 18bpp panels. */
5320f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) < 4 && crtc_state->pipe_bpp == 18)
5330f16cd2aSVille Syrjälä 		pfit_control |= PFIT_PANEL_8TO6_DITHER_ENABLE;
5340f16cd2aSVille Syrjälä 
5350f16cd2aSVille Syrjälä 	crtc_state->gmch_pfit.control = pfit_control;
5360f16cd2aSVille Syrjälä 	crtc_state->gmch_pfit.pgm_ratios = pfit_pgm_ratios;
5370f16cd2aSVille Syrjälä 	crtc_state->gmch_pfit.lvds_border_bits = border;
5380f16cd2aSVille Syrjälä 
5390f16cd2aSVille Syrjälä 	if ((pfit_control & PFIT_ENABLE) == 0)
5400f16cd2aSVille Syrjälä 		return 0;
5410f16cd2aSVille Syrjälä 
5420f16cd2aSVille Syrjälä 	return intel_gmch_pfit_check_timings(crtc_state);
5430f16cd2aSVille Syrjälä }
5440f16cd2aSVille Syrjälä 
intel_panel_fitting(struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)5450f16cd2aSVille Syrjälä int intel_panel_fitting(struct intel_crtc_state *crtc_state,
5460f16cd2aSVille Syrjälä 			const struct drm_connector_state *conn_state)
5470f16cd2aSVille Syrjälä {
5480f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
5490f16cd2aSVille Syrjälä 
5500f16cd2aSVille Syrjälä 	if (HAS_GMCH(display))
5510f16cd2aSVille Syrjälä 		return gmch_panel_fitting(crtc_state, conn_state);
5520f16cd2aSVille Syrjälä 	else
5530f16cd2aSVille Syrjälä 		return pch_panel_fitting(crtc_state, conn_state);
5540f16cd2aSVille Syrjälä }
555