| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 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 |  | 
| Jesse Barnes | c1c7af6 | 2009-09-10 15:28:03 -0700 | [diff] [blame] | 30 | #include <acpi/button.h> | 
| Paul Collins | 565dcd4 | 2009-02-04 23:05:41 +1300 | [diff] [blame] | 31 | #include <linux/dmi.h> | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 32 | #include <linux/i2c.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 33 | #include <linux/slab.h> | 
| David Howells | 760285e | 2012-10-02 18:01:07 +0100 | [diff] [blame] | 34 | #include <drm/drmP.h> | 
|  | 35 | #include <drm/drm_crtc.h> | 
|  | 36 | #include <drm/drm_edid.h> | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 37 | #include "intel_drv.h" | 
| David Howells | 760285e | 2012-10-02 18:01:07 +0100 | [diff] [blame] | 38 | #include <drm/i915_drm.h> | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 39 | #include "i915_drv.h" | 
| Zhao Yakui | e99da35 | 2009-06-26 09:46:18 +0800 | [diff] [blame] | 40 | #include <linux/acpi.h> | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 41 |  | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 42 | /* Private structure for the integrated LVDS support */ | 
| Jani Nikula | c7362c4 | 2012-10-19 14:51:44 +0300 | [diff] [blame] | 43 | struct intel_lvds_connector { | 
|  | 44 | struct intel_connector base; | 
| Jani Nikula | db1740a | 2012-10-19 14:51:45 +0300 | [diff] [blame] | 45 |  | 
|  | 46 | struct notifier_block lid_notifier; | 
| Jani Nikula | c7362c4 | 2012-10-19 14:51:44 +0300 | [diff] [blame] | 47 | }; | 
|  | 48 |  | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 49 | struct intel_lvds_encoder { | 
| Chris Wilson | ea5b213 | 2010-08-04 13:50:23 +0100 | [diff] [blame] | 50 | struct intel_encoder base; | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 51 |  | 
| Daniel Vetter | 13c7d87 | 2012-11-26 17:22:10 +0100 | [diff] [blame] | 52 | bool is_dual_link; | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 53 | u32 reg; | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 54 |  | 
| Jani Nikula | 62165e0 | 2012-10-19 14:51:47 +0300 | [diff] [blame] | 55 | struct intel_lvds_connector *attached_connector; | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 56 | }; | 
|  | 57 |  | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 58 | static struct intel_lvds_encoder *to_lvds_encoder(struct drm_encoder *encoder) | 
| Chris Wilson | ea5b213 | 2010-08-04 13:50:23 +0100 | [diff] [blame] | 59 | { | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 60 | return container_of(encoder, struct intel_lvds_encoder, base.base); | 
| Chris Wilson | ea5b213 | 2010-08-04 13:50:23 +0100 | [diff] [blame] | 61 | } | 
|  | 62 |  | 
| Jani Nikula | c7362c4 | 2012-10-19 14:51:44 +0300 | [diff] [blame] | 63 | static struct intel_lvds_connector *to_lvds_connector(struct drm_connector *connector) | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 64 | { | 
| Jani Nikula | c7362c4 | 2012-10-19 14:51:44 +0300 | [diff] [blame] | 65 | return container_of(connector, struct intel_lvds_connector, base.base); | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 66 | } | 
|  | 67 |  | 
| Daniel Vetter | b1dc332 | 2012-07-02 21:09:00 +0200 | [diff] [blame] | 68 | static bool intel_lvds_get_hw_state(struct intel_encoder *encoder, | 
|  | 69 | enum pipe *pipe) | 
|  | 70 | { | 
|  | 71 | struct drm_device *dev = encoder->base.dev; | 
|  | 72 | struct drm_i915_private *dev_priv = dev->dev_private; | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 73 | struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); | 
|  | 74 | u32 tmp; | 
| Daniel Vetter | b1dc332 | 2012-07-02 21:09:00 +0200 | [diff] [blame] | 75 |  | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 76 | tmp = I915_READ(lvds_encoder->reg); | 
| Daniel Vetter | b1dc332 | 2012-07-02 21:09:00 +0200 | [diff] [blame] | 77 |  | 
|  | 78 | if (!(tmp & LVDS_PORT_EN)) | 
|  | 79 | return false; | 
|  | 80 |  | 
|  | 81 | if (HAS_PCH_CPT(dev)) | 
|  | 82 | *pipe = PORT_TO_PIPE_CPT(tmp); | 
|  | 83 | else | 
|  | 84 | *pipe = PORT_TO_PIPE(tmp); | 
|  | 85 |  | 
|  | 86 | return true; | 
|  | 87 | } | 
|  | 88 |  | 
| Jesse Barnes | 045ac3b | 2013-05-14 17:08:26 -0700 | [diff] [blame] | 89 | static void intel_lvds_get_config(struct intel_encoder *encoder, | 
|  | 90 | struct intel_crtc_config *pipe_config) | 
|  | 91 | { | 
|  | 92 | struct drm_device *dev = encoder->base.dev; | 
|  | 93 | struct drm_i915_private *dev_priv = dev->dev_private; | 
|  | 94 | u32 lvds_reg, tmp, flags = 0; | 
| Ville Syrjälä | 18442d0 | 2013-09-13 16:00:08 +0300 | [diff] [blame] | 95 | int dotclock; | 
| Jesse Barnes | 045ac3b | 2013-05-14 17:08:26 -0700 | [diff] [blame] | 96 |  | 
|  | 97 | if (HAS_PCH_SPLIT(dev)) | 
|  | 98 | lvds_reg = PCH_LVDS; | 
|  | 99 | else | 
|  | 100 | lvds_reg = LVDS; | 
|  | 101 |  | 
|  | 102 | tmp = I915_READ(lvds_reg); | 
|  | 103 | if (tmp & LVDS_HSYNC_POLARITY) | 
|  | 104 | flags |= DRM_MODE_FLAG_NHSYNC; | 
|  | 105 | else | 
|  | 106 | flags |= DRM_MODE_FLAG_PHSYNC; | 
|  | 107 | if (tmp & LVDS_VSYNC_POLARITY) | 
|  | 108 | flags |= DRM_MODE_FLAG_NVSYNC; | 
|  | 109 | else | 
|  | 110 | flags |= DRM_MODE_FLAG_PVSYNC; | 
|  | 111 |  | 
|  | 112 | pipe_config->adjusted_mode.flags |= flags; | 
| Daniel Vetter | 0692282 | 2013-07-11 13:35:40 +0200 | [diff] [blame] | 113 |  | 
| Ville Syrjälä | 18442d0 | 2013-09-13 16:00:08 +0300 | [diff] [blame] | 114 | dotclock = pipe_config->port_clock; | 
|  | 115 |  | 
|  | 116 | if (HAS_PCH_SPLIT(dev_priv->dev)) | 
|  | 117 | ironlake_check_encoder_dotclock(pipe_config, dotclock); | 
|  | 118 |  | 
| Damien Lespiau | 241bfc3 | 2013-09-25 16:45:37 +0100 | [diff] [blame] | 119 | pipe_config->adjusted_mode.crtc_clock = dotclock; | 
| Jesse Barnes | 045ac3b | 2013-05-14 17:08:26 -0700 | [diff] [blame] | 120 | } | 
|  | 121 |  | 
| Daniel Vetter | f6736a1 | 2013-06-05 13:34:30 +0200 | [diff] [blame] | 122 | static void intel_pre_enable_lvds(struct intel_encoder *encoder) | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 123 | { | 
|  | 124 | struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); | 
|  | 125 | struct drm_device *dev = encoder->base.dev; | 
|  | 126 | struct drm_i915_private *dev_priv = dev->dev_private; | 
| Daniel Vetter | 55607e8 | 2013-06-16 21:42:39 +0200 | [diff] [blame] | 127 | struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); | 
| Ville Syrjälä | 4c6df4b | 2013-09-02 21:13:39 +0300 | [diff] [blame] | 128 | const struct drm_display_mode *adjusted_mode = | 
|  | 129 | &crtc->config.adjusted_mode; | 
| Daniel Vetter | 55607e8 | 2013-06-16 21:42:39 +0200 | [diff] [blame] | 130 | int pipe = crtc->pipe; | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 131 | u32 temp; | 
|  | 132 |  | 
| Daniel Vetter | 55607e8 | 2013-06-16 21:42:39 +0200 | [diff] [blame] | 133 | if (HAS_PCH_SPLIT(dev)) { | 
|  | 134 | assert_fdi_rx_pll_disabled(dev_priv, pipe); | 
|  | 135 | assert_shared_dpll_disabled(dev_priv, | 
|  | 136 | intel_crtc_to_shared_dpll(crtc)); | 
|  | 137 | } else { | 
|  | 138 | assert_pll_disabled(dev_priv, pipe); | 
|  | 139 | } | 
|  | 140 |  | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 141 | temp = I915_READ(lvds_encoder->reg); | 
|  | 142 | temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; | 
| Daniel Vetter | 62810e5 | 2012-11-26 17:22:13 +0100 | [diff] [blame] | 143 |  | 
|  | 144 | if (HAS_PCH_CPT(dev)) { | 
|  | 145 | temp &= ~PORT_TRANS_SEL_MASK; | 
|  | 146 | temp |= PORT_TRANS_SEL_CPT(pipe); | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 147 | } else { | 
| Daniel Vetter | 62810e5 | 2012-11-26 17:22:13 +0100 | [diff] [blame] | 148 | if (pipe == 1) { | 
|  | 149 | temp |= LVDS_PIPEB_SELECT; | 
|  | 150 | } else { | 
|  | 151 | temp &= ~LVDS_PIPEB_SELECT; | 
|  | 152 | } | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 153 | } | 
| Daniel Vetter | 62810e5 | 2012-11-26 17:22:13 +0100 | [diff] [blame] | 154 |  | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 155 | /* set the corresponsding LVDS_BORDER bit */ | 
| Daniel Vetter | 2fa2fe9 | 2013-05-07 23:34:16 +0200 | [diff] [blame] | 156 | temp &= ~LVDS_BORDER_ENABLE; | 
| Daniel Vetter | 55607e8 | 2013-06-16 21:42:39 +0200 | [diff] [blame] | 157 | temp |= crtc->config.gmch_pfit.lvds_border_bits; | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 158 | /* Set the B0-B3 data pairs corresponding to whether we're going to | 
|  | 159 | * set the DPLLs for dual-channel mode or not. | 
|  | 160 | */ | 
|  | 161 | if (lvds_encoder->is_dual_link) | 
|  | 162 | temp |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; | 
|  | 163 | else | 
|  | 164 | temp &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); | 
|  | 165 |  | 
|  | 166 | /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) | 
|  | 167 | * appropriately here, but we need to look more thoroughly into how | 
|  | 168 | * panels behave in the two modes. | 
|  | 169 | */ | 
| Daniel Vetter | 62810e5 | 2012-11-26 17:22:13 +0100 | [diff] [blame] | 170 |  | 
|  | 171 | /* Set the dithering flag on LVDS as needed, note that there is no | 
|  | 172 | * special lvds dither control bit on pch-split platforms, dithering is | 
|  | 173 | * only controlled through the PIPECONF reg. */ | 
|  | 174 | if (INTEL_INFO(dev)->gen == 4) { | 
| Daniel Vetter | d8b3224 | 2013-04-25 17:54:44 +0200 | [diff] [blame] | 175 | /* Bspec wording suggests that LVDS port dithering only exists | 
|  | 176 | * for 18bpp panels. */ | 
| Daniel Vetter | 55607e8 | 2013-06-16 21:42:39 +0200 | [diff] [blame] | 177 | if (crtc->config.dither && crtc->config.pipe_bpp == 18) | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 178 | temp |= LVDS_ENABLE_DITHER; | 
|  | 179 | else | 
|  | 180 | temp &= ~LVDS_ENABLE_DITHER; | 
|  | 181 | } | 
|  | 182 | temp &= ~(LVDS_HSYNC_POLARITY | LVDS_VSYNC_POLARITY); | 
| Ville Syrjälä | 4c6df4b | 2013-09-02 21:13:39 +0300 | [diff] [blame] | 183 | if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 184 | temp |= LVDS_HSYNC_POLARITY; | 
| Ville Syrjälä | 4c6df4b | 2013-09-02 21:13:39 +0300 | [diff] [blame] | 185 | if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) | 
| Daniel Vetter | fc68309 | 2012-11-26 17:22:12 +0100 | [diff] [blame] | 186 | temp |= LVDS_VSYNC_POLARITY; | 
|  | 187 |  | 
|  | 188 | I915_WRITE(lvds_encoder->reg, temp); | 
|  | 189 | } | 
|  | 190 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 191 | /** | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 192 | * Sets the power state for the panel. | 
|  | 193 | */ | 
| Daniel Vetter | c22834e | 2012-06-30 15:31:28 +0200 | [diff] [blame] | 194 | static void intel_enable_lvds(struct intel_encoder *encoder) | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 195 | { | 
| Daniel Vetter | c22834e | 2012-06-30 15:31:28 +0200 | [diff] [blame] | 196 | struct drm_device *dev = encoder->base.dev; | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 197 | struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); | 
| Jesse Barnes | 752aa88 | 2013-10-31 18:55:49 +0200 | [diff] [blame] | 198 | struct intel_connector *intel_connector = | 
|  | 199 | &lvds_encoder->attached_connector->base; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 200 | struct drm_i915_private *dev_priv = dev->dev_private; | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 201 | u32 ctl_reg, stat_reg; | 
| Zhenyu Wang | 541998a | 2009-06-05 15:38:44 +0800 | [diff] [blame] | 202 |  | 
| Eric Anholt | c619eed | 2010-01-28 16:45:52 -0800 | [diff] [blame] | 203 | if (HAS_PCH_SPLIT(dev)) { | 
| Zhenyu Wang | 541998a | 2009-06-05 15:38:44 +0800 | [diff] [blame] | 204 | ctl_reg = PCH_PP_CONTROL; | 
| Keith Packard | de842ef | 2011-08-06 10:30:45 -0700 | [diff] [blame] | 205 | stat_reg = PCH_PP_STATUS; | 
| Zhenyu Wang | 541998a | 2009-06-05 15:38:44 +0800 | [diff] [blame] | 206 | } else { | 
|  | 207 | ctl_reg = PP_CONTROL; | 
| Keith Packard | de842ef | 2011-08-06 10:30:45 -0700 | [diff] [blame] | 208 | stat_reg = PP_STATUS; | 
| Zhenyu Wang | 541998a | 2009-06-05 15:38:44 +0800 | [diff] [blame] | 209 | } | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 210 |  | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 211 | I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) | LVDS_PORT_EN); | 
| Chris Wilson | e9e331a | 2010-09-13 01:16:10 +0100 | [diff] [blame] | 212 |  | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 213 | I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 214 | POSTING_READ(lvds_encoder->reg); | 
| Keith Packard | de842ef | 2011-08-06 10:30:45 -0700 | [diff] [blame] | 215 | if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000)) | 
|  | 216 | DRM_ERROR("timed out waiting for panel to power on\n"); | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 217 |  | 
| Jesse Barnes | 752aa88 | 2013-10-31 18:55:49 +0200 | [diff] [blame] | 218 | intel_panel_enable_backlight(intel_connector); | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 219 | } | 
|  | 220 |  | 
| Daniel Vetter | c22834e | 2012-06-30 15:31:28 +0200 | [diff] [blame] | 221 | static void intel_disable_lvds(struct intel_encoder *encoder) | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 222 | { | 
| Daniel Vetter | c22834e | 2012-06-30 15:31:28 +0200 | [diff] [blame] | 223 | struct drm_device *dev = encoder->base.dev; | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 224 | struct intel_lvds_encoder *lvds_encoder = to_lvds_encoder(&encoder->base); | 
| Jesse Barnes | 752aa88 | 2013-10-31 18:55:49 +0200 | [diff] [blame] | 225 | struct intel_connector *intel_connector = | 
|  | 226 | &lvds_encoder->attached_connector->base; | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 227 | struct drm_i915_private *dev_priv = dev->dev_private; | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 228 | u32 ctl_reg, stat_reg; | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 229 |  | 
|  | 230 | if (HAS_PCH_SPLIT(dev)) { | 
|  | 231 | ctl_reg = PCH_PP_CONTROL; | 
| Keith Packard | de842ef | 2011-08-06 10:30:45 -0700 | [diff] [blame] | 232 | stat_reg = PCH_PP_STATUS; | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 233 | } else { | 
|  | 234 | ctl_reg = PP_CONTROL; | 
| Keith Packard | de842ef | 2011-08-06 10:30:45 -0700 | [diff] [blame] | 235 | stat_reg = PP_STATUS; | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 236 | } | 
|  | 237 |  | 
| Jesse Barnes | 752aa88 | 2013-10-31 18:55:49 +0200 | [diff] [blame] | 238 | intel_panel_disable_backlight(intel_connector); | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 239 |  | 
|  | 240 | I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); | 
| Keith Packard | de842ef | 2011-08-06 10:30:45 -0700 | [diff] [blame] | 241 | if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000)) | 
|  | 242 | DRM_ERROR("timed out waiting for panel to power off\n"); | 
| Chris Wilson | 2a1292f | 2010-12-05 19:21:18 +0000 | [diff] [blame] | 243 |  | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 244 | I915_WRITE(lvds_encoder->reg, I915_READ(lvds_encoder->reg) & ~LVDS_PORT_EN); | 
|  | 245 | POSTING_READ(lvds_encoder->reg); | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 246 | } | 
|  | 247 |  | 
| Damien Lespiau | c19de8e | 2013-11-28 15:29:18 +0000 | [diff] [blame] | 248 | static enum drm_mode_status | 
|  | 249 | intel_lvds_mode_valid(struct drm_connector *connector, | 
|  | 250 | struct drm_display_mode *mode) | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 251 | { | 
| Jani Nikula | dd06f90 | 2012-10-19 14:51:50 +0300 | [diff] [blame] | 252 | struct intel_connector *intel_connector = to_intel_connector(connector); | 
|  | 253 | struct drm_display_mode *fixed_mode = intel_connector->panel.fixed_mode; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 254 |  | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 255 | if (mode->hdisplay > fixed_mode->hdisplay) | 
|  | 256 | return MODE_PANEL; | 
|  | 257 | if (mode->vdisplay > fixed_mode->vdisplay) | 
|  | 258 | return MODE_PANEL; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 259 |  | 
|  | 260 | return MODE_OK; | 
|  | 261 | } | 
|  | 262 |  | 
| Daniel Vetter | 7ae8923 | 2013-03-27 00:44:52 +0100 | [diff] [blame] | 263 | static bool intel_lvds_compute_config(struct intel_encoder *intel_encoder, | 
|  | 264 | struct intel_crtc_config *pipe_config) | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 265 | { | 
| Daniel Vetter | 7ae8923 | 2013-03-27 00:44:52 +0100 | [diff] [blame] | 266 | struct drm_device *dev = intel_encoder->base.dev; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 267 | struct drm_i915_private *dev_priv = dev->dev_private; | 
| Daniel Vetter | 7ae8923 | 2013-03-27 00:44:52 +0100 | [diff] [blame] | 268 | struct intel_lvds_encoder *lvds_encoder = | 
|  | 269 | to_lvds_encoder(&intel_encoder->base); | 
| Jani Nikula | 4d89152 | 2012-10-26 12:03:59 +0300 | [diff] [blame] | 270 | struct intel_connector *intel_connector = | 
|  | 271 | &lvds_encoder->attached_connector->base; | 
| Daniel Vetter | 7ae8923 | 2013-03-27 00:44:52 +0100 | [diff] [blame] | 272 | struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode; | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 273 | struct intel_crtc *intel_crtc = lvds_encoder->base.new_crtc; | 
| Daniel Vetter | 4e53c2e | 2013-03-27 00:44:58 +0100 | [diff] [blame] | 274 | unsigned int lvds_bpp; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 275 |  | 
|  | 276 | /* Should never happen!! */ | 
| Chris Wilson | a6c45cf | 2010-09-17 00:32:17 +0100 | [diff] [blame] | 277 | if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) { | 
| Keith Packard | 1ae8c0a | 2009-06-28 15:42:17 -0700 | [diff] [blame] | 278 | DRM_ERROR("Can't support LVDS on pipe A\n"); | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 279 | return false; | 
|  | 280 | } | 
|  | 281 |  | 
| Daniel Vetter | 4e53c2e | 2013-03-27 00:44:58 +0100 | [diff] [blame] | 282 | if ((I915_READ(lvds_encoder->reg) & LVDS_A3_POWER_MASK) == | 
|  | 283 | LVDS_A3_POWER_UP) | 
|  | 284 | lvds_bpp = 8*3; | 
|  | 285 | else | 
|  | 286 | lvds_bpp = 6*3; | 
|  | 287 |  | 
| Daniel Vetter | e29c22c | 2013-02-21 00:00:16 +0100 | [diff] [blame] | 288 | if (lvds_bpp != pipe_config->pipe_bpp && !pipe_config->bw_constrained) { | 
| Daniel Vetter | 4e53c2e | 2013-03-27 00:44:58 +0100 | [diff] [blame] | 289 | DRM_DEBUG_KMS("forcing display bpp (was %d) to LVDS (%d)\n", | 
|  | 290 | pipe_config->pipe_bpp, lvds_bpp); | 
|  | 291 | pipe_config->pipe_bpp = lvds_bpp; | 
|  | 292 | } | 
| Daniel Vetter | d8b3224 | 2013-04-25 17:54:44 +0200 | [diff] [blame] | 293 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 294 | /* | 
| Chris Wilson | 7167704 | 2010-07-17 13:38:43 +0100 | [diff] [blame] | 295 | * We have timings from the BIOS for the panel, put them in | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 296 | * to the adjusted mode.  The CRTC will be set up for this mode, | 
|  | 297 | * with the panel scaling set up to source from the H/VDisplay | 
|  | 298 | * of the original mode. | 
|  | 299 | */ | 
| Jani Nikula | 4d89152 | 2012-10-26 12:03:59 +0300 | [diff] [blame] | 300 | intel_fixed_panel_mode(intel_connector->panel.fixed_mode, | 
| Jani Nikula | dd06f90 | 2012-10-19 14:51:50 +0300 | [diff] [blame] | 301 | adjusted_mode); | 
| Chris Wilson | 1d8e1c7 | 2010-08-07 11:01:28 +0100 | [diff] [blame] | 302 |  | 
|  | 303 | if (HAS_PCH_SPLIT(dev)) { | 
| Daniel Vetter | 5bfe2ac | 2013-03-27 00:44:55 +0100 | [diff] [blame] | 304 | pipe_config->has_pch_encoder = true; | 
|  | 305 |  | 
| Jesse Barnes | b074cec | 2013-04-25 12:55:02 -0700 | [diff] [blame] | 306 | intel_pch_panel_fitting(intel_crtc, pipe_config, | 
|  | 307 | intel_connector->panel.fitting_mode); | 
| Jesse Barnes | 2dd2455 | 2013-04-25 12:55:01 -0700 | [diff] [blame] | 308 | } else { | 
|  | 309 | intel_gmch_panel_fitting(intel_crtc, pipe_config, | 
|  | 310 | intel_connector->panel.fitting_mode); | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 311 |  | 
| Daniel Vetter | 21d8a47 | 2013-07-12 08:07:30 +0200 | [diff] [blame] | 312 | } | 
| Daniel Vetter | f9bef08 | 2012-04-15 19:53:19 +0200 | [diff] [blame] | 313 |  | 
| Zhao Yakui | a3e17eb | 2009-10-10 10:42:37 +0800 | [diff] [blame] | 314 | /* | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 315 | * XXX: It would be nice to support lower refresh rates on the | 
|  | 316 | * panels to reduce power consumption, and perhaps match the | 
|  | 317 | * user's requested refresh rate. | 
|  | 318 | */ | 
|  | 319 |  | 
|  | 320 | return true; | 
|  | 321 | } | 
|  | 322 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 323 | /** | 
|  | 324 | * Detect the LVDS connection. | 
|  | 325 | * | 
| Jesse Barnes | b42d4c5 | 2009-09-10 15:28:04 -0700 | [diff] [blame] | 326 | * Since LVDS doesn't have hotlug, we use the lid as a proxy.  Open means | 
|  | 327 | * connected and closed means disconnected.  We also send hotplug events as | 
|  | 328 | * needed, using lid status notification from the input layer. | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 329 | */ | 
| Chris Wilson | 7b334fc | 2010-09-09 23:51:02 +0100 | [diff] [blame] | 330 | static enum drm_connector_status | 
| Chris Wilson | 930a9e2 | 2010-09-14 11:07:23 +0100 | [diff] [blame] | 331 | intel_lvds_detect(struct drm_connector *connector, bool force) | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 332 | { | 
| Jesse Barnes | 7b9c5ab | 2010-02-12 09:30:00 -0800 | [diff] [blame] | 333 | struct drm_device *dev = connector->dev; | 
| Chris Wilson | 6ee3b5a | 2011-03-24 13:26:43 +0000 | [diff] [blame] | 334 | enum drm_connector_status status; | 
| Jesse Barnes | b42d4c5 | 2009-09-10 15:28:04 -0700 | [diff] [blame] | 335 |  | 
| Chris Wilson | 164c859 | 2013-07-20 20:27:08 +0100 | [diff] [blame] | 336 | DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", | 
| Jani Nikula | c23cc41 | 2014-06-03 14:56:17 +0300 | [diff] [blame^] | 337 | connector->base.id, connector->name); | 
| Chris Wilson | 164c859 | 2013-07-20 20:27:08 +0100 | [diff] [blame] | 338 |  | 
| Chris Wilson | fe16d94 | 2011-02-12 10:29:38 +0000 | [diff] [blame] | 339 | status = intel_panel_detect(dev); | 
|  | 340 | if (status != connector_status_unknown) | 
|  | 341 | return status; | 
| Chris Wilson | 01fe9db | 2011-01-16 19:37:30 +0000 | [diff] [blame] | 342 |  | 
| Chris Wilson | 6ee3b5a | 2011-03-24 13:26:43 +0000 | [diff] [blame] | 343 | return connector_status_connected; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 344 | } | 
|  | 345 |  | 
|  | 346 | /** | 
|  | 347 | * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. | 
|  | 348 | */ | 
|  | 349 | static int intel_lvds_get_modes(struct drm_connector *connector) | 
|  | 350 | { | 
| Jani Nikula | 62165e0 | 2012-10-19 14:51:47 +0300 | [diff] [blame] | 351 | struct intel_lvds_connector *lvds_connector = to_lvds_connector(connector); | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 352 | struct drm_device *dev = connector->dev; | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 353 | struct drm_display_mode *mode; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 354 |  | 
| Jani Nikula | 9cd300e | 2012-10-19 14:51:52 +0300 | [diff] [blame] | 355 | /* use cached edid if we have one */ | 
| Chris Wilson | 2aa4f09 | 2012-11-21 16:14:04 +0000 | [diff] [blame] | 356 | if (!IS_ERR_OR_NULL(lvds_connector->base.edid)) | 
| Jani Nikula | 9cd300e | 2012-10-19 14:51:52 +0300 | [diff] [blame] | 357 | return drm_add_edid_modes(connector, lvds_connector->base.edid); | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 358 |  | 
| Jani Nikula | dd06f90 | 2012-10-19 14:51:50 +0300 | [diff] [blame] | 359 | mode = drm_mode_duplicate(dev, lvds_connector->base.panel.fixed_mode); | 
| Chris Wilson | 311bd68 | 2011-01-13 19:06:50 +0000 | [diff] [blame] | 360 | if (mode == NULL) | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 361 | return 0; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 362 |  | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 363 | drm_mode_probed_add(connector, mode); | 
|  | 364 | return 1; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 365 | } | 
|  | 366 |  | 
| Thomas Bächler | 0544edf | 2010-07-02 10:44:23 +0200 | [diff] [blame] | 367 | static int intel_no_modeset_on_lid_dmi_callback(const struct dmi_system_id *id) | 
|  | 368 | { | 
| Daniel Vetter | bc0daf4 | 2012-04-01 13:16:49 +0200 | [diff] [blame] | 369 | DRM_INFO("Skipping forced modeset for %s\n", id->ident); | 
| Thomas Bächler | 0544edf | 2010-07-02 10:44:23 +0200 | [diff] [blame] | 370 | return 1; | 
|  | 371 | } | 
|  | 372 |  | 
|  | 373 | /* The GPU hangs up on these systems if modeset is performed on LID open */ | 
|  | 374 | static const struct dmi_system_id intel_no_modeset_on_lid[] = { | 
|  | 375 | { | 
|  | 376 | .callback = intel_no_modeset_on_lid_dmi_callback, | 
|  | 377 | .ident = "Toshiba Tecra A11", | 
|  | 378 | .matches = { | 
|  | 379 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), | 
|  | 380 | DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A11"), | 
|  | 381 | }, | 
|  | 382 | }, | 
|  | 383 |  | 
|  | 384 | { }	/* terminating entry */ | 
|  | 385 | }; | 
|  | 386 |  | 
| Linus Torvalds | c9354c8 | 2009-11-02 09:29:55 -0800 | [diff] [blame] | 387 | /* | 
| Zhang Rui | b8efb17 | 2013-02-05 15:41:53 +0800 | [diff] [blame] | 388 | * Lid events. Note the use of 'modeset': | 
|  | 389 | *  - we set it to MODESET_ON_LID_OPEN on lid close, | 
|  | 390 | *    and set it to MODESET_DONE on open | 
| Linus Torvalds | c9354c8 | 2009-11-02 09:29:55 -0800 | [diff] [blame] | 391 | *  - we use it as a "only once" bit (ie we ignore | 
| Zhang Rui | b8efb17 | 2013-02-05 15:41:53 +0800 | [diff] [blame] | 392 | *    duplicate events where it was already properly set) | 
|  | 393 | *  - the suspend/resume paths will set it to | 
|  | 394 | *    MODESET_SUSPENDED and ignore the lid open event, | 
|  | 395 | *    because they restore the mode ("lid open"). | 
| Linus Torvalds | c9354c8 | 2009-11-02 09:29:55 -0800 | [diff] [blame] | 396 | */ | 
| Jesse Barnes | c1c7af6 | 2009-09-10 15:28:03 -0700 | [diff] [blame] | 397 | static int intel_lid_notify(struct notifier_block *nb, unsigned long val, | 
|  | 398 | void *unused) | 
|  | 399 | { | 
| Jani Nikula | db1740a | 2012-10-19 14:51:45 +0300 | [diff] [blame] | 400 | struct intel_lvds_connector *lvds_connector = | 
|  | 401 | container_of(nb, struct intel_lvds_connector, lid_notifier); | 
|  | 402 | struct drm_connector *connector = &lvds_connector->base.base; | 
|  | 403 | struct drm_device *dev = connector->dev; | 
|  | 404 | struct drm_i915_private *dev_priv = dev->dev_private; | 
| Jesse Barnes | c1c7af6 | 2009-09-10 15:28:03 -0700 | [diff] [blame] | 405 |  | 
| Alex Williamson | 2fb4e61 | 2011-04-21 16:08:14 -0600 | [diff] [blame] | 406 | if (dev->switch_power_state != DRM_SWITCH_POWER_ON) | 
|  | 407 | return NOTIFY_OK; | 
|  | 408 |  | 
| Zhang Rui | b8efb17 | 2013-02-05 15:41:53 +0800 | [diff] [blame] | 409 | mutex_lock(&dev_priv->modeset_restore_lock); | 
|  | 410 | if (dev_priv->modeset_restore == MODESET_SUSPENDED) | 
|  | 411 | goto exit; | 
| Zhao Yakui | a256537 | 2009-12-11 09:26:11 +0800 | [diff] [blame] | 412 | /* | 
|  | 413 | * check and update the status of LVDS connector after receiving | 
|  | 414 | * the LID nofication event. | 
|  | 415 | */ | 
| Jani Nikula | db1740a | 2012-10-19 14:51:45 +0300 | [diff] [blame] | 416 | connector->status = connector->funcs->detect(connector, false); | 
| Chris Wilson | 7b334fc | 2010-09-09 23:51:02 +0100 | [diff] [blame] | 417 |  | 
| Thomas Bächler | 0544edf | 2010-07-02 10:44:23 +0200 | [diff] [blame] | 418 | /* Don't force modeset on machines where it causes a GPU lockup */ | 
|  | 419 | if (dmi_check_system(intel_no_modeset_on_lid)) | 
| Zhang Rui | b8efb17 | 2013-02-05 15:41:53 +0800 | [diff] [blame] | 420 | goto exit; | 
| Linus Torvalds | c9354c8 | 2009-11-02 09:29:55 -0800 | [diff] [blame] | 421 | if (!acpi_lid_open()) { | 
| Zhang Rui | b8efb17 | 2013-02-05 15:41:53 +0800 | [diff] [blame] | 422 | /* do modeset on next lid open event */ | 
|  | 423 | dev_priv->modeset_restore = MODESET_ON_LID_OPEN; | 
|  | 424 | goto exit; | 
| Jesse Barnes | 06891e2 | 2009-09-14 10:58:48 -0700 | [diff] [blame] | 425 | } | 
| Jesse Barnes | c1c7af6 | 2009-09-10 15:28:03 -0700 | [diff] [blame] | 426 |  | 
| Zhang Rui | b8efb17 | 2013-02-05 15:41:53 +0800 | [diff] [blame] | 427 | if (dev_priv->modeset_restore == MODESET_DONE) | 
|  | 428 | goto exit; | 
| Linus Torvalds | c9354c8 | 2009-11-02 09:29:55 -0800 | [diff] [blame] | 429 |  | 
| Daniel Vetter | 5be19d9 | 2013-10-09 10:47:12 +0200 | [diff] [blame] | 430 | /* | 
|  | 431 | * Some old platform's BIOS love to wreak havoc while the lid is closed. | 
|  | 432 | * We try to detect this here and undo any damage. The split for PCH | 
|  | 433 | * platforms is rather conservative and a bit arbitrary expect that on | 
|  | 434 | * those platforms VGA disabling requires actual legacy VGA I/O access, | 
|  | 435 | * and as part of the cleanup in the hw state restore we also redisable | 
|  | 436 | * the vga plane. | 
|  | 437 | */ | 
|  | 438 | if (!HAS_PCH_SPLIT(dev)) { | 
|  | 439 | drm_modeset_lock_all(dev); | 
|  | 440 | intel_modeset_setup_hw_state(dev, true); | 
|  | 441 | drm_modeset_unlock_all(dev); | 
|  | 442 | } | 
| Jesse Barnes | 0632419 | 2009-09-10 15:28:05 -0700 | [diff] [blame] | 443 |  | 
| Zhang Rui | b8efb17 | 2013-02-05 15:41:53 +0800 | [diff] [blame] | 444 | dev_priv->modeset_restore = MODESET_DONE; | 
|  | 445 |  | 
|  | 446 | exit: | 
|  | 447 | mutex_unlock(&dev_priv->modeset_restore_lock); | 
| Jesse Barnes | c1c7af6 | 2009-09-10 15:28:03 -0700 | [diff] [blame] | 448 | return NOTIFY_OK; | 
|  | 449 | } | 
|  | 450 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 451 | /** | 
|  | 452 | * intel_lvds_destroy - unregister and free LVDS structures | 
|  | 453 | * @connector: connector to free | 
|  | 454 | * | 
|  | 455 | * Unregister the DDC bus for this connector then free the driver private | 
|  | 456 | * structure. | 
|  | 457 | */ | 
|  | 458 | static void intel_lvds_destroy(struct drm_connector *connector) | 
|  | 459 | { | 
| Jani Nikula | db1740a | 2012-10-19 14:51:45 +0300 | [diff] [blame] | 460 | struct intel_lvds_connector *lvds_connector = | 
|  | 461 | to_lvds_connector(connector); | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 462 |  | 
| Jani Nikula | db1740a | 2012-10-19 14:51:45 +0300 | [diff] [blame] | 463 | if (lvds_connector->lid_notifier.notifier_call) | 
|  | 464 | acpi_lid_notifier_unregister(&lvds_connector->lid_notifier); | 
| Matthew Garrett | aaa6fd2 | 2011-08-12 12:11:33 +0200 | [diff] [blame] | 465 |  | 
| Jani Nikula | 9cd300e | 2012-10-19 14:51:52 +0300 | [diff] [blame] | 466 | if (!IS_ERR_OR_NULL(lvds_connector->base.edid)) | 
|  | 467 | kfree(lvds_connector->base.edid); | 
|  | 468 |  | 
| Jani Nikula | 1d50870 | 2012-10-19 14:51:49 +0300 | [diff] [blame] | 469 | intel_panel_fini(&lvds_connector->base.panel); | 
| Jani Nikula | db1740a | 2012-10-19 14:51:45 +0300 | [diff] [blame] | 470 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 471 | drm_connector_cleanup(connector); | 
|  | 472 | kfree(connector); | 
|  | 473 | } | 
|  | 474 |  | 
| Jesse Barnes | 335041e | 2009-01-22 22:22:06 +1000 | [diff] [blame] | 475 | static int intel_lvds_set_property(struct drm_connector *connector, | 
|  | 476 | struct drm_property *property, | 
|  | 477 | uint64_t value) | 
|  | 478 | { | 
| Jani Nikula | 4d89152 | 2012-10-26 12:03:59 +0300 | [diff] [blame] | 479 | struct intel_connector *intel_connector = to_intel_connector(connector); | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 480 | struct drm_device *dev = connector->dev; | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 481 |  | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 482 | if (property == dev->mode_config.scaling_mode_property) { | 
| Jani Nikula | 62165e0 | 2012-10-19 14:51:47 +0300 | [diff] [blame] | 483 | struct drm_crtc *crtc; | 
| Zhenyu Wang | bb8a356 | 2010-03-29 16:40:50 +0800 | [diff] [blame] | 484 |  | 
| Jesse Barnes | 53bd838 | 2009-07-01 10:04:40 -0700 | [diff] [blame] | 485 | if (value == DRM_MODE_SCALE_NONE) { | 
|  | 486 | DRM_DEBUG_KMS("no scaling not supported\n"); | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 487 | return -EINVAL; | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 488 | } | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 489 |  | 
| Jani Nikula | 4d89152 | 2012-10-26 12:03:59 +0300 | [diff] [blame] | 490 | if (intel_connector->panel.fitting_mode == value) { | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 491 | /* the LVDS scaling property is not changed */ | 
|  | 492 | return 0; | 
|  | 493 | } | 
| Jani Nikula | 4d89152 | 2012-10-26 12:03:59 +0300 | [diff] [blame] | 494 | intel_connector->panel.fitting_mode = value; | 
| Jani Nikula | 62165e0 | 2012-10-19 14:51:47 +0300 | [diff] [blame] | 495 |  | 
|  | 496 | crtc = intel_attached_encoder(connector)->base.crtc; | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 497 | if (crtc && crtc->enabled) { | 
|  | 498 | /* | 
|  | 499 | * If the CRTC is enabled, the display will be changed | 
|  | 500 | * according to the new panel fitting mode. | 
|  | 501 | */ | 
| Chris Wilson | c0c36b94 | 2012-12-19 16:08:43 +0000 | [diff] [blame] | 502 | intel_crtc_restore_mode(crtc); | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 503 | } | 
|  | 504 | } | 
|  | 505 |  | 
| Jesse Barnes | 335041e | 2009-01-22 22:22:06 +1000 | [diff] [blame] | 506 | return 0; | 
|  | 507 | } | 
|  | 508 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 509 | static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { | 
|  | 510 | .get_modes = intel_lvds_get_modes, | 
|  | 511 | .mode_valid = intel_lvds_mode_valid, | 
| Chris Wilson | df0e924 | 2010-09-09 16:20:55 +0100 | [diff] [blame] | 512 | .best_encoder = intel_best_encoder, | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 513 | }; | 
|  | 514 |  | 
|  | 515 | static const struct drm_connector_funcs intel_lvds_connector_funcs = { | 
| Daniel Vetter | c22834e | 2012-06-30 15:31:28 +0200 | [diff] [blame] | 516 | .dpms = intel_connector_dpms, | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 517 | .detect = intel_lvds_detect, | 
|  | 518 | .fill_modes = drm_helper_probe_single_connector_modes, | 
| Jesse Barnes | 335041e | 2009-01-22 22:22:06 +1000 | [diff] [blame] | 519 | .set_property = intel_lvds_set_property, | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 520 | .destroy = intel_lvds_destroy, | 
|  | 521 | }; | 
|  | 522 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 523 | static const struct drm_encoder_funcs intel_lvds_enc_funcs = { | 
| Chris Wilson | ea5b213 | 2010-08-04 13:50:23 +0100 | [diff] [blame] | 524 | .destroy = intel_encoder_destroy, | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 525 | }; | 
|  | 526 |  | 
| Jarod Wilson | 425d244 | 2009-05-05 10:00:25 -0400 | [diff] [blame] | 527 | static int __init intel_no_lvds_dmi_callback(const struct dmi_system_id *id) | 
|  | 528 | { | 
| Daniel Vetter | bc0daf4 | 2012-04-01 13:16:49 +0200 | [diff] [blame] | 529 | DRM_INFO("Skipping LVDS initialization for %s\n", id->ident); | 
| Jarod Wilson | 425d244 | 2009-05-05 10:00:25 -0400 | [diff] [blame] | 530 | return 1; | 
|  | 531 | } | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 532 |  | 
| Jarod Wilson | 425d244 | 2009-05-05 10:00:25 -0400 | [diff] [blame] | 533 | /* These systems claim to have LVDS, but really don't */ | 
| Jaswinder Singh Rajput | 93c05f2 | 2009-06-04 09:41:19 +1000 | [diff] [blame] | 534 | static const struct dmi_system_id intel_no_lvds[] = { | 
| Jarod Wilson | 425d244 | 2009-05-05 10:00:25 -0400 | [diff] [blame] | 535 | { | 
|  | 536 | .callback = intel_no_lvds_dmi_callback, | 
|  | 537 | .ident = "Apple Mac Mini (Core series)", | 
|  | 538 | .matches = { | 
| Keith Packard | 98acd46 | 2009-06-14 12:31:58 -0700 | [diff] [blame] | 539 | DMI_MATCH(DMI_SYS_VENDOR, "Apple"), | 
| Jarod Wilson | 425d244 | 2009-05-05 10:00:25 -0400 | [diff] [blame] | 540 | DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"), | 
|  | 541 | }, | 
|  | 542 | }, | 
|  | 543 | { | 
|  | 544 | .callback = intel_no_lvds_dmi_callback, | 
|  | 545 | .ident = "Apple Mac Mini (Core 2 series)", | 
|  | 546 | .matches = { | 
| Keith Packard | 98acd46 | 2009-06-14 12:31:58 -0700 | [diff] [blame] | 547 | DMI_MATCH(DMI_SYS_VENDOR, "Apple"), | 
| Jarod Wilson | 425d244 | 2009-05-05 10:00:25 -0400 | [diff] [blame] | 548 | DMI_MATCH(DMI_PRODUCT_NAME, "Macmini2,1"), | 
|  | 549 | }, | 
|  | 550 | }, | 
|  | 551 | { | 
|  | 552 | .callback = intel_no_lvds_dmi_callback, | 
|  | 553 | .ident = "MSI IM-945GSE-A", | 
|  | 554 | .matches = { | 
|  | 555 | DMI_MATCH(DMI_SYS_VENDOR, "MSI"), | 
|  | 556 | DMI_MATCH(DMI_PRODUCT_NAME, "A9830IMS"), | 
|  | 557 | }, | 
|  | 558 | }, | 
|  | 559 | { | 
|  | 560 | .callback = intel_no_lvds_dmi_callback, | 
|  | 561 | .ident = "Dell Studio Hybrid", | 
|  | 562 | .matches = { | 
|  | 563 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 
|  | 564 | DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"), | 
|  | 565 | }, | 
|  | 566 | }, | 
| Jarod Wilson | 70aa96c | 2009-05-27 17:20:39 -0400 | [diff] [blame] | 567 | { | 
|  | 568 | .callback = intel_no_lvds_dmi_callback, | 
| Pieterjan Camerlynck | b066254 | 2011-07-26 16:23:54 +0200 | [diff] [blame] | 569 | .ident = "Dell OptiPlex FX170", | 
|  | 570 | .matches = { | 
|  | 571 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), | 
|  | 572 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"), | 
|  | 573 | }, | 
|  | 574 | }, | 
|  | 575 | { | 
|  | 576 | .callback = intel_no_lvds_dmi_callback, | 
| Jarod Wilson | 70aa96c | 2009-05-27 17:20:39 -0400 | [diff] [blame] | 577 | .ident = "AOpen Mini PC", | 
|  | 578 | .matches = { | 
|  | 579 | DMI_MATCH(DMI_SYS_VENDOR, "AOpen"), | 
|  | 580 | DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"), | 
|  | 581 | }, | 
|  | 582 | }, | 
| Michael Cousin | fa0864b | 2009-06-05 21:16:22 +0200 | [diff] [blame] | 583 | { | 
|  | 584 | .callback = intel_no_lvds_dmi_callback, | 
| Tormod Volden | ed8c754 | 2009-07-13 22:26:48 +0200 | [diff] [blame] | 585 | .ident = "AOpen Mini PC MP915", | 
|  | 586 | .matches = { | 
|  | 587 | DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), | 
|  | 588 | DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"), | 
|  | 589 | }, | 
|  | 590 | }, | 
|  | 591 | { | 
|  | 592 | .callback = intel_no_lvds_dmi_callback, | 
| Knut Petersen | 22ab70d | 2011-01-14 15:38:10 +0000 | [diff] [blame] | 593 | .ident = "AOpen i915GMm-HFS", | 
|  | 594 | .matches = { | 
|  | 595 | DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), | 
|  | 596 | DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), | 
|  | 597 | }, | 
|  | 598 | }, | 
|  | 599 | { | 
|  | 600 | .callback = intel_no_lvds_dmi_callback, | 
| Daniel Vetter | e57b688 | 2012-02-08 16:42:52 +0100 | [diff] [blame] | 601 | .ident = "AOpen i45GMx-I", | 
|  | 602 | .matches = { | 
|  | 603 | DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), | 
|  | 604 | DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"), | 
|  | 605 | }, | 
|  | 606 | }, | 
|  | 607 | { | 
|  | 608 | .callback = intel_no_lvds_dmi_callback, | 
| Michael Cousin | fa0864b | 2009-06-05 21:16:22 +0200 | [diff] [blame] | 609 | .ident = "Aopen i945GTt-VFA", | 
|  | 610 | .matches = { | 
|  | 611 | DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"), | 
|  | 612 | }, | 
|  | 613 | }, | 
| Stefan Bader | 9875557ee8 | 2010-03-29 17:53:12 +0200 | [diff] [blame] | 614 | { | 
|  | 615 | .callback = intel_no_lvds_dmi_callback, | 
|  | 616 | .ident = "Clientron U800", | 
|  | 617 | .matches = { | 
|  | 618 | DMI_MATCH(DMI_SYS_VENDOR, "Clientron"), | 
|  | 619 | DMI_MATCH(DMI_PRODUCT_NAME, "U800"), | 
|  | 620 | }, | 
|  | 621 | }, | 
| Hans de Goede | 6a574b5 | 2011-06-04 15:39:21 +0200 | [diff] [blame] | 622 | { | 
| Joel Sass | 44306ab | 2012-01-10 13:03:55 -0500 | [diff] [blame] | 623 | .callback = intel_no_lvds_dmi_callback, | 
|  | 624 | .ident = "Clientron E830", | 
|  | 625 | .matches = { | 
|  | 626 | DMI_MATCH(DMI_SYS_VENDOR, "Clientron"), | 
|  | 627 | DMI_MATCH(DMI_PRODUCT_NAME, "E830"), | 
|  | 628 | }, | 
|  | 629 | }, | 
|  | 630 | { | 
| Hans de Goede | 6a574b5 | 2011-06-04 15:39:21 +0200 | [diff] [blame] | 631 | .callback = intel_no_lvds_dmi_callback, | 
|  | 632 | .ident = "Asus EeeBox PC EB1007", | 
|  | 633 | .matches = { | 
|  | 634 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), | 
|  | 635 | DMI_MATCH(DMI_PRODUCT_NAME, "EB1007"), | 
|  | 636 | }, | 
|  | 637 | }, | 
| Adam Jackson | 0999bbe | 2011-11-28 12:22:56 -0500 | [diff] [blame] | 638 | { | 
|  | 639 | .callback = intel_no_lvds_dmi_callback, | 
|  | 640 | .ident = "Asus AT5NM10T-I", | 
|  | 641 | .matches = { | 
|  | 642 | DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), | 
|  | 643 | DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"), | 
|  | 644 | }, | 
|  | 645 | }, | 
| Marc Gariepy | f5b8a7e | 2012-02-09 09:35:21 -0500 | [diff] [blame] | 646 | { | 
|  | 647 | .callback = intel_no_lvds_dmi_callback, | 
| Ben Mesman | 45a211d | 2013-04-16 20:00:28 +0200 | [diff] [blame] | 648 | .ident = "Hewlett-Packard HP t5740", | 
| Jan-Benedict Glaw | 3347111 | 2012-05-22 15:21:53 +0200 | [diff] [blame] | 649 | .matches = { | 
|  | 650 | DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), | 
| Ben Mesman | 45a211d | 2013-04-16 20:00:28 +0200 | [diff] [blame] | 651 | DMI_MATCH(DMI_PRODUCT_NAME, " t5740"), | 
| Jan-Benedict Glaw | 3347111 | 2012-05-22 15:21:53 +0200 | [diff] [blame] | 652 | }, | 
|  | 653 | }, | 
|  | 654 | { | 
|  | 655 | .callback = intel_no_lvds_dmi_callback, | 
| Marc Gariepy | f5b8a7e | 2012-02-09 09:35:21 -0500 | [diff] [blame] | 656 | .ident = "Hewlett-Packard t5745", | 
|  | 657 | .matches = { | 
|  | 658 | DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), | 
| Marc Gariepy | 6200497 | 2012-05-01 13:37:57 -0400 | [diff] [blame] | 659 | DMI_MATCH(DMI_PRODUCT_NAME, "hp t5745"), | 
| Marc Gariepy | f5b8a7e | 2012-02-09 09:35:21 -0500 | [diff] [blame] | 660 | }, | 
|  | 661 | }, | 
|  | 662 | { | 
|  | 663 | .callback = intel_no_lvds_dmi_callback, | 
|  | 664 | .ident = "Hewlett-Packard st5747", | 
|  | 665 | .matches = { | 
|  | 666 | DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), | 
| Marc Gariepy | 6200497 | 2012-05-01 13:37:57 -0400 | [diff] [blame] | 667 | DMI_MATCH(DMI_PRODUCT_NAME, "hp st5747"), | 
| Marc Gariepy | f5b8a7e | 2012-02-09 09:35:21 -0500 | [diff] [blame] | 668 | }, | 
|  | 669 | }, | 
| Anisse Astier | 97effad | 2012-03-07 18:36:35 +0100 | [diff] [blame] | 670 | { | 
|  | 671 | .callback = intel_no_lvds_dmi_callback, | 
|  | 672 | .ident = "MSI Wind Box DC500", | 
|  | 673 | .matches = { | 
|  | 674 | DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), | 
|  | 675 | DMI_MATCH(DMI_BOARD_NAME, "MS-7469"), | 
|  | 676 | }, | 
|  | 677 | }, | 
| Sjoerd Simons | 9756fe3 | 2012-06-22 09:43:07 +0200 | [diff] [blame] | 678 | { | 
|  | 679 | .callback = intel_no_lvds_dmi_callback, | 
| Calvin Walton | a51d4ed | 2012-08-24 07:56:31 -0400 | [diff] [blame] | 680 | .ident = "Gigabyte GA-D525TUD", | 
|  | 681 | .matches = { | 
|  | 682 | DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co., Ltd."), | 
|  | 683 | DMI_MATCH(DMI_BOARD_NAME, "D525TUD"), | 
|  | 684 | }, | 
|  | 685 | }, | 
| Chris Wilson | c31407a | 2012-10-18 21:07:01 +0100 | [diff] [blame] | 686 | { | 
|  | 687 | .callback = intel_no_lvds_dmi_callback, | 
|  | 688 | .ident = "Supermicro X7SPA-H", | 
|  | 689 | .matches = { | 
|  | 690 | DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"), | 
|  | 691 | DMI_MATCH(DMI_PRODUCT_NAME, "X7SPA-H"), | 
|  | 692 | }, | 
|  | 693 | }, | 
| Christian Lamparter | 9e9dd0e | 2013-04-03 14:34:11 +0200 | [diff] [blame] | 694 | { | 
|  | 695 | .callback = intel_no_lvds_dmi_callback, | 
|  | 696 | .ident = "Fujitsu Esprimo Q900", | 
|  | 697 | .matches = { | 
|  | 698 | DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), | 
|  | 699 | DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Q900"), | 
|  | 700 | }, | 
|  | 701 | }, | 
| Chris Wilson | e5614f0 | 2013-07-03 15:05:03 -0700 | [diff] [blame] | 702 | { | 
|  | 703 | .callback = intel_no_lvds_dmi_callback, | 
| Rob Pearce | 645378d | 2013-10-27 16:13:42 +0000 | [diff] [blame] | 704 | .ident = "Intel D410PT", | 
|  | 705 | .matches = { | 
|  | 706 | DMI_MATCH(DMI_BOARD_VENDOR, "Intel"), | 
|  | 707 | DMI_MATCH(DMI_BOARD_NAME, "D410PT"), | 
|  | 708 | }, | 
|  | 709 | }, | 
|  | 710 | { | 
|  | 711 | .callback = intel_no_lvds_dmi_callback, | 
|  | 712 | .ident = "Intel D425KT", | 
|  | 713 | .matches = { | 
|  | 714 | DMI_MATCH(DMI_BOARD_VENDOR, "Intel"), | 
|  | 715 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "D425KT"), | 
|  | 716 | }, | 
|  | 717 | }, | 
|  | 718 | { | 
|  | 719 | .callback = intel_no_lvds_dmi_callback, | 
| Chris Wilson | e5614f0 | 2013-07-03 15:05:03 -0700 | [diff] [blame] | 720 | .ident = "Intel D510MO", | 
|  | 721 | .matches = { | 
|  | 722 | DMI_MATCH(DMI_BOARD_VENDOR, "Intel"), | 
|  | 723 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "D510MO"), | 
|  | 724 | }, | 
|  | 725 | }, | 
| Jani Nikula | dcf6d29 | 2013-07-03 15:05:05 -0700 | [diff] [blame] | 726 | { | 
|  | 727 | .callback = intel_no_lvds_dmi_callback, | 
|  | 728 | .ident = "Intel D525MW", | 
|  | 729 | .matches = { | 
|  | 730 | DMI_MATCH(DMI_BOARD_VENDOR, "Intel"), | 
|  | 731 | DMI_EXACT_MATCH(DMI_BOARD_NAME, "D525MW"), | 
|  | 732 | }, | 
|  | 733 | }, | 
| Jarod Wilson | 425d244 | 2009-05-05 10:00:25 -0400 | [diff] [blame] | 734 |  | 
|  | 735 | { }	/* terminating entry */ | 
|  | 736 | }; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 737 |  | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 738 | /* | 
|  | 739 | * Enumerate the child dev array parsed from VBT to check whether | 
|  | 740 | * the LVDS is present. | 
|  | 741 | * If it is present, return 1. | 
|  | 742 | * If it is not present, return false. | 
|  | 743 | * If no child dev is parsed from VBT, it assumes that the LVDS is present. | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 744 | */ | 
| Chris Wilson | 270eea0 | 2010-09-24 01:15:02 +0100 | [diff] [blame] | 745 | static bool lvds_is_present_in_vbt(struct drm_device *dev, | 
|  | 746 | u8 *i2c_pin) | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 747 | { | 
|  | 748 | struct drm_i915_private *dev_priv = dev->dev_private; | 
| Chris Wilson | 425904d | 2010-08-22 18:21:42 +0100 | [diff] [blame] | 749 | int i; | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 750 |  | 
| Rodrigo Vivi | 41aa344 | 2013-05-09 20:03:18 -0300 | [diff] [blame] | 751 | if (!dev_priv->vbt.child_dev_num) | 
| Chris Wilson | 425904d | 2010-08-22 18:21:42 +0100 | [diff] [blame] | 752 | return true; | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 753 |  | 
| Rodrigo Vivi | 41aa344 | 2013-05-09 20:03:18 -0300 | [diff] [blame] | 754 | for (i = 0; i < dev_priv->vbt.child_dev_num; i++) { | 
| Paulo Zanoni | 768f69c | 2013-09-11 18:02:47 -0300 | [diff] [blame] | 755 | union child_device_config *uchild = dev_priv->vbt.child_dev + i; | 
|  | 756 | struct old_child_dev_config *child = &uchild->old; | 
| Chris Wilson | 425904d | 2010-08-22 18:21:42 +0100 | [diff] [blame] | 757 |  | 
|  | 758 | /* If the device type is not LFP, continue. | 
|  | 759 | * We have to check both the new identifiers as well as the | 
|  | 760 | * old for compatibility with some BIOSes. | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 761 | */ | 
| Chris Wilson | 425904d | 2010-08-22 18:21:42 +0100 | [diff] [blame] | 762 | if (child->device_type != DEVICE_TYPE_INT_LFP && | 
|  | 763 | child->device_type != DEVICE_TYPE_LFP) | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 764 | continue; | 
|  | 765 |  | 
| Daniel Kurtz | 3bd7d90 | 2012-03-28 02:36:14 +0800 | [diff] [blame] | 766 | if (intel_gmbus_is_port_valid(child->i2c_pin)) | 
|  | 767 | *i2c_pin = child->i2c_pin; | 
| Chris Wilson | 270eea0 | 2010-09-24 01:15:02 +0100 | [diff] [blame] | 768 |  | 
| Chris Wilson | 425904d | 2010-08-22 18:21:42 +0100 | [diff] [blame] | 769 | /* However, we cannot trust the BIOS writers to populate | 
|  | 770 | * the VBT correctly.  Since LVDS requires additional | 
|  | 771 | * information from AIM blocks, a non-zero addin offset is | 
|  | 772 | * a good indicator that the LVDS is actually present. | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 773 | */ | 
| Chris Wilson | 425904d | 2010-08-22 18:21:42 +0100 | [diff] [blame] | 774 | if (child->addin_offset) | 
|  | 775 | return true; | 
|  | 776 |  | 
|  | 777 | /* But even then some BIOS writers perform some black magic | 
|  | 778 | * and instantiate the device without reference to any | 
|  | 779 | * additional data.  Trust that if the VBT was written into | 
|  | 780 | * the OpRegion then they have validated the LVDS's existence. | 
|  | 781 | */ | 
|  | 782 | if (dev_priv->opregion.vbt) | 
|  | 783 | return true; | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 784 | } | 
| Chris Wilson | 425904d | 2010-08-22 18:21:42 +0100 | [diff] [blame] | 785 |  | 
|  | 786 | return false; | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 787 | } | 
|  | 788 |  | 
| Daniel Vetter | 1974cad | 2012-11-26 17:22:09 +0100 | [diff] [blame] | 789 | static int intel_dual_link_lvds_callback(const struct dmi_system_id *id) | 
|  | 790 | { | 
|  | 791 | DRM_INFO("Forcing lvds to dual link mode on %s\n", id->ident); | 
|  | 792 | return 1; | 
|  | 793 | } | 
|  | 794 |  | 
|  | 795 | static const struct dmi_system_id intel_dual_link_lvds[] = { | 
|  | 796 | { | 
|  | 797 | .callback = intel_dual_link_lvds_callback, | 
|  | 798 | .ident = "Apple MacBook Pro (Core i5/i7 Series)", | 
|  | 799 | .matches = { | 
|  | 800 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."), | 
|  | 801 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro8,2"), | 
|  | 802 | }, | 
|  | 803 | }, | 
|  | 804 | { }	/* terminating entry */ | 
|  | 805 | }; | 
|  | 806 |  | 
|  | 807 | bool intel_is_dual_link_lvds(struct drm_device *dev) | 
|  | 808 | { | 
| Daniel Vetter | 13c7d87 | 2012-11-26 17:22:10 +0100 | [diff] [blame] | 809 | struct intel_encoder *encoder; | 
|  | 810 | struct intel_lvds_encoder *lvds_encoder; | 
|  | 811 |  | 
|  | 812 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, | 
|  | 813 | base.head) { | 
|  | 814 | if (encoder->type == INTEL_OUTPUT_LVDS) { | 
|  | 815 | lvds_encoder = to_lvds_encoder(&encoder->base); | 
|  | 816 |  | 
|  | 817 | return lvds_encoder->is_dual_link; | 
|  | 818 | } | 
|  | 819 | } | 
|  | 820 |  | 
|  | 821 | return false; | 
|  | 822 | } | 
|  | 823 |  | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 824 | static bool compute_is_dual_link_lvds(struct intel_lvds_encoder *lvds_encoder) | 
| Daniel Vetter | 13c7d87 | 2012-11-26 17:22:10 +0100 | [diff] [blame] | 825 | { | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 826 | struct drm_device *dev = lvds_encoder->base.base.dev; | 
| Daniel Vetter | 1974cad | 2012-11-26 17:22:09 +0100 | [diff] [blame] | 827 | unsigned int val; | 
|  | 828 | struct drm_i915_private *dev_priv = dev->dev_private; | 
| Daniel Vetter | 1974cad | 2012-11-26 17:22:09 +0100 | [diff] [blame] | 829 |  | 
|  | 830 | /* use the module option value if specified */ | 
| Jani Nikula | d330a95 | 2014-01-21 11:24:25 +0200 | [diff] [blame] | 831 | if (i915.lvds_channel_mode > 0) | 
|  | 832 | return i915.lvds_channel_mode == 2; | 
| Daniel Vetter | 1974cad | 2012-11-26 17:22:09 +0100 | [diff] [blame] | 833 |  | 
|  | 834 | if (dmi_check_system(intel_dual_link_lvds)) | 
|  | 835 | return true; | 
|  | 836 |  | 
| Daniel Vetter | 13c7d87 | 2012-11-26 17:22:10 +0100 | [diff] [blame] | 837 | /* BIOS should set the proper LVDS register value at boot, but | 
|  | 838 | * in reality, it doesn't set the value when the lid is closed; | 
|  | 839 | * we need to check "the value to be set" in VBT when LVDS | 
|  | 840 | * register is uninitialized. | 
|  | 841 | */ | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 842 | val = I915_READ(lvds_encoder->reg); | 
| Daniel Vetter | 13c7d87 | 2012-11-26 17:22:10 +0100 | [diff] [blame] | 843 | if (!(val & ~(LVDS_PIPE_MASK | LVDS_DETECTED))) | 
| Rodrigo Vivi | 41aa344 | 2013-05-09 20:03:18 -0300 | [diff] [blame] | 844 | val = dev_priv->vbt.bios_lvds_val; | 
| Daniel Vetter | 13c7d87 | 2012-11-26 17:22:10 +0100 | [diff] [blame] | 845 |  | 
| Daniel Vetter | 1974cad | 2012-11-26 17:22:09 +0100 | [diff] [blame] | 846 | return (val & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP; | 
|  | 847 | } | 
|  | 848 |  | 
| Chris Wilson | f3cfcba | 2012-02-09 09:35:53 +0000 | [diff] [blame] | 849 | static bool intel_lvds_supported(struct drm_device *dev) | 
|  | 850 | { | 
|  | 851 | /* With the introduction of the PCH we gained a dedicated | 
|  | 852 | * LVDS presence pin, use it. */ | 
| Paulo Zanoni | 311e359 | 2013-03-06 20:03:19 -0300 | [diff] [blame] | 853 | if (HAS_PCH_IBX(dev) || HAS_PCH_CPT(dev)) | 
| Chris Wilson | f3cfcba | 2012-02-09 09:35:53 +0000 | [diff] [blame] | 854 | return true; | 
|  | 855 |  | 
|  | 856 | /* Otherwise LVDS was only attached to mobile products, | 
|  | 857 | * except for the inglorious 830gm */ | 
| Paulo Zanoni | 311e359 | 2013-03-06 20:03:19 -0300 | [diff] [blame] | 858 | if (INTEL_INFO(dev)->gen <= 4 && IS_MOBILE(dev) && !IS_I830(dev)) | 
|  | 859 | return true; | 
|  | 860 |  | 
|  | 861 | return false; | 
| Chris Wilson | f3cfcba | 2012-02-09 09:35:53 +0000 | [diff] [blame] | 862 | } | 
|  | 863 |  | 
| Zhao Yakui | 18f9ed1 | 2009-11-20 03:24:16 +0000 | [diff] [blame] | 864 | /** | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 865 | * intel_lvds_init - setup LVDS connectors on this device | 
|  | 866 | * @dev: drm device | 
|  | 867 | * | 
|  | 868 | * Create the connector, register the LVDS DDC bus, and try to figure out what | 
|  | 869 | * modes we can display on the LVDS panel (if present). | 
|  | 870 | */ | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 871 | void intel_lvds_init(struct drm_device *dev) | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 872 | { | 
|  | 873 | struct drm_i915_private *dev_priv = dev->dev_private; | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 874 | struct intel_lvds_encoder *lvds_encoder; | 
| Eric Anholt | 21d40d3 | 2010-03-25 11:11:14 -0700 | [diff] [blame] | 875 | struct intel_encoder *intel_encoder; | 
| Jani Nikula | c7362c4 | 2012-10-19 14:51:44 +0300 | [diff] [blame] | 876 | struct intel_lvds_connector *lvds_connector; | 
| Zhenyu Wang | bb8a356 | 2010-03-29 16:40:50 +0800 | [diff] [blame] | 877 | struct intel_connector *intel_connector; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 878 | struct drm_connector *connector; | 
|  | 879 | struct drm_encoder *encoder; | 
|  | 880 | struct drm_display_mode *scan; /* *modes, *bios_mode; */ | 
| Jani Nikula | dd06f90 | 2012-10-19 14:51:50 +0300 | [diff] [blame] | 881 | struct drm_display_mode *fixed_mode = NULL; | 
| Vandana Kannan | 4b6ed68 | 2014-02-11 14:26:36 +0530 | [diff] [blame] | 882 | struct drm_display_mode *downclock_mode = NULL; | 
| Jani Nikula | 9cd300e | 2012-10-19 14:51:52 +0300 | [diff] [blame] | 883 | struct edid *edid; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 884 | struct drm_crtc *crtc; | 
|  | 885 | u32 lvds; | 
| Chris Wilson | 270eea0 | 2010-09-24 01:15:02 +0100 | [diff] [blame] | 886 | int pipe; | 
|  | 887 | u8 pin; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 888 |  | 
| Chris Wilson | f3cfcba | 2012-02-09 09:35:53 +0000 | [diff] [blame] | 889 | if (!intel_lvds_supported(dev)) | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 890 | return; | 
| Chris Wilson | f3cfcba | 2012-02-09 09:35:53 +0000 | [diff] [blame] | 891 |  | 
| Jarod Wilson | 425d244 | 2009-05-05 10:00:25 -0400 | [diff] [blame] | 892 | /* Skip init on machines we know falsely report LVDS */ | 
|  | 893 | if (dmi_check_system(intel_no_lvds)) | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 894 | return; | 
| Paul Collins | 565dcd4 | 2009-02-04 23:05:41 +1300 | [diff] [blame] | 895 |  | 
| Chris Wilson | 270eea0 | 2010-09-24 01:15:02 +0100 | [diff] [blame] | 896 | pin = GMBUS_PORT_PANEL; | 
|  | 897 | if (!lvds_is_present_in_vbt(dev, &pin)) { | 
| Matthew Garrett | 11ba159 | 2009-12-15 13:55:24 -0500 | [diff] [blame] | 898 | DRM_DEBUG_KMS("LVDS is not present in VBT\n"); | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 899 | return; | 
| Zhao Yakui | 7cf4f69 | 2009-11-24 09:48:47 +0800 | [diff] [blame] | 900 | } | 
| Zhao Yakui | e99da35 | 2009-06-26 09:46:18 +0800 | [diff] [blame] | 901 |  | 
| Eric Anholt | c619eed | 2010-01-28 16:45:52 -0800 | [diff] [blame] | 902 | if (HAS_PCH_SPLIT(dev)) { | 
| Zhenyu Wang | 541998a | 2009-06-05 15:38:44 +0800 | [diff] [blame] | 903 | if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 904 | return; | 
| Rodrigo Vivi | 41aa344 | 2013-05-09 20:03:18 -0300 | [diff] [blame] | 905 | if (dev_priv->vbt.edp_support) { | 
| Zhao Yakui | 28c9773 | 2009-10-09 11:39:41 +0800 | [diff] [blame] | 906 | DRM_DEBUG_KMS("disable LVDS for eDP support\n"); | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 907 | return; | 
| Zhenyu Wang | 32f9d65 | 2009-07-24 01:00:32 +0800 | [diff] [blame] | 908 | } | 
| Zhenyu Wang | 541998a | 2009-06-05 15:38:44 +0800 | [diff] [blame] | 909 | } | 
|  | 910 |  | 
| Daniel Vetter | b14c567 | 2013-09-19 12:18:32 +0200 | [diff] [blame] | 911 | lvds_encoder = kzalloc(sizeof(*lvds_encoder), GFP_KERNEL); | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 912 | if (!lvds_encoder) | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 913 | return; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 914 |  | 
| Daniel Vetter | b14c567 | 2013-09-19 12:18:32 +0200 | [diff] [blame] | 915 | lvds_connector = kzalloc(sizeof(*lvds_connector), GFP_KERNEL); | 
| Jani Nikula | c7362c4 | 2012-10-19 14:51:44 +0300 | [diff] [blame] | 916 | if (!lvds_connector) { | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 917 | kfree(lvds_encoder); | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 918 | return; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 919 | } | 
|  | 920 |  | 
| Jani Nikula | 62165e0 | 2012-10-19 14:51:47 +0300 | [diff] [blame] | 921 | lvds_encoder->attached_connector = lvds_connector; | 
| Zhenyu Wang | bb8a356 | 2010-03-29 16:40:50 +0800 | [diff] [blame] | 922 |  | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 923 | intel_encoder = &lvds_encoder->base; | 
| Chris Wilson | 4ef69c7 | 2010-09-09 15:14:28 +0100 | [diff] [blame] | 924 | encoder = &intel_encoder->base; | 
| Jani Nikula | c7362c4 | 2012-10-19 14:51:44 +0300 | [diff] [blame] | 925 | intel_connector = &lvds_connector->base; | 
| Chris Wilson | ea5b213 | 2010-08-04 13:50:23 +0100 | [diff] [blame] | 926 | connector = &intel_connector->base; | 
| Zhenyu Wang | bb8a356 | 2010-03-29 16:40:50 +0800 | [diff] [blame] | 927 | drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs, | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 928 | DRM_MODE_CONNECTOR_LVDS); | 
|  | 929 |  | 
| Chris Wilson | 4ef69c7 | 2010-09-09 15:14:28 +0100 | [diff] [blame] | 930 | drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs, | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 931 | DRM_MODE_ENCODER_LVDS); | 
|  | 932 |  | 
| Daniel Vetter | c22834e | 2012-06-30 15:31:28 +0200 | [diff] [blame] | 933 | intel_encoder->enable = intel_enable_lvds; | 
| Daniel Vetter | f6736a1 | 2013-06-05 13:34:30 +0200 | [diff] [blame] | 934 | intel_encoder->pre_enable = intel_pre_enable_lvds; | 
| Daniel Vetter | 7ae8923 | 2013-03-27 00:44:52 +0100 | [diff] [blame] | 935 | intel_encoder->compute_config = intel_lvds_compute_config; | 
| Daniel Vetter | c22834e | 2012-06-30 15:31:28 +0200 | [diff] [blame] | 936 | intel_encoder->disable = intel_disable_lvds; | 
| Daniel Vetter | b1dc332 | 2012-07-02 21:09:00 +0200 | [diff] [blame] | 937 | intel_encoder->get_hw_state = intel_lvds_get_hw_state; | 
| Jesse Barnes | 045ac3b | 2013-05-14 17:08:26 -0700 | [diff] [blame] | 938 | intel_encoder->get_config = intel_lvds_get_config; | 
| Daniel Vetter | b1dc332 | 2012-07-02 21:09:00 +0200 | [diff] [blame] | 939 | intel_connector->get_hw_state = intel_connector_get_hw_state; | 
| Imre Deak | 4932e2c | 2014-02-11 17:12:48 +0200 | [diff] [blame] | 940 | intel_connector->unregister = intel_connector_unregister; | 
| Daniel Vetter | c22834e | 2012-06-30 15:31:28 +0200 | [diff] [blame] | 941 |  | 
| Chris Wilson | df0e924 | 2010-09-09 16:20:55 +0100 | [diff] [blame] | 942 | intel_connector_attach_encoder(intel_connector, intel_encoder); | 
| Eric Anholt | 21d40d3 | 2010-03-25 11:11:14 -0700 | [diff] [blame] | 943 | intel_encoder->type = INTEL_OUTPUT_LVDS; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 944 |  | 
| Ville Syrjälä | bc079e8 | 2014-03-03 16:15:28 +0200 | [diff] [blame] | 945 | intel_encoder->cloneable = 0; | 
| Jesse Barnes | 27f8227 | 2011-09-02 12:54:37 -0700 | [diff] [blame] | 946 | if (HAS_PCH_SPLIT(dev)) | 
|  | 947 | intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); | 
| Daniel Vetter | 0b9f43a | 2012-06-05 10:07:11 +0200 | [diff] [blame] | 948 | else if (IS_GEN4(dev)) | 
|  | 949 | intel_encoder->crtc_mask = (1 << 0) | (1 << 1); | 
| Jesse Barnes | 27f8227 | 2011-09-02 12:54:37 -0700 | [diff] [blame] | 950 | else | 
|  | 951 | intel_encoder->crtc_mask = (1 << 1); | 
|  | 952 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 953 | drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs); | 
|  | 954 | connector->display_info.subpixel_order = SubPixelHorizontalRGB; | 
|  | 955 | connector->interlace_allowed = false; | 
|  | 956 | connector->doublescan_allowed = false; | 
|  | 957 |  | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 958 | if (HAS_PCH_SPLIT(dev)) { | 
|  | 959 | lvds_encoder->reg = PCH_LVDS; | 
|  | 960 | } else { | 
|  | 961 | lvds_encoder->reg = LVDS; | 
|  | 962 | } | 
|  | 963 |  | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 964 | /* create the scaling mode property */ | 
|  | 965 | drm_mode_create_scaling_mode_property(dev); | 
| Rob Clark | 662595d | 2012-10-11 20:36:04 -0500 | [diff] [blame] | 966 | drm_object_attach_property(&connector->base, | 
| Zhao Yakui | 3fbe18d | 2009-06-22 15:31:25 +0800 | [diff] [blame] | 967 | dev->mode_config.scaling_mode_property, | 
| Jesse Barnes | dd1ea37 | 2010-06-24 11:05:10 -0700 | [diff] [blame] | 968 | DRM_MODE_SCALE_ASPECT); | 
| Jani Nikula | 4d89152 | 2012-10-26 12:03:59 +0300 | [diff] [blame] | 969 | intel_connector->panel.fitting_mode = DRM_MODE_SCALE_ASPECT; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 970 | /* | 
|  | 971 | * LVDS discovery: | 
|  | 972 | * 1) check for EDID on DDC | 
|  | 973 | * 2) check for VBT data | 
|  | 974 | * 3) check to see if LVDS is already on | 
|  | 975 | *    if none of the above, no panel | 
|  | 976 | * 4) make sure lid is open | 
|  | 977 | *    if closed, act like it's not there for now | 
|  | 978 | */ | 
|  | 979 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 980 | /* | 
|  | 981 | * Attempt to get the fixed panel mode from DDC.  Assume that the | 
|  | 982 | * preferred mode is the right one. | 
|  | 983 | */ | 
| Daniel Vetter | 4da9854 | 2014-03-21 23:22:35 +0100 | [diff] [blame] | 984 | mutex_lock(&dev->mode_config.mutex); | 
| Jani Nikula | 9cd300e | 2012-10-19 14:51:52 +0300 | [diff] [blame] | 985 | edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, pin)); | 
|  | 986 | if (edid) { | 
|  | 987 | if (drm_add_edid_modes(connector, edid)) { | 
| Chris Wilson | 3f8ff0e | 2010-11-08 23:20:52 +0000 | [diff] [blame] | 988 | drm_mode_connector_update_edid_property(connector, | 
| Jani Nikula | 9cd300e | 2012-10-19 14:51:52 +0300 | [diff] [blame] | 989 | edid); | 
| Chris Wilson | 3f8ff0e | 2010-11-08 23:20:52 +0000 | [diff] [blame] | 990 | } else { | 
| Jani Nikula | 9cd300e | 2012-10-19 14:51:52 +0300 | [diff] [blame] | 991 | kfree(edid); | 
|  | 992 | edid = ERR_PTR(-EINVAL); | 
| Chris Wilson | 3f8ff0e | 2010-11-08 23:20:52 +0000 | [diff] [blame] | 993 | } | 
| Jani Nikula | 9cd300e | 2012-10-19 14:51:52 +0300 | [diff] [blame] | 994 | } else { | 
|  | 995 | edid = ERR_PTR(-ENOENT); | 
| Chris Wilson | 3f8ff0e | 2010-11-08 23:20:52 +0000 | [diff] [blame] | 996 | } | 
| Jani Nikula | 9cd300e | 2012-10-19 14:51:52 +0300 | [diff] [blame] | 997 | lvds_connector->base.edid = edid; | 
|  | 998 |  | 
|  | 999 | if (IS_ERR_OR_NULL(edid)) { | 
| Chris Wilson | 788319d | 2010-09-12 17:34:41 +0100 | [diff] [blame] | 1000 | /* Didn't get an EDID, so | 
|  | 1001 | * Set wide sync ranges so we get all modes | 
|  | 1002 | * handed to valid_mode for checking | 
|  | 1003 | */ | 
|  | 1004 | connector->display_info.min_vfreq = 0; | 
|  | 1005 | connector->display_info.max_vfreq = 200; | 
|  | 1006 | connector->display_info.min_hfreq = 0; | 
|  | 1007 | connector->display_info.max_hfreq = 200; | 
|  | 1008 | } | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1009 |  | 
|  | 1010 | list_for_each_entry(scan, &connector->probed_modes, head) { | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1011 | if (scan->type & DRM_MODE_TYPE_PREFERRED) { | 
| Chris Wilson | 6a9d51b | 2012-11-21 16:14:03 +0000 | [diff] [blame] | 1012 | DRM_DEBUG_KMS("using preferred mode from EDID: "); | 
|  | 1013 | drm_mode_debug_printmodeline(scan); | 
|  | 1014 |  | 
| Jani Nikula | dd06f90 | 2012-10-19 14:51:50 +0300 | [diff] [blame] | 1015 | fixed_mode = drm_mode_duplicate(dev, scan); | 
| Chris Wilson | 6a9d51b | 2012-11-21 16:14:03 +0000 | [diff] [blame] | 1016 | if (fixed_mode) { | 
| Vandana Kannan | 4b6ed68 | 2014-02-11 14:26:36 +0530 | [diff] [blame] | 1017 | downclock_mode = | 
| Vandana Kannan | ec9ed19 | 2013-12-10 13:37:36 +0530 | [diff] [blame] | 1018 | intel_find_panel_downclock(dev, | 
|  | 1019 | fixed_mode, connector); | 
| Vandana Kannan | 4b6ed68 | 2014-02-11 14:26:36 +0530 | [diff] [blame] | 1020 | if (downclock_mode != NULL && | 
|  | 1021 | i915.lvds_downclock) { | 
| Vandana Kannan | ec9ed19 | 2013-12-10 13:37:36 +0530 | [diff] [blame] | 1022 | /* We found the downclock for LVDS. */ | 
|  | 1023 | dev_priv->lvds_downclock_avail = true; | 
|  | 1024 | dev_priv->lvds_downclock = | 
| Vandana Kannan | ec9ed19 | 2013-12-10 13:37:36 +0530 | [diff] [blame] | 1025 | downclock_mode->clock; | 
|  | 1026 | DRM_DEBUG_KMS("LVDS downclock is found" | 
|  | 1027 | " in EDID. Normal clock %dKhz, " | 
|  | 1028 | "downclock %dKhz\n", | 
|  | 1029 | fixed_mode->clock, | 
|  | 1030 | dev_priv->lvds_downclock); | 
|  | 1031 | } | 
| Chris Wilson | 6a9d51b | 2012-11-21 16:14:03 +0000 | [diff] [blame] | 1032 | goto out; | 
|  | 1033 | } | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1034 | } | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1035 | } | 
|  | 1036 |  | 
|  | 1037 | /* Failed to get EDID, what about VBT? */ | 
| Rodrigo Vivi | 41aa344 | 2013-05-09 20:03:18 -0300 | [diff] [blame] | 1038 | if (dev_priv->vbt.lfp_lvds_vbt_mode) { | 
| Chris Wilson | 6a9d51b | 2012-11-21 16:14:03 +0000 | [diff] [blame] | 1039 | DRM_DEBUG_KMS("using mode from VBT: "); | 
| Rodrigo Vivi | 41aa344 | 2013-05-09 20:03:18 -0300 | [diff] [blame] | 1040 | drm_mode_debug_printmodeline(dev_priv->vbt.lfp_lvds_vbt_mode); | 
| Chris Wilson | 6a9d51b | 2012-11-21 16:14:03 +0000 | [diff] [blame] | 1041 |  | 
| Rodrigo Vivi | 41aa344 | 2013-05-09 20:03:18 -0300 | [diff] [blame] | 1042 | fixed_mode = drm_mode_duplicate(dev, dev_priv->vbt.lfp_lvds_vbt_mode); | 
| Jani Nikula | dd06f90 | 2012-10-19 14:51:50 +0300 | [diff] [blame] | 1043 | if (fixed_mode) { | 
|  | 1044 | fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; | 
| Jesse Barnes | e285f3cd | 2009-01-14 10:53:36 -0800 | [diff] [blame] | 1045 | goto out; | 
|  | 1046 | } | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1047 | } | 
|  | 1048 |  | 
|  | 1049 | /* | 
|  | 1050 | * If we didn't get EDID, try checking if the panel is already turned | 
|  | 1051 | * on.  If so, assume that whatever is currently programmed is the | 
|  | 1052 | * correct mode. | 
|  | 1053 | */ | 
| Zhenyu Wang | 541998a | 2009-06-05 15:38:44 +0800 | [diff] [blame] | 1054 |  | 
| Adam Jackson | f2b115e | 2009-12-03 17:14:42 -0500 | [diff] [blame] | 1055 | /* Ironlake: FIXME if still fail, not try pipe mode now */ | 
| Eric Anholt | c619eed | 2010-01-28 16:45:52 -0800 | [diff] [blame] | 1056 | if (HAS_PCH_SPLIT(dev)) | 
| Zhenyu Wang | 541998a | 2009-06-05 15:38:44 +0800 | [diff] [blame] | 1057 | goto failed; | 
|  | 1058 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1059 | lvds = I915_READ(LVDS); | 
|  | 1060 | pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; | 
| Chris Wilson | f875c15 | 2010-09-09 15:44:14 +0100 | [diff] [blame] | 1061 | crtc = intel_get_crtc_for_pipe(dev, pipe); | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1062 |  | 
|  | 1063 | if (crtc && (lvds & LVDS_PORT_EN)) { | 
| Jani Nikula | dd06f90 | 2012-10-19 14:51:50 +0300 | [diff] [blame] | 1064 | fixed_mode = intel_crtc_mode_get(dev, crtc); | 
|  | 1065 | if (fixed_mode) { | 
| Chris Wilson | 6a9d51b | 2012-11-21 16:14:03 +0000 | [diff] [blame] | 1066 | DRM_DEBUG_KMS("using current (BIOS) mode: "); | 
|  | 1067 | drm_mode_debug_printmodeline(fixed_mode); | 
| Jani Nikula | dd06f90 | 2012-10-19 14:51:50 +0300 | [diff] [blame] | 1068 | fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; | 
| Paul Collins | 565dcd4 | 2009-02-04 23:05:41 +1300 | [diff] [blame] | 1069 | goto out; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1070 | } | 
|  | 1071 | } | 
|  | 1072 |  | 
|  | 1073 | /* If we still don't have a mode after all that, give up. */ | 
| Jani Nikula | dd06f90 | 2012-10-19 14:51:50 +0300 | [diff] [blame] | 1074 | if (!fixed_mode) | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1075 | goto failed; | 
|  | 1076 |  | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1077 | out: | 
| Daniel Vetter | 4da9854 | 2014-03-21 23:22:35 +0100 | [diff] [blame] | 1078 | mutex_unlock(&dev->mode_config.mutex); | 
|  | 1079 |  | 
| Daniel Vetter | 7dec060 | 2012-09-11 14:12:25 +0200 | [diff] [blame] | 1080 | lvds_encoder->is_dual_link = compute_is_dual_link_lvds(lvds_encoder); | 
| Daniel Vetter | 13c7d87 | 2012-11-26 17:22:10 +0100 | [diff] [blame] | 1081 | DRM_DEBUG_KMS("detected %s-link lvds configuration\n", | 
|  | 1082 | lvds_encoder->is_dual_link ? "dual" : "single"); | 
|  | 1083 |  | 
| Daniel Vetter | 24ded20 | 2012-06-05 12:14:54 +0200 | [diff] [blame] | 1084 | /* | 
|  | 1085 | * Unlock registers and just | 
|  | 1086 | * leave them unlocked | 
|  | 1087 | */ | 
| Eric Anholt | c619eed | 2010-01-28 16:45:52 -0800 | [diff] [blame] | 1088 | if (HAS_PCH_SPLIT(dev)) { | 
| Keith Packard | ed10fca | 2011-08-06 10:33:12 -0700 | [diff] [blame] | 1089 | I915_WRITE(PCH_PP_CONTROL, | 
|  | 1090 | I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); | 
|  | 1091 | } else { | 
| Keith Packard | ed10fca | 2011-08-06 10:33:12 -0700 | [diff] [blame] | 1092 | I915_WRITE(PP_CONTROL, | 
|  | 1093 | I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); | 
| Zhenyu Wang | 541998a | 2009-06-05 15:38:44 +0800 | [diff] [blame] | 1094 | } | 
| Jani Nikula | db1740a | 2012-10-19 14:51:45 +0300 | [diff] [blame] | 1095 | lvds_connector->lid_notifier.notifier_call = intel_lid_notify; | 
|  | 1096 | if (acpi_lid_notifier_register(&lvds_connector->lid_notifier)) { | 
| Zhao Yakui | 28c9773 | 2009-10-09 11:39:41 +0800 | [diff] [blame] | 1097 | DRM_DEBUG_KMS("lid notifier registration failed\n"); | 
| Jani Nikula | db1740a | 2012-10-19 14:51:45 +0300 | [diff] [blame] | 1098 | lvds_connector->lid_notifier.notifier_call = NULL; | 
| Jesse Barnes | c1c7af6 | 2009-09-10 15:28:03 -0700 | [diff] [blame] | 1099 | } | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1100 | drm_sysfs_connector_add(connector); | 
| Matthew Garrett | aaa6fd2 | 2011-08-12 12:11:33 +0200 | [diff] [blame] | 1101 |  | 
| Vandana Kannan | 4b6ed68 | 2014-02-11 14:26:36 +0530 | [diff] [blame] | 1102 | intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode); | 
| Jani Nikula | 0657b6b | 2012-10-19 14:51:46 +0300 | [diff] [blame] | 1103 | intel_panel_setup_backlight(connector); | 
| Matthew Garrett | aaa6fd2 | 2011-08-12 12:11:33 +0200 | [diff] [blame] | 1104 |  | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 1105 | return; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1106 |  | 
|  | 1107 | failed: | 
| Daniel Vetter | 4da9854 | 2014-03-21 23:22:35 +0100 | [diff] [blame] | 1108 | mutex_unlock(&dev->mode_config.mutex); | 
|  | 1109 |  | 
| Zhao Yakui | 8a4c47f | 2009-07-20 13:48:04 +0800 | [diff] [blame] | 1110 | DRM_DEBUG_KMS("No LVDS modes found, disabling.\n"); | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1111 | drm_connector_cleanup(connector); | 
| Shaohua Li | 1991bdf | 2009-11-17 17:19:23 +0800 | [diff] [blame] | 1112 | drm_encoder_cleanup(encoder); | 
| Jani Nikula | 29b99b4 | 2012-10-19 14:51:43 +0300 | [diff] [blame] | 1113 | kfree(lvds_encoder); | 
| Jani Nikula | c7362c4 | 2012-10-19 14:51:44 +0300 | [diff] [blame] | 1114 | kfree(lvds_connector); | 
| Daniel Vetter | c909335 | 2013-06-06 22:22:47 +0200 | [diff] [blame] | 1115 | return; | 
| Jesse Barnes | 79e5394 | 2008-11-07 14:24:08 -0800 | [diff] [blame] | 1116 | } |