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