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