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