1// SPDX-License-Identifier: MIT
2/*
3 * Copyright © 2024 Intel Corporation
4 */
5
6#include <drm/drm_print.h>
7
8#include "i915_utils.h"
9#include "intel_de.h"
10#include "intel_display_core.h"
11#include "intel_display_driver.h"
12#include "intel_display_regs.h"
13#include "intel_display_types.h"
14#include "intel_lvds_regs.h"
15#include "intel_pfit.h"
16#include "intel_pfit_regs.h"
17#include "skl_scaler.h"
18
19static int intel_pch_pfit_check_dst_window(const struct intel_crtc_state *crtc_state)
20{
21 struct intel_display *display = to_intel_display(crtc_state);
22 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
23 const struct drm_display_mode *adjusted_mode =
24 &crtc_state->hw.adjusted_mode;
25 const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
26 int width = drm_rect_width(r: dst);
27 int height = drm_rect_height(r: dst);
28 int x = dst->x1;
29 int y = dst->y1;
30
31 if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE &&
32 (y & 1 || height & 1)) {
33 drm_dbg_kms(display->drm,
34 "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") misaligned for interlaced output\n",
35 crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
36 return -EINVAL;
37 }
38
39 /*
40 * "Restriction : When pipe scaling is enabled, the scaled
41 * output must equal the pipe active area, so Pipe active
42 * size = (2 * PF window position) + PF window size."
43 *
44 * The vertical direction seems more forgiving than the
45 * horizontal direction, but still has some issues so
46 * let's follow the same hard rule for both.
47 */
48 if (adjusted_mode->crtc_hdisplay != 2 * x + width ||
49 adjusted_mode->crtc_vdisplay != 2 * y + height) {
50 drm_dbg_kms(display->drm,
51 "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") not centered\n",
52 crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
53 return -EINVAL;
54 }
55
56 /*
57 * "Restriction : The X position must not be programmed
58 * to be 1 (28:16=0 0000 0000 0001b)."
59 */
60 if (x == 1) {
61 drm_dbg_kms(display->drm,
62 "[CRTC:%d:%s] pfit window (" DRM_RECT_FMT ") badly positioned\n",
63 crtc->base.base.id, crtc->base.name, DRM_RECT_ARG(dst));
64 return -EINVAL;
65 }
66
67 return 0;
68}
69
70static int intel_pch_pfit_check_src_size(const struct intel_crtc_state *crtc_state)
71{
72 struct intel_display *display = to_intel_display(crtc_state);
73 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
74 int pipe_src_w = drm_rect_width(r: &crtc_state->pipe_src);
75 int pipe_src_h = drm_rect_height(r: &crtc_state->pipe_src);
76 int max_src_w, max_src_h;
77
78 if (DISPLAY_VER(display) >= 8) {
79 max_src_w = 4096;
80 max_src_h = 4096;
81 } else if (DISPLAY_VER(display) >= 7) {
82 /*
83 * PF0 7x5 capable
84 * PF1 3x3 capable (could be switched to 7x5
85 * mode on HSW when PF2 unused)
86 * PF2 3x3 capable
87 *
88 * This assumes we use a 1:1 mapping between pipe and PF.
89 */
90 max_src_w = crtc->pipe == PIPE_A ? 4096 : 2048;
91 max_src_h = 4096;
92 } else {
93 max_src_w = 4096;
94 max_src_h = 4096;
95 }
96
97 if (pipe_src_w > max_src_w || pipe_src_h > max_src_h) {
98 drm_dbg_kms(display->drm,
99 "[CRTC:%d:%s] source size (%dx%d) exceeds pfit max (%dx%d)\n",
100 crtc->base.base.id, crtc->base.name,
101 pipe_src_w, pipe_src_h, max_src_w, max_src_h);
102 return -EINVAL;
103 }
104
105 return 0;
106}
107
108static int intel_pch_pfit_check_scaling(const struct intel_crtc_state *crtc_state)
109{
110 struct intel_display *display = to_intel_display(crtc_state);
111 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
112 const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
113 int pipe_src_w = drm_rect_width(r: &crtc_state->pipe_src);
114 int pipe_src_h = drm_rect_height(r: &crtc_state->pipe_src);
115 int hscale, vscale, max_scale = 0x12000; /* 1.125 */
116 struct drm_rect src;
117
118 drm_rect_init(r: &src, x: 0, y: 0, width: pipe_src_w << 16, height: pipe_src_h << 16);
119
120 hscale = drm_rect_calc_hscale(src: &src, dst, min_hscale: 0, max_hscale: max_scale);
121 if (hscale < 0) {
122 drm_dbg_kms(display->drm,
123 "[CRTC:%d:%s] pfit horizontal downscaling (%d->%d) exceeds max (0x%x)\n",
124 crtc->base.base.id, crtc->base.name,
125 pipe_src_w, drm_rect_width(dst),
126 max_scale);
127 return hscale;
128 }
129
130 vscale = drm_rect_calc_vscale(src: &src, dst, min_vscale: 0, max_vscale: max_scale);
131 if (vscale < 0) {
132 drm_dbg_kms(display->drm,
133 "[CRTC:%d:%s] pfit vertical downscaling (%d->%d) exceeds max (0x%x)\n",
134 crtc->base.base.id, crtc->base.name,
135 pipe_src_h, drm_rect_height(dst),
136 max_scale);
137 return vscale;
138 }
139
140 return 0;
141}
142
143static int intel_pch_pfit_check_timings(const struct intel_crtc_state *crtc_state)
144{
145 struct intel_display *display = to_intel_display(crtc_state);
146 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
147 const struct drm_display_mode *adjusted_mode =
148 &crtc_state->hw.adjusted_mode;
149
150 if (adjusted_mode->crtc_vdisplay < 7) {
151 drm_dbg_kms(display->drm,
152 "[CRTC:%d:%s] vertical active (%d) below minimum (%d) for pfit\n",
153 crtc->base.base.id, crtc->base.name,
154 adjusted_mode->crtc_vdisplay, 7);
155 return -EINVAL;
156 }
157
158 return 0;
159}
160
161static int intel_pch_pfit_check_cloning(const struct intel_crtc_state *crtc_state)
162{
163 struct intel_display *display = to_intel_display(crtc_state);
164 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
165
166 /*
167 * The panel fitter is in the pipe and thus would affect every
168 * cloned output. The relevant properties (scaling mode, TV
169 * margins) are per-connector so we'd have to make sure each
170 * output sets them up identically. Seems like a very niche use
171 * case so let's just reject cloning entirely when pfit is used.
172 */
173 if (crtc_state->uapi.encoder_mask &&
174 !is_power_of_2(n: crtc_state->uapi.encoder_mask)) {
175 drm_dbg_kms(display->drm,
176 "[CRTC:%d:%s] no pfit when cloning\n",
177 crtc->base.base.id, crtc->base.name);
178 return -EINVAL;
179 }
180
181 return 0;
182}
183
184/* adjusted_mode has been preset to be the panel's fixed mode */
185static int pch_panel_fitting(struct intel_crtc_state *crtc_state,
186 const struct drm_connector_state *conn_state)
187{
188 struct intel_display *display = to_intel_display(crtc_state);
189 const struct drm_display_mode *adjusted_mode =
190 &crtc_state->hw.adjusted_mode;
191 int pipe_src_w = drm_rect_width(r: &crtc_state->pipe_src);
192 int pipe_src_h = drm_rect_height(r: &crtc_state->pipe_src);
193 int ret, x, y, width, height;
194
195 /* Native modes don't need fitting */
196 if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
197 adjusted_mode->crtc_vdisplay == pipe_src_h &&
198 crtc_state->output_format != INTEL_OUTPUT_FORMAT_YCBCR420)
199 return 0;
200
201 switch (conn_state->scaling_mode) {
202 case DRM_MODE_SCALE_CENTER:
203 width = pipe_src_w;
204 height = pipe_src_h;
205 x = (adjusted_mode->crtc_hdisplay - width + 1)/2;
206 y = (adjusted_mode->crtc_vdisplay - height + 1)/2;
207 break;
208
209 case DRM_MODE_SCALE_ASPECT:
210 /* Scale but preserve the aspect ratio */
211 {
212 u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
213 u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
214
215 if (scaled_width > scaled_height) { /* pillar */
216 width = scaled_height / pipe_src_h;
217 if (width & 1)
218 width++;
219 x = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
220 y = 0;
221 height = adjusted_mode->crtc_vdisplay;
222 } else if (scaled_width < scaled_height) { /* letter */
223 height = scaled_width / pipe_src_w;
224 if (height & 1)
225 height++;
226 y = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
227 x = 0;
228 width = adjusted_mode->crtc_hdisplay;
229 } else {
230 x = y = 0;
231 width = adjusted_mode->crtc_hdisplay;
232 height = adjusted_mode->crtc_vdisplay;
233 }
234 }
235 break;
236
237 case DRM_MODE_SCALE_NONE:
238 WARN_ON(adjusted_mode->crtc_hdisplay != pipe_src_w);
239 WARN_ON(adjusted_mode->crtc_vdisplay != pipe_src_h);
240 fallthrough;
241 case DRM_MODE_SCALE_FULLSCREEN:
242 x = y = 0;
243 width = adjusted_mode->crtc_hdisplay;
244 height = adjusted_mode->crtc_vdisplay;
245 break;
246
247 default:
248 MISSING_CASE(conn_state->scaling_mode);
249 return -EINVAL;
250 }
251
252 drm_rect_init(r: &crtc_state->pch_pfit.dst,
253 x, y, width, height);
254 crtc_state->pch_pfit.enabled = true;
255
256 /*
257 * SKL+ have unified scalers for pipes/planes so the
258 * checks are done in a single place for all scalers.
259 */
260 if (DISPLAY_VER(display) >= 9)
261 return 0;
262
263 ret = intel_pch_pfit_check_dst_window(crtc_state);
264 if (ret)
265 return ret;
266
267 ret = intel_pch_pfit_check_src_size(crtc_state);
268 if (ret)
269 return ret;
270
271 ret = intel_pch_pfit_check_scaling(crtc_state);
272 if (ret)
273 return ret;
274
275 ret = intel_pch_pfit_check_timings(crtc_state);
276 if (ret)
277 return ret;
278
279 ret = intel_pch_pfit_check_cloning(crtc_state);
280 if (ret)
281 return ret;
282
283 return 0;
284}
285
286static void
287centre_horizontally(struct drm_display_mode *adjusted_mode,
288 int width)
289{
290 u32 border, sync_pos, blank_width, sync_width;
291
292 /* keep the hsync and hblank widths constant */
293 sync_width = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
294 blank_width = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
295 sync_pos = (blank_width - sync_width + 1) / 2;
296
297 border = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
298 border += border & 1; /* make the border even */
299
300 adjusted_mode->crtc_hdisplay = width;
301 adjusted_mode->crtc_hblank_start = width + border;
302 adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_hblank_start + blank_width;
303
304 adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hblank_start + sync_pos;
305 adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + sync_width;
306}
307
308static void
309centre_vertically(struct drm_display_mode *adjusted_mode,
310 int height)
311{
312 u32 border, sync_pos, blank_width, sync_width;
313
314 /* keep the vsync and vblank widths constant */
315 sync_width = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
316 blank_width = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vblank_start;
317 sync_pos = (blank_width - sync_width + 1) / 2;
318
319 border = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
320
321 adjusted_mode->crtc_vdisplay = height;
322 adjusted_mode->crtc_vblank_start = height + border;
323 adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vblank_start + blank_width;
324
325 adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vblank_start + sync_pos;
326 adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + sync_width;
327}
328
329static u32 panel_fitter_scaling(u32 source, u32 target)
330{
331 /*
332 * Floating point operation is not supported. So the FACTOR
333 * is defined, which can avoid the floating point computation
334 * when calculating the panel ratio.
335 */
336#define ACCURACY 12
337#define FACTOR (1 << ACCURACY)
338 u32 ratio = source * FACTOR / target;
339 return (FACTOR * ratio + FACTOR/2) / FACTOR;
340}
341
342static void i965_scale_aspect(struct intel_crtc_state *crtc_state,
343 u32 *pfit_control)
344{
345 const struct drm_display_mode *adjusted_mode =
346 &crtc_state->hw.adjusted_mode;
347 int pipe_src_w = drm_rect_width(r: &crtc_state->pipe_src);
348 int pipe_src_h = drm_rect_height(r: &crtc_state->pipe_src);
349 u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
350 u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
351
352 /* 965+ is easy, it does everything in hw */
353 if (scaled_width > scaled_height)
354 *pfit_control |= PFIT_ENABLE |
355 PFIT_SCALING_PILLAR;
356 else if (scaled_width < scaled_height)
357 *pfit_control |= PFIT_ENABLE |
358 PFIT_SCALING_LETTER;
359 else if (adjusted_mode->crtc_hdisplay != pipe_src_w)
360 *pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
361}
362
363static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state,
364 u32 *pfit_control, u32 *pfit_pgm_ratios,
365 u32 *border)
366{
367 struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
368 int pipe_src_w = drm_rect_width(r: &crtc_state->pipe_src);
369 int pipe_src_h = drm_rect_height(r: &crtc_state->pipe_src);
370 u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
371 u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
372 u32 bits;
373
374 /*
375 * For earlier chips we have to calculate the scaling
376 * ratio by hand and program it into the
377 * PFIT_PGM_RATIO register
378 */
379 if (scaled_width > scaled_height) { /* pillar */
380 centre_horizontally(adjusted_mode,
381 width: scaled_height / pipe_src_h);
382
383 *border = LVDS_BORDER_ENABLE;
384 if (pipe_src_h != adjusted_mode->crtc_vdisplay) {
385 bits = panel_fitter_scaling(source: pipe_src_h,
386 target: adjusted_mode->crtc_vdisplay);
387
388 *pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
389 PFIT_VERT_SCALE(bits));
390 *pfit_control |= (PFIT_ENABLE |
391 PFIT_VERT_INTERP_BILINEAR |
392 PFIT_HORIZ_INTERP_BILINEAR);
393 }
394 } else if (scaled_width < scaled_height) { /* letter */
395 centre_vertically(adjusted_mode,
396 height: scaled_width / pipe_src_w);
397
398 *border = LVDS_BORDER_ENABLE;
399 if (pipe_src_w != adjusted_mode->crtc_hdisplay) {
400 bits = panel_fitter_scaling(source: pipe_src_w,
401 target: adjusted_mode->crtc_hdisplay);
402
403 *pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
404 PFIT_VERT_SCALE(bits));
405 *pfit_control |= (PFIT_ENABLE |
406 PFIT_VERT_INTERP_BILINEAR |
407 PFIT_HORIZ_INTERP_BILINEAR);
408 }
409 } else {
410 /* Aspects match, Let hw scale both directions */
411 *pfit_control |= (PFIT_ENABLE |
412 PFIT_VERT_AUTO_SCALE |
413 PFIT_HORIZ_AUTO_SCALE |
414 PFIT_VERT_INTERP_BILINEAR |
415 PFIT_HORIZ_INTERP_BILINEAR);
416 }
417}
418
419static int intel_gmch_pfit_check_timings(const struct intel_crtc_state *crtc_state)
420{
421 struct intel_display *display = to_intel_display(crtc_state);
422 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
423 const struct drm_display_mode *adjusted_mode =
424 &crtc_state->hw.adjusted_mode;
425 int min;
426
427 if (DISPLAY_VER(display) >= 4)
428 min = 3;
429 else
430 min = 2;
431
432 if (adjusted_mode->crtc_hdisplay < min) {
433 drm_dbg_kms(display->drm,
434 "[CRTC:%d:%s] horizontal active (%d) below minimum (%d) for pfit\n",
435 crtc->base.base.id, crtc->base.name,
436 adjusted_mode->crtc_hdisplay, min);
437 return -EINVAL;
438 }
439
440 if (adjusted_mode->crtc_vdisplay < min) {
441 drm_dbg_kms(display->drm,
442 "[CRTC:%d:%s] vertical active (%d) below minimum (%d) for pfit\n",
443 crtc->base.base.id, crtc->base.name,
444 adjusted_mode->crtc_vdisplay, min);
445 return -EINVAL;
446 }
447
448 return 0;
449}
450
451static int gmch_panel_fitting(struct intel_crtc_state *crtc_state,
452 const struct drm_connector_state *conn_state)
453{
454 struct intel_display *display = to_intel_display(crtc_state);
455 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
456 u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
457 struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
458 int pipe_src_w = drm_rect_width(r: &crtc_state->pipe_src);
459 int pipe_src_h = drm_rect_height(r: &crtc_state->pipe_src);
460
461 /* Native modes don't need fitting */
462 if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
463 adjusted_mode->crtc_vdisplay == pipe_src_h)
464 goto out;
465
466 /*
467 * TODO: implement downscaling for i965+. Need to account
468 * for downscaling in intel_crtc_compute_pixel_rate().
469 */
470 if (adjusted_mode->crtc_hdisplay < pipe_src_w) {
471 drm_dbg_kms(display->drm,
472 "[CRTC:%d:%s] pfit horizontal downscaling (%d->%d) not supported\n",
473 crtc->base.base.id, crtc->base.name,
474 pipe_src_w, adjusted_mode->crtc_hdisplay);
475 return -EINVAL;
476 }
477 if (adjusted_mode->crtc_vdisplay < pipe_src_h) {
478 drm_dbg_kms(display->drm,
479 "[CRTC:%d:%s] pfit vertical downscaling (%d->%d) not supported\n",
480 crtc->base.base.id, crtc->base.name,
481 pipe_src_h, adjusted_mode->crtc_vdisplay);
482 return -EINVAL;
483 }
484
485 switch (conn_state->scaling_mode) {
486 case DRM_MODE_SCALE_CENTER:
487 /*
488 * For centered modes, we have to calculate border widths &
489 * heights and modify the values programmed into the CRTC.
490 */
491 centre_horizontally(adjusted_mode, width: pipe_src_w);
492 centre_vertically(adjusted_mode, height: pipe_src_h);
493 border = LVDS_BORDER_ENABLE;
494 break;
495 case DRM_MODE_SCALE_ASPECT:
496 /* Scale but preserve the aspect ratio */
497 if (DISPLAY_VER(display) >= 4)
498 i965_scale_aspect(crtc_state, pfit_control: &pfit_control);
499 else
500 i9xx_scale_aspect(crtc_state, pfit_control: &pfit_control,
501 pfit_pgm_ratios: &pfit_pgm_ratios, border: &border);
502 break;
503 case DRM_MODE_SCALE_FULLSCREEN:
504 /*
505 * Full scaling, even if it changes the aspect ratio.
506 * Fortunately this is all done for us in hw.
507 */
508 if (pipe_src_h != adjusted_mode->crtc_vdisplay ||
509 pipe_src_w != adjusted_mode->crtc_hdisplay) {
510 pfit_control |= PFIT_ENABLE;
511 if (DISPLAY_VER(display) >= 4)
512 pfit_control |= PFIT_SCALING_AUTO;
513 else
514 pfit_control |= (PFIT_VERT_AUTO_SCALE |
515 PFIT_VERT_INTERP_BILINEAR |
516 PFIT_HORIZ_AUTO_SCALE |
517 PFIT_HORIZ_INTERP_BILINEAR);
518 }
519 break;
520 default:
521 MISSING_CASE(conn_state->scaling_mode);
522 return -EINVAL;
523 }
524
525 /* 965+ wants fuzzy fitting */
526 /* FIXME: handle multiple panels by failing gracefully */
527 if (DISPLAY_VER(display) >= 4)
528 pfit_control |= PFIT_PIPE(crtc->pipe) | PFIT_FILTER_FUZZY;
529
530out:
531 if ((pfit_control & PFIT_ENABLE) == 0) {
532 pfit_control = 0;
533 pfit_pgm_ratios = 0;
534 }
535
536 /* Make sure pre-965 set dither correctly for 18bpp panels. */
537 if (DISPLAY_VER(display) < 4 && crtc_state->pipe_bpp == 18)
538 pfit_control |= PFIT_PANEL_8TO6_DITHER_ENABLE;
539
540 crtc_state->gmch_pfit.control = pfit_control;
541 crtc_state->gmch_pfit.pgm_ratios = pfit_pgm_ratios;
542 crtc_state->gmch_pfit.lvds_border_bits = border;
543
544 if ((pfit_control & PFIT_ENABLE) == 0)
545 return 0;
546
547 return intel_gmch_pfit_check_timings(crtc_state);
548}
549
550enum drm_mode_status
551intel_pfit_mode_valid(struct intel_display *display,
552 const struct drm_display_mode *mode,
553 enum intel_output_format output_format,
554 int num_joined_pipes)
555{
556 return skl_scaler_mode_valid(display, mode, output_format,
557 num_joined_pipes);
558}
559
560int intel_pfit_compute_config(struct intel_crtc_state *crtc_state,
561 const struct drm_connector_state *conn_state)
562{
563 struct intel_display *display = to_intel_display(crtc_state);
564
565 if (HAS_GMCH(display))
566 return gmch_panel_fitting(crtc_state, conn_state);
567 else
568 return pch_panel_fitting(crtc_state, conn_state);
569}
570
571void ilk_pfit_enable(const struct intel_crtc_state *crtc_state)
572{
573 struct intel_display *display = to_intel_display(crtc_state);
574 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
575 const struct drm_rect *dst = &crtc_state->pch_pfit.dst;
576 enum pipe pipe = crtc->pipe;
577 int width = drm_rect_width(r: dst);
578 int height = drm_rect_height(r: dst);
579 int x = dst->x1;
580 int y = dst->y1;
581
582 if (!crtc_state->pch_pfit.enabled)
583 return;
584
585 /*
586 * Force use of hard-coded filter coefficients as some pre-programmed
587 * values are broken, e.g. x201.
588 */
589 if (display->platform.ivybridge || display->platform.haswell)
590 intel_de_write_fw(display, PF_CTL(pipe), PF_ENABLE |
591 PF_FILTER_MED_3x3 | PF_PIPE_SEL_IVB(pipe));
592 else
593 intel_de_write_fw(display, PF_CTL(pipe), PF_ENABLE |
594 PF_FILTER_MED_3x3);
595 intel_de_write_fw(display, PF_WIN_POS(pipe),
596 PF_WIN_XPOS(x) | PF_WIN_YPOS(y));
597 intel_de_write_fw(display, PF_WIN_SZ(pipe),
598 PF_WIN_XSIZE(width) | PF_WIN_YSIZE(height));
599}
600
601void ilk_pfit_disable(const struct intel_crtc_state *old_crtc_state)
602{
603 struct intel_display *display = to_intel_display(old_crtc_state);
604 struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc);
605 enum pipe pipe = crtc->pipe;
606
607 /*
608 * To avoid upsetting the power well on haswell only disable the pfit if
609 * it's in use. The hw state code will make sure we get this right.
610 */
611 if (!old_crtc_state->pch_pfit.enabled)
612 return;
613
614 intel_de_write_fw(display, PF_CTL(pipe), val: 0);
615 intel_de_write_fw(display, PF_WIN_POS(pipe), val: 0);
616 intel_de_write_fw(display, PF_WIN_SZ(pipe), val: 0);
617}
618
619void ilk_pfit_get_config(struct intel_crtc_state *crtc_state)
620{
621 struct intel_display *display = to_intel_display(crtc_state);
622 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
623 u32 ctl, pos, size;
624 enum pipe pipe;
625
626 ctl = intel_de_read(display, PF_CTL(crtc->pipe));
627 if ((ctl & PF_ENABLE) == 0)
628 return;
629
630 if (display->platform.ivybridge || display->platform.haswell)
631 pipe = REG_FIELD_GET(PF_PIPE_SEL_MASK_IVB, ctl);
632 else
633 pipe = crtc->pipe;
634
635 crtc_state->pch_pfit.enabled = true;
636
637 pos = intel_de_read(display, PF_WIN_POS(crtc->pipe));
638 size = intel_de_read(display, PF_WIN_SZ(crtc->pipe));
639
640 drm_rect_init(r: &crtc_state->pch_pfit.dst,
641 REG_FIELD_GET(PF_WIN_XPOS_MASK, pos),
642 REG_FIELD_GET(PF_WIN_YPOS_MASK, pos),
643 REG_FIELD_GET(PF_WIN_XSIZE_MASK, size),
644 REG_FIELD_GET(PF_WIN_YSIZE_MASK, size));
645
646 /*
647 * We currently do not free assignments of panel fitters on
648 * ivb/hsw (since we don't use the higher upscaling modes which
649 * differentiates them) so just WARN about this case for now.
650 */
651 drm_WARN_ON(display->drm, pipe != crtc->pipe);
652}
653
654void i9xx_pfit_enable(const struct intel_crtc_state *crtc_state)
655{
656 struct intel_display *display = to_intel_display(crtc_state);
657 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
658
659 if (!crtc_state->gmch_pfit.control)
660 return;
661
662 /*
663 * The panel fitter should only be adjusted whilst the pipe is disabled,
664 * according to register description and PRM.
665 */
666 drm_WARN_ON(display->drm,
667 intel_de_read(display, PFIT_CONTROL(display)) & PFIT_ENABLE);
668 assert_transcoder_disabled(display, crtc_state->cpu_transcoder);
669
670 intel_de_write(display, PFIT_PGM_RATIOS(display),
671 val: crtc_state->gmch_pfit.pgm_ratios);
672 intel_de_write(display, PFIT_CONTROL(display),
673 val: crtc_state->gmch_pfit.control);
674
675 /*
676 * Border color in case we don't scale up to the full screen. Black by
677 * default, change to something else for debugging.
678 */
679 intel_de_write(display, BCLRPAT(display, crtc->pipe), val: 0);
680}
681
682void i9xx_pfit_disable(const struct intel_crtc_state *old_crtc_state)
683{
684 struct intel_display *display = to_intel_display(old_crtc_state);
685
686 if (!old_crtc_state->gmch_pfit.control)
687 return;
688
689 assert_transcoder_disabled(display, old_crtc_state->cpu_transcoder);
690
691 drm_dbg_kms(display->drm, "disabling pfit, current: 0x%08x\n",
692 intel_de_read(display, PFIT_CONTROL(display)));
693 intel_de_write(display, PFIT_CONTROL(display), val: 0);
694}
695
696static bool i9xx_has_pfit(struct intel_display *display)
697{
698 if (display->platform.i830)
699 return false;
700
701 return DISPLAY_VER(display) >= 4 ||
702 display->platform.pineview || display->platform.mobile;
703}
704
705void i9xx_pfit_get_config(struct intel_crtc_state *crtc_state)
706{
707 struct intel_display *display = to_intel_display(crtc_state);
708 struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
709 enum pipe pipe;
710 u32 tmp;
711
712 if (!i9xx_has_pfit(display))
713 return;
714
715 tmp = intel_de_read(display, PFIT_CONTROL(display));
716 if (!(tmp & PFIT_ENABLE))
717 return;
718
719 /* Check whether the pfit is attached to our pipe. */
720 if (DISPLAY_VER(display) >= 4)
721 pipe = REG_FIELD_GET(PFIT_PIPE_MASK, tmp);
722 else
723 pipe = PIPE_B;
724
725 if (pipe != crtc->pipe)
726 return;
727
728 crtc_state->gmch_pfit.control = tmp;
729 crtc_state->gmch_pfit.pgm_ratios =
730 intel_de_read(display, PFIT_PGM_RATIOS(display));
731}
732