1379bc100SJani Nikula /*
2379bc100SJani Nikula * Copyright © 2006-2007 Intel Corporation
3379bc100SJani Nikula * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
4379bc100SJani Nikula *
5379bc100SJani Nikula * Permission is hereby granted, free of charge, to any person obtaining a
6379bc100SJani Nikula * copy of this software and associated documentation files (the "Software"),
7379bc100SJani Nikula * to deal in the Software without restriction, including without limitation
8379bc100SJani Nikula * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9379bc100SJani Nikula * and/or sell copies of the Software, and to permit persons to whom the
10379bc100SJani Nikula * Software is furnished to do so, subject to the following conditions:
11379bc100SJani Nikula *
12379bc100SJani Nikula * The above copyright notice and this permission notice (including the next
13379bc100SJani Nikula * paragraph) shall be included in all copies or substantial portions of the
14379bc100SJani Nikula * Software.
15379bc100SJani Nikula *
16379bc100SJani Nikula * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17379bc100SJani Nikula * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18379bc100SJani Nikula * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19379bc100SJani Nikula * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20379bc100SJani Nikula * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21379bc100SJani Nikula * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22379bc100SJani Nikula * DEALINGS IN THE SOFTWARE.
23379bc100SJani Nikula *
24379bc100SJani Nikula * Authors:
25379bc100SJani Nikula * Eric Anholt <eric@anholt.net>
26379bc100SJani Nikula * Dave Airlie <airlied@linux.ie>
27379bc100SJani Nikula * Jesse Barnes <jesse.barnes@intel.com>
28379bc100SJani Nikula */
29379bc100SJani Nikula
30379bc100SJani Nikula #include <acpi/button.h>
31379bc100SJani Nikula #include <linux/acpi.h>
32379bc100SJani Nikula #include <linux/dmi.h>
33379bc100SJani Nikula #include <linux/i2c.h>
34379bc100SJani Nikula #include <linux/slab.h>
35379bc100SJani Nikula #include <linux/vga_switcheroo.h>
36379bc100SJani Nikula
37379bc100SJani Nikula #include <drm/drm_atomic_helper.h>
38379bc100SJani Nikula #include <drm/drm_crtc.h>
39379bc100SJani Nikula #include <drm/drm_edid.h>
40379bc100SJani Nikula
41379bc100SJani Nikula #include "i915_drv.h"
42801543b2SJani Nikula #include "i915_reg.h"
43379bc100SJani Nikula #include "intel_atomic.h"
446cc42fbeSJani Nikula #include "intel_backlight.h"
45379bc100SJani Nikula #include "intel_connector.h"
467785ae0bSVille Syrjälä #include "intel_de.h"
471d455f8dSJani Nikula #include "intel_display_types.h"
4880e77e30SJani Nikula #include "intel_dpll.h"
49e04a911fSJani Nikula #include "intel_fdi.h"
50379bc100SJani Nikula #include "intel_gmbus.h"
51379bc100SJani Nikula #include "intel_lvds.h"
5216bede13SVille Syrjälä #include "intel_lvds_regs.h"
53379bc100SJani Nikula #include "intel_panel.h"
54065695b3SJani Nikula #include "intel_pps_regs.h"
55379bc100SJani Nikula
56379bc100SJani Nikula /* Private structure for the integrated LVDS support */
57379bc100SJani Nikula struct intel_lvds_pps {
58379bc100SJani Nikula /* 100us units */
59379bc100SJani Nikula int t1_t2;
60379bc100SJani Nikula int t3;
61379bc100SJani Nikula int t4;
62379bc100SJani Nikula int t5;
63379bc100SJani Nikula int tx;
64379bc100SJani Nikula
65379bc100SJani Nikula int divider;
66379bc100SJani Nikula
67379bc100SJani Nikula int port;
68379bc100SJani Nikula bool powerdown_on_reset;
69379bc100SJani Nikula };
70379bc100SJani Nikula
71379bc100SJani Nikula struct intel_lvds_encoder {
72379bc100SJani Nikula struct intel_encoder base;
73379bc100SJani Nikula
74379bc100SJani Nikula bool is_dual_link;
75379bc100SJani Nikula i915_reg_t reg;
76379bc100SJani Nikula u32 a3_power;
77379bc100SJani Nikula
78379bc100SJani Nikula struct intel_lvds_pps init_pps;
79379bc100SJani Nikula u32 init_lvds_val;
80379bc100SJani Nikula
81379bc100SJani Nikula struct intel_connector *attached_connector;
82379bc100SJani Nikula };
83379bc100SJani Nikula
to_lvds_encoder(struct intel_encoder * encoder)845e800d92SVille Syrjälä static struct intel_lvds_encoder *to_lvds_encoder(struct intel_encoder *encoder)
85379bc100SJani Nikula {
865e800d92SVille Syrjälä return container_of(encoder, struct intel_lvds_encoder, base);
87379bc100SJani Nikula }
88379bc100SJani Nikula
intel_lvds_port_enabled(struct drm_i915_private * i915,i915_reg_t lvds_reg,enum pipe * pipe)897f66476cSVille Syrjälä bool intel_lvds_port_enabled(struct drm_i915_private *i915,
90379bc100SJani Nikula i915_reg_t lvds_reg, enum pipe *pipe)
91379bc100SJani Nikula {
92379bc100SJani Nikula u32 val;
93379bc100SJani Nikula
947f66476cSVille Syrjälä val = intel_de_read(i915, lvds_reg);
95379bc100SJani Nikula
96379bc100SJani Nikula /* asserts want to know the pipe even if the port is disabled */
977f66476cSVille Syrjälä if (HAS_PCH_CPT(i915))
989dd56e97SVille Syrjälä *pipe = REG_FIELD_GET(LVDS_PIPE_SEL_MASK_CPT, val);
99379bc100SJani Nikula else
1009dd56e97SVille Syrjälä *pipe = REG_FIELD_GET(LVDS_PIPE_SEL_MASK, val);
101379bc100SJani Nikula
102379bc100SJani Nikula return val & LVDS_PORT_EN;
103379bc100SJani Nikula }
104379bc100SJani Nikula
intel_lvds_get_hw_state(struct intel_encoder * encoder,enum pipe * pipe)105379bc100SJani Nikula static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
106379bc100SJani Nikula enum pipe *pipe)
107379bc100SJani Nikula {
1087f66476cSVille Syrjälä struct drm_i915_private *i915 = to_i915(encoder->base.dev);
1095e800d92SVille Syrjälä struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(encoder);
110379bc100SJani Nikula intel_wakeref_t wakeref;
111379bc100SJani Nikula bool ret;
112379bc100SJani Nikula
1137f66476cSVille Syrjälä wakeref = intel_display_power_get_if_enabled(i915, encoder->power_domain);
114379bc100SJani Nikula if (!wakeref)
115379bc100SJani Nikula return false;
116379bc100SJani Nikula
1177f66476cSVille Syrjälä ret = intel_lvds_port_enabled(i915, lvds_encoder->reg, pipe);
118379bc100SJani Nikula
1197f66476cSVille Syrjälä intel_display_power_put(i915, encoder->power_domain, wakeref);
120379bc100SJani Nikula
121379bc100SJani Nikula return ret;
122379bc100SJani Nikula }
123379bc100SJani Nikula
intel_lvds_get_config(struct intel_encoder * encoder,struct intel_crtc_state * crtc_state)124379bc100SJani Nikula static void intel_lvds_get_config(struct intel_encoder *encoder,
1257fd7eacfSVille Syrjälä struct intel_crtc_state *crtc_state)
126379bc100SJani Nikula {
127379bc100SJani Nikula struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
1285e800d92SVille Syrjälä struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(encoder);
129379bc100SJani Nikula u32 tmp, flags = 0;
130379bc100SJani Nikula
1317fd7eacfSVille Syrjälä crtc_state->output_types |= BIT(INTEL_OUTPUT_LVDS);
132379bc100SJani Nikula
133cc80e362SJani Nikula tmp = intel_de_read(dev_priv, lvds_encoder->reg);
134379bc100SJani Nikula if (tmp & LVDS_HSYNC_POLARITY)
135379bc100SJani Nikula flags |= DRM_MODE_FLAG_NHSYNC;
136379bc100SJani Nikula else
137379bc100SJani Nikula flags |= DRM_MODE_FLAG_PHSYNC;
138379bc100SJani Nikula if (tmp & LVDS_VSYNC_POLARITY)
139379bc100SJani Nikula flags |= DRM_MODE_FLAG_NVSYNC;
140379bc100SJani Nikula else
141379bc100SJani Nikula flags |= DRM_MODE_FLAG_PVSYNC;
142379bc100SJani Nikula
1437fd7eacfSVille Syrjälä crtc_state->hw.adjusted_mode.flags |= flags;
144379bc100SJani Nikula
145005e9537SMatt Roper if (DISPLAY_VER(dev_priv) < 5)
1467fd7eacfSVille Syrjälä crtc_state->gmch_pfit.lvds_border_bits =
147379bc100SJani Nikula tmp & LVDS_BORDER_ENABLE;
148379bc100SJani Nikula
149379bc100SJani Nikula /* gen2/3 store dither state in pfit control, needs to match */
150005e9537SMatt Roper if (DISPLAY_VER(dev_priv) < 4) {
151cc80e362SJani Nikula tmp = intel_de_read(dev_priv, PFIT_CONTROL);
152379bc100SJani Nikula
153e27525ccSVille Syrjälä crtc_state->gmch_pfit.control |= tmp & PFIT_PANEL_8TO6_DITHER_ENABLE;
154379bc100SJani Nikula }
155379bc100SJani Nikula
1567fd7eacfSVille Syrjälä crtc_state->hw.adjusted_mode.crtc_clock = crtc_state->port_clock;
157379bc100SJani Nikula }
158379bc100SJani Nikula
intel_lvds_pps_get_hw_state(struct drm_i915_private * dev_priv,struct intel_lvds_pps * pps)159379bc100SJani Nikula static void intel_lvds_pps_get_hw_state(struct drm_i915_private *dev_priv,
160379bc100SJani Nikula struct intel_lvds_pps *pps)
161379bc100SJani Nikula {
162379bc100SJani Nikula u32 val;
163379bc100SJani Nikula
164cc80e362SJani Nikula pps->powerdown_on_reset = intel_de_read(dev_priv, PP_CONTROL(0)) & PANEL_POWER_RESET;
165379bc100SJani Nikula
166cc80e362SJani Nikula val = intel_de_read(dev_priv, PP_ON_DELAYS(0));
167379bc100SJani Nikula pps->port = REG_FIELD_GET(PANEL_PORT_SELECT_MASK, val);
168379bc100SJani Nikula pps->t1_t2 = REG_FIELD_GET(PANEL_POWER_UP_DELAY_MASK, val);
169379bc100SJani Nikula pps->t5 = REG_FIELD_GET(PANEL_LIGHT_ON_DELAY_MASK, val);
170379bc100SJani Nikula
171cc80e362SJani Nikula val = intel_de_read(dev_priv, PP_OFF_DELAYS(0));
172379bc100SJani Nikula pps->t3 = REG_FIELD_GET(PANEL_POWER_DOWN_DELAY_MASK, val);
173379bc100SJani Nikula pps->tx = REG_FIELD_GET(PANEL_LIGHT_OFF_DELAY_MASK, val);
174379bc100SJani Nikula
175cc80e362SJani Nikula val = intel_de_read(dev_priv, PP_DIVISOR(0));
176379bc100SJani Nikula pps->divider = REG_FIELD_GET(PP_REFERENCE_DIVIDER_MASK, val);
177379bc100SJani Nikula val = REG_FIELD_GET(PANEL_POWER_CYCLE_DELAY_MASK, val);
178379bc100SJani Nikula /*
179379bc100SJani Nikula * Remove the BSpec specified +1 (100ms) offset that accounts for a
180379bc100SJani Nikula * too short power-cycle delay due to the asynchronous programming of
181379bc100SJani Nikula * the register.
182379bc100SJani Nikula */
183379bc100SJani Nikula if (val)
184379bc100SJani Nikula val--;
185379bc100SJani Nikula /* Convert from 100ms to 100us units */
186379bc100SJani Nikula pps->t4 = val * 1000;
187379bc100SJani Nikula
188005e9537SMatt Roper if (DISPLAY_VER(dev_priv) <= 4 &&
189379bc100SJani Nikula pps->t1_t2 == 0 && pps->t5 == 0 && pps->t3 == 0 && pps->tx == 0) {
190900b8c9eSWambui Karuga drm_dbg_kms(&dev_priv->drm,
191900b8c9eSWambui Karuga "Panel power timings uninitialized, "
192379bc100SJani Nikula "setting defaults\n");
193379bc100SJani Nikula /* Set T2 to 40ms and T5 to 200ms in 100 usec units */
194379bc100SJani Nikula pps->t1_t2 = 40 * 10;
195379bc100SJani Nikula pps->t5 = 200 * 10;
196379bc100SJani Nikula /* Set T3 to 35ms and Tx to 200ms in 100 usec units */
197379bc100SJani Nikula pps->t3 = 35 * 10;
198379bc100SJani Nikula pps->tx = 200 * 10;
199379bc100SJani Nikula }
200379bc100SJani Nikula
201900b8c9eSWambui Karuga drm_dbg(&dev_priv->drm, "LVDS PPS:t1+t2 %d t3 %d t4 %d t5 %d tx %d "
202379bc100SJani Nikula "divider %d port %d powerdown_on_reset %d\n",
203379bc100SJani Nikula pps->t1_t2, pps->t3, pps->t4, pps->t5, pps->tx,
204379bc100SJani Nikula pps->divider, pps->port, pps->powerdown_on_reset);
205379bc100SJani Nikula }
206379bc100SJani Nikula
intel_lvds_pps_init_hw(struct drm_i915_private * dev_priv,struct intel_lvds_pps * pps)207379bc100SJani Nikula static void intel_lvds_pps_init_hw(struct drm_i915_private *dev_priv,
208379bc100SJani Nikula struct intel_lvds_pps *pps)
209379bc100SJani Nikula {
210379bc100SJani Nikula u32 val;
211379bc100SJani Nikula
212cc80e362SJani Nikula val = intel_de_read(dev_priv, PP_CONTROL(0));
213f4224a4cSPankaj Bharadiya drm_WARN_ON(&dev_priv->drm,
214f4224a4cSPankaj Bharadiya (val & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS);
215379bc100SJani Nikula if (pps->powerdown_on_reset)
216379bc100SJani Nikula val |= PANEL_POWER_RESET;
217cc80e362SJani Nikula intel_de_write(dev_priv, PP_CONTROL(0), val);
218379bc100SJani Nikula
219cc80e362SJani Nikula intel_de_write(dev_priv, PP_ON_DELAYS(0),
220c76f6727SVille Syrjälä REG_FIELD_PREP(PANEL_PORT_SELECT_MASK, pps->port) |
221c76f6727SVille Syrjälä REG_FIELD_PREP(PANEL_POWER_UP_DELAY_MASK, pps->t1_t2) |
222c76f6727SVille Syrjälä REG_FIELD_PREP(PANEL_LIGHT_ON_DELAY_MASK, pps->t5));
223379bc100SJani Nikula
224cc80e362SJani Nikula intel_de_write(dev_priv, PP_OFF_DELAYS(0),
225c76f6727SVille Syrjälä REG_FIELD_PREP(PANEL_POWER_DOWN_DELAY_MASK, pps->t3) |
226c76f6727SVille Syrjälä REG_FIELD_PREP(PANEL_LIGHT_OFF_DELAY_MASK, pps->tx));
227379bc100SJani Nikula
228cc80e362SJani Nikula intel_de_write(dev_priv, PP_DIVISOR(0),
229c76f6727SVille Syrjälä REG_FIELD_PREP(PP_REFERENCE_DIVIDER_MASK, pps->divider) |
230c76f6727SVille Syrjälä REG_FIELD_PREP(PANEL_POWER_CYCLE_DELAY_MASK, DIV_ROUND_UP(pps->t4, 1000) + 1));
231379bc100SJani Nikula }
232379bc100SJani Nikula
intel_pre_enable_lvds(struct intel_atomic_state * state,struct intel_encoder * encoder,const struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)233ede9771dSVille Syrjälä static void intel_pre_enable_lvds(struct intel_atomic_state *state,
234ede9771dSVille Syrjälä struct intel_encoder *encoder,
2357fd7eacfSVille Syrjälä const struct intel_crtc_state *crtc_state,
236379bc100SJani Nikula const struct drm_connector_state *conn_state)
237379bc100SJani Nikula {
2385e800d92SVille Syrjälä struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(encoder);
2397f66476cSVille Syrjälä struct drm_i915_private *i915 = to_i915(encoder->base.dev);
2407fd7eacfSVille Syrjälä struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
2417fd7eacfSVille Syrjälä const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
242d048a268SVille Syrjälä enum pipe pipe = crtc->pipe;
243379bc100SJani Nikula u32 temp;
244379bc100SJani Nikula
2457f66476cSVille Syrjälä if (HAS_PCH_SPLIT(i915)) {
2467f66476cSVille Syrjälä assert_fdi_rx_pll_disabled(i915, pipe);
2477fd7eacfSVille Syrjälä assert_shared_dpll_disabled(i915, crtc_state->shared_dpll);
248379bc100SJani Nikula } else {
2497f66476cSVille Syrjälä assert_pll_disabled(i915, pipe);
250379bc100SJani Nikula }
251379bc100SJani Nikula
2527f66476cSVille Syrjälä intel_lvds_pps_init_hw(i915, &lvds_encoder->init_pps);
253379bc100SJani Nikula
254379bc100SJani Nikula temp = lvds_encoder->init_lvds_val;
255379bc100SJani Nikula temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
256379bc100SJani Nikula
2577f66476cSVille Syrjälä if (HAS_PCH_CPT(i915)) {
258379bc100SJani Nikula temp &= ~LVDS_PIPE_SEL_MASK_CPT;
259379bc100SJani Nikula temp |= LVDS_PIPE_SEL_CPT(pipe);
260379bc100SJani Nikula } else {
261379bc100SJani Nikula temp &= ~LVDS_PIPE_SEL_MASK;
262379bc100SJani Nikula temp |= LVDS_PIPE_SEL(pipe);
263379bc100SJani Nikula }
264379bc100SJani Nikula
265379bc100SJani Nikula /* set the corresponsding LVDS_BORDER bit */
266379bc100SJani Nikula temp &= ~LVDS_BORDER_ENABLE;
2677fd7eacfSVille Syrjälä temp |= crtc_state->gmch_pfit.lvds_border_bits;
268379bc100SJani Nikula
269379bc100SJani Nikula /*
270379bc100SJani Nikula * Set the B0-B3 data pairs corresponding to whether we're going to
271379bc100SJani Nikula * set the DPLLs for dual-channel mode or not.
272379bc100SJani Nikula */
273379bc100SJani Nikula if (lvds_encoder->is_dual_link)
274379bc100SJani Nikula temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
275379bc100SJani Nikula else
276379bc100SJani Nikula temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
277379bc100SJani Nikula
278379bc100SJani Nikula /*
279379bc100SJani Nikula * It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
280379bc100SJani Nikula * appropriately here, but we need to look more thoroughly into how
281379bc100SJani Nikula * panels behave in the two modes. For now, let's just maintain the
282379bc100SJani Nikula * value we got from the BIOS.
283379bc100SJani Nikula */
284379bc100SJani Nikula temp &= ~LVDS_A3_POWER_MASK;
285379bc100SJani Nikula temp |= lvds_encoder->a3_power;
286379bc100SJani Nikula
287379bc100SJani Nikula /*
288379bc100SJani Nikula * Set the dithering flag on LVDS as needed, note that there is no
289379bc100SJani Nikula * special lvds dither control bit on pch-split platforms, dithering is
2903eb08ea5SVille Syrjälä * only controlled through the TRANSCONF reg.
291379bc100SJani Nikula */
2927f66476cSVille Syrjälä if (DISPLAY_VER(i915) == 4) {
293379bc100SJani Nikula /*
294379bc100SJani Nikula * Bspec wording suggests that LVDS port dithering only exists
295379bc100SJani Nikula * for 18bpp panels.
296379bc100SJani Nikula */
2977fd7eacfSVille Syrjälä if (crtc_state->dither && crtc_state->pipe_bpp == 18)
298379bc100SJani Nikula temp |= LVDS_ENABLE_DITHER;
299379bc100SJani Nikula else
300379bc100SJani Nikula temp &= ~LVDS_ENABLE_DITHER;
301379bc100SJani Nikula }
302379bc100SJani Nikula temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
303379bc100SJani Nikula if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
304379bc100SJani Nikula temp |= LVDS_HSYNC_POLARITY;
305379bc100SJani Nikula if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
306379bc100SJani Nikula temp |= LVDS_VSYNC_POLARITY;
307379bc100SJani Nikula
3087f66476cSVille Syrjälä intel_de_write(i915, lvds_encoder->reg, temp);
309379bc100SJani Nikula }
310379bc100SJani Nikula
311379bc100SJani Nikula /*
312379bc100SJani Nikula * Sets the power state for the panel.
313379bc100SJani Nikula */
intel_enable_lvds(struct intel_atomic_state * state,struct intel_encoder * encoder,const struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)314ede9771dSVille Syrjälä static void intel_enable_lvds(struct intel_atomic_state *state,
315ede9771dSVille Syrjälä struct intel_encoder *encoder,
3167fd7eacfSVille Syrjälä const struct intel_crtc_state *crtc_state,
317379bc100SJani Nikula const struct drm_connector_state *conn_state)
318379bc100SJani Nikula {
3195e800d92SVille Syrjälä struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(encoder);
3207f66476cSVille Syrjälä struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
321379bc100SJani Nikula
3222324cdffSVille Syrjälä intel_de_rmw(dev_priv, lvds_encoder->reg, 0, LVDS_PORT_EN);
323379bc100SJani Nikula
3242324cdffSVille Syrjälä intel_de_rmw(dev_priv, PP_CONTROL(0), 0, PANEL_POWER_ON);
325cc80e362SJani Nikula intel_de_posting_read(dev_priv, lvds_encoder->reg);
326379bc100SJani Nikula
3274cb3b44dSDaniele Ceraolo Spurio if (intel_de_wait_for_set(dev_priv, PP_STATUS(0), PP_ON, 5000))
328900b8c9eSWambui Karuga drm_err(&dev_priv->drm,
329900b8c9eSWambui Karuga "timed out waiting for panel to power on\n");
330379bc100SJani Nikula
3317fd7eacfSVille Syrjälä intel_backlight_enable(crtc_state, conn_state);
332379bc100SJani Nikula }
333379bc100SJani Nikula
intel_disable_lvds(struct intel_atomic_state * state,struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)334ede9771dSVille Syrjälä static void intel_disable_lvds(struct intel_atomic_state *state,
335ede9771dSVille Syrjälä struct intel_encoder *encoder,
336379bc100SJani Nikula const struct intel_crtc_state *old_crtc_state,
337379bc100SJani Nikula const struct drm_connector_state *old_conn_state)
338379bc100SJani Nikula {
3395e800d92SVille Syrjälä struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(encoder);
340379bc100SJani Nikula struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
341379bc100SJani Nikula
3422324cdffSVille Syrjälä intel_de_rmw(dev_priv, PP_CONTROL(0), PANEL_POWER_ON, 0);
3434cb3b44dSDaniele Ceraolo Spurio if (intel_de_wait_for_clear(dev_priv, PP_STATUS(0), PP_ON, 1000))
344900b8c9eSWambui Karuga drm_err(&dev_priv->drm,
345900b8c9eSWambui Karuga "timed out waiting for panel to power off\n");
346379bc100SJani Nikula
3472324cdffSVille Syrjälä intel_de_rmw(dev_priv, lvds_encoder->reg, LVDS_PORT_EN, 0);
348cc80e362SJani Nikula intel_de_posting_read(dev_priv, lvds_encoder->reg);
349379bc100SJani Nikula }
350379bc100SJani Nikula
gmch_disable_lvds(struct intel_atomic_state * state,struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)351ede9771dSVille Syrjälä static void gmch_disable_lvds(struct intel_atomic_state *state,
352ede9771dSVille Syrjälä struct intel_encoder *encoder,
353379bc100SJani Nikula const struct intel_crtc_state *old_crtc_state,
354379bc100SJani Nikula const struct drm_connector_state *old_conn_state)
355379bc100SJani Nikula
356379bc100SJani Nikula {
357c0a52f8bSJani Nikula intel_backlight_disable(old_conn_state);
358379bc100SJani Nikula
359ede9771dSVille Syrjälä intel_disable_lvds(state, encoder, old_crtc_state, old_conn_state);
360379bc100SJani Nikula }
361379bc100SJani Nikula
pch_disable_lvds(struct intel_atomic_state * state,struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)362ede9771dSVille Syrjälä static void pch_disable_lvds(struct intel_atomic_state *state,
363ede9771dSVille Syrjälä struct intel_encoder *encoder,
364379bc100SJani Nikula const struct intel_crtc_state *old_crtc_state,
365379bc100SJani Nikula const struct drm_connector_state *old_conn_state)
366379bc100SJani Nikula {
367c0a52f8bSJani Nikula intel_backlight_disable(old_conn_state);
368379bc100SJani Nikula }
369379bc100SJani Nikula
pch_post_disable_lvds(struct intel_atomic_state * state,struct intel_encoder * encoder,const struct intel_crtc_state * old_crtc_state,const struct drm_connector_state * old_conn_state)370ede9771dSVille Syrjälä static void pch_post_disable_lvds(struct intel_atomic_state *state,
371ede9771dSVille Syrjälä struct intel_encoder *encoder,
372379bc100SJani Nikula const struct intel_crtc_state *old_crtc_state,
373379bc100SJani Nikula const struct drm_connector_state *old_conn_state)
374379bc100SJani Nikula {
375ede9771dSVille Syrjälä intel_disable_lvds(state, encoder, old_crtc_state, old_conn_state);
376379bc100SJani Nikula }
377379bc100SJani Nikula
intel_lvds_shutdown(struct intel_encoder * encoder)378d2008827SVille Syrjälä static void intel_lvds_shutdown(struct intel_encoder *encoder)
379d2008827SVille Syrjälä {
380d2008827SVille Syrjälä struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
381d2008827SVille Syrjälä
382d2008827SVille Syrjälä if (intel_de_wait_for_clear(dev_priv, PP_STATUS(0), PP_CYCLE_DELAY_ACTIVE, 5000))
383d2008827SVille Syrjälä drm_err(&dev_priv->drm,
384d2008827SVille Syrjälä "timed out waiting for panel power cycle delay\n");
385d2008827SVille Syrjälä }
386d2008827SVille Syrjälä
387379bc100SJani Nikula static enum drm_mode_status
intel_lvds_mode_valid(struct drm_connector * _connector,struct drm_display_mode * mode)38819d7dc66SVille Syrjälä intel_lvds_mode_valid(struct drm_connector *_connector,
389379bc100SJani Nikula struct drm_display_mode *mode)
390379bc100SJani Nikula {
39119d7dc66SVille Syrjälä struct intel_connector *connector = to_intel_connector(_connector);
392*8e1e489cSVille Syrjälä struct drm_i915_private *i915 = to_i915(connector->base.dev);
39309270678SVille Syrjälä const struct drm_display_mode *fixed_mode =
39419d7dc66SVille Syrjälä intel_panel_fixed_mode(connector, mode);
39519d7dc66SVille Syrjälä int max_pixclk = to_i915(connector->base.dev)->max_dotclk_freq;
3968a567b11SVille Syrjälä enum drm_mode_status status;
397379bc100SJani Nikula
398*8e1e489cSVille Syrjälä status = intel_cpu_transcoder_mode_valid(i915, mode);
399*8e1e489cSVille Syrjälä if (status != MODE_OK)
400*8e1e489cSVille Syrjälä return status;
401*8e1e489cSVille Syrjälä
402379bc100SJani Nikula if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
403379bc100SJani Nikula return MODE_NO_DBLESCAN;
4048a567b11SVille Syrjälä
40519d7dc66SVille Syrjälä status = intel_panel_mode_valid(connector, mode);
4068a567b11SVille Syrjälä if (status != MODE_OK)
4078a567b11SVille Syrjälä return status;
4088a567b11SVille Syrjälä
409379bc100SJani Nikula if (fixed_mode->clock > max_pixclk)
410379bc100SJani Nikula return MODE_CLOCK_HIGH;
411379bc100SJani Nikula
412379bc100SJani Nikula return MODE_OK;
413379bc100SJani Nikula }
414379bc100SJani Nikula
intel_lvds_compute_config(struct intel_encoder * encoder,struct intel_crtc_state * crtc_state,struct drm_connector_state * conn_state)41519d7dc66SVille Syrjälä static int intel_lvds_compute_config(struct intel_encoder *encoder,
4167fd7eacfSVille Syrjälä struct intel_crtc_state *crtc_state,
417379bc100SJani Nikula struct drm_connector_state *conn_state)
418379bc100SJani Nikula {
41919d7dc66SVille Syrjälä struct drm_i915_private *i915 = to_i915(encoder->base.dev);
42019d7dc66SVille Syrjälä struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(encoder);
42119d7dc66SVille Syrjälä struct intel_connector *connector = lvds_encoder->attached_connector;
4227fd7eacfSVille Syrjälä struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode;
4237fd7eacfSVille Syrjälä struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
424379bc100SJani Nikula unsigned int lvds_bpp;
425d7ff281cSVille Syrjälä int ret;
426379bc100SJani Nikula
427379bc100SJani Nikula /* Should never happen!! */
4287f66476cSVille Syrjälä if (DISPLAY_VER(i915) < 4 && crtc->pipe == 0) {
4297f66476cSVille Syrjälä drm_err(&i915->drm, "Can't support LVDS on pipe A\n");
430379bc100SJani Nikula return -EINVAL;
431379bc100SJani Nikula }
432379bc100SJani Nikula
433379bc100SJani Nikula if (lvds_encoder->a3_power == LVDS_A3_POWER_UP)
434379bc100SJani Nikula lvds_bpp = 8*3;
435379bc100SJani Nikula else
436379bc100SJani Nikula lvds_bpp = 6*3;
437379bc100SJani Nikula
4387fd7eacfSVille Syrjälä if (lvds_bpp != crtc_state->pipe_bpp && !crtc_state->bw_constrained) {
4397f66476cSVille Syrjälä drm_dbg_kms(&i915->drm,
440900b8c9eSWambui Karuga "forcing display bpp (was %d) to LVDS (%d)\n",
4417fd7eacfSVille Syrjälä crtc_state->pipe_bpp, lvds_bpp);
4427fd7eacfSVille Syrjälä crtc_state->pipe_bpp = lvds_bpp;
443379bc100SJani Nikula }
444379bc100SJani Nikula
445a04d27cdSAnkit Nautiyal crtc_state->sink_format = INTEL_OUTPUT_FORMAT_RGB;
4467fd7eacfSVille Syrjälä crtc_state->output_format = INTEL_OUTPUT_FORMAT_RGB;
447379bc100SJani Nikula
448379bc100SJani Nikula /*
449379bc100SJani Nikula * We have timings from the BIOS for the panel, put them in
450379bc100SJani Nikula * to the adjusted mode. The CRTC will be set up for this mode,
451379bc100SJani Nikula * with the panel scaling set up to source from the H/VDisplay
452379bc100SJani Nikula * of the original mode.
453379bc100SJani Nikula */
45419d7dc66SVille Syrjälä ret = intel_panel_compute_config(connector, adjusted_mode);
455cff4c2c6SVille Syrjälä if (ret)
456cff4c2c6SVille Syrjälä return ret;
457379bc100SJani Nikula
458379bc100SJani Nikula if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
459379bc100SJani Nikula return -EINVAL;
460379bc100SJani Nikula
4617f66476cSVille Syrjälä if (HAS_PCH_SPLIT(i915))
4627fd7eacfSVille Syrjälä crtc_state->has_pch_encoder = true;
463379bc100SJani Nikula
4647fd7eacfSVille Syrjälä ret = intel_panel_fitting(crtc_state, conn_state);
465d7ff281cSVille Syrjälä if (ret)
466d7ff281cSVille Syrjälä return ret;
467379bc100SJani Nikula
468379bc100SJani Nikula /*
469379bc100SJani Nikula * XXX: It would be nice to support lower refresh rates on the
470379bc100SJani Nikula * panels to reduce power consumption, and perhaps match the
471379bc100SJani Nikula * user's requested refresh rate.
472379bc100SJani Nikula */
473379bc100SJani Nikula
474379bc100SJani Nikula return 0;
475379bc100SJani Nikula }
476379bc100SJani Nikula
477379bc100SJani Nikula /*
478379bc100SJani Nikula * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
479379bc100SJani Nikula */
intel_lvds_get_modes(struct drm_connector * _connector)48019d7dc66SVille Syrjälä static int intel_lvds_get_modes(struct drm_connector *_connector)
481379bc100SJani Nikula {
48219d7dc66SVille Syrjälä struct intel_connector *connector = to_intel_connector(_connector);
48319d7dc66SVille Syrjälä const struct drm_edid *fixed_edid = connector->panel.fixed_edid;
484379bc100SJani Nikula
48515d045fdSJani Nikula /* Use panel fixed edid if we have one */
48615d045fdSJani Nikula if (!IS_ERR_OR_NULL(fixed_edid)) {
48719d7dc66SVille Syrjälä drm_edid_connector_update(&connector->base, fixed_edid);
48825fa6b0fSJani Nikula
48919d7dc66SVille Syrjälä return drm_edid_connector_add_modes(&connector->base);
49025fa6b0fSJani Nikula }
491379bc100SJani Nikula
49219d7dc66SVille Syrjälä return intel_panel_get_modes(connector);
493379bc100SJani Nikula }
494379bc100SJani Nikula
495379bc100SJani Nikula static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
496379bc100SJani Nikula .get_modes = intel_lvds_get_modes,
497379bc100SJani Nikula .mode_valid = intel_lvds_mode_valid,
498379bc100SJani Nikula .atomic_check = intel_digital_connector_atomic_check,
499379bc100SJani Nikula };
500379bc100SJani Nikula
501379bc100SJani Nikula static const struct drm_connector_funcs intel_lvds_connector_funcs = {
502b81dddb9SVille Syrjälä .detect = intel_panel_detect,
503379bc100SJani Nikula .fill_modes = drm_helper_probe_single_connector_modes,
504379bc100SJani Nikula .atomic_get_property = intel_digital_connector_atomic_get_property,
505379bc100SJani Nikula .atomic_set_property = intel_digital_connector_atomic_set_property,
506379bc100SJani Nikula .late_register = intel_connector_register,
507379bc100SJani Nikula .early_unregister = intel_connector_unregister,
508379bc100SJani Nikula .destroy = intel_connector_destroy,
509379bc100SJani Nikula .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
510379bc100SJani Nikula .atomic_duplicate_state = intel_digital_connector_duplicate_state,
511379bc100SJani Nikula };
512379bc100SJani Nikula
513379bc100SJani Nikula static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
514379bc100SJani Nikula .destroy = intel_encoder_destroy,
515379bc100SJani Nikula };
516379bc100SJani Nikula
intel_no_lvds_dmi_callback(const struct dmi_system_id * id)517379bc100SJani Nikula static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
518379bc100SJani Nikula {
519379bc100SJani Nikula DRM_INFO("Skipping LVDS initialization for %s\n", id->ident);
520379bc100SJani Nikula return 1;
521379bc100SJani Nikula }
522379bc100SJani Nikula
523379bc100SJani Nikula /* These systems claim to have LVDS, but really don't */
524379bc100SJani Nikula static const struct dmi_system_id intel_no_lvds[] = {
525379bc100SJani Nikula {
526379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
527379bc100SJani Nikula .ident = "Apple Mac Mini (Core series)",
528379bc100SJani Nikula .matches = {
529379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
530379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"),
531379bc100SJani Nikula },
532379bc100SJani Nikula },
533379bc100SJani Nikula {
534379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
535379bc100SJani Nikula .ident = "Apple Mac Mini (Core 2 series)",
536379bc100SJani Nikula .matches = {
537379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
538379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "Macmini2,1"),
539379bc100SJani Nikula },
540379bc100SJani Nikula },
541379bc100SJani Nikula {
542379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
543379bc100SJani Nikula .ident = "MSI IM-945GSE-A",
544379bc100SJani Nikula .matches = {
545379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "MSI"),
546379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "A9830IMS"),
547379bc100SJani Nikula },
548379bc100SJani Nikula },
549379bc100SJani Nikula {
550379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
551379bc100SJani Nikula .ident = "Dell Studio Hybrid",
552379bc100SJani Nikula .matches = {
553379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
554379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
555379bc100SJani Nikula },
556379bc100SJani Nikula },
557379bc100SJani Nikula {
558379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
559379bc100SJani Nikula .ident = "Dell OptiPlex FX170",
560379bc100SJani Nikula .matches = {
561379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
562379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"),
563379bc100SJani Nikula },
564379bc100SJani Nikula },
565379bc100SJani Nikula {
566379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
567379bc100SJani Nikula .ident = "AOpen Mini PC",
568379bc100SJani Nikula .matches = {
569379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
570379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"),
571379bc100SJani Nikula },
572379bc100SJani Nikula },
573379bc100SJani Nikula {
574379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
575379bc100SJani Nikula .ident = "AOpen Mini PC MP915",
576379bc100SJani Nikula .matches = {
577379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
578379bc100SJani Nikula DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"),
579379bc100SJani Nikula },
580379bc100SJani Nikula },
581379bc100SJani Nikula {
582379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
583379bc100SJani Nikula .ident = "AOpen i915GMm-HFS",
584379bc100SJani Nikula .matches = {
585379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
586379bc100SJani Nikula DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
587379bc100SJani Nikula },
588379bc100SJani Nikula },
589379bc100SJani Nikula {
590379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
591379bc100SJani Nikula .ident = "AOpen i45GMx-I",
592379bc100SJani Nikula .matches = {
593379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
594379bc100SJani Nikula DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"),
595379bc100SJani Nikula },
596379bc100SJani Nikula },
597379bc100SJani Nikula {
598379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
599379bc100SJani Nikula .ident = "Aopen i945GTt-VFA",
600379bc100SJani Nikula .matches = {
601379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"),
602379bc100SJani Nikula },
603379bc100SJani Nikula },
604379bc100SJani Nikula {
605379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
606379bc100SJani Nikula .ident = "Clientron U800",
607379bc100SJani Nikula .matches = {
608379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
609379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "U800"),
610379bc100SJani Nikula },
611379bc100SJani Nikula },
612379bc100SJani Nikula {
613379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
614379bc100SJani Nikula .ident = "Clientron E830",
615379bc100SJani Nikula .matches = {
616379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
617379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "E830"),
618379bc100SJani Nikula },
619379bc100SJani Nikula },
620379bc100SJani Nikula {
621379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
622379bc100SJani Nikula .ident = "Asus EeeBox PC EB1007",
623379bc100SJani Nikula .matches = {
624379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
625379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "EB1007"),
626379bc100SJani Nikula },
627379bc100SJani Nikula },
628379bc100SJani Nikula {
629379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
630379bc100SJani Nikula .ident = "Asus AT5NM10T-I",
631379bc100SJani Nikula .matches = {
632379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
633379bc100SJani Nikula DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"),
634379bc100SJani Nikula },
635379bc100SJani Nikula },
636379bc100SJani Nikula {
637379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
638379bc100SJani Nikula .ident = "Hewlett-Packard HP t5740",
639379bc100SJani Nikula .matches = {
640379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
641379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, " t5740"),
642379bc100SJani Nikula },
643379bc100SJani Nikula },
644379bc100SJani Nikula {
645379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
646379bc100SJani Nikula .ident = "Hewlett-Packard t5745",
647379bc100SJani Nikula .matches = {
648379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
649379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "hp t5745"),
650379bc100SJani Nikula },
651379bc100SJani Nikula },
652379bc100SJani Nikula {
653379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
654379bc100SJani Nikula .ident = "Hewlett-Packard st5747",
655379bc100SJani Nikula .matches = {
656379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
657379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "hp st5747"),
658379bc100SJani Nikula },
659379bc100SJani Nikula },
660379bc100SJani Nikula {
661379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
662379bc100SJani Nikula .ident = "MSI Wind Box DC500",
663379bc100SJani Nikula .matches = {
664379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
665379bc100SJani Nikula DMI_MATCH(DMI_BOARD_NAME, "MS-7469"),
666379bc100SJani Nikula },
667379bc100SJani Nikula },
668379bc100SJani Nikula {
669379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
670379bc100SJani Nikula .ident = "Gigabyte GA-D525TUD",
671379bc100SJani Nikula .matches = {
672379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
673379bc100SJani Nikula DMI_MATCH(DMI_BOARD_NAME, "D525TUD"),
674379bc100SJani Nikula },
675379bc100SJani Nikula },
676379bc100SJani Nikula {
677379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
678379bc100SJani Nikula .ident = "Supermicro X7SPA-H",
679379bc100SJani Nikula .matches = {
680379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
681379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "X7SPA-H"),
682379bc100SJani Nikula },
683379bc100SJani Nikula },
684379bc100SJani Nikula {
685379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
686379bc100SJani Nikula .ident = "Fujitsu Esprimo Q900",
687379bc100SJani Nikula .matches = {
688379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
689379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"),
690379bc100SJani Nikula },
691379bc100SJani Nikula },
692379bc100SJani Nikula {
693379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
694379bc100SJani Nikula .ident = "Intel D410PT",
695379bc100SJani Nikula .matches = {
696379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
697379bc100SJani Nikula DMI_MATCH(DMI_BOARD_NAME, "D410PT"),
698379bc100SJani Nikula },
699379bc100SJani Nikula },
700379bc100SJani Nikula {
701379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
702379bc100SJani Nikula .ident = "Intel D425KT",
703379bc100SJani Nikula .matches = {
704379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
705379bc100SJani Nikula DMI_EXACT_MATCH(DMI_BOARD_NAME, "D425KT"),
706379bc100SJani Nikula },
707379bc100SJani Nikula },
708379bc100SJani Nikula {
709379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
710379bc100SJani Nikula .ident = "Intel D510MO",
711379bc100SJani Nikula .matches = {
712379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
713379bc100SJani Nikula DMI_EXACT_MATCH(DMI_BOARD_NAME, "D510MO"),
714379bc100SJani Nikula },
715379bc100SJani Nikula },
716379bc100SJani Nikula {
717379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
718379bc100SJani Nikula .ident = "Intel D525MW",
719379bc100SJani Nikula .matches = {
720379bc100SJani Nikula DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
721379bc100SJani Nikula DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"),
722379bc100SJani Nikula },
723379bc100SJani Nikula },
724379bc100SJani Nikula {
725379bc100SJani Nikula .callback = intel_no_lvds_dmi_callback,
726379bc100SJani Nikula .ident = "Radiant P845",
727379bc100SJani Nikula .matches = {
728379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Radiant Systems Inc"),
729379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "P845"),
730379bc100SJani Nikula },
731379bc100SJani Nikula },
732379bc100SJani Nikula
733379bc100SJani Nikula { } /* terminating entry */
734379bc100SJani Nikula };
735379bc100SJani Nikula
intel_dual_link_lvds_callback(const struct dmi_system_id * id)736379bc100SJani Nikula static int intel_dual_link_lvds_callback(const struct dmi_system_id *id)
737379bc100SJani Nikula {
738379bc100SJani Nikula DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident);
739379bc100SJani Nikula return 1;
740379bc100SJani Nikula }
741379bc100SJani Nikula
742379bc100SJani Nikula static const struct dmi_system_id intel_dual_link_lvds[] = {
743379bc100SJani Nikula {
744379bc100SJani Nikula .callback = intel_dual_link_lvds_callback,
745379bc100SJani Nikula .ident = "Apple MacBook Pro 15\" (2010)",
746379bc100SJani Nikula .matches = {
747379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
748379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6,2"),
749379bc100SJani Nikula },
750379bc100SJani Nikula },
751379bc100SJani Nikula {
752379bc100SJani Nikula .callback = intel_dual_link_lvds_callback,
753379bc100SJani Nikula .ident = "Apple MacBook Pro 15\" (2011)",
754379bc100SJani Nikula .matches = {
755379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
756379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"),
757379bc100SJani Nikula },
758379bc100SJani Nikula },
759379bc100SJani Nikula {
760379bc100SJani Nikula .callback = intel_dual_link_lvds_callback,
761379bc100SJani Nikula .ident = "Apple MacBook Pro 15\" (2012)",
762379bc100SJani Nikula .matches = {
763379bc100SJani Nikula DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
764379bc100SJani Nikula DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro9,1"),
765379bc100SJani Nikula },
766379bc100SJani Nikula },
767379bc100SJani Nikula { } /* terminating entry */
768379bc100SJani Nikula };
769379bc100SJani Nikula
intel_get_lvds_encoder(struct drm_i915_private * i915)7707f66476cSVille Syrjälä struct intel_encoder *intel_get_lvds_encoder(struct drm_i915_private *i915)
771379bc100SJani Nikula {
772379bc100SJani Nikula struct intel_encoder *encoder;
773379bc100SJani Nikula
7747f66476cSVille Syrjälä for_each_intel_encoder(&i915->drm, encoder) {
775379bc100SJani Nikula if (encoder->type == INTEL_OUTPUT_LVDS)
776379bc100SJani Nikula return encoder;
777379bc100SJani Nikula }
778379bc100SJani Nikula
779379bc100SJani Nikula return NULL;
780379bc100SJani Nikula }
781379bc100SJani Nikula
intel_is_dual_link_lvds(struct drm_i915_private * i915)7827f66476cSVille Syrjälä bool intel_is_dual_link_lvds(struct drm_i915_private *i915)
783379bc100SJani Nikula {
7847f66476cSVille Syrjälä struct intel_encoder *encoder = intel_get_lvds_encoder(i915);
785379bc100SJani Nikula
7865e800d92SVille Syrjälä return encoder && to_lvds_encoder(encoder)->is_dual_link;
787379bc100SJani Nikula }
788379bc100SJani Nikula
compute_is_dual_link_lvds(struct intel_lvds_encoder * lvds_encoder)789f6d39f56SVille Syrjälä static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
790379bc100SJani Nikula {
7917f66476cSVille Syrjälä struct drm_i915_private *i915 = to_i915(lvds_encoder->base.base.dev);
792f6d39f56SVille Syrjälä struct intel_connector *connector = lvds_encoder->attached_connector;
793f6d39f56SVille Syrjälä const struct drm_display_mode *fixed_mode =
794f6d39f56SVille Syrjälä intel_panel_preferred_fixed_mode(connector);
795379bc100SJani Nikula unsigned int val;
796379bc100SJani Nikula
797379bc100SJani Nikula /* use the module option value if specified */
7987f66476cSVille Syrjälä if (i915->params.lvds_channel_mode > 0)
7997f66476cSVille Syrjälä return i915->params.lvds_channel_mode == 2;
800379bc100SJani Nikula
801379bc100SJani Nikula /* single channel LVDS is limited to 112 MHz */
802457e992bSVille Syrjälä if (fixed_mode->clock > 112999)
803379bc100SJani Nikula return true;
804379bc100SJani Nikula
805379bc100SJani Nikula if (dmi_check_system(intel_dual_link_lvds))
806379bc100SJani Nikula return true;
807379bc100SJani Nikula
808379bc100SJani Nikula /*
809379bc100SJani Nikula * BIOS should set the proper LVDS register value at boot, but
810379bc100SJani Nikula * in reality, it doesn't set the value when the lid is closed;
811379bc100SJani Nikula * we need to check "the value to be set" in VBT when LVDS
812379bc100SJani Nikula * register is uninitialized.
813379bc100SJani Nikula */
8147f66476cSVille Syrjälä val = intel_de_read(i915, lvds_encoder->reg);
8157f66476cSVille Syrjälä if (HAS_PCH_CPT(i915))
816379bc100SJani Nikula val &= ~(LVDS_DETECTED | LVDS_PIPE_SEL_MASK_CPT);
817379bc100SJani Nikula else
818379bc100SJani Nikula val &= ~(LVDS_DETECTED | LVDS_PIPE_SEL_MASK);
819379bc100SJani Nikula if (val == 0)
8203cf05076SVille Syrjälä val = connector->panel.vbt.bios_lvds_val;
821379bc100SJani Nikula
822379bc100SJani Nikula return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
823379bc100SJani Nikula }
824379bc100SJani Nikula
intel_lvds_add_properties(struct drm_connector * connector)8252f0f603aSVille Syrjälä static void intel_lvds_add_properties(struct drm_connector *connector)
8262f0f603aSVille Syrjälä {
8276ac2f04bSVille Syrjälä intel_attach_scaling_mode_property(connector);
8282f0f603aSVille Syrjälä }
8292f0f603aSVille Syrjälä
830379bc100SJani Nikula /**
831379bc100SJani Nikula * intel_lvds_init - setup LVDS connectors on this device
8327f66476cSVille Syrjälä * @i915: i915 device
833379bc100SJani Nikula *
834379bc100SJani Nikula * Create the connector, register the LVDS DDC bus, and try to figure out what
835379bc100SJani Nikula * modes we can display on the LVDS panel (if present).
836379bc100SJani Nikula */
intel_lvds_init(struct drm_i915_private * i915)8377f66476cSVille Syrjälä void intel_lvds_init(struct drm_i915_private *i915)
838379bc100SJani Nikula {
839379bc100SJani Nikula struct intel_lvds_encoder *lvds_encoder;
84019d7dc66SVille Syrjälä struct intel_connector *connector;
84125fa6b0fSJani Nikula const struct drm_edid *drm_edid;
84219d7dc66SVille Syrjälä struct intel_encoder *encoder;
843379bc100SJani Nikula i915_reg_t lvds_reg;
844379bc100SJani Nikula u32 lvds;
845379bc100SJani Nikula u8 pin;
846379bc100SJani Nikula
847379bc100SJani Nikula /* Skip init on machines we know falsely report LVDS */
848379bc100SJani Nikula if (dmi_check_system(intel_no_lvds)) {
8497f66476cSVille Syrjälä drm_WARN(&i915->drm, !i915->display.vbt.int_lvds_support,
850379bc100SJani Nikula "Useless DMI match. Internal LVDS support disabled by VBT\n");
851379bc100SJani Nikula return;
852379bc100SJani Nikula }
853379bc100SJani Nikula
8547f66476cSVille Syrjälä if (!i915->display.vbt.int_lvds_support) {
8557f66476cSVille Syrjälä drm_dbg_kms(&i915->drm,
856900b8c9eSWambui Karuga "Internal LVDS support disabled by VBT\n");
857379bc100SJani Nikula return;
858379bc100SJani Nikula }
859379bc100SJani Nikula
8607f66476cSVille Syrjälä if (HAS_PCH_SPLIT(i915))
861379bc100SJani Nikula lvds_reg = PCH_LVDS;
862379bc100SJani Nikula else
863379bc100SJani Nikula lvds_reg = LVDS;
864379bc100SJani Nikula
8657f66476cSVille Syrjälä lvds = intel_de_read(i915, lvds_reg);
866379bc100SJani Nikula
8677f66476cSVille Syrjälä if (HAS_PCH_SPLIT(i915)) {
868379bc100SJani Nikula if ((lvds & LVDS_DETECTED) == 0)
869379bc100SJani Nikula return;
870379bc100SJani Nikula }
871379bc100SJani Nikula
872379bc100SJani Nikula pin = GMBUS_PIN_PANEL;
8737f66476cSVille Syrjälä if (!intel_bios_is_lvds_present(i915, &pin)) {
874379bc100SJani Nikula if ((lvds & LVDS_PORT_EN) == 0) {
8757f66476cSVille Syrjälä drm_dbg_kms(&i915->drm,
876900b8c9eSWambui Karuga "LVDS is not present in VBT\n");
877379bc100SJani Nikula return;
878379bc100SJani Nikula }
8797f66476cSVille Syrjälä drm_dbg_kms(&i915->drm,
880900b8c9eSWambui Karuga "LVDS is not present in VBT, but enabled anyway\n");
881379bc100SJani Nikula }
882379bc100SJani Nikula
883379bc100SJani Nikula lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL);
884379bc100SJani Nikula if (!lvds_encoder)
885379bc100SJani Nikula return;
886379bc100SJani Nikula
88719d7dc66SVille Syrjälä connector = intel_connector_alloc();
88819d7dc66SVille Syrjälä if (!connector) {
889379bc100SJani Nikula kfree(lvds_encoder);
890379bc100SJani Nikula return;
891379bc100SJani Nikula }
892379bc100SJani Nikula
89319d7dc66SVille Syrjälä lvds_encoder->attached_connector = connector;
89419d7dc66SVille Syrjälä encoder = &lvds_encoder->base;
895379bc100SJani Nikula
89619d7dc66SVille Syrjälä drm_connector_init(&i915->drm, &connector->base, &intel_lvds_connector_funcs,
897379bc100SJani Nikula DRM_MODE_CONNECTOR_LVDS);
898379bc100SJani Nikula
89919d7dc66SVille Syrjälä drm_encoder_init(&i915->drm, &encoder->base, &intel_lvds_enc_funcs,
900379bc100SJani Nikula DRM_MODE_ENCODER_LVDS, "LVDS");
901379bc100SJani Nikula
90219d7dc66SVille Syrjälä encoder->enable = intel_enable_lvds;
90319d7dc66SVille Syrjälä encoder->pre_enable = intel_pre_enable_lvds;
90419d7dc66SVille Syrjälä encoder->compute_config = intel_lvds_compute_config;
9057f66476cSVille Syrjälä if (HAS_PCH_SPLIT(i915)) {
90619d7dc66SVille Syrjälä encoder->disable = pch_disable_lvds;
90719d7dc66SVille Syrjälä encoder->post_disable = pch_post_disable_lvds;
908379bc100SJani Nikula } else {
90919d7dc66SVille Syrjälä encoder->disable = gmch_disable_lvds;
910379bc100SJani Nikula }
91119d7dc66SVille Syrjälä encoder->get_hw_state = intel_lvds_get_hw_state;
91219d7dc66SVille Syrjälä encoder->get_config = intel_lvds_get_config;
91319d7dc66SVille Syrjälä encoder->update_pipe = intel_backlight_update;
91419d7dc66SVille Syrjälä encoder->shutdown = intel_lvds_shutdown;
91519d7dc66SVille Syrjälä connector->get_hw_state = intel_connector_get_hw_state;
916379bc100SJani Nikula
91719d7dc66SVille Syrjälä intel_connector_attach_encoder(connector, encoder);
918379bc100SJani Nikula
91919d7dc66SVille Syrjälä encoder->type = INTEL_OUTPUT_LVDS;
92019d7dc66SVille Syrjälä encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
92119d7dc66SVille Syrjälä encoder->port = PORT_NONE;
92219d7dc66SVille Syrjälä encoder->cloneable = 0;
9237f66476cSVille Syrjälä if (DISPLAY_VER(i915) < 4)
92419d7dc66SVille Syrjälä encoder->pipe_mask = BIT(PIPE_B);
9252b0b2741SVille Syrjälä else
92619d7dc66SVille Syrjälä encoder->pipe_mask = ~0;
927379bc100SJani Nikula
92819d7dc66SVille Syrjälä drm_connector_helper_add(&connector->base, &intel_lvds_connector_helper_funcs);
92919d7dc66SVille Syrjälä connector->base.display_info.subpixel_order = SubPixelHorizontalRGB;
930379bc100SJani Nikula
931379bc100SJani Nikula lvds_encoder->reg = lvds_reg;
932379bc100SJani Nikula
93319d7dc66SVille Syrjälä intel_lvds_add_properties(&connector->base);
934379bc100SJani Nikula
9357f66476cSVille Syrjälä intel_lvds_pps_get_hw_state(i915, &lvds_encoder->init_pps);
936379bc100SJani Nikula lvds_encoder->init_lvds_val = lvds;
937379bc100SJani Nikula
938379bc100SJani Nikula /*
939379bc100SJani Nikula * LVDS discovery:
940379bc100SJani Nikula * 1) check for EDID on DDC
941379bc100SJani Nikula * 2) check for VBT data
942379bc100SJani Nikula * 3) check to see if LVDS is already on
943379bc100SJani Nikula * if none of the above, no panel
944379bc100SJani Nikula */
945379bc100SJani Nikula
946379bc100SJani Nikula /*
947379bc100SJani Nikula * Attempt to get the fixed panel mode from DDC. Assume that the
948379bc100SJani Nikula * preferred mode is the right one.
949379bc100SJani Nikula */
9507f66476cSVille Syrjälä mutex_lock(&i915->drm.mode_config.mutex);
95125fa6b0fSJani Nikula if (vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC) {
952a036aa1fSJani Nikula drm_edid = drm_edid_read_switcheroo(&connector->base,
9537f66476cSVille Syrjälä intel_gmbus_get_adapter(i915, pin));
954379bc100SJani Nikula } else {
95519d7dc66SVille Syrjälä drm_edid = drm_edid_read_ddc(&connector->base,
9567f66476cSVille Syrjälä intel_gmbus_get_adapter(i915, pin));
957379bc100SJani Nikula }
95825fa6b0fSJani Nikula if (drm_edid) {
95919d7dc66SVille Syrjälä if (drm_edid_connector_update(&connector->base, drm_edid) ||
96019d7dc66SVille Syrjälä !drm_edid_connector_add_modes(&connector->base)) {
96119d7dc66SVille Syrjälä drm_edid_connector_update(&connector->base, NULL);
96225fa6b0fSJani Nikula drm_edid_free(drm_edid);
96325fa6b0fSJani Nikula drm_edid = ERR_PTR(-EINVAL);
96425fa6b0fSJani Nikula }
96525fa6b0fSJani Nikula } else {
96625fa6b0fSJani Nikula drm_edid = ERR_PTR(-ENOENT);
96725fa6b0fSJani Nikula }
96819d7dc66SVille Syrjälä intel_bios_init_panel_late(i915, &connector->panel, NULL,
969c36225a1SJani Nikula IS_ERR(drm_edid) ? NULL : drm_edid);
9703cf05076SVille Syrjälä
971db10c14aSVille Syrjälä /* Try EDID first */
97219d7dc66SVille Syrjälä intel_panel_add_edid_fixed_modes(connector, true);
973379bc100SJani Nikula
974379bc100SJani Nikula /* Failed to get EDID, what about VBT? */
97519d7dc66SVille Syrjälä if (!intel_panel_preferred_fixed_mode(connector))
97619d7dc66SVille Syrjälä intel_panel_add_vbt_lfp_fixed_mode(connector);
977379bc100SJani Nikula
978379bc100SJani Nikula /*
979db10c14aSVille Syrjälä * If we didn't get a fixed mode from EDID or VBT, try checking
980db10c14aSVille Syrjälä * if the panel is already turned on. If so, assume that
981db10c14aSVille Syrjälä * whatever is currently programmed is the correct mode.
982379bc100SJani Nikula */
98319d7dc66SVille Syrjälä if (!intel_panel_preferred_fixed_mode(connector))
98419d7dc66SVille Syrjälä intel_panel_add_encoder_fixed_mode(connector, encoder);
985379bc100SJani Nikula
9867f66476cSVille Syrjälä mutex_unlock(&i915->drm.mode_config.mutex);
987379bc100SJani Nikula
988db10c14aSVille Syrjälä /* If we still don't have a mode after all that, give up. */
98919d7dc66SVille Syrjälä if (!intel_panel_preferred_fixed_mode(connector))
990db10c14aSVille Syrjälä goto failed;
991db10c14aSVille Syrjälä
99219d7dc66SVille Syrjälä intel_panel_init(connector, drm_edid);
993db10c14aSVille Syrjälä
99419d7dc66SVille Syrjälä intel_backlight_setup(connector, INVALID_PIPE);
995379bc100SJani Nikula
996f6d39f56SVille Syrjälä lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder);
9977f66476cSVille Syrjälä drm_dbg_kms(&i915->drm, "detected %s-link lvds configuration\n",
998379bc100SJani Nikula lvds_encoder->is_dual_link ? "dual" : "single");
999379bc100SJani Nikula
1000379bc100SJani Nikula lvds_encoder->a3_power = lvds & LVDS_A3_POWER_MASK;
1001379bc100SJani Nikula
1002379bc100SJani Nikula return;
1003379bc100SJani Nikula
1004379bc100SJani Nikula failed:
10057f66476cSVille Syrjälä drm_dbg_kms(&i915->drm, "No LVDS modes found, disabling.\n");
100619d7dc66SVille Syrjälä drm_connector_cleanup(&connector->base);
100719d7dc66SVille Syrjälä drm_encoder_cleanup(&encoder->base);
1008379bc100SJani Nikula kfree(lvds_encoder);
100919d7dc66SVille Syrjälä intel_connector_free(connector);
1010379bc100SJani Nikula return;
1011379bc100SJani Nikula }
1012