Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f51e471 authored by Rodrigo Vivi's avatar Rodrigo Vivi Committed by Daniel Vetter
Browse files

drm/i915: Match all PSR mode entry conditions before enabling it.



v2: Prefer seq_puts to seq_printf by Paulo Zanoni.
v3: small changes like avoiding calling dp_to_dig_port twice as noticed by
    Paulo Zanoni.
v4: Avoiding reading non-existent registers - noticed by Paulo
    on first psr debugfs patch.
v5: Accepting more suggestions from Paulo:
    * check sw interlace flag instead of i915_read
    * introduce PSR_S3D_ENABLED to avoid forgeting it whenever added.

Cc: Paulo Zanoni <paulo.r.zanoni@intel.com>
Signed-off-by: default avatarRodrigo Vivi <rodrigo.vivi@gmail.com>
Reviewed-by: default avatarPaulo Zanoni <paulo.r.zanoni@intel.com>
Reviewed-by: default avatarShobhit Kumar <shobhit.kumar@intel.com>
[danvet: Fix up debugfs output (spotted by Paulo) and rip out the
power well check since we really can't do that in a race-free manner,
so it's bogus.]
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent e91fd8c6
Loading
Loading
Loading
Loading
+37 −5
Original line number Diff line number Diff line
@@ -1550,17 +1550,49 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
	struct drm_info_node *node = m->private;
	struct drm_device *dev = node->minor->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	u32 psrctl, psrstat, psrperf;
	u32 psrstat, psrperf;

	if (!IS_HASWELL(dev)) {
		seq_puts(m, "PSR not supported on this platform\n");
	} else if (IS_HASWELL(dev) && I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE) {
		seq_puts(m, "PSR enabled\n");
	} else {
		seq_puts(m, "PSR disabled: ");
		switch (dev_priv->no_psr_reason) {
		case PSR_NO_SOURCE:
			seq_puts(m, "not supported on this platform");
			break;
		case PSR_NO_SINK:
			seq_puts(m, "not supported by panel");
			break;
		case PSR_CRTC_NOT_ACTIVE:
			seq_puts(m, "crtc not active");
			break;
		case PSR_PWR_WELL_ENABLED:
			seq_puts(m, "power well enabled");
			break;
		case PSR_NOT_TILED:
			seq_puts(m, "not tiled");
			break;
		case PSR_SPRITE_ENABLED:
			seq_puts(m, "sprite enabled");
			break;
		case PSR_S3D_ENABLED:
			seq_puts(m, "stereo 3d enabled");
			break;
		case PSR_INTERLACED_ENABLED:
			seq_puts(m, "interlaced enabled");
			break;
		case PSR_HSW_NOT_DDIA:
			seq_puts(m, "HSW ties PSR to DDI A (eDP)");
			break;
		default:
			seq_puts(m, "unknown reason");
		}
		seq_puts(m, "\n");
		return 0;
	}

	psrctl = I915_READ(EDP_PSR_CTL);
	seq_printf(m, "PSR Enabled: %s\n",
		   yesno(psrctl & EDP_PSR_ENABLE));

	psrstat = I915_READ(EDP_PSR_STATUS_CTL);

	seq_puts(m, "PSR Current State: ");
+13 −0
Original line number Diff line number Diff line
@@ -593,6 +593,17 @@ struct i915_fbc {
	} no_fbc_reason;
};

enum no_psr_reason {
	PSR_NO_SOURCE, /* Not supported on platform */
	PSR_NO_SINK, /* Not supported by panel */
	PSR_CRTC_NOT_ACTIVE,
	PSR_PWR_WELL_ENABLED,
	PSR_NOT_TILED,
	PSR_SPRITE_ENABLED,
	PSR_S3D_ENABLED,
	PSR_INTERLACED_ENABLED,
	PSR_HSW_NOT_DDIA,
};

enum intel_pch {
	PCH_NONE = 0,	/* No PCH present */
@@ -1173,6 +1184,8 @@ typedef struct drm_i915_private {
	/* Haswell power well */
	struct i915_power_well power_well;

	enum no_psr_reason no_psr_reason;

	struct i915_gpu_error gpu_error;

	struct drm_i915_gem_object *vlv_pctx;
+7 −0
Original line number Diff line number Diff line
@@ -4150,6 +4150,13 @@
#define HSW_TVIDEO_DIP_VSC_DATA(trans) \
	 _TRANSCODER(trans, HSW_VIDEO_DIP_VSC_DATA_A, HSW_VIDEO_DIP_VSC_DATA_B)

#define HSW_STEREO_3D_CTL_A	0x70020
#define   S3D_ENABLE		(1<<31)
#define HSW_STEREO_3D_CTL_B	0x71020

#define HSW_STEREO_3D_CTL(trans) \
	_TRANSCODER(trans, HSW_STEREO_3D_CTL_A, HSW_STEREO_3D_CTL_A)

#define _PCH_TRANS_HTOTAL_B          0xe1000
#define _PCH_TRANS_HBLANK_B          0xe1004
#define _PCH_TRANS_HSYNC_B           0xe1008
+66 −1
Original line number Diff line number Diff line
@@ -1492,11 +1492,76 @@ static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
		   EDP_PSR_ENABLE);
}

static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
{
	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
	struct drm_device *dev = dig_port->base.base.dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct drm_crtc *crtc = dig_port->base.base.crtc;
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
	struct drm_i915_gem_object *obj = to_intel_framebuffer(crtc->fb)->obj;
	struct intel_encoder *intel_encoder = &dp_to_dig_port(intel_dp)->base;

	if (!IS_HASWELL(dev)) {
		DRM_DEBUG_KMS("PSR not supported on this platform\n");
		dev_priv->no_psr_reason = PSR_NO_SOURCE;
		return false;
	}

	if ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
	    (dig_port->port != PORT_A)) {
		DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n");
		dev_priv->no_psr_reason = PSR_HSW_NOT_DDIA;
		return false;
	}

	if (!is_edp_psr(intel_dp)) {
		DRM_DEBUG_KMS("PSR not supported by this panel\n");
		dev_priv->no_psr_reason = PSR_NO_SINK;
		return false;
	}

	if (!intel_crtc->active || !crtc->fb || !crtc->mode.clock) {
		DRM_DEBUG_KMS("crtc not active for PSR\n");
		dev_priv->no_psr_reason = PSR_CRTC_NOT_ACTIVE;
		return false;
	}

	if (obj->tiling_mode != I915_TILING_X ||
	    obj->fence_reg == I915_FENCE_REG_NONE) {
		DRM_DEBUG_KMS("PSR condition failed: fb not tiled or fenced\n");
		dev_priv->no_psr_reason = PSR_NOT_TILED;
		return false;
	}

	if (I915_READ(SPRCTL(intel_crtc->pipe)) & SPRITE_ENABLE) {
		DRM_DEBUG_KMS("PSR condition failed: Sprite is Enabled\n");
		dev_priv->no_psr_reason = PSR_SPRITE_ENABLED;
		return false;
	}

	if (I915_READ(HSW_STEREO_3D_CTL(intel_crtc->config.cpu_transcoder)) &
	    S3D_ENABLE) {
		DRM_DEBUG_KMS("PSR condition failed: Stereo 3D is Enabled\n");
		dev_priv->no_psr_reason = PSR_S3D_ENABLED;
		return false;
	}

	if (crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) {
		DRM_DEBUG_KMS("PSR condition failed: Interlaced is Enabled\n");
		dev_priv->no_psr_reason = PSR_INTERLACED_ENABLED;
		return false;
	}

	return true;
}

void intel_edp_psr_enable(struct intel_dp *intel_dp)
{
	struct drm_device *dev = intel_dp_to_dev(intel_dp);

	if (!is_edp_psr(intel_dp) || intel_edp_is_psr_enabled(dev))
	if (!intel_edp_psr_match_conditions(intel_dp) ||
	    intel_edp_is_psr_enabled(dev))
		return;

	/* Setup PSR once */