diff options
| author | Mark Brown <broonie@kernel.org> | 2026-05-29 22:42:09 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-05-29 22:42:09 +0100 |
| commit | c7c18ef008859bbe0017e164f8522aef5bac3e7b (patch) | |
| tree | 034d71b9449d89e5094a04b8d6300f80dcf59ee2 /drivers | |
| parent | 343e69a2a8b629f8da9062811faa1260bddb17ed (diff) | |
| parent | 470d1ae31d29f90b8998c5c08ee0b267a05fe378 (diff) | |
| download | linux-next-history-c7c18ef008859bbe0017e164f8522aef5bac3e7b.tar.gz | |
Merge branch 'drm-next' of https://gitlab.freedesktop.org/agd5f/linux.git
Diffstat (limited to 'drivers')
70 files changed, 3159 insertions, 743 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index d386bc775d03c..0811593fca7f9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -586,7 +586,7 @@ void amdgpu_coredump(struct amdgpu_device *adev, bool skip_vram_check, */ adev->coredump = coredump; /* Kick off coredump formatting to a worker thread. */ - queue_work(system_unbound_wq, &adev->coredump_work); + queue_work(system_dfl_wq, &adev->coredump_work); drm_info(dev, "AMDGPU device coredump file has been created\n"); drm_info(dev, "Check your /sys/class/drm/card%d/device/devcoredump/data\n", diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 21a3fb574d53e..480eeb8510f87 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1913,6 +1913,19 @@ static void amdgpu_uid_fini(struct amdgpu_device *adev) adev->uid_info = NULL; } +static struct pci_dev *amdgpu_device_find_parent(struct amdgpu_device *adev) +{ + struct pci_dev *parent = adev->pdev; + + /* skip upstream/downstream switches internal to dGPU */ + while ((parent = pci_upstream_bridge(parent))) { + if (parent->vendor == PCI_VENDOR_ID_ATI) + continue; + } + + return parent; +} + /** * amdgpu_device_ip_early_init - run early init for hardware IPs * @@ -5921,8 +5934,6 @@ static void amdgpu_device_partner_bandwidth(struct amdgpu_device *adev, enum pci_bus_speed *speed, enum pcie_link_width *width) { - struct pci_dev *parent = adev->pdev; - if (!speed || !width) return; @@ -5930,13 +5941,11 @@ static void amdgpu_device_partner_bandwidth(struct amdgpu_device *adev, *width = PCIE_LNK_WIDTH_UNKNOWN; if (amdgpu_device_pcie_dynamic_switching_supported(adev)) { - while ((parent = pci_upstream_bridge(parent))) { - /* skip upstream/downstream switches internal to dGPU*/ - if (parent->vendor == PCI_VENDOR_ID_ATI) - continue; + struct pci_dev *parent = amdgpu_device_find_parent(adev); + + if (parent) { *speed = pcie_get_speed_cap(parent); *width = pcie_get_width_cap(parent); - break; } } else { /* use the current speeds rather than max if switching is not supported */ @@ -5963,22 +5972,15 @@ static void amdgpu_device_gpu_bandwidth(struct amdgpu_device *adev, if (!speed || !width) return; - parent = pci_upstream_bridge(parent); - if (parent && parent->vendor == PCI_VENDOR_ID_ATI) { - /* use the upstream/downstream switches internal to dGPU */ + /* use the device itself */ + *speed = pcie_get_speed_cap(adev->pdev); + *width = pcie_get_width_cap(adev->pdev); + + /* use the link outside the device */ + parent = amdgpu_device_find_parent(adev); + if (parent) { *speed = pcie_get_speed_cap(parent); *width = pcie_get_width_cap(parent); - while ((parent = pci_upstream_bridge(parent))) { - if (parent->vendor == PCI_VENDOR_ID_ATI) { - /* use the upstream/downstream switches internal to dGPU */ - *speed = pcie_get_speed_cap(parent); - *width = pcie_get_width_cap(parent); - } - } - } else { - /* use the device itself */ - *speed = pcie_get_speed_cap(adev->pdev); - *width = pcie_get_width_cap(adev->pdev); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c index 3fcbd722ee3b4..229eb6219de01 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c @@ -1804,13 +1804,15 @@ amdgpu_virt_write_cpers_to_ring(struct amdgpu_device *adev, struct amd_sriov_ras_cper_dump *cper_dump = NULL; struct cper_hdr *entry = NULL; struct amdgpu_ring *ring = &adev->cper.ring_buf; - uint32_t checksum, used_size, i; + uint32_t checksum, used_size; + u64 remaining, cnt, i; int ret = 0; checksum = host_telemetry->header.checksum; used_size = host_telemetry->header.used_size; - if (used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10)) + if (used_size < offsetof(struct amd_sriov_ras_cper_dump, buf) || + used_size > (AMD_SRIOV_MSG_RAS_TELEMETRY_SIZE_KB_V1 << 10)) return -EINVAL; cper_dump = kmemdup(&host_telemetry->body.cper_dump, used_size, GFP_KERNEL); @@ -1835,11 +1837,19 @@ amdgpu_virt_write_cpers_to_ring(struct amdgpu_device *adev, } entry = (struct cper_hdr *)&cper_dump->buf[0]; + remaining = (u64)used_size - offsetof(struct amd_sriov_ras_cper_dump, buf); + cnt = min_t(u64, cper_dump->count, CPER_MAX_ALLOWED_COUNT); + + for (i = 0; i < cnt; i++) { + if (entry->record_length < sizeof(struct cper_hdr) || + entry->record_length > remaining) { + ret = -EINVAL; + goto out; + } - for (i = 0; i < cper_dump->count; i++) { amdgpu_cper_ring_write(ring, entry, entry->record_length); - entry = (struct cper_hdr *)((char *)entry + - entry->record_length); + remaining -= entry->record_length; + entry = (struct cper_hdr *)((char *)entry + entry->record_length); } if (cper_dump->overflow_count) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 76f7a78c5e9e9..4558a5663c66e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -266,12 +266,23 @@ static void amdgpu_vm_bo_idle(struct amdgpu_vm_bo_base *vm_bo) */ static void amdgpu_vm_bo_reset_state_machine(struct amdgpu_vm *vm) { + struct amdgpu_vm_bo_base *vm_bo, *tmp; + + /* + * Don't use list splice here, we need the special handling for the root + * PD and set the moved flag appropriately. + */ amdgpu_vm_assert_locked(vm); - list_splice_init(&vm->kernel.idle, &vm->kernel.moved); - list_splice_init(&vm->always_valid.idle, &vm->always_valid.moved); + list_for_each_entry_safe(vm_bo, tmp, &vm->kernel.idle, vm_status) + amdgpu_vm_bo_moved(vm_bo); + list_for_each_entry_safe(vm_bo, tmp, &vm->always_valid.idle, vm_status) + amdgpu_vm_bo_moved(vm_bo); spin_lock(&vm->individual_lock); - list_splice_init(&vm->individual.idle, &vm->individual.moved); + list_for_each_entry_safe(vm_bo, tmp, &vm->individual.idle, vm_status) { + vm_bo->moved = true; + list_move(&vm_bo->vm_status, &vm->individual.moved); + } spin_unlock(&vm->individual_lock); } @@ -1616,7 +1627,8 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, while (!list_empty(&vm->individual.moved)) { bo_va = list_first_entry(&vm->individual.moved, typeof(*bo_va), base.vm_status); - resv = bo_va->base.bo->tbo.base.resv; + bo = bo_va->base.bo; + resv = bo->tbo.base.resv; spin_unlock(&vm->individual_lock); /* Try to reserve the BO to avoid clearing its ptes */ diff --git a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c index db505ab32fa0f..14092150336a5 100644 --- a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c @@ -285,7 +285,8 @@ static int umc_v12_0_convert_error_address(struct amdgpu_device *adev, struct ta_ras_query_address_output *addr_out, bool dump_addr) { - uint32_t col, col_lower, row, row_lower, row_high, bank; + uint32_t row = 0, row_lower = 0, row_high = 0; + uint32_t col = 0, col_lower = 0, bank = 0; uint32_t channel_index = 0, umc_inst = 0; uint32_t i, bit_num, retire_unit, *flip_bits; uint64_t soc_pa, column, err_addr; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index a1087c13f2419..cf7b1b038d5f8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -1821,7 +1821,7 @@ static int kfd_fill_mem_info_for_cpu(int numa_node_id, int *avail_size, return 0; } -#ifdef CONFIG_X86_64 +#if defined(CONFIG_X86_64) && !defined(CONFIG_UML) static int kfd_fill_iolink_info_for_cpu(int numa_node_id, int *avail_size, uint32_t *num_entries, struct crat_subtype_iolink *sub_type_hdr) @@ -1880,7 +1880,7 @@ static int kfd_create_vcrat_image_cpu(void *pcrat_image, size_t *size) struct crat_subtype_generic *sub_type_hdr; int avail_size = *size; int numa_node_id; -#ifdef CONFIG_X86_64 +#if defined(CONFIG_X86_64) && !defined(CONFIG_UML) uint32_t entries = 0; #endif int ret = 0; @@ -1945,7 +1945,7 @@ static int kfd_create_vcrat_image_cpu(void *pcrat_image, size_t *size) sub_type_hdr->length); /* Fill in Subtype: IO Link */ -#ifdef CONFIG_X86_64 +#if defined(CONFIG_X86_64) && !defined(CONFIG_UML) ret = kfd_fill_iolink_info_for_cpu(numa_node_id, &avail_size, &entries, (struct crat_subtype_iolink *)sub_type_hdr); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index 630f46091dc75..dc1c6bd1252f1 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -2350,7 +2350,7 @@ static int kfd_cpumask_to_apic_id(const struct cpumask *cpumask) first_cpu_of_numa_node = cpumask_first(cpumask); if (first_cpu_of_numa_node >= nr_cpu_ids) return -1; -#ifdef CONFIG_X86_64 +#if defined(CONFIG_X86_64) && !defined(CONFIG_UML) return cpu_data(first_cpu_of_numa_node).topo.apicid; #else return first_cpu_of_numa_node; diff --git a/drivers/gpu/drm/amd/display/Kconfig b/drivers/gpu/drm/amd/display/Kconfig index abd3b6564373a..38323e574c6bb 100644 --- a/drivers/gpu/drm/amd/display/Kconfig +++ b/drivers/gpu/drm/amd/display/Kconfig @@ -56,4 +56,16 @@ config DRM_AMD_SECURE_DISPLAY This option enables the calculation of crc of specific region via debugfs. Cooperate with specific DMCU FW. +config DRM_AMD_DC_KUNIT_TEST + tristate "KUnit tests for the AMD DC display driver" if !KUNIT_ALL_TESTS + depends on DRM_AMD_DC && KUNIT && DEBUG_FS + default KUNIT_ALL_TESTS + help + This option enables KUnit tests for the AMD Display Core driver. + These tests validate core functionality like CRC source parsing, + color management, and other utility functions. + + For more information on KUnit and unit tests in general, please + refer to the KUnit documentation in Documentation/dev-tools/kunit/. + endmenu diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile index 89350aa9ca7ec..914f89af047c1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile @@ -59,3 +59,8 @@ AMDGPU_DM = $(addprefix $(AMDDALPATH)/amdgpu_dm/,$(AMDGPUDM)) AMD_DISPLAY_FILES += $(AMDGPU_DM) endif + +# KUnit tests as separate module +ifneq ($(CONFIG_DRM_AMD_DC_KUNIT_TEST),) +obj-y += $(AMDDALPATH)/amdgpu_dm/tests/ +endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index ba7f98a87808c..c5a5e6ca4958a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -7473,7 +7473,7 @@ create_stream_for_sink(struct drm_connector *connector, int preferred_refresh = 0; enum color_transfer_func tf = TRANSFER_FUNC_UNKNOWN; #if defined(CONFIG_DRM_AMD_DC_FP) - struct dsc_dec_dpcd_caps dsc_caps; + struct dsc_dec_dpcd_caps dsc_caps = {0}; #endif struct dc_link *link = NULL; struct dc_sink *sink = NULL; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index 74f700fbeb6f1..c4672ade22d57 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -45,8 +45,6 @@ * in amdgpu_dm_kms.h file */ -#define AMDGPU_DM_MAX_DISPLAY_INDEX 31 - #define AMDGPU_DM_MAX_CRTC 6 #define AMDGPU_DM_MAX_NUM_EDP 2 diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c index fa6883ae4dfb8..4339e84360a58 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.c @@ -29,9 +29,12 @@ #include "amdgpu.h" #include "amdgpu_mode.h" #include "amdgpu_dm.h" +#include "amdgpu_dm_color.h" #include "amdgpu_dm_colorop.h" #include "dc.h" #include "modules/color/color_gamma.h" +#include "amdgpu_dm_kunit_helpers.h" + /** * DOC: overview @@ -157,8 +160,6 @@ * */ -#define MAX_DRM_LUT_VALUE 0xFFFF -#define MAX_DRM_LUT32_VALUE 0xFFFFFFFF #define SDR_WHITE_LEVEL_INIT_VALUE 80 /** @@ -172,7 +173,8 @@ void amdgpu_dm_init_color_mod(void) setup_x_points_distribution(); } -static inline struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x) +STATIC_IFN_KUNIT INLINE_IFN_KUNIT +struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x) { struct fixed31_32 val; @@ -183,6 +185,7 @@ static inline struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x) val.value = x; return val; } +EXPORT_IF_KUNIT(amdgpu_dm_fixpt_from_s3132); #ifdef AMD_PRIVATE_COLOR /* Pre-defined Transfer Functions (TF) @@ -421,12 +424,14 @@ amdgpu_dm_create_color_properties(struct amdgpu_device *adev) * Returns: * DRM LUT or NULL */ -static const struct drm_color_lut * +STATIC_IFN_KUNIT +const struct drm_color_lut * __extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size) { *size = blob ? drm_color_lut_size(blob) : 0; return blob ? (struct drm_color_lut *)blob->data : NULL; } +EXPORT_IF_KUNIT(__extract_blob_lut); /** * __extract_blob_lut32 - Extracts the DRM lut and lut size from a blob. @@ -436,12 +441,14 @@ __extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size) * Returns: * DRM LUT or NULL */ -static const struct drm_color_lut32 * +STATIC_IFN_KUNIT +const struct drm_color_lut32 * __extract_blob_lut32(const struct drm_property_blob *blob, uint32_t *size) { *size = blob ? drm_color_lut32_size(blob) : 0; return blob ? (struct drm_color_lut32 *)blob->data : NULL; } +EXPORT_IF_KUNIT(__extract_blob_lut32); /** * __is_lut_linear - check if the given lut is a linear mapping of values @@ -456,7 +463,8 @@ __extract_blob_lut32(const struct drm_property_blob *blob, uint32_t *size) * True if the given lut is a linear mapping of values, i.e. it acts like a * bypass LUT. Otherwise, false. */ -static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size) +STATIC_IFN_KUNIT +bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size) { int i; uint32_t expected; @@ -476,6 +484,7 @@ static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size) } return true; } +EXPORT_IF_KUNIT(__is_lut_linear); /** * __drm_lut_to_dc_gamma - convert the drm_color_lut to dc_gamma. @@ -485,7 +494,8 @@ static bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size) * * The conversion depends on the size of the lut - whether or not it's legacy. */ -static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut, +STATIC_IFN_KUNIT +void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut, struct dc_gamma *gamma, bool is_legacy) { uint32_t r, g, b; @@ -515,6 +525,7 @@ static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut, gamma->entries.blue[i] = dc_fixpt_from_fraction(b, MAX_DRM_LUT_VALUE); } } +EXPORT_IF_KUNIT(__drm_lut_to_dc_gamma); /** * __drm_lut32_to_dc_gamma - convert the drm_color_lut to dc_gamma. @@ -523,7 +534,8 @@ static void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut, * * The conversion depends on the size of the lut - whether or not it's legacy. */ -static void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc_gamma *gamma) +STATIC_IFN_KUNIT +void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc_gamma *gamma) { int i; @@ -533,6 +545,7 @@ static void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc gamma->entries.blue[i] = dc_fixpt_from_fraction(lut[i].blue, MAX_DRM_LUT32_VALUE); } } +EXPORT_IF_KUNIT(__drm_lut32_to_dc_gamma); /** * __drm_ctm_to_dc_matrix - converts a DRM CTM to a DC CSC float matrix @@ -541,8 +554,9 @@ static void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, struct dc * * The matrix needs to be a 3x4 (12 entry) matrix. */ -static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm, - struct fixed31_32 *matrix) +STATIC_IFN_KUNIT +void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm, + struct fixed31_32 *matrix) { int i; @@ -565,6 +579,7 @@ static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm, matrix[i] = amdgpu_dm_fixpt_from_s3132(ctm->matrix[i - (i / 4)]); } } +EXPORT_IF_KUNIT(__drm_ctm_to_dc_matrix); /** * __drm_ctm_3x4_to_dc_matrix - converts a DRM CTM 3x4 to a DC CSC float matrix @@ -573,8 +588,9 @@ static void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm, * * The matrix needs to be a 3x4 (12 entry) matrix. */ -static void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm, - struct fixed31_32 *matrix) +STATIC_IFN_KUNIT +void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm, + struct fixed31_32 *matrix) { int i; @@ -587,6 +603,7 @@ static void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm, matrix[i] = amdgpu_dm_fixpt_from_s3132(ctm->matrix[i]); } } +EXPORT_IF_KUNIT(__drm_ctm_3x4_to_dc_matrix); /** * __set_legacy_tf - Calculates the legacy transfer function @@ -851,7 +868,8 @@ static int __set_input_tf_32(struct dc_color_caps *caps, struct dc_transfer_func return res ? 0 : -ENOMEM; } -static enum dc_transfer_func_predefined +STATIC_IFN_KUNIT +enum dc_transfer_func_predefined amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf) { switch (tf) { @@ -879,8 +897,10 @@ amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf) return TRANSFER_FUNCTION_GAMMA26; } } +EXPORT_IF_KUNIT(amdgpu_tf_to_dc_tf); -static enum dc_transfer_func_predefined +STATIC_IFN_KUNIT +enum dc_transfer_func_predefined amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf) { switch (tf) { @@ -900,8 +920,10 @@ amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf) return TRANSFER_FUNCTION_LINEAR; } } +EXPORT_IF_KUNIT(amdgpu_colorop_tf_to_dc_tf); -static void __to_dc_lut3d_color(struct dc_rgb *rgb, +STATIC_IFN_KUNIT +void __to_dc_lut3d_color(struct dc_rgb *rgb, const struct drm_color_lut lut, int bit_precision) { @@ -909,8 +931,10 @@ static void __to_dc_lut3d_color(struct dc_rgb *rgb, rgb->green = drm_color_lut_extract(lut.green, bit_precision); rgb->blue = drm_color_lut_extract(lut.blue, bit_precision); } +EXPORT_IF_KUNIT(__to_dc_lut3d_color); -static void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut, +STATIC_IFN_KUNIT +void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut, uint32_t lut3d_size, struct tetrahedral_params *params, bool use_tetrahedral_9, @@ -953,8 +977,10 @@ static void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut, /* lut0 has 1229 points (lut_size/4 + 1) */ __to_dc_lut3d_color(&lut0[lut_i], lut[i], bit_depth); } +EXPORT_IF_KUNIT(__drm_3dlut_to_dc_3dlut); -static void __to_dc_lut3d_32_color(struct dc_rgb *rgb, +STATIC_IFN_KUNIT +void __to_dc_lut3d_32_color(struct dc_rgb *rgb, const struct drm_color_lut32 lut, int bit_precision) { @@ -962,8 +988,10 @@ static void __to_dc_lut3d_32_color(struct dc_rgb *rgb, rgb->green = drm_color_lut32_extract(lut.green, bit_precision); rgb->blue = drm_color_lut32_extract(lut.blue, bit_precision); } +EXPORT_IF_KUNIT(__to_dc_lut3d_32_color); -static void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut, +STATIC_IFN_KUNIT +void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut, uint32_t lut3d_size, struct tetrahedral_params *params, bool use_tetrahedral_9, @@ -1006,6 +1034,7 @@ static void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut, /* lut0 has 1229 points (lut_size/4 + 1) */ __to_dc_lut3d_32_color(&lut0[lut_i], lut[i], bit_depth); } +EXPORT_IF_KUNIT(__drm_3dlut32_to_dc_3dlut); /* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream * @drm_lut3d: user 3D LUT diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h new file mode 100644 index 0000000000000..19d3a13572f55 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: AMD + * + */ + +#ifndef __AMDGPU_DM_COLOR_H__ +#define __AMDGPU_DM_COLOR_H__ + +#define MAX_DRM_LUT_VALUE 0xFFFF +#define MAX_DRM_LUT32_VALUE 0xFFFFFFFF + +#include <linux/types.h> + +struct drm_color_lut; +struct drm_color_lut32; +struct drm_color_ctm; +struct drm_color_ctm_3x4; +struct drm_property_blob; +struct dc_gamma; +struct dc_rgb; +struct fixed31_32; +struct tetrahedral_params; + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +/* + * Prototypes for functions exposed to KUnit tests. The enum types + * used below (dc_transfer_func_predefined, amdgpu_transfer_function, + * drm_colorop_curve_1d_type) must be defined before this header is + * included — the source file (amdgpu_dm_color.c) ensures this via + * its own includes of dc.h, amdgpu_dm.h, and drm/drm_colorop.h. + */ +struct fixed31_32 amdgpu_dm_fixpt_from_s3132(__u64 x); +bool __is_lut_linear(const struct drm_color_lut *lut, uint32_t size); +void __drm_lut_to_dc_gamma(const struct drm_color_lut *lut, + struct dc_gamma *gamma, bool is_legacy); +void __drm_lut32_to_dc_gamma(const struct drm_color_lut32 *lut, + struct dc_gamma *gamma); +void __drm_ctm_to_dc_matrix(const struct drm_color_ctm *ctm, + struct fixed31_32 *matrix); +void __drm_ctm_3x4_to_dc_matrix(const struct drm_color_ctm_3x4 *ctm, + struct fixed31_32 *matrix); +enum dc_transfer_func_predefined +amdgpu_tf_to_dc_tf(enum amdgpu_transfer_function tf); +enum dc_transfer_func_predefined +amdgpu_colorop_tf_to_dc_tf(enum drm_colorop_curve_1d_type tf); +const struct drm_color_lut * +__extract_blob_lut(const struct drm_property_blob *blob, uint32_t *size); +const struct drm_color_lut32 * +__extract_blob_lut32(const struct drm_property_blob *blob, uint32_t *size); +void __to_dc_lut3d_color(struct dc_rgb *rgb, + const struct drm_color_lut lut, + int bit_precision); +void __drm_3dlut_to_dc_3dlut(const struct drm_color_lut *lut, + uint32_t lut3d_size, + struct tetrahedral_params *params, + bool use_tetrahedral_9, + int bit_depth); +void __to_dc_lut3d_32_color(struct dc_rgb *rgb, + const struct drm_color_lut32 lut, + int bit_precision); +void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut, + uint32_t lut3d_size, + struct tetrahedral_params *params, + bool use_tetrahedral_9, + int bit_depth); +#endif + +#endif /* __AMDGPU_DM_COLOR_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c index 7ee051cb3c056..ab20e3f41d604 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_colorop.c @@ -31,6 +31,7 @@ #include "amdgpu.h" #include "amdgpu_dm_colorop.h" +#include "amdgpu_dm_kunit_helpers.h" #include "dc.h" const u64 amdgpu_dm_supported_degam_tfs = @@ -38,18 +39,21 @@ const u64 amdgpu_dm_supported_degam_tfs = BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) | BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) | BIT(DRM_COLOROP_1D_CURVE_GAMMA22); +EXPORT_IF_KUNIT(amdgpu_dm_supported_degam_tfs); const u64 amdgpu_dm_supported_shaper_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) | BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) | BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF) | BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV); +EXPORT_IF_KUNIT(amdgpu_dm_supported_shaper_tfs); const u64 amdgpu_dm_supported_blnd_tfs = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) | BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) | BIT(DRM_COLOROP_1D_CURVE_GAMMA22); +EXPORT_IF_KUNIT(amdgpu_dm_supported_blnd_tfs); #define MAX_COLOR_PIPELINE_OPS 10 diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c index 5adbb0f6a0c88..88f7cfea56240 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.c @@ -33,6 +33,7 @@ #include "amdgpu_securedisplay.h" #include "amdgpu_dm_psr.h" #include "amdgpu_dm_replay.h" +#include "amdgpu_dm_kunit_helpers.h" static const char *const pipe_crc_sources[] = { "none", @@ -43,7 +44,8 @@ static const char *const pipe_crc_sources[] = { "auto", }; -static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source) +STATIC_IFN_KUNIT +enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source) { if (!source || !strcmp(source, "none")) return AMDGPU_DM_PIPE_CRC_SOURCE_NONE; @@ -58,25 +60,32 @@ static enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source) return AMDGPU_DM_PIPE_CRC_SOURCE_INVALID; } +EXPORT_IF_KUNIT(dm_parse_crc_source); -static bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src) +STATIC_IFN_KUNIT +bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src) { return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC) || (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER); } +EXPORT_IF_KUNIT(dm_is_crc_source_crtc); -static bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src) +STATIC_IFN_KUNIT +bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src) { return (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX) || (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER); } +EXPORT_IF_KUNIT(dm_is_crc_source_dprx); -static bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src) +STATIC_IFN_KUNIT +bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src) { return (src == AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER) || (src == AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER) || (src == AMDGPU_DM_PIPE_CRC_SOURCE_NONE); } +EXPORT_IF_KUNIT(dm_need_crc_dither); const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc, size_t *count) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h index 8538513ea8799..c9aa0c82038fe 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crc.h @@ -27,8 +27,11 @@ #ifndef AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_ #define AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_ +#include "dc_types.h" + struct drm_crtc; struct dm_crtc_state; +struct amdgpu_device; enum amdgpu_dm_pipe_crc_source { AMDGPU_DM_PIPE_CRC_SOURCE_NONE = 0, @@ -148,4 +151,11 @@ void amdgpu_dm_crtc_secure_display_create_contexts(struct amdgpu_device *adev); #define amdgpu_dm_crtc_secure_display_create_contexts(x) #endif +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +enum amdgpu_dm_pipe_crc_source dm_parse_crc_source(const char *source); +bool dm_is_crc_source_crtc(enum amdgpu_dm_pipe_crc_source src); +bool dm_is_crc_source_dprx(enum amdgpu_dm_pipe_crc_source src); +bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src); +#endif + #endif /* AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c index eb73bbf8f411f..adc5cd1007f29 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c @@ -31,6 +31,7 @@ #include "dm_helpers.h" #include <drm/display/drm_hdcp_helper.h> #include "hdcp_psp.h" +#include "amdgpu_dm_kunit_helpers.h" /* * If the SRM version being loaded is less than or equal to the @@ -158,7 +159,8 @@ static int psp_set_srm(struct psp_context *psp, return 0; } -static void process_output(struct hdcp_workqueue *hdcp_work) +STATIC_IFN_KUNIT +void process_output(struct hdcp_workqueue *hdcp_work) { struct mod_hdcp_output output = hdcp_work->output; @@ -178,6 +180,7 @@ static void process_output(struct hdcp_workqueue *hdcp_work) schedule_delayed_work(&hdcp_work->property_validate_dwork, msecs_to_jiffies(0)); } +EXPORT_IF_KUNIT(process_output); static void link_lock(struct hdcp_workqueue *work, bool lock) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h index 4faa344f196e7..90b18c450ca61 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.h @@ -31,12 +31,19 @@ #include "hdcp.h" #include "dc.h" #include "dm_cp_psp.h" -#include "amdgpu.h" + +/* + * Minimal declarations needed by this header. + * Full amdgpu/DM definitions come from amdgpu_dm.h included by each .c file. + */ +#define AMDGPU_DM_MAX_DISPLAY_INDEX 31 +struct amdgpu_dm_connector; struct mod_hdcp; struct mod_hdcp_link; struct mod_hdcp_display; struct cp_psp; +struct amdgpu_device; struct hdcp_workqueue { struct work_struct cpirq_work; @@ -87,4 +94,8 @@ void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *work); struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct cp_psp *cp_psp, struct dc *dc); +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +void process_output(struct hdcp_workqueue *hdcp_work); +#endif + #endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c index 7dcc587c45e91..06594fbfceee0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.c @@ -32,6 +32,8 @@ #include "amdgpu_dm_ism.h" #include "amdgpu_dm_crtc.h" #include "amdgpu_dm_trace.h" +#include "amdgpu_dm_kunit_helpers.h" + /** * dm_ism_next_state - Get next state based on current state and event @@ -42,9 +44,10 @@ * This function defines the idle state management FSM. Invalid transitions * are ignored and will not progress the FSM. */ -static bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state, - enum amdgpu_dm_ism_event event, - enum amdgpu_dm_ism_state *next_state) +STATIC_IFN_KUNIT +bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state, + enum amdgpu_dm_ism_event event, + enum amdgpu_dm_ism_state *next_state) { switch (STATE_EVENT(current_state, event)) { case STATE_EVENT(DM_ISM_STATE_FULL_POWER_RUNNING, @@ -125,8 +128,10 @@ static bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state, } return true; } +EXPORT_IF_KUNIT(dm_ism_next_state); -static uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism, +STATIC_IFN_KUNIT +uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism, const struct dc_stream_state *stream) { const struct amdgpu_dm_ism_config *config = &ism->config; @@ -148,6 +153,7 @@ static uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism, return sso_delay_ns; } +EXPORT_IF_KUNIT(dm_ism_get_sso_delay); /** * dm_ism_get_idle_allow_delay - Calculate hysteresis-based idle allow delay @@ -157,8 +163,9 @@ static uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism, * Calculates the delay before allowing idle optimizations based on recent * idle history and the current stream timing. */ -static uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism, - const struct dc_stream_state *stream) +STATIC_IFN_KUNIT +uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism, + const struct dc_stream_state *stream) { const struct amdgpu_dm_ism_config *config = &ism->config; uint32_t v_total, h_total; @@ -217,6 +224,7 @@ static uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism, return ret_ns; } +EXPORT_IF_KUNIT(dm_ism_get_idle_allow_delay); /** * dm_ism_insert_record - Insert a record into the circular history buffer @@ -375,7 +383,7 @@ static enum amdgpu_dm_ism_event dm_ism_dispatch_power_state( } /* Schedule worker */ - mod_delayed_work(system_unbound_wq, &ism->delayed_work, + mod_delayed_work(system_dfl_wq, &ism->delayed_work, nsecs_to_jiffies(delay_ns)); break; @@ -391,14 +399,14 @@ static enum amdgpu_dm_ism_event dm_ism_dispatch_power_state( * have a negative power impact. Skip idle allow here, * and let the sso_delayed_work handle it. */ - mod_delayed_work(system_unbound_wq, + mod_delayed_work(system_dfl_wq, &ism->sso_delayed_work, nsecs_to_jiffies(sso_delay_ns)); } else { /* Enable idle optimization without SSO */ dm_ism_commit_idle_optimization_state( ism, acrtc_state->stream, false, false); - mod_delayed_work(system_unbound_wq, + mod_delayed_work(system_dfl_wq, &ism->sso_delayed_work, nsecs_to_jiffies(sso_delay_ns)); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h index 964408cd9a839..72e2dac49e55a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_ism.h @@ -149,4 +149,14 @@ void amdgpu_dm_ism_disable(struct amdgpu_display_manager *dm); void amdgpu_dm_ism_force_full_power(struct amdgpu_display_manager *dm); void amdgpu_dm_ism_enable(struct amdgpu_display_manager *dm); +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +bool dm_ism_next_state(enum amdgpu_dm_ism_state current_state, + enum amdgpu_dm_ism_event event, + enum amdgpu_dm_ism_state *next_state); +uint64_t dm_ism_get_sso_delay(const struct amdgpu_dm_ism *ism, + const struct dc_stream_state *stream); +uint64_t dm_ism_get_idle_allow_delay(const struct amdgpu_dm_ism *ism, + const struct dc_stream_state *stream); +#endif + #endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h new file mode 100644 index 0000000000000..4b2864375105b --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_kunit_helpers.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#ifndef AMDGPU_DM_KUNIT_HELPERS_H +#define AMDGPU_DM_KUNIT_HELPERS_H + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +#define STATIC_IFN_KUNIT +#define INLINE_IFN_KUNIT inline +#define EXPORT_IF_KUNIT(symbol) EXPORT_SYMBOL(symbol) +#else +#define STATIC_IFN_KUNIT static +#define INLINE_IFN_KUNIT +#define EXPORT_IF_KUNIT(symbol) +#endif + +#endif /* AMDGPU_DM_KUNIT_HELPERS_H */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c index 11b2ea6edf953..2cdb8fea504a0 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.c @@ -183,33 +183,6 @@ static enum amd_pp_clock_type dc_to_pp_clock_type( return amd_pp_clk_type; } -static enum dm_pp_clocks_state pp_to_dc_powerlevel_state( - enum PP_DAL_POWERLEVEL max_clocks_state) -{ - switch (max_clocks_state) { - case PP_DAL_POWERLEVEL_0: - return DM_PP_CLOCKS_DPM_STATE_LEVEL_0; - case PP_DAL_POWERLEVEL_1: - return DM_PP_CLOCKS_DPM_STATE_LEVEL_1; - case PP_DAL_POWERLEVEL_2: - return DM_PP_CLOCKS_DPM_STATE_LEVEL_2; - case PP_DAL_POWERLEVEL_3: - return DM_PP_CLOCKS_DPM_STATE_LEVEL_3; - case PP_DAL_POWERLEVEL_4: - return DM_PP_CLOCKS_DPM_STATE_LEVEL_4; - case PP_DAL_POWERLEVEL_5: - return DM_PP_CLOCKS_DPM_STATE_LEVEL_5; - case PP_DAL_POWERLEVEL_6: - return DM_PP_CLOCKS_DPM_STATE_LEVEL_6; - case PP_DAL_POWERLEVEL_7: - return DM_PP_CLOCKS_DPM_STATE_LEVEL_7; - default: - DRM_ERROR("DM_PPLIB: invalid powerlevel state: %d!\n", - max_clocks_state); - return DM_PP_CLOCKS_STATE_INVALID; - } -} - static void pp_to_dc_clock_levels( const struct amd_pp_clocks *pp_clks, struct dm_pp_clock_levels *dc_clks, @@ -315,7 +288,6 @@ bool dm_pp_get_clock_levels_by_type( DRM_INFO("DM_PPLIB: Warning: using default validation clocks!\n"); validation_clks.engine_max_clock = 72000; validation_clks.memory_max_clock = 80000; - validation_clks.level = 0; } DRM_INFO("DM_PPLIB: Validation clocks:\n"); @@ -323,8 +295,6 @@ bool dm_pp_get_clock_levels_by_type( validation_clks.engine_max_clock); DRM_INFO("DM_PPLIB: memory_max_clock: %d\n", validation_clks.memory_max_clock); - DRM_INFO("DM_PPLIB: level : %d\n", - validation_clks.level); /* Translate 10 kHz to kHz. */ validation_clks.engine_max_clock *= 10; @@ -417,14 +387,6 @@ bool dm_pp_notify_wm_clock_changes( return false; } -bool dm_pp_apply_power_level_change_request( - const struct dc_context *ctx, - struct dm_pp_power_level_change_request *level_change_req) -{ - /* TODO: to be implemented */ - return false; -} - bool dm_pp_apply_clock_for_voltage_request( const struct dc_context *ctx, struct dm_pp_clock_for_voltage_req *clock_for_voltage_req) @@ -446,23 +408,6 @@ bool dm_pp_apply_clock_for_voltage_request( return true; } -bool dm_pp_get_static_clocks( - const struct dc_context *ctx, - struct dm_pp_static_clock_info *static_clk_info) -{ - struct amdgpu_device *adev = ctx->driver_context; - struct amd_pp_clock_info pp_clk_info = {0}; - - if (amdgpu_dpm_get_current_clocks(adev, &pp_clk_info)) - return false; - - static_clk_info->max_clocks_state = pp_to_dc_powerlevel_state(pp_clk_info.max_clocks_state); - static_clk_info->max_mclk_khz = pp_clk_info.max_memory_clock * 10; - static_clk_info->max_sclk_khz = pp_clk_info.max_engine_clock * 10; - - return true; -} - static void pp_rv_set_wm_ranges(struct pp_smu *pp, struct pp_smu_wm_range_sets *ranges) { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c index dc5913a6456e2..4b823bba43921 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c @@ -29,6 +29,8 @@ #include "dc.h" #include "amdgpu_dm.h" #include "modules/power/power_helpers.h" +#include "amdgpu_dm_kunit_helpers.h" + static bool link_supports_psrsu(struct dc_link *link) { @@ -58,7 +60,8 @@ static bool link_supports_psrsu(struct dc_link *link) return false; } -static void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps) +STATIC_IFN_KUNIT +void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps) { struct dpcd_caps *dpcd_caps = &link->dpcd_caps; unsigned int power_opts = 0; @@ -86,6 +89,7 @@ static void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps) caps->rate_control_caps = 0; /* TODO: read in rc caps from aux */ caps->psr_power_opt_flag = power_opts; } +EXPORT_IF_KUNIT(amdgpu_dm_psr_fill_caps); /* * amdgpu_dm_set_psr_caps() - set link psr capabilities diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h index 16d535806ad63..9f3e22520ca02 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.h @@ -39,4 +39,9 @@ bool amdgpu_dm_psr_is_active_allowed(struct amdgpu_display_manager *dm); bool amdgpu_dm_psr_set_event(struct amdgpu_display_manager *dm, struct dc_stream_state *stream, bool set_event, enum psr_event event, bool wait_for_disable); + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps); +#endif + #endif /* AMDGPU_DM_AMDGPU_DM_PSR_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c index 297125d1db708..22aa4305d2af9 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_replay.c @@ -31,6 +31,8 @@ #include "modules/power/power_helpers.h" #include "dmub/inc/dmub_cmd.h" #include "dc/inc/link_service.h" +#include "amdgpu_dm_kunit_helpers.h" + /* * amdgpu_dm_link_supports_replay() - check if the link supports replay @@ -68,6 +70,7 @@ bool amdgpu_dm_link_supports_replay(struct dc_link *link, struct amdgpu_dm_conne return true; } +EXPORT_IF_KUNIT(amdgpu_dm_link_supports_replay); /* * amdgpu_dm_set_replay_caps() - setup Replay capabilities diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig new file mode 100644 index 0000000000000..36676326ade43 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig @@ -0,0 +1,14 @@ +CONFIG_KUNIT=y +CONFIG_PCI=y +CONFIG_DRM=y +CONFIG_DRM_AMDGPU=y +CONFIG_DRM_AMD_DC=y +CONFIG_DEBUG_FS=y +CONFIG_DRM_AMD_DC_KUNIT_TEST=y +CONFIG_FW_LOADER=y +CONFIG_DRM_KMS_HELPER=y +CONFIG_DRM_TTM=y +CONFIG_HWMON=y +CONFIG_I2C=y +CONFIG_POWER_SUPPLY=y +CONFIG_CRC16=y diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile new file mode 100644 index 0000000000000..768f9bbc50e14 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for amdgpu_dm KUnit tests. + +ccflags-y += -I$(src)/.. +ccflags-y += -I$(src)/../.. +ccflags-y += -I$(src)/../../include +ccflags-y += -I$(src)/../../modules/inc +ccflags-y += -I$(src)/../../dc +ccflags-y += -I$(src)/../../../amdgpu + +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crc_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_hdcp_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_color_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_colorop_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_psr_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_replay_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_ism_test.o diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c new file mode 100644 index 0000000000000..377fa4342ca1f --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_color_test.c @@ -0,0 +1,1071 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_color.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <linux/types.h> +#include <drm/drm_colorop.h> +#include <drm/drm_property.h> +#include <uapi/drm/drm_mode.h> + +#include "dc.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_color.h" + +/* ---- Tests for amdgpu_dm_fixpt_from_s3132 ---- */ + +static void dm_test_fixpt_from_s3132_zero(struct kunit *test) +{ + struct fixed31_32 val = amdgpu_dm_fixpt_from_s3132(0ULL); + + KUNIT_EXPECT_EQ(test, val.value, 0LL); +} + +static void dm_test_fixpt_from_s3132_one(struct kunit *test) +{ + /* 1.0 in S31.32 signed-magnitude = 1ULL << 32 */ + struct fixed31_32 val = amdgpu_dm_fixpt_from_s3132(1ULL << 32); + + KUNIT_EXPECT_EQ(test, val.value, (long long)(1ULL << 32)); +} + +static void dm_test_fixpt_from_s3132_negative_one(struct kunit *test) +{ + /* -1.0 in S31.32 signed-magnitude: sign bit set | magnitude 1<<32 */ + __u64 neg_one = (1ULL << 63) | (1ULL << 32); + struct fixed31_32 val = amdgpu_dm_fixpt_from_s3132(neg_one); + + /* 2's complement of 1.0 is -(1<<32) */ + KUNIT_EXPECT_EQ(test, val.value, -(long long)(1ULL << 32)); +} + +static void dm_test_fixpt_from_s3132_half(struct kunit *test) +{ + /* 0.5 in S31.32 = 1ULL << 31 */ + struct fixed31_32 val = amdgpu_dm_fixpt_from_s3132(1ULL << 31); + + KUNIT_EXPECT_EQ(test, val.value, (long long)(1ULL << 31)); +} + +static void dm_test_fixpt_from_s3132_neg_half(struct kunit *test) +{ + __u64 neg_half = (1ULL << 63) | (1ULL << 31); + struct fixed31_32 val = amdgpu_dm_fixpt_from_s3132(neg_half); + + KUNIT_EXPECT_EQ(test, val.value, -(long long)(1ULL << 31)); +} + +/* ---- Tests for __is_lut_linear ---- */ + +static void dm_test_is_lut_linear_with_linear_lut(struct kunit *test) +{ + const uint32_t size = 256; + struct drm_color_lut *lut; + int i; + + lut = kunit_kcalloc(test, size, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, lut); + + for (i = 0; i < size; i++) { + uint16_t val = (uint16_t)(i * MAX_DRM_LUT_VALUE / (size - 1)); + + lut[i].red = val; + lut[i].green = val; + lut[i].blue = val; + } + + KUNIT_EXPECT_TRUE(test, __is_lut_linear(lut, size)); +} + +static void dm_test_is_lut_linear_with_nonlinear_lut(struct kunit *test) +{ + const uint32_t size = 256; + struct drm_color_lut *lut; + int i; + + lut = kunit_kcalloc(test, size, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, lut); + + /* Fill with all-max values: clearly non-linear */ + for (i = 0; i < size; i++) { + lut[i].red = MAX_DRM_LUT_VALUE; + lut[i].green = MAX_DRM_LUT_VALUE; + lut[i].blue = MAX_DRM_LUT_VALUE; + } + + KUNIT_EXPECT_FALSE(test, __is_lut_linear(lut, size)); +} + +static void dm_test_is_lut_linear_rgb_mismatch(struct kunit *test) +{ + const uint32_t size = 256; + struct drm_color_lut *lut; + int i; + + lut = kunit_kcalloc(test, size, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, lut); + + for (i = 0; i < size; i++) { + uint16_t val = (uint16_t)(i * MAX_DRM_LUT_VALUE / (size - 1)); + + lut[i].red = val; + lut[i].green = val; + lut[i].blue = val; + } + + /* Introduce R/G mismatch at entry 128 */ + lut[128].red = lut[128].green + 10; + + KUNIT_EXPECT_FALSE(test, __is_lut_linear(lut, size)); +} + +/* ---- Tests for __drm_ctm_to_dc_matrix ---- */ + +static void dm_test_drm_ctm_to_dc_matrix_identity(struct kunit *test) +{ + struct drm_color_ctm ctm; + struct fixed31_32 matrix[12]; + int i; + long long one = 1LL << 32; + + memset(&ctm, 0, sizeof(ctm)); + + /* Identity 3x3 in S31.32 signed-magnitude: diagonal = 1.0 */ + ctm.matrix[0] = 1ULL << 32; /* [0][0] */ + ctm.matrix[4] = 1ULL << 32; /* [1][1] */ + ctm.matrix[8] = 1ULL << 32; /* [2][2] */ + + __drm_ctm_to_dc_matrix(&ctm, matrix); + + /* Expect 3x4 identity: diag=1.0, 4th column=0, off-diag=0 */ + for (i = 0; i < 12; i++) { + if (i == 0 || i == 5 || i == 10) + KUNIT_EXPECT_EQ(test, matrix[i].value, one); + else + KUNIT_EXPECT_EQ(test, matrix[i].value, 0LL); + } +} + +static void dm_test_drm_ctm_to_dc_matrix_negative(struct kunit *test) +{ + struct drm_color_ctm ctm; + struct fixed31_32 matrix[12]; + long long neg_one = -(1LL << 32); + + memset(&ctm, 0, sizeof(ctm)); + + /* -1.0 in S31.32 signed-magnitude */ + ctm.matrix[0] = (1ULL << 63) | (1ULL << 32); + + __drm_ctm_to_dc_matrix(&ctm, matrix); + + KUNIT_EXPECT_EQ(test, matrix[0].value, neg_one); +} + +static void dm_test_drm_ctm_to_dc_matrix_4th_col_zero(struct kunit *test) +{ + struct drm_color_ctm ctm; + struct fixed31_32 matrix[12]; + + memset(&ctm, 0, sizeof(ctm)); + + /* Fill all 9 CTM entries with 1.0 */ + for (int i = 0; i < 9; i++) + ctm.matrix[i] = 1ULL << 32; + + __drm_ctm_to_dc_matrix(&ctm, matrix); + + /* 4th column (indices 3, 7, 11) must always be zero */ + KUNIT_EXPECT_EQ(test, matrix[3].value, 0LL); + KUNIT_EXPECT_EQ(test, matrix[7].value, 0LL); + KUNIT_EXPECT_EQ(test, matrix[11].value, 0LL); +} + +/* ---- Tests for __drm_ctm_3x4_to_dc_matrix ---- */ + +static void dm_test_drm_ctm_3x4_to_dc_matrix_identity(struct kunit *test) +{ + struct drm_color_ctm_3x4 ctm; + struct fixed31_32 matrix[12]; + int i; + long long one = 1LL << 32; + + memset(&ctm, 0, sizeof(ctm)); + + /* Identity with offsets in 4th column */ + ctm.matrix[0] = 1ULL << 32; /* [0][0] */ + ctm.matrix[5] = 1ULL << 32; /* [1][1] */ + ctm.matrix[10] = 1ULL << 32; /* [2][2] */ + + __drm_ctm_3x4_to_dc_matrix(&ctm, matrix); + + for (i = 0; i < 12; i++) { + if (i == 0 || i == 5 || i == 10) + KUNIT_EXPECT_EQ(test, matrix[i].value, one); + else + KUNIT_EXPECT_EQ(test, matrix[i].value, 0LL); + } +} + +static void dm_test_drm_ctm_3x4_to_dc_matrix_offset(struct kunit *test) +{ + struct drm_color_ctm_3x4 ctm; + struct fixed31_32 matrix[12]; + long long half = 1LL << 31; + + memset(&ctm, 0, sizeof(ctm)); + + /* Set 4th column (offsets) to 0.5 */ + ctm.matrix[3] = 1ULL << 31; + ctm.matrix[7] = 1ULL << 31; + ctm.matrix[11] = 1ULL << 31; + + __drm_ctm_3x4_to_dc_matrix(&ctm, matrix); + + KUNIT_EXPECT_EQ(test, matrix[3].value, half); + KUNIT_EXPECT_EQ(test, matrix[7].value, half); + KUNIT_EXPECT_EQ(test, matrix[11].value, half); +} + +/* ---- Tests for amdgpu_tf_to_dc_tf ---- */ + +static void dm_test_tf_to_dc_tf_default(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_DEFAULT), + (int)TRANSFER_FUNCTION_LINEAR); +} + +static void dm_test_tf_to_dc_tf_identity(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_IDENTITY), + (int)TRANSFER_FUNCTION_LINEAR); +} + +static void dm_test_tf_to_dc_tf_srgb(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_SRGB_EOTF), + (int)TRANSFER_FUNCTION_SRGB); + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_SRGB_INV_EOTF), + (int)TRANSFER_FUNCTION_SRGB); +} + +static void dm_test_tf_to_dc_tf_bt709(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_BT709_OETF), + (int)TRANSFER_FUNCTION_BT709); + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_BT709_INV_OETF), + (int)TRANSFER_FUNCTION_BT709); +} + +static void dm_test_tf_to_dc_tf_pq(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_PQ_EOTF), + (int)TRANSFER_FUNCTION_PQ); + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_PQ_INV_EOTF), + (int)TRANSFER_FUNCTION_PQ); +} + +static void dm_test_tf_to_dc_tf_gamma22(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_GAMMA22_EOTF), + (int)TRANSFER_FUNCTION_GAMMA22); + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_GAMMA22_INV_EOTF), + (int)TRANSFER_FUNCTION_GAMMA22); +} + +static void dm_test_tf_to_dc_tf_gamma24(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_GAMMA24_EOTF), + (int)TRANSFER_FUNCTION_GAMMA24); + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_GAMMA24_INV_EOTF), + (int)TRANSFER_FUNCTION_GAMMA24); +} + +static void dm_test_tf_to_dc_tf_gamma26(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_GAMMA26_EOTF), + (int)TRANSFER_FUNCTION_GAMMA26); + KUNIT_EXPECT_EQ(test, (int)amdgpu_tf_to_dc_tf(AMDGPU_TRANSFER_FUNCTION_GAMMA26_INV_EOTF), + (int)TRANSFER_FUNCTION_GAMMA26); +} + +/* ---- Tests for amdgpu_colorop_tf_to_dc_tf ---- */ + +static void dm_test_colorop_tf_to_dc_tf_srgb(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_colorop_tf_to_dc_tf(DRM_COLOROP_1D_CURVE_SRGB_EOTF), + (int)TRANSFER_FUNCTION_SRGB); + KUNIT_EXPECT_EQ(test, (int)amdgpu_colorop_tf_to_dc_tf(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF), + (int)TRANSFER_FUNCTION_SRGB); +} + +static void dm_test_colorop_tf_to_dc_tf_pq(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_colorop_tf_to_dc_tf(DRM_COLOROP_1D_CURVE_PQ_125_EOTF), + (int)TRANSFER_FUNCTION_PQ); + KUNIT_EXPECT_EQ(test, (int)amdgpu_colorop_tf_to_dc_tf(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF), + (int)TRANSFER_FUNCTION_PQ); +} + +static void dm_test_colorop_tf_to_dc_tf_bt2020(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_colorop_tf_to_dc_tf(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF), + (int)TRANSFER_FUNCTION_BT709); + KUNIT_EXPECT_EQ(test, (int)amdgpu_colorop_tf_to_dc_tf(DRM_COLOROP_1D_CURVE_BT2020_OETF), + (int)TRANSFER_FUNCTION_BT709); +} + +static void dm_test_colorop_tf_to_dc_tf_gamma22(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_colorop_tf_to_dc_tf(DRM_COLOROP_1D_CURVE_GAMMA22), + (int)TRANSFER_FUNCTION_GAMMA22); + KUNIT_EXPECT_EQ(test, (int)amdgpu_colorop_tf_to_dc_tf(DRM_COLOROP_1D_CURVE_GAMMA22_INV), + (int)TRANSFER_FUNCTION_GAMMA22); +} + +static void dm_test_colorop_tf_to_dc_tf_default(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_colorop_tf_to_dc_tf(DRM_COLOROP_1D_CURVE_COUNT), + (int)TRANSFER_FUNCTION_LINEAR); +} + +/* ---- Tests for __drm_lut_to_dc_gamma (legacy path) ---- */ + +static void dm_test_drm_lut_to_dc_gamma_legacy_zero(struct kunit *test) +{ + struct drm_color_lut *lut; + struct dc_gamma *gamma; + + lut = kunit_kcalloc(test, MAX_COLOR_LEGACY_LUT_ENTRIES, + sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + gamma = kunit_kzalloc(test, sizeof(*gamma), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, gamma); + + __drm_lut_to_dc_gamma(lut, gamma, true); + + /* All-zero LUT should produce all-zero gamma entries */ + for (int i = 0; i < MAX_COLOR_LEGACY_LUT_ENTRIES; i++) { + KUNIT_EXPECT_EQ(test, gamma->entries.red[i].value, 0LL); + KUNIT_EXPECT_EQ(test, gamma->entries.green[i].value, 0LL); + KUNIT_EXPECT_EQ(test, gamma->entries.blue[i].value, 0LL); + } +} + +static void dm_test_drm_lut_to_dc_gamma_legacy_max(struct kunit *test) +{ + struct drm_color_lut *lut; + struct dc_gamma *gamma; + long long expected = (long long)MAX_DRM_LUT_VALUE << 32; + + lut = kunit_kcalloc(test, MAX_COLOR_LEGACY_LUT_ENTRIES, + sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + gamma = kunit_kzalloc(test, sizeof(*gamma), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, gamma); + + /* Set first and last entries to max */ + lut[0].red = MAX_DRM_LUT_VALUE; + lut[0].green = MAX_DRM_LUT_VALUE; + lut[0].blue = MAX_DRM_LUT_VALUE; + lut[MAX_COLOR_LEGACY_LUT_ENTRIES - 1].red = MAX_DRM_LUT_VALUE; + lut[MAX_COLOR_LEGACY_LUT_ENTRIES - 1].green = MAX_DRM_LUT_VALUE; + lut[MAX_COLOR_LEGACY_LUT_ENTRIES - 1].blue = MAX_DRM_LUT_VALUE; + + __drm_lut_to_dc_gamma(lut, gamma, true); + + /* Legacy uses dc_fixpt_from_int(val) = val << 32 */ + KUNIT_EXPECT_EQ(test, gamma->entries.red[0].value, expected); + KUNIT_EXPECT_EQ(test, gamma->entries.green[0].value, expected); + KUNIT_EXPECT_EQ(test, gamma->entries.blue[0].value, expected); + KUNIT_EXPECT_EQ(test, gamma->entries.red[MAX_COLOR_LEGACY_LUT_ENTRIES - 1].value, + expected); +} + +static void dm_test_drm_lut_to_dc_gamma_legacy_channels(struct kunit *test) +{ + struct drm_color_lut *lut; + struct dc_gamma *gamma; + + lut = kunit_kcalloc(test, MAX_COLOR_LEGACY_LUT_ENTRIES, + sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + gamma = kunit_kzalloc(test, sizeof(*gamma), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, gamma); + + /* Set distinct values per channel at index 1 */ + lut[1].red = 100; + lut[1].green = 200; + lut[1].blue = 300; + + __drm_lut_to_dc_gamma(lut, gamma, true); + + KUNIT_EXPECT_EQ(test, gamma->entries.red[1].value, 100LL << 32); + KUNIT_EXPECT_EQ(test, gamma->entries.green[1].value, 200LL << 32); + KUNIT_EXPECT_EQ(test, gamma->entries.blue[1].value, 300LL << 32); +} + +/* ---- Tests for __drm_lut_to_dc_gamma (non-legacy path) ---- */ + +static void dm_test_drm_lut_to_dc_gamma_nonlegacy_zero(struct kunit *test) +{ + struct drm_color_lut *lut; + struct dc_gamma *gamma; + + lut = kunit_kcalloc(test, MAX_COLOR_LUT_ENTRIES, + sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + gamma = kunit_kzalloc(test, sizeof(*gamma), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, gamma); + + __drm_lut_to_dc_gamma(lut, gamma, false); + + /* All-zero LUT → fraction(0, 0xFFFF) = 0 */ + KUNIT_EXPECT_EQ(test, gamma->entries.red[0].value, 0LL); + KUNIT_EXPECT_EQ(test, gamma->entries.green[0].value, 0LL); + KUNIT_EXPECT_EQ(test, gamma->entries.blue[0].value, 0LL); +} + +static void dm_test_drm_lut_to_dc_gamma_nonlegacy_max(struct kunit *test) +{ + struct drm_color_lut *lut; + struct dc_gamma *gamma; + long long one = 1LL << 32; + + lut = kunit_kcalloc(test, MAX_COLOR_LUT_ENTRIES, + sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + gamma = kunit_kzalloc(test, sizeof(*gamma), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, gamma); + + /* Max LUT value should map to 1.0 in fixed-point */ + lut[0].red = MAX_DRM_LUT_VALUE; + lut[0].green = MAX_DRM_LUT_VALUE; + lut[0].blue = MAX_DRM_LUT_VALUE; + + __drm_lut_to_dc_gamma(lut, gamma, false); + + /* dc_fixpt_from_fraction(0xFFFF, 0xFFFF) = 1.0 */ + KUNIT_EXPECT_EQ(test, gamma->entries.red[0].value, one); + KUNIT_EXPECT_EQ(test, gamma->entries.green[0].value, one); + KUNIT_EXPECT_EQ(test, gamma->entries.blue[0].value, one); +} + +/* ---- Tests for __drm_lut32_to_dc_gamma ---- */ + +static void dm_test_drm_lut32_to_dc_gamma_zero(struct kunit *test) +{ + struct drm_color_lut32 *lut; + struct dc_gamma *gamma; + + lut = kunit_kcalloc(test, MAX_COLOR_LUT_ENTRIES, + sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + gamma = kunit_kzalloc(test, sizeof(*gamma), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, gamma); + + __drm_lut32_to_dc_gamma(lut, gamma); + + /* All-zero LUT → fraction(0, 0xFFFFFFFF) = 0 */ + KUNIT_EXPECT_EQ(test, gamma->entries.red[0].value, 0LL); + KUNIT_EXPECT_EQ(test, gamma->entries.green[0].value, 0LL); + KUNIT_EXPECT_EQ(test, gamma->entries.blue[0].value, 0LL); +} + +static void dm_test_drm_lut32_to_dc_gamma_max(struct kunit *test) +{ + struct drm_color_lut32 *lut; + struct dc_gamma *gamma; + long long one = 1LL << 32; + + lut = kunit_kcalloc(test, MAX_COLOR_LUT_ENTRIES, + sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + gamma = kunit_kzalloc(test, sizeof(*gamma), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, gamma); + + /* Max 32-bit LUT value should map to 1.0 in fixed-point */ + lut[0].red = MAX_DRM_LUT32_VALUE; + lut[0].green = MAX_DRM_LUT32_VALUE; + lut[0].blue = MAX_DRM_LUT32_VALUE; + + __drm_lut32_to_dc_gamma(lut, gamma); + + /* dc_fixpt_from_fraction(0xFFFFFFFF, 0xFFFFFFFF) = 1.0 */ + KUNIT_EXPECT_EQ(test, gamma->entries.red[0].value, one); + KUNIT_EXPECT_EQ(test, gamma->entries.green[0].value, one); + KUNIT_EXPECT_EQ(test, gamma->entries.blue[0].value, one); +} + +static void dm_test_drm_lut32_to_dc_gamma_channels(struct kunit *test) +{ + struct drm_color_lut32 *lut; + struct dc_gamma *gamma; + + lut = kunit_kcalloc(test, MAX_COLOR_LUT_ENTRIES, + sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + gamma = kunit_kzalloc(test, sizeof(*gamma), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, gamma); + + /* Set distinct values per channel at index 1 */ + lut[1].red = 1000; + lut[1].green = 2000; + lut[1].blue = 3000; + + __drm_lut32_to_dc_gamma(lut, gamma); + + /* Channels should differ */ + KUNIT_EXPECT_NE(test, gamma->entries.red[1].value, + gamma->entries.green[1].value); + KUNIT_EXPECT_NE(test, gamma->entries.green[1].value, + gamma->entries.blue[1].value); + /* Red < Green < Blue since 1000 < 2000 < 3000 */ + KUNIT_EXPECT_LT(test, gamma->entries.red[1].value, + gamma->entries.green[1].value); + KUNIT_EXPECT_LT(test, gamma->entries.green[1].value, + gamma->entries.blue[1].value); +} + +/* ---- Tests for __extract_blob_lut ---- */ + +static void dm_test_extract_blob_lut_null(struct kunit *test) +{ + uint32_t size = 42; + const struct drm_color_lut *lut; + + lut = __extract_blob_lut(NULL, &size); + + KUNIT_EXPECT_NULL(test, lut); + KUNIT_EXPECT_EQ(test, size, 0); +} + +static void dm_test_extract_blob_lut_valid(struct kunit *test) +{ + const int num_entries = 4; + struct drm_property_blob *blob; + struct drm_color_lut *data; + const struct drm_color_lut *lut; + uint32_t size = 0; + + blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob); + + data = kunit_kcalloc(test, num_entries, sizeof(*data), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, data); + + data[0].red = 100; + data[0].green = 200; + data[0].blue = 300; + + blob->data = data; + blob->length = num_entries * sizeof(struct drm_color_lut); + + lut = __extract_blob_lut(blob, &size); + + KUNIT_EXPECT_EQ(test, size, (uint32_t)num_entries); + KUNIT_ASSERT_NOT_NULL(test, lut); + KUNIT_EXPECT_EQ(test, lut[0].red, 100); + KUNIT_EXPECT_EQ(test, lut[0].green, 200); + KUNIT_EXPECT_EQ(test, lut[0].blue, 300); +} + +/* ---- Tests for __extract_blob_lut32 ---- */ + +static void dm_test_extract_blob_lut32_null(struct kunit *test) +{ + uint32_t size = 42; + const struct drm_color_lut32 *lut; + + lut = __extract_blob_lut32(NULL, &size); + + KUNIT_EXPECT_NULL(test, lut); + KUNIT_EXPECT_EQ(test, size, 0); +} + +static void dm_test_extract_blob_lut32_valid(struct kunit *test) +{ + const int num_entries = 4; + struct drm_property_blob *blob; + struct drm_color_lut32 *data; + const struct drm_color_lut32 *lut; + uint32_t size = 0; + + blob = kunit_kzalloc(test, sizeof(*blob), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, blob); + + data = kunit_kcalloc(test, num_entries, sizeof(*data), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, data); + + data[0].red = 100000; + data[0].green = 200000; + data[0].blue = 300000; + + blob->data = data; + blob->length = num_entries * sizeof(struct drm_color_lut32); + + lut = __extract_blob_lut32(blob, &size); + + KUNIT_EXPECT_EQ(test, size, (uint32_t)num_entries); + KUNIT_ASSERT_NOT_NULL(test, lut); + KUNIT_EXPECT_EQ(test, lut[0].red, (uint32_t)100000); + KUNIT_EXPECT_EQ(test, lut[0].green, (uint32_t)200000); + KUNIT_EXPECT_EQ(test, lut[0].blue, (uint32_t)300000); +} + +/* ---- Tests for __to_dc_lut3d_color ---- */ + +static void dm_test_to_dc_lut3d_color_zero(struct kunit *test) +{ + struct dc_rgb rgb = {0}; + struct drm_color_lut lut = {0}; + + __to_dc_lut3d_color(&rgb, lut, 12); + + KUNIT_EXPECT_EQ(test, rgb.red, 0U); + KUNIT_EXPECT_EQ(test, rgb.green, 0U); + KUNIT_EXPECT_EQ(test, rgb.blue, 0U); +} + +static void dm_test_to_dc_lut3d_color_max(struct kunit *test) +{ + struct dc_rgb rgb = {0}; + struct drm_color_lut lut = { + .red = 0xFFFF, + .green = 0xFFFF, + .blue = 0xFFFF, + }; + + /* 12-bit extraction: 0xFFFF maps to (1 << 12) - 1 = 4095 */ + __to_dc_lut3d_color(&rgb, lut, 12); + + KUNIT_EXPECT_EQ(test, rgb.red, 4095U); + KUNIT_EXPECT_EQ(test, rgb.green, 4095U); + KUNIT_EXPECT_EQ(test, rgb.blue, 4095U); +} + +static void dm_test_to_dc_lut3d_color_channels(struct kunit *test) +{ + struct dc_rgb rgb = {0}; + struct drm_color_lut lut = { + .red = 0x8000, + .green = 0x4000, + .blue = 0xC000, + }; + + __to_dc_lut3d_color(&rgb, lut, 12); + + /* Channels should be distinct and ordered: green < red < blue */ + KUNIT_EXPECT_GT(test, rgb.red, rgb.green); + KUNIT_EXPECT_GT(test, rgb.blue, rgb.red); +} + +/* ---- Tests for __drm_3dlut_to_dc_3dlut ---- */ + +static void dm_test_3dlut_to_dc_3dlut_distribution(struct kunit *test) +{ + /* + * Use 9 entries: loop processes 2 groups of 4, then lut0 gets + * one extra final entry. Total: lut0=3, lut1=2, lut2=2, lut3=2. + */ + const uint32_t lut3d_size = 9; + struct drm_color_lut *lut; + struct tetrahedral_params *params; + int i; + + lut = kunit_kcalloc(test, lut3d_size, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + params = kunit_kzalloc(test, sizeof(*params), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, params); + + /* Fill LUT with distinct values per entry */ + for (i = 0; i < lut3d_size; i++) { + lut[i].red = (i + 1) * 1000; + lut[i].green = (i + 1) * 2000; + lut[i].blue = (i + 1) * 3000; + } + + __drm_3dlut_to_dc_3dlut(lut, lut3d_size, params, true, 12); + + /* Group 0: lut[0]→lut0[0], lut[1]→lut1[0], lut[2]→lut2[0], lut[3]→lut3[0] */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[0].red, + drm_color_lut_extract(lut[0].red, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut1[0].red, + drm_color_lut_extract(lut[1].red, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut2[0].red, + drm_color_lut_extract(lut[2].red, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut3[0].red, + drm_color_lut_extract(lut[3].red, 12)); + + /* Group 1: lut[4]→lut0[1], lut[5]→lut1[1], lut[6]→lut2[1], lut[7]→lut3[1] */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[1].red, + drm_color_lut_extract(lut[4].red, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut1[1].red, + drm_color_lut_extract(lut[5].red, 12)); + + /* Final extra entry: lut[8]→lut0[2] */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[2].red, + drm_color_lut_extract(lut[8].red, 12)); +} + +static void dm_test_3dlut_to_dc_3dlut_tetrahedral_17(struct kunit *test) +{ + /* Minimal test with 5 entries using tetrahedral_17 path */ + const uint32_t lut3d_size = 5; + struct drm_color_lut *lut; + struct tetrahedral_params *params; + + lut = kunit_kcalloc(test, lut3d_size, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + params = kunit_kzalloc(test, sizeof(*params), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, params); + + lut[0].red = 0xFFFF; + lut[0].green = 0; + lut[0].blue = 0; + lut[1].red = 0; + lut[1].green = 0xFFFF; + lut[1].blue = 0; + lut[2].red = 0; + lut[2].green = 0; + lut[2].blue = 0xFFFF; + lut[3].red = 0x8000; + lut[3].green = 0x8000; + lut[3].blue = 0x8000; + lut[4].red = 0xFFFF; + lut[4].green = 0xFFFF; + lut[4].blue = 0xFFFF; + + __drm_3dlut_to_dc_3dlut(lut, lut3d_size, params, false, 12); + + /* lut[0]→lut0[0]: red=4095, green=0, blue=0 */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[0].red, 4095U); + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[0].green, 0U); + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[0].blue, 0U); + + /* lut[1]→lut1[0]: red=0, green=4095, blue=0 */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut1[0].green, 4095U); + + /* lut[2]→lut2[0]: red=0, green=0, blue=4095 */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut2[0].blue, 4095U); + + /* lut[4]→lut0[1] (extra final entry): all 4095 */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[1].red, 4095U); + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[1].green, 4095U); + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[1].blue, 4095U); +} + +static void dm_test_3dlut_to_dc_3dlut_green_blue(struct kunit *test) +{ + /* Verify green and blue channels are also correctly distributed */ + const uint32_t lut3d_size = 5; + struct drm_color_lut *lut; + struct tetrahedral_params *params; + + lut = kunit_kcalloc(test, lut3d_size, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + params = kunit_kzalloc(test, sizeof(*params), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, params); + + lut[0].red = 100; + lut[0].green = 200; + lut[0].blue = 300; + lut[3].red = 400; + lut[3].green = 500; + lut[3].blue = 600; + + __drm_3dlut_to_dc_3dlut(lut, lut3d_size, params, true, 12); + + /* lut[0]→lut0[0]: verify all channels */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[0].red, + drm_color_lut_extract(100, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[0].green, + drm_color_lut_extract(200, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[0].blue, + drm_color_lut_extract(300, 12)); + + /* lut[3]→lut3[0]: verify all channels */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut3[0].red, + drm_color_lut_extract(400, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut3[0].green, + drm_color_lut_extract(500, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut3[0].blue, + drm_color_lut_extract(600, 12)); +} + +/* ---- Tests for __to_dc_lut3d_32_color ---- */ + +static void dm_test_to_dc_lut3d_32_color_zero(struct kunit *test) +{ + struct dc_rgb rgb = {0}; + struct drm_color_lut32 lut = {0}; + + __to_dc_lut3d_32_color(&rgb, lut, 12); + + KUNIT_EXPECT_EQ(test, rgb.red, 0U); + KUNIT_EXPECT_EQ(test, rgb.green, 0U); + KUNIT_EXPECT_EQ(test, rgb.blue, 0U); +} + +static void dm_test_to_dc_lut3d_32_color_max(struct kunit *test) +{ + struct dc_rgb rgb = {0}; + struct drm_color_lut32 lut = { + .red = 0xFFFFFFFF, + .green = 0xFFFFFFFF, + .blue = 0xFFFFFFFF, + }; + + /* 12-bit extraction: 0xFFFFFFFF maps to (1 << 12) - 1 = 4095 */ + __to_dc_lut3d_32_color(&rgb, lut, 12); + + KUNIT_EXPECT_EQ(test, rgb.red, 4095U); + KUNIT_EXPECT_EQ(test, rgb.green, 4095U); + KUNIT_EXPECT_EQ(test, rgb.blue, 4095U); +} + +static void dm_test_to_dc_lut3d_32_color_channels(struct kunit *test) +{ + struct dc_rgb rgb = {0}; + struct drm_color_lut32 lut = { + .red = 0x80000000, + .green = 0x40000000, + .blue = 0xC0000000, + }; + + __to_dc_lut3d_32_color(&rgb, lut, 12); + + /* Channels should be distinct and ordered: green < red < blue */ + KUNIT_EXPECT_GT(test, rgb.red, rgb.green); + KUNIT_EXPECT_GT(test, rgb.blue, rgb.red); +} + +/* ---- Tests for __drm_3dlut32_to_dc_3dlut ---- */ + +static void dm_test_3dlut32_to_dc_3dlut_distribution(struct kunit *test) +{ + /* + * Use 9 entries: loop processes 2 groups of 4, then lut0 gets + * one extra final entry. Total: lut0=3, lut1=2, lut2=2, lut3=2. + */ + const uint32_t lut3d_size = 9; + struct drm_color_lut32 *lut; + struct tetrahedral_params *params; + int i; + + lut = kunit_kcalloc(test, lut3d_size, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + params = kunit_kzalloc(test, sizeof(*params), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, params); + + /* Fill LUT with distinct values per entry */ + for (i = 0; i < lut3d_size; i++) { + lut[i].red = (i + 1) * 100000; + lut[i].green = (i + 1) * 200000; + lut[i].blue = (i + 1) * 300000; + } + + __drm_3dlut32_to_dc_3dlut(lut, lut3d_size, params, true, 12); + + /* Group 0: lut[0]→lut0[0], lut[1]→lut1[0], lut[2]→lut2[0], lut[3]→lut3[0] */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[0].red, + drm_color_lut32_extract(lut[0].red, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut1[0].red, + drm_color_lut32_extract(lut[1].red, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut2[0].red, + drm_color_lut32_extract(lut[2].red, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut3[0].red, + drm_color_lut32_extract(lut[3].red, 12)); + + /* Group 1: lut[4]→lut0[1], lut[5]→lut1[1], lut[6]→lut2[1], lut[7]→lut3[1] */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[1].red, + drm_color_lut32_extract(lut[4].red, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut1[1].red, + drm_color_lut32_extract(lut[5].red, 12)); + + /* Final extra entry: lut[8]→lut0[2] */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[2].red, + drm_color_lut32_extract(lut[8].red, 12)); +} + +static void dm_test_3dlut32_to_dc_3dlut_tetrahedral_17(struct kunit *test) +{ + /* Minimal test with 5 entries using tetrahedral_17 path */ + const uint32_t lut3d_size = 5; + struct drm_color_lut32 *lut; + struct tetrahedral_params *params; + + lut = kunit_kcalloc(test, lut3d_size, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + params = kunit_kzalloc(test, sizeof(*params), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, params); + + lut[0].red = 0xFFFFFFFF; + lut[0].green = 0; + lut[0].blue = 0; + lut[1].red = 0; + lut[1].green = 0xFFFFFFFF; + lut[1].blue = 0; + lut[2].red = 0; + lut[2].green = 0; + lut[2].blue = 0xFFFFFFFF; + lut[3].red = 0x80000000; + lut[3].green = 0x80000000; + lut[3].blue = 0x80000000; + lut[4].red = 0xFFFFFFFF; + lut[4].green = 0xFFFFFFFF; + lut[4].blue = 0xFFFFFFFF; + + __drm_3dlut32_to_dc_3dlut(lut, lut3d_size, params, false, 12); + + /* lut[0]→lut0[0]: red=4095, green=0, blue=0 */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[0].red, 4095U); + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[0].green, 0U); + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[0].blue, 0U); + + /* lut[1]→lut1[0]: red=0, green=4095, blue=0 */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut1[0].green, 4095U); + + /* lut[2]→lut2[0]: red=0, green=0, blue=4095 */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut2[0].blue, 4095U); + + /* lut[4]→lut0[1] (extra final entry): all 4095 */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[1].red, 4095U); + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[1].green, 4095U); + KUNIT_EXPECT_EQ(test, params->tetrahedral_17.lut0[1].blue, 4095U); +} + +static void dm_test_3dlut32_to_dc_3dlut_green_blue(struct kunit *test) +{ + /* Verify green and blue channels are also correctly distributed */ + const uint32_t lut3d_size = 5; + struct drm_color_lut32 *lut; + struct tetrahedral_params *params; + + lut = kunit_kcalloc(test, lut3d_size, sizeof(*lut), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, lut); + + params = kunit_kzalloc(test, sizeof(*params), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, params); + + lut[0].red = 100000; + lut[0].green = 200000; + lut[0].blue = 300000; + lut[3].red = 400000; + lut[3].green = 500000; + lut[3].blue = 600000; + + __drm_3dlut32_to_dc_3dlut(lut, lut3d_size, params, true, 12); + + /* lut[0]→lut0[0]: verify all channels */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[0].red, + drm_color_lut32_extract(100000, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[0].green, + drm_color_lut32_extract(200000, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut0[0].blue, + drm_color_lut32_extract(300000, 12)); + + /* lut[3]→lut3[0]: verify all channels */ + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut3[0].red, + drm_color_lut32_extract(400000, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut3[0].green, + drm_color_lut32_extract(500000, 12)); + KUNIT_EXPECT_EQ(test, params->tetrahedral_9.lut3[0].blue, + drm_color_lut32_extract(600000, 12)); +} + +static struct kunit_case dm_color_test_cases[] = { + /* amdgpu_dm_fixpt_from_s3132 */ + KUNIT_CASE(dm_test_fixpt_from_s3132_zero), + KUNIT_CASE(dm_test_fixpt_from_s3132_one), + KUNIT_CASE(dm_test_fixpt_from_s3132_negative_one), + KUNIT_CASE(dm_test_fixpt_from_s3132_half), + KUNIT_CASE(dm_test_fixpt_from_s3132_neg_half), + /* __is_lut_linear */ + KUNIT_CASE(dm_test_is_lut_linear_with_linear_lut), + KUNIT_CASE(dm_test_is_lut_linear_with_nonlinear_lut), + KUNIT_CASE(dm_test_is_lut_linear_rgb_mismatch), + /* __drm_ctm_to_dc_matrix */ + KUNIT_CASE(dm_test_drm_ctm_to_dc_matrix_identity), + KUNIT_CASE(dm_test_drm_ctm_to_dc_matrix_negative), + KUNIT_CASE(dm_test_drm_ctm_to_dc_matrix_4th_col_zero), + /* __drm_ctm_3x4_to_dc_matrix */ + KUNIT_CASE(dm_test_drm_ctm_3x4_to_dc_matrix_identity), + KUNIT_CASE(dm_test_drm_ctm_3x4_to_dc_matrix_offset), + /* amdgpu_tf_to_dc_tf */ + KUNIT_CASE(dm_test_tf_to_dc_tf_default), + KUNIT_CASE(dm_test_tf_to_dc_tf_identity), + KUNIT_CASE(dm_test_tf_to_dc_tf_srgb), + KUNIT_CASE(dm_test_tf_to_dc_tf_bt709), + KUNIT_CASE(dm_test_tf_to_dc_tf_pq), + KUNIT_CASE(dm_test_tf_to_dc_tf_gamma22), + KUNIT_CASE(dm_test_tf_to_dc_tf_gamma24), + KUNIT_CASE(dm_test_tf_to_dc_tf_gamma26), + /* amdgpu_colorop_tf_to_dc_tf */ + KUNIT_CASE(dm_test_colorop_tf_to_dc_tf_srgb), + KUNIT_CASE(dm_test_colorop_tf_to_dc_tf_pq), + KUNIT_CASE(dm_test_colorop_tf_to_dc_tf_bt2020), + KUNIT_CASE(dm_test_colorop_tf_to_dc_tf_gamma22), + KUNIT_CASE(dm_test_colorop_tf_to_dc_tf_default), + /* __drm_lut_to_dc_gamma */ + KUNIT_CASE(dm_test_drm_lut_to_dc_gamma_legacy_zero), + KUNIT_CASE(dm_test_drm_lut_to_dc_gamma_legacy_max), + KUNIT_CASE(dm_test_drm_lut_to_dc_gamma_legacy_channels), + KUNIT_CASE(dm_test_drm_lut_to_dc_gamma_nonlegacy_zero), + KUNIT_CASE(dm_test_drm_lut_to_dc_gamma_nonlegacy_max), + /* __drm_lut32_to_dc_gamma */ + KUNIT_CASE(dm_test_drm_lut32_to_dc_gamma_zero), + KUNIT_CASE(dm_test_drm_lut32_to_dc_gamma_max), + KUNIT_CASE(dm_test_drm_lut32_to_dc_gamma_channels), + /* __extract_blob_lut */ + KUNIT_CASE(dm_test_extract_blob_lut_null), + KUNIT_CASE(dm_test_extract_blob_lut_valid), + /* __extract_blob_lut32 */ + KUNIT_CASE(dm_test_extract_blob_lut32_null), + KUNIT_CASE(dm_test_extract_blob_lut32_valid), + /* __to_dc_lut3d_color */ + KUNIT_CASE(dm_test_to_dc_lut3d_color_zero), + KUNIT_CASE(dm_test_to_dc_lut3d_color_max), + KUNIT_CASE(dm_test_to_dc_lut3d_color_channels), + /* __drm_3dlut_to_dc_3dlut */ + KUNIT_CASE(dm_test_3dlut_to_dc_3dlut_distribution), + KUNIT_CASE(dm_test_3dlut_to_dc_3dlut_tetrahedral_17), + KUNIT_CASE(dm_test_3dlut_to_dc_3dlut_green_blue), + /* __to_dc_lut3d_32_color */ + KUNIT_CASE(dm_test_to_dc_lut3d_32_color_zero), + KUNIT_CASE(dm_test_to_dc_lut3d_32_color_max), + KUNIT_CASE(dm_test_to_dc_lut3d_32_color_channels), + /* __drm_3dlut32_to_dc_3dlut */ + KUNIT_CASE(dm_test_3dlut32_to_dc_3dlut_distribution), + KUNIT_CASE(dm_test_3dlut32_to_dc_3dlut_tetrahedral_17), + KUNIT_CASE(dm_test_3dlut32_to_dc_3dlut_green_blue), + {} +}; + +static struct kunit_suite dm_color_test_suite = { + .name = "amdgpu_dm_color", + .test_cases = dm_color_test_cases, +}; + +kunit_test_suite(dm_color_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_color"); +MODULE_AUTHOR("AMD"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c new file mode 100644 index 0000000000000..6c77a7159188e --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_colorop_test.c @@ -0,0 +1,161 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_colorop.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <drm/drm_colorop.h> + +#include "amdgpu_dm_colorop.h" + +/* Tests for amdgpu_dm_supported_degam_tfs */ + +static void dm_test_supported_degam_tfs_has_srgb_eotf(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs & + BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF)); +} + +static void dm_test_supported_degam_tfs_has_pq125_eotf(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs & + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF)); +} + +static void dm_test_supported_degam_tfs_has_bt2020_inv_oetf(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs & + BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF)); +} + +static void dm_test_supported_degam_tfs_has_gamma22_inv(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_degam_tfs & + BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV)); +} + +static void dm_test_supported_degam_tfs_no_extra_bits(struct kunit *test) +{ + u64 expected = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) | + BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_degam_tfs, expected); +} + +/* Tests for amdgpu_dm_supported_shaper_tfs */ + +static void dm_test_supported_shaper_tfs_has_srgb_inv_eotf(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs & + BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF)); +} + +static void dm_test_supported_shaper_tfs_has_pq125_inv_eotf(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs & + BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF)); +} + +static void dm_test_supported_shaper_tfs_has_bt2020_oetf(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs & + BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF)); +} + +static void dm_test_supported_shaper_tfs_has_gamma22(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_shaper_tfs & + BIT(DRM_COLOROP_1D_CURVE_GAMMA22)); +} + +static void dm_test_supported_shaper_tfs_no_extra_bits(struct kunit *test) +{ + u64 expected = BIT(DRM_COLOROP_1D_CURVE_SRGB_INV_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_PQ_125_INV_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_BT2020_OETF) | + BIT(DRM_COLOROP_1D_CURVE_GAMMA22); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_shaper_tfs, expected); +} + +/* Tests for amdgpu_dm_supported_blnd_tfs */ + +static void dm_test_supported_blnd_tfs_has_srgb_eotf(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs & + BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF)); +} + +static void dm_test_supported_blnd_tfs_has_pq125_eotf(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs & + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF)); +} + +static void dm_test_supported_blnd_tfs_has_bt2020_inv_oetf(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs & + BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF)); +} + +static void dm_test_supported_blnd_tfs_has_gamma22_inv(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_supported_blnd_tfs & + BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV)); +} + +static void dm_test_supported_blnd_tfs_no_extra_bits(struct kunit *test) +{ + u64 expected = BIT(DRM_COLOROP_1D_CURVE_SRGB_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_PQ_125_EOTF) | + BIT(DRM_COLOROP_1D_CURVE_BT2020_INV_OETF) | + BIT(DRM_COLOROP_1D_CURVE_GAMMA22_INV); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_blnd_tfs, expected); +} + +/* degam and blnd should support the same set of EOTF curves */ +static void dm_test_degam_and_blnd_tfs_match(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, amdgpu_dm_supported_degam_tfs, + amdgpu_dm_supported_blnd_tfs); +} + +static struct kunit_case dm_colorop_test_cases[] = { + /* degam TFs */ + KUNIT_CASE(dm_test_supported_degam_tfs_has_srgb_eotf), + KUNIT_CASE(dm_test_supported_degam_tfs_has_pq125_eotf), + KUNIT_CASE(dm_test_supported_degam_tfs_has_bt2020_inv_oetf), + KUNIT_CASE(dm_test_supported_degam_tfs_has_gamma22_inv), + KUNIT_CASE(dm_test_supported_degam_tfs_no_extra_bits), + /* shaper TFs */ + KUNIT_CASE(dm_test_supported_shaper_tfs_has_srgb_inv_eotf), + KUNIT_CASE(dm_test_supported_shaper_tfs_has_pq125_inv_eotf), + KUNIT_CASE(dm_test_supported_shaper_tfs_has_bt2020_oetf), + KUNIT_CASE(dm_test_supported_shaper_tfs_has_gamma22), + KUNIT_CASE(dm_test_supported_shaper_tfs_no_extra_bits), + /* blnd TFs */ + KUNIT_CASE(dm_test_supported_blnd_tfs_has_srgb_eotf), + KUNIT_CASE(dm_test_supported_blnd_tfs_has_pq125_eotf), + KUNIT_CASE(dm_test_supported_blnd_tfs_has_bt2020_inv_oetf), + KUNIT_CASE(dm_test_supported_blnd_tfs_has_gamma22_inv), + KUNIT_CASE(dm_test_supported_blnd_tfs_no_extra_bits), + /* cross-check */ + KUNIT_CASE(dm_test_degam_and_blnd_tfs_match), + {} +}; + +static struct kunit_suite dm_colorop_test_suite = { + .name = "amdgpu_dm_colorop", + .test_cases = dm_colorop_test_cases, +}; + +kunit_test_suite(dm_colorop_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_colorop"); +MODULE_AUTHOR("AMD"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c new file mode 100644 index 0000000000000..bba8b1a8fa1c0 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crc_test.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_crc.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include "amdgpu_dm_crc.h" + +static void dm_test_parse_crc_source_none(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_NONE, dm_parse_crc_source("none")); + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_NONE, dm_parse_crc_source(NULL)); +} + +static void dm_test_parse_crc_source_crtc(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_CRTC, dm_parse_crc_source("crtc")); + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_CRTC, dm_parse_crc_source("auto")); +} + +static void dm_test_parse_crc_source_dprx(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, dm_parse_crc_source("dprx")); +} + +static void dm_test_parse_crc_source_crtc_dither(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER, + dm_parse_crc_source("crtc dither")); +} + +static void dm_test_parse_crc_source_dprx_dither(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER, + dm_parse_crc_source("dprx dither")); +} + +static void dm_test_parse_crc_source_invalid(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_INVALID, + dm_parse_crc_source("invalid")); + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_INVALID, + dm_parse_crc_source("unknown")); + KUNIT_EXPECT_EQ(test, AMDGPU_DM_PIPE_CRC_SOURCE_INVALID, + dm_parse_crc_source("")); +} + +static void dm_test_is_crc_source_crtc(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC)); + KUNIT_EXPECT_TRUE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER)); + + KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)); + KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER)); + KUNIT_EXPECT_FALSE(test, dm_is_crc_source_crtc(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID)); +} + +static void dm_test_is_crc_source_dprx(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)); + KUNIT_EXPECT_TRUE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER)); + + KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC)); + KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER)); + KUNIT_EXPECT_FALSE(test, dm_is_crc_source_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID)); +} + +static void dm_test_need_crc_dither(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + KUNIT_EXPECT_TRUE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER)); + KUNIT_EXPECT_TRUE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER)); + + KUNIT_EXPECT_FALSE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC)); + KUNIT_EXPECT_FALSE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)); + KUNIT_EXPECT_FALSE(test, dm_need_crc_dither(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID)); +} + +static void dm_test_is_valid_crc_source(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC)); + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)); + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC_DITHER)); + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER)); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_MAX)); + KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID)); +} + +static struct kunit_case dm_crc_test_cases[] = { + KUNIT_CASE(dm_test_parse_crc_source_none), + KUNIT_CASE(dm_test_parse_crc_source_crtc), + KUNIT_CASE(dm_test_parse_crc_source_dprx), + KUNIT_CASE(dm_test_parse_crc_source_crtc_dither), + KUNIT_CASE(dm_test_parse_crc_source_dprx_dither), + KUNIT_CASE(dm_test_parse_crc_source_invalid), + KUNIT_CASE(dm_test_is_crc_source_crtc), + KUNIT_CASE(dm_test_is_crc_source_dprx), + KUNIT_CASE(dm_test_need_crc_dither), + KUNIT_CASE(dm_test_is_valid_crc_source), + {} +}; + +static struct kunit_suite dm_crc_test_suite = { + .name = "amdgpu_dm_crc", + .test_cases = dm_crc_test_cases, +}; + +kunit_test_suite(dm_crc_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_crc"); +MODULE_AUTHOR("AMD"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c new file mode 100644 index 0000000000000..d03b606d27bc7 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_hdcp_test.c @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_hdcp.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <linux/workqueue.h> + +#include "amdgpu_dm_hdcp.h" + +static void dummy_work_fn(struct work_struct *work) {} + +/* Tests for process_output() */ + +/* + * Helper: allocate and initialise a minimal hdcp_workqueue sufficient for + * process_output() testing. Only the three delayed works accessed by + * process_output() are initialised; everything else is zeroed. + */ +static struct hdcp_workqueue *alloc_test_workqueue(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->callback_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->watchdog_timer_dwork, dummy_work_fn); + INIT_DELAYED_WORK(&work->property_validate_dwork, dummy_work_fn); + + return work; +} + +/* + * process_output() always schedules property_validate_dwork with delay=0, + * which queues the work item directly (bypassing the timer). Use + * work_pending() rather than delayed_work_pending() to detect this. + */ +static void dm_test_process_output_property_validate_always_scheduled(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue(test); + + /* No flags set: only property_validate_dwork should be enqueued */ + process_output(work); + + KUNIT_EXPECT_TRUE(test, work_pending(&work->property_validate_dwork.work)); + KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork)); + KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork)); + + cancel_delayed_work_sync(&work->property_validate_dwork); +} + +/* + * output.callback_needed=true must schedule callback_dwork. + */ +static void dm_test_process_output_callback_needed(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue(test); + + work->output.callback_needed = true; + work->output.callback_delay = 500; + + 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); +} + +/* + * output.callback_stop=true must cancel a previously scheduled callback_dwork. + */ +static void dm_test_process_output_callback_stop(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue(test); + + /* Pre-schedule callback_dwork with a long delay so it won't fire. */ + 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; + + process_output(work); + + KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->callback_dwork)); + + cancel_delayed_work_sync(&work->property_validate_dwork); +} + +/* + * output.watchdog_timer_needed=true must schedule watchdog_timer_dwork. + */ +static void dm_test_process_output_watchdog_needed(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue(test); + + work->output.watchdog_timer_needed = true; + work->output.watchdog_timer_delay = 1000; + + 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); +} + +/* + * output.watchdog_timer_stop=true must cancel a previously scheduled + * watchdog_timer_dwork. + */ +static void dm_test_process_output_watchdog_stop(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue(test); + + /* Pre-schedule watchdog_timer_dwork with a long delay. */ + 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; + + process_output(work); + + KUNIT_EXPECT_FALSE(test, delayed_work_pending(&work->watchdog_timer_dwork)); + + cancel_delayed_work_sync(&work->property_validate_dwork); +} + +/* + * Both callback_needed and watchdog_timer_needed set: both dworks are + * scheduled independently. + */ +static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *test) +{ + struct hdcp_workqueue *work = alloc_test_workqueue(test); + + work->output.callback_needed = true; + work->output.callback_delay = 200; + work->output.watchdog_timer_needed = true; + work->output.watchdog_timer_delay = 800; + + process_output(work); + + KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->callback_dwork)); + KUNIT_EXPECT_TRUE(test, delayed_work_pending(&work->watchdog_timer_dwork)); + + cancel_delayed_work_sync(&work->callback_dwork); + cancel_delayed_work_sync(&work->watchdog_timer_dwork); + cancel_delayed_work_sync(&work->property_validate_dwork); +} +/* End of tests for process_output() */ + +static struct kunit_case dm_hdcp_test_cases[] = { + KUNIT_CASE(dm_test_process_output_property_validate_always_scheduled), + KUNIT_CASE(dm_test_process_output_callback_needed), + KUNIT_CASE(dm_test_process_output_callback_stop), + 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), + {} +}; + +static struct kunit_suite dm_hdcp_test_suite = { + .name = "amdgpu_dm_hdcp", + .test_cases = dm_hdcp_test_cases, +}; + +kunit_test_suite(dm_hdcp_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_hdcp"); +MODULE_AUTHOR("AMD"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c new file mode 100644 index 0000000000000..e761105e19952 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_ism_test.c @@ -0,0 +1,636 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_ism.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include "dc.h" +#include "amdgpu_dm_ism.h" + +/* + * Helper: allocate and zero-initialise a dc_stream_state for timing tests. + * Only the timing sub-struct is accessed by the functions under test. + */ +static struct dc_stream_state *alloc_test_stream(struct kunit *test) +{ + struct dc_stream_state *stream; + + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, stream); + + return stream; +} + +/* + * Helper: allocate and zero-initialise an ISM instance. + */ +static struct amdgpu_dm_ism *alloc_test_ism(struct kunit *test) +{ + struct amdgpu_dm_ism *ism; + + ism = kunit_kzalloc(test, sizeof(*ism), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ism); + + return ism; +} + +/* ===== Tests for dm_ism_next_state — FULL_POWER_RUNNING transitions ===== */ + +static void dm_test_ism_next_state_running_enter_idle(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_RUNNING, + DM_ISM_EVENT_ENTER_IDLE_REQUESTED, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_WAITING); +} + +static void dm_test_ism_next_state_running_begin_cursor(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_RUNNING, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_BUSY); +} + +static void dm_test_ism_next_state_running_invalid(struct kunit *test) +{ + enum amdgpu_dm_ism_state next = DM_ISM_NUM_STATES; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_RUNNING, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next); + KUNIT_EXPECT_FALSE(test, ok); + /* next should remain untouched on invalid transition */ + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_NUM_STATES); +} + +/* ===== Tests for dm_ism_next_state — FULL_POWER_BUSY transitions ===== */ + +static void dm_test_ism_next_state_busy_enter_idle(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_BUSY, + DM_ISM_EVENT_ENTER_IDLE_REQUESTED, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY); +} + +static void dm_test_ism_next_state_busy_end_cursor(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_FULL_POWER_BUSY, + DM_ISM_EVENT_END_CURSOR_UPDATE, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING); +} + +/* ===== Tests for dm_ism_next_state — HYSTERESIS_WAITING transitions ===== */ + +static void dm_test_ism_next_state_hyst_wait_exit_idle(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_TIMER_ABORTED); +} + +static void dm_test_ism_next_state_hyst_wait_begin_cursor(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY); +} + +static void dm_test_ism_next_state_hyst_wait_timer(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING, + DM_ISM_EVENT_TIMER_ELAPSED, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE); +} + +static void dm_test_ism_next_state_hyst_wait_immediate(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_WAITING, + DM_ISM_EVENT_IMMEDIATE, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE); +} + +/* ===== Tests for dm_ism_next_state — HYSTERESIS_BUSY transitions ===== */ + +static void dm_test_ism_next_state_hyst_busy_exit_idle(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_BUSY, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_BUSY); +} + +static void dm_test_ism_next_state_hyst_busy_end_cursor(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_HYSTERESIS_BUSY, + DM_ISM_EVENT_END_CURSOR_UPDATE, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_WAITING); +} + +/* ===== Tests for dm_ism_next_state — OPTIMIZED_IDLE transitions ===== */ + +static void dm_test_ism_next_state_opt_idle_exit(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING); +} + +static void dm_test_ism_next_state_opt_idle_begin_cursor(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY); +} + +static void dm_test_ism_next_state_opt_idle_sso_timer(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE, + DM_ISM_EVENT_SSO_TIMER_ELAPSED, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE_SSO); +} + +static void dm_test_ism_next_state_opt_idle_immediate(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE, + DM_ISM_EVENT_IMMEDIATE, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_OPTIMIZED_IDLE_SSO); +} + +/* ===== Tests for dm_ism_next_state — OPTIMIZED_IDLE_SSO transitions ===== */ + +static void dm_test_ism_next_state_opt_idle_sso_exit(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE_SSO, + DM_ISM_EVENT_EXIT_IDLE_REQUESTED, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING); +} + +static void dm_test_ism_next_state_opt_idle_sso_cursor(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_OPTIMIZED_IDLE_SSO, + DM_ISM_EVENT_BEGIN_CURSOR_UPDATE, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_HYSTERESIS_BUSY); +} + +/* ===== Tests for dm_ism_next_state — TIMER_ABORTED transitions ===== */ + +static void dm_test_ism_next_state_aborted_immediate(struct kunit *test) +{ + enum amdgpu_dm_ism_state next; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_TIMER_ABORTED, + DM_ISM_EVENT_IMMEDIATE, &next); + KUNIT_EXPECT_TRUE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_STATE_FULL_POWER_RUNNING); +} + +static void dm_test_ism_next_state_aborted_invalid(struct kunit *test) +{ + enum amdgpu_dm_ism_state next = DM_ISM_NUM_STATES; + bool ok; + + ok = dm_ism_next_state(DM_ISM_STATE_TIMER_ABORTED, + DM_ISM_EVENT_ENTER_IDLE_REQUESTED, &next); + KUNIT_EXPECT_FALSE(test, ok); + KUNIT_EXPECT_EQ(test, (int)next, (int)DM_ISM_NUM_STATES); +} + +/* ===== Tests for dm_ism_get_sso_delay ===== */ + +static void dm_test_ism_sso_delay_null_stream(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + + ism->config.sso_num_frames = 5; + + KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, NULL), (uint64_t)0); +} + +static void dm_test_ism_sso_delay_zero_frames(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + + stream->timing.v_total = 1125; + stream->timing.h_total = 2200; + stream->timing.pix_clk_100hz = 1485000; + ism->config.sso_num_frames = 0; + + KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, stream), (uint64_t)0); +} + +static void dm_test_ism_sso_delay_1080p60_3frames(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + uint64_t expected_one_frame_ns, expected; + + /* + * 1080p@60Hz: v_total=1125, h_total=2200, pix_clk=148.5MHz + * pix_clk_100hz = 1485000 + * one_frame_ns = (1125 * 2200 * 10000000) / 1485000 = 16666666 ns + */ + stream->timing.v_total = 1125; + stream->timing.h_total = 2200; + stream->timing.pix_clk_100hz = 1485000; + ism->config.sso_num_frames = 3; + + expected_one_frame_ns = div64_u64((uint64_t)1125 * 2200 * 10000000ULL, + 1485000); + expected = 3 * expected_one_frame_ns; + + KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, stream), expected); +} + +static void dm_test_ism_sso_delay_4k60_1frame(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + uint64_t expected_one_frame_ns; + + /* + * 4K@60Hz: v_total=2250, h_total=4400, pix_clk=594MHz + * pix_clk_100hz = 5940000 + */ + stream->timing.v_total = 2250; + stream->timing.h_total = 4400; + stream->timing.pix_clk_100hz = 5940000; + ism->config.sso_num_frames = 1; + + expected_one_frame_ns = div64_u64((uint64_t)2250 * 4400 * 10000000ULL, + 5940000); + + KUNIT_EXPECT_EQ(test, dm_ism_get_sso_delay(ism, stream), + expected_one_frame_ns); +} + +/* ===== Tests for dm_ism_get_idle_allow_delay ===== */ + +static void dm_test_ism_idle_delay_null_stream(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + + ism->config.filter_num_frames = 5; + ism->config.filter_entry_count = 3; + ism->config.activation_num_delay_frames = 10; + + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, NULL), + (uint64_t)0); +} + +static void dm_test_ism_idle_delay_zero_filter_frames(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + + stream->timing.v_total = 1125; + stream->timing.h_total = 2200; + stream->timing.pix_clk_100hz = 1485000; + ism->config.filter_num_frames = 0; + + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), + (uint64_t)0); +} + +static void dm_test_ism_idle_delay_zero_entry_count(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + + stream->timing.v_total = 1125; + stream->timing.h_total = 2200; + stream->timing.pix_clk_100hz = 1485000; + ism->config.filter_num_frames = 5; + ism->config.filter_entry_count = 0; + + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), + (uint64_t)0); +} + +static void dm_test_ism_idle_delay_zero_delay_frames(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + + stream->timing.v_total = 1125; + stream->timing.h_total = 2200; + stream->timing.pix_clk_100hz = 1485000; + ism->config.filter_num_frames = 5; + ism->config.filter_entry_count = 3; + ism->config.activation_num_delay_frames = 0; + + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), + (uint64_t)0); +} + +static void dm_test_ism_idle_delay_no_short_idles(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + uint64_t one_frame_ns; + + /* + * All history records have long durations (well above the + * short_idle_ns threshold), so no delay should be applied. + */ + 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); + + ism->config.filter_num_frames = 5; + ism->config.filter_entry_count = 3; + ism->config.activation_num_delay_frames = 10; + ism->config.filter_history_size = 8; + ism->config.filter_old_history_threshold = 0; + + /* Fill history with long idle durations */ + for (int i = 0; i < 8; i++) { + ism->records[i].duration_ns = one_frame_ns * 100; + ism->records[i].timestamp_ns = 0; + } + ism->next_record_idx = 8; + + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), + (uint64_t)0); +} + +static void dm_test_ism_idle_delay_enough_short_idles(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + uint64_t one_frame_ns, expected; + + /* + * Fill history with short idle durations that meet the threshold. + * filter_entry_count=3, so 3 short idles should trigger the delay. + */ + 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); + + ism->config.filter_num_frames = 5; + ism->config.filter_entry_count = 3; + ism->config.activation_num_delay_frames = 10; + ism->config.filter_history_size = 8; + ism->config.filter_old_history_threshold = 0; + + /* Fill history with short idle durations (1 frame each) */ + 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; + + expected = 10 * one_frame_ns; + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), + expected); +} + +static void dm_test_ism_idle_delay_wraps_around_buffer(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + uint64_t one_frame_ns, expected; + + /* + * Test the circular buffer wraparound: next_record_idx at 2 means + * the most recent records are at indices 1, 0, 15, 14, ... + */ + 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); + + ism->config.filter_num_frames = 5; + ism->config.filter_entry_count = 3; + ism->config.activation_num_delay_frames = 10; + ism->config.filter_history_size = 8; + ism->config.filter_old_history_threshold = 0; + + /* Fill entire buffer with short idles */ + for (int i = 0; i < AMDGPU_DM_IDLE_HIST_LEN; i++) { + ism->records[i].duration_ns = one_frame_ns; + ism->records[i].timestamp_ns = 0; + } + /* Position next_record_idx at 2 to test wraparound */ + ism->next_record_idx = 2; + + expected = 10 * one_frame_ns; + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), + expected); +} + +static void dm_test_ism_idle_delay_old_history_cutoff(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + uint64_t one_frame_ns; + + /* + * Test old_history_threshold: only recent entries within the + * threshold should be counted. Set up 2 recent short idles but + * require 3 — older entries are outside the threshold. + */ + 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); + + ism->config.filter_num_frames = 5; + ism->config.filter_entry_count = 3; + ism->config.activation_num_delay_frames = 10; + ism->config.filter_history_size = 8; + /* Threshold: entries older than 20 frames are ignored */ + ism->config.filter_old_history_threshold = 20; + + ism->last_idle_timestamp_ns = one_frame_ns * 100; + + /* 2 recent short idles (within threshold) */ + ism->records[6].duration_ns = one_frame_ns; + ism->records[6].timestamp_ns = one_frame_ns * 95; + ism->records[7].duration_ns = one_frame_ns; + ism->records[7].timestamp_ns = one_frame_ns * 98; + + /* Older entries outside the threshold with long durations */ + for (int i = 0; i < 6; i++) { + ism->records[i].duration_ns = one_frame_ns * 100; + ism->records[i].timestamp_ns = one_frame_ns * 10; + } + ism->next_record_idx = 8; + + /* + * Only 2 short idles within threshold, but 3 required — + * should return 0 (no delay). + */ + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), + (uint64_t)0); +} + +static void dm_test_ism_idle_delay_mixed_durations(struct kunit *test) +{ + struct amdgpu_dm_ism *ism = alloc_test_ism(test); + struct dc_stream_state *stream = alloc_test_stream(test); + uint64_t one_frame_ns; + + /* + * Mix of short and long idle durations. Only 2 short idles + * in 8 entries, but filter_entry_count=3, so no delay. + */ + 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); + + ism->config.filter_num_frames = 5; + ism->config.filter_entry_count = 3; + ism->config.activation_num_delay_frames = 10; + ism->config.filter_history_size = 8; + ism->config.filter_old_history_threshold = 0; + + /* 2 short idles, 6 long idles */ + for (int i = 0; i < 8; i++) { + if (i == 6 || i == 7) + ism->records[i].duration_ns = one_frame_ns; + else + ism->records[i].duration_ns = one_frame_ns * 100; + ism->records[i].timestamp_ns = 0; + } + ism->next_record_idx = 8; + + KUNIT_EXPECT_EQ(test, dm_ism_get_idle_allow_delay(ism, stream), + (uint64_t)0); +} + +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), + KUNIT_CASE(dm_test_ism_next_state_running_begin_cursor), + KUNIT_CASE(dm_test_ism_next_state_running_invalid), + /* dm_ism_next_state — FULL_POWER_BUSY */ + KUNIT_CASE(dm_test_ism_next_state_busy_enter_idle), + KUNIT_CASE(dm_test_ism_next_state_busy_end_cursor), + /* dm_ism_next_state — HYSTERESIS_WAITING */ + KUNIT_CASE(dm_test_ism_next_state_hyst_wait_exit_idle), + KUNIT_CASE(dm_test_ism_next_state_hyst_wait_begin_cursor), + KUNIT_CASE(dm_test_ism_next_state_hyst_wait_timer), + KUNIT_CASE(dm_test_ism_next_state_hyst_wait_immediate), + /* dm_ism_next_state — HYSTERESIS_BUSY */ + KUNIT_CASE(dm_test_ism_next_state_hyst_busy_exit_idle), + KUNIT_CASE(dm_test_ism_next_state_hyst_busy_end_cursor), + /* dm_ism_next_state — OPTIMIZED_IDLE */ + KUNIT_CASE(dm_test_ism_next_state_opt_idle_exit), + KUNIT_CASE(dm_test_ism_next_state_opt_idle_begin_cursor), + KUNIT_CASE(dm_test_ism_next_state_opt_idle_sso_timer), + KUNIT_CASE(dm_test_ism_next_state_opt_idle_immediate), + /* dm_ism_next_state — OPTIMIZED_IDLE_SSO */ + KUNIT_CASE(dm_test_ism_next_state_opt_idle_sso_exit), + KUNIT_CASE(dm_test_ism_next_state_opt_idle_sso_cursor), + /* dm_ism_next_state — TIMER_ABORTED */ + KUNIT_CASE(dm_test_ism_next_state_aborted_immediate), + KUNIT_CASE(dm_test_ism_next_state_aborted_invalid), + /* dm_ism_get_sso_delay */ + KUNIT_CASE(dm_test_ism_sso_delay_null_stream), + KUNIT_CASE(dm_test_ism_sso_delay_zero_frames), + KUNIT_CASE(dm_test_ism_sso_delay_1080p60_3frames), + KUNIT_CASE(dm_test_ism_sso_delay_4k60_1frame), + /* dm_ism_get_idle_allow_delay */ + KUNIT_CASE(dm_test_ism_idle_delay_null_stream), + KUNIT_CASE(dm_test_ism_idle_delay_zero_filter_frames), + KUNIT_CASE(dm_test_ism_idle_delay_zero_entry_count), + KUNIT_CASE(dm_test_ism_idle_delay_zero_delay_frames), + KUNIT_CASE(dm_test_ism_idle_delay_no_short_idles), + KUNIT_CASE(dm_test_ism_idle_delay_enough_short_idles), + KUNIT_CASE(dm_test_ism_idle_delay_wraps_around_buffer), + KUNIT_CASE(dm_test_ism_idle_delay_old_history_cutoff), + KUNIT_CASE(dm_test_ism_idle_delay_mixed_durations), + {} +}; + +static struct kunit_suite dm_ism_test_suite = { + .name = "amdgpu_dm_ism", + .test_cases = dm_ism_test_cases, +}; + +kunit_test_suite(dm_ism_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_ism"); +MODULE_AUTHOR("AMD"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c new file mode 100644 index 0000000000000..61a4167898cbd --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_psr_test.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_psr.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include "dc.h" + +/* Extern declaration for the function under test */ +extern void amdgpu_dm_psr_fill_caps(struct dc_link *link, + struct psr_caps *caps); + +/* + * Helper: allocate and zero-initialise a dc_link sufficient for + * amdgpu_dm_psr_fill_caps() testing. The function only accesses + * embedded members (dpcd_caps, psr_settings) so no pointer fields + * need to be wired up. + */ +static struct dc_link *alloc_test_link(struct kunit *test) +{ + struct dc_link *link; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + return link; +} + +/* Tests for amdgpu_dm_psr_fill_caps() — PSR version mapping */ + +static void dm_test_psr_fill_caps_version_1(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + link->psr_settings.psr_version = DC_PSR_VERSION_1; + + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_EQ(test, (int)caps.psr_version, 1); +} + +static void dm_test_psr_fill_caps_version_su1(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + link->psr_settings.psr_version = DC_PSR_VERSION_SU_1; + + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_EQ(test, (int)caps.psr_version, 2); +} + +static void dm_test_psr_fill_caps_version_unsupported(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED; + + amdgpu_dm_psr_fill_caps(link, &caps); + + /* + * Neither DC_PSR_VERSION_1 nor DC_PSR_VERSION_SU_1, + * so psr_version stays at its zero-initialised value. + */ + KUNIT_EXPECT_EQ(test, (int)caps.psr_version, 0); +} + +/* Tests for amdgpu_dm_psr_fill_caps() — RFB setup time */ + +static void dm_test_psr_fill_caps_setup_time_zero(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + /* PSR_SETUP_TIME = 0 → (6 - 0) * 55 = 330 */ + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME = 0; + + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_EQ(test, caps.psr_rfb_setup_time, 330U); +} + +static void dm_test_psr_fill_caps_setup_time_mid(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + /* PSR_SETUP_TIME = 3 → (6 - 3) * 55 = 165 */ + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME = 3; + + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_EQ(test, caps.psr_rfb_setup_time, 165U); +} + +static void dm_test_psr_fill_caps_setup_time_max(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + /* PSR_SETUP_TIME = 6 → (6 - 6) * 55 = 0 */ + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.PSR_SETUP_TIME = 6; + + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_EQ(test, caps.psr_rfb_setup_time, 0U); +} + +/* Tests for amdgpu_dm_psr_fill_caps() — link training flag */ + +static void dm_test_psr_fill_caps_link_training_required(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED = 0; + + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_TRUE(test, caps.psr_exit_link_training_required); +} + +static void dm_test_psr_fill_caps_link_training_not_required(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.LINK_TRAINING_ON_EXIT_NOT_REQUIRED = 1; + + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_FALSE(test, caps.psr_exit_link_training_required); +} + +/* Tests for amdgpu_dm_psr_fill_caps() — DPCD field passthrough */ + +static void dm_test_psr_fill_caps_dpcd_fields(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + + link->dpcd_caps.edp_rev = 0x14; + link->dpcd_caps.psr_info.psr_version = 2; + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED = 1; + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.Y_COORDINATE_REQUIRED = 1; + link->dpcd_caps.psr_info.psr2_su_y_granularity_cap = 4; + link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 1; + link->dpcd_caps.alpm_caps.bits.PM_STATE_2A_SUPPORT = 1; + + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_EQ(test, (int)caps.edp_revision, 0x14); + KUNIT_EXPECT_EQ(test, (int)caps.support_ver, 2); + KUNIT_EXPECT_TRUE(test, caps.su_granularity_required); + KUNIT_EXPECT_TRUE(test, caps.y_coordinate_required); + KUNIT_EXPECT_EQ(test, (int)caps.su_y_granularity, 4); + KUNIT_EXPECT_TRUE(test, caps.alpm_cap); + KUNIT_EXPECT_TRUE(test, caps.standby_support); +} + +static void dm_test_psr_fill_caps_dpcd_fields_unset(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0xFF, sizeof(caps)); + + /* All dpcd_caps fields are zero from kzalloc */ + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_EQ(test, (int)caps.edp_revision, 0); + KUNIT_EXPECT_EQ(test, (int)caps.support_ver, 0); + KUNIT_EXPECT_FALSE(test, caps.su_granularity_required); + KUNIT_EXPECT_FALSE(test, caps.y_coordinate_required); + KUNIT_EXPECT_EQ(test, (int)caps.su_y_granularity, 0); + KUNIT_EXPECT_FALSE(test, caps.alpm_cap); + KUNIT_EXPECT_FALSE(test, caps.standby_support); +} + +/* Tests for amdgpu_dm_psr_fill_caps() — rate control and power opts */ + +static void dm_test_psr_fill_caps_rate_control_always_zero(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + /* Pre-fill caps with non-zero to verify overwrite */ + memset(&caps, 0xFF, sizeof(caps)); + + amdgpu_dm_psr_fill_caps(link, &caps); + + KUNIT_EXPECT_EQ(test, (int)caps.rate_control_caps, 0); +} + +static void dm_test_psr_fill_caps_power_opts_z10_always_set(struct kunit *test) +{ + struct dc_link *link = alloc_test_link(test); + struct psr_caps caps; + + memset(&caps, 0, sizeof(caps)); + + amdgpu_dm_psr_fill_caps(link, &caps); + + /* + * psr_power_opt_z10_static_screen is always added to power_opts + * regardless of amdgpu_dc_feature_mask. + */ + KUNIT_EXPECT_TRUE(test, + (caps.psr_power_opt_flag & + psr_power_opt_z10_static_screen) != 0); +} +/* End of tests for amdgpu_dm_psr_fill_caps() */ + +static struct kunit_case dm_psr_test_cases[] = { + KUNIT_CASE(dm_test_psr_fill_caps_version_1), + KUNIT_CASE(dm_test_psr_fill_caps_version_su1), + KUNIT_CASE(dm_test_psr_fill_caps_version_unsupported), + KUNIT_CASE(dm_test_psr_fill_caps_setup_time_zero), + KUNIT_CASE(dm_test_psr_fill_caps_setup_time_mid), + KUNIT_CASE(dm_test_psr_fill_caps_setup_time_max), + KUNIT_CASE(dm_test_psr_fill_caps_link_training_required), + KUNIT_CASE(dm_test_psr_fill_caps_link_training_not_required), + KUNIT_CASE(dm_test_psr_fill_caps_dpcd_fields), + KUNIT_CASE(dm_test_psr_fill_caps_dpcd_fields_unset), + KUNIT_CASE(dm_test_psr_fill_caps_rate_control_always_zero), + KUNIT_CASE(dm_test_psr_fill_caps_power_opts_z10_always_set), + {} +}; + +static struct kunit_suite dm_psr_test_suite = { + .name = "amdgpu_dm_psr", + .test_cases = dm_psr_test_cases, +}; + +kunit_test_suite(dm_psr_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_psr"); +MODULE_AUTHOR("AMD"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c new file mode 100644 index 0000000000000..28ff8bbcc0f75 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_replay_test.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_replay.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include "dc.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" + +/* Extern declaration for the function under test */ +extern bool amdgpu_dm_link_supports_replay(struct dc_link *link, + struct amdgpu_dm_connector *aconnector); + +/* + * Helper: allocate a dc_link, amdgpu_dm_connector, and dm_connector_state + * wired up so that to_dm_connector_state(aconnector->base.state) works. + */ +struct replay_test_ctx { + struct dc_link *link; + struct amdgpu_dm_connector *aconnector; + struct dm_connector_state *dm_state; +}; + +static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test) +{ + struct replay_test_ctx *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + ctx->link = kunit_kzalloc(test, sizeof(*ctx->link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->link); + + ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); + + ctx->dm_state = kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state); + + /* Wire connector state so to_dm_connector_state() works */ + ctx->aconnector->base.state = &ctx->dm_state->base; + + return ctx; +} + +/* + * Helper: set all conditions for replay support to pass so individual + * tests can disable one condition at a time. + */ +static void set_all_replay_caps(struct replay_test_ctx *ctx) +{ + ctx->dm_state->freesync_capable = true; + ctx->aconnector->vsdb_info.replay_mode = true; + ctx->link->dpcd_caps.edp_rev = EDP_REVISION_13; + ctx->link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 1; + ctx->link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1; + ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 1; + ctx->link->dpcd_caps.pr_info.max_deviation_line = 1; +} + +/* Tests for amdgpu_dm_link_supports_replay() — all caps met */ + +static void dm_test_replay_supports_all_caps(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector)); +} + +/* Tests for amdgpu_dm_link_supports_replay() — freesync not capable */ + +static void dm_test_replay_no_freesync(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->dm_state->freesync_capable = false; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector)); +} + +/* Tests for amdgpu_dm_link_supports_replay() — no replay mode in VSDB */ + +static void dm_test_replay_no_vsdb_replay_mode(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->aconnector->vsdb_info.replay_mode = false; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector)); +} + +/* Tests for amdgpu_dm_link_supports_replay() — eDP revision too low */ + +static void dm_test_replay_edp_rev_too_low(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->dpcd_caps.edp_rev = EDP_REVISION_12; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector)); +} + +/* Tests for amdgpu_dm_link_supports_replay() — no ALPM AUX wake cap */ + +static void dm_test_replay_no_alpm_aux_wake(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 0; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector)); +} + +/* Tests for amdgpu_dm_link_supports_replay() — no adaptive sync SDP */ + +static void dm_test_replay_no_adaptive_sync_sdp(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 0; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector)); +} + +/* Tests for amdgpu_dm_link_supports_replay() — zero pixel deviation */ + +static void dm_test_replay_zero_pixel_deviation(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 0; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector)); +} + +/* Tests for amdgpu_dm_link_supports_replay() — zero max deviation line */ + +static void dm_test_replay_zero_max_deviation_line(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->dpcd_caps.pr_info.max_deviation_line = 0; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector)); +} + +/* Tests for amdgpu_dm_link_supports_replay() — both deviation fields zero */ + +static void dm_test_replay_both_deviations_zero(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 0; + ctx->link->dpcd_caps.pr_info.max_deviation_line = 0; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_link_supports_replay(ctx->link, ctx->aconnector)); +} + +/* End of tests for amdgpu_dm_link_supports_replay() */ + +static struct kunit_case dm_replay_test_cases[] = { + KUNIT_CASE(dm_test_replay_supports_all_caps), + KUNIT_CASE(dm_test_replay_no_freesync), + KUNIT_CASE(dm_test_replay_no_vsdb_replay_mode), + KUNIT_CASE(dm_test_replay_edp_rev_too_low), + KUNIT_CASE(dm_test_replay_no_alpm_aux_wake), + KUNIT_CASE(dm_test_replay_no_adaptive_sync_sdp), + KUNIT_CASE(dm_test_replay_zero_pixel_deviation), + KUNIT_CASE(dm_test_replay_zero_max_deviation_line), + KUNIT_CASE(dm_test_replay_both_deviations_zero), + {} +}; + +static struct kunit_suite dm_replay_test_suite = { + .name = "amdgpu_dm_replay", + .test_cases = dm_replay_test_cases, +}; + +kunit_test_suite(dm_replay_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_replay"); +MODULE_AUTHOR("AMD"); diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c index 30567ef74e5c8..91df37c4c8da3 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser.c @@ -2419,15 +2419,6 @@ static enum bp_result get_integrated_info_v8( info->dentist_vco_freq = le32_to_cpu(info_v8->ulDentistVCOFreq) * 10; info->boot_up_uma_clock = le32_to_cpu(info_v8->ulBootUpUMAClock) * 10; - for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { - /* Convert [10KHz] into [KHz] */ - info->disp_clk_voltage[i].max_supported_clk = - le32_to_cpu(info_v8->sDISPCLK_Voltage[i]. - ulMaximumSupportedCLK) * 10; - info->disp_clk_voltage[i].voltage_index = - le32_to_cpu(info_v8->sDISPCLK_Voltage[i].ulVoltageIndex); - } - info->boot_up_req_display_vector = le32_to_cpu(info_v8->ulBootUpReqDisplayVector); info->gpu_cap_info = @@ -2570,14 +2561,6 @@ static enum bp_result get_integrated_info_v9( info->dentist_vco_freq = le32_to_cpu(info_v9->ulDentistVCOFreq) * 10; info->boot_up_uma_clock = le32_to_cpu(info_v9->ulBootUpUMAClock) * 10; - for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { - /* Convert [10KHz] into [KHz] */ - info->disp_clk_voltage[i].max_supported_clk = - le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulMaximumSupportedCLK) * 10; - info->disp_clk_voltage[i].voltage_index = - le32_to_cpu(info_v9->sDISPCLK_Voltage[i].ulVoltageIndex); - } - info->boot_up_req_display_vector = le32_to_cpu(info_v9->ulBootUpReqDisplayVector); info->gpu_cap_info = le32_to_cpu(info_v9->ulGPUCapInfo); @@ -2719,25 +2702,6 @@ static enum bp_result construct_integrated_info( } } - /* Sort voltage table from low to high*/ - if (result == BP_RESULT_OK) { - int32_t i; - int32_t j; - - for (i = 1; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { - for (j = i; j > 0; --j) { - if ( - info->disp_clk_voltage[j].max_supported_clk < - info->disp_clk_voltage[j-1].max_supported_clk) { - /* swap j and j - 1*/ - swap(info->disp_clk_voltage[j - 1], - info->disp_clk_voltage[j]); - } - } - } - - } - return result; } diff --git a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c index ee6d52b87b029..06bab03a8579f 100644 --- a/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c +++ b/drivers/gpu/drm/amd/display/dc/bios/bios_parser2.c @@ -2671,15 +2671,6 @@ static enum bp_result get_integrated_info_v11( info->dentist_vco_freq = le32_to_cpu(info_v11->ulDentistVCOFreq) * 10; info->boot_up_uma_clock = le32_to_cpu(info_v8->ulBootUpUMAClock) * 10; - for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { - /* Convert [10KHz] into [KHz] */ - info->disp_clk_voltage[i].max_supported_clk = - le32_to_cpu(info_v11->sDISPCLK_Voltage[i]. - ulMaximumSupportedCLK) * 10; - info->disp_clk_voltage[i].voltage_index = - le32_to_cpu(info_v11->sDISPCLK_Voltage[i].ulVoltageIndex); - } - info->boot_up_req_display_vector = le32_to_cpu(info_v11->ulBootUpReqDisplayVector); info->boot_up_nb_voltage = @@ -3032,7 +3023,6 @@ static enum bp_result construct_integrated_info( struct atom_data_revision revision; int32_t i; - int32_t j; if (!info) return result; @@ -3134,14 +3124,6 @@ static enum bp_result construct_integrated_info( DC_LOG_BIOS("driver forced fixdpvoltageswing = %d\n", info->ext_disp_conn_info.fixdpvoltageswing); } } - /* Sort voltage table from low to high*/ - for (i = 1; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { - for (j = i; j > 0; --j) { - if (info->disp_clk_voltage[j].max_supported_clk < - info->disp_clk_voltage[j-1].max_supported_clk) - swap(info->disp_clk_voltage[j-1], info->disp_clk_voltage[j]); - } - } return result; } diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c index 808e24f0e88fd..d891b3bfe2a1f 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.c @@ -62,32 +62,6 @@ static const struct clk_mgr_mask disp_clk_mask = { CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) }; -/* Max clock values for each state indexed by "enum clocks_state": */ -static const struct state_dependent_clocks dce60_max_clks_by_state[] = { -/* ClocksStateInvalid - should not be used */ -{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, -/* ClocksStateUltraLow - not expected to be used for DCE 6.0 */ -{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, -/* ClocksStateLow */ -{ .display_clk_khz = 352000, .pixel_clk_khz = 330000}, -/* ClocksStateNominal */ -{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 }, -/* ClocksStatePerformance */ -{ .display_clk_khz = 600000, .pixel_clk_khz = 400000 } }; - -/* Max clock values for each state indexed by "enum clocks_state": */ -static const struct state_dependent_clocks dce80_max_clks_by_state[] = { -/* ClocksStateInvalid - should not be used */ -{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, -/* ClocksStateUltraLow - not expected to be used for DCE 8.0 */ -{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, -/* ClocksStateLow */ -{ .display_clk_khz = 352000, .pixel_clk_khz = 330000}, -/* ClocksStateNominal */ -{ .display_clk_khz = 625000, .pixel_clk_khz = 400000 }, -/* ClocksStatePerformance */ -{ .display_clk_khz = 625000, .pixel_clk_khz = 400000 } }; - unsigned int dentist_get_divider_from_did(unsigned int did) { if (did < DENTIST_BASE_DID_1) @@ -220,40 +194,6 @@ uint32_t dce_get_max_pixel_clock_for_all_paths(struct dc_state *context) return max_pix_clk; } -enum dm_pp_clocks_state dce_get_required_clocks_state( - struct clk_mgr *clk_mgr_base, - struct dc_state *context) -{ - struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base); - int i; - enum dm_pp_clocks_state low_req_clk; - int max_pix_clk = dce_get_max_pixel_clock_for_all_paths(context); - - /* Iterate from highest supported to lowest valid state, and update - * lowest RequiredState with the lowest state that satisfies - * all required clocks - */ - for (i = clk_mgr_dce->max_clks_state; i >= DM_PP_CLOCKS_STATE_ULTRA_LOW; i--) - if (context->bw_ctx.bw.dce.dispclk_khz > - clk_mgr_dce->max_clks_by_state[i].display_clk_khz - || max_pix_clk > - clk_mgr_dce->max_clks_by_state[i].pixel_clk_khz) - break; - - low_req_clk = i + 1; - if (low_req_clk > clk_mgr_dce->max_clks_state) { - /* set max clock state for high phyclock, invalid on exceeding display clock */ - if (clk_mgr_dce->max_clks_by_state[clk_mgr_dce->max_clks_state].display_clk_khz - < context->bw_ctx.bw.dce.dispclk_khz) - low_req_clk = DM_PP_CLOCKS_STATE_INVALID; - else - low_req_clk = clk_mgr_dce->max_clks_state; - } - - return low_req_clk; -} - - /* TODO: remove use the two broken down functions */ int dce_set_clock( struct clk_mgr *clk_mgr_base, @@ -291,11 +231,6 @@ int dce_set_clock( actual_clock = pxl_clk_params.dfs_bypass_display_clock; } - /* from power down, we need mark the clock state as ClocksStateNominal - * from HWReset, so when resume we will call pplib voltage regulator.*/ - if (requested_clk_khz == 0) - clk_mgr_dce->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; - if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu)) dmcu->funcs->set_psr_wait_loop(dmcu, actual_clock / 1000 / 7); @@ -307,7 +242,6 @@ static void dce_clock_read_integrated_info(struct clk_mgr_internal *clk_mgr_dce) { struct dc_debug_options *debug = &clk_mgr_dce->base.ctx->dc->debug; struct dc_bios *bp = clk_mgr_dce->base.ctx->dc_bios; - int i; if (bp->integrated_info) clk_mgr_dce->base.dentist_vco_freq_khz = bp->integrated_info->dentist_vco_freq; @@ -317,40 +251,6 @@ static void dce_clock_read_integrated_info(struct clk_mgr_internal *clk_mgr_dce) clk_mgr_dce->base.dentist_vco_freq_khz = 3600000; } - /*update the maximum display clock for each power state*/ - for (i = 0; i < NUMBER_OF_DISP_CLK_VOLTAGE; ++i) { - enum dm_pp_clocks_state clk_state = DM_PP_CLOCKS_STATE_INVALID; - - switch (i) { - case 0: - clk_state = DM_PP_CLOCKS_STATE_ULTRA_LOW; - break; - - case 1: - clk_state = DM_PP_CLOCKS_STATE_LOW; - break; - - case 2: - clk_state = DM_PP_CLOCKS_STATE_NOMINAL; - break; - - case 3: - clk_state = DM_PP_CLOCKS_STATE_PERFORMANCE; - break; - - default: - clk_state = DM_PP_CLOCKS_STATE_INVALID; - break; - } - - /*Do not allow bad VBIOS/SBIOS to override with invalid values, - * check for > 100MHz*/ - if (bp->integrated_info) - if (bp->integrated_info->disp_clk_voltage[i].max_supported_clk >= 100000) - clk_mgr_dce->max_clks_by_state[clk_state].display_clk_khz = - bp->integrated_info->disp_clk_voltage[i].max_supported_clk; - } - if (!debug->disable_dfs_bypass && bp->integrated_info) if (bp->integrated_info->gpu_cap_info & DFS_BYPASS_ENABLE) clk_mgr_dce->dfs_bypass_enabled = true; @@ -430,20 +330,9 @@ static void dce_update_clocks(struct clk_mgr *clk_mgr_base, struct dc_state *context, bool safe_to_lower) { - struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base); - struct dm_pp_power_level_change_request level_change_req; - const int max_disp_clk = - clk_mgr_dce->max_clks_by_state[DM_PP_CLOCKS_STATE_PERFORMANCE].display_clk_khz; + const int max_disp_clk = clk_mgr_base->clks.max_supported_dispclk_khz; int patched_disp_clk = MIN(max_disp_clk, context->bw_ctx.bw.dce.dispclk_khz); - level_change_req.power_level = dce_get_required_clocks_state(clk_mgr_base, context); - /* get max clock state from PPLIB */ - if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower) - || level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) { - if (dm_pp_apply_power_level_change_request(clk_mgr_base->ctx, &level_change_req)) - clk_mgr_dce->cur_min_clks_state = level_change_req.power_level; - } - if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr_base->clks.dispclk_khz)) { patched_disp_clk = dce_set_clock(clk_mgr_base, patched_disp_clk); clk_mgr_base->clks.dispclk_khz = patched_disp_clk; @@ -468,17 +357,6 @@ void dce_clk_mgr_construct( struct clk_mgr_internal *clk_mgr) { struct clk_mgr *base = &clk_mgr->base; - struct dm_pp_static_clock_info static_clk_info = {0}; - - if (ctx->dce_version <= DCE_VERSION_6_4) - memcpy(clk_mgr->max_clks_by_state, - dce60_max_clks_by_state, - sizeof(dce60_max_clks_by_state)); - else - memcpy(clk_mgr->max_clks_by_state, - dce80_max_clks_by_state, - sizeof(dce80_max_clks_by_state)); - base->ctx = ctx; base->funcs = &dce_funcs; @@ -494,16 +372,16 @@ void dce_clk_mgr_construct( clk_mgr->dprefclk_ss_divider = 1000; clk_mgr->ss_on_dprefclk = false; - if (ctx->dce_version >= DCE_VERSION_8_0) { - if (dm_pp_get_static_clocks(ctx, &static_clk_info)) - clk_mgr->max_clks_state = static_clk_info.max_clocks_state; - else - clk_mgr->max_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; - clk_mgr->cur_min_clks_state = DM_PP_CLOCKS_STATE_INVALID; - } - - base->clks.max_supported_dispclk_khz = - clk_mgr->max_clks_by_state[DM_PP_CLOCKS_STATE_PERFORMANCE].display_clk_khz; + if (ctx->dce_version >= DCE_VERSION_12_0) + base->clks.max_supported_dispclk_khz = 1133000; + else if (ctx->dce_version >= DCE_VERSION_11_2) + base->clks.max_supported_dispclk_khz = 1132000; + else if (ctx->dce_version >= DCE_VERSION_11_0) + base->clks.max_supported_dispclk_khz = 643000; + else if (ctx->dce_version >= DCE_VERSION_8_0) + base->clks.max_supported_dispclk_khz = 625000; + else + base->clks.max_supported_dispclk_khz = 600000; dce_clock_read_integrated_info(clk_mgr); dce_clock_read_ss_info(clk_mgr); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.h index 9ea1b0a9923da..e1c6a92d7957f 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce100/dce_clk_mgr.h @@ -32,9 +32,6 @@ /* functions shared by other dce clk mgrs */ int dce_adjust_dp_ref_freq_for_ss(struct clk_mgr_internal *clk_mgr_dce, int dp_ref_clk_khz); int dce_get_dp_ref_freq_khz(struct clk_mgr *clk_mgr_base); -enum dm_pp_clocks_state dce_get_required_clocks_state( - struct clk_mgr *clk_mgr_base, - struct dc_state *context); uint32_t dce_get_max_pixel_clock_for_all_paths(struct dc_state *context); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c index b35a44976477a..d52789ba26137 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce110/dce110_clk_mgr.c @@ -51,18 +51,6 @@ static const struct clk_mgr_mask disp_clk_mask = { CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) }; -static const struct state_dependent_clocks dce110_max_clks_by_state[] = { -/*ClocksStateInvalid - should not be used*/ -{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, -/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ -{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, -/*ClocksStateLow*/ -{ .display_clk_khz = 352000, .pixel_clk_khz = 330000 }, -/*ClocksStateNominal*/ -{ .display_clk_khz = 467000, .pixel_clk_khz = 400000 }, -/*ClocksStatePerformance*/ -{ .display_clk_khz = 643000, .pixel_clk_khz = 400000 } }; - static uint32_t determine_sclk_from_bounding_box( const struct dc *dc, uint32_t required_sclk) @@ -257,21 +245,12 @@ static void dce11_update_clocks(struct clk_mgr *clk_mgr_base, bool safe_to_lower) { struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base); - struct dm_pp_power_level_change_request level_change_req; int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz; /*TODO: W/A for dal3 linux, investigate why this works */ if (!clk_mgr_dce->dfs_bypass_active) patched_disp_clk = patched_disp_clk * 115 / 100; - level_change_req.power_level = dce_get_required_clocks_state(clk_mgr_base, context); - /* get max clock state from PPLIB */ - if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower) - || level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) { - if (dm_pp_apply_power_level_change_request(clk_mgr_base->ctx, &level_change_req)) - clk_mgr_dce->cur_min_clks_state = level_change_req.power_level; - } - if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr_base->clks.dispclk_khz)) { context->bw_ctx.bw.dce.dispclk_khz = dce_set_clock(clk_mgr_base, patched_disp_clk); clk_mgr_base->clks.dispclk_khz = patched_disp_clk; @@ -290,10 +269,6 @@ void dce110_clk_mgr_construct( { dce_clk_mgr_construct(ctx, clk_mgr); - memcpy(clk_mgr->max_clks_by_state, - dce110_max_clks_by_state, - sizeof(dce110_max_clks_by_state)); - clk_mgr->regs = &disp_clk_regs; clk_mgr->clk_mgr_shift = &disp_clk_shift; clk_mgr->clk_mgr_mask = &disp_clk_mask; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce112/dce112_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce112/dce112_clk_mgr.c index 1f36ad8a7de46..08ed6f88025fa 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce112/dce112_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce112/dce112_clk_mgr.c @@ -53,19 +53,6 @@ static const struct clk_mgr_mask disp_clk_mask = { CLK_COMMON_MASK_SH_LIST_DCE_COMMON_BASE(_MASK) }; -static const struct state_dependent_clocks dce112_max_clks_by_state[] = { -/*ClocksStateInvalid - should not be used*/ -{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, -/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ -{ .display_clk_khz = 389189, .pixel_clk_khz = 346672 }, -/*ClocksStateLow*/ -{ .display_clk_khz = 459000, .pixel_clk_khz = 400000 }, -/*ClocksStateNominal*/ -{ .display_clk_khz = 667000, .pixel_clk_khz = 600000 }, -/*ClocksStatePerformance*/ -{ .display_clk_khz = 1132000, .pixel_clk_khz = 600000 } }; - - //TODO: remove use the two broken down functions int dce112_set_clock(struct clk_mgr *clk_mgr_base, int requested_clk_khz) { @@ -89,13 +76,6 @@ int dce112_set_clock(struct clk_mgr *clk_mgr_base, int requested_clk_khz) bp->funcs->set_dce_clock(bp, &dce_clk_params); actual_clock = dce_clk_params.target_clock_frequency; - /* - * from power down, we need mark the clock state as ClocksStateNominal - * from HWReset, so when resume we will call pplib voltage regulator. - */ - if (requested_clk_khz == 0) - clk_mgr_dce->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; - /*Program DP ref Clock*/ /*VBIOS will determine DPREFCLK frequency, so we don't set it*/ dce_clk_params.target_clock_frequency = 0; @@ -143,14 +123,6 @@ int dce112_set_dispclk(struct clk_mgr_internal *clk_mgr, int requested_clk_khz) bp->funcs->set_dce_clock(bp, &dce_clk_params); actual_clock = dce_clk_params.target_clock_frequency; - /* - * from power down, we need mark the clock state as ClocksStateNominal - * from HWReset, so when resume we will call pplib voltage regulator. - */ - if (requested_clk_khz == 0) - clk_mgr->cur_min_clks_state = DM_PP_CLOCKS_STATE_NOMINAL; - - if (dmcu && dmcu->funcs->is_dmcu_initialized(dmcu)) { if (clk_mgr->dfs_bypass_disp_clk != actual_clock) dmcu->funcs->set_psr_wait_loop(dmcu, @@ -193,21 +165,12 @@ static void dce112_update_clocks(struct clk_mgr *clk_mgr_base, bool safe_to_lower) { struct clk_mgr_internal *clk_mgr_dce = TO_CLK_MGR_INTERNAL(clk_mgr_base); - struct dm_pp_power_level_change_request level_change_req; int patched_disp_clk = context->bw_ctx.bw.dce.dispclk_khz; /*TODO: W/A for dal3 linux, investigate why this works */ if (!clk_mgr_dce->dfs_bypass_active) patched_disp_clk = patched_disp_clk * 115 / 100; - level_change_req.power_level = dce_get_required_clocks_state(clk_mgr_base, context); - /* get max clock state from PPLIB */ - if ((level_change_req.power_level < clk_mgr_dce->cur_min_clks_state && safe_to_lower) - || level_change_req.power_level > clk_mgr_dce->cur_min_clks_state) { - if (dm_pp_apply_power_level_change_request(clk_mgr_base->ctx, &level_change_req)) - clk_mgr_dce->cur_min_clks_state = level_change_req.power_level; - } - if (should_set_clock(safe_to_lower, patched_disp_clk, clk_mgr_base->clks.dispclk_khz)) { patched_disp_clk = dce112_set_clock(clk_mgr_base, patched_disp_clk); clk_mgr_base->clks.dispclk_khz = patched_disp_clk; @@ -226,10 +189,6 @@ void dce112_clk_mgr_construct( { dce_clk_mgr_construct(ctx, clk_mgr); - memcpy(clk_mgr->max_clks_by_state, - dce112_max_clks_by_state, - sizeof(dce112_max_clks_by_state)); - clk_mgr->regs = &disp_clk_regs; clk_mgr->clk_mgr_shift = &disp_clk_shift; clk_mgr->clk_mgr_mask = &disp_clk_mask; diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce120/dce120_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce120/dce120_clk_mgr.c index c9ba7b3fd2c32..f8ef3a4710fc2 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dce120/dce120_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dce120/dce120_clk_mgr.c @@ -32,18 +32,6 @@ #include "dce100/dce_clk_mgr.h" #include "dce120/dce120_hwseq.h" -static const struct state_dependent_clocks dce120_max_clks_by_state[] = { -/*ClocksStateInvalid - should not be used*/ -{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, -/*ClocksStateUltraLow - currently by HW design team not supposed to be used*/ -{ .display_clk_khz = 0, .pixel_clk_khz = 0 }, -/*ClocksStateLow*/ -{ .display_clk_khz = 460000, .pixel_clk_khz = 400000 }, -/*ClocksStateNominal*/ -{ .display_clk_khz = 670000, .pixel_clk_khz = 600000 }, -/*ClocksStatePerformance*/ -{ .display_clk_khz = 1133000, .pixel_clk_khz = 600000 } }; - /** * dce121_clock_patch_xgmi_ss_info() - Save XGMI spread spectrum info * @clk_mgr_dce: clock manager internal structure @@ -129,10 +117,6 @@ void dce120_clk_mgr_construct(struct dc_context *ctx, struct clk_mgr_internal *c { dce_clk_mgr_construct(ctx, clk_mgr); - memcpy(clk_mgr->max_clks_by_state, - dce120_max_clks_by_state, - sizeof(dce120_max_clks_by_state)); - clk_mgr->base.dprefclk_khz = 600000; clk_mgr->base.funcs = &dce120_funcs; } diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c index bbce751b485fd..44028ba88f809 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_debug.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_debug.c @@ -272,9 +272,9 @@ char *dc_status_to_str(enum dc_status status) return "Fail DP Tunnel BW validation"; case DC_ERROR_UNEXPECTED: return "Unexpected error"; + default: + return "Unexpected status error"; } - - return "Unexpected status error"; } char *dc_pixel_encoding_to_str(enum dc_pixel_encoding pixel_encoding) diff --git a/drivers/gpu/drm/amd/display/dc/dm_services.h b/drivers/gpu/drm/amd/display/dc/dm_services.h index fbbf9c757b3c3..8b062b011fc65 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_services.h +++ b/drivers/gpu/drm/amd/display/dc/dm_services.h @@ -224,18 +224,10 @@ bool dm_pp_apply_display_requirements( const struct dc_context *ctx, const struct dm_pp_display_configuration *pp_display_cfg); -bool dm_pp_apply_power_level_change_request( - const struct dc_context *ctx, - struct dm_pp_power_level_change_request *level_change_req); - bool dm_pp_apply_clock_for_voltage_request( const struct dc_context *ctx, struct dm_pp_clock_for_voltage_req *clock_for_voltage_req); -bool dm_pp_get_static_clocks( - const struct dc_context *ctx, - struct dm_pp_static_clock_info *static_clk_info); - /****** end of PP interfaces ******/ struct persistent_data_flag { diff --git a/drivers/gpu/drm/amd/display/dc/dm_services_types.h b/drivers/gpu/drm/amd/display/dc/dm_services_types.h index 3b093b8699abd..b3505d93503fd 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_services_types.h +++ b/drivers/gpu/drm/amd/display/dc/dm_services_types.h @@ -36,30 +36,7 @@ struct dm_pp_clock_range { int max_khz; }; -enum dm_pp_clocks_state { - DM_PP_CLOCKS_STATE_INVALID, - DM_PP_CLOCKS_STATE_ULTRA_LOW, - DM_PP_CLOCKS_STATE_LOW, - DM_PP_CLOCKS_STATE_NOMINAL, - DM_PP_CLOCKS_STATE_PERFORMANCE, - - /* Starting from DCE11, Max 8 levels of DPM state supported. */ - DM_PP_CLOCKS_DPM_STATE_LEVEL_INVALID = DM_PP_CLOCKS_STATE_INVALID, - DM_PP_CLOCKS_DPM_STATE_LEVEL_0, - DM_PP_CLOCKS_DPM_STATE_LEVEL_1, - DM_PP_CLOCKS_DPM_STATE_LEVEL_2, - /* to be backward compatible */ - DM_PP_CLOCKS_DPM_STATE_LEVEL_3, - DM_PP_CLOCKS_DPM_STATE_LEVEL_4, - DM_PP_CLOCKS_DPM_STATE_LEVEL_5, - DM_PP_CLOCKS_DPM_STATE_LEVEL_6, - DM_PP_CLOCKS_DPM_STATE_LEVEL_7, - - DM_PP_CLOCKS_MAX_STATES -}; - struct dm_pp_gpu_clock_range { - enum dm_pp_clocks_state clock_state; struct dm_pp_clock_range sclk; struct dm_pp_clock_range mclk; struct dm_pp_clock_range eclk; @@ -246,10 +223,6 @@ enum dm_acpi_display_type { AcpiDisplayType_DFP6 = 12 }; -struct dm_pp_power_level_change_request { - enum dm_pp_clocks_state power_level; -}; - struct dm_pp_clock_for_voltage_req { enum dm_pp_clock_type clk_type; uint32_t clocks_in_khz; @@ -258,9 +231,6 @@ struct dm_pp_clock_for_voltage_req { struct dm_pp_static_clock_info { uint32_t max_sclk_khz; uint32_t max_mclk_khz; - - /* max possible display block clocks state */ - enum dm_pp_clocks_state max_clocks_state; }; struct dtn_min_clk_info { diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h index c69ccfcebeb5a..450bce3d8e02c 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr_internal.h @@ -405,12 +405,6 @@ enum clock_type { clock_type_dtbclk, }; - -struct state_dependent_clocks { - int display_clk_khz; - int pixel_clk_khz; -}; - struct clk_mgr_internal { struct clk_mgr base; int smu_ver; @@ -429,8 +423,6 @@ struct clk_mgr_internal { const struct clk_mgr_shift *clk_mgr_shift; const struct clk_mgr_mask *clk_mgr_mask; - struct state_dependent_clocks max_clks_by_state[DM_PP_CLOCKS_MAX_STATES]; - /*TODO: figure out which of the below fields should be here vs in asic specific portion */ /* Cache the status of DFS-bypass feature*/ bool dfs_bypass_enabled; @@ -477,8 +469,6 @@ struct clk_mgr_internal { */ int dprefclk_ss_divider; - enum dm_pp_clocks_state max_clks_state; - enum dm_pp_clocks_state cur_min_clks_state; bool periodic_retraining_disabled; unsigned int cur_phyclk_req_table[MAX_LINKS]; diff --git a/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h b/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h index a0f03fb67605e..3e4e8d55dd0fd 100644 --- a/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h +++ b/drivers/gpu/drm/amd/display/include/grph_object_ctrl_defs.h @@ -273,7 +273,6 @@ struct transmitter_configuration { #define NUMBER_OF_UCHAR_FOR_GUID 16 #define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7 #define NUMBER_OF_CSR_M3_ARB 10 -#define NUMBER_OF_DISP_CLK_VOLTAGE 4 #define NUMBER_OF_AVAILABLE_SCLK 5 struct i2c_reg_info { @@ -302,14 +301,6 @@ struct edp_info { /* V6 */ struct integrated_info { - struct clock_voltage_caps { - /* The Voltage Index indicated by FUSE, same voltage index - shared with SCLK DPM fuse table */ - uint32_t voltage_index; - /* Maximum clock supported with specified voltage index */ - uint32_t max_supported_clk; /* in KHz */ - } disp_clk_voltage[NUMBER_OF_DISP_CLK_VOLTAGE]; - struct display_connection_info { struct external_display_path { /* A bit vector to show what devices are supported */ diff --git a/drivers/gpu/drm/amd/include/dm_pp_interface.h b/drivers/gpu/drm/amd/include/dm_pp_interface.h index 349544504c93c..e3d40fb371039 100644 --- a/drivers/gpu/drm/amd/include/dm_pp_interface.h +++ b/drivers/gpu/drm/amd/include/dm_pp_interface.h @@ -113,24 +113,6 @@ struct amd_pp_display_configuration { struct amd_pp_simple_clock_info { uint32_t engine_max_clock; uint32_t memory_max_clock; - uint32_t level; -}; - -enum PP_DAL_POWERLEVEL { - PP_DAL_POWERLEVEL_INVALID = 0, - PP_DAL_POWERLEVEL_ULTRALOW, - PP_DAL_POWERLEVEL_LOW, - PP_DAL_POWERLEVEL_NOMINAL, - PP_DAL_POWERLEVEL_PERFORMANCE, - - PP_DAL_POWERLEVEL_0 = PP_DAL_POWERLEVEL_ULTRALOW, - PP_DAL_POWERLEVEL_1 = PP_DAL_POWERLEVEL_LOW, - PP_DAL_POWERLEVEL_2 = PP_DAL_POWERLEVEL_NOMINAL, - PP_DAL_POWERLEVEL_3 = PP_DAL_POWERLEVEL_PERFORMANCE, - PP_DAL_POWERLEVEL_4 = PP_DAL_POWERLEVEL_3+1, - PP_DAL_POWERLEVEL_5 = PP_DAL_POWERLEVEL_4+1, - PP_DAL_POWERLEVEL_6 = PP_DAL_POWERLEVEL_5+1, - PP_DAL_POWERLEVEL_7 = PP_DAL_POWERLEVEL_6+1, }; struct amd_pp_clock_info { @@ -142,7 +124,6 @@ struct amd_pp_clock_info { uint32_t max_bus_bandwidth; uint32_t max_engine_clock_in_sr; uint32_t min_engine_clock_in_sr; - enum PP_DAL_POWERLEVEL max_clocks_state; }; enum amd_pp_clock_type { diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h index 1bbf531de5ed7..6371f292f2d80 100644 --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h @@ -417,7 +417,7 @@ struct amd_pm_funcs { void (*display_configuration_changed)(void *handle); void (*print_power_state)(void *handle, void *ps); bool (*vblank_too_short)(void *handle); - void (*enable_bapm)(void *handle, bool enable); + void (*notify_ac_dc)(void *handle); int (*check_state_equal)(void *handle, void *cps, void *rps, @@ -476,8 +476,6 @@ struct amd_pm_funcs { u32 (*get_mclk)(void *handle, bool low); int (*display_configuration_change)(void *handle, const struct amd_pp_display_configuration *input); - int (*get_display_power_level)(void *handle, - struct amd_pp_simple_clock_info *output); int (*get_current_clocks)(void *handle, struct amd_pp_clock_info *clocks); int (*get_clock_by_type)(void *handle, diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index feadf604b4749..f76ba67535519 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -33,8 +33,8 @@ #include <linux/power_supply.h> #include "amdgpu_smu.h" -#define amdgpu_dpm_enable_bapm(adev, e) \ - ((adev)->powerplay.pp_funcs->enable_bapm((adev)->powerplay.pp_handle, (e))) +#define amdgpu_dpm_notify_ac_dc(adev) \ + ((adev)->powerplay.pp_funcs->notify_ac_dc((adev)->powerplay.pp_handle)) #define amdgpu_dpm_is_legacy_dpm(adev) ((adev)->powerplay.pp_handle == (adev)) @@ -504,8 +504,8 @@ void amdgpu_pm_acpi_event_handler(struct amdgpu_device *adev) adev->pm.ac_power = false; if (adev->powerplay.pp_funcs && - adev->powerplay.pp_funcs->enable_bapm) - amdgpu_dpm_enable_bapm(adev, adev->pm.ac_power); + adev->powerplay.pp_funcs->notify_ac_dc) + amdgpu_dpm_notify_ac_dc(adev); if (is_support_sw_smu(adev)) smu_set_ac_dc(adev->powerplay.pp_handle); diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/kv_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/kv_dpm.c index cdf3f8e6ec2fd..b763fdf43c603 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/kv_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/kv_dpm.c @@ -1233,14 +1233,14 @@ static void kv_update_requested_ps(struct amdgpu_device *adev, adev->pm.dpm.requested_ps = &pi->requested_rps; } -static void kv_dpm_enable_bapm(void *handle, bool enable) +static void kv_dpm_enable_bapm(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; struct kv_power_info *pi = kv_get_pi(adev); int ret; if (pi->bapm_enable) { - ret = amdgpu_kv_smc_bapm_enable(adev, enable); + ret = amdgpu_kv_smc_bapm_enable(adev, adev->pm.ac_power); if (ret) drm_err(adev_to_drm(adev), "amdgpu_kv_smc_bapm_enable failed\n"); } @@ -3341,7 +3341,7 @@ static const struct amd_pm_funcs kv_dpm_funcs = { .debugfs_print_current_performance_level = &kv_dpm_debugfs_print_current_performance_level, .force_performance_level = &kv_dpm_force_performance_level, .set_powergating_by_smu = kv_set_powergating_by_smu, - .enable_bapm = &kv_dpm_enable_bapm, + .notify_ac_dc = &kv_dpm_enable_bapm, .get_vce_clock_state = amdgpu_get_vce_clock_state, .check_state_equal = kv_check_state_equal, .read_sensor = &kv_dpm_read_sensor, diff --git a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c index c3aff5d0c53dc..8079da7c53350 100644 --- a/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c +++ b/drivers/gpu/drm/amd/pm/legacy-dpm/si_dpm.c @@ -3888,16 +3888,18 @@ static void si_notify_hardware_vpu_recovery_event(struct amdgpu_device *adev) } #endif -#if 0 -static int si_notify_hw_of_powersource(struct amdgpu_device *adev, bool ac_power) +static void si_notify_hw_of_powersource(void *handle) { - if (ac_power) - return (amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC) == PPSMC_Result_OK) ? - 0 : -EINVAL; + struct amdgpu_device *adev = (struct amdgpu_device *)handle; - return 0; + /* Check if the platform already manages the AC/DC switch via dedicated GPIO. */ + if (adev->pm.dpm.platform_caps & ATOM_PP_PLATFORM_CAP_HARDWAREDC) + return; + + /* The SMU automatically notices DC, but needs to be notified when switching to AC. */ + if (adev->pm.ac_power) + amdgpu_si_send_msg_to_smc(adev, PPSMC_MSG_RunningOnAC); } -#endif static PPSMC_Result si_send_msg_to_smc_with_parameter(struct amdgpu_device *adev, PPSMC_Msg msg, u32 parameter) @@ -7240,6 +7242,7 @@ static void si_parse_pplib_clock_info(struct amdgpu_device *adev, struct evergreen_power_info *eg_pi = evergreen_get_pi(adev); struct si_power_info *si_pi = si_get_pi(adev); struct si_ps *ps = si_get_ps(rps); + struct amdgpu_clock_and_voltage_limits *limits; u16 leakage_voltage; struct rv7xx_pl *pl = &ps->performance_levels[index]; int ret; @@ -7299,12 +7302,30 @@ static void si_parse_pplib_clock_info(struct amdgpu_device *adev, si_pi->mvdd_bootup_value = mvdd; } + /* + * Update maximum allowed clock limits. + * VBIOS can contain conflicting values between: + * - the maximum allowed clocks and voltages on AC or DC + * - the clocks and voltages in power states on AC or DC + */ if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == - ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) { - adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk = pl->sclk; - adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk = pl->mclk; - adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddc = pl->vddc; - adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.vddci = pl->vddci; + ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE) + limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_ac; + else if ((rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) == + ATOM_PPLIB_CLASSIFICATION_UI_BATTERY) + limits = &adev->pm.dpm.dyn_state.max_clock_voltage_on_dc; + else + limits = NULL; + + if (limits) { + if (pl->sclk > limits->sclk) + limits->sclk = pl->sclk; + if (pl->mclk > limits->mclk) + limits->mclk = pl->mclk; + if (pl->vddc > limits->vddc) + limits->vddc = pl->vddc; + if (pl->vddci > limits->vddci) + limits->vddci = pl->vddci; } } @@ -8144,6 +8165,7 @@ static const struct amd_pm_funcs si_dpm_funcs = { .get_vce_clock_state = amdgpu_get_vce_clock_state, .read_sensor = &si_dpm_read_sensor, .pm_compute_clocks = amdgpu_legacy_dpm_compute_clocks, + .notify_ac_dc = si_notify_hw_of_powersource, }; static const struct amdgpu_irq_src_funcs si_dpm_irq_funcs = { diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c index e558b81b25c91..5700bcc7ad9ab 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c +++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c @@ -1020,21 +1020,9 @@ static int pp_display_configuration_change(void *handle, return 0; } -static int pp_get_display_power_level(void *handle, - struct amd_pp_simple_clock_info *output) -{ - struct pp_hwmgr *hwmgr = handle; - - if (!hwmgr || !hwmgr->pm_en || !output) - return -EINVAL; - - return phm_get_dal_power_level(hwmgr, output); -} - static int pp_get_current_clocks(void *handle, struct amd_pp_clock_info *clocks) { - struct amd_pp_simple_clock_info simple_clocks = { 0 }; struct pp_clock_info hw_clocks; struct pp_hwmgr *hwmgr = handle; int ret = 0; @@ -1042,8 +1030,6 @@ static int pp_get_current_clocks(void *handle, if (!hwmgr || !hwmgr->pm_en) return -EINVAL; - phm_get_dal_power_level(hwmgr, &simple_clocks); - if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_PowerContainment)) ret = phm_get_clock_info(hwmgr, &hwmgr->current_ps->hardware, @@ -1068,11 +1054,6 @@ static int pp_get_current_clocks(void *handle, clocks->max_engine_clock_in_sr = hw_clocks.max_eng_clk; clocks->min_engine_clock_in_sr = hw_clocks.min_eng_clk; - if (simple_clocks.level == 0) - clocks->max_clocks_state = PP_DAL_POWERLEVEL_7; - else - clocks->max_clocks_state = simple_clocks.level; - if (0 == phm_get_current_shallow_sleep_clocks(hwmgr, &hwmgr->current_ps->hardware, &hw_clocks)) { clocks->max_engine_clock_in_sr = hw_clocks.max_eng_clk; clocks->min_engine_clock_in_sr = hw_clocks.min_eng_clk; @@ -1149,8 +1130,6 @@ static int pp_get_display_mode_validation_clocks(void *handle, if (!hwmgr || !hwmgr->pm_en || !clocks) return -EINVAL; - clocks->level = PP_DAL_POWERLEVEL_7; - if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_DynamicPatchPowerState)) ret = phm_get_max_high_clocks(hwmgr, clocks); @@ -1550,6 +1529,17 @@ static void pp_pm_compute_clocks(void *handle) NULL); } +static void pp_dpm_notify_ac_dc(void *handle) +{ + struct pp_hwmgr *hwmgr = handle; + + if (!hwmgr || !hwmgr->pm_en) + return; + + if (hwmgr->hwmgr_func->notify_ac_dc) + hwmgr->hwmgr_func->notify_ac_dc(hwmgr); +} + static const struct amd_pm_funcs pp_dpm_funcs = { .load_firmware = pp_dpm_load_fw, .wait_for_fw_loading_complete = pp_dpm_fw_loading_complete, @@ -1588,7 +1578,6 @@ static const struct amd_pm_funcs pp_dpm_funcs = { .get_sclk = pp_dpm_get_sclk, .get_mclk = pp_dpm_get_mclk, .display_configuration_change = pp_display_configuration_change, - .get_display_power_level = pp_get_display_power_level, .get_current_clocks = pp_get_current_clocks, .get_clock_by_type = pp_get_clock_by_type, .get_clock_by_type_with_latency = pp_get_clock_by_type_with_latency, @@ -1615,4 +1604,5 @@ static const struct amd_pm_funcs pp_dpm_funcs = { .gfx_state_change_set = pp_gfx_state_change_set, .get_smu_prv_buf_details = pp_get_prv_buffer_details, .pm_compute_clocks = pp_pm_compute_clocks, + .notify_ac_dc = pp_dpm_notify_ac_dc, }; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c index a59677cf8dfc8..72c2d3b69a038 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c @@ -328,16 +328,6 @@ int phm_store_dal_configuration_data(struct pp_hwmgr *hwmgr, return 0; } -int phm_get_dal_power_level(struct pp_hwmgr *hwmgr, - struct amd_pp_simple_clock_info *info) -{ - PHM_FUNC_CHECK(hwmgr); - - if (info == NULL || hwmgr->hwmgr_func->get_dal_power_level == NULL) - return -EINVAL; - return hwmgr->hwmgr_func->get_dal_power_level(hwmgr, info); -} - int phm_set_cpu_power_state(struct pp_hwmgr *hwmgr) { PHM_FUNC_CHECK(hwmgr); diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c index 59af3314ffc40..bfd8fbb0b49dd 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c @@ -1319,7 +1319,6 @@ static int init_clock_voltage_dependency(struct pp_hwmgr *hwmgr, hwmgr->dyn_state.vddc_dependency_on_sclk = NULL; hwmgr->dyn_state.vddci_dependency_on_mclk = NULL; hwmgr->dyn_state.vddc_dependency_on_mclk = NULL; - hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL; hwmgr->dyn_state.mvdd_dependency_on_mclk = NULL; hwmgr->dyn_state.vce_clock_voltage_dependency_table = NULL; hwmgr->dyn_state.uvd_clock_voltage_dependency_table = NULL; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c index 2e671b45f1740..15456c1c3614d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu10_hwmgr.c @@ -127,41 +127,6 @@ static int smu10_construct_max_power_limits_table(struct pp_hwmgr *hwmgr, return 0; } -static int smu10_init_dynamic_state_adjustment_rule_settings( - struct pp_hwmgr *hwmgr) -{ - int count = 8; - struct phm_clock_voltage_dependency_table *table_clk_vlt; - - table_clk_vlt = kzalloc_flex(*table_clk_vlt, entries, count); - - if (NULL == table_clk_vlt) { - pr_err("Can not allocate memory!\n"); - return -ENOMEM; - } - - table_clk_vlt->count = count; - table_clk_vlt->entries[0].clk = PP_DAL_POWERLEVEL_0; - table_clk_vlt->entries[0].v = 0; - table_clk_vlt->entries[1].clk = PP_DAL_POWERLEVEL_1; - table_clk_vlt->entries[1].v = 1; - table_clk_vlt->entries[2].clk = PP_DAL_POWERLEVEL_2; - table_clk_vlt->entries[2].v = 2; - table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_3; - table_clk_vlt->entries[3].v = 3; - table_clk_vlt->entries[4].clk = PP_DAL_POWERLEVEL_4; - table_clk_vlt->entries[4].v = 4; - table_clk_vlt->entries[5].clk = PP_DAL_POWERLEVEL_5; - table_clk_vlt->entries[5].v = 5; - table_clk_vlt->entries[6].clk = PP_DAL_POWERLEVEL_6; - table_clk_vlt->entries[6].v = 6; - table_clk_vlt->entries[7].clk = PP_DAL_POWERLEVEL_7; - table_clk_vlt->entries[7].v = 7; - hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt; - - return 0; -} - static int smu10_get_system_info_data(struct pp_hwmgr *hwmgr) { struct smu10_hwmgr *smu10_data = (struct smu10_hwmgr *)hwmgr->backend; @@ -175,8 +140,6 @@ static int smu10_get_system_info_data(struct pp_hwmgr *hwmgr) smu10_construct_max_power_limits_table (hwmgr, &hwmgr->dyn_state.max_clock_voltage_on_ac); - smu10_init_dynamic_state_adjustment_rule_settings(hwmgr); - return 0; } @@ -611,9 +574,6 @@ static int smu10_hwmgr_backend_fini(struct pp_hwmgr *hwmgr) kfree(pinfo->vdd_dep_on_phyclk); pinfo->vdd_dep_on_phyclk = NULL; - kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl); - hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL; - kfree(hwmgr->backend); hwmgr->backend = NULL; @@ -962,12 +922,6 @@ static int smu10_store_cc6_data(struct pp_hwmgr *hwmgr, uint32_t separation_time return 0; } -static int smu10_get_dal_power_level(struct pp_hwmgr *hwmgr, - struct amd_pp_simple_clock_info *info) -{ - return -EINVAL; -} - static int smu10_force_clock_level(struct pp_hwmgr *hwmgr, enum pp_clock_type type, uint32_t mask) { @@ -1663,7 +1617,6 @@ static const struct pp_hwmgr_func smu10_hwmgr_funcs = { .store_cc6_data = smu10_store_cc6_data, .force_clock_level = smu10_force_clock_level, .emit_clock_levels = smu10_emit_clock_levels, - .get_dal_power_level = smu10_get_dal_power_level, .get_performance_level = smu10_get_performance_level, .get_current_shallow_sleep_clocks = smu10_get_current_shallow_sleep_clocks, .get_clock_by_type_with_latency = smu10_get_clock_by_type_with_latency, diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index 55e2375e1dad8..95bf187f02a5e 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -5854,6 +5854,20 @@ static int smu7_power_off_asic(struct pp_hwmgr *hwmgr) return result; } +static void smu7_notify_ac_dc(struct pp_hwmgr *hwmgr) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)(hwmgr->adev); + + /* Check if the platform already manages the AC/DC switch via dedicated GPIO. */ + if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_AutomaticDCTransition)) + return; + + /* The SMU automatically notices DC, but needs to be notified when switching to AC. */ + if (adev->pm.ac_power) + smum_send_msg_to_smc(hwmgr, PPSMC_MSG_RunningOnAC, NULL); +} + static const struct pp_hwmgr_func smu7_hwmgr_funcs = { .backend_init = &smu7_hwmgr_backend_init, .backend_fini = &smu7_hwmgr_backend_fini, @@ -5916,6 +5930,7 @@ static const struct pp_hwmgr_func smu7_hwmgr_funcs = { .get_asic_baco_state = smu7_baco_get_state, .set_asic_baco_state = smu7_baco_set_state, .power_off_asic = smu7_power_off_asic, + .notify_ac_dc = smu7_notify_ac_dc, }; uint8_t smu7_get_sleep_divider_id_from_clock(uint32_t clock, diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.c index 5ad6ab3d2d375..8f82d7c07e8c8 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu8_hwmgr.c @@ -270,41 +270,6 @@ static int smu8_construct_max_power_limits_table(struct pp_hwmgr *hwmgr, return 0; } -static int smu8_init_dynamic_state_adjustment_rule_settings( - struct pp_hwmgr *hwmgr, - ATOM_CLK_VOLT_CAPABILITY *disp_voltage_table) -{ - struct phm_clock_voltage_dependency_table *table_clk_vlt; - - table_clk_vlt = kzalloc_flex(*table_clk_vlt, entries, 8); - - if (NULL == table_clk_vlt) { - pr_err("Can not allocate memory!\n"); - return -ENOMEM; - } - - table_clk_vlt->count = 8; - table_clk_vlt->entries[0].clk = PP_DAL_POWERLEVEL_0; - table_clk_vlt->entries[0].v = 0; - table_clk_vlt->entries[1].clk = PP_DAL_POWERLEVEL_1; - table_clk_vlt->entries[1].v = 1; - table_clk_vlt->entries[2].clk = PP_DAL_POWERLEVEL_2; - table_clk_vlt->entries[2].v = 2; - table_clk_vlt->entries[3].clk = PP_DAL_POWERLEVEL_3; - table_clk_vlt->entries[3].v = 3; - table_clk_vlt->entries[4].clk = PP_DAL_POWERLEVEL_4; - table_clk_vlt->entries[4].v = 4; - table_clk_vlt->entries[5].clk = PP_DAL_POWERLEVEL_5; - table_clk_vlt->entries[5].v = 5; - table_clk_vlt->entries[6].clk = PP_DAL_POWERLEVEL_6; - table_clk_vlt->entries[6].v = 6; - table_clk_vlt->entries[7].clk = PP_DAL_POWERLEVEL_7; - table_clk_vlt->entries[7].v = 7; - hwmgr->dyn_state.vddc_dep_on_dal_pwrl = table_clk_vlt; - - return 0; -} - static int smu8_get_system_info_data(struct pp_hwmgr *hwmgr) { struct smu8_hwmgr *data = hwmgr->backend; @@ -403,9 +368,6 @@ static int smu8_get_system_info_data(struct pp_hwmgr *hwmgr) smu8_construct_max_power_limits_table (hwmgr, &hwmgr->dyn_state.max_clock_voltage_on_ac); - smu8_init_dynamic_state_adjustment_rule_settings(hwmgr, - &info->sDISPCLK_Voltage[0]); - return result; } @@ -1149,9 +1111,6 @@ static int smu8_hwmgr_backend_init(struct pp_hwmgr *hwmgr) static int smu8_hwmgr_backend_fini(struct pp_hwmgr *hwmgr) { if (hwmgr != NULL) { - kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl); - hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL; - kfree(hwmgr->backend); hwmgr->backend = NULL; } @@ -1521,27 +1480,6 @@ static int smu8_store_cc6_data(struct pp_hwmgr *hwmgr, uint32_t separation_time, return 0; } -static int smu8_get_dal_power_level(struct pp_hwmgr *hwmgr, - struct amd_pp_simple_clock_info *info) -{ - uint32_t i; - const struct phm_clock_voltage_dependency_table *table = - hwmgr->dyn_state.vddc_dep_on_dal_pwrl; - const struct phm_clock_and_voltage_limits *limits = - &hwmgr->dyn_state.max_clock_voltage_on_ac; - - info->engine_max_clock = limits->sclk; - info->memory_max_clock = limits->mclk; - - for (i = table->count - 1; i > 0; i--) { - if (limits->vddc >= table->entries[i].v) { - info->level = table->entries[i].clk; - return 0; - } - } - return -EINVAL; -} - static int smu8_force_clock_level(struct pp_hwmgr *hwmgr, enum pp_clock_type type, uint32_t mask) { @@ -2062,7 +2000,6 @@ static const struct pp_hwmgr_func smu8_hwmgr_funcs = { .store_cc6_data = smu8_store_cc6_data, .force_clock_level = smu8_force_clock_level, .emit_clock_levels = smu8_emit_clock_levels, - .get_dal_power_level = smu8_get_dal_power_level, .get_performance_level = smu8_get_performance_level, .get_current_shallow_sleep_clocks = smu8_get_current_shallow_sleep_clocks, .get_clock_by_type = smu8_get_clock_by_type, diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c index d9899cf7020b6..4b92b52aba2b8 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c @@ -814,9 +814,6 @@ static int vega10_set_private_data_based_on_pptable(struct pp_hwmgr *hwmgr) static int vega10_hwmgr_backend_fini(struct pp_hwmgr *hwmgr) { - kfree(hwmgr->dyn_state.vddc_dep_on_dal_pwrl); - hwmgr->dyn_state.vddc_dep_on_dal_pwrl = NULL; - kfree(hwmgr->backend); hwmgr->backend = NULL; @@ -4386,20 +4383,6 @@ static uint32_t vega10_get_fan_control_mode(struct pp_hwmgr *hwmgr) return AMD_FAN_CTRL_AUTO; } -static int vega10_get_dal_power_level(struct pp_hwmgr *hwmgr, - struct amd_pp_simple_clock_info *info) -{ - struct phm_ppt_v2_information *table_info = - (struct phm_ppt_v2_information *)hwmgr->pptable; - struct phm_clock_and_voltage_limits *max_limits = - &table_info->max_clock_voltage_on_ac; - - info->engine_max_clock = max_limits->sclk; - info->memory_max_clock = max_limits->mclk; - - return 0; -} - static void vega10_get_sclks(struct pp_hwmgr *hwmgr, struct pp_clock_levels_with_latency *clocks) { @@ -5644,7 +5627,6 @@ static const struct pp_hwmgr_func vega10_hwmgr_funcs = { .set_fan_control_mode = vega10_set_fan_control_mode, .get_fan_control_mode = vega10_get_fan_control_mode, .read_sensor = vega10_read_sensor, - .get_dal_power_level = vega10_get_dal_power_level, .get_clock_by_type_with_latency = vega10_get_clock_by_type_with_latency, .get_clock_by_type_with_voltage = vega10_get_clock_by_type_with_voltage, .set_watermarks_for_clocks_ranges = vega10_set_watermarks_for_clocks_ranges, diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c index a9a85fd639b28..69a9074058ceb 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c @@ -1822,21 +1822,6 @@ static uint32_t vega12_get_fan_control_mode(struct pp_hwmgr *hwmgr) return AMD_FAN_CTRL_AUTO; } -static int vega12_get_dal_power_level(struct pp_hwmgr *hwmgr, - struct amd_pp_simple_clock_info *info) -{ -#if 0 - struct phm_ppt_v2_information *table_info = - (struct phm_ppt_v2_information *)hwmgr->pptable; - struct phm_clock_and_voltage_limits *max_limits = - &table_info->max_clock_voltage_on_ac; - - info->engine_max_clock = max_limits->sclk; - info->memory_max_clock = max_limits->mclk; -#endif - return 0; -} - static int vega12_get_clock_ranges(struct pp_hwmgr *hwmgr, uint32_t *clock, PPCLK_e clock_select, @@ -2963,7 +2948,6 @@ static const struct pp_hwmgr_func vega12_hwmgr_funcs = { .set_fan_control_mode = vega12_set_fan_control_mode, .get_fan_control_mode = vega12_get_fan_control_mode, .read_sensor = vega12_read_sensor, - .get_dal_power_level = vega12_get_dal_power_level, .get_clock_by_type_with_latency = vega12_get_clock_by_type_with_latency, .get_clock_by_type_with_voltage = vega12_get_clock_by_type_with_voltage, .set_watermarks_for_clocks_ranges = vega12_set_watermarks_for_clocks_ranges, diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c index dab9b78a9fc86..7b8f4c1b80eb0 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c @@ -2796,22 +2796,6 @@ static void vega20_set_fan_control_mode(struct pp_hwmgr *hwmgr, uint32_t mode) } } -static int vega20_get_dal_power_level(struct pp_hwmgr *hwmgr, - struct amd_pp_simple_clock_info *info) -{ -#if 0 - struct phm_ppt_v2_information *table_info = - (struct phm_ppt_v2_information *)hwmgr->pptable; - struct phm_clock_and_voltage_limits *max_limits = - &table_info->max_clock_voltage_on_ac; - - info->engine_max_clock = max_limits->sclk; - info->memory_max_clock = max_limits->mclk; -#endif - return 0; -} - - static int vega20_get_sclks(struct pp_hwmgr *hwmgr, struct pp_clock_levels_with_latency *clocks) { @@ -4446,7 +4430,6 @@ static const struct pp_hwmgr_func vega20_hwmgr_funcs = { /* export to DAL */ .get_sclk = vega20_dpm_get_sclk, .get_mclk = vega20_dpm_get_mclk, - .get_dal_power_level = vega20_get_dal_power_level, .get_clock_by_type_with_latency = vega20_get_clock_by_type_with_latency, .get_clock_by_type_with_voltage = vega20_get_clock_by_type_with_voltage, .set_watermarks_for_clocks_ranges = vega20_set_watermarks_for_clocks_ranges, diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hardwaremanager.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hardwaremanager.h index 915f1b8e4dbad..36dcad065faeb 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hardwaremanager.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hardwaremanager.h @@ -426,9 +426,6 @@ extern int phm_check_states_equal(struct pp_hwmgr *hwmgr, extern int phm_store_dal_configuration_data(struct pp_hwmgr *hwmgr, const struct amd_pp_display_configuration *display_config); -extern int phm_get_dal_power_level(struct pp_hwmgr *hwmgr, - struct amd_pp_simple_clock_info *info); - extern int phm_set_cpu_power_state(struct pp_hwmgr *hwmgr); extern int phm_power_down_asic(struct pp_hwmgr *hwmgr); diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h index 3ae45eac0c5ca..ca71efaa16561 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h @@ -292,8 +292,6 @@ struct pp_hwmgr_func { int (*store_cc6_data)(struct pp_hwmgr *hwmgr, uint32_t separation_time, bool cc6_disable, bool pstate_disable, bool pstate_switch_disable); - int (*get_dal_power_level)(struct pp_hwmgr *hwmgr, - struct amd_pp_simple_clock_info *info); int (*get_performance_level)(struct pp_hwmgr *, const struct pp_hw_power_state *, PHM_PerformanceLevelDesignation, uint32_t, PHM_PerformanceLevel *); int (*get_current_shallow_sleep_clocks)(struct pp_hwmgr *hwmgr, @@ -364,6 +362,7 @@ struct pp_hwmgr_func { bool disable); ssize_t (*get_gpu_metrics)(struct pp_hwmgr *hwmgr, void **table); int (*gfx_state_change)(struct pp_hwmgr *hwmgr, uint32_t state); + void (*notify_ac_dc)(struct pp_hwmgr *hwmgr); }; struct pp_table_func { @@ -540,7 +539,6 @@ struct phm_ppt_v1_information { struct phm_clock_array *valid_dcefclk_values; struct phm_clock_and_voltage_limits max_clock_voltage_on_dc; struct phm_clock_and_voltage_limits max_clock_voltage_on_ac; - struct phm_clock_voltage_dependency_table *vddc_dep_on_dal_pwrl; struct phm_ppm_table *ppm_parameter_table; struct phm_cac_tdp_table *cac_dtp_table; struct phm_tdp_table *tdp_table; @@ -632,7 +630,6 @@ struct phm_dynamic_state_info { struct phm_clock_voltage_dependency_table *vddc_dependency_on_mclk; struct phm_clock_voltage_dependency_table *mvdd_dependency_on_mclk; struct phm_clock_voltage_dependency_table *vddc_dependency_on_display_clock; - struct phm_clock_voltage_dependency_table *vddc_dep_on_dal_pwrl; struct phm_clock_array *valid_sclk_values; struct phm_clock_array *valid_mclk_values; struct phm_clock_and_voltage_limits max_clock_voltage_on_dc; diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c index c2761f3d06a0a..571b81cb04750 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_virt_ras_cmd.c @@ -108,15 +108,19 @@ static int amdgpu_virt_ras_remote_ioctl_cmd(struct ras_core_context *ras_core, ret = amdgpu_virt_send_remote_ras_cmd(ras_core->dev, shared_mem.gpa, mem_len); if (!ret) { - if (rcmd->cmd_res) { - ret = rcmd->cmd_res; + uint32_t cmd_res = READ_ONCE(rcmd->cmd_res); + uint32_t osz; + + if (cmd_res) { + ret = cmd_res; goto out; } - cmd->cmd_res = rcmd->cmd_res; - cmd->output_size = rcmd->output_size; - if (rcmd->output_size && (rcmd->output_size <= output_size) && output_data) - memcpy(output_data, rcmd->output_buff_raw, rcmd->output_size); + osz = READ_ONCE(rcmd->output_size); + cmd->cmd_res = cmd_res; + cmd->output_size = osz; + if (osz && osz <= output_size && output_data) + memcpy(output_data, rcmd->output_buff_raw, osz); } out: diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h index 8156531a7b637..f34dda7ce87b1 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h +++ b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h @@ -46,6 +46,15 @@ printk(KERN_WARNING fmt, ##__VA_ARGS__); \ } while (0) +#define RAS_DEV_WARN_RATELIMITED(device, fmt, ...) \ + do { \ + if (device) \ + dev_warn_ratelimited(((struct amdgpu_device *)device)->dev, \ + fmt, ##__VA_ARGS__); \ + else \ + printk_ratelimited(KERN_WARNING fmt, ##__VA_ARGS__); \ + } while (0) + #define RAS_DEV_INFO(device, fmt, ...) \ do { \ if (device) \ diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c index 91dd730de3cec..f32ee2fecf536 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c @@ -193,12 +193,29 @@ static void ras_umc_reserve_eeprom_record(struct ras_core_context *ras_core, } /* When gpu reset is ongoing, ecc logging operations will be pended. + * + * The pending list is bounded by RAS_UMC_PENDING_ECC_MAX so that an ECC + * storm or repeated UMC error injection cannot make this list (and the + * kernel allocations behind it) grow without bound. Once the limit is + * reached, additional events are dropped and counted in + * pending_ecc_dropped, with a rate-limited warning emitted. */ int ras_umc_log_bad_bank_pending(struct ras_core_context *ras_core, struct ras_bank_ecc *bank) { struct ras_umc *ras_umc = &ras_core->ras_umc; struct ras_bank_ecc_node *ecc_node; + mutex_lock(&ras_umc->pending_ecc_lock); + if (ras_umc->pending_ecc_count >= RAS_UMC_PENDING_ECC_MAX) { + ras_umc->pending_ecc_dropped++; + mutex_unlock(&ras_umc->pending_ecc_lock); + RAS_DEV_WARN_RATELIMITED(ras_core->dev, + "pending ECC list full (%u), dropping bad bank event (total dropped:%u)\n", + RAS_UMC_PENDING_ECC_MAX, ras_umc->pending_ecc_dropped); + return -ENOSPC; + } + mutex_unlock(&ras_umc->pending_ecc_lock); + ecc_node = kzalloc_obj(*ecc_node); if (!ecc_node) return -ENOMEM; @@ -206,7 +223,15 @@ int ras_umc_log_bad_bank_pending(struct ras_core_context *ras_core, struct ras_b memcpy(&ecc_node->ecc, bank, sizeof(ecc_node->ecc)); mutex_lock(&ras_umc->pending_ecc_lock); + /* re-check under the lock to honor the cap across concurrent callers */ + if (ras_umc->pending_ecc_count >= RAS_UMC_PENDING_ECC_MAX) { + ras_umc->pending_ecc_dropped++; + mutex_unlock(&ras_umc->pending_ecc_lock); + kfree(ecc_node); + return -ENOSPC; + } list_add_tail(&ecc_node->node, &ras_umc->pending_ecc_list); + ras_umc->pending_ecc_count++; mutex_unlock(&ras_umc->pending_ecc_lock); return 0; @@ -225,8 +250,16 @@ int ras_umc_log_pending_bad_bank(struct ras_core_context *ras_core) if (!ras_umc_log_bad_bank(ras_core, &ecc_node->ecc)) { list_del(&ecc_node->node); kfree(ecc_node); + if (ras_umc->pending_ecc_count) + ras_umc->pending_ecc_count--; } } + if (ras_umc->pending_ecc_dropped) { + RAS_DEV_WARN(ras_core->dev, + "%u pending ECC bad-bank events were dropped during GPU reset\n", + ras_umc->pending_ecc_dropped); + ras_umc->pending_ecc_dropped = 0; + } mutex_unlock(&ras_umc->pending_ecc_lock); return 0; @@ -609,6 +642,8 @@ int ras_umc_sw_fini(struct ras_core_context *ras_core) list_del(&ecc_node->node); kfree(ecc_node); } + ras_umc->pending_ecc_count = 0; + ras_umc->pending_ecc_dropped = 0; mutex_unlock(&ras_umc->pending_ecc_lock); mutex_destroy(&ras_umc->tree_lock); diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h index 1d3026be509b8..237525b46b9bb 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h +++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h @@ -139,8 +139,20 @@ struct ras_umc { struct mutex pending_ecc_lock; struct ras_umc_err_data umc_err_data; struct list_head pending_ecc_list; + /* number of entries currently queued on pending_ecc_list */ + u32 pending_ecc_count; + /* number of entries dropped because pending_ecc_list was full */ + u32 pending_ecc_dropped; }; +/* + * Upper bound on entries that can be queued on pending_ecc_list while a + * GPU reset is in progress. Beyond this, new ECC events are dropped to + * prevent unbounded kernel memory growth in case of an ECC storm or + * malicious/repeated UMC error injection. + */ +#define RAS_UMC_PENDING_ECC_MAX 8192 + int ras_umc_sw_init(struct ras_core_context *ras); int ras_umc_sw_fini(struct ras_core_context *ras); int ras_umc_hw_init(struct ras_core_context *ras); diff --git a/drivers/gpu/drm/radeon/radeon_audio.c b/drivers/gpu/drm/radeon/radeon_audio.c index 8d64ba18572ec..52d0049738b17 100644 --- a/drivers/gpu/drm/radeon/radeon_audio.c +++ b/drivers/gpu/drm/radeon/radeon_audio.c @@ -311,7 +311,7 @@ static void radeon_audio_write_sad_regs(struct drm_encoder *encoder) if (!connector) return; - sad_count = drm_edid_to_sad(radeon_connector->edid, &sads); + sad_count = drm_edid_to_sad(drm_edid_raw(radeon_connector->edid), &sads); if (sad_count < 0) DRM_ERROR("Couldn't read SADs: %d\n", sad_count); if (sad_count <= 0) @@ -335,7 +335,7 @@ static void radeon_audio_write_speaker_allocation(struct drm_encoder *encoder) if (!connector) return; - sad_count = drm_edid_to_speaker_allocation(radeon_connector->edid, &sadb); + sad_count = drm_edid_to_speaker_allocation(drm_edid_raw(radeon_connector->edid), &sadb); if (sad_count < 0) { DRM_DEBUG("Couldn't read Speaker Allocation Data Block: %d\n", sad_count); diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 6a4e268ffd995..b3e7a747f11e3 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -390,10 +390,10 @@ bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev) } /* this is used for atom LCDs as well */ -struct edid * +const struct drm_edid * radeon_bios_get_hardcoded_edid(struct radeon_device *rdev) { - return drm_edid_duplicate(drm_edid_raw(rdev->mode_info.bios_hardcoded_edid)); + return drm_edid_dup(rdev->mode_info.bios_hardcoded_edid); } static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 373f08a125ffe..16b048f58c9be 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -271,8 +271,8 @@ static void radeon_connector_get_edid(struct drm_connector *connector) if ((radeon_connector_encoder_get_dp_bridge_encoder_id(connector) != ENCODER_OBJECT_ID_NONE) && radeon_connector->ddc_bus->has_aux) { - radeon_connector->edid = drm_get_edid(connector, - &radeon_connector->ddc_bus->aux.ddc); + radeon_connector->edid = drm_edid_read_ddc(connector, + &radeon_connector->ddc_bus->aux.ddc); } else if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) || (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) { struct radeon_connector_atom_dig *dig = radeon_connector->con_priv; @@ -280,19 +280,19 @@ static void radeon_connector_get_edid(struct drm_connector *connector) if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT || dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && radeon_connector->ddc_bus->has_aux) - radeon_connector->edid = drm_get_edid(&radeon_connector->base, - &radeon_connector->ddc_bus->aux.ddc); + radeon_connector->edid = drm_edid_read_ddc(&radeon_connector->base, + &radeon_connector->ddc_bus->aux.ddc); else if (radeon_connector->ddc_bus) - radeon_connector->edid = drm_get_edid(&radeon_connector->base, - &radeon_connector->ddc_bus->adapter); + radeon_connector->edid = drm_edid_read_ddc(&radeon_connector->base, + &radeon_connector->ddc_bus->adapter); } else if (vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC && connector->connector_type == DRM_MODE_CONNECTOR_LVDS && radeon_connector->ddc_bus) { - radeon_connector->edid = drm_get_edid_switcheroo(&radeon_connector->base, - &radeon_connector->ddc_bus->adapter); + radeon_connector->edid = drm_edid_read_switcheroo(&radeon_connector->base, + &radeon_connector->ddc_bus->adapter); } else if (radeon_connector->ddc_bus) { - radeon_connector->edid = drm_get_edid(&radeon_connector->base, - &radeon_connector->ddc_bus->adapter); + radeon_connector->edid = drm_edid_read_ddc(&radeon_connector->base, + &radeon_connector->ddc_bus->adapter); } if (!radeon_connector->edid) { @@ -314,25 +314,17 @@ static void radeon_connector_get_edid(struct drm_connector *connector) } } -static void radeon_connector_free_edid(struct drm_connector *connector) -{ - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - - kfree(radeon_connector->edid); - radeon_connector->edid = NULL; -} - static int radeon_ddc_get_modes(struct drm_connector *connector) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); int ret; if (radeon_connector->edid) { - drm_connector_update_edid_property(connector, radeon_connector->edid); - ret = drm_add_edid_modes(connector, radeon_connector->edid); + drm_edid_connector_update(connector, radeon_connector->edid); + ret = drm_edid_connector_add_modes(connector); return ret; } - drm_connector_update_edid_property(connector, NULL); + drm_edid_connector_update(connector, NULL); return 0; } @@ -895,7 +887,9 @@ static void radeon_connector_destroy(struct drm_connector *connector) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); - radeon_connector_free_edid(connector); + drm_edid_free(radeon_connector->edid); + radeon_connector->edid = NULL; + kfree(radeon_connector->con_priv); drm_connector_unregister(connector); drm_connector_cleanup(connector); @@ -1007,7 +1001,8 @@ radeon_vga_detect(struct drm_connector *connector, bool force) dret = radeon_ddc_probe(radeon_connector, false); if (dret) { radeon_connector->detected_by_load = false; - radeon_connector_free_edid(connector); + drm_edid_free(radeon_connector->edid); + radeon_connector->edid = NULL; radeon_connector_get_edid(connector); if (!radeon_connector->edid) { @@ -1016,13 +1011,14 @@ radeon_vga_detect(struct drm_connector *connector, bool force) ret = connector_status_connected; } else { radeon_connector->use_digital = - !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); + drm_edid_is_digital(radeon_connector->edid); /* some oems have boards with separate digital and analog connectors * with a shared ddc line (often vga + hdmi) */ if (radeon_connector->use_digital && radeon_connector->shared_ddc) { - radeon_connector_free_edid(connector); + drm_edid_free(radeon_connector->edid); + radeon_connector->edid = NULL; ret = connector_status_disconnected; } else { ret = connector_status_connected; @@ -1251,7 +1247,8 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) } if (dret) { radeon_connector->detected_by_load = false; - radeon_connector_free_edid(connector); + drm_edid_free(radeon_connector->edid); + radeon_connector->edid = NULL; radeon_connector_get_edid(connector); if (!radeon_connector->edid) { @@ -1271,13 +1268,14 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) } } else { radeon_connector->use_digital = - !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); + drm_edid_is_digital(radeon_connector->edid); /* some oems have boards with separate digital and analog connectors * with a shared ddc line (often vga + hdmi) */ if ((!radeon_connector->use_digital) && radeon_connector->shared_ddc) { - radeon_connector_free_edid(connector); + drm_edid_free(radeon_connector->edid); + radeon_connector->edid = NULL; ret = connector_status_disconnected; } else { ret = connector_status_connected; @@ -1301,7 +1299,8 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) if (list_connector->connector_type != DRM_MODE_CONNECTOR_VGA) { /* hpd is our only option in this case */ if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) { - radeon_connector_free_edid(connector); + drm_edid_free(radeon_connector->edid); + radeon_connector->edid = NULL; ret = connector_status_disconnected; } } @@ -1635,7 +1634,8 @@ radeon_dp_detect(struct drm_connector *connector, bool force) goto out; } - radeon_connector_free_edid(connector); + drm_edid_free(radeon_connector->edid); + radeon_connector->edid = NULL; if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index ae1ecdc2e1894..031a3bf6fe0a4 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -525,7 +525,7 @@ struct radeon_connector { bool use_digital; /* we need to mind the EDID between detect and get modes due to analog/digital/tvencoder */ - struct edid *edid; + const struct drm_edid *edid; void *con_priv; bool dac_load_detect; bool detected_by_load; /* if the connection status was determined by load */ @@ -839,7 +839,7 @@ radeon_get_crtc_scanout_position(struct drm_crtc *crtc, bool in_vblank_irq, const struct drm_display_mode *mode); extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev); -extern struct edid * +extern const struct drm_edid * radeon_bios_get_hardcoded_edid(struct radeon_device *rdev); extern bool radeon_atom_get_clock_info(struct drm_device *dev); extern bool radeon_combios_get_clock_info(struct drm_device *dev); |
