drm fixes for 7.4-rc1

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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEEKbZHaGwW9KfbeusDHTzWXnEhr4FAmrlDS0ACgkQDHTzWXnE
 hr4DCA/+JjpK3jg/31mkZ8JcKoNUbHxaTMnfxysw7s6kFpAMGXDez3BEdO2lSXvX
 bXo3uf8ibEtMnAYXgp5R056BDg9/SzSXq/xR7THPGxLA75+myC+8rHClizhPFf7F
 G8TZRll2ybDVw4GeMf0jMhdFBLOE/xGh+duMgffvY33DX6C7RYnfrFFxtNpAQdW7
 8A7r97XvE0m7V+w3BeirydthmTwh3+DM/7fZ/qUXuf1kF81/7Vclt7k+TMPWLYBy
 sW5wYPzsomXNAfudZ5wX1euJAS1rZXnaLICCqqogYzOew9tow/cqdcopYvGOL4Hx
 zRrqhlAUzKlUN/2Q9JEO14a/2O9+Xtw1/+objwcsZSSzH18L6gbkvt4bcwZ4H03+
 PBuzQ7xDQOZkIZ/KoMkm+OnOUx0cSoQOJftYIUxho0F9QH4GF/Ydk3DZ2Gk2o79D
 Bw+SMfh30Xt5PGGhpKJHkra82RbRto+bRYrnm9t4xv0PivkztIdwXcl1VALjTRl9
 mSZ4kGc2PwS7lwA+qfrdKfPicnuwGbLCca9RwBZq7PQ8onSDhcnrG0R5hFnvZcvP
 0xqAo32obMM0ZIk9krTzs4/dKEDSfmQq7T3Gv0S+jLl5vOHnTZnApP/Ja1WeM335
 2wifZCScnw11TDuWRlEWobQsKk4BYfXRNYzPOdt4t4DbFC71Yos=
 =l+2D
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2026-10-30 13:48:47 -10:00
commit b92f6e90cf
68 changed files with 1607 additions and 468 deletions

View File

@ -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;

View File

@ -768,9 +768,8 @@ int amdgpu_amdkfd_submit_ib(struct amdgpu_device *adev,
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) &&

View File

@ -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)

View File

@ -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;

View File

@ -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 @@ int amdgpu_amdkfd_criu_resume(void *p)
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 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work)
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));

View File

@ -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));

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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))

View File

@ -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]);

View File

@ -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;
}

View File

@ -427,7 +427,7 @@ int amdgpu_vm_pt_clear(struct amdgpu_device *adev, struct amdgpu_vm *vm,
if (r)
goto exit;
r = vm->update_funcs->commit(&params, NULL);
r = vm->update_funcs->commit(&params, &vm->last_update);
exit:
drm_dev_exit(idx);
return r;
@ -531,13 +531,16 @@ static int amdgpu_vm_pt_alloc(struct amdgpu_vm_update_params *p,
r2 = amdgpu_vm_begin_critical(p);
if (r)
return r;
if (r2)
goto error_free_pt;
pt_bo = &pt->bo;
if (r2) {
r = r2;
goto error_unpin;
}
/* Keep a reference to the root directory to avoid
* freeing them up in the wrong order.
*/
pt_bo = &pt->bo;
pt_bo->parent = amdgpu_bo_ref(cursor->parent->bo);
amdgpu_vm_bo_base_init(entry, p->vm, pt_bo);
r = amdgpu_vm_pt_clear(p->adev, p->vm, pt, p->immediate);
@ -549,7 +552,6 @@ static int amdgpu_vm_pt_alloc(struct amdgpu_vm_update_params *p,
error_unpin:
if (p->vm->is_npa)
amdgpu_bo_unpin(pt_bo);
error_free_pt:
amdgpu_bo_unref(&pt_bo);
return r;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;
}

View File

@ -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 &&

View File

@ -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;
}

View File

@ -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 */

View File

@ -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();
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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;

View File

@ -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);

View File

@ -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 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
* 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)

View File

@ -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);

View File

@ -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 @@ static void svm_range_restore_work(struct work_struct *work)
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;

View File

@ -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);

View File

@ -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;

View File

@ -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.
*/

View File

@ -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),

View File

@ -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 {

View File

@ -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;
}

View File

@ -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];

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -2211,8 +2211,10 @@ static ssize_t amdgpu_get_gpuboard_temp_metrics(struct device *dev,
/**
* 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);

View File

@ -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

View File

@ -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,

View File

@ -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,
};

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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;
};

View File

@ -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 @@ static bool smu_v13_0_0_is_od_feature_supported(struct smu_context *smu,
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;

View File

@ -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 @@ static bool smu_v13_0_7_is_od_feature_supported(struct smu_context *smu,
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;

View File

@ -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 @@ static bool smu_v14_0_2_is_od_feature_supported(struct smu_context *smu,
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;

View File

@ -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 int smu_v15_0_8_get_npm_data(struct smu_context *smu,
enum amd_pp_sensors sensor,
uint32_t *value)
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,

View File

@ -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;

View File

@ -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.

View File

@ -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);

View File

@ -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);
}
}