]> www.infradead.org Git - users/hch/misc.git/commitdiff
drm/xe/guc: Add SLPC power profile interface
authorVinay Belgaumkar <vinay.belgaumkar@intel.com>
Wed, 3 Sep 2025 23:21:20 +0000 (16:21 -0700)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Thu, 11 Sep 2025 12:45:05 +0000 (08:45 -0400)
GuC has an interface to set a power profile for the SLPC algorithm.
Base mode is default and ensures a balanced performance, power_saving
mode has conservative up/down thresholds and is suitable for use with
apps that typically need to be power efficient. This will result in
lower GT frequencies, thus consuming lower power.

Selected power profile will be displayed in this format:

$ cat power_profile

  [base]    power_saving

$ echo power_saving > power_profile
$ cat power_profile

  base    [power_saving]

v2: Address review comments (Rodrigo)

Cc: Rodrigo Vivi <rodrigo.vivi@intel.com>
Signed-off-by: Vinay Belgaumkar <vinay.belgaumkar@intel.com>
Reviewed-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://lore.kernel.org/r/20250903232120.390190-1-vinay.belgaumkar@intel.com
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
drivers/gpu/drm/xe/abi/guc_actions_slpc_abi.h
drivers/gpu/drm/xe/xe_gt_freq.c
drivers/gpu/drm/xe/xe_guc_pc.c
drivers/gpu/drm/xe/xe_guc_pc.h
drivers/gpu/drm/xe/xe_guc_pc_types.h

index b28c8fa061f7be75972f66d44286b72d88d905ee..ce5c595175283f8293732e9979017f2cd273d472 100644 (file)
@@ -210,6 +210,11 @@ struct slpc_shared_data {
        u8 reserved_mode_definition[4096];
 } __packed;
 
+enum slpc_power_profile {
+       SLPC_POWER_PROFILE_BASE = 0x0,
+       SLPC_POWER_PROFILE_POWER_SAVING = 0x1
+};
+
 /**
  * DOC: SLPC H2G MESSAGE FORMAT
  *
index 60d9354e7dbf4baeaaac6b1f56192f116ce6a6d1..781e4890fb26f54eb1edc2828c5cf1481dae62c3 100644 (file)
@@ -227,6 +227,31 @@ static ssize_t max_freq_store(struct kobject *kobj,
 }
 static struct kobj_attribute attr_max_freq = __ATTR_RW(max_freq);
 
+static ssize_t power_profile_show(struct kobject *kobj,
+                                 struct kobj_attribute *attr,
+                                 char *buff)
+{
+       struct device *dev = kobj_to_dev(kobj);
+
+       xe_guc_pc_get_power_profile(dev_to_pc(dev), buff);
+
+       return strlen(buff);
+}
+
+static ssize_t power_profile_store(struct kobject *kobj,
+                                  struct kobj_attribute *attr,
+                                  const char *buff, size_t count)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct xe_guc_pc *pc = dev_to_pc(dev);
+       int err;
+
+       err = xe_guc_pc_set_power_profile(pc, buff);
+
+       return err ?: count;
+}
+static struct kobj_attribute attr_power_profile = __ATTR_RW(power_profile);
+
 static const struct attribute *freq_attrs[] = {
        &attr_act_freq.attr,
        &attr_cur_freq.attr,
@@ -236,6 +261,7 @@ static const struct attribute *freq_attrs[] = {
        &attr_rpn_freq.attr,
        &attr_min_freq.attr,
        &attr_max_freq.attr,
+       &attr_power_profile.attr,
        NULL
 };
 
index 88557e86d63714378d7d40e6dcd0ad8e99cd6db2..68a5bf8e394629199af83e2ec328261c2e206930 100644 (file)
  * Xe driver enables SLPC with all of its defaults features and frequency
  * selection, which varies per platform.
  *
+ * Power profiles add another level of control to SLPC. When power saving
+ * profile is chosen, SLPC will use conservative thresholds to ramp frequency,
+ * thus saving power. Base profile is default and ensures balanced performance
+ * for any workload.
+ *
  * Render-C States:
  * ================
  *
@@ -1171,6 +1176,61 @@ static int pc_action_set_strategy(struct xe_guc_pc *pc, u32 val)
        return ret;
 }
 
+static const char *power_profile_to_string(struct xe_guc_pc *pc)
+{
+       switch (pc->power_profile) {
+       case SLPC_POWER_PROFILE_BASE:
+               return "base";
+       case SLPC_POWER_PROFILE_POWER_SAVING:
+               return "power_saving";
+       default:
+               return "invalid";
+       }
+}
+
+void xe_guc_pc_get_power_profile(struct xe_guc_pc *pc, char *profile)
+{
+       switch (pc->power_profile) {
+       case SLPC_POWER_PROFILE_BASE:
+               sprintf(profile, "[%s]    %s\n", "base", "power_saving");
+               break;
+       case SLPC_POWER_PROFILE_POWER_SAVING:
+               sprintf(profile, "%s    [%s]\n", "base", "power_saving");
+               break;
+       default:
+               sprintf(profile, "invalid");
+       }
+}
+
+int xe_guc_pc_set_power_profile(struct xe_guc_pc *pc, const char *buf)
+{
+       int ret = 0;
+       u32 val;
+
+       if (strncmp("base", buf, strlen("base")) == 0)
+               val = SLPC_POWER_PROFILE_BASE;
+       else if (strncmp("power_saving", buf, strlen("power_saving")) == 0)
+               val = SLPC_POWER_PROFILE_POWER_SAVING;
+       else
+               return -EINVAL;
+
+       guard(mutex)(&pc->freq_lock);
+       xe_pm_runtime_get(pc_to_xe(pc));
+
+       ret = pc_action_set_param(pc,
+                                 SLPC_PARAM_POWER_PROFILE,
+                                 val);
+       if (ret)
+               xe_gt_err_once(pc_to_gt(pc), "Failed to set power profile to %d: %pe\n",
+                              val, ERR_PTR(ret));
+       else
+               pc->power_profile = val;
+
+       xe_pm_runtime_put(pc_to_xe(pc));
+
+       return ret;
+}
+
 /**
  * xe_guc_pc_start - Start GuC's Power Conservation component
  * @pc: Xe_GuC_PC instance
@@ -1249,6 +1309,11 @@ int xe_guc_pc_start(struct xe_guc_pc *pc)
        /* Enable SLPC Optimized Strategy for compute */
        ret = pc_action_set_strategy(pc, SLPC_OPTIMIZED_STRATEGY_COMPUTE);
 
+       /* Set cached value of power_profile */
+       ret = xe_guc_pc_set_power_profile(pc, power_profile_to_string(pc));
+       if (unlikely(ret))
+               xe_gt_err(gt, "Failed to set SLPC power profile: %pe\n", ERR_PTR(ret));
+
 out:
        xe_force_wake_put(gt_to_fw(gt), fw_ref);
        return ret;
@@ -1327,6 +1392,8 @@ int xe_guc_pc_init(struct xe_guc_pc *pc)
 
        pc->bo = bo;
 
+       pc->power_profile = SLPC_POWER_PROFILE_BASE;
+
        return devm_add_action_or_reset(xe->drm.dev, xe_guc_pc_fini_hw, pc);
 }
 
index 52ecdd5ddbff275beee4bae3d52f0ac888b73fb7..0e31396f103cd10f941b77f723ad26ff7efb2ed6 100644 (file)
@@ -31,6 +31,8 @@ int xe_guc_pc_get_min_freq(struct xe_guc_pc *pc, u32 *freq);
 int xe_guc_pc_set_min_freq(struct xe_guc_pc *pc, u32 freq);
 int xe_guc_pc_get_max_freq(struct xe_guc_pc *pc, u32 *freq);
 int xe_guc_pc_set_max_freq(struct xe_guc_pc *pc, u32 freq);
+int xe_guc_pc_set_power_profile(struct xe_guc_pc *pc, const char *buf);
+void xe_guc_pc_get_power_profile(struct xe_guc_pc *pc, char *profile);
 
 enum xe_gt_idle_state xe_guc_pc_c_status(struct xe_guc_pc *pc);
 u64 xe_guc_pc_rc6_residency(struct xe_guc_pc *pc);
index c02053948a579c20fa159f5032e901902b84c600..5e4ea53fbee6eea63ea3354002c71c7b48bff8a6 100644 (file)
@@ -37,6 +37,8 @@ struct xe_guc_pc {
        struct mutex freq_lock;
        /** @freq_ready: Only handle freq changes, if they are really ready */
        bool freq_ready;
+       /** @power_profile: Base or power_saving profile */
+       u32 power_profile;
 };
 
 #endif /* _XE_GUC_PC_TYPES_H_ */