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