All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Ville Syrjälä" <ville.syrjala@linux.intel.com>
To: Paulo Zanoni <przanoni@gmail.com>
Cc: intel-gfx@lists.freedesktop.org, Paulo Zanoni <paulo.r.zanoni@intel.com>
Subject: Re: [PATCH 1/3] drm/i915: properly set HSW WM_PIPE registers
Date: Fri, 31 May 2013 18:03:00 +0300	[thread overview]
Message-ID: <20130531150300.GW5004@intel.com> (raw)
In-Reply-To: <1370005715-2885-1-git-send-email-przanoni@gmail.com>

On Fri, May 31, 2013 at 10:08:35AM -0300, Paulo Zanoni wrote:
> From: Paulo Zanoni <paulo.r.zanoni@intel.com>
> 
> We were previously calling sandybridge_update_wm on HSW, but the SNB
> function didn't really match the HSW specification, so we were just
> writing the wrong values.
> 
> With this patch, the haswell_update_wm function will set the correct
> values for the WM_PIPE registers, but it will still keep all the LP
> watermarks disabled.
> 
> The patch may look a little bit over-complicated for now, but it's
> because much of the infrastructure for setting the LP watermarks is
> already in place, so we won't have too much code churn on the patch
> that sets the LP watermarks.
> 
> v2: - Fix pixel_rate on panel fitter case (Ville)
>     - Try to not overflow (Ville)
>     - Remove useless variable (Ville)
>     - Fix p->pri_horiz_pixels (Paulo)
> v3: - Fix rounding errors on hsw_wm_method2 (Ville)
> v4: - Fix memcmp bug (Paulo)
> 
> Signed-off-by: Paulo Zanoni <paulo.r.zanoni@intel.com>
> ---
>  drivers/gpu/drm/i915/i915_reg.h |   3 +
>  drivers/gpu/drm/i915/intel_pm.c | 342 +++++++++++++++++++++++++++++++++++++---
>  2 files changed, 327 insertions(+), 18 deletions(-)
> 
> 
> While doing some more tests I found a memcmp bug that can be reproduced with
> some 2-screen configurations. This patch fixes it.

Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>

and shame on me for not catching it during review.

> 
> 
> diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
> index dbd9de5..5a49f8a 100644
> --- a/drivers/gpu/drm/i915/i915_reg.h
> +++ b/drivers/gpu/drm/i915/i915_reg.h
> @@ -4931,6 +4931,9 @@
>  #define  SFUSE_STRAP_DDIC_DETECTED	(1<<1)
>  #define  SFUSE_STRAP_DDID_DETECTED	(1<<0)
>  
> +#define WM_MISC				0x45260
> +#define  WM_MISC_DATA_PARTITION_5_6	(1 << 0)
> +
>  #define WM_DBG				0x45280
>  #define  WM_DBG_DISALLOW_MULTIPLE_LP	(1<<0)
>  #define  WM_DBG_DISALLOW_MAXFIFO	(1<<1)
> diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
> index 9328ed9..fda7279 100644
> --- a/drivers/gpu/drm/i915/intel_pm.c
> +++ b/drivers/gpu/drm/i915/intel_pm.c
> @@ -2072,19 +2072,170 @@ static void ivybridge_update_wm(struct drm_device *dev)
>  		   cursor_wm);
>  }
>  
> -static void
> -haswell_update_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc)
> +static uint32_t hsw_wm_get_pixel_rate(struct drm_device *dev,
> +				      struct drm_crtc *crtc)
> +{
> +	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
> +	uint32_t pixel_rate, pfit_size;
> +
> +	if (intel_crtc->config.pixel_target_clock)
> +		pixel_rate = intel_crtc->config.pixel_target_clock;
> +	else
> +		pixel_rate = intel_crtc->config.adjusted_mode.clock;
> +
> +	/* We only use IF-ID interlacing. If we ever use PF-ID we'll need to
> +	 * adjust the pixel_rate here. */
> +
> +	pfit_size = intel_crtc->config.pch_pfit.size;
> +	if (pfit_size) {
> +		uint64_t pipe_w, pipe_h, pfit_w, pfit_h;
> +
> +		pipe_w = intel_crtc->config.requested_mode.hdisplay;
> +		pipe_h = intel_crtc->config.requested_mode.vdisplay;
> +		pfit_w = (pfit_size >> 16) & 0xFFFF;
> +		pfit_h = pfit_size & 0xFFFF;
> +		if (pipe_w < pfit_w)
> +			pipe_w = pfit_w;
> +		if (pipe_h < pfit_h)
> +			pipe_h = pfit_h;
> +
> +		pixel_rate = div_u64((uint64_t) pixel_rate * pipe_w * pipe_h,
> +				     pfit_w * pfit_h);
> +	}
> +
> +	return pixel_rate;
> +}
> +
> +static uint32_t hsw_wm_method1(uint32_t pixel_rate, uint8_t bytes_per_pixel,
> +			       uint32_t latency)
> +{
> +	uint64_t ret;
> +
> +	ret = (uint64_t) pixel_rate * bytes_per_pixel * latency;
> +	ret = DIV_ROUND_UP_ULL(ret, 64 * 10000) + 2;
> +
> +	return ret;
> +}
> +
> +static uint32_t hsw_wm_method2(uint32_t pixel_rate, uint32_t pipe_htotal,
> +			       uint32_t horiz_pixels, uint8_t bytes_per_pixel,
> +			       uint32_t latency)
> +{
> +	uint32_t ret;
> +
> +	ret = (latency * pixel_rate) / (pipe_htotal * 10000);
> +	ret = (ret + 1) * horiz_pixels * bytes_per_pixel;
> +	ret = DIV_ROUND_UP(ret, 64) + 2;
> +	return ret;
> +}
> +
> +struct hsw_pipe_wm_parameters {
> +	bool active;
> +	bool sprite_enabled;
> +	uint8_t pri_bytes_per_pixel;
> +	uint8_t spr_bytes_per_pixel;
> +	uint8_t cur_bytes_per_pixel;
> +	uint32_t pri_horiz_pixels;
> +	uint32_t spr_horiz_pixels;
> +	uint32_t cur_horiz_pixels;
> +	uint32_t pipe_htotal;
> +	uint32_t pixel_rate;
> +};
> +
> +struct hsw_wm_values {
> +	uint32_t wm_pipe[3];
> +	uint32_t wm_lp[3];
> +	uint32_t wm_lp_spr[3];
> +	uint32_t wm_linetime[3];
> +};
> +
> +enum hsw_data_buf_partitioning {
> +	HSW_DATA_BUF_PART_1_2,
> +	HSW_DATA_BUF_PART_5_6,
> +};
> +
> +/* Only for WM_PIPE. */
> +static uint32_t hsw_compute_pri_wm_pipe(struct hsw_pipe_wm_parameters *params,
> +					uint32_t mem_value)
> +{
> +	/* TODO: for now, assume the primary plane is always enabled. */
> +	if (!params->active)
> +		return 0;
> +
> +	return hsw_wm_method1(params->pixel_rate,
> +			      params->pri_bytes_per_pixel,
> +			      mem_value);
> +}
> +
> +/* For both WM_PIPE and WM_LP. */
> +static uint32_t hsw_compute_spr_wm(struct hsw_pipe_wm_parameters *params,
> +				   uint32_t mem_value)
> +{
> +	uint32_t method1, method2;
> +
> +	if (!params->active || !params->sprite_enabled)
> +		return 0;
> +
> +	method1 = hsw_wm_method1(params->pixel_rate,
> +				 params->spr_bytes_per_pixel,
> +				 mem_value);
> +	method2 = hsw_wm_method2(params->pixel_rate,
> +				 params->pipe_htotal,
> +				 params->spr_horiz_pixels,
> +				 params->spr_bytes_per_pixel,
> +				 mem_value);
> +	return min(method1, method2);
> +}
> +
> +/* For both WM_PIPE and WM_LP. */
> +static uint32_t hsw_compute_cur_wm(struct hsw_pipe_wm_parameters *params,
> +				   uint32_t mem_value)
> +{
> +	if (!params->active)
> +		return 0;
> +
> +	return hsw_wm_method2(params->pixel_rate,
> +			      params->pipe_htotal,
> +			      params->cur_horiz_pixels,
> +			      params->cur_bytes_per_pixel,
> +			      mem_value);
> +}
> +
> +static uint32_t hsw_compute_wm_pipe(struct drm_i915_private *dev_priv,
> +				    uint32_t mem_value, enum pipe pipe,
> +				    struct hsw_pipe_wm_parameters *params)
> +{
> +	uint32_t pri_val, cur_val, spr_val;
> +
> +	pri_val = hsw_compute_pri_wm_pipe(params, mem_value);
> +	spr_val = hsw_compute_spr_wm(params, mem_value);
> +	cur_val = hsw_compute_cur_wm(params, mem_value);
> +
> +	WARN(pri_val > 127,
> +	     "Primary WM error, mode not supported for pipe %c\n",
> +	     pipe_name(pipe));
> +	WARN(spr_val > 127,
> +	     "Sprite WM error, mode not supported for pipe %c\n",
> +	     pipe_name(pipe));
> +	WARN(cur_val > 63,
> +	     "Cursor WM error, mode not supported for pipe %c\n",
> +	     pipe_name(pipe));
> +
> +	return (pri_val << WM0_PIPE_PLANE_SHIFT) |
> +	       (spr_val << WM0_PIPE_SPRITE_SHIFT) |
> +	       cur_val;
> +}
> +
> +static uint32_t
> +hsw_compute_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc)
>  {
>  	struct drm_i915_private *dev_priv = dev->dev_private;
>  	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
> -	enum pipe pipe = intel_crtc->pipe;
>  	struct drm_display_mode *mode = &intel_crtc->config.adjusted_mode;
>  	u32 linetime, ips_linetime;
>  
> -	if (!intel_crtc_active(crtc)) {
> -		I915_WRITE(PIPE_WM_LINETIME(pipe), 0);
> -		return;
> -	}
> +	if (!intel_crtc_active(crtc))
> +		return 0;
>  
>  	/* The WM are computed with base on how long it takes to fill a single
>  	 * row at the given clock rate, multiplied by 8.
> @@ -2093,29 +2244,184 @@ haswell_update_linetime_wm(struct drm_device *dev, struct drm_crtc *crtc)
>  	ips_linetime = DIV_ROUND_CLOSEST(mode->htotal * 1000 * 8,
>  					 intel_ddi_get_cdclk_freq(dev_priv));
>  
> -	I915_WRITE(PIPE_WM_LINETIME(pipe),
> -		   PIPE_WM_LINETIME_IPS_LINETIME(ips_linetime) |
> -		   PIPE_WM_LINETIME_TIME(linetime));
> +	return PIPE_WM_LINETIME_IPS_LINETIME(ips_linetime) |
> +	       PIPE_WM_LINETIME_TIME(linetime);
>  }
>  
> -static void haswell_update_wm(struct drm_device *dev)
> +static void hsw_compute_wm_parameters(struct drm_device *dev,
> +				      struct hsw_pipe_wm_parameters *params,
> +				      uint32_t *wm)
>  {
>  	struct drm_i915_private *dev_priv = dev->dev_private;
>  	struct drm_crtc *crtc;
> +	struct drm_plane *plane;
> +	uint64_t sskpd = I915_READ64(MCH_SSKPD);
>  	enum pipe pipe;
>  
> -	/* Disable the LP WMs before changine the linetime registers. This is
> -	 * just a temporary code that will be replaced soon. */
> -	I915_WRITE(WM3_LP_ILK, 0);
> -	I915_WRITE(WM2_LP_ILK, 0);
> -	I915_WRITE(WM1_LP_ILK, 0);
> +	if ((sskpd >> 56) & 0xFF)
> +		wm[0] = (sskpd >> 56) & 0xFF;
> +	else
> +		wm[0] = sskpd & 0xF;
> +	wm[1] = ((sskpd >> 4) & 0xFF) * 5;
> +	wm[2] = ((sskpd >> 12) & 0xFF) * 5;
> +	wm[3] = ((sskpd >> 20) & 0x1FF) * 5;
> +	wm[4] = ((sskpd >> 32) & 0x1FF) * 5;
> +
> +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> +		struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
> +		struct hsw_pipe_wm_parameters *p;
> +
> +		pipe = intel_crtc->pipe;
> +		p = &params[pipe];
> +
> +		p->active = intel_crtc_active(crtc);
> +		if (!p->active)
> +			continue;
> +
> +		p->pipe_htotal = intel_crtc->config.adjusted_mode.htotal;
> +		p->pixel_rate = hsw_wm_get_pixel_rate(dev, crtc);
> +		p->pri_bytes_per_pixel = crtc->fb->bits_per_pixel / 8;
> +		p->cur_bytes_per_pixel = 4;
> +		p->pri_horiz_pixels =
> +			intel_crtc->config.requested_mode.hdisplay;
> +		p->cur_horiz_pixels = 64;
> +	}
> +
> +	list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
> +		struct intel_plane *intel_plane = to_intel_plane(plane);
> +		struct hsw_pipe_wm_parameters *p;
> +
> +		pipe = intel_plane->pipe;
> +		p = &params[pipe];
> +
> +		p->sprite_enabled = intel_plane->wm.enable;
> +		p->spr_bytes_per_pixel = intel_plane->wm.bytes_per_pixel;
> +		p->spr_horiz_pixels = intel_plane->wm.horiz_pixels;
> +	}
> +}
> +
> +static void hsw_compute_wm_results(struct drm_device *dev,
> +				   struct hsw_pipe_wm_parameters *params,
> +				   uint32_t *wm,
> +				   struct hsw_wm_values *results)
> +{
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	struct drm_crtc *crtc;
> +	enum pipe pipe;
> +
> +	/* No support for LP WMs yet. */
> +	results->wm_lp[2] = 0;
> +	results->wm_lp[1] = 0;
> +	results->wm_lp[0] = 0;
> +	results->wm_lp_spr[2] = 0;
> +	results->wm_lp_spr[1] = 0;
> +	results->wm_lp_spr[0] = 0;
> +
> +	for_each_pipe(pipe)
> +		results->wm_pipe[pipe] = hsw_compute_wm_pipe(dev_priv, wm[0],
> +							     pipe,
> +							     &params[pipe]);
>  
>  	for_each_pipe(pipe) {
>  		crtc = dev_priv->pipe_to_crtc_mapping[pipe];
> -		haswell_update_linetime_wm(dev, crtc);
> +		results->wm_linetime[pipe] = hsw_compute_linetime_wm(dev, crtc);
>  	}
> +}
> +
> +/*
> + * The spec says we shouldn't write when we don't need, because every write
> + * causes WMs to be re-evaluated, expending some power.
> + */
> +static void hsw_write_wm_values(struct drm_i915_private *dev_priv,
> +				struct hsw_wm_values *results,
> +				enum hsw_data_buf_partitioning partitioning)
> +{
> +	struct hsw_wm_values previous;
> +	uint32_t val;
> +	enum hsw_data_buf_partitioning prev_partitioning;
> +
> +	previous.wm_pipe[0] = I915_READ(WM0_PIPEA_ILK);
> +	previous.wm_pipe[1] = I915_READ(WM0_PIPEB_ILK);
> +	previous.wm_pipe[2] = I915_READ(WM0_PIPEC_IVB);
> +	previous.wm_lp[0] = I915_READ(WM1_LP_ILK);
> +	previous.wm_lp[1] = I915_READ(WM2_LP_ILK);
> +	previous.wm_lp[2] = I915_READ(WM3_LP_ILK);
> +	previous.wm_lp_spr[0] = I915_READ(WM1S_LP_ILK);
> +	previous.wm_lp_spr[1] = I915_READ(WM2S_LP_IVB);
> +	previous.wm_lp_spr[2] = I915_READ(WM3S_LP_IVB);
> +	previous.wm_linetime[0] = I915_READ(PIPE_WM_LINETIME(PIPE_A));
> +	previous.wm_linetime[1] = I915_READ(PIPE_WM_LINETIME(PIPE_B));
> +	previous.wm_linetime[2] = I915_READ(PIPE_WM_LINETIME(PIPE_C));
> +
> +	prev_partitioning = (I915_READ(WM_MISC) & WM_MISC_DATA_PARTITION_5_6) ?
> +			    HSW_DATA_BUF_PART_5_6 : HSW_DATA_BUF_PART_1_2;
> +
> +	if (memcmp(results->wm_pipe, previous.wm_pipe,
> +		   sizeof(results->wm_pipe)) == 0 &&
> +	    memcmp(results->wm_lp, previous.wm_lp,
> +		   sizeof(results->wm_lp)) == 0 &&
> +	    memcmp(results->wm_lp_spr, previous.wm_lp_spr,
> +		   sizeof(results->wm_lp_spr)) == 0 &&
> +	    memcmp(results->wm_linetime, previous.wm_linetime,
> +		   sizeof(results->wm_linetime)) == 0 &&
> +	    partitioning == prev_partitioning)
> +		return;
> +
> +	if (previous.wm_lp[2] != 0)
> +		I915_WRITE(WM3_LP_ILK, 0);
> +	if (previous.wm_lp[1] != 0)
> +		I915_WRITE(WM2_LP_ILK, 0);
> +	if (previous.wm_lp[0] != 0)
> +		I915_WRITE(WM1_LP_ILK, 0);
> +
> +	if (previous.wm_pipe[0] != results->wm_pipe[0])
> +		I915_WRITE(WM0_PIPEA_ILK, results->wm_pipe[0]);
> +	if (previous.wm_pipe[1] != results->wm_pipe[1])
> +		I915_WRITE(WM0_PIPEB_ILK, results->wm_pipe[1]);
> +	if (previous.wm_pipe[2] != results->wm_pipe[2])
> +		I915_WRITE(WM0_PIPEC_IVB, results->wm_pipe[2]);
> +
> +	if (previous.wm_linetime[0] != results->wm_linetime[0])
> +		I915_WRITE(PIPE_WM_LINETIME(PIPE_A), results->wm_linetime[0]);
> +	if (previous.wm_linetime[1] != results->wm_linetime[1])
> +		I915_WRITE(PIPE_WM_LINETIME(PIPE_B), results->wm_linetime[1]);
> +	if (previous.wm_linetime[2] != results->wm_linetime[2])
> +		I915_WRITE(PIPE_WM_LINETIME(PIPE_C), results->wm_linetime[2]);
> +
> +	if (prev_partitioning != partitioning) {
> +		val = I915_READ(WM_MISC);
> +		if (partitioning == HSW_DATA_BUF_PART_1_2)
> +			val &= ~WM_MISC_DATA_PARTITION_5_6;
> +		else
> +			val |= WM_MISC_DATA_PARTITION_5_6;
> +		I915_WRITE(WM_MISC, val);
> +	}
> +
> +	if (previous.wm_lp_spr[0] != results->wm_lp_spr[0])
> +		I915_WRITE(WM1S_LP_ILK, results->wm_lp_spr[0]);
> +	if (previous.wm_lp_spr[1] != results->wm_lp_spr[1])
> +		I915_WRITE(WM2S_LP_IVB, results->wm_lp_spr[1]);
> +	if (previous.wm_lp_spr[2] != results->wm_lp_spr[2])
> +		I915_WRITE(WM3S_LP_IVB, results->wm_lp_spr[2]);
> +
> +	if (results->wm_lp[0] != 0)
> +		I915_WRITE(WM1_LP_ILK, results->wm_lp[0]);
> +	if (results->wm_lp[1] != 0)
> +		I915_WRITE(WM2_LP_ILK, results->wm_lp[1]);
> +	if (results->wm_lp[2] != 0)
> +		I915_WRITE(WM3_LP_ILK, results->wm_lp[2]);
> +}
> +
> +static void haswell_update_wm(struct drm_device *dev)
> +{
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	struct hsw_pipe_wm_parameters params[3];
> +	struct hsw_wm_values results;
> +	uint32_t wm[5];
>  
> -	sandybridge_update_wm(dev);
> +	hsw_compute_wm_parameters(dev, params, wm);
> +	hsw_compute_wm_results(dev, params, wm, &results);
> +	hsw_write_wm_values(dev_priv, &results, HSW_DATA_BUF_PART_1_2);
>  }
>  
>  static void haswell_update_sprite_wm(struct drm_device *dev, int pipe,
> -- 
> 1.8.1.2
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Ville Syrjälä
Intel OTC

  reply	other threads:[~2013-05-31 15:03 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-05-24 14:59 [PATCH 0/5] Haswell watermarks Paulo Zanoni
2013-05-24 14:59 ` [PATCH 1/5] drm/i915: add "enable" argument to intel_update_sprite_watermarks Paulo Zanoni
2013-05-24 16:22   ` Ville Syrjälä
2013-05-24 14:59 ` [PATCH 2/5] drm/i915: add haswell_update_sprite_wm Paulo Zanoni
2013-05-24 17:00   ` Ville Syrjälä
2013-05-24 19:35     ` Daniel Vetter
2013-05-24 14:59 ` [PATCH 3/5] drm/i915: properly set HSW WM_PIPE registers Paulo Zanoni
2013-05-24 16:07   ` Ville Syrjälä
2013-05-24 22:00     ` Paulo Zanoni
2013-05-24 22:02       ` Paulo Zanoni
2013-05-27 11:07       ` Ville Syrjälä
2013-05-27 19:21         ` Paulo Zanoni
2013-05-29 15:39           ` Ville Syrjälä
2013-05-31 13:08             ` [PATCH 1/3] " Paulo Zanoni
2013-05-31 15:03               ` Ville Syrjälä [this message]
2013-05-24 14:59 ` [PATCH 4/5] drm/i915: properly set HSW WM_LP watermarks Paulo Zanoni
2013-05-24 16:11   ` Ville Syrjälä
2013-05-24 22:05     ` Paulo Zanoni
2013-05-29 16:06       ` Ville Syrjälä
2013-05-29 16:24         ` Ville Syrjälä
2013-05-31 13:12           ` [PATCH 2/3] " Paulo Zanoni
2013-05-31 13:58             ` Ville Syrjälä
2013-05-31 14:45               ` Paulo Zanoni
2013-05-31 15:05                 ` Ville Syrjälä
2013-05-24 14:59 ` [PATCH 5/5] drm/i915: add support for 5/6 data buffer partitioning on Haswell Paulo Zanoni
2013-05-29 16:17   ` Ville Syrjälä
2013-05-31 13:19     ` [PATCH 3/3] " Paulo Zanoni
2013-05-31 13:44       ` Ville Syrjälä
2013-05-31 15:19         ` Daniel Vetter

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20130531150300.GW5004@intel.com \
    --to=ville.syrjala@linux.intel.com \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=paulo.r.zanoni@intel.com \
    --cc=przanoni@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.