@@ -207,6 +207,22 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc)
return &crtc_state->base;
}
+int intel_atomic_serialize_global_state(struct intel_atomic_state *state)
+{
+ struct drm_i915_private *dev_priv = to_i915(state->base.dev);
+ struct intel_crtc *crtc;
+
+ for_each_intel_crtc(&dev_priv->drm, crtc) {
+ struct intel_crtc_state *crtc_state;
+
+ crtc_state = intel_atomic_get_crtc_state(&state->base, crtc);
+ if (IS_ERR(crtc_state))
+ return PTR_ERR(crtc_state);
+ }
+
+ return 0;
+}
+
/**
* intel_crtc_destroy_state - destroy crtc state
* @crtc: drm crtc
@@ -7,6 +7,7 @@
#define __INTEL_ATOMIC_H__
#include <linux/types.h>
+#include "intel_display_types.h"
struct drm_atomic_state;
struct drm_connector;
@@ -38,6 +39,8 @@ void intel_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_atomic_state *intel_atomic_state_alloc(struct drm_device *dev);
void intel_atomic_state_clear(struct drm_atomic_state *state);
+int intel_atomic_serialize_global_state(struct intel_atomic_state *state);
+
struct intel_crtc_state *
intel_atomic_get_crtc_state(struct drm_atomic_state *state,
struct intel_crtc *crtc);
@@ -8,6 +8,8 @@
#include "intel_bw.h"
#include "intel_display_types.h"
#include "intel_sideband.h"
+#include "intel_atomic.h"
+#include "intel_pm.h"
/* Parameters for Qclk Geyserville (QGV) */
struct intel_qgv_point {
@@ -113,6 +115,27 @@ static int icl_pcode_read_qgv_point_info(struct drm_i915_private *dev_priv,
return 0;
}
+int icl_pcode_restrict_qgv_points(struct drm_i915_private *dev_priv,
+ u32 points_mask)
+{
+ int ret;
+
+ /* bspec says to keep retrying for at least 1 ms */
+ ret = skl_pcode_request(dev_priv, ICL_PCODE_SAGV_DE_MEM_SS_CONFIG,
+ points_mask,
+ GEN11_PCODE_POINTS_RESTRICTED_MASK,
+ GEN11_PCODE_POINTS_RESTRICTED,
+ 1);
+
+ if (ret < 0) {
+ DRM_ERROR("Failed to disable qgv points (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+
static int icl_get_qgv_points(struct drm_i915_private *dev_priv,
struct intel_qgv_info *qi)
{
@@ -270,22 +293,6 @@ void intel_bw_init_hw(struct drm_i915_private *dev_priv)
icl_get_bw_info(dev_priv, &icl_sa_info);
}
-static unsigned int intel_max_data_rate(struct drm_i915_private *dev_priv,
- int num_planes)
-{
- if (INTEL_GEN(dev_priv) >= 11)
- /*
- * FIXME with SAGV disabled maybe we can assume
- * point 1 will always be used? Seems to match
- * the behaviour observed in the wild.
- */
- return min3(icl_max_bw(dev_priv, num_planes, 0),
- icl_max_bw(dev_priv, num_planes, 1),
- icl_max_bw(dev_priv, num_planes, 2));
- else
- return UINT_MAX;
-}
-
static unsigned int intel_bw_crtc_num_active_planes(const struct intel_crtc_state *crtc_state)
{
/*
@@ -377,7 +384,10 @@ int intel_bw_atomic_check(struct intel_atomic_state *state)
unsigned int data_rate, max_data_rate;
unsigned int num_active_planes;
struct intel_crtc *crtc;
- int i;
+ int i, ret;
+ struct intel_qgv_info qi = {};
+ u32 allowed_points = 0;
+ unsigned int max_bw_point = 0, max_bw = 0;
/* FIXME earlier gens need some checks too */
if (INTEL_GEN(dev_priv) < 11)
@@ -421,16 +431,67 @@ int intel_bw_atomic_check(struct intel_atomic_state *state)
data_rate = intel_bw_data_rate(dev_priv, bw_state);
num_active_planes = intel_bw_num_active_planes(dev_priv, bw_state);
- max_data_rate = intel_max_data_rate(dev_priv, num_active_planes);
-
data_rate = DIV_ROUND_UP(data_rate, 1000);
- if (data_rate > max_data_rate) {
- DRM_DEBUG_KMS("Bandwidth %u MB/s exceeds max available %d MB/s (%d active planes)\n",
- data_rate, max_data_rate, num_active_planes);
+ ret = icl_get_qgv_points(dev_priv, &qi);
+ if (ret < 0)
+ return 0;
+
+ for (i = 0; i < qi.num_points; i++) {
+ max_data_rate = icl_max_bw(dev_priv, num_active_planes, i);
+ /*
+ * We need to know which qgv point gives us
+ * maximum bandwidth in order to disable SAGV
+ * if we find that we exceed SAGV block time
+ * with watermarks. By that moment we already
+ * have those, as it is calculated earlier in
+ * intel_atomic_check,
+ */
+ if (max_data_rate > max_bw) {
+ max_bw_point = i;
+ max_bw = max_data_rate;
+ }
+ if (max_data_rate >= data_rate)
+ allowed_points |= 1 << i;
+ DRM_DEBUG_KMS("QGV point %d: max bw %d required %d\n",
+ i, max_data_rate, data_rate);
+ }
+
+ /*
+ * BSpec states that we always should have at least one allowed point
+ * left, so if we couldn't - simply reject the configuration for obvious
+ * reasons.
+ */
+ if (allowed_points == 0) {
+ DRM_DEBUG_KMS("Could not find any suitable QGV points\n");
return -EINVAL;
}
+ /*
+ * Leave only single point with highest bandwidth, if
+ * we can't enable SAGV according to BSpec.
+ */
+ if (!intel_can_enable_sagv(state))
+ allowed_points = 1 << max_bw_point;
+
+ /*
+ * We store the ones which need to be masked as that is what PCode
+ * actually accepts as a parameter.
+ */
+ state->qgv_points_mask = (~allowed_points) & ((1 << qi.num_points) - 1);
+
+ /*
+ * If the actual mask had changed we need to make sure that
+ * the commits are serialized(in case this is a nomodeset, nonblocking)
+ */
+ if (state->qgv_points_mask != dev_priv->qgv_points_mask) {
+ ret = intel_atomic_serialize_global_state(state);
+ if (ret) {
+ DRM_DEBUG_KMS("Could not serialize global state\n");
+ return ret;
+ }
+ }
+
return 0;
}
@@ -28,5 +28,7 @@ int intel_bw_init(struct drm_i915_private *dev_priv);
int intel_bw_atomic_check(struct intel_atomic_state *state);
void intel_bw_crtc_update(struct intel_bw_state *bw_state,
const struct intel_crtc_state *crtc_state);
+int icl_pcode_restrict_qgv_points(struct drm_i915_private *dev_priv,
+ u32 points_mask);
#endif /* __INTEL_BW_H__ */
@@ -14003,6 +14003,48 @@ static void intel_atomic_cleanup_work(struct work_struct *work)
intel_atomic_helper_free_state(i915);
}
+static void intel_qgv_points_mask(struct intel_atomic_state *state)
+{
+ struct drm_device *dev = state->base.dev;
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ int ret;
+ u32 new_mask = dev_priv->qgv_points_mask | state->qgv_points_mask;
+
+ /*
+ * Restrict required qgv points before updating the configuration.
+ * According to BSpec we can't mask and unmask qgv points at the same
+ * time. Also masking should be done before updating the configuration
+ * and unmasking afterwards.
+ */
+ ret = icl_pcode_restrict_qgv_points(dev_priv, new_mask);
+ if (ret < 0)
+ DRM_DEBUG_KMS("Could not restrict required qgv points(%d)\n",
+ ret);
+ else
+ dev_priv->qgv_points_mask = new_mask;
+}
+
+static void intel_qgv_points_unmask(struct intel_atomic_state *state)
+{
+ struct drm_device *dev = state->base.dev;
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ int ret;
+ u32 new_mask = dev_priv->qgv_points_mask & state->qgv_points_mask;
+
+ /*
+ * Allow required qgv points after updating the configuration.
+ * According to BSpec we can't mask and unmask qgv points at the same
+ * time. Also masking should be done before updating the configuration
+ * and unmasking afterwards.
+ */
+ ret = icl_pcode_restrict_qgv_points(dev_priv, new_mask);
+ if (ret < 0)
+ DRM_DEBUG_KMS("Could not restrict required qgv points(%d)\n",
+ ret);
+ else
+ dev_priv->qgv_points_mask = new_mask;
+}
+
static void intel_atomic_commit_tail(struct intel_atomic_state *state)
{
struct drm_device *dev = state->base.dev;
@@ -14030,6 +14072,9 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
}
}
+ if ((INTEL_GEN(dev_priv) >= 11))
+ intel_qgv_points_mask(state);
+
intel_commit_modeset_disables(state);
/* FIXME: Eventually get rid of our crtc->config pointer */
@@ -14048,8 +14093,9 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
* SKL workaround: bspec recommends we disable the SAGV when we
* have more then one pipe enabled
*/
- if (!intel_can_enable_sagv(state))
- intel_disable_sagv(dev_priv);
+ if (INTEL_GEN(dev_priv) < 11)
+ if (!intel_can_enable_sagv(state))
+ intel_disable_sagv(dev_priv);
intel_modeset_verify_disabled(dev_priv, state);
}
@@ -14127,8 +14173,12 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
if (state->modeset)
intel_verify_planes(state);
- if (state->modeset && intel_can_enable_sagv(state))
- intel_enable_sagv(dev_priv);
+ if (INTEL_GEN(dev_priv) < 11) {
+ if (state->modeset && intel_can_enable_sagv(state))
+ intel_enable_sagv(dev_priv);
+ }
+ else
+ intel_qgv_points_unmask(state);
drm_atomic_helper_commit_hw_done(&state->base);
@@ -519,6 +519,9 @@ struct intel_atomic_state {
struct i915_sw_fence commit_ready;
struct llist_node freed;
+
+ /* Gen11+ only */
+ u32 qgv_points_mask;
};
struct intel_plane_state {
@@ -1335,6 +1335,8 @@ struct drm_i915_private {
u8 num_planes;
} max_bw[6];
+ u32 qgv_points_mask;
+
struct drm_private_obj bw_obj;
struct intel_runtime_pm runtime_pm;
@@ -8869,6 +8869,7 @@ enum {
#define ICL_PCODE_MEM_SUBSYSYSTEM_INFO 0xd
#define ICL_PCODE_MEM_SS_READ_GLOBAL_INFO (0x0 << 8)
#define ICL_PCODE_MEM_SS_READ_QGV_POINT_INFO(point) (((point) << 16) | (0x1 << 8))
+#define ICL_PCODE_SAGV_DE_MEM_SS_CONFIG 0xe
#define GEN6_PCODE_READ_D_COMP 0x10
#define GEN6_PCODE_WRITE_D_COMP 0x11
#define HSW_PCODE_DE_WRITE_FREQ_REQ 0x17
@@ -8881,6 +8882,8 @@ enum {
#define GEN9_SAGV_IS_DISABLED 0x1
#define GEN9_SAGV_ENABLE 0x3
#define GEN12_PCODE_READ_SAGV_BLOCK_TIME_US 0x23
+#define GEN11_PCODE_POINTS_RESTRICTED 0x0
+#define GEN11_PCODE_POINTS_RESTRICTED_MASK 0x1
#define GEN6_PCODE_DATA _MMIO(0x138128)
#define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8
#define GEN6_PCODE_FREQ_RING_RATIO_SHIFT 16