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