Skip to content

Commit d028a64

Browse files
vsyrjalajnikula
authored andcommitted
drm/i915: Try to sanitize bogus DPLL state left over by broken SNB BIOSen
Certain SNB machines (eg. ASUS K53SV) seem to have a broken BIOS which misprograms the hardware badly when encountering a suitably high resolution display. The programmed pipe timings are somewhat bonkers and the DPLL is totally misprogrammed (P divider == 0). That will result in atomic commit timeouts as apparently the pipe is sufficiently stuck to not signal vblank interrupts. IIRC something like this was also observed on some other SNB machine years ago (might have been a Dell XPS 8300) but a BIOS update cured it. Sadly looks like this was never fixed for the ASUS K53SV as the latest BIOS (K53SV.320 11/11/2011) is still broken. The quickest way to deal with this seems to be to shut down the pipe+ports+DPLL. Unfortunately doing this during the normal sanitization phase isn't quite soon enough as we already spew several WARNs about the bogus hardware state. But it's better than hanging the boot for a few dozen seconds. Since this is limited to a few old machines it doesn't seem entirely worthwile to try and rework the readout+sanitization code to handle it more gracefully. v2: Fix potential NULL deref (kbuild test robot) Constify has_bogus_dpll_config() Cc: stable@vger.kernel.org # v4.20+ Cc: Daniel Kamil Kozar <dkk089@gmail.com> Reported-by: Daniel Kamil Kozar <dkk089@gmail.com> Tested-by: Daniel Kamil Kozar <dkk089@gmail.com> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=109245 Fixes: 516a49c ("drm/i915: Fix assert_plane() warning on bootup with external display") Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190111174950.10681-1-ville.syrjala@linux.intel.com Reviewed-by: Mika Kahola <mika.kahola@intel.com> (cherry picked from commit 7bed8ad) Signed-off-by: Jani Nikula <jani.nikula@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/20190205141846.6053-1-ville.syrjala@linux.intel.com
1 parent 2a12103 commit d028a64

File tree

1 file changed

+44
-6
lines changed

1 file changed

+44
-6
lines changed

drivers/gpu/drm/i915/intel_display.c

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15415,16 +15415,45 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc,
1541515415
}
1541615416
}
1541715417

15418+
static bool has_bogus_dpll_config(const struct intel_crtc_state *crtc_state)
15419+
{
15420+
struct drm_i915_private *dev_priv = to_i915(crtc_state->base.crtc->dev);
15421+
15422+
/*
15423+
* Some SNB BIOSen (eg. ASUS K53SV) are known to misprogram
15424+
* the hardware when a high res displays plugged in. DPLL P
15425+
* divider is zero, and the pipe timings are bonkers. We'll
15426+
* try to disable everything in that case.
15427+
*
15428+
* FIXME would be nice to be able to sanitize this state
15429+
* without several WARNs, but for now let's take the easy
15430+
* road.
15431+
*/
15432+
return IS_GEN6(dev_priv) &&
15433+
crtc_state->base.active &&
15434+
crtc_state->shared_dpll &&
15435+
crtc_state->port_clock == 0;
15436+
}
15437+
1541815438
static void intel_sanitize_encoder(struct intel_encoder *encoder)
1541915439
{
1542015440
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
1542115441
struct intel_connector *connector;
15442+
struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
15443+
struct intel_crtc_state *crtc_state = crtc ?
15444+
to_intel_crtc_state(crtc->base.state) : NULL;
1542215445

1542315446
/* We need to check both for a crtc link (meaning that the
1542415447
* encoder is active and trying to read from a pipe) and the
1542515448
* pipe itself being active. */
15426-
bool has_active_crtc = encoder->base.crtc &&
15427-
to_intel_crtc(encoder->base.crtc)->active;
15449+
bool has_active_crtc = crtc_state &&
15450+
crtc_state->base.active;
15451+
15452+
if (crtc_state && has_bogus_dpll_config(crtc_state)) {
15453+
DRM_DEBUG_KMS("BIOS has misprogrammed the hardware. Disabling pipe %c\n",
15454+
pipe_name(crtc->pipe));
15455+
has_active_crtc = false;
15456+
}
1542815457

1542915458
connector = intel_encoder_find_connector(encoder);
1543015459
if (connector && !has_active_crtc) {
@@ -15435,16 +15464,25 @@ static void intel_sanitize_encoder(struct intel_encoder *encoder)
1543515464
/* Connector is active, but has no active pipe. This is
1543615465
* fallout from our resume register restoring. Disable
1543715466
* the encoder manually again. */
15438-
if (encoder->base.crtc) {
15439-
struct drm_crtc_state *crtc_state = encoder->base.crtc->state;
15467+
if (crtc_state) {
15468+
struct drm_encoder *best_encoder;
1544015469

1544115470
DRM_DEBUG_KMS("[ENCODER:%d:%s] manually disabled\n",
1544215471
encoder->base.base.id,
1544315472
encoder->base.name);
15473+
15474+
/* avoid oopsing in case the hooks consult best_encoder */
15475+
best_encoder = connector->base.state->best_encoder;
15476+
connector->base.state->best_encoder = &encoder->base;
15477+
1544415478
if (encoder->disable)
15445-
encoder->disable(encoder, to_intel_crtc_state(crtc_state), connector->base.state);
15479+
encoder->disable(encoder, crtc_state,
15480+
connector->base.state);
1544615481
if (encoder->post_disable)
15447-
encoder->post_disable(encoder, to_intel_crtc_state(crtc_state), connector->base.state);
15482+
encoder->post_disable(encoder, crtc_state,
15483+
connector->base.state);
15484+
15485+
connector->base.state->best_encoder = best_encoder;
1544815486
}
1544915487
encoder->base.crtc = NULL;
1545015488

0 commit comments

Comments
 (0)