From 395072592e8eabf1b2ea424f1ed4c7458440dbf4 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 22 Sep 2011 12:30:16 -0700 Subject: drm/i915: broken copyright encoding in intel_bios.c Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 61abef8a..4c530fa 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -1,5 +1,5 @@ /* - * Copyright © 2006 Intel Corporation + * Copyright © 2006 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), -- cgit v0.10.2 From 562396b9e46bcb34cc18f513fea1c4764d8ea06d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 22 Sep 2011 12:32:35 -0700 Subject: drm/i915: Use DRM_DEBUG_KMS for all messages in intel_bios.c These are all KMS related anyways, so don't hide them under other debug levels. Signed-off-by: Keith Packard Reviewed-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 4c530fa..dcbc839 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -309,6 +309,11 @@ parse_general_features(struct drm_i915_private *dev_priv, dev_priv->lvds_use_ssc = general->enable_ssc; dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, general->ssc_freq); + DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d\n", + dev_priv->int_tv_support, + dev_priv->int_crt_support, + dev_priv->lvds_use_ssc, + dev_priv->lvds_ssc_freq); } } @@ -610,7 +615,7 @@ init_vbt_defaults(struct drm_i915_private *dev_priv) /* Default to using SSC */ dev_priv->lvds_use_ssc = 1; dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1); - DRM_DEBUG("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq); + DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq); /* eDP data */ dev_priv->edp.bpp = 18; @@ -639,7 +644,7 @@ intel_parse_bios(struct drm_device *dev) if (dev_priv->opregion.vbt) { struct vbt_header *vbt = dev_priv->opregion.vbt; if (memcmp(vbt->signature, "$VBT", 4) == 0) { - DRM_DEBUG_DRIVER("Using VBT from OpRegion: %20s\n", + DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n", vbt->signature); bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset); } else -- cgit v0.10.2 From abd06860185fc613776adae792772e076d77caf2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 26 Sep 2011 14:24:14 -0700 Subject: drv/i915: Pull display_clock_mode out of VBT table This tells the driver whether a CK505 clock source is available on pre-PCH hardware. If so, it should be used as the non-SSC source, leaving the internal clock for use as the SSC source. Signed-off-by: Keith Packard Reviewed-by: Chris Wison diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7916bd9..18df595 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -357,6 +357,7 @@ typedef struct drm_i915_private { unsigned int lvds_vbt:1; unsigned int int_crt_support:1; unsigned int lvds_use_ssc:1; + unsigned int display_clock_mode:1; int lvds_ssc_freq; struct { int rate; diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index dcbc839..eb58784 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -309,11 +309,13 @@ parse_general_features(struct drm_i915_private *dev_priv, dev_priv->lvds_use_ssc = general->enable_ssc; dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, general->ssc_freq); - DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d\n", + dev_priv->display_clock_mode = general->display_clock_mode; + DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d\n", dev_priv->int_tv_support, dev_priv->int_crt_support, dev_priv->lvds_use_ssc, - dev_priv->lvds_ssc_freq); + dev_priv->lvds_ssc_freq, + dev_priv->display_clock_mode); } } diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index 5f8e4ed..02b1b624 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -120,7 +120,9 @@ struct bdb_general_features { u8 ssc_freq:1; u8 enable_lfp_on_override:1; u8 disable_ssc_ddt:1; - u8 rsvd8:3; /* finish byte */ + u8 rsvd7:1; + u8 display_clock_mode:1; + u8 rsvd8:1; /* finish byte */ /* bits 3 */ u8 disable_smooth_vision:1; -- cgit v0.10.2 From d2830bdb84fac6e84e45ffe1a4c9bec762295d7d Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 26 Sep 2011 14:25:57 -0700 Subject: drm/i915: Document a few more BDB_GENERAL_FEATURES bits from PCH BIOS This includes whether an eDP panel is present, and whether that should use SSC (and at what frequency) Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index 02b1b624..72fb500 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -135,7 +135,10 @@ struct bdb_general_features { /* bits 5 */ u8 int_crt_support:1; u8 int_tv_support:1; - u8 rsvd11:6; /* finish byte */ + u8 int_efp_support:1; + u8 dp_ssc_enb:1; /* PCH attached eDP supports SSC */ + u8 dp_ssc_freq:1; /* SSC freq for PCH attached eDP */ + u8 rsvd11:3; /* finish byte */ } __attribute__((packed)); /* pre-915 */ -- cgit v0.10.2 From 72bbe58cd9568c7766cc219a779ea68a02132797 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 26 Sep 2011 16:09:45 -0700 Subject: drm/i915: Allow SSC parameter to override VBT value Allow SSC to be enabled even when the BIOS disables it for testing SSC paths. Signed-off-by: Keith Packard Reviewed-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index f07e425..58480de 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -79,11 +79,11 @@ MODULE_PARM_DESC(lvds_downclock, "Use panel (LVDS/eDP) downclocking for power savings " "(default: false)"); -unsigned int i915_panel_use_ssc __read_mostly = 1; +unsigned int i915_panel_use_ssc __read_mostly = -1; module_param_named(lvds_use_ssc, i915_panel_use_ssc, int, 0600); MODULE_PARM_DESC(lvds_use_ssc, "Use Spread Spectrum Clock with panels [LVDS/eDP] " - "(default: true)"); + "(default: auto from VBT)"); int i915_vbt_sdvo_panel_type __read_mostly = -1; module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 04411ad..6039496 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4584,7 +4584,9 @@ static void intel_update_watermarks(struct drm_device *dev) static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) { - return dev_priv->lvds_use_ssc && i915_panel_use_ssc + if (i915_panel_use_ssc >= 0) + return i915_panel_use_ssc != 0; + return dev_priv->lvds_use_ssc && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); } -- cgit v0.10.2 From 199e5d79f1c988a8039fa75b736a3adcdda56abc Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 22 Sep 2011 12:01:57 -0700 Subject: drm/i915: Fix PCH SSC reference clock settings The PCH refclk settings are global, so we need to look at all of the encoders, not just the current encoder when deciding how to configure it. Also, handle systems with more than one panel (any combination of PCH/non-PCH eDP and LVDS). Disable SSC clocks when no panels are connected. Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6039496..f999935 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5113,31 +5113,32 @@ static void ironlake_update_pch_refclk(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mode_config *mode_config = &dev->mode_config; - struct drm_crtc *crtc; struct intel_encoder *encoder; - struct intel_encoder *has_edp_encoder = NULL; u32 temp; bool has_lvds = false; + bool has_cpu_edp = false; + bool has_pch_edp = false; + bool has_panel = false; /* We need to take the global config into account */ - list_for_each_entry(crtc, &mode_config->crtc_list, head) { - if (!crtc->enabled) - continue; - - list_for_each_entry(encoder, &mode_config->encoder_list, - base.head) { - if (encoder->base.crtc != crtc) - continue; - - switch (encoder->type) { - case INTEL_OUTPUT_LVDS: - has_lvds = true; - case INTEL_OUTPUT_EDP: - has_edp_encoder = encoder; - break; - } + list_for_each_entry(encoder, &mode_config->encoder_list, + base.head) { + switch (encoder->type) { + case INTEL_OUTPUT_LVDS: + has_panel = true; + has_lvds = true; + break; + case INTEL_OUTPUT_EDP: + has_panel = true; + if (intel_encoder_is_pch_edp(&encoder->base)) + has_pch_edp = true; + else + has_cpu_edp = true; + break; } } + DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d\n", + has_panel, has_lvds, has_pch_edp, has_cpu_edp); /* Ironlake: try to setup display ref clock before DPLL * enabling. This is only under driver's control after @@ -5148,36 +5149,57 @@ static void ironlake_update_pch_refclk(struct drm_device *dev) /* Always enable nonspread source */ temp &= ~DREF_NONSPREAD_SOURCE_MASK; temp |= DREF_NONSPREAD_SOURCE_ENABLE; - temp &= ~DREF_SSC_SOURCE_MASK; - temp |= DREF_SSC_SOURCE_ENABLE; - I915_WRITE(PCH_DREF_CONTROL, temp); - POSTING_READ(PCH_DREF_CONTROL); - udelay(200); + if (has_panel) { + temp &= ~DREF_SSC_SOURCE_MASK; + temp |= DREF_SSC_SOURCE_ENABLE; - if (has_edp_encoder) { + /* SSC must be turned on before enabling the CPU output */ if (intel_panel_use_ssc(dev_priv)) { + DRM_DEBUG_KMS("Using SSC on panel\n"); temp |= DREF_SSC1_ENABLE; - I915_WRITE(PCH_DREF_CONTROL, temp); - - POSTING_READ(PCH_DREF_CONTROL); - udelay(200); } + + /* Get SSC going before enabling the outputs */ + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + udelay(200); + temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; /* Enable CPU source on CPU attached eDP */ - if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) { - if (intel_panel_use_ssc(dev_priv)) + if (has_cpu_edp) { + if (intel_panel_use_ssc(dev_priv)) { + DRM_DEBUG_KMS("Using SSC on eDP\n"); temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; + } else temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; - } else { - /* Enable SSC on PCH eDP if needed */ - if (intel_panel_use_ssc(dev_priv)) { - DRM_ERROR("enabling SSC on PCH\n"); - temp |= DREF_SUPERSPREAD_SOURCE_ENABLE; - } - } + } else + temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE; + + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + udelay(200); + } else { + DRM_DEBUG_KMS("Disabling SSC entirely\n"); + + temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; + + /* Turn off CPU output */ + temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE; + + I915_WRITE(PCH_DREF_CONTROL, temp); + POSTING_READ(PCH_DREF_CONTROL); + udelay(200); + + /* Turn off the SSC source */ + temp &= ~DREF_SSC_SOURCE_MASK; + temp |= DREF_SSC_SOURCE_DISABLE; + + /* Turn off SSC1 */ + temp &= ~ DREF_SSC1_ENABLE; + I915_WRITE(PCH_DREF_CONTROL, temp); POSTING_READ(PCH_DREF_CONTROL); udelay(200); -- cgit v0.10.2 From 99eb6a01e5ac6cf28aadc64e6ff346939874dfd2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 26 Sep 2011 14:29:12 -0700 Subject: drm/i915: Use CK505 as non-SSC source where available When trying to use SSC on Ibex Peak without CK505, any non-SSC outputs (like VGA or TV) get broken. So, do not use SSC on Ibex Peak unless there is a CK505 available (as specified by the VBT). On Cougar Point, all clocking is internal, so SSC can always be used, and there will never be a CK505 available. This eliminates VGA shimmer on some Ironlake machines which have a CK505 clock source. References: https://bugzilla.kernel.org/show_bug.cgi?id=21742 References: https://bugs.freedesktop.org/show_bug.cgi?id=38750 Signed-off-by: Keith Packard Reviewed-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index f999935..4c9684c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5119,6 +5119,8 @@ static void ironlake_update_pch_refclk(struct drm_device *dev) bool has_cpu_edp = false; bool has_pch_edp = false; bool has_panel = false; + bool has_ck505 = false; + bool can_ssc = false; /* We need to take the global config into account */ list_for_each_entry(encoder, &mode_config->encoder_list, @@ -5137,8 +5139,18 @@ static void ironlake_update_pch_refclk(struct drm_device *dev) break; } } - DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d\n", - has_panel, has_lvds, has_pch_edp, has_cpu_edp); + + if (HAS_PCH_IBX(dev)) { + has_ck505 = dev_priv->display_clock_mode; + can_ssc = has_ck505; + } else { + has_ck505 = false; + can_ssc = true; + } + + DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d has_ck505 %d\n", + has_panel, has_lvds, has_pch_edp, has_cpu_edp, + has_ck505); /* Ironlake: try to setup display ref clock before DPLL * enabling. This is only under driver's control after @@ -5148,14 +5160,18 @@ static void ironlake_update_pch_refclk(struct drm_device *dev) temp = I915_READ(PCH_DREF_CONTROL); /* Always enable nonspread source */ temp &= ~DREF_NONSPREAD_SOURCE_MASK; - temp |= DREF_NONSPREAD_SOURCE_ENABLE; + + if (has_ck505) + temp |= DREF_NONSPREAD_CK505_ENABLE; + else + temp |= DREF_NONSPREAD_SOURCE_ENABLE; if (has_panel) { temp &= ~DREF_SSC_SOURCE_MASK; temp |= DREF_SSC_SOURCE_ENABLE; /* SSC must be turned on before enabling the CPU output */ - if (intel_panel_use_ssc(dev_priv)) { + if (intel_panel_use_ssc(dev_priv) && can_ssc) { DRM_DEBUG_KMS("Using SSC on panel\n"); temp |= DREF_SSC1_ENABLE; } @@ -5169,7 +5185,7 @@ static void ironlake_update_pch_refclk(struct drm_device *dev) /* Enable CPU source on CPU attached eDP */ if (has_cpu_edp) { - if (intel_panel_use_ssc(dev_priv)) { + if (intel_panel_use_ssc(dev_priv) && can_ssc) { DRM_DEBUG_KMS("Using SSC on eDP\n"); temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; } -- cgit v0.10.2 From afffb9dfb62a9eb2a6e467a3875907189e49a2d2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 26 Sep 2011 20:42:37 -0700 Subject: drm/i915: All PCH refclks are 120MHz I can't find any reference clocks which run at 96MHz as seems to be indicated from the comments in this code. Signed-off-by: Keith Packard Reviewed-by: Chris Wilson diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 4c9684c..b072a35 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5281,16 +5281,10 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, num_connectors++; } - if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { - refclk = dev_priv->lvds_ssc_freq * 1000; - DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", - refclk / 1000); - } else { - refclk = 96000; - if (!has_edp_encoder || - intel_encoder_is_pch_edp(&has_edp_encoder->base)) - refclk = 120000; /* 120Mhz refclk */ - } + /* + * Every reference clock in a PCH system is 120MHz + */ + refclk = 120000; /* * Returns a set of divisors for the desired target clock with the given -- cgit v0.10.2 From 9fb526db979581841227e1ed4f75a5fbe853db4a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 26 Sep 2011 22:24:57 -0700 Subject: drm/i915: Initialize PCH refclks at modeset init time The reference clock configuration must be done before any mode setting can occur as all outputs must be disabled to change anything. Initialize the clocks after turning everything off during the initialization process. Also, re-initialize the refclk at resume time. Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 58480de..2b6c2d2 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -471,6 +471,9 @@ static int i915_drm_thaw(struct drm_device *dev) error = i915_gem_init_ringbuffer(dev); mutex_unlock(&dev->struct_mutex); + if (HAS_PCH_SPLIT(dev)) + ironlake_init_pch_refclk(dev); + drm_mode_config_reset(dev); drm_irq_install(dev); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 18df595..98f2e0b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1302,6 +1302,7 @@ extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); extern bool intel_fbc_enabled(struct drm_device *dev); extern void intel_disable_fbc(struct drm_device *dev); extern bool ironlake_set_drps(struct drm_device *dev, u8 val); +extern void ironlake_init_pch_refclk(struct drm_device *dev); extern void ironlake_enable_rc6(struct drm_device *dev); extern void gen6_set_rps(struct drm_device *dev, u8 val); extern void intel_detect_pch (struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index b072a35..91d7d5ed 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5109,7 +5109,10 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc, return ret; } -static void ironlake_update_pch_refclk(struct drm_device *dev) +/* + * Initialize reference clocks when the driver loads + */ +void ironlake_init_pch_refclk(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_mode_config *mode_config = &dev->mode_config; @@ -5411,8 +5414,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, &m_n); - ironlake_update_pch_refclk(dev); - fp = clock.n << 16 | clock.m1 << 8 | clock.m2; if (has_reduced_clock) fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | @@ -7284,6 +7285,9 @@ static void intel_setup_outputs(struct drm_device *dev) /* disable all the possible outputs/crtcs before entering KMS mode */ drm_helper_disable_unused_functions(dev); + + if (HAS_PCH_SPLIT(dev)) + ironlake_init_pch_refclk(dev); } static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) -- cgit v0.10.2 From 7fe0b973fadad1634c1a86dda7c6200146299082 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 19 Sep 2011 13:31:02 -0700 Subject: drm/i915: Enable digital port hotplug on PCH systems We were relying on the BIOS to set these bits, which doesn't always happen. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 9cbb0cd..c22823b 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -1777,6 +1777,26 @@ static void ironlake_irq_preinstall(struct drm_device *dev) POSTING_READ(SDEIER); } +/* + * Enable digital hotplug on the PCH, and configure the DP short pulse + * duration to 2ms (which is the minimum in the Display Port spec) + * + * This register is the same on all known PCH chips. + */ + +static void ironlake_enable_pch_hotplug(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + u32 hotplug; + + hotplug = I915_READ(PCH_PORT_HOTPLUG); + hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK); + hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms; + hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms; + hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms; + I915_WRITE(PCH_PORT_HOTPLUG, hotplug); +} + static int ironlake_irq_postinstall(struct drm_device *dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; @@ -1839,6 +1859,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev) I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); + ironlake_enable_pch_hotplug(dev); + if (IS_IRONLAKE_M(dev)) { /* Clear & enable PCU event interrupts */ I915_WRITE(DEIIR, DE_PCU_EVENT); @@ -1896,6 +1918,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev) I915_WRITE(SDEIER, hotplug_mask); POSTING_READ(SDEIER); + ironlake_enable_pch_hotplug(dev); + return 0; } diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 542453f..b7fbb74 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2903,12 +2903,13 @@ #define SDEIER 0xc400c /* digital port hotplug */ -#define PCH_PORT_HOTPLUG 0xc4030 +#define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */ #define PORTD_HOTPLUG_ENABLE (1 << 20) #define PORTD_PULSE_DURATION_2ms (0) #define PORTD_PULSE_DURATION_4_5ms (1 << 18) #define PORTD_PULSE_DURATION_6ms (2 << 18) #define PORTD_PULSE_DURATION_100ms (3 << 18) +#define PORTD_PULSE_DURATION_MASK (3 << 18) #define PORTD_HOTPLUG_NO_DETECT (0) #define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) #define PORTD_HOTPLUG_LONG_DETECT (1 << 17) @@ -2917,6 +2918,7 @@ #define PORTC_PULSE_DURATION_4_5ms (1 << 10) #define PORTC_PULSE_DURATION_6ms (2 << 10) #define PORTC_PULSE_DURATION_100ms (3 << 10) +#define PORTC_PULSE_DURATION_MASK (3 << 10) #define PORTC_HOTPLUG_NO_DETECT (0) #define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) #define PORTC_HOTPLUG_LONG_DETECT (1 << 9) @@ -2925,6 +2927,7 @@ #define PORTB_PULSE_DURATION_4_5ms (1 << 2) #define PORTB_PULSE_DURATION_6ms (2 << 2) #define PORTB_PULSE_DURATION_100ms (3 << 2) +#define PORTB_PULSE_DURATION_MASK (3 << 2) #define PORTB_HOTPLUG_NO_DETECT (0) #define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) #define PORTB_HOTPLUG_LONG_DETECT (1 << 1) -- cgit v0.10.2 From 192aac1f19e7a2c69fe486b863c914aabd7acc69 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Tue, 20 Sep 2011 10:12:44 -0700 Subject: drm/i915: Shut down PCH interrupts during irq_uninstall This masks out all interrupts and ack's any pending ones at IRQ uninstall time to make sure we don't receive any unexpected interrupts later on. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index c22823b..adeab2a 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -2044,6 +2044,10 @@ static void ironlake_irq_uninstall(struct drm_device *dev) I915_WRITE(GTIMR, 0xffffffff); I915_WRITE(GTIER, 0x0); I915_WRITE(GTIIR, I915_READ(GTIIR)); + + I915_WRITE(SDEIMR, 0xffffffff); + I915_WRITE(SDEIER, 0x0); + I915_WRITE(SDEIIR, I915_READ(SDEIIR)); } static void i915_driver_irq_uninstall(struct drm_device * dev) -- cgit v0.10.2 From 47f0eb2234a2a1c790825393bbaccfadf82463d3 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 19 Sep 2011 14:33:26 -0700 Subject: drm/i915: Only use VBT panel mode on eDP if no EDID is found We're going to assume that EDID is more reliable than the VBT tables for eDP panels, which is notably true on MacBook machines where the VBT contains completely bogus data. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 44fef5e..3717fa6 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1755,7 +1755,16 @@ static int intel_dp_get_modes(struct drm_connector *connector) /* if eDP has no EDID, try to use fixed panel mode from VBT */ if (is_edp(intel_dp)) { - if (dev_priv->panel_fixed_mode != NULL) { + /* initialize panel mode from VBT if available for eDP */ + if (dev_priv->panel_fixed_mode == NULL && dev_priv->lfp_lvds_vbt_mode != NULL) { + dev_priv->panel_fixed_mode = + drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); + if (dev_priv->panel_fixed_mode) { + dev_priv->panel_fixed_mode->type |= + DRM_MODE_TYPE_PREFERRED; + } + } + if (dev_priv->panel_fixed_mode) { struct drm_display_mode *mode; mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); drm_mode_probed_add(connector, mode); @@ -2068,15 +2077,6 @@ intel_dp_init(struct drm_device *dev, int output_reg) intel_encoder->hot_plug = intel_dp_hot_plug; if (is_edp(intel_dp)) { - /* initialize panel mode from VBT if available for eDP */ - if (dev_priv->lfp_lvds_vbt_mode) { - dev_priv->panel_fixed_mode = - drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); - if (dev_priv->panel_fixed_mode) { - dev_priv->panel_fixed_mode->type |= - DRM_MODE_TYPE_PREFERRED; - } - } dev_priv->int_edp_connector = connector; intel_panel_setup_backlight(dev); } -- cgit v0.10.2 From 9b984daec45632c4c1ef6e628dca4d2bc8f544ed Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 19 Sep 2011 13:54:47 -0700 Subject: drm/i915: Check eDP power when doing aux channel communications Verify that the eDP VDD is on, either with the panel being on or with the VDD force-on bit being set. This demonstrates that in many instances, VDD is not on when needed, which leads to failed EDID communications. Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3717fa6..1bf3258 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -279,6 +279,24 @@ intel_hrawclk(struct drm_device *dev) } } +static void +intel_dp_check_edp(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + u32 pp_status, pp_control; + if (!is_edp(intel_dp)) + return; + pp_status = I915_READ(PCH_PP_STATUS); + pp_control = I915_READ(PCH_PP_CONTROL); + if ((pp_status & PP_ON) == 0 && (pp_control & EDP_FORCE_VDD) == 0) { + WARN(1, "eDP powered off while attempting aux channel communication.\n"); + DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n", + pp_status, + I915_READ(PCH_PP_CONTROL)); + } +} + static int intel_dp_aux_ch(struct intel_dp *intel_dp, uint8_t *send, int send_bytes, @@ -295,6 +313,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, uint32_t aux_clock_divider; int try, precharge; + intel_dp_check_edp(intel_dp); /* The clock divider is based off the hrawclk, * and would like to run at 2MHz. So, take the * hrawclk value and divide by 2 and use that @@ -408,6 +427,7 @@ intel_dp_aux_native_write(struct intel_dp *intel_dp, int msg_bytes; uint8_t ack; + intel_dp_check_edp(intel_dp); if (send_bytes > 16) return -1; msg[0] = AUX_NATIVE_WRITE << 4; @@ -450,6 +470,7 @@ intel_dp_aux_native_read(struct intel_dp *intel_dp, uint8_t ack; int ret; + intel_dp_check_edp(intel_dp); msg[0] = AUX_NATIVE_READ << 4; msg[1] = address >> 8; msg[2] = address & 0xff; @@ -493,6 +514,7 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, int reply_bytes; int ret; + intel_dp_check_edp(intel_dp); /* Set up the command byte */ if (mode & MODE_I2C_READ) msg[0] = AUX_I2C_READ << 4; -- cgit v0.10.2 From 1c0ae80a5e2893a3a3ed9582e46249ff559d2739 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Mon, 19 Sep 2011 13:59:29 -0700 Subject: drm/i915: Unlock PCH_PP_CONTROL always Avoid any question about locked registers by just writing the unlock pattern with every write to the register. Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index b7fbb74..5596e8e 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3311,6 +3311,7 @@ #define PCH_PP_STATUS 0xc7200 #define PCH_PP_CONTROL 0xc7204 #define PANEL_UNLOCK_REGS (0xabcd << 16) +#define PANEL_UNLOCK_MASK (0xffff << 16) #define EDP_FORCE_VDD (1 << 3) #define EDP_BLC_ENABLE (1 << 2) #define PANEL_POWER_RESET (1 << 1) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 1bf3258..9c2158c 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -840,6 +840,8 @@ static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) msleep(dev_priv->panel_t3); pp = I915_READ(PCH_PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + pp |= PANEL_UNLOCK_REGS; pp |= EDP_FORCE_VDD; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); @@ -852,6 +854,8 @@ static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp) u32 pp; pp = I915_READ(PCH_PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + pp |= PANEL_UNLOCK_REGS; pp &= ~EDP_FORCE_VDD; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); @@ -871,13 +875,15 @@ static bool ironlake_edp_panel_on (struct intel_dp *intel_dp) return true; pp = I915_READ(PCH_PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + pp |= PANEL_UNLOCK_REGS; /* ILK workaround: disable reset around power sequence */ pp &= ~PANEL_POWER_RESET; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); - pp |= PANEL_UNLOCK_REGS | POWER_TARGET_ON; + pp |= POWER_TARGET_ON; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); @@ -900,6 +906,8 @@ static void ironlake_edp_panel_off (struct drm_device *dev) PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK; pp = I915_READ(PCH_PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + pp |= PANEL_UNLOCK_REGS; /* ILK workaround: disable reset around power sequence */ pp &= ~PANEL_POWER_RESET; @@ -933,6 +941,8 @@ static void ironlake_edp_backlight_on (struct drm_device *dev) */ msleep(300); pp = I915_READ(PCH_PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + pp |= PANEL_UNLOCK_REGS; pp |= EDP_BLC_ENABLE; I915_WRITE(PCH_PP_CONTROL, pp); } @@ -944,6 +954,8 @@ static void ironlake_edp_backlight_off (struct drm_device *dev) DRM_DEBUG_KMS("\n"); pp = I915_READ(PCH_PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + pp |= PANEL_UNLOCK_REGS; pp &= ~EDP_BLC_ENABLE; I915_WRITE(PCH_PP_CONTROL, pp); } -- cgit v0.10.2 From 97af61f57e03a39afa309d1c8a0d8fb9331e2f89 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 28 Sep 2011 16:23:51 -0700 Subject: drm/i915: Check for eDP inside edp panel on/off funcs Cleans up code dealing with eDP a bit. Remove redundant checks in callers Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 9c2158c..f80ff32 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -832,6 +832,8 @@ static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + if (!is_edp(intel_dp)) + return; /* * If the panel wasn't on, make sure there's not a currently * active PP sequence before enabling AUX VDD. @@ -853,6 +855,8 @@ static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + if (!is_edp(intel_dp)) + return; pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; pp |= PANEL_UNLOCK_REGS; @@ -871,6 +875,8 @@ static bool ironlake_edp_panel_on (struct intel_dp *intel_dp) struct drm_i915_private *dev_priv = dev->dev_private; u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_STATE_ON_IDLE; + if (!is_edp(intel_dp)) + return; if (I915_READ(PCH_PP_STATUS) & PP_ON) return true; @@ -905,6 +911,8 @@ static void ironlake_edp_panel_off (struct drm_device *dev) u32 pp, idle_off_mask = PP_ON | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK; + if (!is_edp(intel_dp)) + return; pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; pp |= PANEL_UNLOCK_REGS; @@ -1041,15 +1049,12 @@ static void intel_dp_commit(struct drm_encoder *encoder) struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct drm_device *dev = encoder->dev; - if (is_edp(intel_dp)) - ironlake_edp_panel_vdd_on(intel_dp); + ironlake_edp_panel_vdd_on(intel_dp); intel_dp_start_link_train(intel_dp); - if (is_edp(intel_dp)) { - ironlake_edp_panel_on(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp); - } + ironlake_edp_panel_on(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp); intel_dp_complete_link_train(intel_dp); @@ -1072,20 +1077,16 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) ironlake_edp_backlight_off(dev); intel_dp_sink_dpms(intel_dp, mode); intel_dp_link_down(intel_dp); - if (is_edp(intel_dp)) - ironlake_edp_panel_off(dev); + ironlake_edp_panel_off(dev); if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) ironlake_edp_pll_off(encoder); } else { - if (is_edp(intel_dp)) - ironlake_edp_panel_vdd_on(intel_dp); + ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, mode); if (!(dp_reg & DP_PORT_EN)) { intel_dp_start_link_train(intel_dp); - if (is_edp(intel_dp)) { - ironlake_edp_panel_on(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp); - } + ironlake_edp_panel_on(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp); intel_dp_complete_link_train(intel_dp); } if (is_edp(intel_dp)) -- cgit v0.10.2 From bee7eb2da2fb50ccf76cb7596d20e90d28de040c Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 28 Sep 2011 16:28:00 -0700 Subject: drm/i915: Turn force VDD back off when panel running in intel_dp_dpms The VDD force bit is turned on before touching the panel, but if it was enabled, there was no call to turn it back off. Add a call. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f80ff32..7e59f06 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1088,7 +1088,8 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) ironlake_edp_panel_on(intel_dp); ironlake_edp_panel_vdd_off(intel_dp); intel_dp_complete_link_train(intel_dp); - } + } else + ironlake_edp_panel_vdd_off(intel_dp); if (is_edp(intel_dp)) ironlake_edp_backlight_on(dev); } -- cgit v0.10.2 From 245e2708773796aaa13e97523e035676b008b337 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 5 Oct 2011 19:53:09 -0700 Subject: drm/i915: Ensure panel is on during DPMS off If the panel is already off, we'll need to turn VDD on to execute the (useless) DPMS off code. Yes, it would be better to just not do any of this, but correctness, and *then* performance. Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 7e59f06..68bcd77 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1073,6 +1073,7 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) uint32_t dp_reg = I915_READ(intel_dp->output_reg); if (mode != DRM_MODE_DPMS_ON) { + ironlake_edp_panel_vdd_on(intel_dp); if (is_edp(intel_dp)) ironlake_edp_backlight_off(dev); intel_dp_sink_dpms(intel_dp, mode); @@ -1080,6 +1081,7 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) ironlake_edp_panel_off(dev); if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) ironlake_edp_pll_off(encoder); + ironlake_edp_panel_vdd_off(intel_dp); } else { ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, mode); -- cgit v0.10.2 From 552fb0b7a6e8079339913512b75d8c203f54bfdf Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 28 Sep 2011 16:31:53 -0700 Subject: drm/i915: Delay DP i2c initialization until panel power timings are computed On eDP, DDC requires panel power, but turning that on uses the panel power sequencing timing values fetch from the DPCD data. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 68bcd77..0c19b39 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2079,8 +2079,6 @@ intel_dp_init(struct drm_device *dev, int output_reg) break; } - intel_dp_i2c_init(intel_dp, intel_connector, name); - /* Cache some DPCD data in the eDP case */ if (is_edp(intel_dp)) { bool ret; @@ -2112,6 +2110,8 @@ intel_dp_init(struct drm_device *dev, int output_reg) } } + intel_dp_i2c_init(intel_dp, intel_connector, name); + intel_encoder->hot_plug = intel_dp_hot_plug; if (is_edp(intel_dp)) { -- cgit v0.10.2 From 8c241fef3e6f69f3f675678ae03599ece3f562e2 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 28 Sep 2011 16:38:44 -0700 Subject: drm/i915: Wrap DP EDID fetch functions to enable eDP panel power Talking to the eDP DDC channel requires that the panel be powered up. Wrap both the EDID and modes fetch code with calls to turn the vdd power on and back off. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 0c19b39..320c379 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1721,6 +1721,31 @@ g4x_dp_detect(struct intel_dp *intel_dp) return intel_dp_detect_dpcd(intel_dp); } +static struct edid * +intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct edid *edid; + + ironlake_edp_panel_vdd_on(intel_dp); + edid = drm_get_edid(connector, adapter); + ironlake_edp_panel_vdd_off(intel_dp); + return edid; +} + +static int +intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *adapter) +{ + struct intel_dp *intel_dp = intel_attached_dp(connector); + int ret; + + ironlake_edp_panel_vdd_on(intel_dp); + ret = intel_ddc_get_modes(connector, adapter); + ironlake_edp_panel_vdd_off(intel_dp); + return ret; +} + + /** * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection. * @@ -1753,7 +1778,7 @@ intel_dp_detect(struct drm_connector *connector, bool force) if (intel_dp->force_audio) { intel_dp->has_audio = intel_dp->force_audio > 0; } else { - edid = drm_get_edid(connector, &intel_dp->adapter); + edid = intel_dp_get_edid(connector, &intel_dp->adapter); if (edid) { intel_dp->has_audio = drm_detect_monitor_audio(edid); connector->display_info.raw_edid = NULL; @@ -1774,7 +1799,7 @@ static int intel_dp_get_modes(struct drm_connector *connector) /* We should parse the EDID data and find out if it has an audio sink */ - ret = intel_ddc_get_modes(connector, &intel_dp->adapter); + ret = intel_dp_get_edid_modes(connector, &intel_dp->adapter); if (ret) { if (is_edp(intel_dp) && !dev_priv->panel_fixed_mode) { struct drm_display_mode *newmode; @@ -1819,7 +1844,7 @@ intel_dp_detect_audio(struct drm_connector *connector) struct edid *edid; bool has_audio = false; - edid = drm_get_edid(connector, &intel_dp->adapter); + edid = intel_dp_get_edid(connector, &intel_dp->adapter); if (edid) { has_audio = drm_detect_monitor_audio(edid); -- cgit v0.10.2 From 0b5c541b93792ddd7fe34a450c76377ffad7bef3 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 28 Sep 2011 16:41:05 -0700 Subject: drm/i915: Enable eDP panel power during I2C initialization sequence The DP i2c initialization code does a couple of i2c transactions, which means that an eDP panel must be powered up. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 320c379..2b4acfd 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -595,10 +595,15 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, return -EREMOTEIO; } +static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp); +static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp); + static int intel_dp_i2c_init(struct intel_dp *intel_dp, struct intel_connector *intel_connector, const char *name) { + int ret; + DRM_DEBUG_KMS("i2c_init %s\n", name); intel_dp->algo.running = false; intel_dp->algo.address = 0; @@ -612,7 +617,10 @@ intel_dp_i2c_init(struct intel_dp *intel_dp, intel_dp->adapter.algo_data = &intel_dp->algo; intel_dp->adapter.dev.parent = &intel_connector->base.kdev; - return i2c_dp_aux_add_bus(&intel_dp->adapter); + ironlake_edp_panel_vdd_on(intel_dp); + ret = i2c_dp_aux_add_bus(&intel_dp->adapter); + ironlake_edp_panel_vdd_off(intel_dp); + return ret; } static bool -- cgit v0.10.2 From f58ff8549ec0dba61aa7f2510559bce814507316 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 28 Sep 2011 16:44:14 -0700 Subject: drm/i915: Ensure eDP powered up during DP_SET_POWER operation in dp_prepare Any call to intel_dp_sink_dpms must ensure that the panel has power so that the DP_SET_POWER operation will be correctly received. The only one missing this was in intel_dp_prepare. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 2b4acfd..a178414 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1039,7 +1039,9 @@ static void intel_dp_prepare(struct drm_encoder *encoder) struct drm_device *dev = encoder->dev; /* Wake up the sink first */ + ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); + ironlake_edp_panel_vdd_off(intel_dp); if (is_edp(intel_dp)) { ironlake_edp_backlight_off(dev); -- cgit v0.10.2 From f01eca2e52169eaf3a485cbd9752435489fbfba9 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Wed, 28 Sep 2011 16:48:10 -0700 Subject: drm/i915: Correct eDP panel power sequencing delay computations Store the panel power sequencing delays in the dp private structure, rather than the global device structure. Who knows, maybe we'll get more than one eDP device in the future. From the eDP spec, we need the following numbers: T1 + T3 Power on to Aux Channel operation (panel_power_up_delay) This marks how long it takes the panel to boot up and get ready to receive aux channel communications. T8 Video signal to backlight on (backlight_on_delay) Once a valid video signal is being sent to the device, it can take a while before the panel is actuall showing useful data. This delay allows the panel to get something reasonable up before the backlight is turned on. T9 Backlight off to video off (backlight_off_delay) Turning the backlight off can take a moment, so this delay makes sure there is still valid video data on the screen. T10 Video off to power off (panel_power_down_delay) Presumably this delay allows the panel to perform an orderly shutdown of the display. T11 + T12 Power off to power on (panel_power_cycle_delay) So, once you turn the panel off, you have to wait a while before you can turn it back on. This delay is usually the longest in the entire sequence. Neither the VBIOS source code nor the hardware documentation has a clear mapping between the delay values they provide and those required by the eDP spec. The VBIOS code actually uses two different labels for the delay values in the five words of the relevant VBT table. **** MORE LATER *** Look at both the current hardware register settings and the VBT specified panel power sequencing timings. Use the maximum of the two delays, to make sure things work reliably. If there is no VBT data, then those values will be initialized to zero, so we'll just use the values as programmed in the hardware. Note that the BIOS just fetches delays from the VBT table to place in the hardware registers, so we should get the same values from both places, except for rounding. VBT doesn't provide any values for T1 or T2, so we'll always just use the hardware value for that. The panel power up delay is thus T1 + T2 + T3, which should be sufficient in all cases. The panel power down delay is T1 + T2 + T12, using T1+T2 as a proxy for T11, which isn't available anywhere. For the backlight delays, the eDP spec says T6 + T8 is the delay from the end of link training to backlight on and T9 is the delay from backlight off until video off. The hardware provides a 'backlight on' delay, which I'm taking to be T6 + T8 while the VBT provides something called 'T7', which I'm assuming is s On the macbook air I'm testing with, this yields a power-up delay of over 200ms and a power-down delay of over 600ms. It all works now, but we're frobbing these power controls several times during mode setting, making the whole process take an awfully long time. Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7916bd9..bcdf58b 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -672,7 +672,6 @@ typedef struct drm_i915_private { unsigned int lvds_border_bits; /* Panel fitter placement and size for Ironlake+ */ u32 pch_pf_pos, pch_pf_size; - int panel_t3, panel_t12; struct drm_crtc *plane_to_crtc_mapping[2]; struct drm_crtc *pipe_to_crtc_mapping[2]; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 5596e8e..793cae7 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3318,9 +3318,28 @@ #define PANEL_POWER_OFF (0 << 0) #define PANEL_POWER_ON (1 << 0) #define PCH_PP_ON_DELAYS 0xc7208 +#define PANEL_PORT_SELECT_MASK (3 << 30) +#define PANEL_PORT_SELECT_LVDS (0 << 30) +#define PANEL_PORT_SELECT_DPA (1 << 30) #define EDP_PANEL (1 << 30) +#define PANEL_PORT_SELECT_DPC (2 << 30) +#define PANEL_PORT_SELECT_DPD (3 << 30) +#define PANEL_POWER_UP_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_UP_DELAY_SHIFT 16 +#define PANEL_LIGHT_ON_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_ON_DELAY_SHIFT 0 + #define PCH_PP_OFF_DELAYS 0xc720c +#define PANEL_POWER_DOWN_DELAY_MASK (0x1fff0000) +#define PANEL_POWER_DOWN_DELAY_SHIFT 16 +#define PANEL_LIGHT_OFF_DELAY_MASK (0x1fff) +#define PANEL_LIGHT_OFF_DELAY_SHIFT 0 + #define PCH_PP_DIVISOR 0xc7210 +#define PP_REFERENCE_DIVIDER_MASK (0xffffff00) +#define PP_REFERENCE_DIVIDER_SHIFT 8 +#define PANEL_POWER_CYCLE_DELAY_MASK (0x1f) +#define PANEL_POWER_CYCLE_DELAY_SHIFT 0 #define PCH_DP_B 0xe4100 #define PCH_DPB_AUX_CH_CTL 0xe4110 diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index 5f8e4ed..35d2a50 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -1,5 +1,5 @@ /* - * Copyright © 2006 Intel Corporation + * Copyright © 2006 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -446,11 +446,11 @@ struct bdb_driver_features { #define EDP_VSWING_1_2V 3 struct edp_power_seq { - u16 t3; - u16 t7; + u16 t1_t3; + u16 t8; u16 t9; u16 t10; - u16 t12; + u16 t11_t12; } __attribute__ ((packed)); struct edp_link_params { diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index a178414..ad682a5 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -59,6 +59,11 @@ struct intel_dp { bool is_pch_edp; uint8_t train_set[4]; uint8_t link_status[DP_LINK_STATUS_SIZE]; + int panel_power_up_delay; + int panel_power_down_delay; + int panel_power_cycle_delay; + int backlight_on_delay; + int backlight_off_delay; }; /** @@ -770,6 +775,9 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, } } +static void ironlake_edp_pll_on(struct drm_encoder *encoder); +static void ironlake_edp_pll_off(struct drm_encoder *encoder); + static void intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -779,6 +787,14 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_crtc *crtc = intel_dp->base.base.crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + /* Turn on the eDP PLL if needed */ + if (is_edp(intel_dp)) { + if (!is_pch_edp(intel_dp)) + ironlake_edp_pll_on(encoder); + else + ironlake_edp_pll_off(encoder); + } + intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0; intel_dp->DP |= intel_dp->color_range; @@ -838,16 +854,16 @@ static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp; + u32 pp, pp_status; if (!is_edp(intel_dp)) return; + DRM_DEBUG_KMS("Turn eDP VDD on\n"); /* * If the panel wasn't on, make sure there's not a currently * active PP sequence before enabling AUX VDD. */ - if (!(I915_READ(PCH_PP_STATUS) & PP_ON)) - msleep(dev_priv->panel_t3); + pp_status = I915_READ(PCH_PP_STATUS); pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; @@ -855,6 +871,12 @@ static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) pp |= EDP_FORCE_VDD; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); + DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", + I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); + if (!(pp_status & PP_ON)) { + msleep(intel_dp->panel_power_up_delay); + DRM_DEBUG_KMS("eDP VDD was not on\n"); + } } static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp) @@ -865,6 +887,7 @@ static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp) if (!is_edp(intel_dp)) return; + DRM_DEBUG_KMS("Turn eDP VDD off\n"); pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; pp |= PANEL_UNLOCK_REGS; @@ -873,7 +896,9 @@ static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp) POSTING_READ(PCH_PP_CONTROL); /* Make sure sequencer is idle before allowing subsequent activity */ - msleep(dev_priv->panel_t12); + DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", + I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); + msleep(intel_dp->panel_power_cycle_delay); } /* Returns true if the panel was already on when called */ @@ -884,7 +909,7 @@ static bool ironlake_edp_panel_on (struct intel_dp *intel_dp) u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_STATE_ON_IDLE; if (!is_edp(intel_dp)) - return; + return true; if (I915_READ(PCH_PP_STATUS) & PP_ON) return true; @@ -913,8 +938,10 @@ static bool ironlake_edp_panel_on (struct intel_dp *intel_dp) return false; } -static void ironlake_edp_panel_off (struct drm_device *dev) +static void ironlake_edp_panel_off(struct drm_encoder *encoder) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp, idle_off_mask = PP_ON | PP_SEQUENCE_MASK | PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK; @@ -933,6 +960,7 @@ static void ironlake_edp_panel_off (struct drm_device *dev) pp &= ~POWER_TARGET_ON; I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); + msleep(intel_dp->panel_power_cycle_delay); if (wait_for((I915_READ(PCH_PP_STATUS) & idle_off_mask) == 0, 5000)) DRM_ERROR("panel off wait timed out: 0x%08x\n", @@ -943,11 +971,15 @@ static void ironlake_edp_panel_off (struct drm_device *dev) POSTING_READ(PCH_PP_CONTROL); } -static void ironlake_edp_backlight_on (struct drm_device *dev) +static void ironlake_edp_backlight_on (struct intel_dp *intel_dp) { + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + if (!is_edp(intel_dp)) + return; + DRM_DEBUG_KMS("\n"); /* * If we enable the backlight right away following a panel power @@ -955,25 +987,32 @@ static void ironlake_edp_backlight_on (struct drm_device *dev) * link. So delay a bit to make sure the image is solid before * allowing it to appear. */ - msleep(300); + msleep(intel_dp->backlight_on_delay); pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; pp |= PANEL_UNLOCK_REGS; pp |= EDP_BLC_ENABLE; I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); } -static void ironlake_edp_backlight_off (struct drm_device *dev) +static void ironlake_edp_backlight_off (struct intel_dp *intel_dp) { + struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + if (!is_edp(intel_dp)) + return; + DRM_DEBUG_KMS("\n"); pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; pp |= PANEL_UNLOCK_REGS; pp &= ~EDP_BLC_ENABLE; I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + msleep(intel_dp->backlight_off_delay); } static void ironlake_edp_pll_on(struct drm_encoder *encoder) @@ -1036,40 +1075,31 @@ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode) static void intel_dp_prepare(struct drm_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_device *dev = encoder->dev; /* Wake up the sink first */ ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); ironlake_edp_panel_vdd_off(intel_dp); - if (is_edp(intel_dp)) { - ironlake_edp_backlight_off(dev); - ironlake_edp_panel_off(dev); - if (!is_pch_edp(intel_dp)) - ironlake_edp_pll_on(encoder); - else - ironlake_edp_pll_off(encoder); - } + /* Make sure the panel is off before trying to + * change the mode + */ + ironlake_edp_backlight_off(intel_dp); intel_dp_link_down(intel_dp); + ironlake_edp_panel_off(encoder); } static void intel_dp_commit(struct drm_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_device *dev = encoder->dev; ironlake_edp_panel_vdd_on(intel_dp); - + intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); - ironlake_edp_panel_on(intel_dp); ironlake_edp_panel_vdd_off(intel_dp); - intel_dp_complete_link_train(intel_dp); - - if (is_edp(intel_dp)) - ironlake_edp_backlight_on(dev); + ironlake_edp_backlight_on(intel_dp); intel_dp->dpms_mode = DRM_MODE_DPMS_ON; } @@ -1085,10 +1115,10 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) if (mode != DRM_MODE_DPMS_ON) { ironlake_edp_panel_vdd_on(intel_dp); if (is_edp(intel_dp)) - ironlake_edp_backlight_off(dev); + ironlake_edp_backlight_off(intel_dp); intel_dp_sink_dpms(intel_dp, mode); intel_dp_link_down(intel_dp); - ironlake_edp_panel_off(dev); + ironlake_edp_panel_off(encoder); if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) ironlake_edp_pll_off(encoder); ironlake_edp_panel_vdd_off(intel_dp); @@ -1100,10 +1130,9 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) ironlake_edp_panel_on(intel_dp); ironlake_edp_panel_vdd_off(intel_dp); intel_dp_complete_link_train(intel_dp); + ironlake_edp_backlight_on(intel_dp); } else ironlake_edp_panel_vdd_off(intel_dp); - if (is_edp(intel_dp)) - ironlake_edp_backlight_on(dev); } intel_dp->dpms_mode = mode; } @@ -1626,6 +1655,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN); POSTING_READ(intel_dp->output_reg); + msleep(intel_dp->panel_power_down_delay); } static bool @@ -2117,16 +2147,51 @@ intel_dp_init(struct drm_device *dev, int output_reg) /* Cache some DPCD data in the eDP case */ if (is_edp(intel_dp)) { bool ret; - u32 pp_on, pp_div; + struct edp_power_seq cur, vbt; + u32 pp_on, pp_off, pp_div; pp_on = I915_READ(PCH_PP_ON_DELAYS); + pp_off = I915_READ(PCH_PP_OFF_DELAYS); pp_div = I915_READ(PCH_PP_DIVISOR); - /* Get T3 & T12 values (note: VESA not bspec terminology) */ - dev_priv->panel_t3 = (pp_on & 0x1fff0000) >> 16; - dev_priv->panel_t3 /= 10; /* t3 in 100us units */ - dev_priv->panel_t12 = pp_div & 0xf; - dev_priv->panel_t12 *= 100; /* t12 in 100ms units */ + /* Pull timing values out of registers */ + cur.t1_t3 = (pp_on & PANEL_POWER_UP_DELAY_MASK) >> + PANEL_POWER_UP_DELAY_SHIFT; + + cur.t8 = (pp_on & PANEL_LIGHT_ON_DELAY_MASK) >> + PANEL_LIGHT_ON_DELAY_SHIFT; + + cur.t9 = (pp_off & PANEL_LIGHT_OFF_DELAY_MASK) >> + PANEL_LIGHT_OFF_DELAY_SHIFT; + + cur.t10 = (pp_off & PANEL_POWER_DOWN_DELAY_MASK) >> + PANEL_POWER_DOWN_DELAY_SHIFT; + + cur.t11_t12 = ((pp_div & PANEL_POWER_CYCLE_DELAY_MASK) >> + PANEL_POWER_CYCLE_DELAY_SHIFT) * 1000; + + DRM_DEBUG_KMS("cur t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + cur.t1_t3, cur.t8, cur.t9, cur.t10, cur.t11_t12); + + vbt = dev_priv->edp.pps; + + DRM_DEBUG_KMS("vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n", + vbt.t1_t3, vbt.t8, vbt.t9, vbt.t10, vbt.t11_t12); + +#define get_delay(field) ((max(cur.field, vbt.field) + 9) / 10) + + intel_dp->panel_power_up_delay = get_delay(t1_t3); + intel_dp->backlight_on_delay = get_delay(t8); + intel_dp->backlight_off_delay = get_delay(t9); + intel_dp->panel_power_down_delay = get_delay(t10); + intel_dp->panel_power_cycle_delay = get_delay(t11_t12); + + DRM_DEBUG_KMS("panel power up delay %d, power down delay %d, power cycle delay %d\n", + intel_dp->panel_power_up_delay, intel_dp->panel_power_down_delay, + intel_dp->panel_power_cycle_delay); + + DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", + intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); ironlake_edp_panel_vdd_on(intel_dp); ret = intel_dp_get_dpcd(intel_dp); -- cgit v0.10.2 From d15456de79eea2aa03cd277866db80556e984d49 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 18 Sep 2011 17:35:47 -0700 Subject: drm/i915: Move eDP panel fixed mode from dev_priv to intel_dp This value doesn't come directly from the VBT, and so is rather specific to the particular DP output. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index bcdf58b..e6dd19e 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -347,7 +347,6 @@ typedef struct drm_i915_private { /* LVDS info */ int backlight_level; /* restore backlight to this value */ bool backlight_enabled; - struct drm_display_mode *panel_fixed_mode; struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ad682a5..f252ed2 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -64,6 +64,7 @@ struct intel_dp { int panel_power_cycle_delay; int backlight_on_delay; int backlight_off_delay; + struct drm_display_mode *panel_fixed_mode; /* for eDP */ }; /** @@ -205,16 +206,14 @@ intel_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct intel_dp *intel_dp = intel_attached_dp(connector); - struct drm_device *dev = connector->dev; - struct drm_i915_private *dev_priv = dev->dev_private; int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp)); int max_lanes = intel_dp_max_lane_count(intel_dp); - if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) { - if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay) + if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { + if (mode->hdisplay > intel_dp->panel_fixed_mode->hdisplay) return MODE_PANEL; - if (mode->vdisplay > dev_priv->panel_fixed_mode->vdisplay) + if (mode->vdisplay > intel_dp->panel_fixed_mode->vdisplay) return MODE_PANEL; } @@ -633,22 +632,21 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; - struct drm_i915_private *dev_priv = dev->dev_private; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); int lane_count, clock; int max_lane_count = intel_dp_max_lane_count(intel_dp); int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0; static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 }; - if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) { - intel_fixed_panel_mode(dev_priv->panel_fixed_mode, adjusted_mode); + if (is_edp(intel_dp) && intel_dp->panel_fixed_mode) { + intel_fixed_panel_mode(intel_dp->panel_fixed_mode, adjusted_mode); intel_pch_panel_fitting(dev, DRM_MODE_SCALE_FULLSCREEN, mode, adjusted_mode); /* * the mode->clock is used to calculate the Data&Link M/N * of the pipe. For the eDP the fixed clock should be used. */ - mode->clock = dev_priv->panel_fixed_mode->clock; + mode->clock = intel_dp->panel_fixed_mode->clock; } for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) { @@ -1841,35 +1839,34 @@ static int intel_dp_get_modes(struct drm_connector *connector) ret = intel_dp_get_edid_modes(connector, &intel_dp->adapter); if (ret) { - if (is_edp(intel_dp) && !dev_priv->panel_fixed_mode) { + if (is_edp(intel_dp) && !intel_dp->panel_fixed_mode) { struct drm_display_mode *newmode; list_for_each_entry(newmode, &connector->probed_modes, head) { - if (newmode->type & DRM_MODE_TYPE_PREFERRED) { - dev_priv->panel_fixed_mode = + if ((newmode->type & DRM_MODE_TYPE_PREFERRED)) { + intel_dp->panel_fixed_mode = drm_mode_duplicate(dev, newmode); break; } } } - return ret; } /* if eDP has no EDID, try to use fixed panel mode from VBT */ if (is_edp(intel_dp)) { /* initialize panel mode from VBT if available for eDP */ - if (dev_priv->panel_fixed_mode == NULL && dev_priv->lfp_lvds_vbt_mode != NULL) { - dev_priv->panel_fixed_mode = + if (intel_dp->panel_fixed_mode == NULL && dev_priv->lfp_lvds_vbt_mode != NULL) { + intel_dp->panel_fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); - if (dev_priv->panel_fixed_mode) { - dev_priv->panel_fixed_mode->type |= + if (intel_dp->panel_fixed_mode) { + intel_dp->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; } } - if (dev_priv->panel_fixed_mode) { + if (intel_dp->panel_fixed_mode) { struct drm_display_mode *mode; - mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode); + mode = drm_mode_duplicate(dev, intel_dp->panel_fixed_mode); drm_mode_probed_add(connector, mode); return 1; } -- cgit v0.10.2 From 7d639f35b7f6b218f7b58918fb6b1f028f869894 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 29 Sep 2011 16:05:34 -0700 Subject: drm/i915: edp_panel_on does not need to return a bool The return value was unused, so just stop doing that. Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f252ed2..fee6050 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -900,7 +900,7 @@ static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp) } /* Returns true if the panel was already on when called */ -static bool ironlake_edp_panel_on (struct intel_dp *intel_dp) +static void ironlake_edp_panel_on (struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; @@ -909,7 +909,7 @@ static bool ironlake_edp_panel_on (struct intel_dp *intel_dp) if (!is_edp(intel_dp)) return true; if (I915_READ(PCH_PP_STATUS) & PP_ON) - return true; + return; pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; @@ -932,8 +932,6 @@ static bool ironlake_edp_panel_on (struct intel_dp *intel_dp) pp |= PANEL_POWER_RESET; /* restore panel reset bit */ I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); - - return false; } static void ironlake_edp_panel_off(struct drm_encoder *encoder) -- cgit v0.10.2 From ebf33b18816d9755087474cda7761e5944dd56c1 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 29 Sep 2011 15:53:27 -0700 Subject: drm/i915: Create helper functions to determine eDP power state We need to check eDP VDD force and panel on in several places, so create some simple helper functions to avoid duplicating code. Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index fee6050..ecb9764 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -283,20 +283,34 @@ intel_hrawclk(struct drm_device *dev) } } +static bool ironlake_edp_have_panel_power(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + return (I915_READ(PCH_PP_STATUS) & PP_ON) != 0; +} + +static bool ironlake_edp_have_panel_vdd(struct intel_dp *intel_dp) +{ + struct drm_device *dev = intel_dp->base.base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + + return (I915_READ(PCH_PP_CONTROL) & EDP_FORCE_VDD) != 0; +} + static void intel_dp_check_edp(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp_status, pp_control; + if (!is_edp(intel_dp)) return; - pp_status = I915_READ(PCH_PP_STATUS); - pp_control = I915_READ(PCH_PP_CONTROL); - if ((pp_status & PP_ON) == 0 && (pp_control & EDP_FORCE_VDD) == 0) { + if (!ironlake_edp_have_panel_power(intel_dp) && !ironlake_edp_have_panel_vdd(intel_dp)) { WARN(1, "eDP powered off while attempting aux channel communication.\n"); DRM_DEBUG_KMS("Status 0x%08x Control 0x%08x\n", - pp_status, + I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); } } @@ -852,16 +866,11 @@ static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; - u32 pp, pp_status; + u32 pp; if (!is_edp(intel_dp)) return; DRM_DEBUG_KMS("Turn eDP VDD on\n"); - /* - * If the panel wasn't on, make sure there's not a currently - * active PP sequence before enabling AUX VDD. - */ - pp_status = I915_READ(PCH_PP_STATUS); pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; @@ -871,7 +880,11 @@ static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) POSTING_READ(PCH_PP_CONTROL); DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); - if (!(pp_status & PP_ON)) { + + /* + * If the panel wasn't on, delay before accessing aux channel + */ + if (!ironlake_edp_have_panel_power(intel_dp)) { msleep(intel_dp->panel_power_up_delay); DRM_DEBUG_KMS("eDP VDD was not on\n"); } @@ -908,7 +921,7 @@ static void ironlake_edp_panel_on (struct intel_dp *intel_dp) if (!is_edp(intel_dp)) return true; - if (I915_READ(PCH_PP_STATUS) & PP_ON) + if (ironlake_edp_have_panel_power(intel_dp)) return; pp = I915_READ(PCH_PP_CONTROL); -- cgit v0.10.2 From bd9431597153925b000e810ceadf599b5aa6ad90 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Sun, 18 Sep 2011 23:09:52 -0700 Subject: drm/i915: Disable eDP VDD in a delayed work proc instead of synchronously There's no good reason to turn off the eDP force VDD bit synchronously while probing devices; that just sticks a huge delay into all mode setting paths. Instead, queue a delayed work proc to disable the VDD force bit and then remember when that fires to ensure that the appropriate delay is respected before trying to turn it back on. Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ecb9764..e880a04 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -65,6 +65,9 @@ struct intel_dp { int backlight_on_delay; int backlight_off_delay; struct drm_display_mode *panel_fixed_mode; /* for eDP */ + struct delayed_work panel_vdd_work; + bool want_panel_vdd; + unsigned long panel_off_jiffies; }; /** @@ -614,7 +617,7 @@ intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, } static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp); -static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp); +static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync); static int intel_dp_i2c_init(struct intel_dp *intel_dp, @@ -637,7 +640,7 @@ intel_dp_i2c_init(struct intel_dp *intel_dp, ironlake_edp_panel_vdd_on(intel_dp); ret = i2c_dp_aux_add_bus(&intel_dp->adapter); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); return ret; } @@ -862,6 +865,23 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, } } +static void ironlake_wait_panel_off(struct intel_dp *intel_dp) +{ + unsigned long off_time; + unsigned long delay; + DRM_DEBUG_KMS("Wait for panel power off time\n"); + off_time = intel_dp->panel_off_jiffies + msecs_to_jiffies(intel_dp->panel_power_down_delay); + if (time_after(jiffies, off_time)) { + DRM_DEBUG_KMS("Time already passed"); + return; + } + delay = jiffies_to_msecs(off_time - jiffies); + if (delay > intel_dp->panel_power_down_delay) + delay = intel_dp->panel_power_down_delay; + DRM_DEBUG_KMS("Waiting an additional %ld ms\n", delay); + msleep(delay); +} + static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; @@ -872,6 +892,16 @@ static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) return; DRM_DEBUG_KMS("Turn eDP VDD on\n"); + WARN(intel_dp->want_panel_vdd, + "eDP VDD already requested on\n"); + + intel_dp->want_panel_vdd = true; + if (ironlake_edp_have_panel_vdd(intel_dp)) { + DRM_DEBUG_KMS("eDP VDD already on\n"); + return; + } + + ironlake_wait_panel_off(intel_dp); pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; pp |= PANEL_UNLOCK_REGS; @@ -885,31 +915,64 @@ static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp) * If the panel wasn't on, delay before accessing aux channel */ if (!ironlake_edp_have_panel_power(intel_dp)) { + DRM_DEBUG_KMS("eDP was not running\n"); msleep(intel_dp->panel_power_up_delay); - DRM_DEBUG_KMS("eDP VDD was not on\n"); } } -static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp) +static void ironlake_panel_vdd_off_sync(struct intel_dp *intel_dp) { struct drm_device *dev = intel_dp->base.base.dev; struct drm_i915_private *dev_priv = dev->dev_private; u32 pp; + if (!intel_dp->want_panel_vdd && ironlake_edp_have_panel_vdd(intel_dp)) { + pp = I915_READ(PCH_PP_CONTROL); + pp &= ~PANEL_UNLOCK_MASK; + pp |= PANEL_UNLOCK_REGS; + pp &= ~EDP_FORCE_VDD; + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + + /* Make sure sequencer is idle before allowing subsequent activity */ + DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", + I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); + intel_dp->panel_off_jiffies = jiffies; + } +} + +static void ironlake_panel_vdd_work(struct work_struct *__work) +{ + struct intel_dp *intel_dp = container_of(to_delayed_work(__work), + struct intel_dp, panel_vdd_work); + struct drm_device *dev = intel_dp->base.base.dev; + + mutex_lock(&dev->struct_mutex); + ironlake_panel_vdd_off_sync(intel_dp); + mutex_unlock(&dev->struct_mutex); +} + +static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync) +{ if (!is_edp(intel_dp)) return; - DRM_DEBUG_KMS("Turn eDP VDD off\n"); - pp = I915_READ(PCH_PP_CONTROL); - pp &= ~PANEL_UNLOCK_MASK; - pp |= PANEL_UNLOCK_REGS; - pp &= ~EDP_FORCE_VDD; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); - /* Make sure sequencer is idle before allowing subsequent activity */ - DRM_DEBUG_KMS("PCH_PP_STATUS: 0x%08x PCH_PP_CONTROL: 0x%08x\n", - I915_READ(PCH_PP_STATUS), I915_READ(PCH_PP_CONTROL)); - msleep(intel_dp->panel_power_cycle_delay); + DRM_DEBUG_KMS("Turn eDP VDD off %d\n", intel_dp->want_panel_vdd); + WARN(!intel_dp->want_panel_vdd, "eDP VDD not forced on"); + + intel_dp->want_panel_vdd = false; + + if (sync) { + ironlake_panel_vdd_off_sync(intel_dp); + } else { + /* + * Queue the timer to fire a long + * time from now (relative to the power down delay) + * to keep the panel power up across a sequence of operations + */ + schedule_delayed_work(&intel_dp->panel_vdd_work, + msecs_to_jiffies(intel_dp->panel_power_cycle_delay * 5)); + } } /* Returns true if the panel was already on when called */ @@ -920,10 +983,11 @@ static void ironlake_edp_panel_on (struct intel_dp *intel_dp) u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_STATE_ON_IDLE; if (!is_edp(intel_dp)) - return true; + return; if (ironlake_edp_have_panel_power(intel_dp)) return; + ironlake_wait_panel_off(intel_dp); pp = I915_READ(PCH_PP_CONTROL); pp &= ~PANEL_UNLOCK_MASK; pp |= PANEL_UNLOCK_REGS; @@ -978,6 +1042,7 @@ static void ironlake_edp_panel_off(struct drm_encoder *encoder) pp |= PANEL_POWER_RESET; /* restore panel reset bit */ I915_WRITE(PCH_PP_CONTROL, pp); POSTING_READ(PCH_PP_CONTROL); + intel_dp->panel_off_jiffies = jiffies; } static void ironlake_edp_backlight_on (struct intel_dp *intel_dp) @@ -1088,7 +1153,7 @@ static void intel_dp_prepare(struct drm_encoder *encoder) /* Wake up the sink first */ ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); /* Make sure the panel is off before trying to * change the mode @@ -1106,7 +1171,8 @@ static void intel_dp_commit(struct drm_encoder *encoder) intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); intel_dp_start_link_train(intel_dp); ironlake_edp_panel_on(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, true); + intel_dp_complete_link_train(intel_dp); ironlake_edp_backlight_on(intel_dp); @@ -1130,18 +1196,19 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode) ironlake_edp_panel_off(encoder); if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) ironlake_edp_pll_off(encoder); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); } else { ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, mode); if (!(dp_reg & DP_PORT_EN)) { intel_dp_start_link_train(intel_dp); ironlake_edp_panel_on(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, true); intel_dp_complete_link_train(intel_dp); ironlake_edp_backlight_on(intel_dp); } else - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); + ironlake_edp_backlight_on(intel_dp); } intel_dp->dpms_mode = mode; } @@ -1778,7 +1845,7 @@ intel_dp_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter) ironlake_edp_panel_vdd_on(intel_dp); edid = drm_get_edid(connector, adapter); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); return edid; } @@ -1790,7 +1857,7 @@ intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *ada ironlake_edp_panel_vdd_on(intel_dp); ret = intel_ddc_get_modes(connector, adapter); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); return ret; } @@ -1977,6 +2044,10 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder) i2c_del_adapter(&intel_dp->adapter); drm_encoder_cleanup(encoder); + if (is_edp(intel_dp)) { + cancel_delayed_work_sync(&intel_dp->panel_vdd_work); + ironlake_panel_vdd_off_sync(intel_dp); + } kfree(intel_dp); } @@ -2113,8 +2184,11 @@ intel_dp_init(struct drm_device *dev, int output_reg) else if (output_reg == DP_D || output_reg == PCH_DP_D) intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT); - if (is_edp(intel_dp)) + if (is_edp(intel_dp)) { intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT); + INIT_DELAYED_WORK(&intel_dp->panel_vdd_work, + ironlake_panel_vdd_work); + } intel_encoder->crtc_mask = (1 << 0) | (1 << 1); connector->interlace_allowed = true; @@ -2201,9 +2275,11 @@ intel_dp_init(struct drm_device *dev, int output_reg) DRM_DEBUG_KMS("backlight on delay %d, off delay %d\n", intel_dp->backlight_on_delay, intel_dp->backlight_off_delay); + intel_dp->panel_off_jiffies = jiffies - intel_dp->panel_power_down_delay; + ironlake_edp_panel_vdd_on(intel_dp); ret = intel_dp_get_dpcd(intel_dp); - ironlake_edp_panel_vdd_off(intel_dp); + ironlake_edp_panel_vdd_off(intel_dp, false); if (ret) { if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) dev_priv->no_aux_handshake = -- cgit v0.10.2 From 05ce1a4961cffd7b0c8d4b70a7c9fa341368bc48 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 29 Sep 2011 16:33:01 -0700 Subject: drm/i915: Restrict ILK-specific eDP power hack to ILK This eliminates a fairly long delay when power sequencing newer hardware Signed-off-by: Keith Packard Reviewed-by: Daniel Vetter diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index e880a04..f5303e1 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -992,10 +992,12 @@ static void ironlake_edp_panel_on (struct intel_dp *intel_dp) pp &= ~PANEL_UNLOCK_MASK; pp |= PANEL_UNLOCK_REGS; - /* ILK workaround: disable reset around power sequence */ - pp &= ~PANEL_POWER_RESET; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + if (IS_GEN5(dev)) { + /* ILK workaround: disable reset around power sequence */ + pp &= ~PANEL_POWER_RESET; + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + } pp |= POWER_TARGET_ON; I915_WRITE(PCH_PP_CONTROL, pp); @@ -1006,9 +1008,11 @@ static void ironlake_edp_panel_on (struct intel_dp *intel_dp) DRM_ERROR("panel on wait timed out: 0x%08x\n", I915_READ(PCH_PP_STATUS)); - pp |= PANEL_POWER_RESET; /* restore panel reset bit */ - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + if (IS_GEN5(dev)) { + pp |= PANEL_POWER_RESET; /* restore panel reset bit */ + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + } } static void ironlake_edp_panel_off(struct drm_encoder *encoder) @@ -1025,24 +1029,32 @@ static void ironlake_edp_panel_off(struct drm_encoder *encoder) pp &= ~PANEL_UNLOCK_MASK; pp |= PANEL_UNLOCK_REGS; - /* ILK workaround: disable reset around power sequence */ - pp &= ~PANEL_POWER_RESET; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); + if (IS_GEN5(dev)) { + /* ILK workaround: disable reset around power sequence */ + pp &= ~PANEL_POWER_RESET; + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + } - pp &= ~POWER_TARGET_ON; - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); - msleep(intel_dp->panel_power_cycle_delay); + intel_dp->panel_off_jiffies = jiffies; - if (wait_for((I915_READ(PCH_PP_STATUS) & idle_off_mask) == 0, 5000)) - DRM_ERROR("panel off wait timed out: 0x%08x\n", - I915_READ(PCH_PP_STATUS)); + if (IS_GEN5(dev)) { + pp &= ~POWER_TARGET_ON; + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + pp &= ~POWER_TARGET_ON; + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + msleep(intel_dp->panel_power_cycle_delay); - pp |= PANEL_POWER_RESET; /* restore panel reset bit */ - I915_WRITE(PCH_PP_CONTROL, pp); - POSTING_READ(PCH_PP_CONTROL); - intel_dp->panel_off_jiffies = jiffies; + if (wait_for((I915_READ(PCH_PP_STATUS) & idle_off_mask) == 0, 5000)) + DRM_ERROR("panel off wait timed out: 0x%08x\n", + I915_READ(PCH_PP_STATUS)); + + pp |= PANEL_POWER_RESET; /* restore panel reset bit */ + I915_WRITE(PCH_PP_CONTROL, pp); + POSTING_READ(PCH_PP_CONTROL); + } } static void ironlake_edp_backlight_on (struct intel_dp *intel_dp) -- cgit v0.10.2 From 32ce697c53f41290c3a2d3807b521b0fe4f42d2a Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 29 Sep 2011 16:51:26 -0700 Subject: drm/i915: No need to wait for eDP power off delay if panel is on If the panel is powered up, there's no need to delay for the 'off' interval when turning the panel on. Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index f5303e1..6db2a2d 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -869,7 +869,16 @@ static void ironlake_wait_panel_off(struct intel_dp *intel_dp) { unsigned long off_time; unsigned long delay; + DRM_DEBUG_KMS("Wait for panel power off time\n"); + + if (ironlake_edp_have_panel_power(intel_dp) || + ironlake_edp_have_panel_vdd(intel_dp)) + { + DRM_DEBUG_KMS("Panel still on, no delay needed\n"); + return; + } + off_time = intel_dp->panel_off_jiffies + msecs_to_jiffies(intel_dp->panel_power_down_delay); if (time_after(jiffies, off_time)) { DRM_DEBUG_KMS("Time already passed"); -- cgit v0.10.2 From 46eb303682f72717d1a75cdd2309733bfed43396 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 16 Jun 2011 16:36:23 -0400 Subject: drm/i915: Remove "i2c_speed" nonsense from child device table I have no evidence for this byte being used this way, and lots of counterexamples. Restore the struct to its empirical definition and patch up gmbus setup to match. Signed-off-by: Adam Jackson Acked-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 0e777fd..c02cdac 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -139,7 +139,6 @@ struct sdvo_device_mapping { u8 slave_addr; u8 dvo_wiring; u8 i2c_pin; - u8 i2c_speed; u8 ddc_pin; }; diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index dbda66f..63880e2 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -403,15 +403,13 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv, p_mapping->dvo_wiring = p_child->dvo_wiring; p_mapping->ddc_pin = p_child->ddc_pin; p_mapping->i2c_pin = p_child->i2c_pin; - p_mapping->i2c_speed = p_child->i2c_speed; p_mapping->initialized = 1; - DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d, i2c_speed=%d\n", + DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d\n", p_mapping->dvo_port, p_mapping->slave_addr, p_mapping->dvo_wiring, p_mapping->ddc_pin, - p_mapping->i2c_pin, - p_mapping->i2c_speed); + p_mapping->i2c_pin); } else { DRM_DEBUG_KMS("Maybe one SDVO port is shared by " "two SDVO device.\n"); diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h index a8cad2b..8af3735 100644 --- a/drivers/gpu/drm/i915/intel_bios.h +++ b/drivers/gpu/drm/i915/intel_bios.h @@ -202,8 +202,7 @@ struct bdb_general_features { struct child_device_config { u16 handle; u16 device_type; - u8 i2c_speed; - u8 rsvd[9]; + u8 device_id[10]; /* ascii string */ u16 addin_offset; u8 dvo_port; /* See Device_PORT_* above */ u8 i2c_pin; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 7312002..ed15c00 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1896,7 +1896,7 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv, struct intel_sdvo *sdvo, u32 reg) { struct sdvo_device_mapping *mapping; - u8 pin, speed; + u8 pin; if (IS_SDVOB(reg)) mapping = &dev_priv->sdvo_mappings[0]; @@ -1904,18 +1904,16 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv, mapping = &dev_priv->sdvo_mappings[1]; pin = GMBUS_PORT_DPB; - speed = GMBUS_RATE_1MHZ >> 8; - if (mapping->initialized) { + if (mapping->initialized) pin = mapping->i2c_pin; - speed = mapping->i2c_speed; - } if (pin < GMBUS_NUM_PORTS) { sdvo->i2c = &dev_priv->gmbus[pin].adapter; - intel_gmbus_set_speed(sdvo->i2c, speed); + intel_gmbus_set_speed(sdvo->i2c, GMBUS_RATE_1MHZ >> 8); intel_gmbus_force_bit(sdvo->i2c, true); - } else + } else { sdvo->i2c = &dev_priv->gmbus[GMBUS_PORT_DPB].adapter; + } } static bool -- cgit v0.10.2 From 2294488da3402220120ff10360653a2ae3c15024 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 16 Jun 2011 16:36:24 -0400 Subject: drm/i915: Fix multifunction SDVO detection I can't think of any sensible reason to limit this to a mask of 0x0f, ie, SDVO_OUTPUT_{TMDS,RGB,CVBS,SVID}0. Signed-off-by: Adam Jackson Reviewed-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index ed15c00..6cbbe5c 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1232,8 +1232,7 @@ static bool intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo) { /* Is there more than one type of output? */ - int caps = intel_sdvo->caps.output_flags & 0xf; - return caps & -caps; + return hweight16(intel_sdvo->caps.output_flags) > 1; } static struct edid * -- cgit v0.10.2 From 8bf38485def0078c6429814760463f871756c73b Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 16 Jun 2011 16:36:25 -0400 Subject: drm/i915: Rename intel_sdvo_hdmi_sink_detect This is general TMDS detect, not HDMI specifically. Signed-off-by: Adam Jackson Reviewed-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 6cbbe5c..643b5d7 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1253,7 +1253,7 @@ intel_sdvo_get_analog_edid(struct drm_connector *connector) } enum drm_connector_status -intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) +intel_sdvo_tmds_sink_detect(struct drm_connector *connector) { struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); enum drm_connector_status status; @@ -1348,7 +1348,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) if ((intel_sdvo_connector->output_flag & response) == 0) ret = connector_status_disconnected; else if (IS_TMDS(intel_sdvo_connector)) - ret = intel_sdvo_hdmi_sink_detect(connector); + ret = intel_sdvo_tmds_sink_detect(connector); else { struct edid *edid; -- cgit v0.10.2 From d5090b96256b9bc479514d54cb55dcaba3144a8d Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Thu, 16 Jun 2011 16:36:28 -0400 Subject: drm/i915: Remove redundant bit shifting from intel_gmbus_set_speed Signed-off-by: Adam Jackson Reviewed-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index d98cee6..9ed5380 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -422,13 +422,7 @@ void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed) { struct intel_gmbus *bus = to_intel_gmbus(adapter); - /* speed: - * 0x0 = 100 KHz - * 0x1 = 50 KHz - * 0x2 = 400 KHz - * 0x3 = 1000 Khz - */ - bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | (speed << 8); + bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | speed; } void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit) diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 643b5d7..1b97d1f 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -1908,7 +1908,7 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv, if (pin < GMBUS_NUM_PORTS) { sdvo->i2c = &dev_priv->gmbus[pin].adapter; - intel_gmbus_set_speed(sdvo->i2c, GMBUS_RATE_1MHZ >> 8); + intel_gmbus_set_speed(sdvo->i2c, GMBUS_RATE_1MHZ); intel_gmbus_force_bit(sdvo->i2c, true); } else { sdvo->i2c = &dev_priv->gmbus[GMBUS_PORT_DPB].adapter; -- cgit v0.10.2 From 4fb066ab9ef3111c86d9fb8f13f1178885cf7f1c Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Sep 2011 14:00:20 +0200 Subject: drm/i915: close PM interrupt masking races in the irq handler Quoting Chris Wilson's more concise description: "Ah I think I see the problem. As you point out we only mask the current interrupt received, so that if we have a task pending (and so IMR != 0) we actually unmask the pending interrupt and so could receive it again before the tasklet is finally kicked off by the grumpy scheduler." We need the hw to issue PM interrupts A, B, A while the scheduler is hating us and refuses to run the rps work item. On receiving PM interrupt A we hit the WARN because dev_priv->pm_iir == PM_A | PM_B Also add a posting read as suggested by Chris to ensure proper ordering of the writes to PMIMR and PMIIR. Just in case somebody weakens write ordering. Signed-off-by: Daniel Vetter Reviewed-by: Ben Widawsky Reviewed-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 944d712..98eeddd 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -536,8 +536,9 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS) unsigned long flags; spin_lock_irqsave(&dev_priv->rps_lock, flags); WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); - I915_WRITE(GEN6_PMIMR, pm_iir); dev_priv->pm_iir |= pm_iir; + I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir); + POSTING_READ(GEN6_PMIMR); spin_unlock_irqrestore(&dev_priv->rps_lock, flags); queue_work(dev_priv->wq, &dev_priv->rps_work); } @@ -649,8 +650,9 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS) unsigned long flags; spin_lock_irqsave(&dev_priv->rps_lock, flags); WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); - I915_WRITE(GEN6_PMIMR, pm_iir); dev_priv->pm_iir |= pm_iir; + I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir); + POSTING_READ(GEN6_PMIMR); spin_unlock_irqrestore(&dev_priv->rps_lock, flags); queue_work(dev_priv->wq, &dev_priv->rps_work); } -- cgit v0.10.2 From a9e2641dee52cae2db7688a749344365642a5e79 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Sep 2011 14:00:21 +0200 Subject: drm/i915: close PM interrupt masking races in the rps work func This patch closes the following race: We get a PM interrupt A, mask it, set dev_priv->iir = PM_A and kick of the work item. Scheduler isn't grumpy, so the work queue takes rps_lock, grabs pm_iir = dev_priv->pm_iir and pm_imr = READ(PMIMR). Note that pm_imr == pm_iir because we've just masked the interrupt we've got. Now hw sends out PM interrupt B (not masked), we process it and mask it. Later on the irq handler also clears PMIIR. Then the work item proceeds and at the end clears PMIMR. Because (local) pm_imr == pm_iir we have pm_imr & ~pm_iir == 0 so all interrupts are enabled. Hardware is still interrupt-happy, and sends out a new PM interrupt B. PMIMR doesn't mask B (it does not mask anything), PMIIR is cleared, so we get it and hit the WARN in the interrupt handler (because dev_priv->pm_iir == PM_B). That's why I've moved the WRITE(PMIMR, 0) up under the protection of the rps_lock. And write an uncoditional 0 to PMIMR, because that's what we'll do anyway. This races looks much more likely because we can arbitrarily extend the window by grabing dev->struct mutex right after the irq handler has processed the first PM_B interrupt. Signed-off-by: Daniel Vetter Reviewed-by: Ben Widawsky Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 98eeddd..9ee2729 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -383,6 +383,7 @@ static void gen6_pm_rps_work(struct work_struct *work) pm_iir = dev_priv->pm_iir; dev_priv->pm_iir = 0; pm_imr = I915_READ(GEN6_PMIMR); + I915_WRITE(GEN6_PMIMR, 0); spin_unlock_irq(&dev_priv->rps_lock); if (!pm_iir) @@ -420,7 +421,6 @@ static void gen6_pm_rps_work(struct work_struct *work) * an *extremely* unlikely race with gen6_rps_enable() that is prevented * by holding struct_mutex for the duration of the write. */ - I915_WRITE(GEN6_PMIMR, pm_imr & ~pm_iir); mutex_unlock(&dev_priv->dev->struct_mutex); } -- cgit v0.10.2 From 6fdd4d98e56e0919bb324c2ab1ff66395cf3b385 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Thu, 8 Sep 2011 14:00:22 +0200 Subject: drm/i915: properly cancel rps_work on module unload v2 The rps disabling code wasn't properly cancelling outstanding work items. Also add a comment that explains why we're not racing with the work item that could unmask interrupts - that piece of code confused me quite a bit. v2: Ben Widawsky pointed out that the first patch would deadlock (and a few lesser problems). All corrected. Signed-off-by: Daniel Vetter Reviewed-by: Ben Widawsky Reviewed-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7c8dfcc..186cde2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7658,6 +7658,10 @@ void gen6_disable_rps(struct drm_device *dev) I915_WRITE(GEN6_RPNSWREQ, 1 << 31); I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); I915_WRITE(GEN6_PMIER, 0); + /* Complete PM interrupt masking here doesn't race with the rps work + * item again unmasking PM interrupts because that is using a different + * register (PMIMR) to mask PM interrupts. The only risk is in leaving + * stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */ spin_lock_irq(&dev_priv->rps_lock); dev_priv->pm_iir = 0; @@ -8655,6 +8659,7 @@ void intel_modeset_cleanup(struct drm_device *dev) * enqueue unpin/hotplug work. */ drm_irq_uninstall(dev); cancel_work_sync(&dev_priv->hotplug_work); + cancel_work_sync(&dev_priv->rps_work); /* flush any delayed tasks or pending work */ flush_scheduled_work(); -- cgit v0.10.2 From acc83eb5a1e0ae7dbbf89ca2a1a943ade224bb84 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 12 Sep 2011 20:49:16 +0200 Subject: drm/i915: fix swizzling on gen6+ Fixes tests/gem_tiled_pread on my snb. I know, mesa doesn't use this on gen6+, but I also hate failing testcases. Signed-off-by: Daniel Vetter Reviewed-by: Ben Widawsky Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 99c4faa..103da6f 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -92,7 +92,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; - if (INTEL_INFO(dev)->gen >= 5) { + if (INTEL_INFO(dev)->gen >= 6) { + swizzle_x = I915_BIT_6_SWIZZLE_NONE; + swizzle_y = I915_BIT_6_SWIZZLE_NONE; + } else if (IS_GEN5(dev)) { /* On Ironlake whatever DRAM config, GPU always do * same swizzling setup. */ -- cgit v0.10.2 From 6dacfd2faa398dc5206523ae6a2f8dd037488de8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Mon, 12 Sep 2011 21:30:02 +0200 Subject: drm/i915: simplify swapin/out swizzle checking a bit Use the helper function already employed by the pwrite/pread functions. Signed-off-by: Daniel Vetter Reviewed-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index f0f885f..d8e0c15 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -1476,7 +1476,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj, obj->pages[i] = page; } - if (obj->tiling_mode != I915_TILING_NONE) + if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_do_bit_17_swizzle(obj); return 0; @@ -1498,7 +1498,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj) BUG_ON(obj->madv == __I915_MADV_PURGED); - if (obj->tiling_mode != I915_TILING_NONE) + if (i915_gem_object_needs_bit17_swizzle(obj)) i915_gem_object_save_bit_17_swizzle(obj); if (obj->madv == I915_MADV_DONTNEED) diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 103da6f..31d334d 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -443,14 +443,9 @@ i915_gem_swizzle_page(struct page *page) void i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj) { - struct drm_device *dev = obj->base.dev; - drm_i915_private_t *dev_priv = dev->dev_private; int page_count = obj->base.size >> PAGE_SHIFT; int i; - if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17) - return; - if (obj->bit_17 == NULL) return; @@ -467,14 +462,9 @@ i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj) void i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj) { - struct drm_device *dev = obj->base.dev; - drm_i915_private_t *dev_priv = dev->dev_private; int page_count = obj->base.size >> PAGE_SHIFT; int i; - if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17) - return; - if (obj->bit_17 == NULL) { obj->bit_17 = kmalloc(BITS_TO_LONGS(page_count) * sizeof(long), GFP_KERNEL); -- cgit v0.10.2 From 8ffc02468145ac92b1b88896e0f18bd7bcd52591 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Wed, 14 Sep 2011 14:14:28 +0200 Subject: drm/i915: Defend against userspace creating a gem object with size==0 We currently only round up the userspace size to the next page. We assume that userspace hasn't made a mistake and requested a zero-length gem object and all through our internal code we then presume that every object is backed by at least a single page. Fix that oversight and report EINVAL back to userspace if they try to create a zero length object. [danvet: This fixes tests/gem_bad_length] Signed-off-by: Chris Wilson Signed-Off-by: Daniel Vetter Reviewed-by: Ben Widawsky Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index d8e0c15..52b199d 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -195,6 +195,8 @@ i915_gem_create(struct drm_file *file, u32 handle; size = roundup(size, PAGE_SIZE); + if (size == 0) + return -EINVAL; /* Allocate the new object */ obj = i915_gem_alloc_object(dev, size); -- cgit v0.10.2 From 24dd85ff723f142093f44244764b9b5c152235b8 Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 28 Sep 2011 11:57:23 +0200 Subject: io-mapping: ensure io_mapping_map_atomic _is_ atomic For the !HAVE_ATOMIC_IOMAP case the stub functions did not call pagefault_disable/_enable. The i915 driver relies on the map actually being atomic, otherwise it can deadlock with it's own pagefault handler in the gtt pwrite fastpath. This is exercised by gem_mmap_gtt from the intel-gpu-toosl gem testsuite. v2: Chris Wilson noted the lack of an include. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=38115 Cc: stable@kernel.org Signed-off-by: Daniel Vetter Reviewed-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/include/linux/io-mapping.h b/include/linux/io-mapping.h index 8cdcc2a1..1feeb52 100644 --- a/include/linux/io-mapping.h +++ b/include/linux/io-mapping.h @@ -117,6 +117,8 @@ io_mapping_unmap(void __iomem *vaddr) #else +#include + /* this struct isn't actually defined anywhere */ struct io_mapping; @@ -138,12 +140,14 @@ static inline void __iomem * io_mapping_map_atomic_wc(struct io_mapping *mapping, unsigned long offset) { + pagefault_disable(); return ((char __force __iomem *) mapping) + offset; } static inline void io_mapping_unmap_atomic(void __iomem *vaddr) { + pagefault_enable(); } /* Non-atomic map/unmap */ -- cgit v0.10.2 From 130c2561dea1c7418e53c1db2282b91a38641abe Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Sat, 17 Sep 2011 20:55:46 +0200 Subject: drm/i915: drop KM_USER0 argument to k(un)map_atomic Signed-off-by: Daniel Vetter Reviewed-by: Chris Wilson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 52b199d..0d0ed77 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -802,11 +802,11 @@ i915_gem_shmem_pwrite_fast(struct drm_device *dev, if (IS_ERR(page)) return PTR_ERR(page); - vaddr = kmap_atomic(page, KM_USER0); + vaddr = kmap_atomic(page); ret = __copy_from_user_inatomic(vaddr + page_offset, user_data, page_length); - kunmap_atomic(vaddr, KM_USER0); + kunmap_atomic(vaddr); set_page_dirty(page); mark_page_accessed(page); diff --git a/drivers/gpu/drm/i915/i915_gem_debug.c b/drivers/gpu/drm/i915/i915_gem_debug.c index ac90875..cc93cac 100644 --- a/drivers/gpu/drm/i915/i915_gem_debug.c +++ b/drivers/gpu/drm/i915/i915_gem_debug.c @@ -157,7 +157,7 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle) for (page = 0; page < obj->size / PAGE_SIZE; page++) { int i; - backing_map = kmap_atomic(obj->pages[page], KM_USER0); + backing_map = kmap_atomic(obj->pages[page]); if (backing_map == NULL) { DRM_ERROR("failed to map backing page\n"); @@ -181,13 +181,13 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle) } } } - kunmap_atomic(backing_map, KM_USER0); + kunmap_atomic(backing_map); backing_map = NULL; } out: if (backing_map != NULL) - kunmap_atomic(backing_map, KM_USER0); + kunmap_atomic(backing_map); iounmap(gtt_mapping); /* give syslog time to catch up */ -- cgit v0.10.2 From d15bf23bc025ded52054ac3a05d6433a54e43fd2 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 17 Oct 2011 15:51:52 -0700 Subject: intel-iommu: Workaround IOTLB hang on Ironlake GPU To work around a hardware issue, we have to submit IOTLB flushes while the graphics engine is idle. The graphics driver will (we hope) go to great lengths to ensure that it gets that right on the affected chipset(s)... so let's not screw it over by deferring the unmap and doing it later. That wouldn't be very helpful. Signed-off-by: David Woodhouse Signed-off-by: Ben Widawsky Signed-off-by: Keith Packard diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index c621c98..ff26603 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -3950,7 +3950,11 @@ static void __devinit quirk_calpella_no_shadow_gtt(struct pci_dev *dev) if (!(ggc & GGC_MEMORY_VT_ENABLED)) { printk(KERN_INFO "DMAR: BIOS has allocated no shadow GTT; disabling IOMMU for graphics\n"); dmar_map_gfx = 0; - } + } else if (dmar_map_gfx) { + /* we have to ensure the gfx device is idle before we flush */ + printk(KERN_INFO "DMAR: Disabling batched IOTLB flush on Ironlake\n"); + intel_iommu_strict = 1; + } } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0040, quirk_calpella_no_shadow_gtt); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x0044, quirk_calpella_no_shadow_gtt); -- cgit v0.10.2 From bab31a224e91bc376a68b4463d6d06cb1e4ce623 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 17 Oct 2011 15:51:53 -0700 Subject: intel-iommu: Export a flag indicating that the IOMMU is used for iGFX. We really don't want this to work in the general case; device drivers *shouldn't* care whether they are behind an IOMMU or not. But the integrated graphics is a special case, because the IOMMU and the GTT are all kind of smashed into one and generally horrifically buggy, so it's reasonable for the graphics driver to want to know when the IOMMU is active for the graphics hardware. Signed-off-by: David Woodhouse Signed-off-by: Ben Widawsky Signed-off-by: Keith Packard diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c index ff26603..1e184c1 100644 --- a/drivers/iommu/intel-iommu.c +++ b/drivers/iommu/intel-iommu.c @@ -404,6 +404,9 @@ static int dmar_forcedac; static int intel_iommu_strict; static int intel_iommu_superpage = 1; +int intel_iommu_gfx_mapped; +EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped); + #define DUMMY_DEVICE_DOMAIN_INFO ((struct device_domain_info *)(-1)) static DEFINE_SPINLOCK(device_domain_lock); static LIST_HEAD(device_domain_list); @@ -3226,9 +3229,6 @@ static void __init init_no_remapping_devices(void) } } - if (dmar_map_gfx) - return; - for_each_drhd_unit(drhd) { int i; if (drhd->ignored || drhd->include_all) @@ -3236,18 +3236,23 @@ static void __init init_no_remapping_devices(void) for (i = 0; i < drhd->devices_cnt; i++) if (drhd->devices[i] && - !IS_GFX_DEVICE(drhd->devices[i])) + !IS_GFX_DEVICE(drhd->devices[i])) break; if (i < drhd->devices_cnt) continue; - /* bypass IOMMU if it is just for gfx devices */ - drhd->ignored = 1; - for (i = 0; i < drhd->devices_cnt; i++) { - if (!drhd->devices[i]) - continue; - drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO; + /* This IOMMU has *only* gfx devices. Either bypass it or + set the gfx_mapped flag, as appropriate */ + if (dmar_map_gfx) { + intel_iommu_gfx_mapped = 1; + } else { + drhd->ignored = 1; + for (i = 0; i < drhd->devices_cnt; i++) { + if (!drhd->devices[i]) + continue; + drhd->devices[i]->dev.archdata.iommu = DUMMY_DEVICE_DOMAIN_INFO; + } } } } -- cgit v0.10.2 From f372b85463dac2fc696443d4c5063db2af5dcead Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 17 Oct 2011 15:51:54 -0700 Subject: drm/i915: Remove early exit on i915_gpu_idle [Description from: Daniel Vetter] I've just discussed this quickly with Chris on irc and it's probably best to just kill the list_empty early bailout. gpu_idle isn't a fastpath, so who cares. One candidate where we emit commands to the ring without adding anything onto these lists is e.g. pageflip. There are probably more. Signed-off-by: Ben Widawsky Signed-off-by: Daniel Vetter Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 0d0ed77..6651c36 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2193,14 +2193,8 @@ int i915_gpu_idle(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; - bool lists_empty; int ret, i; - lists_empty = (list_empty(&dev_priv->mm.flushing_list) && - list_empty(&dev_priv->mm.active_list)); - if (lists_empty) - return 0; - /* Flush everything onto the inactive list. */ for (i = 0; i < I915_NUM_RINGS; i++) { ret = i915_ring_idle(&dev_priv->ring[i]); -- cgit v0.10.2 From 5c0422878fcdc279ae9a8e8b66972a15b5efb67f Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Mon, 17 Oct 2011 15:51:55 -0700 Subject: drm/i915: ILK + VT-d workaround Idle the GPU before doing any unmaps. We know if VT-d is in use through an exported variable from iommu code. This should avoid a known HW issue. Signed-off-by: Ben Widawsky Reviewed-by: Daniel Vetter Signed-off-by: Keith Packard diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index 8515101..80a7ed0 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -923,6 +923,9 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, { int ret = -EINVAL; + if (intel_private.base.do_idle_maps) + return -ENODEV; + if (intel_private.clear_fake_agp) { int start = intel_private.base.stolen_size / PAGE_SIZE; int end = intel_private.base.gtt_mappable_entries; @@ -985,6 +988,9 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem, if (mem->page_count == 0) return 0; + if (intel_private.base.do_idle_maps) + return -ENODEV; + intel_gtt_clear_range(pg_start, mem->page_count); if (intel_private.base.needs_dmar) { @@ -1177,6 +1183,25 @@ static void gen6_cleanup(void) { } +/* Certain Gen5 chipsets require require idling the GPU before + * unmapping anything from the GTT when VT-d is enabled. + */ +extern int intel_iommu_gfx_mapped; +static inline int needs_idle_maps(void) +{ + const unsigned short gpu_devid = intel_private.pcidev->device; + + /* Query intel_iommu to see if we need the workaround. Presumably that + * was loaded first. + */ + if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB || + gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) && + intel_iommu_gfx_mapped) + return 1; + + return 0; +} + static int i9xx_setup(void) { u32 reg_addr; @@ -1211,6 +1236,9 @@ static int i9xx_setup(void) intel_private.gtt_bus_addr = reg_addr + gtt_offset; } + if (needs_idle_maps()); + intel_private.base.do_idle_maps = 1; + intel_i9xx_setup_flush(); return 0; diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c index 7a709cd..6042c5e 100644 --- a/drivers/gpu/drm/i915/i915_gem_gtt.c +++ b/drivers/gpu/drm/i915/i915_gem_gtt.c @@ -49,6 +49,28 @@ static unsigned int cache_level_to_agp_type(struct drm_device *dev, } } +static bool do_idling(struct drm_i915_private *dev_priv) +{ + bool ret = dev_priv->mm.interruptible; + + if (unlikely(dev_priv->mm.gtt->do_idle_maps)) { + dev_priv->mm.interruptible = false; + if (i915_gpu_idle(dev_priv->dev)) { + DRM_ERROR("Couldn't idle GPU\n"); + /* Wait a bit, in hopes it avoids the hang */ + udelay(10); + } + } + + return ret; +} + +static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible) +{ + if (unlikely(dev_priv->mm.gtt->do_idle_maps)) + dev_priv->mm.interruptible = interruptible; +} + void i915_gem_restore_gtt_mappings(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -117,6 +139,12 @@ void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj, void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) { + struct drm_device *dev = obj->base.dev; + struct drm_i915_private *dev_priv = dev->dev_private; + bool interruptible; + + interruptible = do_idling(dev_priv); + intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT, obj->base.size >> PAGE_SHIFT); @@ -124,4 +152,6 @@ void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) intel_gtt_unmap_memory(obj->sg_list, obj->num_sg); obj->sg_list = NULL; } + + undo_idling(dev_priv, interruptible); } diff --git a/include/drm/intel-gtt.h b/include/drm/intel-gtt.h index 9e343c0..b174620 100644 --- a/include/drm/intel-gtt.h +++ b/include/drm/intel-gtt.h @@ -13,6 +13,8 @@ const struct intel_gtt { unsigned int gtt_mappable_entries; /* Whether i915 needs to use the dmar apis or not. */ unsigned int needs_dmar : 1; + /* Whether we idle the gpu before mapping/unmapping */ + unsigned int do_idle_maps : 1; } *intel_gtt_get(void); void intel_gtt_chipset_flush(void); -- cgit v0.10.2 From b73fe58cafc6954db0170a5d4ef2b4b6316f6709 Mon Sep 17 00:00:00 2001 From: Ben Widawsky Date: Tue, 4 Oct 2011 15:16:48 -0700 Subject: drm: Add Panel Self Refresh DP addresses Add the addresses and definitions I care about for Panel Self Refresh, as documented in the eDP spec. I'm sending these out before some other patches because this should be a fairly simple one to get upstream and not require too much fuss (where the others may have some fuss). This file is a mess with white spacing. I tried to stay consistent with the surrounding code. v2: had some silly mistakes in v1 which Keith caught Cc: Dave Airlie Cc: Keith Packard Signed-off-by: Ben Widawsky Signed-off-by: Keith Packard diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 91567bb..2b1a358 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -74,6 +74,20 @@ #define DP_TRAINING_AUX_RD_INTERVAL 0x00e +#define DP_PSR_SUPPORT 0x070 +# define DP_PSR_IS_SUPPORTED 1 +#define DP_PSR_CAPS 0x071 +# define DP_PSR_NO_TRAIN_ON_EXIT 1 +# define DP_PSR_SETUP_TIME_330 (0 << 1) +# define DP_PSR_SETUP_TIME_275 (1 << 1) +# define DP_PSR_SETUP_TIME_220 (2 << 1) +# define DP_PSR_SETUP_TIME_165 (3 << 1) +# define DP_PSR_SETUP_TIME_110 (4 << 1) +# define DP_PSR_SETUP_TIME_55 (5 << 1) +# define DP_PSR_SETUP_TIME_0 (6 << 1) +# define DP_PSR_SETUP_TIME_MASK (7 << 1) +# define DP_PSR_SETUP_TIME_SHIFT 1 + /* link configuration */ #define DP_LINK_BW_SET 0x100 # define DP_LINK_BW_1_62 0x06 @@ -133,6 +147,12 @@ #define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108 # define DP_SET_ANSI_8B10B (1 << 0) +#define DP_PSR_EN_CFG 0x170 +# define DP_PSR_ENABLE (1 << 0) +# define DP_PSR_MAIN_LINK_ACTIVE (1 << 1) +# define DP_PSR_CRC_VERIFICATION (1 << 2) +# define DP_PSR_FRAME_CAPTURE (1 << 3) + #define DP_LANE0_1_STATUS 0x202 #define DP_LANE2_3_STATUS 0x203 # define DP_LANE_CR_DONE (1 << 0) @@ -169,6 +189,22 @@ # define DP_SET_POWER_D0 0x1 # define DP_SET_POWER_D3 0x2 +#define DP_PSR_ERROR_STATUS 0x2006 +# define DP_PSR_LINK_CRC_ERROR (1 << 0) +# define DP_PSR_RFB_STORAGE_ERROR (1 << 1) + +#define DP_PSR_ESI 0x2007 +# define DP_PSR_CAPS_CHANGE (1 << 0) + +#define DP_PSR_STATUS 0x2008 +# define DP_PSR_SINK_INACTIVE 0 +# define DP_PSR_SINK_ACTIVE_SRC_SYNCED 1 +# define DP_PSR_SINK_ACTIVE_RFB 2 +# define DP_PSR_SINK_ACTIVE_SINK_SYNCED 3 +# define DP_PSR_SINK_ACTIVE_RESYNC 4 +# define DP_PSR_SINK_INTERNAL_ERROR 7 +# define DP_PSR_SINK_STATE_MASK 0x07 + #define MODE_I2C_START 1 #define MODE_I2C_WRITE 2 #define MODE_I2C_READ 4 -- cgit v0.10.2 From fcbc34e4dc9ed43f5b147e95beec56c987c683c5 Mon Sep 17 00:00:00 2001 From: Kenneth Graunke Date: Tue, 11 Oct 2011 23:41:08 +0200 Subject: drm/i915: Remove implied length of 2 from GFX_OP_PIPE_CONTROL #define. Not all PIPE_CONTROLs have a length of 2, so remove it from the #define and make each invocation specify the desired length. Signed-off-by: Kenneth Graunke [danvet: implement style suggestion from Ben Widawsdy] Signed-Off-by: Daniel Vetter Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 28a313a..1b07547 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -242,7 +242,7 @@ #define ASYNC_FLIP (1<<22) #define DISPLAY_PLANE_A (0<<20) #define DISPLAY_PLANE_B (1<<20) -#define GFX_OP_PIPE_CONTROL ((0x3<<29)|(0x3<<27)|(0x2<<24)|2) +#define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2)) #define PIPE_CONTROL_QW_WRITE (1<<14) #define PIPE_CONTROL_DEPTH_STALL (1<<13) #define PIPE_CONTROL_WC_FLUSH (1<<12) diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 0e99589..7f188c9 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -438,8 +438,8 @@ gen6_blt_ring_sync_to(struct intel_ring_buffer *waiter, #define PIPE_CONTROL_FLUSH(ring__, addr__) \ do { \ - intel_ring_emit(ring__, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | \ - PIPE_CONTROL_DEPTH_STALL | 2); \ + intel_ring_emit(ring__, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | \ + PIPE_CONTROL_DEPTH_STALL); \ intel_ring_emit(ring__, (addr__) | PIPE_CONTROL_GLOBAL_GTT); \ intel_ring_emit(ring__, 0); \ intel_ring_emit(ring__, 0); \ @@ -467,7 +467,7 @@ pc_render_add_request(struct intel_ring_buffer *ring, if (ret) return ret; - intel_ring_emit(ring, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH); intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT); intel_ring_emit(ring, seqno); @@ -483,7 +483,7 @@ pc_render_add_request(struct intel_ring_buffer *ring, PIPE_CONTROL_FLUSH(ring, scratch_addr); scratch_addr += 128; PIPE_CONTROL_FLUSH(ring, scratch_addr); - intel_ring_emit(ring, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH | PIPE_CONTROL_NOTIFY); intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT); -- cgit v0.10.2 From 9d971b37534fb268251f74cc04a36a0a16f7da04 Mon Sep 17 00:00:00 2001 From: Kenneth Graunke Date: Tue, 11 Oct 2011 23:41:09 +0200 Subject: drm/i915: Rename PIPE_CONTROL bit defines to be less terse. "STALL_AT_SCOREBOARD" is much clearer than "STALL_EN" now that there are several different kinds of stalls. Also, "INSTRUCTION_CACHE_INVALIDATE" is a lot easier to understand at a glance than the terse "IS_FLUSH." Signed-off-by: Kenneth Graunke [danvet: use INVALIDATE for ro cache flags for more consistency] Signed-Off-by: Daniel Vetter Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 1b07547..d675d79 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -243,15 +243,15 @@ #define DISPLAY_PLANE_A (0<<20) #define DISPLAY_PLANE_B (1<<20) #define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2)) -#define PIPE_CONTROL_QW_WRITE (1<<14) -#define PIPE_CONTROL_DEPTH_STALL (1<<13) -#define PIPE_CONTROL_WC_FLUSH (1<<12) -#define PIPE_CONTROL_IS_FLUSH (1<<11) /* MBZ on Ironlake */ -#define PIPE_CONTROL_TC_FLUSH (1<<10) /* GM45+ only */ -#define PIPE_CONTROL_ISP_DIS (1<<9) -#define PIPE_CONTROL_NOTIFY (1<<8) +#define PIPE_CONTROL_QW_WRITE (1<<14) +#define PIPE_CONTROL_DEPTH_STALL (1<<13) +#define PIPE_CONTROL_WRITE_FLUSH (1<<12) +#define PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE (1<<11) /* MBZ on Ironlake */ +#define PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE (1<<10) /* GM45+ only */ +#define PIPE_CONTROL_INDIRECT_STATE_DISABLE (1<<9) +#define PIPE_CONTROL_NOTIFY (1<<8) +#define PIPE_CONTROL_STALL_AT_SCOREBOARD (1<<1) #define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */ -#define PIPE_CONTROL_STALL_EN (1<<1) /* in addr word, Ironlake+ only */ /* diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index 7f188c9..ca83635 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -468,7 +468,8 @@ pc_render_add_request(struct intel_ring_buffer *ring, return ret; intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | - PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH); + PIPE_CONTROL_WRITE_FLUSH | + PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE); intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT); intel_ring_emit(ring, seqno); intel_ring_emit(ring, 0); @@ -484,7 +485,8 @@ pc_render_add_request(struct intel_ring_buffer *ring, scratch_addr += 128; PIPE_CONTROL_FLUSH(ring, scratch_addr); intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | - PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH | + PIPE_CONTROL_WRITE_FLUSH | + PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE | PIPE_CONTROL_NOTIFY); intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT); intel_ring_emit(ring, seqno); -- cgit v0.10.2 From 8d31528703ceda6f631e39953130abe9b3ca52b2 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Sun, 16 Oct 2011 10:23:31 +0200 Subject: drm/i915: Use PIPE_CONTROL for flushing on gen6+. v2 by danvet: Use a new flag to flush the render target cache on gen6+ (hw reuses the old write flush bit), as suggested by Ben Widawsdy. Signed-off-by: Jesse Barnes Signed-off-by: Kenneth Graunke [danvet: this seems to fix cairo-perf-trace hangs on my snb] Signed-Off-by: Daniel Vetter Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index d675d79..6f6eed1 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -243,14 +243,20 @@ #define DISPLAY_PLANE_A (0<<20) #define DISPLAY_PLANE_B (1<<20) #define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2)) +#define PIPE_CONTROL_CS_STALL (1<<20) #define PIPE_CONTROL_QW_WRITE (1<<14) #define PIPE_CONTROL_DEPTH_STALL (1<<13) #define PIPE_CONTROL_WRITE_FLUSH (1<<12) +#define PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH (1<<12) /* gen6+ */ #define PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE (1<<11) /* MBZ on Ironlake */ #define PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE (1<<10) /* GM45+ only */ #define PIPE_CONTROL_INDIRECT_STATE_DISABLE (1<<9) #define PIPE_CONTROL_NOTIFY (1<<8) +#define PIPE_CONTROL_VF_CACHE_INVALIDATE (1<<4) +#define PIPE_CONTROL_CONST_CACHE_INVALIDATE (1<<3) +#define PIPE_CONTROL_STATE_CACHE_INVALIDATE (1<<2) #define PIPE_CONTROL_STALL_AT_SCOREBOARD (1<<1) +#define PIPE_CONTROL_DEPTH_CACHE_FLUSH (1<<0) #define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */ diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c index ca83635..ca70e2f 100644 --- a/drivers/gpu/drm/i915/intel_ringbuffer.c +++ b/drivers/gpu/drm/i915/intel_ringbuffer.c @@ -34,6 +34,16 @@ #include "i915_trace.h" #include "intel_drv.h" +/* + * 965+ support PIPE_CONTROL commands, which provide finer grained control + * over cache flushing. + */ +struct pipe_control { + struct drm_i915_gem_object *obj; + volatile u32 *cpu_page; + u32 gtt_offset; +}; + static inline int ring_space(struct intel_ring_buffer *ring) { int space = (ring->head & HEAD_ADDR) - (ring->tail + 8); @@ -123,6 +133,118 @@ render_ring_flush(struct intel_ring_buffer *ring, return 0; } +/** + * Emits a PIPE_CONTROL with a non-zero post-sync operation, for + * implementing two workarounds on gen6. From section 1.4.7.1 + * "PIPE_CONTROL" of the Sandy Bridge PRM volume 2 part 1: + * + * [DevSNB-C+{W/A}] Before any depth stall flush (including those + * produced by non-pipelined state commands), software needs to first + * send a PIPE_CONTROL with no bits set except Post-Sync Operation != + * 0. + * + * [Dev-SNB{W/A}]: Before a PIPE_CONTROL with Write Cache Flush Enable + * =1, a PIPE_CONTROL with any non-zero post-sync-op is required. + * + * And the workaround for these two requires this workaround first: + * + * [Dev-SNB{W/A}]: Pipe-control with CS-stall bit set must be sent + * BEFORE the pipe-control with a post-sync op and no write-cache + * flushes. + * + * And this last workaround is tricky because of the requirements on + * that bit. From section 1.4.7.2.3 "Stall" of the Sandy Bridge PRM + * volume 2 part 1: + * + * "1 of the following must also be set: + * - Render Target Cache Flush Enable ([12] of DW1) + * - Depth Cache Flush Enable ([0] of DW1) + * - Stall at Pixel Scoreboard ([1] of DW1) + * - Depth Stall ([13] of DW1) + * - Post-Sync Operation ([13] of DW1) + * - Notify Enable ([8] of DW1)" + * + * The cache flushes require the workaround flush that triggered this + * one, so we can't use it. Depth stall would trigger the same. + * Post-sync nonzero is what triggered this second workaround, so we + * can't use that one either. Notify enable is IRQs, which aren't + * really our business. That leaves only stall at scoreboard. + */ +static int +intel_emit_post_sync_nonzero_flush(struct intel_ring_buffer *ring) +{ + struct pipe_control *pc = ring->private; + u32 scratch_addr = pc->gtt_offset + 128; + int ret; + + + ret = intel_ring_begin(ring, 6); + if (ret) + return ret; + + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5)); + intel_ring_emit(ring, PIPE_CONTROL_CS_STALL | + PIPE_CONTROL_STALL_AT_SCOREBOARD); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */ + intel_ring_emit(ring, 0); /* low dword */ + intel_ring_emit(ring, 0); /* high dword */ + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); + + ret = intel_ring_begin(ring, 6); + if (ret) + return ret; + + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5)); + intel_ring_emit(ring, PIPE_CONTROL_QW_WRITE); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */ + intel_ring_emit(ring, 0); + intel_ring_emit(ring, 0); + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); + + return 0; +} + +static int +gen6_render_ring_flush(struct intel_ring_buffer *ring, + u32 invalidate_domains, u32 flush_domains) +{ + u32 flags = 0; + struct pipe_control *pc = ring->private; + u32 scratch_addr = pc->gtt_offset + 128; + int ret; + + /* Force SNB workarounds for PIPE_CONTROL flushes */ + intel_emit_post_sync_nonzero_flush(ring); + + /* Just flush everything. Experiments have shown that reducing the + * number of bits based on the write domains has little performance + * impact. + */ + flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; + flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; + flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE; + flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE; + + ret = intel_ring_begin(ring, 6); + if (ret) + return ret; + + intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5)); + intel_ring_emit(ring, flags); + intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); + intel_ring_emit(ring, 0); /* lower dword */ + intel_ring_emit(ring, 0); /* uppwer dword */ + intel_ring_emit(ring, MI_NOOP); + intel_ring_advance(ring); + + return 0; +} + static void ring_write_tail(struct intel_ring_buffer *ring, u32 value) { @@ -206,16 +328,6 @@ static int init_ring_common(struct intel_ring_buffer *ring) return 0; } -/* - * 965+ support PIPE_CONTROL commands, which provide finer grained control - * over cache flushing. - */ -struct pipe_control { - struct drm_i915_gem_object *obj; - volatile u32 *cpu_page; - u32 gtt_offset; -}; - static int init_pipe_control(struct intel_ring_buffer *ring) { @@ -296,8 +408,7 @@ static int init_render_ring(struct intel_ring_buffer *ring) GFX_MODE_ENABLE(GFX_REPLAY_MODE)); } - if (INTEL_INFO(dev)->gen >= 6) { - } else if (IS_GEN5(dev)) { + if (INTEL_INFO(dev)->gen >= 5) { ret = init_pipe_control(ring); if (ret) return ret; @@ -1360,6 +1471,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev) *ring = render_ring; if (INTEL_INFO(dev)->gen >= 6) { ring->add_request = gen6_add_request; + ring->flush = gen6_render_ring_flush; ring->irq_get = gen6_render_ring_get_irq; ring->irq_put = gen6_render_ring_put_irq; } else if (IS_GEN5(dev)) { -- cgit v0.10.2 From c4f9c4c2b3f1831e932e04db992cf6fe92c2a95a Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 10 Oct 2011 14:28:52 -0700 Subject: drm/i915: always set FDI composite sync bit It's needed for 3 pipe support as well as just regular functionality (e.g. DisplayPort). Signed-off-by: Jesse Barnes Tested-by: Adam Jackson Tested-by: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 6f6eed1..d8bf9cf 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -3189,6 +3189,7 @@ #define FDI_LINK_TRAIN_NONE_IVB (3<<8) /* both Tx and Rx */ +#define FDI_COMPOSITE_SYNC (1<<11) #define FDI_LINK_TRAIN_AUTO (1<<10) #define FDI_SCRAMBLING_ENABLE (0<<7) #define FDI_SCRAMBLING_DISABLE (1<<7) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 186cde2..2e28c68 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2601,6 +2601,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; + temp |= FDI_COMPOSITE_SYNC; I915_WRITE(reg, temp | FDI_TX_ENABLE); reg = FDI_RX_CTL(pipe); @@ -2608,6 +2609,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc) temp &= ~FDI_LINK_TRAIN_AUTO; temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; + temp |= FDI_COMPOSITE_SYNC; I915_WRITE(reg, temp | FDI_RX_ENABLE); POSTING_READ(reg); -- cgit v0.10.2 From 4c609cb890b13ef58e0c0cdd238993484e1b9b83 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 2 Sep 2011 12:52:11 -0700 Subject: drm/i915: PLL macro cleanup and pipe assertion check Add a macro for accessing the two pipe PLLs and add a check to make sure we don't access a non-existent one in the enable/disable functions. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index d8bf9cf..6cad6b1 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2967,15 +2967,15 @@ #define _PCH_DPLL_A 0xc6014 #define _PCH_DPLL_B 0xc6018 -#define PCH_DPLL(pipe) _PIPE(pipe, _PCH_DPLL_A, _PCH_DPLL_B) +#define PCH_DPLL(pipe) (pipe == 0 ? _PCH_DPLL_A : _PCH_DPLL_B) #define _PCH_FPA0 0xc6040 #define FP_CB_TUNE (0x3<<22) #define _PCH_FPA1 0xc6044 #define _PCH_FPB0 0xc6048 #define _PCH_FPB1 0xc604c -#define PCH_FP0(pipe) _PIPE(pipe, _PCH_FPA0, _PCH_FPB0) -#define PCH_FP1(pipe) _PIPE(pipe, _PCH_FPA1, _PCH_FPB1) +#define PCH_FP0(pipe) (pipe == 0 ? _PCH_FPA0 : _PCH_FPB0) +#define PCH_FP1(pipe) (pipe == 0 ? _PCH_FPA1 : _PCH_FPB1) #define PCH_DPLL_TEST 0xc606c diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2e28c68..dade95c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1172,6 +1172,9 @@ static void intel_enable_pch_pll(struct drm_i915_private *dev_priv, int reg; u32 val; + if (pipe > 1) + return; + /* PCH only available on ILK+ */ BUG_ON(dev_priv->info->gen < 5); @@ -1192,6 +1195,9 @@ static void intel_disable_pch_pll(struct drm_i915_private *dev_priv, int reg; u32 val; + if (pipe > 1) + return; + /* PCH only available on ILK+ */ BUG_ON(dev_priv->info->gen < 5); -- cgit v0.10.2 From 27f8227b1e2b326a9a0995dd9c1f14893c61ee01 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 2 Sep 2011 12:54:37 -0700 Subject: drm/i915: support 3 pipes on IVB+ Well almost anyway. IVB has 3 planes, pipes, transcoders, and FDI interfaces, but only 2 pipe PLLs. So two of the pipes must use the same pipe timings (e.g. 2 DP plus one other, or two HDMI with the same mode and one other, etc.). Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index d76da38..2eac955 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -2035,7 +2035,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) spin_lock_init(&dev_priv->error_lock); spin_lock_init(&dev_priv->rps_lock); - if (IS_MOBILE(dev) || !IS_GEN2(dev)) + if (IS_IVYBRIDGE(dev)) + dev_priv->num_pipe = 3; + else if (IS_MOBILE(dev) || !IS_GEN2(dev)) dev_priv->num_pipe = 2; else dev_priv->num_pipe = 1; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c02cdac..c5ca0d3 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -674,8 +674,8 @@ typedef struct drm_i915_private { /* Panel fitter placement and size for Ironlake+ */ u32 pch_pf_pos, pch_pf_size; - struct drm_crtc *plane_to_crtc_mapping[2]; - struct drm_crtc *pipe_to_crtc_mapping[2]; + struct drm_crtc *plane_to_crtc_mapping[3]; + struct drm_crtc *pipe_to_crtc_mapping[3]; wait_queue_head_t pending_flip_queue; bool flip_pending_is_done; diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index dade95c..6b1a1ed 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2092,6 +2092,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc, switch (plane) { case 0: case 1: + case 2: break; default: DRM_ERROR("Can't update plane %d in SAREA\n", plane); @@ -2191,6 +2192,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, case 0: case 1: break; + case 2: + if (IS_IVYBRIDGE(dev)) + break; + /* fall through otherwise */ default: DRM_ERROR("no plane for crtc\n"); return -EINVAL; @@ -2889,6 +2894,8 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); else if (pipe == 1 && (temp & TRANSB_DPLL_ENABLE) == 0) temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); + else if (pipe == 2 && (temp & TRANSC_DPLL_ENABLE) == 0) + temp |= (TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); I915_WRITE(PCH_DPLL_SEL, temp); } diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3d73374..3009d2a 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -2215,7 +2215,7 @@ intel_dp_init(struct drm_device *dev, int output_reg) ironlake_panel_vdd_work); } - intel_encoder->crtc_mask = (1 << 0) | (1 << 1); + intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); connector->interlace_allowed = true; connector->doublescan_allowed = 0; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 75026ba..185c5aa 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -514,7 +514,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) connector->polled = DRM_CONNECTOR_POLL_HPD; connector->interlace_allowed = 0; connector->doublescan_allowed = 0; - intel_encoder->crtc_mask = (1 << 0) | (1 << 1); + intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); /* Set up the DDC bus. */ if (sdvox_reg == SDVOB) { diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c index 31da77f..42f165a 100644 --- a/drivers/gpu/drm/i915/intel_lvds.c +++ b/drivers/gpu/drm/i915/intel_lvds.c @@ -888,9 +888,11 @@ bool intel_lvds_init(struct drm_device *dev) intel_encoder->type = INTEL_OUTPUT_LVDS; intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT); - intel_encoder->crtc_mask = (1 << 1); - if (INTEL_INFO(dev)->gen >= 5) - intel_encoder->crtc_mask |= (1 << 0); + if (HAS_PCH_SPLIT(dev)) + intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); + else + intel_encoder->crtc_mask = (1 << 1); + drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs); drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs); connector->display_info.subpixel_order = SubPixelHorizontalRGB; diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c index 1b97d1f..6db3b1c 100644 --- a/drivers/gpu/drm/i915/intel_sdvo.c +++ b/drivers/gpu/drm/i915/intel_sdvo.c @@ -2203,7 +2203,7 @@ intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, uint16_t flags) bytes[0], bytes[1]); return false; } - intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1); + intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2); return true; } -- cgit v0.10.2 From d9d444cbc536050ef66e4cefcd55224c46cbbadf Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 2 Sep 2011 13:03:05 -0700 Subject: drm/i915: split refclk code out of ironlake_crtc_mode_set Just a cleanup to make the mode_set function more manageable. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6b1a1ed..3b62b91 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5241,6 +5241,40 @@ void ironlake_init_pch_refclk(struct drm_device *dev) } } +static int ironlake_get_refclk(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_encoder *encoder; + struct drm_mode_config *mode_config = &dev->mode_config; + struct intel_encoder *edp_encoder = NULL; + int num_connectors = 0; + bool is_lvds = false; + + list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { + if (encoder->base.crtc != crtc) + continue; + + switch (encoder->type) { + case INTEL_OUTPUT_LVDS: + is_lvds = true; + break; + case INTEL_OUTPUT_EDP: + edp_encoder = encoder; + break; + } + num_connectors++; + } + + if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { + DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n", + dev_priv->lvds_ssc_freq); + return dev_priv->lvds_ssc_freq * 1000; + } + + return 120000; +} + static int ironlake_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode, @@ -5300,10 +5334,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, num_connectors++; } - /* - * Every reference clock in a PCH system is 120MHz - */ - refclk = 120000; + refclk = ironlake_get_refclk(crtc); /* * Returns a set of divisors for the desired target clock with the given -- cgit v0.10.2 From 75770564c90c45618003267f4cdde4bbc090f1bd Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 12 Oct 2011 09:01:58 -0700 Subject: drm/i915: use transcoder select bits on VGA and HDMI on CPT Required for 3 pipe functionality. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index 451534c..fee0ad0 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -152,17 +152,13 @@ static void intel_crt_mode_set(struct drm_encoder *encoder, if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) adpa |= ADPA_VSYNC_ACTIVE_HIGH; - if (intel_crtc->pipe == 0) { - if (HAS_PCH_CPT(dev)) - adpa |= PORT_TRANS_A_SEL_CPT; - else - adpa |= ADPA_PIPE_A_SELECT; - } else { - if (HAS_PCH_CPT(dev)) - adpa |= PORT_TRANS_B_SEL_CPT; - else - adpa |= ADPA_PIPE_B_SELECT; - } + /* For CPT allow 3 pipe config, for others just use A or B */ + if (HAS_PCH_CPT(dev)) + adpa |= PORT_TRANS_SEL_CPT(intel_crtc->pipe); + else if (intel_crtc->pipe == 0) + adpa |= ADPA_PIPE_A_SELECT; + else + adpa |= ADPA_PIPE_B_SELECT; if (!HAS_PCH_SPLIT(dev)) I915_WRITE(BCLRPAT(intel_crtc->pipe), 0); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 185c5aa..e7e716c 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -252,12 +252,10 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, intel_write_eld(encoder, adjusted_mode); } - if (intel_crtc->pipe == 1) { - if (HAS_PCH_CPT(dev)) - sdvox |= PORT_TRANS_B_SEL_CPT; - else - sdvox |= SDVO_PIPE_B_SELECT; - } + if (HAS_PCH_CPT(dev)) + sdvox |= PORT_TRANS_SEL_CPT(intel_crtc->pipe); + else if (intel_crtc->pipe == 1) + sdvox |= SDVO_PIPE_B_SELECT; I915_WRITE(intel_hdmi->sdvox_reg, sdvox); POSTING_READ(intel_hdmi->sdvox_reg); -- cgit v0.10.2 From d3ccbe8670520fc61cbe974c97761b0dfc57f6df Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 12 Oct 2011 09:27:42 -0700 Subject: drm/i915: fix PCH PLL assertion check for 3 pipes Add a couple of checks now that we're using the 3rd transcoder: 1) make sure the transcoder PLL enable bit is set for the transcoder in question 2) when checking actual PLL enable, use the selected PLL number rather than the transcoder number (they could be different now) Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3b62b91..63f8141 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -803,6 +803,19 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv, u32 val; bool cur_state; + if (HAS_PCH_CPT(dev_priv->dev)) { + u32 pch_dpll; + + pch_dpll = I915_READ(PCH_DPLL_SEL); + + /* Make sure the selected PLL is enabled to the transcoder */ + WARN(!((pch_dpll >> (4 * pipe)) & 8), + "transcoder %d PLL not enabled\n", pipe); + + /* Convert the transcoder pipe number to a pll pipe number */ + pipe = (pch_dpll >> (4 * pipe)) & 1; + } + reg = PCH_DPLL(pipe); val = I915_READ(reg); cur_state = !!(val & DPLL_VCO_ENABLE); -- cgit v0.10.2 From 4b645f14021871e06ce96c359bbdf0b48248c26e Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 12 Oct 2011 09:51:31 -0700 Subject: drm/i915: add PLL sharing support to handle 3 pipes Add two new fields to the intel_crtc struct for 3 pipe support: no_pll and use_pll_a. The no_pll field is only set on the 3rd pipe to indicate that it doesn't have a PLL of its own and so shouldn't try to write the main PLL regs. The use_pll_a field controls which PLL pipe 3 will share, A or B. The core code will try to share PLLs with whichever pipe has the same timings, rejecting the mode set if none is found. This means that pipe 3 must always be set after one of the other pipes has been configured with real PLL settings. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 63f8141..c9bbf5e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2893,7 +2893,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); int pipe = intel_crtc->pipe; - u32 reg, temp; + u32 reg, temp, transc_sel; /* For PCH output, training FDI link */ dev_priv->display.fdi_link_train(crtc); @@ -2901,6 +2901,9 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) intel_enable_pch_pll(dev_priv, pipe); if (HAS_PCH_CPT(dev)) { + transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL : + TRANSC_DPLLB_SEL; + /* Be sure PCH DPLL SEL is set */ temp = I915_READ(PCH_DPLL_SEL); if (pipe == 0 && (temp & TRANSA_DPLL_ENABLE) == 0) @@ -2908,7 +2911,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) else if (pipe == 1 && (temp & TRANSB_DPLL_ENABLE) == 0) temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); else if (pipe == 2 && (temp & TRANSC_DPLL_ENABLE) == 0) - temp |= (TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); + temp |= (TRANSC_DPLL_ENABLE | transc_sel); I915_WRITE(PCH_DPLL_SEL, temp); } @@ -3080,8 +3083,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); break; case 2: - /* FIXME: manage transcoder PLLs? */ - temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); + /* C shares PLL A or B */ + temp &= ~(TRANSC_DPLL_ENABLE | TRANSB_DPLLB_SEL); break; default: BUG(); /* wtf */ @@ -3090,7 +3093,8 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) } /* disable PCH DPLL */ - intel_disable_pch_pll(dev_priv, pipe); + if (!intel_crtc->no_pll) + intel_disable_pch_pll(dev_priv, pipe); /* Switch from PCDclk to Rawclk */ reg = FDI_RX_CTL(pipe); @@ -5549,16 +5553,34 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, drm_mode_debug_printmodeline(mode); /* PCH eDP needs FDI, but CPU eDP does not */ - if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) { - I915_WRITE(PCH_FP0(pipe), fp); - I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); - - POSTING_READ(PCH_DPLL(pipe)); - udelay(150); + if (!intel_crtc->no_pll) { + if (!has_edp_encoder || + intel_encoder_is_pch_edp(&has_edp_encoder->base)) { + I915_WRITE(PCH_FP0(pipe), fp); + I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); + + POSTING_READ(PCH_DPLL(pipe)); + udelay(150); + } + } else { + if (dpll == (I915_READ(PCH_DPLL(0)) & 0x7fffffff) && + fp == I915_READ(PCH_FP0(0))) { + intel_crtc->use_pll_a = true; + DRM_DEBUG_KMS("using pipe a dpll\n"); + } else if (dpll == (I915_READ(PCH_DPLL(1)) & 0x7fffffff) && + fp == I915_READ(PCH_FP0(1))) { + intel_crtc->use_pll_a = false; + DRM_DEBUG_KMS("using pipe b dpll\n"); + } else { + DRM_DEBUG_KMS("no matching PLL configuration for pipe 2\n"); + return -EINVAL; + } } /* enable transcoder DPLL */ if (HAS_PCH_CPT(dev)) { + u32 transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL : + TRANSC_DPLLB_SEL; temp = I915_READ(PCH_DPLL_SEL); switch (pipe) { case 0: @@ -5568,8 +5590,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, temp |= TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL; break; case 2: - /* FIXME: manage transcoder PLLs? */ - temp |= TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL; + temp |= TRANSC_DPLL_ENABLE | transc_sel; break; default: BUG(); @@ -5587,17 +5608,13 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, if (is_lvds) { temp = I915_READ(PCH_LVDS); temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; - if (pipe == 1) { - if (HAS_PCH_CPT(dev)) - temp |= PORT_TRANS_B_SEL_CPT; - else - temp |= LVDS_PIPEB_SELECT; - } else { - if (HAS_PCH_CPT(dev)) - temp &= ~PORT_TRANS_SEL_MASK; - else - temp &= ~LVDS_PIPEB_SELECT; - } + if (HAS_PCH_CPT(dev)) + temp |= PORT_TRANS_SEL_CPT(pipe); + else if (pipe == 1) + temp |= LVDS_PIPEB_SELECT; + else + temp &= ~LVDS_PIPEB_SELECT; + /* set the corresponsding LVDS_BORDER bit */ temp |= dev_priv->lvds_border_bits; /* Set the B0-B3 data pairs corresponding to whether we're going to @@ -5647,8 +5664,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, I915_WRITE(TRANSDPLINK_N1(pipe), 0); } - if (!has_edp_encoder || - intel_encoder_is_pch_edp(&has_edp_encoder->base)) { + if (!intel_crtc->no_pll && + (!has_edp_encoder || + intel_encoder_is_pch_edp(&has_edp_encoder->base))) { I915_WRITE(PCH_DPLL(pipe), dpll); /* Wait for the clocks to stabilize. */ @@ -5664,18 +5682,20 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } intel_crtc->lowfreq_avail = false; - if (is_lvds && has_reduced_clock && i915_powersave) { - I915_WRITE(PCH_FP1(pipe), fp2); - intel_crtc->lowfreq_avail = true; - if (HAS_PIPE_CXSR(dev)) { - DRM_DEBUG_KMS("enabling CxSR downclocking\n"); - pipeconf |= PIPECONF_CXSR_DOWNCLOCK; - } - } else { - I915_WRITE(PCH_FP1(pipe), fp); - if (HAS_PIPE_CXSR(dev)) { - DRM_DEBUG_KMS("disabling CxSR downclocking\n"); - pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; + if (!intel_crtc->no_pll) { + if (is_lvds && has_reduced_clock && i915_powersave) { + I915_WRITE(PCH_FP1(pipe), fp2); + intel_crtc->lowfreq_avail = true; + if (HAS_PIPE_CXSR(dev)) { + DRM_DEBUG_KMS("enabling CxSR downclocking\n"); + pipeconf |= PIPECONF_CXSR_DOWNCLOCK; + } + } else { + I915_WRITE(PCH_FP1(pipe), fp); + if (HAS_PIPE_CXSR(dev)) { + DRM_DEBUG_KMS("disabling CxSR downclocking\n"); + pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; + } } } @@ -7291,6 +7311,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) intel_crtc->bpp = 24; /* default for pre-Ironlake */ if (HAS_PCH_SPLIT(dev)) { + if (pipe == 2 && IS_IVYBRIDGE(dev)) + intel_crtc->no_pll = true; intel_helper_funcs.prepare = ironlake_crtc_prepare; intel_helper_funcs.commit = ironlake_crtc_commit; } else { diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 98044d6..5829854 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -171,6 +171,9 @@ struct intel_crtc { int16_t cursor_width, cursor_height; bool cursor_visible; unsigned int bpp; + + bool no_pll; /* tertiary pipe for IVB */ + bool use_pll_a; }; #define to_intel_crtc(x) container_of(x, struct intel_crtc, base) -- cgit v0.10.2 From f7cb34d47d2e30a8eb6201390ad81e232541c6d0 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 12 Oct 2011 10:49:14 -0700 Subject: drm/i915: fix debug output for 3 pipe configs We can have more than just A and B these days. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index c9bbf5e..880d44e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5549,7 +5549,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, /* Set up the display plane register */ dspcntr = DISPPLANE_GAMMA_ENABLE; - DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); + DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe); drm_mode_debug_printmodeline(mode); /* PCH eDP needs FDI, but CPU eDP does not */ -- cgit v0.10.2 From 65a21cd65316145f9302594be8e69074369e1050 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 12 Oct 2011 11:10:21 -0700 Subject: drm/i915: fix IVB cursor support The cursor regs have moved around, add the offsets and new macros for getting at them. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 6cad6b1..c7ef323 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2573,10 +2573,18 @@ #define _CURBBASE 0x700c4 #define _CURBPOS 0x700c8 +#define _CURBCNTR_IVB 0x71080 +#define _CURBBASE_IVB 0x71084 +#define _CURBPOS_IVB 0x71088 + #define CURCNTR(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR) #define CURBASE(pipe) _PIPE(pipe, _CURABASE, _CURBBASE) #define CURPOS(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS) +#define CURCNTR_IVB(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR_IVB) +#define CURBASE_IVB(pipe) _PIPE(pipe, _CURABASE, _CURBBASE_IVB) +#define CURPOS_IVB(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS_IVB) + /* Display A control */ #define _DSPACNTR 0x70180 #define DISPLAY_PLANE_ENABLE (1<<31) diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 880d44e..2371a8e 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5998,6 +5998,31 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base) I915_WRITE(CURBASE(pipe), base); } +static void ivb_update_cursor(struct drm_crtc *crtc, u32 base) +{ + struct drm_device *dev = crtc->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + int pipe = intel_crtc->pipe; + bool visible = base != 0; + + if (intel_crtc->cursor_visible != visible) { + uint32_t cntl = I915_READ(CURCNTR_IVB(pipe)); + if (base) { + cntl &= ~CURSOR_MODE; + cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE; + } else { + cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE); + cntl |= CURSOR_MODE_DISABLE; + } + I915_WRITE(CURCNTR_IVB(pipe), cntl); + + intel_crtc->cursor_visible = visible; + } + /* and commit changes on next vblank */ + I915_WRITE(CURBASE_IVB(pipe), base); +} + /* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */ static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on) @@ -6045,11 +6070,16 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc, if (!visible && !intel_crtc->cursor_visible) return; - I915_WRITE(CURPOS(pipe), pos); - if (IS_845G(dev) || IS_I865G(dev)) - i845_update_cursor(crtc, base); - else - i9xx_update_cursor(crtc, base); + if (IS_IVYBRIDGE(dev)) { + I915_WRITE(CURPOS_IVB(pipe), pos); + ivb_update_cursor(crtc, base); + } else { + I915_WRITE(CURPOS(pipe), pos); + if (IS_845G(dev) || IS_I865G(dev)) + i845_update_cursor(crtc, base); + else + i9xx_update_cursor(crtc, base); + } if (visible) intel_mark_busy(dev, to_intel_framebuffer(crtc->fb)->obj); -- cgit v0.10.2 From d64311ab4bd8d1c1e984ce3f0e772266dde95380 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 12 Oct 2011 15:01:33 -0700 Subject: drm/i915: fix transcoder PLL select masking Transcoder A will always use PLL A and transcoder B will use PLL B. But transcoder C could use either, so always mask the select bits off before or'ing in a new value. Reported-by: Adam Jackson Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 2371a8e..ed5d4f4 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2906,12 +2906,16 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) /* Be sure PCH DPLL SEL is set */ temp = I915_READ(PCH_DPLL_SEL); - if (pipe == 0 && (temp & TRANSA_DPLL_ENABLE) == 0) + if (pipe == 0) { + temp &= ~(TRANSA_DPLLB_SEL); temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); - else if (pipe == 1 && (temp & TRANSB_DPLL_ENABLE) == 0) + } else if (pipe == 1) { + temp &= ~(TRANSB_DPLLB_SEL); temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); - else if (pipe == 2 && (temp & TRANSC_DPLL_ENABLE) == 0) + } else if (pipe == 2) { + temp &= ~(TRANSC_DPLLB_SEL); temp |= (TRANSC_DPLL_ENABLE | transc_sel); + } I915_WRITE(PCH_DPLL_SEL, temp); } @@ -3077,14 +3081,14 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc) temp = I915_READ(PCH_DPLL_SEL); switch (pipe) { case 0: - temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); + temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL); break; case 1: temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); break; case 2: /* C shares PLL A or B */ - temp &= ~(TRANSC_DPLL_ENABLE | TRANSB_DPLLB_SEL); + temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); break; default: BUG(); /* wtf */ @@ -5590,6 +5594,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, temp |= TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL; break; case 2: + temp &= ~(TRANSC_DPLLB_SEL); temp |= TRANSC_DPLL_ENABLE | transc_sel; break; default: -- cgit v0.10.2 From d4270e57efe9e2536798c59e1ed2fd0a1e5cdfcf Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 11 Oct 2011 10:43:02 -0700 Subject: drm/i915: export a CPT mode set verification function At the point where we check, we can't do much about the failure, but it can aid debugging. Note that the auto-train override bit will be reset as part of normal mode setting with this patch if a pipe ever does get stuck, but that's consistent with the workaround for CPT provided by the hardware team. This patch helped catch the fact that the pipe wasn't running in the !composite sync FDI case on my IVB SDV, so has already shown to be useful. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ed5d4f4..ad3a018 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -2971,6 +2971,24 @@ static void ironlake_pch_enable(struct drm_crtc *crtc) intel_enable_transcoder(dev_priv, pipe); } +void intel_cpt_verify_modeset(struct drm_device *dev, int pipe) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + int dslreg = PIPEDSL(pipe), tc2reg = TRANS_CHICKEN2(pipe); + u32 temp; + + temp = I915_READ(dslreg); + udelay(500); + if (wait_for(I915_READ(dslreg) != temp, 5)) { + /* Without this, mode sets may fail silently on FDI */ + I915_WRITE(tc2reg, TRANS_AUTOTRAIN_GEN_STALL_DIS); + udelay(250); + I915_WRITE(tc2reg, 0); + if (wait_for(I915_READ(dslreg) != temp, 5)) + DRM_ERROR("mode set failed: pipe %d stuck\n", pipe); + } +} + static void ironlake_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -3340,8 +3358,15 @@ void intel_encoder_prepare(struct drm_encoder *encoder) void intel_encoder_commit(struct drm_encoder *encoder) { struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + struct drm_device *dev = encoder->dev; + struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + struct intel_crtc *intel_crtc = to_intel_crtc(intel_encoder->base.crtc); + /* lvds has its own version of commit see intel_lvds_commit */ encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); + + if (HAS_PCH_CPT(dev)) + intel_cpt_verify_modeset(dev, intel_crtc->pipe); } void intel_encoder_destroy(struct drm_encoder *encoder) diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 3009d2a..74c8352 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1191,6 +1191,8 @@ static void intel_dp_prepare(struct drm_encoder *encoder) static void intel_dp_commit(struct drm_encoder *encoder) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct drm_device *dev = encoder->dev; + struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc); ironlake_edp_panel_vdd_on(intel_dp); intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON); @@ -1202,6 +1204,9 @@ static void intel_dp_commit(struct drm_encoder *encoder) ironlake_edp_backlight_on(intel_dp); intel_dp->dpms_mode = DRM_MODE_DPMS_ON; + + if (HAS_PCH_CPT(dev)) + intel_cpt_verify_modeset(dev, intel_crtc->pipe); } static void diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 5829854..51b1d75 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -382,4 +382,6 @@ extern void intel_fb_restore_mode(struct drm_device *dev); extern void intel_init_clock_gating(struct drm_device *dev); extern void intel_write_eld(struct drm_encoder *encoder, struct drm_display_mode *mode); +extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe); + #endif /* __INTEL_DRV_H__ */ -- cgit v0.10.2 From d6c892df7e98b4fbd78f2365611935afbf3736d7 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 12 Oct 2011 15:36:42 -0700 Subject: drm/i915: set watermarks for third pipe on IVB The watermark reg for the third pipe is in an unusual offset; add support for it and set watermarks for 3 pipe configs. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index c7ef323..5a09416 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -2435,6 +2435,7 @@ #define WM0_PIPE_CURSOR_MASK (0x1f) #define WM0_PIPEB_ILK 0x45104 +#define WM0_PIPEC_IVB 0x45200 #define WM1_LP_ILK 0x45108 #define WM1_LP_SR_EN (1<<31) #define WM1_LP_LATENCY_SHIFT 24 diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index ad3a018..064c659 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -4540,6 +4540,20 @@ static void sandybridge_update_wm(struct drm_device *dev) enabled |= 2; } + /* IVB has 3 pipes */ + if (IS_IVYBRIDGE(dev) && + g4x_compute_wm0(dev, 2, + &sandybridge_display_wm_info, latency, + &sandybridge_cursor_wm_info, latency, + &plane_wm, &cursor_wm)) { + I915_WRITE(WM0_PIPEC_IVB, + (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm); + DRM_DEBUG_KMS("FIFO watermarks For pipe C -" + " plane %d, cursor: %d\n", + plane_wm, cursor_wm); + enabled |= 3; + } + /* * Calculate and update the self-refresh watermark only when one * display plane is used. -- cgit v0.10.2 From 4c9c18c29347a8bfce1dcd28271bf782aab16639 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 13 Oct 2011 09:46:32 -0700 Subject: drm/i915: if transcoder disable fails, say which Just some extra debug output. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 064c659..921253c 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1276,7 +1276,7 @@ static void intel_disable_transcoder(struct drm_i915_private *dev_priv, I915_WRITE(reg, val); /* wait for PCH transcoder off, transcoder state */ if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50)) - DRM_ERROR("failed to disable transcoder\n"); + DRM_ERROR("failed to disable transcoder %d\n", pipe); } /** -- cgit v0.10.2 From a487928908226df493a3ce145ecf4bb39296714e Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 13 Oct 2011 10:08:34 -0700 Subject: drm/i915: remove transcoder PLL mashing from mode_set per specs Belongs in PCH enable instead. The duplication is worrying and the specs explicitly list transcoder select *after* actual PLL enable, which doesn't occur until later. Signed-off-by: Jesse Barnes Tested-By: Eugeni Dodonov Reviewed-By: Eugeni Dodonov Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 921253c..981b1f1 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5620,31 +5620,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc, } } - /* enable transcoder DPLL */ - if (HAS_PCH_CPT(dev)) { - u32 transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL : - TRANSC_DPLLB_SEL; - temp = I915_READ(PCH_DPLL_SEL); - switch (pipe) { - case 0: - temp |= TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL; - break; - case 1: - temp |= TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL; - break; - case 2: - temp &= ~(TRANSC_DPLLB_SEL); - temp |= TRANSC_DPLL_ENABLE | transc_sel; - break; - default: - BUG(); - } - I915_WRITE(PCH_DPLL_SEL, temp); - - POSTING_READ(PCH_DPLL_SEL); - udelay(150); - } - /* The LVDS pin pair needs to be on before the DPLLs are enabled. * This is an exception to the general rule that mode_set doesn't turn * things on. -- cgit v0.10.2 From f700088333c5c7e5a7f4ab71b642362290259e26 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 13 Oct 2011 16:08:51 -0700 Subject: i915: Move i915_read/write out of line With the tracing code in there they are far too big to inline. .text savings compared to a non force inline kernel: i915_restore_display 4393 12036 +7643 i915_save_display 4295 11459 +7164 i915_handle_error 2979 6666 +3687 i915_driver_irq_handler 2923 5086 +2163 i915_ringbuffer_info 458 1661 +1203 i915_save_vga - 1200 +1200 i915_driver_irq_uninstall 453 1624 +1171 i915_driver_irq_postinstall 913 2078 +1165 ironlake_enable_drps 719 1872 +1153 i915_restore_vga - 1142 +1142 intel_display_capture_error_state 784 2030 +1246 intel_init_emon 719 2016 +1297 and more ... [AK: these are older numbers, with the new SNB forcewake checks it will be even worse] Signed-off-by: Andi Kleen Reviewed-by: Daniel Vetter Acked-by: Ben Widawsky Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 74f2cdb..4c8d681 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -898,3 +898,43 @@ module_exit(i915_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL and additional rights"); + +/* We give fast paths for the really cool registers */ +#define NEEDS_FORCE_WAKE(dev_priv, reg) \ + (((dev_priv)->info->gen >= 6) && \ + ((reg) < 0x40000) && \ + ((reg) != FORCEWAKE)) + +#define __i915_read(x, y) \ +u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ + u##x val = 0; \ + if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ + gen6_gt_force_wake_get(dev_priv); \ + val = read##y(dev_priv->regs + reg); \ + gen6_gt_force_wake_put(dev_priv); \ + } else { \ + val = read##y(dev_priv->regs + reg); \ + } \ + trace_i915_reg_rw(false, reg, val, sizeof(val)); \ + return val; \ +} + +__i915_read(8, b) +__i915_read(16, w) +__i915_read(32, l) +__i915_read(64, q) +#undef __i915_read + +#define __i915_write(x, y) \ +void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ + trace_i915_reg_rw(true, reg, val, sizeof(val)); \ + if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ + __gen6_gt_wait_for_fifo(dev_priv); \ + } \ + write##y(val, dev_priv->regs + reg); \ +} +__i915_write(8, b) +__i915_write(16, w) +__i915_write(32, l) +__i915_write(64, q) +#undef __i915_write diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index c5ca0d3..06a37f4 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1355,18 +1355,7 @@ void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv); ((reg) != FORCEWAKE)) #define __i915_read(x, y) \ -static inline u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ - u##x val = 0; \ - if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ - gen6_gt_force_wake_get(dev_priv); \ - val = read##y(dev_priv->regs + reg); \ - gen6_gt_force_wake_put(dev_priv); \ - } else { \ - val = read##y(dev_priv->regs + reg); \ - } \ - trace_i915_reg_rw(false, reg, val, sizeof(val)); \ - return val; \ -} + u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg); __i915_read(8, b) __i915_read(16, w) @@ -1375,13 +1364,8 @@ __i915_read(64, q) #undef __i915_read #define __i915_write(x, y) \ -static inline void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ - trace_i915_reg_rw(true, reg, val, sizeof(val)); \ - if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ - __gen6_gt_wait_for_fifo(dev_priv); \ - } \ - write##y(val, dev_priv->regs + reg); \ -} + void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val); + __i915_write(8, b) __i915_write(16, w) __i915_write(32, l) -- cgit v0.10.2 From f52c619a590fa75276c07dfcaf380dee53e4ea4c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Oct 2011 11:45:40 +0200 Subject: drm/i915/panel: Always record the backlight level again (but cleverly) The commit 47356eb67285014527a5ab87543ba1fae3d1e10a introduced a mechanism to record the backlight level only at disabling time, but it also introduced a regression. Since intel_lvds_enable() may be called without disabling (e.g. intel_lvds_commit() calls it unconditionally), the backlight gets back to the last recorded value. For example, this happens when you dim the backlight, close the lid and open the lid, then the backlight suddenly goes to the brightest. This patch fixes the bug by recording the backlight level always when changed via intel_panel_set_backlight(). And, intel_panel_{enable|disable}_backlight() call the internal function not to update the recorded level wrongly. Cc: Signed-off-by: Takashi Iwai Reviewed-by: Keith Packard Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 03500e9..499d4c0 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -226,7 +226,7 @@ static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level) I915_WRITE(BLC_PWM_CPU_CTL, val | level); } -void intel_panel_set_backlight(struct drm_device *dev, u32 level) +static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level) { struct drm_i915_private *dev_priv = dev->dev_private; u32 tmp; @@ -254,16 +254,21 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level) I915_WRITE(BLC_PWM_CTL, tmp | level); } -void intel_panel_disable_backlight(struct drm_device *dev) +void intel_panel_set_backlight(struct drm_device *dev, u32 level) { struct drm_i915_private *dev_priv = dev->dev_private; - if (dev_priv->backlight_enabled) { - dev_priv->backlight_level = intel_panel_get_backlight(dev); - dev_priv->backlight_enabled = false; - } + dev_priv->backlight_level = level; + if (dev_priv->backlight_enabled) + intel_panel_actually_set_backlight(dev, level); +} + +void intel_panel_disable_backlight(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; - intel_panel_set_backlight(dev, 0); + dev_priv->backlight_enabled = false; + intel_panel_actually_set_backlight(dev, 0); } void intel_panel_enable_backlight(struct drm_device *dev) @@ -273,8 +278,8 @@ void intel_panel_enable_backlight(struct drm_device *dev) if (dev_priv->backlight_level == 0) dev_priv->backlight_level = intel_panel_get_max_backlight(dev); - intel_panel_set_backlight(dev, dev_priv->backlight_level); dev_priv->backlight_enabled = true; + intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); } static void intel_panel_init_backlight(struct drm_device *dev) -- cgit v0.10.2 From cd9dde44f47501394b9f0715b6a36a92aa74c0d0 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Fri, 14 Oct 2011 12:43:49 -0400 Subject: drm/i915/dp: Fix the math in intel_dp_link_required The previous code was confused about units, which is pretty reasonable given that the units themselves are confusing. Signed-off-by: Adam Jackson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 74c8352..ffe98ad 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -184,9 +184,25 @@ intel_dp_link_clock(uint8_t link_bw) return 162000; } -/* I think this is a fiction */ +/* + * The units on the numbers in the next two are... bizarre. Examples will + * make it clearer; this one parallels an example in the eDP spec. + * + * intel_dp_max_data_rate for one lane of 2.7GHz evaluates as: + * + * 270000 * 1 * 8 / 10 == 216000 + * + * The actual data capacity of that configuration is 2.16Gbit/s, so the + * units are decakilobits. ->clock in a drm_display_mode is in kilohertz - + * or equivalently, kilopixels per second - so for 1680x1050R it'd be + * 119000. At 18bpp that's 2142000 kilobits per second. + * + * Thus the strange-looking division by 10 in intel_dp_link_required, to + * get the result in decakilobits instead of kilobits. + */ + static int -intel_dp_link_required(struct drm_device *dev, struct intel_dp *intel_dp, int pixel_clock) +intel_dp_link_required(struct intel_dp *intel_dp, int pixel_clock) { struct drm_crtc *crtc = intel_dp->base.base.crtc; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); @@ -195,7 +211,7 @@ intel_dp_link_required(struct drm_device *dev, struct intel_dp *intel_dp, int pi if (intel_crtc) bpp = intel_crtc->bpp; - return (pixel_clock * bpp + 7) / 8; + return (pixel_clock * bpp + 9) / 10; } static int @@ -223,7 +239,7 @@ intel_dp_mode_valid(struct drm_connector *connector, /* only refuse the mode on non eDP since we have seen some weird eDP panels which are outside spec tolerances but somehow work by magic */ if (!is_edp(intel_dp) && - (intel_dp_link_required(connector->dev, intel_dp, mode->clock) + (intel_dp_link_required(intel_dp, mode->clock) > intel_dp_max_data_rate(max_link_clock, max_lanes))) return MODE_CLOCK_HIGH; @@ -670,7 +686,7 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, for (clock = 0; clock <= max_clock; clock++) { int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count); - if (intel_dp_link_required(encoder->dev, intel_dp, mode->clock) + if (intel_dp_link_required(intel_dp, mode->clock) <= link_avail) { intel_dp->link_bw = bws[clock]; intel_dp->lane_count = lane_count; -- cgit v0.10.2 From dc22ee6fc18ce0f15424e753e8473c306ece95c1 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Fri, 14 Oct 2011 12:43:50 -0400 Subject: drm/i915/dp: Remove eDP special cases from bandwidth checks These were just working around the math being wrong. Signed-off-by: Adam Jackson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index ffe98ad..8139394 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -236,11 +236,8 @@ intel_dp_mode_valid(struct drm_connector *connector, return MODE_PANEL; } - /* only refuse the mode on non eDP since we have seen some weird eDP panels - which are outside spec tolerances but somehow work by magic */ - if (!is_edp(intel_dp) && - (intel_dp_link_required(intel_dp, mode->clock) - > intel_dp_max_data_rate(max_link_clock, max_lanes))) + if (intel_dp_link_required(intel_dp, mode->clock) + > intel_dp_max_data_rate(max_link_clock, max_lanes)) return MODE_CLOCK_HIGH; if (mode->clock < 10000) @@ -700,19 +697,6 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, } } - if (is_edp(intel_dp)) { - /* okay we failed just pick the highest */ - intel_dp->lane_count = max_lane_count; - intel_dp->link_bw = bws[max_clock]; - adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw); - DRM_DEBUG_KMS("Force picking display port link bw %02x lane " - "count %d clock %d\n", - intel_dp->link_bw, intel_dp->lane_count, - adjusted_mode->clock); - - return true; - } - return false; } -- cgit v0.10.2 From a2006cf5a7ad3463e7c1e9da2c4bc90499427558 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 22 Sep 2011 11:15:58 +0530 Subject: drm/i915: read full receiver capability field during DP hot plug Read link status first, followed by the full DPCD receiver cap field rather than just the first 8 bytes. Signed-off-by: Jesse Barnes Reviewed-by: Adam Jackson Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 8139394..e0ff908 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -36,7 +36,7 @@ #include "i915_drv.h" #include "drm_dp_helper.h" - +#define DP_RECEIVER_CAP_SIZE 0xf #define DP_LINK_STATUS_SIZE 6 #define DP_LINK_CHECK_TIMEOUT (10 * 1000) @@ -53,7 +53,7 @@ struct intel_dp { int dpms_mode; uint8_t link_bw; uint8_t lane_count; - uint8_t dpcd[8]; + uint8_t dpcd[DP_RECEIVER_CAP_SIZE]; struct i2c_adapter adapter; struct i2c_algo_dp_aux_data algo; bool is_pch_edp; -- cgit v0.10.2 From a60f0e38d72a5e24085d6e7e27a4cadc20ae268a Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 20 Oct 2011 15:09:17 -0700 Subject: drm/i915: add DP test request handling DPCD 1.1+ adds some automated test infrastructure support. Add support for reading the IRQ source and jumping to a test handling routine if needed. Subsequent patches will handle particular tests; this patch just ACKs any requested tests by default. Signed-off-by: Jesse Barnes Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index e0ff908..58c827b 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -1776,6 +1776,27 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) return false; } +static bool +intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector) +{ + int ret; + + ret = intel_dp_aux_native_read_retry(intel_dp, + DP_DEVICE_SERVICE_IRQ_VECTOR, + sink_irq_vector, 1); + if (!ret) + return false; + + return true; +} + +static void +intel_dp_handle_test_request(struct intel_dp *intel_dp) +{ + /* NAK by default */ + intel_dp_aux_native_write_1(intel_dp, DP_TEST_RESPONSE, DP_TEST_ACK); +} + /* * According to DP spec * 5.1.2: @@ -1788,6 +1809,8 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) static void intel_dp_check_link_status(struct intel_dp *intel_dp) { + u8 sink_irq_vector; + if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON) return; @@ -1806,6 +1829,20 @@ intel_dp_check_link_status(struct intel_dp *intel_dp) return; } + /* Try to read the source of the interrupt */ + if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 && + intel_dp_get_sink_irq(intel_dp, &sink_irq_vector)) { + /* Clear interrupt source */ + intel_dp_aux_native_write_1(intel_dp, + DP_DEVICE_SERVICE_IRQ_VECTOR, + sink_irq_vector); + + if (sink_irq_vector & DP_AUTOMATED_TEST_REQUEST) + intel_dp_handle_test_request(intel_dp); + if (sink_irq_vector & (DP_CP_IRQ | DP_SINK_SPECIFIC_IRQ)) + DRM_DEBUG_DRIVER("CP or sink specific irq unhandled\n"); + } + if (!intel_channel_eq_ok(intel_dp)) { DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n", drm_get_encoder_name(&intel_dp->base.base)); diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 2b1a358..0d2f727 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -153,6 +153,12 @@ # define DP_PSR_CRC_VERIFICATION (1 << 2) # define DP_PSR_FRAME_CAPTURE (1 << 3) +#define DP_DEVICE_SERVICE_IRQ_VECTOR 0x201 +# define DP_REMOTE_CONTROL_COMMAND_PENDING (1 << 0) +# define DP_AUTOMATED_TEST_REQUEST (1 << 1) +# define DP_CP_IRQ (1 << 2) +# define DP_SINK_SPECIFIC_IRQ (1 << 6) + #define DP_LANE0_1_STATUS 0x202 #define DP_LANE2_3_STATUS 0x203 # define DP_LANE_CR_DONE (1 << 0) @@ -185,6 +191,25 @@ # define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0 # define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6 +#define DP_TEST_REQUEST 0x218 +# define DP_TEST_LINK_TRAINING (1 << 0) +# define DP_TEST_LINK_PATTERN (1 << 1) +# define DP_TEST_LINK_EDID_READ (1 << 2) +# define DP_TEST_LINK_PHY_TEST_PATTERN (1 << 3) /* DPCD >= 1.1 */ + +#define DP_TEST_LINK_RATE 0x219 +# define DP_LINK_RATE_162 (0x6) +# define DP_LINK_RATE_27 (0xa) + +#define DP_TEST_LANE_COUNT 0x220 + +#define DP_TEST_PATTERN 0x221 + +#define DP_TEST_RESPONSE 0x260 +# define DP_TEST_ACK (1 << 0) +# define DP_TEST_NAK (1 << 1) +# define DP_TEST_EDID_CHECKSUM_WRITE (1 << 2) + #define DP_SET_POWER 0x600 # define DP_SET_POWER_D0 0x1 # define DP_SET_POWER_D3 0x2 -- cgit v0.10.2 From 64a8fc0145a1d0fdc25fc9367c2e6c621955fb3b Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 22 Sep 2011 11:16:00 +0530 Subject: drm/i915: fix ILK+ infoframe support Misc fixes based on tests with an infoframe analyzer: - checksum *does* include header bytes - DIP enable & AVI infoframe are tied together in hw, so disable both and make sure AVI frames are enabled first - use every vsync flag for SPD frames to avoid reserved value in frequency field when enabling both AVI & SPD Fixes https://bugs.freedesktop.org/show_bug.cgi?id=40281. Signed-off-by: Jesse Barnes Cc: stable@kernel.org Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index e7e716c..d4f5a0b 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -69,8 +69,7 @@ void intel_dip_infoframe_csum(struct dip_infoframe *frame) frame->checksum = 0; frame->ecc = 0; - /* Header isn't part of the checksum */ - for (i = 5; i < frame->len; i++) + for (i = 0; i < frame->len + DIP_HEADER_SIZE; i++) sum += data[i]; frame->checksum = 0x100 - sum; @@ -104,7 +103,7 @@ static u32 intel_infoframe_flags(struct dip_infoframe *frame) flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC; break; case DIP_TYPE_SPD: - flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC; + flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_VSYNC; break; default: DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); @@ -165,9 +164,9 @@ static void ironlake_write_infoframe(struct drm_encoder *encoder, flags = intel_infoframe_index(frame); - val &= ~VIDEO_DIP_SELECT_MASK; + val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */ - I915_WRITE(reg, val | flags); + I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); for (i = 0; i < len; i += 4) { I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); @@ -487,6 +486,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) struct intel_encoder *intel_encoder; struct intel_connector *intel_connector; struct intel_hdmi *intel_hdmi; + int i; intel_hdmi = kzalloc(sizeof(struct intel_hdmi), GFP_KERNEL); if (!intel_hdmi) @@ -539,10 +539,14 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) intel_hdmi->sdvox_reg = sdvox_reg; - if (!HAS_PCH_SPLIT(dev)) + if (!HAS_PCH_SPLIT(dev)) { intel_hdmi->write_infoframe = i9xx_write_infoframe; - else + I915_WRITE(VIDEO_DIP_CTL, 0); + } else { intel_hdmi->write_infoframe = ironlake_write_infoframe; + for_each_pipe(i) + I915_WRITE(TVIDEO_DIP_CTL(i), 0); + } drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); -- cgit v0.10.2 From 260052100ef669b5374f72055379adc5da35034b Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 22 Sep 2011 11:16:01 +0530 Subject: drm/i915: use correct SPD type value SPD frames are actually type 0x83, not just 0x3. Signed-off-by: Jesse Barnes Cc: stable@kernel.org Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 51b1d75..bd9a604 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -187,7 +187,7 @@ struct intel_crtc { #define DIP_VERSION_AVI 0x2 #define DIP_LEN_AVI 13 -#define DIP_TYPE_SPD 0x3 +#define DIP_TYPE_SPD 0x83 #define DIP_VERSION_SPD 0x1 #define DIP_LEN_SPD 25 #define DIP_SPD_UNKNOWN 0 -- cgit v0.10.2 From 1c95822afebae625f48ebabfc470cdbb50671fd5 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Fri, 14 Oct 2011 17:22:25 -0400 Subject: drm/i915/dp: Introduce is_cpu_edp() The obvious counterpart to is_pch_edp(). Convert existing instances of the idiom to the new routine. Signed-off-by: Adam Jackson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 58c827b..eba1ba5 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -95,6 +95,17 @@ static bool is_pch_edp(struct intel_dp *intel_dp) return intel_dp->is_pch_edp; } +/** + * is_cpu_edp - is the port on the CPU and attached to an eDP panel? + * @intel_dp: DP struct + * + * Returns true if the given DP struct corresponds to a CPU eDP port. + */ +static bool is_cpu_edp(struct intel_dp *intel_dp) +{ + return is_edp(intel_dp) && !is_pch_edp(intel_dp); +} + static struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder) { return container_of(encoder, struct intel_dp, base.base); @@ -355,7 +366,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp, * Note that PCH attached eDP panels should use a 125MHz input * clock divider. */ - if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) { + if (is_cpu_edp(intel_dp)) { if (IS_GEN6(dev)) aux_clock_divider = 200; /* SNB eDP input clock at 400Mhz */ else @@ -859,7 +870,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, if (intel_crtc->pipe == 1 && !HAS_PCH_CPT(dev)) intel_dp->DP |= DP_PIPEB_SELECT; - if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) { + if (is_cpu_edp(intel_dp)) { /* don't miss out required setting for eDP */ intel_dp->DP |= DP_PLL_ENABLE; if (adjusted_mode->clock < 200000) -- cgit v0.10.2 From 82d165557ef094d4b4dfc05871aee618ec7102b0 Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Fri, 14 Oct 2011 17:22:26 -0400 Subject: drm/i915/dp: Fix eDP on PCH DP on CPT/PPT According to the gen6 docs, only the DP_A port (on-CPU eDP) still uses the old IBX bit shift for the link training pattern setup bits. Signed-off-by: Adam Jackson Reviewed-by: Jesse Barnes Signed-off-by: Keith Packard diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index eba1ba5..fc1a083 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -829,7 +829,7 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) intel_dp->DP |= DP_SYNC_VS_HIGH; - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) + if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT; else intel_dp->DP |= DP_LINK_TRAIN_OFF; @@ -1558,7 +1558,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) DP_LINK_CONFIGURATION_SIZE); DP |= DP_PORT_EN; - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) + if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) DP &= ~DP_LINK_TRAIN_MASK_CPT; else DP &= ~DP_LINK_TRAIN_MASK; @@ -1577,7 +1577,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp) DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) + if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) reg = DP | DP_LINK_TRAIN_PAT_1_CPT; else reg = DP | DP_LINK_TRAIN_PAT_1; @@ -1652,7 +1652,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels; } - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) + if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) reg = DP | DP_LINK_TRAIN_PAT_2_CPT; else reg = DP | DP_LINK_TRAIN_PAT_2; @@ -1693,7 +1693,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp) ++tries; } - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) + if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) reg = DP | DP_LINK_TRAIN_OFF_CPT; else reg = DP | DP_LINK_TRAIN_OFF; @@ -1723,7 +1723,7 @@ intel_dp_link_down(struct intel_dp *intel_dp) udelay(100); } - if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) { + if (HAS_PCH_CPT(dev) && !is_cpu_edp(intel_dp)) { DP &= ~DP_LINK_TRAIN_MASK_CPT; I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT); } else { -- cgit v0.10.2