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