From mboxrd@z Thu Jan 1 00:00:00 1970 From: Matt Roper Subject: Re: [PATCH v4 1/6] drm/i915/skl: Add support for the SAGV, fix underrun hangs Date: Thu, 28 Jul 2016 06:13:54 -0700 Message-ID: <20160728131354.GI32025@intel.com> References: <1469554483-24999-1-git-send-email-cpaul@redhat.com> <1469554483-24999-2-git-send-email-cpaul@redhat.com> Mime-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: QUOTED-PRINTABLE Return-path: Content-Disposition: inline In-Reply-To: <1469554483-24999-2-git-send-email-cpaul@redhat.com> Sender: stable-owner@vger.kernel.org To: Lyude Cc: intel-gfx@lists.freedesktop.org, Maarten Lankhorst , Ville =?iso-8859-1?Q?Syrj=E4l=E4?= , Daniel Vetter , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, stable@vger.kernel.org, Daniel Vetter List-Id: intel-gfx@lists.freedesktop.org On Tue, Jul 26, 2016 at 01:34:37PM -0400, Lyude wrote: > Since the watermark calculations for Skylake are still broken, we're = apt > to hitting underruns very easily under multi-monitor configurations. > While it would be lovely if this was fixed, it's not. Another problem > that's been coming from this however, is the mysterious issue of > underruns causing full system hangs. An easy way to reproduce this wi= th > a skylake system: >=20 > - Get a laptop with a skylake GPU, and hook up two external monitors = to > it > - Move the cursor from the built-in LCD to one of the external displa= ys > as quickly as you can > - You'll get a few pipe underruns, and eventually the entire system w= ill > just freeze. >=20 > After doing a lot of investigation and reading through the bspec, I > found the existence of the SAGV, which is responsible for adjusting t= he > system agent voltage and clock frequencies depending on how much powe= r > we need. According to the bspec: >=20 > "The display engine access to system memory is blocked during the > adjustment time. SAGV defaults to enabled. Software must use the > GT-driver pcode mailbox to disable SAGV when the display engine is n= ot > able to tolerate the blocking time." >=20 > The rest of the bspec goes on to explain that software can simply lea= ve > the SAGV enabled, and disable it when we use interlaced pipes/have mo= re > then one pipe active. >=20 > Sure enough, with this patchset the system hangs resulting from pipe > underruns on Skylake have completely vanished on my T460s. Additional= ly, > the bspec mentions turning off the SAGV with more then one pipe enabl= ed > as a workaround for display underruns. While this patch doesn't entir= ely > fix that, it looks like it does improve the situation a little bit so > it's likely this is going to be required to make watermarks on Skylak= e > fully functional. >=20 > Changes since v4: > - Use is_power_of_2 against active_crtcs to check whether we have > = 1 > pipe enabled > - Fix skl_sagv_get_hw_state(): (temp & 0x1) indicates disabled, 0x0 > enabled > - Call skl_sagv_enable/disable() from pre/post-plane updates > Changes since v3: > - Use time_before() to compare timeout to jiffies > Changes since v2: > - Really apply minor style nitpicks to patch this time > Changes since v1: > - Added comments about this probably being one of the requirements t= o > fixing Skylake's watermark issues > - Minor style nitpicks from Matt Roper > - Disable these functions on Broxton, since it doesn't have an SAGV >=20 > Reviewed-by: Matt Roper > Signed-off-by: Lyude > Cc: Daniel Vetter > Cc: Ville Syrj=E4l=E4 > Cc: stable@vger.kernel.org > --- > drivers/gpu/drm/i915/i915_drv.h | 2 + > drivers/gpu/drm/i915/i915_reg.h | 5 ++ > drivers/gpu/drm/i915/intel_display.c | 19 +++++++ > drivers/gpu/drm/i915/intel_drv.h | 2 + > drivers/gpu/drm/i915/intel_pm.c | 105 +++++++++++++++++++++++++= ++++++++++ > 5 files changed, 133 insertions(+) >=20 > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i= 915_drv.h > index 9f655e2..1f6fe8c 100644 > --- a/drivers/gpu/drm/i915/i915_drv.h > +++ b/drivers/gpu/drm/i915/i915_drv.h > @@ -1962,6 +1962,8 @@ struct drm_i915_private { > struct i915_suspend_saved_registers regfile; > struct vlv_s0ix_state vlv_s0ix_state; > =20 > + bool skl_sagv_enabled; > + > struct { > /* > * Raw watermark latency values: > diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i= 915_reg.h > index 9397dde..89d146f 100644 > --- a/drivers/gpu/drm/i915/i915_reg.h > +++ b/drivers/gpu/drm/i915/i915_reg.h > @@ -7166,6 +7166,11 @@ enum { > #define HSW_PCODE_DE_WRITE_FREQ_REQ 0x17 > #define DISPLAY_IPS_CONTROL 0x19 > #define HSW_PCODE_DYNAMIC_DUTY_CYCLE_CONTROL 0x1A > +#define GEN9_PCODE_SAGV_CONTROL 0x21 > +#define GEN9_SAGV_DISABLE 0x0 > +#define GEN9_SAGV_LOW_FREQ 0x1 > +#define GEN9_SAGV_HIGH_FREQ 0x2 > +#define GEN9_SAGV_DYNAMIC_FREQ 0x3 > #define GEN6_PCODE_DATA _MMIO(0x138128) > #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 > #define GEN6_PCODE_FREQ_RING_RATIO_SHIFT 16 > diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i= 915/intel_display.c > index 78beb7e..b80c051 100644 > --- a/drivers/gpu/drm/i915/intel_display.c > +++ b/drivers/gpu/drm/i915/intel_display.c > @@ -4562,9 +4562,12 @@ static void intel_post_plane_update(struct int= el_crtc_state *old_crtc_state) > { > struct intel_crtc *crtc =3D to_intel_crtc(old_crtc_state->base.crtc= ); > struct drm_atomic_state *old_state =3D old_crtc_state->base.state; > + struct intel_atomic_state *old_intel_state =3D > + to_intel_atomic_state(old_state); > struct intel_crtc_state *pipe_config =3D > to_intel_crtc_state(crtc->base.state); > struct drm_device *dev =3D crtc->base.dev; > + struct drm_i915_private *dev_priv =3D to_i915(dev); > struct drm_plane *primary =3D crtc->base.primary; > struct drm_plane_state *old_pri_state =3D > drm_atomic_get_existing_plane_state(old_state, primary); > @@ -4589,6 +4592,11 @@ static void intel_post_plane_update(struct int= el_crtc_state *old_crtc_state) > !old_primary_state->visible)) > intel_post_enable_primary(&crtc->base); > } > + > + if (old_intel_state->modeset && > + (old_intel_state->active_crtcs =3D=3D 0 || > + is_power_of_2(old_intel_state->active_crtcs))) We use hweight32 for counting bits elsewhere in the driver, which is a little bit more self-explanatory, but I guess is_power_of_2 works too. > + skl_enable_sagv(dev_priv); > } > =20 > static void intel_pre_plane_update(struct intel_crtc_state *old_crtc= _state) > @@ -4599,6 +4607,8 @@ static void intel_pre_plane_update(struct intel= _crtc_state *old_crtc_state) > struct intel_crtc_state *pipe_config =3D > to_intel_crtc_state(crtc->base.state); > struct drm_atomic_state *old_state =3D old_crtc_state->base.state; > + struct intel_atomic_state *old_intel_state =3D > + to_intel_atomic_state(old_state); > struct drm_plane *primary =3D crtc->base.primary; > struct drm_plane_state *old_pri_state =3D > drm_atomic_get_existing_plane_state(old_state, primary); > @@ -4649,6 +4659,15 @@ static void intel_pre_plane_update(struct inte= l_crtc_state *old_crtc_state) > } > =20 > /* > + * SKL workaround: bspec recommends we disable the SAGV when we hav= e > + * more then one pipe enabled > + */ > + if (old_intel_state->modeset && > + !is_power_of_2(old_intel_state->active_crtcs) && > + old_intel_state->active_crtcs !=3D 0) > + skl_disable_sagv(dev_priv); > + > + /* > * If we're doing a modeset, we're done. No need to do any pre-vbl= ank > * watermark programming here. > */ > diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/= intel_drv.h > index e74d851..113bf48 100644 > --- a/drivers/gpu/drm/i915/intel_drv.h > +++ b/drivers/gpu/drm/i915/intel_drv.h > @@ -1709,6 +1709,8 @@ void ilk_wm_get_hw_state(struct drm_device *dev= ); > void skl_wm_get_hw_state(struct drm_device *dev); > void skl_ddb_get_hw_state(struct drm_i915_private *dev_priv, > struct skl_ddb_allocation *ddb /* out */); > +int skl_enable_sagv(struct drm_i915_private *dev_priv); > +int skl_disable_sagv(struct drm_i915_private *dev_priv); > uint32_t ilk_pipe_pixel_rate(const struct intel_crtc_state *pipe_con= fig); > bool ilk_disable_lp_wm(struct drm_device *dev); > int sanitize_rc6_option(struct drm_i915_private *dev_priv, int enabl= e_rc6); > diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/i= ntel_pm.c > index 64d628c..55a9694 100644 > --- a/drivers/gpu/drm/i915/intel_pm.c > +++ b/drivers/gpu/drm/i915/intel_pm.c > @@ -2876,6 +2876,109 @@ skl_wm_plane_id(const struct intel_plane *pla= ne) > } > =20 > static void > +skl_sagv_get_hw_state(struct drm_i915_private *dev_priv) > +{ > + u32 temp; > + int ret; > + > + if (IS_BROXTON(dev_priv)) > + return; > + > + mutex_lock(&dev_priv->rps.hw_lock); > + ret =3D sandybridge_pcode_read(dev_priv, GEN9_PCODE_SAGV_CONTROL, &= temp); > + mutex_unlock(&dev_priv->rps.hw_lock); > + > + if (!ret) { > + dev_priv->skl_sagv_enabled =3D !(temp & 0x1); > + } else { > + /* > + * If for some reason we can't access the SAGV state, follow > + * the bspec and assume it's enabled > + */ > + DRM_ERROR("Failed to get SAGV state, assuming enabled\n"); > + dev_priv->skl_sagv_enabled =3D true; > + } > +} > + > +/* > + * SAGV dynamically adjusts the system agent voltage and clock frequ= encies > + * depending on power and performance requirements. The display engi= ne access > + * to system memory is blocked during the adjustment time. Having th= is enabled > + * in multi-pipe configurations can cause issues (such as underruns = causing > + * full system hangs), and the bspec also suggests that software dis= able it > + * when more then one pipe is enabled. > + */ > +int > +skl_enable_sagv(struct drm_i915_private *dev_priv) > +{ > + int ret; > + > + if (IS_BROXTON(dev_priv)) > + return 0; > + if (dev_priv->skl_sagv_enabled) > + return 0; > + > + mutex_lock(&dev_priv->rps.hw_lock); > + DRM_DEBUG_KMS("Enabling the SAGV\n"); > + > + ret =3D sandybridge_pcode_write(dev_priv, GEN9_PCODE_SAGV_CONTROL, > + GEN9_SAGV_DYNAMIC_FREQ); > + if (!ret) > + dev_priv->skl_sagv_enabled =3D true; > + else > + DRM_ERROR("Failed to enable the SAGV\n"); > + > + /* We don't need to wait for SAGV when enabling */ > + mutex_unlock(&dev_priv->rps.hw_lock); > + return ret; > +} > + > +int > +skl_disable_sagv(struct drm_i915_private *dev_priv) > +{ > + int ret =3D 0; > + unsigned long timeout; > + u32 temp; > + > + if (IS_BROXTON(dev_priv)) > + return 0; > + if (!dev_priv->skl_sagv_enabled) > + return 0; > + > + mutex_lock(&dev_priv->rps.hw_lock); > + DRM_DEBUG_KMS("Disabling the SAGV\n"); > + > + /* bspec says to keep retrying for at least 1 ms */ > + timeout =3D jiffies + msecs_to_jiffies(1); > + do { > + ret =3D sandybridge_pcode_write(dev_priv, GEN9_PCODE_SAGV_CONTROL, > + GEN9_SAGV_DISABLE); > + if (ret) { > + DRM_ERROR("Failed to disable the SAGV\n"); > + goto out; > + } > + > + ret =3D sandybridge_pcode_read(dev_priv, GEN9_PCODE_SAGV_CONTROL, > + &temp); > + if (ret) { > + DRM_ERROR("Failed to check the status of the SAGV\n"); > + goto out; > + } > + } while (!(temp & 0x1) && time_before(jiffies, timeout)); I think Chris pointed out on a previous iteration that it would be easier / cleaner to move the contents of this loop out to a new functio= n that returns the value of the read and then just call ret =3D wait_for(do_sagv_disable(...), 1); if (ret) DRM_ERROR("Timeout"); Matt > + > + if (temp & 0x1) { > + dev_priv->skl_sagv_enabled =3D false; > + } else { > + ret =3D -1; > + DRM_ERROR("Request to disable SAGV timed out\n"); > + } > + > +out: > + mutex_unlock(&dev_priv->rps.hw_lock); > + return ret; > +} > + > +static void > skl_ddb_get_pipe_allocation_limits(struct drm_device *dev, > const struct intel_crtc_state *cstate, > struct skl_ddb_entry *alloc, /* out */ > @@ -4228,6 +4331,8 @@ void skl_wm_get_hw_state(struct drm_device *dev= ) > /* Easy/common case; just sanitize DDB now if everything off */ > memset(ddb, 0, sizeof(*ddb)); > } > + > + skl_sagv_get_hw_state(dev_priv); > } > =20 > static void ilk_pipe_wm_get_hw_state(struct drm_crtc *crtc) > --=20 > 2.7.4 >=20 > _______________________________________________ > dri-devel mailing list > dri-devel@lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel --=20 Matt Roper Graphics Software Engineer IoTG Platform Enabling & Development Intel Corporation (916) 356-2795