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ä 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ä 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ä 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ä 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ä 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 */ 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 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 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ä 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ä 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ä 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ä 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ä 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ä 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