xref: /linux/drivers/gpu/drm/i915/display/intel_pfit.c (revision 0f16cd2aad7e3d05b846773fb2019ae2b2777695)
1*0f16cd2aSVille Syrjälä // SPDX-License-Identifier: MIT
2*0f16cd2aSVille Syrjälä /*
3*0f16cd2aSVille Syrjälä  * Copyright © 2024 Intel Corporation
4*0f16cd2aSVille Syrjälä  */
5*0f16cd2aSVille Syrjälä 
6*0f16cd2aSVille Syrjälä #include "i915_drv.h"
7*0f16cd2aSVille Syrjälä #include "i915_reg.h"
8*0f16cd2aSVille Syrjälä #include "intel_display_core.h"
9*0f16cd2aSVille Syrjälä #include "intel_display_driver.h"
10*0f16cd2aSVille Syrjälä #include "intel_display_types.h"
11*0f16cd2aSVille Syrjälä #include "intel_lvds_regs.h"
12*0f16cd2aSVille Syrjälä #include "intel_pfit.h"
13*0f16cd2aSVille Syrjälä 
14*0f16cd2aSVille Syrjälä static int intel_pch_pfit_check_dst_window(const struct intel_crtc_state *crtc_state)
15*0f16cd2aSVille Syrjälä {
16*0f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
17*0f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
18*0f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
19*0f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
20*0f16cd2aSVille Syrjälä 	const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
21*0f16cd2aSVille Syrjälä 	int width = drm_rect_width(dst);
22*0f16cd2aSVille Syrjälä 	int height = drm_rect_height(dst);
23*0f16cd2aSVille Syrjälä 	int x = dst->x1;
24*0f16cd2aSVille Syrjälä 	int y = dst->y1;
25*0f16cd2aSVille Syrjälä 
26*0f16cd2aSVille Syrjälä 	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE &&
27*0f16cd2aSVille Syrjälä 	    (y & 1 || height & 1)) {
28*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
29*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") misaligned for interlaced output\n",
30*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
31*0f16cd2aSVille Syrjälä 		return -EINVAL;
32*0f16cd2aSVille Syrjälä 	}
33*0f16cd2aSVille Syrjälä 
34*0f16cd2aSVille Syrjälä 	/*
35*0f16cd2aSVille Syrjälä 	 * "Restriction : When pipe scaling is enabled, the scaled
36*0f16cd2aSVille Syrjälä 	 *  output must equal the pipe active area, so Pipe active
37*0f16cd2aSVille Syrjälä 	 *  size = (2 * PF window position) + PF window size."
38*0f16cd2aSVille Syrjälä 	 *
39*0f16cd2aSVille Syrjälä 	 * The vertical direction seems more forgiving than the
40*0f16cd2aSVille Syrjälä 	 * horizontal direction, but still has some issues so
41*0f16cd2aSVille Syrjälä 	 * let's follow the same hard rule for both.
42*0f16cd2aSVille Syrjälä 	 */
43*0f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay != 2 * x + width ||
44*0f16cd2aSVille Syrjälä 	    adjusted_mode->crtc_vdisplay != 2 * y + height) {
45*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
46*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") not centered\n",
47*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
48*0f16cd2aSVille Syrjälä 		return -EINVAL;
49*0f16cd2aSVille Syrjälä 	}
50*0f16cd2aSVille Syrjälä 
51*0f16cd2aSVille Syrjälä 	/*
52*0f16cd2aSVille Syrjälä 	 * "Restriction : The X position must not be programmed
53*0f16cd2aSVille Syrjälä 	 *  to be 1 (28:16=0 0000 0000 0001b)."
54*0f16cd2aSVille Syrjälä 	 */
55*0f16cd2aSVille Syrjälä 	if (x == 1) {
56*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
57*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") badly positioned\n",
58*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
59*0f16cd2aSVille Syrjälä 		return -EINVAL;
60*0f16cd2aSVille Syrjälä 	}
61*0f16cd2aSVille Syrjälä 
62*0f16cd2aSVille Syrjälä 	return 0;
63*0f16cd2aSVille Syrjälä }
64*0f16cd2aSVille Syrjälä 
65*0f16cd2aSVille Syrjälä static int intel_pch_pfit_check_src_size(const struct intel_crtc_state *crtc_state)
66*0f16cd2aSVille Syrjälä {
67*0f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
68*0f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
69*0f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
70*0f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
71*0f16cd2aSVille Syrjälä 	int max_src_w, max_src_h;
72*0f16cd2aSVille Syrjälä 
73*0f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) >= 8) {
74*0f16cd2aSVille Syrjälä 		max_src_w = 4096;
75*0f16cd2aSVille Syrjälä 		max_src_h = 4096;
76*0f16cd2aSVille Syrjälä 	} else if (DISPLAY_VER(display) >= 7) {
77*0f16cd2aSVille Syrjälä 		/*
78*0f16cd2aSVille Syrjälä 		 * PF0 7x5 capable
79*0f16cd2aSVille Syrjälä 		 * PF1 3x3 capable (could be switched to 7x5
80*0f16cd2aSVille Syrjälä 		 *                  mode on HSW when PF2 unused)
81*0f16cd2aSVille Syrjälä 		 * PF2 3x3 capable
82*0f16cd2aSVille Syrjälä 		 *
83*0f16cd2aSVille Syrjälä 		 * This assumes we use a 1:1 mapping between pipe and PF.
84*0f16cd2aSVille Syrjälä 		 */
85*0f16cd2aSVille Syrjälä 		max_src_w = crtc->pipe == PIPE_A ? 4096 : 2048;
86*0f16cd2aSVille Syrjälä 		max_src_h = 4096;
87*0f16cd2aSVille Syrjälä 	} else {
88*0f16cd2aSVille Syrjälä 		max_src_w = 4096;
89*0f16cd2aSVille Syrjälä 		max_src_h = 4096;
90*0f16cd2aSVille Syrjälä 	}
91*0f16cd2aSVille Syrjälä 
92*0f16cd2aSVille Syrjälä 	if (pipe_src_w > max_src_w || pipe_src_h > max_src_h) {
93*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
94*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] source size (%dx%d) exceeds pfit max (%dx%d)\n",
95*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
96*0f16cd2aSVille Syrjälä 			    pipe_src_w, pipe_src_h, max_src_w, max_src_h);
97*0f16cd2aSVille Syrjälä 		return -EINVAL;
98*0f16cd2aSVille Syrjälä 	}
99*0f16cd2aSVille Syrjälä 
100*0f16cd2aSVille Syrjälä 	return 0;
101*0f16cd2aSVille Syrjälä }
102*0f16cd2aSVille Syrjälä 
103*0f16cd2aSVille Syrjälä static int intel_pch_pfit_check_scaling(const struct intel_crtc_state *crtc_state)
104*0f16cd2aSVille Syrjälä {
105*0f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
106*0f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
107*0f16cd2aSVille Syrjälä 	const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
108*0f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
109*0f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
110*0f16cd2aSVille Syrjälä 	int hscale, vscale, max_scale = 0x12000; /* 1.125 */
111*0f16cd2aSVille Syrjälä 	struct drm_rect src;
112*0f16cd2aSVille Syrjälä 
113*0f16cd2aSVille Syrjälä 	drm_rect_init(&src, 0, 0, pipe_src_w << 16, pipe_src_h << 16);
114*0f16cd2aSVille Syrjälä 
115*0f16cd2aSVille Syrjälä 	hscale = drm_rect_calc_hscale(&src, dst, 0, max_scale);
116*0f16cd2aSVille Syrjälä 	if (hscale < 0) {
117*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
118*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit horizontal downscaling (%d->%d) exceeds max (0x%x)\n",
119*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
120*0f16cd2aSVille Syrjälä 			    pipe_src_w, drm_rect_width(dst),
121*0f16cd2aSVille Syrjälä 			    max_scale);
122*0f16cd2aSVille Syrjälä 		return hscale;
123*0f16cd2aSVille Syrjälä 	}
124*0f16cd2aSVille Syrjälä 
125*0f16cd2aSVille Syrjälä 	vscale = drm_rect_calc_vscale(&src, dst, 0, max_scale);
126*0f16cd2aSVille Syrjälä 	if (vscale < 0) {
127*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
128*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit vertical downscaling (%d->%d) exceeds max (0x%x)\n",
129*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
130*0f16cd2aSVille Syrjälä 			    pipe_src_h, drm_rect_height(dst),
131*0f16cd2aSVille Syrjälä 			    max_scale);
132*0f16cd2aSVille Syrjälä 		return vscale;
133*0f16cd2aSVille Syrjälä 	}
134*0f16cd2aSVille Syrjälä 
135*0f16cd2aSVille Syrjälä 	return 0;
136*0f16cd2aSVille Syrjälä }
137*0f16cd2aSVille Syrjälä 
138*0f16cd2aSVille Syrjälä static int intel_pch_pfit_check_timings(const struct intel_crtc_state *crtc_state)
139*0f16cd2aSVille Syrjälä {
140*0f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
141*0f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
142*0f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
143*0f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
144*0f16cd2aSVille Syrjälä 
145*0f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_vdisplay < 7) {
146*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
147*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] vertical active (%d) below minimum (%d) for pfit\n",
148*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
149*0f16cd2aSVille Syrjälä 			    adjusted_mode->crtc_vdisplay, 7);
150*0f16cd2aSVille Syrjälä 		return -EINVAL;
151*0f16cd2aSVille Syrjälä 	}
152*0f16cd2aSVille Syrjälä 
153*0f16cd2aSVille Syrjälä 	return 0;
154*0f16cd2aSVille Syrjälä }
155*0f16cd2aSVille Syrjälä 
156*0f16cd2aSVille Syrjälä static int intel_pch_pfit_check_cloning(const struct intel_crtc_state *crtc_state)
157*0f16cd2aSVille Syrjälä {
158*0f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
159*0f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
160*0f16cd2aSVille Syrjälä 
161*0f16cd2aSVille Syrjälä 	/*
162*0f16cd2aSVille Syrjälä 	 * The panel fitter is in the pipe and thus would affect every
163*0f16cd2aSVille Syrjälä 	 * cloned output. The relevant properties (scaling mode, TV
164*0f16cd2aSVille Syrjälä 	 * margins) are per-connector so we'd have to make sure each
165*0f16cd2aSVille Syrjälä 	 * output sets them up identically. Seems like a very niche use
166*0f16cd2aSVille Syrjälä 	 * case so let's just reject cloning entirely when pfit is used.
167*0f16cd2aSVille Syrjälä 	 */
168*0f16cd2aSVille Syrjälä 	if (crtc_state->uapi.encoder_mask &&
169*0f16cd2aSVille Syrjälä 	    !is_power_of_2(crtc_state->uapi.encoder_mask)) {
170*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
171*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] no pfit when cloning\n",
172*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name);
173*0f16cd2aSVille Syrjälä 		return -EINVAL;
174*0f16cd2aSVille Syrjälä 	}
175*0f16cd2aSVille Syrjälä 
176*0f16cd2aSVille Syrjälä 	return 0;
177*0f16cd2aSVille Syrjälä }
178*0f16cd2aSVille Syrjälä 
179*0f16cd2aSVille Syrjälä /* adjusted_mode has been preset to be the panel's fixed mode */
180*0f16cd2aSVille Syrjälä static int pch_panel_fitting(struct intel_crtc_state *crtc_state,
181*0f16cd2aSVille Syrjälä 			     const struct drm_connector_state *conn_state)
182*0f16cd2aSVille Syrjälä {
183*0f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
184*0f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
185*0f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
186*0f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
187*0f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
188*0f16cd2aSVille Syrjälä 	int ret, x, y, width, height;
189*0f16cd2aSVille Syrjälä 
190*0f16cd2aSVille Syrjälä 	/* Native modes don't need fitting */
191*0f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
192*0f16cd2aSVille Syrjälä 	    adjusted_mode->crtc_vdisplay == pipe_src_h &&
193*0f16cd2aSVille Syrjälä 	    crtc_state->output_format != INTEL_OUTPUT_FORMAT_YCBCR420)
194*0f16cd2aSVille Syrjälä 		return 0;
195*0f16cd2aSVille Syrjälä 
196*0f16cd2aSVille Syrjälä 	switch (conn_state->scaling_mode) {
197*0f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_CENTER:
198*0f16cd2aSVille Syrjälä 		width = pipe_src_w;
199*0f16cd2aSVille Syrjälä 		height = pipe_src_h;
200*0f16cd2aSVille Syrjälä 		x = (adjusted_mode->crtc_hdisplay - width + 1)/2;
201*0f16cd2aSVille Syrjälä 		y = (adjusted_mode->crtc_vdisplay - height + 1)/2;
202*0f16cd2aSVille Syrjälä 		break;
203*0f16cd2aSVille Syrjälä 
204*0f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_ASPECT:
205*0f16cd2aSVille Syrjälä 		/* Scale but preserve the aspect ratio */
206*0f16cd2aSVille Syrjälä 		{
207*0f16cd2aSVille Syrjälä 			u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
208*0f16cd2aSVille Syrjälä 			u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
209*0f16cd2aSVille Syrjälä 
210*0f16cd2aSVille Syrjälä 			if (scaled_width > scaled_height) { /* pillar */
211*0f16cd2aSVille Syrjälä 				width = scaled_height / pipe_src_h;
212*0f16cd2aSVille Syrjälä 				if (width & 1)
213*0f16cd2aSVille Syrjälä 					width++;
214*0f16cd2aSVille Syrjälä 				x = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
215*0f16cd2aSVille Syrjälä 				y = 0;
216*0f16cd2aSVille Syrjälä 				height = adjusted_mode->crtc_vdisplay;
217*0f16cd2aSVille Syrjälä 			} else if (scaled_width < scaled_height) { /* letter */
218*0f16cd2aSVille Syrjälä 				height = scaled_width / pipe_src_w;
219*0f16cd2aSVille Syrjälä 				if (height & 1)
220*0f16cd2aSVille Syrjälä 					height++;
221*0f16cd2aSVille Syrjälä 				y = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
222*0f16cd2aSVille Syrjälä 				x = 0;
223*0f16cd2aSVille Syrjälä 				width = adjusted_mode->crtc_hdisplay;
224*0f16cd2aSVille Syrjälä 			} else {
225*0f16cd2aSVille Syrjälä 				x = y = 0;
226*0f16cd2aSVille Syrjälä 				width = adjusted_mode->crtc_hdisplay;
227*0f16cd2aSVille Syrjälä 				height = adjusted_mode->crtc_vdisplay;
228*0f16cd2aSVille Syrjälä 			}
229*0f16cd2aSVille Syrjälä 		}
230*0f16cd2aSVille Syrjälä 		break;
231*0f16cd2aSVille Syrjälä 
232*0f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_NONE:
233*0f16cd2aSVille Syrjälä 		WARN_ON(adjusted_mode->crtc_hdisplay != pipe_src_w);
234*0f16cd2aSVille Syrjälä 		WARN_ON(adjusted_mode->crtc_vdisplay != pipe_src_h);
235*0f16cd2aSVille Syrjälä 		fallthrough;
236*0f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_FULLSCREEN:
237*0f16cd2aSVille Syrjälä 		x = y = 0;
238*0f16cd2aSVille Syrjälä 		width = adjusted_mode->crtc_hdisplay;
239*0f16cd2aSVille Syrjälä 		height = adjusted_mode->crtc_vdisplay;
240*0f16cd2aSVille Syrjälä 		break;
241*0f16cd2aSVille Syrjälä 
242*0f16cd2aSVille Syrjälä 	default:
243*0f16cd2aSVille Syrjälä 		MISSING_CASE(conn_state->scaling_mode);
244*0f16cd2aSVille Syrjälä 		return -EINVAL;
245*0f16cd2aSVille Syrjälä 	}
246*0f16cd2aSVille Syrjälä 
247*0f16cd2aSVille Syrjälä 	drm_rect_init(&crtc_state->pch_pfit.dst,
248*0f16cd2aSVille Syrjälä 		      x, y, width, height);
249*0f16cd2aSVille Syrjälä 	crtc_state->pch_pfit.enabled = true;
250*0f16cd2aSVille Syrjälä 
251*0f16cd2aSVille Syrjälä 	/*
252*0f16cd2aSVille Syrjälä 	 * SKL+ have unified scalers for pipes/planes so the
253*0f16cd2aSVille Syrjälä 	 * checks are done in a single place for all scalers.
254*0f16cd2aSVille Syrjälä 	 */
255*0f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) >= 9)
256*0f16cd2aSVille Syrjälä 		return 0;
257*0f16cd2aSVille Syrjälä 
258*0f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_dst_window(crtc_state);
259*0f16cd2aSVille Syrjälä 	if (ret)
260*0f16cd2aSVille Syrjälä 		return ret;
261*0f16cd2aSVille Syrjälä 
262*0f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_src_size(crtc_state);
263*0f16cd2aSVille Syrjälä 	if (ret)
264*0f16cd2aSVille Syrjälä 		return ret;
265*0f16cd2aSVille Syrjälä 
266*0f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_scaling(crtc_state);
267*0f16cd2aSVille Syrjälä 	if (ret)
268*0f16cd2aSVille Syrjälä 		return ret;
269*0f16cd2aSVille Syrjälä 
270*0f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_timings(crtc_state);
271*0f16cd2aSVille Syrjälä 	if (ret)
272*0f16cd2aSVille Syrjälä 		return ret;
273*0f16cd2aSVille Syrjälä 
274*0f16cd2aSVille Syrjälä 	ret = intel_pch_pfit_check_cloning(crtc_state);
275*0f16cd2aSVille Syrjälä 	if (ret)
276*0f16cd2aSVille Syrjälä 		return ret;
277*0f16cd2aSVille Syrjälä 
278*0f16cd2aSVille Syrjälä 	return 0;
279*0f16cd2aSVille Syrjälä }
280*0f16cd2aSVille Syrjälä 
281*0f16cd2aSVille Syrjälä static void
282*0f16cd2aSVille Syrjälä centre_horizontally(struct drm_display_mode *adjusted_mode,
283*0f16cd2aSVille Syrjälä 		    int width)
284*0f16cd2aSVille Syrjälä {
285*0f16cd2aSVille Syrjälä 	u32 border, sync_pos, blank_width, sync_width;
286*0f16cd2aSVille Syrjälä 
287*0f16cd2aSVille Syrjälä 	/* keep the hsync and hblank widths constant */
288*0f16cd2aSVille Syrjälä 	sync_width = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
289*0f16cd2aSVille Syrjälä 	blank_width = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
290*0f16cd2aSVille Syrjälä 	sync_pos = (blank_width - sync_width + 1) / 2;
291*0f16cd2aSVille Syrjälä 
292*0f16cd2aSVille Syrjälä 	border = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
293*0f16cd2aSVille Syrjälä 	border += border & 1; /* make the border even */
294*0f16cd2aSVille Syrjälä 
295*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hdisplay = width;
296*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hblank_start = width + border;
297*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_hblank_start + blank_width;
298*0f16cd2aSVille Syrjälä 
299*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hblank_start + sync_pos;
300*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + sync_width;
301*0f16cd2aSVille Syrjälä }
302*0f16cd2aSVille Syrjälä 
303*0f16cd2aSVille Syrjälä static void
304*0f16cd2aSVille Syrjälä centre_vertically(struct drm_display_mode *adjusted_mode,
305*0f16cd2aSVille Syrjälä 		  int height)
306*0f16cd2aSVille Syrjälä {
307*0f16cd2aSVille Syrjälä 	u32 border, sync_pos, blank_width, sync_width;
308*0f16cd2aSVille Syrjälä 
309*0f16cd2aSVille Syrjälä 	/* keep the vsync and vblank widths constant */
310*0f16cd2aSVille Syrjälä 	sync_width = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
311*0f16cd2aSVille Syrjälä 	blank_width = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vblank_start;
312*0f16cd2aSVille Syrjälä 	sync_pos = (blank_width - sync_width + 1) / 2;
313*0f16cd2aSVille Syrjälä 
314*0f16cd2aSVille Syrjälä 	border = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
315*0f16cd2aSVille Syrjälä 
316*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vdisplay = height;
317*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vblank_start = height + border;
318*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vblank_start + blank_width;
319*0f16cd2aSVille Syrjälä 
320*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vblank_start + sync_pos;
321*0f16cd2aSVille Syrjälä 	adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + sync_width;
322*0f16cd2aSVille Syrjälä }
323*0f16cd2aSVille Syrjälä 
324*0f16cd2aSVille Syrjälä static u32 panel_fitter_scaling(u32 source, u32 target)
325*0f16cd2aSVille Syrjälä {
326*0f16cd2aSVille Syrjälä 	/*
327*0f16cd2aSVille Syrjälä 	 * Floating point operation is not supported. So the FACTOR
328*0f16cd2aSVille Syrjälä 	 * is defined, which can avoid the floating point computation
329*0f16cd2aSVille Syrjälä 	 * when calculating the panel ratio.
330*0f16cd2aSVille Syrjälä 	 */
331*0f16cd2aSVille Syrjälä #define ACCURACY 12
332*0f16cd2aSVille Syrjälä #define FACTOR (1 << ACCURACY)
333*0f16cd2aSVille Syrjälä 	u32 ratio = source * FACTOR / target;
334*0f16cd2aSVille Syrjälä 	return (FACTOR * ratio + FACTOR/2) / FACTOR;
335*0f16cd2aSVille Syrjälä }
336*0f16cd2aSVille Syrjälä 
337*0f16cd2aSVille Syrjälä static void i965_scale_aspect(struct intel_crtc_state *crtc_state,
338*0f16cd2aSVille Syrjälä 			      u32 *pfit_control)
339*0f16cd2aSVille Syrjälä {
340*0f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
341*0f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
342*0f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
343*0f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
344*0f16cd2aSVille Syrjälä 	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
345*0f16cd2aSVille Syrjälä 	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
346*0f16cd2aSVille Syrjälä 
347*0f16cd2aSVille Syrjälä 	/* 965+ is easy, it does everything in hw */
348*0f16cd2aSVille Syrjälä 	if (scaled_width > scaled_height)
349*0f16cd2aSVille Syrjälä 		*pfit_control |= PFIT_ENABLE |
350*0f16cd2aSVille Syrjälä 			PFIT_SCALING_PILLAR;
351*0f16cd2aSVille Syrjälä 	else if (scaled_width < scaled_height)
352*0f16cd2aSVille Syrjälä 		*pfit_control |= PFIT_ENABLE |
353*0f16cd2aSVille Syrjälä 			PFIT_SCALING_LETTER;
354*0f16cd2aSVille Syrjälä 	else if (adjusted_mode->crtc_hdisplay != pipe_src_w)
355*0f16cd2aSVille Syrjälä 		*pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
356*0f16cd2aSVille Syrjälä }
357*0f16cd2aSVille Syrjälä 
358*0f16cd2aSVille Syrjälä static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state,
359*0f16cd2aSVille Syrjälä 			      u32 *pfit_control, u32 *pfit_pgm_ratios,
360*0f16cd2aSVille Syrjälä 			      u32 *border)
361*0f16cd2aSVille Syrjälä {
362*0f16cd2aSVille Syrjälä 	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
363*0f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
364*0f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
365*0f16cd2aSVille Syrjälä 	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
366*0f16cd2aSVille Syrjälä 	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
367*0f16cd2aSVille Syrjälä 	u32 bits;
368*0f16cd2aSVille Syrjälä 
369*0f16cd2aSVille Syrjälä 	/*
370*0f16cd2aSVille Syrjälä 	 * For earlier chips we have to calculate the scaling
371*0f16cd2aSVille Syrjälä 	 * ratio by hand and program it into the
372*0f16cd2aSVille Syrjälä 	 * PFIT_PGM_RATIO register
373*0f16cd2aSVille Syrjälä 	 */
374*0f16cd2aSVille Syrjälä 	if (scaled_width > scaled_height) { /* pillar */
375*0f16cd2aSVille Syrjälä 		centre_horizontally(adjusted_mode,
376*0f16cd2aSVille Syrjälä 				    scaled_height / pipe_src_h);
377*0f16cd2aSVille Syrjälä 
378*0f16cd2aSVille Syrjälä 		*border = LVDS_BORDER_ENABLE;
379*0f16cd2aSVille Syrjälä 		if (pipe_src_h != adjusted_mode->crtc_vdisplay) {
380*0f16cd2aSVille Syrjälä 			bits = panel_fitter_scaling(pipe_src_h,
381*0f16cd2aSVille Syrjälä 						    adjusted_mode->crtc_vdisplay);
382*0f16cd2aSVille Syrjälä 
383*0f16cd2aSVille Syrjälä 			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
384*0f16cd2aSVille Syrjälä 					     PFIT_VERT_SCALE(bits));
385*0f16cd2aSVille Syrjälä 			*pfit_control |= (PFIT_ENABLE |
386*0f16cd2aSVille Syrjälä 					  PFIT_VERT_INTERP_BILINEAR |
387*0f16cd2aSVille Syrjälä 					  PFIT_HORIZ_INTERP_BILINEAR);
388*0f16cd2aSVille Syrjälä 		}
389*0f16cd2aSVille Syrjälä 	} else if (scaled_width < scaled_height) { /* letter */
390*0f16cd2aSVille Syrjälä 		centre_vertically(adjusted_mode,
391*0f16cd2aSVille Syrjälä 				  scaled_width / pipe_src_w);
392*0f16cd2aSVille Syrjälä 
393*0f16cd2aSVille Syrjälä 		*border = LVDS_BORDER_ENABLE;
394*0f16cd2aSVille Syrjälä 		if (pipe_src_w != adjusted_mode->crtc_hdisplay) {
395*0f16cd2aSVille Syrjälä 			bits = panel_fitter_scaling(pipe_src_w,
396*0f16cd2aSVille Syrjälä 						    adjusted_mode->crtc_hdisplay);
397*0f16cd2aSVille Syrjälä 
398*0f16cd2aSVille Syrjälä 			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
399*0f16cd2aSVille Syrjälä 					     PFIT_VERT_SCALE(bits));
400*0f16cd2aSVille Syrjälä 			*pfit_control |= (PFIT_ENABLE |
401*0f16cd2aSVille Syrjälä 					  PFIT_VERT_INTERP_BILINEAR |
402*0f16cd2aSVille Syrjälä 					  PFIT_HORIZ_INTERP_BILINEAR);
403*0f16cd2aSVille Syrjälä 		}
404*0f16cd2aSVille Syrjälä 	} else {
405*0f16cd2aSVille Syrjälä 		/* Aspects match, Let hw scale both directions */
406*0f16cd2aSVille Syrjälä 		*pfit_control |= (PFIT_ENABLE |
407*0f16cd2aSVille Syrjälä 				  PFIT_VERT_AUTO_SCALE |
408*0f16cd2aSVille Syrjälä 				  PFIT_HORIZ_AUTO_SCALE |
409*0f16cd2aSVille Syrjälä 				  PFIT_VERT_INTERP_BILINEAR |
410*0f16cd2aSVille Syrjälä 				  PFIT_HORIZ_INTERP_BILINEAR);
411*0f16cd2aSVille Syrjälä 	}
412*0f16cd2aSVille Syrjälä }
413*0f16cd2aSVille Syrjälä 
414*0f16cd2aSVille Syrjälä static int intel_gmch_pfit_check_timings(const struct intel_crtc_state *crtc_state)
415*0f16cd2aSVille Syrjälä {
416*0f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
417*0f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
418*0f16cd2aSVille Syrjälä 	const struct drm_display_mode *adjusted_mode =
419*0f16cd2aSVille Syrjälä 		&crtc_state->hw.adjusted_mode;
420*0f16cd2aSVille Syrjälä 	int min;
421*0f16cd2aSVille Syrjälä 
422*0f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) >= 4)
423*0f16cd2aSVille Syrjälä 		min = 3;
424*0f16cd2aSVille Syrjälä 	else
425*0f16cd2aSVille Syrjälä 		min = 2;
426*0f16cd2aSVille Syrjälä 
427*0f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay < min) {
428*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
429*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] horizontal active (%d) below minimum (%d) for pfit\n",
430*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
431*0f16cd2aSVille Syrjälä 			    adjusted_mode->crtc_hdisplay, min);
432*0f16cd2aSVille Syrjälä 		return -EINVAL;
433*0f16cd2aSVille Syrjälä 	}
434*0f16cd2aSVille Syrjälä 
435*0f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_vdisplay < min) {
436*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
437*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] vertical active (%d) below minimum (%d) for pfit\n",
438*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
439*0f16cd2aSVille Syrjälä 			    adjusted_mode->crtc_vdisplay, min);
440*0f16cd2aSVille Syrjälä 		return -EINVAL;
441*0f16cd2aSVille Syrjälä 	}
442*0f16cd2aSVille Syrjälä 
443*0f16cd2aSVille Syrjälä 	return 0;
444*0f16cd2aSVille Syrjälä }
445*0f16cd2aSVille Syrjälä 
446*0f16cd2aSVille Syrjälä static int gmch_panel_fitting(struct intel_crtc_state *crtc_state,
447*0f16cd2aSVille Syrjälä 			      const struct drm_connector_state *conn_state)
448*0f16cd2aSVille Syrjälä {
449*0f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
450*0f16cd2aSVille Syrjälä 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
451*0f16cd2aSVille Syrjälä 	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
452*0f16cd2aSVille Syrjälä 	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
453*0f16cd2aSVille Syrjälä 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
454*0f16cd2aSVille Syrjälä 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
455*0f16cd2aSVille Syrjälä 
456*0f16cd2aSVille Syrjälä 	/* Native modes don't need fitting */
457*0f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
458*0f16cd2aSVille Syrjälä 	    adjusted_mode->crtc_vdisplay == pipe_src_h)
459*0f16cd2aSVille Syrjälä 		goto out;
460*0f16cd2aSVille Syrjälä 
461*0f16cd2aSVille Syrjälä 	/*
462*0f16cd2aSVille Syrjälä 	 * TODO: implement downscaling for i965+. Need to account
463*0f16cd2aSVille Syrjälä 	 * for downscaling in intel_crtc_compute_pixel_rate().
464*0f16cd2aSVille Syrjälä 	 */
465*0f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_hdisplay < pipe_src_w) {
466*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
467*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit horizontal downscaling (%d->%d) not supported\n",
468*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
469*0f16cd2aSVille Syrjälä 			    pipe_src_w, adjusted_mode->crtc_hdisplay);
470*0f16cd2aSVille Syrjälä 		return -EINVAL;
471*0f16cd2aSVille Syrjälä 	}
472*0f16cd2aSVille Syrjälä 	if (adjusted_mode->crtc_vdisplay < pipe_src_h) {
473*0f16cd2aSVille Syrjälä 		drm_dbg_kms(display->drm,
474*0f16cd2aSVille Syrjälä 			    "[CRTC:%d:%s] pfit vertical downscaling (%d->%d) not supported\n",
475*0f16cd2aSVille Syrjälä 			    crtc->base.base.id, crtc->base.name,
476*0f16cd2aSVille Syrjälä 			    pipe_src_h, adjusted_mode->crtc_vdisplay);
477*0f16cd2aSVille Syrjälä 		return -EINVAL;
478*0f16cd2aSVille Syrjälä 	}
479*0f16cd2aSVille Syrjälä 
480*0f16cd2aSVille Syrjälä 	switch (conn_state->scaling_mode) {
481*0f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_CENTER:
482*0f16cd2aSVille Syrjälä 		/*
483*0f16cd2aSVille Syrjälä 		 * For centered modes, we have to calculate border widths &
484*0f16cd2aSVille Syrjälä 		 * heights and modify the values programmed into the CRTC.
485*0f16cd2aSVille Syrjälä 		 */
486*0f16cd2aSVille Syrjälä 		centre_horizontally(adjusted_mode, pipe_src_w);
487*0f16cd2aSVille Syrjälä 		centre_vertically(adjusted_mode, pipe_src_h);
488*0f16cd2aSVille Syrjälä 		border = LVDS_BORDER_ENABLE;
489*0f16cd2aSVille Syrjälä 		break;
490*0f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_ASPECT:
491*0f16cd2aSVille Syrjälä 		/* Scale but preserve the aspect ratio */
492*0f16cd2aSVille Syrjälä 		if (DISPLAY_VER(display) >= 4)
493*0f16cd2aSVille Syrjälä 			i965_scale_aspect(crtc_state, &pfit_control);
494*0f16cd2aSVille Syrjälä 		else
495*0f16cd2aSVille Syrjälä 			i9xx_scale_aspect(crtc_state, &pfit_control,
496*0f16cd2aSVille Syrjälä 					  &pfit_pgm_ratios, &border);
497*0f16cd2aSVille Syrjälä 		break;
498*0f16cd2aSVille Syrjälä 	case DRM_MODE_SCALE_FULLSCREEN:
499*0f16cd2aSVille Syrjälä 		/*
500*0f16cd2aSVille Syrjälä 		 * Full scaling, even if it changes the aspect ratio.
501*0f16cd2aSVille Syrjälä 		 * Fortunately this is all done for us in hw.
502*0f16cd2aSVille Syrjälä 		 */
503*0f16cd2aSVille Syrjälä 		if (pipe_src_h != adjusted_mode->crtc_vdisplay ||
504*0f16cd2aSVille Syrjälä 		    pipe_src_w != adjusted_mode->crtc_hdisplay) {
505*0f16cd2aSVille Syrjälä 			pfit_control |= PFIT_ENABLE;
506*0f16cd2aSVille Syrjälä 			if (DISPLAY_VER(display) >= 4)
507*0f16cd2aSVille Syrjälä 				pfit_control |= PFIT_SCALING_AUTO;
508*0f16cd2aSVille Syrjälä 			else
509*0f16cd2aSVille Syrjälä 				pfit_control |= (PFIT_VERT_AUTO_SCALE |
510*0f16cd2aSVille Syrjälä 						 PFIT_VERT_INTERP_BILINEAR |
511*0f16cd2aSVille Syrjälä 						 PFIT_HORIZ_AUTO_SCALE |
512*0f16cd2aSVille Syrjälä 						 PFIT_HORIZ_INTERP_BILINEAR);
513*0f16cd2aSVille Syrjälä 		}
514*0f16cd2aSVille Syrjälä 		break;
515*0f16cd2aSVille Syrjälä 	default:
516*0f16cd2aSVille Syrjälä 		MISSING_CASE(conn_state->scaling_mode);
517*0f16cd2aSVille Syrjälä 		return -EINVAL;
518*0f16cd2aSVille Syrjälä 	}
519*0f16cd2aSVille Syrjälä 
520*0f16cd2aSVille Syrjälä 	/* 965+ wants fuzzy fitting */
521*0f16cd2aSVille Syrjälä 	/* FIXME: handle multiple panels by failing gracefully */
522*0f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) >= 4)
523*0f16cd2aSVille Syrjälä 		pfit_control |= PFIT_PIPE(crtc->pipe) | PFIT_FILTER_FUZZY;
524*0f16cd2aSVille Syrjälä 
525*0f16cd2aSVille Syrjälä out:
526*0f16cd2aSVille Syrjälä 	if ((pfit_control & PFIT_ENABLE) == 0) {
527*0f16cd2aSVille Syrjälä 		pfit_control = 0;
528*0f16cd2aSVille Syrjälä 		pfit_pgm_ratios = 0;
529*0f16cd2aSVille Syrjälä 	}
530*0f16cd2aSVille Syrjälä 
531*0f16cd2aSVille Syrjälä 	/* Make sure pre-965 set dither correctly for 18bpp panels. */
532*0f16cd2aSVille Syrjälä 	if (DISPLAY_VER(display) < 4 && crtc_state->pipe_bpp == 18)
533*0f16cd2aSVille Syrjälä 		pfit_control |= PFIT_PANEL_8TO6_DITHER_ENABLE;
534*0f16cd2aSVille Syrjälä 
535*0f16cd2aSVille Syrjälä 	crtc_state->gmch_pfit.control = pfit_control;
536*0f16cd2aSVille Syrjälä 	crtc_state->gmch_pfit.pgm_ratios = pfit_pgm_ratios;
537*0f16cd2aSVille Syrjälä 	crtc_state->gmch_pfit.lvds_border_bits = border;
538*0f16cd2aSVille Syrjälä 
539*0f16cd2aSVille Syrjälä 	if ((pfit_control & PFIT_ENABLE) == 0)
540*0f16cd2aSVille Syrjälä 		return 0;
541*0f16cd2aSVille Syrjälä 
542*0f16cd2aSVille Syrjälä 	return intel_gmch_pfit_check_timings(crtc_state);
543*0f16cd2aSVille Syrjälä }
544*0f16cd2aSVille Syrjälä 
545*0f16cd2aSVille Syrjälä int intel_panel_fitting(struct intel_crtc_state *crtc_state,
546*0f16cd2aSVille Syrjälä 			const struct drm_connector_state *conn_state)
547*0f16cd2aSVille Syrjälä {
548*0f16cd2aSVille Syrjälä 	struct intel_display *display = to_intel_display(crtc_state);
549*0f16cd2aSVille Syrjälä 
550*0f16cd2aSVille Syrjälä 	if (HAS_GMCH(display))
551*0f16cd2aSVille Syrjälä 		return gmch_panel_fitting(crtc_state, conn_state);
552*0f16cd2aSVille Syrjälä 	else
553*0f16cd2aSVille Syrjälä 		return pch_panel_fitting(crtc_state, conn_state);
554*0f16cd2aSVille Syrjälä }
555