diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 1a533ccdb54f..4683731ac1ca 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -12989,12 +12989,6 @@ static void verify_wm_state(struct intel_crtc *crtc,
 	skl_ddb_get_hw_state(dev_priv, &hw->ddb);
 	sw_ddb = &dev_priv->wm.skl_hw.ddb;
 
-	if (INTEL_GEN(dev_priv) >= 11 &&
-	    hw->ddb.enabled_slices != sw_ddb->enabled_slices)
-		DRM_ERROR("mismatch in DBUF Slices (expected %u, got %u)\n",
-			  sw_ddb->enabled_slices,
-			  hw->ddb.enabled_slices);
-
 	/* planes */
 	for_each_universal_plane(dev_priv, pipe, plane) {
 		struct skl_plane_wm *hw_plane_wm, *sw_plane_wm;
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index bfcf03ab5245..0fbeea61299f 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -3616,7 +3616,7 @@ static u8 intel_enabled_dbuf_slices_num(struct drm_i915_private *dev_priv)
 	 * only that 1 slice enabled until we have a proper way for on-demand
 	 * toggling of the second slice.
 	 */
-	if (0 && I915_READ(DBUF_CTL_S2) & DBUF_POWER_STATE)
+	if (I915_READ(DBUF_CTL_S2) & DBUF_POWER_STATE)
 		enabled_slices++;
 
 	return enabled_slices;
@@ -3821,7 +3821,7 @@ static u16 intel_get_ddb_size(struct drm_i915_private *dev_priv,
 	 * - plane straddling both slices is illegal in multi-pipe scenarios
 	 * - should validate we stay within the hw bandwidth limits
 	 */
-	if (0 && (num_active > 1 || total_data_bw >= GBps(12))) {
+	if ((num_active > 1 || total_data_bw >= GBps(12))) {
 		ddb->enabled_slices = 2;
 	} else {
 		ddb->enabled_slices = 1;
@@ -3831,6 +3831,35 @@ static u16 intel_get_ddb_size(struct drm_i915_private *dev_priv,
 	return ddb_size;
 }
 
+/*
+ * Calculate initial DBuf slice offset, based on slice size
+ * and mask(i.e if slice size is 1024 and second slice is enabled
+ * offset would be 1024)
+ */
+static u32 skl_get_first_dbuf_slice_offset(u32 dbuf_slice_mask,
+					   u32 slice_size, u32 ddb_size)
+{
+	u32 offset = 0;
+
+	if (!dbuf_slice_mask)
+		return 0;
+
+	while (!(dbuf_slice_mask & 1)) {
+		dbuf_slice_mask >>= 1;
+		offset += slice_size;
+		if (offset >= ddb_size)
+			break;
+	}
+	return offset;
+}
+
+static u32 i915_get_allowed_dbuf_mask(struct drm_i915_private *dev_priv,
+				      int pipe, u32 active_pipes,
+				      uint_fixed_16_16_t pipe_ratio);
+
+static uint_fixed_16_16_t
+skl_get_pipe_ratio(const struct intel_crtc_state *crtc_state);
+
 static void
 skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv,
 				   const struct intel_crtc_state *crtc_state,
@@ -3846,7 +3875,13 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv,
 	u32 pipe_width = 0, total_width = 0, width_before_pipe = 0;
 	enum pipe for_pipe = to_intel_crtc(for_crtc)->pipe;
 	u16 ddb_size;
+	u16 ddb_range_size;
 	u32 i;
+	u32 dbuf_slice_mask;
+	u32 active_pipes;
+	u32 offset;
+	u32 slice_size;
+	uint_fixed_16_16_t pipe_ratio;
 
 	if (WARN_ON(!state) || !crtc_state->base.active) {
 		alloc->start = 0;
@@ -3855,14 +3890,23 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv,
 		return;
 	}
 
-	if (intel_state->active_pipe_changes)
+	if (intel_state->active_pipe_changes) {
 		*num_active = hweight8(intel_state->active_pipes);
-	else
+		active_pipes = intel_state->active_pipes;
+	} else {
 		*num_active = hweight8(dev_priv->active_pipes);
+		active_pipes = dev_priv->active_pipes;
+	}
 
 	ddb_size = intel_get_ddb_size(dev_priv, crtc_state, total_data_rate,
 				      *num_active, ddb);
 
+	DRM_DEBUG_KMS("Got total ddb size %d\n", ddb_size);
+
+	slice_size = ddb_size / ddb->enabled_slices;
+
+	DRM_DEBUG_KMS("Got DBuf slice size %d\n", slice_size);
+
 	/*
 	 * If the state doesn't change the active CRTC's or there is no
 	 * modeset request, then there's no need to recalculate;
@@ -3880,6 +3924,39 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv,
 		return;
 	}
 
+	/*
+	 * Calculate pipe ratio as stated in BSpec 28692
+	 */
+	pipe_ratio = skl_get_pipe_ratio(crtc_state);
+
+	/*
+	 * Get allowed DBuf slices for correspondent pipe and platform.
+	 */
+	dbuf_slice_mask = i915_get_allowed_dbuf_mask(dev_priv, for_pipe,
+						     active_pipes, pipe_ratio);
+
+	DRM_DEBUG_KMS("DBuf slice mask %x pipe %d active pipes %x\n",
+		      dbuf_slice_mask,
+		      for_pipe, active_pipes);
+
+	/*
+	 * Figure out at which DBuf slice we start, i.e if we start at Dbuf S2
+	 * and slice size is 1024, the offset would be 1024
+	 */
+	offset = skl_get_first_dbuf_slice_offset(dbuf_slice_mask,
+						 slice_size, ddb_size);
+
+	/*
+	 * Figure out total size of allowed DBuf slices, which is basically
+	 * a number of allowed slices for that pipe multiplied by slice size.
+	 * We might still have some dbuf slices disabled in case if those
+	 * are not needed based on bandwidth requirements and num_active pipes,
+	 * so stick to real ddb size if it happens to be less. Inside of this
+	 * range ddb entries are still allocated in proportion to display width.
+	 */
+	ddb_range_size = min(hweight8(dbuf_slice_mask) * slice_size,
+			     (unsigned int)ddb_size);
+
 	/*
 	 * Watermark/ddb requirement highly depends upon width of the
 	 * framebuffer, So instead of allocating DDB equally among pipes
@@ -3890,10 +3967,23 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv,
 			&crtc_state->base.adjusted_mode;
 		enum pipe pipe = crtc->pipe;
 		int hdisplay, vdisplay;
+		uint_fixed_16_16_t ratio = skl_get_pipe_ratio(crtc_state);
+		u32 pipe_dbuf_slice_mask = i915_get_allowed_dbuf_mask(dev_priv,
+								      pipe,
+								      active_pipes,
+								      ratio);
 
 		if (!crtc_state->base.enable)
 			continue;
 
+		/*
+		 * According to BSpec pipe can share one dbuf slice with another pipes or pipe can use
+		 * multiple dbufs, in both cases we account for other pipes only if they have
+		 * exactly same mask.
+		 */
+		if (dbuf_slice_mask != pipe_dbuf_slice_mask)
+			continue;
+
 		drm_mode_get_hv_timing(adjusted_mode, &hdisplay, &vdisplay);
 		total_width += hdisplay;
 
@@ -3903,8 +3993,11 @@ skl_ddb_get_pipe_allocation_limits(struct drm_i915_private *dev_priv,
 			pipe_width = hdisplay;
 	}
 
-	alloc->start = ddb_size * width_before_pipe / total_width;
-	alloc->end = ddb_size * (width_before_pipe + pipe_width) / total_width;
+	alloc->start = offset + ddb_range_size * width_before_pipe / total_width;
+	alloc->end = offset + ddb_range_size * (width_before_pipe + pipe_width) / total_width;
+
+	DRM_DEBUG_KMS("Pipe %d ddb %d-%d\n", for_pipe,
+		      alloc->start, alloc->end);
 }
 
 static int skl_compute_wm_params(const struct intel_crtc_state *crtc_state,
@@ -4255,6 +4348,109 @@ skl_get_total_relative_data_rate(struct intel_crtc_state *crtc_state,
 	return total_data_rate;
 }
 
+uint_fixed_16_16_t
+skl_get_pipe_ratio(const struct intel_crtc_state *crtc_state)
+{
+	struct drm_plane *plane;
+	const struct drm_plane_state *drm_plane_state;
+	uint_fixed_16_16_t pipe_downscale;
+	uint_fixed_16_16_t max_downscale = u32_to_fixed16(1);
+
+	if (!crtc_state->base.enable)
+		return max_downscale;
+
+	drm_atomic_crtc_state_for_each_plane_state(plane, drm_plane_state, &crtc_state->base) {
+		uint_fixed_16_16_t plane_downscale;
+		const struct intel_plane_state *plane_state =
+			to_intel_plane_state(drm_plane_state);
+
+		if (!intel_wm_plane_visible(crtc_state, plane_state))
+			continue;
+
+		plane_downscale = skl_plane_downscale_amount(crtc_state, plane_state);
+
+		max_downscale = max_fixed16(plane_downscale, max_downscale);
+	}
+
+	pipe_downscale = skl_pipe_downscale_amount(crtc_state);
+
+	pipe_downscale = mul_fixed16(pipe_downscale, max_downscale);
+
+	return pipe_downscale;
+}
+
+#define DBUF_S1_BIT 1
+#define DBUF_S2_BIT 2
+
+struct dbuf_slice_conf_entry {
+	u32 dbuf_mask[I915_MAX_PIPES];
+	u32 active_pipes;
+};
+
+
+/*
+ * Table taken from Bspec 12716
+ * Pipes do have some preferred DBuf slice affinity,
+ * plus there are some hardcoded requirements on how
+ * those should be distributed for multipipe scenarios.
+ * For more DBuf slices algorithm can get even more messy
+ * and less readable, so decided to use a table almost
+ * as is from BSpec itself - that way it is at least easier
+ * to compare, change and check.
+ */
+struct dbuf_slice_conf_entry icl_allowed_dbufs[] = {
+{ { 0, 0, 0, 0 }, 0 },
+{ { DBUF_S1_BIT | DBUF_S2_BIT, 0, 0, 0 }, BIT(PIPE_A) },
+{ { DBUF_S1_BIT, 0, 0, 0 }, BIT(PIPE_A) },
+{ { 0, DBUF_S1_BIT | DBUF_S2_BIT, 0, 0 }, BIT(PIPE_B) },
+{ { 0, DBUF_S1_BIT, 0, 0 }, BIT(PIPE_B) },
+{ { 0, 0, DBUF_S1_BIT | DBUF_S2_BIT }, BIT(PIPE_C) },
+{ { 0, 0, DBUF_S2_BIT, 0 }, BIT(PIPE_C) },
+{ { DBUF_S1_BIT, DBUF_S2_BIT, 0, 0 }, BIT(PIPE_A) | BIT(PIPE_B) },
+{ { DBUF_S1_BIT, 0, DBUF_S2_BIT, 0 }, BIT(PIPE_A) | BIT(PIPE_C) },
+{ { 0, DBUF_S1_BIT, DBUF_S2_BIT, 0 }, BIT(PIPE_B) | BIT(PIPE_C) },
+{ { DBUF_S1_BIT, DBUF_S1_BIT, DBUF_S2_BIT, 0 }, BIT(PIPE_A) | BIT(PIPE_B) | BIT(PIPE_C) },
+};
+
+/*
+ * This function finds an entry with same enabled pipe configuration and
+ * returns correspondent DBuf slice mask as stated in BSpec for particular
+ * platform.
+ */
+static u32 icl_get_allowed_dbuf_mask(int pipe,
+				     u32 active_pipes,
+				     uint_fixed_16_16_t pipe_ratio)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(icl_allowed_dbufs); i++) {
+		if (icl_allowed_dbufs[i].active_pipes == active_pipes) {
+			/*
+			 * According to BSpec 12716: if pipe_ratio >= 88.8
+			 * use single pipe, even if two are accessible.
+			 */
+			if (pipe_ratio.val >= div_fixed16(888, 10).val)
+				++i;
+			return icl_allowed_dbufs[i].dbuf_mask[pipe];
+		}
+	}
+	return 0;
+}
+
+u32 i915_get_allowed_dbuf_mask(struct drm_i915_private *dev_priv,
+				      int pipe, u32 active_pipes,
+				      uint_fixed_16_16_t pipe_ratio)
+{
+	if (IS_GEN(dev_priv, 11))
+		return icl_get_allowed_dbuf_mask(pipe,
+						 active_pipes,
+						 pipe_ratio);
+	/*
+	 * For anything else just return one slice yet.
+	 * Should be extended for other platforms.
+	 */
+	return DBUF_S1_BIT;
+}
+
 static u64
 icl_get_total_relative_data_rate(struct intel_crtc_state *crtc_state,
 				 u64 *plane_data_rate)
