mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 19:13:03 -04:00
Merge tag 'amd-drm-next-7.3-2026-07-15' of https://gitlab.freedesktop.org/agd5f/linux into drm-next
amd-drm-next-7.3-2026-07-15: amdgpu: - mmhub 4.2 update - DCN4.2 updates - DC CACP support - DMUB updates - PSR fixes - Replay fixes - Change system_unbound_wq with system_dfl_wq - GC 12.1 ip dump support - DCN4 fixes - DCN4.2 updates - DSC fixes - More DC KUNIT tests - JPEG cleanups - NUTMEG fixes - 8K fix - Backlight fixes - Cursor fixes - UserQ fixes - Fix bo->pin leak in amdgpu_bo_create_reserved() - MES11 updates - Loosen VFCT bios parsing to deal with pci=realloc messing around with the PCI buses - DCN pageflip timeout fixes - devcoredump fixes - MCLK DPM fix for bonaire SKU - DCE fix for DP MST when analog encoders are present - SI AC/DC switch fix - SMU7 AC/DC switch fix - VM fence handling fix - PCIe DPM fix - SMU 13 updates - SMU 15 updates - Inline drm_simple_encoder_init() - RAS updates - HDA sound fix - Handle dependency between GPU and XHCI devices amdkfd: - Bounds checking in CWSR size calculation - DQM init failure fixes radeon: - Loosen VFCT bios parsing to deal with pci=realloc messing around with the PCI buses Signed-off-by: Dave Airlie <airlied@redhat.com> From: Alex Deucher <alexander.deucher@amd.com> Link: https://patch.msgid.link/20260715222732.2719273-1-alexander.deucher@amd.com
This commit is contained in:
@@ -32,6 +32,9 @@ Interrupts
|
||||
.. kernel-doc:: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
|
||||
:functions: register_hpd_handlers dm_crtc_high_irq dm_pflip_high_irq
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
|
||||
:functions: amdgpu_display_hotplug_work_func
|
||||
|
||||
Atomic Implementation
|
||||
=====================
|
||||
|
||||
@@ -178,3 +181,9 @@ following path:
|
||||
2. On DC interface, :c:type:`struct mpcc_blnd_cfg <mpcc_blnd_cfg>` programs the
|
||||
MPCC blend configuration considering the :c:type:`dc_plane_info
|
||||
<dc_plane_info>` input from DPP.
|
||||
|
||||
Display Properties
|
||||
==================
|
||||
|
||||
.. kernel-doc:: drivers/gpu/drm/amd/amdgpu/amdgpu_display.c
|
||||
:doc: property for adaptive backlight modulation
|
||||
|
||||
@@ -926,7 +926,6 @@ struct amdgpu_device {
|
||||
bool debug_largebar;
|
||||
bool debug_disable_soft_recovery;
|
||||
bool debug_use_vram_fw_buf;
|
||||
bool debug_enable_ras_aca;
|
||||
bool debug_exp_resets;
|
||||
bool debug_disable_gpu_ring_reset;
|
||||
bool debug_vm_userptr;
|
||||
|
||||
@@ -378,19 +378,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;
|
||||
@@ -403,36 +443,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)
|
||||
|
||||
@@ -239,7 +239,7 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
|
||||
info->fw_version = amdgpu_get_firmware_version(cgs_device, type);
|
||||
info->feature_version = (uint16_t)le32_to_cpu(header->ucode_feature_version);
|
||||
} else {
|
||||
char fw_name[30] = {0};
|
||||
const char *fw_name = NULL;
|
||||
int err = 0;
|
||||
uint32_t ucode_size;
|
||||
uint32_t ucode_start_address;
|
||||
@@ -255,17 +255,17 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
|
||||
(adev->pdev->revision == 0x81) ||
|
||||
(adev->pdev->device == 0x665f)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/bonaire_k_smc.bin");
|
||||
fw_name = "bonaire_k_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/bonaire_smc.bin");
|
||||
fw_name = "bonaire_smc.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_HAWAII:
|
||||
if (adev->pdev->revision == 0x80) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/hawaii_k_smc.bin");
|
||||
fw_name = "hawaii_k_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/hawaii_smc.bin");
|
||||
fw_name = "hawaii_smc.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_TOPAZ:
|
||||
@@ -275,76 +275,76 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
|
||||
((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0xD1)) ||
|
||||
((adev->pdev->device == 0x6900) && (adev->pdev->revision == 0xD3))) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/topaz_k_smc.bin");
|
||||
fw_name = "topaz_k_smc.bin";
|
||||
} else
|
||||
strscpy(fw_name, "amdgpu/topaz_smc.bin");
|
||||
fw_name = "topaz_smc.bin";
|
||||
break;
|
||||
case CHIP_TONGA:
|
||||
if (((adev->pdev->device == 0x6939) && (adev->pdev->revision == 0xf1)) ||
|
||||
((adev->pdev->device == 0x6938) && (adev->pdev->revision == 0xf1))) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/tonga_k_smc.bin");
|
||||
fw_name = "tonga_k_smc.bin";
|
||||
} else
|
||||
strscpy(fw_name, "amdgpu/tonga_smc.bin");
|
||||
fw_name = "tonga_smc.bin";
|
||||
break;
|
||||
case CHIP_FIJI:
|
||||
strscpy(fw_name, "amdgpu/fiji_smc.bin");
|
||||
fw_name = "fiji_smc.bin";
|
||||
break;
|
||||
case CHIP_POLARIS11:
|
||||
if (type == CGS_UCODE_ID_SMU) {
|
||||
if (ASICID_IS_P21(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris11_k_smc.bin");
|
||||
fw_name = "polaris11_k_smc.bin";
|
||||
} else if (ASICID_IS_P31(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris11_k2_smc.bin");
|
||||
fw_name = "polaris11_k2_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/polaris11_smc.bin");
|
||||
fw_name = "polaris11_smc.bin";
|
||||
}
|
||||
} else if (type == CGS_UCODE_ID_SMU_SK) {
|
||||
strscpy(fw_name, "amdgpu/polaris11_smc_sk.bin");
|
||||
fw_name = "polaris11_smc_sk.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_POLARIS10:
|
||||
if (type == CGS_UCODE_ID_SMU) {
|
||||
if (ASICID_IS_P20(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris10_k_smc.bin");
|
||||
fw_name = "polaris10_k_smc.bin";
|
||||
} else if (ASICID_IS_P30(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris10_k2_smc.bin");
|
||||
fw_name = "polaris10_k2_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/polaris10_smc.bin");
|
||||
fw_name = "polaris10_smc.bin";
|
||||
}
|
||||
} else if (type == CGS_UCODE_ID_SMU_SK) {
|
||||
strscpy(fw_name, "amdgpu/polaris10_smc_sk.bin");
|
||||
fw_name = "polaris10_smc_sk.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_POLARIS12:
|
||||
if (ASICID_IS_P23(adev->pdev->device, adev->pdev->revision)) {
|
||||
info->is_kicker = true;
|
||||
strscpy(fw_name, "amdgpu/polaris12_k_smc.bin");
|
||||
fw_name = "polaris12_k_smc.bin";
|
||||
} else {
|
||||
strscpy(fw_name, "amdgpu/polaris12_smc.bin");
|
||||
fw_name = "polaris12_smc.bin";
|
||||
}
|
||||
break;
|
||||
case CHIP_VEGAM:
|
||||
strscpy(fw_name, "amdgpu/vegam_smc.bin");
|
||||
fw_name = "vegam_smc.bin";
|
||||
break;
|
||||
case CHIP_VEGA10:
|
||||
if ((adev->pdev->device == 0x687f) &&
|
||||
((adev->pdev->revision == 0xc0) ||
|
||||
(adev->pdev->revision == 0xc1) ||
|
||||
(adev->pdev->revision == 0xc3)))
|
||||
strscpy(fw_name, "amdgpu/vega10_acg_smc.bin");
|
||||
fw_name = "vega10_acg_smc.bin";
|
||||
else
|
||||
strscpy(fw_name, "amdgpu/vega10_smc.bin");
|
||||
fw_name = "vega10_smc.bin";
|
||||
break;
|
||||
case CHIP_VEGA12:
|
||||
strscpy(fw_name, "amdgpu/vega12_smc.bin");
|
||||
fw_name = "vega12_smc.bin";
|
||||
break;
|
||||
case CHIP_VEGA20:
|
||||
strscpy(fw_name, "amdgpu/vega20_smc.bin");
|
||||
fw_name = "vega20_smc.bin";
|
||||
break;
|
||||
default:
|
||||
drm_err(adev_to_drm(adev), "SMC firmware not supported\n");
|
||||
@@ -353,10 +353,8 @@ static int amdgpu_cgs_get_firmware_info(struct cgs_device *cgs_device,
|
||||
|
||||
err = amdgpu_ucode_request(adev, &adev->pm.fw,
|
||||
AMDGPU_UCODE_REQUIRED,
|
||||
"%s", fw_name);
|
||||
"amdgpu/%s", fw_name);
|
||||
if (err) {
|
||||
drm_err(adev_to_drm(adev),
|
||||
"Failed to load firmware \"%s\"\n", fw_name);
|
||||
amdgpu_ucode_release(&adev->pm.fw);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -498,6 +498,25 @@ int amdgpu_cper_init(struct amdgpu_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amdgpu_cper_deferred_init(struct amdgpu_device *adev)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (adev->cper.enabled)
|
||||
return 0;
|
||||
|
||||
r = amdgpu_cper_init(adev);
|
||||
if (r || !adev->cper.enabled)
|
||||
return r;
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
if (adev_to_drm(adev)->primary->debugfs_root)
|
||||
amdgpu_debugfs_ring_init(adev, &adev->cper.ring_buf);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int amdgpu_cper_fini(struct amdgpu_device *adev)
|
||||
{
|
||||
if (amdgpu_sriov_vf(adev))
|
||||
|
||||
@@ -92,6 +92,7 @@ int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev);
|
||||
void amdgpu_cper_ring_write(struct amdgpu_ring *ring,
|
||||
void *src, int count);
|
||||
int amdgpu_cper_init(struct amdgpu_device *adev);
|
||||
int amdgpu_cper_deferred_init(struct amdgpu_device *adev);
|
||||
int amdgpu_cper_fini(struct amdgpu_device *adev);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2178,6 +2178,8 @@ int amdgpu_debugfs_init(struct amdgpu_device *adev)
|
||||
|
||||
if (!ring)
|
||||
continue;
|
||||
if (ring == &adev->cper.ring_buf && !adev->cper.enabled)
|
||||
continue;
|
||||
|
||||
amdgpu_debugfs_ring_init(adev, ring);
|
||||
}
|
||||
|
||||
@@ -235,6 +235,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;
|
||||
}
|
||||
@@ -356,10 +359,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 */
|
||||
@@ -563,6 +570,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);
|
||||
};
|
||||
|
||||
@@ -1203,28 +1203,44 @@ bool amdgpu_device_seamless_boot_supported(struct amdgpu_device *adev)
|
||||
return amdgpu_ip_version(adev, DCE_HWIP, 0) >= IP_VERSION(3, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Intel hosts such as Rocket Lake, Alder Lake, Raptor Lake and Sapphire Rapids
|
||||
* don't support dynamic speed switching. Until we have confirmation from Intel
|
||||
* that a specific host supports it, it's safer that we keep it disabled for all.
|
||||
*
|
||||
* https://edc.intel.com/content/www/us/en/design/products/platforms/details/raptor-lake-s/13th-generation-core-processors-datasheet-volume-1-of-2/005/pci-express-support/
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/2663
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_X86)
|
||||
static const struct x86_cpu_id amdgpu_pcie_dynamic_switching_quirks[] = {
|
||||
/*
|
||||
* Intel hosts such as Rocket Lake, Alder Lake, Raptor Lake and Sapphire Rapids
|
||||
* don't support dynamic speed switching. Until we have confirmation from Intel
|
||||
* that a specific host supports it, it's safer that we keep it disabled for all.
|
||||
*
|
||||
* https://edc.intel.com/content/www/us/en/design/products/platforms/details/raptor-lake-s/13th-generation-core-processors-datasheet-volume-1-of-2/005/pci-express-support/
|
||||
* https://gitlab.freedesktop.org/drm/amd/-/issues/2663
|
||||
*/
|
||||
X86_MATCH_VENDOR_FAM(INTEL, X86_FAMILY_ANY, NULL),
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x08, NULL),
|
||||
{}
|
||||
};
|
||||
|
||||
static bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device *adev)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_X86)
|
||||
struct cpuinfo_x86 *c = &cpu_data(0);
|
||||
|
||||
/* eGPU change speeds based on USB4 fabric conditions */
|
||||
if (dev_is_removable(adev->dev))
|
||||
return true;
|
||||
|
||||
if (c->x86_vendor == X86_VENDOR_INTEL)
|
||||
/* Hosts have problems with dynamic speed switching */
|
||||
if (x86_match_cpu(amdgpu_pcie_dynamic_switching_quirks))
|
||||
return false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
static inline bool amdgpu_device_pcie_dynamic_switching_supported(struct amdgpu_device *adev)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool amdgpu_device_aspm_support_quirk(struct amdgpu_device *adev)
|
||||
{
|
||||
|
||||
@@ -3367,9 +3367,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):
|
||||
|
||||
@@ -139,7 +139,6 @@ enum AMDGPU_DEBUG_MASK {
|
||||
AMDGPU_DEBUG_LARGEBAR = BIT(1),
|
||||
AMDGPU_DEBUG_DISABLE_GPU_SOFT_RECOVERY = BIT(2),
|
||||
AMDGPU_DEBUG_USE_VRAM_FW_BUF = BIT(3),
|
||||
AMDGPU_DEBUG_ENABLE_RAS_ACA = BIT(4),
|
||||
AMDGPU_DEBUG_ENABLE_EXP_RESETS = BIT(5),
|
||||
AMDGPU_DEBUG_DISABLE_GPU_RING_RESET = BIT(6),
|
||||
AMDGPU_DEBUG_SMU_POOL = BIT(7),
|
||||
@@ -2264,11 +2263,6 @@ static void amdgpu_init_debug_options(struct amdgpu_device *adev)
|
||||
adev->debug_use_vram_fw_buf = true;
|
||||
}
|
||||
|
||||
if (amdgpu_debug_mask & AMDGPU_DEBUG_ENABLE_RAS_ACA) {
|
||||
pr_info("debug: enable RAS ACA\n");
|
||||
adev->debug_enable_ras_aca = true;
|
||||
}
|
||||
|
||||
if (amdgpu_debug_mask & AMDGPU_DEBUG_ENABLE_EXP_RESETS) {
|
||||
pr_info("debug: enable experimental reset features\n");
|
||||
adev->debug_exp_resets = true;
|
||||
|
||||
@@ -489,7 +489,13 @@ int amdgpu_device_ip_soft_reset(struct amdgpu_ring *guilty_ring,
|
||||
ip_type = amdgpu_ip_from_ring(guilty_ring->funcs->type);
|
||||
ip_block = amdgpu_device_ip_get_ip_block(adev, ip_type);
|
||||
|
||||
if (!ip_block || !ip_block->version->funcs->soft_reset) {
|
||||
if (unlikely(!ip_block)) {
|
||||
dev_warn(adev->dev, "IP block not found for ring %s\n",
|
||||
guilty_ring->name);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (!ip_block->version->funcs->soft_reset) {
|
||||
dev_warn(adev->dev, "IP block soft reset not supported on %s\n",
|
||||
ip_block->version->funcs->name);
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -545,7 +545,7 @@ void amdgpu_irq_delegate(struct amdgpu_device *adev,
|
||||
unsigned int num_dw)
|
||||
{
|
||||
amdgpu_ih_ring_write(adev, &adev->irq.ih_soft, entry->iv_entry, num_dw);
|
||||
queue_work(system_unbound_wq, &adev->irq.ih_soft_work);
|
||||
queue_work(system_dfl_wq, &adev->irq.ih_soft_work);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -921,6 +921,206 @@ int amdgpu_mes_update_enforce_isolation(struct amdgpu_device *adev)
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_mes_rs64mem_init - initialize RS64 local memory context arrays
|
||||
*
|
||||
* @mes: MES instance
|
||||
*
|
||||
* Returns 0 on success, negative errno on failure.
|
||||
*/
|
||||
int amdgpu_mes_rs64mem_init(struct amdgpu_mes *mes)
|
||||
{
|
||||
struct amdgpu_device *adev = container_of(mes, struct amdgpu_device, mes);
|
||||
int r;
|
||||
|
||||
if (!mes->use_rs64mem)
|
||||
return 0;
|
||||
|
||||
r = amdgpu_bo_create_kernel(adev, PAGE_SIZE, PAGE_SIZE,
|
||||
AMDGPU_GEM_DOMAIN_GTT,
|
||||
&mes->ctx_array_size_bo,
|
||||
&mes->ctx_array_size_gpu_addr,
|
||||
(void **)&mes->ctx_array_size_cpu_ptr);
|
||||
if (r) {
|
||||
dev_err(adev->dev,
|
||||
"Failed to allocate ctx array size BO, r=%d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
memset(mes->ctx_array_size_cpu_ptr, 0, PAGE_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_mes_rs64mem_fini - tear down RS64 local memory management
|
||||
*/
|
||||
void amdgpu_mes_rs64mem_fini(struct amdgpu_mes *mes)
|
||||
{
|
||||
if (mes->ctx_array_size_bo) {
|
||||
amdgpu_bo_free_kernel(&mes->ctx_array_size_bo,
|
||||
&mes->ctx_array_size_gpu_addr,
|
||||
(void **)&mes->ctx_array_size_cpu_ptr);
|
||||
}
|
||||
bitmap_free(mes->proc_ctx_bitmap);
|
||||
bitmap_free(mes->gang_ctx_bitmap);
|
||||
mes->use_rs64mem = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_mes_rs64mem_setup_bitmaps - allocate bitmaps after querying MES
|
||||
*
|
||||
* Called after QUERY_SCHEDULER_STATUS returns and MES has written
|
||||
* the array sizes to the GPU buffer. Reads the sizes and allocates
|
||||
* the tracking bitmaps.
|
||||
*
|
||||
* @mes: MES instance
|
||||
*
|
||||
* Returns 0 on success, negative errno on failure.
|
||||
*/
|
||||
int amdgpu_mes_rs64mem_setup_bitmaps(struct amdgpu_mes *mes)
|
||||
{
|
||||
struct amdgpu_device *adev = container_of(mes, struct amdgpu_device, mes);
|
||||
|
||||
if (!mes->use_rs64mem || !mes->ctx_array_size_cpu_ptr)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* MES FW wrote the sizes to the GPU buffer:
|
||||
* ctx_array_size_cpu_ptr[0] = proc_ctx_array_size (N)
|
||||
* ctx_array_size_cpu_ptr[1] = gang_ctx_array_size (M)
|
||||
*/
|
||||
mes->proc_ctx_array_size = mes->ctx_array_size_cpu_ptr[0];
|
||||
mes->gang_ctx_array_size = mes->ctx_array_size_cpu_ptr[1];
|
||||
|
||||
/* Sanity check - MES FW typically returns N=50, M=300 */
|
||||
if (mes->proc_ctx_array_size == 0 || mes->gang_ctx_array_size == 0) {
|
||||
dev_warn(adev->dev,
|
||||
"MES returned zero ctx array sizes (proc=%u, gang=%u), "
|
||||
"disabling RS64 local memory optimization\n",
|
||||
mes->proc_ctx_array_size, mes->gang_ctx_array_size);
|
||||
mes->use_rs64mem = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cap to safety limits */
|
||||
if (mes->proc_ctx_array_size > AMDGPU_MES_PROC_CTX_ARRAY_MAX)
|
||||
mes->proc_ctx_array_size = AMDGPU_MES_PROC_CTX_ARRAY_MAX;
|
||||
if (mes->gang_ctx_array_size > AMDGPU_MES_GANG_CTX_ARRAY_MAX)
|
||||
mes->gang_ctx_array_size = AMDGPU_MES_GANG_CTX_ARRAY_MAX;
|
||||
|
||||
dev_info(adev->dev,
|
||||
"MES RS64 local memory: proc_ctx_array_size:%u, "
|
||||
"gang_ctx_array_size:%u\n",
|
||||
mes->proc_ctx_array_size, mes->gang_ctx_array_size);
|
||||
|
||||
/* Allocate bitmaps */
|
||||
mes->proc_ctx_bitmap = bitmap_zalloc(mes->proc_ctx_array_size,
|
||||
GFP_KERNEL);
|
||||
if (!mes->proc_ctx_bitmap) {
|
||||
mes->use_rs64mem = false;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mes->gang_ctx_bitmap = bitmap_zalloc(mes->gang_ctx_array_size,
|
||||
GFP_KERNEL);
|
||||
if (!mes->gang_ctx_bitmap) {
|
||||
bitmap_free(mes->proc_ctx_bitmap);
|
||||
mes->proc_ctx_bitmap = NULL;
|
||||
mes->use_rs64mem = false;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_mes_alloc_proc_ctx_index - allocate a process context slot
|
||||
*
|
||||
* @mes: MES instance
|
||||
*
|
||||
* Returns 0 on success, -ENOSPC if all slots are used (caller should
|
||||
* fall back to system memory path).
|
||||
*/
|
||||
int amdgpu_mes_alloc_proc_ctx_index(struct amdgpu_mes *mes,
|
||||
struct amdgpu_usermode_queue *queue)
|
||||
{
|
||||
unsigned long bit;
|
||||
|
||||
if (!mes->use_rs64mem || !mes->proc_ctx_bitmap)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
amdgpu_mes_lock(mes);
|
||||
bit = find_first_zero_bit(mes->proc_ctx_bitmap,
|
||||
mes->proc_ctx_array_size);
|
||||
if (bit >= mes->proc_ctx_array_size) {
|
||||
amdgpu_mes_unlock(mes);
|
||||
return -ENOSPC;
|
||||
}
|
||||
set_bit(bit, mes->proc_ctx_bitmap);
|
||||
queue->proc_ctx_array_index = (uint32_t)bit;
|
||||
amdgpu_mes_unlock(mes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_mes_free_proc_ctx_index - free a process context slot
|
||||
*/
|
||||
void amdgpu_mes_free_proc_ctx_index(struct amdgpu_mes *mes,
|
||||
struct amdgpu_usermode_queue *queue)
|
||||
{
|
||||
if (!mes->use_rs64mem || !mes->proc_ctx_bitmap)
|
||||
return;
|
||||
if (queue->proc_ctx_array_index >= mes->proc_ctx_array_size)
|
||||
return;
|
||||
|
||||
amdgpu_mes_lock(mes);
|
||||
clear_bit(queue->proc_ctx_array_index, mes->proc_ctx_bitmap);
|
||||
amdgpu_mes_unlock(mes);
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_mes_alloc_gang_ctx_index - allocate a gang context slot
|
||||
*/
|
||||
int amdgpu_mes_alloc_gang_ctx_index(struct amdgpu_mes *mes,
|
||||
struct amdgpu_usermode_queue *queue)
|
||||
{
|
||||
unsigned long bit;
|
||||
|
||||
if (!mes->use_rs64mem || !mes->gang_ctx_bitmap)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
amdgpu_mes_lock(mes);
|
||||
bit = find_first_zero_bit(mes->gang_ctx_bitmap,
|
||||
mes->gang_ctx_array_size);
|
||||
if (bit >= mes->gang_ctx_array_size) {
|
||||
amdgpu_mes_unlock(mes);
|
||||
return -ENOSPC;
|
||||
}
|
||||
set_bit(bit, mes->gang_ctx_bitmap);
|
||||
queue->gang_ctx_array_index = bit;
|
||||
amdgpu_mes_unlock(mes);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* amdgpu_mes_free_gang_ctx_index - free a gang context slot
|
||||
*/
|
||||
void amdgpu_mes_free_gang_ctx_index(struct amdgpu_mes *mes,
|
||||
struct amdgpu_usermode_queue *queue)
|
||||
{
|
||||
if (!mes->use_rs64mem || !mes->gang_ctx_bitmap)
|
||||
return;
|
||||
if (queue->gang_ctx_array_index >= mes->gang_ctx_array_size)
|
||||
return;
|
||||
|
||||
amdgpu_mes_lock(mes);
|
||||
clear_bit(queue->gang_ctx_array_index, mes->gang_ctx_bitmap);
|
||||
amdgpu_mes_unlock(mes);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
|
||||
static int amdgpu_debugfs_mes_event_log_show(struct seq_file *m, void *unused)
|
||||
|
||||
@@ -54,7 +54,8 @@ enum amdgpu_mes_priority_level {
|
||||
|
||||
#define AMDGPU_MES_PROC_CTX_SIZE 0x1000 /* one page area */
|
||||
#define AMDGPU_MES_GANG_CTX_SIZE 0x1000 /* one page area */
|
||||
|
||||
#define AMDGPU_MES_PROC_CTX_ARRAY_MAX 128
|
||||
#define AMDGPU_MES_GANG_CTX_ARRAY_MAX 512
|
||||
struct amdgpu_mes_funcs;
|
||||
|
||||
enum amdgpu_mes_pipe {
|
||||
@@ -171,6 +172,17 @@ struct amdgpu_mes {
|
||||
|
||||
bool compute_pipe_reset_enabled;
|
||||
bool gfx_pipe_reset_enabled;
|
||||
|
||||
bool use_rs64mem;
|
||||
struct amdgpu_bo *ctx_array_size_bo;
|
||||
uint64_t ctx_array_size_gpu_addr;
|
||||
uint32_t *ctx_array_size_cpu_ptr;
|
||||
|
||||
uint32_t proc_ctx_array_size;
|
||||
unsigned long *proc_ctx_bitmap;
|
||||
uint32_t gang_ctx_array_size;
|
||||
uint32_t gang_ctx_array_index;
|
||||
unsigned long *gang_ctx_bitmap;
|
||||
};
|
||||
|
||||
struct amdgpu_mes_hung_queue_hqd_info {
|
||||
@@ -268,6 +280,8 @@ struct mes_add_queue_input {
|
||||
uint32_t exclusively_scheduled;
|
||||
uint32_t sh_mem_config_data;
|
||||
uint32_t vm_cntx_cntl;
|
||||
uint32_t process_context_array_index;
|
||||
uint32_t gang_context_array_index;
|
||||
};
|
||||
|
||||
struct mes_remove_queue_input {
|
||||
@@ -276,6 +290,7 @@ struct mes_remove_queue_input {
|
||||
uint64_t gang_context_addr;
|
||||
uint32_t queue_type;
|
||||
bool remove_queue_after_reset;
|
||||
uint32_t gang_context_array_index;
|
||||
};
|
||||
|
||||
struct mes_map_legacy_queue_input {
|
||||
@@ -617,4 +632,15 @@ bool amdgpu_mes_queue_reset_by_mes_supported(struct amdgpu_device *adev);
|
||||
|
||||
int amdgpu_mes_update_enforce_isolation(struct amdgpu_device *adev);
|
||||
|
||||
int amdgpu_mes_rs64mem_init(struct amdgpu_mes *mes);
|
||||
void amdgpu_mes_rs64mem_fini(struct amdgpu_mes *mes);
|
||||
int amdgpu_mes_rs64mem_setup_bitmaps(struct amdgpu_mes *mes);
|
||||
int amdgpu_mes_alloc_proc_ctx_index(struct amdgpu_mes *mes,
|
||||
struct amdgpu_usermode_queue *queue);
|
||||
void amdgpu_mes_free_proc_ctx_index(struct amdgpu_mes *mes,
|
||||
struct amdgpu_usermode_queue *queue);
|
||||
int amdgpu_mes_alloc_gang_ctx_index(struct amdgpu_mes *mes,
|
||||
struct amdgpu_usermode_queue *queue);
|
||||
void amdgpu_mes_free_gang_ctx_index(struct amdgpu_mes *mes,
|
||||
struct amdgpu_usermode_queue *queue);
|
||||
#endif /* __AMDGPU_MES_H__ */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1696,8 +1699,9 @@ u64 amdgpu_bo_print_info(int id, struct amdgpu_bo *bo, struct seq_file *m)
|
||||
if (dma_resv_trylock(bo->tbo.base.resv)) {
|
||||
dma_resv_describe(bo->tbo.base.resv, m);
|
||||
dma_resv_unlock(bo->tbo.base.resv);
|
||||
} else {
|
||||
seq_puts(m, "\n");
|
||||
}
|
||||
seq_puts(m, "\n");
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -596,8 +596,14 @@ static ssize_t amdgpu_ras_debugfs_ctrl_write(struct file *f,
|
||||
ret = amdgpu_ras_feature_enable(adev, &data.head, 1);
|
||||
break;
|
||||
case 2:
|
||||
/* umc ce/ue error injection for a bad page is not allowed */
|
||||
if (data.head.block == AMDGPU_RAS_BLOCK__UMC)
|
||||
/*
|
||||
* UMC ce/ue error injection for a bad page is not allowed. For
|
||||
* uniras (SMU v13+) devices the injection address is validated by
|
||||
* the ras_mgr inject handler, so only run the legacy bad page
|
||||
* check for the legacy RAS path.
|
||||
*/
|
||||
if (data.head.block == AMDGPU_RAS_BLOCK__UMC &&
|
||||
!amdgpu_uniras_enabled(adev))
|
||||
ret = amdgpu_ras_check_bad_page(adev, data.inject.address);
|
||||
if (ret == -EINVAL) {
|
||||
dev_warn(adev->dev, "RAS WARN: input address 0x%llx is invalid.",
|
||||
@@ -3777,8 +3783,11 @@ int amdgpu_ras_block_late_init(struct amdgpu_device *adev,
|
||||
}
|
||||
|
||||
if (amdgpu_uniras_enabled(adev) || (ras_obj->hw_ops &&
|
||||
(ras_obj->hw_ops->query_ras_error_count ||
|
||||
ras_obj->hw_ops->query_ras_error_status))) {
|
||||
(ras_obj->hw_ops->query_ras_error_count ||
|
||||
ras_obj->hw_ops->query_ras_error_status)) ||
|
||||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) ||
|
||||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) ||
|
||||
amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6)) {
|
||||
r = amdgpu_ras_sysfs_create(adev, ras_block);
|
||||
if (r)
|
||||
goto interrupt;
|
||||
@@ -4981,7 +4990,13 @@ void amdgpu_ras_post_reset(struct amdgpu_device *adev,
|
||||
}
|
||||
}
|
||||
|
||||
void amdgpu_ras_resume_after_reset(struct amdgpu_device *adev)
|
||||
int amdgpu_ras_resume_after_reset(struct amdgpu_device *adev)
|
||||
{
|
||||
amdgpu_ras_mgr_resume_after_reset(adev);
|
||||
int r;
|
||||
|
||||
r = amdgpu_ras_mgr_resume_after_reset(adev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
return amdgpu_cper_deferred_init(adev);
|
||||
}
|
||||
|
||||
@@ -980,5 +980,5 @@ void amdgpu_ras_pre_reset(struct amdgpu_device *adev,
|
||||
struct list_head *device_list);
|
||||
void amdgpu_ras_post_reset(struct amdgpu_device *adev,
|
||||
struct list_head *device_list);
|
||||
void amdgpu_ras_resume_after_reset(struct amdgpu_device *adev);
|
||||
int amdgpu_ras_resume_after_reset(struct amdgpu_device *adev);
|
||||
#endif
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1422,18 +1422,21 @@ 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;
|
||||
|
||||
trace_amdgpu_userq_state_start(queue);
|
||||
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) {
|
||||
trace_amdgpu_userq_state_start(queue);
|
||||
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
|
||||
*/
|
||||
trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
|
||||
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.
|
||||
*/
|
||||
trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG);
|
||||
queue->state = AMDGPU_USERQ_STATE_HUNG;
|
||||
amdgpu_userq_fence_driver_force_completion(queue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,9 @@ struct amdgpu_usermode_queue {
|
||||
} va;
|
||||
u64 va_array[6];
|
||||
} userq_vas;
|
||||
|
||||
uint32_t proc_ctx_array_index;
|
||||
uint32_t gang_ctx_array_index;
|
||||
};
|
||||
|
||||
struct amdgpu_userq_funcs {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_simple_kms_helper.h>
|
||||
#include <drm/drm_encoder.h>
|
||||
#include <drm/drm_gem_framebuffer_helper.h>
|
||||
#include <drm/drm_vblank.h>
|
||||
#include <drm/drm_vblank_helper.h>
|
||||
@@ -316,6 +316,10 @@ static struct drm_plane *amdgpu_vkms_plane_init(struct drm_device *dev,
|
||||
return plane;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs drm_encoder_funcs_cleanup = {
|
||||
.destroy = drm_encoder_cleanup,
|
||||
};
|
||||
|
||||
static int amdgpu_vkms_output_init(struct drm_device *dev, struct
|
||||
amdgpu_vkms_output *output, int index)
|
||||
{
|
||||
@@ -342,7 +346,9 @@ static int amdgpu_vkms_output_init(struct drm_device *dev, struct
|
||||
|
||||
drm_connector_helper_add(connector, &amdgpu_vkms_conn_helper_funcs);
|
||||
|
||||
ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_VIRTUAL);
|
||||
ret = drm_encoder_init(dev, encoder,
|
||||
&drm_encoder_funcs_cleanup,
|
||||
DRM_MODE_ENCODER_VIRTUAL, NULL);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to init encoder\n");
|
||||
goto err_encoder;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1393,7 +1393,10 @@ static void amdgpu_xgmi_reset_on_init_work(struct work_struct *work)
|
||||
* no-op for any other reset path where RAS is already
|
||||
* initialized, and for non-uniras devices.
|
||||
*/
|
||||
amdgpu_ras_resume_after_reset(tmp_adev);
|
||||
r = amdgpu_ras_resume_after_reset(tmp_adev);
|
||||
if (r)
|
||||
dev_err(tmp_adev->dev,
|
||||
"failed to resume RAS after XGMI reset-on-init\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4988,12 +4988,12 @@ static int gfx_v10_0_sw_init(struct amdgpu_ip_block *ip_block)
|
||||
|
||||
gfx_v10_0_gpu_early_init(adev);
|
||||
|
||||
gfx_v10_0_alloc_ip_dump(adev);
|
||||
|
||||
r = amdgpu_gfx_sysfs_init(adev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
gfx_v10_0_alloc_ip_dump(adev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1920,12 +1920,12 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gfx_v11_0_alloc_ip_dump(adev);
|
||||
|
||||
r = amdgpu_gfx_sysfs_init(adev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
gfx_v11_0_alloc_ip_dump(adev);
|
||||
|
||||
adev->gfx.me.use_mmio_for_reset = false;
|
||||
adev->gfx.mec.use_mmio_for_reset = true;
|
||||
|
||||
|
||||
@@ -1597,12 +1597,12 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block)
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
gfx_v12_0_alloc_ip_dump(adev);
|
||||
|
||||
r = amdgpu_gfx_sysfs_init(adev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
gfx_v12_0_alloc_ip_dump(adev);
|
||||
|
||||
adev->gfx.me.use_mmio_for_reset = false;
|
||||
adev->gfx.mec.use_mmio_for_reset = true;
|
||||
|
||||
|
||||
@@ -69,6 +69,127 @@ MODULE_FIRMWARE("amdgpu/gc_12_1_0_rlc_1.bin");
|
||||
(SH_MEM_ALIGNMENT_MODE_UNALIGNED_GFX12_1_0 << SH_MEM_CONFIG__ALIGNMENT_MODE__SHIFT) | \
|
||||
(3 << SH_MEM_CONFIG__INITIAL_INST_PREFETCH__SHIFT))
|
||||
|
||||
|
||||
static const struct amdgpu_hwip_reg_entry gc_reg_list_12_1[] = {
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regGRBM_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regGRBM_STATUS2),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regGRBM_STATUS3),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_STALLED_STAT1),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_STALLED_STAT2),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_STALLED_STAT3),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_CPC_STALLED_STAT1),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_CPF_STALLED_STAT1),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_BUSY_STAT),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_CPC_BUSY_STAT),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_CPF_BUSY_STAT),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_CPC_BUSY_STAT2),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_CPF_BUSY_STAT2),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_CPF_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_GFX_ERROR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_GFX_HPD_STATUS0),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_RB_BASE),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_RB_RPTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_RB_WPTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_RB0_BASE),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_RB0_RPTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_RB0_WPTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_IB1_CMD_BUFSZ),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_IB2_CMD_BUFSZ),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_IB1_BASE_LO),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_IB1_BASE_HI),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_IB1_BUFSZ),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_IB2_BASE_LO),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_IB2_BASE_HI),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_IB2_BUFSZ),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCPF_UTCL1_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCPC_UTCL1_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCPG_UTCL1_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regIA_UTCL1_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regIA_UTCL1_STATUS_2),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regPA_CL_CNTL_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regRMI_UTCL1_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regSQC_CACHES),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regSQG_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regWD_UTCL1_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regGCVM_L2_PROTECTION_FAULT_CNTL2),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regGCVM_L2_PROTECTION_FAULT_STATUS_LO32),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regGCVM_L2_PROTECTION_FAULT_STATUS_HI32),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_DEBUG),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_CNTL),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_CNTL),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_INSTR_PNTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_ME_INSTR_PNTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_PFP_INSTR_PNTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_CPC_STATUS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_GFX_RS64_INSTR_PNTR0),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_GFX_RS64_INSTR_PNTR1),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_RS64_INSTR_PNTR),
|
||||
/* cp header registers */
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MES_HEADER_DUMP),
|
||||
/* SE status registers */
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regGRBM_STATUS_SE0),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regGRBM_STATUS_SE1),
|
||||
};
|
||||
|
||||
static const struct amdgpu_hwip_reg_entry gc_cp_reg_list_12_1[] = {
|
||||
/* compute registers */
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_VMID),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PERSISTENT_STATE),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PIPE_PRIORITY),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_QUEUE_PRIORITY),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_QUANTUM),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PQ_BASE),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PQ_BASE_HI),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PQ_RPTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PQ_WPTR_POLL_ADDR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PQ_WPTR_POLL_ADDR_HI),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PQ_DOORBELL_CONTROL),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PQ_CONTROL),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_IB_BASE_ADDR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_IB_BASE_ADDR_HI),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_IB_RPTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_IB_CONTROL),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_DEQUEUE_REQUEST),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_EOP_BASE_ADDR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_EOP_BASE_ADDR_HI),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_EOP_CONTROL),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_EOP_RPTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_EOP_WPTR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_EOP_EVENTS),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_CTX_SAVE_BASE_ADDR_LO),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_CTX_SAVE_BASE_ADDR_HI),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_CTX_SAVE_CONTROL),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_CNTL_STACK_OFFSET),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_CNTL_STACK_SIZE),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_WG_STATE_OFFSET),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_CTX_SAVE_SIZE),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_GDS_RESOURCE_STATE),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_ERROR),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_EOP_WPTR_MEM),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PQ_WPTR_LO),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_PQ_WPTR_HI),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_SUSPEND_CNTL_STACK_OFFSET),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_SUSPEND_CNTL_STACK_DW_CNT),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_SUSPEND_WG_STATE_OFFSET),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_HQD_DEQUEUE_STATUS),
|
||||
/* cp header registers */
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_ME1_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_ME1_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_ME1_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_ME1_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_ME1_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_ME1_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_ME1_HEADER_DUMP),
|
||||
SOC15_REG_ENTRY_STR(GC, 0, regCP_MEC_ME1_HEADER_DUMP),
|
||||
};
|
||||
|
||||
static void gfx_v12_1_xcc_disable_gpa_mode(struct amdgpu_device *adev, int xcc_id);
|
||||
static void gfx_v12_1_set_ring_funcs(struct amdgpu_device *adev);
|
||||
static void gfx_v12_1_set_irq_funcs(struct amdgpu_device *adev);
|
||||
@@ -1149,6 +1270,155 @@ static int gfx_v12_1_rlc_backdoor_autoload_enable(struct amdgpu_device *adev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gfx_v12_1_alloc_ip_dump(struct amdgpu_device *adev)
|
||||
{
|
||||
uint32_t reg_count = ARRAY_SIZE(gc_reg_list_12_1);
|
||||
uint32_t *ptr, inst, num_xcc;
|
||||
|
||||
num_xcc = NUM_XCC(adev->gfx.xcc_mask);
|
||||
|
||||
ptr = kcalloc(reg_count * num_xcc, sizeof(uint32_t), GFP_KERNEL);
|
||||
if (!ptr) {
|
||||
DRM_ERROR("Failed to allocate memory for GFX IP Dump\n");
|
||||
adev->gfx.ip_dump_core = NULL;
|
||||
} else {
|
||||
adev->gfx.ip_dump_core = ptr;
|
||||
}
|
||||
|
||||
/* Allocate memory for compute queue registers for all the instances */
|
||||
reg_count = ARRAY_SIZE(gc_cp_reg_list_12_1);
|
||||
inst = adev->gfx.mec.num_mec * adev->gfx.mec.num_pipe_per_mec *
|
||||
adev->gfx.mec.num_queue_per_pipe;
|
||||
|
||||
ptr = kcalloc(reg_count * inst * num_xcc, sizeof(uint32_t), GFP_KERNEL);
|
||||
if (!ptr) {
|
||||
DRM_ERROR("Failed to allocate memory for Compute Queues IP Dump\n");
|
||||
adev->gfx.ip_dump_compute_queues = NULL;
|
||||
} else {
|
||||
adev->gfx.ip_dump_compute_queues = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_v12_1_ip_print(struct amdgpu_ip_block *ip_block,
|
||||
struct drm_printer *p)
|
||||
{
|
||||
struct amdgpu_device *adev = ip_block->adev;
|
||||
uint32_t i, j, k;
|
||||
uint32_t xcc_id, xcc_offset, inst_offset;
|
||||
uint32_t num_xcc, reg, num_inst;
|
||||
uint32_t reg_count = ARRAY_SIZE(gc_reg_list_12_1);
|
||||
|
||||
if (!adev->gfx.ip_dump_core)
|
||||
return;
|
||||
|
||||
num_xcc = NUM_XCC(adev->gfx.xcc_mask);
|
||||
drm_printf(p, "Number of Instances:%d\n", num_xcc);
|
||||
for (xcc_id = 0; xcc_id < num_xcc; xcc_id++) {
|
||||
xcc_offset = xcc_id * reg_count;
|
||||
drm_printf(p, "\nInstance id:%d\n", xcc_id);
|
||||
for (i = 0; i < reg_count; i++)
|
||||
drm_printf(p, "%-50s \t 0x%08x\n",
|
||||
gc_reg_list_12_1[i].reg_name,
|
||||
adev->gfx.ip_dump_core[xcc_offset + i]);
|
||||
}
|
||||
|
||||
/* print compute queue registers for all instances */
|
||||
if (!adev->gfx.ip_dump_compute_queues)
|
||||
return;
|
||||
|
||||
reg_count = ARRAY_SIZE(gc_cp_reg_list_12_1);
|
||||
drm_printf(p, "\nnum_xcc: %d num_mec: %d num_pipe: %d num_queue: %d\n",
|
||||
num_xcc,
|
||||
adev->gfx.mec.num_mec,
|
||||
adev->gfx.mec.num_pipe_per_mec,
|
||||
adev->gfx.mec.num_queue_per_pipe);
|
||||
|
||||
num_inst = adev->gfx.mec.num_mec * adev->gfx.mec.num_pipe_per_mec *
|
||||
adev->gfx.mec.num_queue_per_pipe;
|
||||
for (xcc_id = 0; xcc_id < num_xcc; xcc_id++) {
|
||||
xcc_offset = xcc_id * reg_count * num_inst;
|
||||
inst_offset = 0;
|
||||
for (i = 0; i < adev->gfx.mec.num_mec; i++) {
|
||||
for (j = 0; j < adev->gfx.mec.num_pipe_per_mec; j++) {
|
||||
for (k = 0; k < adev->gfx.mec.num_queue_per_pipe; k++) {
|
||||
drm_printf(p,
|
||||
"\nxcc:%d mec:%d, pipe:%d, queue:%d\n",
|
||||
xcc_id, i, j, k);
|
||||
for (reg = 0; reg < reg_count; reg++) {
|
||||
drm_printf(p,
|
||||
"%-50s \t 0x%08x\n",
|
||||
gc_cp_reg_list_12_1[reg].reg_name,
|
||||
adev->gfx.ip_dump_compute_queues
|
||||
[xcc_offset + inst_offset +
|
||||
reg]);
|
||||
}
|
||||
inst_offset += reg_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gfx_v12_1_ip_dump(struct amdgpu_ip_block *ip_block)
|
||||
{
|
||||
struct amdgpu_device *adev = ip_block->adev;
|
||||
uint32_t i, j, k;
|
||||
uint32_t num_xcc, reg, num_inst;
|
||||
uint32_t xcc_id, xcc_offset, inst_offset;
|
||||
uint32_t reg_count = ARRAY_SIZE(gc_reg_list_12_1);
|
||||
|
||||
if (!adev->gfx.ip_dump_core)
|
||||
return;
|
||||
|
||||
num_xcc = NUM_XCC(adev->gfx.xcc_mask);
|
||||
|
||||
amdgpu_gfx_off_ctrl(adev, false);
|
||||
for (xcc_id = 0; xcc_id < num_xcc; xcc_id++) {
|
||||
xcc_offset = xcc_id * reg_count;
|
||||
for (i = 0; i < reg_count; i++)
|
||||
adev->gfx.ip_dump_core[xcc_offset + i] =
|
||||
RREG32(SOC15_REG_ENTRY_OFFSET_INST(gc_reg_list_12_1[i],
|
||||
GET_INST(GC, xcc_id)));
|
||||
}
|
||||
amdgpu_gfx_off_ctrl(adev, true);
|
||||
|
||||
/* dump compute queue registers for all instances */
|
||||
if (!adev->gfx.ip_dump_compute_queues)
|
||||
return;
|
||||
|
||||
num_inst = adev->gfx.mec.num_mec * adev->gfx.mec.num_pipe_per_mec *
|
||||
adev->gfx.mec.num_queue_per_pipe;
|
||||
reg_count = ARRAY_SIZE(gc_cp_reg_list_12_1);
|
||||
amdgpu_gfx_off_ctrl(adev, false);
|
||||
mutex_lock(&adev->srbm_mutex);
|
||||
for (xcc_id = 0; xcc_id < num_xcc; xcc_id++) {
|
||||
xcc_offset = xcc_id * reg_count * num_inst;
|
||||
inst_offset = 0;
|
||||
for (i = 0; i < adev->gfx.mec.num_mec; i++) {
|
||||
for (j = 0; j < adev->gfx.mec.num_pipe_per_mec; j++) {
|
||||
for (k = 0; k < adev->gfx.mec.num_queue_per_pipe; k++) {
|
||||
/* ME0 is for GFX so start from 1 for CP */
|
||||
soc_v1_0_grbm_select(adev, 1 + i, j, k, 0,
|
||||
GET_INST(GC, xcc_id));
|
||||
|
||||
for (reg = 0; reg < reg_count; reg++) {
|
||||
adev->gfx.ip_dump_compute_queues
|
||||
[xcc_offset +
|
||||
inst_offset + reg] =
|
||||
RREG32(SOC15_REG_ENTRY_OFFSET_INST(
|
||||
gc_cp_reg_list_12_1[reg],
|
||||
GET_INST(GC, xcc_id)));
|
||||
}
|
||||
inst_offset += reg_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
soc_v1_0_grbm_select(adev, 0, 0, 0, 0, 0);
|
||||
mutex_unlock(&adev->srbm_mutex);
|
||||
amdgpu_gfx_off_ctrl(adev, true);
|
||||
}
|
||||
|
||||
static int gfx_v12_1_sw_init(struct amdgpu_ip_block *ip_block)
|
||||
{
|
||||
uint16_t major_ver, minor_ver;
|
||||
@@ -1287,6 +1557,8 @@ static int gfx_v12_1_sw_init(struct amdgpu_ip_block *ip_block)
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
gfx_v12_1_alloc_ip_dump(adev);
|
||||
|
||||
mutex_init(&adev->gfx.mec.reset_mutex);
|
||||
|
||||
return 0;
|
||||
@@ -1326,6 +1598,9 @@ static int gfx_v12_1_sw_fini(struct amdgpu_ip_block *ip_block)
|
||||
gfx_v12_1_free_microcode(adev);
|
||||
amdgpu_gfx_sysfs_fini(adev);
|
||||
|
||||
kfree(adev->gfx.ip_dump_core);
|
||||
kfree(adev->gfx.ip_dump_compute_queues);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3912,6 +4187,8 @@ static const struct amd_ip_funcs gfx_v12_1_ip_funcs = {
|
||||
.set_clockgating_state = gfx_v12_1_set_clockgating_state,
|
||||
.set_powergating_state = gfx_v12_1_set_powergating_state,
|
||||
.get_clockgating_state = gfx_v12_1_get_clockgating_state,
|
||||
.dump_ip_state = gfx_v12_1_ip_dump,
|
||||
.print_ip_state = gfx_v12_1_ip_print,
|
||||
};
|
||||
|
||||
static const struct amdgpu_ring_funcs gfx_v12_1_ring_funcs_compute = {
|
||||
|
||||
@@ -2442,12 +2442,12 @@ static int gfx_v9_0_sw_init(struct amdgpu_ip_block *ip_block)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gfx_v9_0_alloc_ip_dump(adev);
|
||||
|
||||
r = amdgpu_gfx_sysfs_init(adev);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
gfx_v9_0_alloc_ip_dump(adev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -772,7 +772,7 @@ static int jpeg_v5_0_1_set_ras_interrupt_state(struct amdgpu_device *adev,
|
||||
|
||||
|
||||
|
||||
static int jpeg_v5_0_1_process_interrupt(struct amdgpu_device *adev,
|
||||
int jpeg_v5_0_1_process_interrupt(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
struct amdgpu_iv_entry *entry)
|
||||
{
|
||||
|
||||
@@ -108,4 +108,12 @@ enum amdgpu_jpeg_v5_0_1_sub_block {
|
||||
AMDGPU_JPEG_V5_0_1_MAX_SUB_BLOCK,
|
||||
};
|
||||
|
||||
struct amdgpu_irq_src;
|
||||
struct amdgpu_iv_entry;
|
||||
struct amdgpu_device;
|
||||
|
||||
int jpeg_v5_0_1_process_interrupt(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
struct amdgpu_iv_entry *entry);
|
||||
|
||||
#endif /* __JPEG_V5_0_1_H__ */
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include "vcn/vcn_5_0_0_sh_mask.h"
|
||||
#include "ivsrcid/vcn/irqsrcs_vcn_5_0.h"
|
||||
|
||||
#include "jpeg_v5_0_1.h"
|
||||
|
||||
static void jpeg_v5_0_2_set_dec_ring_funcs(struct amdgpu_device *adev);
|
||||
static void jpeg_v5_0_2_set_irq_funcs(struct amdgpu_device *adev);
|
||||
static int jpeg_v5_0_2_set_powergating_state(struct amdgpu_ip_block *ip_block,
|
||||
@@ -583,65 +585,6 @@ static int jpeg_v5_0_2_set_interrupt_state(struct amdgpu_device *adev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jpeg_v5_0_2_process_interrupt(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
struct amdgpu_iv_entry *entry)
|
||||
{
|
||||
u32 i, inst;
|
||||
|
||||
i = node_id_to_phys_map[entry->node_id];
|
||||
DRM_DEV_DEBUG(adev->dev, "IH: JPEG TRAP\n");
|
||||
|
||||
for (inst = 0; inst < adev->jpeg.num_jpeg_inst; ++inst)
|
||||
if (adev->jpeg.inst[inst].aid_id == i)
|
||||
break;
|
||||
|
||||
if (inst >= adev->jpeg.num_jpeg_inst) {
|
||||
dev_WARN_ONCE(adev->dev, 1,
|
||||
"Interrupt received for unknown JPEG instance %d",
|
||||
entry->node_id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (entry->src_id) {
|
||||
case VCN_5_0__SRCID__JPEG_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[0]);
|
||||
break;
|
||||
case VCN_5_0__SRCID__JPEG1_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[1]);
|
||||
break;
|
||||
case VCN_5_0__SRCID__JPEG2_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[2]);
|
||||
break;
|
||||
case VCN_5_0__SRCID__JPEG3_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[3]);
|
||||
break;
|
||||
case VCN_5_0__SRCID__JPEG4_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[4]);
|
||||
break;
|
||||
case VCN_5_0__SRCID__JPEG5_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[5]);
|
||||
break;
|
||||
case VCN_5_0__SRCID__JPEG6_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[6]);
|
||||
break;
|
||||
case VCN_5_0__SRCID__JPEG7_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[7]);
|
||||
break;
|
||||
case VCN_5_0__SRCID__JPEG8_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[8]);
|
||||
break;
|
||||
case VCN_5_0__SRCID__JPEG9_DECODE:
|
||||
amdgpu_fence_process(&adev->jpeg.inst[inst].ring_dec[9]);
|
||||
break;
|
||||
default:
|
||||
DRM_DEV_ERROR(adev->dev, "Unhandled interrupt: %d %d\n",
|
||||
entry->src_id, entry->src_data[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void jpeg_v5_0_2_core_stall_reset(struct amdgpu_ring *ring)
|
||||
{
|
||||
@@ -747,7 +690,7 @@ static void jpeg_v5_0_2_set_dec_ring_funcs(struct amdgpu_device *adev)
|
||||
|
||||
static const struct amdgpu_irq_src_funcs jpeg_v5_0_2_irq_funcs = {
|
||||
.set = jpeg_v5_0_2_set_interrupt_state,
|
||||
.process = jpeg_v5_0_2_process_interrupt,
|
||||
.process = jpeg_v5_0_1_process_interrupt,
|
||||
};
|
||||
|
||||
static void jpeg_v5_0_2_set_irq_funcs(struct amdgpu_device *adev)
|
||||
|
||||
@@ -121,6 +121,7 @@ static int mes_userq_map(struct amdgpu_usermode_queue *queue)
|
||||
struct amdgpu_userq_obj *ctx = &queue->fw_obj;
|
||||
struct amdgpu_mqd_prop *userq_props = queue->userq_prop;
|
||||
struct mes_add_queue_input queue_input;
|
||||
struct amdgpu_mes *mes = &adev->mes;
|
||||
int r;
|
||||
|
||||
memset(&queue_input, 0x0, sizeof(struct mes_add_queue_input));
|
||||
@@ -146,7 +147,12 @@ static int mes_userq_map(struct amdgpu_usermode_queue *queue)
|
||||
queue_input.doorbell_offset = userq_props->doorbell_index;
|
||||
queue_input.page_table_base_addr = amdgpu_gmc_pd_addr(queue->vm->root.bo);
|
||||
queue_input.wptr_mc_addr = queue->wptr_obj.gpu_addr;
|
||||
|
||||
if (mes->use_rs64mem) {
|
||||
amdgpu_mes_alloc_proc_ctx_index(mes, queue);
|
||||
queue_input.process_context_array_index = queue->proc_ctx_array_index;
|
||||
amdgpu_mes_alloc_gang_ctx_index(mes, queue);
|
||||
queue_input.gang_context_array_index = queue->gang_ctx_array_index;
|
||||
}
|
||||
amdgpu_mes_lock(&adev->mes);
|
||||
r = adev->mes.funcs->add_hw_queue(&adev->mes, &queue_input);
|
||||
amdgpu_mes_unlock(&adev->mes);
|
||||
@@ -165,9 +171,11 @@ static int mes_userq_unmap(struct amdgpu_usermode_queue *queue)
|
||||
struct amdgpu_device *adev = uq_mgr->adev;
|
||||
struct mes_remove_queue_input queue_input;
|
||||
struct amdgpu_userq_obj *ctx = &queue->fw_obj;
|
||||
struct amdgpu_mes *mes = &adev->mes;
|
||||
int r;
|
||||
|
||||
memset(&queue_input, 0x0, sizeof(struct mes_remove_queue_input));
|
||||
queue_input.gang_context_array_index = queue->gang_ctx_array_index;
|
||||
queue_input.doorbell_offset = queue->doorbell_index;
|
||||
queue_input.gang_context_addr = ctx->gpu_addr;
|
||||
queue_input.queue_type = queue->queue_type;
|
||||
@@ -175,6 +183,10 @@ static int mes_userq_unmap(struct amdgpu_usermode_queue *queue)
|
||||
amdgpu_mes_lock(&adev->mes);
|
||||
r = adev->mes.funcs->remove_hw_queue(&adev->mes, &queue_input);
|
||||
amdgpu_mes_unlock(&adev->mes);
|
||||
if (mes->use_rs64mem) {
|
||||
amdgpu_mes_free_proc_ctx_index(mes, queue);
|
||||
amdgpu_mes_free_gang_ctx_index(mes, queue);
|
||||
}
|
||||
if (r)
|
||||
DRM_ERROR("Failed to unmap queue in HW, err (%d)\n", r);
|
||||
return r;
|
||||
|
||||
@@ -335,8 +335,10 @@ static int mes_v11_0_add_hw_queue(struct amdgpu_mes *mes,
|
||||
mes_add_queue_pkt.process_va_end = input->process_va_end;
|
||||
mes_add_queue_pkt.process_quantum = input->process_quantum;
|
||||
mes_add_queue_pkt.process_context_addr = input->process_context_addr;
|
||||
mes_add_queue_pkt.process_context_array_index = input->process_context_array_index;
|
||||
mes_add_queue_pkt.gang_quantum = input->gang_quantum;
|
||||
mes_add_queue_pkt.gang_context_addr = input->gang_context_addr;
|
||||
mes_add_queue_pkt.gang_context_array_index = input->gang_context_array_index;
|
||||
mes_add_queue_pkt.inprocess_gang_priority =
|
||||
convert_to_mes_priority_level(input->inprocess_gang_priority);
|
||||
mes_add_queue_pkt.gang_global_priority_level =
|
||||
@@ -387,6 +389,7 @@ static int mes_v11_0_remove_hw_queue(struct amdgpu_mes *mes,
|
||||
|
||||
mes_remove_queue_pkt.doorbell_offset = input->doorbell_offset;
|
||||
mes_remove_queue_pkt.gang_context_addr = input->gang_context_addr;
|
||||
mes_remove_queue_pkt.gang_context_array_index = input->gang_context_array_index;
|
||||
mes_remove_queue_pkt.queue_type =
|
||||
convert_to_mes_queue_type(input->queue_type);
|
||||
|
||||
@@ -769,6 +772,11 @@ static int mes_v11_0_unmap_legacy_queue(struct amdgpu_mes *mes,
|
||||
convert_to_mes_queue_type(input->queue_type);
|
||||
}
|
||||
|
||||
if (input->action == RESET_QUEUES &&
|
||||
(mes->sched_version & AMDGPU_MES_VERSION_MASK) >= 0x60)
|
||||
mes_remove_queue_pkt.remove_queue_after_reset = 1;
|
||||
|
||||
|
||||
return mes_v11_0_submit_pkt_and_poll_completion(mes,
|
||||
&mes_remove_queue_pkt, sizeof(mes_remove_queue_pkt),
|
||||
offsetof(union MESAPI__REMOVE_QUEUE, api_status));
|
||||
@@ -831,6 +839,58 @@ static int mes_v11_0_query_sched_status(struct amdgpu_mes *mes)
|
||||
offsetof(union MESAPI__QUERY_MES_STATUS, api_status));
|
||||
}
|
||||
|
||||
/*
|
||||
* QUERY_SCHEDULER_STATUS: get proc/gang context array sizes
|
||||
*/
|
||||
static int mes_v11_0_query_ctx_array_sizes(struct amdgpu_mes *mes)
|
||||
{
|
||||
struct amdgpu_device *adev = mes->adev;
|
||||
union MESAPI__QUERY_MES_STATUS mes_query_pkt;
|
||||
int r;
|
||||
|
||||
if (!mes->ctx_array_size_cpu_ptr)
|
||||
return -EINVAL;
|
||||
|
||||
/* Clear the output buffer */
|
||||
mes->ctx_array_size_cpu_ptr[0] = 0; /* proc_ctx_array_size */
|
||||
mes->ctx_array_size_cpu_ptr[1] = 0; /* gang_ctx_array_size */
|
||||
|
||||
memset(&mes_query_pkt, 0, sizeof(mes_query_pkt));
|
||||
|
||||
mes_query_pkt.header.type = MES_API_TYPE_SCHEDULER;
|
||||
mes_query_pkt.header.opcode = MES_SCH_API_QUERY_SCHEDULER_STATUS;
|
||||
mes_query_pkt.header.dwsize = API_FRAME_SIZE_IN_DWORDS;
|
||||
|
||||
mes_query_pkt.subopcode = MES_API_QUERY_MES__GET_CTX_ARRAY_SIZE;
|
||||
|
||||
/*
|
||||
* MES FW will write the array sizes to these GPU addresses:
|
||||
* proc_ctx_array_size_addr -> uint32_t N (e.g., 50)
|
||||
* gang_ctx_array_size_addr -> uint32_t M (e.g., 300)
|
||||
*/
|
||||
mes_query_pkt.ctx_array_size.proc_ctx_array_size_addr =
|
||||
mes->ctx_array_size_gpu_addr;
|
||||
mes_query_pkt.ctx_array_size.gang_ctx_array_size_addr =
|
||||
mes->ctx_array_size_gpu_addr + sizeof(uint32_t);
|
||||
|
||||
mes_query_pkt.api_status.api_completion_fence_addr =
|
||||
mes->ring[0].fence_drv.gpu_addr;
|
||||
mes_query_pkt.api_status.api_completion_fence_value =
|
||||
++mes->ring[0].fence_drv.sync_seq;
|
||||
|
||||
r = mes_v11_0_submit_pkt_and_poll_completion(mes,
|
||||
&mes_query_pkt, sizeof(mes_query_pkt),
|
||||
offsetof(union MESAPI__QUERY_MES_STATUS, api_status));
|
||||
if (r) {
|
||||
dev_err(adev->dev,
|
||||
"MES QUERY_SCHEDULER_STATUS (GET_CTX_ARRAY_SIZE) failed, r=%d\n", r);
|
||||
return r;
|
||||
}
|
||||
|
||||
/* MES has written the sizes - now set up bitmaps */
|
||||
return amdgpu_mes_rs64mem_setup_bitmaps(mes);
|
||||
}
|
||||
|
||||
static int mes_v11_0_misc_op(struct amdgpu_mes *mes,
|
||||
struct mes_misc_op_input *input)
|
||||
{
|
||||
@@ -1904,10 +1964,33 @@ static int mes_v11_0_hw_init(struct amdgpu_ip_block *ip_block)
|
||||
if (r)
|
||||
goto failure;
|
||||
|
||||
/* Allocate GPU buffer for array size query results */
|
||||
r = amdgpu_mes_rs64mem_init(&adev->mes);
|
||||
if (r)
|
||||
dev_warn(adev->dev,
|
||||
"RS64 local memory init failed (%d),"
|
||||
"falling back to system memory path\n", r);
|
||||
|
||||
r = mes_v11_0_set_hw_resources(&adev->mes);
|
||||
|
||||
if (r)
|
||||
goto failure;
|
||||
|
||||
/*
|
||||
* QUERY_SCHEDULER_STATUS to get array sizes (N, M).
|
||||
* MES writes the sizes to the GPU buffer, then we allocate bitmaps.
|
||||
*/
|
||||
if (adev->mes.use_rs64mem) {
|
||||
r = mes_v11_0_query_ctx_array_sizes(&adev->mes);
|
||||
if (r) {
|
||||
dev_warn(adev->dev,
|
||||
"Failed to query ctx array sizes (%d),"
|
||||
"disabling RS64 local memory\n", r);
|
||||
/* Continue without optimization - not fatal */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ((adev->mes.sched_version & AMDGPU_MES_VERSION_MASK) >= 0x52) {
|
||||
r = mes_v11_0_set_hw_resources_1(&adev->mes);
|
||||
if (r) {
|
||||
@@ -1945,6 +2028,10 @@ static int mes_v11_0_hw_init(struct amdgpu_ip_block *ip_block)
|
||||
|
||||
static int mes_v11_0_hw_fini(struct amdgpu_ip_block *ip_block)
|
||||
{
|
||||
struct amdgpu_device *adev = ip_block->adev;
|
||||
|
||||
if (adev->mes.use_rs64mem)
|
||||
amdgpu_mes_rs64mem_fini(&adev->mes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,40 +36,41 @@
|
||||
|
||||
static const char *mmhub_client_ids_v4_2_0[][2] = {
|
||||
[0][0] = "VMC",
|
||||
[4][0] = "DCEDMC",
|
||||
[5][0] = "DCEVGA",
|
||||
[6][0] = "MP0",
|
||||
[7][0] = "MP1",
|
||||
[2][0] = "MPNHT",
|
||||
[7][0] = "MPIFOE",
|
||||
[8][0] = "MPIO",
|
||||
[16][0] = "HDP",
|
||||
[17][0] = "LSDMA",
|
||||
[18][0] = "JPEG",
|
||||
[19][0] = "VCNU0",
|
||||
[21][0] = "VSCH",
|
||||
[22][0] = "VCNU1",
|
||||
[23][0] = "VCN1",
|
||||
[32+20][0] = "VCN0",
|
||||
[2][1] = "DBGUNBIO",
|
||||
[3][1] = "DCEDWB",
|
||||
[4][1] = "DCEDMC",
|
||||
[5][1] = "DCEVGA",
|
||||
[6][1] = "MP0",
|
||||
[7][1] = "MP1",
|
||||
[11][0] = "JPEG0",
|
||||
[12][0] = "VCN0",
|
||||
[13][0] = "VCNU0",
|
||||
[14][0] = "VSCH0",
|
||||
[15][0] = "LSDMA",
|
||||
[32+5][0] = "MPRAS",
|
||||
[32+6][0] = "MP1",
|
||||
[32+7][0] = "MP0",
|
||||
[32+11][0] = "JPEG1",
|
||||
[32+12][0] = "VCN1",
|
||||
[32+13][0] = "VCNU1",
|
||||
[32+14][0] = "VSCH1",
|
||||
[2][1] = "MPNHT",
|
||||
[3][1] = "DBGU0",
|
||||
[7][1] = "MPIFOE",
|
||||
[8][1] = "MPIO",
|
||||
[10][1] = "DBGU0",
|
||||
[11][1] = "DBGU1",
|
||||
[12][1] = "DBGU2",
|
||||
[13][1] = "DBGU3",
|
||||
[14][1] = "XDP",
|
||||
[15][1] = "OSSSYS",
|
||||
[16][1] = "HDP",
|
||||
[17][1] = "LSDMA",
|
||||
[18][1] = "JPEG",
|
||||
[19][1] = "VCNU0",
|
||||
[20][1] = "VCN0",
|
||||
[21][1] = "VSCH",
|
||||
[22][1] = "VCNU1",
|
||||
[23][1] = "VCN1",
|
||||
[10][1] = "UTCL2_NHT",
|
||||
[11][1] = "JPEG0",
|
||||
[12][1] = "VCN0",
|
||||
[13][1] = "VCNU0",
|
||||
[14][1] = "VSCH0",
|
||||
[15][1] = "LSDMA",
|
||||
[32+3][1] = "DBGU1",
|
||||
[32+4][1] = "DBGU2",
|
||||
[32+5][1] = "MPRAS",
|
||||
[32+6][1] = "MP1",
|
||||
[32+7][1] = "MP0",
|
||||
[32+8][1] = "IH",
|
||||
[32+11][1] = "JPEG1",
|
||||
[32+12][1] = "VCN1",
|
||||
[32+13][1] = "VCNU1",
|
||||
[32+14][1] = "VSCH1",
|
||||
};
|
||||
|
||||
static int mmhub_v4_2_0_get_xgmi_info(struct amdgpu_device *adev)
|
||||
|
||||
@@ -211,10 +211,6 @@ int kfd_debugfs_add_process(struct kfd_process *p)
|
||||
entry->proc_dentry = debugfs_create_dir(name,
|
||||
primary_entry->proc_dentry);
|
||||
}
|
||||
if (IS_ERR_OR_NULL(entry->proc_dentry)) {
|
||||
ret = entry->proc_dentry ? PTR_ERR(entry->proc_dentry) : -ENOMEM;
|
||||
goto err_free_entry;
|
||||
}
|
||||
|
||||
list_add(&entry->list, &procs);
|
||||
kfd_debugfs_create_pasid_files(p, entry->proc_dentry);
|
||||
|
||||
@@ -3071,6 +3071,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");
|
||||
|
||||
@@ -3199,6 +3200,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);
|
||||
|
||||
@@ -779,6 +779,14 @@ static int amdgpu_dm_init(struct amdgpu_device *adev)
|
||||
|
||||
dc_hardware_init(adev->dm.dc);
|
||||
|
||||
/* GOP/vBIOS may leave an OPTC enabled for a display present at power-on
|
||||
* but no longer driven (e.g. an external DP unplugged at boot). Such a
|
||||
* dangling pipe keeps DCN out of idle and blocks s0i3. Power it down
|
||||
* here when nothing needs to be preserved.
|
||||
*/
|
||||
if (adev->flags & AMD_IS_APU)
|
||||
dc_disable_dangling_timing_generators(adev->dm.dc);
|
||||
|
||||
adev->dm.hpd_rx_offload_wq = amdgpu_dm_hpd_rx_irq_create_workqueue(adev);
|
||||
if (!adev->dm.hpd_rx_offload_wq) {
|
||||
drm_err(adev_to_drm(adev), "failed to create hpd rx offload workqueue.\n");
|
||||
@@ -1010,14 +1018,11 @@ static void amdgpu_dm_fini(struct amdgpu_device *adev)
|
||||
adev->dm.hpd_rx_offload_wq = NULL;
|
||||
}
|
||||
|
||||
amdgpu_dm_irq_fini(adev);
|
||||
|
||||
/* DC Destroy TODO: Replace destroy DAL */
|
||||
if (adev->dm.dc)
|
||||
dc_destroy(&adev->dm.dc);
|
||||
/*
|
||||
* TODO: pageflip, vlank interrupt
|
||||
*
|
||||
* amdgpu_dm_irq_fini(adev);
|
||||
*/
|
||||
|
||||
if (adev->dm.cgs_device) {
|
||||
amdgpu_cgs_destroy_device(adev->dm.cgs_device);
|
||||
@@ -1496,17 +1501,26 @@ static int dm_hw_init(struct amdgpu_ip_block *ip_block)
|
||||
struct amdgpu_device *adev = ip_block->adev;
|
||||
int r;
|
||||
|
||||
adev->dm.i2c_devres_group = devres_open_group(adev->dev, NULL, GFP_KERNEL);
|
||||
if (!adev->dm.i2c_devres_group)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create DAL display manager */
|
||||
r = amdgpu_dm_init(adev);
|
||||
if (r)
|
||||
return r;
|
||||
goto err_release_i2c;
|
||||
amdgpu_dm_hpd_init(adev);
|
||||
|
||||
r = dm_oem_i2c_hw_init(adev);
|
||||
if (r)
|
||||
drm_info(adev_to_drm(adev), "Failed to add OEM i2c bus\n");
|
||||
|
||||
devres_close_group(adev->dev, adev->dm.i2c_devres_group);
|
||||
return 0;
|
||||
|
||||
err_release_i2c:
|
||||
devres_release_group(adev->dev, adev->dm.i2c_devres_group);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1521,9 +1535,11 @@ static int dm_hw_fini(struct amdgpu_ip_block *ip_block)
|
||||
{
|
||||
struct amdgpu_device *adev = ip_block->adev;
|
||||
|
||||
if (adev->dm.i2c_devres_group)
|
||||
devres_release_group(adev->dev, adev->dm.i2c_devres_group);
|
||||
|
||||
amdgpu_dm_hpd_fini(adev);
|
||||
|
||||
amdgpu_dm_irq_fini(adev);
|
||||
amdgpu_dm_fini(adev);
|
||||
return 0;
|
||||
}
|
||||
@@ -1541,7 +1557,8 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev,
|
||||
acrtc = amdgpu_dm_get_crtc_by_otg_inst(
|
||||
adev, state->stream_status[i].primary_otg_inst);
|
||||
|
||||
if (acrtc && state->stream_status[i].plane_count != 0) {
|
||||
if (acrtc && state->stream_status[i].plane_count != 0 &&
|
||||
amdgpu_ip_version(adev, DCE_HWIP, 0) == 0) {
|
||||
irq_source = IRQ_TYPE_PFLIP + acrtc->otg_inst;
|
||||
rc = dc_interrupt_set(adev->dm.dc, irq_source, enable) ? 0 : -EBUSY;
|
||||
if (rc)
|
||||
@@ -1569,6 +1586,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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3522,21 +3546,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.
|
||||
@@ -3555,14 +3567,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");
|
||||
@@ -3580,8 +3600,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);
|
||||
@@ -3594,6 +3614,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.
|
||||
@@ -3927,9 +3951,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 = dm->adev;
|
||||
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
|
||||
@@ -3939,7 +3967,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);
|
||||
@@ -3955,7 +3984,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);
|
||||
@@ -4104,6 +4134,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,
|
||||
@@ -4126,6 +4178,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 {
|
||||
@@ -4288,6 +4342,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];
|
||||
@@ -4356,39 +4412,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. */
|
||||
@@ -4480,6 +4521,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);
|
||||
}
|
||||
@@ -5896,6 +6046,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
|
||||
@@ -6610,10 +6761,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;
|
||||
}
|
||||
@@ -6649,6 +6805,31 @@ static int dm_crtc_get_cursor_mode(struct amdgpu_device *adev,
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* A non-cursor plane moving or resizing (without a scale change)
|
||||
* changes how much of the CRTC it covers. This can create or
|
||||
* remove a hole under the cursor and thus flip the required
|
||||
* cursor mode (native vs overlay), so its destination rect must
|
||||
* be re-evaluated too.
|
||||
*
|
||||
* The cursor plane itself is deliberately excluded: the cursor
|
||||
* mode depends on the underlying planes' coverage, not on the
|
||||
* cursor's position (see the entire_crtc_covered logic below).
|
||||
* Triggering on cursor movement would force every legacy cursor
|
||||
* update off its fast path, and in a cursor-only commit - where
|
||||
* the underlying planes are not part of the state - the coverage
|
||||
* loop would see no covering plane and misevaluate the mode as
|
||||
* overlay, regressing flip-vs-cursor-legacy.
|
||||
*/
|
||||
if (plane->type != DRM_PLANE_TYPE_CURSOR &&
|
||||
(old_plane_state->crtc_x != plane_state->crtc_x ||
|
||||
old_plane_state->crtc_y != plane_state->crtc_y ||
|
||||
old_plane_state->crtc_w != plane_state->crtc_w ||
|
||||
old_plane_state->crtc_h != plane_state->crtc_h)) {
|
||||
consider_mode_change = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (dm_plane_color_pipeline_active(state, plane, true) !=
|
||||
dm_plane_color_pipeline_active(state, plane, false)) {
|
||||
consider_mode_change = true;
|
||||
@@ -6804,7 +6985,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
|
||||
ret = drm_atomic_helper_check_modeset(dev, state);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "drm_atomic_helper_check_modeset() failed\n");
|
||||
drm_dbg_atomic(dev, "drm_atomic_helper_check_modeset() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -6819,7 +7000,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
|
||||
new_crtc_state = drm_atomic_get_crtc_state(state, new_con_state->crtc);
|
||||
if (IS_ERR(new_crtc_state)) {
|
||||
drm_dbg_atomic(dev, "drm_atomic_get_crtc_state() failed\n");
|
||||
drm_dbg_atomic(dev, "drm_atomic_get_crtc_state() failed: %pe\n", new_crtc_state);
|
||||
ret = PTR_ERR(new_crtc_state);
|
||||
goto fail;
|
||||
}
|
||||
@@ -6839,7 +7020,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
if (drm_atomic_crtc_needs_modeset(new_crtc_state)) {
|
||||
ret = add_affected_mst_dsc_crtcs(state, crtc);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "add_affected_mst_dsc_crtcs() failed\n");
|
||||
drm_dbg_atomic(dev, "add_affected_mst_dsc_crtcs() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -6856,7 +7037,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
|
||||
ret = amdgpu_dm_verify_lut_sizes(new_crtc_state);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "amdgpu_dm_verify_lut_sizes() failed\n");
|
||||
drm_dbg_atomic(dev, "amdgpu_dm_verify_lut_sizes() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -6865,13 +7046,13 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
|
||||
ret = drm_atomic_add_affected_connectors(state, crtc);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "drm_atomic_add_affected_connectors() failed\n");
|
||||
drm_dbg_atomic(dev, "drm_atomic_add_affected_connectors() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = drm_atomic_add_affected_planes(state, crtc);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "drm_atomic_add_affected_planes() failed\n");
|
||||
drm_dbg_atomic(dev, "drm_atomic_add_affected_planes() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -6910,7 +7091,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
|
||||
if (IS_ERR(new_plane_state)) {
|
||||
ret = PTR_ERR(new_plane_state);
|
||||
drm_dbg_atomic(dev, "new_plane_state is BAD\n");
|
||||
drm_dbg_atomic(dev, "new_plane_state is BAD: %pe\n", new_plane_state);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -6924,7 +7105,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
*/
|
||||
ret = drm_atomic_normalize_zpos(dev, state);
|
||||
if (ret) {
|
||||
drm_dbg(dev, "drm_atomic_normalize_zpos() failed\n");
|
||||
drm_dbg(dev, "drm_atomic_normalize_zpos() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -6938,7 +7119,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
ret = dm_crtc_get_cursor_mode(adev, state, dm_new_crtc_state,
|
||||
&dm_new_crtc_state->cursor_mode);
|
||||
if (ret) {
|
||||
drm_dbg(dev, "Failed to determine cursor mode\n");
|
||||
drm_dbg(dev, "Failed to determine cursor mode: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -6970,7 +7151,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
&lock_and_validation_needed,
|
||||
&is_top_most_overlay);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
|
||||
drm_dbg_atomic(dev, "dm_update_plane_state() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -6983,7 +7164,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
false,
|
||||
&lock_and_validation_needed);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "DISABLE: dm_update_crtc_state() failed\n");
|
||||
drm_dbg_atomic(dev, "DISABLE: dm_update_crtc_state() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -6996,7 +7177,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
true,
|
||||
&lock_and_validation_needed);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "ENABLE: dm_update_crtc_state() failed\n");
|
||||
drm_dbg_atomic(dev, "ENABLE: dm_update_crtc_state() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -7010,7 +7191,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
&lock_and_validation_needed,
|
||||
&is_top_most_overlay);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "dm_update_plane_state() failed\n");
|
||||
drm_dbg_atomic(dev, "dm_update_plane_state() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
@@ -7026,7 +7207,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
/* Run this here since we want to validate the streams we created */
|
||||
ret = drm_atomic_helper_check_planes(dev, state);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "drm_atomic_helper_check_planes() failed\n");
|
||||
drm_dbg_atomic(dev, "drm_atomic_helper_check_planes() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -7164,13 +7345,13 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
if (lock_and_validation_needed) {
|
||||
ret = dm_atomic_get_state(state, &dm_state);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "dm_atomic_get_state() failed\n");
|
||||
drm_dbg_atomic(dev, "dm_atomic_get_state() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = do_aquire_global_lock(dev, state);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "do_aquire_global_lock() failed\n");
|
||||
drm_dbg_atomic(dev, "do_aquire_global_lock() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -7178,7 +7359,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
if (dc_resource_is_dsc_encoding_supported(dc)) {
|
||||
ret = compute_mst_dsc_configs_for_state(state, dm_state->context, vars);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "MST_DSC compute_mst_dsc_configs_for_state() failed\n");
|
||||
drm_dbg_atomic(dev, "MST_DSC compute_mst_dsc_configs_for_state() failed: %pe\n", ERR_PTR(ret));
|
||||
ret = -EINVAL;
|
||||
goto fail;
|
||||
}
|
||||
@@ -7187,7 +7368,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
|
||||
ret = dm_update_mst_vcpi_slots_for_dsc(state, dm_state->context, vars);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "dm_update_mst_vcpi_slots_for_dsc() failed\n");
|
||||
drm_dbg_atomic(dev, "dm_update_mst_vcpi_slots_for_dsc() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@@ -7199,7 +7380,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
*/
|
||||
ret = drm_dp_mst_atomic_check(state);
|
||||
if (ret) {
|
||||
drm_dbg_atomic(dev, "MST drm_dp_mst_atomic_check() failed\n");
|
||||
drm_dbg_atomic(dev, "MST drm_dp_mst_atomic_check() failed: %pe\n", ERR_PTR(ret));
|
||||
goto fail;
|
||||
}
|
||||
status = dc_validate_global_state(dc, dm_state->context, DC_VALIDATE_MODE_ONLY);
|
||||
@@ -7288,7 +7469,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
|
||||
else if (ret == -EINTR || ret == -EAGAIN || ret == -ERESTARTSYS)
|
||||
drm_dbg_atomic(dev, "Atomic check stopped due to signal.\n");
|
||||
else
|
||||
drm_dbg_atomic(dev, "Atomic check failed with err: %d\n", ret);
|
||||
drm_dbg_atomic(dev, "Atomic check failed: %pe\n", ERR_PTR(ret));
|
||||
|
||||
trace_amdgpu_dm_atomic_check_finish(state, ret);
|
||||
|
||||
|
||||
@@ -324,6 +324,8 @@ struct hpd_rx_irq_offload_work {
|
||||
* @ddev: DRM base driver structure
|
||||
* @display_indexes_num: Max number of display streams supported
|
||||
* @irq_handler_list_table_lock: Synchronizes access to IRQ tables
|
||||
* @irq_wq: Dedicated high-priority unbound workqueue for deferred IRQ work
|
||||
* @vmin_vmax_wq: Dedicated unbound workqueue for deferred vmin/vmax updates
|
||||
* @backlight_dev: Backlight control device
|
||||
* @backlight_link: Link on which to control backlight
|
||||
* @backlight_caps: Capabilities of the backlight device
|
||||
@@ -563,6 +565,8 @@ struct amdgpu_display_manager {
|
||||
dmub_outbox_params[1];
|
||||
|
||||
spinlock_t irq_handler_list_table_lock;
|
||||
struct workqueue_struct *irq_wq;
|
||||
struct workqueue_struct *vmin_vmax_wq;
|
||||
|
||||
struct backlight_device *backlight_dev[AMDGPU_DM_MAX_NUM_EDP];
|
||||
|
||||
@@ -688,6 +692,13 @@ struct amdgpu_display_manager {
|
||||
*/
|
||||
void *bb_from_dmub;
|
||||
|
||||
/**
|
||||
* @i2c_devres_group:
|
||||
*
|
||||
* Devres group for DM i2c adapter lifetime management.
|
||||
*/
|
||||
void *i2c_devres_group;
|
||||
|
||||
/**
|
||||
* @oem_i2c:
|
||||
*
|
||||
|
||||
@@ -116,8 +116,31 @@ static const struct component_ops amdgpu_dm_audio_component_bind_ops = {
|
||||
.unbind = amdgpu_dm_audio_component_unbind,
|
||||
};
|
||||
|
||||
STATIC_IFN_KUNIT
|
||||
void amdgpu_dm_audio_init_pins(struct amdgpu_device *adev, int audio_count,
|
||||
const unsigned int *inst_array)
|
||||
{
|
||||
int i;
|
||||
|
||||
adev->mode_info.audio.num_pins = audio_count;
|
||||
|
||||
for (i = 0; i < audio_count; i++) {
|
||||
adev->mode_info.audio.pin[i].channels = -1;
|
||||
adev->mode_info.audio.pin[i].rate = -1;
|
||||
adev->mode_info.audio.pin[i].bits_per_sample = -1;
|
||||
adev->mode_info.audio.pin[i].status_bits = 0;
|
||||
adev->mode_info.audio.pin[i].category_code = 0;
|
||||
adev->mode_info.audio.pin[i].connected = false;
|
||||
adev->mode_info.audio.pin[i].id = inst_array[i];
|
||||
adev->mode_info.audio.pin[i].offset = 0;
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_audio_init_pins);
|
||||
|
||||
int amdgpu_dm_audio_init(struct amdgpu_device *adev)
|
||||
{
|
||||
unsigned int inst_array[MAX_AUDIOS];
|
||||
int audio_count;
|
||||
int i, ret;
|
||||
|
||||
if (!amdgpu_audio)
|
||||
@@ -125,19 +148,10 @@ int amdgpu_dm_audio_init(struct amdgpu_device *adev)
|
||||
|
||||
adev->mode_info.audio.enabled = true;
|
||||
|
||||
adev->mode_info.audio.num_pins = adev->dm.dc->res_pool->audio_count;
|
||||
|
||||
for (i = 0; i < adev->mode_info.audio.num_pins; i++) {
|
||||
adev->mode_info.audio.pin[i].channels = -1;
|
||||
adev->mode_info.audio.pin[i].rate = -1;
|
||||
adev->mode_info.audio.pin[i].bits_per_sample = -1;
|
||||
adev->mode_info.audio.pin[i].status_bits = 0;
|
||||
adev->mode_info.audio.pin[i].category_code = 0;
|
||||
adev->mode_info.audio.pin[i].connected = false;
|
||||
adev->mode_info.audio.pin[i].id =
|
||||
adev->dm.dc->res_pool->audios[i]->inst;
|
||||
adev->mode_info.audio.pin[i].offset = 0;
|
||||
}
|
||||
audio_count = adev->dm.dc->res_pool->audio_count;
|
||||
for (i = 0; i < audio_count; i++)
|
||||
inst_array[i] = adev->dm.dc->res_pool->audios[i]->inst;
|
||||
amdgpu_dm_audio_init_pins(adev, audio_count, inst_array);
|
||||
|
||||
ret = component_add(adev->dev, &amdgpu_dm_audio_component_bind_ops);
|
||||
if (ret < 0)
|
||||
@@ -168,7 +182,7 @@ void amdgpu_dm_audio_fini(struct amdgpu_device *adev)
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_audio_fini);
|
||||
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin)
|
||||
void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin)
|
||||
{
|
||||
struct drm_audio_component *acomp = adev->dm.audio_component;
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ void amdgpu_dm_commit_audio(struct drm_device *dev,
|
||||
void amdgpu_dm_fill_audio_info(struct audio_info *audio_info,
|
||||
const struct drm_connector *drm_connector,
|
||||
const struct dc_sink *dc_sink);
|
||||
void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
|
||||
struct device;
|
||||
@@ -48,9 +49,10 @@ int amdgpu_dm_audio_component_bind(struct device *kdev,
|
||||
struct device *hda_kdev, void *data);
|
||||
void amdgpu_dm_audio_component_unbind(struct device *kdev,
|
||||
struct device *hda_kdev, void *data);
|
||||
void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin);
|
||||
int amdgpu_dm_audio_get_param(void);
|
||||
void amdgpu_dm_audio_set_param(int val);
|
||||
void amdgpu_dm_audio_init_pins(struct amdgpu_device *adev, int audio_count,
|
||||
const unsigned int *inst_array);
|
||||
#endif
|
||||
|
||||
#endif /* __AMDGPU_DM_AUDIO_H__ */
|
||||
|
||||
@@ -219,7 +219,8 @@ u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps,
|
||||
}
|
||||
EXPORT_IF_KUNIT(convert_brightness_to_user);
|
||||
|
||||
static struct dc_stream_state *dm_find_stream_with_link(
|
||||
STATIC_IFN_KUNIT
|
||||
struct dc_stream_state *dm_find_stream_with_link(
|
||||
struct amdgpu_display_manager *dm,
|
||||
struct dc_link *link)
|
||||
{
|
||||
@@ -235,6 +236,7 @@ static struct dc_stream_state *dm_find_stream_with_link(
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_find_stream_with_link);
|
||||
|
||||
STATIC_IFN_KUNIT
|
||||
int amdgpu_dm_backlight_get_device_index(struct amdgpu_display_manager *dm,
|
||||
@@ -346,8 +348,10 @@ void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm,
|
||||
if (rc)
|
||||
dm->actual_brightness[bl_idx] = user_brightness;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_backlight_set_level);
|
||||
|
||||
static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
|
||||
STATIC_IFN_KUNIT
|
||||
int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = bl_get_data(bd);
|
||||
int i = amdgpu_dm_backlight_get_device_index(dm, bd);
|
||||
@@ -356,9 +360,10 @@ static int amdgpu_dm_backlight_update_status(struct backlight_device *bd)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_backlight_update_status);
|
||||
|
||||
static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm,
|
||||
int bl_idx)
|
||||
STATIC_IFN_KUNIT
|
||||
u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm, int bl_idx)
|
||||
{
|
||||
int ret;
|
||||
struct amdgpu_dm_backlight_caps caps;
|
||||
@@ -382,14 +387,17 @@ static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm,
|
||||
|
||||
return convert_brightness_to_user(&caps, ret);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_level);
|
||||
|
||||
static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
|
||||
STATIC_IFN_KUNIT
|
||||
int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = bl_get_data(bd);
|
||||
int i = amdgpu_dm_backlight_get_device_index(dm, bd);
|
||||
|
||||
return amdgpu_dm_backlight_get_level(dm, i);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_brightness);
|
||||
|
||||
static const struct backlight_ops amdgpu_dm_backlight_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
@@ -407,12 +415,12 @@ void amdgpu_dm_backlight_fill_props(const struct amdgpu_dm_backlight_caps *caps,
|
||||
|
||||
if (get_brightness_range(caps, &min, &max)) {
|
||||
if (is_system_supplied)
|
||||
props->brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level,
|
||||
props->brightness = DIV_ROUND_CLOSEST(max * caps->ac_level,
|
||||
100);
|
||||
else
|
||||
props->brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level,
|
||||
props->brightness = DIV_ROUND_CLOSEST(max * caps->dc_level,
|
||||
100);
|
||||
props->max_brightness = max - min;
|
||||
props->max_brightness = max;
|
||||
} else {
|
||||
props->brightness = MAX_BACKLIGHT_LEVEL;
|
||||
props->max_brightness = MAX_BACKLIGHT_LEVEL;
|
||||
@@ -486,6 +494,7 @@ amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector)
|
||||
drm_dbg_driver(drm, "DM: Registered Backlight device: %s\n", bl_name);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_register_backlight_device);
|
||||
|
||||
void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
@@ -508,6 +517,8 @@ void amdgpu_dm_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
|
||||
/*
|
||||
* ||
|
||||
@@ -520,6 +531,9 @@ void amdgpu_dm_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;
|
||||
|
||||
@@ -535,8 +549,6 @@ void amdgpu_dm_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 =
|
||||
@@ -559,7 +571,6 @@ EXPORT_IF_KUNIT(amdgpu_dm_update_connector_ext_caps);
|
||||
void amdgpu_dm_setup_backlight_device(struct amdgpu_display_manager *dm,
|
||||
struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
struct amdgpu_dm_backlight_caps *caps;
|
||||
struct dc_link *link = aconnector->dc_link;
|
||||
int bl_idx = dm->num_of_edps;
|
||||
|
||||
@@ -579,10 +590,12 @@ void amdgpu_dm_setup_backlight_device(struct amdgpu_display_manager *dm,
|
||||
dm->num_of_edps++;
|
||||
|
||||
amdgpu_dm_update_connector_ext_caps(aconnector);
|
||||
caps = &dm->backlight_caps[aconnector->bl_idx];
|
||||
|
||||
/* Only offer ABM property when non-OLED and user didn't turn off by module parameter */
|
||||
if (caps->ext_caps && !caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0)
|
||||
/* Offer ABM property when user didn't turn off by module parameter.
|
||||
* OLED panels are included to support CACP (Content Adaptive
|
||||
* Contrast and Power) feature via set_abm_level.
|
||||
*/
|
||||
if (amdgpu_dm_abm_level < 0)
|
||||
drm_object_attach_property(&aconnector->base.base,
|
||||
dm->adev->mode_info.abm_level_property,
|
||||
ABM_SYSFS_CONTROL);
|
||||
@@ -601,9 +614,10 @@ EXPORT_IF_KUNIT(amdgpu_dm_setup_backlight_device);
|
||||
* carefully.
|
||||
*/
|
||||
|
||||
static ssize_t panel_power_savings_show(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
STATIC_IFN_KUNIT
|
||||
ssize_t panel_power_savings_show(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct drm_connector *connector = dev_get_drvdata(device);
|
||||
struct drm_device *dev = connector->dev;
|
||||
@@ -617,10 +631,12 @@ static ssize_t panel_power_savings_show(struct device *device,
|
||||
|
||||
return sysfs_emit(buf, "%u\n", val);
|
||||
}
|
||||
EXPORT_IF_KUNIT(panel_power_savings_show);
|
||||
|
||||
static ssize_t panel_power_savings_store(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
STATIC_IFN_KUNIT
|
||||
ssize_t panel_power_savings_store(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct drm_connector *connector = dev_get_drvdata(device);
|
||||
struct drm_device *dev = connector->dev;
|
||||
@@ -650,6 +666,7 @@ static ssize_t panel_power_savings_store(struct device *device,
|
||||
|
||||
return count;
|
||||
}
|
||||
EXPORT_IF_KUNIT(panel_power_savings_store);
|
||||
|
||||
static DEVICE_ATTR_RW(panel_power_savings);
|
||||
|
||||
@@ -666,22 +683,16 @@ const struct attribute_group amdgpu_group = {
|
||||
bool
|
||||
amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector)
|
||||
{
|
||||
struct dc_link *link = amdgpu_dm_connector->dc_link;
|
||||
|
||||
if (amdgpu_dm_abm_level >= 0)
|
||||
return false;
|
||||
|
||||
if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP)
|
||||
return false;
|
||||
|
||||
/* check for OLED panels */
|
||||
if (amdgpu_dm_connector->bl_idx >= 0) {
|
||||
struct drm_device *drm = amdgpu_dm_connector->base.dev;
|
||||
struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm;
|
||||
struct amdgpu_dm_backlight_caps *caps;
|
||||
|
||||
caps = &dm->backlight_caps[amdgpu_dm_connector->bl_idx];
|
||||
if (caps->aux_support)
|
||||
return false;
|
||||
}
|
||||
if (link->panel_type != PANEL_TYPE_LCD && !link->panel_config.cacp.cacp_supported)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ struct amdgpu_display_manager;
|
||||
struct amdgpu_dm_connector;
|
||||
struct backlight_device;
|
||||
struct backlight_properties;
|
||||
struct dc_link;
|
||||
struct dc_stream_state;
|
||||
struct device;
|
||||
struct device_attribute;
|
||||
struct drm_connector;
|
||||
struct attribute_group;
|
||||
|
||||
@@ -49,6 +53,17 @@ bool amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *aconnector);
|
||||
extern const struct attribute_group amdgpu_group;
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
|
||||
struct dc_stream_state *dm_find_stream_with_link(struct amdgpu_display_manager *dm,
|
||||
struct dc_link *link);
|
||||
int amdgpu_dm_backlight_update_status(struct backlight_device *bd);
|
||||
u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm, int bl_idx);
|
||||
int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd);
|
||||
ssize_t panel_power_savings_show(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
char *buf);
|
||||
ssize_t panel_power_savings_store(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps,
|
||||
unsigned int *min, unsigned int *max);
|
||||
void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps,
|
||||
|
||||
@@ -235,7 +235,7 @@ STATIC_IFN_KUNIT enum drm_mode_subconnector get_subconnector_type(struct dc_link
|
||||
}
|
||||
EXPORT_IF_KUNIT(get_subconnector_type);
|
||||
|
||||
static void update_subconnector_property(struct amdgpu_dm_connector *aconnector)
|
||||
STATIC_IFN_KUNIT void update_subconnector_property(struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
struct dc_link *link = aconnector->dc_link;
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
@@ -251,10 +251,11 @@ static void update_subconnector_property(struct amdgpu_dm_connector *aconnector)
|
||||
connector->dev->mode_config.dp_subconnector_property,
|
||||
subconnector);
|
||||
}
|
||||
EXPORT_IF_KUNIT(update_subconnector_property);
|
||||
|
||||
static int amdgpu_dm_connector_get_modes(struct drm_connector *connector);
|
||||
|
||||
static void amdgpu_dm_fbc_init(struct drm_connector *connector)
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_fbc_init(struct drm_connector *connector)
|
||||
{
|
||||
struct amdgpu_device *adev = drm_to_adev(connector->dev);
|
||||
struct dm_compressor_info *compressor = &adev->dm.compressor;
|
||||
@@ -292,6 +293,7 @@ static void amdgpu_dm_fbc_init(struct drm_connector *connector)
|
||||
}
|
||||
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_fbc_init);
|
||||
|
||||
|
||||
int amdgpu_dm_detect_mst_link_for_all_connectors(struct drm_device *dev)
|
||||
@@ -329,6 +331,7 @@ int amdgpu_dm_detect_mst_link_for_all_connectors(struct drm_device *dev)
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_detect_mst_link_for_all_connectors);
|
||||
|
||||
static void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
@@ -391,44 +394,44 @@ amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state,
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_find_first_crtc_matching_connector);
|
||||
|
||||
static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
struct drm_display_info *display_info = &connector->display_info;
|
||||
struct dc_link *link = aconnector->dc_link;
|
||||
struct amdgpu_device *adev;
|
||||
enum dc_panel_type panel_type = PANEL_TYPE_NONE;
|
||||
|
||||
adev = drm_to_adev(connector->dev);
|
||||
|
||||
link->panel_type = PANEL_TYPE_NONE;
|
||||
|
||||
switch (display_info->amd_vsdb.panel_type) {
|
||||
case AMD_VSDB_PANEL_TYPE_OLED:
|
||||
link->panel_type = PANEL_TYPE_OLED;
|
||||
panel_type = PANEL_TYPE_OLED;
|
||||
break;
|
||||
case AMD_VSDB_PANEL_TYPE_MINILED:
|
||||
link->panel_type = PANEL_TYPE_MINILED;
|
||||
panel_type = PANEL_TYPE_MINILED;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If VSDB didn't determine panel type, check DPCD ext caps */
|
||||
if (link->panel_type == PANEL_TYPE_NONE) {
|
||||
if (panel_type == PANEL_TYPE_NONE) {
|
||||
if (link->dpcd_sink_ext_caps.bits.miniled == 1)
|
||||
link->panel_type = PANEL_TYPE_MINILED;
|
||||
panel_type = PANEL_TYPE_MINILED;
|
||||
if (link->dpcd_sink_ext_caps.bits.oled == 1)
|
||||
link->panel_type = PANEL_TYPE_OLED;
|
||||
panel_type = PANEL_TYPE_OLED;
|
||||
}
|
||||
|
||||
/* If VSDB and DPCD didn't determine panel type, check DID */
|
||||
if (link->panel_type == PANEL_TYPE_NONE) {
|
||||
if (panel_type == PANEL_TYPE_NONE) {
|
||||
if (display_info->panel_type == DRM_MODE_PANEL_TYPE_LCD)
|
||||
link->panel_type = PANEL_TYPE_LCD;
|
||||
panel_type = PANEL_TYPE_LCD;
|
||||
else if (display_info->panel_type == DRM_MODE_PANEL_TYPE_OLED)
|
||||
link->panel_type = PANEL_TYPE_OLED;
|
||||
panel_type = PANEL_TYPE_OLED;
|
||||
}
|
||||
|
||||
if (link->panel_type == PANEL_TYPE_NONE) {
|
||||
if (panel_type == PANEL_TYPE_NONE) {
|
||||
struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb;
|
||||
u32 lum1_max = vsdb->luminance_range1.max_luminance;
|
||||
u32 lum2_max = vsdb->luminance_range2.max_luminance;
|
||||
@@ -437,9 +440,14 @@ static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
|
||||
link->local_sink->edid_caps.manufacturer_id ==
|
||||
DDC_MANUFACTURERNAME_SAMSUNG &&
|
||||
lum1_max >= ((lum2_max * 3) / 2))
|
||||
link->panel_type = PANEL_TYPE_MINILED;
|
||||
panel_type = PANEL_TYPE_MINILED;
|
||||
}
|
||||
|
||||
if (panel_type != PANEL_TYPE_NONE)
|
||||
link->panel_type = panel_type;
|
||||
else
|
||||
link->panel_type = PANEL_TYPE_LCD;
|
||||
|
||||
if (link->panel_type == PANEL_TYPE_OLED)
|
||||
drm_object_property_set_value(&connector->base,
|
||||
adev_to_drm(adev)->mode_config.panel_type_property,
|
||||
@@ -455,6 +463,34 @@ static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector)
|
||||
|
||||
drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_panel_type);
|
||||
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_update_cacp_caps(struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev);
|
||||
struct dc_link *link = aconnector->dc_link;
|
||||
|
||||
link->panel_config.cacp.cacp_supported = true;
|
||||
|
||||
if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 1, 4) ||
|
||||
amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(3, 1, 6)) {
|
||||
link->panel_config.cacp.cacp_supported = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (link->connector_signal != SIGNAL_TYPE_EDP &&
|
||||
link->connector_signal != SIGNAL_TYPE_LVDS) {
|
||||
link->panel_config.cacp.cacp_supported = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (link->panel_type == PANEL_TYPE_LCD)
|
||||
link->panel_config.cacp.cacp_supported = false;
|
||||
|
||||
drm_dbg_kms(aconnector->base.dev, "cacp_supported: %d\n",
|
||||
link->panel_config.cacp.cacp_supported);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_update_cacp_caps);
|
||||
|
||||
DEFINE_FREE(sink_release, struct dc_sink *, if (_T) dc_sink_release(_T))
|
||||
|
||||
@@ -464,6 +500,8 @@ void amdgpu_dm_update_connector_after_detect(
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
struct dc_sink *sink __free(sink_release) = NULL;
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct amdgpu_device *adev = drm_to_adev(dev);
|
||||
int inst;
|
||||
|
||||
/* MST handled by drm_mst framework */
|
||||
if (aconnector->mst_mgr.mst_state)
|
||||
@@ -581,7 +619,8 @@ void amdgpu_dm_update_connector_after_detect(
|
||||
|
||||
amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid, true);
|
||||
amdgpu_dm_update_connector_ext_caps(aconnector);
|
||||
dm_set_panel_type(aconnector);
|
||||
amdgpu_dm_set_panel_type(aconnector);
|
||||
amdgpu_dm_update_cacp_caps(aconnector);
|
||||
|
||||
if (aconnector->hdmi_comp_auto) {
|
||||
if (sink->sink_signal != SIGNAL_TYPE_HDMI_FRL)
|
||||
@@ -601,6 +640,13 @@ void amdgpu_dm_update_connector_after_detect(
|
||||
/* Set CP to DESIRED if it was ENABLED, so we can re-enable it again on hotplug */
|
||||
if (connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED)
|
||||
connector->state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
|
||||
|
||||
mutex_lock(&adev->dm.audio_lock);
|
||||
inst = aconnector->audio_inst;
|
||||
aconnector->audio_inst = -1;
|
||||
mutex_unlock(&adev->dm.audio_lock);
|
||||
if (inst != -1)
|
||||
amdgpu_dm_audio_eld_notify(adev, inst);
|
||||
}
|
||||
|
||||
update_subconnector_property(aconnector);
|
||||
@@ -798,7 +844,7 @@ STATIC_IFN_KUNIT bool adjust_colour_depth_from_display_info(
|
||||
}
|
||||
EXPORT_IF_KUNIT(adjust_colour_depth_from_display_info);
|
||||
|
||||
static void fill_stream_properties_from_drm_display_mode(
|
||||
STATIC_IFN_KUNIT void fill_stream_properties_from_drm_display_mode(
|
||||
struct dc_stream_state *stream,
|
||||
const struct drm_display_mode *mode_in,
|
||||
const struct drm_connector *connector,
|
||||
@@ -938,8 +984,9 @@ static void fill_stream_properties_from_drm_display_mode(
|
||||
stream->output_color_space = amdgpu_dm_get_output_color_space(timing_out, connector_state);
|
||||
stream->content_type = get_output_content_type(connector_state);
|
||||
}
|
||||
EXPORT_IF_KUNIT(fill_stream_properties_from_drm_display_mode);
|
||||
|
||||
static void
|
||||
STATIC_IFN_KUNIT void
|
||||
copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode,
|
||||
struct drm_display_mode *dst_mode)
|
||||
{
|
||||
@@ -958,6 +1005,7 @@ copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode,
|
||||
dst_mode->crtc_vsync_end = src_mode->crtc_vsync_end;
|
||||
dst_mode->crtc_vtotal = src_mode->crtc_vtotal;
|
||||
}
|
||||
EXPORT_IF_KUNIT(copy_crtc_timing_for_drm_display_mode);
|
||||
|
||||
STATIC_IFN_KUNIT void
|
||||
decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode,
|
||||
@@ -1227,6 +1275,9 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
|
||||
drm_connector->display_info.max_dsc_bpp;
|
||||
struct dc_dsc_config_options dsc_options = {0};
|
||||
|
||||
if (!aconnector->dc_link)
|
||||
return;
|
||||
|
||||
dc_dsc_get_default_config_option(dc, &dsc_options);
|
||||
dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16;
|
||||
|
||||
@@ -1288,7 +1339,7 @@ static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector,
|
||||
(dsc_caps->is_frl == 1) ? "HDMI FRL RX" : "DP-HDMI PCON");
|
||||
}
|
||||
}
|
||||
} else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
|
||||
} else if (sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) {
|
||||
struct dc_dsc_policy dsc_policy = {0};
|
||||
|
||||
frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link);
|
||||
@@ -2333,7 +2384,7 @@ amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
|
||||
drm_atomic_get_new_connector_state(state, conn);
|
||||
struct drm_connector_state *old_con_state =
|
||||
drm_atomic_get_old_connector_state(state, conn);
|
||||
struct drm_crtc *crtc = new_con_state->crtc;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn);
|
||||
int ret;
|
||||
@@ -2349,6 +2400,7 @@ amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
|
||||
return ret;
|
||||
}
|
||||
|
||||
crtc = new_con_state->crtc;
|
||||
if (!crtc)
|
||||
return 0;
|
||||
|
||||
|
||||
@@ -147,6 +147,17 @@ int amdgpu_dm_encoder_init(struct drm_device *dev,
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
|
||||
enum drm_mode_subconnector get_subconnector_type(struct dc_link *link);
|
||||
void update_subconnector_property(struct amdgpu_dm_connector *aconnector);
|
||||
void amdgpu_dm_fbc_init(struct drm_connector *connector);
|
||||
void amdgpu_dm_set_panel_type(struct amdgpu_dm_connector *aconnector);
|
||||
void amdgpu_dm_update_cacp_caps(struct amdgpu_dm_connector *aconnector);
|
||||
void fill_stream_properties_from_drm_display_mode(
|
||||
struct dc_stream_state *stream,
|
||||
const struct drm_display_mode *mode_in,
|
||||
const struct drm_connector *connector,
|
||||
const struct drm_connector_state *connector_state,
|
||||
const struct dc_stream_state *old_stream,
|
||||
int requested_bpc);
|
||||
enum display_content_type
|
||||
get_output_content_type(const struct drm_connector_state *connector_state);
|
||||
bool adjust_colour_depth_from_display_info(struct dc_crtc_timing *timing_out,
|
||||
@@ -155,8 +166,12 @@ bool adjust_colour_depth_from_display_info(struct dc_crtc_timing *timing_out,
|
||||
int to_drm_connector_type(enum signal_type st, uint32_t connector_id);
|
||||
bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector, struct drm_display_mode *mode);
|
||||
enum dc_aspect_ratio get_aspect_ratio(const struct drm_display_mode *mode_in);
|
||||
void copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode,
|
||||
struct drm_display_mode *dst_mode);
|
||||
void decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode,
|
||||
const struct drm_display_mode *native_mode,
|
||||
bool scale_enabled);
|
||||
void amdgpu_dm_set_panel_type(struct amdgpu_dm_connector *aconnector);
|
||||
void amdgpu_dm_update_cacp_caps(struct amdgpu_dm_connector *aconnector);
|
||||
#endif
|
||||
#endif /* __AMDGPU_DM_CONNECTOR_H__ */
|
||||
|
||||
@@ -281,7 +281,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))
|
||||
@@ -292,39 +299,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+ */
|
||||
|
||||
@@ -147,7 +147,7 @@ int dm_dmub_hw_init(struct amdgpu_device *adev)
|
||||
struct dmub_srv_hw_params hw_params;
|
||||
enum dmub_status status;
|
||||
const unsigned char *fw_inst_const, *fw_bss_data;
|
||||
u32 i, fw_inst_const_size, fw_bss_data_size;
|
||||
u32 fw_inst_const_size, fw_bss_data_size;
|
||||
bool has_hw_support;
|
||||
|
||||
if (!dmub_srv)
|
||||
@@ -243,8 +243,7 @@ int dm_dmub_hw_init(struct amdgpu_device *adev)
|
||||
if (dmcu)
|
||||
hw_params.psp_version = dmcu->psp_version;
|
||||
|
||||
for (i = 0; i < fb_info->num_fb; ++i)
|
||||
hw_params.fb[i] = &fb_info->fb[i];
|
||||
hw_params.fb_info = fb_info;
|
||||
|
||||
/* Enable usb4 dpia in the FW APU */
|
||||
if (dc->caps.is_apu &&
|
||||
@@ -507,6 +506,7 @@ int dm_dmub_sw_init(struct amdgpu_device *adev)
|
||||
DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_CURSOR_OFFLOAD */
|
||||
};
|
||||
int r;
|
||||
int mem_domain = AMDGPU_GEM_DOMAIN_GTT;
|
||||
|
||||
switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) {
|
||||
case IP_VERSION(2, 1, 0):
|
||||
@@ -640,13 +640,19 @@ int dm_dmub_sw_init(struct amdgpu_device *adev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Limit to allocate dmub to GTT on DCN32/1
|
||||
* TODO: Other asics with GDDR7 may have worse latency
|
||||
*/
|
||||
if (dmub_asic != DMUB_ASIC_DCN32 &&
|
||||
dmub_asic != DMUB_ASIC_DCN321)
|
||||
mem_domain |= AMDGPU_GEM_DOMAIN_VRAM;
|
||||
|
||||
/*
|
||||
* Allocate a framebuffer based on the total size of all the regions.
|
||||
* TODO: Move this into GART.
|
||||
*/
|
||||
r = amdgpu_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE,
|
||||
AMDGPU_GEM_DOMAIN_VRAM |
|
||||
AMDGPU_GEM_DOMAIN_GTT,
|
||||
mem_domain,
|
||||
&adev->dm.dmub_bo,
|
||||
&adev->dm.dmub_bo_gpu_addr,
|
||||
&adev->dm.dmub_bo_cpu_addr);
|
||||
|
||||
@@ -258,6 +258,30 @@ static void link_lock(struct hdcp_workqueue *work, bool lock)
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_IFN_KUNIT
|
||||
void hdcp_update_display_encryption_control(struct hdcp_workqueue *hdcp_work,
|
||||
struct hdcp_workqueue *hdcp_w,
|
||||
unsigned int conn_index,
|
||||
bool enable_encryption)
|
||||
{
|
||||
if (enable_encryption) {
|
||||
/* Explicitly set the saved SRM as sysfs call will be after we already enabled hdcp
|
||||
* (s3 resume case)
|
||||
*/
|
||||
if (hdcp_work->srm_size > 0)
|
||||
psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm,
|
||||
hdcp_work->srm_size,
|
||||
&hdcp_work->srm_version);
|
||||
|
||||
schedule_delayed_work(&hdcp_w->property_validate_dwork,
|
||||
msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
|
||||
} else {
|
||||
hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
|
||||
cancel_delayed_work(&hdcp_w->property_validate_dwork);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(hdcp_update_display_encryption_control);
|
||||
|
||||
void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
|
||||
unsigned int link_index,
|
||||
struct amdgpu_dm_connector *aconnector,
|
||||
@@ -281,22 +305,8 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work,
|
||||
dc->debug.hdcp_lc_force_fw_enable,
|
||||
dc->debug.hdcp_lc_enable_sw_fallback,
|
||||
&link_adjust, &display_adjust);
|
||||
|
||||
if (enable_encryption) {
|
||||
/* Explicitly set the saved SRM as sysfs call will be after we already enabled hdcp
|
||||
* (s3 resume case)
|
||||
*/
|
||||
if (hdcp_work->srm_size > 0)
|
||||
psp_set_srm(hdcp_work->hdcp.config.psp.handle, hdcp_work->srm,
|
||||
hdcp_work->srm_size,
|
||||
&hdcp_work->srm_version);
|
||||
|
||||
schedule_delayed_work(&hdcp_w->property_validate_dwork,
|
||||
msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS));
|
||||
} else {
|
||||
hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF;
|
||||
cancel_delayed_work(&hdcp_w->property_validate_dwork);
|
||||
}
|
||||
hdcp_update_display_encryption_control(hdcp_work, hdcp_w, conn_index,
|
||||
enable_encryption);
|
||||
|
||||
mod_hdcp_update_display(&hdcp_w->hdcp, conn_index, &link_adjust, &display_adjust, &hdcp_w->output);
|
||||
|
||||
@@ -363,6 +373,7 @@ void hdcp_handle_cpirq(struct hdcp_workqueue *hdcp_work, unsigned int link_index
|
||||
|
||||
schedule_work(&hdcp_w->cpirq_work);
|
||||
}
|
||||
EXPORT_IF_KUNIT(hdcp_handle_cpirq);
|
||||
|
||||
static void event_callback(struct work_struct *work)
|
||||
{
|
||||
@@ -381,7 +392,8 @@ static void event_callback(struct work_struct *work)
|
||||
process_output(hdcp_work);
|
||||
}
|
||||
|
||||
static void event_property_update(struct work_struct *work)
|
||||
STATIC_IFN_KUNIT
|
||||
void event_property_update(struct work_struct *work)
|
||||
{
|
||||
struct hdcp_workqueue *hdcp_work = container_of(work, struct hdcp_workqueue,
|
||||
property_update_work);
|
||||
@@ -440,6 +452,7 @@ static void event_property_update(struct work_struct *work)
|
||||
drm_modeset_unlock(&dev->mode_config.connection_mutex);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(event_property_update);
|
||||
|
||||
static void event_property_validate(struct work_struct *work)
|
||||
{
|
||||
@@ -576,15 +589,27 @@ static bool enable_assr(void *handle, struct dc_link *link)
|
||||
static void update_config(void *handle, struct cp_psp_stream_config *config)
|
||||
{
|
||||
struct hdcp_workqueue *hdcp_work = handle;
|
||||
struct amdgpu_dm_connector *aconnector = config->dm_stream_ctx;
|
||||
int link_index = aconnector->dc_link->link_index;
|
||||
unsigned int conn_index = aconnector->base.index;
|
||||
struct mod_hdcp_display *display = &hdcp_work[link_index].display;
|
||||
struct mod_hdcp_link *link = &hdcp_work[link_index].link;
|
||||
struct hdcp_workqueue *hdcp_w = &hdcp_work[link_index];
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
const struct dc *dc;
|
||||
int link_index;
|
||||
unsigned int conn_index;
|
||||
struct mod_hdcp_display *display;
|
||||
struct mod_hdcp_link *link;
|
||||
struct hdcp_workqueue *hdcp_w;
|
||||
struct dc_sink *sink = NULL;
|
||||
bool link_is_hdcp14 = false;
|
||||
const struct dc *dc = aconnector->dc_link->dc;
|
||||
|
||||
aconnector = config->dm_stream_ctx;
|
||||
if (!aconnector || !aconnector->dc_link)
|
||||
return;
|
||||
|
||||
link_index = aconnector->dc_link->link_index;
|
||||
display = &hdcp_work[link_index].display;
|
||||
link = &hdcp_work[link_index].link;
|
||||
hdcp_w = &hdcp_work[link_index];
|
||||
|
||||
conn_index = aconnector->base.index;
|
||||
dc = aconnector->dc_link->dc;
|
||||
|
||||
if (config->dpms_off) {
|
||||
hdcp_remove_display(hdcp_work, link_index, aconnector);
|
||||
@@ -622,7 +647,7 @@ static void update_config(void *handle, struct cp_psp_stream_config *config)
|
||||
link->dp.mst_enabled = config->mst_enabled;
|
||||
link->dp.dp2_enabled = config->dp2_enabled;
|
||||
link->dp.usb4_enabled = config->usb4_enabled;
|
||||
if (aconnector->dc_sink->sink_signal == SIGNAL_TYPE_HDMI_FRL)
|
||||
if (sink && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL)
|
||||
link->hdmi.frl_enabled = config->frl_enabled;
|
||||
display->adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION;
|
||||
link->adjust.auth_delay = 2;
|
||||
@@ -872,4 +897,5 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev,
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_IF_KUNIT(hdcp_create_workqueue);
|
||||
|
||||
|
||||
@@ -108,6 +108,11 @@ void hdcp_get_link_display_adjustments(
|
||||
bool hdcp_lc_enable_sw_fallback,
|
||||
struct mod_hdcp_link_adjustment *link_adjust,
|
||||
struct mod_hdcp_display_adjustment *display_adjust);
|
||||
void hdcp_update_display_encryption_control(struct hdcp_workqueue *hdcp_work,
|
||||
struct hdcp_workqueue *hdcp_w,
|
||||
unsigned int conn_index,
|
||||
bool enable_encryption);
|
||||
void event_property_update(struct work_struct *work);
|
||||
#endif
|
||||
|
||||
#endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */
|
||||
|
||||
@@ -98,7 +98,7 @@ STATIC_IFN_KUNIT u32 edid_extract_panel_id(struct edid *edid)
|
||||
}
|
||||
EXPORT_IF_KUNIT(edid_extract_panel_id);
|
||||
|
||||
static void apply_edid_quirks(struct dc_link *link, struct edid *edid,
|
||||
STATIC_IFN_KUNIT void apply_edid_quirks(struct dc_link *link, struct edid *edid,
|
||||
struct dc_edid_caps *edid_caps)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector = link->priv;
|
||||
@@ -141,6 +141,7 @@ static void apply_edid_quirks(struct dc_link *link, struct edid *edid,
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(apply_edid_quirks);
|
||||
|
||||
/**
|
||||
* dm_helpers_parse_edid_caps() - Parse edid caps
|
||||
@@ -237,8 +238,9 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_parse_edid_caps);
|
||||
|
||||
static void
|
||||
STATIC_IFN_KUNIT void
|
||||
fill_dc_mst_payload_table_from_drm(struct dc_link *link,
|
||||
bool enable,
|
||||
struct drm_dp_mst_atomic_payload *target_payload,
|
||||
@@ -288,13 +290,15 @@ fill_dc_mst_payload_table_from_drm(struct dc_link *link,
|
||||
/* Overwrite the old table */
|
||||
*table = new_table;
|
||||
}
|
||||
EXPORT_IF_KUNIT(fill_dc_mst_payload_table_from_drm);
|
||||
|
||||
void dm_helpers_dp_update_branch_info(
|
||||
struct dc_context *ctx,
|
||||
const struct dc_link *link)
|
||||
{}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_update_branch_info);
|
||||
|
||||
static void dm_helpers_construct_old_payload(
|
||||
STATIC_IFN_KUNIT void dm_helpers_construct_old_payload(
|
||||
struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_topology_state *mst_state,
|
||||
struct drm_dp_mst_atomic_payload *new_payload,
|
||||
@@ -325,6 +329,7 @@ static void dm_helpers_construct_old_payload(
|
||||
old_payload->time_slots = allocated_time_slots;
|
||||
old_payload->pbn = allocated_time_slots * pbn_per_slot;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_construct_old_payload);
|
||||
|
||||
/*
|
||||
* Writes payload allocation table in immediate downstream device.
|
||||
@@ -377,6 +382,7 @@ bool dm_helpers_dp_mst_write_payload_allocation_table(
|
||||
|
||||
return true;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_mst_write_payload_allocation_table);
|
||||
|
||||
/*
|
||||
* poll pending down reply
|
||||
@@ -385,6 +391,7 @@ void dm_helpers_dp_mst_poll_pending_down_reply(
|
||||
struct dc_context *ctx,
|
||||
const struct dc_link *link)
|
||||
{}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_mst_poll_pending_down_reply);
|
||||
|
||||
/*
|
||||
* Clear payload allocation table before enable MST DP link.
|
||||
@@ -393,6 +400,7 @@ void dm_helpers_dp_mst_clear_payload_allocation_table(
|
||||
struct dc_context *ctx,
|
||||
const struct dc_link *link)
|
||||
{}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_mst_clear_payload_allocation_table);
|
||||
|
||||
/*
|
||||
* Polls for ACT (allocation change trigger) handled and sends
|
||||
@@ -423,6 +431,7 @@ enum act_return_status dm_helpers_dp_mst_poll_for_allocation_change_trigger(
|
||||
|
||||
return ACT_SUCCESS;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_mst_poll_for_allocation_change_trigger);
|
||||
|
||||
void dm_helpers_dp_mst_send_payload_allocation(
|
||||
struct dc_context *ctx,
|
||||
@@ -457,6 +466,7 @@ void dm_helpers_dp_mst_send_payload_allocation(
|
||||
clr_flag, false);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_mst_send_payload_allocation);
|
||||
|
||||
void dm_helpers_dp_mst_update_mst_mgr_for_deallocation(
|
||||
struct dc_context *ctx,
|
||||
@@ -485,6 +495,7 @@ void dm_helpers_dp_mst_update_mst_mgr_for_deallocation(
|
||||
amdgpu_dm_set_mst_status(&aconnector->mst_status, set_flag, true);
|
||||
amdgpu_dm_set_mst_status(&aconnector->mst_status, clr_flag, false);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_mst_update_mst_mgr_for_deallocation);
|
||||
|
||||
void dm_dtn_log_begin(struct dc_context *ctx,
|
||||
struct dc_log_buffer_ctx *log_ctx)
|
||||
@@ -704,6 +715,7 @@ bool dm_helpers_submit_i2c(
|
||||
|
||||
return result;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_submit_i2c);
|
||||
|
||||
bool dm_helpers_execute_fused_io(
|
||||
struct dc_context *ctx,
|
||||
@@ -717,8 +729,9 @@ bool dm_helpers_execute_fused_io(
|
||||
|
||||
return amdgpu_dm_execute_fused_io(dev, link, commands, count, timeout_us);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_execute_fused_io);
|
||||
|
||||
static bool execute_synaptics_rc_command(struct drm_dp_aux *aux,
|
||||
STATIC_IFN_KUNIT bool execute_synaptics_rc_command(struct drm_dp_aux *aux,
|
||||
bool is_write_cmd,
|
||||
unsigned char cmd,
|
||||
unsigned int length,
|
||||
@@ -790,8 +803,9 @@ static bool execute_synaptics_rc_command(struct drm_dp_aux *aux,
|
||||
DRM_ERROR("%s: write cmd ..., err = %d\n", __func__, ret);
|
||||
return false;
|
||||
}
|
||||
EXPORT_IF_KUNIT(execute_synaptics_rc_command);
|
||||
|
||||
static void apply_synaptics_fifo_reset_wa(struct drm_dp_aux *aux)
|
||||
STATIC_IFN_KUNIT void apply_synaptics_fifo_reset_wa(struct drm_dp_aux *aux)
|
||||
{
|
||||
unsigned char data[16] = {0};
|
||||
|
||||
@@ -855,11 +869,12 @@ static void apply_synaptics_fifo_reset_wa(struct drm_dp_aux *aux)
|
||||
|
||||
drm_dbg_dp(aux->drm_dev, "Done\n");
|
||||
}
|
||||
EXPORT_IF_KUNIT(apply_synaptics_fifo_reset_wa);
|
||||
|
||||
/* MST Dock */
|
||||
static const uint8_t SYNAPTICS_DEVICE_ID[] = "SYNA";
|
||||
|
||||
static uint8_t write_dsc_enable_synaptics_non_virtual_dpcd_mst(
|
||||
STATIC_IFN_KUNIT uint8_t write_dsc_enable_synaptics_non_virtual_dpcd_mst(
|
||||
struct drm_dp_aux *aux,
|
||||
const struct dc_stream_state *stream,
|
||||
bool enable)
|
||||
@@ -895,6 +910,7 @@ static uint8_t write_dsc_enable_synaptics_non_virtual_dpcd_mst(
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_IF_KUNIT(write_dsc_enable_synaptics_non_virtual_dpcd_mst);
|
||||
|
||||
bool dm_helpers_dp_write_dsc_enable(
|
||||
struct dc_context *ctx,
|
||||
@@ -978,6 +994,21 @@ bool dm_helpers_dp_write_dsc_enable(
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_write_dsc_enable);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
|
||||
uint dm_helpers_get_dc_debug_mask(void)
|
||||
{
|
||||
return amdgpu_dc_debug_mask;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_get_dc_debug_mask);
|
||||
|
||||
void dm_helpers_set_dc_debug_mask(uint debug_mask)
|
||||
{
|
||||
amdgpu_dc_debug_mask = debug_mask;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_set_dc_debug_mask);
|
||||
#endif
|
||||
|
||||
bool dm_helpers_dp_write_hblank_reduction(struct dc_context *ctx, const struct dc_stream_state *stream)
|
||||
{
|
||||
@@ -1001,8 +1032,9 @@ bool dm_helpers_is_dp_sink_present(struct dc_link *link)
|
||||
mutex_unlock(&aconnector->dm_dp_aux.aux.hw_mutex);
|
||||
return dp_sink_present;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_is_dp_sink_present);
|
||||
|
||||
static int
|
||||
STATIC_IFN_KUNIT int
|
||||
dm_helpers_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
|
||||
{
|
||||
struct drm_connector *connector = data;
|
||||
@@ -1040,8 +1072,9 @@ dm_helpers_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len)
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_probe_acpi_edid);
|
||||
|
||||
static const struct drm_edid *
|
||||
STATIC_IFN_KUNIT const struct drm_edid *
|
||||
dm_helpers_read_acpi_edid(struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
@@ -1062,8 +1095,9 @@ dm_helpers_read_acpi_edid(struct amdgpu_dm_connector *aconnector)
|
||||
|
||||
return drm_edid_read_custom(connector, dm_helpers_probe_acpi_edid, connector);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_read_acpi_edid);
|
||||
|
||||
static const struct drm_edid *
|
||||
STATIC_IFN_KUNIT const struct drm_edid *
|
||||
dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link, struct amdgpu_dm_connector *aconnector)
|
||||
{
|
||||
struct dc_bios *bios = link->ctx->dc_bios;
|
||||
@@ -1101,6 +1135,7 @@ dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link, struct amdgpu_dm_conn
|
||||
|
||||
return edid;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_read_vbios_hardcoded_edid);
|
||||
|
||||
STATIC_IFN_KUNIT uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane)
|
||||
{
|
||||
@@ -1125,7 +1160,7 @@ STATIC_IFN_KUNIT uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_pe
|
||||
}
|
||||
EXPORT_IF_KUNIT(get_max_frl_rate);
|
||||
|
||||
static uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice)
|
||||
STATIC_IFN_KUNIT uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice)
|
||||
{
|
||||
uint8_t dsc_max_slices;
|
||||
|
||||
@@ -1148,6 +1183,7 @@ static uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice)
|
||||
|
||||
return dsc_max_slices;
|
||||
}
|
||||
EXPORT_IF_KUNIT(get_dsc_max_slices);
|
||||
|
||||
void populate_hdmi_info_from_connector(bool enable_frl, struct drm_hdmi_info *hdmi, struct dc_edid_caps *edid_caps)
|
||||
{
|
||||
@@ -1220,11 +1256,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 */
|
||||
@@ -1286,6 +1336,7 @@ int dm_helper_dmub_aux_transfer_sync(
|
||||
return amdgpu_dm_process_dmub_aux_transfer_sync(ctx, link->link_index, payload,
|
||||
operation_result);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helper_dmub_aux_transfer_sync);
|
||||
|
||||
int dm_helpers_dmub_set_config_sync(struct dc_context *ctx,
|
||||
const struct dc_link *link,
|
||||
@@ -1300,18 +1351,21 @@ void dm_set_dcn_clocks(struct dc_context *ctx, struct dc_clocks *clks)
|
||||
{
|
||||
/* TODO: something */
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_set_dcn_clocks);
|
||||
|
||||
void dm_helpers_dmu_timeout(struct dc_context *ctx)
|
||||
{
|
||||
// TODO:
|
||||
//amdgpu_device_gpu_recover(dc_context->driver-context, NULL);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dmu_timeout);
|
||||
|
||||
void dm_helpers_smu_timeout(struct dc_context *ctx, unsigned int msg_id, unsigned int param, unsigned int timeout_us)
|
||||
{
|
||||
// TODO:
|
||||
//amdgpu_device_gpu_recover(dc_context->driver-context, NULL);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_smu_timeout);
|
||||
|
||||
void dm_helpers_init_panel_settings(
|
||||
struct dc_context *ctx,
|
||||
@@ -1330,6 +1384,7 @@ void dm_helpers_init_panel_settings(
|
||||
panel_config->dsc.disable_dsc_edp = false;
|
||||
panel_config->dsc.force_dsc_edp_policy = 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_init_panel_settings);
|
||||
|
||||
void dm_helpers_override_panel_settings(
|
||||
struct dc_context *ctx,
|
||||
@@ -1347,6 +1402,7 @@ void dm_helpers_override_panel_settings(
|
||||
link->panel_config.psr.disallow_replay = true;
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_override_panel_settings);
|
||||
|
||||
void *dm_helpers_allocate_gpu_mem(
|
||||
struct dc_context *ctx,
|
||||
@@ -1382,6 +1438,7 @@ bool dm_helpers_dmub_outbox_interrupt_control(struct dc_context *ctx, bool enabl
|
||||
enable ? "en" : "dis", ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dmub_outbox_interrupt_control);
|
||||
|
||||
void dm_helpers_mst_enable_stream_features(const struct dc_stream_state *stream)
|
||||
{
|
||||
@@ -1407,6 +1464,7 @@ void dm_helpers_mst_enable_stream_features(const struct dc_stream_state *stream)
|
||||
&new_downspread.raw,
|
||||
sizeof(new_downspread));
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_mst_enable_stream_features);
|
||||
|
||||
bool dm_helpers_dp_handle_test_pattern_request(
|
||||
struct dc_context *ctx,
|
||||
@@ -1545,11 +1603,13 @@ bool dm_helpers_dp_handle_test_pattern_request(
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_handle_test_pattern_request);
|
||||
|
||||
void dm_set_phyd32clk(struct dc_context *ctx, int freq_khz)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_set_phyd32clk);
|
||||
|
||||
void dm_helpers_enable_periodic_detection(struct dc_context *ctx, bool enable)
|
||||
{
|
||||
@@ -1561,6 +1621,7 @@ void dm_helpers_enable_periodic_detection(struct dc_context *ctx, bool enable)
|
||||
schedule_work(&adev->dm.idle_workqueue->work);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_enable_periodic_detection);
|
||||
|
||||
void dm_helpers_dp_mst_update_branch_bandwidth(
|
||||
struct dc_context *ctx,
|
||||
@@ -1568,6 +1629,7 @@ void dm_helpers_dp_mst_update_branch_bandwidth(
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_dp_mst_update_branch_bandwidth);
|
||||
|
||||
STATIC_IFN_KUNIT const uint32_t dm_freesync_pcon_whitelist[] = {
|
||||
DP_BRANCH_DEVICE_ID_0060AD,
|
||||
@@ -1754,6 +1816,7 @@ void dm_helpers_read_mccs_caps(struct dc_context *ctx, struct dc_link *link,
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_read_mccs_caps);
|
||||
|
||||
static int mccs_operation_vcp_set(unsigned int vcp_code, struct dc_link *link, uint16_t value)
|
||||
{
|
||||
@@ -1828,4 +1891,5 @@ void dm_helpers_mccs_vcp_set(struct dc_context *ctx, struct dc_link *link,
|
||||
drm_dbg_driver(dev, "%s: Failed to set VCP code %d", __func__,
|
||||
sink->edid_caps.freesync_vcp_code);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_helpers_mccs_vcp_set);
|
||||
|
||||
|
||||
@@ -9,12 +9,53 @@
|
||||
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
struct amdgpu_dm_connector;
|
||||
struct dc_link;
|
||||
struct dc_context;
|
||||
struct dc_stream_state;
|
||||
struct dc_edid_caps;
|
||||
struct dc_dp_mst_stream_allocation_table;
|
||||
struct drm_dp_aux;
|
||||
struct drm_dp_mst_atomic_payload;
|
||||
struct drm_dp_mst_topology_mgr;
|
||||
struct drm_dp_mst_topology_state;
|
||||
|
||||
/* Exported for KUnit testing */
|
||||
u32 edid_extract_panel_id(struct edid *edid);
|
||||
void apply_edid_quirks(struct dc_link *link, struct edid *edid,
|
||||
struct dc_edid_caps *edid_caps);
|
||||
uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane);
|
||||
uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice);
|
||||
bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id);
|
||||
extern const uint32_t dm_freesync_pcon_whitelist[];
|
||||
uint32_t dm_freesync_pcon_whitelist_count(void);
|
||||
void fill_dc_mst_payload_table_from_drm(struct dc_link *link,
|
||||
bool enable,
|
||||
struct drm_dp_mst_atomic_payload *target_payload,
|
||||
struct dc_dp_mst_stream_allocation_table *table);
|
||||
void dm_helpers_construct_old_payload(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_topology_state *mst_state,
|
||||
struct drm_dp_mst_atomic_payload *new_payload,
|
||||
struct drm_dp_mst_atomic_payload *old_payload);
|
||||
bool dm_helpers_dp_write_dsc_enable(struct dc_context *ctx,
|
||||
const struct dc_stream_state *stream,
|
||||
bool enable);
|
||||
uint dm_helpers_get_dc_debug_mask(void);
|
||||
void dm_helpers_set_dc_debug_mask(uint debug_mask);
|
||||
int dm_helpers_probe_acpi_edid(void *data, u8 *buf, unsigned int block, size_t len);
|
||||
const struct drm_edid *dm_helpers_read_acpi_edid(struct amdgpu_dm_connector *aconnector);
|
||||
const struct drm_edid *dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link,
|
||||
struct amdgpu_dm_connector *aconnector);
|
||||
bool execute_synaptics_rc_command(struct drm_dp_aux *aux,
|
||||
bool is_write_cmd,
|
||||
unsigned char cmd,
|
||||
unsigned int length,
|
||||
unsigned int offset,
|
||||
unsigned char *data);
|
||||
void apply_synaptics_fifo_reset_wa(struct drm_dp_aux *aux);
|
||||
uint8_t write_dsc_enable_synaptics_non_virtual_dpcd_mst(struct drm_dp_aux *aux,
|
||||
const struct dc_stream_state *stream,
|
||||
bool enable);
|
||||
#endif /* CONFIG_DRM_AMD_DC_KUNIT_TEST */
|
||||
|
||||
#endif /* __AMDGPU_DM_HELPERS_H__ */
|
||||
|
||||
@@ -195,6 +195,9 @@ static struct list_head *remove_irq_handler(struct amdgpu_device *adev,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (int_params->int_context == INTERRUPT_LOW_IRQ_CONTEXT)
|
||||
cancel_work_sync(&handler->work);
|
||||
|
||||
kfree(handler);
|
||||
|
||||
DRM_DEBUG_KMS(
|
||||
@@ -204,55 +207,6 @@ static struct list_head *remove_irq_handler(struct amdgpu_device *adev,
|
||||
return hnd_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* unregister_all_irq_handlers() - Cleans up handlers from the DM IRQ table
|
||||
* @adev: The base driver device containing the DM device
|
||||
*
|
||||
* Go through low and high context IRQ tables and deallocate handlers.
|
||||
*/
|
||||
static void unregister_all_irq_handlers(struct amdgpu_device *adev)
|
||||
{
|
||||
struct list_head *hnd_list_low;
|
||||
struct list_head *hnd_list_high;
|
||||
struct list_head *entry, *tmp;
|
||||
struct amdgpu_dm_irq_handler_data *handler;
|
||||
unsigned long irq_table_flags;
|
||||
int i;
|
||||
|
||||
DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
|
||||
|
||||
for (i = 0; i < DAL_IRQ_SOURCES_NUMBER; i++) {
|
||||
hnd_list_low = &adev->dm.irq_handler_list_low_tab[i];
|
||||
hnd_list_high = &adev->dm.irq_handler_list_high_tab[i];
|
||||
|
||||
list_for_each_safe(entry, tmp, hnd_list_low) {
|
||||
|
||||
handler = list_entry(entry, struct amdgpu_dm_irq_handler_data,
|
||||
list);
|
||||
|
||||
if (handler == NULL || handler->handler == NULL)
|
||||
continue;
|
||||
|
||||
list_del(&handler->list);
|
||||
kfree(handler);
|
||||
}
|
||||
|
||||
list_for_each_safe(entry, tmp, hnd_list_high) {
|
||||
|
||||
handler = list_entry(entry, struct amdgpu_dm_irq_handler_data,
|
||||
list);
|
||||
|
||||
if (handler == NULL || handler->handler == NULL)
|
||||
continue;
|
||||
|
||||
list_del(&handler->list);
|
||||
kfree(handler);
|
||||
}
|
||||
}
|
||||
|
||||
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
||||
}
|
||||
|
||||
static bool
|
||||
validate_irq_registration_params(struct dc_interrupt_params *int_params,
|
||||
void (*ih)(void *))
|
||||
@@ -443,6 +397,21 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev)
|
||||
|
||||
spin_lock_init(&adev->dm.irq_handler_list_table_lock);
|
||||
|
||||
adev->dm.irq_wq = alloc_workqueue("amdgpu_dm_irq",
|
||||
WQ_UNBOUND | WQ_HIGHPRI, 0);
|
||||
|
||||
if (!adev->dm.irq_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
adev->dm.vmin_vmax_wq = alloc_workqueue("amdgpu_dm_vmin_vmax",
|
||||
WQ_UNBOUND, 0);
|
||||
|
||||
if (!adev->dm.vmin_vmax_wq) {
|
||||
destroy_workqueue(adev->dm.irq_wq);
|
||||
adev->dm.irq_wq = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
|
||||
/* low context handler list init */
|
||||
lh = &adev->dm.irq_handler_list_low_tab[src];
|
||||
@@ -459,38 +428,81 @@ EXPORT_IF_KUNIT(amdgpu_dm_irq_init);
|
||||
* amdgpu_dm_irq_fini() - Tear down DM IRQ management
|
||||
* @adev: The base driver device containing the DM device
|
||||
*
|
||||
* Flush all work within the low context IRQ table.
|
||||
* Removes all handlers from the IRQ tables under the spinlock, cancels
|
||||
* pending work items, and deallocates all handler data.
|
||||
*/
|
||||
void amdgpu_dm_irq_fini(struct amdgpu_device *adev)
|
||||
{
|
||||
int src;
|
||||
struct list_head *lh;
|
||||
LIST_HEAD(low_handlers);
|
||||
LIST_HEAD(high_handlers);
|
||||
struct list_head *entry, *tmp;
|
||||
struct amdgpu_dm_irq_handler_data *handler;
|
||||
unsigned long irq_table_flags;
|
||||
|
||||
DRM_DEBUG_KMS("DM_IRQ: releasing resources.\n");
|
||||
|
||||
for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) {
|
||||
DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
|
||||
/* The handler was removed from the table,
|
||||
* it means it is safe to flush all the 'work'
|
||||
* (because no code can schedule a new one).
|
||||
|
||||
/*
|
||||
* Move all handlers from the low and high context tables to
|
||||
* temporary lists under the lock. This prevents the ISR from
|
||||
* finding them while we process them outside the lock.
|
||||
*/
|
||||
lh = &adev->dm.irq_handler_list_low_tab[src];
|
||||
list_splice_init(&adev->dm.irq_handler_list_low_tab[src],
|
||||
&low_handlers);
|
||||
list_splice_init(&adev->dm.irq_handler_list_high_tab[src],
|
||||
&high_handlers);
|
||||
|
||||
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
||||
|
||||
if (!list_empty(lh)) {
|
||||
list_for_each_safe(entry, tmp, lh) {
|
||||
handler = list_entry(
|
||||
entry,
|
||||
struct amdgpu_dm_irq_handler_data,
|
||||
list);
|
||||
flush_work(&handler->work);
|
||||
}
|
||||
/*
|
||||
* Cancel all pending work for the low-context handlers
|
||||
* outside the lock. cancel_work_sync() may sleep and waits
|
||||
* until any running work completes, preventing UAF.
|
||||
*/
|
||||
list_for_each_safe(entry, tmp, &low_handlers) {
|
||||
handler = list_entry(entry,
|
||||
struct amdgpu_dm_irq_handler_data,
|
||||
list);
|
||||
cancel_work_sync(&handler->work);
|
||||
}
|
||||
|
||||
/*
|
||||
* High-context handlers are executed synchronously within ISR
|
||||
* context (see amdgpu_dm_irq_immediate_work()) and have no
|
||||
* work_struct, so there is no pending work to cancel here.
|
||||
* They will be freed along with low_handlers after the loop.
|
||||
*/
|
||||
}
|
||||
|
||||
/* Deallocate all handlers. */
|
||||
list_for_each_safe(entry, tmp, &low_handlers) {
|
||||
handler = list_entry(entry,
|
||||
struct amdgpu_dm_irq_handler_data,
|
||||
list);
|
||||
list_del(&handler->list);
|
||||
kfree(handler);
|
||||
}
|
||||
|
||||
list_for_each_safe(entry, tmp, &high_handlers) {
|
||||
handler = list_entry(entry,
|
||||
struct amdgpu_dm_irq_handler_data,
|
||||
list);
|
||||
list_del(&handler->list);
|
||||
kfree(handler);
|
||||
}
|
||||
|
||||
if (adev->dm.vmin_vmax_wq) {
|
||||
destroy_workqueue(adev->dm.vmin_vmax_wq);
|
||||
adev->dm.vmin_vmax_wq = NULL;
|
||||
}
|
||||
|
||||
if (adev->dm.irq_wq) {
|
||||
destroy_workqueue(adev->dm.irq_wq);
|
||||
adev->dm.irq_wq = NULL;
|
||||
}
|
||||
/* Deallocate handlers from the table. */
|
||||
unregister_all_irq_handlers(adev);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_fini);
|
||||
|
||||
@@ -498,7 +510,6 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev)
|
||||
{
|
||||
struct drm_device *dev = adev_to_drm(adev);
|
||||
int src;
|
||||
struct list_head *hnd_list_h;
|
||||
struct list_head *hnd_list_l;
|
||||
unsigned long irq_table_flags;
|
||||
struct list_head *entry, *tmp;
|
||||
@@ -511,12 +522,15 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev)
|
||||
/**
|
||||
* Disable HW interrupt for HPD and HPDRX only since FLIP and VBLANK
|
||||
* will be disabled from manage_dm_interrupts on disable CRTC.
|
||||
*
|
||||
* Disable the HW interrupt first, then flush any pending work. Since
|
||||
* the HW interrupt is disabled under the lock, no new IRQ can be
|
||||
* generated after the disable completes. Any work already queued by an
|
||||
* in-flight ISR will be flushed below.
|
||||
*/
|
||||
for (src = DC_IRQ_SOURCE_HPD1; src <= DC_IRQ_SOURCE_HPD6RX; src++) {
|
||||
hnd_list_l = &adev->dm.irq_handler_list_low_tab[src];
|
||||
hnd_list_h = &adev->dm.irq_handler_list_high_tab[src];
|
||||
if (!list_empty(hnd_list_l) || !list_empty(hnd_list_h))
|
||||
dc_interrupt_set(adev->dm.dc, src, false);
|
||||
dc_interrupt_set(adev->dm.dc, src, false);
|
||||
|
||||
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
||||
|
||||
@@ -537,6 +551,7 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev)
|
||||
if (dev->mode_config.poll_enabled)
|
||||
drm_kms_helper_poll_disable(dev);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_suspend);
|
||||
|
||||
void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev)
|
||||
{
|
||||
@@ -558,6 +573,7 @@ void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev)
|
||||
|
||||
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_resume_early);
|
||||
|
||||
void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev)
|
||||
{
|
||||
@@ -586,23 +602,27 @@ void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev)
|
||||
if (dev->mode_config.poll_enabled)
|
||||
drm_kms_helper_poll_enable(dev);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_resume_late);
|
||||
|
||||
/*
|
||||
* amdgpu_dm_irq_schedule_work - schedule all work items registered for the
|
||||
* "irq_source".
|
||||
*/
|
||||
static void amdgpu_dm_irq_schedule_work(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_irq_schedule_work(struct amdgpu_device *adev,
|
||||
enum dc_irq_source irq_source)
|
||||
{
|
||||
struct list_head *handler_list = &adev->dm.irq_handler_list_low_tab[irq_source];
|
||||
struct amdgpu_dm_irq_handler_data *handler_data;
|
||||
bool work_queued = false;
|
||||
unsigned long irq_table_flags;
|
||||
|
||||
DM_IRQ_TABLE_LOCK(adev, irq_table_flags);
|
||||
|
||||
if (list_empty(handler_list))
|
||||
return;
|
||||
goto out_unlock;
|
||||
|
||||
list_for_each_entry(handler_data, handler_list, list) {
|
||||
if (queue_work(system_highpri_wq, &handler_data->work)) {
|
||||
if (queue_work(adev->dm.irq_wq, &handler_data->work)) {
|
||||
work_queued = true;
|
||||
break;
|
||||
}
|
||||
@@ -617,7 +637,7 @@ static void amdgpu_dm_irq_schedule_work(struct amdgpu_device *adev,
|
||||
handler_data_add = kzalloc_obj(*handler_data, GFP_ATOMIC);
|
||||
if (!handler_data_add) {
|
||||
DRM_ERROR("DM_IRQ: failed to allocate irq handler!\n");
|
||||
return;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*copy new amdgpu_dm_irq_handler_data members from handler_data*/
|
||||
@@ -630,7 +650,7 @@ static void amdgpu_dm_irq_schedule_work(struct amdgpu_device *adev,
|
||||
|
||||
INIT_WORK(&handler_data_add->work, dm_irq_work_func);
|
||||
|
||||
if (queue_work(system_highpri_wq, &handler_data_add->work))
|
||||
if (queue_work(adev->dm.irq_wq, &handler_data_add->work))
|
||||
DRM_DEBUG("Queued work for handling interrupt from "
|
||||
"display for IRQ source %d\n",
|
||||
irq_source);
|
||||
@@ -639,13 +659,17 @@ static void amdgpu_dm_irq_schedule_work(struct amdgpu_device *adev,
|
||||
"from display for IRQ source %d\n",
|
||||
irq_source);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_schedule_work);
|
||||
|
||||
/*
|
||||
* amdgpu_dm_irq_immediate_work
|
||||
* Callback high irq work immediately, don't send to work queue
|
||||
*/
|
||||
static void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev,
|
||||
enum dc_irq_source irq_source)
|
||||
{
|
||||
struct amdgpu_dm_irq_handler_data *handler_data;
|
||||
@@ -664,6 +688,7 @@ static void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev,
|
||||
|
||||
DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_immediate_work);
|
||||
|
||||
/**
|
||||
* amdgpu_dm_irq_handler - Generic DM IRQ handler
|
||||
@@ -674,7 +699,7 @@ static void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev,
|
||||
* Calls all registered high irq work immediately, and schedules work for low
|
||||
* irq. The DM IRQ table is used to find the corresponding handlers.
|
||||
*/
|
||||
static int amdgpu_dm_irq_handler(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_irq_handler(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
struct amdgpu_iv_entry *entry)
|
||||
{
|
||||
@@ -694,6 +719,7 @@ static int amdgpu_dm_irq_handler(struct amdgpu_device *adev,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_irq_handler);
|
||||
|
||||
STATIC_IFN_KUNIT enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type)
|
||||
{
|
||||
@@ -716,7 +742,7 @@ STATIC_IFN_KUNIT enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_hpd_to_dal_irq_source);
|
||||
|
||||
static int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int type,
|
||||
enum amdgpu_interrupt_state state)
|
||||
@@ -727,6 +753,7 @@ static int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev,
|
||||
dc_interrupt_set(adev->dm.dc, src, st);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_hpd_irq_state);
|
||||
|
||||
static inline int dm_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
@@ -762,7 +789,7 @@ static inline int dm_irq_state(struct amdgpu_device *adev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amdgpu_dm_set_pflip_irq_state(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_set_pflip_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state)
|
||||
@@ -775,8 +802,9 @@ static int amdgpu_dm_set_pflip_irq_state(struct amdgpu_device *adev,
|
||||
IRQ_TYPE_PFLIP,
|
||||
__func__);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_pflip_irq_state);
|
||||
|
||||
static int amdgpu_dm_set_crtc_irq_state(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_set_crtc_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state)
|
||||
@@ -789,8 +817,9 @@ static int amdgpu_dm_set_crtc_irq_state(struct amdgpu_device *adev,
|
||||
IRQ_TYPE_VBLANK,
|
||||
__func__);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_crtc_irq_state);
|
||||
|
||||
static int amdgpu_dm_set_vline0_irq_state(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_set_vline0_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state)
|
||||
@@ -803,8 +832,9 @@ static int amdgpu_dm_set_vline0_irq_state(struct amdgpu_device *adev,
|
||||
IRQ_TYPE_VLINE0,
|
||||
__func__);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_vline0_irq_state);
|
||||
|
||||
static int amdgpu_dm_set_dmub_outbox_irq_state(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_set_dmub_outbox_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state)
|
||||
@@ -815,8 +845,9 @@ static int amdgpu_dm_set_dmub_outbox_irq_state(struct amdgpu_device *adev,
|
||||
dc_interrupt_set(adev->dm.dc, irq_source, st);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_dmub_outbox_irq_state);
|
||||
|
||||
static int amdgpu_dm_set_vupdate_irq_state(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_set_vupdate_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state)
|
||||
@@ -829,8 +860,9 @@ static int amdgpu_dm_set_vupdate_irq_state(struct amdgpu_device *adev,
|
||||
IRQ_TYPE_VUPDATE,
|
||||
__func__);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_vupdate_irq_state);
|
||||
|
||||
static int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int type,
|
||||
enum amdgpu_interrupt_state state)
|
||||
@@ -841,6 +873,7 @@ static int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *adev,
|
||||
dc_interrupt_set(adev->dm.dc, irq_source, st);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_dmub_trace_irq_state);
|
||||
|
||||
static const struct amdgpu_irq_src_funcs dm_crtc_irq_funcs = {
|
||||
.set = amdgpu_dm_set_crtc_irq_state,
|
||||
@@ -900,12 +933,15 @@ void amdgpu_dm_set_irq_funcs(struct amdgpu_device *adev)
|
||||
adev->hpd_irq.num_types = adev->mode_info.num_hpd;
|
||||
adev->hpd_irq.funcs = &dm_hpd_irq_funcs;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_set_irq_funcs);
|
||||
|
||||
void amdgpu_dm_outbox_init(struct amdgpu_device *adev)
|
||||
{
|
||||
dc_interrupt_set(adev->dm.dc,
|
||||
DC_IRQ_SOURCE_DMCUB_OUTBOX,
|
||||
true);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_outbox_init);
|
||||
|
||||
/**
|
||||
* amdgpu_dm_hpd_init - hpd setup callback.
|
||||
@@ -988,6 +1024,7 @@ void amdgpu_dm_hpd_init(struct amdgpu_device *adev)
|
||||
if (use_polling)
|
||||
drm_kms_helper_poll_init(dev);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_hpd_init);
|
||||
|
||||
/**
|
||||
* amdgpu_dm_hpd_fini - hpd tear down callback.
|
||||
@@ -1041,6 +1078,7 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev)
|
||||
if (dev->mode_config.poll_enabled)
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_hpd_fini);
|
||||
|
||||
/* ========== HPD handling ========== */
|
||||
static void force_connector_state(
|
||||
@@ -1058,7 +1096,7 @@ static void force_connector_state(
|
||||
mutex_unlock(&aconnector->hpd_lock);
|
||||
}
|
||||
|
||||
static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
|
||||
STATIC_IFN_KUNIT void dm_handle_hpd_rx_offload_work(struct work_struct *work)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work *offload_work;
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
@@ -1151,6 +1189,7 @@ static void dm_handle_hpd_rx_offload_work(struct work_struct *work)
|
||||
kfree(offload_work);
|
||||
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_handle_hpd_rx_offload_work);
|
||||
|
||||
struct hpd_rx_irq_offload_work_queue *amdgpu_dm_hpd_rx_irq_create_workqueue(struct amdgpu_device *adev)
|
||||
{
|
||||
@@ -1187,6 +1226,7 @@ struct hpd_rx_irq_offload_work_queue *amdgpu_dm_hpd_rx_irq_create_workqueue(stru
|
||||
kfree(hpd_rx_offload_wq);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_hpd_rx_irq_create_workqueue);
|
||||
|
||||
void amdgpu_dm_hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm)
|
||||
{
|
||||
@@ -1197,6 +1237,7 @@ void amdgpu_dm_hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm)
|
||||
flush_workqueue(dm->hpd_rx_offload_wq[i].wq);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_hpd_rx_irq_work_suspend);
|
||||
|
||||
STATIC_IFN_KUNIT bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2)
|
||||
{
|
||||
@@ -1290,8 +1331,9 @@ void amdgpu_dm_hdmi_hpd_debounce_work(struct work_struct *work)
|
||||
dc_allow_idle_optimizations(dc, true);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_hdmi_hpd_debounce_work);
|
||||
|
||||
static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector,
|
||||
STATIC_IFN_KUNIT void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector,
|
||||
enum dc_detect_reason reason)
|
||||
{
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
@@ -1389,16 +1431,18 @@ static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector,
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(handle_hpd_irq_helper);
|
||||
|
||||
static void handle_hpd_irq(void *param)
|
||||
STATIC_IFN_KUNIT void handle_hpd_irq(void *param)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
|
||||
|
||||
handle_hpd_irq_helper(aconnector, DETECT_REASON_HPD);
|
||||
|
||||
}
|
||||
EXPORT_IF_KUNIT(handle_hpd_irq);
|
||||
|
||||
static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq,
|
||||
STATIC_IFN_KUNIT void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq,
|
||||
union hpd_irq_data hpd_irq_data)
|
||||
{
|
||||
struct hpd_rx_irq_offload_work *offload_work = kzalloc_obj(*offload_work);
|
||||
@@ -1416,8 +1460,9 @@ static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_
|
||||
queue_work(offload_wq->wq, &offload_work->work);
|
||||
drm_dbg_kms(adev_to_drm(adev), "queue work to handle hpd_rx offload work");
|
||||
}
|
||||
EXPORT_IF_KUNIT(schedule_hpd_rx_offload_work);
|
||||
|
||||
static void handle_hpd_rx_irq(void *param)
|
||||
STATIC_IFN_KUNIT void handle_hpd_rx_irq(void *param)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param;
|
||||
struct drm_connector *connector = &aconnector->base;
|
||||
@@ -1550,6 +1595,7 @@ static void handle_hpd_rx_irq(void *param)
|
||||
|
||||
mutex_unlock(&aconnector->hpd_lock);
|
||||
}
|
||||
EXPORT_IF_KUNIT(handle_hpd_rx_irq);
|
||||
|
||||
/**
|
||||
* dmub_hpd_callback - DMUB HPD interrupt processing callback.
|
||||
@@ -1559,7 +1605,7 @@ static void handle_hpd_rx_irq(void *param)
|
||||
* Dmub Hpd interrupt processing callback. Gets displayindex through the
|
||||
* ink index and calls helper to do the processing.
|
||||
*/
|
||||
static void dmub_hpd_callback(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT void dmub_hpd_callback(struct amdgpu_device *adev,
|
||||
struct dmub_notification *notify)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
@@ -1625,6 +1671,7 @@ static void dmub_hpd_callback(struct amdgpu_device *adev,
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(dmub_hpd_callback);
|
||||
|
||||
/**
|
||||
* dmub_hpd_sense_callback - DMUB HPD sense processing callback.
|
||||
@@ -1634,11 +1681,12 @@ static void dmub_hpd_callback(struct amdgpu_device *adev,
|
||||
* HPD sense changes can occur during low power states and need to be
|
||||
* notified from firmware to driver.
|
||||
*/
|
||||
static void dmub_hpd_sense_callback(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT void dmub_hpd_sense_callback(struct amdgpu_device *adev,
|
||||
struct dmub_notification *notify)
|
||||
{
|
||||
drm_dbg_driver(adev_to_drm(adev), "DMUB HPD SENSE callback.\n");
|
||||
}
|
||||
EXPORT_IF_KUNIT(dmub_hpd_sense_callback);
|
||||
|
||||
int amdgpu_dm_register_hpd_handlers(struct amdgpu_device *adev)
|
||||
{
|
||||
@@ -1716,6 +1764,7 @@ int amdgpu_dm_register_hpd_handlers(struct amdgpu_device *adev)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_register_hpd_handlers);
|
||||
|
||||
/* ========== IRQ handlers ========== */
|
||||
struct amdgpu_crtc *
|
||||
@@ -1747,7 +1796,7 @@ EXPORT_IF_KUNIT(amdgpu_dm_get_crtc_by_otg_inst);
|
||||
* Handles the pageflip interrupt by notifying all interested parties
|
||||
* that the pageflip has been completed.
|
||||
*/
|
||||
static void dm_pflip_high_irq(void *interrupt_params)
|
||||
STATIC_IFN_KUNIT void dm_pflip_high_irq(void *interrupt_params)
|
||||
{
|
||||
struct amdgpu_crtc *amdgpu_crtc;
|
||||
struct common_irq_params *irq_params = interrupt_params;
|
||||
@@ -1843,8 +1892,9 @@ static void dm_pflip_high_irq(void *interrupt_params)
|
||||
"crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n",
|
||||
amdgpu_crtc->crtc_id, amdgpu_crtc, vrr_active, (int)!e);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_pflip_high_irq);
|
||||
|
||||
static void dm_handle_vmin_vmax_update(struct work_struct *offload_work)
|
||||
STATIC_IFN_KUNIT void dm_handle_vmin_vmax_update(struct work_struct *offload_work)
|
||||
{
|
||||
struct vupdate_offload_work *work = container_of(offload_work, struct vupdate_offload_work, work);
|
||||
struct amdgpu_device *adev = work->adev;
|
||||
@@ -1859,6 +1909,7 @@ static void dm_handle_vmin_vmax_update(struct work_struct *offload_work)
|
||||
kfree(work->adjust);
|
||||
kfree(work);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_handle_vmin_vmax_update);
|
||||
|
||||
static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
|
||||
struct dc_stream_state *stream,
|
||||
@@ -1887,91 +1938,26 @@ static void schedule_dc_vmin_vmax(struct amdgpu_device *adev,
|
||||
offload_work->stream = stream;
|
||||
offload_work->adjust = adjust_copy;
|
||||
|
||||
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 = amdgpu_dm_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
queue_work(adev->dm.vmin_vmax_wq, &offload_work->work);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Performs writeback completion, vblank event handling, CRC processing, VRR BTR
|
||||
* updates and pageflip completion delivery.
|
||||
*
|
||||
* Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK
|
||||
* event handler.
|
||||
* 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 amdgpu_crtc *acrtc;
|
||||
unsigned long flags;
|
||||
int vrr_active;
|
||||
|
||||
acrtc = amdgpu_dm_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 && acrtc->wb_pending) {
|
||||
struct dc_stream_state *stream = acrtc->dm_irq_params.stream;
|
||||
@@ -2000,12 +1986,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);
|
||||
|
||||
/**
|
||||
@@ -2038,18 +2029,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;
|
||||
@@ -2061,6 +2067,106 @@ static void dm_crtc_high_irq(void *interrupt_params)
|
||||
spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags);
|
||||
}
|
||||
|
||||
STATIC_IFN_KUNIT 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 = amdgpu_dm_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);
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_vupdate_high_irq);
|
||||
|
||||
/**
|
||||
* 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_IFN_KUNIT 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 = amdgpu_dm_get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK);
|
||||
if (!acrtc)
|
||||
return;
|
||||
|
||||
dm_crtc_high_irq_handler(adev, acrtc);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_crtc_high_irq);
|
||||
|
||||
#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY)
|
||||
/**
|
||||
* dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for
|
||||
@@ -2084,7 +2190,7 @@ static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params)
|
||||
}
|
||||
#endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */
|
||||
|
||||
static void dm_handle_hpd_work(struct work_struct *work)
|
||||
STATIC_IFN_KUNIT void dm_handle_hpd_work(struct work_struct *work)
|
||||
{
|
||||
struct dmub_hpd_work *dmub_hpd_wrk;
|
||||
|
||||
@@ -2104,6 +2210,7 @@ static void dm_handle_hpd_work(struct work_struct *work)
|
||||
kfree(dmub_hpd_wrk);
|
||||
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_handle_hpd_work);
|
||||
|
||||
STATIC_IFN_KUNIT const char *dmub_notification_type_str(enum dmub_notification_type e)
|
||||
{
|
||||
@@ -2138,7 +2245,7 @@ EXPORT_IF_KUNIT(dmub_notification_type_str);
|
||||
* Handles the Outbox Interrupt
|
||||
* event handler.
|
||||
*/
|
||||
static void dm_dmub_outbox1_low_irq(void *interrupt_params)
|
||||
STATIC_IFN_KUNIT void dm_dmub_outbox1_low_irq(void *interrupt_params)
|
||||
{
|
||||
struct dmub_notification notify = {0};
|
||||
struct common_irq_params *irq_params = interrupt_params;
|
||||
@@ -2202,6 +2309,7 @@ static void dm_dmub_outbox1_low_irq(void *interrupt_params)
|
||||
} while (notify.pending_notification);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_dmub_outbox1_low_irq);
|
||||
|
||||
/* Register IRQ sources and initialize IRQ callbacks */
|
||||
int amdgpu_dm_dce110_register_irq_handlers(struct amdgpu_device *adev)
|
||||
@@ -2340,6 +2448,7 @@ int amdgpu_dm_dce110_register_irq_handlers(struct amdgpu_device *adev)
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_dce110_register_irq_handlers);
|
||||
|
||||
/* Register IRQ sources and initialize IRQ callbacks */
|
||||
int amdgpu_dm_dcn10_register_irq_handlers(struct amdgpu_device *adev)
|
||||
@@ -2375,38 +2484,6 @@ int amdgpu_dm_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++) {
|
||||
@@ -2478,37 +2555,6 @@ int amdgpu_dm_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);
|
||||
@@ -2521,6 +2567,7 @@ int amdgpu_dm_dcn10_register_irq_handlers(struct amdgpu_device *adev)
|
||||
|
||||
return r;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_dcn10_register_irq_handlers);
|
||||
|
||||
/* Register Outbox IRQ sources and initialize IRQ callbacks */
|
||||
int amdgpu_dm_register_outbox_irq_handlers(struct amdgpu_device *adev)
|
||||
@@ -2558,3 +2605,4 @@ int amdgpu_dm_register_outbox_irq_handlers(struct amdgpu_device *adev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_register_outbox_irq_handlers);
|
||||
|
||||
@@ -123,9 +123,66 @@ int amdgpu_dm_dcn10_register_irq_handlers(struct amdgpu_device *adev);
|
||||
int amdgpu_dm_register_outbox_irq_handlers(struct amdgpu_device *adev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST)
|
||||
struct amdgpu_irq_src;
|
||||
struct amdgpu_iv_entry;
|
||||
enum amdgpu_interrupt_state;
|
||||
|
||||
enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type);
|
||||
bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2);
|
||||
const char *dmub_notification_type_str(enum dmub_notification_type e);
|
||||
int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int type,
|
||||
enum amdgpu_interrupt_state state);
|
||||
int amdgpu_dm_set_dmub_outbox_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state);
|
||||
int amdgpu_dm_set_dmub_trace_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int type,
|
||||
enum amdgpu_interrupt_state state);
|
||||
int amdgpu_dm_set_pflip_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state);
|
||||
int amdgpu_dm_set_crtc_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state);
|
||||
int amdgpu_dm_set_vline0_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state);
|
||||
int amdgpu_dm_set_vupdate_irq_state(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
unsigned int crtc_id,
|
||||
enum amdgpu_interrupt_state state);
|
||||
void amdgpu_dm_irq_schedule_work(struct amdgpu_device *adev,
|
||||
enum dc_irq_source irq_source);
|
||||
void amdgpu_dm_irq_immediate_work(struct amdgpu_device *adev,
|
||||
enum dc_irq_source irq_source);
|
||||
void dm_handle_hpd_rx_offload_work(struct work_struct *work);
|
||||
void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector,
|
||||
enum dc_detect_reason reason);
|
||||
void handle_hpd_irq(void *param);
|
||||
void schedule_hpd_rx_offload_work(struct amdgpu_device *adev,
|
||||
struct hpd_rx_irq_offload_work_queue *offload_wq,
|
||||
union hpd_irq_data hpd_irq_data);
|
||||
void handle_hpd_rx_irq(void *param);
|
||||
void dmub_hpd_callback(struct amdgpu_device *adev,
|
||||
struct dmub_notification *notify);
|
||||
void dmub_hpd_sense_callback(struct amdgpu_device *adev,
|
||||
struct dmub_notification *notify);
|
||||
void dm_pflip_high_irq(void *interrupt_params);
|
||||
void dm_vupdate_high_irq(void *interrupt_params);
|
||||
void dm_crtc_high_irq(void *interrupt_params);
|
||||
void dm_handle_hpd_work(struct work_struct *work);
|
||||
void dm_dmub_outbox1_low_irq(void *interrupt_params);
|
||||
int amdgpu_dm_irq_handler(struct amdgpu_device *adev,
|
||||
struct amdgpu_irq_src *source,
|
||||
struct amdgpu_iv_entry *entry);
|
||||
void dm_handle_vmin_vmax_update(struct work_struct *offload_work);
|
||||
#endif
|
||||
|
||||
#endif /* __AMDGPU_DM_IRQ_H__ */
|
||||
|
||||
@@ -502,6 +502,7 @@ void amdgpu_dm_ism_commit_event(struct amdgpu_dm_ism *ism,
|
||||
|
||||
} while (next_event < DM_ISM_NUM_EVENTS);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_ism_commit_event);
|
||||
|
||||
|
||||
static void dm_ism_delayed_work_func(struct work_struct *work)
|
||||
@@ -568,6 +569,7 @@ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm)
|
||||
disable_delayed_work_sync(&ism->sso_delayed_work);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_ism_disable);
|
||||
|
||||
/**
|
||||
* amdgpu_dm_ism_force_full_power - Force every CRTC's ISM FSM to FULL_POWER
|
||||
@@ -603,6 +605,7 @@ void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm)
|
||||
DM_ISM_EVENT_EXIT_IDLE_REQUESTED);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_ism_force_full_power);
|
||||
|
||||
/**
|
||||
* amdgpu_dm_ism_enable - enable the ISM
|
||||
@@ -626,6 +629,7 @@ void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm)
|
||||
enable_delayed_work(&ism->sso_delayed_work);
|
||||
}
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_ism_enable);
|
||||
|
||||
void amdgpu_dm_ism_init(struct amdgpu_dm_ism *ism,
|
||||
struct amdgpu_dm_ism_config *config)
|
||||
|
||||
@@ -862,10 +862,11 @@ void dm_handle_mst_sideband_msg_ready_event(
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_handle_mst_sideband_msg_ready_event);
|
||||
|
||||
static void dm_handle_mst_down_rep_msg_ready(struct drm_dp_mst_topology_mgr *mgr)
|
||||
STATIC_IFN_KUNIT void dm_handle_mst_down_rep_msg_ready(struct drm_dp_mst_topology_mgr *mgr)
|
||||
{
|
||||
dm_handle_mst_sideband_msg_ready_event(mgr, DOWN_REP_MSG_RDY_EVENT);
|
||||
}
|
||||
EXPORT_IF_KUNIT(dm_handle_mst_down_rep_msg_ready);
|
||||
|
||||
static const struct drm_dp_mst_topology_cbs dm_mst_cbs = {
|
||||
.add_connector = dm_dp_add_mst_connector,
|
||||
@@ -900,6 +901,7 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
|
||||
|
||||
drm_connector_attach_dp_subconnector_property(&aconnector->base);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_initialize_dp_connector);
|
||||
|
||||
uint32_t dm_mst_get_pbn_divider(struct dc_link *link)
|
||||
{
|
||||
|
||||
@@ -111,6 +111,7 @@ void dm_dp_aux_fill_payload_flags(u8 request, struct aux_payload *payload);
|
||||
ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg);
|
||||
u8 dm_mst_msg_ready_mask(enum mst_msg_ready_type msg_rdy_type);
|
||||
void dm_mst_select_esi_dpcd(u8 dpcd_rev, int *dpcd_addr, u8 *dpcd_bytes_to_read);
|
||||
void dm_handle_mst_down_rep_msg_ready(struct drm_dp_mst_topology_mgr *mgr);
|
||||
struct drm_encoder *dm_mst_atomic_best_encoder(struct drm_connector *connector,
|
||||
struct drm_atomic_commit *state);
|
||||
int dm_dp_mst_atomic_check(struct drm_connector *connector,
|
||||
|
||||
@@ -328,7 +328,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev,
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_validate_dcc);
|
||||
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx9_attrs_from_modifiers(struct amdgpu_device *adev,
|
||||
const struct amdgpu_framebuffer *afb,
|
||||
const enum surface_pixel_format format,
|
||||
const enum dc_rotation_angle rotation,
|
||||
@@ -374,13 +374,13 @@ STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(s
|
||||
|
||||
ret = amdgpu_dm_plane_validate_dcc(adev, format, rotation, tiling_info, dcc, address, plane_size);
|
||||
if (ret)
|
||||
drm_dbg_kms(adev_to_drm(adev), "amdgpu_dm_plane_validate_dcc: returned error: %d\n", ret);
|
||||
drm_dbg_kms(adev_to_drm(adev), "amdgpu_dm_plane_validate_dcc: returned error: %pe\n", ERR_PTR(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers);
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_attrs_from_modifiers);
|
||||
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amdgpu_device *adev,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx12_attrs_from_modifiers(struct amdgpu_device *adev,
|
||||
const struct amdgpu_framebuffer *afb,
|
||||
const enum surface_pixel_format format,
|
||||
const enum dc_rotation_angle rotation,
|
||||
@@ -415,11 +415,11 @@ STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(
|
||||
/* TODO: This seems wrong because there is no DCC plane on GFX12. */
|
||||
ret = amdgpu_dm_plane_validate_dcc(adev, format, rotation, tiling_info, dcc, address, plane_size);
|
||||
if (ret)
|
||||
drm_dbg_kms(adev_to_drm(adev), "amdgpu_dm_plane_validate_dcc: returned error: %d\n", ret);
|
||||
drm_dbg_kms(adev_to_drm(adev), "amdgpu_dm_plane_validate_dcc: returned: %pe\n", ERR_PTR(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers);
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx12_attrs_from_modifiers);
|
||||
|
||||
static void amdgpu_dm_plane_add_gfx10_1_modifiers(const struct amdgpu_device *adev,
|
||||
uint64_t **mods,
|
||||
@@ -927,14 +927,14 @@ int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev,
|
||||
}
|
||||
|
||||
if (adev->family == AMDGPU_FAMILY_GC_12_0_0) {
|
||||
ret = amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(adev, afb, format,
|
||||
ret = amdgpu_dm_plane_fill_gfx12_attrs_from_modifiers(adev, afb, format,
|
||||
rotation, plane_size,
|
||||
tiling_info, dcc,
|
||||
address);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (adev->family >= AMDGPU_FAMILY_AI) {
|
||||
ret = amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(adev, afb, format,
|
||||
ret = amdgpu_dm_plane_fill_gfx9_attrs_from_modifiers(adev, afb, format,
|
||||
rotation, plane_size,
|
||||
tiling_info, dcc,
|
||||
address);
|
||||
@@ -975,13 +975,15 @@ static int amdgpu_dm_plane_helper_prepare_fb(struct drm_plane *plane,
|
||||
adev = amdgpu_ttm_adev(rbo->tbo.bdev);
|
||||
r = amdgpu_bo_reserve(rbo, true);
|
||||
if (r) {
|
||||
drm_err(adev_to_drm(adev), "fail to reserve bo (%d)\n", r);
|
||||
drm_err(adev_to_drm(adev), "fail to reserve bo: %pe\n", ERR_PTR(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dma_resv_reserve_fences(rbo->tbo.base.resv, TTM_NUM_MOVE_FENCES);
|
||||
if (r)
|
||||
if (r) {
|
||||
drm_err(adev_to_drm(adev), "reserving fence slot failed: %pe\n", ERR_PTR(r));
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
if (plane->type != DRM_PLANE_TYPE_CURSOR)
|
||||
domain = amdgpu_display_supported_domains(adev, rbo->flags);
|
||||
@@ -992,13 +994,13 @@ static int amdgpu_dm_plane_helper_prepare_fb(struct drm_plane *plane,
|
||||
r = amdgpu_bo_pin(rbo, domain);
|
||||
if (unlikely(r != 0)) {
|
||||
if (r != -ERESTARTSYS)
|
||||
DRM_ERROR("Failed to pin framebuffer with error %d\n", r);
|
||||
DRM_ERROR("Failed to pin framebuffer: %pe\n", ERR_PTR(r));
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
r = amdgpu_ttm_alloc_gart(&rbo->tbo);
|
||||
if (unlikely(r != 0)) {
|
||||
DRM_ERROR("%p bind failed\n", rbo);
|
||||
DRM_ERROR("%p bind failed: %pe\n", rbo, ERR_PTR(r));
|
||||
goto error_unpin;
|
||||
}
|
||||
|
||||
@@ -1058,7 +1060,7 @@ static void amdgpu_dm_plane_helper_cleanup_fb(struct drm_plane *plane,
|
||||
rbo = gem_to_amdgpu_bo(old_state->fb->obj[0]);
|
||||
r = amdgpu_bo_reserve(rbo, false);
|
||||
if (unlikely(r)) {
|
||||
DRM_ERROR("failed to reserve rbo before unpin\n");
|
||||
DRM_ERROR("failed to reserve rbo before unpin: %pe\n", ERR_PTR(r));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1254,8 +1256,8 @@ int amdgpu_dm_plane_fill_dc_scaling_info(struct amdgpu_device *adev,
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_dc_scaling_info);
|
||||
|
||||
static int amdgpu_dm_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_commit *state)
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_commit *state)
|
||||
{
|
||||
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
|
||||
plane);
|
||||
@@ -1300,9 +1302,10 @@ static int amdgpu_dm_plane_atomic_check(struct drm_plane *plane,
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_atomic_check);
|
||||
|
||||
static int amdgpu_dm_plane_atomic_async_check(struct drm_plane *plane,
|
||||
struct drm_atomic_commit *state, bool flip)
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_plane_atomic_async_check(struct drm_plane *plane,
|
||||
struct drm_atomic_commit *state, bool flip)
|
||||
{
|
||||
struct drm_crtc_state *new_crtc_state;
|
||||
struct drm_plane_state *new_plane_state;
|
||||
@@ -1324,6 +1327,7 @@ static int amdgpu_dm_plane_atomic_async_check(struct drm_plane *plane,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_atomic_async_check);
|
||||
|
||||
int amdgpu_dm_plane_get_cursor_position(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
struct dc_cursor_position *position)
|
||||
@@ -1483,7 +1487,7 @@ static void amdgpu_dm_plane_atomic_async_update(struct drm_plane *plane,
|
||||
amdgpu_dm_plane_handle_cursor_update(plane, old_state);
|
||||
}
|
||||
|
||||
static void amdgpu_dm_plane_panic_flush(struct drm_plane *plane)
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_plane_panic_flush(struct drm_plane *plane)
|
||||
{
|
||||
struct dm_plane_state *dm_plane_state = to_dm_plane_state(plane->state);
|
||||
struct drm_framebuffer *fb = plane->state->fb;
|
||||
@@ -1496,6 +1500,7 @@ static void amdgpu_dm_plane_panic_flush(struct drm_plane *plane)
|
||||
|
||||
dc_plane_force_dcc_and_tiling_disable(dc_plane_state, fb->modifier ? true : false);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_panic_flush);
|
||||
|
||||
static const struct drm_plane_helper_funcs dm_plane_helper_funcs = {
|
||||
.prepare_fb = amdgpu_dm_plane_helper_prepare_fb,
|
||||
@@ -1515,7 +1520,7 @@ static const struct drm_plane_helper_funcs dm_primary_plane_helper_funcs = {
|
||||
.panic_flush = amdgpu_dm_plane_panic_flush,
|
||||
};
|
||||
|
||||
static void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane)
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane)
|
||||
{
|
||||
struct dm_plane_state *amdgpu_state;
|
||||
|
||||
@@ -1532,8 +1537,10 @@ static void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane)
|
||||
amdgpu_state->shaper_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
|
||||
amdgpu_state->blend_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_drm_plane_reset);
|
||||
|
||||
static struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct drm_plane *plane)
|
||||
STATIC_IFN_KUNIT struct drm_plane_state *
|
||||
amdgpu_dm_plane_drm_plane_duplicate_state(struct drm_plane *plane)
|
||||
{
|
||||
struct dm_plane_state *dm_plane_state, *old_dm_plane_state;
|
||||
|
||||
@@ -1572,6 +1579,7 @@ static struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct
|
||||
|
||||
return &dm_plane_state->base;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_drm_plane_duplicate_state);
|
||||
|
||||
STATIC_IFN_KUNIT bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane,
|
||||
uint32_t format,
|
||||
@@ -1636,8 +1644,8 @@ STATIC_IFN_KUNIT bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *pla
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_format_mod_supported);
|
||||
|
||||
static void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state)
|
||||
{
|
||||
struct dm_plane_state *dm_plane_state = to_dm_plane_state(state);
|
||||
|
||||
@@ -1657,6 +1665,7 @@ static void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane,
|
||||
|
||||
drm_atomic_helper_plane_destroy_state(plane, state);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_plane_drm_plane_destroy_state);
|
||||
|
||||
#ifdef AMD_PRIVATE_COLOR
|
||||
static void
|
||||
@@ -1861,8 +1870,8 @@ dm_plane_init_colorops(struct drm_plane *plane)
|
||||
if (dc->ctx->dce_version >= DCN_VERSION_3_0) {
|
||||
ret = amdgpu_dm_initialize_default_pipeline(plane, &pipelines[len]);
|
||||
if (ret) {
|
||||
drm_err(plane->dev, "Failed to create color pipeline for plane %d: %d\n",
|
||||
plane->base.id, ret);
|
||||
drm_err(plane->dev, "Failed to create color pipeline for plane %d: %pe\n",
|
||||
plane->base.id, ERR_PTR(ret));
|
||||
goto out;
|
||||
}
|
||||
len++;
|
||||
|
||||
@@ -92,7 +92,7 @@ int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev,
|
||||
int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane,
|
||||
const struct dc_plane_cap *plane_cap,
|
||||
uint32_t *formats, int max_formats);
|
||||
int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev,
|
||||
int amdgpu_dm_plane_fill_gfx9_attrs_from_modifiers(struct amdgpu_device *adev,
|
||||
const struct amdgpu_framebuffer *afb,
|
||||
const enum surface_pixel_format format,
|
||||
const enum dc_rotation_angle rotation,
|
||||
@@ -100,7 +100,7 @@ int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_devi
|
||||
struct dc_tiling_info *tiling_info,
|
||||
struct dc_plane_dcc_param *dcc,
|
||||
struct dc_plane_address *address);
|
||||
int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amdgpu_device *adev,
|
||||
int amdgpu_dm_plane_fill_gfx12_attrs_from_modifiers(struct amdgpu_device *adev,
|
||||
const struct amdgpu_framebuffer *afb,
|
||||
const enum surface_pixel_format format,
|
||||
const enum dc_rotation_angle rotation,
|
||||
@@ -115,5 +115,14 @@ void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev,
|
||||
struct drm_framebuffer *fb,
|
||||
int *min_downscale,
|
||||
int *max_upscale);
|
||||
int amdgpu_dm_plane_atomic_async_check(struct drm_plane *plane,
|
||||
struct drm_atomic_commit *state, bool flip);
|
||||
int amdgpu_dm_plane_atomic_check(struct drm_plane *plane,
|
||||
struct drm_atomic_commit *state);
|
||||
void amdgpu_dm_plane_panic_flush(struct drm_plane *plane);
|
||||
void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane);
|
||||
struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct drm_plane *plane);
|
||||
void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane,
|
||||
struct drm_plane_state *state);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -85,7 +85,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_wb_connector_get_modes(struct drm_connector *conn
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_wb_connector_get_modes);
|
||||
|
||||
static int amdgpu_dm_wb_prepare_job(struct drm_writeback_connector *wb_connector,
|
||||
STATIC_IFN_KUNIT int amdgpu_dm_wb_prepare_job(struct drm_writeback_connector *wb_connector,
|
||||
struct drm_writeback_job *job)
|
||||
{
|
||||
struct amdgpu_framebuffer *afb;
|
||||
@@ -107,13 +107,15 @@ static int amdgpu_dm_wb_prepare_job(struct drm_writeback_connector *wb_connector
|
||||
|
||||
r = amdgpu_bo_reserve(rbo, true);
|
||||
if (r) {
|
||||
drm_err(adev_to_drm(adev), "fail to reserve bo (%d)\n", r);
|
||||
drm_err(adev_to_drm(adev), "fail to reserve bo: %pe\n", ERR_PTR(r));
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dma_resv_reserve_fences(rbo->tbo.base.resv, TTM_NUM_MOVE_FENCES);
|
||||
if (r)
|
||||
if (r) {
|
||||
drm_err(adev_to_drm(adev), "reserving fence slot failed: %pe\n", ERR_PTR(r));
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
domain = amdgpu_display_supported_domains(adev, rbo->flags);
|
||||
|
||||
@@ -121,13 +123,13 @@ static int amdgpu_dm_wb_prepare_job(struct drm_writeback_connector *wb_connector
|
||||
r = amdgpu_bo_pin(rbo, domain);
|
||||
if (unlikely(r != 0)) {
|
||||
if (r != -ERESTARTSYS)
|
||||
DRM_ERROR("Failed to pin framebuffer with error %d\n", r);
|
||||
DRM_ERROR("Failed to pin framebuffer: %pe\n", ERR_PTR(r));
|
||||
goto error_unlock;
|
||||
}
|
||||
|
||||
r = amdgpu_ttm_alloc_gart(&rbo->tbo);
|
||||
if (unlikely(r != 0)) {
|
||||
DRM_ERROR("%p bind failed\n", rbo);
|
||||
DRM_ERROR("%p bind failed: %pe\n", rbo, ERR_PTR(r));
|
||||
goto error_unpin;
|
||||
}
|
||||
|
||||
@@ -146,8 +148,9 @@ static int amdgpu_dm_wb_prepare_job(struct drm_writeback_connector *wb_connector
|
||||
amdgpu_bo_unreserve(rbo);
|
||||
return r;
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_wb_prepare_job);
|
||||
|
||||
static void amdgpu_dm_wb_cleanup_job(struct drm_writeback_connector *connector,
|
||||
STATIC_IFN_KUNIT void amdgpu_dm_wb_cleanup_job(struct drm_writeback_connector *connector,
|
||||
struct drm_writeback_job *job)
|
||||
{
|
||||
struct amdgpu_bo *rbo;
|
||||
@@ -159,7 +162,7 @@ static void amdgpu_dm_wb_cleanup_job(struct drm_writeback_connector *connector,
|
||||
rbo = gem_to_amdgpu_bo(job->fb->obj[0]);
|
||||
r = amdgpu_bo_reserve(rbo, false);
|
||||
if (unlikely(r)) {
|
||||
DRM_ERROR("failed to reserve rbo before unpin\n");
|
||||
DRM_ERROR("failed to reserve rbo before unpin: %pe\n", ERR_PTR(r));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -167,6 +170,7 @@ static void amdgpu_dm_wb_cleanup_job(struct drm_writeback_connector *connector,
|
||||
amdgpu_bo_unreserve(rbo);
|
||||
amdgpu_bo_unref(&rbo);
|
||||
}
|
||||
EXPORT_IF_KUNIT(amdgpu_dm_wb_cleanup_job);
|
||||
|
||||
static const struct drm_encoder_helper_funcs amdgpu_dm_wb_encoder_helper_funcs = {
|
||||
.atomic_check = amdgpu_dm_wb_encoder_atomic_check,
|
||||
|
||||
@@ -44,6 +44,10 @@ int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state);
|
||||
int amdgpu_dm_wb_connector_get_modes(struct drm_connector *connector);
|
||||
int amdgpu_dm_wb_prepare_job(struct drm_writeback_connector *wb_connector,
|
||||
struct drm_writeback_job *job);
|
||||
void amdgpu_dm_wb_cleanup_job(struct drm_writeback_connector *connector,
|
||||
struct drm_writeback_job *job);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
*/
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include <drm/drm_audio_component.h>
|
||||
|
||||
#include "dc.h"
|
||||
#include "dc/inc/core_types.h"
|
||||
#include "dc/inc/hw/audio.h"
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_mode.h"
|
||||
#include "amdgpu_dm.h"
|
||||
@@ -38,6 +41,57 @@ static void dm_test_audio_init_disabled(struct kunit *test)
|
||||
amdgpu_dm_audio_set_param(saved_audio);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_audio_init_enabled_success - Test init deeper path when audio is enabled
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_audio_init_enabled_success(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct dc *dc;
|
||||
struct resource_pool *res_pool;
|
||||
struct audio *audio0;
|
||||
struct audio *audio1;
|
||||
struct device *dev;
|
||||
int saved_audio = amdgpu_dm_audio_get_param();
|
||||
|
||||
adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL);
|
||||
audio0 = kunit_kzalloc(test, sizeof(*audio0), GFP_KERNEL);
|
||||
audio1 = kunit_kzalloc(test, sizeof(*audio1), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res_pool);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio0);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio1);
|
||||
|
||||
dev = root_device_register("kunit-dm-audio-init");
|
||||
KUNIT_ASSERT_FALSE(test, IS_ERR(dev));
|
||||
|
||||
audio0->inst = 2;
|
||||
audio1->inst = 6;
|
||||
res_pool->audio_count = 2;
|
||||
res_pool->audios[0] = audio0;
|
||||
res_pool->audios[1] = audio1;
|
||||
dc->res_pool = res_pool;
|
||||
adev->dm.dc = dc;
|
||||
adev->dev = dev;
|
||||
|
||||
amdgpu_dm_audio_set_param(1);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, amdgpu_dm_audio_init(adev), 0);
|
||||
KUNIT_EXPECT_TRUE(test, adev->mode_info.audio.enabled);
|
||||
KUNIT_EXPECT_TRUE(test, adev->dm.audio_registered);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.num_pins, 2);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[0].id, 2U);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[1].id, 6U);
|
||||
|
||||
amdgpu_dm_audio_fini(adev);
|
||||
root_device_unregister(dev);
|
||||
amdgpu_dm_audio_set_param(saved_audio);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_audio_fini() */
|
||||
|
||||
/**
|
||||
@@ -455,9 +509,73 @@ static void dm_test_eld_notify_null_callback(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_audio_init_pins() */
|
||||
|
||||
/**
|
||||
* dm_test_audio_init_pins_sets_defaults - pin entries are initialised to default values
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* amdgpu_dm_audio_init_pins() must set num_pins from audio_count, reset every
|
||||
* pin to the sentinel defaults (-1 for rate/channels/bits, 0 for the rest)
|
||||
* and copy each pin's hardware instance index from res_pool->audios[i]->inst.
|
||||
*/
|
||||
static void dm_test_audio_init_pins_sets_defaults(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
const unsigned int inst_array[] = {3, 7};
|
||||
int i;
|
||||
|
||||
adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
|
||||
|
||||
amdgpu_dm_audio_init_pins(adev, 2, inst_array);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.num_pins, 2);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[i].channels, -1);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[i].rate, -1);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[i].bits_per_sample, -1);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[i].status_bits, 0);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[i].category_code, 0);
|
||||
KUNIT_EXPECT_FALSE(test, adev->mode_info.audio.pin[i].connected);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[i].offset, 0);
|
||||
}
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[0].id, 3U);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[1].id, 7U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_audio_init_pins_zero_count - zero audio_count leaves num_pins at zero
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* When res_pool->audio_count is 0, num_pins must be 0 and no pins touched.
|
||||
*/
|
||||
static void dm_test_audio_init_pins_zero_count(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
|
||||
adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev);
|
||||
|
||||
/* Pre-fill a sentinel so we can confirm the loop never ran. */
|
||||
adev->mode_info.audio.pin[0].channels = 99;
|
||||
|
||||
amdgpu_dm_audio_init_pins(adev, 0, NULL);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.num_pins, 0);
|
||||
KUNIT_EXPECT_EQ(test, adev->mode_info.audio.pin[0].channels, 99);
|
||||
}
|
||||
|
||||
/* End of tests for amdgpu_dm_audio_init_pins() */
|
||||
|
||||
static struct kunit_case dm_audio_test_cases[] = {
|
||||
/* amdgpu_dm_audio_init */
|
||||
KUNIT_CASE(dm_test_audio_init_disabled),
|
||||
KUNIT_CASE(dm_test_audio_init_enabled_success),
|
||||
/* amdgpu_dm_audio_init_pins */
|
||||
KUNIT_CASE(dm_test_audio_init_pins_sets_defaults),
|
||||
KUNIT_CASE(dm_test_audio_init_pins_zero_count),
|
||||
/* amdgpu_dm_audio_fini */
|
||||
KUNIT_CASE(dm_test_audio_fini_without_enabled_audio),
|
||||
/* amdgpu_dm_fill_audio_info */
|
||||
|
||||
@@ -8,13 +8,21 @@
|
||||
#include <kunit/test.h>
|
||||
#include <linux/backlight.h>
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_mode_config.h>
|
||||
#include <drm/drm_property.h>
|
||||
|
||||
#include "dc.h"
|
||||
#include "dc_dmub_srv.h"
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_display.h"
|
||||
#include "amdgpu_mode.h"
|
||||
#include "amdgpu_dm.h"
|
||||
#include "amdgpu_dm_backlight.h"
|
||||
#include "amdgpu_dm_kunit_test_helpers.h"
|
||||
#include "amd_shared.h"
|
||||
#include "link_service.h"
|
||||
#include "dc/inc/hw/panel_cntl.h"
|
||||
|
||||
struct dm_backlight_connector_fixture {
|
||||
@@ -23,6 +31,12 @@ struct dm_backlight_connector_fixture {
|
||||
struct dc_link *link;
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs dm_backlight_test_connector_funcs = {
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static void setup_test_connector(struct kunit *test,
|
||||
struct dm_backlight_connector_fixture *fixture,
|
||||
int bl_idx, enum signal_type signal)
|
||||
@@ -40,6 +54,587 @@ static void setup_test_connector(struct kunit *test,
|
||||
fixture->link->connector_signal = signal;
|
||||
}
|
||||
|
||||
static void setup_test_dm_ddev(struct kunit *test, struct amdgpu_display_manager *dm)
|
||||
{
|
||||
struct drm_device *ddev;
|
||||
|
||||
ddev = kunit_kzalloc(test, sizeof(*ddev), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ddev);
|
||||
|
||||
INIT_LIST_HEAD(&ddev->mode_config.connector_list);
|
||||
spin_lock_init(&ddev->mode_config.connector_list_lock);
|
||||
dm->ddev = ddev;
|
||||
}
|
||||
|
||||
/* Tests for dm_find_stream_with_link() */
|
||||
|
||||
/**
|
||||
* dm_test_find_stream_with_link_returns_match - Test matching stream lookup
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_find_stream_with_link_returns_match(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *other_link = dm_kunit_alloc_link(test);
|
||||
struct dc_link *target_link = dm_kunit_alloc_link(test);
|
||||
struct dc_stream_state *stream;
|
||||
|
||||
dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, other_link);
|
||||
dm_kunit_add_stream_to_state(test, dm->dc->current_state, 1, target_link);
|
||||
stream = dm_find_stream_with_link(dm, target_link);
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, stream);
|
||||
KUNIT_EXPECT_PTR_EQ(test, stream->link, target_link);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_find_stream_with_link_missing - Test missing stream lookup
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_find_stream_with_link_missing(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *stream_link = dm_kunit_alloc_link(test);
|
||||
struct dc_link *missing_link = dm_kunit_alloc_link(test);
|
||||
|
||||
dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, stream_link);
|
||||
|
||||
KUNIT_EXPECT_NULL(test, dm_find_stream_with_link(dm, missing_link));
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_backlight_set_level() */
|
||||
|
||||
/**
|
||||
* dm_test_backlight_set_level_connector_off - Test connector-off cache path
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* If the matching connector has no encoder, set_level() must cache the
|
||||
* requested brightness and return before touching DC or backlight hardware.
|
||||
*/
|
||||
static void dm_test_backlight_set_level_connector_off(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
|
||||
setup_test_dm_ddev(test, dm);
|
||||
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector);
|
||||
INIT_LIST_HEAD(&aconnector->base.head);
|
||||
aconnector->bl_idx = 1;
|
||||
aconnector->base.encoder = NULL;
|
||||
list_add_tail(&aconnector->base.head, &dm->ddev->mode_config.connector_list);
|
||||
|
||||
amdgpu_dm_backlight_set_level(dm, 1, 1234);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm->brightness[1], 1234U);
|
||||
KUNIT_EXPECT_EQ(test, dm->actual_brightness[1], 0U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_backlight_set_level_no_stream - Test no-stream early return
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With no stream for the backlight link, set_level() records the requested
|
||||
* brightness and exits before calling the power-module programming path.
|
||||
*/
|
||||
static void dm_test_backlight_set_level_no_stream(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *link = dm_kunit_alloc_link(test);
|
||||
|
||||
setup_test_dm_ddev(test, dm);
|
||||
dm->backlight_caps[1].caps_valid = true;
|
||||
dm->backlight_caps[1].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
|
||||
dm->backlight_caps[1].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
|
||||
dm->backlight_link[1] = link;
|
||||
|
||||
amdgpu_dm_backlight_set_level(dm, 1, 2000);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm->brightness[1], 2000U);
|
||||
KUNIT_EXPECT_EQ(test, dm->actual_brightness[1], 0U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_backlight_set_level_aux_programs_power_module - Test AUX programming path
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With a matching stream present, set_level() walks into the DC programming
|
||||
* path. A NULL power_module makes mod_power_set_backlight_nits() a safe
|
||||
* early-false, and ips_support disabled leaves idle optimizations untouched.
|
||||
* A non-matching connector exercises the connector-list skip, and a non-zero
|
||||
* brightness_mask exercises the quirk-OR path.
|
||||
*/
|
||||
static void dm_test_backlight_set_level_aux_programs_power_module(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *link = dm_kunit_alloc_link(test);
|
||||
struct amdgpu_dm_connector *other;
|
||||
|
||||
setup_test_dm_ddev(test, dm);
|
||||
mutex_init(&dm->dc_lock);
|
||||
dm->power_module = NULL;
|
||||
|
||||
/* Non-matching connector exercises the bl_idx skip (continue). */
|
||||
other = kunit_kzalloc(test, sizeof(*other), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, other);
|
||||
INIT_LIST_HEAD(&other->base.head);
|
||||
other->bl_idx = 0;
|
||||
list_add_tail(&other->base.head, &dm->ddev->mode_config.connector_list);
|
||||
|
||||
dm->backlight_caps[1].caps_valid = true;
|
||||
dm->backlight_caps[1].aux_support = true;
|
||||
dm->backlight_caps[1].brightness_mask = 0x3;
|
||||
dm->backlight_caps[1].aux_min_input_signal = 1;
|
||||
dm->backlight_caps[1].aux_max_input_signal = 512;
|
||||
dm->backlight_caps[1].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
|
||||
dm->backlight_caps[1].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
|
||||
dm->backlight_link[1] = link;
|
||||
dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, link);
|
||||
|
||||
amdgpu_dm_backlight_set_level(dm, 1, 2000);
|
||||
|
||||
/* power_module is NULL so programming fails; actual stays unchanged. */
|
||||
KUNIT_EXPECT_EQ(test, dm->brightness[1], 2000U);
|
||||
KUNIT_EXPECT_EQ(test, dm->actual_brightness[1], 0U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_backlight_set_level_pwm_programs_power_module - Test PWM programming path
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* With aux_support cleared, set_level() takes the millipercent branch:
|
||||
* get_brightness_range() + mod_power_set_backlight_percent(). A NULL
|
||||
* power_module keeps the call a safe early-false.
|
||||
*/
|
||||
static void dm_test_backlight_set_level_pwm_programs_power_module(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *link = dm_kunit_alloc_link(test);
|
||||
|
||||
setup_test_dm_ddev(test, dm);
|
||||
mutex_init(&dm->dc_lock);
|
||||
dm->power_module = NULL;
|
||||
|
||||
dm->backlight_caps[1].caps_valid = true;
|
||||
dm->backlight_caps[1].aux_support = false;
|
||||
dm->backlight_caps[1].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
|
||||
dm->backlight_caps[1].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
|
||||
dm->backlight_link[1] = link;
|
||||
dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, link);
|
||||
|
||||
amdgpu_dm_backlight_set_level(dm, 1, 2000);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm->brightness[1], 2000U);
|
||||
KUNIT_EXPECT_EQ(test, dm->actual_brightness[1], 0U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_backlight_set_level_reallows_idle - Test idle-optimization toggle path
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* When ips_support is set and dmub idle is allowed, set_level() disables idle
|
||||
* optimizations around the programming call and re-enables them afterwards.
|
||||
* disable_idle_power_optimizations keeps dc_allow_idle_optimizations() a safe
|
||||
* early return, and ctx->logger is wired because DC_LOG_* dereferences it.
|
||||
*/
|
||||
static void dm_test_backlight_set_level_reallows_idle(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *link = dm_kunit_alloc_link(test);
|
||||
struct dc_dmub_srv *dmub_srv;
|
||||
struct dal_logger *logger;
|
||||
struct dc_context *ctx;
|
||||
|
||||
setup_test_dm_ddev(test, dm);
|
||||
mutex_init(&dm->dc_lock);
|
||||
dm->power_module = NULL;
|
||||
|
||||
/* dm_kunit_alloc_dm() leaves dc->ctx NULL; the idle path dereferences it. */
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
dm->dc->ctx = ctx;
|
||||
|
||||
logger = kunit_kzalloc(test, sizeof(*logger), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, logger);
|
||||
logger->dev = &adev->ddev;
|
||||
dm->dc->ctx->logger = logger;
|
||||
|
||||
dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub_srv);
|
||||
dmub_srv->idle_allowed = true;
|
||||
dm->dc->ctx->dmub_srv = dmub_srv;
|
||||
dm->dc->caps.ips_support = true;
|
||||
/* Keep dc_allow_idle_optimizations() a safe early return. */
|
||||
dm->dc->debug.disable_idle_power_optimizations = true;
|
||||
|
||||
dm->backlight_caps[1].caps_valid = true;
|
||||
dm->backlight_caps[1].aux_support = true;
|
||||
dm->backlight_caps[1].aux_min_input_signal = 1;
|
||||
dm->backlight_caps[1].aux_max_input_signal = 512;
|
||||
dm->backlight_caps[1].min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT;
|
||||
dm->backlight_caps[1].max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT;
|
||||
dm->backlight_link[1] = link;
|
||||
dm_kunit_add_stream_to_state(test, dm->dc->current_state, 0, link);
|
||||
|
||||
amdgpu_dm_backlight_set_level(dm, 1, 2000);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm->brightness[1], 2000U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_backlight_update_status_no_stream - Test update_status wrapper
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_backlight_update_status_no_stream(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct backlight_device *bd;
|
||||
|
||||
setup_test_dm_ddev(test, dm);
|
||||
bd = kunit_kzalloc(test, sizeof(*bd), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bd);
|
||||
dev_set_drvdata(&bd->dev, dm);
|
||||
bd->props.brightness = 3456;
|
||||
dm->num_of_edps = 2;
|
||||
dm->backlight_dev[1] = bd;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_update_status(bd), 0);
|
||||
KUNIT_EXPECT_EQ(test, dm->brightness[1], 3456U);
|
||||
}
|
||||
|
||||
static void setup_test_link_service(struct kunit *test, struct dc_link *link)
|
||||
{
|
||||
struct link_service *link_srv;
|
||||
struct dc_context *ctx;
|
||||
struct dc *dc;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link_srv);
|
||||
|
||||
dc->ctx = ctx;
|
||||
dc->link_srv = link_srv;
|
||||
ctx->dc = dc;
|
||||
link->dc = dc;
|
||||
link->ctx = ctx;
|
||||
}
|
||||
|
||||
static int dm_test_get_backlight_level_mid(const struct dc_link *link)
|
||||
{
|
||||
return (0x101 * AMDGPU_DM_DEFAULT_MIN_BACKLIGHT) + 1000;
|
||||
}
|
||||
|
||||
static int dm_test_get_backlight_level_error(const struct dc_link *link)
|
||||
{
|
||||
return DC_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
static bool dm_test_get_backlight_level_nits(struct dc_link *link,
|
||||
uint32_t *avg,
|
||||
uint32_t *peak)
|
||||
{
|
||||
*avg = 250000;
|
||||
*peak = 300000;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dm_test_get_backlight_level_nits_fail(struct dc_link *link,
|
||||
uint32_t *avg,
|
||||
uint32_t *peak)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_backlight_get_level()/get_brightness() */
|
||||
|
||||
/**
|
||||
* dm_test_backlight_get_level_pwm_success - Test PWM brightness readback
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_backlight_get_level_pwm_success(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *link = dm_kunit_alloc_link(test);
|
||||
u32 hw_level = dm_test_get_backlight_level_mid(link);
|
||||
|
||||
setup_test_link_service(test, link);
|
||||
link->dc->link_srv->edp_get_backlight_level = dm_test_get_backlight_level_mid;
|
||||
dm->backlight_link[0] = link;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_get_level(dm, 0),
|
||||
convert_brightness_to_user(&dm->backlight_caps[0], hw_level));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_backlight_get_level_pwm_error - Test PWM readback fallback
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_backlight_get_level_pwm_error(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *link = dm_kunit_alloc_link(test);
|
||||
|
||||
setup_test_link_service(test, link);
|
||||
link->dc->link_srv->edp_get_backlight_level = dm_test_get_backlight_level_error;
|
||||
dm->brightness[0] = 4321;
|
||||
dm->backlight_link[0] = link;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_get_level(dm, 0), 4321U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_backlight_get_level_aux_success - Test AUX brightness readback
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_backlight_get_level_aux_success(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *link = dm_kunit_alloc_link(test);
|
||||
struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0];
|
||||
|
||||
setup_test_link_service(test, link);
|
||||
link->dc->link_srv->edp_get_backlight_level_nits = dm_test_get_backlight_level_nits;
|
||||
dm->backlight_link[0] = link;
|
||||
caps->caps_valid = true;
|
||||
caps->aux_support = true;
|
||||
caps->aux_min_input_signal = 1;
|
||||
caps->aux_max_input_signal = 512;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_get_level(dm, 0),
|
||||
convert_brightness_to_user(caps, 250000));
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_backlight_get_level_aux_error - Test AUX readback fallback
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_backlight_get_level_aux_error(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *link = dm_kunit_alloc_link(test);
|
||||
struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0];
|
||||
|
||||
setup_test_link_service(test, link);
|
||||
link->dc->link_srv->edp_get_backlight_level_nits = dm_test_get_backlight_level_nits_fail;
|
||||
dm->brightness[0] = 6789;
|
||||
dm->backlight_link[0] = link;
|
||||
caps->caps_valid = true;
|
||||
caps->aux_support = true;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_get_level(dm, 0), 6789U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_backlight_get_brightness_uses_device_index - Test get_brightness wrapper
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_backlight_get_brightness_uses_device_index(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test);
|
||||
struct dc_link *link = dm_kunit_alloc_link(test);
|
||||
struct backlight_device *bd;
|
||||
|
||||
setup_test_link_service(test, link);
|
||||
link->dc->link_srv->edp_get_backlight_level = dm_test_get_backlight_level_error;
|
||||
bd = kunit_kzalloc(test, sizeof(*bd), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, bd);
|
||||
dev_set_drvdata(&bd->dev, dm);
|
||||
dm->num_of_edps = 2;
|
||||
dm->backlight_dev[1] = bd;
|
||||
dm->brightness[1] = 2468;
|
||||
dm->backlight_link[1] = link;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_get_brightness(bd), 2468);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_register_backlight_device() */
|
||||
|
||||
/**
|
||||
* dm_test_register_backlight_device_negative_index - Test invalid index no-op
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_register_backlight_device_negative_index(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev = dm_kunit_alloc_adev(test);
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
aconnector->bl_idx = -1;
|
||||
|
||||
amdgpu_dm_register_backlight_device(aconnector);
|
||||
KUNIT_EXPECT_NULL(test, adev->dm.backlight_dev[0]);
|
||||
}
|
||||
|
||||
static struct drm_connector *setup_panel_power_savings_connector(struct kunit *test,
|
||||
struct device **device_out,
|
||||
struct dm_connector_state **state_out)
|
||||
{
|
||||
struct dm_connector_state *state;
|
||||
struct drm_connector *connector;
|
||||
struct amdgpu_device *adev;
|
||||
struct device *device;
|
||||
int ret;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
ret = drmm_mode_config_init(&adev->ddev);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
|
||||
device = kunit_kzalloc(test, sizeof(*device), GFP_KERNEL);
|
||||
state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, device);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
|
||||
|
||||
connector->dev = &adev->ddev;
|
||||
connector->state = &state->base;
|
||||
dev_set_drvdata(device, connector);
|
||||
*device_out = device;
|
||||
*state_out = state;
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
static void dm_test_free_sysfs_buf(void *data)
|
||||
{
|
||||
free_page((unsigned long)data);
|
||||
}
|
||||
|
||||
static char *dm_test_alloc_sysfs_buf(struct kunit *test)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
buf = (char *)get_zeroed_page(GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, buf);
|
||||
KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_free_sysfs_buf, buf), 0);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Tests for panel_power_savings_show()/panel_power_savings_store() */
|
||||
|
||||
/**
|
||||
* dm_test_panel_power_savings_show_maps_disable_to_zero - Test show output
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_panel_power_savings_show_maps_disable_to_zero(struct kunit *test)
|
||||
{
|
||||
struct dm_connector_state *state;
|
||||
struct device *device;
|
||||
char *buf;
|
||||
|
||||
setup_panel_power_savings_connector(test, &device, &state);
|
||||
buf = dm_test_alloc_sysfs_buf(test);
|
||||
state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, panel_power_savings_show(device, NULL, buf), 2);
|
||||
KUNIT_EXPECT_STREQ(test, buf, "0\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_panel_power_savings_show_reports_level - Test show output for active level
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* When abm_level is not the immediate-disable sentinel, show() reports the
|
||||
* raw level value.
|
||||
*/
|
||||
static void dm_test_panel_power_savings_show_reports_level(struct kunit *test)
|
||||
{
|
||||
struct dm_connector_state *state;
|
||||
struct device *device;
|
||||
char *buf;
|
||||
|
||||
setup_panel_power_savings_connector(test, &device, &state);
|
||||
buf = dm_test_alloc_sysfs_buf(test);
|
||||
state->abm_level = 3;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, panel_power_savings_show(device, NULL, buf), 2);
|
||||
KUNIT_EXPECT_STREQ(test, buf, "3\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_panel_power_savings_store_sets_disable - Test zero maps to disable
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_panel_power_savings_store_sets_disable(struct kunit *test)
|
||||
{
|
||||
struct dm_connector_state *state;
|
||||
struct device *device;
|
||||
size_t count = strlen("0");
|
||||
|
||||
setup_panel_power_savings_connector(test, &device, &state);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, panel_power_savings_store(device, NULL, "0", count),
|
||||
(ssize_t)count);
|
||||
KUNIT_EXPECT_EQ(test, state->abm_level, ABM_LEVEL_IMMEDIATE_DISABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_panel_power_savings_store_forbidden - Test forbidden update
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_panel_power_savings_store_forbidden(struct kunit *test)
|
||||
{
|
||||
struct dm_connector_state *state;
|
||||
struct device *device;
|
||||
|
||||
setup_panel_power_savings_connector(test, &device, &state);
|
||||
state->abm_sysfs_forbidden = true;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, panel_power_savings_store(device, NULL, "1", 1), -EBUSY);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_panel_power_savings_store_rejects_invalid_text - Test parse failure
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_panel_power_savings_store_rejects_invalid_text(struct kunit *test)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct drm_device *drm;
|
||||
struct device *device;
|
||||
|
||||
connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
|
||||
drm = kunit_kzalloc(test, sizeof(*drm), GFP_KERNEL);
|
||||
device = kunit_kzalloc(test, sizeof(*device), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, device);
|
||||
|
||||
connector->dev = drm;
|
||||
dev_set_drvdata(device, connector);
|
||||
|
||||
KUNIT_EXPECT_LT(test, panel_power_savings_store(device, NULL, "bad", 3), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_panel_power_savings_store_rejects_out_of_range - Test range failure
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_panel_power_savings_store_rejects_out_of_range(struct kunit *test)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct drm_device *drm;
|
||||
struct device *device;
|
||||
|
||||
connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL);
|
||||
drm = kunit_kzalloc(test, sizeof(*drm), GFP_KERNEL);
|
||||
device = kunit_kzalloc(test, sizeof(*device), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, device);
|
||||
|
||||
connector->dev = drm;
|
||||
dev_set_drvdata(device, connector);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, panel_power_savings_store(device, NULL, "5", 1), -EINVAL);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_backlight_get_device_index() */
|
||||
|
||||
/**
|
||||
@@ -799,8 +1394,8 @@ static void dm_test_backlight_fill_props_ac_linear(struct kunit *test)
|
||||
amdgpu_dm_backlight_fill_props(&caps, true, false, &props);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, props.brightness,
|
||||
DIV_ROUND_CLOSEST((max - min) * caps.ac_level, 100));
|
||||
KUNIT_EXPECT_EQ(test, props.max_brightness, max - min);
|
||||
DIV_ROUND_CLOSEST(max * caps.ac_level, 100));
|
||||
KUNIT_EXPECT_EQ(test, props.max_brightness, max);
|
||||
KUNIT_EXPECT_EQ(test, props.scale, BACKLIGHT_SCALE_LINEAR);
|
||||
KUNIT_EXPECT_EQ(test, props.type, BACKLIGHT_RAW);
|
||||
}
|
||||
@@ -825,8 +1420,8 @@ static void dm_test_backlight_fill_props_dc_nonlinear(struct kunit *test)
|
||||
amdgpu_dm_backlight_fill_props(&caps, false, true, &props);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, props.brightness,
|
||||
DIV_ROUND_CLOSEST((max - min) * caps.dc_level, 100));
|
||||
KUNIT_EXPECT_EQ(test, props.max_brightness, max - min);
|
||||
DIV_ROUND_CLOSEST(max * caps.dc_level, 100));
|
||||
KUNIT_EXPECT_EQ(test, props.max_brightness, max);
|
||||
KUNIT_EXPECT_EQ(test, props.scale, BACKLIGHT_SCALE_NON_LINEAR);
|
||||
KUNIT_EXPECT_EQ(test, props.type, BACKLIGHT_RAW);
|
||||
}
|
||||
@@ -1026,6 +1621,7 @@ static void dm_test_should_create_sysfs_no_backlight_index(struct kunit *test)
|
||||
amdgpu_dm_set_abm_level_param(-1);
|
||||
setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP);
|
||||
fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
|
||||
fixture.link->panel_type = PANEL_TYPE_LCD;
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
|
||||
|
||||
@@ -1033,10 +1629,13 @@ static void dm_test_should_create_sysfs_no_backlight_index(struct kunit *test)
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_should_create_sysfs_aux_backlight - Test AUX backlight disables sysfs
|
||||
* dm_test_should_create_sysfs_oled_no_cacp - Test OLED without CACP disables sysfs
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* A non-LCD panel that does not support CACP must not expose the sysfs
|
||||
* backlight interface.
|
||||
*/
|
||||
static void dm_test_should_create_sysfs_aux_backlight(struct kunit *test)
|
||||
static void dm_test_should_create_sysfs_oled_no_cacp(struct kunit *test)
|
||||
{
|
||||
struct dm_backlight_connector_fixture fixture = {};
|
||||
int saved_abm_level = amdgpu_dm_get_abm_level_param();
|
||||
@@ -1044,7 +1643,8 @@ static void dm_test_should_create_sysfs_aux_backlight(struct kunit *test)
|
||||
amdgpu_dm_set_abm_level_param(-1);
|
||||
setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
|
||||
fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
|
||||
fixture.adev->dm.backlight_caps[0].aux_support = true;
|
||||
fixture.link->panel_type = PANEL_TYPE_OLED;
|
||||
fixture.link->panel_config.cacp.cacp_supported = false;
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
|
||||
|
||||
@@ -1052,10 +1652,13 @@ static void dm_test_should_create_sysfs_aux_backlight(struct kunit *test)
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_should_create_sysfs_pwm_backlight - Test PWM backlight enables sysfs
|
||||
* dm_test_should_create_sysfs_oled_cacp - Test OLED with CACP enables sysfs
|
||||
* @test: The KUnit test context
|
||||
*
|
||||
* An OLED panel that supports CACP must expose the sysfs backlight
|
||||
* interface so the ABM/CACP level can be controlled.
|
||||
*/
|
||||
static void dm_test_should_create_sysfs_pwm_backlight(struct kunit *test)
|
||||
static void dm_test_should_create_sysfs_oled_cacp(struct kunit *test)
|
||||
{
|
||||
struct dm_backlight_connector_fixture fixture = {};
|
||||
int saved_abm_level = amdgpu_dm_get_abm_level_param();
|
||||
@@ -1063,7 +1666,27 @@ static void dm_test_should_create_sysfs_pwm_backlight(struct kunit *test)
|
||||
amdgpu_dm_set_abm_level_param(-1);
|
||||
setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
|
||||
fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
|
||||
fixture.adev->dm.backlight_caps[0].aux_support = false;
|
||||
fixture.link->panel_type = PANEL_TYPE_OLED;
|
||||
fixture.link->panel_config.cacp.cacp_supported = true;
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
|
||||
|
||||
amdgpu_dm_set_abm_level_param(saved_abm_level);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_should_create_sysfs_lcd_panel - Test LCD eDP panel enables sysfs
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_should_create_sysfs_lcd_panel(struct kunit *test)
|
||||
{
|
||||
struct dm_backlight_connector_fixture fixture = {};
|
||||
int saved_abm_level = amdgpu_dm_get_abm_level_param();
|
||||
|
||||
amdgpu_dm_set_abm_level_param(-1);
|
||||
setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP);
|
||||
fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
|
||||
fixture.link->panel_type = PANEL_TYPE_LCD;
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector));
|
||||
|
||||
@@ -1147,11 +1770,13 @@ static void dm_test_setup_backlight_device_oled_success(struct kunit *test)
|
||||
struct dm_backlight_connector_fixture fixture = {};
|
||||
struct amdgpu_display_manager *dm;
|
||||
int saved_backlight = amdgpu_dm_get_backlight_param();
|
||||
int saved_abm_level = amdgpu_dm_get_abm_level_param();
|
||||
|
||||
amdgpu_dm_set_backlight_param(-1);
|
||||
/* Skip ABM property attach (requires full DRM object setup) */
|
||||
amdgpu_dm_set_abm_level_param(0);
|
||||
setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP);
|
||||
fixture.link->type = dc_connection_single;
|
||||
/* OLED panel avoids the ABM property attach path */
|
||||
fixture.link->dpcd_sink_ext_caps.bits.oled = 1;
|
||||
dm = &fixture.adev->dm;
|
||||
dm->adev = fixture.adev;
|
||||
@@ -1166,9 +1791,90 @@ static void dm_test_setup_backlight_device_oled_success(struct kunit *test)
|
||||
KUNIT_EXPECT_TRUE(test, dm->backlight_caps[0].aux_support);
|
||||
|
||||
amdgpu_dm_set_backlight_param(saved_backlight);
|
||||
amdgpu_dm_set_abm_level_param(saved_abm_level);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_setup_backlight_device_attaches_abm_property - Test ABM property path
|
||||
* @test: The KUnit test context
|
||||
*/
|
||||
static void dm_test_setup_backlight_device_attaches_abm_property(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_display_manager *dm;
|
||||
struct amdgpu_device *adev;
|
||||
struct drm_property *prop;
|
||||
struct dc_link *link;
|
||||
int saved_abm_level = amdgpu_dm_get_abm_level_param();
|
||||
int saved_backlight = amdgpu_dm_get_backlight_param();
|
||||
int old_count;
|
||||
int ret;
|
||||
|
||||
amdgpu_dm_set_abm_level_param(-1);
|
||||
amdgpu_dm_set_backlight_param(-1);
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
ret = drmm_mode_config_init(&adev->ddev);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
prop = drm_property_create_range(&adev->ddev, 0, "abm level", 0, 4);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, prop);
|
||||
adev->mode_info.abm_level_property = prop;
|
||||
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, NULL);
|
||||
ret = drmm_connector_init(&adev->ddev, &aconnector->base,
|
||||
&dm_backlight_test_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_eDP, NULL);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
link = dm_kunit_alloc_link(test);
|
||||
link->connector_signal = SIGNAL_TYPE_EDP;
|
||||
link->type = dc_connection_single;
|
||||
aconnector->dc_link = link;
|
||||
aconnector->bl_idx = -1;
|
||||
dm = &adev->dm;
|
||||
dm->adev = adev;
|
||||
dm->ddev = &adev->ddev;
|
||||
old_count = aconnector->base.base.properties->count;
|
||||
|
||||
amdgpu_dm_setup_backlight_device(dm, aconnector);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm->num_of_edps, 1);
|
||||
KUNIT_EXPECT_EQ(test, aconnector->bl_idx, 0);
|
||||
KUNIT_EXPECT_EQ(test, aconnector->base.base.properties->count, old_count + 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, aconnector->base.base.properties->properties[old_count], prop);
|
||||
KUNIT_EXPECT_EQ(test, aconnector->base.base.properties->values[old_count],
|
||||
(uint64_t)ABM_SYSFS_CONTROL);
|
||||
|
||||
amdgpu_dm_set_backlight_param(saved_backlight);
|
||||
amdgpu_dm_set_abm_level_param(saved_abm_level);
|
||||
}
|
||||
|
||||
static struct kunit_case dm_backlight_test_cases[] = {
|
||||
/* dm_find_stream_with_link */
|
||||
KUNIT_CASE(dm_test_find_stream_with_link_returns_match),
|
||||
KUNIT_CASE(dm_test_find_stream_with_link_missing),
|
||||
/* amdgpu_dm_backlight_set_level / update_status */
|
||||
KUNIT_CASE(dm_test_backlight_set_level_connector_off),
|
||||
KUNIT_CASE(dm_test_backlight_set_level_no_stream),
|
||||
KUNIT_CASE(dm_test_backlight_set_level_aux_programs_power_module),
|
||||
KUNIT_CASE(dm_test_backlight_set_level_pwm_programs_power_module),
|
||||
KUNIT_CASE(dm_test_backlight_set_level_reallows_idle),
|
||||
KUNIT_CASE(dm_test_backlight_update_status_no_stream),
|
||||
/* amdgpu_dm_backlight_get_level / get_brightness */
|
||||
KUNIT_CASE(dm_test_backlight_get_level_pwm_success),
|
||||
KUNIT_CASE(dm_test_backlight_get_level_pwm_error),
|
||||
KUNIT_CASE(dm_test_backlight_get_level_aux_success),
|
||||
KUNIT_CASE(dm_test_backlight_get_level_aux_error),
|
||||
KUNIT_CASE(dm_test_backlight_get_brightness_uses_device_index),
|
||||
/* amdgpu_dm_register_backlight_device */
|
||||
KUNIT_CASE(dm_test_register_backlight_device_negative_index),
|
||||
/* panel_power_savings_show / store */
|
||||
KUNIT_CASE(dm_test_panel_power_savings_show_maps_disable_to_zero),
|
||||
KUNIT_CASE(dm_test_panel_power_savings_show_reports_level),
|
||||
KUNIT_CASE(dm_test_panel_power_savings_store_sets_disable),
|
||||
KUNIT_CASE(dm_test_panel_power_savings_store_forbidden),
|
||||
KUNIT_CASE(dm_test_panel_power_savings_store_rejects_invalid_text),
|
||||
KUNIT_CASE(dm_test_panel_power_savings_store_rejects_out_of_range),
|
||||
/* amdgpu_dm_backlight_get_device_index */
|
||||
KUNIT_CASE(dm_test_backlight_device_index_matches_second),
|
||||
KUNIT_CASE(dm_test_backlight_device_index_missing_fallback),
|
||||
@@ -1220,13 +1926,15 @@ static struct kunit_case dm_backlight_test_cases[] = {
|
||||
KUNIT_CASE(dm_test_should_create_sysfs_abm_forced),
|
||||
KUNIT_CASE(dm_test_should_create_sysfs_non_edp),
|
||||
KUNIT_CASE(dm_test_should_create_sysfs_no_backlight_index),
|
||||
KUNIT_CASE(dm_test_should_create_sysfs_aux_backlight),
|
||||
KUNIT_CASE(dm_test_should_create_sysfs_pwm_backlight),
|
||||
KUNIT_CASE(dm_test_should_create_sysfs_oled_no_cacp),
|
||||
KUNIT_CASE(dm_test_should_create_sysfs_oled_cacp),
|
||||
KUNIT_CASE(dm_test_should_create_sysfs_lcd_panel),
|
||||
/* amdgpu_dm_setup_backlight_device */
|
||||
KUNIT_CASE(dm_test_setup_backlight_device_non_edp),
|
||||
KUNIT_CASE(dm_test_setup_backlight_device_connection_none),
|
||||
KUNIT_CASE(dm_test_setup_backlight_device_max_edps),
|
||||
KUNIT_CASE(dm_test_setup_backlight_device_oled_success),
|
||||
KUNIT_CASE(dm_test_setup_backlight_device_attaches_abm_property),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -402,8 +402,236 @@ static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *te
|
||||
cancel_delayed_work_sync(&work->watchdog_timer_dwork);
|
||||
cancel_delayed_work_sync(&work->property_validate_dwork);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_process_output_callback_stop_and_needed_requeues - stop+needed requeues callback work
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* When callback_stop and callback_needed are both set, process_output()
|
||||
* should cancel the previous callback_dwork and queue it again.
|
||||
*/
|
||||
static void dm_test_process_output_callback_stop_and_needed_requeues(struct kunit *test)
|
||||
{
|
||||
struct hdcp_workqueue *work = alloc_test_workqueue(test);
|
||||
|
||||
schedule_delayed_work(&work->callback_dwork, msecs_to_jiffies(10000));
|
||||
KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->callback_dwork));
|
||||
|
||||
work->output.callback_stop = true;
|
||||
work->output.callback_needed = true;
|
||||
work->output.callback_delay = 300;
|
||||
|
||||
process_output(work);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->callback_dwork));
|
||||
|
||||
cancel_delayed_work_sync(&work->callback_dwork);
|
||||
cancel_delayed_work_sync(&work->property_validate_dwork);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_process_output_watchdog_stop_and_needed_requeues - stop+needed requeues watchdog work
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* When watchdog_timer_stop and watchdog_timer_needed are both set,
|
||||
* process_output() should cancel previous watchdog work and queue it again.
|
||||
*/
|
||||
static void dm_test_process_output_watchdog_stop_and_needed_requeues(struct kunit *test)
|
||||
{
|
||||
struct hdcp_workqueue *work = alloc_test_workqueue(test);
|
||||
|
||||
schedule_delayed_work(&work->watchdog_timer_dwork, msecs_to_jiffies(10000));
|
||||
KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));
|
||||
|
||||
work->output.watchdog_timer_stop = true;
|
||||
work->output.watchdog_timer_needed = true;
|
||||
work->output.watchdog_timer_delay = 700;
|
||||
|
||||
process_output(work);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork));
|
||||
|
||||
cancel_delayed_work_sync(&work->watchdog_timer_dwork);
|
||||
cancel_delayed_work_sync(&work->property_validate_dwork);
|
||||
}
|
||||
/* End of tests for process_output() */
|
||||
|
||||
/* Tests for event_property_update() */
|
||||
|
||||
/**
|
||||
* alloc_test_workqueue_for_property_update - allocate minimal workqueue for callback tests
|
||||
* @test: KUnit test context for managed allocation
|
||||
*
|
||||
* Allocates a minimal hdcp_workqueue with property_update_work initialised
|
||||
* so event_property_update() can resolve container_of() safely.
|
||||
*/
|
||||
static struct hdcp_workqueue *alloc_test_workqueue_for_property_update(struct kunit *test)
|
||||
{
|
||||
struct hdcp_workqueue *work;
|
||||
|
||||
work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, work);
|
||||
|
||||
INIT_WORK(&work->property_update_work, dummy_work_fn);
|
||||
|
||||
return work;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_event_property_update_skips_null_connector - null connector is ignored
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* If aconnector entry is NULL, event_property_update() should skip it
|
||||
* without modifying encryption_status.
|
||||
*/
|
||||
static void dm_test_event_property_update_skips_null_connector(struct kunit *test)
|
||||
{
|
||||
struct hdcp_workqueue *work = alloc_test_workqueue_for_property_update(test);
|
||||
enum mod_hdcp_encryption_status before;
|
||||
|
||||
work->encryption_status[0] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON;
|
||||
before = work->encryption_status[0];
|
||||
|
||||
event_property_update(&work->property_update_work);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, work->encryption_status[0], before);
|
||||
}
|
||||
|
||||
/* End of tests for event_property_update() */
|
||||
|
||||
/* Tests for hdcp_handle_cpirq() */
|
||||
|
||||
/**
|
||||
* dm_test_hdcp_handle_cpirq_schedules_work - cpirq handler queues cpirq_work
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* hdcp_handle_cpirq() should schedule cpirq_work for the selected link.
|
||||
*/
|
||||
static void dm_test_hdcp_handle_cpirq_schedules_work(struct kunit *test)
|
||||
{
|
||||
struct hdcp_workqueue *work;
|
||||
|
||||
work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, work);
|
||||
|
||||
INIT_WORK(&work->cpirq_work, dummy_work_fn);
|
||||
|
||||
hdcp_handle_cpirq(work, 0);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, work_pending(&work->cpirq_work));
|
||||
|
||||
cancel_work_sync(&work->cpirq_work);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_hdcp_handle_cpirq_selects_link_index - only selected link work is queued
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* hdcp_handle_cpirq() should schedule cpirq_work for the selected index
|
||||
* and not queue unrelated links.
|
||||
*/
|
||||
static void dm_test_hdcp_handle_cpirq_selects_link_index(struct kunit *test)
|
||||
{
|
||||
struct hdcp_workqueue *work;
|
||||
|
||||
work = kunit_kcalloc(test, 2, sizeof(*work), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, work);
|
||||
|
||||
INIT_WORK(&work[0].cpirq_work, dummy_work_fn);
|
||||
INIT_WORK(&work[1].cpirq_work, dummy_work_fn);
|
||||
|
||||
hdcp_handle_cpirq(work, 1);
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, work_pending(&work[0].cpirq_work));
|
||||
KUNIT_EXPECT_TRUE(test, work_pending(&work[1].cpirq_work));
|
||||
|
||||
cancel_work_sync(&work[1].cpirq_work);
|
||||
}
|
||||
|
||||
/* End of tests for hdcp_handle_cpirq() */
|
||||
|
||||
/* Tests for hdcp_update_display() helper logic */
|
||||
|
||||
/**
|
||||
* dm_test_hdcp_update_display_enable_schedules_property_validate - enable path queues validate work
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* hdcp_update_display() should schedule property_validate_dwork when
|
||||
* encryption is enabled.
|
||||
*/
|
||||
static void dm_test_hdcp_update_display_enable_schedules_property_validate(struct kunit *test)
|
||||
{
|
||||
struct hdcp_workqueue *work;
|
||||
|
||||
work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, work);
|
||||
|
||||
INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn);
|
||||
work->srm_size = 0;
|
||||
|
||||
hdcp_update_display_encryption_control(work, work, 0, true);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->property_validate_dwork));
|
||||
|
||||
cancel_delayed_work_sync(&work->property_validate_dwork);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_hdcp_update_display_disable_resets_status_and_cancels_validate - disable path state update
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* hdcp_update_display() should set encryption_status to HDCP_OFF and
|
||||
* cancel property_validate_dwork when encryption is disabled.
|
||||
*/
|
||||
static void dm_test_hdcp_update_display_disable_resets_status_and_cancels_validate(struct kunit *test)
|
||||
{
|
||||
struct hdcp_workqueue *work;
|
||||
|
||||
work = kunit_kzalloc(test, sizeof(*work), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, work);
|
||||
|
||||
INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn);
|
||||
schedule_delayed_work(&work->property_validate_dwork, msecs_to_jiffies(10000));
|
||||
KUNIT_ASSERT_TRUE(test, delayed_work_pending(&work->property_validate_dwork));
|
||||
|
||||
work->encryption_status[3] = MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON;
|
||||
|
||||
hdcp_update_display_encryption_control(work, work, 3, false);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, work->encryption_status[3],
|
||||
MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF);
|
||||
KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->property_validate_dwork));
|
||||
}
|
||||
|
||||
/* End of tests for hdcp_update_display() helper logic */
|
||||
|
||||
/* Tests for hdcp_create_workqueue() */
|
||||
|
||||
/**
|
||||
* dm_test_hdcp_create_workqueue_zero_max_links_returns_null - zero-link creation fails early
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* When dc->caps.max_links is zero, hdcp_create_workqueue() should fail
|
||||
* the initial allocation path and return NULL.
|
||||
*/
|
||||
static void dm_test_hdcp_create_workqueue_zero_max_links_returns_null(struct kunit *test)
|
||||
{
|
||||
struct cp_psp cp_psp = {0};
|
||||
struct dc *dc;
|
||||
struct hdcp_workqueue *work;
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc);
|
||||
|
||||
dc->caps.max_links = 0;
|
||||
|
||||
work = hdcp_create_workqueue(NULL, &cp_psp, dc);
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ(test, work, NULL);
|
||||
}
|
||||
|
||||
/* End of tests for hdcp_create_workqueue() */
|
||||
|
||||
static struct kunit_case dm_hdcp_test_cases[] = {
|
||||
/* hdcp_get_content_protection_from_status() */
|
||||
KUNIT_CASE(dm_test_hdcp_get_cp_disabled_returns_desired),
|
||||
@@ -423,6 +651,18 @@ static struct kunit_case dm_hdcp_test_cases[] = {
|
||||
KUNIT_CASE(dm_test_process_output_watchdog_needed),
|
||||
KUNIT_CASE(dm_test_process_output_watchdog_stop),
|
||||
KUNIT_CASE(dm_test_process_output_callback_and_watchdog_needed),
|
||||
KUNIT_CASE(dm_test_process_output_callback_stop_and_needed_requeues),
|
||||
KUNIT_CASE(dm_test_process_output_watchdog_stop_and_needed_requeues),
|
||||
/* event_property_update() */
|
||||
KUNIT_CASE(dm_test_event_property_update_skips_null_connector),
|
||||
/* hdcp_handle_cpirq() */
|
||||
KUNIT_CASE(dm_test_hdcp_handle_cpirq_schedules_work),
|
||||
KUNIT_CASE(dm_test_hdcp_handle_cpirq_selects_link_index),
|
||||
/* hdcp_update_display() helper logic */
|
||||
KUNIT_CASE(dm_test_hdcp_update_display_enable_schedules_property_validate),
|
||||
KUNIT_CASE(dm_test_hdcp_update_display_disable_resets_status_and_cancels_validate),
|
||||
/* hdcp_create_workqueue() */
|
||||
KUNIT_CASE(dm_test_hdcp_create_workqueue_zero_max_links_returns_null),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,9 @@
|
||||
#include <kunit/test.h>
|
||||
|
||||
#include "dc.h"
|
||||
#include "amdgpu.h"
|
||||
#include "amdgpu_mode.h"
|
||||
#include "amdgpu_dm.h"
|
||||
#include "amdgpu_dm_ism.h"
|
||||
#include "amdgpu_dm_kunit_test_helpers.h"
|
||||
|
||||
@@ -848,6 +851,345 @@ static void dm_test_dispatch_next_event_no_action_state(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, (int)result, (int)DM_ISM_NUM_EVENTS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper: allocate an amdgpu_crtc (which embeds the ISM) wired up to a
|
||||
* minimally-populated amdgpu_device so that the container_of()/drm_to_adev()
|
||||
* lookups inside the ISM event machinery resolve correctly.
|
||||
*
|
||||
* The amdgpu_device is large, so it must be heap-allocated via kunit_kzalloc.
|
||||
* acrtc->base.dev points at the embedded &adev->ddev, which is what
|
||||
* drm_to_adev() expects (it is a container_of of ddev).
|
||||
*/
|
||||
static struct amdgpu_crtc *alloc_test_acrtc(struct kunit *test,
|
||||
struct amdgpu_device **adev_out)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc;
|
||||
struct dc *dc;
|
||||
|
||||
adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, adev);
|
||||
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc);
|
||||
|
||||
acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, acrtc);
|
||||
|
||||
adev->dm.dc = dc;
|
||||
adev->dm.ddev = &adev->ddev;
|
||||
mutex_init(&adev->dm.dc_lock);
|
||||
|
||||
acrtc->base.dev = &adev->ddev;
|
||||
|
||||
if (adev_out)
|
||||
*adev_out = adev;
|
||||
|
||||
return acrtc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper: register an already-allocated amdgpu_crtc into the DRM device's
|
||||
* crtc_list so that drm_for_each_crtc() iterates it. Only the list linkage is
|
||||
* required for the ISM enable/disable/force-full-power helpers.
|
||||
*/
|
||||
static void register_test_acrtc(struct amdgpu_device *adev,
|
||||
struct amdgpu_crtc *acrtc)
|
||||
{
|
||||
INIT_LIST_HEAD(&adev->ddev.mode_config.crtc_list);
|
||||
INIT_LIST_HEAD(&acrtc->base.head);
|
||||
list_add_tail(&acrtc->base.head, &adev->ddev.mode_config.crtc_list);
|
||||
}
|
||||
|
||||
/* ===== Tests for amdgpu_dm_ism_commit_event ===== */
|
||||
|
||||
/**
|
||||
* dm_test_ism_commit_event_no_state - commit_event returns early without a crtc state
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* When the CRTC has no atomic state (base.state == NULL) the function takes
|
||||
* the NO_STATE early-return path and the FSM is left untouched.
|
||||
*/
|
||||
static void dm_test_ism_commit_event_no_state(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc = alloc_test_acrtc(test, &adev);
|
||||
struct amdgpu_dm_ism_config config = { 0 };
|
||||
|
||||
amdgpu_dm_ism_init(&acrtc->ism, &config);
|
||||
acrtc->base.state = NULL;
|
||||
|
||||
guard(mutex)(&adev->dm.dc_lock);
|
||||
amdgpu_dm_ism_commit_event(&acrtc->ism,
|
||||
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
|
||||
|
||||
amdgpu_dm_ism_fini(&acrtc->ism);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_ism_commit_event_cursor_transition - cursor begin/end drive FSM without DC work
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* BEGIN_CURSOR_UPDATE then END_CURSOR_UPDATE from FULL_POWER_RUNNING traverse
|
||||
* the FULL_POWER_BUSY state. Neither transition reaches the idle-optimization
|
||||
* commit path, so no DC hardware access occurs and the FSM returns to
|
||||
* FULL_POWER_RUNNING.
|
||||
*/
|
||||
static void dm_test_ism_commit_event_cursor_transition(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc = alloc_test_acrtc(test, &adev);
|
||||
struct dm_crtc_state *dm_state;
|
||||
struct amdgpu_dm_ism_config config = { 0 };
|
||||
|
||||
dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dm_state);
|
||||
|
||||
amdgpu_dm_ism_init(&acrtc->ism, &config);
|
||||
acrtc->base.state = &dm_state->base;
|
||||
|
||||
guard(mutex)(&adev->dm.dc_lock);
|
||||
amdgpu_dm_ism_commit_event(&acrtc->ism,
|
||||
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE);
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_FULL_POWER_BUSY);
|
||||
|
||||
amdgpu_dm_ism_commit_event(&acrtc->ism,
|
||||
DM_ISM_EVENT_END_CURSOR_UPDATE);
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
|
||||
|
||||
amdgpu_dm_ism_fini(&acrtc->ism);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_ism_commit_event_invalid_event - invalid event leaves FSM unchanged
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* EXIT_IDLE_REQUESTED is not a valid event from FULL_POWER_RUNNING, so the
|
||||
* FSM does not transition and no power-state dispatch occurs.
|
||||
*/
|
||||
static void dm_test_ism_commit_event_invalid_event(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc = alloc_test_acrtc(test, &adev);
|
||||
struct dm_crtc_state *dm_state;
|
||||
struct amdgpu_dm_ism_config config = { 0 };
|
||||
|
||||
dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dm_state);
|
||||
|
||||
amdgpu_dm_ism_init(&acrtc->ism, &config);
|
||||
acrtc->base.state = &dm_state->base;
|
||||
|
||||
guard(mutex)(&adev->dm.dc_lock);
|
||||
amdgpu_dm_ism_commit_event(&acrtc->ism,
|
||||
DM_ISM_EVENT_EXIT_IDLE_REQUESTED);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
|
||||
|
||||
amdgpu_dm_ism_fini(&acrtc->ism);
|
||||
}
|
||||
|
||||
/* ===== Tests for amdgpu_dm_ism_force_full_power ===== */
|
||||
|
||||
/**
|
||||
* dm_test_ism_force_full_power - force-full-power sends EXIT_IDLE to every CRTC
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* From the initial FULL_POWER_RUNNING state EXIT_IDLE_REQUESTED is a no-op, so
|
||||
* the FSM remains in FULL_POWER_RUNNING and no DC work is scheduled.
|
||||
*/
|
||||
static void dm_test_ism_force_full_power(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc = alloc_test_acrtc(test, &adev);
|
||||
struct dm_crtc_state *dm_state;
|
||||
struct amdgpu_dm_ism_config config = { 0 };
|
||||
|
||||
dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dm_state);
|
||||
|
||||
amdgpu_dm_ism_init(&acrtc->ism, &config);
|
||||
acrtc->base.state = &dm_state->base;
|
||||
register_test_acrtc(adev, acrtc);
|
||||
|
||||
guard(mutex)(&adev->dm.dc_lock);
|
||||
amdgpu_dm_ism_force_full_power(&adev->dm);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
|
||||
|
||||
amdgpu_dm_ism_fini(&acrtc->ism);
|
||||
}
|
||||
|
||||
/* ===== Tests for amdgpu_dm_ism_disable / amdgpu_dm_ism_enable ===== */
|
||||
|
||||
/**
|
||||
* dm_test_ism_disable_enable_cycle - disable then enable quiesces and re-arms work
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* Walks every CRTC's ISM, disabling its delayed work (synchronously) and then
|
||||
* re-enabling it. With no work ever scheduled both calls complete without
|
||||
* touching the FSM state. disable must be called without dc_lock held.
|
||||
*/
|
||||
static void dm_test_ism_disable_enable_cycle(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc = alloc_test_acrtc(test, &adev);
|
||||
struct amdgpu_dm_ism_config config = { 0 };
|
||||
|
||||
amdgpu_dm_ism_init(&acrtc->ism, &config);
|
||||
register_test_acrtc(adev, acrtc);
|
||||
|
||||
amdgpu_dm_ism_disable(&adev->dm);
|
||||
amdgpu_dm_ism_enable(&adev->dm);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_FULL_POWER_RUNNING);
|
||||
|
||||
amdgpu_dm_ism_fini(&acrtc->ism);
|
||||
}
|
||||
|
||||
/* ===== Tests for dm_ism_dispatch_power_state (via commit_event) ===== */
|
||||
|
||||
/*
|
||||
* Build a config + history that makes dm_ism_get_idle_allow_delay() return a
|
||||
* non-zero hysteresis delay. A non-zero delay keeps the FSM parked in
|
||||
* HYSTERESIS_WAITING (the dispatcher returns DM_ISM_NUM_EVENTS instead of an
|
||||
* immediate follow-up event), preventing the cascade into the DC-dependent
|
||||
* OPTIMIZED_IDLE / *_SSO states.
|
||||
*/
|
||||
static void setup_idle_delay_history(struct amdgpu_dm_ism *ism,
|
||||
struct dc_stream_state *stream)
|
||||
{
|
||||
uint64_t one_frame_ns;
|
||||
|
||||
stream->timing.v_total = 1125;
|
||||
stream->timing.h_total = 2200;
|
||||
stream->timing.pix_clk_100hz = 1485000;
|
||||
|
||||
one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL, 1485000);
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ism->records[i].duration_ns = one_frame_ns;
|
||||
ism->records[i].timestamp_ns = 0;
|
||||
}
|
||||
ism->next_record_idx = 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_ism_dispatch_hysteresis_schedule_and_cancel - cover the HYSTERESIS_WAITING dispatch arms
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* ENTER_IDLE_REQUESTED moves FULL_POWER_RUNNING -> HYSTERESIS_WAITING. The
|
||||
* current-state arm of dm_ism_dispatch_power_state() then records the idle
|
||||
* timestamp, computes a (non-zero) idle-allow delay and schedules the delayed
|
||||
* worker. A subsequent BEGIN_CURSOR_UPDATE (-> HYSTERESIS_BUSY) exercises the
|
||||
* previous-state arm that cancels that pending worker. Neither arm reaches the
|
||||
* DC-dependent idle-optimization commit path.
|
||||
*/
|
||||
static void dm_test_ism_dispatch_hysteresis_schedule_and_cancel(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc = alloc_test_acrtc(test, &adev);
|
||||
struct dm_crtc_state *dm_state;
|
||||
struct dc_stream_state *stream;
|
||||
struct amdgpu_dm_ism_config config = {
|
||||
.filter_num_frames = 5,
|
||||
.filter_entry_count = 3,
|
||||
.activation_num_delay_frames = 10,
|
||||
.filter_history_size = 8,
|
||||
.filter_old_history_threshold = 0,
|
||||
.sso_num_frames = 0,
|
||||
};
|
||||
|
||||
dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dm_state);
|
||||
stream = dm_kunit_alloc_stream(test, NULL);
|
||||
|
||||
amdgpu_dm_ism_init(&acrtc->ism, &config);
|
||||
setup_idle_delay_history(&acrtc->ism, stream);
|
||||
dm_state->stream = stream;
|
||||
acrtc->base.state = &dm_state->base;
|
||||
|
||||
scoped_guard(mutex, &adev->dm.dc_lock) {
|
||||
/* Enter HYSTERESIS_WAITING: schedules the idle-allow worker. */
|
||||
amdgpu_dm_ism_commit_event(&acrtc->ism,
|
||||
DM_ISM_EVENT_ENTER_IDLE_REQUESTED);
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_HYSTERESIS_WAITING);
|
||||
|
||||
/* Cursor update cancels the pending worker (prev-state arm). */
|
||||
amdgpu_dm_ism_commit_event(&acrtc->ism,
|
||||
DM_ISM_EVENT_BEGIN_CURSOR_UPDATE);
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_HYSTERESIS_BUSY);
|
||||
}
|
||||
|
||||
amdgpu_dm_ism_fini(&acrtc->ism);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_ism_dispatch_optimized_idle_defers_sso - cover OPTIMIZED_IDLE dispatch without DC
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* With sso_num_frames < filter_num_frames the OPTIMIZED_IDLE current-state arm
|
||||
* skips the idle-optimization commit and only schedules the SSO worker. Driving
|
||||
* HYSTERESIS_WAITING -> OPTIMIZED_IDLE via TIMER_ELAPSED exercises that arm
|
||||
* (get_sso_delay + the skip branch + mod_delayed_work) without any DC access.
|
||||
*/
|
||||
static void dm_test_ism_dispatch_optimized_idle_defers_sso(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_device *adev;
|
||||
struct amdgpu_crtc *acrtc = alloc_test_acrtc(test, &adev);
|
||||
struct dm_crtc_state *dm_state;
|
||||
struct dc_stream_state *stream;
|
||||
struct amdgpu_dm_ism_config config = {
|
||||
.filter_num_frames = 5,
|
||||
.filter_entry_count = 3,
|
||||
.activation_num_delay_frames = 10,
|
||||
.filter_history_size = 8,
|
||||
.filter_old_history_threshold = 0,
|
||||
.sso_num_frames = 2,
|
||||
};
|
||||
|
||||
dm_state = kunit_kzalloc(test, sizeof(*dm_state), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dm_state);
|
||||
stream = dm_kunit_alloc_stream(test, NULL);
|
||||
|
||||
amdgpu_dm_ism_init(&acrtc->ism, &config);
|
||||
setup_idle_delay_history(&acrtc->ism, stream);
|
||||
dm_state->stream = stream;
|
||||
acrtc->base.state = &dm_state->base;
|
||||
|
||||
scoped_guard(mutex, &adev->dm.dc_lock) {
|
||||
amdgpu_dm_ism_commit_event(&acrtc->ism,
|
||||
DM_ISM_EVENT_ENTER_IDLE_REQUESTED);
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_HYSTERESIS_WAITING);
|
||||
|
||||
/*
|
||||
* Timer fires: HYSTERESIS_WAITING -> OPTIMIZED_IDLE. sso_delay
|
||||
* is non-zero and sso_num_frames < filter_num_frames, so the
|
||||
* commit is deferred to the SSO worker and the FSM parks here.
|
||||
*/
|
||||
amdgpu_dm_ism_commit_event(&acrtc->ism,
|
||||
DM_ISM_EVENT_TIMER_ELAPSED);
|
||||
KUNIT_EXPECT_EQ(test, (int)acrtc->ism.current_state,
|
||||
(int)DM_ISM_STATE_OPTIMIZED_IDLE);
|
||||
|
||||
/* Cancel the scheduled SSO worker while still holding dc_lock. */
|
||||
cancel_delayed_work(&acrtc->ism.sso_delayed_work);
|
||||
}
|
||||
|
||||
amdgpu_dm_ism_fini(&acrtc->ism);
|
||||
}
|
||||
|
||||
static struct kunit_case dm_ism_test_cases[] = {
|
||||
/* dm_ism_next_state — FULL_POWER_RUNNING */
|
||||
KUNIT_CASE(dm_test_ism_next_state_running_enter_idle),
|
||||
@@ -910,6 +1252,17 @@ static struct kunit_case dm_ism_test_cases[] = {
|
||||
KUNIT_CASE(dm_test_dispatch_next_event_opt_idle_with_sso_delay),
|
||||
KUNIT_CASE(dm_test_dispatch_next_event_timer_aborted),
|
||||
KUNIT_CASE(dm_test_dispatch_next_event_no_action_state),
|
||||
/* amdgpu_dm_ism_commit_event */
|
||||
KUNIT_CASE(dm_test_ism_commit_event_no_state),
|
||||
KUNIT_CASE(dm_test_ism_commit_event_cursor_transition),
|
||||
KUNIT_CASE(dm_test_ism_commit_event_invalid_event),
|
||||
/* amdgpu_dm_ism_force_full_power */
|
||||
KUNIT_CASE(dm_test_ism_force_full_power),
|
||||
/* amdgpu_dm_ism_disable / amdgpu_dm_ism_enable */
|
||||
KUNIT_CASE(dm_test_ism_disable_enable_cycle),
|
||||
/* dm_ism_dispatch_power_state (via commit_event) */
|
||||
KUNIT_CASE(dm_test_ism_dispatch_hysteresis_schedule_and_cancel),
|
||||
KUNIT_CASE(dm_test_ism_dispatch_optimized_idle_defers_sso),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
#include <kunit/test.h>
|
||||
|
||||
#include <drm/drm_drv.h>
|
||||
#include <drm/drm_fixed.h>
|
||||
#include <drm/drm_kunit_helpers.h>
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_mode_config.h>
|
||||
#include <drm/display/drm_dp.h>
|
||||
#include <drm/display/drm_dp_helper.h>
|
||||
#include <drm/display/drm_dp_mst_helper.h>
|
||||
@@ -31,7 +35,9 @@ static u8 dm_mst_test_dpcd[0x10];
|
||||
static u8 dm_mst_test_desc_dpcd[0x10];
|
||||
static struct aux_payload dm_mst_test_last_payload;
|
||||
static int dm_mst_test_aux_transfer_raw_result;
|
||||
static u8 dm_mst_test_aux_transfer_raw_reply;
|
||||
static enum aux_return_code_type dm_mst_test_aux_transfer_raw_operation_result;
|
||||
static ssize_t dm_mst_test_aux_transfer_override;
|
||||
|
||||
static int dm_mst_test_aux_transfer_raw(struct ddc_service *ddc,
|
||||
struct aux_payload *payload,
|
||||
@@ -41,6 +47,7 @@ static int dm_mst_test_aux_transfer_raw(struct ddc_service *ddc,
|
||||
|
||||
dm_mst_test_last_payload = *payload;
|
||||
*operation_result = dm_mst_test_aux_transfer_raw_operation_result;
|
||||
payload->reply[0] = dm_mst_test_aux_transfer_raw_reply;
|
||||
|
||||
if (dm_mst_test_aux_transfer_raw_result)
|
||||
return dm_mst_test_aux_transfer_raw_result;
|
||||
@@ -64,6 +71,7 @@ static void dm_mst_test_setup_dm_aux(struct amdgpu_dm_dp_aux *dm_aux,
|
||||
{
|
||||
memset(&dm_mst_test_last_payload, 0, sizeof(dm_mst_test_last_payload));
|
||||
dm_mst_test_aux_transfer_raw_result = 0;
|
||||
dm_mst_test_aux_transfer_raw_reply = 0;
|
||||
dm_mst_test_aux_transfer_raw_operation_result = AUX_RET_SUCCESS;
|
||||
link_srv->aux_transfer_raw = dm_mst_test_aux_transfer_raw;
|
||||
dc->link_srv = link_srv;
|
||||
@@ -87,6 +95,11 @@ static ssize_t dm_mst_test_aux_transfer(struct drm_dp_aux *aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
size_t i;
|
||||
ssize_t ret;
|
||||
|
||||
ret = dm_mst_test_aux_transfer_override;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (msg->request & ~DP_AUX_I2C_MOT) {
|
||||
case DP_AUX_NATIVE_READ:
|
||||
@@ -103,6 +116,53 @@ static ssize_t dm_mst_test_aux_transfer(struct drm_dp_aux *aux,
|
||||
}
|
||||
}
|
||||
|
||||
static struct amdgpu_dm_connector *dm_mst_test_alloc_sideband_connector(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct link_service *link_srv;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, aconnector);
|
||||
KUNIT_ASSERT_NOT_NULL(test, link_srv);
|
||||
KUNIT_ASSERT_NOT_NULL(test, link);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc);
|
||||
|
||||
mutex_init(&aconnector->handle_mst_msg_ready);
|
||||
link_srv->get_status = dm_mst_test_get_status;
|
||||
dc->link_srv = link_srv;
|
||||
link->dc = dc;
|
||||
link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14;
|
||||
link->link_status.dpcd_caps = &link->dpcd_caps;
|
||||
aconnector->dc_link = link;
|
||||
aconnector->dm_dp_aux.aux.name = "dm_mst_test_sideband_aux";
|
||||
aconnector->dm_dp_aux.aux.transfer = dm_mst_test_aux_transfer;
|
||||
drm_dp_aux_init(&aconnector->dm_dp_aux.aux);
|
||||
drm_dp_dpcd_set_probe(&aconnector->dm_dp_aux.aux, false);
|
||||
|
||||
memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd));
|
||||
dm_mst_test_aux_transfer_override = 0;
|
||||
|
||||
return aconnector;
|
||||
}
|
||||
|
||||
static uint32_t dm_mst_test_dp_link_bandwidth_kbps(
|
||||
const struct dc_link *link,
|
||||
const struct dc_link_settings *link_settings)
|
||||
{
|
||||
return 4320000;
|
||||
}
|
||||
|
||||
static const struct dc_link_settings *dm_mst_test_dp_get_verified_link_cap(
|
||||
const struct dc_link *link)
|
||||
{
|
||||
return &link->verified_link_cap;
|
||||
}
|
||||
|
||||
static ssize_t dm_mst_test_desc_aux_transfer(struct drm_dp_aux *aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
@@ -254,6 +314,35 @@ static void dm_mst_test_pbn_divider_null_link(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, dm_mst_get_pbn_divider(NULL), 0U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_mst_test_pbn_divider_uses_link_bandwidth - Test pbn_divider with link cap
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* Verify that dm_mst_get_pbn_divider() uses the DC link service to derive the
|
||||
* fixed-point PBN divider when a link is present.
|
||||
*/
|
||||
static void dm_mst_test_pbn_divider_uses_link_bandwidth(struct kunit *test)
|
||||
{
|
||||
struct link_service *link_srv;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, link_srv);
|
||||
KUNIT_ASSERT_NOT_NULL(test, link);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc);
|
||||
|
||||
link_srv->dp_get_verified_link_cap = dm_mst_test_dp_get_verified_link_cap;
|
||||
link_srv->dp_link_bandwidth_kbps = dm_mst_test_dp_link_bandwidth_kbps;
|
||||
dc->link_srv = link_srv;
|
||||
link->dc = dc;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_mst_get_pbn_divider(link),
|
||||
(uint32_t)(dfixed_const(1000) / 100));
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_mst_reset_mst_connector_setting */
|
||||
|
||||
/**
|
||||
@@ -331,6 +420,7 @@ static void dm_mst_test_retrieve_downstream_present(struct kunit *test)
|
||||
memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd));
|
||||
/* PORT_PRESENT = 1, PORT_TYPE = 2 (0b101) */
|
||||
dm_mst_test_dpcd[DP_DOWNSTREAMPORT_PRESENT] = 0x05;
|
||||
dm_mst_test_aux_transfer_override = 0;
|
||||
|
||||
aux->name = "dm_mst_test_aux";
|
||||
aux->transfer = dm_mst_test_aux_transfer;
|
||||
@@ -345,6 +435,35 @@ static void dm_mst_test_retrieve_downstream_present(struct kunit *test)
|
||||
(int)aconnector->mst_downstream_port_present.fields.PORT_TYPE, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_mst_test_retrieve_downstream_aux_error - Test downstream read failure
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* Verify that retrieve_downstream_port_device() returns false when the AUX
|
||||
* DPCD read fails.
|
||||
*/
|
||||
static void dm_mst_test_retrieve_downstream_aux_error(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct drm_dp_aux *aux;
|
||||
|
||||
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
|
||||
aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, aconnector);
|
||||
KUNIT_ASSERT_NOT_NULL(test, aux);
|
||||
|
||||
dm_mst_test_aux_transfer_override = -EIO;
|
||||
aux->name = "dm_mst_test_aux";
|
||||
aux->transfer = dm_mst_test_aux_transfer;
|
||||
drm_dp_aux_init(aux);
|
||||
drm_dp_dpcd_set_probe(aux, false);
|
||||
aconnector->dsc_aux = aux;
|
||||
|
||||
KUNIT_EXPECT_FALSE(test, retrieve_downstream_port_device(aconnector));
|
||||
|
||||
dm_mst_test_aux_transfer_override = 0;
|
||||
}
|
||||
|
||||
/* Tests for retrieve_branch_specific_data */
|
||||
|
||||
/**
|
||||
@@ -710,6 +829,55 @@ static void dm_mst_test_aux_transfer_hpd_discon_quirk(struct kunit *test)
|
||||
DP_SIDEBAND_MSG_DOWN_REQ_BASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_mst_test_aux_transfer_non_ack_reply - non-ACK AUX reply is logged.
|
||||
* @test: KUnit test context.
|
||||
*
|
||||
* A successful read with a nonzero reply byte should still return the backend
|
||||
* byte count while exercising the non-ACK reply handling path.
|
||||
*/
|
||||
static void dm_mst_test_aux_transfer_non_ack_reply(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_dp_aux *dm_aux;
|
||||
struct amdgpu_device *adev;
|
||||
struct ddc_service *ddc;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
struct link_service *link_srv;
|
||||
struct dc_context *ctx;
|
||||
u8 buffer[2] = { 0 };
|
||||
struct drm_dp_aux_msg msg = {
|
||||
.address = 4,
|
||||
.request = DP_AUX_NATIVE_READ,
|
||||
.buffer = buffer,
|
||||
.size = sizeof(buffer),
|
||||
};
|
||||
ssize_t ret;
|
||||
|
||||
dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL);
|
||||
adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL);
|
||||
ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dm_aux);
|
||||
KUNIT_ASSERT_NOT_NULL(test, adev);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ddc);
|
||||
KUNIT_ASSERT_NOT_NULL(test, link);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc);
|
||||
KUNIT_ASSERT_NOT_NULL(test, link_srv);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ctx);
|
||||
|
||||
dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev);
|
||||
dm_mst_test_aux_transfer_raw_reply = DP_AUX_NATIVE_REPLY_NACK;
|
||||
|
||||
ret = dm_dp_aux_transfer(&dm_aux->aux, &msg);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer));
|
||||
KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 4U);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_mst_test_fill_payload_flags_native_write - native write request decode.
|
||||
* @test: KUnit test context.
|
||||
@@ -843,31 +1011,8 @@ static void dm_mst_test_select_esi_dpcd_esi(struct kunit *test)
|
||||
static void dm_mst_test_sideband_msg_ready_no_ready_bits(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct link_service *link_srv;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
|
||||
aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL);
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, aconnector);
|
||||
KUNIT_ASSERT_NOT_NULL(test, link_srv);
|
||||
KUNIT_ASSERT_NOT_NULL(test, link);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc);
|
||||
|
||||
mutex_init(&aconnector->handle_mst_msg_ready);
|
||||
link_srv->get_status = dm_mst_test_get_status;
|
||||
dc->link_srv = link_srv;
|
||||
link->dc = dc;
|
||||
link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14;
|
||||
link->link_status.dpcd_caps = &link->dpcd_caps;
|
||||
aconnector->dc_link = link;
|
||||
aconnector->dm_dp_aux.aux.name = "dm_mst_test_sideband_aux";
|
||||
aconnector->dm_dp_aux.aux.transfer = dm_mst_test_aux_transfer;
|
||||
drm_dp_aux_init(&aconnector->dm_dp_aux.aux);
|
||||
drm_dp_dpcd_set_probe(&aconnector->dm_dp_aux.aux, false);
|
||||
memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd));
|
||||
aconnector = dm_mst_test_alloc_sideband_connector(test);
|
||||
|
||||
dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr,
|
||||
DOWN_REP_MSG_RDY_EVENT);
|
||||
@@ -875,6 +1020,185 @@ static void dm_mst_test_sideband_msg_ready_no_ready_bits(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, dm_mst_test_dpcd[1], (u8)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_mst_test_sideband_msg_ready_read_error - Test ESI read failure path
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* Verify that dm_handle_mst_sideband_msg_ready_event() returns cleanly when
|
||||
* the DPCD read fails before a ready bit can be handled.
|
||||
*/
|
||||
static void dm_mst_test_sideband_msg_ready_read_error(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
|
||||
aconnector = dm_mst_test_alloc_sideband_connector(test);
|
||||
dm_mst_test_aux_transfer_override = -EIO;
|
||||
|
||||
dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr,
|
||||
DOWN_REP_MSG_RDY_EVENT);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_mst_test_dpcd[1], (u8)0);
|
||||
dm_mst_test_aux_transfer_override = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_mst_test_sideband_msg_ready_without_mst_state - Test ready bit no-op path
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* Verify that a DOWN_REP ready bit is filtered and then ignored when the MST
|
||||
* topology manager is not enabled.
|
||||
*/
|
||||
static void dm_mst_test_sideband_msg_ready_without_mst_state(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
|
||||
aconnector = dm_mst_test_alloc_sideband_connector(test);
|
||||
dm_mst_test_dpcd[(DP_SINK_COUNT_ESI + 1) & 0xf] = DP_DOWN_REP_MSG_RDY;
|
||||
|
||||
dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr,
|
||||
DOWN_REP_MSG_RDY_EVENT);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_mst_test_dpcd[(DP_SINK_COUNT_ESI + 1) & 0xf],
|
||||
DP_DOWN_REP_MSG_RDY);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_mst_test_down_rep_msg_ready_wrapper - Test DOWN_REP wrapper
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* Verify that dm_handle_mst_down_rep_msg_ready() forwards to the generic MST
|
||||
* sideband handler with the DOWN_REP event selection.
|
||||
*/
|
||||
static void dm_mst_test_down_rep_msg_ready_wrapper(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
|
||||
aconnector = dm_mst_test_alloc_sideband_connector(test);
|
||||
|
||||
dm_handle_mst_down_rep_msg_ready(&aconnector->mst_mgr);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, dm_mst_test_dpcd[1], (u8)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_mst_test_initialize_dp_connector_edp - Test eDP initialization path
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* Verify that amdgpu_dm_initialize_dp_connector() initializes the DP AUX state
|
||||
* and exits before MST topology setup for eDP connectors.
|
||||
*/
|
||||
static void dm_mst_test_initialize_dp_connector_edp(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_device *adev;
|
||||
struct ddc_service *ddc;
|
||||
struct dc_link *link;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
link = dm_kunit_alloc_link(test);
|
||||
ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ddc);
|
||||
|
||||
adev->dm.adev = adev;
|
||||
adev->dm.ddev = &adev->ddev;
|
||||
link->ddc = ddc;
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, link);
|
||||
aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP;
|
||||
|
||||
amdgpu_dm_initialize_dp_connector(&adev->dm, aconnector, 5);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, aconnector->dm_dp_aux.aux.transfer == dm_dp_aux_transfer);
|
||||
KUNIT_EXPECT_PTR_EQ(test, aconnector->dm_dp_aux.aux.drm_dev, &adev->ddev);
|
||||
KUNIT_EXPECT_PTR_EQ(test, aconnector->dm_dp_aux.ddc_service, ddc);
|
||||
KUNIT_EXPECT_PTR_EQ(test, aconnector->mst_mgr.dev, NULL);
|
||||
KUNIT_EXPECT_NOT_NULL(test, aconnector->dm_dp_aux.aux.name);
|
||||
if (aconnector->dm_dp_aux.aux.name)
|
||||
KUNIT_EXPECT_NOT_NULL(test, strstr(aconnector->dm_dp_aux.aux.name, "5"));
|
||||
|
||||
drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux);
|
||||
kfree(aconnector->dm_dp_aux.aux.name);
|
||||
}
|
||||
|
||||
static bool dm_mst_test_dp_get_max_link_enc_cap(const struct dc_link *link,
|
||||
struct dc_link_settings *cap)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dm_mst_test_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs dm_mst_test_connector_funcs = {
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.destroy = dm_mst_test_connector_destroy,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
/**
|
||||
* dm_mst_test_initialize_dp_connector_mst - Test MST root initialization path
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* Verify that amdgpu_dm_initialize_dp_connector() initializes the MST topology
|
||||
* manager for a non-eDP DisplayPort connector. This exercises the path past the
|
||||
* eDP early return, including dc_link_dp_get_max_link_enc_cap() and
|
||||
* drm_dp_mst_topology_mgr_init(). A fully initialized DRM mode config and
|
||||
* connector are required because the topology manager registers a private
|
||||
* atomic object and the subconnector property is attached to the connector.
|
||||
*/
|
||||
static void dm_mst_test_initialize_dp_connector_mst(struct kunit *test)
|
||||
{
|
||||
struct amdgpu_dm_connector *aconnector;
|
||||
struct amdgpu_device *adev;
|
||||
struct link_service *link_srv;
|
||||
struct ddc_service *ddc;
|
||||
struct dc_link *link;
|
||||
struct dc *dc;
|
||||
int ret;
|
||||
|
||||
adev = dm_kunit_alloc_adev(test);
|
||||
|
||||
ret = drmm_mode_config_init(&adev->ddev);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL);
|
||||
link = dm_kunit_alloc_link(test);
|
||||
dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL);
|
||||
link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, ddc);
|
||||
KUNIT_ASSERT_NOT_NULL(test, dc);
|
||||
KUNIT_ASSERT_NOT_NULL(test, link_srv);
|
||||
|
||||
link_srv->dp_get_max_link_enc_cap = dm_mst_test_dp_get_max_link_enc_cap;
|
||||
dc->link_srv = link_srv;
|
||||
link->dc = dc;
|
||||
link->ddc = ddc;
|
||||
|
||||
adev->dm.adev = adev;
|
||||
adev->dm.ddev = &adev->ddev;
|
||||
|
||||
aconnector = dm_kunit_alloc_connector(test, adev, link);
|
||||
|
||||
ret = drm_connector_init(&adev->ddev, &aconnector->base,
|
||||
&dm_mst_test_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DisplayPort);
|
||||
KUNIT_ASSERT_EQ(test, ret, 0);
|
||||
|
||||
amdgpu_dm_initialize_dp_connector(&adev->dm, aconnector, 7);
|
||||
|
||||
KUNIT_EXPECT_TRUE(test, aconnector->dm_dp_aux.aux.transfer == dm_dp_aux_transfer);
|
||||
KUNIT_EXPECT_PTR_EQ(test, aconnector->mst_mgr.dev, &adev->ddev);
|
||||
KUNIT_EXPECT_PTR_EQ(test, aconnector->mst_mgr.aux, &aconnector->dm_dp_aux.aux);
|
||||
KUNIT_EXPECT_EQ(test, aconnector->mst_mgr.max_payloads, 4);
|
||||
KUNIT_EXPECT_TRUE(test, aconnector->mst_mgr.cbs != NULL);
|
||||
|
||||
drm_dp_mst_topology_mgr_destroy(&aconnector->mst_mgr);
|
||||
drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux);
|
||||
kfree(aconnector->dm_dp_aux.aux.name);
|
||||
drm_connector_cleanup(&aconnector->base);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_mst_test_atomic_best_encoder - Test MST encoder selection
|
||||
* @test: KUnit test context
|
||||
@@ -1015,6 +1339,7 @@ static void dm_mst_test_detect_unregistered(struct kunit *test)
|
||||
(int)connector_status_disconnected);
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_DRM_AMD_DC_FP)
|
||||
/**
|
||||
* dm_mst_test_fp_guarded_public_stubs - Test FP-off public fallbacks
|
||||
* @test: KUnit test context
|
||||
@@ -1027,6 +1352,7 @@ static void dm_mst_test_fp_guarded_public_stubs(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(NULL, NULL),
|
||||
(enum dc_status)DC_OK);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct kunit_case dm_mst_types_test_cases[] = {
|
||||
/* needs_dsc_aux_workaround tests */
|
||||
@@ -1038,11 +1364,13 @@ static struct kunit_case dm_mst_types_test_cases[] = {
|
||||
KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_zero_sink_count),
|
||||
/* dm_mst_get_pbn_divider tests */
|
||||
KUNIT_CASE(dm_mst_test_pbn_divider_null_link),
|
||||
KUNIT_CASE(dm_mst_test_pbn_divider_uses_link_bandwidth),
|
||||
/* amdgpu_dm_mst_reset_mst_connector_setting tests */
|
||||
KUNIT_CASE(dm_mst_test_reset_connector_setting),
|
||||
/* retrieve_downstream_port_device tests */
|
||||
KUNIT_CASE(dm_mst_test_retrieve_downstream_no_aux),
|
||||
KUNIT_CASE(dm_mst_test_retrieve_downstream_present),
|
||||
KUNIT_CASE(dm_mst_test_retrieve_downstream_aux_error),
|
||||
/* retrieve_branch_specific_data tests */
|
||||
KUNIT_CASE(dm_mst_test_retrieve_branch_no_parent),
|
||||
KUNIT_CASE(dm_mst_test_retrieve_branch_reads_oui),
|
||||
@@ -1056,6 +1384,7 @@ static struct kunit_case dm_mst_types_test_cases[] = {
|
||||
KUNIT_CASE(dm_mst_test_aux_transfer_partial_write),
|
||||
KUNIT_CASE(dm_mst_test_aux_transfer_error_result),
|
||||
KUNIT_CASE(dm_mst_test_aux_transfer_hpd_discon_quirk),
|
||||
KUNIT_CASE(dm_mst_test_aux_transfer_non_ack_reply),
|
||||
/* dm_dp_aux_fill_payload_flags tests */
|
||||
KUNIT_CASE(dm_mst_test_fill_payload_flags_native_write),
|
||||
KUNIT_CASE(dm_mst_test_fill_payload_flags_native_read),
|
||||
@@ -1068,6 +1397,12 @@ static struct kunit_case dm_mst_types_test_cases[] = {
|
||||
KUNIT_CASE(dm_mst_test_select_esi_dpcd_esi),
|
||||
/* dm_handle_mst_sideband_msg_ready_event tests */
|
||||
KUNIT_CASE(dm_mst_test_sideband_msg_ready_no_ready_bits),
|
||||
KUNIT_CASE(dm_mst_test_sideband_msg_ready_read_error),
|
||||
KUNIT_CASE(dm_mst_test_sideband_msg_ready_without_mst_state),
|
||||
KUNIT_CASE(dm_mst_test_down_rep_msg_ready_wrapper),
|
||||
/* amdgpu_dm_initialize_dp_connector tests */
|
||||
KUNIT_CASE(dm_mst_test_initialize_dp_connector_edp),
|
||||
KUNIT_CASE(dm_mst_test_initialize_dp_connector_mst),
|
||||
/* dm_mst_atomic_best_encoder tests */
|
||||
KUNIT_CASE(dm_mst_test_atomic_best_encoder),
|
||||
/* dm_dp_create_fake_mst_encoders tests */
|
||||
@@ -1077,7 +1412,9 @@ static struct kunit_case dm_mst_types_test_cases[] = {
|
||||
/* dm_dp_mst_detect tests */
|
||||
KUNIT_CASE(dm_mst_test_detect_unregistered),
|
||||
/* CONFIG_DRM_AMD_DC_FP disabled public paths */
|
||||
#if !defined(CONFIG_DRM_AMD_DC_FP)
|
||||
KUNIT_CASE(dm_mst_test_fp_guarded_public_stubs),
|
||||
#endif
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -364,6 +364,47 @@ static void dm_test_wb_connector_init_success(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, wbcon->base.encoder.possible_crtcs, 0x1);
|
||||
}
|
||||
|
||||
/* Tests for amdgpu_dm_wb_prepare_job / amdgpu_dm_wb_cleanup_job */
|
||||
|
||||
/**
|
||||
* dm_test_wb_prepare_job_no_fb - Verify prepare_job early return without a framebuffer
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* When job->fb is NULL there is nothing to pin, so amdgpu_dm_wb_prepare_job()
|
||||
* must return 0 without touching any buffer object.
|
||||
*/
|
||||
static void dm_test_wb_prepare_job_no_fb(struct kunit *test)
|
||||
{
|
||||
struct drm_writeback_job *job;
|
||||
int ret;
|
||||
|
||||
job = kunit_kzalloc(test, sizeof(*job), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, job);
|
||||
|
||||
job->fb = NULL;
|
||||
ret = amdgpu_dm_wb_prepare_job(NULL, job);
|
||||
KUNIT_EXPECT_EQ(test, ret, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* dm_test_wb_cleanup_job_no_fb - Verify cleanup_job early return without a framebuffer
|
||||
* @test: KUnit test context
|
||||
*
|
||||
* When job->fb is NULL there is nothing to unpin, so amdgpu_dm_wb_cleanup_job()
|
||||
* must return immediately.
|
||||
*/
|
||||
static void dm_test_wb_cleanup_job_no_fb(struct kunit *test)
|
||||
{
|
||||
struct drm_writeback_job *job;
|
||||
|
||||
job = kunit_kzalloc(test, sizeof(*job), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_NULL(test, job);
|
||||
|
||||
job->fb = NULL;
|
||||
/* Should return without dereferencing any buffer object. */
|
||||
amdgpu_dm_wb_cleanup_job(NULL, job);
|
||||
}
|
||||
|
||||
static struct kunit_case dm_wb_test_cases[] = {
|
||||
/* amdgpu_dm_wb_encoder_atomic_check */
|
||||
KUNIT_CASE(dm_test_wb_atomic_check_no_job),
|
||||
@@ -378,6 +419,9 @@ static struct kunit_case dm_wb_test_cases[] = {
|
||||
KUNIT_CASE(dm_test_wb_get_modes_bounded_by_max),
|
||||
/* amdgpu_dm_wb_connector_init */
|
||||
KUNIT_CASE(dm_test_wb_connector_init_success),
|
||||
/* amdgpu_dm_wb_prepare_job / amdgpu_dm_wb_cleanup_job */
|
||||
KUNIT_CASE(dm_test_wb_prepare_job_no_fb),
|
||||
KUNIT_CASE(dm_test_wb_cleanup_job_no_fb),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -999,15 +999,21 @@ void dcn42_get_smu_clocks(struct clk_mgr_internal *clk_mgr_int)
|
||||
clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_fclk_levels = dpm_clks->NumFclkLevelsEnabled;
|
||||
clk_mgr_base->bw_params->clk_table.num_entries = dpm_clks->NumFclkLevelsEnabled;
|
||||
|
||||
/* Memory Pstate table is in reverse order*/
|
||||
/* Memory Pstate table is in reverse order for dcn42. Proper way to map this is for pmfw to provide an fclk indexed uclk key since we consume fclk matched pairs.*/
|
||||
ASSERT(dpm_clks->NumMemPstatesEnabled <= NUM_MEM_PSTATE_LEVELS);
|
||||
if (dpm_clks->NumMemPstatesEnabled > NUM_MEM_PSTATE_LEVELS)
|
||||
dpm_clks->NumMemPstatesEnabled = NUM_MEM_PSTATE_LEVELS;
|
||||
for (i = 0; i < dpm_clks->NumMemPstatesEnabled; i++) {
|
||||
clk_mgr_base->bw_params->clk_table.entries[dpm_clks->NumMemPstatesEnabled - 1 - i].memclk_mhz = dpm_clks->MemPstateTable[i].MemClk;
|
||||
clk_mgr_base->bw_params->clk_table.entries[dpm_clks->NumMemPstatesEnabled - 1 - i].wck_ratio = dcn42_convert_wck_ratio(dpm_clks->MemPstateTable[i].WckRatio) ;
|
||||
for (i = 0; i < dpm_clks->NumDcfClkLevelsEnabled; i++) {
|
||||
if (i < dpm_clks->NumMemPstatesEnabled) {
|
||||
clk_mgr_base->bw_params->clk_table.entries[dpm_clks->NumMemPstatesEnabled - 1 - i].memclk_mhz = dpm_clks->MemPstateTable[i].MemClk;
|
||||
clk_mgr_base->bw_params->clk_table.entries[dpm_clks->NumMemPstatesEnabled - 1 - i].wck_ratio = dcn42_convert_wck_ratio(dpm_clks->MemPstateTable[i].WckRatio);
|
||||
} else {
|
||||
clk_mgr_base->bw_params->clk_table.entries[i].memclk_mhz = dpm_clks->MemPstateTable[0].MemClk;
|
||||
clk_mgr_base->bw_params->clk_table.entries[i].wck_ratio = dcn42_convert_wck_ratio(dpm_clks->MemPstateTable[0].WckRatio);
|
||||
}
|
||||
}
|
||||
clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_memclk_levels = dpm_clks->NumMemPstatesEnabled;
|
||||
|
||||
clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_memclk_levels = dpm_clks->NumDcfClkLevelsEnabled;
|
||||
|
||||
/* DTBCLK*/
|
||||
clk_mgr_base->bw_params->clk_table.entries[0].dtbclk_mhz = 600; /* Fixed on platform */
|
||||
|
||||
@@ -769,22 +769,23 @@ void dc_stream_set_dither_option(struct dc_stream_state *stream,
|
||||
{
|
||||
struct bit_depth_reduction_params params;
|
||||
struct dc_link *link = stream->link;
|
||||
struct pipe_ctx *pipes = NULL;
|
||||
struct resource_context *res_ctx = &link->dc->current_state->res_ctx;
|
||||
struct pipe_ctx *otg_master;
|
||||
struct pipe_ctx *opp_heads[MAX_PIPES];
|
||||
int opp_cnt;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_PIPES; i++) {
|
||||
if (link->dc->current_state->res_ctx.pipe_ctx[i].stream ==
|
||||
stream) {
|
||||
pipes = &link->dc->current_state->res_ctx.pipe_ctx[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pipes)
|
||||
otg_master = resource_get_otg_master_for_stream(res_ctx, stream);
|
||||
if (!otg_master)
|
||||
return;
|
||||
if (option > DITHER_OPTION_MAX)
|
||||
return;
|
||||
|
||||
opp_cnt = resource_get_opp_heads_for_otg_master(otg_master, res_ctx, opp_heads);
|
||||
|
||||
if (opp_cnt == 0)
|
||||
return;
|
||||
|
||||
dc_exit_ips_for_hw_access(stream->ctx->dc);
|
||||
|
||||
stream->dither_option = option;
|
||||
@@ -793,16 +794,30 @@ void dc_stream_set_dither_option(struct dc_stream_state *stream,
|
||||
resource_build_bit_depth_reduction_params(stream, ¶ms);
|
||||
stream->bit_depth_params = params;
|
||||
|
||||
if (pipes->plane_res.xfm &&
|
||||
pipes->plane_res.xfm->funcs->transform_set_pixel_storage_depth) {
|
||||
pipes->plane_res.xfm->funcs->transform_set_pixel_storage_depth(
|
||||
pipes->plane_res.xfm,
|
||||
pipes->plane_res.scl_data.lb_params.depth,
|
||||
&stream->bit_depth_params);
|
||||
}
|
||||
/*
|
||||
* Program bit-depth reduction (dither) on every OPP head of the
|
||||
* stream. Under ODM combine there is more than one OPP head and they
|
||||
* must all be kept in sync, otherwise (e.g. when CRC capture requests
|
||||
* dither off) a secondary ODM segment can keep dither enabled and
|
||||
* produce a different CRC than the primary segment.
|
||||
*/
|
||||
for (i = 0; i < opp_cnt; i++) {
|
||||
struct pipe_ctx *opp_head = opp_heads[i];
|
||||
|
||||
pipes->stream_res.opp->funcs->
|
||||
opp_program_bit_depth_reduction(pipes->stream_res.opp, ¶ms);
|
||||
if (opp_head->plane_res.xfm &&
|
||||
opp_head->plane_res.xfm->funcs->transform_set_pixel_storage_depth) {
|
||||
opp_head->plane_res.xfm->funcs->transform_set_pixel_storage_depth(
|
||||
opp_head->plane_res.xfm,
|
||||
opp_head->plane_res.scl_data.lb_params.depth,
|
||||
&stream->bit_depth_params);
|
||||
}
|
||||
|
||||
if (opp_head->stream_res.opp &&
|
||||
opp_head->stream_res.opp->funcs->opp_program_bit_depth_reduction) {
|
||||
opp_head->stream_res.opp->funcs->opp_program_bit_depth_reduction(
|
||||
opp_head->stream_res.opp, ¶ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool dc_stream_set_gamut_remap(struct dc *dc, const struct dc_stream_state *stream)
|
||||
@@ -1930,6 +1945,13 @@ bool dc_validate_boot_timing(const struct dc *dc,
|
||||
struct display_stream_compressor *dsc = NULL;
|
||||
struct dcn_dsc_state dsc_state = {0};
|
||||
|
||||
if (dc->ctx->dce_version < DCN_VERSION_4_2) {
|
||||
/*vbios enabled eDP dsc for one of DCN315 only but it has known issue,
|
||||
since there is no production bios update, block it there*/
|
||||
DC_LOG_DEBUG("boot timing validation failed due to unsupported DSC on this ASIC\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Find DSC associated with this timing generator */
|
||||
if (tg_inst < (unsigned int)dc->res_pool->res_cap->num_dsc) {
|
||||
dsc = dc->res_pool->dscs[tg_inst];
|
||||
@@ -6218,6 +6240,160 @@ void dc_interrupt_ack(struct dc *dc, enum dc_irq_source src)
|
||||
dal_irq_service_ack(dc->res_pool->irqs, src);
|
||||
}
|
||||
|
||||
/* Preserve this tg if a physical link is still lighting a present display */
|
||||
static bool should_preserve_tg(struct dc *dc, struct timing_generator *tg)
|
||||
{
|
||||
unsigned int i, j;
|
||||
|
||||
/* Check if a physical link is lighting this tg */
|
||||
for (i = 0; i < dc->link_count; i++) {
|
||||
struct dc_link *link = dc->links[i];
|
||||
int fe;
|
||||
|
||||
if (!link || link->ep_type != DISPLAY_ENDPOINT_PHY ||
|
||||
!link->link_enc ||
|
||||
!link->link_enc->funcs->is_dig_enabled ||
|
||||
!link->link_enc->funcs->is_dig_enabled(link->link_enc) ||
|
||||
!link->link_enc->funcs->get_dig_frontend)
|
||||
continue;
|
||||
|
||||
/* Get the DIG front-end this link's encoder drives; skip if none */
|
||||
fe = link->link_enc->funcs->get_dig_frontend(link->link_enc);
|
||||
if (fe == ENGINE_ID_UNKNOWN)
|
||||
continue;
|
||||
|
||||
/* Find the stream encoder bound to this link's front-end */
|
||||
for (j = 0; j < dc->res_pool->stream_enc_count; j++) {
|
||||
struct stream_encoder *se = dc->res_pool->stream_enc[j];
|
||||
|
||||
/* Skip unless this stream encoder feeds our front-end and drives this tg */
|
||||
if (se->id != fe || !se->funcs->dig_source_otg ||
|
||||
(int)se->funcs->dig_source_otg(se) != tg->inst)
|
||||
continue;
|
||||
|
||||
/* This link drives the OTG: keep a seamless-boot eDP, or
|
||||
* any external link whose sink is still connected.
|
||||
*/
|
||||
if (link->connector_signal == SIGNAL_TYPE_EDP)
|
||||
return true;
|
||||
if (link->link_enc->funcs->get_hpd_state &&
|
||||
dc->link_srv->get_hpd_state(link))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* GOP/vBIOS may leave an OPTC enabled for a display present at power-on but no
|
||||
* longer driven (e.g. external DP unplugged at boot). Such a dangling pipe keeps
|
||||
* DCN out of idle and blocks s0i3. If nothing needs to survive (no committed
|
||||
* stream or seamless-boot eDP) and no sink is still connected, power down all hw
|
||||
* blocks.
|
||||
*/
|
||||
void dc_disable_dangling_timing_generators(struct dc *dc)
|
||||
{
|
||||
struct dce_hwseq *hws = dc->hwseq;
|
||||
bool any_dangling = false;
|
||||
bool any_preserved = false;
|
||||
bool any_connected = false;
|
||||
unsigned int i;
|
||||
|
||||
/* No real hw to touch on a virtual/emulated environment */
|
||||
if (dc->ctx->dce_environment == DCE_ENV_VIRTUAL_HW)
|
||||
return;
|
||||
|
||||
/* Wake hw out of IPS before reading/touching tg state */
|
||||
dc_exit_ips_for_hw_access(dc);
|
||||
|
||||
/* Classify every enabled tg as either to-preserve or dangling */
|
||||
for (i = 0; i < dc->res_pool->timing_generator_count; i++) {
|
||||
struct timing_generator *tg = dc->res_pool->timing_generators[i];
|
||||
|
||||
if (!tg || !tg->funcs->is_tg_enabled ||
|
||||
!tg->funcs->is_tg_enabled(tg))
|
||||
continue;
|
||||
|
||||
if (should_preserve_tg(dc, tg))
|
||||
any_preserved = true;
|
||||
else
|
||||
any_dangling = true;
|
||||
}
|
||||
|
||||
/* A physically connected sink (HPD asserted) will be re-lit by a
|
||||
* subsequent atomic commit. For that case we don't call the global
|
||||
* power_down().
|
||||
*/
|
||||
for (i = 0; i < dc->link_count; i++) {
|
||||
struct dc_link *link = dc->links[i];
|
||||
|
||||
if (link && link->ep_type == DISPLAY_ENDPOINT_PHY &&
|
||||
link->link_enc && link->link_enc->funcs &&
|
||||
link->link_enc->funcs->get_hpd_state &&
|
||||
dc->link_srv->get_hpd_state(link)) {
|
||||
any_connected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!any_dangling)
|
||||
return;
|
||||
|
||||
if (!any_preserved && !any_connected && hws && hws->funcs.power_down) {
|
||||
/* Truly headless / all sinks unplugged: nothing to preserve */
|
||||
DC_LOG_DC("%s: powering down dangling hw blocks to allow idle\n",
|
||||
__func__);
|
||||
hws->funcs.power_down(dc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
unsigned 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 &&
|
||||
|
||||
@@ -720,20 +720,17 @@ bool dc_stream_remove_writeback(struct dc *dc,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* remove writeback info for disabled writeback pipes from stream */
|
||||
/* remove writeback info for the requested writeback pipe from stream */
|
||||
for (i = 0, j = 0; i < stream->num_wb_info; i++) {
|
||||
if (stream->writeback_info[i].wb_enabled) {
|
||||
/* drop every entry that targets the pipe being removed */
|
||||
if (stream->writeback_info[i].dwb_pipe_inst == dwb_pipe_inst)
|
||||
continue;
|
||||
|
||||
if (stream->writeback_info[i].dwb_pipe_inst == dwb_pipe_inst)
|
||||
stream->writeback_info[i].wb_enabled = false;
|
||||
|
||||
/* trim the array */
|
||||
if (j < i) {
|
||||
memcpy(&stream->writeback_info[j], &stream->writeback_info[i],
|
||||
sizeof(struct dc_writeback_info));
|
||||
j++;
|
||||
}
|
||||
}
|
||||
/* keep this entry, compacting it down when earlier entries were removed */
|
||||
if (j != i)
|
||||
memcpy(&stream->writeback_info[j], &stream->writeback_info[i],
|
||||
sizeof(struct dc_writeback_info));
|
||||
j++;
|
||||
}
|
||||
stream->num_wb_info = j;
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ struct dcn_dsc_reg_state;
|
||||
struct dcn_optc_reg_state;
|
||||
struct dcn_dccg_reg_state;
|
||||
|
||||
#define DC_VER "3.2.388"
|
||||
#define DC_VER "3.2.389"
|
||||
|
||||
/**
|
||||
* MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC
|
||||
@@ -632,6 +632,7 @@ enum visual_confirm {
|
||||
VISUAL_CONFIRM_SMARTMUX_DGPU = 10,
|
||||
VISUAL_CONFIRM_REPLAY = 12,
|
||||
VISUAL_CONFIRM_SUBVP = 14,
|
||||
VISUAL_CONFIRM_ABM = 15,
|
||||
VISUAL_CONFIRM_MCLK_SWITCH = 16,
|
||||
VISUAL_CONFIRM_FAMS2 = 19,
|
||||
VISUAL_CONFIRM_HW_CURSOR = 20,
|
||||
@@ -1288,6 +1289,8 @@ struct dc_debug_options {
|
||||
bool enable_replay_esd_recovery;
|
||||
uint8_t iommu_mismatch_temp_wka;
|
||||
bool disable_dynamic_expansion_for_test_pattern;
|
||||
uint32_t dml21_custom_derate_num_dpms;
|
||||
uint32_t dml21_custom_derate_at_dpm[DML2_MAX_NUM_DPM_LVL];
|
||||
};
|
||||
|
||||
|
||||
@@ -1933,6 +1936,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;
|
||||
@@ -2993,6 +2998,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);
|
||||
|
||||
@@ -3007,6 +3013,8 @@ void dc_resume(struct dc *dc);
|
||||
|
||||
void dc_power_down_on_boot(struct dc *dc);
|
||||
|
||||
void dc_disable_dangling_timing_generators(struct dc *dc);
|
||||
|
||||
/*
|
||||
* HDCP Interfaces
|
||||
*/
|
||||
|
||||
@@ -1908,6 +1908,8 @@ static void dc_dmub_srv_ib_based_fams2_update_config(struct dc *dc,
|
||||
config->global.features.bits.enable = enable && context->bw_ctx.bw.dcn.fams2_global_config.features.bits.enable;
|
||||
config->global.features.bits.enable_ppt_check = dc->debug.fams2_config.bits.enable_ppt_check;
|
||||
|
||||
dmub_srv_flush_buffer_mem(dc->ctx->dmub_srv->dmub, &dc->ctx->dmub_srv->dmub->ib_mem_gart);
|
||||
|
||||
dm_execute_dmub_cmd_list(dc->ctx, 1, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
}
|
||||
|
||||
@@ -2073,7 +2075,7 @@ bool dc_dmub_srv_ips_query_residency_info(const struct dc_context *ctx, uint8_t
|
||||
union dmub_rb_cmd cmd;
|
||||
uint32_t bytes = sizeof(struct dmub_ips_residency_info);
|
||||
|
||||
dmub_flush_buffer_mem(&ctx->dmub_srv->dmub->scratch_mem_fb);
|
||||
dmub_srv_flush_buffer_mem(ctx->dmub_srv->dmub, &ctx->dmub_srv->dmub->scratch_mem_fb);
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
|
||||
cmd.ips_query_residency_info.header.type = DMUB_CMD__IPS;
|
||||
|
||||
@@ -1347,6 +1347,14 @@ struct dc_panel_config {
|
||||
struct ilr {
|
||||
bool optimize_edp_link_rate; /* eDP ILR */
|
||||
} ilr;
|
||||
/* CACP*/
|
||||
struct cacp {
|
||||
unsigned int cacp_supported;
|
||||
unsigned int cacp_control_mode;
|
||||
unsigned int strscl_valid;
|
||||
unsigned int strscl_sdr[4];
|
||||
unsigned int strscl_hdr[4];
|
||||
} cacp;
|
||||
/* Adaptive VariBright*/
|
||||
struct adaptive_vb {
|
||||
bool disable_adaptive_vb;
|
||||
|
||||
@@ -572,7 +572,7 @@ static void dccg35_set_hdmistreamclk_src_new(
|
||||
case 0:
|
||||
REG_UPDATE_2(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_EN,
|
||||
(src == HDMI_STREAM_REFCLK) ? 0 : 1,
|
||||
DPSTREAMCLK0_SRC_SEL,
|
||||
HDMISTREAMCLK0_SRC_SEL,
|
||||
(src == HDMI_STREAM_REFCLK) ? 0 : src);
|
||||
break;
|
||||
default:
|
||||
@@ -1654,7 +1654,7 @@ void dccg35_set_dpstreamclk_root_clock_gating(struct dccg *dccg, int dp_hpo_inst
|
||||
|
||||
|
||||
|
||||
static void dccg35_set_hdmistreamclk(
|
||||
void dccg35_set_hdmistreamclk(
|
||||
struct dccg *dccg,
|
||||
enum streamclk_source src,
|
||||
uint32_t otg_inst)
|
||||
@@ -1680,8 +1680,11 @@ void dccg35_set_hdmistreamclk_root_clock_gating(struct dccg *dccg, bool enable)
|
||||
{
|
||||
struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg);
|
||||
|
||||
if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmistream)
|
||||
REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, HDMISTREAMCLK0_ROOT_GATE_DISABLE, enable ? 1 : 0);
|
||||
if (!dccg->ctx->dc->debug.root_clock_optimization.bits.hdmistream && !enable) {
|
||||
DC_LOG_DEBUG("%s: HDMISTREAMCLK0_ROOT_GATE DISABLE = 0 bypassed", __func__);
|
||||
return;
|
||||
}
|
||||
REG_UPDATE(DCCG_GATE_DISABLE_CNTL6, HDMISTREAMCLK0_ROOT_GATE_DISABLE, enable ? 1 : 0);
|
||||
|
||||
DC_LOG_DEBUG("%s: HDMISTREAMCLK0_ROOT_GATE_DISABLE = %d\n", __func__, enable ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -269,5 +269,8 @@ void dccg35_disable_dscclk(struct dccg *dccg, int inst);
|
||||
|
||||
void dccg35_enable_symclk_se(struct dccg *dccg, uint32_t stream_enc_inst, uint32_t link_enc_inst);
|
||||
void dccg35_disable_symclk_se(struct dccg *dccg, uint32_t stream_enc_inst, uint32_t link_enc_inst);
|
||||
|
||||
void dccg35_set_hdmistreamclk(
|
||||
struct dccg *dccg,
|
||||
enum streamclk_source src,
|
||||
uint32_t otg_inst);
|
||||
#endif //__DCN35_DCCG_H__
|
||||
|
||||
@@ -313,7 +313,7 @@ static void dccg42_init(struct dccg *dccg)
|
||||
static const struct dccg_funcs dccg42_funcs = {
|
||||
.enable_hdmicharclk = dccg401_enable_hdmicharclk,
|
||||
.disable_hdmicharclk = dccg42_disable_hdmicharclk,
|
||||
.set_hdmistreamclk = dccg401_set_hdmistreamclk,
|
||||
.set_hdmistreamclk = dccg35_set_hdmistreamclk,
|
||||
.set_hdmistreamclk_root_clock_gating = dccg35_set_hdmistreamclk_root_clock_gating,
|
||||
.update_dpp_dto = dccg35_update_dpp_dto,
|
||||
.dpp_root_clock_control = dccg35_dpp_root_clock_control,
|
||||
|
||||
@@ -23,14 +23,14 @@
|
||||
# Makefile for common 'dce' logic
|
||||
# HW object file under this folder follow similar pattern for HW programming
|
||||
# - register offset and/or shift + mask stored in the dec_hw struct
|
||||
# - register programming through common macros that look up register
|
||||
# - register programming through common macros that look up register
|
||||
# offset/shift/mask stored in dce_hw struct
|
||||
|
||||
DCE = dce_audio.o dce_stream_encoder.o dce_link_encoder.o \
|
||||
dce_mem_input.o dce_clock_source.o dce_scl_filters.o dce_transform.o \
|
||||
dce_opp.o dce_dmcu.o dce_abm.o dce_ipp.o dce_aux.o \
|
||||
dce_i2c.o dce_i2c_hw.o dce_i2c_sw.o dmub_psr.o dmub_abm.o dmub_abm_lcd.o dce_panel_cntl.o \
|
||||
dmub_hw_lock_mgr.o dmub_outbox.o dmub_replay.o
|
||||
dmub_hw_lock_mgr.o dmub_outbox.o dmub_replay.o dmub_abm_cacp.o
|
||||
|
||||
AMD_DAL_DCE = $(addprefix $(AMDDALPATH)/dc/dce/,$(DCE))
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "dmub_abm.h"
|
||||
#include "dmub_abm_lcd.h"
|
||||
#include "dmub_abm_cacp.h"
|
||||
#include "dc.h"
|
||||
#include "core_types.h"
|
||||
#include "dmub_cmd.h"
|
||||
@@ -36,6 +37,7 @@
|
||||
|
||||
#define ABM_FEATURE_NO_SUPPORT 0
|
||||
#define ABM_LCD_SUPPORT 1
|
||||
#define ABM_CACP_SUPPORT 2
|
||||
|
||||
static unsigned int abm_feature_support(struct abm *abm, unsigned int panel_inst)
|
||||
{
|
||||
@@ -52,15 +54,48 @@ static unsigned int abm_feature_support(struct abm *abm, unsigned int panel_inst
|
||||
}
|
||||
|
||||
if (i < edp_num) {
|
||||
ret = ABM_LCD_SUPPORT;
|
||||
if (edp_links[panel_inst]->panel_config.cacp.cacp_supported)
|
||||
ret = ABM_CACP_SUPPORT;
|
||||
else if ((edp_links[panel_inst]->panel_type == PANEL_TYPE_LCD) ||
|
||||
(edp_links[panel_inst]->panel_type == PANEL_TYPE_MINILED))
|
||||
ret = ABM_LCD_SUPPORT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum dc_panel_type abm_get_paneltype(struct abm *abm, unsigned int panel_inst)
|
||||
{
|
||||
struct dc_context *dc = abm->ctx;
|
||||
struct dc_link *edp_links[MAX_NUM_EDP];
|
||||
unsigned int i, edp_num;
|
||||
enum dc_panel_type ret = PANEL_TYPE_NONE;
|
||||
|
||||
dc_get_edp_links(dc->dc, edp_links, &edp_num);
|
||||
|
||||
for (i = 0; i < edp_num; i++) {
|
||||
if (edp_links[i]->link_status.link_active
|
||||
&& panel_inst == i)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < edp_num)
|
||||
ret = edp_links[panel_inst]->panel_type;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dmub_abm_init_ex(struct abm *abm, uint32_t backlight, uint32_t user_level)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
uint8_t panel_mask = 0;
|
||||
|
||||
dmub_abm_init(abm, backlight, user_level);
|
||||
for (i = 0; i < MAX_NUM_EDP; i++)
|
||||
panel_mask |= (0x01 << i);
|
||||
|
||||
if (panel_mask)
|
||||
dmub_cacp_enable_fractional_pwm(abm, panel_mask);
|
||||
}
|
||||
|
||||
static unsigned int dmub_abm_get_current_backlight_ex(struct abm *abm)
|
||||
@@ -82,17 +117,23 @@ static bool dmub_abm_set_level_ex(struct abm *abm, uint32_t level)
|
||||
bool ret = false;
|
||||
unsigned int feature_support, i;
|
||||
uint8_t panel_mask0 = 0;
|
||||
uint8_t panel_mask1 = 0;
|
||||
|
||||
for (i = 0; i < MAX_NUM_EDP; i++) {
|
||||
feature_support = abm_feature_support(abm, i);
|
||||
|
||||
if (feature_support == ABM_LCD_SUPPORT)
|
||||
panel_mask0 |= (0x01 << i);
|
||||
else if (feature_support == ABM_CACP_SUPPORT)
|
||||
panel_mask1 |= (0x01 << i);
|
||||
}
|
||||
|
||||
if (panel_mask0)
|
||||
ret = dmub_abm_set_level(abm, level, panel_mask0);
|
||||
|
||||
if (panel_mask1)
|
||||
ret = dmub_cacp_set_level(abm, level, panel_mask1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -107,6 +148,8 @@ static bool dmub_abm_init_config_ex(struct abm *abm,
|
||||
|
||||
if (feature_support == ABM_LCD_SUPPORT)
|
||||
dmub_abm_init_config(abm, src, bytes, inst);
|
||||
else if (feature_support == ABM_CACP_SUPPORT)
|
||||
dmub_cacp_init(abm, src, bytes, inst);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -120,6 +163,8 @@ static bool dmub_abm_set_pause_ex(struct abm *abm, bool pause, unsigned int pane
|
||||
|
||||
if (feature_support == ABM_LCD_SUPPORT)
|
||||
ret = dmub_abm_set_pause(abm, pause, panel_inst, stream_inst);
|
||||
else if (feature_support == ABM_CACP_SUPPORT)
|
||||
ret = dmub_cacp_set_pause(abm, pause, panel_inst, stream_inst);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -163,6 +208,25 @@ static bool dmub_abm_set_pipe_ex(struct abm *abm,
|
||||
|
||||
if (feature_support == ABM_LCD_SUPPORT)
|
||||
ret = dmub_abm_set_pipe(abm, otg_inst, option, panel_inst, pwrseq_inst);
|
||||
else if (feature_support == ABM_CACP_SUPPORT)
|
||||
ret = dmub_cacp_set_pipe(abm, otg_inst, option, panel_inst, pwrseq_inst);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool dmub_abm_set_event_ex(struct abm *abm, unsigned int full_screen, unsigned int trans_info,
|
||||
unsigned int hdr_mode, unsigned int scaling_enable, unsigned int scaling_strength_map,
|
||||
unsigned int panel_inst)
|
||||
{
|
||||
bool ret = false;
|
||||
unsigned int feature_support;
|
||||
|
||||
feature_support = abm_feature_support(abm, panel_inst);
|
||||
|
||||
if (feature_support == ABM_LCD_SUPPORT)
|
||||
ret = dmub_abm_set_event(abm, scaling_enable, scaling_strength_map, panel_inst);
|
||||
else if (feature_support == ABM_CACP_SUPPORT)
|
||||
ret = dmub_cacp_set_event(abm, full_screen, trans_info, hdr_mode, scaling_enable, panel_inst);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -176,11 +240,17 @@ static bool dmub_abm_set_backlight_level_pwm_ex(struct abm *abm,
|
||||
(void)controller_id;
|
||||
bool ret = false;
|
||||
unsigned int feature_support;
|
||||
enum dc_panel_type panel_type = PANEL_TYPE_NONE;
|
||||
|
||||
feature_support = abm_feature_support(abm, panel_inst);
|
||||
|
||||
if (feature_support == ABM_LCD_SUPPORT)
|
||||
ret = dmub_abm_set_backlight_level(abm, backlight_pwm_u16_16, frame_ramp, panel_inst);
|
||||
else if (feature_support == ABM_CACP_SUPPORT) {
|
||||
panel_type = abm_get_paneltype(abm, panel_inst);
|
||||
if (panel_type == PANEL_TYPE_MINILED)
|
||||
ret = dmub_cacp_set_backlight_level(abm, backlight_pwm_u16_16, frame_ramp, panel_inst);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -194,6 +264,7 @@ static const struct abm_funcs abm_funcs = {
|
||||
.set_abm_pause = dmub_abm_set_pause_ex,
|
||||
.save_restore = dmub_abm_save_restore_ex,
|
||||
.set_pipe_ex = dmub_abm_set_pipe_ex,
|
||||
.set_abm_event = dmub_abm_set_event_ex,
|
||||
.set_backlight_level_pwm = dmub_abm_set_backlight_level_pwm_ex,
|
||||
};
|
||||
|
||||
|
||||
213
drivers/gpu/drm/amd/display/dc/dce/dmub_abm_cacp.c
Normal file
213
drivers/gpu/drm/amd/display/dc/dce/dmub_abm_cacp.c
Normal file
@@ -0,0 +1,213 @@
|
||||
/* Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. */
|
||||
|
||||
#include "dmub_abm.h"
|
||||
#include "dmub_abm_cacp.h"
|
||||
#include "dce_abm.h"
|
||||
#include "dc.h"
|
||||
#include "dc_dmub_srv.h"
|
||||
#include "dmub/dmub_srv.h"
|
||||
#include "core_types.h"
|
||||
|
||||
#define CACP_LEVEL_NUM 4
|
||||
|
||||
void dmub_cacp_init(struct abm *abm, const char *src, unsigned int bytes, unsigned int panel_inst)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
struct dc_context *dc = abm->ctx;
|
||||
uint8_t panel_mask = 0x01 << panel_inst;
|
||||
struct dc_link *edp_links[MAX_NUM_EDP];
|
||||
unsigned int i, edp_num;
|
||||
|
||||
// TODO: Optimize by only reading back final 4 bytes
|
||||
dmub_srv_flush_buffer_mem(dc->dmub_srv->dmub, &dc->dmub_srv->dmub->scratch_mem_fb);
|
||||
|
||||
// Copy iramtable into cw7
|
||||
memcpy(dc->dmub_srv->dmub->scratch_mem_fb.cpu_addr, (void *)src, bytes);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
// Fw will copy from cw7 to fw_state
|
||||
cmd.cacp_init_config.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_init_config.header.sub_type = DMUB_CMD__CACP_INIT_CONFIG;
|
||||
cmd.cacp_init_config.cacp_init_config_data.src.quad_part = dc->dmub_srv->dmub->scratch_mem_fb.gpu_addr;
|
||||
cmd.cacp_init_config.cacp_init_config_data.bytes = (uint16_t)bytes;
|
||||
cmd.cacp_init_config.cacp_init_config_data.panel_mask = panel_mask;
|
||||
cmd.cacp_init_config.cacp_init_config_data.visual_confirm =
|
||||
(dc->dc->debug.visual_confirm == VISUAL_CONFIRM_ABM) ? true : false;
|
||||
|
||||
cmd.cacp_init_config.header.payload_bytes = sizeof(struct dmub_cmd_cacp_init_config_data);
|
||||
|
||||
dc_get_edp_links(dc->dc, edp_links, &edp_num);
|
||||
for (i = 0; i < edp_num; i++) {
|
||||
if (panel_inst == i)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i < edp_num) {
|
||||
cmd.cacp_init_config.cacp_init_config_data.strscl_valid =
|
||||
(uint8_t)edp_links[panel_inst]->panel_config.cacp.strscl_valid;
|
||||
cmd.cacp_init_config.cacp_init_config_data.mode =
|
||||
edp_links[panel_inst]->panel_config.cacp.cacp_control_mode ?
|
||||
DMUB_CMD_CACP_CONTROL_MODE_1 : DMUB_CMD_CACP_CONTROL_MODE_0;
|
||||
for (int j = 0; j < CACP_LEVEL_NUM; j++) {
|
||||
cmd.cacp_init_config.cacp_init_config_data.strscl_sdr[j] =
|
||||
(uint8_t)edp_links[panel_inst]->panel_config.cacp.strscl_sdr[j];
|
||||
cmd.cacp_init_config.cacp_init_config_data.strscl_hdr[j] =
|
||||
(uint8_t)edp_links[panel_inst]->panel_config.cacp.strscl_hdr[j];
|
||||
}
|
||||
}
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
}
|
||||
|
||||
bool dmub_cacp_set_level(struct abm *abm, unsigned int abm_level, unsigned char panel_mask)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
struct dc_context *dc = abm->ctx;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cacp_set_level.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_set_level.header.sub_type = DMUB_CMD__CACP_SET_LEVEL;
|
||||
|
||||
cmd.cacp_set_level.cacp_set_level_data.level = abm_level;
|
||||
cmd.cacp_set_level.cacp_set_level_data.version = DMUB_CMD_CACP_CONTROL_VERSION_1;
|
||||
cmd.cacp_set_level.cacp_set_level_data.panel_mask = panel_mask;
|
||||
|
||||
cmd.cacp_set_level.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_level_data);
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dmub_cacp_set_pipe(struct abm *abm, unsigned int otg_inst,
|
||||
unsigned int pipe_option, unsigned int panel_inst, unsigned int pwrseq_inst)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
struct dc_context *dc = abm->ctx;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cacp_set_pipe.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_set_pipe.header.sub_type = DMUB_CMD__CACP_SET_PIPE;
|
||||
|
||||
cmd.cacp_set_pipe.cacp_set_pipe_data.otg_inst = (uint8_t)otg_inst;
|
||||
cmd.cacp_set_pipe.cacp_set_pipe_data.panel_inst = (uint8_t)panel_inst;
|
||||
cmd.cacp_set_pipe.cacp_set_pipe_data.set_pipe_option = (uint8_t)pipe_option;
|
||||
cmd.cacp_set_pipe.cacp_set_pipe_data.pwrseq_inst = (uint8_t)pwrseq_inst;
|
||||
cmd.cacp_set_pipe.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_pipe_data);
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dmub_cacp_set_event(struct abm *abm, unsigned int full_screen, unsigned int trans_info,
|
||||
unsigned int hdr_mode, unsigned int scaling_enable, unsigned int panel_inst)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
struct dc_context *dc = abm->ctx;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cacp_set_event.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_set_event.header.sub_type = DMUB_CMD__CACP_SET_EVENT;
|
||||
|
||||
//TODO:
|
||||
cmd.cacp_set_event.cacp_set_event_data.full_screen_mode = (uint8_t)full_screen;
|
||||
cmd.cacp_set_event.cacp_set_event_data.trans_info = trans_info;
|
||||
cmd.cacp_set_event.cacp_set_event_data.hdr_mode = (uint8_t)hdr_mode;
|
||||
cmd.cacp_set_event.cacp_set_event_data.vb_scaling_enable = (uint8_t)scaling_enable;
|
||||
cmd.cacp_set_event.cacp_set_event_data.panel_mask = (1<<panel_inst);
|
||||
|
||||
cmd.cacp_set_event.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_event_data);
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dmub_cacp_set_pause(struct abm *abm, bool pause, unsigned int panel_inst, unsigned int otg_inst)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
struct dc_context *dc = abm->ctx;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cacp_pause.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_pause.header.sub_type = DMUB_CMD__CACP_PAUSE;
|
||||
|
||||
cmd.cacp_pause.cacp_pause_data.panel_mask = (1<<panel_inst);
|
||||
cmd.cacp_pause.cacp_pause_data.otg_inst = (uint8_t)otg_inst;
|
||||
cmd.cacp_pause.cacp_pause_data.enable = pause;
|
||||
|
||||
cmd.cacp_pause.header.payload_bytes = sizeof(struct dmub_cmd_cacp_pause_data);
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dmub_cacp_set_backlight_level(struct abm *abm,
|
||||
unsigned int backlight_pwm_u16_16,
|
||||
unsigned int frame_ramp,
|
||||
unsigned int panel_inst)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
struct dc_context *dc = abm->ctx;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cacp_set_backlight.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_set_backlight.header.sub_type = DMUB_CMD__CACP_SET_BACKLIGHT;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.frame_ramp = frame_ramp;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.backlight_user_level = backlight_pwm_u16_16;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.version = DMUB_CMD_CACP_CONTROL_VERSION_1;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.panel_mask = (0x01 << panel_inst);
|
||||
cmd.cacp_set_backlight.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_backlight_data);
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void dmub_cacp_enable_fractional_pwm(struct abm *abm, uint8_t panel_mask)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
struct dc_context *dc = abm->ctx;
|
||||
uint32_t fractional_pwm = (dc->dc->config.disable_fractional_pwm == false) ? 1 : 0;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cacp_set_pwm_frac.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_set_pwm_frac.header.sub_type = DMUB_CMD__CACP_SET_PWM_FRAC;
|
||||
cmd.cacp_set_pwm_frac.cacp_set_pwm_frac_data.fractional_pwm = fractional_pwm;
|
||||
cmd.cacp_set_pwm_frac.cacp_set_pwm_frac_data.version = DMUB_CMD_CACP_CONTROL_VERSION_1;
|
||||
cmd.cacp_set_pwm_frac.cacp_set_pwm_frac_data.panel_mask = panel_mask;
|
||||
cmd.cacp_set_pwm_frac.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_pwm_frac_data);
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
}
|
||||
|
||||
bool dmub_cacp_get_histogram(
|
||||
struct dc_context *dc,
|
||||
unsigned int panel_inst,
|
||||
unsigned int *histogram,
|
||||
enum dmub_abm_histogram_type histogram_type,
|
||||
unsigned int size)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
|
||||
dmub_srv_flush_buffer_mem(dc->dmub_srv->dmub, &dc->dmub_srv->dmub->scratch_mem_fb);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cacp_get_histogram.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_get_histogram.header.sub_type = DMUB_CMD__CACP_GET_HISTOGRAM;
|
||||
|
||||
cmd.cacp_get_histogram.dest.quad_part = dc->dmub_srv->dmub->scratch_mem_fb.gpu_addr;
|
||||
cmd.cacp_get_histogram.bytes = (uint16_t)size;
|
||||
cmd.cacp_get_histogram.panel_inst = (uint8_t)panel_inst;
|
||||
cmd.cacp_get_histogram.histogram_type = histogram_type;
|
||||
cmd.cacp_get_histogram.header.payload_bytes = sizeof(struct dmub_rb_cmd_cacp_get_histogram);
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
|
||||
memcpy((void *)histogram, dc->dmub_srv->dmub->scratch_mem_fb.cpu_addr, size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
20
drivers/gpu/drm/amd/display/dc/dce/dmub_abm_cacp.h
Normal file
20
drivers/gpu/drm/amd/display/dc/dce/dmub_abm_cacp.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* Copyright (C) 2022 Advanced Micro Devices, Inc. All rights reserved. */
|
||||
|
||||
#include "dmub_cmd.h"
|
||||
|
||||
#ifndef __DMUB_ABM_CACP_H__
|
||||
#define __DMUB_ABM_CACP_H__
|
||||
|
||||
void dmub_cacp_init(struct abm *abm, const char *src, unsigned int bytes, unsigned int panel_inst);
|
||||
bool dmub_cacp_set_level(struct abm *abm, unsigned int cacp_level, unsigned char panel_mask);
|
||||
bool dmub_cacp_set_pipe(struct abm *abm, unsigned int otg_inst, unsigned int pipe_option,
|
||||
unsigned int panel_inst, unsigned int pwrseq_inst);
|
||||
bool dmub_cacp_set_event(struct abm *abm, unsigned int full_screen, unsigned int trans_info, unsigned int hdr_mode,
|
||||
unsigned int scaling_enable, unsigned int panel_inst);
|
||||
bool dmub_cacp_set_pause(struct abm *abm, bool pause, unsigned int panel_inst, unsigned int otg_inst);
|
||||
bool dmub_cacp_set_backlight_level(struct abm *abm, unsigned int backlight_pwm_u16_16, unsigned int frame_ramp,
|
||||
unsigned int panel_inst);
|
||||
void dmub_cacp_enable_fractional_pwm(struct abm *abm, uint8_t panel_mask);
|
||||
bool dmub_cacp_get_histogram(struct dc_context *dc, unsigned int panel_inst, unsigned int *histogram,
|
||||
enum dmub_abm_histogram_type histogram_type, unsigned int size);
|
||||
#endif
|
||||
@@ -166,7 +166,7 @@ void dmub_abm_init_config(struct abm *abm,
|
||||
uint8_t panel_mask = 0x01 << inst;
|
||||
|
||||
// TODO: Optimize by only reading back final 4 bytes
|
||||
dmub_flush_buffer_mem(&dc->dmub_srv->dmub->scratch_mem_fb);
|
||||
dmub_srv_flush_buffer_mem(dc->dmub_srv->dmub, &dc->dmub_srv->dmub->scratch_mem_fb);
|
||||
|
||||
// Copy iramtable into cw7
|
||||
memcpy(dc->dmub_srv->dmub->scratch_mem_fb.cpu_addr, (void *)src, bytes);
|
||||
@@ -227,7 +227,7 @@ bool dmub_abm_save_restore(
|
||||
unsigned int bytes = sizeof(struct abm_save_restore);
|
||||
|
||||
// TODO: Optimize by only reading back final 4 bytes
|
||||
dmub_flush_buffer_mem(&dc->dmub_srv->dmub->scratch_mem_fb);
|
||||
dmub_srv_flush_buffer_mem(dc->dmub_srv->dmub, &dc->dmub_srv->dmub->scratch_mem_fb);
|
||||
|
||||
// Copy iramtable into cw7
|
||||
memcpy(dc->dmub_srv->dmub->scratch_mem_fb.cpu_addr, (void *)pData, bytes);
|
||||
|
||||
@@ -1189,8 +1189,9 @@ void dcn10_link_encoder_dp_set_phy_pattern(
|
||||
set_dp_phy_pattern_prbs7(enc10);
|
||||
break;
|
||||
case DP_TEST_PATTERN_80BIT_CUSTOM:
|
||||
set_dp_phy_pattern_80bit_custom(
|
||||
enc10, param->custom_pattern);
|
||||
if (param)
|
||||
set_dp_phy_pattern_80bit_custom(
|
||||
enc10, param->custom_pattern);
|
||||
break;
|
||||
case DP_TEST_PATTERN_CP2520_1:
|
||||
set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc10, 1);
|
||||
@@ -1202,8 +1203,9 @@ void dcn10_link_encoder_dp_set_phy_pattern(
|
||||
set_dp_phy_pattern_hbr2_compliance_cp2520_2(enc10, 3);
|
||||
break;
|
||||
case DP_TEST_PATTERN_VIDEO_MODE: {
|
||||
set_dp_phy_pattern_passthrough_mode(
|
||||
enc10, param->dp_panel_mode);
|
||||
if (param)
|
||||
set_dp_phy_pattern_passthrough_mode(
|
||||
enc10, param->dp_panel_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,6 @@ static const struct dml2_soc_bb dml2_socbb_dcn42 = {
|
||||
.xtalclk_mhz = 24,
|
||||
.pcie_refclk_mhz = 100,
|
||||
.dchub_refclk_mhz = 50,
|
||||
.mall_allocated_for_dcn_mbytes = 0,
|
||||
.max_outstanding_reqs = 256,
|
||||
.fabric_datapath_to_dcn_data_return_bytes = 32,
|
||||
.return_bus_width_bytes = 64,
|
||||
@@ -216,11 +215,6 @@ static const struct dml2_soc_bb dml2_socbb_dcn42 = {
|
||||
.phy_downspread_percent = 0.38,
|
||||
.dcn_downspread_percent = 0.38,
|
||||
.dispclk_dppclk_vco_speed_mhz = 3000,
|
||||
.do_urgent_latency_adjustment = 0,
|
||||
.mem_word_bytes = 32,
|
||||
.num_dcc_mcaches = 8,
|
||||
.mcache_size_bytes = 2048,
|
||||
.mcache_line_size_bytes = 32,
|
||||
.max_fclk_for_uclk_dpm_khz = 2200 * 1000,
|
||||
};
|
||||
|
||||
|
||||
@@ -963,14 +963,13 @@ static void dpp401_dscl_program_isharp(struct dpp *dpp_base,
|
||||
|
||||
PERF_TRACE();
|
||||
/*power on isharp_delta_mem first*/
|
||||
if (dpp_base->ctx->dc->caps.ips_v2_support) {
|
||||
/*HW default is LS, need to wake up*/
|
||||
REG_UPDATE_2(ISHARP_DELTA_LUT_MEM_PWR_CTRL,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_FORCE, 0,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_DIS, 1);
|
||||
REG_WAIT(ISHARP_DELTA_LUT_MEM_PWR_CTRL,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_STATE, 0, 1, 100);
|
||||
}
|
||||
REG_UPDATE_2(ISHARP_DELTA_LUT_MEM_PWR_CTRL,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_FORCE, 0,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_DIS, 1);
|
||||
|
||||
REG_WAIT(ISHARP_DELTA_LUT_MEM_PWR_CTRL,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_STATE, 0, 1, 100);
|
||||
|
||||
/* ISHARP_MODE */
|
||||
REG_SET_6(ISHARP_MODE, 0,
|
||||
ISHARP_EN, scl_data->dscl_prog_data.isharp_en,
|
||||
@@ -1049,12 +1048,10 @@ static void dpp401_dscl_program_isharp(struct dpp *dpp_base,
|
||||
}
|
||||
|
||||
/*power on isharp_delta_mem first*/
|
||||
if (dpp_base->ctx->dc->caps.ips_v2_support) {
|
||||
/*HW default is LS, need to wake up*/
|
||||
REG_UPDATE_SEQ_2(ISHARP_DELTA_LUT_MEM_PWR_CTRL,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_FORCE, 0,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_DIS, 0);
|
||||
}
|
||||
REG_UPDATE_SEQ_2(ISHARP_DELTA_LUT_MEM_PWR_CTRL,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_FORCE, 0,
|
||||
ISHARP_DELTA_LUT_MEM_PWR_DIS, 0);
|
||||
|
||||
PERF_TRACE();
|
||||
} // dpp401_dscl_program_isharp
|
||||
/**
|
||||
|
||||
@@ -1250,7 +1250,8 @@ static const struct hubbub_funcs hubbub4_01_funcs = {
|
||||
.program_compbuf_segments = dcn401_program_compbuf_segments,
|
||||
.wait_for_det_update = dcn401_wait_for_det_update,
|
||||
.program_arbiter = dcn401_program_arbiter,
|
||||
.hubbub_read_reg_state = hubbub3_read_reg_state
|
||||
.hubbub_read_reg_state = hubbub3_read_reg_state,
|
||||
.get_mall_en = hubbub32_get_mall_en
|
||||
};
|
||||
|
||||
void hubbub401_construct(struct dcn20_hubbub *hubbub2,
|
||||
|
||||
@@ -133,7 +133,9 @@
|
||||
HUBBUB_SF(DCHUBBUB_CTRL_STATUS, ROB_OVERFLOW_STATUS, mask_sh),\
|
||||
HUBBUB_SF(DCHUBBUB_CTRL_STATUS, ROB_OVERFLOW_CLEAR, mask_sh),\
|
||||
HUBBUB_SF(DCHUBBUB_CTRL_STATUS, DCHUBBUB_HW_DEBUG, mask_sh),\
|
||||
HUBBUB_SF(DCHUBBUB_CTRL_STATUS, CSTATE_SWATH_CHK_GOOD_MODE, mask_sh)
|
||||
HUBBUB_SF(DCHUBBUB_CTRL_STATUS, CSTATE_SWATH_CHK_GOOD_MODE, mask_sh),\
|
||||
HUBBUB_SF(DCHUBBUB_ARB_MALL_CNTL, MALL_PREFETCH_COMPLETE, mask_sh),\
|
||||
HUBBUB_SF(DCHUBBUB_ARB_MALL_CNTL, MALL_IN_USE, mask_sh)
|
||||
|
||||
bool hubbub401_program_urgent_watermarks(
|
||||
struct hubbub *hubbub,
|
||||
|
||||
@@ -772,8 +772,7 @@ bool hubp1_is_flip_pending(struct hubp *hubp)
|
||||
if (flip_pending)
|
||||
return true;
|
||||
|
||||
if (hubp &&
|
||||
earliest_inuse_address.grph.addr.quad_part != hubp->request_address.grph.addr.quad_part)
|
||||
if (earliest_inuse_address.grph.addr.quad_part != hubp->request_address.grph.addr.quad_part)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
@@ -286,7 +286,8 @@
|
||||
type MCACHEID_MALL_PREF_2H_P0;\
|
||||
type MCACHEID_MALL_PREF_1H_P1;\
|
||||
type MCACHEID_MALL_PREF_2H_P1;\
|
||||
type HUBP_FGCG_REP_DIS
|
||||
type HUBP_FGCG_REP_DIS;\
|
||||
type DST_Y_DELTA_DRQ_LIMIT
|
||||
|
||||
#define DCN42_HUBP_REG_FIELD_VARIABLE_LIST(type) \
|
||||
type HUBP_3DLUT_CROSSBAR_SEL_G;\
|
||||
|
||||
@@ -580,7 +580,56 @@ static bool hubp42_program_surface_flip_and_addr(
|
||||
|
||||
return true;
|
||||
}
|
||||
void hubp42_setup_interdependent(
|
||||
struct hubp *hubp,
|
||||
struct dml2_dchub_per_pipe_register_set *pipe_regs)
|
||||
{
|
||||
struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp);
|
||||
|
||||
REG_SET_2(PREFETCH_SETTINGS, 0,
|
||||
DST_Y_PREFETCH, pipe_regs->dlg_regs.dst_y_prefetch,
|
||||
VRATIO_PREFETCH, pipe_regs->dlg_regs.vratio_prefetch);
|
||||
|
||||
REG_SET(PREFETCH_SETTINGS_C, 0,
|
||||
VRATIO_PREFETCH_C, pipe_regs->dlg_regs.vratio_prefetch_c);
|
||||
|
||||
REG_SET_2(VBLANK_PARAMETERS_0, 0,
|
||||
DST_Y_PER_VM_VBLANK, pipe_regs->dlg_regs.dst_y_per_vm_vblank,
|
||||
DST_Y_PER_ROW_VBLANK, pipe_regs->dlg_regs.dst_y_per_row_vblank);
|
||||
|
||||
REG_SET_2(FLIP_PARAMETERS_0, 0,
|
||||
DST_Y_PER_VM_FLIP, pipe_regs->dlg_regs.dst_y_per_vm_flip,
|
||||
DST_Y_PER_ROW_FLIP, pipe_regs->dlg_regs.dst_y_per_row_flip);
|
||||
|
||||
REG_SET(VBLANK_PARAMETERS_3, 0,
|
||||
REFCYC_PER_META_CHUNK_VBLANK_L, pipe_regs->dlg_regs.refcyc_per_meta_chunk_vblank_l);
|
||||
|
||||
REG_SET(VBLANK_PARAMETERS_4, 0,
|
||||
REFCYC_PER_META_CHUNK_VBLANK_C, pipe_regs->dlg_regs.refcyc_per_meta_chunk_vblank_c);
|
||||
|
||||
REG_SET(FLIP_PARAMETERS_2, 0,
|
||||
REFCYC_PER_META_CHUNK_FLIP_L, pipe_regs->dlg_regs.refcyc_per_meta_chunk_flip_l);
|
||||
|
||||
REG_SET_2(PER_LINE_DELIVERY_PRE, 0,
|
||||
REFCYC_PER_LINE_DELIVERY_PRE_L, pipe_regs->dlg_regs.refcyc_per_line_delivery_pre_l,
|
||||
REFCYC_PER_LINE_DELIVERY_PRE_C, pipe_regs->dlg_regs.refcyc_per_line_delivery_pre_c);
|
||||
|
||||
REG_SET(DCN_SURF0_TTU_CNTL1, 0,
|
||||
REFCYC_PER_REQ_DELIVERY_PRE,
|
||||
pipe_regs->ttu_regs.refcyc_per_req_delivery_pre_l);
|
||||
REG_SET(DCN_SURF1_TTU_CNTL1, 0,
|
||||
REFCYC_PER_REQ_DELIVERY_PRE,
|
||||
pipe_regs->ttu_regs.refcyc_per_req_delivery_pre_c);
|
||||
REG_SET(DCN_CUR0_TTU_CNTL1, 0,
|
||||
REFCYC_PER_REQ_DELIVERY_PRE, pipe_regs->ttu_regs.refcyc_per_req_delivery_pre_cur0);
|
||||
|
||||
REG_SET_2(DCN_GLOBAL_TTU_CNTL, 0,
|
||||
MIN_TTU_VBLANK, pipe_regs->ttu_regs.min_ttu_vblank,
|
||||
QoS_LEVEL_FLIP, pipe_regs->ttu_regs.qos_level_flip);
|
||||
|
||||
REG_SET(DST_Y_DELTA_DRQ_LIMIT, 0,
|
||||
DST_Y_DELTA_DRQ_LIMIT, pipe_regs->dlg_regs.dst_y_delta_drq_limit);
|
||||
}
|
||||
struct hubp_funcs dcn42_hubp_funcs = {
|
||||
.hubp_enable_tripleBuffer = hubp2_enable_triplebuffer,
|
||||
.hubp_is_triplebuffer_enabled = hubp2_is_triplebuffer_enabled,
|
||||
@@ -588,7 +637,7 @@ struct hubp_funcs dcn42_hubp_funcs = {
|
||||
.hubp_program_surface_config = hubp42_program_surface_config,
|
||||
.hubp_is_flip_pending = hubp2_is_flip_pending,
|
||||
.hubp_setup2 = hubp42_setup,
|
||||
.hubp_setup_interdependent2 = hubp401_setup_interdependent,
|
||||
.hubp_setup_interdependent2 = hubp42_setup_interdependent,
|
||||
.hubp_set_vm_system_aperture_settings = hubp3_set_vm_system_aperture_settings,
|
||||
.set_blank = hubp2_set_blank,
|
||||
.set_blank_regs = hubp2_set_blank_regs,
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
HUBP_SF(HUBP0_3DLUT_FL_CONFIG, HUBP0_3DLUT_FL_FORMAT, mask_sh),\
|
||||
HUBP_SF(HUBP0_3DLUT_FL_BIAS_SCALE, HUBP0_3DLUT_FL_BIAS, mask_sh),\
|
||||
HUBP_SF(HUBP0_3DLUT_FL_BIAS_SCALE, HUBP0_3DLUT_FL_SCALE, mask_sh),\
|
||||
HUBP_SF(HUBPREQ0_DST_Y_DELTA_DRQ_LIMIT, DST_Y_DELTA_DRQ_LIMIT, mask_sh),\
|
||||
HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_ENABLE, mask_sh),\
|
||||
HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_DONE, mask_sh),\
|
||||
HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_ADDRESSING_MODE, mask_sh),\
|
||||
@@ -54,6 +55,7 @@
|
||||
HUBP_SF(HUBP0_3DLUT_FL_CONFIG, HUBP0_3DLUT_FL_FORMAT, mask_sh),\
|
||||
HUBP_SF(HUBP0_3DLUT_FL_BIAS_SCALE, HUBP0_3DLUT_FL_BIAS, mask_sh),\
|
||||
HUBP_SF(HUBP0_3DLUT_FL_BIAS_SCALE, HUBP0_3DLUT_FL_SCALE, mask_sh),\
|
||||
HUBP_SF(HUBPREQ0_DST_Y_DELTA_DRQ_LIMIT, DST_Y_DELTA_DRQ_LIMIT, mask_sh),\
|
||||
HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_ENABLE, mask_sh),\
|
||||
HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_DONE, mask_sh),\
|
||||
HUBP_SF(CURSOR0_0_HUBP_3DLUT_CONTROL, HUBP_3DLUT_ADDRESSING_MODE, mask_sh),\
|
||||
@@ -95,4 +97,7 @@ void hubp42_setup(
|
||||
union dml2_global_sync_programming *pipe_global_sync,
|
||||
struct dc_crtc_timing *timing);
|
||||
|
||||
void hubp42_setup_interdependent(
|
||||
struct hubp *hubp,
|
||||
struct dml2_dchub_per_pipe_register_set *pipe_regs);
|
||||
#endif /* __DC_HUBP_DCN42_H__ */
|
||||
|
||||
@@ -1287,7 +1287,9 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx)
|
||||
return;
|
||||
|
||||
if (link->local_sink && link->local_sink->sink_signal == SIGNAL_TYPE_EDP) {
|
||||
if (!link->skip_implict_edp_power_control && hws)
|
||||
if (link->skip_implict_edp_power_control)
|
||||
return;
|
||||
if (hws)
|
||||
hws->funcs.edp_backlight_control(link, false);
|
||||
link->dc->hwss.set_abm_immediate_disable(pipe_ctx);
|
||||
}
|
||||
@@ -1793,7 +1795,9 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw(
|
||||
dc->link_srv->set_dsc_enable(pipe_ctx, true);
|
||||
}
|
||||
|
||||
if (!stream->dpms_off)
|
||||
if (!stream->dpms_off &&
|
||||
!(link->connector_signal == SIGNAL_TYPE_EDP &&
|
||||
link->skip_implict_edp_power_control))
|
||||
dc->link_srv->set_dpms_on(context, pipe_ctx);
|
||||
|
||||
/* DCN3.1 FPGA Workaround
|
||||
@@ -1812,7 +1816,9 @@ enum dc_status dce110_apply_single_controller_ctx_to_hw(
|
||||
* is constructed with the same sink). Make sure not to override
|
||||
* and link programming on the main.
|
||||
*/
|
||||
if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM) {
|
||||
if (dc_state_get_pipe_subvp_type(context, pipe_ctx) != SUBVP_PHANTOM &&
|
||||
!(link->connector_signal == SIGNAL_TYPE_EDP &&
|
||||
link->skip_implict_edp_power_control)) {
|
||||
pipe_ctx->stream->link->psr_settings.psr_feature_enabled = false;
|
||||
pipe_ctx->stream->link->replay_settings.replay_feature_enabled = false;
|
||||
}
|
||||
|
||||
@@ -136,6 +136,26 @@ void dcn21_PLAT_58856_wa(struct dc_state *context, struct pipe_ctx *pipe_ctx)
|
||||
pipe_ctx->stream->dpms_off = true;
|
||||
}
|
||||
|
||||
bool dcn21_dmub_cacp_set_pipe(struct abm *abm, uint32_t otg_inst,
|
||||
uint32_t option, uint32_t panel_inst, uint32_t pwrseq_inst)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
struct dc_context *dc = abm->ctx;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cacp_set_pipe.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_set_pipe.header.sub_type = DMUB_CMD__CACP_SET_PIPE;
|
||||
cmd.cacp_set_pipe.cacp_set_pipe_data.otg_inst = (uint8_t)otg_inst;
|
||||
cmd.cacp_set_pipe.cacp_set_pipe_data.pwrseq_inst = (uint8_t)pwrseq_inst;
|
||||
cmd.cacp_set_pipe.cacp_set_pipe_data.set_pipe_option = (uint8_t)option;
|
||||
cmd.cacp_set_pipe.cacp_set_pipe_data.panel_inst = (uint8_t)panel_inst;
|
||||
cmd.cacp_set_pipe.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_pipe_data);
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dcn21_dmub_abm_set_pipe(struct abm *abm, uint32_t otg_inst,
|
||||
uint32_t option, uint32_t panel_inst, uint32_t pwrseq_inst)
|
||||
{
|
||||
@@ -181,10 +201,12 @@ void dcn21_set_abm_immediate_disable(struct pipe_ctx *pipe_ctx)
|
||||
uint32_t otg_inst = pipe_ctx->stream_res.tg->inst;
|
||||
struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl;
|
||||
struct dmcu *dmcu = pipe_ctx->stream->ctx->dc->res_pool->dmcu;
|
||||
struct dc_link *link = pipe_ctx->stream->link;
|
||||
|
||||
// make a short term w/a for an issue that backlight ramping unexpectedly paused in the middle,
|
||||
// will decouple backlight from ABM and redefine DMUB interface, then this w/a could be removed
|
||||
if (pipe_ctx->stream->abm_level == 0 || pipe_ctx->stream->abm_level == ABM_LEVEL_IMMEDIATE_DISABLE) {
|
||||
if ((pipe_ctx->stream->abm_level == 0 || pipe_ctx->stream->abm_level == ABM_LEVEL_IMMEDIATE_DISABLE)
|
||||
&& (link && !link->panel_config.cacp.cacp_supported)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -197,6 +219,9 @@ void dcn21_set_abm_immediate_disable(struct pipe_ctx *pipe_ctx)
|
||||
if (abm->funcs && abm->funcs->set_pipe_ex) {
|
||||
abm->funcs->set_pipe_ex(abm, otg_inst, SET_ABM_PIPE_IMMEDIATELY_DISABLE,
|
||||
panel_cntl->inst, panel_cntl->pwrseq_inst);
|
||||
} else if (link && link->panel_config.cacp.cacp_supported) {
|
||||
dcn21_dmub_cacp_set_pipe(abm, otg_inst, SET_CACP_PIPE_IMMEDIATELY_DISABLE,
|
||||
panel_cntl->inst, panel_cntl->pwrseq_inst);
|
||||
} else {
|
||||
dcn21_dmub_abm_set_pipe(abm,
|
||||
otg_inst,
|
||||
@@ -214,6 +239,7 @@ void dcn21_set_pipe(struct pipe_ctx *pipe_ctx)
|
||||
struct timing_generator *tg = pipe_ctx->stream_res.tg;
|
||||
struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl;
|
||||
struct dmcu *dmcu = pipe_ctx->stream->ctx->dc->res_pool->dmcu;
|
||||
struct dc_link *link = pipe_ctx->stream->link;
|
||||
uint32_t otg_inst;
|
||||
|
||||
if (!abm || !tg || !panel_cntl)
|
||||
@@ -233,6 +259,11 @@ void dcn21_set_pipe(struct pipe_ctx *pipe_ctx)
|
||||
panel_cntl->inst,
|
||||
panel_cntl->pwrseq_inst);
|
||||
} else {
|
||||
|
||||
if (link && link->panel_config.cacp.cacp_supported)
|
||||
dcn21_dmub_cacp_set_pipe(abm, otg_inst, SET_CACP_PIPE_NORMAL,
|
||||
panel_cntl->inst, panel_cntl->pwrseq_inst);
|
||||
else
|
||||
dcn21_dmub_abm_set_pipe(abm, otg_inst,
|
||||
SET_ABM_PIPE_NORMAL,
|
||||
panel_cntl->inst,
|
||||
@@ -250,6 +281,7 @@ bool dcn21_set_backlight_level(struct pipe_ctx *pipe_ctx,
|
||||
uint32_t otg_inst;
|
||||
uint32_t backlight_pwm_u16_16 = backlight_level_params->backlight_pwm_u16_16;
|
||||
uint32_t frame_ramp = backlight_level_params->frame_ramp;
|
||||
struct dc_link *link = pipe_ctx->stream->link;
|
||||
|
||||
if (!abm || !tg || !panel_cntl)
|
||||
return false;
|
||||
@@ -268,6 +300,12 @@ bool dcn21_set_backlight_level(struct pipe_ctx *pipe_ctx,
|
||||
panel_cntl->inst,
|
||||
panel_cntl->pwrseq_inst);
|
||||
} else {
|
||||
if (link && link->panel_config.cacp.cacp_supported)
|
||||
dcn21_dmub_cacp_set_pipe(abm, otg_inst,
|
||||
SET_CACP_PIPE_NORMAL,
|
||||
panel_cntl->inst,
|
||||
panel_cntl->pwrseq_inst);
|
||||
else
|
||||
dcn21_dmub_abm_set_pipe(abm,
|
||||
otg_inst,
|
||||
SET_ABM_PIPE_NORMAL,
|
||||
|
||||
@@ -47,6 +47,8 @@ void dcn21_optimize_pwr_state(
|
||||
void dcn21_PLAT_58856_wa(struct dc_state *context,
|
||||
struct pipe_ctx *pipe_ctx);
|
||||
|
||||
bool dcn21_dmub_cacp_set_pipe(struct abm *abm, uint32_t otg_inst,
|
||||
uint32_t option, uint32_t panel_inst, uint32_t pwrseq_inst);
|
||||
bool dcn21_dmub_abm_set_pipe(struct abm *abm, uint32_t otg_inst,
|
||||
uint32_t option, uint32_t panel_inst, uint32_t pwrseq_inst);
|
||||
void dcn21_set_pipe(struct pipe_ctx *pipe_ctx);
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "dce/dce_i2c_hw.h"
|
||||
#include "dce/dmub_abm_lcd.h"
|
||||
#include "dio/dcn10/dcn10_dio.h"
|
||||
#include "dce/dmub_abm_cacp.h"
|
||||
|
||||
#define DC_LOGGER_INIT(logger)
|
||||
|
||||
@@ -536,10 +537,12 @@ static void dcn31_reset_back_end_for_pipe(
|
||||
}
|
||||
ASSERT(!pipe_ctx->top_pipe);
|
||||
|
||||
dc->hwss.set_abm_immediate_disable(pipe_ctx);
|
||||
|
||||
link = pipe_ctx->stream->link;
|
||||
|
||||
if (!(link->connector_signal == SIGNAL_TYPE_EDP &&
|
||||
link->skip_implict_edp_power_control))
|
||||
dc->hwss.set_abm_immediate_disable(pipe_ctx);
|
||||
|
||||
if (dc->hwseq)
|
||||
dc->hwseq->wa_state.skip_blank_stream = false;
|
||||
|
||||
@@ -554,9 +557,11 @@ static void dcn31_reset_back_end_for_pipe(
|
||||
pipe_ctx->stream_res.tg,
|
||||
OPTC_DSC_DISABLED, 0, 0);
|
||||
|
||||
pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg);
|
||||
|
||||
pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false);
|
||||
if (!(link->connector_signal == SIGNAL_TYPE_EDP &&
|
||||
link->skip_implict_edp_power_control)) {
|
||||
pipe_ctx->stream_res.tg->funcs->disable_crtc(pipe_ctx->stream_res.tg);
|
||||
pipe_ctx->stream_res.tg->funcs->enable_optc_clock(pipe_ctx->stream_res.tg, false);
|
||||
}
|
||||
if (pipe_ctx->stream_res.tg->funcs->set_odm_bypass)
|
||||
pipe_ctx->stream_res.tg->funcs->set_odm_bypass(
|
||||
pipe_ctx->stream_res.tg, &pipe_ctx->stream->timing);
|
||||
@@ -585,7 +590,12 @@ static void dcn31_reset_back_end_for_pipe(
|
||||
* screen only, the dpms_off would be true but
|
||||
* VBIOS lit up eDP, so check link status too.
|
||||
*/
|
||||
if (!pipe_ctx->stream->dpms_off || link->link_status.link_active)
|
||||
if (link->connector_signal == SIGNAL_TYPE_EDP &&
|
||||
link->skip_implict_edp_power_control) {
|
||||
/* DMSS is holding the panel across the commit; skip dpms-off. */
|
||||
if (pipe_ctx->stream_res.audio)
|
||||
dc->hwss.disable_audio_stream(pipe_ctx);
|
||||
} else if (!pipe_ctx->stream->dpms_off || link->link_status.link_active)
|
||||
dc->link_srv->set_dpms_off(pipe_ctx);
|
||||
else if (pipe_ctx->stream_res.audio)
|
||||
dc->hwss.disable_audio_stream(pipe_ctx);
|
||||
@@ -718,6 +728,34 @@ static void dmub_abm_set_backlight(struct dc_context *dc,
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
}
|
||||
|
||||
static bool dmub_cacp_set_backlight(struct dc_context *dc,
|
||||
struct set_backlight_level_params *backlight_level_params,
|
||||
unsigned int panel_inst)
|
||||
{
|
||||
union dmub_rb_cmd cmd;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.cacp_set_backlight.header.type = DMUB_CMD__CACP;
|
||||
cmd.cacp_set_backlight.header.sub_type = DMUB_CMD__CACP_SET_BACKLIGHT;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.aux_inst = backlight_level_params->aux_inst;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.frame_ramp = backlight_level_params->frame_ramp;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.backlight_user_level =
|
||||
backlight_level_params->backlight_pwm_u16_16;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.backlight_control_type =
|
||||
(enum dmub_backlight_control_type)backlight_level_params->control_type;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.min_luminance = backlight_level_params->min_luminance;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.max_luminance = backlight_level_params->max_luminance;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.min_backlight_pwm = backlight_level_params->min_backlight_pwm;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.max_backlight_pwm = backlight_level_params->max_backlight_pwm;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.version = DMUB_CMD_CACP_CONTROL_VERSION_1;
|
||||
cmd.cacp_set_backlight.cacp_set_backlight_data.panel_mask = (0x01 << panel_inst);
|
||||
cmd.cacp_set_backlight.header.payload_bytes = sizeof(struct dmub_cmd_cacp_set_backlight_data);
|
||||
|
||||
dc_wake_and_execute_dmub_cmd(dc, &cmd, DM_DMUB_WAIT_TYPE_WAIT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dcn31_set_backlight_level(struct pipe_ctx *pipe_ctx,
|
||||
struct set_backlight_level_params *backlight_level_params)
|
||||
{
|
||||
@@ -726,20 +764,38 @@ bool dcn31_set_backlight_level(struct pipe_ctx *pipe_ctx,
|
||||
struct timing_generator *tg = pipe_ctx->stream_res.tg;
|
||||
struct panel_cntl *panel_cntl = pipe_ctx->stream->link->panel_cntl;
|
||||
uint32_t otg_inst;
|
||||
struct dc_link *link = pipe_ctx->stream->link;
|
||||
|
||||
if (!abm || !tg || !panel_cntl)
|
||||
return false;
|
||||
|
||||
otg_inst = tg->inst;
|
||||
|
||||
if (link && link->panel_config.cacp.cacp_supported)
|
||||
dcn21_dmub_cacp_set_pipe(abm, otg_inst,
|
||||
SET_CACP_PIPE_NORMAL,
|
||||
panel_cntl->inst,
|
||||
panel_cntl->pwrseq_inst);
|
||||
else
|
||||
dcn21_dmub_abm_set_pipe(abm,
|
||||
otg_inst,
|
||||
SET_ABM_PIPE_NORMAL,
|
||||
panel_cntl->inst,
|
||||
panel_cntl->pwrseq_inst);
|
||||
|
||||
if (backlight_level_params->control_type != BACKLIGHT_CONTROL_AMD_AUX)
|
||||
if (link && link->panel_type == PANEL_TYPE_OLED) {
|
||||
/* For OLED panel with AMD AUX, skip set backlight call */
|
||||
if (backlight_level_params->control_type == BACKLIGHT_CONTROL_VESA_AUX)
|
||||
dmub_cacp_set_backlight(dc, backlight_level_params, panel_cntl->inst);
|
||||
} else if (link && link->panel_type == PANEL_TYPE_MINILED) {
|
||||
/* For MiniLED panel we need to check if CACP or ABM is being used */
|
||||
if (link->panel_config.cacp.cacp_supported)
|
||||
dmub_cacp_set_backlight(dc, backlight_level_params, panel_cntl->inst);
|
||||
else
|
||||
dmub_abm_set_backlight(dc, backlight_level_params, panel_cntl->inst);
|
||||
} else {
|
||||
dmub_abm_set_backlight(dc, backlight_level_params, panel_cntl->inst);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user