[PATCH 13/13] drm/i915: force psr exit when busy

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



PSR tracking engine in HSW doesn't detect automagically some directly copy area
operations through scanout so we have to kick it manually and reschedule it to
come back to normal operation as soon as possible.

Signed-off-by: Rodrigo Vivi <rodrigo.vivi at gmail.com>
---
 drivers/gpu/drm/i915/i915_gem.c  |  2 ++
 drivers/gpu/drm/i915/i915_reg.h  |  1 +
 drivers/gpu/drm/i915/intel_dp.c  | 61 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h |  4 +++
 4 files changed, 68 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 58048d4..fc640fc 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -3684,6 +3684,8 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
 		goto unlock;
 	}
 
+	intel_edp_psr_force_exit(dev);
+
 	/* Count all active objects as busy, even if they are currently not used
 	 * by the gpu. Users of this interface expect objects to eventually
 	 * become non-busy without any further actions, therefore emit any
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 16ab839..9ad699e 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -1836,6 +1836,7 @@
 #define   EDP_PSR_PERF_CNT_MASK		0xffffff
 
 #define EDP_PSR_DEBUG_CTL		0x64860
+#define   EDP_PSR_DEBUG_FORCE_EXIT	(3<<30)
 #define   EDP_PSR_DEBUG_MASK_LPSP	(1<<27)
 #define   EDP_PSR_DEBUG_MASK_MEMUP	(1<<26)
 #define   EDP_PSR_DEBUG_MASK_HPD	(1<<25)
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index c5ea419..4ab9aa9 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1359,9 +1359,54 @@ static bool is_edp_psr(struct intel_dp *intel_dp)
 static bool intel_edp_is_psr_enabled(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (!IS_HASWELL(dev))
+		return false;
+
 	return I915_READ(EDP_PSR_CTL) & EDP_PSR_ENABLE;
 }
 
+static void intel_edp_psr_delayed_normal_work(struct work_struct *__work)
+{
+	struct intel_dp *intel_dp = container_of(to_delayed_work(__work),
+						 struct intel_dp,
+						 edp_psr_delayed_normal_work);
+	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	mutex_lock(&intel_dp->psr_exit_mutex);
+	I915_WRITE(EDP_PSR_DEBUG_CTL, I915_READ(EDP_PSR_DEBUG_CTL) &
+		   ~EDP_PSR_DEBUG_FORCE_EXIT);
+	mutex_unlock(&intel_dp->psr_exit_mutex);
+}
+
+void intel_edp_psr_force_exit(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_encoder *encoder;
+	struct intel_dp *intel_dp = NULL;
+
+	if (!intel_edp_is_psr_enabled(dev))
+		return;
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head)
+		if (encoder->type == INTEL_OUTPUT_EDP)
+			intel_dp = enc_to_intel_dp(&encoder->base);
+
+	if (!intel_dp)
+		return;
+
+	if (WARN_ON(!intel_dp->psr_setup_done))
+		return;
+
+	mutex_lock(&intel_dp->psr_exit_mutex);
+	I915_WRITE(EDP_PSR_DEBUG_CTL, I915_READ(EDP_PSR_DEBUG_CTL) |
+		   EDP_PSR_DEBUG_FORCE_EXIT);
+	mutex_unlock(&intel_dp->psr_exit_mutex);
+
+	schedule_delayed_work(&intel_dp->edp_psr_delayed_normal_work,
+			      msecs_to_jiffies(100));
+}
 
 void intel_edp_psr_write_vsc(struct intel_dp* intel_dp,
 			     struct edp_vsc_psr *vsc_psr)
@@ -1397,6 +1442,18 @@ void intel_edp_psr_write_vsc(struct intel_dp* intel_dp,
 	POSTING_READ(ctl_reg);
 }
 
+static void intel_edp_psr_setup(struct intel_dp *intel_dp)
+{
+	if (intel_dp->psr_setup_done)
+		return;
+
+	INIT_DELAYED_WORK(&intel_dp->edp_psr_delayed_normal_work,
+			  intel_edp_psr_delayed_normal_work);
+	mutex_init(&intel_dp->psr_exit_mutex);
+
+	intel_dp->psr_setup_done = true;
+}
+
 static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -1504,6 +1561,8 @@ static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
 
 static void intel_edp_psr_do_enable(struct intel_dp* intel_dp)
 {
+	/* Setup PSR once */
+	intel_edp_psr_setup(intel_dp);
 
 	/* Enable PSR on the panel */
 	intel_edp_psr_enable_sink(intel_dp);
@@ -3370,6 +3429,8 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 	if (is_edp(intel_dp))
 		intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
 
+	intel_dp->psr_setup_done = false;
+
 	intel_dp_i2c_init(intel_dp, intel_connector, name);
 
 	/* Cache DPCD and EDID for edp. */
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index c6c8076..15e8023 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -481,6 +481,9 @@ struct intel_dp {
 	int backlight_on_delay;
 	int backlight_off_delay;
 	struct delayed_work panel_vdd_work;
+	struct delayed_work edp_psr_delayed_normal_work;
+	struct mutex psr_exit_mutex;
+	bool psr_setup_done;
 	bool want_panel_vdd;
 	struct intel_connector *attached_connector;
 };
@@ -812,5 +815,6 @@ extern bool intel_set_pch_fifo_underrun_reporting(struct drm_device *dev,
 extern void intel_edp_psr_enable(struct intel_dp* intel_dp);
 extern void intel_edp_psr_disable(struct intel_dp* intel_dp);
 extern void intel_edp_psr_update(struct drm_device *dev);
+extern void intel_edp_psr_force_exit(struct drm_device *dev);
 
 #endif /* __INTEL_DRV_H__ */
-- 
1.7.11.7



[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux