drm/i915/bxt: Fix DSI HW state readout

Currently the machine hangs during booting while accessing the
BXT_MIPI_PORT_CTRL register during pipe HW state readout. After some
experimentation I found that the hang is caused by the DSI PLL being
disabled, or it being enabled but with an incorrect divider
configuration. Enabling the PLL got rid of the boot problem, so fix
this by checking the PLL enabled state/configuration before attempting
to read out the HW state.

The DSI_PLL_ENABLE register is in the always-on power well, while the
BXT_DSI_PLL_CTL is in power well 0. This isn't exactly matched by the
transcoder power domain, but what we really need is just a runtime PM
reference, which is provided by any power domain.

Ville also found this dependency specified in BSpec, so I added a
reference to that too.

v2:
- Make sure we hold a power reference while accessing the PLL registers.
v3: (Jani)
- Simplify check in bxt_get_dsi_transcoder_state()
- Add comment explaining why we check for valid dividers in
  bxt_dsi_pll_is_enabled()

CC: Shashank Sharma <shashank.sharma@intel.com>
CC: Uma Shankar <uma.shankar@intel.com>
CC: Jani Nikula <jani.nikula@intel.com>
Fixes: c6c794a2fc ("drm/i915/bxt: Initialize MIPI DSI for BXT")
Signed-off-by: Imre Deak <imre.deak@intel.com>
Reviewed-by: Jani Nikula <jani.nikula@intel.com>
Reviewed-by: Shashank Sharma <shashank.sharma@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1458816100-31269-1-git-send-email-imre.deak@intel.com
This commit is contained in:
Imre Deak 2016-03-24 12:41:40 +02:00
parent 307e449880
commit db18b6a64c
5 changed files with 63 additions and 0 deletions

View File

@ -7811,9 +7811,11 @@ enum skl_disp_power_wells {
#define BXT_DSIC_16X_BY2 (1 << 10) #define BXT_DSIC_16X_BY2 (1 << 10)
#define BXT_DSIC_16X_BY3 (2 << 10) #define BXT_DSIC_16X_BY3 (2 << 10)
#define BXT_DSIC_16X_BY4 (3 << 10) #define BXT_DSIC_16X_BY4 (3 << 10)
#define BXT_DSIC_16X_MASK (3 << 10)
#define BXT_DSIA_16X_BY2 (1 << 8) #define BXT_DSIA_16X_BY2 (1 << 8)
#define BXT_DSIA_16X_BY3 (2 << 8) #define BXT_DSIA_16X_BY3 (2 << 8)
#define BXT_DSIA_16X_BY4 (3 << 8) #define BXT_DSIA_16X_BY4 (3 << 8)
#define BXT_DSIA_16X_MASK (3 << 8)
#define BXT_DSI_FREQ_SEL_SHIFT 8 #define BXT_DSI_FREQ_SEL_SHIFT 8
#define BXT_DSI_FREQ_SEL_MASK (0xF << BXT_DSI_FREQ_SEL_SHIFT) #define BXT_DSI_FREQ_SEL_MASK (0xF << BXT_DSI_FREQ_SEL_SHIFT)

View File

@ -36,6 +36,7 @@
#include "intel_drv.h" #include "intel_drv.h"
#include <drm/i915_drm.h> #include <drm/i915_drm.h>
#include "i915_drv.h" #include "i915_drv.h"
#include "intel_dsi.h"
#include "i915_trace.h" #include "i915_trace.h"
#include <drm/drm_atomic.h> #include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h> #include <drm/drm_atomic_helper.h>
@ -9870,6 +9871,16 @@ static bool bxt_get_dsi_transcoder_state(struct intel_crtc *crtc,
continue; continue;
*power_domain_mask |= BIT(power_domain); *power_domain_mask |= BIT(power_domain);
/*
* The PLL needs to be enabled with a valid divider
* configuration, otherwise accessing DSI registers will hang
* the machine. See BSpec North Display Engine
* registers/MIPI[BXT]. We can break out here early, since we
* need the same DSI PLL to be enabled for both DSI ports.
*/
if (!intel_dsi_pll_is_enabled(dev_priv))
break;
/* XXX: this works for video mode only */ /* XXX: this works for video mode only */
tmp = I915_READ(BXT_MIPI_PORT_CTRL(port)); tmp = I915_READ(BXT_MIPI_PORT_CTRL(port));
if (!(tmp & DPI_ENABLE)) if (!(tmp & DPI_ENABLE))

View File

@ -684,6 +684,14 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
if (!intel_display_power_get_if_enabled(dev_priv, power_domain)) if (!intel_display_power_get_if_enabled(dev_priv, power_domain))
return false; return false;
/*
* On Broxton the PLL needs to be enabled with a valid divider
* configuration, otherwise accessing DSI registers will hang the
* machine. See BSpec North Display Engine registers/MIPI[BXT].
*/
if (IS_BROXTON(dev_priv) && !intel_dsi_pll_is_enabled(dev_priv))
goto out_put_power;
/* XXX: this only works for one DSI output */ /* XXX: this only works for one DSI output */
for_each_dsi_port(port, intel_dsi->ports) { for_each_dsi_port(port, intel_dsi->ports) {
i915_reg_t ctrl_reg = IS_BROXTON(dev) ? i915_reg_t ctrl_reg = IS_BROXTON(dev) ?
@ -726,6 +734,7 @@ static bool intel_dsi_get_hw_state(struct intel_encoder *encoder,
break; break;
} }
out_put_power:
intel_display_power_put(dev_priv, power_domain); intel_display_power_put(dev_priv, power_domain);
return active; return active;

View File

@ -126,6 +126,7 @@ static inline struct intel_dsi *enc_to_intel_dsi(struct drm_encoder *encoder)
return container_of(encoder, struct intel_dsi, base.base); return container_of(encoder, struct intel_dsi, base.base);
} }
bool intel_dsi_pll_is_enabled(struct drm_i915_private *dev_priv);
extern void intel_enable_dsi_pll(struct intel_encoder *encoder); extern void intel_enable_dsi_pll(struct intel_encoder *encoder);
extern void intel_disable_dsi_pll(struct intel_encoder *encoder); extern void intel_disable_dsi_pll(struct intel_encoder *encoder);
extern u32 intel_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp); extern u32 intel_dsi_get_pclk(struct intel_encoder *encoder, int pipe_bpp);

View File

@ -192,6 +192,36 @@ static void vlv_disable_dsi_pll(struct intel_encoder *encoder)
mutex_unlock(&dev_priv->sb_lock); mutex_unlock(&dev_priv->sb_lock);
} }
static bool bxt_dsi_pll_is_enabled(struct drm_i915_private *dev_priv)
{
bool enabled;
u32 val;
u32 mask;
mask = BXT_DSI_PLL_DO_ENABLE | BXT_DSI_PLL_LOCKED;
val = I915_READ(BXT_DSI_PLL_ENABLE);
enabled = (val & mask) == mask;
if (!enabled)
return false;
/*
* Both dividers must be programmed with valid values even if only one
* of the PLL is used, see BSpec/Broxton Clocks. Check this here for
* paranoia, since BIOS is known to misconfigure PLLs in this way at
* times, and since accessing DSI registers with invalid dividers
* causes a system hang.
*/
val = I915_READ(BXT_DSI_PLL_CTL);
if (!(val & BXT_DSIA_16X_MASK) || !(val & BXT_DSIC_16X_MASK)) {
DRM_DEBUG_DRIVER("PLL is enabled with invalid divider settings (%08x)\n",
val);
enabled = false;
}
return enabled;
}
static void bxt_disable_dsi_pll(struct intel_encoder *encoder) static void bxt_disable_dsi_pll(struct intel_encoder *encoder)
{ {
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private; struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
@ -486,6 +516,16 @@ static void bxt_enable_dsi_pll(struct intel_encoder *encoder)
DRM_DEBUG_KMS("DSI PLL locked\n"); DRM_DEBUG_KMS("DSI PLL locked\n");
} }
bool intel_dsi_pll_is_enabled(struct drm_i915_private *dev_priv)
{
if (IS_BROXTON(dev_priv))
return bxt_dsi_pll_is_enabled(dev_priv);
MISSING_CASE(INTEL_DEVID(dev_priv));
return false;
}
void intel_enable_dsi_pll(struct intel_encoder *encoder) void intel_enable_dsi_pll(struct intel_encoder *encoder)
{ {
struct drm_device *dev = encoder->base.dev; struct drm_device *dev = encoder->base.dev;