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> 30*2c9916cdSFrançois Tigeot #include <drm/drm_atomic_helper.h> 3118e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 3218e26a6dSFrançois Tigeot #include <drm/drm_crtc_helper.h> 3318e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 3418e26a6dSFrançois Tigeot #include "intel_drv.h" 355c6c6f23SFrançois Tigeot #include <drm/i915_drm.h> 36e3adcf8fSFrançois Tigeot #include "i915_drv.h" 37e3adcf8fSFrançois Tigeot 38e3adcf8fSFrançois Tigeot /* Here's the desired hotplug mode */ 39e3adcf8fSFrançois Tigeot #define ADPA_HOTPLUG_BITS (ADPA_CRT_HOTPLUG_PERIOD_128 | \ 40e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_WARMUP_10MS | \ 41e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_SAMPLE_4S | \ 42e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_VOLTAGE_50 | \ 43e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_VOLREF_325MV | \ 44e3adcf8fSFrançois Tigeot ADPA_CRT_HOTPLUG_ENABLE) 45e3adcf8fSFrançois Tigeot 46e3adcf8fSFrançois Tigeot struct intel_crt { 47e3adcf8fSFrançois Tigeot struct intel_encoder base; 48e9243325SFrançois Tigeot /* DPMS state is stored in the connector, which we need in the 49e9243325SFrançois Tigeot * encoder's enable/disable callbacks */ 50e9243325SFrançois Tigeot struct intel_connector *connector; 51e3adcf8fSFrançois Tigeot bool force_hotplug_required; 52e9243325SFrançois Tigeot u32 adpa_reg; 53e3adcf8fSFrançois Tigeot }; 54e3adcf8fSFrançois Tigeot 5519df918dSFrançois Tigeot static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder) 56e3adcf8fSFrançois Tigeot { 5719df918dSFrançois Tigeot return container_of(encoder, struct intel_crt, base); 5819df918dSFrançois Tigeot } 5919df918dSFrançois Tigeot 609edbd4a0SFrançois Tigeot static struct intel_crt *intel_attached_crt(struct drm_connector *connector) 619edbd4a0SFrançois Tigeot { 629edbd4a0SFrançois Tigeot return intel_encoder_to_crt(intel_attached_encoder(connector)); 639edbd4a0SFrançois Tigeot } 649edbd4a0SFrançois Tigeot 6519df918dSFrançois Tigeot static bool intel_crt_get_hw_state(struct intel_encoder *encoder, 6619df918dSFrançois Tigeot enum i915_pipe *pipe) 6719df918dSFrançois Tigeot { 6819df918dSFrançois Tigeot struct drm_device *dev = encoder->base.dev; 69e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 7019df918dSFrançois Tigeot struct intel_crt *crt = intel_encoder_to_crt(encoder); 71ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 7219df918dSFrançois Tigeot u32 tmp; 73e3adcf8fSFrançois Tigeot 74ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(encoder); 75*2c9916cdSFrançois Tigeot if (!intel_display_power_is_enabled(dev_priv, power_domain)) 76ba55f2f5SFrançois Tigeot return false; 77ba55f2f5SFrançois Tigeot 7819df918dSFrançois Tigeot tmp = I915_READ(crt->adpa_reg); 7919df918dSFrançois Tigeot 8019df918dSFrançois Tigeot if (!(tmp & ADPA_DAC_ENABLE)) 8119df918dSFrançois Tigeot return false; 8219df918dSFrançois Tigeot 8319df918dSFrançois Tigeot if (HAS_PCH_CPT(dev)) 8419df918dSFrançois Tigeot *pipe = PORT_TO_PIPE_CPT(tmp); 85e3adcf8fSFrançois Tigeot else 8619df918dSFrançois Tigeot *pipe = PORT_TO_PIPE(tmp); 87e3adcf8fSFrançois Tigeot 8819df918dSFrançois Tigeot return true; 8919df918dSFrançois Tigeot } 9019df918dSFrançois Tigeot 919edbd4a0SFrançois Tigeot static unsigned int intel_crt_get_flags(struct intel_encoder *encoder) 925d0b1887SFrançois Tigeot { 935d0b1887SFrançois Tigeot struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; 945d0b1887SFrançois Tigeot struct intel_crt *crt = intel_encoder_to_crt(encoder); 955d0b1887SFrançois Tigeot u32 tmp, flags = 0; 965d0b1887SFrançois Tigeot 975d0b1887SFrançois Tigeot tmp = I915_READ(crt->adpa_reg); 985d0b1887SFrançois Tigeot 995d0b1887SFrançois Tigeot if (tmp & ADPA_HSYNC_ACTIVE_HIGH) 1005d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_PHSYNC; 1015d0b1887SFrançois Tigeot else 1025d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_NHSYNC; 1035d0b1887SFrançois Tigeot 1045d0b1887SFrançois Tigeot if (tmp & ADPA_VSYNC_ACTIVE_HIGH) 1055d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_PVSYNC; 1065d0b1887SFrançois Tigeot else 1075d0b1887SFrançois Tigeot flags |= DRM_MODE_FLAG_NVSYNC; 1085d0b1887SFrançois Tigeot 1099edbd4a0SFrançois Tigeot return flags; 1109edbd4a0SFrançois Tigeot } 1119edbd4a0SFrançois Tigeot 1129edbd4a0SFrançois Tigeot static void intel_crt_get_config(struct intel_encoder *encoder, 113*2c9916cdSFrançois Tigeot struct intel_crtc_state *pipe_config) 1149edbd4a0SFrançois Tigeot { 1159edbd4a0SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 1169edbd4a0SFrançois Tigeot int dotclock; 1179edbd4a0SFrançois Tigeot 118*2c9916cdSFrançois Tigeot pipe_config->base.adjusted_mode.flags |= intel_crt_get_flags(encoder); 1199edbd4a0SFrançois Tigeot 1209edbd4a0SFrançois Tigeot dotclock = pipe_config->port_clock; 1219edbd4a0SFrançois Tigeot 1229edbd4a0SFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 1239edbd4a0SFrançois Tigeot ironlake_check_encoder_dotclock(pipe_config, dotclock); 1249edbd4a0SFrançois Tigeot 125*2c9916cdSFrançois Tigeot pipe_config->base.adjusted_mode.crtc_clock = dotclock; 1269edbd4a0SFrançois Tigeot } 1279edbd4a0SFrançois Tigeot 1289edbd4a0SFrançois Tigeot static void hsw_crt_get_config(struct intel_encoder *encoder, 129*2c9916cdSFrançois Tigeot struct intel_crtc_state *pipe_config) 1309edbd4a0SFrançois Tigeot { 1319edbd4a0SFrançois Tigeot intel_ddi_get_config(encoder, pipe_config); 1329edbd4a0SFrançois Tigeot 133*2c9916cdSFrançois Tigeot pipe_config->base.adjusted_mode.flags &= ~(DRM_MODE_FLAG_PHSYNC | 1349edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NHSYNC | 1359edbd4a0SFrançois Tigeot DRM_MODE_FLAG_PVSYNC | 1369edbd4a0SFrançois Tigeot DRM_MODE_FLAG_NVSYNC); 137*2c9916cdSFrançois Tigeot pipe_config->base.adjusted_mode.flags |= intel_crt_get_flags(encoder); 1385d0b1887SFrançois Tigeot } 1395d0b1887SFrançois Tigeot 14024edb884SFrançois Tigeot static void hsw_crt_pre_enable(struct intel_encoder *encoder) 14124edb884SFrançois Tigeot { 14224edb884SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 14324edb884SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 14424edb884SFrançois Tigeot 14524edb884SFrançois Tigeot WARN(I915_READ(SPLL_CTL) & SPLL_PLL_ENABLE, "SPLL already enabled\n"); 14624edb884SFrançois Tigeot I915_WRITE(SPLL_CTL, 14724edb884SFrançois Tigeot SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | SPLL_PLL_SSC); 14824edb884SFrançois Tigeot POSTING_READ(SPLL_CTL); 14924edb884SFrançois Tigeot udelay(20); 15024edb884SFrançois Tigeot } 15124edb884SFrançois Tigeot 15219df918dSFrançois Tigeot /* Note: The caller is required to filter out dpms modes not supported by the 15319df918dSFrançois Tigeot * platform. */ 15419df918dSFrançois Tigeot static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode) 15519df918dSFrançois Tigeot { 15619df918dSFrançois Tigeot struct drm_device *dev = encoder->base.dev; 15719df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 15819df918dSFrançois Tigeot struct intel_crt *crt = intel_encoder_to_crt(encoder); 159ba55f2f5SFrançois Tigeot struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc); 160*2c9916cdSFrançois Tigeot struct drm_display_mode *adjusted_mode = &crtc->config->base.adjusted_mode; 161ba55f2f5SFrançois Tigeot u32 adpa; 16219df918dSFrançois Tigeot 163ba55f2f5SFrançois Tigeot if (INTEL_INFO(dev)->gen >= 5) 164ba55f2f5SFrançois Tigeot adpa = ADPA_HOTPLUG_BITS; 165ba55f2f5SFrançois Tigeot else 166ba55f2f5SFrançois Tigeot adpa = 0; 167ba55f2f5SFrançois Tigeot 168ba55f2f5SFrançois Tigeot if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) 169ba55f2f5SFrançois Tigeot adpa |= ADPA_HSYNC_ACTIVE_HIGH; 170ba55f2f5SFrançois Tigeot if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) 171ba55f2f5SFrançois Tigeot adpa |= ADPA_VSYNC_ACTIVE_HIGH; 172ba55f2f5SFrançois Tigeot 173ba55f2f5SFrançois Tigeot /* For CPT allow 3 pipe config, for others just use A or B */ 174ba55f2f5SFrançois Tigeot if (HAS_PCH_LPT(dev)) 175ba55f2f5SFrançois Tigeot ; /* Those bits don't exist here */ 176ba55f2f5SFrançois Tigeot else if (HAS_PCH_CPT(dev)) 177ba55f2f5SFrançois Tigeot adpa |= PORT_TRANS_SEL_CPT(crtc->pipe); 178ba55f2f5SFrançois Tigeot else if (crtc->pipe == 0) 179ba55f2f5SFrançois Tigeot adpa |= ADPA_PIPE_A_SELECT; 180ba55f2f5SFrançois Tigeot else 181ba55f2f5SFrançois Tigeot adpa |= ADPA_PIPE_B_SELECT; 182ba55f2f5SFrançois Tigeot 183ba55f2f5SFrançois Tigeot if (!HAS_PCH_SPLIT(dev)) 184ba55f2f5SFrançois Tigeot I915_WRITE(BCLRPAT(crtc->pipe), 0); 185e3adcf8fSFrançois Tigeot 186e3adcf8fSFrançois Tigeot switch (mode) { 187e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_ON: 188ba55f2f5SFrançois Tigeot adpa |= ADPA_DAC_ENABLE; 189e3adcf8fSFrançois Tigeot break; 190e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_STANDBY: 191ba55f2f5SFrançois Tigeot adpa |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; 192e3adcf8fSFrançois Tigeot break; 193e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_SUSPEND: 194ba55f2f5SFrançois Tigeot adpa |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; 195e3adcf8fSFrançois Tigeot break; 196e3adcf8fSFrançois Tigeot case DRM_MODE_DPMS_OFF: 197ba55f2f5SFrançois Tigeot adpa |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; 198e3adcf8fSFrançois Tigeot break; 199e3adcf8fSFrançois Tigeot } 200e3adcf8fSFrançois Tigeot 201ba55f2f5SFrançois Tigeot I915_WRITE(crt->adpa_reg, adpa); 20219df918dSFrançois Tigeot } 20319df918dSFrançois Tigeot 20419df918dSFrançois Tigeot static void intel_disable_crt(struct intel_encoder *encoder) 20519df918dSFrançois Tigeot { 20619df918dSFrançois Tigeot intel_crt_set_dpms(encoder, DRM_MODE_DPMS_OFF); 20719df918dSFrançois Tigeot } 20819df918dSFrançois Tigeot 20924edb884SFrançois Tigeot 21024edb884SFrançois Tigeot static void hsw_crt_post_disable(struct intel_encoder *encoder) 21124edb884SFrançois Tigeot { 21224edb884SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 21324edb884SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 21424edb884SFrançois Tigeot uint32_t val; 21524edb884SFrançois Tigeot 21624edb884SFrançois Tigeot DRM_DEBUG_KMS("Disabling SPLL\n"); 21724edb884SFrançois Tigeot val = I915_READ(SPLL_CTL); 21824edb884SFrançois Tigeot WARN_ON(!(val & SPLL_PLL_ENABLE)); 21924edb884SFrançois Tigeot I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE); 22024edb884SFrançois Tigeot POSTING_READ(SPLL_CTL); 22124edb884SFrançois Tigeot } 22224edb884SFrançois Tigeot 22319df918dSFrançois Tigeot static void intel_enable_crt(struct intel_encoder *encoder) 22419df918dSFrançois Tigeot { 22519df918dSFrançois Tigeot struct intel_crt *crt = intel_encoder_to_crt(encoder); 22619df918dSFrançois Tigeot 22719df918dSFrançois Tigeot intel_crt_set_dpms(encoder, crt->connector->base.dpms); 22819df918dSFrançois Tigeot } 22919df918dSFrançois Tigeot 2305d0b1887SFrançois Tigeot /* Special dpms function to support cloning between dvo/sdvo/crt. */ 23119df918dSFrançois Tigeot static void intel_crt_dpms(struct drm_connector *connector, int mode) 23219df918dSFrançois Tigeot { 23319df918dSFrançois Tigeot struct drm_device *dev = connector->dev; 23419df918dSFrançois Tigeot struct intel_encoder *encoder = intel_attached_encoder(connector); 23519df918dSFrançois Tigeot struct drm_crtc *crtc; 23619df918dSFrançois Tigeot int old_dpms; 23719df918dSFrançois Tigeot 23819df918dSFrançois Tigeot /* PCH platforms and VLV only support on/off. */ 23919df918dSFrançois Tigeot if (INTEL_INFO(dev)->gen >= 5 && mode != DRM_MODE_DPMS_ON) 24019df918dSFrançois Tigeot mode = DRM_MODE_DPMS_OFF; 24119df918dSFrançois Tigeot 24219df918dSFrançois Tigeot if (mode == connector->dpms) 24319df918dSFrançois Tigeot return; 24419df918dSFrançois Tigeot 24519df918dSFrançois Tigeot old_dpms = connector->dpms; 24619df918dSFrançois Tigeot connector->dpms = mode; 24719df918dSFrançois Tigeot 24819df918dSFrançois Tigeot /* Only need to change hw state when actually enabled */ 24919df918dSFrançois Tigeot crtc = encoder->base.crtc; 25019df918dSFrançois Tigeot if (!crtc) { 25119df918dSFrançois Tigeot encoder->connectors_active = false; 25219df918dSFrançois Tigeot return; 25319df918dSFrançois Tigeot } 25419df918dSFrançois Tigeot 25519df918dSFrançois Tigeot /* We need the pipe to run for anything but OFF. */ 25619df918dSFrançois Tigeot if (mode == DRM_MODE_DPMS_OFF) 25719df918dSFrançois Tigeot encoder->connectors_active = false; 25819df918dSFrançois Tigeot else 25919df918dSFrançois Tigeot encoder->connectors_active = true; 26019df918dSFrançois Tigeot 2615d0b1887SFrançois Tigeot /* We call connector dpms manually below in case pipe dpms doesn't 2625d0b1887SFrançois Tigeot * change due to cloning. */ 26319df918dSFrançois Tigeot if (mode < old_dpms) { 26419df918dSFrançois Tigeot /* From off to on, enable the pipe first. */ 26519df918dSFrançois Tigeot intel_crtc_update_dpms(crtc); 26619df918dSFrançois Tigeot 26719df918dSFrançois Tigeot intel_crt_set_dpms(encoder, mode); 26819df918dSFrançois Tigeot } else { 26919df918dSFrançois Tigeot intel_crt_set_dpms(encoder, mode); 27019df918dSFrançois Tigeot 27119df918dSFrançois Tigeot intel_crtc_update_dpms(crtc); 27219df918dSFrançois Tigeot } 27319df918dSFrançois Tigeot 27419df918dSFrançois Tigeot intel_modeset_check_state(connector->dev); 275e3adcf8fSFrançois Tigeot } 276e3adcf8fSFrançois Tigeot 2779edbd4a0SFrançois Tigeot static enum drm_mode_status 2789edbd4a0SFrançois Tigeot intel_crt_mode_valid(struct drm_connector *connector, 279e3adcf8fSFrançois Tigeot struct drm_display_mode *mode) 280e3adcf8fSFrançois Tigeot { 281e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 282e3adcf8fSFrançois Tigeot 283e3adcf8fSFrançois Tigeot int max_clock = 0; 284e3adcf8fSFrançois Tigeot if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 285e3adcf8fSFrançois Tigeot return MODE_NO_DBLESCAN; 286e3adcf8fSFrançois Tigeot 287e3adcf8fSFrançois Tigeot if (mode->clock < 25000) 288e3adcf8fSFrançois Tigeot return MODE_CLOCK_LOW; 289e3adcf8fSFrançois Tigeot 290e3adcf8fSFrançois Tigeot if (IS_GEN2(dev)) 291e3adcf8fSFrançois Tigeot max_clock = 350000; 292e3adcf8fSFrançois Tigeot else 293e3adcf8fSFrançois Tigeot max_clock = 400000; 294e3adcf8fSFrançois Tigeot if (mode->clock > max_clock) 295e3adcf8fSFrançois Tigeot return MODE_CLOCK_HIGH; 296e3adcf8fSFrançois Tigeot 29719df918dSFrançois Tigeot /* The FDI receiver on LPT only supports 8bpc and only has 2 lanes. */ 29819df918dSFrançois Tigeot if (HAS_PCH_LPT(dev) && 29919df918dSFrançois Tigeot (ironlake_get_lanes_required(mode->clock, 270000, 24) > 2)) 30019df918dSFrançois Tigeot return MODE_CLOCK_HIGH; 30119df918dSFrançois Tigeot 302e3adcf8fSFrançois Tigeot return MODE_OK; 303e3adcf8fSFrançois Tigeot } 304e3adcf8fSFrançois Tigeot 3058e26cdf6SFrançois Tigeot static bool intel_crt_compute_config(struct intel_encoder *encoder, 306*2c9916cdSFrançois Tigeot struct intel_crtc_state *pipe_config) 307e3adcf8fSFrançois Tigeot { 3088e26cdf6SFrançois Tigeot struct drm_device *dev = encoder->base.dev; 3098e26cdf6SFrançois Tigeot 3108e26cdf6SFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 3118e26cdf6SFrançois Tigeot pipe_config->has_pch_encoder = true; 3128e26cdf6SFrançois Tigeot 3135d0b1887SFrançois Tigeot /* LPT FDI RX only supports 8bpc. */ 3145d0b1887SFrançois Tigeot if (HAS_PCH_LPT(dev)) 3155d0b1887SFrançois Tigeot pipe_config->pipe_bpp = 24; 3165d0b1887SFrançois Tigeot 317ba55f2f5SFrançois Tigeot /* FDI must always be 2.7 GHz */ 31824edb884SFrançois Tigeot if (HAS_DDI(dev)) { 31924edb884SFrançois Tigeot pipe_config->ddi_pll_sel = PORT_CLK_SEL_SPLL; 320ba55f2f5SFrançois Tigeot pipe_config->port_clock = 135000 * 2; 32124edb884SFrançois Tigeot } 322ba55f2f5SFrançois Tigeot 323e3adcf8fSFrançois Tigeot return true; 324e3adcf8fSFrançois Tigeot } 325e3adcf8fSFrançois Tigeot 326e3adcf8fSFrançois Tigeot static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector) 327e3adcf8fSFrançois Tigeot { 328e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 329e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 330e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 331e3adcf8fSFrançois Tigeot u32 adpa; 332e3adcf8fSFrançois Tigeot bool ret; 333e3adcf8fSFrançois Tigeot 334e3adcf8fSFrançois Tigeot /* The first time through, trigger an explicit detection cycle */ 335e3adcf8fSFrançois Tigeot if (crt->force_hotplug_required) { 336e3adcf8fSFrançois Tigeot bool turn_off_dac = HAS_PCH_SPLIT(dev); 337e3adcf8fSFrançois Tigeot u32 save_adpa; 338e3adcf8fSFrançois Tigeot 339e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 0; 340e3adcf8fSFrançois Tigeot 341a2fdbec6SFrançois Tigeot save_adpa = adpa = I915_READ(crt->adpa_reg); 342e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa); 343e3adcf8fSFrançois Tigeot 344e3adcf8fSFrançois Tigeot adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; 345e3adcf8fSFrançois Tigeot if (turn_off_dac) 346e3adcf8fSFrançois Tigeot adpa &= ~ADPA_DAC_ENABLE; 347e3adcf8fSFrançois Tigeot 348a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, adpa); 349e3adcf8fSFrançois Tigeot 350a2fdbec6SFrançois Tigeot if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, 35119df918dSFrançois Tigeot 1000)) 35219df918dSFrançois Tigeot DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER"); 353e3adcf8fSFrançois Tigeot 354e3adcf8fSFrançois Tigeot if (turn_off_dac) { 355a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, save_adpa); 356a2fdbec6SFrançois Tigeot POSTING_READ(crt->adpa_reg); 357e3adcf8fSFrançois Tigeot } 358e3adcf8fSFrançois Tigeot } 359e3adcf8fSFrançois Tigeot 360e3adcf8fSFrançois Tigeot /* Check the status to see if both blue and green are on now */ 361a2fdbec6SFrançois Tigeot adpa = I915_READ(crt->adpa_reg); 362e3adcf8fSFrançois Tigeot if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0) 363e3adcf8fSFrançois Tigeot ret = true; 364e3adcf8fSFrançois Tigeot else 365e3adcf8fSFrançois Tigeot ret = false; 366e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("ironlake hotplug adpa=0x%x, result %d\n", adpa, ret); 367e3adcf8fSFrançois Tigeot 368e3adcf8fSFrançois Tigeot return ret; 369e3adcf8fSFrançois Tigeot } 370e3adcf8fSFrançois Tigeot 37119df918dSFrançois Tigeot static bool valleyview_crt_detect_hotplug(struct drm_connector *connector) 37219df918dSFrançois Tigeot { 37319df918dSFrançois Tigeot struct drm_device *dev = connector->dev; 374a2fdbec6SFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 37519df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 37619df918dSFrançois Tigeot u32 adpa; 37719df918dSFrançois Tigeot bool ret; 37819df918dSFrançois Tigeot u32 save_adpa; 37919df918dSFrançois Tigeot 380a2fdbec6SFrançois Tigeot save_adpa = adpa = I915_READ(crt->adpa_reg); 38119df918dSFrançois Tigeot DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa); 38219df918dSFrançois Tigeot 38319df918dSFrançois Tigeot adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER; 38419df918dSFrançois Tigeot 385a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, adpa); 38619df918dSFrançois Tigeot 387a2fdbec6SFrançois Tigeot if (wait_for((I915_READ(crt->adpa_reg) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0, 38819df918dSFrançois Tigeot 1000)) { 38919df918dSFrançois Tigeot DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER"); 390a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, save_adpa); 39119df918dSFrançois Tigeot } 39219df918dSFrançois Tigeot 39319df918dSFrançois Tigeot /* Check the status to see if both blue and green are on now */ 394a2fdbec6SFrançois Tigeot adpa = I915_READ(crt->adpa_reg); 39519df918dSFrançois Tigeot if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0) 39619df918dSFrançois Tigeot ret = true; 39719df918dSFrançois Tigeot else 39819df918dSFrançois Tigeot ret = false; 39919df918dSFrançois Tigeot 40019df918dSFrançois Tigeot DRM_DEBUG_KMS("valleyview hotplug adpa=0x%x, result %d\n", adpa, ret); 40119df918dSFrançois Tigeot 40219df918dSFrançois Tigeot return ret; 40319df918dSFrançois Tigeot } 40419df918dSFrançois Tigeot 405e3adcf8fSFrançois Tigeot /** 406e3adcf8fSFrançois Tigeot * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. 407e3adcf8fSFrançois Tigeot * 408e3adcf8fSFrançois Tigeot * Not for i915G/i915GM 409e3adcf8fSFrançois Tigeot * 410e3adcf8fSFrançois Tigeot * \return true if CRT is connected. 411e3adcf8fSFrançois Tigeot * \return false if CRT is disconnected. 412e3adcf8fSFrançois Tigeot */ 413e3adcf8fSFrançois Tigeot static bool intel_crt_detect_hotplug(struct drm_connector *connector) 414e3adcf8fSFrançois Tigeot { 415e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 416e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 417e3adcf8fSFrançois Tigeot u32 hotplug_en, orig, stat; 418e3adcf8fSFrançois Tigeot bool ret = false; 419e3adcf8fSFrançois Tigeot int i, tries = 0; 420e3adcf8fSFrançois Tigeot 421e3adcf8fSFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 422e3adcf8fSFrançois Tigeot return intel_ironlake_crt_detect_hotplug(connector); 423e3adcf8fSFrançois Tigeot 42419df918dSFrançois Tigeot if (IS_VALLEYVIEW(dev)) 42519df918dSFrançois Tigeot return valleyview_crt_detect_hotplug(connector); 42619df918dSFrançois Tigeot 427e3adcf8fSFrançois Tigeot /* 428e3adcf8fSFrançois Tigeot * On 4 series desktop, CRT detect sequence need to be done twice 429e3adcf8fSFrançois Tigeot * to get a reliable result. 430e3adcf8fSFrançois Tigeot */ 431e3adcf8fSFrançois Tigeot 432e3adcf8fSFrançois Tigeot if (IS_G4X(dev) && !IS_GM45(dev)) 433e3adcf8fSFrançois Tigeot tries = 2; 434e3adcf8fSFrançois Tigeot else 435e3adcf8fSFrançois Tigeot tries = 1; 436e3adcf8fSFrançois Tigeot hotplug_en = orig = I915_READ(PORT_HOTPLUG_EN); 437e3adcf8fSFrançois Tigeot hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; 438e3adcf8fSFrançois Tigeot 439e3adcf8fSFrançois Tigeot for (i = 0; i < tries ; i++) { 440e3adcf8fSFrançois Tigeot /* turn on the FORCE_DETECT */ 441e3adcf8fSFrançois Tigeot I915_WRITE(PORT_HOTPLUG_EN, hotplug_en); 442e3adcf8fSFrançois Tigeot /* wait for FORCE_DETECT to go off */ 44319df918dSFrançois Tigeot if (wait_for((I915_READ(PORT_HOTPLUG_EN) & 44419df918dSFrançois Tigeot CRT_HOTPLUG_FORCE_DETECT) == 0, 44519df918dSFrançois Tigeot 1000)) 446e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("timed out waiting for FORCE_DETECT to go off"); 447e3adcf8fSFrançois Tigeot } 448e3adcf8fSFrançois Tigeot 449e3adcf8fSFrançois Tigeot stat = I915_READ(PORT_HOTPLUG_STAT); 450e3adcf8fSFrançois Tigeot if ((stat & CRT_HOTPLUG_MONITOR_MASK) != CRT_HOTPLUG_MONITOR_NONE) 451e3adcf8fSFrançois Tigeot ret = true; 452e3adcf8fSFrançois Tigeot 453e3adcf8fSFrançois Tigeot /* clear the interrupt we just generated, if any */ 454e3adcf8fSFrançois Tigeot I915_WRITE(PORT_HOTPLUG_STAT, CRT_HOTPLUG_INT_STATUS); 455e3adcf8fSFrançois Tigeot 456e3adcf8fSFrançois Tigeot /* and put the bits back */ 457e3adcf8fSFrançois Tigeot I915_WRITE(PORT_HOTPLUG_EN, orig); 458e3adcf8fSFrançois Tigeot 459e3adcf8fSFrançois Tigeot return ret; 460e3adcf8fSFrançois Tigeot } 461e3adcf8fSFrançois Tigeot 46219df918dSFrançois Tigeot static struct edid *intel_crt_get_edid(struct drm_connector *connector, 463ba55f2f5SFrançois Tigeot struct i2c_adapter *i2c) 46419df918dSFrançois Tigeot { 46519df918dSFrançois Tigeot struct edid *edid; 46619df918dSFrançois Tigeot 46719df918dSFrançois Tigeot edid = drm_get_edid(connector, i2c); 46819df918dSFrançois Tigeot 46919df918dSFrançois Tigeot if (!edid && !intel_gmbus_is_forced_bit(i2c)) { 47019df918dSFrançois Tigeot DRM_DEBUG_KMS("CRT GMBUS EDID read failed, retry using GPIO bit-banging\n"); 47119df918dSFrançois Tigeot intel_gmbus_force_bit(i2c, true); 47219df918dSFrançois Tigeot edid = drm_get_edid(connector, i2c); 47319df918dSFrançois Tigeot intel_gmbus_force_bit(i2c, false); 47419df918dSFrançois Tigeot } 47519df918dSFrançois Tigeot 47619df918dSFrançois Tigeot return edid; 47719df918dSFrançois Tigeot } 47819df918dSFrançois Tigeot 47919df918dSFrançois Tigeot /* local version of intel_ddc_get_modes() to use intel_crt_get_edid() */ 48019df918dSFrançois Tigeot static int intel_crt_ddc_get_modes(struct drm_connector *connector, 481ba55f2f5SFrançois Tigeot struct i2c_adapter *adapter) 48219df918dSFrançois Tigeot { 48319df918dSFrançois Tigeot struct edid *edid; 48419df918dSFrançois Tigeot int ret; 48519df918dSFrançois Tigeot 48619df918dSFrançois Tigeot edid = intel_crt_get_edid(connector, adapter); 48719df918dSFrançois Tigeot if (!edid) 48819df918dSFrançois Tigeot return 0; 48919df918dSFrançois Tigeot 49019df918dSFrançois Tigeot ret = intel_connector_update_modes(connector, edid); 491158486a6SFrançois Tigeot kfree(edid); 49219df918dSFrançois Tigeot 49319df918dSFrançois Tigeot return ret; 49419df918dSFrançois Tigeot } 49519df918dSFrançois Tigeot 496e3adcf8fSFrançois Tigeot static bool intel_crt_detect_ddc(struct drm_connector *connector) 497e3adcf8fSFrançois Tigeot { 498e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 499e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private; 500e3adcf8fSFrançois Tigeot struct edid *edid; 501ba55f2f5SFrançois Tigeot struct i2c_adapter *i2c; 502e3adcf8fSFrançois Tigeot 50319df918dSFrançois Tigeot BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG); 50419df918dSFrançois Tigeot 5055d0b1887SFrançois Tigeot i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); 50619df918dSFrançois Tigeot edid = intel_crt_get_edid(connector, i2c); 50719df918dSFrançois Tigeot 50819df918dSFrançois Tigeot if (edid) { 50919df918dSFrançois Tigeot bool is_digital = edid->input & DRM_EDID_INPUT_DIGITAL; 51019df918dSFrançois Tigeot 511e3adcf8fSFrançois Tigeot /* 512e3adcf8fSFrançois Tigeot * This may be a DVI-I connector with a shared DDC 513e3adcf8fSFrançois Tigeot * link between analog and digital outputs, so we 514e3adcf8fSFrançois Tigeot * have to check the EDID input spec of the attached device. 515e3adcf8fSFrançois Tigeot */ 516e3adcf8fSFrançois Tigeot if (!is_digital) { 517e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n"); 518e3adcf8fSFrançois Tigeot return true; 51919df918dSFrançois Tigeot } 52019df918dSFrançois Tigeot 521e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n"); 52219df918dSFrançois Tigeot } else { 52319df918dSFrançois Tigeot DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [no valid EDID found]\n"); 524e3adcf8fSFrançois Tigeot } 52519df918dSFrançois Tigeot 5265d0b1887SFrançois Tigeot kfree(edid); 527e3adcf8fSFrançois Tigeot 528e3adcf8fSFrançois Tigeot return false; 529e3adcf8fSFrançois Tigeot } 530e3adcf8fSFrançois Tigeot 531e3adcf8fSFrançois Tigeot static enum drm_connector_status 532e3adcf8fSFrançois Tigeot intel_crt_load_detect(struct intel_crt *crt) 533e3adcf8fSFrançois Tigeot { 534e3adcf8fSFrançois Tigeot struct drm_device *dev = crt->base.base.dev; 535e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 536e3adcf8fSFrançois Tigeot uint32_t pipe = to_intel_crtc(crt->base.base.crtc)->pipe; 537e3adcf8fSFrançois Tigeot uint32_t save_bclrpat; 538e3adcf8fSFrançois Tigeot uint32_t save_vtotal; 539e3adcf8fSFrançois Tigeot uint32_t vtotal, vactive; 540e3adcf8fSFrançois Tigeot uint32_t vsample; 541e3adcf8fSFrançois Tigeot uint32_t vblank, vblank_start, vblank_end; 542e3adcf8fSFrançois Tigeot uint32_t dsl; 543e3adcf8fSFrançois Tigeot uint32_t bclrpat_reg; 544e3adcf8fSFrançois Tigeot uint32_t vtotal_reg; 545e3adcf8fSFrançois Tigeot uint32_t vblank_reg; 546e3adcf8fSFrançois Tigeot uint32_t vsync_reg; 547e3adcf8fSFrançois Tigeot uint32_t pipeconf_reg; 548e3adcf8fSFrançois Tigeot uint32_t pipe_dsl_reg; 549e3adcf8fSFrançois Tigeot uint8_t st00; 550e3adcf8fSFrançois Tigeot enum drm_connector_status status; 551e3adcf8fSFrançois Tigeot 552e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("starting load-detect on CRT\n"); 553e3adcf8fSFrançois Tigeot 554e3adcf8fSFrançois Tigeot bclrpat_reg = BCLRPAT(pipe); 555e3adcf8fSFrançois Tigeot vtotal_reg = VTOTAL(pipe); 556e3adcf8fSFrançois Tigeot vblank_reg = VBLANK(pipe); 557e3adcf8fSFrançois Tigeot vsync_reg = VSYNC(pipe); 558e3adcf8fSFrançois Tigeot pipeconf_reg = PIPECONF(pipe); 559e3adcf8fSFrançois Tigeot pipe_dsl_reg = PIPEDSL(pipe); 560e3adcf8fSFrançois Tigeot 561e3adcf8fSFrançois Tigeot save_bclrpat = I915_READ(bclrpat_reg); 562e3adcf8fSFrançois Tigeot save_vtotal = I915_READ(vtotal_reg); 563e3adcf8fSFrançois Tigeot vblank = I915_READ(vblank_reg); 564e3adcf8fSFrançois Tigeot 565e3adcf8fSFrançois Tigeot vtotal = ((save_vtotal >> 16) & 0xfff) + 1; 566e3adcf8fSFrançois Tigeot vactive = (save_vtotal & 0x7ff) + 1; 567e3adcf8fSFrançois Tigeot 568e3adcf8fSFrançois Tigeot vblank_start = (vblank & 0xfff) + 1; 569e3adcf8fSFrançois Tigeot vblank_end = ((vblank >> 16) & 0xfff) + 1; 570e3adcf8fSFrançois Tigeot 571e3adcf8fSFrançois Tigeot /* Set the border color to purple. */ 572e3adcf8fSFrançois Tigeot I915_WRITE(bclrpat_reg, 0x500050); 573e3adcf8fSFrançois Tigeot 574e3adcf8fSFrançois Tigeot if (!IS_GEN2(dev)) { 575e3adcf8fSFrançois Tigeot uint32_t pipeconf = I915_READ(pipeconf_reg); 576e3adcf8fSFrançois Tigeot I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER); 577e3adcf8fSFrançois Tigeot POSTING_READ(pipeconf_reg); 578e3adcf8fSFrançois Tigeot /* Wait for next Vblank to substitue 579e3adcf8fSFrançois Tigeot * border color for Color info */ 580e3adcf8fSFrançois Tigeot intel_wait_for_vblank(dev, pipe); 581e3adcf8fSFrançois Tigeot st00 = I915_READ8(VGA_MSR_WRITE); 582e3adcf8fSFrançois Tigeot status = ((st00 & (1 << 4)) != 0) ? 583e3adcf8fSFrançois Tigeot connector_status_connected : 584e3adcf8fSFrançois Tigeot connector_status_disconnected; 585e3adcf8fSFrançois Tigeot 586e3adcf8fSFrançois Tigeot I915_WRITE(pipeconf_reg, pipeconf); 587e3adcf8fSFrançois Tigeot } else { 588e3adcf8fSFrançois Tigeot bool restore_vblank = false; 589e3adcf8fSFrançois Tigeot int count, detect; 590e3adcf8fSFrançois Tigeot 591e3adcf8fSFrançois Tigeot /* 592e3adcf8fSFrançois Tigeot * If there isn't any border, add some. 593e3adcf8fSFrançois Tigeot * Yes, this will flicker 594e3adcf8fSFrançois Tigeot */ 595e3adcf8fSFrançois Tigeot if (vblank_start <= vactive && vblank_end >= vtotal) { 596e3adcf8fSFrançois Tigeot uint32_t vsync = I915_READ(vsync_reg); 597e3adcf8fSFrançois Tigeot uint32_t vsync_start = (vsync & 0xffff) + 1; 598e3adcf8fSFrançois Tigeot 599e3adcf8fSFrançois Tigeot vblank_start = vsync_start; 600e3adcf8fSFrançois Tigeot I915_WRITE(vblank_reg, 601e3adcf8fSFrançois Tigeot (vblank_start - 1) | 602e3adcf8fSFrançois Tigeot ((vblank_end - 1) << 16)); 603e3adcf8fSFrançois Tigeot restore_vblank = true; 604e3adcf8fSFrançois Tigeot } 605e3adcf8fSFrançois Tigeot /* sample in the vertical border, selecting the larger one */ 606e3adcf8fSFrançois Tigeot if (vblank_start - vactive >= vtotal - vblank_end) 607e3adcf8fSFrançois Tigeot vsample = (vblank_start + vactive) >> 1; 608e3adcf8fSFrançois Tigeot else 609e3adcf8fSFrançois Tigeot vsample = (vtotal + vblank_end) >> 1; 610e3adcf8fSFrançois Tigeot 611e3adcf8fSFrançois Tigeot /* 612e3adcf8fSFrançois Tigeot * Wait for the border to be displayed 613e3adcf8fSFrançois Tigeot */ 614e3adcf8fSFrançois Tigeot while (I915_READ(pipe_dsl_reg) >= vactive) 615e3adcf8fSFrançois Tigeot ; 616e3adcf8fSFrançois Tigeot while ((dsl = I915_READ(pipe_dsl_reg)) <= vsample) 617e3adcf8fSFrançois Tigeot ; 618e3adcf8fSFrançois Tigeot /* 619e3adcf8fSFrançois Tigeot * Watch ST00 for an entire scanline 620e3adcf8fSFrançois Tigeot */ 621e3adcf8fSFrançois Tigeot detect = 0; 622e3adcf8fSFrançois Tigeot count = 0; 623e3adcf8fSFrançois Tigeot do { 624e3adcf8fSFrançois Tigeot count++; 625e3adcf8fSFrançois Tigeot /* Read the ST00 VGA status register */ 626e3adcf8fSFrançois Tigeot st00 = I915_READ8(VGA_MSR_WRITE); 627e3adcf8fSFrançois Tigeot if (st00 & (1 << 4)) 628e3adcf8fSFrançois Tigeot detect++; 629e3adcf8fSFrançois Tigeot } while ((I915_READ(pipe_dsl_reg) == dsl)); 630e3adcf8fSFrançois Tigeot 631e3adcf8fSFrançois Tigeot /* restore vblank if necessary */ 632e3adcf8fSFrançois Tigeot if (restore_vblank) 633e3adcf8fSFrançois Tigeot I915_WRITE(vblank_reg, vblank); 634e3adcf8fSFrançois Tigeot /* 635e3adcf8fSFrançois Tigeot * If more than 3/4 of the scanline detected a monitor, 636e3adcf8fSFrançois Tigeot * then it is assumed to be present. This works even on i830, 637e3adcf8fSFrançois Tigeot * where there isn't any way to force the border color across 638e3adcf8fSFrançois Tigeot * the screen 639e3adcf8fSFrançois Tigeot */ 640e3adcf8fSFrançois Tigeot status = detect * 4 > count * 3 ? 641e3adcf8fSFrançois Tigeot connector_status_connected : 642e3adcf8fSFrançois Tigeot connector_status_disconnected; 643e3adcf8fSFrançois Tigeot } 644e3adcf8fSFrançois Tigeot 645e3adcf8fSFrançois Tigeot /* Restore previous settings */ 646e3adcf8fSFrançois Tigeot I915_WRITE(bclrpat_reg, save_bclrpat); 647e3adcf8fSFrançois Tigeot 648e3adcf8fSFrançois Tigeot return status; 649e3adcf8fSFrançois Tigeot } 650e3adcf8fSFrançois Tigeot 651e3adcf8fSFrançois Tigeot static enum drm_connector_status 652e3adcf8fSFrançois Tigeot intel_crt_detect(struct drm_connector *connector, bool force) 653e3adcf8fSFrançois Tigeot { 654e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 655ba55f2f5SFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 656e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 657ba55f2f5SFrançois Tigeot struct intel_encoder *intel_encoder = &crt->base; 658ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 659e3adcf8fSFrançois Tigeot enum drm_connector_status status; 660e3adcf8fSFrançois Tigeot struct intel_load_detect_pipe tmp; 661ba55f2f5SFrançois Tigeot struct drm_modeset_acquire_ctx ctx; 662ba55f2f5SFrançois Tigeot 6639edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s] force=%d\n", 664b4efbf42Szrj connector->base.id, connector->name, 6659edbd4a0SFrançois Tigeot force); 6669edbd4a0SFrançois Tigeot 667ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(intel_encoder); 668ba55f2f5SFrançois Tigeot intel_display_power_get(dev_priv, power_domain); 669ba55f2f5SFrançois Tigeot 670e3adcf8fSFrançois Tigeot if (I915_HAS_HOTPLUG(dev)) { 67119df918dSFrançois Tigeot /* We can not rely on the HPD pin always being correctly wired 67219df918dSFrançois Tigeot * up, for example many KVM do not pass it through, and so 67319df918dSFrançois Tigeot * only trust an assertion that the monitor is connected. 67419df918dSFrançois Tigeot */ 675e3adcf8fSFrançois Tigeot if (intel_crt_detect_hotplug(connector)) { 676e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT detected via hotplug\n"); 677ba55f2f5SFrançois Tigeot status = connector_status_connected; 678ba55f2f5SFrançois Tigeot goto out; 67919df918dSFrançois Tigeot } else 680e3adcf8fSFrançois Tigeot DRM_DEBUG_KMS("CRT not detected via hotplug\n"); 681e3adcf8fSFrançois Tigeot } 682e3adcf8fSFrançois Tigeot 683ba55f2f5SFrançois Tigeot if (intel_crt_detect_ddc(connector)) { 684ba55f2f5SFrançois Tigeot status = connector_status_connected; 685ba55f2f5SFrançois Tigeot goto out; 686ba55f2f5SFrançois Tigeot } 687e3adcf8fSFrançois Tigeot 68819df918dSFrançois Tigeot /* Load detection is broken on HPD capable machines. Whoever wants a 68919df918dSFrançois Tigeot * broken monitor (without edid) to work behind a broken kvm (that fails 69019df918dSFrançois Tigeot * to have the right resistors for HP detection) needs to fix this up. 69119df918dSFrançois Tigeot * For now just bail out. */ 692ba55f2f5SFrançois Tigeot if (I915_HAS_HOTPLUG(dev)) { 693ba55f2f5SFrançois Tigeot status = connector_status_disconnected; 694ba55f2f5SFrançois Tigeot goto out; 695ba55f2f5SFrançois Tigeot } 69619df918dSFrançois Tigeot 697ba55f2f5SFrançois Tigeot if (!force) { 698ba55f2f5SFrançois Tigeot status = connector->status; 699ba55f2f5SFrançois Tigeot goto out; 700ba55f2f5SFrançois Tigeot } 701e3adcf8fSFrançois Tigeot 70224edb884SFrançois Tigeot drm_modeset_acquire_init(&ctx, 0); 70324edb884SFrançois Tigeot 704e3adcf8fSFrançois Tigeot /* for pre-945g platforms use load detect */ 705ba55f2f5SFrançois Tigeot if (intel_get_load_detect_pipe(connector, NULL, &tmp, &ctx)) { 706e3adcf8fSFrançois Tigeot if (intel_crt_detect_ddc(connector)) 707e3adcf8fSFrançois Tigeot status = connector_status_connected; 708e3adcf8fSFrançois Tigeot else 709e3adcf8fSFrançois Tigeot status = intel_crt_load_detect(crt); 71024edb884SFrançois Tigeot intel_release_load_detect_pipe(connector, &tmp); 711e3adcf8fSFrançois Tigeot } else 712e3adcf8fSFrançois Tigeot status = connector_status_unknown; 713e3adcf8fSFrançois Tigeot 71424edb884SFrançois Tigeot drm_modeset_drop_locks(&ctx); 71524edb884SFrançois Tigeot drm_modeset_acquire_fini(&ctx); 71624edb884SFrançois Tigeot 717ba55f2f5SFrançois Tigeot out: 718ba55f2f5SFrançois Tigeot intel_display_power_put(dev_priv, power_domain); 719e3adcf8fSFrançois Tigeot return status; 720e3adcf8fSFrançois Tigeot } 721e3adcf8fSFrançois Tigeot 722e3adcf8fSFrançois Tigeot static void intel_crt_destroy(struct drm_connector *connector) 723e3adcf8fSFrançois Tigeot { 724e3adcf8fSFrançois Tigeot drm_connector_cleanup(connector); 7255d0b1887SFrançois Tigeot kfree(connector); 726e3adcf8fSFrançois Tigeot } 727e3adcf8fSFrançois Tigeot 728e3adcf8fSFrançois Tigeot static int intel_crt_get_modes(struct drm_connector *connector) 729e3adcf8fSFrançois Tigeot { 730e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 731e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 732ba55f2f5SFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 733ba55f2f5SFrançois Tigeot struct intel_encoder *intel_encoder = &crt->base; 734ba55f2f5SFrançois Tigeot enum intel_display_power_domain power_domain; 735e3adcf8fSFrançois Tigeot int ret; 736ba55f2f5SFrançois Tigeot struct i2c_adapter *i2c; 737ba55f2f5SFrançois Tigeot 738ba55f2f5SFrançois Tigeot power_domain = intel_display_port_power_domain(intel_encoder); 739ba55f2f5SFrançois Tigeot intel_display_power_get(dev_priv, power_domain); 740e3adcf8fSFrançois Tigeot 7415d0b1887SFrançois Tigeot i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->vbt.crt_ddc_pin); 74219df918dSFrançois Tigeot ret = intel_crt_ddc_get_modes(connector, i2c); 743e3adcf8fSFrançois Tigeot if (ret || !IS_G4X(dev)) 744ba55f2f5SFrançois Tigeot goto out; 745e3adcf8fSFrançois Tigeot 746e3adcf8fSFrançois Tigeot /* Try to probe digital port for output in DVI-I -> VGA mode. */ 74719df918dSFrançois Tigeot i2c = intel_gmbus_get_adapter(dev_priv, GMBUS_PORT_DPB); 748ba55f2f5SFrançois Tigeot ret = intel_crt_ddc_get_modes(connector, i2c); 749ba55f2f5SFrançois Tigeot 750ba55f2f5SFrançois Tigeot out: 751ba55f2f5SFrançois Tigeot intel_display_power_put(dev_priv, power_domain); 752ba55f2f5SFrançois Tigeot 753ba55f2f5SFrançois Tigeot return ret; 754e3adcf8fSFrançois Tigeot } 755e3adcf8fSFrançois Tigeot 756e3adcf8fSFrançois Tigeot static int intel_crt_set_property(struct drm_connector *connector, 757e3adcf8fSFrançois Tigeot struct drm_property *property, 758e3adcf8fSFrançois Tigeot uint64_t value) 759e3adcf8fSFrançois Tigeot { 760e3adcf8fSFrançois Tigeot return 0; 761e3adcf8fSFrançois Tigeot } 762e3adcf8fSFrançois Tigeot 763e3adcf8fSFrançois Tigeot static void intel_crt_reset(struct drm_connector *connector) 764e3adcf8fSFrançois Tigeot { 765e3adcf8fSFrançois Tigeot struct drm_device *dev = connector->dev; 76619df918dSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 767e3adcf8fSFrançois Tigeot struct intel_crt *crt = intel_attached_crt(connector); 768e3adcf8fSFrançois Tigeot 7699edbd4a0SFrançois Tigeot if (INTEL_INFO(dev)->gen >= 5) { 77019df918dSFrançois Tigeot u32 adpa; 77119df918dSFrançois Tigeot 772a2fdbec6SFrançois Tigeot adpa = I915_READ(crt->adpa_reg); 77319df918dSFrançois Tigeot adpa &= ~ADPA_CRT_HOTPLUG_MASK; 77419df918dSFrançois Tigeot adpa |= ADPA_HOTPLUG_BITS; 775a2fdbec6SFrançois Tigeot I915_WRITE(crt->adpa_reg, adpa); 776a2fdbec6SFrançois Tigeot POSTING_READ(crt->adpa_reg); 77719df918dSFrançois Tigeot 778*2c9916cdSFrançois Tigeot DRM_DEBUG_KMS("crt adpa set to 0x%x\n", adpa); 779e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 1; 780e3adcf8fSFrançois Tigeot } 781e3adcf8fSFrançois Tigeot 78219df918dSFrançois Tigeot } 78319df918dSFrançois Tigeot 784e3adcf8fSFrançois Tigeot /* 785e3adcf8fSFrançois Tigeot * Routines for controlling stuff on the analog port 786e3adcf8fSFrançois Tigeot */ 787e3adcf8fSFrançois Tigeot 788e3adcf8fSFrançois Tigeot static const struct drm_connector_funcs intel_crt_connector_funcs = { 789e3adcf8fSFrançois Tigeot .reset = intel_crt_reset, 79019df918dSFrançois Tigeot .dpms = intel_crt_dpms, 791e3adcf8fSFrançois Tigeot .detect = intel_crt_detect, 792e3adcf8fSFrançois Tigeot .fill_modes = drm_helper_probe_single_connector_modes, 793e3adcf8fSFrançois Tigeot .destroy = intel_crt_destroy, 794e3adcf8fSFrançois Tigeot .set_property = intel_crt_set_property, 795*2c9916cdSFrançois Tigeot .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 796*2c9916cdSFrançois Tigeot .atomic_get_property = intel_connector_atomic_get_property, 797e3adcf8fSFrançois Tigeot }; 798e3adcf8fSFrançois Tigeot 799e3adcf8fSFrançois Tigeot static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = { 800e3adcf8fSFrançois Tigeot .mode_valid = intel_crt_mode_valid, 801e3adcf8fSFrançois Tigeot .get_modes = intel_crt_get_modes, 802e3adcf8fSFrançois Tigeot .best_encoder = intel_best_encoder, 803e3adcf8fSFrançois Tigeot }; 804e3adcf8fSFrançois Tigeot 805e3adcf8fSFrançois Tigeot static const struct drm_encoder_funcs intel_crt_enc_funcs = { 806e3adcf8fSFrançois Tigeot .destroy = intel_encoder_destroy, 807e3adcf8fSFrançois Tigeot }; 808e3adcf8fSFrançois Tigeot 80924edb884SFrançois Tigeot static int intel_no_crt_dmi_callback(const struct dmi_system_id *id) 810e3adcf8fSFrançois Tigeot { 81119df918dSFrançois Tigeot DRM_INFO("Skipping CRT initialization for %s\n", id->ident); 812e3adcf8fSFrançois Tigeot return 1; 813e3adcf8fSFrançois Tigeot } 814e3adcf8fSFrançois Tigeot 815e3adcf8fSFrançois Tigeot static const struct dmi_system_id intel_no_crt[] = { 816e3adcf8fSFrançois Tigeot { 817e3adcf8fSFrançois Tigeot .callback = intel_no_crt_dmi_callback, 818e3adcf8fSFrançois Tigeot .ident = "ACER ZGB", 819e3adcf8fSFrançois Tigeot .matches = { 820e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "ACER"), 821e3adcf8fSFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), 822e3adcf8fSFrançois Tigeot }, 823e3adcf8fSFrançois Tigeot }, 824ba55f2f5SFrançois Tigeot { 825ba55f2f5SFrançois Tigeot .callback = intel_no_crt_dmi_callback, 826ba55f2f5SFrançois Tigeot .ident = "DELL XPS 8700", 827ba55f2f5SFrançois Tigeot .matches = { 828ba55f2f5SFrançois Tigeot DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), 829ba55f2f5SFrançois Tigeot DMI_MATCH(DMI_PRODUCT_NAME, "XPS 8700"), 830ba55f2f5SFrançois Tigeot }, 831ba55f2f5SFrançois Tigeot }, 832e3adcf8fSFrançois Tigeot { } 833e3adcf8fSFrançois Tigeot }; 834e3adcf8fSFrançois Tigeot 835e3adcf8fSFrançois Tigeot void intel_crt_init(struct drm_device *dev) 836e3adcf8fSFrançois Tigeot { 837e3adcf8fSFrançois Tigeot struct drm_connector *connector; 838e3adcf8fSFrançois Tigeot struct intel_crt *crt; 839e3adcf8fSFrançois Tigeot struct intel_connector *intel_connector; 840e3adcf8fSFrançois Tigeot struct drm_i915_private *dev_priv = dev->dev_private; 841e3adcf8fSFrançois Tigeot 842e3adcf8fSFrançois Tigeot /* Skip machines without VGA that falsely report hotplug events */ 843e3adcf8fSFrançois Tigeot if (dmi_check_system(intel_no_crt)) 844e3adcf8fSFrançois Tigeot return; 845e3adcf8fSFrançois Tigeot 846159fc1d7SFrançois Tigeot crt = kzalloc(sizeof(struct intel_crt), GFP_KERNEL); 84719df918dSFrançois Tigeot if (!crt) 84819df918dSFrançois Tigeot return; 84919df918dSFrançois Tigeot 8509edbd4a0SFrançois Tigeot intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL); 85119df918dSFrançois Tigeot if (!intel_connector) { 852158486a6SFrançois Tigeot kfree(crt); 85319df918dSFrançois Tigeot return; 85419df918dSFrançois Tigeot } 855e3adcf8fSFrançois Tigeot 856e3adcf8fSFrançois Tigeot connector = &intel_connector->base; 85719df918dSFrançois Tigeot crt->connector = intel_connector; 858e3adcf8fSFrançois Tigeot drm_connector_init(dev, &intel_connector->base, 859e3adcf8fSFrançois Tigeot &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); 860e3adcf8fSFrançois Tigeot 861e3adcf8fSFrançois Tigeot drm_encoder_init(dev, &crt->base.base, &intel_crt_enc_funcs, 862e3adcf8fSFrançois Tigeot DRM_MODE_ENCODER_DAC); 863e3adcf8fSFrançois Tigeot 864e3adcf8fSFrançois Tigeot intel_connector_attach_encoder(intel_connector, &crt->base); 865e3adcf8fSFrançois Tigeot 866e3adcf8fSFrançois Tigeot crt->base.type = INTEL_OUTPUT_ANALOG; 867ba55f2f5SFrançois Tigeot crt->base.cloneable = (1 << INTEL_OUTPUT_DVO) | (1 << INTEL_OUTPUT_HDMI); 868e9243325SFrançois Tigeot if (IS_I830(dev)) 869e9243325SFrançois Tigeot crt->base.crtc_mask = (1 << 0); 870e9243325SFrançois Tigeot else 871e9243325SFrançois Tigeot crt->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); 872e9243325SFrançois Tigeot 873e3adcf8fSFrançois Tigeot if (IS_GEN2(dev)) 874e3adcf8fSFrançois Tigeot connector->interlace_allowed = 0; 875e3adcf8fSFrançois Tigeot else 876e3adcf8fSFrançois Tigeot connector->interlace_allowed = 1; 877e3adcf8fSFrançois Tigeot connector->doublescan_allowed = 0; 878e3adcf8fSFrançois Tigeot 879e9243325SFrançois Tigeot if (HAS_PCH_SPLIT(dev)) 880e9243325SFrançois Tigeot crt->adpa_reg = PCH_ADPA; 881e9243325SFrançois Tigeot else if (IS_VALLEYVIEW(dev)) 882e9243325SFrançois Tigeot crt->adpa_reg = VLV_ADPA; 883e9243325SFrançois Tigeot else 884e9243325SFrançois Tigeot crt->adpa_reg = ADPA; 885e9243325SFrançois Tigeot 8868e26cdf6SFrançois Tigeot crt->base.compute_config = intel_crt_compute_config; 88719df918dSFrançois Tigeot crt->base.disable = intel_disable_crt; 88819df918dSFrançois Tigeot crt->base.enable = intel_enable_crt; 8898e26cdf6SFrançois Tigeot if (I915_HAS_HOTPLUG(dev)) 8908e26cdf6SFrançois Tigeot crt->base.hpd_pin = HPD_CRT; 8919edbd4a0SFrançois Tigeot if (HAS_DDI(dev)) { 8929edbd4a0SFrançois Tigeot crt->base.get_config = hsw_crt_get_config; 89319df918dSFrançois Tigeot crt->base.get_hw_state = intel_ddi_get_hw_state; 89424edb884SFrançois Tigeot crt->base.pre_enable = hsw_crt_pre_enable; 89524edb884SFrançois Tigeot crt->base.post_disable = hsw_crt_post_disable; 8969edbd4a0SFrançois Tigeot } else { 8979edbd4a0SFrançois Tigeot crt->base.get_config = intel_crt_get_config; 89819df918dSFrançois Tigeot crt->base.get_hw_state = intel_crt_get_hw_state; 8999edbd4a0SFrançois Tigeot } 90019df918dSFrançois Tigeot intel_connector->get_hw_state = intel_connector_get_hw_state; 901ba55f2f5SFrançois Tigeot intel_connector->unregister = intel_connector_unregister; 90219df918dSFrançois Tigeot 903e3adcf8fSFrançois Tigeot drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); 904e3adcf8fSFrançois Tigeot 905c6f73aabSFrançois Tigeot drm_connector_register(connector); 906e3adcf8fSFrançois Tigeot 9078e26cdf6SFrançois Tigeot if (!I915_HAS_HOTPLUG(dev)) 9088e26cdf6SFrançois Tigeot intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT; 909e3adcf8fSFrançois Tigeot 910e3adcf8fSFrançois Tigeot /* 911e3adcf8fSFrançois Tigeot * Configure the automatic hotplug detection stuff 912e3adcf8fSFrançois Tigeot */ 913e3adcf8fSFrançois Tigeot crt->force_hotplug_required = 0; 914e3adcf8fSFrançois Tigeot 91519df918dSFrançois Tigeot /* 91619df918dSFrançois Tigeot * TODO: find a proper way to discover whether we need to set the the 91719df918dSFrançois Tigeot * polarity and link reversal bits or not, instead of relying on the 91819df918dSFrançois Tigeot * BIOS. 91919df918dSFrançois Tigeot */ 92019df918dSFrançois Tigeot if (HAS_PCH_LPT(dev)) { 92119df918dSFrançois Tigeot u32 fdi_config = FDI_RX_POLARITY_REVERSED_LPT | 92219df918dSFrançois Tigeot FDI_RX_LINK_REVERSAL_OVERRIDE; 92319df918dSFrançois Tigeot 92419df918dSFrançois Tigeot dev_priv->fdi_rx_config = I915_READ(_FDI_RXA_CTL) & fdi_config; 92519df918dSFrançois Tigeot } 926ba55f2f5SFrançois Tigeot 927ba55f2f5SFrançois Tigeot intel_crt_reset(connector); 928e3adcf8fSFrançois Tigeot } 929