summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-30 13:48:47 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-30 13:48:47 -1000
commitb92f6e90cf056ec80bafb858d5749829a55ac75e (patch)
tree8323bbba29b8ea88affcf1be7a3c67f91f0542ad /drivers/gpu
parent51b4bf5fe7c90ebf08599b9a3c7620b4a3fd4b22 (diff)
parentf94ed3d7cba3c83eef50b1a96d630d63cf54f69c (diff)
Merge tag 'drm-next-2026-10-31' of https://gitlab.freedesktop.org/drm/kernel
Pull drm fixes from Dave Airlie: "The final set of merge window fixes, from AMD mostly. with one set of panthor mmu fixes. amdgpu: - UALink fixes - Runtime PM fixes - GPUVM fixes - Mac fixes - HDMI fixes - DP MST fix - Userptr fix - SMU fixes - DSC fix - VCE/UVD sched teardown fix - Display runpm reference fix - Display TD-LUT fix - SMU 13.x fixes - SMU 14.x fixes - Gang submit fix - Passive DVI sink fix - USB4 tunnel fix - Freesync fix amdkfd: - SVM fixes - Fix doorbell allocation race - GC 12.1 fix - GPU reset fix - Buffer accounting fixes - Bounds checking fixes - Hotplug fixes - SVM fixes - CRIU fixes - GFX12 fix panthor: - Fix bugs in MMU handling" * tag 'drm-next-2026-10-31' of https://gitlab.freedesktop.org/drm/kernel: (52 commits) drm/amd/display: refresh cached HDMI FreeSync state drm/amd/display: Cap DSC bpp for USB4 DP tunnels drm/amd/display: Poll passive DVI sinks for EDID readiness drm/amdgpu: re-enable GFXOFF for compute on GFX12 drm/amdkfd: evict the queues only once for CRIU PROCESS_INFO drm/amdgpu: drop the gang leader reference when freeing gang jobs drm/amdkfd: send SMI process and VM fault events only to allowed clients drm/amdkfd: validate SRAT subtables before parsing drm/amd/pm: preserve committed OD features for SMU 14.0.2 drm/amd/pm: add zero fan stop temperature support on smu 14.0.2 drm/amd/pm: preserve committed OD features for SMU 13.0.0/13.0.7 drm/amd/display: Initialize missing TD-LUT programming inputs drm/amdkfd: split vram_pages of SVM ranges prefetched without a GPU mapping drm/amdgpu: reject CS chunks too large for kvmalloc() drm/amd/display: take a runtime PM reference only when a CRTC turns on drm/amdgpu: guard VCE/UVD sched entity teardown on failed probe drm/amdkfd: stop restore retries on device unplug drm/amdkfd: tear down KFD processes before early fini_sw on disconnect drm/amdkfd: Validate the control stack of a restored queue drm/amdkfd: correct vram_available calculation ...
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.c5
-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.c105
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c17
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c14
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c24
-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_uvd.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c3
-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.c48
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_crat.c35
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device.c9
-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_mqd_manager.h9
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c2
-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_process_queue_manager.c30
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_queue.c23
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c60
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.c36
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c15
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c9
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c24
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c17
-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/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.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.c200
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c199
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c226
-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
-rw-r--r--drivers/gpu/drm/panthor/panthor_mmu.c45
68 files changed, 1604 insertions, 465 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
index 6ff3d80ff468..78a6f743137e 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h
@@ -845,8 +845,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.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
index 3391e8c77f13..53dde87f97f5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
@@ -768,9 +768,8 @@ err:
void amdgpu_amdkfd_set_compute_idle(struct amdgpu_device *adev, bool idle)
{
enum amd_powergating_state state = idle ? AMD_PG_STATE_GATE : AMD_PG_STATE_UNGATE;
- if ((IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 11 &&
- ((adev->mes.kiq_version & AMDGPU_MES_VERSION_MASK) <= 64)) ||
- (IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 12)) {
+ if (IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 11 &&
+ ((adev->mes.kiq_version & AMDGPU_MES_VERSION_MASK) <= 64)) {
pr_debug("GFXOFF is %s\n", idle ? "enabled" : "disabled");
amdgpu_gfx_off_ctrl(adev, idle);
} else if ((IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 9) &&
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..3a4b3424b5b2 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,
@@ -2827,6 +2854,7 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work)
struct task_struct *usertask;
struct mm_struct *mm;
uint32_t evicted_bos;
+ int ret;
mutex_lock(&process_info->notifier_lock);
evicted_bos = process_info->evicted_bos;
@@ -2846,14 +2874,16 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work)
mutex_lock(&process_info->lock);
- if (update_invalid_user_pages(process_info, mm))
+ ret = update_invalid_user_pages(process_info, mm);
+ if (ret)
goto unlock_out;
/* userptr_inval_list can be empty if all evicted userptr BOs
* have been freed. In that case there is nothing to validate
* and we can just restart the queues.
*/
if (!list_empty(&process_info->userptr_inval_list)) {
- if (validate_invalid_user_pages(process_info))
+ ret = validate_invalid_user_pages(process_info);
+ if (ret)
goto unlock_out;
}
/* Final check for concurrent evicton and atomic update. If
@@ -2862,11 +2892,14 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work)
* reference counting inside KFD will handle this case.
*/
mutex_lock(&process_info->notifier_lock);
- if (process_info->evicted_bos != evicted_bos)
+ if (process_info->evicted_bos != evicted_bos) {
+ ret = -EAGAIN;
goto unlock_notifier_out;
+ }
if (confirm_valid_user_pages_locked(process_info)) {
WARN(1, "User pages unexpectedly invalid");
+ ret = -EINVAL;
goto unlock_notifier_out;
}
@@ -2884,8 +2917,10 @@ unlock_notifier_out:
unlock_out:
mutex_unlock(&process_info->lock);
- /* If validation failed, reschedule another attempt */
- if (evicted_bos) {
+ /* If validation failed, reschedule another attempt, unless the
+ * device is gone for good
+ */
+ if (evicted_bos && ret != -ENODEV) {
queue_delayed_work(system_freezable_wq,
&process_info->restore_userptr_work,
msecs_to_jiffies(AMDGPU_USERPTR_RESTORE_DELAY_MS));
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 5558e7330cf4..d69018a14d96 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -218,6 +218,12 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p,
size = p->chunks[i].length_dw;
+ if (size > INT_MAX / sizeof(uint32_t)) {
+ ret = -EINVAL;
+ i--;
+ goto free_partial_kdata;
+ }
+
p->chunks[i].kdata = vmemdup_array_user(u64_to_user_ptr(user_chunk.chunk_data),
size,
sizeof(uint32_t));
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index bcaa59da2369..53f05ee7193f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -594,10 +594,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;
}
/**
@@ -2784,6 +2782,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();
@@ -4360,8 +4364,11 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev)
* before ip_fini_early to prevent kfd locking refcount issues by calling
* amdgpu_amdkfd_suspend()
*/
- if (pci_dev_is_disconnected(adev->pdev))
+ if (pci_dev_is_disconnected(adev->pdev)) {
+ /* Drain KFD processes while kfd.dev is still valid */
+ amdgpu_amdkfd_teardown_processes(adev);
amdgpu_amdkfd_device_fini_sw(adev);
+ }
amdgpu_coredump_fini(adev);
amdgpu_device_ip_fini_early(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
index cb7bf7b6d4c4..731fe8f9e922 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
@@ -2986,19 +2986,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);
@@ -3044,7 +3031,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_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index 9cc210198fa4..8689a3c933b2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -313,22 +313,7 @@ void amdgpu_job_free_resources(struct amdgpu_job *job)
static void amdgpu_job_free_cb(struct drm_sched_job *s_job)
{
- struct amdgpu_job *job = to_amdgpu_job(s_job);
-
- drm_sched_job_cleanup(s_job);
-
- amdgpu_sync_free(&job->explicit_sync);
-
- if (dma_fence_was_initialized(&job->hw_fence->base))
- dma_fence_put(&job->hw_fence->base);
- else
- kfree(job->hw_fence);
- if (dma_fence_was_initialized(&job->hw_vm_fence->base))
- dma_fence_put(&job->hw_vm_fence->base);
- else
- kfree(job->hw_vm_fence);
-
- kfree(job);
+ amdgpu_job_free(to_amdgpu_job(s_job));
}
void amdgpu_job_set_gang_leader(struct amdgpu_job *job,
@@ -349,13 +334,14 @@ void amdgpu_job_set_gang_leader(struct amdgpu_job *job,
void amdgpu_job_free(struct amdgpu_job *job)
{
- if (job->base.entity)
+ if (job->gang_submit != &job->base.s_fence->scheduled)
+ dma_fence_put(job->gang_submit);
+
+ if (job->base.s_fence)
drm_sched_job_cleanup(&job->base);
amdgpu_job_free_resources(job);
amdgpu_sync_free(&job->explicit_sync);
- if (job->gang_submit != &job->base.s_fence->scheduled)
- dma_fence_put(job->gang_submit);
if (dma_fence_was_initialized(&job->hw_fence->base))
dma_fence_put(&job->hw_fence->base);
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 8f0720a77520..0f86777250fc 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_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
index ecd7caa95d4b..cd35ff7ca864 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
@@ -377,7 +377,8 @@ int amdgpu_uvd_sw_fini(struct amdgpu_device *adev)
void *addr = amdgpu_bo_kptr(adev->uvd.ib_bo);
int i, j;
- drm_sched_entity_destroy(&adev->uvd.entity);
+ if (adev->uvd.entity.rq)
+ drm_sched_entity_destroy(&adev->uvd.entity);
for (j = 0; j < adev->uvd.num_uvd_inst; ++j) {
if (adev->uvd.harvest_config & (1 << j))
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
index 1f7a98cdce5a..3952af9ae115 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
@@ -253,7 +253,8 @@ int amdgpu_vce_sw_fini(struct amdgpu_device *adev)
if (adev->vce.vcpu_bo == NULL)
return 0;
- drm_sched_entity_destroy(&adev->vce.entity);
+ if (adev->vce.entity.rq)
+ drm_sched_entity_destroy(&adev->vce.entity);
for (i = 0; i < adev->vce.num_rings; i++)
amdgpu_ring_fini(&adev->vce.ring[i]);
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..d65ae53c81a5 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;
}
/*
@@ -2869,6 +2876,7 @@ static int criu_process_info(struct file *filep,
struct kfd_process *p,
struct kfd_ioctl_criu_args *args)
{
+ bool paused_here = false;
int ret = 0;
mutex_lock(&p->mutex);
@@ -2879,11 +2887,15 @@ static int criu_process_info(struct file *filep,
goto err_unlock;
}
- ret = kfd_process_evict_queues(p, KFD_QUEUE_EVICTION_CRIU_CHECKPOINT);
- if (ret)
- goto err_unlock;
+ /* UNPAUSE restores the queues once, so evict them only once */
+ if (!p->queues_paused) {
+ ret = kfd_process_evict_queues(p, KFD_QUEUE_EVICTION_CRIU_CHECKPOINT);
+ if (ret)
+ goto err_unlock;
- p->queues_paused = true;
+ p->queues_paused = true;
+ paused_here = true;
+ }
args->pid = task_pid_nr_ns(p->lead_thread,
task_active_pid_ns(p->lead_thread));
@@ -2898,7 +2910,7 @@ static int criu_process_info(struct file *filep,
args->priv_data_size);
err_unlock:
- if (ret) {
+ if (ret && paused_here) {
kfd_process_restore_queues(p);
p->queues_paused = false;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
index 6e0df685503d..32d7573dffc9 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
@@ -2056,27 +2056,33 @@ static void kfd_find_numa_node_in_srat(struct kfd_node *kdev)
return;
}
+ if (table_header->length < sizeof(struct acpi_table_srat))
+ goto invalid_srat;
+
table_end = (unsigned long)table_header + table_header->length;
/* Parse all entries looking for a match. */
sub_header = (struct acpi_subtable_header *)
((unsigned long)table_header +
sizeof(struct acpi_table_srat));
- subtable_len = sub_header->length;
- while (((unsigned long)sub_header) + subtable_len < table_end) {
- /*
- * If length is 0, break from this loop to avoid
- * infinite loop.
- */
- if (subtable_len == 0) {
- pr_err("SRAT invalid zero length\n");
- break;
- }
+ while ((unsigned long)sub_header < table_end) {
+ unsigned long start = (unsigned long)sub_header;
+
+ if (table_end - start < sizeof(*sub_header))
+ goto invalid_srat;
+
+ subtable_len = sub_header->length;
+ if (subtable_len < sizeof(*sub_header) ||
+ subtable_len > table_end - start)
+ goto invalid_srat;
switch (sub_header->type) {
case ACPI_SRAT_TYPE_CPU_AFFINITY:
cpu = (struct acpi_srat_cpu_affinity *)sub_header;
+ if (subtable_len < sizeof(*cpu))
+ goto invalid_srat;
+
pxm = *((u32 *)cpu->proximity_domain_hi) << 8 |
cpu->proximity_domain_lo;
if (pxm > max_pxm)
@@ -2084,6 +2090,9 @@ static void kfd_find_numa_node_in_srat(struct kfd_node *kdev)
break;
case ACPI_SRAT_TYPE_GENERIC_AFFINITY:
gpu = (struct acpi_srat_generic_affinity *)sub_header;
+ if (subtable_len < sizeof(*gpu))
+ goto invalid_srat;
+
bdf = *((u16 *)(&gpu->device_handle[0])) << 16 |
*((u16 *)(&gpu->device_handle[2]));
if (bdf == pci_id) {
@@ -2100,9 +2109,13 @@ static void kfd_find_numa_node_in_srat(struct kfd_node *kdev)
sub_header = (struct acpi_subtable_header *)
((unsigned long)sub_header + subtable_len);
- subtable_len = sub_header->length;
}
+ goto out;
+
+invalid_srat:
+ pr_err("SRAT contains an invalid subtable\n");
+out:
acpi_put_table(table_header);
/* Workaround bad cpu-gpu binding case */
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
index bb4c6695f3cf..0f96c9e3fa4d 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
@@ -1814,7 +1814,14 @@ void kgd2kfd_teardown_processes(struct amdgpu_device *adev)
mutex_unlock(&kfd_processes_mutex);
- /* wait all kfd processes use adev terminate */
+ /* kfd.dev may already be freed on surprise removal */
+ if (!adev->kfd.dev)
+ return;
+
+ /*
+ * Wait for all KFD processes using adev to terminate. Must be
+ * unbounded: device state is freed once they are gone.
+ */
while (!!atomic_read(&adev->kfd.dev->kfd_processes_count))
cond_resched();
}
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_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
index 38b46b696243..d63f7e9fae30 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
@@ -138,6 +138,15 @@ struct mqd_manager {
uint32_t ctl_stack_size;
};
+/* Keep offset <= size <= wave state offset, as init_mqd() and CP do */
+static inline void kfd_mqd_restore_ctl_stack(u32 *size, u32 *offset, u32 *wg_offset,
+ u32 ctl_stack_size)
+{
+ *size = ctl_stack_size;
+ *offset = min(*offset, ctl_stack_size);
+ *wg_offset = max(*wg_offset, ctl_stack_size);
+}
+
struct mqd_user_context_save_area_header {
/* Byte offset from start of user context
* save area to the last saved top (lowest
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c
index 4f8a8a1a6186..6c9bafa3962e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c
@@ -305,6 +305,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
addr = mqd_mem_obj->gpu_addr;
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
*mqd = m;
if (gart_addr)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c
index bf015dc5b868..5709949de2eb 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c
@@ -340,6 +340,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
addr = mqd_mem_obj->gpu_addr;
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
*mqd = m;
if (gart_addr)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c
index 6ea09b031caf..aea80266d254 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c
@@ -393,6 +393,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size));
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
/* Update MQD base address to the newly allocated location */
m->cp_mqd_base_addr_lo = lower_32_bits(addr);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c
index d508ac5a18f8..33f0ecbe6b0a 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c
@@ -690,6 +690,8 @@ static void restore_mqd_v12_1(struct mqd_manager *mm, void **mqd,
memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size) *
NUM_XCC(mm->dev->xcc_mask));
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
/* Update MQD base address to the newly allocated location */
m->cp_mqd_base_addr_lo = lower_32_bits(addr);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
index 6e6bc1ec0b64..fca43017ee7e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
@@ -462,6 +462,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
addr = mqd_mem_obj->gpu_addr;
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
*mqd = m;
if (gart_addr)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c
index 029572548c14..5ead0d5613b6 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c
@@ -317,6 +317,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
addr = mqd_mem_obj->gpu_addr;
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
*mqd = m;
if (gart_addr)
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_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index a15a826519ad..78ab759ad854 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -26,6 +26,7 @@
#include <linux/list.h>
#include "kfd_device_queue_manager.h"
#include "kfd_priv.h"
+#include "kfd_topology.h"
#include "kfd_kernel_queue.h"
#include "amdgpu_amdkfd.h"
#include "amdgpu_reset.h"
@@ -983,7 +984,7 @@ int kfd_criu_checkpoint_queues(struct kfd_process *p,
}
static void set_queue_properties_from_criu(struct queue_properties *qp,
- struct kfd_criu_queue_priv_data *q_data, uint32_t num_xcc)
+ struct kfd_criu_queue_priv_data *q_data)
{
qp->is_interop = false;
qp->queue_percent = q_data->q_percent;
@@ -996,11 +997,6 @@ static void set_queue_properties_from_criu(struct queue_properties *qp,
qp->eop_ring_buffer_size = q_data->eop_ring_buffer_size;
qp->ctx_save_restore_area_address = q_data->ctx_save_restore_area_address;
qp->ctx_save_restore_area_size = q_data->ctx_save_restore_area_size;
- if (q_data->type == KFD_QUEUE_TYPE_COMPUTE)
- qp->ctl_stack_size = q_data->ctl_stack_size / num_xcc;
- else
- qp->ctl_stack_size = q_data->ctl_stack_size;
-
qp->type = q_data->type;
qp->format = q_data->format;
}
@@ -1012,6 +1008,7 @@ int kfd_criu_restore_queue(struct kfd_process *p,
{
uint8_t *mqd, *ctl_stack, *q_extra_data = NULL;
struct kfd_criu_queue_priv_data *q_data;
+ struct kfd_topology_device *topo_dev;
struct kfd_process_device *pdd;
uint64_t q_extra_data_size;
struct queue_properties qp;
@@ -1048,6 +1045,24 @@ int kfd_criu_restore_queue(struct kfd_process *p,
goto exit;
}
+ memset(&qp, 0, sizeof(qp));
+ set_queue_properties_from_criu(&qp, q_data);
+
+ /* Per-XCC control stack size from topology, as create-queue requires */
+ if (qp.type == KFD_QUEUE_TYPE_COMPUTE && pdd->dev->kfd->cwsr_enabled) {
+ topo_dev = kfd_topology_device_by_id(pdd->dev->id);
+ if (!topo_dev) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ qp.ctl_stack_size = topo_dev->node_props.ctl_stack_size;
+ }
+
+ if (q_data->ctl_stack_size > (u64)qp.ctl_stack_size * NUM_XCC(pdd->dev->xcc_mask)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
*priv_data_offset += sizeof(*q_data);
q_extra_data_size = (uint64_t)q_data->ctl_stack_size + q_data->mqd_size;
@@ -1077,9 +1092,6 @@ int kfd_criu_restore_queue(struct kfd_process *p,
mqd = q_extra_data;
ctl_stack = mqd + q_data->mqd_size;
- memset(&qp, 0, sizeof(qp));
- set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->xcc_mask));
-
print_queue_properties(&qp);
ret = pqm_create_queue(&p->pqm, pdd->dev, &qp, &queue_id, q_data, mqd, ctl_stack, NULL);
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_smi_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
index 6a7b4d959541..c87370a9f5f0 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
@@ -40,7 +40,7 @@ struct kfd_smi_client {
struct kfd_node *dev;
spinlock_t lock;
struct rcu_head rcu;
- pid_t pid;
+ pid_t tgid;
bool suser;
};
@@ -160,18 +160,18 @@ static int kfd_smi_ev_release(struct inode *inode, struct file *filep)
return 0;
}
-static bool kfd_smi_ev_enabled(pid_t pid, struct kfd_smi_client *client,
+static bool kfd_smi_ev_enabled(pid_t tgid, struct kfd_smi_client *client,
unsigned int event)
{
uint64_t events = READ_ONCE(client->events);
- if (pid && client->pid != pid && !client->suser)
+ if (tgid && client->tgid != tgid && !client->suser)
return false;
return events & KFD_SMI_EVENT_MASK_FROM_INDEX(event);
}
-static void add_event_to_kfifo(pid_t pid, struct kfd_node *dev,
+static void add_event_to_kfifo(pid_t tgid, struct kfd_node *dev,
unsigned int smi_event, char *event_msg, int len)
{
struct kfd_smi_client *client;
@@ -179,7 +179,7 @@ static void add_event_to_kfifo(pid_t pid, struct kfd_node *dev,
rcu_read_lock();
list_for_each_entry_rcu(client, &dev->smi_clients, list) {
- if (!kfd_smi_ev_enabled(pid, client, smi_event))
+ if (!kfd_smi_ev_enabled(tgid, client, smi_event))
continue;
spin_lock(&client->lock);
if (kfifo_avail(&client->fifo) >= len) {
@@ -210,27 +210,33 @@ static inline pid_t kfd_smi_task_to_pid(struct task_struct *task)
return task ? task_tgid_nr_ns(task, task_active_pid_ns(task)) : 0;
}
+/*
+ * Clients are matched by global TGID, which is unique across PID namespaces
+ * and stays valid after the task exited. Events show kfd_smi_task_to_pid().
+ */
+static inline pid_t kfd_smi_task_tgid(struct task_struct *task)
+{
+ return task ? task_tgid_nr(task) : 0;
+}
+
__printf(4, 5)
-static void kfd_smi_event_add(struct task_struct *task, struct kfd_node *dev,
+static void kfd_smi_event_add(pid_t tgid, struct kfd_node *dev,
unsigned int event, char *fmt, ...)
{
char fifo_in[KFD_SMI_EVENT_MSG_SIZE];
int len;
va_list args;
- pid_t pid;
if (list_empty(&dev->smi_clients))
return;
- pid = kfd_smi_task_to_pid(task);
-
len = scnprintf(fifo_in, sizeof(fifo_in), "%x ", event);
va_start(args, fmt);
len += vscnprintf(fifo_in + len, sizeof(fifo_in) - len, fmt, args);
va_end(args);
- add_event_to_kfifo(pid, dev, event, fifo_in, len);
+ add_event_to_kfifo(tgid, dev, event, fifo_in, len);
}
void kfd_smi_event_update_gpu_reset(struct kfd_node *dev, bool post_reset,
@@ -252,14 +258,14 @@ void kfd_smi_event_update_gpu_reset(struct kfd_node *dev, bool post_reset,
amdgpu_reset_get_desc(reset_context, reset_cause,
sizeof(reset_cause));
- kfd_smi_event_add(NULL, dev, event, KFD_EVENT_FMT_UPDATE_GPU_RESET(
+ kfd_smi_event_add(0, dev, event, KFD_EVENT_FMT_UPDATE_GPU_RESET(
dev->reset_seq_num, reset_cause));
}
void kfd_smi_event_update_thermal_throttling(struct kfd_node *dev,
uint64_t throttle_bitmask)
{
- kfd_smi_event_add(NULL, dev, KFD_SMI_EVENT_THERMAL_THROTTLE,
+ kfd_smi_event_add(0, dev, KFD_SMI_EVENT_THERMAL_THROTTLE,
KFD_EVENT_FMT_THERMAL_THROTTLING(
throttle_bitmask,
amdgpu_dpm_get_thermal_throttling_counter(dev->adev)));
@@ -271,10 +277,14 @@ void kfd_smi_event_update_vmfault(struct kfd_node *dev, uint16_t pasid)
task_info = amdgpu_vm_get_task_info_pasid(dev->adev, pasid);
if (task_info) {
- /* Report VM faults from user applications, not retry from kernel */
+ /*
+ * Report VM faults from user applications, not retry from
+ * kernel, and only to clients allowed to see that process.
+ */
if (task_info->task.pid)
- kfd_smi_event_add(NULL, dev, KFD_SMI_EVENT_VMFAULT, KFD_EVENT_FMT_VMFAULT(
- task_info->task.pid, task_info->task.comm));
+ kfd_smi_event_add(task_info->tgid, dev, KFD_SMI_EVENT_VMFAULT,
+ KFD_EVENT_FMT_VMFAULT(task_info->task.pid,
+ task_info->task.comm));
amdgpu_vm_put_task_info(task_info);
}
}
@@ -283,7 +293,7 @@ void kfd_smi_event_page_fault_start(struct kfd_node *node, struct task_struct *t
unsigned long address, bool write_fault,
ktime_t ts)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_PAGE_FAULT_START,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_PAGE_FAULT_START,
KFD_EVENT_FMT_PAGEFAULT_START(ktime_to_ns(ts),
kfd_smi_task_to_pid(task), address, node->id,
write_fault ? 'W' : 'R'));
@@ -292,7 +302,7 @@ void kfd_smi_event_page_fault_start(struct kfd_node *node, struct task_struct *t
void kfd_smi_event_page_fault_end(struct kfd_node *node, struct task_struct *task,
unsigned long address, bool migration)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_PAGE_FAULT_END,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_PAGE_FAULT_END,
KFD_EVENT_FMT_PAGEFAULT_END(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), address, node->id,
migration ? 'M' : 'U'));
@@ -304,7 +314,7 @@ void kfd_smi_event_migration_start(struct kfd_node *node, struct task_struct *ta
uint32_t prefetch_loc, uint32_t preferred_loc,
uint32_t trigger)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_MIGRATE_START,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_MIGRATE_START,
KFD_EVENT_FMT_MIGRATE_START(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), start, end - start, from,
to, prefetch_loc, preferred_loc, trigger));
@@ -315,7 +325,7 @@ void kfd_smi_event_migration_end(struct kfd_node *node, struct task_struct *task
uint32_t from, uint32_t to, uint32_t trigger,
int error_code)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_MIGRATE_END,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_MIGRATE_END,
KFD_EVENT_FMT_MIGRATE_END(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), start, end - start, from,
to, trigger, error_code));
@@ -324,14 +334,14 @@ void kfd_smi_event_migration_end(struct kfd_node *node, struct task_struct *task
void kfd_smi_event_queue_eviction(struct kfd_node *node, struct task_struct *task,
uint32_t trigger)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_QUEUE_EVICTION,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_QUEUE_EVICTION,
KFD_EVENT_FMT_QUEUE_EVICTION(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), node->id, trigger));
}
void kfd_smi_event_queue_restore(struct kfd_node *node, struct task_struct *task)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_QUEUE_RESTORE,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_QUEUE_RESTORE,
KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), node->id, '0'));
}
@@ -348,7 +358,7 @@ void kfd_smi_event_queue_restore_rescheduled(struct mm_struct *mm)
for (i = 0; i < p->n_pdds; i++) {
struct kfd_process_device *pdd = p->pdds[i];
- kfd_smi_event_add(p->lead_thread, pdd->dev,
+ kfd_smi_event_add(kfd_smi_task_tgid(p->lead_thread), pdd->dev,
KFD_SMI_EVENT_QUEUE_RESTORE,
KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(p->lead_thread),
@@ -361,7 +371,7 @@ void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, struct task_struct *tas
unsigned long address, unsigned long last,
uint32_t trigger)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_UNMAP_FROM_GPU,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_UNMAP_FROM_GPU,
KFD_EVENT_FMT_UNMAP_FROM_GPU(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), address,
last - address + 1, node->id, trigger));
@@ -379,7 +389,7 @@ void kfd_smi_event_process(struct kfd_process_device *pdd, bool start)
task_info = amdgpu_vm_get_task_info_vm(avm);
if (task_info) {
- kfd_smi_event_add(NULL, pdd->dev,
+ kfd_smi_event_add(kfd_smi_task_tgid(pdd->process->lead_thread), pdd->dev,
start ? KFD_SMI_EVENT_PROCESS_START :
KFD_SMI_EVENT_PROCESS_END,
KFD_EVENT_FMT_PROCESS(task_info->task.pid,
@@ -408,7 +418,7 @@ int kfd_smi_event_open(struct kfd_node *dev, uint32_t *fd)
spin_lock_init(&client->lock);
client->events = 0;
client->dev = dev;
- client->pid = kfd_smi_task_to_pid(current);
+ client->tgid = kfd_smi_task_tgid(current);
client->suser = capable(CAP_SYS_ADMIN);
spin_lock(&dev->smi_lock);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index cfb428c72686..ed5e3318877b 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -1020,9 +1020,12 @@ svm_range_split_pages(struct svm_range *new, struct svm_range *old,
uint64_t start, uint64_t last)
{
uint64_t npages = last - start + 1;
+ bool has_dma_addr = false;
int i, r;
for (i = 0; i < MAX_GPU_INSTANCE; i++) {
+ if (old->dma_addr[i])
+ has_dma_addr = true;
r = svm_range_split_array(&new->dma_addr[i], &old->dma_addr[i],
sizeof(*old->dma_addr[i]), old->start,
npages, new->start, new->npages,
@@ -1030,8 +1033,16 @@ svm_range_split_pages(struct svm_range *new, struct svm_range *old,
if (r)
return r;
}
- if (old->actual_loc)
+ if (old->actual_loc) {
+ /*
+ * Pages migrated by prefetch have no dma_addr until they are
+ * mapped on a GPU. A fully resident range still splits exactly,
+ * old->npages isn't adjusted yet here.
+ */
+ if (!has_dma_addr && old->vram_pages == old->npages)
+ new->vram_pages = new->npages;
old->vram_pages -= new->vram_pages;
+ }
return 0;
}
@@ -1963,7 +1974,7 @@ static void svm_range_restore_work(struct work_struct *work)
struct mm_struct *mm;
int evicted_ranges;
int invalid;
- int r;
+ int r = 0;
svms = container_of(dwork, struct svm_range_list, restore_work);
evicted_ranges = atomic_read(&svms->evicted_ranges);
@@ -2037,8 +2048,10 @@ out_reschedule:
mmap_write_unlock(mm);
mutex_unlock(&process_info->lock);
- /* If validation failed, reschedule another attempt */
- if (evicted_ranges) {
+ /* If validation failed, reschedule another attempt, unless the
+ * device is gone for good
+ */
+ if (evicted_ranges && r != -ENODEV) {
pr_debug("reschedule to restore svm range\n");
queue_delayed_work(system_freezable_wq, &svms->restore_work,
msecs_to_jiffies(AMDGPU_SVM_RANGE_RESTORE_DELAY_MS));
@@ -2158,14 +2171,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.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 5b634ae07c0d..0ea45ab12119 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -2021,10 +2021,6 @@ static int dm_resume(struct amdgpu_ip_block *ip_block)
/* On resume we need to rewrite the MSTM control bits to enable MST*/
s3_handle_mst(ddev, false);
- /* Exit IPS before the detection loop's first AUX/DDC access. */
- scoped_guard(mutex, &dm->dc_lock)
- dc_exit_ips_for_hw_access(dm->dc);
-
/* Do detection*/
drm_connector_list_iter_begin(ddev, &iter);
drm_for_each_connector_iter(connector, &iter) {
@@ -4709,8 +4705,6 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_commit *state,
if (dm_old_crtc_state->stream)
remove_stream(adev, acrtc, dm_old_crtc_state->stream);
- pm_runtime_get_noresume(dev->dev);
-
acrtc->enabled = true;
acrtc->hw_mode = new_crtc_state->mode;
crtc->hwmode = new_crtc_state->mode;
@@ -5294,8 +5288,13 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state)
#ifdef CONFIG_DEBUG_FS
enum amdgpu_dm_pipe_crc_source cur_crc_src;
#endif
- /* Count number of newly disabled CRTCs for dropping PM refs later. */
- if (old_crtc_state->active && !new_crtc_state->active)
+ /*
+ * One runtime PM reference per active CRTC: take it when the
+ * CRTC turns on, count it when it turns off for dropping later.
+ */
+ if (!old_crtc_state->active && new_crtc_state->active)
+ pm_runtime_get_noresume(dev->dev);
+ else if (old_crtc_state->active && !new_crtc_state->active)
crtc_disable_count++;
dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index 2b81396bd06e..b498fc5fbb14 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -1289,10 +1289,10 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link,
dc_link_get_link_cap(aconnector->dc_link));
- if (aconnector->dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
- aconnector->dc_link->dpia_bw_alloc_config.bw_alloc_enabled) {
+ if (aconnector->dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) {
dsc_options.max_target_bpp_limit_override_x16 = 8 * 16;
- dsc_options.force_dsc_when_not_needed = true;
+ if (aconnector->dc_link->dpia_bw_alloc_config.bw_alloc_enabled)
+ dsc_options.force_dsc_when_not_needed = true;
}
/* Set DSC policy according to dsc_clock_en */
@@ -3780,10 +3780,9 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
parse_edid_displayid_vrr(connector, edid);
has_vsdb = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info) != 0;
+ amdgpu_dm_connector->vsdb_info = vsdb_info;
if (has_vsdb) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
-
/* copy refresh rate info as long as VSDB advertises FreeSync */
if (vsdb_info.freesync_supported) {
amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
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..41e52c54156e 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*/
@@ -1244,6 +1244,26 @@ void populate_hdmi_info_from_connector(bool enable_frl, struct drm_hdmi_info *hd
}
EXPORT_IF_KUNIT(populate_hdmi_info_from_connector);
+static void dm_helpers_wait_for_edid_ready(struct dc_link *link,
+ struct i2c_adapter *ddc)
+{
+ const unsigned int retry_count = 50;
+ const unsigned int retry_interval_ms = 20;
+ unsigned int retry;
+
+ if (!ddc || link->dpcd_caps.dongle_type !=
+ DISPLAY_DONGLE_DP_DVI_DONGLE)
+ return;
+
+ for (retry = 0; retry < retry_count; retry++) {
+ if (drm_probe_ddc(ddc))
+ return;
+
+ if (retry + 1 < retry_count)
+ msleep(retry_interval_ms);
+ }
+}
+
enum dc_edid_status dm_helpers_read_local_edid(
struct dc_context *ctx,
struct dc_link *link,
@@ -1268,6 +1288,8 @@ enum dc_edid_status dm_helpers_read_local_edid(
if (link->dc->hwss.prepare_ddc)
link->dc->hwss.prepare_ddc(link);
+ dm_helpers_wait_for_edid_ready(link, ddc);
+
/* some dongles read edid incorrectly the first time,
* do check sum and retry to make sure read correct edid.
*/
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index ee3f3df6bb8d..5697645ef7cc 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -1342,6 +1342,8 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state,
DRM_DEBUG_DRIVER("%s: MST_DSC Try to set up params from %d streams\n", __func__, dc_state->stream_count);
for (i = 0; i < dc_state->stream_count; i++) {
struct dc_dsc_policy dsc_policy = {0};
+ u32 min_target_bpp_x16;
+ u32 max_target_bpp_x16;
stream = dc_state->streams[i];
@@ -1380,7 +1382,15 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state,
dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
dc_link->dpia_bw_alloc_config.bw_alloc_enabled)
params[count].clock_force_enable = DSC_CLK_FORCE_ENABLE;
+
dc_dsc_get_policy_for_timing(params[count].timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link));
+ min_target_bpp_x16 = dsc_policy.min_target_bpp * 16;
+ max_target_bpp_x16 = dsc_policy.max_target_bpp * 16;
+ if (dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) {
+ max_target_bpp_x16 = min_t(u32, max_target_bpp_x16, 8 * 16);
+ if (dc_link->dpia_bw_alloc_config.bw_alloc_enabled)
+ max_target_bpp_x16 = min_target_bpp_x16;
+ }
is_frl_endpoint_present = get_conv_frl_bw(aconnector, &frl_conv_bw_in_kbps, &frl_conv_dsc_bw_in_kbps);
if (stream->sink->dsc_caps.dsc_dec_caps.is_dsc_supported &&
is_frl_endpoint_present &&
@@ -1390,11 +1400,8 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state,
if (!dc_dsc_compute_bandwidth_range(
stream->sink->ctx->dc->res_pool->dscs[0],
stream->sink->ctx->dc->debug.dsc_min_slice_height_override,
- dsc_policy.min_target_bpp * 16,
- (dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
- dc_link->dpia_bw_alloc_config.bw_alloc_enabled) ?
- dsc_policy.min_target_bpp * 16 :
- dsc_policy.max_target_bpp * 16,
+ min_target_bpp_x16,
+ max_target_bpp_x16,
&stream->sink->dsc_caps.dsc_dec_caps,
&stream->timing,
dc_link_get_highest_encoding_format(dc_link),
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/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
index 2d43a35c4c42..7a288b352079 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
@@ -11052,6 +11052,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex
calculate_tdlut_setting_params->cursor_buffer_size = mode_lib->ip.cursor_buffer_size;
calculate_tdlut_setting_params->gpuvm_enable = display_cfg->gpuvm_enable;
calculate_tdlut_setting_params->gpuvm_page_size_kbytes = display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes;
+ calculate_tdlut_setting_params->tdlut_mpc_width_flag = display_cfg->plane_descriptors[k].tdlut.tdlut_mpc_width_flag;
+ calculate_tdlut_setting_params->is_gfx11 = dml_get_gfx_version(display_cfg->plane_descriptors[k].surface.tiling);
// output
calculate_tdlut_setting_params->tdlut_pte_bytes_per_frame = &s->tdlut_pte_bytes_per_frame[k];
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 2aeb9a0e6e80..382ad9bd4e57 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..e80087e35e19 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
@@ -63,6 +63,9 @@ static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min, int32_t *max);
static int smu_v13_0_0_init_ppt_limits(struct smu_context *smu);
+static int
+smu_v13_0_0_upload_overdrive_table(struct smu_context *smu,
+ OverDriveTableExternal_t *od_table);
static const struct smu_feature_bits smu_v13_0_0_dpm_features = {
.bits = {
@@ -1079,6 +1082,93 @@ out:
return feature_enabled;
}
+static u32
+smu_v13_0_0_filter_od_feature_mask(struct smu_context *smu, u32 feature_mask)
+{
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+ u32 supported_mask =
+ pptable->SkuTable.OverDriveLimitsBasicMax.FeatureCtrlMask;
+
+ return feature_mask & supported_mask;
+}
+
+static bool smu_v13_0_0_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return !memcmp(od->FanLinearPwmPoints, boot->FanLinearPwmPoints,
+ sizeof(od->FanLinearPwmPoints)) &&
+ !memcmp(od->FanLinearTempPoints, boot->FanLinearTempPoints,
+ sizeof(od->FanLinearTempPoints)) &&
+ od->FanMinimumPwm == boot->FanMinimumPwm &&
+ od->AcousticTargetRpmThreshold == boot->AcousticTargetRpmThreshold &&
+ od->AcousticLimitRpmThreshold == boot->AcousticLimitRpmThreshold &&
+ od->FanTargetTemperature == boot->FanTargetTemperature &&
+ od->FanMode == boot->FanMode;
+}
+
+static bool smu_v13_0_0_zero_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return od->FanZeroRpmEnable == boot->FanZeroRpmEnable &&
+ od->FanZeroRpmStopTemp == boot->FanZeroRpmStopTemp;
+}
+
+static u32 smu_v13_0_0_normalize_feature_mask(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot,
+ u32 feature_mask)
+{
+ if (od->GfxclkFmin == boot->GfxclkFmin &&
+ od->GfxclkFmax == boot->GfxclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFXCLK_BIT);
+ if (od->UclkFmin == boot->UclkFmin &&
+ od->UclkFmax == boot->UclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_UCLK_BIT);
+ if (!memcmp(od->VoltageOffsetPerZoneBoundary,
+ boot->VoltageOffsetPerZoneBoundary,
+ sizeof(od->VoltageOffsetPerZoneBoundary)))
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
+ if (smu_v13_0_0_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
+ if (smu_v13_0_0_zero_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ if (!od->Ppt)
+ feature_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+
+ return feature_mask;
+}
+
+static int
+smu_v13_0_0_upload_ppt_overdrive_table(struct smu_context *smu, s16 ppt)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ OverDriveTableExternal_t *od_table = table_context->overdrive_table;
+ OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ u32 next_mask = table_context->user_od_feature_mask;
+ int ret;
+
+ /* Do not commit OD settings staged after the last c operation. */
+ memcpy(&upload_table, user_od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.Ppt = ppt;
+ if (ppt)
+ next_mask |= BIT(PP_OD_FEATURE_PPT_BIT);
+ else
+ next_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+ upload_table.OverDriveTable.FeatureCtrlMask =
+ smu_v13_0_0_filter_od_feature_mask(smu, next_mask);
+
+ ret = smu_v13_0_0_upload_overdrive_table(smu, &upload_table);
+ if (ret)
+ return ret;
+
+ table_context->user_od_feature_mask =
+ upload_table.OverDriveTable.FeatureCtrlMask;
+ od_table->OverDriveTable.Ppt = ppt;
+ user_od_table->OverDriveTable.Ppt = ppt;
+
+ return 0;
+}
+
static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min,
@@ -1514,41 +1604,30 @@ static int smu_v13_0_0_od_restore_table_single(struct smu_context *smu, long inp
boot_overdrive_table->OverDriveTable.FanLinearPwmPoints[i];
}
od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_LIMIT:
od_table->OverDriveTable.AcousticLimitRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_TARGET:
od_table->OverDriveTable.AcousticTargetRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticTargetRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
od_table->OverDriveTable.FanTargetTemperature =
boot_overdrive_table->OverDriveTable.FanTargetTemperature;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_MINIMUM_PWM:
od_table->OverDriveTable.FanMinimumPwm =
boot_overdrive_table->OverDriveTable.FanMinimumPwm;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_ENABLE:
od_table->OverDriveTable.FanZeroRpmEnable =
boot_overdrive_table->OverDriveTable.FanZeroRpmEnable;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_STOP_TEMP:
od_table->OverDriveTable.FanZeroRpmStopTemp =
boot_overdrive_table->OverDriveTable.FanZeroRpmStopTemp;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
default:
dev_info(adev->dev, "Invalid table index: %ld\n", input);
@@ -1566,10 +1645,17 @@ static int smu_v13_0_0_od_edit_dpm_table(struct smu_context *smu,
struct smu_table_context *table_context = &smu->smu_table;
OverDriveTableExternal_t *od_table =
(OverDriveTableExternal_t *)table_context->overdrive_table;
+ OverDriveTableExternal_t *user_od_table =
+ (OverDriveTableExternal_t *)table_context->user_overdrive_table;
+ OverDriveTableExternal_t *boot_od_table =
+ (OverDriveTableExternal_t *)table_context->boot_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ OverDriveTable_t *od = &od_table->OverDriveTable;
+ OverDriveTable_t *boot = &boot_od_table->OverDriveTable;
struct amdgpu_device *adev = smu->adev;
uint32_t offset_of_voltageoffset;
int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
+ u32 next_mask;
int i, ret = 0;
switch (type) {
@@ -1891,44 +1977,43 @@ 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;
+ sizeof(OverDriveTableExternal_t));
+ od_table->OverDriveTable.FeatureCtrlMask = 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.
- */
offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset)) {
- smu_v13_0_0_dump_od_table(smu, od_table);
+ next_mask = smu_v13_0_0_filter_od_feature_mask(smu,
+ table_context->user_od_feature_mask |
+ od->FeatureCtrlMask);
+ next_mask = smu_v13_0_0_normalize_feature_mask(od, boot,
+ next_mask);
- ret = smu_v13_0_0_upload_overdrive_table(smu, od_table);
+ if (memcmp((u8 *)od_table + offset_of_voltageoffset,
+ (u8 *)user_od_table + offset_of_voltageoffset,
+ sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset) ||
+ next_mask != table_context->user_od_feature_mask) {
+ memcpy(&upload_table, od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.FeatureCtrlMask = next_mask;
+ smu_v13_0_0_dump_od_table(smu, &upload_table);
+
+ ret = smu_v13_0_0_upload_overdrive_table(smu, &upload_table);
if (ret) {
dev_err(adev->dev, "Failed to upload overdrive table!\n");
return ret;
}
+ table_context->user_od_feature_mask = next_mask;
od_table->OverDriveTable.FeatureCtrlMask = 0;
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
+ memcpy((u8 *)user_od_table + offset_of_voltageoffset,
(u8 *)od_table + offset_of_voltageoffset,
sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
+ user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (!memcmp(table_context->user_overdrive_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t)))
- smu->user_dpm_profile.user_od = false;
- else
- smu->user_dpm_profile.user_od = true;
+ smu->user_dpm_profile.user_od =
+ !!(next_mask & ~BIT(PP_OD_FEATURE_PPT_BIT));
}
break;
@@ -2191,6 +2276,7 @@ static int smu_v13_0_0_set_default_od_settings(struct smu_context *smu)
memcpy(od_table,
boot_od_table,
sizeof(OverDriveTableExternal_t));
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
/*
* For S3/S4/Runpm resume, we need to setup those overdrive tables again,
@@ -2200,7 +2286,9 @@ static int smu_v13_0_0_set_default_od_settings(struct smu_context *smu)
memcpy(user_od_table,
boot_od_table,
sizeof(OverDriveTableExternal_t));
+ user_od_table->OverDriveTable.FeatureCtrlMask = 0;
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,
@@ -2237,6 +2325,14 @@ static int smu_v13_0_0_set_default_od_settings(struct smu_context *smu)
user_od_table_bak.OverDriveTable.FanZeroRpmEnable;
user_od_table->OverDriveTable.FanZeroRpmStopTemp =
user_od_table_bak.OverDriveTable.FanZeroRpmStopTemp;
+ user_od_table->OverDriveTable.FanMode =
+ user_od_table_bak.OverDriveTable.FanMode;
+ user_od_table->OverDriveTable.FeatureCtrlMask = 0;
+ }
+ if (smu->adev->in_suspend) {
+ user_od_table->OverDriveTable.Ppt = 0;
+ smu->smu_table.user_od_feature_mask &=
+ ~BIT(PP_OD_FEATURE_PPT_BIT);
}
smu_v13_0_0_set_supported_od_feature_mask(smu);
@@ -2249,17 +2345,22 @@ static int smu_v13_0_0_restore_user_od_settings(struct smu_context *smu)
struct smu_table_context *table_context = &smu->smu_table;
OverDriveTableExternal_t *od_table = table_context->overdrive_table;
OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ u32 next_mask;
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);
- res = smu_v13_0_0_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
+ next_mask = smu_v13_0_0_filter_od_feature_mask(smu,
+ table_context->user_od_feature_mask);
+ memcpy(&upload_table, user_od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.FeatureCtrlMask = next_mask;
+ res = smu_v13_0_0_upload_overdrive_table(smu, &upload_table);
+ if (res == 0) {
+ table_context->user_od_feature_mask = next_mask;
+ smu->user_dpm_profile.user_od =
+ !!(next_mask & ~BIT(PP_OD_FEATURE_PPT_BIT));
memcpy(od_table, user_od_table, sizeof(OverDriveTableExternal_t));
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
+ }
return res;
}
@@ -3053,10 +3154,8 @@ static int smu_v13_0_0_set_ppt_limit(struct smu_context *smu,
SkuTable_t *skutable = &pptable->SkuTable;
uint32_t msg_limit = skutable->MsgLimits.Power
[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
OverDriveTableExternal_t current_od_table;
+ s16 ppt;
int ret = 0;
if (limit_type != SMU_PPT_LIMIT_PPT0)
@@ -3068,10 +3167,7 @@ static int smu_v13_0_0_set_ppt_limit(struct smu_context *smu,
return ret;
if (current_od_table.OverDriveTable.Ppt) {
- od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v13_0_0_upload_overdrive_table(smu, od_table);
+ ret = smu_v13_0_0_upload_ppt_overdrive_table(smu, 0);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
return ret;
@@ -3083,10 +3179,8 @@ static int smu_v13_0_0_set_ppt_limit(struct smu_context *smu,
if (ret)
return ret;
- od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v13_0_0_upload_overdrive_table(smu, od_table);
+ ppt = (limit * 100) / msg_limit - 100;
+ ret = smu_v13_0_0_upload_ppt_overdrive_table(smu, ppt);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
return ret;
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..8285d4a91589 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
@@ -63,6 +63,9 @@ static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min, int32_t *max);
static int smu_v13_0_7_init_ppt_limits(struct smu_context *smu);
+static int
+smu_v13_0_7_upload_overdrive_table(struct smu_context *smu,
+ OverDriveTableExternal_t *od_table);
static const struct smu_feature_bits smu_v13_0_7_dpm_features = {
.bits = {
@@ -94,6 +97,7 @@ static const struct smu_feature_bits smu_v13_0_7_dpm_features = {
#define PP_OD_FEATURE_GFXCLK_FMAX 1
#define PP_OD_FEATURE_UCLK_FMIN 2
#define PP_OD_FEATURE_UCLK_FMAX 3
+
#define PP_OD_FEATURE_GFX_VF_CURVE 4
#define PP_OD_FEATURE_FAN_CURVE_TEMP 5
#define PP_OD_FEATURE_FAN_CURVE_PWM 6
@@ -1089,6 +1093,93 @@ out:
return feature_enabled;
}
+static u32
+smu_v13_0_7_filter_od_feature_mask(struct smu_context *smu, u32 feature_mask)
+{
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+ u32 supported_mask =
+ pptable->SkuTable.OverDriveLimitsBasicMax.FeatureCtrlMask;
+
+ return feature_mask & supported_mask;
+}
+
+static bool smu_v13_0_7_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return !memcmp(od->FanLinearPwmPoints, boot->FanLinearPwmPoints,
+ sizeof(od->FanLinearPwmPoints)) &&
+ !memcmp(od->FanLinearTempPoints, boot->FanLinearTempPoints,
+ sizeof(od->FanLinearTempPoints)) &&
+ od->FanMinimumPwm == boot->FanMinimumPwm &&
+ od->AcousticTargetRpmThreshold == boot->AcousticTargetRpmThreshold &&
+ od->AcousticLimitRpmThreshold == boot->AcousticLimitRpmThreshold &&
+ od->FanTargetTemperature == boot->FanTargetTemperature &&
+ od->FanMode == boot->FanMode;
+}
+
+static bool smu_v13_0_7_zero_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return od->FanZeroRpmEnable == boot->FanZeroRpmEnable &&
+ od->FanZeroRpmStopTemp == boot->FanZeroRpmStopTemp;
+}
+
+static u32 smu_v13_0_7_normalize_feature_mask(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot,
+ u32 feature_mask)
+{
+ if (od->GfxclkFmin == boot->GfxclkFmin &&
+ od->GfxclkFmax == boot->GfxclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFXCLK_BIT);
+ if (od->UclkFmin == boot->UclkFmin &&
+ od->UclkFmax == boot->UclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_UCLK_BIT);
+ if (!memcmp(od->VoltageOffsetPerZoneBoundary,
+ boot->VoltageOffsetPerZoneBoundary,
+ sizeof(od->VoltageOffsetPerZoneBoundary)))
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
+ if (smu_v13_0_7_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
+ if (smu_v13_0_7_zero_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ if (!od->Ppt)
+ feature_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+
+ return feature_mask;
+}
+
+static int
+smu_v13_0_7_upload_ppt_overdrive_table(struct smu_context *smu, s16 ppt)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ OverDriveTableExternal_t *od_table = table_context->overdrive_table;
+ OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ u32 next_mask = table_context->user_od_feature_mask;
+ int ret;
+
+ /* Do not commit OD settings staged after the last c operation. */
+ memcpy(&upload_table, user_od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.Ppt = ppt;
+ if (ppt)
+ next_mask |= BIT(PP_OD_FEATURE_PPT_BIT);
+ else
+ next_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+ upload_table.OverDriveTable.FeatureCtrlMask =
+ smu_v13_0_7_filter_od_feature_mask(smu, next_mask);
+
+ ret = smu_v13_0_7_upload_overdrive_table(smu, &upload_table);
+ if (ret)
+ return ret;
+
+ table_context->user_od_feature_mask =
+ upload_table.OverDriveTable.FeatureCtrlMask;
+ od_table->OverDriveTable.Ppt = ppt;
+ user_od_table->OverDriveTable.Ppt = ppt;
+
+ return 0;
+}
+
static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min,
@@ -1523,41 +1614,30 @@ static int smu_v13_0_7_od_restore_table_single(struct smu_context *smu, long inp
boot_overdrive_table->OverDriveTable.FanLinearPwmPoints[i];
}
od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_LIMIT:
od_table->OverDriveTable.AcousticLimitRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_TARGET:
od_table->OverDriveTable.AcousticTargetRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticTargetRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
od_table->OverDriveTable.FanTargetTemperature =
boot_overdrive_table->OverDriveTable.FanTargetTemperature;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_MINIMUM_PWM:
od_table->OverDriveTable.FanMinimumPwm =
boot_overdrive_table->OverDriveTable.FanMinimumPwm;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_ENABLE:
od_table->OverDriveTable.FanZeroRpmEnable =
boot_overdrive_table->OverDriveTable.FanZeroRpmEnable;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_STOP_TEMP:
od_table->OverDriveTable.FanZeroRpmStopTemp =
boot_overdrive_table->OverDriveTable.FanZeroRpmStopTemp;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
default:
dev_info(adev->dev, "Invalid table index: %ld\n", input);
@@ -1575,10 +1655,17 @@ static int smu_v13_0_7_od_edit_dpm_table(struct smu_context *smu,
struct smu_table_context *table_context = &smu->smu_table;
OverDriveTableExternal_t *od_table =
(OverDriveTableExternal_t *)table_context->overdrive_table;
+ OverDriveTableExternal_t *user_od_table =
+ (OverDriveTableExternal_t *)table_context->user_overdrive_table;
+ OverDriveTableExternal_t *boot_od_table =
+ (OverDriveTableExternal_t *)table_context->boot_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ OverDriveTable_t *od = &od_table->OverDriveTable;
+ OverDriveTable_t *boot = &boot_od_table->OverDriveTable;
struct amdgpu_device *adev = smu->adev;
uint32_t offset_of_voltageoffset;
int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
+ u32 next_mask;
int i, ret = 0;
switch (type) {
@@ -1900,45 +1987,44 @@ 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;
}
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.
- */
offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset)) {
- smu_v13_0_7_dump_od_table(smu, od_table);
+ next_mask = smu_v13_0_7_filter_od_feature_mask(smu,
+ table_context->user_od_feature_mask |
+ od->FeatureCtrlMask);
+ next_mask = smu_v13_0_7_normalize_feature_mask(od, boot,
+ next_mask);
- ret = smu_v13_0_7_upload_overdrive_table(smu, od_table);
+ if (memcmp((u8 *)od_table + offset_of_voltageoffset,
+ (u8 *)user_od_table + offset_of_voltageoffset,
+ sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset) ||
+ next_mask != table_context->user_od_feature_mask) {
+ memcpy(&upload_table, od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.FeatureCtrlMask = next_mask;
+ smu_v13_0_7_dump_od_table(smu, &upload_table);
+
+ ret = smu_v13_0_7_upload_overdrive_table(smu, &upload_table);
if (ret) {
dev_err(adev->dev, "Failed to upload overdrive table!\n");
return ret;
}
+ table_context->user_od_feature_mask = next_mask;
od_table->OverDriveTable.FeatureCtrlMask = 0;
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
+ memcpy((u8 *)user_od_table + offset_of_voltageoffset,
(u8 *)od_table + offset_of_voltageoffset,
sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
+ user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (!memcmp(table_context->user_overdrive_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t)))
- smu->user_dpm_profile.user_od = false;
- else
- smu->user_dpm_profile.user_od = true;
+ smu->user_dpm_profile.user_od =
+ !!(next_mask & ~BIT(PP_OD_FEATURE_PPT_BIT));
}
break;
@@ -2193,6 +2279,7 @@ static int smu_v13_0_7_set_default_od_settings(struct smu_context *smu)
memcpy(od_table,
boot_od_table,
sizeof(OverDriveTableExternal_t));
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
/*
* For S3/S4/Runpm resume, we need to setup those overdrive tables again,
@@ -2202,7 +2289,9 @@ static int smu_v13_0_7_set_default_od_settings(struct smu_context *smu)
memcpy(user_od_table,
boot_od_table,
sizeof(OverDriveTableExternal_t));
+ user_od_table->OverDriveTable.FeatureCtrlMask = 0;
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,
@@ -2239,6 +2328,14 @@ static int smu_v13_0_7_set_default_od_settings(struct smu_context *smu)
user_od_table_bak.OverDriveTable.FanZeroRpmEnable;
user_od_table->OverDriveTable.FanZeroRpmStopTemp =
user_od_table_bak.OverDriveTable.FanZeroRpmStopTemp;
+ user_od_table->OverDriveTable.FanMode =
+ user_od_table_bak.OverDriveTable.FanMode;
+ user_od_table->OverDriveTable.FeatureCtrlMask = 0;
+ }
+ if (smu->adev->in_suspend) {
+ user_od_table->OverDriveTable.Ppt = 0;
+ smu->smu_table.user_od_feature_mask &=
+ ~BIT(PP_OD_FEATURE_PPT_BIT);
}
smu_v13_0_7_set_supported_od_feature_mask(smu);
@@ -2251,17 +2348,22 @@ static int smu_v13_0_7_restore_user_od_settings(struct smu_context *smu)
struct smu_table_context *table_context = &smu->smu_table;
OverDriveTableExternal_t *od_table = table_context->overdrive_table;
OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ u32 next_mask;
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);
- res = smu_v13_0_7_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
+ next_mask = smu_v13_0_7_filter_od_feature_mask(smu,
+ table_context->user_od_feature_mask);
+ memcpy(&upload_table, user_od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.FeatureCtrlMask = next_mask;
+ res = smu_v13_0_7_upload_overdrive_table(smu, &upload_table);
+ if (res == 0) {
+ table_context->user_od_feature_mask = next_mask;
+ smu->user_dpm_profile.user_od =
+ !!(next_mask & ~BIT(PP_OD_FEATURE_PPT_BIT));
memcpy(od_table, user_od_table, sizeof(OverDriveTableExternal_t));
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
+ }
return res;
}
@@ -2673,10 +2775,8 @@ static int smu_v13_0_7_set_ppt_limit(struct smu_context *smu,
SkuTable_t *skutable = &pptable->SkuTable;
uint32_t msg_limit = skutable->MsgLimits.Power
[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
OverDriveTableExternal_t current_od_table;
+ s16 ppt;
int ret = 0;
if (limit_type != SMU_PPT_LIMIT_PPT0)
@@ -2688,10 +2788,7 @@ static int smu_v13_0_7_set_ppt_limit(struct smu_context *smu,
return ret;
if (current_od_table.OverDriveTable.Ppt) {
- od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v13_0_7_upload_overdrive_table(smu, od_table);
+ ret = smu_v13_0_7_upload_ppt_overdrive_table(smu, 0);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
return ret;
@@ -2703,10 +2800,8 @@ static int smu_v13_0_7_set_ppt_limit(struct smu_context *smu,
if (ret)
return ret;
- od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v13_0_7_upload_overdrive_table(smu, od_table);
+ ppt = (limit * 100) / msg_limit - 100;
+ ret = smu_v13_0_7_upload_ppt_overdrive_table(smu, ppt);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
return 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 6621e50de965..6a12d6f8c9dd 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
@@ -91,6 +91,10 @@ static const struct smu_feature_bits smu_v14_0_2_dpm_features = {
#define PP_OD_FEATURE_FAN_ZERO_RPM_ENABLE 11
#define PP_OD_FEATURE_FAN_ZERO_RPM_STOP_TEMP 12
+#define SMU_14_0_2_OD_FAN_FEATURE_MASK \
+ (BIT(PP_OD_FEATURE_FAN_CURVE_BIT) | \
+ BIT(PP_OD_FEATURE_FAN_LEGACY_BIT))
+
static struct cmn2asic_msg_mapping smu_v14_0_2_message_map[SMU_MSG_MAX_COUNT] = {
MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 1),
MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 1),
@@ -963,6 +967,84 @@ out:
return feature_enabled;
}
+static u32
+smu_v14_0_2_filter_od_feature_mask(struct smu_context *smu, u32 feature_mask)
+{
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+ u32 supported_mask =
+ pptable->SkuTable.OverDriveLimitsBasicMax.FeatureCtrlMask;
+
+ return feature_mask & supported_mask;
+}
+
+static u32
+smu_v14_0_2_get_od_feature_mask(struct smu_context *smu, u32 pending_mask)
+{
+ u32 supported_mask = smu_v14_0_2_filter_od_feature_mask(smu, ~0U);
+ u32 fan_mask = supported_mask & SMU_14_0_2_OD_FAN_FEATURE_MASK;
+ u32 mask = smu->smu_table.user_od_feature_mask;
+
+ if (pending_mask & fan_mask)
+ mask &= ~fan_mask;
+ mask |= pending_mask;
+
+ return smu_v14_0_2_filter_od_feature_mask(smu, mask);
+}
+
+static bool smu_v14_0_2_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return !memcmp(od->FanLinearPwmPoints, boot->FanLinearPwmPoints,
+ sizeof(od->FanLinearPwmPoints)) &&
+ !memcmp(od->FanLinearTempPoints, boot->FanLinearTempPoints,
+ sizeof(od->FanLinearTempPoints)) &&
+ od->AcousticTargetRpmThreshold == boot->AcousticTargetRpmThreshold &&
+ od->AcousticLimitRpmThreshold == boot->AcousticLimitRpmThreshold &&
+ od->FanTargetTemperature == boot->FanTargetTemperature &&
+ od->FanMode == boot->FanMode;
+}
+
+static bool smu_v14_0_2_legacy_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return od->FanMinimumPwm == boot->FanMinimumPwm &&
+ od->FanMode == boot->FanMode;
+}
+
+static bool smu_v14_0_2_zero_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return od->FanZeroRpmEnable == boot->FanZeroRpmEnable &&
+ od->FanZeroRpmStopTemp == boot->FanZeroRpmStopTemp;
+}
+
+static u32 smu_v14_0_2_normalize_feature_mask(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot,
+ u32 feature_mask)
+{
+ if (od->GfxclkFoffset == boot->GfxclkFoffset)
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFXCLK_BIT);
+ if (od->UclkFmin == boot->UclkFmin &&
+ od->UclkFmax == boot->UclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_UCLK_BIT);
+ if (!memcmp(od->VoltageOffsetPerZoneBoundary,
+ boot->VoltageOffsetPerZoneBoundary,
+ sizeof(od->VoltageOffsetPerZoneBoundary)))
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
+ if ((feature_mask & BIT(PP_OD_FEATURE_FAN_CURVE_BIT)) &&
+ smu_v14_0_2_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
+ if ((feature_mask & BIT(PP_OD_FEATURE_FAN_LEGACY_BIT)) &&
+ smu_v14_0_2_legacy_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_FAN_LEGACY_BIT);
+ if (smu_v14_0_2_zero_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ if (!od->Ppt)
+ feature_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+
+ return feature_mask;
+}
+
static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min,
@@ -2317,6 +2399,38 @@ static int smu_v14_0_2_upload_overdrive_table(struct smu_context *smu,
return ret;
}
+static int
+smu_v14_0_2_upload_ppt_overdrive_table(struct smu_context *smu, s16 ppt)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ OverDriveTableExternal_t *od_table = table_context->overdrive_table;
+ OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ u32 next_mask = table_context->user_od_feature_mask;
+ int ret;
+
+ /* Do not commit OD settings staged after the last c operation. */
+ memcpy(&upload_table, user_od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.Ppt = ppt;
+ if (ppt)
+ next_mask |= BIT(PP_OD_FEATURE_PPT_BIT);
+ else
+ next_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+ upload_table.OverDriveTable.FeatureCtrlMask =
+ smu_v14_0_2_filter_od_feature_mask(smu, next_mask);
+
+ ret = smu_v14_0_2_upload_overdrive_table(smu, &upload_table);
+ if (ret)
+ return ret;
+
+ table_context->user_od_feature_mask =
+ upload_table.OverDriveTable.FeatureCtrlMask;
+ od_table->OverDriveTable.Ppt = ppt;
+ user_od_table->OverDriveTable.Ppt = ppt;
+
+ return 0;
+}
+
static void smu_v14_0_2_set_supported_od_feature_mask(struct smu_context *smu)
{
struct amdgpu_device *adev = smu->adev;
@@ -2375,6 +2489,7 @@ static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu)
memcpy(od_table,
boot_od_table,
sizeof(OverDriveTableExternal_t));
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
/*
* For S3/S4/Runpm resume, we need to setup those overdrive tables again,
@@ -2384,7 +2499,9 @@ static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu)
memcpy(user_od_table,
boot_od_table,
sizeof(OverDriveTableExternal_t));
+ user_od_table->OverDriveTable.FeatureCtrlMask = 0;
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,
@@ -2419,6 +2536,14 @@ static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu)
user_od_table_bak.OverDriveTable.FanZeroRpmEnable;
user_od_table->OverDriveTable.FanZeroRpmStopTemp =
user_od_table_bak.OverDriveTable.FanZeroRpmStopTemp;
+ user_od_table->OverDriveTable.FanMode =
+ user_od_table_bak.OverDriveTable.FanMode;
+ user_od_table->OverDriveTable.FeatureCtrlMask = 0;
+ }
+ if (smu->adev->in_suspend) {
+ user_od_table->OverDriveTable.Ppt = 0;
+ smu->smu_table.user_od_feature_mask &=
+ ~BIT(PP_OD_FEATURE_PPT_BIT);
}
smu_v14_0_2_set_supported_od_feature_mask(smu);
@@ -2431,17 +2556,22 @@ static int smu_v14_0_2_restore_user_od_settings(struct smu_context *smu)
struct smu_table_context *table_context = &smu->smu_table;
OverDriveTableExternal_t *od_table = table_context->overdrive_table;
OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ u32 next_mask;
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);
- res = smu_v14_0_2_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
+ next_mask = smu_v14_0_2_filter_od_feature_mask(smu,
+ table_context->user_od_feature_mask);
+ memcpy(&upload_table, user_od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.FeatureCtrlMask = next_mask;
+ res = smu_v14_0_2_upload_overdrive_table(smu, &upload_table);
+ if (res == 0) {
+ table_context->user_od_feature_mask = next_mask;
+ smu->user_dpm_profile.user_od =
+ !!(next_mask & ~BIT(PP_OD_FEATURE_PPT_BIT));
memcpy(od_table, user_od_table, sizeof(OverDriveTableExternal_t));
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
+ }
return res;
}
@@ -2465,43 +2595,30 @@ static int smu_v14_0_2_od_restore_table_single(struct smu_context *smu, long inp
boot_overdrive_table->OverDriveTable.FanLinearPwmPoints[i];
}
od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
- od_table->OverDriveTable.FeatureCtrlMask &= ~BIT(PP_OD_FEATURE_FAN_LEGACY_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_ENABLE:
od_table->OverDriveTable.FanZeroRpmEnable =
boot_overdrive_table->OverDriveTable.FanZeroRpmEnable;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_STOP_TEMP:
od_table->OverDriveTable.FanZeroRpmStopTemp =
boot_overdrive_table->OverDriveTable.FanZeroRpmStopTemp;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_LIMIT:
od_table->OverDriveTable.AcousticLimitRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_TARGET:
od_table->OverDriveTable.AcousticTargetRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticTargetRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
od_table->OverDriveTable.FanTargetTemperature =
boot_overdrive_table->OverDriveTable.FanTargetTemperature;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_MINIMUM_PWM:
od_table->OverDriveTable.FanMinimumPwm =
boot_overdrive_table->OverDriveTable.FanMinimumPwm;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_LEGACY_BIT);
- od_table->OverDriveTable.FeatureCtrlMask &= ~BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
default:
dev_info(adev->dev, "Invalid table index: %ld\n", input);
@@ -2519,10 +2636,17 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
struct smu_table_context *table_context = &smu->smu_table;
OverDriveTableExternal_t *od_table =
(OverDriveTableExternal_t *)table_context->overdrive_table;
+ OverDriveTableExternal_t *user_od_table =
+ (OverDriveTableExternal_t *)table_context->user_overdrive_table;
+ OverDriveTableExternal_t *boot_od_table =
+ (OverDriveTableExternal_t *)table_context->boot_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ OverDriveTable_t *od = &od_table->OverDriveTable;
+ OverDriveTable_t *boot = &boot_od_table->OverDriveTable;
struct amdgpu_device *adev = smu->adev;
uint32_t offset_of_voltageoffset;
int32_t minimum, maximum;
- uint32_t feature_ctrlmask;
+ u32 next_mask;
int i, ret = 0;
switch (type) {
@@ -2694,6 +2818,7 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
od_table->OverDriveTable.AcousticLimitRpmThreshold = input[0];
od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
+ od_table->OverDriveTable.FeatureCtrlMask &= ~BIT(PP_OD_FEATURE_FAN_LEGACY_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_TARGET:
@@ -2716,6 +2841,7 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
od_table->OverDriveTable.AcousticTargetRpmThreshold = input[0];
od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
+ od_table->OverDriveTable.FeatureCtrlMask &= ~BIT(PP_OD_FEATURE_FAN_LEGACY_BIT);
break;
case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
@@ -2738,6 +2864,7 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
od_table->OverDriveTable.FanTargetTemperature = input[0];
od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
+ od_table->OverDriveTable.FeatureCtrlMask &= ~BIT(PP_OD_FEATURE_FAN_LEGACY_BIT);
break;
case PP_OD_EDIT_FAN_MINIMUM_PWM:
@@ -2811,44 +2938,42 @@ 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;
}
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.
- */
offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset)) {
- smu_v14_0_2_dump_od_table(smu, od_table);
+ next_mask = smu_v14_0_2_get_od_feature_mask(smu,
+ od->FeatureCtrlMask);
+ next_mask = smu_v14_0_2_normalize_feature_mask(od, boot,
+ next_mask);
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
+ if (memcmp((u8 *)od_table + offset_of_voltageoffset,
+ (u8 *)user_od_table + offset_of_voltageoffset,
+ sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset) ||
+ next_mask != table_context->user_od_feature_mask) {
+ memcpy(&upload_table, od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.FeatureCtrlMask = next_mask;
+ smu_v14_0_2_dump_od_table(smu, &upload_table);
+
+ ret = smu_v14_0_2_upload_overdrive_table(smu, &upload_table);
if (ret) {
dev_err(adev->dev, "Failed to upload overdrive table!\n");
return ret;
}
+ table_context->user_od_feature_mask = next_mask;
od_table->OverDriveTable.FeatureCtrlMask = 0;
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
+ memcpy((u8 *)user_od_table + offset_of_voltageoffset,
(u8 *)od_table + offset_of_voltageoffset,
sizeof(OverDriveTableExternal_t) - offset_of_voltageoffset);
+ user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (!memcmp(table_context->user_overdrive_table,
- table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t)))
- smu->user_dpm_profile.user_od = false;
- else
- smu->user_dpm_profile.user_od = true;
+ smu->user_dpm_profile.user_od =
+ !!(next_mask & ~BIT(PP_OD_FEATURE_PPT_BIT));
}
break;
@@ -2866,10 +2991,8 @@ static int smu_v14_0_2_set_ppt_limit(struct smu_context *smu,
PPTable_t *pptable = smu->smu_table.driver_pptable;
uint32_t msg_limit = pptable->SkuTable.MsgLimits.Power
[PPT_THROTTLER_PPT0][POWER_SOURCE_AC];
- struct smu_table_context *table_context = &smu->smu_table;
- OverDriveTableExternal_t *od_table =
- (OverDriveTableExternal_t *)table_context->overdrive_table;
OverDriveTableExternal_t current_od_table;
+ s16 ppt;
int ret = 0;
if (limit_type != SMU_PPT_LIMIT_PPT0)
@@ -2881,10 +3004,7 @@ static int smu_v14_0_2_set_ppt_limit(struct smu_context *smu,
return ret;
if (current_od_table.OverDriveTable.Ppt) {
- od_table->OverDriveTable.Ppt = 0;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
+ ret = smu_v14_0_2_upload_ppt_overdrive_table(smu, 0);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
return ret;
@@ -2896,10 +3016,8 @@ static int smu_v14_0_2_set_ppt_limit(struct smu_context *smu,
if (ret)
return ret;
- od_table->OverDriveTable.Ppt = (limit * 100) / msg_limit - 100;
- od_table->OverDriveTable.FeatureCtrlMask |= 1U << PP_OD_FEATURE_PPT_BIT;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
+ ppt = (limit * 100) / msg_limit - 100;
+ ret = smu_v14_0_2_upload_ppt_overdrive_table(smu, ppt);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
return 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);
diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c
index d75d575473da..86d0b8f9e7d7 100644
--- a/drivers/gpu/drm/panthor/panthor_mmu.c
+++ b/drivers/gpu/drm/panthor/panthor_mmu.c
@@ -616,8 +616,7 @@ static int panthor_mmu_as_enable(struct panthor_device *ptdev, u32 as_nr,
return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
}
-static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
- bool recycle_slot)
+static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr)
{
struct panthor_mmu *mmu = ptdev->mmu;
struct panthor_vm *vm = ptdev->mmu->as.slots[as_nr].vm;
@@ -628,12 +627,6 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
panthor_irq_disable_events(&ptdev->mmu->irq,
panthor_mmu_as_fault_mask(ptdev, as_nr));
- /* Flush+invalidate RW caches, invalidate RO ones. */
- ret = panthor_gpu_flush_caches(ptdev, CACHE_CLEAN | CACHE_INV,
- CACHE_CLEAN | CACHE_INV, CACHE_INV);
- if (ret)
- return ret;
-
if (vm && vm->locked_region.size) {
/* Unlock the region if there's a lock pending. */
ret = as_send_cmd_and_wait(ptdev, vm->as.id, AS_COMMAND_UNLOCK);
@@ -641,17 +634,22 @@ static int panthor_mmu_as_disable(struct panthor_device *ptdev, u32 as_nr,
return ret;
}
- /* If the slot is going to be used immediately, don't bother changing
- * the config.
- */
- if (recycle_slot)
- return 0;
-
gpu_write64(mmu->iomem, AS_TRANSTAB(as_nr), 0);
gpu_write64(mmu->iomem, AS_MEMATTR(as_nr), 0);
gpu_write64(mmu->iomem, AS_TRANSCFG(as_nr), AS_TRANSCFG_ADRMODE_UNMAPPED);
- return as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
+ ret = as_send_cmd_and_wait(ptdev, as_nr, AS_COMMAND_UPDATE);
+ if (ret)
+ return ret;
+
+ /* Flush+invalidate RW caches after we've unmapped, to make sure any
+ * cacheline eviction is effective before we potentially return
+ * memory pointed by this VM to the system. This needs to be done
+ * after the UPDATE(UNMAPPED) operation to guarantee that not further
+ * PT-walk can pull VM data into the cache.
+ */
+ return panthor_gpu_flush_caches(ptdev, CACHE_CLEAN | CACHE_INV,
+ CACHE_CLEAN | CACHE_INV, 0);
}
static u32 panthor_mmu_fault_mask(struct panthor_device *ptdev, u32 value)
@@ -773,7 +771,7 @@ int panthor_vm_active(struct panthor_vm *vm)
drm_WARN_ON(&ptdev->base, refcount_read(&lru_vm->as.active_cnt));
as = lru_vm->as.id;
- ret = panthor_mmu_as_disable(ptdev, as, true);
+ ret = panthor_mmu_as_disable(ptdev, as);
if (ret)
goto out_unlock;
@@ -915,7 +913,7 @@ static void panthor_vm_declare_unusable(struct panthor_vm *vm)
vm->unusable = true;
mutex_lock(&ptdev->mmu->as.slots_lock);
if (vm->as.id >= 0 && drm_dev_enter(&ptdev->base, &cookie)) {
- panthor_mmu_as_disable(ptdev, vm->as.id, false);
+ panthor_mmu_as_disable(ptdev, vm->as.id);
drm_dev_exit(cookie);
}
mutex_unlock(&ptdev->mmu->as.slots_lock);
@@ -1832,13 +1830,12 @@ static void panthor_vm_unlock_region(struct panthor_vm *vm)
if (vm->as.id >= 0) {
int ret;
- /* flush+invalidate RW caches and invalidate RO ones.
+ /* flush+invalidate RW caches.
* TODO: See if we can use FLUSH_PA_RANGE when the physical
* range is narrow enough and the HW supports it.
*/
ret = panthor_gpu_flush_caches(ptdev, CACHE_CLEAN | CACHE_INV,
- CACHE_CLEAN | CACHE_INV,
- CACHE_INV);
+ CACHE_CLEAN | CACHE_INV, 0);
/* Unlock the region if the flush is effective. */
if (!ret)
@@ -1909,7 +1906,7 @@ static void panthor_mmu_irq_handler(struct panthor_irq *pirq, u32 status)
ptdev->mmu->as.slots[as].vm->unhandled_fault = true;
/* Disable the MMU to kill jobs on this AS. */
- panthor_mmu_as_disable(ptdev, as, false);
+ panthor_mmu_as_disable(ptdev, as);
mutex_unlock(&ptdev->mmu->as.slots_lock);
status &= ~mask;
@@ -1943,7 +1940,7 @@ void panthor_mmu_suspend(struct panthor_device *ptdev)
if (vm) {
drm_WARN_ON(&ptdev->base,
- panthor_mmu_as_disable(ptdev, i, false));
+ panthor_mmu_as_disable(ptdev, i));
panthor_vm_release_as_locked(vm);
}
}
@@ -2068,7 +2065,7 @@ static void panthor_vm_free(struct drm_gpuvm *gpuvm)
int cookie;
if (drm_dev_enter(&ptdev->base, &cookie)) {
- panthor_mmu_as_disable(ptdev, vm->as.id, false);
+ panthor_mmu_as_disable(ptdev, vm->as.id);
drm_dev_exit(cookie);
}
@@ -3362,7 +3359,7 @@ void panthor_mmu_unplug(struct panthor_device *ptdev)
if (vm) {
drm_WARN_ON(&ptdev->base,
- panthor_mmu_as_disable(ptdev, i, false));
+ panthor_mmu_as_disable(ptdev, i));
panthor_vm_release_as_locked(vm);
}
}