[PATCH 10/11] drm/i915: PSR Baytrail: Force exit by inactivating it.

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

 



Baytrail cannot easily detect screen updates and force PSR exit.
So we inactivate it on {busy_ioctl, set_domain, sw_finish and mark_busy}
and update to enable it back it later with a delayed workqueue.

Cc: Chris Wilson <chris@xxxxxxxxxxxxxxxxxx>
Cc: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@xxxxxxxxx>
---
 drivers/gpu/drm/i915/i915_drv.h      |  2 +
 drivers/gpu/drm/i915/i915_gem.c      | 12 +++++
 drivers/gpu/drm/i915/intel_display.c | 24 ++++++++-
 drivers/gpu/drm/i915/intel_dp.c      | 99 +++++++++++++++++++++++++++++++++---
 drivers/gpu/drm/i915/intel_drv.h     |  1 +
 5 files changed, 128 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 18f457e..a4e666c 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -785,6 +785,8 @@ struct i915_psr {
 	bool sink_support;
 	bool source_ok;
 	bool setup_done;
+	bool active;
+	struct delayed_work work;
 	void (*setup)(struct intel_dp *intel_dp);
 	void (*enable_sink)(struct intel_dp *intel_dp);
 	void (*enable_source)(struct intel_dp *intel_dp);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 3618bb0..c6bfc81 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1251,6 +1251,14 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
 		goto unlock;
 	}
 
+	/* Here we don't reschedule work to get PSR back because userspace
+	* can set domain and take as much as time it wants to write it.
+	* We only reschedule it back on next finish_page_flip. There is the
+	* risk of PSR be inactive forever depending on how compositor works,
+	* but this is the safest way to avoid loosing screen updates.
+	*/
+	intel_edp_psr_exit(dev, false);
+
 	/* Try to flush the object off the GPU without holding the lock.
 	 * We will repeat the flush holding the lock in the normal manner
 	 * to catch cases where we are gazumped.
@@ -1296,6 +1304,8 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
 	if (ret)
 		return ret;
 
+	intel_edp_psr_exit(dev, true);
+
 	obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
 	if (&obj->base == NULL) {
 		ret = -ENOENT;
@@ -3982,6 +3992,8 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
 	if (ret)
 		return ret;
 
+	intel_edp_psr_exit(dev, true);
+
 	obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
 	if (&obj->base == NULL) {
 		ret = -ENOENT;
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 26196b8..5e4c768 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4433,6 +4433,8 @@ static void intel_connector_check_state(struct intel_connector *connector)
  * consider. */
 void intel_connector_dpms(struct drm_connector *connector, int mode)
 {
+	struct drm_device *dev = connector->dev;
+	struct intel_encoder *intel_encoder;
 	/* All the simple cases only support two dpms states. */
 	if (mode != DRM_MODE_DPMS_ON)
 		mode = DRM_MODE_DPMS_OFF;
@@ -4443,9 +4445,15 @@ void intel_connector_dpms(struct drm_connector *connector, int mode)
 	connector->dpms = mode;
 
 	/* Only need to change hw state when actually enabled */
-	if (connector->encoder)
-		intel_encoder_dpms(to_intel_encoder(connector->encoder), mode);
+	if (connector->encoder) {
+
+		intel_encoder = to_intel_encoder(connector->encoder);
+		intel_encoder_dpms(intel_encoder, mode);
 
+		if (intel_encoder->type == INTEL_OUTPUT_EDP &&
+		    mode == DRM_MODE_DPMS_OFF)
+			intel_edp_psr_exit(dev, false);
+	}
 	intel_modeset_check_state(connector->dev);
 }
 
@@ -7512,6 +7520,8 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
 	if (!visible && !intel_crtc->cursor_visible)
 		return;
 
+	intel_edp_psr_exit(dev, true);
+
 	if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev) || IS_BROADWELL(dev)) {
 		I915_WRITE(CURPOS_IVB(pipe), pos);
 		ivb_update_cursor(crtc, base);
@@ -8224,12 +8234,15 @@ void intel_mark_idle(struct drm_device *dev)
 		gen6_rps_idle(dev->dev_private);
 }
 
+
 void intel_mark_fb_busy(struct drm_i915_gem_object *obj,
 			struct intel_ring_buffer *ring)
 {
 	struct drm_device *dev = obj->base.dev;
 	struct drm_crtc *crtc;
 
+	intel_edp_psr_exit(dev, true);
+
 	if (!i915.powersave)
 		return;
 
@@ -8330,6 +8343,10 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
 	queue_work(dev_priv->wq, &work->work);
 
 	trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj);
+
+	/* Get PSR back after set_domain inactivated it
+	 * without rescheduling it back. */
+	intel_edp_psr_update(dev);
 }
 
 void intel_finish_page_flip(struct drm_device *dev, int pipe)
@@ -8696,6 +8713,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
 	if (work == NULL)
 		return -ENOMEM;
 
+	/* Exit PSR early in page flip */
+	intel_edp_psr_exit(dev, true);
+
 	work->event = event;
 	work->crtc = crtc;
 	work->old_fb_obj = to_intel_framebuffer(old_fb)->obj;
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 3adc127..f386860 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1883,6 +1883,8 @@ static void intel_edp_psr_do_enable(struct intel_dp *intel_dp)
 
 	/* Enable PSR on the host */
 	dev_priv->psr.enable_source(intel_dp);
+
+	dev_priv->psr.active = true;
 }
 
 void intel_edp_psr_enable(struct intel_dp *intel_dp)
@@ -1919,6 +1921,8 @@ void vlv_edp_psr_disable(struct intel_dp *intel_dp)
 	if (!dev_priv->psr.setup_done)
 		return;
 
+	intel_edp_psr_exit(dev, false);
+
 	if (wait_for((I915_READ(VLV_PSRSTAT(intel_crtc->pipe)) &
 		      VLV_EDP_PSR_IN_TRANS) == 0, 250))
 		WARN(1, "PSR transition took longer than expected\n");
@@ -1947,18 +1951,11 @@ void intel_edp_psr_disable(struct intel_dp *intel_dp)
 		DRM_ERROR("Timed out waiting for PSR Idle State\n");
 }
 
-void intel_edp_psr_update(struct drm_device *dev)
+void intel_edp_psr_do_update(struct drm_device *dev)
 {
-	struct drm_i915_private *dev_priv = dev->dev_private;
 	struct intel_encoder *encoder;
 	struct intel_dp *intel_dp = NULL;
 
-	if (!HAS_PSR(dev))
-		return;
-
-	if (!dev_priv->psr.setup_done)
-		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);
@@ -1973,6 +1970,90 @@ void intel_edp_psr_update(struct drm_device *dev)
 		}
 }
 
+void intel_edp_psr_update(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (!HAS_PSR(dev))
+		return;
+
+	if (!dev_priv->psr.setup_done)
+		return;
+
+	if (IS_VALLEYVIEW(dev))
+		intel_edp_psr_exit(dev, true);
+	else
+		intel_edp_psr_do_update(dev);
+}
+
+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 drm_device *dev = dev_priv->dev;
+
+	intel_edp_psr_do_update(dev);
+}
+
+void intel_edp_psr_inactivate(struct drm_device *dev)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_connector *connector;
+	struct intel_encoder *encoder;
+	struct intel_crtc *intel_crtc;
+	struct intel_dp *intel_dp = NULL;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list,
+			    base.head) {
+
+		if (connector->base.dpms != DRM_MODE_DPMS_ON)
+			continue;
+
+		encoder = to_intel_encoder(connector->base.encoder);
+		if (encoder->type == INTEL_OUTPUT_EDP) {
+
+			intel_dp = enc_to_intel_dp(&encoder->base);
+			intel_crtc = to_intel_crtc(encoder->base.crtc);
+
+			if (!vlv_edp_is_psr_enabled_on_pipe(dev,
+							    intel_crtc->pipe))
+				continue;
+
+			dev_priv->psr.active = false;
+
+			I915_WRITE(VLV_PSRCTL(intel_crtc->pipe),
+				   VLV_EDP_PSR_RESET);
+			/* WaClearPSRReset:vlv */
+			I915_WRITE(VLV_PSRCTL(intel_crtc->pipe), 0);
+
+			intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+		}
+	}
+}
+
+void intel_edp_psr_exit(struct drm_device *dev, bool schedule_back)
+{
+	struct drm_i915_private *dev_priv = dev->dev_private;
+
+	if (!HAS_PSR(dev))
+		return;
+
+	if (!IS_VALLEYVIEW(dev))
+		return;
+
+	if (!dev_priv->psr.setup_done)
+		return;
+
+	cancel_delayed_work_sync(&dev_priv->psr.work);
+
+	if (dev_priv->psr.active)
+		intel_edp_psr_inactivate(dev);
+
+	if (schedule_back)
+		schedule_delayed_work(&dev_priv->psr.work,
+				      msecs_to_jiffies(5000));
+}
+
 void intel_edp_psr_init(struct drm_device *dev)
 {
 	struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1980,6 +2061,8 @@ void intel_edp_psr_init(struct drm_device *dev)
 	if (!HAS_PSR(dev))
 		return;
 
+	INIT_DELAYED_WORK(&dev_priv->psr.work, intel_edp_psr_work);
+
 	if (IS_VALLEYVIEW(dev)) {
 		dev_priv->psr.setup = vlv_edp_psr_setup;
 		dev_priv->psr.enable_sink = vlv_edp_psr_enable_sink;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 2e4516d..47b8c9f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -757,6 +757,7 @@ 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_edp_psr_update(struct drm_device *dev);
+void intel_edp_psr_exit(struct drm_device *dev, bool schedule_back);
 void intel_edp_psr_init(struct drm_device *dev);
 
 
-- 
1.8.3.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/intel-gfx





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