1e3adcf8fSFrançois Tigeot /* 2e3adcf8fSFrançois Tigeot * Copyright © 2006-2007 Intel Corporation 3e3adcf8fSFrançois Tigeot * 4e3adcf8fSFrançois Tigeot * Permission is hereby granted, free of charge, to any person obtaining a 5e3adcf8fSFrançois Tigeot * copy of this software and associated documentation files (the "Software"), 6e3adcf8fSFrançois Tigeot * to deal in the Software without restriction, including without limitation 7e3adcf8fSFrançois Tigeot * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8e3adcf8fSFrançois Tigeot * and/or sell copies of the Software, and to permit persons to whom the 9e3adcf8fSFrançois Tigeot * Software is furnished to do so, subject to the following conditions: 10e3adcf8fSFrançois Tigeot * 11e3adcf8fSFrançois Tigeot * The above copyright notice and this permission notice (including the next 12e3adcf8fSFrançois Tigeot * paragraph) shall be included in all copies or substantial portions of the 13e3adcf8fSFrançois Tigeot * Software. 14e3adcf8fSFrançois Tigeot * 15e3adcf8fSFrançois Tigeot * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16e3adcf8fSFrançois Tigeot * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17e3adcf8fSFrançois Tigeot * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18e3adcf8fSFrançois Tigeot * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19e3adcf8fSFrançois Tigeot * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20e3adcf8fSFrançois Tigeot * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21e3adcf8fSFrançois Tigeot * DEALINGS IN THE SOFTWARE. 22e3adcf8fSFrançois Tigeot * 23e3adcf8fSFrançois Tigeot * Authors: 24e3adcf8fSFrançois Tigeot * Eric Anholt <eric@anholt.net> 25e3adcf8fSFrançois Tigeot * 26e3adcf8fSFrançois Tigeot * $FreeBSD: src/sys/dev/drm2/i915/intel_crt.c,v 1.1 2012/05/22 11:07:44 kib Exp $ 27e3adcf8fSFrançois Tigeot */ 28e3adcf8fSFrançois Tigeot 29*18e26a6dSFrançois Tigeot #include <drm/drmP.h> 30*18e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 31*18e26a6dSFrançois Tigeot #include <drm/drm_crtc_helper.h> 32*18e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 33*18e26a6dSFrançois Tigeot #include "intel_drv.h" 34e3adcf8fSFrançois Tigeot #include "i915_drm.h" 35e3adcf8fSFrançois Tigeot #include "i915_drv.h" 36e3adcf8fSFrançois Tigeot 37e3adcf8fSFrançois Tigeot /* Here's the desired hotplug mode */ 38e3adcf8fSFrançois Tigeot #define ADPA_HOTPLUG_BITS (ADPA_CRT_HOTPLUG_PERIOD_128 | \ 39e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_WARMUP_10MS | \ 40e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_SAMPLE_4S | \ 41e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_VOLTAGE_50 | \ 42e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_VOLREF_325MV | \ 43e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_ENABLE) 44e3adcf8fSFrançois Tigeot 45e3adcf8fSFrançois Tigeot struct intel_crt { 46e3adcf8fSFrançois Tigeot struct intel_encoder base; 47e3adcf8fSFrançois Tigeot bool force_hotplug_required; 48e3adcf8fSFrançois Tigeot }; 49e3adcf8fSFrançois Tigeot 50e3adcf8fSFrançois Tigeot static struct intel_crt *intel_attached_crt(struct drm_connector *connector) 51e3adcf8fSFrançois Tigeot { 52e3adcf8fSFrançois Tigeot return container_of(intel_attached_encoder(connector), 53e3adcf8fSFrançois Tigeot struct intel_crt, base); 54e3adcf8fSFrançois Tigeot } 55e3adcf8fSFrançois Tigeot 56e3adcf8fSFrançois Tigeot static void intel_crt_dpms(struct drm_encoder *encoder, int mode) 57e3adcf8fSFrançois Tigeot { 58e3adcf8fSFrançois Tigeot struct drm_device *dev = encoder->dev; 59e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 60e3adcf8fSFrançois Tigeot u32 temp, reg; 61e3adcf8fSFrançois Tigeot 62e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 63e3adcf8fSFrançois Tigeot reg = PCH_ADPA; 64e3adcf8fSFrançois Tigeot else 65e3adcf8fSFrançois Tigeot reg = ADPA; 66e3adcf8fSFrançois Tigeot 67e3adcf8fSFrançois Tigeot temp = I915_READ(reg); 68e3adcf8fSFrançois Tigeot temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); 69e3adcf8fSFrançois Tigeot temp &= ~ADPA_DAC_ENABLE; 70e3adcf8fSFrançois Tigeot 71e3adcf8fSFrançois Tigeot switch (mode) { 72e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_ON: 73e3adcf8fSFrançois Tigeot temp |= ADPA_DAC_ENABLE; 74e3adcf8fSFrançois Tigeot break; 75e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_STANDBY: 76e3adcf8fSFrançois Tigeot temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; 77e3adcf8fSFrançois Tigeot break; 78e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_SUSPEND: 79e3adcf8fSFrançois Tigeot temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; 80e3adcf8fSFrançois Tigeot break; 81e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_OFF: 82e3adcf8fSFrançois Tigeot temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; 83e3adcf8fSFrançois Tigeot break; 84e3adcf8fSFrançois Tigeot } 85e3adcf8fSFrançois Tigeot 86e3adcf8fSFrançois Tigeot I915_WRITE(reg, temp); 87e3adcf8fSFrançois Tigeot } 88e3adcf8fSFrançois Tigeot 89e3adcf8fSFrançois Tigeot static int intel_crt_mode_valid(struct drm_connector *connector, 90e3adcf8fSFrançois Tigeot struct drm_display_mode *mode) 91e3adcf8fSFrançois Tigeot { 92e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 93e3adcf8fSFrançois Tigeot 94e3adcf8fSFrançois Tigeot int max_clock = 0; 95e3adcf8fSFrançois Tigeot if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 96e3adcf8fSFrançois Tigeot return MODE_NO_DBLESCAN; 97e3adcf8fSFrançois Tigeot 98e3adcf8fSFrançois Tigeot if (mode->clock < 25000) 99e3adcf8fSFrançois Tigeot return MODE_CLOCK_LOW; 100e3adcf8fSFrançois Tigeot 101e3adcf8fSFrançois Tigeot if (IS_GEN2(dev)) 102e3adcf8fSFrançois Tigeot max_clock = 350000; 103e3adcf8fSFrançois Tigeot else 104e3adcf8fSFrançois Tigeot max_clock = 400000; 105e3adcf8fSFrançois Tigeot if (mode->clock > max_clock) 106e3adcf8fSFrançois Tigeot return MODE_CLOCK_HIGH; 107e3adcf8fSFrançois Tigeot 108e3adcf8fSFrançois Tigeot return MODE_OK; 109e3adcf8fSFrançois Tigeot } 110e3adcf8fSFrançois Tigeot 111e3adcf8fSFrançois Tigeot static bool intel_crt_mode_fixup(struct drm_encoder *encoder, 1126f486c69SFrançois Tigeot const struct drm_display_mode *mode, 113e3adcf8fSFrançois Tigeot struct drm_display_mode *adjusted_mode) 114e3adcf8fSFrançois Tigeot { 115e3adcf8fSFrançois Tigeot return true; 116e3adcf8fSFrançois Tigeot } 117e3adcf8fSFrançois Tigeot 118e3adcf8fSFrançois Tigeot static void intel_crt_mode_set(struct drm_encoder *encoder, 119e3adcf8fSFrançois Tigeot struct drm_display_mode *mode, 120e3adcf8fSFrançois Tigeot struct drm_display_mode *adjusted_mode) 121e3adcf8fSFrançois Tigeot { 122e3adcf8fSFrançois Tigeot 123e3adcf8fSFrançois Tigeot struct drm_device *dev = encoder->dev; 124e3adcf8fSFrançois Tigeot struct drm_crtc *crtc = encoder->crtc; 125e3adcf8fSFrançois Tigeot struct intel_crtc *intel_crtc = to_intel_crtc(crtc); 126e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 127e3adcf8fSFrançois Tigeot int dpll_md_reg; 128e3adcf8fSFrançois Tigeot u32 adpa, dpll_md; 129e3adcf8fSFrançois Tigeot u32 adpa_reg; 130e3adcf8fSFrançois Tigeot 131e3adcf8fSFrançois Tigeot dpll_md_reg = DPLL_MD(intel_crtc->pipe); 132e3adcf8fSFrançois Tigeot 133e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 134e3adcf8fSFrançois Tigeot adpa_reg = PCH_ADPA; 135e3adcf8fSFrançois Tigeot else 136e3adcf8fSFrançois Tigeot adpa_reg = ADPA; 137e3adcf8fSFrançois Tigeot 138e3adcf8fSFrançois Tigeot /* 139e3adcf8fSFrançois Tigeot * Disable separate mode multiplier used when cloning SDVO to CRT 140e3adcf8fSFrançois Tigeot * XXX this needs to be adjusted when we really are cloning 141e3adcf8fSFrançois Tigeot */ 142e3adcf8fSFrançois Tigeot if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) { 143e3adcf8fSFrançois Tigeot dpll_md = I915_READ(dpll_md_reg); 144e3adcf8fSFrançois Tigeot I915_WRITE(dpll_md_reg, 145e3adcf8fSFrançois Tigeot dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); 146e3adcf8fSFrançois Tigeot } 147e3adcf8fSFrançois Tigeot 148e3adcf8fSFrançois Tigeot adpa = ADPA_HOTPLUG_BITS; 149e3adcf8fSFrançois Tigeot if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) 150e3adcf8fSFrançois Tigeot adpa |= ADPA_HSYNC_ACTIVE_HIGH; 151e3adcf8fSFrançois Tigeot if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) 152e3adcf8fSFrançois Tigeot adpa |= ADPA_VSYNC_ACTIVE_HIGH; 153e3adcf8fSFrançois Tigeot 154e3adcf8fSFrançois Tigeot /* For CPT allow 3 pipe config, for others just use A or B */ 155e3adcf8fSFrançois Tigeot if (HAS_PCH_CPT(dev)) 156e3adcf8fSFrançois Tigeot adpa |= PORT_TRANS_SEL_CPT(intel_crtc->pipe); 157e3adcf8fSFrançois Tigeot else if (intel_crtc->pipe == 0) 158e3adcf8fSFrançois Tigeot adpa |= ADPA_PIPE_A_SELECT; 159e3adcf8fSFrançois Tigeot else 160e3adcf8fSFrançois Tigeot adpa |= ADPA_PIPE_B_SELECT; 161e3adcf8fSFrançois Tigeot 162e3adcf8fSFrançois Tigeot if (!HAS_PCH_SPLIT(dev)) 163e3adcf8fSFrançois Tigeot I915_WRITE(BCLRPAT(intel_crtc->pipe), 0); 164e3adcf8fSFrançois Tigeot 165e3adcf8fSFrançois Tigeot I915_WRITE(adpa_reg, adpa); 166e3adcf8fSFrançois Tigeot } 167e3adcf8fSFrançois Tigeot 168e3adcf8fSFrançois Tigeot static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector) 169e3adcf8fSFrançois Tigeot { 170e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 171e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 172e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 173e3adcf8fSFrançois Tigeot u32 adpa; 174e3adcf8fSFrançois Tigeot bool ret; 175e3adcf8fSFrançois Tigeot 176e3adcf8fSFrançois Tigeot /* The first time through, trigger an explicit detection cycle */ 177e3adcf8fSFrançois Tigeot if (crt->force_hotplug_required) { 178e3adcf8fSFrançois Tigeot bool turn_off_dac = HAS_PCH_SPLIT(dev); 179e3adcf8fSFrançois Tigeot u32 save_adpa; 180e3adcf8fSFrançois Tigeot 181e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 0; 182e3adcf8fSFrançois Tigeot 183e3adcf8fSFrançois Tigeot save_adpa = adpa = I915_READ(PCH_ADPA); 184e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa); 185e3adcf8fSFrançois Tigeot 186e3adcf8fSFrançois Tigeot adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; 187e3adcf8fSFrançois Tigeot if (turn_off_dac) 188e3adcf8fSFrançois Tigeot adpa &= ~ADPA_DAC_ENABLE; 189e3adcf8fSFrançois Tigeot 190e3adcf8fSFrançois Tigeot I915_WRITE(PCH_ADPA, adpa); 191e3adcf8fSFrançois Tigeot 192e3adcf8fSFrançois Tigeot if (_intel_wait_for(dev, 193e3adcf8fSFrançois Tigeot (I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, 194e3adcf8fSFrançois Tigeot 1000, 1, "915crt")) 195e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER\n"); 196e3adcf8fSFrançois Tigeot 197e3adcf8fSFrançois Tigeot if (turn_off_dac) { 198e3adcf8fSFrançois Tigeot I915_WRITE(PCH_ADPA, save_adpa); 199e3adcf8fSFrançois Tigeot POSTING_READ(PCH_ADPA); 200e3adcf8fSFrançois Tigeot } 201e3adcf8fSFrançois Tigeot } 202e3adcf8fSFrançois Tigeot 203e3adcf8fSFrançois Tigeot /* Check the status to see if both blue and green are on now */ 204e3adcf8fSFrançois Tigeot adpa = I915_READ(PCH_ADPA); 205e3adcf8fSFrançois Tigeot if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0) 206e3adcf8fSFrançois Tigeot ret = true; 207e3adcf8fSFrançois Tigeot else 208e3adcf8fSFrançois Tigeot ret = false; 209e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("ironlake hotplug adpa=0x%x, result %d\n", adpa, ret); 210e3adcf8fSFrançois Tigeot 211e3adcf8fSFrançois Tigeot return ret; 212e3adcf8fSFrançois Tigeot } 213e3adcf8fSFrançois Tigeot 214e3adcf8fSFrançois Tigeot /** 215e3adcf8fSFrançois Tigeot * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. 216e3adcf8fSFrançois Tigeot * 217e3adcf8fSFrançois Tigeot * Not for i915G/i915GM 218e3adcf8fSFrançois Tigeot * 219e3adcf8fSFrançois Tigeot * \return true if CRT is connected. 220e3adcf8fSFrançois Tigeot * \return false if CRT is disconnected. 221e3adcf8fSFrançois Tigeot */ 222e3adcf8fSFrançois Tigeot static bool intel_crt_detect_hotplug(struct drm_connector *connector) 223e3adcf8fSFrançois Tigeot { 224e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 225e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 226e3adcf8fSFrançois Tigeot u32 hotplug_en, orig, stat; 227e3adcf8fSFrançois Tigeot bool ret = false; 228e3adcf8fSFrançois Tigeot int i, tries = 0; 229e3adcf8fSFrançois Tigeot 230e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 231e3adcf8fSFrançois Tigeot return intel_ironlake_crt_detect_hotplug(connector); 232e3adcf8fSFrançois Tigeot 233e3adcf8fSFrançois Tigeot /* 234e3adcf8fSFrançois Tigeot * On 4 series desktop, CRT detect sequence need to be done twice 235e3adcf8fSFrançois Tigeot * to get a reliable result. 236e3adcf8fSFrançois Tigeot */ 237e3adcf8fSFrançois Tigeot 238e3adcf8fSFrançois Tigeot if (IS_G4X(dev) && !IS_GM45(dev)) 239e3adcf8fSFrançois Tigeot tries = 2; 240e3adcf8fSFrançois Tigeot else 241e3adcf8fSFrançois Tigeot tries = 1; 242e3adcf8fSFrançois Tigeot hotplug_en = orig = I915_READ(PORT_HOTPLUG_EN); 243e3adcf8fSFrançois Tigeot hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; 244e3adcf8fSFrançois Tigeot 245e3adcf8fSFrançois Tigeot for (i = 0; i < tries ; i++) { 246e3adcf8fSFrançois Tigeot /* turn on the FORCE_DETECT */ 247e3adcf8fSFrançois Tigeot I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); 248e3adcf8fSFrançois Tigeot /* wait for FORCE_DETECT to go off */ 249e3adcf8fSFrançois Tigeot if (_intel_wait_for(dev, 250e3adcf8fSFrançois Tigeot (I915_READ(PORT_HOTPLUG_EN) & CRT_HOTPLUG_FORCE_DETECT) == 0, 251e3adcf8fSFrançois Tigeot 1000, 1, "915cr2")) 252e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("timed out waiting for FORCE_DETECT to go off"); 253e3adcf8fSFrançois Tigeot } 254e3adcf8fSFrançois Tigeot 255e3adcf8fSFrançois Tigeot stat = I915_READ(PORT_HOTPLUG_STAT); 256e3adcf8fSFrançois Tigeot if ((stat & CRT_HOTPLUG_MONITOR_MASK) != CRT_HOTPLUG_MONITOR_NONE) 257e3adcf8fSFrançois Tigeot ret = true; 258e3adcf8fSFrançois Tigeot 259e3adcf8fSFrançois Tigeot /* clear the interrupt we just generated, if any */ 260e3adcf8fSFrançois Tigeot I915_WRITE(PORT_HOTPLUG_STAT, CRT_HOTPLUG_INT_STATUS); 261e3adcf8fSFrançois Tigeot 262e3adcf8fSFrançois Tigeot /* and put the bits back */ 263e3adcf8fSFrançois Tigeot I915_WRITE(PORT_HOTPLUG_EN, orig); 264e3adcf8fSFrançois Tigeot 265e3adcf8fSFrançois Tigeot return ret; 266e3adcf8fSFrançois Tigeot } 267e3adcf8fSFrançois Tigeot 268e3adcf8fSFrançois Tigeot static bool intel_crt_detect_ddc(struct drm_connector *connector) 269e3adcf8fSFrançois Tigeot { 270e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 271e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private; 272e3adcf8fSFrançois Tigeot 273e3adcf8fSFrançois Tigeot /* CRT should always be at 0, but check anyway */ 274e3adcf8fSFrançois Tigeot if (crt->base.type != INTEL_OUTPUT_ANALOG) 275e3adcf8fSFrançois Tigeot return false; 276e3adcf8fSFrançois Tigeot 277e3adcf8fSFrançois Tigeot if (intel_ddc_probe(&crt->base, dev_priv->crt_ddc_pin)) { 278e3adcf8fSFrançois Tigeot struct edid *edid; 279e3adcf8fSFrançois Tigeot bool is_digital = false; 280e3adcf8fSFrançois Tigeot 281e3adcf8fSFrançois Tigeot edid = drm_get_edid(connector, 282e3adcf8fSFrançois Tigeot dev_priv->gmbus[dev_priv->crt_ddc_pin]); 283e3adcf8fSFrançois Tigeot /* 284e3adcf8fSFrançois Tigeot * This may be a DVI-I connector with a shared DDC 285e3adcf8fSFrançois Tigeot * link between analog and digital outputs, so we 286e3adcf8fSFrançois Tigeot * have to check the EDID input spec of the attached device. 287e3adcf8fSFrançois Tigeot * 288e3adcf8fSFrançois Tigeot * On the other hand, what should we do if it is a broken EDID? 289e3adcf8fSFrançois Tigeot */ 290e3adcf8fSFrançois Tigeot if (edid != NULL) { 291e3adcf8fSFrançois Tigeot is_digital = edid->input & DRM_EDID_INPUT_DIGITAL; 292e3adcf8fSFrançois Tigeot connector->display_info.raw_edid = NULL; 293e3adcf8fSFrançois Tigeot drm_free(edid, DRM_MEM_KMS); 294e3adcf8fSFrançois Tigeot } 295e3adcf8fSFrançois Tigeot 296e3adcf8fSFrançois Tigeot if (!is_digital) { 297e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); 298e3adcf8fSFrançois Tigeot return true; 299e3adcf8fSFrançois Tigeot } else { 300e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n"); 301e3adcf8fSFrançois Tigeot } 302e3adcf8fSFrançois Tigeot } 303e3adcf8fSFrançois Tigeot 304e3adcf8fSFrançois Tigeot return false; 305e3adcf8fSFrançois Tigeot } 306e3adcf8fSFrançois Tigeot 307e3adcf8fSFrançois Tigeot static enum drm_connector_status 308e3adcf8fSFrançois Tigeot intel_crt_load_detect(struct intel_crt *crt) 309e3adcf8fSFrançois Tigeot { 310e3adcf8fSFrançois Tigeot struct drm_device *dev = crt->base.base.dev; 311e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 312e3adcf8fSFrançois Tigeot uint32_t pipe = to_intel_crtc(crt->base.base.crtc)->pipe; 313e3adcf8fSFrançois Tigeot uint32_t save_bclrpat; 314e3adcf8fSFrançois Tigeot uint32_t save_vtotal; 315e3adcf8fSFrançois Tigeot uint32_t vtotal, vactive; 316e3adcf8fSFrançois Tigeot uint32_t vsample; 317e3adcf8fSFrançois Tigeot uint32_t vblank, vblank_start, vblank_end; 318e3adcf8fSFrançois Tigeot uint32_t dsl; 319e3adcf8fSFrançois Tigeot uint32_t bclrpat_reg; 320e3adcf8fSFrançois Tigeot uint32_t vtotal_reg; 321e3adcf8fSFrançois Tigeot uint32_t vblank_reg; 322e3adcf8fSFrançois Tigeot uint32_t vsync_reg; 323e3adcf8fSFrançois Tigeot uint32_t pipeconf_reg; 324e3adcf8fSFrançois Tigeot uint32_t pipe_dsl_reg; 325e3adcf8fSFrançois Tigeot uint8_t st00; 326e3adcf8fSFrançois Tigeot enum drm_connector_status status; 327e3adcf8fSFrançois Tigeot 328e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("starting load-detect on CRT\n"); 329e3adcf8fSFrançois Tigeot 330e3adcf8fSFrançois Tigeot bclrpat_reg = BCLRPAT(pipe); 331e3adcf8fSFrançois Tigeot vtotal_reg = VTOTAL(pipe); 332e3adcf8fSFrançois Tigeot vblank_reg = VBLANK(pipe); 333e3adcf8fSFrançois Tigeot vsync_reg = VSYNC(pipe); 334e3adcf8fSFrançois Tigeot pipeconf_reg = PIPECONF(pipe); 335e3adcf8fSFrançois Tigeot pipe_dsl_reg = PIPEDSL(pipe); 336e3adcf8fSFrançois Tigeot 337e3adcf8fSFrançois Tigeot save_bclrpat = I915_READ(bclrpat_reg); 338e3adcf8fSFrançois Tigeot save_vtotal = I915_READ(vtotal_reg); 339e3adcf8fSFrançois Tigeot vblank = I915_READ(vblank_reg); 340e3adcf8fSFrançois Tigeot 341e3adcf8fSFrançois Tigeot vtotal = ((save_vtotal >> 16) & 0xfff) + 1; 342e3adcf8fSFrançois Tigeot vactive = (save_vtotal & 0x7ff) + 1; 343e3adcf8fSFrançois Tigeot 344e3adcf8fSFrançois Tigeot vblank_start = (vblank & 0xfff) + 1; 345e3adcf8fSFrançois Tigeot vblank_end = ((vblank >> 16) & 0xfff) + 1; 346e3adcf8fSFrançois Tigeot 347e3adcf8fSFrançois Tigeot /* Set the border color to purple. */ 348e3adcf8fSFrançois Tigeot I915_WRITE(bclrpat_reg, 0x500050); 349e3adcf8fSFrançois Tigeot 350e3adcf8fSFrançois Tigeot if (!IS_GEN2(dev)) { 351e3adcf8fSFrançois Tigeot uint32_t pipeconf = I915_READ(pipeconf_reg); 352e3adcf8fSFrançois Tigeot I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); 353e3adcf8fSFrançois Tigeot POSTING_READ(pipeconf_reg); 354e3adcf8fSFrançois Tigeot /* Wait for next Vblank to substitue 355e3adcf8fSFrançois Tigeot * border color for Color info */ 356e3adcf8fSFrançois Tigeot intel_wait_for_vblank(dev, pipe); 357e3adcf8fSFrançois Tigeot st00 = I915_READ8(VGA_MSR_WRITE); 358e3adcf8fSFrançois Tigeot status = ((st00 & (1 << 4)) != 0) ? 359e3adcf8fSFrançois Tigeot connector_status_connected : 360e3adcf8fSFrançois Tigeot connector_status_disconnected; 361e3adcf8fSFrançois Tigeot 362e3adcf8fSFrançois Tigeot I915_WRITE(pipeconf_reg, pipeconf); 363e3adcf8fSFrançois Tigeot } else { 364e3adcf8fSFrançois Tigeot bool restore_vblank = false; 365e3adcf8fSFrançois Tigeot int count, detect; 366e3adcf8fSFrançois Tigeot 367e3adcf8fSFrançois Tigeot /* 368e3adcf8fSFrançois Tigeot * If there isn't any border, add some. 369e3adcf8fSFrançois Tigeot * Yes, this will flicker 370e3adcf8fSFrançois Tigeot */ 371e3adcf8fSFrançois Tigeot if (vblank_start <= vactive && vblank_end >= vtotal) { 372e3adcf8fSFrançois Tigeot uint32_t vsync = I915_READ(vsync_reg); 373e3adcf8fSFrançois Tigeot uint32_t vsync_start = (vsync & 0xffff) + 1; 374e3adcf8fSFrançois Tigeot 375e3adcf8fSFrançois Tigeot vblank_start = vsync_start; 376e3adcf8fSFrançois Tigeot I915_WRITE(vblank_reg, 377e3adcf8fSFrançois Tigeot (vblank_start - 1) | 378e3adcf8fSFrançois Tigeot ((vblank_end - 1) << 16)); 379e3adcf8fSFrançois Tigeot restore_vblank = true; 380e3adcf8fSFrançois Tigeot } 381e3adcf8fSFrançois Tigeot /* sample in the vertical border, selecting the larger one */ 382e3adcf8fSFrançois Tigeot if (vblank_start - vactive >= vtotal - vblank_end) 383e3adcf8fSFrançois Tigeot vsample = (vblank_start + vactive) >> 1; 384e3adcf8fSFrançois Tigeot else 385e3adcf8fSFrançois Tigeot vsample = (vtotal + vblank_end) >> 1; 386e3adcf8fSFrançois Tigeot 387e3adcf8fSFrançois Tigeot /* 388e3adcf8fSFrançois Tigeot * Wait for the border to be displayed 389e3adcf8fSFrançois Tigeot */ 390e3adcf8fSFrançois Tigeot while (I915_READ(pipe_dsl_reg) >= vactive) 391e3adcf8fSFrançois Tigeot ; 392e3adcf8fSFrançois Tigeot while ((dsl = I915_READ(pipe_dsl_reg)) <= vsample) 393e3adcf8fSFrançois Tigeot ; 394e3adcf8fSFrançois Tigeot /* 395e3adcf8fSFrançois Tigeot * Watch ST00 for an entire scanline 396e3adcf8fSFrançois Tigeot */ 397e3adcf8fSFrançois Tigeot detect = 0; 398e3adcf8fSFrançois Tigeot count = 0; 399e3adcf8fSFrançois Tigeot do { 400e3adcf8fSFrançois Tigeot count++; 401e3adcf8fSFrançois Tigeot /* Read the ST00 VGA status register */ 402e3adcf8fSFrançois Tigeot st00 = I915_READ8(VGA_MSR_WRITE); 403e3adcf8fSFrançois Tigeot if (st00 & (1 << 4)) 404e3adcf8fSFrançois Tigeot detect++; 405e3adcf8fSFrançois Tigeot } while ((I915_READ(pipe_dsl_reg) == dsl)); 406e3adcf8fSFrançois Tigeot 407e3adcf8fSFrançois Tigeot /* restore vblank if necessary */ 408e3adcf8fSFrançois Tigeot if (restore_vblank) 409e3adcf8fSFrançois Tigeot I915_WRITE(vblank_reg, vblank); 410e3adcf8fSFrançois Tigeot /* 411e3adcf8fSFrançois Tigeot * If more than 3/4 of the scanline detected a monitor, 412e3adcf8fSFrançois Tigeot * then it is assumed to be present. This works even on i830, 413e3adcf8fSFrançois Tigeot * where there isn't any way to force the border color across 414e3adcf8fSFrançois Tigeot * the screen 415e3adcf8fSFrançois Tigeot */ 416e3adcf8fSFrançois Tigeot status = detect * 4 > count * 3 ? 417e3adcf8fSFrançois Tigeot connector_status_connected : 418e3adcf8fSFrançois Tigeot connector_status_disconnected; 419e3adcf8fSFrançois Tigeot } 420e3adcf8fSFrançois Tigeot 421e3adcf8fSFrançois Tigeot /* Restore previous settings */ 422e3adcf8fSFrançois Tigeot I915_WRITE(bclrpat_reg, save_bclrpat); 423e3adcf8fSFrançois Tigeot 424e3adcf8fSFrançois Tigeot return status; 425e3adcf8fSFrançois Tigeot } 426e3adcf8fSFrançois Tigeot 427e3adcf8fSFrançois Tigeot static enum drm_connector_status 428e3adcf8fSFrançois Tigeot intel_crt_detect(struct drm_connector *connector, bool force) 429e3adcf8fSFrançois Tigeot { 430e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 431e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 432e3adcf8fSFrançois Tigeot enum drm_connector_status status; 433e3adcf8fSFrançois Tigeot struct intel_load_detect_pipe tmp; 434e3adcf8fSFrançois Tigeot 435e3adcf8fSFrançois Tigeot if (I915_HAS_HOTPLUG(dev)) { 436e3adcf8fSFrançois Tigeot if (intel_crt_detect_hotplug(connector)) { 437e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT detected via hotplug\n"); 438e3adcf8fSFrançois Tigeot return connector_status_connected; 439e3adcf8fSFrançois Tigeot } else { 440e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT not detected via hotplug\n"); 441e3adcf8fSFrançois Tigeot return connector_status_disconnected; 442e3adcf8fSFrançois Tigeot } 443e3adcf8fSFrançois Tigeot } 444e3adcf8fSFrançois Tigeot 445e3adcf8fSFrançois Tigeot if (intel_crt_detect_ddc(connector)) 446e3adcf8fSFrançois Tigeot return connector_status_connected; 447e3adcf8fSFrançois Tigeot 448e3adcf8fSFrançois Tigeot if (!force) 449e3adcf8fSFrançois Tigeot return connector->status; 450e3adcf8fSFrançois Tigeot 451e3adcf8fSFrançois Tigeot /* for pre-945g platforms use load detect */ 452e3adcf8fSFrançois Tigeot if (intel_get_load_detect_pipe(&crt->base, connector, NULL, 453e3adcf8fSFrançois Tigeot &tmp)) { 454e3adcf8fSFrançois Tigeot if (intel_crt_detect_ddc(connector)) 455e3adcf8fSFrançois Tigeot status = connector_status_connected; 456e3adcf8fSFrançois Tigeot else 457e3adcf8fSFrançois Tigeot status = intel_crt_load_detect(crt); 458e3adcf8fSFrançois Tigeot intel_release_load_detect_pipe(&crt->base, connector, 459e3adcf8fSFrançois Tigeot &tmp); 460e3adcf8fSFrançois Tigeot } else 461e3adcf8fSFrançois Tigeot status = connector_status_unknown; 462e3adcf8fSFrançois Tigeot 463e3adcf8fSFrançois Tigeot return status; 464e3adcf8fSFrançois Tigeot } 465e3adcf8fSFrançois Tigeot 466e3adcf8fSFrançois Tigeot static void intel_crt_destroy(struct drm_connector *connector) 467e3adcf8fSFrançois Tigeot { 468e3adcf8fSFrançois Tigeot 469e3adcf8fSFrançois Tigeot #if 0 470e3adcf8fSFrançois Tigeot drm_sysfs_connector_remove(connector); 471e3adcf8fSFrançois Tigeot #endif 472e3adcf8fSFrançois Tigeot drm_connector_cleanup(connector); 473e3adcf8fSFrançois Tigeot drm_free(connector, DRM_MEM_KMS); 474e3adcf8fSFrançois Tigeot } 475e3adcf8fSFrançois Tigeot 476e3adcf8fSFrançois Tigeot static int intel_crt_get_modes(struct drm_connector *connector) 477e3adcf8fSFrançois Tigeot { 478e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 479e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 480e3adcf8fSFrançois Tigeot int ret; 481e3adcf8fSFrançois Tigeot 482e3adcf8fSFrançois Tigeot ret = intel_ddc_get_modes(connector, 483e3adcf8fSFrançois Tigeot dev_priv->gmbus[dev_priv->crt_ddc_pin]); 484e3adcf8fSFrançois Tigeot if (ret || !IS_G4X(dev)) 485e3adcf8fSFrançois Tigeot return ret; 486e3adcf8fSFrançois Tigeot 487e3adcf8fSFrançois Tigeot /* Try to probe digital port for output in DVI-I -> VGA mode. */ 488e3adcf8fSFrançois Tigeot return (intel_ddc_get_modes(connector, 489e3adcf8fSFrançois Tigeot dev_priv->gmbus[GMBUS_PORT_DPB])); 490e3adcf8fSFrançois Tigeot } 491e3adcf8fSFrançois Tigeot 492e3adcf8fSFrançois Tigeot static int intel_crt_set_property(struct drm_connector *connector, 493e3adcf8fSFrançois Tigeot struct drm_property *property, 494e3adcf8fSFrançois Tigeot uint64_t value) 495e3adcf8fSFrançois Tigeot { 496e3adcf8fSFrançois Tigeot return 0; 497e3adcf8fSFrançois Tigeot } 498e3adcf8fSFrançois Tigeot 499e3adcf8fSFrançois Tigeot static void intel_crt_reset(struct drm_connector *connector) 500e3adcf8fSFrançois Tigeot { 501e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 502e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 503e3adcf8fSFrançois Tigeot 504e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 505e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 1; 506e3adcf8fSFrançois Tigeot } 507e3adcf8fSFrançois Tigeot 508e3adcf8fSFrançois Tigeot /* 509e3adcf8fSFrançois Tigeot * Routines for controlling stuff on the analog port 510e3adcf8fSFrançois Tigeot */ 511e3adcf8fSFrançois Tigeot 512e3adcf8fSFrançois Tigeot static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = { 513e3adcf8fSFrançois Tigeot .dpms = intel_crt_dpms, 514e3adcf8fSFrançois Tigeot .mode_fixup = intel_crt_mode_fixup, 515e3adcf8fSFrançois Tigeot .prepare = intel_encoder_prepare, 516e3adcf8fSFrançois Tigeot .commit = intel_encoder_commit, 517e3adcf8fSFrançois Tigeot .mode_set = intel_crt_mode_set, 518e3adcf8fSFrançois Tigeot }; 519e3adcf8fSFrançois Tigeot 520e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_crt_connector_funcs = { 521e3adcf8fSFrançois Tigeot .reset = intel_crt_reset, 522e3adcf8fSFrançois Tigeot .dpms = drm_helper_connector_dpms, 523e3adcf8fSFrançois Tigeot .detect = intel_crt_detect, 524e3adcf8fSFrançois Tigeot .fill_modes = drm_helper_probe_single_connector_modes, 525e3adcf8fSFrançois Tigeot .destroy = intel_crt_destroy, 526e3adcf8fSFrançois Tigeot .set_property = intel_crt_set_property, 527e3adcf8fSFrançois Tigeot }; 528e3adcf8fSFrançois Tigeot 529e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { 530e3adcf8fSFrançois Tigeot .mode_valid = intel_crt_mode_valid, 531e3adcf8fSFrançois Tigeot .get_modes = intel_crt_get_modes, 532e3adcf8fSFrançois Tigeot .best_encoder = intel_best_encoder, 533e3adcf8fSFrançois Tigeot }; 534e3adcf8fSFrançois Tigeot 535e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_crt_enc_funcs = { 536e3adcf8fSFrançois Tigeot .destroy = intel_encoder_destroy, 537e3adcf8fSFrançois Tigeot }; 538e3adcf8fSFrançois Tigeot 539e3adcf8fSFrançois Tigeot static int intel_no_crt_dmi_callback(const struct dmi_system_id *id) 540e3adcf8fSFrançois Tigeot { 541e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("Skipping CRT initialization for %s\n", id->ident); 542e3adcf8fSFrançois Tigeot return 1; 543e3adcf8fSFrançois Tigeot } 544e3adcf8fSFrançois Tigeot 545e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_crt[] = { 546e3adcf8fSFrançois Tigeot { 547e3adcf8fSFrançois Tigeot .callback = intel_no_crt_dmi_callback, 548e3adcf8fSFrançois Tigeot .ident = "ACER ZGB", 549e3adcf8fSFrançois Tigeot .matches = { 550e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "ACER"), 551e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), 552e3adcf8fSFrançois Tigeot }, 553e3adcf8fSFrançois Tigeot }, 554e3adcf8fSFrançois Tigeot { } 555e3adcf8fSFrançois Tigeot }; 556e3adcf8fSFrançois Tigeot 557e3adcf8fSFrançois Tigeot void intel_crt_init(struct drm_device *dev) 558e3adcf8fSFrançois Tigeot { 559e3adcf8fSFrançois Tigeot struct drm_connector *connector; 560e3adcf8fSFrançois Tigeot struct intel_crt *crt; 561e3adcf8fSFrançois Tigeot struct intel_connector *intel_connector; 562e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 563e3adcf8fSFrançois Tigeot 564e3adcf8fSFrançois Tigeot /* Skip machines without VGA that falsely report hotplug events */ 565e3adcf8fSFrançois Tigeot if (dmi_check_system(intel_no_crt)) 566e3adcf8fSFrançois Tigeot return; 567e3adcf8fSFrançois Tigeot 568e3adcf8fSFrançois Tigeot crt = kmalloc(sizeof(struct intel_crt), DRM_MEM_KMS, M_WAITOK | M_ZERO); 569e3adcf8fSFrançois Tigeot intel_connector = kmalloc(sizeof(struct intel_connector), DRM_MEM_KMS, 570e3adcf8fSFrançois Tigeot M_WAITOK | M_ZERO); 571e3adcf8fSFrançois Tigeot 572e3adcf8fSFrançois Tigeot connector = &intel_connector->base; 573e3adcf8fSFrançois Tigeot drm_connector_init(dev, &intel_connector->base, 574e3adcf8fSFrançois Tigeot &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); 575e3adcf8fSFrançois Tigeot 576e3adcf8fSFrançois Tigeot drm_encoder_init(dev, &crt->base.base, &intel_crt_enc_funcs, 577e3adcf8fSFrançois Tigeot DRM_MODE_ENCODER_DAC); 578e3adcf8fSFrançois Tigeot 579e3adcf8fSFrançois Tigeot intel_connector_attach_encoder(intel_connector, &crt->base); 580e3adcf8fSFrançois Tigeot 581e3adcf8fSFrançois Tigeot crt->base.type = INTEL_OUTPUT_ANALOG; 582e3adcf8fSFrançois Tigeot crt->base.clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT | 583e3adcf8fSFrançois Tigeot 1 << INTEL_ANALOG_CLONE_BIT | 584e3adcf8fSFrançois Tigeot 1 << INTEL_SDVO_LVDS_CLONE_BIT); 585e3adcf8fSFrançois Tigeot crt->base.crtc_mask = (1 << 0) | (1 << 1); 586e3adcf8fSFrançois Tigeot if (IS_GEN2(dev)) 587e3adcf8fSFrançois Tigeot connector->interlace_allowed = 0; 588e3adcf8fSFrançois Tigeot else 589e3adcf8fSFrançois Tigeot connector->interlace_allowed = 1; 590e3adcf8fSFrançois Tigeot connector->doublescan_allowed = 0; 591e3adcf8fSFrançois Tigeot 592e3adcf8fSFrançois Tigeot drm_encoder_helper_add(&crt->base.base, &intel_crt_helper_funcs); 593e3adcf8fSFrançois Tigeot drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); 594e3adcf8fSFrançois Tigeot 595e3adcf8fSFrançois Tigeot #if 0 596e3adcf8fSFrançois Tigeot drm_sysfs_connector_add(connector); 597e3adcf8fSFrançois Tigeot #endif 598e3adcf8fSFrançois Tigeot 599e3adcf8fSFrançois Tigeot if (I915_HAS_HOTPLUG(dev)) 600e3adcf8fSFrançois Tigeot connector->polled = DRM_CONNECTOR_POLL_HPD; 601e3adcf8fSFrançois Tigeot else 602e3adcf8fSFrançois Tigeot connector->polled = DRM_CONNECTOR_POLL_CONNECT; 603e3adcf8fSFrançois Tigeot 604e3adcf8fSFrançois Tigeot /* 605e3adcf8fSFrançois Tigeot * Configure the automatic hotplug detection stuff 606e3adcf8fSFrançois Tigeot */ 607e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 0; 608e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) { 609e3adcf8fSFrançois Tigeot u32 adpa; 610e3adcf8fSFrançois Tigeot 611e3adcf8fSFrançois Tigeot adpa = I915_READ(PCH_ADPA); 612e3adcf8fSFrançois Tigeot adpa &= ~ADPA_CRT_HOTPLUG_MASK; 613e3adcf8fSFrançois Tigeot adpa |= ADPA_HOTPLUG_BITS; 614e3adcf8fSFrançois Tigeot I915_WRITE(PCH_ADPA, adpa); 615e3adcf8fSFrançois Tigeot POSTING_READ(PCH_ADPA); 616e3adcf8fSFrançois Tigeot 617e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("pch crt adpa set to 0x%x\n", adpa); 618e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 1; 619e3adcf8fSFrançois Tigeot } 620e3adcf8fSFrançois Tigeot 621e3adcf8fSFrançois Tigeot dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS; 622e3adcf8fSFrançois Tigeot } 623