xref: /openbsd-src/sys/dev/pci/drm/i915/display/intel_panel.c (revision f005ef32267c16bdb134f0e9fa4477dbe07c263a)
1c349dbc7Sjsg /*
2c349dbc7Sjsg  * Copyright © 2006-2010 Intel Corporation
3c349dbc7Sjsg  * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
4c349dbc7Sjsg  *
5c349dbc7Sjsg  * Permission is hereby granted, free of charge, to any person obtaining a
6c349dbc7Sjsg  * copy of this software and associated documentation files (the "Software"),
7c349dbc7Sjsg  * to deal in the Software without restriction, including without limitation
8c349dbc7Sjsg  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9c349dbc7Sjsg  * and/or sell copies of the Software, and to permit persons to whom the
10c349dbc7Sjsg  * Software is furnished to do so, subject to the following conditions:
11c349dbc7Sjsg  *
12c349dbc7Sjsg  * The above copyright notice and this permission notice (including the next
13c349dbc7Sjsg  * paragraph) shall be included in all copies or substantial portions of the
14c349dbc7Sjsg  * Software.
15c349dbc7Sjsg  *
16c349dbc7Sjsg  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17c349dbc7Sjsg  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18c349dbc7Sjsg  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19c349dbc7Sjsg  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20c349dbc7Sjsg  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21c349dbc7Sjsg  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22c349dbc7Sjsg  * DEALINGS IN THE SOFTWARE.
23c349dbc7Sjsg  *
24c349dbc7Sjsg  * Authors:
25c349dbc7Sjsg  *	Eric Anholt <eric@anholt.net>
26c349dbc7Sjsg  *      Dave Airlie <airlied@linux.ie>
27c349dbc7Sjsg  *      Jesse Barnes <jesse.barnes@intel.com>
28c349dbc7Sjsg  *      Chris Wilson <chris@chris-wilson.co.uk>
29c349dbc7Sjsg  */
30c349dbc7Sjsg 
31c349dbc7Sjsg #include <linux/kernel.h>
32c349dbc7Sjsg #include <linux/pwm.h>
33c349dbc7Sjsg 
34*f005ef32Sjsg #include <drm/drm_edid.h>
35*f005ef32Sjsg 
36*f005ef32Sjsg #include "i915_reg.h"
37b35a56d4Sjsg #include "intel_backlight.h"
38c349dbc7Sjsg #include "intel_connector.h"
395ca02815Sjsg #include "intel_de.h"
40c349dbc7Sjsg #include "intel_display_types.h"
411bb76ff1Sjsg #include "intel_drrs.h"
42*f005ef32Sjsg #include "intel_lvds_regs.h"
43c349dbc7Sjsg #include "intel_panel.h"
441bb76ff1Sjsg #include "intel_quirks.h"
45*f005ef32Sjsg #include "intel_vrr.h"
46c349dbc7Sjsg 
intel_panel_use_ssc(struct drm_i915_private * i915)471bb76ff1Sjsg bool intel_panel_use_ssc(struct drm_i915_private *i915)
48c349dbc7Sjsg {
491bb76ff1Sjsg 	if (i915->params.panel_use_ssc >= 0)
501bb76ff1Sjsg 		return i915->params.panel_use_ssc != 0;
511bb76ff1Sjsg 	return i915->display.vbt.lvds_use_ssc &&
521bb76ff1Sjsg 		!intel_has_quirk(i915, QUIRK_LVDS_SSC_DISABLE);
53c349dbc7Sjsg }
54c349dbc7Sjsg 
551bb76ff1Sjsg const struct drm_display_mode *
intel_panel_preferred_fixed_mode(struct intel_connector * connector)561bb76ff1Sjsg intel_panel_preferred_fixed_mode(struct intel_connector *connector)
57c349dbc7Sjsg {
581bb76ff1Sjsg 	return list_first_entry_or_null(&connector->panel.fixed_modes,
591bb76ff1Sjsg 					struct drm_display_mode, head);
601bb76ff1Sjsg }
611bb76ff1Sjsg 
is_in_vrr_range(struct intel_connector * connector,int vrefresh)62*f005ef32Sjsg static bool is_in_vrr_range(struct intel_connector *connector, int vrefresh)
63*f005ef32Sjsg {
64*f005ef32Sjsg 	const struct drm_display_info *info = &connector->base.display_info;
65*f005ef32Sjsg 
66*f005ef32Sjsg 	return intel_vrr_is_capable(connector) &&
67*f005ef32Sjsg 		vrefresh >= info->monitor_range.min_vfreq &&
68*f005ef32Sjsg 		vrefresh <= info->monitor_range.max_vfreq;
69*f005ef32Sjsg }
70*f005ef32Sjsg 
is_best_fixed_mode(struct intel_connector * connector,int vrefresh,int fixed_mode_vrefresh,const struct drm_display_mode * best_mode)71*f005ef32Sjsg static bool is_best_fixed_mode(struct intel_connector *connector,
72*f005ef32Sjsg 			       int vrefresh, int fixed_mode_vrefresh,
73*f005ef32Sjsg 			       const struct drm_display_mode *best_mode)
74*f005ef32Sjsg {
75*f005ef32Sjsg 	/* we want to always return something */
76*f005ef32Sjsg 	if (!best_mode)
77*f005ef32Sjsg 		return true;
78*f005ef32Sjsg 
79*f005ef32Sjsg 	/*
80*f005ef32Sjsg 	 * With VRR always pick a mode with equal/higher than requested
81*f005ef32Sjsg 	 * vrefresh, which we can then reduce to match the requested
82*f005ef32Sjsg 	 * vrefresh by extending the vblank length.
83*f005ef32Sjsg 	 */
84*f005ef32Sjsg 	if (is_in_vrr_range(connector, vrefresh) &&
85*f005ef32Sjsg 	    is_in_vrr_range(connector, fixed_mode_vrefresh) &&
86*f005ef32Sjsg 	    fixed_mode_vrefresh < vrefresh)
87*f005ef32Sjsg 		return false;
88*f005ef32Sjsg 
89*f005ef32Sjsg 	/* pick the fixed_mode that is closest in terms of vrefresh */
90*f005ef32Sjsg 	return abs(fixed_mode_vrefresh - vrefresh) <
91*f005ef32Sjsg 		abs(drm_mode_vrefresh(best_mode) - vrefresh);
92*f005ef32Sjsg }
93*f005ef32Sjsg 
941bb76ff1Sjsg const struct drm_display_mode *
intel_panel_fixed_mode(struct intel_connector * connector,const struct drm_display_mode * mode)951bb76ff1Sjsg intel_panel_fixed_mode(struct intel_connector *connector,
961bb76ff1Sjsg 		       const struct drm_display_mode *mode)
971bb76ff1Sjsg {
981bb76ff1Sjsg 	const struct drm_display_mode *fixed_mode, *best_mode = NULL;
991bb76ff1Sjsg 	int vrefresh = drm_mode_vrefresh(mode);
1001bb76ff1Sjsg 
1011bb76ff1Sjsg 	list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) {
102*f005ef32Sjsg 		int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
103*f005ef32Sjsg 
104*f005ef32Sjsg 		if (is_best_fixed_mode(connector, vrefresh,
105*f005ef32Sjsg 				       fixed_mode_vrefresh, best_mode))
1061bb76ff1Sjsg 			best_mode = fixed_mode;
1071bb76ff1Sjsg 	}
1081bb76ff1Sjsg 
1091bb76ff1Sjsg 	return best_mode;
1101bb76ff1Sjsg }
1111bb76ff1Sjsg 
is_alt_drrs_mode(const struct drm_display_mode * mode,const struct drm_display_mode * preferred_mode)1121bb76ff1Sjsg static bool is_alt_drrs_mode(const struct drm_display_mode *mode,
1131bb76ff1Sjsg 			     const struct drm_display_mode *preferred_mode)
1141bb76ff1Sjsg {
1151bb76ff1Sjsg 	return drm_mode_match(mode, preferred_mode,
116c349dbc7Sjsg 			      DRM_MODE_MATCH_TIMINGS |
117c349dbc7Sjsg 			      DRM_MODE_MATCH_FLAGS |
118c349dbc7Sjsg 			      DRM_MODE_MATCH_3D_FLAGS) &&
1191bb76ff1Sjsg 		mode->clock != preferred_mode->clock;
120c349dbc7Sjsg }
121c349dbc7Sjsg 
is_alt_fixed_mode(const struct drm_display_mode * mode,const struct drm_display_mode * preferred_mode)1221bb76ff1Sjsg static bool is_alt_fixed_mode(const struct drm_display_mode *mode,
1231bb76ff1Sjsg 			      const struct drm_display_mode *preferred_mode)
1241bb76ff1Sjsg {
125d6420040Sjsg 	u32 sync_flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC |
126d6420040Sjsg 		DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC;
127d6420040Sjsg 
128d6420040Sjsg 	return (mode->flags & ~sync_flags) == (preferred_mode->flags & ~sync_flags) &&
1291bb76ff1Sjsg 		mode->hdisplay == preferred_mode->hdisplay &&
1301bb76ff1Sjsg 		mode->vdisplay == preferred_mode->vdisplay;
1311bb76ff1Sjsg }
1321bb76ff1Sjsg 
1331bb76ff1Sjsg const struct drm_display_mode *
intel_panel_downclock_mode(struct intel_connector * connector,const struct drm_display_mode * adjusted_mode)1341bb76ff1Sjsg intel_panel_downclock_mode(struct intel_connector *connector,
1351bb76ff1Sjsg 			   const struct drm_display_mode *adjusted_mode)
1361bb76ff1Sjsg {
1371bb76ff1Sjsg 	const struct drm_display_mode *fixed_mode, *best_mode = NULL;
1381bb76ff1Sjsg 	int min_vrefresh = connector->panel.vbt.seamless_drrs_min_refresh_rate;
1391bb76ff1Sjsg 	int max_vrefresh = drm_mode_vrefresh(adjusted_mode);
1401bb76ff1Sjsg 
1411bb76ff1Sjsg 	/* pick the fixed_mode with the lowest refresh rate */
1421bb76ff1Sjsg 	list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) {
1431bb76ff1Sjsg 		int vrefresh = drm_mode_vrefresh(fixed_mode);
1441bb76ff1Sjsg 
1451bb76ff1Sjsg 		if (is_alt_drrs_mode(fixed_mode, adjusted_mode) &&
1461bb76ff1Sjsg 		    vrefresh >= min_vrefresh && vrefresh < max_vrefresh) {
1471bb76ff1Sjsg 			max_vrefresh = vrefresh;
1481bb76ff1Sjsg 			best_mode = fixed_mode;
1491bb76ff1Sjsg 		}
1501bb76ff1Sjsg 	}
1511bb76ff1Sjsg 
1521bb76ff1Sjsg 	return best_mode;
1531bb76ff1Sjsg }
1541bb76ff1Sjsg 
1551bb76ff1Sjsg const struct drm_display_mode *
intel_panel_highest_mode(struct intel_connector * connector,const struct drm_display_mode * adjusted_mode)1561bb76ff1Sjsg intel_panel_highest_mode(struct intel_connector *connector,
1571bb76ff1Sjsg 			 const struct drm_display_mode *adjusted_mode)
1581bb76ff1Sjsg {
1591bb76ff1Sjsg 	const struct drm_display_mode *fixed_mode, *best_mode = adjusted_mode;
1601bb76ff1Sjsg 
1611bb76ff1Sjsg 	/* pick the fixed_mode that has the highest clock */
1621bb76ff1Sjsg 	list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) {
1631bb76ff1Sjsg 		if (fixed_mode->clock > best_mode->clock)
1641bb76ff1Sjsg 			best_mode = fixed_mode;
1651bb76ff1Sjsg 	}
1661bb76ff1Sjsg 
1671bb76ff1Sjsg 	return best_mode;
1681bb76ff1Sjsg }
1691bb76ff1Sjsg 
intel_panel_get_modes(struct intel_connector * connector)1701bb76ff1Sjsg int intel_panel_get_modes(struct intel_connector *connector)
1711bb76ff1Sjsg {
1721bb76ff1Sjsg 	const struct drm_display_mode *fixed_mode;
1731bb76ff1Sjsg 	int num_modes = 0;
1741bb76ff1Sjsg 
1751bb76ff1Sjsg 	list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) {
1761bb76ff1Sjsg 		struct drm_display_mode *mode;
1771bb76ff1Sjsg 
1781bb76ff1Sjsg 		mode = drm_mode_duplicate(connector->base.dev, fixed_mode);
1791bb76ff1Sjsg 		if (mode) {
1801bb76ff1Sjsg 			drm_mode_probed_add(&connector->base, mode);
1811bb76ff1Sjsg 			num_modes++;
1821bb76ff1Sjsg 		}
1831bb76ff1Sjsg 	}
1841bb76ff1Sjsg 
1851bb76ff1Sjsg 	return num_modes;
1861bb76ff1Sjsg }
1871bb76ff1Sjsg 
has_drrs_modes(struct intel_connector * connector)188*f005ef32Sjsg static bool has_drrs_modes(struct intel_connector *connector)
189*f005ef32Sjsg {
190*f005ef32Sjsg 	const struct drm_display_mode *mode1;
191*f005ef32Sjsg 
192*f005ef32Sjsg 	list_for_each_entry(mode1, &connector->panel.fixed_modes, head) {
193*f005ef32Sjsg 		const struct drm_display_mode *mode2 = mode1;
194*f005ef32Sjsg 
195*f005ef32Sjsg 		list_for_each_entry_continue(mode2, &connector->panel.fixed_modes, head) {
196*f005ef32Sjsg 			if (is_alt_drrs_mode(mode1, mode2))
197*f005ef32Sjsg 				return true;
198*f005ef32Sjsg 		}
199*f005ef32Sjsg 	}
200*f005ef32Sjsg 
201*f005ef32Sjsg 	return false;
202*f005ef32Sjsg }
203*f005ef32Sjsg 
intel_panel_drrs_type(struct intel_connector * connector)2041bb76ff1Sjsg enum drrs_type intel_panel_drrs_type(struct intel_connector *connector)
2051bb76ff1Sjsg {
2061bb76ff1Sjsg 	return connector->panel.vbt.drrs_type;
2071bb76ff1Sjsg }
2081bb76ff1Sjsg 
intel_panel_compute_config(struct intel_connector * connector,struct drm_display_mode * adjusted_mode)2091bb76ff1Sjsg int intel_panel_compute_config(struct intel_connector *connector,
2101bb76ff1Sjsg 			       struct drm_display_mode *adjusted_mode)
2111bb76ff1Sjsg {
2121bb76ff1Sjsg 	const struct drm_display_mode *fixed_mode =
2131bb76ff1Sjsg 		intel_panel_fixed_mode(connector, adjusted_mode);
214*f005ef32Sjsg 	int vrefresh, fixed_mode_vrefresh;
215*f005ef32Sjsg 	bool is_vrr;
2161bb76ff1Sjsg 
2171bb76ff1Sjsg 	if (!fixed_mode)
2181bb76ff1Sjsg 		return 0;
2191bb76ff1Sjsg 
220*f005ef32Sjsg 	vrefresh = drm_mode_vrefresh(adjusted_mode);
221*f005ef32Sjsg 	fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
222*f005ef32Sjsg 
223*f005ef32Sjsg 	/*
224*f005ef32Sjsg 	 * Assume that we shouldn't muck about with the
225*f005ef32Sjsg 	 * timings if they don't land in the VRR range.
226*f005ef32Sjsg 	 */
227*f005ef32Sjsg 	is_vrr = is_in_vrr_range(connector, vrefresh) &&
228*f005ef32Sjsg 		is_in_vrr_range(connector, fixed_mode_vrefresh);
229*f005ef32Sjsg 
230*f005ef32Sjsg 	if (!is_vrr) {
2311bb76ff1Sjsg 		/*
2321bb76ff1Sjsg 		 * We don't want to lie too much to the user about the refresh
2331bb76ff1Sjsg 		 * rate they're going to get. But we have to allow a bit of latitude
2341bb76ff1Sjsg 		 * for Xorg since it likes to automagically cook up modes with slightly
2351bb76ff1Sjsg 		 * off refresh rates.
2361bb76ff1Sjsg 		 */
237*f005ef32Sjsg 		if (abs(vrefresh - fixed_mode_vrefresh) > 1) {
2381bb76ff1Sjsg 			drm_dbg_kms(connector->base.dev,
2391bb76ff1Sjsg 				    "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n",
2401bb76ff1Sjsg 				    connector->base.base.id, connector->base.name,
241*f005ef32Sjsg 				    vrefresh, fixed_mode_vrefresh);
2421bb76ff1Sjsg 
2431bb76ff1Sjsg 			return -EINVAL;
2441bb76ff1Sjsg 		}
245*f005ef32Sjsg 	}
2461bb76ff1Sjsg 
2471bb76ff1Sjsg 	drm_mode_copy(adjusted_mode, fixed_mode);
2481bb76ff1Sjsg 
249*f005ef32Sjsg 	if (is_vrr && fixed_mode_vrefresh != vrefresh)
250*f005ef32Sjsg 		adjusted_mode->vtotal =
251*f005ef32Sjsg 			DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000,
252*f005ef32Sjsg 					  adjusted_mode->htotal * vrefresh);
253*f005ef32Sjsg 
2541bb76ff1Sjsg 	drm_mode_set_crtcinfo(adjusted_mode, 0);
2551bb76ff1Sjsg 
2561bb76ff1Sjsg 	return 0;
2571bb76ff1Sjsg }
2581bb76ff1Sjsg 
intel_panel_add_edid_alt_fixed_modes(struct intel_connector * connector)2591bb76ff1Sjsg static void intel_panel_add_edid_alt_fixed_modes(struct intel_connector *connector)
260c349dbc7Sjsg {
261c349dbc7Sjsg 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
2621bb76ff1Sjsg 	const struct drm_display_mode *preferred_mode =
2631bb76ff1Sjsg 		intel_panel_preferred_fixed_mode(connector);
2641bb76ff1Sjsg 	struct drm_display_mode *mode, *next;
265c349dbc7Sjsg 
2661bb76ff1Sjsg 	list_for_each_entry_safe(mode, next, &connector->base.probed_modes, head) {
2671bb76ff1Sjsg 		if (!is_alt_fixed_mode(mode, preferred_mode))
268c349dbc7Sjsg 			continue;
269c349dbc7Sjsg 
270c349dbc7Sjsg 		drm_dbg_kms(&dev_priv->drm,
2711bb76ff1Sjsg 			    "[CONNECTOR:%d:%s] using alternate EDID fixed mode: " DRM_MODE_FMT "\n",
2721bb76ff1Sjsg 			    connector->base.base.id, connector->base.name,
2731bb76ff1Sjsg 			    DRM_MODE_ARG(mode));
274c349dbc7Sjsg 
2751bb76ff1Sjsg 		list_move_tail(&mode->head, &connector->panel.fixed_modes);
2761bb76ff1Sjsg 	}
277c349dbc7Sjsg }
278c349dbc7Sjsg 
intel_panel_add_edid_preferred_mode(struct intel_connector * connector)2791bb76ff1Sjsg static void intel_panel_add_edid_preferred_mode(struct intel_connector *connector)
280c349dbc7Sjsg {
281c349dbc7Sjsg 	struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
2821bb76ff1Sjsg 	struct drm_display_mode *scan, *fixed_mode = NULL;
283c349dbc7Sjsg 
2841bb76ff1Sjsg 	if (list_empty(&connector->base.probed_modes))
2851bb76ff1Sjsg 		return;
286c349dbc7Sjsg 
2871bb76ff1Sjsg 	/* make sure the preferred mode is first */
2881bb76ff1Sjsg 	list_for_each_entry(scan, &connector->base.probed_modes, head) {
2891bb76ff1Sjsg 		if (scan->type & DRM_MODE_TYPE_PREFERRED) {
2901bb76ff1Sjsg 			fixed_mode = scan;
2911bb76ff1Sjsg 			break;
2921bb76ff1Sjsg 		}
2931bb76ff1Sjsg 	}
2941bb76ff1Sjsg 
295c349dbc7Sjsg 	if (!fixed_mode)
2961bb76ff1Sjsg 		fixed_mode = list_first_entry(&connector->base.probed_modes,
2971bb76ff1Sjsg 					      typeof(*fixed_mode), head);
2981bb76ff1Sjsg 
2991bb76ff1Sjsg 	drm_dbg_kms(&dev_priv->drm,
3001bb76ff1Sjsg 		    "[CONNECTOR:%d:%s] using %s EDID fixed mode: " DRM_MODE_FMT "\n",
3011bb76ff1Sjsg 		    connector->base.base.id, connector->base.name,
3021bb76ff1Sjsg 		    fixed_mode->type & DRM_MODE_TYPE_PREFERRED ? "preferred" : "first",
3031bb76ff1Sjsg 		    DRM_MODE_ARG(fixed_mode));
304c349dbc7Sjsg 
305c349dbc7Sjsg 	fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
306c349dbc7Sjsg 
3071bb76ff1Sjsg 	list_move_tail(&fixed_mode->head, &connector->panel.fixed_modes);
3081bb76ff1Sjsg }
3091bb76ff1Sjsg 
intel_panel_destroy_probed_modes(struct intel_connector * connector)3101bb76ff1Sjsg static void intel_panel_destroy_probed_modes(struct intel_connector *connector)
3111bb76ff1Sjsg {
3121bb76ff1Sjsg 	struct drm_i915_private *i915 = to_i915(connector->base.dev);
3131bb76ff1Sjsg 	struct drm_display_mode *mode, *next;
3141bb76ff1Sjsg 
3151bb76ff1Sjsg 	list_for_each_entry_safe(mode, next, &connector->base.probed_modes, head) {
3161bb76ff1Sjsg 		drm_dbg_kms(&i915->drm,
3171bb76ff1Sjsg 			    "[CONNECTOR:%d:%s] not using EDID mode: " DRM_MODE_FMT "\n",
3181bb76ff1Sjsg 			    connector->base.base.id, connector->base.name,
3191bb76ff1Sjsg 			    DRM_MODE_ARG(mode));
3201bb76ff1Sjsg 		list_del(&mode->head);
3211bb76ff1Sjsg 		drm_mode_destroy(&i915->drm, mode);
3221bb76ff1Sjsg 	}
3231bb76ff1Sjsg }
3241bb76ff1Sjsg 
intel_panel_add_edid_fixed_modes(struct intel_connector * connector,bool use_alt_fixed_modes)3251bb76ff1Sjsg void intel_panel_add_edid_fixed_modes(struct intel_connector *connector,
3261bb76ff1Sjsg 				      bool use_alt_fixed_modes)
3271bb76ff1Sjsg {
3281bb76ff1Sjsg 	intel_panel_add_edid_preferred_mode(connector);
3291bb76ff1Sjsg 	if (intel_panel_preferred_fixed_mode(connector) && use_alt_fixed_modes)
3301bb76ff1Sjsg 		intel_panel_add_edid_alt_fixed_modes(connector);
3311bb76ff1Sjsg 	intel_panel_destroy_probed_modes(connector);
3321bb76ff1Sjsg }
3331bb76ff1Sjsg 
intel_panel_add_fixed_mode(struct intel_connector * connector,struct drm_display_mode * fixed_mode,const char * type)3341bb76ff1Sjsg static void intel_panel_add_fixed_mode(struct intel_connector *connector,
3351bb76ff1Sjsg 				       struct drm_display_mode *fixed_mode,
3361bb76ff1Sjsg 				       const char *type)
3371bb76ff1Sjsg {
3381bb76ff1Sjsg 	struct drm_i915_private *i915 = to_i915(connector->base.dev);
3391bb76ff1Sjsg 	struct drm_display_info *info = &connector->base.display_info;
3401bb76ff1Sjsg 
3411bb76ff1Sjsg 	if (!fixed_mode)
3421bb76ff1Sjsg 		return;
3431bb76ff1Sjsg 
3441bb76ff1Sjsg 	fixed_mode->type |= DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
345c349dbc7Sjsg 
346c349dbc7Sjsg 	info->width_mm = fixed_mode->width_mm;
347c349dbc7Sjsg 	info->height_mm = fixed_mode->height_mm;
348c349dbc7Sjsg 
3491bb76ff1Sjsg 	drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] using %s fixed mode: " DRM_MODE_FMT "\n",
3501bb76ff1Sjsg 		    connector->base.base.id, connector->base.name, type,
3511bb76ff1Sjsg 		    DRM_MODE_ARG(fixed_mode));
3521bb76ff1Sjsg 
3531bb76ff1Sjsg 	list_add_tail(&fixed_mode->head, &connector->panel.fixed_modes);
3541bb76ff1Sjsg }
3551bb76ff1Sjsg 
intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector * connector)3561bb76ff1Sjsg void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector)
3571bb76ff1Sjsg {
3581bb76ff1Sjsg 	struct drm_i915_private *i915 = to_i915(connector->base.dev);
3591bb76ff1Sjsg 	const struct drm_display_mode *mode;
3601bb76ff1Sjsg 
3611bb76ff1Sjsg 	mode = connector->panel.vbt.lfp_lvds_vbt_mode;
3621bb76ff1Sjsg 	if (!mode)
3631bb76ff1Sjsg 		return;
3641bb76ff1Sjsg 
3651bb76ff1Sjsg 	intel_panel_add_fixed_mode(connector,
3661bb76ff1Sjsg 				   drm_mode_duplicate(&i915->drm, mode),
3671bb76ff1Sjsg 				   "VBT LFP");
3681bb76ff1Sjsg }
3691bb76ff1Sjsg 
intel_panel_add_vbt_sdvo_fixed_mode(struct intel_connector * connector)3701bb76ff1Sjsg void intel_panel_add_vbt_sdvo_fixed_mode(struct intel_connector *connector)
3711bb76ff1Sjsg {
3721bb76ff1Sjsg 	struct drm_i915_private *i915 = to_i915(connector->base.dev);
3731bb76ff1Sjsg 	const struct drm_display_mode *mode;
3741bb76ff1Sjsg 
3751bb76ff1Sjsg 	mode = connector->panel.vbt.sdvo_lvds_vbt_mode;
3761bb76ff1Sjsg 	if (!mode)
3771bb76ff1Sjsg 		return;
3781bb76ff1Sjsg 
3791bb76ff1Sjsg 	intel_panel_add_fixed_mode(connector,
3801bb76ff1Sjsg 				   drm_mode_duplicate(&i915->drm, mode),
3811bb76ff1Sjsg 				   "VBT SDVO");
3821bb76ff1Sjsg }
3831bb76ff1Sjsg 
intel_panel_add_encoder_fixed_mode(struct intel_connector * connector,struct intel_encoder * encoder)3841bb76ff1Sjsg void intel_panel_add_encoder_fixed_mode(struct intel_connector *connector,
3851bb76ff1Sjsg 					struct intel_encoder *encoder)
3861bb76ff1Sjsg {
3871bb76ff1Sjsg 	intel_panel_add_fixed_mode(connector,
3881bb76ff1Sjsg 				   intel_encoder_current_mode(encoder),
3891bb76ff1Sjsg 				   "current (BIOS)");
390c349dbc7Sjsg }
391c349dbc7Sjsg 
392c349dbc7Sjsg /* 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)3931bb76ff1Sjsg static int pch_panel_fitting(struct intel_crtc_state *crtc_state,
394ad8b1aafSjsg 			     const struct drm_connector_state *conn_state)
395c349dbc7Sjsg {
396ad8b1aafSjsg 	const struct drm_display_mode *adjusted_mode =
397ad8b1aafSjsg 		&crtc_state->hw.adjusted_mode;
3981bb76ff1Sjsg 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
3991bb76ff1Sjsg 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
400ad8b1aafSjsg 	int x, y, width, height;
401c349dbc7Sjsg 
402c349dbc7Sjsg 	/* Native modes don't need fitting */
4031bb76ff1Sjsg 	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
4041bb76ff1Sjsg 	    adjusted_mode->crtc_vdisplay == pipe_src_h &&
405ad8b1aafSjsg 	    crtc_state->output_format != INTEL_OUTPUT_FORMAT_YCBCR420)
406ad8b1aafSjsg 		return 0;
407c349dbc7Sjsg 
408ad8b1aafSjsg 	switch (conn_state->scaling_mode) {
409c349dbc7Sjsg 	case DRM_MODE_SCALE_CENTER:
4101bb76ff1Sjsg 		width = pipe_src_w;
4111bb76ff1Sjsg 		height = pipe_src_h;
412c349dbc7Sjsg 		x = (adjusted_mode->crtc_hdisplay - width + 1)/2;
413c349dbc7Sjsg 		y = (adjusted_mode->crtc_vdisplay - height + 1)/2;
414c349dbc7Sjsg 		break;
415c349dbc7Sjsg 
416c349dbc7Sjsg 	case DRM_MODE_SCALE_ASPECT:
417c349dbc7Sjsg 		/* Scale but preserve the aspect ratio */
418c349dbc7Sjsg 		{
4191bb76ff1Sjsg 			u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
4201bb76ff1Sjsg 			u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
421c349dbc7Sjsg 			if (scaled_width > scaled_height) { /* pillar */
4221bb76ff1Sjsg 				width = scaled_height / pipe_src_h;
423c349dbc7Sjsg 				if (width & 1)
424c349dbc7Sjsg 					width++;
425c349dbc7Sjsg 				x = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
426c349dbc7Sjsg 				y = 0;
427c349dbc7Sjsg 				height = adjusted_mode->crtc_vdisplay;
428c349dbc7Sjsg 			} else if (scaled_width < scaled_height) { /* letter */
4291bb76ff1Sjsg 				height = scaled_width / pipe_src_w;
430c349dbc7Sjsg 				if (height & 1)
431c349dbc7Sjsg 				    height++;
432c349dbc7Sjsg 				y = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
433c349dbc7Sjsg 				x = 0;
434c349dbc7Sjsg 				width = adjusted_mode->crtc_hdisplay;
435c349dbc7Sjsg 			} else {
436c349dbc7Sjsg 				x = y = 0;
437c349dbc7Sjsg 				width = adjusted_mode->crtc_hdisplay;
438c349dbc7Sjsg 				height = adjusted_mode->crtc_vdisplay;
439c349dbc7Sjsg 			}
440c349dbc7Sjsg 		}
441c349dbc7Sjsg 		break;
442c349dbc7Sjsg 
443ad8b1aafSjsg 	case DRM_MODE_SCALE_NONE:
4441bb76ff1Sjsg 		WARN_ON(adjusted_mode->crtc_hdisplay != pipe_src_w);
4451bb76ff1Sjsg 		WARN_ON(adjusted_mode->crtc_vdisplay != pipe_src_h);
446ad8b1aafSjsg 		fallthrough;
447c349dbc7Sjsg 	case DRM_MODE_SCALE_FULLSCREEN:
448c349dbc7Sjsg 		x = y = 0;
449c349dbc7Sjsg 		width = adjusted_mode->crtc_hdisplay;
450c349dbc7Sjsg 		height = adjusted_mode->crtc_vdisplay;
451c349dbc7Sjsg 		break;
452c349dbc7Sjsg 
453c349dbc7Sjsg 	default:
454ad8b1aafSjsg 		MISSING_CASE(conn_state->scaling_mode);
455ad8b1aafSjsg 		return -EINVAL;
456c349dbc7Sjsg 	}
457c349dbc7Sjsg 
458ad8b1aafSjsg 	drm_rect_init(&crtc_state->pch_pfit.dst,
459ad8b1aafSjsg 		      x, y, width, height);
460ad8b1aafSjsg 	crtc_state->pch_pfit.enabled = true;
461ad8b1aafSjsg 
462ad8b1aafSjsg 	return 0;
463c349dbc7Sjsg }
464c349dbc7Sjsg 
465c349dbc7Sjsg static void
centre_horizontally(struct drm_display_mode * adjusted_mode,int width)466c349dbc7Sjsg centre_horizontally(struct drm_display_mode *adjusted_mode,
467c349dbc7Sjsg 		    int width)
468c349dbc7Sjsg {
469c349dbc7Sjsg 	u32 border, sync_pos, blank_width, sync_width;
470c349dbc7Sjsg 
471c349dbc7Sjsg 	/* keep the hsync and hblank widths constant */
472c349dbc7Sjsg 	sync_width = adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start;
473c349dbc7Sjsg 	blank_width = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
474c349dbc7Sjsg 	sync_pos = (blank_width - sync_width + 1) / 2;
475c349dbc7Sjsg 
476c349dbc7Sjsg 	border = (adjusted_mode->crtc_hdisplay - width + 1) / 2;
477c349dbc7Sjsg 	border += border & 1; /* make the border even */
478c349dbc7Sjsg 
479c349dbc7Sjsg 	adjusted_mode->crtc_hdisplay = width;
480c349dbc7Sjsg 	adjusted_mode->crtc_hblank_start = width + border;
481c349dbc7Sjsg 	adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_hblank_start + blank_width;
482c349dbc7Sjsg 
483c349dbc7Sjsg 	adjusted_mode->crtc_hsync_start = adjusted_mode->crtc_hblank_start + sync_pos;
484c349dbc7Sjsg 	adjusted_mode->crtc_hsync_end = adjusted_mode->crtc_hsync_start + sync_width;
485c349dbc7Sjsg }
486c349dbc7Sjsg 
487c349dbc7Sjsg static void
centre_vertically(struct drm_display_mode * adjusted_mode,int height)488c349dbc7Sjsg centre_vertically(struct drm_display_mode *adjusted_mode,
489c349dbc7Sjsg 		  int height)
490c349dbc7Sjsg {
491c349dbc7Sjsg 	u32 border, sync_pos, blank_width, sync_width;
492c349dbc7Sjsg 
493c349dbc7Sjsg 	/* keep the vsync and vblank widths constant */
494c349dbc7Sjsg 	sync_width = adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start;
495c349dbc7Sjsg 	blank_width = adjusted_mode->crtc_vblank_end - adjusted_mode->crtc_vblank_start;
496c349dbc7Sjsg 	sync_pos = (blank_width - sync_width + 1) / 2;
497c349dbc7Sjsg 
498c349dbc7Sjsg 	border = (adjusted_mode->crtc_vdisplay - height + 1) / 2;
499c349dbc7Sjsg 
500c349dbc7Sjsg 	adjusted_mode->crtc_vdisplay = height;
501c349dbc7Sjsg 	adjusted_mode->crtc_vblank_start = height + border;
502c349dbc7Sjsg 	adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vblank_start + blank_width;
503c349dbc7Sjsg 
504c349dbc7Sjsg 	adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vblank_start + sync_pos;
505c349dbc7Sjsg 	adjusted_mode->crtc_vsync_end = adjusted_mode->crtc_vsync_start + sync_width;
506c349dbc7Sjsg }
507c349dbc7Sjsg 
panel_fitter_scaling(u32 source,u32 target)508ad8b1aafSjsg static u32 panel_fitter_scaling(u32 source, u32 target)
509c349dbc7Sjsg {
510c349dbc7Sjsg 	/*
511c349dbc7Sjsg 	 * Floating point operation is not supported. So the FACTOR
512c349dbc7Sjsg 	 * is defined, which can avoid the floating point computation
513c349dbc7Sjsg 	 * when calculating the panel ratio.
514c349dbc7Sjsg 	 */
515c349dbc7Sjsg #define ACCURACY 12
516c349dbc7Sjsg #define FACTOR (1 << ACCURACY)
517c349dbc7Sjsg 	u32 ratio = source * FACTOR / target;
518c349dbc7Sjsg 	return (FACTOR * ratio + FACTOR/2) / FACTOR;
519c349dbc7Sjsg }
520c349dbc7Sjsg 
i965_scale_aspect(struct intel_crtc_state * crtc_state,u32 * pfit_control)521ad8b1aafSjsg static void i965_scale_aspect(struct intel_crtc_state *crtc_state,
522c349dbc7Sjsg 			      u32 *pfit_control)
523c349dbc7Sjsg {
524ad8b1aafSjsg 	const struct drm_display_mode *adjusted_mode =
525ad8b1aafSjsg 		&crtc_state->hw.adjusted_mode;
5261bb76ff1Sjsg 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
5271bb76ff1Sjsg 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
5281bb76ff1Sjsg 	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
5291bb76ff1Sjsg 	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
530c349dbc7Sjsg 
531c349dbc7Sjsg 	/* 965+ is easy, it does everything in hw */
532c349dbc7Sjsg 	if (scaled_width > scaled_height)
533c349dbc7Sjsg 		*pfit_control |= PFIT_ENABLE |
534c349dbc7Sjsg 			PFIT_SCALING_PILLAR;
535c349dbc7Sjsg 	else if (scaled_width < scaled_height)
536c349dbc7Sjsg 		*pfit_control |= PFIT_ENABLE |
537c349dbc7Sjsg 			PFIT_SCALING_LETTER;
5381bb76ff1Sjsg 	else if (adjusted_mode->crtc_hdisplay != pipe_src_w)
539c349dbc7Sjsg 		*pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
540c349dbc7Sjsg }
541c349dbc7Sjsg 
i9xx_scale_aspect(struct intel_crtc_state * crtc_state,u32 * pfit_control,u32 * pfit_pgm_ratios,u32 * border)542ad8b1aafSjsg static void i9xx_scale_aspect(struct intel_crtc_state *crtc_state,
543c349dbc7Sjsg 			      u32 *pfit_control, u32 *pfit_pgm_ratios,
544c349dbc7Sjsg 			      u32 *border)
545c349dbc7Sjsg {
546ad8b1aafSjsg 	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
5471bb76ff1Sjsg 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
5481bb76ff1Sjsg 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
5491bb76ff1Sjsg 	u32 scaled_width = adjusted_mode->crtc_hdisplay * pipe_src_h;
5501bb76ff1Sjsg 	u32 scaled_height = pipe_src_w * adjusted_mode->crtc_vdisplay;
551c349dbc7Sjsg 	u32 bits;
552c349dbc7Sjsg 
553c349dbc7Sjsg 	/*
554c349dbc7Sjsg 	 * For earlier chips we have to calculate the scaling
555c349dbc7Sjsg 	 * ratio by hand and program it into the
556c349dbc7Sjsg 	 * PFIT_PGM_RATIO register
557c349dbc7Sjsg 	 */
558c349dbc7Sjsg 	if (scaled_width > scaled_height) { /* pillar */
559c349dbc7Sjsg 		centre_horizontally(adjusted_mode,
5601bb76ff1Sjsg 				    scaled_height / pipe_src_h);
561c349dbc7Sjsg 
562c349dbc7Sjsg 		*border = LVDS_BORDER_ENABLE;
5631bb76ff1Sjsg 		if (pipe_src_h != adjusted_mode->crtc_vdisplay) {
5641bb76ff1Sjsg 			bits = panel_fitter_scaling(pipe_src_h,
565c349dbc7Sjsg 						    adjusted_mode->crtc_vdisplay);
566c349dbc7Sjsg 
567*f005ef32Sjsg 			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
568*f005ef32Sjsg 					     PFIT_VERT_SCALE(bits));
569c349dbc7Sjsg 			*pfit_control |= (PFIT_ENABLE |
570*f005ef32Sjsg 					  PFIT_VERT_INTERP_BILINEAR |
571*f005ef32Sjsg 					  PFIT_HORIZ_INTERP_BILINEAR);
572c349dbc7Sjsg 		}
573c349dbc7Sjsg 	} else if (scaled_width < scaled_height) { /* letter */
574c349dbc7Sjsg 		centre_vertically(adjusted_mode,
5751bb76ff1Sjsg 				  scaled_width / pipe_src_w);
576c349dbc7Sjsg 
577c349dbc7Sjsg 		*border = LVDS_BORDER_ENABLE;
5781bb76ff1Sjsg 		if (pipe_src_w != adjusted_mode->crtc_hdisplay) {
5791bb76ff1Sjsg 			bits = panel_fitter_scaling(pipe_src_w,
580c349dbc7Sjsg 						    adjusted_mode->crtc_hdisplay);
581c349dbc7Sjsg 
582*f005ef32Sjsg 			*pfit_pgm_ratios |= (PFIT_HORIZ_SCALE(bits) |
583*f005ef32Sjsg 					     PFIT_VERT_SCALE(bits));
584c349dbc7Sjsg 			*pfit_control |= (PFIT_ENABLE |
585*f005ef32Sjsg 					  PFIT_VERT_INTERP_BILINEAR |
586*f005ef32Sjsg 					  PFIT_HORIZ_INTERP_BILINEAR);
587c349dbc7Sjsg 		}
588c349dbc7Sjsg 	} else {
589c349dbc7Sjsg 		/* Aspects match, Let hw scale both directions */
590c349dbc7Sjsg 		*pfit_control |= (PFIT_ENABLE |
591*f005ef32Sjsg 				  PFIT_VERT_AUTO_SCALE |
592*f005ef32Sjsg 				  PFIT_HORIZ_AUTO_SCALE |
593*f005ef32Sjsg 				  PFIT_VERT_INTERP_BILINEAR |
594*f005ef32Sjsg 				  PFIT_HORIZ_INTERP_BILINEAR);
595c349dbc7Sjsg 	}
596c349dbc7Sjsg }
597c349dbc7Sjsg 
gmch_panel_fitting(struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)5981bb76ff1Sjsg static int gmch_panel_fitting(struct intel_crtc_state *crtc_state,
599ad8b1aafSjsg 			      const struct drm_connector_state *conn_state)
600c349dbc7Sjsg {
601ad8b1aafSjsg 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
602ad8b1aafSjsg 	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
603c349dbc7Sjsg 	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
604ad8b1aafSjsg 	struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
6051bb76ff1Sjsg 	int pipe_src_w = drm_rect_width(&crtc_state->pipe_src);
6061bb76ff1Sjsg 	int pipe_src_h = drm_rect_height(&crtc_state->pipe_src);
607c349dbc7Sjsg 
608c349dbc7Sjsg 	/* Native modes don't need fitting */
6091bb76ff1Sjsg 	if (adjusted_mode->crtc_hdisplay == pipe_src_w &&
6101bb76ff1Sjsg 	    adjusted_mode->crtc_vdisplay == pipe_src_h)
611c349dbc7Sjsg 		goto out;
612c349dbc7Sjsg 
613ad8b1aafSjsg 	switch (conn_state->scaling_mode) {
614c349dbc7Sjsg 	case DRM_MODE_SCALE_CENTER:
615c349dbc7Sjsg 		/*
616c349dbc7Sjsg 		 * For centered modes, we have to calculate border widths &
617c349dbc7Sjsg 		 * heights and modify the values programmed into the CRTC.
618c349dbc7Sjsg 		 */
6191bb76ff1Sjsg 		centre_horizontally(adjusted_mode, pipe_src_w);
6201bb76ff1Sjsg 		centre_vertically(adjusted_mode, pipe_src_h);
621c349dbc7Sjsg 		border = LVDS_BORDER_ENABLE;
622c349dbc7Sjsg 		break;
623c349dbc7Sjsg 	case DRM_MODE_SCALE_ASPECT:
624c349dbc7Sjsg 		/* Scale but preserve the aspect ratio */
6255ca02815Sjsg 		if (DISPLAY_VER(dev_priv) >= 4)
626ad8b1aafSjsg 			i965_scale_aspect(crtc_state, &pfit_control);
627c349dbc7Sjsg 		else
628ad8b1aafSjsg 			i9xx_scale_aspect(crtc_state, &pfit_control,
629c349dbc7Sjsg 					  &pfit_pgm_ratios, &border);
630c349dbc7Sjsg 		break;
631c349dbc7Sjsg 	case DRM_MODE_SCALE_FULLSCREEN:
632c349dbc7Sjsg 		/*
633c349dbc7Sjsg 		 * Full scaling, even if it changes the aspect ratio.
634c349dbc7Sjsg 		 * Fortunately this is all done for us in hw.
635c349dbc7Sjsg 		 */
6361bb76ff1Sjsg 		if (pipe_src_h != adjusted_mode->crtc_vdisplay ||
6371bb76ff1Sjsg 		    pipe_src_w != adjusted_mode->crtc_hdisplay) {
638c349dbc7Sjsg 			pfit_control |= PFIT_ENABLE;
6395ca02815Sjsg 			if (DISPLAY_VER(dev_priv) >= 4)
640c349dbc7Sjsg 				pfit_control |= PFIT_SCALING_AUTO;
641c349dbc7Sjsg 			else
642*f005ef32Sjsg 				pfit_control |= (PFIT_VERT_AUTO_SCALE |
643*f005ef32Sjsg 						 PFIT_VERT_INTERP_BILINEAR |
644*f005ef32Sjsg 						 PFIT_HORIZ_AUTO_SCALE |
645*f005ef32Sjsg 						 PFIT_HORIZ_INTERP_BILINEAR);
646c349dbc7Sjsg 		}
647c349dbc7Sjsg 		break;
648c349dbc7Sjsg 	default:
649ad8b1aafSjsg 		MISSING_CASE(conn_state->scaling_mode);
650ad8b1aafSjsg 		return -EINVAL;
651c349dbc7Sjsg 	}
652c349dbc7Sjsg 
653c349dbc7Sjsg 	/* 965+ wants fuzzy fitting */
654c349dbc7Sjsg 	/* FIXME: handle multiple panels by failing gracefully */
6555ca02815Sjsg 	if (DISPLAY_VER(dev_priv) >= 4)
656ad8b1aafSjsg 		pfit_control |= PFIT_PIPE(crtc->pipe) | PFIT_FILTER_FUZZY;
657c349dbc7Sjsg 
658c349dbc7Sjsg out:
659c349dbc7Sjsg 	if ((pfit_control & PFIT_ENABLE) == 0) {
660c349dbc7Sjsg 		pfit_control = 0;
661c349dbc7Sjsg 		pfit_pgm_ratios = 0;
662c349dbc7Sjsg 	}
663c349dbc7Sjsg 
664c349dbc7Sjsg 	/* Make sure pre-965 set dither correctly for 18bpp panels. */
6655ca02815Sjsg 	if (DISPLAY_VER(dev_priv) < 4 && crtc_state->pipe_bpp == 18)
666*f005ef32Sjsg 		pfit_control |= PFIT_PANEL_8TO6_DITHER_ENABLE;
667c349dbc7Sjsg 
668ad8b1aafSjsg 	crtc_state->gmch_pfit.control = pfit_control;
669ad8b1aafSjsg 	crtc_state->gmch_pfit.pgm_ratios = pfit_pgm_ratios;
670ad8b1aafSjsg 	crtc_state->gmch_pfit.lvds_border_bits = border;
671ad8b1aafSjsg 
672ad8b1aafSjsg 	return 0;
673c349dbc7Sjsg }
674c349dbc7Sjsg 
intel_panel_fitting(struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)6751bb76ff1Sjsg int intel_panel_fitting(struct intel_crtc_state *crtc_state,
6761bb76ff1Sjsg 			const struct drm_connector_state *conn_state)
6771bb76ff1Sjsg {
6781bb76ff1Sjsg 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
6791bb76ff1Sjsg 	struct drm_i915_private *i915 = to_i915(crtc->base.dev);
6801bb76ff1Sjsg 
6811bb76ff1Sjsg 	if (HAS_GMCH(i915))
6821bb76ff1Sjsg 		return gmch_panel_fitting(crtc_state, conn_state);
6831bb76ff1Sjsg 	else
6841bb76ff1Sjsg 		return pch_panel_fitting(crtc_state, conn_state);
6851bb76ff1Sjsg }
6861bb76ff1Sjsg 
687ad8b1aafSjsg enum drm_connector_status
intel_panel_detect(struct drm_connector * connector,bool force)688ad8b1aafSjsg intel_panel_detect(struct drm_connector *connector, bool force)
689ad8b1aafSjsg {
690ad8b1aafSjsg 	struct drm_i915_private *i915 = to_i915(connector->dev);
691ad8b1aafSjsg 
692ad8b1aafSjsg 	if (!INTEL_DISPLAY_ENABLED(i915))
693ad8b1aafSjsg 		return connector_status_disconnected;
694ad8b1aafSjsg 
695ad8b1aafSjsg 	return connector_status_connected;
696ad8b1aafSjsg }
697ad8b1aafSjsg 
6981bb76ff1Sjsg enum drm_mode_status
intel_panel_mode_valid(struct intel_connector * connector,const struct drm_display_mode * mode)6991bb76ff1Sjsg intel_panel_mode_valid(struct intel_connector *connector,
7001bb76ff1Sjsg 		       const struct drm_display_mode *mode)
701c349dbc7Sjsg {
7021bb76ff1Sjsg 	const struct drm_display_mode *fixed_mode =
7031bb76ff1Sjsg 		intel_panel_fixed_mode(connector, mode);
704c349dbc7Sjsg 
7051bb76ff1Sjsg 	if (!fixed_mode)
7061bb76ff1Sjsg 		return MODE_OK;
7071bb76ff1Sjsg 
7081bb76ff1Sjsg 	if (mode->hdisplay != fixed_mode->hdisplay)
7091bb76ff1Sjsg 		return MODE_PANEL;
7101bb76ff1Sjsg 
7111bb76ff1Sjsg 	if (mode->vdisplay != fixed_mode->vdisplay)
7121bb76ff1Sjsg 		return MODE_PANEL;
7131bb76ff1Sjsg 
7141bb76ff1Sjsg 	if (drm_mode_vrefresh(mode) != drm_mode_vrefresh(fixed_mode))
7151bb76ff1Sjsg 		return MODE_PANEL;
7161bb76ff1Sjsg 
7171bb76ff1Sjsg 	return MODE_OK;
7181bb76ff1Sjsg }
7191bb76ff1Sjsg 
intel_panel_init_alloc(struct intel_connector * connector)720e33d9a96Sjsg void intel_panel_init_alloc(struct intel_connector *connector)
721e33d9a96Sjsg {
722e33d9a96Sjsg 	struct intel_panel *panel = &connector->panel;
723e33d9a96Sjsg 
72426d05183Sjsg 	connector->panel.vbt.panel_type = -1;
725*f005ef32Sjsg 	connector->panel.vbt.backlight.controller = -1;
726e33d9a96Sjsg 	INIT_LIST_HEAD(&panel->fixed_modes);
727e33d9a96Sjsg }
728e33d9a96Sjsg 
intel_panel_init(struct intel_connector * connector,const struct drm_edid * fixed_edid)729*f005ef32Sjsg int intel_panel_init(struct intel_connector *connector,
730*f005ef32Sjsg 		     const struct drm_edid *fixed_edid)
7311bb76ff1Sjsg {
7321bb76ff1Sjsg 	struct intel_panel *panel = &connector->panel;
7331bb76ff1Sjsg 
734*f005ef32Sjsg 	panel->fixed_edid = fixed_edid;
735*f005ef32Sjsg 
7361bb76ff1Sjsg 	intel_backlight_init_funcs(panel);
7371bb76ff1Sjsg 
738*f005ef32Sjsg 	if (!has_drrs_modes(connector))
739*f005ef32Sjsg 		connector->panel.vbt.drrs_type = DRRS_TYPE_NONE;
740*f005ef32Sjsg 
7411bb76ff1Sjsg 	drm_dbg_kms(connector->base.dev,
7421bb76ff1Sjsg 		    "[CONNECTOR:%d:%s] DRRS type: %s\n",
7431bb76ff1Sjsg 		    connector->base.base.id, connector->base.name,
7441bb76ff1Sjsg 		    intel_drrs_type_str(intel_panel_drrs_type(connector)));
745c349dbc7Sjsg 
746c349dbc7Sjsg 	return 0;
747c349dbc7Sjsg }
748c349dbc7Sjsg 
intel_panel_fini(struct intel_connector * connector)7491bb76ff1Sjsg void intel_panel_fini(struct intel_connector *connector)
750c349dbc7Sjsg {
7511bb76ff1Sjsg 	struct intel_panel *panel = &connector->panel;
7521bb76ff1Sjsg 	struct drm_display_mode *fixed_mode, *next;
753c349dbc7Sjsg 
754*f005ef32Sjsg 	if (!IS_ERR_OR_NULL(panel->fixed_edid))
755*f005ef32Sjsg 		drm_edid_free(panel->fixed_edid);
756*f005ef32Sjsg 
7571bb76ff1Sjsg 	intel_backlight_destroy(panel);
758c349dbc7Sjsg 
7591bb76ff1Sjsg 	intel_bios_fini_panel(panel);
760c349dbc7Sjsg 
7611bb76ff1Sjsg 	list_for_each_entry_safe(fixed_mode, next, &panel->fixed_modes, head) {
7621bb76ff1Sjsg 		list_del(&fixed_mode->head);
7631bb76ff1Sjsg 		drm_mode_destroy(connector->base.dev, fixed_mode);
7641bb76ff1Sjsg 	}
765c349dbc7Sjsg }
766