summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-10-28 16:22:37 +1000
committerDave Airlie <airlied@redhat.com>2026-10-28 16:26:16 +1000
commitaf16ecbdf81301e88bb03e114706e0fc7c2a8e42 (patch)
tree267703e25921b4fa37ec0b2f7ce48f77febe0c44 /drivers/gpu
parentfad06cfaf385787b9ffa2f73f049d42dbe1de57f (diff)
parent9131cc85d0dedd8ba4cae60dbe5985d081551f30 (diff)
Merge tag 'amd-drm-next-fixes-7.4-2026-10-21' of https://gitlab.freedesktop.org/drm/amdgpu/kernel into drm-next
amd-drm-next-fixes-7.4-2026-10-21: amdgpu: - UALink fixes - Runtime PM fixes - GPUVM fixes - Mac fixes - HDMI fixes - DP MST fix - Userptr fix - SMU fixes - DSC fix amdkfd: - SVM fixes - Fix doorbell allocation race - GC 12.1 fix - GPU reset fix - Buffer accounting fixes Signed-off-by: Dave Airlie <airlied@redhat.com> From: Alex Deucher <alexander.deucher@amd.com> Link: https://patch.msgid.link/20261021173450.2959689-1-alexander.deucher@amd.com
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu.h11
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h19
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c44
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c87
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c28
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_object.c33
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c21
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c12
-rw-r--r--drivers/gpu/drm/amd/amdgpu/cik.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/si.c10
-rw-r--r--drivers/gpu/drm/amd/amdgpu/vi.c10
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c33
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c9
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c23
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_priv.h4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process.c4
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_queue.c23
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.c15
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h4
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml/dsc/rc_calc_fpu.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c3
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/link_dpms.c2
-rw-r--r--drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c129
-rw-r--r--drivers/gpu/drm/amd/include/kgd_pp_interface.h3
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_dpm.c103
-rw-r--r--drivers/gpu/drm/amd/pm/amdgpu_pm.c33
-rw-r--r--drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h2
-rw-r--r--drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c33
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c101
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h8
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_pmfw.h12
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_ppsmc.h5
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h1
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c35
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c32
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c38
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c151
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.h4
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c26
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h2
47 files changed, 950 insertions, 234 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index ce671731b700..c7577b608f3e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -844,8 +844,17 @@ struct amdgpu_device {
struct mutex mn_lock;
DECLARE_HASHTABLE(mn_hash, 7);
- /* tracking pinned memory */
+ /* tracking pinned memory
+ *
+ * vram_pin_size = sum(vram_pin_size_xcp[]) + vram_pin_size_common
+ *
+ * vram_pin_size total pin size in device
+ * vram_pin_size_xcp[i] pins owned by partition i (bo->xcp_id = i, i >= 0)
+ * vram_pin_size_common pins not owned by any partition, bo->xcp_id = -1
+ */
atomic64_t vram_pin_size;
+ atomic64_t vram_pin_size_xcp[MAX_XCP];
+ atomic64_t vram_pin_size_common;
atomic64_t visible_pin_size;
atomic64_t gart_pin_size;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
index e3da382ceb3c..77c0504cc158 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h
@@ -42,6 +42,12 @@
extern uint64_t amdgpu_amdkfd_total_mem_size;
+/*
+ * Align VRAM availability to 2MB to avoid fragmentation caused by 4K
+ * allocations in the tail 2MB BO chunk.
+ */
+#define VRAM_AVAILABLITY_ALIGN (1 << 21)
+
enum TLB_FLUSH_TYPE {
TLB_FLUSH_LEGACY = 0,
TLB_FLUSH_LIGHTWEIGHT,
@@ -106,6 +112,7 @@ struct amdgpu_kfd_dev {
struct kfd_dev *dev;
int64_t vram_used[MAX_XCP];
uint64_t vram_used_aligned[MAX_XCP];
+ atomic64_t vram_pinned[MAX_XCP];
bool init_complete;
struct work_struct reset_work;
@@ -389,6 +396,8 @@ void amdgpu_amdkfd_gpuvm_destroy_cb(struct amdgpu_device *adev,
* Allows KFD to release its resources associated with the GEM object.
*/
void amdgpu_amdkfd_release_notify(struct amdgpu_bo *bo);
+void amdgpu_amdkfd_account_vram_pin(struct amdgpu_bo *bo, int xcp_id);
+void amdgpu_amdkfd_account_vram_unpin(struct amdgpu_bo *bo, int xcp_id);
void amdgpu_amdkfd_reserve_system_mem(uint64_t size);
#else
static inline
@@ -406,6 +415,16 @@ static inline
void amdgpu_amdkfd_release_notify(struct amdgpu_bo *bo)
{
}
+
+static inline
+void amdgpu_amdkfd_account_vram_pin(struct amdgpu_bo *bo, int xcp_id)
+{
+}
+
+static inline
+void amdgpu_amdkfd_account_vram_unpin(struct amdgpu_bo *bo, int xcp_id)
+{
+}
#endif
#if IS_ENABLED(CONFIG_HSA_AMD_SVM)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c
index 0694bff871d2..5b526add8645 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v12_1.c
@@ -77,25 +77,21 @@ static uint32_t get_sdma_rlc_reg_offset(struct amdgpu_device *adev,
unsigned int engine_id,
unsigned int queue_id)
{
- uint32_t sdma_engine_reg_base = 0;
- uint32_t sdma_rlc_reg_offset;
uint32_t dev_inst = GET_INST(SDMA0, engine_id);
+ int xcc_id = adev->sdma.instance[engine_id].xcc_id;
+ int xcc_inst = dev_inst % adev->sdma.num_inst_per_xcc;
+ uint32_t sdma_engine_reg_base;
+ uint32_t sdma_rlc_reg_offset;
- switch (dev_inst % adev->sdma.num_inst_per_xcc) {
- case 0:
- sdma_engine_reg_base = SOC15_REG_OFFSET(SDMA0,
- dev_inst / adev->sdma.num_inst_per_xcc,
- regSDMA0_SDMA_QUEUE0_RB_CNTL) - regSDMA0_SDMA_QUEUE0_RB_CNTL;
- break;
- case 1:
- sdma_engine_reg_base = SOC15_REG_OFFSET(SDMA1,
- dev_inst / adev->sdma.num_inst_per_xcc,
- regSDMA1_SDMA_QUEUE0_RB_CNTL) - regSDMA0_SDMA_QUEUE0_RB_CNTL;
- break;
- default:
- WARN(1, "Invalid SDMA engine id %d\n", engine_id);
- break;
- }
+ /*
+ * On SDMA v7 (gc_12_x) all SDMA queue registers live in the GC_HWIP
+ * aperture. The second SDMA per XCC is reached by adding a fixed
+ * offset (regSDMA1_* - regSDMA0_* == SDMA1_REG_OFFSET
+ */
+ sdma_engine_reg_base = adev->reg_offset[GC_HWIP][xcc_id][0];
+ if (xcc_inst != 0)
+ sdma_engine_reg_base +=
+ regSDMA1_SDMA_QUEUE0_RB_CNTL - regSDMA0_SDMA_QUEUE0_RB_CNTL;
sdma_rlc_reg_offset = sdma_engine_reg_base
+ queue_id * (regSDMA0_SDMA_QUEUE1_RB_CNTL - regSDMA0_SDMA_QUEUE0_RB_CNTL);
@@ -141,8 +137,7 @@ static int hqd_sdma_dump_v12_1(struct amdgpu_device *adev,
uint32_t engine_id, uint32_t queue_id,
uint32_t (**dump)[2], uint32_t *n_regs)
{
- uint32_t sdma_rlc_reg_offset = get_sdma_rlc_reg_offset(adev,
- engine_id, queue_id);
+ uint32_t sdma_rlc_reg_offset;
uint32_t i = 0, reg;
const uint32_t first_reg = regSDMA0_SDMA_QUEUE0_RB_CNTL;
@@ -150,6 +145,17 @@ static int hqd_sdma_dump_v12_1(struct amdgpu_device *adev,
#undef HQD_N_REGS
#define HQD_N_REGS (last_reg - first_reg + 1)
+ /*
+ * dqm_debugfs_hqds() iterates engine_id over
+ * node_id * get_num_all_sdma_engines()..+get_num_all_sdma_engines().
+ * On partitioned devices that range can address a logical SDMA engine
+ * beyond the instances that actually exist on this node; reject it
+ */
+ if (engine_id >= adev->sdma.num_instances)
+ return -EINVAL;
+
+ sdma_rlc_reg_offset = get_sdma_rlc_reg_offset(adev, engine_id, queue_id);
+
*dump = kmalloc(HQD_N_REGS*2*sizeof(uint32_t), GFP_KERNEL);
if (*dump == NULL)
return -ENOMEM;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index 9a7e14a3c719..ff77e8ae9fe6 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -46,12 +46,6 @@
#define AMDGPU_USERPTR_RESTORE_DELAY_MS 1
#define AMDGPU_RESERVE_MEM_LIMIT (3UL << 29)
-/*
- * Align VRAM availability to 2MB to avoid fragmentation caused by 4K allocations in the tail 2MB
- * BO chunk
- */
-#define VRAM_AVAILABLITY_ALIGN (1 << 21)
-
/* Impose limit on how much memory KFD can use */
static struct {
uint64_t max_system_mem_limit;
@@ -73,6 +67,8 @@ static const char * const domain_bit_to_string[] = {
#define domain_string(domain) domain_bit_to_string[ffs(domain)-1]
static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work);
+static size_t __get_available_memory(struct amdgpu_device *adev,
+ uint8_t xcp_id, int64_t vram_used);
static bool kfd_mem_is_attached(struct amdgpu_vm *avm,
struct kgd_mem *mem)
@@ -169,13 +165,8 @@ void amdgpu_amdkfd_reserve_system_mem(uint64_t size)
int amdgpu_amdkfd_reserve_mem_limit(struct amdgpu_device *adev,
uint64_t size, u32 alloc_flag, int8_t xcp_id)
{
- uint64_t reserved_for_pt =
- ESTIMATE_PT_SIZE(amdgpu_amdkfd_total_mem_size);
- struct amdgpu_ras *con = amdgpu_ras_get_context(adev);
- uint64_t reserved_for_ras = (con ? con->reserved_pages_in_bytes : 0);
size_t system_mem_needed, ttm_mem_needed, vram_needed;
int ret = 0;
- uint64_t vram_size = 0;
system_mem_needed = 0;
ttm_mem_needed = 0;
@@ -196,7 +187,6 @@ int amdgpu_amdkfd_reserve_mem_limit(struct amdgpu_device *adev,
if (WARN_ONCE(xcp_id < 0, "invalid XCP ID %d", xcp_id))
return -EINVAL;
- vram_size = KFD_XCP_MEMORY_SIZE(adev, xcp_id);
if (adev->apu_prefer_gtt) {
system_mem_needed = size;
ttm_mem_needed = size;
@@ -232,14 +222,11 @@ int amdgpu_amdkfd_reserve_mem_limit(struct amdgpu_device *adev,
* VRAM check since ttm_mem_limit check already cover this allocation
*/
- if (adev && xcp_id >= 0 && (!adev->apu_prefer_gtt || adev->gmc.is_app_apu)) {
- uint64_t vram_available =
- vram_size - reserved_for_pt - reserved_for_ras -
- atomic64_read(&adev->vram_pin_size);
- if (adev->kfd.vram_used[xcp_id] + vram_needed > vram_available) {
- ret = -ENOMEM;
- goto release;
- }
+ if (adev && xcp_id >= 0 && (!adev->apu_prefer_gtt || adev->gmc.is_app_apu) &&
+ vram_needed > __get_available_memory(adev, xcp_id,
+ adev->kfd.vram_used[xcp_id])) {
+ ret = -ENOMEM;
+ goto release;
}
/* Update memory accounting by decreasing available system
@@ -318,6 +305,30 @@ void amdgpu_amdkfd_release_notify(struct amdgpu_bo *bo)
kfree(bo->kfd_bo);
}
+void amdgpu_amdkfd_account_vram_pin(struct amdgpu_bo *bo, int xcp_id)
+{
+ if (!bo->kfd_bo)
+ return;
+
+ if (WARN_ONCE(xcp_id < 0, "KFD BO pinned in VRAM with no partition"))
+ return;
+
+ atomic64_add(amdgpu_bo_size(bo),
+ &amdgpu_ttm_adev(bo->tbo.bdev)->kfd.vram_pinned[xcp_id]);
+}
+
+void amdgpu_amdkfd_account_vram_unpin(struct amdgpu_bo *bo, int xcp_id)
+{
+ if (!bo->kfd_bo)
+ return;
+
+ if (WARN_ONCE(xcp_id < 0, "KFD BO unpinned in VRAM with no partition"))
+ return;
+
+ atomic64_sub(amdgpu_bo_size(bo),
+ &amdgpu_ttm_adev(bo->tbo.bdev)->kfd.vram_pinned[xcp_id]);
+}
+
/**
* create_dmamap_sg_bo() - Creates a amdgpu_bo object to reflect information
* about USERPTR or DOOREBELL or MMIO BO.
@@ -1536,6 +1547,7 @@ static int amdgpu_amdkfd_gpuvm_pin_bo(struct amdgpu_bo *bo, u32 domain)
pr_err("Error in Pinning BO to domain: %d\n", domain);
amdgpu_bo_sync_wait(bo, AMDGPU_FENCE_OWNER_KFD, false);
+
out:
amdgpu_bo_unreserve(bo);
return ret;
@@ -1558,6 +1570,7 @@ static void amdgpu_amdkfd_gpuvm_unpin_bo(struct amdgpu_bo *bo)
return;
amdgpu_bo_unpin(bo);
+
amdgpu_bo_unreserve(bo);
}
@@ -1661,8 +1674,9 @@ out_unlock:
return ret;
}
-size_t amdgpu_amdkfd_get_available_memory(struct amdgpu_device *adev,
- uint8_t xcp_id)
+/* Caller must hold kfd_mem_limit.mem_limit_lock. */
+static size_t __get_available_memory(struct amdgpu_device *adev,
+ uint8_t xcp_id, int64_t vram_used)
{
uint64_t reserved_for_pt =
ESTIMATE_PT_SIZE(amdgpu_amdkfd_total_mem_size);
@@ -1671,14 +1685,17 @@ size_t amdgpu_amdkfd_get_available_memory(struct amdgpu_device *adev,
ssize_t available;
uint64_t vram_available, system_mem_available, ttm_mem_available;
- spin_lock(&kfd_mem_limit.mem_limit_lock);
+ lockdep_assert_held(&kfd_mem_limit.mem_limit_lock);
+
if (adev->apu_prefer_gtt && !adev->gmc.is_app_apu)
vram_available = KFD_XCP_MEMORY_SIZE(adev, xcp_id)
- - adev->kfd.vram_used_aligned[xcp_id];
+ - vram_used;
else
vram_available = KFD_XCP_MEMORY_SIZE(adev, xcp_id)
- - adev->kfd.vram_used_aligned[xcp_id]
- - atomic64_read(&adev->vram_pin_size)
+ - vram_used
+ - atomic64_read(&adev->vram_pin_size_xcp[xcp_id])
+ - atomic64_read(&adev->vram_pin_size_common)
+ + atomic64_read(&adev->kfd.vram_pinned[xcp_id])
- reserved_for_pt
- reserved_for_ras;
@@ -1693,19 +1710,29 @@ size_t amdgpu_amdkfd_get_available_memory(struct amdgpu_device *adev,
available = min3(system_mem_available, ttm_mem_available,
vram_available);
- available = ALIGN_DOWN(available, PAGE_SIZE);
} else {
- available = ALIGN_DOWN(vram_available, VRAM_AVAILABLITY_ALIGN);
+ available = vram_available;
}
- spin_unlock(&kfd_mem_limit.mem_limit_lock);
-
if (available < 0)
available = 0;
return available;
}
+size_t amdgpu_amdkfd_get_available_memory(struct amdgpu_device *adev,
+ uint8_t xcp_id)
+{
+ size_t available;
+
+ spin_lock(&kfd_mem_limit.mem_limit_lock);
+ available = __get_available_memory(adev, xcp_id,
+ adev->kfd.vram_used_aligned[xcp_id]);
+ spin_unlock(&kfd_mem_limit.mem_limit_lock);
+
+ return available;
+}
+
int amdgpu_amdkfd_gpuvm_alloc_memory_of_gpu(
struct amdgpu_device *adev, uint64_t va, uint64_t size,
void *drm_priv, struct kgd_mem **mem,
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index 48279392d385..f147c0c459da 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -593,10 +593,8 @@ bool amdgpu_device_supports_boco(struct amdgpu_device *adev)
if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
return false;
- if (adev->has_pr3 ||
- ((adev->flags & AMD_IS_PX) && amdgpu_is_atpx_hybrid()))
- return true;
- return false;
+ /* BOCO needs a platform power resource (_PR3) to cut the power */
+ return adev->has_pr3;
}
/**
@@ -2783,6 +2781,12 @@ static int amdgpu_device_ip_late_init(struct amdgpu_device *adev)
amdgpu_device_set_cg_state(adev, AMD_CG_STATE_GATE);
amdgpu_device_set_pg_state(adev, AMD_PG_STATE_GATE);
+ r = amdgpu_dpm_reapply_umd_profile(adev);
+ if (r) {
+ dev_err(adev->dev, "Failed to restore UMD profile state: %d\n", r);
+ return r;
+ }
+
amdgpu_device_fill_reset_magic(adev);
r = amdgpu_device_enable_mgpu_fan_boost();
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index ab6f9131ac11..3e5634de077f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2974,19 +2974,6 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
return 0;
}
-static void amdgpu_restore_umd_profile_pstate_after_runpm(struct amdgpu_device *adev)
-{
- enum amd_dpm_forced_level level;
- uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD |
- AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK |
- AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK |
- AMD_DPM_FORCED_LEVEL_PROFILE_PEAK;
-
- level = amdgpu_dpm_get_performance_level(adev);
- if (level & profile_mode_mask)
- amdgpu_asic_update_umd_stable_pstate(adev, true);
-}
-
static int amdgpu_pmops_runtime_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
@@ -3032,7 +3019,6 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX)
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
- amdgpu_restore_umd_profile_pstate_after_runpm(adev);
adev->in_runpm = false;
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
index 8e15b204f117..59ea7af5b0e4 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
@@ -780,9 +780,16 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
return copy_to_user(out, &count, min(size, 4u)) ? -EFAULT : 0;
}
- case AMDGPU_INFO_TIMESTAMP:
+ case AMDGPU_INFO_TIMESTAMP: {
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev->dev, pm);
+
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
ui64 = amdgpu_gfx_get_gpu_clock_counter(adev);
return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0;
+ }
case AMDGPU_INFO_FW_VERSION: {
struct drm_amdgpu_info_firmware fw_info;
@@ -906,6 +913,12 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
if (info->read_mmr_reg.count > 128)
return -EINVAL;
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev->dev, pm);
+
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
regs = kmalloc_array(info->read_mmr_reg.count, sizeof(*regs),
GFP_KERNEL);
if (!regs)
@@ -940,6 +953,13 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
uint64_t vm_size;
uint32_t pcie_gen_mask, pcie_width_mask;
+ /* The clock limits are only available while the SMU is up. */
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev->dev, pm);
+
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
dev_info = kzalloc_obj(*dev_info);
if (!dev_info)
return -ENOMEM;
@@ -1167,6 +1187,12 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
}
}
case AMDGPU_INFO_SENSOR: {
+ PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(dev->dev, pm);
+
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
if (!adev->pm.dpm_enabled)
return -ENOENT;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
index 2a6a5bc01eb4..6aa895f63aea 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c
@@ -923,6 +923,19 @@ void amdgpu_bo_unref(struct amdgpu_bo **bo)
*bo = NULL;
}
+/* Map a BO onto vram_pin_size_xcp[]. bo->xcp_id < 0 means "any partition":
+ * such pins are not owned by a single partition and go to
+ * vram_pin_size_common, which every partition subtracts.
+ */
+static int amdgpu_bo_get_xcp(struct amdgpu_bo *bo)
+{
+ s8 xcp = bo->xcp_id;
+
+ if (xcp >= 0 && xcp < MAX_XCP)
+ return xcp;
+ return -1;
+}
+
/**
* amdgpu_bo_pin - pin an &amdgpu_bo buffer object
* @bo: &amdgpu_bo buffer object to be pinned
@@ -1003,7 +1016,17 @@ int amdgpu_bo_pin(struct amdgpu_bo *bo, u32 domain)
ttm_bo_pin(&bo->tbo);
if (bo->tbo.resource->mem_type == TTM_PL_VRAM) {
+ int xcp = amdgpu_bo_get_xcp(bo);
+
atomic64_add(amdgpu_bo_size(bo), &adev->vram_pin_size);
+ if (xcp >= 0) {
+ atomic64_add(amdgpu_bo_size(bo),
+ &adev->vram_pin_size_xcp[xcp]);
+ } else {
+ atomic64_add(amdgpu_bo_size(bo),
+ &adev->vram_pin_size_common);
+ }
+ amdgpu_amdkfd_account_vram_pin(bo, xcp);
atomic64_add(amdgpu_vram_mgr_bo_visible_size(bo),
&adev->visible_pin_size);
} else if (bo->tbo.resource->mem_type == TTM_PL_TT) {
@@ -1036,7 +1059,17 @@ void amdgpu_bo_unpin(struct amdgpu_bo *bo)
dma_buf_unpin(bo->tbo.base.import_attach);
if (bo->tbo.resource->mem_type == TTM_PL_VRAM) {
+ int xcp = amdgpu_bo_get_xcp(bo);
+
atomic64_sub(amdgpu_bo_size(bo), &adev->vram_pin_size);
+ if (xcp >= 0) {
+ atomic64_sub(amdgpu_bo_size(bo),
+ &adev->vram_pin_size_xcp[xcp]);
+ } else {
+ atomic64_sub(amdgpu_bo_size(bo),
+ &adev->vram_pin_size_common);
+ }
+ amdgpu_amdkfd_account_vram_unpin(bo, xcp);
atomic64_sub(amdgpu_vram_mgr_bo_visible_size(bo),
&adev->visible_pin_size);
} else if (bo->tbo.resource->mem_type == TTM_PL_TT) {
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 6b3fbf79abc9..756ba0dad85d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -784,7 +784,8 @@ int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo,
goto out_unlock;
}
if (unlikely((gtt->userflags & AMDGPU_GEM_USERPTR_ANONONLY) &&
- vma->vm_file)) {
+ (vma->vm_file ||
+ vma->vm_end < start + (u64)ttm->num_pages * PAGE_SIZE))) {
r = -EPERM;
goto out_unlock;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
index 17888cf54447..6cd3927a7e82 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c
@@ -137,14 +137,21 @@ static bool __check_vpod_info(struct amdgpu_device *adev,
vpod->size, ppod->size);
return false;
}
- if (vpod->addr_mode >= AMDGPU_UALINK_ADDR_MODE_MAX) {
+
+ switch (vpod->addr_mode) {
+ case AMDGPU_UALINK_ADDR_MODE_SOURCE_ALIAS:
+ break;
+ case AMDGPU_UALINK_ADDR_MODE_SOURCE_IDENT:
dev_warn(adev->dev,
- "UALINK: invalid addr mode %u\n",
+ "UALINK: source-identification addr mode is not supported\n");
+ return false;
+ default:
+ dev_warn(adev->dev, "UALINK: invalid addr mode %u\n",
vpod->addr_mode);
return false;
}
- weight =
- bitmap_weight(vpod->active_accel_bits, AMDGPU_UALINK_ACCEL_MAX);
+
+ weight = bitmap_weight(vpod->active_accel_bits, AMDGPU_UALINK_ACCEL_MAX);
if (weight != vpod->size) {
dev_warn(adev->dev,
"UALINK: vPod size doesn't match vpod_active_accels list: %u != %u\n",
@@ -1351,8 +1358,7 @@ static ssize_t ualink_vpod_config_commit_store(struct kobject *kobj,
if (info->vpod.id != AMDGPU_UALINK_VPOD_ID_INVALID &&
!__check_vpod_info(adev, info)) {
dev_err(adev->dev,
- "UALINK: vpod-commit __check_vpod_info() failed: %d\n",
- r);
+ "UALINK: vpod-commit __check_vpod_info() failed\n");
return -EINVAL;
}
@@ -6075,9 +6081,8 @@ static int amdgpu_ualink_remote_shootdown(struct amdgpu_device *adev,
struct amdgpu_ualink_ring *ring;
int r;
- dev_dbg(adev->dev, "skip remote shootdown to remote_accel_id %u\n",
+ dev_dbg(adev->dev, "send remote shootdown to remote_accel_id %u\n",
remote_accel_id);
- return 0;
peer = &remote->peer[remote_accel_id];
ring = &peer->shootdown;
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 2bd07007def4..c41e9c0916b7 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -2737,10 +2737,7 @@ int amdgpu_vm_make_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm)
if (vm->use_cpu_for_update) {
/* Sync with last SDMA update/clear before switching to CPU */
- r = amdgpu_bo_sync_wait(vm->root.bo,
- AMDGPU_FENCE_OWNER_UNDEFINED, true);
- if (r)
- goto unreserve_bo;
+ dma_fence_wait(vm->last_update, false);
vm->update_funcs = &amdgpu_vm_cpu_funcs;
r = amdgpu_vm_pt_map_tables(adev, vm);
@@ -2797,6 +2794,7 @@ int amdgpu_vm_make_npa(struct amdgpu_device *adev, struct amdgpu_vm *vm)
return r;
vm->is_npa = true;
+ vm->need_tlb_fence = false;
return 0;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
index c03327f1242d..c73eb416c0e2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c
@@ -427,7 +427,7 @@ int amdgpu_vm_pt_clear(struct amdgpu_device *adev, struct amdgpu_vm *vm,
if (r)
goto exit;
- r = vm->update_funcs->commit(&params, NULL);
+ r = vm->update_funcs->commit(&params, &vm->last_update);
exit:
drm_dev_exit(idx);
return r;
@@ -531,13 +531,16 @@ static int amdgpu_vm_pt_alloc(struct amdgpu_vm_update_params *p,
r2 = amdgpu_vm_begin_critical(p);
if (r)
return r;
- if (r2)
- goto error_free_pt;
+
+ pt_bo = &pt->bo;
+ if (r2) {
+ r = r2;
+ goto error_unpin;
+ }
/* Keep a reference to the root directory to avoid
* freeing them up in the wrong order.
*/
- pt_bo = &pt->bo;
pt_bo->parent = amdgpu_bo_ref(cursor->parent->bo);
amdgpu_vm_bo_base_init(entry, p->vm, pt_bo);
r = amdgpu_vm_pt_clear(p->adev, p->vm, pt, p->immediate);
@@ -549,7 +552,6 @@ static int amdgpu_vm_pt_alloc(struct amdgpu_vm_update_params *p,
error_unpin:
if (p->vm->is_npa)
amdgpu_bo_unpin(pt_bo);
-error_free_pt:
amdgpu_bo_unref(&pt_bo);
return r;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/cik.c b/drivers/gpu/drm/amd/amdgpu/cik.c
index d29991a4af4c..e1029239b2c6 100644
--- a/drivers/gpu/drm/amd/amdgpu/cik.c
+++ b/drivers/gpu/drm/amd/amdgpu/cik.c
@@ -1927,6 +1927,16 @@ static bool cik_need_reset_on_init(struct amdgpu_device *adev)
{
u32 clock_cntl, pc;
+ /*
+ * MacBookPros with switchable graphics put VRAM at 0 when the iGPU
+ * is enabled. Any later post (S3 resume, GPU reset) moves it back to
+ * the vbios default, so reset and post on load to start with the
+ * vbios layout.
+ */
+ if (adev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
+ !(RREG32(mmMC_VM_FB_LOCATION) & MC_VM_FB_LOCATION__FB_BASE_MASK))
+ return true;
+
if (adev->flags & AMD_IS_APU)
return false;
diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
index 85ba228f002d..822681635a65 100644
--- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c
@@ -2179,10 +2179,22 @@ static int gmc_v9_0_hw_init(struct amdgpu_ip_block *ip_block)
* (type 2), which flushes both. Due to a race condition with
* concurrent memory accesses using the same TLB cache line, we still
* need a second TLB flush after this.
+ *
+ * GC 9.4.3, 9.4.4 and 9.5.0 hit a similar race.
*/
- adev->gmc.flush_tlb_needs_extra_type_2 =
- amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 0) &&
- adev->gmc.xgmi.num_physical_nodes;
+ switch (amdgpu_ip_version(adev, GC_HWIP, 0)) {
+ case IP_VERSION(9, 4, 0):
+ adev->gmc.flush_tlb_needs_extra_type_2 =
+ adev->gmc.xgmi.num_physical_nodes;
+ break;
+ case IP_VERSION(9, 4, 3):
+ case IP_VERSION(9, 4, 4):
+ case IP_VERSION(9, 5, 0):
+ adev->gmc.flush_tlb_needs_extra_type_2 = true;
+ break;
+ default:
+ break;
+ }
/* The sequence of these two function calls matters.*/
gmc_v9_0_init_golden_registers(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c
index 0adfd4b101aa..e5e590425f42 100644
--- a/drivers/gpu/drm/amd/amdgpu/si.c
+++ b/drivers/gpu/drm/amd/amdgpu/si.c
@@ -1512,6 +1512,16 @@ static void si_invalidate_hdp(struct amdgpu_device *adev,
static bool si_need_reset_on_init(struct amdgpu_device *adev)
{
+ /*
+ * MacBookPros with switchable graphics put VRAM at 0 when the iGPU
+ * is enabled. Any later post (S3 resume, GPU reset) moves it back to
+ * the vbios default, so reset and post on load to start with the
+ * vbios layout.
+ */
+ if (adev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
+ !(RREG32(mmMC_VM_FB_LOCATION) & MC_VM_FB_LOCATION__FB_BASE_MASK))
+ return true;
+
return false;
}
diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c
index a0435fd5525b..cad6bbfcedf3 100644
--- a/drivers/gpu/drm/amd/amdgpu/vi.c
+++ b/drivers/gpu/drm/amd/amdgpu/vi.c
@@ -1405,6 +1405,16 @@ static bool vi_need_reset_on_init(struct amdgpu_device *adev)
unsigned int i;
u32 clock_cntl, pc;
+ /*
+ * MacBookPros with switchable graphics put VRAM at 0 when the iGPU
+ * is enabled. Any later post (S3 resume, GPU reset) moves it back to
+ * the vbios default, so reset and post on load to start with the
+ * vbios layout.
+ */
+ if (adev->pdev->subsystem_vendor == PCI_VENDOR_ID_APPLE &&
+ !(RREG32(mmMC_VM_FB_LOCATION) & MC_VM_FB_LOCATION__FB_BASE_MASK))
+ return true;
+
for (i = 0; i < ARRAY_SIZE(vi_reset_quirks); i++)
if (adev->pdev->device == vi_reset_quirks[i].device &&
adev->pdev->subsystem_vendor == vi_reset_quirks[i].subsystem_vendor &&
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index 344da6c0e96a..cad60ccdb216 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -427,12 +427,10 @@ static int kfd_ioctl_create_queue(struct file *filep, struct kfd_process *p,
}
}
- if (!pdd->qpd.proc_doorbells) {
- err = kfd_alloc_process_doorbells(dev->kfd, pdd);
- if (err) {
- pr_debug("failed to allocate process doorbells\n");
- goto err_bind_process;
- }
+ err = kfd_alloc_process_doorbells(dev->kfd, pdd);
+ if (err) {
+ pr_debug("failed to allocate process doorbells\n");
+ goto err_bind_process;
}
err = kfd_queue_acquire_buffers(pdd, &q_properties);
@@ -1112,11 +1110,19 @@ static int kfd_ioctl_get_available_memory(struct file *filep,
{
struct kfd_ioctl_get_available_memory_args *args = data;
struct kfd_process_device *pdd = kfd_lock_pdd_by_id(p, args->gpu_id);
+ struct amdgpu_device *adev;
+ size_t available;
if (!pdd)
return -EINVAL;
- args->available = amdgpu_amdkfd_get_available_memory(pdd->dev->adev,
- pdd->dev->node_id);
+
+ adev = pdd->dev->adev;
+ available = amdgpu_amdkfd_get_available_memory(adev, pdd->dev->node_id);
+ if (adev->apu_prefer_gtt)
+ args->available = ALIGN_DOWN(available, PAGE_SIZE);
+ else
+ args->available = ALIGN_DOWN(available, VRAM_AVAILABLITY_ALIGN);
+
kfd_unlock_pdd(pdd);
return 0;
}
@@ -1305,6 +1311,9 @@ static int kfd_ioctl_free_memory_of_gpu(struct file *filep,
goto err_unlock;
}
+ /* Queues a reset could not stop may still access the buffer */
+ kfd_process_wait_gpu_reset_complete(p);
+
ret = amdgpu_amdkfd_gpuvm_free_memory_of_gpu(pdd->dev->adev,
(struct kgd_mem *)mem, pdd->drm_priv, &size);
@@ -2455,11 +2464,9 @@ static int criu_restore_devices(struct kfd_process *p,
goto exit;
}
- if (!pdd->qpd.proc_doorbells) {
- ret = kfd_alloc_process_doorbells(dev->kfd, pdd);
- if (ret)
- goto exit;
- }
+ ret = kfd_alloc_process_doorbells(dev->kfd, pdd);
+ if (ret)
+ goto exit;
}
/*
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
index 5e57576fba21..ce67e56efa8c 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c
@@ -4215,6 +4215,15 @@ int dqm_debugfs_hqds(struct seq_file *m, void *data)
queue++) {
r = dqm->dev->kfd2kgd->hqd_sdma_dump(
dqm->dev->adev, pipe, queue, &dump, &n_regs);
+ if (r == -EINVAL) {
+ /*
+ * SDMA engine not present/mapped on this
+ * device; skip it instead of aborting the
+ * whole dump.
+ */
+ r = 0;
+ break;
+ }
if (r)
break;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
index fdcf7f2d1b5b..6af268a031f2 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c
@@ -239,11 +239,9 @@ phys_addr_t kfd_get_process_doorbells(struct kfd_process_device *pdd)
struct amdgpu_device *adev = pdd->dev->adev;
uint32_t first_db_index;
- if (!pdd->qpd.proc_doorbells) {
- if (kfd_alloc_process_doorbells(pdd->dev->kfd, pdd))
- /* phys_addr_t 0 is error */
- return 0;
- }
+ if (kfd_alloc_process_doorbells(pdd->dev->kfd, pdd))
+ /* phys_addr_t 0 is error */
+ return 0;
first_db_index = amdgpu_doorbell_index_on_bar(adev,
pdd->qpd.proc_doorbells,
@@ -257,12 +255,21 @@ int kfd_alloc_process_doorbells(struct kfd_dev *kfd, struct kfd_process_device *
int r;
struct qcm_process_device *qpd = &pdd->qpd;
+ mutex_lock(&pdd->doorbell_mutex);
+
+ /* Another thread may have won the check-and-allocate race */
+ if (qpd->proc_doorbells) {
+ mutex_unlock(&pdd->doorbell_mutex);
+ return 0;
+ }
+
/* Allocate bitmap for dynamic doorbell allocation */
qpd->doorbell_bitmap = bitmap_zalloc(KFD_MAX_NUM_OF_QUEUES_PER_PROCESS,
GFP_KERNEL);
if (!qpd->doorbell_bitmap) {
DRM_ERROR("Failed to allocate process doorbell bitmap\n");
- return -ENOMEM;
+ r = -ENOMEM;
+ goto unlock;
}
r = init_doorbell_bitmap(&pdd->qpd, kfd);
@@ -284,11 +291,15 @@ int kfd_alloc_process_doorbells(struct kfd_dev *kfd, struct kfd_process_device *
DRM_ERROR("Failed to allocate process doorbells\n");
goto err;
}
+
+ mutex_unlock(&pdd->doorbell_mutex);
return 0;
err:
bitmap_free(qpd->doorbell_bitmap);
qpd->doorbell_bitmap = NULL;
+unlock:
+ mutex_unlock(&pdd->doorbell_mutex);
return r;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
index b9a31489b1c8..288731acc928 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h
@@ -784,6 +784,9 @@ struct kfd_process_device {
/* per-process-per device QCM data structure */
struct qcm_process_device qpd;
+ /* Serializes process doorbell allocation for this device */
+ struct mutex doorbell_mutex;
+
/*Apertures*/
uint64_t lds_base;
uint64_t lds_limit;
@@ -1130,6 +1133,7 @@ void kfd_unref_process(struct kfd_process *p);
int kfd_process_evict_queues(struct kfd_process *p, uint32_t trigger);
int kfd_process_restore_queues(struct kfd_process *p);
void kfd_suspend_all_processes(void);
+void kfd_process_wait_gpu_reset_complete(struct kfd_process *p);
int kfd_resume_all_processes(void);
struct kfd_process_device *kfd_process_device_data_by_id(struct kfd_process *process,
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
index 226f52626e84..6663718fb83c 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c
@@ -1233,6 +1233,7 @@ static void kfd_process_destroy_pdds(struct kfd_process *p)
}
atomic_dec(&pdd->dev->kfd->kfd_processes_count);
+ mutex_destroy(&pdd->doorbell_mutex);
kfree(pdd);
p->pdds[i] = NULL;
@@ -1293,7 +1294,7 @@ static void kfd_process_remove_sysfs(struct kfd_process *p)
/*
* If any GPU is ongoing reset, wait for reset complete.
*/
-static void kfd_process_wait_gpu_reset_complete(struct kfd_process *p)
+void kfd_process_wait_gpu_reset_complete(struct kfd_process *p)
{
int i;
@@ -1802,6 +1803,7 @@ struct kfd_process_device *kfd_create_process_device_data(struct kfd_node *dev,
pdd->dev = dev;
INIT_LIST_HEAD(&pdd->qpd.queues_list);
INIT_LIST_HEAD(&pdd->qpd.priv_queue_list);
+ mutex_init(&pdd->doorbell_mutex);
pdd->qpd.dqm = dev->dqm;
pdd->qpd.pqm = &p->pqm;
pdd->qpd.evicted = 0;
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
index ae3e7c54014a..6010a5d7a7eb 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c
@@ -230,6 +230,15 @@ void kfd_queue_buffer_put(struct amdgpu_bo **bo)
amdgpu_bo_unref(bo);
}
+static void kfd_queue_put_bos(struct queue_properties *properties)
+{
+ kfd_queue_buffer_put(&properties->wptr_bo);
+ kfd_queue_buffer_put(&properties->rptr_bo);
+ kfd_queue_buffer_put(&properties->ring_bo);
+ kfd_queue_buffer_put(&properties->eop_buf_bo);
+ kfd_queue_buffer_put(&properties->cwsr_bo);
+}
+
int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_properties *properties)
{
struct kfd_topology_device *topo_dev;
@@ -343,7 +352,13 @@ out_err_release:
* dropping the VM reservation.
*/
kfd_queue_unref_bo_vas(pdd, properties);
- kfd_queue_release_buffers(pdd, properties);
+ /*
+ * The SVM CWSR range is the last thing acquired and a failed
+ * kfd_queue_buffer_svm_get() takes nothing, so only the BOs are held
+ * here. Dropping the SVM reference would drop the one of another queue
+ * using the same CWSR address.
+ */
+ kfd_queue_put_bos(properties);
return err;
}
@@ -352,11 +367,7 @@ int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_prope
struct kfd_topology_device *topo_dev;
u64 total_cwsr_size;
- kfd_queue_buffer_put(&properties->wptr_bo);
- kfd_queue_buffer_put(&properties->rptr_bo);
- kfd_queue_buffer_put(&properties->ring_bo);
- kfd_queue_buffer_put(&properties->eop_buf_bo);
- kfd_queue_buffer_put(&properties->cwsr_bo);
+ kfd_queue_put_bos(properties);
topo_dev = kfd_topology_device_by_id(pdd->dev->id);
if (!topo_dev)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index cfb428c72686..1f5d73f0ebce 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -2158,14 +2158,25 @@ static struct svm_range *svm_range_clone(struct svm_range *old)
svm_range_free(new, false);
return NULL;
}
- if (old->svm_bo) {
+
+ /* old->lock keeps old->svm_bo alive while taking the reference */
+ mutex_lock(&old->lock);
+ if (svm_bo_ref_unless_zero(old->svm_bo)) {
new->ttm_res = old->ttm_res;
new->offset = old->offset;
- new->svm_bo = svm_range_bo_ref(old->svm_bo);
+ new->svm_bo = old->svm_bo;
spin_lock(&new->svm_bo->list_lock);
list_add(&new->svm_bo_list, &new->svm_bo->range_list);
spin_unlock(&new->svm_bo->list_lock);
}
+ mutex_unlock(&old->lock);
+
+ /* prange only holds an svm_bo reference while ttm_res is set, unref
+ * outside old->lock, svm_range_bo_release takes it and would deadlock.
+ */
+ if (new->svm_bo && !new->ttm_res)
+ svm_range_bo_unref(new->svm_bo);
+
new->flags = old->flags;
new->preferred_loc = old->preferred_loc;
new->prefetch_loc = old->prefetch_loc;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index 3217aa82bea2..6febc15ebc2b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -309,7 +309,7 @@ fill_dc_mst_payload_table_from_drm(struct dc_link *link,
break;
}
}
- ASSERT(i != copy_of_link_table.stream_count);
+ /* Payload absent from HW table when never allocated: nothing to remove. */
}
/* Fill payload info*/
diff --git a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
index eb6e7f4043fd..9e0fe41f28a5 100644
--- a/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
+++ b/drivers/gpu/drm/amd/display/dc/dc_hdmi_types.h
@@ -235,7 +235,8 @@ enum link_result {
LINK_RESULT_SUCCESS,
LINK_RESULT_LOWER_LINKRATE,
LINK_RESULT_TIMEOUT,
- LINK_RESULT_FALLBACK
+ LINK_RESULT_FALLBACK,
+ LINK_RESULT_NO_TIMEOUT
};
enum hdmi_frl_link_rate {
@@ -274,6 +275,7 @@ struct dc_hdmi_frl_flags {
bool force_frl_dsc;
bool force_frl_max;
bool apply_vsdb_rcc_wa;
+ bool no_timeout;
};
struct dc_hdmi_frl_link_training_overrides {
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dsc/rc_calc_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dsc/rc_calc_fpu.c
index 71eedb3301e4..62673dc8b04a 100644
--- a/drivers/gpu/drm/amd/display/dc/dml/dsc/rc_calc_fpu.c
+++ b/drivers/gpu/drm/amd/display/dc/dml/dsc/rc_calc_fpu.c
@@ -45,7 +45,7 @@ static int median3(int a, int b, int c)
if (b > c)
swap(b, c);
if (a > b)
- swap(b, c);
+ swap(a, b);
return b;
}
diff --git a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c
index a2c0fa21ee94..62288133cd55 100644
--- a/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c
+++ b/drivers/gpu/drm/amd/display/dc/link/hwss/link_hwss_hpo_frl.c
@@ -59,6 +59,9 @@ static void disable_hpo_frl_link_output(struct dc_link *link,
enum signal_type signal)
{
(void)link_res;
+ if (link->frl_flags.no_timeout)
+ return;
+
if (dc_is_hdmi_frl_signal(signal))
link->hpo_frl_link_enc->funcs->disable_link_encoder(link->hpo_frl_link_enc);
link->link_enc->funcs->disable_output(link->link_enc, signal);
diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
index 0d3b65d40807..59a3f4b88a37 100644
--- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
+++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c
@@ -2038,6 +2038,8 @@ static enum dc_status enable_link_hdmi_frl(struct pipe_ctx *pipe_ctx)
pipe_ctx->stream_res.hpo_frl_stream_enc->funcs->hdmi_frl_enable(
pipe_ctx->stream_res.hpo_frl_stream_enc,
pipe_ctx->stream_res.tg->inst);
+ } else if (link_stat == LINK_RESULT_NO_TIMEOUT) {
+ return status;
} else {
status = DC_FAIL_HDMI_FRL_LINK_TRAINING;
stream->link->frl_link_settings.frl_link_rate = 0;
diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
index 5e0979ac3dcf..960f18fe275b 100644
--- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
+++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_hdmi_frl.c
@@ -442,7 +442,7 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
union hdmi_scdc_status_flags_data status_data = {0};
union hdmi_scdc_source_test_req test_req = {0};
union hdmi_scdc_LTP_req_data ltp_req = {0};
- unsigned long long poll_timeout_ns = 200 * 1000 * 1000;
+ unsigned long long poll_timeout_ns = 220 * 1000 * 1000;
unsigned long long wait_time_ns = 2 * 1000 * 1000;
unsigned long long num_polls = 0;
struct hpo_frl_link_encoder *hpo_frl_link_enc = ddc_service->link->hpo_frl_link_enc;
@@ -451,11 +451,37 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
uint8_t FFE_Levels = hdmi_frl_get_max_ffe_level(ddc_service->link, link_settings);
uint8_t current_FFE = 0;
bool override_FFE = false;
- bool flt_no_timeout = false;
DC_LOGGER_INIT(ddc_service->link->ctx->logger);
FRL_INFO("FRL LINK TRAINING: Starting FRL Link Training.\n");
+ /*Test Condition - FLT_no_timeout avoid link training*/
+ offset = HDMI_SCDC_SOURCE_TEST_REQ;
+ link_query_ddc_data(ddc_service, slave_address, &offset,
+ sizeof(offset), &test_req.byte, sizeof(test_req.byte));
+ FRL_INFO("FRL TEST REQ: FLT_no_timeout = %d \n", test_req.fields.FLT_NO_TIMEOUT);
+
+ if (test_req.fields.FLT_NO_TIMEOUT && ddc_service->link->frl_flags.no_timeout) {
+ bool de_emphasis_only = false;
+ bool pre_shoot_only = false;
+ bool no_ffe = false;
+
+ read_idcc_data(ddc_service, HDMI_IDCC_SCOPE_RW_CA,
+ ddc_service->link->hdmi_cable_id.raw, 0,
+ sizeof(ddc_service->link->hdmi_cable_id.raw));
+
+ hdmi_return_preeshoot_and_deemphasis(ddc_service->link, &test_req,
+ &de_emphasis_only, &pre_shoot_only, &no_ffe);
+
+ dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0,
+ de_emphasis_only,
+ pre_shoot_only,
+ no_ffe,
+ link_settings);
+
+ return LINK_RESULT_NO_TIMEOUT;
+ }
+
hdmi_frl_LTS_clear_Link_Setting(ddc_service);
offset = HDMI_SCDC_SINK_VERSION;
@@ -511,13 +537,68 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
}
} while (elapsed < poll_timeout_ns);
- /*Test Condition - FLT_no_timeout avoid link training*/
- offset = HDMI_SCDC_SOURCE_TEST_REQ;
- link_query_ddc_data(ddc_service, slave_address, &offset,
- sizeof(offset), &test_req.byte, sizeof(test_req.byte));
- FRL_INFO("FRL TEST REQ: FLT_no_timeout = %d \n", test_req.fields.FLT_NO_TIMEOUT);
- if (test_req.fields.FLT_NO_TIMEOUT)
- flt_no_timeout = true;
+ if (test_req.fields.FLT_NO_TIMEOUT) {
+ ddc_service->link->frl_flags.no_timeout = true;
+
+ /*Specify FRL rate*/
+ write_buffer[0] = HDMI_SCDC_CONFIG_1;
+ /*FRL_RATE*/
+ write_buffer[1] = (uint8_t)(link_settings->frl_link_rate | (FFE_Levels << 4));
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+ FRL_INFO("FRL LINK TRAINING: Write link rate = %d. Max FFE_Levels = %d\n",
+ link_settings->frl_link_rate, FFE_Levels);
+
+ offset = HDMI_SCDC_UPDATE_0;
+ /*Check FLT_update flag*/
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), &scdc_update.byte[0],
+ sizeof(scdc_update.byte[0]));
+
+ offset = HDMI_SCDC_LTP_REQ;
+ link_query_ddc_data(ddc_service, slave_address,
+ &offset, sizeof(offset), ltp_req.byte,
+ sizeof(ltp_req.byte));
+
+ ln0_pattern = ltp_req.fields.LN0_LTP_REQ;
+ ln1_pattern = ltp_req.fields.LN1_LTP_REQ;
+ ln2_pattern = ltp_req.fields.LN2_LTP_REQ;
+ ln3_pattern = ltp_req.fields.LN3_LTP_REQ;
+
+ hpo_frl_link_enc->funcs->set_hdmi_training_pattern(
+ hpo_frl_link_enc,
+ ln0_pattern - 1,
+ ln1_pattern - 1,
+ ln2_pattern - 1,
+ ln3_pattern - 1);
+
+ read_idcc_data(ddc_service, HDMI_IDCC_SCOPE_RW_CA,
+ ddc_service->link->hdmi_cable_id.raw, 0,
+ sizeof(ddc_service->link->hdmi_cable_id.raw));
+
+ bool de_emphasis_only = false;
+ bool pre_shoot_only = false;
+ bool no_ffe = false;
+
+ hdmi_return_preeshoot_and_deemphasis(ddc_service->link, &test_req,
+ &de_emphasis_only, &pre_shoot_only, &no_ffe);
+
+ dio_link_enc->funcs->prog_eq_setting(dio_link_enc, 0,
+ de_emphasis_only,
+ pre_shoot_only,
+ no_ffe,
+ link_settings);
+
+ write_buffer[0] = HDMI_SCDC_UPDATE_0;
+ /*FLT_update - bit 5*/
+ write_buffer[1] = (scdc_update.fields.FLT_UPDATE << 5);
+ link_query_ddc_data(ddc_service, slave_address,
+ write_buffer, sizeof(write_buffer), NULL, 0);
+
+ return LINK_RESULT_NO_TIMEOUT;
+ }
+
+ ddc_service->link->frl_flags.no_timeout = false;
if (status_data.fields.FLT_READY) {
/*Specify FRL rate*/
@@ -534,9 +615,7 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
/*Start FLT Timer = 200 ms, or 300ms if link rate >= 16Gbps*/
num_polls = 0;
if (link_settings->frl_link_rate >= HDMI_FRL_LINK_RATE_16GBPS)
- poll_timeout_ns = 300 * 1000 * 1000;
- if (flt_no_timeout)
- poll_timeout_ns = 1000 * 1000 * 1000;
+ poll_timeout_ns = 320 * 1000 * 1000;
flt_poll_start = dm_get_timestamp(ddc_service->ctx);
uint8_t prev_flt_update = 0;
@@ -561,21 +640,9 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
prev_flt_update = scdc_update.fields.FLT_UPDATE;
}
- if (flt_no_timeout) {
- offset = HDMI_SCDC_SOURCE_TEST_REQ;
- link_query_ddc_data(ddc_service, slave_address, &offset,
- sizeof(offset), &test_req.byte, sizeof(test_req.byte));
- if (test_req.fields.FLT_NO_TIMEOUT == 0) {
- FRL_INFO("FRL TEST REQ: FLT_no_timeout = %d \n", test_req.fields.FLT_NO_TIMEOUT);
- return LINK_RESULT_SUCCESS;
- }
- }
-
/*Set TxFFE = TxFFE0*/
/*Program FFE_Levels - scdc_config has this field at 0 */
if (override_FFE) {
- if (flt_no_timeout)
- current_FFE = 0;
if (current_FFE == 0)
dio_link_enc->funcs->prog_eq_setting(dio_link_enc, current_FFE,
test_req.fields.TXFFE_DEEMPHASIS,
@@ -626,8 +693,7 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
if (ln0_pattern == 0x03 || ln1_pattern == 0x03 ||
ln2_pattern == 0x03 || ln3_pattern == 0x03)
- if (!flt_no_timeout)
- continue;
+ continue;
if (link_settings->frl_num_lanes == 3) {
ln3_pattern = 1;
@@ -651,8 +717,6 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
override_FFE = true;
if (current_FFE > FFE_Levels)
current_FFE = 0;
- if (flt_no_timeout)
- current_FFE = 0;
}
} else {
if (!ln0_pattern && !ln1_pattern && !ln2_pattern && !ln3_pattern) {
@@ -677,8 +741,6 @@ static enum link_result hdmi_frl_perform_link_training(struct ddc_service *ddc_s
override_FFE = true;
if (current_FFE > FFE_Levels)
current_FFE = 0;
- if (flt_no_timeout)
- current_FFE = 0;
}
}
@@ -733,7 +795,7 @@ enum link_result hdmi_frl_perform_link_training_with_retries(
/* FRL Link Training */
while (status != LINK_RESULT_FALLBACK) {
- if (status == LINK_RESULT_SUCCESS) {
+ if (status == LINK_RESULT_SUCCESS || status == LINK_RESULT_NO_TIMEOUT) {
break;
} else if (status == LINK_RESULT_LOWER_LINKRATE) {
FRL_INFO("FRL LINK TRAINING: Sink requested lower link rate during link enable. \n");
@@ -780,6 +842,9 @@ enum link_result hdmi_frl_perform_link_training_with_fallback(
status = hdmi_frl_perform_link_training(link->ddc,
&link->frl_link_settings);
+ if (status == LINK_RESULT_NO_TIMEOUT)
+ break;
+
link->dc->hwss.disable_link_output(link, link_res, SIGNAL_TYPE_HDMI_FRL);
if (status == LINK_RESULT_SUCCESS)
@@ -892,7 +957,7 @@ void hdmi_frl_verify_link_cap(struct dc_link *link,
status = hdmi_frl_perform_link_training_with_fallback(link, &link_res, frl_phy_clock_source_id);
- if (status == LINK_RESULT_SUCCESS) {
+ if (status == LINK_RESULT_SUCCESS || status == LINK_RESULT_NO_TIMEOUT) {
cur->frl_link_rate = link->frl_link_settings.frl_link_rate;
cur->frl_num_lanes = link->frl_link_settings.frl_num_lanes;
success = true;
diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
index 4b863a2e38f1..7db44fa24042 100644
--- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h
+++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h
@@ -449,6 +449,8 @@ struct amd_pm_funcs {
int (*print_clock_levels)(void *handle, enum pp_clock_type type, char *buf);
int (*emit_clock_levels)(void *handle, enum pp_clock_type type, char *buf, int *offset);
int (*force_performance_level)(void *handle, enum amd_dpm_forced_level level);
+ int (*reapply_performance_level)(void *handle,
+ enum amd_dpm_forced_level level);
int (*get_sclk_od)(void *handle);
int (*set_sclk_od)(void *handle, uint32_t value);
int (*get_mclk_od)(void *handle);
@@ -530,6 +532,7 @@ struct amd_pm_funcs {
ssize_t (*get_temp_metrics)(void *handle, enum smu_temp_metric_type type, void *table);
bool (*temp_metrics_is_supported)(void *handle, enum smu_temp_metric_type type);
u64 (*get_npm_cap)(void *handle);
+ int (*set_npm_limit)(void *handle, uint32_t limit);
ssize_t (*get_xcp_metrics)(void *handle, int xcp_id, void *table);
ssize_t (*get_pm_metrics)(void *handle, void *pmmetrics, size_t size);
int (*set_watermarks_for_clock_ranges)(void *handle,
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index df5bbdab1ea3..75d8bb4868e8 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -1082,6 +1082,91 @@ static void amdgpu_dpm_exit_umd_state(struct amdgpu_device *adev)
AMD_PG_STATE_GATE);
}
+/**
+ * amdgpu_dpm_reapply_umd_profile - Restore the active UMD profile
+ * @adev: amdgpu device
+ *
+ * Resume and reset retain the selected DPM level in software, but the
+ * underlying GFX and PMFW state is lost. Reapply a retained UMD profile
+ * after the common resume path has restored its default CG and PG state.
+ */
+int amdgpu_dpm_reapply_umd_profile(struct amdgpu_device *adev)
+{
+ const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+ enum amd_dpm_forced_level level;
+ int ret, fallback_ret;
+
+ if (!pp_funcs || !pp_funcs->reapply_performance_level ||
+ !pp_funcs->force_performance_level)
+ return 0;
+
+ /* Serialize the retained level with the GFX and PMFW reprogramming. */
+ mutex_lock(&adev->pm.mutex);
+ if (adev->pm.dpm.thermal_active) {
+ dev_warn(adev->dev,
+ "Cannot restore UMD profile while thermal control is active\n");
+ ret = 0;
+ goto unlock;
+ }
+
+ if (pp_funcs->get_performance_level)
+ level = pp_funcs->get_performance_level(adev->powerplay.pp_handle);
+ else
+ level = adev->pm.dpm.forced_level;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ break;
+ default:
+ ret = 0;
+ goto unlock;
+ }
+
+ /* The active profile already owns the GFX PG reference. */
+ ret = amdgpu_device_ip_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_GFX,
+ AMD_CG_STATE_UNGATE);
+ if (ret)
+ goto fallback_auto;
+
+ ret = amdgpu_asic_update_umd_stable_pstate(adev, true);
+ if (ret)
+ goto fallback_auto;
+
+ ret = pp_funcs->reapply_performance_level(adev->powerplay.pp_handle,
+ level);
+ if (ret)
+ goto fallback_auto;
+
+unlock:
+ mutex_unlock(&adev->pm.mutex);
+
+ return ret;
+
+fallback_auto:
+ dev_warn(adev->dev, "Failed to restore UMD profile state: %d\n", ret);
+
+ /* Restore normal CG before releasing the profile's PG reference. */
+ amdgpu_asic_update_umd_stable_pstate(adev, false);
+ amdgpu_device_ip_set_clockgating_state(adev, AMD_IP_BLOCK_TYPE_GFX,
+ AMD_CG_STATE_GATE);
+ mutex_unlock(&adev->pm.mutex);
+
+ mutex_lock(&adev->pm.stable_pstate_ctx_lock);
+ fallback_ret = amdgpu_dpm_force_performance_level(adev,
+ AMD_DPM_FORCED_LEVEL_AUTO);
+ if (fallback_ret)
+ dev_warn(adev->dev, "Failed to fall back to AUTO performance level: %d\n",
+ fallback_ret);
+ else
+ adev->pm.stable_pstate_ctx = NULL;
+ mutex_unlock(&adev->pm.stable_pstate_ctx_lock);
+
+ return 0;
+}
+
int amdgpu_dpm_force_performance_level(struct amdgpu_device *adev,
enum amd_dpm_forced_level level)
{
@@ -2124,6 +2209,24 @@ u64 amdgpu_dpm_get_npm_cap(struct amdgpu_device *adev)
return cap;
}
+int amdgpu_dpm_set_npm_limit(struct amdgpu_device *adev, uint32_t limit)
+{
+ const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
+ int ret;
+
+ if (!pp_funcs || !pp_funcs->set_npm_limit)
+ return -EOPNOTSUPP;
+
+ if (amdgpu_sriov_vf(adev))
+ return -EPERM;
+
+ mutex_lock(&adev->pm.mutex);
+ ret = pp_funcs->set_npm_limit(adev->powerplay.pp_handle, limit);
+ mutex_unlock(&adev->pm.mutex);
+
+ return ret;
+}
+
/**
* amdgpu_dpm_get_xcp_metrics - Retrieve metrics for a specific compute
* partition
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index c6e3748bb11b..48c52ac0fc77 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -2211,8 +2211,10 @@ out:
/**
* DOC: cur_node_power_limit
*
- * The amdgpu driver provides a sysfs API for retrieving current node power limit.
- * The file cur_node_power_limit is used for this.
+ * The amdgpu driver provides a sysfs API for reading and writing the current node
+ * power limit in watts. The file cur_node_power_limit is used for this. Writing a
+ * new limit sends SetNpmLimit to PMFW. The file is writable only when the ASIC
+ * reports the write capability bit for this attribute.
*/
static ssize_t amdgpu_show_cur_node_power_limit(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -2231,6 +2233,27 @@ static ssize_t amdgpu_show_cur_node_power_limit(struct device *dev,
return sysfs_emit(buf, "%u\n", nplimit);
}
+static ssize_t amdgpu_store_cur_node_power_limit(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);
+ u32 limit;
+ int r;
+
+ r = amdgpu_pm_get_access_if_active(adev);
+ if (r)
+ return r;
+
+ r = kstrtou32(buf, 0, &limit);
+ if (!r)
+ r = amdgpu_dpm_set_npm_limit(adev, limit);
+
+ amdgpu_pm_put_access(adev);
+ return r ? r : count;
+}
+
/**
* DOC: node_power
*
@@ -2259,7 +2282,8 @@ static ssize_t amdgpu_show_node_power(struct device *dev,
*
* The amdgpu driver provides a sysfs API for retrieving current node power management status.
* The file npm_status is used for this. It shows the status as enabled or disabled based on
- * current node power value. If node power is zero, status is disabled else enabled.
+ * current node power or npm status value. If node power or npm status is zero, status is
+ * disabled else enabled.
*/
static ssize_t amdgpu_show_npm_status(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -2371,7 +2395,8 @@ static ssize_t amdgpu_show_baseboard_power_limit(struct device *dev,
static DEVICE_ATTR(baseboard_temp, 0444, amdgpu_get_baseboard_temp_metrics, NULL);
static DEVICE_ATTR(gpuboard_temp, 0444, amdgpu_get_gpuboard_temp_metrics, NULL);
-static DEVICE_ATTR(cur_node_power_limit, 0444, amdgpu_show_cur_node_power_limit, NULL);
+static DEVICE_ATTR(cur_node_power_limit, 0644, amdgpu_show_cur_node_power_limit,
+ amdgpu_store_cur_node_power_limit);
static DEVICE_ATTR(node_power, 0444, amdgpu_show_node_power, NULL);
static DEVICE_ATTR(global_ppt_resid, 0444, amdgpu_show_global_ppt_resid, NULL);
static DEVICE_ATTR(max_node_power_limit, 0444, amdgpu_show_max_node_power_limit, NULL);
diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index 64d68a905f86..ba8624d28f28 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -484,6 +484,7 @@ void amdgpu_dpm_set_power_state(struct amdgpu_device *adev,
enum amd_dpm_forced_level amdgpu_dpm_get_performance_level(struct amdgpu_device *adev);
int amdgpu_dpm_force_performance_level(struct amdgpu_device *adev,
enum amd_dpm_forced_level level);
+int amdgpu_dpm_reapply_umd_profile(struct amdgpu_device *adev);
int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev,
struct pp_states_info *states);
int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev,
@@ -617,5 +618,6 @@ bool amdgpu_dpm_reset_vcn_is_supported(struct amdgpu_device *adev);
bool amdgpu_dpm_is_temp_metrics_supported(struct amdgpu_device *adev,
enum smu_temp_metric_type type);
u64 amdgpu_dpm_get_npm_cap(struct amdgpu_device *adev);
+int amdgpu_dpm_set_npm_limit(struct amdgpu_device *adev, uint32_t limit);
#endif
diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
index 239c64b8edca..7b0ff5a0993e 100644
--- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
+++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c
@@ -358,24 +358,46 @@ static void pp_dpm_en_umd_pstate(struct pp_hwmgr *hwmgr,
}
}
-static int pp_dpm_force_performance_level(void *handle,
- enum amd_dpm_forced_level level)
+static int pp_dpm_apply_performance_level(struct pp_hwmgr *hwmgr,
+ enum amd_dpm_forced_level level,
+ bool reapply)
{
- struct pp_hwmgr *hwmgr = handle;
+ int ret;
if (!hwmgr || !hwmgr->pm_en)
return -EINVAL;
- if (level == hwmgr->dpm_level)
+ if (!reapply && level == hwmgr->dpm_level)
return 0;
pp_dpm_en_umd_pstate(hwmgr, &level);
hwmgr->request_dpm_level = level;
- hwmgr_handle_task(hwmgr, AMD_PP_TASK_READJUST_POWER_STATE, NULL);
+ ret = hwmgr_handle_task(hwmgr, AMD_PP_TASK_READJUST_POWER_STATE, NULL);
+ if (ret)
+ return ret;
+
+ ret = phm_force_dpm_levels(hwmgr, level);
+ if (ret)
+ return ret;
+
+ hwmgr->dpm_level = level;
return 0;
}
+static int pp_dpm_force_performance_level(void *handle,
+ enum amd_dpm_forced_level level)
+{
+ return pp_dpm_apply_performance_level(handle, level, false);
+}
+
+static int
+pp_dpm_reapply_performance_level(void *handle,
+ enum amd_dpm_forced_level level)
+{
+ return pp_dpm_apply_performance_level(handle, level, true);
+}
+
static enum amd_dpm_forced_level pp_dpm_get_performance_level(
void *handle)
{
@@ -1536,6 +1558,7 @@ static const struct amd_pm_funcs pp_dpm_funcs = {
.load_firmware = pp_dpm_load_fw,
.wait_for_fw_loading_complete = pp_dpm_fw_loading_complete,
.force_performance_level = pp_dpm_force_performance_level,
+ .reapply_performance_level = pp_dpm_reapply_performance_level,
.get_performance_level = pp_dpm_get_performance_level,
.get_current_power_state = pp_dpm_get_current_power_state,
.dispatch_tasks = pp_dpm_dispatch_tasks,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
index a65aa2129845..c9fe323fa624 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c
@@ -2366,6 +2366,13 @@ static int smu_set_powergating_state(struct amdgpu_ip_block *ip_block,
return 0;
}
+static void smu_enter_umd_pstate(struct smu_context *smu)
+{
+ smu_gpo_control(smu, false);
+ smu_gfx_ulv_control(smu, false);
+ smu_deep_sleep_control(smu, false);
+}
+
static int smu_enable_umd_pstate(void *handle,
enum amd_dpm_forced_level *level)
{
@@ -2384,9 +2391,7 @@ static int smu_enable_umd_pstate(void *handle,
/* enter umd pstate, save current level, disable gfx cg*/
if (*level & profile_mode_mask) {
smu_dpm_ctx->saved_dpm_level = smu_dpm_ctx->dpm_level;
- smu_gpo_control(smu, false);
- smu_gfx_ulv_control(smu, false);
- smu_deep_sleep_control(smu, false);
+ smu_enter_umd_pstate(smu);
amdgpu_asic_update_umd_stable_pstate(smu->adev, true);
}
} else {
@@ -2445,7 +2450,8 @@ static void smu_power_profile_mode_put(struct smu_context *smu,
static int smu_adjust_power_state_dynamic(struct smu_context *smu,
enum amd_dpm_forced_level level,
- bool skip_display_settings)
+ bool skip_display_settings,
+ bool force_performance_level)
{
int ret = 0;
struct smu_dpm_context *smu_dpm_ctx = &(smu->smu_dpm);
@@ -2472,7 +2478,7 @@ static int smu_adjust_power_state_dynamic(struct smu_context *smu,
}
}
- if (smu_dpm_ctx->dpm_level != level) {
+ if (force_performance_level || smu_dpm_ctx->dpm_level != level) {
ret = smu_asic_set_performance_level(smu, level);
if (ret) {
if (ret == -EOPNOTSUPP)
@@ -2495,6 +2501,39 @@ static int smu_adjust_power_state_dynamic(struct smu_context *smu,
return ret;
}
+static int
+smu_reapply_power_state_dynamic(struct smu_context *smu,
+ enum amd_dpm_forced_level level)
+{
+ struct smu_dpm_context *smu_dpm_ctx = &smu->smu_dpm;
+ int ret;
+
+ ret = smu_apply_clocks_adjust_rules(smu);
+ if (ret) {
+ dev_err(smu->adev->dev, "Failed to apply clocks adjust rules!");
+ return ret;
+ }
+
+ ret = smu_asic_set_performance_level(smu, level);
+ if (ret) {
+ if (ret == -EOPNOTSUPP)
+ dev_info(smu->adev->dev, "set performance level %d not supported",
+ level);
+ else
+ dev_err(smu->adev->dev, "Failed to set performance level %d",
+ level);
+ return ret;
+ }
+
+ smu_dpm_ctx->dpm_level = level;
+
+ if (smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_MANUAL ||
+ smu_dpm_ctx->dpm_level == AMD_DPM_FORCED_LEVEL_PERF_DETERMINISM)
+ return 0;
+
+ return smu_bump_power_profile_mode(smu, NULL, 0);
+}
+
static int smu_handle_task(struct smu_context *smu,
enum amd_dpm_forced_level level,
enum amd_pp_task task_id)
@@ -2509,13 +2548,13 @@ static int smu_handle_task(struct smu_context *smu,
ret = smu_pre_display_config_changed(smu);
if (ret)
return ret;
- ret = smu_adjust_power_state_dynamic(smu, level, false);
+ ret = smu_adjust_power_state_dynamic(smu, level, false, false);
break;
case AMD_PP_TASK_COMPLETE_INIT:
- ret = smu_adjust_power_state_dynamic(smu, level, true);
+ ret = smu_adjust_power_state_dynamic(smu, level, true, false);
break;
case AMD_PP_TASK_READJUST_POWER_STATE:
- ret = smu_adjust_power_state_dynamic(smu, level, true);
+ ret = smu_adjust_power_state_dynamic(smu, level, true, false);
break;
default:
break;
@@ -2644,6 +2683,37 @@ static int smu_force_performance_level(void *handle,
return ret;
}
+static int
+smu_reapply_performance_level(void *handle,
+ enum amd_dpm_forced_level level)
+{
+ struct smu_context *smu = handle;
+ struct smu_dpm_context *smu_dpm_ctx = &smu->smu_dpm;
+
+ if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
+ return -EOPNOTSUPP;
+
+ if (!smu->is_apu && !smu_dpm_ctx->dpm_context)
+ return -EINVAL;
+
+ switch (level) {
+ case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK:
+ case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (smu_dpm_ctx->dpm_level != level)
+ return -EINVAL;
+
+ smu_enter_umd_pstate(smu);
+
+ return smu_reapply_power_state_dynamic(smu, level);
+}
+
static int smu_set_display_count(void *handle, uint32_t count)
{
struct smu_context *smu = handle;
@@ -3995,6 +4065,19 @@ static u64 smu_get_npm_cap(void *handle)
return smu->ppt_funcs->get_npm_cap(smu);
}
+static int smu_set_npm_limit(void *handle, uint32_t limit)
+{
+ struct smu_context *smu = handle;
+
+ if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled)
+ return -EOPNOTSUPP;
+
+ if (!smu->ppt_funcs || !smu->ppt_funcs->set_npm_limit)
+ return -EOPNOTSUPP;
+
+ return smu->ppt_funcs->set_npm_limit(smu, limit);
+}
+
static ssize_t smu_sys_get_xcp_metrics(void *handle, int xcp_id, void *table)
{
struct smu_context *smu = handle;
@@ -4017,6 +4100,7 @@ static const struct amd_pm_funcs swsmu_pm_funcs = {
.force_clock_level = smu_force_ppclk_levels,
.emit_clock_levels = smu_emit_ppclk_levels,
.force_performance_level = smu_force_performance_level,
+ .reapply_performance_level = smu_reapply_performance_level,
.read_sensor = smu_read_sensor,
.get_apu_thermal_limit = smu_get_apu_thermal_limit,
.set_apu_thermal_limit = smu_set_apu_thermal_limit,
@@ -4069,6 +4153,7 @@ static const struct amd_pm_funcs swsmu_pm_funcs = {
.get_xcp_metrics = smu_sys_get_xcp_metrics,
.get_temp_metrics = smu_sys_get_temp_metrics,
.temp_metrics_is_supported = smu_temp_metrics_is_supported,
+ .set_npm_limit = smu_set_npm_limit,
.get_npm_cap = smu_get_npm_cap,
};
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
index 1e5fc6ff76c8..9d47d2ab1b49 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
@@ -433,6 +433,8 @@ struct smu_table_context {
void *boot_overdrive_table;
void *user_overdrive_table;
+ uint32_t user_od_feature_mask;
+
struct smu_driver_table driver_tables[SMU_DRIVER_TABLE_COUNT];
};
@@ -1626,6 +1628,12 @@ struct pptable_funcs {
* unsupported.
*/
u64 (*get_npm_cap)(struct smu_context *smu);
+
+ /**
+ * @set_npm_limit: Set node power limit in watts.
+ */
+ int (*set_npm_limit)(struct smu_context *smu, uint32_t limit);
+
/**
* @ras_send_msg: Send a message with a parameter from Ras
* &msg: Type of message.
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_pmfw.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_pmfw.h
index a3401c4cc20b..61d76ebfdbd4 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_pmfw.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_pmfw.h
@@ -328,7 +328,7 @@ typedef struct __attribute__((packed, aligned(4))) {
uint64_t GfxclkLowUtilizationAcc[8];
} MetricsTable_t;
-#define SMU_SYSTEM_METRICS_TABLE_VERSION 0x1
+#define SMU_SYSTEM_METRICS_TABLE_VERSION 0x2
#pragma pack(push, 4)
typedef struct {
@@ -343,9 +343,13 @@ typedef struct {
//NPM: NODE POWER MANAGEMENT
uint32_t NodePowerLimit;
uint32_t NodePower;
- uint32_t GlobalPPTResidencyAcc;
+ uint32_t NPMResidencyAcc;
uint16_t SystemPower[SYSTEM_POWER_MAX_ENTRIES]; // UBB Current Power and Power Threshold
+
+ // NPM flags
+ uint32_t NpmSupported;
+ uint32_t NpmEnabled;
} SystemMetricsTable_t;
#pragma pack(pop)
@@ -368,7 +372,7 @@ typedef struct __attribute__((aligned(4))) {
uint8_t FruId[PRODUCT_FRU_ID_LEN];
} FRUProductInfo_t;
-#define SMU_STATIC_METRICS_TABLE_VERSION 0x1
+#define SMU_STATIC_METRICS_TABLE_VERSION 0x2
#pragma pack(push, 4)
typedef struct {
@@ -421,6 +425,8 @@ typedef struct {
uint32_t PPT1Max;
uint32_t PPT1Min;
uint32_t PPT1Default;
+
+ uint32_t MaxNodePowerLimit;
} StaticMetricsTable_t;
#pragma pack(pop)
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_ppsmc.h
index 7ffb445f4c0c..888261c26409 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_ppsmc.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v15_0_8_ppsmc.h
@@ -83,7 +83,10 @@
#define PPSMC_MSG_SetSoftMaxGl2clk 0x32
#define PPSMC_MSG_SetSoftMinFclk 0x33
#define PPSMC_MSG_SetSoftMaxFclk 0x34
-#define PPSMC_Message_Count 0x35
+/* NPM limit setter; NPM reads are served from the SystemMetricsTable. */
+#define PPSMC_MSG_SetNpmLimit 0x40
+#define PPSMC_MSG_PrepareNodeForUnload 0x41
+#define PPSMC_Message_Count 0x42
/* PSMC Reset Types for driver msg argument */
#define PPSMC_RESET_TYPE_DRIVER_MODE_1_RESET 0x1
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
index 2631e12b5e59..31b4ef025351 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h
@@ -305,7 +305,9 @@
__SMU_DUMMY_MAP(SetSoftMaxFclk), \
__SMU_DUMMY_MAP(SetSoftMaxGl2clk), \
__SMU_DUMMY_MAP(SetSoftMinGl2clk), \
- __SMU_DUMMY_MAP(GetSystemMetricsVersion),
+ __SMU_DUMMY_MAP(GetSystemMetricsVersion), \
+ __SMU_DUMMY_MAP(SetNpmLimit), \
+ __SMU_DUMMY_MAP(PrepareNodeForUnload),
#undef __SMU_DUMMY_MAP
#define __SMU_DUMMY_MAP(type) SMU_MSG_##type
diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h
index 0c97bee2c4bb..f443000ba657 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h
@@ -89,6 +89,7 @@ struct smu_15_0_dpm_context {
uint32_t workload_policy_mask;
uint32_t dcef_min_ds_clk;
uint64_t caps;
+ bool node_unload;
uint32_t board_volt;
};
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
index 6beafabf1306..468aaeb79ffb 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
@@ -1569,7 +1569,6 @@ static int smu_v13_0_0_od_edit_dpm_table(struct smu_context *smu,
struct amdgpu_device *adev = smu->adev;
uint32_t offset_of_voltageoffset;
int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
int i, ret = 0;
switch (type) {
@@ -1891,21 +1890,16 @@ static int smu_v13_0_0_od_edit_dpm_table(struct smu_context *smu,
if (ret)
return ret;
} else {
- feature_ctrlmask = od_table->OverDriveTable.FeatureCtrlMask;
memcpy(od_table,
table_context->boot_overdrive_table,
sizeof(OverDriveTableExternal_t));
- od_table->OverDriveTable.FeatureCtrlMask = feature_ctrlmask;
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
+ table_context->user_od_feature_mask = 0;
}
fallthrough;
case PP_OD_COMMIT_DPM_TABLE:
- /*
- * The member below instructs PMFW the settings focused in
- * this single operation.
- * `uint32_t FeatureCtrlMask;`
- * It does not contain actual informations about user's custom
- * settings. Thus we do not cache it.
- */
+ table_context->user_od_feature_mask =
+ od_table->OverDriveTable.FeatureCtrlMask;
offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
if (memcmp((u8 *)od_table + offset_of_voltageoffset,
table_context->user_overdrive_table + offset_of_voltageoffset,
@@ -1918,7 +1912,6 @@ static int smu_v13_0_0_od_edit_dpm_table(struct smu_context *smu,
return ret;
}
- od_table->OverDriveTable.FeatureCtrlMask = 0;
memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
(u8 *)od_table + offset_of_voltageoffset,
sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
@@ -2201,6 +2194,7 @@ static int smu_v13_0_0_set_default_od_settings(struct smu_context *smu)
boot_od_table,
sizeof(OverDriveTableExternal_t));
smu->user_dpm_profile.user_od = false;
+ smu->smu_table.user_od_feature_mask = 0;
} else if (smu->user_dpm_profile.user_od) {
memcpy(&user_od_table_bak,
user_od_table,
@@ -2250,12 +2244,8 @@ static int smu_v13_0_0_restore_user_od_settings(struct smu_context *smu)
OverDriveTableExternal_t *od_table = table_context->overdrive_table;
OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
int res;
-
- user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
- BIT(PP_OD_FEATURE_UCLK_BIT) |
- BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
- BIT(PP_OD_FEATURE_FAN_CURVE_BIT) |
- BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ user_od_table->OverDriveTable.FeatureCtrlMask =
+ table_context->user_od_feature_mask;
res = smu_v13_0_0_upload_overdrive_table(smu, user_od_table);
user_od_table->OverDriveTable.FeatureCtrlMask = 0;
if (res == 0)
@@ -3069,8 +3059,10 @@ static int smu_v13_0_0_set_ppt_limit(struct smu_context *smu,
if (current_od_table.OverDriveTable.Ppt) {
od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
+ table_context->user_od_feature_mask &=
+ ~(1U << PP_OD_FEATURE_PPT_BIT);
+ od_table->OverDriveTable.FeatureCtrlMask =
+ table_context->user_od_feature_mask;
ret = smu_v13_0_0_upload_overdrive_table(smu, od_table);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
@@ -3084,8 +3076,9 @@ static int smu_v13_0_0_set_ppt_limit(struct smu_context *smu,
return ret;
od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
+ table_context->user_od_feature_mask |= 1U << PP_OD_FEATURE_PPT_BIT;
+ od_table->OverDriveTable.FeatureCtrlMask =
+ table_context->user_od_feature_mask;
ret = smu_v13_0_0_upload_overdrive_table(smu, od_table);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
index 5fe409a23772..51e419e6380c 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
@@ -1578,7 +1578,6 @@ static int smu_v13_0_7_od_edit_dpm_table(struct smu_context *smu,
struct amdgpu_device *adev = smu->adev;
uint32_t offset_of_voltageoffset;
int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
int i, ret = 0;
switch (type) {
@@ -1900,22 +1899,17 @@ static int smu_v13_0_7_od_edit_dpm_table(struct smu_context *smu,
if (ret)
return ret;
} else {
- feature_ctrlmask = od_table->OverDriveTable.FeatureCtrlMask;
memcpy(od_table,
table_context->boot_overdrive_table,
sizeof(OverDriveTableExternal_t));
- od_table->OverDriveTable.FeatureCtrlMask = feature_ctrlmask;
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
+ table_context->user_od_feature_mask = 0;
}
fallthrough;
case PP_OD_COMMIT_DPM_TABLE:
- /*
- * The member below instructs PMFW the settings focused in
- * this single operation.
- * `uint32_t FeatureCtrlMask;`
- * It does not contain actual informations about user's custom
- * settings. Thus we do not cache it.
- */
+ table_context->user_od_feature_mask =
+ od_table->OverDriveTable.FeatureCtrlMask;
offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
if (memcmp((u8 *)od_table + offset_of_voltageoffset,
table_context->user_overdrive_table + offset_of_voltageoffset,
@@ -1928,7 +1922,6 @@ static int smu_v13_0_7_od_edit_dpm_table(struct smu_context *smu,
return ret;
}
- od_table->OverDriveTable.FeatureCtrlMask = 0;
memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
(u8 *)od_table + offset_of_voltageoffset,
sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
@@ -2203,6 +2196,7 @@ static int smu_v13_0_7_set_default_od_settings(struct smu_context *smu)
boot_od_table,
sizeof(OverDriveTableExternal_t));
smu->user_dpm_profile.user_od = false;
+ smu->smu_table.user_od_feature_mask = 0;
} else if (smu->user_dpm_profile.user_od) {
memcpy(&user_od_table_bak,
user_od_table,
@@ -2253,11 +2247,8 @@ static int smu_v13_0_7_restore_user_od_settings(struct smu_context *smu)
OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
int res;
- user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
- BIT(PP_OD_FEATURE_UCLK_BIT) |
- BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
- BIT(PP_OD_FEATURE_FAN_CURVE_BIT) |
- BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ user_od_table->OverDriveTable.FeatureCtrlMask =
+ table_context->user_od_feature_mask;
res = smu_v13_0_7_upload_overdrive_table(smu, user_od_table);
user_od_table->OverDriveTable.FeatureCtrlMask = 0;
if (res == 0)
@@ -2689,7 +2680,10 @@ static int smu_v13_0_7_set_ppt_limit(struct smu_context *smu,
if (current_od_table.OverDriveTable.Ppt) {
od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
+ table_context->user_od_feature_mask &=
+ ~(1U << PP_OD_FEATURE_PPT_BIT);
+ od_table->OverDriveTable.FeatureCtrlMask =
+ table_context->user_od_feature_mask;
ret = smu_v13_0_7_upload_overdrive_table(smu, od_table);
if (ret) {
@@ -2704,7 +2698,9 @@ static int smu_v13_0_7_set_ppt_limit(struct smu_context *smu,
return ret;
od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
+ table_context->user_od_feature_mask |= 1U << PP_OD_FEATURE_PPT_BIT;
+ od_table->OverDriveTable.FeatureCtrlMask =
+ table_context->user_od_feature_mask;
ret = smu_v13_0_7_upload_overdrive_table(smu, od_table);
if (ret) {
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
index ed99e61f18e1..549716349117 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
@@ -2360,6 +2360,7 @@ static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu)
boot_od_table,
sizeof(OverDriveTableExternal_t));
smu->user_dpm_profile.user_od = false;
+ smu->smu_table.user_od_feature_mask = 0;
} else if (smu->user_dpm_profile.user_od) {
memcpy(&user_od_table_bak,
user_od_table,
@@ -2406,11 +2407,8 @@ static int smu_v14_0_2_restore_user_od_settings(struct smu_context *smu)
OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
int res;
- user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
- BIT(PP_OD_FEATURE_UCLK_BIT) |
- BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
- BIT(PP_OD_FEATURE_FAN_CURVE_BIT) |
- BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ user_od_table->OverDriveTable.FeatureCtrlMask =
+ table_context->user_od_feature_mask;
res = smu_v14_0_2_upload_overdrive_table(smu, user_od_table);
user_od_table->OverDriveTable.FeatureCtrlMask = 0;
if (res == 0)
@@ -2490,7 +2488,6 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
struct amdgpu_device *adev = smu->adev;
uint32_t offset_of_voltageoffset;
int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
int i, ret = 0;
switch (type) {
@@ -2758,21 +2755,26 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
if (ret)
return ret;
} else {
- feature_ctrlmask = od_table->OverDriveTable.FeatureCtrlMask;
memcpy(od_table,
table_context->boot_overdrive_table,
sizeof(OverDriveTableExternal_t));
- od_table->OverDriveTable.FeatureCtrlMask = feature_ctrlmask;
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
+ table_context->user_od_feature_mask = 0;
}
fallthrough;
case PP_OD_COMMIT_DPM_TABLE:
/*
- * The member below instructs PMFW the settings focused in
- * this single operation.
- * `uint32_t FeatureCtrlMask;`
- * It does not contain actual informations about user's custom
- * settings. Thus we do not cache it.
+ * PMFW interprets FeatureCtrlMask as the complete set of OD
+ * features that are currently overridden, not as a delta
+ * describing this single operation. Any feature whose bit is
+ * clear gets reset to its default on import. od_table's mask
+ * has accumulated every feature the user touched since the
+ * last full restore, so record it and keep restating it on
+ * subsequent uploads.
*/
+ table_context->user_od_feature_mask =
+ od_table->OverDriveTable.FeatureCtrlMask;
+
offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
if (memcmp((u8 *)od_table + offset_of_voltageoffset,
table_context->user_overdrive_table + offset_of_voltageoffset,
@@ -2785,7 +2787,6 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
return ret;
}
- od_table->OverDriveTable.FeatureCtrlMask = 0;
memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
(u8 *)od_table + offset_of_voltageoffset,
sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
@@ -2829,7 +2830,10 @@ static int smu_v14_0_2_set_ppt_limit(struct smu_context *smu,
if (current_od_table.OverDriveTable.Ppt) {
od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
+ table_context->user_od_feature_mask &=
+ ~(1U << PP_OD_FEATURE_PPT_BIT);
+ od_table->OverDriveTable.FeatureCtrlMask =
+ table_context->user_od_feature_mask;
ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
if (ret) {
@@ -2844,7 +2848,9 @@ static int smu_v14_0_2_set_ppt_limit(struct smu_context *smu,
return ret;
od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
+ table_context->user_od_feature_mask |= 1U << PP_OD_FEATURE_PPT_BIT;
+ od_table->OverDriveTable.FeatureCtrlMask =
+ table_context->user_od_feature_mask;
ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
if (ret) {
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c
index 9e42c27f60ee..ce56d540e85e 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c
@@ -25,6 +25,7 @@
#include <linux/firmware.h>
#include "amdgpu.h"
+#include "amdgpu_xgmi.h"
#include "amdgpu_smu.h"
#include "smu_v15_0_8_pmfw.h"
#include "smu15_driver_if_v15_0_8.h"
@@ -137,6 +138,8 @@ static const struct cmn2asic_msg_mapping smu_v15_0_8_message_map[SMU_MSG_MAX_COU
MSG_MAP(SetSoftMaxGl2clk, PPSMC_MSG_SetSoftMaxGl2clk, 0),
MSG_MAP(SetSoftMinFclk, PPSMC_MSG_SetSoftMinFclk, 0),
MSG_MAP(SetSoftMaxFclk, PPSMC_MSG_SetSoftMaxFclk, 0),
+ MSG_MAP(SetNpmLimit, PPSMC_MSG_SetNpmLimit, 0),
+ MSG_MAP(PrepareNodeForUnload, PPSMC_MSG_PrepareNodeForUnload, 0),
};
/* TODO: Update the clk map once enum PPCLK is updated in smu15_driver_if_v15_0_8.h */
@@ -588,19 +591,99 @@ static int smu_v15_0_8_get_system_metrics_table(struct smu_context *smu)
return 0;
}
+static inline void smu_v15_0_8_cap_set(struct smu_context *smu, u32 cap)
+{
+ struct smu_15_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
+
+ dpm_context->caps |= BIT_ULL(cap);
+}
+
+static bool smu_v15_0_8_cap_supported(struct smu_context *smu, u32 cap)
+{
+ struct smu_15_0_dpm_context *dpm_context = smu->smu_dpm.dpm_context;
+
+ return !!(dpm_context->caps & BIT_ULL(cap));
+}
+
+static void smu_v15_0_8_init_npm_cap(struct smu_context *smu)
+{
+ struct smu_table_context *smu_table = &smu->smu_table;
+ struct smu_table *tables = smu_table->tables;
+ SystemMetricsTable_t *metrics;
+ struct smu_table *sys_table;
+ uint32_t table_version;
+ int ret;
+
+ if (smu->adev->gmc.xgmi.physical_node_id != 0)
+ return;
+
+ ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GetSystemMetricsVersion,
+ &table_version);
+ if (ret)
+ return;
+
+ if (table_version < SMU_SYSTEM_METRICS_TABLE_VERSION)
+ return;
+
+ ret = smu_v15_0_8_get_system_metrics_table(smu);
+ if (ret)
+ return;
+
+ sys_table = &tables[SMU_TABLE_PMFW_SYSTEM_METRICS];
+ metrics = (SystemMetricsTable_t *)sys_table->cache.buffer;
+ if (!metrics->NpmSupported)
+ return;
+
+ smu_v15_0_8_cap_set(smu, SMU_15_0_8_CAP_NPM);
+}
+
+static void smu_v15_0_8_init_node_unload_cap(struct smu_context *smu)
+{
+ uint32_t fw_ver = smu->smc_fw_version;
+ uint32_t pgm = (fw_ver >> 24) & 0xFF;
+
+ if (smu_v15_0_8_cap_supported(smu, SMU_15_0_8_CAP_NODE_UNLOAD))
+ return;
+
+ if (pgm == 2 && fw_ver >= SMU_15_0_8_NODE_UNLOAD_MIN_FW_VERSION)
+ smu_v15_0_8_cap_set(smu, SMU_15_0_8_CAP_NODE_UNLOAD);
+}
+
+static void smu_v15_0_8_init_caps(struct smu_context *smu)
+{
+ smu_v15_0_8_init_npm_cap(smu);
+ smu_v15_0_8_init_node_unload_cap(smu);
+}
+
+static u64 smu_v15_0_8_get_npm_cap(struct smu_context *smu)
+{
+ if (!smu_v15_0_8_cap_supported(smu, SMU_15_0_8_CAP_NPM))
+ return 0;
+
+ return AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_CUR_NODE_POWER_LIMIT) |
+ AMDGPU_NPM_CAP_W(AMDGPU_NPM_CAP_CUR_NODE_POWER_LIMIT) |
+ AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_NODE_POWER) |
+ AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_GLOBAL_PPT_RESID) |
+ AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_MAX_NODE_POWER_LIMIT) |
+ AMDGPU_NPM_CAP_R(AMDGPU_NPM_CAP_NPM_STATUS);
+}
+
static int smu_v15_0_8_get_npm_data(struct smu_context *smu,
enum amd_pp_sensors sensor,
uint32_t *value)
{
struct smu_table_context *smu_table = &smu->smu_table;
+ PPTable_t *pptable = (PPTable_t *)smu_table->driver_pptable;
struct smu_table *tables = smu_table->tables;
SystemMetricsTable_t *metrics;
struct smu_table *sys_table;
int ret;
+ if (!smu_v15_0_8_cap_supported(smu, SMU_15_0_8_CAP_NPM))
+ return -EOPNOTSUPP;
+
if (sensor == AMDGPU_PP_SENSOR_MAXNODEPOWERLIMIT) {
- /*TBD as of now put 0 */
- *value = 0;
+ *value = pptable->MaxNodePowerLimit;
return 0;
}
@@ -619,10 +702,10 @@ static int smu_v15_0_8_get_npm_data(struct smu_context *smu,
*value = SMUQ10_ROUND(metrics->NodePower);
break;
case AMDGPU_PP_SENSOR_NPMSTATUS:
- *value = !!SMUQ10_ROUND(metrics->NodePower);
+ *value = !!metrics->NpmEnabled;
break;
case AMDGPU_PP_SENSOR_GPPTRESIDENCY:
- *value = SMUQ10_ROUND(metrics->GlobalPPTResidencyAcc);
+ *value = SMUQ10_ROUND(metrics->NPMResidencyAcc);
break;
default:
return -EINVAL;
@@ -631,6 +714,15 @@ static int smu_v15_0_8_get_npm_data(struct smu_context *smu,
return 0;
}
+static int smu_v15_0_8_set_npm_limit(struct smu_context *smu, uint32_t limit)
+{
+ if (!smu_v15_0_8_cap_supported(smu, SMU_15_0_8_CAP_NPM))
+ return -EOPNOTSUPP;
+
+ return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_SetNpmLimit,
+ limit, NULL);
+}
+
static int smu_v15_0_8_read_sensor(struct smu_context *smu,
enum amd_pp_sensors sensor, void *data,
uint32_t *size)
@@ -1149,6 +1241,10 @@ static int smu_v15_0_8_set_driver_pptable(struct smu_context *smu)
static_metrics->pldmVersion[0];
dpm_context->board_volt = static_metrics->InputTelemetryVoltageInmV;
smu_v15_0_8_init_xgmi_data(smu, static_metrics);
+ smu_v15_0_8_init_caps(smu);
+ if (smu_v15_0_8_cap_supported(smu, SMU_15_0_8_CAP_NPM))
+ pptable->MaxNodePowerLimit =
+ static_metrics->MaxNodePowerLimit;
pptable->init = true;
}
@@ -1285,8 +1381,46 @@ static int smu_v15_0_8_register_irq_handler(struct smu_context *smu)
return ret;
}
+static void smu_v15_0_8_notify_node_unload(struct smu_context *smu)
+{
+ if (!smu_v15_0_8_cap_supported(smu, SMU_15_0_8_CAP_NODE_UNLOAD))
+ return;
+
+ smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_PrepareNodeForUnload, 0, NULL);
+}
+
+static void smu_v15_0_8_mark_node_unload(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ struct amdgpu_hive_info *hive;
+ struct amdgpu_device *peer;
+ struct smu_context *peer_smu;
+ struct smu_15_0_dpm_context *dpm;
+
+ hive = amdgpu_get_xgmi_hive(adev);
+ if (!hive) {
+ dpm = smu->smu_dpm.dpm_context;
+ if (dpm)
+ dpm->node_unload = true;
+ return;
+ }
+
+ list_for_each_entry(peer, &hive->device_list, gmc.xgmi.head) {
+ peer_smu = peer->powerplay.pp_handle;
+ if (!peer_smu)
+ continue;
+ dpm = peer_smu->smu_dpm.dpm_context;
+ if (dpm)
+ dpm->node_unload = true;
+ }
+
+ amdgpu_put_xgmi_hive(hive);
+}
+
static int smu_v15_0_8_notify_unload(struct smu_context *smu)
{
+ struct smu_context *primary_smu;
+ struct smu_15_0_dpm_context *dpm = smu->smu_dpm.dpm_context;
struct smu_msg_ctl *ctl = &smu->msg_ctl;
struct smu_msg_args args = {
.msg = SMU_MSG_PrepareMp1ForUnload,
@@ -1296,6 +1430,13 @@ static int smu_v15_0_8_notify_unload(struct smu_context *smu)
if (amdgpu_in_reset(smu->adev))
return 0;
+ if (dpm && !dpm->node_unload) {
+ primary_smu = smu_get_primary_dev(smu);
+ if (primary_smu)
+ smu_v15_0_8_notify_node_unload(primary_smu);
+ smu_v15_0_8_mark_node_unload(smu);
+ }
+
dev_dbg(smu->adev->dev, "Notify PMFW about driver unload");
/* Ignore return, just intimate FW that driver is not going to be there */
ctl->ops->send_msg(ctl, &args);
@@ -2382,6 +2523,8 @@ static const struct pptable_funcs smu_v15_0_8_ppt_funcs = {
.get_unique_id = smu_v15_0_8_get_unique_id,
.get_ppt_limit = smu_v15_0_8_get_ppt_limit,
.set_ppt_limit = smu_v15_0_8_set_ppt_limit,
+ .get_npm_cap = smu_v15_0_8_get_npm_cap,
+ .set_npm_limit = smu_v15_0_8_set_npm_limit,
.emit_clk_levels = smu_v15_0_8_emit_clk_levels,
.read_sensor = smu_v15_0_8_read_sensor,
.populate_umd_state_clk = smu_v15_0_8_populate_umd_state_clk,
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.h b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.h
index 46fce0a79c21..dff601bc0f9a 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.h
@@ -32,6 +32,9 @@
#define SMU_15_0_8_MAX_AID 2
#define SMU_15_0_8_MAX_MID 2
#define SMU_15_0_8_MAX_HBM_STACKS 12
+#define SMU_15_0_8_CAP_NPM 1
+#define SMU_15_0_8_CAP_NODE_UNLOAD 2
+#define SMU_15_0_8_NODE_UNLOAD_MIN_FW_VERSION 0x027d0c00u /* 2.125.12.0 */
extern void smu_v15_0_8_set_ppt_funcs(struct smu_context *smu);
typedef struct {
@@ -61,6 +64,7 @@ typedef struct {
uint32_t PPT1Max;
uint32_t PPT1Min;
uint32_t PPT1Default;
+ uint32_t MaxNodePowerLimit;
bool init;
} PPTable_t;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
index 2bd3ea17e789..ff59bf19c3e7 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c
@@ -27,6 +27,32 @@
#include "smu_cmn.h"
#include "soc15_common.h"
+#include "amdgpu_xgmi.h"
+
+struct smu_context *smu_get_primary_dev(struct smu_context *smu)
+{
+ struct amdgpu_device *adev = smu->adev;
+ struct amdgpu_hive_info *hive;
+ struct amdgpu_device *peer;
+
+ if (adev->gmc.xgmi.physical_node_id == 0)
+ return smu;
+
+ hive = amdgpu_get_xgmi_hive(adev);
+ if (!hive)
+ return NULL;
+
+ list_for_each_entry(peer, &hive->device_list, gmc.xgmi.head) {
+ if (peer->gmc.xgmi.physical_node_id == 0) {
+ amdgpu_put_xgmi_hive(hive);
+ return peer->powerplay.pp_handle;
+ }
+ }
+
+ amdgpu_put_xgmi_hive(hive);
+ return NULL;
+}
+
/*
* DO NOT use these for err/warn/info/debug messages.
* Use dev_err, dev_warn, dev_info and dev_dbg instead.
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h
index ae6742f5298f..6d7f47c934f2 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h
@@ -249,6 +249,8 @@ int smu_cmn_dpm_pcie_gen_idx(int gen);
int smu_cmn_dpm_pcie_width_idx(int width);
int smu_cmn_check_fw_version(struct smu_context *smu);
+struct smu_context *smu_get_primary_dev(struct smu_context *smu);
+
int smu_cmn_get_pptable_from_firmware(struct smu_context *smu, void **table,
uint32_t *size, uint32_t pptable_id);