[PATCH v2] drm/i915: Decode system memory bandwidth

Mahesh Kumar mahesh1.kumar at intel.com
Mon Jul 9 13:59:00 UTC 2018


This patch adds support to decode system memory bandwidth and other
parameters, which will be used for arbitrated display memory
percentage calculation in GEN9 based systems and WM latency level-0
Work-around decision on GEN9+ platforms.

Signed-off-by: Mahesh Kumar <mahesh1.kumar at intel.com>
---
 drivers/gpu/drm/i915/i915_drv.c | 233 ++++++++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/i915_drv.h |  13 +++
 drivers/gpu/drm/i915/i915_reg.h |  38 +++++++
 drivers/gpu/drm/i915/intel_pm.c |  12 +++
 4 files changed, 296 insertions(+)

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 0db3c83cce29..c817ed62d130 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1063,6 +1063,233 @@ static void intel_sanitize_options(struct drm_i915_private *dev_priv)
 	intel_gvt_sanitize_options(dev_priv);
 }
 
+static enum memdev_rank
+skl_memdev_get_channel_rank(struct memdev_info *memdev_info, u32 val)
+{
+	u8 l_rank, s_rank;
+	u8 l_size, s_size;
+	u8 l_width, s_width;
+	enum memdev_rank rank;
+	bool found_16gb = false;
+
+	if (!val)
+		return I915_DRAM_RANK_INVALID;
+
+	l_size = (val >> SKL_DRAM_SIZE_L_SHIFT) & SKL_DRAM_SIZE_MASK;
+	s_size = (val >> SKL_DRAM_SIZE_S_SHIFT) & SKL_DRAM_SIZE_MASK;
+	l_width = (val >> SKL_DRAM_WIDTH_L_SHIFT) & SKL_DRAM_WIDTH_MASK;
+	s_width = (val >> SKL_DRAM_WIDTH_S_SHIFT) & SKL_DRAM_WIDTH_MASK;
+	l_rank = (val >> SKL_DRAM_RANK_L_SHIFT) & SKL_DRAM_RANK_MASK;
+	s_rank = (val >> SKL_DRAM_RANK_S_SHIFT) & SKL_DRAM_RANK_MASK;
+
+	if (l_size == 0 && s_size == 0)
+		return I915_DRAM_RANK_INVALID;
+
+	memdev_info->valid_dimm = true;
+
+	if (l_rank == SKL_DRAM_RANK_DUAL || s_rank == SKL_DRAM_RANK_DUAL)
+		rank = I915_DRAM_RANK_DUAL;
+	else if ((l_size && l_rank == SKL_DRAM_RANK_SINGLE) &&
+		 (s_size && s_rank == SKL_DRAM_RANK_SINGLE))
+		rank = I915_DRAM_RANK_DUAL;
+	else
+		rank = I915_DRAM_RANK_SINGLE;
+
+	if ((l_size == 16 && l_width == SKL_DRAM_WIDTH_X8 &&
+	     l_rank == SKL_DRAM_RANK_SINGLE) ||
+	    (s_size == 16 && s_width == SKL_DRAM_WIDTH_X8 &&
+	     s_rank == SKL_DRAM_RANK_SINGLE))
+		found_16gb = true;
+	else if ((l_size == 32 && l_width == SKL_DRAM_WIDTH_X8 &&
+		  l_rank == SKL_DRAM_RANK_DUAL) ||
+		 (s_size == 32 && s_width == SKL_DRAM_WIDTH_X8 &&
+		  s_rank == SKL_DRAM_RANK_DUAL))
+		found_16gb = true;
+	else if ((l_size == 8 && l_width == SKL_DRAM_WIDTH_X16 &&
+		  l_rank == SKL_DRAM_RANK_SINGLE) ||
+		 (s_size == 8 && s_width == SKL_DRAM_WIDTH_X16 &&
+		  s_rank == SKL_DRAM_RANK_SINGLE))
+		found_16gb = true;
+	else if ((l_size == 16 && l_width == SKL_DRAM_WIDTH_X16 &&
+		  l_rank == SKL_DRAM_RANK_DUAL) ||
+		 (s_size == 16 && s_width == SKL_DRAM_WIDTH_X16 &&
+		  s_rank == SKL_DRAM_RANK_DUAL))
+		found_16gb = true;
+
+	if (memdev_info->is_16gb_dimm == false)
+		memdev_info->is_16gb_dimm = found_16gb;
+
+	return rank;
+}
+
+static int
+skl_get_dimm_info(struct drm_i915_private *dev_priv)
+{
+	struct memdev_info *memdev_info = &dev_priv->memdev_info;
+	enum memdev_rank ch0_rank, ch1_rank;
+	u32 val;
+
+	val = I915_READ(SKL_MAD_DIMM_CH0_0_0_0_MCHBAR_MCMAIN);
+	DRM_INFO("Ch0 info : 0x%x\n", val);
+	ch0_rank = skl_memdev_get_channel_rank(memdev_info, val);
+	if (ch0_rank != I915_DRAM_RANK_INVALID)
+		memdev_info->num_channels++;
+
+	val = I915_READ(SKL_MAD_DIMM_CH1_0_0_0_MCHBAR_MCMAIN);
+	DRM_INFO("Ch1 info : 0x%x\n", val);
+	ch1_rank = skl_memdev_get_channel_rank(memdev_info, val);
+	if (ch1_rank != I915_DRAM_RANK_INVALID)
+		memdev_info->num_channels++;
+
+	if (memdev_info->num_channels == 0) {
+		DRM_INFO("Number of memory channels is zero\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * If any of the channel is single rank channel, worst case output
+	 * will be same as if single rank memory, so consider single rank
+	 * memory.
+	 */
+	if (ch0_rank == I915_DRAM_RANK_SINGLE ||
+	    ch1_rank == I915_DRAM_RANK_SINGLE)
+		memdev_info->rank = I915_DRAM_RANK_SINGLE;
+	else
+		memdev_info->rank = max(ch0_rank, ch1_rank);
+
+	if (memdev_info->rank == I915_DRAM_RANK_INVALID) {
+		DRM_INFO("couldn't get memory rank information\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+skl_get_memdev_info(struct drm_i915_private *dev_priv)
+{
+	struct memdev_info *memdev_info = &dev_priv->memdev_info;
+	u32 mem_freq_khz, val;
+	int ret;
+
+	ret = skl_get_dimm_info(dev_priv);
+	if (ret)
+		return ret;
+
+	val = I915_READ(SKL_MC_BIOS_DATA_0_0_0_MCHBAR_PCU);
+	mem_freq_khz = DIV_ROUND_UP((val & SKL_REQ_DATA_MASK) *
+				    SKL_MEMORY_FREQ_MULTIPLIER_HZ, 1000);
+
+	memdev_info->bandwidth_kbps = memdev_info->num_channels *
+							mem_freq_khz * 8;
+
+	if (memdev_info->bandwidth_kbps == 0) {
+		DRM_INFO("Couldn't get system memory bandwidth\n");
+		return -EINVAL;
+	}
+
+	memdev_info->valid = true;
+	return 0;
+}
+
+static int
+bxt_get_memdev_info(struct drm_i915_private *dev_priv)
+{
+	struct memdev_info *memdev_info = &dev_priv->memdev_info;
+	u32 dram_channels;
+	u32 mem_freq_khz, val;
+	u8 num_active_channels;
+	int i;
+
+	val = I915_READ(BXT_P_CR_MC_BIOS_REQ_0_0_0);
+	mem_freq_khz = DIV_ROUND_UP((val & BXT_REQ_DATA_MASK) *
+				    BXT_MEMORY_FREQ_MULTIPLIER_HZ, 1000);
+
+	dram_channels = (val >> BXT_DRAM_CHANNEL_ACTIVE_SHIFT) &
+					BXT_DRAM_CHANNEL_ACTIVE_MASK;
+	num_active_channels = hweight32(dram_channels);
+
+	/* Each active bit represents 4-byte channel */
+	memdev_info->bandwidth_kbps = (mem_freq_khz * num_active_channels * 4);
+
+	if (memdev_info->bandwidth_kbps == 0) {
+		DRM_ERROR("Couldn't get system memory bandwidth\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Now read each DUNIT8/9/10/11 to check the rank of each dimms.
+	 */
+	for (i = BXT_D_CR_DRP0_DUNIT_START; i <= BXT_D_CR_DRP0_DUNIT_END; i++) {
+		val = I915_READ(BXT_D_CR_DRP0_DUNIT(i));
+		if (val != 0xFFFFFFFF) {
+			u8 rank;
+			enum memdev_rank ch_rank;
+
+			memdev_info->num_channels++;
+			rank = val & BXT_DRAM_RANK_MASK;
+			if (rank == BXT_DRAM_RANK_SINGLE)
+				ch_rank = I915_DRAM_RANK_SINGLE;
+			else if (rank == BXT_DRAM_RANK_DUAL)
+				ch_rank = I915_DRAM_RANK_DUAL;
+			else
+				ch_rank = I915_DRAM_RANK_INVALID;
+
+			/*
+			 * If any of the channel is single rank channel,
+			 * worst case output will be same as if single rank
+			 * memory, so consider single rank memory.
+			 */
+			if (memdev_info->rank == I915_DRAM_RANK_INVALID)
+				memdev_info->rank = ch_rank;
+			else if (ch_rank == I915_DRAM_RANK_SINGLE)
+				memdev_info->rank = I915_DRAM_RANK_SINGLE;
+		}
+	}
+
+	if (memdev_info->rank == I915_DRAM_RANK_INVALID) {
+		DRM_INFO("couldn't get memory rank information\n");
+		return -EINVAL;
+	}
+
+	memdev_info->valid = true;
+	memdev_info->valid_dimm = true;
+	return 0;
+}
+
+static void
+intel_get_memdev_info(struct drm_i915_private *dev_priv)
+{
+	struct memdev_info *memdev_info = &dev_priv->memdev_info;
+	int ret;
+
+	memdev_info->valid = false;
+	memdev_info->valid_dimm = false;
+	memdev_info->is_16gb_dimm = false;
+	memdev_info->rank = I915_DRAM_RANK_INVALID;
+	memdev_info->bandwidth_kbps = 0;
+	memdev_info->num_channels = 0;
+
+	if (INTEL_GEN(dev_priv) < 9)
+		return;
+
+	if (IS_BROXTON(dev_priv) || IS_GEMINILAKE(dev_priv))
+		ret = bxt_get_memdev_info(dev_priv);
+	else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv))
+		ret = skl_get_memdev_info(dev_priv);
+	else
+		ret = skl_get_dimm_info(dev_priv);
+
+	if (ret)
+		return;
+
+	DRM_DEBUG_DRIVER("DRAM bandwidth: %u KBps, total-channels: %u\n",
+			 memdev_info->bandwidth_kbps,
+			 memdev_info->num_channels);
+	DRM_DEBUG_DRIVER("DRAM rank: %s rank 16GB-dimm=%s\n",
+			 (memdev_info->rank == I915_DRAM_RANK_DUAL) ?
+			  "dual" : "single", yesno(memdev_info->is_16gb_dimm));
+}
+
 /**
  * i915_driver_init_hw - setup state requiring device access
  * @dev_priv: device private
@@ -1181,6 +1408,12 @@ static int i915_driver_init_hw(struct drm_i915_private *dev_priv)
 	if (ret)
 		goto err_ggtt;
 
+	/*
+	 * Fill the memdev structure to get the system raw bandwidth
+	 * This will be used by WM algorithm, to implement GEN9 based WA
+	 */
+	intel_get_memdev_info(dev_priv);
+
 	return 0;
 
 err_ggtt:
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 09ab12458244..244adf8a30f1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1912,6 +1912,19 @@ struct drm_i915_private {
 		bool distrust_bios_wm;
 	} wm;
 
+	struct memdev_info {
+		bool valid;
+		bool valid_dimm;
+		bool is_16gb_dimm;
+		u8 num_channels;
+		enum memdev_rank {
+			I915_DRAM_RANK_INVALID = 0,
+			I915_DRAM_RANK_SINGLE,
+			I915_DRAM_RANK_DUAL
+		} rank;
+		u32 bandwidth_kbps;
+	} memdev_info;
+
 	struct i915_runtime_pm runtime_pm;
 
 	struct {
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 0424e45f88db..fc8f387141f5 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -9421,6 +9421,44 @@ enum skl_power_gate {
 #define  DC_STATE_DEBUG_MASK_CORES	(1 << 0)
 #define  DC_STATE_DEBUG_MASK_MEMORY_UP	(1 << 1)
 
+#define BXT_P_CR_MC_BIOS_REQ_0_0_0	_MMIO(MCHBAR_MIRROR_BASE_SNB + 0x7114)
+#define  BXT_REQ_DATA_MASK			0x3F
+#define  BXT_DRAM_CHANNEL_ACTIVE_SHIFT		12
+#define  BXT_DRAM_CHANNEL_ACTIVE_MASK		0xF
+#define  BXT_MEMORY_FREQ_MULTIPLIER_HZ		133333333
+
+#define BXT_D_CR_DRP0_DUNIT8			0x1000
+#define BXT_D_CR_DRP0_DUNIT9			0x1200
+#define  BXT_D_CR_DRP0_DUNIT_START		8
+#define  BXT_D_CR_DRP0_DUNIT_END		11
+#define BXT_D_CR_DRP0_DUNIT(x)	_MMIO(MCHBAR_MIRROR_BASE_SNB + \
+				      _PICK_EVEN((x) - 8, BXT_D_CR_DRP0_DUNIT8,\
+						 BXT_D_CR_DRP0_DUNIT9))
+#define  BXT_DRAM_RANK_MASK			0x3
+#define  BXT_DRAM_RANK_SINGLE			0x1
+#define  BXT_DRAM_RANK_DUAL			0x3
+
+#define SKL_MEMORY_FREQ_MULTIPLIER_HZ		266666666
+#define SKL_MC_BIOS_DATA_0_0_0_MCHBAR_PCU	_MMIO(MCHBAR_MIRROR_BASE_SNB + 0x5E04)
+#define  SKL_REQ_DATA_MASK			(0xF << 0)
+
+#define SKL_MAD_DIMM_CH0_0_0_0_MCHBAR_MCMAIN	_MMIO(MCHBAR_MIRROR_BASE_SNB + 0x500C)
+#define SKL_MAD_DIMM_CH1_0_0_0_MCHBAR_MCMAIN	_MMIO(MCHBAR_MIRROR_BASE_SNB + 0x5010)
+#define  SKL_DRAM_SIZE_MASK			0x3F
+#define  SKL_DRAM_SIZE_L_SHIFT			0
+#define  SKL_DRAM_SIZE_S_SHIFT			16
+#define  SKL_DRAM_WIDTH_MASK			0x3
+#define  SKL_DRAM_WIDTH_L_SHIFT			8
+#define  SKL_DRAM_WIDTH_S_SHIFT			24
+#define  SKL_DRAM_WIDTH_X8			0x0
+#define  SKL_DRAM_WIDTH_X16			0x1
+#define  SKL_DRAM_WIDTH_X32			0x2
+#define  SKL_DRAM_RANK_MASK			0x1
+#define  SKL_DRAM_RANK_L_SHIFT			10
+#define  SKL_DRAM_RANK_S_SHIFT			26
+#define  SKL_DRAM_RANK_SINGLE			0x0
+#define  SKL_DRAM_RANK_DUAL			0x1
+
 /* Please see hsw_read_dcomp() and hsw_write_dcomp() before using this register,
  * since on HSW we can't write to it using I915_WRITE. */
 #define D_COMP_HSW			_MMIO(MCHBAR_MIRROR_BASE_SNB + 0x5F0C)
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 53aaaa3e6886..34a688d9b780 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2874,6 +2874,18 @@ static void intel_read_wm_latency(struct drm_i915_private *dev_priv,
 			}
 		}
 
+		/*
+		 * WA Level-0 adjustment for 16GB DIMMs: SKL+
+		 * If we could not get dimm info enable this WA to prevent from
+		 * any underrun.
+		 */
+		if (dev_priv->memdev_info.valid_dimm) {
+			if (dev_priv->memdev_info.is_16gb_dimm)
+				wm[0] += 1;
+		} else {
+			wm[0] += 1;
+		}
+
 	} else if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv)) {
 		uint64_t sskpd = I915_READ64(MCH_SSKPD);
 
-- 
2.16.2



More information about the Intel-gfx-trybot mailing list