xref: /dflybsd-src/sys/dev/drm/i915/intel_lvds.c (revision a85cb24f18e3804e75ab8bcda7692564d0563317)
1e3adcf8fSFrançois Tigeot /*
2e3adcf8fSFrançois Tigeot  * Copyright © 2006-2007 Intel Corporation
3e3adcf8fSFrançois Tigeot  * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
4e3adcf8fSFrançois Tigeot  *
5e3adcf8fSFrançois Tigeot  * Permission is hereby granted, free of charge, to any person obtaining a
6e3adcf8fSFrançois Tigeot  * copy of this software and associated documentation files (the "Software"),
7e3adcf8fSFrançois Tigeot  * to deal in the Software without restriction, including without limitation
8e3adcf8fSFrançois Tigeot  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9e3adcf8fSFrançois Tigeot  * and/or sell copies of the Software, and to permit persons to whom the
10e3adcf8fSFrançois Tigeot  * Software is furnished to do so, subject to the following conditions:
11e3adcf8fSFrançois Tigeot  *
12e3adcf8fSFrançois Tigeot  * The above copyright notice and this permission notice (including the next
13e3adcf8fSFrançois Tigeot  * paragraph) shall be included in all copies or substantial portions of the
14e3adcf8fSFrançois Tigeot  * Software.
15e3adcf8fSFrançois Tigeot  *
16e3adcf8fSFrançois Tigeot  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17e3adcf8fSFrançois Tigeot  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18e3adcf8fSFrançois Tigeot  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19e3adcf8fSFrançois Tigeot  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20e3adcf8fSFrançois Tigeot  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21e3adcf8fSFrançois Tigeot  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22e3adcf8fSFrançois Tigeot  * DEALINGS IN THE SOFTWARE.
23e3adcf8fSFrançois Tigeot  *
24e3adcf8fSFrançois Tigeot  * Authors:
25e3adcf8fSFrançois Tigeot  *	Eric Anholt <eric@anholt.net>
26e3adcf8fSFrançois Tigeot  *      Dave Airlie <airlied@linux.ie>
27e3adcf8fSFrançois Tigeot  *      Jesse Barnes <jesse.barnes@intel.com>
28e3adcf8fSFrançois Tigeot  */
29bf017597SFrançois Tigeot 
30610e3300SPeeter Must #include "opt_drm.h"
31e3adcf8fSFrançois Tigeot 
325d0b1887SFrançois Tigeot #include <linux/dmi.h>
33a2fdbec6SFrançois Tigeot #include <linux/i2c.h>
34bf017597SFrançois Tigeot #include <linux/slab.h>
358621f407SFrançois Tigeot #include <linux/vga_switcheroo.h>
3618e26a6dSFrançois Tigeot #include <drm/drmP.h>
372c9916cdSFrançois Tigeot #include <drm/drm_atomic_helper.h>
3818e26a6dSFrançois Tigeot #include <drm/drm_crtc.h>
3918e26a6dSFrançois Tigeot #include <drm/drm_edid.h>
4018e26a6dSFrançois Tigeot #include "intel_drv.h"
415c6c6f23SFrançois Tigeot #include <drm/i915_drm.h>
42e3adcf8fSFrançois Tigeot #include "i915_drv.h"
43bf017597SFrançois Tigeot #include <linux/acpi.h>
44e3adcf8fSFrançois Tigeot 
45e3adcf8fSFrançois Tigeot /* Private structure for the integrated LVDS support */
4619df918dSFrançois Tigeot struct intel_lvds_connector {
4719df918dSFrançois Tigeot 	struct intel_connector base;
4819df918dSFrançois Tigeot 
4919df918dSFrançois Tigeot 	struct notifier_block lid_notifier;
5019df918dSFrançois Tigeot };
5119df918dSFrançois Tigeot 
521e12ee3bSFrançois Tigeot struct intel_lvds_pps {
531e12ee3bSFrançois Tigeot 	/* 100us units */
541e12ee3bSFrançois Tigeot 	int t1_t2;
551e12ee3bSFrançois Tigeot 	int t3;
561e12ee3bSFrançois Tigeot 	int t4;
571e12ee3bSFrançois Tigeot 	int t5;
581e12ee3bSFrançois Tigeot 	int tx;
591e12ee3bSFrançois Tigeot 
601e12ee3bSFrançois Tigeot 	int divider;
611e12ee3bSFrançois Tigeot 
621e12ee3bSFrançois Tigeot 	int port;
631e12ee3bSFrançois Tigeot 	bool powerdown_on_reset;
641e12ee3bSFrançois Tigeot };
651e12ee3bSFrançois Tigeot 
6619df918dSFrançois Tigeot struct intel_lvds_encoder {
67e3adcf8fSFrançois Tigeot 	struct intel_encoder base;
68e3adcf8fSFrançois Tigeot 
69a2fdbec6SFrançois Tigeot 	bool is_dual_link;
70aee94f86SFrançois Tigeot 	i915_reg_t reg;
7124edb884SFrançois Tigeot 	u32 a3_power;
72e3adcf8fSFrançois Tigeot 
731e12ee3bSFrançois Tigeot 	struct intel_lvds_pps init_pps;
741e12ee3bSFrançois Tigeot 	u32 init_lvds_val;
751e12ee3bSFrançois Tigeot 
7619df918dSFrançois Tigeot 	struct intel_lvds_connector *attached_connector;
77e3adcf8fSFrançois Tigeot };
78e3adcf8fSFrançois Tigeot 
7919df918dSFrançois Tigeot static struct intel_lvds_encoder *to_lvds_encoder(struct drm_encoder *encoder)
80e3adcf8fSFrançois Tigeot {
8119df918dSFrançois Tigeot 	return container_of(encoder, struct intel_lvds_encoder, base.base);
82e3adcf8fSFrançois Tigeot }
83e3adcf8fSFrançois Tigeot 
8419df918dSFrançois Tigeot static struct intel_lvds_connector *to_lvds_connector(struct drm_connector *connector)
85e3adcf8fSFrançois Tigeot {
8619df918dSFrançois Tigeot 	return container_of(connector, struct intel_lvds_connector, base.base);
8719df918dSFrançois Tigeot }
8819df918dSFrançois Tigeot 
8919df918dSFrançois Tigeot static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
9019df918dSFrançois Tigeot 				    enum i915_pipe *pipe)
9119df918dSFrançois Tigeot {
9219df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
93bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
94a2fdbec6SFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
95a2fdbec6SFrançois Tigeot 	u32 tmp;
96aee94f86SFrançois Tigeot 	bool ret;
9719df918dSFrançois Tigeot 
98*a85cb24fSFrançois Tigeot 	if (!intel_display_power_get_if_enabled(dev_priv,
99*a85cb24fSFrançois Tigeot 						encoder->power_domain))
10024edb884SFrançois Tigeot 		return false;
10124edb884SFrançois Tigeot 
102aee94f86SFrançois Tigeot 	ret = false;
103aee94f86SFrançois Tigeot 
104a2fdbec6SFrançois Tigeot 	tmp = I915_READ(lvds_encoder->reg);
10519df918dSFrançois Tigeot 
10619df918dSFrançois Tigeot 	if (!(tmp & LVDS_PORT_EN))
107aee94f86SFrançois Tigeot 		goto out;
10819df918dSFrançois Tigeot 
1091e12ee3bSFrançois Tigeot 	if (HAS_PCH_CPT(dev_priv))
11019df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE_CPT(tmp);
11119df918dSFrançois Tigeot 	else
11219df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE(tmp);
11319df918dSFrançois Tigeot 
114aee94f86SFrançois Tigeot 	ret = true;
115aee94f86SFrançois Tigeot 
116aee94f86SFrançois Tigeot out:
117*a85cb24fSFrançois Tigeot 	intel_display_power_put(dev_priv, encoder->power_domain);
118aee94f86SFrançois Tigeot 
119aee94f86SFrançois Tigeot 	return ret;
120e3adcf8fSFrançois Tigeot }
121e3adcf8fSFrançois Tigeot 
1225d0b1887SFrançois Tigeot static void intel_lvds_get_config(struct intel_encoder *encoder,
1232c9916cdSFrançois Tigeot 				  struct intel_crtc_state *pipe_config)
1245d0b1887SFrançois Tigeot {
1254be47400SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
126352ff8bdSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
127352ff8bdSFrançois Tigeot 	u32 tmp, flags = 0;
1285d0b1887SFrançois Tigeot 
129352ff8bdSFrançois Tigeot 	tmp = I915_READ(lvds_encoder->reg);
1305d0b1887SFrançois Tigeot 	if (tmp & LVDS_HSYNC_POLARITY)
1315d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NHSYNC;
1325d0b1887SFrançois Tigeot 	else
1335d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PHSYNC;
1345d0b1887SFrançois Tigeot 	if (tmp & LVDS_VSYNC_POLARITY)
1355d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NVSYNC;
1365d0b1887SFrançois Tigeot 	else
1375d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PVSYNC;
1385d0b1887SFrançois Tigeot 
1392c9916cdSFrançois Tigeot 	pipe_config->base.adjusted_mode.flags |= flags;
1405d0b1887SFrançois Tigeot 
1414be47400SFrançois Tigeot 	if (INTEL_GEN(dev_priv) < 5)
142c0e85e96SFrançois Tigeot 		pipe_config->gmch_pfit.lvds_border_bits =
143c0e85e96SFrançois Tigeot 			tmp & LVDS_BORDER_ENABLE;
144c0e85e96SFrançois Tigeot 
1455d0b1887SFrançois Tigeot 	/* gen2/3 store dither state in pfit control, needs to match */
1464be47400SFrançois Tigeot 	if (INTEL_GEN(dev_priv) < 4) {
1475d0b1887SFrançois Tigeot 		tmp = I915_READ(PFIT_CONTROL);
1485d0b1887SFrançois Tigeot 
1495d0b1887SFrançois Tigeot 		pipe_config->gmch_pfit.control |= tmp & PANEL_8TO6_DITHER_ENABLE;
1505d0b1887SFrançois Tigeot 	}
1519edbd4a0SFrançois Tigeot 
1528621f407SFrançois Tigeot 	pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
1535d0b1887SFrançois Tigeot }
1545d0b1887SFrançois Tigeot 
1551e12ee3bSFrançois Tigeot static void intel_lvds_pps_get_hw_state(struct drm_i915_private *dev_priv,
1561e12ee3bSFrançois Tigeot 					struct intel_lvds_pps *pps)
1571e12ee3bSFrançois Tigeot {
1581e12ee3bSFrançois Tigeot 	u32 val;
1591e12ee3bSFrançois Tigeot 
1601e12ee3bSFrançois Tigeot 	pps->powerdown_on_reset = I915_READ(PP_CONTROL(0)) & PANEL_POWER_RESET;
1611e12ee3bSFrançois Tigeot 
1621e12ee3bSFrançois Tigeot 	val = I915_READ(PP_ON_DELAYS(0));
1631e12ee3bSFrançois Tigeot 	pps->port = (val & PANEL_PORT_SELECT_MASK) >>
1641e12ee3bSFrançois Tigeot 		    PANEL_PORT_SELECT_SHIFT;
1651e12ee3bSFrançois Tigeot 	pps->t1_t2 = (val & PANEL_POWER_UP_DELAY_MASK) >>
1661e12ee3bSFrançois Tigeot 		     PANEL_POWER_UP_DELAY_SHIFT;
1671e12ee3bSFrançois Tigeot 	pps->t5 = (val & PANEL_LIGHT_ON_DELAY_MASK) >>
1681e12ee3bSFrançois Tigeot 		  PANEL_LIGHT_ON_DELAY_SHIFT;
1691e12ee3bSFrançois Tigeot 
1701e12ee3bSFrançois Tigeot 	val = I915_READ(PP_OFF_DELAYS(0));
1711e12ee3bSFrançois Tigeot 	pps->t3 = (val & PANEL_POWER_DOWN_DELAY_MASK) >>
1721e12ee3bSFrançois Tigeot 		  PANEL_POWER_DOWN_DELAY_SHIFT;
1731e12ee3bSFrançois Tigeot 	pps->tx = (val & PANEL_LIGHT_OFF_DELAY_MASK) >>
1741e12ee3bSFrançois Tigeot 		  PANEL_LIGHT_OFF_DELAY_SHIFT;
1751e12ee3bSFrançois Tigeot 
1761e12ee3bSFrançois Tigeot 	val = I915_READ(PP_DIVISOR(0));
1771e12ee3bSFrançois Tigeot 	pps->divider = (val & PP_REFERENCE_DIVIDER_MASK) >>
1781e12ee3bSFrançois Tigeot 		       PP_REFERENCE_DIVIDER_SHIFT;
1791e12ee3bSFrançois Tigeot 	val = (val & PANEL_POWER_CYCLE_DELAY_MASK) >>
1801e12ee3bSFrançois Tigeot 	      PANEL_POWER_CYCLE_DELAY_SHIFT;
1811e12ee3bSFrançois Tigeot 	/*
1821e12ee3bSFrançois Tigeot 	 * Remove the BSpec specified +1 (100ms) offset that accounts for a
1831e12ee3bSFrançois Tigeot 	 * too short power-cycle delay due to the asynchronous programming of
1841e12ee3bSFrançois Tigeot 	 * the register.
1851e12ee3bSFrançois Tigeot 	 */
1861e12ee3bSFrançois Tigeot 	if (val)
1871e12ee3bSFrançois Tigeot 		val--;
1881e12ee3bSFrançois Tigeot 	/* Convert from 100ms to 100us units */
1891e12ee3bSFrançois Tigeot 	pps->t4 = val * 1000;
1901e12ee3bSFrançois Tigeot 
1911e12ee3bSFrançois Tigeot 	if (INTEL_INFO(dev_priv)->gen <= 4 &&
1921e12ee3bSFrançois Tigeot 	    pps->t1_t2 == 0 && pps->t5 == 0 && pps->t3 == 0 && pps->tx == 0) {
1931e12ee3bSFrançois Tigeot 		DRM_DEBUG_KMS("Panel power timings uninitialized, "
1941e12ee3bSFrançois Tigeot 			      "setting defaults\n");
1951e12ee3bSFrançois Tigeot 		/* Set T2 to 40ms and T5 to 200ms in 100 usec units */
1961e12ee3bSFrançois Tigeot 		pps->t1_t2 = 40 * 10;
1971e12ee3bSFrançois Tigeot 		pps->t5 = 200 * 10;
1981e12ee3bSFrançois Tigeot 		/* Set T3 to 35ms and Tx to 200ms in 100 usec units */
1991e12ee3bSFrançois Tigeot 		pps->t3 = 35 * 10;
2001e12ee3bSFrançois Tigeot 		pps->tx = 200 * 10;
2011e12ee3bSFrançois Tigeot 	}
2021e12ee3bSFrançois Tigeot 
2031e12ee3bSFrançois Tigeot 	DRM_DEBUG_DRIVER("LVDS PPS:t1+t2 %d t3 %d t4 %d t5 %d tx %d "
2041e12ee3bSFrançois Tigeot 			 "divider %d port %d powerdown_on_reset %d\n",
2051e12ee3bSFrançois Tigeot 			 pps->t1_t2, pps->t3, pps->t4, pps->t5, pps->tx,
2061e12ee3bSFrançois Tigeot 			 pps->divider, pps->port, pps->powerdown_on_reset);
2071e12ee3bSFrançois Tigeot }
2081e12ee3bSFrançois Tigeot 
2091e12ee3bSFrançois Tigeot static void intel_lvds_pps_init_hw(struct drm_i915_private *dev_priv,
2101e12ee3bSFrançois Tigeot 				   struct intel_lvds_pps *pps)
2111e12ee3bSFrançois Tigeot {
2121e12ee3bSFrançois Tigeot 	u32 val;
2131e12ee3bSFrançois Tigeot 
2141e12ee3bSFrançois Tigeot 	val = I915_READ(PP_CONTROL(0));
2151e12ee3bSFrançois Tigeot 	WARN_ON((val & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS);
2161e12ee3bSFrançois Tigeot 	if (pps->powerdown_on_reset)
2171e12ee3bSFrançois Tigeot 		val |= PANEL_POWER_RESET;
2181e12ee3bSFrançois Tigeot 	I915_WRITE(PP_CONTROL(0), val);
2191e12ee3bSFrançois Tigeot 
2201e12ee3bSFrançois Tigeot 	I915_WRITE(PP_ON_DELAYS(0), (pps->port << PANEL_PORT_SELECT_SHIFT) |
2211e12ee3bSFrançois Tigeot 				    (pps->t1_t2 << PANEL_POWER_UP_DELAY_SHIFT) |
2221e12ee3bSFrançois Tigeot 				    (pps->t5 << PANEL_LIGHT_ON_DELAY_SHIFT));
2231e12ee3bSFrançois Tigeot 	I915_WRITE(PP_OFF_DELAYS(0), (pps->t3 << PANEL_POWER_DOWN_DELAY_SHIFT) |
2241e12ee3bSFrançois Tigeot 				     (pps->tx << PANEL_LIGHT_OFF_DELAY_SHIFT));
2251e12ee3bSFrançois Tigeot 
2261e12ee3bSFrançois Tigeot 	val = pps->divider << PP_REFERENCE_DIVIDER_SHIFT;
2271e12ee3bSFrançois Tigeot 	val |= (DIV_ROUND_UP(pps->t4, 1000) + 1) <<
2281e12ee3bSFrançois Tigeot 	       PANEL_POWER_CYCLE_DELAY_SHIFT;
2291e12ee3bSFrançois Tigeot 	I915_WRITE(PP_DIVISOR(0), val);
2301e12ee3bSFrançois Tigeot }
2311e12ee3bSFrançois Tigeot 
2321e12ee3bSFrançois Tigeot static void intel_pre_enable_lvds(struct intel_encoder *encoder,
2331e12ee3bSFrançois Tigeot 				  struct intel_crtc_state *pipe_config,
2341e12ee3bSFrançois Tigeot 				  struct drm_connector_state *conn_state)
235a2fdbec6SFrançois Tigeot {
236a2fdbec6SFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
2371e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
2381e12ee3bSFrançois Tigeot 	struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc);
2391e12ee3bSFrançois Tigeot 	const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
2409edbd4a0SFrançois Tigeot 	int pipe = crtc->pipe;
241a2fdbec6SFrançois Tigeot 	u32 temp;
242a2fdbec6SFrançois Tigeot 
2431e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
2449edbd4a0SFrançois Tigeot 		assert_fdi_rx_pll_disabled(dev_priv, pipe);
2459edbd4a0SFrançois Tigeot 		assert_shared_dpll_disabled(dev_priv,
2461e12ee3bSFrançois Tigeot 					    pipe_config->shared_dpll);
2479edbd4a0SFrançois Tigeot 	} else {
2489edbd4a0SFrançois Tigeot 		assert_pll_disabled(dev_priv, pipe);
2499edbd4a0SFrançois Tigeot 	}
2509edbd4a0SFrançois Tigeot 
2511e12ee3bSFrançois Tigeot 	intel_lvds_pps_init_hw(dev_priv, &lvds_encoder->init_pps);
2521e12ee3bSFrançois Tigeot 
2531e12ee3bSFrançois Tigeot 	temp = lvds_encoder->init_lvds_val;
254a2fdbec6SFrançois Tigeot 	temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
255a2fdbec6SFrançois Tigeot 
2561e12ee3bSFrançois Tigeot 	if (HAS_PCH_CPT(dev_priv)) {
257a2fdbec6SFrançois Tigeot 		temp &= ~PORT_TRANS_SEL_MASK;
258a2fdbec6SFrançois Tigeot 		temp |= PORT_TRANS_SEL_CPT(pipe);
259a2fdbec6SFrançois Tigeot 	} else {
260a2fdbec6SFrançois Tigeot 		if (pipe == 1) {
261a2fdbec6SFrançois Tigeot 			temp |= LVDS_PIPEB_SELECT;
262a2fdbec6SFrançois Tigeot 		} else {
263a2fdbec6SFrançois Tigeot 			temp &= ~LVDS_PIPEB_SELECT;
264a2fdbec6SFrançois Tigeot 		}
265a2fdbec6SFrançois Tigeot 	}
266a2fdbec6SFrançois Tigeot 
267a2fdbec6SFrançois Tigeot 	/* set the corresponsding LVDS_BORDER bit */
2685d0b1887SFrançois Tigeot 	temp &= ~LVDS_BORDER_ENABLE;
2691e12ee3bSFrançois Tigeot 	temp |= pipe_config->gmch_pfit.lvds_border_bits;
270a2fdbec6SFrançois Tigeot 	/* Set the B0-B3 data pairs corresponding to whether we're going to
271a2fdbec6SFrançois Tigeot 	 * set the DPLLs for dual-channel mode or not.
272a2fdbec6SFrançois Tigeot 	 */
273a2fdbec6SFrançois Tigeot 	if (lvds_encoder->is_dual_link)
274a2fdbec6SFrançois Tigeot 		temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
275a2fdbec6SFrançois Tigeot 	else
276a2fdbec6SFrançois Tigeot 		temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
277a2fdbec6SFrançois Tigeot 
278a2fdbec6SFrançois Tigeot 	/* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
279a2fdbec6SFrançois Tigeot 	 * appropriately here, but we need to look more thoroughly into how
28024edb884SFrançois Tigeot 	 * panels behave in the two modes. For now, let's just maintain the
28124edb884SFrançois Tigeot 	 * value we got from the BIOS.
282a2fdbec6SFrançois Tigeot 	 */
28324edb884SFrançois Tigeot 	temp &= ~LVDS_A3_POWER_MASK;
28424edb884SFrançois Tigeot 	temp |= lvds_encoder->a3_power;
285a2fdbec6SFrançois Tigeot 
286a2fdbec6SFrançois Tigeot 	/* Set the dithering flag on LVDS as needed, note that there is no
287a2fdbec6SFrançois Tigeot 	 * special lvds dither control bit on pch-split platforms, dithering is
288a2fdbec6SFrançois Tigeot 	 * only controlled through the PIPECONF reg. */
2891487f786SFrançois Tigeot 	if (IS_GEN4(dev_priv)) {
2905d0b1887SFrançois Tigeot 		/* Bspec wording suggests that LVDS port dithering only exists
2915d0b1887SFrançois Tigeot 		 * for 18bpp panels. */
2921e12ee3bSFrançois Tigeot 		if (pipe_config->dither && pipe_config->pipe_bpp == 18)
293a2fdbec6SFrançois Tigeot 			temp |= LVDS_ENABLE_DITHER;
294a2fdbec6SFrançois Tigeot 		else
295a2fdbec6SFrançois Tigeot 			temp &= ~LVDS_ENABLE_DITHER;
296a2fdbec6SFrançois Tigeot 	}
297a2fdbec6SFrançois Tigeot 	temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
2989edbd4a0SFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
299a2fdbec6SFrançois Tigeot 		temp |= LVDS_HSYNC_POLARITY;
3009edbd4a0SFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
301a2fdbec6SFrançois Tigeot 		temp |= LVDS_VSYNC_POLARITY;
302a2fdbec6SFrançois Tigeot 
303a2fdbec6SFrançois Tigeot 	I915_WRITE(lvds_encoder->reg, temp);
304a2fdbec6SFrançois Tigeot }
305a2fdbec6SFrançois Tigeot 
306e3adcf8fSFrançois Tigeot /**
307e3adcf8fSFrançois Tigeot  * Sets the power state for the panel.
308e3adcf8fSFrançois Tigeot  */
3091e12ee3bSFrançois Tigeot static void intel_enable_lvds(struct intel_encoder *encoder,
3101e12ee3bSFrançois Tigeot 			      struct intel_crtc_state *pipe_config,
3111e12ee3bSFrançois Tigeot 			      struct drm_connector_state *conn_state)
312e3adcf8fSFrançois Tigeot {
31319df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
31419df918dSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
3159edbd4a0SFrançois Tigeot 	struct intel_connector *intel_connector =
3169edbd4a0SFrançois Tigeot 		&lvds_encoder->attached_connector->base;
317bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
318e3adcf8fSFrançois Tigeot 
319a2fdbec6SFrançois Tigeot 	I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN);
320e3adcf8fSFrançois Tigeot 
3211e12ee3bSFrançois Tigeot 	I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) | PANEL_POWER_ON);
322a2fdbec6SFrançois Tigeot 	POSTING_READ(lvds_encoder->reg);
3231e12ee3bSFrançois Tigeot 	if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, PP_ON, 1000))
32419df918dSFrançois Tigeot 		DRM_ERROR("timed out waiting for panel to power on\n");
325e3adcf8fSFrançois Tigeot 
3269edbd4a0SFrançois Tigeot 	intel_panel_enable_backlight(intel_connector);
327e3adcf8fSFrançois Tigeot }
328e3adcf8fSFrançois Tigeot 
3291e12ee3bSFrançois Tigeot static void intel_disable_lvds(struct intel_encoder *encoder,
3301e12ee3bSFrançois Tigeot 			       struct intel_crtc_state *old_crtc_state,
3311e12ee3bSFrançois Tigeot 			       struct drm_connector_state *old_conn_state)
332e3adcf8fSFrançois Tigeot {
33319df918dSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
3341e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
335e3adcf8fSFrançois Tigeot 
3361e12ee3bSFrançois Tigeot 	I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) & ~PANEL_POWER_ON);
3371e12ee3bSFrançois Tigeot 	if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, 0, 1000))
338e3adcf8fSFrançois Tigeot 		DRM_ERROR("timed out waiting for panel to power off\n");
339e3adcf8fSFrançois Tigeot 
340a2fdbec6SFrançois Tigeot 	I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN);
341a2fdbec6SFrançois Tigeot 	POSTING_READ(lvds_encoder->reg);
342e3adcf8fSFrançois Tigeot }
343e3adcf8fSFrançois Tigeot 
3441e12ee3bSFrançois Tigeot static void gmch_disable_lvds(struct intel_encoder *encoder,
3451e12ee3bSFrançois Tigeot 			      struct intel_crtc_state *old_crtc_state,
3461e12ee3bSFrançois Tigeot 			      struct drm_connector_state *old_conn_state)
3471e12ee3bSFrançois Tigeot 
348a05eeebfSFrançois Tigeot {
349a05eeebfSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
350a05eeebfSFrançois Tigeot 	struct intel_connector *intel_connector =
351a05eeebfSFrançois Tigeot 		&lvds_encoder->attached_connector->base;
352a05eeebfSFrançois Tigeot 
353a05eeebfSFrançois Tigeot 	intel_panel_disable_backlight(intel_connector);
354a05eeebfSFrançois Tigeot 
3551e12ee3bSFrançois Tigeot 	intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
356a05eeebfSFrançois Tigeot }
357a05eeebfSFrançois Tigeot 
3581e12ee3bSFrançois Tigeot static void pch_disable_lvds(struct intel_encoder *encoder,
3591e12ee3bSFrançois Tigeot 			     struct intel_crtc_state *old_crtc_state,
3601e12ee3bSFrançois Tigeot 			     struct drm_connector_state *old_conn_state)
361a05eeebfSFrançois Tigeot {
362a05eeebfSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
363a05eeebfSFrançois Tigeot 	struct intel_connector *intel_connector =
364a05eeebfSFrançois Tigeot 		&lvds_encoder->attached_connector->base;
365a05eeebfSFrançois Tigeot 
366a05eeebfSFrançois Tigeot 	intel_panel_disable_backlight(intel_connector);
367a05eeebfSFrançois Tigeot }
368a05eeebfSFrançois Tigeot 
3691e12ee3bSFrançois Tigeot static void pch_post_disable_lvds(struct intel_encoder *encoder,
3701e12ee3bSFrançois Tigeot 				  struct intel_crtc_state *old_crtc_state,
3711e12ee3bSFrançois Tigeot 				  struct drm_connector_state *old_conn_state)
372a05eeebfSFrançois Tigeot {
3731e12ee3bSFrançois Tigeot 	intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
374a05eeebfSFrançois Tigeot }
375a05eeebfSFrançois Tigeot 
3769edbd4a0SFrançois Tigeot static enum drm_mode_status
3779edbd4a0SFrançois Tigeot intel_lvds_mode_valid(struct drm_connector *connector,
378e3adcf8fSFrançois Tigeot 		      struct drm_display_mode *mode)
379e3adcf8fSFrançois Tigeot {
38019df918dSFrançois Tigeot 	struct intel_connector *intel_connector = to_intel_connector(connector);
38119df918dSFrançois Tigeot 	struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
382352ff8bdSFrançois Tigeot 	int max_pixclk = to_i915(connector->dev)->max_dotclk_freq;
383e3adcf8fSFrançois Tigeot 
384e3adcf8fSFrançois Tigeot 	if (mode->hdisplay > fixed_mode->hdisplay)
385e3adcf8fSFrançois Tigeot 		return MODE_PANEL;
386e3adcf8fSFrançois Tigeot 	if (mode->vdisplay > fixed_mode->vdisplay)
387e3adcf8fSFrançois Tigeot 		return MODE_PANEL;
388352ff8bdSFrançois Tigeot 	if (fixed_mode->clock > max_pixclk)
389352ff8bdSFrançois Tigeot 		return MODE_CLOCK_HIGH;
390e3adcf8fSFrançois Tigeot 
391e3adcf8fSFrançois Tigeot 	return MODE_OK;
392e3adcf8fSFrançois Tigeot }
393e3adcf8fSFrançois Tigeot 
3948e26cdf6SFrançois Tigeot static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
3951e12ee3bSFrançois Tigeot 				      struct intel_crtc_state *pipe_config,
3961e12ee3bSFrançois Tigeot 				      struct drm_connector_state *conn_state)
397e3adcf8fSFrançois Tigeot {
3981e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(intel_encoder->base.dev);
3998e26cdf6SFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder =
4008e26cdf6SFrançois Tigeot 		to_lvds_encoder(&intel_encoder->base);
40119df918dSFrançois Tigeot 	struct intel_connector *intel_connector =
40219df918dSFrançois Tigeot 		&lvds_encoder->attached_connector->base;
4032c9916cdSFrançois Tigeot 	struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
404477eb7f9SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc);
4058e26cdf6SFrançois Tigeot 	unsigned int lvds_bpp;
406e3adcf8fSFrançois Tigeot 
407e3adcf8fSFrançois Tigeot 	/* Should never happen!! */
4081e12ee3bSFrançois Tigeot 	if (INTEL_GEN(dev_priv) < 4 && intel_crtc->pipe == 0) {
409e3adcf8fSFrançois Tigeot 		DRM_ERROR("Can't support LVDS on pipe A\n");
410e3adcf8fSFrançois Tigeot 		return false;
411e3adcf8fSFrançois Tigeot 	}
412e3adcf8fSFrançois Tigeot 
41324edb884SFrançois Tigeot 	if (lvds_encoder->a3_power == LVDS_A3_POWER_UP)
4148e26cdf6SFrançois Tigeot 		lvds_bpp = 8*3;
4158e26cdf6SFrançois Tigeot 	else
4168e26cdf6SFrançois Tigeot 		lvds_bpp = 6*3;
4178e26cdf6SFrançois Tigeot 
4185d0b1887SFrançois Tigeot 	if (lvds_bpp != pipe_config->pipe_bpp && !pipe_config->bw_constrained) {
4198e26cdf6SFrançois Tigeot 		DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n",
4208e26cdf6SFrançois Tigeot 			      pipe_config->pipe_bpp, lvds_bpp);
4218e26cdf6SFrançois Tigeot 		pipe_config->pipe_bpp = lvds_bpp;
4228e26cdf6SFrançois Tigeot 	}
4235d0b1887SFrançois Tigeot 
424e3adcf8fSFrançois Tigeot 	/*
425e3adcf8fSFrançois Tigeot 	 * We have timings from the BIOS for the panel, put them in
426e3adcf8fSFrançois Tigeot 	 * to the adjusted mode.  The CRTC will be set up for this mode,
427e3adcf8fSFrançois Tigeot 	 * with the panel scaling set up to source from the H/VDisplay
428e3adcf8fSFrançois Tigeot 	 * of the original mode.
429e3adcf8fSFrançois Tigeot 	 */
43019df918dSFrançois Tigeot 	intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
43119df918dSFrançois Tigeot 			       adjusted_mode);
432e3adcf8fSFrançois Tigeot 
4331e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
4348e26cdf6SFrançois Tigeot 		pipe_config->has_pch_encoder = true;
4358e26cdf6SFrançois Tigeot 
4365d0b1887SFrançois Tigeot 		intel_pch_panel_fitting(intel_crtc, pipe_config,
4375d0b1887SFrançois Tigeot 					intel_connector->panel.fitting_mode);
438e3adcf8fSFrançois Tigeot 	} else {
4395d0b1887SFrançois Tigeot 		intel_gmch_panel_fitting(intel_crtc, pipe_config,
4405d0b1887SFrançois Tigeot 					 intel_connector->panel.fitting_mode);
441e3adcf8fSFrançois Tigeot 
442e3adcf8fSFrançois Tigeot 	}
443e3adcf8fSFrançois Tigeot 
444e3adcf8fSFrançois Tigeot 	/*
445e3adcf8fSFrançois Tigeot 	 * XXX: It would be nice to support lower refresh rates on the
446e3adcf8fSFrançois Tigeot 	 * panels to reduce power consumption, and perhaps match the
447e3adcf8fSFrançois Tigeot 	 * user's requested refresh rate.
448e3adcf8fSFrançois Tigeot 	 */
449e3adcf8fSFrançois Tigeot 
450e3adcf8fSFrançois Tigeot 	return true;
451e3adcf8fSFrançois Tigeot }
452e3adcf8fSFrançois Tigeot 
453e3adcf8fSFrançois Tigeot /**
454e3adcf8fSFrançois Tigeot  * Detect the LVDS connection.
455e3adcf8fSFrançois Tigeot  *
456e3adcf8fSFrançois Tigeot  * Since LVDS doesn't have hotlug, we use the lid as a proxy.  Open means
457e3adcf8fSFrançois Tigeot  * connected and closed means disconnected.  We also send hotplug events as
458e3adcf8fSFrançois Tigeot  * needed, using lid status notification from the input layer.
459e3adcf8fSFrançois Tigeot  */
460e3adcf8fSFrançois Tigeot static enum drm_connector_status
461e3adcf8fSFrançois Tigeot intel_lvds_detect(struct drm_connector *connector, bool force)
462e3adcf8fSFrançois Tigeot {
463*a85cb24fSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(connector->dev);
464e3adcf8fSFrançois Tigeot 	enum drm_connector_status status;
465e3adcf8fSFrançois Tigeot 
4669edbd4a0SFrançois Tigeot 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
467b4efbf42Szrj 		      connector->base.id, connector->name);
4689edbd4a0SFrançois Tigeot 
469*a85cb24fSFrançois Tigeot 	status = intel_panel_detect(dev_priv);
470e3adcf8fSFrançois Tigeot 	if (status != connector_status_unknown)
471e3adcf8fSFrançois Tigeot 		return status;
472e3adcf8fSFrançois Tigeot 
473e3adcf8fSFrançois Tigeot 	return connector_status_connected;
474e3adcf8fSFrançois Tigeot }
475e3adcf8fSFrançois Tigeot 
476e3adcf8fSFrançois Tigeot /**
477e3adcf8fSFrançois Tigeot  * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
478e3adcf8fSFrançois Tigeot  */
479e3adcf8fSFrançois Tigeot static int intel_lvds_get_modes(struct drm_connector *connector)
480e3adcf8fSFrançois Tigeot {
48119df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector = to_lvds_connector(connector);
482e3adcf8fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
483e3adcf8fSFrançois Tigeot 	struct drm_display_mode *mode;
484e3adcf8fSFrançois Tigeot 
48519df918dSFrançois Tigeot 	/* use cached edid if we have one */
48619df918dSFrançois Tigeot 	if (!IS_ERR_OR_NULL(lvds_connector->base.edid))
48719df918dSFrançois Tigeot 		return drm_add_edid_modes(connector, lvds_connector->base.edid);
488e3adcf8fSFrançois Tigeot 
48919df918dSFrançois Tigeot 	mode = drm_mode_duplicate(dev, lvds_connector->base.panel.fixed_mode);
490e3adcf8fSFrançois Tigeot 	if (mode == NULL)
491e3adcf8fSFrançois Tigeot 		return 0;
492e3adcf8fSFrançois Tigeot 
493e3adcf8fSFrançois Tigeot 	drm_mode_probed_add(connector, mode);
494e3adcf8fSFrançois Tigeot 	return 1;
495e3adcf8fSFrançois Tigeot }
496e3adcf8fSFrançois Tigeot 
49776594197Szrj #if 0 /* unused */
498e3adcf8fSFrançois Tigeot static int intel_no_modeset_on_lid_dmi_callback(const struct dmi_system_id *id)
499e3adcf8fSFrançois Tigeot {
50019df918dSFrançois Tigeot 	DRM_INFO("Skipping forced modeset for %s\n", id->ident);
501e3adcf8fSFrançois Tigeot 	return 1;
502e3adcf8fSFrançois Tigeot }
503e3adcf8fSFrançois Tigeot 
504e3adcf8fSFrançois Tigeot /* The GPU hangs up on these systems if modeset is performed on LID open */
505e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_modeset_on_lid[] = {
506e3adcf8fSFrançois Tigeot 	{
507e3adcf8fSFrançois Tigeot 		.callback = intel_no_modeset_on_lid_dmi_callback,
508e3adcf8fSFrançois Tigeot 		.ident = "Toshiba Tecra A11",
509e3adcf8fSFrançois Tigeot 		.matches = {
510e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
511e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A11"),
512e3adcf8fSFrançois Tigeot 		},
513e3adcf8fSFrançois Tigeot 	},
514e3adcf8fSFrançois Tigeot 
515e3adcf8fSFrançois Tigeot 	{ }	/* terminating entry */
516e3adcf8fSFrançois Tigeot };
517e3adcf8fSFrançois Tigeot 
518e3adcf8fSFrançois Tigeot /*
519a2fdbec6SFrançois Tigeot  * Lid events. Note the use of 'modeset':
520a2fdbec6SFrançois Tigeot  *  - we set it to MODESET_ON_LID_OPEN on lid close,
521a2fdbec6SFrançois Tigeot  *    and set it to MODESET_DONE on open
522e3adcf8fSFrançois Tigeot  *  - we use it as a "only once" bit (ie we ignore
523a2fdbec6SFrançois Tigeot  *    duplicate events where it was already properly set)
524a2fdbec6SFrançois Tigeot  *  - the suspend/resume paths will set it to
525a2fdbec6SFrançois Tigeot  *    MODESET_SUSPENDED and ignore the lid open event,
526a2fdbec6SFrançois Tigeot  *    because they restore the mode ("lid open").
527e3adcf8fSFrançois Tigeot  */
528e3adcf8fSFrançois Tigeot static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
529e3adcf8fSFrançois Tigeot 			    void *unused)
530e3adcf8fSFrançois Tigeot {
53119df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector =
53219df918dSFrançois Tigeot 		container_of(nb, struct intel_lvds_connector, lid_notifier);
53319df918dSFrançois Tigeot 	struct drm_connector *connector = &lvds_connector->base.base;
53419df918dSFrançois Tigeot 	struct drm_device *dev = connector->dev;
535bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
536e3adcf8fSFrançois Tigeot 
537e3adcf8fSFrançois Tigeot 	if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
538e3adcf8fSFrançois Tigeot 		return NOTIFY_OK;
539e3adcf8fSFrançois Tigeot 
540a2fdbec6SFrançois Tigeot 	mutex_lock(&dev_priv->modeset_restore_lock);
541a2fdbec6SFrançois Tigeot 	if (dev_priv->modeset_restore == MODESET_SUSPENDED)
542a2fdbec6SFrançois Tigeot 		goto exit;
543e3adcf8fSFrançois Tigeot 	/*
544e3adcf8fSFrançois Tigeot 	 * check and update the status of LVDS connector after receiving
545e3adcf8fSFrançois Tigeot 	 * the LID nofication event.
546e3adcf8fSFrançois Tigeot 	 */
54719df918dSFrançois Tigeot 	connector->status = connector->funcs->detect(connector, false);
548e3adcf8fSFrançois Tigeot 
549e3adcf8fSFrançois Tigeot 	/* Don't force modeset on machines where it causes a GPU lockup */
550e3adcf8fSFrançois Tigeot 	if (dmi_check_system(intel_no_modeset_on_lid))
551a2fdbec6SFrançois Tigeot 		goto exit;
552e3adcf8fSFrançois Tigeot 	if (!acpi_lid_open()) {
553a2fdbec6SFrançois Tigeot 		/* do modeset on next lid open event */
554a2fdbec6SFrançois Tigeot 		dev_priv->modeset_restore = MODESET_ON_LID_OPEN;
555a2fdbec6SFrançois Tigeot 		goto exit;
556e3adcf8fSFrançois Tigeot 	}
557e3adcf8fSFrançois Tigeot 
558a2fdbec6SFrançois Tigeot 	if (dev_priv->modeset_restore == MODESET_DONE)
559a2fdbec6SFrançois Tigeot 		goto exit;
560e3adcf8fSFrançois Tigeot 
5619edbd4a0SFrançois Tigeot 	/*
5629edbd4a0SFrançois Tigeot 	 * Some old platform's BIOS love to wreak havoc while the lid is closed.
5639edbd4a0SFrançois Tigeot 	 * We try to detect this here and undo any damage. The split for PCH
5649edbd4a0SFrançois Tigeot 	 * platforms is rather conservative and a bit arbitrary expect that on
5659edbd4a0SFrançois Tigeot 	 * those platforms VGA disabling requires actual legacy VGA I/O access,
5669edbd4a0SFrançois Tigeot 	 * and as part of the cleanup in the hw state restore we also redisable
5679edbd4a0SFrançois Tigeot 	 * the vga plane.
5689edbd4a0SFrançois Tigeot 	 */
5691e12ee3bSFrançois Tigeot 	if (!HAS_PCH_SPLIT(dev_priv))
570a05eeebfSFrançois Tigeot 		intel_display_resume(dev);
571e3adcf8fSFrançois Tigeot 
572a2fdbec6SFrançois Tigeot 	dev_priv->modeset_restore = MODESET_DONE;
573a2fdbec6SFrançois Tigeot 
574a2fdbec6SFrançois Tigeot exit:
575a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev_priv->modeset_restore_lock);
576e3adcf8fSFrançois Tigeot 	return NOTIFY_OK;
577e3adcf8fSFrançois Tigeot }
578e3adcf8fSFrançois Tigeot #endif
579e3adcf8fSFrançois Tigeot 
580e3adcf8fSFrançois Tigeot /**
581e3adcf8fSFrançois Tigeot  * intel_lvds_destroy - unregister and free LVDS structures
582e3adcf8fSFrançois Tigeot  * @connector: connector to free
583e3adcf8fSFrançois Tigeot  *
584e3adcf8fSFrançois Tigeot  * Unregister the DDC bus for this connector then free the driver private
585e3adcf8fSFrançois Tigeot  * structure.
586e3adcf8fSFrançois Tigeot  */
587e3adcf8fSFrançois Tigeot static void intel_lvds_destroy(struct drm_connector *connector)
588e3adcf8fSFrançois Tigeot {
58919df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector =
59019df918dSFrançois Tigeot 		to_lvds_connector(connector);
591e3adcf8fSFrançois Tigeot 
592e3adcf8fSFrançois Tigeot #if 0
59319df918dSFrançois Tigeot 	if (lvds_connector->lid_notifier.notifier_call)
59419df918dSFrançois Tigeot 		acpi_lid_notifier_unregister(&lvds_connector->lid_notifier);
595e3adcf8fSFrançois Tigeot #endif
59619df918dSFrançois Tigeot 
59719df918dSFrançois Tigeot 	if (!IS_ERR_OR_NULL(lvds_connector->base.edid))
598158486a6SFrançois Tigeot 		kfree(lvds_connector->base.edid);
59919df918dSFrançois Tigeot 
60019df918dSFrançois Tigeot 	intel_panel_fini(&lvds_connector->base.panel);
60119df918dSFrançois Tigeot 
602e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
603158486a6SFrançois Tigeot 	kfree(connector);
604e3adcf8fSFrançois Tigeot }
605e3adcf8fSFrançois Tigeot 
606e3adcf8fSFrançois Tigeot static int intel_lvds_set_property(struct drm_connector *connector,
607e3adcf8fSFrançois Tigeot 				   struct drm_property *property,
608e3adcf8fSFrançois Tigeot 				   uint64_t value)
609e3adcf8fSFrançois Tigeot {
61019df918dSFrançois Tigeot 	struct intel_connector *intel_connector = to_intel_connector(connector);
611e3adcf8fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
612e3adcf8fSFrançois Tigeot 
613e3adcf8fSFrançois Tigeot 	if (property == dev->mode_config.scaling_mode_property) {
61419df918dSFrançois Tigeot 		struct drm_crtc *crtc;
615e3adcf8fSFrançois Tigeot 
616e3adcf8fSFrançois Tigeot 		if (value == DRM_MODE_SCALE_NONE) {
617e3adcf8fSFrançois Tigeot 			DRM_DEBUG_KMS("no scaling not supported\n");
618e3adcf8fSFrançois Tigeot 			return -EINVAL;
619e3adcf8fSFrançois Tigeot 		}
620e3adcf8fSFrançois Tigeot 
62119df918dSFrançois Tigeot 		if (intel_connector->panel.fitting_mode == value) {
622e3adcf8fSFrançois Tigeot 			/* the LVDS scaling property is not changed */
623e3adcf8fSFrançois Tigeot 			return 0;
624e3adcf8fSFrançois Tigeot 		}
62519df918dSFrançois Tigeot 		intel_connector->panel.fitting_mode = value;
62619df918dSFrançois Tigeot 
62719df918dSFrançois Tigeot 		crtc = intel_attached_encoder(connector)->base.crtc;
628477eb7f9SFrançois Tigeot 		if (crtc && crtc->state->enable) {
629e3adcf8fSFrançois Tigeot 			/*
630e3adcf8fSFrançois Tigeot 			 * If the CRTC is enabled, the display will be changed
631e3adcf8fSFrançois Tigeot 			 * according to the new panel fitting mode.
632e3adcf8fSFrançois Tigeot 			 */
633a2fdbec6SFrançois Tigeot 			intel_crtc_restore_mode(crtc);
634e3adcf8fSFrançois Tigeot 		}
635e3adcf8fSFrançois Tigeot 	}
636e3adcf8fSFrançois Tigeot 
637e3adcf8fSFrançois Tigeot 	return 0;
638e3adcf8fSFrançois Tigeot }
639e3adcf8fSFrançois Tigeot 
640e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
641e3adcf8fSFrançois Tigeot 	.get_modes = intel_lvds_get_modes,
642e3adcf8fSFrançois Tigeot 	.mode_valid = intel_lvds_mode_valid,
643e3adcf8fSFrançois Tigeot };
644e3adcf8fSFrançois Tigeot 
645e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_lvds_connector_funcs = {
646a05eeebfSFrançois Tigeot 	.dpms = drm_atomic_helper_connector_dpms,
647e3adcf8fSFrançois Tigeot 	.detect = intel_lvds_detect,
648e3adcf8fSFrançois Tigeot 	.fill_modes = drm_helper_probe_single_connector_modes,
649e3adcf8fSFrançois Tigeot 	.set_property = intel_lvds_set_property,
6502c9916cdSFrançois Tigeot 	.atomic_get_property = intel_connector_atomic_get_property,
6511487f786SFrançois Tigeot 	.late_register = intel_connector_register,
6521487f786SFrançois Tigeot 	.early_unregister = intel_connector_unregister,
653e3adcf8fSFrançois Tigeot 	.destroy = intel_lvds_destroy,
6542c9916cdSFrançois Tigeot 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
655477eb7f9SFrançois Tigeot 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
656e3adcf8fSFrançois Tigeot };
657e3adcf8fSFrançois Tigeot 
658e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
659e3adcf8fSFrançois Tigeot 	.destroy = intel_encoder_destroy,
660e3adcf8fSFrançois Tigeot };
661e3adcf8fSFrançois Tigeot 
66224edb884SFrançois Tigeot static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
663e3adcf8fSFrançois Tigeot {
66419df918dSFrançois Tigeot 	DRM_INFO("Skipping LVDS initialization for %s\n", id->ident);
665e3adcf8fSFrançois Tigeot 	return 1;
666e3adcf8fSFrançois Tigeot }
667e3adcf8fSFrançois Tigeot 
668e3adcf8fSFrançois Tigeot /* These systems claim to have LVDS, but really don't */
669e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_lvds[] = {
670e3adcf8fSFrançois Tigeot 	{
671e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
672e3adcf8fSFrançois Tigeot 		.ident = "Apple Mac Mini (Core series)",
673e3adcf8fSFrançois Tigeot 		.matches = {
674e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
675e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"),
676e3adcf8fSFrançois Tigeot 		},
677e3adcf8fSFrançois Tigeot 	},
678e3adcf8fSFrançois Tigeot 	{
679e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
680e3adcf8fSFrançois Tigeot 		.ident = "Apple Mac Mini (Core 2 series)",
681e3adcf8fSFrançois Tigeot 		.matches = {
682e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
683e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "Macmini2,1"),
684e3adcf8fSFrançois Tigeot 		},
685e3adcf8fSFrançois Tigeot 	},
686e3adcf8fSFrançois Tigeot 	{
687e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
688e3adcf8fSFrançois Tigeot 		.ident = "MSI IM-945GSE-A",
689e3adcf8fSFrançois Tigeot 		.matches = {
690e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "MSI"),
691e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "A9830IMS"),
692e3adcf8fSFrançois Tigeot 		},
693e3adcf8fSFrançois Tigeot 	},
694e3adcf8fSFrançois Tigeot 	{
695e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
696e3adcf8fSFrançois Tigeot 		.ident = "Dell Studio Hybrid",
697e3adcf8fSFrançois Tigeot 		.matches = {
698e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
699e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
700e3adcf8fSFrançois Tigeot 		},
701e3adcf8fSFrançois Tigeot 	},
702e3adcf8fSFrançois Tigeot 	{
703e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
704e3adcf8fSFrançois Tigeot 		.ident = "Dell OptiPlex FX170",
705e3adcf8fSFrançois Tigeot 		.matches = {
706e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
707e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"),
708e3adcf8fSFrançois Tigeot 		},
709e3adcf8fSFrançois Tigeot 	},
710e3adcf8fSFrançois Tigeot 	{
711e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
712e3adcf8fSFrançois Tigeot 		.ident = "AOpen Mini PC",
713e3adcf8fSFrançois Tigeot 		.matches = {
714e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
715e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"),
716e3adcf8fSFrançois Tigeot 		},
717e3adcf8fSFrançois Tigeot 	},
718e3adcf8fSFrançois Tigeot 	{
719e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
720e3adcf8fSFrançois Tigeot 		.ident = "AOpen Mini PC MP915",
721e3adcf8fSFrançois Tigeot 		.matches = {
722e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
723e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"),
724e3adcf8fSFrançois Tigeot 		},
725e3adcf8fSFrançois Tigeot 	},
726e3adcf8fSFrançois Tigeot 	{
727e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
728e3adcf8fSFrançois Tigeot 		.ident = "AOpen i915GMm-HFS",
729e3adcf8fSFrançois Tigeot 		.matches = {
730e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
731e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
732e3adcf8fSFrançois Tigeot 		},
733e3adcf8fSFrançois Tigeot 	},
734e3adcf8fSFrançois Tigeot 	{
735e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
736e3adcf8fSFrançois Tigeot                 .ident = "AOpen i45GMx-I",
737e3adcf8fSFrançois Tigeot                 .matches = {
738e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
739e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"),
740e3adcf8fSFrançois Tigeot                 },
741e3adcf8fSFrançois Tigeot         },
742e3adcf8fSFrançois Tigeot 	{
743e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
744e3adcf8fSFrançois Tigeot 		.ident = "Aopen i945GTt-VFA",
745e3adcf8fSFrançois Tigeot 		.matches = {
746e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"),
747e3adcf8fSFrançois Tigeot 		},
748e3adcf8fSFrançois Tigeot 	},
749e3adcf8fSFrançois Tigeot 	{
750e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
751e3adcf8fSFrançois Tigeot 		.ident = "Clientron U800",
752e3adcf8fSFrançois Tigeot 		.matches = {
753e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
754e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "U800"),
755e3adcf8fSFrançois Tigeot 		},
756e3adcf8fSFrançois Tigeot 	},
757e3adcf8fSFrançois Tigeot 	{
758e3adcf8fSFrançois Tigeot                 .callback = intel_no_lvds_dmi_callback,
759e3adcf8fSFrançois Tigeot                 .ident = "Clientron E830",
760e3adcf8fSFrançois Tigeot                 .matches = {
761e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
762e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_PRODUCT_NAME, "E830"),
763e3adcf8fSFrançois Tigeot                 },
764e3adcf8fSFrançois Tigeot         },
765e3adcf8fSFrançois Tigeot         {
766e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
767e3adcf8fSFrançois Tigeot 		.ident = "Asus EeeBox PC EB1007",
768e3adcf8fSFrançois Tigeot 		.matches = {
769e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
770e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "EB1007"),
771e3adcf8fSFrançois Tigeot 		},
772e3adcf8fSFrançois Tigeot 	},
773e3adcf8fSFrançois Tigeot 	{
774e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
775e3adcf8fSFrançois Tigeot 		.ident = "Asus AT5NM10T-I",
776e3adcf8fSFrançois Tigeot 		.matches = {
777e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
778e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"),
779e3adcf8fSFrançois Tigeot 		},
780e3adcf8fSFrançois Tigeot 	},
781e3adcf8fSFrançois Tigeot 	{
782e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
783a2fdbec6SFrançois Tigeot 		.ident = "Hewlett-Packard HP t5740",
78419df918dSFrançois Tigeot 		.matches = {
78519df918dSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
786a2fdbec6SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, " t5740"),
78719df918dSFrançois Tigeot 		},
78819df918dSFrançois Tigeot 	},
78919df918dSFrançois Tigeot 	{
79019df918dSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
791e3adcf8fSFrançois Tigeot 		.ident = "Hewlett-Packard t5745",
792e3adcf8fSFrançois Tigeot 		.matches = {
793e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
794e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "hp t5745"),
795e3adcf8fSFrançois Tigeot 		},
796e3adcf8fSFrançois Tigeot 	},
797e3adcf8fSFrançois Tigeot 	{
798e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
799e3adcf8fSFrançois Tigeot 		.ident = "Hewlett-Packard st5747",
800e3adcf8fSFrançois Tigeot 		.matches = {
801e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
802e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "hp st5747"),
803e3adcf8fSFrançois Tigeot 		},
804e3adcf8fSFrançois Tigeot 	},
805e3adcf8fSFrançois Tigeot 	{
806e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
807e3adcf8fSFrançois Tigeot 		.ident = "MSI Wind Box DC500",
808e3adcf8fSFrançois Tigeot 		.matches = {
809e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
810e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "MS-7469"),
811e3adcf8fSFrançois Tigeot 		},
812e3adcf8fSFrançois Tigeot 	},
813e3adcf8fSFrançois Tigeot 	{
814e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
81519df918dSFrançois Tigeot 		.ident = "Gigabyte GA-D525TUD",
81619df918dSFrançois Tigeot 		.matches = {
81719df918dSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
81819df918dSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "D525TUD"),
81919df918dSFrançois Tigeot 		},
82019df918dSFrançois Tigeot 	},
82119df918dSFrançois Tigeot 	{
82219df918dSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
823e3adcf8fSFrançois Tigeot 		.ident = "Supermicro X7SPA-H",
824e3adcf8fSFrançois Tigeot 		.matches = {
825e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
826e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "X7SPA-H"),
827e3adcf8fSFrançois Tigeot 		},
828e3adcf8fSFrançois Tigeot 	},
82919df918dSFrançois Tigeot 	{
83019df918dSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
83119df918dSFrançois Tigeot 		.ident = "Fujitsu Esprimo Q900",
83219df918dSFrançois Tigeot 		.matches = {
83319df918dSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
83419df918dSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"),
83519df918dSFrançois Tigeot 		},
83619df918dSFrançois Tigeot 	},
8375d0b1887SFrançois Tigeot 	{
8385d0b1887SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8399edbd4a0SFrançois Tigeot 		.ident = "Intel D410PT",
8409edbd4a0SFrançois Tigeot 		.matches = {
8419edbd4a0SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8429edbd4a0SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "D410PT"),
8439edbd4a0SFrançois Tigeot 		},
8449edbd4a0SFrançois Tigeot 	},
8459edbd4a0SFrançois Tigeot 	{
8469edbd4a0SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8479edbd4a0SFrançois Tigeot 		.ident = "Intel D425KT",
8489edbd4a0SFrançois Tigeot 		.matches = {
8499edbd4a0SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8509edbd4a0SFrançois Tigeot 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "D425KT"),
8519edbd4a0SFrançois Tigeot 		},
8529edbd4a0SFrançois Tigeot 	},
8539edbd4a0SFrançois Tigeot 	{
8549edbd4a0SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8555d0b1887SFrançois Tigeot 		.ident = "Intel D510MO",
8565d0b1887SFrançois Tigeot 		.matches = {
8575d0b1887SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8585d0b1887SFrançois Tigeot 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "D510MO"),
8595d0b1887SFrançois Tigeot 		},
8605d0b1887SFrançois Tigeot 	},
8615d0b1887SFrançois Tigeot 	{
8625d0b1887SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8635d0b1887SFrançois Tigeot 		.ident = "Intel D525MW",
8645d0b1887SFrançois Tigeot 		.matches = {
8655d0b1887SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8665d0b1887SFrançois Tigeot 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"),
8675d0b1887SFrançois Tigeot 		},
8685d0b1887SFrançois Tigeot 	},
869e3adcf8fSFrançois Tigeot 
870e3adcf8fSFrançois Tigeot 	{ }	/* terminating entry */
871e3adcf8fSFrançois Tigeot };
872e3adcf8fSFrançois Tigeot 
873a2fdbec6SFrançois Tigeot static int intel_dual_link_lvds_callback(const struct dmi_system_id *id)
874a2fdbec6SFrançois Tigeot {
875a2fdbec6SFrançois Tigeot 	DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident);
876a2fdbec6SFrançois Tigeot 	return 1;
877a2fdbec6SFrançois Tigeot }
878a2fdbec6SFrançois Tigeot 
879a2fdbec6SFrançois Tigeot static const struct dmi_system_id intel_dual_link_lvds[] = {
880a2fdbec6SFrançois Tigeot 	{
881a2fdbec6SFrançois Tigeot 		.callback = intel_dual_link_lvds_callback,
882477eb7f9SFrançois Tigeot 		.ident = "Apple MacBook Pro 15\" (2010)",
883477eb7f9SFrançois Tigeot 		.matches = {
884477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
885477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6,2"),
886477eb7f9SFrançois Tigeot 		},
887477eb7f9SFrançois Tigeot 	},
888477eb7f9SFrançois Tigeot 	{
889477eb7f9SFrançois Tigeot 		.callback = intel_dual_link_lvds_callback,
890477eb7f9SFrançois Tigeot 		.ident = "Apple MacBook Pro 15\" (2011)",
891a2fdbec6SFrançois Tigeot 		.matches = {
892a2fdbec6SFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
893a2fdbec6SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"),
894a2fdbec6SFrançois Tigeot 		},
895a2fdbec6SFrançois Tigeot 	},
896477eb7f9SFrançois Tigeot 	{
897477eb7f9SFrançois Tigeot 		.callback = intel_dual_link_lvds_callback,
898477eb7f9SFrançois Tigeot 		.ident = "Apple MacBook Pro 15\" (2012)",
899477eb7f9SFrançois Tigeot 		.matches = {
900477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
901477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro9,1"),
902477eb7f9SFrançois Tigeot 		},
903477eb7f9SFrançois Tigeot 	},
904a2fdbec6SFrançois Tigeot 	{ }	/* terminating entry */
905a2fdbec6SFrançois Tigeot };
906a2fdbec6SFrançois Tigeot 
9071487f786SFrançois Tigeot struct intel_encoder *intel_get_lvds_encoder(struct drm_device *dev)
9081487f786SFrançois Tigeot {
9091487f786SFrançois Tigeot 	struct intel_encoder *intel_encoder;
9101487f786SFrançois Tigeot 
9111487f786SFrançois Tigeot 	for_each_intel_encoder(dev, intel_encoder)
9121487f786SFrançois Tigeot 		if (intel_encoder->type == INTEL_OUTPUT_LVDS)
9131487f786SFrançois Tigeot 			return intel_encoder;
9141487f786SFrançois Tigeot 
9151487f786SFrançois Tigeot 	return NULL;
9161487f786SFrançois Tigeot }
9171487f786SFrançois Tigeot 
918a2fdbec6SFrançois Tigeot bool intel_is_dual_link_lvds(struct drm_device *dev)
919a2fdbec6SFrançois Tigeot {
9201487f786SFrançois Tigeot 	struct intel_encoder *encoder = intel_get_lvds_encoder(dev);
921a2fdbec6SFrançois Tigeot 
9221487f786SFrançois Tigeot 	return encoder && to_lvds_encoder(&encoder->base)->is_dual_link;
923a2fdbec6SFrançois Tigeot }
924a2fdbec6SFrançois Tigeot 
925a2fdbec6SFrançois Tigeot static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
926a2fdbec6SFrançois Tigeot {
927a2fdbec6SFrançois Tigeot 	struct drm_device *dev = lvds_encoder->base.base.dev;
928a2fdbec6SFrançois Tigeot 	unsigned int val;
929bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
930a2fdbec6SFrançois Tigeot 
931a2fdbec6SFrançois Tigeot 	/* use the module option value if specified */
932ba55f2f5SFrançois Tigeot 	if (i915.lvds_channel_mode > 0)
933ba55f2f5SFrançois Tigeot 		return i915.lvds_channel_mode == 2;
934a2fdbec6SFrançois Tigeot 
935477eb7f9SFrançois Tigeot 	/* single channel LVDS is limited to 112 MHz */
936477eb7f9SFrançois Tigeot 	if (lvds_encoder->attached_connector->base.panel.fixed_mode->clock
937477eb7f9SFrançois Tigeot 	    > 112999)
938477eb7f9SFrançois Tigeot 		return true;
939477eb7f9SFrançois Tigeot 
940a2fdbec6SFrançois Tigeot 	if (dmi_check_system(intel_dual_link_lvds))
941a2fdbec6SFrançois Tigeot 		return true;
942a2fdbec6SFrançois Tigeot 
943a2fdbec6SFrançois Tigeot 	/* BIOS should set the proper LVDS register value at boot, but
944a2fdbec6SFrançois Tigeot 	 * in reality, it doesn't set the value when the lid is closed;
945a2fdbec6SFrançois Tigeot 	 * we need to check "the value to be set" in VBT when LVDS
946a2fdbec6SFrançois Tigeot 	 * register is uninitialized.
947a2fdbec6SFrançois Tigeot 	 */
948a2fdbec6SFrançois Tigeot 	val = I915_READ(lvds_encoder->reg);
949a2fdbec6SFrançois Tigeot 	if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED)))
9505d0b1887SFrançois Tigeot 		val = dev_priv->vbt.bios_lvds_val;
951a2fdbec6SFrançois Tigeot 
952a2fdbec6SFrançois Tigeot 	return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
953a2fdbec6SFrançois Tigeot }
954a2fdbec6SFrançois Tigeot 
9551e12ee3bSFrançois Tigeot static bool intel_lvds_supported(struct drm_i915_private *dev_priv)
956e3adcf8fSFrançois Tigeot {
957e3adcf8fSFrançois Tigeot 	/* With the introduction of the PCH we gained a dedicated
958e3adcf8fSFrançois Tigeot 	 * LVDS presence pin, use it. */
9591e12ee3bSFrançois Tigeot 	if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv))
960e3adcf8fSFrançois Tigeot 		return true;
961e3adcf8fSFrançois Tigeot 
962e3adcf8fSFrançois Tigeot 	/* Otherwise LVDS was only attached to mobile products,
963e3adcf8fSFrançois Tigeot 	 * except for the inglorious 830gm */
9641e12ee3bSFrançois Tigeot 	if (INTEL_GEN(dev_priv) <= 4 &&
9651e12ee3bSFrançois Tigeot 	    IS_MOBILE(dev_priv) && !IS_I830(dev_priv))
9668e26cdf6SFrançois Tigeot 		return true;
9678e26cdf6SFrançois Tigeot 
9688e26cdf6SFrançois Tigeot 	return false;
969e3adcf8fSFrançois Tigeot }
970e3adcf8fSFrançois Tigeot 
971e3adcf8fSFrançois Tigeot /**
972e3adcf8fSFrançois Tigeot  * intel_lvds_init - setup LVDS connectors on this device
973e3adcf8fSFrançois Tigeot  * @dev: drm device
974e3adcf8fSFrançois Tigeot  *
975e3adcf8fSFrançois Tigeot  * Create the connector, register the LVDS DDC bus, and try to figure out what
976e3adcf8fSFrançois Tigeot  * modes we can display on the LVDS panel (if present).
977e3adcf8fSFrançois Tigeot  */
978*a85cb24fSFrançois Tigeot void intel_lvds_init(struct drm_i915_private *dev_priv)
979e3adcf8fSFrançois Tigeot {
980*a85cb24fSFrançois Tigeot 	struct drm_device *dev = &dev_priv->drm;
98119df918dSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder;
982e3adcf8fSFrançois Tigeot 	struct intel_encoder *intel_encoder;
98319df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector;
984e3adcf8fSFrançois Tigeot 	struct intel_connector *intel_connector;
985e3adcf8fSFrançois Tigeot 	struct drm_connector *connector;
986e3adcf8fSFrançois Tigeot 	struct drm_encoder *encoder;
987e3adcf8fSFrançois Tigeot 	struct drm_display_mode *scan; /* *modes, *bios_mode; */
98819df918dSFrançois Tigeot 	struct drm_display_mode *fixed_mode = NULL;
989ba55f2f5SFrançois Tigeot 	struct drm_display_mode *downclock_mode = NULL;
99019df918dSFrançois Tigeot 	struct edid *edid;
9914be47400SFrançois Tigeot 	struct intel_crtc *crtc;
992aee94f86SFrançois Tigeot 	i915_reg_t lvds_reg;
993e3adcf8fSFrançois Tigeot 	u32 lvds;
994e3adcf8fSFrançois Tigeot 	int pipe;
995e3adcf8fSFrançois Tigeot 	u8 pin;
996e3adcf8fSFrançois Tigeot 
9971e12ee3bSFrançois Tigeot 	if (!intel_lvds_supported(dev_priv))
9985d0b1887SFrançois Tigeot 		return;
999e3adcf8fSFrançois Tigeot 
1000e3adcf8fSFrançois Tigeot 	/* Skip init on machines we know falsely report LVDS */
1001e3adcf8fSFrançois Tigeot 	if (dmi_check_system(intel_no_lvds))
10025d0b1887SFrançois Tigeot 		return;
1003e3adcf8fSFrançois Tigeot 
10041e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv))
1005352ff8bdSFrançois Tigeot 		lvds_reg = PCH_LVDS;
1006352ff8bdSFrançois Tigeot 	else
1007352ff8bdSFrançois Tigeot 		lvds_reg = LVDS;
1008352ff8bdSFrançois Tigeot 
1009352ff8bdSFrançois Tigeot 	lvds = I915_READ(lvds_reg);
1010352ff8bdSFrançois Tigeot 
10111e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
1012352ff8bdSFrançois Tigeot 		if ((lvds & LVDS_DETECTED) == 0)
10135d0b1887SFrançois Tigeot 			return;
10148621f407SFrançois Tigeot 		if (dev_priv->vbt.edp.support) {
1015e3adcf8fSFrançois Tigeot 			DRM_DEBUG_KMS("disable LVDS for eDP support\n");
10165d0b1887SFrançois Tigeot 			return;
1017e3adcf8fSFrançois Tigeot 		}
1018e3adcf8fSFrançois Tigeot 	}
1019e3adcf8fSFrançois Tigeot 
1020a05eeebfSFrançois Tigeot 	pin = GMBUS_PIN_PANEL;
10218621f407SFrançois Tigeot 	if (!intel_bios_is_lvds_present(dev_priv, &pin)) {
1022352ff8bdSFrançois Tigeot 		if ((lvds & LVDS_PORT_EN) == 0) {
1023a05eeebfSFrançois Tigeot 			DRM_DEBUG_KMS("LVDS is not present in VBT\n");
1024a05eeebfSFrançois Tigeot 			return;
1025a05eeebfSFrançois Tigeot 		}
1026a05eeebfSFrançois Tigeot 		DRM_DEBUG_KMS("LVDS is not present in VBT, but enabled anyway\n");
1027a05eeebfSFrançois Tigeot 	}
1028a05eeebfSFrançois Tigeot 
10299edbd4a0SFrançois Tigeot 	lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL);
103019df918dSFrançois Tigeot 	if (!lvds_encoder)
10315d0b1887SFrançois Tigeot 		return;
1032e3adcf8fSFrançois Tigeot 
10339edbd4a0SFrançois Tigeot 	lvds_connector = kzalloc(sizeof(*lvds_connector), GFP_KERNEL);
103419df918dSFrançois Tigeot 	if (!lvds_connector) {
1035158486a6SFrançois Tigeot 		kfree(lvds_encoder);
10365d0b1887SFrançois Tigeot 		return;
1037e3adcf8fSFrançois Tigeot 	}
1038e3adcf8fSFrançois Tigeot 
1039477eb7f9SFrançois Tigeot 	if (intel_connector_init(&lvds_connector->base) < 0) {
1040477eb7f9SFrançois Tigeot 		kfree(lvds_connector);
1041477eb7f9SFrançois Tigeot 		kfree(lvds_encoder);
1042477eb7f9SFrançois Tigeot 		return;
1043477eb7f9SFrançois Tigeot 	}
1044477eb7f9SFrançois Tigeot 
104519df918dSFrançois Tigeot 	lvds_encoder->attached_connector = lvds_connector;
104619df918dSFrançois Tigeot 
104719df918dSFrançois Tigeot 	intel_encoder = &lvds_encoder->base;
1048e3adcf8fSFrançois Tigeot 	encoder = &intel_encoder->base;
104919df918dSFrançois Tigeot 	intel_connector = &lvds_connector->base;
1050e3adcf8fSFrançois Tigeot 	connector = &intel_connector->base;
1051e3adcf8fSFrançois Tigeot 	drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs,
1052e3adcf8fSFrançois Tigeot 			   DRM_MODE_CONNECTOR_LVDS);
1053e3adcf8fSFrançois Tigeot 
1054e3adcf8fSFrançois Tigeot 	drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
10551487f786SFrançois Tigeot 			 DRM_MODE_ENCODER_LVDS, "LVDS");
1056e3adcf8fSFrançois Tigeot 
105719df918dSFrançois Tigeot 	intel_encoder->enable = intel_enable_lvds;
10589edbd4a0SFrançois Tigeot 	intel_encoder->pre_enable = intel_pre_enable_lvds;
10598e26cdf6SFrançois Tigeot 	intel_encoder->compute_config = intel_lvds_compute_config;
1060a05eeebfSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
1061a05eeebfSFrançois Tigeot 		intel_encoder->disable = pch_disable_lvds;
1062a05eeebfSFrançois Tigeot 		intel_encoder->post_disable = pch_post_disable_lvds;
1063a05eeebfSFrançois Tigeot 	} else {
1064a05eeebfSFrançois Tigeot 		intel_encoder->disable = gmch_disable_lvds;
1065a05eeebfSFrançois Tigeot 	}
106619df918dSFrançois Tigeot 	intel_encoder->get_hw_state = intel_lvds_get_hw_state;
10675d0b1887SFrançois Tigeot 	intel_encoder->get_config = intel_lvds_get_config;
106819df918dSFrançois Tigeot 	intel_connector->get_hw_state = intel_connector_get_hw_state;
106919df918dSFrançois Tigeot 
1070e3adcf8fSFrançois Tigeot 	intel_connector_attach_encoder(intel_connector, intel_encoder);
1071e3adcf8fSFrançois Tigeot 
10721e12ee3bSFrançois Tigeot 	intel_encoder->type = INTEL_OUTPUT_LVDS;
1073*a85cb24fSFrançois Tigeot 	intel_encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
10741e12ee3bSFrançois Tigeot 	intel_encoder->port = PORT_NONE;
1075ba55f2f5SFrançois Tigeot 	intel_encoder->cloneable = 0;
10761e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv))
1077e3adcf8fSFrançois Tigeot 		intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
10781e12ee3bSFrançois Tigeot 	else if (IS_GEN4(dev_priv))
107919df918dSFrançois Tigeot 		intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
1080e3adcf8fSFrançois Tigeot 	else
1081e3adcf8fSFrançois Tigeot 		intel_encoder->crtc_mask = (1 << 1);
1082e3adcf8fSFrançois Tigeot 
1083e3adcf8fSFrançois Tigeot 	drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
1084e3adcf8fSFrançois Tigeot 	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
1085e3adcf8fSFrançois Tigeot 	connector->interlace_allowed = false;
1086e3adcf8fSFrançois Tigeot 	connector->doublescan_allowed = false;
1087e3adcf8fSFrançois Tigeot 
1088352ff8bdSFrançois Tigeot 	lvds_encoder->reg = lvds_reg;
1089a2fdbec6SFrançois Tigeot 
1090e3adcf8fSFrançois Tigeot 	/* create the scaling mode property */
1091e3adcf8fSFrançois Tigeot 	drm_mode_create_scaling_mode_property(dev);
1092b5162e19SFrançois Tigeot 	drm_object_attach_property(&connector->base,
1093e3adcf8fSFrançois Tigeot 				      dev->mode_config.scaling_mode_property,
1094e3adcf8fSFrançois Tigeot 				      DRM_MODE_SCALE_ASPECT);
109519df918dSFrançois Tigeot 	intel_connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT;
10961e12ee3bSFrançois Tigeot 
10971e12ee3bSFrançois Tigeot 	intel_lvds_pps_get_hw_state(dev_priv, &lvds_encoder->init_pps);
10981e12ee3bSFrançois Tigeot 	lvds_encoder->init_lvds_val = lvds;
10991e12ee3bSFrançois Tigeot 
1100e3adcf8fSFrançois Tigeot 	/*
1101e3adcf8fSFrançois Tigeot 	 * LVDS discovery:
1102e3adcf8fSFrançois Tigeot 	 * 1) check for EDID on DDC
1103e3adcf8fSFrançois Tigeot 	 * 2) check for VBT data
1104e3adcf8fSFrançois Tigeot 	 * 3) check to see if LVDS is already on
1105e3adcf8fSFrançois Tigeot 	 *    if none of the above, no panel
1106e3adcf8fSFrançois Tigeot 	 * 4) make sure lid is open
1107e3adcf8fSFrançois Tigeot 	 *    if closed, act like it's not there for now
1108e3adcf8fSFrançois Tigeot 	 */
1109e3adcf8fSFrançois Tigeot 
1110e3adcf8fSFrançois Tigeot 	/*
1111e3adcf8fSFrançois Tigeot 	 * Attempt to get the fixed panel mode from DDC.  Assume that the
1112e3adcf8fSFrançois Tigeot 	 * preferred mode is the right one.
1113e3adcf8fSFrançois Tigeot 	 */
1114ba55f2f5SFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
1115c0e85e96SFrançois Tigeot 	if (vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC)
1116c0e85e96SFrançois Tigeot 		edid = drm_get_edid_switcheroo(connector,
1117c0e85e96SFrançois Tigeot 				    intel_gmbus_get_adapter(dev_priv, pin));
1118c0e85e96SFrançois Tigeot 	else
1119c0e85e96SFrançois Tigeot 		edid = drm_get_edid(connector,
1120c0e85e96SFrançois Tigeot 				    intel_gmbus_get_adapter(dev_priv, pin));
112119df918dSFrançois Tigeot 	if (edid) {
112219df918dSFrançois Tigeot 		if (drm_add_edid_modes(connector, edid)) {
1123e3adcf8fSFrançois Tigeot 			drm_mode_connector_update_edid_property(connector,
112419df918dSFrançois Tigeot 								edid);
1125e3adcf8fSFrançois Tigeot 		} else {
1126158486a6SFrançois Tigeot 			kfree(edid);
112719df918dSFrançois Tigeot 			edid = ERR_PTR(-EINVAL);
1128e3adcf8fSFrançois Tigeot 		}
112919df918dSFrançois Tigeot 	} else {
113019df918dSFrançois Tigeot 		edid = ERR_PTR(-ENOENT);
1131e3adcf8fSFrançois Tigeot 	}
113219df918dSFrançois Tigeot 	lvds_connector->base.edid = edid;
113319df918dSFrançois Tigeot 
1134e3adcf8fSFrançois Tigeot 	list_for_each_entry(scan, &connector->probed_modes, head) {
1135e3adcf8fSFrançois Tigeot 		if (scan->type & DRM_MODE_TYPE_PREFERRED) {
113619df918dSFrançois Tigeot 			DRM_DEBUG_KMS("using preferred mode from EDID: ");
113719df918dSFrançois Tigeot 			drm_mode_debug_printmodeline(scan);
113819df918dSFrançois Tigeot 
113919df918dSFrançois Tigeot 			fixed_mode = drm_mode_duplicate(dev, scan);
1140a05eeebfSFrançois Tigeot 			if (fixed_mode)
1141e3adcf8fSFrançois Tigeot 				goto out;
1142e3adcf8fSFrançois Tigeot 		}
1143e3adcf8fSFrançois Tigeot 	}
1144e3adcf8fSFrançois Tigeot 
1145e3adcf8fSFrançois Tigeot 	/* Failed to get EDID, what about VBT? */
11465d0b1887SFrançois Tigeot 	if (dev_priv->vbt.lfp_lvds_vbt_mode) {
114719df918dSFrançois Tigeot 		DRM_DEBUG_KMS("using mode from VBT: ");
11485d0b1887SFrançois Tigeot 		drm_mode_debug_printmodeline(dev_priv->vbt.lfp_lvds_vbt_mode);
114919df918dSFrançois Tigeot 
11505d0b1887SFrançois Tigeot 		fixed_mode = drm_mode_duplicate(dev, dev_priv->vbt.lfp_lvds_vbt_mode);
115119df918dSFrançois Tigeot 		if (fixed_mode) {
115219df918dSFrançois Tigeot 			fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
11538621f407SFrançois Tigeot 			connector->display_info.width_mm = fixed_mode->width_mm;
11548621f407SFrançois Tigeot 			connector->display_info.height_mm = fixed_mode->height_mm;
1155e3adcf8fSFrançois Tigeot 			goto out;
1156e3adcf8fSFrançois Tigeot 		}
1157e3adcf8fSFrançois Tigeot 	}
1158e3adcf8fSFrançois Tigeot 
1159e3adcf8fSFrançois Tigeot 	/*
1160e3adcf8fSFrançois Tigeot 	 * If we didn't get EDID, try checking if the panel is already turned
1161e3adcf8fSFrançois Tigeot 	 * on.  If so, assume that whatever is currently programmed is the
1162e3adcf8fSFrançois Tigeot 	 * correct mode.
1163e3adcf8fSFrançois Tigeot 	 */
1164e3adcf8fSFrançois Tigeot 
1165e3adcf8fSFrançois Tigeot 	/* Ironlake: FIXME if still fail, not try pipe mode now */
11661e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv))
1167e3adcf8fSFrançois Tigeot 		goto failed;
1168e3adcf8fSFrançois Tigeot 
1169e3adcf8fSFrançois Tigeot 	pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
11704be47400SFrançois Tigeot 	crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
1171e3adcf8fSFrançois Tigeot 
1172e3adcf8fSFrançois Tigeot 	if (crtc && (lvds & LVDS_PORT_EN)) {
11734be47400SFrançois Tigeot 		fixed_mode = intel_crtc_mode_get(dev, &crtc->base);
117419df918dSFrançois Tigeot 		if (fixed_mode) {
117519df918dSFrançois Tigeot 			DRM_DEBUG_KMS("using current (BIOS) mode: ");
117619df918dSFrançois Tigeot 			drm_mode_debug_printmodeline(fixed_mode);
117719df918dSFrançois Tigeot 			fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
1178e3adcf8fSFrançois Tigeot 			goto out;
1179e3adcf8fSFrançois Tigeot 		}
1180e3adcf8fSFrançois Tigeot 	}
1181e3adcf8fSFrançois Tigeot 
1182e3adcf8fSFrançois Tigeot 	/* If we still don't have a mode after all that, give up. */
118319df918dSFrançois Tigeot 	if (!fixed_mode)
1184e3adcf8fSFrançois Tigeot 		goto failed;
1185e3adcf8fSFrançois Tigeot 
1186e3adcf8fSFrançois Tigeot out:
1187ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
1188ba55f2f5SFrançois Tigeot 
1189477eb7f9SFrançois Tigeot 	intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
11901487f786SFrançois Tigeot 	intel_panel_setup_backlight(connector, INVALID_PIPE);
1191477eb7f9SFrançois Tigeot 
1192a2fdbec6SFrançois Tigeot 	lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder);
1193a2fdbec6SFrançois Tigeot 	DRM_DEBUG_KMS("detected %s-link lvds configuration\n",
1194a2fdbec6SFrançois Tigeot 		      lvds_encoder->is_dual_link ? "dual" : "single");
1195a2fdbec6SFrançois Tigeot 
1196aee94f86SFrançois Tigeot 	lvds_encoder->a3_power = lvds & LVDS_A3_POWER_MASK;
119724edb884SFrançois Tigeot 
1198e3adcf8fSFrançois Tigeot #if 0
119919df918dSFrançois Tigeot 	lvds_connector->lid_notifier.notifier_call = intel_lid_notify;
120019df918dSFrançois Tigeot 	if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) {
120119df918dSFrançois Tigeot 		DRM_DEBUG_KMS("lid notifier registration failed\n");
120219df918dSFrançois Tigeot 		lvds_connector->lid_notifier.notifier_call = NULL;
120319df918dSFrançois Tigeot 	}
1204e3adcf8fSFrançois Tigeot #endif
120519df918dSFrançois Tigeot 
12065d0b1887SFrançois Tigeot 	return;
1207e3adcf8fSFrançois Tigeot 
1208e3adcf8fSFrançois Tigeot failed:
1209ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
1210ba55f2f5SFrançois Tigeot 
1211e3adcf8fSFrançois Tigeot 	DRM_DEBUG_KMS("No LVDS modes found, disabling.\n");
1212e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
1213e3adcf8fSFrançois Tigeot 	drm_encoder_cleanup(encoder);
1214158486a6SFrançois Tigeot 	kfree(lvds_encoder);
1215158486a6SFrançois Tigeot 	kfree(lvds_connector);
12165d0b1887SFrançois Tigeot 	return;
1217e3adcf8fSFrançois Tigeot }
1218