aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_display.c
diff options
context:
space:
mode:
authorImre Deak <imre.deak@intel.com>2016-12-05 11:27:37 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-01-15 07:42:57 -0500
commit3f41ee3a45cb3b2a458e7c4aa69c0638fd745ad2 (patch)
treee3be44cb25baa0e09491321d64983acc6c342bdb /drivers/gpu/drm/i915/intel_display.c
parent93f2976eb0276c932b22da771c90b9b4cd52ee49 (diff)
drm/i915/gen9: Fix PCODE polling during CDCLK change notification
commit 2c7d0602c815277f7cb7c932b091288710d8aba7 upstream. commit 848496e5902833600f7992f4faa82dc1546051ba Author: Ville Syrjälä <ville.syrjala@linux.intel.com> Date: Wed Jul 13 16:32:03 2016 +0300 drm/i915: Wait up to 3ms for the pcu to ack the cdclk change request on SKL increased the timeout to match the spec, but we still see a timeout on at least one SKL. A CDCLK change request following the failed one will succeed nevertheless. I could reproduce this problem easily by running kms_pipe_crc_basic in a loop. In all failure cases _wait_for() was pre-empted for >3ms and so in the worst case - when the pre-emption happened right after calculating timeout__ in _wait_for() - we called skl_cdclk_wait_for_pcu_ready() only once which failed and so _wait_for() timed out. As opposed to this the spec says to keep retrying the request for at most a 3ms period. To fix this send the first request explicitly to guarantee that there is 3ms between the first and last request. Though this matches the spec, I noticed that in rare cases this can still time out if we sent only a few requests (in the worst case 2) _and_ PCODE is busy for some reason even after a previous request and a 3ms delay. To work around this retry the polling with pre-emption disabled to maximize the number of requests. Also increase the timeout to 10ms to account for interrupts that could reduce the number of requests. With this change I couldn't trigger the problem. v2: - Use 1ms poll period instead of 10us. (Chris) v3: - Poll with pre-emption disabled to increase the number of request attempts. (Ville, Chris) - Factor out a helper to poll, it's also needed by the next patch. v4: - Pass reply_mask, reply to skl_pcode_request(), instead of assuming the reply is generic. (Ville) v5: - List the request specific timeout values as code comment. (Ville) v6: - Try the poll first with preemption enabled. - Add code comment about first request being queued by PCODE. (Art) - Add timeout_base_ms argument. (Ville) v7: - Clarify code comment about first queued request. (Chris) Cc: Ville Syrjälä <ville.syrjala@linux.intel.com> Cc: Chris Wilson <chris@chris-wilson.co.uk> Cc: Art Runyan <arthur.j.runyan@intel.com> Cc: <stable@vger.kernel.org> # v4.2- : 3b2c171 : drm/i915: Wait up to 3ms Cc: <stable@vger.kernel.org> # v4.2- Fixes: 5d96d8afcfbb ("drm/i915/skl: Deinit/init the display at suspend/resume") Reference: https://bugs.freedesktop.org/show_bug.cgi?id=97929 Testcase: igt/kms_pipe_crc_basic/suspend-read-crc-pipe-B Signed-off-by: Imre Deak <imre.deak@intel.com> Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk> Link: http://patchwork.freedesktop.org/patch/msgid/1480955258-26311-1-git-send-email-imre.deak@intel.com (cherry picked from commit a0b8a1fe34430c3a82258e8cb45f5968bdf31afd) Signed-off-by: Jani Nikula <jani.nikula@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_display.c')
-rw-r--r--drivers/gpu/drm/i915/intel_display.c31
1 files changed, 10 insertions, 21 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 71441c329603..c9e83f39ec0a 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -6262,36 +6262,25 @@ skl_dpll0_disable(struct drm_i915_private *dev_priv)
6262 dev_priv->cdclk_pll.vco = 0; 6262 dev_priv->cdclk_pll.vco = 0;
6263} 6263}
6264 6264
6265static bool skl_cdclk_pcu_ready(struct drm_i915_private *dev_priv)
6266{
6267 int ret;
6268 u32 val;
6269
6270 /* inform PCU we want to change CDCLK */
6271 val = SKL_CDCLK_PREPARE_FOR_CHANGE;
6272 mutex_lock(&dev_priv->rps.hw_lock);
6273 ret = sandybridge_pcode_read(dev_priv, SKL_PCODE_CDCLK_CONTROL, &val);
6274 mutex_unlock(&dev_priv->rps.hw_lock);
6275
6276 return ret == 0 && (val & SKL_CDCLK_READY_FOR_CHANGE);
6277}
6278
6279static bool skl_cdclk_wait_for_pcu_ready(struct drm_i915_private *dev_priv)
6280{
6281 return _wait_for(skl_cdclk_pcu_ready(dev_priv), 3000, 10) == 0;
6282}
6283
6284static void skl_set_cdclk(struct drm_i915_private *dev_priv, int cdclk, int vco) 6265static void skl_set_cdclk(struct drm_i915_private *dev_priv, int cdclk, int vco)
6285{ 6266{
6286 struct drm_device *dev = &dev_priv->drm; 6267 struct drm_device *dev = &dev_priv->drm;
6287 u32 freq_select, pcu_ack; 6268 u32 freq_select, pcu_ack;
6269 int ret;
6288 6270
6289 WARN_ON((cdclk == 24000) != (vco == 0)); 6271 WARN_ON((cdclk == 24000) != (vco == 0));
6290 6272
6291 DRM_DEBUG_DRIVER("Changing CDCLK to %d kHz (VCO %d kHz)\n", cdclk, vco); 6273 DRM_DEBUG_DRIVER("Changing CDCLK to %d kHz (VCO %d kHz)\n", cdclk, vco);
6292 6274
6293 if (!skl_cdclk_wait_for_pcu_ready(dev_priv)) { 6275 mutex_lock(&dev_priv->rps.hw_lock);
6294 DRM_ERROR("failed to inform PCU about cdclk change\n"); 6276 ret = skl_pcode_request(dev_priv, SKL_PCODE_CDCLK_CONTROL,
6277 SKL_CDCLK_PREPARE_FOR_CHANGE,
6278 SKL_CDCLK_READY_FOR_CHANGE,
6279 SKL_CDCLK_READY_FOR_CHANGE, 3);
6280 mutex_unlock(&dev_priv->rps.hw_lock);
6281 if (ret) {
6282 DRM_ERROR("Failed to inform PCU about cdclk change (%d)\n",
6283 ret);
6295 return; 6284 return;
6296 } 6285 }
6297 6286