Linux Audio

Check our new training course

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