Add per-GPU sysfs files under /sys/class/drm/cardX/device/ptl to
control the Peak Tops Limiter (PTL) feature. Exposes ptl_enable
(enable/disable PTL), ptl_format (set/query preferred formats),
and ptl_supported_formats (list supported formats)

Example usage
-------------
Query PTL status:
    `cat /sys/class/drm/card1/device/ptl/ptl_enable`

Enable PTL:
    `sudo bash -c "echo 1 > /sys/class/drm/card1/device/ptl/ptl_enable"`

Disable PTL:
    `sudo bash -c "echo 0 > /sys/class/drm/card1/device/ptl/ptl_enable"`

Set PTL preferred formats:
    `sudo bash -c "echo I8,F32 > /sys/class/drm/card1/device/ptl/ptl_format"`

Query supported formats:
    `cat /sys/class/drm/card1/device/ptl/ptl_supported_formats`

v3 changes:
 * move N/A to previous format in format show(Alex)
 * fix format check for format store(Alex)
 * drop the ptl declarations into amdgpu_ptl.h(Alex)

v2 changes:
 * add usage commands in commit info (Alex)
 * move amdgpu_ptl_fmt into kgd_kfd_interface.h (Alex)

Signed-off-by: Perry Yuan <[email protected]>
Reviewed-by: Yifan Zhang <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_device.c |   2 +
 drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c    | 191 +++++++++++++++++++++
 drivers/gpu/drm/amd/include/amdgpu_ptl.h   |   4 +
 3 files changed, 197 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index a07fe386d275..da7585d1c6e7 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -4337,6 +4337,7 @@ static int amdgpu_device_sys_interface_init(struct 
amdgpu_device *adev)
        amdgpu_reg_state_sysfs_init(adev);
        amdgpu_xcp_sysfs_init(adev);
        amdgpu_uma_sysfs_init(adev);
+       amdgpu_ptl_sysfs_init(adev);
 
        return r;
 }
@@ -4353,6 +4354,7 @@ static void amdgpu_device_sys_interface_fini(struct 
amdgpu_device *adev)
        amdgpu_reg_state_sysfs_fini(adev);
        amdgpu_xcp_sysfs_fini(adev);
        amdgpu_uma_sysfs_fini(adev);
+       amdgpu_ptl_sysfs_fini(adev);
 }
 
 /**
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c 
b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
index 19b81f515374..a1481ad465a4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c
@@ -52,6 +52,15 @@ static int psp_load_smu_fw(struct psp_context *psp);
 static int psp_rap_terminate(struct psp_context *psp);
 static int psp_securedisplay_terminate(struct psp_context *psp);
 
+static const char * const amdgpu_ptl_fmt_str[] = {
+       [AMDGPU_PTL_FMT_I8]      = "I8",
+       [AMDGPU_PTL_FMT_F16]     = "F16",
+       [AMDGPU_PTL_FMT_BF16]    = "BF16",
+       [AMDGPU_PTL_FMT_F32]     = "F32",
+       [AMDGPU_PTL_FMT_F64]     = "F64",
+       [AMDGPU_PTL_FMT_INVALID] = "INVALID",
+};
+
 static int psp_ring_init(struct psp_context *psp,
                         enum psp_ring_type ring_type)
 {
@@ -1307,6 +1316,163 @@ int amdgpu_ptl_perf_monitor_ctrl(struct amdgpu_device 
*adev, u32 req_code,
 
        return psp_ptl_invoke(psp, req_code, ptl_state, &ptl_fmt1, &ptl_fmt2);
 }
+
+static enum amdgpu_ptl_fmt str_to_ptl_fmt(const char *str)
+{
+       int i;
+
+       for (i = 0; i < AMDGPU_PTL_FMT_INVALID; ++i) {
+               if (!strcmp(str, amdgpu_ptl_fmt_str[i]))
+                       return (enum amdgpu_ptl_fmt)i;
+       }
+
+       return AMDGPU_PTL_FMT_INVALID;
+}
+
+static ssize_t ptl_supported_formats_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       ssize_t len = 0;
+
+       for (int i = 0; i < AMDGPU_PTL_FMT_INVALID; ++i) {
+               len += sysfs_emit_at(buf, len, "%s%s",
+                               amdgpu_ptl_fmt_str[i],
+                               (i < AMDGPU_PTL_FMT_INVALID - 1) ? "," : "\n");
+       }
+
+       return len;
+}
+
+static ssize_t ptl_enable_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = drm_to_adev(ddev);
+       struct amdgpu_ptl *ptl = &adev->psp.ptl;
+       uint32_t ptl_state, fmt1, fmt2;
+       int ret;
+       bool enable;
+
+       mutex_lock(&ptl->mutex);
+       if (sysfs_streq(buf, "enabled") || sysfs_streq(buf, "1")) {
+               enable = true;
+       } else if (sysfs_streq(buf, "disabled") || sysfs_streq(buf, "0")) {
+               enable = false;
+       } else {
+               mutex_unlock(&ptl->mutex);
+               return -EINVAL;
+       }
+
+       fmt1 = ptl->fmt1;
+       fmt2 = ptl->fmt2;
+       ptl_state = enable ? 1 : 0;
+
+       ret = amdgpu_ptl_perf_monitor_ctrl(adev, PSP_PTL_PERF_MON_SET, 
&ptl_state, &fmt1, &fmt2);
+       if (ret) {
+               dev_err(adev->dev, "Failed to set PTL err = %d\n", ret);
+               mutex_unlock(&ptl->mutex);
+               return ret;
+       }
+
+       mutex_unlock(&ptl->mutex);
+       return count;
+}
+
+static ssize_t ptl_enable_show(struct device *dev, struct device_attribute 
*attr, char *buf)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = drm_to_adev(ddev);
+       struct amdgpu_ptl *ptl = &adev->psp.ptl;
+
+       return sysfs_emit(buf, "%s\n", ptl->enabled ? "enabled" : "disabled");
+}
+
+static ssize_t ptl_format_store(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = drm_to_adev(ddev);
+       char fmt1_str[8], fmt2_str[8];
+       enum amdgpu_ptl_fmt fmt1_enum, fmt2_enum;
+       struct amdgpu_ptl *ptl = &adev->psp.ptl;
+       uint32_t ptl_state, fmt1, fmt2;
+       int ret;
+
+       /* Only allow format update when PTL is enabled */
+       if (!ptl->enabled)
+               return -EPERM;
+
+       mutex_lock(&ptl->mutex);
+       /* Parse input, expecting "FMT1,FMT2" */
+       if (sscanf(buf, "%7[^,],%7s", fmt1_str, fmt2_str) != 2) {
+               mutex_unlock(&ptl->mutex);
+               return -EINVAL;
+       }
+
+       fmt1_enum = str_to_ptl_fmt(fmt1_str);
+       fmt2_enum = str_to_ptl_fmt(fmt2_str);
+
+       if (fmt1_enum >= AMDGPU_PTL_FMT_INVALID ||
+                       fmt2_enum >= AMDGPU_PTL_FMT_INVALID ||
+                       fmt1_enum == fmt2_enum) {
+               mutex_unlock(&ptl->mutex);
+               return -EINVAL;
+       }
+
+       ptl_state = ptl->enabled;
+       fmt1 = fmt1_enum;
+       fmt2 = fmt2_enum;
+       ret = amdgpu_ptl_perf_monitor_ctrl(adev, PSP_PTL_PERF_MON_SET, 
&ptl_state, &fmt1, &fmt2);
+       if (ret) {
+               dev_err(adev->dev, "Failed to update PTL err = %d\n", ret);
+               mutex_unlock(&ptl->mutex);
+               return ret;
+       }
+       mutex_unlock(&ptl->mutex);
+
+       return count;
+}
+
+static ssize_t ptl_format_show(struct device *dev, struct device_attribute 
*attr, char *buf)
+{
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = drm_to_adev(ddev);
+       struct psp_context *psp = &adev->psp;
+
+       return sysfs_emit(buf, "%s,%s\n",
+                       amdgpu_ptl_fmt_str[psp->ptl.fmt1],
+                       amdgpu_ptl_fmt_str[psp->ptl.fmt2]);
+}
+
+static umode_t amdgpu_ptl_is_visible(struct kobject *kobj, struct attribute 
*attr, int idx)
+{
+       struct device *dev = kobj_to_dev(kobj);
+       struct drm_device *ddev = dev_get_drvdata(dev);
+       struct amdgpu_device *adev = drm_to_adev(ddev);
+
+       /* Only show PTL sysfs files if PTL hardware is supported */
+       if (!adev->psp.ptl.hw_supported)
+               return 0;
+
+       return attr->mode;
+}
+
+int amdgpu_ptl_sysfs_init(struct amdgpu_device *adev)
+{
+       if (!adev->psp.ptl.hw_supported)
+               return 0;
+
+       return sysfs_create_group(&adev->dev->kobj, &amdgpu_ptl_attr_group);
+}
+
+void amdgpu_ptl_sysfs_fini(struct amdgpu_device *adev)
+{
+       if (!adev->psp.ptl.hw_supported)
+               return;
+
+       sysfs_remove_group(&adev->dev->kobj, &amdgpu_ptl_attr_group);
 }
 
 int psp_spatial_partition(struct psp_context *psp, int mode)
@@ -4303,6 +4469,31 @@ void psp_copy_fw(struct psp_context *psp, uint8_t 
*start_addr, uint32_t bin_size
 static DEVICE_ATTR(usbc_pd_fw, 0644,
                   psp_usbc_pd_fw_sysfs_read,
                   psp_usbc_pd_fw_sysfs_write);
+/**
+ * DOC: PTL sysfs attributes
+ * These sysfs files under /sys/class/drm/cardX/device/ptl allow users to 
enable or disable
+ * the Peak Tops Limiter (PTL), configure preferred PTL data formats, and 
query supported
+ * formats for each GPU.
+ */
+static DEVICE_ATTR(ptl_enable, 0644,
+                       ptl_enable_show, ptl_enable_store);
+static DEVICE_ATTR(ptl_format, 0644,
+                       ptl_format_show, ptl_format_store);
+static DEVICE_ATTR(ptl_supported_formats, 0444,
+                       ptl_supported_formats_show, NULL);
+
+static struct attribute *ptl_attrs[] = {
+       &dev_attr_ptl_enable.attr,
+       &dev_attr_ptl_format.attr,
+       &dev_attr_ptl_supported_formats.attr,
+       NULL,
+};
+
+const struct attribute_group amdgpu_ptl_attr_group = {
+       .name = "ptl",
+       .attrs = ptl_attrs,
+       .is_visible = amdgpu_ptl_is_visible,
+};
 
 int is_psp_fw_valid(struct psp_bin_desc bin)
 {
diff --git a/drivers/gpu/drm/amd/include/amdgpu_ptl.h 
b/drivers/gpu/drm/amd/include/amdgpu_ptl.h
index 12c9e0b4645a..e5ea1084bb09 100644
--- a/drivers/gpu/drm/amd/include/amdgpu_ptl.h
+++ b/drivers/gpu/drm/amd/include/amdgpu_ptl.h
@@ -45,4 +45,8 @@ struct amdgpu_ptl {
 int amdgpu_ptl_perf_monitor_ctrl(struct amdgpu_device *adev, u32 req_code,
                u32 *ptl_state, u32 *fmt1, u32 *fmt2);
 
+int amdgpu_ptl_sysfs_init(struct amdgpu_device *adev);
+void amdgpu_ptl_sysfs_fini(struct amdgpu_device *adev);
+
+extern const struct attribute_group amdgpu_ptl_attr_group;
 #endif /* __AMDGPU_PTL_H__ */
-- 
2.34.1

Reply via email to