File Index
Files and directories
Sign in
FILE

intel_pfit.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/i915/display/intel_pfit.c C
  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
 19static 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
 70static 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
108static 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
143static 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
161static 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 */
185static 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	const struct drm_display_mode *adjusted_mode =
190		&crtc_state->hw.adjusted_mode;
191	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
192	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
193	int ret, x, y, width, height;
194
195	/* Native modes don't need fitting */
196	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
197	    adjusted_mode->crtc_vdisplay == pipe_src_h &&
198	    crtc_state->output_format != INTEL_OUTPUT_FORMAT_YCBCR420)
199		return 0;
200
201	switch (conn_state->scaling_mode) {
202	case DRM_MODE_SCALE_CENTER:
203		width = pipe_src_w;
204		height = pipe_src_h;
205		x = (adjusted_mode->crtc_hdisplay - width + 1)/2;
206		y = (adjusted_mode->crtc_vdisplay - height + 1)/2;
207		break;
208
209	case DRM_MODE_SCALE_ASPECT:
210		/* Scale but preserve the aspect ratio */
211		{
212			u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
213			u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
214
215			if (scaled_width > scaled_height) { /* pillar */
216				width = scaled_height / pipe_src_h;
217				if (width & 1)
218					width++;
219				x = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
220				y = 0;
221				height = adjusted_mode->crtc_vdisplay;
222			} else if (scaled_width < scaled_height) { /* letter */
223				height = scaled_width / pipe_src_w;
224				if (height & 1)
225					height++;
226				y = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
227				x = 0;
228				width = adjusted_mode->crtc_hdisplay;
229			} else {
230				x = y = 0;
231				width = adjusted_mode->crtc_hdisplay;
232				height = adjusted_mode->crtc_vdisplay;
233			}
234		}
235		break;
236
237	case DRM_MODE_SCALE_NONE:
238		WARN_ON(adjusted_mode->crtc_hdisplay != pipe_src_w);
239		WARN_ON(adjusted_mode->crtc_vdisplay != pipe_src_h);
240		fallthrough;
241	case DRM_MODE_SCALE_FULLSCREEN:
242		x = y = 0;
243		width = adjusted_mode->crtc_hdisplay;
244		height = adjusted_mode->crtc_vdisplay;
245		break;
246
247	default:
248		MISSING_CASE(conn_state->scaling_mode);
249		return -EINVAL;
250	}
251
252	drm_rect_init(&crtc_state->pch_pfit.dst,
253		      x, y, width, height);
254	crtc_state->pch_pfit.enabled = true;
255
256	/*
257	 * SKL+ have unified scalers for pipes/planes so the
258	 * checks are done in a single place for all scalers.
259	 */
260	if (DISPLAY_VER(display) >= 9)
261		return 0;
262
263	ret = intel_pch_pfit_check_dst_window(crtc_state);
264	if (ret)
265		return ret;
266
267	ret = intel_pch_pfit_check_src_size(crtc_state);
268	if (ret)
269		return ret;
270
271	ret = intel_pch_pfit_check_scaling(crtc_state);
272	if (ret)
273		return ret;
274
275	ret = intel_pch_pfit_check_timings(crtc_state);
276	if (ret)
277		return ret;
278
279	ret = intel_pch_pfit_check_cloning(crtc_state);
280	if (ret)
281		return ret;
282
283	return 0;
284}
285
286static void
287centre_horizontally(struct drm_display_mode *adjusted_mode,
288		    int width)
289{
290	u32 border, sync_pos, blank_width, sync_width;
291
292	/* keep the hsync and hblank widths constant */
293	sync_width = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
294	blank_width = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
295	sync_pos = (blank_width - sync_width + 1) / 2;
296
297	border = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
298	border += border & 1; /* make the border even */
299
300	adjusted_mode->crtc_hdisplay = width;
301	adjusted_mode->crtc_hblank_start = width + border;
302	adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_hblank_start + blank_width;
303
304	adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hblank_start + sync_pos;
305	adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + sync_width;
306}
307
308static void
309centre_vertically(struct drm_display_mode *adjusted_mode,
310		  int height)
311{
312	u32 border, sync_pos, blank_width, sync_width;
313
314	/* keep the vsync and vblank widths constant */
315	sync_width = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
316	blank_width = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vblank_start;
317	sync_pos = (blank_width - sync_width + 1) / 2;
318
319	border = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
320
321	adjusted_mode->crtc_vdisplay = height;
322	adjusted_mode->crtc_vblank_start = height + border;
323	adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vblank_start + blank_width;
324
325	adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vblank_start + sync_pos;
326	adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + sync_width;
327}
328
329static u32 panel_fitter_scaling(u32 source, u32 target)
330{
331	/*
332	 * Floating point operation is not supported. So the FACTOR
333	 * is defined, which can avoid the floating point computation
334	 * when calculating the panel ratio.
335	 */
336#define ACCURACY 12
337#define FACTOR (1 << ACCURACY)
338	u32 ratio = source * FACTOR / target;
339	return (FACTOR * ratio + FACTOR/2) / FACTOR;
340}
341
342static void i965_scale_aspect(struct intel_crtc_state *crtc_state,
343			      u32 *pfit_control)
344{
345	const struct drm_display_mode *adjusted_mode =
346		&crtc_state->hw.adjusted_mode;
347	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
348	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
349	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
350	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
351
352	/* 965+ is easy, it does everything in hw */
353	if (scaled_width > scaled_height)
354		*pfit_control |= PFIT_ENABLE |
355			PFIT_SCALING_PILLAR;
356	else if (scaled_width < scaled_height)
357		*pfit_control |= PFIT_ENABLE |
358			PFIT_SCALING_LETTER;
359	else if (adjusted_mode->crtc_hdisplay != pipe_src_w)
360		*pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
361}
362
363static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state,
364			      u32 *pfit_control, u32 *pfit_pgm_ratios,
365			      u32 *border)
366{
367	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
368	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
369	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
370	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
371	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
372	u32 bits;
373
374	/*
375	 * For earlier chips we have to calculate the scaling
376	 * ratio by hand and program it into the
377	 * PFIT_PGM_RATIO register
378	 */
379	if (scaled_width > scaled_height) { /* pillar */
380		centre_horizontally(adjusted_mode,
381				    scaled_height / pipe_src_h);
382
383		*border = LVDS_BORDER_ENABLE;
384		if (pipe_src_h != adjusted_mode->crtc_vdisplay) {
385			bits = panel_fitter_scaling(pipe_src_h,
386						    adjusted_mode->crtc_vdisplay);
387
388			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
389					     PFIT_VERT_SCALE(bits));
390			*pfit_control |= (PFIT_ENABLE |
391					  PFIT_VERT_INTERP_BILINEAR |
392					  PFIT_HORIZ_INTERP_BILINEAR);
393		}
394	} else if (scaled_width < scaled_height) { /* letter */
395		centre_vertically(adjusted_mode,
396				  scaled_width / pipe_src_w);
397
398		*border = LVDS_BORDER_ENABLE;
399		if (pipe_src_w != adjusted_mode->crtc_hdisplay) {
400			bits = panel_fitter_scaling(pipe_src_w,
401						    adjusted_mode->crtc_hdisplay);
402
403			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
404					     PFIT_VERT_SCALE(bits));
405			*pfit_control |= (PFIT_ENABLE |
406					  PFIT_VERT_INTERP_BILINEAR |
407					  PFIT_HORIZ_INTERP_BILINEAR);
408		}
409	} else {
410		/* Aspects match, Let hw scale both directions */
411		*pfit_control |= (PFIT_ENABLE |
412				  PFIT_VERT_AUTO_SCALE |
413				  PFIT_HORIZ_AUTO_SCALE |
414				  PFIT_VERT_INTERP_BILINEAR |
415				  PFIT_HORIZ_INTERP_BILINEAR);
416	}
417}
418
419static int intel_gmch_pfit_check_timings(const struct intel_crtc_state *crtc_state)
420{
421	struct intel_display *display = to_intel_display(crtc_state);
422	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
423	const struct drm_display_mode *adjusted_mode =
424		&crtc_state->hw.adjusted_mode;
425	int min;
426
427	if (DISPLAY_VER(display) >= 4)
428		min = 3;
429	else
430		min = 2;
431
432	if (adjusted_mode->crtc_hdisplay < min) {
433		drm_dbg_kms(display->drm,
434			    "[CRTC:%d:%s] horizontal active (%d) below minimum (%d) for pfit\n",
435			    crtc->base.base.id, crtc->base.name,
436			    adjusted_mode->crtc_hdisplay, min);
437		return -EINVAL;
438	}
439
440	if (adjusted_mode->crtc_vdisplay < min) {
441		drm_dbg_kms(display->drm,
442			    "[CRTC:%d:%s] vertical active (%d) below minimum (%d) for pfit\n",
443			    crtc->base.base.id, crtc->base.name,
444			    adjusted_mode->crtc_vdisplay, min);
445		return -EINVAL;
446	}
447
448	return 0;
449}
450
451static int gmch_panel_fitting(struct intel_crtc_state *crtc_state,
452			      const struct drm_connector_state *conn_state)
453{
454	struct intel_display *display = to_intel_display(crtc_state);
455	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
456	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
457	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
458	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
459	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
460
461	/* Native modes don't need fitting */
462	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
463	    adjusted_mode->crtc_vdisplay == pipe_src_h)
464		goto out;
465
466	/*
467	 * TODO: implement downscaling for i965+. Need to account
468	 * for downscaling in intel_crtc_compute_pixel_rate().
469	 */
470	if (adjusted_mode->crtc_hdisplay < pipe_src_w) {
471		drm_dbg_kms(display->drm,
472			    "[CRTC:%d:%s] pfit horizontal downscaling (%d->%d) not supported\n",
473			    crtc->base.base.id, crtc->base.name,
474			    pipe_src_w, adjusted_mode->crtc_hdisplay);
475		return -EINVAL;
476	}
477	if (adjusted_mode->crtc_vdisplay < pipe_src_h) {
478		drm_dbg_kms(display->drm,
479			    "[CRTC:%d:%s] pfit vertical downscaling (%d->%d) not supported\n",
480			    crtc->base.base.id, crtc->base.name,
481			    pipe_src_h, adjusted_mode->crtc_vdisplay);
482		return -EINVAL;
483	}
484
485	switch (conn_state->scaling_mode) {
486	case DRM_MODE_SCALE_CENTER:
487		/*
488		 * For centered modes, we have to calculate border widths &
489		 * heights and modify the values programmed into the CRTC.
490		 */
491		centre_horizontally(adjusted_mode, pipe_src_w);
492		centre_vertically(adjusted_mode, pipe_src_h);
493		border = LVDS_BORDER_ENABLE;
494		break;
495	case DRM_MODE_SCALE_ASPECT:
496		/* Scale but preserve the aspect ratio */
497		if (DISPLAY_VER(display) >= 4)
498			i965_scale_aspect(crtc_state, &pfit_control);
499		else
500			i9xx_scale_aspect(crtc_state, &pfit_control,
501					  &pfit_pgm_ratios, &border);
502		break;
503	case DRM_MODE_SCALE_FULLSCREEN:
504		/*
505		 * Full scaling, even if it changes the aspect ratio.
506		 * Fortunately this is all done for us in hw.
507		 */
508		if (pipe_src_h != adjusted_mode->crtc_vdisplay ||
509		    pipe_src_w != adjusted_mode->crtc_hdisplay) {
510			pfit_control |= PFIT_ENABLE;
511			if (DISPLAY_VER(display) >= 4)
512				pfit_control |= PFIT_SCALING_AUTO;
513			else
514				pfit_control |= (PFIT_VERT_AUTO_SCALE |
515						 PFIT_VERT_INTERP_BILINEAR |
516						 PFIT_HORIZ_AUTO_SCALE |
517						 PFIT_HORIZ_INTERP_BILINEAR);
518		}
519		break;
520	default:
521		MISSING_CASE(conn_state->scaling_mode);
522		return -EINVAL;
523	}
524
525	/* 965+ wants fuzzy fitting */
526	/* FIXME: handle multiple panels by failing gracefully */
527	if (DISPLAY_VER(display) >= 4)
528		pfit_control |= PFIT_PIPE(crtc->pipe) | PFIT_FILTER_FUZZY;
529
530out:
531	if ((pfit_control & PFIT_ENABLE) == 0) {
532		pfit_control = 0;
533		pfit_pgm_ratios = 0;
534	}
535
536	/* Make sure pre-965 set dither correctly for 18bpp panels. */
537	if (DISPLAY_VER(display) < 4 && crtc_state->pipe_bpp == 18)
538		pfit_control |= PFIT_PANEL_8TO6_DITHER_ENABLE;
539
540	crtc_state->gmch_pfit.control = pfit_control;
541	crtc_state->gmch_pfit.pgm_ratios = pfit_pgm_ratios;
542	crtc_state->gmch_pfit.lvds_border_bits = border;
543
544	if ((pfit_control & PFIT_ENABLE) == 0)
545		return 0;
546
547	return intel_gmch_pfit_check_timings(crtc_state);
548}
549
550enum drm_mode_status
551intel_pfit_mode_valid(struct intel_display *display,
552		      const struct drm_display_mode *mode,
553		      enum intel_output_format output_format,
554		      int num_joined_pipes)
555{
556	return skl_scaler_mode_valid(display, mode, output_format,
557				     num_joined_pipes);
558}
559
560int intel_pfit_compute_config(struct intel_crtc_state *crtc_state,
561			      const struct drm_connector_state *conn_state)
562{
563	struct intel_display *display = to_intel_display(crtc_state);
564
565	if (HAS_GMCH(display))
566		return gmch_panel_fitting(crtc_state, conn_state);
567	else
568		return pch_panel_fitting(crtc_state, conn_state);
569}
570
571void ilk_pfit_enable(const struct intel_crtc_state *crtc_state)
572{
573	struct intel_display *display = to_intel_display(crtc_state);
574	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
575	const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
576	enum pipe pipe = crtc->pipe;
577	int width = drm_rect_width(dst);
578	int height = drm_rect_height(dst);
579	int x = dst->x1;
580	int y = dst->y1;
581
582	if (!crtc_state->pch_pfit.enabled)
583		return;
584
585	/*
586	 * Force use of hard-coded filter coefficients as some pre-programmed
587	 * values are broken, e.g. x201.
588	 */
589	if (display->platform.ivybridge || display->platform.haswell)
590		intel_de_write_fw(display, PF_CTL(pipe), PF_ENABLE |
591				  PF_FILTER_MED_3x3 | PF_PIPE_SEL_IVB(pipe));
592	else
593		intel_de_write_fw(display, PF_CTL(pipe), PF_ENABLE |
594				  PF_FILTER_MED_3x3);
595	intel_de_write_fw(display, PF_WIN_POS(pipe),
596			  PF_WIN_XPOS(x) | PF_WIN_YPOS(y));
597	intel_de_write_fw(display, PF_WIN_SZ(pipe),
598			  PF_WIN_XSIZE(width) | PF_WIN_YSIZE(height));
599}
600
601void ilk_pfit_disable(const struct intel_crtc_state *old_crtc_state)
602{
603	struct intel_display *display = to_intel_display(old_crtc_state);
604	struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc);
605	enum pipe pipe = crtc->pipe;
606
607	/*
608	 * To avoid upsetting the power well on haswell only disable the pfit if
609	 * it's in use. The hw state code will make sure we get this right.
610	 */
611	if (!old_crtc_state->pch_pfit.enabled)
612		return;
613
614	intel_de_write_fw(display, PF_CTL(pipe), 0);
615	intel_de_write_fw(display, PF_WIN_POS(pipe), 0);
616	intel_de_write_fw(display, PF_WIN_SZ(pipe), 0);
617}
618
619void ilk_pfit_get_config(struct intel_crtc_state *crtc_state)
620{
621	struct intel_display *display = to_intel_display(crtc_state);
622	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
623	u32 ctl, pos, size;
624	enum pipe pipe;
625
626	ctl = intel_de_read(display, PF_CTL(crtc->pipe));
627	if ((ctl & PF_ENABLE) == 0)
628		return;
629
630	if (display->platform.ivybridge || display->platform.haswell)
631		pipe = REG_FIELD_GET(PF_PIPE_SEL_MASK_IVB, ctl);
632	else
633		pipe = crtc->pipe;
634
635	crtc_state->pch_pfit.enabled = true;
636
637	pos = intel_de_read(display, PF_WIN_POS(crtc->pipe));
638	size = intel_de_read(display, PF_WIN_SZ(crtc->pipe));
639
640	drm_rect_init(&crtc_state->pch_pfit.dst,
641		      REG_FIELD_GET(PF_WIN_XPOS_MASK, pos),
642		      REG_FIELD_GET(PF_WIN_YPOS_MASK, pos),
643		      REG_FIELD_GET(PF_WIN_XSIZE_MASK, size),
644		      REG_FIELD_GET(PF_WIN_YSIZE_MASK, size));
645
646	/*
647	 * We currently do not free assignments of panel fitters on
648	 * ivb/hsw (since we don't use the higher upscaling modes which
649	 * differentiates them) so just WARN about this case for now.
650	 */
651	drm_WARN_ON(display->drm, pipe != crtc->pipe);
652}
653
654void i9xx_pfit_enable(const struct intel_crtc_state *crtc_state)
655{
656	struct intel_display *display = to_intel_display(crtc_state);
657	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
658
659	if (!crtc_state->gmch_pfit.control)
660		return;
661
662	/*
663	 * The panel fitter should only be adjusted whilst the pipe is disabled,
664	 * according to register description and PRM.
665	 */
666	drm_WARN_ON(display->drm,
667		    intel_de_read(display, PFIT_CONTROL(display)) & PFIT_ENABLE);
668	assert_transcoder_disabled(display, crtc_state->cpu_transcoder);
669
670	intel_de_write(display, PFIT_PGM_RATIOS(display),
671		       crtc_state->gmch_pfit.pgm_ratios);
672	intel_de_write(display, PFIT_CONTROL(display),
673		       crtc_state->gmch_pfit.control);
674
675	/*
676	 * Border color in case we don't scale up to the full screen. Black by
677	 * default, change to something else for debugging.
678	 */
679	intel_de_write(display, BCLRPAT(display, crtc->pipe), 0);
680}
681
682void i9xx_pfit_disable(const struct intel_crtc_state *old_crtc_state)
683{
684	struct intel_display *display = to_intel_display(old_crtc_state);
685
686	if (!old_crtc_state->gmch_pfit.control)
687		return;
688
689	assert_transcoder_disabled(display, old_crtc_state->cpu_transcoder);
690
691	drm_dbg_kms(display->drm, "disabling pfit, current: 0x%08x\n",
692		    intel_de_read(display, PFIT_CONTROL(display)));
693	intel_de_write(display, PFIT_CONTROL(display), 0);
694}
695
696static bool i9xx_has_pfit(struct intel_display *display)
697{
698	if (display->platform.i830)
699		return false;
700
701	return DISPLAY_VER(display) >= 4 ||
702		display->platform.pineview || display->platform.mobile;
703}
704
705void i9xx_pfit_get_config(struct intel_crtc_state *crtc_state)
706{
707	struct intel_display *display = to_intel_display(crtc_state);
708	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
709	enum pipe pipe;
710	u32 tmp;
711
712	if (!i9xx_has_pfit(display))
713		return;
714
715	tmp = intel_de_read(display, PFIT_CONTROL(display));
716	if (!(tmp & PFIT_ENABLE))
717		return;
718
719	/* Check whether the pfit is attached to our pipe. */
720	if (DISPLAY_VER(display) >= 4)
721		pipe = REG_FIELD_GET(PFIT_PIPE_MASK, tmp);
722	else
723		pipe = PIPE_B;
724
725	if (pipe != crtc->pipe)
726		return;
727
728	crtc_state->gmch_pfit.control = tmp;
729	crtc_state->gmch_pfit.pgm_ratios =
730		intel_de_read(display, PFIT_PGM_RATIOS(display));
731}
~/ Read-only file preview.