drm/i915: hw state readout and cross-checking for shared dplls

Just the plumbing, all the modeset and enable code has not yet been
switched over to use the new state. It seems to be decently broken
anyway, at least wrt to handling of the special pixel mutliplier
enabling sequence. Follow-up patches will clean up that mess.

Another missing piece is more careful handling (and fixup) of the fp1
alternate divisor state. The BIOS most likely doesn't bother to
program that one to what we expect. So we need to be more careful with
comparing that state, both for cross checking but also when checking
for dpll sharing when acquiring shared dpll. Otherwise fastboot will
deny a few shared dpll configurations which would otherwise work.

v2: We need to memcpy the pipe config dpll hw state into the pll, for
otherwise the cross-check code will get angry.

v3: Don't forget to read the pch pll state in the crtc get_pipe_config
function for ibx/ilk platforms.

Reviewed-by: Damien Lespiau <damien.lespiau@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index cb724b6..5b2fd90 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -3087,7 +3087,11 @@
 	crtc->config.shared_dpll = i;
 	DRM_DEBUG_DRIVER("using %s for pipe %c\n", pll->name,
 			 pipe_name(crtc->pipe));
+
 	if (pll->active == 0) {
+		memcpy(&pll->hw_state, &crtc->config.dpll_hw_state,
+		       sizeof(pll->hw_state));
+
 		DRM_DEBUG_DRIVER("setting up %s\n", pll->name);
 		WARN_ON(pll->on);
 		assert_shared_dpll_disabled(dev_priv, pll);
@@ -5718,6 +5722,13 @@
 					     &fp, &reduced_clock,
 					     has_reduced_clock ? &fp2 : NULL);
 
+		intel_crtc->config.dpll_hw_state.dpll = dpll | DPLL_VCO_ENABLE;
+		intel_crtc->config.dpll_hw_state.fp0 = fp;
+		if (has_reduced_clock)
+			intel_crtc->config.dpll_hw_state.fp1 = fp2;
+		else
+			intel_crtc->config.dpll_hw_state.fp1 = fp;
+
 		pll = intel_get_shared_dpll(intel_crtc, dpll, fp);
 		if (pll == NULL) {
 			DRM_DEBUG_DRIVER("failed to find PLL for pipe %c\n",
@@ -5837,6 +5848,8 @@
 		return false;
 
 	if (I915_READ(PCH_TRANSCONF(crtc->pipe)) & TRANS_ENABLE) {
+		struct intel_shared_dpll *pll;
+
 		pipe_config->has_pch_encoder = true;
 
 		tmp = I915_READ(FDI_RX_CTL(crtc->pipe));
@@ -5858,6 +5871,11 @@
 			else
 				pipe_config->shared_dpll = DPLL_ID_PCH_PLL_A;
 		}
+
+		pll = &dev_priv->shared_dplls[pipe_config->shared_dpll];
+
+		WARN_ON(!pll->get_hw_state(dev_priv, pll,
+					   &pipe_config->dpll_hw_state));
 	} else {
 		pipe_config->pixel_multiplier = 1;
 	}
@@ -8054,6 +8072,15 @@
 			  struct intel_crtc_config *current_config,
 			  struct intel_crtc_config *pipe_config)
 {
+#define PIPE_CONF_CHECK_X(name)	\
+	if (current_config->name != pipe_config->name) { \
+		DRM_ERROR("mismatch in " #name " " \
+			  "(expected 0x%08x, found 0x%08x)\n", \
+			  current_config->name, \
+			  pipe_config->name); \
+		return false; \
+	}
+
 #define PIPE_CONF_CHECK_I(name)	\
 	if (current_config->name != pipe_config->name) { \
 		DRM_ERROR("mismatch in " #name " " \
@@ -8130,7 +8157,11 @@
 	PIPE_CONF_CHECK_I(ips_enabled);
 
 	PIPE_CONF_CHECK_I(shared_dpll);
+	PIPE_CONF_CHECK_X(dpll_hw_state.dpll);
+	PIPE_CONF_CHECK_X(dpll_hw_state.fp0);
+	PIPE_CONF_CHECK_X(dpll_hw_state.fp1);
 
+#undef PIPE_CONF_CHECK_X
 #undef PIPE_CONF_CHECK_I
 #undef PIPE_CONF_CHECK_FLAGS
 #undef PIPE_CONF_QUIRK
@@ -8315,6 +8346,10 @@
 		WARN(pll->refcount != enabled_crtcs,
 		     "pll enabled crtcs mismatch (expected %i, found %i)\n",
 		     pll->refcount, enabled_crtcs);
+
+		WARN(pll->on && memcmp(&pll->hw_state, &dpll_hw_state,
+				       sizeof(dpll_hw_state)),
+		     "pll hw state mismatch\n");
 	}
 }
 
@@ -8755,6 +8790,9 @@
 	uint32_t val;
 
 	val = I915_READ(PCH_DPLL(pll->id));
+	hw_state->dpll = val;
+	hw_state->fp0 = I915_READ(PCH_FP0(pll->id));
+	hw_state->fp1 = I915_READ(PCH_FP1(pll->id));
 
 	return val & DPLL_VCO_ENABLE;
 }