mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
amd-drm-fixes-7.2-2026-07-17:
amdgpu: - DCN 4.2 fixes - NUTMEG fixes - 8K panel fix - Backlight fixes - UserQ fix - Fix bo->pin leaking in amdgpu_bo_create_reserved() - VFCT fixes - devcoredump fixes - Display fixes - SMU7 DPM fix - AC/DC fixes for SMU7 and SI - Queue reset fix - PCIe DPM fix - XHCI/GPU resume ordering fix - Pageflip timeout fix amdkfd: - Fix potential overflow in CWSR size calculation - DQM error clean up fixes -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQQgO5Idg2tXNTSZAr293/aFa7yZ2AUCalqifQAKCRC93/aFa7yZ 2JreAQC8qdjr8xl1zMJkKuGPLS8GP1LxDQIyBY+s+7kTc0UU6QD+Pxo9ZbX/3Rxf uf6GJ4eq1kCs5b0VwI1EK61UaYHhLgA= =Kenl -----END PGP SIGNATURE----- Merge tag 'amd-drm-fixes-7.2-2026-07-17' of https://gitlab.freedesktop.org/agd5f/linux into drm-fixes amd-drm-fixes-7.2-2026-07-17: amdgpu: - DCN 4.2 fixes - NUTMEG fixes - 8K panel fix - Backlight fixes - UserQ fix - Fix bo->pin leaking in amdgpu_bo_create_reserved() - VFCT fixes - devcoredump fixes - Display fixes - SMU7 DPM fix - AC/DC fixes for SMU7 and SI - Queue reset fix - PCIe DPM fix - XHCI/GPU resume ordering fix - Pageflip timeout fix amdkfd: - Fix potential overflow in CWSR size calculation - DQM error clean up fixes Signed-off-by: Dave Airlie <airlied@redhat.com> From: Alex Deucher <alexander.deucher@amd.com> Link: https://patch.msgid.link/20260717215008.998399-1-alexander.deucher@amd.com
This commit is contained in:
commit
973fd9493e
|
|
@ -372,19 +372,59 @@ static bool amdgpu_read_disabled_bios(struct amdgpu_device *adev)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/**
|
||||
* amdgpu_acpi_vfct_match() - Check if a VFCT entry matches the device
|
||||
* @adev: AMDGPU device
|
||||
* @vhdr: VFCT image header to check
|
||||
*
|
||||
* VFCT entries contain the PCI bus number as recorded during BIOS POST.
|
||||
* On systems where the kernel renumbers PCI buses (e.g. pci=realloc or
|
||||
* resource conflicts), the runtime bus number may differ from the POST
|
||||
* value. Match by device identity (vendor + device + function) and use
|
||||
* the bus number as a preference: exact bus match is preferred, but when
|
||||
* the bus numbers disagree we accept the entry if the device identity
|
||||
* matches.
|
||||
*
|
||||
* Returns: 0 on match, -ENODEV on no match
|
||||
*/
|
||||
static int amdgpu_acpi_vfct_match(struct amdgpu_device *adev,
|
||||
VFCT_IMAGE_HEADER *vhdr)
|
||||
{
|
||||
/* Vendor and device IDs must always match */
|
||||
if (vhdr->VendorID != adev->pdev->vendor ||
|
||||
vhdr->DeviceID != adev->pdev->device)
|
||||
return -ENODEV;
|
||||
|
||||
if (vhdr->PCIDevice != PCI_SLOT(adev->pdev->devfn) ||
|
||||
vhdr->PCIFunction != PCI_FUNC(adev->pdev->devfn))
|
||||
return -ENODEV;
|
||||
|
||||
/* Exact bus number match - preferred */
|
||||
if (vhdr->PCIBus == adev->pdev->bus->number)
|
||||
return 0;
|
||||
|
||||
/* Bus mismatch but device identity matches (PCI renumbering case) */
|
||||
dev_notice(adev->dev,
|
||||
"VFCT bus number mismatch: table %u != runtime %u, matching by device identity (vendor 0x%04x device 0x%04x)\n",
|
||||
vhdr->PCIBus, adev->pdev->bus->number,
|
||||
adev->pdev->vendor, adev->pdev->device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
|
||||
{
|
||||
struct acpi_table_header *hdr;
|
||||
acpi_size tbl_size;
|
||||
UEFI_ACPI_VFCT *vfct;
|
||||
unsigned int offset;
|
||||
bool r = false;
|
||||
|
||||
if (!ACPI_SUCCESS(acpi_get_table("VFCT", 1, &hdr)))
|
||||
return false;
|
||||
tbl_size = hdr->length;
|
||||
if (tbl_size < sizeof(UEFI_ACPI_VFCT)) {
|
||||
dev_info(adev->dev, "ACPI VFCT table present but broken (too short #1),skipping\n");
|
||||
return false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
vfct = (UEFI_ACPI_VFCT *)hdr;
|
||||
|
|
@ -397,36 +437,36 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
|
|||
offset += sizeof(VFCT_IMAGE_HEADER);
|
||||
if (offset > tbl_size) {
|
||||
dev_info(adev->dev, "ACPI VFCT image header truncated,skipping\n");
|
||||
return false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
offset += vhdr->ImageLength;
|
||||
if (offset > tbl_size) {
|
||||
dev_info(adev->dev, "ACPI VFCT image truncated,skipping\n");
|
||||
return false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (vhdr->ImageLength &&
|
||||
vhdr->PCIBus == adev->pdev->bus->number &&
|
||||
vhdr->PCIDevice == PCI_SLOT(adev->pdev->devfn) &&
|
||||
vhdr->PCIFunction == PCI_FUNC(adev->pdev->devfn) &&
|
||||
vhdr->VendorID == adev->pdev->vendor &&
|
||||
vhdr->DeviceID == adev->pdev->device) {
|
||||
!amdgpu_acpi_vfct_match(adev, vhdr)) {
|
||||
adev->bios = kmemdup(&vbios->VbiosContent,
|
||||
vhdr->ImageLength,
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!check_atom_bios(adev, vhdr->ImageLength)) {
|
||||
amdgpu_bios_release(adev);
|
||||
return false;
|
||||
goto out;
|
||||
}
|
||||
adev->bios_size = vhdr->ImageLength;
|
||||
return true;
|
||||
r = true;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(adev->dev, "ACPI VFCT table present but broken (too short #2),skipping\n");
|
||||
return false;
|
||||
|
||||
out:
|
||||
acpi_put_table(hdr);
|
||||
return r;
|
||||
}
|
||||
#else
|
||||
static inline bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev)
|
||||
|
|
|
|||
|
|
@ -234,6 +234,9 @@ amdgpu_devcoredump_print_ibs(struct drm_printer *p,
|
|||
drm_printf(p, "\nIB #%d 0x%llx %d dw\n", i,
|
||||
coredump->ibs[i].gpu_addr,
|
||||
coredump->ibs[i].ib_size_dw);
|
||||
|
||||
for (int j = 0; j < coredump->ibs[i].ib_size_dw; j++)
|
||||
drm_printf(p, "0xffffffff\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -355,10 +358,14 @@ amdgpu_devcoredump_format(char *buffer, size_t count, struct amdgpu_coredump_inf
|
|||
drm_printf(&p, "kernel: %s\n", init_utsname()->release);
|
||||
drm_printf(&p, "module: " KBUILD_MODNAME "\n");
|
||||
drm_printf(&p, "time: %ptSp\n", &coredump->reset_time);
|
||||
drm_printf(&p, "pasid: %u\n", coredump->pasid);
|
||||
drm_printf(&p, "vmid: %u\n", coredump->vmid);
|
||||
|
||||
if (coredump->reset_task_info.task.pid)
|
||||
drm_printf(&p, "process_name: %s PID: %d\n",
|
||||
drm_printf(&p, "process_name: %s TGID: %d thread: %s PID: %d\n",
|
||||
coredump->reset_task_info.process_name,
|
||||
coredump->reset_task_info.tgid,
|
||||
coredump->reset_task_info.task.comm,
|
||||
coredump->reset_task_info.task.pid);
|
||||
|
||||
/* SOC Information */
|
||||
|
|
@ -562,6 +569,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check,
|
|||
amdgpu_vm_put_task_info(ti);
|
||||
}
|
||||
coredump->pasid = job->pasid;
|
||||
coredump->vmid = job->vmid;
|
||||
coredump->num_ibs = job->num_ibs;
|
||||
for (i = 0; i < job->num_ibs; ++i) {
|
||||
coredump->ibs[i].gpu_addr = job->ibs[i].gpu_addr;
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ struct amdgpu_coredump_info {
|
|||
char *formatted;
|
||||
|
||||
unsigned int pasid;
|
||||
unsigned int vmid;
|
||||
int num_ibs;
|
||||
struct amdgpu_coredump_ib_info ibs[] __counted_by(num_ibs);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1323,6 +1323,15 @@ static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device
|
|||
|
||||
if (c->x86_vendor == X86_VENDOR_INTEL)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* AMD Ryzen Pinnacle Ridge (Zen+, family 0x17 model 0x08) CPUs don't
|
||||
* support PCIe dynamic speed switching.
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/work_items/5436
|
||||
*/
|
||||
if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 0x17 &&
|
||||
c->x86_model == 0x08)
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3137,9 +3137,11 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev)
|
|||
case IP_VERSION(11, 5, 3):
|
||||
case IP_VERSION(11, 5, 4):
|
||||
case IP_VERSION(11, 5, 6):
|
||||
adev->family = AMDGPU_FAMILY_GC_11_5_0;
|
||||
break;
|
||||
case IP_VERSION(11, 7, 0):
|
||||
case IP_VERSION(11, 7, 1):
|
||||
adev->family = AMDGPU_FAMILY_GC_11_5_0;
|
||||
adev->family = AMDGPU_FAMILY_GC_11_5_4;
|
||||
break;
|
||||
case IP_VERSION(12, 0, 0):
|
||||
case IP_VERSION(12, 0, 1):
|
||||
|
|
|
|||
|
|
@ -276,10 +276,12 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev,
|
|||
goto error_free;
|
||||
}
|
||||
|
||||
r = amdgpu_bo_pin(*bo_ptr, domain);
|
||||
if (r) {
|
||||
dev_err(adev->dev, "(%d) kernel bo pin failed\n", r);
|
||||
goto error_unreserve;
|
||||
if (free) {
|
||||
r = amdgpu_bo_pin(*bo_ptr, domain);
|
||||
if (r) {
|
||||
dev_err(adev->dev, "(%d) kernel bo pin failed\n", r);
|
||||
goto error_unreserve;
|
||||
}
|
||||
}
|
||||
|
||||
r = amdgpu_ttm_alloc_gart(&(*bo_ptr)->tbo);
|
||||
|
|
@ -302,7 +304,8 @@ int amdgpu_bo_create_reserved(struct amdgpu_device *adev,
|
|||
return 0;
|
||||
|
||||
error_unpin:
|
||||
amdgpu_bo_unpin(*bo_ptr);
|
||||
if (free)
|
||||
amdgpu_bo_unpin(*bo_ptr);
|
||||
error_unreserve:
|
||||
amdgpu_bo_unreserve(*bo_ptr);
|
||||
|
||||
|
|
|
|||
|
|
@ -2684,12 +2684,22 @@ void amdgpu_sdma_set_buffer_funcs_scheds(struct amdgpu_device *adev,
|
|||
return;
|
||||
}
|
||||
|
||||
/* Navi1x's workaround requires us to limit to a single SDMA sched
|
||||
* for ttm.
|
||||
*/
|
||||
hub = &adev->vmhub[AMDGPU_GFXHUB(0)];
|
||||
adev->mman.num_buffer_funcs_scheds = hub->sdma_invalidation_workaround ?
|
||||
1 : n;
|
||||
|
||||
/*
|
||||
* Allow using multiple SDMA schedulers only on GPUs where
|
||||
* we are allowed to do concurrent VM flushes.
|
||||
* This consideration is necessary because all GART windows
|
||||
* are mapped in VMID 0 (the kernel VMID) so each buffer
|
||||
* entity would flush VMID 0 concurrently.
|
||||
*
|
||||
* Also consider the SDMA invalidation workaround on
|
||||
* Navi 1x GPUs, which also prevents us from using
|
||||
* multiple SDMA engines on VMID 0 at the same time.
|
||||
*/
|
||||
adev->mman.num_buffer_funcs_scheds =
|
||||
(adev->vm_manager.concurrent_flush &&
|
||||
!hub->sdma_invalidation_workaround) ? n : 1;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
|
|
|
|||
|
|
@ -1376,16 +1376,19 @@ void amdgpu_userq_pre_reset(struct amdgpu_device *adev)
|
|||
|
||||
/* TODO: We probably need a new lock for the queue state */
|
||||
xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) {
|
||||
if (queue->state != AMDGPU_USERQ_STATE_MAPPED)
|
||||
continue;
|
||||
|
||||
userq_funcs = adev->userq_funcs[queue->queue_type];
|
||||
userq_funcs->unmap(queue);
|
||||
/* just mark all queues as hung at this point.
|
||||
* if unmap succeeds, we could map again
|
||||
* in amdgpu_userq_post_reset() if vram is not lost
|
||||
if (queue->state == AMDGPU_USERQ_STATE_MAPPED) {
|
||||
userq_funcs = adev->userq_funcs[queue->queue_type];
|
||||
userq_funcs->unmap(queue);
|
||||
/* just mark all queues as hung at this point.
|
||||
* if unmap succeeds, we could map again
|
||||
* in amdgpu_userq_post_reset() if vram is not lost
|
||||
*/
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
}
|
||||
/* Force-complete any pending fence regardless of queue state so
|
||||
* that eviction/suspend and queue teardown waiters don't block
|
||||
* forever on a fence that will never signal after the reset.
|
||||
*/
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
amdgpu_userq_fence_driver_force_completion(queue);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -855,12 +855,10 @@ void amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job,
|
|||
job->oa_size);
|
||||
}
|
||||
|
||||
if (vm_flush_needed || pasid_mapping_needed || cleaner_shader_needed) {
|
||||
amdgpu_fence_emit(ring, job->hw_vm_fence, 0);
|
||||
fence = &job->hw_vm_fence->base;
|
||||
/* get a ref for the job */
|
||||
dma_fence_get(fence);
|
||||
}
|
||||
amdgpu_fence_emit(ring, job->hw_vm_fence, 0);
|
||||
fence = &job->hw_vm_fence->base;
|
||||
/* get a ref for the job */
|
||||
dma_fence_get(fence);
|
||||
|
||||
if (vm_flush_needed) {
|
||||
mutex_lock(&id_mgr->lock);
|
||||
|
|
|
|||
|
|
@ -3103,6 +3103,7 @@ static void deallocate_hiq_sdma_mqd(struct kfd_node *dev,
|
|||
struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev)
|
||||
{
|
||||
struct device_queue_manager *dqm;
|
||||
int i;
|
||||
|
||||
pr_debug("Loading device queue manager\n");
|
||||
|
||||
|
|
@ -3231,6 +3232,9 @@ struct device_queue_manager *device_queue_manager_init(struct kfd_node *dev)
|
|||
deallocate_hiq_sdma_mqd(dev, &dqm->hiq_sdma_mqd);
|
||||
|
||||
out_free:
|
||||
for (i = 0; i < KFD_MQD_TYPE_MAX; i++)
|
||||
kfree(dqm->mqd_mgrs[i]);
|
||||
|
||||
kfree(dqm);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/overflow.h>
|
||||
#include "kfd_priv.h"
|
||||
#include "kfd_topology.h"
|
||||
#include "kfd_svm.h"
|
||||
|
|
@ -235,7 +236,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
|
|||
struct kfd_topology_device *topo_dev;
|
||||
u64 expected_queue_size;
|
||||
struct amdgpu_vm *vm;
|
||||
u32 total_cwsr_size;
|
||||
u64 total_cwsr_size;
|
||||
int err;
|
||||
|
||||
topo_dev = kfd_topology_device_by_id(pdd->dev->id);
|
||||
|
|
@ -308,8 +309,14 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
|
|||
goto out_err_unreserve;
|
||||
}
|
||||
|
||||
total_cwsr_size = (properties->ctx_save_restore_area_size +
|
||||
topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask);
|
||||
total_cwsr_size = (u64)properties->ctx_save_restore_area_size +
|
||||
topo_dev->node_props.debug_memory_size;
|
||||
if (check_mul_overflow(total_cwsr_size,
|
||||
NUM_XCC(pdd->dev->xcc_mask),
|
||||
&total_cwsr_size)) {
|
||||
err = -EINVAL;
|
||||
goto out_err_unreserve;
|
||||
}
|
||||
total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);
|
||||
|
||||
err = kfd_queue_buffer_get(vm, (void *)properties->ctx_save_restore_area_address,
|
||||
|
|
@ -344,7 +351,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope
|
|||
int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_properties *properties)
|
||||
{
|
||||
struct kfd_topology_device *topo_dev;
|
||||
u32 total_cwsr_size;
|
||||
u64 total_cwsr_size;
|
||||
|
||||
kfd_queue_buffer_put(&properties->wptr_bo);
|
||||
kfd_queue_buffer_put(&properties->rptr_bo);
|
||||
|
|
@ -355,8 +362,12 @@ int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_prope
|
|||
topo_dev = kfd_topology_device_by_id(pdd->dev->id);
|
||||
if (!topo_dev)
|
||||
return -EINVAL;
|
||||
total_cwsr_size = (properties->ctx_save_restore_area_size +
|
||||
topo_dev->node_props.debug_memory_size) * NUM_XCC(pdd->dev->xcc_mask);
|
||||
total_cwsr_size = (u64)properties->ctx_save_restore_area_size +
|
||||
topo_dev->node_props.debug_memory_size;
|
||||
if (check_mul_overflow(total_cwsr_size,
|
||||
NUM_XCC(pdd->dev->xcc_mask),
|
||||
&total_cwsr_size))
|
||||
return -EINVAL;
|
||||
total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);
|
||||
|
||||
kfd_queue_buffer_svm_put(pdd, properties->ctx_save_restore_area_address, total_cwsr_size);
|
||||
|
|
|
|||
|
|
@ -580,89 +580,25 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
|
|||
queue_work(system_percpu_wq, &offload_work->work);
|
||||
}
|
||||
|
||||
static void dm_vupdate_high_irq(void *interrupt_params)
|
||||
{
|
||||
struct common_irq_params *irq_params = interrupt_params;
|
||||
struct amdgpu_device *adev = irq_params->adev;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
struct drm_device *drm_dev;
|
||||
struct drm_vblank_crtc *vblank;
|
||||
ktime_t frame_duration_ns, previous_timestamp;
|
||||
unsigned long flags;
|
||||
int vrr_active;
|
||||
|
||||
acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
|
||||
|
||||
if (acrtc) {
|
||||
vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
|
||||
drm_dev = acrtc->base.dev;
|
||||
vblank = drm_crtc_vblank_crtc(&acrtc->base);
|
||||
previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
|
||||
frame_duration_ns = vblank->time - previous_timestamp;
|
||||
|
||||
if (frame_duration_ns > 0) {
|
||||
trace_amdgpu_refresh_rate_track(acrtc->base.index,
|
||||
frame_duration_ns,
|
||||
ktime_divns(NSEC_PER_SEC, frame_duration_ns));
|
||||
atomic64_set(&irq_params->previous_timestamp, vblank->time);
|
||||
}
|
||||
|
||||
drm_dbg_vbl(drm_dev,
|
||||
"crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
|
||||
vrr_active);
|
||||
|
||||
/* Core vblank handling is done here after end of front-porch in
|
||||
* vrr mode, as vblank timestamping will give valid results
|
||||
* while now done after front-porch. This will also deliver
|
||||
* page-flip completion events that have been queued to us
|
||||
* if a pageflip happened inside front-porch.
|
||||
*/
|
||||
if (vrr_active && acrtc->dm_irq_params.stream) {
|
||||
bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
|
||||
bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
|
||||
bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
|
||||
== VRR_STATE_ACTIVE_VARIABLE;
|
||||
|
||||
amdgpu_dm_crtc_handle_vblank(acrtc);
|
||||
|
||||
/* BTR processing for pre-DCE12 ASICs */
|
||||
if (adev->family < AMDGPU_FAMILY_AI) {
|
||||
spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
|
||||
mod_freesync_handle_v_update(
|
||||
adev->dm.freesync_module,
|
||||
acrtc->dm_irq_params.stream,
|
||||
&acrtc->dm_irq_params.vrr_params);
|
||||
|
||||
if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
|
||||
schedule_dc_vmin_vmax(adev,
|
||||
acrtc->dm_irq_params.stream,
|
||||
&acrtc->dm_irq_params.vrr_params.adjust);
|
||||
}
|
||||
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_crtc_high_irq() - Handles CRTC interrupt
|
||||
* @interrupt_params: used for determining the CRTC instance
|
||||
* dm_crtc_high_irq_handler() - Common OTG vblank/flip event handling
|
||||
* @adev: amdgpu device
|
||||
* @acrtc: the CRTC to service
|
||||
*
|
||||
* Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK
|
||||
* event handler.
|
||||
* Performs writeback completion, vblank event handling, CRC processing, VRR BTR
|
||||
* updates and pageflip completion delivery.
|
||||
*
|
||||
* On DCN this is driven by VUPDATE_NO_LOCK (the register latch point) from
|
||||
* dm_vupdate_high_irq(); on DCE it is driven by VLINE0 at the start of vblank
|
||||
* from dm_crtc_high_irq().
|
||||
*/
|
||||
static void dm_crtc_high_irq(void *interrupt_params)
|
||||
static void dm_crtc_high_irq_handler(struct amdgpu_device *adev,
|
||||
struct amdgpu_crtc *acrtc)
|
||||
{
|
||||
struct common_irq_params *irq_params = interrupt_params;
|
||||
struct amdgpu_device *adev = irq_params->adev;
|
||||
struct drm_writeback_job *job;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
unsigned long flags;
|
||||
int vrr_active;
|
||||
|
||||
acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
|
||||
if (!acrtc)
|
||||
return;
|
||||
bool is_dcn = amdgpu_ip_version(adev, DCE_HWIP, 0) != 0;
|
||||
|
||||
if (acrtc->wb_conn) {
|
||||
spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags);
|
||||
|
|
@ -699,12 +635,17 @@ static void dm_crtc_high_irq(void *interrupt_params)
|
|||
vrr_active, acrtc->dm_irq_params.active_planes);
|
||||
|
||||
/**
|
||||
* Core vblank handling at start of front-porch is only possible
|
||||
* in non-vrr mode, as only there vblank timestamping will give
|
||||
* valid results while done in front-porch. Otherwise defer it
|
||||
* to dm_vupdate_high_irq after end of front-porch.
|
||||
* Core vblank handling.
|
||||
*
|
||||
* On DCN this handler runs at VUPDATE_NO_LOCK, the register latch
|
||||
* point, which is the correct place to timestamp both VRR and non-VRR
|
||||
* vblanks.
|
||||
*
|
||||
* On DCE this handler runs at the start of front-porch, where only
|
||||
* non-VRR timestamping is valid; VRR vblank is deferred to
|
||||
* dm_vupdate_high_irq() after end of front-porch.
|
||||
*/
|
||||
if (!vrr_active)
|
||||
if (is_dcn || !vrr_active)
|
||||
amdgpu_dm_crtc_handle_vblank(acrtc);
|
||||
|
||||
/**
|
||||
|
|
@ -737,18 +678,33 @@ static void dm_crtc_high_irq(void *interrupt_params)
|
|||
}
|
||||
|
||||
/*
|
||||
* If there aren't any active_planes then DCH HUBP may be clock-gated.
|
||||
* In that case, pageflip completion interrupts won't fire and pageflip
|
||||
* completion events won't get delivered. Prevent this by sending
|
||||
* pending pageflip events from here if a flip is still pending.
|
||||
* Deliver pageflip completion events (DCN only).
|
||||
*
|
||||
* If any planes are enabled, use dm_pflip_high_irq() instead, to
|
||||
* avoid race conditions between flip programming and completion,
|
||||
* which could cause too early flip completion events.
|
||||
* Since GRPH_PFLIP is not used, VUPDATE_NO_LOCK is the flip latch
|
||||
* point. Deliver any pending pageflip completion event from here,
|
||||
* once HW has consumed the new address (the OTG no longer reports a
|
||||
* pending flip).
|
||||
*
|
||||
* Also handle the case here where there aren't any active planes and
|
||||
* DCN HUBP may be clock-gated, so the flip-pending status may be
|
||||
* undefined.
|
||||
*/
|
||||
if (adev->family >= AMDGPU_FAMILY_RV &&
|
||||
acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
|
||||
acrtc->dm_irq_params.active_planes == 0) {
|
||||
if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
|
||||
acrtc->event) {
|
||||
|
||||
if (!dc_get_flip_pending_on_otg(adev->dm.dc, acrtc->otg_inst)) {
|
||||
drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
|
||||
acrtc->event = NULL;
|
||||
drm_crtc_vblank_put(&acrtc->base);
|
||||
acrtc->pflip_status = AMDGPU_FLIP_NONE;
|
||||
}
|
||||
/*
|
||||
* If the flip is still pending, leave it armed and
|
||||
* retry on the next vupdate.
|
||||
*/
|
||||
} else if (is_dcn && acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED &&
|
||||
acrtc->dm_irq_params.active_planes == 0) {
|
||||
|
||||
if (acrtc->event) {
|
||||
drm_crtc_send_vblank_event(&acrtc->base, acrtc->event);
|
||||
acrtc->event = NULL;
|
||||
|
|
@ -760,6 +716,104 @@ static void dm_crtc_high_irq(void *interrupt_params)
|
|||
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
|
||||
}
|
||||
|
||||
static void dm_vupdate_high_irq(void *interrupt_params)
|
||||
{
|
||||
struct common_irq_params *irq_params = interrupt_params;
|
||||
struct amdgpu_device *adev = irq_params->adev;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
struct drm_device *drm_dev;
|
||||
struct drm_vblank_crtc *vblank;
|
||||
ktime_t frame_duration_ns, previous_timestamp;
|
||||
unsigned long flags;
|
||||
int vrr_active;
|
||||
|
||||
acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE);
|
||||
if (!acrtc)
|
||||
return;
|
||||
|
||||
vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc);
|
||||
drm_dev = acrtc->base.dev;
|
||||
vblank = drm_crtc_vblank_crtc(&acrtc->base);
|
||||
previous_timestamp = atomic64_read(&irq_params->previous_timestamp);
|
||||
frame_duration_ns = vblank->time - previous_timestamp;
|
||||
|
||||
if (frame_duration_ns > 0) {
|
||||
trace_amdgpu_refresh_rate_track(acrtc->base.index,
|
||||
frame_duration_ns,
|
||||
ktime_divns(NSEC_PER_SEC, frame_duration_ns));
|
||||
atomic64_set(&irq_params->previous_timestamp, vblank->time);
|
||||
}
|
||||
|
||||
drm_dbg_vbl(drm_dev,
|
||||
"crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id,
|
||||
vrr_active);
|
||||
|
||||
/*
|
||||
* On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
|
||||
* vblank and pageflip completion events; VSTARTUP and GRPH_PFLIP are
|
||||
* not used. Run the full handler here.
|
||||
*/
|
||||
if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
|
||||
dm_crtc_high_irq_handler(adev, acrtc);
|
||||
return;
|
||||
}
|
||||
|
||||
/* DCE only below. */
|
||||
|
||||
/* Core vblank handling is done here after end of front-porch in
|
||||
* vrr mode, as vblank timestamping will give valid results
|
||||
* while now done after front-porch. This will also deliver
|
||||
* page-flip completion events that have been queued to us
|
||||
* if a pageflip happened inside front-porch.
|
||||
*/
|
||||
if (vrr_active && acrtc->dm_irq_params.stream) {
|
||||
bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled;
|
||||
bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled;
|
||||
bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state
|
||||
== VRR_STATE_ACTIVE_VARIABLE;
|
||||
|
||||
amdgpu_dm_crtc_handle_vblank(acrtc);
|
||||
|
||||
/* BTR processing for pre-DCE12 ASICs */
|
||||
if (adev->family < AMDGPU_FAMILY_AI) {
|
||||
spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags);
|
||||
mod_freesync_handle_v_update(
|
||||
adev->dm.freesync_module,
|
||||
acrtc->dm_irq_params.stream,
|
||||
&acrtc->dm_irq_params.vrr_params);
|
||||
|
||||
if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) {
|
||||
schedule_dc_vmin_vmax(adev,
|
||||
acrtc->dm_irq_params.stream,
|
||||
&acrtc->dm_irq_params.vrr_params.adjust);
|
||||
}
|
||||
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_crtc_high_irq() - Handles CRTC interrupt
|
||||
* @interrupt_params: used for determining the CRTC instance
|
||||
*
|
||||
* Handles the CRTC/VSYNC interrupt by notifying DRM's VBLANK event handler.
|
||||
*
|
||||
* Used on DCE (VLINE0, set to vblank start). On DCN the equivalent handling is
|
||||
* driven by VUPDATE_NO_LOCK in dm_vupdate_high_irq().
|
||||
*/
|
||||
static void dm_crtc_high_irq(void *interrupt_params)
|
||||
{
|
||||
struct common_irq_params *irq_params = interrupt_params;
|
||||
struct amdgpu_device *adev = irq_params->adev;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
|
||||
acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
|
||||
if (!acrtc)
|
||||
return;
|
||||
|
||||
dm_crtc_high_irq_handler(adev, acrtc);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
|
||||
/**
|
||||
* dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
|
||||
|
|
@ -3298,6 +3352,13 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
|
|||
*/
|
||||
if (!dc_interrupt_set(adev->dm.dc, irq_source, enable))
|
||||
drm_warn(adev_to_drm(adev), "Failed to %sable vblank interrupt\n", enable ? "en" : "dis");
|
||||
|
||||
} else if (acrtc && state->stream_status[i].plane_count != 0) {
|
||||
/* DCN only needs to toggle VUPDATE_NO_LOCK */
|
||||
rc = amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, enable);
|
||||
if (rc)
|
||||
drm_warn(adev_to_drm(adev), "Failed to %sable vupdate interrupt\n",
|
||||
enable ? "en" : "dis");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4069,6 +4130,8 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
|
|||
caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps;
|
||||
caps->aux_support = false;
|
||||
|
||||
panel_backlight_quirk = drm_get_panel_backlight_quirk(aconnector->drm_edid);
|
||||
|
||||
if (caps->ext_caps->bits.oled == 1
|
||||
/*
|
||||
* ||
|
||||
|
|
@ -4081,6 +4144,9 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
|
|||
caps->aux_support = false;
|
||||
else if (amdgpu_backlight == 1)
|
||||
caps->aux_support = true;
|
||||
else if (!IS_ERR_OR_NULL(panel_backlight_quirk) &&
|
||||
panel_backlight_quirk->force_pwm)
|
||||
caps->aux_support = false;
|
||||
if (caps->aux_support)
|
||||
aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX;
|
||||
|
||||
|
|
@ -4096,8 +4162,6 @@ static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
|
|||
else
|
||||
caps->aux_min_input_signal = 1;
|
||||
|
||||
panel_backlight_quirk =
|
||||
drm_get_panel_backlight_quirk(aconnector->drm_edid);
|
||||
if (!IS_ERR_OR_NULL(panel_backlight_quirk)) {
|
||||
if (panel_backlight_quirk->min_brightness) {
|
||||
caps->min_input_signal =
|
||||
|
|
@ -4863,38 +4927,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
|
|||
* for acknowledging and handling.
|
||||
*/
|
||||
|
||||
/* Use VSTARTUP interrupt */
|
||||
for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP;
|
||||
i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1;
|
||||
i++) {
|
||||
r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq);
|
||||
|
||||
if (r) {
|
||||
drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
|
||||
int_params.irq_source =
|
||||
dc_interrupt_to_irq_source(dc, i, 0);
|
||||
|
||||
if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
|
||||
int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 ||
|
||||
int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) {
|
||||
drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1];
|
||||
|
||||
c_irq_params->adev = adev;
|
||||
c_irq_params->irq_src = int_params.irq_source;
|
||||
|
||||
if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
|
||||
dm_crtc_high_irq, c_irq_params))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Use otg vertical line interrupt */
|
||||
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
|
||||
for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) {
|
||||
|
|
@ -4966,37 +4998,6 @@ static int dcn10_register_irq_handlers(struct amdgpu_device *adev)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Use GRPH_PFLIP interrupt */
|
||||
for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT;
|
||||
i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1;
|
||||
i++) {
|
||||
r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq);
|
||||
if (r) {
|
||||
drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT;
|
||||
int_params.irq_source =
|
||||
dc_interrupt_to_irq_source(dc, i, 0);
|
||||
|
||||
if (int_params.irq_source == DC_IRQ_SOURCE_INVALID ||
|
||||
int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST ||
|
||||
int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) {
|
||||
drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST];
|
||||
|
||||
c_irq_params->adev = adev;
|
||||
c_irq_params->irq_src = int_params.irq_source;
|
||||
|
||||
if (!amdgpu_dm_irq_register_interrupt(adev, &int_params,
|
||||
dm_pflip_high_irq, c_irq_params))
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* HPD */
|
||||
r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT,
|
||||
&adev->hpd_irq);
|
||||
|
|
@ -5563,11 +5564,11 @@ amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
|
|||
caps = &dm->backlight_caps[aconnector->bl_idx];
|
||||
if (get_brightness_range(caps, &min, &max)) {
|
||||
if (power_supply_is_system_supplied() > 0)
|
||||
props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, 100);
|
||||
props.brightness = DIV_ROUND_CLOSEST(max * caps->ac_level, 100);
|
||||
else
|
||||
props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, 100);
|
||||
props.brightness = DIV_ROUND_CLOSEST(max * caps->dc_level, 100);
|
||||
/* min is zero, so max needs to be adjusted */
|
||||
props.max_brightness = max - min;
|
||||
props.max_brightness = max;
|
||||
drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max,
|
||||
caps->ac_level, caps->dc_level);
|
||||
} else
|
||||
|
|
@ -9674,21 +9675,9 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
|
|||
if (acrtc_state) {
|
||||
timing = &acrtc_state->stream->timing;
|
||||
|
||||
if (amdgpu_ip_version(adev, DCE_HWIP, 0) >=
|
||||
IP_VERSION(3, 2, 0) &&
|
||||
!(adev->flags & AMD_IS_APU)) {
|
||||
/*
|
||||
* DGPUs NV3x and newer that support idle optimizations
|
||||
* experience intermittent flip-done timeouts on cursor
|
||||
* updates. Restore 5s offdelay behavior for now.
|
||||
*
|
||||
* Discussion on the issue:
|
||||
* https://lore.kernel.org/amd-gfx/20260217191632.1243826-1-sysdadmin@m1k.cloud/
|
||||
*/
|
||||
config.offdelay_ms = 5000;
|
||||
config.disable_immediate = false;
|
||||
} else if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
|
||||
IP_VERSION(3, 5, 0)) {
|
||||
if (amdgpu_ip_version(adev, DCE_HWIP, 0) <
|
||||
IP_VERSION(3, 5, 0) ||
|
||||
!(adev->flags & AMD_IS_APU)) {
|
||||
/*
|
||||
* Older HW and DGPU have issues with instant off;
|
||||
* use a 2 frame offdelay.
|
||||
|
|
@ -9707,14 +9696,22 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
|
|||
|
||||
drm_crtc_vblank_on_config(&acrtc->base,
|
||||
&config);
|
||||
/* Allow RX6xxx, RX7700, RX7800 GPUs to call amdgpu_irq_get.*/
|
||||
/*
|
||||
* Since pflip_high_irq is no longer registered for DCN, grab an
|
||||
* extra reference to vupdate irq instead to workaround this
|
||||
* issue:
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/work_items/3936
|
||||
*
|
||||
* The callbacks to drm_vblank_on/off should really take care of
|
||||
* this though.
|
||||
*/
|
||||
switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
|
||||
case IP_VERSION(3, 0, 0):
|
||||
case IP_VERSION(3, 0, 2):
|
||||
case IP_VERSION(3, 0, 3):
|
||||
case IP_VERSION(3, 2, 0):
|
||||
if (amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type))
|
||||
drm_err(dev, "DM_IRQ: Cannot get pageflip irq!\n");
|
||||
if (amdgpu_irq_get(adev, &adev->vupdate_irq, irq_type))
|
||||
drm_err(dev, "DM_IRQ: Cannot get vupdate irq!\n");
|
||||
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
|
||||
if (amdgpu_irq_get(adev, &adev->vline0_irq, irq_type))
|
||||
drm_err(dev, "DM_IRQ: Cannot get vline0 irq!\n");
|
||||
|
|
@ -9732,8 +9729,8 @@ static void manage_dm_interrupts(struct amdgpu_device *adev,
|
|||
if (amdgpu_irq_put(adev, &adev->vline0_irq, irq_type))
|
||||
drm_err(dev, "DM_IRQ: Cannot put vline0 irq!\n");
|
||||
#endif
|
||||
if (amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type))
|
||||
drm_err(dev, "DM_IRQ: Cannot put pageflip irq!\n");
|
||||
if (amdgpu_irq_put(adev, &adev->vupdate_irq, irq_type))
|
||||
drm_err(dev, "DM_IRQ: Cannot put vupdate irq!\n");
|
||||
}
|
||||
|
||||
drm_crtc_vblank_off(&acrtc->base);
|
||||
|
|
@ -9746,6 +9743,10 @@ static void dm_update_pflip_irq_state(struct amdgpu_device *adev,
|
|||
int irq_type =
|
||||
amdgpu_display_crtc_idx_to_irq_type(adev, acrtc->crtc_id);
|
||||
|
||||
/* GRPH_PFLIP is not used on DCN; nothing to reapply. */
|
||||
if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0)
|
||||
return;
|
||||
|
||||
/**
|
||||
* This reads the current state for the IRQ and force reapplies
|
||||
* the setting to hardware.
|
||||
|
|
@ -10078,9 +10079,13 @@ static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
|
|||
struct dm_crtc_state *old_state,
|
||||
struct dm_crtc_state *new_state)
|
||||
{
|
||||
struct amdgpu_device *adev = drm_to_adev(new_state->base.crtc->dev);
|
||||
bool old_vrr_active = amdgpu_dm_crtc_vrr_active(old_state);
|
||||
bool new_vrr_active = amdgpu_dm_crtc_vrr_active(new_state);
|
||||
|
||||
/* Only DCE gates vupdate on VRR, keep it enabled for DCN */
|
||||
bool vrr_gates_vupdate = amdgpu_ip_version(adev, DCE_HWIP, 0) == 0;
|
||||
|
||||
if (!old_vrr_active && new_vrr_active) {
|
||||
/* Transition VRR inactive -> active:
|
||||
* While VRR is active, we must not disable vblank irq, as a
|
||||
|
|
@ -10090,7 +10095,8 @@ static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
|
|||
* We also need vupdate irq for the actual core vblank handling
|
||||
* at end of vblank.
|
||||
*/
|
||||
WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
|
||||
if (vrr_gates_vupdate)
|
||||
WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, true) != 0);
|
||||
WARN_ON(drm_crtc_vblank_get(new_state->base.crtc) != 0);
|
||||
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR off->on: Get vblank ref\n",
|
||||
__func__, new_state->base.crtc->base.id);
|
||||
|
|
@ -10106,7 +10112,8 @@ static void amdgpu_dm_handle_vrr_transition(struct amdgpu_display_manager *dm,
|
|||
/* Transition VRR active -> inactive:
|
||||
* Allow vblank irq disable again for fixed refresh rate.
|
||||
*/
|
||||
WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
|
||||
if (vrr_gates_vupdate)
|
||||
WARN_ON(amdgpu_dm_crtc_set_vupdate_irq(new_state->base.crtc, false) != 0);
|
||||
drm_crtc_vblank_put(new_state->base.crtc);
|
||||
drm_dbg_driver(new_state->base.crtc->dev, "%s: crtc=%u VRR on->off: Drop vblank ref\n",
|
||||
__func__, new_state->base.crtc->base.id);
|
||||
|
|
@ -10255,6 +10262,28 @@ static void amdgpu_dm_enable_self_refresh(struct amdgpu_display_manager *dm,
|
|||
}
|
||||
}
|
||||
|
||||
static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc,
|
||||
struct dm_crtc_state *acrtc_state,
|
||||
bool pflip_update,
|
||||
bool cursor_update)
|
||||
{
|
||||
assert_spin_locked(&acrtc->base.dev->event_lock);
|
||||
|
||||
if (!acrtc->base.state->event || acrtc_state->active_planes == 0)
|
||||
return;
|
||||
|
||||
if (pflip_update) {
|
||||
drm_crtc_vblank_get(&acrtc->base);
|
||||
WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE);
|
||||
/* Arm flip completion handling and event delivery after programming. */
|
||||
prepare_flip_isr(acrtc);
|
||||
} else if (cursor_update) {
|
||||
drm_crtc_vblank_get(&acrtc->base);
|
||||
acrtc->event = acrtc->base.state->event;
|
||||
acrtc->base.state->event = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
|
||||
struct drm_device *dev,
|
||||
struct amdgpu_display_manager *dm,
|
||||
|
|
@ -10277,6 +10306,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
|
|||
bool vrr_active = amdgpu_dm_crtc_vrr_active(acrtc_state);
|
||||
bool cursor_update = false;
|
||||
bool pflip_present = false;
|
||||
bool immediate_flip = false;
|
||||
bool flip_latched_during_prog = false;
|
||||
bool dirty_rects_changed = false;
|
||||
bool updated_planes_and_streams = false;
|
||||
struct {
|
||||
|
|
@ -10441,6 +10472,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
|
|||
acrtc_state->update_type == UPDATE_TYPE_FAST &&
|
||||
get_mem_type(old_plane_state->fb) == get_mem_type(fb);
|
||||
|
||||
immediate_flip |= bundle->flip_addrs[planes_count].flip_immediate;
|
||||
|
||||
timestamp_ns = ktime_get_ns();
|
||||
bundle->flip_addrs[planes_count].flip_timestamp_in_us = div_u64(timestamp_ns, 1000);
|
||||
bundle->surface_updates[planes_count].flip_addr = &bundle->flip_addrs[planes_count];
|
||||
|
|
@ -10509,39 +10542,24 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
|
|||
usleep_range(1000, 1100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare the flip event for the pageflip interrupt to handle.
|
||||
*
|
||||
* This only works in the case where we've already turned on the
|
||||
* appropriate hardware blocks (eg. HUBP) so in the transition case
|
||||
* from 0 -> n planes we have to skip a hardware generated event
|
||||
* and rely on sending it from software.
|
||||
*/
|
||||
if (acrtc_attach->base.state->event &&
|
||||
acrtc_state->active_planes > 0) {
|
||||
drm_crtc_vblank_get(pcrtc);
|
||||
|
||||
spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
|
||||
|
||||
WARN_ON(acrtc_attach->pflip_status != AMDGPU_FLIP_NONE);
|
||||
prepare_flip_isr(acrtc_attach);
|
||||
|
||||
spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
|
||||
}
|
||||
|
||||
if (acrtc_state->stream) {
|
||||
if (acrtc_state->freesync_vrr_info_changed)
|
||||
bundle->stream_update.vrr_infopacket =
|
||||
&acrtc_state->stream->vrr_infopacket;
|
||||
}
|
||||
} else if (cursor_update && acrtc_state->active_planes > 0) {
|
||||
spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
|
||||
if (acrtc_attach->base.state->event) {
|
||||
drm_crtc_vblank_get(pcrtc);
|
||||
acrtc_attach->event = acrtc_attach->base.state->event;
|
||||
acrtc_attach->base.state->event = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for
|
||||
* event delivery. Only GRPH_FLIP handler can send pflip events, and it
|
||||
* only fires if HW latched to the flip. Maintain legacy behavior by
|
||||
* arming event before programming.
|
||||
*/
|
||||
if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) {
|
||||
scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) {
|
||||
dm_arm_vblank_event(acrtc_attach, acrtc_state,
|
||||
pflip_present, cursor_update);
|
||||
}
|
||||
spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
|
||||
}
|
||||
|
||||
/* Update the planes if changed or disable if we don't have any. */
|
||||
|
|
@ -10633,6 +10651,115 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state,
|
|||
acrtc_state->cursor_mode == DM_CURSOR_NATIVE_MODE)
|
||||
amdgpu_dm_commit_cursors(state);
|
||||
|
||||
/*
|
||||
* DCN specific vblank handling
|
||||
* ============================
|
||||
*
|
||||
* With the event_lock held, arm the vblank event, and determine whether
|
||||
* deliver it immediately, or in VUPDATE_NO_LOCK IRQ (i.e. HW latch
|
||||
* point) handler. Do this *after* programming so that the IRQ handler
|
||||
* will not deliver the event before HW laches onto the programmed
|
||||
* values:
|
||||
*
|
||||
* Commit thread IRQ handler HW
|
||||
* -----------------------------------------------------------------
|
||||
* arm_vblank_event()
|
||||
* vupdate()
|
||||
* vupdate_handler()
|
||||
* cook_timestamp()
|
||||
* # prev flip already latched,
|
||||
* # so flip_latched == true.
|
||||
* if event_armed && flip_latched:
|
||||
* send_vblank_event()
|
||||
* # sent before latch, **BAD!**
|
||||
* hw_program()
|
||||
* vupdate()
|
||||
* **latch**
|
||||
*
|
||||
* There's a consequence of arming after: it's possible for HW to latch
|
||||
* between start of HW programming and acrtc->event/pflip_status arming.
|
||||
* When this happens, the IRQ handler will send the event on the next
|
||||
* immediate latch point, even though HW has already latched. This is
|
||||
* handled by optimistically checking for HW latch after programming,
|
||||
* and if latched, send the event immediately:
|
||||
*
|
||||
* Commit thread IRQ handler HW
|
||||
* -----------------------------------------------------------------
|
||||
* hw_program()
|
||||
* vupdate()
|
||||
* **latch**
|
||||
* vupdate_handler()
|
||||
* cook_timestamp()
|
||||
* # event_armed == false
|
||||
* # **no event sent!**
|
||||
* arm_vblank_event()
|
||||
* if flip_latched:
|
||||
* **send_vblank_event()**
|
||||
* disarm_vblank_event()
|
||||
*
|
||||
* The IRQ handler is expected to cook the timestamp, but we need to
|
||||
* cook the timestamp before optimistic sending as well. That's because
|
||||
* the following sequence is possible:
|
||||
*
|
||||
* Commit thread IRQ handler HW
|
||||
* -----------------------------------------------------------------
|
||||
* hw_program()
|
||||
* arm_vblank_event()
|
||||
* vupdate()
|
||||
* **latch**
|
||||
* if flip_latched:
|
||||
* # Need cook before send!
|
||||
* **cook_timestamp()**
|
||||
* send_vblank_event()
|
||||
* disarm_vblank_event()
|
||||
* vupdate_handler()
|
||||
* cook_timestamp()
|
||||
* # event_armed == false
|
||||
* # no event sent!
|
||||
*
|
||||
* Cooking twice is OK, since DRM scanout accurate timestamps report A)
|
||||
* the previous vactive start if currently in vactive, or B) the next
|
||||
* vactive start if currently in vblank (see &get_vblank_counter). 'A)'
|
||||
* is what we want for the optimistic send, and for 'B)', we'll cook a
|
||||
* timestamp no later than the next IRQ handler run.
|
||||
*
|
||||
* The more correct fix is to wrap programming and arming with the
|
||||
* event_lock and thus serializing it with the IRQ handler. However,
|
||||
* there are various sleep-waits within
|
||||
* update_planes_and_stream_adapter() that makes spin locking illegal.
|
||||
* And on full updates, it can take 1-2 frame-times to return (see
|
||||
* commit_planes_for_stream).
|
||||
*
|
||||
* On DCE, GRPH_PFLIP IRQ is used and takes care of this.
|
||||
*/
|
||||
if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) != 0) {
|
||||
spin_lock_irqsave(&pcrtc->dev->event_lock, flags);
|
||||
|
||||
if (updated_planes_and_streams) {
|
||||
flip_latched_during_prog =
|
||||
!dc_get_flip_pending_on_otg(dm->dc, acrtc_attach->otg_inst);
|
||||
}
|
||||
|
||||
dm_arm_vblank_event(acrtc_attach, acrtc_state,
|
||||
pflip_present, cursor_update);
|
||||
|
||||
/*
|
||||
* Deliver the event immediately on immediate flip, or on a
|
||||
* update that has already latched.
|
||||
*/
|
||||
if ((immediate_flip || flip_latched_during_prog) &&
|
||||
acrtc_attach->pflip_status == AMDGPU_FLIP_SUBMITTED &&
|
||||
acrtc_attach->event) {
|
||||
drm_crtc_accurate_vblank_count(&acrtc_attach->base);
|
||||
drm_crtc_send_vblank_event(&acrtc_attach->base,
|
||||
acrtc_attach->event);
|
||||
acrtc_attach->event = NULL;
|
||||
drm_crtc_vblank_put(&acrtc_attach->base);
|
||||
acrtc_attach->pflip_status = AMDGPU_FLIP_NONE;
|
||||
}
|
||||
spin_unlock_irqrestore(&pcrtc->dev->event_lock, flags);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
kfree(bundle);
|
||||
}
|
||||
|
|
@ -12098,6 +12225,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm,
|
|||
/* Release extra reference */
|
||||
if (new_stream)
|
||||
dc_stream_release(new_stream);
|
||||
new_stream = NULL;
|
||||
|
||||
/*
|
||||
* We want to do dc stream updates that do not require a
|
||||
|
|
@ -12814,10 +12942,15 @@ static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev,
|
|||
/* Overlay cursor not supported on HW before DCN
|
||||
* DCN401/420 does not have the cursor-on-scaled-plane or cursor-on-yuv-plane restrictions
|
||||
* as previous DCN generations, so enable native mode on DCN401/420
|
||||
*
|
||||
* Always set native cursor mode when the CRTC is disabled,
|
||||
* to make sure it doesn't cause atomic commits to fail when
|
||||
* they are trying to disable the CRTC.
|
||||
*/
|
||||
if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 0, 1) ||
|
||||
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0) ||
|
||||
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1)) {
|
||||
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 1) ||
|
||||
!dm_crtc_state->base.enable) {
|
||||
*cursor_mode = DM_CURSOR_NATIVE_MODE;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -274,7 +274,14 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
|
|||
drm_crtc_vblank_restore(crtc);
|
||||
}
|
||||
|
||||
if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
|
||||
/*
|
||||
* On DCN, VUPDATE_NO_LOCK is the single OTG interrupt used to deliver
|
||||
* vblank and pageflip completion events, so enable it whenever vblank
|
||||
* is enabled. On DCE, vupdate is only needed in VRR mode.
|
||||
*/
|
||||
if (amdgpu_ip_version(adev, DCE_HWIP, 0) != 0) {
|
||||
rc = amdgpu_dm_crtc_set_vupdate_irq(crtc, enable);
|
||||
} else if (dc_supports_vrr(dm->dc->ctx->dce_version)) {
|
||||
if (enable) {
|
||||
/* vblank irq on -> Only need vupdate irq in vrr mode */
|
||||
if (amdgpu_dm_crtc_vrr_active(acrtc_state))
|
||||
|
|
@ -285,39 +292,46 @@ static inline int amdgpu_dm_crtc_set_vblank(struct drm_crtc *crtc, bool enable)
|
|||
}
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* crtc vblank or vstartup interrupt */
|
||||
if (enable) {
|
||||
rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type);
|
||||
drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc);
|
||||
} else {
|
||||
rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type);
|
||||
drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc);
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* hubp surface flip interrupt
|
||||
*
|
||||
* We have no guarantee that the frontend index maps to the same
|
||||
* backend index - some even map to more than one.
|
||||
*
|
||||
* TODO: Use a different interrupt or check DC itself for the mapping.
|
||||
* VLINE0 (crtc_irq) and GRPH_PFLIP (pageflip_irq) are only used on
|
||||
* DCE. On DCN, vblank and pageflip completion are delivered from
|
||||
* VUPDATE_NO_LOCK (enabled above), so don't touch them here.
|
||||
*/
|
||||
if (enable) {
|
||||
rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type);
|
||||
drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc);
|
||||
} else {
|
||||
rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type);
|
||||
drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc);
|
||||
}
|
||||
if (amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
|
||||
/* crtc vblank or vstartup interrupt */
|
||||
if (enable) {
|
||||
rc = amdgpu_irq_get(adev, &adev->crtc_irq, irq_type);
|
||||
drm_dbg_vbl(crtc->dev, "Get crtc_irq ret=%d\n", rc);
|
||||
} else {
|
||||
rc = amdgpu_irq_put(adev, &adev->crtc_irq, irq_type);
|
||||
drm_dbg_vbl(crtc->dev, "Put crtc_irq ret=%d\n", rc);
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* hubp surface flip interrupt
|
||||
*
|
||||
* We have no guarantee that the frontend index maps to the same
|
||||
* backend index - some even map to more than one.
|
||||
*
|
||||
* TODO: Use a different interrupt or check DC itself for the mapping.
|
||||
*/
|
||||
if (enable) {
|
||||
rc = amdgpu_irq_get(adev, &adev->pageflip_irq, irq_type);
|
||||
drm_dbg_vbl(crtc->dev, "Get pageflip_irq ret=%d\n", rc);
|
||||
} else {
|
||||
rc = amdgpu_irq_put(adev, &adev->pageflip_irq, irq_type);
|
||||
drm_dbg_vbl(crtc->dev, "Put pageflip_irq ret=%d\n", rc);
|
||||
}
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
|
||||
/* crtc vline0 interrupt, only available on DCN+ */
|
||||
|
|
|
|||
|
|
@ -1201,11 +1201,25 @@ enum dc_edid_status dm_helpers_read_local_edid(
|
|||
continue;
|
||||
|
||||
edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw()
|
||||
if (!edid ||
|
||||
edid->extensions >= sizeof(sink->dc_edid.raw_edid) / EDID_LENGTH)
|
||||
/*
|
||||
* Use the length of the EDID property blob populated by
|
||||
* drm_edid_connector_update() above. It reflects the true number
|
||||
* of EDID blocks, including any HDMI Forum EDID Extension Override
|
||||
* Data Block (HF-EEODB) count, which the raw byte 0x7e extension
|
||||
* count can hide (e.g. HDMI 8K sinks).
|
||||
*/
|
||||
if (!edid || !connector->edid_blob_ptr ||
|
||||
connector->edid_blob_ptr->length > sizeof(sink->dc_edid.raw_edid))
|
||||
return EDID_BAD_INPUT;
|
||||
|
||||
sink->dc_edid.length = EDID_LENGTH * (edid->extensions + 1);
|
||||
/*
|
||||
* FIXME: amdgpu_dm today does not consider the HF-EEODB, which
|
||||
* may contain additional mode info for sinks. This is a
|
||||
* workaround until dc_edid is refactored out from DC into
|
||||
* amdgpu_dm's ownership, allowing amdgpu_dm to use drm_edid
|
||||
* directly
|
||||
*/
|
||||
sink->dc_edid.length = connector->edid_blob_ptr->length;
|
||||
memmove(sink->dc_edid.raw_edid, (uint8_t *)edid, sink->dc_edid.length);
|
||||
|
||||
/* We don't need the original edid anymore */
|
||||
|
|
|
|||
|
|
@ -6165,6 +6165,51 @@ void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src)
|
|||
dal_irq_service_ack(dc->res_pool->irqs, src);
|
||||
}
|
||||
|
||||
/*
|
||||
* dc_get_flip_pending_on_otg() - Check if a GRPH_FLIP is still pending on OTG
|
||||
*
|
||||
* @dc: display core context @otg_inst: OTG instance to query
|
||||
*
|
||||
* Reads the HUBP flip-pending status for the pipe(s) bound to @otg_inst,
|
||||
* returning true if any of them has not yet latched its programmed surface
|
||||
* address.
|
||||
*
|
||||
* Unlike dc_plane_get_status(), this does not take or mutate a dc_plane_state,
|
||||
* so it is safe to call from interrupt context without racing a concurrent
|
||||
* commit that may be updating plane state.
|
||||
*
|
||||
* Return: true if a flip is still pending on the OTG, false otherwise.
|
||||
*/
|
||||
bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst)
|
||||
{
|
||||
bool flip_pending = false;
|
||||
int i;
|
||||
|
||||
if (!dc || !dc->current_state)
|
||||
return false;
|
||||
|
||||
dc_exit_ips_for_hw_access(dc);
|
||||
|
||||
for (i = 0; i < dc->res_pool->pipe_count; i++) {
|
||||
struct pipe_ctx *pipe_ctx = &dc->current_state->res_ctx.pipe_ctx[i];
|
||||
struct hubp *hubp = pipe_ctx->plane_res.hubp;
|
||||
|
||||
if (!pipe_ctx->plane_state || !pipe_ctx->stream_res.tg)
|
||||
continue;
|
||||
|
||||
if (pipe_ctx->stream_res.tg->inst != otg_inst)
|
||||
continue;
|
||||
|
||||
if (hubp && hubp->funcs->hubp_is_flip_pending &&
|
||||
hubp->funcs->hubp_is_flip_pending(hubp)) {
|
||||
flip_pending = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return flip_pending;
|
||||
}
|
||||
|
||||
void dc_power_down_on_boot(struct dc *dc)
|
||||
{
|
||||
if (dc->ctx->dce_environment != DCE_ENV_VIRTUAL_HW &&
|
||||
|
|
|
|||
|
|
@ -1815,6 +1815,8 @@ struct dc_scratch_space {
|
|||
bool dp_skip_DID2;
|
||||
bool dp_skip_reset_segment;
|
||||
bool dp_skip_fs_144hz;
|
||||
/* Some DP bridges don't work with RBR and must use HBR. */
|
||||
bool dp_skip_rbr;
|
||||
bool dp_mot_reset_segment;
|
||||
/* Some USB4 docks do not handle turning off MST DSC once it has been enabled. */
|
||||
bool dpia_mst_dsc_always_on;
|
||||
|
|
@ -2881,6 +2883,7 @@ enum dc_irq_source dc_interrupt_to_irq_source(
|
|||
uint32_t ext_id);
|
||||
bool dc_interrupt_set(struct dc *dc, enum dc_irq_source src, bool enable);
|
||||
void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src);
|
||||
bool dc_get_flip_pending_on_otg(struct dc *dc, int otg_inst);
|
||||
enum dc_irq_source dc_get_hpd_irq_source_at_index(
|
||||
struct dc *dc, uint32_t link_index);
|
||||
|
||||
|
|
|
|||
|
|
@ -1229,9 +1229,9 @@ static bool get_dp_dto_frequency_100hz(
|
|||
*/
|
||||
modulo_hz = REG_READ(MODULO[inst]);
|
||||
if (modulo_hz) {
|
||||
temp = div_u64((uint64_t)clock_hz * dp_dto_ref_khz * 10, modulo_hz);
|
||||
ASSERT(temp / 100 <= 0xFFFFFFFFUL);
|
||||
*pixel_clk_100hz = (unsigned int)(temp / 100);
|
||||
temp = clock_hz * dp_dto_ref_khz * 10;
|
||||
ASSERT(temp <= UINT_MAX * modulo_hz * 100ULL);
|
||||
*pixel_clk_100hz = div_u64(temp, modulo_hz * 100);
|
||||
} else
|
||||
*pixel_clk_100hz = 0;
|
||||
} else {
|
||||
|
|
@ -1285,13 +1285,12 @@ static bool dcn401_get_dp_dto_frequency_100hz(const struct clock_source *clock_s
|
|||
* - target pix_clk_hz = (DPDTO INTEGER * DPDTO MODULO + DPDTO PHASE)
|
||||
*/
|
||||
temp = (unsigned long long)dp_dto_integer * modulo_hz + phase_hz;
|
||||
|
||||
if (temp / 100 > 0xFFFFFFFFUL) {
|
||||
if (temp > (UINT_MAX * 100ULL)) {
|
||||
/* pixel rate 100hz should never be this high, if it is, throw an assert and return 0 */
|
||||
BREAK_TO_DEBUGGER();
|
||||
*pixel_clk_100hz = 0;
|
||||
} else {
|
||||
*pixel_clk_100hz = (unsigned int)(temp / 100);
|
||||
*pixel_clk_100hz = div_u64(temp, 100);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ static bool detect_dp(struct dc_link *link,
|
|||
link->dpcd_caps.sink_count.bits.SINK_COUNT = 1;
|
||||
/* NUTMEG requires that we use HBR, doesn't work with RBR. */
|
||||
if (link->dpcd_caps.branch_dev_id == DP_BRANCH_DEVICE_ID_00001A)
|
||||
link->preferred_link_setting.link_rate = LINK_RATE_HIGH;
|
||||
link->wa_flags.dp_skip_rbr = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -750,8 +750,10 @@ static bool decide_dp_link_settings(struct dc_link *link, struct dc_link_setting
|
|||
if (req_bw > dp_link_bandwidth_kbps(link, &link->verified_link_cap))
|
||||
return false;
|
||||
|
||||
if (link->preferred_link_setting.link_rate != LINK_RATE_UNKNOWN)
|
||||
initial_link_setting.link_rate = link->preferred_link_setting.link_rate;
|
||||
if (link->wa_flags.dp_skip_rbr) {
|
||||
initial_link_setting.link_rate = LINK_RATE_HIGH;
|
||||
current_link_setting.link_rate = LINK_RATE_HIGH;
|
||||
}
|
||||
|
||||
/* search for the minimum link setting that:
|
||||
* 1. is supported according to the link training result
|
||||
|
|
|
|||
|
|
@ -992,6 +992,11 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link(
|
|||
for (i = 0; i < pool->stream_enc_count; i++) {
|
||||
if (!res_ctx->is_stream_enc_acquired[i] &&
|
||||
pool->stream_enc[i]) {
|
||||
/* DP/MST needs a digital encoder; skip analog/no-DP encoders */
|
||||
if (dc_is_dp_signal(stream->signal) &&
|
||||
(!pool->stream_enc[i]->funcs ||
|
||||
!pool->stream_enc[i]->funcs->dp_set_stream_attribute))
|
||||
continue;
|
||||
/* Store first available for MST second display
|
||||
* in daisy chain use case
|
||||
*/
|
||||
|
|
@ -1014,7 +1019,7 @@ struct stream_encoder *dce100_find_first_free_match_stream_enc_for_link(
|
|||
* required for non DP connectors.
|
||||
*/
|
||||
|
||||
if (j >= 0 && link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT)
|
||||
if (j >= 0 && dc_is_dp_signal(stream->signal))
|
||||
return pool->stream_enc[j];
|
||||
|
||||
return NULL;
|
||||
|
|
|
|||
|
|
@ -2142,6 +2142,7 @@ static bool dcn42_resource_construct(
|
|||
dc->config.use_pipe_ctx_sync_logic = true;
|
||||
dc->config.dc_mode_clk_limit_support = false;
|
||||
dc->config.enable_windowed_mpo_odm = true;
|
||||
dc->config.set_pipe_unlock_order = true; /* Need to ensure DET gets freed before allocating */
|
||||
/* Use psp mailbox to enable assr */
|
||||
dc->config.use_assr_psp_message = true;
|
||||
/* dcn42 and afterward always support external panel replay */
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#include "dcn35/dcn35_resource.h"
|
||||
#include "dcn321/dcn321_resource.h"
|
||||
#include "dcn401/dcn401_resource.h"
|
||||
#include "dcn42/dcn42_resource.h"
|
||||
#include "dcn42/dcn42_resource_fpu.h"
|
||||
|
||||
#include "dcn10/dcn10_ipp.h"
|
||||
|
|
@ -116,6 +117,23 @@
|
|||
#define regAPG9_APG_DBG_GEN_CONTROL 0x38ae
|
||||
#define regAPG9_APG_DBG_GEN_CONTROL_BASE_IDX 2
|
||||
|
||||
#define regHUBP0_HUBPREQ_DEBUG_DB 0x05f8
|
||||
#define regHUBP0_HUBPREQ_DEBUG_DB_BASE_IDX 2
|
||||
#define regHUBP0_HUBPREQ_DEBUG 0x05f9
|
||||
#define regHUBP0_HUBPREQ_DEBUG_BASE_IDX 2
|
||||
#define regHUBP1_HUBPREQ_DEBUG_DB 0x06d4
|
||||
#define regHUBP1_HUBPREQ_DEBUG_DB_BASE_IDX 2
|
||||
#define regHUBP1_HUBPREQ_DEBUG 0x06d5
|
||||
#define regHUBP1_HUBPREQ_DEBUG_BASE_IDX 2
|
||||
#define regHUBP2_HUBPREQ_DEBUG_DB 0x07b0
|
||||
#define regHUBP2_HUBPREQ_DEBUG_DB_BASE_IDX 2
|
||||
#define regHUBP2_HUBPREQ_DEBUG 0x07b1
|
||||
#define regHUBP2_HUBPREQ_DEBUG_BASE_IDX 2
|
||||
#define regHUBP3_HUBPREQ_DEBUG_DB 0x088c
|
||||
#define regHUBP3_HUBPREQ_DEBUG_DB_BASE_IDX 2
|
||||
#define regHUBP3_HUBPREQ_DEBUG 0x088d
|
||||
#define regHUBP3_HUBPREQ_DEBUG_BASE_IDX 2
|
||||
|
||||
enum dcn401_clk_src_array_id {
|
||||
DCN401_CLK_SRC_PLL0,
|
||||
DCN401_CLK_SRC_PLL1,
|
||||
|
|
@ -461,7 +479,7 @@ static const struct dcn_optc_mask optc_mask = {
|
|||
OPTC_COMMON_MASK_SH_LIST_DCN42B(_MASK)};
|
||||
|
||||
#define hubp_regs_init(id) \
|
||||
HUBP_REG_LIST_DCN42B_RI(id)
|
||||
HUBP_REG_LIST_DCN42_RI(id)
|
||||
|
||||
static struct dcn_hubp2_registers hubp_regs[4];
|
||||
|
||||
|
|
@ -1882,9 +1900,7 @@ static struct resource_funcs dcn42b_res_pool_funcs = {
|
|||
.update_soc_for_wm_a = dcn30_update_soc_for_wm_a,
|
||||
.add_phantom_pipes = dcn32_add_phantom_pipes,
|
||||
.calculate_mall_ways_from_bytes = dcn32_calculate_mall_ways_from_bytes,
|
||||
#ifdef CONFIG_DRM_AMD_DC_DML21
|
||||
.prepare_mcache_programming = dcn42b_prepare_mcache_programming,
|
||||
#endif
|
||||
.build_pipe_pix_clk_params = dcn42b_build_pipe_pix_clk_params,
|
||||
.get_power_profile = dcn401_get_power_profile,
|
||||
.get_vstartup_for_pipe = dcn401_get_vstartup_for_pipe,
|
||||
|
|
@ -2087,6 +2103,7 @@ static bool dcn42b_resource_construct(
|
|||
dc->config.use_pipe_ctx_sync_logic = true;
|
||||
dc->config.dc_mode_clk_limit_support = false;
|
||||
dc->config.enable_windowed_mpo_odm = true;
|
||||
dc->config.set_pipe_unlock_order = true; /* Need to ensure DET gets freed before allocating */
|
||||
/* Use psp mailbox to enable assr */
|
||||
dc->config.use_assr_psp_message = true;
|
||||
/* dcn42 and afterward always support external panel replay */
|
||||
|
|
|
|||
|
|
@ -344,7 +344,6 @@
|
|||
* DCCG_SRII(PHASE, DP_DTO, 3),
|
||||
* DCCG_SRII(MODULO, DP_DTO, 3),
|
||||
* SR(DSCCLK3_DTO_PARAM),
|
||||
* SR(HDMISTREAMCLK_CNTL),
|
||||
* SR(SYMCLKD_CLOCK_ENABLE),
|
||||
* SR(SYMCLKE_CLOCK_ENABLE)
|
||||
*/
|
||||
|
|
@ -360,6 +359,7 @@
|
|||
SR(PHYBSYMCLK_CLOCK_CNTL), \
|
||||
SR(PHYCSYMCLK_CLOCK_CNTL), \
|
||||
SR(DPSTREAMCLK_CNTL), \
|
||||
SR(HDMISTREAMCLK_CNTL), \
|
||||
SR(SYMCLK32_SE_CNTL), \
|
||||
SR(SYMCLK32_LE_CNTL), \
|
||||
DCCG_SRII(PIXEL_RATE_CNTL, OTG, 0), \
|
||||
|
|
@ -542,120 +542,6 @@
|
|||
SRI_ARR(DC_ABM1_ACE_OFFSET_SLOPE_DATA, ABM, id), \
|
||||
SRI_ARR(DC_ABM1_ACE_PWL_CNTL, ABM, id)
|
||||
|
||||
/* HUBP */
|
||||
/* Not in DCN42B: HUBPREQ_DEBUG_DB and HUBPREQ_DEBUG */
|
||||
#define HUBP_REG_LIST_DCN42B_RI(id) \
|
||||
SRI_ARR(DCN_DMDATA_VM_CNTL, HUBPREQ, id), \
|
||||
SRI_ARR(FLIP_PARAMETERS_3, HUBPREQ, id), \
|
||||
SRI_ARR(FLIP_PARAMETERS_4, HUBPREQ, id), \
|
||||
SRI_ARR(FLIP_PARAMETERS_5, HUBPREQ, id), \
|
||||
SRI_ARR(FLIP_PARAMETERS_6, HUBPREQ, id), \
|
||||
SRI_ARR(VBLANK_PARAMETERS_5, HUBPREQ, id), \
|
||||
SRI_ARR(VBLANK_PARAMETERS_6, HUBPREQ, id), \
|
||||
HUBP_REG_LIST_DCN_VM_RI(id), \
|
||||
SRI_ARR(PREFETCH_SETTINGS, HUBPREQ, id), \
|
||||
SRI_ARR(PREFETCH_SETTINGS_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_VM_SYSTEM_APERTURE_LOW_ADDR, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_VM_SYSTEM_APERTURE_HIGH_ADDR, HUBPREQ, id), \
|
||||
SRI_ARR(CURSOR_SETTINGS, HUBPREQ, id), \
|
||||
SRI_ARR(CURSOR_SURFACE_ADDRESS_HIGH, CURSOR0_, id), \
|
||||
SRI_ARR(CURSOR_SURFACE_ADDRESS, CURSOR0_, id), \
|
||||
SRI_ARR(CURSOR_SIZE, CURSOR0_, id), \
|
||||
SRI_ARR(CURSOR_CONTROL, CURSOR0_, id), \
|
||||
SRI_ARR(CURSOR_POSITION, CURSOR0_, id), \
|
||||
SRI_ARR(CURSOR_HOT_SPOT, CURSOR0_, id), \
|
||||
SRI_ARR(CURSOR_DST_OFFSET, CURSOR0_, id), \
|
||||
SRI_ARR(DMDATA_ADDRESS_HIGH, CURSOR0_, id), \
|
||||
SRI_ARR(DMDATA_ADDRESS_LOW, CURSOR0_, id), \
|
||||
SRI_ARR(DMDATA_CNTL, CURSOR0_, id), \
|
||||
SRI_ARR(DMDATA_SW_CNTL, CURSOR0_, id), \
|
||||
SRI_ARR(DMDATA_QOS_CNTL, CURSOR0_, id), \
|
||||
SRI_ARR(DMDATA_SW_DATA, CURSOR0_, id), \
|
||||
SRI_ARR(DMDATA_STATUS, CURSOR0_, id), \
|
||||
SRI_ARR(FLIP_PARAMETERS_0, HUBPREQ, id), \
|
||||
SRI_ARR(FLIP_PARAMETERS_1, HUBPREQ, id), \
|
||||
SRI_ARR(FLIP_PARAMETERS_2, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_CUR1_TTU_CNTL0, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_CUR1_TTU_CNTL1, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_FLIP_CONTROL2, HUBPREQ, id), \
|
||||
SRI_ARR(VMID_SETTINGS_0, HUBPREQ, id), \
|
||||
SRI_ARR(DCHUBP_CNTL, HUBP, id), \
|
||||
SRI_ARR(DCSURF_ADDR_CONFIG, HUBP, id), \
|
||||
SRI_ARR(DCSURF_TILING_CONFIG, HUBP, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_PITCH, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_PITCH_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_CONFIG, HUBP, id), \
|
||||
SRI_ARR(DCSURF_FLIP_CONTROL, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_PRI_VIEWPORT_DIMENSION, HUBP, id), \
|
||||
SRI_ARR(DCSURF_PRI_VIEWPORT_START, HUBP, id), \
|
||||
SRI_ARR(DCSURF_SEC_VIEWPORT_DIMENSION, HUBP, id), \
|
||||
SRI_ARR(DCSURF_SEC_VIEWPORT_START, HUBP, id), \
|
||||
SRI_ARR(DCSURF_PRI_VIEWPORT_DIMENSION_C, HUBP, id), \
|
||||
SRI_ARR(DCSURF_PRI_VIEWPORT_START_C, HUBP, id), \
|
||||
SRI_ARR(DCSURF_SEC_VIEWPORT_DIMENSION_C, HUBP, id), \
|
||||
SRI_ARR(DCSURF_SEC_VIEWPORT_START_C, HUBP, id), \
|
||||
SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_PRIMARY_SURFACE_ADDRESS_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SECONDARY_SURFACE_ADDRESS_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_PRIMARY_META_SURFACE_ADDRESS_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS_HIGH_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SECONDARY_META_SURFACE_ADDRESS_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_INUSE, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_INUSE_HIGH, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_INUSE_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_INUSE_HIGH_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_HIGH, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_EARLIEST_INUSE_HIGH_C, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_CONTROL, HUBPREQ, id), \
|
||||
SRI_ARR(DCSURF_SURFACE_FLIP_INTERRUPT, HUBPREQ, id), \
|
||||
SRI_ARR(HUBPRET_CONTROL, HUBPRET, id), \
|
||||
SRI_ARR(HUBPRET_READ_LINE_STATUS, HUBPRET, id), \
|
||||
SRI_ARR(DCN_EXPANSION_MODE, HUBPREQ, id), \
|
||||
SRI_ARR(DCHUBP_REQ_SIZE_CONFIG, HUBP, id), \
|
||||
SRI_ARR(DCHUBP_REQ_SIZE_CONFIG_C, HUBP, id), \
|
||||
SRI_ARR(BLANK_OFFSET_0, HUBPREQ, id), \
|
||||
SRI_ARR(BLANK_OFFSET_1, HUBPREQ, id), \
|
||||
SRI_ARR(DST_DIMENSIONS, HUBPREQ, id), \
|
||||
SRI_ARR(DST_AFTER_SCALER, HUBPREQ, id), \
|
||||
SRI_ARR(VBLANK_PARAMETERS_0, HUBPREQ, id), \
|
||||
SRI_ARR(REF_FREQ_TO_PIX_FREQ, HUBPREQ, id), \
|
||||
SRI_ARR(VBLANK_PARAMETERS_1, HUBPREQ, id), \
|
||||
SRI_ARR(VBLANK_PARAMETERS_3, HUBPREQ, id), \
|
||||
SRI_ARR(NOM_PARAMETERS_4, HUBPREQ, id), \
|
||||
SRI_ARR(NOM_PARAMETERS_5, HUBPREQ, id), \
|
||||
SRI_ARR(PER_LINE_DELIVERY_PRE, HUBPREQ, id), \
|
||||
SRI_ARR(PER_LINE_DELIVERY, HUBPREQ, id), \
|
||||
SRI_ARR(VBLANK_PARAMETERS_2, HUBPREQ, id), \
|
||||
SRI_ARR(VBLANK_PARAMETERS_4, HUBPREQ, id), \
|
||||
SRI_ARR(NOM_PARAMETERS_6, HUBPREQ, id), \
|
||||
SRI_ARR(NOM_PARAMETERS_7, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_TTU_QOS_WM, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_GLOBAL_TTU_CNTL, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_SURF0_TTU_CNTL0, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_SURF0_TTU_CNTL1, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_SURF1_TTU_CNTL0, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_SURF1_TTU_CNTL1, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_CUR0_TTU_CNTL0, HUBPREQ, id), \
|
||||
SRI_ARR(DCN_CUR0_TTU_CNTL1, HUBPREQ, id), \
|
||||
SRI_ARR(HUBP_CLK_CNTL, HUBP, id), \
|
||||
SRI_ARR(HUBPRET_READ_LINE_VALUE, HUBPRET, id), \
|
||||
SRI_ARR(DCHUBP_MALL_CONFIG, HUBP, id), \
|
||||
SRI_ARR(DCHUBP_VMPG_CONFIG, HUBP, id), \
|
||||
SRI_ARR(UCLK_PSTATE_FORCE, HUBPREQ, id), \
|
||||
SRI_ARR(HUBP_3DLUT_DLG_PARAM, CURSOR0_, id), \
|
||||
HUBP_3DLUT_FL_REG_LIST_DCN401(id)
|
||||
struct dcn42b_resource_pool {
|
||||
struct resource_pool base;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3892,13 +3892,16 @@ static void si_notify_hw_of_powersource(void *handle)
|
|||
{
|
||||
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
|
||||
|
||||
/* 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)
|
||||
/*
|
||||
* Check if the platform already manages the AC/DC switch via dedicated GPIO.
|
||||
* Otherwise SMU automatically notices DC, but needs to be notified of AC.
|
||||
*/
|
||||
if (adev->pm.ac_power &&
|
||||
(adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC))
|
||||
amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC);
|
||||
|
||||
/* Recompute clocks with updated max_limits. */
|
||||
amdgpu_legacy_dpm_compute_clocks(adev);
|
||||
}
|
||||
|
||||
static PPSMC_Result si_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
|
||||
|
|
@ -7689,7 +7692,7 @@ static int si_dpm_process_interrupt(struct amdgpu_device *adev,
|
|||
break;
|
||||
}
|
||||
|
||||
if (queue_thermal)
|
||||
if (queue_thermal && amdgpu_dpm)
|
||||
schedule_work(&adev->pm.dpm.thermal.work);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -106,11 +106,8 @@ int hwmgr_early_init(struct pp_hwmgr *hwmgr)
|
|||
hwmgr->od_enabled = false;
|
||||
switch (hwmgr->chip_id) {
|
||||
case CHIP_BONAIRE:
|
||||
/* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM
|
||||
* R7 260X cards with old MC ucode: MCLK DPM is unstable
|
||||
*/
|
||||
if (adev->pdev->subsystem_vendor == 0x106B ||
|
||||
adev->pdev->device == 0x6658) {
|
||||
/* R9 M380 in iMac 2015: SMU hangs when enabling MCLK DPM */
|
||||
if (adev->pdev->subsystem_vendor == 0x106B) {
|
||||
dev_info(adev->dev, "disabling MCLK DPM on quirky ASIC");
|
||||
adev->pm.pp_feature &= ~PP_MCLK_DPM_MASK;
|
||||
hwmgr->feature_mask &= ~PP_MCLK_DPM_MASK;
|
||||
|
|
|
|||
|
|
@ -5857,15 +5857,19 @@ static int smu7_power_off_asic(struct pp_hwmgr *hwmgr)
|
|||
static void smu7_notify_ac_dc(struct pp_hwmgr *hwmgr)
|
||||
{
|
||||
struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev);
|
||||
const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
|
||||
|
||||
/* Check if the platform already manages the AC/DC switch via dedicated GPIO. */
|
||||
if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps,
|
||||
/*
|
||||
* Check if the platform already manages the AC/DC switch via dedicated GPIO.
|
||||
* Otherwise SMU automatically notices DC, but needs to be notified of AC.
|
||||
*/
|
||||
if (adev->pm.ac_power &&
|
||||
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);
|
||||
|
||||
/* Recompute clocks with updated max_limits. */
|
||||
pp_funcs->pm_compute_clocks(adev->powerplay.pp_handle);
|
||||
}
|
||||
|
||||
static const struct pp_hwmgr_func smu7_hwmgr_funcs = {
|
||||
|
|
|
|||
|
|
@ -1367,6 +1367,14 @@ static void smu_feature_cap_init(struct smu_context *smu)
|
|||
bitmap_zero(fea_cap->cap_map, SMU_FEATURE_CAP_ID__COUNT);
|
||||
}
|
||||
|
||||
static int smu_set_power_dep(struct smu_context *smu, bool enable)
|
||||
{
|
||||
if (!smu->ppt_funcs->set_power_dep)
|
||||
return 0;
|
||||
|
||||
return smu->ppt_funcs->set_power_dep(smu, enable);
|
||||
}
|
||||
|
||||
static int smu_sw_init(struct amdgpu_ip_block *ip_block)
|
||||
{
|
||||
struct amdgpu_device *adev = ip_block->adev;
|
||||
|
|
@ -1428,6 +1436,8 @@ static int smu_sw_init(struct amdgpu_ip_block *ip_block)
|
|||
if (!smu->ppt_funcs->get_fan_control_mode)
|
||||
smu->adev->pm.no_fan = true;
|
||||
|
||||
smu_set_power_dep(smu, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1450,6 +1460,8 @@ static int smu_sw_fini(struct amdgpu_ip_block *ip_block)
|
|||
|
||||
smu_fini_microcode(smu);
|
||||
|
||||
smu_set_power_dep(smu, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -749,6 +749,9 @@ struct smu_context {
|
|||
bool pm_enabled;
|
||||
bool is_apu;
|
||||
|
||||
/* Power dependency link from an integrated xHCI controller to the GPU */
|
||||
struct device_link *usb_power_link;
|
||||
|
||||
uint32_t smc_driver_if_version;
|
||||
uint32_t smc_fw_if_version;
|
||||
uint32_t smc_fw_version;
|
||||
|
|
@ -1648,12 +1651,19 @@ struct pptable_funcs {
|
|||
int (*ras_send_msg)(struct smu_context *smu,
|
||||
enum smu_message_type msg, uint32_t param, uint32_t *read_arg);
|
||||
|
||||
|
||||
/**
|
||||
* @get_ras_smu_drv: Get RAS smu driver interface
|
||||
* Return: ras_smu_drv *
|
||||
*/
|
||||
int (*get_ras_smu_drv)(struct smu_context *smu, const struct ras_smu_drv **ras_smu_drv);
|
||||
|
||||
/**
|
||||
* @set_power_dep: Create or destroy a power dependency link
|
||||
* from an integrated xHCI controller to the GPU so that the GPU is
|
||||
* resumed before the USB controller during PM resume. @enable is true
|
||||
* to create the link and false to tear it down.
|
||||
*/
|
||||
int (*set_power_dep)(struct smu_context *smu, bool enable);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
|
|
|||
|
|
@ -1701,6 +1701,50 @@ static int smu_v14_0_0_restore_user_od_settings(struct smu_context *smu)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Link any xHCI controller sharing the GPU's PCIe root port as a consumer
|
||||
* of the GPU so the GPU resumes first, avoiding an xHCI resume race.
|
||||
*/
|
||||
static int smu_v14_0_0_set_power_dep(struct smu_context *smu, bool enable)
|
||||
{
|
||||
struct amdgpu_device *adev = smu->adev;
|
||||
struct pci_dev *gpu_pdev = adev->pdev;
|
||||
struct pci_dev *root_port, *usb_pdev = NULL;
|
||||
struct device_link *link;
|
||||
|
||||
if (!enable) {
|
||||
if (smu->usb_power_link) {
|
||||
device_link_del(smu->usb_power_link);
|
||||
smu->usb_power_link = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
root_port = pcie_find_root_port(gpu_pdev);
|
||||
while ((usb_pdev = pci_get_class(PCI_CLASS_SERIAL_USB_XHCI, usb_pdev))) {
|
||||
struct pci_dev *usb_root;
|
||||
|
||||
usb_root = pcie_find_root_port(usb_pdev);
|
||||
if (usb_root != root_port)
|
||||
continue;
|
||||
|
||||
/* Create device link: USB (consumer) depends on GPU (supplier) */
|
||||
link = device_link_add(&usb_pdev->dev, &gpu_pdev->dev,
|
||||
DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME);
|
||||
if (link) {
|
||||
smu->usb_power_link = link;
|
||||
drm_info(adev_to_drm(adev), "USB controller %s D0 power state depends on %s\n",
|
||||
pci_name(usb_pdev), pci_name(gpu_pdev));
|
||||
/* Only create one link for the first USB controller found */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pci_dev_put(usb_pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
|
||||
.check_fw_status = smu_v14_0_check_fw_status,
|
||||
.check_fw_version = smu_cmn_check_fw_version,
|
||||
|
|
@ -1734,6 +1778,7 @@ static const struct pptable_funcs smu_v14_0_0_ppt_funcs = {
|
|||
.dpm_set_umsch_mm_enable = smu_v14_0_0_set_umsch_mm_enable,
|
||||
.get_dpm_clock_table = smu_v14_0_common_get_dpm_table,
|
||||
.set_mall_enable = smu_v14_0_common_set_mall_enable,
|
||||
.set_power_dep = smu_v14_0_0_set_power_dep,
|
||||
};
|
||||
|
||||
static void smu_v14_0_0_init_msg_ctl(struct smu_context *smu)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,15 @@ struct drm_get_panel_backlight_quirk {
|
|||
};
|
||||
|
||||
static const struct drm_get_panel_backlight_quirk drm_panel_min_backlight_quirks[] = {
|
||||
/* Lenovo Legion 5 15ARH05, AUX backlight non-functional, force PWM */
|
||||
{
|
||||
.dmi_match.field = DMI_SYS_VENDOR,
|
||||
.dmi_match.value = "LENOVO",
|
||||
.dmi_match_other.field = DMI_PRODUCT_VERSION,
|
||||
.dmi_match_other.value = "Lenovo Legion 5 15ARH05",
|
||||
.ident.panel_id = drm_edid_encode_panel_id('B', 'O', 'E', 0x08df),
|
||||
.quirk = { .force_pwm = true, },
|
||||
},
|
||||
/* 13 inch matte panel */
|
||||
{
|
||||
.dmi_match.field = DMI_BOARD_VENDOR,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ int drm_get_panel_orientation_quirk(int width, int height);
|
|||
struct drm_panel_backlight_quirk {
|
||||
u16 min_brightness;
|
||||
u32 brightness_mask;
|
||||
bool force_pwm;
|
||||
};
|
||||
|
||||
const struct drm_panel_backlight_quirk *
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user