amd-drm-next-7.2-2026-05-29:

amdgpu:
 - GEM_OP warning fix
 - GEM_OP locking fix
 - Userq fixes
 - DCN 2.1 refclk fix
 - SI fixes
 - HMM fixes
 - Add DC KUNIT tests
 - UML fixes
 - Switch to system_dfl_wq
 - Old DC power state cleanup
 - RAS fixes
 
 amdkfd:
 - svm_range_set_attr locking fix
 - CRIU restore fix
 - KFD debugger fix
 
 radeon:
 - Use struct drm_edid instead of struct edid
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQgO5Idg2tXNTSZAr293/aFa7yZ2AUCahoHWgAKCRC93/aFa7yZ
 2GLXAQCrU7fTQgRln2bqDXIx1V/KA85OSxDCzc8FGHoxtQny0AD+Lh/HeiZ2ISR1
 7Ydh1S2vhvnzf9QIlNWYYExyQvXyXgM=
 =ntZ8
 -----END PGP SIGNATURE-----

Merge tag 'amd-drm-next-7.2-2026-05-29' of https://gitlab.freedesktop.org/agd5f/linux into drm-next

amd-drm-next-7.2-2026-05-29:

amdgpu:
- GEM_OP warning fix
- GEM_OP locking fix
- Userq fixes
- DCN 2.1 refclk fix
- SI fixes
- HMM fixes
- Add DC KUNIT tests
- UML fixes
- Switch to system_dfl_wq
- Old DC power state cleanup
- RAS fixes

amdkfd:
- svm_range_set_attr locking fix
- CRIU restore fix
- KFD debugger fix

radeon:
- Use struct drm_edid instead of struct edid

Signed-off-by: Dave Airlie <airlied@redhat.com>

From: Alex Deucher <alexander.deucher@amd.com>
Link: https://patch.msgid.link/20260529214346.2328355-1-alexander.deucher@amd.com
This commit is contained in:
Dave Airlie 2026-06-03 16:41:52 +10:00
commit 9bb8af2770
80 changed files with 3307 additions and 955 deletions

View File

@ -586,7 +586,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
*/
adev->coredump = coredump;
/* Kick off coredump formatting to a worker thread. */
queue_work(system_unbound_wq, &adev->coredump_work);
queue_work(system_dfl_wq, &adev->coredump_work);
drm_info(dev, "AMDGPU device coredump file has been created\n");
drm_info(dev, "Check your /sys/class/drm/card%d/device/devcoredump/data\n",

View File

@ -1913,6 +1913,19 @@ static void amdgpu_uid_fini(struct amdgpu_device *adev)
adev->uid_info = NULL;
}
static struct pci_dev *amdgpu_device_find_parent(struct amdgpu_device *adev)
{
struct pci_dev *parent = adev->pdev;
/* skip upstream/downstream switches internal to dGPU */
while ((parent = pci_upstream_bridge(parent))) {
if (parent->vendor == PCI_VENDOR_ID_ATI)
continue;
}
return parent;
}
/**
* amdgpu_device_ip_early_init - run early init for hardware IPs
*
@ -5921,8 +5934,6 @@ static void amdgpu_device_partner_bandwidth(struct amdgpu_device *adev,
enum pci_bus_speed *speed,
enum pcie_link_width *width)
{
struct pci_dev *parent = adev->pdev;
if (!speed || !width)
return;
@ -5930,13 +5941,11 @@ static void amdgpu_device_partner_bandwidth(struct amdgpu_device *adev,
*width = PCIE_LNK_WIDTH_UNKNOWN;
if (amdgpu_device_pcie_dynamic_switching_supported(adev)) {
while ((parent = pci_upstream_bridge(parent))) {
/* skip upstream/downstream switches internal to dGPU*/
if (parent->vendor == PCI_VENDOR_ID_ATI)
continue;
struct pci_dev *parent = amdgpu_device_find_parent(adev);
if (parent) {
*speed = pcie_get_speed_cap(parent);
*width = pcie_get_width_cap(parent);
break;
}
} else {
/* use the current speeds rather than max if switching is not supported */
@ -5963,22 +5972,15 @@ static void amdgpu_device_gpu_bandwidth(struct amdgpu_device *adev,
if (!speed || !width)
return;
parent = pci_upstream_bridge(parent);
if (parent && parent->vendor == PCI_VENDOR_ID_ATI) {
/* use the upstream/downstream switches internal to dGPU */
/* use the device itself */
*speed = pcie_get_speed_cap(adev->pdev);
*width = pcie_get_width_cap(adev->pdev);
/* use the link outside the device */
parent = amdgpu_device_find_parent(adev);
if (parent) {
*speed = pcie_get_speed_cap(parent);
*width = pcie_get_width_cap(parent);
while ((parent = pci_upstream_bridge(parent))) {
if (parent->vendor == PCI_VENDOR_ID_ATI) {
/* use the upstream/downstream switches internal to dGPU */
*speed = pcie_get_speed_cap(parent);
*width = pcie_get_width_cap(parent);
}
}
} else {
/* use the device itself */
*speed = pcie_get_speed_cap(adev->pdev);
*width = pcie_get_width_cap(adev->pdev);
}
}

View File

@ -1094,9 +1094,16 @@ int amdgpu_gem_op_ioctl(struct drm_device *dev, void *data,
* If that number is larger than the size of the array, the ioctl must
* be retried.
*/
if (args->num_entries > INT_MAX / sizeof(*vm_entries)) {
r = -EINVAL;
goto out_exec;
}
vm_entries = kvcalloc(args->num_entries, sizeof(*vm_entries), GFP_KERNEL);
if (!vm_entries)
return -ENOMEM;
if (!vm_entries) {
r = -ENOMEM;
goto out_exec;
}
amdgpu_vm_bo_va_for_each_valid_mapping(bo_va, mapping) {
if (num_mappings < args->num_entries) {

View File

@ -51,8 +51,6 @@
#include "amdgpu_amdkfd.h"
#include "amdgpu_hmm.h"
#define MAX_WALK_BYTE (2UL << 30)
/**
* amdgpu_hmm_invalidate_gfx - callback to notify about mm change
*
@ -78,6 +76,7 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni,
mmu_interval_set_seq(mni, cur_seq);
amdgpu_vm_bo_invalidate(bo, false);
r = dma_resv_wait_timeout(bo->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP,
false, MAX_SCHEDULE_TIMEOUT);
mutex_unlock(&adev->notifier_lock);
@ -170,11 +169,13 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
void *owner,
struct amdgpu_hmm_range *range)
{
unsigned long end;
const u64 max_bytes = SZ_2G;
struct hmm_range *hmm_range = &range->hmm_range;
unsigned long timeout;
unsigned long *pfns;
int r = 0;
struct hmm_range *hmm_range = &range->hmm_range;
unsigned long end;
int r;
pfns = kvmalloc_array(npages, sizeof(*pfns), GFP_KERNEL);
if (unlikely(!pfns)) {
@ -191,8 +192,9 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
end = start + npages * PAGE_SIZE;
hmm_range->dev_private_owner = owner;
hmm_range->notifier_seq = mmu_interval_read_begin(notifier);
do {
hmm_range->end = min(hmm_range->start + MAX_WALK_BYTE, end);
hmm_range->end = min(hmm_range->start + max_bytes, end);
pr_debug("hmm range: start = 0x%lx, end = 0x%lx",
hmm_range->start, hmm_range->end);
@ -200,7 +202,6 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
timeout = jiffies + msecs_to_jiffies(HMM_RANGE_DEFAULT_TIMEOUT);
retry:
hmm_range->notifier_seq = mmu_interval_read_begin(notifier);
r = hmm_range_fault(hmm_range);
if (unlikely(r)) {
if (r == -EBUSY && !time_after(jiffies, timeout))
@ -210,7 +211,7 @@ int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier,
if (hmm_range->end == end)
break;
hmm_range->hmm_pfns += MAX_WALK_BYTE >> PAGE_SHIFT;
hmm_range->hmm_pfns += max_bytes >> PAGE_SHIFT;
hmm_range->start = hmm_range->end;
} while (hmm_range->end < end);

View File

@ -2280,7 +2280,8 @@ void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev)
list_for_each_entry(obj, &con->head, node) {
if (amdgpu_ras_is_supported(adev, obj->head.block) &&
(obj->attr_inuse == 1)) {
sprintf(fs_info.debugfs_name, "%s_err_inject",
snprintf(fs_info.debugfs_name, sizeof(fs_info.debugfs_name),
"%s_err_inject",
get_ras_block_str(&obj->head));
fs_info.head = obj->head;
amdgpu_ras_debugfs_create(adev, &fs_info, dir);

View File

@ -215,33 +215,15 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell)
xa_unlock_irqrestore(xa, flags);
}
static int amdgpu_userq_buffer_va_list_add(struct amdgpu_usermode_queue *queue,
struct amdgpu_bo_va_mapping *va_map, u64 addr)
{
struct amdgpu_userq_va_cursor *va_cursor;
struct userq_va_list;
va_cursor = kzalloc_obj(*va_cursor);
if (!va_cursor)
return -ENOMEM;
INIT_LIST_HEAD(&va_cursor->list);
va_cursor->gpu_addr = addr;
va_map->bo_va->userq_va_mapped = true;
list_add(&va_cursor->list, &queue->userq_va_list);
return 0;
}
int amdgpu_userq_input_va_validate(struct amdgpu_device *adev,
struct amdgpu_usermode_queue *queue,
u64 addr, u64 expected_size)
u64 addr, u64 expected_size,
u64 *va_out)
{
struct amdgpu_bo_va_mapping *va_map;
struct amdgpu_vm *vm = queue->vm;
u64 user_addr;
u64 size;
int r = 0;
/* Caller must hold vm->root.bo reservation */
dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv);
@ -250,20 +232,18 @@ int amdgpu_userq_input_va_validate(struct amdgpu_device *adev,
size = expected_size >> AMDGPU_GPU_PAGE_SHIFT;
va_map = amdgpu_vm_bo_lookup_mapping(vm, user_addr);
if (!va_map) {
r = -EINVAL;
goto out_err;
}
if (!va_map)
return -EINVAL;
/* Only validate the userq whether resident in the VM mapping range */
if (user_addr >= va_map->start &&
va_map->last - user_addr + 1 >= size) {
amdgpu_userq_buffer_va_list_add(queue, va_map, user_addr);
va_map->bo_va->userq_va_mapped = true;
*va_out = user_addr;
return 0;
}
r = -EINVAL;
out_err:
return r;
return -EINVAL;
}
static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr)
@ -284,14 +264,16 @@ static bool amdgpu_userq_buffer_va_mapped(struct amdgpu_vm *vm, u64 addr)
static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue)
{
struct amdgpu_userq_va_cursor *va_cursor, *tmp;
int r = 0;
int i, r = 0;
list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) {
r += amdgpu_userq_buffer_va_mapped(queue->vm, va_cursor->gpu_addr);
for (i = 0; i < ARRAY_SIZE(queue->userq_vas.va_array); i++) {
if (!queue->userq_vas.va_array[i])
continue;
r += amdgpu_userq_buffer_va_mapped(queue->vm,
queue->userq_vas.va_array[i]);
dev_dbg(queue->userq_mgr->adev->dev,
"validate the userq mapping:%p va:%llx r:%d\n",
queue, va_cursor->gpu_addr, r);
queue, queue->userq_vas.va_array[i], r);
}
if (r != 0)
@ -300,24 +282,7 @@ static bool amdgpu_userq_buffer_vas_mapped(struct amdgpu_usermode_queue *queue)
return false;
}
static void amdgpu_userq_buffer_vas_list_cleanup(struct amdgpu_device *adev,
struct amdgpu_usermode_queue *queue)
{
struct amdgpu_userq_va_cursor *va_cursor, *tmp;
struct amdgpu_bo_va_mapping *mapping;
/* Caller must hold vm->root.bo reservation */
dma_resv_assert_held(queue->vm->root.bo->tbo.base.resv);
list_for_each_entry_safe(va_cursor, tmp, &queue->userq_va_list, list) {
mapping = amdgpu_vm_bo_lookup_mapping(queue->vm, va_cursor->gpu_addr);
if (mapping)
dev_dbg(adev->dev, "delete the userq:%p va:%llx\n",
queue, va_cursor->gpu_addr);
list_del(&va_cursor->list);
kfree(va_cursor);
}
}
static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue)
{
@ -417,18 +382,14 @@ static void amdgpu_userq_cleanup(struct amdgpu_usermode_queue *queue)
{
struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr;
struct amdgpu_device *adev = uq_mgr->adev;
const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type];
/* Wait for mode-1 reset to complete */
down_read(&adev->reset_domain->sem);
uq_funcs->mqd_destroy(queue);
/* Use interrupt-safe locking since IRQ handlers may access these XArrays */
xa_erase_irq(&adev->userq_doorbell_xa, queue->doorbell_index);
amdgpu_userq_fence_driver_free(queue);
queue->fence_drv = NULL;
queue->userq_mgr = NULL;
list_del(&queue->userq_va_list);
up_read(&adev->reset_domain->sem);
}
@ -467,81 +428,15 @@ amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *uq_mgr,
dma_fence_put(ev_fence);
}
int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_userq_obj *userq_obj,
int size)
{
struct amdgpu_device *adev = uq_mgr->adev;
struct amdgpu_bo_param bp;
int r;
memset(&bp, 0, sizeof(bp));
bp.byte_align = PAGE_SIZE;
bp.domain = AMDGPU_GEM_DOMAIN_GTT;
bp.flags = AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS |
AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
bp.type = ttm_bo_type_kernel;
bp.size = size;
bp.resv = NULL;
bp.bo_ptr_size = sizeof(struct amdgpu_bo);
r = amdgpu_bo_create(adev, &bp, &userq_obj->obj);
if (r) {
drm_file_err(uq_mgr->file, "Failed to allocate BO for userqueue (%d)", r);
return r;
}
r = amdgpu_bo_reserve(userq_obj->obj, true);
if (r) {
drm_file_err(uq_mgr->file, "Failed to reserve BO to map (%d)", r);
goto free_obj;
}
r = amdgpu_bo_pin(userq_obj->obj, AMDGPU_GEM_DOMAIN_GTT);
if (r)
goto unresv;
r = amdgpu_ttm_alloc_gart(&(userq_obj->obj)->tbo);
if (r) {
drm_file_err(uq_mgr->file, "Failed to alloc GART for userqueue object (%d)", r);
goto unpin_bo;
}
r = amdgpu_bo_kmap(userq_obj->obj, &userq_obj->cpu_ptr);
if (r) {
drm_file_err(uq_mgr->file, "Failed to map BO for userqueue (%d)", r);
goto unpin_bo;
}
userq_obj->gpu_addr = amdgpu_bo_gpu_offset(userq_obj->obj);
amdgpu_bo_unreserve(userq_obj->obj);
memset(userq_obj->cpu_ptr, 0, size);
return 0;
unpin_bo:
amdgpu_bo_unpin(userq_obj->obj);
unresv:
amdgpu_bo_unreserve(userq_obj->obj);
free_obj:
amdgpu_bo_unref(&userq_obj->obj);
return r;
}
void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_userq_obj *userq_obj)
{
amdgpu_bo_kunmap(userq_obj->obj);
amdgpu_bo_unpin(userq_obj->obj);
amdgpu_bo_unref(&userq_obj->obj);
}
uint64_t
static int
amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_db_info *db_info,
struct drm_file *filp)
struct drm_file *filp,
u64 *index)
{
uint64_t index;
u64 doorbell_index;
struct drm_gem_object *gobj;
struct amdgpu_userq_obj *db_obj = db_info->db_obj;
int r, db_size;
@ -588,12 +483,13 @@ amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr,
goto unpin_bo;
}
index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj,
db_info->doorbell_offset, db_size);
doorbell_index = amdgpu_doorbell_index_on_bar(uq_mgr->adev, db_obj->obj,
db_info->doorbell_offset, db_size);
drm_dbg_driver(adev_to_drm(uq_mgr->adev),
"[Usermode queues] doorbell index=%lld\n", index);
"[Usermode queues] doorbell index=%lld\n", doorbell_index);
amdgpu_bo_unreserve(db_obj->obj);
return index;
*index = doorbell_index;
return 0;
unpin_bo:
amdgpu_bo_unpin(db_obj->obj);
@ -608,9 +504,7 @@ static int
amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_queue *queue)
{
struct amdgpu_device *adev = uq_mgr->adev;
struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr);
struct amdgpu_vm *vm = &fpriv->vm;
const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type];
int r = 0;
cancel_delayed_work_sync(&uq_mgr->resume_work);
@ -618,14 +512,6 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
/* Cancel any pending hang detection work and cleanup */
cancel_delayed_work_sync(&queue->hang_detect_work);
r = amdgpu_bo_reserve(vm->root.bo, false);
if (r) {
drm_file_err(uq_mgr->file, "Failed to reserve root bo during userqueue destroy\n");
return r;
}
amdgpu_userq_buffer_vas_list_cleanup(adev, queue);
amdgpu_bo_unreserve(vm->root.bo);
mutex_lock(&uq_mgr->userq_mutex);
amdgpu_userq_wait_for_last_fence(queue);
@ -637,6 +523,10 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que
amdgpu_userq_cleanup(queue);
mutex_unlock(&uq_mgr->userq_mutex);
cancel_delayed_work_sync(&queue->hang_detect_work);
uq_funcs->mqd_destroy(queue);
queue->userq_mgr = NULL;
amdgpu_bo_reserve(queue->db_obj.obj, true);
amdgpu_bo_unpin(queue->db_obj.obj);
amdgpu_bo_unreserve(queue->db_obj.obj);
@ -739,7 +629,6 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
}
kref_init(&queue->refcount);
INIT_LIST_HEAD(&queue->userq_va_list);
queue->doorbell_handle = args->in.doorbell_handle;
queue->queue_type = args->in.ip_type;
queue->vm = &fpriv->vm;
@ -748,26 +637,29 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
INIT_DELAYED_WORK(&queue->hang_detect_work,
amdgpu_userq_hang_detect_work);
mutex_init(&queue->fence_drv_lock);
xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC);
r = amdgpu_userq_fence_driver_alloc(adev, &queue->fence_drv);
if (r)
goto free_queue;
xa_init_flags(&queue->fence_drv_xa, XA_FLAGS_ALLOC);
mutex_init(&queue->fence_drv_lock);
/* Make sure the queue can actually run with those virtual addresses. */
r = amdgpu_bo_reserve(fpriv->vm.root.bo, false);
if (r)
goto free_fence_drv;
if (amdgpu_userq_input_va_validate(adev, queue, args->in.queue_va,
args->in.queue_size) ||
args->in.queue_size,
&queue->userq_vas.va.queue_rb) ||
amdgpu_userq_input_va_validate(adev, queue, args->in.rptr_va,
AMDGPU_GPU_PAGE_SIZE) ||
AMDGPU_GPU_PAGE_SIZE,
&queue->userq_vas.va.rptr) ||
amdgpu_userq_input_va_validate(adev, queue, args->in.wptr_va,
AMDGPU_GPU_PAGE_SIZE)) {
AMDGPU_GPU_PAGE_SIZE,
&queue->userq_vas.va.wptr)) {
r = -EINVAL;
amdgpu_bo_unreserve(fpriv->vm.root.bo);
goto clean_mapping;
goto free_fence_drv;
}
amdgpu_bo_unreserve(fpriv->vm.root.bo);
@ -776,18 +668,17 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
db_info.doorbell_handle = queue->doorbell_handle;
db_info.db_obj = &queue->db_obj;
db_info.doorbell_offset = args->in.doorbell_offset;
index = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp);
if (index == (uint64_t)-EINVAL) {
r = amdgpu_userq_get_doorbell_index(uq_mgr, &db_info, filp, &index);
if (r) {
drm_file_err(uq_mgr->file, "Failed to get doorbell for queue\n");
r = -EINVAL;
goto clean_mapping;
goto free_fence_drv;
}
queue->doorbell_index = index;
r = uq_funcs->mqd_create(queue, &args->in);
if (r) {
drm_file_err(uq_mgr->file, "Failed to create Queue\n");
goto clean_mapping;
goto clean_doorbell_bo;
}
/* Update VM owner at userq submit-time for page-fault attribution. */
@ -808,7 +699,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
if (r) {
drm_file_err(uq_mgr->file, "Failed to map Queue\n");
mutex_unlock(&uq_mgr->userq_mutex);
goto clean_doorbell;
goto erase_doorbell;
}
}
@ -831,15 +722,15 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args)
args->out.queue_id = qid;
return 0;
clean_doorbell:
erase_doorbell:
xa_erase_irq(&adev->userq_doorbell_xa, index);
clean_mqd:
uq_funcs->mqd_destroy(queue);
clean_mapping:
amdgpu_bo_reserve(fpriv->vm.root.bo, true);
amdgpu_userq_buffer_vas_list_cleanup(adev, queue);
amdgpu_bo_unreserve(fpriv->vm.root.bo);
mutex_destroy(&queue->fence_drv_lock);
clean_doorbell_bo:
amdgpu_bo_reserve(queue->db_obj.obj, true);
amdgpu_bo_unpin(queue->db_obj.obj);
amdgpu_bo_unreserve(queue->db_obj.obj);
amdgpu_bo_unref(&queue->db_obj.obj);
free_fence_drv:
amdgpu_userq_fence_driver_free(queue);
free_queue:

View File

@ -48,11 +48,6 @@ struct amdgpu_userq_obj {
struct amdgpu_bo *obj;
};
struct amdgpu_userq_va_cursor {
u64 gpu_addr;
struct list_head list;
};
struct amdgpu_usermode_queue {
int queue_type;
enum amdgpu_userq_state state;
@ -93,7 +88,17 @@ struct amdgpu_usermode_queue {
struct delayed_work hang_detect_work;
struct kref refcount;
struct list_head userq_va_list;
union {
struct {
u64 queue_rb;
u64 wptr;
u64 rptr;
u64 eop;
u64 shadow;
u64 csa;
} va;
u64 va_array[6];
} userq_vas;
};
struct amdgpu_userq_funcs {
@ -151,22 +156,11 @@ void amdgpu_userq_mgr_cancel_reset_work(struct amdgpu_device *adev);
void amdgpu_userq_mgr_cancel_resume(struct amdgpu_userq_mgr *userq_mgr);
void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr);
int amdgpu_userq_create_object(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_userq_obj *userq_obj,
int size);
void amdgpu_userq_destroy_object(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_userq_obj *userq_obj);
void amdgpu_userq_evict(struct amdgpu_userq_mgr *uq_mgr);
void amdgpu_userq_ensure_ev_fence(struct amdgpu_userq_mgr *userq_mgr,
struct amdgpu_eviction_fence_mgr *evf_mgr);
uint64_t amdgpu_userq_get_doorbell_index(struct amdgpu_userq_mgr *uq_mgr,
struct amdgpu_db_info *db_info,
struct drm_file *filp);
u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev);
bool amdgpu_userq_enabled(struct drm_device *dev);
@ -185,7 +179,8 @@ void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell);
int amdgpu_userq_input_va_validate(struct amdgpu_device *adev,
struct amdgpu_usermode_queue *queue,
u64 addr, u64 expected_size);
u64 addr, u64 expected_size, u64 *va_out);
void amdgpu_userq_gem_va_unmap_validate(struct amdgpu_device *adev,
struct amdgpu_bo_va_mapping *mapping,
uint64_t saddr);

View File

@ -1804,13 +1804,15 @@ amdgpu_virt_write_cpers_to_ring(struct amdgpu_device *adev,
struct amd_sriov_ras_cper_dump *cper_dump = NULL;
struct cper_hdr *entry = NULL;
struct amdgpu_ring *ring = &adev->cper.ring_buf;
uint32_t checksum, used_size, i;
uint32_t checksum, used_size;
u64 remaining, cnt, i;
int ret = 0;
checksum = host_telemetry->header.checksum;
used_size = host_telemetry->header.used_size;
if (used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10))
if (used_size < offsetof(struct amd_sriov_ras_cper_dump, buf) ||
used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10))
return -EINVAL;
cper_dump = kmemdup(&host_telemetry->body.cper_dump, used_size, GFP_KERNEL);
@ -1835,11 +1837,19 @@ amdgpu_virt_write_cpers_to_ring(struct amdgpu_device *adev,
}
entry = (struct cper_hdr *)&cper_dump->buf[0];
remaining = (u64)used_size - offsetof(struct amd_sriov_ras_cper_dump, buf);
cnt = min_t(u64, cper_dump->count, CPER_MAX_ALLOWED_COUNT);
for (i = 0; i < cnt; i++) {
if (entry->record_length < sizeof(struct cper_hdr) ||
entry->record_length > remaining) {
ret = -EINVAL;
goto out;
}
for (i = 0; i < cper_dump->count; i++) {
amdgpu_cper_ring_write(ring, entry, entry->record_length);
entry = (struct cper_hdr *)((char *)entry +
entry->record_length);
remaining -= entry->record_length;
entry = (struct cper_hdr *)((char *)entry + entry->record_length);
}
if (cper_dump->overflow_count)

View File

@ -266,12 +266,23 @@ static void amdgpu_vm_bo_idle(struct amdgpu_vm_bo_base *vm_bo)
*/
static void amdgpu_vm_bo_reset_state_machine(struct amdgpu_vm *vm)
{
struct amdgpu_vm_bo_base *vm_bo, *tmp;
/*
* Don't use list splice here, we need the special handling for the root
* PD and set the moved flag appropriately.
*/
amdgpu_vm_assert_locked(vm);
list_splice_init(&vm->kernel.idle, &vm->kernel.moved);
list_splice_init(&vm->always_valid.idle, &vm->always_valid.moved);
list_for_each_entry_safe(vm_bo, tmp, &vm->kernel.idle, vm_status)
amdgpu_vm_bo_moved(vm_bo);
list_for_each_entry_safe(vm_bo, tmp, &vm->always_valid.idle, vm_status)
amdgpu_vm_bo_moved(vm_bo);
spin_lock(&vm->individual_lock);
list_splice_init(&vm->individual.idle, &vm->individual.moved);
list_for_each_entry_safe(vm_bo, tmp, &vm->individual.idle, vm_status) {
vm_bo->moved = true;
list_move(&vm_bo->vm_status, &vm->individual.moved);
}
spin_unlock(&vm->individual_lock);
}
@ -1600,6 +1611,7 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev,
{
struct amdgpu_bo_va *bo_va, *tmp;
struct dma_resv *resv;
struct amdgpu_bo *bo;
bool clear, unlock;
int r;
@ -1615,11 +1627,13 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev,
while (!list_empty(&vm->individual.moved)) {
bo_va = list_first_entry(&vm->individual.moved,
typeof(*bo_va), base.vm_status);
resv = bo_va->base.bo->tbo.base.resv;
bo = bo_va->base.bo;
resv = bo->tbo.base.resv;
spin_unlock(&vm->individual_lock);
/* Try to reserve the BO to avoid clearing its ptes */
if (!adev->debug_vm && dma_resv_trylock(resv)) {
if (!adev->debug_vm && !amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) &&
dma_resv_trylock(resv)) {
clear = false;
unlock = true;
/* The caller is already holding the reservation lock */

View File

@ -81,7 +81,7 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev,
ret = amdgpu_ttm_alloc_gart(&wptr_obj->obj->tbo);
if (ret) {
DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret);
goto fail_map;
goto fail_alloc_gart;
}
queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(wptr_obj->obj);
@ -89,6 +89,8 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev,
drm_exec_fini(&exec);
return 0;
fail_alloc_gart:
amdgpu_bo_unpin(wptr_obj->obj);
fail_map:
amdgpu_bo_unref(&wptr_obj->obj);
fail_lock:
@ -190,12 +192,16 @@ static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr,
* for the same.
*/
size = AMDGPU_USERQ_PROC_CTX_SZ + AMDGPU_USERQ_GANG_CTX_SZ;
r = amdgpu_userq_create_object(uq_mgr, ctx, size);
r = amdgpu_bo_create_kernel(uq_mgr->adev, size, 0,
AMDGPU_GEM_DOMAIN_GTT,
&ctx->obj, &ctx->gpu_addr,
&ctx->cpu_ptr);
if (r) {
DRM_ERROR("Failed to allocate ctx space bo for userqueue, err:%d\n", r);
return r;
}
memset(ctx->cpu_ptr, 0, size);
return 0;
}
@ -268,13 +274,19 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
return -ENOMEM;
}
r = amdgpu_userq_create_object(uq_mgr, &queue->mqd,
AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size));
r = amdgpu_bo_create_kernel(adev,
AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size),
0, AMDGPU_GEM_DOMAIN_GTT,
&queue->mqd.obj, &queue->mqd.gpu_addr,
&queue->mqd.cpu_ptr);
if (r) {
DRM_ERROR("Failed to create MQD object for userqueue\n");
goto free_props;
}
memset(queue->mqd.cpu_ptr, 0,
AMDGPU_MQD_SIZE_ALIGN(mqd_hw_default->mqd_size));
/* Initialize the MQD BO with user given values */
userq_props->wptr_gpu_addr = mqd_user->wptr_va;
userq_props->rptr_gpu_addr = mqd_user->rptr_va;
@ -306,8 +318,9 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
kfree(compute_mqd);
goto free_mqd;
}
r = amdgpu_userq_input_va_validate(adev, queue, compute_mqd->eop_va,
2048);
r = amdgpu_userq_input_va_validate(adev, queue,
compute_mqd->eop_va, 2048,
&queue->userq_vas.va.eop);
amdgpu_bo_unreserve(queue->vm->root.bo);
if (r) {
kfree(compute_mqd);
@ -356,7 +369,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
goto free_mqd;
}
r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->shadow_va,
shadow_info.shadow_size);
shadow_info.shadow_size,
&queue->userq_vas.va.shadow);
if (r) {
amdgpu_bo_unreserve(queue->vm->root.bo);
kfree(mqd_gfx_v11);
@ -364,7 +378,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
}
r = amdgpu_userq_input_va_validate(adev, queue, mqd_gfx_v11->csa_va,
shadow_info.csa_size);
shadow_info.csa_size,
&queue->userq_vas.va.csa);
amdgpu_bo_unreserve(queue->vm->root.bo);
if (r) {
kfree(mqd_gfx_v11);
@ -394,7 +409,8 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
goto free_mqd;
}
r = amdgpu_userq_input_va_validate(adev, queue, mqd_sdma_v11->csa_va,
32);
32,
&queue->userq_vas.va.csa);
amdgpu_bo_unreserve(queue->vm->root.bo);
if (r) {
kfree(mqd_sdma_v11);
@ -430,10 +446,12 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
return 0;
free_ctx:
amdgpu_userq_destroy_object(uq_mgr, &queue->fw_obj);
amdgpu_bo_free_kernel(&queue->fw_obj.obj, &queue->fw_obj.gpu_addr,
&queue->fw_obj.cpu_ptr);
free_mqd:
amdgpu_userq_destroy_object(uq_mgr, &queue->mqd);
amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr,
&queue->mqd.cpu_ptr);
free_props:
kfree(userq_props);
@ -443,11 +461,12 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue,
static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue)
{
struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr;
amdgpu_userq_destroy_object(uq_mgr, &queue->fw_obj);
amdgpu_bo_free_kernel(&queue->fw_obj.obj, &queue->fw_obj.gpu_addr,
&queue->fw_obj.cpu_ptr);
kfree(queue->userq_prop);
amdgpu_userq_destroy_object(uq_mgr, &queue->mqd);
amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr,
&queue->mqd.cpu_ptr);
}
static int mes_userq_preempt(struct amdgpu_usermode_queue *queue)

View File

@ -285,7 +285,8 @@ static int umc_v12_0_convert_error_address(struct amdgpu_device *adev,
struct ta_ras_query_address_output *addr_out,
bool dump_addr)
{
uint32_t col, col_lower, row, row_lower, row_high, bank;
uint32_t row = 0, row_lower = 0, row_high = 0;
uint32_t col = 0, col_lower = 0, bank = 0;
uint32_t channel_index = 0, umc_inst = 0;
uint32_t i, bit_num, retire_unit, *flip_bits;
uint64_t soc_pa, column, err_addr;

View File

@ -2399,6 +2399,11 @@ static int criu_restore_devices(struct kfd_process *p,
ret = -EINVAL;
goto exit;
}
if (pdd->drm_file) {
ret = -EINVAL;
goto exit;
}
pdd->user_gpu_id = device_buckets[i].user_gpu_id;
drm_file = fget(device_buckets[i].drm_fd);
@ -2409,11 +2414,6 @@ static int criu_restore_devices(struct kfd_process *p,
goto exit;
}
if (pdd->drm_file) {
ret = -EINVAL;
goto exit;
}
/* create the vm using render nodes for kfd pdd */
if (kfd_process_device_init_vm(pdd, drm_file)) {
pr_err("could not init vm for given pdd\n");

View File

@ -1821,7 +1821,7 @@ static int kfd_fill_mem_info_for_cpu(int numa_node_id, int *avail_size,
return 0;
}
#ifdef CONFIG_X86_64
#if defined(CONFIG_X86_64) && !defined(CONFIG_UML)
static int kfd_fill_iolink_info_for_cpu(int numa_node_id, int *avail_size,
uint32_t *num_entries,
struct crat_subtype_iolink *sub_type_hdr)
@ -1880,7 +1880,7 @@ static int kfd_create_vcrat_image_cpu(void *pcrat_image, size_t *size)
struct crat_subtype_generic *sub_type_hdr;
int avail_size = *size;
int numa_node_id;
#ifdef CONFIG_X86_64
#if defined(CONFIG_X86_64) && !defined(CONFIG_UML)
uint32_t entries = 0;
#endif
int ret = 0;
@ -1945,7 +1945,7 @@ static int kfd_create_vcrat_image_cpu(void *pcrat_image, size_t *size)
sub_type_hdr->length);
/* Fill in Subtype: IO Link */
#ifdef CONFIG_X86_64
#if defined(CONFIG_X86_64) && !defined(CONFIG_UML)
ret = kfd_fill_iolink_info_for_cpu(numa_node_id, &avail_size,
&entries,
(struct crat_subtype_iolink *)sub_type_hdr);

View File

@ -3477,12 +3477,14 @@ static void copy_context_work_handler(struct work_struct *work)
static uint32_t *get_queue_ids(uint32_t num_queues, uint32_t *usr_queue_id_array)
{
size_t array_size = num_queues * sizeof(uint32_t);
if (!usr_queue_id_array)
return NULL;
return memdup_user(usr_queue_id_array, array_size);
if (num_queues > KFD_MAX_NUM_OF_QUEUES_PER_PROCESS)
return ERR_PTR(-EINVAL);
return memdup_user(usr_queue_id_array,
array_size(num_queues, sizeof(uint32_t)));
}
int resume_queues(struct kfd_process *p,

View File

@ -3732,6 +3732,9 @@ svm_range_set_attr(struct kfd_process *p, struct mm_struct *mm,
svms = &p->svms;
if (!process_info)
return -EINVAL;
mutex_lock(&process_info->lock);
svm_range_list_lock_and_flush_work(svms, mm);

View File

@ -2350,7 +2350,7 @@ static int kfd_cpumask_to_apic_id(const struct cpumask *cpumask)
first_cpu_of_numa_node = cpumask_first(cpumask);
if (first_cpu_of_numa_node >= nr_cpu_ids)
return -1;
#ifdef CONFIG_X86_64
#if defined(CONFIG_X86_64) && !defined(CONFIG_UML)
return cpu_data(first_cpu_of_numa_node).topo.apicid;
#else
return first_cpu_of_numa_node;

View File

@ -56,4 +56,16 @@ config DRM_AMD_SECURE_DISPLAY
This option enables the calculation of crc of specific region via
debugfs. Cooperate with specific DMCU FW.
config DRM_AMD_DC_KUNIT_TEST
tristate "KUnit tests for the AMD DC display driver" if !KUNIT_ALL_TESTS
depends on DRM_AMD_DC && KUNIT && DEBUG_FS
default KUNIT_ALL_TESTS
help
This option enables KUnit tests for the AMD Display Core driver.
These tests validate core functionality like CRC source parsing,
color management, and other utility functions.
For more information on KUnit and unit tests in general, please
refer to the KUnit documentation in Documentation/dev-tools/kunit/.
endmenu

View File

@ -59,3 +59,8 @@ AMDGPU_DM = $(addprefix $(AMDDALPATH)/amdgpu_dm/,$(AMDGPUDM))
AMD_DISPLAY_FILES += $(AMDGPU_DM)
endif
# KUnit tests as separate module
ifneq ($(CONFIG_DRM_AMD_DC_KUNIT_TEST),)
obj-y += $(AMDDALPATH)/amdgpu_dm/tests/
endif

View File

@ -7473,7 +7473,7 @@ create_stream_for_sink(struct drm_connector *connector,
int preferred_refresh = 0;
enum color_transfer_func tf = TRANSFER_FUNC_UNKNOWN;
#if defined(CONFIG_DRM_AMD_DC_FP)
struct dsc_dec_dpcd_caps dsc_caps;
struct dsc_dec_dpcd_caps dsc_caps = {0};
#endif
struct dc_link *link = NULL;
struct dc_sink *sink = NULL;

View File

@ -45,8 +45,6 @@
* in amdgpu_dm_kms.h file
*/
#define AMDGPU_DM_MAX_DISPLAY_INDEX 31
#define AMDGPU_DM_MAX_CRTC 6
#define AMDGPU_DM_MAX_NUM_EDP 2

View File

@ -29,9 +29,12 @@
#include "amdgpu.h"
#include "amdgpu_mode.h"
#include "amdgpu_dm.h"
#include "amdgpu_dm_color.h"
#include "amdgpu_dm_colorop.h"
#include "dc.h"
#include "modules/color/color_gamma.h"
#include "amdgpu_dm_kunit_helpers.h"
/**
* DOC: overview
@ -157,8 +160,6 @@
*
*/
#define MAX_DRM_LUT_VALUE 0xFFFF
#define MAX_DRM_LUT32_VALUE 0xFFFFFFFF
#define SDR_WHITE_LEVEL_INIT_VALUE 80
/**
@ -172,7 +173,8 @@ void amdgpu_dm_init_color_mod(void)
setup_x_points_distribution();
}
static inline struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x)
STATIC_IFN_KUNIT INLINE_IFN_KUNIT
struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x)
{
struct fixed31_32 val;
@ -183,6 +185,7 @@ static inline struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x)
val.value = x;
return val;
}
EXPORT_IF_KUNIT(amdgpu_dm_fixpt_from_s3132);
#ifdef AMD_PRIVATE_COLOR
/* Pre-defined Transfer Functions (TF)
@ -421,12 +424,14 @@ amdgpu_dm_create_color_properties(struct amdgpu_device *adev)
* Returns:
* DRM LUT or NULL
*/
static const struct drm_color_lut *
STATIC_IFN_KUNIT
const struct drm_color_lut *
__extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size)
{
*size = blob ? drm_color_lut_size(blob) : 0;
return blob ? (struct drm_color_lut *)blob->data : NULL;
}
EXPORT_IF_KUNIT(__extract_blob_lut);
/**
* __extract_blob_lut32 - Extracts the DRM lut and lut size from a blob.
@ -436,12 +441,14 @@ __extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size)
* Returns:
* DRM LUT or NULL
*/
static const struct drm_color_lut32 *
STATIC_IFN_KUNIT
const struct drm_color_lut32 *
__extract_blob_lut32(const struct drm_property_blob *blob, uint32_t *size)
{
*size = blob ? drm_color_lut32_size(blob) : 0;
return blob ? (struct drm_color_lut32 *)blob->data : NULL;
}
EXPORT_IF_KUNIT(__extract_blob_lut32);
/**
* __is_lut_linear - check if the given lut is a linear mapping of values
@ -456,7 +463,8 @@ __extract_blob_lut32(const struct drm_property_blob *blob, uint32_t *size)
* True if the given lut is a linear mapping of values, i.e. it acts like a
* bypass LUT. Otherwise, false.
*/
static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size)
STATIC_IFN_KUNIT
bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size)
{
int i;
uint32_t expected;
@ -476,6 +484,7 @@ static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size)
}
return true;
}
EXPORT_IF_KUNIT(__is_lut_linear);
/**
* __drm_lut_to_dc_gamma - convert the drm_color_lut to dc_gamma.
@ -485,7 +494,8 @@ static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size)
*
* The conversion depends on the size of the lut - whether or not it's legacy.
*/
static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
STATIC_IFN_KUNIT
void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
struct dc_gamma *gamma, bool is_legacy)
{
uint32_t r, g, b;
@ -515,6 +525,7 @@ static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
gamma->entries.blue[i] = dc_fixpt_from_fraction(b, MAX_DRM_LUT_VALUE);
}
}
EXPORT_IF_KUNIT(__drm_lut_to_dc_gamma);
/**
* __drm_lut32_to_dc_gamma - convert the drm_color_lut to dc_gamma.
@ -523,7 +534,8 @@ static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
*
* The conversion depends on the size of the lut - whether or not it's legacy.
*/
static void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc_gamma *gamma)
STATIC_IFN_KUNIT
void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc_gamma *gamma)
{
int i;
@ -533,6 +545,7 @@ static void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc
gamma->entries.blue[i] = dc_fixpt_from_fraction(lut[i].blue, MAX_DRM_LUT32_VALUE);
}
}
EXPORT_IF_KUNIT(__drm_lut32_to_dc_gamma);
/**
* __drm_ctm_to_dc_matrix - converts a DRM CTM to a DC CSC float matrix
@ -541,8 +554,9 @@ static void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc
*
* The matrix needs to be a 3x4 (12 entry) matrix.
*/
static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
struct fixed31_32 *matrix)
STATIC_IFN_KUNIT
void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
struct fixed31_32 *matrix)
{
int i;
@ -565,6 +579,7 @@ static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
matrix[i] = amdgpu_dm_fixpt_from_s3132(ctm->matrix[i - (i / 4)]);
}
}
EXPORT_IF_KUNIT(__drm_ctm_to_dc_matrix);
/**
* __drm_ctm_3x4_to_dc_matrix - converts a DRM CTM 3x4 to a DC CSC float matrix
@ -573,8 +588,9 @@ static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
*
* The matrix needs to be a 3x4 (12 entry) matrix.
*/
static void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm,
struct fixed31_32 *matrix)
STATIC_IFN_KUNIT
void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm,
struct fixed31_32 *matrix)
{
int i;
@ -587,6 +603,7 @@ static void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm,
matrix[i] = amdgpu_dm_fixpt_from_s3132(ctm->matrix[i]);
}
}
EXPORT_IF_KUNIT(__drm_ctm_3x4_to_dc_matrix);
/**
* __set_legacy_tf - Calculates the legacy transfer function
@ -851,7 +868,8 @@ static int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func
return res ? 0 : -ENOMEM;
}
static enum dc_transfer_func_predefined
STATIC_IFN_KUNIT
enum dc_transfer_func_predefined
amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf)
{
switch (tf) {
@ -879,8 +897,10 @@ amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf)
return TRANSFER_FUNCTION_GAMMA26;
}
}
EXPORT_IF_KUNIT(amdgpu_tf_to_dc_tf);
static enum dc_transfer_func_predefined
STATIC_IFN_KUNIT
enum dc_transfer_func_predefined
amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf)
{
switch (tf) {
@ -900,8 +920,10 @@ amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf)
return TRANSFER_FUNCTION_LINEAR;
}
}
EXPORT_IF_KUNIT(amdgpu_colorop_tf_to_dc_tf);
static void __to_dc_lut3d_color(struct dc_rgb *rgb,
STATIC_IFN_KUNIT
void __to_dc_lut3d_color(struct dc_rgb *rgb,
const struct drm_color_lut lut,
int bit_precision)
{
@ -909,8 +931,10 @@ static void __to_dc_lut3d_color(struct dc_rgb *rgb,
rgb->green = drm_color_lut_extract(lut.green, bit_precision);
rgb->blue = drm_color_lut_extract(lut.blue, bit_precision);
}
EXPORT_IF_KUNIT(__to_dc_lut3d_color);
static void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut,
STATIC_IFN_KUNIT
void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut,
uint32_t lut3d_size,
struct tetrahedral_params *params,
bool use_tetrahedral_9,
@ -953,8 +977,10 @@ static void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut,
/* lut0 has 1229 points (lut_size/4 + 1) */
__to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth);
}
EXPORT_IF_KUNIT(__drm_3dlut_to_dc_3dlut);
static void __to_dc_lut3d_32_color(struct dc_rgb *rgb,
STATIC_IFN_KUNIT
void __to_dc_lut3d_32_color(struct dc_rgb *rgb,
const struct drm_color_lut32 lut,
int bit_precision)
{
@ -962,8 +988,10 @@ static void __to_dc_lut3d_32_color(struct dc_rgb *rgb,
rgb->green = drm_color_lut32_extract(lut.green, bit_precision);
rgb->blue = drm_color_lut32_extract(lut.blue, bit_precision);
}
EXPORT_IF_KUNIT(__to_dc_lut3d_32_color);
static void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut,
STATIC_IFN_KUNIT
void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut,
uint32_t lut3d_size,
struct tetrahedral_params *params,
bool use_tetrahedral_9,
@ -1006,6 +1034,7 @@ static void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut,
/* lut0 has 1229 points (lut_size/4 + 1) */
__to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth);
}
EXPORT_IF_KUNIT(__drm_3dlut32_to_dc_3dlut);
/* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream
* @drm_lut3d: user 3D LUT

View File

@ -0,0 +1,89 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright 2026 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: AMD
*
*/
#ifndef __AMDGPU_DM_COLOR_H__
#define __AMDGPU_DM_COLOR_H__
#define MAX_DRM_LUT_VALUE 0xFFFF
#define MAX_DRM_LUT32_VALUE 0xFFFFFFFF
#include <linux/types.h>
struct drm_color_lut;
struct drm_color_lut32;
struct drm_color_ctm;
struct drm_color_ctm_3x4;
struct drm_property_blob;
struct dc_gamma;
struct dc_rgb;
struct fixed31_32;
struct tetrahedral_params;
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
/*
* Prototypes for functions exposed to KUnit tests. The enum types
* used below (dc_transfer_func_predefined, amdgpu_transfer_function,
* drm_colorop_curve_1d_type) must be defined before this header is
* included the source file (amdgpu_dm_color.c) ensures this via
* its own includes of dc.h, amdgpu_dm.h, and drm/drm_colorop.h.
*/
struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x);
bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size);
void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut,
struct dc_gamma *gamma, bool is_legacy);
void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut,
struct dc_gamma *gamma);
void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm,
struct fixed31_32 *matrix);
void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm,
struct fixed31_32 *matrix);
enum dc_transfer_func_predefined
amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf);
enum dc_transfer_func_predefined
amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf);
const struct drm_color_lut *
__extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size);
const struct drm_color_lut32 *
__extract_blob_lut32(const struct drm_property_blob *blob, uint32_t *size);
void __to_dc_lut3d_color(struct dc_rgb *rgb,
const struct drm_color_lut lut,
int bit_precision);
void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut,
uint32_t lut3d_size,
struct tetrahedral_params *params,
bool use_tetrahedral_9,
int bit_depth);
void __to_dc_lut3d_32_color(struct dc_rgb *rgb,
const struct drm_color_lut32 lut,
int bit_precision);
void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut,
uint32_t lut3d_size,
struct tetrahedral_params *params,
bool use_tetrahedral_9,
int bit_depth);
#endif
#endif /* __AMDGPU_DM_COLOR_H__ */

View File

@ -31,6 +31,7 @@
#include "amdgpu.h"
#include "amdgpu_dm_colorop.h"
#include "amdgpu_dm_kunit_helpers.h"
#include "dc.h"
const u64 amdgpu_dm_supported_degam_tfs =
@ -38,18 +39,21 @@ const u64 amdgpu_dm_supported_degam_tfs =
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
EXPORT_IF_KUNIT(amdgpu_dm_supported_degam_tfs);
const u64 amdgpu_dm_supported_shaper_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF) |
BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV);
EXPORT_IF_KUNIT(amdgpu_dm_supported_shaper_tfs);
const u64 amdgpu_dm_supported_blnd_tfs =
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
EXPORT_IF_KUNIT(amdgpu_dm_supported_blnd_tfs);
#define MAX_COLOR_PIPELINE_OPS 10

View File

@ -33,6 +33,7 @@
#include "amdgpu_securedisplay.h"
#include "amdgpu_dm_psr.h"
#include "amdgpu_dm_replay.h"
#include "amdgpu_dm_kunit_helpers.h"
static const char *const pipe_crc_sources[] = {
"none",
@ -43,7 +44,8 @@ static const char *const pipe_crc_sources[] = {
"auto",
};
static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source)
STATIC_IFN_KUNIT
enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source)
{
if (!source || !strcmp(source, "none"))
return AMDGPU_DM_PIPE_CRC_SOURCE_NONE;
@ -58,25 +60,32 @@ static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source)
return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID;
}
EXPORT_IF_KUNIT(dm_parse_crc_source);
static bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src)
STATIC_IFN_KUNIT
bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src)
{
return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC) ||
(src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER);
}
EXPORT_IF_KUNIT(dm_is_crc_source_crtc);
static bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src)
STATIC_IFN_KUNIT
bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src)
{
return (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX) ||
(src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER);
}
EXPORT_IF_KUNIT(dm_is_crc_source_dprx);
static bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src)
STATIC_IFN_KUNIT
bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src)
{
return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER) ||
(src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER) ||
(src == AMDGPU_DM_PIPE_CRC_SOURCE_NONE);
}
EXPORT_IF_KUNIT(dm_need_crc_dither);
const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc,
size_t *count)

View File

@ -27,8 +27,11 @@
#ifndef AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_
#define AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_
#include "dc_types.h"
struct drm_crtc;
struct dm_crtc_state;
struct amdgpu_device;
enum amdgpu_dm_pipe_crc_source {
AMDGPU_DM_PIPE_CRC_SOURCE_NONE = 0,
@ -148,4 +151,11 @@ void amdgpu_dm_crtc_secure_display_create_contexts(struct amdgpu_device *adev);
#define amdgpu_dm_crtc_secure_display_create_contexts(x)
#endif
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source);
bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src);
bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src);
bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src);
#endif
#endif /* AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_ */

View File

@ -31,6 +31,7 @@
#include "dm_helpers.h"
#include <drm/display/drm_hdcp_helper.h>
#include "hdcp_psp.h"
#include "amdgpu_dm_kunit_helpers.h"
/*
* If the SRM version being loaded is less than or equal to the
@ -158,7 +159,8 @@ static int psp_set_srm(struct psp_context *psp,
return 0;
}
static void process_output(struct hdcp_workqueue *hdcp_work)
STATIC_IFN_KUNIT
void process_output(struct hdcp_workqueue *hdcp_work)
{
struct mod_hdcp_output output = hdcp_work->output;
@ -178,6 +180,7 @@ static void process_output(struct hdcp_workqueue *hdcp_work)
schedule_delayed_work(&hdcp_work->property_validate_dwork, msecs_to_jiffies(0));
}
EXPORT_IF_KUNIT(process_output);
static void link_lock(struct hdcp_workqueue *work, bool lock)
{

View File

@ -31,12 +31,19 @@
#include "hdcp.h"
#include "dc.h"
#include "dm_cp_psp.h"
#include "amdgpu.h"
/*
* Minimal declarations needed by this header.
* Full amdgpu/DM definitions come from amdgpu_dm.h included by each .c file.
*/
#define AMDGPU_DM_MAX_DISPLAY_INDEX 31
struct amdgpu_dm_connector;
struct mod_hdcp;
struct mod_hdcp_link;
struct mod_hdcp_display;
struct cp_psp;
struct amdgpu_device;
struct hdcp_workqueue {
struct work_struct cpirq_work;
@ -87,4 +94,8 @@ void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *work);
struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct cp_psp *cp_psp, struct dc *dc);
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
void process_output(struct hdcp_workqueue *hdcp_work);
#endif
#endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */

View File

@ -32,6 +32,8 @@
#include "amdgpu_dm_ism.h"
#include "amdgpu_dm_crtc.h"
#include "amdgpu_dm_trace.h"
#include "amdgpu_dm_kunit_helpers.h"
/**
* dm_ism_next_state - Get next state based on current state and event
@ -42,9 +44,10 @@
* This function defines the idle state management FSM. Invalid transitions
* are ignored and will not progress the FSM.
*/
static bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state,
enum amdgpu_dm_ism_event event,
enum amdgpu_dm_ism_state *next_state)
STATIC_IFN_KUNIT
bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state,
enum amdgpu_dm_ism_event event,
enum amdgpu_dm_ism_state *next_state)
{
switch (STATE_EVENT(current_state, event)) {
case STATE_EVENT(DM_ISM_STATE_FULL_POWER_RUNNING,
@ -125,8 +128,10 @@ static bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state,
}
return true;
}
EXPORT_IF_KUNIT(dm_ism_next_state);
static uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism,
STATIC_IFN_KUNIT
uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism,
const struct dc_stream_state *stream)
{
const struct amdgpu_dm_ism_config *config = &ism->config;
@ -148,6 +153,7 @@ static uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism,
return sso_delay_ns;
}
EXPORT_IF_KUNIT(dm_ism_get_sso_delay);
/**
* dm_ism_get_idle_allow_delay - Calculate hysteresis-based idle allow delay
@ -157,8 +163,9 @@ static uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism,
* Calculates the delay before allowing idle optimizations based on recent
* idle history and the current stream timing.
*/
static uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism,
const struct dc_stream_state *stream)
STATIC_IFN_KUNIT
uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism,
const struct dc_stream_state *stream)
{
const struct amdgpu_dm_ism_config *config = &ism->config;
uint32_t v_total, h_total;
@ -217,6 +224,7 @@ static uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism,
return ret_ns;
}
EXPORT_IF_KUNIT(dm_ism_get_idle_allow_delay);
/**
* dm_ism_insert_record - Insert a record into the circular history buffer
@ -375,7 +383,7 @@ static enum amdgpu_dm_ism_event dm_ism_dispatch_power_state(
}
/* Schedule worker */
mod_delayed_work(system_unbound_wq, &ism->delayed_work,
mod_delayed_work(system_dfl_wq, &ism->delayed_work,
nsecs_to_jiffies(delay_ns));
break;
@ -391,14 +399,14 @@ static enum amdgpu_dm_ism_event dm_ism_dispatch_power_state(
* have a negative power impact. Skip idle allow here,
* and let the sso_delayed_work handle it.
*/
mod_delayed_work(system_unbound_wq,
mod_delayed_work(system_dfl_wq,
&ism->sso_delayed_work,
nsecs_to_jiffies(sso_delay_ns));
} else {
/* Enable idle optimization without SSO */
dm_ism_commit_idle_optimization_state(
ism, acrtc_state->stream, false, false);
mod_delayed_work(system_unbound_wq,
mod_delayed_work(system_dfl_wq,
&ism->sso_delayed_work,
nsecs_to_jiffies(sso_delay_ns));
}

View File

@ -149,4 +149,14 @@ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm);
void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm);
void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm);
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state,
enum amdgpu_dm_ism_event event,
enum amdgpu_dm_ism_state *next_state);
uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism,
const struct dc_stream_state *stream);
uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism,
const struct dc_stream_state *stream);
#endif
#endif

View File

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: MIT */
/*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#ifndef AMDGPU_DM_KUNIT_HELPERS_H
#define AMDGPU_DM_KUNIT_HELPERS_H
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
#define STATIC_IFN_KUNIT
#define INLINE_IFN_KUNIT inline
#define EXPORT_IF_KUNIT(symbol) EXPORT_SYMBOL(symbol)
#else
#define STATIC_IFN_KUNIT static
#define INLINE_IFN_KUNIT
#define EXPORT_IF_KUNIT(symbol)
#endif
#endif /* AMDGPU_DM_KUNIT_HELPERS_H */

View File

@ -183,33 +183,6 @@ static enum amd_pp_clock_type dc_to_pp_clock_type(
return amd_pp_clk_type;
}
static enum dm_pp_clocks_state pp_to_dc_powerlevel_state(
enum PP_DAL_POWERLEVEL max_clocks_state)
{
switch (max_clocks_state) {
case PP_DAL_POWERLEVEL_0:
return DM_PP_CLOCKS_DPM_STATE_LEVEL_0;
case PP_DAL_POWERLEVEL_1:
return DM_PP_CLOCKS_DPM_STATE_LEVEL_1;
case PP_DAL_POWERLEVEL_2:
return DM_PP_CLOCKS_DPM_STATE_LEVEL_2;
case PP_DAL_POWERLEVEL_3:
return DM_PP_CLOCKS_DPM_STATE_LEVEL_3;
case PP_DAL_POWERLEVEL_4:
return DM_PP_CLOCKS_DPM_STATE_LEVEL_4;
case PP_DAL_POWERLEVEL_5:
return DM_PP_CLOCKS_DPM_STATE_LEVEL_5;
case PP_DAL_POWERLEVEL_6:
return DM_PP_CLOCKS_DPM_STATE_LEVEL_6;
case PP_DAL_POWERLEVEL_7:
return DM_PP_CLOCKS_DPM_STATE_LEVEL_7;
default:
DRM_ERROR("DM_PPLIB: invalid powerlevel state: %d!\n",
max_clocks_state);
return DM_PP_CLOCKS_STATE_INVALID;
}
}
static void pp_to_dc_clock_levels(
const struct amd_pp_clocks *pp_clks,
struct dm_pp_clock_levels *dc_clks,
@ -315,7 +288,6 @@ bool dm_pp_get_clock_levels_by_type(
DRM_INFO("DM_PPLIB: Warning: using default validation clocks!\n");
validation_clks.engine_max_clock = 72000;
validation_clks.memory_max_clock = 80000;
validation_clks.level = 0;
}
DRM_INFO("DM_PPLIB: Validation clocks:\n");
@ -323,8 +295,6 @@ bool dm_pp_get_clock_levels_by_type(
validation_clks.engine_max_clock);
DRM_INFO("DM_PPLIB: memory_max_clock: %d\n",
validation_clks.memory_max_clock);
DRM_INFO("DM_PPLIB: level : %d\n",
validation_clks.level);
/* Translate 10 kHz to kHz. */
validation_clks.engine_max_clock *= 10;
@ -417,14 +387,6 @@ bool dm_pp_notify_wm_clock_changes(
return false;
}
bool dm_pp_apply_power_level_change_request(
const struct dc_context *ctx,
struct dm_pp_power_level_change_request *level_change_req)
{
/* TODO: to be implemented */
return false;
}
bool dm_pp_apply_clock_for_voltage_request(
const struct dc_context *ctx,
struct dm_pp_clock_for_voltage_req *clock_for_voltage_req)
@ -446,23 +408,6 @@ bool dm_pp_apply_clock_for_voltage_request(
return true;
}
bool dm_pp_get_static_clocks(
const struct dc_context *ctx,
struct dm_pp_static_clock_info *static_clk_info)
{
struct amdgpu_device *adev = ctx->driver_context;
struct amd_pp_clock_info pp_clk_info = {0};
if (amdgpu_dpm_get_current_clocks(adev, &pp_clk_info))
return false;
static_clk_info->max_clocks_state = pp_to_dc_powerlevel_state(pp_clk_info.max_clocks_state);
static_clk_info->max_mclk_khz = pp_clk_info.max_memory_clock * 10;
static_clk_info->max_sclk_khz = pp_clk_info.max_engine_clock * 10;
return true;
}
static void pp_rv_set_wm_ranges(struct pp_smu *pp,
struct pp_smu_wm_range_sets *ranges)
{

View File

@ -29,6 +29,8 @@
#include "dc.h"
#include "amdgpu_dm.h"
#include "modules/power/power_helpers.h"
#include "amdgpu_dm_kunit_helpers.h"
static bool link_supports_psrsu(struct dc_link *link)
{
@ -58,7 +60,8 @@ static bool link_supports_psrsu(struct dc_link *link)
return false;
}
static void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps)
STATIC_IFN_KUNIT
void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps)
{
struct dpcd_caps *dpcd_caps = &link->dpcd_caps;
unsigned int power_opts = 0;
@ -86,6 +89,7 @@ static void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps)
caps->rate_control_caps = 0; /* TODO: read in rc caps from aux */
caps->psr_power_opt_flag = power_opts;
}
EXPORT_IF_KUNIT(amdgpu_dm_psr_fill_caps);
/*
* amdgpu_dm_set_psr_caps() - set link psr capabilities

View File

@ -39,4 +39,9 @@ bool amdgpu_dm_psr_is_active_allowed(struct amdgpu_display_manager *dm);
bool amdgpu_dm_psr_set_event(struct amdgpu_display_manager *dm,
struct dc_stream_state *stream, bool set_event, enum psr_event event,
bool wait_for_disable);
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps);
#endif
#endif /* AMDGPU_DM_AMDGPU_DM_PSR_H_ */

View File

@ -31,6 +31,8 @@
#include "modules/power/power_helpers.h"
#include "dmub/inc/dmub_cmd.h"
#include "dc/inc/link_service.h"
#include "amdgpu_dm_kunit_helpers.h"
/*
* amdgpu_dm_link_supports_replay() - check if the link supports replay
@ -68,6 +70,7 @@ bool amdgpu_dm_link_supports_replay(struct dc_link *link, struct amdgpu_dm_conne
return true;
}
EXPORT_IF_KUNIT(amdgpu_dm_link_supports_replay);
/*
* amdgpu_dm_set_replay_caps() - setup Replay capabilities

View File

@ -0,0 +1,14 @@
CONFIG_KUNIT=y
CONFIG_PCI=y
CONFIG_DRM=y
CONFIG_DRM_AMDGPU=y
CONFIG_DRM_AMD_DC=y
CONFIG_DEBUG_FS=y
CONFIG_DRM_AMD_DC_KUNIT_TEST=y
CONFIG_FW_LOADER=y
CONFIG_DRM_KMS_HELPER=y
CONFIG_DRM_TTM=y
CONFIG_HWMON=y
CONFIG_I2C=y
CONFIG_POWER_SUPPLY=y
CONFIG_CRC16=y

View File

@ -0,0 +1,18 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for amdgpu_dm KUnit tests.
ccflags-y += -I$(src)/..
ccflags-y += -I$(src)/../..
ccflags-y += -I$(src)/../../include
ccflags-y += -I$(src)/../../modules/inc
ccflags-y += -I$(src)/../../dc
ccflags-y += -I$(src)/../../../amdgpu
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crc_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_hdcp_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_color_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_colorop_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_psr_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_replay_test.o
obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_ism_test.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,161 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_colorop.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include <drm/drm_colorop.h>
#include "amdgpu_dm_colorop.h"
/* Tests for amdgpu_dm_supported_degam_tfs */
static void dm_test_supported_degam_tfs_has_srgb_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs &
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF));
}
static void dm_test_supported_degam_tfs_has_pq125_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs &
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF));
}
static void dm_test_supported_degam_tfs_has_bt2020_inv_oetf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs &
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF));
}
static void dm_test_supported_degam_tfs_has_gamma22_inv(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs &
BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV));
}
static void dm_test_supported_degam_tfs_no_extra_bits(struct kunit *test)
{
u64 expected = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV);
KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_degam_tfs, expected);
}
/* Tests for amdgpu_dm_supported_shaper_tfs */
static void dm_test_supported_shaper_tfs_has_srgb_inv_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs &
BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF));
}
static void dm_test_supported_shaper_tfs_has_pq125_inv_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs &
BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF));
}
static void dm_test_supported_shaper_tfs_has_bt2020_oetf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs &
BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF));
}
static void dm_test_supported_shaper_tfs_has_gamma22(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs &
BIT(DRM_COLOROP_1D_CURVE_GAMMA22));
}
static void dm_test_supported_shaper_tfs_no_extra_bits(struct kunit *test)
{
u64 expected = BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF) |
BIT(DRM_COLOROP_1D_CURVE_GAMMA22);
KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_shaper_tfs, expected);
}
/* Tests for amdgpu_dm_supported_blnd_tfs */
static void dm_test_supported_blnd_tfs_has_srgb_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs &
BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF));
}
static void dm_test_supported_blnd_tfs_has_pq125_eotf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs &
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF));
}
static void dm_test_supported_blnd_tfs_has_bt2020_inv_oetf(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs &
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF));
}
static void dm_test_supported_blnd_tfs_has_gamma22_inv(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs &
BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV));
}
static void dm_test_supported_blnd_tfs_no_extra_bits(struct kunit *test)
{
u64 expected = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) |
BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) |
BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV);
KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_blnd_tfs, expected);
}
/* degam and blnd should support the same set of EOTF curves */
static void dm_test_degam_and_blnd_tfs_match(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_degam_tfs,
amdgpu_dm_supported_blnd_tfs);
}
static struct kunit_case dm_colorop_test_cases[] = {
/* degam TFs */
KUNIT_CASE(dm_test_supported_degam_tfs_has_srgb_eotf),
KUNIT_CASE(dm_test_supported_degam_tfs_has_pq125_eotf),
KUNIT_CASE(dm_test_supported_degam_tfs_has_bt2020_inv_oetf),
KUNIT_CASE(dm_test_supported_degam_tfs_has_gamma22_inv),
KUNIT_CASE(dm_test_supported_degam_tfs_no_extra_bits),
/* shaper TFs */
KUNIT_CASE(dm_test_supported_shaper_tfs_has_srgb_inv_eotf),
KUNIT_CASE(dm_test_supported_shaper_tfs_has_pq125_inv_eotf),
KUNIT_CASE(dm_test_supported_shaper_tfs_has_bt2020_oetf),
KUNIT_CASE(dm_test_supported_shaper_tfs_has_gamma22),
KUNIT_CASE(dm_test_supported_shaper_tfs_no_extra_bits),
/* blnd TFs */
KUNIT_CASE(dm_test_supported_blnd_tfs_has_srgb_eotf),
KUNIT_CASE(dm_test_supported_blnd_tfs_has_pq125_eotf),
KUNIT_CASE(dm_test_supported_blnd_tfs_has_bt2020_inv_oetf),
KUNIT_CASE(dm_test_supported_blnd_tfs_has_gamma22_inv),
KUNIT_CASE(dm_test_supported_blnd_tfs_no_extra_bits),
/* cross-check */
KUNIT_CASE(dm_test_degam_and_blnd_tfs_match),
{}
};
static struct kunit_suite dm_colorop_test_suite = {
.name = "amdgpu_dm_colorop",
.test_cases = dm_colorop_test_cases,
};
kunit_test_suite(dm_colorop_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_colorop");
MODULE_AUTHOR("AMD");

View File

@ -0,0 +1,121 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_crc.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include "amdgpu_dm_crc.h"
static void dm_test_parse_crc_source_none(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_NONE, dm_parse_crc_source("none"));
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_NONE, dm_parse_crc_source(NULL));
}
static void dm_test_parse_crc_source_crtc(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_CRTC, dm_parse_crc_source("crtc"));
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_CRTC, dm_parse_crc_source("auto"));
}
static void dm_test_parse_crc_source_dprx(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, dm_parse_crc_source("dprx"));
}
static void dm_test_parse_crc_source_crtc_dither(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER,
dm_parse_crc_source("crtc dither"));
}
static void dm_test_parse_crc_source_dprx_dither(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER,
dm_parse_crc_source("dprx dither"));
}
static void dm_test_parse_crc_source_invalid(struct kunit *test)
{
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_INVALID,
dm_parse_crc_source("invalid"));
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_INVALID,
dm_parse_crc_source("unknown"));
KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_INVALID,
dm_parse_crc_source(""));
}
static void dm_test_is_crc_source_crtc(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
KUNIT_EXPECT_TRUE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID));
}
static void dm_test_is_crc_source_dprx(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
KUNIT_EXPECT_TRUE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER));
KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID));
}
static void dm_test_need_crc_dither(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
KUNIT_EXPECT_TRUE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER));
KUNIT_EXPECT_TRUE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
KUNIT_EXPECT_FALSE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
KUNIT_EXPECT_FALSE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
KUNIT_EXPECT_FALSE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID));
}
static void dm_test_is_valid_crc_source(struct kunit *test)
{
KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC));
KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX));
KUNIT_EXPECT_TRUE(test,
amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER));
KUNIT_EXPECT_TRUE(test,
amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER));
KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_NONE));
KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_MAX));
KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID));
}
static struct kunit_case dm_crc_test_cases[] = {
KUNIT_CASE(dm_test_parse_crc_source_none),
KUNIT_CASE(dm_test_parse_crc_source_crtc),
KUNIT_CASE(dm_test_parse_crc_source_dprx),
KUNIT_CASE(dm_test_parse_crc_source_crtc_dither),
KUNIT_CASE(dm_test_parse_crc_source_dprx_dither),
KUNIT_CASE(dm_test_parse_crc_source_invalid),
KUNIT_CASE(dm_test_is_crc_source_crtc),
KUNIT_CASE(dm_test_is_crc_source_dprx),
KUNIT_CASE(dm_test_need_crc_dither),
KUNIT_CASE(dm_test_is_valid_crc_source),
{}
};
static struct kunit_suite dm_crc_test_suite = {
.name = "amdgpu_dm_crc",
.test_cases = dm_crc_test_cases,
};
kunit_test_suite(dm_crc_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_crc");
MODULE_AUTHOR("AMD");

View File

@ -0,0 +1,175 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_hdcp.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include <linux/workqueue.h>
#include "amdgpu_dm_hdcp.h"
static void dummy_work_fn(struct work_struct *work) {}
/* Tests for process_output() */
/*
* Helper: allocate and initialise a minimal hdcp_workqueue sufficient for
* process_output() testing. Only the three delayed works accessed by
* process_output() are initialised; everything else is zeroed.
*/
static struct hdcp_workqueue *alloc_test_workqueue(struct kunit *test)
{
struct hdcp_workqueue *work;
work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, work);
INIT_DELAYED_WORK(&work->callback_dwork, dummy_work_fn);
INIT_DELAYED_WORK(&work->watchdog_timer_dwork, dummy_work_fn);
INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn);
return work;
}
/*
* process_output() always schedules property_validate_dwork with delay=0,
* which queues the work item directly (bypassing the timer). Use
* work_pending() rather than delayed_work_pending() to detect this.
*/
static void dm_test_process_output_property_validate_always_scheduled(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
/* No flags set: only property_validate_dwork should be enqueued */
process_output(work);
KUNIT_EXPECT_TRUE(test, work_pending(&work->property_validate_dwork.work));
KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork));
KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork));
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.callback_needed=true must schedule callback_dwork.
*/
static void dm_test_process_output_callback_needed(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
work->output.callback_needed = true;
work->output.callback_delay = 500;
process_output(work);
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->callback_dwork));
cancel_delayed_work_sync(&work->callback_dwork);
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.callback_stop=true must cancel a previously scheduled callback_dwork.
*/
static void dm_test_process_output_callback_stop(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
/* Pre-schedule callback_dwork with a long delay so it won't fire. */
schedule_delayed_work(&work->callback_dwork, msecs_to_jiffies(10000));
KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->callback_dwork));
work->output.callback_stop = true;
process_output(work);
KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork));
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.watchdog_timer_needed=true must schedule watchdog_timer_dwork.
*/
static void dm_test_process_output_watchdog_needed(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
work->output.watchdog_timer_needed = true;
work->output.watchdog_timer_delay = 1000;
process_output(work);
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));
cancel_delayed_work_sync(&work->watchdog_timer_dwork);
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* output.watchdog_timer_stop=true must cancel a previously scheduled
* watchdog_timer_dwork.
*/
static void dm_test_process_output_watchdog_stop(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
/* Pre-schedule watchdog_timer_dwork with a long delay. */
schedule_delayed_work(&work->watchdog_timer_dwork, msecs_to_jiffies(10000));
KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));
work->output.watchdog_timer_stop = true;
process_output(work);
KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork));
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/*
* Both callback_needed and watchdog_timer_needed set: both dworks are
* scheduled independently.
*/
static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *test)
{
struct hdcp_workqueue *work = alloc_test_workqueue(test);
work->output.callback_needed = true;
work->output.callback_delay = 200;
work->output.watchdog_timer_needed = true;
work->output.watchdog_timer_delay = 800;
process_output(work);
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->callback_dwork));
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));
cancel_delayed_work_sync(&work->callback_dwork);
cancel_delayed_work_sync(&work->watchdog_timer_dwork);
cancel_delayed_work_sync(&work->property_validate_dwork);
}
/* End of tests for process_output() */
static struct kunit_case dm_hdcp_test_cases[] = {
KUNIT_CASE(dm_test_process_output_property_validate_always_scheduled),
KUNIT_CASE(dm_test_process_output_callback_needed),
KUNIT_CASE(dm_test_process_output_callback_stop),
KUNIT_CASE(dm_test_process_output_watchdog_needed),
KUNIT_CASE(dm_test_process_output_watchdog_stop),
KUNIT_CASE(dm_test_process_output_callback_and_watchdog_needed),
{}
};
static struct kunit_suite dm_hdcp_test_suite = {
.name = "amdgpu_dm_hdcp",
.test_cases = dm_hdcp_test_cases,
};
kunit_test_suite(dm_hdcp_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_hdcp");
MODULE_AUTHOR("AMD");

View File

@ -0,0 +1,636 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_ism.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include "dc.h"
#include "amdgpu_dm_ism.h"
/*
* Helper: allocate and zero-initialise a dc_stream_state for timing tests.
* Only the timing sub-struct is accessed by the functions under test.
*/
static struct dc_stream_state *alloc_test_stream(struct kunit *test)
{
struct dc_stream_state *stream;
stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, stream);
return stream;
}
/*
* Helper: allocate and zero-initialise an ISM instance.
*/
static struct amdgpu_dm_ism *alloc_test_ism(struct kunit *test)
{
struct amdgpu_dm_ism *ism;
ism = kunit_kzalloc(test, sizeof(*ism), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ism);
return ism;
}
/* ===== Tests for dm_ism_next_state — FULL_POWER_RUNNING transitions ===== */
static void dm_test_ism_next_state_running_enter_idle(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_RUNNING,
DM_ISM_EVENT_ENTER_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_WAITING);
}
static void dm_test_ism_next_state_running_begin_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_RUNNING,
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_BUSY);
}
static void dm_test_ism_next_state_running_invalid(struct kunit *test)
{
enum amdgpu_dm_ism_state next = DM_ISM_NUM_STATES;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_RUNNING,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_FALSE(test, ok);
/* next should remain untouched on invalid transition */
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_NUM_STATES);
}
/* ===== Tests for dm_ism_next_state — FULL_POWER_BUSY transitions ===== */
static void dm_test_ism_next_state_busy_enter_idle(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_BUSY,
DM_ISM_EVENT_ENTER_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY);
}
static void dm_test_ism_next_state_busy_end_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_BUSY,
DM_ISM_EVENT_END_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
/* ===== Tests for dm_ism_next_state — HYSTERESIS_WAITING transitions ===== */
static void dm_test_ism_next_state_hyst_wait_exit_idle(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_TIMER_ABORTED);
}
static void dm_test_ism_next_state_hyst_wait_begin_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING,
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY);
}
static void dm_test_ism_next_state_hyst_wait_timer(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING,
DM_ISM_EVENT_TIMER_ELAPSED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE);
}
static void dm_test_ism_next_state_hyst_wait_immediate(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING,
DM_ISM_EVENT_IMMEDIATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE);
}
/* ===== Tests for dm_ism_next_state — HYSTERESIS_BUSY transitions ===== */
static void dm_test_ism_next_state_hyst_busy_exit_idle(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_BUSY,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_BUSY);
}
static void dm_test_ism_next_state_hyst_busy_end_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_BUSY,
DM_ISM_EVENT_END_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_WAITING);
}
/* ===== Tests for dm_ism_next_state — OPTIMIZED_IDLE transitions ===== */
static void dm_test_ism_next_state_opt_idle_exit(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
static void dm_test_ism_next_state_opt_idle_begin_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE,
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY);
}
static void dm_test_ism_next_state_opt_idle_sso_timer(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE,
DM_ISM_EVENT_SSO_TIMER_ELAPSED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE_SSO);
}
static void dm_test_ism_next_state_opt_idle_immediate(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE,
DM_ISM_EVENT_IMMEDIATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE_SSO);
}
/* ===== Tests for dm_ism_next_state — OPTIMIZED_IDLE_SSO transitions ===== */
static void dm_test_ism_next_state_opt_idle_sso_exit(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE_SSO,
DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
static void dm_test_ism_next_state_opt_idle_sso_cursor(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE_SSO,
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY);
}
/* ===== Tests for dm_ism_next_state — TIMER_ABORTED transitions ===== */
static void dm_test_ism_next_state_aborted_immediate(struct kunit *test)
{
enum amdgpu_dm_ism_state next;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_TIMER_ABORTED,
DM_ISM_EVENT_IMMEDIATE, &next);
KUNIT_EXPECT_TRUE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING);
}
static void dm_test_ism_next_state_aborted_invalid(struct kunit *test)
{
enum amdgpu_dm_ism_state next = DM_ISM_NUM_STATES;
bool ok;
ok = dm_ism_next_state(DM_ISM_STATE_TIMER_ABORTED,
DM_ISM_EVENT_ENTER_IDLE_REQUESTED, &next);
KUNIT_EXPECT_FALSE(test, ok);
KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_NUM_STATES);
}
/* ===== Tests for dm_ism_get_sso_delay ===== */
static void dm_test_ism_sso_delay_null_stream(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
ism->config.sso_num_frames = 5;
KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, NULL), (uint64_t)0);
}
static void dm_test_ism_sso_delay_zero_frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.sso_num_frames = 0;
KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, stream), (uint64_t)0);
}
static void dm_test_ism_sso_delay_1080p60_3frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t expected_one_frame_ns, expected;
/*
* 1080p@60Hz: v_total=1125, h_total=2200, pix_clk=148.5MHz
* pix_clk_100hz = 1485000
* one_frame_ns = (1125 * 2200 * 10000000) / 1485000 = 16666666 ns
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.sso_num_frames = 3;
expected_one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
expected = 3 * expected_one_frame_ns;
KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, stream), expected);
}
static void dm_test_ism_sso_delay_4k60_1frame(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t expected_one_frame_ns;
/*
* 4K@60Hz: v_total=2250, h_total=4400, pix_clk=594MHz
* pix_clk_100hz = 5940000
*/
stream->timing.v_total = 2250;
stream->timing.h_total = 4400;
stream->timing.pix_clk_100hz = 5940000;
ism->config.sso_num_frames = 1;
expected_one_frame_ns = div64_u64((uint64_t)2250 * 4400 * 10000000ULL,
5940000);
KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, stream),
expected_one_frame_ns);
}
/* ===== Tests for dm_ism_get_idle_allow_delay ===== */
static void dm_test_ism_idle_delay_null_stream(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, NULL),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_zero_filter_frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.filter_num_frames = 0;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_zero_entry_count(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 0;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_zero_delay_frames(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 0;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_no_short_idles(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns;
/*
* All history records have long durations (well above the
* short_idle_ns threshold), so no delay should be applied.
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
ism->config.filter_old_history_threshold = 0;
/* Fill history with long idle durations */
for (int i = 0; i < 8; i++) {
ism->records[i].duration_ns = one_frame_ns * 100;
ism->records[i].timestamp_ns = 0;
}
ism->next_record_idx = 8;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_enough_short_idles(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns, expected;
/*
* Fill history with short idle durations that meet the threshold.
* filter_entry_count=3, so 3 short idles should trigger the delay.
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
ism->config.filter_old_history_threshold = 0;
/* Fill history with short idle durations (1 frame each) */
for (int i = 0; i < 8; i++) {
ism->records[i].duration_ns = one_frame_ns;
ism->records[i].timestamp_ns = 0;
}
ism->next_record_idx = 8;
expected = 10 * one_frame_ns;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
expected);
}
static void dm_test_ism_idle_delay_wraps_around_buffer(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns, expected;
/*
* Test the circular buffer wraparound: next_record_idx at 2 means
* the most recent records are at indices 1, 0, 15, 14, ...
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
ism->config.filter_old_history_threshold = 0;
/* Fill entire buffer with short idles */
for (int i = 0; i < AMDGPU_DM_IDLE_HIST_LEN; i++) {
ism->records[i].duration_ns = one_frame_ns;
ism->records[i].timestamp_ns = 0;
}
/* Position next_record_idx at 2 to test wraparound */
ism->next_record_idx = 2;
expected = 10 * one_frame_ns;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
expected);
}
static void dm_test_ism_idle_delay_old_history_cutoff(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns;
/*
* Test old_history_threshold: only recent entries within the
* threshold should be counted. Set up 2 recent short idles but
* require 3 older entries are outside the threshold.
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
/* Threshold: entries older than 20 frames are ignored */
ism->config.filter_old_history_threshold = 20;
ism->last_idle_timestamp_ns = one_frame_ns * 100;
/* 2 recent short idles (within threshold) */
ism->records[6].duration_ns = one_frame_ns;
ism->records[6].timestamp_ns = one_frame_ns * 95;
ism->records[7].duration_ns = one_frame_ns;
ism->records[7].timestamp_ns = one_frame_ns * 98;
/* Older entries outside the threshold with long durations */
for (int i = 0; i < 6; i++) {
ism->records[i].duration_ns = one_frame_ns * 100;
ism->records[i].timestamp_ns = one_frame_ns * 10;
}
ism->next_record_idx = 8;
/*
* Only 2 short idles within threshold, but 3 required
* should return 0 (no delay).
*/
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static void dm_test_ism_idle_delay_mixed_durations(struct kunit *test)
{
struct amdgpu_dm_ism *ism = alloc_test_ism(test);
struct dc_stream_state *stream = alloc_test_stream(test);
uint64_t one_frame_ns;
/*
* Mix of short and long idle durations. Only 2 short idles
* in 8 entries, but filter_entry_count=3, so no delay.
*/
stream->timing.v_total = 1125;
stream->timing.h_total = 2200;
stream->timing.pix_clk_100hz = 1485000;
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL,
1485000);
ism->config.filter_num_frames = 5;
ism->config.filter_entry_count = 3;
ism->config.activation_num_delay_frames = 10;
ism->config.filter_history_size = 8;
ism->config.filter_old_history_threshold = 0;
/* 2 short idles, 6 long idles */
for (int i = 0; i < 8; i++) {
if (i == 6 || i == 7)
ism->records[i].duration_ns = one_frame_ns;
else
ism->records[i].duration_ns = one_frame_ns * 100;
ism->records[i].timestamp_ns = 0;
}
ism->next_record_idx = 8;
KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream),
(uint64_t)0);
}
static struct kunit_case dm_ism_test_cases[] = {
/* dm_ism_next_state — FULL_POWER_RUNNING */
KUNIT_CASE(dm_test_ism_next_state_running_enter_idle),
KUNIT_CASE(dm_test_ism_next_state_running_begin_cursor),
KUNIT_CASE(dm_test_ism_next_state_running_invalid),
/* dm_ism_next_state — FULL_POWER_BUSY */
KUNIT_CASE(dm_test_ism_next_state_busy_enter_idle),
KUNIT_CASE(dm_test_ism_next_state_busy_end_cursor),
/* dm_ism_next_state — HYSTERESIS_WAITING */
KUNIT_CASE(dm_test_ism_next_state_hyst_wait_exit_idle),
KUNIT_CASE(dm_test_ism_next_state_hyst_wait_begin_cursor),
KUNIT_CASE(dm_test_ism_next_state_hyst_wait_timer),
KUNIT_CASE(dm_test_ism_next_state_hyst_wait_immediate),
/* dm_ism_next_state — HYSTERESIS_BUSY */
KUNIT_CASE(dm_test_ism_next_state_hyst_busy_exit_idle),
KUNIT_CASE(dm_test_ism_next_state_hyst_busy_end_cursor),
/* dm_ism_next_state — OPTIMIZED_IDLE */
KUNIT_CASE(dm_test_ism_next_state_opt_idle_exit),
KUNIT_CASE(dm_test_ism_next_state_opt_idle_begin_cursor),
KUNIT_CASE(dm_test_ism_next_state_opt_idle_sso_timer),
KUNIT_CASE(dm_test_ism_next_state_opt_idle_immediate),
/* dm_ism_next_state — OPTIMIZED_IDLE_SSO */
KUNIT_CASE(dm_test_ism_next_state_opt_idle_sso_exit),
KUNIT_CASE(dm_test_ism_next_state_opt_idle_sso_cursor),
/* dm_ism_next_state — TIMER_ABORTED */
KUNIT_CASE(dm_test_ism_next_state_aborted_immediate),
KUNIT_CASE(dm_test_ism_next_state_aborted_invalid),
/* dm_ism_get_sso_delay */
KUNIT_CASE(dm_test_ism_sso_delay_null_stream),
KUNIT_CASE(dm_test_ism_sso_delay_zero_frames),
KUNIT_CASE(dm_test_ism_sso_delay_1080p60_3frames),
KUNIT_CASE(dm_test_ism_sso_delay_4k60_1frame),
/* dm_ism_get_idle_allow_delay */
KUNIT_CASE(dm_test_ism_idle_delay_null_stream),
KUNIT_CASE(dm_test_ism_idle_delay_zero_filter_frames),
KUNIT_CASE(dm_test_ism_idle_delay_zero_entry_count),
KUNIT_CASE(dm_test_ism_idle_delay_zero_delay_frames),
KUNIT_CASE(dm_test_ism_idle_delay_no_short_idles),
KUNIT_CASE(dm_test_ism_idle_delay_enough_short_idles),
KUNIT_CASE(dm_test_ism_idle_delay_wraps_around_buffer),
KUNIT_CASE(dm_test_ism_idle_delay_old_history_cutoff),
KUNIT_CASE(dm_test_ism_idle_delay_mixed_durations),
{}
};
static struct kunit_suite dm_ism_test_suite = {
.name = "amdgpu_dm_ism",
.test_cases = dm_ism_test_cases,
};
kunit_test_suite(dm_ism_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_ism");
MODULE_AUTHOR("AMD");

View File

@ -0,0 +1,255 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_psr.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include "dc.h"
/* Extern declaration for the function under test */
extern void amdgpu_dm_psr_fill_caps(struct dc_link *link,
struct psr_caps *caps);
/*
* Helper: allocate and zero-initialise a dc_link sufficient for
* amdgpu_dm_psr_fill_caps() testing. The function only accesses
* embedded members (dpcd_caps, psr_settings) so no pointer fields
* need to be wired up.
*/
static struct dc_link *alloc_test_link(struct kunit *test)
{
struct dc_link *link;
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, link);
return link;
}
/* Tests for amdgpu_dm_psr_fill_caps() — PSR version mapping */
static void dm_test_psr_fill_caps_version_1(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
link->psr_settings.psr_version = DC_PSR_VERSION_1;
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_EQ(test, (int)caps.psr_version, 1);
}
static void dm_test_psr_fill_caps_version_su1(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
link->psr_settings.psr_version = DC_PSR_VERSION_SU_1;
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_EQ(test, (int)caps.psr_version, 2);
}
static void dm_test_psr_fill_caps_version_unsupported(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED;
amdgpu_dm_psr_fill_caps(link, &caps);
/*
* Neither DC_PSR_VERSION_1 nor DC_PSR_VERSION_SU_1,
* so psr_version stays at its zero-initialised value.
*/
KUNIT_EXPECT_EQ(test, (int)caps.psr_version, 0);
}
/* Tests for amdgpu_dm_psr_fill_caps() — RFB setup time */
static void dm_test_psr_fill_caps_setup_time_zero(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
/* PSR_SETUP_TIME = 0 → (6 - 0) * 55 = 330 */
link->dpcd_caps.psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME = 0;
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_EQ(test, caps.psr_rfb_setup_time, 330U);
}
static void dm_test_psr_fill_caps_setup_time_mid(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
/* PSR_SETUP_TIME = 3 → (6 - 3) * 55 = 165 */
link->dpcd_caps.psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME = 3;
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_EQ(test, caps.psr_rfb_setup_time, 165U);
}
static void dm_test_psr_fill_caps_setup_time_max(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
/* PSR_SETUP_TIME = 6 → (6 - 6) * 55 = 0 */
link->dpcd_caps.psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME = 6;
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_EQ(test, caps.psr_rfb_setup_time, 0U);
}
/* Tests for amdgpu_dm_psr_fill_caps() — link training flag */
static void dm_test_psr_fill_caps_link_training_required(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED = 0;
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_TRUE(test, caps.psr_exit_link_training_required);
}
static void dm_test_psr_fill_caps_link_training_not_required(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED = 1;
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_FALSE(test, caps.psr_exit_link_training_required);
}
/* Tests for amdgpu_dm_psr_fill_caps() — DPCD field passthrough */
static void dm_test_psr_fill_caps_dpcd_fields(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
link->dpcd_caps.edp_rev = 0x14;
link->dpcd_caps.psr_info.psr_version = 2;
link->dpcd_caps.psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED = 1;
link->dpcd_caps.psr_info.psr_dpcd_caps.bits.Y_COORDINATE_REQUIRED = 1;
link->dpcd_caps.psr_info.psr2_su_y_granularity_cap = 4;
link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 1;
link->dpcd_caps.alpm_caps.bits.PM_STATE_2A_SUPPORT = 1;
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_EQ(test, (int)caps.edp_revision, 0x14);
KUNIT_EXPECT_EQ(test, (int)caps.support_ver, 2);
KUNIT_EXPECT_TRUE(test, caps.su_granularity_required);
KUNIT_EXPECT_TRUE(test, caps.y_coordinate_required);
KUNIT_EXPECT_EQ(test, (int)caps.su_y_granularity, 4);
KUNIT_EXPECT_TRUE(test, caps.alpm_cap);
KUNIT_EXPECT_TRUE(test, caps.standby_support);
}
static void dm_test_psr_fill_caps_dpcd_fields_unset(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0xFF, sizeof(caps));
/* All dpcd_caps fields are zero from kzalloc */
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_EQ(test, (int)caps.edp_revision, 0);
KUNIT_EXPECT_EQ(test, (int)caps.support_ver, 0);
KUNIT_EXPECT_FALSE(test, caps.su_granularity_required);
KUNIT_EXPECT_FALSE(test, caps.y_coordinate_required);
KUNIT_EXPECT_EQ(test, (int)caps.su_y_granularity, 0);
KUNIT_EXPECT_FALSE(test, caps.alpm_cap);
KUNIT_EXPECT_FALSE(test, caps.standby_support);
}
/* Tests for amdgpu_dm_psr_fill_caps() — rate control and power opts */
static void dm_test_psr_fill_caps_rate_control_always_zero(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
/* Pre-fill caps with non-zero to verify overwrite */
memset(&caps, 0xFF, sizeof(caps));
amdgpu_dm_psr_fill_caps(link, &caps);
KUNIT_EXPECT_EQ(test, (int)caps.rate_control_caps, 0);
}
static void dm_test_psr_fill_caps_power_opts_z10_always_set(struct kunit *test)
{
struct dc_link *link = alloc_test_link(test);
struct psr_caps caps;
memset(&caps, 0, sizeof(caps));
amdgpu_dm_psr_fill_caps(link, &caps);
/*
* psr_power_opt_z10_static_screen is always added to power_opts
* regardless of amdgpu_dc_feature_mask.
*/
KUNIT_EXPECT_TRUE(test,
(caps.psr_power_opt_flag &
psr_power_opt_z10_static_screen) != 0);
}
/* End of tests for amdgpu_dm_psr_fill_caps() */
static struct kunit_case dm_psr_test_cases[] = {
KUNIT_CASE(dm_test_psr_fill_caps_version_1),
KUNIT_CASE(dm_test_psr_fill_caps_version_su1),
KUNIT_CASE(dm_test_psr_fill_caps_version_unsupported),
KUNIT_CASE(dm_test_psr_fill_caps_setup_time_zero),
KUNIT_CASE(dm_test_psr_fill_caps_setup_time_mid),
KUNIT_CASE(dm_test_psr_fill_caps_setup_time_max),
KUNIT_CASE(dm_test_psr_fill_caps_link_training_required),
KUNIT_CASE(dm_test_psr_fill_caps_link_training_not_required),
KUNIT_CASE(dm_test_psr_fill_caps_dpcd_fields),
KUNIT_CASE(dm_test_psr_fill_caps_dpcd_fields_unset),
KUNIT_CASE(dm_test_psr_fill_caps_rate_control_always_zero),
KUNIT_CASE(dm_test_psr_fill_caps_power_opts_z10_always_set),
{}
};
static struct kunit_suite dm_psr_test_suite = {
.name = "amdgpu_dm_psr",
.test_cases = dm_psr_test_cases,
};
kunit_test_suite(dm_psr_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_psr");
MODULE_AUTHOR("AMD");

View File

@ -0,0 +1,206 @@
// SPDX-License-Identifier: GPL-2.0 OR MIT
/*
* KUnit tests for amdgpu_dm_replay.c
*
* Copyright 2026 Advanced Micro Devices, Inc.
*/
#include <kunit/test.h>
#include "dc.h"
#include "amdgpu_mode.h"
#include "amdgpu_dm.h"
/* Extern declaration for the function under test */
extern bool amdgpu_dm_link_supports_replay(struct dc_link *link,
struct amdgpu_dm_connector *aconnector);
/*
* Helper: allocate a dc_link, amdgpu_dm_connector, and dm_connector_state
* wired up so that to_dm_connector_state(aconnector->base.state) works.
*/
struct replay_test_ctx {
struct dc_link *link;
struct amdgpu_dm_connector *aconnector;
struct dm_connector_state *dm_state;
};
static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test)
{
struct replay_test_ctx *ctx;
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx);
ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->link);
ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector);
ctx->dm_state = kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state);
/* Wire connector state so to_dm_connector_state() works */
ctx->aconnector->base.state = &ctx->dm_state->base;
return ctx;
}
/*
* Helper: set all conditions for replay support to pass so individual
* tests can disable one condition at a time.
*/
static void set_all_replay_caps(struct replay_test_ctx *ctx)
{
ctx->dm_state->freesync_capable = true;
ctx->aconnector->vsdb_info.replay_mode = true;
ctx->link->dpcd_caps.edp_rev = EDP_REVISION_13;
ctx->link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 1;
ctx->link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1;
ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 1;
ctx->link->dpcd_caps.pr_info.max_deviation_line = 1;
}
/* Tests for amdgpu_dm_link_supports_replay() — all caps met */
static void dm_test_replay_supports_all_caps(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
KUNIT_EXPECT_TRUE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — freesync not capable */
static void dm_test_replay_no_freesync(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->dm_state->freesync_capable = false;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — no replay mode in VSDB */
static void dm_test_replay_no_vsdb_replay_mode(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->aconnector->vsdb_info.replay_mode = false;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — eDP revision too low */
static void dm_test_replay_edp_rev_too_low(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.edp_rev = EDP_REVISION_12;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — no ALPM AUX wake cap */
static void dm_test_replay_no_alpm_aux_wake(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — no adaptive sync SDP */
static void dm_test_replay_no_adaptive_sync_sdp(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — zero pixel deviation */
static void dm_test_replay_zero_pixel_deviation(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — zero max deviation line */
static void dm_test_replay_zero_max_deviation_line(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.pr_info.max_deviation_line = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* Tests for amdgpu_dm_link_supports_replay() — both deviation fields zero */
static void dm_test_replay_both_deviations_zero(struct kunit *test)
{
struct replay_test_ctx *ctx = alloc_replay_ctx(test);
set_all_replay_caps(ctx);
ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 0;
ctx->link->dpcd_caps.pr_info.max_deviation_line = 0;
KUNIT_EXPECT_FALSE(test,
amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector));
}
/* End of tests for amdgpu_dm_link_supports_replay() */
static struct kunit_case dm_replay_test_cases[] = {
KUNIT_CASE(dm_test_replay_supports_all_caps),
KUNIT_CASE(dm_test_replay_no_freesync),
KUNIT_CASE(dm_test_replay_no_vsdb_replay_mode),
KUNIT_CASE(dm_test_replay_edp_rev_too_low),
KUNIT_CASE(dm_test_replay_no_alpm_aux_wake),
KUNIT_CASE(dm_test_replay_no_adaptive_sync_sdp),
KUNIT_CASE(dm_test_replay_zero_pixel_deviation),
KUNIT_CASE(dm_test_replay_zero_max_deviation_line),
KUNIT_CASE(dm_test_replay_both_deviations_zero),
{}
};
static struct kunit_suite dm_replay_test_suite = {
.name = "amdgpu_dm_replay",
.test_cases = dm_replay_test_cases,
};
kunit_test_suite(dm_replay_test_suite);
MODULE_LICENSE("Dual MIT/GPL");
MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_replay");
MODULE_AUTHOR("AMD");

View File

@ -2419,15 +2419,6 @@ static enum bp_result get_integrated_info_v8(
info->dentist_vco_freq = le32_to_cpu(info_v8->ulDentistVCOFreq) * 10;
info->boot_up_uma_clock = le32_to_cpu(info_v8->ulBootUpUMAClock) * 10;
for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) {
/* Convert [10KHz] into [KHz] */
info->disp_clk_voltage[i].max_supported_clk =
le32_to_cpu(info_v8->sDISPCLK_Voltage[i].
ulMaximumSupportedCLK) * 10;
info->disp_clk_voltage[i].voltage_index =
le32_to_cpu(info_v8->sDISPCLK_Voltage[i].ulVoltageIndex);
}
info->boot_up_req_display_vector =
le32_to_cpu(info_v8->ulBootUpReqDisplayVector);
info->gpu_cap_info =
@ -2570,14 +2561,6 @@ static enum bp_result get_integrated_info_v9(
info->dentist_vco_freq = le32_to_cpu(info_v9->ulDentistVCOFreq) * 10;
info->boot_up_uma_clock = le32_to_cpu(info_v9->ulBootUpUMAClock) * 10;
for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) {
/* Convert [10KHz] into [KHz] */
info->disp_clk_voltage[i].max_supported_clk =
le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulMaximumSupportedCLK) * 10;
info->disp_clk_voltage[i].voltage_index =
le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulVoltageIndex);
}
info->boot_up_req_display_vector =
le32_to_cpu(info_v9->ulBootUpReqDisplayVector);
info->gpu_cap_info = le32_to_cpu(info_v9->ulGPUCapInfo);
@ -2719,25 +2702,6 @@ static enum bp_result construct_integrated_info(
}
}
/* Sort voltage table from low to high*/
if (result == BP_RESULT_OK) {
int32_t i;
int32_t j;
for (i = 1; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) {
for (j = i; j > 0; --j) {
if (
info->disp_clk_voltage[j].max_supported_clk <
info->disp_clk_voltage[j-1].max_supported_clk) {
/* swap j and j - 1*/
swap(info->disp_clk_voltage[j - 1],
info->disp_clk_voltage[j]);
}
}
}
}
return result;
}

View File

@ -2671,15 +2671,6 @@ static enum bp_result get_integrated_info_v11(
info->dentist_vco_freq = le32_to_cpu(info_v11->ulDentistVCOFreq) * 10;
info->boot_up_uma_clock = le32_to_cpu(info_v8->ulBootUpUMAClock) * 10;
for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) {
/* Convert [10KHz] into [KHz] */
info->disp_clk_voltage[i].max_supported_clk =
le32_to_cpu(info_v11->sDISPCLK_Voltage[i].
ulMaximumSupportedCLK) * 10;
info->disp_clk_voltage[i].voltage_index =
le32_to_cpu(info_v11->sDISPCLK_Voltage[i].ulVoltageIndex);
}
info->boot_up_req_display_vector =
le32_to_cpu(info_v11->ulBootUpReqDisplayVector);
info->boot_up_nb_voltage =
@ -3032,7 +3023,6 @@ static enum bp_result construct_integrated_info(
struct atom_data_revision revision;
int32_t i;
int32_t j;
if (!info)
return result;
@ -3134,14 +3124,6 @@ static enum bp_result construct_integrated_info(
DC_LOG_BIOS("driver forced fixdpvoltageswing = %d\n", info->ext_disp_conn_info.fixdpvoltageswing);
}
}
/* Sort voltage table from low to high*/
for (i = 1; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) {
for (j = i; j > 0; --j) {
if (info->disp_clk_voltage[j].max_supported_clk <
info->disp_clk_voltage[j-1].max_supported_clk)
swap(info->disp_clk_voltage[j-1], info->disp_clk_voltage[j]);
}
}
return result;
}

View File

@ -62,32 +62,6 @@ static const struct clk_mgr_mask disp_clk_mask = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK)
};
/* Max clock values for each state indexed by "enum clocks_state": */
static const struct state_dependent_clocks dce60_max_clks_by_state[] = {
/* ClocksStateInvalid - should not be used */
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/* ClocksStateUltraLow - not expected to be used for DCE 6.0 */
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/* ClocksStateLow */
{ .display_clk_khz = 352000, .pixel_clk_khz = 330000},
/* ClocksStateNominal */
{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 },
/* ClocksStatePerformance */
{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 } };
/* Max clock values for each state indexed by "enum clocks_state": */
static const struct state_dependent_clocks dce80_max_clks_by_state[] = {
/* ClocksStateInvalid - should not be used */
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/* ClocksStateUltraLow - not expected to be used for DCE 8.0 */
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/* ClocksStateLow */
{ .display_clk_khz = 352000, .pixel_clk_khz = 330000},
/* ClocksStateNominal */
{ .display_clk_khz = 625000, .pixel_clk_khz = 400000 },
/* ClocksStatePerformance */
{ .display_clk_khz = 625000, .pixel_clk_khz = 400000 } };
unsigned int dentist_get_divider_from_did(unsigned int did)
{
if (did < DENTIST_BASE_DID_1)
@ -220,40 +194,6 @@ uint32_t dce_get_max_pixel_clock_for_all_paths(struct dc_state *context)
return max_pix_clk;
}
enum dm_pp_clocks_state dce_get_required_clocks_state(
struct clk_mgr *clk_mgr_base,
struct dc_state *context)
{
struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base);
int i;
enum dm_pp_clocks_state low_req_clk;
int max_pix_clk = dce_get_max_pixel_clock_for_all_paths(context);
/* Iterate from highest supported to lowest valid state, and update
* lowest RequiredState with the lowest state that satisfies
* all required clocks
*/
for (i = clk_mgr_dce->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; i--)
if (context->bw_ctx.bw.dce.dispclk_khz >
clk_mgr_dce->max_clks_by_state[i].display_clk_khz
|| max_pix_clk >
clk_mgr_dce->max_clks_by_state[i].pixel_clk_khz)
break;
low_req_clk = i + 1;
if (low_req_clk > clk_mgr_dce->max_clks_state) {
/* set max clock state for high phyclock, invalid on exceeding display clock */
if (clk_mgr_dce->max_clks_by_state[clk_mgr_dce->max_clks_state].display_clk_khz
< context->bw_ctx.bw.dce.dispclk_khz)
low_req_clk = DM_PP_CLOCKS_STATE_INVALID;
else
low_req_clk = clk_mgr_dce->max_clks_state;
}
return low_req_clk;
}
/* TODO: remove use the two broken down functions */
int dce_set_clock(
struct clk_mgr *clk_mgr_base,
@ -291,11 +231,6 @@ int dce_set_clock(
actual_clock = pxl_clk_params.dfs_bypass_display_clock;
}
/* from power down, we need mark the clock state as ClocksStateNominal
* from HWReset, so when resume we will call pplib voltage regulator.*/
if (requested_clk_khz == 0)
clk_mgr_dce->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu))
dmcu->funcs->set_psr_wait_loop(dmcu, actual_clock / 1000 / 7);
@ -307,7 +242,6 @@ static void dce_clock_read_integrated_info(struct clk_mgr_internal *clk_mgr_dce)
{
struct dc_debug_options *debug = &clk_mgr_dce->base.ctx->dc->debug;
struct dc_bios *bp = clk_mgr_dce->base.ctx->dc_bios;
int i;
if (bp->integrated_info)
clk_mgr_dce->base.dentist_vco_freq_khz = bp->integrated_info->dentist_vco_freq;
@ -317,40 +251,6 @@ static void dce_clock_read_integrated_info(struct clk_mgr_internal *clk_mgr_dce)
clk_mgr_dce->base.dentist_vco_freq_khz = 3600000;
}
/*update the maximum display clock for each power state*/
for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) {
enum dm_pp_clocks_state clk_state = DM_PP_CLOCKS_STATE_INVALID;
switch (i) {
case 0:
clk_state = DM_PP_CLOCKS_STATE_ULTRA_LOW;
break;
case 1:
clk_state = DM_PP_CLOCKS_STATE_LOW;
break;
case 2:
clk_state = DM_PP_CLOCKS_STATE_NOMINAL;
break;
case 3:
clk_state = DM_PP_CLOCKS_STATE_PERFORMANCE;
break;
default:
clk_state = DM_PP_CLOCKS_STATE_INVALID;
break;
}
/*Do not allow bad VBIOS/SBIOS to override with invalid values,
* check for > 100MHz*/
if (bp->integrated_info)
if (bp->integrated_info->disp_clk_voltage[i].max_supported_clk >= 100000)
clk_mgr_dce->max_clks_by_state[clk_state].display_clk_khz =
bp->integrated_info->disp_clk_voltage[i].max_supported_clk;
}
if (!debug->disable_dfs_bypass && bp->integrated_info)
if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE)
clk_mgr_dce->dfs_bypass_enabled = true;
@ -430,20 +330,9 @@ static void dce_update_clocks(struct clk_mgr *clk_mgr_base,
struct dc_state *context,
bool safe_to_lower)
{
struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base);
struct dm_pp_power_level_change_request level_change_req;
const int max_disp_clk =
clk_mgr_dce->max_clks_by_state[DM_PP_CLOCKS_STATE_PERFORMANCE].display_clk_khz;
const int max_disp_clk = clk_mgr_base->clks.max_supported_dispclk_khz;
int patched_disp_clk = MIN(max_disp_clk, context->bw_ctx.bw.dce.dispclk_khz);
level_change_req.power_level = dce_get_required_clocks_state(clk_mgr_base, context);
/* get max clock state from PPLIB */
if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower)
|| level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) {
if (dm_pp_apply_power_level_change_request(clk_mgr_base->ctx, &level_change_req))
clk_mgr_dce->cur_min_clks_state = level_change_req.power_level;
}
if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr_base->clks.dispclk_khz)) {
patched_disp_clk = dce_set_clock(clk_mgr_base, patched_disp_clk);
clk_mgr_base->clks.dispclk_khz = patched_disp_clk;
@ -468,17 +357,6 @@ void dce_clk_mgr_construct(
struct clk_mgr_internal *clk_mgr)
{
struct clk_mgr *base = &clk_mgr->base;
struct dm_pp_static_clock_info static_clk_info = {0};
if (ctx->dce_version <= DCE_VERSION_6_4)
memcpy(clk_mgr->max_clks_by_state,
dce60_max_clks_by_state,
sizeof(dce60_max_clks_by_state));
else
memcpy(clk_mgr->max_clks_by_state,
dce80_max_clks_by_state,
sizeof(dce80_max_clks_by_state));
base->ctx = ctx;
base->funcs = &dce_funcs;
@ -494,16 +372,16 @@ void dce_clk_mgr_construct(
clk_mgr->dprefclk_ss_divider = 1000;
clk_mgr->ss_on_dprefclk = false;
if (ctx->dce_version >= DCE_VERSION_8_0) {
if (dm_pp_get_static_clocks(ctx, &static_clk_info))
clk_mgr->max_clks_state = static_clk_info.max_clocks_state;
else
clk_mgr->max_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
clk_mgr->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID;
}
base->clks.max_supported_dispclk_khz =
clk_mgr->max_clks_by_state[DM_PP_CLOCKS_STATE_PERFORMANCE].display_clk_khz;
if (ctx->dce_version >= DCE_VERSION_12_0)
base->clks.max_supported_dispclk_khz = 1133000;
else if (ctx->dce_version >= DCE_VERSION_11_2)
base->clks.max_supported_dispclk_khz = 1132000;
else if (ctx->dce_version >= DCE_VERSION_11_0)
base->clks.max_supported_dispclk_khz = 643000;
else if (ctx->dce_version >= DCE_VERSION_8_0)
base->clks.max_supported_dispclk_khz = 625000;
else
base->clks.max_supported_dispclk_khz = 600000;
dce_clock_read_integrated_info(clk_mgr);
dce_clock_read_ss_info(clk_mgr);

View File

@ -32,9 +32,6 @@
/* functions shared by other dce clk mgrs */
int dce_adjust_dp_ref_freq_for_ss(struct clk_mgr_internal *clk_mgr_dce, int dp_ref_clk_khz);
int dce_get_dp_ref_freq_khz(struct clk_mgr *clk_mgr_base);
enum dm_pp_clocks_state dce_get_required_clocks_state(
struct clk_mgr *clk_mgr_base,
struct dc_state *context);
uint32_t dce_get_max_pixel_clock_for_all_paths(struct dc_state *context);

View File

@ -51,18 +51,6 @@ static const struct clk_mgr_mask disp_clk_mask = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK)
};
static const struct state_dependent_clocks dce110_max_clks_by_state[] = {
/*ClocksStateInvalid - should not be used*/
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/
{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 },
/*ClocksStateLow*/
{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 },
/*ClocksStateNominal*/
{ .display_clk_khz = 467000, .pixel_clk_khz = 400000 },
/*ClocksStatePerformance*/
{ .display_clk_khz = 643000, .pixel_clk_khz = 400000 } };
static uint32_t determine_sclk_from_bounding_box(
const struct dc *dc,
uint32_t required_sclk)
@ -257,21 +245,12 @@ static void dce11_update_clocks(struct clk_mgr *clk_mgr_base,
bool safe_to_lower)
{
struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base);
struct dm_pp_power_level_change_request level_change_req;
int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz;
/*TODO: W/A for dal3 linux, investigate why this works */
if (!clk_mgr_dce->dfs_bypass_active)
patched_disp_clk = patched_disp_clk * 115 / 100;
level_change_req.power_level = dce_get_required_clocks_state(clk_mgr_base, context);
/* get max clock state from PPLIB */
if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower)
|| level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) {
if (dm_pp_apply_power_level_change_request(clk_mgr_base->ctx, &level_change_req))
clk_mgr_dce->cur_min_clks_state = level_change_req.power_level;
}
if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr_base->clks.dispclk_khz)) {
context->bw_ctx.bw.dce.dispclk_khz = dce_set_clock(clk_mgr_base, patched_disp_clk);
clk_mgr_base->clks.dispclk_khz = patched_disp_clk;
@ -290,10 +269,6 @@ void dce110_clk_mgr_construct(
{
dce_clk_mgr_construct(ctx, clk_mgr);
memcpy(clk_mgr->max_clks_by_state,
dce110_max_clks_by_state,
sizeof(dce110_max_clks_by_state));
clk_mgr->regs = &disp_clk_regs;
clk_mgr->clk_mgr_shift = &disp_clk_shift;
clk_mgr->clk_mgr_mask = &disp_clk_mask;

View File

@ -53,19 +53,6 @@ static const struct clk_mgr_mask disp_clk_mask = {
CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK)
};
static const struct state_dependent_clocks dce112_max_clks_by_state[] = {
/*ClocksStateInvalid - should not be used*/
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/
{ .display_clk_khz = 389189, .pixel_clk_khz = 346672 },
/*ClocksStateLow*/
{ .display_clk_khz = 459000, .pixel_clk_khz = 400000 },
/*ClocksStateNominal*/
{ .display_clk_khz = 667000, .pixel_clk_khz = 600000 },
/*ClocksStatePerformance*/
{ .display_clk_khz = 1132000, .pixel_clk_khz = 600000 } };
//TODO: remove use the two broken down functions
int dce112_set_clock(struct clk_mgr *clk_mgr_base, int requested_clk_khz)
{
@ -89,13 +76,6 @@ int dce112_set_clock(struct clk_mgr *clk_mgr_base, int requested_clk_khz)
bp->funcs->set_dce_clock(bp, &dce_clk_params);
actual_clock = dce_clk_params.target_clock_frequency;
/*
* from power down, we need mark the clock state as ClocksStateNominal
* from HWReset, so when resume we will call pplib voltage regulator.
*/
if (requested_clk_khz == 0)
clk_mgr_dce->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
/*Program DP ref Clock*/
/*VBIOS will determine DPREFCLK frequency, so we don't set it*/
dce_clk_params.target_clock_frequency = 0;
@ -143,14 +123,6 @@ int dce112_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_clk_khz)
bp->funcs->set_dce_clock(bp, &dce_clk_params);
actual_clock = dce_clk_params.target_clock_frequency;
/*
* from power down, we need mark the clock state as ClocksStateNominal
* from HWReset, so when resume we will call pplib voltage regulator.
*/
if (requested_clk_khz == 0)
clk_mgr->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL;
if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu)) {
if (clk_mgr->dfs_bypass_disp_clk != actual_clock)
dmcu->funcs->set_psr_wait_loop(dmcu,
@ -193,21 +165,12 @@ static void dce112_update_clocks(struct clk_mgr *clk_mgr_base,
bool safe_to_lower)
{
struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base);
struct dm_pp_power_level_change_request level_change_req;
int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz;
/*TODO: W/A for dal3 linux, investigate why this works */
if (!clk_mgr_dce->dfs_bypass_active)
patched_disp_clk = patched_disp_clk * 115 / 100;
level_change_req.power_level = dce_get_required_clocks_state(clk_mgr_base, context);
/* get max clock state from PPLIB */
if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower)
|| level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) {
if (dm_pp_apply_power_level_change_request(clk_mgr_base->ctx, &level_change_req))
clk_mgr_dce->cur_min_clks_state = level_change_req.power_level;
}
if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr_base->clks.dispclk_khz)) {
patched_disp_clk = dce112_set_clock(clk_mgr_base, patched_disp_clk);
clk_mgr_base->clks.dispclk_khz = patched_disp_clk;
@ -226,10 +189,6 @@ void dce112_clk_mgr_construct(
{
dce_clk_mgr_construct(ctx, clk_mgr);
memcpy(clk_mgr->max_clks_by_state,
dce112_max_clks_by_state,
sizeof(dce112_max_clks_by_state));
clk_mgr->regs = &disp_clk_regs;
clk_mgr->clk_mgr_shift = &disp_clk_shift;
clk_mgr->clk_mgr_mask = &disp_clk_mask;

View File

@ -32,18 +32,6 @@
#include "dce100/dce_clk_mgr.h"
#include "dce120/dce120_hwseq.h"
static const struct state_dependent_clocks dce120_max_clks_by_state[] = {
/*ClocksStateInvalid - should not be used*/
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/
{ .display_clk_khz = 0, .pixel_clk_khz = 0 },
/*ClocksStateLow*/
{ .display_clk_khz = 460000, .pixel_clk_khz = 400000 },
/*ClocksStateNominal*/
{ .display_clk_khz = 670000, .pixel_clk_khz = 600000 },
/*ClocksStatePerformance*/
{ .display_clk_khz = 1133000, .pixel_clk_khz = 600000 } };
/**
* dce121_clock_patch_xgmi_ss_info() - Save XGMI spread spectrum info
* @clk_mgr_dce: clock manager internal structure
@ -129,10 +117,6 @@ void dce120_clk_mgr_construct(struct dc_context *ctx, struct clk_mgr_internal *c
{
dce_clk_mgr_construct(ctx, clk_mgr);
memcpy(clk_mgr->max_clks_by_state,
dce120_max_clks_by_state,
sizeof(dce120_max_clks_by_state));
clk_mgr->base.dprefclk_khz = 600000;
clk_mgr->base.funcs = &dce120_funcs;
}

View File

@ -272,9 +272,9 @@ char *dc_status_to_str(enum dc_status status)
return "Fail DP Tunnel BW validation";
case DC_ERROR_UNEXPECTED:
return "Unexpected error";
default:
return "Unexpected status error";
}
return "Unexpected status error";
}
char *dc_pixel_encoding_to_str(enum dc_pixel_encoding pixel_encoding)

View File

@ -105,15 +105,26 @@ static void dccg21_update_dpp_dto(struct dccg *dccg, int dpp_inst, int req_dppcl
* dccg2_init() unconditionally overwrites MICROSECOND_TIME_BASE_DIV to
* 0x00120264, destroying the marker before it can be read.
*
* Guard the call: if the S0i3 marker is present, skip dccg2_init() so the
* Guard the call: if the S0i3 marker is present, skip init so the
* WA can function correctly. bios_golden_init() will handle init in that case.
*
* DCN21 uses 48MHz refclk, not 100MHz, so we must explicitly set the correct
* values (48MHz is taken from rn_clk_mgr_construct()).
*/
static void dccg21_init(struct dccg *dccg)
{
struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
if (dccg2_is_s0i3_golden_init_wa_done(dccg))
return;
dccg2_init(dccg);
/* 48MHz refclk from rn_clk_mgr_construct() */
REG_WRITE(MICROSECOND_TIME_BASE_DIV, 0x00120230);
REG_WRITE(MILLISECOND_TIME_BASE_DIV, 0x0010bb80);
REG_WRITE(DISPCLK_FREQ_CHANGE_CNTL, 0x0e01003c);
if (REG(REFCLK_CNTL))
REG_WRITE(REFCLK_CNTL, 0);
}
static const struct dccg_funcs dccg21_funcs = {

View File

@ -224,18 +224,10 @@ bool dm_pp_apply_display_requirements(
const struct dc_context *ctx,
const struct dm_pp_display_configuration *pp_display_cfg);
bool dm_pp_apply_power_level_change_request(
const struct dc_context *ctx,
struct dm_pp_power_level_change_request *level_change_req);
bool dm_pp_apply_clock_for_voltage_request(
const struct dc_context *ctx,
struct dm_pp_clock_for_voltage_req *clock_for_voltage_req);
bool dm_pp_get_static_clocks(
const struct dc_context *ctx,
struct dm_pp_static_clock_info *static_clk_info);
/****** end of PP interfaces ******/
struct persistent_data_flag {

View File

@ -36,30 +36,7 @@ struct dm_pp_clock_range {
int max_khz;
};
enum dm_pp_clocks_state {
DM_PP_CLOCKS_STATE_INVALID,
DM_PP_CLOCKS_STATE_ULTRA_LOW,
DM_PP_CLOCKS_STATE_LOW,
DM_PP_CLOCKS_STATE_NOMINAL,
DM_PP_CLOCKS_STATE_PERFORMANCE,
/* Starting from DCE11, Max 8 levels of DPM state supported. */
DM_PP_CLOCKS_DPM_STATE_LEVEL_INVALID = DM_PP_CLOCKS_STATE_INVALID,
DM_PP_CLOCKS_DPM_STATE_LEVEL_0,
DM_PP_CLOCKS_DPM_STATE_LEVEL_1,
DM_PP_CLOCKS_DPM_STATE_LEVEL_2,
/* to be backward compatible */
DM_PP_CLOCKS_DPM_STATE_LEVEL_3,
DM_PP_CLOCKS_DPM_STATE_LEVEL_4,
DM_PP_CLOCKS_DPM_STATE_LEVEL_5,
DM_PP_CLOCKS_DPM_STATE_LEVEL_6,
DM_PP_CLOCKS_DPM_STATE_LEVEL_7,
DM_PP_CLOCKS_MAX_STATES
};
struct dm_pp_gpu_clock_range {
enum dm_pp_clocks_state clock_state;
struct dm_pp_clock_range sclk;
struct dm_pp_clock_range mclk;
struct dm_pp_clock_range eclk;
@ -246,10 +223,6 @@ enum dm_acpi_display_type {
AcpiDisplayType_DFP6 = 12
};
struct dm_pp_power_level_change_request {
enum dm_pp_clocks_state power_level;
};
struct dm_pp_clock_for_voltage_req {
enum dm_pp_clock_type clk_type;
uint32_t clocks_in_khz;
@ -258,9 +231,6 @@ struct dm_pp_clock_for_voltage_req {
struct dm_pp_static_clock_info {
uint32_t max_sclk_khz;
uint32_t max_mclk_khz;
/* max possible display block clocks state */
enum dm_pp_clocks_state max_clocks_state;
};
struct dtn_min_clk_info {

View File

@ -405,12 +405,6 @@ enum clock_type {
clock_type_dtbclk,
};
struct state_dependent_clocks {
int display_clk_khz;
int pixel_clk_khz;
};
struct clk_mgr_internal {
struct clk_mgr base;
int smu_ver;
@ -429,8 +423,6 @@ struct clk_mgr_internal {
const struct clk_mgr_shift *clk_mgr_shift;
const struct clk_mgr_mask *clk_mgr_mask;
struct state_dependent_clocks max_clks_by_state[DM_PP_CLOCKS_MAX_STATES];
/*TODO: figure out which of the below fields should be here vs in asic specific portion */
/* Cache the status of DFS-bypass feature*/
bool dfs_bypass_enabled;
@ -477,8 +469,6 @@ struct clk_mgr_internal {
*/
int dprefclk_ss_divider;
enum dm_pp_clocks_state max_clks_state;
enum dm_pp_clocks_state cur_min_clks_state;
bool periodic_retraining_disabled;
unsigned int cur_phyclk_req_table[MAX_LINKS];

View File

@ -273,7 +273,6 @@ struct transmitter_configuration {
#define NUMBER_OF_UCHAR_FOR_GUID 16
#define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7
#define NUMBER_OF_CSR_M3_ARB 10
#define NUMBER_OF_DISP_CLK_VOLTAGE 4
#define NUMBER_OF_AVAILABLE_SCLK 5
struct i2c_reg_info {
@ -302,14 +301,6 @@ struct edp_info {
/* V6 */
struct integrated_info {
struct clock_voltage_caps {
/* The Voltage Index indicated by FUSE, same voltage index
shared with SCLK DPM fuse table */
uint32_t voltage_index;
/* Maximum clock supported with specified voltage index */
uint32_t max_supported_clk; /* in KHz */
} disp_clk_voltage[NUMBER_OF_DISP_CLK_VOLTAGE];
struct display_connection_info {
struct external_display_path {
/* A bit vector to show what devices are supported */

View File

@ -113,24 +113,6 @@ struct amd_pp_display_configuration {
struct amd_pp_simple_clock_info {
uint32_t engine_max_clock;
uint32_t memory_max_clock;
uint32_t level;
};
enum PP_DAL_POWERLEVEL {
PP_DAL_POWERLEVEL_INVALID = 0,
PP_DAL_POWERLEVEL_ULTRALOW,
PP_DAL_POWERLEVEL_LOW,
PP_DAL_POWERLEVEL_NOMINAL,
PP_DAL_POWERLEVEL_PERFORMANCE,
PP_DAL_POWERLEVEL_0 = PP_DAL_POWERLEVEL_ULTRALOW,
PP_DAL_POWERLEVEL_1 = PP_DAL_POWERLEVEL_LOW,
PP_DAL_POWERLEVEL_2 = PP_DAL_POWERLEVEL_NOMINAL,
PP_DAL_POWERLEVEL_3 = PP_DAL_POWERLEVEL_PERFORMANCE,
PP_DAL_POWERLEVEL_4 = PP_DAL_POWERLEVEL_3+1,
PP_DAL_POWERLEVEL_5 = PP_DAL_POWERLEVEL_4+1,
PP_DAL_POWERLEVEL_6 = PP_DAL_POWERLEVEL_5+1,
PP_DAL_POWERLEVEL_7 = PP_DAL_POWERLEVEL_6+1,
};
struct amd_pp_clock_info {
@ -142,7 +124,6 @@ struct amd_pp_clock_info {
uint32_t max_bus_bandwidth;
uint32_t max_engine_clock_in_sr;
uint32_t min_engine_clock_in_sr;
enum PP_DAL_POWERLEVEL max_clocks_state;
};
enum amd_pp_clock_type {

View File

@ -417,7 +417,7 @@ struct amd_pm_funcs {
void (*display_configuration_changed)(void *handle);
void (*print_power_state)(void *handle, void *ps);
bool (*vblank_too_short)(void *handle);
void (*enable_bapm)(void *handle, bool enable);
void (*notify_ac_dc)(void *handle);
int (*check_state_equal)(void *handle,
void *cps,
void *rps,
@ -476,8 +476,6 @@ struct amd_pm_funcs {
u32 (*get_mclk)(void *handle, bool low);
int (*display_configuration_change)(void *handle,
const struct amd_pp_display_configuration *input);
int (*get_display_power_level)(void *handle,
struct amd_pp_simple_clock_info *output);
int (*get_current_clocks)(void *handle,
struct amd_pp_clock_info *clocks);
int (*get_clock_by_type)(void *handle,

View File

@ -33,8 +33,8 @@
#include <linux/power_supply.h>
#include "amdgpu_smu.h"
#define amdgpu_dpm_enable_bapm(adev, e) \
((adev)->powerplay.pp_funcs->enable_bapm((adev)->powerplay.pp_handle, (e)))
#define amdgpu_dpm_notify_ac_dc(adev) \
((adev)->powerplay.pp_funcs->notify_ac_dc((adev)->powerplay.pp_handle))
#define amdgpu_dpm_is_legacy_dpm(adev) ((adev)->powerplay.pp_handle == (adev))
@ -504,8 +504,8 @@ void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev)
adev->pm.ac_power = false;
if (adev->powerplay.pp_funcs &&
adev->powerplay.pp_funcs->enable_bapm)
amdgpu_dpm_enable_bapm(adev, adev->pm.ac_power);
adev->powerplay.pp_funcs->notify_ac_dc)
amdgpu_dpm_notify_ac_dc(adev);
if (is_support_sw_smu(adev))
smu_set_ac_dc(adev->powerplay.pp_handle);

View File

@ -1233,14 +1233,14 @@ static void kv_update_requested_ps(struct amdgpu_device *adev,
adev->pm.dpm.requested_ps = &pi->requested_rps;
}
static void kv_dpm_enable_bapm(void *handle, bool enable)
static void kv_dpm_enable_bapm(void *handle)
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
struct kv_power_info *pi = kv_get_pi(adev);
int ret;
if (pi->bapm_enable) {
ret = amdgpu_kv_smc_bapm_enable(adev, enable);
ret = amdgpu_kv_smc_bapm_enable(adev, adev->pm.ac_power);
if (ret)
drm_err(adev_to_drm(adev), "amdgpu_kv_smc_bapm_enable failed\n");
}
@ -3341,7 +3341,7 @@ static const struct amd_pm_funcs kv_dpm_funcs = {
.debugfs_print_current_performance_level = &kv_dpm_debugfs_print_current_performance_level,
.force_performance_level = &kv_dpm_force_performance_level,
.set_powergating_by_smu = kv_set_powergating_by_smu,
.enable_bapm = &kv_dpm_enable_bapm,
.notify_ac_dc = &kv_dpm_enable_bapm,
.get_vce_clock_state = amdgpu_get_vce_clock_state,
.check_state_equal = kv_check_state_equal,
.read_sensor = &kv_dpm_read_sensor,

View File

@ -3076,6 +3076,10 @@ static bool si_dpm_vblank_too_short(void *handle)
/* we never hit the non-gddr5 limit so disable it */
u32 switch_limit = adev->gmc.vram_type == AMDGPU_VRAM_TYPE_GDDR5 ? 450 : 0;
/* Disregard vblank time when there are no displays connected */
if (!adev->pm.pm_display_cfg.num_display)
return false;
/* Consider zero vblank time too short and disable MCLK switching.
* Note that the vblank time is set to maximum when no displays are attached,
* so we'll still enable MCLK switching in that case.
@ -3884,16 +3888,18 @@ static void si_notify_hardware_vpu_recovery_event(struct amdgpu_device *adev)
}
#endif
#if 0
static int si_notify_hw_of_powersource(struct amdgpu_device *adev, bool ac_power)
static void si_notify_hw_of_powersource(void *handle)
{
if (ac_power)
return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC) == PPSMC_Result_OK) ?
0 : -EINVAL;
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
return 0;
/* Check if the platform already manages the AC/DC switch via dedicated GPIO. */
if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC)
return;
/* The SMU automatically notices DC, but needs to be notified when switching to AC. */
if (adev->pm.ac_power)
amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC);
}
#endif
static PPSMC_Result si_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
PPSMC_Msg msg, u32 parameter)
@ -7236,6 +7242,7 @@ static void si_parse_pplib_clock_info(struct amdgpu_device *adev,
struct evergreen_power_info *eg_pi = evergreen_get_pi(adev);
struct si_power_info *si_pi = si_get_pi(adev);
struct si_ps *ps = si_get_ps(rps);
struct amdgpu_clock_and_voltage_limits *limits;
u16 leakage_voltage;
struct rv7xx_pl *pl = &ps->performance_levels[index];
int ret;
@ -7295,12 +7302,30 @@ static void si_parse_pplib_clock_info(struct amdgpu_device *adev,
si_pi->mvdd_bootup_value = mvdd;
}
/*
* Update maximum allowed clock limits.
* VBIOS can contain conflicting values between:
* - the maximum allowed clocks and voltages on AC or DC
* - the clocks and voltages in power states on AC or DC
*/
if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) {
adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk;
adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk;
adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc;
adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci;
ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE)
limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
else if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)
limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_dc;
else
limits = NULL;
if (limits) {
if (pl->sclk > limits->sclk)
limits->sclk = pl->sclk;
if (pl->mclk > limits->mclk)
limits->mclk = pl->mclk;
if (pl->vddc > limits->vddc)
limits->vddc = pl->vddc;
if (pl->vddci > limits->vddci)
limits->vddci = pl->vddci;
}
}
@ -8140,6 +8165,7 @@ static const struct amd_pm_funcs si_dpm_funcs = {
.get_vce_clock_state = amdgpu_get_vce_clock_state,
.read_sensor = &si_dpm_read_sensor,
.pm_compute_clocks = amdgpu_legacy_dpm_compute_clocks,
.notify_ac_dc = si_notify_hw_of_powersource,
};
static const struct amdgpu_irq_src_funcs si_dpm_irq_funcs = {

View File

@ -1020,21 +1020,9 @@ static int pp_display_configuration_change(void *handle,
return 0;
}
static int pp_get_display_power_level(void *handle,
struct amd_pp_simple_clock_info *output)
{
struct pp_hwmgr *hwmgr = handle;
if (!hwmgr || !hwmgr->pm_en || !output)
return -EINVAL;
return phm_get_dal_power_level(hwmgr, output);
}
static int pp_get_current_clocks(void *handle,
struct amd_pp_clock_info *clocks)
{
struct amd_pp_simple_clock_info simple_clocks = { 0 };
struct pp_clock_info hw_clocks;
struct pp_hwmgr *hwmgr = handle;
int ret = 0;
@ -1042,8 +1030,6 @@ static int pp_get_current_clocks(void *handle,
if (!hwmgr || !hwmgr->pm_en)
return -EINVAL;
phm_get_dal_power_level(hwmgr, &simple_clocks);
if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
PHM_PlatformCaps_PowerContainment))
ret = phm_get_clock_info(hwmgr, &hwmgr->current_ps->hardware,
@ -1068,11 +1054,6 @@ static int pp_get_current_clocks(void *handle,
clocks->max_engine_clock_in_sr = hw_clocks.max_eng_clk;
clocks->min_engine_clock_in_sr = hw_clocks.min_eng_clk;
if (simple_clocks.level == 0)
clocks->max_clocks_state = PP_DAL_POWERLEVEL_7;
else
clocks->max_clocks_state = simple_clocks.level;
if (0 == phm_get_current_shallow_sleep_clocks(hwmgr, &hwmgr->current_ps->hardware, &hw_clocks)) {
clocks->max_engine_clock_in_sr = hw_clocks.max_eng_clk;
clocks->min_engine_clock_in_sr = hw_clocks.min_eng_clk;
@ -1149,8 +1130,6 @@ static int pp_get_display_mode_validation_clocks(void *handle,
if (!hwmgr || !hwmgr->pm_en || !clocks)
return -EINVAL;
clocks->level = PP_DAL_POWERLEVEL_7;
if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DynamicPatchPowerState))
ret = phm_get_max_high_clocks(hwmgr, clocks);
@ -1550,6 +1529,17 @@ static void pp_pm_compute_clocks(void *handle)
NULL);
}
static void pp_dpm_notify_ac_dc(void *handle)
{
struct pp_hwmgr *hwmgr = handle;
if (!hwmgr || !hwmgr->pm_en)
return;
if (hwmgr->hwmgr_func->notify_ac_dc)
hwmgr->hwmgr_func->notify_ac_dc(hwmgr);
}
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,
@ -1588,7 +1578,6 @@ static const struct amd_pm_funcs pp_dpm_funcs = {
.get_sclk = pp_dpm_get_sclk,
.get_mclk = pp_dpm_get_mclk,
.display_configuration_change = pp_display_configuration_change,
.get_display_power_level = pp_get_display_power_level,
.get_current_clocks = pp_get_current_clocks,
.get_clock_by_type = pp_get_clock_by_type,
.get_clock_by_type_with_latency = pp_get_clock_by_type_with_latency,
@ -1615,4 +1604,5 @@ static const struct amd_pm_funcs pp_dpm_funcs = {
.gfx_state_change_set = pp_gfx_state_change_set,
.get_smu_prv_buf_details = pp_get_prv_buffer_details,
.pm_compute_clocks = pp_pm_compute_clocks,
.notify_ac_dc = pp_dpm_notify_ac_dc,
};

View File

@ -328,16 +328,6 @@ int phm_store_dal_configuration_data(struct pp_hwmgr *hwmgr,
return 0;
}
int phm_get_dal_power_level(struct pp_hwmgr *hwmgr,
struct amd_pp_simple_clock_info *info)
{
PHM_FUNC_CHECK(hwmgr);
if (info == NULL || hwmgr->hwmgr_func->get_dal_power_level == NULL)
return -EINVAL;
return hwmgr->hwmgr_func->get_dal_power_level(hwmgr, info);
}
int phm_set_cpu_power_state(struct pp_hwmgr *hwmgr)
{
PHM_FUNC_CHECK(hwmgr);

View File

@ -1319,7 +1319,6 @@ static int init_clock_voltage_dependency(struct pp_hwmgr *hwmgr,
hwmgr->dyn_state.vddc_dependency_on_sclk = NULL;
hwmgr->dyn_state.vddci_dependency_on_mclk = NULL;
hwmgr->dyn_state.vddc_dependency_on_mclk = NULL;
hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
hwmgr->dyn_state.mvdd_dependency_on_mclk = NULL;
hwmgr->dyn_state.vce_clock_voltage_dependency_table = NULL;
hwmgr->dyn_state.uvd_clock_voltage_dependency_table = NULL;

View File

@ -127,41 +127,6 @@ static int smu10_construct_max_power_limits_table(struct pp_hwmgr *hwmgr,
return 0;
}
static int smu10_init_dynamic_state_adjustment_rule_settings(
struct pp_hwmgr *hwmgr)
{
int count = 8;
struct phm_clock_voltage_dependency_table *table_clk_vlt;
table_clk_vlt = kzalloc_flex(*table_clk_vlt, entries, count);
if (NULL == table_clk_vlt) {
pr_err("Can not allocate memory!\n");
return -ENOMEM;
}
table_clk_vlt->count = count;
table_clk_vlt->entries[0].clk = PP_DAL_POWERLEVEL_0;
table_clk_vlt->entries[0].v = 0;
table_clk_vlt->entries[1].clk = PP_DAL_POWERLEVEL_1;
table_clk_vlt->entries[1].v = 1;
table_clk_vlt->entries[2].clk = PP_DAL_POWERLEVEL_2;
table_clk_vlt->entries[2].v = 2;
table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_3;
table_clk_vlt->entries[3].v = 3;
table_clk_vlt->entries[4].clk = PP_DAL_POWERLEVEL_4;
table_clk_vlt->entries[4].v = 4;
table_clk_vlt->entries[5].clk = PP_DAL_POWERLEVEL_5;
table_clk_vlt->entries[5].v = 5;
table_clk_vlt->entries[6].clk = PP_DAL_POWERLEVEL_6;
table_clk_vlt->entries[6].v = 6;
table_clk_vlt->entries[7].clk = PP_DAL_POWERLEVEL_7;
table_clk_vlt->entries[7].v = 7;
hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt;
return 0;
}
static int smu10_get_system_info_data(struct pp_hwmgr *hwmgr)
{
struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)hwmgr->backend;
@ -175,8 +140,6 @@ static int smu10_get_system_info_data(struct pp_hwmgr *hwmgr)
smu10_construct_max_power_limits_table (hwmgr,
&hwmgr->dyn_state.max_clock_voltage_on_ac);
smu10_init_dynamic_state_adjustment_rule_settings(hwmgr);
return 0;
}
@ -611,9 +574,6 @@ static int smu10_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
kfree(pinfo->vdd_dep_on_phyclk);
pinfo->vdd_dep_on_phyclk = NULL;
kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
kfree(hwmgr->backend);
hwmgr->backend = NULL;
@ -962,12 +922,6 @@ static int smu10_store_cc6_data(struct pp_hwmgr *hwmgr, uint32_t separation_time
return 0;
}
static int smu10_get_dal_power_level(struct pp_hwmgr *hwmgr,
struct amd_pp_simple_clock_info *info)
{
return -EINVAL;
}
static int smu10_force_clock_level(struct pp_hwmgr *hwmgr,
enum pp_clock_type type, uint32_t mask)
{
@ -1663,7 +1617,6 @@ static const struct pp_hwmgr_func smu10_hwmgr_funcs = {
.store_cc6_data = smu10_store_cc6_data,
.force_clock_level = smu10_force_clock_level,
.emit_clock_levels = smu10_emit_clock_levels,
.get_dal_power_level = smu10_get_dal_power_level,
.get_performance_level = smu10_get_performance_level,
.get_current_shallow_sleep_clocks = smu10_get_current_shallow_sleep_clocks,
.get_clock_by_type_with_latency = smu10_get_clock_by_type_with_latency,

View File

@ -5854,6 +5854,20 @@ static int smu7_power_off_asic(struct pp_hwmgr *hwmgr)
return result;
}
static void smu7_notify_ac_dc(struct pp_hwmgr *hwmgr)
{
struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
/* Check if the platform already manages the AC/DC switch via dedicated GPIO. */
if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
PHM_PlatformCaps_AutomaticDCTransition))
return;
/* The SMU automatically notices DC, but needs to be notified when switching to AC. */
if (adev->pm.ac_power)
smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunningOnAC, NULL);
}
static const struct pp_hwmgr_func smu7_hwmgr_funcs = {
.backend_init = &smu7_hwmgr_backend_init,
.backend_fini = &smu7_hwmgr_backend_fini,
@ -5916,6 +5930,7 @@ static const struct pp_hwmgr_func smu7_hwmgr_funcs = {
.get_asic_baco_state = smu7_baco_get_state,
.set_asic_baco_state = smu7_baco_set_state,
.power_off_asic = smu7_power_off_asic,
.notify_ac_dc = smu7_notify_ac_dc,
};
uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock,

View File

@ -270,41 +270,6 @@ static int smu8_construct_max_power_limits_table(struct pp_hwmgr *hwmgr,
return 0;
}
static int smu8_init_dynamic_state_adjustment_rule_settings(
struct pp_hwmgr *hwmgr,
ATOM_CLK_VOLT_CAPABILITY *disp_voltage_table)
{
struct phm_clock_voltage_dependency_table *table_clk_vlt;
table_clk_vlt = kzalloc_flex(*table_clk_vlt, entries, 8);
if (NULL == table_clk_vlt) {
pr_err("Can not allocate memory!\n");
return -ENOMEM;
}
table_clk_vlt->count = 8;
table_clk_vlt->entries[0].clk = PP_DAL_POWERLEVEL_0;
table_clk_vlt->entries[0].v = 0;
table_clk_vlt->entries[1].clk = PP_DAL_POWERLEVEL_1;
table_clk_vlt->entries[1].v = 1;
table_clk_vlt->entries[2].clk = PP_DAL_POWERLEVEL_2;
table_clk_vlt->entries[2].v = 2;
table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_3;
table_clk_vlt->entries[3].v = 3;
table_clk_vlt->entries[4].clk = PP_DAL_POWERLEVEL_4;
table_clk_vlt->entries[4].v = 4;
table_clk_vlt->entries[5].clk = PP_DAL_POWERLEVEL_5;
table_clk_vlt->entries[5].v = 5;
table_clk_vlt->entries[6].clk = PP_DAL_POWERLEVEL_6;
table_clk_vlt->entries[6].v = 6;
table_clk_vlt->entries[7].clk = PP_DAL_POWERLEVEL_7;
table_clk_vlt->entries[7].v = 7;
hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt;
return 0;
}
static int smu8_get_system_info_data(struct pp_hwmgr *hwmgr)
{
struct smu8_hwmgr *data = hwmgr->backend;
@ -403,9 +368,6 @@ static int smu8_get_system_info_data(struct pp_hwmgr *hwmgr)
smu8_construct_max_power_limits_table (hwmgr,
&hwmgr->dyn_state.max_clock_voltage_on_ac);
smu8_init_dynamic_state_adjustment_rule_settings(hwmgr,
&info->sDISPCLK_Voltage[0]);
return result;
}
@ -1149,9 +1111,6 @@ static int smu8_hwmgr_backend_init(struct pp_hwmgr *hwmgr)
static int smu8_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
{
if (hwmgr != NULL) {
kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
kfree(hwmgr->backend);
hwmgr->backend = NULL;
}
@ -1521,27 +1480,6 @@ static int smu8_store_cc6_data(struct pp_hwmgr *hwmgr, uint32_t separation_time,
return 0;
}
static int smu8_get_dal_power_level(struct pp_hwmgr *hwmgr,
struct amd_pp_simple_clock_info *info)
{
uint32_t i;
const struct phm_clock_voltage_dependency_table *table =
hwmgr->dyn_state.vddc_dep_on_dal_pwrl;
const struct phm_clock_and_voltage_limits *limits =
&hwmgr->dyn_state.max_clock_voltage_on_ac;
info->engine_max_clock = limits->sclk;
info->memory_max_clock = limits->mclk;
for (i = table->count - 1; i > 0; i--) {
if (limits->vddc >= table->entries[i].v) {
info->level = table->entries[i].clk;
return 0;
}
}
return -EINVAL;
}
static int smu8_force_clock_level(struct pp_hwmgr *hwmgr,
enum pp_clock_type type, uint32_t mask)
{
@ -2062,7 +2000,6 @@ static const struct pp_hwmgr_func smu8_hwmgr_funcs = {
.store_cc6_data = smu8_store_cc6_data,
.force_clock_level = smu8_force_clock_level,
.emit_clock_levels = smu8_emit_clock_levels,
.get_dal_power_level = smu8_get_dal_power_level,
.get_performance_level = smu8_get_performance_level,
.get_current_shallow_sleep_clocks = smu8_get_current_shallow_sleep_clocks,
.get_clock_by_type = smu8_get_clock_by_type,

View File

@ -814,9 +814,6 @@ static int vega10_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr)
static int vega10_hwmgr_backend_fini(struct pp_hwmgr *hwmgr)
{
kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl);
hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL;
kfree(hwmgr->backend);
hwmgr->backend = NULL;
@ -4386,20 +4383,6 @@ static uint32_t vega10_get_fan_control_mode(struct pp_hwmgr *hwmgr)
return AMD_FAN_CTRL_AUTO;
}
static int vega10_get_dal_power_level(struct pp_hwmgr *hwmgr,
struct amd_pp_simple_clock_info *info)
{
struct phm_ppt_v2_information *table_info =
(struct phm_ppt_v2_information *)hwmgr->pptable;
struct phm_clock_and_voltage_limits *max_limits =
&table_info->max_clock_voltage_on_ac;
info->engine_max_clock = max_limits->sclk;
info->memory_max_clock = max_limits->mclk;
return 0;
}
static void vega10_get_sclks(struct pp_hwmgr *hwmgr,
struct pp_clock_levels_with_latency *clocks)
{
@ -5644,7 +5627,6 @@ static const struct pp_hwmgr_func vega10_hwmgr_funcs = {
.set_fan_control_mode = vega10_set_fan_control_mode,
.get_fan_control_mode = vega10_get_fan_control_mode,
.read_sensor = vega10_read_sensor,
.get_dal_power_level = vega10_get_dal_power_level,
.get_clock_by_type_with_latency = vega10_get_clock_by_type_with_latency,
.get_clock_by_type_with_voltage = vega10_get_clock_by_type_with_voltage,
.set_watermarks_for_clocks_ranges = vega10_set_watermarks_for_clocks_ranges,

View File

@ -1822,21 +1822,6 @@ static uint32_t vega12_get_fan_control_mode(struct pp_hwmgr *hwmgr)
return AMD_FAN_CTRL_AUTO;
}
static int vega12_get_dal_power_level(struct pp_hwmgr *hwmgr,
struct amd_pp_simple_clock_info *info)
{
#if 0
struct phm_ppt_v2_information *table_info =
(struct phm_ppt_v2_information *)hwmgr->pptable;
struct phm_clock_and_voltage_limits *max_limits =
&table_info->max_clock_voltage_on_ac;
info->engine_max_clock = max_limits->sclk;
info->memory_max_clock = max_limits->mclk;
#endif
return 0;
}
static int vega12_get_clock_ranges(struct pp_hwmgr *hwmgr,
uint32_t *clock,
PPCLK_e clock_select,
@ -2963,7 +2948,6 @@ static const struct pp_hwmgr_func vega12_hwmgr_funcs = {
.set_fan_control_mode = vega12_set_fan_control_mode,
.get_fan_control_mode = vega12_get_fan_control_mode,
.read_sensor = vega12_read_sensor,
.get_dal_power_level = vega12_get_dal_power_level,
.get_clock_by_type_with_latency = vega12_get_clock_by_type_with_latency,
.get_clock_by_type_with_voltage = vega12_get_clock_by_type_with_voltage,
.set_watermarks_for_clocks_ranges = vega12_set_watermarks_for_clocks_ranges,

View File

@ -2796,22 +2796,6 @@ static void vega20_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode)
}
}
static int vega20_get_dal_power_level(struct pp_hwmgr *hwmgr,
struct amd_pp_simple_clock_info *info)
{
#if 0
struct phm_ppt_v2_information *table_info =
(struct phm_ppt_v2_information *)hwmgr->pptable;
struct phm_clock_and_voltage_limits *max_limits =
&table_info->max_clock_voltage_on_ac;
info->engine_max_clock = max_limits->sclk;
info->memory_max_clock = max_limits->mclk;
#endif
return 0;
}
static int vega20_get_sclks(struct pp_hwmgr *hwmgr,
struct pp_clock_levels_with_latency *clocks)
{
@ -4446,7 +4430,6 @@ static const struct pp_hwmgr_func vega20_hwmgr_funcs = {
/* export to DAL */
.get_sclk = vega20_dpm_get_sclk,
.get_mclk = vega20_dpm_get_mclk,
.get_dal_power_level = vega20_get_dal_power_level,
.get_clock_by_type_with_latency = vega20_get_clock_by_type_with_latency,
.get_clock_by_type_with_voltage = vega20_get_clock_by_type_with_voltage,
.set_watermarks_for_clocks_ranges = vega20_set_watermarks_for_clocks_ranges,

View File

@ -426,9 +426,6 @@ extern int phm_check_states_equal(struct pp_hwmgr *hwmgr,
extern int phm_store_dal_configuration_data(struct pp_hwmgr *hwmgr,
const struct amd_pp_display_configuration *display_config);
extern int phm_get_dal_power_level(struct pp_hwmgr *hwmgr,
struct amd_pp_simple_clock_info *info);
extern int phm_set_cpu_power_state(struct pp_hwmgr *hwmgr);
extern int phm_power_down_asic(struct pp_hwmgr *hwmgr);

View File

@ -292,8 +292,6 @@ struct pp_hwmgr_func {
int (*store_cc6_data)(struct pp_hwmgr *hwmgr, uint32_t separation_time,
bool cc6_disable, bool pstate_disable,
bool pstate_switch_disable);
int (*get_dal_power_level)(struct pp_hwmgr *hwmgr,
struct amd_pp_simple_clock_info *info);
int (*get_performance_level)(struct pp_hwmgr *, const struct pp_hw_power_state *,
PHM_PerformanceLevelDesignation, uint32_t, PHM_PerformanceLevel *);
int (*get_current_shallow_sleep_clocks)(struct pp_hwmgr *hwmgr,
@ -364,6 +362,7 @@ struct pp_hwmgr_func {
bool disable);
ssize_t (*get_gpu_metrics)(struct pp_hwmgr *hwmgr, void **table);
int (*gfx_state_change)(struct pp_hwmgr *hwmgr, uint32_t state);
void (*notify_ac_dc)(struct pp_hwmgr *hwmgr);
};
struct pp_table_func {
@ -540,7 +539,6 @@ struct phm_ppt_v1_information {
struct phm_clock_array *valid_dcefclk_values;
struct phm_clock_and_voltage_limits max_clock_voltage_on_dc;
struct phm_clock_and_voltage_limits max_clock_voltage_on_ac;
struct phm_clock_voltage_dependency_table *vddc_dep_on_dal_pwrl;
struct phm_ppm_table *ppm_parameter_table;
struct phm_cac_tdp_table *cac_dtp_table;
struct phm_tdp_table *tdp_table;
@ -632,7 +630,6 @@ struct phm_dynamic_state_info {
struct phm_clock_voltage_dependency_table *vddc_dependency_on_mclk;
struct phm_clock_voltage_dependency_table *mvdd_dependency_on_mclk;
struct phm_clock_voltage_dependency_table *vddc_dependency_on_display_clock;
struct phm_clock_voltage_dependency_table *vddc_dep_on_dal_pwrl;
struct phm_clock_array *valid_sclk_values;
struct phm_clock_array *valid_mclk_values;
struct phm_clock_and_voltage_limits max_clock_voltage_on_dc;

View File

@ -108,15 +108,19 @@ static int amdgpu_virt_ras_remote_ioctl_cmd(struct ras_core_context *ras_core,
ret = amdgpu_virt_send_remote_ras_cmd(ras_core->dev,
shared_mem.gpa, mem_len);
if (!ret) {
if (rcmd->cmd_res) {
ret = rcmd->cmd_res;
uint32_t cmd_res = READ_ONCE(rcmd->cmd_res);
uint32_t osz;
if (cmd_res) {
ret = cmd_res;
goto out;
}
cmd->cmd_res = rcmd->cmd_res;
cmd->output_size = rcmd->output_size;
if (rcmd->output_size && (rcmd->output_size <= output_size) && output_data)
memcpy(output_data, rcmd->output_buff_raw, rcmd->output_size);
osz = READ_ONCE(rcmd->output_size);
cmd->cmd_res = cmd_res;
cmd->output_size = osz;
if (osz && osz <= output_size && output_data)
memcpy(output_data, rcmd->output_buff_raw, osz);
}
out:

View File

@ -46,6 +46,15 @@
printk(KERN_WARNING fmt, ##__VA_ARGS__); \
} while (0)
#define RAS_DEV_WARN_RATELIMITED(device, fmt, ...) \
do { \
if (device) \
dev_warn_ratelimited(((struct amdgpu_device *)device)->dev, \
fmt, ##__VA_ARGS__); \
else \
printk_ratelimited(KERN_WARNING fmt, ##__VA_ARGS__); \
} while (0)
#define RAS_DEV_INFO(device, fmt, ...) \
do { \
if (device) \

View File

@ -193,12 +193,29 @@ static void ras_umc_reserve_eeprom_record(struct ras_core_context *ras_core,
}
/* When gpu reset is ongoing, ecc logging operations will be pended.
*
* The pending list is bounded by RAS_UMC_PENDING_ECC_MAX so that an ECC
* storm or repeated UMC error injection cannot make this list (and the
* kernel allocations behind it) grow without bound. Once the limit is
* reached, additional events are dropped and counted in
* pending_ecc_dropped, with a rate-limited warning emitted.
*/
int ras_umc_log_bad_bank_pending(struct ras_core_context *ras_core, struct ras_bank_ecc *bank)
{
struct ras_umc *ras_umc = &ras_core->ras_umc;
struct ras_bank_ecc_node *ecc_node;
mutex_lock(&ras_umc->pending_ecc_lock);
if (ras_umc->pending_ecc_count >= RAS_UMC_PENDING_ECC_MAX) {
ras_umc->pending_ecc_dropped++;
mutex_unlock(&ras_umc->pending_ecc_lock);
RAS_DEV_WARN_RATELIMITED(ras_core->dev,
"pending ECC list full (%u), dropping bad bank event (total dropped:%u)\n",
RAS_UMC_PENDING_ECC_MAX, ras_umc->pending_ecc_dropped);
return -ENOSPC;
}
mutex_unlock(&ras_umc->pending_ecc_lock);
ecc_node = kzalloc_obj(*ecc_node);
if (!ecc_node)
return -ENOMEM;
@ -206,7 +223,15 @@ int ras_umc_log_bad_bank_pending(struct ras_core_context *ras_core, struct ras_b
memcpy(&ecc_node->ecc, bank, sizeof(ecc_node->ecc));
mutex_lock(&ras_umc->pending_ecc_lock);
/* re-check under the lock to honor the cap across concurrent callers */
if (ras_umc->pending_ecc_count >= RAS_UMC_PENDING_ECC_MAX) {
ras_umc->pending_ecc_dropped++;
mutex_unlock(&ras_umc->pending_ecc_lock);
kfree(ecc_node);
return -ENOSPC;
}
list_add_tail(&ecc_node->node, &ras_umc->pending_ecc_list);
ras_umc->pending_ecc_count++;
mutex_unlock(&ras_umc->pending_ecc_lock);
return 0;
@ -225,8 +250,16 @@ int ras_umc_log_pending_bad_bank(struct ras_core_context *ras_core)
if (!ras_umc_log_bad_bank(ras_core, &ecc_node->ecc)) {
list_del(&ecc_node->node);
kfree(ecc_node);
if (ras_umc->pending_ecc_count)
ras_umc->pending_ecc_count--;
}
}
if (ras_umc->pending_ecc_dropped) {
RAS_DEV_WARN(ras_core->dev,
"%u pending ECC bad-bank events were dropped during GPU reset\n",
ras_umc->pending_ecc_dropped);
ras_umc->pending_ecc_dropped = 0;
}
mutex_unlock(&ras_umc->pending_ecc_lock);
return 0;
@ -609,6 +642,8 @@ int ras_umc_sw_fini(struct ras_core_context *ras_core)
list_del(&ecc_node->node);
kfree(ecc_node);
}
ras_umc->pending_ecc_count = 0;
ras_umc->pending_ecc_dropped = 0;
mutex_unlock(&ras_umc->pending_ecc_lock);
mutex_destroy(&ras_umc->tree_lock);

View File

@ -139,8 +139,20 @@ struct ras_umc {
struct mutex pending_ecc_lock;
struct ras_umc_err_data umc_err_data;
struct list_head pending_ecc_list;
/* number of entries currently queued on pending_ecc_list */
u32 pending_ecc_count;
/* number of entries dropped because pending_ecc_list was full */
u32 pending_ecc_dropped;
};
/*
* Upper bound on entries that can be queued on pending_ecc_list while a
* GPU reset is in progress. Beyond this, new ECC events are dropped to
* prevent unbounded kernel memory growth in case of an ECC storm or
* malicious/repeated UMC error injection.
*/
#define RAS_UMC_PENDING_ECC_MAX 8192
int ras_umc_sw_init(struct ras_core_context *ras);
int ras_umc_sw_fini(struct ras_core_context *ras);
int ras_umc_hw_init(struct ras_core_context *ras);

View File

@ -311,7 +311,7 @@ static void radeon_audio_write_sad_regs(struct drm_encoder *encoder)
if (!connector)
return;
sad_count = drm_edid_to_sad(radeon_connector->edid, &sads);
sad_count = drm_edid_to_sad(drm_edid_raw(radeon_connector->edid), &sads);
if (sad_count < 0)
DRM_ERROR("Couldn't read SADs: %d\n", sad_count);
if (sad_count <= 0)
@ -335,7 +335,7 @@ static void radeon_audio_write_speaker_allocation(struct drm_encoder *encoder)
if (!connector)
return;
sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb);
sad_count = drm_edid_to_speaker_allocation(drm_edid_raw(radeon_connector->edid), &sadb);
if (sad_count < 0) {
DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n",
sad_count);

View File

@ -390,10 +390,10 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
}
/* this is used for atom LCDs as well */
struct edid *
const struct drm_edid *
radeon_bios_get_hardcoded_edid(struct radeon_device *rdev)
{
return drm_edid_duplicate(drm_edid_raw(rdev->mode_info.bios_hardcoded_edid));
return drm_edid_dup(rdev->mode_info.bios_hardcoded_edid);
}
static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev,

View File

@ -271,8 +271,8 @@ static void radeon_connector_get_edid(struct drm_connector *connector)
if ((radeon_connector_encoder_get_dp_bridge_encoder_id(connector) !=
ENCODER_OBJECT_ID_NONE) &&
radeon_connector->ddc_bus->has_aux) {
radeon_connector->edid = drm_get_edid(connector,
&radeon_connector->ddc_bus->aux.ddc);
radeon_connector->edid = drm_edid_read_ddc(connector,
&radeon_connector->ddc_bus->aux.ddc);
} else if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
(connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
@ -280,19 +280,19 @@ static void radeon_connector_get_edid(struct drm_connector *connector)
if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||
dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) &&
radeon_connector->ddc_bus->has_aux)
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
&radeon_connector->ddc_bus->aux.ddc);
radeon_connector->edid = drm_edid_read_ddc(&radeon_connector->base,
&radeon_connector->ddc_bus->aux.ddc);
else if (radeon_connector->ddc_bus)
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
&radeon_connector->ddc_bus->adapter);
radeon_connector->edid = drm_edid_read_ddc(&radeon_connector->base,
&radeon_connector->ddc_bus->adapter);
} else if (vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC &&
connector->connector_type == DRM_MODE_CONNECTOR_LVDS &&
radeon_connector->ddc_bus) {
radeon_connector->edid = drm_get_edid_switcheroo(&radeon_connector->base,
&radeon_connector->ddc_bus->adapter);
radeon_connector->edid = drm_edid_read_switcheroo(&radeon_connector->base,
&radeon_connector->ddc_bus->adapter);
} else if (radeon_connector->ddc_bus) {
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
&radeon_connector->ddc_bus->adapter);
radeon_connector->edid = drm_edid_read_ddc(&radeon_connector->base,
&radeon_connector->ddc_bus->adapter);
}
if (!radeon_connector->edid) {
@ -314,25 +314,17 @@ static void radeon_connector_get_edid(struct drm_connector *connector)
}
}
static void radeon_connector_free_edid(struct drm_connector *connector)
{
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
kfree(radeon_connector->edid);
radeon_connector->edid = NULL;
}
static int radeon_ddc_get_modes(struct drm_connector *connector)
{
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
int ret;
if (radeon_connector->edid) {
drm_connector_update_edid_property(connector, radeon_connector->edid);
ret = drm_add_edid_modes(connector, radeon_connector->edid);
drm_edid_connector_update(connector, radeon_connector->edid);
ret = drm_edid_connector_add_modes(connector);
return ret;
}
drm_connector_update_edid_property(connector, NULL);
drm_edid_connector_update(connector, NULL);
return 0;
}
@ -895,7 +887,9 @@ static void radeon_connector_destroy(struct drm_connector *connector)
{
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
radeon_connector_free_edid(connector);
drm_edid_free(radeon_connector->edid);
radeon_connector->edid = NULL;
kfree(radeon_connector->con_priv);
drm_connector_unregister(connector);
drm_connector_cleanup(connector);
@ -1007,7 +1001,8 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
dret = radeon_ddc_probe(radeon_connector, false);
if (dret) {
radeon_connector->detected_by_load = false;
radeon_connector_free_edid(connector);
drm_edid_free(radeon_connector->edid);
radeon_connector->edid = NULL;
radeon_connector_get_edid(connector);
if (!radeon_connector->edid) {
@ -1016,13 +1011,14 @@ radeon_vga_detect(struct drm_connector *connector, bool force)
ret = connector_status_connected;
} else {
radeon_connector->use_digital =
!!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
drm_edid_is_digital(radeon_connector->edid);
/* some oems have boards with separate digital and analog connectors
* with a shared ddc line (often vga + hdmi)
*/
if (radeon_connector->use_digital && radeon_connector->shared_ddc) {
radeon_connector_free_edid(connector);
drm_edid_free(radeon_connector->edid);
radeon_connector->edid = NULL;
ret = connector_status_disconnected;
} else {
ret = connector_status_connected;
@ -1251,7 +1247,8 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
}
if (dret) {
radeon_connector->detected_by_load = false;
radeon_connector_free_edid(connector);
drm_edid_free(radeon_connector->edid);
radeon_connector->edid = NULL;
radeon_connector_get_edid(connector);
if (!radeon_connector->edid) {
@ -1271,13 +1268,14 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
}
} else {
radeon_connector->use_digital =
!!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL);
drm_edid_is_digital(radeon_connector->edid);
/* some oems have boards with separate digital and analog connectors
* with a shared ddc line (often vga + hdmi)
*/
if ((!radeon_connector->use_digital) && radeon_connector->shared_ddc) {
radeon_connector_free_edid(connector);
drm_edid_free(radeon_connector->edid);
radeon_connector->edid = NULL;
ret = connector_status_disconnected;
} else {
ret = connector_status_connected;
@ -1301,7 +1299,8 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
if (list_connector->connector_type != DRM_MODE_CONNECTOR_VGA) {
/* hpd is our only option in this case */
if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
radeon_connector_free_edid(connector);
drm_edid_free(radeon_connector->edid);
radeon_connector->edid = NULL;
ret = connector_status_disconnected;
}
}
@ -1635,7 +1634,8 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
goto out;
}
radeon_connector_free_edid(connector);
drm_edid_free(radeon_connector->edid);
radeon_connector->edid = NULL;
if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) ||
(connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) {

View File

@ -525,7 +525,7 @@ struct radeon_connector {
bool use_digital;
/* we need to mind the EDID between detect
and get modes due to analog/digital/tvencoder */
struct edid *edid;
const struct drm_edid *edid;
void *con_priv;
bool dac_load_detect;
bool detected_by_load; /* if the connection status was determined by load */
@ -839,7 +839,7 @@ radeon_get_crtc_scanout_position(struct drm_crtc *crtc, bool in_vblank_irq,
const struct drm_display_mode *mode);
extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
extern struct edid *
extern const struct drm_edid *
radeon_bios_get_hardcoded_edid(struct radeon_device *rdev);
extern bool radeon_atom_get_clock_info(struct drm_device *dev);
extern bool radeon_combios_get_clock_info(struct drm_device *dev);