summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-10-29 14:41:47 +1000
committerDave Airlie <airlied@redhat.com>2026-10-29 16:01:40 +1000
commitf94ed3d7cba3c83eef50b1a96d630d63cf54f69c (patch)
tree97e14658229f9e3f2306dc49d15e32e7acb0aaf1
parent460635aa3d5ddba65ee147e9f864a17d6b2665b1 (diff)
parentfbb201890156e04b062c4488d1b500de52367684 (diff)
Merge tag 'amd-drm-next-fixes-7.4-2026-10-28' of https://gitlab.freedesktop.org/drm/amdgpu/kernel into drm-next
amd-drm-next-fixes-7.4-2026-10-28: amdgpu: - 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: - Bounds checking fixes - Hotplug fixes - SVM fixes - CRIU fixes - GFX12 fix Signed-off-by: Dave Airlie <airlied@redhat.com> From: Alex Deucher <alexander.deucher@amd.com> Link: https://patch.msgid.link/20261028214423.1295475-1-alexander.deucher@amd.com
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c18
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c6
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_device.c5
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_job.c24
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c3
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c3
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_chardev.c15
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_crat.c35
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_device.c9
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h9
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c2
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c30
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c60
-rw-r--r--drivers/gpu/drm/amd/amdkfd/kfd_svm.c21
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c15
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c9
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c22
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c17
-rw-r--r--drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c2
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c193
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c193
-rw-r--r--drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c283
28 files changed, 734 insertions, 255 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
index 3391e8c77f13..53dde87f97f5 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c
@@ -768,9 +768,8 @@ err:
void amdgpu_amdkfd_set_compute_idle(struct amdgpu_device *adev, bool idle)
{
enum amd_powergating_state state = idle ? AMD_PG_STATE_GATE : AMD_PG_STATE_UNGATE;
- if ((IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 11 &&
- ((adev->mes.kiq_version & AMDGPU_MES_VERSION_MASK) <= 64)) ||
- (IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 12)) {
+ if (IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 11 &&
+ ((adev->mes.kiq_version & AMDGPU_MES_VERSION_MASK) <= 64)) {
pr_debug("GFXOFF is %s\n", idle ? "enabled" : "disabled");
amdgpu_gfx_off_ctrl(adev, idle);
} else if ((IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)) == 9) &&
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
index ff77e8ae9fe6..3a4b3424b5b2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c
@@ -2854,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;
@@ -2873,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
@@ -2889,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;
}
@@ -2911,8 +2917,10 @@ unlock_notifier_out:
unlock_out:
mutex_unlock(&process_info->lock);
- /* If validation failed, reschedule another attempt */
- if (evicted_bos) {
+ /* If validation failed, reschedule another attempt, unless the
+ * device is gone for good
+ */
+ if (evicted_bos && ret != -ENODEV) {
queue_delayed_work(system_freezable_wq,
&process_info->restore_userptr_work,
msecs_to_jiffies(AMDGPU_USERPTR_RESTORE_DELAY_MS));
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
index 65e13ad445bb..70bc53923d11 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c
@@ -218,6 +218,12 @@ static int amdgpu_cs_pass1(struct amdgpu_cs_parser *p,
size = p->chunks[i].length_dw;
+ if (size > INT_MAX / sizeof(uint32_t)) {
+ ret = -EINVAL;
+ i--;
+ goto free_partial_kdata;
+ }
+
p->chunks[i].kdata = vmemdup_array_user(u64_to_user_ptr(user_chunk.chunk_data),
size,
sizeof(uint32_t));
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
index f147c0c459da..c65bc575a57a 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
@@ -4331,8 +4331,11 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev)
* before ip_fini_early to prevent kfd locking refcount issues by calling
* amdgpu_amdkfd_suspend()
*/
- if (pci_dev_is_disconnected(adev->pdev))
+ if (pci_dev_is_disconnected(adev->pdev)) {
+ /* Drain KFD processes while kfd.dev is still valid */
+ amdgpu_amdkfd_teardown_processes(adev);
amdgpu_amdkfd_device_fini_sw(adev);
+ }
amdgpu_coredump_fini(adev);
amdgpu_device_ip_fini_early(adev);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
index 9cc210198fa4..8689a3c933b2 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c
@@ -313,22 +313,7 @@ void amdgpu_job_free_resources(struct amdgpu_job *job)
static void amdgpu_job_free_cb(struct drm_sched_job *s_job)
{
- struct amdgpu_job *job = to_amdgpu_job(s_job);
-
- drm_sched_job_cleanup(s_job);
-
- amdgpu_sync_free(&job->explicit_sync);
-
- if (dma_fence_was_initialized(&job->hw_fence->base))
- dma_fence_put(&job->hw_fence->base);
- else
- kfree(job->hw_fence);
- if (dma_fence_was_initialized(&job->hw_vm_fence->base))
- dma_fence_put(&job->hw_vm_fence->base);
- else
- kfree(job->hw_vm_fence);
-
- kfree(job);
+ amdgpu_job_free(to_amdgpu_job(s_job));
}
void amdgpu_job_set_gang_leader(struct amdgpu_job *job,
@@ -349,13 +334,14 @@ void amdgpu_job_set_gang_leader(struct amdgpu_job *job,
void amdgpu_job_free(struct amdgpu_job *job)
{
- if (job->base.entity)
+ if (job->gang_submit != &job->base.s_fence->scheduled)
+ dma_fence_put(job->gang_submit);
+
+ if (job->base.s_fence)
drm_sched_job_cleanup(&job->base);
amdgpu_job_free_resources(job);
amdgpu_sync_free(&job->explicit_sync);
- if (job->gang_submit != &job->base.s_fence->scheduled)
- dma_fence_put(job->gang_submit);
if (dma_fence_was_initialized(&job->hw_fence->base))
dma_fence_put(&job->hw_fence->base);
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
index ecd7caa95d4b..cd35ff7ca864 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c
@@ -377,7 +377,8 @@ int amdgpu_uvd_sw_fini(struct amdgpu_device *adev)
void *addr = amdgpu_bo_kptr(adev->uvd.ib_bo);
int i, j;
- drm_sched_entity_destroy(&adev->uvd.entity);
+ if (adev->uvd.entity.rq)
+ drm_sched_entity_destroy(&adev->uvd.entity);
for (j = 0; j < adev->uvd.num_uvd_inst; ++j) {
if (adev->uvd.harvest_config & (1 << j))
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
index 1f7a98cdce5a..3952af9ae115 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c
@@ -253,7 +253,8 @@ int amdgpu_vce_sw_fini(struct amdgpu_device *adev)
if (adev->vce.vcpu_bo == NULL)
return 0;
- drm_sched_entity_destroy(&adev->vce.entity);
+ if (adev->vce.entity.rq)
+ drm_sched_entity_destroy(&adev->vce.entity);
for (i = 0; i < adev->vce.num_rings; i++)
amdgpu_ring_fini(&adev->vce.ring[i]);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
index cad60ccdb216..d65ae53c81a5 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c
@@ -2876,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);
@@ -2886,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));
@@ -2905,7 +2910,7 @@ static int criu_process_info(struct file *filep,
args->priv_data_size);
err_unlock:
- if (ret) {
+ if (ret && paused_here) {
kfd_process_restore_queues(p);
p->queues_paused = false;
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
index 6e0df685503d..32d7573dffc9 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c
@@ -2056,27 +2056,33 @@ static void kfd_find_numa_node_in_srat(struct kfd_node *kdev)
return;
}
+ if (table_header->length < sizeof(struct acpi_table_srat))
+ goto invalid_srat;
+
table_end = (unsigned long)table_header + table_header->length;
/* Parse all entries looking for a match. */
sub_header = (struct acpi_subtable_header *)
((unsigned long)table_header +
sizeof(struct acpi_table_srat));
- subtable_len = sub_header->length;
- while (((unsigned long)sub_header) + subtable_len < table_end) {
- /*
- * If length is 0, break from this loop to avoid
- * infinite loop.
- */
- if (subtable_len == 0) {
- pr_err("SRAT invalid zero length\n");
- break;
- }
+ while ((unsigned long)sub_header < table_end) {
+ unsigned long start = (unsigned long)sub_header;
+
+ if (table_end - start < sizeof(*sub_header))
+ goto invalid_srat;
+
+ subtable_len = sub_header->length;
+ if (subtable_len < sizeof(*sub_header) ||
+ subtable_len > table_end - start)
+ goto invalid_srat;
switch (sub_header->type) {
case ACPI_SRAT_TYPE_CPU_AFFINITY:
cpu = (struct acpi_srat_cpu_affinity *)sub_header;
+ if (subtable_len < sizeof(*cpu))
+ goto invalid_srat;
+
pxm = *((u32 *)cpu->proximity_domain_hi) << 8 |
cpu->proximity_domain_lo;
if (pxm > max_pxm)
@@ -2084,6 +2090,9 @@ static void kfd_find_numa_node_in_srat(struct kfd_node *kdev)
break;
case ACPI_SRAT_TYPE_GENERIC_AFFINITY:
gpu = (struct acpi_srat_generic_affinity *)sub_header;
+ if (subtable_len < sizeof(*gpu))
+ goto invalid_srat;
+
bdf = *((u16 *)(&gpu->device_handle[0])) << 16 |
*((u16 *)(&gpu->device_handle[2]));
if (bdf == pci_id) {
@@ -2100,9 +2109,13 @@ static void kfd_find_numa_node_in_srat(struct kfd_node *kdev)
sub_header = (struct acpi_subtable_header *)
((unsigned long)sub_header + subtable_len);
- subtable_len = sub_header->length;
}
+ goto out;
+
+invalid_srat:
+ pr_err("SRAT contains an invalid subtable\n");
+out:
acpi_put_table(table_header);
/* Workaround bad cpu-gpu binding case */
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
index bb4c6695f3cf..0f96c9e3fa4d 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
@@ -1814,7 +1814,14 @@ void kgd2kfd_teardown_processes(struct amdgpu_device *adev)
mutex_unlock(&kfd_processes_mutex);
- /* wait all kfd processes use adev terminate */
+ /* kfd.dev may already be freed on surprise removal */
+ if (!adev->kfd.dev)
+ return;
+
+ /*
+ * Wait for all KFD processes using adev to terminate. Must be
+ * unbounded: device state is freed once they are gone.
+ */
while (!!atomic_read(&adev->kfd.dev->kfd_processes_count))
cond_resched();
}
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
index 38b46b696243..d63f7e9fae30 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h
@@ -138,6 +138,15 @@ struct mqd_manager {
uint32_t ctl_stack_size;
};
+/* Keep offset <= size <= wave state offset, as init_mqd() and CP do */
+static inline void kfd_mqd_restore_ctl_stack(u32 *size, u32 *offset, u32 *wg_offset,
+ u32 ctl_stack_size)
+{
+ *size = ctl_stack_size;
+ *offset = min(*offset, ctl_stack_size);
+ *wg_offset = max(*wg_offset, ctl_stack_size);
+}
+
struct mqd_user_context_save_area_header {
/* Byte offset from start of user context
* save area to the last saved top (lowest
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c
index 4f8a8a1a6186..6c9bafa3962e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c
@@ -305,6 +305,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
addr = mqd_mem_obj->gpu_addr;
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
*mqd = m;
if (gart_addr)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c
index bf015dc5b868..5709949de2eb 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c
@@ -340,6 +340,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
addr = mqd_mem_obj->gpu_addr;
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
*mqd = m;
if (gart_addr)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c
index 6ea09b031caf..aea80266d254 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c
@@ -393,6 +393,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size));
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
/* Update MQD base address to the newly allocated location */
m->cp_mqd_base_addr_lo = lower_32_bits(addr);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c
index d508ac5a18f8..33f0ecbe6b0a 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c
@@ -690,6 +690,8 @@ static void restore_mqd_v12_1(struct mqd_manager *mm, void **mqd,
memset(m, 0, AMDGPU_MQD_SIZE_ALIGN(mm->mqd_size) *
NUM_XCC(mm->dev->xcc_mask));
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
/* Update MQD base address to the newly allocated location */
m->cp_mqd_base_addr_lo = lower_32_bits(addr);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
index 6e6bc1ec0b64..fca43017ee7e 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c
@@ -462,6 +462,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
addr = mqd_mem_obj->gpu_addr;
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
*mqd = m;
if (gart_addr)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c
index 029572548c14..5ead0d5613b6 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c
@@ -317,6 +317,8 @@ static void restore_mqd(struct mqd_manager *mm, void **mqd,
addr = mqd_mem_obj->gpu_addr;
memcpy(m, mqd_src, sizeof(*m));
+ kfd_mqd_restore_ctl_stack(&m->cp_hqd_cntl_stack_size, &m->cp_hqd_cntl_stack_offset,
+ &m->cp_hqd_wg_state_offset, qp->ctl_stack_size);
*mqd = m;
if (gart_addr)
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
index a15a826519ad..78ab759ad854 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c
@@ -26,6 +26,7 @@
#include <linux/list.h>
#include "kfd_device_queue_manager.h"
#include "kfd_priv.h"
+#include "kfd_topology.h"
#include "kfd_kernel_queue.h"
#include "amdgpu_amdkfd.h"
#include "amdgpu_reset.h"
@@ -983,7 +984,7 @@ int kfd_criu_checkpoint_queues(struct kfd_process *p,
}
static void set_queue_properties_from_criu(struct queue_properties *qp,
- struct kfd_criu_queue_priv_data *q_data, uint32_t num_xcc)
+ struct kfd_criu_queue_priv_data *q_data)
{
qp->is_interop = false;
qp->queue_percent = q_data->q_percent;
@@ -996,11 +997,6 @@ static void set_queue_properties_from_criu(struct queue_properties *qp,
qp->eop_ring_buffer_size = q_data->eop_ring_buffer_size;
qp->ctx_save_restore_area_address = q_data->ctx_save_restore_area_address;
qp->ctx_save_restore_area_size = q_data->ctx_save_restore_area_size;
- if (q_data->type == KFD_QUEUE_TYPE_COMPUTE)
- qp->ctl_stack_size = q_data->ctl_stack_size / num_xcc;
- else
- qp->ctl_stack_size = q_data->ctl_stack_size;
-
qp->type = q_data->type;
qp->format = q_data->format;
}
@@ -1012,6 +1008,7 @@ int kfd_criu_restore_queue(struct kfd_process *p,
{
uint8_t *mqd, *ctl_stack, *q_extra_data = NULL;
struct kfd_criu_queue_priv_data *q_data;
+ struct kfd_topology_device *topo_dev;
struct kfd_process_device *pdd;
uint64_t q_extra_data_size;
struct queue_properties qp;
@@ -1048,6 +1045,24 @@ int kfd_criu_restore_queue(struct kfd_process *p,
goto exit;
}
+ memset(&qp, 0, sizeof(qp));
+ set_queue_properties_from_criu(&qp, q_data);
+
+ /* Per-XCC control stack size from topology, as create-queue requires */
+ if (qp.type == KFD_QUEUE_TYPE_COMPUTE && pdd->dev->kfd->cwsr_enabled) {
+ topo_dev = kfd_topology_device_by_id(pdd->dev->id);
+ if (!topo_dev) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ qp.ctl_stack_size = topo_dev->node_props.ctl_stack_size;
+ }
+
+ if (q_data->ctl_stack_size > (u64)qp.ctl_stack_size * NUM_XCC(pdd->dev->xcc_mask)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
*priv_data_offset += sizeof(*q_data);
q_extra_data_size = (uint64_t)q_data->ctl_stack_size + q_data->mqd_size;
@@ -1077,9 +1092,6 @@ int kfd_criu_restore_queue(struct kfd_process *p,
mqd = q_extra_data;
ctl_stack = mqd + q_data->mqd_size;
- memset(&qp, 0, sizeof(qp));
- set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->xcc_mask));
-
print_queue_properties(&qp);
ret = pqm_create_queue(&p->pqm, pdd->dev, &qp, &queue_id, q_data, mqd, ctl_stack, NULL);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
index 6a7b4d959541..c87370a9f5f0 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c
@@ -40,7 +40,7 @@ struct kfd_smi_client {
struct kfd_node *dev;
spinlock_t lock;
struct rcu_head rcu;
- pid_t pid;
+ pid_t tgid;
bool suser;
};
@@ -160,18 +160,18 @@ static int kfd_smi_ev_release(struct inode *inode, struct file *filep)
return 0;
}
-static bool kfd_smi_ev_enabled(pid_t pid, struct kfd_smi_client *client,
+static bool kfd_smi_ev_enabled(pid_t tgid, struct kfd_smi_client *client,
unsigned int event)
{
uint64_t events = READ_ONCE(client->events);
- if (pid && client->pid != pid && !client->suser)
+ if (tgid && client->tgid != tgid && !client->suser)
return false;
return events & KFD_SMI_EVENT_MASK_FROM_INDEX(event);
}
-static void add_event_to_kfifo(pid_t pid, struct kfd_node *dev,
+static void add_event_to_kfifo(pid_t tgid, struct kfd_node *dev,
unsigned int smi_event, char *event_msg, int len)
{
struct kfd_smi_client *client;
@@ -179,7 +179,7 @@ static void add_event_to_kfifo(pid_t pid, struct kfd_node *dev,
rcu_read_lock();
list_for_each_entry_rcu(client, &dev->smi_clients, list) {
- if (!kfd_smi_ev_enabled(pid, client, smi_event))
+ if (!kfd_smi_ev_enabled(tgid, client, smi_event))
continue;
spin_lock(&client->lock);
if (kfifo_avail(&client->fifo) >= len) {
@@ -210,27 +210,33 @@ static inline pid_t kfd_smi_task_to_pid(struct task_struct *task)
return task ? task_tgid_nr_ns(task, task_active_pid_ns(task)) : 0;
}
+/*
+ * Clients are matched by global TGID, which is unique across PID namespaces
+ * and stays valid after the task exited. Events show kfd_smi_task_to_pid().
+ */
+static inline pid_t kfd_smi_task_tgid(struct task_struct *task)
+{
+ return task ? task_tgid_nr(task) : 0;
+}
+
__printf(4, 5)
-static void kfd_smi_event_add(struct task_struct *task, struct kfd_node *dev,
+static void kfd_smi_event_add(pid_t tgid, struct kfd_node *dev,
unsigned int event, char *fmt, ...)
{
char fifo_in[KFD_SMI_EVENT_MSG_SIZE];
int len;
va_list args;
- pid_t pid;
if (list_empty(&dev->smi_clients))
return;
- pid = kfd_smi_task_to_pid(task);
-
len = scnprintf(fifo_in, sizeof(fifo_in), "%x ", event);
va_start(args, fmt);
len += vscnprintf(fifo_in + len, sizeof(fifo_in) - len, fmt, args);
va_end(args);
- add_event_to_kfifo(pid, dev, event, fifo_in, len);
+ add_event_to_kfifo(tgid, dev, event, fifo_in, len);
}
void kfd_smi_event_update_gpu_reset(struct kfd_node *dev, bool post_reset,
@@ -252,14 +258,14 @@ void kfd_smi_event_update_gpu_reset(struct kfd_node *dev, bool post_reset,
amdgpu_reset_get_desc(reset_context, reset_cause,
sizeof(reset_cause));
- kfd_smi_event_add(NULL, dev, event, KFD_EVENT_FMT_UPDATE_GPU_RESET(
+ kfd_smi_event_add(0, dev, event, KFD_EVENT_FMT_UPDATE_GPU_RESET(
dev->reset_seq_num, reset_cause));
}
void kfd_smi_event_update_thermal_throttling(struct kfd_node *dev,
uint64_t throttle_bitmask)
{
- kfd_smi_event_add(NULL, dev, KFD_SMI_EVENT_THERMAL_THROTTLE,
+ kfd_smi_event_add(0, dev, KFD_SMI_EVENT_THERMAL_THROTTLE,
KFD_EVENT_FMT_THERMAL_THROTTLING(
throttle_bitmask,
amdgpu_dpm_get_thermal_throttling_counter(dev->adev)));
@@ -271,10 +277,14 @@ void kfd_smi_event_update_vmfault(struct kfd_node *dev, uint16_t pasid)
task_info = amdgpu_vm_get_task_info_pasid(dev->adev, pasid);
if (task_info) {
- /* Report VM faults from user applications, not retry from kernel */
+ /*
+ * Report VM faults from user applications, not retry from
+ * kernel, and only to clients allowed to see that process.
+ */
if (task_info->task.pid)
- kfd_smi_event_add(NULL, dev, KFD_SMI_EVENT_VMFAULT, KFD_EVENT_FMT_VMFAULT(
- task_info->task.pid, task_info->task.comm));
+ kfd_smi_event_add(task_info->tgid, dev, KFD_SMI_EVENT_VMFAULT,
+ KFD_EVENT_FMT_VMFAULT(task_info->task.pid,
+ task_info->task.comm));
amdgpu_vm_put_task_info(task_info);
}
}
@@ -283,7 +293,7 @@ void kfd_smi_event_page_fault_start(struct kfd_node *node, struct task_struct *t
unsigned long address, bool write_fault,
ktime_t ts)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_PAGE_FAULT_START,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_PAGE_FAULT_START,
KFD_EVENT_FMT_PAGEFAULT_START(ktime_to_ns(ts),
kfd_smi_task_to_pid(task), address, node->id,
write_fault ? 'W' : 'R'));
@@ -292,7 +302,7 @@ void kfd_smi_event_page_fault_start(struct kfd_node *node, struct task_struct *t
void kfd_smi_event_page_fault_end(struct kfd_node *node, struct task_struct *task,
unsigned long address, bool migration)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_PAGE_FAULT_END,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_PAGE_FAULT_END,
KFD_EVENT_FMT_PAGEFAULT_END(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), address, node->id,
migration ? 'M' : 'U'));
@@ -304,7 +314,7 @@ void kfd_smi_event_migration_start(struct kfd_node *node, struct task_struct *ta
uint32_t prefetch_loc, uint32_t preferred_loc,
uint32_t trigger)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_MIGRATE_START,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_MIGRATE_START,
KFD_EVENT_FMT_MIGRATE_START(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), start, end - start, from,
to, prefetch_loc, preferred_loc, trigger));
@@ -315,7 +325,7 @@ void kfd_smi_event_migration_end(struct kfd_node *node, struct task_struct *task
uint32_t from, uint32_t to, uint32_t trigger,
int error_code)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_MIGRATE_END,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_MIGRATE_END,
KFD_EVENT_FMT_MIGRATE_END(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), start, end - start, from,
to, trigger, error_code));
@@ -324,14 +334,14 @@ void kfd_smi_event_migration_end(struct kfd_node *node, struct task_struct *task
void kfd_smi_event_queue_eviction(struct kfd_node *node, struct task_struct *task,
uint32_t trigger)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_QUEUE_EVICTION,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_QUEUE_EVICTION,
KFD_EVENT_FMT_QUEUE_EVICTION(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), node->id, trigger));
}
void kfd_smi_event_queue_restore(struct kfd_node *node, struct task_struct *task)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_QUEUE_RESTORE,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_QUEUE_RESTORE,
KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), node->id, '0'));
}
@@ -348,7 +358,7 @@ void kfd_smi_event_queue_restore_rescheduled(struct mm_struct *mm)
for (i = 0; i < p->n_pdds; i++) {
struct kfd_process_device *pdd = p->pdds[i];
- kfd_smi_event_add(p->lead_thread, pdd->dev,
+ kfd_smi_event_add(kfd_smi_task_tgid(p->lead_thread), pdd->dev,
KFD_SMI_EVENT_QUEUE_RESTORE,
KFD_EVENT_FMT_QUEUE_RESTORE(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(p->lead_thread),
@@ -361,7 +371,7 @@ void kfd_smi_event_unmap_from_gpu(struct kfd_node *node, struct task_struct *tas
unsigned long address, unsigned long last,
uint32_t trigger)
{
- kfd_smi_event_add(task, node, KFD_SMI_EVENT_UNMAP_FROM_GPU,
+ kfd_smi_event_add(kfd_smi_task_tgid(task), node, KFD_SMI_EVENT_UNMAP_FROM_GPU,
KFD_EVENT_FMT_UNMAP_FROM_GPU(ktime_get_boottime_ns(),
kfd_smi_task_to_pid(task), address,
last - address + 1, node->id, trigger));
@@ -379,7 +389,7 @@ void kfd_smi_event_process(struct kfd_process_device *pdd, bool start)
task_info = amdgpu_vm_get_task_info_vm(avm);
if (task_info) {
- kfd_smi_event_add(NULL, pdd->dev,
+ kfd_smi_event_add(kfd_smi_task_tgid(pdd->process->lead_thread), pdd->dev,
start ? KFD_SMI_EVENT_PROCESS_START :
KFD_SMI_EVENT_PROCESS_END,
KFD_EVENT_FMT_PROCESS(task_info->task.pid,
@@ -408,7 +418,7 @@ int kfd_smi_event_open(struct kfd_node *dev, uint32_t *fd)
spin_lock_init(&client->lock);
client->events = 0;
client->dev = dev;
- client->pid = kfd_smi_task_to_pid(current);
+ client->tgid = kfd_smi_task_tgid(current);
client->suser = capable(CAP_SYS_ADMIN);
spin_lock(&dev->smi_lock);
diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
index 1f5d73f0ebce..ed5e3318877b 100644
--- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
+++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c
@@ -1020,9 +1020,12 @@ svm_range_split_pages(struct svm_range *new, struct svm_range *old,
uint64_t start, uint64_t last)
{
uint64_t npages = last - start + 1;
+ bool has_dma_addr = false;
int i, r;
for (i = 0; i < MAX_GPU_INSTANCE; i++) {
+ if (old->dma_addr[i])
+ has_dma_addr = true;
r = svm_range_split_array(&new->dma_addr[i], &old->dma_addr[i],
sizeof(*old->dma_addr[i]), old->start,
npages, new->start, new->npages,
@@ -1030,8 +1033,16 @@ svm_range_split_pages(struct svm_range *new, struct svm_range *old,
if (r)
return r;
}
- if (old->actual_loc)
+ if (old->actual_loc) {
+ /*
+ * Pages migrated by prefetch have no dma_addr until they are
+ * mapped on a GPU. A fully resident range still splits exactly,
+ * old->npages isn't adjusted yet here.
+ */
+ if (!has_dma_addr && old->vram_pages == old->npages)
+ new->vram_pages = new->npages;
old->vram_pages -= new->vram_pages;
+ }
return 0;
}
@@ -1963,7 +1974,7 @@ static void svm_range_restore_work(struct work_struct *work)
struct mm_struct *mm;
int evicted_ranges;
int invalid;
- int r;
+ int r = 0;
svms = container_of(dwork, struct svm_range_list, restore_work);
evicted_ranges = atomic_read(&svms->evicted_ranges);
@@ -2037,8 +2048,10 @@ out_reschedule:
mmap_write_unlock(mm);
mutex_unlock(&process_info->lock);
- /* If validation failed, reschedule another attempt */
- if (evicted_ranges) {
+ /* If validation failed, reschedule another attempt, unless the
+ * device is gone for good
+ */
+ if (evicted_ranges && r != -ENODEV) {
pr_debug("reschedule to restore svm range\n");
queue_delayed_work(system_freezable_wq, &svms->restore_work,
msecs_to_jiffies(AMDGPU_SVM_RANGE_RESTORE_DELAY_MS));
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 5b634ae07c0d..0ea45ab12119 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -2021,10 +2021,6 @@ static int dm_resume(struct amdgpu_ip_block *ip_block)
/* On resume we need to rewrite the MSTM control bits to enable MST*/
s3_handle_mst(ddev, false);
- /* Exit IPS before the detection loop's first AUX/DDC access. */
- scoped_guard(mutex, &dm->dc_lock)
- dc_exit_ips_for_hw_access(dm->dc);
-
/* Do detection*/
drm_connector_list_iter_begin(ddev, &iter);
drm_for_each_connector_iter(connector, &iter) {
@@ -4709,8 +4705,6 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_commit *state,
if (dm_old_crtc_state->stream)
remove_stream(adev, acrtc, dm_old_crtc_state->stream);
- pm_runtime_get_noresume(dev->dev);
-
acrtc->enabled = true;
acrtc->hw_mode = new_crtc_state->mode;
crtc->hwmode = new_crtc_state->mode;
@@ -5294,8 +5288,13 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state)
#ifdef CONFIG_DEBUG_FS
enum amdgpu_dm_pipe_crc_source cur_crc_src;
#endif
- /* Count number of newly disabled CRTCs for dropping PM refs later. */
- if (old_crtc_state->active && !new_crtc_state->active)
+ /*
+ * One runtime PM reference per active CRTC: take it when the
+ * CRTC turns on, count it when it turns off for dropping later.
+ */
+ if (!old_crtc_state->active && new_crtc_state->active)
+ pm_runtime_get_noresume(dev->dev);
+ else if (old_crtc_state->active && !new_crtc_state->active)
crtc_disable_count++;
dm_new_crtc_state = to_dm_crtc_state(new_crtc_state);
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
index 2b81396bd06e..b498fc5fbb14 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c
@@ -1289,10 +1289,10 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link,
dc_link_get_link_cap(aconnector->dc_link));
- if (aconnector->dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
- aconnector->dc_link->dpia_bw_alloc_config.bw_alloc_enabled) {
+ if (aconnector->dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) {
dsc_options.max_target_bpp_limit_override_x16 = 8 * 16;
- dsc_options.force_dsc_when_not_needed = true;
+ if (aconnector->dc_link->dpia_bw_alloc_config.bw_alloc_enabled)
+ dsc_options.force_dsc_when_not_needed = true;
}
/* Set DSC policy according to dsc_clock_en */
@@ -3780,10 +3780,9 @@ void amdgpu_dm_update_freesync_caps(struct drm_connector *connector,
parse_edid_displayid_vrr(connector, edid);
has_vsdb = get_amd_vsdb(amdgpu_dm_connector, &vsdb_info) != 0;
+ amdgpu_dm_connector->vsdb_info = vsdb_info;
if (has_vsdb) {
- amdgpu_dm_connector->vsdb_info = vsdb_info;
-
/* copy refresh rate info as long as VSDB advertises FreeSync */
if (vsdb_info.freesync_supported) {
amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz;
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index 6febc15ebc2b..41e52c54156e 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -1244,6 +1244,26 @@ void populate_hdmi_info_from_connector(bool enable_frl, struct drm_hdmi_info *hd
}
EXPORT_IF_KUNIT(populate_hdmi_info_from_connector);
+static void dm_helpers_wait_for_edid_ready(struct dc_link *link,
+ struct i2c_adapter *ddc)
+{
+ const unsigned int retry_count = 50;
+ const unsigned int retry_interval_ms = 20;
+ unsigned int retry;
+
+ if (!ddc || link->dpcd_caps.dongle_type !=
+ DISPLAY_DONGLE_DP_DVI_DONGLE)
+ return;
+
+ for (retry = 0; retry < retry_count; retry++) {
+ if (drm_probe_ddc(ddc))
+ return;
+
+ if (retry + 1 < retry_count)
+ msleep(retry_interval_ms);
+ }
+}
+
enum dc_edid_status dm_helpers_read_local_edid(
struct dc_context *ctx,
struct dc_link *link,
@@ -1268,6 +1288,8 @@ enum dc_edid_status dm_helpers_read_local_edid(
if (link->dc->hwss.prepare_ddc)
link->dc->hwss.prepare_ddc(link);
+ dm_helpers_wait_for_edid_ready(link, ddc);
+
/* some dongles read edid incorrectly the first time,
* do check sum and retry to make sure read correct edid.
*/
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index ee3f3df6bb8d..5697645ef7cc 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -1342,6 +1342,8 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state,
DRM_DEBUG_DRIVER("%s: MST_DSC Try to set up params from %d streams\n", __func__, dc_state->stream_count);
for (i = 0; i < dc_state->stream_count; i++) {
struct dc_dsc_policy dsc_policy = {0};
+ u32 min_target_bpp_x16;
+ u32 max_target_bpp_x16;
stream = dc_state->streams[i];
@@ -1380,7 +1382,15 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state,
dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
dc_link->dpia_bw_alloc_config.bw_alloc_enabled)
params[count].clock_force_enable = DSC_CLK_FORCE_ENABLE;
+
dc_dsc_get_policy_for_timing(params[count].timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link));
+ min_target_bpp_x16 = dsc_policy.min_target_bpp * 16;
+ max_target_bpp_x16 = dsc_policy.max_target_bpp * 16;
+ if (dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA) {
+ max_target_bpp_x16 = min_t(u32, max_target_bpp_x16, 8 * 16);
+ if (dc_link->dpia_bw_alloc_config.bw_alloc_enabled)
+ max_target_bpp_x16 = min_target_bpp_x16;
+ }
is_frl_endpoint_present = get_conv_frl_bw(aconnector, &frl_conv_bw_in_kbps, &frl_conv_dsc_bw_in_kbps);
if (stream->sink->dsc_caps.dsc_dec_caps.is_dsc_supported &&
is_frl_endpoint_present &&
@@ -1390,11 +1400,8 @@ static int compute_mst_dsc_configs_for_link(struct drm_atomic_commit *state,
if (!dc_dsc_compute_bandwidth_range(
stream->sink->ctx->dc->res_pool->dscs[0],
stream->sink->ctx->dc->debug.dsc_min_slice_height_override,
- dsc_policy.min_target_bpp * 16,
- (dc_link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA &&
- dc_link->dpia_bw_alloc_config.bw_alloc_enabled) ?
- dsc_policy.min_target_bpp * 16 :
- dsc_policy.max_target_bpp * 16,
+ min_target_bpp_x16,
+ max_target_bpp_x16,
&stream->sink->dsc_caps.dsc_dec_caps,
&stream->timing,
dc_link_get_highest_encoding_format(dc_link),
diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
index 2d43a35c4c42..7a288b352079 100644
--- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
+++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c
@@ -11052,6 +11052,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex
calculate_tdlut_setting_params->cursor_buffer_size = mode_lib->ip.cursor_buffer_size;
calculate_tdlut_setting_params->gpuvm_enable = display_cfg->gpuvm_enable;
calculate_tdlut_setting_params->gpuvm_page_size_kbytes = display_cfg->plane_descriptors[k].overrides.gpuvm_min_page_size_kbytes;
+ calculate_tdlut_setting_params->tdlut_mpc_width_flag = display_cfg->plane_descriptors[k].tdlut.tdlut_mpc_width_flag;
+ calculate_tdlut_setting_params->is_gfx11 = dml_get_gfx_version(display_cfg->plane_descriptors[k].surface.tiling);
// output
calculate_tdlut_setting_params->tdlut_pte_bytes_per_frame = &s->tdlut_pte_bytes_per_frame[k];
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
index 468aaeb79ffb..e80087e35e19 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c
@@ -63,6 +63,9 @@ static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min, int32_t *max);
static int smu_v13_0_0_init_ppt_limits(struct smu_context *smu);
+static int
+smu_v13_0_0_upload_overdrive_table(struct smu_context *smu,
+ OverDriveTableExternal_t *od_table);
static const struct smu_feature_bits smu_v13_0_0_dpm_features = {
.bits = {
@@ -1079,6 +1082,93 @@ out:
return feature_enabled;
}
+static u32
+smu_v13_0_0_filter_od_feature_mask(struct smu_context *smu, u32 feature_mask)
+{
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+ u32 supported_mask =
+ pptable->SkuTable.OverDriveLimitsBasicMax.FeatureCtrlMask;
+
+ return feature_mask & supported_mask;
+}
+
+static bool smu_v13_0_0_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return !memcmp(od->FanLinearPwmPoints, boot->FanLinearPwmPoints,
+ sizeof(od->FanLinearPwmPoints)) &&
+ !memcmp(od->FanLinearTempPoints, boot->FanLinearTempPoints,
+ sizeof(od->FanLinearTempPoints)) &&
+ od->FanMinimumPwm == boot->FanMinimumPwm &&
+ od->AcousticTargetRpmThreshold == boot->AcousticTargetRpmThreshold &&
+ od->AcousticLimitRpmThreshold == boot->AcousticLimitRpmThreshold &&
+ od->FanTargetTemperature == boot->FanTargetTemperature &&
+ od->FanMode == boot->FanMode;
+}
+
+static bool smu_v13_0_0_zero_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return od->FanZeroRpmEnable == boot->FanZeroRpmEnable &&
+ od->FanZeroRpmStopTemp == boot->FanZeroRpmStopTemp;
+}
+
+static u32 smu_v13_0_0_normalize_feature_mask(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot,
+ u32 feature_mask)
+{
+ if (od->GfxclkFmin == boot->GfxclkFmin &&
+ od->GfxclkFmax == boot->GfxclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFXCLK_BIT);
+ if (od->UclkFmin == boot->UclkFmin &&
+ od->UclkFmax == boot->UclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_UCLK_BIT);
+ if (!memcmp(od->VoltageOffsetPerZoneBoundary,
+ boot->VoltageOffsetPerZoneBoundary,
+ sizeof(od->VoltageOffsetPerZoneBoundary)))
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
+ if (smu_v13_0_0_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
+ if (smu_v13_0_0_zero_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ if (!od->Ppt)
+ feature_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+
+ return feature_mask;
+}
+
+static int
+smu_v13_0_0_upload_ppt_overdrive_table(struct smu_context *smu, s16 ppt)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ OverDriveTableExternal_t *od_table = table_context->overdrive_table;
+ OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ u32 next_mask = table_context->user_od_feature_mask;
+ int ret;
+
+ /* Do not commit OD settings staged after the last c operation. */
+ memcpy(&upload_table, user_od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.Ppt = ppt;
+ if (ppt)
+ next_mask |= BIT(PP_OD_FEATURE_PPT_BIT);
+ else
+ next_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+ upload_table.OverDriveTable.FeatureCtrlMask =
+ smu_v13_0_0_filter_od_feature_mask(smu, next_mask);
+
+ ret = smu_v13_0_0_upload_overdrive_table(smu, &upload_table);
+ if (ret)
+ return ret;
+
+ table_context->user_od_feature_mask =
+ upload_table.OverDriveTable.FeatureCtrlMask;
+ od_table->OverDriveTable.Ppt = ppt;
+ user_od_table->OverDriveTable.Ppt = ppt;
+
+ return 0;
+}
+
static void smu_v13_0_0_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min,
@@ -1514,41 +1604,30 @@ static int smu_v13_0_0_od_restore_table_single(struct smu_context *smu, long inp
boot_overdrive_table->OverDriveTable.FanLinearPwmPoints[i];
}
od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_LIMIT:
od_table->OverDriveTable.AcousticLimitRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_TARGET:
od_table->OverDriveTable.AcousticTargetRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticTargetRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
od_table->OverDriveTable.FanTargetTemperature =
boot_overdrive_table->OverDriveTable.FanTargetTemperature;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_MINIMUM_PWM:
od_table->OverDriveTable.FanMinimumPwm =
boot_overdrive_table->OverDriveTable.FanMinimumPwm;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_ENABLE:
od_table->OverDriveTable.FanZeroRpmEnable =
boot_overdrive_table->OverDriveTable.FanZeroRpmEnable;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_STOP_TEMP:
od_table->OverDriveTable.FanZeroRpmStopTemp =
boot_overdrive_table->OverDriveTable.FanZeroRpmStopTemp;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
default:
dev_info(adev->dev, "Invalid table index: %ld\n", input);
@@ -1566,9 +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;
+ u32 next_mask;
int i, ret = 0;
switch (type) {
@@ -1892,36 +1979,41 @@ static int smu_v13_0_0_od_edit_dpm_table(struct smu_context *smu,
} else {
memcpy(od_table,
table_context->boot_overdrive_table,
- sizeof(OverDriveTableExternal_t));
+ sizeof(OverDriveTableExternal_t));
od_table->OverDriveTable.FeatureCtrlMask = 0;
- table_context->user_od_feature_mask = 0;
}
fallthrough;
case PP_OD_COMMIT_DPM_TABLE:
- table_context->user_od_feature_mask =
- od_table->OverDriveTable.FeatureCtrlMask;
offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- 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;
}
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
+ table_context->user_od_feature_mask = next_mask;
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
+ 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;
@@ -2184,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,
@@ -2193,6 +2286,7 @@ 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) {
@@ -2231,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);
@@ -2243,13 +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 =
- table_context->user_od_feature_mask;
- res = smu_v13_0_0_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
+
+ 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;
}
@@ -3043,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)
@@ -3058,12 +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;
- table_context->user_od_feature_mask &=
- ~(1U << PP_OD_FEATURE_PPT_BIT);
- od_table->OverDriveTable.FeatureCtrlMask =
- table_context->user_od_feature_mask;
- ret = smu_v13_0_0_upload_overdrive_table(smu, od_table);
+ 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;
@@ -3075,11 +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;
- table_context->user_od_feature_mask |= 1U << PP_OD_FEATURE_PPT_BIT;
- od_table->OverDriveTable.FeatureCtrlMask =
- table_context->user_od_feature_mask;
- ret = smu_v13_0_0_upload_overdrive_table(smu, od_table);
+ ppt = (limit * 100) / msg_limit - 100;
+ ret = smu_v13_0_0_upload_ppt_overdrive_table(smu, ppt);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
return ret;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
index 51e419e6380c..8285d4a91589 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c
@@ -63,6 +63,9 @@ static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min, int32_t *max);
static int smu_v13_0_7_init_ppt_limits(struct smu_context *smu);
+static int
+smu_v13_0_7_upload_overdrive_table(struct smu_context *smu,
+ OverDriveTableExternal_t *od_table);
static const struct smu_feature_bits smu_v13_0_7_dpm_features = {
.bits = {
@@ -94,6 +97,7 @@ static const struct smu_feature_bits smu_v13_0_7_dpm_features = {
#define PP_OD_FEATURE_GFXCLK_FMAX 1
#define PP_OD_FEATURE_UCLK_FMIN 2
#define PP_OD_FEATURE_UCLK_FMAX 3
+
#define PP_OD_FEATURE_GFX_VF_CURVE 4
#define PP_OD_FEATURE_FAN_CURVE_TEMP 5
#define PP_OD_FEATURE_FAN_CURVE_PWM 6
@@ -1089,6 +1093,93 @@ out:
return feature_enabled;
}
+static u32
+smu_v13_0_7_filter_od_feature_mask(struct smu_context *smu, u32 feature_mask)
+{
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+ u32 supported_mask =
+ pptable->SkuTable.OverDriveLimitsBasicMax.FeatureCtrlMask;
+
+ return feature_mask & supported_mask;
+}
+
+static bool smu_v13_0_7_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return !memcmp(od->FanLinearPwmPoints, boot->FanLinearPwmPoints,
+ sizeof(od->FanLinearPwmPoints)) &&
+ !memcmp(od->FanLinearTempPoints, boot->FanLinearTempPoints,
+ sizeof(od->FanLinearTempPoints)) &&
+ od->FanMinimumPwm == boot->FanMinimumPwm &&
+ od->AcousticTargetRpmThreshold == boot->AcousticTargetRpmThreshold &&
+ od->AcousticLimitRpmThreshold == boot->AcousticLimitRpmThreshold &&
+ od->FanTargetTemperature == boot->FanTargetTemperature &&
+ od->FanMode == boot->FanMode;
+}
+
+static bool smu_v13_0_7_zero_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return od->FanZeroRpmEnable == boot->FanZeroRpmEnable &&
+ od->FanZeroRpmStopTemp == boot->FanZeroRpmStopTemp;
+}
+
+static u32 smu_v13_0_7_normalize_feature_mask(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot,
+ u32 feature_mask)
+{
+ if (od->GfxclkFmin == boot->GfxclkFmin &&
+ od->GfxclkFmax == boot->GfxclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFXCLK_BIT);
+ if (od->UclkFmin == boot->UclkFmin &&
+ od->UclkFmax == boot->UclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_UCLK_BIT);
+ if (!memcmp(od->VoltageOffsetPerZoneBoundary,
+ boot->VoltageOffsetPerZoneBoundary,
+ sizeof(od->VoltageOffsetPerZoneBoundary)))
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
+ if (smu_v13_0_7_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
+ if (smu_v13_0_7_zero_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ if (!od->Ppt)
+ feature_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+
+ return feature_mask;
+}
+
+static int
+smu_v13_0_7_upload_ppt_overdrive_table(struct smu_context *smu, s16 ppt)
+{
+ struct smu_table_context *table_context = &smu->smu_table;
+ OverDriveTableExternal_t *od_table = table_context->overdrive_table;
+ OverDriveTableExternal_t *user_od_table = table_context->user_overdrive_table;
+ OverDriveTableExternal_t upload_table;
+ u32 next_mask = table_context->user_od_feature_mask;
+ int ret;
+
+ /* Do not commit OD settings staged after the last c operation. */
+ memcpy(&upload_table, user_od_table, sizeof(upload_table));
+ upload_table.OverDriveTable.Ppt = ppt;
+ if (ppt)
+ next_mask |= BIT(PP_OD_FEATURE_PPT_BIT);
+ else
+ next_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+ upload_table.OverDriveTable.FeatureCtrlMask =
+ smu_v13_0_7_filter_od_feature_mask(smu, next_mask);
+
+ ret = smu_v13_0_7_upload_overdrive_table(smu, &upload_table);
+ if (ret)
+ return ret;
+
+ table_context->user_od_feature_mask =
+ upload_table.OverDriveTable.FeatureCtrlMask;
+ od_table->OverDriveTable.Ppt = ppt;
+ user_od_table->OverDriveTable.Ppt = ppt;
+
+ return 0;
+}
+
static void smu_v13_0_7_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min,
@@ -1523,41 +1614,30 @@ static int smu_v13_0_7_od_restore_table_single(struct smu_context *smu, long inp
boot_overdrive_table->OverDriveTable.FanLinearPwmPoints[i];
}
od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_LIMIT:
od_table->OverDriveTable.AcousticLimitRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticLimitRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_ACOUSTIC_TARGET:
od_table->OverDriveTable.AcousticTargetRpmThreshold =
boot_overdrive_table->OverDriveTable.AcousticTargetRpmThreshold;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_TARGET_TEMPERATURE:
od_table->OverDriveTable.FanTargetTemperature =
boot_overdrive_table->OverDriveTable.FanTargetTemperature;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_MINIMUM_PWM:
od_table->OverDriveTable.FanMinimumPwm =
boot_overdrive_table->OverDriveTable.FanMinimumPwm;
- od_table->OverDriveTable.FanMode = FAN_MODE_AUTO;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_ENABLE:
od_table->OverDriveTable.FanZeroRpmEnable =
boot_overdrive_table->OverDriveTable.FanZeroRpmEnable;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
case PP_OD_EDIT_FAN_ZERO_RPM_STOP_TEMP:
od_table->OverDriveTable.FanZeroRpmStopTemp =
boot_overdrive_table->OverDriveTable.FanZeroRpmStopTemp;
- od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
default:
dev_info(adev->dev, "Invalid table index: %ld\n", input);
@@ -1575,9 +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;
+ u32 next_mask;
int i, ret = 0;
switch (type) {
@@ -1903,35 +1991,40 @@ static int smu_v13_0_7_od_edit_dpm_table(struct smu_context *smu,
table_context->boot_overdrive_table,
sizeof(OverDriveTableExternal_t));
od_table->OverDriveTable.FeatureCtrlMask = 0;
- table_context->user_od_feature_mask = 0;
}
fallthrough;
case PP_OD_COMMIT_DPM_TABLE:
- table_context->user_od_feature_mask =
- od_table->OverDriveTable.FeatureCtrlMask;
offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- 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;
}
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
+ table_context->user_od_feature_mask = next_mask;
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
+ 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;
@@ -2186,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,
@@ -2195,6 +2289,7 @@ 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) {
@@ -2233,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);
@@ -2245,14 +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 =
- table_context->user_od_feature_mask;
- res = smu_v13_0_7_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
+ 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;
}
@@ -2664,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)
@@ -2679,13 +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;
- table_context->user_od_feature_mask &=
- ~(1U << PP_OD_FEATURE_PPT_BIT);
- od_table->OverDriveTable.FeatureCtrlMask =
- table_context->user_od_feature_mask;
-
- ret = smu_v13_0_7_upload_overdrive_table(smu, od_table);
+ 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;
@@ -2697,12 +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;
- table_context->user_od_feature_mask |= 1U << PP_OD_FEATURE_PPT_BIT;
- od_table->OverDriveTable.FeatureCtrlMask =
- table_context->user_od_feature_mask;
-
- ret = smu_v13_0_7_upload_overdrive_table(smu, od_table);
+ ppt = (limit * 100) / msg_limit - 100;
+ ret = smu_v13_0_7_upload_ppt_overdrive_table(smu, ppt);
if (ret) {
dev_err(smu->adev->dev, "Failed to upload overdrive table!\n");
return ret;
diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
index 549716349117..6a12d6f8c9dd 100644
--- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
+++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c
@@ -89,6 +89,11 @@ static const struct smu_feature_bits smu_v14_0_2_dpm_features = {
#define PP_OD_FEATURE_FAN_TARGET_TEMPERATURE 9
#define PP_OD_FEATURE_FAN_MINIMUM_PWM 10
#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),
@@ -962,6 +967,84 @@ out:
return feature_enabled;
}
+static u32
+smu_v14_0_2_filter_od_feature_mask(struct smu_context *smu, u32 feature_mask)
+{
+ PPTable_t *pptable = smu->smu_table.driver_pptable;
+ u32 supported_mask =
+ pptable->SkuTable.OverDriveLimitsBasicMax.FeatureCtrlMask;
+
+ return feature_mask & supported_mask;
+}
+
+static u32
+smu_v14_0_2_get_od_feature_mask(struct smu_context *smu, u32 pending_mask)
+{
+ u32 supported_mask = smu_v14_0_2_filter_od_feature_mask(smu, ~0U);
+ u32 fan_mask = supported_mask & SMU_14_0_2_OD_FAN_FEATURE_MASK;
+ u32 mask = smu->smu_table.user_od_feature_mask;
+
+ if (pending_mask & fan_mask)
+ mask &= ~fan_mask;
+ mask |= pending_mask;
+
+ return smu_v14_0_2_filter_od_feature_mask(smu, mask);
+}
+
+static bool smu_v14_0_2_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return !memcmp(od->FanLinearPwmPoints, boot->FanLinearPwmPoints,
+ sizeof(od->FanLinearPwmPoints)) &&
+ !memcmp(od->FanLinearTempPoints, boot->FanLinearTempPoints,
+ sizeof(od->FanLinearTempPoints)) &&
+ od->AcousticTargetRpmThreshold == boot->AcousticTargetRpmThreshold &&
+ od->AcousticLimitRpmThreshold == boot->AcousticLimitRpmThreshold &&
+ od->FanTargetTemperature == boot->FanTargetTemperature &&
+ od->FanMode == boot->FanMode;
+}
+
+static bool smu_v14_0_2_legacy_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return od->FanMinimumPwm == boot->FanMinimumPwm &&
+ od->FanMode == boot->FanMode;
+}
+
+static bool smu_v14_0_2_zero_fan_is_default(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot)
+{
+ return od->FanZeroRpmEnable == boot->FanZeroRpmEnable &&
+ od->FanZeroRpmStopTemp == boot->FanZeroRpmStopTemp;
+}
+
+static u32 smu_v14_0_2_normalize_feature_mask(const OverDriveTable_t *od,
+ const OverDriveTable_t *boot,
+ u32 feature_mask)
+{
+ if (od->GfxclkFoffset == boot->GfxclkFoffset)
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFXCLK_BIT);
+ if (od->UclkFmin == boot->UclkFmin &&
+ od->UclkFmax == boot->UclkFmax)
+ feature_mask &= ~BIT(PP_OD_FEATURE_UCLK_BIT);
+ if (!memcmp(od->VoltageOffsetPerZoneBoundary,
+ boot->VoltageOffsetPerZoneBoundary,
+ sizeof(od->VoltageOffsetPerZoneBoundary)))
+ feature_mask &= ~BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT);
+ if ((feature_mask & BIT(PP_OD_FEATURE_FAN_CURVE_BIT)) &&
+ smu_v14_0_2_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_FAN_CURVE_BIT);
+ if ((feature_mask & BIT(PP_OD_FEATURE_FAN_LEGACY_BIT)) &&
+ smu_v14_0_2_legacy_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_FAN_LEGACY_BIT);
+ if (smu_v14_0_2_zero_fan_is_default(od, boot))
+ feature_mask &= ~BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ if (!od->Ppt)
+ feature_mask &= ~BIT(PP_OD_FEATURE_PPT_BIT);
+
+ return feature_mask;
+}
+
static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,
int od_feature_bit,
int32_t *min,
@@ -1020,6 +1103,10 @@ static void smu_v14_0_2_get_od_setting_limits(struct smu_context *smu,
od_min_setting = overdrive_lowerlimits->FanZeroRpmEnable;
od_max_setting = overdrive_upperlimits->FanZeroRpmEnable;
break;
+ case PP_OD_FEATURE_FAN_ZERO_RPM_STOP_TEMP:
+ od_min_setting = pptable->SkuTable.MsgLimits.FanStopTempMin;
+ od_max_setting = pptable->SkuTable.MsgLimits.FanStopTempMax;
+ break;
default:
od_min_setting = od_max_setting = INT_MAX;
break;
@@ -1244,6 +1331,24 @@ static int smu_v14_0_2_emit_clk_levels(struct smu_context *smu,
min_value, max_value);
break;
+ case SMU_OD_FAN_ZERO_RPM_STOP_TEMP:
+ if (!smu_v14_0_2_is_od_feature_supported(smu,
+ PP_OD_FEATURE_ZERO_FAN_BIT))
+ break;
+
+ size += sysfs_emit_at(buf, size, "FAN_ZERO_RPM_STOP_TEMPERATURE:\n");
+ size += sysfs_emit_at(buf, size, "%d\n",
+ (int)od_table->OverDriveTable.FanZeroRpmStopTemp);
+
+ size += sysfs_emit_at(buf, size, "%s:\n", "OD_RANGE");
+ smu_v14_0_2_get_od_setting_limits(smu,
+ PP_OD_FEATURE_FAN_ZERO_RPM_STOP_TEMP,
+ &min_value,
+ &max_value);
+ size += sysfs_emit_at(buf, size, "ZERO_RPM_STOP_TEMPERATURE: %u %u\n",
+ min_value, max_value);
+ break;
+
case SMU_OD_RANGE:
if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_GFXCLK_BIT) &&
!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_UCLK_BIT) &&
@@ -2294,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;
@@ -2311,7 +2448,9 @@ static void smu_v14_0_2_set_supported_od_feature_mask(struct smu_context *smu)
OD_OPS_SUPPORT_FAN_MINIMUM_PWM_RETRIEVE |
OD_OPS_SUPPORT_FAN_MINIMUM_PWM_SET |
OD_OPS_SUPPORT_FAN_ZERO_RPM_ENABLE_RETRIEVE |
- OD_OPS_SUPPORT_FAN_ZERO_RPM_ENABLE_SET;
+ OD_OPS_SUPPORT_FAN_ZERO_RPM_ENABLE_SET |
+ OD_OPS_SUPPORT_FAN_ZERO_RPM_STOP_TEMP_RETRIEVE |
+ OD_OPS_SUPPORT_FAN_ZERO_RPM_STOP_TEMP_SET;
}
static int smu_v14_0_2_get_overdrive_table(struct smu_context *smu,
@@ -2350,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,
@@ -2359,6 +2499,7 @@ 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) {
@@ -2393,6 +2534,16 @@ static int smu_v14_0_2_set_default_od_settings(struct smu_context *smu)
user_od_table_bak.OverDriveTable.FanMinimumPwm;
user_od_table->OverDriveTable.FanZeroRpmEnable =
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);
@@ -2405,14 +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 =
- table_context->user_od_feature_mask;
- res = smu_v14_0_2_upload_overdrive_table(smu, user_od_table);
- user_od_table->OverDriveTable.FeatureCtrlMask = 0;
- if (res == 0)
+ 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;
}
@@ -2436,38 +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;
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);
@@ -2485,9 +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;
+ u32 next_mask;
int i, ret = 0;
switch (type) {
@@ -2659,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:
@@ -2681,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:
@@ -2703,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:
@@ -2749,6 +2911,27 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
break;
+ case PP_OD_EDIT_FAN_ZERO_RPM_STOP_TEMP:
+ if (!smu_v14_0_2_is_od_feature_supported(smu, PP_OD_FEATURE_ZERO_FAN_BIT)) {
+ dev_warn(adev->dev, "Zero RPM setting not supported!\n");
+ return -ENOTSUPP;
+ }
+
+ smu_v14_0_2_get_od_setting_limits(smu,
+ PP_OD_FEATURE_FAN_ZERO_RPM_STOP_TEMP,
+ &minimum,
+ &maximum);
+ if (input[0] < minimum ||
+ input[0] > maximum) {
+ dev_info(adev->dev, "zero RPM stop temperature setting(%ld) must be within [%d, %d]!\n",
+ input[0], minimum, maximum);
+ return -EINVAL;
+ }
+
+ od_table->OverDriveTable.FanZeroRpmStopTemp = input[0];
+ od_table->OverDriveTable.FeatureCtrlMask |= BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
+ break;
+
case PP_OD_RESTORE_DEFAULT_TABLE:
if (size == 1) {
ret = smu_v14_0_2_od_restore_table_single(smu, input[0]);
@@ -2759,44 +2942,38 @@ static int smu_v14_0_2_od_edit_dpm_table(struct smu_context *smu,
table_context->boot_overdrive_table,
sizeof(OverDriveTableExternal_t));
od_table->OverDriveTable.FeatureCtrlMask = 0;
- table_context->user_od_feature_mask = 0;
}
fallthrough;
case PP_OD_COMMIT_DPM_TABLE:
- /*
- * PMFW interprets FeatureCtrlMask as the complete set of OD
- * features that are currently overridden, not as a delta
- * describing this single operation. Any feature whose bit is
- * clear gets reset to its default on import. od_table's mask
- * has accumulated every feature the user touched since the
- * last full restore, so record it and keep restating it on
- * subsequent uploads.
- */
- table_context->user_od_feature_mask =
- od_table->OverDriveTable.FeatureCtrlMask;
-
offset_of_voltageoffset = offsetof(OverDriveTable_t, VoltageOffsetPerZoneBoundary);
- if (memcmp((u8 *)od_table + offset_of_voltageoffset,
- table_context->user_overdrive_table + offset_of_voltageoffset,
- 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;
}
- memcpy(table_context->user_overdrive_table + offset_of_voltageoffset,
+ table_context->user_od_feature_mask = next_mask;
+ od_table->OverDriveTable.FeatureCtrlMask = 0;
+ 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;
@@ -2814,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)
@@ -2829,13 +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;
- table_context->user_od_feature_mask &=
- ~(1U << PP_OD_FEATURE_PPT_BIT);
- od_table->OverDriveTable.FeatureCtrlMask =
- table_context->user_od_feature_mask;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
+ 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;
@@ -2847,12 +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;
- table_context->user_od_feature_mask |= 1U << PP_OD_FEATURE_PPT_BIT;
- od_table->OverDriveTable.FeatureCtrlMask =
- table_context->user_od_feature_mask;
-
- ret = smu_v14_0_2_upload_overdrive_table(smu, od_table);
+ 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;