xref: /dflybsd-src/sys/dev/drm/i915/intel_lvds.c (revision 3f2dd94a569761201b5b0a18b2f697f97fe1b9dc)
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 
to_lvds_encoder(struct drm_encoder * encoder)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 
to_lvds_connector(struct drm_connector * connector)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 
intel_lvds_get_hw_state(struct intel_encoder * encoder,enum i915_pipe * pipe)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 
98a85cb24fSFrançois Tigeot 	if (!intel_display_power_get_if_enabled(dev_priv,
99a85cb24fSFranç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:
117a85cb24fSFranç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 
intel_lvds_get_config(struct intel_encoder * encoder,struct intel_crtc_state * pipe_config)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 
intel_lvds_pps_get_hw_state(struct drm_i915_private * dev_priv,struct intel_lvds_pps * pps)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 
intel_lvds_pps_init_hw(struct drm_i915_private * dev_priv,struct intel_lvds_pps * pps)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 
intel_pre_enable_lvds(struct intel_encoder * encoder,const struct intel_crtc_state * pipe_config,const struct drm_connector_state * conn_state)2321e12ee3bSFrançois Tigeot static void intel_pre_enable_lvds(struct intel_encoder *encoder,
233*3f2dd94aSFrançois Tigeot 				  const struct intel_crtc_state *pipe_config,
234*3f2dd94aSFrançois Tigeot 				  const 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  */
intel_enable_lvds(struct intel_encoder * encoder,const struct intel_crtc_state * pipe_config,const struct drm_connector_state * conn_state)3091e12ee3bSFrançois Tigeot static void intel_enable_lvds(struct intel_encoder *encoder,
310*3f2dd94aSFrançois Tigeot 			      const struct intel_crtc_state *pipe_config,
311*3f2dd94aSFrançois Tigeot 			      const 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);
315bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
316e3adcf8fSFrançois Tigeot 
317a2fdbec6SFrançois Tigeot 	I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN);
318e3adcf8fSFrançois Tigeot 
3191e12ee3bSFrançois Tigeot 	I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) | PANEL_POWER_ON);
320a2fdbec6SFrançois Tigeot 	POSTING_READ(lvds_encoder->reg);
3211e12ee3bSFrançois Tigeot 	if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, PP_ON, 1000))
32219df918dSFrançois Tigeot 		DRM_ERROR("timed out waiting for panel to power on\n");
323e3adcf8fSFrançois Tigeot 
324*3f2dd94aSFrançois Tigeot 	intel_panel_enable_backlight(pipe_config, conn_state);
325e3adcf8fSFrançois Tigeot }
326e3adcf8fSFrançois Tigeot 
intel_disable_lvds(struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)3271e12ee3bSFrançois Tigeot static void intel_disable_lvds(struct intel_encoder *encoder,
328*3f2dd94aSFrançois Tigeot 			       const struct intel_crtc_state *old_crtc_state,
329*3f2dd94aSFrançois Tigeot 			       const struct drm_connector_state *old_conn_state)
330e3adcf8fSFrançois Tigeot {
33119df918dSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
3321e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
333e3adcf8fSFrançois Tigeot 
3341e12ee3bSFrançois Tigeot 	I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) & ~PANEL_POWER_ON);
3351e12ee3bSFrançois Tigeot 	if (intel_wait_for_register(dev_priv, PP_STATUS(0), PP_ON, 0, 1000))
336e3adcf8fSFrançois Tigeot 		DRM_ERROR("timed out waiting for panel to power off\n");
337e3adcf8fSFrançois Tigeot 
338a2fdbec6SFrançois Tigeot 	I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN);
339a2fdbec6SFrançois Tigeot 	POSTING_READ(lvds_encoder->reg);
340e3adcf8fSFrançois Tigeot }
341e3adcf8fSFrançois Tigeot 
gmch_disable_lvds(struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)3421e12ee3bSFrançois Tigeot static void gmch_disable_lvds(struct intel_encoder *encoder,
343*3f2dd94aSFrançois Tigeot 			      const struct intel_crtc_state *old_crtc_state,
344*3f2dd94aSFrançois Tigeot 			      const struct drm_connector_state *old_conn_state)
3451e12ee3bSFrançois Tigeot 
346a05eeebfSFrançois Tigeot {
347*3f2dd94aSFrançois Tigeot 	intel_panel_disable_backlight(old_conn_state);
348a05eeebfSFrançois Tigeot 
3491e12ee3bSFrançois Tigeot 	intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
350a05eeebfSFrançois Tigeot }
351a05eeebfSFrançois Tigeot 
pch_disable_lvds(struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)3521e12ee3bSFrançois Tigeot static void pch_disable_lvds(struct intel_encoder *encoder,
353*3f2dd94aSFrançois Tigeot 			     const struct intel_crtc_state *old_crtc_state,
354*3f2dd94aSFrançois Tigeot 			     const struct drm_connector_state *old_conn_state)
355a05eeebfSFrançois Tigeot {
356*3f2dd94aSFrançois Tigeot 	intel_panel_disable_backlight(old_conn_state);
357a05eeebfSFrançois Tigeot }
358a05eeebfSFrançois Tigeot 
pch_post_disable_lvds(struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)3591e12ee3bSFrançois Tigeot static void pch_post_disable_lvds(struct intel_encoder *encoder,
360*3f2dd94aSFrançois Tigeot 				  const struct intel_crtc_state *old_crtc_state,
361*3f2dd94aSFrançois Tigeot 				  const struct drm_connector_state *old_conn_state)
362a05eeebfSFrançois Tigeot {
3631e12ee3bSFrançois Tigeot 	intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
364a05eeebfSFrançois Tigeot }
365a05eeebfSFrançois Tigeot 
3669edbd4a0SFrançois Tigeot static enum drm_mode_status
intel_lvds_mode_valid(struct drm_connector * connector,struct drm_display_mode * mode)3679edbd4a0SFrançois Tigeot intel_lvds_mode_valid(struct drm_connector *connector,
368e3adcf8fSFrançois Tigeot 		      struct drm_display_mode *mode)
369e3adcf8fSFrançois Tigeot {
37019df918dSFrançois Tigeot 	struct intel_connector *intel_connector = to_intel_connector(connector);
37119df918dSFrançois Tigeot 	struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
372352ff8bdSFrançois Tigeot 	int max_pixclk = to_i915(connector->dev)->max_dotclk_freq;
373e3adcf8fSFrançois Tigeot 
374e3adcf8fSFrançois Tigeot 	if (mode->hdisplay > fixed_mode->hdisplay)
375e3adcf8fSFrançois Tigeot 		return MODE_PANEL;
376e3adcf8fSFrançois Tigeot 	if (mode->vdisplay > fixed_mode->vdisplay)
377e3adcf8fSFrançois Tigeot 		return MODE_PANEL;
378352ff8bdSFrançois Tigeot 	if (fixed_mode->clock > max_pixclk)
379352ff8bdSFrançois Tigeot 		return MODE_CLOCK_HIGH;
380e3adcf8fSFrançois Tigeot 
381e3adcf8fSFrançois Tigeot 	return MODE_OK;
382e3adcf8fSFrançois Tigeot }
383e3adcf8fSFrançois Tigeot 
intel_lvds_compute_config(struct intel_encoder * intel_encoder,struct intel_crtc_state * pipe_config,struct drm_connector_state * conn_state)3848e26cdf6SFrançois Tigeot static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder,
3851e12ee3bSFrançois Tigeot 				      struct intel_crtc_state *pipe_config,
3861e12ee3bSFrançois Tigeot 				      struct drm_connector_state *conn_state)
387e3adcf8fSFrançois Tigeot {
3881e12ee3bSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(intel_encoder->base.dev);
3898e26cdf6SFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder =
3908e26cdf6SFrançois Tigeot 		to_lvds_encoder(&intel_encoder->base);
39119df918dSFrançois Tigeot 	struct intel_connector *intel_connector =
39219df918dSFrançois Tigeot 		&lvds_encoder->attached_connector->base;
3932c9916cdSFrançois Tigeot 	struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
394477eb7f9SFrançois Tigeot 	struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc);
3958e26cdf6SFrançois Tigeot 	unsigned int lvds_bpp;
396e3adcf8fSFrançois Tigeot 
397e3adcf8fSFrançois Tigeot 	/* Should never happen!! */
3981e12ee3bSFrançois Tigeot 	if (INTEL_GEN(dev_priv) < 4 && intel_crtc->pipe == 0) {
399e3adcf8fSFrançois Tigeot 		DRM_ERROR("Can't support LVDS on pipe A\n");
400e3adcf8fSFrançois Tigeot 		return false;
401e3adcf8fSFrançois Tigeot 	}
402e3adcf8fSFrançois Tigeot 
40324edb884SFrançois Tigeot 	if (lvds_encoder->a3_power == LVDS_A3_POWER_UP)
4048e26cdf6SFrançois Tigeot 		lvds_bpp = 8*3;
4058e26cdf6SFrançois Tigeot 	else
4068e26cdf6SFrançois Tigeot 		lvds_bpp = 6*3;
4078e26cdf6SFrançois Tigeot 
4085d0b1887SFrançois Tigeot 	if (lvds_bpp != pipe_config->pipe_bpp && !pipe_config->bw_constrained) {
4098e26cdf6SFrançois Tigeot 		DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n",
4108e26cdf6SFrançois Tigeot 			      pipe_config->pipe_bpp, lvds_bpp);
4118e26cdf6SFrançois Tigeot 		pipe_config->pipe_bpp = lvds_bpp;
4128e26cdf6SFrançois Tigeot 	}
4135d0b1887SFrançois Tigeot 
414e3adcf8fSFrançois Tigeot 	/*
415e3adcf8fSFrançois Tigeot 	 * We have timings from the BIOS for the panel, put them in
416e3adcf8fSFrançois Tigeot 	 * to the adjusted mode.  The CRTC will be set up for this mode,
417e3adcf8fSFrançois Tigeot 	 * with the panel scaling set up to source from the H/VDisplay
418e3adcf8fSFrançois Tigeot 	 * of the original mode.
419e3adcf8fSFrançois Tigeot 	 */
42019df918dSFrançois Tigeot 	intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
42119df918dSFrançois Tigeot 			       adjusted_mode);
422e3adcf8fSFrançois Tigeot 
4231e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
4248e26cdf6SFrançois Tigeot 		pipe_config->has_pch_encoder = true;
4258e26cdf6SFrançois Tigeot 
4265d0b1887SFrançois Tigeot 		intel_pch_panel_fitting(intel_crtc, pipe_config,
427*3f2dd94aSFrançois Tigeot 					conn_state->scaling_mode);
428e3adcf8fSFrançois Tigeot 	} else {
4295d0b1887SFrançois Tigeot 		intel_gmch_panel_fitting(intel_crtc, pipe_config,
430*3f2dd94aSFrançois Tigeot 					 conn_state->scaling_mode);
431e3adcf8fSFrançois Tigeot 
432e3adcf8fSFrançois Tigeot 	}
433e3adcf8fSFrançois Tigeot 
434e3adcf8fSFrançois Tigeot 	/*
435e3adcf8fSFrançois Tigeot 	 * XXX: It would be nice to support lower refresh rates on the
436e3adcf8fSFrançois Tigeot 	 * panels to reduce power consumption, and perhaps match the
437e3adcf8fSFrançois Tigeot 	 * user's requested refresh rate.
438e3adcf8fSFrançois Tigeot 	 */
439e3adcf8fSFrançois Tigeot 
440e3adcf8fSFrançois Tigeot 	return true;
441e3adcf8fSFrançois Tigeot }
442e3adcf8fSFrançois Tigeot 
443e3adcf8fSFrançois Tigeot /**
444e3adcf8fSFrançois Tigeot  * Detect the LVDS connection.
445e3adcf8fSFrançois Tigeot  *
446e3adcf8fSFrançois Tigeot  * Since LVDS doesn't have hotlug, we use the lid as a proxy.  Open means
447e3adcf8fSFrançois Tigeot  * connected and closed means disconnected.  We also send hotplug events as
448e3adcf8fSFrançois Tigeot  * needed, using lid status notification from the input layer.
449e3adcf8fSFrançois Tigeot  */
450e3adcf8fSFrançois Tigeot static enum drm_connector_status
intel_lvds_detect(struct drm_connector * connector,bool force)451e3adcf8fSFrançois Tigeot intel_lvds_detect(struct drm_connector *connector, bool force)
452e3adcf8fSFrançois Tigeot {
453a85cb24fSFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(connector->dev);
454e3adcf8fSFrançois Tigeot 	enum drm_connector_status status;
455e3adcf8fSFrançois Tigeot 
4569edbd4a0SFrançois Tigeot 	DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
457b4efbf42Szrj 		      connector->base.id, connector->name);
4589edbd4a0SFrançois Tigeot 
459a85cb24fSFrançois Tigeot 	status = intel_panel_detect(dev_priv);
460e3adcf8fSFrançois Tigeot 	if (status != connector_status_unknown)
461e3adcf8fSFrançois Tigeot 		return status;
462e3adcf8fSFrançois Tigeot 
463e3adcf8fSFrançois Tigeot 	return connector_status_connected;
464e3adcf8fSFrançois Tigeot }
465e3adcf8fSFrançois Tigeot 
466e3adcf8fSFrançois Tigeot /**
467e3adcf8fSFrançois Tigeot  * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
468e3adcf8fSFrançois Tigeot  */
intel_lvds_get_modes(struct drm_connector * connector)469e3adcf8fSFrançois Tigeot static int intel_lvds_get_modes(struct drm_connector *connector)
470e3adcf8fSFrançois Tigeot {
47119df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector = to_lvds_connector(connector);
472e3adcf8fSFrançois Tigeot 	struct drm_device *dev = connector->dev;
473e3adcf8fSFrançois Tigeot 	struct drm_display_mode *mode;
474e3adcf8fSFrançois Tigeot 
47519df918dSFrançois Tigeot 	/* use cached edid if we have one */
47619df918dSFrançois Tigeot 	if (!IS_ERR_OR_NULL(lvds_connector->base.edid))
47719df918dSFrançois Tigeot 		return drm_add_edid_modes(connector, lvds_connector->base.edid);
478e3adcf8fSFrançois Tigeot 
47919df918dSFrançois Tigeot 	mode = drm_mode_duplicate(dev, lvds_connector->base.panel.fixed_mode);
480e3adcf8fSFrançois Tigeot 	if (mode == NULL)
481e3adcf8fSFrançois Tigeot 		return 0;
482e3adcf8fSFrançois Tigeot 
483e3adcf8fSFrançois Tigeot 	drm_mode_probed_add(connector, mode);
484e3adcf8fSFrançois Tigeot 	return 1;
485e3adcf8fSFrançois Tigeot }
486e3adcf8fSFrançois Tigeot 
48776594197Szrj #if 0 /* unused */
488e3adcf8fSFrançois Tigeot static int intel_no_modeset_on_lid_dmi_callback(const struct dmi_system_id *id)
489e3adcf8fSFrançois Tigeot {
49019df918dSFrançois Tigeot 	DRM_INFO("Skipping forced modeset for %s\n", id->ident);
491e3adcf8fSFrançois Tigeot 	return 1;
492e3adcf8fSFrançois Tigeot }
493e3adcf8fSFrançois Tigeot 
494e3adcf8fSFrançois Tigeot /* The GPU hangs up on these systems if modeset is performed on LID open */
495e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_modeset_on_lid[] = {
496e3adcf8fSFrançois Tigeot 	{
497e3adcf8fSFrançois Tigeot 		.callback = intel_no_modeset_on_lid_dmi_callback,
498e3adcf8fSFrançois Tigeot 		.ident = "Toshiba Tecra A11",
499e3adcf8fSFrançois Tigeot 		.matches = {
500e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
501e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A11"),
502e3adcf8fSFrançois Tigeot 		},
503e3adcf8fSFrançois Tigeot 	},
504e3adcf8fSFrançois Tigeot 
505e3adcf8fSFrançois Tigeot 	{ }	/* terminating entry */
506e3adcf8fSFrançois Tigeot };
507e3adcf8fSFrançois Tigeot 
508e3adcf8fSFrançois Tigeot /*
509a2fdbec6SFrançois Tigeot  * Lid events. Note the use of 'modeset':
510a2fdbec6SFrançois Tigeot  *  - we set it to MODESET_ON_LID_OPEN on lid close,
511a2fdbec6SFrançois Tigeot  *    and set it to MODESET_DONE on open
512e3adcf8fSFrançois Tigeot  *  - we use it as a "only once" bit (ie we ignore
513a2fdbec6SFrançois Tigeot  *    duplicate events where it was already properly set)
514a2fdbec6SFrançois Tigeot  *  - the suspend/resume paths will set it to
515a2fdbec6SFrançois Tigeot  *    MODESET_SUSPENDED and ignore the lid open event,
516a2fdbec6SFrançois Tigeot  *    because they restore the mode ("lid open").
517e3adcf8fSFrançois Tigeot  */
518e3adcf8fSFrançois Tigeot static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
519e3adcf8fSFrançois Tigeot 			    void *unused)
520e3adcf8fSFrançois Tigeot {
52119df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector =
52219df918dSFrançois Tigeot 		container_of(nb, struct intel_lvds_connector, lid_notifier);
52319df918dSFrançois Tigeot 	struct drm_connector *connector = &lvds_connector->base.base;
52419df918dSFrançois Tigeot 	struct drm_device *dev = connector->dev;
525bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
526e3adcf8fSFrançois Tigeot 
527e3adcf8fSFrançois Tigeot 	if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
528e3adcf8fSFrançois Tigeot 		return NOTIFY_OK;
529e3adcf8fSFrançois Tigeot 
530a2fdbec6SFrançois Tigeot 	mutex_lock(&dev_priv->modeset_restore_lock);
531a2fdbec6SFrançois Tigeot 	if (dev_priv->modeset_restore == MODESET_SUSPENDED)
532a2fdbec6SFrançois Tigeot 		goto exit;
533e3adcf8fSFrançois Tigeot 	/*
534e3adcf8fSFrançois Tigeot 	 * check and update the status of LVDS connector after receiving
535e3adcf8fSFrançois Tigeot 	 * the LID nofication event.
536e3adcf8fSFrançois Tigeot 	 */
53719df918dSFrançois Tigeot 	connector->status = connector->funcs->detect(connector, false);
538e3adcf8fSFrançois Tigeot 
539e3adcf8fSFrançois Tigeot 	/* Don't force modeset on machines where it causes a GPU lockup */
540e3adcf8fSFrançois Tigeot 	if (dmi_check_system(intel_no_modeset_on_lid))
541a2fdbec6SFrançois Tigeot 		goto exit;
542e3adcf8fSFrançois Tigeot 	if (!acpi_lid_open()) {
543a2fdbec6SFrançois Tigeot 		/* do modeset on next lid open event */
544a2fdbec6SFrançois Tigeot 		dev_priv->modeset_restore = MODESET_ON_LID_OPEN;
545a2fdbec6SFrançois Tigeot 		goto exit;
546e3adcf8fSFrançois Tigeot 	}
547e3adcf8fSFrançois Tigeot 
548a2fdbec6SFrançois Tigeot 	if (dev_priv->modeset_restore == MODESET_DONE)
549a2fdbec6SFrançois Tigeot 		goto exit;
550e3adcf8fSFrançois Tigeot 
5519edbd4a0SFrançois Tigeot 	/*
5529edbd4a0SFrançois Tigeot 	 * Some old platform's BIOS love to wreak havoc while the lid is closed.
5539edbd4a0SFrançois Tigeot 	 * We try to detect this here and undo any damage. The split for PCH
5549edbd4a0SFrançois Tigeot 	 * platforms is rather conservative and a bit arbitrary expect that on
5559edbd4a0SFrançois Tigeot 	 * those platforms VGA disabling requires actual legacy VGA I/O access,
5569edbd4a0SFrançois Tigeot 	 * and as part of the cleanup in the hw state restore we also redisable
5579edbd4a0SFrançois Tigeot 	 * the vga plane.
5589edbd4a0SFrançois Tigeot 	 */
5591e12ee3bSFrançois Tigeot 	if (!HAS_PCH_SPLIT(dev_priv))
560a05eeebfSFrançois Tigeot 		intel_display_resume(dev);
561e3adcf8fSFrançois Tigeot 
562a2fdbec6SFrançois Tigeot 	dev_priv->modeset_restore = MODESET_DONE;
563a2fdbec6SFrançois Tigeot 
564a2fdbec6SFrançois Tigeot exit:
565a2fdbec6SFrançois Tigeot 	mutex_unlock(&dev_priv->modeset_restore_lock);
566e3adcf8fSFrançois Tigeot 	return NOTIFY_OK;
567e3adcf8fSFrançois Tigeot }
568e3adcf8fSFrançois Tigeot #endif
569e3adcf8fSFrançois Tigeot 
570e3adcf8fSFrançois Tigeot /**
571e3adcf8fSFrançois Tigeot  * intel_lvds_destroy - unregister and free LVDS structures
572e3adcf8fSFrançois Tigeot  * @connector: connector to free
573e3adcf8fSFrançois Tigeot  *
574e3adcf8fSFrançois Tigeot  * Unregister the DDC bus for this connector then free the driver private
575e3adcf8fSFrançois Tigeot  * structure.
576e3adcf8fSFrançois Tigeot  */
intel_lvds_destroy(struct drm_connector * connector)577e3adcf8fSFrançois Tigeot static void intel_lvds_destroy(struct drm_connector *connector)
578e3adcf8fSFrançois Tigeot {
57919df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector =
58019df918dSFrançois Tigeot 		to_lvds_connector(connector);
581e3adcf8fSFrançois Tigeot 
582e3adcf8fSFrançois Tigeot #if 0
58319df918dSFrançois Tigeot 	if (lvds_connector->lid_notifier.notifier_call)
58419df918dSFrançois Tigeot 		acpi_lid_notifier_unregister(&lvds_connector->lid_notifier);
585e3adcf8fSFrançois Tigeot #endif
58619df918dSFrançois Tigeot 
58719df918dSFrançois Tigeot 	if (!IS_ERR_OR_NULL(lvds_connector->base.edid))
588158486a6SFrançois Tigeot 		kfree(lvds_connector->base.edid);
58919df918dSFrançois Tigeot 
59019df918dSFrançois Tigeot 	intel_panel_fini(&lvds_connector->base.panel);
59119df918dSFrançois Tigeot 
592e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
593158486a6SFrançois Tigeot 	kfree(connector);
594e3adcf8fSFrançois Tigeot }
595e3adcf8fSFrançois Tigeot 
596e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
597e3adcf8fSFrançois Tigeot 	.get_modes = intel_lvds_get_modes,
598e3adcf8fSFrançois Tigeot 	.mode_valid = intel_lvds_mode_valid,
599*3f2dd94aSFrançois Tigeot 	.atomic_check = intel_digital_connector_atomic_check,
600e3adcf8fSFrançois Tigeot };
601e3adcf8fSFrançois Tigeot 
602e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_lvds_connector_funcs = {
603e3adcf8fSFrançois Tigeot 	.detect = intel_lvds_detect,
604e3adcf8fSFrançois Tigeot 	.fill_modes = drm_helper_probe_single_connector_modes,
605*3f2dd94aSFrançois Tigeot 	.atomic_get_property = intel_digital_connector_atomic_get_property,
606*3f2dd94aSFrançois Tigeot 	.atomic_set_property = intel_digital_connector_atomic_set_property,
6071487f786SFrançois Tigeot 	.late_register = intel_connector_register,
6081487f786SFrançois Tigeot 	.early_unregister = intel_connector_unregister,
609e3adcf8fSFrançois Tigeot 	.destroy = intel_lvds_destroy,
6102c9916cdSFrançois Tigeot 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
611*3f2dd94aSFrançois Tigeot 	.atomic_duplicate_state = intel_digital_connector_duplicate_state,
612e3adcf8fSFrançois Tigeot };
613e3adcf8fSFrançois Tigeot 
614e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
615e3adcf8fSFrançois Tigeot 	.destroy = intel_encoder_destroy,
616e3adcf8fSFrançois Tigeot };
617e3adcf8fSFrançois Tigeot 
intel_no_lvds_dmi_callback(const struct dmi_system_id * id)61824edb884SFrançois Tigeot static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
619e3adcf8fSFrançois Tigeot {
62019df918dSFrançois Tigeot 	DRM_INFO("Skipping LVDS initialization for %s\n", id->ident);
621e3adcf8fSFrançois Tigeot 	return 1;
622e3adcf8fSFrançois Tigeot }
623e3adcf8fSFrançois Tigeot 
624e3adcf8fSFrançois Tigeot /* These systems claim to have LVDS, but really don't */
625e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_lvds[] = {
626e3adcf8fSFrançois Tigeot 	{
627e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
628e3adcf8fSFrançois Tigeot 		.ident = "Apple Mac Mini (Core series)",
629e3adcf8fSFrançois Tigeot 		.matches = {
630e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
631e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"),
632e3adcf8fSFrançois Tigeot 		},
633e3adcf8fSFrançois Tigeot 	},
634e3adcf8fSFrançois Tigeot 	{
635e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
636e3adcf8fSFrançois Tigeot 		.ident = "Apple Mac Mini (Core 2 series)",
637e3adcf8fSFrançois Tigeot 		.matches = {
638e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
639e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "Macmini2,1"),
640e3adcf8fSFrançois Tigeot 		},
641e3adcf8fSFrançois Tigeot 	},
642e3adcf8fSFrançois Tigeot 	{
643e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
644e3adcf8fSFrançois Tigeot 		.ident = "MSI IM-945GSE-A",
645e3adcf8fSFrançois Tigeot 		.matches = {
646e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "MSI"),
647e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "A9830IMS"),
648e3adcf8fSFrançois Tigeot 		},
649e3adcf8fSFrançois Tigeot 	},
650e3adcf8fSFrançois Tigeot 	{
651e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
652e3adcf8fSFrançois Tigeot 		.ident = "Dell Studio Hybrid",
653e3adcf8fSFrançois Tigeot 		.matches = {
654e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
655e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
656e3adcf8fSFrançois Tigeot 		},
657e3adcf8fSFrançois Tigeot 	},
658e3adcf8fSFrançois Tigeot 	{
659e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
660e3adcf8fSFrançois Tigeot 		.ident = "Dell OptiPlex FX170",
661e3adcf8fSFrançois Tigeot 		.matches = {
662e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
663e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"),
664e3adcf8fSFrançois Tigeot 		},
665e3adcf8fSFrançois Tigeot 	},
666e3adcf8fSFrançois Tigeot 	{
667e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
668e3adcf8fSFrançois Tigeot 		.ident = "AOpen Mini PC",
669e3adcf8fSFrançois Tigeot 		.matches = {
670e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
671e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"),
672e3adcf8fSFrançois Tigeot 		},
673e3adcf8fSFrançois Tigeot 	},
674e3adcf8fSFrançois Tigeot 	{
675e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
676e3adcf8fSFrançois Tigeot 		.ident = "AOpen Mini PC MP915",
677e3adcf8fSFrançois Tigeot 		.matches = {
678e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
679e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"),
680e3adcf8fSFrançois Tigeot 		},
681e3adcf8fSFrançois Tigeot 	},
682e3adcf8fSFrançois Tigeot 	{
683e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
684e3adcf8fSFrançois Tigeot 		.ident = "AOpen i915GMm-HFS",
685e3adcf8fSFrançois Tigeot 		.matches = {
686e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
687e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
688e3adcf8fSFrançois Tigeot 		},
689e3adcf8fSFrançois Tigeot 	},
690e3adcf8fSFrançois Tigeot 	{
691e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
692e3adcf8fSFrançois Tigeot                 .ident = "AOpen i45GMx-I",
693e3adcf8fSFrançois Tigeot                 .matches = {
694e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
695e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"),
696e3adcf8fSFrançois Tigeot                 },
697e3adcf8fSFrançois Tigeot         },
698e3adcf8fSFrançois Tigeot 	{
699e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
700e3adcf8fSFrançois Tigeot 		.ident = "Aopen i945GTt-VFA",
701e3adcf8fSFrançois Tigeot 		.matches = {
702e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"),
703e3adcf8fSFrançois Tigeot 		},
704e3adcf8fSFrançois Tigeot 	},
705e3adcf8fSFrançois Tigeot 	{
706e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
707e3adcf8fSFrançois Tigeot 		.ident = "Clientron U800",
708e3adcf8fSFrançois Tigeot 		.matches = {
709e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
710e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "U800"),
711e3adcf8fSFrançois Tigeot 		},
712e3adcf8fSFrançois Tigeot 	},
713e3adcf8fSFrançois Tigeot 	{
714e3adcf8fSFrançois Tigeot                 .callback = intel_no_lvds_dmi_callback,
715e3adcf8fSFrançois Tigeot                 .ident = "Clientron E830",
716e3adcf8fSFrançois Tigeot                 .matches = {
717e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
718e3adcf8fSFrançois Tigeot                         DMI_MATCH(DMI_PRODUCT_NAME, "E830"),
719e3adcf8fSFrançois Tigeot                 },
720e3adcf8fSFrançois Tigeot         },
721e3adcf8fSFrançois Tigeot         {
722e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
723e3adcf8fSFrançois Tigeot 		.ident = "Asus EeeBox PC EB1007",
724e3adcf8fSFrançois Tigeot 		.matches = {
725e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
726e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "EB1007"),
727e3adcf8fSFrançois Tigeot 		},
728e3adcf8fSFrançois Tigeot 	},
729e3adcf8fSFrançois Tigeot 	{
730e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
731e3adcf8fSFrançois Tigeot 		.ident = "Asus AT5NM10T-I",
732e3adcf8fSFrançois Tigeot 		.matches = {
733e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
734e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"),
735e3adcf8fSFrançois Tigeot 		},
736e3adcf8fSFrançois Tigeot 	},
737e3adcf8fSFrançois Tigeot 	{
738e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
739a2fdbec6SFrançois Tigeot 		.ident = "Hewlett-Packard HP t5740",
74019df918dSFrançois Tigeot 		.matches = {
74119df918dSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
742a2fdbec6SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, " t5740"),
74319df918dSFrançois Tigeot 		},
74419df918dSFrançois Tigeot 	},
74519df918dSFrançois Tigeot 	{
74619df918dSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
747e3adcf8fSFrançois Tigeot 		.ident = "Hewlett-Packard t5745",
748e3adcf8fSFrançois Tigeot 		.matches = {
749e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
750e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "hp t5745"),
751e3adcf8fSFrançois Tigeot 		},
752e3adcf8fSFrançois Tigeot 	},
753e3adcf8fSFrançois Tigeot 	{
754e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
755e3adcf8fSFrançois Tigeot 		.ident = "Hewlett-Packard st5747",
756e3adcf8fSFrançois Tigeot 		.matches = {
757e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
758e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "hp st5747"),
759e3adcf8fSFrançois Tigeot 		},
760e3adcf8fSFrançois Tigeot 	},
761e3adcf8fSFrançois Tigeot 	{
762e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
763e3adcf8fSFrançois Tigeot 		.ident = "MSI Wind Box DC500",
764e3adcf8fSFrançois Tigeot 		.matches = {
765e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
766e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "MS-7469"),
767e3adcf8fSFrançois Tigeot 		},
768e3adcf8fSFrançois Tigeot 	},
769e3adcf8fSFrançois Tigeot 	{
770e3adcf8fSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
77119df918dSFrançois Tigeot 		.ident = "Gigabyte GA-D525TUD",
77219df918dSFrançois Tigeot 		.matches = {
77319df918dSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
77419df918dSFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "D525TUD"),
77519df918dSFrançois Tigeot 		},
77619df918dSFrançois Tigeot 	},
77719df918dSFrançois Tigeot 	{
77819df918dSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
779e3adcf8fSFrançois Tigeot 		.ident = "Supermicro X7SPA-H",
780e3adcf8fSFrançois Tigeot 		.matches = {
781e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
782e3adcf8fSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "X7SPA-H"),
783e3adcf8fSFrançois Tigeot 		},
784e3adcf8fSFrançois Tigeot 	},
78519df918dSFrançois Tigeot 	{
78619df918dSFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
78719df918dSFrançois Tigeot 		.ident = "Fujitsu Esprimo Q900",
78819df918dSFrançois Tigeot 		.matches = {
78919df918dSFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
79019df918dSFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"),
79119df918dSFrançois Tigeot 		},
79219df918dSFrançois Tigeot 	},
7935d0b1887SFrançois Tigeot 	{
7945d0b1887SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
7959edbd4a0SFrançois Tigeot 		.ident = "Intel D410PT",
7969edbd4a0SFrançois Tigeot 		.matches = {
7979edbd4a0SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
7989edbd4a0SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_NAME, "D410PT"),
7999edbd4a0SFrançois Tigeot 		},
8009edbd4a0SFrançois Tigeot 	},
8019edbd4a0SFrançois Tigeot 	{
8029edbd4a0SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8039edbd4a0SFrançois Tigeot 		.ident = "Intel D425KT",
8049edbd4a0SFrançois Tigeot 		.matches = {
8059edbd4a0SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8069edbd4a0SFrançois Tigeot 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "D425KT"),
8079edbd4a0SFrançois Tigeot 		},
8089edbd4a0SFrançois Tigeot 	},
8099edbd4a0SFrançois Tigeot 	{
8109edbd4a0SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8115d0b1887SFrançois Tigeot 		.ident = "Intel D510MO",
8125d0b1887SFrançois Tigeot 		.matches = {
8135d0b1887SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8145d0b1887SFrançois Tigeot 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "D510MO"),
8155d0b1887SFrançois Tigeot 		},
8165d0b1887SFrançois Tigeot 	},
8175d0b1887SFrançois Tigeot 	{
8185d0b1887SFrançois Tigeot 		.callback = intel_no_lvds_dmi_callback,
8195d0b1887SFrançois Tigeot 		.ident = "Intel D525MW",
8205d0b1887SFrançois Tigeot 		.matches = {
8215d0b1887SFrançois Tigeot 			DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
8225d0b1887SFrançois Tigeot 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"),
8235d0b1887SFrançois Tigeot 		},
8245d0b1887SFrançois Tigeot 	},
825e3adcf8fSFrançois Tigeot 
826e3adcf8fSFrançois Tigeot 	{ }	/* terminating entry */
827e3adcf8fSFrançois Tigeot };
828e3adcf8fSFrançois Tigeot 
intel_dual_link_lvds_callback(const struct dmi_system_id * id)829a2fdbec6SFrançois Tigeot static int intel_dual_link_lvds_callback(const struct dmi_system_id *id)
830a2fdbec6SFrançois Tigeot {
831a2fdbec6SFrançois Tigeot 	DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident);
832a2fdbec6SFrançois Tigeot 	return 1;
833a2fdbec6SFrançois Tigeot }
834a2fdbec6SFrançois Tigeot 
835a2fdbec6SFrançois Tigeot static const struct dmi_system_id intel_dual_link_lvds[] = {
836a2fdbec6SFrançois Tigeot 	{
837a2fdbec6SFrançois Tigeot 		.callback = intel_dual_link_lvds_callback,
838477eb7f9SFrançois Tigeot 		.ident = "Apple MacBook Pro 15\" (2010)",
839477eb7f9SFrançois Tigeot 		.matches = {
840477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
841477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6,2"),
842477eb7f9SFrançois Tigeot 		},
843477eb7f9SFrançois Tigeot 	},
844477eb7f9SFrançois Tigeot 	{
845477eb7f9SFrançois Tigeot 		.callback = intel_dual_link_lvds_callback,
846477eb7f9SFrançois Tigeot 		.ident = "Apple MacBook Pro 15\" (2011)",
847a2fdbec6SFrançois Tigeot 		.matches = {
848a2fdbec6SFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
849a2fdbec6SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"),
850a2fdbec6SFrançois Tigeot 		},
851a2fdbec6SFrançois Tigeot 	},
852477eb7f9SFrançois Tigeot 	{
853477eb7f9SFrançois Tigeot 		.callback = intel_dual_link_lvds_callback,
854477eb7f9SFrançois Tigeot 		.ident = "Apple MacBook Pro 15\" (2012)",
855477eb7f9SFrançois Tigeot 		.matches = {
856477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
857477eb7f9SFrançois Tigeot 			DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro9,1"),
858477eb7f9SFrançois Tigeot 		},
859477eb7f9SFrançois Tigeot 	},
860a2fdbec6SFrançois Tigeot 	{ }	/* terminating entry */
861a2fdbec6SFrançois Tigeot };
862a2fdbec6SFrançois Tigeot 
intel_get_lvds_encoder(struct drm_device * dev)8631487f786SFrançois Tigeot struct intel_encoder *intel_get_lvds_encoder(struct drm_device *dev)
8641487f786SFrançois Tigeot {
8651487f786SFrançois Tigeot 	struct intel_encoder *intel_encoder;
8661487f786SFrançois Tigeot 
8671487f786SFrançois Tigeot 	for_each_intel_encoder(dev, intel_encoder)
8681487f786SFrançois Tigeot 		if (intel_encoder->type == INTEL_OUTPUT_LVDS)
8691487f786SFrançois Tigeot 			return intel_encoder;
8701487f786SFrançois Tigeot 
8711487f786SFrançois Tigeot 	return NULL;
8721487f786SFrançois Tigeot }
8731487f786SFrançois Tigeot 
intel_is_dual_link_lvds(struct drm_device * dev)874a2fdbec6SFrançois Tigeot bool intel_is_dual_link_lvds(struct drm_device *dev)
875a2fdbec6SFrançois Tigeot {
8761487f786SFrançois Tigeot 	struct intel_encoder *encoder = intel_get_lvds_encoder(dev);
877a2fdbec6SFrançois Tigeot 
8781487f786SFrançois Tigeot 	return encoder && to_lvds_encoder(&encoder->base)->is_dual_link;
879a2fdbec6SFrançois Tigeot }
880a2fdbec6SFrançois Tigeot 
compute_is_dual_link_lvds(struct intel_lvds_encoder * lvds_encoder)881a2fdbec6SFrançois Tigeot static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
882a2fdbec6SFrançois Tigeot {
883a2fdbec6SFrançois Tigeot 	struct drm_device *dev = lvds_encoder->base.base.dev;
884a2fdbec6SFrançois Tigeot 	unsigned int val;
885bf017597SFrançois Tigeot 	struct drm_i915_private *dev_priv = to_i915(dev);
886a2fdbec6SFrançois Tigeot 
887a2fdbec6SFrançois Tigeot 	/* use the module option value if specified */
888*3f2dd94aSFrançois Tigeot 	if (i915_modparams.lvds_channel_mode > 0)
889*3f2dd94aSFrançois Tigeot 		return i915_modparams.lvds_channel_mode == 2;
890a2fdbec6SFrançois Tigeot 
891477eb7f9SFrançois Tigeot 	/* single channel LVDS is limited to 112 MHz */
892477eb7f9SFrançois Tigeot 	if (lvds_encoder->attached_connector->base.panel.fixed_mode->clock
893477eb7f9SFrançois Tigeot 	    > 112999)
894477eb7f9SFrançois Tigeot 		return true;
895477eb7f9SFrançois Tigeot 
896a2fdbec6SFrançois Tigeot 	if (dmi_check_system(intel_dual_link_lvds))
897a2fdbec6SFrançois Tigeot 		return true;
898a2fdbec6SFrançois Tigeot 
899a2fdbec6SFrançois Tigeot 	/* BIOS should set the proper LVDS register value at boot, but
900a2fdbec6SFrançois Tigeot 	 * in reality, it doesn't set the value when the lid is closed;
901a2fdbec6SFrançois Tigeot 	 * we need to check "the value to be set" in VBT when LVDS
902a2fdbec6SFrançois Tigeot 	 * register is uninitialized.
903a2fdbec6SFrançois Tigeot 	 */
904a2fdbec6SFrançois Tigeot 	val = I915_READ(lvds_encoder->reg);
905a2fdbec6SFrançois Tigeot 	if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED)))
9065d0b1887SFrançois Tigeot 		val = dev_priv->vbt.bios_lvds_val;
907a2fdbec6SFrançois Tigeot 
908a2fdbec6SFrançois Tigeot 	return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
909a2fdbec6SFrançois Tigeot }
910a2fdbec6SFrançois Tigeot 
intel_lvds_supported(struct drm_i915_private * dev_priv)9111e12ee3bSFrançois Tigeot static bool intel_lvds_supported(struct drm_i915_private *dev_priv)
912e3adcf8fSFrançois Tigeot {
913e3adcf8fSFrançois Tigeot 	/* With the introduction of the PCH we gained a dedicated
914e3adcf8fSFrançois Tigeot 	 * LVDS presence pin, use it. */
9151e12ee3bSFrançois Tigeot 	if (HAS_PCH_IBX(dev_priv) || HAS_PCH_CPT(dev_priv))
916e3adcf8fSFrançois Tigeot 		return true;
917e3adcf8fSFrançois Tigeot 
918e3adcf8fSFrançois Tigeot 	/* Otherwise LVDS was only attached to mobile products,
919e3adcf8fSFrançois Tigeot 	 * except for the inglorious 830gm */
9201e12ee3bSFrançois Tigeot 	if (INTEL_GEN(dev_priv) <= 4 &&
9211e12ee3bSFrançois Tigeot 	    IS_MOBILE(dev_priv) && !IS_I830(dev_priv))
9228e26cdf6SFrançois Tigeot 		return true;
9238e26cdf6SFrançois Tigeot 
9248e26cdf6SFrançois Tigeot 	return false;
925e3adcf8fSFrançois Tigeot }
926e3adcf8fSFrançois Tigeot 
927e3adcf8fSFrançois Tigeot /**
928e3adcf8fSFrançois Tigeot  * intel_lvds_init - setup LVDS connectors on this device
929e3adcf8fSFrançois Tigeot  * @dev: drm device
930e3adcf8fSFrançois Tigeot  *
931e3adcf8fSFrançois Tigeot  * Create the connector, register the LVDS DDC bus, and try to figure out what
932e3adcf8fSFrançois Tigeot  * modes we can display on the LVDS panel (if present).
933e3adcf8fSFrançois Tigeot  */
intel_lvds_init(struct drm_i915_private * dev_priv)934a85cb24fSFrançois Tigeot void intel_lvds_init(struct drm_i915_private *dev_priv)
935e3adcf8fSFrançois Tigeot {
936a85cb24fSFrançois Tigeot 	struct drm_device *dev = &dev_priv->drm;
93719df918dSFrançois Tigeot 	struct intel_lvds_encoder *lvds_encoder;
938e3adcf8fSFrançois Tigeot 	struct intel_encoder *intel_encoder;
93919df918dSFrançois Tigeot 	struct intel_lvds_connector *lvds_connector;
940e3adcf8fSFrançois Tigeot 	struct intel_connector *intel_connector;
941e3adcf8fSFrançois Tigeot 	struct drm_connector *connector;
942e3adcf8fSFrançois Tigeot 	struct drm_encoder *encoder;
943e3adcf8fSFrançois Tigeot 	struct drm_display_mode *scan; /* *modes, *bios_mode; */
94419df918dSFrançois Tigeot 	struct drm_display_mode *fixed_mode = NULL;
945ba55f2f5SFrançois Tigeot 	struct drm_display_mode *downclock_mode = NULL;
94619df918dSFrançois Tigeot 	struct edid *edid;
947aee94f86SFrançois Tigeot 	i915_reg_t lvds_reg;
948e3adcf8fSFrançois Tigeot 	u32 lvds;
949e3adcf8fSFrançois Tigeot 	u8 pin;
950*3f2dd94aSFrançois Tigeot 	u32 allowed_scalers;
951e3adcf8fSFrançois Tigeot 
9521e12ee3bSFrançois Tigeot 	if (!intel_lvds_supported(dev_priv))
9535d0b1887SFrançois Tigeot 		return;
954e3adcf8fSFrançois Tigeot 
955e3adcf8fSFrançois Tigeot 	/* Skip init on machines we know falsely report LVDS */
956e3adcf8fSFrançois Tigeot 	if (dmi_check_system(intel_no_lvds))
9575d0b1887SFrançois Tigeot 		return;
958e3adcf8fSFrançois Tigeot 
9591e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv))
960352ff8bdSFrançois Tigeot 		lvds_reg = PCH_LVDS;
961352ff8bdSFrançois Tigeot 	else
962352ff8bdSFrançois Tigeot 		lvds_reg = LVDS;
963352ff8bdSFrançois Tigeot 
964352ff8bdSFrançois Tigeot 	lvds = I915_READ(lvds_reg);
965352ff8bdSFrançois Tigeot 
9661e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
967352ff8bdSFrançois Tigeot 		if ((lvds & LVDS_DETECTED) == 0)
9685d0b1887SFrançois Tigeot 			return;
9698621f407SFrançois Tigeot 		if (dev_priv->vbt.edp.support) {
970e3adcf8fSFrançois Tigeot 			DRM_DEBUG_KMS("disable LVDS for eDP support\n");
9715d0b1887SFrançois Tigeot 			return;
972e3adcf8fSFrançois Tigeot 		}
973e3adcf8fSFrançois Tigeot 	}
974e3adcf8fSFrançois Tigeot 
975a05eeebfSFrançois Tigeot 	pin = GMBUS_PIN_PANEL;
9768621f407SFrançois Tigeot 	if (!intel_bios_is_lvds_present(dev_priv, &pin)) {
977352ff8bdSFrançois Tigeot 		if ((lvds & LVDS_PORT_EN) == 0) {
978a05eeebfSFrançois Tigeot 			DRM_DEBUG_KMS("LVDS is not present in VBT\n");
979a05eeebfSFrançois Tigeot 			return;
980a05eeebfSFrançois Tigeot 		}
981a05eeebfSFrançois Tigeot 		DRM_DEBUG_KMS("LVDS is not present in VBT, but enabled anyway\n");
982a05eeebfSFrançois Tigeot 	}
983a05eeebfSFrançois Tigeot 
9849edbd4a0SFrançois Tigeot 	lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL);
98519df918dSFrançois Tigeot 	if (!lvds_encoder)
9865d0b1887SFrançois Tigeot 		return;
987e3adcf8fSFrançois Tigeot 
9889edbd4a0SFrançois Tigeot 	lvds_connector = kzalloc(sizeof(*lvds_connector), GFP_KERNEL);
98919df918dSFrançois Tigeot 	if (!lvds_connector) {
990158486a6SFrançois Tigeot 		kfree(lvds_encoder);
9915d0b1887SFrançois Tigeot 		return;
992e3adcf8fSFrançois Tigeot 	}
993e3adcf8fSFrançois Tigeot 
994477eb7f9SFrançois Tigeot 	if (intel_connector_init(&lvds_connector->base) < 0) {
995477eb7f9SFrançois Tigeot 		kfree(lvds_connector);
996477eb7f9SFrançois Tigeot 		kfree(lvds_encoder);
997477eb7f9SFrançois Tigeot 		return;
998477eb7f9SFrançois Tigeot 	}
999477eb7f9SFrançois Tigeot 
100019df918dSFrançois Tigeot 	lvds_encoder->attached_connector = lvds_connector;
100119df918dSFrançois Tigeot 
100219df918dSFrançois Tigeot 	intel_encoder = &lvds_encoder->base;
1003e3adcf8fSFrançois Tigeot 	encoder = &intel_encoder->base;
100419df918dSFrançois Tigeot 	intel_connector = &lvds_connector->base;
1005e3adcf8fSFrançois Tigeot 	connector = &intel_connector->base;
1006e3adcf8fSFrançois Tigeot 	drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs,
1007e3adcf8fSFrançois Tigeot 			   DRM_MODE_CONNECTOR_LVDS);
1008e3adcf8fSFrançois Tigeot 
1009e3adcf8fSFrançois Tigeot 	drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
10101487f786SFrançois Tigeot 			 DRM_MODE_ENCODER_LVDS, "LVDS");
1011e3adcf8fSFrançois Tigeot 
101219df918dSFrançois Tigeot 	intel_encoder->enable = intel_enable_lvds;
10139edbd4a0SFrançois Tigeot 	intel_encoder->pre_enable = intel_pre_enable_lvds;
10148e26cdf6SFrançois Tigeot 	intel_encoder->compute_config = intel_lvds_compute_config;
1015a05eeebfSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv)) {
1016a05eeebfSFrançois Tigeot 		intel_encoder->disable = pch_disable_lvds;
1017a05eeebfSFrançois Tigeot 		intel_encoder->post_disable = pch_post_disable_lvds;
1018a05eeebfSFrançois Tigeot 	} else {
1019a05eeebfSFrançois Tigeot 		intel_encoder->disable = gmch_disable_lvds;
1020a05eeebfSFrançois Tigeot 	}
102119df918dSFrançois Tigeot 	intel_encoder->get_hw_state = intel_lvds_get_hw_state;
10225d0b1887SFrançois Tigeot 	intel_encoder->get_config = intel_lvds_get_config;
102319df918dSFrançois Tigeot 	intel_connector->get_hw_state = intel_connector_get_hw_state;
102419df918dSFrançois Tigeot 
1025e3adcf8fSFrançois Tigeot 	intel_connector_attach_encoder(intel_connector, intel_encoder);
1026e3adcf8fSFrançois Tigeot 
10271e12ee3bSFrançois Tigeot 	intel_encoder->type = INTEL_OUTPUT_LVDS;
1028a85cb24fSFrançois Tigeot 	intel_encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
10291e12ee3bSFrançois Tigeot 	intel_encoder->port = PORT_NONE;
1030ba55f2f5SFrançois Tigeot 	intel_encoder->cloneable = 0;
10311e12ee3bSFrançois Tigeot 	if (HAS_PCH_SPLIT(dev_priv))
1032e3adcf8fSFrançois Tigeot 		intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
10331e12ee3bSFrançois Tigeot 	else if (IS_GEN4(dev_priv))
103419df918dSFrançois Tigeot 		intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
1035e3adcf8fSFrançois Tigeot 	else
1036e3adcf8fSFrançois Tigeot 		intel_encoder->crtc_mask = (1 << 1);
1037e3adcf8fSFrançois Tigeot 
1038e3adcf8fSFrançois Tigeot 	drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
1039e3adcf8fSFrançois Tigeot 	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
1040e3adcf8fSFrançois Tigeot 	connector->interlace_allowed = false;
1041e3adcf8fSFrançois Tigeot 	connector->doublescan_allowed = false;
1042e3adcf8fSFrançois Tigeot 
1043352ff8bdSFrançois Tigeot 	lvds_encoder->reg = lvds_reg;
1044a2fdbec6SFrançois Tigeot 
1045e3adcf8fSFrançois Tigeot 	/* create the scaling mode property */
1046*3f2dd94aSFrançois Tigeot 	allowed_scalers = BIT(DRM_MODE_SCALE_ASPECT);
1047*3f2dd94aSFrançois Tigeot 	allowed_scalers |= BIT(DRM_MODE_SCALE_FULLSCREEN);
1048*3f2dd94aSFrançois Tigeot 	allowed_scalers |= BIT(DRM_MODE_SCALE_CENTER);
1049*3f2dd94aSFrançois Tigeot 	drm_connector_attach_scaling_mode_property(connector, allowed_scalers);
1050*3f2dd94aSFrançois Tigeot 	connector->state->scaling_mode = DRM_MODE_SCALE_ASPECT;
10511e12ee3bSFrançois Tigeot 
10521e12ee3bSFrançois Tigeot 	intel_lvds_pps_get_hw_state(dev_priv, &lvds_encoder->init_pps);
10531e12ee3bSFrançois Tigeot 	lvds_encoder->init_lvds_val = lvds;
10541e12ee3bSFrançois Tigeot 
1055e3adcf8fSFrançois Tigeot 	/*
1056e3adcf8fSFrançois Tigeot 	 * LVDS discovery:
1057e3adcf8fSFrançois Tigeot 	 * 1) check for EDID on DDC
1058e3adcf8fSFrançois Tigeot 	 * 2) check for VBT data
1059e3adcf8fSFrançois Tigeot 	 * 3) check to see if LVDS is already on
1060e3adcf8fSFrançois Tigeot 	 *    if none of the above, no panel
1061e3adcf8fSFrançois Tigeot 	 * 4) make sure lid is open
1062e3adcf8fSFrançois Tigeot 	 *    if closed, act like it's not there for now
1063e3adcf8fSFrançois Tigeot 	 */
1064e3adcf8fSFrançois Tigeot 
1065e3adcf8fSFrançois Tigeot 	/*
1066e3adcf8fSFrançois Tigeot 	 * Attempt to get the fixed panel mode from DDC.  Assume that the
1067e3adcf8fSFrançois Tigeot 	 * preferred mode is the right one.
1068e3adcf8fSFrançois Tigeot 	 */
1069ba55f2f5SFrançois Tigeot 	mutex_lock(&dev->mode_config.mutex);
1070c0e85e96SFrançois Tigeot 	if (vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC)
1071c0e85e96SFrançois Tigeot 		edid = drm_get_edid_switcheroo(connector,
1072c0e85e96SFrançois Tigeot 				    intel_gmbus_get_adapter(dev_priv, pin));
1073c0e85e96SFrançois Tigeot 	else
1074c0e85e96SFrançois Tigeot 		edid = drm_get_edid(connector,
1075c0e85e96SFrançois Tigeot 				    intel_gmbus_get_adapter(dev_priv, pin));
107619df918dSFrançois Tigeot 	if (edid) {
107719df918dSFrançois Tigeot 		if (drm_add_edid_modes(connector, edid)) {
1078e3adcf8fSFrançois Tigeot 			drm_mode_connector_update_edid_property(connector,
107919df918dSFrançois Tigeot 								edid);
1080e3adcf8fSFrançois Tigeot 		} else {
1081158486a6SFrançois Tigeot 			kfree(edid);
108219df918dSFrançois Tigeot 			edid = ERR_PTR(-EINVAL);
1083e3adcf8fSFrançois Tigeot 		}
108419df918dSFrançois Tigeot 	} else {
108519df918dSFrançois Tigeot 		edid = ERR_PTR(-ENOENT);
1086e3adcf8fSFrançois Tigeot 	}
108719df918dSFrançois Tigeot 	lvds_connector->base.edid = edid;
108819df918dSFrançois Tigeot 
1089e3adcf8fSFrançois Tigeot 	list_for_each_entry(scan, &connector->probed_modes, head) {
1090e3adcf8fSFrançois Tigeot 		if (scan->type & DRM_MODE_TYPE_PREFERRED) {
109119df918dSFrançois Tigeot 			DRM_DEBUG_KMS("using preferred mode from EDID: ");
109219df918dSFrançois Tigeot 			drm_mode_debug_printmodeline(scan);
109319df918dSFrançois Tigeot 
109419df918dSFrançois Tigeot 			fixed_mode = drm_mode_duplicate(dev, scan);
1095a05eeebfSFrançois Tigeot 			if (fixed_mode)
1096e3adcf8fSFrançois Tigeot 				goto out;
1097e3adcf8fSFrançois Tigeot 		}
1098e3adcf8fSFrançois Tigeot 	}
1099e3adcf8fSFrançois Tigeot 
1100e3adcf8fSFrançois Tigeot 	/* Failed to get EDID, what about VBT? */
11015d0b1887SFrançois Tigeot 	if (dev_priv->vbt.lfp_lvds_vbt_mode) {
110219df918dSFrançois Tigeot 		DRM_DEBUG_KMS("using mode from VBT: ");
11035d0b1887SFrançois Tigeot 		drm_mode_debug_printmodeline(dev_priv->vbt.lfp_lvds_vbt_mode);
110419df918dSFrançois Tigeot 
11055d0b1887SFrançois Tigeot 		fixed_mode = drm_mode_duplicate(dev, dev_priv->vbt.lfp_lvds_vbt_mode);
110619df918dSFrançois Tigeot 		if (fixed_mode) {
110719df918dSFrançois Tigeot 			fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
11088621f407SFrançois Tigeot 			connector->display_info.width_mm = fixed_mode->width_mm;
11098621f407SFrançois Tigeot 			connector->display_info.height_mm = fixed_mode->height_mm;
1110e3adcf8fSFrançois Tigeot 			goto out;
1111e3adcf8fSFrançois Tigeot 		}
1112e3adcf8fSFrançois Tigeot 	}
1113e3adcf8fSFrançois Tigeot 
1114e3adcf8fSFrançois Tigeot 	/*
1115e3adcf8fSFrançois Tigeot 	 * If we didn't get EDID, try checking if the panel is already turned
1116e3adcf8fSFrançois Tigeot 	 * on.  If so, assume that whatever is currently programmed is the
1117e3adcf8fSFrançois Tigeot 	 * correct mode.
1118e3adcf8fSFrançois Tigeot 	 */
1119*3f2dd94aSFrançois Tigeot 	fixed_mode = intel_encoder_current_mode(intel_encoder);
112019df918dSFrançois Tigeot 	if (fixed_mode) {
112119df918dSFrançois Tigeot 		DRM_DEBUG_KMS("using current (BIOS) mode: ");
112219df918dSFrançois Tigeot 		drm_mode_debug_printmodeline(fixed_mode);
112319df918dSFrançois Tigeot 		fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
1124e3adcf8fSFrançois Tigeot 	}
1125e3adcf8fSFrançois Tigeot 
1126e3adcf8fSFrançois Tigeot 	/* If we still don't have a mode after all that, give up. */
112719df918dSFrançois Tigeot 	if (!fixed_mode)
1128e3adcf8fSFrançois Tigeot 		goto failed;
1129e3adcf8fSFrançois Tigeot 
1130e3adcf8fSFrançois Tigeot out:
1131ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
1132ba55f2f5SFrançois Tigeot 
1133*3f2dd94aSFrançois Tigeot 	intel_panel_init(&intel_connector->panel, fixed_mode, NULL,
1134*3f2dd94aSFrançois Tigeot 			 downclock_mode);
11351487f786SFrançois Tigeot 	intel_panel_setup_backlight(connector, INVALID_PIPE);
1136477eb7f9SFrançois Tigeot 
1137a2fdbec6SFrançois Tigeot 	lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder);
1138a2fdbec6SFrançois Tigeot 	DRM_DEBUG_KMS("detected %s-link lvds configuration\n",
1139a2fdbec6SFrançois Tigeot 		      lvds_encoder->is_dual_link ? "dual" : "single");
1140a2fdbec6SFrançois Tigeot 
1141aee94f86SFrançois Tigeot 	lvds_encoder->a3_power = lvds & LVDS_A3_POWER_MASK;
114224edb884SFrançois Tigeot 
1143e3adcf8fSFrançois Tigeot #if 0
114419df918dSFrançois Tigeot 	lvds_connector->lid_notifier.notifier_call = intel_lid_notify;
114519df918dSFrançois Tigeot 	if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) {
114619df918dSFrançois Tigeot 		DRM_DEBUG_KMS("lid notifier registration failed\n");
114719df918dSFrançois Tigeot 		lvds_connector->lid_notifier.notifier_call = NULL;
114819df918dSFrançois Tigeot 	}
1149e3adcf8fSFrançois Tigeot #endif
115019df918dSFrançois Tigeot 
11515d0b1887SFrançois Tigeot 	return;
1152e3adcf8fSFrançois Tigeot 
1153e3adcf8fSFrançois Tigeot failed:
1154ba55f2f5SFrançois Tigeot 	mutex_unlock(&dev->mode_config.mutex);
1155ba55f2f5SFrançois Tigeot 
1156e3adcf8fSFrançois Tigeot 	DRM_DEBUG_KMS("No LVDS modes found, disabling.\n");
1157e3adcf8fSFrançois Tigeot 	drm_connector_cleanup(connector);
1158e3adcf8fSFrançois Tigeot 	drm_encoder_cleanup(encoder);
1159158486a6SFrançois Tigeot 	kfree(lvds_encoder);
1160158486a6SFrançois Tigeot 	kfree(lvds_connector);
11615d0b1887SFrançois Tigeot 	return;
1162e3adcf8fSFrançois Tigeot }
1163