xref: /linux/drivers/gpu/drm/i915/display/intel_pfit.c (revision bba2c3615bd6cfee7456d1130f2e6b01b3f4e9ba)
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2024 Intel Corporation
4  */
5 
6 #include <drm/drm_print.h>
7 
8 #include "intel_de.h"
9 #include "intel_display_core.h"
10 #include "intel_display_driver.h"
11 #include "intel_display_regs.h"
12 #include "intel_display_types.h"
13 #include "intel_display_utils.h"
14 #include "intel_lvds_regs.h"
15 #include "intel_pfit.h"
16 #include "intel_pfit_regs.h"
17 #include "skl_scaler.h"
18 
19 static int intel_pch_pfit_check_dst_window(const struct intel_crtc_state *crtc_state)
20 {
21 	struct intel_display *display = to_intel_display(crtc_state);
22 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
23 	const struct drm_display_mode *adjusted_mode =
24 		&crtc_state->hw.adjusted_mode;
25 	const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
26 	int width = drm_rect_width(dst);
27 	int height = drm_rect_height(dst);
28 	int x = dst->x1;
29 	int y = dst->y1;
30 
31 	if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE &&
32 	    (y & 1 || height & 1)) {
33 		drm_dbg_kms(display->drm,
34 			    "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") misaligned for interlaced output\n",
35 			    crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
36 		return -EINVAL;
37 	}
38 
39 	/*
40 	 * "Restriction : When pipe scaling is enabled, the scaled
41 	 *  output must equal the pipe active area, so Pipe active
42 	 *  size = (2 * PF window position) + PF window size."
43 	 *
44 	 * The vertical direction seems more forgiving than the
45 	 * horizontal direction, but still has some issues so
46 	 * let's follow the same hard rule for both.
47 	 */
48 	if (adjusted_mode->crtc_hdisplay != 2 * x + width ||
49 	    adjusted_mode->crtc_vdisplay != 2 * y + height) {
50 		drm_dbg_kms(display->drm,
51 			    "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") not centered\n",
52 			    crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
53 		return -EINVAL;
54 	}
55 
56 	/*
57 	 * "Restriction : The X position must not be programmed
58 	 *  to be 1 (28:16=0 0000 0000 0001b)."
59 	 */
60 	if (x == 1) {
61 		drm_dbg_kms(display->drm,
62 			    "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") badly positioned\n",
63 			    crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
64 		return -EINVAL;
65 	}
66 
67 	return 0;
68 }
69 
70 static int intel_pch_pfit_check_src_size(const struct intel_crtc_state *crtc_state)
71 {
72 	struct intel_display *display = to_intel_display(crtc_state);
73 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
74 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
75 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
76 	int max_src_w, max_src_h;
77 
78 	if (DISPLAY_VER(display) >= 8) {
79 		max_src_w = 4096;
80 		max_src_h = 4096;
81 	} else if (DISPLAY_VER(display) >= 7) {
82 		/*
83 		 * PF0 7x5 capable
84 		 * PF1 3x3 capable (could be switched to 7x5
85 		 *                  mode on HSW when PF2 unused)
86 		 * PF2 3x3 capable
87 		 *
88 		 * This assumes we use a 1:1 mapping between pipe and PF.
89 		 */
90 		max_src_w = crtc->pipe == PIPE_A ? 4096 : 2048;
91 		max_src_h = 4096;
92 	} else {
93 		max_src_w = 4096;
94 		max_src_h = 4096;
95 	}
96 
97 	if (pipe_src_w > max_src_w || pipe_src_h > max_src_h) {
98 		drm_dbg_kms(display->drm,
99 			    "[CRTC:%d:%s] source size (%dx%d) exceeds pfit max (%dx%d)\n",
100 			    crtc->base.base.id, crtc->base.name,
101 			    pipe_src_w, pipe_src_h, max_src_w, max_src_h);
102 		return -EINVAL;
103 	}
104 
105 	return 0;
106 }
107 
108 static int intel_pch_pfit_check_scaling(const struct intel_crtc_state *crtc_state)
109 {
110 	struct intel_display *display = to_intel_display(crtc_state);
111 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
112 	const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
113 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
114 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
115 	int hscale, vscale, max_scale = 0x12000; /* 1.125 */
116 	struct drm_rect src;
117 
118 	drm_rect_init(&src, 0, 0, pipe_src_w << 16, pipe_src_h << 16);
119 
120 	hscale = drm_rect_calc_hscale(&src, dst, 0, max_scale);
121 	if (hscale < 0) {
122 		drm_dbg_kms(display->drm,
123 			    "[CRTC:%d:%s] pfit horizontal downscaling (%d->%d) exceeds max (0x%x)\n",
124 			    crtc->base.base.id, crtc->base.name,
125 			    pipe_src_w, drm_rect_width(dst),
126 			    max_scale);
127 		return hscale;
128 	}
129 
130 	vscale = drm_rect_calc_vscale(&src, dst, 0, max_scale);
131 	if (vscale < 0) {
132 		drm_dbg_kms(display->drm,
133 			    "[CRTC:%d:%s] pfit vertical downscaling (%d->%d) exceeds max (0x%x)\n",
134 			    crtc->base.base.id, crtc->base.name,
135 			    pipe_src_h, drm_rect_height(dst),
136 			    max_scale);
137 		return vscale;
138 	}
139 
140 	return 0;
141 }
142 
143 static int intel_pch_pfit_check_timings(const struct intel_crtc_state *crtc_state)
144 {
145 	struct intel_display *display = to_intel_display(crtc_state);
146 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
147 	const struct drm_display_mode *adjusted_mode =
148 		&crtc_state->hw.adjusted_mode;
149 
150 	if (adjusted_mode->crtc_vdisplay < 7) {
151 		drm_dbg_kms(display->drm,
152 			    "[CRTC:%d:%s] vertical active (%d) below minimum (%d) for pfit\n",
153 			    crtc->base.base.id, crtc->base.name,
154 			    adjusted_mode->crtc_vdisplay, 7);
155 		return -EINVAL;
156 	}
157 
158 	return 0;
159 }
160 
161 static int intel_pch_pfit_check_cloning(const struct intel_crtc_state *crtc_state)
162 {
163 	struct intel_display *display = to_intel_display(crtc_state);
164 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
165 
166 	/*
167 	 * The panel fitter is in the pipe and thus would affect every
168 	 * cloned output. The relevant properties (scaling mode, TV
169 	 * margins) are per-connector so we'd have to make sure each
170 	 * output sets them up identically. Seems like a very niche use
171 	 * case so let's just reject cloning entirely when pfit is used.
172 	 */
173 	if (crtc_state->uapi.encoder_mask &&
174 	    !is_power_of_2(crtc_state->uapi.encoder_mask)) {
175 		drm_dbg_kms(display->drm,
176 			    "[CRTC:%d:%s] no pfit when cloning\n",
177 			    crtc->base.base.id, crtc->base.name);
178 		return -EINVAL;
179 	}
180 
181 	return 0;
182 }
183 
184 /* adjusted_mode has been preset to be the panel's fixed mode */
185 static int pch_panel_fitting(struct intel_crtc_state *crtc_state,
186 			     const struct drm_connector_state *conn_state)
187 {
188 	struct intel_display *display = to_intel_display(crtc_state);
189 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
190 	const struct drm_display_mode *adjusted_mode =
191 		&crtc_state->hw.adjusted_mode;
192 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
193 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
194 	int ret, x, y, width, height;
195 
196 	/* Native modes don't need fitting */
197 	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
198 	    adjusted_mode->crtc_vdisplay == pipe_src_h &&
199 	    crtc_state->output_format != INTEL_OUTPUT_FORMAT_YCBCR420 &&
200 	    crtc_state->hw.sharpness_strength == 0)
201 		return 0;
202 
203 	switch (conn_state->scaling_mode) {
204 	case DRM_MODE_SCALE_CENTER:
205 		if (adjusted_mode->crtc_hdisplay < pipe_src_w ||
206 		    adjusted_mode->crtc_vdisplay < pipe_src_h) {
207 			drm_dbg_kms(display->drm,
208 				    "[CRTC:%d:%s] pfit center mode source (%dx%d) exceeds display (%dx%d)\n",
209 				    crtc->base.base.id, crtc->base.name,
210 				    pipe_src_w, pipe_src_h,
211 				    adjusted_mode->crtc_hdisplay,
212 				    adjusted_mode->crtc_vdisplay);
213 			return -EINVAL;
214 		}
215 		width = pipe_src_w;
216 		height = pipe_src_h;
217 		x = (adjusted_mode->crtc_hdisplay - width + 1)/2;
218 		y = (adjusted_mode->crtc_vdisplay - height + 1)/2;
219 		break;
220 
221 	case DRM_MODE_SCALE_ASPECT:
222 		/* Scale but preserve the aspect ratio */
223 		{
224 			u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
225 			u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
226 
227 			if (scaled_width > scaled_height) { /* pillar */
228 				width = scaled_height / pipe_src_h;
229 				if (width & 1)
230 					width++;
231 				x = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
232 				y = 0;
233 				height = adjusted_mode->crtc_vdisplay;
234 			} else if (scaled_width < scaled_height) { /* letter */
235 				height = scaled_width / pipe_src_w;
236 				if (height & 1)
237 					height++;
238 				y = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
239 				x = 0;
240 				width = adjusted_mode->crtc_hdisplay;
241 			} else {
242 				x = y = 0;
243 				width = adjusted_mode->crtc_hdisplay;
244 				height = adjusted_mode->crtc_vdisplay;
245 			}
246 		}
247 		break;
248 
249 	case DRM_MODE_SCALE_NONE:
250 		WARN_ON(adjusted_mode->crtc_hdisplay != pipe_src_w);
251 		WARN_ON(adjusted_mode->crtc_vdisplay != pipe_src_h);
252 		fallthrough;
253 	case DRM_MODE_SCALE_FULLSCREEN:
254 		x = y = 0;
255 		width = adjusted_mode->crtc_hdisplay;
256 		height = adjusted_mode->crtc_vdisplay;
257 		break;
258 
259 	default:
260 		MISSING_CASE(conn_state->scaling_mode);
261 		return -EINVAL;
262 	}
263 
264 	if (crtc_state->hw.sharpness_strength &&
265 	    (width != pipe_src_w || height != pipe_src_h ||
266 	     crtc_state->hw.scaling_filter != DRM_SCALING_FILTER_DEFAULT ||
267 	     crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB)) {
268 		drm_dbg_kms(display->drm,
269 			    "[CRTC:%d:%s] no scaling/YCbCr output with sharpness filter\n",
270 			    crtc->base.base.id, crtc->base.name);
271 		return -EINVAL;
272 	}
273 
274 	drm_rect_init(&crtc_state->pch_pfit.dst,
275 		      x, y, width, height);
276 	crtc_state->pch_pfit.enabled = true;
277 
278 	/*
279 	 * SKL+ have unified scalers for pipes/planes so the
280 	 * checks are done in a single place for all scalers.
281 	 */
282 	if (DISPLAY_VER(display) >= 9)
283 		return 0;
284 
285 	ret = intel_pch_pfit_check_dst_window(crtc_state);
286 	if (ret)
287 		return ret;
288 
289 	ret = intel_pch_pfit_check_src_size(crtc_state);
290 	if (ret)
291 		return ret;
292 
293 	ret = intel_pch_pfit_check_scaling(crtc_state);
294 	if (ret)
295 		return ret;
296 
297 	ret = intel_pch_pfit_check_timings(crtc_state);
298 	if (ret)
299 		return ret;
300 
301 	ret = intel_pch_pfit_check_cloning(crtc_state);
302 	if (ret)
303 		return ret;
304 
305 	return 0;
306 }
307 
308 static void
309 centre_horizontally(struct drm_display_mode *adjusted_mode,
310 		    int width)
311 {
312 	u32 border, sync_pos, blank_width, sync_width;
313 
314 	/* keep the hsync and hblank widths constant */
315 	sync_width = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
316 	blank_width = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
317 	sync_pos = (blank_width - sync_width + 1) / 2;
318 
319 	border = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
320 	border += border & 1; /* make the border even */
321 
322 	adjusted_mode->crtc_hdisplay = width;
323 	adjusted_mode->crtc_hblank_start = width + border;
324 	adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_hblank_start + blank_width;
325 
326 	adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hblank_start + sync_pos;
327 	adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + sync_width;
328 }
329 
330 static void
331 centre_vertically(struct drm_display_mode *adjusted_mode,
332 		  int height)
333 {
334 	u32 border, sync_pos, blank_width, sync_width;
335 
336 	/* keep the vsync and vblank widths constant */
337 	sync_width = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
338 	blank_width = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vblank_start;
339 	sync_pos = (blank_width - sync_width + 1) / 2;
340 
341 	border = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
342 
343 	adjusted_mode->crtc_vdisplay = height;
344 	adjusted_mode->crtc_vblank_start = height + border;
345 	adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vblank_start + blank_width;
346 
347 	adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vblank_start + sync_pos;
348 	adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + sync_width;
349 }
350 
351 static u32 panel_fitter_scaling(u32 source, u32 target)
352 {
353 	/*
354 	 * Floating point operation is not supported. So the FACTOR
355 	 * is defined, which can avoid the floating point computation
356 	 * when calculating the panel ratio.
357 	 */
358 #define ACCURACY 12
359 #define FACTOR (1 << ACCURACY)
360 	u32 ratio = source * FACTOR / target;
361 	return (FACTOR * ratio + FACTOR/2) / FACTOR;
362 }
363 
364 static void i965_scale_aspect(struct intel_crtc_state *crtc_state,
365 			      u32 *pfit_control)
366 {
367 	const struct drm_display_mode *adjusted_mode =
368 		&crtc_state->hw.adjusted_mode;
369 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
370 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
371 	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
372 	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
373 
374 	/* 965+ is easy, it does everything in hw */
375 	if (scaled_width > scaled_height)
376 		*pfit_control |= PFIT_ENABLE |
377 			PFIT_SCALING_PILLAR;
378 	else if (scaled_width < scaled_height)
379 		*pfit_control |= PFIT_ENABLE |
380 			PFIT_SCALING_LETTER;
381 	else if (adjusted_mode->crtc_hdisplay != pipe_src_w)
382 		*pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
383 }
384 
385 static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state,
386 			      u32 *pfit_control, u32 *pfit_pgm_ratios,
387 			      u32 *border)
388 {
389 	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
390 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
391 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
392 	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
393 	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
394 	u32 bits;
395 
396 	/*
397 	 * For earlier chips we have to calculate the scaling
398 	 * ratio by hand and program it into the
399 	 * PFIT_PGM_RATIO register
400 	 */
401 	if (scaled_width > scaled_height) { /* pillar */
402 		centre_horizontally(adjusted_mode,
403 				    scaled_height / pipe_src_h);
404 
405 		*border = LVDS_BORDER_ENABLE;
406 		if (pipe_src_h != adjusted_mode->crtc_vdisplay) {
407 			bits = panel_fitter_scaling(pipe_src_h,
408 						    adjusted_mode->crtc_vdisplay);
409 
410 			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
411 					     PFIT_VERT_SCALE(bits));
412 			*pfit_control |= (PFIT_ENABLE |
413 					  PFIT_VERT_INTERP_BILINEAR |
414 					  PFIT_HORIZ_INTERP_BILINEAR);
415 		}
416 	} else if (scaled_width < scaled_height) { /* letter */
417 		centre_vertically(adjusted_mode,
418 				  scaled_width / pipe_src_w);
419 
420 		*border = LVDS_BORDER_ENABLE;
421 		if (pipe_src_w != adjusted_mode->crtc_hdisplay) {
422 			bits = panel_fitter_scaling(pipe_src_w,
423 						    adjusted_mode->crtc_hdisplay);
424 
425 			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
426 					     PFIT_VERT_SCALE(bits));
427 			*pfit_control |= (PFIT_ENABLE |
428 					  PFIT_VERT_INTERP_BILINEAR |
429 					  PFIT_HORIZ_INTERP_BILINEAR);
430 		}
431 	} else {
432 		/* Aspects match, Let hw scale both directions */
433 		*pfit_control |= (PFIT_ENABLE |
434 				  PFIT_VERT_AUTO_SCALE |
435 				  PFIT_HORIZ_AUTO_SCALE |
436 				  PFIT_VERT_INTERP_BILINEAR |
437 				  PFIT_HORIZ_INTERP_BILINEAR);
438 	}
439 }
440 
441 static int intel_gmch_pfit_check_timings(const struct intel_crtc_state *crtc_state)
442 {
443 	struct intel_display *display = to_intel_display(crtc_state);
444 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
445 	const struct drm_display_mode *adjusted_mode =
446 		&crtc_state->hw.adjusted_mode;
447 	int min;
448 
449 	if (DISPLAY_VER(display) >= 4)
450 		min = 3;
451 	else
452 		min = 2;
453 
454 	if (adjusted_mode->crtc_hdisplay < min) {
455 		drm_dbg_kms(display->drm,
456 			    "[CRTC:%d:%s] horizontal active (%d) below minimum (%d) for pfit\n",
457 			    crtc->base.base.id, crtc->base.name,
458 			    adjusted_mode->crtc_hdisplay, min);
459 		return -EINVAL;
460 	}
461 
462 	if (adjusted_mode->crtc_vdisplay < min) {
463 		drm_dbg_kms(display->drm,
464 			    "[CRTC:%d:%s] vertical active (%d) below minimum (%d) for pfit\n",
465 			    crtc->base.base.id, crtc->base.name,
466 			    adjusted_mode->crtc_vdisplay, min);
467 		return -EINVAL;
468 	}
469 
470 	return 0;
471 }
472 
473 static int gmch_panel_fitting(struct intel_crtc_state *crtc_state,
474 			      const struct drm_connector_state *conn_state)
475 {
476 	struct intel_display *display = to_intel_display(crtc_state);
477 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
478 	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
479 	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
480 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
481 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
482 
483 	/* Native modes don't need fitting */
484 	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
485 	    adjusted_mode->crtc_vdisplay == pipe_src_h)
486 		goto out;
487 
488 	/*
489 	 * TODO: implement downscaling for i965+. Need to account
490 	 * for downscaling in intel_crtc_compute_pixel_rate().
491 	 */
492 	if (adjusted_mode->crtc_hdisplay < pipe_src_w) {
493 		drm_dbg_kms(display->drm,
494 			    "[CRTC:%d:%s] pfit horizontal downscaling (%d->%d) not supported\n",
495 			    crtc->base.base.id, crtc->base.name,
496 			    pipe_src_w, adjusted_mode->crtc_hdisplay);
497 		return -EINVAL;
498 	}
499 	if (adjusted_mode->crtc_vdisplay < pipe_src_h) {
500 		drm_dbg_kms(display->drm,
501 			    "[CRTC:%d:%s] pfit vertical downscaling (%d->%d) not supported\n",
502 			    crtc->base.base.id, crtc->base.name,
503 			    pipe_src_h, adjusted_mode->crtc_vdisplay);
504 		return -EINVAL;
505 	}
506 
507 	switch (conn_state->scaling_mode) {
508 	case DRM_MODE_SCALE_CENTER:
509 		/*
510 		 * For centered modes, we have to calculate border widths &
511 		 * heights and modify the values programmed into the CRTC.
512 		 */
513 		centre_horizontally(adjusted_mode, pipe_src_w);
514 		centre_vertically(adjusted_mode, pipe_src_h);
515 		border = LVDS_BORDER_ENABLE;
516 		break;
517 	case DRM_MODE_SCALE_ASPECT:
518 		/* Scale but preserve the aspect ratio */
519 		if (DISPLAY_VER(display) >= 4)
520 			i965_scale_aspect(crtc_state, &pfit_control);
521 		else
522 			i9xx_scale_aspect(crtc_state, &pfit_control,
523 					  &pfit_pgm_ratios, &border);
524 		break;
525 	case DRM_MODE_SCALE_FULLSCREEN:
526 		/*
527 		 * Full scaling, even if it changes the aspect ratio.
528 		 * Fortunately this is all done for us in hw.
529 		 */
530 		if (pipe_src_h != adjusted_mode->crtc_vdisplay ||
531 		    pipe_src_w != adjusted_mode->crtc_hdisplay) {
532 			pfit_control |= PFIT_ENABLE;
533 			if (DISPLAY_VER(display) >= 4)
534 				pfit_control |= PFIT_SCALING_AUTO;
535 			else
536 				pfit_control |= (PFIT_VERT_AUTO_SCALE |
537 						 PFIT_VERT_INTERP_BILINEAR |
538 						 PFIT_HORIZ_AUTO_SCALE |
539 						 PFIT_HORIZ_INTERP_BILINEAR);
540 		}
541 		break;
542 	default:
543 		MISSING_CASE(conn_state->scaling_mode);
544 		return -EINVAL;
545 	}
546 
547 	/* 965+ wants fuzzy fitting */
548 	/* FIXME: handle multiple panels by failing gracefully */
549 	if (DISPLAY_VER(display) >= 4)
550 		pfit_control |= PFIT_PIPE(crtc->pipe) | PFIT_FILTER_FUZZY;
551 
552 out:
553 	if ((pfit_control & PFIT_ENABLE) == 0) {
554 		pfit_control = 0;
555 		pfit_pgm_ratios = 0;
556 	}
557 
558 	/* Make sure pre-965 set dither correctly for 18bpp panels. */
559 	if (DISPLAY_VER(display) < 4 && crtc_state->pipe_bpp == 18)
560 		pfit_control |= PFIT_PANEL_8TO6_DITHER_ENABLE;
561 
562 	crtc_state->gmch_pfit.control = pfit_control;
563 	crtc_state->gmch_pfit.pgm_ratios = pfit_pgm_ratios;
564 	crtc_state->gmch_pfit.lvds_border_bits = border;
565 
566 	if ((pfit_control & PFIT_ENABLE) == 0)
567 		return 0;
568 
569 	return intel_gmch_pfit_check_timings(crtc_state);
570 }
571 
572 enum drm_mode_status
573 intel_pfit_mode_valid(struct intel_display *display,
574 		      const struct drm_display_mode *mode,
575 		      enum intel_output_format output_format,
576 		      int num_joined_pipes)
577 {
578 	return skl_scaler_mode_valid(display, mode, output_format,
579 				     num_joined_pipes);
580 }
581 
582 int intel_pfit_compute_config(struct intel_crtc_state *crtc_state,
583 			      const struct drm_connector_state *conn_state)
584 {
585 	struct intel_display *display = to_intel_display(crtc_state);
586 
587 	if (HAS_GMCH(display))
588 		return gmch_panel_fitting(crtc_state, conn_state);
589 	else
590 		return pch_panel_fitting(crtc_state, conn_state);
591 }
592 
593 void ilk_pfit_enable(const struct intel_crtc_state *crtc_state)
594 {
595 	struct intel_display *display = to_intel_display(crtc_state);
596 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
597 	const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
598 	enum pipe pipe = crtc->pipe;
599 	int width = drm_rect_width(dst);
600 	int height = drm_rect_height(dst);
601 	int x = dst->x1;
602 	int y = dst->y1;
603 
604 	if (!crtc_state->pch_pfit.enabled)
605 		return;
606 
607 	/*
608 	 * Force use of hard-coded filter coefficients as some pre-programmed
609 	 * values are broken, e.g. x201.
610 	 */
611 	if (display->platform.ivybridge || display->platform.haswell)
612 		intel_de_write_fw(display, PF_CTL(pipe), PF_ENABLE |
613 				  PF_FILTER_MED_3x3 | PF_PIPE_SEL_IVB(pipe));
614 	else
615 		intel_de_write_fw(display, PF_CTL(pipe), PF_ENABLE |
616 				  PF_FILTER_MED_3x3);
617 	intel_de_write_fw(display, PF_WIN_POS(pipe),
618 			  PF_WIN_XPOS(x) | PF_WIN_YPOS(y));
619 	intel_de_write_fw(display, PF_WIN_SZ(pipe),
620 			  PF_WIN_XSIZE(width) | PF_WIN_YSIZE(height));
621 }
622 
623 void ilk_pfit_disable(const struct intel_crtc_state *old_crtc_state)
624 {
625 	struct intel_display *display = to_intel_display(old_crtc_state);
626 	struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc);
627 	enum pipe pipe = crtc->pipe;
628 
629 	/*
630 	 * To avoid upsetting the power well on haswell only disable the pfit if
631 	 * it's in use. The hw state code will make sure we get this right.
632 	 */
633 	if (!old_crtc_state->pch_pfit.enabled)
634 		return;
635 
636 	intel_de_write_fw(display, PF_CTL(pipe), 0);
637 	intel_de_write_fw(display, PF_WIN_POS(pipe), 0);
638 	intel_de_write_fw(display, PF_WIN_SZ(pipe), 0);
639 }
640 
641 void ilk_pfit_get_config(struct intel_crtc_state *crtc_state)
642 {
643 	struct intel_display *display = to_intel_display(crtc_state);
644 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
645 	u32 ctl, pos, size;
646 	enum pipe pipe;
647 
648 	ctl = intel_de_read(display, PF_CTL(crtc->pipe));
649 	if ((ctl & PF_ENABLE) == 0)
650 		return;
651 
652 	if (display->platform.ivybridge || display->platform.haswell)
653 		pipe = REG_FIELD_GET(PF_PIPE_SEL_MASK_IVB, ctl);
654 	else
655 		pipe = crtc->pipe;
656 
657 	crtc_state->pch_pfit.enabled = true;
658 
659 	pos = intel_de_read(display, PF_WIN_POS(crtc->pipe));
660 	size = intel_de_read(display, PF_WIN_SZ(crtc->pipe));
661 
662 	drm_rect_init(&crtc_state->pch_pfit.dst,
663 		      REG_FIELD_GET(PF_WIN_XPOS_MASK, pos),
664 		      REG_FIELD_GET(PF_WIN_YPOS_MASK, pos),
665 		      REG_FIELD_GET(PF_WIN_XSIZE_MASK, size),
666 		      REG_FIELD_GET(PF_WIN_YSIZE_MASK, size));
667 
668 	/*
669 	 * We currently do not free assignments of panel fitters on
670 	 * ivb/hsw (since we don't use the higher upscaling modes which
671 	 * differentiates them) so just WARN about this case for now.
672 	 */
673 	drm_WARN_ON(display->drm, pipe != crtc->pipe);
674 }
675 
676 void i9xx_pfit_enable(const struct intel_crtc_state *crtc_state)
677 {
678 	struct intel_display *display = to_intel_display(crtc_state);
679 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
680 
681 	if (!crtc_state->gmch_pfit.control)
682 		return;
683 
684 	/*
685 	 * The panel fitter should only be adjusted whilst the pipe is disabled,
686 	 * according to register description and PRM.
687 	 */
688 	drm_WARN_ON(display->drm,
689 		    intel_de_read(display, PFIT_CONTROL(display)) & PFIT_ENABLE);
690 	assert_transcoder_disabled(display, crtc_state->cpu_transcoder);
691 
692 	intel_de_write(display, PFIT_PGM_RATIOS(display),
693 		       crtc_state->gmch_pfit.pgm_ratios);
694 	intel_de_write(display, PFIT_CONTROL(display),
695 		       crtc_state->gmch_pfit.control);
696 
697 	/*
698 	 * Border color in case we don't scale up to the full screen. Black by
699 	 * default, change to something else for debugging.
700 	 */
701 	intel_de_write(display, BCLRPAT(display, crtc->pipe), 0);
702 }
703 
704 void i9xx_pfit_disable(const struct intel_crtc_state *old_crtc_state)
705 {
706 	struct intel_display *display = to_intel_display(old_crtc_state);
707 
708 	if (!old_crtc_state->gmch_pfit.control)
709 		return;
710 
711 	assert_transcoder_disabled(display, old_crtc_state->cpu_transcoder);
712 
713 	drm_dbg_kms(display->drm, "disabling pfit, current: 0x%08x\n",
714 		    intel_de_read(display, PFIT_CONTROL(display)));
715 	intel_de_write(display, PFIT_CONTROL(display), 0);
716 }
717 
718 static bool i9xx_has_pfit(struct intel_display *display)
719 {
720 	if (display->platform.i830)
721 		return false;
722 
723 	return DISPLAY_VER(display) >= 4 ||
724 		display->platform.pineview || display->platform.mobile;
725 }
726 
727 void i9xx_pfit_get_config(struct intel_crtc_state *crtc_state)
728 {
729 	struct intel_display *display = to_intel_display(crtc_state);
730 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
731 	enum pipe pipe;
732 	u32 tmp;
733 
734 	if (!i9xx_has_pfit(display))
735 		return;
736 
737 	tmp = intel_de_read(display, PFIT_CONTROL(display));
738 	if (!(tmp & PFIT_ENABLE))
739 		return;
740 
741 	/* Check whether the pfit is attached to our pipe. */
742 	if (DISPLAY_VER(display) >= 4)
743 		pipe = REG_FIELD_GET(PFIT_PIPE_MASK, tmp);
744 	else
745 		pipe = PIPE_B;
746 
747 	if (pipe != crtc->pipe)
748 		return;
749 
750 	crtc_state->gmch_pfit.control = tmp;
751 	crtc_state->gmch_pfit.pgm_ratios =
752 		intel_de_read(display, PFIT_PGM_RATIOS(display));
753 }
754