@@ -1714,7 +1714,12 @@ bool chv_phy_powergate_ch(struct drm_i915_private *dev_priv, enum dpio_phy phy,
static inline int intel_slpc_active(struct drm_i915_private *dev_priv)
{
- return 0;
+ int ret = 0;
+
+ if (dev_priv->guc.slpc.vma && dev_priv->guc.slpc.enabled)
+ ret = 1;
+
+ return ret;
}
/* intel_pm.c */
@@ -145,6 +145,8 @@ struct intel_guc {
uint64_t submissions[I915_NUM_ENGINES];
uint32_t last_seqno[I915_NUM_ENGINES];
+
+ struct intel_slpc slpc;
};
static inline int intel_slpc_enabled(void)
@@ -6656,7 +6656,8 @@ void intel_init_gt_powersave(struct drm_i915_private *dev_priv)
void intel_cleanup_gt_powersave(struct drm_i915_private *dev_priv)
{
- if (intel_slpc_enabled())
+ if (intel_slpc_enabled() &&
+ dev_priv->guc.slpc.vma)
intel_slpc_cleanup(dev_priv);
else if (IS_VALLEYVIEW(dev_priv))
valleyview_cleanup_gt_powersave(dev_priv);
@@ -6746,7 +6747,8 @@ void intel_enable_gt_powersave(struct drm_i915_private *dev_priv)
mutex_lock(&dev_priv->rps.hw_lock);
- if (intel_slpc_enabled()) {
+ if (intel_slpc_enabled() &&
+ dev_priv->guc.slpc.vma) {
gen9_enable_rc6(dev_priv);
intel_slpc_enable(dev_priv);
if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv))
@@ -22,15 +22,103 @@
*
*/
#include <linux/firmware.h>
+#include <asm/msr-index.h>
#include "i915_drv.h"
#include "intel_guc.h"
+static unsigned int slpc_get_platform_sku(struct drm_i915_private *dev_priv)
+{
+ enum slpc_platform_sku platform_sku;
+
+ if (IS_SKL_ULX(dev_priv))
+ platform_sku = SLPC_PLATFORM_SKU_ULX;
+ else if (IS_SKL_ULT(dev_priv))
+ platform_sku = SLPC_PLATFORM_SKU_ULT;
+ else
+ platform_sku = SLPC_PLATFORM_SKU_DT;
+
+ WARN_ON(platform_sku > 0xFF);
+
+ return platform_sku;
+}
+
+static unsigned int slpc_get_slice_count(struct drm_i915_private *dev_priv)
+{
+ unsigned int slice_count = 1;
+
+ if (IS_SKYLAKE(dev_priv))
+ slice_count = INTEL_INFO(dev_priv)->slice_total;
+
+ return slice_count;
+}
+
+static void slpc_shared_data_init(struct drm_i915_private *dev_priv)
+{
+ struct drm_i915_gem_object *obj;
+ struct page *page;
+ struct slpc_shared_data *data;
+ u64 msr_value;
+
+ if (!dev_priv->guc.slpc.vma)
+ return;
+
+ obj = dev_priv->guc.slpc.vma->obj;
+
+ page = i915_gem_object_get_page(obj, 0);
+ if (page) {
+ data = kmap_atomic(page);
+ memset(data, 0, sizeof(struct slpc_shared_data));
+
+ data->shared_data_size = sizeof(struct slpc_shared_data);
+ data->global_state = (u32)SLPC_GLOBAL_STATE_NOT_RUNNING;
+ data->platform_info.platform_sku =
+ (u8)slpc_get_platform_sku(dev_priv);
+ data->platform_info.slice_count =
+ (u8)slpc_get_slice_count(dev_priv);
+ data->platform_info.power_plan_source =
+ (u8)SLPC_POWER_PLAN_SOURCE(SLPC_POWER_PLAN_BALANCED,
+ SLPC_POWER_SOURCE_AC);
+ rdmsrl(MSR_TURBO_RATIO_LIMIT, msr_value);
+ data->platform_info.P0_freq = (u8)msr_value;
+ rdmsrl(MSR_PLATFORM_INFO, msr_value);
+ data->platform_info.P1_freq = (u8)(msr_value >> 8);
+ data->platform_info.Pe_freq = (u8)(msr_value >> 40);
+ data->platform_info.Pn_freq = (u8)(msr_value >> 48);
+
+ kunmap_atomic(data);
+ }
+}
+
void intel_slpc_init(struct drm_i915_private *dev_priv)
{
+ struct intel_guc *guc = &dev_priv->guc;
+ struct i915_vma *vma;
+
+ /* Allocate shared data structure */
+ vma = dev_priv->guc.slpc.vma;
+ if (!vma) {
+ vma = guc_allocate_vma(guc,
+ PAGE_ALIGN(sizeof(struct slpc_shared_data)));
+ if (IS_ERR(vma)) {
+ DRM_ERROR("slpc_shared_data allocation failed\n");
+ i915.enable_slpc = 0;
+ return;
+ }
+
+ dev_priv->guc.slpc.vma = vma;
+ }
+
+ slpc_shared_data_init(dev_priv);
}
void intel_slpc_cleanup(struct drm_i915_private *dev_priv)
{
+ struct intel_guc *guc = &dev_priv->guc;
+
+ /* Release shared data structure */
+ mutex_lock(&dev_priv->drm.struct_mutex);
+ i915_vma_unpin_and_release(&guc->slpc.vma);
+ mutex_unlock(&dev_priv->drm.struct_mutex);
}
void intel_slpc_suspend(struct drm_i915_private *dev_priv)
@@ -24,6 +24,105 @@
#ifndef _INTEL_SLPC_H_
#define _INTEL_SLPC_H_
+enum slpc_global_state {
+ SLPC_GLOBAL_STATE_NOT_RUNNING = 0,
+ SLPC_GLOBAL_STATE_INITIALIZING = 1,
+ SLPC_GLOBAL_STATE_RESETTING = 2,
+ SLPC_GLOBAL_STATE_RUNNING = 3,
+ SLPC_GLOBAL_STATE_SHUTTING_DOWN = 4,
+ SLPC_GLOBAL_STATE_ERROR = 5
+};
+
+enum slpc_platform_sku {
+ SLPC_PLATFORM_SKU_UNDEFINED = 0,
+ SLPC_PLATFORM_SKU_ULX = 1,
+ SLPC_PLATFORM_SKU_ULT = 2,
+ SLPC_PLATFORM_SKU_T = 3,
+ SLPC_PLATFORM_SKU_MOBL = 4,
+ SLPC_PLATFORM_SKU_DT = 5,
+ SLPC_PLATFORM_SKU_UNKNOWN = 6,
+};
+
+enum slpc_power_plan {
+ SLPC_POWER_PLAN_UNDEFINED = 0,
+ SLPC_POWER_PLAN_BATTERY_SAVER = 1,
+ SLPC_POWER_PLAN_BALANCED = 2,
+ SLPC_POWER_PLAN_PERFORMANCE = 3,
+ SLPC_POWER_PLAN_UNKNOWN = 4,
+};
+
+enum slpc_power_source {
+ SLPC_POWER_SOURCE_UNDEFINED = 0,
+ SLPC_POWER_SOURCE_AC = 1,
+ SLPC_POWER_SOURCE_DC = 2,
+ SLPC_POWER_SOURCE_UNKNOWN = 3,
+};
+
+#define SLPC_POWER_PLAN_SOURCE(plan, source) ((plan) | ((source) << 6))
+#define SLPC_POWER_PLAN(plan_source) ((plan_source) & 0x3F)
+#define SLPC_POWER_SOURCE(plan_source) ((plan_source) >> 6)
+
+struct slpc_platform_info {
+ u8 platform_sku;
+ u8 slice_count;
+ u8 reserved;
+ u8 power_plan_source;
+ u8 P0_freq;
+ u8 P1_freq;
+ u8 Pe_freq;
+ u8 Pn_freq;
+ u32 reserved1;
+ u32 reserved2;
+} __packed;
+
+struct slpc_task_state_data {
+ union {
+ u32 bitfield1;
+ struct {
+ u32 gtperf_task_active:1;
+ u32 gtperf_stall_possible:1;
+ u32 gtperf_gaming_mode:1;
+ u32 gtperf_target_fps:8;
+ u32 dcc_task_active:1;
+ u32 in_dcc:1;
+ u32 in_dct:1;
+ u32 freq_switch_active:1;
+ u32 ibc_enabled:1;
+ u32 ibc_active:1;
+ u32 pg1_enabled:1;
+ u32 pg1_active:1;
+ u32 reserved:13;
+ };
+ };
+ union {
+ u32 bitfield2;
+ struct {
+ u32 freq_unslice_max:8;
+ u32 freq_unslice_min:8;
+ u32 freq_slice_max:8;
+ u32 freq_slice_min:8;
+ };
+ };
+};
+
+#define SLPC_MAX_OVERRIDE_PARAMETERS 192
+#define SLPC_OVERRIDE_BITFIELD_SIZE ((SLPC_MAX_OVERRIDE_PARAMETERS + 31) / 32)
+
+struct slpc_shared_data {
+ u32 reserved;
+ u32 shared_data_size;
+ u32 global_state;
+ struct slpc_platform_info platform_info;
+ struct slpc_task_state_data task_state_data;
+ u32 override_parameters_set_bits[SLPC_OVERRIDE_BITFIELD_SIZE];
+ u32 override_parameters_values[SLPC_MAX_OVERRIDE_PARAMETERS];
+} __packed;
+
+struct intel_slpc {
+ struct i915_vma *vma;
+ bool enabled;
+};
+
/* intel_slpc.c */
void intel_slpc_init(struct drm_i915_private *dev_priv);
void intel_slpc_cleanup(struct drm_i915_private *dev_priv);