Merge tag 'drm-fixes-2026-07-18-1' of https://gitlab.freedesktop.org/drm/kernel

Pull drm fixes from Daie Airlie:
 "Weekly drm fixes, there is amdgpu, xe and i915 and then a lot of
  scattered fixes.

  Looks about the right level for the new right.

  ttm:
   - Handle NULL pages and backup handles in ttm_pool_backup() correctly

  gpusvm:
   - Improve unmap and error handling on gpusvm

  udmabuf:
   - Always synchronize for CPU in begin_cpu_udmabuf

  xe:
   - Fix BO prefetch with CONSULT_MEM_ADVISE_PREF_LOCK
   - Hold a dma-buf reference for imported BOs
   - Fix writable override for CRI
   - Fix VF CCS attach/detach race with in-flight BO moves
   - Fix WOPCM size for LNL+
   - Reset current_op in xe_pt_update_ops_init
   - Keep scheduler timeline name alive
   - Hold device ref until queue teardown completes
   - Disable display in admin only PF mode

  i915:
   - NV12 display fix for bigjoiner
   - clear watermark on plane disable
   - GT selftest fixes

  host1x:
   - Fix UAF

  amdxdna
   - Fix UAF
   - Reject more invalid amdxdna command submissions

  ivpu:
   - Fix wrong read
   - Handle invalid firmware log in ivpu

  panthor:
   - Fix error handling

  virtio:
   - Fix virtio deadlock
   - Fix invalid gem detach

  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

* tag 'drm-fixes-2026-07-18-1' of https://gitlab.freedesktop.org/drm/kernel: (61 commits)
  Revert "drm/amd/display: Restore 5s vbl offdelay for NV3x+ DGPUs"
  drm/amd/display: check GRPH_FLIP status before sending event
  drm/amd/display: consolidate DCN vblank/flip handling onto vupdate_no_lock
  drm/amd: Create a device link between APU display and XHCI devices
  drm/amd/display: wire DCN42B mcache programming callback
  drm/amd/display: set new_stream to NULL after release
  drm/amd/display: Force PWM backlight on Lenovo Legion 5 15ARH05
  drm/amdkfd: free MQD managers on DQM init failures
  drm/amdgpu/ttm: Consider concurrent VM flushes for buffer entities
  drm/amd/pm/smu7: Fix AC/DC switch notification
  drm/amdgpu: Disable PCIe dynamic speed switching on Ryzen Pinnacle Ridge
  drm/amdgpu: always emit the job vm fence
  drm/amd/pm/si: Fix AC/DC switch notification
  drm/amd/pm/si: Don't schedule thermal work when queue isn't initialized
  drm/amd/display: dce100: skip non-DP stream encoders for DP MST
  drm/amd/display: Set native cursor mode for disabled CRTCs
  drm/amd/pm/ci: Don't disable MCLK DPM on Bonaire 0x6658 (R7 260X)
  drm/amd/display: fix __udivdi3 link error
  drm/amdgpu: Reserve space for IB contents in devcoredumps
  drm/amdgpu: Print vmid, pasid and more task info in devcoredump
  ...
This commit is contained in:
Linus Torvalds
2026-07-17 16:56:55 -07:00
67 changed files with 1076 additions and 564 deletions

View File

@@ -577,6 +577,7 @@ void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
amdxdna_arg_bos_put(job);
amdxdna_gem_put_obj(job->cmd_bo);
dma_fence_put(job->fence);
mmdrop(job->mm);
}
int amdxdna_cmd_submit(struct amdxdna_client *client,
@@ -590,6 +591,10 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
int ret, idx;
XDNA_DBG(xdna, "Command BO hdl %d, Arg BO count %d", cmd_bo_hdl, arg_bo_cnt);
if (!xdna->dev_info->ops->cmd_submit)
return -EOPNOTSUPP;
job = kzalloc_flex(*job, bos, arg_bo_cnt);
if (!job)
return -ENOMEM;
@@ -603,6 +608,16 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
ret = -EINVAL;
goto free_job;
}
} else if (!drv_cmd) {
/*
* Only internal driver commands (drv_cmd != NULL) may omit a
* command BO. A user command submission with the invalid handle
* would leave job->cmd_bo NULL and later fault when the scheduler
* dereferences it in amdxdna_cmd_set_state().
*/
XDNA_DBG(xdna, "Command BO handle required for user submission");
ret = -EINVAL;
goto free_job;
}
ret = amdxdna_arg_bos_lookup(client, job, arg_bo_hdls, arg_bo_cnt);
@@ -628,6 +643,7 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
job->hwctx = hwctx;
job->mm = current->mm;
mmgrab(job->mm);
job->fence = amdxdna_fence_create(hwctx);
if (!job->fence) {
@@ -662,6 +678,8 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
cmd_put:
amdxdna_gem_put_obj(job->cmd_bo);
free_job:
if (job->mm)
mmdrop(job->mm);
kfree(job);
return ret;
}

View File

@@ -43,6 +43,10 @@ static int fw_log_from_bo(struct ivpu_device *vdev, struct ivpu_bo *bo, u32 *off
ivpu_dbg(vdev, FW_BOOT, "Invalid header size 0x%x\n", log->header_size);
return -EINVAL;
}
if (log->size < log->header_size) {
ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
return -EINVAL;
}
if ((char *)log + log->size > (char *)ivpu_bo_vaddr(bo) + ivpu_bo_size(bo)) {
ivpu_dbg(vdev, FW_BOOT, "Invalid log size 0x%x\n", log->size);
return -EINVAL;

View File

@@ -927,7 +927,7 @@ static void diagnose_failure_mtl(struct ivpu_device *vdev)
static void diagnose_failure_lnl(struct ivpu_device *vdev)
{
u32 reg = REGB_RD32(VPU_HW_BTRS_MTL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
u32 reg = REGB_RD32(VPU_HW_BTRS_LNL_INTERRUPT_STAT) & BTRS_LNL_IRQ_MASK;
if (REG_TEST_FLD(VPU_HW_BTRS_LNL_INTERRUPT_STAT, ATS_ERR, reg)) {
ivpu_err(vdev, "ATS_ERR_LOG1 0x%08x ATS_ERR_LOG2 0x%08x\n",

View File

@@ -224,21 +224,22 @@ static int begin_cpu_udmabuf(struct dma_buf *buf,
{
struct udmabuf *ubuf = buf->priv;
struct device *dev = ubuf->device->this_device;
int ret = 0;
if (!ubuf->sg) {
ubuf->sg = get_sg_table(dev, buf, direction);
if (IS_ERR(ubuf->sg)) {
int ret;
ret = PTR_ERR(ubuf->sg);
ubuf->sg = NULL;
return ret;
} else {
ubuf->sg_dir = direction;
}
} else {
dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
}
return ret;
dma_sync_sgtable_for_cpu(dev, ubuf->sg, direction);
return 0;
}
static int end_cpu_udmabuf(struct dma_buf *buf,

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

@@ -1146,20 +1146,35 @@ static void __drm_gpusvm_unmap_pages(struct drm_gpusvm *gpusvm,
};
bool use_iova = dma_use_iova(&svm_pages->state);
if (use_iova)
dma_iova_destroy(dev, &svm_pages->state,
svm_pages->state_offset,
svm_pages->dma_addr[0].dir, 0);
/*
* IOVA is reserved for the whole range but only the linked
* system pages (state_offset bytes) need unlinking; free the
* entire reservation to avoid leaking the device-page part.
* On the error path state_offset is 0, so just free it.
*/
if (use_iova) {
if (svm_pages->state_offset)
dma_iova_unlink(dev, &svm_pages->state, 0,
svm_pages->state_offset,
svm_pages->dma_addr[0].dir, 0);
dma_iova_free(dev, &svm_pages->state);
}
for (i = 0, j = 0; i < npages; j++) {
struct drm_pagemap_addr *addr = &svm_pages->dma_addr[j];
if (!use_iova && addr->proto == DRM_INTERCONNECT_SYSTEM)
dma_unmap_page(dev,
addr->addr,
PAGE_SIZE << addr->order,
addr->dir);
else if (dpagemap && dpagemap->ops->device_unmap)
if (addr->proto == DRM_INTERCONNECT_SYSTEM) {
/*
* Linked IOVA pages were already torn down by
* the dma_iova_unlink()/dma_iova_free() above;
* only the non-IOVA mappings need unmap here.
*/
if (!use_iova)
dma_unmap_page(dev,
addr->addr,
PAGE_SIZE << addr->order,
addr->dir);
} else if (dpagemap && dpagemap->ops->device_unmap)
dpagemap->ops->device_unmap(dpagemap,
dev, addr);
i += 1 << addr->order;
@@ -1486,7 +1501,7 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
/* Unlock and restart mapping to allocate memory. */
drm_gpusvm_notifier_unlock(gpusvm);
svm_pages->dma_addr =
kvmalloc_objs(*svm_pages->dma_addr, npages);
kvzalloc_objs(*svm_pages->dma_addr, npages);
if (!svm_pages->dma_addr) {
err = -ENOMEM;
goto err_free;
@@ -1529,6 +1544,16 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
err = -EAGAIN;
goto err_unmap;
}
/*
* Set the dpagemap as soon as the first
* device page is mapped so the err_unmap path
* can device_unmap() the device mappings that
* have already been created.
*/
drm_pagemap_get(dpagemap);
drm_pagemap_put(svm_pages->dpagemap);
svm_pages->dpagemap = dpagemap;
}
svm_pages->dma_addr[j] =
dpagemap->ops->device_map(dpagemap,
@@ -1596,12 +1621,8 @@ int drm_gpusvm_get_pages(struct drm_gpusvm *gpusvm,
goto err_unmap;
}
if (pagemap) {
if (pagemap)
flags.has_devmem_pages = true;
drm_pagemap_get(dpagemap);
drm_pagemap_put(svm_pages->dpagemap);
svm_pages->dpagemap = dpagemap;
}
/* WRITE_ONCE pairs with READ_ONCE for opportunistic checks */
WRITE_ONCE(svm_pages->flags.__flags, flags.__flags);

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

@@ -2126,19 +2126,6 @@ static int skl_check_main_surface(struct intel_plane_state *plane_state)
return 0;
}
/* Divide a U16.16 fixed-point value by 2, staying in fixed-point domain */
static inline u32 fp_16_16_div2(u32 fp)
{
return fp >> 1;
}
/* Convert a U16.16 fixed-point value to integer, rounding up */
static inline int fp_16_16_to_int_ceil(u32 fp)
{
return DIV_ROUND_UP(fp, 1 << 16);
}
static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
{
struct intel_display *display = to_intel_display(plane_state);
@@ -2154,14 +2141,20 @@ static int skl_check_nv12_aux_surface(struct intel_plane_state *plane_state)
int max_height = intel_plane_max_height(plane, fb, uv_plane, rotation);
/*
* LNL+ UV surface start/size =
* ceiling(half of Y plane start/size). Use ceiling division
* unconditionally; it is a no-op for even values.
* UV (chroma) start/size = ceiling(half of the *integer* Y plane
* start/size), i.e. the value the luma surface programs (src >> 16),
* not the raw U16.16. A bigjoiner seam mapped through the scaler can
* give a fractional luma src; ceiling that directly would round the
* chroma one column too far and read past the chroma surface.
*/
int x = fp_16_16_to_int_ceil(fp_16_16_div2(plane_state->uapi.src.x1));
int y = fp_16_16_to_int_ceil(fp_16_16_div2(plane_state->uapi.src.y1));
int w = fp_16_16_to_int_ceil(fp_16_16_div2(drm_rect_width(&plane_state->uapi.src)));
int h = fp_16_16_to_int_ceil(fp_16_16_div2(drm_rect_height(&plane_state->uapi.src)));
int luma_x = plane_state->uapi.src.x1 >> 16;
int luma_y = plane_state->uapi.src.y1 >> 16;
int luma_w = drm_rect_width(&plane_state->uapi.src) >> 16;
int luma_h = drm_rect_height(&plane_state->uapi.src) >> 16;
int x = DIV_ROUND_UP(luma_x, 2);
int y = DIV_ROUND_UP(luma_y, 2);
int w = DIV_ROUND_UP(luma_x + luma_w, 2) - x;
int h = DIV_ROUND_UP(luma_y + luma_h, 2) - y;
u32 offset;
/* FIXME not quite sure how/if these apply to the chroma plane */

View File

@@ -3856,7 +3856,7 @@ void skl_wm_plane_disable_noatomic(struct intel_crtc *crtc,
return;
skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0);
skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb[plane->id], 0, 0);
skl_ddb_entry_init(&crtc_state->wm.skl.plane_ddb_y[plane->id], 0, 0);
crtc_state->wm.skl.plane_min_ddb[plane->id] = 0;
crtc_state->wm.skl.plane_interim_ddb[plane->id] = 0;

View File

@@ -259,7 +259,7 @@ void intel_engines_driver_register(struct drm_i915_private *i915)
p = &prev->rb_right;
}
if (IS_ENABLED(CONFIG_DRM_I915_SELFTESTS) &&
if (IS_ENABLED(CONFIG_DRM_I915_SELFTEST) &&
IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM)) {
struct intel_engine_cs *engine;
unsigned int isolation;

View File

@@ -16,9 +16,9 @@ static int cmp_u64(const void *A, const void *B)
{
const u64 *a = A, *b = B;
if (a < b)
if (*a < *b)
return -1;
else if (a > b)
else if (*a > *b)
return 1;
else
return 0;
@@ -28,9 +28,9 @@ static int cmp_u32(const void *A, const void *B)
{
const u32 *a = A, *b = B;
if (a < b)
if (*a < *b)
return -1;
else if (a > b)
else if (*a > *b)
return 1;
else
return 0;

View File

@@ -182,7 +182,10 @@ int panthor_device_init(struct panthor_device *ptdev)
return ret;
#ifdef CONFIG_DEBUG_FS
drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
ret = drmm_mutex_init(&ptdev->base, &ptdev->gems.lock);
if (ret)
return ret;
INIT_LIST_HEAD(&ptdev->gems.node);
#endif

View File

@@ -829,6 +829,7 @@ static int panthor_fw_load(struct panthor_device *ptdev)
}
if (hdr.size > iter.size) {
ret = -EINVAL;
drm_err(&ptdev->base, "Firmware image is truncated\n");
goto out;
}

View File

@@ -1051,9 +1051,31 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
return -EBUSY;
#ifdef CONFIG_X86
/* Anything returned to the system needs to be cached. */
if (tt->caching != ttm_cached)
set_pages_array_wb(tt->pages, tt->num_pages);
/* Anything returned to the system needs to be cached. Walk allocations
* skipping NULL pages and issue set_pages_array_wb() per contiguous run.
*/
if (tt->caching != ttm_cached) {
pgoff_t run_start = 0, run_count = 0;
for (i = 0; i < tt->num_pages; i += num_pages) {
page = tt->pages[i];
if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
if (run_count) {
set_pages_array_wb(&tt->pages[run_start],
run_count);
run_count = 0;
}
num_pages = 1;
continue;
}
num_pages = 1UL << ttm_pool_page_order(pool, page);
if (!run_count)
run_start = i;
run_count += num_pages;
}
if (run_count)
set_pages_array_wb(&tt->pages[run_start], run_count);
}
#endif
if (tt->dma_address || flags->purge) {
@@ -1061,7 +1083,7 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
unsigned int order;
page = tt->pages[i];
if (unlikely(!page)) {
if (unlikely(!page || ttm_backup_page_ptr_is_handle(page))) {
num_pages = 1;
continue;
}
@@ -1104,6 +1126,10 @@ long ttm_pool_backup(struct ttm_pool *pool, struct ttm_tt *tt,
if (unlikely(!page))
continue;
/* Already-handled entry from a previous attempt. */
if (unlikely(ttm_backup_page_ptr_is_handle(page)))
continue;
ttm_pool_split_for_swap(pool, page);
shandle = ttm_backup_backup_page(backup, page, flags->writeback, i,

View File

@@ -139,13 +139,15 @@ void virtio_gpu_gem_object_close(struct drm_gem_object *obj,
if (!vgdev->has_virgl_3d)
return;
objs = virtio_gpu_array_alloc(1);
if (!objs)
return;
virtio_gpu_array_add_obj(objs, obj);
if (vfpriv->context_created) {
objs = virtio_gpu_array_alloc(1);
if (!objs)
return;
virtio_gpu_array_add_obj(objs, obj);
virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
objs);
virtio_gpu_cmd_context_detach_resource(vgdev, vfpriv->ctx_id,
objs);
}
virtio_gpu_notify(vgdev);
}

View File

@@ -49,7 +49,10 @@ static void virtio_gpu_config_changed_work_func(struct work_struct *work)
virtio_gpu_cmd_get_edids(vgdev);
virtio_gpu_cmd_get_display_info(vgdev);
virtio_gpu_notify(vgdev);
drm_helper_hpd_irq_event(vgdev->ddev);
wait_event_timeout(vgdev->resp_wq,
!vgdev->display_info_pending,
5 * HZ);
drm_kms_helper_hotplug_event(vgdev->ddev);
}
events_clear |= VIRTIO_GPU_EVENT_DISPLAY;
}

View File

@@ -840,9 +840,6 @@ static void virtio_gpu_cmd_get_display_info_cb(struct virtio_gpu_device *vgdev,
vgdev->display_info_pending = false;
spin_unlock(&vgdev->display_info_lock);
wake_up(&vgdev->resp_wq);
if (!drm_helper_hpd_irq_event(vgdev->ddev))
drm_kms_helper_hotplug_event(vgdev->ddev);
}
static void virtio_gpu_cmd_get_capset_info_cb(struct virtio_gpu_device *vgdev,

View File

@@ -1102,6 +1102,21 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
xe_pm_runtime_get_noresume(xe);
}
/*
* Attach CCS BBs before submitting the copy job below so a VF
* migration racing the copy sees valid, up to date attach state.
*/
if (IS_VF_CCS_READY(xe) &&
((move_lacks_source && new_mem->mem_type == XE_PL_TT) ||
(old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) &&
handle_system_ccs) {
ret = xe_sriov_vf_ccs_attach_bo(bo, new_mem);
if (ret) {
xe_pm_runtime_put(xe);
goto out;
}
}
if (move_lacks_source) {
u32 flags = 0;
@@ -1139,22 +1154,19 @@ static int xe_bo_move(struct ttm_buffer_object *ttm_bo, bool evict,
ttm_bo_move_null(ttm_bo, new_mem);
}
dma_fence_put(fence);
xe_pm_runtime_put(xe);
/*
* CCS meta data is migrated from TT -> SMEM. So, let us detach the
* BBs from BO as it is no longer needed.
* Detach must wait for the copy above to complete: a VF migration
* racing an in-flight copy must still see valid CCS BBs, so don't
* tear them down until the copy fence has signaled.
*/
if (IS_VF_CCS_READY(xe) && old_mem_type == XE_PL_TT &&
new_mem->mem_type == XE_PL_SYSTEM)
new_mem->mem_type == XE_PL_SYSTEM) {
dma_fence_wait(fence, false);
xe_sriov_vf_ccs_detach_bo(bo);
}
if (IS_VF_CCS_READY(xe) &&
((move_lacks_source && new_mem->mem_type == XE_PL_TT) ||
(old_mem_type == XE_PL_SYSTEM && new_mem->mem_type == XE_PL_TT)) &&
handle_system_ccs)
ret = xe_sriov_vf_ccs_attach_bo(bo);
dma_fence_put(fence);
xe_pm_runtime_put(xe);
out:
if ((!ttm_bo->resource || ttm_bo->resource->mem_type == XE_PL_SYSTEM) &&
@@ -1349,7 +1361,7 @@ int xe_bo_notifier_prepare_pinned(struct xe_bo *bo)
backup = xe_bo_init_locked(xe, NULL, NULL, bo->ttm.base.resv, NULL, xe_bo_size(bo),
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
XE_BO_FLAG_PINNED, &exec);
XE_BO_FLAG_PINNED, NULL, &exec);
if (IS_ERR(backup)) {
drm_exec_retry_on_contention(&exec);
ret = PTR_ERR(backup);
@@ -1490,7 +1502,7 @@ int xe_bo_evict_pinned(struct xe_bo *bo)
xe_bo_size(bo),
DRM_XE_GEM_CPU_CACHING_WB, ttm_bo_type_kernel,
XE_BO_FLAG_SYSTEM | XE_BO_FLAG_NEEDS_CPU_ACCESS |
XE_BO_FLAG_PINNED, &exec);
XE_BO_FLAG_PINNED, NULL, &exec);
if (IS_ERR(backup)) {
drm_exec_retry_on_contention(&exec);
ret = PTR_ERR(backup);
@@ -1826,6 +1838,8 @@ static void xe_ttm_bo_destroy(struct ttm_buffer_object *ttm_bo)
if (bo->ttm.base.import_attach)
drm_prime_gem_destroy(&bo->ttm.base, NULL);
if (bo->dma_buf)
dma_buf_put(bo->dma_buf);
drm_gem_object_release(&bo->ttm.base);
xe_assert(xe, list_empty(&ttm_bo->base.gpuva.list));
@@ -2283,6 +2297,8 @@ void xe_bo_free(struct xe_bo *bo)
* @cpu_caching: The cpu caching used for system memory backing store.
* @type: The TTM buffer object type.
* @flags: XE_BO_FLAG_ flags.
* @dma_buf: The dma-buf to reference for the BO lifetime (imported BOs),
* or NULL.
* @exec: The drm_exec transaction to use for exhaustive eviction.
*
* Initialize or create an xe buffer object. On failure, any allocated buffer
@@ -2294,7 +2310,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
struct xe_tile *tile, struct dma_resv *resv,
struct ttm_lru_bulk_move *bulk, size_t size,
u16 cpu_caching, enum ttm_bo_type type,
u32 flags, struct drm_exec *exec)
u32 flags, struct dma_buf *dma_buf,
struct drm_exec *exec)
{
struct ttm_operation_ctx ctx = {
.interruptible = true,
@@ -2383,6 +2400,17 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
placement = (type == ttm_bo_type_sg ||
bo->flags & XE_BO_FLAG_DEFER_BACKING) ? &sys_placement :
&bo->placement;
/*
* For imported BOs, keep the exporter dma-buf alive for the BO
* lifetime. Taken before ttm_bo_init_reserved() to also cover a
* creation failure there. Released in xe_ttm_bo_destroy().
*/
if (dma_buf) {
get_dma_buf(dma_buf);
bo->dma_buf = dma_buf;
}
err = ttm_bo_init_reserved(&xe->ttm, &bo->ttm, type,
placement, alignment,
&ctx, NULL, resv, xe_ttm_bo_destroy);
@@ -2500,7 +2528,7 @@ __xe_bo_create_locked(struct xe_device *xe,
vm && !xe_vm_in_fault_mode(vm) &&
flags & XE_BO_FLAG_USER ?
&vm->lru_bulk_move : NULL, size,
cpu_caching, type, flags, exec);
cpu_caching, type, flags, NULL, exec);
if (IS_ERR(bo))
return bo;

View File

@@ -118,7 +118,8 @@ struct xe_bo *xe_bo_init_locked(struct xe_device *xe, struct xe_bo *bo,
struct xe_tile *tile, struct dma_resv *resv,
struct ttm_lru_bulk_move *bulk, size_t size,
u16 cpu_caching, enum ttm_bo_type type,
u32 flags, struct drm_exec *exec);
u32 flags, struct dma_buf *dma_buf,
struct drm_exec *exec);
struct xe_bo *xe_bo_create_locked(struct xe_device *xe, struct xe_tile *tile,
struct xe_vm *vm, size_t size,
enum ttm_bo_type type, u32 flags,

View File

@@ -36,6 +36,8 @@ struct xe_bo {
struct xe_bo *backup_obj;
/** @parent_obj: Ref to parent bo if this a backup_obj */
struct xe_bo *parent_obj;
/** @dma_buf: Imported dma-buf ref to keep its resv alive. */
struct dma_buf *dma_buf;
/** @flags: flags for this buffer object */
u32 flags;
/** @vm: VM this BO is attached to, for extobj this will be NULL */

View File

@@ -426,7 +426,6 @@ static const struct drm_ioctl_desc xe_ioctls_admin_only[] = {
static const struct drm_driver admin_only_driver = {
.driver_features =
XE_DISPLAY_DRIVER_FEATURES |
DRIVER_GEM | DRIVER_RENDER | DRIVER_GEM_GPUVA,
.open = xe_file_open,
.postclose = xe_file_close,
@@ -438,7 +437,6 @@ static const struct drm_driver admin_only_driver = {
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
XE_DISPLAY_DRIVER_OPS,
};
/**
@@ -580,7 +578,7 @@ int xe_device_init_early(struct xe_device *xe)
WQ_MEM_RECLAIM);
xe->ordered_wq = alloc_ordered_workqueue("xe-ordered-wq", 0);
xe->unordered_wq = alloc_workqueue("xe-unordered-wq", WQ_PERCPU, 0);
xe->destroy_wq = alloc_workqueue("xe-destroy-wq", WQ_PERCPU, 0);
xe->destroy_wq = alloc_workqueue("xe-destroy-wq", WQ_PERCPU | WQ_MEM_RECLAIM, 0);
if (!xe->ordered_wq || !xe->unordered_wq ||
!xe->preempt_fence_wq || !xe->destroy_wq) {
/*

View File

@@ -355,7 +355,7 @@ struct xe_device {
/** @unordered_wq: used to serialize unordered work */
struct workqueue_struct *unordered_wq;
/** @destroy_wq: used to serialize user destroy work, like queue */
/** @destroy_wq: used to serialize SVM pagemap destroy work */
struct workqueue_struct *destroy_wq;
/** @tiles: device tiles */

View File

@@ -302,7 +302,7 @@ xe_dma_buf_create_obj(struct drm_device *dev, struct dma_buf *dma_buf)
bo = xe_bo_init_locked(xe, NULL, NULL, resv, NULL, dma_buf->size,
0, /* Will require 1way or 2way for vm_bind */
ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec);
ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, dma_buf, &exec);
drm_exec_retry_on_contention(&exec);
if (IS_ERR(bo)) {
ret = PTR_ERR(bo);

View File

@@ -10,6 +10,7 @@
#include <linux/workqueue.h>
#include "xe_gpu_scheduler_types.h"
#include "xe_hw_fence_types.h"
struct dma_fence;
struct xe_exec_queue;
@@ -24,6 +25,10 @@ struct xe_guc_exec_queue {
struct rcu_head rcu;
/** @sched: GPU scheduler for this xe_exec_queue */
struct xe_gpu_scheduler sched;
/**
* @name: Scheduler timeline name, kept with @sched until RCU free.
*/
char name[MAX_FENCE_NAME_LEN];
/** @entity: Scheduler entity for this xe_exec_queue */
struct xe_sched_entity entity;
/**

View File

@@ -10,6 +10,7 @@
#include <linux/circ_buf.h>
#include <linux/dma-fence-array.h>
#include <drm/drm_drv.h>
#include <drm/drm_managed.h>
#include "abi/guc_actions_abi.h"
@@ -37,6 +38,7 @@
#include "xe_macros.h"
#include "xe_map.h"
#include "xe_mocs.h"
#include "xe_module.h"
#include "xe_pm.h"
#include "xe_ring_ops_types.h"
#include "xe_sched_job.h"
@@ -232,17 +234,9 @@ static bool exec_queue_killed_or_banned_or_wedged(struct xe_exec_queue *q)
static void guc_submit_sw_fini(struct drm_device *drm, void *arg)
{
struct xe_guc *guc = arg;
struct xe_device *xe = guc_to_xe(guc);
struct xe_gt *gt = guc_to_gt(guc);
int ret;
ret = wait_event_timeout(guc->submission_state.fini_wq,
xa_empty(&guc->submission_state.exec_queue_lookup),
HZ * 5);
drain_workqueue(xe->destroy_wq);
xe_gt_assert(gt, ret);
xe_gt_assert(gt, xa_empty(&guc->submission_state.exec_queue_lookup));
xa_destroy(&guc->submission_state.exec_queue_lookup);
}
@@ -319,8 +313,6 @@ int xe_guc_submit_init(struct xe_guc *guc, unsigned int num_ids)
xa_init(&guc->submission_state.exec_queue_lookup);
init_waitqueue_head(&guc->submission_state.fini_wq);
primelockdep(guc);
guc->submission_state.initialized = true;
@@ -411,9 +403,6 @@ static void __release_guc_id(struct xe_guc *guc, struct xe_exec_queue *q,
xe_guc_id_mgr_release_locked(&guc->submission_state.idm,
q->guc->id, q->width);
if (xa_empty(&guc->submission_state.exec_queue_lookup))
wake_up(&guc->submission_state.fini_wq);
mutex_unlock(&guc->submission_state.lock);
}
@@ -1685,6 +1674,7 @@ static void guc_exec_queue_fini(struct xe_exec_queue *q)
{
struct xe_guc_exec_queue *ge = q->guc;
struct xe_guc *guc = exec_queue_to_guc(q);
struct drm_device *drm = &guc_to_xe(guc)->drm;
if (xe_exec_queue_is_multi_queue_secondary(q)) {
struct xe_exec_queue_group *group = q->multi_queue.group;
@@ -1703,36 +1693,52 @@ static void guc_exec_queue_fini(struct xe_exec_queue *q)
* (timeline name).
*/
kfree_rcu(ge, rcu);
drm_dev_put(drm);
}
static void guc_exec_queue_do_destroy(struct xe_exec_queue *q)
{
struct xe_guc_exec_queue *ge = q->guc;
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
struct drm_device *drm = &xe->drm;
/*
* guc_exec_queue_fini() drops the queue's drm_device ref.
* Keep the device alive until the PM-runtime guard unwinds.
*/
drm_dev_get(drm);
scoped_guard(xe_pm_runtime, xe) {
trace_xe_exec_queue_destroy(q);
/* Confirm no work left behind accessing device structures */
cancel_delayed_work_sync(&ge->sched.base.work_tdr);
xe_exec_queue_fini(q);
}
drm_dev_put(drm);
}
static void __guc_exec_queue_destroy_async(struct work_struct *w)
{
struct xe_guc_exec_queue *ge =
container_of(w, struct xe_guc_exec_queue, destroy_async);
struct xe_exec_queue *q = ge->q;
struct xe_guc *guc = exec_queue_to_guc(q);
guard(xe_pm_runtime)(guc_to_xe(guc));
trace_xe_exec_queue_destroy(q);
/* Confirm no work left behind accessing device structures */
cancel_delayed_work_sync(&ge->sched.base.work_tdr);
xe_exec_queue_fini(q);
guc_exec_queue_do_destroy(ge->q);
}
static void guc_exec_queue_destroy_async(struct xe_exec_queue *q)
{
struct xe_guc *guc = exec_queue_to_guc(q);
struct xe_device *xe = guc_to_xe(guc);
INIT_WORK(&q->guc->destroy_async, __guc_exec_queue_destroy_async);
/* We must block on kernel engines so slabs are empty on driver unload */
if (q->flags & EXEC_QUEUE_FLAG_PERMANENT || exec_queue_wedged(q))
__guc_exec_queue_destroy_async(&q->guc->destroy_async);
guc_exec_queue_do_destroy(q);
else
queue_work(xe->destroy_wq, &q->guc->destroy_async);
xe_destroy_wq_queue(&q->guc->destroy_async);
}
static void __guc_exec_queue_destroy(struct xe_guc *guc, struct xe_exec_queue *q)
@@ -1927,6 +1933,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
{
struct xe_gpu_scheduler *sched;
struct xe_guc *guc = exec_queue_to_guc(q);
struct drm_device *drm = &guc_to_xe(guc)->drm;
struct workqueue_struct *submit_wq = NULL;
struct xe_guc_exec_queue *ge;
long timeout;
@@ -1938,6 +1945,8 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
if (!ge)
return -ENOMEM;
drm_dev_get(drm);
q->guc = ge;
ge->q = q;
init_rcu_head(&ge->rcu);
@@ -1955,6 +1964,8 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
xe_exec_queue_assign_name(q, q->guc->id);
strscpy(ge->name, q->name, sizeof(ge->name));
/*
* Use primary queue's submit_wq for all secondary queues of a
* multi queue group. This serialization avoids any locking around
@@ -1969,7 +1980,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
err = xe_sched_init(&ge->sched, &drm_sched_ops, &xe_sched_ops,
submit_wq, xe_lrc_ring_size() / MAX_JOB_SIZE_BYTES, 64,
timeout, guc_to_gt(guc)->ordered_wq, NULL,
q->name, gt_to_xe(q->gt)->drm.dev);
ge->name, gt_to_xe(q->gt)->drm.dev);
if (err)
goto err_release_id;
@@ -2014,6 +2025,7 @@ static int guc_exec_queue_init(struct xe_exec_queue *q)
release_guc_id(guc, q);
err_free:
kfree(ge);
drm_dev_put(drm);
return err;
}

View File

@@ -100,8 +100,6 @@ struct xe_guc {
* even initialized - before that not even the lock is valid
*/
bool initialized;
/** @submission_state.fini_wq: submit fini wait queue */
wait_queue_head_t fini_wq;
} submission_state;
/** @hwconfig: Hardware config state */

View File

@@ -1166,6 +1166,8 @@ static int emit_flush_invalidate(u32 *dw, int i, u32 flags)
* @tile: Tile whose migration context to be used.
* @q : Execution to be used along with migration context.
* @src_bo: The buffer object @src is currently bound to.
* @new_mem: The (not yet committed) destination resource @src_bo is being
* moved into; src_bo->ttm.resource is still the old resource.
* @read_write : Creates BB commands for CCS read/write.
*
* Creates batch buffer instructions to copy CCS metadata from CCS pool to
@@ -1177,12 +1179,13 @@ static int emit_flush_invalidate(u32 *dw, int i, u32 flags)
*/
int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
struct xe_bo *src_bo,
struct ttm_resource *new_mem,
enum xe_sriov_vf_ccs_rw_ctxs read_write)
{
bool src_is_pltt = read_write == XE_SRIOV_VF_CCS_READ_CTX;
bool dst_is_pltt = read_write == XE_SRIOV_VF_CCS_WRITE_CTX;
struct ttm_resource *src = src_bo->ttm.resource;
struct ttm_resource *src = new_mem;
struct xe_migrate *m = tile->migrate;
struct xe_gt *gt = tile->primary_gt;
u32 batch_size, batch_size_allocated;

View File

@@ -138,6 +138,7 @@ struct dma_fence *xe_migrate_resolve(struct xe_migrate *m,
int xe_migrate_ccs_rw_copy(struct xe_tile *tile, struct xe_exec_queue *q,
struct xe_bo *src_bo,
struct ttm_resource *new_mem,
enum xe_sriov_vf_ccs_rw_ctxs read_write);
void xe_migrate_ccs_rw_copy_clear(struct xe_bo *src_bo,

View File

@@ -7,6 +7,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <drm/drm_module.h>
@@ -91,6 +92,50 @@ static int xe_check_nomodeset(void)
return 0;
}
static struct workqueue_struct *xe_destroy_wq;
static int __init xe_destroy_wq_module_init(void)
{
xe_destroy_wq = alloc_workqueue("xe-guc-destroy-wq", WQ_UNBOUND, 0);
if (!xe_destroy_wq)
return -ENOMEM;
return 0;
}
static void xe_destroy_wq_module_exit(void)
{
if (xe_destroy_wq)
destroy_workqueue(xe_destroy_wq);
xe_destroy_wq = NULL;
}
/**
* xe_destroy_wq_queue() - Queue work on the destroy workqueue
* @work: work item to queue
*
* The destroy workqueue has module lifetime and is used for GuC exec queue
* teardown that can outlive a single xe_device. SVM pagemap destroy uses the
* per-device xe->destroy_wq instead.
*
* Return: %true if @work was queued, %false if it was already pending.
*/
bool xe_destroy_wq_queue(struct work_struct *work)
{
return queue_work(xe_destroy_wq, work);
}
/**
* xe_destroy_wq_flush() - Flush the destroy workqueue
*
* Drains all pending destroy work. Called from PCI remove to ensure
* teardown ordering before the device is destroyed.
*/
void xe_destroy_wq_flush(void)
{
if (xe_destroy_wq)
flush_workqueue(xe_destroy_wq);
}
struct init_funcs {
int (*init)(void);
void (*exit)(void);
@@ -112,6 +157,10 @@ static const struct init_funcs init_funcs[] = {
.init = xe_sched_job_module_init,
.exit = xe_sched_job_module_exit,
},
{
.init = xe_destroy_wq_module_init,
.exit = xe_destroy_wq_module_exit,
},
{
.init = xe_register_pci_driver,
.exit = xe_unregister_pci_driver,

View File

@@ -8,6 +8,8 @@
#include <linux/types.h>
struct work_struct;
/* Module modprobe variables */
struct xe_modparam {
bool force_execlist;
@@ -27,5 +29,8 @@ struct xe_modparam {
extern struct xe_modparam xe_modparam;
bool xe_destroy_wq_queue(struct work_struct *work);
void xe_destroy_wq_flush(void);
#endif

View File

@@ -60,35 +60,40 @@ static bool xe_nvm_writable_override(struct xe_device *xe)
struct xe_mmio *mmio = xe_root_tile_mmio(xe);
bool writable_override;
struct xe_reg reg;
u32 test_bit;
u32 test_bit, test_val;
switch (xe->info.platform) {
case XE_CRESCENTISLAND:
reg = PCODE_SCRATCH(0);
test_bit = FDO_MODE;
test_val = FDO_MODE;
break;
case XE_BATTLEMAGE:
reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
test_val = 0;
break;
case XE_PVC:
reg = HECI_FWSTS2(PVC_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
test_val = 0;
break;
case XE_DG2:
reg = HECI_FWSTS2(DG2_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
test_val = 0;
break;
case XE_DG1:
reg = HECI_FWSTS2(DG1_GSC_HECI2_BASE);
test_bit = HECI_FW_STATUS_2_NVM_ACCESS_MODE;
test_val = 0;
break;
default:
drm_err(&xe->drm, "Unknown platform\n");
return true;
}
writable_override = !(xe_mmio_read32(mmio, reg) & test_bit);
writable_override = (xe_mmio_read32(mmio, reg) & test_bit) == test_val;
if (writable_override)
drm_info(&xe->drm, "NVM access overridden by jumper\n");
return writable_override;

View File

@@ -788,7 +788,8 @@ static int xe_info_init_early(struct xe_device *xe,
xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) &&
xe_modparam.probe_display &&
desc->has_display;
desc->has_display &&
!xe_device_is_admin_only(xe);
xe->info.force_execlist = xe_modparam.force_execlist;
xe_assert(xe, desc->max_gt_per_tile > 0);
@@ -1048,6 +1049,12 @@ static void xe_pci_remove(struct pci_dev *pdev)
return;
xe_device_remove(xe);
/*
* Preserve remove-time flush after moving destroy work to module
* lifetime.
*/
xe_destroy_wq_flush();
xe_pm_fini(xe);
}

View File

@@ -2365,8 +2365,11 @@ static void
xe_pt_update_ops_init(struct xe_vm_pgtable_update_ops *pt_update_ops)
{
init_llist_head(&pt_update_ops->deferred);
pt_update_ops->current_op = 0;
pt_update_ops->start = ~0x0ull;
pt_update_ops->last = 0x0ull;
pt_update_ops->needs_svm_lock = false;
pt_update_ops->needs_invalidation = false;
xe_page_reclaim_list_init(&pt_update_ops->prl);
}

View File

@@ -404,6 +404,8 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx)
/**
* xe_sriov_vf_ccs_attach_bo - Insert CCS read write commands in the BO.
* @bo: the &buffer object to which batch buffer commands will be added.
* @new_mem: the (not yet committed) destination resource @bo is being moved
* into; bo->ttm.resource is still the old resource at this point.
*
* This function shall be called only by VF. It inserts the PTEs and copy
* command instructions in the BO by calling xe_migrate_ccs_rw_copy()
@@ -411,7 +413,7 @@ void xe_sriov_vf_ccs_rw_update_bb_addr(struct xe_sriov_vf_ccs_ctx *ctx)
*
* Returns: 0 if successful, negative error code on failure.
*/
int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo)
int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem)
{
struct xe_device *xe = xe_bo_device(bo);
enum xe_sriov_vf_ccs_rw_ctxs ctx_id;
@@ -430,7 +432,21 @@ int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo)
xe_assert(xe, !bb);
ctx = &xe->sriov.vf.ccs.contexts[ctx_id];
err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, ctx_id);
err = xe_migrate_ccs_rw_copy(tile, ctx->mig_q, bo, new_mem, ctx_id);
if (err)
goto err_unwind;
}
return 0;
err_unwind:
/*
* Clean up any contexts already attached. Can't reuse
* xe_sriov_vf_ccs_detach_bo() here as it requires both contexts
* attached before cleaning up either one.
*/
for_each_ccs_rw_ctx(ctx_id) {
if (bo->bb_ccs[ctx_id])
xe_migrate_ccs_rw_copy_clear(bo, ctx_id);
}
return err;
}

View File

@@ -11,11 +11,12 @@
#include "xe_sriov_vf_ccs_types.h"
struct drm_printer;
struct ttm_resource;
struct xe_device;
struct xe_bo;
int xe_sriov_vf_ccs_init(struct xe_device *xe);
int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo);
int xe_sriov_vf_ccs_attach_bo(struct xe_bo *bo, struct ttm_resource *new_mem);
int xe_sriov_vf_ccs_detach_bo(struct xe_bo *bo);
int xe_sriov_vf_ccs_register_context(struct xe_device *xe);
void xe_sriov_vf_ccs_rebase(struct xe_device *xe);

View File

@@ -3255,11 +3255,26 @@ static int op_lock_and_prep(struct drm_exec *exec, struct xe_vm *vm,
.request_decompress = false,
.check_purged = true,
});
if (!err && !xe_vma_has_no_bo(vma))
err = xe_bo_migrate(xe_vma_bo(vma),
region_to_mem_type[region],
NULL,
exec);
if (!err && !xe_vma_has_no_bo(vma)) {
struct xe_bo *bo = xe_vma_bo(vma);
u32 mem_type;
if (region == DRM_XE_CONSULT_MEM_ADVISE_PREF_LOC) {
unsigned int i;
mem_type = XE_PL_TT;
for (i = 0; i < bo->placement.num_placement; i++) {
if (mem_type_is_vram(bo->placements[i].mem_type)) {
mem_type = bo->placements[i].mem_type;
break;
}
}
} else {
mem_type = region_to_mem_type[region];
}
err = xe_bo_migrate(bo, mem_type, NULL, exec);
}
break;
}
default:

View File

@@ -49,9 +49,9 @@
*/
/* Default WOPCM size is 2MB from Gen11, 1MB on previous platforms */
/* FIXME: Larger size require for 2 tile PVC, do a proper probe sooner or later */
/* FIXME: Larger size require for some platforms, do a proper probe sooner or later */
#define DGFX_WOPCM_SIZE SZ_4M
/* FIXME: Larger size require for MTL, do a proper probe sooner or later */
#define LNL_WOPCM_SIZE SZ_8M
#define MTL_WOPCM_SIZE SZ_4M
#define WOPCM_SIZE SZ_2M
@@ -179,9 +179,14 @@ static int __wopcm_init_regs(struct xe_device *xe, struct xe_gt *gt,
u32 xe_wopcm_size(struct xe_device *xe)
{
return IS_DGFX(xe) ? DGFX_WOPCM_SIZE :
xe->info.platform == XE_METEORLAKE ? MTL_WOPCM_SIZE :
WOPCM_SIZE;
if (xe->info.platform >= XE_LUNARLAKE)
return LNL_WOPCM_SIZE;
else if (IS_DGFX(xe))
return DGFX_WOPCM_SIZE;
else if (xe->info.platform == XE_METEORLAKE)
return MTL_WOPCM_SIZE;
else
return WOPCM_SIZE;
}
static u32 max_wopcm_size(struct xe_device *xe)

View File

@@ -1012,10 +1012,10 @@ void host1x_bo_clear_cached_mappings(struct host1x_bo *bo)
if (WARN_ON(!cache))
continue;
mutex_lock(&mapping->cache->lock);
mutex_lock(&cache->lock);
WARN_ON(kref_read(&mapping->ref) != 1);
__host1x_bo_unpin(&mapping->ref);
mutex_unlock(&mapping->cache->lock);
mutex_unlock(&cache->lock);
}
}
EXPORT_SYMBOL(host1x_bo_clear_cached_mappings);

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 *