[Intel-gfx] [PATCH 2/2] drm/i915: Add Baytrail PSR Support.

Ville Syrjälä ville.syrjala at linux.intel.com
Fri Feb 7 18:24:01 CET 2014


On Wed, Feb 05, 2014 at 05:04:31PM -0200, Rodrigo Vivi wrote:
> This patch adds PSR Support to Baytrail.
> 
> 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 on next display mark_idle.
> 
> v2: Also inactivate PSR on cursor update.
> v3: Inactivate PSR on mark_busy, dset_domain and sw_finish_ioctl, and
>     early on page flip besides avoid initializing inactive/active flag
>     more than once.
> v4: Fix identation issues.
> v5: Rebase and add Baytrail per pipe support although leaving PIPE_B
>     support disabled by for now since it isn't working properly yet.
> v6: Removing forgotten comment and useless clkgating definition.
> v7: Remove inactivate from set_domain. Chris warned this was semanticaly
>     wrong.
> v8: Accept Ville's suggestions: Use register's names matching spec and
>     warn if transition took longer than it should.
> v9: New version with delayed work to get PSR back. Disabling it on
>     set_domain but not rescheduing it back until next finish_page_flip.
> 
> Cc: Chris Wilson <chris at chris-wilson.co.uk>
> Cc: Ville Syrjälä <ville.syrjala at linux.intel.com>
> Signed-off-by: Rodrigo Vivi <rodrigo.vivi at gmail.com>
> ---
>  drivers/gpu/drm/i915/i915_debugfs.c  |  36 ++++-
>  drivers/gpu/drm/i915/i915_drv.h      |   5 +-
>  drivers/gpu/drm/i915/i915_gem.c      |  12 ++
>  drivers/gpu/drm/i915/i915_reg.h      |  37 +++++
>  drivers/gpu/drm/i915/i915_suspend.c  |   2 +-
>  drivers/gpu/drm/i915/intel_display.c |  18 ++-
>  drivers/gpu/drm/i915/intel_dp.c      | 256 ++++++++++++++++++++++++++++++-----
>  drivers/gpu/drm/i915/intel_drv.h     |   1 +
>  8 files changed, 323 insertions(+), 44 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
> index bc8707f..2949c48 100644
> --- a/drivers/gpu/drm/i915/i915_debugfs.c
> +++ b/drivers/gpu/drm/i915/i915_debugfs.c
> @@ -1900,6 +1900,8 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
>  	struct drm_device *dev = node->minor->dev;
>  	struct drm_i915_private *dev_priv = dev->dev_private;
>  	u32 psrperf = 0;
> +	u32 statA = 0;
> +	u32 statB = 0;
>  	bool enabled = false;
>  
>  	intel_runtime_pm_get(dev_priv);
> @@ -1907,14 +1909,38 @@ static int i915_edp_psr_status(struct seq_file *m, void *data)
>  	seq_printf(m, "Sink_Support: %s\n", yesno(dev_priv->psr.sink_support));
>  	seq_printf(m, "Source_OK: %s\n", yesno(dev_priv->psr.source_ok));
>  
> -	enabled = HAS_PSR(dev) &&
> -		I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
> -	seq_printf(m, "Enabled: %s\n", yesno(enabled));
> +	if (HAS_PSR(dev)) {
> +		if (IS_VALLEYVIEW(dev)) {
> +			statA = I915_READ(VLV_PSRSTAT(PIPE_A)) &
> +				VLV_EDP_PSR_CURR_STATE_MASK;
> +			statB = I915_READ(VLV_PSRSTAT(PIPE_B)) &
> +				VLV_EDP_PSR_CURR_STATE_MASK;
> +			enabled = ((statA == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
> +				   (statA == VLV_EDP_PSR_ACTIVE_SF_UPDATE) ||
> +				   (statB == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
> +				   (statB == VLV_EDP_PSR_ACTIVE_SF_UPDATE));
> +		} else
> +			enabled = I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
> +	}
> +	seq_printf(m, "Enabled: %s", yesno(enabled));
>  
> -	if (HAS_PSR(dev))
> +	if (IS_VALLEYVIEW(dev)) {
> +		if ((statA == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
> +		    (statA == VLV_EDP_PSR_ACTIVE_SF_UPDATE))
> +			seq_puts(m, " pipe A");
> +		if ((statB == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
> +		    (statB == VLV_EDP_PSR_ACTIVE_SF_UPDATE))
> +			seq_puts(m, " pipe B");
> +	}
> +
> +	seq_puts(m, "\n");
> +
> +	/* VLV PSR has no kind of performance counter */
> +	if (HAS_PSR(dev) && !IS_VALLEYVIEW(dev)) {
>  		psrperf = I915_READ(EDP_PSR_PERF_CNT(dev)) &
>  			EDP_PSR_PERF_CNT_MASK;
> -	seq_printf(m, "Performance_Counter: %u\n", psrperf);
> +		seq_printf(m, "Performance_Counter: %u\n", psrperf);
> +	}
>  
>  	intel_runtime_pm_put(dev_priv);
>  	return 0;
> diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> index 21470be..87c346a 100644
> --- a/drivers/gpu/drm/i915/i915_drv.h
> +++ b/drivers/gpu/drm/i915/i915_drv.h
> @@ -747,7 +747,9 @@ struct i915_psr {
>  	bool sink_support;
>  	bool source_ok;
>  	bool setup_done;
> +	bool active;
>  	struct mutex lock;
> +	struct delayed_work work;
>  };
>  
>  enum intel_pch {
> @@ -1867,7 +1869,8 @@ struct drm_i915_file_private {
>  
>  #define HAS_DDI(dev)		(INTEL_INFO(dev)->has_ddi)
>  #define HAS_FPGA_DBG_UNCLAIMED(dev)	(INTEL_INFO(dev)->has_fpga_dbg)
> -#define HAS_PSR(dev)		(IS_HASWELL(dev) || IS_BROADWELL(dev))
> +#define HAS_PSR(dev)		(IS_HASWELL(dev) || IS_BROADWELL(dev) || \
> +				 IS_VALLEYVIEW(dev))
>  #define HAS_PC8(dev)		(IS_HASWELL(dev)) /* XXX HSW:ULX */
>  #define HAS_RUNTIME_PM(dev)	(IS_HASWELL(dev))
>  
> diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
> index 39770f7..5ef1380 100644
> --- a/drivers/gpu/drm/i915/i915_gem.c
> +++ b/drivers/gpu/drm/i915/i915_gem.c
> @@ -1256,6 +1256,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_inactivate(dev, false);

This will still block until PSR is deactivated (aux communication and
all). That still seems like a bad idea to do while holding struct_mutex.

> +
>  	/* 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.
> @@ -1299,6 +1307,8 @@ i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
>  	if (ret)
>  		return ret;
>  
> +	intel_edp_psr_inactivate(dev, true);
> +
>  	obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
>  	if (&obj->base == NULL) {
>  		ret = -ENOENT;
> @@ -4059,6 +4069,8 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
>  	if (ret)
>  		return ret;
>  
> +	intel_edp_psr_inactivate(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/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index cbbaf26..a5815d0 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -1968,6 +1968,43 @@
>  #define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
>  #define VSYNCSHIFT(trans) _TRANSCODER(trans, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
>  
> +/* VLV eDP PSR registers */
> +#define _PSRCTLA				(VLV_DISPLAY_BASE + 0x60090)
> +#define _PSRCTLB				(VLV_DISPLAY_BASE + 0x61090)
> +#define  VLV_EDP_PSR_ENABLE			(1<<0)
> +#define  VLV_EDP_PSR_RESET			(1<<1)
> +#define  VLV_EDP_PSR_MODE_MASK			(7<<2)
> +#define  VLV_EDP_PSR_MODE_HW_TIMER		(1<<3)
> +#define  VLV_EDP_PSR_MODE_SW_TIMER		(1<<2)
> +#define  VLV_EDP_PSR_SINGLE_FRAME_UPDATE	(1<<7)
> +#define  VLV_EDP_PSR_ACTIVE_ENTRY		(1<<8)
> +#define  VLV_EDP_PSR_SRC_TRANSMITTER_STATE	(1<<9)
> +#define  VLV_EDP_PSR_DBL_FRAME			(1<<10)
> +#define  VLV_EDP_PSR_FRAME_COUNT_MASK		(0xff<<16)
> +#define  VLV_EDP_PSR_IDLE_FRAME_SHIFT		16
> +#define  VLV_EDP_PSR_INT_TRANSITION		(1<<24)
> +#define VLV_PSRCTL(pipe) _PIPE(pipe, _PSRCTLA, _PSRCTLB)
> +
> +#define _VSCSDPA			(VLV_DISPLAY_BASE + 0x600a0)
> +#define _VSCSDPB			(VLV_DISPLAY_BASE + 0x610a0)
> +#define  VLV_EDP_PSR_SDP_FREQ_MASK	(3<<30)
> +#define  VLV_EDP_PSR_SDP_FREQ_ONCE	(1<<31)
> +#define  VLV_EDP_PSR_SDP_FREQ_EVFRAME	(1<<30)
> +#define VLV_VSCSDP(pipe)	_PIPE(pipe, _VSCSDPA, _VSCSDPB)
> +
> +#define _PSRSTATA			(VLV_DISPLAY_BASE + 0x60094)
> +#define _PSRSTATB			(VLV_DISPLAY_BASE + 0x61094)
> +#define  VLV_EDP_PSR_LAST_STATE_MASK	(7<<3)
> +#define  VLV_EDP_PSR_CURR_STATE_MASK	7
> +#define  VLV_EDP_PSR_DISABLED		(0<<0)
> +#define  VLV_EDP_PSR_INACTIVE		(1<<0)
> +#define  VLV_EDP_PSR_IN_TRANS_TO_ACTIVE	(2<<0)
> +#define  VLV_EDP_PSR_ACTIVE_NORFB_UP	(3<<0)
> +#define  VLV_EDP_PSR_ACTIVE_SF_UPDATE	(4<<0)
> +#define  VLV_EDP_PSR_EXIT		(5<<0)
> +#define  VLV_EDP_PSR_IN_TRANS		(1<<7)
> +#define VLV_PSRSTAT(pipe) _PIPE(pipe, _PSRSTATA, _PSRSTATB)
> +
>  /* HSW+ eDP PSR registers */
>  #define EDP_PSR_BASE(dev)                       (IS_HASWELL(dev) ? 0x64800 : 0x6f800)
>  #define EDP_PSR_CTL(dev)			(EDP_PSR_BASE(dev) + 0)
> diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
> index ffcba21..6b31020 100644
> --- a/drivers/gpu/drm/i915/i915_suspend.c
> +++ b/drivers/gpu/drm/i915/i915_suspend.c
> @@ -289,8 +289,8 @@ static void i915_restore_display(struct drm_device *dev)
>  	}
>  
>  	/* Force a full PSR setup on resume */
> +	intel_edp_psr_inactivate(dev, false);
>  	dev_priv->psr.setup_done = false;
> -	intel_edp_psr_update(dev);
>  
>  	/* only restore FBC info on the platform that supports FBC*/
>  	intel_disable_fbc(dev);
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index 1a9aa19..92d6df4 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -4451,9 +4451,13 @@ 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;
> +
>  	/* All the simple cases only support two dpms states. */
> -	if (mode != DRM_MODE_DPMS_ON)
> +	if (mode != DRM_MODE_DPMS_ON) {
>  		mode = DRM_MODE_DPMS_OFF;
> +		intel_edp_psr_inactivate(dev, false);

Maybe check if this connector even has PSR enabled?

> +	}
>  
>  	if (mode == connector->dpms)
>  		return;
> @@ -7501,6 +7505,8 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
>  	u32 base = 0, pos = 0;
>  	bool visible;
>  
> +	intel_edp_psr_inactivate(dev, true);

Don't need to inactivate unless the cursor is or was visible.

> +
>  	if (on)
>  		base = intel_crtc->cursor_addr;
>  
> @@ -8230,12 +8236,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_inactivate(dev, true);
> +
>  	if (!i915.powersave)
>  		return;
>  
> @@ -8336,6 +8345,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. */
> +	schedule_delayed_work(&dev_priv->psr.work, msecs_to_jiffies(5000));
>  }
>  
>  void intel_finish_page_flip(struct drm_device *dev, int pipe)
> @@ -8688,6 +8701,9 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
>  	if (work == NULL)
>  		return -ENOMEM;
>  
> +	/* Inactivate PSR early in page flip */
> +	intel_edp_psr_inactivate(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 80054bb..576c204 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -1578,21 +1578,35 @@ static void intel_dp_get_config(struct intel_encoder *encoder,
>  	}
>  }
>  
> -static bool is_edp_psr(struct drm_device *dev)
> +static bool is_edp_psr(struct intel_dp *intel_dp)
> +{
> +	return intel_dp->psr_dpcd[0] & DP_PSR_IS_SUPPORTED;
> +}
> +
> +static bool vlv_edp_is_psr_enabled_on_pipe(struct drm_device *dev, int pipe)
>  {
>  	struct drm_i915_private *dev_priv = dev->dev_private;
> +	uint32_t val;
>  
> -	return dev_priv->psr.sink_support;
> +	val = I915_READ(VLV_PSRSTAT(pipe)) &
> +		VLV_EDP_PSR_CURR_STATE_MASK;
> +	return (val == VLV_EDP_PSR_ACTIVE_NORFB_UP) ||
> +		(val == VLV_EDP_PSR_ACTIVE_SF_UPDATE);
>  }
>  
>  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;
> +	if (HAS_PSR(dev)) {
> +		if (IS_VALLEYVIEW(dev))
> +			return vlv_edp_is_psr_enabled_on_pipe(dev, PIPE_A) ||
> +				vlv_edp_is_psr_enabled_on_pipe(dev, PIPE_B);
> +		else
> +			return I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
> +	}
>  
> -	return I915_READ(EDP_PSR_CTL(dev)) & EDP_PSR_ENABLE;
> +	return false;
>  }
>  
>  static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
> @@ -1624,32 +1638,60 @@ static void intel_edp_psr_write_vsc(struct intel_dp *intel_dp,
>  	POSTING_READ(ctl_reg);
>  }
>  
> +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 drm_device *dev = dev_priv->dev;
> +
> +	intel_edp_psr_update(dev);
> +}
> +
>  static void intel_edp_psr_setup(struct intel_dp *intel_dp)
>  {
> -	struct drm_device *dev = intel_dp_to_dev(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;
>  	struct edp_vsc_psr psr_vsc;
> +	uint32_t val;
>  
>  	if (dev_priv->psr.setup_done)
>  		return;
>  
> -	mutex_init(&dev_priv->psr.lock);
> -
> -	/* 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);
> -
> -	/* 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);
> +	if (IS_VALLEYVIEW(dev)) {
> +		val  = I915_READ(VLV_VSCSDP(PIPE_A));
> +		val &= ~VLV_EDP_PSR_SDP_FREQ_MASK;
> +		val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME;
> +		I915_WRITE(VLV_VSCSDP(PIPE_A), val);
> +
> +		val  = I915_READ(VLV_VSCSDP(PIPE_B));
> +		val &= ~VLV_EDP_PSR_SDP_FREQ_MASK;
> +		val |= VLV_EDP_PSR_SDP_FREQ_EVFRAME;
> +		I915_WRITE(VLV_VSCSDP(PIPE_B), val);
> +	} else {
> +		/* 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);
> +
> +		/* 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);
> +	}
>  
>  	dev_priv->psr.setup_done = true;
>  }
>  
> +static void vlv_edp_psr_enable_sink(struct intel_dp *intel_dp)
> +{
> +	/* Enable PSR in sink */
> +	intel_dp_aux_native_write_1(intel_dp, DP_PSR_EN_CFG,
> +					    DP_PSR_ENABLE);
> +}
> +
>  static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
>  {
>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> @@ -1680,6 +1722,27 @@ static void intel_edp_psr_enable_sink(struct intel_dp *intel_dp)
>  		   (aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT));
>  }
>  
> +static void vlv_edp_psr_enable_source(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;
> +	struct intel_crtc *intel_crtc =
> +		to_intel_crtc(intel_dig_port->base.base.crtc);
> +
> +	uint32_t idle_frames = 1;
> +	uint32_t val = 0;
> +
> +	val |= VLV_EDP_PSR_ENABLE;
> +	val &= ~VLV_EDP_PSR_MODE_MASK;
> +
> +	val |= VLV_EDP_PSR_MODE_HW_TIMER;
> +	val &= ~VLV_EDP_PSR_FRAME_COUNT_MASK;
> +	val |= idle_frames << EDP_PSR_IDLE_FRAME_SHIFT;
> +
> +	I915_WRITE(VLV_PSRCTL(intel_crtc->pipe), val);
> +}
> +
>  static void intel_edp_psr_enable_source(struct intel_dp *intel_dp)
>  {
>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> @@ -1721,8 +1784,8 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
>  		return false;
>  	}
>  
> -	if ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
> -	    (dig_port->port != PORT_A)) {
> +	if (HAS_DDI(dev) && ((intel_encoder->type != INTEL_OUTPUT_EDP) ||
> +			     (dig_port->port != PORT_A))) {
>  		DRM_DEBUG_KMS("HSW ties PSR to DDI A (eDP)\n");
>  		return false;
>  	}
> @@ -1767,23 +1830,45 @@ static bool intel_edp_psr_match_conditions(struct intel_dp *intel_dp)
>  		return false;
>  	}
>  
> +	/* Baytrail supports per-pipe PSR configuration, however PSR on
> +	* PIPE_B isn't working properly. So let's keep it disabled for now. */
> +	if (IS_VALLEYVIEW(dev) && intel_crtc->pipe != PIPE_A) {
> +		DRM_DEBUG_KMS("PSR on BYT isn't enabled on pipe B.\n");
> +		return false;
> +	}
> +
>  	dev_priv->psr.source_ok = true;
>  	return true;
>  }
>  
>  static void intel_edp_psr_do_enable(struct intel_dp *intel_dp)
>  {
> -	struct drm_device *dev = intel_dp_to_dev(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;
> +	struct intel_crtc *intel_crtc =
> +		to_intel_crtc(intel_dig_port->base.base.crtc);
>  
> -	if (!intel_edp_psr_match_conditions(intel_dp) ||
> -	    intel_edp_is_psr_enabled(dev))
> -		return;
> +	if (IS_VALLEYVIEW(dev)) {
> +		if (vlv_edp_is_psr_enabled_on_pipe(dev, intel_crtc->pipe))
> +			return;
> +	} else
> +		if (intel_edp_is_psr_enabled(dev))
> +			return;
>  
>  	/* Enable PSR on the panel */
> -	intel_edp_psr_enable_sink(intel_dp);
> +	if (IS_VALLEYVIEW(dev))
> +		vlv_edp_psr_enable_sink(intel_dp);
> +	else
> +		intel_edp_psr_enable_sink(intel_dp);
>  
>  	/* Enable PSR on the host */
> -	intel_edp_psr_enable_source(intel_dp);
> +	if (IS_VALLEYVIEW(dev))
> +		vlv_edp_psr_enable_source(intel_dp);
> +	else
> +		intel_edp_psr_enable_source(intel_dp);
> +
> +	dev_priv->psr.active = true;
>  }
>  
>  void intel_edp_psr_enable(struct intel_dp *intel_dp)
> @@ -1791,16 +1876,43 @@ 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 (!is_edp_psr(intel_dp))
> +		return;
> +
>  	/* Setup PSR once */
>  	intel_edp_psr_setup(intel_dp);
>  
>  	mutex_lock(&dev_priv->psr.lock);
> -	if (intel_edp_psr_match_conditions(intel_dp) &&
> -	    !intel_edp_is_psr_enabled(dev))
> +	if (intel_edp_psr_match_conditions(intel_dp))
>  		intel_edp_psr_do_enable(intel_dp);
>  	mutex_unlock(&dev_priv->psr.lock);
>  }
>  
> +void vlv_edp_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;
> +	struct intel_crtc *intel_crtc =
> +		to_intel_crtc(intel_dig_port->base.base.crtc);
> +	uint32_t val;
> +
> +	if (!dev_priv->psr.setup_done)
> +		return;
> +
> +	intel_edp_psr_inactivate(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");
> +
> +	val = I915_READ(VLV_PSRCTL(intel_crtc->pipe));
> +	val &= ~VLV_EDP_PSR_ACTIVE_ENTRY;
> +	val &= ~VLV_EDP_PSR_ENABLE;
> +	val &= ~VLV_EDP_PSR_MODE_MASK;
> +	I915_WRITE(VLV_PSRCTL(intel_crtc->pipe), val);
> +}
> +
>  void intel_edp_psr_disable(struct intel_dp *intel_dp)
>  {
>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> @@ -1823,6 +1935,7 @@ void intel_edp_psr_update(struct drm_device *dev)
>  	struct drm_i915_private *dev_priv = dev->dev_private;
>  	struct intel_encoder *encoder;
>  	struct intel_dp *intel_dp = NULL;
> +	struct intel_crtc *intel_crtc;
>  
>  	if (!dev_priv->psr.setup_done)
>  		return;
> @@ -1830,26 +1943,93 @@ void intel_edp_psr_update(struct drm_device *dev)
>  	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 (!is_edp_psr(dev))
> -				return;
> +			intel_crtc = to_intel_crtc(encoder->base.crtc);
>  
>  			mutex_lock(&dev_priv->psr.lock);
> -			if (!intel_edp_psr_match_conditions(intel_dp))
> -				intel_edp_psr_disable(intel_dp);
> -			else
> +			if (!intel_edp_psr_match_conditions(intel_dp)) {
> +				if (IS_VALLEYVIEW(dev))
> +					vlv_edp_psr_disable(intel_dp);
> +				else
> +					intel_edp_psr_disable(intel_dp);
> +			} else
>  				if (!intel_edp_is_psr_enabled(dev))
>  					intel_edp_psr_do_enable(intel_dp);
>  			mutex_unlock(&dev_priv->psr.lock);
>  		}
>  }
>  
> +void intel_edp_psr_do_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);

Still digging through the encoder and crtc pointers w/o holding any
modeset locks. Feels dangerous. intel_edp_psr_match_conditions() does
the same kind of stuff, and that too now gets called outside modeset
codepaths.

> +
> +			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_inactivate(struct drm_device *dev, bool schedule_back)
> +{
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +
> +	if (!IS_VALLEYVIEW(dev))
> +		return;
> +
> +	if (!dev_priv->psr.setup_done)
> +		return;
> +
> +	/* If it was requested to not turn psr back delayed work must be
> +	 * canceled even if it is already on inactivated state. */
> +	if (!schedule_back)
> +		cancel_delayed_work_sync(&dev_priv->psr.work);
> +
> +	if (!dev_priv->psr.active)
> +		return;

What if psr gets re-activated just after you checked above?

> +
> +	cancel_delayed_work_sync(&dev_priv->psr.work);
> +
> +	intel_edp_psr_do_inactivate(dev);
> +
> +	if (schedule_back)
> +		schedule_delayed_work(&dev_priv->psr.work,
> +				      msecs_to_jiffies(5000));
> +}
> +
>  static void intel_disable_dp(struct intel_encoder *encoder)
>  {
>  	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
>  	enum port port = dp_to_dig_port(intel_dp)->port;
>  	struct drm_device *dev = encoder->base.dev;
>  
> +	if (IS_VALLEYVIEW(dev))
> +		vlv_edp_psr_disable(intel_dp);
> +
>  	/* Make sure the panel is off before trying to change the mode. But also
>  	 * ensure that we have vdd while we switch off the panel. */
>  	intel_edp_backlight_off(intel_dp);
> @@ -1906,6 +2086,7 @@ static void vlv_enable_dp(struct intel_encoder *encoder)
>  	struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
>  
>  	intel_edp_backlight_on(intel_dp);
> +	intel_edp_psr_enable(intel_dp);
>  }
>  
>  static void g4x_pre_enable_dp(struct intel_encoder *encoder)
> @@ -3846,8 +4027,11 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
>  		BUG();
>  	}
>  
> -	if (is_edp(intel_dp))
> +	if (is_edp(intel_dp)) {
>  		intel_dp_init_panel_power_sequencer(dev, intel_dp, &power_seq);
> +		INIT_DELAYED_WORK(&dev_priv->psr.work, intel_edp_psr_work);
> +		mutex_init(&dev_priv->psr.lock);

Initializing global state from a per-connector init function seems a bit
weird.

> +	}
>  
>  	error = intel_dp_i2c_init(intel_dp, intel_connector, name);
>  	WARN(error, "intel_dp_i2c_init failed with error %d for port %c\n",
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 71c1371..d8103f9 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -748,6 +748,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_inactivate(struct drm_device *dev, bool schedule_back);
>  
>  
>  /* intel_dsi.c */
> -- 
> 1.8.1.2

-- 
Ville Syrjälä
Intel OTC



More information about the Intel-gfx mailing list