[Intel-gfx] [PATCH 02/15] drm/i915: Introduce intel_psr.c

R, Durgadoss durgadoss.r at intel.com
Tue Nov 18 19:16:23 CET 2014


>-----Original Message-----
>From: Intel-gfx [mailto:intel-gfx-bounces at lists.freedesktop.org] On Behalf Of Rodrigo Vivi
>Sent: Friday, November 14, 2014 10:22 PM
>To: intel-gfx at lists.freedesktop.org
>Cc: Vivi, Rodrigo
>Subject: [Intel-gfx] [PATCH 02/15] drm/i915: Introduce intel_psr.c
>
>No functional changes. Just cleaning and reorganizing it.
>
>v2: Rebase it puting it to begin of psr rework. This helps to blame easily
>at least latest changes.
>
>Signed-off-by: Rodrigo Vivi <rodrigo.vivi at intel.com>
>---
> drivers/gpu/drm/i915/Makefile            |   1 +
> drivers/gpu/drm/i915/intel_ddi.c         |   4 +-
> drivers/gpu/drm/i915/intel_display.c     |   2 +-
> drivers/gpu/drm/i915/intel_dp.c          | 381 +----------------------------
> drivers/gpu/drm/i915/intel_drv.h         |  21 +-
> drivers/gpu/drm/i915/intel_frontbuffer.c |   4 +-
> drivers/gpu/drm/i915/intel_psr.c         | 408 +++++++++++++++++++++++++++++++
> 7 files changed, 428 insertions(+), 393 deletions(-)
> create mode 100644 drivers/gpu/drm/i915/intel_psr.c
>
>diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
>index 891e584..e4083e4 100644
>--- a/drivers/gpu/drm/i915/Makefile
>+++ b/drivers/gpu/drm/i915/Makefile
>@@ -51,6 +51,7 @@ i915-y += intel_audio.o \
> 	  intel_frontbuffer.o \
> 	  intel_modes.o \
> 	  intel_overlay.o \
>+	  intel_psr.o \
> 	  intel_sideband.o \
> 	  intel_sprite.o
> i915-$(CONFIG_ACPI)		+= intel_acpi.o intel_opregion.o
>diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
>index 68703ce..a5a8acd 100644
>--- a/drivers/gpu/drm/i915/intel_ddi.c
>+++ b/drivers/gpu/drm/i915/intel_ddi.c
>@@ -1228,7 +1228,7 @@ static void intel_enable_ddi(struct intel_encoder *intel_encoder)
> 			intel_dp_stop_link_train(intel_dp);
>
> 		intel_edp_backlight_on(intel_dp);
>-		intel_edp_psr_enable(intel_dp);
>+		intel_psr_enable(intel_dp);
> 	}
>
> 	if (intel_crtc->config.has_audio) {
>@@ -1254,7 +1254,7 @@ static void intel_disable_ddi(struct intel_encoder *intel_encoder)
> 	if (type == INTEL_OUTPUT_EDP) {
> 		struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
>
>-		intel_edp_psr_disable(intel_dp);
>+		intel_psr_disable(intel_dp);
> 		intel_edp_backlight_off(intel_dp);
> 	}
> }
>diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
>index 4706856..2ca6939 100644
>--- a/drivers/gpu/drm/i915/intel_display.c
>+++ b/drivers/gpu/drm/i915/intel_display.c
>@@ -12310,7 +12310,7 @@ static void intel_setup_outputs(struct drm_device *dev)
> 	if (SUPPORTS_TV(dev))
> 		intel_tv_init(dev);
>
>-	intel_edp_psr_init(dev);
>+	intel_psr_init(dev);
>
> 	for_each_intel_encoder(dev, encoder) {
> 		encoder->base.possible_crtcs = encoder->crtc_mask;
>diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
>index 01834cd..98f7ecd 100644
>--- a/drivers/gpu/drm/i915/intel_dp.c
>+++ b/drivers/gpu/drm/i915/intel_dp.c
>@@ -2066,385 +2066,6 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
> 	}
> }
>
>-static bool is_edp_psr(struct intel_dp *intel_dp)
>-{
>-	return intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
>-}
>-
>-static bool intel_edp_is_psr_enabled(struct drm_device *dev)
>-{
>-	struct drm_i915_private *dev_priv = dev->dev_private;
>-
>-	if (!HAS_PSR(dev))
>-		return false;
>-
>-	return I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
>-}
>-
>-static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
>-				    struct edp_vsc_psr *vsc_psr)
>-{
>-	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 intel_crtc *crtc = to_intel_crtc(dig_port->base.base.crtc);
>-	u32 ctl_reg = HSW_TVIDEO_DIP_CTL(crtc->config.cpu_transcoder);
>-	u32 data_reg = HSW_TVIDEO_DIP_VSC_DATA(crtc->config.cpu_transcoder);
>-	uint32_t *data = (uint32_t *) vsc_psr;
>-	unsigned int i;
>-
>-	/* As per BSPec (Pipe Video Data Island Packet), we need to disable
>-	   the video DIP being updated before program video DIP data buffer
>-	   registers for DIP being updated. */
>-	I915_WRITE(ctl_reg, 0);
>-	POSTING_READ(ctl_reg);
>-
>-	for (i = 0; i < VIDEO_DIP_VSC_DATA_SIZE; i += 4) {
>-		if (i < sizeof(struct edp_vsc_psr))
>-			I915_WRITE(data_reg + i, *data++);
>-		else
>-			I915_WRITE(data_reg + i, 0);
>-	}
>-
>-	I915_WRITE(ctl_reg, VIDEO_DIP_ENABLE_VSC_HSW);
>-	POSTING_READ(ctl_reg);
>-}
>-
>-static void intel_edp_psr_setup_vsc(struct intel_dp *intel_dp)
>-{
>-	struct edp_vsc_psr psr_vsc;
>-
>-	/* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
>-	memset(&psr_vsc, 0, sizeof(psr_vsc));
>-	psr_vsc.sdp_header.HB0 = 0;
>-	psr_vsc.sdp_header.HB1 = 0x7;
>-	psr_vsc.sdp_header.HB2 = 0x2;
>-	psr_vsc.sdp_header.HB3 = 0x8;
>-	intel_edp_psr_write_vsc(intel_dp, &psr_vsc);
>-}
>-
>-static void intel_edp_psr_enable_sink(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;
>-	uint32_t aux_clock_divider;
>-	int precharge = 0x3;
>-	bool only_standby = false;
>-	static const uint8_t aux_msg[] = {
>-		[0] = DP_AUX_NATIVE_WRITE << 4,
>-		[1] = DP_SET_POWER >> 8,
>-		[2] = DP_SET_POWER & 0xff,
>-		[3] = 1 - 1,
>-		[4] = DP_SET_POWER_D0,
>-	};
>-	int i;
>-
>-	BUILD_BUG_ON(sizeof(aux_msg) > 20);
>-
>-	aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, 0);
>-
>-	if (IS_BROADWELL(dev) && dig_port->port != PORT_A)
>-		only_standby = true;
>-
>-	/* Enable PSR in sink */
>-	if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT || only_standby)
>-		drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
>-				   DP_PSR_ENABLE & ~DP_PSR_MAIN_LINK_ACTIVE);
>-	else
>-		drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
>-				   DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE);
>-
>-	/* Setup AUX registers */
>-	for (i = 0; i < sizeof(aux_msg); i += 4)
>-		I915_WRITE(EDP_PSR_AUX_DATA1(dev) + i,
>-			   intel_dp_pack_aux(&aux_msg[i], sizeof(aux_msg) - i));
>-
>-	I915_WRITE(EDP_PSR_AUX_CTL(dev),
>-		   DP_AUX_CH_CTL_TIME_OUT_400us |
>-		   (sizeof(aux_msg) << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
>-		   (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
>-		   (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT));
>-}
>-
>-static void intel_edp_psr_enable_source(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;
>-	uint32_t max_sleep_time = 0x1f;
>-	uint32_t idle_frames = 1;
>-	uint32_t val = 0x0;
>-	const uint32_t link_entry_time = EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES;
>-	bool only_standby = false;
>-
>-	if (IS_BROADWELL(dev) && dig_port->port != PORT_A)
>-		only_standby = true;
>-
>-	if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT || only_standby) {
>-		val |= EDP_PSR_LINK_STANDBY;
>-		val |= EDP_PSR_TP2_TP3_TIME_0us;
>-		val |= EDP_PSR_TP1_TIME_0us;
>-		val |= EDP_PSR_SKIP_AUX_EXIT;
>-		val |= IS_BROADWELL(dev) ? BDW_PSR_SINGLE_FRAME : 0;
>-	} else
>-		val |= EDP_PSR_LINK_DISABLE;
>-
>-	I915_WRITE(EDP_PSR_CTL(dev), val |
>-		   (IS_BROADWELL(dev) ? 0 : link_entry_time) |
>-		   max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT |
>-		   idle_frames << EDP_PSR_IDLE_FRAME_SHIFT |
>-		   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);
>-
>-	lockdep_assert_held(&dev_priv->psr.lock);
>-	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>-	WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
>-
>-	dev_priv->psr.source_ok = false;
>-
>-	if (IS_HASWELL(dev) && dig_port->port != PORT_A) {
>-		DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n");
>-		return false;
>-	}
>-
>-	if (!i915.enable_psr) {
>-		DRM_DEBUG_KMS("PSR disable by flag\n");
>-		return false;
>-	}
>-
>-	/* Below limitations aren't valid for Broadwell */
>-	if (IS_BROADWELL(dev))
>-		goto out;
>-
>-	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");
>-		return false;
>-	}
>-
>-	if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) {
>-		DRM_DEBUG_KMS("PSR condition failed: Interlaced is Enabled\n");
>-		return false;
>-	}
>-
>- out:
>-	dev_priv->psr.source_ok = true;
>-	return true;
>-}
>-
>-static void intel_edp_psr_do_enable(struct intel_dp *intel_dp)
>-{
>-	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>-	struct drm_device *dev = intel_dig_port->base.base.dev;
>-	struct drm_i915_private *dev_priv = dev->dev_private;
>-
>-	WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE);
>-	WARN_ON(dev_priv->psr.active);
>-	lockdep_assert_held(&dev_priv->psr.lock);
>-
>-	/* Enable/Re-enable PSR on the host */
>-	intel_edp_psr_enable_source(intel_dp);
>-
>-	dev_priv->psr.active = true;
>-}
>-
>-void intel_edp_psr_enable(struct intel_dp *intel_dp)
>-{
>-	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>-	struct drm_i915_private *dev_priv = dev->dev_private;
>-
>-	if (!HAS_PSR(dev)) {
>-		DRM_DEBUG_KMS("PSR not supported on this platform\n");
>-		return;
>-	}
>-
>-	if (!is_edp_psr(intel_dp)) {
>-		DRM_DEBUG_KMS("PSR not supported by this panel\n");
>-		return;
>-	}
>-
>-	mutex_lock(&dev_priv->psr.lock);
>-	if (dev_priv->psr.enabled) {
>-		DRM_DEBUG_KMS("PSR already in use\n");
>-		goto unlock;
>-	}
>-
>-	if (!intel_edp_psr_match_conditions(intel_dp))
>-		goto unlock;
>-
>-	dev_priv->psr.busy_frontbuffer_bits = 0;
>-
>-	intel_edp_psr_setup_vsc(intel_dp);
>-
>-	/* Avoid continuous PSR exit by masking memup and hpd */
>-	I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP |
>-		   EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
>-
>-	/* Enable PSR on the panel */
>-	intel_edp_psr_enable_sink(intel_dp);
>-
>-	dev_priv->psr.enabled = intel_dp;
>-unlock:
>-	mutex_unlock(&dev_priv->psr.lock);
>-}
>-
>-void intel_edp_psr_disable(struct intel_dp *intel_dp)
>-{
>-	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>-	struct drm_i915_private *dev_priv = dev->dev_private;
>-
>-	mutex_lock(&dev_priv->psr.lock);
>-	if (!dev_priv->psr.enabled) {
>-		mutex_unlock(&dev_priv->psr.lock);
>-		return;
>-	}
>-
>-	if (dev_priv->psr.active) {
>-		I915_WRITE(EDP_PSR_CTL(dev),
>-			   I915_READ(EDP_PSR_CTL(dev)) & ~EDP_PSR_ENABLE);
>-
>-		/* Wait till PSR is idle */
>-		if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev)) &
>-			       EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10))
>-			DRM_ERROR("Timed out waiting for PSR Idle State\n");
>-
>-		dev_priv->psr.active = false;
>-	} else {
>-		WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE);
>-	}
>-
>-	dev_priv->psr.enabled = NULL;
>-	mutex_unlock(&dev_priv->psr.lock);
>-
>-	cancel_delayed_work_sync(&dev_priv->psr.work);
>-}
>-
>-static void intel_edp_psr_work(struct work_struct *work)
>-{
>-	struct drm_i915_private *dev_priv =
>-		container_of(work, typeof(*dev_priv), psr.work.work);
>-	struct intel_dp *intel_dp = dev_priv->psr.enabled;
>-
>-	/* We have to make sure PSR is ready for re-enable
>-	 * otherwise it keeps disabled until next full enable/disable cycle.
>-	 * PSR might take some time to get fully disabled
>-	 * and be ready for re-enable.
>-	 */
>-	if (wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev_priv->dev)) &
>-		      EDP_PSR_STATUS_STATE_MASK) == 0, 50)) {
>-		DRM_ERROR("Timed out waiting for PSR Idle for re-enable\n");
>-		return;
>-	}
>-
>-	mutex_lock(&dev_priv->psr.lock);
>-	intel_dp = dev_priv->psr.enabled;
>-
>-	if (!intel_dp)
>-		goto unlock;
>-
>-	/*
>-	 * The delayed work can race with an invalidate hence we need to
>-	 * recheck. Since psr_flush first clears this and then reschedules we
>-	 * won't ever miss a flush when bailing out here.
>-	 */
>-	if (dev_priv->psr.busy_frontbuffer_bits)
>-		goto unlock;
>-
>-	intel_edp_psr_do_enable(intel_dp);
>-unlock:
>-	mutex_unlock(&dev_priv->psr.lock);
>-}
>-
>-static void intel_edp_psr_do_exit(struct drm_device *dev)
>-{
>-	struct drm_i915_private *dev_priv = dev->dev_private;
>-
>-	if (dev_priv->psr.active) {
>-		u32 val = I915_READ(EDP_PSR_CTL(dev));
>-
>-		WARN_ON(!(val & EDP_PSR_ENABLE));
>-
>-		I915_WRITE(EDP_PSR_CTL(dev), val & ~EDP_PSR_ENABLE);
>-
>-		dev_priv->psr.active = false;
>-	}
>-
>-}
>-
>-void intel_edp_psr_invalidate(struct drm_device *dev,
>-			      unsigned frontbuffer_bits)
>-{
>-	struct drm_i915_private *dev_priv = dev->dev_private;
>-	struct drm_crtc *crtc;
>-	enum pipe pipe;
>-
>-	mutex_lock(&dev_priv->psr.lock);
>-	if (!dev_priv->psr.enabled) {
>-		mutex_unlock(&dev_priv->psr.lock);
>-		return;
>-	}
>-
>-	crtc = dp_to_dig_port(dev_priv->psr.enabled)->base.base.crtc;
>-	pipe = to_intel_crtc(crtc)->pipe;
>-
>-	intel_edp_psr_do_exit(dev);
>-
>-	frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe);
>-
>-	dev_priv->psr.busy_frontbuffer_bits |= frontbuffer_bits;
>-	mutex_unlock(&dev_priv->psr.lock);
>-}
>-
>-void intel_edp_psr_flush(struct drm_device *dev,
>-			 unsigned frontbuffer_bits)
>-{
>-	struct drm_i915_private *dev_priv = dev->dev_private;
>-	struct drm_crtc *crtc;
>-	enum pipe pipe;
>-
>-	mutex_lock(&dev_priv->psr.lock);
>-	if (!dev_priv->psr.enabled) {
>-		mutex_unlock(&dev_priv->psr.lock);
>-		return;
>-	}
>-
>-	crtc = dp_to_dig_port(dev_priv->psr.enabled)->base.base.crtc;
>-	pipe = to_intel_crtc(crtc)->pipe;
>-	dev_priv->psr.busy_frontbuffer_bits &= ~frontbuffer_bits;
>-
>-	/*
>-	 * On Haswell sprite plane updates don't result in a psr invalidating
>-	 * signal in the hardware. Which means we need to manually fake this in
>-	 * software for all flushes, not just when we've seen a preceding
>-	 * invalidation through frontbuffer rendering.
>-	 */
>-	if (IS_HASWELL(dev) &&
>-	    (frontbuffer_bits & INTEL_FRONTBUFFER_SPRITE(pipe)))
>-		intel_edp_psr_do_exit(dev);
>-
>-	if (!dev_priv->psr.active && !dev_priv->psr.busy_frontbuffer_bits)
>-		schedule_delayed_work(&dev_priv->psr.work,
>-				      msecs_to_jiffies(100));
>-	mutex_unlock(&dev_priv->psr.lock);
>-}
>-
>-void intel_edp_psr_init(struct drm_device *dev)
>-{
>-	struct drm_i915_private *dev_priv = dev->dev_private;
>-
>-	INIT_DELAYED_WORK(&dev_priv->psr.work, intel_edp_psr_work);
>-	mutex_init(&dev_priv->psr.lock);
>-}
>-
> static void intel_disable_dp(struct intel_encoder *encoder)
> {
> 	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
>@@ -5115,7 +4736,7 @@ void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate)
> 	 * hard to tell without seeing the user of this function of this code.
> 	 * Check locking and ordering once that lands.
> 	 */
>-	if (INTEL_INFO(dev)->gen < 8 && intel_edp_is_psr_enabled(dev)) {
>+	if (INTEL_INFO(dev)->gen < 8 && intel_psr_is_enabled(dev)) {

Shouldn't we do <= 8 to cover CHV ?

Also, I see that we are converting the 'static' _is_enabled function into a
non-static (exported) one and use 'psr.enabled' variable. (patch 9/15)
In this case, shouldn't we protect this function using psr.lock ?
[actually a comment on patch 9/15, but the caller is here]

Thanks,
Durga

> 		DRM_DEBUG_KMS("DRRS is disabled as PSR is enabled\n");
> 		return;
> 	}
>diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>index 8432ae2..ccfee9c 100644
>--- a/drivers/gpu/drm/i915/intel_drv.h
>+++ b/drivers/gpu/drm/i915/intel_drv.h
>@@ -996,21 +996,16 @@ void intel_edp_backlight_off(struct intel_dp *intel_dp);
> void intel_edp_panel_vdd_on(struct intel_dp *intel_dp);
> void intel_edp_panel_on(struct intel_dp *intel_dp);
> void intel_edp_panel_off(struct intel_dp *intel_dp);
>-void intel_edp_psr_enable(struct intel_dp *intel_dp);
>-void intel_edp_psr_disable(struct intel_dp *intel_dp);
> void intel_dp_set_drrs_state(struct drm_device *dev, int refresh_rate);
>-void intel_edp_psr_invalidate(struct drm_device *dev,
>-			      unsigned frontbuffer_bits);
>-void intel_edp_psr_flush(struct drm_device *dev,
>-			 unsigned frontbuffer_bits);
>-void intel_edp_psr_init(struct drm_device *dev);
>-
> void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector);
> void intel_dp_mst_suspend(struct drm_device *dev);
> void intel_dp_mst_resume(struct drm_device *dev);
> int intel_dp_max_link_bw(struct intel_dp *intel_dp);
> void intel_dp_hot_plug(struct intel_encoder *intel_encoder);
> void vlv_power_sequencer_reset(struct drm_i915_private *dev_priv);
>+uint32_t intel_dp_pack_aux(const uint8_t *src, int src_bytes);
>+void intel_dp_unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes);
>+
> /* intel_dp_mst.c */
> int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
> void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
>@@ -1114,6 +1109,16 @@ void intel_backlight_register(struct drm_device *dev);
> void intel_backlight_unregister(struct drm_device *dev);
>
>
>+/* intel_psr.c */
>+bool intel_psr_is_enabled(struct drm_device *dev);
>+void intel_psr_enable(struct intel_dp *intel_dp);
>+void intel_psr_disable(struct intel_dp *intel_dp);
>+void intel_psr_invalidate(struct drm_device *dev,
>+			      unsigned frontbuffer_bits);
>+void intel_psr_flush(struct drm_device *dev,
>+			 unsigned frontbuffer_bits);
>+void intel_psr_init(struct drm_device *dev);
>+
> /* intel_runtime_pm.c */
> int intel_power_domains_init(struct drm_i915_private *);
> void intel_power_domains_fini(struct drm_i915_private *);
>diff --git a/drivers/gpu/drm/i915/intel_frontbuffer.c b/drivers/gpu/drm/i915/intel_frontbuffer.c
>index 58cf2e6..79f6d72 100644
>--- a/drivers/gpu/drm/i915/intel_frontbuffer.c
>+++ b/drivers/gpu/drm/i915/intel_frontbuffer.c
>@@ -156,7 +156,7 @@ void intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
>
> 	intel_mark_fb_busy(dev, obj->frontbuffer_bits, ring);
>
>-	intel_edp_psr_invalidate(dev, obj->frontbuffer_bits);
>+	intel_psr_invalidate(dev, obj->frontbuffer_bits);
> }
>
> /**
>@@ -182,7 +182,7 @@ void intel_frontbuffer_flush(struct drm_device *dev,
>
> 	intel_mark_fb_busy(dev, frontbuffer_bits, NULL);
>
>-	intel_edp_psr_flush(dev, frontbuffer_bits);
>+	intel_psr_flush(dev, frontbuffer_bits);
>
> 	/*
> 	 * FIXME: Unconditional fbc flushing here is a rather gross hack and
>diff --git a/drivers/gpu/drm/i915/intel_psr.c b/drivers/gpu/drm/i915/intel_psr.c
>new file mode 100644
>index 0000000..7b3ed91
>--- /dev/null
>+++ b/drivers/gpu/drm/i915/intel_psr.c
>@@ -0,0 +1,408 @@
>+/*
>+ * Copyright © 2014 Intel Corporation
>+ *
>+ * Permission is hereby granted, free of charge, to any person obtaining a
>+ * copy of this software and associated documentation files (the "Software"),
>+ * to deal in the Software without restriction, including without limitation
>+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
>+ * and/or sell copies of the Software, and to permit persons to whom the
>+ * Software is furnished to do so, subject to the following conditions:
>+ *
>+ * The above copyright notice and this permission notice (including the next
>+ * paragraph) shall be included in all copies or substantial portions of the
>+ * Software.
>+ *
>+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
>+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
>+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
>+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
>+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
>+ * DEALINGS IN THE SOFTWARE.
>+ */
>+
>+#include <drm/drmP.h>
>+
>+#include "intel_drv.h"
>+#include "i915_drv.h"
>+
>+static bool is_edp_psr(struct intel_dp *intel_dp)
>+{
>+	return intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
>+}
>+
>+bool intel_psr_is_enabled(struct drm_device *dev)
>+{
>+	struct drm_i915_private *dev_priv = dev->dev_private;
>+
>+	if (!HAS_PSR(dev))
>+		return false;
>+
>+	return I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
>+}
>+
>+static void intel_psr_write_vsc(struct intel_dp *intel_dp,
>+				    struct edp_vsc_psr *vsc_psr)
>+{
>+	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 intel_crtc *crtc = to_intel_crtc(dig_port->base.base.crtc);
>+	u32 ctl_reg = HSW_TVIDEO_DIP_CTL(crtc->config.cpu_transcoder);
>+	u32 data_reg = HSW_TVIDEO_DIP_VSC_DATA(crtc->config.cpu_transcoder);
>+	uint32_t *data = (uint32_t *) vsc_psr;
>+	unsigned int i;
>+
>+	/* As per BSPec (Pipe Video Data Island Packet), we need to disable
>+	   the video DIP being updated before program video DIP data buffer
>+	   registers for DIP being updated. */
>+	I915_WRITE(ctl_reg, 0);
>+	POSTING_READ(ctl_reg);
>+
>+	for (i = 0; i < VIDEO_DIP_VSC_DATA_SIZE; i += 4) {
>+		if (i < sizeof(struct edp_vsc_psr))
>+			I915_WRITE(data_reg + i, *data++);
>+		else
>+			I915_WRITE(data_reg + i, 0);
>+	}
>+
>+	I915_WRITE(ctl_reg, VIDEO_DIP_ENABLE_VSC_HSW);
>+	POSTING_READ(ctl_reg);
>+}
>+
>+static void intel_psr_setup_vsc(struct intel_dp *intel_dp)
>+{
>+	struct edp_vsc_psr psr_vsc;
>+
>+	/* Prepare VSC packet as per EDP 1.3 spec, Table 3.10 */
>+	memset(&psr_vsc, 0, sizeof(psr_vsc));
>+	psr_vsc.sdp_header.HB0 = 0;
>+	psr_vsc.sdp_header.HB1 = 0x7;
>+	psr_vsc.sdp_header.HB2 = 0x2;
>+	psr_vsc.sdp_header.HB3 = 0x8;
>+	intel_psr_write_vsc(intel_dp, &psr_vsc);
>+}
>+
>+static void intel_psr_enable_sink(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;
>+	uint32_t aux_clock_divider;
>+	int precharge = 0x3;
>+	bool only_standby = false;
>+	static const uint8_t aux_msg[] = {
>+		[0] = DP_AUX_NATIVE_WRITE << 4,
>+		[1] = DP_SET_POWER >> 8,
>+		[2] = DP_SET_POWER & 0xff,
>+		[3] = 1 - 1,
>+		[4] = DP_SET_POWER_D0,
>+	};
>+	int i;
>+
>+	BUILD_BUG_ON(sizeof(aux_msg) > 20);
>+
>+	aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, 0);
>+
>+	if (IS_BROADWELL(dev) && dig_port->port != PORT_A)
>+		only_standby = true;
>+
>+	/* Enable PSR in sink */
>+	if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT || only_standby)
>+		drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
>+				   DP_PSR_ENABLE & ~DP_PSR_MAIN_LINK_ACTIVE);
>+	else
>+		drm_dp_dpcd_writeb(&intel_dp->aux, DP_PSR_EN_CFG,
>+				   DP_PSR_ENABLE | DP_PSR_MAIN_LINK_ACTIVE);
>+
>+	/* Setup AUX registers */
>+	for (i = 0; i < sizeof(aux_msg); i += 4)
>+		I915_WRITE(EDP_PSR_AUX_DATA1(dev) + i,
>+			   intel_dp_pack_aux(&aux_msg[i], sizeof(aux_msg) - i));
>+
>+	I915_WRITE(EDP_PSR_AUX_CTL(dev),
>+		   DP_AUX_CH_CTL_TIME_OUT_400us |
>+		   (sizeof(aux_msg) << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
>+		   (precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
>+		   (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT));
>+}
>+
>+static void intel_psr_enable_source(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;
>+	uint32_t max_sleep_time = 0x1f;
>+	uint32_t idle_frames = 1;
>+	uint32_t val = 0x0;
>+	const uint32_t link_entry_time = EDP_PSR_MIN_LINK_ENTRY_TIME_8_LINES;
>+	bool only_standby = false;
>+
>+	if (IS_BROADWELL(dev) && dig_port->port != PORT_A)
>+		only_standby = true;
>+
>+	if (intel_dp->psr_dpcd[1] & DP_PSR_NO_TRAIN_ON_EXIT || only_standby) {
>+		val |= EDP_PSR_LINK_STANDBY;
>+		val |= EDP_PSR_TP2_TP3_TIME_0us;
>+		val |= EDP_PSR_TP1_TIME_0us;
>+		val |= EDP_PSR_SKIP_AUX_EXIT;
>+		val |= IS_BROADWELL(dev) ? BDW_PSR_SINGLE_FRAME : 0;
>+	} else
>+		val |= EDP_PSR_LINK_DISABLE;
>+
>+	I915_WRITE(EDP_PSR_CTL(dev), val |
>+		   (IS_BROADWELL(dev) ? 0 : link_entry_time) |
>+		   max_sleep_time << EDP_PSR_MAX_SLEEP_TIME_SHIFT |
>+		   idle_frames << EDP_PSR_IDLE_FRAME_SHIFT |
>+		   EDP_PSR_ENABLE);
>+}
>+
>+static bool intel_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);
>+
>+	lockdep_assert_held(&dev_priv->psr.lock);
>+	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
>+	WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
>+
>+	dev_priv->psr.source_ok = false;
>+
>+	if (IS_HASWELL(dev) && dig_port->port != PORT_A) {
>+		DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n");
>+		return false;
>+	}
>+
>+	if (!i915.enable_psr) {
>+		DRM_DEBUG_KMS("PSR disable by flag\n");
>+		return false;
>+	}
>+
>+	/* Below limitations aren't valid for Broadwell */
>+	if (IS_BROADWELL(dev))
>+		goto out;
>+
>+	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");
>+		return false;
>+	}
>+
>+	if (intel_crtc->config.adjusted_mode.flags & DRM_MODE_FLAG_INTERLACE) {
>+		DRM_DEBUG_KMS("PSR condition failed: Interlaced is Enabled\n");
>+		return false;
>+	}
>+
>+ out:
>+	dev_priv->psr.source_ok = true;
>+	return true;
>+}
>+
>+static void intel_psr_do_enable(struct intel_dp *intel_dp)
>+{
>+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>+	struct drm_device *dev = intel_dig_port->base.base.dev;
>+	struct drm_i915_private *dev_priv = dev->dev_private;
>+
>+	WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE);
>+	WARN_ON(dev_priv->psr.active);
>+	lockdep_assert_held(&dev_priv->psr.lock);
>+
>+	/* Enable/Re-enable PSR on the host */
>+	intel_psr_enable_source(intel_dp);
>+
>+	dev_priv->psr.active = true;
>+}
>+
>+void intel_psr_enable(struct intel_dp *intel_dp)
>+{
>+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>+	struct drm_device *dev = intel_dig_port->base.base.dev;
>+	struct drm_i915_private *dev_priv = dev->dev_private;
>+
>+	if (!HAS_PSR(dev)) {
>+		DRM_DEBUG_KMS("PSR not supported on this platform\n");
>+		return;
>+	}
>+
>+	if (!is_edp_psr(intel_dp)) {
>+		DRM_DEBUG_KMS("PSR not supported by this panel\n");
>+		return;
>+	}
>+
>+	mutex_lock(&dev_priv->psr.lock);
>+	if (dev_priv->psr.enabled) {
>+		DRM_DEBUG_KMS("PSR already in use\n");
>+		goto unlock;
>+	}
>+
>+	if (!intel_psr_match_conditions(intel_dp))
>+		goto unlock;
>+
>+	dev_priv->psr.busy_frontbuffer_bits = 0;
>+
>+	intel_psr_setup_vsc(intel_dp);
>+
>+	/* Avoid continuous PSR exit by masking memup and hpd */
>+	I915_WRITE(EDP_PSR_DEBUG_CTL(dev), EDP_PSR_DEBUG_MASK_MEMUP |
>+		   EDP_PSR_DEBUG_MASK_HPD | EDP_PSR_DEBUG_MASK_LPSP);
>+
>+	/* Enable PSR on the panel */
>+	intel_psr_enable_sink(intel_dp);
>+
>+	dev_priv->psr.enabled = intel_dp;
>+unlock:
>+	mutex_unlock(&dev_priv->psr.lock);
>+}
>+
>+void intel_psr_disable(struct intel_dp *intel_dp)
>+{
>+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>+	struct drm_device *dev = intel_dig_port->base.base.dev;
>+	struct drm_i915_private *dev_priv = dev->dev_private;
>+
>+	mutex_lock(&dev_priv->psr.lock);
>+	if (!dev_priv->psr.enabled) {
>+		mutex_unlock(&dev_priv->psr.lock);
>+		return;
>+	}
>+
>+	if (dev_priv->psr.active) {
>+		I915_WRITE(EDP_PSR_CTL(dev),
>+			   I915_READ(EDP_PSR_CTL(dev)) & ~EDP_PSR_ENABLE);
>+
>+		/* Wait till PSR is idle */
>+		if (_wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev)) &
>+			       EDP_PSR_STATUS_STATE_MASK) == 0, 2000, 10))
>+			DRM_ERROR("Timed out waiting for PSR Idle State\n");
>+
>+		dev_priv->psr.active = false;
>+	} else {
>+		WARN_ON(I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE);
>+	}
>+
>+	dev_priv->psr.enabled = NULL;
>+	mutex_unlock(&dev_priv->psr.lock);
>+
>+	cancel_delayed_work_sync(&dev_priv->psr.work);
>+}
>+
>+static void intel_psr_work(struct work_struct *work)
>+{
>+	struct drm_i915_private *dev_priv =
>+		container_of(work, typeof(*dev_priv), psr.work.work);
>+	struct intel_dp *intel_dp = dev_priv->psr.enabled;
>+
>+	/* We have to make sure PSR is ready for re-enable
>+	 * otherwise it keeps disabled until next full enable/disable cycle.
>+	 * PSR might take some time to get fully disabled
>+	 * and be ready for re-enable.
>+	 */
>+	if (wait_for((I915_READ(EDP_PSR_STATUS_CTL(dev_priv->dev)) &
>+		      EDP_PSR_STATUS_STATE_MASK) == 0, 50)) {
>+		DRM_ERROR("Timed out waiting for PSR Idle for re-enable\n");
>+		return;
>+	}
>+
>+	mutex_lock(&dev_priv->psr.lock);
>+	intel_dp = dev_priv->psr.enabled;
>+
>+	if (!intel_dp)
>+		goto unlock;
>+
>+	/*
>+	 * The delayed work can race with an invalidate hence we need to
>+	 * recheck. Since psr_flush first clears this and then reschedules we
>+	 * won't ever miss a flush when bailing out here.
>+	 */
>+	if (dev_priv->psr.busy_frontbuffer_bits)
>+		goto unlock;
>+
>+	intel_psr_do_enable(intel_dp);
>+unlock:
>+	mutex_unlock(&dev_priv->psr.lock);
>+}
>+
>+static void intel_psr_exit(struct drm_device *dev)
>+{
>+	struct drm_i915_private *dev_priv = dev->dev_private;
>+
>+	if (dev_priv->psr.active) {
>+		u32 val = I915_READ(EDP_PSR_CTL(dev));
>+
>+		WARN_ON(!(val & EDP_PSR_ENABLE));
>+
>+		I915_WRITE(EDP_PSR_CTL(dev), val & ~EDP_PSR_ENABLE);
>+
>+		dev_priv->psr.active = false;
>+	}
>+
>+}
>+
>+void intel_psr_invalidate(struct drm_device *dev,
>+			      unsigned frontbuffer_bits)
>+{
>+	struct drm_i915_private *dev_priv = dev->dev_private;
>+	struct drm_crtc *crtc;
>+	enum pipe pipe;
>+
>+	mutex_lock(&dev_priv->psr.lock);
>+	if (!dev_priv->psr.enabled) {
>+		mutex_unlock(&dev_priv->psr.lock);
>+		return;
>+	}
>+
>+	crtc = dp_to_dig_port(dev_priv->psr.enabled)->base.base.crtc;
>+	pipe = to_intel_crtc(crtc)->pipe;
>+
>+	intel_psr_exit(dev);
>+
>+	frontbuffer_bits &= INTEL_FRONTBUFFER_ALL_MASK(pipe);
>+
>+	dev_priv->psr.busy_frontbuffer_bits |= frontbuffer_bits;
>+	mutex_unlock(&dev_priv->psr.lock);
>+}
>+
>+void intel_psr_flush(struct drm_device *dev,
>+			 unsigned frontbuffer_bits)
>+{
>+	struct drm_i915_private *dev_priv = dev->dev_private;
>+	struct drm_crtc *crtc;
>+	enum pipe pipe;
>+
>+	mutex_lock(&dev_priv->psr.lock);
>+	if (!dev_priv->psr.enabled) {
>+		mutex_unlock(&dev_priv->psr.lock);
>+		return;
>+	}
>+
>+	crtc = dp_to_dig_port(dev_priv->psr.enabled)->base.base.crtc;
>+	pipe = to_intel_crtc(crtc)->pipe;
>+	dev_priv->psr.busy_frontbuffer_bits &= ~frontbuffer_bits;
>+
>+	/*
>+	 * On Haswell sprite plane updates don't result in a psr invalidating
>+	 * signal in the hardware. Which means we need to manually fake this in
>+	 * software for all flushes, not just when we've seen a preceding
>+	 * invalidation through frontbuffer rendering.
>+	 */
>+	if (IS_HASWELL(dev) &&
>+	    (frontbuffer_bits & INTEL_FRONTBUFFER_SPRITE(pipe)))
>+		intel_psr_exit(dev);
>+
>+	if (!dev_priv->psr.active && !dev_priv->psr.busy_frontbuffer_bits)
>+		schedule_delayed_work(&dev_priv->psr.work,
>+				      msecs_to_jiffies(100));
>+	mutex_unlock(&dev_priv->psr.lock);
>+}
>+
>+void intel_psr_init(struct drm_device *dev)
>+{
>+	struct drm_i915_private *dev_priv = dev->dev_private;
>+
>+	INIT_DELAYED_WORK(&dev_priv->psr.work, intel_psr_work);
>+	mutex_init(&dev_priv->psr.lock);
>+}
>--
>1.9.3
>
>_______________________________________________
>Intel-gfx mailing list
>Intel-gfx at lists.freedesktop.org
>http://lists.freedesktop.org/mailman/listinfo/intel-gfx


More information about the Intel-gfx mailing list