1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright © 2024 Intel Corporation
4 */
5
6 #include "i915_reg.h"
7 #include "i915_utils.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
intel_pch_pfit_check_dst_window(const struct intel_crtc_state * crtc_state)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
intel_pch_pfit_check_src_size(const struct intel_crtc_state * crtc_state)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
intel_pch_pfit_check_scaling(const struct intel_crtc_state * crtc_state)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
intel_pch_pfit_check_timings(const struct intel_crtc_state * crtc_state)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
intel_pch_pfit_check_cloning(const struct intel_crtc_state * crtc_state)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 */
pch_panel_fitting(struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)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
centre_horizontally(struct drm_display_mode * adjusted_mode,int width)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
centre_vertically(struct drm_display_mode * adjusted_mode,int height)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
panel_fitter_scaling(u32 source,u32 target)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
i965_scale_aspect(struct intel_crtc_state * crtc_state,u32 * pfit_control)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
i9xx_scale_aspect(struct intel_crtc_state * crtc_state,u32 * pfit_control,u32 * pfit_pgm_ratios,u32 * border)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
intel_gmch_pfit_check_timings(const struct intel_crtc_state * crtc_state)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
gmch_panel_fitting(struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)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
intel_panel_fitting(struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)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