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:
Dave Airlie 2026-07-18 08:20:12 +10:00
commit 973fd9493e
31 changed files with 720 additions and 431 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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