diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-30 13:48:47 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-30 13:48:47 -1000 |
| commit | b92f6e90cf056ec80bafb858d5749829a55ac75e (patch) | |
| tree | 8323bbba29b8ea88affcf1be7a3c67f91f0542ad /drivers/gpu | |
| parent | 51b4bf5fe7c90ebf08599b9a3c7620b4a3fd4b22 (diff) | |
| parent | f94ed3d7cba3c83eef50b1a96d630d63cf54f69c (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')
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(¶ms, NULL); + r = vm->update_funcs->commit(¶ms, &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); } } |
