1*e3adcf8fSFrançois Tigeot /* 2*e3adcf8fSFrançois Tigeot * Copyright © 2006-2007 Intel Corporation 3*e3adcf8fSFrançois Tigeot * Copyright (c) 2006 Dave Airlie <airlied@linux.ie> 4*e3adcf8fSFrançois Tigeot * 5*e3adcf8fSFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a 6*e3adcf8fSFrançois Tigeot * copy of this software and associated documentation files (the "Software"), 7*e3adcf8fSFrançois Tigeot * to deal in the Software without restriction, including without limitation 8*e3adcf8fSFrançois Tigeot * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9*e3adcf8fSFrançois Tigeot * and/or sell copies of the Software, and to permit persons to whom the 10*e3adcf8fSFrançois Tigeot * Software is furnished to do so, subject to the following conditions: 11*e3adcf8fSFrançois Tigeot * 12*e3adcf8fSFrançois Tigeot * The above copyright notice and this permission notice (including the next 13*e3adcf8fSFrançois Tigeot * paragraph) shall be included in all copies or substantial portions of the 14*e3adcf8fSFrançois Tigeot * Software. 15*e3adcf8fSFrançois Tigeot * 16*e3adcf8fSFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17*e3adcf8fSFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18*e3adcf8fSFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19*e3adcf8fSFrançois Tigeot * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20*e3adcf8fSFrançois Tigeot * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21*e3adcf8fSFrançois Tigeot * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22*e3adcf8fSFrançois Tigeot * DEALINGS IN THE SOFTWARE. 23*e3adcf8fSFrançois Tigeot * 24*e3adcf8fSFrançois Tigeot * Authors: 25*e3adcf8fSFrançois Tigeot * Eric Anholt <eric@anholt.net> 26*e3adcf8fSFrançois Tigeot * Dave Airlie <airlied@linux.ie> 27*e3adcf8fSFrançois Tigeot * Jesse Barnes <jesse.barnes@intel.com> 28*e3adcf8fSFrançois Tigeot * $FreeBSD: src/sys/dev/drm2/i915/intel_lvds.c,v 1.1 2012/05/22 11:07:44 kib Exp $ 29*e3adcf8fSFrançois Tigeot */ 30*e3adcf8fSFrançois Tigeot 31*e3adcf8fSFrançois Tigeot #include <dev/drm/drmP.h> 32*e3adcf8fSFrançois Tigeot #include <dev/drm/drm.h> 33*e3adcf8fSFrançois Tigeot #include <dev/drm/drm_crtc.h> 34*e3adcf8fSFrançois Tigeot #include <dev/drm/drm_edid.h> 35*e3adcf8fSFrançois Tigeot #include "i915_drm.h" 36*e3adcf8fSFrançois Tigeot #include "i915_drv.h" 37*e3adcf8fSFrançois Tigeot #include "intel_drv.h" 38*e3adcf8fSFrançois Tigeot 39*e3adcf8fSFrançois Tigeot /* Private structure for the integrated LVDS support */ 40*e3adcf8fSFrançois Tigeot struct intel_lvds { 41*e3adcf8fSFrançois Tigeot struct intel_encoder base; 42*e3adcf8fSFrançois Tigeot 43*e3adcf8fSFrançois Tigeot struct edid *edid; 44*e3adcf8fSFrançois Tigeot 45*e3adcf8fSFrançois Tigeot int fitting_mode; 46*e3adcf8fSFrançois Tigeot u32 pfit_control; 47*e3adcf8fSFrançois Tigeot u32 pfit_pgm_ratios; 48*e3adcf8fSFrançois Tigeot bool pfit_dirty; 49*e3adcf8fSFrançois Tigeot 50*e3adcf8fSFrançois Tigeot struct drm_display_mode *fixed_mode; 51*e3adcf8fSFrançois Tigeot }; 52*e3adcf8fSFrançois Tigeot 53*e3adcf8fSFrançois Tigeot static struct intel_lvds *to_intel_lvds(struct drm_encoder *encoder) 54*e3adcf8fSFrançois Tigeot { 55*e3adcf8fSFrançois Tigeot return container_of(encoder, struct intel_lvds, base.base); 56*e3adcf8fSFrançois Tigeot } 57*e3adcf8fSFrançois Tigeot 58*e3adcf8fSFrançois Tigeot static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector) 59*e3adcf8fSFrançois Tigeot { 60*e3adcf8fSFrançois Tigeot return container_of(intel_attached_encoder(connector), 61*e3adcf8fSFrançois Tigeot struct intel_lvds, base); 62*e3adcf8fSFrançois Tigeot } 63*e3adcf8fSFrançois Tigeot 64*e3adcf8fSFrançois Tigeot /** 65*e3adcf8fSFrançois Tigeot * Sets the power state for the panel. 66*e3adcf8fSFrançois Tigeot */ 67*e3adcf8fSFrançois Tigeot static void intel_lvds_enable(struct intel_lvds *intel_lvds) 68*e3adcf8fSFrançois Tigeot { 69*e3adcf8fSFrançois Tigeot struct drm_device *dev = intel_lvds->base.base.dev; 70*e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 71*e3adcf8fSFrançois Tigeot u32 ctl_reg, lvds_reg, stat_reg; 72*e3adcf8fSFrançois Tigeot 73*e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 74*e3adcf8fSFrançois Tigeot ctl_reg = PCH_PP_CONTROL; 75*e3adcf8fSFrançois Tigeot lvds_reg = PCH_LVDS; 76*e3adcf8fSFrançois Tigeot stat_reg = PCH_PP_STATUS; 77*e3adcf8fSFrançois Tigeot } else { 78*e3adcf8fSFrançois Tigeot ctl_reg = PP_CONTROL; 79*e3adcf8fSFrançois Tigeot lvds_reg = LVDS; 80*e3adcf8fSFrançois Tigeot stat_reg = PP_STATUS; 81*e3adcf8fSFrançois Tigeot } 82*e3adcf8fSFrançois Tigeot 83*e3adcf8fSFrançois Tigeot I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN); 84*e3adcf8fSFrançois Tigeot 85*e3adcf8fSFrançois Tigeot if (intel_lvds->pfit_dirty) { 86*e3adcf8fSFrançois Tigeot /* 87*e3adcf8fSFrançois Tigeot * Enable automatic panel scaling so that non-native modes 88*e3adcf8fSFrançois Tigeot * fill the screen. The panel fitter should only be 89*e3adcf8fSFrançois Tigeot * adjusted whilst the pipe is disabled, according to 90*e3adcf8fSFrançois Tigeot * register description and PRM. 91*e3adcf8fSFrançois Tigeot */ 92*e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n", 93*e3adcf8fSFrançois Tigeot intel_lvds->pfit_control, 94*e3adcf8fSFrançois Tigeot intel_lvds->pfit_pgm_ratios); 95*e3adcf8fSFrançois Tigeot 96*e3adcf8fSFrançois Tigeot I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios); 97*e3adcf8fSFrançois Tigeot I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control); 98*e3adcf8fSFrançois Tigeot intel_lvds->pfit_dirty = false; 99*e3adcf8fSFrançois Tigeot } 100*e3adcf8fSFrançois Tigeot 101*e3adcf8fSFrançois Tigeot I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON); 102*e3adcf8fSFrançois Tigeot POSTING_READ(lvds_reg); 103*e3adcf8fSFrançois Tigeot if (_intel_wait_for(dev, 104*e3adcf8fSFrançois Tigeot (I915_READ(stat_reg) & PP_ON) == 0, 1000, 105*e3adcf8fSFrançois Tigeot 1, "915lvds")) 106*e3adcf8fSFrançois Tigeot DRM_ERROR("timed out waiting for panel to power off\n"); 107*e3adcf8fSFrançois Tigeot 108*e3adcf8fSFrançois Tigeot intel_panel_enable_backlight(dev); 109*e3adcf8fSFrançois Tigeot } 110*e3adcf8fSFrançois Tigeot 111*e3adcf8fSFrançois Tigeot static void intel_lvds_disable(struct intel_lvds *intel_lvds) 112*e3adcf8fSFrançois Tigeot { 113*e3adcf8fSFrançois Tigeot struct drm_device *dev = intel_lvds->base.base.dev; 114*e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 115*e3adcf8fSFrançois Tigeot u32 ctl_reg, lvds_reg, stat_reg; 116*e3adcf8fSFrançois Tigeot 117*e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 118*e3adcf8fSFrançois Tigeot ctl_reg = PCH_PP_CONTROL; 119*e3adcf8fSFrançois Tigeot lvds_reg = PCH_LVDS; 120*e3adcf8fSFrançois Tigeot stat_reg = PCH_PP_STATUS; 121*e3adcf8fSFrançois Tigeot } else { 122*e3adcf8fSFrançois Tigeot ctl_reg = PP_CONTROL; 123*e3adcf8fSFrançois Tigeot lvds_reg = LVDS; 124*e3adcf8fSFrançois Tigeot stat_reg = PP_STATUS; 125*e3adcf8fSFrançois Tigeot } 126*e3adcf8fSFrançois Tigeot 127*e3adcf8fSFrançois Tigeot intel_panel_disable_backlight(dev); 128*e3adcf8fSFrançois Tigeot 129*e3adcf8fSFrançois Tigeot I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON); 130*e3adcf8fSFrançois Tigeot if (_intel_wait_for(dev, 131*e3adcf8fSFrançois Tigeot (I915_READ(stat_reg) & PP_ON) == 0, 1000, 132*e3adcf8fSFrançois Tigeot 1, "915lvo")) 133*e3adcf8fSFrançois Tigeot DRM_ERROR("timed out waiting for panel to power off\n"); 134*e3adcf8fSFrançois Tigeot 135*e3adcf8fSFrançois Tigeot if (intel_lvds->pfit_control) { 136*e3adcf8fSFrançois Tigeot I915_WRITE(PFIT_CONTROL, 0); 137*e3adcf8fSFrançois Tigeot intel_lvds->pfit_dirty = true; 138*e3adcf8fSFrançois Tigeot } 139*e3adcf8fSFrançois Tigeot 140*e3adcf8fSFrançois Tigeot I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN); 141*e3adcf8fSFrançois Tigeot POSTING_READ(lvds_reg); 142*e3adcf8fSFrançois Tigeot } 143*e3adcf8fSFrançois Tigeot 144*e3adcf8fSFrançois Tigeot static void intel_lvds_dpms(struct drm_encoder *encoder, int mode) 145*e3adcf8fSFrançois Tigeot { 146*e3adcf8fSFrançois Tigeot struct intel_lvds *intel_lvds = to_intel_lvds(encoder); 147*e3adcf8fSFrançois Tigeot 148*e3adcf8fSFrançois Tigeot if (mode == DRM_MODE_DPMS_ON) 149*e3adcf8fSFrançois Tigeot intel_lvds_enable(intel_lvds); 150*e3adcf8fSFrançois Tigeot else 151*e3adcf8fSFrançois Tigeot intel_lvds_disable(intel_lvds); 152*e3adcf8fSFrançois Tigeot 153*e3adcf8fSFrançois Tigeot /* XXX: We never power down the LVDS pairs. */ 154*e3adcf8fSFrançois Tigeot } 155*e3adcf8fSFrançois Tigeot 156*e3adcf8fSFrançois Tigeot static int intel_lvds_mode_valid(struct drm_connector *connector, 157*e3adcf8fSFrançois Tigeot struct drm_display_mode *mode) 158*e3adcf8fSFrançois Tigeot { 159*e3adcf8fSFrançois Tigeot struct intel_lvds *intel_lvds = intel_attached_lvds(connector); 160*e3adcf8fSFrançois Tigeot struct drm_display_mode *fixed_mode = intel_lvds->fixed_mode; 161*e3adcf8fSFrançois Tigeot 162*e3adcf8fSFrançois Tigeot if (mode->hdisplay > fixed_mode->hdisplay) 163*e3adcf8fSFrançois Tigeot return MODE_PANEL; 164*e3adcf8fSFrançois Tigeot if (mode->vdisplay > fixed_mode->vdisplay) 165*e3adcf8fSFrançois Tigeot return MODE_PANEL; 166*e3adcf8fSFrançois Tigeot 167*e3adcf8fSFrançois Tigeot return MODE_OK; 168*e3adcf8fSFrançois Tigeot } 169*e3adcf8fSFrançois Tigeot 170*e3adcf8fSFrançois Tigeot static void 171*e3adcf8fSFrançois Tigeot centre_horizontally(struct drm_display_mode *mode, 172*e3adcf8fSFrançois Tigeot int width) 173*e3adcf8fSFrançois Tigeot { 174*e3adcf8fSFrançois Tigeot u32 border, sync_pos, blank_width, sync_width; 175*e3adcf8fSFrançois Tigeot 176*e3adcf8fSFrançois Tigeot /* keep the hsync and hblank widths constant */ 177*e3adcf8fSFrançois Tigeot sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start; 178*e3adcf8fSFrançois Tigeot blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start; 179*e3adcf8fSFrançois Tigeot sync_pos = (blank_width - sync_width + 1) / 2; 180*e3adcf8fSFrançois Tigeot 181*e3adcf8fSFrançois Tigeot border = (mode->hdisplay - width + 1) / 2; 182*e3adcf8fSFrançois Tigeot border += border & 1; /* make the border even */ 183*e3adcf8fSFrançois Tigeot 184*e3adcf8fSFrançois Tigeot mode->crtc_hdisplay = width; 185*e3adcf8fSFrançois Tigeot mode->crtc_hblank_start = width + border; 186*e3adcf8fSFrançois Tigeot mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width; 187*e3adcf8fSFrançois Tigeot 188*e3adcf8fSFrançois Tigeot mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; 189*e3adcf8fSFrançois Tigeot mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; 190*e3adcf8fSFrançois Tigeot 191*e3adcf8fSFrançois Tigeot mode->private_flags |= INTEL_MODE_CRTC_TIMINGS_SET; 192*e3adcf8fSFrançois Tigeot } 193*e3adcf8fSFrançois Tigeot 194*e3adcf8fSFrançois Tigeot static void 195*e3adcf8fSFrançois Tigeot centre_vertically(struct drm_display_mode *mode, 196*e3adcf8fSFrançois Tigeot int height) 197*e3adcf8fSFrançois Tigeot { 198*e3adcf8fSFrançois Tigeot u32 border, sync_pos, blank_width, sync_width; 199*e3adcf8fSFrançois Tigeot 200*e3adcf8fSFrançois Tigeot /* keep the vsync and vblank widths constant */ 201*e3adcf8fSFrançois Tigeot sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start; 202*e3adcf8fSFrançois Tigeot blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start; 203*e3adcf8fSFrançois Tigeot sync_pos = (blank_width - sync_width + 1) / 2; 204*e3adcf8fSFrançois Tigeot 205*e3adcf8fSFrançois Tigeot border = (mode->vdisplay - height + 1) / 2; 206*e3adcf8fSFrançois Tigeot 207*e3adcf8fSFrançois Tigeot mode->crtc_vdisplay = height; 208*e3adcf8fSFrançois Tigeot mode->crtc_vblank_start = height + border; 209*e3adcf8fSFrançois Tigeot mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width; 210*e3adcf8fSFrançois Tigeot 211*e3adcf8fSFrançois Tigeot mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; 212*e3adcf8fSFrançois Tigeot mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; 213*e3adcf8fSFrançois Tigeot 214*e3adcf8fSFrançois Tigeot mode->private_flags |= INTEL_MODE_CRTC_TIMINGS_SET; 215*e3adcf8fSFrançois Tigeot } 216*e3adcf8fSFrançois Tigeot 217*e3adcf8fSFrançois Tigeot static inline u32 panel_fitter_scaling(u32 source, u32 target) 218*e3adcf8fSFrançois Tigeot { 219*e3adcf8fSFrançois Tigeot /* 220*e3adcf8fSFrançois Tigeot * Floating point operation is not supported. So the FACTOR 221*e3adcf8fSFrançois Tigeot * is defined, which can avoid the floating point computation 222*e3adcf8fSFrançois Tigeot * when calculating the panel ratio. 223*e3adcf8fSFrançois Tigeot */ 224*e3adcf8fSFrançois Tigeot #define ACCURACY 12 225*e3adcf8fSFrançois Tigeot #define FACTOR (1 << ACCURACY) 226*e3adcf8fSFrançois Tigeot u32 ratio = source * FACTOR / target; 227*e3adcf8fSFrançois Tigeot return (FACTOR * ratio + FACTOR/2) / FACTOR; 228*e3adcf8fSFrançois Tigeot } 229*e3adcf8fSFrançois Tigeot 230*e3adcf8fSFrançois Tigeot static bool intel_lvds_mode_fixup(struct drm_encoder *encoder, 231*e3adcf8fSFrançois Tigeot struct drm_display_mode *mode, 232*e3adcf8fSFrançois Tigeot struct drm_display_mode *adjusted_mode) 233*e3adcf8fSFrançois Tigeot { 234*e3adcf8fSFrançois Tigeot struct drm_device *dev = encoder->dev; 235*e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 236*e3adcf8fSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); 237*e3adcf8fSFrançois Tigeot struct intel_lvds *intel_lvds = to_intel_lvds(encoder); 238*e3adcf8fSFrançois Tigeot struct drm_encoder *tmp_encoder; 239*e3adcf8fSFrançois Tigeot u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; 240*e3adcf8fSFrançois Tigeot int pipe; 241*e3adcf8fSFrançois Tigeot 242*e3adcf8fSFrançois Tigeot /* Should never happen!! */ 243*e3adcf8fSFrançois Tigeot if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) { 244*e3adcf8fSFrançois Tigeot DRM_ERROR("Can't support LVDS on pipe A\n"); 245*e3adcf8fSFrançois Tigeot return false; 246*e3adcf8fSFrançois Tigeot } 247*e3adcf8fSFrançois Tigeot 248*e3adcf8fSFrançois Tigeot /* Should never happen!! */ 249*e3adcf8fSFrançois Tigeot list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) { 250*e3adcf8fSFrançois Tigeot if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) { 251*e3adcf8fSFrançois Tigeot DRM_ERROR("Can't enable LVDS and another " 252*e3adcf8fSFrançois Tigeot "encoder on the same pipe\n"); 253*e3adcf8fSFrançois Tigeot return false; 254*e3adcf8fSFrançois Tigeot } 255*e3adcf8fSFrançois Tigeot } 256*e3adcf8fSFrançois Tigeot 257*e3adcf8fSFrançois Tigeot /* 258*e3adcf8fSFrançois Tigeot * We have timings from the BIOS for the panel, put them in 259*e3adcf8fSFrançois Tigeot * to the adjusted mode. The CRTC will be set up for this mode, 260*e3adcf8fSFrançois Tigeot * with the panel scaling set up to source from the H/VDisplay 261*e3adcf8fSFrançois Tigeot * of the original mode. 262*e3adcf8fSFrançois Tigeot */ 263*e3adcf8fSFrançois Tigeot intel_fixed_panel_mode(intel_lvds->fixed_mode, adjusted_mode); 264*e3adcf8fSFrançois Tigeot 265*e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 266*e3adcf8fSFrançois Tigeot intel_pch_panel_fitting(dev, intel_lvds->fitting_mode, 267*e3adcf8fSFrançois Tigeot mode, adjusted_mode); 268*e3adcf8fSFrançois Tigeot return true; 269*e3adcf8fSFrançois Tigeot } 270*e3adcf8fSFrançois Tigeot 271*e3adcf8fSFrançois Tigeot /* Native modes don't need fitting */ 272*e3adcf8fSFrançois Tigeot if (adjusted_mode->hdisplay == mode->hdisplay && 273*e3adcf8fSFrançois Tigeot adjusted_mode->vdisplay == mode->vdisplay) 274*e3adcf8fSFrançois Tigeot goto out; 275*e3adcf8fSFrançois Tigeot 276*e3adcf8fSFrançois Tigeot /* 965+ wants fuzzy fitting */ 277*e3adcf8fSFrançois Tigeot if (INTEL_INFO(dev)->gen >= 4) 278*e3adcf8fSFrançois Tigeot pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | 279*e3adcf8fSFrançois Tigeot PFIT_FILTER_FUZZY); 280*e3adcf8fSFrançois Tigeot 281*e3adcf8fSFrançois Tigeot /* 282*e3adcf8fSFrançois Tigeot * Enable automatic panel scaling for non-native modes so that they fill 283*e3adcf8fSFrançois Tigeot * the screen. Should be enabled before the pipe is enabled, according 284*e3adcf8fSFrançois Tigeot * to register description and PRM. 285*e3adcf8fSFrançois Tigeot * Change the value here to see the borders for debugging 286*e3adcf8fSFrançois Tigeot */ 287*e3adcf8fSFrançois Tigeot for_each_pipe(pipe) 288*e3adcf8fSFrançois Tigeot I915_WRITE(BCLRPAT(pipe), 0); 289*e3adcf8fSFrançois Tigeot 290*e3adcf8fSFrançois Tigeot drm_mode_set_crtcinfo(adjusted_mode, 0); 291*e3adcf8fSFrançois Tigeot 292*e3adcf8fSFrançois Tigeot switch (intel_lvds->fitting_mode) { 293*e3adcf8fSFrançois Tigeot case DRM_MODE_SCALE_CENTER: 294*e3adcf8fSFrançois Tigeot /* 295*e3adcf8fSFrançois Tigeot * For centered modes, we have to calculate border widths & 296*e3adcf8fSFrançois Tigeot * heights and modify the values programmed into the CRTC. 297*e3adcf8fSFrançois Tigeot */ 298*e3adcf8fSFrançois Tigeot centre_horizontally(adjusted_mode, mode->hdisplay); 299*e3adcf8fSFrançois Tigeot centre_vertically(adjusted_mode, mode->vdisplay); 300*e3adcf8fSFrançois Tigeot border = LVDS_BORDER_ENABLE; 301*e3adcf8fSFrançois Tigeot break; 302*e3adcf8fSFrançois Tigeot 303*e3adcf8fSFrançois Tigeot case DRM_MODE_SCALE_ASPECT: 304*e3adcf8fSFrançois Tigeot /* Scale but preserve the aspect ratio */ 305*e3adcf8fSFrançois Tigeot if (INTEL_INFO(dev)->gen >= 4) { 306*e3adcf8fSFrançois Tigeot u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; 307*e3adcf8fSFrançois Tigeot u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; 308*e3adcf8fSFrançois Tigeot 309*e3adcf8fSFrançois Tigeot /* 965+ is easy, it does everything in hw */ 310*e3adcf8fSFrançois Tigeot if (scaled_width > scaled_height) 311*e3adcf8fSFrançois Tigeot pfit_control |= PFIT_ENABLE | PFIT_SCALING_PILLAR; 312*e3adcf8fSFrançois Tigeot else if (scaled_width < scaled_height) 313*e3adcf8fSFrançois Tigeot pfit_control |= PFIT_ENABLE | PFIT_SCALING_LETTER; 314*e3adcf8fSFrançois Tigeot else if (adjusted_mode->hdisplay != mode->hdisplay) 315*e3adcf8fSFrançois Tigeot pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; 316*e3adcf8fSFrançois Tigeot } else { 317*e3adcf8fSFrançois Tigeot u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay; 318*e3adcf8fSFrançois Tigeot u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay; 319*e3adcf8fSFrançois Tigeot /* 320*e3adcf8fSFrançois Tigeot * For earlier chips we have to calculate the scaling 321*e3adcf8fSFrançois Tigeot * ratio by hand and program it into the 322*e3adcf8fSFrançois Tigeot * PFIT_PGM_RATIO register 323*e3adcf8fSFrançois Tigeot */ 324*e3adcf8fSFrançois Tigeot if (scaled_width > scaled_height) { /* pillar */ 325*e3adcf8fSFrançois Tigeot centre_horizontally(adjusted_mode, scaled_height / mode->vdisplay); 326*e3adcf8fSFrançois Tigeot 327*e3adcf8fSFrançois Tigeot border = LVDS_BORDER_ENABLE; 328*e3adcf8fSFrançois Tigeot if (mode->vdisplay != adjusted_mode->vdisplay) { 329*e3adcf8fSFrançois Tigeot u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay); 330*e3adcf8fSFrançois Tigeot pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | 331*e3adcf8fSFrançois Tigeot bits << PFIT_VERT_SCALE_SHIFT); 332*e3adcf8fSFrançois Tigeot pfit_control |= (PFIT_ENABLE | 333*e3adcf8fSFrançois Tigeot VERT_INTERP_BILINEAR | 334*e3adcf8fSFrançois Tigeot HORIZ_INTERP_BILINEAR); 335*e3adcf8fSFrançois Tigeot } 336*e3adcf8fSFrançois Tigeot } else if (scaled_width < scaled_height) { /* letter */ 337*e3adcf8fSFrançois Tigeot centre_vertically(adjusted_mode, scaled_width / mode->hdisplay); 338*e3adcf8fSFrançois Tigeot 339*e3adcf8fSFrançois Tigeot border = LVDS_BORDER_ENABLE; 340*e3adcf8fSFrançois Tigeot if (mode->hdisplay != adjusted_mode->hdisplay) { 341*e3adcf8fSFrançois Tigeot u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay); 342*e3adcf8fSFrançois Tigeot pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | 343*e3adcf8fSFrançois Tigeot bits << PFIT_VERT_SCALE_SHIFT); 344*e3adcf8fSFrançois Tigeot pfit_control |= (PFIT_ENABLE | 345*e3adcf8fSFrançois Tigeot VERT_INTERP_BILINEAR | 346*e3adcf8fSFrançois Tigeot HORIZ_INTERP_BILINEAR); 347*e3adcf8fSFrançois Tigeot } 348*e3adcf8fSFrançois Tigeot } else 349*e3adcf8fSFrançois Tigeot /* Aspects match, Let hw scale both directions */ 350*e3adcf8fSFrançois Tigeot pfit_control |= (PFIT_ENABLE | 351*e3adcf8fSFrançois Tigeot VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | 352*e3adcf8fSFrançois Tigeot VERT_INTERP_BILINEAR | 353*e3adcf8fSFrançois Tigeot HORIZ_INTERP_BILINEAR); 354*e3adcf8fSFrançois Tigeot } 355*e3adcf8fSFrançois Tigeot break; 356*e3adcf8fSFrançois Tigeot 357*e3adcf8fSFrançois Tigeot case DRM_MODE_SCALE_FULLSCREEN: 358*e3adcf8fSFrançois Tigeot /* 359*e3adcf8fSFrançois Tigeot * Full scaling, even if it changes the aspect ratio. 360*e3adcf8fSFrançois Tigeot * Fortunately this is all done for us in hw. 361*e3adcf8fSFrançois Tigeot */ 362*e3adcf8fSFrançois Tigeot if (mode->vdisplay != adjusted_mode->vdisplay || 363*e3adcf8fSFrançois Tigeot mode->hdisplay != adjusted_mode->hdisplay) { 364*e3adcf8fSFrançois Tigeot pfit_control |= PFIT_ENABLE; 365*e3adcf8fSFrançois Tigeot if (INTEL_INFO(dev)->gen >= 4) 366*e3adcf8fSFrançois Tigeot pfit_control |= PFIT_SCALING_AUTO; 367*e3adcf8fSFrançois Tigeot else 368*e3adcf8fSFrançois Tigeot pfit_control |= (VERT_AUTO_SCALE | 369*e3adcf8fSFrançois Tigeot VERT_INTERP_BILINEAR | 370*e3adcf8fSFrançois Tigeot HORIZ_AUTO_SCALE | 371*e3adcf8fSFrançois Tigeot HORIZ_INTERP_BILINEAR); 372*e3adcf8fSFrançois Tigeot } 373*e3adcf8fSFrançois Tigeot break; 374*e3adcf8fSFrançois Tigeot 375*e3adcf8fSFrançois Tigeot default: 376*e3adcf8fSFrançois Tigeot break; 377*e3adcf8fSFrançois Tigeot } 378*e3adcf8fSFrançois Tigeot 379*e3adcf8fSFrançois Tigeot out: 380*e3adcf8fSFrançois Tigeot /* If not enabling scaling, be consistent and always use 0. */ 381*e3adcf8fSFrançois Tigeot if ((pfit_control & PFIT_ENABLE) == 0) { 382*e3adcf8fSFrançois Tigeot pfit_control = 0; 383*e3adcf8fSFrançois Tigeot pfit_pgm_ratios = 0; 384*e3adcf8fSFrançois Tigeot } 385*e3adcf8fSFrançois Tigeot 386*e3adcf8fSFrançois Tigeot /* Make sure pre-965 set dither correctly */ 387*e3adcf8fSFrançois Tigeot if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither) 388*e3adcf8fSFrançois Tigeot pfit_control |= PANEL_8TO6_DITHER_ENABLE; 389*e3adcf8fSFrançois Tigeot 390*e3adcf8fSFrançois Tigeot if (pfit_control != intel_lvds->pfit_control || 391*e3adcf8fSFrançois Tigeot pfit_pgm_ratios != intel_lvds->pfit_pgm_ratios) { 392*e3adcf8fSFrançois Tigeot intel_lvds->pfit_control = pfit_control; 393*e3adcf8fSFrançois Tigeot intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios; 394*e3adcf8fSFrançois Tigeot intel_lvds->pfit_dirty = true; 395*e3adcf8fSFrançois Tigeot } 396*e3adcf8fSFrançois Tigeot dev_priv->lvds_border_bits = border; 397*e3adcf8fSFrançois Tigeot 398*e3adcf8fSFrançois Tigeot /* 399*e3adcf8fSFrançois Tigeot * XXX: It would be nice to support lower refresh rates on the 400*e3adcf8fSFrançois Tigeot * panels to reduce power consumption, and perhaps match the 401*e3adcf8fSFrançois Tigeot * user's requested refresh rate. 402*e3adcf8fSFrançois Tigeot */ 403*e3adcf8fSFrançois Tigeot 404*e3adcf8fSFrançois Tigeot return true; 405*e3adcf8fSFrançois Tigeot } 406*e3adcf8fSFrançois Tigeot 407*e3adcf8fSFrançois Tigeot static void intel_lvds_prepare(struct drm_encoder *encoder) 408*e3adcf8fSFrançois Tigeot { 409*e3adcf8fSFrançois Tigeot struct intel_lvds *intel_lvds = to_intel_lvds(encoder); 410*e3adcf8fSFrançois Tigeot 411*e3adcf8fSFrançois Tigeot /* 412*e3adcf8fSFrançois Tigeot * Prior to Ironlake, we must disable the pipe if we want to adjust 413*e3adcf8fSFrançois Tigeot * the panel fitter. However at all other times we can just reset 414*e3adcf8fSFrançois Tigeot * the registers regardless. 415*e3adcf8fSFrançois Tigeot */ 416*e3adcf8fSFrançois Tigeot if (!HAS_PCH_SPLIT(encoder->dev) && intel_lvds->pfit_dirty) 417*e3adcf8fSFrançois Tigeot intel_lvds_disable(intel_lvds); 418*e3adcf8fSFrançois Tigeot } 419*e3adcf8fSFrançois Tigeot 420*e3adcf8fSFrançois Tigeot static void intel_lvds_commit(struct drm_encoder *encoder) 421*e3adcf8fSFrançois Tigeot { 422*e3adcf8fSFrançois Tigeot struct intel_lvds *intel_lvds = to_intel_lvds(encoder); 423*e3adcf8fSFrançois Tigeot 424*e3adcf8fSFrançois Tigeot /* Always do a full power on as we do not know what state 425*e3adcf8fSFrançois Tigeot * we were left in. 426*e3adcf8fSFrançois Tigeot */ 427*e3adcf8fSFrançois Tigeot intel_lvds_enable(intel_lvds); 428*e3adcf8fSFrançois Tigeot } 429*e3adcf8fSFrançois Tigeot 430*e3adcf8fSFrançois Tigeot static void intel_lvds_mode_set(struct drm_encoder *encoder, 431*e3adcf8fSFrançois Tigeot struct drm_display_mode *mode, 432*e3adcf8fSFrançois Tigeot struct drm_display_mode *adjusted_mode) 433*e3adcf8fSFrançois Tigeot { 434*e3adcf8fSFrançois Tigeot /* 435*e3adcf8fSFrançois Tigeot * The LVDS pin pair will already have been turned on in the 436*e3adcf8fSFrançois Tigeot * intel_crtc_mode_set since it has a large impact on the DPLL 437*e3adcf8fSFrançois Tigeot * settings. 438*e3adcf8fSFrançois Tigeot */ 439*e3adcf8fSFrançois Tigeot } 440*e3adcf8fSFrançois Tigeot 441*e3adcf8fSFrançois Tigeot /** 442*e3adcf8fSFrançois Tigeot * Detect the LVDS connection. 443*e3adcf8fSFrançois Tigeot * 444*e3adcf8fSFrançois Tigeot * Since LVDS doesn't have hotlug, we use the lid as a proxy. Open means 445*e3adcf8fSFrançois Tigeot * connected and closed means disconnected. We also send hotplug events as 446*e3adcf8fSFrançois Tigeot * needed, using lid status notification from the input layer. 447*e3adcf8fSFrançois Tigeot */ 448*e3adcf8fSFrançois Tigeot static enum drm_connector_status 449*e3adcf8fSFrançois Tigeot intel_lvds_detect(struct drm_connector *connector, bool force) 450*e3adcf8fSFrançois Tigeot { 451*e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 452*e3adcf8fSFrançois Tigeot enum drm_connector_status status; 453*e3adcf8fSFrançois Tigeot 454*e3adcf8fSFrançois Tigeot status = intel_panel_detect(dev); 455*e3adcf8fSFrançois Tigeot if (status != connector_status_unknown) 456*e3adcf8fSFrançois Tigeot return status; 457*e3adcf8fSFrançois Tigeot 458*e3adcf8fSFrançois Tigeot return connector_status_connected; 459*e3adcf8fSFrançois Tigeot } 460*e3adcf8fSFrançois Tigeot 461*e3adcf8fSFrançois Tigeot /** 462*e3adcf8fSFrançois Tigeot * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. 463*e3adcf8fSFrançois Tigeot */ 464*e3adcf8fSFrançois Tigeot static int intel_lvds_get_modes(struct drm_connector *connector) 465*e3adcf8fSFrançois Tigeot { 466*e3adcf8fSFrançois Tigeot struct intel_lvds *intel_lvds = intel_attached_lvds(connector); 467*e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 468*e3adcf8fSFrançois Tigeot struct drm_display_mode *mode; 469*e3adcf8fSFrançois Tigeot 470*e3adcf8fSFrançois Tigeot if (intel_lvds->edid) 471*e3adcf8fSFrançois Tigeot return drm_add_edid_modes(connector, intel_lvds->edid); 472*e3adcf8fSFrançois Tigeot 473*e3adcf8fSFrançois Tigeot mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode); 474*e3adcf8fSFrançois Tigeot if (mode == NULL) 475*e3adcf8fSFrançois Tigeot return 0; 476*e3adcf8fSFrançois Tigeot 477*e3adcf8fSFrançois Tigeot drm_mode_probed_add(connector, mode); 478*e3adcf8fSFrançois Tigeot return 1; 479*e3adcf8fSFrançois Tigeot } 480*e3adcf8fSFrançois Tigeot 481*e3adcf8fSFrançois Tigeot static int intel_no_modeset_on_lid_dmi_callback(const struct dmi_system_id *id) 482*e3adcf8fSFrançois Tigeot { 483*e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("Skipping forced modeset for %s\n", id->ident); 484*e3adcf8fSFrançois Tigeot return 1; 485*e3adcf8fSFrançois Tigeot } 486*e3adcf8fSFrançois Tigeot 487*e3adcf8fSFrançois Tigeot /* The GPU hangs up on these systems if modeset is performed on LID open */ 488*e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_modeset_on_lid[] = { 489*e3adcf8fSFrançois Tigeot { 490*e3adcf8fSFrançois Tigeot .callback = intel_no_modeset_on_lid_dmi_callback, 491*e3adcf8fSFrançois Tigeot .ident = "Toshiba Tecra A11", 492*e3adcf8fSFrançois Tigeot .matches = { 493*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), 494*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A11"), 495*e3adcf8fSFrançois Tigeot }, 496*e3adcf8fSFrançois Tigeot }, 497*e3adcf8fSFrançois Tigeot 498*e3adcf8fSFrançois Tigeot { } /* terminating entry */ 499*e3adcf8fSFrançois Tigeot }; 500*e3adcf8fSFrançois Tigeot 501*e3adcf8fSFrançois Tigeot #ifdef NOTYET 502*e3adcf8fSFrançois Tigeot /* 503*e3adcf8fSFrançois Tigeot * Lid events. Note the use of 'modeset_on_lid': 504*e3adcf8fSFrançois Tigeot * - we set it on lid close, and reset it on open 505*e3adcf8fSFrançois Tigeot * - we use it as a "only once" bit (ie we ignore 506*e3adcf8fSFrançois Tigeot * duplicate events where it was already properly 507*e3adcf8fSFrançois Tigeot * set/reset) 508*e3adcf8fSFrançois Tigeot * - the suspend/resume paths will also set it to 509*e3adcf8fSFrançois Tigeot * zero, since they restore the mode ("lid open"). 510*e3adcf8fSFrançois Tigeot */ 511*e3adcf8fSFrançois Tigeot static int intel_lid_notify(struct notifier_block *nb, unsigned long val, 512*e3adcf8fSFrançois Tigeot void *unused) 513*e3adcf8fSFrançois Tigeot { 514*e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = 515*e3adcf8fSFrançois Tigeot container_of(nb, struct drm_i915_private, lid_notifier); 516*e3adcf8fSFrançois Tigeot struct drm_device *dev = dev_priv->dev; 517*e3adcf8fSFrançois Tigeot struct drm_connector *connector = dev_priv->int_lvds_connector; 518*e3adcf8fSFrançois Tigeot 519*e3adcf8fSFrançois Tigeot if (dev->switch_power_state != DRM_SWITCH_POWER_ON) 520*e3adcf8fSFrançois Tigeot return NOTIFY_OK; 521*e3adcf8fSFrançois Tigeot 522*e3adcf8fSFrançois Tigeot /* 523*e3adcf8fSFrançois Tigeot * check and update the status of LVDS connector after receiving 524*e3adcf8fSFrançois Tigeot * the LID nofication event. 525*e3adcf8fSFrançois Tigeot */ 526*e3adcf8fSFrançois Tigeot if (connector) 527*e3adcf8fSFrançois Tigeot connector->status = connector->funcs->detect(connector, 528*e3adcf8fSFrançois Tigeot false); 529*e3adcf8fSFrançois Tigeot 530*e3adcf8fSFrançois Tigeot /* Don't force modeset on machines where it causes a GPU lockup */ 531*e3adcf8fSFrançois Tigeot if (dmi_check_system(intel_no_modeset_on_lid)) 532*e3adcf8fSFrançois Tigeot return NOTIFY_OK; 533*e3adcf8fSFrançois Tigeot if (!acpi_lid_open()) { 534*e3adcf8fSFrançois Tigeot dev_priv->modeset_on_lid = 1; 535*e3adcf8fSFrançois Tigeot return NOTIFY_OK; 536*e3adcf8fSFrançois Tigeot } 537*e3adcf8fSFrançois Tigeot 538*e3adcf8fSFrançois Tigeot if (!dev_priv->modeset_on_lid) 539*e3adcf8fSFrançois Tigeot return NOTIFY_OK; 540*e3adcf8fSFrançois Tigeot 541*e3adcf8fSFrançois Tigeot dev_priv->modeset_on_lid = 0; 542*e3adcf8fSFrançois Tigeot 543*e3adcf8fSFrançois Tigeot mutex_lock(&dev->mode_config.mutex); 544*e3adcf8fSFrançois Tigeot drm_helper_resume_force_mode(dev); 545*e3adcf8fSFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 546*e3adcf8fSFrançois Tigeot 547*e3adcf8fSFrançois Tigeot return NOTIFY_OK; 548*e3adcf8fSFrançois Tigeot } 549*e3adcf8fSFrançois Tigeot #endif 550*e3adcf8fSFrançois Tigeot 551*e3adcf8fSFrançois Tigeot /** 552*e3adcf8fSFrançois Tigeot * intel_lvds_destroy - unregister and free LVDS structures 553*e3adcf8fSFrançois Tigeot * @connector: connector to free 554*e3adcf8fSFrançois Tigeot * 555*e3adcf8fSFrançois Tigeot * Unregister the DDC bus for this connector then free the driver private 556*e3adcf8fSFrançois Tigeot * structure. 557*e3adcf8fSFrançois Tigeot */ 558*e3adcf8fSFrançois Tigeot static void intel_lvds_destroy(struct drm_connector *connector) 559*e3adcf8fSFrançois Tigeot { 560*e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 561*e3adcf8fSFrançois Tigeot #if 0 562*e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 563*e3adcf8fSFrançois Tigeot #endif 564*e3adcf8fSFrançois Tigeot 565*e3adcf8fSFrançois Tigeot intel_panel_destroy_backlight(dev); 566*e3adcf8fSFrançois Tigeot 567*e3adcf8fSFrançois Tigeot #if 0 568*e3adcf8fSFrançois Tigeot if (dev_priv->lid_notifier.notifier_call) 569*e3adcf8fSFrançois Tigeot acpi_lid_notifier_unregister(&dev_priv->lid_notifier); 570*e3adcf8fSFrançois Tigeot #endif 571*e3adcf8fSFrançois Tigeot #if 0 572*e3adcf8fSFrançois Tigeot drm_sysfs_connector_remove(connector); 573*e3adcf8fSFrançois Tigeot #endif 574*e3adcf8fSFrançois Tigeot drm_connector_cleanup(connector); 575*e3adcf8fSFrançois Tigeot drm_free(connector, DRM_MEM_KMS); 576*e3adcf8fSFrançois Tigeot } 577*e3adcf8fSFrançois Tigeot 578*e3adcf8fSFrançois Tigeot static int intel_lvds_set_property(struct drm_connector *connector, 579*e3adcf8fSFrançois Tigeot struct drm_property *property, 580*e3adcf8fSFrançois Tigeot uint64_t value) 581*e3adcf8fSFrançois Tigeot { 582*e3adcf8fSFrançois Tigeot struct intel_lvds *intel_lvds = intel_attached_lvds(connector); 583*e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 584*e3adcf8fSFrançois Tigeot 585*e3adcf8fSFrançois Tigeot if (property == dev->mode_config.scaling_mode_property) { 586*e3adcf8fSFrançois Tigeot struct drm_crtc *crtc = intel_lvds->base.base.crtc; 587*e3adcf8fSFrançois Tigeot 588*e3adcf8fSFrançois Tigeot if (value == DRM_MODE_SCALE_NONE) { 589*e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("no scaling not supported\n"); 590*e3adcf8fSFrançois Tigeot return -EINVAL; 591*e3adcf8fSFrançois Tigeot } 592*e3adcf8fSFrançois Tigeot 593*e3adcf8fSFrançois Tigeot if (intel_lvds->fitting_mode == value) { 594*e3adcf8fSFrançois Tigeot /* the LVDS scaling property is not changed */ 595*e3adcf8fSFrançois Tigeot return 0; 596*e3adcf8fSFrançois Tigeot } 597*e3adcf8fSFrançois Tigeot intel_lvds->fitting_mode = value; 598*e3adcf8fSFrançois Tigeot if (crtc && crtc->enabled) { 599*e3adcf8fSFrançois Tigeot /* 600*e3adcf8fSFrançois Tigeot * If the CRTC is enabled, the display will be changed 601*e3adcf8fSFrançois Tigeot * according to the new panel fitting mode. 602*e3adcf8fSFrançois Tigeot */ 603*e3adcf8fSFrançois Tigeot drm_crtc_helper_set_mode(crtc, &crtc->mode, 604*e3adcf8fSFrançois Tigeot crtc->x, crtc->y, crtc->fb); 605*e3adcf8fSFrançois Tigeot } 606*e3adcf8fSFrançois Tigeot } 607*e3adcf8fSFrançois Tigeot 608*e3adcf8fSFrançois Tigeot return 0; 609*e3adcf8fSFrançois Tigeot } 610*e3adcf8fSFrançois Tigeot 611*e3adcf8fSFrançois Tigeot static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = { 612*e3adcf8fSFrançois Tigeot .dpms = intel_lvds_dpms, 613*e3adcf8fSFrançois Tigeot .mode_fixup = intel_lvds_mode_fixup, 614*e3adcf8fSFrançois Tigeot .prepare = intel_lvds_prepare, 615*e3adcf8fSFrançois Tigeot .mode_set = intel_lvds_mode_set, 616*e3adcf8fSFrançois Tigeot .commit = intel_lvds_commit, 617*e3adcf8fSFrançois Tigeot }; 618*e3adcf8fSFrançois Tigeot 619*e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = { 620*e3adcf8fSFrançois Tigeot .get_modes = intel_lvds_get_modes, 621*e3adcf8fSFrançois Tigeot .mode_valid = intel_lvds_mode_valid, 622*e3adcf8fSFrançois Tigeot .best_encoder = intel_best_encoder, 623*e3adcf8fSFrançois Tigeot }; 624*e3adcf8fSFrançois Tigeot 625*e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_lvds_connector_funcs = { 626*e3adcf8fSFrançois Tigeot .dpms = drm_helper_connector_dpms, 627*e3adcf8fSFrançois Tigeot .detect = intel_lvds_detect, 628*e3adcf8fSFrançois Tigeot .fill_modes = drm_helper_probe_single_connector_modes, 629*e3adcf8fSFrançois Tigeot .set_property = intel_lvds_set_property, 630*e3adcf8fSFrançois Tigeot .destroy = intel_lvds_destroy, 631*e3adcf8fSFrançois Tigeot }; 632*e3adcf8fSFrançois Tigeot 633*e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_lvds_enc_funcs = { 634*e3adcf8fSFrançois Tigeot .destroy = intel_encoder_destroy, 635*e3adcf8fSFrançois Tigeot }; 636*e3adcf8fSFrançois Tigeot 637*e3adcf8fSFrançois Tigeot static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id) 638*e3adcf8fSFrançois Tigeot { 639*e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("Skipping LVDS initialization for %s\n", id->ident); 640*e3adcf8fSFrançois Tigeot return 1; 641*e3adcf8fSFrançois Tigeot } 642*e3adcf8fSFrançois Tigeot 643*e3adcf8fSFrançois Tigeot /* These systems claim to have LVDS, but really don't */ 644*e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_lvds[] = { 645*e3adcf8fSFrançois Tigeot { 646*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 647*e3adcf8fSFrançois Tigeot .ident = "Apple Mac Mini (Core series)", 648*e3adcf8fSFrançois Tigeot .matches = { 649*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "Apple"), 650*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"), 651*e3adcf8fSFrançois Tigeot }, 652*e3adcf8fSFrançois Tigeot }, 653*e3adcf8fSFrançois Tigeot { 654*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 655*e3adcf8fSFrançois Tigeot .ident = "Apple Mac Mini (Core 2 series)", 656*e3adcf8fSFrançois Tigeot .matches = { 657*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "Apple"), 658*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "Macmini2,1"), 659*e3adcf8fSFrançois Tigeot }, 660*e3adcf8fSFrançois Tigeot }, 661*e3adcf8fSFrançois Tigeot { 662*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 663*e3adcf8fSFrançois Tigeot .ident = "MSI IM-945GSE-A", 664*e3adcf8fSFrançois Tigeot .matches = { 665*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "MSI"), 666*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "A9830IMS"), 667*e3adcf8fSFrançois Tigeot }, 668*e3adcf8fSFrançois Tigeot }, 669*e3adcf8fSFrançois Tigeot { 670*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 671*e3adcf8fSFrançois Tigeot .ident = "Dell Studio Hybrid", 672*e3adcf8fSFrançois Tigeot .matches = { 673*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 674*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"), 675*e3adcf8fSFrançois Tigeot }, 676*e3adcf8fSFrançois Tigeot }, 677*e3adcf8fSFrançois Tigeot { 678*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 679*e3adcf8fSFrançois Tigeot .ident = "Dell OptiPlex FX170", 680*e3adcf8fSFrançois Tigeot .matches = { 681*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 682*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"), 683*e3adcf8fSFrançois Tigeot }, 684*e3adcf8fSFrançois Tigeot }, 685*e3adcf8fSFrançois Tigeot { 686*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 687*e3adcf8fSFrançois Tigeot .ident = "AOpen Mini PC", 688*e3adcf8fSFrançois Tigeot .matches = { 689*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "AOpen"), 690*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"), 691*e3adcf8fSFrançois Tigeot }, 692*e3adcf8fSFrançois Tigeot }, 693*e3adcf8fSFrançois Tigeot { 694*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 695*e3adcf8fSFrançois Tigeot .ident = "AOpen Mini PC MP915", 696*e3adcf8fSFrançois Tigeot .matches = { 697*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), 698*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"), 699*e3adcf8fSFrançois Tigeot }, 700*e3adcf8fSFrançois Tigeot }, 701*e3adcf8fSFrançois Tigeot { 702*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 703*e3adcf8fSFrançois Tigeot .ident = "AOpen i915GMm-HFS", 704*e3adcf8fSFrançois Tigeot .matches = { 705*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), 706*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"), 707*e3adcf8fSFrançois Tigeot }, 708*e3adcf8fSFrançois Tigeot }, 709*e3adcf8fSFrançois Tigeot { 710*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 711*e3adcf8fSFrançois Tigeot .ident = "AOpen i45GMx-I", 712*e3adcf8fSFrançois Tigeot .matches = { 713*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"), 714*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_NAME, "i45GMx-I"), 715*e3adcf8fSFrançois Tigeot }, 716*e3adcf8fSFrançois Tigeot }, 717*e3adcf8fSFrançois Tigeot { 718*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 719*e3adcf8fSFrançois Tigeot .ident = "Aopen i945GTt-VFA", 720*e3adcf8fSFrançois Tigeot .matches = { 721*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"), 722*e3adcf8fSFrançois Tigeot }, 723*e3adcf8fSFrançois Tigeot }, 724*e3adcf8fSFrançois Tigeot { 725*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 726*e3adcf8fSFrançois Tigeot .ident = "Clientron U800", 727*e3adcf8fSFrançois Tigeot .matches = { 728*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "Clientron"), 729*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "U800"), 730*e3adcf8fSFrançois Tigeot }, 731*e3adcf8fSFrançois Tigeot }, 732*e3adcf8fSFrançois Tigeot { 733*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 734*e3adcf8fSFrançois Tigeot .ident = "Clientron E830", 735*e3adcf8fSFrançois Tigeot .matches = { 736*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "Clientron"), 737*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "E830"), 738*e3adcf8fSFrançois Tigeot }, 739*e3adcf8fSFrançois Tigeot }, 740*e3adcf8fSFrançois Tigeot { 741*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 742*e3adcf8fSFrançois Tigeot .ident = "Asus EeeBox PC EB1007", 743*e3adcf8fSFrançois Tigeot .matches = { 744*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), 745*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "EB1007"), 746*e3adcf8fSFrançois Tigeot }, 747*e3adcf8fSFrançois Tigeot }, 748*e3adcf8fSFrançois Tigeot { 749*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 750*e3adcf8fSFrançois Tigeot .ident = "Asus AT5NM10T-I", 751*e3adcf8fSFrançois Tigeot .matches = { 752*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."), 753*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"), 754*e3adcf8fSFrançois Tigeot }, 755*e3adcf8fSFrançois Tigeot }, 756*e3adcf8fSFrançois Tigeot { 757*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 758*e3adcf8fSFrançois Tigeot .ident = "Hewlett-Packard t5745", 759*e3adcf8fSFrançois Tigeot .matches = { 760*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), 761*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "hp t5745"), 762*e3adcf8fSFrançois Tigeot }, 763*e3adcf8fSFrançois Tigeot }, 764*e3adcf8fSFrançois Tigeot { 765*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 766*e3adcf8fSFrançois Tigeot .ident = "Hewlett-Packard st5747", 767*e3adcf8fSFrançois Tigeot .matches = { 768*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"), 769*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "hp st5747"), 770*e3adcf8fSFrançois Tigeot }, 771*e3adcf8fSFrançois Tigeot }, 772*e3adcf8fSFrançois Tigeot { 773*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 774*e3adcf8fSFrançois Tigeot .ident = "MSI Wind Box DC500", 775*e3adcf8fSFrançois Tigeot .matches = { 776*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"), 777*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_BOARD_NAME, "MS-7469"), 778*e3adcf8fSFrançois Tigeot }, 779*e3adcf8fSFrançois Tigeot }, 780*e3adcf8fSFrançois Tigeot { 781*e3adcf8fSFrançois Tigeot .callback = intel_no_lvds_dmi_callback, 782*e3adcf8fSFrançois Tigeot .ident = "Supermicro X7SPA-H", 783*e3adcf8fSFrançois Tigeot .matches = { 784*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "Supermicro"), 785*e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "X7SPA-H"), 786*e3adcf8fSFrançois Tigeot }, 787*e3adcf8fSFrançois Tigeot }, 788*e3adcf8fSFrançois Tigeot 789*e3adcf8fSFrançois Tigeot { } /* terminating entry */ 790*e3adcf8fSFrançois Tigeot }; 791*e3adcf8fSFrançois Tigeot 792*e3adcf8fSFrançois Tigeot /** 793*e3adcf8fSFrançois Tigeot * intel_find_lvds_downclock - find the reduced downclock for LVDS in EDID 794*e3adcf8fSFrançois Tigeot * @dev: drm device 795*e3adcf8fSFrançois Tigeot * @connector: LVDS connector 796*e3adcf8fSFrançois Tigeot * 797*e3adcf8fSFrançois Tigeot * Find the reduced downclock for LVDS in EDID. 798*e3adcf8fSFrançois Tigeot */ 799*e3adcf8fSFrançois Tigeot static void intel_find_lvds_downclock(struct drm_device *dev, 800*e3adcf8fSFrançois Tigeot struct drm_display_mode *fixed_mode, 801*e3adcf8fSFrançois Tigeot struct drm_connector *connector) 802*e3adcf8fSFrançois Tigeot { 803*e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 804*e3adcf8fSFrançois Tigeot struct drm_display_mode *scan; 805*e3adcf8fSFrançois Tigeot int temp_downclock; 806*e3adcf8fSFrançois Tigeot 807*e3adcf8fSFrançois Tigeot temp_downclock = fixed_mode->clock; 808*e3adcf8fSFrançois Tigeot list_for_each_entry(scan, &connector->probed_modes, head) { 809*e3adcf8fSFrançois Tigeot /* 810*e3adcf8fSFrançois Tigeot * If one mode has the same resolution with the fixed_panel 811*e3adcf8fSFrançois Tigeot * mode while they have the different refresh rate, it means 812*e3adcf8fSFrançois Tigeot * that the reduced downclock is found for the LVDS. In such 813*e3adcf8fSFrançois Tigeot * case we can set the different FPx0/1 to dynamically select 814*e3adcf8fSFrançois Tigeot * between low and high frequency. 815*e3adcf8fSFrançois Tigeot */ 816*e3adcf8fSFrançois Tigeot if (scan->hdisplay == fixed_mode->hdisplay && 817*e3adcf8fSFrançois Tigeot scan->hsync_start == fixed_mode->hsync_start && 818*e3adcf8fSFrançois Tigeot scan->hsync_end == fixed_mode->hsync_end && 819*e3adcf8fSFrançois Tigeot scan->htotal == fixed_mode->htotal && 820*e3adcf8fSFrançois Tigeot scan->vdisplay == fixed_mode->vdisplay && 821*e3adcf8fSFrançois Tigeot scan->vsync_start == fixed_mode->vsync_start && 822*e3adcf8fSFrançois Tigeot scan->vsync_end == fixed_mode->vsync_end && 823*e3adcf8fSFrançois Tigeot scan->vtotal == fixed_mode->vtotal) { 824*e3adcf8fSFrançois Tigeot if (scan->clock < temp_downclock) { 825*e3adcf8fSFrançois Tigeot /* 826*e3adcf8fSFrançois Tigeot * The downclock is already found. But we 827*e3adcf8fSFrançois Tigeot * expect to find the lower downclock. 828*e3adcf8fSFrançois Tigeot */ 829*e3adcf8fSFrançois Tigeot temp_downclock = scan->clock; 830*e3adcf8fSFrançois Tigeot } 831*e3adcf8fSFrançois Tigeot } 832*e3adcf8fSFrançois Tigeot } 833*e3adcf8fSFrançois Tigeot if (temp_downclock < fixed_mode->clock && i915_lvds_downclock) { 834*e3adcf8fSFrançois Tigeot /* We found the downclock for LVDS. */ 835*e3adcf8fSFrançois Tigeot dev_priv->lvds_downclock_avail = 1; 836*e3adcf8fSFrançois Tigeot dev_priv->lvds_downclock = temp_downclock; 837*e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("LVDS downclock is found in EDID. " 838*e3adcf8fSFrançois Tigeot "Normal clock %dKhz, downclock %dKhz\n", 839*e3adcf8fSFrançois Tigeot fixed_mode->clock, temp_downclock); 840*e3adcf8fSFrançois Tigeot } 841*e3adcf8fSFrançois Tigeot } 842*e3adcf8fSFrançois Tigeot 843*e3adcf8fSFrançois Tigeot /* 844*e3adcf8fSFrançois Tigeot * Enumerate the child dev array parsed from VBT to check whether 845*e3adcf8fSFrançois Tigeot * the LVDS is present. 846*e3adcf8fSFrançois Tigeot * If it is present, return 1. 847*e3adcf8fSFrançois Tigeot * If it is not present, return false. 848*e3adcf8fSFrançois Tigeot * If no child dev is parsed from VBT, it assumes that the LVDS is present. 849*e3adcf8fSFrançois Tigeot */ 850*e3adcf8fSFrançois Tigeot static bool lvds_is_present_in_vbt(struct drm_device *dev, 851*e3adcf8fSFrançois Tigeot u8 *i2c_pin) 852*e3adcf8fSFrançois Tigeot { 853*e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 854*e3adcf8fSFrançois Tigeot int i; 855*e3adcf8fSFrançois Tigeot 856*e3adcf8fSFrançois Tigeot if (!dev_priv->child_dev_num) 857*e3adcf8fSFrançois Tigeot return true; 858*e3adcf8fSFrançois Tigeot 859*e3adcf8fSFrançois Tigeot for (i = 0; i < dev_priv->child_dev_num; i++) { 860*e3adcf8fSFrançois Tigeot struct child_device_config *child = dev_priv->child_dev + i; 861*e3adcf8fSFrançois Tigeot 862*e3adcf8fSFrançois Tigeot /* If the device type is not LFP, continue. 863*e3adcf8fSFrançois Tigeot * We have to check both the new identifiers as well as the 864*e3adcf8fSFrançois Tigeot * old for compatibility with some BIOSes. 865*e3adcf8fSFrançois Tigeot */ 866*e3adcf8fSFrançois Tigeot if (child->device_type != DEVICE_TYPE_INT_LFP && 867*e3adcf8fSFrançois Tigeot child->device_type != DEVICE_TYPE_LFP) 868*e3adcf8fSFrançois Tigeot continue; 869*e3adcf8fSFrançois Tigeot 870*e3adcf8fSFrançois Tigeot if (child->i2c_pin) 871*e3adcf8fSFrançois Tigeot *i2c_pin = child->i2c_pin; 872*e3adcf8fSFrançois Tigeot 873*e3adcf8fSFrançois Tigeot /* However, we cannot trust the BIOS writers to populate 874*e3adcf8fSFrançois Tigeot * the VBT correctly. Since LVDS requires additional 875*e3adcf8fSFrançois Tigeot * information from AIM blocks, a non-zero addin offset is 876*e3adcf8fSFrançois Tigeot * a good indicator that the LVDS is actually present. 877*e3adcf8fSFrançois Tigeot */ 878*e3adcf8fSFrançois Tigeot if (child->addin_offset) 879*e3adcf8fSFrançois Tigeot return true; 880*e3adcf8fSFrançois Tigeot 881*e3adcf8fSFrançois Tigeot /* But even then some BIOS writers perform some black magic 882*e3adcf8fSFrançois Tigeot * and instantiate the device without reference to any 883*e3adcf8fSFrançois Tigeot * additional data. Trust that if the VBT was written into 884*e3adcf8fSFrançois Tigeot * the OpRegion then they have validated the LVDS's existence. 885*e3adcf8fSFrançois Tigeot */ 886*e3adcf8fSFrançois Tigeot if (dev_priv->opregion.vbt) 887*e3adcf8fSFrançois Tigeot return true; 888*e3adcf8fSFrançois Tigeot } 889*e3adcf8fSFrançois Tigeot 890*e3adcf8fSFrançois Tigeot return false; 891*e3adcf8fSFrançois Tigeot } 892*e3adcf8fSFrançois Tigeot 893*e3adcf8fSFrançois Tigeot static bool intel_lvds_supported(struct drm_device *dev) 894*e3adcf8fSFrançois Tigeot { 895*e3adcf8fSFrançois Tigeot /* With the introduction of the PCH we gained a dedicated 896*e3adcf8fSFrançois Tigeot * LVDS presence pin, use it. */ 897*e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 898*e3adcf8fSFrançois Tigeot return true; 899*e3adcf8fSFrançois Tigeot 900*e3adcf8fSFrançois Tigeot /* Otherwise LVDS was only attached to mobile products, 901*e3adcf8fSFrançois Tigeot * except for the inglorious 830gm */ 902*e3adcf8fSFrançois Tigeot return IS_MOBILE(dev) && !IS_I830(dev); 903*e3adcf8fSFrançois Tigeot } 904*e3adcf8fSFrançois Tigeot 905*e3adcf8fSFrançois Tigeot /** 906*e3adcf8fSFrançois Tigeot * intel_lvds_init - setup LVDS connectors on this device 907*e3adcf8fSFrançois Tigeot * @dev: drm device 908*e3adcf8fSFrançois Tigeot * 909*e3adcf8fSFrançois Tigeot * Create the connector, register the LVDS DDC bus, and try to figure out what 910*e3adcf8fSFrançois Tigeot * modes we can display on the LVDS panel (if present). 911*e3adcf8fSFrançois Tigeot */ 912*e3adcf8fSFrançois Tigeot bool intel_lvds_init(struct drm_device *dev) 913*e3adcf8fSFrançois Tigeot { 914*e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 915*e3adcf8fSFrançois Tigeot struct intel_lvds *intel_lvds; 916*e3adcf8fSFrançois Tigeot struct intel_encoder *intel_encoder; 917*e3adcf8fSFrançois Tigeot struct intel_connector *intel_connector; 918*e3adcf8fSFrançois Tigeot struct drm_connector *connector; 919*e3adcf8fSFrançois Tigeot struct drm_encoder *encoder; 920*e3adcf8fSFrançois Tigeot struct drm_display_mode *scan; /* *modes, *bios_mode; */ 921*e3adcf8fSFrançois Tigeot struct drm_crtc *crtc; 922*e3adcf8fSFrançois Tigeot u32 lvds; 923*e3adcf8fSFrançois Tigeot int pipe; 924*e3adcf8fSFrançois Tigeot u8 pin; 925*e3adcf8fSFrançois Tigeot 926*e3adcf8fSFrançois Tigeot if (!intel_lvds_supported(dev)) 927*e3adcf8fSFrançois Tigeot return false; 928*e3adcf8fSFrançois Tigeot 929*e3adcf8fSFrançois Tigeot /* Skip init on machines we know falsely report LVDS */ 930*e3adcf8fSFrançois Tigeot if (dmi_check_system(intel_no_lvds)) 931*e3adcf8fSFrançois Tigeot return false; 932*e3adcf8fSFrançois Tigeot 933*e3adcf8fSFrançois Tigeot pin = GMBUS_PORT_PANEL; 934*e3adcf8fSFrançois Tigeot if (!lvds_is_present_in_vbt(dev, &pin)) { 935*e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("LVDS is not present in VBT\n"); 936*e3adcf8fSFrançois Tigeot return false; 937*e3adcf8fSFrançois Tigeot } 938*e3adcf8fSFrançois Tigeot 939*e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 940*e3adcf8fSFrançois Tigeot if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0) 941*e3adcf8fSFrançois Tigeot return false; 942*e3adcf8fSFrançois Tigeot if (dev_priv->edp.support) { 943*e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("disable LVDS for eDP support\n"); 944*e3adcf8fSFrançois Tigeot return false; 945*e3adcf8fSFrançois Tigeot } 946*e3adcf8fSFrançois Tigeot } 947*e3adcf8fSFrançois Tigeot 948*e3adcf8fSFrançois Tigeot intel_lvds = kmalloc(sizeof(struct intel_lvds), DRM_MEM_KMS, 949*e3adcf8fSFrançois Tigeot M_WAITOK | M_ZERO); 950*e3adcf8fSFrançois Tigeot intel_connector = kmalloc(sizeof(struct intel_connector), DRM_MEM_KMS, 951*e3adcf8fSFrançois Tigeot M_WAITOK | M_ZERO); 952*e3adcf8fSFrançois Tigeot 953*e3adcf8fSFrançois Tigeot if (!HAS_PCH_SPLIT(dev)) { 954*e3adcf8fSFrançois Tigeot intel_lvds->pfit_control = I915_READ(PFIT_CONTROL); 955*e3adcf8fSFrançois Tigeot } 956*e3adcf8fSFrançois Tigeot 957*e3adcf8fSFrançois Tigeot intel_encoder = &intel_lvds->base; 958*e3adcf8fSFrançois Tigeot encoder = &intel_encoder->base; 959*e3adcf8fSFrançois Tigeot connector = &intel_connector->base; 960*e3adcf8fSFrançois Tigeot drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs, 961*e3adcf8fSFrançois Tigeot DRM_MODE_CONNECTOR_LVDS); 962*e3adcf8fSFrançois Tigeot 963*e3adcf8fSFrançois Tigeot drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs, 964*e3adcf8fSFrançois Tigeot DRM_MODE_ENCODER_LVDS); 965*e3adcf8fSFrançois Tigeot 966*e3adcf8fSFrançois Tigeot intel_connector_attach_encoder(intel_connector, intel_encoder); 967*e3adcf8fSFrançois Tigeot intel_encoder->type = INTEL_OUTPUT_LVDS; 968*e3adcf8fSFrançois Tigeot 969*e3adcf8fSFrançois Tigeot intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT); 970*e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 971*e3adcf8fSFrançois Tigeot intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); 972*e3adcf8fSFrançois Tigeot else 973*e3adcf8fSFrançois Tigeot intel_encoder->crtc_mask = (1 << 1); 974*e3adcf8fSFrançois Tigeot 975*e3adcf8fSFrançois Tigeot drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs); 976*e3adcf8fSFrançois Tigeot drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs); 977*e3adcf8fSFrançois Tigeot connector->display_info.subpixel_order = SubPixelHorizontalRGB; 978*e3adcf8fSFrançois Tigeot connector->interlace_allowed = false; 979*e3adcf8fSFrançois Tigeot connector->doublescan_allowed = false; 980*e3adcf8fSFrançois Tigeot 981*e3adcf8fSFrançois Tigeot /* create the scaling mode property */ 982*e3adcf8fSFrançois Tigeot drm_mode_create_scaling_mode_property(dev); 983*e3adcf8fSFrançois Tigeot /* 984*e3adcf8fSFrançois Tigeot * the initial panel fitting mode will be FULL_SCREEN. 985*e3adcf8fSFrançois Tigeot */ 986*e3adcf8fSFrançois Tigeot 987*e3adcf8fSFrançois Tigeot drm_connector_attach_property(&intel_connector->base, 988*e3adcf8fSFrançois Tigeot dev->mode_config.scaling_mode_property, 989*e3adcf8fSFrançois Tigeot DRM_MODE_SCALE_ASPECT); 990*e3adcf8fSFrançois Tigeot intel_lvds->fitting_mode = DRM_MODE_SCALE_ASPECT; 991*e3adcf8fSFrançois Tigeot /* 992*e3adcf8fSFrançois Tigeot * LVDS discovery: 993*e3adcf8fSFrançois Tigeot * 1) check for EDID on DDC 994*e3adcf8fSFrançois Tigeot * 2) check for VBT data 995*e3adcf8fSFrançois Tigeot * 3) check to see if LVDS is already on 996*e3adcf8fSFrançois Tigeot * if none of the above, no panel 997*e3adcf8fSFrançois Tigeot * 4) make sure lid is open 998*e3adcf8fSFrançois Tigeot * if closed, act like it's not there for now 999*e3adcf8fSFrançois Tigeot */ 1000*e3adcf8fSFrançois Tigeot 1001*e3adcf8fSFrançois Tigeot /* 1002*e3adcf8fSFrançois Tigeot * Attempt to get the fixed panel mode from DDC. Assume that the 1003*e3adcf8fSFrançois Tigeot * preferred mode is the right one. 1004*e3adcf8fSFrançois Tigeot */ 1005*e3adcf8fSFrançois Tigeot intel_lvds->edid = drm_get_edid(connector, dev_priv->gmbus[pin]); 1006*e3adcf8fSFrançois Tigeot if (intel_lvds->edid) { 1007*e3adcf8fSFrançois Tigeot if (drm_add_edid_modes(connector, 1008*e3adcf8fSFrançois Tigeot intel_lvds->edid)) { 1009*e3adcf8fSFrançois Tigeot drm_mode_connector_update_edid_property(connector, 1010*e3adcf8fSFrançois Tigeot intel_lvds->edid); 1011*e3adcf8fSFrançois Tigeot } else { 1012*e3adcf8fSFrançois Tigeot drm_free(intel_lvds->edid, DRM_MEM_KMS); 1013*e3adcf8fSFrançois Tigeot intel_lvds->edid = NULL; 1014*e3adcf8fSFrançois Tigeot } 1015*e3adcf8fSFrançois Tigeot } 1016*e3adcf8fSFrançois Tigeot if (!intel_lvds->edid) { 1017*e3adcf8fSFrançois Tigeot /* Didn't get an EDID, so 1018*e3adcf8fSFrançois Tigeot * Set wide sync ranges so we get all modes 1019*e3adcf8fSFrançois Tigeot * handed to valid_mode for checking 1020*e3adcf8fSFrançois Tigeot */ 1021*e3adcf8fSFrançois Tigeot connector->display_info.min_vfreq = 0; 1022*e3adcf8fSFrançois Tigeot connector->display_info.max_vfreq = 200; 1023*e3adcf8fSFrançois Tigeot connector->display_info.min_hfreq = 0; 1024*e3adcf8fSFrançois Tigeot connector->display_info.max_hfreq = 200; 1025*e3adcf8fSFrançois Tigeot } 1026*e3adcf8fSFrançois Tigeot 1027*e3adcf8fSFrançois Tigeot list_for_each_entry(scan, &connector->probed_modes, head) { 1028*e3adcf8fSFrançois Tigeot if (scan->type & DRM_MODE_TYPE_PREFERRED) { 1029*e3adcf8fSFrançois Tigeot intel_lvds->fixed_mode = 1030*e3adcf8fSFrançois Tigeot drm_mode_duplicate(dev, scan); 1031*e3adcf8fSFrançois Tigeot intel_find_lvds_downclock(dev, 1032*e3adcf8fSFrançois Tigeot intel_lvds->fixed_mode, 1033*e3adcf8fSFrançois Tigeot connector); 1034*e3adcf8fSFrançois Tigeot goto out; 1035*e3adcf8fSFrançois Tigeot } 1036*e3adcf8fSFrançois Tigeot } 1037*e3adcf8fSFrançois Tigeot 1038*e3adcf8fSFrançois Tigeot /* Failed to get EDID, what about VBT? */ 1039*e3adcf8fSFrançois Tigeot if (dev_priv->lfp_lvds_vbt_mode) { 1040*e3adcf8fSFrançois Tigeot intel_lvds->fixed_mode = 1041*e3adcf8fSFrançois Tigeot drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); 1042*e3adcf8fSFrançois Tigeot if (intel_lvds->fixed_mode) { 1043*e3adcf8fSFrançois Tigeot intel_lvds->fixed_mode->type |= 1044*e3adcf8fSFrançois Tigeot DRM_MODE_TYPE_PREFERRED; 1045*e3adcf8fSFrançois Tigeot goto out; 1046*e3adcf8fSFrançois Tigeot } 1047*e3adcf8fSFrançois Tigeot } 1048*e3adcf8fSFrançois Tigeot 1049*e3adcf8fSFrançois Tigeot /* 1050*e3adcf8fSFrançois Tigeot * If we didn't get EDID, try checking if the panel is already turned 1051*e3adcf8fSFrançois Tigeot * on. If so, assume that whatever is currently programmed is the 1052*e3adcf8fSFrançois Tigeot * correct mode. 1053*e3adcf8fSFrançois Tigeot */ 1054*e3adcf8fSFrançois Tigeot 1055*e3adcf8fSFrançois Tigeot /* Ironlake: FIXME if still fail, not try pipe mode now */ 1056*e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 1057*e3adcf8fSFrançois Tigeot goto failed; 1058*e3adcf8fSFrançois Tigeot 1059*e3adcf8fSFrançois Tigeot lvds = I915_READ(LVDS); 1060*e3adcf8fSFrançois Tigeot pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; 1061*e3adcf8fSFrançois Tigeot crtc = intel_get_crtc_for_pipe(dev, pipe); 1062*e3adcf8fSFrançois Tigeot 1063*e3adcf8fSFrançois Tigeot if (crtc && (lvds & LVDS_PORT_EN)) { 1064*e3adcf8fSFrançois Tigeot intel_lvds->fixed_mode = intel_crtc_mode_get(dev, crtc); 1065*e3adcf8fSFrançois Tigeot if (intel_lvds->fixed_mode) { 1066*e3adcf8fSFrançois Tigeot intel_lvds->fixed_mode->type |= 1067*e3adcf8fSFrançois Tigeot DRM_MODE_TYPE_PREFERRED; 1068*e3adcf8fSFrançois Tigeot goto out; 1069*e3adcf8fSFrançois Tigeot } 1070*e3adcf8fSFrançois Tigeot } 1071*e3adcf8fSFrançois Tigeot 1072*e3adcf8fSFrançois Tigeot /* If we still don't have a mode after all that, give up. */ 1073*e3adcf8fSFrançois Tigeot if (!intel_lvds->fixed_mode) 1074*e3adcf8fSFrançois Tigeot goto failed; 1075*e3adcf8fSFrançois Tigeot 1076*e3adcf8fSFrançois Tigeot out: 1077*e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 1078*e3adcf8fSFrançois Tigeot u32 pwm; 1079*e3adcf8fSFrançois Tigeot 1080*e3adcf8fSFrançois Tigeot pipe = (I915_READ(PCH_LVDS) & LVDS_PIPEB_SELECT) ? 1 : 0; 1081*e3adcf8fSFrançois Tigeot 1082*e3adcf8fSFrançois Tigeot /* make sure PWM is enabled and locked to the LVDS pipe */ 1083*e3adcf8fSFrançois Tigeot pwm = I915_READ(BLC_PWM_CPU_CTL2); 1084*e3adcf8fSFrançois Tigeot if (pipe == 0 && (pwm & PWM_PIPE_B)) 1085*e3adcf8fSFrançois Tigeot I915_WRITE(BLC_PWM_CPU_CTL2, pwm & ~PWM_ENABLE); 1086*e3adcf8fSFrançois Tigeot if (pipe) 1087*e3adcf8fSFrançois Tigeot pwm |= PWM_PIPE_B; 1088*e3adcf8fSFrançois Tigeot else 1089*e3adcf8fSFrançois Tigeot pwm &= ~PWM_PIPE_B; 1090*e3adcf8fSFrançois Tigeot I915_WRITE(BLC_PWM_CPU_CTL2, pwm | PWM_ENABLE); 1091*e3adcf8fSFrançois Tigeot 1092*e3adcf8fSFrançois Tigeot pwm = I915_READ(BLC_PWM_PCH_CTL1); 1093*e3adcf8fSFrançois Tigeot pwm |= PWM_PCH_ENABLE; 1094*e3adcf8fSFrançois Tigeot I915_WRITE(BLC_PWM_PCH_CTL1, pwm); 1095*e3adcf8fSFrançois Tigeot /* 1096*e3adcf8fSFrançois Tigeot * Unlock registers and just 1097*e3adcf8fSFrançois Tigeot * leave them unlocked 1098*e3adcf8fSFrançois Tigeot */ 1099*e3adcf8fSFrançois Tigeot I915_WRITE(PCH_PP_CONTROL, 1100*e3adcf8fSFrançois Tigeot I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS); 1101*e3adcf8fSFrançois Tigeot } else { 1102*e3adcf8fSFrançois Tigeot /* 1103*e3adcf8fSFrançois Tigeot * Unlock registers and just 1104*e3adcf8fSFrançois Tigeot * leave them unlocked 1105*e3adcf8fSFrançois Tigeot */ 1106*e3adcf8fSFrançois Tigeot I915_WRITE(PP_CONTROL, 1107*e3adcf8fSFrançois Tigeot I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS); 1108*e3adcf8fSFrançois Tigeot } 1109*e3adcf8fSFrançois Tigeot #ifdef NOTYET 1110*e3adcf8fSFrançois Tigeot dev_priv->lid_notifier.notifier_call = intel_lid_notify; 1111*e3adcf8fSFrançois Tigeot if (acpi_lid_notifier_register(&dev_priv->lid_notifier)) { 1112*e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("lid notifier registration failed\n"); 1113*e3adcf8fSFrançois Tigeot dev_priv->lid_notifier.notifier_call = NULL; 1114*e3adcf8fSFrançois Tigeot } 1115*e3adcf8fSFrançois Tigeot #endif 1116*e3adcf8fSFrançois Tigeot /* keep the LVDS connector */ 1117*e3adcf8fSFrançois Tigeot dev_priv->int_lvds_connector = connector; 1118*e3adcf8fSFrançois Tigeot #if 0 1119*e3adcf8fSFrançois Tigeot drm_sysfs_connector_add(connector); 1120*e3adcf8fSFrançois Tigeot #endif 1121*e3adcf8fSFrançois Tigeot intel_panel_setup_backlight(dev); 1122*e3adcf8fSFrançois Tigeot return true; 1123*e3adcf8fSFrançois Tigeot 1124*e3adcf8fSFrançois Tigeot failed: 1125*e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("No LVDS modes found, disabling.\n"); 1126*e3adcf8fSFrançois Tigeot drm_connector_cleanup(connector); 1127*e3adcf8fSFrançois Tigeot drm_encoder_cleanup(encoder); 1128*e3adcf8fSFrançois Tigeot drm_free(intel_lvds, DRM_MEM_KMS); 1129*e3adcf8fSFrançois Tigeot drm_free(intel_connector, DRM_MEM_KMS); 1130*e3adcf8fSFrançois Tigeot return false; 1131*e3adcf8fSFrançois Tigeot } 1132