Branch data Line data Source code
1 : : /*
2 : : * Copyright © 2006-2007 Intel Corporation
3 : : * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
4 : : *
5 : : * Permission is hereby granted, free of charge, to any person obtaining a
6 : : * copy of this software and associated documentation files (the "Software"),
7 : : * to deal in the Software without restriction, including without limitation
8 : : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 : : * and/or sell copies of the Software, and to permit persons to whom the
10 : : * Software is furnished to do so, subject to the following conditions:
11 : : *
12 : : * The above copyright notice and this permission notice (including the next
13 : : * paragraph) shall be included in all copies or substantial portions of the
14 : : * Software.
15 : : *
16 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 : : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 : : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 : : * DEALINGS IN THE SOFTWARE.
23 : : *
24 : : * Authors:
25 : : * Eric Anholt <eric@anholt.net>
26 : : * Dave Airlie <airlied@linux.ie>
27 : : * Jesse Barnes <jesse.barnes@intel.com>
28 : : */
29 : :
30 : : #include <acpi/button.h>
31 : : #include <linux/acpi.h>
32 : : #include <linux/dmi.h>
33 : : #include <linux/i2c.h>
34 : : #include <linux/slab.h>
35 : : #include <linux/vga_switcheroo.h>
36 : :
37 : : #include <drm/drm_atomic_helper.h>
38 : : #include <drm/drm_crtc.h>
39 : : #include <drm/drm_edid.h>
40 : : #include <drm/i915_drm.h>
41 : :
42 : : #include "i915_drv.h"
43 : : #include "intel_atomic.h"
44 : : #include "intel_connector.h"
45 : : #include "intel_display_types.h"
46 : : #include "intel_gmbus.h"
47 : : #include "intel_lvds.h"
48 : : #include "intel_panel.h"
49 : :
50 : : /* Private structure for the integrated LVDS support */
51 : : struct intel_lvds_pps {
52 : : /* 100us units */
53 : : int t1_t2;
54 : : int t3;
55 : : int t4;
56 : : int t5;
57 : : int tx;
58 : :
59 : : int divider;
60 : :
61 : : int port;
62 : : bool powerdown_on_reset;
63 : : };
64 : :
65 : : struct intel_lvds_encoder {
66 : : struct intel_encoder base;
67 : :
68 : : bool is_dual_link;
69 : : i915_reg_t reg;
70 : : u32 a3_power;
71 : :
72 : : struct intel_lvds_pps init_pps;
73 : : u32 init_lvds_val;
74 : :
75 : : struct intel_connector *attached_connector;
76 : : };
77 : :
78 : 0 : static struct intel_lvds_encoder *to_lvds_encoder(struct drm_encoder *encoder)
79 : : {
80 : 0 : return container_of(encoder, struct intel_lvds_encoder, base.base);
81 : : }
82 : :
83 : 0 : bool intel_lvds_port_enabled(struct drm_i915_private *dev_priv,
84 : : i915_reg_t lvds_reg, enum pipe *pipe)
85 : : {
86 : 0 : u32 val;
87 : :
88 : 0 : val = I915_READ(lvds_reg);
89 : :
90 : : /* asserts want to know the pipe even if the port is disabled */
91 [ # # # # ]: 0 : if (HAS_PCH_CPT(dev_priv))
92 : 0 : *pipe = (val & LVDS_PIPE_SEL_MASK_CPT) >> LVDS_PIPE_SEL_SHIFT_CPT;
93 : : else
94 : 0 : *pipe = (val & LVDS_PIPE_SEL_MASK) >> LVDS_PIPE_SEL_SHIFT;
95 : :
96 : 0 : return val & LVDS_PORT_EN;
97 : : }
98 : :
99 : 0 : static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
100 : : enum pipe *pipe)
101 : : {
102 : 0 : struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
103 : 0 : struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
104 : 0 : intel_wakeref_t wakeref;
105 : 0 : bool ret;
106 : :
107 : 0 : wakeref = intel_display_power_get_if_enabled(dev_priv,
108 : : encoder->power_domain);
109 [ # # ]: 0 : if (!wakeref)
110 : : return false;
111 : :
112 : 0 : ret = intel_lvds_port_enabled(dev_priv, lvds_encoder->reg, pipe);
113 : :
114 : 0 : intel_display_power_put(dev_priv, encoder->power_domain, wakeref);
115 : :
116 : 0 : return ret;
117 : : }
118 : :
119 : 0 : static void intel_lvds_get_config(struct intel_encoder *encoder,
120 : : struct intel_crtc_state *pipe_config)
121 : : {
122 : 0 : struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
123 : 0 : struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
124 : 0 : u32 tmp, flags = 0;
125 : :
126 : 0 : pipe_config->output_types |= BIT(INTEL_OUTPUT_LVDS);
127 : :
128 : 0 : tmp = I915_READ(lvds_encoder->reg);
129 [ # # ]: 0 : if (tmp & LVDS_HSYNC_POLARITY)
130 : : flags |= DRM_MODE_FLAG_NHSYNC;
131 : : else
132 : 0 : flags |= DRM_MODE_FLAG_PHSYNC;
133 [ # # ]: 0 : if (tmp & LVDS_VSYNC_POLARITY)
134 : 0 : flags |= DRM_MODE_FLAG_NVSYNC;
135 : : else
136 : 0 : flags |= DRM_MODE_FLAG_PVSYNC;
137 : :
138 : 0 : pipe_config->hw.adjusted_mode.flags |= flags;
139 : :
140 [ # # ]: 0 : if (INTEL_GEN(dev_priv) < 5)
141 : 0 : pipe_config->gmch_pfit.lvds_border_bits =
142 : 0 : tmp & LVDS_BORDER_ENABLE;
143 : :
144 : : /* gen2/3 store dither state in pfit control, needs to match */
145 [ # # ]: 0 : if (INTEL_GEN(dev_priv) < 4) {
146 : 0 : tmp = I915_READ(PFIT_CONTROL);
147 : :
148 : 0 : pipe_config->gmch_pfit.control |= tmp & PANEL_8TO6_DITHER_ENABLE;
149 : : }
150 : :
151 : 0 : pipe_config->hw.adjusted_mode.crtc_clock = pipe_config->port_clock;
152 : 0 : }
153 : :
154 : 0 : static void intel_lvds_pps_get_hw_state(struct drm_i915_private *dev_priv,
155 : : struct intel_lvds_pps *pps)
156 : : {
157 : 0 : u32 val;
158 : :
159 : 0 : pps->powerdown_on_reset = I915_READ(PP_CONTROL(0)) & PANEL_POWER_RESET;
160 : :
161 : 0 : val = I915_READ(PP_ON_DELAYS(0));
162 : 0 : pps->port = REG_FIELD_GET(PANEL_PORT_SELECT_MASK, val);
163 : 0 : pps->t1_t2 = REG_FIELD_GET(PANEL_POWER_UP_DELAY_MASK, val);
164 : 0 : pps->t5 = REG_FIELD_GET(PANEL_LIGHT_ON_DELAY_MASK, val);
165 : :
166 : 0 : val = I915_READ(PP_OFF_DELAYS(0));
167 : 0 : pps->t3 = REG_FIELD_GET(PANEL_POWER_DOWN_DELAY_MASK, val);
168 : 0 : pps->tx = REG_FIELD_GET(PANEL_LIGHT_OFF_DELAY_MASK, val);
169 : :
170 : 0 : val = I915_READ(PP_DIVISOR(0));
171 : 0 : pps->divider = REG_FIELD_GET(PP_REFERENCE_DIVIDER_MASK, val);
172 : 0 : val = REG_FIELD_GET(PANEL_POWER_CYCLE_DELAY_MASK, val);
173 : : /*
174 : : * Remove the BSpec specified +1 (100ms) offset that accounts for a
175 : : * too short power-cycle delay due to the asynchronous programming of
176 : : * the register.
177 : : */
178 [ # # ]: 0 : if (val)
179 : 0 : val--;
180 : : /* Convert from 100ms to 100us units */
181 : 0 : pps->t4 = val * 1000;
182 : :
183 [ # # ]: 0 : if (INTEL_GEN(dev_priv) <= 4 &&
184 [ # # # # : 0 : pps->t1_t2 == 0 && pps->t5 == 0 && pps->t3 == 0 && pps->tx == 0) {
# # # # ]
185 : 0 : DRM_DEBUG_KMS("Panel power timings uninitialized, "
186 : : "setting defaults\n");
187 : : /* Set T2 to 40ms and T5 to 200ms in 100 usec units */
188 : 0 : pps->t1_t2 = 40 * 10;
189 : 0 : pps->t5 = 200 * 10;
190 : : /* Set T3 to 35ms and Tx to 200ms in 100 usec units */
191 : 0 : pps->t3 = 35 * 10;
192 : 0 : pps->tx = 200 * 10;
193 : : }
194 : :
195 : 0 : DRM_DEBUG_DRIVER("LVDS PPS:t1+t2 %d t3 %d t4 %d t5 %d tx %d "
196 : : "divider %d port %d powerdown_on_reset %d\n",
197 : : pps->t1_t2, pps->t3, pps->t4, pps->t5, pps->tx,
198 : : pps->divider, pps->port, pps->powerdown_on_reset);
199 : 0 : }
200 : :
201 : 0 : static void intel_lvds_pps_init_hw(struct drm_i915_private *dev_priv,
202 : : struct intel_lvds_pps *pps)
203 : : {
204 : 0 : u32 val;
205 : :
206 : 0 : val = I915_READ(PP_CONTROL(0));
207 [ # # ]: 0 : WARN_ON((val & PANEL_UNLOCK_MASK) != PANEL_UNLOCK_REGS);
208 [ # # ]: 0 : if (pps->powerdown_on_reset)
209 : 0 : val |= PANEL_POWER_RESET;
210 : 0 : I915_WRITE(PP_CONTROL(0), val);
211 : :
212 : 0 : I915_WRITE(PP_ON_DELAYS(0),
213 : : REG_FIELD_PREP(PANEL_PORT_SELECT_MASK, pps->port) |
214 : : REG_FIELD_PREP(PANEL_POWER_UP_DELAY_MASK, pps->t1_t2) |
215 : : REG_FIELD_PREP(PANEL_LIGHT_ON_DELAY_MASK, pps->t5));
216 : :
217 : 0 : I915_WRITE(PP_OFF_DELAYS(0),
218 : : REG_FIELD_PREP(PANEL_POWER_DOWN_DELAY_MASK, pps->t3) |
219 : : REG_FIELD_PREP(PANEL_LIGHT_OFF_DELAY_MASK, pps->tx));
220 : :
221 : 0 : I915_WRITE(PP_DIVISOR(0),
222 : : REG_FIELD_PREP(PP_REFERENCE_DIVIDER_MASK, pps->divider) |
223 : : REG_FIELD_PREP(PANEL_POWER_CYCLE_DELAY_MASK,
224 : : DIV_ROUND_UP(pps->t4, 1000) + 1));
225 : 0 : }
226 : :
227 : 0 : static void intel_pre_enable_lvds(struct intel_encoder *encoder,
228 : : const struct intel_crtc_state *pipe_config,
229 : : const struct drm_connector_state *conn_state)
230 : : {
231 : 0 : struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
232 [ # # ]: 0 : struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
233 : 0 : struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
234 : 0 : const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
235 : 0 : enum pipe pipe = crtc->pipe;
236 : 0 : u32 temp;
237 : :
238 [ # # ]: 0 : if (HAS_PCH_SPLIT(dev_priv)) {
239 : 0 : assert_fdi_rx_pll_disabled(dev_priv, pipe);
240 : 0 : assert_shared_dpll_disabled(dev_priv,
241 : : pipe_config->shared_dpll);
242 : : } else {
243 : 0 : assert_pll_disabled(dev_priv, pipe);
244 : : }
245 : :
246 : 0 : intel_lvds_pps_init_hw(dev_priv, &lvds_encoder->init_pps);
247 : :
248 : 0 : temp = lvds_encoder->init_lvds_val;
249 : 0 : temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
250 : :
251 [ # # ]: 0 : if (HAS_PCH_CPT(dev_priv)) {
252 : 0 : temp &= ~LVDS_PIPE_SEL_MASK_CPT;
253 : 0 : temp |= LVDS_PIPE_SEL_CPT(pipe);
254 : : } else {
255 : 0 : temp &= ~LVDS_PIPE_SEL_MASK;
256 : 0 : temp |= LVDS_PIPE_SEL(pipe);
257 : : }
258 : :
259 : : /* set the corresponsding LVDS_BORDER bit */
260 : 0 : temp &= ~LVDS_BORDER_ENABLE;
261 : 0 : temp |= pipe_config->gmch_pfit.lvds_border_bits;
262 : :
263 : : /*
264 : : * Set the B0-B3 data pairs corresponding to whether we're going to
265 : : * set the DPLLs for dual-channel mode or not.
266 : : */
267 [ # # ]: 0 : if (lvds_encoder->is_dual_link)
268 : 0 : temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
269 : : else
270 : 0 : temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
271 : :
272 : : /*
273 : : * It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
274 : : * appropriately here, but we need to look more thoroughly into how
275 : : * panels behave in the two modes. For now, let's just maintain the
276 : : * value we got from the BIOS.
277 : : */
278 : 0 : temp &= ~LVDS_A3_POWER_MASK;
279 : 0 : temp |= lvds_encoder->a3_power;
280 : :
281 : : /*
282 : : * Set the dithering flag on LVDS as needed, note that there is no
283 : : * special lvds dither control bit on pch-split platforms, dithering is
284 : : * only controlled through the PIPECONF reg.
285 : : */
286 [ # # ]: 0 : if (IS_GEN(dev_priv, 4)) {
287 : : /*
288 : : * Bspec wording suggests that LVDS port dithering only exists
289 : : * for 18bpp panels.
290 : : */
291 [ # # # # ]: 0 : if (pipe_config->dither && pipe_config->pipe_bpp == 18)
292 : 0 : temp |= LVDS_ENABLE_DITHER;
293 : : else
294 : 0 : temp &= ~LVDS_ENABLE_DITHER;
295 : : }
296 : 0 : temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY);
297 [ # # ]: 0 : if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
298 : 0 : temp |= LVDS_HSYNC_POLARITY;
299 [ # # ]: 0 : if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
300 : 0 : temp |= LVDS_VSYNC_POLARITY;
301 : :
302 : 0 : I915_WRITE(lvds_encoder->reg, temp);
303 : 0 : }
304 : :
305 : : /*
306 : : * Sets the power state for the panel.
307 : : */
308 : 0 : static void intel_enable_lvds(struct intel_encoder *encoder,
309 : : const struct intel_crtc_state *pipe_config,
310 : : const struct drm_connector_state *conn_state)
311 : : {
312 : 0 : struct drm_device *dev = encoder->base.dev;
313 : 0 : struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
314 : 0 : struct drm_i915_private *dev_priv = to_i915(dev);
315 : :
316 : 0 : I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN);
317 : :
318 : 0 : I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) | PANEL_POWER_ON);
319 : 0 : POSTING_READ(lvds_encoder->reg);
320 : :
321 [ # # ]: 0 : if (intel_de_wait_for_set(dev_priv, PP_STATUS(0), PP_ON, 5000))
322 : 0 : DRM_ERROR("timed out waiting for panel to power on\n");
323 : :
324 : 0 : intel_panel_enable_backlight(pipe_config, conn_state);
325 : 0 : }
326 : :
327 : : static void intel_disable_lvds(struct intel_encoder *encoder,
328 : : const struct intel_crtc_state *old_crtc_state,
329 : : const struct drm_connector_state *old_conn_state)
330 : : {
331 : : struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base);
332 : : struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
333 : :
334 : : I915_WRITE(PP_CONTROL(0), I915_READ(PP_CONTROL(0)) & ~PANEL_POWER_ON);
335 : : if (intel_de_wait_for_clear(dev_priv, PP_STATUS(0), PP_ON, 1000))
336 : : DRM_ERROR("timed out waiting for panel to power off\n");
337 : :
338 : : I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN);
339 : : POSTING_READ(lvds_encoder->reg);
340 : : }
341 : :
342 : 0 : static void gmch_disable_lvds(struct intel_encoder *encoder,
343 : : const struct intel_crtc_state *old_crtc_state,
344 : : const struct drm_connector_state *old_conn_state)
345 : :
346 : : {
347 : 0 : intel_panel_disable_backlight(old_conn_state);
348 : :
349 : 0 : intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
350 : 0 : }
351 : :
352 : 0 : static void pch_disable_lvds(struct intel_encoder *encoder,
353 : : const struct intel_crtc_state *old_crtc_state,
354 : : const struct drm_connector_state *old_conn_state)
355 : : {
356 : 0 : intel_panel_disable_backlight(old_conn_state);
357 : 0 : }
358 : :
359 : 0 : static void pch_post_disable_lvds(struct intel_encoder *encoder,
360 : : const struct intel_crtc_state *old_crtc_state,
361 : : const struct drm_connector_state *old_conn_state)
362 : : {
363 : 0 : intel_disable_lvds(encoder, old_crtc_state, old_conn_state);
364 : 0 : }
365 : :
366 : : static enum drm_mode_status
367 : 0 : intel_lvds_mode_valid(struct drm_connector *connector,
368 : : struct drm_display_mode *mode)
369 : : {
370 : 0 : struct intel_connector *intel_connector = to_intel_connector(connector);
371 : 0 : struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode;
372 [ # # ]: 0 : int max_pixclk = to_i915(connector->dev)->max_dotclk_freq;
373 : :
374 [ # # ]: 0 : if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
375 : : return MODE_NO_DBLESCAN;
376 [ # # ]: 0 : if (mode->hdisplay > fixed_mode->hdisplay)
377 : : return MODE_PANEL;
378 [ # # ]: 0 : if (mode->vdisplay > fixed_mode->vdisplay)
379 : : return MODE_PANEL;
380 [ # # ]: 0 : if (fixed_mode->clock > max_pixclk)
381 : 0 : return MODE_CLOCK_HIGH;
382 : :
383 : : return MODE_OK;
384 : : }
385 : :
386 : 0 : static int intel_lvds_compute_config(struct intel_encoder *intel_encoder,
387 : : struct intel_crtc_state *pipe_config,
388 : : struct drm_connector_state *conn_state)
389 : : {
390 [ # # ]: 0 : struct drm_i915_private *dev_priv = to_i915(intel_encoder->base.dev);
391 : 0 : struct intel_lvds_encoder *lvds_encoder =
392 : : to_lvds_encoder(&intel_encoder->base);
393 : 0 : struct intel_connector *intel_connector =
394 : : lvds_encoder->attached_connector;
395 : 0 : struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode;
396 : 0 : struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->uapi.crtc);
397 : 0 : unsigned int lvds_bpp;
398 : :
399 : : /* Should never happen!! */
400 [ # # # # ]: 0 : if (INTEL_GEN(dev_priv) < 4 && intel_crtc->pipe == 0) {
401 : 0 : DRM_ERROR("Can't support LVDS on pipe A\n");
402 : 0 : return -EINVAL;
403 : : }
404 : :
405 [ # # ]: 0 : if (lvds_encoder->a3_power == LVDS_A3_POWER_UP)
406 : : lvds_bpp = 8*3;
407 : : else
408 : 0 : lvds_bpp = 6*3;
409 : :
410 [ # # # # ]: 0 : if (lvds_bpp != pipe_config->pipe_bpp && !pipe_config->bw_constrained) {
411 : 0 : DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n",
412 : : pipe_config->pipe_bpp, lvds_bpp);
413 : 0 : pipe_config->pipe_bpp = lvds_bpp;
414 : : }
415 : :
416 : 0 : pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
417 : :
418 : : /*
419 : : * We have timings from the BIOS for the panel, put them in
420 : : * to the adjusted mode. The CRTC will be set up for this mode,
421 : : * with the panel scaling set up to source from the H/VDisplay
422 : : * of the original mode.
423 : : */
424 : 0 : intel_fixed_panel_mode(intel_connector->panel.fixed_mode,
425 : : adjusted_mode);
426 : :
427 [ # # ]: 0 : if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
428 : : return -EINVAL;
429 : :
430 [ # # ]: 0 : if (HAS_PCH_SPLIT(dev_priv)) {
431 : 0 : pipe_config->has_pch_encoder = true;
432 : :
433 : 0 : intel_pch_panel_fitting(intel_crtc, pipe_config,
434 : 0 : conn_state->scaling_mode);
435 : : } else {
436 : 0 : intel_gmch_panel_fitting(intel_crtc, pipe_config,
437 : 0 : conn_state->scaling_mode);
438 : :
439 : : }
440 : :
441 : : /*
442 : : * XXX: It would be nice to support lower refresh rates on the
443 : : * panels to reduce power consumption, and perhaps match the
444 : : * user's requested refresh rate.
445 : : */
446 : :
447 : : return 0;
448 : : }
449 : :
450 : : static enum drm_connector_status
451 : 0 : intel_lvds_detect(struct drm_connector *connector, bool force)
452 : : {
453 : 0 : return connector_status_connected;
454 : : }
455 : :
456 : : /*
457 : : * Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
458 : : */
459 : 0 : static int intel_lvds_get_modes(struct drm_connector *connector)
460 : : {
461 : 0 : struct intel_connector *intel_connector = to_intel_connector(connector);
462 : 0 : struct drm_device *dev = connector->dev;
463 : 0 : struct drm_display_mode *mode;
464 : :
465 : : /* use cached edid if we have one */
466 [ # # # # ]: 0 : if (!IS_ERR_OR_NULL(intel_connector->edid))
467 : 0 : return drm_add_edid_modes(connector, intel_connector->edid);
468 : :
469 : 0 : mode = drm_mode_duplicate(dev, intel_connector->panel.fixed_mode);
470 [ # # ]: 0 : if (mode == NULL)
471 : : return 0;
472 : :
473 : 0 : drm_mode_probed_add(connector, mode);
474 : 0 : return 1;
475 : : }
476 : :
477 : : static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
478 : : .get_modes = intel_lvds_get_modes,
479 : : .mode_valid = intel_lvds_mode_valid,
480 : : .atomic_check = intel_digital_connector_atomic_check,
481 : : };
482 : :
483 : : static const struct drm_connector_funcs intel_lvds_connector_funcs = {
484 : : .detect = intel_lvds_detect,
485 : : .fill_modes = drm_helper_probe_single_connector_modes,
486 : : .atomic_get_property = intel_digital_connector_atomic_get_property,
487 : : .atomic_set_property = intel_digital_connector_atomic_set_property,
488 : : .late_register = intel_connector_register,
489 : : .early_unregister = intel_connector_unregister,
490 : : .destroy = intel_connector_destroy,
491 : : .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
492 : : .atomic_duplicate_state = intel_digital_connector_duplicate_state,
493 : : };
494 : :
495 : : static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
496 : : .destroy = intel_encoder_destroy,
497 : : };
498 : :
499 : 0 : static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
500 : : {
501 : 0 : DRM_INFO("Skipping LVDS initialization for %s\n", id->ident);
502 : 0 : return 1;
503 : : }
504 : :
505 : : /* These systems claim to have LVDS, but really don't */
506 : : static const struct dmi_system_id intel_no_lvds[] = {
507 : : {
508 : : .callback = intel_no_lvds_dmi_callback,
509 : : .ident = "Apple Mac Mini (Core series)",
510 : : .matches = {
511 : : DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
512 : : DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"),
513 : : },
514 : : },
515 : : {
516 : : .callback = intel_no_lvds_dmi_callback,
517 : : .ident = "Apple Mac Mini (Core 2 series)",
518 : : .matches = {
519 : : DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
520 : : DMI_MATCH(DMI_PRODUCT_NAME, "Macmini2,1"),
521 : : },
522 : : },
523 : : {
524 : : .callback = intel_no_lvds_dmi_callback,
525 : : .ident = "MSI IM-945GSE-A",
526 : : .matches = {
527 : : DMI_MATCH(DMI_SYS_VENDOR, "MSI"),
528 : : DMI_MATCH(DMI_PRODUCT_NAME, "A9830IMS"),
529 : : },
530 : : },
531 : : {
532 : : .callback = intel_no_lvds_dmi_callback,
533 : : .ident = "Dell Studio Hybrid",
534 : : .matches = {
535 : : DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
536 : : DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
537 : : },
538 : : },
539 : : {
540 : : .callback = intel_no_lvds_dmi_callback,
541 : : .ident = "Dell OptiPlex FX170",
542 : : .matches = {
543 : : DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
544 : : DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"),
545 : : },
546 : : },
547 : : {
548 : : .callback = intel_no_lvds_dmi_callback,
549 : : .ident = "AOpen Mini PC",
550 : : .matches = {
551 : : DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
552 : : DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"),
553 : : },
554 : : },
555 : : {
556 : : .callback = intel_no_lvds_dmi_callback,
557 : : .ident = "AOpen Mini PC MP915",
558 : : .matches = {
559 : : DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
560 : : DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"),
561 : : },
562 : : },
563 : : {
564 : : .callback = intel_no_lvds_dmi_callback,
565 : : .ident = "AOpen i915GMm-HFS",
566 : : .matches = {
567 : : DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
568 : : DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
569 : : },
570 : : },
571 : : {
572 : : .callback = intel_no_lvds_dmi_callback,
573 : : .ident = "AOpen i45GMx-I",
574 : : .matches = {
575 : : DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
576 : : DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"),
577 : : },
578 : : },
579 : : {
580 : : .callback = intel_no_lvds_dmi_callback,
581 : : .ident = "Aopen i945GTt-VFA",
582 : : .matches = {
583 : : DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"),
584 : : },
585 : : },
586 : : {
587 : : .callback = intel_no_lvds_dmi_callback,
588 : : .ident = "Clientron U800",
589 : : .matches = {
590 : : DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
591 : : DMI_MATCH(DMI_PRODUCT_NAME, "U800"),
592 : : },
593 : : },
594 : : {
595 : : .callback = intel_no_lvds_dmi_callback,
596 : : .ident = "Clientron E830",
597 : : .matches = {
598 : : DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
599 : : DMI_MATCH(DMI_PRODUCT_NAME, "E830"),
600 : : },
601 : : },
602 : : {
603 : : .callback = intel_no_lvds_dmi_callback,
604 : : .ident = "Asus EeeBox PC EB1007",
605 : : .matches = {
606 : : DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
607 : : DMI_MATCH(DMI_PRODUCT_NAME, "EB1007"),
608 : : },
609 : : },
610 : : {
611 : : .callback = intel_no_lvds_dmi_callback,
612 : : .ident = "Asus AT5NM10T-I",
613 : : .matches = {
614 : : DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
615 : : DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"),
616 : : },
617 : : },
618 : : {
619 : : .callback = intel_no_lvds_dmi_callback,
620 : : .ident = "Hewlett-Packard HP t5740",
621 : : .matches = {
622 : : DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
623 : : DMI_MATCH(DMI_PRODUCT_NAME, " t5740"),
624 : : },
625 : : },
626 : : {
627 : : .callback = intel_no_lvds_dmi_callback,
628 : : .ident = "Hewlett-Packard t5745",
629 : : .matches = {
630 : : DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
631 : : DMI_MATCH(DMI_PRODUCT_NAME, "hp t5745"),
632 : : },
633 : : },
634 : : {
635 : : .callback = intel_no_lvds_dmi_callback,
636 : : .ident = "Hewlett-Packard st5747",
637 : : .matches = {
638 : : DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
639 : : DMI_MATCH(DMI_PRODUCT_NAME, "hp st5747"),
640 : : },
641 : : },
642 : : {
643 : : .callback = intel_no_lvds_dmi_callback,
644 : : .ident = "MSI Wind Box DC500",
645 : : .matches = {
646 : : DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
647 : : DMI_MATCH(DMI_BOARD_NAME, "MS-7469"),
648 : : },
649 : : },
650 : : {
651 : : .callback = intel_no_lvds_dmi_callback,
652 : : .ident = "Gigabyte GA-D525TUD",
653 : : .matches = {
654 : : DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."),
655 : : DMI_MATCH(DMI_BOARD_NAME, "D525TUD"),
656 : : },
657 : : },
658 : : {
659 : : .callback = intel_no_lvds_dmi_callback,
660 : : .ident = "Supermicro X7SPA-H",
661 : : .matches = {
662 : : DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"),
663 : : DMI_MATCH(DMI_PRODUCT_NAME, "X7SPA-H"),
664 : : },
665 : : },
666 : : {
667 : : .callback = intel_no_lvds_dmi_callback,
668 : : .ident = "Fujitsu Esprimo Q900",
669 : : .matches = {
670 : : DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
671 : : DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"),
672 : : },
673 : : },
674 : : {
675 : : .callback = intel_no_lvds_dmi_callback,
676 : : .ident = "Intel D410PT",
677 : : .matches = {
678 : : DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
679 : : DMI_MATCH(DMI_BOARD_NAME, "D410PT"),
680 : : },
681 : : },
682 : : {
683 : : .callback = intel_no_lvds_dmi_callback,
684 : : .ident = "Intel D425KT",
685 : : .matches = {
686 : : DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
687 : : DMI_EXACT_MATCH(DMI_BOARD_NAME, "D425KT"),
688 : : },
689 : : },
690 : : {
691 : : .callback = intel_no_lvds_dmi_callback,
692 : : .ident = "Intel D510MO",
693 : : .matches = {
694 : : DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
695 : : DMI_EXACT_MATCH(DMI_BOARD_NAME, "D510MO"),
696 : : },
697 : : },
698 : : {
699 : : .callback = intel_no_lvds_dmi_callback,
700 : : .ident = "Intel D525MW",
701 : : .matches = {
702 : : DMI_MATCH(DMI_BOARD_VENDOR, "Intel"),
703 : : DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"),
704 : : },
705 : : },
706 : : {
707 : : .callback = intel_no_lvds_dmi_callback,
708 : : .ident = "Radiant P845",
709 : : .matches = {
710 : : DMI_MATCH(DMI_SYS_VENDOR, "Radiant Systems Inc"),
711 : : DMI_MATCH(DMI_PRODUCT_NAME, "P845"),
712 : : },
713 : : },
714 : :
715 : : { } /* terminating entry */
716 : : };
717 : :
718 : 0 : static int intel_dual_link_lvds_callback(const struct dmi_system_id *id)
719 : : {
720 : 0 : DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident);
721 : 0 : return 1;
722 : : }
723 : :
724 : : static const struct dmi_system_id intel_dual_link_lvds[] = {
725 : : {
726 : : .callback = intel_dual_link_lvds_callback,
727 : : .ident = "Apple MacBook Pro 15\" (2010)",
728 : : .matches = {
729 : : DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
730 : : DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro6,2"),
731 : : },
732 : : },
733 : : {
734 : : .callback = intel_dual_link_lvds_callback,
735 : : .ident = "Apple MacBook Pro 15\" (2011)",
736 : : .matches = {
737 : : DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
738 : : DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"),
739 : : },
740 : : },
741 : : {
742 : : .callback = intel_dual_link_lvds_callback,
743 : : .ident = "Apple MacBook Pro 15\" (2012)",
744 : : .matches = {
745 : : DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
746 : : DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro9,1"),
747 : : },
748 : : },
749 : : { } /* terminating entry */
750 : : };
751 : :
752 : 0 : struct intel_encoder *intel_get_lvds_encoder(struct drm_i915_private *dev_priv)
753 : : {
754 : 0 : struct intel_encoder *encoder;
755 : :
756 [ # # # # ]: 0 : for_each_intel_encoder(&dev_priv->drm, encoder) {
757 [ # # # # ]: 0 : if (encoder->type == INTEL_OUTPUT_LVDS)
758 : 0 : return encoder;
759 : : }
760 : :
761 : : return NULL;
762 : : }
763 : :
764 : 0 : bool intel_is_dual_link_lvds(struct drm_i915_private *dev_priv)
765 : : {
766 : 0 : struct intel_encoder *encoder = intel_get_lvds_encoder(dev_priv);
767 : :
768 [ # # # # ]: 0 : return encoder && to_lvds_encoder(&encoder->base)->is_dual_link;
769 : : }
770 : :
771 : 0 : static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder)
772 : : {
773 : 0 : struct drm_device *dev = lvds_encoder->base.base.dev;
774 : 0 : unsigned int val;
775 [ # # ]: 0 : struct drm_i915_private *dev_priv = to_i915(dev);
776 : :
777 : : /* use the module option value if specified */
778 [ # # ]: 0 : if (i915_modparams.lvds_channel_mode > 0)
779 : 0 : return i915_modparams.lvds_channel_mode == 2;
780 : :
781 : : /* single channel LVDS is limited to 112 MHz */
782 [ # # ]: 0 : if (lvds_encoder->attached_connector->panel.fixed_mode->clock > 112999)
783 : : return true;
784 : :
785 [ # # ]: 0 : if (dmi_check_system(intel_dual_link_lvds))
786 : : return true;
787 : :
788 : : /*
789 : : * BIOS should set the proper LVDS register value at boot, but
790 : : * in reality, it doesn't set the value when the lid is closed;
791 : : * we need to check "the value to be set" in VBT when LVDS
792 : : * register is uninitialized.
793 : : */
794 : 0 : val = I915_READ(lvds_encoder->reg);
795 [ # # ]: 0 : if (HAS_PCH_CPT(dev_priv))
796 : 0 : val &= ~(LVDS_DETECTED | LVDS_PIPE_SEL_MASK_CPT);
797 : : else
798 : 0 : val &= ~(LVDS_DETECTED | LVDS_PIPE_SEL_MASK);
799 [ # # ]: 0 : if (val == 0)
800 : 0 : val = dev_priv->vbt.bios_lvds_val;
801 : :
802 : 0 : return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP;
803 : : }
804 : :
805 : : /**
806 : : * intel_lvds_init - setup LVDS connectors on this device
807 : : * @dev_priv: i915 device
808 : : *
809 : : * Create the connector, register the LVDS DDC bus, and try to figure out what
810 : : * modes we can display on the LVDS panel (if present).
811 : : */
812 : 0 : void intel_lvds_init(struct drm_i915_private *dev_priv)
813 : : {
814 : 0 : struct drm_device *dev = &dev_priv->drm;
815 : 0 : struct intel_lvds_encoder *lvds_encoder;
816 : 0 : struct intel_encoder *intel_encoder;
817 : 0 : struct intel_connector *intel_connector;
818 : 0 : struct drm_connector *connector;
819 : 0 : struct drm_encoder *encoder;
820 : 0 : struct drm_display_mode *fixed_mode = NULL;
821 : 0 : struct drm_display_mode *downclock_mode = NULL;
822 : 0 : struct edid *edid;
823 : 0 : i915_reg_t lvds_reg;
824 : 0 : u32 lvds;
825 : 0 : u8 pin;
826 : 0 : u32 allowed_scalers;
827 : :
828 : : /* Skip init on machines we know falsely report LVDS */
829 [ # # ]: 0 : if (dmi_check_system(intel_no_lvds)) {
830 [ # # ]: 0 : WARN(!dev_priv->vbt.int_lvds_support,
831 : : "Useless DMI match. Internal LVDS support disabled by VBT\n");
832 : 0 : return;
833 : : }
834 : :
835 [ # # ]: 0 : if (!dev_priv->vbt.int_lvds_support) {
836 : 0 : DRM_DEBUG_KMS("Internal LVDS support disabled by VBT\n");
837 : 0 : return;
838 : : }
839 : :
840 [ # # ]: 0 : if (HAS_PCH_SPLIT(dev_priv))
841 : : lvds_reg = PCH_LVDS;
842 : : else
843 : 0 : lvds_reg = LVDS;
844 : :
845 : 0 : lvds = I915_READ(lvds_reg);
846 : :
847 [ # # ]: 0 : if (HAS_PCH_SPLIT(dev_priv)) {
848 [ # # ]: 0 : if ((lvds & LVDS_DETECTED) == 0)
849 : : return;
850 : : }
851 : :
852 : 0 : pin = GMBUS_PIN_PANEL;
853 [ # # ]: 0 : if (!intel_bios_is_lvds_present(dev_priv, &pin)) {
854 [ # # ]: 0 : if ((lvds & LVDS_PORT_EN) == 0) {
855 : 0 : DRM_DEBUG_KMS("LVDS is not present in VBT\n");
856 : 0 : return;
857 : : }
858 : 0 : DRM_DEBUG_KMS("LVDS is not present in VBT, but enabled anyway\n");
859 : : }
860 : :
861 : 0 : lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL);
862 [ # # ]: 0 : if (!lvds_encoder)
863 : : return;
864 : :
865 : 0 : intel_connector = intel_connector_alloc();
866 [ # # ]: 0 : if (!intel_connector) {
867 : 0 : kfree(lvds_encoder);
868 : 0 : return;
869 : : }
870 : :
871 : 0 : lvds_encoder->attached_connector = intel_connector;
872 : :
873 : 0 : intel_encoder = &lvds_encoder->base;
874 : 0 : encoder = &intel_encoder->base;
875 : 0 : connector = &intel_connector->base;
876 : 0 : drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs,
877 : : DRM_MODE_CONNECTOR_LVDS);
878 : :
879 : 0 : drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
880 : : DRM_MODE_ENCODER_LVDS, "LVDS");
881 : :
882 : 0 : intel_encoder->enable = intel_enable_lvds;
883 : 0 : intel_encoder->pre_enable = intel_pre_enable_lvds;
884 : 0 : intel_encoder->compute_config = intel_lvds_compute_config;
885 [ # # ]: 0 : if (HAS_PCH_SPLIT(dev_priv)) {
886 : 0 : intel_encoder->disable = pch_disable_lvds;
887 : 0 : intel_encoder->post_disable = pch_post_disable_lvds;
888 : : } else {
889 : 0 : intel_encoder->disable = gmch_disable_lvds;
890 : : }
891 : 0 : intel_encoder->get_hw_state = intel_lvds_get_hw_state;
892 : 0 : intel_encoder->get_config = intel_lvds_get_config;
893 : 0 : intel_encoder->update_pipe = intel_panel_update_backlight;
894 : 0 : intel_connector->get_hw_state = intel_connector_get_hw_state;
895 : :
896 : 0 : intel_connector_attach_encoder(intel_connector, intel_encoder);
897 : :
898 : 0 : intel_encoder->type = INTEL_OUTPUT_LVDS;
899 : 0 : intel_encoder->power_domain = POWER_DOMAIN_PORT_OTHER;
900 : 0 : intel_encoder->port = PORT_NONE;
901 : 0 : intel_encoder->cloneable = 0;
902 [ # # ]: 0 : if (INTEL_GEN(dev_priv) < 4)
903 : 0 : intel_encoder->pipe_mask = BIT(PIPE_B);
904 : : else
905 : 0 : intel_encoder->pipe_mask = ~0;
906 : :
907 : 0 : drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
908 : 0 : connector->display_info.subpixel_order = SubPixelHorizontalRGB;
909 : 0 : connector->interlace_allowed = false;
910 : 0 : connector->doublescan_allowed = false;
911 : :
912 : 0 : lvds_encoder->reg = lvds_reg;
913 : :
914 : : /* create the scaling mode property */
915 : 0 : allowed_scalers = BIT(DRM_MODE_SCALE_ASPECT);
916 : 0 : allowed_scalers |= BIT(DRM_MODE_SCALE_FULLSCREEN);
917 : 0 : allowed_scalers |= BIT(DRM_MODE_SCALE_CENTER);
918 : 0 : drm_connector_attach_scaling_mode_property(connector, allowed_scalers);
919 : 0 : connector->state->scaling_mode = DRM_MODE_SCALE_ASPECT;
920 : :
921 : 0 : intel_lvds_pps_get_hw_state(dev_priv, &lvds_encoder->init_pps);
922 : 0 : lvds_encoder->init_lvds_val = lvds;
923 : :
924 : : /*
925 : : * LVDS discovery:
926 : : * 1) check for EDID on DDC
927 : : * 2) check for VBT data
928 : : * 3) check to see if LVDS is already on
929 : : * if none of the above, no panel
930 : : */
931 : :
932 : : /*
933 : : * Attempt to get the fixed panel mode from DDC. Assume that the
934 : : * preferred mode is the right one.
935 : : */
936 : 0 : mutex_lock(&dev->mode_config.mutex);
937 : 0 : if (vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC)
938 : : edid = drm_get_edid_switcheroo(connector,
939 : : intel_gmbus_get_adapter(dev_priv, pin));
940 : : else
941 : 0 : edid = drm_get_edid(connector,
942 : : intel_gmbus_get_adapter(dev_priv, pin));
943 [ # # ]: 0 : if (edid) {
944 [ # # ]: 0 : if (drm_add_edid_modes(connector, edid)) {
945 : 0 : drm_connector_update_edid_property(connector,
946 : : edid);
947 : : } else {
948 : 0 : kfree(edid);
949 : 0 : edid = ERR_PTR(-EINVAL);
950 : : }
951 : : } else {
952 : : edid = ERR_PTR(-ENOENT);
953 : : }
954 : 0 : intel_connector->edid = edid;
955 : :
956 : 0 : fixed_mode = intel_panel_edid_fixed_mode(intel_connector);
957 [ # # ]: 0 : if (fixed_mode)
958 : 0 : goto out;
959 : :
960 : : /* Failed to get EDID, what about VBT? */
961 : 0 : fixed_mode = intel_panel_vbt_fixed_mode(intel_connector);
962 [ # # ]: 0 : if (fixed_mode)
963 : 0 : goto out;
964 : :
965 : : /*
966 : : * If we didn't get EDID, try checking if the panel is already turned
967 : : * on. If so, assume that whatever is currently programmed is the
968 : : * correct mode.
969 : : */
970 : 0 : fixed_mode = intel_encoder_current_mode(intel_encoder);
971 [ # # ]: 0 : if (fixed_mode) {
972 : 0 : DRM_DEBUG_KMS("using current (BIOS) mode: ");
973 : 0 : drm_mode_debug_printmodeline(fixed_mode);
974 : 0 : fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
975 : : }
976 : :
977 : : /* If we still don't have a mode after all that, give up. */
978 [ # # ]: 0 : if (!fixed_mode)
979 : 0 : goto failed;
980 : :
981 : 0 : out:
982 : 0 : mutex_unlock(&dev->mode_config.mutex);
983 : :
984 : 0 : intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
985 : 0 : intel_panel_setup_backlight(connector, INVALID_PIPE);
986 : :
987 : 0 : lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder);
988 [ # # ]: 0 : DRM_DEBUG_KMS("detected %s-link lvds configuration\n",
989 : : lvds_encoder->is_dual_link ? "dual" : "single");
990 : :
991 : 0 : lvds_encoder->a3_power = lvds & LVDS_A3_POWER_MASK;
992 : :
993 : 0 : return;
994 : :
995 : : failed:
996 : 0 : mutex_unlock(&dev->mode_config.mutex);
997 : :
998 : 0 : DRM_DEBUG_KMS("No LVDS modes found, disabling.\n");
999 : 0 : drm_connector_cleanup(connector);
1000 : 0 : drm_encoder_cleanup(encoder);
1001 : 0 : kfree(lvds_encoder);
1002 : 0 : intel_connector_free(intel_connector);
1003 : 0 : return;
1004 : : }
|