xref: /dflybsd-src/sys/dev/drm/i915/intel_lvds.c (revision 4be47400997875399098ad904fb4ca65b3244cca)
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);
9524edb884SFrançois Tigeot 	enum intel_display_power_domain power_domain;
96a2fdbec6SFrançois Tigeot 	u32 tmp;
97aee94f86SFrançois Tigeot 	bool ret;
9819df918dSFrançois Tigeot 
9924edb884SFrançois Tigeot 	power_domain = intel_display_port_power_domain(encoder);
100aee94f86SFrançois Tigeot 	if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
10124edb884SFrançois Tigeot 		return false;
10224edb884SFrançois Tigeot 
103aee94f86SFrançois Tigeot 	ret = false;
104aee94f86SFrançois Tigeot 
105a2fdbec6SFrançois Tigeot 	tmp = I915_READ(lvds_encoder->reg);
10619df918dSFrançois Tigeot 
10719df918dSFrançois Tigeot 	if (!(tmp & LVDS_PORT_EN))
108aee94f86SFrançois Tigeot 		goto out;
10919df918dSFrançois Tigeot 
1101e12ee3bSFrançois Tigeot 	if (HAS_PCH_CPT(dev_priv))
11119df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE_CPT(tmp);
11219df918dSFrançois Tigeot 	else
11319df918dSFrançois Tigeot 		*pipe = PORT_TO_PIPE(tmp);
11419df918dSFrançois Tigeot 
115aee94f86SFrançois Tigeot 	ret = true;
116aee94f86SFrançois Tigeot 
117aee94f86SFrançois Tigeot out:
118aee94f86SFrançois Tigeot 	intel_display_power_put(dev_priv, power_domain);
119aee94f86SFrançois Tigeot 
120aee94f86SFrançois Tigeot 	return ret;
121e3adcf8fSFrançois Tigeot }
122e3adcf8fSFrançois Tigeot 
1235d0b1887SFrançois Tigeot static void intel_lvds_get_config(struct intel_encoder *encoder,
1242c9916cdSFrançois Tigeot 				  struct intel_crtc_state *pipe_config)
1255d0b1887SFrançois Tigeot {
126*4be47400SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
127352ff8bdSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
128352ff8bdSFrançois Tigeot 	u32 tmp, flags = 0;
1295d0b1887SFrançois Tigeot 
130352ff8bdSFrançois Tigeot 	tmp = I915_READ(lvds_encoder->reg);
1315d0b1887SFrançois Tigeot 	if (tmp & LVDS_HSYNC_POLARITY)
1325d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NHSYNC;
1335d0b1887SFrançois Tigeot 	else
1345d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PHSYNC;
1355d0b1887SFrançois Tigeot 	if (tmp & LVDS_VSYNC_POLARITY)
1365d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_NVSYNC;
1375d0b1887SFrançois Tigeot 	else
1385d0b1887SFrançois Tigeot 		flags |= DRM_MODE_FLAG_PVSYNC;
1395d0b1887SFrançois Tigeot 
1402c9916cdSFrançois Tigeot 	pipe_config->base.adjusted_mode.flags |= flags;
1415d0b1887SFrançois Tigeot 
142*4be47400SFrançois Tigeot 	if (INTEL_GEN(dev_priv) < 5)
143c0e85e96SFrançois Tigeot 		pipe_config->gmch_pfit.lvds_border_bits =
144c0e85e96SFrançois Tigeot 			tmp & LVDS_BORDER_ENABLE;
145c0e85e96SFrançois Tigeot 
1465d0b1887SFrançois Tigeot 	/* gen2/3 store dither state in pfit control, needs to match */
147*4be47400SFrançois Tigeot 	if (INTEL_GEN(dev_priv) < 4) {
1485d0b1887SFrançois Tigeot 		tmp = I915_READ(PFIT_CONTROL);
1495d0b1887SFrançois Tigeot 
1505d0b1887SFrançois Tigeot 		pipe_config->gmch_pfit.control |= tmp & PANEL_8TO6_DITHER_ENABLE;
1515d0b1887SFrançois Tigeot 	}
1529edbd4a0SFrançois Tigeot 
1538621f407SFrançois Tigeot 	pipe_config->base.adjusted_mode.crtc_clock = pipe_config->port_clock;
1545d0b1887SFrançois Tigeot }
1555d0b1887SFrançois Tigeot 
1561e12ee3bSFrançois Tigeot static void intel_lvds_pps_get_hw_state(struct drm_i915_private *dev_priv,
1571e12ee3bSFrançois Tigeot 					struct intel_lvds_pps *pps)
1581e12ee3bSFrançois Tigeot {
1591e12ee3bSFrançois Tigeot 	u32 val;
1601e12ee3bSFrançois Tigeot 
1611e12ee3bSFrançois Tigeot 	pps->powerdown_on_reset = I915_READ(PP_CONTROL(0)) & PANEL_POWER_RESET;
1621e12ee3bSFrançois Tigeot 
1631e12ee3bSFrançois Tigeot 	val = I915_READ(PP_ON_DELAYS(0));
1641e12ee3bSFrançois Tigeot 	pps->port = (val & PANEL_PORT_SELECT_MASK) >>
1651e12ee3bSFrançois Tigeot 		    PANEL_PORT_SELECT_SHIFT;
1661e12ee3bSFrançois Tigeot 	pps->t1_t2 = (val & PANEL_POWER_UP_DELAY_MASK) >>
1671e12ee3bSFrançois Tigeot 		     PANEL_POWER_UP_DELAY_SHIFT;
1681e12ee3bSFrançois Tigeot 	pps->t5 = (val & PANEL_LIGHT_ON_DELAY_MASK) >>
1691e12ee3bSFrançois Tigeot 		  PANEL_LIGHT_ON_DELAY_SHIFT;
1701e12ee3bSFrançois Tigeot 
1711e12ee3bSFrançois Tigeot 	val = I915_READ(PP_OFF_DELAYS(0));
1721e12ee3bSFrançois Tigeot 	pps->t3 = (val & PANEL_POWER_DOWN_DELAY_MASK) >>
1731e12ee3bSFrançois Tigeot 		  PANEL_POWER_DOWN_DELAY_SHIFT;
1741e12ee3bSFrançois Tigeot 	pps->tx = (val & PANEL_LIGHT_OFF_DELAY_MASK) >>
1751e12ee3bSFrançois Tigeot 		  PANEL_LIGHT_OFF_DELAY_SHIFT;
1761e12ee3bSFrançois Tigeot 
1771e12ee3bSFrançois Tigeot 	val = I915_READ(PP_DIVISOR(0));
1781e12ee3bSFrançois Tigeot 	pps->divider = (val & PP_REFERENCE_DIVIDER_MASK) >>
1791e12ee3bSFrançois Tigeot 		       PP_REFERENCE_DIVIDER_SHIFT;
1801e12ee3bSFrançois Tigeot 	val = (val & PANEL_POWER_CYCLE_DELAY_MASK) >>
1811e12ee3bSFrançois Tigeot 	      PANEL_POWER_CYCLE_DELAY_SHIFT;
1821e12ee3bSFrançois Tigeot 	/*
1831e12ee3bSFrançois Tigeot 	 * Remove the BSpec specified +1 (100ms) offset that accounts for a
1841e12ee3bSFrançois Tigeot 	 * too short power-cycle delay due to the asynchronous programming of
1851e12ee3bSFrançois Tigeot 	 * the register.
1861e12ee3bSFrançois Tigeot 	 */
1871e12ee3bSFrançois Tigeot 	if (val)
1881e12ee3bSFrançois Tigeot 		val--;
1891e12ee3bSFrançois Tigeot 	/* Convert from 100ms to 100us units */
1901e12ee3bSFrançois Tigeot 	pps->t4 = val * 1000;
1911e12ee3bSFrançois Tigeot 
1921e12ee3bSFrançois Tigeot 	if (INTEL_INFO(dev_priv)->gen <= 4 &&
1931e12ee3bSFrançois Tigeot 	    pps->t1_t2 == 0 && pps->t5 == 0 && pps->t3 == 0 && pps->tx == 0) {
1941e12ee3bSFrançois Tigeot 		DRM_DEBUG_KMS("Panel power timings uninitialized, "
1951e12ee3bSFrançois Tigeot 			      "setting defaults\n");
1961e12ee3bSFrançois Tigeot 		/* Set T2 to 40ms and T5 to 200ms in 100 usec units */
1971e12ee3bSFrançois Tigeot 		pps->t1_t2 = 40 * 10;
1981e12ee3bSFrançois Tigeot 		pps->t5 = 200 * 10;
1991e12ee3bSFrançois Tigeot 		/* Set T3 to 35ms and Tx to 200ms in 100 usec units */
2001e12ee3bSFrançois Tigeot 		pps->t3 = 35 * 10;
2011e12ee3bSFrançois Tigeot 		pps->tx = 200 * 10;
2021e12ee3bSFrançois Tigeot 	}
2031e12ee3bSFrançois Tigeot 
2041e12ee3bSFrançois Tigeot 	DRM_DEBUG_DRIVER("LVDS PPS:t1+t2 %d t3 %d t4 %d t5 %d tx %d "
2051e12ee3bSFrançois Tigeot 			 "divider %d port %d powerdown_on_reset %d\n",
2061e12ee3bSFrançois Tigeot 			 pps->t1_t2, pps->t3, pps->t4, pps->t5, pps->tx,
2071e12ee3bSFrançois Tigeot 			 pps->divider, pps->port, pps->powerdown_on_reset);
2081e12ee3bSFrançois Tigeot }
2091e12ee3bSFrançois Tigeot 
2101e12ee3bSFrançois Tigeot static void intel_lvds_pps_init_hw(struct drm_i915_private *dev_priv,
2111e12ee3bSFrançois Tigeot 				   struct intel_lvds_pps *pps)
2121e12ee3bSFrançois Tigeot {
2131e12ee3bSFrançois Tigeot 	u32 val;
2141e12ee3bSFrançois Tigeot 
2151e12ee3bSFrançois Tigeot 	val = I915_READ(PP_CONTROL(0));
2161e12ee3bSFrançois Tigeot 	WARN_ON((val & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS);
2171e12ee3bSFrançois Tigeot 	if (pps->powerdown_on_reset)
2181e12ee3bSFrançois Tigeot 		val |= PANEL_POWER_RESET;
2191e12ee3bSFrançois Tigeot 	I915_WRITE(PP_CONTROL(0), val);
2201e12ee3bSFrançois Tigeot 
2211e12ee3bSFrançois Tigeot 	I915_WRITE(PP_ON_DELAYS(0), (pps->port << PANEL_PORT_SELECT_SHIFT) |
2221e12ee3bSFrançois Tigeot 				    (pps->t1_t2 << PANEL_POWER_UP_DELAY_SHIFT) |
2231e12ee3bSFrançois Tigeot 				    (pps->t5 << PANEL_LIGHT_ON_DELAY_SHIFT));
2241e12ee3bSFrançois Tigeot 	I915_WRITE(PP_OFF_DELAYS(0), (pps->t3 << PANEL_POWER_DOWN_DELAY_SHIFT) |
2251e12ee3bSFrançois Tigeot 				     (pps->tx << PANEL_LIGHT_OFF_DELAY_SHIFT));
2261e12ee3bSFrançois Tigeot 
2271e12ee3bSFrançois Tigeot 	val = pps->divider << PP_REFERENCE_DIVIDER_SHIFT;
2281e12ee3bSFrançois Tigeot 	val |= (DIV_ROUND_UP(pps->t4, 1000) + 1) <<
2291e12ee3bSFrançois Tigeot 	       PANEL_POWER_CYCLE_DELAY_SHIFT;
2301e12ee3bSFrançois Tigeot 	I915_WRITE(PP_DIVISOR(0), val);
2311e12ee3bSFrançois Tigeot }
2321e12ee3bSFrançois Tigeot 
2331e12ee3bSFrançois Tigeot static void intel_pre_enable_lvds(struct intel_encoder *encoder,
2341e12ee3bSFrançois Tigeot 				  struct intel_crtc_state *pipe_config,
2351e12ee3bSFrançois Tigeot 				  struct drm_connector_state *conn_state)
236a2fdbec6SFrançois Tigeot {
237a2fdbec6SFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
2381e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
2391e12ee3bSFrançois Tigeot 	struct intel_crtc *crtc = to_intel_crtc(pipe_config->base.crtc);
2401e12ee3bSFrançois Tigeot 	const struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
2419edbd4a0SFrançois Tigeot 	int pipe = crtc->pipe;
242a2fdbec6SFrançois Tigeot 	u32 temp;
243a2fdbec6SFrançois Tigeot 
2441e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
2459edbd4a0SFrançois Tigeot 		assert_fdi_rx_pll_disabled(dev_priv, pipe);
2469edbd4a0SFrançois Tigeot 		assert_shared_dpll_disabled(dev_priv,
2471e12ee3bSFrançois Tigeot 					    pipe_config->shared_dpll);
2489edbd4a0SFrançois Tigeot 	} else {
2499edbd4a0SFrançois Tigeot 		assert_pll_disabled(dev_priv, pipe);
2509edbd4a0SFrançois Tigeot 	}
2519edbd4a0SFrançois Tigeot 
2521e12ee3bSFrançois Tigeot 	intel_lvds_pps_init_hw(dev_priv, &lvds_encoder->init_pps);
2531e12ee3bSFrançois Tigeot 
2541e12ee3bSFrançois Tigeot 	temp = lvds_encoder->init_lvds_val;
255a2fdbec6SFrançois Tigeot 	temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
256a2fdbec6SFrançois Tigeot 
2571e12ee3bSFrançois Tigeot 	if (HAS_PCH_CPT(dev_priv)) {
258a2fdbec6SFrançois Tigeot 		temp &= ~PORT_TRANS_SEL_MASK;
259a2fdbec6SFrançois Tigeot 		temp |= PORT_TRANS_SEL_CPT(pipe);
260a2fdbec6SFrançois Tigeot 	} else {
261a2fdbec6SFrançois Tigeot 		if (pipe == 1) {
262a2fdbec6SFrançois Tigeot 			temp |= LVDS_PIPEB_SELECT;
263a2fdbec6SFrançois Tigeot 		} else {
264a2fdbec6SFrançois Tigeot 			temp &= ~LVDS_PIPEB_SELECT;
265a2fdbec6SFrançois Tigeot 		}
266a2fdbec6SFrançois Tigeot 	}
267a2fdbec6SFrançois Tigeot 
268a2fdbec6SFrançois Tigeot 	/* set the corresponsding LVDS_BORDER bit */
2695d0b1887SFrançois Tigeot 	temp &= ~LVDS_BORDER_ENABLE;
2701e12ee3bSFrançois Tigeot 	temp |= pipe_config->gmch_pfit.lvds_border_bits;
271a2fdbec6SFrançois Tigeot 	/* Set the B0-B3 data pairs corresponding to whether we're going to
272a2fdbec6SFrançois Tigeot 	 * set the DPLLs for dual-channel mode or not.
273a2fdbec6SFrançois Tigeot 	 */
274a2fdbec6SFrançois Tigeot 	if (lvds_encoder->is_dual_link)
275a2fdbec6SFrançois Tigeot 		temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
276a2fdbec6SFrançois Tigeot 	else
277a2fdbec6SFrançois Tigeot 		temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
278a2fdbec6SFrançois Tigeot 
279a2fdbec6SFrançois Tigeot 	/* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
280a2fdbec6SFrançois Tigeot 	 * appropriately here, but we need to look more thoroughly into how
28124edb884SFrançois Tigeot 	 * panels behave in the two modes. For now, let's just maintain the
28224edb884SFrançois Tigeot 	 * value we got from the BIOS.
283a2fdbec6SFrançois Tigeot 	 */
28424edb884SFrançois Tigeot 	temp &= ~LVDS_A3_POWER_MASK;
28524edb884SFrançois Tigeot 	temp |= lvds_encoder->a3_power;
286a2fdbec6SFrançois Tigeot 
287a2fdbec6SFrançois Tigeot 	/* Set the dithering flag on LVDS as needed, note that there is no
288a2fdbec6SFrançois Tigeot 	 * special lvds dither control bit on pch-split platforms, dithering is
289a2fdbec6SFrançois Tigeot 	 * only controlled through the PIPECONF reg. */
2901487f786SFrançois Tigeot 	if (IS_GEN4(dev_priv)) {
2915d0b1887SFrançois Tigeot 		/* Bspec wording suggests that LVDS port dithering only exists
2925d0b1887SFrançois Tigeot 		 * for 18bpp panels. */
2931e12ee3bSFrançois Tigeot 		if (pipe_config->dither && pipe_config->pipe_bpp == 18)
294a2fdbec6SFrançois Tigeot 			temp |= LVDS_ENABLE_DITHER;
295a2fdbec6SFrançois Tigeot 		else
296a2fdbec6SFrançois Tigeot 			temp &= ~LVDS_ENABLE_DITHER;
297a2fdbec6SFrançois Tigeot 	}
298a2fdbec6SFrançois Tigeot 	temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
2999edbd4a0SFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
300a2fdbec6SFrançois Tigeot 		temp |= LVDS_HSYNC_POLARITY;
3019edbd4a0SFrançois Tigeot 	if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
302a2fdbec6SFrançois Tigeot 		temp |= LVDS_VSYNC_POLARITY;
303a2fdbec6SFrançois Tigeot 
304a2fdbec6SFrançois Tigeot 	I915_WRITE(lvds_encoder->reg, temp);
305a2fdbec6SFrançois Tigeot }
306a2fdbec6SFrançois Tigeot 
307e3adcf8fSFrançois Tigeot /**
308e3adcf8fSFrançois Tigeot  * Sets the power state for the panel.
309e3adcf8fSFrançois Tigeot  */
3101e12ee3bSFrançois Tigeot static void intel_enable_lvds(struct intel_encoder *encoder,
3111e12ee3bSFrançois Tigeot 			      struct intel_crtc_state *pipe_config,
3121e12ee3bSFrançois Tigeot 			      struct drm_connector_state *conn_state)
313e3adcf8fSFrançois Tigeot {
31419df918dSFrançois Tigeot 	struct drm_device *dev = encoder->base.dev;
31519df918dSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
3169edbd4a0SFrançois Tigeot 	struct intel_connector *intel_connector =
3179edbd4a0SFrançois Tigeot 		&lvds_encoder->attached_connector->base;
318bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
319e3adcf8fSFrançois Tigeot 
320a2fdbec6SFrançois Tigeot 	I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN);
321e3adcf8fSFrançois Tigeot 
3221e12ee3bSFrançois Tigeot 	I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) | PANEL_POWER_ON);
323a2fdbec6SFrançois Tigeot 	POSTING_READ(lvds_encoder->reg);
3241e12ee3bSFrançois Tigeot 	if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, PP_ON, 1000))
32519df918dSFrançois Tigeot 		DRM_ERROR("timed out waiting for panel to power on\n");
326e3adcf8fSFrançois Tigeot 
3279edbd4a0SFrançois Tigeot 	intel_panel_enable_backlight(intel_connector);
328e3adcf8fSFrançois Tigeot }
329e3adcf8fSFrançois Tigeot 
3301e12ee3bSFrançois Tigeot static void intel_disable_lvds(struct intel_encoder *encoder,
3311e12ee3bSFrançois Tigeot 			       struct intel_crtc_state *old_crtc_state,
3321e12ee3bSFrançois Tigeot 			       struct drm_connector_state *old_conn_state)
333e3adcf8fSFrançois Tigeot {
33419df918dSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
3351e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
336e3adcf8fSFrançois Tigeot 
3371e12ee3bSFrançois Tigeot 	I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) & ~PANEL_POWER_ON);
3381e12ee3bSFrançois Tigeot 	if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, 0, 1000))
339e3adcf8fSFrançois Tigeot 		DRM_ERROR("timed out waiting for panel to power off\n");
340e3adcf8fSFrançois Tigeot 
341a2fdbec6SFrançois Tigeot 	I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN);
342a2fdbec6SFrançois Tigeot 	POSTING_READ(lvds_encoder->reg);
343e3adcf8fSFrançois Tigeot }
344e3adcf8fSFrançois Tigeot 
3451e12ee3bSFrançois Tigeot static void gmch_disable_lvds(struct intel_encoder *encoder,
3461e12ee3bSFrançois Tigeot 			      struct intel_crtc_state *old_crtc_state,
3471e12ee3bSFrançois Tigeot 			      struct drm_connector_state *old_conn_state)
3481e12ee3bSFrançois Tigeot 
349a05eeebfSFrançois Tigeot {
350a05eeebfSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
351a05eeebfSFrançois Tigeot 	struct intel_connector *intel_connector =
352a05eeebfSFrançois Tigeot 		&lvds_encoder->attached_connector->base;
353a05eeebfSFrançois Tigeot 
354a05eeebfSFrançois Tigeot 	intel_panel_disable_backlight(intel_connector);
355a05eeebfSFrançois Tigeot 
3561e12ee3bSFrançois Tigeot 	intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
357a05eeebfSFrançois Tigeot }
358a05eeebfSFrançois Tigeot 
3591e12ee3bSFrançois Tigeot static void pch_disable_lvds(struct intel_encoder *encoder,
3601e12ee3bSFrançois Tigeot 			     struct intel_crtc_state *old_crtc_state,
3611e12ee3bSFrançois Tigeot 			     struct drm_connector_state *old_conn_state)
362a05eeebfSFrançois Tigeot {
363a05eeebfSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
364a05eeebfSFrançois Tigeot 	struct intel_connector *intel_connector =
365a05eeebfSFrançois Tigeot 		&lvds_encoder->attached_connector->base;
366a05eeebfSFrançois Tigeot 
367a05eeebfSFrançois Tigeot 	intel_panel_disable_backlight(intel_connector);
368a05eeebfSFrançois Tigeot }
369a05eeebfSFrançois Tigeot 
3701e12ee3bSFrançois Tigeot static void pch_post_disable_lvds(struct intel_encoder *encoder,
3711e12ee3bSFrançois Tigeot 				  struct intel_crtc_state *old_crtc_state,
3721e12ee3bSFrançois Tigeot 				  struct drm_connector_state *old_conn_state)
373a05eeebfSFrançois Tigeot {
3741e12ee3bSFrançois Tigeot 	intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
375a05eeebfSFrançois Tigeot }
376a05eeebfSFrançois Tigeot 
3779edbd4a0SFrançois Tigeot static enum drm_mode_status
3789edbd4a0SFrançois Tigeot intel_lvds_mode_valid(struct drm_connector *connector,
379e3adcf8fSFrançois Tigeot 		      struct drm_display_mode *mode)
380e3adcf8fSFrançois Tigeot {
38119df918dSFrançois Tigeot 	struct intel_connector *intel_connector = to_intel_connector(connector);
38219df918dSFrançois Tigeot 	struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
383352ff8bdSFrançois Tigeot 	int max_pixclk = to_i915(connector->dev)->max_dotclk_freq;
384e3adcf8fSFrançois Tigeot 
385e3adcf8fSFrançois Tigeot 	if (mode->hdisplay > fixed_mode->hdisplay)
386e3adcf8fSFrançois Tigeot 		return MODE_PANEL;
387e3adcf8fSFrançois Tigeot 	if (mode->vdisplay > fixed_mode->vdisplay)
388e3adcf8fSFrançois Tigeot 		return MODE_PANEL;
389352ff8bdSFrançois Tigeot 	if (fixed_mode->clock > max_pixclk)
390352ff8bdSFrançois Tigeot 		return MODE_CLOCK_HIGH;
391e3adcf8fSFrançois Tigeot 
392e3adcf8fSFrançois Tigeot 	return MODE_OK;
393e3adcf8fSFrançois Tigeot }
394e3adcf8fSFrançois Tigeot 
3958e26cdf6SFrançois Tigeot static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
3961e12ee3bSFrançois Tigeot 				      struct intel_crtc_state *pipe_config,
3971e12ee3bSFrançois Tigeot 				      struct drm_connector_state *conn_state)
398e3adcf8fSFrançois Tigeot {
3991e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(intel_encoder->base.dev);
4008e26cdf6SFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder =
4018e26cdf6SFrançois Tigeot 		to_lvds_encoder(&intel_encoder->base);
40219df918dSFrançois Tigeot 	struct intel_connector *intel_connector =
40319df918dSFrançois Tigeot 		&lvds_encoder->attached_connector->base;
4042c9916cdSFrançois Tigeot 	struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
405477eb7f9SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc);
4068e26cdf6SFrançois Tigeot 	unsigned int lvds_bpp;
407e3adcf8fSFrançois Tigeot 
408e3adcf8fSFrançois Tigeot 	/* Should never happen!! */
4091e12ee3bSFrançois Tigeot 	if (INTEL_GEN(dev_priv) < 4 && intel_crtc->pipe == 0) {
410e3adcf8fSFrançois Tigeot 		DRM_ERROR("Can't support LVDS on pipe A\n");
411e3adcf8fSFrançois Tigeot 		return false;
412e3adcf8fSFrançois Tigeot 	}
413e3adcf8fSFrançois Tigeot 
41424edb884SFrançois Tigeot 	if (lvds_encoder->a3_power == LVDS_A3_POWER_UP)
4158e26cdf6SFrançois Tigeot 		lvds_bpp = 8*3;
4168e26cdf6SFrançois Tigeot 	else
4178e26cdf6SFrançois Tigeot 		lvds_bpp = 6*3;
4188e26cdf6SFrançois Tigeot 
4195d0b1887SFrançois Tigeot 	if (lvds_bpp != pipe_config->pipe_bpp && !pipe_config->bw_constrained) {
4208e26cdf6SFrançois Tigeot 		DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n",
4218e26cdf6SFrançois Tigeot 			      pipe_config->pipe_bpp, lvds_bpp);
4228e26cdf6SFrançois Tigeot 		pipe_config->pipe_bpp = lvds_bpp;
4238e26cdf6SFrançois Tigeot 	}
4245d0b1887SFrançois Tigeot 
425e3adcf8fSFrançois Tigeot 	/*
426e3adcf8fSFrançois Tigeot 	 * We have timings from the BIOS for the panel, put them in
427e3adcf8fSFrançois Tigeot 	 * to the adjusted mode.  The CRTC will be set up for this mode,
428e3adcf8fSFrançois Tigeot 	 * with the panel scaling set up to source from the H/VDisplay
429e3adcf8fSFrançois Tigeot 	 * of the original mode.
430e3adcf8fSFrançois Tigeot 	 */
43119df918dSFrançois Tigeot 	intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
43219df918dSFrançois Tigeot 			       adjusted_mode);
433e3adcf8fSFrançois Tigeot 
4341e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
4358e26cdf6SFrançois Tigeot 		pipe_config->has_pch_encoder = true;
4368e26cdf6SFrançois Tigeot 
4375d0b1887SFrançois Tigeot 		intel_pch_panel_fitting(intel_crtc, pipe_config,
4385d0b1887SFrançois Tigeot 					intel_connector->panel.fitting_mode);
439e3adcf8fSFrançois Tigeot 	} else {
4405d0b1887SFrançois Tigeot 		intel_gmch_panel_fitting(intel_crtc, pipe_config,
4415d0b1887SFrançois Tigeot 					 intel_connector->panel.fitting_mode);
442e3adcf8fSFrançois Tigeot 
443e3adcf8fSFrançois Tigeot 	}
444e3adcf8fSFrançois Tigeot 
445e3adcf8fSFrançois Tigeot 	/*
446e3adcf8fSFrançois Tigeot 	 * XXX: It would be nice to support lower refresh rates on the
447e3adcf8fSFrançois Tigeot 	 * panels to reduce power consumption, and perhaps match the
448e3adcf8fSFrançois Tigeot 	 * user's requested refresh rate.
449e3adcf8fSFrançois Tigeot 	 */
450e3adcf8fSFrançois Tigeot 
451e3adcf8fSFrançois Tigeot 	return true;
452e3adcf8fSFrançois Tigeot }
453e3adcf8fSFrançois Tigeot 
454e3adcf8fSFrançois Tigeot /**
455e3adcf8fSFrançois Tigeot  * Detect the LVDS connection.
456e3adcf8fSFrançois Tigeot  *
457e3adcf8fSFrançois Tigeot  * Since LVDS doesn't have hotlug, we use the lid as a proxy.  Open means
458e3adcf8fSFrançois Tigeot  * connected and closed means disconnected.  We also send hotplug events as
459e3adcf8fSFrançois Tigeot  * needed, using lid status notification from the input layer.
460e3adcf8fSFrançois Tigeot  */
461e3adcf8fSFrançois Tigeot static enum drm_connector_status
462e3adcf8fSFrançois Tigeot intel_lvds_detect(struct drm_connector *connector, bool force)
463e3adcf8fSFrançois Tigeot {
464e3adcf8fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
465e3adcf8fSFrançois Tigeot 	enum drm_connector_status status;
466e3adcf8fSFrançois Tigeot 
4679edbd4a0SFrançois Tigeot 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
468b4efbf42Szrj 		      connector->base.id, connector->name);
4699edbd4a0SFrançois Tigeot 
470e3adcf8fSFrançois Tigeot 	status = intel_panel_detect(dev);
471e3adcf8fSFrançois Tigeot 	if (status != connector_status_unknown)
472e3adcf8fSFrançois Tigeot 		return status;
473e3adcf8fSFrançois Tigeot 
474e3adcf8fSFrançois Tigeot 	return connector_status_connected;
475e3adcf8fSFrançois Tigeot }
476e3adcf8fSFrançois Tigeot 
477e3adcf8fSFrançois Tigeot /**
478e3adcf8fSFrançois Tigeot  * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
479e3adcf8fSFrançois Tigeot  */
480e3adcf8fSFrançois Tigeot static int intel_lvds_get_modes(struct drm_connector *connector)
481e3adcf8fSFrançois Tigeot {
48219df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector = to_lvds_connector(connector);
483e3adcf8fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
484e3adcf8fSFrançois Tigeot 	struct drm_display_mode *mode;
485e3adcf8fSFrançois Tigeot 
48619df918dSFrançois Tigeot 	/* use cached edid if we have one */
48719df918dSFrançois Tigeot 	if (!IS_ERR_OR_NULL(lvds_connector->base.edid))
48819df918dSFrançois Tigeot 		return drm_add_edid_modes(connector, lvds_connector->base.edid);
489e3adcf8fSFrançois Tigeot 
49019df918dSFrançois Tigeot 	mode = drm_mode_duplicate(dev, lvds_connector->base.panel.fixed_mode);
491e3adcf8fSFrançois Tigeot 	if (mode == NULL)
492e3adcf8fSFrançois Tigeot 		return 0;
493e3adcf8fSFrançois Tigeot 
494e3adcf8fSFrançois Tigeot 	drm_mode_probed_add(connector, mode);
495e3adcf8fSFrançois Tigeot 	return 1;
496e3adcf8fSFrançois Tigeot }
497e3adcf8fSFrançois Tigeot 
49876594197Szrj #if 0 /* unused */
499e3adcf8fSFrançois Tigeot static int intel_no_modeset_on_lid_dmi_callback(const struct dmi_system_id *id)
500e3adcf8fSFrançois Tigeot {
50119df918dSFrançois Tigeot 	DRM_INFO("Skipping forced modeset for %s\n", id->ident);
502e3adcf8fSFrançois Tigeot 	return 1;
503e3adcf8fSFrançois Tigeot }
504e3adcf8fSFrançois Tigeot 
505e3adcf8fSFrançois Tigeot /* The GPU hangs up on these systems if modeset is performed on LID open */
506e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_modeset_on_lid[] = {
507e3adcf8fSFrançois Tigeot 	{
508e3adcf8fSFrançois Tigeot 		.callback = intel_no_modeset_on_lid_dmi_callback,
509e3adcf8fSFrançois Tigeot 		.ident = "Toshiba Tecra A11",
510e3adcf8fSFrançois Tigeot 		.matches = {
511e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
512e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A11"),
513e3adcf8fSFrançois Tigeot 		},
514e3adcf8fSFrançois Tigeot 	},
515e3adcf8fSFrançois Tigeot 
516e3adcf8fSFrançois Tigeot 	{ }	/* terminating entry */
517e3adcf8fSFrançois Tigeot };
518e3adcf8fSFrançois Tigeot 
519e3adcf8fSFrançois Tigeot /*
520a2fdbec6SFrançois Tigeot  * Lid events. Note the use of 'modeset':
521a2fdbec6SFrançois Tigeot  *  - we set it to MODESET_ON_LID_OPEN on lid close,
522a2fdbec6SFrançois Tigeot  *    and set it to MODESET_DONE on open
523e3adcf8fSFrançois Tigeot  *  - we use it as a "only once" bit (ie we ignore
524a2fdbec6SFrançois Tigeot  *    duplicate events where it was already properly set)
525a2fdbec6SFrançois Tigeot  *  - the suspend/resume paths will set it to
526a2fdbec6SFrançois Tigeot  *    MODESET_SUSPENDED and ignore the lid open event,
527a2fdbec6SFrançois Tigeot  *    because they restore the mode ("lid open").
528e3adcf8fSFrançois Tigeot  */
529e3adcf8fSFrançois Tigeot static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
530e3adcf8fSFrançois Tigeot 			    void *unused)
531e3adcf8fSFrançois Tigeot {
53219df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector =
53319df918dSFrançois Tigeot 		container_of(nb, struct intel_lvds_connector, lid_notifier);
53419df918dSFrançois Tigeot 	struct drm_connector *connector = &lvds_connector->base.base;
53519df918dSFrançois Tigeot 	struct drm_device *dev = connector->dev;
536bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
537e3adcf8fSFrançois Tigeot 
538e3adcf8fSFrançois Tigeot 	if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
539e3adcf8fSFrançois Tigeot 		return NOTIFY_OK;
540e3adcf8fSFrançois Tigeot 
541a2fdbec6SFrançois Tigeot 	mutex_lock(&dev_priv->modeset_restore_lock);
542a2fdbec6SFrançois Tigeot 	if (dev_priv->modeset_restore == MODESET_SUSPENDED)
543a2fdbec6SFrançois Tigeot 		goto exit;
544e3adcf8fSFrançois Tigeot 	/*
545e3adcf8fSFrançois Tigeot 	 * check and update the status of LVDS connector after receiving
546e3adcf8fSFrançois Tigeot 	 * the LID nofication event.
547e3adcf8fSFrançois Tigeot 	 */
54819df918dSFrançois Tigeot 	connector->status = connector->funcs->detect(connector, false);
549e3adcf8fSFrançois Tigeot 
550e3adcf8fSFrançois Tigeot 	/* Don't force modeset on machines where it causes a GPU lockup */
551e3adcf8fSFrançois Tigeot 	if (dmi_check_system(intel_no_modeset_on_lid))
552a2fdbec6SFrançois Tigeot 		goto exit;
553e3adcf8fSFrançois Tigeot 	if (!acpi_lid_open()) {
554a2fdbec6SFrançois Tigeot 		/* do modeset on next lid open event */
555a2fdbec6SFrançois Tigeot 		dev_priv->modeset_restore = MODESET_ON_LID_OPEN;
556a2fdbec6SFrançois Tigeot 		goto exit;
557e3adcf8fSFrançois Tigeot 	}
558e3adcf8fSFrançois Tigeot 
559a2fdbec6SFrançois Tigeot 	if (dev_priv->modeset_restore == MODESET_DONE)
560a2fdbec6SFrançois Tigeot 		goto exit;
561e3adcf8fSFrançois Tigeot 
5629edbd4a0SFrançois Tigeot 	/*
5639edbd4a0SFrançois Tigeot 	 * Some old platform's BIOS love to wreak havoc while the lid is closed.
5649edbd4a0SFrançois Tigeot 	 * We try to detect this here and undo any damage. The split for PCH
5659edbd4a0SFrançois Tigeot 	 * platforms is rather conservative and a bit arbitrary expect that on
5669edbd4a0SFrançois Tigeot 	 * those platforms VGA disabling requires actual legacy VGA I/O access,
5679edbd4a0SFrançois Tigeot 	 * and as part of the cleanup in the hw state restore we also redisable
5689edbd4a0SFrançois Tigeot 	 * the vga plane.
5699edbd4a0SFrançois Tigeot 	 */
5701e12ee3bSFrançois Tigeot 	if (!HAS_PCH_SPLIT(dev_priv))
571a05eeebfSFrançois Tigeot 		intel_display_resume(dev);
572e3adcf8fSFrançois Tigeot 
573a2fdbec6SFrançois Tigeot 	dev_priv->modeset_restore = MODESET_DONE;
574a2fdbec6SFrançois Tigeot 
575a2fdbec6SFrançois Tigeot exit:
576a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev_priv->modeset_restore_lock);
577e3adcf8fSFrançois Tigeot 	return NOTIFY_OK;
578e3adcf8fSFrançois Tigeot }
579e3adcf8fSFrançois Tigeot #endif
580e3adcf8fSFrançois Tigeot 
581e3adcf8fSFrançois Tigeot /**
582e3adcf8fSFrançois Tigeot  * intel_lvds_destroy - unregister and free LVDS structures
583e3adcf8fSFrançois Tigeot  * @connector: connector to free
584e3adcf8fSFrançois Tigeot  *
585e3adcf8fSFrançois Tigeot  * Unregister the DDC bus for this connector then free the driver private
586e3adcf8fSFrançois Tigeot  * structure.
587e3adcf8fSFrançois Tigeot  */
588e3adcf8fSFrançois Tigeot static void intel_lvds_destroy(struct drm_connector *connector)
589e3adcf8fSFrançois Tigeot {
59019df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector =
59119df918dSFrançois Tigeot 		to_lvds_connector(connector);
592e3adcf8fSFrançois Tigeot 
593e3adcf8fSFrançois Tigeot #if 0
59419df918dSFrançois Tigeot 	if (lvds_connector->lid_notifier.notifier_call)
59519df918dSFrançois Tigeot 		acpi_lid_notifier_unregister(&lvds_connector->lid_notifier);
596e3adcf8fSFrançois Tigeot #endif
59719df918dSFrançois Tigeot 
59819df918dSFrançois Tigeot 	if (!IS_ERR_OR_NULL(lvds_connector->base.edid))
599158486a6SFrançois Tigeot 		kfree(lvds_connector->base.edid);
60019df918dSFrançois Tigeot 
60119df918dSFrançois Tigeot 	intel_panel_fini(&lvds_connector->base.panel);
60219df918dSFrançois Tigeot 
603e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
604158486a6SFrançois Tigeot 	kfree(connector);
605e3adcf8fSFrançois Tigeot }
606e3adcf8fSFrançois Tigeot 
607e3adcf8fSFrançois Tigeot static int intel_lvds_set_property(struct drm_connector *connector,
608e3adcf8fSFrançois Tigeot 				   struct drm_property *property,
609e3adcf8fSFrançois Tigeot 				   uint64_t value)
610e3adcf8fSFrançois Tigeot {
61119df918dSFrançois Tigeot 	struct intel_connector *intel_connector = to_intel_connector(connector);
612e3adcf8fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
613e3adcf8fSFrançois Tigeot 
614e3adcf8fSFrançois Tigeot 	if (property == dev->mode_config.scaling_mode_property) {
61519df918dSFrançois Tigeot 		struct drm_crtc *crtc;
616e3adcf8fSFrançois Tigeot 
617e3adcf8fSFrançois Tigeot 		if (value == DRM_MODE_SCALE_NONE) {
618e3adcf8fSFrançois Tigeot 			DRM_DEBUG_KMS("no scaling not supported\n");
619e3adcf8fSFrançois Tigeot 			return -EINVAL;
620e3adcf8fSFrançois Tigeot 		}
621e3adcf8fSFrançois Tigeot 
62219df918dSFrançois Tigeot 		if (intel_connector->panel.fitting_mode == value) {
623e3adcf8fSFrançois Tigeot 			/* the LVDS scaling property is not changed */
624e3adcf8fSFrançois Tigeot 			return 0;
625e3adcf8fSFrançois Tigeot 		}
62619df918dSFrançois Tigeot 		intel_connector->panel.fitting_mode = value;
62719df918dSFrançois Tigeot 
62819df918dSFrançois Tigeot 		crtc = intel_attached_encoder(connector)->base.crtc;
629477eb7f9SFrançois Tigeot 		if (crtc && crtc->state->enable) {
630e3adcf8fSFrançois Tigeot 			/*
631e3adcf8fSFrançois Tigeot 			 * If the CRTC is enabled, the display will be changed
632e3adcf8fSFrançois Tigeot 			 * according to the new panel fitting mode.
633e3adcf8fSFrançois Tigeot 			 */
634a2fdbec6SFrançois Tigeot 			intel_crtc_restore_mode(crtc);
635e3adcf8fSFrançois Tigeot 		}
636e3adcf8fSFrançois Tigeot 	}
637e3adcf8fSFrançois Tigeot 
638e3adcf8fSFrançois Tigeot 	return 0;
639e3adcf8fSFrançois Tigeot }
640e3adcf8fSFrançois Tigeot 
641e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
642e3adcf8fSFrançois Tigeot 	.get_modes = intel_lvds_get_modes,
643e3adcf8fSFrançois Tigeot 	.mode_valid = intel_lvds_mode_valid,
644e3adcf8fSFrançois Tigeot };
645e3adcf8fSFrançois Tigeot 
646e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_lvds_connector_funcs = {
647a05eeebfSFrançois Tigeot 	.dpms = drm_atomic_helper_connector_dpms,
648e3adcf8fSFrançois Tigeot 	.detect = intel_lvds_detect,
649e3adcf8fSFrançois Tigeot 	.fill_modes = drm_helper_probe_single_connector_modes,
650e3adcf8fSFrançois Tigeot 	.set_property = intel_lvds_set_property,
6512c9916cdSFrançois Tigeot 	.atomic_get_property = intel_connector_atomic_get_property,
6521487f786SFrançois Tigeot 	.late_register = intel_connector_register,
6531487f786SFrançois Tigeot 	.early_unregister = intel_connector_unregister,
654e3adcf8fSFrançois Tigeot 	.destroy = intel_lvds_destroy,
6552c9916cdSFrançois Tigeot 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
656477eb7f9SFrançois Tigeot 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
657e3adcf8fSFrançois Tigeot };
658e3adcf8fSFrançois Tigeot 
659e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
660e3adcf8fSFrançois Tigeot 	.destroy = intel_encoder_destroy,
661e3adcf8fSFrançois Tigeot };
662e3adcf8fSFrançois Tigeot 
66324edb884SFrançois Tigeot static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
664e3adcf8fSFrançois Tigeot {
66519df918dSFrançois Tigeot 	DRM_INFO("Skipping LVDS initialization for %s\n", id->ident);
666e3adcf8fSFrançois Tigeot 	return 1;
667e3adcf8fSFrançois Tigeot }
668e3adcf8fSFrançois Tigeot 
669e3adcf8fSFrançois Tigeot /* These systems claim to have LVDS, but really don't */
670e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_lvds[] = {
671e3adcf8fSFrançois Tigeot 	{
672e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
673e3adcf8fSFrançois Tigeot 		.ident = "Apple Mac Mini (Core series)",
674e3adcf8fSFrançois Tigeot 		.matches = {
675e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
676e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"),
677e3adcf8fSFrançois Tigeot 		},
678e3adcf8fSFrançois Tigeot 	},
679e3adcf8fSFrançois Tigeot 	{
680e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
681e3adcf8fSFrançois Tigeot 		.ident = "Apple Mac Mini (Core 2 series)",
682e3adcf8fSFrançois Tigeot 		.matches = {
683e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
684e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "Macmini2,1"),
685e3adcf8fSFrançois Tigeot 		},
686e3adcf8fSFrançois Tigeot 	},
687e3adcf8fSFrançois Tigeot 	{
688e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
689e3adcf8fSFrançois Tigeot 		.ident = "MSI IM-945GSE-A",
690e3adcf8fSFrançois Tigeot 		.matches = {
691e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "MSI"),
692e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "A9830IMS"),
693e3adcf8fSFrançois Tigeot 		},
694e3adcf8fSFrançois Tigeot 	},
695e3adcf8fSFrançois Tigeot 	{
696e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
697e3adcf8fSFrançois Tigeot 		.ident = "Dell Studio Hybrid",
698e3adcf8fSFrançois Tigeot 		.matches = {
699e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
700e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
701e3adcf8fSFrançois Tigeot 		},
702e3adcf8fSFrançois Tigeot 	},
703e3adcf8fSFrançois Tigeot 	{
704e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
705e3adcf8fSFrançois Tigeot 		.ident = "Dell OptiPlex FX170",
706e3adcf8fSFrançois Tigeot 		.matches = {
707e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
708e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"),
709e3adcf8fSFrançois Tigeot 		},
710e3adcf8fSFrançois Tigeot 	},
711e3adcf8fSFrançois Tigeot 	{
712e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
713e3adcf8fSFrançois Tigeot 		.ident = "AOpen Mini PC",
714e3adcf8fSFrançois Tigeot 		.matches = {
715e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
716e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"),
717e3adcf8fSFrançois Tigeot 		},
718e3adcf8fSFrançois Tigeot 	},
719e3adcf8fSFrançois Tigeot 	{
720e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
721e3adcf8fSFrançois Tigeot 		.ident = "AOpen Mini PC MP915",
722e3adcf8fSFrançois Tigeot 		.matches = {
723e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
724e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"),
725e3adcf8fSFrançois Tigeot 		},
726e3adcf8fSFrançois Tigeot 	},
727e3adcf8fSFrançois Tigeot 	{
728e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
729e3adcf8fSFrançois Tigeot 		.ident = "AOpen i915GMm-HFS",
730e3adcf8fSFrançois Tigeot 		.matches = {
731e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
732e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
733e3adcf8fSFrançois Tigeot 		},
734e3adcf8fSFrançois Tigeot 	},
735e3adcf8fSFrançois Tigeot 	{
736e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
737e3adcf8fSFrançois Tigeot                 .ident = "AOpen i45GMx-I",
738e3adcf8fSFrançois Tigeot                 .matches = {
739e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
740e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"),
741e3adcf8fSFrançois Tigeot                 },
742e3adcf8fSFrançois Tigeot         },
743e3adcf8fSFrançois Tigeot 	{
744e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
745e3adcf8fSFrançois Tigeot 		.ident = "Aopen i945GTt-VFA",
746e3adcf8fSFrançois Tigeot 		.matches = {
747e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"),
748e3adcf8fSFrançois Tigeot 		},
749e3adcf8fSFrançois Tigeot 	},
750e3adcf8fSFrançois Tigeot 	{
751e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
752e3adcf8fSFrançois Tigeot 		.ident = "Clientron U800",
753e3adcf8fSFrançois Tigeot 		.matches = {
754e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
755e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "U800"),
756e3adcf8fSFrançois Tigeot 		},
757e3adcf8fSFrançois Tigeot 	},
758e3adcf8fSFrançois Tigeot 	{
759e3adcf8fSFrançois Tigeot                 .callback = intel_no_lvds_dmi_callback,
760e3adcf8fSFrançois Tigeot                 .ident = "Clientron E830",
761e3adcf8fSFrançois Tigeot                 .matches = {
762e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
763e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_PRODUCT_NAME, "E830"),
764e3adcf8fSFrançois Tigeot                 },
765e3adcf8fSFrançois Tigeot         },
766e3adcf8fSFrançois Tigeot         {
767e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
768e3adcf8fSFrançois Tigeot 		.ident = "Asus EeeBox PC EB1007",
769e3adcf8fSFrançois Tigeot 		.matches = {
770e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
771e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "EB1007"),
772e3adcf8fSFrançois Tigeot 		},
773e3adcf8fSFrançois Tigeot 	},
774e3adcf8fSFrançois Tigeot 	{
775e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
776e3adcf8fSFrançois Tigeot 		.ident = "Asus AT5NM10T-I",
777e3adcf8fSFrançois Tigeot 		.matches = {
778e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
779e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"),
780e3adcf8fSFrançois Tigeot 		},
781e3adcf8fSFrançois Tigeot 	},
782e3adcf8fSFrançois Tigeot 	{
783e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
784a2fdbec6SFrançois Tigeot 		.ident = "Hewlett-Packard HP t5740",
78519df918dSFrançois Tigeot 		.matches = {
78619df918dSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
787a2fdbec6SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, " t5740"),
78819df918dSFrançois Tigeot 		},
78919df918dSFrançois Tigeot 	},
79019df918dSFrançois Tigeot 	{
79119df918dSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
792e3adcf8fSFrançois Tigeot 		.ident = "Hewlett-Packard t5745",
793e3adcf8fSFrançois Tigeot 		.matches = {
794e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
795e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "hp t5745"),
796e3adcf8fSFrançois Tigeot 		},
797e3adcf8fSFrançois Tigeot 	},
798e3adcf8fSFrançois Tigeot 	{
799e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
800e3adcf8fSFrançois Tigeot 		.ident = "Hewlett-Packard st5747",
801e3adcf8fSFrançois Tigeot 		.matches = {
802e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
803e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "hp st5747"),
804e3adcf8fSFrançois Tigeot 		},
805e3adcf8fSFrançois Tigeot 	},
806e3adcf8fSFrançois Tigeot 	{
807e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
808e3adcf8fSFrançois Tigeot 		.ident = "MSI Wind Box DC500",
809e3adcf8fSFrançois Tigeot 		.matches = {
810e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
811e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "MS-7469"),
812e3adcf8fSFrançois Tigeot 		},
813e3adcf8fSFrançois Tigeot 	},
814e3adcf8fSFrançois Tigeot 	{
815e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
81619df918dSFrançois Tigeot 		.ident = "Gigabyte GA-D525TUD",
81719df918dSFrançois Tigeot 		.matches = {
81819df918dSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
81919df918dSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "D525TUD"),
82019df918dSFrançois Tigeot 		},
82119df918dSFrançois Tigeot 	},
82219df918dSFrançois Tigeot 	{
82319df918dSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
824e3adcf8fSFrançois Tigeot 		.ident = "Supermicro X7SPA-H",
825e3adcf8fSFrançois Tigeot 		.matches = {
826e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
827e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "X7SPA-H"),
828e3adcf8fSFrançois Tigeot 		},
829e3adcf8fSFrançois Tigeot 	},
83019df918dSFrançois Tigeot 	{
83119df918dSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
83219df918dSFrançois Tigeot 		.ident = "Fujitsu Esprimo Q900",
83319df918dSFrançois Tigeot 		.matches = {
83419df918dSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
83519df918dSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"),
83619df918dSFrançois Tigeot 		},
83719df918dSFrançois Tigeot 	},
8385d0b1887SFrançois Tigeot 	{
8395d0b1887SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8409edbd4a0SFrançois Tigeot 		.ident = "Intel D410PT",
8419edbd4a0SFrançois Tigeot 		.matches = {
8429edbd4a0SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8439edbd4a0SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "D410PT"),
8449edbd4a0SFrançois Tigeot 		},
8459edbd4a0SFrançois Tigeot 	},
8469edbd4a0SFrançois Tigeot 	{
8479edbd4a0SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8489edbd4a0SFrançois Tigeot 		.ident = "Intel D425KT",
8499edbd4a0SFrançois Tigeot 		.matches = {
8509edbd4a0SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8519edbd4a0SFrançois Tigeot 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "D425KT"),
8529edbd4a0SFrançois Tigeot 		},
8539edbd4a0SFrançois Tigeot 	},
8549edbd4a0SFrançois Tigeot 	{
8559edbd4a0SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8565d0b1887SFrançois Tigeot 		.ident = "Intel D510MO",
8575d0b1887SFrançois Tigeot 		.matches = {
8585d0b1887SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8595d0b1887SFrançois Tigeot 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "D510MO"),
8605d0b1887SFrançois Tigeot 		},
8615d0b1887SFrançois Tigeot 	},
8625d0b1887SFrançois Tigeot 	{
8635d0b1887SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8645d0b1887SFrançois Tigeot 		.ident = "Intel D525MW",
8655d0b1887SFrançois Tigeot 		.matches = {
8665d0b1887SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8675d0b1887SFrançois Tigeot 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"),
8685d0b1887SFrançois Tigeot 		},
8695d0b1887SFrançois Tigeot 	},
870e3adcf8fSFrançois Tigeot 
871e3adcf8fSFrançois Tigeot 	{ }	/* terminating entry */
872e3adcf8fSFrançois Tigeot };
873e3adcf8fSFrançois Tigeot 
874a2fdbec6SFrançois Tigeot static int intel_dual_link_lvds_callback(const struct dmi_system_id *id)
875a2fdbec6SFrançois Tigeot {
876a2fdbec6SFrançois Tigeot 	DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident);
877a2fdbec6SFrançois Tigeot 	return 1;
878a2fdbec6SFrançois Tigeot }
879a2fdbec6SFrançois Tigeot 
880a2fdbec6SFrançois Tigeot static const struct dmi_system_id intel_dual_link_lvds[] = {
881a2fdbec6SFrançois Tigeot 	{
882a2fdbec6SFrançois Tigeot 		.callback = intel_dual_link_lvds_callback,
883477eb7f9SFrançois Tigeot 		.ident = "Apple MacBook Pro 15\" (2010)",
884477eb7f9SFrançois Tigeot 		.matches = {
885477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
886477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6,2"),
887477eb7f9SFrançois Tigeot 		},
888477eb7f9SFrançois Tigeot 	},
889477eb7f9SFrançois Tigeot 	{
890477eb7f9SFrançois Tigeot 		.callback = intel_dual_link_lvds_callback,
891477eb7f9SFrançois Tigeot 		.ident = "Apple MacBook Pro 15\" (2011)",
892a2fdbec6SFrançois Tigeot 		.matches = {
893a2fdbec6SFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
894a2fdbec6SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"),
895a2fdbec6SFrançois Tigeot 		},
896a2fdbec6SFrançois Tigeot 	},
897477eb7f9SFrançois Tigeot 	{
898477eb7f9SFrançois Tigeot 		.callback = intel_dual_link_lvds_callback,
899477eb7f9SFrançois Tigeot 		.ident = "Apple MacBook Pro 15\" (2012)",
900477eb7f9SFrançois Tigeot 		.matches = {
901477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
902477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro9,1"),
903477eb7f9SFrançois Tigeot 		},
904477eb7f9SFrançois Tigeot 	},
905a2fdbec6SFrançois Tigeot 	{ }	/* terminating entry */
906a2fdbec6SFrançois Tigeot };
907a2fdbec6SFrançois Tigeot 
9081487f786SFrançois Tigeot struct intel_encoder *intel_get_lvds_encoder(struct drm_device *dev)
9091487f786SFrançois Tigeot {
9101487f786SFrançois Tigeot 	struct intel_encoder *intel_encoder;
9111487f786SFrançois Tigeot 
9121487f786SFrançois Tigeot 	for_each_intel_encoder(dev, intel_encoder)
9131487f786SFrançois Tigeot 		if (intel_encoder->type == INTEL_OUTPUT_LVDS)
9141487f786SFrançois Tigeot 			return intel_encoder;
9151487f786SFrançois Tigeot 
9161487f786SFrançois Tigeot 	return NULL;
9171487f786SFrançois Tigeot }
9181487f786SFrançois Tigeot 
919a2fdbec6SFrançois Tigeot bool intel_is_dual_link_lvds(struct drm_device *dev)
920a2fdbec6SFrançois Tigeot {
9211487f786SFrançois Tigeot 	struct intel_encoder *encoder = intel_get_lvds_encoder(dev);
922a2fdbec6SFrançois Tigeot 
9231487f786SFrançois Tigeot 	return encoder && to_lvds_encoder(&encoder->base)->is_dual_link;
924a2fdbec6SFrançois Tigeot }
925a2fdbec6SFrançois Tigeot 
926a2fdbec6SFrançois Tigeot static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
927a2fdbec6SFrançois Tigeot {
928a2fdbec6SFrançois Tigeot 	struct drm_device *dev = lvds_encoder->base.base.dev;
929a2fdbec6SFrançois Tigeot 	unsigned int val;
930bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
931a2fdbec6SFrançois Tigeot 
932a2fdbec6SFrançois Tigeot 	/* use the module option value if specified */
933ba55f2f5SFrançois Tigeot 	if (i915.lvds_channel_mode > 0)
934ba55f2f5SFrançois Tigeot 		return i915.lvds_channel_mode == 2;
935a2fdbec6SFrançois Tigeot 
936477eb7f9SFrançois Tigeot 	/* single channel LVDS is limited to 112 MHz */
937477eb7f9SFrançois Tigeot 	if (lvds_encoder->attached_connector->base.panel.fixed_mode->clock
938477eb7f9SFrançois Tigeot 	    > 112999)
939477eb7f9SFrançois Tigeot 		return true;
940477eb7f9SFrançois Tigeot 
941a2fdbec6SFrançois Tigeot 	if (dmi_check_system(intel_dual_link_lvds))
942a2fdbec6SFrançois Tigeot 		return true;
943a2fdbec6SFrançois Tigeot 
944a2fdbec6SFrançois Tigeot 	/* BIOS should set the proper LVDS register value at boot, but
945a2fdbec6SFrançois Tigeot 	 * in reality, it doesn't set the value when the lid is closed;
946a2fdbec6SFrançois Tigeot 	 * we need to check "the value to be set" in VBT when LVDS
947a2fdbec6SFrançois Tigeot 	 * register is uninitialized.
948a2fdbec6SFrançois Tigeot 	 */
949a2fdbec6SFrançois Tigeot 	val = I915_READ(lvds_encoder->reg);
950a2fdbec6SFrançois Tigeot 	if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED)))
9515d0b1887SFrançois Tigeot 		val = dev_priv->vbt.bios_lvds_val;
952a2fdbec6SFrançois Tigeot 
953a2fdbec6SFrançois Tigeot 	return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
954a2fdbec6SFrançois Tigeot }
955a2fdbec6SFrançois Tigeot 
9561e12ee3bSFrançois Tigeot static bool intel_lvds_supported(struct drm_i915_private *dev_priv)
957e3adcf8fSFrançois Tigeot {
958e3adcf8fSFrançois Tigeot 	/* With the introduction of the PCH we gained a dedicated
959e3adcf8fSFrançois Tigeot 	 * LVDS presence pin, use it. */
9601e12ee3bSFrançois Tigeot 	if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv))
961e3adcf8fSFrançois Tigeot 		return true;
962e3adcf8fSFrançois Tigeot 
963e3adcf8fSFrançois Tigeot 	/* Otherwise LVDS was only attached to mobile products,
964e3adcf8fSFrançois Tigeot 	 * except for the inglorious 830gm */
9651e12ee3bSFrançois Tigeot 	if (INTEL_GEN(dev_priv) <= 4 &&
9661e12ee3bSFrançois Tigeot 	    IS_MOBILE(dev_priv) && !IS_I830(dev_priv))
9678e26cdf6SFrançois Tigeot 		return true;
9688e26cdf6SFrançois Tigeot 
9698e26cdf6SFrançois Tigeot 	return false;
970e3adcf8fSFrançois Tigeot }
971e3adcf8fSFrançois Tigeot 
972e3adcf8fSFrançois Tigeot /**
973e3adcf8fSFrançois Tigeot  * intel_lvds_init - setup LVDS connectors on this device
974e3adcf8fSFrançois Tigeot  * @dev: drm device
975e3adcf8fSFrançois Tigeot  *
976e3adcf8fSFrançois Tigeot  * Create the connector, register the LVDS DDC bus, and try to figure out what
977e3adcf8fSFrançois Tigeot  * modes we can display on the LVDS panel (if present).
978e3adcf8fSFrançois Tigeot  */
9795d0b1887SFrançois Tigeot void intel_lvds_init(struct drm_device *dev)
980e3adcf8fSFrançois Tigeot {
981bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
98219df918dSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder;
983e3adcf8fSFrançois Tigeot 	struct intel_encoder *intel_encoder;
98419df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector;
985e3adcf8fSFrançois Tigeot 	struct intel_connector *intel_connector;
986e3adcf8fSFrançois Tigeot 	struct drm_connector *connector;
987e3adcf8fSFrançois Tigeot 	struct drm_encoder *encoder;
988e3adcf8fSFrançois Tigeot 	struct drm_display_mode *scan; /* *modes, *bios_mode; */
98919df918dSFrançois Tigeot 	struct drm_display_mode *fixed_mode = NULL;
990ba55f2f5SFrançois Tigeot 	struct drm_display_mode *downclock_mode = NULL;
99119df918dSFrançois Tigeot 	struct edid *edid;
992*4be47400SFrançois Tigeot 	struct intel_crtc *crtc;
993aee94f86SFrançois Tigeot 	i915_reg_t lvds_reg;
994e3adcf8fSFrançois Tigeot 	u32 lvds;
995e3adcf8fSFrançois Tigeot 	int pipe;
996e3adcf8fSFrançois Tigeot 	u8 pin;
997e3adcf8fSFrançois Tigeot 
9981e12ee3bSFrançois Tigeot 	if (!intel_lvds_supported(dev_priv))
9995d0b1887SFrançois Tigeot 		return;
1000e3adcf8fSFrançois Tigeot 
1001e3adcf8fSFrançois Tigeot 	/* Skip init on machines we know falsely report LVDS */
1002e3adcf8fSFrançois Tigeot 	if (dmi_check_system(intel_no_lvds))
10035d0b1887SFrançois Tigeot 		return;
1004e3adcf8fSFrançois Tigeot 
10051e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv))
1006352ff8bdSFrançois Tigeot 		lvds_reg = PCH_LVDS;
1007352ff8bdSFrançois Tigeot 	else
1008352ff8bdSFrançois Tigeot 		lvds_reg = LVDS;
1009352ff8bdSFrançois Tigeot 
1010352ff8bdSFrançois Tigeot 	lvds = I915_READ(lvds_reg);
1011352ff8bdSFrançois Tigeot 
10121e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
1013352ff8bdSFrançois Tigeot 		if ((lvds & LVDS_DETECTED) == 0)
10145d0b1887SFrançois Tigeot 			return;
10158621f407SFrançois Tigeot 		if (dev_priv->vbt.edp.support) {
1016e3adcf8fSFrançois Tigeot 			DRM_DEBUG_KMS("disable LVDS for eDP support\n");
10175d0b1887SFrançois Tigeot 			return;
1018e3adcf8fSFrançois Tigeot 		}
1019e3adcf8fSFrançois Tigeot 	}
1020e3adcf8fSFrançois Tigeot 
1021a05eeebfSFrançois Tigeot 	pin = GMBUS_PIN_PANEL;
10228621f407SFrançois Tigeot 	if (!intel_bios_is_lvds_present(dev_priv, &pin)) {
1023352ff8bdSFrançois Tigeot 		if ((lvds & LVDS_PORT_EN) == 0) {
1024a05eeebfSFrançois Tigeot 			DRM_DEBUG_KMS("LVDS is not present in VBT\n");
1025a05eeebfSFrançois Tigeot 			return;
1026a05eeebfSFrançois Tigeot 		}
1027a05eeebfSFrançois Tigeot 		DRM_DEBUG_KMS("LVDS is not present in VBT, but enabled anyway\n");
1028a05eeebfSFrançois Tigeot 	}
1029a05eeebfSFrançois Tigeot 
10309edbd4a0SFrançois Tigeot 	lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL);
103119df918dSFrançois Tigeot 	if (!lvds_encoder)
10325d0b1887SFrançois Tigeot 		return;
1033e3adcf8fSFrançois Tigeot 
10349edbd4a0SFrançois Tigeot 	lvds_connector = kzalloc(sizeof(*lvds_connector), GFP_KERNEL);
103519df918dSFrançois Tigeot 	if (!lvds_connector) {
1036158486a6SFrançois Tigeot 		kfree(lvds_encoder);
10375d0b1887SFrançois Tigeot 		return;
1038e3adcf8fSFrançois Tigeot 	}
1039e3adcf8fSFrançois Tigeot 
1040477eb7f9SFrançois Tigeot 	if (intel_connector_init(&lvds_connector->base) < 0) {
1041477eb7f9SFrançois Tigeot 		kfree(lvds_connector);
1042477eb7f9SFrançois Tigeot 		kfree(lvds_encoder);
1043477eb7f9SFrançois Tigeot 		return;
1044477eb7f9SFrançois Tigeot 	}
1045477eb7f9SFrançois Tigeot 
104619df918dSFrançois Tigeot 	lvds_encoder->attached_connector = lvds_connector;
104719df918dSFrançois Tigeot 
104819df918dSFrançois Tigeot 	intel_encoder = &lvds_encoder->base;
1049e3adcf8fSFrançois Tigeot 	encoder = &intel_encoder->base;
105019df918dSFrançois Tigeot 	intel_connector = &lvds_connector->base;
1051e3adcf8fSFrançois Tigeot 	connector = &intel_connector->base;
1052e3adcf8fSFrançois Tigeot 	drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs,
1053e3adcf8fSFrançois Tigeot 			   DRM_MODE_CONNECTOR_LVDS);
1054e3adcf8fSFrançois Tigeot 
1055e3adcf8fSFrançois Tigeot 	drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
10561487f786SFrançois Tigeot 			 DRM_MODE_ENCODER_LVDS, "LVDS");
1057e3adcf8fSFrançois Tigeot 
105819df918dSFrançois Tigeot 	intel_encoder->enable = intel_enable_lvds;
10599edbd4a0SFrançois Tigeot 	intel_encoder->pre_enable = intel_pre_enable_lvds;
10608e26cdf6SFrançois Tigeot 	intel_encoder->compute_config = intel_lvds_compute_config;
1061a05eeebfSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
1062a05eeebfSFrançois Tigeot 		intel_encoder->disable = pch_disable_lvds;
1063a05eeebfSFrançois Tigeot 		intel_encoder->post_disable = pch_post_disable_lvds;
1064a05eeebfSFrançois Tigeot 	} else {
1065a05eeebfSFrançois Tigeot 		intel_encoder->disable = gmch_disable_lvds;
1066a05eeebfSFrançois Tigeot 	}
106719df918dSFrançois Tigeot 	intel_encoder->get_hw_state = intel_lvds_get_hw_state;
10685d0b1887SFrançois Tigeot 	intel_encoder->get_config = intel_lvds_get_config;
106919df918dSFrançois Tigeot 	intel_connector->get_hw_state = intel_connector_get_hw_state;
107019df918dSFrançois Tigeot 
1071e3adcf8fSFrançois Tigeot 	intel_connector_attach_encoder(intel_connector, intel_encoder);
1072e3adcf8fSFrançois Tigeot 
10731e12ee3bSFrançois Tigeot 	intel_encoder->type = INTEL_OUTPUT_LVDS;
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;
1170*4be47400SFrançois Tigeot 	crtc = intel_get_crtc_for_pipe(dev_priv, pipe);
1171e3adcf8fSFrançois Tigeot 
1172e3adcf8fSFrançois Tigeot 	if (crtc && (lvds & LVDS_PORT_EN)) {
1173*4be47400SFranç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