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 275d0b1887SFrançois Tigeot #include <linux/dmi.h> 285d0b1887SFrançois Tigeot #include <linux/i2c.h> 2918e26a6dSFrançois Tigeot #include <drm/drmP.h> 3018e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 3118e26a6dSFrançois Tigeot #include <drm/drm_crtc_helper.h> 3218e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 3318e26a6dSFrançois Tigeot #include "intel_drv.h" 345c6c6f23SFrançois Tigeot #include <drm/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; 47e9243325SFrançois Tigeot /* DPMS state is stored in the connector, which we need in the 48e9243325SFrançois Tigeot * encoder's enable/disable callbacks */ 49e9243325SFrançois Tigeot struct intel_connector *connector; 50e3adcf8fSFrançois Tigeot bool force_hotplug_required; 51e9243325SFrançois Tigeot u32 adpa_reg; 52e3adcf8fSFrançois Tigeot }; 53e3adcf8fSFrançois Tigeot 5419df918dSFrançois Tigeot static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder) 55e3adcf8fSFrançois Tigeot { 5619df918dSFrançois Tigeot return container_of(encoder, struct intel_crt, base); 5719df918dSFrançois Tigeot } 5819df918dSFrançois Tigeot 599edbd4a0SFrançois Tigeot static struct intel_crt *intel_attached_crt(struct drm_connector *connector) 609edbd4a0SFrançois Tigeot { 619edbd4a0SFrançois Tigeot return intel_encoder_to_crt(intel_attached_encoder(connector)); 629edbd4a0SFrançois Tigeot } 639edbd4a0SFrançois Tigeot 6419df918dSFrançois Tigeot static bool intel_crt_get_hw_state(struct intel_encoder *encoder, 6519df918dSFrançois Tigeot enum i915_pipe *pipe) 6619df918dSFrançois Tigeot { 6719df918dSFrançois Tigeot struct drm_device *dev = encoder->base.dev; 68e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 6919df918dSFrançois Tigeot struct intel_crt *crt = intel_encoder_to_crt(encoder); 70ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 7119df918dSFrançois Tigeot u32 tmp; 72e3adcf8fSFrançois Tigeot 73ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(encoder); 74ba55f2f5SFrançois Tigeot if (!intel_display_power_enabled(dev_priv, power_domain)) 75ba55f2f5SFrançois Tigeot return false; 76ba55f2f5SFrançois Tigeot 7719df918dSFrançois Tigeot tmp = I915_READ(crt->adpa_reg); 7819df918dSFrançois Tigeot 7919df918dSFrançois Tigeot if (!(tmp & ADPA_DAC_ENABLE)) 8019df918dSFrançois Tigeot return false; 8119df918dSFrançois Tigeot 8219df918dSFrançois Tigeot if (HAS_PCH_CPT(dev)) 8319df918dSFrançois Tigeot *pipe = PORT_TO_PIPE_CPT(tmp); 84e3adcf8fSFrançois Tigeot else 8519df918dSFrançois Tigeot *pipe = PORT_TO_PIPE(tmp); 86e3adcf8fSFrançois Tigeot 8719df918dSFrançois Tigeot return true; 8819df918dSFrançois Tigeot } 8919df918dSFrançois Tigeot 909edbd4a0SFrançois Tigeot static unsigned int intel_crt_get_flags(struct intel_encoder *encoder) 915d0b1887SFrançois Tigeot { 925d0b1887SFrançois Tigeot struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; 935d0b1887SFrançois Tigeot struct intel_crt *crt = intel_encoder_to_crt(encoder); 945d0b1887SFrançois Tigeot u32 tmp, flags = 0; 955d0b1887SFrançois Tigeot 965d0b1887SFrançois Tigeot tmp = I915_READ(crt->adpa_reg); 975d0b1887SFrançois Tigeot 985d0b1887SFrançois Tigeot if (tmp & ADPA_HSYNC_ACTIVE_HIGH) 995d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_PHSYNC; 1005d0b1887SFrançois Tigeot else 1015d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_NHSYNC; 1025d0b1887SFrançois Tigeot 1035d0b1887SFrançois Tigeot if (tmp & ADPA_VSYNC_ACTIVE_HIGH) 1045d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_PVSYNC; 1055d0b1887SFrançois Tigeot else 1065d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_NVSYNC; 1075d0b1887SFrançois Tigeot 1089edbd4a0SFrançois Tigeot return flags; 1099edbd4a0SFrançois Tigeot } 1109edbd4a0SFrançois Tigeot 1119edbd4a0SFrançois Tigeot static void intel_crt_get_config(struct intel_encoder *encoder, 1129edbd4a0SFrançois Tigeot struct intel_crtc_config *pipe_config) 1139edbd4a0SFrançois Tigeot { 1149edbd4a0SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 1159edbd4a0SFrançois Tigeot int dotclock; 1169edbd4a0SFrançois Tigeot 1179edbd4a0SFrançois Tigeot pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder); 1189edbd4a0SFrançois Tigeot 1199edbd4a0SFrançois Tigeot dotclock = pipe_config->port_clock; 1209edbd4a0SFrançois Tigeot 1219edbd4a0SFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 1229edbd4a0SFrançois Tigeot ironlake_check_encoder_dotclock(pipe_config, dotclock); 1239edbd4a0SFrançois Tigeot 1249edbd4a0SFrançois Tigeot pipe_config->adjusted_mode.crtc_clock = dotclock; 1259edbd4a0SFrançois Tigeot } 1269edbd4a0SFrançois Tigeot 1279edbd4a0SFrançois Tigeot static void hsw_crt_get_config(struct intel_encoder *encoder, 1289edbd4a0SFrançois Tigeot struct intel_crtc_config *pipe_config) 1299edbd4a0SFrançois Tigeot { 1309edbd4a0SFrançois Tigeot intel_ddi_get_config(encoder, pipe_config); 1319edbd4a0SFrançois Tigeot 1329edbd4a0SFrançois Tigeot pipe_config->adjusted_mode.flags &= ~(DRM_MODE_FLAG_PHSYNC | 1339edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | 1349edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PVSYNC | 1359edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NVSYNC); 1369edbd4a0SFrançois Tigeot pipe_config->adjusted_mode.flags |= intel_crt_get_flags(encoder); 1375d0b1887SFrançois Tigeot } 1385d0b1887SFrançois Tigeot 139*24edb884SFrançois Tigeot static void hsw_crt_pre_enable(struct intel_encoder *encoder) 140*24edb884SFrançois Tigeot { 141*24edb884SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 142*24edb884SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 143*24edb884SFrançois Tigeot 144*24edb884SFrançois Tigeot WARN(I915_READ(SPLL_CTL) & SPLL_PLL_ENABLE, "SPLL already enabled\n"); 145*24edb884SFrançois Tigeot I915_WRITE(SPLL_CTL, 146*24edb884SFrançois Tigeot SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | SPLL_PLL_SSC); 147*24edb884SFrançois Tigeot POSTING_READ(SPLL_CTL); 148*24edb884SFrançois Tigeot udelay(20); 149*24edb884SFrançois Tigeot } 150*24edb884SFrançois Tigeot 15119df918dSFrançois Tigeot /* Note: The caller is required to filter out dpms modes not supported by the 15219df918dSFrançois Tigeot * platform. */ 15319df918dSFrançois Tigeot static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) 15419df918dSFrançois Tigeot { 15519df918dSFrançois Tigeot struct drm_device *dev = encoder->base.dev; 15619df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 15719df918dSFrançois Tigeot struct intel_crt *crt = intel_encoder_to_crt(encoder); 158ba55f2f5SFrançois Tigeot struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); 159ba55f2f5SFrançois Tigeot struct drm_display_mode *adjusted_mode = &crtc->config.adjusted_mode; 160ba55f2f5SFrançois Tigeot u32 adpa; 16119df918dSFrançois Tigeot 162ba55f2f5SFrançois Tigeot if (INTEL_INFO(dev)->gen >= 5) 163ba55f2f5SFrançois Tigeot adpa = ADPA_HOTPLUG_BITS; 164ba55f2f5SFrançois Tigeot else 165ba55f2f5SFrançois Tigeot adpa = 0; 166ba55f2f5SFrançois Tigeot 167ba55f2f5SFrançois Tigeot if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) 168ba55f2f5SFrançois Tigeot adpa |= ADPA_HSYNC_ACTIVE_HIGH; 169ba55f2f5SFrançois Tigeot if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) 170ba55f2f5SFrançois Tigeot adpa |= ADPA_VSYNC_ACTIVE_HIGH; 171ba55f2f5SFrançois Tigeot 172ba55f2f5SFrançois Tigeot /* For CPT allow 3 pipe config, for others just use A or B */ 173ba55f2f5SFrançois Tigeot if (HAS_PCH_LPT(dev)) 174ba55f2f5SFrançois Tigeot ; /* Those bits don't exist here */ 175ba55f2f5SFrançois Tigeot else if (HAS_PCH_CPT(dev)) 176ba55f2f5SFrançois Tigeot adpa |= PORT_TRANS_SEL_CPT(crtc->pipe); 177ba55f2f5SFrançois Tigeot else if (crtc->pipe == 0) 178ba55f2f5SFrançois Tigeot adpa |= ADPA_PIPE_A_SELECT; 179ba55f2f5SFrançois Tigeot else 180ba55f2f5SFrançois Tigeot adpa |= ADPA_PIPE_B_SELECT; 181ba55f2f5SFrançois Tigeot 182ba55f2f5SFrançois Tigeot if (!HAS_PCH_SPLIT(dev)) 183ba55f2f5SFrançois Tigeot I915_WRITE(BCLRPAT(crtc->pipe), 0); 184e3adcf8fSFrançois Tigeot 185e3adcf8fSFrançois Tigeot switch (mode) { 186e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_ON: 187ba55f2f5SFrançois Tigeot adpa |= ADPA_DAC_ENABLE; 188e3adcf8fSFrançois Tigeot break; 189e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_STANDBY: 190ba55f2f5SFrançois Tigeot adpa |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; 191e3adcf8fSFrançois Tigeot break; 192e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_SUSPEND: 193ba55f2f5SFrançois Tigeot adpa |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; 194e3adcf8fSFrançois Tigeot break; 195e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_OFF: 196ba55f2f5SFrançois Tigeot adpa |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; 197e3adcf8fSFrançois Tigeot break; 198e3adcf8fSFrançois Tigeot } 199e3adcf8fSFrançois Tigeot 200ba55f2f5SFrançois Tigeot I915_WRITE(crt->adpa_reg, adpa); 20119df918dSFrançois Tigeot } 20219df918dSFrançois Tigeot 20319df918dSFrançois Tigeot static void intel_disable_crt(struct intel_encoder *encoder) 20419df918dSFrançois Tigeot { 20519df918dSFrançois Tigeot intel_crt_set_dpms(encoder, DRM_MODE_DPMS_OFF); 20619df918dSFrançois Tigeot } 20719df918dSFrançois Tigeot 208*24edb884SFrançois Tigeot 209*24edb884SFrançois Tigeot static void hsw_crt_post_disable(struct intel_encoder *encoder) 210*24edb884SFrançois Tigeot { 211*24edb884SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 212*24edb884SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 213*24edb884SFrançois Tigeot uint32_t val; 214*24edb884SFrançois Tigeot 215*24edb884SFrançois Tigeot DRM_DEBUG_KMS("Disabling SPLL\n"); 216*24edb884SFrançois Tigeot val = I915_READ(SPLL_CTL); 217*24edb884SFrançois Tigeot WARN_ON(!(val & SPLL_PLL_ENABLE)); 218*24edb884SFrançois Tigeot I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE); 219*24edb884SFrançois Tigeot POSTING_READ(SPLL_CTL); 220*24edb884SFrançois Tigeot } 221*24edb884SFrançois Tigeot 22219df918dSFrançois Tigeot static void intel_enable_crt(struct intel_encoder *encoder) 22319df918dSFrançois Tigeot { 22419df918dSFrançois Tigeot struct intel_crt *crt = intel_encoder_to_crt(encoder); 22519df918dSFrançois Tigeot 22619df918dSFrançois Tigeot intel_crt_set_dpms(encoder, crt->connector->base.dpms); 22719df918dSFrançois Tigeot } 22819df918dSFrançois Tigeot 2295d0b1887SFrançois Tigeot /* Special dpms function to support cloning between dvo/sdvo/crt. */ 23019df918dSFrançois Tigeot static void intel_crt_dpms(struct drm_connector *connector, int mode) 23119df918dSFrançois Tigeot { 23219df918dSFrançois Tigeot struct drm_device *dev = connector->dev; 23319df918dSFrançois Tigeot struct intel_encoder *encoder = intel_attached_encoder(connector); 23419df918dSFrançois Tigeot struct drm_crtc *crtc; 23519df918dSFrançois Tigeot int old_dpms; 23619df918dSFrançois Tigeot 23719df918dSFrançois Tigeot /* PCH platforms and VLV only support on/off. */ 23819df918dSFrançois Tigeot if (INTEL_INFO(dev)->gen >= 5 && mode != DRM_MODE_DPMS_ON) 23919df918dSFrançois Tigeot mode = DRM_MODE_DPMS_OFF; 24019df918dSFrançois Tigeot 24119df918dSFrançois Tigeot if (mode == connector->dpms) 24219df918dSFrançois Tigeot return; 24319df918dSFrançois Tigeot 24419df918dSFrançois Tigeot old_dpms = connector->dpms; 24519df918dSFrançois Tigeot connector->dpms = mode; 24619df918dSFrançois Tigeot 24719df918dSFrançois Tigeot /* Only need to change hw state when actually enabled */ 24819df918dSFrançois Tigeot crtc = encoder->base.crtc; 24919df918dSFrançois Tigeot if (!crtc) { 25019df918dSFrançois Tigeot encoder->connectors_active = false; 25119df918dSFrançois Tigeot return; 25219df918dSFrançois Tigeot } 25319df918dSFrançois Tigeot 25419df918dSFrançois Tigeot /* We need the pipe to run for anything but OFF. */ 25519df918dSFrançois Tigeot if (mode == DRM_MODE_DPMS_OFF) 25619df918dSFrançois Tigeot encoder->connectors_active = false; 25719df918dSFrançois Tigeot else 25819df918dSFrançois Tigeot encoder->connectors_active = true; 25919df918dSFrançois Tigeot 2605d0b1887SFrançois Tigeot /* We call connector dpms manually below in case pipe dpms doesn't 2615d0b1887SFrançois Tigeot * change due to cloning. */ 26219df918dSFrançois Tigeot if (mode < old_dpms) { 26319df918dSFrançois Tigeot /* From off to on, enable the pipe first. */ 26419df918dSFrançois Tigeot intel_crtc_update_dpms(crtc); 26519df918dSFrançois Tigeot 26619df918dSFrançois Tigeot intel_crt_set_dpms(encoder, mode); 26719df918dSFrançois Tigeot } else { 26819df918dSFrançois Tigeot intel_crt_set_dpms(encoder, mode); 26919df918dSFrançois Tigeot 27019df918dSFrançois Tigeot intel_crtc_update_dpms(crtc); 27119df918dSFrançois Tigeot } 27219df918dSFrançois Tigeot 27319df918dSFrançois Tigeot intel_modeset_check_state(connector->dev); 274e3adcf8fSFrançois Tigeot } 275e3adcf8fSFrançois Tigeot 2769edbd4a0SFrançois Tigeot static enum drm_mode_status 2779edbd4a0SFrançois Tigeot intel_crt_mode_valid(struct drm_connector *connector, 278e3adcf8fSFrançois Tigeot struct drm_display_mode *mode) 279e3adcf8fSFrançois Tigeot { 280e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 281e3adcf8fSFrançois Tigeot 282e3adcf8fSFrançois Tigeot int max_clock = 0; 283e3adcf8fSFrançois Tigeot if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 284e3adcf8fSFrançois Tigeot return MODE_NO_DBLESCAN; 285e3adcf8fSFrançois Tigeot 286e3adcf8fSFrançois Tigeot if (mode->clock < 25000) 287e3adcf8fSFrançois Tigeot return MODE_CLOCK_LOW; 288e3adcf8fSFrançois Tigeot 289e3adcf8fSFrançois Tigeot if (IS_GEN2(dev)) 290e3adcf8fSFrançois Tigeot max_clock = 350000; 291e3adcf8fSFrançois Tigeot else 292e3adcf8fSFrançois Tigeot max_clock = 400000; 293e3adcf8fSFrançois Tigeot if (mode->clock > max_clock) 294e3adcf8fSFrançois Tigeot return MODE_CLOCK_HIGH; 295e3adcf8fSFrançois Tigeot 29619df918dSFrançois Tigeot /* The FDI receiver on LPT only supports 8bpc and only has 2 lanes. */ 29719df918dSFrançois Tigeot if (HAS_PCH_LPT(dev) && 29819df918dSFrançois Tigeot (ironlake_get_lanes_required(mode->clock, 270000, 24) > 2)) 29919df918dSFrançois Tigeot return MODE_CLOCK_HIGH; 30019df918dSFrançois Tigeot 301e3adcf8fSFrançois Tigeot return MODE_OK; 302e3adcf8fSFrançois Tigeot } 303e3adcf8fSFrançois Tigeot 3048e26cdf6SFrançois Tigeot static bool intel_crt_compute_config(struct intel_encoder *encoder, 3058e26cdf6SFrançois Tigeot struct intel_crtc_config *pipe_config) 306e3adcf8fSFrançois Tigeot { 3078e26cdf6SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 3088e26cdf6SFrançois Tigeot 3098e26cdf6SFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 3108e26cdf6SFrançois Tigeot pipe_config->has_pch_encoder = true; 3118e26cdf6SFrançois Tigeot 3125d0b1887SFrançois Tigeot /* LPT FDI RX only supports 8bpc. */ 3135d0b1887SFrançois Tigeot if (HAS_PCH_LPT(dev)) 3145d0b1887SFrançois Tigeot pipe_config->pipe_bpp = 24; 3155d0b1887SFrançois Tigeot 316ba55f2f5SFrançois Tigeot /* FDI must always be 2.7 GHz */ 317*24edb884SFrançois Tigeot if (HAS_DDI(dev)) { 318*24edb884SFrançois Tigeot pipe_config->ddi_pll_sel = PORT_CLK_SEL_SPLL; 319ba55f2f5SFrançois Tigeot pipe_config->port_clock = 135000 * 2; 320*24edb884SFrançois Tigeot } 321ba55f2f5SFrançois Tigeot 322e3adcf8fSFrançois Tigeot return true; 323e3adcf8fSFrançois Tigeot } 324e3adcf8fSFrançois Tigeot 325e3adcf8fSFrançois Tigeot static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector) 326e3adcf8fSFrançois Tigeot { 327e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 328e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 329e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 330e3adcf8fSFrançois Tigeot u32 adpa; 331e3adcf8fSFrançois Tigeot bool ret; 332e3adcf8fSFrançois Tigeot 333e3adcf8fSFrançois Tigeot /* The first time through, trigger an explicit detection cycle */ 334e3adcf8fSFrançois Tigeot if (crt->force_hotplug_required) { 335e3adcf8fSFrançois Tigeot bool turn_off_dac = HAS_PCH_SPLIT(dev); 336e3adcf8fSFrançois Tigeot u32 save_adpa; 337e3adcf8fSFrançois Tigeot 338e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 0; 339e3adcf8fSFrançois Tigeot 340a2fdbec6SFrançois Tigeot save_adpa = adpa = I915_READ(crt->adpa_reg); 341e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa); 342e3adcf8fSFrançois Tigeot 343e3adcf8fSFrançois Tigeot adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; 344e3adcf8fSFrançois Tigeot if (turn_off_dac) 345e3adcf8fSFrançois Tigeot adpa &= ~ADPA_DAC_ENABLE; 346e3adcf8fSFrançois Tigeot 347a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, adpa); 348e3adcf8fSFrançois Tigeot 349a2fdbec6SFrançois Tigeot if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, 35019df918dSFrançois Tigeot 1000)) 35119df918dSFrançois Tigeot DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER"); 352e3adcf8fSFrançois Tigeot 353e3adcf8fSFrançois Tigeot if (turn_off_dac) { 354a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, save_adpa); 355a2fdbec6SFrançois Tigeot POSTING_READ(crt->adpa_reg); 356e3adcf8fSFrançois Tigeot } 357e3adcf8fSFrançois Tigeot } 358e3adcf8fSFrançois Tigeot 359e3adcf8fSFrançois Tigeot /* Check the status to see if both blue and green are on now */ 360a2fdbec6SFrançois Tigeot adpa = I915_READ(crt->adpa_reg); 361e3adcf8fSFrançois Tigeot if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0) 362e3adcf8fSFrançois Tigeot ret = true; 363e3adcf8fSFrançois Tigeot else 364e3adcf8fSFrançois Tigeot ret = false; 365e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("ironlake hotplug adpa=0x%x, result %d\n", adpa, ret); 366e3adcf8fSFrançois Tigeot 367e3adcf8fSFrançois Tigeot return ret; 368e3adcf8fSFrançois Tigeot } 369e3adcf8fSFrançois Tigeot 37019df918dSFrançois Tigeot static bool valleyview_crt_detect_hotplug(struct drm_connector *connector) 37119df918dSFrançois Tigeot { 37219df918dSFrançois Tigeot struct drm_device *dev = connector->dev; 373a2fdbec6SFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 37419df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 37519df918dSFrançois Tigeot u32 adpa; 37619df918dSFrançois Tigeot bool ret; 37719df918dSFrançois Tigeot u32 save_adpa; 37819df918dSFrançois Tigeot 379a2fdbec6SFrançois Tigeot save_adpa = adpa = I915_READ(crt->adpa_reg); 38019df918dSFrançois Tigeot DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa); 38119df918dSFrançois Tigeot 38219df918dSFrançois Tigeot adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; 38319df918dSFrançois Tigeot 384a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, adpa); 38519df918dSFrançois Tigeot 386a2fdbec6SFrançois Tigeot if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, 38719df918dSFrançois Tigeot 1000)) { 38819df918dSFrançois Tigeot DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER"); 389a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, save_adpa); 39019df918dSFrançois Tigeot } 39119df918dSFrançois Tigeot 39219df918dSFrançois Tigeot /* Check the status to see if both blue and green are on now */ 393a2fdbec6SFrançois Tigeot adpa = I915_READ(crt->adpa_reg); 39419df918dSFrançois Tigeot if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0) 39519df918dSFrançois Tigeot ret = true; 39619df918dSFrançois Tigeot else 39719df918dSFrançois Tigeot ret = false; 39819df918dSFrançois Tigeot 39919df918dSFrançois Tigeot DRM_DEBUG_KMS("valleyview hotplug adpa=0x%x, result %d\n", adpa, ret); 40019df918dSFrançois Tigeot 40119df918dSFrançois Tigeot return ret; 40219df918dSFrançois Tigeot } 40319df918dSFrançois Tigeot 404e3adcf8fSFrançois Tigeot /** 405e3adcf8fSFrançois Tigeot * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. 406e3adcf8fSFrançois Tigeot * 407e3adcf8fSFrançois Tigeot * Not for i915G/i915GM 408e3adcf8fSFrançois Tigeot * 409e3adcf8fSFrançois Tigeot * \return true if CRT is connected. 410e3adcf8fSFrançois Tigeot * \return false if CRT is disconnected. 411e3adcf8fSFrançois Tigeot */ 412e3adcf8fSFrançois Tigeot static bool intel_crt_detect_hotplug(struct drm_connector *connector) 413e3adcf8fSFrançois Tigeot { 414e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 415e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 416e3adcf8fSFrançois Tigeot u32 hotplug_en, orig, stat; 417e3adcf8fSFrançois Tigeot bool ret = false; 418e3adcf8fSFrançois Tigeot int i, tries = 0; 419e3adcf8fSFrançois Tigeot 420e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 421e3adcf8fSFrançois Tigeot return intel_ironlake_crt_detect_hotplug(connector); 422e3adcf8fSFrançois Tigeot 42319df918dSFrançois Tigeot if (IS_VALLEYVIEW(dev)) 42419df918dSFrançois Tigeot return valleyview_crt_detect_hotplug(connector); 42519df918dSFrançois Tigeot 426e3adcf8fSFrançois Tigeot /* 427e3adcf8fSFrançois Tigeot * On 4 series desktop, CRT detect sequence need to be done twice 428e3adcf8fSFrançois Tigeot * to get a reliable result. 429e3adcf8fSFrançois Tigeot */ 430e3adcf8fSFrançois Tigeot 431e3adcf8fSFrançois Tigeot if (IS_G4X(dev) && !IS_GM45(dev)) 432e3adcf8fSFrançois Tigeot tries = 2; 433e3adcf8fSFrançois Tigeot else 434e3adcf8fSFrançois Tigeot tries = 1; 435e3adcf8fSFrançois Tigeot hotplug_en = orig = I915_READ(PORT_HOTPLUG_EN); 436e3adcf8fSFrançois Tigeot hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; 437e3adcf8fSFrançois Tigeot 438e3adcf8fSFrançois Tigeot for (i = 0; i < tries ; i++) { 439e3adcf8fSFrançois Tigeot /* turn on the FORCE_DETECT */ 440e3adcf8fSFrançois Tigeot I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); 441e3adcf8fSFrançois Tigeot /* wait for FORCE_DETECT to go off */ 44219df918dSFrançois Tigeot if (wait_for((I915_READ(PORT_HOTPLUG_EN) & 44319df918dSFrançois Tigeot CRT_HOTPLUG_FORCE_DETECT) == 0, 44419df918dSFrançois Tigeot 1000)) 445e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("timed out waiting for FORCE_DETECT to go off"); 446e3adcf8fSFrançois Tigeot } 447e3adcf8fSFrançois Tigeot 448e3adcf8fSFrançois Tigeot stat = I915_READ(PORT_HOTPLUG_STAT); 449e3adcf8fSFrançois Tigeot if ((stat & CRT_HOTPLUG_MONITOR_MASK) != CRT_HOTPLUG_MONITOR_NONE) 450e3adcf8fSFrançois Tigeot ret = true; 451e3adcf8fSFrançois Tigeot 452e3adcf8fSFrançois Tigeot /* clear the interrupt we just generated, if any */ 453e3adcf8fSFrançois Tigeot I915_WRITE(PORT_HOTPLUG_STAT, CRT_HOTPLUG_INT_STATUS); 454e3adcf8fSFrançois Tigeot 455e3adcf8fSFrançois Tigeot /* and put the bits back */ 456e3adcf8fSFrançois Tigeot I915_WRITE(PORT_HOTPLUG_EN, orig); 457e3adcf8fSFrançois Tigeot 458e3adcf8fSFrançois Tigeot return ret; 459e3adcf8fSFrançois Tigeot } 460e3adcf8fSFrançois Tigeot 46119df918dSFrançois Tigeot static struct edid *intel_crt_get_edid(struct drm_connector *connector, 462ba55f2f5SFrançois Tigeot struct i2c_adapter *i2c) 46319df918dSFrançois Tigeot { 46419df918dSFrançois Tigeot struct edid *edid; 46519df918dSFrançois Tigeot 46619df918dSFrançois Tigeot edid = drm_get_edid(connector, i2c); 46719df918dSFrançois Tigeot 46819df918dSFrançois Tigeot if (!edid && !intel_gmbus_is_forced_bit(i2c)) { 46919df918dSFrançois Tigeot DRM_DEBUG_KMS("CRT GMBUS EDID read failed, retry using GPIO bit-banging\n"); 47019df918dSFrançois Tigeot intel_gmbus_force_bit(i2c, true); 47119df918dSFrançois Tigeot edid = drm_get_edid(connector, i2c); 47219df918dSFrançois Tigeot intel_gmbus_force_bit(i2c, false); 47319df918dSFrançois Tigeot } 47419df918dSFrançois Tigeot 47519df918dSFrançois Tigeot return edid; 47619df918dSFrançois Tigeot } 47719df918dSFrançois Tigeot 47819df918dSFrançois Tigeot /* local version of intel_ddc_get_modes() to use intel_crt_get_edid() */ 47919df918dSFrançois Tigeot static int intel_crt_ddc_get_modes(struct drm_connector *connector, 480ba55f2f5SFrançois Tigeot struct i2c_adapter *adapter) 48119df918dSFrançois Tigeot { 48219df918dSFrançois Tigeot struct edid *edid; 48319df918dSFrançois Tigeot int ret; 48419df918dSFrançois Tigeot 48519df918dSFrançois Tigeot edid = intel_crt_get_edid(connector, adapter); 48619df918dSFrançois Tigeot if (!edid) 48719df918dSFrançois Tigeot return 0; 48819df918dSFrançois Tigeot 48919df918dSFrançois Tigeot ret = intel_connector_update_modes(connector, edid); 490158486a6SFrançois Tigeot kfree(edid); 49119df918dSFrançois Tigeot 49219df918dSFrançois Tigeot return ret; 49319df918dSFrançois Tigeot } 49419df918dSFrançois Tigeot 495e3adcf8fSFrançois Tigeot static bool intel_crt_detect_ddc(struct drm_connector *connector) 496e3adcf8fSFrançois Tigeot { 497e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 498e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private; 499e3adcf8fSFrançois Tigeot struct edid *edid; 500ba55f2f5SFrançois Tigeot struct i2c_adapter *i2c; 501e3adcf8fSFrançois Tigeot 50219df918dSFrançois Tigeot BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG); 50319df918dSFrançois Tigeot 5045d0b1887SFrançois Tigeot i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); 50519df918dSFrançois Tigeot edid = intel_crt_get_edid(connector, i2c); 50619df918dSFrançois Tigeot 50719df918dSFrançois Tigeot if (edid) { 50819df918dSFrançois Tigeot bool is_digital = edid->input & DRM_EDID_INPUT_DIGITAL; 50919df918dSFrançois Tigeot 510e3adcf8fSFrançois Tigeot /* 511e3adcf8fSFrançois Tigeot * This may be a DVI-I connector with a shared DDC 512e3adcf8fSFrançois Tigeot * link between analog and digital outputs, so we 513e3adcf8fSFrançois Tigeot * have to check the EDID input spec of the attached device. 514e3adcf8fSFrançois Tigeot */ 515e3adcf8fSFrançois Tigeot if (!is_digital) { 516e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); 517e3adcf8fSFrançois Tigeot return true; 51819df918dSFrançois Tigeot } 51919df918dSFrançois Tigeot 520e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n"); 52119df918dSFrançois Tigeot } else { 52219df918dSFrançois Tigeot DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [no valid EDID found]\n"); 523e3adcf8fSFrançois Tigeot } 52419df918dSFrançois Tigeot 5255d0b1887SFrançois Tigeot kfree(edid); 526e3adcf8fSFrançois Tigeot 527e3adcf8fSFrançois Tigeot return false; 528e3adcf8fSFrançois Tigeot } 529e3adcf8fSFrançois Tigeot 530e3adcf8fSFrançois Tigeot static enum drm_connector_status 531e3adcf8fSFrançois Tigeot intel_crt_load_detect(struct intel_crt *crt) 532e3adcf8fSFrançois Tigeot { 533e3adcf8fSFrançois Tigeot struct drm_device *dev = crt->base.base.dev; 534e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 535e3adcf8fSFrançois Tigeot uint32_t pipe = to_intel_crtc(crt->base.base.crtc)->pipe; 536e3adcf8fSFrançois Tigeot uint32_t save_bclrpat; 537e3adcf8fSFrançois Tigeot uint32_t save_vtotal; 538e3adcf8fSFrançois Tigeot uint32_t vtotal, vactive; 539e3adcf8fSFrançois Tigeot uint32_t vsample; 540e3adcf8fSFrançois Tigeot uint32_t vblank, vblank_start, vblank_end; 541e3adcf8fSFrançois Tigeot uint32_t dsl; 542e3adcf8fSFrançois Tigeot uint32_t bclrpat_reg; 543e3adcf8fSFrançois Tigeot uint32_t vtotal_reg; 544e3adcf8fSFrançois Tigeot uint32_t vblank_reg; 545e3adcf8fSFrançois Tigeot uint32_t vsync_reg; 546e3adcf8fSFrançois Tigeot uint32_t pipeconf_reg; 547e3adcf8fSFrançois Tigeot uint32_t pipe_dsl_reg; 548e3adcf8fSFrançois Tigeot uint8_t st00; 549e3adcf8fSFrançois Tigeot enum drm_connector_status status; 550e3adcf8fSFrançois Tigeot 551e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("starting load-detect on CRT\n"); 552e3adcf8fSFrançois Tigeot 553e3adcf8fSFrançois Tigeot bclrpat_reg = BCLRPAT(pipe); 554e3adcf8fSFrançois Tigeot vtotal_reg = VTOTAL(pipe); 555e3adcf8fSFrançois Tigeot vblank_reg = VBLANK(pipe); 556e3adcf8fSFrançois Tigeot vsync_reg = VSYNC(pipe); 557e3adcf8fSFrançois Tigeot pipeconf_reg = PIPECONF(pipe); 558e3adcf8fSFrançois Tigeot pipe_dsl_reg = PIPEDSL(pipe); 559e3adcf8fSFrançois Tigeot 560e3adcf8fSFrançois Tigeot save_bclrpat = I915_READ(bclrpat_reg); 561e3adcf8fSFrançois Tigeot save_vtotal = I915_READ(vtotal_reg); 562e3adcf8fSFrançois Tigeot vblank = I915_READ(vblank_reg); 563e3adcf8fSFrançois Tigeot 564e3adcf8fSFrançois Tigeot vtotal = ((save_vtotal >> 16) & 0xfff) + 1; 565e3adcf8fSFrançois Tigeot vactive = (save_vtotal & 0x7ff) + 1; 566e3adcf8fSFrançois Tigeot 567e3adcf8fSFrançois Tigeot vblank_start = (vblank & 0xfff) + 1; 568e3adcf8fSFrançois Tigeot vblank_end = ((vblank >> 16) & 0xfff) + 1; 569e3adcf8fSFrançois Tigeot 570e3adcf8fSFrançois Tigeot /* Set the border color to purple. */ 571e3adcf8fSFrançois Tigeot I915_WRITE(bclrpat_reg, 0x500050); 572e3adcf8fSFrançois Tigeot 573e3adcf8fSFrançois Tigeot if (!IS_GEN2(dev)) { 574e3adcf8fSFrançois Tigeot uint32_t pipeconf = I915_READ(pipeconf_reg); 575e3adcf8fSFrançois Tigeot I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); 576e3adcf8fSFrançois Tigeot POSTING_READ(pipeconf_reg); 577e3adcf8fSFrançois Tigeot /* Wait for next Vblank to substitue 578e3adcf8fSFrançois Tigeot * border color for Color info */ 579e3adcf8fSFrançois Tigeot intel_wait_for_vblank(dev, pipe); 580e3adcf8fSFrançois Tigeot st00 = I915_READ8(VGA_MSR_WRITE); 581e3adcf8fSFrançois Tigeot status = ((st00 & (1 << 4)) != 0) ? 582e3adcf8fSFrançois Tigeot connector_status_connected : 583e3adcf8fSFrançois Tigeot connector_status_disconnected; 584e3adcf8fSFrançois Tigeot 585e3adcf8fSFrançois Tigeot I915_WRITE(pipeconf_reg, pipeconf); 586e3adcf8fSFrançois Tigeot } else { 587e3adcf8fSFrançois Tigeot bool restore_vblank = false; 588e3adcf8fSFrançois Tigeot int count, detect; 589e3adcf8fSFrançois Tigeot 590e3adcf8fSFrançois Tigeot /* 591e3adcf8fSFrançois Tigeot * If there isn't any border, add some. 592e3adcf8fSFrançois Tigeot * Yes, this will flicker 593e3adcf8fSFrançois Tigeot */ 594e3adcf8fSFrançois Tigeot if (vblank_start <= vactive && vblank_end >= vtotal) { 595e3adcf8fSFrançois Tigeot uint32_t vsync = I915_READ(vsync_reg); 596e3adcf8fSFrançois Tigeot uint32_t vsync_start = (vsync & 0xffff) + 1; 597e3adcf8fSFrançois Tigeot 598e3adcf8fSFrançois Tigeot vblank_start = vsync_start; 599e3adcf8fSFrançois Tigeot I915_WRITE(vblank_reg, 600e3adcf8fSFrançois Tigeot (vblank_start - 1) | 601e3adcf8fSFrançois Tigeot ((vblank_end - 1) << 16)); 602e3adcf8fSFrançois Tigeot restore_vblank = true; 603e3adcf8fSFrançois Tigeot } 604e3adcf8fSFrançois Tigeot /* sample in the vertical border, selecting the larger one */ 605e3adcf8fSFrançois Tigeot if (vblank_start - vactive >= vtotal - vblank_end) 606e3adcf8fSFrançois Tigeot vsample = (vblank_start + vactive) >> 1; 607e3adcf8fSFrançois Tigeot else 608e3adcf8fSFrançois Tigeot vsample = (vtotal + vblank_end) >> 1; 609e3adcf8fSFrançois Tigeot 610e3adcf8fSFrançois Tigeot /* 611e3adcf8fSFrançois Tigeot * Wait for the border to be displayed 612e3adcf8fSFrançois Tigeot */ 613e3adcf8fSFrançois Tigeot while (I915_READ(pipe_dsl_reg) >= vactive) 614e3adcf8fSFrançois Tigeot ; 615e3adcf8fSFrançois Tigeot while ((dsl = I915_READ(pipe_dsl_reg)) <= vsample) 616e3adcf8fSFrançois Tigeot ; 617e3adcf8fSFrançois Tigeot /* 618e3adcf8fSFrançois Tigeot * Watch ST00 for an entire scanline 619e3adcf8fSFrançois Tigeot */ 620e3adcf8fSFrançois Tigeot detect = 0; 621e3adcf8fSFrançois Tigeot count = 0; 622e3adcf8fSFrançois Tigeot do { 623e3adcf8fSFrançois Tigeot count++; 624e3adcf8fSFrançois Tigeot /* Read the ST00 VGA status register */ 625e3adcf8fSFrançois Tigeot st00 = I915_READ8(VGA_MSR_WRITE); 626e3adcf8fSFrançois Tigeot if (st00 & (1 << 4)) 627e3adcf8fSFrançois Tigeot detect++; 628e3adcf8fSFrançois Tigeot } while ((I915_READ(pipe_dsl_reg) == dsl)); 629e3adcf8fSFrançois Tigeot 630e3adcf8fSFrançois Tigeot /* restore vblank if necessary */ 631e3adcf8fSFrançois Tigeot if (restore_vblank) 632e3adcf8fSFrançois Tigeot I915_WRITE(vblank_reg, vblank); 633e3adcf8fSFrançois Tigeot /* 634e3adcf8fSFrançois Tigeot * If more than 3/4 of the scanline detected a monitor, 635e3adcf8fSFrançois Tigeot * then it is assumed to be present. This works even on i830, 636e3adcf8fSFrançois Tigeot * where there isn't any way to force the border color across 637e3adcf8fSFrançois Tigeot * the screen 638e3adcf8fSFrançois Tigeot */ 639e3adcf8fSFrançois Tigeot status = detect * 4 > count * 3 ? 640e3adcf8fSFrançois Tigeot connector_status_connected : 641e3adcf8fSFrançois Tigeot connector_status_disconnected; 642e3adcf8fSFrançois Tigeot } 643e3adcf8fSFrançois Tigeot 644e3adcf8fSFrançois Tigeot /* Restore previous settings */ 645e3adcf8fSFrançois Tigeot I915_WRITE(bclrpat_reg, save_bclrpat); 646e3adcf8fSFrançois Tigeot 647e3adcf8fSFrançois Tigeot return status; 648e3adcf8fSFrançois Tigeot } 649e3adcf8fSFrançois Tigeot 650e3adcf8fSFrançois Tigeot static enum drm_connector_status 651e3adcf8fSFrançois Tigeot intel_crt_detect(struct drm_connector *connector, bool force) 652e3adcf8fSFrançois Tigeot { 653e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 654ba55f2f5SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 655e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 656ba55f2f5SFrançois Tigeot struct intel_encoder *intel_encoder = &crt->base; 657ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 658e3adcf8fSFrançois Tigeot enum drm_connector_status status; 659e3adcf8fSFrançois Tigeot struct intel_load_detect_pipe tmp; 660ba55f2f5SFrançois Tigeot struct drm_modeset_acquire_ctx ctx; 661ba55f2f5SFrançois Tigeot 6629edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n", 663b4efbf42Szrj connector->base.id, connector->name, 6649edbd4a0SFrançois Tigeot force); 6659edbd4a0SFrançois Tigeot 666ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(intel_encoder); 667ba55f2f5SFrançois Tigeot intel_display_power_get(dev_priv, power_domain); 668ba55f2f5SFrançois Tigeot 669e3adcf8fSFrançois Tigeot if (I915_HAS_HOTPLUG(dev)) { 67019df918dSFrançois Tigeot /* We can not rely on the HPD pin always being correctly wired 67119df918dSFrançois Tigeot * up, for example many KVM do not pass it through, and so 67219df918dSFrançois Tigeot * only trust an assertion that the monitor is connected. 67319df918dSFrançois Tigeot */ 674e3adcf8fSFrançois Tigeot if (intel_crt_detect_hotplug(connector)) { 675e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT detected via hotplug\n"); 676ba55f2f5SFrançois Tigeot status = connector_status_connected; 677ba55f2f5SFrançois Tigeot goto out; 67819df918dSFrançois Tigeot } else 679e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT not detected via hotplug\n"); 680e3adcf8fSFrançois Tigeot } 681e3adcf8fSFrançois Tigeot 682ba55f2f5SFrançois Tigeot if (intel_crt_detect_ddc(connector)) { 683ba55f2f5SFrançois Tigeot status = connector_status_connected; 684ba55f2f5SFrançois Tigeot goto out; 685ba55f2f5SFrançois Tigeot } 686e3adcf8fSFrançois Tigeot 68719df918dSFrançois Tigeot /* Load detection is broken on HPD capable machines. Whoever wants a 68819df918dSFrançois Tigeot * broken monitor (without edid) to work behind a broken kvm (that fails 68919df918dSFrançois Tigeot * to have the right resistors for HP detection) needs to fix this up. 69019df918dSFrançois Tigeot * For now just bail out. */ 691ba55f2f5SFrançois Tigeot if (I915_HAS_HOTPLUG(dev)) { 692ba55f2f5SFrançois Tigeot status = connector_status_disconnected; 693ba55f2f5SFrançois Tigeot goto out; 694ba55f2f5SFrançois Tigeot } 69519df918dSFrançois Tigeot 696ba55f2f5SFrançois Tigeot if (!force) { 697ba55f2f5SFrançois Tigeot status = connector->status; 698ba55f2f5SFrançois Tigeot goto out; 699ba55f2f5SFrançois Tigeot } 700e3adcf8fSFrançois Tigeot 701*24edb884SFrançois Tigeot drm_modeset_acquire_init(&ctx, 0); 702*24edb884SFrançois Tigeot 703e3adcf8fSFrançois Tigeot /* for pre-945g platforms use load detect */ 704ba55f2f5SFrançois Tigeot if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) { 705e3adcf8fSFrançois Tigeot if (intel_crt_detect_ddc(connector)) 706e3adcf8fSFrançois Tigeot status = connector_status_connected; 707e3adcf8fSFrançois Tigeot else 708e3adcf8fSFrançois Tigeot status = intel_crt_load_detect(crt); 709*24edb884SFrançois Tigeot intel_release_load_detect_pipe(connector, &tmp); 710e3adcf8fSFrançois Tigeot } else 711e3adcf8fSFrançois Tigeot status = connector_status_unknown; 712e3adcf8fSFrançois Tigeot 713*24edb884SFrançois Tigeot drm_modeset_drop_locks(&ctx); 714*24edb884SFrançois Tigeot drm_modeset_acquire_fini(&ctx); 715*24edb884SFrançois Tigeot 716ba55f2f5SFrançois Tigeot out: 717ba55f2f5SFrançois Tigeot intel_display_power_put(dev_priv, power_domain); 718e3adcf8fSFrançois Tigeot return status; 719e3adcf8fSFrançois Tigeot } 720e3adcf8fSFrançois Tigeot 721e3adcf8fSFrançois Tigeot static void intel_crt_destroy(struct drm_connector *connector) 722e3adcf8fSFrançois Tigeot { 723e3adcf8fSFrançois Tigeot drm_connector_cleanup(connector); 7245d0b1887SFrançois Tigeot kfree(connector); 725e3adcf8fSFrançois Tigeot } 726e3adcf8fSFrançois Tigeot 727e3adcf8fSFrançois Tigeot static int intel_crt_get_modes(struct drm_connector *connector) 728e3adcf8fSFrançois Tigeot { 729e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 730e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 731ba55f2f5SFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 732ba55f2f5SFrançois Tigeot struct intel_encoder *intel_encoder = &crt->base; 733ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 734e3adcf8fSFrançois Tigeot int ret; 735ba55f2f5SFrançois Tigeot struct i2c_adapter *i2c; 736ba55f2f5SFrançois Tigeot 737ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(intel_encoder); 738ba55f2f5SFrançois Tigeot intel_display_power_get(dev_priv, power_domain); 739e3adcf8fSFrançois Tigeot 7405d0b1887SFrançois Tigeot i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); 74119df918dSFrançois Tigeot ret = intel_crt_ddc_get_modes(connector, i2c); 742e3adcf8fSFrançois Tigeot if (ret || !IS_G4X(dev)) 743ba55f2f5SFrançois Tigeot goto out; 744e3adcf8fSFrançois Tigeot 745e3adcf8fSFrançois Tigeot /* Try to probe digital port for output in DVI-I -> VGA mode. */ 74619df918dSFrançois Tigeot i2c = intel_gmbus_get_adapter(dev_priv, GMBUS_PORT_DPB); 747ba55f2f5SFrançois Tigeot ret = intel_crt_ddc_get_modes(connector, i2c); 748ba55f2f5SFrançois Tigeot 749ba55f2f5SFrançois Tigeot out: 750ba55f2f5SFrançois Tigeot intel_display_power_put(dev_priv, power_domain); 751ba55f2f5SFrançois Tigeot 752ba55f2f5SFrançois Tigeot return ret; 753e3adcf8fSFrançois Tigeot } 754e3adcf8fSFrançois Tigeot 755e3adcf8fSFrançois Tigeot static int intel_crt_set_property(struct drm_connector *connector, 756e3adcf8fSFrançois Tigeot struct drm_property *property, 757e3adcf8fSFrançois Tigeot uint64_t value) 758e3adcf8fSFrançois Tigeot { 759e3adcf8fSFrançois Tigeot return 0; 760e3adcf8fSFrançois Tigeot } 761e3adcf8fSFrançois Tigeot 762e3adcf8fSFrançois Tigeot static void intel_crt_reset(struct drm_connector *connector) 763e3adcf8fSFrançois Tigeot { 764e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 76519df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 766e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 767e3adcf8fSFrançois Tigeot 7689edbd4a0SFrançois Tigeot if (INTEL_INFO(dev)->gen >= 5) { 76919df918dSFrançois Tigeot u32 adpa; 77019df918dSFrançois Tigeot 771a2fdbec6SFrançois Tigeot adpa = I915_READ(crt->adpa_reg); 77219df918dSFrançois Tigeot adpa &= ~ADPA_CRT_HOTPLUG_MASK; 77319df918dSFrançois Tigeot adpa |= ADPA_HOTPLUG_BITS; 774a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, adpa); 775a2fdbec6SFrançois Tigeot POSTING_READ(crt->adpa_reg); 77619df918dSFrançois Tigeot 77719df918dSFrançois Tigeot DRM_DEBUG_KMS("pch crt adpa set to 0x%x\n", adpa); 778e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 1; 779e3adcf8fSFrançois Tigeot } 780e3adcf8fSFrançois Tigeot 78119df918dSFrançois Tigeot } 78219df918dSFrançois Tigeot 783e3adcf8fSFrançois Tigeot /* 784e3adcf8fSFrançois Tigeot * Routines for controlling stuff on the analog port 785e3adcf8fSFrançois Tigeot */ 786e3adcf8fSFrançois Tigeot 787e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_crt_connector_funcs = { 788e3adcf8fSFrançois Tigeot .reset = intel_crt_reset, 78919df918dSFrançois Tigeot .dpms = intel_crt_dpms, 790e3adcf8fSFrançois Tigeot .detect = intel_crt_detect, 791e3adcf8fSFrançois Tigeot .fill_modes = drm_helper_probe_single_connector_modes, 792e3adcf8fSFrançois Tigeot .destroy = intel_crt_destroy, 793e3adcf8fSFrançois Tigeot .set_property = intel_crt_set_property, 794e3adcf8fSFrançois Tigeot }; 795e3adcf8fSFrançois Tigeot 796e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { 797e3adcf8fSFrançois Tigeot .mode_valid = intel_crt_mode_valid, 798e3adcf8fSFrançois Tigeot .get_modes = intel_crt_get_modes, 799e3adcf8fSFrançois Tigeot .best_encoder = intel_best_encoder, 800e3adcf8fSFrançois Tigeot }; 801e3adcf8fSFrançois Tigeot 802e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_crt_enc_funcs = { 803e3adcf8fSFrançois Tigeot .destroy = intel_encoder_destroy, 804e3adcf8fSFrançois Tigeot }; 805e3adcf8fSFrançois Tigeot 806*24edb884SFrançois Tigeot static int intel_no_crt_dmi_callback(const struct dmi_system_id *id) 807e3adcf8fSFrançois Tigeot { 80819df918dSFrançois Tigeot DRM_INFO("Skipping CRT initialization for %s\n", id->ident); 809e3adcf8fSFrançois Tigeot return 1; 810e3adcf8fSFrançois Tigeot } 811e3adcf8fSFrançois Tigeot 812e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_crt[] = { 813e3adcf8fSFrançois Tigeot { 814e3adcf8fSFrançois Tigeot .callback = intel_no_crt_dmi_callback, 815e3adcf8fSFrançois Tigeot .ident = "ACER ZGB", 816e3adcf8fSFrançois Tigeot .matches = { 817e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "ACER"), 818e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), 819e3adcf8fSFrançois Tigeot }, 820e3adcf8fSFrançois Tigeot }, 821ba55f2f5SFrançois Tigeot { 822ba55f2f5SFrançois Tigeot .callback = intel_no_crt_dmi_callback, 823ba55f2f5SFrançois Tigeot .ident = "DELL XPS 8700", 824ba55f2f5SFrançois Tigeot .matches = { 825ba55f2f5SFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 826ba55f2f5SFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "XPS 8700"), 827ba55f2f5SFrançois Tigeot }, 828ba55f2f5SFrançois Tigeot }, 829e3adcf8fSFrançois Tigeot { } 830e3adcf8fSFrançois Tigeot }; 831e3adcf8fSFrançois Tigeot 832e3adcf8fSFrançois Tigeot void intel_crt_init(struct drm_device *dev) 833e3adcf8fSFrançois Tigeot { 834e3adcf8fSFrançois Tigeot struct drm_connector *connector; 835e3adcf8fSFrançois Tigeot struct intel_crt *crt; 836e3adcf8fSFrançois Tigeot struct intel_connector *intel_connector; 837e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 838e3adcf8fSFrançois Tigeot 839e3adcf8fSFrançois Tigeot /* Skip machines without VGA that falsely report hotplug events */ 840e3adcf8fSFrançois Tigeot if (dmi_check_system(intel_no_crt)) 841e3adcf8fSFrançois Tigeot return; 842e3adcf8fSFrançois Tigeot 843159fc1d7SFrançois Tigeot crt = kzalloc(sizeof(struct intel_crt), GFP_KERNEL); 84419df918dSFrançois Tigeot if (!crt) 84519df918dSFrançois Tigeot return; 84619df918dSFrançois Tigeot 8479edbd4a0SFrançois Tigeot intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL); 84819df918dSFrançois Tigeot if (!intel_connector) { 849158486a6SFrançois Tigeot kfree(crt); 85019df918dSFrançois Tigeot return; 85119df918dSFrançois Tigeot } 852e3adcf8fSFrançois Tigeot 853e3adcf8fSFrançois Tigeot connector = &intel_connector->base; 85419df918dSFrançois Tigeot crt->connector = intel_connector; 855e3adcf8fSFrançois Tigeot drm_connector_init(dev, &intel_connector->base, 856e3adcf8fSFrançois Tigeot &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); 857e3adcf8fSFrançois Tigeot 858e3adcf8fSFrançois Tigeot drm_encoder_init(dev, &crt->base.base, &intel_crt_enc_funcs, 859e3adcf8fSFrançois Tigeot DRM_MODE_ENCODER_DAC); 860e3adcf8fSFrançois Tigeot 861e3adcf8fSFrançois Tigeot intel_connector_attach_encoder(intel_connector, &crt->base); 862e3adcf8fSFrançois Tigeot 863e3adcf8fSFrançois Tigeot crt->base.type = INTEL_OUTPUT_ANALOG; 864ba55f2f5SFrançois Tigeot crt->base.cloneable = (1 << INTEL_OUTPUT_DVO) | (1 << INTEL_OUTPUT_HDMI); 865e9243325SFrançois Tigeot if (IS_I830(dev)) 866e9243325SFrançois Tigeot crt->base.crtc_mask = (1 << 0); 867e9243325SFrançois Tigeot else 868e9243325SFrançois Tigeot crt->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); 869e9243325SFrançois Tigeot 870e3adcf8fSFrançois Tigeot if (IS_GEN2(dev)) 871e3adcf8fSFrançois Tigeot connector->interlace_allowed = 0; 872e3adcf8fSFrançois Tigeot else 873e3adcf8fSFrançois Tigeot connector->interlace_allowed = 1; 874e3adcf8fSFrançois Tigeot connector->doublescan_allowed = 0; 875e3adcf8fSFrançois Tigeot 876e9243325SFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 877e9243325SFrançois Tigeot crt->adpa_reg = PCH_ADPA; 878e9243325SFrançois Tigeot else if (IS_VALLEYVIEW(dev)) 879e9243325SFrançois Tigeot crt->adpa_reg = VLV_ADPA; 880e9243325SFrançois Tigeot else 881e9243325SFrançois Tigeot crt->adpa_reg = ADPA; 882e9243325SFrançois Tigeot 8838e26cdf6SFrançois Tigeot crt->base.compute_config = intel_crt_compute_config; 88419df918dSFrançois Tigeot crt->base.disable = intel_disable_crt; 88519df918dSFrançois Tigeot crt->base.enable = intel_enable_crt; 8868e26cdf6SFrançois Tigeot if (I915_HAS_HOTPLUG(dev)) 8878e26cdf6SFrançois Tigeot crt->base.hpd_pin = HPD_CRT; 8889edbd4a0SFrançois Tigeot if (HAS_DDI(dev)) { 8899edbd4a0SFrançois Tigeot crt->base.get_config = hsw_crt_get_config; 89019df918dSFrançois Tigeot crt->base.get_hw_state = intel_ddi_get_hw_state; 891*24edb884SFrançois Tigeot crt->base.pre_enable = hsw_crt_pre_enable; 892*24edb884SFrançois Tigeot crt->base.post_disable = hsw_crt_post_disable; 8939edbd4a0SFrançois Tigeot } else { 8949edbd4a0SFrançois Tigeot crt->base.get_config = intel_crt_get_config; 89519df918dSFrançois Tigeot crt->base.get_hw_state = intel_crt_get_hw_state; 8969edbd4a0SFrançois Tigeot } 89719df918dSFrançois Tigeot intel_connector->get_hw_state = intel_connector_get_hw_state; 898ba55f2f5SFrançois Tigeot intel_connector->unregister = intel_connector_unregister; 89919df918dSFrançois Tigeot 900e3adcf8fSFrançois Tigeot drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); 901e3adcf8fSFrançois Tigeot 902c6f73aabSFrançois Tigeot drm_connector_register(connector); 903e3adcf8fSFrançois Tigeot 9048e26cdf6SFrançois Tigeot if (!I915_HAS_HOTPLUG(dev)) 9058e26cdf6SFrançois Tigeot intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT; 906e3adcf8fSFrançois Tigeot 907e3adcf8fSFrançois Tigeot /* 908e3adcf8fSFrançois Tigeot * Configure the automatic hotplug detection stuff 909e3adcf8fSFrançois Tigeot */ 910e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 0; 911e3adcf8fSFrançois Tigeot 91219df918dSFrançois Tigeot /* 91319df918dSFrançois Tigeot * TODO: find a proper way to discover whether we need to set the the 91419df918dSFrançois Tigeot * polarity and link reversal bits or not, instead of relying on the 91519df918dSFrançois Tigeot * BIOS. 91619df918dSFrançois Tigeot */ 91719df918dSFrançois Tigeot if (HAS_PCH_LPT(dev)) { 91819df918dSFrançois Tigeot u32 fdi_config = FDI_RX_POLARITY_REVERSED_LPT | 91919df918dSFrançois Tigeot FDI_RX_LINK_REVERSAL_OVERRIDE; 92019df918dSFrançois Tigeot 92119df918dSFrançois Tigeot dev_priv->fdi_rx_config = I915_READ(_FDI_RXA_CTL) & fdi_config; 92219df918dSFrançois Tigeot } 923ba55f2f5SFrançois Tigeot 924ba55f2f5SFrançois Tigeot intel_crt_reset(connector); 925e3adcf8fSFrançois Tigeot } 926