diff options
Diffstat (limited to 'drivers/gpu')
639 files changed, 39195 insertions, 24334 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/Makefile b/drivers/gpu/drm/amd/amdgpu/Makefile index ba80542ead9d..5100e35027ec 100644 --- a/drivers/gpu/drm/amd/amdgpu/Makefile +++ b/drivers/gpu/drm/amd/amdgpu/Makefile @@ -70,7 +70,7 @@ amdgpu-y += amdgpu_device.o amdgpu_reg_access.o amdgpu_doorbell_mgr.o amdgpu_kms amdgpu_umc.o smu_v11_0_i2c.o amdgpu_fru_eeprom.o amdgpu_rap.o \ amdgpu_fw_attestation.o amdgpu_securedisplay.o \ amdgpu_eeprom.o amdgpu_mca.o amdgpu_psp_ta.o amdgpu_lsdma.o amdgpu_lockdep.o \ - amdgpu_ring_mux.o amdgpu_xcp.o amdgpu_seq64.o amdgpu_aca.o amdgpu_dev_coredump.o \ + amdgpu_ring_mux.o amdgpu_xcp.o amdgpu_seq64.o amdgpu_dev_coredump.o \ amdgpu_cper.o amdgpu_userq_fence.o amdgpu_eviction_fence.o amdgpu_ip.o amdgpu-$(CONFIG_PROC_FS) += amdgpu_fdinfo.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index 7b09410d6d8f..dd8ea71077af 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -44,6 +44,7 @@ #include <linux/hashtable.h> #include <linux/dma-fence.h> #include <linux/pci.h> +#include <linux/xarray.h> #include <drm/ttm/ttm_bo.h> #include <drm/ttm/ttm_placement.h> @@ -103,7 +104,6 @@ #include "amdgpu_smuio.h" #include "amdgpu_fdinfo.h" #include "amdgpu_mca.h" -#include "amdgpu_aca.h" #include "amdgpu_ras.h" #include "amdgpu_lockdep.h" #include "amdgpu_cper.h" @@ -113,6 +113,7 @@ #include "amdgpu_userq.h" #include "amdgpu_eviction_fence.h" #include "amdgpu_ip.h" +#include "amdgpu_sa.h" #if defined(CONFIG_DRM_AMD_ISP) #include "amdgpu_isp.h" #endif @@ -272,7 +273,6 @@ extern int amdgpu_ptl; extern uint amdgpu_hdmi_hpd_debounce_delay_ms; -#define AMDGPU_VM_MAX_NUM_CTX 4096 #define AMDGPU_SG_THRESHOLD (256*1024*1024) #define AMDGPU_WAIT_IDLE_TIMEOUT_IN_MS 3000 #define AMDGPU_MAX_USEC_TIMEOUT 100000 /* 100 ms */ @@ -305,9 +305,10 @@ extern uint amdgpu_hdmi_hpd_debounce_delay_ms; /* reset mask */ #define AMDGPU_RESET_TYPE_FULL (1 << 0) /* full adapter reset, mode1/mode2/BACO/etc. */ -#define AMDGPU_RESET_TYPE_SOFT_RESET (1 << 1) /* IP level soft reset */ +#define AMDGPU_RESET_TYPE_SOFT_RECOVERY (1 << 1) /* soft recovery, eg. kill shaders */ #define AMDGPU_RESET_TYPE_PER_QUEUE (1 << 2) /* per queue */ #define AMDGPU_RESET_TYPE_PER_PIPE (1 << 3) /* per pipe */ +#define AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET (1 << 4) /* soft-resets an IP block */ /* max cursor sizes (in pixels) */ #define CIK_CURSOR_WIDTH 128 @@ -387,37 +388,6 @@ struct amdgpu_clock { uint32_t max_pixel_clock; }; -/* sub-allocation manager, it has to be protected by another lock. - * By conception this is an helper for other part of the driver - * like the indirect buffer or semaphore, which both have their - * locking. - * - * Principe is simple, we keep a list of sub allocation in offset - * order (first entry has offset == 0, last entry has the highest - * offset). - * - * When allocating new object we first check if there is room at - * the end total_size - (last_object_offset + last_object_size) >= - * alloc_size. If so we allocate new object there. - * - * When there is not enough room at the end, we start waiting for - * each sub object until we reach object_offset+object_size >= - * alloc_size, this object then become the sub object we return. - * - * Alignment can't be bigger than page size. - * - * Hole are not considered for allocation to keep things simple. - * Assumption is that there won't be hole (all object on same - * alignment). - */ - -struct amdgpu_sa_manager { - struct drm_suballoc_manager base; - struct amdgpu_bo *bo; - uint64_t gpu_addr; - void *cpu_ptr; -}; - /* * IRQS. */ @@ -446,8 +416,7 @@ struct amdgpu_fpriv { struct amdgpu_bo_va *prt_va; struct amdgpu_bo_va *csa_va; struct amdgpu_bo_va *seq64_va; - struct mutex bo_list_lock; - struct idr bo_list_handles; + struct xarray bo_list_handles; struct amdgpu_ctx_mgr ctx_mgr; struct amdgpu_userq_mgr userq_mgr; @@ -587,8 +556,6 @@ struct amdgpu_asic_funcs { /* invalidate hdp read cache */ void (*invalidate_hdp)(struct amdgpu_device *adev, struct amdgpu_ring *ring); - /* check if the asic needs a full reset of if soft reset will work */ - bool (*need_full_reset)(struct amdgpu_device *adev); /* initialize doorbell layout for specific asic*/ void (*init_doorbell_index)(struct amdgpu_device *adev); /* PCIe bandwidth usage */ @@ -851,6 +818,7 @@ struct amdgpu_device { struct dev_pm_domain vga_pm_domain; bool have_disp_power_ref; bool have_atomics_support; + bool is_sw_smu; /* BIOS */ bool is_atom_fw; @@ -1022,9 +990,6 @@ struct amdgpu_device { /* MCA */ struct amdgpu_mca mca; - /* ACA */ - struct amdgpu_aca aca; - /* CPER */ struct amdgpu_cper cper; @@ -1136,6 +1101,8 @@ struct amdgpu_device { bool debug_vm_userptr; bool debug_disable_ce_logs; bool debug_enable_ce_cs; + bool debug_hibernation_thaw_resume_gpu; + bool debug_disable_ip_block_soft_reset; /* Protection for the following isolation structure */ struct mutex enforce_isolation_mutex; @@ -1356,7 +1323,6 @@ int emu_soc_asic_init(struct amdgpu_device *adev); #define amdgpu_asic_read_bios_from_rom(adev, b, l) (adev)->asic_funcs->read_bios_from_rom((adev), (b), (l)) #define amdgpu_asic_read_register(adev, se, sh, offset, v)((adev)->asic_funcs->read_register((adev), (se), (sh), (offset), (v))) #define amdgpu_asic_get_config_memsize(adev) (adev)->asic_funcs->get_config_memsize((adev)) -#define amdgpu_asic_need_full_reset(adev) (adev)->asic_funcs->need_full_reset((adev)) #define amdgpu_asic_init_doorbell_index(adev) (adev)->asic_funcs->init_doorbell_index((adev)) #define amdgpu_asic_get_pcie_usage(adev, cnt0, cnt1) ((adev)->asic_funcs->get_pcie_usage((adev), (cnt0), (cnt1))) #define amdgpu_asic_need_reset_on_init(adev) (adev)->asic_funcs->need_reset_on_init((adev)) @@ -1468,6 +1434,8 @@ int amdgpu_enable_vblank_kms(struct drm_crtc *crtc); void amdgpu_disable_vblank_kms(struct drm_crtc *crtc); int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp); +int amdgpu_proc_options_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp); /* * functions used by amdgpu_encoder.c diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c deleted file mode 100644 index db7858fe0c3d..000000000000 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.c +++ /dev/null @@ -1,985 +0,0 @@ -/* - * Copyright 2023 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. - * - */ - -#include <linux/list.h> -#include "amdgpu.h" -#include "amdgpu_aca.h" -#include "amdgpu_ras.h" - -#define ACA_BANK_HWID(type, hwid, mcatype) [ACA_HWIP_TYPE_##type] = {hwid, mcatype} - -typedef int bank_handler_t(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type, void *data); - -static struct aca_hwip aca_hwid_mcatypes[ACA_HWIP_TYPE_COUNT] = { - ACA_BANK_HWID(SMU, 0x01, 0x01), - ACA_BANK_HWID(PCS_XGMI, 0x50, 0x00), - ACA_BANK_HWID(UMC, 0x96, 0x00), -}; - -static void aca_banks_init(struct aca_banks *banks) -{ - if (!banks) - return; - - memset(banks, 0, sizeof(*banks)); - INIT_LIST_HEAD(&banks->list); -} - -static int aca_banks_add_bank(struct aca_banks *banks, struct aca_bank *bank) -{ - struct aca_bank_node *node; - - if (!bank) - return -EINVAL; - - node = kvzalloc_obj(*node); - if (!node) - return -ENOMEM; - - memcpy(&node->bank, bank, sizeof(*bank)); - - INIT_LIST_HEAD(&node->node); - list_add_tail(&node->node, &banks->list); - - banks->nr_banks++; - - return 0; -} - -static void aca_banks_release(struct aca_banks *banks) -{ - struct aca_bank_node *node, *tmp; - - if (list_empty(&banks->list)) - return; - - list_for_each_entry_safe(node, tmp, &banks->list, node) { - list_del(&node->node); - kvfree(node); - banks->nr_banks--; - } -} - -static int aca_smu_get_valid_aca_count(struct amdgpu_device *adev, enum aca_smu_type type, u32 *count) -{ - struct amdgpu_aca *aca = &adev->aca; - const struct aca_smu_funcs *smu_funcs = aca->smu_funcs; - - if (!count) - return -EINVAL; - - if (!smu_funcs || !smu_funcs->get_valid_aca_count) - return -EOPNOTSUPP; - - return smu_funcs->get_valid_aca_count(adev, type, count); -} - -static struct aca_regs_dump { - const char *name; - int reg_idx; -} aca_regs[] = { - {"CONTROL", ACA_REG_IDX_CTL}, - {"STATUS", ACA_REG_IDX_STATUS}, - {"ADDR", ACA_REG_IDX_ADDR}, - {"MISC", ACA_REG_IDX_MISC0}, - {"CONFIG", ACA_REG_IDX_CONFIG}, - {"IPID", ACA_REG_IDX_IPID}, - {"SYND", ACA_REG_IDX_SYND}, - {"DESTAT", ACA_REG_IDX_DESTAT}, - {"DEADDR", ACA_REG_IDX_DEADDR}, - {"CONTROL_MASK", ACA_REG_IDX_CTL_MASK}, -}; - -static void aca_smu_bank_dump(struct amdgpu_device *adev, int idx, int total, struct aca_bank *bank, - struct ras_query_context *qctx) -{ - u64 event_id = qctx ? qctx->evid.event_id : RAS_EVENT_INVALID_ID; - int i; - - if (adev->debug_disable_ce_logs && - bank->smu_err_type == ACA_SMU_TYPE_CE && - !ACA_BANK_ERR_IS_DEFFERED(bank)) - return; - - RAS_EVENT_LOG(adev, event_id, HW_ERR "Accelerator Check Architecture events logged\n"); - /* plus 1 for output format, e.g: ACA[08/08]: xxxx */ - for (i = 0; i < ARRAY_SIZE(aca_regs); i++) - RAS_EVENT_LOG(adev, event_id, HW_ERR "ACA[%02d/%02d].%s=0x%016llx\n", - idx + 1, total, aca_regs[i].name, bank->regs[aca_regs[i].reg_idx]); - - if (ACA_REG__STATUS__SCRUB(bank->regs[ACA_REG_IDX_STATUS])) - RAS_EVENT_LOG(adev, event_id, HW_ERR "hardware error logged by the scrubber\n"); -} - -static bool aca_bank_hwip_is_matched(struct aca_bank *bank, enum aca_hwip_type type) -{ - - struct aca_hwip *hwip; - int hwid, mcatype; - u64 ipid; - - if (!bank || type == ACA_HWIP_TYPE_UNKNOW) - return false; - - hwip = &aca_hwid_mcatypes[type]; - if (!hwip->hwid) - return false; - - ipid = bank->regs[ACA_REG_IDX_IPID]; - hwid = ACA_REG__IPID__HARDWAREID(ipid); - mcatype = ACA_REG__IPID__MCATYPE(ipid); - - return hwip->hwid == hwid && hwip->mcatype == mcatype; -} - -static int aca_smu_get_valid_aca_banks(struct amdgpu_device *adev, enum aca_smu_type type, - int start, int count, - struct aca_banks *banks, struct ras_query_context *qctx) -{ - struct amdgpu_aca *aca = &adev->aca; - const struct aca_smu_funcs *smu_funcs = aca->smu_funcs; - struct aca_bank bank; - int i, max_count, ret; - - if (!count) - return 0; - - if (!smu_funcs || !smu_funcs->get_valid_aca_bank) - return -EOPNOTSUPP; - - switch (type) { - case ACA_SMU_TYPE_UE: - max_count = smu_funcs->max_ue_bank_count; - break; - case ACA_SMU_TYPE_CE: - max_count = smu_funcs->max_ce_bank_count; - break; - default: - return -EINVAL; - } - - if (start + count > max_count) - return -EINVAL; - - count = min_t(int, count, max_count); - for (i = 0; i < count; i++) { - memset(&bank, 0, sizeof(bank)); - ret = smu_funcs->get_valid_aca_bank(adev, type, start + i, &bank); - if (ret) - return ret; - - bank.smu_err_type = type; - - /* - * Poison being consumed when injecting a UE while running background workloads, - * which are unexpected. - */ - if (type == ACA_SMU_TYPE_UE && - ACA_REG__STATUS__POISON(bank.regs[ACA_REG_IDX_STATUS]) && - !aca_bank_hwip_is_matched(&bank, ACA_HWIP_TYPE_UMC)) - continue; - - aca_smu_bank_dump(adev, i, count, &bank, qctx); - - ret = aca_banks_add_bank(banks, &bank); - if (ret) - return ret; - } - - return 0; -} - -static bool aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type) -{ - const struct aca_bank_ops *bank_ops = handle->bank_ops; - - /* Parse all deferred errors with UMC aca handle */ - if (ACA_BANK_ERR_IS_DEFFERED(bank)) - return handle->hwip == ACA_HWIP_TYPE_UMC; - - if (!aca_bank_hwip_is_matched(bank, handle->hwip)) - return false; - - if (!bank_ops->aca_bank_is_valid) - return true; - - return bank_ops->aca_bank_is_valid(handle, bank, type, handle->data); -} - -static struct aca_bank_error *new_bank_error(struct aca_error *aerr, struct aca_bank_info *info) -{ - struct aca_bank_error *bank_error; - - bank_error = kvzalloc_obj(*bank_error); - if (!bank_error) - return NULL; - - INIT_LIST_HEAD(&bank_error->node); - memcpy(&bank_error->info, info, sizeof(*info)); - - mutex_lock(&aerr->lock); - list_add_tail(&bank_error->node, &aerr->list); - aerr->nr_errors++; - mutex_unlock(&aerr->lock); - - return bank_error; -} - -static struct aca_bank_error *find_bank_error(struct aca_error *aerr, struct aca_bank_info *info) -{ - struct aca_bank_error *bank_error = NULL; - struct aca_bank_info *tmp_info; - bool found = false; - - mutex_lock(&aerr->lock); - list_for_each_entry(bank_error, &aerr->list, node) { - tmp_info = &bank_error->info; - if (tmp_info->socket_id == info->socket_id && - tmp_info->die_id == info->die_id) { - found = true; - goto out_unlock; - } - } - -out_unlock: - mutex_unlock(&aerr->lock); - - return found ? bank_error : NULL; -} - -static void aca_bank_error_remove(struct aca_error *aerr, struct aca_bank_error *bank_error) -{ - if (!aerr || !bank_error) - return; - - list_del(&bank_error->node); - aerr->nr_errors--; - - kvfree(bank_error); -} - -static struct aca_bank_error *get_bank_error(struct aca_error *aerr, struct aca_bank_info *info) -{ - struct aca_bank_error *bank_error; - - if (!aerr || !info) - return NULL; - - bank_error = find_bank_error(aerr, info); - if (bank_error) - return bank_error; - - return new_bank_error(aerr, info); -} - -int aca_error_cache_log_bank_error(struct aca_handle *handle, struct aca_bank_info *info, - enum aca_error_type type, u64 count) -{ - struct aca_error_cache *error_cache = &handle->error_cache; - struct aca_bank_error *bank_error; - struct aca_error *aerr; - - if (!handle || !info || type >= ACA_ERROR_TYPE_COUNT) - return -EINVAL; - - if (!count) - return 0; - - aerr = &error_cache->errors[type]; - bank_error = get_bank_error(aerr, info); - if (!bank_error) - return -ENOMEM; - - bank_error->count += count; - - return 0; -} - -static int aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type) -{ - const struct aca_bank_ops *bank_ops = handle->bank_ops; - - if (!bank) - return -EINVAL; - - if (!bank_ops->aca_bank_parser) - return -EOPNOTSUPP; - - return bank_ops->aca_bank_parser(handle, bank, type, - handle->data); -} - -static int handler_aca_log_bank_error(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - int ret; - - ret = aca_bank_parser(handle, bank, type); - if (ret) - return ret; - - return 0; -} - -static int aca_dispatch_bank(struct aca_handle_manager *mgr, struct aca_bank *bank, - enum aca_smu_type type, bank_handler_t handler, void *data) -{ - struct aca_handle *handle; - int ret; - - if (list_empty(&mgr->list)) - return 0; - - list_for_each_entry(handle, &mgr->list, node) { - if (!aca_bank_is_valid(handle, bank, type)) - continue; - - ret = handler(handle, bank, type, data); - if (ret) - return ret; - } - - return 0; -} - -static int aca_dispatch_banks(struct aca_handle_manager *mgr, struct aca_banks *banks, - enum aca_smu_type type, bank_handler_t handler, void *data) -{ - struct aca_bank_node *node; - struct aca_bank *bank; - int ret; - - if (!mgr || !banks) - return -EINVAL; - - /* pre check to avoid unnecessary operations */ - if (list_empty(&mgr->list) || list_empty(&banks->list)) - return 0; - - list_for_each_entry(node, &banks->list, node) { - bank = &node->bank; - - ret = aca_dispatch_bank(mgr, bank, type, handler, data); - if (ret) - return ret; - } - - return 0; -} - -static bool aca_bank_should_update(struct amdgpu_device *adev, enum aca_smu_type type) -{ - struct amdgpu_aca *aca = &adev->aca; - bool ret = true; - - /* - * Because the UE Valid MCA count will only be cleared after reset, - * in order to avoid repeated counting of the error count, - * the aca bank is only updated once during the gpu recovery stage. - */ - if (type == ACA_SMU_TYPE_UE) { - if (amdgpu_ras_intr_triggered()) - ret = atomic_cmpxchg(&aca->ue_update_flag, 0, 1) == 0; - else - atomic_set(&aca->ue_update_flag, 0); - } - - return ret; -} - -static void aca_banks_generate_cper(struct amdgpu_device *adev, - enum aca_smu_type type, - struct aca_banks *banks, - int count) -{ - struct aca_bank_node *node; - struct aca_bank *bank; - int r; - - if (!adev->cper.enabled) - return; - - if (!banks || !count) { - dev_warn(adev->dev, "fail to generate cper records\n"); - return; - } - - /* UEs must be encoded into separate CPER entries */ - if (type == ACA_SMU_TYPE_UE) { - struct aca_banks de_banks; - - aca_banks_init(&de_banks); - list_for_each_entry(node, &banks->list, node) { - bank = &node->bank; - if (bank->aca_err_type == ACA_ERROR_TYPE_DEFERRED) { - r = aca_banks_add_bank(&de_banks, bank); - if (r) - dev_warn(adev->dev, "fail to add de banks, ret = %d\n", r); - } else { - if (amdgpu_cper_generate_ue_record(adev, bank)) - dev_warn(adev->dev, "fail to generate ue cper records\n"); - } - } - - if (!list_empty(&de_banks.list)) { - if (amdgpu_cper_generate_ce_records(adev, &de_banks, de_banks.nr_banks)) - dev_warn(adev->dev, "fail to generate de cper records\n"); - } - - aca_banks_release(&de_banks); - } else { - /* - * SMU_TYPE_CE banks are combined into 1 CPER entries, - * they could be CEs or DEs or both - */ - if (amdgpu_cper_generate_ce_records(adev, banks, count)) - dev_warn(adev->dev, "fail to generate ce cper records\n"); - } -} - -static int aca_banks_update(struct amdgpu_device *adev, enum aca_smu_type type, - bank_handler_t handler, struct ras_query_context *qctx, void *data) -{ - struct amdgpu_aca *aca = &adev->aca; - struct aca_banks banks; - u32 count = 0; - int ret; - - if (list_empty(&aca->mgr.list)) - return 0; - - if (!aca_bank_should_update(adev, type)) - return 0; - - ret = aca_smu_get_valid_aca_count(adev, type, &count); - if (ret) - return ret; - - if (!count) - return 0; - - aca_banks_init(&banks); - - ret = aca_smu_get_valid_aca_banks(adev, type, 0, count, &banks, qctx); - if (ret) - goto err_release_banks; - - if (list_empty(&banks.list)) { - ret = 0; - goto err_release_banks; - } - - ret = aca_dispatch_banks(&aca->mgr, &banks, type, - handler, data); - if (ret) - goto err_release_banks; - - aca_banks_generate_cper(adev, type, &banks, count); - -err_release_banks: - aca_banks_release(&banks); - - return ret; -} - -static int aca_log_aca_error_data(struct aca_bank_error *bank_error, enum aca_error_type type, struct ras_err_data *err_data) -{ - struct aca_bank_info *info; - struct amdgpu_smuio_mcm_config_info mcm_info; - u64 count; - - if (type >= ACA_ERROR_TYPE_COUNT) - return -EINVAL; - - count = bank_error->count; - if (!count) - return 0; - - info = &bank_error->info; - mcm_info.die_id = info->die_id; - mcm_info.socket_id = info->socket_id; - - switch (type) { - case ACA_ERROR_TYPE_UE: - amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, count); - break; - case ACA_ERROR_TYPE_CE: - amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, count); - break; - case ACA_ERROR_TYPE_DEFERRED: - amdgpu_ras_error_statistic_de_count(err_data, &mcm_info, count); - break; - default: - break; - } - - return 0; -} - -static int aca_log_aca_error(struct aca_handle *handle, enum aca_error_type type, struct ras_err_data *err_data) -{ - struct aca_error_cache *error_cache = &handle->error_cache; - struct aca_error *aerr = &error_cache->errors[type]; - struct aca_bank_error *bank_error, *tmp; - - mutex_lock(&aerr->lock); - - if (list_empty(&aerr->list)) - goto out_unlock; - - list_for_each_entry_safe(bank_error, tmp, &aerr->list, node) { - aca_log_aca_error_data(bank_error, type, err_data); - aca_bank_error_remove(aerr, bank_error); - } - -out_unlock: - mutex_unlock(&aerr->lock); - - return 0; -} - -static int __aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle, enum aca_error_type type, - struct ras_err_data *err_data, struct ras_query_context *qctx) -{ - enum aca_smu_type smu_type; - int ret; - - switch (type) { - case ACA_ERROR_TYPE_UE: - smu_type = ACA_SMU_TYPE_UE; - break; - case ACA_ERROR_TYPE_CE: - case ACA_ERROR_TYPE_DEFERRED: - smu_type = ACA_SMU_TYPE_CE; - break; - default: - return -EINVAL; - } - - /* update aca bank to aca source error_cache first */ - ret = aca_banks_update(adev, smu_type, handler_aca_log_bank_error, qctx, NULL); - if (ret) - return ret; - - /* DEs may contain in CEs or UEs */ - if (type != ACA_ERROR_TYPE_DEFERRED) - aca_log_aca_error(handle, ACA_ERROR_TYPE_DEFERRED, err_data); - - return aca_log_aca_error(handle, type, err_data); -} - -static bool aca_handle_is_valid(struct aca_handle *handle) -{ - if (!handle->mask || !list_empty(&handle->node)) - return false; - - return true; -} - -int amdgpu_aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle, - enum aca_error_type type, struct ras_err_data *err_data, - struct ras_query_context *qctx) -{ - if (!handle || !err_data) - return -EINVAL; - - if (aca_handle_is_valid(handle)) - return -EOPNOTSUPP; - - if ((type < 0) || (!(BIT(type) & handle->mask))) - return 0; - - return __aca_get_error_data(adev, handle, type, err_data, qctx); -} - -static void aca_error_init(struct aca_error *aerr, enum aca_error_type type) -{ - mutex_init(&aerr->lock); - INIT_LIST_HEAD(&aerr->list); - aerr->type = type; - aerr->nr_errors = 0; -} - -static void aca_init_error_cache(struct aca_handle *handle) -{ - struct aca_error_cache *error_cache = &handle->error_cache; - int type; - - for (type = ACA_ERROR_TYPE_UE; type < ACA_ERROR_TYPE_COUNT; type++) - aca_error_init(&error_cache->errors[type], type); -} - -static void aca_error_fini(struct aca_error *aerr) -{ - struct aca_bank_error *bank_error, *tmp; - - mutex_lock(&aerr->lock); - if (list_empty(&aerr->list)) - goto out_unlock; - - list_for_each_entry_safe(bank_error, tmp, &aerr->list, node) - aca_bank_error_remove(aerr, bank_error); - -out_unlock: - mutex_unlock(&aerr->lock); - mutex_destroy(&aerr->lock); -} - -static void aca_fini_error_cache(struct aca_handle *handle) -{ - struct aca_error_cache *error_cache = &handle->error_cache; - int type; - - for (type = ACA_ERROR_TYPE_UE; type < ACA_ERROR_TYPE_COUNT; type++) - aca_error_fini(&error_cache->errors[type]); -} - -static int add_aca_handle(struct amdgpu_device *adev, struct aca_handle_manager *mgr, struct aca_handle *handle, - const char *name, const struct aca_info *ras_info, void *data) -{ - memset(handle, 0, sizeof(*handle)); - - handle->adev = adev; - handle->mgr = mgr; - handle->name = name; - handle->hwip = ras_info->hwip; - handle->mask = ras_info->mask; - handle->bank_ops = ras_info->bank_ops; - handle->data = data; - aca_init_error_cache(handle); - - INIT_LIST_HEAD(&handle->node); - list_add_tail(&handle->node, &mgr->list); - mgr->nr_handles++; - - return 0; -} - -static ssize_t aca_sysfs_read(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct aca_handle *handle = container_of(attr, struct aca_handle, aca_attr); - - /* NOTE: the aca cache will be auto cleared once read, - * So the driver should unify the query entry point, forward request to ras query interface directly */ - return amdgpu_ras_aca_sysfs_read(dev, attr, handle, buf, handle->data); -} - -static int add_aca_sysfs(struct amdgpu_device *adev, struct aca_handle *handle) -{ - struct device_attribute *aca_attr = &handle->aca_attr; - - snprintf(handle->attr_name, sizeof(handle->attr_name) - 1, "aca_%s", handle->name); - aca_attr->show = aca_sysfs_read; - aca_attr->attr.name = handle->attr_name; - aca_attr->attr.mode = S_IRUGO; - sysfs_attr_init(&aca_attr->attr); - - return sysfs_add_file_to_group(&adev->dev->kobj, - &aca_attr->attr, - "ras"); -} - -int amdgpu_aca_add_handle(struct amdgpu_device *adev, struct aca_handle *handle, - const char *name, const struct aca_info *ras_info, void *data) -{ - struct amdgpu_aca *aca = &adev->aca; - int ret; - - if (!amdgpu_aca_is_enabled(adev)) - return 0; - - ret = add_aca_handle(adev, &aca->mgr, handle, name, ras_info, data); - if (ret) - return ret; - - return add_aca_sysfs(adev, handle); -} - -static void remove_aca_handle(struct aca_handle *handle) -{ - struct aca_handle_manager *mgr = handle->mgr; - - aca_fini_error_cache(handle); - list_del(&handle->node); - mgr->nr_handles--; -} - -static void remove_aca_sysfs(struct aca_handle *handle) -{ - struct amdgpu_device *adev = handle->adev; - struct device_attribute *aca_attr = &handle->aca_attr; - - if (adev->dev->kobj.sd) - sysfs_remove_file_from_group(&adev->dev->kobj, - &aca_attr->attr, - "ras"); -} - -void amdgpu_aca_remove_handle(struct aca_handle *handle) -{ - if (!handle || list_empty(&handle->node)) - return; - - remove_aca_sysfs(handle); - remove_aca_handle(handle); -} - -static int aca_manager_init(struct aca_handle_manager *mgr) -{ - INIT_LIST_HEAD(&mgr->list); - mgr->nr_handles = 0; - - return 0; -} - -static void aca_manager_fini(struct aca_handle_manager *mgr) -{ - struct aca_handle *handle, *tmp; - - if (list_empty(&mgr->list)) - return; - - list_for_each_entry_safe(handle, tmp, &mgr->list, node) - amdgpu_aca_remove_handle(handle); -} - -bool amdgpu_aca_is_enabled(struct amdgpu_device *adev) -{ - return (adev->aca.is_enabled || - adev->debug_enable_ras_aca); -} - -int amdgpu_aca_init(struct amdgpu_device *adev) -{ - struct amdgpu_aca *aca = &adev->aca; - int ret; - - atomic_set(&aca->ue_update_flag, 0); - - ret = aca_manager_init(&aca->mgr); - if (ret) - return ret; - - return 0; -} - -void amdgpu_aca_fini(struct amdgpu_device *adev) -{ - struct amdgpu_aca *aca = &adev->aca; - - aca_manager_fini(&aca->mgr); - - atomic_set(&aca->ue_update_flag, 0); -} - -int amdgpu_aca_reset(struct amdgpu_device *adev) -{ - struct amdgpu_aca *aca = &adev->aca; - - atomic_set(&aca->ue_update_flag, 0); - - return 0; -} - -void amdgpu_aca_set_smu_funcs(struct amdgpu_device *adev, const struct aca_smu_funcs *smu_funcs) -{ - struct amdgpu_aca *aca = &adev->aca; - - WARN_ON(aca->smu_funcs); - aca->smu_funcs = smu_funcs; -} - -int aca_bank_info_decode(struct aca_bank *bank, struct aca_bank_info *info) -{ - u64 ipid; - u32 instidhi, instidlo; - - if (!bank || !info) - return -EINVAL; - - ipid = bank->regs[ACA_REG_IDX_IPID]; - info->hwid = ACA_REG__IPID__HARDWAREID(ipid); - info->mcatype = ACA_REG__IPID__MCATYPE(ipid); - /* - * Unfied DieID Format: SAASS. A:AID, S:Socket. - * Unfied DieID[4:4] = InstanceId[0:0] - * Unfied DieID[0:3] = InstanceIdHi[0:3] - */ - instidhi = ACA_REG__IPID__INSTANCEIDHI(ipid); - instidlo = ACA_REG__IPID__INSTANCEIDLO(ipid); - info->die_id = ((instidhi >> 2) & 0x03); - info->socket_id = ((instidlo & 0x1) << 2) | (instidhi & 0x03); - - return 0; -} - -static int aca_bank_get_error_code(struct amdgpu_device *adev, struct aca_bank *bank) -{ - struct amdgpu_aca *aca = &adev->aca; - const struct aca_smu_funcs *smu_funcs = aca->smu_funcs; - - if (!smu_funcs || !smu_funcs->parse_error_code) - return -EOPNOTSUPP; - - return smu_funcs->parse_error_code(adev, bank); -} - -int aca_bank_check_error_codes(struct amdgpu_device *adev, struct aca_bank *bank, int *err_codes, int size) -{ - int i, error_code; - - if (!bank || !err_codes) - return -EINVAL; - - error_code = aca_bank_get_error_code(adev, bank); - if (error_code < 0) - return error_code; - - for (i = 0; i < size; i++) { - if (err_codes[i] == error_code) - return 0; - } - - return -EINVAL; -} - -int amdgpu_aca_smu_set_debug_mode(struct amdgpu_device *adev, bool en) -{ - struct amdgpu_aca *aca = &adev->aca; - const struct aca_smu_funcs *smu_funcs = aca->smu_funcs; - - if (!smu_funcs || !smu_funcs->set_debug_mode) - return -EOPNOTSUPP; - - return smu_funcs->set_debug_mode(adev, en); -} - -#if defined(CONFIG_DEBUG_FS) -static int amdgpu_aca_smu_debug_mode_set(void *data, u64 val) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)data; - int ret; - - ret = amdgpu_ras_set_aca_debug_mode(adev, val ? true : false); - if (ret) - return ret; - - dev_info(adev->dev, "amdgpu set smu aca debug mode %s success\n", val ? "on" : "off"); - - return 0; -} - -static void aca_dump_entry(struct seq_file *m, struct aca_bank *bank, enum aca_smu_type type, int idx) -{ - struct aca_bank_info info; - int i, ret; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return; - - seq_printf(m, "aca entry[%d].type: %s\n", idx, type == ACA_SMU_TYPE_UE ? "UE" : "CE"); - seq_printf(m, "aca entry[%d].info: socketid:%d aid:%d hwid:0x%03x mcatype:0x%04x\n", - idx, info.socket_id, info.die_id, info.hwid, info.mcatype); - - for (i = 0; i < ARRAY_SIZE(aca_regs); i++) - seq_printf(m, "aca entry[%d].regs[%d]: 0x%016llx\n", idx, aca_regs[i].reg_idx, bank->regs[aca_regs[i].reg_idx]); -} - -struct aca_dump_context { - struct seq_file *m; - int idx; -}; - -static int handler_aca_bank_dump(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - struct aca_dump_context *ctx = (struct aca_dump_context *)data; - - aca_dump_entry(ctx->m, bank, type, ctx->idx++); - - return handler_aca_log_bank_error(handle, bank, type, NULL); -} - -static int aca_dump_show(struct seq_file *m, enum aca_smu_type type) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)m->private; - struct aca_dump_context context = { - .m = m, - .idx = 0, - }; - - return aca_banks_update(adev, type, handler_aca_bank_dump, NULL, (void *)&context); -} - -static int aca_dump_ce_show(struct seq_file *m, void *unused) -{ - return aca_dump_show(m, ACA_SMU_TYPE_CE); -} - -static int aca_dump_ce_open(struct inode *inode, struct file *file) -{ - return single_open(file, aca_dump_ce_show, inode->i_private); -} - -static const struct file_operations aca_ce_dump_debug_fops = { - .owner = THIS_MODULE, - .open = aca_dump_ce_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int aca_dump_ue_show(struct seq_file *m, void *unused) -{ - return aca_dump_show(m, ACA_SMU_TYPE_UE); -} - -static int aca_dump_ue_open(struct inode *inode, struct file *file) -{ - return single_open(file, aca_dump_ue_show, inode->i_private); -} - -static const struct file_operations aca_ue_dump_debug_fops = { - .owner = THIS_MODULE, - .open = aca_dump_ue_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -DEFINE_DEBUGFS_ATTRIBUTE(aca_debug_mode_fops, NULL, amdgpu_aca_smu_debug_mode_set, "%llu\n"); -#endif - -void amdgpu_aca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root) -{ -#if defined(CONFIG_DEBUG_FS) - if (!root) - return; - - debugfs_create_file("aca_debug_mode", 0200, root, adev, &aca_debug_mode_fops); - debugfs_create_file("aca_ue_dump", 0400, root, adev, &aca_ue_dump_debug_fops); - debugfs_create_file("aca_ce_dump", 0400, root, adev, &aca_ce_dump_debug_fops); -#endif -} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.h deleted file mode 100644 index 38c88897e1ec..000000000000 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_aca.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2023 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. - * - */ - -#ifndef __AMDGPU_ACA_H__ -#define __AMDGPU_ACA_H__ - -#include <linux/list.h> - -struct ras_err_data; -struct ras_query_context; - -#define ACA_MAX_REGS_COUNT (16) - -#define ACA_REG_FIELD(x, h, l) (((x) & GENMASK_ULL(h, l)) >> l) -#define ACA_REG__STATUS__VAL(x) ACA_REG_FIELD(x, 63, 63) -#define ACA_REG__STATUS__OVERFLOW(x) ACA_REG_FIELD(x, 62, 62) -#define ACA_REG__STATUS__UC(x) ACA_REG_FIELD(x, 61, 61) -#define ACA_REG__STATUS__EN(x) ACA_REG_FIELD(x, 60, 60) -#define ACA_REG__STATUS__MISCV(x) ACA_REG_FIELD(x, 59, 59) -#define ACA_REG__STATUS__ADDRV(x) ACA_REG_FIELD(x, 58, 58) -#define ACA_REG__STATUS__PCC(x) ACA_REG_FIELD(x, 57, 57) -#define ACA_REG__STATUS__ERRCOREIDVAL(x) ACA_REG_FIELD(x, 56, 56) -#define ACA_REG__STATUS__TCC(x) ACA_REG_FIELD(x, 55, 55) -#define ACA_REG__STATUS__SYNDV(x) ACA_REG_FIELD(x, 53, 53) -#define ACA_REG__STATUS__CECC(x) ACA_REG_FIELD(x, 46, 46) -#define ACA_REG__STATUS__UECC(x) ACA_REG_FIELD(x, 45, 45) -#define ACA_REG__STATUS__DEFERRED(x) ACA_REG_FIELD(x, 44, 44) -#define ACA_REG__STATUS__POISON(x) ACA_REG_FIELD(x, 43, 43) -#define ACA_REG__STATUS__SCRUB(x) ACA_REG_FIELD(x, 40, 40) -#define ACA_REG__STATUS__ERRCOREID(x) ACA_REG_FIELD(x, 37, 32) -#define ACA_REG__STATUS__ADDRLSB(x) ACA_REG_FIELD(x, 29, 24) -#define ACA_REG__STATUS__ERRORCODEEXT(x) ACA_REG_FIELD(x, 21, 16) -#define ACA_REG__STATUS__ERRORCODE(x) ACA_REG_FIELD(x, 15, 0) - -#define ACA_REG__IPID__MCATYPE(x) ACA_REG_FIELD(x, 63, 48) -#define ACA_REG__IPID__INSTANCEIDHI(x) ACA_REG_FIELD(x, 47, 44) -#define ACA_REG__IPID__HARDWAREID(x) ACA_REG_FIELD(x, 43, 32) -#define ACA_REG__IPID__INSTANCEIDLO(x) ACA_REG_FIELD(x, 31, 0) - -#define ACA_REG__MISC0__VALID(x) ACA_REG_FIELD(x, 63, 63) -#define ACA_REG__MISC0__OVRFLW(x) ACA_REG_FIELD(x, 48, 48) -#define ACA_REG__MISC0__ERRCNT(x) ACA_REG_FIELD(x, 43, 32) - -#define ACA_REG__SYND__ERRORINFORMATION(x) ACA_REG_FIELD(x, 17, 0) - -/* NOTE: The following codes refers to the smu header file */ -#define ACA_EXTERROR_CODE_CE 0x3a -#define ACA_EXTERROR_CODE_FAULT 0x3b - -#define ACA_ERROR_UE_MASK BIT_MASK(ACA_ERROR_TYPE_UE) -#define ACA_ERROR_CE_MASK BIT_MASK(ACA_ERROR_TYPE_CE) -#define ACA_ERROR_DEFERRED_MASK BIT_MASK(ACA_ERROR_TYPE_DEFERRED) - -#define mmSMNAID_AID0_MCA_SMU 0x03b30400 /* SMN AID AID0 */ -#define mmSMNAID_XCD0_MCA_SMU 0x36430400 /* SMN AID XCD0 */ -#define mmSMNAID_XCD1_MCA_SMU 0x38430400 /* SMN AID XCD1 */ -#define mmSMNXCD_XCD0_MCA_SMU 0x40430400 /* SMN XCD XCD0 */ - -#define ACA_BANK_ERR_IS_DEFFERED(bank) \ - (ACA_REG__STATUS__POISON((bank)->regs[ACA_REG_IDX_STATUS]) || \ - ACA_REG__STATUS__DEFERRED((bank)->regs[ACA_REG_IDX_STATUS])) - -enum aca_reg_idx { - ACA_REG_IDX_CTL = 0, - ACA_REG_IDX_STATUS = 1, - ACA_REG_IDX_ADDR = 2, - ACA_REG_IDX_MISC0 = 3, - ACA_REG_IDX_CONFIG = 4, - ACA_REG_IDX_IPID = 5, - ACA_REG_IDX_SYND = 6, - ACA_REG_IDX_DESTAT = 8, - ACA_REG_IDX_DEADDR = 9, - ACA_REG_IDX_CTL_MASK = 10, - ACA_REG_IDX_COUNT = 16, -}; - -enum aca_hwip_type { - ACA_HWIP_TYPE_UNKNOW = -1, - ACA_HWIP_TYPE_PSP = 0, - ACA_HWIP_TYPE_UMC, - ACA_HWIP_TYPE_SMU, - ACA_HWIP_TYPE_PCS_XGMI, - ACA_HWIP_TYPE_COUNT, -}; - -enum aca_error_type { - ACA_ERROR_TYPE_INVALID = -1, - ACA_ERROR_TYPE_UE = 0, - ACA_ERROR_TYPE_CE, - ACA_ERROR_TYPE_DEFERRED, - ACA_ERROR_TYPE_COUNT -}; - -enum aca_smu_type { - ACA_SMU_TYPE_INVALID = -1, - ACA_SMU_TYPE_UE = 0, - ACA_SMU_TYPE_CE, - ACA_SMU_TYPE_COUNT, -}; - -struct aca_hwip { - int hwid; - int mcatype; -}; - -struct aca_bank { - enum aca_error_type aca_err_type; - enum aca_smu_type smu_err_type; - u64 regs[ACA_MAX_REGS_COUNT]; -}; - -struct aca_bank_node { - struct aca_bank bank; - struct list_head node; -}; - -struct aca_banks { - int nr_banks; - struct list_head list; -}; - -struct aca_bank_info { - int die_id; - int socket_id; - int hwid; - int mcatype; -}; - -struct aca_bank_error { - struct list_head node; - struct aca_bank_info info; - u64 count; -}; - -struct aca_error { - struct list_head list; - struct mutex lock; - enum aca_error_type type; - int nr_errors; -}; - -struct aca_handle_manager { - struct list_head list; - int nr_handles; -}; - -struct aca_error_cache { - struct aca_error errors[ACA_ERROR_TYPE_COUNT]; -}; - -struct aca_handle { - struct list_head node; - enum aca_hwip_type hwip; - struct amdgpu_device *adev; - struct aca_handle_manager *mgr; - struct aca_error_cache error_cache; - const struct aca_bank_ops *bank_ops; - struct device_attribute aca_attr; - char attr_name[64]; - const char *name; - u32 mask; - void *data; -}; - -struct aca_bank_ops { - int (*aca_bank_parser)(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type, void *data); - bool (*aca_bank_is_valid)(struct aca_handle *handle, struct aca_bank *bank, enum aca_smu_type type, - void *data); -}; - -struct aca_smu_funcs { - int max_ue_bank_count; - int max_ce_bank_count; - int (*set_debug_mode)(struct amdgpu_device *adev, bool enable); - int (*get_valid_aca_count)(struct amdgpu_device *adev, enum aca_smu_type type, u32 *count); - int (*get_valid_aca_bank)(struct amdgpu_device *adev, enum aca_smu_type type, int idx, struct aca_bank *bank); - int (*parse_error_code)(struct amdgpu_device *adev, struct aca_bank *bank); -}; - -struct amdgpu_aca { - struct aca_handle_manager mgr; - const struct aca_smu_funcs *smu_funcs; - atomic_t ue_update_flag; - bool is_enabled; -}; - -struct aca_info { - enum aca_hwip_type hwip; - const struct aca_bank_ops *bank_ops; - u32 mask; -}; - -int amdgpu_aca_init(struct amdgpu_device *adev); -void amdgpu_aca_fini(struct amdgpu_device *adev); -int amdgpu_aca_reset(struct amdgpu_device *adev); -void amdgpu_aca_set_smu_funcs(struct amdgpu_device *adev, const struct aca_smu_funcs *smu_funcs); -bool amdgpu_aca_is_enabled(struct amdgpu_device *adev); - -int aca_bank_info_decode(struct aca_bank *bank, struct aca_bank_info *info); -int aca_bank_check_error_codes(struct amdgpu_device *adev, struct aca_bank *bank, int *err_codes, int size); - -int amdgpu_aca_add_handle(struct amdgpu_device *adev, struct aca_handle *handle, - const char *name, const struct aca_info *aca_info, void *data); -void amdgpu_aca_remove_handle(struct aca_handle *handle); -int amdgpu_aca_get_error_data(struct amdgpu_device *adev, struct aca_handle *handle, - enum aca_error_type type, struct ras_err_data *err_data, - struct ras_query_context *qctx); -int amdgpu_aca_smu_set_debug_mode(struct amdgpu_device *adev, bool en); -void amdgpu_aca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root); -int aca_error_cache_log_bank_error(struct aca_handle *handle, struct aca_bank_info *info, - enum aca_error_type type, u64 count); -#endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c index 4c732e0f776e..9014678d75ab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c @@ -508,6 +508,7 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) u32 val = 0; u32 count = 0; struct amdgpu_device *adev = ip_block->adev; + int ret = 0; /* return early if no ACP */ if (!adev->acp.acp_genpd) { @@ -529,7 +530,8 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) break; if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } udelay(100); } @@ -546,21 +548,24 @@ static int acp_hw_fini(struct amdgpu_ip_block *ip_block) break; if (--count == 0) { dev_err(&adev->pdev->dev, "Failed to reset ACP\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto out; } udelay(100); } - +out: device_for_each_child(adev->acp.parent, NULL, acp_genpd_remove_device); mfd_remove_devices(adev->acp.parent); kfree(adev->acp.i2s_pdata); kfree(adev->acp.acp_res); + pm_genpd_remove(&adev->acp.acp_genpd->gpd); kfree(adev->acp.acp_genpd); + adev->acp.acp_genpd = NULL; kfree(adev->acp.acp_cell); - return 0; + return ret; } static int acp_suspend(struct amdgpu_ip_block *ip_block) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c index 516ab9cf88fc..7f5abb03be1b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_acpi.c @@ -140,13 +140,15 @@ static struct amdgpu_acpi_priv { * @atif: atif structure * @function: the ATIF function to execute * @params: ATIF function params + * @min_size: minimum size of the expected output buffer in bytes * * Executes the requested ATIF function (all asics). * Returns a pointer to the acpi output buffer. */ static union acpi_object *amdgpu_atif_call(struct amdgpu_atif *atif, int function, - struct acpi_buffer *params) + struct acpi_buffer *params, + size_t min_size) { acpi_status status; union acpi_object *obj; @@ -189,6 +191,28 @@ static union acpi_object *amdgpu_atif_call(struct amdgpu_atif *atif, return NULL; } + if (obj->buffer.length < sizeof(u16)) { + DRM_DEBUG_DRIVER("ATIF buffer too small to hold size field: %u\n", + obj->buffer.length); + kfree(obj); + return NULL; + } + + if (obj->buffer.length < *(u16 *)obj->buffer.pointer) { + DRM_DEBUG_DRIVER("ATIF buffer length mismatch: reported %u, actual %u\n", + *(u16 *)obj->buffer.pointer, + obj->buffer.length); + kfree(obj); + return NULL; + } + + if (*(u16 *)obj->buffer.pointer < min_size) { + DRM_DEBUG_DRIVER("ATIF buffer too small: expected %zu, got %u\n", + min_size, *(u16 *)obj->buffer.pointer); + kfree(obj); + return NULL; + } + return obj; } @@ -251,19 +275,14 @@ int amdgpu_atif_verify_interface(struct amdgpu_atif *atif) size_t size; int err = 0; - info = amdgpu_atif_call(atif, ATIF_FUNCTION_VERIFY_INTERFACE, NULL); + info = amdgpu_atif_call(atif, ATIF_FUNCTION_VERIFY_INTERFACE, NULL, + sizeof(output)); if (!info) return -EIO; memset(&output, 0, sizeof(output)); - size = *(u16 *) info->buffer.pointer; - if (size < 12) { - DRM_INFO("ATIF buffer is too small: %zu\n", size); - err = -EINVAL; - goto out; - } - size = min(sizeof(output), size); + size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer); memcpy(&output, info->buffer.pointer, size); @@ -273,7 +292,6 @@ int amdgpu_atif_verify_interface(struct amdgpu_atif *atif) amdgpu_atif_parse_notification(&atif->notifications, output.notification_mask); amdgpu_atif_parse_functions(&atif->functions, output.function_bits); -out: kfree(info); return err; } @@ -299,20 +317,14 @@ int amdgpu_atif_get_notification_params(struct amdgpu_atif *atif) int err = 0; info = amdgpu_atif_call(atif, ATIF_FUNCTION_GET_SYSTEM_PARAMETERS, - NULL); + NULL, offsetof(struct atif_system_params, command_code)); if (!info) { err = -EIO; goto out; } - size = *(u16 *) info->buffer.pointer; - if (size < 10) { - err = -EINVAL; - goto out; - } - memset(¶ms, 0, sizeof(params)); - size = min(sizeof(params), size); + size = min(sizeof(params), (size_t)*(u16 *)info->buffer.pointer); memcpy(¶ms, info->buffer.pointer, size); DRM_DEBUG_DRIVER("SYSTEM_PARAMS: mask = %#x, flags = %#x\n", @@ -376,20 +388,14 @@ int amdgpu_atif_query_backlight_caps(struct amdgpu_atif *atif) info = amdgpu_atif_call(atif, ATIF_FUNCTION_QUERY_BRIGHTNESS_TRANSFER_CHARACTERISTICS, - ¶ms); + ¶ms, offsetof(struct atif_qbtc_output, data_points)); if (!info) { err = -EIO; goto out; } - size = *(u16 *) info->buffer.pointer; - if (size < 10) { - err = -EINVAL; - goto out; - } - memset(&characteristics, 0, sizeof(characteristics)); - size = min(sizeof(characteristics), size); + size = min(sizeof(characteristics), (size_t)*(u16 *)info->buffer.pointer); memcpy(&characteristics, info->buffer.pointer, size); atif->backlight_caps.caps_valid = true; @@ -427,24 +433,18 @@ static int amdgpu_atif_get_sbios_requests(struct amdgpu_atif *atif, int count = 0; info = amdgpu_atif_call(atif, ATIF_FUNCTION_GET_SYSTEM_BIOS_REQUESTS, - NULL); + NULL, sizeof(*req)); if (!info) return -EIO; - size = *(u16 *)info->buffer.pointer; - if (size < 0xd) { - count = -EINVAL; - goto out; - } memset(req, 0, sizeof(*req)); - size = min(sizeof(*req), size); + size = min(sizeof(*req), (size_t)*(u16 *)info->buffer.pointer); memcpy(req, info->buffer.pointer, size); DRM_DEBUG_DRIVER("SBIOS pending requests: %#x\n", req->pending); count = hweight32(req->pending); -out: kfree(info); return count; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index da325863ad76..c693c508df1a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -957,3 +957,17 @@ int amdgpu_amdkfd_config_sq_perfmon(struct amdgpu_device *adev, uint32_t xcp_id, return r; } + +/* Reset an MES queue */ +int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev, + uint32_t node_id, + int queue_type, + int pipe, int queue, + unsigned int db) +{ + if (!adev->kfd.init_complete) + return 0; + + return kgd2kfd_reset_mes_queue(adev->kfd.dev, node_id, queue_type, + pipe, queue, db); +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index e443a7277299..338412a750ed 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -210,6 +210,7 @@ int amdgpu_amdkfd_evict_userptr(struct mmu_interval_notifier *mni, int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo, uint32_t domain, struct dma_fence *fence); +int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms); #else static inline bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm) @@ -241,6 +242,11 @@ int amdgpu_amdkfd_bo_validate_and_fence(struct amdgpu_bo *bo, { return 0; } +static inline +int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms) +{ + return -EOPNOTSUPP; +} #endif /* Shared API */ int amdgpu_amdkfd_alloc_kernel_mem(struct amdgpu_device *adev, size_t size, @@ -275,7 +281,11 @@ int amdgpu_amdkfd_stop_sched(struct amdgpu_device *adev, uint32_t node_id); int amdgpu_amdkfd_config_sq_perfmon(struct amdgpu_device *adev, uint32_t xcp_id, bool core_override_enable, bool reg_override_enable, bool perfmon_override_enable); bool amdgpu_amdkfd_compute_active(struct amdgpu_device *adev, uint32_t node_id); - +int amdgpu_amdkfd_reset_mes_queue(struct amdgpu_device *adev, + uint32_t node_id, + int queue_type, + int pipe, int queue, + unsigned int db); /* Read user wptr from a specified user address space with page fault * disabled. The memory must be pinned and mapped to the hardware when @@ -326,9 +336,9 @@ int amdgpu_amdkfd_gpuvm_unmap_memory_from_gpu( int amdgpu_amdkfd_gpuvm_dmaunmap_mem(struct kgd_mem *mem, void *drm_priv); int amdgpu_amdkfd_gpuvm_sync_memory( struct amdgpu_device *adev, struct kgd_mem *mem, bool intr); -int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem, - void **kptr, uint64_t *size); -void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem); +int amdgpu_amdkfd_gpuvm_map_bo_to_kernel(struct kgd_mem *mem, void **kptr, + u64 *size, u32 domain); +void amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(struct kgd_mem *mem); int amdgpu_amdkfd_map_gtt_bo_to_gart(struct amdgpu_bo *bo, struct amdgpu_bo **bo_gart); @@ -446,6 +456,9 @@ bool kgd2kfd_vmfault_fast_path(struct amdgpu_device *adev, struct amdgpu_iv_entr bool retry_fault); void kgd2kfd_lock_kfd(void); void kgd2kfd_teardown_processes(struct amdgpu_device *adev); +int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id, + int queue_type, int pipe, int queue, + unsigned int db); #else static inline int kgd2kfd_init(void) @@ -576,5 +589,12 @@ static inline void kgd2kfd_teardown_processes(struct amdgpu_device *adev) { } +static inline int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id, + int queue_type, int pipe, int queue, + unsigned int db) +{ + return 0; +} + #endif #endif /* AMDGPU_AMDKFD_H_INCLUDED */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c index 6ed399163547..bc079b95fc52 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gc_9_4_3.c @@ -530,6 +530,66 @@ static uint32_t kgd_v9_4_3_ptl_ctrl(struct amdgpu_device *adev, ptl_state, fmt1, fmt2); } +static int kgd_gfx_v9_4_3_hqd_sdma_get_counter(struct amdgpu_device *adev, + void *mqd, uint32_t num_sdma_queues_per_eng, + uint64_t *val) +{ + struct v9_sdma_mqd *m = get_sdma_mqd(mqd); + uint32_t sdma_rlc_reg_offset = 0; + uint32_t sdma_rlc_rb_cntl; + uint32_t engine_id, queue_id; + uint32_t engines = adev->sdma.num_instances; + uint32_t sdma_rlcx_rb_base, sdma_rlcx_rb_base_hi; + bool found = false; + + if (!m) + return -EINVAL; + + if (((amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 3) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4)) && + adev->gfx.mec_fw_version < 194) || + (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0) && + adev->gfx.mec_fw_version < 44)) { + pr_warn_once("MEC FW doesn't support SDMA counter!\n"); + return -EOPNOTSUPP; + } + + /* SDMA doesn't support over-subscription, there must be + * a HQD associated with a MQD, so found must be true in + * the finding loop. + */ + for (engine_id = 0; engine_id < engines && !found; engine_id++) { + for (queue_id = 0; queue_id < num_sdma_queues_per_eng; queue_id++) { + sdma_rlc_reg_offset = get_sdma_rlc_reg_offset(adev, + engine_id, queue_id); + sdma_rlcx_rb_base = RREG32(sdma_rlc_reg_offset + + regSDMA_RLC0_RB_BASE); + sdma_rlcx_rb_base_hi = RREG32(sdma_rlc_reg_offset + + regSDMA_RLC0_RB_BASE_HI); + + if (m->sdmax_rlcx_rb_base == sdma_rlcx_rb_base && + m->sdmax_rlcx_rb_base_hi == sdma_rlcx_rb_base_hi) { + found = true; + break; + } + } + } + + sdma_rlc_rb_cntl = RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_RB_CNTL); + + /* Read sdma activity counter from utilization register + * if hw queue is enabled, otherwise read from MQD. + */ + if (sdma_rlc_rb_cntl & SDMA_RLC0_RB_CNTL__RB_ENABLE_MASK) + *val = (uint64_t)RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_UTILIZATION_HI) << 32 | + RREG32(sdma_rlc_reg_offset + regSDMA_RLC0_UTILIZATION_LO); + else + *val = (uint64_t)m->sdmax_rlcx_utilization_hi << 32 | + m->sdmax_rlcx_utilization_lo; + + return 0; +} + const struct kfd2kgd_calls gc_9_4_3_kfd2kgd = { .program_sh_mem_settings = kgd_gfx_v9_program_sh_mem_settings, .set_pasid_vmid_mapping = kgd_gfx_v9_4_3_set_pasid_vmid_mapping, @@ -566,5 +626,6 @@ const struct kfd2kgd_calls gc_9_4_3_kfd2kgd = { .hqd_get_pq_addr = kgd_gfx_v9_hqd_get_pq_addr, .hqd_reset = kgd_gfx_v9_hqd_reset, .hqd_sdma_get_doorbell = kgd_gfx_v9_4_3_hqd_sdma_get_doorbell, - .ptl_ctrl = kgd_v9_4_3_ptl_ctrl + .ptl_ctrl = kgd_v9_4_3_ptl_ctrl, + .hqd_sdma_get_counter = kgd_gfx_v9_4_3_hqd_sdma_get_counter }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 35fe2c974699..20831dbebc31 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -2271,11 +2271,14 @@ err_reserve_bo_failed: return ret; } -/** amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel() - Map a GTT BO for kernel CPU access +/** amdgpu_amdkfd_gpuvm_map_bo_to_kernel() - Map GTT or VRAM BO for kernel CPU access * * @mem: Buffer object to be mapped for CPU access * @kptr[out]: pointer in kernel CPU address space * @size[out]: size of the buffer + * @domain[IN]: domain for pinning (AMDGPU_GEM_DOMAIN_GTT, AMDGPU_GEM_DOMAIN_VRAM, + * or their combination to let the driver choose). CPU visibility is + * automatically enforced by amdgpu_bo_pin() * * Pins the BO and maps it for kernel CPU access. The eviction fence is removed * from the BO, since pinned BOs cannot be evicted. The bo must remain on the @@ -2284,8 +2287,8 @@ err_reserve_bo_failed: * * Return: 0 on success, error code on failure */ -int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem, - void **kptr, uint64_t *size) +int amdgpu_amdkfd_gpuvm_map_bo_to_kernel(struct kgd_mem *mem, void **kptr, + u64 *size, u32 domain) { int ret; struct amdgpu_bo *bo = mem->bo; @@ -2295,6 +2298,11 @@ int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem, return -EINVAL; } + if (!(domain & (AMDGPU_GEM_DOMAIN_GTT | AMDGPU_GEM_DOMAIN_VRAM))) { + pr_debug("Invalid domain 0x%x for kernel mapping\n", domain); + return -EINVAL; + } + mutex_lock(&mem->process_info->lock); ret = amdgpu_bo_reserve(bo, true); @@ -2303,7 +2311,7 @@ int amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(struct kgd_mem *mem, goto bo_reserve_failed; } - ret = amdgpu_bo_pin(bo, AMDGPU_GEM_DOMAIN_GTT); + ret = amdgpu_bo_pin(bo, domain); if (ret) { pr_err("Failed to pin bo. ret %d\n", ret); goto pin_failed; @@ -2336,7 +2344,7 @@ bo_reserve_failed: return ret; } -/** amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel() - Unmap a GTT BO for kernel CPU access +/** amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel() - Unmap GTT or VRAM BO for kernel CPU access * * @mem: Buffer object to be unmapped for CPU access * @@ -2344,7 +2352,7 @@ bo_reserve_failed: * eviction fence, so this function should only be used for cleanup before the * BO is destroyed. */ -void amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(struct kgd_mem *mem) +void amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(struct kgd_mem *mem) { struct amdgpu_bo *bo = mem->bo; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c index acd22bff1882..27c0dc8f6137 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c @@ -1923,7 +1923,7 @@ int amdgpu_atombios_init(struct amdgpu_device *adev) atom_card_info->pll_read = cail_pll_read; atom_card_info->pll_write = cail_pll_write; - adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios); + adev->mode_info.atom_context = amdgpu_atom_parse(atom_card_info, adev->bios, adev->bios_size); if (!adev->mode_info.atom_context) { amdgpu_atombios_fini(adev); return -ENOMEM; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c index 3893e6fc2f03..e2a4644896ca 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atpx_handler.c @@ -89,6 +89,15 @@ bool amdgpu_is_atpx_hybrid(void) return amdgpu_atpx_priv.atpx.is_hybrid; } +static bool amdgpu_atpx_buffer_validate(const union acpi_object *obj, + size_t min_size) +{ + return obj && obj->type == ACPI_TYPE_BUFFER && + obj->buffer.length >= sizeof(u16) && + obj->buffer.length >= *(u16 *)obj->buffer.pointer && + *(u16 *)obj->buffer.pointer >= min_size; +} + /** * amdgpu_atpx_call - call an ATPX method * @@ -179,15 +188,15 @@ static int amdgpu_atpx_validate(struct amdgpu_atpx *atpx) if (!info) return -EIO; - memset(&output, 0, sizeof(output)); - - size = *(u16 *) info->buffer.pointer; - if (size < 10) { - pr_err("ATPX buffer is too small: %zu\n", size); + if (!amdgpu_atpx_buffer_validate(info, sizeof(output))) { + pr_err("Invalid ATPX GET_PX_PARAMETERS response\n"); kfree(info); return -EINVAL; } - size = min(sizeof(output), size); + + memset(&output, 0, sizeof(output)); + + size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer); memcpy(&output, info->buffer.pointer, size); @@ -258,15 +267,15 @@ static int amdgpu_atpx_verify_interface(struct amdgpu_atpx *atpx) if (!info) return -EIO; - memset(&output, 0, sizeof(output)); - - size = *(u16 *) info->buffer.pointer; - if (size < 8) { - pr_err("ATPX buffer is too small: %zu\n", size); + if (!amdgpu_atpx_buffer_validate(info, sizeof(output))) { + pr_err("Invalid ATPX VERIFY_INTERFACE response\n"); err = -EINVAL; goto out; } - size = min(sizeof(output), size); + + memset(&output, 0, sizeof(output)); + + size = min(sizeof(output), (size_t)*(u16 *)info->buffer.pointer); memcpy(&output, info->buffer.pointer, size); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c index aa039e148a5e..3ebdd792feec 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c @@ -296,8 +296,14 @@ static int amdgpu_atrm_call(acpi_handle atrm_handle, uint8_t *bios, } obj = (union acpi_object *)buffer.pointer; - memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length); - len = obj->buffer.length; + if (!obj || obj->type != ACPI_TYPE_BUFFER) { + DRM_ERROR("ATRM returned an invalid object\n"); + kfree(buffer.pointer); + return -EINVAL; + } + + len = min_t(size_t, obj->buffer.length, len); + memcpy(bios+offset, obj->buffer.pointer, len); kfree(buffer.pointer); return len; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c index 43864df8af04..ce1d08f112a8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.c @@ -38,14 +38,6 @@ #define AMDGPU_BO_LIST_NUM_BUCKETS (AMDGPU_BO_LIST_MAX_PRIORITY + 1) #define AMDGPU_BO_LIST_MAX_ENTRIES (128 * 1024) -static void amdgpu_bo_list_free_rcu(struct rcu_head *rcu) -{ - struct amdgpu_bo_list *list = container_of(rcu, struct amdgpu_bo_list, - rhead); - mutex_destroy(&list->bo_list_mutex); - kvfree(list); -} - static void amdgpu_bo_list_free(struct kref *ref) { struct amdgpu_bo_list *list = container_of(ref, struct amdgpu_bo_list, @@ -54,7 +46,8 @@ static void amdgpu_bo_list_free(struct kref *ref) amdgpu_bo_list_for_each_entry(e, list) amdgpu_bo_unref(&e->bo); - call_rcu(&list->rhead, amdgpu_bo_list_free_rcu); + + kvfree(list); } static int amdgpu_bo_list_entry_cmp(const void *_a, const void *_b) @@ -66,9 +59,9 @@ static int amdgpu_bo_list_entry_cmp(const void *_a, const void *_b) return (int)a->priority - (int)b->priority; } -int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, - struct drm_amdgpu_bo_list_entry *info, - size_t num_entries, struct amdgpu_bo_list **result) +struct amdgpu_bo_list * +amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, + struct drm_amdgpu_bo_list_entry *info, size_t num_entries) { unsigned last_entry = 0, first_userptr = num_entries; struct amdgpu_bo_list_entry *array; @@ -79,7 +72,7 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, list = kvzalloc_flex(*list, entries, num_entries); if (!list) - return -ENOMEM; + return ERR_PTR(-ENOMEM); kref_init(&list->refcount); @@ -134,9 +127,7 @@ int amdgpu_bo_list_create(struct amdgpu_device *adev, struct drm_file *filp, trace_amdgpu_cs_bo_status(list->num_entries, total_size); - mutex_init(&list->bo_list_mutex); - *result = list; - return 0; + return list; error_free: for (i = 0; i < last_entry; ++i) @@ -144,150 +135,125 @@ error_free: for (i = first_userptr; i < num_entries; ++i) amdgpu_bo_unref(&array[i].bo); kvfree(list); - return r; + return ERR_PTR(r); } -static void amdgpu_bo_list_destroy(struct amdgpu_fpriv *fpriv, int id) +struct amdgpu_bo_list *amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, u32 id) { struct amdgpu_bo_list *list; - mutex_lock(&fpriv->bo_list_lock); - list = idr_remove(&fpriv->bo_list_handles, id); - mutex_unlock(&fpriv->bo_list_lock); + xa_lock(&fpriv->bo_list_handles); + list = xa_load(&fpriv->bo_list_handles, id); if (list) - kref_put(&list->refcount, amdgpu_bo_list_free); -} - -int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id, - struct amdgpu_bo_list **result) -{ - rcu_read_lock(); - *result = idr_find(&fpriv->bo_list_handles, id); - - if (*result && kref_get_unless_zero(&(*result)->refcount)) { - rcu_read_unlock(); - return 0; - } + kref_get(&list->refcount); + else + list = ERR_PTR(-ENOENT); + xa_unlock(&fpriv->bo_list_handles); - rcu_read_unlock(); - *result = NULL; - return -ENOENT; + return list; } void amdgpu_bo_list_put(struct amdgpu_bo_list *list) { - kref_put(&list->refcount, amdgpu_bo_list_free); + if (list) + kref_put(&list->refcount, amdgpu_bo_list_free); } -int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in, - struct drm_amdgpu_bo_list_entry **info_param) +struct drm_amdgpu_bo_list_entry * +amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in) { - const uint32_t info_size = sizeof(struct drm_amdgpu_bo_list_entry); const void __user *uptr = u64_to_user_ptr(in->bo_info_ptr); - const uint32_t bo_info_size = in->bo_info_size; const uint32_t bo_number = in->bo_number; - struct drm_amdgpu_bo_list_entry *info; if (bo_number > AMDGPU_BO_LIST_MAX_ENTRIES) - return -EINVAL; + return ERR_PTR(-EINVAL); - /* copy the handle array from userspace to a kernel buffer */ - if (likely(info_size == bo_info_size)) { - info = vmemdup_array_user(uptr, bo_number, info_size); - if (IS_ERR(info)) - return PTR_ERR(info); - } else { - const uint32_t bytes = min(bo_info_size, info_size); - unsigned i; - - info = kvmalloc_array(bo_number, info_size, GFP_KERNEL); - if (!info) - return -ENOMEM; - - memset(info, 0, bo_number * info_size); - for (i = 0; i < bo_number; ++i, uptr += bo_info_size) { - if (copy_from_user(&info[i], uptr, bytes)) { - kvfree(info); - return -EFAULT; - } - } - } + if (in->bo_info_size != sizeof(struct drm_amdgpu_bo_list_entry)) + return ERR_PTR(-EINVAL); - *info_param = info; - return 0; + return vmemdup_array_user(uptr, bo_number, + sizeof(struct drm_amdgpu_bo_list_entry)); } int amdgpu_bo_list_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { - struct amdgpu_device *adev = drm_to_adev(dev); struct amdgpu_fpriv *fpriv = filp->driver_priv; + struct amdgpu_device *adev = drm_to_adev(dev); + struct amdgpu_bo_list *list, *prev, *curr; union drm_amdgpu_bo_list *args = data; uint32_t handle = args->in.list_handle; - struct drm_amdgpu_bo_list_entry *info = NULL; - struct amdgpu_bo_list *list, *old; + struct drm_amdgpu_bo_list_entry *info; int r; - r = amdgpu_bo_create_list_entry_array(&args->in, &info); - if (r) - return r; - switch (args->in.operation) { case AMDGPU_BO_LIST_OP_CREATE: - r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number, - &list); - if (r) - goto error_free; + case AMDGPU_BO_LIST_OP_UPDATE: + info = amdgpu_bo_create_list_entry_array(&args->in); + if (IS_ERR(info)) + return PTR_ERR(info); - mutex_lock(&fpriv->bo_list_lock); - r = idr_alloc(&fpriv->bo_list_handles, list, 1, 0, GFP_KERNEL); - mutex_unlock(&fpriv->bo_list_lock); - if (r < 0) { - goto error_put_list; - } + list = amdgpu_bo_list_create(adev, filp, info, + args->in.bo_number); + kvfree(info); + if (IS_ERR(list)) + return PTR_ERR(list); - handle = r; break; case AMDGPU_BO_LIST_OP_DESTROY: - amdgpu_bo_list_destroy(fpriv, handle); + list = xa_erase(&fpriv->bo_list_handles, handle); + amdgpu_bo_list_put(list); handle = 0; + break; - case AMDGPU_BO_LIST_OP_UPDATE: - r = amdgpu_bo_list_create(adev, filp, info, args->in.bo_number, - &list); + default: + return -EINVAL; + }; + + switch (args->in.operation) { + case AMDGPU_BO_LIST_OP_CREATE: + r = xa_alloc(&fpriv->bo_list_handles, &handle, list, + xa_limit_32b, GFP_KERNEL); if (r) - goto error_free; + goto error_put_list; + + break; - mutex_lock(&fpriv->bo_list_lock); - old = idr_replace(&fpriv->bo_list_handles, list, handle); - mutex_unlock(&fpriv->bo_list_lock); + case AMDGPU_BO_LIST_OP_UPDATE: + curr = xa_load(&fpriv->bo_list_handles, handle); + if (!curr) { + r = -ENOENT; + goto error_put_list; + } - if (IS_ERR(old)) { - r = PTR_ERR(old); + prev = xa_cmpxchg(&fpriv->bo_list_handles, handle, curr, list, + GFP_KERNEL); + if (xa_is_err(prev)) { + r = xa_err(prev); + goto error_put_list; + } else if (prev != curr) { + r = -ENOENT; goto error_put_list; } - amdgpu_bo_list_put(old); + amdgpu_bo_list_put(curr); break; + case AMDGPU_BO_LIST_OP_DESTROY: default: - r = -EINVAL; - goto error_free; + /* Handled above. */ + break; } memset(args, 0, sizeof(*args)); args->out.list_handle = handle; - kvfree(info); return 0; error_put_list: amdgpu_bo_list_put(list); - -error_free: - kvfree(info); return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h index 2b5e7c46a39d..bde912150824 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bo_list.h @@ -43,7 +43,6 @@ struct amdgpu_bo_list_entry { }; struct amdgpu_bo_list { - struct rcu_head rhead; struct kref refcount; struct amdgpu_bo *gds_obj; struct amdgpu_bo *gws_obj; @@ -51,24 +50,19 @@ struct amdgpu_bo_list { unsigned first_userptr; unsigned num_entries; - /* Protect access during command submission. - */ - struct mutex bo_list_mutex; - struct amdgpu_bo_list_entry entries[] __counted_by(num_entries); }; -int amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, int id, - struct amdgpu_bo_list **result); +struct amdgpu_bo_list *amdgpu_bo_list_get(struct amdgpu_fpriv *fpriv, u32 id); void amdgpu_bo_list_put(struct amdgpu_bo_list *list); -int amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in, - struct drm_amdgpu_bo_list_entry **info_param); +struct drm_amdgpu_bo_list_entry * +amdgpu_bo_create_list_entry_array(struct drm_amdgpu_bo_list_in *in); -int amdgpu_bo_list_create(struct amdgpu_device *adev, - struct drm_file *filp, - struct drm_amdgpu_bo_list_entry *info, - size_t num_entries, - struct amdgpu_bo_list **list); +struct amdgpu_bo_list * +amdgpu_bo_list_create(struct amdgpu_device *adev, + struct drm_file *filp, + struct drm_amdgpu_bo_list_entry *info, + size_t num_entries); #define amdgpu_bo_list_for_each_entry(e, list) \ for (e = list->entries; \ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c index d5e59c24d907..6fb129025761 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.c @@ -289,40 +289,6 @@ struct cper_hdr *amdgpu_cper_alloc_entry(struct amdgpu_device *adev, return hdr; } -int amdgpu_cper_generate_ue_record(struct amdgpu_device *adev, - struct aca_bank *bank) -{ - struct cper_hdr *fatal = NULL; - struct cper_sec_crashdump_reg_data reg_data = { 0 }; - struct amdgpu_ring *ring = &adev->cper.ring_buf; - int ret; - - fatal = amdgpu_cper_alloc_entry(adev, AMDGPU_CPER_TYPE_FATAL, 1); - if (!fatal) { - dev_err(adev->dev, "fail to alloc cper entry for ue record\n"); - return -ENOMEM; - } - - reg_data.status_lo = lower_32_bits(bank->regs[ACA_REG_IDX_STATUS]); - reg_data.status_hi = upper_32_bits(bank->regs[ACA_REG_IDX_STATUS]); - reg_data.addr_lo = lower_32_bits(bank->regs[ACA_REG_IDX_ADDR]); - reg_data.addr_hi = upper_32_bits(bank->regs[ACA_REG_IDX_ADDR]); - reg_data.ipid_lo = lower_32_bits(bank->regs[ACA_REG_IDX_IPID]); - reg_data.ipid_hi = upper_32_bits(bank->regs[ACA_REG_IDX_IPID]); - reg_data.synd_lo = lower_32_bits(bank->regs[ACA_REG_IDX_SYND]); - reg_data.synd_hi = upper_32_bits(bank->regs[ACA_REG_IDX_SYND]); - - amdgpu_cper_entry_fill_hdr(adev, fatal, AMDGPU_CPER_TYPE_FATAL, CPER_SEV_FATAL_UNCORRECTED); - ret = amdgpu_cper_entry_fill_fatal_section(adev, fatal, 0, reg_data); - if (ret) - return ret; - - amdgpu_cper_ring_write(ring, fatal, fatal->record_length); - kfree(fatal); - - return 0; -} - int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev) { struct cper_hdr *bp_threshold = NULL; @@ -348,83 +314,6 @@ int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev) return 0; } -static enum cper_error_severity amdgpu_aca_err_type_to_cper_sev(struct amdgpu_device *adev, - enum aca_error_type aca_err_type) -{ - switch (aca_err_type) { - case ACA_ERROR_TYPE_UE: - return CPER_SEV_FATAL_UNCORRECTED; - case ACA_ERROR_TYPE_CE: - return CPER_SEV_NON_FATAL_CORRECTED; - case ACA_ERROR_TYPE_DEFERRED: - return CPER_SEV_NON_FATAL_UNCORRECTED; - default: - dev_err(adev->dev, "Unknown ACA error type!\n"); - return CPER_SEV_FATAL_UNCORRECTED; - } -} - -int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev, - struct aca_banks *banks, - uint16_t bank_count) -{ - struct cper_hdr *corrected = NULL; - enum cper_error_severity sev = CPER_SEV_NON_FATAL_CORRECTED; - struct amdgpu_ring *ring = &adev->cper.ring_buf; - uint32_t reg_data[CPER_ACA_REG_COUNT] = { 0 }; - struct aca_bank_node *node; - struct aca_bank *bank; - uint32_t i = 0; - int ret; - - corrected = amdgpu_cper_alloc_entry(adev, AMDGPU_CPER_TYPE_RUNTIME, bank_count); - if (!corrected) { - dev_err(adev->dev, "fail to allocate cper entry for ce records\n"); - return -ENOMEM; - } - - /* Raise severity if any DE is detected in the ACA bank list */ - list_for_each_entry(node, &banks->list, node) { - bank = &node->bank; - if (bank->aca_err_type == ACA_ERROR_TYPE_DEFERRED) { - sev = CPER_SEV_NON_FATAL_UNCORRECTED; - break; - } - } - - amdgpu_cper_entry_fill_hdr(adev, corrected, AMDGPU_CPER_TYPE_RUNTIME, sev); - - /* Combine CE and DE in cper record */ - list_for_each_entry(node, &banks->list, node) { - bank = &node->bank; - reg_data[CPER_ACA_REG_CTL_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_CTL]); - reg_data[CPER_ACA_REG_CTL_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_CTL]); - reg_data[CPER_ACA_REG_STATUS_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_STATUS]); - reg_data[CPER_ACA_REG_STATUS_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_STATUS]); - reg_data[CPER_ACA_REG_ADDR_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_ADDR]); - reg_data[CPER_ACA_REG_ADDR_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_ADDR]); - reg_data[CPER_ACA_REG_MISC0_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_MISC0]); - reg_data[CPER_ACA_REG_MISC0_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_MISC0]); - reg_data[CPER_ACA_REG_CONFIG_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_CONFIG]); - reg_data[CPER_ACA_REG_CONFIG_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_CONFIG]); - reg_data[CPER_ACA_REG_IPID_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_IPID]); - reg_data[CPER_ACA_REG_IPID_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_IPID]); - reg_data[CPER_ACA_REG_SYND_LO] = lower_32_bits(bank->regs[ACA_REG_IDX_SYND]); - reg_data[CPER_ACA_REG_SYND_HI] = upper_32_bits(bank->regs[ACA_REG_IDX_SYND]); - - ret = amdgpu_cper_entry_fill_runtime_section(adev, corrected, i++, - amdgpu_aca_err_type_to_cper_sev(adev, bank->aca_err_type), - reg_data, CPER_ACA_REG_COUNT); - if (ret) - return ret; - } - - amdgpu_cper_ring_write(ring, corrected, corrected->record_length); - kfree(corrected); - - return 0; -} - static bool amdgpu_cper_is_hdr(struct amdgpu_ring *ring, u64 pos) { char signature[CPER_SIGNATURE_SZ]; @@ -592,8 +481,7 @@ int amdgpu_cper_init(struct amdgpu_device *adev) if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_cper_en(adev)) return 0; - else if (!amdgpu_sriov_vf(adev) && !amdgpu_uniras_enabled(adev) && - !amdgpu_aca_is_enabled(adev)) + else if (!amdgpu_sriov_vf(adev) && !amdgpu_uniras_enabled(adev)) return 0; r = amdgpu_cper_ring_init(adev); @@ -612,7 +500,7 @@ int amdgpu_cper_init(struct amdgpu_device *adev) int amdgpu_cper_fini(struct amdgpu_device *adev) { - if (!amdgpu_aca_is_enabled(adev) && !amdgpu_sriov_ras_cper_en(adev)) + if (amdgpu_sriov_vf(adev)) return 0; adev->cper.enabled = false; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h index 353421807387..d12c98077d9d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cper.h @@ -26,7 +26,6 @@ #define __AMDGPU_CPER_H__ #include "amd_cper.h" -#include "amdgpu_aca.h" #define CPER_MAX_ALLOWED_COUNT 0x1000 #define CPER_MAX_RING_SIZE 0X100000 @@ -88,13 +87,6 @@ int amdgpu_cper_entry_fill_bad_page_threshold_section(struct amdgpu_device *adev struct cper_hdr *amdgpu_cper_alloc_entry(struct amdgpu_device *adev, enum amdgpu_cper_type type, uint16_t section_count); -/* UE must be encoded into separated cper entries, 1 UE 1 cper */ -int amdgpu_cper_generate_ue_record(struct amdgpu_device *adev, - struct aca_bank *bank); -/* CEs and DEs are combined into 1 cper entry */ -int amdgpu_cper_generate_ce_records(struct amdgpu_device *adev, - struct aca_banks *banks, - uint16_t bank_count); /* Bad page threshold is encoded into separated cper entry */ int amdgpu_cper_generate_bp_threshold_record(struct amdgpu_device *adev); void amdgpu_cper_ring_write(struct amdgpu_ring *ring, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index c2e6495a28bc..d777375e5350 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -140,24 +140,19 @@ static int amdgpu_cs_p1_bo_handles(struct amdgpu_cs_parser *p, struct drm_amdgpu_bo_list_in *data) { struct drm_amdgpu_bo_list_entry *info; - int r; - - r = amdgpu_bo_create_list_entry_array(data, &info); - if (r) - return r; - - r = amdgpu_bo_list_create(p->adev, p->filp, info, data->bo_number, - &p->bo_list); - if (r) - goto error_free; + struct amdgpu_bo_list *list; - kvfree(info); - return 0; + info = amdgpu_bo_create_list_entry_array(data); + if (IS_ERR(info)) + return PTR_ERR(info); -error_free: + list = amdgpu_bo_list_create(p->adev, p->filp, info, data->bo_number); kvfree(info); + if (IS_ERR(list)) + return PTR_ERR(list); - return r; + p->bo_list = list; + return 0; } /* Copy the data from userspace and go over it the first time */ @@ -846,6 +841,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, { struct amdgpu_fpriv *fpriv = p->filp->driver_priv; struct ttm_operation_ctx ctx = { true, false }; + struct amdgpu_bo_list *list = NULL; struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_bo_list_entry *e; struct drm_gem_object *obj; @@ -857,25 +853,24 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, if (p->bo_list) return -EINVAL; - r = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle, - &p->bo_list); - if (r) - return r; + list = amdgpu_bo_list_get(fpriv, cs->in.bo_list_handle); } else if (!p->bo_list) { /* Create a empty bo_list when no handle is provided */ - r = amdgpu_bo_list_create(p->adev, p->filp, NULL, 0, - &p->bo_list); - if (r) - return r; + list = amdgpu_bo_list_create(p->adev, p->filp, NULL, 0); } - mutex_lock(&p->bo_list->bo_list_mutex); + if (IS_ERR(list)) + return PTR_ERR(list); + else if (list) + p->bo_list = list; + else + list = p->bo_list; /* Get userptr backing pages. If pages are updated after registered * in amdgpu_gem_userptr_ioctl(), amdgpu_cs_list_validate() will do * amdgpu_ttm_backend_bind() to flush and invalidate new pages */ - amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) { + amdgpu_bo_list_for_each_userptr_entry(e, list) { bool userpage_invalidated = false; struct amdgpu_bo *bo = e->bo; @@ -905,7 +900,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, if (unlikely(r)) goto out_free_user_pages; - amdgpu_bo_list_for_each_entry(e, p->bo_list) { + amdgpu_bo_list_for_each_entry(e, list) { r = drm_exec_prepare_obj(&p->exec, &e->bo->tbo.base, TTM_NUM_MOVE_FENCES + p->gang_size); drm_exec_retry_on_contention(&p->exec); @@ -924,7 +919,7 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, } } - amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) { + amdgpu_bo_list_for_each_userptr_entry(e, list) { struct mm_struct *usermm; usermm = amdgpu_ttm_tt_get_usermm(e->bo->tbo.ttm); @@ -977,17 +972,15 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, p->bytes_moved_vis); for (i = 0; i < p->gang_size; ++i) - amdgpu_job_set_resources(p->jobs[i], p->bo_list->gds_obj, - p->bo_list->gws_obj, - p->bo_list->oa_obj); + amdgpu_job_set_resources(p->jobs[i], list->gds_obj, + list->gws_obj, list->oa_obj); return 0; out_free_user_pages: - amdgpu_bo_list_for_each_userptr_entry(e, p->bo_list) { + amdgpu_bo_list_for_each_userptr_entry(e, list) { amdgpu_hmm_range_free(e->range); e->range = NULL; } - mutex_unlock(&p->bo_list->bo_list_mutex); return r; } @@ -1322,7 +1315,7 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, e->range = NULL; } - if (r || !list_empty(&vm->individual.moved)) { + if (r || !list_empty(&vm->individual.needs_update)) { r = -EAGAIN; mutex_unlock(&p->adev->notifier_lock); return r; @@ -1371,7 +1364,6 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, amdgpu_vm_move_to_lru_tail(p->adev, &fpriv->vm); mutex_unlock(&p->adev->notifier_lock); - mutex_unlock(&p->bo_list->bo_list_mutex); return 0; } @@ -1443,28 +1435,25 @@ int amdgpu_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) r = amdgpu_cs_patch_jobs(&parser); if (r) - goto error_backoff; + goto error_fini; r = amdgpu_cs_vm_handling(&parser); if (r) - goto error_backoff; + goto error_fini; r = amdgpu_cs_sync_rings(&parser); if (r) - goto error_backoff; + goto error_fini; trace_amdgpu_cs_ibs(&parser); r = amdgpu_cs_submit(&parser, data); if (r) - goto error_backoff; + goto error_fini; amdgpu_cs_parser_fini(&parser); return 0; -error_backoff: - mutex_unlock(&parser.bo_list->bo_list_mutex); - error_fini: amdgpu_cs_parser_fini(&parser); return r; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c index ce35b415093d..d53259a5b82f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.c @@ -283,6 +283,8 @@ static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev, if (!entity) return res; + drm_sched_entity_destroy(&entity->entity); + for (i = 0; i < amdgpu_sched_jobs; ++i) { res = ktime_add(res, amdgpu_ctx_fence_time(entity->fences[i])); dma_fence_put(entity->fences[i]); @@ -294,32 +296,20 @@ static ktime_t amdgpu_ctx_fini_entity(struct amdgpu_device *adev, return res; } -static int amdgpu_ctx_get_stable_pstate(struct amdgpu_ctx *ctx, - u32 *stable_pstate) +static u32 amdgpu_get_stable_pstate(struct amdgpu_device *adev) { - struct amdgpu_device *adev = ctx->mgr->adev; - enum amd_dpm_forced_level current_level; - - current_level = amdgpu_dpm_get_performance_level(adev); - - switch (current_level) { + switch (amdgpu_dpm_get_performance_level(adev)) { case AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD: - *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_STANDARD; - break; + return AMDGPU_CTX_STABLE_PSTATE_STANDARD; case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK: - *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK; - break; + return AMDGPU_CTX_STABLE_PSTATE_MIN_SCLK; case AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK: - *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK; - break; + return AMDGPU_CTX_STABLE_PSTATE_MIN_MCLK; case AMD_DPM_FORCED_LEVEL_PROFILE_PEAK: - *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_PEAK; - break; + return AMDGPU_CTX_STABLE_PSTATE_PEAK; default: - *stable_pstate = AMDGPU_CTX_STABLE_PSTATE_NONE; - break; + return AMDGPU_CTX_STABLE_PSTATE_NONE; } - return 0; } static int amdgpu_ctx_init(struct amdgpu_ctx_mgr *mgr, int32_t priority, @@ -383,9 +373,9 @@ static int __amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, if (current_ctx && current_ctx != ctx) return -EBUSY; - r = amdgpu_ctx_get_stable_pstate(ctx, ¤t_stable_pstate); - if (r || current_stable_pstate == stable_pstate) - return r; + current_stable_pstate = amdgpu_get_stable_pstate(adev); + if (current_stable_pstate == stable_pstate) + return 0; r = amdgpu_dpm_force_performance_level(adev, level); if (r) @@ -416,7 +406,7 @@ static int amdgpu_ctx_set_stable_pstate(struct amdgpu_ctx *ctx, return r; } -static void amdgpu_ctx_fini(struct kref *ref) +void amdgpu_ctx_fini(struct kref *ref) { struct amdgpu_ctx *ctx = container_of(ref, struct amdgpu_ctx, refcount); struct amdgpu_ctx_mgr *mgr = ctx->mgr; @@ -504,53 +494,26 @@ static int amdgpu_ctx_alloc(struct amdgpu_device *adev, if (!ctx) return -ENOMEM; - mutex_lock(&mgr->lock); - r = idr_alloc(&mgr->ctx_handles, ctx, 1, AMDGPU_VM_MAX_NUM_CTX, GFP_KERNEL); - if (r < 0) { - mutex_unlock(&mgr->lock); - kfree(ctx); - return r; - } - - *id = (uint32_t)r; r = amdgpu_ctx_init(mgr, priority, filp, ctx); if (r) { - idr_remove(&mgr->ctx_handles, *id); - *id = 0; kfree(ctx); + return r; } - mutex_unlock(&mgr->lock); - return r; -} - -static void amdgpu_ctx_do_release(struct kref *ref) -{ - struct amdgpu_ctx *ctx; - u32 i, j; - ctx = container_of(ref, struct amdgpu_ctx, refcount); - for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) { - for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) { - if (!ctx->entities[i][j]) - continue; - - drm_sched_entity_destroy(&ctx->entities[i][j]->entity); - } - } + r = xa_alloc(&mgr->ctx_handles, id, ctx, xa_limit_32b, GFP_KERNEL); + if (r) + amdgpu_ctx_put(ctx); - amdgpu_ctx_fini(ref); + return r; } static int amdgpu_ctx_free(struct amdgpu_fpriv *fpriv, uint32_t id) { - struct amdgpu_ctx_mgr *mgr = &fpriv->ctx_mgr; struct amdgpu_ctx *ctx; - mutex_lock(&mgr->lock); - ctx = idr_remove(&mgr->ctx_handles, id); - if (ctx) - kref_put(&ctx->refcount, amdgpu_ctx_do_release); - mutex_unlock(&mgr->lock); + ctx = xa_erase(&fpriv->ctx_mgr.ctx_handles, id); + amdgpu_ctx_put(ctx); + return ctx ? 0 : -EINVAL; } @@ -559,19 +522,11 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev, union drm_amdgpu_ctx_out *out) { struct amdgpu_ctx *ctx; - struct amdgpu_ctx_mgr *mgr; unsigned reset_counter; - if (!fpriv) - return -EINVAL; - - mgr = &fpriv->ctx_mgr; - mutex_lock(&mgr->lock); - ctx = idr_find(&mgr->ctx_handles, id); - if (!ctx) { - mutex_unlock(&mgr->lock); + ctx = amdgpu_ctx_get(fpriv, id); + if (!ctx) return -EINVAL; - } /* TODO: these two are always zero */ out->state.flags = 0x0; @@ -586,7 +541,8 @@ static int amdgpu_ctx_query(struct amdgpu_device *adev, out->state.reset_status = AMDGPU_CTX_UNKNOWN_RESET; ctx->reset_counter_query = reset_counter; - mutex_unlock(&mgr->lock); + amdgpu_ctx_put(ctx); + return 0; } @@ -619,18 +575,10 @@ static int amdgpu_ctx_query2(struct amdgpu_device *adev, { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct amdgpu_ctx *ctx; - struct amdgpu_ctx_mgr *mgr; - if (!fpriv) - return -EINVAL; - - mgr = &fpriv->ctx_mgr; - mutex_lock(&mgr->lock); - ctx = idr_find(&mgr->ctx_handles, id); - if (!ctx) { - mutex_unlock(&mgr->lock); + ctx = amdgpu_ctx_get(fpriv, id); + if (!ctx) return -EINVAL; - } out->state.flags = 0x0; out->state.hangs = 0x0; @@ -671,7 +619,8 @@ static int amdgpu_ctx_query2(struct amdgpu_device *adev, msecs_to_jiffies(AMDGPU_RAS_COUNTE_DELAY_MS)); } - mutex_unlock(&mgr->lock); + amdgpu_ctx_put(ctx); + return 0; } @@ -680,26 +629,26 @@ static int amdgpu_ctx_stable_pstate(struct amdgpu_device *adev, bool set, u32 *stable_pstate) { struct amdgpu_ctx *ctx; - struct amdgpu_ctx_mgr *mgr; - int r; + int r = 0; - if (!fpriv) + ctx = amdgpu_ctx_get(fpriv, id); + if (!ctx) return -EINVAL; - mgr = &fpriv->ctx_mgr; - mutex_lock(&mgr->lock); - ctx = idr_find(&mgr->ctx_handles, id); - if (!ctx) { - mutex_unlock(&mgr->lock); - return -EINVAL; - } + /* + * The get path is odd in this uapi - it will check whether the context + * id exist, but otherwise does nothing with it. In other words, the + * uapi has historically been implemented as being able to query the + * global device state, as long as the caller supplies a random valid + * context id. + */ if (set) r = amdgpu_ctx_set_stable_pstate(ctx, *stable_pstate); else - r = amdgpu_ctx_get_stable_pstate(ctx, stable_pstate); + *stable_pstate = amdgpu_get_stable_pstate(adev); - mutex_unlock(&mgr->lock); + amdgpu_ctx_put(ctx); return r; } @@ -778,23 +727,14 @@ struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id) mgr = &fpriv->ctx_mgr; - mutex_lock(&mgr->lock); - ctx = idr_find(&mgr->ctx_handles, id); + xa_lock(&mgr->ctx_handles); + ctx = xa_load(&mgr->ctx_handles, id); if (ctx) kref_get(&ctx->refcount); - mutex_unlock(&mgr->lock); + xa_unlock(&mgr->ctx_handles); return ctx; } -int amdgpu_ctx_put(struct amdgpu_ctx *ctx) -{ - if (ctx == NULL) - return -EINVAL; - - kref_put(&ctx->refcount, amdgpu_ctx_do_release); - return 0; -} - uint64_t amdgpu_ctx_add_fence(struct amdgpu_ctx *ctx, struct drm_sched_entity *entity, struct dma_fence *fence) @@ -928,8 +868,7 @@ void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr, unsigned int i; mgr->adev = adev; - mutex_init(&mgr->lock); - idr_init_base(&mgr->ctx_handles, 1); + xa_init_flags(&mgr->ctx_handles, XA_FLAGS_ALLOC1); for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) atomic64_set(&mgr->time_spend[i], 0); @@ -938,13 +877,13 @@ void amdgpu_ctx_mgr_init(struct amdgpu_ctx_mgr *mgr, long amdgpu_ctx_mgr_entity_flush(struct amdgpu_ctx_mgr *mgr, long timeout) { struct amdgpu_ctx *ctx; - struct idr *idp; - uint32_t id, i, j; + unsigned long id; + int i, j; - idp = &mgr->ctx_handles; - - mutex_lock(&mgr->lock); - idr_for_each_entry(idp, ctx, id) { + xa_lock(&mgr->ctx_handles); + xa_for_each(&mgr->ctx_handles, id, ctx) { + kref_get(&ctx->refcount); + xa_unlock(&mgr->ctx_handles); for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) { for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) { struct drm_sched_entity *entity; @@ -956,45 +895,21 @@ long amdgpu_ctx_mgr_entity_flush(struct amdgpu_ctx_mgr *mgr, long timeout) timeout = drm_sched_entity_flush(entity, timeout); } } + amdgpu_ctx_put(ctx); + xa_lock(&mgr->ctx_handles); } - mutex_unlock(&mgr->lock); + xa_unlock(&mgr->ctx_handles); return timeout; } -static void amdgpu_ctx_mgr_entity_fini(struct amdgpu_ctx_mgr *mgr) +void amdgpu_ctx_mgr_fini(struct amdgpu_ctx_mgr *mgr) { struct amdgpu_ctx *ctx; - struct idr *idp; - uint32_t id, i, j; - - idp = &mgr->ctx_handles; - - idr_for_each_entry(idp, ctx, id) { - if (kref_read(&ctx->refcount) != 1) { - drm_err(adev_to_drm(mgr->adev), "ctx %p is still alive\n", ctx); - continue; - } + unsigned long id; - for (i = 0; i < AMDGPU_HW_IP_NUM; ++i) { - for (j = 0; j < amdgpu_ctx_num_entities[i]; ++j) { - struct drm_sched_entity *entity; - - if (!ctx->entities[i][j]) - continue; - - entity = &ctx->entities[i][j]->entity; - drm_sched_entity_fini(entity); - } - } - kref_put(&ctx->refcount, amdgpu_ctx_fini); - } -} - -void amdgpu_ctx_mgr_fini(struct amdgpu_ctx_mgr *mgr) -{ - amdgpu_ctx_mgr_entity_fini(mgr); - idr_destroy(&mgr->ctx_handles); - mutex_destroy(&mgr->lock); + xa_for_each(&mgr->ctx_handles, id, ctx) + amdgpu_ctx_put(ctx); + xa_destroy(&mgr->ctx_handles); } void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr, @@ -1002,21 +917,21 @@ void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr, { struct amdgpu_ctx *ctx; unsigned int hw_ip, i; - uint32_t id; + unsigned long id; /* * This is a little bit racy because it can be that a ctx or a fence are * destroyed just in the moment we try to account them. But that is ok * since exactly that case is explicitely allowed by the interface. */ - mutex_lock(&mgr->lock); for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) { uint64_t ns = atomic64_read(&mgr->time_spend[hw_ip]); usage[hw_ip] = ns_to_ktime(ns); } - idr_for_each_entry(&mgr->ctx_handles, ctx, id) { + xa_lock(&mgr->ctx_handles); + xa_for_each(&mgr->ctx_handles, id, ctx) { for (hw_ip = 0; hw_ip < AMDGPU_HW_IP_NUM; ++hw_ip) { for (i = 0; i < amdgpu_ctx_num_entities[hw_ip]; ++i) { struct amdgpu_ctx_entity *centity; @@ -1030,5 +945,5 @@ void amdgpu_ctx_mgr_usage(struct amdgpu_ctx_mgr *mgr, } } } - mutex_unlock(&mgr->lock); + xa_unlock(&mgr->ctx_handles); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h index e444b2088d40..a4b89eca4169 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ctx.h @@ -25,6 +25,7 @@ #include <linux/ktime.h> #include <linux/types.h> +#include <linux/xarray.h> #include "amdgpu_ring.h" @@ -60,16 +61,21 @@ struct amdgpu_ctx { struct amdgpu_ctx_mgr { struct amdgpu_device *adev; - struct mutex lock; - /* protected by lock */ - struct idr ctx_handles; + struct xarray ctx_handles; atomic64_t time_spend[AMDGPU_HW_IP_NUM]; }; extern const unsigned int amdgpu_ctx_num_entities[AMDGPU_HW_IP_NUM]; struct amdgpu_ctx *amdgpu_ctx_get(struct amdgpu_fpriv *fpriv, uint32_t id); -int amdgpu_ctx_put(struct amdgpu_ctx *ctx); + +void amdgpu_ctx_fini(struct kref *kref); + +static inline void amdgpu_ctx_put(struct amdgpu_ctx *ctx) +{ + if (ctx) + kref_put(&ctx->refcount, amdgpu_ctx_fini); +} int amdgpu_ctx_get_entity(struct amdgpu_ctx *ctx, u32 hw_ip, u32 instance, u32 ring, struct drm_sched_entity **entity); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index 389bad724273..0455c2cd043f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -26,6 +26,7 @@ #include <linux/kthread.h> #include <linux/pci.h> #include <linux/uaccess.h> +#include <linux/security.h> #include <linux/pm_runtime.h> #include "amdgpu.h" @@ -1739,6 +1740,12 @@ int amdgpu_debugfs_regs_init(struct amdgpu_device *adev) struct dentry *ent, *root = minor->debugfs_root; unsigned int i; + if (security_locked_down(LOCKDOWN_PCI_ACCESS)) { + drm_info(adev_to_drm(adev), + "amdgpu: HW debugfs nodes disabled (kernel lockdown)\n"); + return 0; + } + for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) { ent = debugfs_create_file(debugfs_regs_names[i], S_IFREG | 0400, root, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c index e77db76b48b8..4fd0df3aa70d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_dev_coredump.c @@ -64,6 +64,7 @@ const char *hw_ip_names[MAX_HWIP] = { [VCN1_HWIP] = "VCN1", [VCE_HWIP] = "VCE", [VPE_HWIP] = "VPE", + [UMSCH_HWIP] = "UMSCH", [DF_HWIP] = "DF", [DCE_HWIP] = "DCE", [OSSSYS_HWIP] = "OSSSYS", diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 211d30f03d25..78c96c7102e4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -74,6 +74,7 @@ #include "amdgpu_ras.h" #include "amdgpu_ras_mgr.h" #include "amdgpu_pmu.h" +#include "amdgpu_smu.h" #include "amdgpu_fru_eeprom.h" #include "amdgpu_reset.h" #include "amdgpu_virt.h" @@ -2130,6 +2131,8 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev) adev->cg_flags &= amdgpu_cg_mask; adev->pg_flags &= amdgpu_pg_mask; + amdgpu_smu_early_init(adev); + return 0; } @@ -3677,6 +3680,10 @@ static void amdgpu_device_sys_interface_fini(struct amdgpu_device *adev) amdgpu_pm_sysfs_fini(adev); if (adev->ucode_sysfs_en) amdgpu_ucode_sysfs_fini(adev); + + amdgpu_discovery_sysfs_fini(adev); + amdgpu_preempt_mgr_sysfs_fini(adev); + amdgpu_device_attr_sysfs_fini(adev); amdgpu_fru_sysfs_fini(adev); @@ -3773,6 +3780,7 @@ int amdgpu_device_init(struct amdgpu_device *adev, spin_lock_init(&adev->irq.lock); + amdgpu_early_init_rlc_reg_funcs(adev); amdgpu_device_init_apu_flags(adev); r = amdgpu_device_check_arguments(adev); @@ -4184,8 +4192,6 @@ static void amdgpu_device_unmap_mmio(struct amdgpu_device *adev) iounmap(adev->rmmio); adev->rmmio = NULL; - if (adev->mman.aper_base_kaddr) - iounmap(adev->mman.aper_base_kaddr); adev->mman.aper_base_kaddr = NULL; /* Memory manager related */ @@ -4210,6 +4216,7 @@ void amdgpu_device_fini_hw(struct amdgpu_device *adev) if (adev->mman.initialized) drain_workqueue(adev->mman.bdev.wq); + adev->shutdown = true; unregister_pm_notifier(&adev->pm_nb); @@ -4709,161 +4716,6 @@ exit: } /** - * amdgpu_device_ip_check_soft_reset - did soft reset succeed - * - * @adev: amdgpu_device pointer - * - * The list of all the hardware IPs that make up the asic is walked and - * the check_soft_reset callbacks are run. check_soft_reset determines - * if the asic is still hung or not. - * Returns true if any of the IPs are still in a hung state, false if not. - */ -static bool amdgpu_device_ip_check_soft_reset(struct amdgpu_device *adev) -{ - int i; - bool asic_hang = false; - - if (amdgpu_sriov_vf(adev)) - return true; - - if (amdgpu_asic_need_full_reset(adev)) - return true; - - for (i = 0; i < adev->num_ip_blocks; i++) { - if (!adev->ip_blocks[i].status.valid) - continue; - if (adev->ip_blocks[i].version->funcs->check_soft_reset) - adev->ip_blocks[i].status.hang = - adev->ip_blocks[i].version->funcs->check_soft_reset( - &adev->ip_blocks[i]); - if (adev->ip_blocks[i].status.hang) { - dev_info(adev->dev, "IP block:%s is hung!\n", adev->ip_blocks[i].version->funcs->name); - asic_hang = true; - } - } - return asic_hang; -} - -/** - * amdgpu_device_ip_pre_soft_reset - prepare for soft reset - * - * @adev: amdgpu_device pointer - * - * The list of all the hardware IPs that make up the asic is walked and the - * pre_soft_reset callbacks are run if the block is hung. pre_soft_reset - * handles any IP specific hardware or software state changes that are - * necessary for a soft reset to succeed. - * Returns 0 on success, negative error code on failure. - */ -static int amdgpu_device_ip_pre_soft_reset(struct amdgpu_device *adev) -{ - int i, r = 0; - - for (i = 0; i < adev->num_ip_blocks; i++) { - if (!adev->ip_blocks[i].status.valid) - continue; - if (adev->ip_blocks[i].status.hang && - adev->ip_blocks[i].version->funcs->pre_soft_reset) { - r = adev->ip_blocks[i].version->funcs->pre_soft_reset(&adev->ip_blocks[i]); - if (r) - return r; - } - } - - return 0; -} - -/** - * amdgpu_device_ip_need_full_reset - check if a full asic reset is needed - * - * @adev: amdgpu_device pointer - * - * Some hardware IPs cannot be soft reset. If they are hung, a full gpu - * reset is necessary to recover. - * Returns true if a full asic reset is required, false if not. - */ -static bool amdgpu_device_ip_need_full_reset(struct amdgpu_device *adev) -{ - int i; - - if (amdgpu_asic_need_full_reset(adev)) - return true; - - for (i = 0; i < adev->num_ip_blocks; i++) { - if (!adev->ip_blocks[i].status.valid) - continue; - if ((adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GMC) || - (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_SMC) || - (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_ACP) || - (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_DCE) || - adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_PSP) { - if (adev->ip_blocks[i].status.hang) { - dev_info(adev->dev, "Some block need full reset!\n"); - return true; - } - } - } - return false; -} - -/** - * amdgpu_device_ip_soft_reset - do a soft reset - * - * @adev: amdgpu_device pointer - * - * The list of all the hardware IPs that make up the asic is walked and the - * soft_reset callbacks are run if the block is hung. soft_reset handles any - * IP specific hardware or software state changes that are necessary to soft - * reset the IP. - * Returns 0 on success, negative error code on failure. - */ -static int amdgpu_device_ip_soft_reset(struct amdgpu_device *adev) -{ - int i, r = 0; - - for (i = 0; i < adev->num_ip_blocks; i++) { - if (!adev->ip_blocks[i].status.valid) - continue; - if (adev->ip_blocks[i].status.hang && - adev->ip_blocks[i].version->funcs->soft_reset) { - r = adev->ip_blocks[i].version->funcs->soft_reset(&adev->ip_blocks[i]); - if (r) - return r; - } - } - - return 0; -} - -/** - * amdgpu_device_ip_post_soft_reset - clean up from soft reset - * - * @adev: amdgpu_device pointer - * - * The list of all the hardware IPs that make up the asic is walked and the - * post_soft_reset callbacks are run if the asic was hung. post_soft_reset - * handles any IP specific hardware or software state changes that are - * necessary after the IP has been soft reset. - * Returns 0 on success, negative error code on failure. - */ -static int amdgpu_device_ip_post_soft_reset(struct amdgpu_device *adev) -{ - int i, r = 0; - - for (i = 0; i < adev->num_ip_blocks; i++) { - if (!adev->ip_blocks[i].status.valid) - continue; - if (adev->ip_blocks[i].status.hang && - adev->ip_blocks[i].version->funcs->post_soft_reset) - r = adev->ip_blocks[i].version->funcs->post_soft_reset(&adev->ip_blocks[i]); - if (r) - return r; - } - - return 0; -} - -/** * amdgpu_device_reset_sriov - reset ASIC for SR-IOV vf * * @adev: amdgpu_device pointer @@ -5154,20 +5006,7 @@ int amdgpu_device_pre_asic_reset(struct amdgpu_device *adev, /* Don't suspend on bare metal if we are not going to HW reset the ASIC */ if (!amdgpu_sriov_vf(adev)) { - - if (!need_full_reset) - need_full_reset = amdgpu_device_ip_need_full_reset(adev); - - if (!need_full_reset && amdgpu_gpu_recovery && - amdgpu_device_ip_check_soft_reset(adev)) { - amdgpu_device_ip_pre_soft_reset(adev); - r = amdgpu_device_ip_soft_reset(adev); - amdgpu_device_ip_post_soft_reset(adev); - if (r || amdgpu_device_ip_check_soft_reset(adev)) { - dev_info(adev->dev, "soft reset failed, will fallback to full reset!\n"); - need_full_reset = true; - } - } + need_full_reset = true; if (!test_bit(AMDGPU_SKIP_COREDUMP, &reset_context->flags)) { dev_info(tmp_adev->dev, "Dumping IP State\n"); @@ -5620,8 +5459,7 @@ static void amdgpu_device_halt_activities(struct amdgpu_device *adev, drm_client_dev_suspend(adev_to_drm(tmp_adev)); /* disable ras on ALL IPs */ - if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev) && - amdgpu_device_ip_need_full_reset(tmp_adev)) + if (!need_emergency_restart && !amdgpu_reset_in_dpc(adev)) amdgpu_ras_suspend(tmp_adev); amdgpu_userq_pre_reset(tmp_adev); @@ -6893,7 +6731,7 @@ ssize_t amdgpu_get_soft_full_reset_mask(struct amdgpu_ring *ring) if (unlikely(!ring->adev->debug_disable_soft_recovery) && !amdgpu_sriov_vf(ring->adev) && ring->funcs->soft_recovery) - size |= AMDGPU_RESET_TYPE_SOFT_RESET; + size |= AMDGPU_RESET_TYPE_SOFT_RECOVERY; return size; } @@ -6909,8 +6747,8 @@ ssize_t amdgpu_show_reset_mask(char *buf, uint32_t supported_reset) } - if (supported_reset & AMDGPU_RESET_TYPE_SOFT_RESET) - size += sysfs_emit_at(buf, size, "soft "); + if (supported_reset & AMDGPU_RESET_TYPE_SOFT_RECOVERY) + size += sysfs_emit_at(buf, size, "soft_recovery "); if (supported_reset & AMDGPU_RESET_TYPE_PER_QUEUE) size += sysfs_emit_at(buf, size, "queue "); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index be5069642a90..a015d55aa158 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -22,6 +22,7 @@ */ #include <linux/firmware.h> +#include <linux/kernfs.h> #include "amdgpu.h" #include "amdgpu_discovery.h" @@ -148,6 +149,26 @@ MODULE_FIRMWARE("amdgpu/aldebaran_ip_discovery.bin"); #define mmDRIVER_SCRATCH_1 0x95 #define mmDRIVER_SCRATCH_2 0x96 +struct ip_discovery_top { + struct kobject kobj; + struct kset die_kset; + struct pci_dev *pdev; + struct amdgpu_device *adev; + uint8_t *discovery_bin; + uint32_t bin_size; + bool standalone_mode; +}; + +/* List to track early-initialized ip_discovery_top entries */ +struct early_ip_discovery { + struct list_head list; + struct pci_dev *pdev; + struct ip_discovery_top *ip_top; +}; + +static LIST_HEAD(early_ip_discovery_list); +static DEFINE_MUTEX(early_ip_discovery_mutex); + static const char *hw_id_names[HW_ID_MAX] = { [MP1_HWID] = "MP1", [MP2_HWID] = "MP2", @@ -226,6 +247,7 @@ static const char *hw_id_names[HW_ID_MAX] = { [XGBE_HWID] = "XGBE", [MP0_HWID] = "MP0", [VPE_HWID] = "VPE", + [UMSCH_HWID] = "UMSCH", [ATU_HWID] = "ATU", [AIGC_HWID] = "AIGC", }; @@ -258,6 +280,7 @@ static int hw_id_map[MAX_HWIP] = { [DCI_HWIP] = DCI_HWID, [PCIE_HWIP] = PCIE_HWID, [VPE_HWIP] = VPE_HWID, + [UMSCH_HWIP] = UMSCH_HWID, [ISP_HWIP] = ISP_HWID, [ATU_HWIP] = ATU_HWID, }; @@ -542,25 +565,37 @@ static const char *amdgpu_discovery_get_fw_name(struct amdgpu_device *adev) } } -static int amdgpu_discovery_get_table_info(struct amdgpu_device *adev, - struct table_info **info, - uint16_t table_id) +static struct table_info * +amdgpu_discovery_get_table_info_from_bin(uint8_t *discovery_bin, + uint16_t table_id) { - struct binary_header *bhdr = - (struct binary_header *)adev->discovery.bin; + struct binary_header *bhdr = (struct binary_header *)discovery_bin; struct binary_header_v2 *bhdrv2; switch (bhdr->version_major) { case 2: - bhdrv2 = (struct binary_header_v2 *)adev->discovery.bin; - *info = &bhdrv2->table_list[table_id]; - break; + bhdrv2 = (struct binary_header_v2 *)discovery_bin; + return &bhdrv2->table_list[table_id]; case 1: case 0: - *info = &bhdr->table_list[table_id]; - break; + return &bhdr->table_list[table_id]; default: - dev_err(adev->dev, "Invalid ip discovery table version %d\n",bhdr->version_major); + return NULL; + } +} + +static int amdgpu_discovery_get_table_info(struct amdgpu_device *adev, + struct table_info **info, + uint16_t table_id) +{ + struct binary_header *bhdr = + (struct binary_header *)adev->discovery.bin; + + *info = amdgpu_discovery_get_table_info_from_bin(adev->discovery.bin, + table_id); + if (!*info) { + dev_err(adev->dev, "Invalid ip discovery table version %d\n", + bhdr->version_major); return -EINVAL; } @@ -724,11 +759,11 @@ out: return r; } -static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev); - void amdgpu_discovery_fini(struct amdgpu_device *adev) { - amdgpu_discovery_sysfs_fini(adev); + if (adev->discovery.ip_top && !adev->discovery.ip_top->standalone_mode) + amdgpu_discovery_sysfs_fini(adev); + kfree(adev->discovery.bin); adev->discovery.bin = NULL; } @@ -737,15 +772,17 @@ static int amdgpu_discovery_validate_ip(struct amdgpu_device *adev, uint8_t instance, uint16_t hw_id) { if (instance >= HWIP_MAX_INSTANCE) { - dev_err(adev->dev, - "Unexpected instance_number (%d) from ip discovery blob\n", - instance); + if (adev) + dev_err(adev->dev, + "Unexpected instance_number (%d) from ip discovery blob\n", + instance); return -EINVAL; } if (hw_id >= HW_ID_MAX) { - dev_err(adev->dev, - "Unexpected hw_id (%d) from ip discovery blob\n", - hw_id); + if (adev) + dev_err(adev->dev, + "Unexpected hw_id (%d) from ip discovery blob\n", + hw_id); return -EINVAL; } @@ -1111,12 +1148,6 @@ static const struct kobj_type ip_discovery_ktype = { .sysfs_ops = &kobj_sysfs_ops, }; -struct ip_discovery_top { - struct kobject kobj; /* ip_discovery/ */ - struct kset die_kset; /* ip_discovery/die/, contains ip_die_entry */ - struct amdgpu_device *adev; -}; - static void die_kobj_release(struct kobject *kobj) { struct ip_discovery_top *ip_top = container_of(to_kset(kobj), @@ -1132,8 +1163,14 @@ static void ip_disc_release(struct kobject *kobj) kobj); struct amdgpu_device *adev = ip_top->adev; + /* In standalone mode, discovery_bin is managed by devm and will be + * freed automatically when the PCI device is removed. Do not manually + * free it here to avoid double-free. + */ + kfree(ip_top); - adev->discovery.ip_top = NULL; + if (adev) + adev->discovery.ip_top = NULL; } static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev, @@ -1141,6 +1178,10 @@ static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev, { uint8_t harvest = 0; + /* In early init mode (adev == NULL), harvest info is not available */ + if (!adev) + return 0; + /* Until a uniform way is figured, get mask based on hwid */ switch (hw_id) { case VCN_HWID: @@ -1169,11 +1210,14 @@ static uint8_t amdgpu_discovery_get_harvest_info(struct amdgpu_device *adev, } static int amdgpu_discovery_sysfs_ips(struct amdgpu_device *adev, + struct ip_discovery_top *ip_top, struct ip_die_entry *ip_die_entry, const size_t _ip_offset, const int num_ips, bool reg_base_64) { - uint8_t *discovery_bin = adev->discovery.bin; + uint8_t *discovery_bin = ip_top->standalone_mode ? + ip_top->discovery_bin : + adev->discovery.bin; int ii, jj, kk, res; uint16_t hw_id; uint8_t inst; @@ -1270,10 +1314,12 @@ next_ip: return 0; } -static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev) +static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev, + struct ip_discovery_top *ip_top) { - struct ip_discovery_top *ip_top = adev->discovery.ip_top; - uint8_t *discovery_bin = adev->discovery.bin; + uint8_t *discovery_bin = ip_top->standalone_mode ? + ip_top->discovery_bin : + adev->discovery.bin; struct table_info *info; struct ip_discovery_header *ihdr; struct die_header *dhdr; @@ -1282,9 +1328,10 @@ static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev) size_t ip_offset; int ii, res; - res = amdgpu_discovery_get_table_info(adev, &info, IP_DISCOVERY); - if (res) - return res; + info = amdgpu_discovery_get_table_info_from_bin(discovery_bin, + IP_DISCOVERY); + if (!info) + return -EINVAL; ihdr = (struct ip_discovery_header *)(discovery_bin + le16_to_cpu(info->offset)); @@ -1322,7 +1369,8 @@ static int amdgpu_discovery_sysfs_recurse(struct amdgpu_device *adev) return res; } - amdgpu_discovery_sysfs_ips(adev, ip_die_entry, ip_offset, num_ips, !!ihdr->base_addr_64_bit); + amdgpu_discovery_sysfs_ips(adev, ip_top, ip_die_entry, ip_offset, + num_ips, !!ihdr->base_addr_64_bit); } return 0; @@ -1338,12 +1386,30 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev) if (!discovery_bin) return -EINVAL; + /* If early init already created sysfs in standalone mode, skip normal init */ + if (adev->discovery.ip_top && adev->discovery.ip_top->standalone_mode) + return 0; + ip_top = kzalloc_obj(*ip_top); if (!ip_top) return -ENOMEM; ip_top->adev = adev; - adev->discovery.ip_top = ip_top; + + /* Check if ip_discovery already exists before creating. + * This shouldn't normally happen but handle it gracefully. + */ + if (adev->dev->kobj.sd) { + struct kernfs_node *existing; + + existing = kernfs_find_and_get(adev->dev->kobj.sd, "ip_discovery"); + if (existing) { + kernfs_put(existing); + kfree(ip_top); + return 0; + } + } + res = kobject_init_and_add(&ip_top->kobj, &ip_discovery_ktype, &adev->dev->kobj, "ip_discovery"); if (res) { @@ -1351,6 +1417,8 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev) goto Err; } + adev->discovery.ip_top = ip_top; + die_kset = &ip_top->die_kset; kobject_set_name(&die_kset->kobj, "%s", "die"); die_kset->kobj.parent = &ip_top->kobj; @@ -1365,7 +1433,7 @@ static int amdgpu_discovery_sysfs_init(struct amdgpu_device *adev) ip_hw_instance_attrs[ii] = &ip_hw_attr[ii].attr; ip_hw_instance_attrs[ii] = NULL; - res = amdgpu_discovery_sysfs_recurse(adev); + res = amdgpu_discovery_sysfs_recurse(adev, ip_top); return res; Err: @@ -1412,7 +1480,7 @@ static void amdgpu_discovery_sysfs_die_free(struct ip_die_entry *ip_die_entry) kobject_put(&ip_die_entry->ip_kset.kobj); } -static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev) +void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev) { struct ip_discovery_top *ip_top = adev->discovery.ip_top; struct list_head *el, *tmp; @@ -1421,6 +1489,16 @@ static void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev) if (!ip_top) return; + /* + * In standalone mode the sysfs hierarchy is tied to the PCI device + * lifetime and is torn down by amdgpu_discovery_sysfs_early_fini(). + * Freeing it here would leave a dangling pointer in the early + * discovery list, causing a use-after-free on driver unbind. + */ + if (ip_top->standalone_mode) + return; + + adev->discovery.ip_top = NULL; die_kset = &ip_top->die_kset; spin_lock(&die_kset->list_lock); list_for_each_prev_safe(el, tmp, &die_kset->list) { @@ -1479,6 +1557,150 @@ void amdgpu_discovery_dump(struct amdgpu_device *adev, struct drm_printer *p) spin_unlock(&die_kset->list_lock); } +int amdgpu_discovery_sysfs_early_init(struct amdgpu_device *adev, struct pci_dev *pdev) +{ + struct ip_discovery_top *ip_top; + struct early_ip_discovery *early_entry, *tmp; + struct kset *die_kset; + uint8_t *discovery_bin; + int res, ii; + + if (!adev || !adev->discovery.bin) + return -EINVAL; + + if (adev->discovery.ip_top) + return 0; + + mutex_lock(&early_ip_discovery_mutex); + list_for_each_entry_safe(early_entry, tmp, &early_ip_discovery_list, list) { + if (early_entry->pdev == pdev) { + adev->discovery.ip_top = early_entry->ip_top; + early_entry->ip_top->adev = adev; + mutex_unlock(&early_ip_discovery_mutex); + return 0; + } + } + mutex_unlock(&early_ip_discovery_mutex); + + discovery_bin = adev->discovery.bin; + + early_entry = kzalloc(sizeof(*early_entry), GFP_KERNEL); + if (!early_entry) + return -ENOMEM; + + ip_top = kzalloc(sizeof(*ip_top), GFP_KERNEL); + if (!ip_top) { + kfree(early_entry); + return -ENOMEM; + } + + ip_top->discovery_bin = devm_kmemdup(&pdev->dev, discovery_bin, + DISCOVERY_TMR_SIZE, GFP_KERNEL); + if (!ip_top->discovery_bin) { + kfree(ip_top); + kfree(early_entry); + return -ENOMEM; + } + + ip_top->bin_size = DISCOVERY_TMR_SIZE; + ip_top->pdev = pdev; + ip_top->adev = adev; + ip_top->standalone_mode = true; + + /* Check if ip_discovery already exists (from previous probe attempt). + * This can happen if the module was unloaded and reloaded but the + * sysfs persisted (tied to PCI device lifetime). + */ + if (pdev->dev.kobj.sd) { + struct kernfs_node *existing; + + existing = kernfs_find_and_get(pdev->dev.kobj.sd, "ip_discovery"); + if (existing) { + kernfs_put(existing); + kfree(ip_top); + kfree(early_entry); + return 0; + } + } + + res = kobject_init_and_add(&ip_top->kobj, &ip_discovery_ktype, + &pdev->dev.kobj, "ip_discovery"); + if (res) + goto err_put_kobj; + + adev->discovery.ip_top = ip_top; + + die_kset = &ip_top->die_kset; + kobject_set_name(&die_kset->kobj, "%s", "die"); + die_kset->kobj.parent = &ip_top->kobj; + die_kset->kobj.ktype = &die_kobj_ktype; + res = kset_register(&ip_top->die_kset); + if (res) + goto err_put_die_kset; + + for (ii = 0; ii < ARRAY_SIZE(ip_hw_attr); ii++) + ip_hw_instance_attrs[ii] = &ip_hw_attr[ii].attr; + ip_hw_instance_attrs[ii] = NULL; + + res = amdgpu_discovery_sysfs_recurse(NULL, ip_top); + if (res) + goto err_put_die_kset; + + early_entry->pdev = pdev; + early_entry->ip_top = ip_top; + mutex_lock(&early_ip_discovery_mutex); + list_add(&early_entry->list, &early_ip_discovery_list); + mutex_unlock(&early_ip_discovery_mutex); + + return 0; + +err_put_die_kset: + kobject_put(&ip_top->die_kset.kobj); +err_put_kobj: + kobject_put(&ip_top->kobj); + kfree(early_entry); + adev->discovery.ip_top = NULL; + return res; +} + +void amdgpu_discovery_sysfs_early_fini(struct pci_dev *pdev) +{ + struct early_ip_discovery *entry, *tmp_entry; + struct ip_discovery_top *ip_top = NULL; + struct list_head *el, *tmp; + struct kset *die_kset; + + /* Find the entry in our tracking list */ + mutex_lock(&early_ip_discovery_mutex); + list_for_each_entry_safe(entry, tmp_entry, &early_ip_discovery_list, list) { + if (entry->pdev == pdev) { + ip_top = entry->ip_top; + list_del(&entry->list); + kfree(entry); + break; + } + } + mutex_unlock(&early_ip_discovery_mutex); + + if (!ip_top) + return; + + /* Clean up sysfs hierarchy */ + die_kset = &ip_top->die_kset; + + spin_lock(&die_kset->list_lock); + list_for_each_prev_safe(el, tmp, &die_kset->list) { + list_del_init(el); + spin_unlock(&die_kset->list_lock); + amdgpu_discovery_sysfs_die_free(to_ip_die_entry(list_to_kobj(el))); + spin_lock(&die_kset->list_lock); + } + spin_unlock(&die_kset->list_lock); + + kobject_put(&ip_top->die_kset.kobj); + kobject_put(&ip_top->kobj); + /* ip_top itself will be freed by kobject_put via ip_disc_release */ +} /* ================================================== */ @@ -1504,6 +1726,9 @@ static int amdgpu_discovery_reg_base_init(struct amdgpu_device *adev) r = amdgpu_discovery_init(adev); if (r) return r; + + amdgpu_discovery_sysfs_early_init(adev, adev->pdev); + discovery_bin = adev->discovery.bin; wafl_ver = 0; adev->gfx.xcc_mask = 0; @@ -2119,6 +2344,8 @@ static int amdgpu_discovery_set_common_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): amdgpu_device_ip_block_add(adev, &soc21_common_ip_block); break; case IP_VERSION(12, 0, 0): @@ -2180,6 +2407,8 @@ static int amdgpu_discovery_set_gmc_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): amdgpu_device_ip_block_add(adev, &gmc_v11_0_ip_block); break; case IP_VERSION(12, 0, 0): @@ -2506,6 +2735,8 @@ static int amdgpu_discovery_set_gc_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): amdgpu_device_ip_block_add(adev, &gfx_v11_0_ip_block); break; case IP_VERSION(12, 0, 0): @@ -2630,7 +2861,12 @@ static int amdgpu_discovery_set_mm_ip_blocks(struct amdgpu_device *adev) return -EINVAL; } } else { - switch (amdgpu_ip_version(adev, UVD_HWIP, 0)) { + uint32_t vcn_version = amdgpu_ip_version(adev, UVD_HWIP, 0); + + /* no VCN discovered; nothing to add */ + if (!vcn_version) + return 0; + switch (vcn_version) { case IP_VERSION(1, 0, 0): case IP_VERSION(1, 0, 1): amdgpu_device_ip_block_add(adev, &vcn_v1_0_ip_block); @@ -2698,7 +2934,7 @@ static int amdgpu_discovery_set_mm_ip_blocks(struct amdgpu_device *adev) default: dev_err(adev->dev, "Failed to add vcn/jpeg ip block(UVD_HWIP:0x%x)\n", - amdgpu_ip_version(adev, UVD_HWIP, 0)); + vcn_version); return -EINVAL; } } @@ -2719,6 +2955,8 @@ static int amdgpu_discovery_set_mes_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): amdgpu_device_ip_block_add(adev, &mes_v11_0_ip_block); adev->enable_mes = true; adev->enable_mes_kiq = true; @@ -3127,6 +3365,8 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->family = AMDGPU_FAMILY_GC_11_5_0; break; case IP_VERSION(12, 0, 0): @@ -3156,6 +3396,8 @@ int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->flags |= AMD_IS_APU; break; default: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h index e0010f6a3eda..5b2b16f68576 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.h @@ -41,6 +41,7 @@ struct amdgpu_discovery_info { bool reserve_tmr; }; +void amdgpu_discovery_sysfs_fini(struct amdgpu_device *adev); void amdgpu_discovery_fini(struct amdgpu_device *adev); int amdgpu_discovery_set_ip_blocks(struct amdgpu_device *adev); @@ -53,4 +54,9 @@ int amdgpu_discovery_get_gc_major_minor_version(struct amdgpu_device *adev, void amdgpu_discovery_dump(struct amdgpu_device *adev, struct drm_printer *p); +/* Early sysfs functions for persistent ip_discovery export */ +int amdgpu_discovery_sysfs_early_init(struct amdgpu_device *adev, + struct pci_dev *pdev); +void amdgpu_discovery_sysfs_early_fini(struct pci_dev *pdev); + #endif /* __AMDGPU_DISCOVERY__ */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index bf4260269681..ad631ad31899 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -33,7 +33,6 @@ #include <drm/drm_vblank.h> #include <linux/cc_platform.h> -#include <linux/console.h> #include <linux/dynamic_debug.h> #include <linux/module.h> #include <linux/mmu_notifier.h> @@ -146,7 +145,9 @@ enum AMDGPU_DEBUG_MASK { AMDGPU_DEBUG_SMU_POOL = BIT(7), AMDGPU_DEBUG_VM_USERPTR = BIT(8), AMDGPU_DEBUG_DISABLE_RAS_CE_LOG = BIT(9), - AMDGPU_DEBUG_ENABLE_CE_CS = BIT(10) + AMDGPU_DEBUG_ENABLE_CE_CS = BIT(10), + AMDGPU_DEBUG_HIBERNATION_THAW_RESUME_GPU = BIT(11), + AMDGPU_DEBUG_DISABLE_IP_BLOCK_SOFT_RESET = BIT(12), }; unsigned int amdgpu_vram_limit = UINT_MAX; @@ -1939,6 +1940,7 @@ static const struct pci_device_id pciidlist[] = { {0x1002, 0x6646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY}, {0x1002, 0x6647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE|AMD_IS_MOBILITY}, {0x1002, 0x6649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, + {0x1002, 0x664D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, {0x1002, 0x6650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, {0x1002, 0x6651, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, {0x1002, 0x6658, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_BONAIRE}, @@ -2008,6 +2010,7 @@ static const struct pci_device_id pciidlist[] = { {0x1002, 0x6930, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, {0x1002, 0x6938, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, {0x1002, 0x6939, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, + {0x1002, 0x693B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_TONGA}, /* fiji */ {0x1002, 0x7300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_FIJI}, {0x1002, 0x730F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_FIJI}, @@ -2036,6 +2039,7 @@ static const struct pci_device_id pciidlist[] = { {0x1002, 0x67C4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, {0x1002, 0x67C7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, {0x1002, 0x67D0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, + {0x1002, 0x67D4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, {0x1002, 0x67DF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, {0x1002, 0x67C8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, {0x1002, 0x67C9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS10}, @@ -2049,6 +2053,7 @@ static const struct pci_device_id pciidlist[] = { {0x1002, 0x6985, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, {0x1002, 0x6986, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, {0x1002, 0x6987, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, + {0x1002, 0x698F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, {0x1002, 0x6995, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, {0x1002, 0x6997, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, {0x1002, 0x699F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_POLARIS12}, @@ -2250,7 +2255,7 @@ static void amdgpu_init_debug_options(struct amdgpu_device *adev) } if (amdgpu_debug_mask & AMDGPU_DEBUG_DISABLE_GPU_SOFT_RECOVERY) { - pr_info("debug: soft reset for GPU recovery disabled\n"); + pr_info("debug: soft recovery disabled\n"); adev->debug_disable_soft_recovery = true; } @@ -2291,6 +2296,16 @@ static void amdgpu_init_debug_options(struct amdgpu_device *adev) pr_info("debug: allowing command submission to CE engine\n"); adev->debug_enable_ce_cs = true; } + + if (amdgpu_debug_mask & AMDGPU_DEBUG_HIBERNATION_THAW_RESUME_GPU) { + pr_info("debug: resume gpu in thaw() of hibernation\n"); + adev->debug_hibernation_thaw_resume_gpu = true; + } + + if (amdgpu_debug_mask & AMDGPU_DEBUG_DISABLE_IP_BLOCK_SOFT_RESET) { + pr_info("debug: IP block soft reset disabled\n"); + adev->debug_disable_ip_block_soft_reset = true; + } } static unsigned long amdgpu_fix_asic_type(struct pci_dev *pdev, unsigned long flags) @@ -2552,6 +2567,8 @@ amdgpu_pci_remove(struct pci_dev *pdev) amdgpu_driver_unload_kms(dev); + amdgpu_discovery_sysfs_early_fini(pdev); + /* * Flush any in flight DMA operations from device. * Clear the Bus Master Enable bit and then wait on the PCIe Device @@ -2705,9 +2722,10 @@ static int amdgpu_pmops_freeze(struct device *dev) static int amdgpu_pmops_thaw(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); + struct amdgpu_device *adev = drm_to_adev(drm_dev); /* do not resume device if it's normal hibernation */ - if (console_suspend_enabled && + if (!adev->debug_hibernation_thaw_resume_gpu && !pm_hibernate_is_recovering() && !pm_hibernation_mode_is_suspend()) return 0; @@ -3076,6 +3094,7 @@ const struct drm_ioctl_desc amdgpu_ioctls_kms[] = { DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_SIGNAL, amdgpu_userq_signal_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_USERQ_WAIT, amdgpu_userq_wait_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(AMDGPU_GEM_LIST_HANDLES, amdgpu_gem_list_handles_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(AMDGPU_PROC_OPTIONS, amdgpu_proc_options_ioctl, DRM_AUTH|DRM_RENDER_ALLOW), }; static const struct drm_driver amdgpu_kms_driver = { @@ -3196,6 +3215,14 @@ static void __exit amdgpu_exit(void) amdgpu_sync_fini(); mmu_notifier_synchronize(); amdgpu_xcp_drv_release(); + + /* + * Flush outstanding call_rcu() callbacks before the + * module text is freed. Otherwise a grace period elapsing after + * unload invokes a callback in already-freed module memory and + * faults in rcu_do_batch(). + */ + rcu_barrier(); } module_init(amdgpu_init); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c index ea69b1bac7c6..3043ad041bb4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c @@ -727,6 +727,15 @@ void amdgpu_ring_set_fence_errors_and_reemit(struct amdgpu_ring *ring, last_seq = amdgpu_fence_read(ring) & ring->fence_drv.num_fences_mask; seq = ring->fence_drv.sync_seq & ring->fence_drv.num_fences_mask; + /* If there is nothing to reemit, return early and set an error on the fence + * if applicable. If all of the fences are siganlled, this will be a nop. + * if there are still fences and ring_backup_entries_to_copy is 0, then + * we are skipping it on purpose. + */ + if (!ring->ring_backup_entries_to_copy) { + amdgpu_fence_driver_force_completion(ring, &guilty_fence->base); + return; + } ring->reemit = true; amdgpu_ring_alloc(ring, ring->ring_backup_entries_to_copy); spin_lock_irqsave(&ring->fence_drv.lock, flags); @@ -741,7 +750,8 @@ void amdgpu_ring_set_fence_errors_and_reemit(struct amdgpu_ring *ring, if (unprocessed && !dma_fence_is_signaled_locked(unprocessed)) { fence = container_of(unprocessed, struct amdgpu_fence, base); is_guilty_fence = fence == guilty_fence; - is_guilty_context = fence->context == guilty_fence->context; + is_guilty_context = guilty_fence ? + (fence->context == guilty_fence->context) : false; /* mark all fences from the guilty context with an error */ if (is_guilty_fence) @@ -794,6 +804,17 @@ void amdgpu_ring_backup_unprocessed_commands(struct amdgpu_ring *ring, seq = ring->fence_drv.sync_seq & ring->fence_drv.num_fences_mask; ring->ring_backup_entries_to_copy = 0; + /* if we've already seen this fence, return early. + * ring->ring_backup_entries_to_copy is set to 0 so + * the reemit helper will return early as well to + * avoid getting stuck in a reemit loop. + */ + if (ring->guilty_fence == guilty_fence) { + ring->guilty_fence = NULL; + return; + } + ring->guilty_fence = guilty_fence; + do { last_seq++; last_seq &= ring->fence_drv.num_fences_mask; @@ -811,6 +832,36 @@ void amdgpu_ring_backup_unprocessed_commands(struct amdgpu_ring *ring, } while (last_seq != seq); } +struct amdgpu_fence * +amdgpu_ring_find_guilty_fence(struct amdgpu_ring *ring) +{ + struct dma_fence *unprocessed; + struct dma_fence __rcu **ptr; + struct amdgpu_fence *fence; + u32 seq, last_seq; + + last_seq = amdgpu_fence_read(ring) & ring->fence_drv.num_fences_mask; + seq = ring->fence_drv.sync_seq & ring->fence_drv.num_fences_mask; + + do { + last_seq++; + last_seq &= ring->fence_drv.num_fences_mask; + + ptr = &ring->fence_drv.fences[last_seq]; + rcu_read_lock(); + unprocessed = rcu_dereference(*ptr); + + if (unprocessed && !dma_fence_is_signaled(unprocessed)) { + fence = container_of(unprocessed, struct amdgpu_fence, base); + rcu_read_unlock(); + return fence; + } + rcu_read_unlock(); + } while (last_seq != seq); + + return NULL; +} + /* * Common fence implementation */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index 76da3f932f24..6a0699746fbc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -535,6 +535,7 @@ int amdgpu_gem_userptr_ioctl(struct drm_device *dev, void *data, bo = gem_to_amdgpu_bo(gobj); bo->preferred_domains = AMDGPU_GEM_DOMAIN_GTT; bo->allowed_domains = AMDGPU_GEM_DOMAIN_GTT; + bo->parent = amdgpu_bo_ref(fpriv->vm.root.bo); r = amdgpu_ttm_tt_set_userptr(&bo->tbo, args->addr, args->flags); if (r) goto release_object; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c index 85372af1216d..96c9d4f00b27 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.c @@ -34,6 +34,7 @@ #include "amdgpu_xcp.h" #include "amdgpu_xgmi.h" #include "amdgpu_mes.h" +#include "mes_userqueue.h" #include "nvd.h" /* delay 0.1 second to enable gfx off feature */ @@ -377,6 +378,30 @@ int amdgpu_gfx_kiq_init(struct amdgpu_device *adev, return 0; } +static void amdgpu_gfx_mqd_reset_restore(struct amdgpu_ring *ring) +{ + struct amdgpu_device *adev = ring->adev; + int mqd_idx, mqd_size; + + /* restore mqd with the backup copy */ + if (ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE) { + mqd_idx = ring - &adev->gfx.compute_ring[0]; + mqd_size = adev->mqds[AMDGPU_HW_IP_COMPUTE].mqd_size; + if (adev->gfx.mec.mqd_backup[mqd_idx]) + memcpy_toio(ring->mqd_ptr, adev->gfx.mec.mqd_backup[mqd_idx], mqd_size); + } else if (ring->funcs->type == AMDGPU_RING_TYPE_GFX) { + mqd_size = adev->mqds[AMDGPU_HW_IP_GFX].mqd_size; + mqd_idx = ring - &adev->gfx.gfx_ring[0]; + + if (adev->gfx.me.mqd_backup[mqd_idx]) + memcpy_toio(ring->mqd_ptr, adev->gfx.me.mqd_backup[mqd_idx], mqd_size); + } + /* reset the ring */ + ring->wptr = 0; + atomic64_set((atomic64_t *)ring->wptr_cpu_addr, 0); + amdgpu_ring_clear_ring(ring); +} + /* create MQD for each compute/gfx queue */ int amdgpu_gfx_mqd_sw_init(struct amdgpu_device *adev, unsigned int mqd_size, int xcc_id) @@ -1964,6 +1989,60 @@ static ssize_t amdgpu_gfx_get_compute_reset_mask(struct device *dev, return amdgpu_show_reset_mask(buf, adev->gfx.compute_supported_reset); } +static int amdgpu_gfx_mes_reset_queue_start(struct amdgpu_ring *ring, + unsigned int vmid, + struct amdgpu_fence *timedout_fence, + bool use_mmio) +{ + struct amdgpu_device *adev = ring->adev; + bool reinit_queue; + int r; + + if ((ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE) && + adev->mes.compute_pipe_reset_enabled) + reinit_queue = true; + else if ((ring->funcs->type == AMDGPU_RING_TYPE_GFX) && + adev->mes.gfx_pipe_reset_enabled) + reinit_queue = true; + else + reinit_queue = use_mmio; + + amdgpu_ring_reset_helper_begin(ring, timedout_fence); + + r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio, 0); + if (r) + return r; + + if (reinit_queue) { + r = amdgpu_mes_unmap_legacy_queue(adev, ring, + RESET_QUEUES, 0, 0, 0); + if (r) + return r; + amdgpu_gfx_mqd_reset_restore(ring); + + r = amdgpu_mes_map_legacy_queue(adev, ring, 0); + if (r) { + dev_err(adev->dev, "failed to remap kgq\n"); + return r; + } + } + return 0; +} + +int amdgpu_gfx_mes_reset_queue(struct amdgpu_ring *ring, + unsigned int vmid, + struct amdgpu_fence *timedout_fence, + bool use_mmio) +{ + int r; + + r = amdgpu_gfx_mes_reset_queue_start(ring, vmid, timedout_fence, + use_mmio); + if (r) + return r; + return amdgpu_ring_reset_helper_end(ring, timedout_fence); +} + static DEVICE_ATTR(run_cleaner_shader, 0200, NULL, amdgpu_gfx_set_run_cleaner_shader); @@ -2122,6 +2201,200 @@ void amdgpu_gfx_sysfs_fini(struct amdgpu_device *adev) } } +static void amdgpu_gfx_reset_start_compute_scheds(struct amdgpu_device *adev, + struct amdgpu_ring *guilty_ring) +{ + struct amdgpu_ring *ring; + int i; + + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + ring = &adev->gfx.compute_ring[i]; + if (ring == guilty_ring) + continue; + drm_sched_wqueue_start(&ring->sched); + } +} + +static void amdgpu_gfx_reset_stop_compute_scheds(struct amdgpu_device *adev, + struct amdgpu_ring *guilty_ring) +{ + struct amdgpu_ring *ring; + int i; + + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + ring = &adev->gfx.compute_ring[i]; + if (ring == guilty_ring) + continue; + drm_sched_wqueue_stop(&ring->sched); + } +} + +/* + * Match the MES-reported hung doorbell against a compute ring and run + * the reset. On hit, the matched ring and its guilty fence are returned + * via *out_ring / *out_fence so the caller can defer reset end until + * after MES has resumed all gangs. + */ +static int amdgpu_gfx_reset_mes_kcq(struct amdgpu_device *adev, + struct amdgpu_ring *guilty_ring, + unsigned int db, + struct amdgpu_ring **out_ring, + struct amdgpu_fence **out_fence) +{ + bool use_mmio = adev->gfx.mec.use_mmio_for_reset; + struct amdgpu_fence *fence; + struct amdgpu_ring *ring; + int i, r; + + *out_ring = NULL; + *out_fence = NULL; + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + ring = &adev->gfx.compute_ring[i]; + if (ring == guilty_ring) + continue; + if (ring->doorbell_index == db) { + fence = amdgpu_ring_find_guilty_fence(ring); + r = amdgpu_gfx_mes_reset_queue_start(ring, 0, fence, + use_mmio); + if (r) + return r; + *out_ring = ring; + *out_fence = fence; + break; + } + } + return 0; +} + +int amdgpu_gfx_reset_mes_compute(struct amdgpu_device *adev, + struct amdgpu_ring *ring, + struct amdgpu_fence *guilty_fence, + struct amdgpu_usermode_queue *uq, + unsigned int *hung_queue_count, + void *faulty_queue_input) +{ + struct amdgpu_mes_hung_queue_hqd_info *hqd_info = + (struct amdgpu_mes_hung_queue_hqd_info *) + &adev->gfx.mec.mes_hung_db_array[adev->mes.hung_queue_hqd_info_offset]; + int i, r, pipe, queue, queue_type; + unsigned int num_hung = 0; + bool use_mmio = adev->gfx.mec.use_mmio_for_reset; + struct mes_remove_queue_input *queue_input = (struct mes_remove_queue_input *)faulty_queue_input; + struct amdgpu_gfx_deferred_entry deferred_end[AMDGPU_MAX_COMPUTE_RINGS + 1]; + int n_deferred = 0; + int ring_err; + + guard(mutex)(&adev->gfx.mec.reset_mutex); + /* stop the drm schedulers for all compute queues */ + amdgpu_gfx_reset_stop_compute_scheds(adev, ring); + /* suspend all will determine which queues are hung. + * reset detect will return the array of bad queue doorbells + */ + r = amdgpu_mes_suspend(adev, 0); + /* if suspend all success, it should no hang queue */ + if (!r) + /* always reset the KCQ/userq since we need to signal the fence + * and we could be stuck in a loop which is preemptable. + */ + goto fence_reset; + r = amdgpu_mes_detect_and_reset_hung_queues(adev, AMDGPU_RING_TYPE_COMPUTE, + true, &num_hung, adev->gfx.mec.mes_hung_db_array, 0); + if (r) + goto out; + if (hung_queue_count) + *hung_queue_count = num_hung; + +fence_reset: + /* reset the queue this came from if specified */ + if (ring) { + r = amdgpu_gfx_mes_reset_queue_start(ring, 0, guilty_fence, + use_mmio); + if (r) + goto out; + deferred_end[n_deferred].ring = ring; + deferred_end[n_deferred].fence = guilty_fence; + n_deferred++; + } + if (uq) { + r = mes_userq_reset(uq); + if (r) + goto out; + } + for (i = 0; i < num_hung; i++) { + struct amdgpu_ring *hr = NULL; + struct amdgpu_fence *hf = NULL; + + pipe = hqd_info[i].pipe_index; + queue = hqd_info[i].queue_index; + queue_type = hqd_info[i].queue_type; + + /* reset any KCQs */ + r = amdgpu_gfx_reset_mes_kcq(adev, ring, + adev->gfx.mec.mes_hung_db_array[i], + &hr, &hf); + if (r) + goto out; + if (hr) { + deferred_end[n_deferred].ring = hr; + deferred_end[n_deferred].fence = hf; + n_deferred++; + } + /* reset any KFD queues */ + r = amdgpu_amdkfd_reset_mes_queue(adev, 0, queue_type, pipe, queue, + adev->gfx.mec.mes_hung_db_array[i]); + if (r) + goto out; + /* reset KGD user queues */ + r = mes_userq_reset_queue(adev, uq, queue_type, pipe, queue, + adev->gfx.mec.mes_hung_db_array[i]); + if (r) + goto out; + } + + /* MES doesn't detect any hung queue but we have a known bad queue + * and it is not KCQ + */ + if (!num_hung && queue_input && !ring) { + /* MES suspend_all is successful means this bad queue is + * preempted successfuly. Remove it before resume all so it + * doesn't get mapped back + */ + if (!down_read_trylock(&adev->reset_domain->sem)) { + r = -EIO; + goto out; + } + amdgpu_mes_lock(&adev->mes); + r = adev->mes.funcs->remove_hw_queue(&adev->mes, queue_input); + amdgpu_mes_unlock(&adev->mes); + up_read(&adev->reset_domain->sem); + } + +out: + /* resume all will enable the non-hung queues */ + amdgpu_mes_resume(adev, 0); + + /* Now CP is running again — replay backed-up commands and ring + * doorbells on each reset queue. + */ + ring_err = r; + for (i = 0; i < n_deferred; i++) { + int er = amdgpu_ring_reset_helper_end(deferred_end[i].ring, + deferred_end[i].fence); + + if (er && !ring_err) + ring_err = er; + } + + if (!ring_err) + amdgpu_gfx_reset_start_compute_scheds(adev, ring); + + /* If this reset is triggered by non-KCQ, the KCQ result after resume must + * not override the reset result; otherwise a false reset failure is returned + * to the non-KCQ caller + */ + return ring ? ring_err : r; +} + int amdgpu_gfx_cleaner_shader_sw_init(struct amdgpu_device *adev, unsigned int cleaner_shader_size) { @@ -2460,9 +2733,8 @@ void amdgpu_gfx_profile_ring_begin_use(struct amdgpu_ring *ring) else profile = PP_SMC_POWER_PROFILE_COMPUTE; - atomic_inc(&adev->gfx.total_submission_cnt); - - cancel_delayed_work_sync(&adev->gfx.idle_work); + if (!atomic_fetch_inc(&adev->gfx.total_submission_cnt)) + cancel_delayed_work_sync(&adev->gfx.idle_work); /* We can safely return early here because we've cancelled the * the delayed work so there is no one else to set it to false @@ -2490,9 +2762,9 @@ void amdgpu_gfx_profile_ring_end_use(struct amdgpu_ring *ring) if (amdgpu_dpm_is_overdrive_enabled(adev)) return; - atomic_dec(&ring->adev->gfx.total_submission_cnt); - - schedule_delayed_work(&ring->adev->gfx.idle_work, GFX_PROFILE_IDLE_TIMEOUT); + if (atomic_dec_and_test(&ring->adev->gfx.total_submission_cnt)) + schedule_delayed_work(&ring->adev->gfx.idle_work, + GFX_PROFILE_IDLE_TIMEOUT); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h index 54c1eb9c499b..aefd4f03b443 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h @@ -36,6 +36,8 @@ #include "amdgpu_ring_mux.h" #include "amdgpu_xcp.h" +struct amdgpu_usermode_queue; + /* GFX current status */ #define AMDGPU_GFX_NORMAL_MODE 0x00000000L #define AMDGPU_GFX_SAFE_MODE 0x00000001L @@ -116,6 +118,9 @@ struct amdgpu_mec { u32 num_pipe_per_mec; u32 num_queue_per_pipe; void *mqd_backup[AMDGPU_MAX_COMPUTE_RINGS * AMDGPU_MAX_GC_INSTANCES]; + bool use_mmio_for_reset; + u32 *mes_hung_db_array; + struct mutex reset_mutex; }; struct amdgpu_mec_bitmap { @@ -401,6 +406,7 @@ struct amdgpu_me { uint32_t num_pipe_per_me; uint32_t num_queue_per_pipe; void *mqd_backup[AMDGPU_MAX_GFX_RINGS]; + bool use_mmio_for_reset; /* These are the resources for which amdgpu takes ownership */ DECLARE_BITMAP(queue_bitmap, AMDGPU_MAX_GFX_QUEUES); @@ -479,8 +485,6 @@ struct amdgpu_gfx { const struct amdgpu_gfx_funcs *funcs; /* reset mask */ - uint32_t grbm_soft_reset; - uint32_t srbm_soft_reset; uint32_t gfx_supported_reset; uint32_t compute_supported_reset; @@ -543,6 +547,11 @@ struct amdgpu_gfx { bool disable_uq; }; +struct amdgpu_gfx_deferred_entry { + struct amdgpu_ring *ring; + struct amdgpu_fence *fence; +}; + struct amdgpu_gfx_ras_reg_entry { struct amdgpu_ras_err_status_reg_entry reg_entry; enum amdgpu_gfx_ras_mem_id_type mem_id_type; @@ -641,6 +650,12 @@ int amdgpu_gfx_poison_consumption_handler(struct amdgpu_device *adev, bool amdgpu_gfx_is_master_xcc(struct amdgpu_device *adev, int xcc_id); int amdgpu_gfx_sysfs_init(struct amdgpu_device *adev); void amdgpu_gfx_sysfs_fini(struct amdgpu_device *adev); +int amdgpu_gfx_reset_mes_compute(struct amdgpu_device *adev, + struct amdgpu_ring *ring, + struct amdgpu_fence *guilty_fence, + struct amdgpu_usermode_queue *uq, + unsigned int *hung_queue_count, + void *faulty_queue_input); void amdgpu_gfx_ras_error_func(struct amdgpu_device *adev, void *ras_error_status, void (*func)(struct amdgpu_device *adev, void *ras_error_status, @@ -667,6 +682,11 @@ void amdgpu_debugfs_compute_sched_mask_init(struct amdgpu_device *adev); int amdgpu_gfx_ring_preempt_ib(struct amdgpu_ring *ring); +int amdgpu_gfx_mes_reset_queue(struct amdgpu_ring *ring, + unsigned int vmid, + struct amdgpu_fence *timedout_fence, + bool use_mmio); + static inline const char *amdgpu_gfx_compute_mode_desc(int mode) { switch (mode) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c index 5f7745143f56..4000b2c6fc98 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c @@ -977,6 +977,8 @@ void amdgpu_gmc_tmz_set(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): /* Don't enable it by default yet. */ if (amdgpu_tmz < 1) { @@ -1761,10 +1763,15 @@ int amdgpu_gmc_init_mem_ranges(struct amdgpu_device *adev) valid = true; else valid = amdgpu_gmc_validate_partition_info(adev); - if (!valid) { - /* TODO: handle invalid case */ + if (!valid) dev_warn(adev->dev, "Mem ranges not matching with hardware config\n"); + + if (!adev->gmc.num_mem_partitions) { + dev_err(adev->dev, "num_mem_partitions is zero\n"); + kfree(adev->gmc.mem_partitions); + adev->gmc.mem_partitions = NULL; + return -EINVAL; } return 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h index ddb0d500e0fa..3ca187f5ade8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.h @@ -286,7 +286,6 @@ struct amdgpu_gmc { struct amdgpu_irq_src vm_fault; uint32_t vram_type; uint8_t vram_vendor; - uint32_t srbm_soft_reset; bool prt_warning; uint32_t sdpif_register; /* apertures */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index 99bc9ad67d5b..a7d13e337d84 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -67,7 +67,6 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, { struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier); struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); - struct amdgpu_bo *vm_root = bo->vm_bo->vm->root.bo; long r; if (!mmu_notifier_range_blockable(range)) @@ -78,7 +77,7 @@ static bool amdgpu_hmm_invalidate_gfx(struct mmu_interval_notifier *mni, mmu_interval_set_seq(mni, cur_seq); amdgpu_vm_bo_invalidate(bo, false); - r = dma_resv_wait_timeout(vm_root->tbo.base.resv, + r = dma_resv_wait_timeout(bo->parent->tbo.base.resv, DMA_RESV_USAGE_BOOKKEEP, false, MAX_SCHEDULE_TIMEOUT); mutex_unlock(&adev->notifier_lock); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c index 6aa54156bbc9..33a04113ed74 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.c @@ -369,43 +369,152 @@ int amdgpu_device_ip_wait_for_idle(struct amdgpu_device *adev, } /** - * amdgpu_device_ip_is_hw - is the hardware IP enabled + * amdgpu_device_ip_is_valid - is the hardware IP valid * * @adev: amdgpu_device pointer * @block_type: Type of hardware IP (SMU, GFX, UVD, etc.) * - * Check if the hardware IP is enable or not. - * Returns true if it the IP is enable, false if not. + * Check if the hardware IP is valid or not. + * Returns true if it the IP is valid, false if not. */ -bool amdgpu_device_ip_is_hw(struct amdgpu_device *adev, - enum amd_ip_block_type block_type) +bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev, + enum amd_ip_block_type block_type) { struct amdgpu_ip_block *ip_block; ip_block = amdgpu_device_ip_get_ip_block(adev, block_type); if (ip_block) - return ip_block->status.hw; + return ip_block->status.valid; return false; } /** - * amdgpu_device_ip_is_valid - is the hardware IP valid + * amdgpu_ip_from_ring() - Find IP block type corresponding to ring type. * - * @adev: amdgpu_device pointer - * @block_type: Type of hardware IP (SMU, GFX, UVD, etc.) + * @ring_type: The ring type whose IP block you are looking for. + */ +static enum amd_ip_block_type amdgpu_ip_from_ring(const enum amdgpu_ring_type ring_type) +{ + switch (ring_type) { + case AMDGPU_RING_TYPE_GFX: + case AMDGPU_RING_TYPE_COMPUTE: + return AMD_IP_BLOCK_TYPE_GFX; + + case AMDGPU_RING_TYPE_SDMA: + return AMD_IP_BLOCK_TYPE_SDMA; + + case AMDGPU_RING_TYPE_UVD: + case AMDGPU_RING_TYPE_UVD_ENC: + return AMD_IP_BLOCK_TYPE_UVD; + + case AMDGPU_RING_TYPE_VCE: + return AMD_IP_BLOCK_TYPE_VCE; + + case AMDGPU_RING_TYPE_VCN_DEC: + case AMDGPU_RING_TYPE_VCN_ENC: + return AMD_IP_BLOCK_TYPE_VCN; + + case AMDGPU_RING_TYPE_VCN_JPEG: + return AMD_IP_BLOCK_TYPE_JPEG; + + case AMDGPU_RING_TYPE_VPE: + return AMD_IP_BLOCK_TYPE_VPE; + + default: + return AMD_IP_BLOCK_TYPE_NUM; + } +} + +/** + * amdgpu_ring_mask_from_ip() - Find mask of ring types corresponding to an IP block type. * - * Check if the hardware IP is valid or not. - * Returns true if it the IP is valid, false if not. + * @ip_type: The IP block type whose rings you are looking for. */ -bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev, - enum amd_ip_block_type block_type) +static u32 amdgpu_ring_mask_from_ip(const enum amd_ip_block_type ip_type) +{ + switch (ip_type) { + case AMD_IP_BLOCK_TYPE_GFX: + return BIT(AMDGPU_RING_TYPE_GFX) | BIT(AMDGPU_RING_TYPE_COMPUTE); + + case AMD_IP_BLOCK_TYPE_SDMA: + return BIT(AMDGPU_RING_TYPE_SDMA); + + case AMD_IP_BLOCK_TYPE_UVD: + return BIT(AMDGPU_RING_TYPE_UVD) | BIT(AMDGPU_RING_TYPE_UVD_ENC); + + case AMD_IP_BLOCK_TYPE_VCE: + return BIT(AMD_IP_BLOCK_TYPE_VCE); + + case AMD_IP_BLOCK_TYPE_VCN: + return BIT(AMDGPU_RING_TYPE_VCN_DEC) | BIT(AMDGPU_RING_TYPE_VCN_ENC); + + case AMD_IP_BLOCK_TYPE_JPEG: + return BIT(AMDGPU_RING_TYPE_VCN_JPEG); + + case AMD_IP_BLOCK_TYPE_VPE: + return BIT(AMDGPU_RING_TYPE_VPE); + + default: + return 0; + } +} + +/** + * amdgpu_device_ip_soft_reset() - Perform a graceful soft reset on an IP block. + * + * @guilty_ring: The ring which is guilty of causing a reset. + * @guilty_fence: The fence which didn't signal. + * + * IP block soft reset is used when attempting to recover + * from a GPU hang in a situation where a more fine grained + * reset type isn't available or didn't work. This effectively + * resets all rings that belong to the same device IP block + * and re-initializes the device IP block. + * + * The reset is handled gracefully, meaning that we try to + * minimize collateral damage (ie. avoid rejecting non-guilty jobs) + * as well as back up and restore the contents of all rings + * so that the system can move on from the hang. + */ +int amdgpu_device_ip_soft_reset(struct amdgpu_ring *guilty_ring, + struct amdgpu_fence *guilty_fence) { + struct amdgpu_device *adev = guilty_ring->adev; struct amdgpu_ip_block *ip_block; + enum amd_ip_block_type ip_type; + u32 ring_type_mask; + int r; - ip_block = amdgpu_device_ip_get_ip_block(adev, block_type); - if (ip_block) - return ip_block->status.valid; + ip_type = amdgpu_ip_from_ring(guilty_ring->funcs->type); + ip_block = amdgpu_device_ip_get_ip_block(adev, ip_type); - return false; + if (!ip_block || !ip_block->version->funcs->soft_reset) { + dev_warn(adev->dev, "IP block soft reset not supported on %s\n", + ip_block->version->funcs->name); + return -EOPNOTSUPP; + } + + dev_err(adev->dev, "Starting %s IP block soft reset\n", + ip_block->version->funcs->name); + + ring_type_mask = amdgpu_ring_mask_from_ip(ip_type); + + amdgpu_device_lock_reset_domain(adev->reset_domain); + amdgpu_multi_ring_reset_helper_begin(ring_type_mask, guilty_ring, guilty_fence); + + r = ip_block->version->funcs->soft_reset(ip_block); + + r = amdgpu_multi_ring_reset_helper_end(ring_type_mask, guilty_ring, r); + amdgpu_device_unlock_reset_domain(adev->reset_domain); + + if (r) { + dev_err(adev->dev, "Failed %s IP block soft reset: %d\n", + ip_block->version->funcs->name, r); + return r; + } + + dev_err(adev->dev, "Successful %s IP block soft reset\n", + ip_block->version->funcs->name); + return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h index 1d0df6d93957..70fc4e5db51f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ip.h @@ -68,6 +68,7 @@ enum amd_hw_ip_block_type { ISP_HWIP, ATU_HWIP, AIGC_HWIP, + UMSCH_HWIP, MAX_HWIP }; @@ -84,6 +85,9 @@ enum amd_hw_ip_block_type { #define IP_VERSION_SUBREV(ver) ((ver) & 0xF) #define IP_VERSION_MAJ_MIN_REV(ver) ((ver) >> 8) +struct amdgpu_ring; +struct amdgpu_fence; + struct amdgpu_ip_map_info { /* Map of logical to actual dev instances/mask */ uint32_t dev_inst[MAX_HWIP][HWIP_MAX_INSTANCE]; @@ -146,9 +150,9 @@ void amdgpu_device_ip_get_clockgating_state(struct amdgpu_device *adev, u64 *flags); int amdgpu_device_ip_wait_for_idle(struct amdgpu_device *adev, enum amd_ip_block_type block_type); -bool amdgpu_device_ip_is_hw(struct amdgpu_device *adev, - enum amd_ip_block_type block_type); bool amdgpu_device_ip_is_valid(struct amdgpu_device *adev, enum amd_ip_block_type block_type); +int amdgpu_device_ip_soft_reset(struct amdgpu_ring *guilty_ring, + struct amdgpu_fence *guilty_fence); #endif /* __AMDGPU_IP_H__ */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c index 9ecc6387c1eb..cff73f1b5a72 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.c @@ -112,7 +112,7 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job) amdgpu_job_core_dump(adev, job); if (amdgpu_gpu_recovery && - amdgpu_ring_is_reset_type_supported(ring, AMDGPU_RESET_TYPE_SOFT_RESET) && + amdgpu_ring_is_reset_type_supported(ring, AMDGPU_RESET_TYPE_SOFT_RECOVERY) && amdgpu_ring_soft_recovery(ring, job->vmid, s_job->s_fence->parent)) { dev_err(adev->dev, "ring %s timeout, but soft recovered\n", s_job->sched->name); @@ -151,6 +151,17 @@ static enum drm_gpu_sched_stat amdgpu_job_timedout(struct drm_sched_job *s_job) dev_err(adev->dev, "Ring %s reset failed\n", ring->sched.name); } + /* Attempt an IP block soft reset, if supported. */ + if (amdgpu_gpu_recovery && + amdgpu_ring_is_reset_type_supported(ring, AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET)) { + r = amdgpu_device_ip_soft_reset(ring, job->hw_fence); + if (!r) { + atomic_inc(&ring->adev->gpu_reset_counter); + drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, info); + goto exit; + } + } + if (dma_fence_get_status(&s_job->s_fence->finished) == 0) dma_fence_set_error(&s_job->s_fence->finished, -ETIME); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c index 63ee6ba6a931..57935c321515 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c @@ -134,8 +134,8 @@ void amdgpu_jpeg_ring_begin_use(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; - atomic_inc(&adev->jpeg.total_submission_cnt); - cancel_delayed_work_sync(&adev->jpeg.idle_work); + if (!atomic_fetch_inc(&adev->jpeg.total_submission_cnt)) + cancel_delayed_work_sync(&adev->jpeg.idle_work); mutex_lock(&adev->jpeg.jpeg_pg_lock); amdgpu_device_ip_set_powergating_state(adev, AMD_IP_BLOCK_TYPE_JPEG, @@ -145,8 +145,9 @@ void amdgpu_jpeg_ring_begin_use(struct amdgpu_ring *ring) void amdgpu_jpeg_ring_end_use(struct amdgpu_ring *ring) { - atomic_dec(&ring->adev->jpeg.total_submission_cnt); - schedule_delayed_work(&ring->adev->jpeg.idle_work, JPEG_IDLE_TIMEOUT); + if (atomic_dec_and_test(&ring->adev->jpeg.total_submission_cnt)) + schedule_delayed_work(&ring->adev->jpeg.idle_work, + JPEG_IDLE_TIMEOUT); } int amdgpu_jpeg_dec_ring_test_ring(struct amdgpu_ring *ring) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h index 346ae0ab09d3..fe95d9188713 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.h @@ -149,6 +149,9 @@ struct amdgpu_jpeg { u32 *ip_dump; u32 reg_count; const struct amdgpu_hwip_reg_entry *reg_list; + + bool disable_uq; + bool disable_kq; }; int amdgpu_jpeg_sw_init(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 71272f40feef..215aa678d1d0 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1424,6 +1424,33 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) } /** + * amdgpu_proc_options_ioctl - set per-fd user options + * + * @dev: drm dev pointer + * @data: pointer to struct drm_amdgpu_proc_options + * @filp: drm file + * + * Sets options stored on the per-file amdgpu_fpriv. Currently the only + * supported option is %AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY which + * controls how KFD delivers SIGBUS for poison/RAS events to the calling + * process (immediate, suppressed, or delayed by N milliseconds). + */ +int amdgpu_proc_options_ioctl(struct drm_device *dev, void *data, + struct drm_file *filp) +{ + struct drm_amdgpu_proc_options *args = data; + + switch (args->op) { + case AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY: + return amdgpu_amdkfd_set_sigbus_delay(current, + args->kfd_sigbus_delay.value); + default: + DRM_DEBUG_KMS("Invalid user option op %u\n", args->op); + return -EINVAL; + } +} + +/** * amdgpu_driver_open_kms - drm callback for open * * @dev: drm dev pointer @@ -1504,8 +1531,7 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) if (r) goto error_vm; - mutex_init(&fpriv->bo_list_lock); - idr_init_base(&fpriv->bo_list_handles, 1); + xa_init_flags(&fpriv->bo_list_handles, XA_FLAGS_ALLOC1); r = amdgpu_userq_mgr_init(&fpriv->userq_mgr, file_priv, adev); if (r) @@ -1550,8 +1576,8 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev, struct amdgpu_fpriv *fpriv = file_priv->driver_priv; struct amdgpu_bo_list *list; struct amdgpu_bo *pd; + unsigned long handle; u32 pasid; - int handle; if (!fpriv) return; @@ -1587,11 +1613,9 @@ void amdgpu_driver_postclose_kms(struct drm_device *dev, amdgpu_pasid_free_delayed(pd->tbo.base.resv, pasid); amdgpu_bo_unref(&pd); - idr_for_each_entry(&fpriv->bo_list_handles, list, handle) + xa_for_each(&fpriv->bo_list_handles, handle, list) amdgpu_bo_list_put(list); - - idr_destroy(&fpriv->bo_list_handles); - mutex_destroy(&fpriv->bo_list_lock); + xa_destroy(&fpriv->bo_list_handles); kfree(fpriv); file_priv->driver_priv = NULL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c index cc6d1a4e4c3a..9a7f7d2b2767 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.c @@ -27,16 +27,6 @@ #include "umc/umc_6_7_0_offset.h" #include "umc/umc_6_7_0_sh_mask.h" -static bool amdgpu_mca_is_deferred_error(struct amdgpu_device *adev, - uint64_t mc_status) -{ - if (adev->umc.ras->check_ecc_err_status) - return adev->umc.ras->check_ecc_err_status(adev, - AMDGPU_MCA_ERROR_TYPE_DE, &mc_status); - - return false; -} - void amdgpu_mca_query_correctable_error_count(struct amdgpu_device *adev, uint64_t mc_status_addr, unsigned long *error_count) @@ -155,479 +145,3 @@ int amdgpu_mca_mpio_ras_sw_init(struct amdgpu_device *adev) return 0; } - -static void amdgpu_mca_bank_set_init(struct mca_bank_set *mca_set) -{ - if (!mca_set) - return; - - memset(mca_set, 0, sizeof(*mca_set)); - INIT_LIST_HEAD(&mca_set->list); -} - -static int amdgpu_mca_bank_set_add_entry(struct mca_bank_set *mca_set, struct mca_bank_entry *entry) -{ - struct mca_bank_node *node; - - if (!entry) - return -EINVAL; - - node = kvzalloc_obj(*node); - if (!node) - return -ENOMEM; - - memcpy(&node->entry, entry, sizeof(*entry)); - - INIT_LIST_HEAD(&node->node); - list_add_tail(&node->node, &mca_set->list); - - mca_set->nr_entries++; - - return 0; -} - -static int amdgpu_mca_bank_set_merge(struct mca_bank_set *mca_set, struct mca_bank_set *new) -{ - struct mca_bank_node *node; - - list_for_each_entry(node, &new->list, node) - amdgpu_mca_bank_set_add_entry(mca_set, &node->entry); - - return 0; -} - -static void amdgpu_mca_bank_set_remove_node(struct mca_bank_set *mca_set, struct mca_bank_node *node) -{ - if (!node) - return; - - list_del(&node->node); - kvfree(node); - - mca_set->nr_entries--; -} - -static void amdgpu_mca_bank_set_release(struct mca_bank_set *mca_set) -{ - struct mca_bank_node *node, *tmp; - - if (list_empty(&mca_set->list)) - return; - - list_for_each_entry_safe(node, tmp, &mca_set->list, node) - amdgpu_mca_bank_set_remove_node(mca_set, node); -} - -void amdgpu_mca_smu_init_funcs(struct amdgpu_device *adev, const struct amdgpu_mca_smu_funcs *mca_funcs) -{ - struct amdgpu_mca *mca = &adev->mca; - - mca->mca_funcs = mca_funcs; -} - -int amdgpu_mca_init(struct amdgpu_device *adev) -{ - struct amdgpu_mca *mca = &adev->mca; - struct mca_bank_cache *mca_cache; - int i; - - atomic_set(&mca->ue_update_flag, 0); - - for (i = 0; i < ARRAY_SIZE(mca->mca_caches); i++) { - mca_cache = &mca->mca_caches[i]; - mutex_init(&mca_cache->lock); - amdgpu_mca_bank_set_init(&mca_cache->mca_set); - } - - return 0; -} - -void amdgpu_mca_fini(struct amdgpu_device *adev) -{ - struct amdgpu_mca *mca = &adev->mca; - struct mca_bank_cache *mca_cache; - int i; - - atomic_set(&mca->ue_update_flag, 0); - - for (i = 0; i < ARRAY_SIZE(mca->mca_caches); i++) { - mca_cache = &mca->mca_caches[i]; - amdgpu_mca_bank_set_release(&mca_cache->mca_set); - mutex_destroy(&mca_cache->lock); - } -} - -int amdgpu_mca_reset(struct amdgpu_device *adev) -{ - amdgpu_mca_fini(adev); - - return amdgpu_mca_init(adev); -} - -int amdgpu_mca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable) -{ - const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs; - - if (mca_funcs && mca_funcs->mca_set_debug_mode) - return mca_funcs->mca_set_debug_mode(adev, enable); - - return -EOPNOTSUPP; -} - -static void amdgpu_mca_smu_mca_bank_dump(struct amdgpu_device *adev, int idx, struct mca_bank_entry *entry, - struct ras_query_context *qctx) -{ - u64 event_id = qctx ? qctx->evid.event_id : RAS_EVENT_INVALID_ID; - - RAS_EVENT_LOG(adev, event_id, HW_ERR "Accelerator Check Architecture events logged\n"); - RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].STATUS=0x%016llx\n", - idx, entry->regs[MCA_REG_IDX_STATUS]); - RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].ADDR=0x%016llx\n", - idx, entry->regs[MCA_REG_IDX_ADDR]); - RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].MISC0=0x%016llx\n", - idx, entry->regs[MCA_REG_IDX_MISC0]); - RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].IPID=0x%016llx\n", - idx, entry->regs[MCA_REG_IDX_IPID]); - RAS_EVENT_LOG(adev, event_id, HW_ERR "aca entry[%02d].SYND=0x%016llx\n", - idx, entry->regs[MCA_REG_IDX_SYND]); -} - -static int amdgpu_mca_smu_get_valid_mca_count(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, uint32_t *count) -{ - const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs; - - if (!count) - return -EINVAL; - - if (mca_funcs && mca_funcs->mca_get_valid_mca_count) - return mca_funcs->mca_get_valid_mca_count(adev, type, count); - - return -EOPNOTSUPP; -} - -static int amdgpu_mca_smu_get_mca_entry(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, - int idx, struct mca_bank_entry *entry) -{ - const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs; - int count; - - if (!mca_funcs || !mca_funcs->mca_get_mca_entry) - return -EOPNOTSUPP; - - switch (type) { - case AMDGPU_MCA_ERROR_TYPE_UE: - count = mca_funcs->max_ue_count; - break; - case AMDGPU_MCA_ERROR_TYPE_CE: - count = mca_funcs->max_ce_count; - break; - default: - return -EINVAL; - } - - if (idx >= count) - return -EINVAL; - - return mca_funcs->mca_get_mca_entry(adev, type, idx, entry); -} - -static bool amdgpu_mca_bank_should_update(struct amdgpu_device *adev, enum amdgpu_mca_error_type type) -{ - struct amdgpu_mca *mca = &adev->mca; - bool ret = true; - - /* - * Because the UE Valid MCA count will only be cleared after reset, - * in order to avoid repeated counting of the error count, - * the aca bank is only updated once during the gpu recovery stage. - */ - if (type == AMDGPU_MCA_ERROR_TYPE_UE) { - if (amdgpu_ras_intr_triggered()) - ret = atomic_cmpxchg(&mca->ue_update_flag, 0, 1) == 0; - else - atomic_set(&mca->ue_update_flag, 0); - } - - return ret; -} - -static bool amdgpu_mca_bank_should_dump(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, - struct mca_bank_entry *entry) -{ - bool ret; - - switch (type) { - case AMDGPU_MCA_ERROR_TYPE_CE: - ret = amdgpu_mca_is_deferred_error(adev, entry->regs[MCA_REG_IDX_STATUS]); - break; - case AMDGPU_MCA_ERROR_TYPE_UE: - default: - ret = true; - break; - } - - return ret; -} - -static int amdgpu_mca_smu_get_mca_set(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, struct mca_bank_set *mca_set, - struct ras_query_context *qctx) -{ - struct mca_bank_entry entry; - uint32_t count = 0, i; - int ret; - - if (!mca_set) - return -EINVAL; - - if (!amdgpu_mca_bank_should_update(adev, type)) - return 0; - - ret = amdgpu_mca_smu_get_valid_mca_count(adev, type, &count); - if (ret) - return ret; - - for (i = 0; i < count; i++) { - memset(&entry, 0, sizeof(entry)); - ret = amdgpu_mca_smu_get_mca_entry(adev, type, i, &entry); - if (ret) - return ret; - - amdgpu_mca_bank_set_add_entry(mca_set, &entry); - - if (amdgpu_mca_bank_should_dump(adev, type, &entry)) - amdgpu_mca_smu_mca_bank_dump(adev, i, &entry, qctx); - } - - return 0; -} - -static int amdgpu_mca_smu_parse_mca_error_count(struct amdgpu_device *adev, enum amdgpu_ras_block blk, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count) -{ - const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs; - - if (!count || !entry) - return -EINVAL; - - if (!mca_funcs || !mca_funcs->mca_parse_mca_error_count) - return -EOPNOTSUPP; - - return mca_funcs->mca_parse_mca_error_count(adev, blk, type, entry, count); -} - -static int amdgpu_mca_dispatch_mca_set(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type, - struct mca_bank_set *mca_set, struct ras_err_data *err_data) -{ - struct amdgpu_smuio_mcm_config_info mcm_info; - struct mca_bank_node *node, *tmp; - struct mca_bank_entry *entry; - uint32_t count; - int ret; - - if (!mca_set) - return -EINVAL; - - if (!mca_set->nr_entries) - return 0; - - list_for_each_entry_safe(node, tmp, &mca_set->list, node) { - entry = &node->entry; - - count = 0; - ret = amdgpu_mca_smu_parse_mca_error_count(adev, blk, type, entry, &count); - if (ret && ret != -EOPNOTSUPP) - return ret; - - if (!count) - continue; - - memset(&mcm_info, 0, sizeof(mcm_info)); - - mcm_info.socket_id = entry->info.socket_id; - mcm_info.die_id = entry->info.aid; - - if (type == AMDGPU_MCA_ERROR_TYPE_UE) { - amdgpu_ras_error_statistic_ue_count(err_data, - &mcm_info, (uint64_t)count); - } else { - if (amdgpu_mca_is_deferred_error(adev, entry->regs[MCA_REG_IDX_STATUS])) - amdgpu_ras_error_statistic_de_count(err_data, - &mcm_info, (uint64_t)count); - else - amdgpu_ras_error_statistic_ce_count(err_data, - &mcm_info, (uint64_t)count); - } - - amdgpu_mca_bank_set_remove_node(mca_set, node); - } - - return 0; -} - -static int amdgpu_mca_add_mca_set_to_cache(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, struct mca_bank_set *new) -{ - struct mca_bank_cache *mca_cache = &adev->mca.mca_caches[type]; - int ret; - - mutex_lock(&mca_cache->lock); - ret = amdgpu_mca_bank_set_merge(&mca_cache->mca_set, new); - mutex_unlock(&mca_cache->lock); - - return ret; -} - -int amdgpu_mca_smu_log_ras_error(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type, - struct ras_err_data *err_data, struct ras_query_context *qctx) -{ - struct mca_bank_set mca_set; - struct mca_bank_cache *mca_cache = &adev->mca.mca_caches[type]; - int ret; - - amdgpu_mca_bank_set_init(&mca_set); - - ret = amdgpu_mca_smu_get_mca_set(adev, type, &mca_set, qctx); - if (ret) - goto out_mca_release; - - ret = amdgpu_mca_dispatch_mca_set(adev, blk, type, &mca_set, err_data); - if (ret) - goto out_mca_release; - - /* add remain mca bank to mca cache */ - if (mca_set.nr_entries) { - ret = amdgpu_mca_add_mca_set_to_cache(adev, type, &mca_set); - if (ret) - goto out_mca_release; - } - - /* dispatch mca set again if mca cache has valid data */ - mutex_lock(&mca_cache->lock); - if (mca_cache->mca_set.nr_entries) - ret = amdgpu_mca_dispatch_mca_set(adev, blk, type, &mca_cache->mca_set, err_data); - mutex_unlock(&mca_cache->lock); - -out_mca_release: - amdgpu_mca_bank_set_release(&mca_set); - - return ret; -} - -#if defined(CONFIG_DEBUG_FS) -static int amdgpu_mca_smu_debug_mode_set(void *data, u64 val) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)data; - int ret; - - ret = amdgpu_ras_set_mca_debug_mode(adev, val ? true : false); - if (ret) - return ret; - - dev_info(adev->dev, "amdgpu set smu mca debug mode %s success\n", val ? "on" : "off"); - - return 0; -} - -static void mca_dump_entry(struct seq_file *m, struct mca_bank_entry *entry) -{ - int i, idx = entry->idx; - int reg_idx_array[] = { - MCA_REG_IDX_STATUS, - MCA_REG_IDX_ADDR, - MCA_REG_IDX_MISC0, - MCA_REG_IDX_IPID, - MCA_REG_IDX_SYND, - }; - - seq_printf(m, "mca entry[%d].type: %s\n", idx, entry->type == AMDGPU_MCA_ERROR_TYPE_UE ? "UE" : "CE"); - seq_printf(m, "mca entry[%d].ip: %d\n", idx, entry->ip); - seq_printf(m, "mca entry[%d].info: socketid:%d aid:%d hwid:0x%03x mcatype:0x%04x\n", - idx, entry->info.socket_id, entry->info.aid, entry->info.hwid, entry->info.mcatype); - - for (i = 0; i < ARRAY_SIZE(reg_idx_array); i++) - seq_printf(m, "mca entry[%d].regs[%d]: 0x%016llx\n", idx, reg_idx_array[i], entry->regs[reg_idx_array[i]]); -} - -static int mca_dump_show(struct seq_file *m, enum amdgpu_mca_error_type type) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)m->private; - struct mca_bank_node *node; - struct mca_bank_set mca_set; - struct ras_query_context qctx; - int ret; - - amdgpu_mca_bank_set_init(&mca_set); - - qctx.evid.event_id = RAS_EVENT_INVALID_ID; - ret = amdgpu_mca_smu_get_mca_set(adev, type, &mca_set, &qctx); - if (ret) - goto err_free_mca_set; - - seq_printf(m, "amdgpu smu %s valid mca count: %d\n", - type == AMDGPU_MCA_ERROR_TYPE_UE ? "UE" : "CE", mca_set.nr_entries); - - if (!mca_set.nr_entries) - goto err_free_mca_set; - - list_for_each_entry(node, &mca_set.list, node) - mca_dump_entry(m, &node->entry); - - /* add mca bank to mca bank cache */ - ret = amdgpu_mca_add_mca_set_to_cache(adev, type, &mca_set); - -err_free_mca_set: - amdgpu_mca_bank_set_release(&mca_set); - - return ret; -} - -static int mca_dump_ce_show(struct seq_file *m, void *unused) -{ - return mca_dump_show(m, AMDGPU_MCA_ERROR_TYPE_CE); -} - -static int mca_dump_ce_open(struct inode *inode, struct file *file) -{ - return single_open(file, mca_dump_ce_show, inode->i_private); -} - -static const struct file_operations mca_ce_dump_debug_fops = { - .owner = THIS_MODULE, - .open = mca_dump_ce_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int mca_dump_ue_show(struct seq_file *m, void *unused) -{ - return mca_dump_show(m, AMDGPU_MCA_ERROR_TYPE_UE); -} - -static int mca_dump_ue_open(struct inode *inode, struct file *file) -{ - return single_open(file, mca_dump_ue_show, inode->i_private); -} - -static const struct file_operations mca_ue_dump_debug_fops = { - .owner = THIS_MODULE, - .open = mca_dump_ue_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -DEFINE_DEBUGFS_ATTRIBUTE(mca_debug_mode_fops, NULL, amdgpu_mca_smu_debug_mode_set, "%llu\n"); -#endif - -void amdgpu_mca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root) -{ -#if defined(CONFIG_DEBUG_FS) - if (!root) - return; - - debugfs_create_file("mca_debug_mode", 0200, root, adev, &mca_debug_mode_fops); - debugfs_create_file("mca_ue_dump", 0400, root, adev, &mca_ue_dump_debug_fops); - debugfs_create_file("mca_ce_dump", 0400, root, adev, &mca_ce_dump_debug_fops); -#endif -} - diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h index e80323ff90c1..6d12f8a516d5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mca.h @@ -23,45 +23,6 @@ #include "amdgpu_ras.h" -#define MCA_MAX_REGS_COUNT (16) - -#define MCA_REG_FIELD(x, h, l) (((x) & GENMASK_ULL(h, l)) >> l) -#define MCA_REG__STATUS__VAL(x) MCA_REG_FIELD(x, 63, 63) -#define MCA_REG__STATUS__OVERFLOW(x) MCA_REG_FIELD(x, 62, 62) -#define MCA_REG__STATUS__UC(x) MCA_REG_FIELD(x, 61, 61) -#define MCA_REG__STATUS__EN(x) MCA_REG_FIELD(x, 60, 60) -#define MCA_REG__STATUS__MISCV(x) MCA_REG_FIELD(x, 59, 59) -#define MCA_REG__STATUS__ADDRV(x) MCA_REG_FIELD(x, 58, 58) -#define MCA_REG__STATUS__PCC(x) MCA_REG_FIELD(x, 57, 57) -#define MCA_REG__STATUS__ERRCOREIDVAL(x) MCA_REG_FIELD(x, 56, 56) -#define MCA_REG__STATUS__TCC(x) MCA_REG_FIELD(x, 55, 55) -#define MCA_REG__STATUS__SYNDV(x) MCA_REG_FIELD(x, 53, 53) -#define MCA_REG__STATUS__CECC(x) MCA_REG_FIELD(x, 46, 46) -#define MCA_REG__STATUS__UECC(x) MCA_REG_FIELD(x, 45, 45) -#define MCA_REG__STATUS__DEFERRED(x) MCA_REG_FIELD(x, 44, 44) -#define MCA_REG__STATUS__POISON(x) MCA_REG_FIELD(x, 43, 43) -#define MCA_REG__STATUS__SCRUB(x) MCA_REG_FIELD(x, 40, 40) -#define MCA_REG__STATUS__ERRCOREID(x) MCA_REG_FIELD(x, 37, 32) -#define MCA_REG__STATUS__ADDRLSB(x) MCA_REG_FIELD(x, 29, 24) -#define MCA_REG__STATUS__ERRORCODEEXT(x) MCA_REG_FIELD(x, 21, 16) -#define MCA_REG__STATUS__ERRORCODE(x) MCA_REG_FIELD(x, 15, 0) - -#define MCA_REG__MISC0__ERRCNT(x) MCA_REG_FIELD(x, 43, 32) - -#define MCA_REG__SYND__ERRORINFORMATION(x) MCA_REG_FIELD(x, 17, 0) - -enum amdgpu_mca_ip { - AMDGPU_MCA_IP_UNKNOW = -1, - AMDGPU_MCA_IP_PSP = 0, - AMDGPU_MCA_IP_SDMA, - AMDGPU_MCA_IP_GC, - AMDGPU_MCA_IP_SMU, - AMDGPU_MCA_IP_MP5, - AMDGPU_MCA_IP_UMC, - AMDGPU_MCA_IP_PCS_XGMI, - AMDGPU_MCA_IP_COUNT, -}; - enum amdgpu_mca_error_type { AMDGPU_MCA_ERROR_TYPE_UE = 0, AMDGPU_MCA_ERROR_TYPE_CE, @@ -77,77 +38,20 @@ struct amdgpu_mca_ras { struct amdgpu_mca_ras_block *ras; }; -struct mca_bank_set { - int nr_entries; - struct list_head list; -}; - -struct mca_bank_cache { - struct mca_bank_set mca_set; - struct mutex lock; -}; - struct amdgpu_mca { struct amdgpu_mca_ras mp0; struct amdgpu_mca_ras mp1; struct amdgpu_mca_ras mpio; - const struct amdgpu_mca_smu_funcs *mca_funcs; - struct mca_bank_cache mca_caches[AMDGPU_MCA_ERROR_TYPE_DE]; - atomic_t ue_update_flag; -}; - -enum mca_reg_idx { - MCA_REG_IDX_STATUS = 1, - MCA_REG_IDX_ADDR = 2, - MCA_REG_IDX_MISC0 = 3, - MCA_REG_IDX_IPID = 5, - MCA_REG_IDX_SYND = 6, - MCA_REG_IDX_COUNT = 16, -}; - -struct mca_bank_info { - int socket_id; - int aid; - int hwid; - int mcatype; -}; - -struct mca_bank_entry { - int idx; - enum amdgpu_mca_error_type type; - enum amdgpu_mca_ip ip; - struct mca_bank_info info; - uint64_t regs[MCA_MAX_REGS_COUNT]; -}; - -struct mca_bank_node { - struct mca_bank_entry entry; - struct list_head node; -}; - -struct amdgpu_mca_smu_funcs { - int max_ue_count; - int max_ce_count; - int (*mca_set_debug_mode)(struct amdgpu_device *adev, bool enable); - int (*mca_parse_mca_error_count)(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type, - struct mca_bank_entry *entry, uint32_t *count); - int (*mca_get_valid_mca_count)(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, - uint32_t *count); - int (*mca_get_mca_entry)(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, - int idx, struct mca_bank_entry *entry); }; void amdgpu_mca_query_correctable_error_count(struct amdgpu_device *adev, uint64_t mc_status_addr, unsigned long *error_count); - void amdgpu_mca_query_uncorrectable_error_count(struct amdgpu_device *adev, uint64_t mc_status_addr, unsigned long *error_count); - void amdgpu_mca_reset_error_count(struct amdgpu_device *adev, uint64_t mc_status_addr); - void amdgpu_mca_query_ras_error_count(struct amdgpu_device *adev, uint64_t mc_status_addr, void *ras_error_status); @@ -155,15 +59,4 @@ int amdgpu_mca_mp0_ras_sw_init(struct amdgpu_device *adev); int amdgpu_mca_mp1_ras_sw_init(struct amdgpu_device *adev); int amdgpu_mca_mpio_ras_sw_init(struct amdgpu_device *adev); -void amdgpu_mca_smu_init_funcs(struct amdgpu_device *adev, const struct amdgpu_mca_smu_funcs *mca_funcs); -int amdgpu_mca_init(struct amdgpu_device *adev); -void amdgpu_mca_fini(struct amdgpu_device *adev); -int amdgpu_mca_reset(struct amdgpu_device *adev); -int amdgpu_mca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable); -int amdgpu_mca_smu_get_mca_set_error_count(struct amdgpu_device *adev, enum amdgpu_ras_block blk, - enum amdgpu_mca_error_type type, uint32_t *total); -void amdgpu_mca_smu_debugfs_init(struct amdgpu_device *adev, struct dentry *root); -int amdgpu_mca_smu_log_ras_error(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type, - struct ras_err_data *err_data, struct ras_query_context *qctx); - #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c index e3972673fd64..6c0dde3786e3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.c @@ -217,7 +217,7 @@ int amdgpu_mes_init(struct amdgpu_device *adev) if (r) goto error_doorbell; - if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(12, 1, 0)) { + if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(11, 0, 0)) { /* When queue/pipe reset is done in MES instead of in the * driver, MES passes hung queues information to the driver in * hung_queue_hqd_info. Calculate required space to store this @@ -252,6 +252,10 @@ int amdgpu_mes_init(struct amdgpu_device *adev) } } + adev->gfx.mec.mes_hung_db_array = + kcalloc(amdgpu_mes_get_hung_queue_db_array_size(adev), + sizeof(u32), GFP_KERNEL); + return 0; error_doorbell: @@ -279,6 +283,8 @@ void amdgpu_mes_fini(struct amdgpu_device *adev) int i; int num_xcc = adev->gfx.xcc_mask ? NUM_XCC(adev->gfx.xcc_mask) : 1; + kfree(adev->gfx.mec.mes_hung_db_array); + amdgpu_bo_free_kernel(&adev->mes.event_log_gpu_obj, &adev->mes.event_log_gpu_addr, &adev->mes.event_log_cpu_addr); @@ -439,6 +445,59 @@ int amdgpu_mes_reset_legacy_queue(struct amdgpu_device *adev, return r; } +int amdgpu_mes_reset_queue_mmio(struct amdgpu_device *adev, + int queue_type, + unsigned int vmid, + unsigned int me, + unsigned int pipe, + unsigned int queue, + uint32_t xcc_id) +{ + struct mes_reset_queue_input queue_input; + int r; + + memset(&queue_input, 0, sizeof(queue_input)); + + queue_input.xcc_id = xcc_id; + queue_input.me_id = me; + queue_input.pipe_id = pipe; + queue_input.queue_id = queue; + queue_input.vmid = vmid; + queue_input.queue_type = queue_type; + queue_input.use_mmio = true; + + amdgpu_mes_lock(&adev->mes); + r = adev->mes.funcs->reset_hw_queue(&adev->mes, &queue_input); + amdgpu_mes_unlock(&adev->mes); + if (r) + dev_err(adev->dev, "failed to reset legacy queue\n"); + + return r; +} + +int amdgpu_mes_reset_user_queue(struct amdgpu_device *adev, + int queue_type, + unsigned int doorbell_index, + unsigned int xcc_id) +{ + struct mes_reset_queue_input queue_input; + int r; + + memset(&queue_input, 0, sizeof(queue_input)); + + queue_input.xcc_id = xcc_id; + queue_input.queue_type = queue_type; + queue_input.doorbell_offset = doorbell_index; + + amdgpu_mes_lock(&adev->mes); + r = adev->mes.funcs->reset_hw_queue(&adev->mes, &queue_input); + amdgpu_mes_unlock(&adev->mes); + if (r) + dev_err(adev->dev, "failed to reset user queue\n"); + + return r; +} + int amdgpu_mes_get_hung_queue_db_array_size(struct amdgpu_device *adev) { return adev->mes.hung_queue_db_array_size; @@ -805,8 +864,13 @@ bool amdgpu_mes_suspend_resume_all_supported(struct amdgpu_device *adev) bool amdgpu_mes_queue_reset_by_mes_supported(struct amdgpu_device *adev) { - return (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(12, 1, 0) && - (adev->mes.sched_version & AMDGPU_MES_VERSION_MASK) >= 0x73); + u32 ip_maj = IP_VERSION_MAJ(amdgpu_ip_version(adev, GC_HWIP, 0)); + u32 ip_min = IP_VERSION_MIN(amdgpu_ip_version(adev, GC_HWIP, 0)); + u32 mes_sched = adev->mes.sched_version & AMDGPU_MES_VERSION_MASK; + + return (ip_maj == 11 && mes_sched >= 0x8c) || + ((ip_maj == 12 && ip_min == 0) && mes_sched >= 0x8d) || + ((ip_maj == 12 && ip_min == 1) && mes_sched >= 0x73); } /* Fix me -- node_id is used to identify the correct MES instances in the future */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h index fdd06a17520a..f25cffad8efe 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mes.h @@ -168,6 +168,9 @@ struct amdgpu_mes { int master_xcc_ids[AMDGPU_MAX_MES_INST_PIPES]; struct amdgpu_bo *shared_cmd_buf_obj[AMDGPU_MAX_MES_INST_PIPES]; uint64_t shared_cmd_buf_gpu_addr[AMDGPU_MAX_MES_INST_PIPES]; + + bool compute_pipe_reset_enabled; + bool gfx_pipe_reset_enabled; }; struct amdgpu_mes_hung_queue_hqd_info { @@ -271,6 +274,7 @@ struct mes_remove_queue_input { uint32_t xcc_id; uint32_t doorbell_offset; uint64_t gang_context_addr; + uint32_t queue_type; bool remove_queue_after_reset; }; @@ -302,12 +306,14 @@ struct mes_suspend_gang_input { uint64_t gang_context_addr; uint64_t suspend_fence_addr; uint32_t suspend_fence_value; + uint32_t doorbell_offset; }; struct mes_resume_gang_input { uint32_t xcc_id; bool resume_all_gangs; uint64_t gang_context_addr; + uint32_t doorbell_offset; }; struct mes_reset_queue_input { @@ -459,6 +465,17 @@ int amdgpu_mes_reset_legacy_queue(struct amdgpu_device *adev, unsigned int vmid, bool use_mmio, uint32_t xcc_id); +int amdgpu_mes_reset_queue_mmio(struct amdgpu_device *adev, + int queue_type, + unsigned int vmid, + unsigned int me, + unsigned int pipe, + unsigned int queue, + uint32_t xcc_id); +int amdgpu_mes_reset_user_queue(struct amdgpu_device *adev, + int queue_type, + unsigned int doorbell_index, + unsigned int xcc_id); int amdgpu_mes_get_hung_queue_db_array_size(struct amdgpu_device *adev); int amdgpu_mes_detect_and_reset_hung_queues(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h index 6b8214650e5d..c5120ba51e24 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_mmhub.h @@ -21,29 +21,6 @@ #ifndef __AMDGPU_MMHUB_H__ #define __AMDGPU_MMHUB_H__ -enum amdgpu_mmhub_ras_memory_id { - AMDGPU_MMHUB_WGMI_PAGEMEM = 0, - AMDGPU_MMHUB_RGMI_PAGEMEM = 1, - AMDGPU_MMHUB_WDRAM_PAGEMEM = 2, - AMDGPU_MMHUB_RDRAM_PAGEMEM = 3, - AMDGPU_MMHUB_WIO_CMDMEM = 4, - AMDGPU_MMHUB_RIO_CMDMEM = 5, - AMDGPU_MMHUB_WGMI_CMDMEM = 6, - AMDGPU_MMHUB_RGMI_CMDMEM = 7, - AMDGPU_MMHUB_WDRAM_CMDMEM = 8, - AMDGPU_MMHUB_RDRAM_CMDMEM = 9, - AMDGPU_MMHUB_MAM_DMEM0 = 10, - AMDGPU_MMHUB_MAM_DMEM1 = 11, - AMDGPU_MMHUB_MAM_DMEM2 = 12, - AMDGPU_MMHUB_MAM_DMEM3 = 13, - AMDGPU_MMHUB_WRET_TAGMEM = 19, - AMDGPU_MMHUB_RRET_TAGMEM = 20, - AMDGPU_MMHUB_WIO_DATAMEM = 21, - AMDGPU_MMHUB_WGMI_DATAMEM = 22, - AMDGPU_MMHUB_WDRAM_DATAMEM = 23, - AMDGPU_MMHUB_MEMORY_BLOCK_LAST, -}; - struct amdgpu_mmhub_ras { struct amdgpu_ras_block_object ras_block; }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h index 4d68732d6223..ff11a0903499 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h @@ -312,46 +312,6 @@ uint32_t amdgpu_bo_mem_stats_placement(struct amdgpu_bo *bo); uint32_t amdgpu_bo_get_preferred_domain(struct amdgpu_device *adev, uint32_t domain); -/* - * sub allocation - */ -static inline struct amdgpu_sa_manager * -to_amdgpu_sa_manager(struct drm_suballoc_manager *manager) -{ - return container_of(manager, struct amdgpu_sa_manager, base); -} - -static inline uint64_t amdgpu_sa_bo_gpu_addr(struct drm_suballoc *sa_bo) -{ - return to_amdgpu_sa_manager(sa_bo->manager)->gpu_addr + - drm_suballoc_soffset(sa_bo); -} - -static inline void *amdgpu_sa_bo_cpu_addr(struct drm_suballoc *sa_bo) -{ - return to_amdgpu_sa_manager(sa_bo->manager)->cpu_ptr + - drm_suballoc_soffset(sa_bo); -} - -int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, - struct amdgpu_sa_manager *sa_manager, - unsigned size, u32 align, u32 domain); -void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev, - struct amdgpu_sa_manager *sa_manager); -int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev, - struct amdgpu_sa_manager *sa_manager); -int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager, - struct drm_suballoc **sa_bo, - unsigned int size); -void amdgpu_sa_bo_free(struct drm_suballoc **sa_bo, - struct dma_fence *fence); -#if defined(CONFIG_DEBUG_FS) -void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager, - struct seq_file *m); -u64 amdgpu_bo_print_info(int id, struct amdgpu_bo *bo, struct seq_file *m); -#endif -void amdgpu_debugfs_sa_init(struct amdgpu_device *adev); - bool amdgpu_bo_support_uswc(u64 bo_flags); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c index b1dc33301d83..e8592970aaab 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_preempt_mgr.c @@ -47,6 +47,17 @@ static ssize_t mem_info_preempt_used_show(struct device *dev, static DEVICE_ATTR_RO(mem_info_preempt_used); /** + * amdgpu_preempt_mgr_sysfs_fini - remove PREEMPT manager sysfs attributes + * + * @adev: amdgpu_device pointer + */ +void amdgpu_preempt_mgr_sysfs_fini(struct amdgpu_device *adev) +{ + if (adev->dev->kobj.sd) + device_remove_file(adev->dev, &dev_attr_mem_info_preempt_used); +} + +/** * amdgpu_preempt_mgr_new - allocate a new node * * @man: TTM memory type manager @@ -137,9 +148,6 @@ void amdgpu_preempt_mgr_fini(struct amdgpu_device *adev) if (ret) return; - if (adev->dev->kobj.sd) - device_remove_file(adev->dev, &dev_attr_mem_info_preempt_used); - ttm_resource_manager_cleanup(man); ttm_set_driver_manager(&adev->mman.bdev, AMDGPU_PL_PREEMPT, NULL); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c index 0d3c18f04ac3..8ae72c862d11 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp_ta.c @@ -166,7 +166,8 @@ static ssize_t ta_if_load_debugfs_write(struct file *fp, const char *buf, size_t if (ret) return -EFAULT; - if (ta_bin_len > PSP_1_MEG) + if (ta_bin_len < sizeof(struct common_firmware_header) || + ta_bin_len > PSP_1_MEG) return -EINVAL; copy_pos += sizeof(uint32_t); @@ -321,6 +322,8 @@ static ssize_t ta_if_invoke_debugfs_write(struct file *fp, const char *buf, size ret = copy_from_user((void *)&shared_buf_len, &buf[copy_pos], sizeof(uint32_t)); if (ret) return -EFAULT; + if (!shared_buf_len || shared_buf_len > PSP_1_MEG) + return -EINVAL; copy_pos += sizeof(uint32_t); shared_buf = memdup_user(&buf[copy_pos], shared_buf_len); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 764cd4950408..148bb4cb0a2d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -128,12 +128,6 @@ const char *get_ras_block_str(struct ras_common_if *ras_block) /* typical ECC bad page rate is 1 bad page per 100MB VRAM */ #define RAS_BAD_PAGE_COVER (100 * 1024 * 1024ULL) -#define MAX_UMC_POISON_POLLING_TIME_ASYNC 10 - -#define AMDGPU_RAS_RETIRE_PAGE_INTERVAL 100 //ms - -#define MAX_FLUSH_RETIRE_DWORK_TIMES 100 - #define BYPASS_ALLOCATED_ADDRESS 0x0 #define BYPASS_INITIALIZATION_ADDRESS 0x1 @@ -249,16 +243,12 @@ static int amdgpu_check_address_validity(struct amdgpu_device *adev, (address >= RAS_UMC_INJECT_ADDR_LIMIT)) return -EFAULT; - if (amdgpu_uniras_enabled(adev)) { - if (amdgpu_sriov_vf(adev)) - count = amdgpu_virt_ras_convert_retired_address(adev, address, - page_pfns, ARRAY_SIZE(page_pfns)); - else - count = amdgpu_ras_mgr_lookup_bad_pages_in_a_row(adev, address, - page_pfns, ARRAY_SIZE(page_pfns)); - } else - count = amdgpu_umc_lookup_bad_pages_in_a_row(adev, - address, page_pfns, ARRAY_SIZE(page_pfns)); + if (amdgpu_sriov_vf(adev)) + count = amdgpu_virt_ras_convert_retired_address(adev, address, + page_pfns, ARRAY_SIZE(page_pfns)); + else + count = amdgpu_ras_mgr_lookup_bad_pages_in_a_row(adev, address, + page_pfns, ARRAY_SIZE(page_pfns)); if (count <= 0) return -EPERM; @@ -1381,76 +1371,6 @@ static void amdgpu_ras_mgr_virt_error_data_statistics_update(struct ras_manager obj->err_data.de_count = err_data->de_count; } -static struct ras_manager *get_ras_manager(struct amdgpu_device *adev, enum amdgpu_ras_block blk) -{ - struct ras_common_if head; - - memset(&head, 0, sizeof(head)); - head.block = blk; - - return amdgpu_ras_find_obj(adev, &head); -} - -int amdgpu_ras_bind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk, - const struct aca_info *aca_info, void *data) -{ - struct ras_manager *obj; - - /* in resume phase, no need to create aca fs node */ - if (adev->in_suspend || amdgpu_reset_in_recovery(adev)) - return 0; - - obj = get_ras_manager(adev, blk); - if (!obj) - return -EINVAL; - - return amdgpu_aca_add_handle(adev, &obj->aca_handle, ras_block_str(blk), aca_info, data); -} - -int amdgpu_ras_unbind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk) -{ - struct ras_manager *obj; - - obj = get_ras_manager(adev, blk); - if (!obj) - return -EINVAL; - - amdgpu_aca_remove_handle(&obj->aca_handle); - - return 0; -} - -static int amdgpu_aca_log_ras_error_data(struct amdgpu_device *adev, enum amdgpu_ras_block blk, - enum aca_error_type type, struct ras_err_data *err_data, - struct ras_query_context *qctx) -{ - struct ras_manager *obj; - - obj = get_ras_manager(adev, blk); - if (!obj) - return -EINVAL; - - return amdgpu_aca_get_error_data(adev, &obj->aca_handle, type, err_data, qctx); -} - -ssize_t amdgpu_ras_aca_sysfs_read(struct device *dev, struct device_attribute *attr, - struct aca_handle *handle, char *buf, void *data) -{ - struct ras_manager *obj = container_of(handle, struct ras_manager, aca_handle); - struct ras_query_if info = { - .head = obj->head, - }; - - if (!amdgpu_ras_get_error_query_ready(obj->adev)) - return sysfs_emit(buf, "Query currently inaccessible\n"); - - if (amdgpu_ras_query_error_status(obj->adev, &info)) - return -EINVAL; - - return sysfs_emit(buf, "%s: %lu\n%s: %lu\n%s: %lu\n", "ue", info.ue_count, - "ce", info.ce_count, "de", info.de_count); -} - static int amdgpu_ras_query_error_status_helper(struct amdgpu_device *adev, struct ras_query_if *info, struct ras_err_data *err_data, @@ -1459,7 +1379,6 @@ static int amdgpu_ras_query_error_status_helper(struct amdgpu_device *adev, { enum amdgpu_ras_block blk = info ? info->head.block : AMDGPU_RAS_BLOCK_COUNT; struct amdgpu_ras_block_object *block_obj = NULL; - int ret; if (blk == AMDGPU_RAS_BLOCK_COUNT) return -EINVAL; @@ -1469,7 +1388,7 @@ static int amdgpu_ras_query_error_status_helper(struct amdgpu_device *adev, if (error_query_mode == AMDGPU_RAS_VIRT_ERROR_COUNT_QUERY) { return amdgpu_virt_req_ras_err_count(adev, blk, err_data); - } else if (error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY) { + } else { if (info->head.block == AMDGPU_RAS_BLOCK__UMC) { amdgpu_ras_get_ecc_info(adev, err_data); } else { @@ -1490,24 +1409,6 @@ static int amdgpu_ras_query_error_status_helper(struct amdgpu_device *adev, block_obj->hw_ops->query_ras_error_status(adev); } } - } else { - if (amdgpu_aca_is_enabled(adev)) { - ret = amdgpu_aca_log_ras_error_data(adev, blk, ACA_ERROR_TYPE_UE, err_data, qctx); - if (ret) - return ret; - - ret = amdgpu_aca_log_ras_error_data(adev, blk, ACA_ERROR_TYPE_CE, err_data, qctx); - if (ret) - return ret; - - ret = amdgpu_aca_log_ras_error_data(adev, blk, ACA_ERROR_TYPE_DEFERRED, err_data, qctx); - if (ret) - return ret; - } else { - /* FIXME: add code to check return value later */ - amdgpu_mca_smu_log_ras_error(adev, blk, AMDGPU_MCA_ERROR_TYPE_UE, err_data, qctx); - amdgpu_mca_smu_log_ras_error(adev, blk, AMDGPU_MCA_ERROR_TYPE_CE, err_data, qctx); - } } return 0; @@ -1624,8 +1525,6 @@ int amdgpu_ras_reset_error_count(struct amdgpu_device *adev, enum amdgpu_ras_block block) { struct amdgpu_ras_block_object *block_obj = amdgpu_ras_get_ras_block(adev, block, 0); - const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs; - const struct aca_smu_funcs *smu_funcs = adev->aca.smu_funcs; if (!block_obj || !block_obj->hw_ops) { dev_dbg_once(adev->dev, "%s doesn't config RAS function\n", @@ -1633,17 +1532,14 @@ int amdgpu_ras_reset_error_count(struct amdgpu_device *adev, return -EOPNOTSUPP; } - if (!amdgpu_ras_is_supported(adev, block) || - !amdgpu_ras_get_aca_debug_mode(adev)) + if (!amdgpu_ras_is_supported(adev, block)) return -EOPNOTSUPP; if (amdgpu_sriov_vf(adev)) return -EOPNOTSUPP; /* skip ras error reset in gpu reset */ - if ((amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) && - ((smu_funcs && smu_funcs->set_debug_mode) || - (mca_funcs && mca_funcs->mca_set_debug_mode))) + if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) return -EOPNOTSUPP; if (block_obj->hw_ops->reset_ras_error_count) @@ -2090,9 +1986,6 @@ int amdgpu_ras_sysfs_create(struct amdgpu_device *adev, { struct ras_manager *obj = amdgpu_ras_find_obj(adev, head); - if (amdgpu_aca_is_enabled(adev)) - return 0; - if (!obj || obj->attr_inuse) return -EINVAL; @@ -2130,9 +2023,6 @@ int amdgpu_ras_sysfs_remove(struct amdgpu_device *adev, { struct ras_manager *obj = amdgpu_ras_find_obj(adev, head); - if (amdgpu_aca_is_enabled(adev)) - return 0; - if (!obj || !obj->attr_inuse) return -EINVAL; @@ -2245,25 +2135,6 @@ static void amdgpu_ras_debugfs_create(struct amdgpu_device *adev, obj, &amdgpu_ras_debugfs_ops); } -static bool amdgpu_ras_aca_is_supported(struct amdgpu_device *adev) -{ - bool ret; - - switch (amdgpu_ip_version(adev, MP0_HWIP, 0)) { - case IP_VERSION(13, 0, 6): - case IP_VERSION(13, 0, 12): - case IP_VERSION(13, 0, 14): - case IP_VERSION(13, 0, 15): - ret = true; - break; - default: - ret = false; - break; - } - - return ret; -} - void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); @@ -2290,13 +2161,6 @@ void amdgpu_ras_debugfs_create_all(struct amdgpu_device *adev) amdgpu_ras_debugfs_create(adev, &fs_info, dir); } } - - if (amdgpu_ras_aca_is_supported(adev)) { - if (amdgpu_aca_is_enabled(adev)) - amdgpu_aca_smu_debugfs_init(adev, dir); - else - amdgpu_mca_smu_debugfs_init(adev, dir); - } } /* debugfs end */ @@ -2489,14 +2353,6 @@ static void amdgpu_ras_interrupt_poison_creation_handler(struct ras_manager *obj event_id = amdgpu_ras_acquire_event_id(adev, type); RAS_EVENT_LOG(adev, event_id, "Poison is created\n"); - if (amdgpu_ip_version(obj->adev, UMC_HWIP, 0) >= IP_VERSION(12, 0, 0)) { - struct amdgpu_ras *con = amdgpu_ras_get_context(obj->adev); - - atomic_inc(&con->page_retirement_req_cnt); - atomic_inc(&con->poison_creation_count); - - wake_up(&con->page_retirement_wq); - } } static void amdgpu_ras_interrupt_umc_handler(struct ras_manager *obj, @@ -3026,77 +2882,6 @@ static int amdgpu_ras_realloc_eh_data_space(struct amdgpu_device *adev, return 0; } -static int amdgpu_ras_mca2pa_by_idx(struct amdgpu_device *adev, - struct eeprom_table_record *bps, - struct ras_err_data *err_data) -{ - struct ta_ras_query_address_input addr_in; - uint32_t socket = 0; - int ret = 0; - - if (adev->smuio.funcs && adev->smuio.funcs->get_socket_id) - socket = adev->smuio.funcs->get_socket_id(adev); - - /* reinit err_data */ - err_data->err_addr_cnt = 0; - err_data->err_addr_len = adev->umc.retire_unit; - - memset(&addr_in, 0, sizeof(addr_in)); - addr_in.ma.err_addr = bps->address; - addr_in.ma.socket_id = socket; - addr_in.ma.ch_inst = bps->mem_channel; - if (!amdgpu_ras_smu_eeprom_supported(adev)) { - /* tell RAS TA the node instance is not used */ - addr_in.ma.node_inst = TA_RAS_INV_NODE; - } else { - addr_in.ma.umc_inst = bps->mcumc_id; - addr_in.ma.node_inst = bps->cu; - } - - if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr) - ret = adev->umc.ras->convert_ras_err_addr(adev, err_data, - &addr_in, NULL, false); - - return ret; -} - -static int amdgpu_ras_mca2pa(struct amdgpu_device *adev, - struct eeprom_table_record *bps, - struct ras_err_data *err_data) -{ - struct ta_ras_query_address_input addr_in; - uint32_t die_id, socket = 0; - - if (adev->smuio.funcs && adev->smuio.funcs->get_socket_id) - socket = adev->smuio.funcs->get_socket_id(adev); - - /* although die id is gotten from PA in nps1 mode, the id is - * fitable for any nps mode - */ - if (adev->umc.ras && adev->umc.ras->get_die_id_from_pa) - die_id = adev->umc.ras->get_die_id_from_pa(adev, bps->address, - bps->retired_page << AMDGPU_GPU_PAGE_SHIFT); - else - return -EINVAL; - - /* reinit err_data */ - err_data->err_addr_cnt = 0; - err_data->err_addr_len = adev->umc.retire_unit; - - memset(&addr_in, 0, sizeof(addr_in)); - addr_in.ma.err_addr = bps->address; - addr_in.ma.ch_inst = bps->mem_channel; - addr_in.ma.umc_inst = bps->mcumc_id; - addr_in.ma.node_inst = die_id; - addr_in.ma.socket_id = socket; - - if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr) - return adev->umc.ras->convert_ras_err_addr(adev, err_data, - &addr_in, NULL, false); - else - return -EINVAL; -} - static bool __check_record_in_range(struct amdgpu_device *adev, struct eeprom_table_record *bps, int count) { @@ -3157,117 +2942,13 @@ static int __amdgpu_ras_convert_rec_array_from_rom(struct amdgpu_device *adev, struct eeprom_table_record *bps, struct ras_err_data *err_data, enum amdgpu_memory_partition nps) { - int i = 0; - uint64_t chan_idx_v2; - enum amdgpu_memory_partition save_nps; - - save_nps = (bps[0].retired_page >> UMC_NPS_SHIFT) & UMC_NPS_MASK; - chan_idx_v2 = bps[0].retired_page & UMC_CHANNEL_IDX_V2; - /*old asics just have pa in eeprom*/ - if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) < 12) { - memcpy(err_data->err_addr, bps, - sizeof(struct eeprom_table_record) * adev->umc.retire_unit); - goto out; - } - - for (i = 0; i < adev->umc.retire_unit; i++) - bps[i].retired_page &= ~(UMC_NPS_MASK << UMC_NPS_SHIFT); + memcpy(err_data->err_addr, bps, + sizeof(struct eeprom_table_record) * adev->umc.retire_unit); - if (save_nps || chan_idx_v2) { - if (save_nps == nps) { - if (amdgpu_umc_pages_in_a_row(adev, err_data, - bps[0].retired_page << AMDGPU_GPU_PAGE_SHIFT)) - return -EINVAL; - for (i = 0; i < adev->umc.retire_unit; i++) { - err_data->err_addr[i].address = bps[0].address; - err_data->err_addr[i].mem_channel = bps[0].mem_channel; - err_data->err_addr[i].bank = bps[0].bank; - err_data->err_addr[i].err_type = bps[0].err_type; - err_data->err_addr[i].mcumc_id = bps[0].mcumc_id; - } - } else { - if (amdgpu_ras_mca2pa_by_idx(adev, &bps[0], err_data)) - return -EINVAL; - } - } else { - if (bps[0].address == 0) { - /* for specific old eeprom data, mca address is not stored, - * calc it from pa - */ - if (amdgpu_umc_pa2mca(adev, bps[0].retired_page << AMDGPU_GPU_PAGE_SHIFT, - &(bps[0].address), AMDGPU_NPS1_PARTITION_MODE)) - return -EINVAL; - } - - if (amdgpu_ras_mca2pa(adev, &bps[0], err_data)) { - if (nps == AMDGPU_NPS1_PARTITION_MODE) - memcpy(err_data->err_addr, bps, - sizeof(struct eeprom_table_record) * adev->umc.retire_unit); - else - return -EOPNOTSUPP; - } - } - -out: return __amdgpu_ras_restore_bad_pages(adev, err_data->err_addr, adev->umc.retire_unit); } -static int __amdgpu_ras_convert_rec_from_rom(struct amdgpu_device *adev, - struct eeprom_table_record *bps, struct ras_err_data *err_data, - enum amdgpu_memory_partition nps) -{ - int i = 0; - uint64_t chan_idx_v2; - enum amdgpu_memory_partition save_nps; - - if (!amdgpu_ras_smu_eeprom_supported(adev)) { - save_nps = (bps->retired_page >> UMC_NPS_SHIFT) & UMC_NPS_MASK; - chan_idx_v2 = bps->retired_page & UMC_CHANNEL_IDX_V2; - bps->retired_page &= ~(UMC_NPS_MASK << UMC_NPS_SHIFT); - } else { - /* if pmfw manages eeprom, save_nps is not stored on eeprom, - * we should always convert mca address into physical address, - * make save_nps different from nps - */ - save_nps = nps + 1; - } - - if (save_nps == nps) { - if (amdgpu_umc_pages_in_a_row(adev, err_data, - bps->retired_page << AMDGPU_GPU_PAGE_SHIFT)) - return -EINVAL; - for (i = 0; i < adev->umc.retire_unit; i++) { - err_data->err_addr[i].address = bps->address; - err_data->err_addr[i].mem_channel = bps->mem_channel; - err_data->err_addr[i].bank = bps->bank; - err_data->err_addr[i].err_type = bps->err_type; - err_data->err_addr[i].mcumc_id = bps->mcumc_id; - } - } else { - if (save_nps || chan_idx_v2) { - if (amdgpu_ras_mca2pa_by_idx(adev, bps, err_data)) - return -EINVAL; - } else { - /* for specific old eeprom data, mca address is not stored, - * calc it from pa - */ - if (bps->address == 0) - if (amdgpu_umc_pa2mca(adev, - bps->retired_page << AMDGPU_GPU_PAGE_SHIFT, - &(bps->address), - AMDGPU_NPS1_PARTITION_MODE)) - return -EINVAL; - - if (amdgpu_ras_mca2pa(adev, bps, err_data)) - return -EOPNOTSUPP; - } - } - - return __amdgpu_ras_restore_bad_pages(adev, err_data->err_addr, - adev->umc.retire_unit); -} - /* it deal with vram only. */ int amdgpu_ras_add_bad_pages(struct amdgpu_device *adev, struct eeprom_table_record *bps, int pages, bool from_rom) @@ -3300,8 +2981,7 @@ int amdgpu_ras_add_bad_pages(struct amdgpu_device *adev, if (from_rom) { /* there is no pa recs in V3, so skip pa recs processing */ - if ((control->tbl_hdr.version < RAS_TABLE_VER_V3) && - !amdgpu_ras_smu_eeprom_supported(adev)) { + if (control->tbl_hdr.version < RAS_TABLE_VER_V3) { for (i = 0; i < pages; i++) { if (control->ras_num_recs - i >= adev->umc.retire_unit) { if ((bps[i].address == bps[i + 1].address) && @@ -3318,10 +2998,8 @@ int amdgpu_ras_add_bad_pages(struct amdgpu_device *adev, } } } - for (; i < pages; i++) { - ret = __amdgpu_ras_convert_rec_from_rom(adev, - &bps[i], &err_data, nps); - } + for (; i < pages; i++) + bps[i].retired_page &= ~(UMC_NPS_MASK << UMC_NPS_SHIFT); con->eh_data->count_saved = con->eh_data->count; } else { @@ -3346,7 +3024,7 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev, struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct ras_err_handler_data *data; struct amdgpu_ras_eeprom_control *control; - int save_count, unit_num, i; + int save_count, unit_num; if (!con || !con->eh_data) { if (new_cnt) @@ -3367,12 +3045,7 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev, mutex_lock(&con->recovery_lock); control = &con->eeprom_control; data = con->eh_data; - if (amdgpu_ras_smu_eeprom_supported(adev)) - unit_num = control->ras_num_recs - - control->ras_num_recs_old; - else - unit_num = data->count / adev->umc.retire_unit - - control->ras_num_recs; + unit_num = data->count / adev->umc.retire_unit - control->ras_num_recs; save_count = con->bad_page_num - control->ras_num_bad_pages; mutex_unlock(&con->recovery_lock); @@ -3383,21 +3056,10 @@ int amdgpu_ras_save_bad_pages(struct amdgpu_device *adev, /* only new entries are saved */ if (unit_num && save_count) { /*old asics only save pa to eeprom like before*/ - if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) < 12) { - if (amdgpu_ras_eeprom_append(control, - &data->bps[data->count_saved], unit_num)) { - dev_err(adev->dev, "Failed to save EEPROM table data!"); - return -EIO; - } - } else { - for (i = 0; i < unit_num; i++) { - if (amdgpu_ras_eeprom_append(control, - &data->bps[data->count_saved + - i * adev->umc.retire_unit], 1)) { - dev_err(adev->dev, "Failed to save EEPROM table data!"); - return -EIO; - } - } + if (amdgpu_ras_eeprom_append(control, + &data->bps[data->count_saved], unit_num)) { + dev_err(adev->dev, "Failed to save EEPROM table data!"); + return -EIO; } dev_info(adev->dev, "Saved %d pages to EEPROM table.\n", save_count); @@ -3416,7 +3078,7 @@ static int amdgpu_ras_load_bad_pages(struct amdgpu_device *adev) struct amdgpu_ras_eeprom_control *control = &adev->psp.ras_context.ras->eeprom_control; struct eeprom_table_record *bps; - int ret, i = 0; + int ret; /* no bad page record, skip eeprom access */ if (control->ras_num_recs == 0 || amdgpu_bad_page_threshold == 0) @@ -3430,33 +3092,6 @@ static int amdgpu_ras_load_bad_pages(struct amdgpu_device *adev) if (ret) { dev_err(adev->dev, "Failed to load EEPROM table records!"); } else { - if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr) { - /*In V3, there is no pa recs, and some cases(when address==0) may be parsed - as pa recs, so add verion check to avoid it. - */ - if ((control->tbl_hdr.version < RAS_TABLE_VER_V3) && - !amdgpu_ras_smu_eeprom_supported(adev)) { - for (i = 0; i < control->ras_num_recs; i++) { - if ((control->ras_num_recs - i) >= adev->umc.retire_unit) { - if ((bps[i].address == bps[i + 1].address) && - (bps[i].mem_channel == bps[i + 1].mem_channel)) { - control->ras_num_pa_recs += adev->umc.retire_unit; - i += (adev->umc.retire_unit - 1); - } else { - control->ras_num_mca_recs += - (control->ras_num_recs - i); - break; - } - } else { - control->ras_num_mca_recs += (control->ras_num_recs - i); - break; - } - } - } else { - control->ras_num_mca_recs = control->ras_num_recs; - } - } - ret = amdgpu_ras_add_bad_pages(adev, bps, control->ras_num_recs, true); if (ret) goto out; @@ -3550,293 +3185,6 @@ static void amdgpu_ras_validate_threshold(struct amdgpu_device *adev, } } -int amdgpu_ras_put_poison_req(struct amdgpu_device *adev, - enum amdgpu_ras_block block, uint16_t pasid, - pasid_notify pasid_fn, void *data, uint32_t reset) -{ - int ret = 0; - struct ras_poison_msg poison_msg; - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - - memset(&poison_msg, 0, sizeof(poison_msg)); - poison_msg.block = block; - poison_msg.pasid = pasid; - poison_msg.reset = reset; - poison_msg.pasid_fn = pasid_fn; - poison_msg.data = data; - - ret = kfifo_put(&con->poison_fifo, poison_msg); - if (!ret) { - dev_err(adev->dev, "Poison message fifo is full!\n"); - return -ENOSPC; - } - - return 0; -} - -static int amdgpu_ras_get_poison_req(struct amdgpu_device *adev, - struct ras_poison_msg *poison_msg) -{ - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - - return kfifo_get(&con->poison_fifo, poison_msg); -} - -static void amdgpu_ras_ecc_log_init(struct ras_ecc_log_info *ecc_log) -{ - mutex_init(&ecc_log->lock); - - INIT_RADIX_TREE(&ecc_log->de_page_tree, GFP_KERNEL); - ecc_log->de_queried_count = 0; - ecc_log->consumption_q_count = 0; -} - -static void amdgpu_ras_ecc_log_fini(struct ras_ecc_log_info *ecc_log) -{ - struct radix_tree_iter iter; - void __rcu **slot; - struct ras_ecc_err *ecc_err; - - mutex_lock(&ecc_log->lock); - radix_tree_for_each_slot(slot, &ecc_log->de_page_tree, &iter, 0) { - ecc_err = radix_tree_deref_slot(slot); - kfree(ecc_err->err_pages.pfn); - kfree(ecc_err); - radix_tree_iter_delete(&ecc_log->de_page_tree, &iter, slot); - } - mutex_unlock(&ecc_log->lock); - - mutex_destroy(&ecc_log->lock); - ecc_log->de_queried_count = 0; - ecc_log->consumption_q_count = 0; -} - -static bool amdgpu_ras_schedule_retirement_dwork(struct amdgpu_ras *con, - uint32_t delayed_ms) -{ - int ret; - - mutex_lock(&con->umc_ecc_log.lock); - ret = radix_tree_tagged(&con->umc_ecc_log.de_page_tree, - UMC_ECC_NEW_DETECTED_TAG); - mutex_unlock(&con->umc_ecc_log.lock); - - if (ret) - schedule_delayed_work(&con->page_retirement_dwork, - msecs_to_jiffies(delayed_ms)); - - return ret ? true : false; -} - -static void amdgpu_ras_do_page_retirement(struct work_struct *work) -{ - struct amdgpu_ras *con = container_of(work, struct amdgpu_ras, - page_retirement_dwork.work); - struct amdgpu_device *adev = con->adev; - struct ras_err_data err_data; - - /* If gpu reset is ongoing, delay retiring the bad pages */ - if (amdgpu_in_reset(adev) || amdgpu_ras_in_recovery(adev)) { - amdgpu_ras_schedule_retirement_dwork(con, - AMDGPU_RAS_RETIRE_PAGE_INTERVAL * 3); - return; - } - - amdgpu_ras_error_data_init(&err_data); - - amdgpu_umc_handle_bad_pages(adev, &err_data); - - amdgpu_ras_error_data_fini(&err_data); - - amdgpu_ras_schedule_retirement_dwork(con, - AMDGPU_RAS_RETIRE_PAGE_INTERVAL); -} - -static int amdgpu_ras_poison_creation_handler(struct amdgpu_device *adev, - uint32_t poison_creation_count) -{ - int ret = 0; - struct ras_ecc_log_info *ecc_log; - struct ras_query_if info; - u32 timeout = MAX_UMC_POISON_POLLING_TIME_ASYNC; - struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); - u64 de_queried_count; - u64 consumption_q_count; - enum ras_event_type type = RAS_EVENT_TYPE_POISON_CREATION; - - memset(&info, 0, sizeof(info)); - info.head.block = AMDGPU_RAS_BLOCK__UMC; - - ecc_log = &ras->umc_ecc_log; - ecc_log->de_queried_count = 0; - ecc_log->consumption_q_count = 0; - - do { - ret = amdgpu_ras_query_error_status_with_event(adev, &info, type); - if (ret) - return ret; - - de_queried_count = ecc_log->de_queried_count; - consumption_q_count = ecc_log->consumption_q_count; - - if (de_queried_count && consumption_q_count) - break; - - msleep(100); - } while (--timeout); - - if (de_queried_count) - schedule_delayed_work(&ras->page_retirement_dwork, 0); - - if (amdgpu_ras_is_rma(adev) && atomic_cmpxchg(&ras->rma_in_recovery, 0, 1) == 0) - amdgpu_ras_reset_gpu(adev); - - return 0; -} - -static void amdgpu_ras_clear_poison_fifo(struct amdgpu_device *adev) -{ - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - struct ras_poison_msg msg; - int ret; - - do { - ret = kfifo_get(&con->poison_fifo, &msg); - } while (ret); -} - -static int amdgpu_ras_poison_consumption_handler(struct amdgpu_device *adev, - uint32_t msg_count, uint32_t *gpu_reset) -{ - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - uint32_t reset_flags = 0, reset = 0; - struct ras_poison_msg msg; - int ret, i; - - kgd2kfd_set_sram_ecc_flag(adev->kfd.dev); - - for (i = 0; i < msg_count; i++) { - ret = amdgpu_ras_get_poison_req(adev, &msg); - if (!ret) - continue; - - if (msg.pasid_fn) - msg.pasid_fn(adev, msg.pasid, msg.data); - - reset_flags |= msg.reset; - } - - /* - * Try to ensure poison creation handler is completed first - * to set rma if bad page exceed threshold. - */ - flush_delayed_work(&con->page_retirement_dwork); - - /* for RMA, amdgpu_ras_poison_creation_handler will trigger gpu reset */ - if (reset_flags && !amdgpu_ras_is_rma(adev)) { - if (reset_flags & AMDGPU_RAS_GPU_RESET_MODE1_RESET) - reset = AMDGPU_RAS_GPU_RESET_MODE1_RESET; - else if (reset_flags & AMDGPU_RAS_GPU_RESET_MODE2_RESET) - reset = AMDGPU_RAS_GPU_RESET_MODE2_RESET; - else - reset = reset_flags; - - con->gpu_reset_flags |= reset; - amdgpu_ras_reset_gpu(adev); - - *gpu_reset = reset; - - /* Wait for gpu recovery to complete */ - flush_work(&con->recovery_work); - } - - return 0; -} - -static int amdgpu_ras_page_retirement_thread(void *param) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)param; - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - uint32_t poison_creation_count, msg_count; - uint32_t gpu_reset; - int ret; - - while (!kthread_should_stop()) { - - wait_event_interruptible(con->page_retirement_wq, - kthread_should_stop() || - atomic_read(&con->page_retirement_req_cnt)); - - if (kthread_should_stop()) - break; - - mutex_lock(&con->poison_lock); - gpu_reset = 0; - - do { - poison_creation_count = atomic_read(&con->poison_creation_count); - ret = amdgpu_ras_poison_creation_handler(adev, poison_creation_count); - if (ret == -EIO) - break; - - if (poison_creation_count) { - atomic_sub(poison_creation_count, &con->poison_creation_count); - atomic_sub(poison_creation_count, &con->page_retirement_req_cnt); - } - } while (atomic_read(&con->poison_creation_count) && - !atomic_read(&con->poison_consumption_count)); - - if (ret != -EIO) { - msg_count = kfifo_len(&con->poison_fifo); - if (msg_count) { - ret = amdgpu_ras_poison_consumption_handler(adev, - msg_count, &gpu_reset); - if ((ret != -EIO) && - (gpu_reset != AMDGPU_RAS_GPU_RESET_MODE1_RESET)) - atomic_sub(msg_count, &con->page_retirement_req_cnt); - } - } - - if ((ret == -EIO) || (gpu_reset == AMDGPU_RAS_GPU_RESET_MODE1_RESET)) { - /* gpu mode-1 reset is ongoing or just completed ras mode-1 reset */ - /* Clear poison creation request */ - atomic_set(&con->poison_creation_count, 0); - atomic_set(&con->poison_consumption_count, 0); - - /* Clear poison fifo */ - amdgpu_ras_clear_poison_fifo(adev); - - /* Clear all poison requests */ - atomic_set(&con->page_retirement_req_cnt, 0); - - if (ret == -EIO) { - /* Wait for mode-1 reset to complete */ - down_read(&adev->reset_domain->sem); - up_read(&adev->reset_domain->sem); - } - - /* Wake up work to save bad pages to eeprom */ - schedule_delayed_work(&con->page_retirement_dwork, 0); - } else if (gpu_reset) { - /* gpu just completed mode-2 reset or other reset */ - /* Clear poison consumption messages cached in fifo */ - msg_count = kfifo_len(&con->poison_fifo); - if (msg_count) { - amdgpu_ras_clear_poison_fifo(adev); - atomic_sub(msg_count, &con->page_retirement_req_cnt); - } - - atomic_set(&con->poison_consumption_count, 0); - - /* Wake up work to save bad pages to eeprom */ - schedule_delayed_work(&con->page_retirement_dwork, 0); - } - mutex_unlock(&con->poison_lock); - } - - return 0; -} - int amdgpu_ras_init_badpage_info(struct amdgpu_device *adev) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); @@ -3846,7 +3194,14 @@ int amdgpu_ras_init_badpage_info(struct amdgpu_device *adev) if (!con || amdgpu_sriov_vf(adev)) return 0; - if (amdgpu_uniras_enabled(adev)) + /* + * For the reset-on-init path (e.g. an NPS memory partition, + * switch) the RAS IP block hw_init has not been enabled and + * the amdgpu_uniras_enabled return false, check amdgpu ras + * context uniras_enabled flag, eeprom init will be called + * during RAS IP block hw_init. + */ + if (amdgpu_uniras_enabled(adev) || con->uniras_enabled) return 0; control = &con->eeprom_control; @@ -3855,9 +3210,6 @@ int amdgpu_ras_init_badpage_info(struct amdgpu_device *adev) ret = amdgpu_ras_eeprom_init(control); control->is_eeprom_valid = !ret; - if (!adev->umc.ras || !adev->umc.ras->convert_ras_err_addr) - control->ras_num_pa_recs = control->ras_num_recs; - if (adev->umc.ras && adev->umc.ras->get_retire_flip_bits) adev->umc.ras->get_retire_flip_bits(adev); @@ -3877,13 +3229,6 @@ int amdgpu_ras_init_badpage_info(struct amdgpu_device *adev) adev, control->bad_channel_bitmap); con->update_channel_flag = false; } - - /* The format action is only applied to new ASICs */ - if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) >= 12 && - control->tbl_hdr.version < RAS_TABLE_VER_V3) - if (!amdgpu_ras_eeprom_reset_table(control)) - if (amdgpu_ras_save_bad_pages(adev, NULL)) - dev_warn(adev->dev, "Failed to format RAS EEPROM data in V3 version!\n"); } return 0; @@ -3917,10 +3262,8 @@ int amdgpu_ras_recovery_init(struct amdgpu_device *adev, bool init_bp_info) } mutex_init(&con->recovery_lock); - mutex_init(&con->poison_lock); INIT_WORK(&con->recovery_work, amdgpu_ras_do_recovery); atomic_set(&con->in_recovery, 0); - atomic_set(&con->rma_in_recovery, 0); con->eeprom_control.bad_channel_bitmap = 0; max_eeprom_records_count = amdgpu_ras_eeprom_max_record_count(&con->eeprom_control); @@ -3933,21 +3276,8 @@ int amdgpu_ras_recovery_init(struct amdgpu_device *adev, bool init_bp_info) } mutex_init(&con->page_rsv_lock); - INIT_KFIFO(con->poison_fifo); mutex_init(&con->page_retirement_lock); - init_waitqueue_head(&con->page_retirement_wq); - atomic_set(&con->page_retirement_req_cnt, 0); - atomic_set(&con->poison_creation_count, 0); - atomic_set(&con->poison_consumption_count, 0); - con->page_retirement_thread = - kthread_run(amdgpu_ras_page_retirement_thread, adev, "umc_page_retirement"); - if (IS_ERR(con->page_retirement_thread)) { - con->page_retirement_thread = NULL; - dev_warn(adev->dev, "Failed to create umc_page_retirement thread!!!\n"); - } - - INIT_DELAYED_WORK(&con->page_retirement_dwork, amdgpu_ras_do_page_retirement); - amdgpu_ras_ecc_log_init(&con->umc_ecc_log); + #ifdef CONFIG_X86_MCE_AMD if ((adev->asic_type == CHIP_ALDEBARAN) && (adev->gmc.xgmi.connected_to_cpu)) @@ -3978,33 +3308,15 @@ static int amdgpu_ras_recovery_fini(struct amdgpu_device *adev) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); struct ras_err_handler_data *data = con->eh_data; - int max_flush_timeout = MAX_FLUSH_RETIRE_DWORK_TIMES; - bool ret; /* recovery_init failed to init it, fini is useless */ if (!data) return 0; - /* Save all cached bad pages to eeprom */ - do { - flush_delayed_work(&con->page_retirement_dwork); - ret = amdgpu_ras_schedule_retirement_dwork(con, 0); - } while (ret && max_flush_timeout--); - - if (con->page_retirement_thread) - kthread_stop(con->page_retirement_thread); - - atomic_set(&con->page_retirement_req_cnt, 0); - atomic_set(&con->poison_creation_count, 0); - mutex_destroy(&con->page_rsv_lock); cancel_work_sync(&con->recovery_work); - cancel_delayed_work_sync(&con->page_retirement_dwork); - - amdgpu_ras_ecc_log_fini(&con->umc_ecc_log); - mutex_lock(&con->recovery_lock); con->eh_data = NULL; kfree(data->bps); @@ -4206,15 +3518,6 @@ init_ras_enabled_flag: adev->ras_enabled = amdgpu_ras_enable == 0 ? 0 : adev->ras_hw_enabled & amdgpu_ras_mask; - /* aca is disabled by default except for psp v13_0_6/v13_0_12/v13_0_14 */ - if (!amdgpu_sriov_vf(adev)) { - adev->aca.is_enabled = - (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 6) || - amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 12) || - amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 14) || - amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(13, 0, 15)); - } - /* bad page feature is not applicable to specific app platform */ if (adev->gmc.is_app_apu && amdgpu_ip_version(adev, UMC_HWIP, 0) == IP_VERSION(12, 0, 0)) @@ -4435,15 +3738,6 @@ int amdgpu_ras_init(struct amdgpu_device *adev) goto release_con; } - if (amdgpu_ras_aca_is_supported(adev)) { - if (amdgpu_aca_is_enabled(adev)) - r = amdgpu_aca_init(adev); - else - r = amdgpu_mca_init(adev); - if (r) - goto release_con; - } - con->init_task_pid = task_pid_nr(current); get_task_comm(con->init_task_comm, current); @@ -4541,9 +3835,9 @@ int amdgpu_ras_block_late_init(struct amdgpu_device *adev, goto cleanup; } - if (ras_obj->hw_ops && + if (amdgpu_uniras_enabled(adev) || (ras_obj->hw_ops && (ras_obj->hw_ops->query_ras_error_count || - ras_obj->hw_ops->query_ras_error_status)) { + ras_obj->hw_ops->query_ras_error_status))) { r = amdgpu_ras_sysfs_create(adev, ras_block); if (r) goto interrupt; @@ -4671,28 +3965,13 @@ int amdgpu_ras_late_init(struct amdgpu_device *adev) amdgpu_ras_event_mgr_init(adev); - if (amdgpu_ras_aca_is_supported(adev)) { - if (amdgpu_reset_in_recovery(adev)) { - if (amdgpu_aca_is_enabled(adev)) - r = amdgpu_aca_reset(adev); - else - r = amdgpu_mca_reset(adev); - if (r) - return r; - } - - if (!amdgpu_sriov_vf(adev)) { - if (amdgpu_aca_is_enabled(adev)) - amdgpu_ras_set_aca_debug_mode(adev, false); - else - amdgpu_ras_set_mca_debug_mode(adev, false); - } - } - /* Guest side doesn't need init ras feature */ if (amdgpu_sriov_vf(adev) && !amdgpu_sriov_ras_telemetry_en(adev)) return 0; + if (amdgpu_uniras_enabled(adev)) + amdgpu_ras_mgr_set_debug_mode(adev, false); + list_for_each_entry_safe(node, tmp, &adev->ras_list, node) { obj = node->ras_obj; if (!obj) { @@ -4773,13 +4052,6 @@ int amdgpu_ras_fini(struct amdgpu_device *adev) amdgpu_ras_fs_fini(adev); amdgpu_ras_interrupt_remove_all(adev); - if (amdgpu_ras_aca_is_supported(adev)) { - if (amdgpu_aca_is_enabled(adev)) - amdgpu_aca_fini(adev); - else - amdgpu_mca_fini(adev); - } - WARN(AMDGPU_RAS_GET_FEATURES(con->features), "Feature mask is not cleared"); if (AMDGPU_RAS_GET_FEATURES(con->features)) @@ -5064,6 +4336,13 @@ static void amdgpu_register_bad_pages_mca_notifier(struct amdgpu_device *adev) * Use this list instead of mgpu_info to find the amdgpu * device on which the UMC error was reported. */ + if (mce_adev_list.num_gpu >= MAX_GPU_INSTANCE) { + dev_warn_ratelimited(adev->dev, + "mce_adev_list full, skip notifier registration (max=%d)\n", + MAX_GPU_INSTANCE); + return; + } + mce_adev_list.devs[mce_adev_list.num_gpu++] = adev; /* @@ -5181,59 +4460,10 @@ int amdgpu_ras_reset_gpu(struct amdgpu_device *adev) return 0; } -int amdgpu_ras_set_mca_debug_mode(struct amdgpu_device *adev, bool enable) -{ - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - int ret = 0; - - if (con) { - ret = amdgpu_mca_smu_set_debug_mode(adev, enable); - if (!ret) - con->is_aca_debug_mode = enable; - } - - return ret; -} - -int amdgpu_ras_set_aca_debug_mode(struct amdgpu_device *adev, bool enable) -{ - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - int ret = 0; - - if (con) { - if (amdgpu_aca_is_enabled(adev)) - ret = amdgpu_aca_smu_set_debug_mode(adev, enable); - else - ret = amdgpu_mca_smu_set_debug_mode(adev, enable); - if (!ret) - con->is_aca_debug_mode = enable; - } - - return ret; -} - -bool amdgpu_ras_get_aca_debug_mode(struct amdgpu_device *adev) -{ - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - const struct aca_smu_funcs *smu_funcs = adev->aca.smu_funcs; - const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs; - - if (!con) - return false; - - if ((amdgpu_aca_is_enabled(adev) && smu_funcs && smu_funcs->set_debug_mode) || - (!amdgpu_aca_is_enabled(adev) && mca_funcs && mca_funcs->mca_set_debug_mode)) - return con->is_aca_debug_mode; - else - return true; -} - bool amdgpu_ras_get_error_query_mode(struct amdgpu_device *adev, unsigned int *error_query_mode) { struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - const struct amdgpu_mca_smu_funcs *mca_funcs = adev->mca.mca_funcs; - const struct aca_smu_funcs *smu_funcs = adev->aca.smu_funcs; if (!con) { *error_query_mode = AMDGPU_RAS_INVALID_ERROR_QUERY; @@ -5242,9 +4472,6 @@ bool amdgpu_ras_get_error_query_mode(struct amdgpu_device *adev, if (amdgpu_sriov_vf(adev)) { *error_query_mode = AMDGPU_RAS_VIRT_ERROR_COUNT_QUERY; - } else if ((smu_funcs && smu_funcs->set_debug_mode) || (mca_funcs && mca_funcs->mca_set_debug_mode)) { - *error_query_mode = - (con->is_aca_debug_mode) ? AMDGPU_RAS_DIRECT_ERROR_QUERY : AMDGPU_RAS_FIRMWARE_ERROR_QUERY; } else { *error_query_mode = AMDGPU_RAS_DIRECT_ERROR_QUERY; } @@ -5834,3 +5061,8 @@ void amdgpu_ras_post_reset(struct amdgpu_device *adev, amdgpu_ras_mgr_post_reset(tmp_adev); } } + +void amdgpu_ras_resume_after_reset(struct amdgpu_device *adev) +{ + amdgpu_ras_mgr_resume_after_reset(adev); +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h index a86ab65aa2f0..a44aed7f169e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h @@ -31,7 +31,6 @@ #include "ta_ras_if.h" #include "amdgpu_ras_eeprom.h" #include "amdgpu_smuio.h" -#include "amdgpu_aca.h" struct amdgpu_iv_entry; @@ -466,14 +465,6 @@ struct ras_query_context { typedef int (*pasid_notify)(struct amdgpu_device *adev, uint16_t pasid, void *data); -struct ras_poison_msg { - enum amdgpu_ras_block block; - uint16_t pasid; - uint32_t reset; - pasid_notify pasid_fn; - void *data; -}; - struct ras_err_pages { uint32_t count; uint64_t *pfn; @@ -492,8 +483,6 @@ struct ras_ecc_err { struct ras_ecc_log_info { struct mutex lock; struct radix_tree_root de_page_tree; - uint64_t de_queried_count; - uint64_t consumption_q_count; }; struct ras_critical_region { @@ -549,7 +538,6 @@ struct amdgpu_ras { /* gpu recovery */ struct work_struct recovery_work; atomic_t in_recovery; - atomic_t rma_in_recovery; struct amdgpu_device *adev; /* error handler data */ struct ras_err_handler_data *eh_data; @@ -581,22 +569,15 @@ struct amdgpu_ras { /* Indicates smu whether need update bad channel info */ bool update_channel_flag; /* Record status of smu mca debug mode */ - bool is_aca_debug_mode; + bool is_mca_debug_mode; bool is_rma; /* Record special requirements of gpu reset caller */ uint32_t gpu_reset_flags; - struct task_struct *page_retirement_thread; - wait_queue_head_t page_retirement_wq; struct mutex page_retirement_lock; - atomic_t page_retirement_req_cnt; - atomic_t poison_creation_count; - atomic_t poison_consumption_count; struct mutex page_rsv_lock; - DECLARE_KFIFO(poison_fifo, struct ras_poison_msg, 128); struct ras_ecc_log_info umc_ecc_log; - struct delayed_work page_retirement_dwork; /* ras errors detected */ unsigned long ras_err_state; @@ -615,9 +596,6 @@ struct amdgpu_ras { struct list_head critical_region_head; struct mutex critical_region_lock; - /* Protect poison injection */ - struct mutex poison_lock; - /* Disable/Enable uniras switch */ bool uniras_enabled; const struct ras_smu_drv *ras_smu_drv; @@ -702,8 +680,6 @@ struct ras_manager { struct ras_ih_data ih_data; struct ras_err_data err_data; - - struct aca_handle aca_handle; }; struct ras_badpage { @@ -964,8 +940,7 @@ struct amdgpu_ras* amdgpu_ras_get_context(struct amdgpu_device *adev); int amdgpu_ras_set_context(struct amdgpu_device *adev, struct amdgpu_ras *ras_con); int amdgpu_ras_set_mca_debug_mode(struct amdgpu_device *adev, bool enable); -int amdgpu_ras_set_aca_debug_mode(struct amdgpu_device *adev, bool enable); -bool amdgpu_ras_get_aca_debug_mode(struct amdgpu_device *adev); +bool amdgpu_ras_get_mca_debug_mode(struct amdgpu_device *adev); bool amdgpu_ras_get_error_query_mode(struct amdgpu_device *adev, unsigned int *mode); @@ -1006,12 +981,6 @@ int amdgpu_ras_error_statistic_de_count(struct ras_err_data *err_data, struct amdgpu_smuio_mcm_config_info *mcm_info, u64 count); void amdgpu_ras_query_boot_status(struct amdgpu_device *adev, u32 num_instances); -int amdgpu_ras_bind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk, - const struct aca_info *aca_info, void *data); -int amdgpu_ras_unbind_aca(struct amdgpu_device *adev, enum amdgpu_ras_block blk); - -ssize_t amdgpu_ras_aca_sysfs_read(struct device *dev, struct device_attribute *attr, - struct aca_handle *handle, char *buf, void *data); void amdgpu_ras_set_fed(struct amdgpu_device *adev, bool status); bool amdgpu_ras_get_fed_status(struct amdgpu_device *adev); @@ -1029,10 +998,6 @@ int amdgpu_ras_reserve_page(struct amdgpu_device *adev, uint64_t pfn); int amdgpu_ras_add_critical_region(struct amdgpu_device *adev, struct amdgpu_bo *bo); bool amdgpu_ras_check_critical_address(struct amdgpu_device *adev, uint64_t addr); -int amdgpu_ras_put_poison_req(struct amdgpu_device *adev, - enum amdgpu_ras_block block, uint16_t pasid, - pasid_notify pasid_fn, void *data, uint32_t reset); - bool amdgpu_ras_in_recovery(struct amdgpu_device *adev); __printf(3, 4) @@ -1045,4 +1010,5 @@ void amdgpu_ras_pre_reset(struct amdgpu_device *adev, struct list_head *device_list); void amdgpu_ras_post_reset(struct amdgpu_device *adev, struct list_head *device_list); +void amdgpu_ras_resume_after_reset(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c index b265b4d9053f..baa8cc3646d5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.c @@ -124,8 +124,6 @@ RAS_TABLE_V2_1_INFO_SIZE) \ / RAS_TABLE_RECORD_SIZE) -#define RAS_SMU_MESSAGE_TIMEOUT_MS 1000 /* 1s */ - /* Given a zero-based index of an EEPROM RAS record, yields the EEPROM * offset off of RAS_TABLE_START. That is, this is something you can * add to control->i2c_address, and then tell I2C layer to read @@ -159,6 +157,9 @@ static bool __is_ras_eeprom_supported(struct amdgpu_device *adev) { + if (amdgpu_sriov_vf(adev)) + return false; + switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { case IP_VERSION(11, 0, 2): /* VEGA20 and ARCTURUS */ case IP_VERSION(11, 0, 7): /* Sienna cichlid */ @@ -449,57 +450,46 @@ int amdgpu_ras_eeprom_reset_table(struct amdgpu_ras_eeprom_control *control) struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr; struct amdgpu_ras_eeprom_table_ras_info *rai = &control->tbl_rai; struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - u32 erase_res = 0; u8 csum; int res; mutex_lock(&control->ras_tbl_mutex); - if (!amdgpu_ras_smu_eeprom_supported(adev)) { - hdr->header = RAS_TABLE_HDR_VAL; - amdgpu_ras_set_eeprom_table_version(control); - - if (hdr->version >= RAS_TABLE_VER_V2_1) { - hdr->first_rec_offset = RAS_RECORD_START_V2_1; - hdr->tbl_size = RAS_TABLE_HEADER_SIZE + - RAS_TABLE_V2_1_INFO_SIZE; - rai->rma_status = GPU_HEALTH_USABLE; - - control->ras_record_offset = RAS_RECORD_START_V2_1; - control->ras_max_record_count = RAS_MAX_RECORD_COUNT_V2_1; - /** - * GPU health represented as a percentage. - * 0 means worst health, 100 means fully health. - */ - rai->health_percent = 100; - /* ecc_page_threshold = 0 means disable bad page retirement */ - rai->ecc_page_threshold = con->bad_page_cnt_threshold; - } else { - hdr->first_rec_offset = RAS_RECORD_START; - hdr->tbl_size = RAS_TABLE_HEADER_SIZE; + hdr->header = RAS_TABLE_HDR_VAL; + amdgpu_ras_set_eeprom_table_version(control); - control->ras_record_offset = RAS_RECORD_START; - control->ras_max_record_count = RAS_MAX_RECORD_COUNT; - } + if (hdr->version >= RAS_TABLE_VER_V2_1) { + hdr->first_rec_offset = RAS_RECORD_START_V2_1; + hdr->tbl_size = RAS_TABLE_HEADER_SIZE + + RAS_TABLE_V2_1_INFO_SIZE; + rai->rma_status = GPU_HEALTH_USABLE; - csum = __calc_hdr_byte_sum(control); - if (hdr->version >= RAS_TABLE_VER_V2_1) - csum += __calc_ras_info_byte_sum(control); - csum = -csum; - hdr->checksum = csum; - res = __write_table_header(control); - if (!res && hdr->version > RAS_TABLE_VER_V1) - res = __write_table_ras_info(control); + control->ras_record_offset = RAS_RECORD_START_V2_1; + control->ras_max_record_count = RAS_MAX_RECORD_COUNT_V2_1; + /** + * GPU health represented as a percentage. + * 0 means worst health, 100 means fully health. + */ + rai->health_percent = 100; + /* ecc_page_threshold = 0 means disable bad page retirement */ + rai->ecc_page_threshold = con->bad_page_cnt_threshold; } else { - res = amdgpu_ras_smu_erase_ras_table(adev, &erase_res); - if (res || erase_res) { - dev_warn(adev->dev, "RAS EEPROM reset failed, res:%d result:%d", - res, erase_res); - if (!res) - res = -EIO; - } + hdr->first_rec_offset = RAS_RECORD_START; + hdr->tbl_size = RAS_TABLE_HEADER_SIZE; + + control->ras_record_offset = RAS_RECORD_START; + control->ras_max_record_count = RAS_MAX_RECORD_COUNT; } + csum = __calc_hdr_byte_sum(control); + if (hdr->version >= RAS_TABLE_VER_V2_1) + csum += __calc_ras_info_byte_sum(control); + csum = -csum; + hdr->checksum = csum; + res = __write_table_header(control); + if (!res && hdr->version > RAS_TABLE_VER_V1) + res = __write_table_ras_info(control); + control->ras_num_recs = 0; control->ras_num_bad_pages = 0; control->ras_num_mca_recs = 0; @@ -662,7 +652,6 @@ amdgpu_ras_eeprom_append_table(struct amdgpu_ras_eeprom_control *control, const u32 num) { struct amdgpu_ras *con = amdgpu_ras_get_context(to_amdgpu_device(control)); - struct amdgpu_device *adev = to_amdgpu_device(control); u32 a, b, i; u8 *buf, *pp; int res; @@ -767,10 +756,7 @@ amdgpu_ras_eeprom_append_table(struct amdgpu_ras_eeprom_control *control, % control->ras_max_record_count; /*old asics only save pa to eeprom like before*/ - if (IP_VERSION_MAJ(amdgpu_ip_version(adev, UMC_HWIP, 0)) < 12) - control->ras_num_pa_recs += num; - else - control->ras_num_mca_recs += num; + control->ras_num_pa_recs += num; control->ras_num_bad_pages = con->bad_page_num; Out: @@ -879,71 +865,6 @@ Out: return res; } -int amdgpu_ras_eeprom_update_record_num(struct amdgpu_ras_eeprom_control *control) -{ - struct amdgpu_device *adev = to_amdgpu_device(control); - int ret, retry = 20; - - if (!amdgpu_ras_smu_eeprom_supported(adev)) - return 0; - - control->ras_num_recs_old = control->ras_num_recs; - - do { - /* 1000ms timeout is long enough, smu_get_badpage_count won't - * return -EBUSY before timeout. - */ - ret = amdgpu_ras_smu_get_badpage_count(adev, - &(control->ras_num_recs), RAS_SMU_MESSAGE_TIMEOUT_MS); - if (!ret && - (control->ras_num_recs_old == control->ras_num_recs)) { - /* record number update in PMFW needs some time, - * smu_get_badpage_count may return immediately without - * count update, sleep for a while and retry again. - */ - msleep(50); - retry--; - } else { - break; - } - } while (retry); - - /* no update of record number is not a real failure, - * don't print warning here - */ - if (!ret && (control->ras_num_recs_old == control->ras_num_recs)) - ret = -EINVAL; - - return ret; -} - -static int amdgpu_ras_smu_eeprom_append(struct amdgpu_ras_eeprom_control *control) -{ - struct amdgpu_device *adev = to_amdgpu_device(control); - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - - if (!amdgpu_ras_smu_eeprom_supported(adev) || !con) - return 0; - - control->ras_num_bad_pages = con->bad_page_num; - - if (amdgpu_bad_page_threshold != 0 && - control->ras_num_bad_pages > con->bad_page_cnt_threshold) { - dev_warn(adev->dev, - "Saved bad pages %d reaches threshold value %d\n", - control->ras_num_bad_pages, con->bad_page_cnt_threshold); - - if (adev->cper.enabled && amdgpu_cper_generate_bp_threshold_record(adev)) - dev_warn(adev->dev, "fail to generate bad page threshold cper records\n"); - - if ((amdgpu_bad_page_threshold != -1) && - (amdgpu_bad_page_threshold != -2)) - con->is_rma = true; - } - - return 0; -} - /** * amdgpu_ras_eeprom_append -- append records to the EEPROM RAS table * @control: pointer to control structure @@ -968,9 +889,6 @@ int amdgpu_ras_eeprom_append(struct amdgpu_ras_eeprom_control *control, if (!__is_ras_eeprom_supported(adev)) return 0; - if (amdgpu_ras_smu_eeprom_supported(adev)) - return amdgpu_ras_smu_eeprom_append(control); - if (num == 0) { dev_err(adev->dev, "will not append 0 records\n"); return -EINVAL; @@ -1046,52 +964,6 @@ static int __amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control, return res; } -int amdgpu_ras_eeprom_read_idx(struct amdgpu_ras_eeprom_control *control, - struct eeprom_table_record *record, u32 rec_idx, - const u32 num) -{ - struct amdgpu_device *adev = to_amdgpu_device(control); - uint64_t ts, end_idx; - int i, ret; - u64 mca, ipid; - u32 cu, mem_channel, mcumc_id; - - if (!amdgpu_ras_smu_eeprom_supported(adev)) - return 0; - - if (!adev->umc.ras || !adev->umc.ras->mca_ipid_parse) - return -EOPNOTSUPP; - - end_idx = rec_idx + num; - for (i = rec_idx; i < end_idx; i++) { - ret = amdgpu_ras_smu_get_badpage_mca_addr(adev, i, &mca); - if (ret) - return ret; - - ret = amdgpu_ras_smu_get_badpage_ipid(adev, i, &ipid); - if (ret) - return ret; - - ret = amdgpu_ras_smu_get_timestamp(adev, i, &ts); - if (ret) - return ret; - - record[i - rec_idx].address = mca; - /* retired_page (pa) is unused now */ - record[i - rec_idx].retired_page = 0x1ULL; - record[i - rec_idx].ts = ts; - record[i - rec_idx].err_type = AMDGPU_RAS_EEPROM_ERR_NON_RECOVERABLE; - - adev->umc.ras->mca_ipid_parse(adev, ipid, - &cu, &mem_channel, &mcumc_id, NULL); - record[i - rec_idx].cu = (u8)cu; - record[i - rec_idx].mem_channel = (u8)mem_channel; - record[i - rec_idx].mcumc_id = (u8)mcumc_id; - } - - return 0; -} - /** * amdgpu_ras_eeprom_read -- read EEPROM * @control: pointer to control structure @@ -1113,9 +985,6 @@ int amdgpu_ras_eeprom_read(struct amdgpu_ras_eeprom_control *control, u8 *buf, *pp; u32 g0, g1; - if (amdgpu_ras_smu_eeprom_supported(adev)) - return amdgpu_ras_eeprom_read_idx(control, record, 0, num); - if (!__is_ras_eeprom_supported(adev)) return 0; @@ -1396,6 +1265,86 @@ Out: } static ssize_t +amdgpu_ras_debugfs_table_read_uniras(struct amdgpu_device *adev, + char __user *buf, + size_t size, loff_t *pos) +{ + struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev); + struct ras_core_context *ras_core = ras_mgr ? ras_mgr->ras_core : NULL; + struct eeprom_umc_record *records = NULL; + struct ras_eeprom_control *control; + size_t bufsz, len = 0; + u32 num_recs; + char *kbuf; + ssize_t res; + int i; + + if (!ras_core) + return 0; + + /* pmfw manages eeprom data by itself */ + if (ras_fw_eeprom_supported(ras_core)) + return 0; + + control = &ras_core->ras_eeprom; + num_recs = ras_eeprom_get_record_count(ras_core); + + bufsz = strlen(tbl_hdr_str) + tbl_hdr_fmt_size + + strlen(rec_hdr_str) + (size_t)rec_hdr_fmt_size * num_recs + 1; + + kbuf = kvmalloc(bufsz, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + if (num_recs) { + records = kvcalloc(num_recs, sizeof(*records), GFP_KERNEL); + if (!records) { + res = -ENOMEM; + goto out; + } + + res = ras_eeprom_read(ras_core, records, num_recs); + if (res) + goto out; + } + + len += scnprintf(kbuf + len, bufsz - len, "%s", tbl_hdr_str); + len += scnprintf(kbuf + len, bufsz - len, tbl_hdr_fmt, + control->tbl_hdr.header, + control->tbl_hdr.version, + control->tbl_hdr.first_rec_offset, + control->tbl_hdr.tbl_size, + control->tbl_hdr.checksum); + len += scnprintf(kbuf + len, bufsz - len, "%s", rec_hdr_str); + + for (i = 0; i < num_recs; i++) { + u32 ai = RAS_RI_TO_AI(control, i); + int et = records[i].err_type; + const char *ets = (et >= 0 && et < AMDGPU_RAS_EEPROM_ERR_COUNT) ? + record_err_type_str[et] : "na"; + + len += scnprintf(kbuf + len, bufsz - len, rec_hdr_fmt, + i, + RAS_INDEX_TO_OFFSET(control, ai), + ets, + records[i].bank, + records[i].ts, + records[i].offset, + records[i].mem_channel, + records[i].mcumc_id, + records[i].retired_row_pfn); + } + + res = simple_read_from_buffer(buf, size, pos, kbuf, len); + +out: + kvfree(records); + kvfree(kbuf); + + return res; +} + +static ssize_t amdgpu_ras_debugfs_eeprom_table_read(struct file *f, char __user *buf, size_t size, loff_t *pos) { @@ -1408,6 +1357,10 @@ amdgpu_ras_debugfs_eeprom_table_read(struct file *f, char __user *buf, if (!size) return size; + if (amdgpu_uniras_enabled(adev)) + return amdgpu_ras_debugfs_table_read_uniras(adev, buf, + size, pos); + if (!ras || !control) { res = snprintf(data, sizeof(data), "Not supported\n"); if (*pos >= res) @@ -1521,42 +1474,6 @@ Out: return res == RAS_TABLE_V2_1_INFO_SIZE ? 0 : res; } -static int amdgpu_ras_smu_eeprom_init(struct amdgpu_ras_eeprom_control *control) -{ - struct amdgpu_device *adev = to_amdgpu_device(control); - struct amdgpu_ras_eeprom_table_header *hdr = &control->tbl_hdr; - struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); - uint64_t local_time; - int res; - - ras->is_rma = false; - - if (!__is_ras_eeprom_supported(adev)) - return 0; - mutex_init(&control->ras_tbl_mutex); - - res = amdgpu_ras_smu_get_table_version(adev, &(hdr->version)); - if (res) - return res; - - res = amdgpu_ras_smu_get_badpage_count(adev, - &(control->ras_num_recs), 100); - if (res) - return res; - - local_time = (uint64_t)ktime_get_real_seconds(); - res = amdgpu_ras_smu_set_timestamp(adev, local_time); - if (res) - return res; - - control->ras_max_record_count = 4000; - - control->ras_num_mca_recs = 0; - control->ras_num_pa_recs = 0; - - return 0; -} - int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control) { struct amdgpu_device *adev = to_amdgpu_device(control); @@ -1567,9 +1484,6 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control) uint32_t vram_type = adev->gmc.vram_type; int res; - if (amdgpu_ras_smu_eeprom_supported(adev)) - return amdgpu_ras_smu_eeprom_init(control); - ras->is_rma = false; if (!__is_ras_eeprom_supported(adev)) @@ -1663,47 +1577,6 @@ int amdgpu_ras_eeprom_init(struct amdgpu_ras_eeprom_control *control) return 0; } -static int amdgpu_ras_smu_eeprom_check(struct amdgpu_ras_eeprom_control *control) -{ - struct amdgpu_device *adev = to_amdgpu_device(control); - struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); - - if (!__is_ras_eeprom_supported(adev)) - return 0; - - control->ras_num_bad_pages = ras->bad_page_num; - - if ((ras->bad_page_cnt_threshold < control->ras_num_bad_pages) && - amdgpu_bad_page_threshold != 0) { - dev_warn(adev->dev, - "RAS records:%d exceed threshold:%d\n", - control->ras_num_bad_pages, ras->bad_page_cnt_threshold); - if ((amdgpu_bad_page_threshold == -1) || - (amdgpu_bad_page_threshold == -2)) { - dev_warn(adev->dev, - "Please consult AMD Service Action Guide (SAG) for appropriate service procedures\n"); - } else { - ras->is_rma = true; - dev_warn(adev->dev, - "User defined threshold is set, runtime service will be halt when threshold is reached\n"); - } - - return 0; - } - - dev_dbg(adev->dev, - "Found existing EEPROM table with %d records", - control->ras_num_bad_pages); - - /* Warn if we are at 90% of the threshold or above - */ - if (10 * control->ras_num_bad_pages >= 9 * ras->bad_page_cnt_threshold) - dev_warn(adev->dev, "RAS records:%u exceeds 90%% of threshold:%d", - control->ras_num_bad_pages, - ras->bad_page_cnt_threshold); - return 0; -} - int amdgpu_ras_eeprom_check(struct amdgpu_ras_eeprom_control *control) { struct amdgpu_device *adev = to_amdgpu_device(control); @@ -1711,9 +1584,6 @@ int amdgpu_ras_eeprom_check(struct amdgpu_ras_eeprom_control *control) struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); int res = 0; - if (amdgpu_ras_smu_eeprom_supported(adev)) - return amdgpu_ras_smu_eeprom_check(control); - if (!__is_ras_eeprom_supported(adev)) return 0; @@ -1973,7 +1843,7 @@ void amdgpu_ras_check_bad_page_status(struct amdgpu_device *adev) struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); struct amdgpu_ras_eeprom_control *control = ras ? &ras->eeprom_control : NULL; - if (!control || amdgpu_bad_page_threshold == 0) + if (!__is_ras_eeprom_supported(adev) || !control || amdgpu_bad_page_threshold == 0) return; if (control->ras_num_bad_pages > ras->bad_page_cnt_threshold) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h index a62114800a92..3c7fcce5fe8b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras_eeprom.h @@ -82,7 +82,6 @@ struct amdgpu_ras_eeprom_control { /* Number of records in the table. */ u32 ras_num_recs; - u32 ras_num_recs_old; /* the bad page number is ras_num_recs or * ras_num_recs * umc.retire_unit @@ -191,8 +190,6 @@ int amdgpu_ras_eeprom_read_idx(struct amdgpu_ras_eeprom_control *control, struct eeprom_table_record *record, u32 rec_idx, const u32 num); -int amdgpu_ras_eeprom_update_record_num(struct amdgpu_ras_eeprom_control *control); - void amdgpu_ras_check_bad_page_status(struct amdgpu_device *adev); extern const struct file_operations amdgpu_ras_debugfs_eeprom_size_ops; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index b97fa35bac23..4d417c4a5cd2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -935,6 +935,194 @@ int amdgpu_ring_reset_helper_end(struct amdgpu_ring *ring, return 0; } +/** + * amdgpu_multi_ring_reset_helper_begin() - Prepare multiple rings for a reset. + * + * @ring_type_mask: Bitmask of affected ring types + * @guilty_ring: The ring which is guilty of causing a reset. + * @guilty_fence: The fence which didn't signal on the guilty ring. + * + * Useful when performing a GPU reset method that affects + * multiple rings at the same time, such as an IP block soft + * reset. For example, a GFX IP block soft reset will affect + * every graphics and compute queue. + * + * This function should be called before such a reset. + * + * Prepare the affected rings before the reset, make sure to + * minimize collateral damage, and backup the contents of + * the rings. Then the caller can call the actual HW specific + * reset function. + * + * After the reset is complete, the caller should then call + * amdgpu_multi_ring_reset_helper_end() to restore the rings. + */ +void amdgpu_multi_ring_reset_helper_begin(const u32 ring_type_mask, + struct amdgpu_ring *guilty_ring, + struct amdgpu_fence *guilty_fence) +{ + struct amdgpu_device *adev = guilty_ring->adev; + struct amdgpu_fence *ring_guilty_fence; + struct amdgpu_ring *ring; + bool rings_busy; + int i; + u32 t; + + for (i = 0; i < adev->num_rings; ++i) { + ring = adev->rings[i]; + + if (!(BIT(ring->funcs->type) & ring_type_mask)) + continue; + + /* Don't accept new submissions on the ring. */ + if (amdgpu_ring_sched_ready(ring) && !drm_sched_is_stopped(&ring->sched)) + drm_sched_wqueue_stop(&ring->sched); + + /* + * Clear the preempt condition to stop the ring + * from starting its next submission. This ensures + * that only the currently executing submission + * can be rejected because of the reset and helps + * minimize collateral damage. + */ + if (ring->funcs->init_cond_exec) + amdgpu_ring_set_preempt_cond_exec(ring, false); + } + + /* Flush HDP cache so the GPU can see the updated COND_EXEC values */ + amdgpu_device_flush_hdp(adev, NULL); + + /* + * Give some time for non-guilty rings to finish their + * current submission, to try to minimize collateral damage. + * + * Note that this is just a best effort, but really there + * is no way to really know which ring is actually responsible + * because different rings may share resources, eg. a compute + * ring may hog shader engines, causing a graphics ring to hang. + */ + for (t = 0; t < adev->usec_timeout; t += 10000) { + rings_busy = false; + + /* Check if any of the non-guilty rings are busy */ + for (i = 0; i < adev->num_rings; ++i) { + ring = adev->rings[i]; + + if (!(BIT(ring->funcs->type) & ring_type_mask)) + continue; + + if (ring == guilty_ring) + continue; + + rings_busy |= + atomic_read(&ring->fence_drv.last_seq) != + READ_ONCE(ring->fence_drv.sync_seq); + } + + if (!rings_busy) + break; + + mdelay(10); + } + + for (i = 0; i < adev->num_rings; ++i) { + ring = adev->rings[i]; + + if (!(BIT(ring->funcs->type) & ring_type_mask)) + continue; + + /* + * Find guilty fences, ie. the fences that didn't signal + * on each ring. At this point there is no way to know + * which one is really responsible for the hang, and no + * way to save any of them, so we treat all of them as guilty. + */ + ring_guilty_fence = + ring == guilty_ring ? guilty_fence : + amdgpu_ring_find_guilty_fence(ring); + + /* + * Backup current contents of the ring. + * The helper takes care to only reemit unsignalled fences + * so we don't have to worry about that here. + */ + amdgpu_ring_reset_helper_begin(ring, ring_guilty_fence); + } +} + +/** + * amdgpu_multi_ring_reset_helper_end() - Prepare multiple rings for a reset. + * + * @ring_type_mask: Bitmask of affected ring types + * @guilty_ring: The ring which is guilty of causing a reset. + * @ret: Return code from the reset function. + * + * After calling amdgpu_multi_ring_reset_helper_begin() + * and executing the actual reset method, call this + * function to restore normal operation. + * + * In case the reset failed, this function should still + * be called to restore preemption state, but it won't attempt to + * fully restore the ring contents. + */ +int amdgpu_multi_ring_reset_helper_end(const u32 ring_type_mask, + struct amdgpu_ring *guilty_ring, int ret) +{ + struct amdgpu_device *adev = guilty_ring->adev; + struct amdgpu_ring *ring; + int i, r; + + /* Set preempt condition, rings are now allowed to execute submissions */ + for (i = 0; i < adev->num_rings; ++i) { + ring = adev->rings[i]; + + if (!(BIT(ring->funcs->type) & ring_type_mask)) + continue; + + if (ring->funcs->init_cond_exec) + amdgpu_ring_set_preempt_cond_exec(ring, true); + } + + /* Flush HDP cache so the GPU can see the updated COND_EXEC values */ + amdgpu_device_flush_hdp(adev, NULL); + + /* If the reset was unsuccessful, return without restoring anything else. */ + if (ret) + return ret; + + /* Restore contents of all rings */ + for (i = 0; i < adev->num_rings; ++i) { + ring = adev->rings[i]; + + if (!(BIT(ring->funcs->type) & ring_type_mask)) + continue; + + /* Restore contents of the ring */ + r = amdgpu_ring_reset_helper_end(ring, ring->guilty_fence); + if (r) { + dev_err(adev->dev, + "Failed to recover ring %s after soft reset\n", + ring->name); + return r; + } + } + + /* Accept submissions on all rings again */ + for (i = 0; i < adev->num_rings; ++i) { + ring = adev->rings[i]; + + if (!(BIT(ring->funcs->type) & ring_type_mask)) + continue; + + if (!amdgpu_ring_sched_ready(ring)) + continue; + + drm_sched_wqueue_start(&ring->sched); + } + + return 0; +} + bool amdgpu_ring_is_reset_type_supported(struct amdgpu_ring *ring, u32 reset_type) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h index 8f28b3bd7010..9d3934b4f106 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.h @@ -314,6 +314,7 @@ struct amdgpu_ring { uint32_t *ring_backup; unsigned int ring_backup_entries_to_copy; bool reemit; + struct amdgpu_fence *guilty_fence; unsigned rptr_offs; u64 rptr_gpu_addr; u32 *rptr_cpu_addr; @@ -588,10 +589,17 @@ int amdgpu_ib_ring_tests(struct amdgpu_device *adev); bool amdgpu_ring_sched_ready(struct amdgpu_ring *ring); void amdgpu_ring_backup_unprocessed_commands(struct amdgpu_ring *ring, struct amdgpu_fence *guilty_fence); +struct amdgpu_fence * +amdgpu_ring_find_guilty_fence(struct amdgpu_ring *ring); void amdgpu_ring_reset_helper_begin(struct amdgpu_ring *ring, struct amdgpu_fence *guilty_fence); int amdgpu_ring_reset_helper_end(struct amdgpu_ring *ring, struct amdgpu_fence *guilty_fence); +void amdgpu_multi_ring_reset_helper_begin(const u32 ring_type_mask, + struct amdgpu_ring *guilty_ring, + struct amdgpu_fence *guilty_fence); +int amdgpu_multi_ring_reset_helper_end(const u32 ring_type_mask, + struct amdgpu_ring *guilty_ring, int ret); bool amdgpu_ring_is_reset_type_supported(struct amdgpu_ring *ring, u32 reset_type); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c index 572a60e1b3cb..002fae3c380e 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.c @@ -583,3 +583,42 @@ int amdgpu_gfx_rlc_init_microcode(struct amdgpu_device *adev, amdgpu_gfx_rlc_init_microcode_v2_5(adev); return 0; } + +static const struct amdgpu_rlc_reg_funcs amdgpu_sriov_rlc_reg_funcs = { + .rreg32 = amdgpu_sriov_rreg, + .wreg32 = amdgpu_sriov_wreg, +}; + +static u32 +amdgpu_rlc_rreg(struct amdgpu_device *adev, u32 reg, u32 acc_flags, u32 hwip, + u32 xcc_id) +{ + return amdgpu_device_rreg(adev, reg, 0); +} + +static void +amdgpu_rlc_wreg(struct amdgpu_device *adev, u32 reg, u32 value, u32 acc_flags, + u32 hwip, u32 xcc_id) +{ + amdgpu_device_wreg(adev, reg, value, 0); +} + +static const struct amdgpu_rlc_reg_funcs amdgpu_rlc_reg_funcs = { + .rreg32 = amdgpu_rlc_rreg, + .wreg32 = amdgpu_rlc_wreg, +}; + +void amdgpu_early_init_rlc_reg_funcs(struct amdgpu_device *adev) +{ + adev->gfx.rlc.reg_funcs = &amdgpu_rlc_reg_funcs; +} + +void amdgpu_init_rlc_reg_funcs(struct amdgpu_device *adev) +{ + if (amdgpu_sriov_vf(adev) && + adev->gfx.rlc.funcs && + adev->gfx.rlc.rlcg_reg_access_supported) + adev->gfx.rlc.reg_funcs = &amdgpu_sriov_rlc_reg_funcs; + else + adev->gfx.rlc.reg_funcs = &amdgpu_rlc_reg_funcs; +} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h index e535534237a1..959d60c90dcd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rlc.h @@ -262,6 +262,11 @@ struct amdgpu_rlc_funcs { bool (*is_rlcg_access_range)(struct amdgpu_device *adev, uint32_t reg); }; +struct amdgpu_rlc_reg_funcs { + u32 (*rreg32)(struct amdgpu_device *adev, u32 reg, u32 acc_flags, u32 hwip, u32 xcc_id); + void (*wreg32)(struct amdgpu_device *adev, u32 reg, u32 val, u32 acc_flags, u32 hwip, u32 xcc_id); +}; + struct amdgpu_rlcg_reg_access_ctrl { uint32_t scratch_reg0; uint32_t scratch_reg1; @@ -303,6 +308,7 @@ struct amdgpu_rlc { /* safe mode for updating CG/PG state */ bool in_safe_mode[AMDGPU_MAX_RLC_INSTANCES]; const struct amdgpu_rlc_funcs *funcs; + const struct amdgpu_rlc_reg_funcs *reg_funcs; /* for firmware data */ u32 save_and_restore_offset; @@ -374,4 +380,8 @@ void amdgpu_gfx_rlc_fini(struct amdgpu_device *adev); int amdgpu_gfx_rlc_init_microcode(struct amdgpu_device *adev, uint16_t version_major, uint16_t version_minor); + +void amdgpu_early_init_rlc_reg_funcs(struct amdgpu_device *adev); +void amdgpu_init_rlc_reg_funcs(struct amdgpu_device *adev); + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h new file mode 100644 index 000000000000..8c85c80fc119 --- /dev/null +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.h @@ -0,0 +1,77 @@ +/* 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. + * + */ + +#ifndef AMDGPU_SA_H_ +#define AMDGPU_SA_H_ + +#include <drm/drm_suballoc.h> + +struct amdgpu_device; +struct amdgpu_bo; + +struct amdgpu_sa_manager { + struct drm_suballoc_manager base; + struct amdgpu_bo *bo; + uint64_t gpu_addr; + void *cpu_ptr; +}; + +static inline struct amdgpu_sa_manager * +to_amdgpu_sa_manager(struct drm_suballoc_manager *manager) +{ + return container_of(manager, struct amdgpu_sa_manager, base); +} + +static inline uint64_t amdgpu_sa_bo_gpu_addr(struct drm_suballoc *sa_bo) +{ + return to_amdgpu_sa_manager(sa_bo->manager)->gpu_addr + + drm_suballoc_soffset(sa_bo); +} + +static inline void *amdgpu_sa_bo_cpu_addr(struct drm_suballoc *sa_bo) +{ + return to_amdgpu_sa_manager(sa_bo->manager)->cpu_ptr + + drm_suballoc_soffset(sa_bo); +} + +int amdgpu_sa_bo_manager_init(struct amdgpu_device *adev, + struct amdgpu_sa_manager *sa_manager, + unsigned size, u32 align, u32 domain); +void amdgpu_sa_bo_manager_fini(struct amdgpu_device *adev, + struct amdgpu_sa_manager *sa_manager); +int amdgpu_sa_bo_manager_start(struct amdgpu_device *adev, + struct amdgpu_sa_manager *sa_manager); +int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager, + struct drm_suballoc **sa_bo, + unsigned int size); +void amdgpu_sa_bo_free(struct drm_suballoc **sa_bo, + struct dma_fence *fence); +#if defined(CONFIG_DEBUG_FS) +void amdgpu_sa_bo_dump_debug_info(struct amdgpu_sa_manager *sa_manager, + struct seq_file *m); +u64 amdgpu_bo_print_info(int id, struct amdgpu_bo *bo, struct seq_file *m); +#endif +void amdgpu_debugfs_sa_init(struct amdgpu_device *adev); + +#endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c index 0eecfaa3a94c..8effb1158430 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sched.c @@ -39,7 +39,7 @@ static int amdgpu_sched_process_priority_override(struct amdgpu_device *adev, struct amdgpu_fpriv *fpriv; struct amdgpu_ctx_mgr *mgr; struct amdgpu_ctx *ctx; - uint32_t id; + unsigned long id; int r; if (fd_empty(f)) @@ -50,10 +50,10 @@ static int amdgpu_sched_process_priority_override(struct amdgpu_device *adev, return r; mgr = &fpriv->ctx_mgr; - mutex_lock(&mgr->lock); - idr_for_each_entry(&mgr->ctx_handles, ctx, id) + xa_lock(&mgr->ctx_handles); + xa_for_each(&mgr->ctx_handles, id, ctx) amdgpu_ctx_priority_override(ctx, priority); - mutex_unlock(&mgr->lock); + xa_unlock(&mgr->ctx_handles); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index fcd81242059e..fbac732f3e01 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -553,10 +553,11 @@ static int amdgpu_sdma_soft_reset(struct amdgpu_device *adev, u32 instance_id) int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id, bool caller_handles_kernel_queues) { - int ret = 0; struct amdgpu_sdma_instance *sdma_instance = &adev->sdma.instance[instance_id]; struct amdgpu_ring *gfx_ring = &sdma_instance->ring; struct amdgpu_ring *page_ring = &sdma_instance->page; + struct amdgpu_fence *gfx_fence, *page_fence; + int ret = 0; if (amdgpu_sriov_vf(adev)) return -EOPNOTSUPP; @@ -569,9 +570,14 @@ int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id, * the reset is in progress. */ drm_sched_wqueue_stop(&gfx_ring->sched); + gfx_fence = amdgpu_ring_find_guilty_fence(gfx_ring); + amdgpu_ring_reset_helper_begin(gfx_ring, gfx_fence); - if (adev->sdma.has_page_queue) + if (adev->sdma.has_page_queue) { drm_sched_wqueue_stop(&page_ring->sched); + page_fence = amdgpu_ring_find_guilty_fence(page_ring); + amdgpu_ring_reset_helper_begin(page_ring, page_fence); + } } if (sdma_instance->funcs->stop_kernel_queue) { @@ -600,14 +606,19 @@ exit: * to be submitted to the queues after the reset is complete. */ if (!ret) { - amdgpu_fence_driver_force_completion(gfx_ring, NULL); + ret = amdgpu_ring_reset_helper_end(gfx_ring, gfx_fence); + if (ret) + goto unlock; drm_sched_wqueue_start(&gfx_ring->sched); if (adev->sdma.has_page_queue) { - amdgpu_fence_driver_force_completion(page_ring, NULL); + ret = amdgpu_ring_reset_helper_end(page_ring, page_fence); + if (ret) + goto unlock; drm_sched_wqueue_start(&page_ring->sched); } } } +unlock: mutex_unlock(&sdma_instance->engine_reset_mutex); return ret; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h index 2bf365609775..4f4e56022c97 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.h @@ -85,34 +85,6 @@ struct amdgpu_sdma_instance { const struct amdgpu_sdma_funcs *funcs; }; -enum amdgpu_sdma_ras_memory_id { - AMDGPU_SDMA_MBANK_DATA_BUF0 = 1, - AMDGPU_SDMA_MBANK_DATA_BUF1 = 2, - AMDGPU_SDMA_MBANK_DATA_BUF2 = 3, - AMDGPU_SDMA_MBANK_DATA_BUF3 = 4, - AMDGPU_SDMA_MBANK_DATA_BUF4 = 5, - AMDGPU_SDMA_MBANK_DATA_BUF5 = 6, - AMDGPU_SDMA_MBANK_DATA_BUF6 = 7, - AMDGPU_SDMA_MBANK_DATA_BUF7 = 8, - AMDGPU_SDMA_MBANK_DATA_BUF8 = 9, - AMDGPU_SDMA_MBANK_DATA_BUF9 = 10, - AMDGPU_SDMA_MBANK_DATA_BUF10 = 11, - AMDGPU_SDMA_MBANK_DATA_BUF11 = 12, - AMDGPU_SDMA_MBANK_DATA_BUF12 = 13, - AMDGPU_SDMA_MBANK_DATA_BUF13 = 14, - AMDGPU_SDMA_MBANK_DATA_BUF14 = 15, - AMDGPU_SDMA_MBANK_DATA_BUF15 = 16, - AMDGPU_SDMA_UCODE_BUF = 17, - AMDGPU_SDMA_RB_CMD_BUF = 18, - AMDGPU_SDMA_IB_CMD_BUF = 19, - AMDGPU_SDMA_UTCL1_RD_FIFO = 20, - AMDGPU_SDMA_UTCL1_RDBST_FIFO = 21, - AMDGPU_SDMA_UTCL1_WR_FIFO = 22, - AMDGPU_SDMA_DATA_LUT_FIFO = 23, - AMDGPU_SDMA_SPLIT_DAT_BUF = 24, - AMDGPU_SDMA_MEMORY_BLOCK_LAST, -}; - struct amdgpu_sdma_ras { struct amdgpu_ras_block_object ras_block; }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h index 85724ec6aaf8..5324030a13f5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trace.h @@ -28,6 +28,8 @@ #include <linux/types.h> #include <linux/tracepoint.h> +#include "amdgpu_userq_fence.h" + #undef TRACE_SYSTEM #define TRACE_SYSTEM amdgpu #define TRACE_INCLUDE_FILE amdgpu_trace @@ -582,6 +584,154 @@ TRACE_EVENT(amdgpu_reset_reg_dumps, __entry->value) ); +DECLARE_EVENT_CLASS(amdgpu_userq_queue, + TP_PROTO(struct amdgpu_usermode_queue *queue), + TP_ARGS(queue), + TP_STRUCT__entry( + __field(void *, queue) + __field(u64, doorbell_index) + __field(int, queue_type) + __field(int, state) + __field(u32, xcp_id) + ), + TP_fast_assign( + __entry->queue = queue; + __entry->doorbell_index = queue->doorbell_index; + __entry->queue_type = queue->queue_type; + __entry->state = queue->state; + __entry->xcp_id = queue->xcp_id; + ), + TP_printk("queue=%p, doorbell=%llu, type=%d, state=%d, xcp_id=%u", + __entry->queue, __entry->doorbell_index, + __entry->queue_type, __entry->state, __entry->xcp_id) +); +DEFINE_EVENT(amdgpu_userq_queue, amdgpu_userq_create_start, + TP_PROTO(struct amdgpu_usermode_queue *queue), + TP_ARGS(queue)); +DEFINE_EVENT(amdgpu_userq_queue, amdgpu_userq_destroy_start, + TP_PROTO(struct amdgpu_usermode_queue *queue), + TP_ARGS(queue)); +DECLARE_EVENT_CLASS(amdgpu_userq_queue_result, + TP_PROTO(struct amdgpu_usermode_queue *queue, int result), + TP_ARGS(queue, result), + TP_STRUCT__entry( + __field(void *, queue) + __field(u64, doorbell_index) + __field(int, queue_type) + __field(int, state) + __field(u32, xcp_id) + __field(int, result) + ), + TP_fast_assign( + __entry->queue = queue; + __entry->doorbell_index = queue->doorbell_index; + __entry->queue_type = queue->queue_type; + __entry->state = queue->state; + __entry->xcp_id = queue->xcp_id; + __entry->result = result; + ), + TP_printk("queue=%p, doorbell=%llu, type=%d, state=%d, xcp_id=%u, result=%d", + __entry->queue, __entry->doorbell_index, + __entry->queue_type, __entry->state, + __entry->xcp_id, __entry->result) +); +DEFINE_EVENT(amdgpu_userq_queue_result, amdgpu_userq_create_end, + TP_PROTO(struct amdgpu_usermode_queue *queue, int result), + TP_ARGS(queue, result)); +DEFINE_EVENT(amdgpu_userq_queue_result, amdgpu_userq_destroy_end, + TP_PROTO(struct amdgpu_usermode_queue *queue, int result), + TP_ARGS(queue, result)); + +TRACE_EVENT(amdgpu_userq_emit_fence, + TP_PROTO(struct device *device, struct amdgpu_usermode_queue *queue, struct amdgpu_userq_fence *fence), + TP_ARGS(device, queue, fence), + TP_STRUCT__entry( + __field(u64, fence_context) + __field(u64, fence_seqno) + __string(dev, dev_name(device)) + __field(u64, doorbell_index) + __field(u64, client_id) + __field(u32, queue_type) + ), + TP_fast_assign( + __entry->fence_context = fence->base.context; + __entry->fence_seqno = fence->base.seqno; + __assign_str(dev); + __entry->doorbell_index = queue->doorbell_index; + __entry->client_id = queue->userq_mgr->file->client_id; + __entry->queue_type = queue->queue_type; + ), + TP_printk("dev=%s, client_id=%llu, type=%u, doorbell=%llu, fence=%llu:%llu", + __get_str(dev), __entry->client_id, __entry->queue_type, __entry->doorbell_index, + __entry->fence_context, + __entry->fence_seqno) +); + +TRACE_EVENT(amdgpu_userq_wait_deps, + TP_PROTO(struct device *device, struct amdgpu_usermode_queue *queue, struct amdgpu_userq_fence *dep), + TP_ARGS(device, queue, dep), + TP_STRUCT__entry( + __field(u64, context) + __field(u64, dep_context) + __field(u64, dep_seqno) + __string(dev, dev_name(device)) + __field(u64, doorbell_index) + __field(u64, client_id) + __field(u32, queue_type) + ), + TP_fast_assign( + __assign_str(dev); + __entry->doorbell_index = queue->doorbell_index; + __entry->queue_type = queue->queue_type; + __entry->client_id = queue->userq_mgr->file->client_id; + __entry->context = queue->fence_drv->context; + __entry->dep_context = dep->base.context; + __entry->dep_seqno = dep->base.seqno; + ), + TP_printk("dev=%s, client_id=%llu, type=%u, doorbell=%llu, context=%llu depends on fence=%llu:%llu", + __get_str(dev), __entry->client_id, __entry->queue_type, __entry->doorbell_index, __entry->context, + __entry->dep_context, + __entry->dep_seqno) +); + +TRACE_EVENT(amdgpu_userq_state_start, + TP_PROTO(struct amdgpu_usermode_queue *queue), + TP_ARGS(queue), + TP_STRUCT__entry( + __field(u64, doorbell_index) + __field(u64, client_id) + __field(u32, queue_type) + __field(u32, from) + ), + TP_fast_assign( + __entry->doorbell_index = queue->doorbell_index; + __entry->queue_type = queue->queue_type; + __entry->client_id = queue->userq_mgr->file->client_id; + __entry->from = queue->state; + ), + TP_printk("client_id=%llu, type=%u, doorbell=%llu, from=%d", + __entry->client_id, __entry->queue_type, __entry->doorbell_index, __entry->from) +); + +TRACE_EVENT(amdgpu_userq_state_changed, + TP_PROTO(struct amdgpu_usermode_queue *queue, enum amdgpu_userq_state new_state), + TP_ARGS(queue, new_state), + TP_STRUCT__entry( + __field(u64, doorbell_index) + __field(u64, client_id) + __field(u32, queue_type) + __field(u32, to) + ), + TP_fast_assign( + __entry->doorbell_index = queue->doorbell_index; + __entry->queue_type = queue->queue_type; + __entry->client_id = queue->userq_mgr->file->client_id; + __entry->to = new_state; + ), + TP_printk("client_id=%llu, type=%u, doorbell=%llu, to=%d", + __entry->client_id, __entry->queue_type, __entry->doorbell_index, __entry->to) +); + #undef AMDGPU_JOB_GET_TIMELINE_NAME #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 16c060badaee..b10b0878df37 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -208,9 +208,10 @@ static int amdgpu_ttm_map_buffer(struct amdgpu_ttm_buffer_entity *entity, void *cpu_addr; uint64_t flags; int r; + const u64 GTT_MAX_PAGES = (AMDGPU_GTT_MAX_TRANSFER_SIZE >> PAGE_SHIFT); BUG_ON(adev->mman.buffer_funcs->copy_max_bytes < - AMDGPU_GTT_MAX_TRANSFER_SIZE * 8); + GTT_MAX_PAGES * AMDGPU_GPU_PAGES_IN_CPU_PAGE * 8); if (WARN_ON(mem->mem_type == AMDGPU_PL_PREEMPT)) return -EINVAL; @@ -230,7 +231,7 @@ static int amdgpu_ttm_map_buffer(struct amdgpu_ttm_buffer_entity *entity, offset = mm_cur->start & ~PAGE_MASK; num_pages = PFN_UP(*size + offset); - num_pages = min_t(uint32_t, num_pages, AMDGPU_GTT_MAX_TRANSFER_SIZE); + num_pages = min_t(uint32_t, num_pages, GTT_MAX_PAGES); *size = min(*size, (uint64_t)num_pages * PAGE_SIZE - offset); @@ -2033,6 +2034,7 @@ static int amdgpu_ttm_buffer_entity_init(struct amdgpu_gtt_mgr *mgr, u32 num_gart_windows) { int i, r, num_pages; + const u64 GTT_MAX_PAGES = (AMDGPU_GTT_MAX_TRANSFER_SIZE >> PAGE_SHIFT); r = drm_sched_entity_init(&entity->base, prio, scheds, num_schedulers, NULL); if (r) @@ -2045,7 +2047,7 @@ static int amdgpu_ttm_buffer_entity_init(struct amdgpu_gtt_mgr *mgr, if (num_gart_windows == 0) return 0; - num_pages = num_gart_windows * AMDGPU_GTT_MAX_TRANSFER_SIZE; + num_pages = num_gart_windows * GTT_MAX_PAGES; r = amdgpu_gtt_mgr_alloc_entries(mgr, &entity->gart_node, num_pages, DRM_MM_INSERT_BEST); if (r) { @@ -2056,7 +2058,7 @@ static int amdgpu_ttm_buffer_entity_init(struct amdgpu_gtt_mgr *mgr, for (i = 0; i < num_gart_windows; i++) { entity->gart_window_offs[i] = amdgpu_gtt_node_to_byte_offset(&entity->gart_node) + - i * AMDGPU_GTT_MAX_TRANSFER_SIZE * PAGE_SIZE; + i * GTT_MAX_PAGES * PAGE_SIZE; } return 0; @@ -2118,18 +2120,23 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) /* Change the size here instead of the init above so only lpfn is affected */ amdgpu_ttm_disable_buffer_funcs(adev); #ifdef CONFIG_64BIT -#ifdef CONFIG_X86 - if (adev->gmc.xgmi.connected_to_cpu) - adev->mman.aper_base_kaddr = ioremap_cache(adev->gmc.aper_base, - adev->gmc.visible_vram_size); - - else if (adev->gmc.is_app_apu) + if (adev->gmc.xgmi.connected_to_cpu) { + void *kaddr = devm_memremap(adev->dev, adev->gmc.aper_base, + adev->gmc.visible_vram_size, + MEMREMAP_WB); + if (IS_ERR(kaddr)) + return PTR_ERR(kaddr); + adev->mman.aper_base_kaddr = (__force void __iomem *)kaddr; + } else if (adev->gmc.is_app_apu) { DRM_DEBUG_DRIVER( "No need to ioremap when real vram size is 0\n"); - else -#endif - adev->mman.aper_base_kaddr = ioremap_wc(adev->gmc.aper_base, - adev->gmc.visible_vram_size); + } else { + adev->mman.aper_base_kaddr = devm_ioremap_wc(adev->dev, + adev->gmc.aper_base, + adev->gmc.visible_vram_size); + if (!adev->mman.aper_base_kaddr) + return -ENOMEM; + } #endif amdgpu_ttm_init_vram_resv_regions(adev); @@ -2187,7 +2194,7 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) return r; } - /* Create a boorbell page for kernel usages */ + /* Create a doorbell page for kernel usages */ r = amdgpu_doorbell_create_kernel_doorbells(adev); if (r) { dev_err(adev->dev, "Failed to initialize kernel doorbells.\n"); @@ -2246,8 +2253,6 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) */ void amdgpu_ttm_fini(struct amdgpu_device *adev) { - int idx; - if (!adev->mman.initialized) return; @@ -2270,14 +2275,7 @@ void amdgpu_ttm_fini(struct amdgpu_device *adev) amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_FW_VRAM_USAGE); amdgpu_ttm_unmark_vram_reserved(adev, AMDGPU_RESV_DRV_VRAM_USAGE); - if (drm_dev_enter(adev_to_drm(adev), &idx)) { - - if (adev->mman.aper_base_kaddr) - iounmap(adev->mman.aper_base_kaddr); - adev->mman.aper_base_kaddr = NULL; - - drm_dev_exit(idx); - } + adev->mman.aper_base_kaddr = NULL; if (!adev->gmc.is_app_apu) amdgpu_vram_mgr_fini(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index 2d72fa217274..ff9e2e346609 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -39,7 +39,7 @@ #define AMDGPU_PL_MMIO_REMAP (TTM_PL_PRIV + 5) #define __AMDGPU_PL_NUM (TTM_PL_PRIV + 6) -#define AMDGPU_GTT_MAX_TRANSFER_SIZE 1024 +#define AMDGPU_GTT_MAX_TRANSFER_SIZE (1ULL << 22) extern const struct attribute_group amdgpu_vram_mgr_attr_group; extern const struct attribute_group amdgpu_gtt_mgr_attr_group; @@ -140,6 +140,7 @@ int amdgpu_gtt_mgr_init(struct amdgpu_device *adev, uint64_t gtt_size); void amdgpu_gtt_mgr_fini(struct amdgpu_device *adev); int amdgpu_preempt_mgr_init(struct amdgpu_device *adev); void amdgpu_preempt_mgr_fini(struct amdgpu_device *adev); +void amdgpu_preempt_mgr_sysfs_fini(struct amdgpu_device *adev); int amdgpu_vram_mgr_init(struct amdgpu_device *adev); void amdgpu_vram_mgr_fini(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c index b8ed931f8a40..2a5f5e6188bb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.c @@ -97,7 +97,6 @@ void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev, { struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status; struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - struct amdgpu_ras_eeprom_control *control = &con->eeprom_control; unsigned int error_query_mode; int ret = 0; unsigned long err_count; @@ -118,77 +117,66 @@ void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev, err_data->err_addr_len = adev->umc.max_ras_err_cnt_per_query; mutex_lock(&con->page_retirement_lock); - if (!amdgpu_ras_smu_eeprom_supported(adev)) { - ret = amdgpu_dpm_get_ecc_info(adev, (void *)&(con->umc_ecc)); - if (ret == -EOPNOTSUPP && - error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY) { - if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops && - adev->umc.ras->ras_block.hw_ops->query_ras_error_count) - adev->umc.ras->ras_block.hw_ops->query_ras_error_count(adev, - ras_error_status); - - if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops && - adev->umc.ras->ras_block.hw_ops->query_ras_error_address && - adev->umc.max_ras_err_cnt_per_query) { - kfree(err_data->err_addr); - err_data->err_addr = - kzalloc_objs(struct eeprom_table_record, - adev->umc.max_ras_err_cnt_per_query); - - /* still call query_ras_error_address to clear error status - * even NOMEM error is encountered - */ - if (!err_data->err_addr) - dev_warn(adev->dev, - "Failed to alloc memory for umc error address record!\n"); - else - err_data->err_addr_len = - adev->umc.max_ras_err_cnt_per_query; - - /* umc query_ras_error_address is also responsible for clearing - * error status - */ - adev->umc.ras->ras_block.hw_ops->query_ras_error_address(adev, - ras_error_status); - } - } else if (error_query_mode == AMDGPU_RAS_FIRMWARE_ERROR_QUERY || - (!ret && error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY)) { - if (adev->umc.ras && - adev->umc.ras->ecc_info_query_ras_error_count) - adev->umc.ras->ecc_info_query_ras_error_count(adev, - ras_error_status); - - if (adev->umc.ras && - adev->umc.ras->ecc_info_query_ras_error_address && - adev->umc.max_ras_err_cnt_per_query) { - kfree(err_data->err_addr); - err_data->err_addr = - kzalloc_objs(struct eeprom_table_record, - adev->umc.max_ras_err_cnt_per_query); - - /* still call query_ras_error_address to clear error status - * even NOMEM error is encountered - */ - if (!err_data->err_addr) - dev_warn(adev->dev, - "Failed to alloc memory for umc error address record!\n"); - else - err_data->err_addr_len = - adev->umc.max_ras_err_cnt_per_query; - - /* umc query_ras_error_address is also responsible for clearing - * error status - */ - adev->umc.ras->ecc_info_query_ras_error_address(adev, - ras_error_status); - } + ret = amdgpu_dpm_get_ecc_info(adev, (void *)&(con->umc_ecc)); + if (ret == -EOPNOTSUPP && + error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY) { + if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops && + adev->umc.ras->ras_block.hw_ops->query_ras_error_count) + adev->umc.ras->ras_block.hw_ops->query_ras_error_count(adev, + ras_error_status); + + if (adev->umc.ras && adev->umc.ras->ras_block.hw_ops && + adev->umc.ras->ras_block.hw_ops->query_ras_error_address && + adev->umc.max_ras_err_cnt_per_query) { + err_data->err_addr = + kzalloc_objs(struct eeprom_table_record, + adev->umc.max_ras_err_cnt_per_query); + + /* still call query_ras_error_address to clear error status + * even NOMEM error is encountered + */ + if (!err_data->err_addr) + dev_warn(adev->dev, + "Failed to alloc memory for umc error address record!\n"); + else + err_data->err_addr_len = + adev->umc.max_ras_err_cnt_per_query; + + /* umc query_ras_error_address is also responsible for clearing + * error status + */ + adev->umc.ras->ras_block.hw_ops->query_ras_error_address(adev, + ras_error_status); } - } else { - if (!amdgpu_ras_eeprom_update_record_num(control)) { - err_data->err_addr_cnt = err_data->de_count = - control->ras_num_recs - control->ras_num_recs_old; - amdgpu_ras_eeprom_read_idx(control, err_data->err_addr, - control->ras_num_recs_old, err_data->de_count); + } else if (error_query_mode == AMDGPU_RAS_FIRMWARE_ERROR_QUERY || + (!ret && error_query_mode == AMDGPU_RAS_DIRECT_ERROR_QUERY)) { + if (adev->umc.ras && + adev->umc.ras->ecc_info_query_ras_error_count) + adev->umc.ras->ecc_info_query_ras_error_count(adev, + ras_error_status); + + if (adev->umc.ras && + adev->umc.ras->ecc_info_query_ras_error_address && + adev->umc.max_ras_err_cnt_per_query) { + err_data->err_addr = + kcalloc(adev->umc.max_ras_err_cnt_per_query, + sizeof(struct eeprom_table_record), GFP_KERNEL); + + /* still call query_ras_error_address to clear error status + * even NOMEM error is encountered + */ + if (!err_data->err_addr) + dev_warn(adev->dev, + "Failed to alloc memory for umc error address record!\n"); + else + err_data->err_addr_len = + adev->umc.max_ras_err_cnt_per_query; + + /* umc query_ras_error_address is also responsible for clearing + * error status + */ + adev->umc.ras->ecc_info_query_ras_error_address(adev, + ras_error_status); } } @@ -276,7 +264,7 @@ int amdgpu_umc_pasid_poison_handler(struct amdgpu_device *adev, } amdgpu_ras_error_data_fini(&err_data); - } else if (amdgpu_uniras_enabled(adev)) { + } else { struct ras_ih_info ih_info = {0}; ih_info.block = block; @@ -285,17 +273,6 @@ int amdgpu_umc_pasid_poison_handler(struct amdgpu_device *adev, ih_info.pasid_fn = pasid_fn; ih_info.data = data; amdgpu_ras_mgr_handle_consumer_interrupt(adev, &ih_info); - } else { - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - int ret; - - ret = amdgpu_ras_put_poison_req(adev, - block, pasid, pasid_fn, data, reset); - if (!ret) { - atomic_inc(&con->page_retirement_req_cnt); - atomic_inc(&con->poison_consumption_count); - wake_up(&con->page_retirement_wq); - } } } else { if (adev->virt.ops && adev->virt.ops->ras_poison_handler) @@ -512,129 +489,3 @@ int amdgpu_umc_loop_channels(struct amdgpu_device *adev, return 0; } - -int amdgpu_umc_update_ecc_status(struct amdgpu_device *adev, - uint64_t status, uint64_t ipid, uint64_t addr) -{ - if (adev->umc.ras->update_ecc_status) - return adev->umc.ras->update_ecc_status(adev, - status, ipid, addr); - return 0; -} - -int amdgpu_umc_logs_ecc_err(struct amdgpu_device *adev, - struct radix_tree_root *ecc_tree, struct ras_ecc_err *ecc_err) -{ - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - struct ras_ecc_log_info *ecc_log; - int ret; - - ecc_log = &con->umc_ecc_log; - - mutex_lock(&ecc_log->lock); - ret = radix_tree_insert(ecc_tree, ecc_err->pa_pfn, ecc_err); - if (!ret) - radix_tree_tag_set(ecc_tree, - ecc_err->pa_pfn, UMC_ECC_NEW_DETECTED_TAG); - mutex_unlock(&ecc_log->lock); - - return ret; -} - -int amdgpu_umc_pages_in_a_row(struct amdgpu_device *adev, - struct ras_err_data *err_data, uint64_t pa_addr) -{ - struct ta_ras_query_address_output addr_out; - - /* reinit err_data */ - err_data->err_addr_cnt = 0; - err_data->err_addr_len = adev->umc.retire_unit; - - addr_out.pa.pa = pa_addr; - if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr) - return adev->umc.ras->convert_ras_err_addr(adev, err_data, NULL, - &addr_out, false); - else - return -EINVAL; -} - -int amdgpu_umc_lookup_bad_pages_in_a_row(struct amdgpu_device *adev, - uint64_t pa_addr, uint64_t *pfns, int len) -{ - int i, ret; - struct ras_err_data err_data; - - err_data.err_addr = kzalloc_objs(struct eeprom_table_record, - adev->umc.retire_unit); - if (!err_data.err_addr) { - dev_warn(adev->dev, "Failed to alloc memory in bad page lookup!\n"); - return 0; - } - - ret = amdgpu_umc_pages_in_a_row(adev, &err_data, pa_addr); - if (ret) - goto out; - - for (i = 0; i < adev->umc.retire_unit; i++) { - if (i >= len) - goto out; - - pfns[i] = err_data.err_addr[i].retired_page; - } - ret = i; - adev->umc.err_addr_cnt = err_data.err_addr_cnt; - -out: - kfree(err_data.err_addr); - return ret; -} - -int amdgpu_umc_mca_to_addr(struct amdgpu_device *adev, - uint64_t err_addr, uint32_t ch, uint32_t umc, - uint32_t node, uint32_t socket, - struct ta_ras_query_address_output *addr_out, bool dump_addr) -{ - struct ta_ras_query_address_input addr_in; - int ret; - - memset(&addr_in, 0, sizeof(addr_in)); - addr_in.ma.err_addr = err_addr; - addr_in.ma.ch_inst = ch; - addr_in.ma.umc_inst = umc; - addr_in.ma.node_inst = node; - addr_in.ma.socket_id = socket; - - if (adev->umc.ras && adev->umc.ras->convert_ras_err_addr) { - ret = adev->umc.ras->convert_ras_err_addr(adev, NULL, &addr_in, - addr_out, dump_addr); - if (ret) - return ret; - } else { - return 0; - } - - return 0; -} - -int amdgpu_umc_pa2mca(struct amdgpu_device *adev, - uint64_t pa, uint64_t *mca, enum amdgpu_memory_partition nps) -{ - struct ta_ras_query_address_input addr_in; - struct ta_ras_query_address_output addr_out; - int ret; - - /* nps: the pa belongs to */ - addr_in.pa.pa = pa | ((uint64_t)nps << 58); - addr_in.addr_type = TA_RAS_PA_TO_MCA; - ret = psp_ras_query_address(&adev->psp, &addr_in, &addr_out); - if (ret) { - dev_warn(adev->dev, "Failed to query RAS MCA address for 0x%llx", - pa); - - return ret; - } - - *mca = addr_out.ma.err_addr; - - return 0; -} diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h index 8494a55ebf76..cf06d5f856f9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_umc.h @@ -103,18 +103,7 @@ struct amdgpu_umc_ras { void *ras_error_status); bool (*check_ecc_err_status)(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, void *ras_error_status); - int (*update_ecc_status)(struct amdgpu_device *adev, - uint64_t status, uint64_t ipid, uint64_t addr); - int (*convert_ras_err_addr)(struct amdgpu_device *adev, - struct ras_err_data *err_data, - struct ta_ras_query_address_input *addr_in, - struct ta_ras_query_address_output *addr_out, - bool dump_addr); - uint32_t (*get_die_id_from_pa)(struct amdgpu_device *adev, - uint64_t mca_addr, uint64_t retired_page); void (*get_retire_flip_bits)(struct amdgpu_device *adev); - void (*mca_ipid_parse)(struct amdgpu_device *adev, uint64_t ipid, - uint32_t *did, uint32_t *ch, uint32_t *umc_inst, uint32_t *sid); }; struct amdgpu_umc_funcs { @@ -179,21 +168,6 @@ int amdgpu_umc_page_retirement_mca(struct amdgpu_device *adev, int amdgpu_umc_loop_channels(struct amdgpu_device *adev, umc_func func, void *data); -int amdgpu_umc_update_ecc_status(struct amdgpu_device *adev, - uint64_t status, uint64_t ipid, uint64_t addr); -int amdgpu_umc_logs_ecc_err(struct amdgpu_device *adev, - struct radix_tree_root *ecc_tree, struct ras_ecc_err *ecc_err); - void amdgpu_umc_handle_bad_pages(struct amdgpu_device *adev, void *ras_error_status); -int amdgpu_umc_pages_in_a_row(struct amdgpu_device *adev, - struct ras_err_data *err_data, uint64_t pa_addr); -int amdgpu_umc_lookup_bad_pages_in_a_row(struct amdgpu_device *adev, - uint64_t pa_addr, uint64_t *pfns, int len); -int amdgpu_umc_mca_to_addr(struct amdgpu_device *adev, - uint64_t err_addr, uint32_t ch, uint32_t umc, - uint32_t node, uint32_t socket, - struct ta_ras_query_address_output *addr_out, bool dump_addr); -int amdgpu_umc_pa2mca(struct amdgpu_device *adev, - uint64_t pa, uint64_t *mca, enum amdgpu_memory_partition nps); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 91554e7c092c..82c8809d1d9c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -33,6 +33,7 @@ #include "amdgpu_userq.h" #include "amdgpu_hmm.h" #include "amdgpu_userq_fence.h" +#include "amdgpu_trace.h" u32 amdgpu_userq_get_supported_ip_mask(struct amdgpu_device *adev) { @@ -88,14 +89,7 @@ static void amdgpu_userq_mgr_reset_work(struct work_struct *work) container_of(work, struct amdgpu_userq_mgr, reset_work); struct amdgpu_device *adev = uq_mgr->adev; - const int queue_types[] = { - AMDGPU_RING_TYPE_COMPUTE, - AMDGPU_RING_TYPE_GFX, - AMDGPU_RING_TYPE_SDMA - }; - const int num_queue_types = ARRAY_SIZE(queue_types); - bool gpu_reset = false; - int i, r; + struct amdgpu_reset_context reset_context; if (unlikely(adev->debug_disable_gpu_ring_reset)) { dev_err(adev->dev, "userq reset disabled by debug mask\n"); @@ -109,42 +103,15 @@ static void amdgpu_userq_mgr_reset_work(struct work_struct *work) if (!amdgpu_gpu_recovery) return; - /* - * Iterate through all queue types to detect and reset problematic queues - * Process each queue type in the defined order - */ - for (i = 0; i < num_queue_types; i++) { - int ring_type = queue_types[i]; - const struct amdgpu_userq_funcs *funcs = - adev->userq_funcs[ring_type]; - - if (!amdgpu_userq_is_reset_type_supported(adev, ring_type, - AMDGPU_RESET_TYPE_PER_QUEUE)) - continue; + memset(&reset_context, 0, sizeof(reset_context)); - if (atomic_read(&uq_mgr->userq_count[ring_type]) > 0 && - funcs && funcs->detect_and_reset) { - r = funcs->detect_and_reset(adev, ring_type); - if (r) { - gpu_reset = true; - break; - } - } - } + reset_context.method = AMD_RESET_METHOD_NONE; + reset_context.reset_req_dev = adev; + reset_context.src = AMDGPU_RESET_SRC_USERQ; + set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags); + /*set_bit(AMDGPU_SKIP_COREDUMP, &reset_context.flags);*/ - if (gpu_reset) { - struct amdgpu_reset_context reset_context; - - memset(&reset_context, 0, sizeof(reset_context)); - - reset_context.method = AMD_RESET_METHOD_NONE; - reset_context.reset_req_dev = adev; - reset_context.src = AMDGPU_RESET_SRC_USERQ; - set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags); - /*set_bit(AMDGPU_SKIP_COREDUMP, &reset_context.flags);*/ - - amdgpu_device_gpu_recover(adev, NULL, &reset_context); - } + amdgpu_device_gpu_recover(adev, NULL, &reset_context); } static void amdgpu_userq_hang_detect_work(struct work_struct *work) @@ -152,12 +119,45 @@ static void amdgpu_userq_hang_detect_work(struct work_struct *work) struct amdgpu_usermode_queue *queue = container_of(work, struct amdgpu_usermode_queue, hang_detect_work.work); + struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; + struct amdgpu_device *adev = uq_mgr->adev; + const struct amdgpu_userq_funcs *userq_funcs = + adev->userq_funcs[queue->queue_type]; + bool gpu_reset = false; + + if (unlikely(adev->debug_disable_gpu_ring_reset)) { + dev_err(adev->dev, "userq reset disabled by debug mask\n"); + return; + } + + /* + * If GPU recovery feature is disabled system-wide, + * skip all reset detection logic + */ + if (!amdgpu_gpu_recovery) + return; + + if (amdgpu_userq_is_reset_type_supported(adev, queue->queue_type, + AMDGPU_RESET_TYPE_PER_QUEUE)) { + int r; + + if (queue->queue_type == AMDGPU_HW_IP_COMPUTE) + r = amdgpu_gfx_reset_mes_compute(adev, NULL, NULL, + queue, NULL, NULL); + else + r = userq_funcs->reset(queue); + if (r) + gpu_reset = true; + } else { + gpu_reset = true; + } /* * Don't schedule the work here! Scheduling or queue work from one reset * handler to another is illegal if you don't take extra precautions! */ - amdgpu_userq_mgr_reset_work(&queue->userq_mgr->reset_work); + if (gpu_reset) + amdgpu_userq_mgr_reset_work(&queue->userq_mgr->reset_work); } /* @@ -293,11 +293,15 @@ static int amdgpu_userq_preempt_helper(struct amdgpu_usermode_queue *queue) int r; if (queue->state == AMDGPU_USERQ_STATE_MAPPED) { + trace_amdgpu_userq_state_start(queue); + r = userq_funcs->preempt(queue); if (r) { + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG); queue->state = AMDGPU_USERQ_STATE_HUNG; return r; } else { + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_PREEMPTED); queue->state = AMDGPU_USERQ_STATE_PREEMPTED; } } @@ -313,10 +317,14 @@ static int amdgpu_userq_restore_helper(struct amdgpu_usermode_queue *queue) int r = 0; if (queue->state == AMDGPU_USERQ_STATE_PREEMPTED) { + trace_amdgpu_userq_state_start(queue); + r = userq_funcs->restore(queue); if (r) { + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG); queue->state = AMDGPU_USERQ_STATE_HUNG; } else { + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED); queue->state = AMDGPU_USERQ_STATE_MAPPED; } } @@ -334,12 +342,15 @@ static int amdgpu_userq_unmap_helper(struct amdgpu_usermode_queue *queue) if ((queue->state == AMDGPU_USERQ_STATE_MAPPED) || (queue->state == AMDGPU_USERQ_STATE_PREEMPTED)) { + trace_amdgpu_userq_state_start(queue); r = userq_funcs->unmap(queue); if (r) { + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG); queue->state = AMDGPU_USERQ_STATE_HUNG; return r; } else { + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_UNMAPPED); queue->state = AMDGPU_USERQ_STATE_UNMAPPED; } } @@ -356,11 +367,15 @@ static int amdgpu_userq_map_helper(struct amdgpu_usermode_queue *queue) int r; if (queue->state == AMDGPU_USERQ_STATE_UNMAPPED) { + trace_amdgpu_userq_state_start(queue); + r = userq_funcs->map(queue); if (r) { + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG); queue->state = AMDGPU_USERQ_STATE_HUNG; return r; } else { + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED); queue->state = AMDGPU_USERQ_STATE_MAPPED; } } @@ -507,6 +522,8 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que const struct amdgpu_userq_funcs *uq_funcs = adev->userq_funcs[queue->queue_type]; int r = 0; + trace_amdgpu_userq_destroy_start(queue); + cancel_delayed_work_sync(&uq_mgr->resume_work); /* Cancel any pending hang detection work and cleanup */ @@ -532,6 +549,7 @@ amdgpu_userq_destroy(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_que amdgpu_bo_unreserve(queue->db_obj.obj); amdgpu_bo_unref(&queue->db_obj.obj); + trace_amdgpu_userq_destroy_end(queue, r); kfree(queue); pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); @@ -629,6 +647,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) queue->queue_type = args->in.ip_type; queue->vm = &fpriv->vm; queue->priority = priority; + queue->xcp_id = (fpriv->xcp_id != AMDGPU_XCP_NO_PARTITION) ? + fpriv->xcp_id : 0; queue->userq_mgr = uq_mgr; INIT_DELAYED_WORK(&queue->hang_detect_work, amdgpu_userq_hang_detect_work); @@ -671,6 +691,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) } queue->doorbell_index = index; + queue->doorbell_offset = (u32)args->in.doorbell_offset; + trace_amdgpu_userq_create_start(queue); r = uq_funcs->mqd_create(queue, &args->in); if (r) { drm_file_err(uq_mgr->file, "Failed to create Queue\n"); @@ -680,8 +702,8 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) /* Update VM owner at userq submit-time for page-fault attribution. */ amdgpu_vm_set_task_info(&fpriv->vm); - r = xa_err(xa_store_irq(&adev->userq_doorbell_xa, index, queue, - GFP_KERNEL)); + r = xa_insert_irq(&adev->userq_doorbell_xa, index, queue, + GFP_KERNEL); if (r) goto clean_mqd; @@ -694,6 +716,7 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) r = amdgpu_userq_map_helper(queue); if (r) { drm_file_err(uq_mgr->file, "Failed to map Queue\n"); + trace_amdgpu_userq_create_end(queue, r); mutex_unlock(&uq_mgr->userq_mutex); goto erase_doorbell; } @@ -710,11 +733,13 @@ amdgpu_userq_create(struct drm_file *filp, union drm_amdgpu_userq *args) * This drops the last reference which should take care of * all cleanup. */ + trace_amdgpu_userq_create_end(queue, r); amdgpu_userq_put(queue); return r; } amdgpu_debugfs_userq_init(filp, queue, qid); + trace_amdgpu_userq_create_end(queue, 0); args->out.queue_id = qid; return 0; @@ -730,6 +755,7 @@ clean_doorbell_bo: free_fence_drv: amdgpu_userq_fence_driver_free(queue); free_queue: + trace_amdgpu_userq_create_end(queue, r); kfree(queue); err_pm_runtime: pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); @@ -862,16 +888,10 @@ int amdgpu_userq_ioctl(struct drm_device *dev, void *data, static int amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) { - struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr); - struct amdgpu_vm *vm = &fpriv->vm; struct amdgpu_usermode_queue *queue; unsigned long queue_id; int ret = 0, r; - - if (amdgpu_bo_reserve(vm->root.bo, false)) - return false; - mutex_lock(&uq_mgr->userq_mutex); /* Resume all the queues for this process */ xa_for_each(&uq_mgr->userq_xa, queue_id, queue) { @@ -879,6 +899,7 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) if (!amdgpu_userq_buffer_vas_mapped(queue)) { drm_file_err(uq_mgr->file, "trying restore queue without va mapping\n"); + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_INVALID_VA); queue->state = AMDGPU_USERQ_STATE_INVALID_VA; continue; } @@ -886,10 +907,8 @@ amdgpu_userq_restore_all(struct amdgpu_userq_mgr *uq_mgr) r = amdgpu_userq_map_helper(queue); if (r) ret = r; - } mutex_unlock(&uq_mgr->userq_mutex); - amdgpu_bo_unreserve(vm->root.bo); if (ret) drm_file_err(uq_mgr->file, @@ -923,7 +942,8 @@ amdgpu_userq_bo_validate(struct amdgpu_device *adev, struct drm_exec *exec, spin_unlock(&vm->individual_lock); bo = bo_va->base.bo; - ret = drm_exec_prepare_obj(exec, &bo->tbo.base, 2); + ret = drm_exec_prepare_obj(exec, &bo->tbo.base, + TTM_NUM_MOVE_FENCES + 1); if (unlikely(ret)) return ret; @@ -946,7 +966,7 @@ amdgpu_userq_bo_validate(struct amdgpu_device *adev, struct drm_exec *exec, /* Make sure the whole VM is ready to be used */ static int -amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr) +amdgpu_userq_vm_validate_and_restore_queue(struct amdgpu_userq_mgr *uq_mgr) { struct amdgpu_fpriv *fpriv = uq_mgr_to_fpriv(uq_mgr); bool invalidated = false, new_addition = false; @@ -1072,8 +1092,12 @@ retry_lock: dma_fence_wait(vm->last_update, false); ret = amdgpu_evf_mgr_rearm(&fpriv->evf_mgr, &exec); - if (ret) + if (ret) { drm_file_err(uq_mgr->file, "Failed to replace eviction fence\n"); + goto unlock_all; + } + + ret = amdgpu_userq_restore_all(uq_mgr); unlock_all: drm_exec_fini(&exec); @@ -1099,18 +1123,34 @@ static void amdgpu_userq_restore_worker(struct work_struct *work) if (!dma_fence_is_signaled(ev_fence)) goto put_fence; - ret = amdgpu_userq_vm_validate(uq_mgr); + ret = amdgpu_userq_vm_validate_and_restore_queue(uq_mgr); if (ret) { drm_file_err(uq_mgr->file, "Failed to validate BOs to restore ret=%d\n", ret); goto put_fence; } - amdgpu_userq_restore_all(uq_mgr); - put_fence: dma_fence_put(ev_fence); } +void amdgpu_userq_process_reset_irq(struct amdgpu_device *adev, + u32 pasid, u32 doorbell_offset) +{ + struct xarray *xa = &adev->userq_doorbell_xa; + struct amdgpu_usermode_queue *queue; + unsigned long flags, idx; + + xa_lock_irqsave(xa, flags); + xa_for_each(xa, idx, queue) { + if (queue->vm && queue->vm->pasid == pasid && + queue->doorbell_offset == doorbell_offset) { + amdgpu_userq_start_hang_detect_work(queue); + break; + } + } + xa_unlock_irqrestore(xa, flags); +} + static int amdgpu_userq_evict_all(struct amdgpu_userq_mgr *uq_mgr) { @@ -1166,6 +1206,7 @@ int amdgpu_userq_mgr_init(struct amdgpu_userq_mgr *userq_mgr, struct drm_file *f xa_init_flags(&userq_mgr->userq_xa, XA_FLAGS_ALLOC); userq_mgr->adev = adev; userq_mgr->file = file_priv; + mutex_init(&userq_mgr->proc_ctx_lock); INIT_DELAYED_WORK(&userq_mgr->resume_work, amdgpu_userq_restore_worker); INIT_WORK(&userq_mgr->reset_work, amdgpu_userq_mgr_reset_work); @@ -1219,6 +1260,11 @@ void amdgpu_userq_mgr_fini(struct amdgpu_userq_mgr *userq_mgr) */ cancel_work_sync(&userq_mgr->reset_work); + amdgpu_bo_free_kernel(&userq_mgr->proc_ctx_obj.obj, + &userq_mgr->proc_ctx_obj.gpu_addr, + &userq_mgr->proc_ctx_obj.cpu_ptr); + + mutex_destroy(&userq_mgr->proc_ctx_lock); mutex_destroy(&userq_mgr->userq_mutex); } @@ -1370,12 +1416,14 @@ void amdgpu_userq_pre_reset(struct amdgpu_device *adev) if (queue->state != AMDGPU_USERQ_STATE_MAPPED) continue; + trace_amdgpu_userq_state_start(queue); userq_funcs = adev->userq_funcs[queue->queue_type]; userq_funcs->unmap(queue); /* just mark all queues as hung at this point. * if unmap succeeds, we could map again * in amdgpu_userq_post_reset() if vram is not lost */ + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_HUNG); queue->state = AMDGPU_USERQ_STATE_HUNG; amdgpu_userq_fence_driver_force_completion(queue); } @@ -1394,6 +1442,8 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost) xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) { if (queue->state == AMDGPU_USERQ_STATE_HUNG && !vram_lost) { + trace_amdgpu_userq_state_start(queue); + userq_funcs = adev->userq_funcs[queue->queue_type]; /* Re-map queue */ r = userq_funcs->map(queue); @@ -1401,6 +1451,7 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost) dev_err(adev->dev, "Failed to remap queue %ld\n", queue_id); continue; } + trace_amdgpu_userq_state_changed(queue, AMDGPU_USERQ_STATE_MAPPED); queue->state = AMDGPU_USERQ_STATE_MAPPED; } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h index d1751febaefe..61e5f8a06eb2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.h @@ -53,6 +53,7 @@ struct amdgpu_usermode_queue { enum amdgpu_userq_state state; uint64_t doorbell_handle; uint64_t doorbell_index; + u32 doorbell_offset; uint64_t flags; struct amdgpu_mqd_prop *userq_prop; struct amdgpu_userq_mgr *userq_mgr; @@ -111,8 +112,7 @@ struct amdgpu_userq_funcs { int (*map)(struct amdgpu_usermode_queue *queue); int (*preempt)(struct amdgpu_usermode_queue *queue); int (*restore)(struct amdgpu_usermode_queue *queue); - int (*detect_and_reset)(struct amdgpu_device *adev, - int queue_type); + int (*reset)(struct amdgpu_usermode_queue *queue); }; /* Usermode queues for gfx */ @@ -127,6 +127,8 @@ struct amdgpu_userq_mgr { struct amdgpu_device *adev; struct delayed_work resume_work; struct drm_file *file; + struct mutex proc_ctx_lock; + struct amdgpu_userq_obj proc_ctx_obj; /** * @reset_work: @@ -177,6 +179,16 @@ int amdgpu_userq_post_reset(struct amdgpu_device *adev, bool vram_lost); void amdgpu_userq_start_hang_detect_work(struct amdgpu_usermode_queue *queue); void amdgpu_userq_process_fence_irq(struct amdgpu_device *adev, u32 doorbell); +/* + * CP packs the per-process doorbell_id of the queue in + * CTXID0[9:0] on priv-fault (same encoding KFD uses via + * KFD_CTXID0_DOORBELL_ID_MASK) + */ +#define AMDGPU_CTXID0_DOORBELL_ID_MASK 0x3ff + +void amdgpu_userq_process_reset_irq(struct amdgpu_device *adev, + u32 pasid, u32 doorbell_offset); + int amdgpu_userq_input_va_validate(struct amdgpu_device *adev, struct amdgpu_usermode_queue *queue, u64 addr, u64 expected_size, u64 *va_out); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c index f74ad378e407..7e80442ec3e5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq_fence.c @@ -30,7 +30,7 @@ #include <drm/drm_syncobj.h> #include "amdgpu.h" -#include "amdgpu_userq_fence.h" +#include "amdgpu_trace.h" #define AMDGPU_USERQ_MAX_HANDLES (1U << 16) @@ -528,6 +528,8 @@ int amdgpu_userq_signal_ioctl(struct drm_device *dev, void *data, /* Create the new fence */ amdgpu_userq_fence_init(queue, fence, wptr); + trace_amdgpu_userq_emit_fence(dev->dev, queue, fence); + mutex_unlock(&userq_mgr->userq_mutex); /* @@ -701,7 +703,7 @@ amdgpu_userq_wait_add_fence(struct drm_amdgpu_userq_wait *wait_info, } static int -amdgpu_userq_wait_return_fence_info(struct drm_file *filp, +amdgpu_userq_wait_return_fence_info(struct drm_device *dev, struct drm_file *filp, struct drm_amdgpu_userq_wait *wait_info, u32 *syncobj_handles, u64 *timeline_points, u32 *timeline_handles, @@ -869,6 +871,8 @@ amdgpu_userq_wait_return_fence_info(struct drm_file *filp, amdgpu_userq_fence_driver_get(fence_drv); + trace_amdgpu_userq_wait_deps(dev->dev, waitq, userq_fence); + /* Store drm syncobj's gpu va address and value */ fence_info[cnt].va = fence_drv->va; fence_info[cnt].value = fences[i]->seqno; @@ -969,7 +973,7 @@ int amdgpu_userq_wait_ioctl(struct drm_device *dev, void *data, gobj_write, gobj_read); } else { - r = amdgpu_userq_wait_return_fence_info(filp, wait_info, + r = amdgpu_userq_wait_return_fence_info(dev, filp, wait_info, syncobj_handles, timeline_points, timeline_handles, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 480bf88def46..23383ac5323f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -655,6 +655,14 @@ static int amdgpu_uvd_cs_msg_decode(struct amdgpu_device *adev, uint32_t *msg, unsigned int image_size, tmp, min_dpb_size, num_dpb_buffer; unsigned int min_ctx_size = ~0; + /* Reject invalid dimensions to prevent division by zero */ + if (width < 16 || height < 16) { + dev_WARN_ONCE(adev->dev, 1, + "Invalid UVD decoding dimensions (%dx%d)!\n", + width, height); + return -EINVAL; + } + image_size = width * height; image_size += image_size / 2; image_size = ALIGN(image_size, 1024); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c index efdebd9c0a1f..eef3c9853a5c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vce.c @@ -877,9 +877,20 @@ int amdgpu_vce_ring_parse_cs(struct amdgpu_cs_parser *p, goto out; } - *size = amdgpu_ib_get_value(ib, idx + 8) * - amdgpu_ib_get_value(ib, idx + 10) * - 8 * 3 / 2; + uint32_t width, height; + width = amdgpu_ib_get_value(ib, idx + 8); + height = amdgpu_ib_get_value(ib, idx + 10); + + if (width == 0 || height == 0 || + width > 4096 || height > 2304) { + DRM_ERROR("invalid VCE image size: %ux%u\n", + width, height); + r = -EINVAL; + goto out; + } + + *size = width * height * 8 * 3 / 2; + break; case 0x04000001: /* config extension */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index 616967519869..fe504f1a3fc8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -506,9 +506,8 @@ void amdgpu_vcn_ring_begin_use(struct amdgpu_ring *ring) struct amdgpu_device *adev = ring->adev; struct amdgpu_vcn_inst *vcn_inst = &adev->vcn.inst[ring->me]; - atomic_inc(&vcn_inst->total_submission_cnt); - - cancel_delayed_work_sync(&vcn_inst->idle_work); + if (!atomic_fetch_inc(&vcn_inst->total_submission_cnt)) + cancel_delayed_work_sync(&vcn_inst->idle_work); mutex_lock(&vcn_inst->vcn_pg_lock); vcn_inst->set_pg_state(vcn_inst, AMD_PG_STATE_UNGATE); @@ -550,10 +549,9 @@ void amdgpu_vcn_ring_end_use(struct amdgpu_ring *ring) !adev->vcn.inst[ring->me].using_unified_queue) atomic_dec(&ring->adev->vcn.inst[ring->me].dpg_enc_submission_cnt); - atomic_dec(&ring->adev->vcn.inst[ring->me].total_submission_cnt); - - schedule_delayed_work(&ring->adev->vcn.inst[ring->me].idle_work, - VCN_IDLE_TIMEOUT); + if (atomic_dec_and_test(&ring->adev->vcn.inst[ring->me].total_submission_cnt)) + schedule_delayed_work(&ring->adev->vcn.inst[ring->me].idle_work, + VCN_IDLE_TIMEOUT); } int amdgpu_vcn_dec_ring_test_ring(struct amdgpu_ring *ring) @@ -1485,6 +1483,37 @@ int vcn_set_powergating_state(struct amdgpu_ip_block *ip_block, return ret; } +static struct amdgpu_fence * +amdgpu_vcn_ring_reset_begin_helper(struct amdgpu_ring *ring, + struct amdgpu_ring *guilty_ring, + struct amdgpu_fence *timedout_fence) +{ + struct amdgpu_fence *fence; + + drm_sched_wqueue_stop(&ring->sched); + if (ring == guilty_ring) + fence = timedout_fence; + else + fence = amdgpu_ring_find_guilty_fence(ring); + amdgpu_ring_reset_helper_begin(ring, fence); + + return fence; +} + +static int +amdgpu_vcn_ring_reset_end_helper(struct amdgpu_ring *ring, + struct amdgpu_fence *fence) +{ + int r; + + r = amdgpu_ring_reset_helper_end(ring, fence); + if (r) + return r; + + drm_sched_wqueue_start(&ring->sched); + return 0; +} + /** * amdgpu_vcn_ring_reset - Reset a VCN ring * @ring: ring to reset @@ -1502,48 +1531,33 @@ int amdgpu_vcn_ring_reset(struct amdgpu_ring *ring, { struct amdgpu_device *adev = ring->adev; struct amdgpu_vcn_inst *vinst = &adev->vcn.inst[ring->me]; + struct amdgpu_fence *dec_fence; + struct amdgpu_fence *enc_fence[AMDGPU_VCN_MAX_ENC_RINGS]; int r, i; if (adev->vcn.inst[ring->me].using_unified_queue) return -EINVAL; mutex_lock(&vinst->engine_reset_mutex); - /* Stop the scheduler's work queue for the dec and enc rings if they are running. - * This ensures that no new tasks are submitted to the queues while - * the reset is in progress. - */ - drm_sched_wqueue_stop(&vinst->ring_dec.sched); + dec_fence = amdgpu_vcn_ring_reset_begin_helper(&vinst->ring_dec, ring, + timedout_fence); for (i = 0; i < vinst->num_enc_rings; i++) - drm_sched_wqueue_stop(&vinst->ring_enc[i].sched); + enc_fence[i] = amdgpu_vcn_ring_reset_begin_helper(&vinst->ring_enc[i], ring, + timedout_fence); /* Perform the VCN reset for the specified instance */ r = vinst->reset(vinst); if (r) goto unlock; - r = amdgpu_ring_test_ring(&vinst->ring_dec); + + r = amdgpu_vcn_ring_reset_end_helper(&vinst->ring_dec, dec_fence); if (r) goto unlock; for (i = 0; i < vinst->num_enc_rings; i++) { - r = amdgpu_ring_test_ring(&vinst->ring_enc[i]); + r = amdgpu_vcn_ring_reset_end_helper(&vinst->ring_enc[i], enc_fence[i]); if (r) goto unlock; } - amdgpu_fence_driver_force_completion(&vinst->ring_dec, - (&vinst->ring_dec == ring) ? - &timedout_fence->base : NULL); - for (i = 0; i < vinst->num_enc_rings; i++) - amdgpu_fence_driver_force_completion(&vinst->ring_enc[i], - (&vinst->ring_enc[i] == ring) ? - &timedout_fence->base : NULL); - - /* Restart the scheduler's work queue for the dec and enc rings - * if they were stopped by this function. This allows new tasks - * to be submitted to the queues after the reset is complete. - */ - drm_sched_wqueue_start(&vinst->ring_dec.sched); - for (i = 0; i < vinst->num_enc_rings; i++) - drm_sched_wqueue_start(&vinst->ring_enc[i].sched); - unlock: mutex_unlock(&vinst->engine_reset_mutex); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h index 82624b44e661..bea95307fd42 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.h @@ -368,6 +368,9 @@ struct amdgpu_vcn { struct mutex workload_profile_mutex; u32 reg_count; const struct amdgpu_hwip_reg_entry *reg_list; + + bool disable_uq; + bool disable_kq; }; struct amdgpu_fw_shared_rb_ptrs_struct { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index fee4c94c2585..f317f888b59f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -142,7 +142,7 @@ static void amdgpu_vm_assert_locked(struct amdgpu_vm *vm) static void amdgpu_vm_bo_status_init(struct amdgpu_vm_bo_status *lists) { INIT_LIST_HEAD(&lists->evicted); - INIT_LIST_HEAD(&lists->moved); + INIT_LIST_HEAD(&lists->needs_update); INIT_LIST_HEAD(&lists->idle); } @@ -211,14 +211,14 @@ static void amdgpu_vm_bo_evicted(struct amdgpu_vm_bo_base *vm_bo) amdgpu_vm_bo_unlock_lists(vm_bo); } /** - * amdgpu_vm_bo_moved - vm_bo is moved + * amdgpu_vm_bo_needs_update - vm_bo needs pagetable update * - * @vm_bo: vm_bo which is moved + * @vm_bo: vm_bo which is out of date * - * State for vm_bo objects meaning the underlying BO was moved but the new - * location not yet reflected in the page tables. + * State for vm_bo objects meaning the underlying BO had mapping changes (move, PRT bind/unbind) + * but the new location is not yet reflected in the page tables. */ -static void amdgpu_vm_bo_moved(struct amdgpu_vm_bo_base *vm_bo) +static void amdgpu_vm_bo_needs_update(struct amdgpu_vm_bo_base *vm_bo) { struct amdgpu_vm_bo_status *lists; struct amdgpu_bo *bo = vm_bo->bo; @@ -232,8 +232,7 @@ static void amdgpu_vm_bo_moved(struct amdgpu_vm_bo_base *vm_bo) vm_bo->moved = false; list_move(&vm_bo->vm_status, &lists->idle); } else { - vm_bo->moved = true; - list_move(&vm_bo->vm_status, &lists->moved); + list_move(&vm_bo->vm_status, &lists->needs_update); } amdgpu_vm_bo_unlock_lists(vm_bo); } @@ -274,14 +273,14 @@ static void amdgpu_vm_bo_reset_state_machine(struct amdgpu_vm *vm) */ amdgpu_vm_assert_locked(vm); list_for_each_entry_safe(vm_bo, tmp, &vm->kernel.idle, vm_status) - amdgpu_vm_bo_moved(vm_bo); + amdgpu_vm_bo_needs_update(vm_bo); list_for_each_entry_safe(vm_bo, tmp, &vm->always_valid.idle, vm_status) - amdgpu_vm_bo_moved(vm_bo); + amdgpu_vm_bo_needs_update(vm_bo); spin_lock(&vm->individual_lock); 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); + list_move(&vm_bo->vm_status, &vm->individual.needs_update); } spin_unlock(&vm->individual_lock); } @@ -436,7 +435,7 @@ void amdgpu_vm_bo_base_init(struct amdgpu_vm_bo_base *base, */ if (bo->preferred_domains & amdgpu_mem_type_to_domain(bo->tbo.resource->mem_type)) - amdgpu_vm_bo_moved(base); + amdgpu_vm_bo_needs_update(base); else amdgpu_vm_bo_evicted(base); } @@ -608,7 +607,8 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, return r; vm->update_funcs->map_table(to_amdgpu_bo_vm(bo_base->bo)); - amdgpu_vm_bo_moved(bo_base); + bo_base->moved = true; + amdgpu_vm_bo_needs_update(bo_base); } /* @@ -625,7 +625,8 @@ int amdgpu_vm_validate(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (r) return r; - amdgpu_vm_bo_moved(bo_base); + bo_base->moved = true; + amdgpu_vm_bo_needs_update(bo_base); } if (!ticket) @@ -645,7 +646,8 @@ restart: if (r) return r; - amdgpu_vm_bo_moved(bo_base); + bo_base->moved = true; + amdgpu_vm_bo_needs_update(bo_base); /* It's a bit inefficient to always jump back to the start, but * we would need to re-structure the KFD for properly fixing @@ -979,7 +981,7 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev, amdgpu_vm_assert_locked(vm); - if (list_empty(&vm->kernel.moved)) + if (list_empty(&vm->kernel.needs_update)) return 0; if (!drm_dev_enter(adev_to_drm(adev), &idx)) @@ -995,7 +997,7 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev, if (r) goto error; - list_for_each_entry(entry, &vm->kernel.moved, vm_status) { + list_for_each_entry(entry, &vm->kernel.needs_update, vm_status) { /* vm_flush_needed after updating moved PDEs */ flush_tlb_needed |= entry->moved; @@ -1011,7 +1013,8 @@ int amdgpu_vm_update_pdes(struct amdgpu_device *adev, if (flush_tlb_needed) atomic64_inc(&vm->tlb_seq); - list_for_each_entry_safe(entry, tmp, &vm->kernel.moved, vm_status) + list_for_each_entry_safe(entry, tmp, &vm->kernel.needs_update, + vm_status) amdgpu_vm_bo_idle(entry); error: @@ -1615,7 +1618,7 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, bool clear, unlock; int r; - list_for_each_entry_safe(bo_va, tmp, &vm->always_valid.moved, + list_for_each_entry_safe(bo_va, tmp, &vm->always_valid.needs_update, base.vm_status) { /* Per VM BOs never need to bo cleared in the page tables */ r = amdgpu_vm_bo_update(adev, bo_va, false); @@ -1624,8 +1627,8 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, } spin_lock(&vm->individual_lock); - while (!list_empty(&vm->individual.moved)) { - bo_va = list_first_entry(&vm->individual.moved, + while (!list_empty(&vm->individual.needs_update)) { + bo_va = list_first_entry(&vm->individual.needs_update, typeof(*bo_va), base.vm_status); bo = bo_va->base.bo; resv = bo->tbo.base.resv; @@ -1786,7 +1789,7 @@ static void amdgpu_vm_bo_insert_map(struct amdgpu_device *adev, amdgpu_vm_prt_get(adev); if (amdgpu_vm_is_bo_always_valid(vm, bo) && !bo_va->base.moved) - amdgpu_vm_bo_moved(&bo_va->base); + amdgpu_vm_bo_needs_update(&bo_va->base); trace_amdgpu_vm_bo_map(bo_va, mapping); } @@ -2095,7 +2098,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev, if (amdgpu_vm_is_bo_always_valid(vm, bo) && !before->bo_va->base.moved) - amdgpu_vm_bo_moved(&before->bo_va->base); + amdgpu_vm_bo_needs_update(&before->bo_va->base); } else { kfree(before); } @@ -2110,7 +2113,7 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev, if (amdgpu_vm_is_bo_always_valid(vm, bo) && !after->bo_va->base.moved) - amdgpu_vm_bo_moved(&after->bo_va->base); + amdgpu_vm_bo_needs_update(&after->bo_va->base); } else { kfree(after); } @@ -2284,7 +2287,8 @@ void amdgpu_vm_bo_invalidate(struct amdgpu_bo *bo, bool evicted) if (bo_base->moved) continue; - amdgpu_vm_bo_moved(bo_base); + bo_base->moved = true; + amdgpu_vm_bo_needs_update(bo_base); } } @@ -3098,7 +3102,7 @@ static void amdgpu_debugfs_vm_bo_status_info(struct seq_file *m, id = 0; seq_puts(m, "\tMoved BOs:\n"); - list_for_each_entry(base, &lists->moved, vm_status) { + list_for_each_entry(base, &lists->needs_update, vm_status) { if (!base->bo) continue; @@ -3107,7 +3111,7 @@ static void amdgpu_debugfs_vm_bo_status_info(struct seq_file *m, id = 0; seq_puts(m, "\tIdle BOs:\n"); - list_for_each_entry(base, &lists->moved, vm_status) { + list_for_each_entry(base, &lists->needs_update, vm_status) { if (!base->bo) continue; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index b32f51a78cd8..5822836fa4a3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -212,7 +212,8 @@ struct amdgpu_vm_bo_base { * protected by vm BO being reserved */ bool shared; - /* protected by the BO being reserved */ + /* if the BO was moved and all mappings are invalid + * protected by the BO being reserved */ bool moved; }; @@ -220,14 +221,14 @@ struct amdgpu_vm_bo_base { * The following status lists contain amdgpu_vm_bo_base objects for * either PD/PTs, per VM BOs or BOs with individual resv object. * - * The state transits are: evicted -> moved -> idle + * The state transits are: evicted -> needs_update -> idle */ struct amdgpu_vm_bo_status { /* BOs evicted which need to move into place again */ struct list_head evicted; - /* BOs which moved but new location hasn't been updated in the PDs/PTs */ - struct list_head moved; + /* BOs whose mappings changed but PDs/PTs haven't been updated */ + struct list_head needs_update; /* BOs done with the state machine and need no further action */ struct list_head idle; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c index 409e103ffe8c..35faea0ff17f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c @@ -381,7 +381,8 @@ int amdgpu_xcp_get_inst_details(struct amdgpu_xcp *xcp, enum AMDGPU_XCP_IP_BLOCK ip, uint32_t *inst_mask) { - if (!xcp->valid || !inst_mask || !(xcp->ip[ip].valid)) + if (!xcp->valid || !inst_mask || ip >= AMDGPU_XCP_MAX_BLOCKS || + !(xcp->ip[ip].valid)) return -EINVAL; *inst_mask = xcp->ip[ip].inst_mask; @@ -468,14 +469,18 @@ void amdgpu_xcp_release_sched(struct amdgpu_device *adev, { struct drm_gpu_scheduler *sched = container_of(entity->entity.rq, typeof(*sched), rq); + struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr; - if (!adev->xcp_mgr) + if (!xcp_mgr) return; if (drm_sched_wqueue_ready(sched)) { struct amdgpu_ring *ring = to_amdgpu_ring(sched); - atomic_dec(&adev->xcp_mgr->xcp[ring->xcp_id].ref_cnt); + mutex_lock(&xcp_mgr->xcp_lock); + if (ring->xcp_id < xcp_mgr->num_xcps && xcp_mgr->xcp[ring->xcp_id].valid) + atomic_dec(&xcp_mgr->xcp[ring->xcp_id].ref_cnt); + mutex_unlock(&xcp_mgr->xcp_lock); } } @@ -488,7 +493,9 @@ int amdgpu_xcp_select_scheds(struct amdgpu_device *adev, u32 sel_xcp_id; int i; struct amdgpu_xcp_mgr *xcp_mgr = adev->xcp_mgr; + int r = 0; + mutex_lock(&xcp_mgr->xcp_lock); if (fpriv->xcp_id == AMDGPU_XCP_NO_PARTITION) { u32 least_ref_cnt = ~0; @@ -505,19 +512,27 @@ int amdgpu_xcp_select_scheds(struct amdgpu_device *adev, } sel_xcp_id = fpriv->xcp_id; + if (sel_xcp_id >= xcp_mgr->num_xcps || !xcp_mgr->xcp[sel_xcp_id].valid) { + dev_err(adev->dev, "Selected partition #%d is not valid.", sel_xcp_id); + r = -ENODEV; + goto out; + } + if (xcp_mgr->xcp[sel_xcp_id].gpu_sched[hw_ip][hw_prio].num_scheds) { *num_scheds = - xcp_mgr->xcp[fpriv->xcp_id].gpu_sched[hw_ip][hw_prio].num_scheds; + xcp_mgr->xcp[sel_xcp_id].gpu_sched[hw_ip][hw_prio].num_scheds; *scheds = - xcp_mgr->xcp[fpriv->xcp_id].gpu_sched[hw_ip][hw_prio].sched; - atomic_inc(&adev->xcp_mgr->xcp[sel_xcp_id].ref_cnt); + xcp_mgr->xcp[sel_xcp_id].gpu_sched[hw_ip][hw_prio].sched; + atomic_inc(&xcp_mgr->xcp[sel_xcp_id].ref_cnt); dev_dbg(adev->dev, "Selected partition #%d", sel_xcp_id); } else { dev_err(adev->dev, "Failed to schedule partition #%d.", sel_xcp_id); - return -ENOENT; + r = -ENOENT; } - return 0; +out: + mutex_unlock(&xcp_mgr->xcp_lock); + return r; } static void amdgpu_set_xcp_id(struct amdgpu_device *adev, @@ -574,6 +589,9 @@ static void amdgpu_xcp_gpu_sched_update(struct amdgpu_device *adev, { unsigned int *num_gpu_sched; + if (sel_xcp_id >= MAX_XCP || sel_xcp_id == AMDGPU_XCP_NO_PARTITION) + return; + num_gpu_sched = &adev->xcp_mgr->xcp[sel_xcp_id] .gpu_sched[ring->funcs->type][ring->hw_prio].num_scheds; adev->xcp_mgr->xcp[sel_xcp_id].gpu_sched[ring->funcs->type][ring->hw_prio] @@ -903,7 +921,7 @@ static void amdgpu_xcp_cfg_sysfs_init(struct amdgpu_device *adev) { struct amdgpu_xcp_res_details *xcp_res; struct amdgpu_xcp_cfg *xcp_cfg; - int i, r, j, rid, mode; + int i, r, rid, mode; if (!adev->xcp_mgr) return; @@ -949,14 +967,16 @@ static void amdgpu_xcp_cfg_sysfs_init(struct amdgpu_device *adev) &xcp_cfg_res_sysfs_ktype, &xcp_cfg->kobj, "%s", xcp_res_names[rid]); - if (r) + if (r) { + kobject_put(&xcp_res->kobj); goto err; + } } adev->xcp_mgr->xcp_cfg = xcp_cfg; return; err: - for (j = 0; j < i; j++) { + while (i--) { xcp_res = &xcp_cfg->xcp_res[i]; kobject_put(&xcp_res->kobj); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c index e63d05c477a0..d2c5bb50d94a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xgmi.c @@ -106,53 +106,6 @@ static const int walf_pcs_err_noncorrectable_mask_reg_aldebaran[] = { smnPCS_GOPX1_PCS_ERROR_NONCORRECTABLE_MASK + 0x100000 }; -static const int xgmi3x16_pcs_err_status_reg_v6_4[] = { - smnPCS_XGMI3X16_PCS_ERROR_STATUS, - smnPCS_XGMI3X16_PCS_ERROR_STATUS + 0x100000 -}; - -static const int xgmi3x16_pcs_err_noncorrectable_mask_reg_v6_4[] = { - smnPCS_XGMI3X16_PCS_ERROR_NONCORRECTABLE_MASK, - smnPCS_XGMI3X16_PCS_ERROR_NONCORRECTABLE_MASK + 0x100000 -}; - -static const u64 xgmi_v6_4_0_mca_base_array[] = { - 0x11a09200, - 0x11b09200, -}; - -static const char *xgmi_v6_4_0_ras_error_code_ext[32] = { - [0x00] = "XGMI PCS DataLossErr", - [0x01] = "XGMI PCS TrainingErr", - [0x02] = "XGMI PCS FlowCtrlAckErr", - [0x03] = "XGMI PCS RxFifoUnderflowErr", - [0x04] = "XGMI PCS RxFifoOverflowErr", - [0x05] = "XGMI PCS CRCErr", - [0x06] = "XGMI PCS BERExceededErr", - [0x07] = "XGMI PCS TxMetaDataErr", - [0x08] = "XGMI PCS ReplayBufParityErr", - [0x09] = "XGMI PCS DataParityErr", - [0x0a] = "XGMI PCS ReplayFifoOverflowErr", - [0x0b] = "XGMI PCS ReplayFifoUnderflowErr", - [0x0c] = "XGMI PCS ElasticFifoOverflowErr", - [0x0d] = "XGMI PCS DeskewErr", - [0x0e] = "XGMI PCS FlowCtrlCRCErr", - [0x0f] = "XGMI PCS DataStartupLimitErr", - [0x10] = "XGMI PCS FCInitTimeoutErr", - [0x11] = "XGMI PCS RecoveryTimeoutErr", - [0x12] = "XGMI PCS ReadySerialTimeoutErr", - [0x13] = "XGMI PCS ReadySerialAttemptErr", - [0x14] = "XGMI PCS RecoveryAttemptErr", - [0x15] = "XGMI PCS RecoveryRelockAttemptErr", - [0x16] = "XGMI PCS ReplayAttemptErr", - [0x17] = "XGMI PCS SyncHdrErr", - [0x18] = "XGMI PCS TxReplayTimeoutErr", - [0x19] = "XGMI PCS RxReplayTimeoutErr", - [0x1a] = "XGMI PCS LinkSubTxTimeoutErr", - [0x1b] = "XGMI PCS LinkSubRxTimeoutErr", - [0x1c] = "XGMI PCS RxCMDPktErr", -}; - static const struct amdgpu_pcs_ras_field xgmi_pcs_ras_fields[] = { {"XGMI PCS DataLossErr", SOC15_REG_FIELD(XGMI0_PCS_GOPX16_PCS_ERROR_STATUS, DataLossErr)}, @@ -1152,91 +1105,15 @@ int amdgpu_xgmi_remove_device(struct amdgpu_device *adev) return 0; } -static int xgmi_v6_4_0_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - struct amdgpu_device *adev = handle->adev; - struct aca_bank_info info; - const char *error_str; - u64 status, count; - int ret, ext_error_code; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return ret; - - status = bank->regs[ACA_REG_IDX_STATUS]; - ext_error_code = ACA_REG__STATUS__ERRORCODEEXT(status); - - error_str = ext_error_code < ARRAY_SIZE(xgmi_v6_4_0_ras_error_code_ext) ? - xgmi_v6_4_0_ras_error_code_ext[ext_error_code] : NULL; - if (error_str) - dev_info(adev->dev, "%s detected\n", error_str); - - count = ACA_REG__MISC0__ERRCNT(bank->regs[ACA_REG_IDX_MISC0]); - - switch (type) { - case ACA_SMU_TYPE_UE: - if (ext_error_code != 0 && ext_error_code != 1 && ext_error_code != 9) - count = 0ULL; - - bank->aca_err_type = ACA_ERROR_TYPE_UE; - ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE, count); - break; - case ACA_SMU_TYPE_CE: - count = ext_error_code == 6 ? count : 0ULL; - bank->aca_err_type = ACA_ERROR_TYPE_CE; - ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, count); - break; - default: - return -EINVAL; - } - - return ret; -} - -static const struct aca_bank_ops xgmi_v6_4_0_aca_bank_ops = { - .aca_bank_parser = xgmi_v6_4_0_aca_bank_parser, -}; - -static const struct aca_info xgmi_v6_4_0_aca_info = { - .hwip = ACA_HWIP_TYPE_PCS_XGMI, - .mask = ACA_ERROR_UE_MASK | ACA_ERROR_CE_MASK, - .bank_ops = &xgmi_v6_4_0_aca_bank_ops, -}; - static int amdgpu_xgmi_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block) { - int r; - if (!adev->gmc.xgmi.supported || adev->gmc.xgmi.num_physical_nodes == 0) return 0; amdgpu_ras_reset_error_count(adev, AMDGPU_RAS_BLOCK__XGMI_WAFL); - r = amdgpu_ras_block_late_init(adev, ras_block); - if (r) - return r; - - switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) { - case IP_VERSION(6, 4, 0): - case IP_VERSION(6, 4, 1): - r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__XGMI_WAFL, - &xgmi_v6_4_0_aca_info, NULL); - if (r) - goto late_fini; - break; - default: - break; - } - - return 0; - -late_fini: - amdgpu_ras_block_late_fini(adev, ras_block); - - return r; + return amdgpu_ras_block_late_init(adev, ras_block); } uint64_t amdgpu_xgmi_get_relative_phy_addr(struct amdgpu_device *adev, @@ -1252,7 +1129,7 @@ static void pcs_clear_status(struct amdgpu_device *adev, uint32_t pcs_status_reg WREG32_PCIE(pcs_status_reg, 0); } -static void amdgpu_xgmi_legacy_reset_ras_error_count(struct amdgpu_device *adev) +static void amdgpu_xgmi_reset_ras_error_count(struct amdgpu_device *adev) { uint32_t i; @@ -1278,54 +1155,6 @@ static void amdgpu_xgmi_legacy_reset_ras_error_count(struct amdgpu_device *adev) default: break; } - - switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) { - case IP_VERSION(6, 4, 0): - case IP_VERSION(6, 4, 1): - for (i = 0; i < ARRAY_SIZE(xgmi3x16_pcs_err_status_reg_v6_4); i++) - pcs_clear_status(adev, - xgmi3x16_pcs_err_status_reg_v6_4[i]); - break; - default: - break; - } -} - -static void __xgmi_v6_4_0_reset_error_count(struct amdgpu_device *adev, int xgmi_inst, u64 mca_base) -{ - uint64_t smn_base = - amdgpu_reg_get_smn_base64(adev, XGMI_HWIP, xgmi_inst); - - WREG64_MCA(smn_base, mca_base, ACA_REG_IDX_STATUS, 0ULL); -} - -static void xgmi_v6_4_0_reset_error_count(struct amdgpu_device *adev, int xgmi_inst) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(xgmi_v6_4_0_mca_base_array); i++) - __xgmi_v6_4_0_reset_error_count(adev, xgmi_inst, xgmi_v6_4_0_mca_base_array[i]); -} - -static void xgmi_v6_4_0_reset_ras_error_count(struct amdgpu_device *adev) -{ - int i; - - for_each_inst(i, adev->aid_mask) - xgmi_v6_4_0_reset_error_count(adev, i); -} - -static void amdgpu_xgmi_reset_ras_error_count(struct amdgpu_device *adev) -{ - switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) { - case IP_VERSION(6, 4, 0): - case IP_VERSION(6, 4, 1): - xgmi_v6_4_0_reset_ras_error_count(adev); - break; - default: - amdgpu_xgmi_legacy_reset_ras_error_count(adev); - break; - } } static int amdgpu_xgmi_query_pcs_error_status(struct amdgpu_device *adev, @@ -1343,11 +1172,7 @@ static int amdgpu_xgmi_query_pcs_error_status(struct amdgpu_device *adev, if (is_xgmi_pcs) { if (amdgpu_ip_version(adev, XGMI_HWIP, 0) == - IP_VERSION(6, 1, 0) || - amdgpu_ip_version(adev, XGMI_HWIP, 0) == - IP_VERSION(6, 4, 0) || - amdgpu_ip_version(adev, XGMI_HWIP, 0) == - IP_VERSION(6, 4, 1)) { + IP_VERSION(6, 1, 0)) { pcs_ras_fields = &xgmi3x16_pcs_ras_fields[0]; field_array_size = ARRAY_SIZE(xgmi3x16_pcs_ras_fields); } else { @@ -1381,11 +1206,11 @@ static int amdgpu_xgmi_query_pcs_error_status(struct amdgpu_device *adev, return 0; } -static void amdgpu_xgmi_legacy_query_ras_error_count(struct amdgpu_device *adev, +static void amdgpu_xgmi_query_ras_error_count(struct amdgpu_device *adev, void *ras_error_status) { struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status; - int i, supported = 1; + int i; uint32_t data, mask_data = 0; uint32_t ue_cnt = 0, ce_cnt = 0; @@ -1449,26 +1274,6 @@ static void amdgpu_xgmi_legacy_query_ras_error_count(struct amdgpu_device *adev, } break; default: - supported = 0; - break; - } - - switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) { - case IP_VERSION(6, 4, 0): - case IP_VERSION(6, 4, 1): - /* check xgmi3x16 pcs error */ - for (i = 0; i < ARRAY_SIZE(xgmi3x16_pcs_err_status_reg_v6_4); i++) { - data = RREG32_PCIE(xgmi3x16_pcs_err_status_reg_v6_4[i]); - mask_data = - RREG32_PCIE(xgmi3x16_pcs_err_noncorrectable_mask_reg_v6_4[i]); - if (data) - amdgpu_xgmi_query_pcs_error_status(adev, data, - mask_data, &ue_cnt, &ce_cnt, true, true); - } - break; - default: - if (!supported) - dev_warn(adev->dev, "XGMI RAS error query not supported"); break; } @@ -1478,90 +1283,6 @@ static void amdgpu_xgmi_legacy_query_ras_error_count(struct amdgpu_device *adev, err_data->ce_count += ce_cnt; } -static enum aca_error_type xgmi_v6_4_0_pcs_mca_get_error_type(struct amdgpu_device *adev, u64 status) -{ - const char *error_str; - int ext_error_code; - - ext_error_code = ACA_REG__STATUS__ERRORCODEEXT(status); - - error_str = ext_error_code < ARRAY_SIZE(xgmi_v6_4_0_ras_error_code_ext) ? - xgmi_v6_4_0_ras_error_code_ext[ext_error_code] : NULL; - if (error_str) - dev_info(adev->dev, "%s detected\n", error_str); - - switch (ext_error_code) { - case 0: - return ACA_ERROR_TYPE_UE; - case 6: - return ACA_ERROR_TYPE_CE; - default: - return -EINVAL; - } - - return -EINVAL; -} - -static void __xgmi_v6_4_0_query_error_count(struct amdgpu_device *adev, struct amdgpu_smuio_mcm_config_info *mcm_info, - u64 mca_base, struct ras_err_data *err_data) -{ - int xgmi_inst = mcm_info->die_id; - uint64_t smn_base; - u64 status = 0; - - status = RREG64_MCA(xgmi_inst, mca_base, ACA_REG_IDX_STATUS); - if (!ACA_REG__STATUS__VAL(status)) - return; - - switch (xgmi_v6_4_0_pcs_mca_get_error_type(adev, status)) { - case ACA_ERROR_TYPE_UE: - amdgpu_ras_error_statistic_ue_count(err_data, mcm_info, 1ULL); - break; - case ACA_ERROR_TYPE_CE: - amdgpu_ras_error_statistic_ce_count(err_data, mcm_info, 1ULL); - break; - default: - break; - } - smn_base = amdgpu_reg_get_smn_base64(adev, XGMI_HWIP, xgmi_inst); - WREG64_MCA(smn_base, mca_base, ACA_REG_IDX_STATUS, 0ULL); -} - -static void xgmi_v6_4_0_query_error_count(struct amdgpu_device *adev, int xgmi_inst, struct ras_err_data *err_data) -{ - struct amdgpu_smuio_mcm_config_info mcm_info = { - .socket_id = adev->smuio.funcs->get_socket_id(adev), - .die_id = xgmi_inst, - }; - int i; - - for (i = 0; i < ARRAY_SIZE(xgmi_v6_4_0_mca_base_array); i++) - __xgmi_v6_4_0_query_error_count(adev, &mcm_info, xgmi_v6_4_0_mca_base_array[i], err_data); -} - -static void xgmi_v6_4_0_query_ras_error_count(struct amdgpu_device *adev, void *ras_error_status) -{ - struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status; - int i; - - for_each_inst(i, adev->aid_mask) - xgmi_v6_4_0_query_error_count(adev, i, err_data); -} - -static void amdgpu_xgmi_query_ras_error_count(struct amdgpu_device *adev, - void *ras_error_status) -{ - switch (amdgpu_ip_version(adev, XGMI_HWIP, 0)) { - case IP_VERSION(6, 4, 0): - case IP_VERSION(6, 4, 1): - xgmi_v6_4_0_query_ras_error_count(adev, ras_error_status); - break; - default: - amdgpu_xgmi_legacy_query_ras_error_count(adev, ras_error_status); - break; - } -} - /* Trigger XGMI/WAFL error */ static int amdgpu_ras_error_inject_xgmi(struct amdgpu_device *adev, void *inject_if, uint32_t instance_mask) @@ -1663,6 +1384,16 @@ static void amdgpu_xgmi_reset_on_init_work(struct work_struct *work) if (r && r != -EHWPOISON) dev_err(tmp_adev->dev, "error during bad page data initialization"); + + /* + * For the reset-on-init path (e.g. an NPS memory partition + * switch) the RAS IP block hw_init was skipped under the + * minimal init level, so uniras was never enabled. Bring it + * up now that the reset domain has been unlocked. This is a + * no-op for any other reset path where RAS is already + * initialized, and for non-uniras devices. + */ + amdgpu_ras_resume_after_reset(tmp_adev); } } diff --git a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c index 72ea37dbfea8..cddfe4015f53 100644 --- a/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c +++ b/drivers/gpu/drm/amd/amdgpu/aqua_vanjaram.c @@ -273,8 +273,10 @@ static int aqua_vanjaram_get_xcp_res_info(struct amdgpu_xcp_mgr *xcp_mgr, xcp_cfg->num_res = ARRAY_SIZE(max_res); for (i = 0; i < xcp_cfg->num_res; i++) { - res_lt_xcp = max_res[i] < num_xcp; xcp_cfg->xcp_res[i].id = i; + if (!max_res[i]) + continue; + res_lt_xcp = max_res[i] < num_xcp; xcp_cfg->xcp_res[i].num_inst = res_lt_xcp ? 1 : max_res[i] / num_xcp; xcp_cfg->xcp_res[i].num_inst = @@ -589,6 +591,29 @@ static struct aqua_reg_list pcie_reg_addrs[] = { { smreg_0x1A380088, 6, DW_ADDR_INCR }, }; +/* + * Return the GPU's internal US switch port, or NULL if it is not visible + * (e.g. passthrough) or the EP is parented under an unrelated bridge. + */ +static struct pci_dev *aqua_vanjaram_get_us_pdev(struct amdgpu_device *adev) +{ + struct pci_dev *ds_pdev, *us_pdev; + + ds_pdev = pci_upstream_bridge(adev->pdev); + if (!ds_pdev || ds_pdev->vendor != PCI_VENDOR_ID_ATI || + pci_pcie_type(ds_pdev) != PCI_EXP_TYPE_DOWNSTREAM) + return NULL; + + us_pdev = pci_upstream_bridge(ds_pdev); + if (!us_pdev || + (us_pdev->vendor != PCI_VENDOR_ID_ATI && + us_pdev->vendor != PCI_VENDOR_ID_AMD) || + pci_pcie_type(us_pdev) != PCI_EXP_TYPE_UPSTREAM) + return NULL; + + return us_pdev; +} + static ssize_t aqua_vanjaram_read_pcie_state(struct amdgpu_device *adev, void *buf, size_t max_size) { @@ -596,7 +621,7 @@ static ssize_t aqua_vanjaram_read_pcie_state(struct amdgpu_device *adev, uint32_t start_addr, incrx, num_regs, szbuf; struct amdgpu_regs_pcie_v1_0 *pcie_regs; struct amdgpu_smn_reg_data *reg_data; - struct pci_dev *us_pdev, *ds_pdev; + struct pci_dev *us_pdev; int aer_cap, r, n; if (!buf || !max_size) @@ -628,25 +653,27 @@ static ssize_t aqua_vanjaram_read_pcie_state(struct amdgpu_device *adev, } } - ds_pdev = pci_upstream_bridge(adev->pdev); - us_pdev = pci_upstream_bridge(ds_pdev); + us_pdev = aqua_vanjaram_get_us_pdev(adev); + if (us_pdev) { + pcie_capability_read_word(us_pdev, PCI_EXP_DEVSTA, + &pcie_regs->device_status); + pcie_capability_read_word(us_pdev, PCI_EXP_LNKSTA, + &pcie_regs->link_status); + + aer_cap = pci_find_ext_capability(us_pdev, PCI_EXT_CAP_ID_ERR); + if (aer_cap) { + pci_read_config_dword(us_pdev, + aer_cap + PCI_ERR_COR_STATUS, + &pcie_regs->pcie_corr_err_status); + pci_read_config_dword(us_pdev, + aer_cap + PCI_ERR_UNCOR_STATUS, + &pcie_regs->pcie_uncorr_err_status); + } - pcie_capability_read_word(us_pdev, PCI_EXP_DEVSTA, - &pcie_regs->device_status); - pcie_capability_read_word(us_pdev, PCI_EXP_LNKSTA, - &pcie_regs->link_status); - - aer_cap = pci_find_ext_capability(us_pdev, PCI_EXT_CAP_ID_ERR); - if (aer_cap) { - pci_read_config_dword(us_pdev, aer_cap + PCI_ERR_COR_STATUS, - &pcie_regs->pcie_corr_err_status); - pci_read_config_dword(us_pdev, aer_cap + PCI_ERR_UNCOR_STATUS, - &pcie_regs->pcie_uncorr_err_status); + pci_read_config_dword(us_pdev, PCI_PRIMARY_BUS, + &pcie_regs->sub_bus_number_latency); } - pci_read_config_dword(us_pdev, PCI_PRIMARY_BUS, - &pcie_regs->sub_bus_number_latency); - pcie_reg_state->common_header.structure_size = szbuf; pcie_reg_state->common_header.format_revision = 1; pcie_reg_state->common_header.content_revision = 0; diff --git a/drivers/gpu/drm/amd/amdgpu/atom.c b/drivers/gpu/drm/amd/amdgpu/atom.c index ca5d091549e1..e0e585f280e2 100644 --- a/drivers/gpu/drm/amd/amdgpu/atom.c +++ b/drivers/gpu/drm/amd/amdgpu/atom.c @@ -114,8 +114,10 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, uint32_t index, uint32_t data) { uint32_t temp = 0xCDCDCDCD; + int start = base; - while (1) + /* IIO opcodes read up to base+3; keep within the BIOS image */ + while (base + 3 < ctx->bios_size) switch (CU8(base)) { case ATOM_IIO_NOP: base++; @@ -180,6 +182,9 @@ static uint32_t atom_iio_execute(struct atom_context *ctx, int base, pr_info("Unknown IIO opcode\n"); return 0; } + + pr_info("IIO method starting at offset %d runs past BIOS image\n", start); + return 0; } static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr, @@ -1327,11 +1332,25 @@ static void atom_index_iio(struct atom_context *ctx, int base) ctx->iio = kzalloc(2 * 256, GFP_KERNEL); if (!ctx->iio) return; - while (CU8(base) == ATOM_IIO_START) { - ctx->iio[CU8(base + 1)] = base + 2; + while (base + 1 < ctx->bios_size && CU8(base) == ATOM_IIO_START) { + uint8_t index = CU8(base + 1); + int start = base + 2; base += 2; - while (CU8(base) != ATOM_IIO_END) - base += atom_iio_len[CU8(base)]; + while (base < ctx->bios_size && CU8(base) != ATOM_IIO_END) { + uint8_t op = CU8(base); + + /* + * Unknown opcode: its length is unknown so the byte + * stream cannot be resynced reliably. + */ + if (op >= ARRAY_SIZE(atom_iio_len)) + return; + base += atom_iio_len[op]; + } + if (base >= ctx->bios_size) + return; + /* Only index well-formed methods, others stay 0 */ + ctx->iio[index] = start; base += 3; } } @@ -1339,6 +1358,7 @@ static void atom_index_iio(struct atom_context *ctx, int base) static void atom_get_vbios_name(struct atom_context *ctx) { unsigned char *p_rom; + unsigned char *p_end; unsigned char str_num; unsigned short off_to_vbios_str; unsigned char *c_ptr; @@ -1349,39 +1369,48 @@ static void atom_get_vbios_name(struct atom_context *ctx) char *back; p_rom = ctx->bios; + p_end = p_rom + ctx->bios_size; + + if (p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START + 1 >= p_end) + goto no_name; str_num = *(p_rom + OFFSET_TO_GET_ATOMBIOS_NUMBER_OF_STRINGS); - if (str_num != 0) { - off_to_vbios_str = - *(unsigned short *)(p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START); + if (!str_num) + goto no_name; - c_ptr = (unsigned char *)(p_rom + off_to_vbios_str); - } else { - /* do not know where to find name */ - memcpy(ctx->name, na, 7); - ctx->name[7] = 0; - return; - } + off_to_vbios_str = + *(unsigned short *)(p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START); + + c_ptr = (unsigned char *)(p_rom + off_to_vbios_str); + if (c_ptr >= p_end) + goto no_name; /* * skip the atombios strings, usually 4 * 1st is P/N, 2nd is ASIC, 3rd is PCI type, 4th is Memory type */ for (i = 0; i < str_num; i++) { - while (*c_ptr != 0) + while (c_ptr < p_end && *c_ptr != 0) c_ptr++; c_ptr++; } /* skip the following 2 chars: 0x0D 0x0A */ c_ptr += 2; + if (c_ptr >= p_end) + goto no_name; - name_size = strnlen(c_ptr, STRLEN_LONG - 1); + name_size = strnlen(c_ptr, min(STRLEN_LONG - 1, (int)(p_end - c_ptr))); memcpy(ctx->name, c_ptr, name_size); back = ctx->name + name_size; while ((*--back) == ' ') ; *(back + 1) = '\0'; + return; + +no_name: + /* do not know where to find name */ + strscpy(ctx->name, na, sizeof(ctx->name)); } static void atom_get_vbios_date(struct atom_context *ctx) @@ -1553,7 +1582,7 @@ static inline void atom_print_vbios_info(struct atom_context *ctx) drm_info(ctx->card->dev, "ATOM BIOS: %s\n", vbios_info); } -struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios) +struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios, uint32_t bios_size) { int base; struct atom_context *ctx = @@ -1567,6 +1596,7 @@ struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios) ctx->card = card; ctx->bios = bios; + ctx->bios_size = bios_size; if (CU16(0) != ATOM_BIOS_MAGIC) { pr_info("Invalid BIOS magic\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/atom.h b/drivers/gpu/drm/amd/amdgpu/atom.h index bb3d9eb7eb6b..4687c019cbe3 100644 --- a/drivers/gpu/drm/amd/amdgpu/atom.h +++ b/drivers/gpu/drm/amd/amdgpu/atom.h @@ -133,6 +133,7 @@ struct atom_context { struct card_info *card; struct mutex mutex; void *bios; + uint32_t bios_size; uint32_t cmd_table, data_table; uint16_t *iio; @@ -160,7 +161,7 @@ struct atom_context { extern int amdgpu_atom_debug; -struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios); +struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios, uint32_t bios_size); int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size); int amdgpu_atom_asic_init(struct atom_context *ctx); void amdgpu_atom_destroy(struct atom_context *ctx); diff --git a/drivers/gpu/drm/amd/amdgpu/cik.c b/drivers/gpu/drm/amd/amdgpu/cik.c index 29954c7d61b0..77e120a72815 100644 --- a/drivers/gpu/drm/amd/amdgpu/cik.c +++ b/drivers/gpu/drm/amd/amdgpu/cik.c @@ -1876,12 +1876,6 @@ static void cik_invalidate_hdp(struct amdgpu_device *adev, } } -static bool cik_need_full_reset(struct amdgpu_device *adev) -{ - /* change this when we support soft reset */ - return true; -} - static void cik_get_pcie_usage(struct amdgpu_device *adev, uint64_t *count0, uint64_t *count1) { @@ -1971,7 +1965,6 @@ static const struct amdgpu_asic_funcs cik_asic_funcs = .get_config_memsize = &cik_get_config_memsize, .flush_hdp = &cik_flush_hdp, .invalidate_hdp = &cik_invalidate_hdp, - .need_full_reset = &cik_need_full_reset, .init_doorbell_index = &legacy_doorbell_index_init, .get_pcie_usage = &cik_get_pcie_usage, .need_reset_on_init = &cik_need_reset_on_init, diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index c8f465158e71..f2977fe6d824 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -410,36 +410,6 @@ static u32 dce_v10_0_hpd_get_gpio_reg(struct amdgpu_device *adev) return mmDC_GPIO_HPD_A; } -static bool dce_v10_0_is_display_hung(struct amdgpu_device *adev) -{ - u32 crtc_hung = 0; - u32 crtc_status[6]; - u32 i, j, tmp; - - for (i = 0; i < adev->mode_info.num_crtc; i++) { - tmp = RREG32(mmCRTC_CONTROL + crtc_offsets[i]); - if (REG_GET_FIELD(tmp, CRTC_CONTROL, CRTC_MASTER_EN)) { - crtc_status[i] = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]); - crtc_hung |= (1 << i); - } - } - - for (j = 0; j < 10; j++) { - for (i = 0; i < adev->mode_info.num_crtc; i++) { - if (crtc_hung & (1 << i)) { - tmp = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]); - if (tmp != crtc_status[i]) - crtc_hung &= ~(1 << i); - } - } - if (crtc_hung == 0) - return false; - udelay(100); - } - - return true; -} - static void dce_v10_0_set_vga_render_state(struct amdgpu_device *adev, bool render) { @@ -2956,40 +2926,6 @@ static bool dce_v10_0_is_idle(struct amdgpu_ip_block *ip_block) return true; } -static bool dce_v10_0_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - return dce_v10_0_is_display_hung(adev); -} - -static int dce_v10_0_soft_reset(struct amdgpu_ip_block *ip_block) -{ - u32 srbm_soft_reset = 0, tmp; - struct amdgpu_device *adev = ip_block->adev; - - if (dce_v10_0_is_display_hung(adev)) - srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_DC_MASK; - - if (srbm_soft_reset) { - tmp = RREG32(mmSRBM_SOFT_RESET); - tmp |= srbm_soft_reset; - dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp); - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - - udelay(50); - - tmp &= ~srbm_soft_reset; - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - - /* Wait a little for things to settle down */ - udelay(50); - } - return 0; -} - static void dce_v10_0_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev, int crtc, enum amdgpu_interrupt_state state) @@ -3332,8 +3268,6 @@ static const struct amd_ip_funcs dce_v10_0_ip_funcs = { .suspend = dce_v10_0_suspend, .resume = dce_v10_0_resume, .is_idle = dce_v10_0_is_idle, - .check_soft_reset = dce_v10_0_check_soft_reset, - .soft_reset = dce_v10_0_soft_reset, .set_clockgating_state = dce_v10_0_set_clockgating_state, .set_powergating_state = dce_v10_0_set_powergating_state, }; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c index 58d0da5c2a74..c68de0fe1d7d 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c @@ -378,35 +378,6 @@ static u32 dce_v6_0_hpd_get_gpio_reg(struct amdgpu_device *adev) return mmDC_GPIO_HPD_A; } -static bool dce_v6_0_is_display_hung(struct amdgpu_device *adev) -{ - u32 crtc_hung = 0; - u32 crtc_status[6]; - u32 i, j, tmp; - - for (i = 0; i < adev->mode_info.num_crtc; i++) { - if (RREG32(mmCRTC_CONTROL + crtc_offsets[i]) & CRTC_CONTROL__CRTC_MASTER_EN_MASK) { - crtc_status[i] = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]); - crtc_hung |= (1 << i); - } - } - - for (j = 0; j < 10; j++) { - for (i = 0; i < adev->mode_info.num_crtc; i++) { - if (crtc_hung & (1 << i)) { - tmp = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]); - if (tmp != crtc_status[i]) - crtc_hung &= ~(1 << i); - } - } - if (crtc_hung == 0) - return false; - udelay(100); - } - - return true; -} - static void dce_v6_0_set_vga_render_state(struct amdgpu_device *adev, bool render) { @@ -2901,33 +2872,6 @@ static bool dce_v6_0_is_idle(struct amdgpu_ip_block *ip_block) return true; } -static int dce_v6_0_soft_reset(struct amdgpu_ip_block *ip_block) -{ - u32 srbm_soft_reset = 0, tmp; - struct amdgpu_device *adev = ip_block->adev; - - if (dce_v6_0_is_display_hung(adev)) - srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_DC_MASK; - - if (srbm_soft_reset) { - tmp = RREG32(mmSRBM_SOFT_RESET); - tmp |= srbm_soft_reset; - dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp); - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - - udelay(50); - - tmp &= ~srbm_soft_reset; - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - - /* Wait a little for things to settle down */ - udelay(50); - } - return 0; -} - static void dce_v6_0_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev, int crtc, enum amdgpu_interrupt_state state) @@ -3224,7 +3168,6 @@ static const struct amd_ip_funcs dce_v6_0_ip_funcs = { .suspend = dce_v6_0_suspend, .resume = dce_v6_0_resume, .is_idle = dce_v6_0_is_idle, - .soft_reset = dce_v6_0_soft_reset, .set_clockgating_state = dce_v6_0_set_clockgating_state, .set_powergating_state = dce_v6_0_set_powergating_state, }; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index 6d19f6d94d25..c3906270f25e 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -362,35 +362,6 @@ static u32 dce_v8_0_hpd_get_gpio_reg(struct amdgpu_device *adev) return mmDC_GPIO_HPD_A; } -static bool dce_v8_0_is_display_hung(struct amdgpu_device *adev) -{ - u32 crtc_hung = 0; - u32 crtc_status[6]; - u32 i, j, tmp; - - for (i = 0; i < adev->mode_info.num_crtc; i++) { - if (RREG32(mmCRTC_CONTROL + crtc_offsets[i]) & CRTC_CONTROL__CRTC_MASTER_EN_MASK) { - crtc_status[i] = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]); - crtc_hung |= (1 << i); - } - } - - for (j = 0; j < 10; j++) { - for (i = 0; i < adev->mode_info.num_crtc; i++) { - if (crtc_hung & (1 << i)) { - tmp = RREG32(mmCRTC_STATUS_HV_COUNT + crtc_offsets[i]); - if (tmp != crtc_status[i]) - crtc_hung &= ~(1 << i); - } - } - if (crtc_hung == 0) - return false; - udelay(100); - } - - return true; -} - static void dce_v8_0_set_vga_render_state(struct amdgpu_device *adev, bool render) { @@ -2873,33 +2844,6 @@ static bool dce_v8_0_is_idle(struct amdgpu_ip_block *ip_block) return true; } -static int dce_v8_0_soft_reset(struct amdgpu_ip_block *ip_block) -{ - u32 srbm_soft_reset = 0, tmp; - struct amdgpu_device *adev = ip_block->adev; - - if (dce_v8_0_is_display_hung(adev)) - srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_DC_MASK; - - if (srbm_soft_reset) { - tmp = RREG32(mmSRBM_SOFT_RESET); - tmp |= srbm_soft_reset; - dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp); - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - - udelay(50); - - tmp &= ~srbm_soft_reset; - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - - /* Wait a little for things to settle down */ - udelay(50); - } - return 0; -} - static void dce_v8_0_set_crtc_vblank_interrupt_state(struct amdgpu_device *adev, int crtc, enum amdgpu_interrupt_state state) @@ -3241,7 +3185,6 @@ static const struct amd_ip_funcs dce_v8_0_ip_funcs = { .suspend = dce_v8_0_suspend, .resume = dce_v8_0_resume, .is_idle = dce_v8_0_is_idle, - .soft_reset = dce_v8_0_soft_reset, .set_clockgating_state = dce_v8_0_set_clockgating_state, .set_powergating_state = dce_v8_0_set_powergating_state, }; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 0780c5e5de4f..ddf190672530 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -4022,7 +4022,7 @@ static void gfx_v10_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -7852,6 +7852,8 @@ static int gfx_v10_0_early_init(struct amdgpu_ip_block *ip_block) /* init rlcg reg access ctrl */ gfx_v10_0_init_rlcg_reg_access_ctrl(adev); + amdgpu_init_rlc_reg_funcs(adev); + return gfx_v10_0_init_microcode(adev); } @@ -8658,7 +8660,7 @@ static void gfx_v10_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -8693,7 +8695,7 @@ static void gfx_v10_0_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -8726,9 +8728,9 @@ static void gfx_v10_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -8776,9 +8778,6 @@ static void gfx_v10_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index f856b0cf5bec..2a121df90574 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -133,6 +133,14 @@ MODULE_FIRMWARE("amdgpu/gc_11_5_6_pfp.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_me.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_mec.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_rlc.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_pfp.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_me.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_mec.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_rlc.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_pfp.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_me.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_mec.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_rlc.bin"); static const struct amdgpu_hwip_reg_entry gc_reg_list_11_0[] = { SOC15_REG_ENTRY_STR(GC, 0, regGRBM_STATUS), @@ -546,7 +554,7 @@ static void gfx_v11_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -1128,6 +1136,8 @@ static int gfx_v11_0_gpu_early_init(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->gfx.config.max_hw_contexts = 8; adev->gfx.config.sc_prim_fifo_size_frontend = 0x20; adev->gfx.config.sc_prim_fifo_size_backend = 0x100; @@ -1612,6 +1622,8 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->gfx.me.num_me = 1; adev->gfx.me.num_pipe_per_me = 1; adev->gfx.me.num_queue_per_pipe = 2; @@ -1911,6 +1923,11 @@ static int gfx_v11_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; + adev->gfx.me.use_mmio_for_reset = false; + adev->gfx.mec.use_mmio_for_reset = true; + + mutex_init(&adev->gfx.mec.reset_mutex); + return 0; } @@ -3085,7 +3102,9 @@ static int gfx_v11_0_wait_for_rlc_autoload_complete(struct amdgpu_device *adev) amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 2) || amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 3) || amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 4) || - amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 6)) + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 5, 6) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 7, 0) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(11, 7, 1)) bootload_status = RREG32_SOC15(GC, 0, regRLC_RLCS_BOOTLOAD_STATUS_gc_11_0_1); else @@ -4219,13 +4238,13 @@ static int gfx_v11_0_gfx_mqd_init(struct amdgpu_device *adev, void *m, return 0; } -static int gfx_v11_0_kgq_init_queue(struct amdgpu_ring *ring, bool reset) +static int gfx_v11_0_kgq_init_queue(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; struct v11_gfx_mqd *mqd = ring->mqd_ptr; int mqd_idx = ring - &adev->gfx.gfx_ring[0]; - if (!reset && !amdgpu_in_reset(adev) && !adev->in_suspend) { + if (!amdgpu_in_reset(adev) && !adev->in_suspend) { memset((void *)mqd, 0, sizeof(*mqd)); mutex_lock(&adev->srbm_mutex); soc21_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0); @@ -4252,7 +4271,7 @@ static int gfx_v11_0_cp_async_gfx_ring_resume(struct amdgpu_device *adev) int r, i; for (i = 0; i < adev->gfx.num_gfx_rings; i++) { - r = gfx_v11_0_kgq_init_queue(&adev->gfx.gfx_ring[i], false); + r = gfx_v11_0_kgq_init_queue(&adev->gfx.gfx_ring[i]); if (r) return r; } @@ -4589,13 +4608,13 @@ static int gfx_v11_0_kiq_init_queue(struct amdgpu_ring *ring) return 0; } -static int gfx_v11_0_kcq_init_queue(struct amdgpu_ring *ring, bool reset) +static int gfx_v11_0_kcq_init_queue(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; struct v11_compute_mqd *mqd = ring->mqd_ptr; int mqd_idx = ring - &adev->gfx.compute_ring[0]; - if (!reset && !amdgpu_in_reset(adev) && !adev->in_suspend) { + if (!amdgpu_in_reset(adev) && !adev->in_suspend) { memset((void *)mqd, 0, sizeof(*mqd)); mutex_lock(&adev->srbm_mutex); soc21_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0); @@ -4632,7 +4651,7 @@ static int gfx_v11_0_kcq_resume(struct amdgpu_device *adev) gfx_v11_0_cp_compute_enable(adev, true); for (i = 0; i < adev->gfx.num_compute_rings; i++) { - r = gfx_v11_0_kcq_init_queue(&adev->gfx.compute_ring[i], false); + r = gfx_v11_0_kcq_init_queue(&adev->gfx.compute_ring[i]); if (r) return r; } @@ -5251,38 +5270,12 @@ static int gfx_v11_0_soft_reset(struct amdgpu_ip_block *ip_block) amdgpu_gfx_rlc_exit_safe_mode(adev, 0); - return gfx_v11_0_cp_resume(adev); -} - -static bool gfx_v11_0_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - int i, r; - struct amdgpu_device *adev = ip_block->adev; - struct amdgpu_ring *ring; - long tmo = msecs_to_jiffies(1000); - - for (i = 0; i < adev->gfx.num_gfx_rings; i++) { - ring = &adev->gfx.gfx_ring[i]; - r = amdgpu_ring_test_ib(ring, tmo); - if (r) - return true; - } - - for (i = 0; i < adev->gfx.num_compute_rings; i++) { - ring = &adev->gfx.compute_ring[i]; - r = amdgpu_ring_test_ib(ring, tmo); - if (r) - return true; - } - - return false; -} + r = gfx_v11_0_cp_resume(adev); + if (r) + return r; -static int gfx_v11_0_post_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; /** - * GFX soft reset will impact MES, need resume MES when do GFX soft reset + * GFX soft reset impacts MES, resume MES after GFX soft reset is finished */ return amdgpu_mes_resume(adev, 0); } @@ -5406,6 +5399,8 @@ static int gfx_v11_0_early_init(struct amdgpu_ip_block *ip_block) gfx_v11_0_init_rlcg_reg_access_ctrl(adev); + amdgpu_init_rlc_reg_funcs(adev); + return gfx_v11_0_init_microcode(adev); } @@ -5758,6 +5753,8 @@ static void gfx_v11_cntl_power_gating(struct amdgpu_device *adev, bool enable) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): WREG32_SOC15(GC, 0, regRLC_PG_DELAY_3, RLC_PG_DELAY_3_DEFAULT_GC_11_0_1); break; default: @@ -5798,6 +5795,8 @@ static int gfx_v11_0_set_powergating_state(struct amdgpu_ip_block *ip_block, case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): if (!enable) amdgpu_gfx_off_ctrl(adev, false); @@ -5834,6 +5833,8 @@ static int gfx_v11_0_set_clockgating_state(struct amdgpu_ip_block *ip_block, case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): gfx_v11_0_update_gfx_clock_gating(adev, state == AMD_CG_STATE_GATE); break; @@ -5997,7 +5998,7 @@ static void gfx_v11_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -6032,7 +6033,7 @@ static void gfx_v11_0_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -6065,9 +6066,9 @@ static void gfx_v11_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -6121,9 +6122,6 @@ static void gfx_v11_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | @@ -6510,25 +6508,33 @@ static int gfx_v11_0_eop_irq(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) { u32 doorbell_offset = entry->src_data[0]; - u8 me_id, pipe_id, queue_id; - struct amdgpu_ring *ring; - int i; DRM_DEBUG("IH: CP EOP\n"); - if (adev->enable_mes && doorbell_offset) { - amdgpu_userq_process_fence_irq(adev, doorbell_offset); - } else { - me_id = (entry->ring_id & 0x0c) >> 2; - pipe_id = (entry->ring_id & 0x03) >> 0; - queue_id = (entry->ring_id & 0x70) >> 4; + if (!adev->gfx.disable_kq) { + u8 me_id = (entry->ring_id & 0x0c) >> 2; + u8 pipe_id = (entry->ring_id & 0x03) >> 0; + u8 queue_id = (entry->ring_id & 0x70) >> 4; + struct amdgpu_ring *ring; + int i; switch (me_id) { case 0: - if (pipe_id == 0) - amdgpu_fence_process(&adev->gfx.gfx_ring[0]); - else - amdgpu_fence_process(&adev->gfx.gfx_ring[1]); + /* + * MES splits gfx HQDs per (me,pipe): KGQ owns queue=0, + * userq gfx owns queue>=1 (see amdgpu_mes_get_hqd_mask). + * Require a strict (me,pipe,queue) match so userq gfx + * EOPs fall through to amdgpu_userq_process_fence_irq(). + */ + for (i = 0; i < adev->gfx.num_gfx_rings; i++) { + ring = &adev->gfx.gfx_ring[i]; + if ((ring->me == me_id) && + (ring->pipe == pipe_id) && + (ring->queue == queue_id)) { + amdgpu_fence_process(ring); + return 0; + } + } break; case 1: case 2: @@ -6540,13 +6546,20 @@ static int gfx_v11_0_eop_irq(struct amdgpu_device *adev, */ if ((ring->me == me_id) && (ring->pipe == pipe_id) && - (ring->queue == queue_id)) + (ring->queue == queue_id)) { amdgpu_fence_process(ring); + return 0; + } } break; + default: + break; } } + if (adev->enable_mes && doorbell_offset) + amdgpu_userq_process_fence_irq(adev, doorbell_offset); + return 0; } @@ -6676,22 +6689,29 @@ static int gfx_v11_0_set_priv_inst_fault_state(struct amdgpu_device *adev, static void gfx_v11_0_handle_priv_fault(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) { - u8 me_id, pipe_id, queue_id; - struct amdgpu_ring *ring; - int i; - - me_id = (entry->ring_id & 0x0c) >> 2; - pipe_id = (entry->ring_id & 0x03) >> 0; - queue_id = (entry->ring_id & 0x70) >> 4; + u32 doorbell_offset = entry->src_data[0] & AMDGPU_CTXID0_DOORBELL_ID_MASK; + /* + * Try KQ first by ring_id (HW slot is authoritative). The + * KMD compute_hqd_mask contract guarantees KCQ and user queues + * never share a HW slot. + */ if (!adev->gfx.disable_kq) { + u8 me_id = (entry->ring_id & 0x0c) >> 2; + u8 pipe_id = (entry->ring_id & 0x03) >> 0; + u8 queue_id = (entry->ring_id & 0x70) >> 4; + struct amdgpu_ring *ring; + int i; + switch (me_id) { case 0: for (i = 0; i < adev->gfx.num_gfx_rings; i++) { ring = &adev->gfx.gfx_ring[i]; if (ring->me == me_id && ring->pipe == pipe_id && - ring->queue == queue_id) + ring->queue == queue_id) { drm_sched_fault(&ring->sched); + return; + } } break; case 1: @@ -6699,8 +6719,10 @@ static void gfx_v11_0_handle_priv_fault(struct amdgpu_device *adev, for (i = 0; i < adev->gfx.num_compute_rings; i++) { ring = &adev->gfx.compute_ring[i]; if (ring->me == me_id && ring->pipe == pipe_id && - ring->queue == queue_id) + ring->queue == queue_id) { drm_sched_fault(&ring->sched); + return; + } } break; default: @@ -6708,6 +6730,11 @@ static void gfx_v11_0_handle_priv_fault(struct amdgpu_device *adev, break; } } + + /* No KQ matched: HW slot is a MES-scheduled user queue. */ + if (adev->enable_mes && doorbell_offset) + amdgpu_userq_process_reset_irq(adev, entry->pasid, + doorbell_offset); } static int gfx_v11_0_priv_reg_irq(struct amdgpu_device *adev, @@ -6814,233 +6841,14 @@ static void gfx_v11_0_emit_mem_sync(struct amdgpu_ring *ring) amdgpu_ring_write(ring, gcr_cntl); /* GCR_CNTL */ } -static bool gfx_v11_pipe_reset_support(struct amdgpu_device *adev) -{ - /* Disable the pipe reset until the CPFW fully support it.*/ - dev_warn_once(adev->dev, "The CPFW hasn't support pipe reset yet.\n"); - return false; -} - - -static int gfx_v11_reset_gfx_pipe(struct amdgpu_ring *ring) -{ - struct amdgpu_device *adev = ring->adev; - uint32_t reset_pipe = 0, clean_pipe = 0; - int r; - - if (!gfx_v11_pipe_reset_support(adev)) - return -EOPNOTSUPP; - - gfx_v11_0_set_safe_mode(adev, 0); - mutex_lock(&adev->srbm_mutex); - soc21_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0); - - switch (ring->pipe) { - case 0: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, - PFP_PIPE0_RESET, 1); - reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, - ME_PIPE0_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, - PFP_PIPE0_RESET, 0); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, - ME_PIPE0_RESET, 0); - break; - case 1: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, - PFP_PIPE1_RESET, 1); - reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, - ME_PIPE1_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, - PFP_PIPE1_RESET, 0); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, - ME_PIPE1_RESET, 0); - break; - default: - break; - } - - WREG32_SOC15(GC, 0, regCP_ME_CNTL, reset_pipe); - WREG32_SOC15(GC, 0, regCP_ME_CNTL, clean_pipe); - - r = (RREG32(SOC15_REG_OFFSET(GC, 0, regCP_GFX_RS64_INSTR_PNTR1)) << 2) - - RS64_FW_UC_START_ADDR_LO; - soc21_grbm_select(adev, 0, 0, 0, 0); - mutex_unlock(&adev->srbm_mutex); - gfx_v11_0_unset_safe_mode(adev, 0); - - dev_info(adev->dev, "The ring %s pipe reset to the ME firmware start PC: %s\n", ring->name, - r == 0 ? "successfully" : "failed"); - /* FIXME: Sometimes driver can't cache the ME firmware start PC correctly, - * so the pipe reset status relies on the later gfx ring test result. - */ - return 0; -} - static int gfx_v11_0_reset_kgq(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *timedout_fence) { struct amdgpu_device *adev = ring->adev; - bool use_mmio = false; - int r; - - amdgpu_ring_reset_helper_begin(ring, timedout_fence); - - r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio, 0); - if (r) { - - dev_warn(adev->dev, "reset via MES failed and try pipe reset %d\n", r); - r = gfx_v11_reset_gfx_pipe(ring); - if (r) - return r; - } - - if (use_mmio) { - r = gfx_v11_0_kgq_init_queue(ring, true); - if (r) { - dev_err(adev->dev, "failed to init kgq\n"); - return r; - } - - r = amdgpu_mes_map_legacy_queue(adev, ring, 0); - if (r) { - dev_err(adev->dev, "failed to remap kgq\n"); - return r; - } - } - - return amdgpu_ring_reset_helper_end(ring, timedout_fence); -} - -static int gfx_v11_0_reset_compute_pipe(struct amdgpu_ring *ring) -{ - - struct amdgpu_device *adev = ring->adev; - uint32_t reset_pipe = 0, clean_pipe = 0; - int r; - - if (!gfx_v11_pipe_reset_support(adev)) - return -EOPNOTSUPP; - - gfx_v11_0_set_safe_mode(adev, 0); - mutex_lock(&adev->srbm_mutex); - soc21_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0); - - reset_pipe = RREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL); - clean_pipe = reset_pipe; - - if (adev->gfx.rs64_enable) { - - switch (ring->pipe) { - case 0: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE0_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE0_RESET, 0); - break; - case 1: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE1_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE1_RESET, 0); - break; - case 2: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE2_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE2_RESET, 0); - break; - case 3: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE3_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE3_RESET, 0); - break; - default: - break; - } - WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, reset_pipe); - WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, clean_pipe); - r = (RREG32_SOC15(GC, 0, regCP_MEC_RS64_INSTR_PNTR) << 2) - - RS64_FW_UC_START_ADDR_LO; - } else { - if (ring->me == 1) { - switch (ring->pipe) { - case 0: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE0_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE0_RESET, 0); - break; - case 1: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE1_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE1_RESET, 0); - break; - case 2: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE2_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE2_RESET, 0); - break; - case 3: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE3_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE3_RESET, 0); - break; - default: - break; - } - /* mec1 fw pc: CP_MEC1_INSTR_PNTR */ - } else { - switch (ring->pipe) { - case 0: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME2_PIPE0_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME2_PIPE0_RESET, 0); - break; - case 1: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME2_PIPE1_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME2_PIPE1_RESET, 0); - break; - case 2: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME2_PIPE2_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME2_PIPE2_RESET, 0); - break; - case 3: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME2_PIPE3_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME2_PIPE3_RESET, 0); - break; - default: - break; - } - /* mec2 fw pc: CP:CP_MEC2_INSTR_PNTR */ - } - WREG32_SOC15(GC, 0, regCP_MEC_CNTL, reset_pipe); - WREG32_SOC15(GC, 0, regCP_MEC_CNTL, clean_pipe); - r = RREG32(SOC15_REG_OFFSET(GC, 0, regCP_MEC1_INSTR_PNTR)); - } + bool use_mmio = adev->gfx.me.use_mmio_for_reset; - soc21_grbm_select(adev, 0, 0, 0, 0); - mutex_unlock(&adev->srbm_mutex); - gfx_v11_0_unset_safe_mode(adev, 0); - - dev_info(adev->dev, "The ring %s pipe resets to MEC FW start PC: %s\n", ring->name, - r == 0 ? "successfully" : "failed"); - /*FIXME:Sometimes driver can't cache the MEC firmware start PC correctly, so the pipe - * reset status relies on the compute ring test result. - */ - return 0; + return amdgpu_gfx_mes_reset_queue(ring, vmid, timedout_fence, use_mmio); } static int gfx_v11_0_reset_kcq(struct amdgpu_ring *ring, @@ -7048,30 +6856,8 @@ static int gfx_v11_0_reset_kcq(struct amdgpu_ring *ring, struct amdgpu_fence *timedout_fence) { struct amdgpu_device *adev = ring->adev; - int r = 0; - - amdgpu_ring_reset_helper_begin(ring, timedout_fence); - - r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, true, 0); - if (r) { - dev_warn(adev->dev, "fail(%d) to reset kcq and try pipe reset\n", r); - r = gfx_v11_0_reset_compute_pipe(ring); - if (r) - return r; - } - - r = gfx_v11_0_kcq_init_queue(ring, true); - if (r) { - dev_err(adev->dev, "fail to init kcq\n"); - return r; - } - r = amdgpu_mes_map_legacy_queue(adev, ring, 0); - if (r) { - dev_err(adev->dev, "failed to remap kcq\n"); - return r; - } - return amdgpu_ring_reset_helper_end(ring, timedout_fence); + return amdgpu_gfx_reset_mes_compute(adev, ring, timedout_fence, NULL, NULL, NULL); } static void gfx_v11_ip_print(struct amdgpu_ip_block *ip_block, struct drm_printer *p) @@ -7249,8 +7035,6 @@ static const struct amd_ip_funcs gfx_v11_0_ip_funcs = { .is_idle = gfx_v11_0_is_idle, .wait_for_idle = gfx_v11_0_wait_for_idle, .soft_reset = gfx_v11_0_soft_reset, - .check_soft_reset = gfx_v11_0_check_soft_reset, - .post_soft_reset = gfx_v11_0_post_soft_reset, .set_clockgating_state = gfx_v11_0_set_clockgating_state, .set_powergating_state = gfx_v11_0_set_powergating_state, .get_clockgating_state = gfx_v11_0_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c index f66293fc675e..c765af54669c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_0.c @@ -440,7 +440,7 @@ static void gfx_v12_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -1603,6 +1603,11 @@ static int gfx_v12_0_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; + adev->gfx.me.use_mmio_for_reset = false; + adev->gfx.mec.use_mmio_for_reset = true; + + mutex_init(&adev->gfx.mec.reset_mutex); + return 0; } @@ -3071,13 +3076,13 @@ static int gfx_v12_0_gfx_mqd_init(struct amdgpu_device *adev, void *m, return 0; } -static int gfx_v12_0_kgq_init_queue(struct amdgpu_ring *ring, bool reset) +static int gfx_v12_0_kgq_init_queue(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; struct v12_gfx_mqd *mqd = ring->mqd_ptr; int mqd_idx = ring - &adev->gfx.gfx_ring[0]; - if (!reset && !amdgpu_in_reset(adev) && !adev->in_suspend) { + if (!amdgpu_in_reset(adev) && !adev->in_suspend) { memset((void *)mqd, 0, sizeof(*mqd)); mutex_lock(&adev->srbm_mutex); soc24_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0); @@ -3104,7 +3109,7 @@ static int gfx_v12_0_cp_async_gfx_ring_resume(struct amdgpu_device *adev) int i, r; for (i = 0; i < adev->gfx.num_gfx_rings; i++) { - r = gfx_v12_0_kgq_init_queue(&adev->gfx.gfx_ring[i], false); + r = gfx_v12_0_kgq_init_queue(&adev->gfx.gfx_ring[i]); if (r) return r; } @@ -3441,13 +3446,13 @@ static int gfx_v12_0_kiq_init_queue(struct amdgpu_ring *ring) return 0; } -static int gfx_v12_0_kcq_init_queue(struct amdgpu_ring *ring, bool reset) +static int gfx_v12_0_kcq_init_queue(struct amdgpu_ring *ring) { struct amdgpu_device *adev = ring->adev; struct v12_compute_mqd *mqd = ring->mqd_ptr; int mqd_idx = ring - &adev->gfx.compute_ring[0]; - if (!reset && !amdgpu_in_reset(adev) && !adev->in_suspend) { + if (!amdgpu_in_reset(adev) && !adev->in_suspend) { memset((void *)mqd, 0, sizeof(*mqd)); mutex_lock(&adev->srbm_mutex); soc24_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0); @@ -3485,7 +3490,7 @@ static int gfx_v12_0_kcq_resume(struct amdgpu_device *adev) gfx_v12_0_cp_compute_enable(adev, true); for (i = 0; i < adev->gfx.num_compute_rings; i++) { - r = gfx_v12_0_kcq_init_queue(&adev->gfx.compute_ring[i], false); + r = gfx_v12_0_kcq_init_queue(&adev->gfx.compute_ring[i]); if (r) return r; } @@ -3519,10 +3524,19 @@ static int gfx_v12_0_cp_resume(struct amdgpu_device *adev) gfx_v12_0_cp_gfx_enable(adev, true); } - if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) + if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) { r = amdgpu_mes_kiq_hw_init(adev, 0); - else + /* + * With MES, GFX KIQ ring is owned by the MES and is never + * initialized/used directly by the driver, so it must + * not be left flagged as ready. mes_v12_0_hw_init() clears + * but clear here if MES init fails + */ + if (r) + adev->gfx.kiq[0].ring.sched.ready = false; + } else { r = gfx_v12_0_kiq_resume(adev); + } if (r) return r; @@ -3977,6 +3991,8 @@ static int gfx_v12_0_early_init(struct amdgpu_ip_block *ip_block) gfx_v12_0_init_rlcg_reg_access_ctrl(adev); + amdgpu_init_rlc_reg_funcs(adev); + return gfx_v12_0_init_microcode(adev); } @@ -4493,7 +4509,7 @@ static void gfx_v12_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, control |= ib->length_dw | (vmid << 24); amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -4512,7 +4528,7 @@ static void gfx_v12_0_ring_emit_ib_compute(struct amdgpu_ring *ring, u32 control = INDIRECT_BUFFER_VALID | ib->length_dw | (vmid << 24); amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -4543,9 +4559,9 @@ static void gfx_v12_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -4593,9 +4609,6 @@ static void gfx_v12_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | @@ -4838,25 +4851,33 @@ static int gfx_v12_0_eop_irq(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) { u32 doorbell_offset = entry->src_data[0]; - u8 me_id, pipe_id, queue_id; - struct amdgpu_ring *ring; - int i; DRM_DEBUG("IH: CP EOP\n"); - if (adev->enable_mes && doorbell_offset) { - amdgpu_userq_process_fence_irq(adev, doorbell_offset); - } else { - me_id = (entry->ring_id & 0x0c) >> 2; - pipe_id = (entry->ring_id & 0x03) >> 0; - queue_id = (entry->ring_id & 0x70) >> 4; + if (!adev->gfx.disable_kq) { + u8 me_id = (entry->ring_id & 0x0c) >> 2; + u8 pipe_id = (entry->ring_id & 0x03) >> 0; + u8 queue_id = (entry->ring_id & 0x70) >> 4; + struct amdgpu_ring *ring; + int i; switch (me_id) { case 0: - if (pipe_id == 0) - amdgpu_fence_process(&adev->gfx.gfx_ring[0]); - else - amdgpu_fence_process(&adev->gfx.gfx_ring[1]); + /* + * MES splits gfx HQDs per (me,pipe): KGQ owns queue=0, + * userq gfx owns queue>=1 (see amdgpu_mes_get_hqd_mask). + * Require a strict (me,pipe,queue) match so userq gfx + * EOPs fall through to amdgpu_userq_process_fence_irq(). + */ + for (i = 0; i < adev->gfx.num_gfx_rings; i++) { + ring = &adev->gfx.gfx_ring[i]; + if ((ring->me == me_id) && + (ring->pipe == pipe_id) && + (ring->queue == queue_id)) { + amdgpu_fence_process(ring); + return 0; + } + } break; case 1: case 2: @@ -4868,13 +4889,20 @@ static int gfx_v12_0_eop_irq(struct amdgpu_device *adev, */ if ((ring->me == me_id) && (ring->pipe == pipe_id) && - (ring->queue == queue_id)) + (ring->queue == queue_id)) { amdgpu_fence_process(ring); + return 0; + } } break; + default: + break; } } + if (adev->enable_mes && doorbell_offset) + amdgpu_userq_process_fence_irq(adev, doorbell_offset); + return 0; } @@ -5004,22 +5032,30 @@ static int gfx_v12_0_set_priv_inst_fault_state(struct amdgpu_device *adev, static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev, struct amdgpu_iv_entry *entry) { - u8 me_id, pipe_id, queue_id; - struct amdgpu_ring *ring; - int i; - - me_id = (entry->ring_id & 0x0c) >> 2; - pipe_id = (entry->ring_id & 0x03) >> 0; - queue_id = (entry->ring_id & 0x70) >> 4; + u32 doorbell_offset = entry->src_data[0] & AMDGPU_CTXID0_DOORBELL_ID_MASK; + /* + * Try KQ first by ring_id; UQ as fallback. KCQ and UQ never share + * a HW slot (compute_hqd_mask contract). + */ if (!adev->gfx.disable_kq) { + u8 me_id, pipe_id, queue_id; + struct amdgpu_ring *ring; + int i; + + me_id = (entry->ring_id & 0x0c) >> 2; + pipe_id = (entry->ring_id & 0x03) >> 0; + queue_id = (entry->ring_id & 0x70) >> 4; + switch (me_id) { case 0: for (i = 0; i < adev->gfx.num_gfx_rings; i++) { ring = &adev->gfx.gfx_ring[i]; if (ring->me == me_id && ring->pipe == pipe_id && - ring->queue == queue_id) + ring->queue == queue_id) { drm_sched_fault(&ring->sched); + return; + } } break; case 1: @@ -5027,8 +5063,10 @@ static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev, for (i = 0; i < adev->gfx.num_compute_rings; i++) { ring = &adev->gfx.compute_ring[i]; if (ring->me == me_id && ring->pipe == pipe_id && - ring->queue == queue_id) + ring->queue == queue_id) { drm_sched_fault(&ring->sched); + return; + } } break; default: @@ -5036,6 +5074,11 @@ static void gfx_v12_0_handle_priv_fault(struct amdgpu_device *adev, break; } } + + /* No KQ matched: HW slot is a MES-scheduled user queue. */ + if (adev->enable_mes && doorbell_offset) + amdgpu_userq_process_reset_irq(adev, entry->pasid, + doorbell_offset); } static int gfx_v12_0_priv_reg_irq(struct amdgpu_device *adev, @@ -5240,185 +5283,14 @@ static void gfx_v12_ip_dump(struct amdgpu_ip_block *ip_block) amdgpu_gfx_off_ctrl(adev, true); } -static bool gfx_v12_pipe_reset_support(struct amdgpu_device *adev) -{ - /* Disable the pipe reset until the CPFW fully support it.*/ - dev_warn_once(adev->dev, "The CPFW hasn't support pipe reset yet.\n"); - return false; -} - -static int gfx_v12_reset_gfx_pipe(struct amdgpu_ring *ring) -{ - struct amdgpu_device *adev = ring->adev; - uint32_t reset_pipe = 0, clean_pipe = 0; - int r; - - if (!gfx_v12_pipe_reset_support(adev)) - return -EOPNOTSUPP; - - gfx_v12_0_set_safe_mode(adev, 0); - mutex_lock(&adev->srbm_mutex); - soc24_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0); - - switch (ring->pipe) { - case 0: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, - PFP_PIPE0_RESET, 1); - reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, - ME_PIPE0_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, - PFP_PIPE0_RESET, 0); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, - ME_PIPE0_RESET, 0); - break; - case 1: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, - PFP_PIPE1_RESET, 1); - reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, - ME_PIPE1_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, - PFP_PIPE1_RESET, 0); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, - ME_PIPE1_RESET, 0); - break; - default: - break; - } - - WREG32_SOC15(GC, 0, regCP_ME_CNTL, reset_pipe); - WREG32_SOC15(GC, 0, regCP_ME_CNTL, clean_pipe); - - r = (RREG32(SOC15_REG_OFFSET(GC, 0, regCP_GFX_RS64_INSTR_PNTR1)) << 2) - - RS64_FW_UC_START_ADDR_LO; - soc24_grbm_select(adev, 0, 0, 0, 0); - mutex_unlock(&adev->srbm_mutex); - gfx_v12_0_unset_safe_mode(adev, 0); - - dev_info(adev->dev, "The ring %s pipe reset: %s\n", ring->name, - r == 0 ? "successfully" : "failed"); - /* Sometimes the ME start pc counter can't cache correctly, so the - * PC check only as a reference and pipe reset result rely on the - * later ring test. - */ - return 0; -} - static int gfx_v12_0_reset_kgq(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *timedout_fence) { struct amdgpu_device *adev = ring->adev; - bool use_mmio = false; - int r; - - amdgpu_ring_reset_helper_begin(ring, timedout_fence); - - r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, use_mmio, 0); - if (r) { - dev_warn(adev->dev, "reset via MES failed and try pipe reset %d\n", r); - r = gfx_v12_reset_gfx_pipe(ring); - if (r) - return r; - } - - if (use_mmio) { - r = gfx_v12_0_kgq_init_queue(ring, true); - if (r) { - dev_err(adev->dev, "failed to init kgq\n"); - return r; - } - - r = amdgpu_mes_map_legacy_queue(adev, ring, 0); - if (r) { - dev_err(adev->dev, "failed to remap kgq\n"); - return r; - } - } - - return amdgpu_ring_reset_helper_end(ring, timedout_fence); -} - -static int gfx_v12_0_reset_compute_pipe(struct amdgpu_ring *ring) -{ - struct amdgpu_device *adev = ring->adev; - uint32_t reset_pipe = 0, clean_pipe = 0; - int r = 0; - - if (!gfx_v12_pipe_reset_support(adev)) - return -EOPNOTSUPP; - - gfx_v12_0_set_safe_mode(adev, 0); - mutex_lock(&adev->srbm_mutex); - soc24_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0); - - reset_pipe = RREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL); - clean_pipe = reset_pipe; - - if (adev->gfx.rs64_enable) { - switch (ring->pipe) { - case 0: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE0_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE0_RESET, 0); - break; - case 1: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE1_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE1_RESET, 0); - break; - case 2: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE2_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE2_RESET, 0); - break; - case 3: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE3_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_RS64_CNTL, - MEC_PIPE3_RESET, 0); - break; - default: - break; - } - WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, reset_pipe); - WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, clean_pipe); - r = (RREG32_SOC15(GC, 0, regCP_MEC_RS64_INSTR_PNTR) << 2) - - RS64_FW_UC_START_ADDR_LO; - } else { - switch (ring->pipe) { - case 0: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE0_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE0_RESET, 0); - break; - case 1: - reset_pipe = REG_SET_FIELD(reset_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE1_RESET, 1); - clean_pipe = REG_SET_FIELD(clean_pipe, CP_MEC_CNTL, - MEC_ME1_PIPE1_RESET, 0); - break; - default: - break; - } - WREG32_SOC15(GC, 0, regCP_MEC_CNTL, reset_pipe); - WREG32_SOC15(GC, 0, regCP_MEC_CNTL, clean_pipe); - /* Doesn't find the F32 MEC instruction pointer register, and suppose - * the driver won't run into the F32 mode. - */ - } + bool use_mmio = adev->gfx.me.use_mmio_for_reset; - soc24_grbm_select(adev, 0, 0, 0, 0); - mutex_unlock(&adev->srbm_mutex); - gfx_v12_0_unset_safe_mode(adev, 0); - - dev_info(adev->dev, "The ring %s pipe resets: %s\n", ring->name, - r == 0 ? "successfully" : "failed"); - /* Need the ring test to verify the pipe reset result.*/ - return 0; + return amdgpu_gfx_mes_reset_queue(ring, vmid, timedout_fence, use_mmio); } static int gfx_v12_0_reset_kcq(struct amdgpu_ring *ring, @@ -5426,30 +5298,8 @@ static int gfx_v12_0_reset_kcq(struct amdgpu_ring *ring, struct amdgpu_fence *timedout_fence) { struct amdgpu_device *adev = ring->adev; - int r; - - amdgpu_ring_reset_helper_begin(ring, timedout_fence); - - r = amdgpu_mes_reset_legacy_queue(ring->adev, ring, vmid, true, 0); - if (r) { - dev_warn(adev->dev, "fail(%d) to reset kcq and try pipe reset\n", r); - r = gfx_v12_0_reset_compute_pipe(ring); - if (r) - return r; - } - - r = gfx_v12_0_kcq_init_queue(ring, true); - if (r) { - dev_err(adev->dev, "failed to init kcq\n"); - return r; - } - r = amdgpu_mes_map_legacy_queue(adev, ring, 0); - if (r) { - dev_err(adev->dev, "failed to remap kcq\n"); - return r; - } - return amdgpu_ring_reset_helper_end(ring, timedout_fence); + return amdgpu_gfx_reset_mes_compute(adev, ring, timedout_fence, NULL, NULL, NULL); } static void gfx_v12_0_ring_begin_use(struct amdgpu_ring *ring) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c index 61c3577f829f..e87f1baf5cb6 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v12_1.c @@ -248,7 +248,7 @@ static void gfx_v12_1_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, PACKET3_WAIT_REG_MEM__FUNCTION(3))); /* equal */ if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -1287,6 +1287,8 @@ static int gfx_v12_1_sw_init(struct amdgpu_ip_block *ip_block) if (r) return r; + mutex_init(&adev->gfx.mec.reset_mutex); + return 0; } @@ -2547,10 +2549,19 @@ static int gfx_v12_1_xcc_cp_resume(struct amdgpu_device *adev, uint16_t xcc_mask gfx_v12_1_xcc_cp_compute_enable(adev, true, xcc_id); - if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) + if (adev->enable_mes_kiq && adev->mes.kiq_hw_init) { r = amdgpu_mes_kiq_hw_init(adev, xcc_id); - else + /* + * With MES, GFX KIQ ring is owned by the MES and is never + * initialized/used directly by the driver, so it must + * not be left flagged as ready. mes_v12_0_hw_init() clears + * but clear here if MES init fails + */ + if (r) + adev->gfx.kiq[xcc_id].ring.sched.ready = false; + } else { r = gfx_v12_1_xcc_kiq_resume(adev, xcc_id); + } if (r) return r; @@ -2995,6 +3006,8 @@ static int gfx_v12_1_early_init(struct amdgpu_ip_block *ip_block) gfx_v12_1_init_rlcg_reg_access_ctrl(adev); + amdgpu_init_rlc_reg_funcs(adev); + return gfx_v12_1_init_microcode(adev); } @@ -3433,7 +3446,7 @@ static void gfx_v12_1_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -3466,9 +3479,9 @@ static void gfx_v12_1_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -3515,9 +3528,6 @@ static void gfx_v12_1_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (PACKET3_WRITE_DATA__DST_SEL(5) | PACKET3_WRITE_DATA__WR_CONFIRM(1))); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 130196859ff3..bee2ff6865f9 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -1487,7 +1487,14 @@ static int gfx_v8_0_do_edc_gpr_workarounds(struct amdgpu_device *adev) /* bail if the compute ring is not ready */ if (!ring->sched.ready) - return 0; + return -EBUSY; + + if (amdgpu_in_reset(adev)) { + /* Set preempt condition to execute IB */ + amdgpu_ring_set_preempt_cond_exec(ring, true); + /* Flush HDP cache so the GPU can see the updated COND_EXEC value */ + amdgpu_device_flush_hdp(adev, NULL); + } tmp = RREG32(mmGB_EDC_MODE); WREG32(mmGB_EDC_MODE, 0); @@ -2028,6 +2035,11 @@ static int gfx_v8_0_sw_init(struct amdgpu_ip_block *ip_block) adev->gfx.compute_supported_reset = amdgpu_get_soft_full_reset_mask(&adev->gfx.compute_ring[0]); + if (!amdgpu_sriov_vf(adev) && !adev->debug_disable_ip_block_soft_reset) { + adev->gfx.compute_supported_reset |= AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET; + adev->gfx.gfx_supported_reset |= AMDGPU_RESET_TYPE_IP_BLOCK_SOFT_RESET; + } + return 0; } @@ -4703,12 +4715,14 @@ static int gfx_v8_0_cp_test_all_rings(struct amdgpu_device *adev) if (r) return r; + r = 0; + for (i = 0; i < adev->gfx.num_compute_rings; i++) { ring = &adev->gfx.compute_ring[i]; - amdgpu_ring_test_helper(ring); + r |= amdgpu_ring_test_helper(ring); } - return 0; + return r; } static int gfx_v8_0_cp_resume(struct amdgpu_device *adev) @@ -4868,14 +4882,12 @@ static int gfx_v8_0_hw_fini(struct amdgpu_ip_block *ip_block) } amdgpu_gfx_rlc_enter_safe_mode(adev, 0); - if (!gfx_v8_0_wait_for_idle(ip_block)) - gfx_v8_0_cp_enable(adev, false); - else + if (!amdgpu_in_reset(adev) && gfx_v8_0_wait_for_idle(ip_block)) pr_err("cp is busy, skip halt cp\n"); - if (!gfx_v8_0_wait_for_rlc_idle(adev)) - adev->gfx.rlc.funcs->stop(adev); - else - pr_err("rlc is busy, skip halt rlc\n"); + if (!amdgpu_in_reset(adev) && gfx_v8_0_wait_for_rlc_idle(adev)) + pr_err("rlc is busy\n"); + gfx_v8_0_cp_enable(adev, false); + adev->gfx.rlc.funcs->stop(adev); amdgpu_gfx_rlc_exit_safe_mode(adev, 0); return 0; @@ -4891,128 +4903,49 @@ static int gfx_v8_0_resume(struct amdgpu_ip_block *ip_block) return gfx_v8_0_hw_init(ip_block); } -static bool gfx_v8_0_check_soft_reset(struct amdgpu_ip_block *ip_block) +static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; u32 grbm_soft_reset = 0, srbm_soft_reset = 0; u32 tmp; + int i; + int r; - /* GRBM_STATUS */ - tmp = RREG32(mmGRBM_STATUS); - if (tmp & (GRBM_STATUS__PA_BUSY_MASK | GRBM_STATUS__SC_BUSY_MASK | - GRBM_STATUS__BCI_BUSY_MASK | GRBM_STATUS__SX_BUSY_MASK | - GRBM_STATUS__TA_BUSY_MASK | GRBM_STATUS__VGT_BUSY_MASK | - GRBM_STATUS__DB_BUSY_MASK | GRBM_STATUS__CB_BUSY_MASK | - GRBM_STATUS__GDS_BUSY_MASK | GRBM_STATUS__SPI_BUSY_MASK | - GRBM_STATUS__IA_BUSY_MASK | GRBM_STATUS__IA_BUSY_NO_DMA_MASK | - GRBM_STATUS__CP_BUSY_MASK | GRBM_STATUS__CP_COHERENCY_BUSY_MASK)) { - grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, - GRBM_SOFT_RESET, SOFT_RESET_CP, 1); - grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, - GRBM_SOFT_RESET, SOFT_RESET_GFX, 1); - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, - SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1); - } - - /* GRBM_STATUS2 */ - tmp = RREG32(mmGRBM_STATUS2); - if (REG_GET_FIELD(tmp, GRBM_STATUS2, RLC_BUSY)) - grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, - GRBM_SOFT_RESET, SOFT_RESET_RLC, 1); - - if (REG_GET_FIELD(tmp, GRBM_STATUS2, CPF_BUSY) || - REG_GET_FIELD(tmp, GRBM_STATUS2, CPC_BUSY) || - REG_GET_FIELD(tmp, GRBM_STATUS2, CPG_BUSY)) { - grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, - SOFT_RESET_CPF, 1); - grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, - SOFT_RESET_CPC, 1); - grbm_soft_reset = REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, - SOFT_RESET_CPG, 1); - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, - SOFT_RESET_GRBM, 1); - } - - /* SRBM_STATUS */ - tmp = RREG32(mmSRBM_STATUS); - if (REG_GET_FIELD(tmp, SRBM_STATUS, GRBM_RQ_PENDING)) - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, - SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1); - if (REG_GET_FIELD(tmp, SRBM_STATUS, SEM_BUSY)) - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, - SRBM_SOFT_RESET, SOFT_RESET_SEM, 1); - - if (grbm_soft_reset || srbm_soft_reset) { - adev->gfx.grbm_soft_reset = grbm_soft_reset; - adev->gfx.srbm_soft_reset = srbm_soft_reset; - return true; - } else { - adev->gfx.grbm_soft_reset = 0; - adev->gfx.srbm_soft_reset = 0; - return false; - } -} - -static int gfx_v8_0_pre_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 grbm_soft_reset = 0; - - if ((!adev->gfx.grbm_soft_reset) && - (!adev->gfx.srbm_soft_reset)) - return 0; - - grbm_soft_reset = adev->gfx.grbm_soft_reset; - - /* stop the rlc */ - adev->gfx.rlc.funcs->stop(adev); + grbm_soft_reset = + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_RLC, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_GFX, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CP, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CPF, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CPC, 1) | + REG_SET_FIELD(0, GRBM_SOFT_RESET, SOFT_RESET_CPG, 1); - if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) || - REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_GFX)) - /* Disable GFX parsing/prefetching */ - gfx_v8_0_cp_gfx_enable(adev, false); + srbm_soft_reset = + REG_SET_FIELD(0, SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1) | + REG_SET_FIELD(0, SRBM_SOFT_RESET, SOFT_RESET_SEM, 1); - if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) || - REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPF) || - REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPC) || - REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPG)) { - int i; + for (i = 0; i < adev->gfx.num_compute_rings; i++) { + struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; - for (i = 0; i < adev->gfx.num_compute_rings; i++) { - struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; + mutex_lock(&adev->srbm_mutex); + vi_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0); + gfx_v8_0_deactivate_hqd(adev, 2); + vi_srbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); - mutex_lock(&adev->srbm_mutex); - vi_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0); - gfx_v8_0_deactivate_hqd(adev, 2); - vi_srbm_select(adev, 0, 0, 0, 0); - mutex_unlock(&adev->srbm_mutex); - } - /* Disable MEC parsing/prefetching */ - gfx_v8_0_cp_compute_enable(adev, false); + udelay(50); } - return 0; -} - -static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 grbm_soft_reset = 0, srbm_soft_reset = 0; - u32 tmp; - - if ((!adev->gfx.grbm_soft_reset) && - (!adev->gfx.srbm_soft_reset)) - return 0; - - grbm_soft_reset = adev->gfx.grbm_soft_reset; - srbm_soft_reset = adev->gfx.srbm_soft_reset; + ip_block->version->funcs->set_clockgating_state(ip_block, AMD_CG_STATE_UNGATE); + ip_block->version->funcs->set_powergating_state(ip_block, AMD_PG_STATE_UNGATE); + ip_block->version->funcs->suspend(ip_block); if (grbm_soft_reset || srbm_soft_reset) { tmp = RREG32(mmGMCON_DEBUG); tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_STALL, 1); tmp = REG_SET_FIELD(tmp, GMCON_DEBUG, GFX_CLEAR, 1); WREG32(mmGMCON_DEBUG, tmp); - udelay(50); + + udelay(100); } if (grbm_soft_reset) { @@ -5022,11 +4955,13 @@ static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block) WREG32(mmGRBM_SOFT_RESET, tmp); tmp = RREG32(mmGRBM_SOFT_RESET); - udelay(50); + udelay(100); tmp &= ~grbm_soft_reset; WREG32(mmGRBM_SOFT_RESET, tmp); tmp = RREG32(mmGRBM_SOFT_RESET); + + udelay(100); } if (srbm_soft_reset) { @@ -5036,11 +4971,13 @@ static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block) WREG32(mmSRBM_SOFT_RESET, tmp); tmp = RREG32(mmSRBM_SOFT_RESET); - udelay(50); + udelay(100); tmp &= ~srbm_soft_reset; WREG32(mmSRBM_SOFT_RESET, tmp); tmp = RREG32(mmSRBM_SOFT_RESET); + + udelay(100); } if (grbm_soft_reset || srbm_soft_reset) { @@ -5051,48 +4988,15 @@ static int gfx_v8_0_soft_reset(struct amdgpu_ip_block *ip_block) } /* Wait a little for things to settle down */ - udelay(50); - - return 0; -} - -static int gfx_v8_0_post_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 grbm_soft_reset = 0; - - if ((!adev->gfx.grbm_soft_reset) && - (!adev->gfx.srbm_soft_reset)) - return 0; - - grbm_soft_reset = adev->gfx.grbm_soft_reset; - - if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) || - REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPF) || - REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPC) || - REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CPG)) { - int i; - - for (i = 0; i < adev->gfx.num_compute_rings; i++) { - struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; - - mutex_lock(&adev->srbm_mutex); - vi_srbm_select(adev, ring->me, ring->pipe, ring->queue, 0); - gfx_v8_0_deactivate_hqd(adev, 2); - vi_srbm_select(adev, 0, 0, 0, 0); - mutex_unlock(&adev->srbm_mutex); - } - gfx_v8_0_kiq_resume(adev); - gfx_v8_0_kcq_resume(adev); - } - - if (REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP) || - REG_GET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_GFX)) - gfx_v8_0_cp_gfx_resume(adev); + udelay(100); - gfx_v8_0_cp_test_all_rings(adev); + r = ip_block->version->funcs->resume(ip_block); + r |= ip_block->version->funcs->late_init(ip_block); + if (r) + return r; - adev->gfx.rlc.funcs->start(adev); + ip_block->version->funcs->set_clockgating_state(ip_block, AMD_CG_STATE_GATE); + ip_block->version->funcs->set_powergating_state(ip_block, AMD_PG_STATE_GATE); return 0; } @@ -6256,9 +6160,6 @@ static void gfx_v8_0_ring_emit_fence_compute(struct amdgpu_ring *ring, static void gfx_v8_0_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, u64 seq, unsigned int flags) { - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | @@ -6862,10 +6763,7 @@ static const struct amd_ip_funcs gfx_v8_0_ip_funcs = { .resume = gfx_v8_0_resume, .is_idle = gfx_v8_0_is_idle, .wait_for_idle = gfx_v8_0_wait_for_idle, - .check_soft_reset = gfx_v8_0_check_soft_reset, - .pre_soft_reset = gfx_v8_0_pre_soft_reset, .soft_reset = gfx_v8_0_soft_reset, - .post_soft_reset = gfx_v8_0_post_soft_reset, .set_clockgating_state = gfx_v8_0_set_clockgating_state, .set_powergating_state = gfx_v8_0_set_powergating_state, .get_clockgating_state = gfx_v8_0_get_clockgating_state, @@ -6926,10 +6824,12 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_compute = { .get_wptr = gfx_v8_0_ring_get_wptr_compute, .set_wptr = gfx_v8_0_ring_set_wptr_compute, .emit_frame_size = + 5 + /* gfx_v8_0_ring_emit_init_cond_exec (from amdgpu_ib_schedule) */ 20 + /* gfx_v8_0_ring_emit_gds_switch */ 7 + /* gfx_v8_0_ring_emit_hdp_flush */ 5 + /* hdp_invalidate */ 7 + /* gfx_v8_0_ring_emit_pipeline_sync */ + 5 + /* gfx_v8_0_ring_emit_init_cond_exec (from amdgpu_vm_flush) */ VI_FLUSH_GPU_TLB_NUM_WREG * 5 + 7 + /* gfx_v8_0_ring_emit_vm_flush */ 7 + 7 + 7 + /* gfx_v8_0_ring_emit_fence_compute x3 for user fence, vm fence */ 7 + /* gfx_v8_0_emit_mem_sync_compute */ @@ -6950,6 +6850,7 @@ static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_compute = { .soft_recovery = gfx_v8_0_ring_soft_recovery, .emit_mem_sync = gfx_v8_0_emit_mem_sync_compute, .emit_wave_limit = gfx_v8_0_emit_wave_limit, + .init_cond_exec = gfx_v8_0_ring_emit_init_cond_exec, }; static const struct amdgpu_ring_funcs gfx_v8_0_ring_funcs_kiq = { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 81a759a98725..9f81fd715418 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -1183,7 +1183,7 @@ static void gfx_v9_0_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -4875,6 +4875,8 @@ static int gfx_v9_0_early_init(struct amdgpu_ip_block *ip_block) /* init rlcg reg access ctrl */ gfx_v9_0_init_rlcg_reg_access_ctrl(adev); + amdgpu_init_rlc_reg_funcs(adev); + return gfx_v9_0_init_microcode(adev); } @@ -5474,7 +5476,7 @@ static void gfx_v9_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, header); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -5570,7 +5572,7 @@ static void gfx_v9_0_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -5611,9 +5613,9 @@ static void gfx_v9_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index 510266ba0c38..b89cbc2df951 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -39,7 +39,6 @@ #include "gfx_v9_4_3.h" #include "gfx_v9_4_3_cleaner_shader.h" #include "amdgpu_xcp.h" -#include "amdgpu_aca.h" MODULE_FIRMWARE("amdgpu/gc_9_4_3_mec.bin"); MODULE_FIRMWARE("amdgpu/gc_9_4_4_mec.bin"); @@ -405,7 +404,7 @@ static void gfx_v9_4_3_wait_reg_mem(struct amdgpu_ring *ring, int eng_sel, WAIT_REG_MEM_ENGINE(eng_sel))); if (mem_space) - BUG_ON(addr0 & 0x3); /* Dword align */ + WARN_ON(addr0 & 0x3); /* Dword align */ amdgpu_ring_write(ring, addr0); amdgpu_ring_write(ring, addr1); amdgpu_ring_write(ring, ref); @@ -851,73 +850,6 @@ static const struct amdgpu_gfx_funcs gfx_v9_4_3_gfx_funcs = { .get_hdp_flush_mask = &amdgpu_gfx_get_hdp_flush_mask, }; -static int gfx_v9_4_3_aca_bank_parser(struct aca_handle *handle, - struct aca_bank *bank, enum aca_smu_type type, - void *data) -{ - struct aca_bank_info info; - u64 misc0; - u32 instlo; - int ret; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return ret; - - /* NOTE: overwrite info.die_id with xcd id for gfx */ - instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]); - instlo &= GENMASK(31, 1); - info.die_id = instlo == mmSMNAID_XCD0_MCA_SMU ? 0 : 1; - - misc0 = bank->regs[ACA_REG_IDX_MISC0]; - - switch (type) { - case ACA_SMU_TYPE_UE: - bank->aca_err_type = ACA_ERROR_TYPE_UE; - ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, 1ULL); - break; - case ACA_SMU_TYPE_CE: - bank->aca_err_type = ACA_ERROR_TYPE_CE; - ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, - ACA_REG__MISC0__ERRCNT(misc0)); - break; - default: - return -EINVAL; - } - - return ret; -} - -static bool gfx_v9_4_3_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - u32 instlo; - - instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]); - instlo &= GENMASK(31, 1); - switch (instlo) { - case mmSMNAID_XCD0_MCA_SMU: - case mmSMNAID_XCD1_MCA_SMU: - case mmSMNXCD_XCD0_MCA_SMU: - return true; - default: - break; - } - - return false; -} - -static const struct aca_bank_ops gfx_v9_4_3_aca_bank_ops = { - .aca_bank_parser = gfx_v9_4_3_aca_bank_parser, - .aca_bank_is_valid = gfx_v9_4_3_aca_bank_is_valid, -}; - -static const struct aca_info gfx_v9_4_3_aca_info = { - .hwip = ACA_HWIP_TYPE_SMU, - .mask = ACA_ERROR_UE_MASK | ACA_ERROR_CE_MASK, - .bank_ops = &gfx_v9_4_3_aca_bank_ops, -}; - static int gfx_v9_4_3_gpu_early_init(struct amdgpu_device *adev) { adev->gfx.funcs = &gfx_v9_4_3_gfx_funcs; @@ -1107,22 +1039,24 @@ static int gfx_v9_4_3_sw_init(struct amdgpu_ip_block *ip_block) /* set up the compute queues - allocate horizontally across pipes */ for (xcc_id = 0; xcc_id < num_xcc; xcc_id++) { ring_id = 0; - for (i = 0; i < adev->gfx.mec.num_mec; ++i) { - for (j = 0; j < adev->gfx.mec.num_queue_per_pipe; j++) { - for (k = 0; k < adev->gfx.mec.num_pipe_per_mec; - k++) { - if (!amdgpu_gfx_is_mec_queue_enabled( - adev, xcc_id, i, k, j)) - continue; - - r = gfx_v9_4_3_compute_ring_init(adev, - ring_id, - xcc_id, - i, k, j); - if (r) - return r; - - ring_id++; + if (!adev->gfx.disable_kq) { + for (i = 0; i < adev->gfx.mec.num_mec; ++i) { + for (j = 0; j < adev->gfx.mec.num_queue_per_pipe; j++) { + for (k = 0; k < adev->gfx.mec.num_pipe_per_mec; + k++) { + if (!amdgpu_gfx_is_mec_queue_enabled( + adev, xcc_id, i, k, j)) + continue; + + r = gfx_v9_4_3_compute_ring_init(adev, + ring_id, + xcc_id, + i, k, j); + if (r) + return r; + + ring_id++; + } } } } @@ -2350,6 +2284,65 @@ static void gfx_v9_4_3_xcc_fini(struct amdgpu_device *adev, int xcc_id) gfx_v9_4_3_xcc_cp_compute_enable(adev, false, xcc_id); } +static int gfx_v9_4_3_set_userq_eop_interrupts(struct amdgpu_device *adev, + bool enable) +{ + int num_xcc = NUM_XCC(adev->gfx.xcc_mask); + unsigned int irq_type; + int m, p, xcc_id, r; + + if (adev->gfx.disable_kq) { + for (xcc_id = 0; xcc_id < num_xcc; xcc_id++) { + for (m = 0; m < adev->gfx.mec.num_mec; ++m) { + for (p = 0; p < adev->gfx.mec.num_pipe_per_mec; p++) { + irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP + + (m * adev->gfx.mec.num_pipe_per_mec) + + p; + + if (enable) + r = amdgpu_irq_get(adev, &adev->gfx.eop_irq, + irq_type); + else + r = amdgpu_irq_put(adev, &adev->gfx.eop_irq, + irq_type); + if (r) { + if (!enable) + return r; + goto err_compute; + } + } + } + } + } + + return 0; + +err_compute: + for (p--; p >= 0; p--) { + irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP + + (m * adev->gfx.mec.num_pipe_per_mec) + p; + amdgpu_irq_put(adev, &adev->gfx.eop_irq, irq_type); + } + for (m--; m >= 0; m--) { + for (p = adev->gfx.mec.num_pipe_per_mec - 1; p >= 0; p--) { + irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP + + (m * adev->gfx.mec.num_pipe_per_mec) + p; + amdgpu_irq_put(adev, &adev->gfx.eop_irq, irq_type); + } + } + for (xcc_id--; xcc_id >= 0; xcc_id--) { + for (m = adev->gfx.mec.num_mec - 1; m <= 0; m--) { + for (p = adev->gfx.mec.num_pipe_per_mec - 1; p >= 0; p--) { + irq_type = AMDGPU_CP_IRQ_COMPUTE_MEC1_PIPE0_EOP + + (m * adev->gfx.mec.num_pipe_per_mec) + p; + amdgpu_irq_put(adev, &adev->gfx.eop_irq, irq_type); + } + } + } + + return r; +} + static int gfx_v9_4_3_hw_init(struct amdgpu_ip_block *ip_block) { int r; @@ -2382,9 +2375,14 @@ static int gfx_v9_4_3_hw_init(struct amdgpu_ip_block *ip_block) r = amdgpu_irq_get(adev, &adev->gfx.bad_op_irq, 0); if (r) goto err_bad_op; + r = gfx_v9_4_3_set_userq_eop_interrupts(adev, true); + if (r) + goto err_bad_eop; return 0; +err_bad_eop: + amdgpu_irq_put(adev, &adev->gfx.bad_op_irq, 0); err_bad_op: amdgpu_irq_put(adev, &adev->gfx.priv_inst_irq, 0); err_priv_inst: @@ -2467,6 +2465,7 @@ static int gfx_v9_4_3_hw_fini(struct amdgpu_ip_block *ip_block) amdgpu_irq_put(adev, &adev->gfx.bad_op_irq, 0); amdgpu_irq_put(adev, &adev->gfx.priv_inst_irq, 0); amdgpu_irq_put(adev, &adev->gfx.priv_reg_irq, 0); + gfx_v9_4_3_set_userq_eop_interrupts(adev, false); num_xcc = NUM_XCC(adev->gfx.xcc_mask); for (i = 0; i < num_xcc; i++) { @@ -2612,8 +2611,24 @@ static int gfx_v9_4_3_early_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - adev->gfx.num_compute_rings = min(amdgpu_gfx_get_num_kcq(adev), - AMDGPU_MAX_COMPUTE_RINGS); + switch (amdgpu_user_queue) { + case -1: + case 0: + default: + adev->gfx.disable_kq = false; + adev->gfx.disable_uq = true; + break; + case 2: + adev->gfx.disable_kq = true; + adev->gfx.disable_uq = true; + break; + } + + if (adev->gfx.disable_kq) + adev->gfx.num_compute_rings = 0; + else + adev->gfx.num_compute_rings = min(amdgpu_gfx_get_num_kcq(adev), + AMDGPU_MAX_COMPUTE_RINGS); gfx_v9_4_3_set_kiq_pm4_funcs(adev); gfx_v9_4_3_set_ring_funcs(adev); gfx_v9_4_3_set_irq_funcs(adev); @@ -2623,6 +2638,8 @@ static int gfx_v9_4_3_early_init(struct amdgpu_ip_block *ip_block) /* init rlcg reg access ctrl */ gfx_v9_4_3_init_rlcg_reg_access_ctrl(adev); + amdgpu_init_rlc_reg_funcs(adev); + return gfx_v9_4_3_init_microcode(adev); } @@ -2944,7 +2961,7 @@ static void gfx_v9_4_3_ring_emit_ib_compute(struct amdgpu_ring *ring, } amdgpu_ring_write(ring, PACKET3(PACKET3_INDIRECT_BUFFER, 2)); - BUG_ON(ib->gpu_addr & 0x3); /* Dword align */ + WARN_ON(ib->gpu_addr & 0x3); /* Dword align */ amdgpu_ring_write(ring, #ifdef __BIG_ENDIAN (2 << 0) | @@ -2978,9 +2995,9 @@ static void gfx_v9_4_3_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, * aligned if only send 32bit data low (discard data high) */ if (write64bit) - BUG_ON(addr & 0x7); + WARN_ON(addr & 0x7); else - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -3040,9 +3057,6 @@ static void gfx_v9_4_3_ring_emit_fence_kiq(struct amdgpu_ring *ring, u64 addr, { struct amdgpu_device *adev = ring->adev; - /* we only allocate 32bit for each seq wb address */ - BUG_ON(flags & AMDGPU_FENCE_FLAG_64BIT); - /* write fence seq to the "addr" */ amdgpu_ring_write(ring, PACKET3(PACKET3_WRITE_DATA, 3)); amdgpu_ring_write(ring, (WRITE_DATA_ENGINE_SEL(0) | @@ -3712,872 +3726,6 @@ pipe_reset: return amdgpu_ring_reset_helper_end(ring, timedout_fence); } -enum amdgpu_gfx_cp_ras_mem_id { - AMDGPU_GFX_CP_MEM1 = 1, - AMDGPU_GFX_CP_MEM2, - AMDGPU_GFX_CP_MEM3, - AMDGPU_GFX_CP_MEM4, - AMDGPU_GFX_CP_MEM5, -}; - -enum amdgpu_gfx_gcea_ras_mem_id { - AMDGPU_GFX_GCEA_IOWR_CMDMEM = 4, - AMDGPU_GFX_GCEA_IORD_CMDMEM, - AMDGPU_GFX_GCEA_GMIWR_CMDMEM, - AMDGPU_GFX_GCEA_GMIRD_CMDMEM, - AMDGPU_GFX_GCEA_DRAMWR_CMDMEM, - AMDGPU_GFX_GCEA_DRAMRD_CMDMEM, - AMDGPU_GFX_GCEA_MAM_DMEM0, - AMDGPU_GFX_GCEA_MAM_DMEM1, - AMDGPU_GFX_GCEA_MAM_DMEM2, - AMDGPU_GFX_GCEA_MAM_DMEM3, - AMDGPU_GFX_GCEA_MAM_AMEM0, - AMDGPU_GFX_GCEA_MAM_AMEM1, - AMDGPU_GFX_GCEA_MAM_AMEM2, - AMDGPU_GFX_GCEA_MAM_AMEM3, - AMDGPU_GFX_GCEA_MAM_AFLUSH_BUFFER, - AMDGPU_GFX_GCEA_WRET_TAGMEM, - AMDGPU_GFX_GCEA_RRET_TAGMEM, - AMDGPU_GFX_GCEA_IOWR_DATAMEM, - AMDGPU_GFX_GCEA_GMIWR_DATAMEM, - AMDGPU_GFX_GCEA_DRAM_DATAMEM, -}; - -enum amdgpu_gfx_gc_cane_ras_mem_id { - AMDGPU_GFX_GC_CANE_MEM0 = 0, -}; - -enum amdgpu_gfx_gcutcl2_ras_mem_id { - AMDGPU_GFX_GCUTCL2_MEM2P512X95 = 160, -}; - -enum amdgpu_gfx_gds_ras_mem_id { - AMDGPU_GFX_GDS_MEM0 = 0, -}; - -enum amdgpu_gfx_lds_ras_mem_id { - AMDGPU_GFX_LDS_BANK0 = 0, - AMDGPU_GFX_LDS_BANK1, - AMDGPU_GFX_LDS_BANK2, - AMDGPU_GFX_LDS_BANK3, - AMDGPU_GFX_LDS_BANK4, - AMDGPU_GFX_LDS_BANK5, - AMDGPU_GFX_LDS_BANK6, - AMDGPU_GFX_LDS_BANK7, - AMDGPU_GFX_LDS_BANK8, - AMDGPU_GFX_LDS_BANK9, - AMDGPU_GFX_LDS_BANK10, - AMDGPU_GFX_LDS_BANK11, - AMDGPU_GFX_LDS_BANK12, - AMDGPU_GFX_LDS_BANK13, - AMDGPU_GFX_LDS_BANK14, - AMDGPU_GFX_LDS_BANK15, - AMDGPU_GFX_LDS_BANK16, - AMDGPU_GFX_LDS_BANK17, - AMDGPU_GFX_LDS_BANK18, - AMDGPU_GFX_LDS_BANK19, - AMDGPU_GFX_LDS_BANK20, - AMDGPU_GFX_LDS_BANK21, - AMDGPU_GFX_LDS_BANK22, - AMDGPU_GFX_LDS_BANK23, - AMDGPU_GFX_LDS_BANK24, - AMDGPU_GFX_LDS_BANK25, - AMDGPU_GFX_LDS_BANK26, - AMDGPU_GFX_LDS_BANK27, - AMDGPU_GFX_LDS_BANK28, - AMDGPU_GFX_LDS_BANK29, - AMDGPU_GFX_LDS_BANK30, - AMDGPU_GFX_LDS_BANK31, - AMDGPU_GFX_LDS_SP_BUFFER_A, - AMDGPU_GFX_LDS_SP_BUFFER_B, -}; - -enum amdgpu_gfx_rlc_ras_mem_id { - AMDGPU_GFX_RLC_GPMF32 = 1, - AMDGPU_GFX_RLC_RLCVF32, - AMDGPU_GFX_RLC_SCRATCH, - AMDGPU_GFX_RLC_SRM_ARAM, - AMDGPU_GFX_RLC_SRM_DRAM, - AMDGPU_GFX_RLC_TCTAG, - AMDGPU_GFX_RLC_SPM_SE, - AMDGPU_GFX_RLC_SPM_GRBMT, -}; - -enum amdgpu_gfx_sp_ras_mem_id { - AMDGPU_GFX_SP_SIMDID0 = 0, -}; - -enum amdgpu_gfx_spi_ras_mem_id { - AMDGPU_GFX_SPI_MEM0 = 0, - AMDGPU_GFX_SPI_MEM1, - AMDGPU_GFX_SPI_MEM2, - AMDGPU_GFX_SPI_MEM3, -}; - -enum amdgpu_gfx_sqc_ras_mem_id { - AMDGPU_GFX_SQC_INST_CACHE_A = 100, - AMDGPU_GFX_SQC_INST_CACHE_B = 101, - AMDGPU_GFX_SQC_INST_CACHE_TAG_A = 102, - AMDGPU_GFX_SQC_INST_CACHE_TAG_B = 103, - AMDGPU_GFX_SQC_INST_CACHE_MISS_FIFO_A = 104, - AMDGPU_GFX_SQC_INST_CACHE_MISS_FIFO_B = 105, - AMDGPU_GFX_SQC_INST_CACHE_GATCL1_MISS_FIFO_A = 106, - AMDGPU_GFX_SQC_INST_CACHE_GATCL1_MISS_FIFO_B = 107, - AMDGPU_GFX_SQC_DATA_CACHE_A = 200, - AMDGPU_GFX_SQC_DATA_CACHE_B = 201, - AMDGPU_GFX_SQC_DATA_CACHE_TAG_A = 202, - AMDGPU_GFX_SQC_DATA_CACHE_TAG_B = 203, - AMDGPU_GFX_SQC_DATA_CACHE_MISS_FIFO_A = 204, - AMDGPU_GFX_SQC_DATA_CACHE_MISS_FIFO_B = 205, - AMDGPU_GFX_SQC_DATA_CACHE_HIT_FIFO_A = 206, - AMDGPU_GFX_SQC_DATA_CACHE_HIT_FIFO_B = 207, - AMDGPU_GFX_SQC_DIRTY_BIT_A = 208, - AMDGPU_GFX_SQC_DIRTY_BIT_B = 209, - AMDGPU_GFX_SQC_WRITE_DATA_BUFFER_CU0 = 210, - AMDGPU_GFX_SQC_WRITE_DATA_BUFFER_CU1 = 211, - AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_DATA_CACHE_A = 212, - AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_DATA_CACHE_B = 213, - AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_INST_CACHE = 108, -}; - -enum amdgpu_gfx_sq_ras_mem_id { - AMDGPU_GFX_SQ_SGPR_MEM0 = 0, - AMDGPU_GFX_SQ_SGPR_MEM1, - AMDGPU_GFX_SQ_SGPR_MEM2, - AMDGPU_GFX_SQ_SGPR_MEM3, -}; - -enum amdgpu_gfx_ta_ras_mem_id { - AMDGPU_GFX_TA_FS_AFIFO_RAM_LO = 1, - AMDGPU_GFX_TA_FS_AFIFO_RAM_HI, - AMDGPU_GFX_TA_FS_CFIFO_RAM, - AMDGPU_GFX_TA_FSX_LFIFO, - AMDGPU_GFX_TA_FS_DFIFO_RAM, -}; - -enum amdgpu_gfx_tcc_ras_mem_id { - AMDGPU_GFX_TCC_MEM1 = 1, -}; - -enum amdgpu_gfx_tca_ras_mem_id { - AMDGPU_GFX_TCA_MEM1 = 1, -}; - -enum amdgpu_gfx_tci_ras_mem_id { - AMDGPU_GFX_TCIW_MEM = 1, -}; - -enum amdgpu_gfx_tcp_ras_mem_id { - AMDGPU_GFX_TCP_LFIFO0 = 1, - AMDGPU_GFX_TCP_SET0BANK0_RAM, - AMDGPU_GFX_TCP_SET0BANK1_RAM, - AMDGPU_GFX_TCP_SET0BANK2_RAM, - AMDGPU_GFX_TCP_SET0BANK3_RAM, - AMDGPU_GFX_TCP_SET1BANK0_RAM, - AMDGPU_GFX_TCP_SET1BANK1_RAM, - AMDGPU_GFX_TCP_SET1BANK2_RAM, - AMDGPU_GFX_TCP_SET1BANK3_RAM, - AMDGPU_GFX_TCP_SET2BANK0_RAM, - AMDGPU_GFX_TCP_SET2BANK1_RAM, - AMDGPU_GFX_TCP_SET2BANK2_RAM, - AMDGPU_GFX_TCP_SET2BANK3_RAM, - AMDGPU_GFX_TCP_SET3BANK0_RAM, - AMDGPU_GFX_TCP_SET3BANK1_RAM, - AMDGPU_GFX_TCP_SET3BANK2_RAM, - AMDGPU_GFX_TCP_SET3BANK3_RAM, - AMDGPU_GFX_TCP_VM_FIFO, - AMDGPU_GFX_TCP_DB_TAGRAM0, - AMDGPU_GFX_TCP_DB_TAGRAM1, - AMDGPU_GFX_TCP_DB_TAGRAM2, - AMDGPU_GFX_TCP_DB_TAGRAM3, - AMDGPU_GFX_TCP_UTCL1_LFIFO_PROBE0, - AMDGPU_GFX_TCP_UTCL1_LFIFO_PROBE1, - AMDGPU_GFX_TCP_CMD_FIFO, -}; - -enum amdgpu_gfx_td_ras_mem_id { - AMDGPU_GFX_TD_UTD_CS_FIFO_MEM = 1, - AMDGPU_GFX_TD_UTD_SS_FIFO_LO_MEM, - AMDGPU_GFX_TD_UTD_SS_FIFO_HI_MEM, -}; - -enum amdgpu_gfx_tcx_ras_mem_id { - AMDGPU_GFX_TCX_FIFOD0 = 0, - AMDGPU_GFX_TCX_FIFOD1, - AMDGPU_GFX_TCX_FIFOD2, - AMDGPU_GFX_TCX_FIFOD3, - AMDGPU_GFX_TCX_FIFOD4, - AMDGPU_GFX_TCX_FIFOD5, - AMDGPU_GFX_TCX_FIFOD6, - AMDGPU_GFX_TCX_FIFOD7, - AMDGPU_GFX_TCX_FIFOB0, - AMDGPU_GFX_TCX_FIFOB1, - AMDGPU_GFX_TCX_FIFOB2, - AMDGPU_GFX_TCX_FIFOB3, - AMDGPU_GFX_TCX_FIFOB4, - AMDGPU_GFX_TCX_FIFOB5, - AMDGPU_GFX_TCX_FIFOB6, - AMDGPU_GFX_TCX_FIFOB7, - AMDGPU_GFX_TCX_FIFOA0, - AMDGPU_GFX_TCX_FIFOA1, - AMDGPU_GFX_TCX_FIFOA2, - AMDGPU_GFX_TCX_FIFOA3, - AMDGPU_GFX_TCX_FIFOA4, - AMDGPU_GFX_TCX_FIFOA5, - AMDGPU_GFX_TCX_FIFOA6, - AMDGPU_GFX_TCX_FIFOA7, - AMDGPU_GFX_TCX_CFIFO0, - AMDGPU_GFX_TCX_CFIFO1, - AMDGPU_GFX_TCX_CFIFO2, - AMDGPU_GFX_TCX_CFIFO3, - AMDGPU_GFX_TCX_CFIFO4, - AMDGPU_GFX_TCX_CFIFO5, - AMDGPU_GFX_TCX_CFIFO6, - AMDGPU_GFX_TCX_CFIFO7, - AMDGPU_GFX_TCX_FIFO_ACKB0, - AMDGPU_GFX_TCX_FIFO_ACKB1, - AMDGPU_GFX_TCX_FIFO_ACKB2, - AMDGPU_GFX_TCX_FIFO_ACKB3, - AMDGPU_GFX_TCX_FIFO_ACKB4, - AMDGPU_GFX_TCX_FIFO_ACKB5, - AMDGPU_GFX_TCX_FIFO_ACKB6, - AMDGPU_GFX_TCX_FIFO_ACKB7, - AMDGPU_GFX_TCX_FIFO_ACKD0, - AMDGPU_GFX_TCX_FIFO_ACKD1, - AMDGPU_GFX_TCX_FIFO_ACKD2, - AMDGPU_GFX_TCX_FIFO_ACKD3, - AMDGPU_GFX_TCX_FIFO_ACKD4, - AMDGPU_GFX_TCX_FIFO_ACKD5, - AMDGPU_GFX_TCX_FIFO_ACKD6, - AMDGPU_GFX_TCX_FIFO_ACKD7, - AMDGPU_GFX_TCX_DST_FIFOA0, - AMDGPU_GFX_TCX_DST_FIFOA1, - AMDGPU_GFX_TCX_DST_FIFOA2, - AMDGPU_GFX_TCX_DST_FIFOA3, - AMDGPU_GFX_TCX_DST_FIFOA4, - AMDGPU_GFX_TCX_DST_FIFOA5, - AMDGPU_GFX_TCX_DST_FIFOA6, - AMDGPU_GFX_TCX_DST_FIFOA7, - AMDGPU_GFX_TCX_DST_FIFOB0, - AMDGPU_GFX_TCX_DST_FIFOB1, - AMDGPU_GFX_TCX_DST_FIFOB2, - AMDGPU_GFX_TCX_DST_FIFOB3, - AMDGPU_GFX_TCX_DST_FIFOB4, - AMDGPU_GFX_TCX_DST_FIFOB5, - AMDGPU_GFX_TCX_DST_FIFOB6, - AMDGPU_GFX_TCX_DST_FIFOB7, - AMDGPU_GFX_TCX_DST_FIFOD0, - AMDGPU_GFX_TCX_DST_FIFOD1, - AMDGPU_GFX_TCX_DST_FIFOD2, - AMDGPU_GFX_TCX_DST_FIFOD3, - AMDGPU_GFX_TCX_DST_FIFOD4, - AMDGPU_GFX_TCX_DST_FIFOD5, - AMDGPU_GFX_TCX_DST_FIFOD6, - AMDGPU_GFX_TCX_DST_FIFOD7, - AMDGPU_GFX_TCX_DST_FIFO_ACKB0, - AMDGPU_GFX_TCX_DST_FIFO_ACKB1, - AMDGPU_GFX_TCX_DST_FIFO_ACKB2, - AMDGPU_GFX_TCX_DST_FIFO_ACKB3, - AMDGPU_GFX_TCX_DST_FIFO_ACKB4, - AMDGPU_GFX_TCX_DST_FIFO_ACKB5, - AMDGPU_GFX_TCX_DST_FIFO_ACKB6, - AMDGPU_GFX_TCX_DST_FIFO_ACKB7, - AMDGPU_GFX_TCX_DST_FIFO_ACKD0, - AMDGPU_GFX_TCX_DST_FIFO_ACKD1, - AMDGPU_GFX_TCX_DST_FIFO_ACKD2, - AMDGPU_GFX_TCX_DST_FIFO_ACKD3, - AMDGPU_GFX_TCX_DST_FIFO_ACKD4, - AMDGPU_GFX_TCX_DST_FIFO_ACKD5, - AMDGPU_GFX_TCX_DST_FIFO_ACKD6, - AMDGPU_GFX_TCX_DST_FIFO_ACKD7, -}; - -enum amdgpu_gfx_atc_l2_ras_mem_id { - AMDGPU_GFX_ATC_L2_MEM0 = 0, -}; - -enum amdgpu_gfx_utcl2_ras_mem_id { - AMDGPU_GFX_UTCL2_MEM0 = 0, -}; - -enum amdgpu_gfx_vml2_ras_mem_id { - AMDGPU_GFX_VML2_MEM0 = 0, -}; - -enum amdgpu_gfx_vml2_walker_ras_mem_id { - AMDGPU_GFX_VML2_WALKER_MEM0 = 0, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_cp_mem_list[] = { - {AMDGPU_GFX_CP_MEM1, "CP_MEM1"}, - {AMDGPU_GFX_CP_MEM2, "CP_MEM2"}, - {AMDGPU_GFX_CP_MEM3, "CP_MEM3"}, - {AMDGPU_GFX_CP_MEM4, "CP_MEM4"}, - {AMDGPU_GFX_CP_MEM5, "CP_MEM5"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_gcea_mem_list[] = { - {AMDGPU_GFX_GCEA_IOWR_CMDMEM, "GCEA_IOWR_CMDMEM"}, - {AMDGPU_GFX_GCEA_IORD_CMDMEM, "GCEA_IORD_CMDMEM"}, - {AMDGPU_GFX_GCEA_GMIWR_CMDMEM, "GCEA_GMIWR_CMDMEM"}, - {AMDGPU_GFX_GCEA_GMIRD_CMDMEM, "GCEA_GMIRD_CMDMEM"}, - {AMDGPU_GFX_GCEA_DRAMWR_CMDMEM, "GCEA_DRAMWR_CMDMEM"}, - {AMDGPU_GFX_GCEA_DRAMRD_CMDMEM, "GCEA_DRAMRD_CMDMEM"}, - {AMDGPU_GFX_GCEA_MAM_DMEM0, "GCEA_MAM_DMEM0"}, - {AMDGPU_GFX_GCEA_MAM_DMEM1, "GCEA_MAM_DMEM1"}, - {AMDGPU_GFX_GCEA_MAM_DMEM2, "GCEA_MAM_DMEM2"}, - {AMDGPU_GFX_GCEA_MAM_DMEM3, "GCEA_MAM_DMEM3"}, - {AMDGPU_GFX_GCEA_MAM_AMEM0, "GCEA_MAM_AMEM0"}, - {AMDGPU_GFX_GCEA_MAM_AMEM1, "GCEA_MAM_AMEM1"}, - {AMDGPU_GFX_GCEA_MAM_AMEM2, "GCEA_MAM_AMEM2"}, - {AMDGPU_GFX_GCEA_MAM_AMEM3, "GCEA_MAM_AMEM3"}, - {AMDGPU_GFX_GCEA_MAM_AFLUSH_BUFFER, "GCEA_MAM_AFLUSH_BUFFER"}, - {AMDGPU_GFX_GCEA_WRET_TAGMEM, "GCEA_WRET_TAGMEM"}, - {AMDGPU_GFX_GCEA_RRET_TAGMEM, "GCEA_RRET_TAGMEM"}, - {AMDGPU_GFX_GCEA_IOWR_DATAMEM, "GCEA_IOWR_DATAMEM"}, - {AMDGPU_GFX_GCEA_GMIWR_DATAMEM, "GCEA_GMIWR_DATAMEM"}, - {AMDGPU_GFX_GCEA_DRAM_DATAMEM, "GCEA_DRAM_DATAMEM"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_gc_cane_mem_list[] = { - {AMDGPU_GFX_GC_CANE_MEM0, "GC_CANE_MEM0"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_gcutcl2_mem_list[] = { - {AMDGPU_GFX_GCUTCL2_MEM2P512X95, "GCUTCL2_MEM2P512X95"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_gds_mem_list[] = { - {AMDGPU_GFX_GDS_MEM0, "GDS_MEM"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_lds_mem_list[] = { - {AMDGPU_GFX_LDS_BANK0, "LDS_BANK0"}, - {AMDGPU_GFX_LDS_BANK1, "LDS_BANK1"}, - {AMDGPU_GFX_LDS_BANK2, "LDS_BANK2"}, - {AMDGPU_GFX_LDS_BANK3, "LDS_BANK3"}, - {AMDGPU_GFX_LDS_BANK4, "LDS_BANK4"}, - {AMDGPU_GFX_LDS_BANK5, "LDS_BANK5"}, - {AMDGPU_GFX_LDS_BANK6, "LDS_BANK6"}, - {AMDGPU_GFX_LDS_BANK7, "LDS_BANK7"}, - {AMDGPU_GFX_LDS_BANK8, "LDS_BANK8"}, - {AMDGPU_GFX_LDS_BANK9, "LDS_BANK9"}, - {AMDGPU_GFX_LDS_BANK10, "LDS_BANK10"}, - {AMDGPU_GFX_LDS_BANK11, "LDS_BANK11"}, - {AMDGPU_GFX_LDS_BANK12, "LDS_BANK12"}, - {AMDGPU_GFX_LDS_BANK13, "LDS_BANK13"}, - {AMDGPU_GFX_LDS_BANK14, "LDS_BANK14"}, - {AMDGPU_GFX_LDS_BANK15, "LDS_BANK15"}, - {AMDGPU_GFX_LDS_BANK16, "LDS_BANK16"}, - {AMDGPU_GFX_LDS_BANK17, "LDS_BANK17"}, - {AMDGPU_GFX_LDS_BANK18, "LDS_BANK18"}, - {AMDGPU_GFX_LDS_BANK19, "LDS_BANK19"}, - {AMDGPU_GFX_LDS_BANK20, "LDS_BANK20"}, - {AMDGPU_GFX_LDS_BANK21, "LDS_BANK21"}, - {AMDGPU_GFX_LDS_BANK22, "LDS_BANK22"}, - {AMDGPU_GFX_LDS_BANK23, "LDS_BANK23"}, - {AMDGPU_GFX_LDS_BANK24, "LDS_BANK24"}, - {AMDGPU_GFX_LDS_BANK25, "LDS_BANK25"}, - {AMDGPU_GFX_LDS_BANK26, "LDS_BANK26"}, - {AMDGPU_GFX_LDS_BANK27, "LDS_BANK27"}, - {AMDGPU_GFX_LDS_BANK28, "LDS_BANK28"}, - {AMDGPU_GFX_LDS_BANK29, "LDS_BANK29"}, - {AMDGPU_GFX_LDS_BANK30, "LDS_BANK30"}, - {AMDGPU_GFX_LDS_BANK31, "LDS_BANK31"}, - {AMDGPU_GFX_LDS_SP_BUFFER_A, "LDS_SP_BUFFER_A"}, - {AMDGPU_GFX_LDS_SP_BUFFER_B, "LDS_SP_BUFFER_B"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_rlc_mem_list[] = { - {AMDGPU_GFX_RLC_GPMF32, "RLC_GPMF32"}, - {AMDGPU_GFX_RLC_RLCVF32, "RLC_RLCVF32"}, - {AMDGPU_GFX_RLC_SCRATCH, "RLC_SCRATCH"}, - {AMDGPU_GFX_RLC_SRM_ARAM, "RLC_SRM_ARAM"}, - {AMDGPU_GFX_RLC_SRM_DRAM, "RLC_SRM_DRAM"}, - {AMDGPU_GFX_RLC_TCTAG, "RLC_TCTAG"}, - {AMDGPU_GFX_RLC_SPM_SE, "RLC_SPM_SE"}, - {AMDGPU_GFX_RLC_SPM_GRBMT, "RLC_SPM_GRBMT"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_sp_mem_list[] = { - {AMDGPU_GFX_SP_SIMDID0, "SP_SIMDID0"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_spi_mem_list[] = { - {AMDGPU_GFX_SPI_MEM0, "SPI_MEM0"}, - {AMDGPU_GFX_SPI_MEM1, "SPI_MEM1"}, - {AMDGPU_GFX_SPI_MEM2, "SPI_MEM2"}, - {AMDGPU_GFX_SPI_MEM3, "SPI_MEM3"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_sqc_mem_list[] = { - {AMDGPU_GFX_SQC_INST_CACHE_A, "SQC_INST_CACHE_A"}, - {AMDGPU_GFX_SQC_INST_CACHE_B, "SQC_INST_CACHE_B"}, - {AMDGPU_GFX_SQC_INST_CACHE_TAG_A, "SQC_INST_CACHE_TAG_A"}, - {AMDGPU_GFX_SQC_INST_CACHE_TAG_B, "SQC_INST_CACHE_TAG_B"}, - {AMDGPU_GFX_SQC_INST_CACHE_MISS_FIFO_A, "SQC_INST_CACHE_MISS_FIFO_A"}, - {AMDGPU_GFX_SQC_INST_CACHE_MISS_FIFO_B, "SQC_INST_CACHE_MISS_FIFO_B"}, - {AMDGPU_GFX_SQC_INST_CACHE_GATCL1_MISS_FIFO_A, "SQC_INST_CACHE_GATCL1_MISS_FIFO_A"}, - {AMDGPU_GFX_SQC_INST_CACHE_GATCL1_MISS_FIFO_B, "SQC_INST_CACHE_GATCL1_MISS_FIFO_B"}, - {AMDGPU_GFX_SQC_DATA_CACHE_A, "SQC_DATA_CACHE_A"}, - {AMDGPU_GFX_SQC_DATA_CACHE_B, "SQC_DATA_CACHE_B"}, - {AMDGPU_GFX_SQC_DATA_CACHE_TAG_A, "SQC_DATA_CACHE_TAG_A"}, - {AMDGPU_GFX_SQC_DATA_CACHE_TAG_B, "SQC_DATA_CACHE_TAG_B"}, - {AMDGPU_GFX_SQC_DATA_CACHE_MISS_FIFO_A, "SQC_DATA_CACHE_MISS_FIFO_A"}, - {AMDGPU_GFX_SQC_DATA_CACHE_MISS_FIFO_B, "SQC_DATA_CACHE_MISS_FIFO_B"}, - {AMDGPU_GFX_SQC_DATA_CACHE_HIT_FIFO_A, "SQC_DATA_CACHE_HIT_FIFO_A"}, - {AMDGPU_GFX_SQC_DATA_CACHE_HIT_FIFO_B, "SQC_DATA_CACHE_HIT_FIFO_B"}, - {AMDGPU_GFX_SQC_DIRTY_BIT_A, "SQC_DIRTY_BIT_A"}, - {AMDGPU_GFX_SQC_DIRTY_BIT_B, "SQC_DIRTY_BIT_B"}, - {AMDGPU_GFX_SQC_WRITE_DATA_BUFFER_CU0, "SQC_WRITE_DATA_BUFFER_CU0"}, - {AMDGPU_GFX_SQC_WRITE_DATA_BUFFER_CU1, "SQC_WRITE_DATA_BUFFER_CU1"}, - {AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_DATA_CACHE_A, "SQC_UTCL1_MISS_LFIFO_DATA_CACHE_A"}, - {AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_DATA_CACHE_B, "SQC_UTCL1_MISS_LFIFO_DATA_CACHE_B"}, - {AMDGPU_GFX_SQC_UTCL1_MISS_LFIFO_INST_CACHE, "SQC_UTCL1_MISS_LFIFO_INST_CACHE"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_sq_mem_list[] = { - {AMDGPU_GFX_SQ_SGPR_MEM0, "SQ_SGPR_MEM0"}, - {AMDGPU_GFX_SQ_SGPR_MEM1, "SQ_SGPR_MEM1"}, - {AMDGPU_GFX_SQ_SGPR_MEM2, "SQ_SGPR_MEM2"}, - {AMDGPU_GFX_SQ_SGPR_MEM3, "SQ_SGPR_MEM3"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_ta_mem_list[] = { - {AMDGPU_GFX_TA_FS_AFIFO_RAM_LO, "TA_FS_AFIFO_RAM_LO"}, - {AMDGPU_GFX_TA_FS_AFIFO_RAM_HI, "TA_FS_AFIFO_RAM_HI"}, - {AMDGPU_GFX_TA_FS_CFIFO_RAM, "TA_FS_CFIFO_RAM"}, - {AMDGPU_GFX_TA_FSX_LFIFO, "TA_FSX_LFIFO"}, - {AMDGPU_GFX_TA_FS_DFIFO_RAM, "TA_FS_DFIFO_RAM"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tcc_mem_list[] = { - {AMDGPU_GFX_TCC_MEM1, "TCC_MEM1"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tca_mem_list[] = { - {AMDGPU_GFX_TCA_MEM1, "TCA_MEM1"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tci_mem_list[] = { - {AMDGPU_GFX_TCIW_MEM, "TCIW_MEM"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tcp_mem_list[] = { - {AMDGPU_GFX_TCP_LFIFO0, "TCP_LFIFO0"}, - {AMDGPU_GFX_TCP_SET0BANK0_RAM, "TCP_SET0BANK0_RAM"}, - {AMDGPU_GFX_TCP_SET0BANK1_RAM, "TCP_SET0BANK1_RAM"}, - {AMDGPU_GFX_TCP_SET0BANK2_RAM, "TCP_SET0BANK2_RAM"}, - {AMDGPU_GFX_TCP_SET0BANK3_RAM, "TCP_SET0BANK3_RAM"}, - {AMDGPU_GFX_TCP_SET1BANK0_RAM, "TCP_SET1BANK0_RAM"}, - {AMDGPU_GFX_TCP_SET1BANK1_RAM, "TCP_SET1BANK1_RAM"}, - {AMDGPU_GFX_TCP_SET1BANK2_RAM, "TCP_SET1BANK2_RAM"}, - {AMDGPU_GFX_TCP_SET1BANK3_RAM, "TCP_SET1BANK3_RAM"}, - {AMDGPU_GFX_TCP_SET2BANK0_RAM, "TCP_SET2BANK0_RAM"}, - {AMDGPU_GFX_TCP_SET2BANK1_RAM, "TCP_SET2BANK1_RAM"}, - {AMDGPU_GFX_TCP_SET2BANK2_RAM, "TCP_SET2BANK2_RAM"}, - {AMDGPU_GFX_TCP_SET2BANK3_RAM, "TCP_SET2BANK3_RAM"}, - {AMDGPU_GFX_TCP_SET3BANK0_RAM, "TCP_SET3BANK0_RAM"}, - {AMDGPU_GFX_TCP_SET3BANK1_RAM, "TCP_SET3BANK1_RAM"}, - {AMDGPU_GFX_TCP_SET3BANK2_RAM, "TCP_SET3BANK2_RAM"}, - {AMDGPU_GFX_TCP_SET3BANK3_RAM, "TCP_SET3BANK3_RAM"}, - {AMDGPU_GFX_TCP_VM_FIFO, "TCP_VM_FIFO"}, - {AMDGPU_GFX_TCP_DB_TAGRAM0, "TCP_DB_TAGRAM0"}, - {AMDGPU_GFX_TCP_DB_TAGRAM1, "TCP_DB_TAGRAM1"}, - {AMDGPU_GFX_TCP_DB_TAGRAM2, "TCP_DB_TAGRAM2"}, - {AMDGPU_GFX_TCP_DB_TAGRAM3, "TCP_DB_TAGRAM3"}, - {AMDGPU_GFX_TCP_UTCL1_LFIFO_PROBE0, "TCP_UTCL1_LFIFO_PROBE0"}, - {AMDGPU_GFX_TCP_UTCL1_LFIFO_PROBE1, "TCP_UTCL1_LFIFO_PROBE1"}, - {AMDGPU_GFX_TCP_CMD_FIFO, "TCP_CMD_FIFO"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_td_mem_list[] = { - {AMDGPU_GFX_TD_UTD_CS_FIFO_MEM, "TD_UTD_CS_FIFO_MEM"}, - {AMDGPU_GFX_TD_UTD_SS_FIFO_LO_MEM, "TD_UTD_SS_FIFO_LO_MEM"}, - {AMDGPU_GFX_TD_UTD_SS_FIFO_HI_MEM, "TD_UTD_SS_FIFO_HI_MEM"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_tcx_mem_list[] = { - {AMDGPU_GFX_TCX_FIFOD0, "TCX_FIFOD0"}, - {AMDGPU_GFX_TCX_FIFOD1, "TCX_FIFOD1"}, - {AMDGPU_GFX_TCX_FIFOD2, "TCX_FIFOD2"}, - {AMDGPU_GFX_TCX_FIFOD3, "TCX_FIFOD3"}, - {AMDGPU_GFX_TCX_FIFOD4, "TCX_FIFOD4"}, - {AMDGPU_GFX_TCX_FIFOD5, "TCX_FIFOD5"}, - {AMDGPU_GFX_TCX_FIFOD6, "TCX_FIFOD6"}, - {AMDGPU_GFX_TCX_FIFOD7, "TCX_FIFOD7"}, - {AMDGPU_GFX_TCX_FIFOB0, "TCX_FIFOB0"}, - {AMDGPU_GFX_TCX_FIFOB1, "TCX_FIFOB1"}, - {AMDGPU_GFX_TCX_FIFOB2, "TCX_FIFOB2"}, - {AMDGPU_GFX_TCX_FIFOB3, "TCX_FIFOB3"}, - {AMDGPU_GFX_TCX_FIFOB4, "TCX_FIFOB4"}, - {AMDGPU_GFX_TCX_FIFOB5, "TCX_FIFOB5"}, - {AMDGPU_GFX_TCX_FIFOB6, "TCX_FIFOB6"}, - {AMDGPU_GFX_TCX_FIFOB7, "TCX_FIFOB7"}, - {AMDGPU_GFX_TCX_FIFOA0, "TCX_FIFOA0"}, - {AMDGPU_GFX_TCX_FIFOA1, "TCX_FIFOA1"}, - {AMDGPU_GFX_TCX_FIFOA2, "TCX_FIFOA2"}, - {AMDGPU_GFX_TCX_FIFOA3, "TCX_FIFOA3"}, - {AMDGPU_GFX_TCX_FIFOA4, "TCX_FIFOA4"}, - {AMDGPU_GFX_TCX_FIFOA5, "TCX_FIFOA5"}, - {AMDGPU_GFX_TCX_FIFOA6, "TCX_FIFOA6"}, - {AMDGPU_GFX_TCX_FIFOA7, "TCX_FIFOA7"}, - {AMDGPU_GFX_TCX_CFIFO0, "TCX_CFIFO0"}, - {AMDGPU_GFX_TCX_CFIFO1, "TCX_CFIFO1"}, - {AMDGPU_GFX_TCX_CFIFO2, "TCX_CFIFO2"}, - {AMDGPU_GFX_TCX_CFIFO3, "TCX_CFIFO3"}, - {AMDGPU_GFX_TCX_CFIFO4, "TCX_CFIFO4"}, - {AMDGPU_GFX_TCX_CFIFO5, "TCX_CFIFO5"}, - {AMDGPU_GFX_TCX_CFIFO6, "TCX_CFIFO6"}, - {AMDGPU_GFX_TCX_CFIFO7, "TCX_CFIFO7"}, - {AMDGPU_GFX_TCX_FIFO_ACKB0, "TCX_FIFO_ACKB0"}, - {AMDGPU_GFX_TCX_FIFO_ACKB1, "TCX_FIFO_ACKB1"}, - {AMDGPU_GFX_TCX_FIFO_ACKB2, "TCX_FIFO_ACKB2"}, - {AMDGPU_GFX_TCX_FIFO_ACKB3, "TCX_FIFO_ACKB3"}, - {AMDGPU_GFX_TCX_FIFO_ACKB4, "TCX_FIFO_ACKB4"}, - {AMDGPU_GFX_TCX_FIFO_ACKB5, "TCX_FIFO_ACKB5"}, - {AMDGPU_GFX_TCX_FIFO_ACKB6, "TCX_FIFO_ACKB6"}, - {AMDGPU_GFX_TCX_FIFO_ACKB7, "TCX_FIFO_ACKB7"}, - {AMDGPU_GFX_TCX_FIFO_ACKD0, "TCX_FIFO_ACKD0"}, - {AMDGPU_GFX_TCX_FIFO_ACKD1, "TCX_FIFO_ACKD1"}, - {AMDGPU_GFX_TCX_FIFO_ACKD2, "TCX_FIFO_ACKD2"}, - {AMDGPU_GFX_TCX_FIFO_ACKD3, "TCX_FIFO_ACKD3"}, - {AMDGPU_GFX_TCX_FIFO_ACKD4, "TCX_FIFO_ACKD4"}, - {AMDGPU_GFX_TCX_FIFO_ACKD5, "TCX_FIFO_ACKD5"}, - {AMDGPU_GFX_TCX_FIFO_ACKD6, "TCX_FIFO_ACKD6"}, - {AMDGPU_GFX_TCX_FIFO_ACKD7, "TCX_FIFO_ACKD7"}, - {AMDGPU_GFX_TCX_DST_FIFOA0, "TCX_DST_FIFOA0"}, - {AMDGPU_GFX_TCX_DST_FIFOA1, "TCX_DST_FIFOA1"}, - {AMDGPU_GFX_TCX_DST_FIFOA2, "TCX_DST_FIFOA2"}, - {AMDGPU_GFX_TCX_DST_FIFOA3, "TCX_DST_FIFOA3"}, - {AMDGPU_GFX_TCX_DST_FIFOA4, "TCX_DST_FIFOA4"}, - {AMDGPU_GFX_TCX_DST_FIFOA5, "TCX_DST_FIFOA5"}, - {AMDGPU_GFX_TCX_DST_FIFOA6, "TCX_DST_FIFOA6"}, - {AMDGPU_GFX_TCX_DST_FIFOA7, "TCX_DST_FIFOA7"}, - {AMDGPU_GFX_TCX_DST_FIFOB0, "TCX_DST_FIFOB0"}, - {AMDGPU_GFX_TCX_DST_FIFOB1, "TCX_DST_FIFOB1"}, - {AMDGPU_GFX_TCX_DST_FIFOB2, "TCX_DST_FIFOB2"}, - {AMDGPU_GFX_TCX_DST_FIFOB3, "TCX_DST_FIFOB3"}, - {AMDGPU_GFX_TCX_DST_FIFOB4, "TCX_DST_FIFOB4"}, - {AMDGPU_GFX_TCX_DST_FIFOB5, "TCX_DST_FIFOB5"}, - {AMDGPU_GFX_TCX_DST_FIFOB6, "TCX_DST_FIFOB6"}, - {AMDGPU_GFX_TCX_DST_FIFOB7, "TCX_DST_FIFOB7"}, - {AMDGPU_GFX_TCX_DST_FIFOD0, "TCX_DST_FIFOD0"}, - {AMDGPU_GFX_TCX_DST_FIFOD1, "TCX_DST_FIFOD1"}, - {AMDGPU_GFX_TCX_DST_FIFOD2, "TCX_DST_FIFOD2"}, - {AMDGPU_GFX_TCX_DST_FIFOD3, "TCX_DST_FIFOD3"}, - {AMDGPU_GFX_TCX_DST_FIFOD4, "TCX_DST_FIFOD4"}, - {AMDGPU_GFX_TCX_DST_FIFOD5, "TCX_DST_FIFOD5"}, - {AMDGPU_GFX_TCX_DST_FIFOD6, "TCX_DST_FIFOD6"}, - {AMDGPU_GFX_TCX_DST_FIFOD7, "TCX_DST_FIFOD7"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKB0, "TCX_DST_FIFO_ACKB0"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKB1, "TCX_DST_FIFO_ACKB1"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKB2, "TCX_DST_FIFO_ACKB2"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKB3, "TCX_DST_FIFO_ACKB3"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKB4, "TCX_DST_FIFO_ACKB4"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKB5, "TCX_DST_FIFO_ACKB5"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKB6, "TCX_DST_FIFO_ACKB6"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKB7, "TCX_DST_FIFO_ACKB7"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKD0, "TCX_DST_FIFO_ACKD0"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKD1, "TCX_DST_FIFO_ACKD1"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKD2, "TCX_DST_FIFO_ACKD2"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKD3, "TCX_DST_FIFO_ACKD3"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKD4, "TCX_DST_FIFO_ACKD4"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKD5, "TCX_DST_FIFO_ACKD5"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKD6, "TCX_DST_FIFO_ACKD6"}, - {AMDGPU_GFX_TCX_DST_FIFO_ACKD7, "TCX_DST_FIFO_ACKD7"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_atc_l2_mem_list[] = { - {AMDGPU_GFX_ATC_L2_MEM, "ATC_L2_MEM"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_utcl2_mem_list[] = { - {AMDGPU_GFX_UTCL2_MEM, "UTCL2_MEM"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_vml2_mem_list[] = { - {AMDGPU_GFX_VML2_MEM, "VML2_MEM"}, -}; - -static const struct amdgpu_ras_memory_id_entry gfx_v9_4_3_ras_vml2_walker_mem_list[] = { - {AMDGPU_GFX_VML2_WALKER_MEM, "VML2_WALKER_MEM"}, -}; - -static const struct amdgpu_gfx_ras_mem_id_entry gfx_v9_4_3_ras_mem_list_array[AMDGPU_GFX_MEM_TYPE_NUM] = { - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_cp_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_gcea_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_gc_cane_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_gcutcl2_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_gds_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_lds_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_rlc_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_sp_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_spi_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_sqc_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_sq_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_ta_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tcc_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tca_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tci_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tcp_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_td_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_tcx_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_atc_l2_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_utcl2_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_vml2_mem_list) - AMDGPU_GFX_MEMID_ENT(gfx_v9_4_3_ras_vml2_walker_mem_list) -}; - -static const struct amdgpu_gfx_ras_reg_entry gfx_v9_4_3_ce_reg_list[] = { - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regRLC_CE_ERR_STATUS_LOW, regRLC_CE_ERR_STATUS_HIGH), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "RLC"}, - AMDGPU_GFX_RLC_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPC_CE_ERR_STATUS_LO, regCPC_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPC"}, - AMDGPU_GFX_CP_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPF_CE_ERR_STATUS_LO, regCPF_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPF"}, - AMDGPU_GFX_CP_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPG_CE_ERR_STATUS_LO, regCPG_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPG"}, - AMDGPU_GFX_CP_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGDS_CE_ERR_STATUS_LO, regGDS_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "GDS"}, - AMDGPU_GFX_GDS_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGC_CANE_CE_ERR_STATUS_LO, regGC_CANE_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CANE"}, - AMDGPU_GFX_GC_CANE_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSPI_CE_ERR_STATUS_LO, regSPI_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SPI"}, - AMDGPU_GFX_SPI_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSP0_CE_ERR_STATUS_LO, regSP0_CE_ERR_STATUS_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SP0"}, - AMDGPU_GFX_SP_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSP1_CE_ERR_STATUS_LO, regSP1_CE_ERR_STATUS_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SP1"}, - AMDGPU_GFX_SP_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSQ_CE_ERR_STATUS_LO, regSQ_CE_ERR_STATUS_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SQ"}, - AMDGPU_GFX_SQ_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSQC_CE_EDC_LO, regSQC_CE_EDC_HI), - 5, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SQC"}, - AMDGPU_GFX_SQC_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCX_CE_ERR_STATUS_LO, regTCX_CE_ERR_STATUS_HI), - 2, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCX"}, - AMDGPU_GFX_TCX_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCC_CE_ERR_STATUS_LO, regTCC_CE_ERR_STATUS_HI), - 16, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCC"}, - AMDGPU_GFX_TCC_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTA_CE_EDC_LO, regTA_CE_EDC_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TA"}, - AMDGPU_GFX_TA_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCI_CE_EDC_LO_REG, regTCI_CE_EDC_HI_REG), - 27, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCI"}, - AMDGPU_GFX_TCI_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCP_CE_EDC_LO_REG, regTCP_CE_EDC_HI_REG), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCP"}, - AMDGPU_GFX_TCP_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTD_CE_EDC_LO, regTD_CE_EDC_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TD"}, - AMDGPU_GFX_TD_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGCEA_CE_ERR_STATUS_LO, regGCEA_CE_ERR_STATUS_HI), - 16, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "GCEA"}, - AMDGPU_GFX_GCEA_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regLDS_CE_ERR_STATUS_LO, regLDS_CE_ERR_STATUS_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "LDS"}, - AMDGPU_GFX_LDS_MEM, 4}, -}; - -static const struct amdgpu_gfx_ras_reg_entry gfx_v9_4_3_ue_reg_list[] = { - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regRLC_UE_ERR_STATUS_LOW, regRLC_UE_ERR_STATUS_HIGH), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "RLC"}, - AMDGPU_GFX_RLC_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPC_UE_ERR_STATUS_LO, regCPC_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPC"}, - AMDGPU_GFX_CP_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPF_UE_ERR_STATUS_LO, regCPF_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPF"}, - AMDGPU_GFX_CP_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regCPG_UE_ERR_STATUS_LO, regCPG_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CPG"}, - AMDGPU_GFX_CP_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGDS_UE_ERR_STATUS_LO, regGDS_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "GDS"}, - AMDGPU_GFX_GDS_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGC_CANE_UE_ERR_STATUS_LO, regGC_CANE_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "CANE"}, - AMDGPU_GFX_GC_CANE_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSPI_UE_ERR_STATUS_LO, regSPI_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SPI"}, - AMDGPU_GFX_SPI_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSP0_UE_ERR_STATUS_LO, regSP0_UE_ERR_STATUS_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SP0"}, - AMDGPU_GFX_SP_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSP1_UE_ERR_STATUS_LO, regSP1_UE_ERR_STATUS_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SP1"}, - AMDGPU_GFX_SP_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSQ_UE_ERR_STATUS_LO, regSQ_UE_ERR_STATUS_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SQ"}, - AMDGPU_GFX_SQ_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regSQC_UE_EDC_LO, regSQC_UE_EDC_HI), - 5, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SQC"}, - AMDGPU_GFX_SQC_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCX_UE_ERR_STATUS_LO, regTCX_UE_ERR_STATUS_HI), - 2, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCX"}, - AMDGPU_GFX_TCX_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCC_UE_ERR_STATUS_LO, regTCC_UE_ERR_STATUS_HI), - 16, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCC"}, - AMDGPU_GFX_TCC_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTA_UE_EDC_LO, regTA_UE_EDC_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TA"}, - AMDGPU_GFX_TA_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCI_UE_EDC_LO_REG, regTCI_UE_EDC_HI_REG), - 27, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCI"}, - AMDGPU_GFX_TCI_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCP_UE_EDC_LO_REG, regTCP_UE_EDC_HI_REG), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCP"}, - AMDGPU_GFX_TCP_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTD_UE_EDC_LO, regTD_UE_EDC_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TD"}, - AMDGPU_GFX_TD_MEM, 4}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regTCA_UE_ERR_STATUS_LO, regTCA_UE_ERR_STATUS_HI), - 2, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "TCA"}, - AMDGPU_GFX_TCA_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regGCEA_UE_ERR_STATUS_LO, regGCEA_UE_ERR_STATUS_HI), - 16, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "GCEA"}, - AMDGPU_GFX_GCEA_MEM, 1}, - {{AMDGPU_RAS_REG_ENTRY(GC, 0, regLDS_UE_ERR_STATUS_LO, regLDS_UE_ERR_STATUS_HI), - 10, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "LDS"}, - AMDGPU_GFX_LDS_MEM, 4}, -}; - -static void gfx_v9_4_3_inst_query_ras_err_count(struct amdgpu_device *adev, - void *ras_error_status, int xcc_id) -{ - struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status; - unsigned long ce_count = 0, ue_count = 0; - uint32_t i, j, k; - - /* NOTE: convert xcc_id to physical XCD ID (XCD0 or XCD1) */ - struct amdgpu_smuio_mcm_config_info mcm_info = { - .socket_id = adev->smuio.funcs->get_socket_id(adev), - .die_id = xcc_id & 0x01 ? 1 : 0, - }; - - mutex_lock(&adev->grbm_idx_mutex); - - for (i = 0; i < ARRAY_SIZE(gfx_v9_4_3_ce_reg_list); i++) { - for (j = 0; j < gfx_v9_4_3_ce_reg_list[i].se_num; j++) { - for (k = 0; k < gfx_v9_4_3_ce_reg_list[i].reg_entry.reg_inst; k++) { - /* no need to select if instance number is 1 */ - if (gfx_v9_4_3_ce_reg_list[i].se_num > 1 || - gfx_v9_4_3_ce_reg_list[i].reg_entry.reg_inst > 1) - gfx_v9_4_3_xcc_select_se_sh(adev, j, 0, k, xcc_id); - - amdgpu_ras_inst_query_ras_error_count(adev, - &(gfx_v9_4_3_ce_reg_list[i].reg_entry), - 1, - gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ce_reg_list[i].mem_id_type].mem_id_ent, - gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ce_reg_list[i].mem_id_type].size, - GET_INST(GC, xcc_id), - AMDGPU_RAS_ERROR__SINGLE_CORRECTABLE, - &ce_count); - - amdgpu_ras_inst_query_ras_error_count(adev, - &(gfx_v9_4_3_ue_reg_list[i].reg_entry), - 1, - gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ue_reg_list[i].mem_id_type].mem_id_ent, - gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ue_reg_list[i].mem_id_type].size, - GET_INST(GC, xcc_id), - AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE, - &ue_count); - } - } - } - - /* handle extra register entries of UE */ - for (; i < ARRAY_SIZE(gfx_v9_4_3_ue_reg_list); i++) { - for (j = 0; j < gfx_v9_4_3_ue_reg_list[i].se_num; j++) { - for (k = 0; k < gfx_v9_4_3_ue_reg_list[i].reg_entry.reg_inst; k++) { - /* no need to select if instance number is 1 */ - if (gfx_v9_4_3_ue_reg_list[i].se_num > 1 || - gfx_v9_4_3_ue_reg_list[i].reg_entry.reg_inst > 1) - gfx_v9_4_3_xcc_select_se_sh(adev, j, 0, k, xcc_id); - - amdgpu_ras_inst_query_ras_error_count(adev, - &(gfx_v9_4_3_ue_reg_list[i].reg_entry), - 1, - gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ue_reg_list[i].mem_id_type].mem_id_ent, - gfx_v9_4_3_ras_mem_list_array[gfx_v9_4_3_ue_reg_list[i].mem_id_type].size, - GET_INST(GC, xcc_id), - AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE, - &ue_count); - } - } - } - - gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, - xcc_id); - mutex_unlock(&adev->grbm_idx_mutex); - - /* the caller should make sure initialize value of - * err_data->ue_count and err_data->ce_count - */ - amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, ue_count); - amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, ce_count); -} - -static void gfx_v9_4_3_inst_reset_ras_err_count(struct amdgpu_device *adev, - void *ras_error_status, int xcc_id) -{ - uint32_t i, j, k; - - mutex_lock(&adev->grbm_idx_mutex); - - for (i = 0; i < ARRAY_SIZE(gfx_v9_4_3_ce_reg_list); i++) { - for (j = 0; j < gfx_v9_4_3_ce_reg_list[i].se_num; j++) { - for (k = 0; k < gfx_v9_4_3_ce_reg_list[i].reg_entry.reg_inst; k++) { - /* no need to select if instance number is 1 */ - if (gfx_v9_4_3_ce_reg_list[i].se_num > 1 || - gfx_v9_4_3_ce_reg_list[i].reg_entry.reg_inst > 1) - gfx_v9_4_3_xcc_select_se_sh(adev, j, 0, k, xcc_id); - - amdgpu_ras_inst_reset_ras_error_count(adev, - &(gfx_v9_4_3_ce_reg_list[i].reg_entry), - 1, - GET_INST(GC, xcc_id)); - - amdgpu_ras_inst_reset_ras_error_count(adev, - &(gfx_v9_4_3_ue_reg_list[i].reg_entry), - 1, - GET_INST(GC, xcc_id)); - } - } - } - - /* handle extra register entries of UE */ - for (; i < ARRAY_SIZE(gfx_v9_4_3_ue_reg_list); i++) { - for (j = 0; j < gfx_v9_4_3_ue_reg_list[i].se_num; j++) { - for (k = 0; k < gfx_v9_4_3_ue_reg_list[i].reg_entry.reg_inst; k++) { - /* no need to select if instance number is 1 */ - if (gfx_v9_4_3_ue_reg_list[i].se_num > 1 || - gfx_v9_4_3_ue_reg_list[i].reg_entry.reg_inst > 1) - gfx_v9_4_3_xcc_select_se_sh(adev, j, 0, k, xcc_id); - - amdgpu_ras_inst_reset_ras_error_count(adev, - &(gfx_v9_4_3_ue_reg_list[i].reg_entry), - 1, - GET_INST(GC, xcc_id)); - } - } - } - - gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, - xcc_id); - mutex_unlock(&adev->grbm_idx_mutex); -} - static void gfx_v9_4_3_inst_enable_watchdog_timer(struct amdgpu_device *adev, void *ras_error_status, int xcc_id) { @@ -4610,18 +3758,6 @@ static void gfx_v9_4_3_inst_enable_watchdog_timer(struct amdgpu_device *adev, mutex_unlock(&adev->grbm_idx_mutex); } -static void gfx_v9_4_3_query_ras_error_count(struct amdgpu_device *adev, - void *ras_error_status) -{ - amdgpu_gfx_ras_error_func(adev, ras_error_status, - gfx_v9_4_3_inst_query_ras_err_count); -} - -static void gfx_v9_4_3_reset_ras_error_count(struct amdgpu_device *adev) -{ - amdgpu_gfx_ras_error_func(adev, NULL, gfx_v9_4_3_inst_reset_ras_err_count); -} - static void gfx_v9_4_3_enable_watchdog_timer(struct amdgpu_device *adev) { amdgpu_gfx_ras_error_func(adev, NULL, gfx_v9_4_3_inst_enable_watchdog_timer); @@ -5102,37 +4238,9 @@ struct amdgpu_xcp_ip_funcs gfx_v9_4_3_xcp_funcs = { .resume = &gfx_v9_4_3_xcp_resume }; -struct amdgpu_ras_block_hw_ops gfx_v9_4_3_ras_ops = { - .query_ras_error_count = &gfx_v9_4_3_query_ras_error_count, - .reset_ras_error_count = &gfx_v9_4_3_reset_ras_error_count, -}; - -static int gfx_v9_4_3_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block) -{ - int r; - - r = amdgpu_ras_block_late_init(adev, ras_block); - if (r) - return r; - - r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__GFX, - &gfx_v9_4_3_aca_info, - NULL); - if (r) - goto late_fini; - - return 0; - -late_fini: - amdgpu_ras_block_late_fini(adev, ras_block); - - return r; -} - struct amdgpu_gfx_ras gfx_v9_4_3_ras = { .ras_block = { - .hw_ops = &gfx_v9_4_3_ras_ops, - .ras_late_init = &gfx_v9_4_3_ras_late_init, + .hw_ops = NULL, }, .enable_watchdog_timer = &gfx_v9_4_3_enable_watchdog_timer, }; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c index 8eb9847d9e1e..c40d9c467204 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v11_0.c @@ -606,6 +606,8 @@ static void gmc_v11_0_set_gfxhub_funcs(struct amdgpu_device *adev) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): adev->gfxhub.funcs = &gfxhub_v11_5_0_funcs; break; default: @@ -781,6 +783,8 @@ static int gmc_v11_0_sw_init(struct amdgpu_ip_block *ip_block) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): set_bit(AMDGPU_GFXHUB(0), adev->vmhubs_mask); set_bit(AMDGPU_MMHUB0(0), adev->vmhubs_mask); /* diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index c2a41fa3a396..64ebedc595b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -167,44 +167,6 @@ static void gmc_v8_0_init_golden_registers(struct amdgpu_device *adev) } } -static void gmc_v8_0_mc_stop(struct amdgpu_device *adev) -{ - u32 blackout; - struct amdgpu_ip_block *ip_block; - - ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_GMC); - if (!ip_block) - return; - - gmc_v8_0_wait_for_idle(ip_block); - - blackout = RREG32(mmMC_SHARED_BLACKOUT_CNTL); - if (REG_GET_FIELD(blackout, MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE) != 1) { - /* Block CPU access */ - WREG32(mmBIF_FB_EN, 0); - /* blackout the MC */ - blackout = REG_SET_FIELD(blackout, - MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE, 1); - WREG32(mmMC_SHARED_BLACKOUT_CNTL, blackout); - } - /* wait for the MC to settle */ - udelay(100); -} - -static void gmc_v8_0_mc_resume(struct amdgpu_device *adev) -{ - u32 tmp; - - /* unblackout the MC */ - tmp = RREG32(mmMC_SHARED_BLACKOUT_CNTL); - tmp = REG_SET_FIELD(tmp, MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE, 0); - WREG32(mmMC_SHARED_BLACKOUT_CNTL, tmp); - /* allow CPU access */ - tmp = REG_SET_FIELD(0, BIF_FB_EN, FB_READ_EN, 1); - tmp = REG_SET_FIELD(tmp, BIF_FB_EN, FB_WRITE_EN, 1); - WREG32(mmBIF_FB_EN, tmp); -} - /** * gmc_v8_0_init_microcode - load ucode images from disk * @@ -1293,89 +1255,6 @@ static int gmc_v8_0_wait_for_idle(struct amdgpu_ip_block *ip_block) } -static bool gmc_v8_0_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - u32 srbm_soft_reset = 0; - struct amdgpu_device *adev = ip_block->adev; - u32 tmp = RREG32(mmSRBM_STATUS); - - if (tmp & SRBM_STATUS__VMC_BUSY_MASK) - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, - SRBM_SOFT_RESET, SOFT_RESET_VMC, 1); - - if (tmp & (SRBM_STATUS__MCB_BUSY_MASK | SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK | - SRBM_STATUS__MCC_BUSY_MASK | SRBM_STATUS__MCD_BUSY_MASK)) { - if (!(adev->flags & AMD_IS_APU)) - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, - SRBM_SOFT_RESET, SOFT_RESET_MC, 1); - } - - if (srbm_soft_reset) { - adev->gmc.srbm_soft_reset = srbm_soft_reset; - return true; - } - - adev->gmc.srbm_soft_reset = 0; - - return false; -} - -static int gmc_v8_0_pre_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - if (!adev->gmc.srbm_soft_reset) - return 0; - - gmc_v8_0_mc_stop(adev); - if (gmc_v8_0_wait_for_idle(ip_block)) - dev_warn(adev->dev, "Wait for GMC idle timed out !\n"); - - return 0; -} - -static int gmc_v8_0_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 srbm_soft_reset; - - if (!adev->gmc.srbm_soft_reset) - return 0; - srbm_soft_reset = adev->gmc.srbm_soft_reset; - - if (srbm_soft_reset) { - u32 tmp; - - tmp = RREG32(mmSRBM_SOFT_RESET); - tmp |= srbm_soft_reset; - dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp); - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - - udelay(50); - - tmp &= ~srbm_soft_reset; - WREG32(mmSRBM_SOFT_RESET, tmp); - tmp = RREG32(mmSRBM_SOFT_RESET); - - /* Wait a little for things to settle down */ - udelay(50); - } - - return 0; -} - -static int gmc_v8_0_post_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - if (!adev->gmc.srbm_soft_reset) - return 0; - - gmc_v8_0_mc_resume(adev); - return 0; -} - static int gmc_v8_0_vm_fault_interrupt_state(struct amdgpu_device *adev, struct amdgpu_irq_src *src, unsigned int type, @@ -1715,10 +1594,6 @@ static const struct amd_ip_funcs gmc_v8_0_ip_funcs = { .resume = gmc_v8_0_resume, .is_idle = gmc_v8_0_is_idle, .wait_for_idle = gmc_v8_0_wait_for_idle, - .check_soft_reset = gmc_v8_0_check_soft_reset, - .pre_soft_reset = gmc_v8_0_pre_soft_reset, - .soft_reset = gmc_v8_0_soft_reset, - .post_soft_reset = gmc_v8_0_post_soft_reset, .set_clockgating_state = gmc_v8_0_set_clockgating_state, .set_powergating_state = gmc_v8_0_set_powergating_state, .get_clockgating_state = gmc_v8_0_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index 8a5c44810ba1..1fcc0594fd0a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -57,6 +57,7 @@ #include "umc_v6_0.h" #include "umc_v6_7.h" #include "umc_v12_0.h" +#include "ras_umc_v12_0.h" #include "hdp_v4_0.h" #include "mca_v3_0.h" @@ -1382,7 +1383,7 @@ static void gmc_v9_0_set_umc_funcs(struct amdgpu_device *adev) case IP_VERSION(12, 0, 0): case IP_VERSION(12, 5, 0): adev->umc.max_ras_err_cnt_per_query = - UMC_V12_0_TOTAL_CHANNEL_NUM(adev) * UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL; + UMC_V12_0_TOTAL_CHANNEL_NUM * UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL; adev->umc.channel_inst_num = UMC_V12_0_CHANNEL_INSTANCE_NUM; adev->umc.umc_inst_num = UMC_V12_0_UMC_INSTANCE_NUM; adev->umc.node_inst_num /= UMC_V12_0_UMC_INSTANCE_NUM; @@ -2025,11 +2026,19 @@ static int gmc_v9_0_sw_init(struct amdgpu_ip_block *ip_block) * The first KFD VMID is 8 for GPUs with graphics, 3 for * compute-only GPUs. On compute-only GPUs that leaves 2 VMIDs * for video processing. + * + * If kernel queues are disabled, allow KFD to use all vmids. */ - adev->vm_manager.first_kfd_vmid = - (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 1) || - amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 2) || - amdgpu_is_multi_aid(adev)) ? + if (adev->gfx.disable_kq && + adev->jpeg.disable_kq && + adev->vcn.disable_kq && + adev->sdma.no_user_submission) + adev->vm_manager.first_kfd_vmid = 1; + else + adev->vm_manager.first_kfd_vmid = + (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 1) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 2) || + amdgpu_is_multi_aid(adev)) ? 3 : 8; diff --git a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c index f5927c3553ce..05b164f38c97 100644 --- a/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/imu_v11_0.c @@ -43,6 +43,8 @@ MODULE_FIRMWARE("amdgpu/gc_11_5_2_imu.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_3_imu.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_4_imu.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_imu.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_imu.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_imu.bin"); static int imu_v11_0_init_microcode(struct amdgpu_device *adev) { diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c index 0c746580de11..0fdc32b3ae91 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v4_0_3.c @@ -119,6 +119,19 @@ static int jpeg_v4_0_3_early_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; + switch (amdgpu_user_queue) { + case -1: + case 0: + default: + adev->jpeg.disable_kq = false; + adev->jpeg.disable_uq = true; + break; + case 2: + adev->jpeg.disable_kq = true; + adev->jpeg.disable_uq = true; + break; + } + adev->jpeg.num_jpeg_rings = AMDGPU_MAX_JPEG_RINGS_4_0_3; jpeg_v4_0_3_set_dec_ring_funcs(adev); @@ -175,6 +188,10 @@ static int jpeg_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block) for (j = 0; j < adev->jpeg.num_jpeg_rings; ++j) { ring = &adev->jpeg.inst[i].ring_dec[j]; ring->use_doorbell = true; + if (adev->jpeg.disable_kq) { + ring->no_scheduler = true; + ring->no_user_submission = true; + } ring->vm_hub = AMDGPU_MMHUB0(adev->jpeg.inst[i].aid_id); if (!amdgpu_sriov_vf(adev)) { ring->doorbell_index = @@ -1010,7 +1027,7 @@ void jpeg_v4_0_3_dec_ring_nop(struct amdgpu_ring *ring, uint32_t count) static bool jpeg_v4_0_3_is_idle(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - bool ret = false; + bool ret = true; int i, j; for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { @@ -1425,72 +1442,6 @@ static const struct amdgpu_ras_block_hw_ops jpeg_v4_0_3_ras_hw_ops = { .query_poison_status = jpeg_v4_0_3_query_ras_poison_status, }; -static int jpeg_v4_0_3_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - struct aca_bank_info info; - u64 misc0; - int ret; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return ret; - - misc0 = bank->regs[ACA_REG_IDX_MISC0]; - switch (type) { - case ACA_SMU_TYPE_UE: - bank->aca_err_type = ACA_ERROR_TYPE_UE; - ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE, - 1ULL); - break; - case ACA_SMU_TYPE_CE: - bank->aca_err_type = ACA_ERROR_TYPE_CE; - ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, - ACA_REG__MISC0__ERRCNT(misc0)); - break; - default: - return -EINVAL; - } - - return ret; -} - -/* reference to smu driver if header file */ -static int jpeg_v4_0_3_err_codes[] = { - 16, 17, 18, 19, 20, 21, 22, 23, /* JPEG[0-7][S|D] */ - 24, 25, 26, 27, 28, 29, 30, 31 -}; - -static bool jpeg_v4_0_3_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - u32 instlo; - - instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]); - instlo &= GENMASK(31, 1); - - if (instlo != mmSMNAID_AID0_MCA_SMU) - return false; - - if (aca_bank_check_error_codes(handle->adev, bank, - jpeg_v4_0_3_err_codes, - ARRAY_SIZE(jpeg_v4_0_3_err_codes))) - return false; - - return true; -} - -static const struct aca_bank_ops jpeg_v4_0_3_aca_bank_ops = { - .aca_bank_parser = jpeg_v4_0_3_aca_bank_parser, - .aca_bank_is_valid = jpeg_v4_0_3_aca_bank_is_valid, -}; - -static const struct aca_info jpeg_v4_0_3_aca_info = { - .hwip = ACA_HWIP_TYPE_SMU, - .mask = ACA_ERROR_UE_MASK, - .bank_ops = &jpeg_v4_0_3_aca_bank_ops, -}; - static int jpeg_v4_0_3_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block) { int r; @@ -1506,11 +1457,6 @@ static int jpeg_v4_0_3_ras_late_init(struct amdgpu_device *adev, struct ras_comm goto late_fini; } - r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__JPEG, - &jpeg_v4_0_3_aca_info, NULL); - if (r) - goto late_fini; - return 0; late_fini: diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c index 250316704dfa..8846cb3ed12b 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_1.c @@ -118,6 +118,19 @@ static int jpeg_v5_0_1_early_init(struct amdgpu_ip_block *ip_block) if (!adev->jpeg.num_jpeg_inst || adev->jpeg.num_jpeg_inst > AMDGPU_MAX_JPEG_INSTANCES) return -ENOENT; + switch (amdgpu_user_queue) { + case -1: + case 0: + default: + adev->jpeg.disable_kq = false; + adev->jpeg.disable_uq = true; + break; + case 2: + adev->jpeg.disable_kq = true; + adev->jpeg.disable_uq = true; + break; + } + adev->jpeg.num_jpeg_rings = AMDGPU_MAX_JPEG_RINGS; jpeg_v5_0_1_set_dec_ring_funcs(adev); jpeg_v5_0_1_set_irq_funcs(adev); @@ -172,6 +185,10 @@ static int jpeg_v5_0_1_sw_init(struct amdgpu_ip_block *ip_block) for (j = 0; j < adev->jpeg.num_jpeg_rings; ++j) { ring = &adev->jpeg.inst[i].ring_dec[j]; ring->use_doorbell = true; + if (adev->jpeg.disable_kq) { + ring->no_scheduler = true; + ring->no_user_submission = true; + } ring->vm_hub = AMDGPU_MMHUB0(adev->jpeg.inst[i].aid_id); if (!amdgpu_sriov_vf(adev)) { ring->doorbell_index = @@ -657,7 +674,7 @@ static void jpeg_v5_0_1_dec_ring_set_wptr(struct amdgpu_ring *ring) static bool jpeg_v5_0_1_is_idle(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; - bool ret = false; + bool ret = true; int i, j; for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { @@ -871,10 +888,7 @@ static const struct amd_ip_funcs jpeg_v5_0_1_ip_funcs = { .resume = jpeg_v5_0_1_resume, .is_idle = jpeg_v5_0_1_is_idle, .wait_for_idle = jpeg_v5_0_1_wait_for_idle, - .check_soft_reset = NULL, - .pre_soft_reset = NULL, .soft_reset = NULL, - .post_soft_reset = NULL, .set_clockgating_state = jpeg_v5_0_1_set_clockgating_state, .set_powergating_state = jpeg_v5_0_1_set_powergating_state, .dump_ip_state = amdgpu_jpeg_dump_ip_state, @@ -1003,73 +1017,6 @@ static const struct amdgpu_ras_block_hw_ops jpeg_v5_0_1_ras_hw_ops = { .query_poison_status = jpeg_v5_0_1_query_ras_poison_status, }; -static int jpeg_v5_0_1_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - struct aca_bank_info info; - u64 misc0; - int ret; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return ret; - - misc0 = bank->regs[ACA_REG_IDX_MISC0]; - switch (type) { - case ACA_SMU_TYPE_UE: - bank->aca_err_type = ACA_ERROR_TYPE_UE; - ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE, - 1ULL); - break; - case ACA_SMU_TYPE_CE: - bank->aca_err_type = ACA_ERROR_TYPE_CE; - ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, - ACA_REG__MISC0__ERRCNT(misc0)); - break; - default: - return -EINVAL; - } - - return ret; -} - -/* reference to smu driver if header file */ -static int jpeg_v5_0_1_err_codes[] = { - 16, 17, 18, 19, 20, 21, 22, 23, /* JPEG[0-9][S|D] */ - 24, 25, 26, 27, 28, 29, 30, 31, - 48, 49, 50, 51, -}; - -static bool jpeg_v5_0_1_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - u32 instlo; - - instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]); - instlo &= GENMASK(31, 1); - - if (instlo != mmSMNAID_AID0_MCA_SMU) - return false; - - if (aca_bank_check_error_codes(handle->adev, bank, - jpeg_v5_0_1_err_codes, - ARRAY_SIZE(jpeg_v5_0_1_err_codes))) - return false; - - return true; -} - -static const struct aca_bank_ops jpeg_v5_0_1_aca_bank_ops = { - .aca_bank_parser = jpeg_v5_0_1_aca_bank_parser, - .aca_bank_is_valid = jpeg_v5_0_1_aca_bank_is_valid, -}; - -static const struct aca_info jpeg_v5_0_1_aca_info = { - .hwip = ACA_HWIP_TYPE_SMU, - .mask = ACA_ERROR_UE_MASK, - .bank_ops = &jpeg_v5_0_1_aca_bank_ops, -}; - static int jpeg_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block) { int r; @@ -1078,11 +1025,6 @@ static int jpeg_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_comm if (r) return r; - r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__JPEG, - &jpeg_v5_0_1_aca_info, NULL); - if (r) - goto late_fini; - if (amdgpu_ras_is_supported(adev, ras_block->block) && adev->jpeg.inst->ras_poison_irq.funcs) { r = amdgpu_irq_get(adev, &adev->jpeg.inst->ras_poison_irq, 0); diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c index 7a4ecea6b39a..ff02f72352a8 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_0_2.c @@ -690,10 +690,7 @@ static const struct amd_ip_funcs jpeg_v5_0_2_ip_funcs = { .resume = jpeg_v5_0_2_resume, .is_idle = jpeg_v5_0_2_is_idle, .wait_for_idle = jpeg_v5_0_2_wait_for_idle, - .check_soft_reset = NULL, - .pre_soft_reset = NULL, .soft_reset = NULL, - .post_soft_reset = NULL, .set_clockgating_state = jpeg_v5_0_2_set_clockgating_state, .set_powergating_state = jpeg_v5_0_2_set_powergating_state, .dump_ip_state = amdgpu_jpeg_dump_ip_state, diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 16625c31bfd3..e947c16e694d 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -133,8 +133,8 @@ static int mes_userq_map(struct amdgpu_usermode_queue *queue) queue_input.gang_quantum = 10000; queue_input.paging = false; - queue_input.process_context_addr = ctx->gpu_addr; - queue_input.gang_context_addr = ctx->gpu_addr + AMDGPU_USERQ_PROC_CTX_SZ; + queue_input.process_context_addr = uq_mgr->proc_ctx_obj.gpu_addr; + queue_input.gang_context_addr = ctx->gpu_addr; queue_input.inprocess_gang_priority = AMDGPU_MES_PRIORITY_LEVEL_NORMAL; queue_input.gang_global_priority_level = convert_to_mes_priority(queue->priority); @@ -169,7 +169,8 @@ static int mes_userq_unmap(struct amdgpu_usermode_queue *queue) memset(&queue_input, 0x0, sizeof(struct mes_remove_queue_input)); queue_input.doorbell_offset = queue->doorbell_index; - queue_input.gang_context_addr = ctx->gpu_addr + AMDGPU_USERQ_PROC_CTX_SZ; + queue_input.gang_context_addr = ctx->gpu_addr; + queue_input.queue_type = queue->queue_type; amdgpu_mes_lock(&adev->mes); r = adev->mes.funcs->remove_hw_queue(&adev->mes, &queue_input); @@ -179,6 +180,63 @@ static int mes_userq_unmap(struct amdgpu_usermode_queue *queue) return r; } +int mes_userq_reset(struct amdgpu_usermode_queue *queue) +{ + struct amdgpu_userq_mgr *uq_mgr = queue->userq_mgr; + struct amdgpu_device *adev = uq_mgr->adev; + struct mes_reset_queue_input queue_input; + int r; + + /* XXX: add a FW version check for SDMA per queue reset */ + memset(&queue_input, 0x0, sizeof(struct mes_reset_queue_input)); + queue_input.doorbell_offset = queue->doorbell_index; + queue_input.queue_type = queue->queue_type; + + amdgpu_mes_lock(&adev->mes); + r = adev->mes.funcs->reset_hw_queue(&adev->mes, &queue_input); + amdgpu_mes_unlock(&adev->mes); + if (r) + return r; + return mes_userq_unmap(queue); +} + +int mes_userq_reset_queue(struct amdgpu_device *adev, + struct amdgpu_usermode_queue *guilty_uq, + int queue_type, + unsigned int pipe, + unsigned int queue, + unsigned int db) +{ + struct amdgpu_usermode_queue *uq; + bool use_mmio = adev->gfx.mec.use_mmio_for_reset; + unsigned long uq_id; + int r; + + xa_for_each(&adev->userq_doorbell_xa, uq_id, uq) { + if (uq->queue_type == queue_type) { + if (uq == guilty_uq) + continue; + if (uq->doorbell_index == db) { + uq->state = AMDGPU_USERQ_STATE_HUNG; + if (use_mmio) + r = amdgpu_mes_reset_queue_mmio(adev, queue_type, 0, 1, pipe, queue, 0); + else + r = amdgpu_mes_reset_user_queue(adev, queue_type, db, 0); + if (r) + return r; + r = mes_userq_unmap(uq); + if (r) + return r; + atomic_inc(&adev->gpu_reset_counter); + amdgpu_userq_fence_driver_force_completion(uq); + drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, NULL); + break; + } + } + } + return 0; +} + static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_usermode_queue *queue, struct drm_amdgpu_userq_in *mqd_user) @@ -186,12 +244,8 @@ static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr, struct amdgpu_userq_obj *ctx = &queue->fw_obj; int r, size; - /* - * The FW expects at least one page space allocated for - * process ctx and gang ctx each. Create an object - * for the same. - */ - size = AMDGPU_USERQ_PROC_CTX_SZ + AMDGPU_USERQ_GANG_CTX_SZ; + /* The FW expects at least one page space allocated for gang ctx. */ + size = AMDGPU_USERQ_GANG_CTX_SZ; r = amdgpu_bo_create_kernel(uq_mgr->adev, size, 0, AMDGPU_GEM_DOMAIN_GTT, &ctx->obj, &ctx->gpu_addr, @@ -205,54 +259,26 @@ static int mes_userq_create_ctx_space(struct amdgpu_userq_mgr *uq_mgr, return 0; } -static int mes_userq_detect_and_reset(struct amdgpu_device *adev, - int queue_type) +static int mes_userq_create_proc_ctx_space(struct amdgpu_userq_mgr *uq_mgr) { - int db_array_size = amdgpu_mes_get_hung_queue_db_array_size(adev); - struct mes_detect_and_reset_queue_input input; - struct amdgpu_usermode_queue *queue; - unsigned int hung_db_num = 0; - unsigned long queue_id; - u32 db_array[8]; - bool found_hung_queue = false; - int r, i; - - if (db_array_size > 8) { - dev_err(adev->dev, "DB array size (%d vs 8) too small\n", - db_array_size); - return -EINVAL; - } - - memset(&input, 0x0, sizeof(struct mes_detect_and_reset_queue_input)); + int r = 0; - input.queue_type = queue_type; - - amdgpu_mes_lock(&adev->mes); - r = amdgpu_mes_detect_and_reset_hung_queues(adev, queue_type, false, - &hung_db_num, db_array, 0); - amdgpu_mes_unlock(&adev->mes); - if (r) { - dev_err(adev->dev, "Failed to detect and reset queues, err (%d)\n", r); - } else if (hung_db_num) { - xa_for_each(&adev->userq_doorbell_xa, queue_id, queue) { - if (queue->queue_type == queue_type) { - for (i = 0; i < hung_db_num; i++) { - if (queue->doorbell_index == db_array[i]) { - queue->state = AMDGPU_USERQ_STATE_HUNG; - found_hung_queue = true; - atomic_inc(&adev->gpu_reset_counter); - amdgpu_userq_fence_driver_force_completion(queue); - drm_dev_wedged_event(adev_to_drm(adev), DRM_WEDGE_RECOVERY_NONE, NULL); - } - } - } - } + mutex_lock(&uq_mgr->proc_ctx_lock); + /* This check is a necessary because amdgpu_bo_create_kernel() + * calls helpers like amdgpu_bo_pin() and memset() unconditionally + */ + if (!uq_mgr->proc_ctx_obj.obj) { + r = amdgpu_bo_create_kernel(uq_mgr->adev, AMDGPU_USERQ_PROC_CTX_SZ, + 0, AMDGPU_GEM_DOMAIN_GTT, + &uq_mgr->proc_ctx_obj.obj, + &uq_mgr->proc_ctx_obj.gpu_addr, + &uq_mgr->proc_ctx_obj.cpu_ptr); + + if (!r) + memset(uq_mgr->proc_ctx_obj.cpu_ptr, 0, AMDGPU_USERQ_PROC_CTX_SZ); } - if (found_hung_queue) { - /* Resume scheduling after hang recovery */ - r = amdgpu_mes_resume(adev, input.xcc_id); - } + mutex_unlock(&uq_mgr->proc_ctx_lock); return r; } @@ -429,7 +455,14 @@ static int mes_userq_mqd_create(struct amdgpu_usermode_queue *queue, goto free_mqd; } - /* Create BO for FW operations */ + /* Create per-process MES process context BO */ + r = mes_userq_create_proc_ctx_space(uq_mgr); + if (r) { + DRM_ERROR("Failed to allocate MES process context space bo, error: %d\n", r); + goto free_mqd; + } + + /* Create BO of a gang for FW operations */ r = mes_userq_create_ctx_space(uq_mgr, queue, mqd_user); if (r) { DRM_ERROR("Failed to allocate BO for userqueue (%d)", r); @@ -497,7 +530,7 @@ static int mes_userq_preempt(struct amdgpu_usermode_queue *queue) *fence_ptr = 0; memset(&queue_input, 0x0, sizeof(struct mes_suspend_gang_input)); - queue_input.gang_context_addr = ctx->gpu_addr + AMDGPU_USERQ_PROC_CTX_SZ; + queue_input.gang_context_addr = ctx->gpu_addr; queue_input.suspend_fence_addr = fence_gpu_addr; queue_input.suspend_fence_value = 1; amdgpu_mes_lock(&adev->mes); @@ -534,7 +567,7 @@ static int mes_userq_restore(struct amdgpu_usermode_queue *queue) return 0; memset(&queue_input, 0x0, sizeof(struct mes_resume_gang_input)); - queue_input.gang_context_addr = ctx->gpu_addr + AMDGPU_USERQ_PROC_CTX_SZ; + queue_input.gang_context_addr = ctx->gpu_addr; amdgpu_mes_lock(&adev->mes); r = adev->mes.funcs->resume_gang(&adev->mes, &queue_input); @@ -549,7 +582,7 @@ const struct amdgpu_userq_funcs userq_mes_funcs = { .mqd_destroy = mes_userq_mqd_destroy, .unmap = mes_userq_unmap, .map = mes_userq_map, - .detect_and_reset = mes_userq_detect_and_reset, .preempt = mes_userq_preempt, .restore = mes_userq_restore, + .reset = mes_userq_reset, }; diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.h b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.h index 090ae8897770..a473360d6a8b 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.h +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.h @@ -27,4 +27,13 @@ #include "amdgpu_userq.h" extern const struct amdgpu_userq_funcs userq_mes_funcs; + +int mes_userq_reset(struct amdgpu_usermode_queue *queue); +int mes_userq_reset_queue(struct amdgpu_device *adev, + struct amdgpu_usermode_queue *guilty_uq, + int queue_type, + unsigned int pipe, + unsigned int queue, + unsigned int db); + #endif diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index ac6d4f277336..8f136ff7d96f 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -60,6 +60,10 @@ MODULE_FIRMWARE("amdgpu/gc_11_5_4_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_4_mes1.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_mes_2.bin"); MODULE_FIRMWARE("amdgpu/gc_11_5_6_mes1.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_mes_2.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_0_mes1.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_mes_2.bin"); +MODULE_FIRMWARE("amdgpu/gc_11_7_1_mes1.bin"); static int mes_v11_0_hw_init(struct amdgpu_ip_block *ip_block); static int mes_v11_0_hw_fini(struct amdgpu_ip_block *ip_block); @@ -383,6 +387,8 @@ static int mes_v11_0_remove_hw_queue(struct amdgpu_mes *mes, mes_remove_queue_pkt.doorbell_offset = input->doorbell_offset; mes_remove_queue_pkt.gang_context_addr = input->gang_context_addr; + mes_remove_queue_pkt.queue_type = + convert_to_mes_queue_type(input->queue_type); if (mes_rev >= 0x60) mes_remove_queue_pkt.remove_queue_after_reset = input->remove_queue_after_reset; @@ -392,6 +398,230 @@ static int mes_v11_0_remove_hw_queue(struct amdgpu_mes *mes, offsetof(union MESAPI__REMOVE_QUEUE, api_status)); } +static bool mes_v11_0_pipe_reset_support(struct amdgpu_device *adev) +{ + /* Disable the pipe reset until the CPFW fully support it.*/ + dev_warn_once(adev->dev, "The CPFW hasn't support pipe reset yet.\n"); + return false; +} +static int mes_v11_0_reset_gfx_pipe_mmio(struct amdgpu_device *adev, + u32 me, u32 pipe, u32 queue) +{ + uint32_t reset_pipe = 0, clean_pipe = 0; + int r; + + if (!mes_v11_0_pipe_reset_support(adev)) + return -EOPNOTSUPP; + + amdgpu_gfx_rlc_enter_safe_mode(adev, 0); + mutex_lock(&adev->srbm_mutex); + soc21_grbm_select(adev, me, pipe, queue, 0); + + switch (pipe) { + case 0: + reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, + PFP_PIPE0_RESET, 1); + reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, + ME_PIPE0_RESET, 1); + clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, + PFP_PIPE0_RESET, 0); + clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, + ME_PIPE0_RESET, 0); + break; + case 1: + reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, + PFP_PIPE1_RESET, 1); + reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, + ME_PIPE1_RESET, 1); + clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, + PFP_PIPE1_RESET, 0); + clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, + ME_PIPE1_RESET, 0); + break; + default: + break; + } + + WREG32_SOC15(GC, 0, regCP_ME_CNTL, reset_pipe); + WREG32_SOC15(GC, 0, regCP_ME_CNTL, clean_pipe); + + r = (RREG32(SOC15_REG_OFFSET(GC, 0, regCP_GFX_RS64_INSTR_PNTR1)) << 2) - + RS64_FW_UC_START_ADDR_LO; + soc21_grbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + amdgpu_gfx_rlc_exit_safe_mode(adev, 0); + + dev_info(adev->dev, "The gfx pipe reset to the ME firmware start PC: %s\n", + r == 0 ? "successfully" : "failed"); + /* FIXME: Sometimes driver can't cache the ME firmware start PC correctly, + * so the pipe reset status relies on the later gfx ring test result. + */ + return 0; +} + +/* + * With MEC pipe reset asserted, clear CP_HQD_ACTIVE / CP_HQD_DEQUEUE_REQUEST for + * every queue on (me, pipe). HQDs must be torn down while pipe reset stays + * asserted; only then clear the pipe reset bit. + * Caller must hold adev->srbm_mutex. + */ +static void mes_v11_0_clear_hqds_on_mec_pipe(struct amdgpu_device *adev, u32 me, + u32 pipe) +{ + unsigned int q; + + for (q = 0; q < adev->gfx.mec.num_queue_per_pipe; q++) { + soc21_grbm_select(adev, me, pipe, q, 0); + /* Start from a clean HQD dequeue state before forcing HQD inactive. */ + WREG32_SOC15(GC, 0, regCP_HQD_ACTIVE, 0); + WREG32_SOC15(GC, 0, regCP_HQD_DEQUEUE_REQUEST, 0); + } +} + +static int mes_v11_0_reset_compute_pipe_mmio(struct amdgpu_device *adev, + u32 me, u32 pipe, u32 queue) +{ + uint32_t reset_val, clean_val; + int r; + + amdgpu_gfx_rlc_enter_safe_mode(adev, 0); + mutex_lock(&adev->srbm_mutex); + soc21_grbm_select(adev, me, pipe, queue, 0); + + if (adev->gfx.rs64_enable) { + reset_val = RREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL); + clean_val = reset_val; + + switch (pipe) { + case 0: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL, + MEC_PIPE0_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL, + MEC_PIPE0_RESET, 0); + break; + case 1: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL, + MEC_PIPE1_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL, + MEC_PIPE1_RESET, 0); + break; + case 2: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL, + MEC_PIPE2_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL, + MEC_PIPE2_RESET, 0); + break; + case 3: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL, + MEC_PIPE3_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL, + MEC_PIPE3_RESET, 0); + break; + default: + break; + } + WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, reset_val); + mes_v11_0_clear_hqds_on_mec_pipe(adev, me, pipe); + WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, clean_val); + r = (RREG32_SOC15(GC, 0, regCP_MEC_RS64_INSTR_PNTR) << 2) - + RS64_FW_UC_START_ADDR_LO; + } else { + reset_val = RREG32_SOC15(GC, 0, regCP_MEC_CNTL); + clean_val = reset_val; + + if (me == 1) { + switch (pipe) { + case 0: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME1_PIPE0_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME1_PIPE0_RESET, 0); + break; + case 1: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME1_PIPE1_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME1_PIPE1_RESET, 0); + break; + case 2: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME1_PIPE2_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME1_PIPE2_RESET, 0); + break; + case 3: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME1_PIPE3_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME1_PIPE3_RESET, 0); + break; + default: + break; + } + /* mec1 fw pc: CP_MEC1_INSTR_PNTR */ + } else { + switch (pipe) { + case 0: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME2_PIPE0_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME2_PIPE0_RESET, 0); + break; + case 1: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME2_PIPE1_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME2_PIPE1_RESET, 0); + break; + case 2: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME2_PIPE2_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME2_PIPE2_RESET, 0); + break; + case 3: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME2_PIPE3_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME2_PIPE3_RESET, 0); + break; + default: + break; + } + /* mec2 fw pc: CP:CP_MEC2_INSTR_PNTR */ + } + WREG32_SOC15(GC, 0, regCP_MEC_CNTL, reset_val); + mes_v11_0_clear_hqds_on_mec_pipe(adev, me, pipe); + WREG32_SOC15(GC, 0, regCP_MEC_CNTL, clean_val); + r = RREG32(SOC15_REG_OFFSET(GC, 0, regCP_MEC1_INSTR_PNTR)); + } + + soc21_grbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + amdgpu_gfx_rlc_exit_safe_mode(adev, 0); + + dev_dbg(adev->dev, "MEC pipe me%u pipe%u queue%u resets to MEC FW start PC: %s\n", + me, pipe, queue, r == 0 ? "successfully" : "failed"); + /*FIXME:Sometimes driver can't cache the MEC firmware start PC correctly, so the pipe + * reset status relies on the compute ring test result. + */ + return 0; +} + +static int mes_v11_0_reset_pipe_mmio(struct amdgpu_mes *mes, uint32_t queue_type, + uint32_t me_id, uint32_t pipe_id, + uint32_t queue_id, uint32_t vmid) +{ + struct amdgpu_device *adev = mes->adev; + + if (queue_type == AMDGPU_RING_TYPE_GFX) + return mes_v11_0_reset_gfx_pipe_mmio(adev, me_id, pipe_id, queue_id); + else if (queue_type == AMDGPU_RING_TYPE_COMPUTE) + return mes_v11_0_reset_compute_pipe_mmio(adev, me_id, pipe_id, queue_id); + else + return -EOPNOTSUPP; +} + static int mes_v11_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_type, uint32_t me_id, uint32_t pipe_id, uint32_t queue_id, uint32_t vmid) @@ -559,6 +789,7 @@ static int mes_v11_0_suspend_gang(struct amdgpu_mes *mes, mes_suspend_gang_pkt.gang_context_addr = input->gang_context_addr; mes_suspend_gang_pkt.suspend_fence_addr = input->suspend_fence_addr; mes_suspend_gang_pkt.suspend_fence_value = input->suspend_fence_value; + mes_suspend_gang_pkt.doorbell_offset = input->doorbell_offset; return mes_v11_0_submit_pkt_and_poll_completion(mes, &mes_suspend_gang_pkt, sizeof(mes_suspend_gang_pkt), @@ -578,6 +809,7 @@ static int mes_v11_0_resume_gang(struct amdgpu_mes *mes, mes_resume_gang_pkt.resume_all_gangs = input->resume_all_gangs; mes_resume_gang_pkt.gang_context_addr = input->gang_context_addr; + mes_resume_gang_pkt.doorbell_offset = input->doorbell_offset; return mes_v11_0_submit_pkt_and_poll_completion(mes, &mes_resume_gang_pkt, sizeof(mes_resume_gang_pkt), @@ -764,10 +996,16 @@ static int mes_v11_0_reset_hw_queue(struct amdgpu_mes *mes, { union MESAPI__RESET mes_reset_queue_pkt; - if (input->use_mmio) - return mes_v11_0_reset_queue_mmio(mes, input->queue_type, - input->me_id, input->pipe_id, - input->queue_id, input->vmid); + if (input->use_mmio) { + int r = mes_v11_0_reset_queue_mmio(mes, input->queue_type, + input->me_id, input->pipe_id, + input->queue_id, input->vmid); + if (r) + return mes_v11_0_reset_pipe_mmio(mes, input->queue_type, + input->me_id, input->pipe_id, + input->queue_id, input->vmid); + return 0; + } memset(&mes_reset_queue_pkt, 0, sizeof(mes_reset_queue_pkt)); diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c index 7453fb11289e..ce5064200743 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_0.c @@ -26,7 +26,7 @@ #include "amdgpu.h" #include "gfx_v12_0.h" #include "soc15_common.h" -#include "soc21.h" +#include "soc24.h" #include "gc/gc_12_0_0_offset.h" #include "gc/gc_12_0_0_sh_mask.h" #include "gc/gc_11_0_0_default.h" @@ -371,6 +371,8 @@ static int mes_v12_0_remove_hw_queue(struct amdgpu_mes *mes, mes_remove_queue_pkt.doorbell_offset = input->doorbell_offset; mes_remove_queue_pkt.gang_context_addr = input->gang_context_addr; + mes_remove_queue_pkt.queue_type = + convert_to_mes_queue_type(input->queue_type); if (mes_rev >= 0x5a) mes_remove_queue_pkt.remove_queue_after_reset = input->remove_queue_after_reset; @@ -413,6 +415,171 @@ int gfx_v12_0_request_gfx_index_mutex(struct amdgpu_device *adev, return 0; } +static bool mes_v12_0_pipe_reset_support(struct amdgpu_device *adev) +{ + /* Disable the pipe reset until the CPFW fully support it.*/ + dev_warn_once(adev->dev, "The CPFW hasn't support pipe reset yet.\n"); + return false; +} + +static int mes_v12_0_reset_gfx_pipe_mmio(struct amdgpu_device *adev, + u32 me, u32 pipe, u32 queue) +{ + uint32_t reset_pipe = 0, clean_pipe = 0; + int r; + + if (!mes_v12_0_pipe_reset_support(adev)) + return -EOPNOTSUPP; + + amdgpu_gfx_rlc_enter_safe_mode(adev, 0); + mutex_lock(&adev->srbm_mutex); + soc24_grbm_select(adev, me, pipe, queue, 0); + + switch (pipe) { + case 0: + reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, + PFP_PIPE0_RESET, 1); + reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, + ME_PIPE0_RESET, 1); + clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, + PFP_PIPE0_RESET, 0); + clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, + ME_PIPE0_RESET, 0); + break; + case 1: + reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, + PFP_PIPE1_RESET, 1); + reset_pipe = REG_SET_FIELD(reset_pipe, CP_ME_CNTL, + ME_PIPE1_RESET, 1); + clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, + PFP_PIPE1_RESET, 0); + clean_pipe = REG_SET_FIELD(clean_pipe, CP_ME_CNTL, + ME_PIPE1_RESET, 0); + break; + default: + break; + } + + WREG32_SOC15(GC, 0, regCP_ME_CNTL, reset_pipe); + WREG32_SOC15(GC, 0, regCP_ME_CNTL, clean_pipe); + + r = (RREG32(SOC15_REG_OFFSET(GC, 0, regCP_GFX_RS64_INSTR_PNTR1)) << 2) - + RS64_FW_UC_START_ADDR_LO; + soc24_grbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + amdgpu_gfx_rlc_exit_safe_mode(adev, 0); + + dev_info(adev->dev, "The gfx pipe reset: %s\n", + r == 0 ? "successfully" : "failed"); + /* Sometimes the ME start pc counter can't cache correctly, so the + * PC check only as a reference and pipe reset result rely on the + * later ring test. + */ + return 0; +} + +/* + * With MEC pipe reset asserted, clear CP_HQD_ACTIVE / CP_HQD_DEQUEUE_REQUEST for + * every queue on (me, pipe). HQDs must be torn down while pipe reset stays + * asserted; only then clear the pipe reset bit. + * Caller must hold adev->srbm_mutex. + */ +static void mes_v12_0_clear_hqds_on_mec_pipe(struct amdgpu_device *adev, u32 me, + u32 pipe) +{ + unsigned int q; + + for (q = 0; q < adev->gfx.mec.num_queue_per_pipe; q++) { + soc24_grbm_select(adev, me, pipe, q, 0); + /* Start from a clean HQD dequeue state before forcing HQD inactive. */ + WREG32_SOC15(GC, 0, regCP_HQD_ACTIVE, 0); + WREG32_SOC15(GC, 0, regCP_HQD_DEQUEUE_REQUEST, 0); + } +} + +static int mes_v12_0_reset_compute_pipe_mmio(struct amdgpu_device *adev, + u32 me, u32 pipe, u32 queue) +{ + uint32_t reset_val, clean_val; + int r = 0; + + amdgpu_gfx_rlc_enter_safe_mode(adev, 0); + mutex_lock(&adev->srbm_mutex); + soc24_grbm_select(adev, me, pipe, queue, 0); + if (adev->gfx.rs64_enable) { + reset_val = RREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL); + clean_val = reset_val; + + switch (pipe) { + case 0: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL, + MEC_PIPE0_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL, + MEC_PIPE0_RESET, 0); + break; + case 1: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL, + MEC_PIPE1_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL, + MEC_PIPE1_RESET, 0); + break; + case 2: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL, + MEC_PIPE2_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL, + MEC_PIPE2_RESET, 0); + break; + case 3: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_RS64_CNTL, + MEC_PIPE3_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_RS64_CNTL, + MEC_PIPE3_RESET, 0); + break; + default: + break; + } + WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, reset_val); + mes_v12_0_clear_hqds_on_mec_pipe(adev, me, pipe); + soc24_grbm_select(adev, me, pipe, queue, 0); + WREG32_SOC15(GC, 0, regCP_MEC_RS64_CNTL, clean_val); + r = (RREG32_SOC15(GC, 0, regCP_MEC_RS64_INSTR_PNTR) << 2) - + RS64_FW_UC_START_ADDR_LO; + } else { + reset_val = RREG32_SOC15(GC, 0, regCP_MEC_CNTL); + clean_val = reset_val; + + switch (pipe) { + case 0: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME1_PIPE0_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME1_PIPE0_RESET, 0); + break; + case 1: + reset_val = REG_SET_FIELD(reset_val, CP_MEC_CNTL, + MEC_ME1_PIPE1_RESET, 1); + clean_val = REG_SET_FIELD(clean_val, CP_MEC_CNTL, + MEC_ME1_PIPE1_RESET, 0); + break; + default: + break; + } + + WREG32_SOC15(GC, 0, regCP_MEC_CNTL, reset_val); + mes_v12_0_clear_hqds_on_mec_pipe(adev, me, pipe); + soc24_grbm_select(adev, me, pipe, queue, 0); + WREG32_SOC15(GC, 0, regCP_MEC_CNTL, clean_val); + } + + soc24_grbm_select(adev, 0, 0, 0, 0); + mutex_unlock(&adev->srbm_mutex); + amdgpu_gfx_rlc_exit_safe_mode(adev, 0); + + dev_dbg(adev->dev, "MEC pipe me%u pipe%u queue%u resets to MEC FW start PC: %s\n", + me, pipe, queue, r == 0 ? "successfully" : "failed"); + return 0; +} + static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_type, uint32_t me_id, uint32_t pipe_id, uint32_t queue_id, uint32_t vmid) @@ -442,7 +609,7 @@ static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_typ mutex_unlock(&adev->gfx.reset_sem_mutex); mutex_lock(&adev->srbm_mutex); - soc21_grbm_select(adev, me_id, pipe_id, queue_id, 0); + soc24_grbm_select(adev, me_id, pipe_id, queue_id, 0); /* wait till dequeue take effects */ for (i = 0; i < adev->usec_timeout; i++) { if (!(RREG32_SOC15(GC, 0, regCP_GFX_HQD_ACTIVE) & 1)) @@ -454,13 +621,13 @@ static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_typ r = -ETIMEDOUT; } - soc21_grbm_select(adev, 0, 0, 0, 0); + soc24_grbm_select(adev, 0, 0, 0, 0); mutex_unlock(&adev->srbm_mutex); } else if (queue_type == AMDGPU_RING_TYPE_COMPUTE) { dev_info(adev->dev, "reset compute queue (%d:%d:%d)\n", me_id, pipe_id, queue_id); mutex_lock(&adev->srbm_mutex); - soc21_grbm_select(adev, me_id, pipe_id, queue_id, 0); + soc24_grbm_select(adev, me_id, pipe_id, queue_id, 0); WREG32_SOC15(GC, 0, regCP_HQD_DEQUEUE_REQUEST, 0x2); WREG32_SOC15(GC, 0, regSPI_COMPUTE_QUEUE_RESET, 0x1); @@ -474,7 +641,7 @@ static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_typ dev_err(adev->dev, "failed to wait on hqd deactivate\n"); r = -ETIMEDOUT; } - soc21_grbm_select(adev, 0, 0, 0, 0); + soc24_grbm_select(adev, 0, 0, 0, 0); mutex_unlock(&adev->srbm_mutex); } else if (queue_type == AMDGPU_RING_TYPE_SDMA) { dev_info(adev->dev, "reset sdma queue (%d:%d:%d)\n", @@ -507,6 +674,20 @@ static int mes_v12_0_reset_queue_mmio(struct amdgpu_mes *mes, uint32_t queue_typ return r; } +static int mes_v12_0_reset_pipe_mmio(struct amdgpu_mes *mes, uint32_t queue_type, + uint32_t me_id, uint32_t pipe_id, + uint32_t queue_id, uint32_t vmid) +{ + struct amdgpu_device *adev = mes->adev; + + if (queue_type == AMDGPU_RING_TYPE_GFX) + return mes_v12_0_reset_gfx_pipe_mmio(adev, me_id, pipe_id, queue_id); + else if (queue_type == AMDGPU_RING_TYPE_COMPUTE) + return mes_v12_0_reset_compute_pipe_mmio(adev, me_id, pipe_id, queue_id); + else + return -EOPNOTSUPP; +} + static int mes_v12_0_map_legacy_queue(struct amdgpu_mes *mes, struct mes_map_legacy_queue_input *input) { @@ -528,10 +709,15 @@ static int mes_v12_0_map_legacy_queue(struct amdgpu_mes *mes, convert_to_mes_queue_type(input->queue_type); mes_add_queue_pkt.map_legacy_kq = 1; - if (mes->adev->enable_uni_mes) - pipe = AMDGPU_MES_KIQ_PIPE; - else + if (mes->adev->enable_uni_mes) { + /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */ + if (input->queue_type == AMDGPU_RING_TYPE_MES) + pipe = AMDGPU_MES_KIQ_PIPE; + else + pipe = AMDGPU_MES_SCHED_PIPE; + } else { pipe = AMDGPU_MES_SCHED_PIPE; + } return mes_v12_0_submit_pkt_and_poll_completion(mes, pipe, &mes_add_queue_pkt, sizeof(mes_add_queue_pkt), @@ -565,12 +751,28 @@ static int mes_v12_0_unmap_legacy_queue(struct amdgpu_mes *mes, mes_remove_queue_pkt.unmap_legacy_queue = 1; mes_remove_queue_pkt.queue_type = convert_to_mes_queue_type(input->queue_type); + /* + * A reset-time unmap: the queue was already reset via MMIO while + * gangs are suspended and it is on the MES hung/fail list. Tell + * MES to just drop its internal state for it. Without this flag + * MES asks CP to unmap the already-reset (still wedged) queue + * again, which times out and forces a GPU reset. + */ + if (input->action == RESET_QUEUES && + (mes->sched_version & AMDGPU_MES_VERSION_MASK) >= 0x5a) + mes_remove_queue_pkt.remove_queue_after_reset = 1; + } - if (mes->adev->enable_uni_mes) - pipe = AMDGPU_MES_KIQ_PIPE; - else + if (mes->adev->enable_uni_mes) { + /* Keep scheduler queue on KIQ pipe; unmap all other kernel queues on sched pipe. */ + if (input->queue_type == AMDGPU_RING_TYPE_MES) + pipe = AMDGPU_MES_KIQ_PIPE; + else + pipe = AMDGPU_MES_SCHED_PIPE; + } else { pipe = AMDGPU_MES_SCHED_PIPE; + } return mes_v12_0_submit_pkt_and_poll_completion(mes, pipe, &mes_remove_queue_pkt, sizeof(mes_remove_queue_pkt), @@ -592,6 +794,7 @@ static int mes_v12_0_suspend_gang(struct amdgpu_mes *mes, mes_suspend_gang_pkt.gang_context_addr = input->gang_context_addr; mes_suspend_gang_pkt.suspend_fence_addr = input->suspend_fence_addr; mes_suspend_gang_pkt.suspend_fence_value = input->suspend_fence_value; + mes_suspend_gang_pkt.doorbell_offset = input->doorbell_offset; return mes_v12_0_submit_pkt_and_poll_completion(mes, AMDGPU_MES_SCHED_PIPE, &mes_suspend_gang_pkt, sizeof(mes_suspend_gang_pkt), @@ -611,6 +814,7 @@ static int mes_v12_0_resume_gang(struct amdgpu_mes *mes, mes_resume_gang_pkt.resume_all_gangs = input->resume_all_gangs; mes_resume_gang_pkt.gang_context_addr = input->gang_context_addr; + mes_resume_gang_pkt.doorbell_offset = input->doorbell_offset; return mes_v12_0_submit_pkt_and_poll_completion(mes, AMDGPU_MES_SCHED_PIPE, &mes_resume_gang_pkt, sizeof(mes_resume_gang_pkt), @@ -886,10 +1090,16 @@ static int mes_v12_0_reset_hw_queue(struct amdgpu_mes *mes, union MESAPI__RESET mes_reset_queue_pkt; int pipe; - if (input->use_mmio) - return mes_v12_0_reset_queue_mmio(mes, input->queue_type, - input->me_id, input->pipe_id, - input->queue_id, input->vmid); + if (input->use_mmio) { + int r = mes_v12_0_reset_queue_mmio(mes, input->queue_type, + input->me_id, input->pipe_id, + input->queue_id, input->vmid); + if (r) + return mes_v12_0_reset_pipe_mmio(mes, input->queue_type, + input->me_id, input->pipe_id, + input->queue_id, input->vmid); + return 0; + } memset(&mes_reset_queue_pkt, 0, sizeof(mes_reset_queue_pkt)); @@ -913,10 +1123,7 @@ static int mes_v12_0_reset_hw_queue(struct amdgpu_mes *mes, mes_reset_queue_pkt.doorbell_offset = input->doorbell_offset; } - if (input->is_kq) - pipe = AMDGPU_MES_KIQ_PIPE; - else - pipe = AMDGPU_MES_SCHED_PIPE; + pipe = AMDGPU_MES_SCHED_PIPE; return mes_v12_0_submit_pkt_and_poll_completion(mes, pipe, &mes_reset_queue_pkt, sizeof(mes_reset_queue_pkt), @@ -1092,7 +1299,7 @@ static void mes_v12_0_enable(struct amdgpu_device *adev, bool enable) if (enable) { mutex_lock(&adev->srbm_mutex); for (pipe = 0; pipe < AMDGPU_MAX_MES_PIPES; pipe++) { - soc21_grbm_select(adev, 3, pipe, 0, 0); + soc24_grbm_select(adev, 3, pipe, 0, 0); if (amdgpu_mes_log_enable) { u32 log_size = AMDGPU_MES_LOG_BUFFER_SIZE + AMDGPU_MES_MSCRATCH_SIZE; /* In case uni mes is not enabled, only program for pipe 0 */ @@ -1131,7 +1338,7 @@ static void mes_v12_0_enable(struct amdgpu_device *adev, bool enable) WREG32_SOC15(GC, 0, regCP_MES_CNTL, data); } - soc21_grbm_select(adev, 0, 0, 0, 0); + soc24_grbm_select(adev, 0, 0, 0, 0); mutex_unlock(&adev->srbm_mutex); if (amdgpu_emu_mode) @@ -1163,7 +1370,7 @@ static void mes_v12_0_set_ucode_start_addr(struct amdgpu_device *adev) mutex_lock(&adev->srbm_mutex); for (pipe = 0; pipe < AMDGPU_MAX_MES_PIPES; pipe++) { /* me=3, queue=0 */ - soc21_grbm_select(adev, 3, pipe, 0, 0); + soc24_grbm_select(adev, 3, pipe, 0, 0); /* set ucode start address */ ucode_addr = adev->mes.uc_start_addr[pipe] >> 2; @@ -1172,7 +1379,7 @@ static void mes_v12_0_set_ucode_start_addr(struct amdgpu_device *adev) WREG32_SOC15(GC, 0, regCP_MES_PRGRM_CNTR_START_HI, upper_32_bits(ucode_addr)); - soc21_grbm_select(adev, 0, 0, 0, 0); + soc24_grbm_select(adev, 0, 0, 0, 0); } mutex_unlock(&adev->srbm_mutex); } @@ -1201,7 +1408,7 @@ static int mes_v12_0_load_microcode(struct amdgpu_device *adev, mutex_lock(&adev->srbm_mutex); /* me=3, pipe=0, queue=0 */ - soc21_grbm_select(adev, 3, pipe, 0, 0); + soc24_grbm_select(adev, 3, pipe, 0, 0); WREG32_SOC15(GC, 0, regCP_MES_IC_BASE_CNTL, 0); @@ -1236,7 +1443,7 @@ static int mes_v12_0_load_microcode(struct amdgpu_device *adev, WREG32_SOC15(GC, 0, regCP_MES_IC_OP_CNTL, data); } - soc21_grbm_select(adev, 0, 0, 0, 0); + soc24_grbm_select(adev, 0, 0, 0, 0); mutex_unlock(&adev->srbm_mutex); return 0; @@ -1383,7 +1590,7 @@ static void mes_v12_0_queue_init_register(struct amdgpu_ring *ring) uint32_t data = 0; mutex_lock(&adev->srbm_mutex); - soc21_grbm_select(adev, 3, ring->pipe, 0, 0); + soc24_grbm_select(adev, 3, ring->pipe, 0, 0); /* set CP_HQD_VMID.VMID = 0. */ data = RREG32_SOC15(GC, 0, regCP_HQD_VMID); @@ -1434,7 +1641,7 @@ static void mes_v12_0_queue_init_register(struct amdgpu_ring *ring) /* set CP_HQD_ACTIVE.ACTIVE=1 */ WREG32_SOC15(GC, 0, regCP_HQD_ACTIVE, mqd->cp_hqd_active); - soc21_grbm_select(adev, 0, 0, 0, 0); + soc24_grbm_select(adev, 0, 0, 0, 0); mutex_unlock(&adev->srbm_mutex); } @@ -1500,14 +1707,14 @@ static int mes_v12_0_queue_init(struct amdgpu_device *adev, ((pipe == AMDGPU_MES_KIQ_PIPE) && !adev->mes.kiq_version)) { /* get MES scheduler/KIQ versions */ mutex_lock(&adev->srbm_mutex); - soc21_grbm_select(adev, 3, pipe, 0, 0); + soc24_grbm_select(adev, 3, pipe, 0, 0); if (pipe == AMDGPU_MES_SCHED_PIPE) adev->mes.sched_version = RREG32_SOC15(GC, 0, regCP_MES_GP3_LO); else if (pipe == AMDGPU_MES_KIQ_PIPE && adev->enable_mes_kiq) adev->mes.kiq_version = RREG32_SOC15(GC, 0, regCP_MES_GP3_LO); - soc21_grbm_select(adev, 0, 0, 0, 0); + soc24_grbm_select(adev, 0, 0, 0, 0); mutex_unlock(&adev->srbm_mutex); } @@ -1695,7 +1902,7 @@ static void mes_v12_0_kiq_dequeue_sched(struct amdgpu_device *adev) int i; mutex_lock(&adev->srbm_mutex); - soc21_grbm_select(adev, 3, AMDGPU_MES_SCHED_PIPE, 0, 0); + soc24_grbm_select(adev, 3, AMDGPU_MES_SCHED_PIPE, 0, 0); /* disable the queue if it's active */ if (RREG32_SOC15(GC, 0, regCP_HQD_ACTIVE) & 1) { @@ -1719,7 +1926,7 @@ static void mes_v12_0_kiq_dequeue_sched(struct amdgpu_device *adev) WREG32_SOC15(GC, 0, regCP_HQD_PQ_WPTR_HI, 0); WREG32_SOC15(GC, 0, regCP_HQD_PQ_RPTR, 0); - soc21_grbm_select(adev, 0, 0, 0, 0); + soc24_grbm_select(adev, 0, 0, 0, 0); mutex_unlock(&adev->srbm_mutex); adev->mes.ring[0].sched.ready = false; diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c index 8a90ad5a51b8..f7d5879c6e44 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v12_1.c @@ -362,6 +362,8 @@ static int mes_v12_1_remove_hw_queue(struct amdgpu_mes *mes, mes_remove_queue_pkt.doorbell_offset = input->doorbell_offset; mes_remove_queue_pkt.gang_context_addr = input->gang_context_addr; + mes_remove_queue_pkt.queue_type = + convert_to_mes_queue_type(input->queue_type); return mes_v12_1_submit_pkt_and_poll_completion(mes, xcc_id, AMDGPU_MES_SCHED_PIPE, @@ -417,10 +419,15 @@ static int mes_v12_1_map_legacy_queue(struct amdgpu_mes *mes, convert_to_mes_queue_type(input->queue_type); mes_add_queue_pkt.map_legacy_kq = 1; - if (mes->adev->enable_uni_mes) - pipe = AMDGPU_MES_KIQ_PIPE; - else + if (mes->adev->enable_uni_mes) { + /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */ + if (input->queue_type == AMDGPU_RING_TYPE_MES) + pipe = AMDGPU_MES_KIQ_PIPE; + else + pipe = AMDGPU_MES_SCHED_PIPE; + } else { pipe = AMDGPU_MES_SCHED_PIPE; + } return mes_v12_1_submit_pkt_and_poll_completion(mes, input->xcc_id, pipe, @@ -457,10 +464,15 @@ static int mes_v12_1_unmap_legacy_queue(struct amdgpu_mes *mes, convert_to_mes_queue_type(input->queue_type); } - if (mes->adev->enable_uni_mes) - pipe = AMDGPU_MES_KIQ_PIPE; - else + if (mes->adev->enable_uni_mes) { + /* Keep scheduler queue on KIQ pipe; map all other kernel queues on sched pipe. */ + if (input->queue_type == AMDGPU_RING_TYPE_MES) + pipe = AMDGPU_MES_KIQ_PIPE; + else + pipe = AMDGPU_MES_SCHED_PIPE; + } else { pipe = AMDGPU_MES_SCHED_PIPE; + } return mes_v12_1_submit_pkt_and_poll_completion(mes, input->xcc_id, pipe, @@ -484,6 +496,7 @@ static int mes_v12_1_suspend_gang(struct amdgpu_mes *mes, mes_suspend_gang_pkt.gang_context_addr = input->gang_context_addr; mes_suspend_gang_pkt.suspend_fence_addr = input->suspend_fence_addr; mes_suspend_gang_pkt.suspend_fence_value = input->suspend_fence_value; + mes_suspend_gang_pkt.doorbell_offset = input->doorbell_offset; /* Suspend gang is handled by master MES */ return mes_v12_1_submit_pkt_and_poll_completion(mes, input->xcc_id, AMDGPU_MES_SCHED_PIPE, @@ -504,6 +517,7 @@ static int mes_v12_1_resume_gang(struct amdgpu_mes *mes, mes_resume_gang_pkt.resume_all_gangs = input->resume_all_gangs; mes_resume_gang_pkt.gang_context_addr = input->gang_context_addr; + mes_resume_gang_pkt.doorbell_offset = input->doorbell_offset; /* Resume gang is handled by master MES */ return mes_v12_1_submit_pkt_and_poll_completion(mes, input->xcc_id, AMDGPU_MES_SCHED_PIPE, @@ -2260,6 +2274,7 @@ static int mes_v12_1_test_queue(struct amdgpu_device *adev, int xcc_id, remove_queue.xcc_id = xcc_id; remove_queue.doorbell_offset = doorbell_idx; remove_queue.gang_context_addr = add_queue.gang_context_addr; + remove_queue.queue_type = queue_type; r = mes_v12_1_remove_hw_queue(&adev->mes, &remove_queue); error: diff --git a/drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c b/drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c index cc688ae79e84..47d07cd25fc4 100644 --- a/drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c +++ b/drivers/gpu/drm/amd/amdgpu/mmhub_v1_8.c @@ -29,7 +29,6 @@ #include "soc15_common.h" #include "soc15.h" -#include "amdgpu_ras.h" #include "amdgpu_psp.h" #define regVM_L2_CNTL3_DEFAULT 0x80100007 @@ -636,236 +635,8 @@ const struct amdgpu_mmhub_funcs mmhub_v1_8_funcs = { .get_clockgating = mmhub_v1_8_get_clockgating, }; -static const struct amdgpu_ras_err_status_reg_entry mmhub_v1_8_ce_reg_list[] = { - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA0_CE_ERR_STATUS_LO, regMMEA0_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA0"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA1_CE_ERR_STATUS_LO, regMMEA1_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA1"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA2_CE_ERR_STATUS_LO, regMMEA2_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA2"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA3_CE_ERR_STATUS_LO, regMMEA3_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA3"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA4_CE_ERR_STATUS_LO, regMMEA4_CE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA4"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMM_CANE_CE_ERR_STATUS_LO, regMM_CANE_CE_ERR_STATUS_HI), - 1, 0, "MM_CANE"}, -}; - -static const struct amdgpu_ras_err_status_reg_entry mmhub_v1_8_ue_reg_list[] = { - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA0_UE_ERR_STATUS_LO, regMMEA0_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA0"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA1_UE_ERR_STATUS_LO, regMMEA1_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA1"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA2_UE_ERR_STATUS_LO, regMMEA2_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA2"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA3_UE_ERR_STATUS_LO, regMMEA3_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA3"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMMEA4_UE_ERR_STATUS_LO, regMMEA4_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "MMEA4"}, - {AMDGPU_RAS_REG_ENTRY(MMHUB, 0, regMM_CANE_UE_ERR_STATUS_LO, regMM_CANE_UE_ERR_STATUS_HI), - 1, 0, "MM_CANE"}, -}; - -static const struct amdgpu_ras_memory_id_entry mmhub_v1_8_ras_memory_list[] = { - {AMDGPU_MMHUB_WGMI_PAGEMEM, "MMEA_WGMI_PAGEMEM"}, - {AMDGPU_MMHUB_RGMI_PAGEMEM, "MMEA_RGMI_PAGEMEM"}, - {AMDGPU_MMHUB_WDRAM_PAGEMEM, "MMEA_WDRAM_PAGEMEM"}, - {AMDGPU_MMHUB_RDRAM_PAGEMEM, "MMEA_RDRAM_PAGEMEM"}, - {AMDGPU_MMHUB_WIO_CMDMEM, "MMEA_WIO_CMDMEM"}, - {AMDGPU_MMHUB_RIO_CMDMEM, "MMEA_RIO_CMDMEM"}, - {AMDGPU_MMHUB_WGMI_CMDMEM, "MMEA_WGMI_CMDMEM"}, - {AMDGPU_MMHUB_RGMI_CMDMEM, "MMEA_RGMI_CMDMEM"}, - {AMDGPU_MMHUB_WDRAM_CMDMEM, "MMEA_WDRAM_CMDMEM"}, - {AMDGPU_MMHUB_RDRAM_CMDMEM, "MMEA_RDRAM_CMDMEM"}, - {AMDGPU_MMHUB_MAM_DMEM0, "MMEA_MAM_DMEM0"}, - {AMDGPU_MMHUB_MAM_DMEM1, "MMEA_MAM_DMEM1"}, - {AMDGPU_MMHUB_MAM_DMEM2, "MMEA_MAM_DMEM2"}, - {AMDGPU_MMHUB_MAM_DMEM3, "MMEA_MAM_DMEM3"}, - {AMDGPU_MMHUB_WRET_TAGMEM, "MMEA_WRET_TAGMEM"}, - {AMDGPU_MMHUB_RRET_TAGMEM, "MMEA_RRET_TAGMEM"}, - {AMDGPU_MMHUB_WIO_DATAMEM, "MMEA_WIO_DATAMEM"}, - {AMDGPU_MMHUB_WGMI_DATAMEM, "MMEA_WGMI_DATAMEM"}, - {AMDGPU_MMHUB_WDRAM_DATAMEM, "MMEA_WDRAM_DATAMEM"}, -}; - -static void mmhub_v1_8_inst_query_ras_error_count(struct amdgpu_device *adev, - uint32_t mmhub_inst, - void *ras_err_status) -{ - struct ras_err_data *err_data = (struct ras_err_data *)ras_err_status; - unsigned long ue_count = 0, ce_count = 0; - - /* NOTE: mmhub is converted by aid_mask and the range is 0-3, - * which can be used as die ID directly */ - struct amdgpu_smuio_mcm_config_info mcm_info = { - .socket_id = adev->smuio.funcs->get_socket_id(adev), - .die_id = mmhub_inst, - }; - - amdgpu_ras_inst_query_ras_error_count(adev, - mmhub_v1_8_ce_reg_list, - ARRAY_SIZE(mmhub_v1_8_ce_reg_list), - mmhub_v1_8_ras_memory_list, - ARRAY_SIZE(mmhub_v1_8_ras_memory_list), - mmhub_inst, - AMDGPU_RAS_ERROR__SINGLE_CORRECTABLE, - &ce_count); - amdgpu_ras_inst_query_ras_error_count(adev, - mmhub_v1_8_ue_reg_list, - ARRAY_SIZE(mmhub_v1_8_ue_reg_list), - mmhub_v1_8_ras_memory_list, - ARRAY_SIZE(mmhub_v1_8_ras_memory_list), - mmhub_inst, - AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE, - &ue_count); - - amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, ce_count); - amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, ue_count); -} - -static void mmhub_v1_8_query_ras_error_count(struct amdgpu_device *adev, - void *ras_err_status) -{ - uint32_t inst_mask; - uint32_t i; - - if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__MMHUB)) { - dev_warn(adev->dev, "MMHUB RAS is not supported\n"); - return; - } - - inst_mask = adev->aid_mask; - for_each_inst(i, inst_mask) - mmhub_v1_8_inst_query_ras_error_count(adev, i, ras_err_status); -} - -static void mmhub_v1_8_inst_reset_ras_error_count(struct amdgpu_device *adev, - uint32_t mmhub_inst) -{ - amdgpu_ras_inst_reset_ras_error_count(adev, - mmhub_v1_8_ce_reg_list, - ARRAY_SIZE(mmhub_v1_8_ce_reg_list), - mmhub_inst); - amdgpu_ras_inst_reset_ras_error_count(adev, - mmhub_v1_8_ue_reg_list, - ARRAY_SIZE(mmhub_v1_8_ue_reg_list), - mmhub_inst); -} - -static void mmhub_v1_8_reset_ras_error_count(struct amdgpu_device *adev) -{ - uint32_t inst_mask; - uint32_t i; - - if (!amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__MMHUB)) { - dev_warn(adev->dev, "MMHUB RAS is not supported\n"); - return; - } - - inst_mask = adev->aid_mask; - for_each_inst(i, inst_mask) - mmhub_v1_8_inst_reset_ras_error_count(adev, i); -} - -static const struct amdgpu_ras_block_hw_ops mmhub_v1_8_ras_hw_ops = { - .query_ras_error_count = mmhub_v1_8_query_ras_error_count, - .reset_ras_error_count = mmhub_v1_8_reset_ras_error_count, -}; - -static int mmhub_v1_8_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - struct aca_bank_info info; - u64 misc0; - int ret; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return ret; - - misc0 = bank->regs[ACA_REG_IDX_MISC0]; - switch (type) { - case ACA_SMU_TYPE_UE: - bank->aca_err_type = ACA_ERROR_TYPE_UE; - ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE, - 1ULL); - break; - case ACA_SMU_TYPE_CE: - bank->aca_err_type = ACA_ERROR_TYPE_CE; - ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, - ACA_REG__MISC0__ERRCNT(misc0)); - break; - default: - return -EINVAL; - } - - return ret; -} - -/* reference to smu driver if header file */ -static int mmhub_v1_8_err_codes[] = { - 0, 1, 2, 3, 4, /* CODE_DAGB0 - 4 */ - 5, 6, 7, 8, 9, /* CODE_EA0 - 4 */ - 10, /* CODE_UTCL2_ROUTER */ - 11, /* CODE_VML2 */ - 12, /* CODE_VML2_WALKER */ - 13, /* CODE_MMCANE */ -}; - -static bool mmhub_v1_8_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - u32 instlo; - - instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]); - instlo &= GENMASK(31, 1); - - if (instlo != mmSMNAID_AID0_MCA_SMU) - return false; - - if (aca_bank_check_error_codes(handle->adev, bank, - mmhub_v1_8_err_codes, - ARRAY_SIZE(mmhub_v1_8_err_codes))) - return false; - - return true; -} - -static const struct aca_bank_ops mmhub_v1_8_aca_bank_ops = { - .aca_bank_parser = mmhub_v1_8_aca_bank_parser, - .aca_bank_is_valid = mmhub_v1_8_aca_bank_is_valid, -}; - -static const struct aca_info mmhub_v1_8_aca_info = { - .hwip = ACA_HWIP_TYPE_SMU, - .mask = ACA_ERROR_UE_MASK, - .bank_ops = &mmhub_v1_8_aca_bank_ops, -}; - -static int mmhub_v1_8_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block) -{ - int r; - - r = amdgpu_ras_block_late_init(adev, ras_block); - if (r) - return r; - - r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__MMHUB, - &mmhub_v1_8_aca_info, NULL); - if (r) - goto late_fini; - - return 0; - -late_fini: - amdgpu_ras_block_late_fini(adev, ras_block); - - return r; -} - struct amdgpu_mmhub_ras mmhub_v1_8_ras = { .ras_block = { - .hw_ops = &mmhub_v1_8_ras_hw_ops, - .ras_late_init = mmhub_v1_8_ras_late_init, + .hw_ops = NULL, }, }; diff --git a/drivers/gpu/drm/amd/amdgpu/nv.c b/drivers/gpu/drm/amd/amdgpu/nv.c index 72edf5326b05..77557ee3ca16 100644 --- a/drivers/gpu/drm/amd/amdgpu/nv.c +++ b/drivers/gpu/drm/amd/amdgpu/nv.c @@ -507,11 +507,6 @@ void nv_set_virt_ops(struct amdgpu_device *adev) adev->virt.ops = &xgpu_nv_virt_ops; } -static bool nv_need_full_reset(struct amdgpu_device *adev) -{ - return true; -} - static bool nv_need_reset_on_init(struct amdgpu_device *adev) { u32 sol_reg; @@ -595,7 +590,6 @@ static const struct amdgpu_asic_funcs nv_asic_funcs = { .set_vce_clocks = &nv_set_vce_clocks, .get_config_memsize = &nv_get_config_memsize, .init_doorbell_index = &nv_init_doorbell_index, - .need_full_reset = &nv_need_full_reset, .need_reset_on_init = &nv_need_reset_on_init, .get_pcie_replay_count = &amdgpu_nbio_get_pcie_replay_count, .supports_baco = &amdgpu_dpm_is_baco_supported, diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c index 2a8582e87f2b..2a4d91368ac6 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c @@ -33,6 +33,8 @@ MODULE_FIRMWARE("amdgpu/psp_15_0_0_toc.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_0_ta.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_9_toc.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_9_ta.bin"); static int psp_v15_0_0_init_microcode(struct psp_context *psp) { diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c index 3fde9be74690..c2d098cd72ce 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v3_0.c @@ -1237,65 +1237,6 @@ static int sdma_v3_0_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static bool sdma_v3_0_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 srbm_soft_reset = 0; - u32 tmp = RREG32(mmSRBM_STATUS2); - - if ((tmp & SRBM_STATUS2__SDMA_BUSY_MASK) || - (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK)) { - srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA_MASK; - srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA1_MASK; - } - - if (srbm_soft_reset) { - adev->sdma.srbm_soft_reset = srbm_soft_reset; - return true; - } else { - adev->sdma.srbm_soft_reset = 0; - return false; - } -} - -static int sdma_v3_0_pre_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 srbm_soft_reset = 0; - - if (!adev->sdma.srbm_soft_reset) - return 0; - - srbm_soft_reset = adev->sdma.srbm_soft_reset; - - if (REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA) || - REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA1)) { - sdma_v3_0_ctx_switch_enable(adev, false); - sdma_v3_0_enable(adev, false); - } - - return 0; -} - -static int sdma_v3_0_post_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 srbm_soft_reset = 0; - - if (!adev->sdma.srbm_soft_reset) - return 0; - - srbm_soft_reset = adev->sdma.srbm_soft_reset; - - if (REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA) || - REG_GET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA1)) { - sdma_v3_0_gfx_resume(adev); - sdma_v3_0_rlc_resume(adev); - } - - return 0; -} - static int sdma_v3_0_soft_reset(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -1552,9 +1493,6 @@ static const struct amd_ip_funcs sdma_v3_0_ip_funcs = { .resume = sdma_v3_0_resume, .is_idle = sdma_v3_0_is_idle, .wait_for_idle = sdma_v3_0_wait_for_idle, - .check_soft_reset = sdma_v3_0_check_soft_reset, - .pre_soft_reset = sdma_v3_0_pre_soft_reset, - .post_soft_reset = sdma_v3_0_post_soft_reset, .soft_reset = sdma_v3_0_soft_reset, .set_clockgating_state = sdma_v3_0_set_clockgating_state, .set_powergating_state = sdma_v3_0_set_powergating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index 88428b88e00f..484f1a6b5fbc 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -95,8 +95,6 @@ static const struct amdgpu_hwip_reg_entry sdma_reg_list_4_4_2[] = { SOC15_REG_ENTRY_STR(GC, 0, regSDMA_VM_CNTL) }; -#define mmSMNAID_AID0_MCA_SMU 0x03b30400 - #define WREG32_SDMA(instance, offset, value) \ WREG32(sdma_v4_4_2_get_reg_offset(adev, (instance), (offset)), value) #define RREG32_SDMA(instance, offset) \ @@ -457,7 +455,7 @@ static void sdma_v4_4_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 /* write the fence */ amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -467,7 +465,7 @@ static void sdma_v4_4_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 addr += 4; amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); @@ -1359,6 +1357,19 @@ static int sdma_v4_4_2_early_init(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int r; + switch (amdgpu_user_queue) { + case -1: + case 0: + default: + adev->sdma.no_user_submission = false; + adev->sdma.disable_uq = true; + break; + case 2: + adev->sdma.no_user_submission = true; + adev->sdma.disable_uq = true; + break; + } + r = sdma_v4_4_2_init_microcode(adev); if (r) return r; @@ -1478,6 +1489,7 @@ static int sdma_v4_4_2_sw_init(struct amdgpu_ip_block *ip_block) /* doorbell size is 2 dwords, get DWORD offset */ ring->doorbell_index = adev->doorbell_index.sdma_engine[i] << 1; ring->vm_hub = AMDGPU_MMHUB0(aid_id); + ring->no_user_submission = adev->sdma.no_user_submission; sprintf(ring->name, "sdma%d.%d", aid_id, i % adev->sdma.num_inst_per_aid); @@ -2404,187 +2416,9 @@ struct amdgpu_xcp_ip_funcs sdma_v4_4_2_xcp_funcs = { .resume = &sdma_v4_4_2_xcp_resume }; -static const struct amdgpu_ras_err_status_reg_entry sdma_v4_2_2_ue_reg_list[] = { - {AMDGPU_RAS_REG_ENTRY(SDMA0, 0, regSDMA_UE_ERR_STATUS_LO, regSDMA_UE_ERR_STATUS_HI), - 1, (AMDGPU_RAS_ERR_INFO_VALID | AMDGPU_RAS_ERR_STATUS_VALID), "SDMA"}, -}; - -static const struct amdgpu_ras_memory_id_entry sdma_v4_4_2_ras_memory_list[] = { - {AMDGPU_SDMA_MBANK_DATA_BUF0, "SDMA_MBANK_DATA_BUF0"}, - {AMDGPU_SDMA_MBANK_DATA_BUF1, "SDMA_MBANK_DATA_BUF1"}, - {AMDGPU_SDMA_MBANK_DATA_BUF2, "SDMA_MBANK_DATA_BUF2"}, - {AMDGPU_SDMA_MBANK_DATA_BUF3, "SDMA_MBANK_DATA_BUF3"}, - {AMDGPU_SDMA_MBANK_DATA_BUF4, "SDMA_MBANK_DATA_BUF4"}, - {AMDGPU_SDMA_MBANK_DATA_BUF5, "SDMA_MBANK_DATA_BUF5"}, - {AMDGPU_SDMA_MBANK_DATA_BUF6, "SDMA_MBANK_DATA_BUF6"}, - {AMDGPU_SDMA_MBANK_DATA_BUF7, "SDMA_MBANK_DATA_BUF7"}, - {AMDGPU_SDMA_MBANK_DATA_BUF8, "SDMA_MBANK_DATA_BUF8"}, - {AMDGPU_SDMA_MBANK_DATA_BUF9, "SDMA_MBANK_DATA_BUF9"}, - {AMDGPU_SDMA_MBANK_DATA_BUF10, "SDMA_MBANK_DATA_BUF10"}, - {AMDGPU_SDMA_MBANK_DATA_BUF11, "SDMA_MBANK_DATA_BUF11"}, - {AMDGPU_SDMA_MBANK_DATA_BUF12, "SDMA_MBANK_DATA_BUF12"}, - {AMDGPU_SDMA_MBANK_DATA_BUF13, "SDMA_MBANK_DATA_BUF13"}, - {AMDGPU_SDMA_MBANK_DATA_BUF14, "SDMA_MBANK_DATA_BUF14"}, - {AMDGPU_SDMA_MBANK_DATA_BUF15, "SDMA_MBANK_DATA_BUF15"}, - {AMDGPU_SDMA_UCODE_BUF, "SDMA_UCODE_BUF"}, - {AMDGPU_SDMA_RB_CMD_BUF, "SDMA_RB_CMD_BUF"}, - {AMDGPU_SDMA_IB_CMD_BUF, "SDMA_IB_CMD_BUF"}, - {AMDGPU_SDMA_UTCL1_RD_FIFO, "SDMA_UTCL1_RD_FIFO"}, - {AMDGPU_SDMA_UTCL1_RDBST_FIFO, "SDMA_UTCL1_RDBST_FIFO"}, - {AMDGPU_SDMA_UTCL1_WR_FIFO, "SDMA_UTCL1_WR_FIFO"}, - {AMDGPU_SDMA_DATA_LUT_FIFO, "SDMA_DATA_LUT_FIFO"}, - {AMDGPU_SDMA_SPLIT_DAT_BUF, "SDMA_SPLIT_DAT_BUF"}, -}; - -static void sdma_v4_4_2_inst_query_ras_error_count(struct amdgpu_device *adev, - uint32_t sdma_inst, - void *ras_err_status) -{ - struct ras_err_data *err_data = (struct ras_err_data *)ras_err_status; - uint32_t sdma_dev_inst = GET_INST(SDMA0, sdma_inst); - unsigned long ue_count = 0; - struct amdgpu_smuio_mcm_config_info mcm_info = { - .socket_id = adev->smuio.funcs->get_socket_id(adev), - .die_id = adev->sdma.instance[sdma_inst].aid_id, - }; - - /* sdma v4_4_2 doesn't support query ce counts */ - amdgpu_ras_inst_query_ras_error_count(adev, - sdma_v4_2_2_ue_reg_list, - ARRAY_SIZE(sdma_v4_2_2_ue_reg_list), - sdma_v4_4_2_ras_memory_list, - ARRAY_SIZE(sdma_v4_4_2_ras_memory_list), - sdma_dev_inst, - AMDGPU_RAS_ERROR__MULTI_UNCORRECTABLE, - &ue_count); - - amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, ue_count); -} - -static void sdma_v4_4_2_query_ras_error_count(struct amdgpu_device *adev, - void *ras_err_status) -{ - uint32_t inst_mask; - int i = 0; - - inst_mask = GENMASK(adev->sdma.num_instances - 1, 0); - if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__SDMA)) { - for_each_inst(i, inst_mask) - sdma_v4_4_2_inst_query_ras_error_count(adev, i, ras_err_status); - } else { - dev_warn(adev->dev, "SDMA RAS is not supported\n"); - } -} - -static void sdma_v4_4_2_inst_reset_ras_error_count(struct amdgpu_device *adev, - uint32_t sdma_inst) -{ - uint32_t sdma_dev_inst = GET_INST(SDMA0, sdma_inst); - - amdgpu_ras_inst_reset_ras_error_count(adev, - sdma_v4_2_2_ue_reg_list, - ARRAY_SIZE(sdma_v4_2_2_ue_reg_list), - sdma_dev_inst); -} - -static void sdma_v4_4_2_reset_ras_error_count(struct amdgpu_device *adev) -{ - uint32_t inst_mask; - int i = 0; - - inst_mask = GENMASK(adev->sdma.num_instances - 1, 0); - if (amdgpu_ras_is_supported(adev, AMDGPU_RAS_BLOCK__SDMA)) { - for_each_inst(i, inst_mask) - sdma_v4_4_2_inst_reset_ras_error_count(adev, i); - } else { - dev_warn(adev->dev, "SDMA RAS is not supported\n"); - } -} - -static const struct amdgpu_ras_block_hw_ops sdma_v4_4_2_ras_hw_ops = { - .query_ras_error_count = sdma_v4_4_2_query_ras_error_count, - .reset_ras_error_count = sdma_v4_4_2_reset_ras_error_count, -}; - -static int sdma_v4_4_2_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - struct aca_bank_info info; - u64 misc0; - int ret; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return ret; - - misc0 = bank->regs[ACA_REG_IDX_MISC0]; - switch (type) { - case ACA_SMU_TYPE_UE: - bank->aca_err_type = ACA_ERROR_TYPE_UE; - ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE, - 1ULL); - break; - case ACA_SMU_TYPE_CE: - bank->aca_err_type = ACA_ERROR_TYPE_CE; - ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, - ACA_REG__MISC0__ERRCNT(misc0)); - break; - default: - return -EINVAL; - } - - return ret; -} - -/* CODE_SDMA0 - CODE_SDMA4, reference to smu driver if header file */ -static int sdma_v4_4_2_err_codes[] = { 33, 34, 35, 36 }; - -static bool sdma_v4_4_2_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - u32 instlo; - - instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]); - instlo &= GENMASK(31, 1); - - if (instlo != mmSMNAID_AID0_MCA_SMU) - return false; - - if (aca_bank_check_error_codes(handle->adev, bank, - sdma_v4_4_2_err_codes, - ARRAY_SIZE(sdma_v4_4_2_err_codes))) - return false; - - return true; -} - -static const struct aca_bank_ops sdma_v4_4_2_aca_bank_ops = { - .aca_bank_parser = sdma_v4_4_2_aca_bank_parser, - .aca_bank_is_valid = sdma_v4_4_2_aca_bank_is_valid, -}; - -static const struct aca_info sdma_v4_4_2_aca_info = { - .hwip = ACA_HWIP_TYPE_SMU, - .mask = ACA_ERROR_UE_MASK, - .bank_ops = &sdma_v4_4_2_aca_bank_ops, -}; - -static int sdma_v4_4_2_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block) -{ - int r; - - r = amdgpu_sdma_ras_late_init(adev, ras_block); - if (r) - return r; - - return amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__SDMA, - &sdma_v4_4_2_aca_info, NULL); -} - static struct amdgpu_sdma_ras sdma_v4_4_2_ras = { .ras_block = { - .hw_ops = &sdma_v4_4_2_ras_hw_ops, - .ras_late_init = sdma_v4_4_2_ras_late_init, + .hw_ops = NULL, }, }; diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c index fa02907217e0..b809942b1eb7 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_0.c @@ -527,7 +527,7 @@ static void sdma_v5_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -538,7 +538,7 @@ static void sdma_v5_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c index f6ecbc524c9b..87c1e29fd298 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v5_2.c @@ -377,7 +377,7 @@ static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -388,7 +388,7 @@ static void sdma_v5_2_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c index d894b7599c18..7a3f1a60b014 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v6_0.c @@ -361,7 +361,7 @@ static void sdma_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -372,7 +372,7 @@ static void sdma_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); @@ -793,23 +793,6 @@ static int sdma_v6_0_soft_reset(struct amdgpu_ip_block *ip_block) return sdma_v6_0_start(adev); } -static bool sdma_v6_0_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - struct amdgpu_ring *ring; - int i, r; - long tmo = msecs_to_jiffies(1000); - - for (i = 0; i < adev->sdma.num_instances; i++) { - ring = &adev->sdma.instance[i].ring; - r = amdgpu_ring_test_ib(ring, tmo); - if (r) - return true; - } - - return false; -} - /** * sdma_v6_0_start - setup and start the async dma engines * @@ -1747,7 +1730,6 @@ const struct amd_ip_funcs sdma_v6_0_ip_funcs = { .is_idle = sdma_v6_0_is_idle, .wait_for_idle = sdma_v6_0_wait_for_idle, .soft_reset = sdma_v6_0_soft_reset, - .check_soft_reset = sdma_v6_0_check_soft_reset, .set_clockgating_state = sdma_v6_0_set_clockgating_state, .set_powergating_state = sdma_v6_0_set_powergating_state, .get_clockgating_state = sdma_v6_0_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c index f154b68dda70..84305b6800fe 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_0.c @@ -363,7 +363,7 @@ static void sdma_v7_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -374,7 +374,7 @@ static void sdma_v7_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); @@ -784,23 +784,6 @@ static int sdma_v7_0_soft_reset(struct amdgpu_ip_block *ip_block) return sdma_v7_0_start(adev); } -static bool sdma_v7_0_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - struct amdgpu_ring *ring; - int i, r; - long tmo = msecs_to_jiffies(1000); - - for (i = 0; i < adev->sdma.num_instances; i++) { - ring = &adev->sdma.instance[i].ring; - r = amdgpu_ring_test_ib(ring, tmo); - if (r) - return true; - } - - return false; -} - static int sdma_v7_0_reset_queue(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *timedout_fence) @@ -1679,7 +1662,6 @@ const struct amd_ip_funcs sdma_v7_0_ip_funcs = { .is_idle = sdma_v7_0_is_idle, .wait_for_idle = sdma_v7_0_wait_for_idle, .soft_reset = sdma_v7_0_soft_reset, - .check_soft_reset = sdma_v7_0_check_soft_reset, .set_clockgating_state = sdma_v7_0_set_clockgating_state, .set_powergating_state = sdma_v7_0_set_powergating_state, .get_clockgating_state = sdma_v7_0_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c index cd9668605a50..322e6f4dd121 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v7_1.c @@ -331,7 +331,7 @@ static void sdma_v7_1_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* Ucached(UC) */ /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, lower_32_bits(seq)); @@ -342,7 +342,7 @@ static void sdma_v7_1_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 se amdgpu_ring_write(ring, SDMA_PKT_COPY_LINEAR_HEADER_OP(SDMA_OP_FENCE) | SDMA_PKT_FENCE_HEADER_MTYPE(0x3)); /* zero in first two bits */ - BUG_ON(addr & 0x3); + WARN_ON(addr & 0x3); amdgpu_ring_write(ring, lower_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(addr)); amdgpu_ring_write(ring, upper_32_bits(seq)); @@ -775,23 +775,6 @@ static int sdma_v7_1_soft_reset(struct amdgpu_ip_block *ip_block) return sdma_v7_1_inst_start(adev, inst_mask); } -static bool sdma_v7_1_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - struct amdgpu_ring *ring; - int i, r; - long tmo = msecs_to_jiffies(1000); - - for (i = 0; i < adev->sdma.num_instances; i++) { - ring = &adev->sdma.instance[i].ring; - r = amdgpu_ring_test_ib(ring, tmo); - if (r) - return true; - } - - return false; -} - static int sdma_v7_1_reset_queue(struct amdgpu_ring *ring, unsigned int vmid, struct amdgpu_fence *timedout_fence) @@ -1644,7 +1627,6 @@ const struct amd_ip_funcs sdma_v7_1_ip_funcs = { .is_idle = sdma_v7_1_is_idle, .wait_for_idle = sdma_v7_1_wait_for_idle, .soft_reset = sdma_v7_1_soft_reset, - .check_soft_reset = sdma_v7_1_check_soft_reset, .set_clockgating_state = sdma_v7_1_set_clockgating_state, .set_powergating_state = sdma_v7_1_set_powergating_state, .get_clockgating_state = sdma_v7_1_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/si.c b/drivers/gpu/drm/amd/amdgpu/si.c index c26cb3e8bff6..b104469c38ec 100644 --- a/drivers/gpu/drm/amd/amdgpu/si.c +++ b/drivers/gpu/drm/amd/amdgpu/si.c @@ -1509,12 +1509,6 @@ static void si_invalidate_hdp(struct amdgpu_device *adev, } } -static bool si_need_full_reset(struct amdgpu_device *adev) -{ - /* change this when we support soft reset */ - return true; -} - static bool si_need_reset_on_init(struct amdgpu_device *adev) { return false; @@ -2019,7 +2013,6 @@ static const struct amdgpu_asic_funcs si_asic_funcs = .get_config_memsize = &si_get_config_memsize, .flush_hdp = &si_flush_hdp, .invalidate_hdp = &si_invalidate_hdp, - .need_full_reset = &si_need_full_reset, .get_pcie_usage = &si_get_pcie_usage, .need_reset_on_init = &si_need_reset_on_init, .get_pcie_replay_count = &si_get_pcie_replay_count, diff --git a/drivers/gpu/drm/amd/amdgpu/soc15.c b/drivers/gpu/drm/amd/amdgpu/soc15.c index 87b398dd0769..ed3fd58b78d0 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15.c +++ b/drivers/gpu/drm/amd/amdgpu/soc15.c @@ -721,12 +721,6 @@ void soc15_set_virt_ops(struct amdgpu_device *adev) soc15_reg_base_init(adev); } -static bool soc15_need_full_reset(struct amdgpu_device *adev) -{ - /* change this when we implement soft reset */ - return true; -} - static void soc15_get_pcie_usage(struct amdgpu_device *adev, uint64_t *count0, uint64_t *count1) { @@ -878,7 +872,6 @@ static const struct amdgpu_asic_funcs soc15_asic_funcs = .set_uvd_clocks = &soc15_set_uvd_clocks, .set_vce_clocks = &soc15_set_vce_clocks, .get_config_memsize = &soc15_get_config_memsize, - .need_full_reset = &soc15_need_full_reset, .init_doorbell_index = &vega10_doorbell_index_init, .get_pcie_usage = &soc15_get_pcie_usage, .need_reset_on_init = &soc15_need_reset_on_init, @@ -899,7 +892,6 @@ static const struct amdgpu_asic_funcs vega20_asic_funcs = .set_uvd_clocks = &soc15_set_uvd_clocks, .set_vce_clocks = &soc15_set_vce_clocks, .get_config_memsize = &soc15_get_config_memsize, - .need_full_reset = &soc15_need_full_reset, .init_doorbell_index = &vega20_doorbell_index_init, .get_pcie_usage = &vega20_get_pcie_usage, .need_reset_on_init = &soc15_need_reset_on_init, @@ -920,7 +912,6 @@ static const struct amdgpu_asic_funcs aqua_vanjaram_asic_funcs = .set_uvd_clocks = &soc15_set_uvd_clocks, .set_vce_clocks = &soc15_set_vce_clocks, .get_config_memsize = &soc15_get_config_memsize, - .need_full_reset = &soc15_need_full_reset, .init_doorbell_index = &aqua_vanjaram_doorbell_index_init, .need_reset_on_init = &soc15_need_reset_on_init, .get_pcie_replay_count = &amdgpu_nbio_get_pcie_replay_count, diff --git a/drivers/gpu/drm/amd/amdgpu/soc15_common.h b/drivers/gpu/drm/amd/amdgpu/soc15_common.h index a7b5a95ebebb..47e0329b6f3f 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc15_common.h +++ b/drivers/gpu/drm/amd/amdgpu/soc15_common.h @@ -38,30 +38,30 @@ (adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + (reg)+(offset)) #define __WREG32_SOC15_RLC__(reg, value, flag, hwip, inst) \ - ((amdgpu_sriov_vf(adev) && adev->gfx.rlc.funcs && adev->gfx.rlc.rlcg_reg_access_supported) ? \ - amdgpu_sriov_wreg(adev, reg, value, flag, hwip, inst) : \ - WREG32(reg, value)) + adev->gfx.rlc.reg_funcs->wreg32(adev, reg, value, flag, hwip, inst) #define __RREG32_SOC15_RLC__(reg, flag, hwip, inst) \ - ((amdgpu_sriov_vf(adev) && adev->gfx.rlc.funcs && adev->gfx.rlc.rlcg_reg_access_supported) ? \ - amdgpu_sriov_rreg(adev, reg, flag, hwip, inst) : \ - RREG32(reg)) - -#define WREG32_FIELD15(ip, idx, reg, field, val) \ - __WREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][idx][mm##reg##_BASE_IDX] + mm##reg, \ - (__RREG32_SOC15_RLC__( \ - adev->reg_offset[ip##_HWIP][idx][mm##reg##_BASE_IDX] + mm##reg, \ - 0, ip##_HWIP, idx) & \ - ~REG_FIELD_MASK(reg, field)) | (val) << REG_FIELD_SHIFT(reg, field), \ - 0, ip##_HWIP, idx) - -#define WREG32_FIELD15_PREREG(ip, idx, reg_name, field, val) \ - __WREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][idx][reg##reg_name##_BASE_IDX] + reg##reg_name, \ - (__RREG32_SOC15_RLC__( \ - adev->reg_offset[ip##_HWIP][idx][reg##reg_name##_BASE_IDX] + reg##reg_name, \ - 0, ip##_HWIP, idx) & \ - ~REG_FIELD_MASK(reg_name, field)) | (val) << REG_FIELD_SHIFT(reg_name, field), \ - 0, ip##_HWIP, idx) + adev->gfx.rlc.reg_funcs->rreg32(adev, reg, flag, hwip, inst) + +#define WREG32_FIELD15(ip, idx, reg_name, field, val) \ +do { \ + u32 reg__ = adev->reg_offset[ip##_HWIP][idx][mm##reg_name##_BASE_IDX] + mm##reg_name; \ + u32 val__ = __RREG32_SOC15_RLC__(reg__, 0, ip##_HWIP, idx); \ +\ + val__ &= ~REG_FIELD_MASK(reg_name, field); \ + val__ |= (val) << REG_FIELD_SHIFT(reg_name, field); \ + __WREG32_SOC15_RLC__(reg__, val__, 0, ip##_HWIP, idx); \ +} while (0) + +#define WREG32_FIELD15_PREREG(ip, idx, reg_name, field, val) \ +do { \ + u32 reg__ = adev->reg_offset[ip##_HWIP][idx][reg##reg_name##_BASE_IDX] + reg##reg_name; \ + u32 val__ = __RREG32_SOC15_RLC__(reg__, 0, ip##_HWIP, idx); \ +\ + val__ &= ~REG_FIELD_MASK(reg_name, field); \ + val__ |= (val) << REG_FIELD_SHIFT(reg_name, field); \ + __WREG32_SOC15_RLC__(reg__, val__, 0, ip##_HWIP, idx); \ +} while (0) #define RREG32_SOC15(ip, inst, reg) \ __RREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg, \ @@ -181,12 +181,15 @@ WREG32_RLC_EX(prefix, target_reg, value, inst); \ } while (0) -#define WREG32_FIELD15_RLC(ip, idx, reg, field, val) \ - __WREG32_SOC15_RLC__((adev->reg_offset[ip##_HWIP][idx][mm##reg##_BASE_IDX] + mm##reg), \ - (__RREG32_SOC15_RLC__(adev->reg_offset[ip##_HWIP][idx][mm##reg##_BASE_IDX] + mm##reg, \ - AMDGPU_REGS_RLC, ip##_HWIP, idx) & \ - ~REG_FIELD_MASK(reg, field)) | (val) << REG_FIELD_SHIFT(reg, field), \ - AMDGPU_REGS_RLC, ip##_HWIP, idx) +#define WREG32_FIELD15_RLC(ip, idx, reg_name, field, val) \ +do { \ + u32 reg__ = adev->reg_offset[ip##_HWIP][idx][mm##reg_name##_BASE_IDX] + mm##reg_name; \ + u32 val__ = __RREG32_SOC15_RLC__(reg__, AMDGPU_REGS_RLC, ip##_HWIP, idx); \ +\ + val__ &= ~REG_FIELD_MASK(reg_name, field); \ + val__ |= (val) << REG_FIELD_SHIFT(reg_name, field); \ + __WREG32_SOC15_RLC__(reg__, val__, AMDGPU_REGS_RLC, ip##_HWIP, idx); \ +} while (0) #define WREG32_SOC15_OFFSET_RLC(ip, inst, reg, offset, value) \ __WREG32_SOC15_RLC__((adev->reg_offset[ip##_HWIP][inst][reg##_BASE_IDX] + reg) + offset, value, AMDGPU_REGS_RLC, ip##_HWIP, inst) @@ -207,10 +210,4 @@ amdgpu_reg_get_smn_base64(adev, ip##_HWIP, inst), \ value) -#define RREG64_MCA(smn_base, mca_base, idx) \ - RREG64_PCIE_EXT(smn_base + mca_base + (idx * 8)) - -#define WREG64_MCA(smn_base, mca_base, idx, val) \ - WREG64_PCIE_EXT(smn_base + mca_base + (idx * 8), val) - #endif diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index 963659deeaff..09f28dbd60ee 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -461,17 +461,6 @@ const struct amdgpu_ip_block_version soc21_common_ip_block = { .funcs = &soc21_common_ip_funcs, }; -static bool soc21_need_full_reset(struct amdgpu_device *adev) -{ - switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { - case IP_VERSION(11, 0, 0): - case IP_VERSION(11, 0, 2): - case IP_VERSION(11, 0, 3): - default: - return true; - } -} - static bool soc21_need_reset_on_init(struct amdgpu_device *adev) { u32 sol_reg; @@ -550,7 +539,6 @@ static const struct amdgpu_asic_funcs soc21_asic_funcs = { .set_vce_clocks = &soc21_set_vce_clocks, .get_config_memsize = &soc21_get_config_memsize, .init_doorbell_index = &soc21_init_doorbell_index, - .need_full_reset = &soc21_need_full_reset, .need_reset_on_init = &soc21_need_reset_on_init, .get_pcie_replay_count = &amdgpu_nbio_get_pcie_replay_count, .supports_baco = &amdgpu_dpm_is_baco_supported, @@ -838,6 +826,62 @@ static int soc21_common_early_init(struct amdgpu_ip_block *ip_block) adev->pg_flags = 0; adev->external_rev_id = adev->rev_id + 0xd0; break; + case IP_VERSION(11, 7, 0): + adev->cg_flags = AMD_CG_SUPPORT_VCN_MGCG | + AMD_CG_SUPPORT_JPEG_MGCG | + AMD_CG_SUPPORT_GFX_CGCG | + AMD_CG_SUPPORT_GFX_CGLS | + AMD_CG_SUPPORT_GFX_MGCG | + AMD_CG_SUPPORT_GFX_FGCG | + AMD_CG_SUPPORT_REPEATER_FGCG | + AMD_CG_SUPPORT_GFX_PERF_CLK | + AMD_CG_SUPPORT_GFX_3D_CGCG | + AMD_CG_SUPPORT_GFX_3D_CGLS | + AMD_CG_SUPPORT_MC_MGCG | + AMD_CG_SUPPORT_MC_LS | + AMD_CG_SUPPORT_HDP_LS | + AMD_CG_SUPPORT_HDP_DS | + AMD_CG_SUPPORT_HDP_SD | + AMD_CG_SUPPORT_ATHUB_MGCG | + AMD_CG_SUPPORT_ATHUB_LS | + AMD_CG_SUPPORT_IH_CG | + AMD_CG_SUPPORT_BIF_MGCG | + AMD_CG_SUPPORT_BIF_LS; + adev->pg_flags = AMD_PG_SUPPORT_VCN_DPG | + AMD_PG_SUPPORT_VCN | + AMD_PG_SUPPORT_JPEG_DPG | + AMD_PG_SUPPORT_JPEG | + AMD_PG_SUPPORT_GFX_PG; + adev->external_rev_id = adev->rev_id + 0xF; + break; + case IP_VERSION(11, 7, 1): + adev->cg_flags = AMD_CG_SUPPORT_VCN_MGCG | + AMD_CG_SUPPORT_JPEG_MGCG | + AMD_CG_SUPPORT_GFX_CGCG | + AMD_CG_SUPPORT_GFX_CGLS | + AMD_CG_SUPPORT_GFX_MGCG | + AMD_CG_SUPPORT_GFX_FGCG | + AMD_CG_SUPPORT_REPEATER_FGCG | + AMD_CG_SUPPORT_GFX_PERF_CLK | + AMD_CG_SUPPORT_GFX_3D_CGCG | + AMD_CG_SUPPORT_GFX_3D_CGLS | + AMD_CG_SUPPORT_MC_MGCG | + AMD_CG_SUPPORT_MC_LS | + AMD_CG_SUPPORT_HDP_LS | + AMD_CG_SUPPORT_HDP_DS | + AMD_CG_SUPPORT_HDP_SD | + AMD_CG_SUPPORT_ATHUB_MGCG | + AMD_CG_SUPPORT_ATHUB_LS | + AMD_CG_SUPPORT_IH_CG | + AMD_CG_SUPPORT_BIF_MGCG | + AMD_CG_SUPPORT_BIF_LS; + adev->pg_flags = AMD_PG_SUPPORT_VCN_DPG | + AMD_PG_SUPPORT_VCN | + AMD_PG_SUPPORT_JPEG_DPG | + AMD_PG_SUPPORT_JPEG | + AMD_PG_SUPPORT_GFX_PG; + adev->external_rev_id = adev->rev_id + 0x40; + break; default: /* FIXME: not supported yet */ return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdgpu/soc24.c b/drivers/gpu/drm/amd/amdgpu/soc24.c index 265db9331d0b..e5e3a460e486 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc24.c +++ b/drivers/gpu/drm/amd/amdgpu/soc24.c @@ -238,16 +238,6 @@ const struct amdgpu_ip_block_version soc24_common_ip_block = { .funcs = &soc24_common_ip_funcs, }; -static bool soc24_need_full_reset(struct amdgpu_device *adev) -{ - switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { - case IP_VERSION(12, 0, 0): - case IP_VERSION(12, 0, 1): - default: - return true; - } -} - static bool soc24_need_reset_on_init(struct amdgpu_device *adev) { u32 sol_reg; @@ -330,7 +320,6 @@ static const struct amdgpu_asic_funcs soc24_asic_funcs = { .get_xclk = &soc24_get_xclk, .get_config_memsize = &soc24_get_config_memsize, .init_doorbell_index = &soc24_init_doorbell_index, - .need_full_reset = &soc24_need_full_reset, .need_reset_on_init = &soc24_need_reset_on_init, .get_pcie_replay_count = &soc24_get_pcie_replay_count, .supports_baco = &amdgpu_dpm_is_baco_supported, @@ -496,8 +485,36 @@ static int soc24_common_suspend(struct amdgpu_ip_block *ip_block) return soc24_common_hw_fini(ip_block); } +static bool soc24_need_reset_on_resume(struct amdgpu_device *adev) +{ + u32 sol_reg1, sol_reg2; + + /* Will reset for the following suspend abort cases. + * 1) Only reset dGPU side. + * 2) S3 suspend got aborted and TOS is active. + * As for dGPU suspend abort cases the SOL value + * will be kept as zero at this resume point. + */ + if (!(adev->flags & AMD_IS_APU) && adev->in_s3) { + sol_reg1 = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_81); + msleep(100); + sol_reg2 = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_81); + + return (sol_reg1 != sol_reg2); + } + + return false; +} + static int soc24_common_resume(struct amdgpu_ip_block *ip_block) { + struct amdgpu_device *adev = ip_block->adev; + + if (soc24_need_reset_on_resume(adev)) { + dev_info(adev->dev, "S3 suspend aborted, resetting..."); + soc24_asic_reset(adev); + } + return soc24_common_hw_init(ip_block); } diff --git a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c index 5f05c8e68297..a9039fb1a77b 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c +++ b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c @@ -223,15 +223,6 @@ static int soc_v1_0_read_register(struct amdgpu_device *adev, return -EINVAL; } -static bool soc_v1_0_need_full_reset(struct amdgpu_device *adev) -{ - switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { - case IP_VERSION(12, 1, 0): - default: - return true; - } -} - static bool soc_v1_0_need_reset_on_init(struct amdgpu_device *adev) { @@ -271,7 +262,6 @@ static const struct amdgpu_asic_funcs soc_v1_0_asic_funcs = { .read_register = &soc_v1_0_read_register, .get_config_memsize = &soc_v1_0_get_config_memsize, .get_xclk = &soc_v1_0_get_xclk, - .need_full_reset = &soc_v1_0_need_full_reset, .init_doorbell_index = &soc_v1_0_doorbell_index_init, .need_reset_on_init = &soc_v1_0_need_reset_on_init, .encode_ext_smn_addressing = &soc_v1_0_encode_ext_smn_addressing, @@ -600,8 +590,10 @@ static int soc_v1_0_get_xcp_res_info(struct amdgpu_xcp_mgr *xcp_mgr, xcp_cfg->num_res = ARRAY_SIZE(max_res); for (i = 0; i < xcp_cfg->num_res; i++) { - res_lt_xcp = max_res[i] < num_xcp; xcp_cfg->xcp_res[i].id = i; + if (!max_res[i]) + continue; + res_lt_xcp = max_res[i] < num_xcp; xcp_cfg->xcp_res[i].num_inst = res_lt_xcp ? 1 : max_res[i] / num_xcp; xcp_cfg->xcp_res[i].num_inst = diff --git a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c index ee8038df17e3..a3e883f6f099 100644 --- a/drivers/gpu/drm/amd/amdgpu/tonga_ih.c +++ b/drivers/gpu/drm/amd/amdgpu/tonga_ih.c @@ -390,43 +390,6 @@ static int tonga_ih_wait_for_idle(struct amdgpu_ip_block *ip_block) return -ETIMEDOUT; } -static bool tonga_ih_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 srbm_soft_reset = 0; - u32 tmp = RREG32(mmSRBM_STATUS); - - if (tmp & SRBM_STATUS__IH_BUSY_MASK) - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, - SOFT_RESET_IH, 1); - - if (srbm_soft_reset) { - adev->irq.srbm_soft_reset = srbm_soft_reset; - return true; - } else { - adev->irq.srbm_soft_reset = 0; - return false; - } -} - -static int tonga_ih_pre_soft_reset(struct amdgpu_ip_block *ip_block) -{ - if (!ip_block->adev->irq.srbm_soft_reset) - return 0; - - return tonga_ih_hw_fini(ip_block); -} - -static int tonga_ih_post_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - if (!adev->irq.srbm_soft_reset) - return 0; - - return tonga_ih_hw_init(ip_block); -} - static int tonga_ih_soft_reset(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -481,10 +444,7 @@ static const struct amd_ip_funcs tonga_ih_ip_funcs = { .resume = tonga_ih_resume, .is_idle = tonga_ih_is_idle, .wait_for_idle = tonga_ih_wait_for_idle, - .check_soft_reset = tonga_ih_check_soft_reset, - .pre_soft_reset = tonga_ih_pre_soft_reset, .soft_reset = tonga_ih_soft_reset, - .post_soft_reset = tonga_ih_post_soft_reset, .set_clockgating_state = tonga_ih_set_clockgating_state, .set_powergating_state = tonga_ih_set_powergating_state, }; diff --git a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c index 14092150336a..67bdf7303e6b 100644 --- a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c +++ b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.c @@ -28,48 +28,6 @@ #include "umc/umc_12_0_0_sh_mask.h" #include "mp/mp_13_0_6_sh_mask.h" -#define MAX_ECC_NUM_PER_RETIREMENT 32 -#define DELAYED_TIME_FOR_GPU_RESET 1000 //ms - -static inline uint64_t get_umc_v12_0_reg_offset(struct amdgpu_device *adev, - uint32_t node_inst, - uint32_t umc_inst, - uint32_t ch_inst) -{ - uint32_t index = umc_inst * adev->umc.channel_inst_num + ch_inst; - uint64_t cross_node_offset = (node_inst == 0) ? 0 : UMC_V12_0_CROSS_NODE_OFFSET; - - umc_inst = index / 4; - ch_inst = index % 4; - - return adev->umc.channel_offs * ch_inst + UMC_V12_0_INST_DIST * umc_inst + - UMC_V12_0_NODE_DIST * node_inst + cross_node_offset; -} - -static int umc_v12_0_reset_error_count_per_channel(struct amdgpu_device *adev, - uint32_t node_inst, uint32_t umc_inst, - uint32_t ch_inst, void *data) -{ - uint64_t odecc_err_cnt_addr; - uint64_t umc_reg_offset = - get_umc_v12_0_reg_offset(adev, node_inst, umc_inst, ch_inst); - - odecc_err_cnt_addr = - SOC15_REG_OFFSET(UMC, 0, regUMCCH0_OdEccErrCnt); - - /* clear error count */ - WREG32_PCIE_EXT((odecc_err_cnt_addr + umc_reg_offset) * 4, - UMC_V12_0_CE_CNT_INIT); - - return 0; -} - -static void umc_v12_0_reset_error_count(struct amdgpu_device *adev) -{ - amdgpu_umc_loop_channels(adev, - umc_v12_0_reset_error_count_per_channel, NULL); -} - bool umc_v12_0_is_deferred_error(struct amdgpu_device *adev, uint64_t mc_umc_status) { dev_dbg(adev->dev, @@ -115,65 +73,6 @@ bool umc_v12_0_is_correctable_error(struct amdgpu_device *adev, uint64_t mc_umc_ !(umc_v12_0_is_uncorrectable_error(adev, mc_umc_status))))); } -static void umc_v12_0_query_error_count_per_type(struct amdgpu_device *adev, - uint64_t umc_reg_offset, - unsigned long *error_count, - check_error_type_func error_type_func) -{ - uint64_t mc_umc_status; - uint64_t mc_umc_status_addr; - - mc_umc_status_addr = - SOC15_REG_OFFSET(UMC, 0, regMCA_UMC_UMC0_MCUMC_STATUST0); - - /* Check MCUMC_STATUS */ - mc_umc_status = - RREG64_PCIE_EXT((mc_umc_status_addr + umc_reg_offset) * 4); - - if (error_type_func(adev, mc_umc_status)) - *error_count += 1; -} - -static int umc_v12_0_query_error_count(struct amdgpu_device *adev, - uint32_t node_inst, uint32_t umc_inst, - uint32_t ch_inst, void *data) -{ - struct ras_err_data *err_data = (struct ras_err_data *)data; - unsigned long ue_count = 0, ce_count = 0, de_count = 0; - - /* NOTE: node_inst is converted by adev->umc.active_mask and the range is [0-3], - * which can be used as die ID directly */ - struct amdgpu_smuio_mcm_config_info mcm_info = { - .socket_id = adev->smuio.funcs->get_socket_id(adev), - .die_id = node_inst, - }; - - uint64_t umc_reg_offset = - get_umc_v12_0_reg_offset(adev, node_inst, umc_inst, ch_inst); - - umc_v12_0_query_error_count_per_type(adev, umc_reg_offset, - &ce_count, umc_v12_0_is_correctable_error); - umc_v12_0_query_error_count_per_type(adev, umc_reg_offset, - &ue_count, umc_v12_0_is_uncorrectable_error); - umc_v12_0_query_error_count_per_type(adev, umc_reg_offset, - &de_count, umc_v12_0_is_deferred_error); - - amdgpu_ras_error_statistic_ue_count(err_data, &mcm_info, ue_count); - amdgpu_ras_error_statistic_ce_count(err_data, &mcm_info, ce_count); - amdgpu_ras_error_statistic_de_count(err_data, &mcm_info, de_count); - - return 0; -} - -static void umc_v12_0_query_ras_error_count(struct amdgpu_device *adev, - void *ras_error_status) -{ - amdgpu_umc_loop_channels(adev, - umc_v12_0_query_error_count, ras_error_status); - - umc_v12_0_reset_error_count(adev); -} - static void umc_v12_0_get_retire_flip_bits(struct amdgpu_device *adev) { enum amdgpu_memory_partition nps = AMDGPU_NPS1_PARTITION_MODE; @@ -279,190 +178,6 @@ static void umc_v12_0_get_retire_flip_bits(struct amdgpu_device *adev) adev->umc.retire_unit = 0x1 << flip_bits->bit_num; } -static int umc_v12_0_convert_error_address(struct amdgpu_device *adev, - struct ras_err_data *err_data, - struct ta_ras_query_address_input *addr_in, - struct ta_ras_query_address_output *addr_out, - bool dump_addr) -{ - 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; - struct ta_ras_query_address_output addr_out_tmp; - struct ta_ras_query_address_output *paddr_out; - int ret = 0; - - if (!addr_out) - paddr_out = &addr_out_tmp; - else - paddr_out = addr_out; - - err_addr = bank = 0; - if (addr_in) { - err_addr = addr_in->ma.err_addr; - addr_in->addr_type = TA_RAS_MCA_TO_PA; - ret = psp_ras_query_address(&adev->psp, addr_in, paddr_out); - if (ret) { - dev_warn(adev->dev, "Failed to query RAS physical address for 0x%llx", - err_addr); - - goto out; - } - - bank = paddr_out->pa.bank; - /* no need to care about umc inst if addr_in is NULL */ - umc_inst = addr_in->ma.umc_inst; - } - - flip_bits = adev->umc.flip_bits.flip_bits_in_pa; - bit_num = adev->umc.flip_bits.bit_num; - retire_unit = adev->umc.retire_unit; - - soc_pa = paddr_out->pa.pa; - channel_index = paddr_out->pa.channel_idx; - /* clear loop bits in soc physical address */ - for (i = 0; i < bit_num; i++) - soc_pa &= ~BIT_ULL(flip_bits[i]); - - paddr_out->pa.pa = soc_pa; - /* get column bit 0 and 1 in mca address */ - col_lower = (err_addr >> 1) & 0x3ULL; - /* extra row bit will be handled later */ - row_lower = (err_addr >> UMC_V12_0_MA_R0_BIT) & 0x1fffULL; - row_lower &= ~BIT_ULL(adev->umc.flip_bits.flip_row_bit); - - if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(9, 5, 0)) { - row_high = (soc_pa >> adev->umc.flip_bits.r13_in_pa) & 0x3ULL; - /* it's 2.25GB in each channel, from MCA address to PA - * [R14 R13] is converted if the two bits value are 0x3, - * get them from PA instead of MCA address. - */ - row_lower |= (row_high << 13); - } - - if (!err_data && !dump_addr) - goto out; - - /* loop for all possibilities of retired bits */ - for (column = 0; column < retire_unit; column++) { - soc_pa = paddr_out->pa.pa; - for (i = 0; i < bit_num; i++) - soc_pa |= (((column >> i) & 0x1ULL) << flip_bits[i]); - - col = ((column & 0x7) << 2) | col_lower; - /* handle extra row bit */ - if (bit_num == RETIRE_FLIP_BITS_NUM) - row = ((column >> 3) << adev->umc.flip_bits.flip_row_bit) | - row_lower; - - if (dump_addr) - dev_info(adev->dev, - "Error Address(PA):0x%-10llx Row:0x%-4x Col:0x%-2x Bank:0x%x Channel:0x%x\n", - soc_pa, row, col, bank, channel_index); - - if (err_data) - amdgpu_umc_fill_error_record(err_data, err_addr, - soc_pa, channel_index, umc_inst); - } - -out: - return ret; -} - -static int umc_v12_0_query_error_address(struct amdgpu_device *adev, - uint32_t node_inst, uint32_t umc_inst, - uint32_t ch_inst, void *data) -{ - struct ras_err_data *err_data = (struct ras_err_data *)data; - struct ta_ras_query_address_input addr_in; - uint64_t mc_umc_status_addr; - uint64_t mc_umc_status, err_addr; - uint64_t mc_umc_addrt0; - uint64_t umc_reg_offset = - get_umc_v12_0_reg_offset(adev, node_inst, umc_inst, ch_inst); - - mc_umc_status_addr = - SOC15_REG_OFFSET(UMC, 0, regMCA_UMC_UMC0_MCUMC_STATUST0); - - mc_umc_status = RREG64_PCIE_EXT((mc_umc_status_addr + umc_reg_offset) * 4); - - if (mc_umc_status == 0) - return 0; - - if (!err_data->err_addr) { - /* clear umc status */ - WREG64_PCIE_EXT((mc_umc_status_addr + umc_reg_offset) * 4, 0x0ULL); - - return 0; - } - - /* calculate error address if ue error is detected */ - if (umc_v12_0_is_uncorrectable_error(adev, mc_umc_status) || - umc_v12_0_is_deferred_error(adev, mc_umc_status)) { - mc_umc_addrt0 = - SOC15_REG_OFFSET(UMC, 0, regMCA_UMC_UMC0_MCUMC_ADDRT0); - - err_addr = RREG64_PCIE_EXT((mc_umc_addrt0 + umc_reg_offset) * 4); - - err_addr = REG_GET_FIELD(err_addr, MCA_UMC_UMC0_MCUMC_ADDRT0, ErrorAddr); - - if (!adev->aid_mask && - adev->smuio.funcs && - adev->smuio.funcs->get_socket_id) - addr_in.ma.socket_id = adev->smuio.funcs->get_socket_id(adev); - else - addr_in.ma.socket_id = 0; - - addr_in.ma.err_addr = err_addr; - addr_in.ma.ch_inst = ch_inst; - addr_in.ma.umc_inst = umc_inst; - addr_in.ma.node_inst = node_inst; - - umc_v12_0_convert_error_address(adev, err_data, &addr_in, NULL, true); - } - - /* clear umc status */ - WREG64_PCIE_EXT((mc_umc_status_addr + umc_reg_offset) * 4, 0x0ULL); - - return 0; -} - -static void umc_v12_0_query_ras_error_address(struct amdgpu_device *adev, - void *ras_error_status) -{ - amdgpu_umc_loop_channels(adev, - umc_v12_0_query_error_address, ras_error_status); -} - -static int umc_v12_0_err_cnt_init_per_channel(struct amdgpu_device *adev, - uint32_t node_inst, uint32_t umc_inst, - uint32_t ch_inst, void *data) -{ - uint32_t odecc_cnt_sel; - uint64_t odecc_cnt_sel_addr, odecc_err_cnt_addr; - uint64_t umc_reg_offset = - get_umc_v12_0_reg_offset(adev, node_inst, umc_inst, ch_inst); - - odecc_cnt_sel_addr = - SOC15_REG_OFFSET(UMC, 0, regUMCCH0_OdEccCntSel); - odecc_err_cnt_addr = - SOC15_REG_OFFSET(UMC, 0, regUMCCH0_OdEccErrCnt); - - odecc_cnt_sel = RREG32_PCIE_EXT((odecc_cnt_sel_addr + umc_reg_offset) * 4); - - /* set ce error interrupt type to APIC based interrupt */ - odecc_cnt_sel = REG_SET_FIELD(odecc_cnt_sel, UMCCH0_OdEccCntSel, - OdEccErrInt, 0x1); - WREG32_PCIE_EXT((odecc_cnt_sel_addr + umc_reg_offset) * 4, odecc_cnt_sel); - - /* set error count to initial value */ - WREG32_PCIE_EXT((odecc_err_cnt_addr + umc_reg_offset) * 4, UMC_V12_0_CE_CNT_INIT); - - return 0; -} - static bool umc_v12_0_check_ecc_err_status(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, void *ras_error_status) { @@ -482,309 +197,11 @@ static bool umc_v12_0_check_ecc_err_status(struct amdgpu_device *adev, return false; } -static void umc_v12_0_err_cnt_init(struct amdgpu_device *adev) -{ - amdgpu_umc_loop_channels(adev, - umc_v12_0_err_cnt_init_per_channel, NULL); -} - -static bool umc_v12_0_query_ras_poison_mode(struct amdgpu_device *adev) -{ - /* - * Force return true, because regUMCCH0_EccCtrl - * is not accessible from host side - */ - return true; -} - -const struct amdgpu_ras_block_hw_ops umc_v12_0_ras_hw_ops = { - .query_ras_error_count = umc_v12_0_query_ras_error_count, - .query_ras_error_address = umc_v12_0_query_ras_error_address, -}; - -static int umc_v12_0_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - struct amdgpu_device *adev = handle->adev; - struct aca_bank_info info; - enum aca_error_type err_type; - u64 status, count; - u32 ext_error_code; - int ret; - - status = bank->regs[ACA_REG_IDX_STATUS]; - if (umc_v12_0_is_deferred_error(adev, status)) - err_type = ACA_ERROR_TYPE_DEFERRED; - else if (umc_v12_0_is_uncorrectable_error(adev, status)) - err_type = ACA_ERROR_TYPE_UE; - else if (umc_v12_0_is_correctable_error(adev, status)) - err_type = ACA_ERROR_TYPE_CE; - else - return 0; - bank->aca_err_type = err_type; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return ret; - - amdgpu_umc_update_ecc_status(adev, - bank->regs[ACA_REG_IDX_STATUS], - bank->regs[ACA_REG_IDX_IPID], - bank->regs[ACA_REG_IDX_ADDR]); - - ext_error_code = ACA_REG__STATUS__ERRORCODEEXT(status); - if (umc_v12_0_is_deferred_error(adev, status)) - count = ext_error_code == 0 ? - adev->umc.err_addr_cnt / adev->umc.retire_unit : 1ULL; - else - count = ext_error_code == 0 ? - ACA_REG__MISC0__ERRCNT(bank->regs[ACA_REG_IDX_MISC0]) : 1ULL; - - return aca_error_cache_log_bank_error(handle, &info, err_type, count); -} - -static const struct aca_bank_ops umc_v12_0_aca_bank_ops = { - .aca_bank_parser = umc_v12_0_aca_bank_parser, -}; - -const struct aca_info umc_v12_0_aca_info = { - .hwip = ACA_HWIP_TYPE_UMC, - .mask = ACA_ERROR_UE_MASK | ACA_ERROR_CE_MASK | ACA_ERROR_DEFERRED_MASK, - .bank_ops = &umc_v12_0_aca_bank_ops, -}; - -static int umc_v12_0_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block) -{ - int ret; - - ret = amdgpu_umc_ras_late_init(adev, ras_block); - if (ret) - return ret; - - ret = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__UMC, - &umc_v12_0_aca_info, NULL); - if (ret) - return ret; - - return 0; -} - -static int umc_v12_0_update_ecc_status(struct amdgpu_device *adev, - uint64_t status, uint64_t ipid, uint64_t addr) -{ - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - uint16_t hwid, mcatype; - uint64_t page_pfn[UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL]; - uint64_t err_addr, pa_addr = 0; - struct ras_ecc_err *ecc_err; - struct ta_ras_query_address_output addr_out; - uint32_t shift_bit = adev->umc.flip_bits.flip_bits_in_pa[2]; - int count, ret, i; - - hwid = REG_GET_FIELD(ipid, MCMP1_IPIDT0, HardwareID); - mcatype = REG_GET_FIELD(ipid, MCMP1_IPIDT0, McaType); - - /* The IP block decode of consumption is SMU */ - if (hwid != MCA_UMC_HWID_V12_0 || mcatype != MCA_UMC_MCATYPE_V12_0) { - con->umc_ecc_log.consumption_q_count++; - return 0; - } - - if (!status) - return 0; - - if (!umc_v12_0_is_deferred_error(adev, status)) - return 0; - - err_addr = REG_GET_FIELD(addr, - MCA_UMC_UMC0_MCUMC_ADDRT0, ErrorAddr); - - dev_dbg(adev->dev, - "UMC:IPID:0x%llx, socket:%llu, aid:%llu, inst:%llu, ch:%llu, err_addr:0x%llx\n", - ipid, - MCA_IPID_2_SOCKET_ID(ipid), - MCA_IPID_2_DIE_ID(ipid), - MCA_IPID_2_UMC_INST(ipid), - MCA_IPID_2_UMC_CH(ipid), - err_addr); - - ret = amdgpu_umc_mca_to_addr(adev, - err_addr, MCA_IPID_2_UMC_CH(ipid), - MCA_IPID_2_UMC_INST(ipid), MCA_IPID_2_DIE_ID(ipid), - MCA_IPID_2_SOCKET_ID(ipid), &addr_out, true); - if (ret) - return ret; - - ecc_err = kzalloc_obj(*ecc_err); - if (!ecc_err) - return -ENOMEM; - - pa_addr = addr_out.pa.pa; - ecc_err->status = status; - ecc_err->ipid = ipid; - ecc_err->addr = addr; - ecc_err->pa_pfn = pa_addr >> AMDGPU_GPU_PAGE_SHIFT; - ecc_err->channel_idx = addr_out.pa.channel_idx; - - /* If converted pa_pfn is 0, use pa C4 pfn. */ - if (!ecc_err->pa_pfn) - ecc_err->pa_pfn = BIT_ULL(shift_bit) >> AMDGPU_GPU_PAGE_SHIFT; - - ret = amdgpu_umc_logs_ecc_err(adev, &con->umc_ecc_log.de_page_tree, ecc_err); - if (ret) { - if (ret == -EEXIST) - con->umc_ecc_log.de_queried_count++; - else - dev_err(adev->dev, "Fail to log ecc error! ret:%d\n", ret); - - kfree(ecc_err); - return ret; - } - - con->umc_ecc_log.de_queried_count++; - - memset(page_pfn, 0, sizeof(page_pfn)); - count = amdgpu_umc_lookup_bad_pages_in_a_row(adev, - pa_addr, - page_pfn, ARRAY_SIZE(page_pfn)); - if (count <= 0) { - dev_warn(adev->dev, "Fail to convert error address! count:%d\n", count); - return 0; - } - - /* Reserve memory */ - for (i = 0; i < count; i++) - amdgpu_ras_reserve_page(adev, page_pfn[i]); - - /* The problem case is as follows: - * 1. GPU A triggers a gpu ras reset, and GPU A drives - * GPU B to also perform a gpu ras reset. - * 2. After gpu B ras reset started, gpu B queried a DE - * data. Since the DE data was queried in the ras reset - * thread instead of the page retirement thread, bad - * page retirement work would not be triggered. Then - * even if all gpu resets are completed, the bad pages - * will be cached in RAM until GPU B's bad page retirement - * work is triggered again and then saved to eeprom. - * Trigger delayed work to save the bad pages to eeprom in time - * after gpu ras reset is completed. - */ - if (amdgpu_ras_in_recovery(adev)) - schedule_delayed_work(&con->page_retirement_dwork, - msecs_to_jiffies(DELAYED_TIME_FOR_GPU_RESET)); - - return 0; -} - -static int umc_v12_0_fill_error_record(struct amdgpu_device *adev, - struct ras_ecc_err *ecc_err, void *ras_error_status) -{ - struct ras_err_data *err_data = (struct ras_err_data *)ras_error_status; - uint64_t page_pfn[UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL]; - int ret, i, count; - - if (!err_data || !ecc_err) - return -EINVAL; - - memset(page_pfn, 0, sizeof(page_pfn)); - count = amdgpu_umc_lookup_bad_pages_in_a_row(adev, - ecc_err->pa_pfn << AMDGPU_GPU_PAGE_SHIFT, - page_pfn, ARRAY_SIZE(page_pfn)); - - for (i = 0; i < count; i++) { - ret = amdgpu_umc_fill_error_record(err_data, - ecc_err->addr, - page_pfn[i] << AMDGPU_GPU_PAGE_SHIFT, - ecc_err->channel_idx, - MCA_IPID_2_UMC_INST(ecc_err->ipid)); - if (ret) - break; - } - - err_data->de_count++; - - return ret; -} - -static void umc_v12_0_query_ras_ecc_err_addr(struct amdgpu_device *adev, - void *ras_error_status) -{ - struct amdgpu_ras *con = amdgpu_ras_get_context(adev); - struct ras_ecc_err *entries[MAX_ECC_NUM_PER_RETIREMENT]; - struct radix_tree_root *ecc_tree; - int new_detected, ret, i; - - ecc_tree = &con->umc_ecc_log.de_page_tree; - - mutex_lock(&con->umc_ecc_log.lock); - new_detected = radix_tree_gang_lookup_tag(ecc_tree, (void **)entries, - 0, ARRAY_SIZE(entries), UMC_ECC_NEW_DETECTED_TAG); - for (i = 0; i < new_detected; i++) { - if (!entries[i]) - continue; - - ret = umc_v12_0_fill_error_record(adev, entries[i], ras_error_status); - if (ret) { - dev_err(adev->dev, "Fail to fill umc error record, ret:%d\n", ret); - break; - } - radix_tree_tag_clear(ecc_tree, - entries[i]->pa_pfn, UMC_ECC_NEW_DETECTED_TAG); - } - mutex_unlock(&con->umc_ecc_log.lock); -} - -static uint32_t umc_v12_0_get_die_id(struct amdgpu_device *adev, - uint64_t mca_addr, uint64_t retired_page) -{ - uint32_t die = 0; - - /* we only calculate die id for nps1 mode right now */ - die += ((((retired_page >> 12) & 0x1ULL)^ - ((retired_page >> 20) & 0x1ULL) ^ - ((retired_page >> 27) & 0x1ULL) ^ - ((retired_page >> 34) & 0x1ULL) ^ - ((retired_page >> 41) & 0x1ULL)) << 0); - - /* the original PA_C4 and PA_R13 may be cleared in retired_page, so - * get them from mca_addr. - */ - die += ((((retired_page >> 13) & 0x1ULL) ^ - ((mca_addr >> 5) & 0x1ULL) ^ - ((retired_page >> 28) & 0x1ULL) ^ - ((mca_addr >> 23) & 0x1ULL) ^ - ((retired_page >> 42) & 0x1ULL)) << 1); - die &= 3; - - return die; -} - -static void umc_v12_0_mca_ipid_parse(struct amdgpu_device *adev, uint64_t ipid, - uint32_t *did, uint32_t *ch, uint32_t *umc_inst, uint32_t *sid) -{ - if (did) - *did = MCA_IPID_2_DIE_ID(ipid); - if (ch) - *ch = MCA_IPID_2_UMC_CH(ipid); - if (umc_inst) - *umc_inst = MCA_IPID_2_UMC_INST(ipid); - if (sid) - *sid = MCA_IPID_2_SOCKET_ID(ipid); -} - struct amdgpu_umc_ras umc_v12_0_ras = { .ras_block = { - .hw_ops = &umc_v12_0_ras_hw_ops, - .ras_late_init = umc_v12_0_ras_late_init, + .hw_ops = NULL, }, - .err_cnt_init = umc_v12_0_err_cnt_init, - .query_ras_poison_mode = umc_v12_0_query_ras_poison_mode, - .ecc_info_query_ras_error_address = umc_v12_0_query_ras_ecc_err_addr, .check_ecc_err_status = umc_v12_0_check_ecc_err_status, - .update_ecc_status = umc_v12_0_update_ecc_status, - .convert_ras_err_addr = umc_v12_0_convert_error_address, - .get_die_id_from_pa = umc_v12_0_get_die_id, .get_retire_flip_bits = umc_v12_0_get_retire_flip_bits, - .mca_ipid_parse = umc_v12_0_mca_ipid_parse, }; diff --git a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.h b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.h index 63b7e7254526..9d9e84d8d3bb 100644 --- a/drivers/gpu/drm/amd/amdgpu/umc_v12_0.h +++ b/drivers/gpu/drm/amd/amdgpu/umc_v12_0.h @@ -26,31 +26,6 @@ #include "soc15_common.h" #include "amdgpu.h" -#define UMC_V12_0_NODE_DIST 0x40000000 -#define UMC_V12_0_INST_DIST 0x40000 - -/* UMC register per channel offset */ -#define UMC_V12_0_PER_CHANNEL_OFFSET 0x400 - -/* UMC cross node offset */ -#define UMC_V12_0_CROSS_NODE_OFFSET 0x100000000 - -/* OdEccErrCnt max value */ -#define UMC_V12_0_CE_CNT_MAX 0xffff -/* umc ce interrupt threshold */ -#define UMC_V12_0_CE_INT_THRESHOLD 0xffff -/* umc ce count initial value */ -#define UMC_V12_0_CE_CNT_INIT (UMC_V12_0_CE_CNT_MAX - UMC_V12_0_CE_INT_THRESHOLD) - -/* number of umc channel instance with memory map register access */ -#define UMC_V12_0_CHANNEL_INSTANCE_NUM 8 -/* number of umc instance with memory map register access */ -#define UMC_V12_0_UMC_INSTANCE_NUM 4 - -/* Total channel instances for all available umc nodes */ -#define UMC_V12_0_TOTAL_CHANNEL_NUM(adev) \ - (UMC_V12_0_CHANNEL_INSTANCE_NUM * (adev)->gmc.num_umc) - /* one piece of normalized address is mapped to 8 pieces of physical address */ #define UMC_V12_0_NA_MAP_PA_NUM 8 /* R13 bit shift should be considered, double the number */ @@ -75,9 +50,6 @@ /* row bits in MCA address */ #define UMC_V12_0_MA_R0_BIT 10 -#define MCA_UMC_HWID_V12_0 0x96 -#define MCA_UMC_MCATYPE_V12_0 0x0 - #define MCA_IPID_LO_2_UMC_CH(_ipid_lo) (((((_ipid_lo) >> 20) & 0x1) * 4) + \ (((_ipid_lo) >> 12) & 0xF)) #define MCA_IPID_LO_2_UMC_INST(_ipid_lo) (((_ipid_lo) >> 21) & 0x7) diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index ecd7ead7a60b..8bb9592b0981 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -1165,36 +1165,6 @@ static int uvd_v6_0_wait_for_idle(struct amdgpu_ip_block *ip_block) } #define AMDGPU_UVD_STATUS_BUSY_MASK 0xfd -static bool uvd_v6_0_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 srbm_soft_reset = 0; - u32 tmp = RREG32(mmSRBM_STATUS); - - if (REG_GET_FIELD(tmp, SRBM_STATUS, UVD_RQ_PENDING) || - REG_GET_FIELD(tmp, SRBM_STATUS, UVD_BUSY) || - (RREG32(mmUVD_STATUS) & AMDGPU_UVD_STATUS_BUSY_MASK)) - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_UVD, 1); - - if (srbm_soft_reset) { - adev->uvd.inst->srbm_soft_reset = srbm_soft_reset; - return true; - } else { - adev->uvd.inst->srbm_soft_reset = 0; - return false; - } -} - -static int uvd_v6_0_pre_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - if (!adev->uvd.inst->srbm_soft_reset) - return 0; - - uvd_v6_0_stop(adev); - return 0; -} static int uvd_v6_0_soft_reset(struct amdgpu_ip_block *ip_block) { @@ -1227,18 +1197,6 @@ static int uvd_v6_0_soft_reset(struct amdgpu_ip_block *ip_block) return 0; } -static int uvd_v6_0_post_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - if (!adev->uvd.inst->srbm_soft_reset) - return 0; - - mdelay(5); - - return uvd_v6_0_start(adev); -} - static int uvd_v6_0_set_interrupt_state(struct amdgpu_device *adev, struct amdgpu_irq_src *source, unsigned type, @@ -1538,10 +1496,7 @@ static const struct amd_ip_funcs uvd_v6_0_ip_funcs = { .resume = uvd_v6_0_resume, .is_idle = uvd_v6_0_is_idle, .wait_for_idle = uvd_v6_0_wait_for_idle, - .check_soft_reset = uvd_v6_0_check_soft_reset, - .pre_soft_reset = uvd_v6_0_pre_soft_reset, .soft_reset = uvd_v6_0_soft_reset, - .post_soft_reset = uvd_v6_0_post_soft_reset, .set_clockgating_state = uvd_v6_0_set_clockgating_state, .set_powergating_state = uvd_v6_0_set_powergating_state, .get_clockgating_state = uvd_v6_0_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c index c69f7d82060f..9f4e88440c0a 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c @@ -631,47 +631,6 @@ static int vce_v3_0_wait_for_idle(struct amdgpu_ip_block *ip_block) #define AMDGPU_VCE_STATUS_BUSY_MASK (VCE_STATUS_VCPU_REPORT_AUTO_BUSY_MASK | \ VCE_STATUS_VCPU_REPORT_RB0_BUSY_MASK) -static bool vce_v3_0_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - u32 srbm_soft_reset = 0; - - /* According to VCE team , we should use VCE_STATUS instead - * SRBM_STATUS.VCE_BUSY bit for busy status checking. - * GRBM_GFX_INDEX.INSTANCE_INDEX is used to specify which VCE - * instance's registers are accessed - * (0 for 1st instance, 10 for 2nd instance). - * - *VCE_STATUS - *|UENC|ACPI|AUTO ACTIVE|RB1 |RB0 |RB2 | |FW_LOADED|JOB | - *|----+----+-----------+----+----+----+----------+---------+----| - *|bit8|bit7| bit6 |bit5|bit4|bit3| bit2 | bit1 |bit0| - * - * VCE team suggest use bit 3--bit 6 for busy status check - */ - mutex_lock(&adev->grbm_idx_mutex); - WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(0)); - if (RREG32(mmVCE_STATUS) & AMDGPU_VCE_STATUS_BUSY_MASK) { - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE0, 1); - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE1, 1); - } - WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(1)); - if (RREG32(mmVCE_STATUS) & AMDGPU_VCE_STATUS_BUSY_MASK) { - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE0, 1); - srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE1, 1); - } - WREG32(mmGRBM_GFX_INDEX, GET_VCE_INSTANCE(0)); - mutex_unlock(&adev->grbm_idx_mutex); - - if (srbm_soft_reset) { - adev->vce.srbm_soft_reset = srbm_soft_reset; - return true; - } else { - adev->vce.srbm_soft_reset = 0; - return false; - } -} - static int vce_v3_0_soft_reset(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -703,31 +662,6 @@ static int vce_v3_0_soft_reset(struct amdgpu_ip_block *ip_block) return 0; } -static int vce_v3_0_pre_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - if (!adev->vce.srbm_soft_reset) - return 0; - - mdelay(5); - - return vce_v3_0_suspend(ip_block); -} - - -static int vce_v3_0_post_soft_reset(struct amdgpu_ip_block *ip_block) -{ - struct amdgpu_device *adev = ip_block->adev; - - if (!adev->vce.srbm_soft_reset) - return 0; - - mdelay(5); - - return vce_v3_0_resume(ip_block); -} - static int vce_v3_0_set_interrupt_state(struct amdgpu_device *adev, struct amdgpu_irq_src *source, unsigned type, @@ -909,10 +843,7 @@ static const struct amd_ip_funcs vce_v3_0_ip_funcs = { .resume = vce_v3_0_resume, .is_idle = vce_v3_0_is_idle, .wait_for_idle = vce_v3_0_wait_for_idle, - .check_soft_reset = vce_v3_0_check_soft_reset, - .pre_soft_reset = vce_v3_0_pre_soft_reset, .soft_reset = vce_v3_0_soft_reset, - .post_soft_reset = vce_v3_0_post_soft_reset, .set_clockgating_state = vce_v3_0_set_clockgating_state, .set_powergating_state = vce_v3_0_set_powergating_state, .get_clockgating_state = vce_v3_0_get_clockgating_state, diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c index 8b8184fe6764..0d8a3cea63ee 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v2_5.c @@ -159,9 +159,8 @@ static void vcn_v2_5_ring_begin_use(struct amdgpu_ring *ring) struct amdgpu_device *adev = ring->adev; struct amdgpu_vcn_inst *v = &adev->vcn.inst[ring->me]; - atomic_inc(&adev->vcn.inst[0].total_submission_cnt); - - cancel_delayed_work_sync(&adev->vcn.inst[0].idle_work); + if (!atomic_fetch_inc(&adev->vcn.inst[0].total_submission_cnt)) + cancel_delayed_work_sync(&adev->vcn.inst[0].idle_work); /* We can safely return early here because we've cancelled the * the delayed work so there is no one else to set it to false @@ -207,10 +206,9 @@ static void vcn_v2_5_ring_end_use(struct amdgpu_ring *ring) !adev->vcn.inst[ring->me].using_unified_queue) atomic_dec(&adev->vcn.inst[ring->me].dpg_enc_submission_cnt); - atomic_dec(&adev->vcn.inst[0].total_submission_cnt); - - schedule_delayed_work(&adev->vcn.inst[0].idle_work, - VCN_IDLE_TIMEOUT); + if (atomic_dec_and_test(&adev->vcn.inst[0].total_submission_cnt)) + schedule_delayed_work(&adev->vcn.inst[0].idle_work, + VCN_IDLE_TIMEOUT); } /** diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index ff7269bafae8..0cce78b205a8 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -1927,14 +1927,17 @@ out: #define RENCODE_IB_PARAM_SESSION_INIT 0x00000003 /* return the offset in ib if id is found, -1 otherwise */ -static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start) +static int vcn_v4_0_enc_find_ib_param(struct amdgpu_ib *ib, uint32_t id, int start, uint32_t *length) { int i; uint32_t len; for (i = start; (len = amdgpu_ib_get_value(ib, i)) >= 8; i += len / 4) { - if (amdgpu_ib_get_value(ib, i + 1) == id) + if (amdgpu_ib_get_value(ib, i + 1) == id) { + if (length) + *length = len; return i; + } } return -1; } @@ -1944,14 +1947,14 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, struct amdgpu_ib *ib) { struct amdgpu_ring *ring = amdgpu_job_ring(job); - uint32_t val; + uint32_t val, len; int idx = 0, sidx; /* The first instance can decode anything */ if (!ring->me) return 0; - while ((idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, idx)) >= 0) { + while ((idx = vcn_v4_0_enc_find_ib_param(ib, RADEON_VCN_ENGINE_INFO, idx, &len)) >= 0) { val = amdgpu_ib_get_value(ib, idx + 2); /* RADEON_VCN_ENGINE_TYPE */ if (val == RADEON_VCN_ENGINE_TYPE_DECODE) { uint32_t valid_buf_flag = amdgpu_ib_get_value(ib, idx + 6); @@ -1964,12 +1967,12 @@ static int vcn_v4_0_ring_patch_cs_in_place(struct amdgpu_cs_parser *p, amdgpu_ib_get_value(ib, idx + 8); return vcn_v4_0_dec_msg(p, job, msg_buffer_addr); } else if (val == RADEON_VCN_ENGINE_TYPE_ENCODE) { - sidx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, idx); + sidx = vcn_v4_0_enc_find_ib_param(ib, RENCODE_IB_PARAM_SESSION_INIT, idx, NULL); if (sidx >= 0 && amdgpu_ib_get_value(ib, sidx + 2) == RENCODE_ENCODE_STANDARD_AV1) return vcn_v4_0_limit_sched(p, job); } - idx += amdgpu_ib_get_value(ib, idx) / 4; + idx += len / 4; } return 0; } @@ -1992,7 +1995,7 @@ static int vcn_v4_0_ring_reset(struct amdgpu_ring *ring, return amdgpu_ring_reset_helper_end(ring, timedout_fence); } -static struct amdgpu_ring_funcs vcn_v4_0_unified_ring_vm_funcs = { +static const struct amdgpu_ring_funcs vcn_v4_0_unified_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, @@ -2025,6 +2028,40 @@ static struct amdgpu_ring_funcs vcn_v4_0_unified_ring_vm_funcs = { .reset = vcn_v4_0_ring_reset, }; +static const struct amdgpu_ring_funcs vcn_v4_0_unified_ring_vm_funcs_secure = { + .type = AMDGPU_RING_TYPE_VCN_ENC, + .align_mask = 0x3f, + .nop = VCN_ENC_CMD_NO_OP, + .secure_submission_supported = true, + .no_user_fence = true, + .extra_bytes = sizeof(struct amdgpu_vcn_rb_metadata), + .get_rptr = vcn_v4_0_unified_ring_get_rptr, + .get_wptr = vcn_v4_0_unified_ring_get_wptr, + .set_wptr = vcn_v4_0_unified_ring_set_wptr, + .patch_cs_in_place = vcn_v4_0_ring_patch_cs_in_place, + .emit_frame_size = + SOC15_FLUSH_GPU_TLB_NUM_WREG * 3 + + SOC15_FLUSH_GPU_TLB_NUM_REG_WAIT * 4 + + 4 + /* vcn_v2_0_enc_ring_emit_vm_flush */ + 5 + 5 + /* vcn_v2_0_enc_ring_emit_fence x2 vm fence */ + 1, /* vcn_v2_0_enc_ring_insert_end */ + .emit_ib_size = 5, /* vcn_v2_0_enc_ring_emit_ib */ + .emit_ib = vcn_v2_0_enc_ring_emit_ib, + .emit_fence = vcn_v2_0_enc_ring_emit_fence, + .emit_vm_flush = vcn_v2_0_enc_ring_emit_vm_flush, + .test_ring = amdgpu_vcn_enc_ring_test_ring, + .test_ib = amdgpu_vcn_unified_ring_test_ib, + .insert_nop = amdgpu_ring_insert_nop, + .insert_end = vcn_v2_0_enc_ring_insert_end, + .pad_ib = amdgpu_ring_generic_pad_ib, + .begin_use = amdgpu_vcn_ring_begin_use, + .end_use = amdgpu_vcn_ring_end_use, + .emit_wreg = vcn_v2_0_enc_ring_emit_wreg, + .emit_reg_wait = vcn_v2_0_enc_ring_emit_reg_wait, + .emit_reg_write_reg_wait = amdgpu_ring_emit_reg_write_reg_wait_helper, + .reset = vcn_v4_0_ring_reset, +}; + /** * vcn_v4_0_set_unified_ring_funcs - set unified ring functions * @@ -2041,10 +2078,11 @@ static void vcn_v4_0_set_unified_ring_funcs(struct amdgpu_device *adev) continue; if (amdgpu_ip_version(adev, VCN_HWIP, 0) == IP_VERSION(4, 0, 2)) - vcn_v4_0_unified_ring_vm_funcs.secure_submission_supported = true; - - adev->vcn.inst[i].ring_enc[0].funcs = - (const struct amdgpu_ring_funcs *)&vcn_v4_0_unified_ring_vm_funcs; + adev->vcn.inst[i].ring_enc[0].funcs = + &vcn_v4_0_unified_ring_vm_funcs_secure; + else + adev->vcn.inst[i].ring_enc[0].funcs = + &vcn_v4_0_unified_ring_vm_funcs; adev->vcn.inst[i].ring_enc[0].me = i; } } diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c index 7f001c32e911..179b892fb410 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_3.c @@ -115,6 +115,19 @@ static int vcn_v4_0_3_early_init(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int i, r; + switch (amdgpu_user_queue) { + case -1: + case 0: + default: + adev->vcn.disable_kq = false; + adev->vcn.disable_uq = true; + break; + case 2: + adev->vcn.disable_kq = true; + adev->vcn.disable_uq = true; + break; + } + for (i = 0; i < adev->vcn.num_vcn_inst; ++i) /* re-use enc ring as unified ring */ adev->vcn.inst[i].num_enc_rings = 1; @@ -217,6 +230,10 @@ static int vcn_v4_0_3_sw_init(struct amdgpu_ip_block *ip_block) ring = &adev->vcn.inst[i].ring_enc[0]; ring->use_doorbell = true; + if (adev->vcn.disable_kq) { + ring->no_scheduler = true; + ring->no_user_submission = true; + } if (!amdgpu_sriov_vf(adev)) ring->doorbell_index = @@ -2146,71 +2163,6 @@ static const struct amdgpu_ras_block_hw_ops vcn_v4_0_3_ras_hw_ops = { .query_poison_status = vcn_v4_0_3_query_poison_status, }; -static int vcn_v4_0_3_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - struct aca_bank_info info; - u64 misc0; - int ret; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return ret; - - misc0 = bank->regs[ACA_REG_IDX_MISC0]; - switch (type) { - case ACA_SMU_TYPE_UE: - bank->aca_err_type = ACA_ERROR_TYPE_UE; - ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE, - 1ULL); - break; - case ACA_SMU_TYPE_CE: - bank->aca_err_type = ACA_ERROR_TYPE_CE; - ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, - ACA_REG__MISC0__ERRCNT(misc0)); - break; - default: - return -EINVAL; - } - - return ret; -} - -/* reference to smu driver if header file */ -static int vcn_v4_0_3_err_codes[] = { - 14, 15, /* VCN */ -}; - -static bool vcn_v4_0_3_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - u32 instlo; - - instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]); - instlo &= GENMASK(31, 1); - - if (instlo != mmSMNAID_AID0_MCA_SMU) - return false; - - if (aca_bank_check_error_codes(handle->adev, bank, - vcn_v4_0_3_err_codes, - ARRAY_SIZE(vcn_v4_0_3_err_codes))) - return false; - - return true; -} - -static const struct aca_bank_ops vcn_v4_0_3_aca_bank_ops = { - .aca_bank_parser = vcn_v4_0_3_aca_bank_parser, - .aca_bank_is_valid = vcn_v4_0_3_aca_bank_is_valid, -}; - -static const struct aca_info vcn_v4_0_3_aca_info = { - .hwip = ACA_HWIP_TYPE_SMU, - .mask = ACA_ERROR_UE_MASK, - .bank_ops = &vcn_v4_0_3_aca_bank_ops, -}; - static int vcn_v4_0_3_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block) { int r; @@ -2226,11 +2178,6 @@ static int vcn_v4_0_3_ras_late_init(struct amdgpu_device *adev, struct ras_commo goto late_fini; } - r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__VCN, - &vcn_v4_0_3_aca_info, NULL); - if (r) - goto late_fini; - return 0; late_fini: diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c index 1571cc5a148c..c8879a6e5297 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0_5.c @@ -1479,10 +1479,11 @@ static int vcn_v4_0_5_ring_reset(struct amdgpu_ring *ring, return amdgpu_ring_reset_helper_end(ring, timedout_fence); } -static struct amdgpu_ring_funcs vcn_v4_0_5_unified_ring_vm_funcs = { +static const struct amdgpu_ring_funcs vcn_v4_0_5_unified_ring_vm_funcs = { .type = AMDGPU_RING_TYPE_VCN_ENC, .align_mask = 0x3f, .nop = VCN_ENC_CMD_NO_OP, + .secure_submission_supported = true, .no_user_fence = true, .get_rptr = vcn_v4_0_5_unified_ring_get_rptr, .get_wptr = vcn_v4_0_5_unified_ring_get_wptr, @@ -1525,9 +1526,6 @@ static void vcn_v4_0_5_set_unified_ring_funcs(struct amdgpu_device *adev) if (adev->vcn.harvest_config & (1 << i)) continue; - if (amdgpu_ip_version(adev, VCN_HWIP, 0) == IP_VERSION(4, 0, 5)) - vcn_v4_0_5_unified_ring_vm_funcs.secure_submission_supported = true; - adev->vcn.inst[i].ring_enc[0].funcs = &vcn_v4_0_5_unified_ring_vm_funcs; adev->vcn.inst[i].ring_enc[0].me = i; } diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c index d3db0494341e..1a07c3bf4425 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_1.c @@ -94,6 +94,19 @@ static int vcn_v5_0_1_early_init(struct amdgpu_ip_block *ip_block) struct amdgpu_device *adev = ip_block->adev; int i, r; + switch (amdgpu_user_queue) { + case -1: + case 0: + default: + adev->vcn.disable_kq = false; + adev->vcn.disable_uq = true; + break; + case 2: + adev->vcn.disable_kq = true; + adev->vcn.disable_uq = true; + break; + } + for (i = 0; i < adev->vcn.num_vcn_inst; ++i) /* re-use enc ring as unified ring */ adev->vcn.inst[i].num_enc_rings = 1; @@ -188,6 +201,10 @@ static int vcn_v5_0_1_sw_init(struct amdgpu_ip_block *ip_block) ring = &adev->vcn.inst[i].ring_enc[0]; ring->use_doorbell = true; + if (adev->vcn.disable_kq) { + ring->no_scheduler = true; + ring->no_user_submission = true; + } if (!amdgpu_sriov_vf(adev)) ring->doorbell_index = (adev->doorbell_index.vcn.vcn_ring0_1 << 1) + @@ -1657,10 +1674,7 @@ static const struct amd_ip_funcs vcn_v5_0_1_ip_funcs = { .resume = vcn_v5_0_1_resume, .is_idle = vcn_v5_0_1_is_idle, .wait_for_idle = vcn_v5_0_1_wait_for_idle, - .check_soft_reset = NULL, - .pre_soft_reset = NULL, .soft_reset = NULL, - .post_soft_reset = NULL, .set_clockgating_state = vcn_v5_0_1_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, .dump_ip_state = amdgpu_vcn_dump_ip_state, @@ -1713,71 +1727,6 @@ static const struct amdgpu_ras_block_hw_ops vcn_v5_0_1_ras_hw_ops = { .query_poison_status = vcn_v5_0_1_query_poison_status, }; -static int vcn_v5_0_1_aca_bank_parser(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - struct aca_bank_info info; - u64 misc0; - int ret; - - ret = aca_bank_info_decode(bank, &info); - if (ret) - return ret; - - misc0 = bank->regs[ACA_REG_IDX_MISC0]; - switch (type) { - case ACA_SMU_TYPE_UE: - bank->aca_err_type = ACA_ERROR_TYPE_UE; - ret = aca_error_cache_log_bank_error(handle, &info, ACA_ERROR_TYPE_UE, - 1ULL); - break; - case ACA_SMU_TYPE_CE: - bank->aca_err_type = ACA_ERROR_TYPE_CE; - ret = aca_error_cache_log_bank_error(handle, &info, bank->aca_err_type, - ACA_REG__MISC0__ERRCNT(misc0)); - break; - default: - return -EINVAL; - } - - return ret; -} - -/* reference to smu driver if header file */ -static int vcn_v5_0_1_err_codes[] = { - 14, 15, 47, /* VCN [D|V|S] */ -}; - -static bool vcn_v5_0_1_aca_bank_is_valid(struct aca_handle *handle, struct aca_bank *bank, - enum aca_smu_type type, void *data) -{ - u32 instlo; - - instlo = ACA_REG__IPID__INSTANCEIDLO(bank->regs[ACA_REG_IDX_IPID]); - instlo &= GENMASK(31, 1); - - if (instlo != mmSMNAID_AID0_MCA_SMU) - return false; - - if (aca_bank_check_error_codes(handle->adev, bank, - vcn_v5_0_1_err_codes, - ARRAY_SIZE(vcn_v5_0_1_err_codes))) - return false; - - return true; -} - -static const struct aca_bank_ops vcn_v5_0_1_aca_bank_ops = { - .aca_bank_parser = vcn_v5_0_1_aca_bank_parser, - .aca_bank_is_valid = vcn_v5_0_1_aca_bank_is_valid, -}; - -static const struct aca_info vcn_v5_0_1_aca_info = { - .hwip = ACA_HWIP_TYPE_SMU, - .mask = ACA_ERROR_UE_MASK, - .bank_ops = &vcn_v5_0_1_aca_bank_ops, -}; - static int vcn_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *ras_block) { int r; @@ -1786,11 +1735,6 @@ static int vcn_v5_0_1_ras_late_init(struct amdgpu_device *adev, struct ras_commo if (r) return r; - r = amdgpu_ras_bind_aca(adev, AMDGPU_RAS_BLOCK__VCN, - &vcn_v5_0_1_aca_info, NULL); - if (r) - goto late_fini; - if (amdgpu_ras_is_supported(adev, ras_block->block) && adev->vcn.inst->ras_poison_irq.funcs) { r = amdgpu_irq_get(adev, &adev->vcn.inst->ras_poison_irq, 0); diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c index bbc172db91a1..b9f6ae75ea72 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_2.c @@ -1203,10 +1203,7 @@ static const struct amd_ip_funcs vcn_v5_0_2_ip_funcs = { .resume = vcn_v5_0_2_resume, .is_idle = vcn_v5_0_2_is_idle, .wait_for_idle = vcn_v5_0_2_wait_for_idle, - .check_soft_reset = NULL, - .pre_soft_reset = NULL, .soft_reset = NULL, - .post_soft_reset = NULL, .set_clockgating_state = vcn_v5_0_2_set_clockgating_state, .set_powergating_state = vcn_set_powergating_state, }; diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index a256320b92f3..5715b6b596af 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -1328,27 +1328,6 @@ static void vi_invalidate_hdp(struct amdgpu_device *adev, } } -static bool vi_need_full_reset(struct amdgpu_device *adev) -{ - switch (adev->asic_type) { - case CHIP_CARRIZO: - case CHIP_STONEY: - /* CZ has hang issues with full reset at the moment */ - return false; - case CHIP_FIJI: - case CHIP_TONGA: - /* XXX: soft reset should work on fiji and tonga */ - return true; - case CHIP_POLARIS10: - case CHIP_POLARIS11: - case CHIP_POLARIS12: - case CHIP_TOPAZ: - default: - /* change this when we support soft reset */ - return true; - } -} - static void vi_get_pcie_usage(struct amdgpu_device *adev, uint64_t *count0, uint64_t *count1) { @@ -1437,7 +1416,6 @@ static const struct amdgpu_asic_funcs vi_asic_funcs = .get_config_memsize = &vi_get_config_memsize, .flush_hdp = &vi_flush_hdp, .invalidate_hdp = &vi_invalidate_hdp, - .need_full_reset = &vi_need_full_reset, .init_doorbell_index = &legacy_doorbell_index_init, .get_pcie_usage = &vi_get_pcie_usage, .need_reset_on_init = &vi_need_reset_on_init, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index 531e20748198..411ee894f623 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -1783,9 +1783,6 @@ static int kfd_ptl_control(struct kfd_process_device *pdd, bool enable) uint32_t ptl_state = enable ? 1 : 0; int ret; - if (!ptl->hw_supported) - return -EOPNOTSUPP; - if (!pdd->dev->kfd2kgd || !pdd->dev->kfd2kgd->ptl_ctrl) return -EOPNOTSUPP; @@ -1804,6 +1801,9 @@ int kfd_ptl_disable_request(struct kfd_process_device *pdd, struct amdgpu_ptl *ptl = &adev->psp.ptl; int ret = 0; + if (!ptl->hw_supported) + return -EOPNOTSUPP; + mutex_lock(&ptl->mutex); if (pdd->ptl_disable_req) @@ -1833,6 +1833,9 @@ int kfd_ptl_disable_release(struct kfd_process_device *pdd, struct amdgpu_ptl *ptl = &adev->psp.ptl; int ret = 0; + if (!ptl->hw_supported) + return -EOPNOTSUPP; + mutex_lock(&ptl->mutex); if (!pdd->ptl_disable_req) @@ -1914,13 +1917,13 @@ static int criu_checkpoint_devices(struct kfd_process *p, struct kfd_criu_device_bucket *device_buckets = NULL; int ret = 0, i; - device_buckets = kvzalloc(num_devices * sizeof(*device_buckets), GFP_KERNEL); + device_buckets = kvcalloc(num_devices, sizeof(*device_buckets), GFP_KERNEL); if (!device_buckets) { ret = -ENOMEM; goto exit; } - device_priv = kvzalloc(num_devices * sizeof(*device_priv), GFP_KERNEL); + device_priv = kvcalloc(num_devices, sizeof(*device_priv), GFP_KERNEL); if (!device_priv) { ret = -ENOMEM; goto exit; @@ -2040,17 +2043,17 @@ static int criu_checkpoint_bos(struct kfd_process *p, int ret = 0, pdd_index, bo_index = 0, id; void *mem; - bo_buckets = kvzalloc(num_bos * sizeof(*bo_buckets), GFP_KERNEL); + bo_buckets = kvcalloc(num_bos, sizeof(*bo_buckets), GFP_KERNEL); if (!bo_buckets) return -ENOMEM; - bo_privs = kvzalloc(num_bos * sizeof(*bo_privs), GFP_KERNEL); + bo_privs = kvcalloc(num_bos, sizeof(*bo_privs), GFP_KERNEL); if (!bo_privs) { ret = -ENOMEM; goto exit; } - files = kvzalloc(num_bos * sizeof(struct file *), GFP_KERNEL); + files = kvcalloc(num_bos, sizeof(struct file *), GFP_KERNEL); if (!files) { ret = -ENOMEM; goto exit; @@ -2581,7 +2584,7 @@ static int criu_restore_bos(struct kfd_process *p, if (!bo_buckets) return -ENOMEM; - files = kvzalloc(args->num_bos * sizeof(struct file *), GFP_KERNEL); + files = kvcalloc(args->num_bos, sizeof(struct file *), GFP_KERNEL); if (!files) { ret = -ENOMEM; goto exit; @@ -3734,7 +3737,8 @@ static int kfd_mmap(struct file *filep, struct vm_area_struct *vma) return kfd_doorbell_mmap(dev, process, vma); case KFD_MMAP_TYPE_EVENTS: - return kfd_event_mmap(process, vma); + pr_warn("KFD_MMAP_TYPE_EVENTS is no longer supported\n"); + return -EINVAL; case KFD_MMAP_TYPE_RESERVED_MEM: pr_warn("KFD_MMAP_TYPE_RESERVED_MEM is no longer supported\n"); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index f28259d13818..2a239f45fc24 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -1715,6 +1715,8 @@ int kfd_get_gpu_cache_info(struct kfd_node *kdev, struct kfd_gpu_cache_info **pc case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): /* Cacheline size not available in IP discovery for gc11. * kfd_fill_gpu_cache_info_from_gfx_config to hard code it */ diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 5eb863dec8f4..586e640f13dc 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -169,6 +169,8 @@ static void kfd_device_info_set_event_interrupt_class(struct kfd_dev *kfd) case IP_VERSION(11, 5, 3): case IP_VERSION(11, 5, 4): case IP_VERSION(11, 5, 6): + case IP_VERSION(11, 7, 0): + case IP_VERSION(11, 7, 1): kfd->device_info.event_interrupt_class = &event_interrupt_class_v11; break; case IP_VERSION(12, 0, 0): @@ -451,6 +453,14 @@ struct kfd_dev *kgd2kfd_probe(struct amdgpu_device *adev, bool vf) gfx_target_version = 110504; f2g = &gfx_v11_kfd2kgd; break; + case IP_VERSION(11, 7, 0): + gfx_target_version = 110700; + f2g = &gfx_v11_kfd2kgd; + break; + case IP_VERSION(11, 7, 1): + gfx_target_version = 110701; + f2g = &gfx_v11_kfd2kgd; + break; case IP_VERSION(12, 0, 0): gfx_target_version = 120000; f2g = &gfx_v12_kfd2kgd; @@ -1796,6 +1806,30 @@ void kgd2kfd_teardown_processes(struct amdgpu_device *adev) cond_resched(); } +int kgd2kfd_reset_mes_queue(struct kfd_dev *kfd, uint32_t node_id, + int queue_type, int pipe, int queue, + unsigned int db) +{ + struct kfd_node *node; + int ret; + + if (!kfd->init_complete) + return 0; + + if (node_id >= kfd->num_nodes) { + dev_warn(kfd->adev->dev, "Invalid node ID: %u exceeds %u\n", + node_id, kfd->num_nodes - 1); + return -EINVAL; + } + node = kfd->nodes[node_id]; + + ret = kfd_reset_queue_mes(node->dqm, queue_type, pipe, queue, db); + if (ret) + dev_err(kfd_device, "Error resetting queue\n"); + + return ret; +} + #if defined(CONFIG_DEBUG_FS) /* This function will send a package to HIQ to hang the HWS diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index 2e010c1f8828..97402e6c8f83 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -37,7 +37,8 @@ #include "amdgpu_amdkfd.h" #include "amdgpu_reset.h" #include "amdgpu_sdma.h" -#include "mes_v11_api_def.h" +#include "amdgpu_ring.h" +#include "amdgpu_mes.h" #include "kfd_debug.h" /* Size of the per-pipe EOP queue */ @@ -71,12 +72,11 @@ static int allocate_sdma_queue(struct device_queue_manager *dqm, struct queue *q, const uint32_t *restore_sdma_id); static int reset_queues_on_hws_hang(struct device_queue_manager *dqm, bool is_sdma); -static int resume_all_queues_mes(struct device_queue_manager *dqm); -static int suspend_all_queues_mes(struct device_queue_manager *dqm); static struct queue *find_queue_by_doorbell_offset(struct device_queue_manager *dqm, u32 doorbell_offset); static void set_queue_as_reset(struct device_queue_manager *dqm, struct queue *q, struct qcm_process_device *qpd); +static int reset_queues_mes(struct device_queue_manager *dqm, struct queue *q); static inline enum KFD_MQD_TYPE get_mqd_type_from_queue_type(enum kfd_queue_type type) @@ -184,24 +184,24 @@ static void kfd_hws_hang(struct device_queue_manager *dqm) amdgpu_amdkfd_gpu_reset(dqm->dev->adev); } -static int convert_to_mes_queue_type(int queue_type) +static int convert_to_amdgpu_ring_type(int queue_type) { - int mes_queue_type; + int amdgpu_ring_type; switch (queue_type) { case KFD_QUEUE_TYPE_COMPUTE: - mes_queue_type = MES_QUEUE_TYPE_COMPUTE; + amdgpu_ring_type = AMDGPU_RING_TYPE_COMPUTE; break; case KFD_QUEUE_TYPE_SDMA: - mes_queue_type = MES_QUEUE_TYPE_SDMA; + amdgpu_ring_type = AMDGPU_RING_TYPE_SDMA; break; default: WARN(1, "Invalid queue type %d", queue_type); - mes_queue_type = -EINVAL; + amdgpu_ring_type = -EINVAL; break; } - return mes_queue_type; + return amdgpu_ring_type; } static int add_queue_mes(struct device_queue_manager *dqm, struct queue *q, @@ -251,7 +251,7 @@ static int add_queue_mes(struct device_queue_manager *dqm, struct queue *q, (qpd->pqm->process->debug_trap_enabled || kfd_dbg_has_ttmps_always_setup(q->device)); - queue_type = convert_to_mes_queue_type(q->properties.type); + queue_type = convert_to_amdgpu_ring_type(q->properties.type); if (queue_type < 0) { dev_err(adev->dev, "Queue type not supported with MES, queue:%d\n", q->properties.type); @@ -300,6 +300,7 @@ static int remove_queue_mes_on_reset_option(struct device_queue_manager *dqm, st memset(&queue_input, 0x0, sizeof(struct mes_remove_queue_input)); queue_input.doorbell_offset = q->properties.doorbell_off; queue_input.gang_context_addr = q->gang_ctx_gpu_addr; + queue_input.queue_type = convert_to_amdgpu_ring_type(q->properties.type); queue_input.remove_queue_after_reset = flush_mes_queue; queue_input.xcc_id = ffs(dqm->dev->xcc_mask) - 1; @@ -308,14 +309,14 @@ static int remove_queue_mes_on_reset_option(struct device_queue_manager *dqm, st amdgpu_mes_unlock(&adev->mes); up_read(&adev->reset_domain->sem); - if (is_for_reset) + /* If is_for_reset set, it is a mes internal cleanup */ + if (!r || is_for_reset) return r; - if (r) { - if (!suspend_all_queues_mes(dqm)) - return resume_all_queues_mes(dqm); - - dev_err(adev->dev, "failed to remove hardware queue from MES, doorbell=0x%x\n", + /* remove_hw_queue failure indicates a queue hang. reset the queue */ + r = reset_queues_mes(dqm, q); + if (r && amdgpu_gpu_recovery) { + dev_err(adev->dev, "failed to remove queue from MES, doorbell=0x%x\n", q->properties.doorbell_off); dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n"); kfd_hws_hang(dqm); @@ -407,16 +408,50 @@ static int add_all_kfd_queues_mes(struct device_queue_manager *dqm) return retval; } -static int reset_queues_mes(struct device_queue_manager *dqm) +static int reset_queue_mes(struct device_queue_manager *dqm, struct queue *q, + int queue_type, int pipe, int queue, unsigned int db) { struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev; - int hqd_info_size = adev->mes.hung_queue_hqd_info_offset; - int num_hung = 0, r = 0, i, pipe, queue, queue_type; - u32 *hung_array = dqm->hung_db_array; - struct amdgpu_mes_hung_queue_hqd_info *hqd_info = dqm->hqd_info; struct kfd_process_device *pdd; + bool use_mmio = adev->gfx.mec.use_mmio_for_reset; + int r; + + pdd = kfd_get_process_device_data(q->device, q->process); + if (!pdd) + return -ENODEV; + + if (use_mmio) + r = amdgpu_mes_reset_queue_mmio(adev, queue_type, 0, 1, pipe, queue, + ffs(dqm->dev->xcc_mask) - 1); + else + r = amdgpu_mes_reset_user_queue(adev, queue_type, db, + ffs(dqm->dev->xcc_mask) - 1); + if (r) + return r; + /* Proceed remove_queue with reset=true */ + remove_queue_mes_on_reset_option(dqm, q, &pdd->qpd, true, true); + set_queue_as_reset(dqm, q, &pdd->qpd); + return 0; +} + +int kfd_reset_queue_mes(struct device_queue_manager *dqm, int queue_type, + int pipe, int queue, unsigned int db) +{ struct queue *q; + q = find_queue_by_doorbell_offset(dqm, db); + if (!q) + return 0; + return reset_queue_mes(dqm, q, queue_type, pipe, queue, db); +} + +static int reset_queues_mes(struct device_queue_manager *dqm, struct queue *q) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev; + unsigned int num_hung = 0; + int r = 0; + struct mes_remove_queue_input queue_input; + if (!amdgpu_mes_queue_reset_by_mes_supported(adev)) { r = -ENOTRECOVERABLE; goto fail; @@ -431,111 +466,29 @@ static int reset_queues_mes(struct device_queue_manager *dqm) goto fail; } - if (!hung_array || !hqd_info) { - r = -ENOMEM; - goto fail; - } - - memset(hqd_info, 0, hqd_info_size * sizeof(struct amdgpu_mes_hung_queue_hqd_info)); - - /* - * AMDGPU_RING_TYPE_COMPUTE parameter does not matter if called - * post suspend_all as reset & detect will return all hung queue types. - * - * Passed parameter is for targeting queues not scheduled by MES add_queue. - */ - r = amdgpu_mes_detect_and_reset_hung_queues(adev, AMDGPU_RING_TYPE_COMPUTE, - false, &num_hung, hung_array, ffs(dqm->dev->xcc_mask) - 1); - - if (!num_hung || r) { - r = -ENOTRECOVERABLE; + memset(&queue_input, 0x0, sizeof(struct mes_remove_queue_input)); + queue_input.doorbell_offset = q->properties.doorbell_off; + queue_input.gang_context_addr = q->gang_ctx_gpu_addr; + queue_input.queue_type = convert_to_amdgpu_ring_type(q->properties.type); + queue_input.remove_queue_after_reset = false; + queue_input.xcc_id = ffs(dqm->dev->xcc_mask) - 1; + /* pass the known bad queue info to the reset function */ + r = amdgpu_gfx_reset_mes_compute(adev, NULL, NULL, NULL, &num_hung, &queue_input); + if (r) goto fail; - } - - /* MES resets queue/pipe and cleans up internally */ - for (i = 0; i < num_hung; i++) { - hqd_info[i].bit0_31 = hung_array[i + hqd_info_size]; - pipe = hqd_info[i].pipe_index; - queue = hqd_info[i].queue_index; - queue_type = hqd_info[i].queue_type; - - if (queue_type != MES_QUEUE_TYPE_COMPUTE && - queue_type != MES_QUEUE_TYPE_SDMA) { - pr_warn("Unsupported hung queue reset type: %d\n", queue_type); - hung_array[i] = AMDGPU_MES_INVALID_DB_OFFSET; - continue; - } - - q = find_queue_by_doorbell_offset(dqm, hung_array[i]); - if (!q) { - r = -ENOTRECOVERABLE; - goto fail; - } - - pdd = kfd_get_process_device_data(q->device, q->process); - if (!pdd) { - r = -ENODEV; - goto fail; - } - - pr_warn("Hang detected doorbell %x pipe %d queue %d type %d\n", - hung_array[i], pipe, queue, queue_type); - /* Proceed remove_queue with reset=true */ - remove_queue_mes_on_reset_option(dqm, q, &pdd->qpd, true, false); - set_queue_as_reset(dqm, q, &pdd->qpd); - } dqm->detect_hang_count = num_hung; - kfd_signal_reset_event(dqm->dev); + /* When MES doesn't detect any queue hang, no reset happens. Don't signal reset + * event. + */ + if (dqm->detect_hang_count) + kfd_signal_reset_event(dqm->dev); fail: dqm->detect_hang_count = 0; return r; } -static int suspend_all_queues_mes(struct device_queue_manager *dqm) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev; - int r = 0; - - if (!down_read_trylock(&adev->reset_domain->sem)) - return -EIO; - - r = amdgpu_mes_suspend(adev, ffs(dqm->dev->xcc_mask) - 1); - up_read(&adev->reset_domain->sem); - - if (r) { - if (!reset_queues_mes(dqm)) - return 0; - - dev_err(adev->dev, "failed to suspend gangs from MES\n"); - dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n"); - kfd_hws_hang(dqm); - } - - return r; -} - -static int resume_all_queues_mes(struct device_queue_manager *dqm) -{ - struct amdgpu_device *adev = (struct amdgpu_device *)dqm->dev->adev; - int r = 0; - - if (!down_read_trylock(&adev->reset_domain->sem)) - return -EIO; - - r = amdgpu_mes_resume(adev, ffs(dqm->dev->xcc_mask) - 1); - up_read(&adev->reset_domain->sem); - - if (r) { - dev_err(adev->dev, "failed to resume gangs from MES\n"); - dev_err(adev->dev, "MES might be in unrecoverable state, issue a GPU reset\n"); - kfd_hws_hang(dqm); - } - - return r; -} - static void increment_queue_count(struct device_queue_manager *dqm, struct qcm_process_device *qpd, struct queue *q) @@ -1064,8 +1017,15 @@ static int destroy_queue_nocpsch(struct device_queue_manager *dqm, /* Get the SDMA queue stats */ if ((q->properties.type == KFD_QUEUE_TYPE_SDMA) || (q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI)) { - retval = read_sdma_queue_counter((uint64_t __user *)q->properties.read_ptr, - &sdma_val); + if (dqm->dev->kfd2kgd->hqd_sdma_get_counter) + retval = dqm->dev->kfd2kgd->hqd_sdma_get_counter( + dqm->dev->adev, q->mqd, + dqm->dev->kfd->device_info.num_sdma_queues_per_engine, + &sdma_val); + else + retval = read_sdma_queue_counter( + (uint64_t __user *)q->properties.read_ptr, + &sdma_val); if (retval) dev_err(dev, "Failed to read SDMA queue counter for queue: %d\n", q->properties.queue_id); @@ -2703,8 +2663,16 @@ static int destroy_queue_cpsch(struct device_queue_manager *dqm, /* Get the SDMA queue stats */ if ((q->properties.type == KFD_QUEUE_TYPE_SDMA) || (q->properties.type == KFD_QUEUE_TYPE_SDMA_XGMI)) { - retval = read_sdma_queue_counter((uint64_t __user *)q->properties.read_ptr, - &sdma_val); + if (dqm->dev->kfd2kgd->hqd_sdma_get_counter) + retval = dqm->dev->kfd2kgd->hqd_sdma_get_counter( + dqm->dev->adev, q->mqd, + dqm->dev->kfd->device_info.num_sdma_queues_per_engine, + &sdma_val); + else + retval = read_sdma_queue_counter( + (uint64_t __user *)q->properties.read_ptr, + &sdma_val); + if (retval) dev_err(dev, "Failed to read SDMA queue counter for queue: %d\n", q->properties.queue_id); @@ -3244,12 +3212,12 @@ void device_queue_manager_uninit(struct device_queue_manager *dqm) kfree(dqm); } +/* bad queue notified by interrupt from CP */ int kfd_dqm_suspend_bad_queue_mes(struct kfd_node *knode, u32 pasid, u32 doorbell_id) { struct kfd_process_device *pdd = NULL; struct kfd_process *p = kfd_lookup_process_by_pasid(pasid, &pdd); struct device_queue_manager *dqm = knode->dqm; - struct device *dev = dqm->dev->adev->dev; struct qcm_process_device *qpd; struct queue *q = NULL; int ret = 0; @@ -3264,19 +3232,10 @@ int kfd_dqm_suspend_bad_queue_mes(struct kfd_node *knode, u32 pasid, u32 doorbel list_for_each_entry(q, &qpd->queues_list, list) { if (q->doorbell_id == doorbell_id && q->properties.is_active) { - /* suspend all queues will save any good queues and mark the rest as bad */ - suspend_all_queues_mes(dqm); - + reset_queues_mes(dqm, q); q->properties.is_evicted = true; q->properties.is_active = false; decrement_queue_count(dqm, qpd, q); - - /* this will remove the bad queue and sched a GPU reset if needed */ - ret = remove_queue_mes(dqm, q, qpd); - if (ret) - dev_err(dev, "Removing bad queue failed"); - /* resume the good queues */ - resume_all_queues_mes(dqm); break; } } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h index e0b6a47e7722..2229f8b2f446 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.h @@ -333,6 +333,8 @@ int debug_refresh_runlist(struct device_queue_manager *dqm); bool kfd_dqm_is_queue_in_process(struct device_queue_manager *dqm, struct qcm_process_device *qpd, int doorbell_off, u32 *queue_format); +int kfd_reset_queue_mes(struct device_queue_manager *dqm, int queue_type, + int pipe, int queue, unsigned int db); static inline unsigned int get_sh_mem_bases_32(struct kfd_process_device *pdd) { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 81900b49d9d5..dae01e2bb464 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -29,10 +29,12 @@ #include <linux/uaccess.h> #include <linux/mman.h> #include <linux/memory.h> +#include <linux/workqueue.h> #include "kfd_priv.h" #include "kfd_events.h" #include "kfd_device_queue_manager.h" #include <linux/device.h> +#include <drm/amdgpu_drm.h> /* * Wrapper around wait_queue_entry_t @@ -44,67 +46,19 @@ struct kfd_event_waiter { bool event_age_enabled; /* set to true when last_event_age is non-zero */ }; -/* - * Each signal event needs a 64-bit signal slot where the signaler will write - * a 1 before sending an interrupt. (This is needed because some interrupts - * do not contain enough spare data bits to identify an event.) - * We get whole pages and map them to the process VA. - * Individual signal events use their event_id as slot index. - */ -struct kfd_signal_page { - uint64_t *kernel_address; - uint64_t __user *user_address; - bool need_to_free_pages; -}; - -static uint64_t *page_slots(struct kfd_signal_page *page) -{ - return page->kernel_address; -} - -static struct kfd_signal_page *allocate_signal_page(struct kfd_process *p) -{ - void *backing_store; - struct kfd_signal_page *page; - - page = kzalloc_obj(*page); - if (!page) - return NULL; - - backing_store = (void *) __get_free_pages(GFP_KERNEL, - get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); - if (!backing_store) - goto fail_alloc_signal_store; - - /* Initialize all events to unsignaled */ - memset(backing_store, (uint8_t) UNSIGNALED_EVENT_SLOT, - KFD_SIGNAL_EVENT_LIMIT * 8); - - page->kernel_address = backing_store; - page->need_to_free_pages = true; - pr_debug("Allocated new event signal page at %p, for process %p\n", - page, p); - - return page; - -fail_alloc_signal_store: - kfree(page); - return NULL; -} - static int allocate_event_notification_slot(struct kfd_process *p, struct kfd_event *ev, const int *restore_id) { int id; - if (!p->signal_page) { - p->signal_page = allocate_signal_page(p); - if (!p->signal_page) - return -ENOMEM; - /* Oldest user mode expects 256 event slots */ - p->signal_mapped_size = 256*8; - } + /* + * The signal page is allocated in user mode and mapped to the kernel + * via the event_page_offset of the create event IOCTL. Without it no + * signal events can be created. + */ + if (!p->signal_page) + return -ENOMEM; if (restore_id) { id = idr_alloc(&p->event_idr, ev, *restore_id, *restore_id + 1, @@ -123,7 +77,7 @@ static int allocate_event_notification_slot(struct kfd_process *p, return id; ev->event_id = id; - page_slots(p->signal_page)[id] = UNSIGNALED_EVENT_SLOT; + p->signal_page[id] = UNSIGNALED_EVENT_SLOT; return 0; } @@ -169,7 +123,7 @@ static struct kfd_event *lookup_signaled_event_by_partial_id( */ if (bits > 31 || (1U << bits) >= KFD_SIGNAL_EVENT_LIMIT) { if (signal_mailbox_updated && - page_slots(p->signal_page)[id] == UNSIGNALED_EVENT_SLOT) + p->signal_page[id] == UNSIGNALED_EVENT_SLOT) return NULL; return idr_find(&p->event_idr, id); @@ -179,7 +133,7 @@ static struct kfd_event *lookup_signaled_event_by_partial_id( * and find the first one that has signaled. */ for (ev = NULL; id < KFD_SIGNAL_EVENT_LIMIT && !ev; id += 1U << bits) { - if (page_slots(p->signal_page)[id] == UNSIGNALED_EVENT_SLOT) + if (p->signal_page[id] == UNSIGNALED_EVENT_SLOT) continue; ev = idr_find(&p->event_idr, id); @@ -210,10 +164,8 @@ static int create_signal_event(struct file *devkfd, struct kfd_process *p, p->signal_event_count++; - ev->user_signal_address = &p->signal_page->user_address[ev->event_id]; - pr_debug("Signal event number %zu created with id %d, address %p\n", - p->signal_event_count, ev->event_id, - ev->user_signal_address); + pr_debug("Signal event number %zu created with id %d\n", + p->signal_event_count, ev->event_id); return 0; } @@ -293,26 +245,9 @@ static void destroy_events(struct kfd_process *p) mutex_destroy(&p->event_mutex); } -/* - * We assume that the process is being destroyed and there is no need to - * unmap the pages or keep bookkeeping data in order. - */ -static void shutdown_signal_page(struct kfd_process *p) -{ - struct kfd_signal_page *page = p->signal_page; - - if (page) { - if (page->need_to_free_pages) - free_pages((unsigned long)page->kernel_address, - get_order(KFD_SIGNAL_EVENT_LIMIT * 8)); - kfree(page); - } -} - void kfd_event_free_process(struct kfd_process *p) { destroy_events(p); - shutdown_signal_page(p); } static bool event_can_be_gpu_signaled(const struct kfd_event *ev) @@ -329,8 +264,6 @@ static bool event_can_be_cpu_signaled(const struct kfd_event *ev) static int kfd_event_page_set(struct kfd_process *p, void *kernel_address, uint64_t size, uint64_t user_handle) { - struct kfd_signal_page *page; - if (p->signal_page) return -EBUSY; @@ -340,17 +273,11 @@ static int kfd_event_page_set(struct kfd_process *p, void *kernel_address, return -EINVAL; } - page = kzalloc_obj(*page); - if (!page) - return -ENOMEM; - /* Initialize all events to unsignaled */ memset(kernel_address, (uint8_t) UNSIGNALED_EVENT_SLOT, KFD_SIGNAL_EVENT_LIMIT * 8); - page->kernel_address = kernel_address; - - p->signal_page = page; + p->signal_page = kernel_address; p->signal_mapped_size = size; p->signal_handle = user_handle; return 0; @@ -387,7 +314,8 @@ int kfd_kmap_event_page(struct kfd_process *p, uint64_t event_page_offset) return -EINVAL; } - err = amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel(mem, &kern_addr, &size); + err = amdgpu_amdkfd_gpuvm_map_bo_to_kernel(mem, &kern_addr, &size, + AMDGPU_GEM_DOMAIN_GTT); if (err) { pr_err("Failed to map event page to kernel\n"); return err; @@ -396,7 +324,7 @@ int kfd_kmap_event_page(struct kfd_process *p, uint64_t event_page_offset) err = kfd_event_page_set(p, kern_addr, size, event_page_offset); if (err) { pr_err("Failed to set event page\n"); - amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(mem); + amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(mem); return err; } return err; @@ -717,7 +645,7 @@ unlock_rcu: static void acknowledge_signal(struct kfd_process *p, struct kfd_event *ev) { - WRITE_ONCE(page_slots(p->signal_page)[ev->event_id], UNSIGNALED_EVENT_SLOT); + WRITE_ONCE(p->signal_page[ev->event_id], UNSIGNALED_EVENT_SLOT); } static void set_event_from_interrupt(struct kfd_process *p, @@ -760,7 +688,7 @@ void kfd_signal_event_interrupt(u32 pasid, uint32_t partial_id, * in the interrupt payload was invalid and do an * exhaustive search of signaled events. */ - uint64_t *slots = page_slots(p->signal_page); + uint64_t *slots = p->signal_page; uint32_t id; if (valid_id_bits) @@ -1068,51 +996,6 @@ out: return ret; } -int kfd_event_mmap(struct kfd_process *p, struct vm_area_struct *vma) -{ - unsigned long pfn; - struct kfd_signal_page *page; - int ret; - - /* check required size doesn't exceed the allocated size */ - if (get_order(KFD_SIGNAL_EVENT_LIMIT * 8) < - get_order(vma->vm_end - vma->vm_start)) { - pr_err("Event page mmap requested illegal size\n"); - return -EINVAL; - } - - page = p->signal_page; - if (!page) { - /* Probably KFD bug, but mmap is user-accessible. */ - pr_debug("Signal page could not be found\n"); - return -EINVAL; - } - - pfn = __pa(page->kernel_address); - pfn >>= PAGE_SHIFT; - - vm_flags_set(vma, VM_IO | VM_DONTCOPY | VM_DONTEXPAND | VM_NORESERVE - | VM_DONTDUMP | VM_PFNMAP); - - pr_debug("Mapping signal page\n"); - pr_debug(" start user address == 0x%08lx\n", vma->vm_start); - pr_debug(" end user address == 0x%08lx\n", vma->vm_end); - pr_debug(" pfn == 0x%016lX\n", pfn); - pr_debug(" vm_flags == 0x%08lX\n", vma->vm_flags); - pr_debug(" size == 0x%08lX\n", - vma->vm_end - vma->vm_start); - - page->user_address = (uint64_t __user *)vma->vm_start; - - /* mapping the page to user process */ - ret = remap_pfn_range(vma, vma->vm_start, pfn, - vma->vm_end - vma->vm_start, vma->vm_page_prot); - if (!ret) - p->signal_mapped_size = vma->vm_end - vma->vm_start; - - return ret; -} - /* * Assumes that p is not going away. */ @@ -1338,6 +1221,71 @@ void kfd_signal_reset_event(struct kfd_node *dev) srcu_read_unlock(&kfd_processes_srcu, idx); } +/* + * Per-process opt-in for poison-consumption SIGBUS handling. + * + * Default: kernel sends SIGBUS to the process immediately when poison is + * consumed, in addition to delivering the KFD HW/MEMORY exception events. + * + * Userspace (ROCr) can opt-in per-process via the + * DRM_IOCTL_AMDGPU_PROC_OPTIONS / AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY + * option. This lets the app's registered system-event callback handle the + * RAS error first, instead of being killed by SIGBUS. + * + * Encoded value (stored on the kfd_process): + * 0 - default: SIGBUS immediately (no opt-in) + * 0xFFFFFFFF - opt-in, never escalate to SIGBUS + * N (other) - opt-in, escalate to SIGBUS after N ms if app does not + * handle the error in time (safety timeout) + */ + +void kfd_signal_sigbus_delayed_fn(struct work_struct *work) +{ + struct kfd_process *p = container_of(to_delayed_work(work), + struct kfd_process, signal_work); + + if (p->lead_thread) + send_sig(SIGBUS, p->lead_thread, 0); + + kfd_unref_process(p); +} + +static void kfd_signal_sigbus_with_delay(struct kfd_node *dev, + struct kfd_process *p) +{ + u32 delay_ms = atomic_read(&p->kfd_sigbus_delay_ms); + + if (delay_ms == AMDGPU_PROC_OPTIONS_KFD_SIGBUS_DELAY_DISABLED) { + dev_info(dev->adev->dev, + "SIGBUS suppressed for process %s(pid:%d): app opted in to handle RAS error\n", + p->lead_thread->comm, p->lead_thread->pid); + return; + } + + if (delay_ms == 0) + goto send_now; + + /* + * Take an extra reference for the delayed worker. If the work is + * already pending (e.g. another device of this process consumed poison + * just before), drop the reference and skip rescheduling - the process + * only needs to be notified once. + */ + kref_get(&p->ref); + if (!schedule_delayed_work(&p->signal_work, msecs_to_jiffies(delay_ms))) { + kfd_unref_process(p); + return; + } + + dev_info(dev->adev->dev, + "Deferring SIGBUS to process %s(pid:%d) by %u ms (RAS error opt-in safety timeout)\n", + p->lead_thread->comm, p->lead_thread->pid, delay_ms); + return; + +send_now: + send_sig(SIGBUS, p->lead_thread, 0); +} + void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid) { struct kfd_process *p = kfd_lookup_process_by_pasid(pasid, NULL); @@ -1392,7 +1340,7 @@ void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid) rcu_read_unlock(); /* user application will handle SIGBUS signal */ - send_sig(SIGBUS, p->lead_thread, 0); + kfd_signal_sigbus_with_delay(dev, p); kfd_unref_process(p); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.h b/drivers/gpu/drm/amd/amdkfd/kfd_events.h index 1dc21c13833b..827a2c7d7721 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.h @@ -49,7 +49,6 @@ #define UNSIGNALED_EVENT_SLOT ((uint64_t)-1) struct kfd_event_waiter; -struct signal_page; struct kfd_event { u32 event_id; @@ -63,9 +62,6 @@ struct kfd_event { spinlock_t lock; wait_queue_head_t wq; /* List of event waiters. */ - /* Only for signal events. */ - uint64_t __user *user_signal_address; - /* type specific data */ union { struct kfd_hsa_memory_exception_data memory_exception_data; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c index 226e76ae0be7..7cd236c1ff75 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_migrate.c @@ -128,7 +128,7 @@ svm_migrate_copy_memory_gart(struct amdgpu_device *adev, dma_addr_t *sys, enum MIGRATION_COPY_DIR direction, struct dma_fence **mfence) { - const u64 GTT_MAX_PAGES = AMDGPU_GTT_MAX_TRANSFER_SIZE; + const u64 GTT_MAX_PAGES = (AMDGPU_GTT_MAX_TRANSFER_SIZE >> PAGE_SHIFT); struct amdgpu_ring *ring; struct amdgpu_ttm_buffer_entity *entity; u64 gart_s, gart_d; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c index 723b725d20b8..9b7859a77950 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c @@ -315,3 +315,19 @@ bool kfd_check_hiq_mqd_doorbell_id(struct kfd_node *node, uint32_t doorbell_id, return false; } + +bool mqd_on_vram(struct amdgpu_device *adev) +{ + if (adev->apu_prefer_gtt) + return false; + + switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { + case IP_VERSION(9, 4, 2): + case IP_VERSION(9, 4, 3): + case IP_VERSION(9, 4, 4): + case IP_VERSION(9, 5, 0): + return true; + default: + return false; + } +} diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h index 06ca6235ff1b..59eff3389d39 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h @@ -127,6 +127,7 @@ struct mqd_manager { struct mutex mqd_mutex; struct kfd_node *dev; uint32_t mqd_size; + uint32_t ctl_stack_size; }; struct mqd_user_context_save_area_header { @@ -201,4 +202,6 @@ uint64_t kfd_mqd_stride(struct mqd_manager *mm, struct queue_properties *q); bool kfd_check_hiq_mqd_doorbell_id(struct kfd_node *node, uint32_t doorbell_id, uint32_t inst); +bool mqd_on_vram(struct amdgpu_device *adev); + #endif /* KFD_MQD_MANAGER_H_ */ diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c index 8e8ec266ca46..e034da638c07 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c @@ -203,8 +203,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control = min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1); + m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, + ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index fff137e00b5e..350fcbbba4b2 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -241,8 +241,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control = min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1); + m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, + ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c index 8c815f129614..7c387fa90076 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12.c @@ -216,8 +216,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control = min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1); + m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, + ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c index 475589b924e9..431a940f91f3 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v12_1.c @@ -294,8 +294,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control = min(0xA, - ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1); + m->cp_hqd_eop_control = q->eop_ring_buffer_size ? min(0xA, + ffs(q->eop_ring_buffer_size / sizeof(unsigned int)) - 1 - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index 17bfb419b202..75e5a9f67d50 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -27,6 +27,7 @@ #include <linux/uaccess.h> #include "kfd_priv.h" #include "kfd_mqd_manager.h" +#include "kfd_topology.h" #include "v9_structs.h" #include "gc/gc_9_0_offset.h" #include "gc/gc_9_0_sh_mask.h" @@ -115,20 +116,6 @@ static void set_priority(struct v9_mqd *m, struct queue_properties *q) m->cp_hqd_pipe_priority = pipe_priority_map[q->priority]; } -static bool mqd_on_vram(struct amdgpu_device *adev) -{ - if (adev->apu_prefer_gtt) - return false; - - switch (amdgpu_ip_version(adev, GC_HWIP, 0)) { - case IP_VERSION(9, 4, 3): - case IP_VERSION(9, 5, 0): - return true; - default: - return false; - } -} - static struct kfd_mem_obj *allocate_mqd(struct mqd_manager *mm, struct queue_properties *q) { @@ -411,8 +398,11 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd, static int get_checkpoint_info(struct mqd_manager *mm, void *mqd, u32 *ctl_stack_size) { struct v9_mqd *m = get_mqd(mqd); + u32 per_xcc_size; + + per_xcc_size = min_t(u32, m->cp_hqd_cntl_stack_size, mm->ctl_stack_size); - if (check_mul_overflow(m->cp_hqd_cntl_stack_size, NUM_XCC(mm->dev->xcc_mask), ctl_stack_size)) + if (check_mul_overflow(per_xcc_size, NUM_XCC(mm->dev->xcc_mask), ctl_stack_size)) return -EINVAL; return 0; @@ -421,13 +411,15 @@ static int get_checkpoint_info(struct mqd_manager *mm, void *mqd, u32 *ctl_stack static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, void *ctl_stack_dst) { struct v9_mqd *m; + u32 ctl_stack_copy_size; /* Control stack is located one page after MQD. */ void *ctl_stack = (void *)((uintptr_t)mqd + AMDGPU_GPU_PAGE_SIZE); m = get_mqd(mqd); + ctl_stack_copy_size = min_t(u32, m->cp_hqd_cntl_stack_size, mm->ctl_stack_size); memcpy(mqd_dst, m, sizeof(struct v9_mqd)); - memcpy(ctl_stack_dst, ctl_stack, m->cp_hqd_cntl_stack_size); + memcpy(ctl_stack_dst, ctl_stack, ctl_stack_copy_size); } static void checkpoint_mqd_v9_4_3(struct mqd_manager *mm, @@ -436,15 +428,19 @@ static void checkpoint_mqd_v9_4_3(struct mqd_manager *mm, void *ctl_stack_dst) { struct v9_mqd *m; + u32 ctl_stack_stride; int xcc; uint64_t size = get_mqd(mqd)->cp_mqd_stride_size; + ctl_stack_stride = min_t(u32, get_mqd(mqd)->cp_hqd_cntl_stack_size, + mm->ctl_stack_size); + for (xcc = 0; xcc < NUM_XCC(mm->dev->xcc_mask); xcc++) { m = get_mqd(mqd + size * xcc); checkpoint_mqd(mm, m, (uint8_t *)mqd_dst + sizeof(*m) * xcc, - (uint8_t *)ctl_stack_dst + m->cp_hqd_cntl_stack_size * xcc); + (uint8_t *)ctl_stack_dst + ctl_stack_stride * xcc); } } @@ -998,6 +994,15 @@ struct mqd_manager *mqd_manager_init_v9(enum KFD_MQD_TYPE type, mqd->is_occupied = kfd_is_occupied_cp; mqd->get_checkpoint_info = get_checkpoint_info; mqd->mqd_size = sizeof(struct v9_mqd); + if (dev->kfd->cwsr_enabled) { + struct kfd_topology_device *topo_dev; + + topo_dev = kfd_topology_device_by_id(dev->id); + if (topo_dev) + mqd->ctl_stack_size = + ALIGN(topo_dev->node_props.ctl_stack_size, + AMDGPU_GPU_PAGE_SIZE); + } mqd->mqd_stride = mqd_stride_v9; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c index c86779af323b..60b87a500698 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c @@ -214,8 +214,8 @@ static void __update_mqd(struct mqd_manager *mm, void *mqd, * more than (EOP entry count - 1) so a queue size of 0x800 dwords * is safe, giving a maximum field value of 0xA. */ - m->cp_hqd_eop_control |= min(0xA, - order_base_2(q->eop_ring_buffer_size / 4) - 1); + m->cp_hqd_eop_control |= q->eop_ring_buffer_size ? min(0xA, + order_base_2(q->eop_ring_buffer_size / 4) - 1) : 0; m->cp_hqd_eop_base_addr_lo = lower_32_bits(q->eop_ring_buffer_address >> 8); m->cp_hqd_eop_base_addr_hi = diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index acd0e41e744c..90f010cbe54e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -952,11 +952,32 @@ struct kfd_process { struct idr event_idr; /* Event page */ u64 signal_handle; - struct kfd_signal_page *signal_page; + /* + * Each signal event needs a 64-bit signal slot where the signaler will + * write a 1 before sending an interrupt. (This is needed because some + * interrupts do not contain enough spare data bits to identify an + * event.) The signal page is allocated in user mode and mapped to the + * kernel; individual signal events use their event_id as slot index. + */ + uint64_t *signal_page; size_t signal_mapped_size; size_t signal_event_count; bool signal_event_limit_reached; + /** + * @kfd_sigbus_delay_ms: Per-process KFD SIGBUS delivery option for + * poison/RAS events (set via DRM_IOCTL_AMDGPU_PROC_OPTIONS / + * AMDGPU_PROC_OPTIONS_OP_KFD_SIGBUS_DELAY). + * + * 0 - send SIGBUS immediately (default) + * 0xFFFFFFFF - suppress SIGBUS delivery + * other - delay SIGBUS delivery by this many milliseconds + */ + atomic_t kfd_sigbus_delay_ms; + + /* Delayed signal delivery to user */ + struct delayed_work signal_work; + /* Information used for memory eviction */ void *kgd_process_info; /* Eviction fence that is attached to all the BOs of this process. The @@ -1525,7 +1546,6 @@ extern const struct kfd_device_global_init_class device_global_init_class_cik; int kfd_event_init_process(struct kfd_process *p); void kfd_event_free_process(struct kfd_process *p); -int kfd_event_mmap(struct kfd_process *process, struct vm_area_struct *vma); int kfd_wait_on_events(struct kfd_process *p, uint32_t num_events, void __user *data, bool all, uint32_t *user_timeout_ms, @@ -1554,6 +1574,7 @@ void kfd_signal_vm_fault_event(struct kfd_process_device *pdd, void kfd_signal_reset_event(struct kfd_node *dev); void kfd_signal_poison_consumed_event(struct kfd_node *dev, u32 pasid); +void kfd_signal_sigbus_delayed_fn(struct work_struct *work); void kfd_signal_process_terminate_event(struct kfd_process *p); static inline void kfd_flush_tlb(struct kfd_process_device *pdd) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index ca71fa726e32..767c2cc8e29e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -153,6 +153,21 @@ static void kfd_sdma_activity_worker(struct work_struct *work) (q->properties.type != KFD_QUEUE_TYPE_SDMA_XGMI)) continue; + if (dqm->dev->kfd2kgd->hqd_sdma_get_counter) { + val = 0; + ret = dqm->dev->kfd2kgd->hqd_sdma_get_counter( + dqm->dev->adev, q->mqd, + dqm->dev->kfd->device_info.num_sdma_queues_per_engine, + &val); + + if (ret) + pr_debug("Failed to read SDMA queue active counter %i\n", ret); + else + workarea->sdma_activity_counter += val; + + continue; + } + sdma_q = kzalloc_obj(struct temp_sdma_queue_list); if (!sdma_q) { dqm_unlock(dqm); @@ -171,7 +186,7 @@ static void kfd_sdma_activity_worker(struct work_struct *work) * count */ if (list_empty(&sdma_q_list.list)) { - workarea->sdma_activity_counter = pdd->sdma_past_activity_counter; + workarea->sdma_activity_counter += pdd->sdma_past_activity_counter; dqm_unlock(dqm); return; } @@ -721,7 +736,7 @@ static void kfd_process_free_gpuvm(struct kgd_mem *mem, struct kfd_node *dev = pdd->dev; if (kptr && *kptr) { - amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(mem); + amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(mem); *kptr = NULL; } @@ -761,10 +776,16 @@ static int kfd_process_alloc_gpuvm(struct kfd_process_device *pdd, } if (kptr) { - err = amdgpu_amdkfd_gpuvm_map_gtt_bo_to_kernel( - (struct kgd_mem *)*mem, kptr, NULL); + u32 domain; + + if (flags & KFD_IOC_ALLOC_MEM_FLAGS_VRAM) + domain = AMDGPU_GEM_DOMAIN_VRAM; + else + domain = AMDGPU_GEM_DOMAIN_GTT; + err = amdgpu_amdkfd_gpuvm_map_bo_to_kernel((struct kgd_mem *)*mem, + kptr, NULL, domain); if (err) { - pr_debug("Map GTT BO to kernel failed\n"); + pr_debug("Map BO to kernel failed err %d\n", err); goto sync_memory_failed; } } @@ -986,6 +1007,33 @@ out: return process; } +/** + * amdgpu_amdkfd_set_sigbus_delay - Set per-process KFD SIGBUS delay + * @task: task in the target process + * @ms: encoded delay value (0 = immediate, 0xFFFFFFFF = suppress, + * otherwise delay in milliseconds) + * + * Stores the SIGBUS delivery option on the kfd_process associated with + * @task. If the calling process has not opened /dev/kfd yet (no + * kfd_process exists), this is a no-op - the option only applies to + * processes that actually use KFD. + */ +int amdgpu_amdkfd_set_sigbus_delay(struct task_struct *task, u32 ms) +{ + struct kfd_process *p; + + if (!task->mm) + return -EINVAL; + + p = kfd_lookup_process_by_mm(task->mm); + if (!p) + return 0; + + atomic_set(&p->kfd_sigbus_delay_ms, ms); + kfd_unref_process(p); + return 0; +} + static struct kfd_process *find_process_by_mm(const struct mm_struct *mm) { struct kfd_process *process; @@ -1092,7 +1140,7 @@ static void kfd_process_kunmap_signal_bo(struct kfd_process *p) if (!mem) goto out; - amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(mem); + amdgpu_amdkfd_gpuvm_unmap_bo_from_kernel(mem); out: mutex_unlock(&p->mutex); @@ -1330,6 +1378,11 @@ void kfd_process_notifier_release_internal(struct kfd_process *p) kfd_process_table_remove(p); cancel_delayed_work_sync(&p->eviction_work); cancel_delayed_work_sync(&p->restore_work); + /* + * If work pending, cancel it and drop the extra ref + */ + if (cancel_delayed_work_sync(&p->signal_work)) + kfd_unref_process(p); /* * Dequeue and destroy user queues, it is not safe for GPU to access @@ -1436,8 +1489,7 @@ static int kfd_process_device_init_cwsr_dgpu(struct kfd_process_device *pdd) { struct kfd_node *dev = pdd->dev; struct qcm_process_device *qpd = &pdd->qpd; - uint32_t flags = KFD_IOC_ALLOC_MEM_FLAGS_GTT - | KFD_IOC_ALLOC_MEM_FLAGS_NO_SUBSTITUTE + u32 flags = KFD_IOC_ALLOC_MEM_FLAGS_NO_SUBSTITUTE | KFD_IOC_ALLOC_MEM_FLAGS_EXECUTABLE; struct kgd_mem *mem; void *kaddr; @@ -1446,7 +1498,12 @@ static int kfd_process_device_init_cwsr_dgpu(struct kfd_process_device *pdd) if (!dev->kfd->cwsr_enabled || qpd->cwsr_kaddr || !qpd->cwsr_base) return 0; - /* cwsr_base is only set for dGPU */ + if (KFD_GC_VERSION(dev) >= IP_VERSION(9, 4, 2) && !dev->adev->apu_prefer_gtt) + flags |= KFD_IOC_ALLOC_MEM_FLAGS_VRAM; + else + flags |= KFD_IOC_ALLOC_MEM_FLAGS_GTT; + + /* Allocate CWSR TBA/TMA buffers */ ret = kfd_process_alloc_gpuvm(pdd, qpd->cwsr_base, KFD_CWSR_TBA_TMA_SIZE, flags, &mem, &kaddr); if (ret) @@ -1586,6 +1643,7 @@ struct kfd_process *create_process(const struct task_struct *thread, bool primar INIT_DELAYED_WORK(&process->eviction_work, evict_process_worker); INIT_DELAYED_WORK(&process->restore_work, restore_process_worker); + INIT_DELAYED_WORK(&process->signal_work, kfd_signal_sigbus_delayed_fn); process->last_restore_timestamp = get_jiffies_64(); err = kfd_event_init_process(process); if (err) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 0ac35789b239..acbdca91cde5 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -1040,7 +1040,13 @@ int kfd_criu_restore_queue(struct kfd_process *p, ctl_stack = mqd + q_data->mqd_size; memset(&qp, 0, sizeof(qp)); - set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->adev->gfx.xcc_mask)); + set_queue_properties_from_criu(&qp, q_data, NUM_XCC(pdd->dev->xcc_mask)); + + ret = kfd_queue_acquire_buffers(pdd, &qp); + if (ret) { + pr_debug("failed to acquire user queue buffers for CRIU\n"); + goto exit; + } ret = kfd_queue_acquire_buffers(pdd, &qp); if (ret) { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c index e659cd50eb0b..6a7b4d959541 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_smi_events.c @@ -224,10 +224,10 @@ static void kfd_smi_event_add(struct task_struct *task, struct kfd_node *dev, pid = kfd_smi_task_to_pid(task); - len = snprintf(fifo_in, sizeof(fifo_in), "%x ", event); + len = scnprintf(fifo_in, sizeof(fifo_in), "%x ", event); va_start(args, fmt); - len += vsnprintf(fifo_in + len, sizeof(fifo_in) - len, fmt, args); + len += vscnprintf(fifo_in + len, sizeof(fifo_in) - len, fmt, args); va_end(args); add_event_to_kfifo(pid, dev, event, fifo_in, len); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c index 0900bb23349e..30ad10bbd47e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_svm.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_svm.c @@ -3473,7 +3473,13 @@ svm_range_is_valid(struct kfd_process *p, uint64_t start, uint64_t size) unsigned long start_unchg = start; start <<= PAGE_SHIFT; - end = start + (size << PAGE_SHIFT); + + if (size == 0) + return -EINVAL; + + if (check_add_overflow(start, size << PAGE_SHIFT, &end)) + return -EOVERFLOW; + do { vma = vma_lookup(p->mm, start); if (!vma || (vma->vm_flags & device_vma)) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index 00517c3d0e6a..35b3abe57b80 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -2013,12 +2013,21 @@ static void kfd_topology_set_capabilities(struct kfd_topology_device *dev) dev->node_props.capability |= HSA_CAP_TRAP_DEBUG_PRECISE_MEMORY_OPERATIONS_SUPPORTED; - if (!amdgpu_sriov_vf(dev->gpu->adev)) + if (KFD_GC_VERSION(dev->gpu) >= IP_VERSION(9, 4, 3) && + !amdgpu_sriov_vf(dev->gpu->adev)) dev->node_props.capability |= HSA_CAP_PER_QUEUE_RESET_SUPPORTED; } else { dev->node_props.debug_prop |= HSA_DBG_WATCH_ADDR_MASK_LO_BIT_GFX10 | HSA_DBG_WATCH_ADDR_MASK_HI_BIT; + /* gfx11 dGPU and gfx12.0 */ + if ((KFD_GC_VERSION(dev->gpu) == IP_VERSION(11, 0, 0) || + KFD_GC_VERSION(dev->gpu) == IP_VERSION(11, 0, 2) || + KFD_GC_VERSION(dev->gpu) == IP_VERSION(11, 0, 3) || + KFD_GC_VERSION(dev->gpu) == IP_VERSION(12, 0, 0) || + KFD_GC_VERSION(dev->gpu) == IP_VERSION(12, 0, 1)) && + !amdgpu_sriov_vf(dev->gpu->adev)) + dev->node_props.capability |= HSA_CAP_PER_QUEUE_RESET_SUPPORTED; if (KFD_GC_VERSION(dev->gpu) >= IP_VERSION(12, 0, 0)) dev->node_props.capability |= diff --git a/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c b/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c index 995cae6be144..84e13537a7ba 100644 --- a/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c +++ b/drivers/gpu/drm/amd/amdxcp/amdgpu_xcp_drv.c @@ -45,7 +45,7 @@ static const struct drm_driver amdgpu_xcp_driver = { .minor = 0, }; -static int8_t pdev_num; +static u8 pdev_num; static struct xcp_device *xcp_dev[MAX_XCP_PLATFORM_DEVICE]; static DEFINE_MUTEX(xcp_mutex); @@ -56,6 +56,11 @@ int amdgpu_xcp_drm_dev_alloc(struct drm_device **ddev) char *dev_name; int ret, i; + if (!ddev) + return -EINVAL; + + BUILD_BUG_ON(MAX_XCP_PLATFORM_DEVICE >= U8_MAX); + guard(mutex)(&xcp_mutex); if (pdev_num >= MAX_XCP_PLATFORM_DEVICE) @@ -105,7 +110,7 @@ out_unregister: } EXPORT_SYMBOL(amdgpu_xcp_drm_dev_alloc); -static void free_xcp_dev(int8_t index) +static void free_xcp_dev(uint8_t index) { if ((index < MAX_XCP_PLATFORM_DEVICE) && (xcp_dev[index])) { struct platform_device *pdev = xcp_dev[index]->pdev; @@ -114,17 +119,18 @@ static void free_xcp_dev(int8_t index) platform_device_unregister(pdev); xcp_dev[index] = NULL; - pdev_num--; + if (pdev_num > 0) + pdev_num--; } } void amdgpu_xcp_drm_dev_free(struct drm_device *ddev) { - int8_t i; + uint8_t i; guard(mutex)(&xcp_mutex); - for (i = 0; i < MAX_XCP_PLATFORM_DEVICE; i++) { + for (i = 0; pdev_num && i < MAX_XCP_PLATFORM_DEVICE; i++) { if ((xcp_dev[i]) && (&xcp_dev[i]->drm == ddev)) { free_xcp_dev(i); break; @@ -135,7 +141,7 @@ EXPORT_SYMBOL(amdgpu_xcp_drm_dev_free); void amdgpu_xcp_drv_release(void) { - int8_t i; + uint8_t i; guard(mutex)(&xcp_mutex); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile index 54a93e4255b3..d83878e35b61 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/Makefile @@ -41,7 +41,11 @@ AMDGPUDM = \ amdgpu_dm_quirks.o \ amdgpu_dm_wb.o \ amdgpu_dm_colorop.o \ - amdgpu_dm_ism.o + amdgpu_dm_ism.o \ + amdgpu_dm_backlight.o \ + amdgpu_dm_audio.o \ + amdgpu_dm_dmub.o \ + amdgpu_dm_connector.o ifdef CONFIG_DRM_AMD_DC_FP AMDGPUDM += dc_fpu.o 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 d2dcef74625c..67b825cbb88f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -66,6 +66,11 @@ #endif #include "amdgpu_dm_psr.h" #include "amdgpu_dm_replay.h" +#include "amdgpu_dm_backlight.h" +#include "amdgpu_dm_audio.h" +#include "amdgpu_dm_dmub.h" +#include "amdgpu_dm_connector.h" +#include "amdgpu_dm_kunit_helpers.h" #include "ivsrcid/ivsrcid_vislands30.h" @@ -94,7 +99,6 @@ #include <drm/drm_mode.h> #include <drm/drm_utils.h> #include <drm/drm_vblank.h> -#include <drm/drm_audio_component.h> #include <drm/drm_colorop.h> #include <drm/drm_gem_atomic_helper.h> @@ -107,60 +111,9 @@ #include "modules/inc/mod_power.h" #include "modules/power/power_helpers.h" -static_assert(AMDGPU_DMUB_NOTIFICATION_MAX == DMUB_NOTIFICATION_MAX, "AMDGPU_DMUB_NOTIFICATION_MAX mismatch"); - -#define FIRMWARE_RENOIR_DMUB "amdgpu/renoir_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB); -#define FIRMWARE_SIENNA_CICHLID_DMUB "amdgpu/sienna_cichlid_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID_DMUB); -#define FIRMWARE_NAVY_FLOUNDER_DMUB "amdgpu/navy_flounder_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER_DMUB); -#define FIRMWARE_GREEN_SARDINE_DMUB "amdgpu/green_sardine_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE_DMUB); -#define FIRMWARE_VANGOGH_DMUB "amdgpu/vangogh_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_VANGOGH_DMUB); -#define FIRMWARE_DIMGREY_CAVEFISH_DMUB "amdgpu/dimgrey_cavefish_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH_DMUB); -#define FIRMWARE_BEIGE_GOBY_DMUB "amdgpu/beige_goby_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY_DMUB); -#define FIRMWARE_YELLOW_CARP_DMUB "amdgpu/yellow_carp_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP_DMUB); -#define FIRMWARE_DCN_314_DMUB "amdgpu/dcn_3_1_4_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_314_DMUB); -#define FIRMWARE_DCN_315_DMUB "amdgpu/dcn_3_1_5_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_315_DMUB); -#define FIRMWARE_DCN316_DMUB "amdgpu/dcn_3_1_6_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN316_DMUB); - -#define FIRMWARE_DCN_V3_2_0_DMCUB "amdgpu/dcn_3_2_0_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_0_DMCUB); -#define FIRMWARE_DCN_V3_2_1_DMCUB "amdgpu/dcn_3_2_1_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_1_DMCUB); - -#define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin" MODULE_FIRMWARE(FIRMWARE_RAVEN_DMCU); - -#define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin" MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU); -#define FIRMWARE_DCN_35_DMUB "amdgpu/dcn_3_5_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_35_DMUB); - -#define FIRMWARE_DCN_351_DMUB "amdgpu/dcn_3_5_1_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_351_DMUB); - -#define FIRMWARE_DCN_36_DMUB "amdgpu/dcn_3_6_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB); - -#define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB); - -#define FIRMWARE_DCN_42_DMUB "amdgpu/dcn_4_2_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB); - -#define FIRMWARE_DCN_42B_DMUB "amdgpu/dcn_4_2_1_dmcub.bin" -MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB); - /** * DOC: overview * @@ -174,46 +127,7 @@ MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB); /* basic init/fini API */ static int amdgpu_dm_init(struct amdgpu_device *adev); static void amdgpu_dm_fini(struct amdgpu_device *adev); -static bool is_freesync_video_mode(const struct drm_display_mode *mode, struct amdgpu_dm_connector *aconnector); static void reset_freesync_config_for_crtc(struct dm_crtc_state *new_crtc_state); -static struct amdgpu_i2c_adapter * -create_i2c(struct ddc_service *ddc_service, bool oem); - -static enum drm_mode_subconnector get_subconnector_type(struct dc_link *link) -{ - switch (link->dpcd_caps.dongle_type) { - case DISPLAY_DONGLE_NONE: - return DRM_MODE_SUBCONNECTOR_Native; - case DISPLAY_DONGLE_DP_VGA_CONVERTER: - return DRM_MODE_SUBCONNECTOR_VGA; - case DISPLAY_DONGLE_DP_DVI_CONVERTER: - case DISPLAY_DONGLE_DP_DVI_DONGLE: - return DRM_MODE_SUBCONNECTOR_DVID; - case DISPLAY_DONGLE_DP_HDMI_CONVERTER: - case DISPLAY_DONGLE_DP_HDMI_DONGLE: - return DRM_MODE_SUBCONNECTOR_HDMIA; - case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE: - default: - return DRM_MODE_SUBCONNECTOR_Unknown; - } -} - -static void update_subconnector_property(struct amdgpu_dm_connector *aconnector) -{ - struct dc_link *link = aconnector->dc_link; - struct drm_connector *connector = &aconnector->base; - enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown; - - if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) - return; - - if (aconnector->dc_sink) - subconnector = get_subconnector_type(link); - - drm_object_property_set_value(&connector->base, - connector->dev->mode_config.dp_subconnector_property, - subconnector); -} /* * initializes drm_device display related structures, based on the information @@ -226,32 +140,23 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev); /* removes and deallocates the drm structures, created by the above function */ static void amdgpu_dm_destroy_drm_device(struct amdgpu_display_manager *dm); -static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm, - struct amdgpu_dm_connector *amdgpu_dm_connector, - u32 link_index, - struct amdgpu_encoder *amdgpu_encoder); -static int amdgpu_dm_encoder_init(struct drm_device *dev, - struct amdgpu_encoder *aencoder, - uint32_t link_index); - -static int amdgpu_dm_connector_get_modes(struct drm_connector *connector); - static int amdgpu_dm_atomic_setup_commit(struct drm_atomic_commit *state); static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state); +static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context); static int amdgpu_dm_atomic_check(struct drm_device *dev, struct drm_atomic_commit *state); -static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector); -static void handle_hpd_rx_irq(void *param); - -static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, - int bl_idx, - u32 user_brightness); - -static bool +STATIC_IFN_KUNIT bool is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, struct drm_crtc_state *new_crtc_state); + +static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc) +{ + if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter) + dc_exit_ips_for_hw_access(dc); +} + /* * dm_vblank_get_counter * @@ -332,40 +237,14 @@ static int dm_wait_for_idle(struct amdgpu_ip_block *ip_block) return 0; } -static bool dm_check_soft_reset(struct amdgpu_ip_block *ip_block) -{ - return false; -} - static int dm_soft_reset(struct amdgpu_ip_block *ip_block) { /* XXX todo */ return 0; } -static struct amdgpu_crtc * -get_crtc_by_otg_inst(struct amdgpu_device *adev, - int otg_inst) -{ - struct drm_device *dev = adev_to_drm(adev); - struct drm_crtc *crtc; - struct amdgpu_crtc *amdgpu_crtc; - - if (WARN_ON(otg_inst == -1)) - return adev->mode_info.crtcs[0]; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - amdgpu_crtc = to_amdgpu_crtc(crtc); - - if (amdgpu_crtc->otg_inst == otg_inst) - return amdgpu_crtc; - } - - return NULL; -} - -static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, - struct dm_crtc_state *new_state) +STATIC_IFN_KUNIT bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, + struct dm_crtc_state *new_state) { if (new_state->stream->adjust.timing_adjust_pending) return true; @@ -376,13 +255,14 @@ static inline bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, else return false; } +EXPORT_IF_KUNIT(is_dc_timing_adjust_needed); /* * DC will program planes with their z-order determined by their ordering * in the dc_surface_updates array. This comparator is used to sort them * by descending zpos. */ -static int dm_plane_layer_index_cmp(const void *a, const void *b) +STATIC_IFN_KUNIT int dm_plane_layer_index_cmp(const void *a, const void *b) { const struct dc_surface_update *sa = (struct dc_surface_update *)a; const struct dc_surface_update *sb = (struct dc_surface_update *)b; @@ -390,6 +270,7 @@ static int dm_plane_layer_index_cmp(const void *a, const void *b) /* Sort by descending dc_plane layer_index (i.e. normalized_zpos) */ return sb->surface->layer_index - sa->surface->layer_index; } +EXPORT_IF_KUNIT(dm_plane_layer_index_cmp); /** * update_planes_and_stream_adapter() - Send planes to be updated in DC @@ -430,633 +311,6 @@ static inline bool update_planes_and_stream_adapter(struct dc *dc, stream_update); } -/** - * dm_pflip_high_irq() - Handle pageflip interrupt - * @interrupt_params: ignored - * - * Handles the pageflip interrupt by notifying all interested parties - * that the pageflip has been completed. - */ -static void dm_pflip_high_irq(void *interrupt_params) -{ - struct amdgpu_crtc *amdgpu_crtc; - struct common_irq_params *irq_params = interrupt_params; - struct amdgpu_device *adev = irq_params->adev; - struct drm_device *dev = adev_to_drm(adev); - unsigned long flags; - struct drm_pending_vblank_event *e; - u32 vpos, hpos, v_blank_start, v_blank_end; - bool vrr_active; - - amdgpu_crtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_PFLIP); - - /* IRQ could occur when in initial stage */ - /* TODO work and BO cleanup */ - if (amdgpu_crtc == NULL) { - drm_dbg_state(dev, "CRTC is null, returning.\n"); - return; - } - - spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); - - if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED) { - drm_dbg_state(dev, - "amdgpu_crtc->pflip_status = %d != AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p]\n", - amdgpu_crtc->pflip_status, AMDGPU_FLIP_SUBMITTED, - amdgpu_crtc->crtc_id, amdgpu_crtc); - spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); - return; - } - - /* page flip completed. */ - e = amdgpu_crtc->event; - amdgpu_crtc->event = NULL; - - WARN_ON(!e); - - vrr_active = amdgpu_dm_crtc_vrr_active_irq(amdgpu_crtc); - - /* Fixed refresh rate, or VRR scanout position outside front-porch? */ - if (!vrr_active || - !dc_stream_get_scanoutpos(amdgpu_crtc->dm_irq_params.stream, &v_blank_start, - &v_blank_end, &hpos, &vpos) || - (vpos < v_blank_start)) { - /* Update to correct count and vblank timestamp if racing with - * vblank irq. This also updates to the correct vblank timestamp - * even in VRR mode, as scanout is past the front-porch atm. - */ - drm_crtc_accurate_vblank_count(&amdgpu_crtc->base); - - /* Wake up userspace by sending the pageflip event with proper - * count and timestamp of vblank of flip completion. - */ - if (e) { - drm_crtc_send_vblank_event(&amdgpu_crtc->base, e); - - /* Event sent, so done with vblank for this flip */ - drm_crtc_vblank_put(&amdgpu_crtc->base); - } - } else if (e) { - /* VRR active and inside front-porch: vblank count and - * timestamp for pageflip event will only be up to date after - * drm_crtc_handle_vblank() has been executed from late vblank - * irq handler after start of back-porch (vline 0). We queue the - * pageflip event for send-out by drm_crtc_handle_vblank() with - * updated timestamp and count, once it runs after us. - * - * We need to open-code this instead of using the helper - * drm_crtc_arm_vblank_event(), as that helper would - * call drm_crtc_accurate_vblank_count(), which we must - * not call in VRR mode while we are in front-porch! - */ - - /* sequence will be replaced by real count during send-out. */ - e->sequence = drm_crtc_vblank_count(&amdgpu_crtc->base); - e->pipe = amdgpu_crtc->crtc_id; - - list_add_tail(&e->base.link, &adev_to_drm(adev)->vblank_event_list); - e = NULL; - } - - /* Keep track of vblank of this flip for flip throttling. We use the - * cooked hw counter, as that one incremented at start of this vblank - * of pageflip completion, so last_flip_vblank is the forbidden count - * for queueing new pageflips if vsync + VRR is enabled. - */ - amdgpu_crtc->dm_irq_params.last_flip_vblank = - amdgpu_get_vblank_counter_kms(&amdgpu_crtc->base); - - amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE; - spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); - - drm_dbg_state(dev, - "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n", - amdgpu_crtc->crtc_id, amdgpu_crtc, vrr_active, (int)!e); -} - -static void dm_handle_vmin_vmax_update(struct work_struct *offload_work) -{ - struct vupdate_offload_work *work = container_of(offload_work, struct vupdate_offload_work, work); - struct amdgpu_device *adev = work->adev; - struct dc_stream_state *stream = work->stream; - struct dc_crtc_timing_adjust *adjust = work->adjust; - - mutex_lock(&adev->dm.dc_lock); - dc_stream_adjust_vmin_vmax(adev->dm.dc, stream, adjust); - mutex_unlock(&adev->dm.dc_lock); - - dc_stream_release(stream); - kfree(work->adjust); - kfree(work); -} - -static void schedule_dc_vmin_vmax(struct amdgpu_device *adev, - struct dc_stream_state *stream, - struct dc_crtc_timing_adjust *adjust) -{ - struct vupdate_offload_work *offload_work = kzalloc_obj(*offload_work, - GFP_NOWAIT); - if (!offload_work) { - drm_dbg_driver(adev_to_drm(adev), "Failed to allocate vupdate_offload_work\n"); - return; - } - - struct dc_crtc_timing_adjust *adjust_copy = kzalloc_obj(*adjust_copy, - GFP_NOWAIT); - if (!adjust_copy) { - drm_dbg_driver(adev_to_drm(adev), "Failed to allocate adjust_copy\n"); - kfree(offload_work); - return; - } - - dc_stream_retain(stream); - memcpy(adjust_copy, adjust, sizeof(*adjust_copy)); - - INIT_WORK(&offload_work->work, dm_handle_vmin_vmax_update); - offload_work->adev = adev; - offload_work->stream = stream; - offload_work->adjust = adjust_copy; - - queue_work(system_percpu_wq, &offload_work->work); -} - -static void dm_vupdate_high_irq(void *interrupt_params) -{ - struct common_irq_params *irq_params = interrupt_params; - struct amdgpu_device *adev = irq_params->adev; - struct amdgpu_crtc *acrtc; - struct drm_device *drm_dev; - struct drm_vblank_crtc *vblank; - ktime_t frame_duration_ns, previous_timestamp; - unsigned long flags; - int vrr_active; - - acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE); - - if (acrtc) { - vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); - drm_dev = acrtc->base.dev; - vblank = drm_crtc_vblank_crtc(&acrtc->base); - previous_timestamp = atomic64_read(&irq_params->previous_timestamp); - frame_duration_ns = vblank->time - previous_timestamp; - - if (frame_duration_ns > 0) { - trace_amdgpu_refresh_rate_track(acrtc->base.index, - frame_duration_ns, - ktime_divns(NSEC_PER_SEC, frame_duration_ns)); - atomic64_set(&irq_params->previous_timestamp, vblank->time); - } - - drm_dbg_vbl(drm_dev, - "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, - vrr_active); - - /* Core vblank handling is done here after end of front-porch in - * vrr mode, as vblank timestamping will give valid results - * while now done after front-porch. This will also deliver - * page-flip completion events that have been queued to us - * if a pageflip happened inside front-porch. - */ - if (vrr_active && acrtc->dm_irq_params.stream) { - bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; - bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; - bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state - == VRR_STATE_ACTIVE_VARIABLE; - - amdgpu_dm_crtc_handle_vblank(acrtc); - - /* BTR processing for pre-DCE12 ASICs */ - if (adev->family < AMDGPU_FAMILY_AI) { - spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); - mod_freesync_handle_v_update( - adev->dm.freesync_module, - acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params); - - if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { - schedule_dc_vmin_vmax(adev, - acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params.adjust); - } - spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); - } - } - } -} - -/** - * dm_crtc_high_irq() - Handles CRTC interrupt - * @interrupt_params: used for determining the CRTC instance - * - * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK - * event handler. - */ -static void dm_crtc_high_irq(void *interrupt_params) -{ - struct common_irq_params *irq_params = interrupt_params; - struct amdgpu_device *adev = irq_params->adev; - struct drm_writeback_job *job; - struct amdgpu_crtc *acrtc; - unsigned long flags; - int vrr_active; - - acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK); - if (!acrtc) - return; - - if (acrtc->wb_conn) { - spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags); - - if (acrtc->wb_pending) { - job = list_first_entry_or_null(&acrtc->wb_conn->job_queue, - struct drm_writeback_job, - list_entry); - acrtc->wb_pending = false; - spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags); - - if (job) { - unsigned int v_total, refresh_hz; - struct dc_stream_state *stream = acrtc->dm_irq_params.stream; - - v_total = stream->adjust.v_total_max ? - stream->adjust.v_total_max : stream->timing.v_total; - refresh_hz = div_u64((uint64_t) stream->timing.pix_clk_100hz * - 100LL, (v_total * stream->timing.h_total)); - mdelay(1000 / refresh_hz); - - drm_writeback_signal_completion(acrtc->wb_conn, 0); - dc_stream_fc_disable_writeback(adev->dm.dc, - acrtc->dm_irq_params.stream, 0); - } - } else - spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags); - } - - vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); - - drm_dbg_vbl(adev_to_drm(adev), - "crtc:%d, vupdate-vrr:%d, planes:%d\n", acrtc->crtc_id, - vrr_active, acrtc->dm_irq_params.active_planes); - - /** - * Core vblank handling at start of front-porch is only possible - * in non-vrr mode, as only there vblank timestamping will give - * valid results while done in front-porch. Otherwise defer it - * to dm_vupdate_high_irq after end of front-porch. - */ - if (!vrr_active) - amdgpu_dm_crtc_handle_vblank(acrtc); - - /** - * Following stuff must happen at start of vblank, for crc - * computation and below-the-range btr support in vrr mode. - */ - amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); - - /* BTR updates need to happen before VUPDATE on Vega and above. */ - if (adev->family < AMDGPU_FAMILY_AI) - return; - - spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); - - if (acrtc->dm_irq_params.stream && - acrtc->dm_irq_params.vrr_params.supported) { - bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; - bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; - bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state == VRR_STATE_ACTIVE_VARIABLE; - - mod_freesync_handle_v_update(adev->dm.freesync_module, - acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params); - - /* update vmin_vmax only if freesync is enabled, or only if PSR and REPLAY are disabled */ - if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { - schedule_dc_vmin_vmax(adev, acrtc->dm_irq_params.stream, - &acrtc->dm_irq_params.vrr_params.adjust); - } - } - - /* - * If there aren't any active_planes then DCH HUBP may be clock-gated. - * In that case, pageflip completion interrupts won't fire and pageflip - * completion events won't get delivered. Prevent this by sending - * pending pageflip events from here if a flip is still pending. - * - * If any planes are enabled, use dm_pflip_high_irq() instead, to - * avoid race conditions between flip programming and completion, - * which could cause too early flip completion events. - */ - if (adev->family >= AMDGPU_FAMILY_RV && - acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED && - acrtc->dm_irq_params.active_planes == 0) { - if (acrtc->event) { - drm_crtc_send_vblank_event(&acrtc->base, acrtc->event); - acrtc->event = NULL; - drm_crtc_vblank_put(&acrtc->base); - } - acrtc->pflip_status = AMDGPU_FLIP_NONE; - } - - spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); -} - -#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) -/** - * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for - * DCN generation ASICs - * @interrupt_params: interrupt parameters - * - * Used to set crc window/read out crc value at vertical line 0 position - */ -static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params) -{ - struct common_irq_params *irq_params = interrupt_params; - struct amdgpu_device *adev = irq_params->adev; - struct amdgpu_crtc *acrtc; - - acrtc = get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VLINE0); - - if (!acrtc) - return; - - amdgpu_dm_crtc_handle_crc_window_irq(&acrtc->base); -} -#endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */ - -/** - * dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command. - * @adev: amdgpu_device pointer - * @notify: dmub notification structure - * - * Dmub AUX or SET_CONFIG command completion processing callback - * Copies dmub notification to DM which is to be read by AUX command. - * issuing thread and also signals the event to wake up the thread. - */ -static void dmub_aux_setconfig_callback(struct amdgpu_device *adev, - struct dmub_notification *notify) -{ - if (adev->dm.dmub_notify) - memcpy(adev->dm.dmub_notify, notify, sizeof(struct dmub_notification)); - if (notify->type == DMUB_NOTIFICATION_AUX_REPLY) - complete(&adev->dm.dmub_aux_transfer_done); -} - -static void dmub_aux_fused_io_callback(struct amdgpu_device *adev, - struct dmub_notification *notify) -{ - if (!adev || !notify) { - ASSERT(false); - return; - } - - const struct dmub_cmd_fused_request *req = ¬ify->fused_request; - const uint8_t ddc_line = req->u.aux.ddc_line; - - if (ddc_line >= ARRAY_SIZE(adev->dm.fused_io)) { - ASSERT(false); - return; - } - - struct fused_io_sync *sync = &adev->dm.fused_io[ddc_line]; - - static_assert(sizeof(*req) <= sizeof(sync->reply_data), "Size mismatch"); - memcpy(sync->reply_data, req, sizeof(*req)); - complete(&sync->replied); -} - -/** - * dmub_hpd_callback - DMUB HPD interrupt processing callback. - * @adev: amdgpu_device pointer - * @notify: dmub notification structure - * - * Dmub Hpd interrupt processing callback. Gets displayindex through the - * ink index and calls helper to do the processing. - */ -static void dmub_hpd_callback(struct amdgpu_device *adev, - struct dmub_notification *notify) -{ - struct amdgpu_dm_connector *aconnector; - struct amdgpu_dm_connector *hpd_aconnector = NULL; - struct drm_connector *connector; - struct drm_connector_list_iter iter; - struct dc_link *link; - u8 link_index = 0; - struct drm_device *dev; - - if (adev == NULL) - return; - - if (notify == NULL) { - drm_err(adev_to_drm(adev), "DMUB HPD callback notification was NULL"); - return; - } - - if (notify->link_index > adev->dm.dc->link_count) { - drm_err(adev_to_drm(adev), "DMUB HPD index (%u)is abnormal", notify->link_index); - return; - } - - /* Skip DMUB HPD IRQ in suspend/resume. We will probe them later. */ - if (notify->type == DMUB_NOTIFICATION_HPD && adev->in_suspend) { - drm_info(adev_to_drm(adev), "Skip DMUB HPD IRQ callback in suspend/resume\n"); - return; - } - - link_index = notify->link_index; - link = adev->dm.dc->links[link_index]; - dev = adev->dm.ddev; - - drm_connector_list_iter_begin(dev, &iter); - drm_for_each_connector_iter(connector, &iter) { - - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - aconnector = to_amdgpu_dm_connector(connector); - if (link && aconnector->dc_link == link) { - if (notify->type == DMUB_NOTIFICATION_HPD) - drm_info(adev_to_drm(adev), "DMUB HPD IRQ callback: link_index=%u\n", link_index); - else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ) - drm_info(adev_to_drm(adev), "DMUB HPD RX IRQ callback: link_index=%u\n", link_index); - else - drm_warn(adev_to_drm(adev), "DMUB Unknown HPD callback type %d, link_index=%u\n", - notify->type, link_index); - - hpd_aconnector = aconnector; - break; - } - } - drm_connector_list_iter_end(&iter); - - if (hpd_aconnector) { - if (notify->type == DMUB_NOTIFICATION_HPD) { - if (hpd_aconnector->dc_link->hpd_status == (notify->hpd_status == DP_HPD_PLUG)) - drm_warn(adev_to_drm(adev), "DMUB reported hpd status unchanged. link_index=%u\n", link_index); - handle_hpd_irq_helper(hpd_aconnector); - } else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ) { - handle_hpd_rx_irq(hpd_aconnector); - } - } -} - -/** - * dmub_hpd_sense_callback - DMUB HPD sense processing callback. - * @adev: amdgpu_device pointer - * @notify: dmub notification structure - * - * HPD sense changes can occur during low power states and need to be - * notified from firmware to driver. - */ -static void dmub_hpd_sense_callback(struct amdgpu_device *adev, - struct dmub_notification *notify) -{ - drm_dbg_driver(adev_to_drm(adev), "DMUB HPD SENSE callback.\n"); -} - -/** - * register_dmub_notify_callback - Sets callback for DMUB notify - * @adev: amdgpu_device pointer - * @type: Type of dmub notification - * @callback: Dmub interrupt callback function - * @dmub_int_thread_offload: offload indicator - * - * API to register a dmub callback handler for a dmub notification - * Also sets indicator whether callback processing to be offloaded. - * to dmub interrupt handling thread - * Return: true if successfully registered, false if there is existing registration - */ -static bool register_dmub_notify_callback(struct amdgpu_device *adev, - enum dmub_notification_type type, - dmub_notify_interrupt_callback_t callback, - bool dmub_int_thread_offload) -{ - if (callback != NULL && type < ARRAY_SIZE(adev->dm.dmub_thread_offload)) { - adev->dm.dmub_callback[type] = callback; - adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload; - } else - return false; - - return true; -} - -static void dm_handle_hpd_work(struct work_struct *work) -{ - struct dmub_hpd_work *dmub_hpd_wrk; - - dmub_hpd_wrk = container_of(work, struct dmub_hpd_work, handle_hpd_work); - - if (!dmub_hpd_wrk->dmub_notify) { - drm_err(adev_to_drm(dmub_hpd_wrk->adev), "dmub_hpd_wrk dmub_notify is NULL"); - return; - } - - if (dmub_hpd_wrk->dmub_notify->type < ARRAY_SIZE(dmub_hpd_wrk->adev->dm.dmub_callback)) { - dmub_hpd_wrk->adev->dm.dmub_callback[dmub_hpd_wrk->dmub_notify->type](dmub_hpd_wrk->adev, - dmub_hpd_wrk->dmub_notify); - } - - kfree(dmub_hpd_wrk->dmub_notify); - kfree(dmub_hpd_wrk); - -} - -static const char *dmub_notification_type_str(enum dmub_notification_type e) -{ - switch (e) { - case DMUB_NOTIFICATION_NO_DATA: - return "NO_DATA"; - case DMUB_NOTIFICATION_AUX_REPLY: - return "AUX_REPLY"; - case DMUB_NOTIFICATION_HPD: - return "HPD"; - case DMUB_NOTIFICATION_HPD_IRQ: - return "HPD_IRQ"; - case DMUB_NOTIFICATION_SET_CONFIG_REPLY: - return "SET_CONFIG_REPLY"; - case DMUB_NOTIFICATION_DPIA_NOTIFICATION: - return "DPIA_NOTIFICATION"; - case DMUB_NOTIFICATION_HPD_SENSE_NOTIFY: - return "HPD_SENSE_NOTIFY"; - case DMUB_NOTIFICATION_FUSED_IO: - return "FUSED_IO"; - default: - return "<unknown>"; - } -} - -#define DMUB_TRACE_MAX_READ 64 -/** - * dm_dmub_outbox1_low_irq() - Handles Outbox interrupt - * @interrupt_params: used for determining the Outbox instance - * - * Handles the Outbox Interrupt - * event handler. - */ -static void dm_dmub_outbox1_low_irq(void *interrupt_params) -{ - struct dmub_notification notify = {0}; - struct common_irq_params *irq_params = interrupt_params; - struct amdgpu_device *adev = irq_params->adev; - struct amdgpu_display_manager *dm = &adev->dm; - struct dmcub_trace_buf_entry entry = { 0 }; - u32 count = 0; - struct dmub_hpd_work *dmub_hpd_wrk; - - do { - if (dc_dmub_srv_get_dmub_outbox0_msg(dm->dc, &entry)) { - trace_amdgpu_dmub_trace_high_irq(entry.trace_code, entry.tick_count, - entry.param0, entry.param1); - - drm_dbg_driver(adev_to_drm(adev), "trace_code:%u, tick_count:%u, param0:%u, param1:%u\n", - entry.trace_code, entry.tick_count, entry.param0, entry.param1); - } else - break; - - count++; - - } while (count <= DMUB_TRACE_MAX_READ); - - if (count > DMUB_TRACE_MAX_READ) - drm_dbg_driver(adev_to_drm(adev), "Warning : count > DMUB_TRACE_MAX_READ"); - - if (dc_enable_dmub_notifications(adev->dm.dc) && - irq_params->irq_src == DC_IRQ_SOURCE_DMCUB_OUTBOX) { - - do { - dc_stat_get_dmub_notification(adev->dm.dc, ¬ify); - if (notify.type >= ARRAY_SIZE(dm->dmub_thread_offload)) { - drm_err(adev_to_drm(adev), "DM: notify type %d invalid!", notify.type); - continue; - } - if (!dm->dmub_callback[notify.type]) { - drm_warn(adev_to_drm(adev), "DMUB notification skipped due to no handler: type=%s\n", - dmub_notification_type_str(notify.type)); - continue; - } - if (dm->dmub_thread_offload[notify.type] == true) { - dmub_hpd_wrk = kzalloc_obj(*dmub_hpd_wrk, - GFP_ATOMIC); - if (!dmub_hpd_wrk) { - drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk"); - return; - } - dmub_hpd_wrk->dmub_notify = kmemdup(¬ify, sizeof(struct dmub_notification), - GFP_ATOMIC); - if (!dmub_hpd_wrk->dmub_notify) { - kfree(dmub_hpd_wrk); - drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk->dmub_notify"); - return; - } - INIT_WORK(&dmub_hpd_wrk->handle_hpd_work, dm_handle_hpd_work); - dmub_hpd_wrk->adev = adev; - queue_work(adev->dm.delayed_hpd_wq, &dmub_hpd_wrk->handle_hpd_work); - } else { - dm->dmub_callback[notify.type](adev, ¬ify); - } - } while (notify.pending_notification); - } -} - static int dm_set_clockgating_state(struct amdgpu_ip_block *ip_block, enum amd_clockgating_state state) { @@ -1073,401 +327,6 @@ static int dm_set_powergating_state(struct amdgpu_ip_block *ip_block, static int dm_early_init(struct amdgpu_ip_block *ip_block); /* Allocate memory for FBC compressed data */ -static void amdgpu_dm_fbc_init(struct drm_connector *connector) -{ - struct amdgpu_device *adev = drm_to_adev(connector->dev); - struct dm_compressor_info *compressor = &adev->dm.compressor; - struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(connector); - struct drm_display_mode *mode; - unsigned long max_size = 0; - - if (adev->dm.dc->fbc_compressor == NULL) - return; - - if (aconn->dc_link->connector_signal != SIGNAL_TYPE_EDP) - return; - - if (compressor->bo_ptr) - return; - - - list_for_each_entry(mode, &connector->modes, head) { - if (max_size < (unsigned long) mode->htotal * mode->vtotal) - max_size = (unsigned long) mode->htotal * mode->vtotal; - } - - if (max_size) { - int r = amdgpu_bo_create_kernel(adev, max_size * 4, PAGE_SIZE, - AMDGPU_GEM_DOMAIN_GTT, &compressor->bo_ptr, - &compressor->gpu_addr, &compressor->cpu_addr); - - if (r) - drm_err(adev_to_drm(adev), "DM: Failed to initialize FBC\n"); - else { - adev->dm.dc->ctx->fbc_gpu_addr = compressor->gpu_addr; - drm_info(adev_to_drm(adev), "DM: FBC alloc %lu\n", max_size*4); - } - - } - -} - -static int amdgpu_dm_audio_component_get_eld(struct device *kdev, int port, - int pipe, bool *enabled, - unsigned char *buf, int max_bytes) -{ - struct drm_device *dev = dev_get_drvdata(kdev); - struct amdgpu_device *adev = drm_to_adev(dev); - struct drm_connector *connector; - struct drm_connector_list_iter conn_iter; - struct amdgpu_dm_connector *aconnector; - int ret = 0; - - *enabled = false; - - mutex_lock(&adev->dm.audio_lock); - - drm_connector_list_iter_begin(dev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - aconnector = to_amdgpu_dm_connector(connector); - if (aconnector->audio_inst != port) - continue; - - *enabled = true; - mutex_lock(&connector->eld_mutex); - ret = drm_eld_size(connector->eld); - memcpy(buf, connector->eld, min(max_bytes, ret)); - mutex_unlock(&connector->eld_mutex); - - break; - } - drm_connector_list_iter_end(&conn_iter); - - mutex_unlock(&adev->dm.audio_lock); - - drm_dbg_kms(adev_to_drm(adev), "Get ELD : idx=%d ret=%d en=%d\n", port, ret, *enabled); - - return ret; -} - -static const struct drm_audio_component_ops amdgpu_dm_audio_component_ops = { - .get_eld = amdgpu_dm_audio_component_get_eld, -}; - -static int amdgpu_dm_audio_component_bind(struct device *kdev, - struct device *hda_kdev, void *data) -{ - struct drm_device *dev = dev_get_drvdata(kdev); - struct amdgpu_device *adev = drm_to_adev(dev); - struct drm_audio_component *acomp = data; - - acomp->ops = &amdgpu_dm_audio_component_ops; - acomp->dev = kdev; - adev->dm.audio_component = acomp; - - return 0; -} - -static void amdgpu_dm_audio_component_unbind(struct device *kdev, - struct device *hda_kdev, void *data) -{ - struct amdgpu_device *adev = drm_to_adev(dev_get_drvdata(kdev)); - struct drm_audio_component *acomp = data; - - acomp->ops = NULL; - acomp->dev = NULL; - adev->dm.audio_component = NULL; -} - -static const struct component_ops amdgpu_dm_audio_component_bind_ops = { - .bind = amdgpu_dm_audio_component_bind, - .unbind = amdgpu_dm_audio_component_unbind, -}; - -static int amdgpu_dm_audio_init(struct amdgpu_device *adev) -{ - int i, ret; - - if (!amdgpu_audio) - return 0; - - adev->mode_info.audio.enabled = true; - - adev->mode_info.audio.num_pins = adev->dm.dc->res_pool->audio_count; - - for (i = 0; i < adev->mode_info.audio.num_pins; i++) { - adev->mode_info.audio.pin[i].channels = -1; - adev->mode_info.audio.pin[i].rate = -1; - adev->mode_info.audio.pin[i].bits_per_sample = -1; - adev->mode_info.audio.pin[i].status_bits = 0; - adev->mode_info.audio.pin[i].category_code = 0; - adev->mode_info.audio.pin[i].connected = false; - adev->mode_info.audio.pin[i].id = - adev->dm.dc->res_pool->audios[i]->inst; - adev->mode_info.audio.pin[i].offset = 0; - } - - ret = component_add(adev->dev, &amdgpu_dm_audio_component_bind_ops); - if (ret < 0) - return ret; - - adev->dm.audio_registered = true; - - return 0; -} - -static void amdgpu_dm_audio_fini(struct amdgpu_device *adev) -{ - if (!amdgpu_audio) - return; - - if (!adev->mode_info.audio.enabled) - return; - - if (adev->dm.audio_registered) { - component_del(adev->dev, &amdgpu_dm_audio_component_bind_ops); - adev->dm.audio_registered = false; - } - - /* TODO: Disable audio? */ - - adev->mode_info.audio.enabled = false; -} - -static void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin) -{ - struct drm_audio_component *acomp = adev->dm.audio_component; - - if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) { - drm_dbg_kms(adev_to_drm(adev), "Notify ELD: %d\n", pin); - - acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, - pin, -1); - } -} - -static int dm_dmub_hw_init(struct amdgpu_device *adev) -{ - const struct dmcub_firmware_header_v1_0 *hdr; - struct dmub_srv *dmub_srv = adev->dm.dmub_srv; - struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info; - const struct firmware *dmub_fw = adev->dm.dmub_fw; - struct dc *dc = adev->dm.dc; - struct dmcu *dmcu = adev->dm.dc->res_pool->dmcu; - struct abm *abm = adev->dm.dc->res_pool->abm; - struct dc_context *ctx = adev->dm.dc->ctx; - struct dmub_srv_hw_params hw_params; - enum dmub_status status; - const unsigned char *fw_inst_const, *fw_bss_data; - u32 i, fw_inst_const_size, fw_bss_data_size; - bool has_hw_support; - - if (!dmub_srv) - /* DMUB isn't supported on the ASIC. */ - return 0; - - if (!fb_info) { - drm_err(adev_to_drm(adev), "No framebuffer info for DMUB service.\n"); - return -EINVAL; - } - - if (!dmub_fw) { - /* Firmware required for DMUB support. */ - drm_err(adev_to_drm(adev), "No firmware provided for DMUB.\n"); - return -EINVAL; - } - - /* initialize register offsets for ASICs with runtime initialization available */ - if (dmub_srv->hw_funcs.init_reg_offsets) - dmub_srv->hw_funcs.init_reg_offsets(dmub_srv, ctx); - - status = dmub_srv_has_hw_support(dmub_srv, &has_hw_support); - if (status != DMUB_STATUS_OK) { - drm_err(adev_to_drm(adev), "Error checking HW support for DMUB: %d\n", status); - return -EINVAL; - } - - if (!has_hw_support) { - drm_info(adev_to_drm(adev), "DMUB unsupported on ASIC\n"); - return 0; - } - - /* Reset DMCUB if it was previously running - before we overwrite its memory. */ - status = dmub_srv_hw_reset(dmub_srv); - if (status != DMUB_STATUS_OK) - drm_warn(adev_to_drm(adev), "Error resetting DMUB HW: %d\n", status); - - hdr = (const struct dmcub_firmware_header_v1_0 *)dmub_fw->data; - - fw_inst_const = dmub_fw->data + - le32_to_cpu(hdr->header.ucode_array_offset_bytes) + - PSP_HEADER_BYTES_256; - - fw_bss_data = dmub_fw->data + - le32_to_cpu(hdr->header.ucode_array_offset_bytes) + - le32_to_cpu(hdr->inst_const_bytes); - - /* Copy firmware and bios info into FB memory. */ - fw_inst_const_size = adev->dm.fw_inst_size; - - fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes); - - /* if adev->firmware.load_type == AMDGPU_FW_LOAD_PSP, - * amdgpu_ucode_init_single_fw will load dmub firmware - * fw_inst_const part to cw0; otherwise, the firmware back door load - * will be done by dm_dmub_hw_init - */ - if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) { - memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const, - fw_inst_const_size); - } - - if (fw_bss_data_size) - memcpy(fb_info->fb[DMUB_WINDOW_2_BSS_DATA].cpu_addr, - fw_bss_data, fw_bss_data_size); - - /* Copy firmware bios info into FB memory. */ - memcpy(fb_info->fb[DMUB_WINDOW_3_VBIOS].cpu_addr, adev->bios, - adev->bios_size); - - /* Reset regions that need to be reset. */ - memset(fb_info->fb[DMUB_WINDOW_4_MAILBOX].cpu_addr, 0, - fb_info->fb[DMUB_WINDOW_4_MAILBOX].size); - - memset(fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr, 0, - fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size); - - memset(fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr, 0, - fb_info->fb[DMUB_WINDOW_6_FW_STATE].size); - - memset(fb_info->fb[DMUB_WINDOW_SHARED_STATE].cpu_addr, 0, - fb_info->fb[DMUB_WINDOW_SHARED_STATE].size); - - /* Initialize hardware. */ - memset(&hw_params, 0, sizeof(hw_params)); - hw_params.soc_fb_info.fb_base = adev->gmc.fb_start; - hw_params.soc_fb_info.fb_offset = adev->vm_manager.vram_base_offset; - - /* backdoor load firmware and trigger dmub running */ - if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) - hw_params.load_inst_const = true; - - if (dmcu) - hw_params.psp_version = dmcu->psp_version; - - for (i = 0; i < fb_info->num_fb; ++i) - hw_params.fb[i] = &fb_info->fb[i]; - - /* Enable usb4 dpia in the FW APU */ - if (dc->caps.is_apu && - dc->res_pool->usb4_dpia_count != 0 && - !dc->debug.dpia_debug.bits.disable_dpia) { - hw_params.dpia_supported = true; - hw_params.disable_dpia = dc->debug.dpia_debug.bits.disable_dpia; - hw_params.dpia_hpd_int_enable_supported = false; - hw_params.enable_non_transparent_setconfig = dc->config.consolidated_dpia_dp_lt; - hw_params.disable_dpia_bw_allocation = !dc->config.usb4_bw_alloc_support; - } - - switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { - case IP_VERSION(3, 5, 0): - case IP_VERSION(3, 5, 1): - case IP_VERSION(3, 6, 0): - case IP_VERSION(4, 2, 0): - case IP_VERSION(4, 2, 1): - hw_params.ips_sequential_ono = adev->external_rev_id > 0x10; - hw_params.lower_hbr3_phy_ssc = true; - break; - default: - break; - } - - status = dmub_srv_hw_init(dmub_srv, &hw_params); - if (status != DMUB_STATUS_OK) { - drm_err(adev_to_drm(adev), "Error initializing DMUB HW: %d\n", status); - return -EINVAL; - } - - /* Wait for firmware load to finish. */ - status = dmub_srv_wait_for_auto_load(dmub_srv, 100000); - if (status != DMUB_STATUS_OK) - drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status); - - /* Init DMCU and ABM if available. */ - if (dmcu && abm) { - dmcu->funcs->dmcu_init(dmcu); - abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu); - } - - if (!adev->dm.dc->ctx->dmub_srv) - adev->dm.dc->ctx->dmub_srv = dc_dmub_srv_create(adev->dm.dc, dmub_srv); - if (!adev->dm.dc->ctx->dmub_srv) { - drm_err(adev_to_drm(adev), "Couldn't allocate DC DMUB server!\n"); - return -ENOMEM; - } - - drm_info(adev_to_drm(adev), "DMUB hardware initialized: version=0x%08X\n", - adev->dm.dmcub_fw_version); - - /* Keeping sanity checks off if - * DCN31 >= 4.0.59.0 - * DCN314 >= 8.0.16.0 - * Otherwise, turn on sanity checks - */ - switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { - case IP_VERSION(3, 1, 2): - case IP_VERSION(3, 1, 3): - if (adev->dm.dmcub_fw_version && - adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) && - adev->dm.dmcub_fw_version < DMUB_FW_VERSION(4, 0, 59)) - adev->dm.dc->debug.sanity_checks = true; - break; - case IP_VERSION(3, 1, 4): - if (adev->dm.dmcub_fw_version && - adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) && - adev->dm.dmcub_fw_version < DMUB_FW_VERSION(8, 0, 16)) - adev->dm.dc->debug.sanity_checks = true; - break; - default: - break; - } - - return 0; -} - -static void dm_dmub_hw_resume(struct amdgpu_device *adev) -{ - struct dmub_srv *dmub_srv = adev->dm.dmub_srv; - enum dmub_status status; - bool init; - int r; - - if (!dmub_srv) { - /* DMUB isn't supported on the ASIC. */ - return; - } - - status = dmub_srv_is_hw_init(dmub_srv, &init); - if (status != DMUB_STATUS_OK) - drm_warn(adev_to_drm(adev), "DMUB hardware init check failed: %d\n", status); - - if (status == DMUB_STATUS_OK && init) { - /* Wait for firmware load to finish. */ - status = dmub_srv_wait_for_auto_load(dmub_srv, 100000); - if (status != DMUB_STATUS_OK) - drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status); - } else { - /* Perform the full hardware initialization. */ - r = dm_dmub_hw_init(adev); - if (r) - drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r); - } -} - static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_addr_space_config *pa_config) { u64 pt_base; @@ -1545,151 +404,6 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_ } -static void force_connector_state( - struct amdgpu_dm_connector *aconnector, - enum drm_connector_force force_state) -{ - struct drm_connector *connector = &aconnector->base; - - mutex_lock(&connector->dev->mode_config.mutex); - aconnector->base.force = force_state; - mutex_unlock(&connector->dev->mode_config.mutex); - - mutex_lock(&aconnector->hpd_lock); - drm_kms_helper_connector_hotplug_event(connector); - mutex_unlock(&aconnector->hpd_lock); -} - -static void dm_handle_hpd_rx_offload_work(struct work_struct *work) -{ - struct hpd_rx_irq_offload_work *offload_work; - struct amdgpu_dm_connector *aconnector; - struct dc_link *dc_link; - struct amdgpu_device *adev; - enum dc_connection_type new_connection_type = dc_connection_none; - unsigned long flags; - union test_response test_response; - - memset(&test_response, 0, sizeof(test_response)); - - offload_work = container_of(work, struct hpd_rx_irq_offload_work, work); - aconnector = offload_work->offload_wq->aconnector; - adev = offload_work->adev; - - if (!aconnector) { - drm_err(adev_to_drm(adev), "Can't retrieve aconnector in hpd_rx_irq_offload_work"); - goto skip; - } - - dc_link = aconnector->dc_link; - - mutex_lock(&aconnector->hpd_lock); - if (!dc_link_detect_connection_type(dc_link, &new_connection_type)) - drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n"); - mutex_unlock(&aconnector->hpd_lock); - - if (new_connection_type == dc_connection_none) - goto skip; - - if (amdgpu_in_reset(adev)) - goto skip; - - if (offload_work->data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY || - offload_work->data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) { - dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr, DOWN_OR_UP_MSG_RDY_EVENT); - spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags); - offload_work->offload_wq->is_handling_mst_msg_rdy_event = false; - spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags); - goto skip; - } - - mutex_lock(&adev->dm.dc_lock); - if (offload_work->data.bytes.device_service_irq.bits.AUTOMATED_TEST) { - dc_link_dp_handle_automated_test(dc_link); - - if (aconnector->timing_changed) { - /* force connector disconnect and reconnect */ - force_connector_state(aconnector, DRM_FORCE_OFF); - msleep(100); - force_connector_state(aconnector, DRM_FORCE_UNSPECIFIED); - } - - test_response.bits.ACK = 1; - - core_link_write_dpcd( - dc_link, - DP_TEST_RESPONSE, - &test_response.raw, - sizeof(test_response)); - } else if ((dc_link->connector_signal != SIGNAL_TYPE_EDP) && - dc_link_check_link_loss_status(dc_link, &offload_work->data) && - dc_link_dp_allow_hpd_rx_irq(dc_link)) { - /* offload_work->data is from handle_hpd_rx_irq-> - * schedule_hpd_rx_offload_work.this is defer handle - * for hpd short pulse. upon here, link status may be - * changed, need get latest link status from dpcd - * registers. if link status is good, skip run link - * training again. - */ - union hpd_irq_data irq_data; - - memset(&irq_data, 0, sizeof(irq_data)); - - /* before dc_link_dp_handle_link_loss, allow new link lost handle - * request be added to work queue if link lost at end of dc_link_ - * dp_handle_link_loss - */ - spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags); - offload_work->offload_wq->is_handling_link_loss = false; - spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags); - - if ((dc_link_dp_read_hpd_rx_irq_data(dc_link, &irq_data) == DC_OK) && - dc_link_check_link_loss_status(dc_link, &irq_data)) - dc_link_dp_handle_link_loss(dc_link); - } - mutex_unlock(&adev->dm.dc_lock); - -skip: - kfree(offload_work); - -} - -static struct hpd_rx_irq_offload_work_queue *hpd_rx_irq_create_workqueue(struct amdgpu_device *adev) -{ - struct dc *dc = adev->dm.dc; - int max_caps = dc->caps.max_links; - int i = 0; - struct hpd_rx_irq_offload_work_queue *hpd_rx_offload_wq = NULL; - - hpd_rx_offload_wq = kzalloc_objs(*hpd_rx_offload_wq, max_caps); - - if (!hpd_rx_offload_wq) - return NULL; - - - for (i = 0; i < max_caps; i++) { - hpd_rx_offload_wq[i].wq = - create_singlethread_workqueue("amdgpu_dm_hpd_rx_offload_wq"); - - if (hpd_rx_offload_wq[i].wq == NULL) { - drm_err(adev_to_drm(adev), "create amdgpu_dm_hpd_rx_offload_wq fail!"); - goto out_err; - } - - spin_lock_init(&hpd_rx_offload_wq[i].offload_lock); - } - - return hpd_rx_offload_wq; - -out_err: - for (i = 0; i < max_caps; i++) { - if (hpd_rx_offload_wq[i].wq) - destroy_workqueue(hpd_rx_offload_wq[i].wq); - } - kfree(hpd_rx_offload_wq); - return NULL; -} - struct amdgpu_stutter_quirk { u16 chip_vendor; u16 chip_device; @@ -1775,119 +489,6 @@ dm_free_gpu_mem( } -static enum dmub_status -dm_dmub_send_vbios_gpint_command(struct amdgpu_device *adev, - enum dmub_gpint_command command_code, - uint16_t param, - uint32_t timeout_us) -{ - union dmub_gpint_data_register reg, test; - uint32_t i; - - /* Assume that VBIOS DMUB is ready to take commands */ - - reg.bits.status = 1; - reg.bits.command_code = command_code; - reg.bits.param = param; - - cgs_write_register(adev->dm.cgs_device, 0x34c0 + 0x01f8, reg.all); - - for (i = 0; i < timeout_us; ++i) { - udelay(1); - - /* Check if our GPINT got acked */ - reg.bits.status = 0; - test = (union dmub_gpint_data_register) - cgs_read_register(adev->dm.cgs_device, 0x34c0 + 0x01f8); - - if (test.all == reg.all) - return DMUB_STATUS_OK; - } - - return DMUB_STATUS_TIMEOUT; -} - -static void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev) -{ - void *bb; - long long addr; - unsigned int bb_size; - int i = 0; - uint16_t chunk; - enum dmub_gpint_command send_addrs[] = { - DMUB_GPINT__SET_BB_ADDR_WORD0, - DMUB_GPINT__SET_BB_ADDR_WORD1, - DMUB_GPINT__SET_BB_ADDR_WORD2, - DMUB_GPINT__SET_BB_ADDR_WORD3, - }; - enum dmub_status ret; - - switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { - case IP_VERSION(4, 0, 1): - bb_size = sizeof(struct dml2_soc_bb); - break; - case IP_VERSION(4, 2, 0): - case IP_VERSION(4, 2, 1): - bb_size = sizeof(struct dml2_soc_bb); - break; - default: - return NULL; - } - - bb = dm_allocate_gpu_mem(adev, - DC_MEM_ALLOC_TYPE_GART, - bb_size, - &addr); - if (!bb) - return NULL; - - for (i = 0; i < 4; i++) { - /* Extract 16-bit chunk */ - chunk = ((uint64_t) addr >> (i * 16)) & 0xFFFF; - /* Send the chunk */ - ret = dm_dmub_send_vbios_gpint_command(adev, send_addrs[i], chunk, 30000); - if (ret != DMUB_STATUS_OK) - goto free_bb; - } - - /* Now ask DMUB to copy the bb */ - ret = dm_dmub_send_vbios_gpint_command(adev, DMUB_GPINT__BB_COPY, 1, 200000); - if (ret != DMUB_STATUS_OK) - goto free_bb; - - return bb; - -free_bb: - dm_free_gpu_mem(adev, DC_MEM_ALLOC_TYPE_GART, (void *) bb); - return NULL; - -} - -static enum dmub_ips_disable_type dm_get_default_ips_mode( - struct amdgpu_device *adev) -{ - enum dmub_ips_disable_type ret = DMUB_IPS_ENABLE; - - switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { - case IP_VERSION(3, 5, 0): - case IP_VERSION(3, 6, 0): - case IP_VERSION(3, 5, 1): - ret = DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF; - break; - case IP_VERSION(4, 2, 0): - case IP_VERSION(4, 2, 1): - ret = DMUB_IPS_ENABLE; - break; - default: - /* ASICs older than DCN35 do not have IPSs */ - if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0)) - ret = DMUB_IPS_DISABLE_ALL; - break; - } - - return ret; -} - static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm) { struct mod_power_init_params init_data[MAX_NUM_EDP]; @@ -1952,40 +553,6 @@ static int amdgpu_dm_init_power_module(struct amdgpu_display_manager *dm) return 0; } -static void hdmi_frl_status_polling_work(struct work_struct *work) -{ - struct amdgpu_display_manager *dm = - container_of(to_delayed_work(work), struct amdgpu_display_manager, - hdmi_frl_status_polling_work); - struct dc *dc = dm->dc; - struct dc_link *dc_link; - bool link_update = false; - - for (int i = 0; i < MAX_LINKS; i++) { - dc_link = dc->links[i]; - - if (!dc_link || !dc_link->local_sink) - continue; - - if (!dc_is_hdmi_signal(dc_link->connector_signal)) - continue; - - if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL) - continue; - - link_update = dc_link_frl_poll_status_flag(dc_link); - if (link_update) { - mutex_lock(&dm->dc_lock); - dc_link_detect(dc_link, DETECT_REASON_RETRAIN); - mutex_unlock(&dm->dc_lock); - } - } - - queue_delayed_work(dm->hdmi_frl_status_polling_wq, - &dm->hdmi_frl_status_polling_work, - msecs_to_jiffies(dm->hdmi_frl_status_polling_delay_ms)); -} - static int amdgpu_dm_init(struct amdgpu_device *adev) { struct dc_init_data init_data; @@ -2212,7 +779,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) dc_hardware_init(adev->dm.dc); - adev->dm.hpd_rx_offload_wq = hpd_rx_irq_create_workqueue(adev); + adev->dm.hpd_rx_offload_wq = amdgpu_dm_hpd_rx_irq_create_workqueue(adev); if (!adev->dm.hpd_rx_offload_wq) { drm_err(adev_to_drm(adev), "failed to create hpd rx offload workqueue.\n"); goto error; @@ -2265,8 +832,6 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) create_singlethread_workqueue("hdmi_frl_status_polling_workqueue"); if (!adev->dm.hdmi_frl_status_polling_wq) drm_err(adev_to_drm(adev), "failed to initialize hdmi_frl_status_polling_workqueue\n"); - adev->dm.hdmi_frl_status_polling_delay_ms = 200; - INIT_DELAYED_WORK(&adev->dm.hdmi_frl_status_polling_work, hdmi_frl_status_polling_work); } if (dc_is_dmub_outbox_supported(adev->dm.dc)) { init_completion(&adev->dm.dmub_aux_transfer_done); @@ -2283,8 +848,8 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) } amdgpu_dm_outbox_init(adev); - if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY, - dmub_aux_setconfig_callback, false)) { + if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_AUX_REPLY, + dm_dmub_aux_setconfig_callback, false)) { drm_err(adev_to_drm(adev), "fail to register dmub aux callback"); goto error; } @@ -2292,16 +857,17 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) for (size_t i = 0; i < ARRAY_SIZE(adev->dm.fused_io); i++) init_completion(&adev->dm.fused_io[i].replied); - if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO, - dmub_aux_fused_io_callback, false)) { + if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_FUSED_IO, + dm_dmub_aux_fused_io_callback, false)) { drm_err(adev_to_drm(adev), "fail to register dmub fused io callback"); goto error; } /* Enable outbox notification only after IRQ handlers are registered and DMUB is alive. * It is expected that DMUB will resend any pending notifications at this point. Note - * that hpd and hpd_irq handler registration are deferred to register_hpd_handlers() to - * align legacy interface initialization sequence. Connection status will be proactivly - * detected once in the amdgpu_dm_initialize_drm_device. + * that hpd and hpd_irq handler registration are deferred to + * amdgpu_dm_register_hpd_handlers() to align legacy interface initialization + * sequence. Connection status will be proactivly detected once in the + * amdgpu_dm_initialize_drm_device. */ dc_enable_dmub_outbox(adev->dm.dc); @@ -2581,224 +1147,6 @@ static int load_dmcu_fw(struct amdgpu_device *adev) return 0; } -static uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address) -{ - struct amdgpu_device *adev = ctx; - - return dm_read_reg(adev->dm.dc->ctx, address); -} - -static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address, - uint32_t value) -{ - struct amdgpu_device *adev = ctx; - - return dm_write_reg(adev->dm.dc->ctx, address, value); -} - -static int dm_dmub_sw_init(struct amdgpu_device *adev) -{ - struct dmub_srv_create_params create_params; - struct dmub_srv_fw_meta_info_params fw_meta_info_params; - struct dmub_srv_region_params region_params; - struct dmub_srv_region_info region_info; - struct dmub_srv_memory_params memory_params; - struct dmub_fw_meta_info fw_info; - struct dmub_srv_fb_info *fb_info; - struct dmub_srv *dmub_srv; - const struct dmcub_firmware_header_v1_0 *hdr; - enum dmub_asic dmub_asic; - enum dmub_status status; - static enum dmub_window_memory_type window_memory_type[DMUB_WINDOW_TOTAL] = { - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_0_INST_CONST - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_1_STACK - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_2_BSS_DATA - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_3_VBIOS - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_4_MAILBOX - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_5_TRACEBUFF - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_6_FW_STATE - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_7_SCRATCH_MEM - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_IB_MEM - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_SHARED_STATE - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_LSDMA_BUFFER - DMUB_WINDOW_MEMORY_TYPE_FB, //DMUB_WINDOW_CURSOR_OFFLOAD - }; - int r; - - switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { - case IP_VERSION(2, 1, 0): - dmub_asic = DMUB_ASIC_DCN21; - break; - case IP_VERSION(3, 0, 0): - dmub_asic = DMUB_ASIC_DCN30; - break; - case IP_VERSION(3, 0, 1): - dmub_asic = DMUB_ASIC_DCN301; - break; - case IP_VERSION(3, 0, 2): - dmub_asic = DMUB_ASIC_DCN302; - break; - case IP_VERSION(3, 0, 3): - dmub_asic = DMUB_ASIC_DCN303; - break; - case IP_VERSION(3, 1, 2): - case IP_VERSION(3, 1, 3): - dmub_asic = (adev->external_rev_id == YELLOW_CARP_B0) ? DMUB_ASIC_DCN31B : DMUB_ASIC_DCN31; - break; - case IP_VERSION(3, 1, 4): - dmub_asic = DMUB_ASIC_DCN314; - break; - case IP_VERSION(3, 1, 5): - dmub_asic = DMUB_ASIC_DCN315; - break; - case IP_VERSION(3, 1, 6): - dmub_asic = DMUB_ASIC_DCN316; - break; - case IP_VERSION(3, 2, 0): - dmub_asic = DMUB_ASIC_DCN32; - break; - case IP_VERSION(3, 2, 1): - dmub_asic = DMUB_ASIC_DCN321; - break; - case IP_VERSION(3, 5, 0): - case IP_VERSION(3, 5, 1): - dmub_asic = DMUB_ASIC_DCN35; - break; - case IP_VERSION(3, 6, 0): - dmub_asic = DMUB_ASIC_DCN36; - break; - case IP_VERSION(4, 0, 1): - dmub_asic = DMUB_ASIC_DCN401; - break; - case IP_VERSION(4, 2, 0): - dmub_asic = DMUB_ASIC_DCN42; - break; - case IP_VERSION(4, 2, 1): - dmub_asic = DMUB_ASIC_DCN42B; - break; - default: - /* ASIC doesn't support DMUB. */ - return 0; - } - - hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data; - adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version); - - if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { - adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id = - AMDGPU_UCODE_ID_DMCUB; - adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw = - adev->dm.dmub_fw; - adev->firmware.fw_size += - ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE); - - drm_info(adev_to_drm(adev), "Loading DMUB firmware via PSP: version=0x%08X\n", - adev->dm.dmcub_fw_version); - } - - - adev->dm.dmub_srv = kzalloc_obj(*adev->dm.dmub_srv); - dmub_srv = adev->dm.dmub_srv; - - if (!dmub_srv) { - drm_err(adev_to_drm(adev), "Failed to allocate DMUB service!\n"); - return -ENOMEM; - } - - memset(&create_params, 0, sizeof(create_params)); - create_params.user_ctx = adev; - create_params.funcs.reg_read = amdgpu_dm_dmub_reg_read; - create_params.funcs.reg_write = amdgpu_dm_dmub_reg_write; - create_params.asic = dmub_asic; - - /* Create the DMUB service. */ - status = dmub_srv_create(dmub_srv, &create_params); - if (status != DMUB_STATUS_OK) { - drm_err(adev_to_drm(adev), "Error creating DMUB service: %d\n", status); - return -EINVAL; - } - - /* Extract the FW meta info. */ - memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params)); - - fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) - - PSP_HEADER_BYTES_256; - fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes); - fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data + - le32_to_cpu(hdr->header.ucode_array_offset_bytes) + - PSP_HEADER_BYTES_256; - fw_meta_info_params.fw_bss_data = fw_meta_info_params.bss_data_size ? adev->dm.dmub_fw->data + - le32_to_cpu(hdr->header.ucode_array_offset_bytes) + - le32_to_cpu(hdr->inst_const_bytes) : NULL; - fw_meta_info_params.custom_psp_footer_size = 0; - - status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info); - if (status != DMUB_STATUS_OK) { - /* Skip returning early, just log the error. */ - drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status); - // return -EINVAL; - } - - /* Calculate the size of all the regions for the DMUB service. */ - memset(®ion_params, 0, sizeof(region_params)); - - region_params.inst_const_size = fw_meta_info_params.inst_const_size; - region_params.bss_data_size = fw_meta_info_params.bss_data_size; - region_params.vbios_size = adev->bios_size; - region_params.fw_bss_data = fw_meta_info_params.fw_bss_data; - region_params.fw_inst_const = fw_meta_info_params.fw_inst_const; - region_params.window_memory_type = window_memory_type; - region_params.fw_info = (status == DMUB_STATUS_OK) ? &fw_info : NULL; - - status = dmub_srv_calc_region_info(dmub_srv, ®ion_params, - ®ion_info); - - if (status != DMUB_STATUS_OK) { - drm_err(adev_to_drm(adev), "Error calculating DMUB region info: %d\n", status); - return -EINVAL; - } - - /* - * Allocate a framebuffer based on the total size of all the regions. - * TODO: Move this into GART. - */ - r = amdgpu_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE, - AMDGPU_GEM_DOMAIN_VRAM | - AMDGPU_GEM_DOMAIN_GTT, - &adev->dm.dmub_bo, - &adev->dm.dmub_bo_gpu_addr, - &adev->dm.dmub_bo_cpu_addr); - if (r) - return r; - - /* Rebase the regions on the framebuffer address. */ - memset(&memory_params, 0, sizeof(memory_params)); - memory_params.cpu_fb_addr = adev->dm.dmub_bo_cpu_addr; - memory_params.gpu_fb_addr = adev->dm.dmub_bo_gpu_addr; - memory_params.region_info = ®ion_info; - memory_params.window_memory_type = window_memory_type; - - adev->dm.dmub_fb_info = kzalloc_obj(*adev->dm.dmub_fb_info); - fb_info = adev->dm.dmub_fb_info; - - if (!fb_info) { - drm_err(adev_to_drm(adev), - "Failed to allocate framebuffer info for DMUB service!\n"); - return -ENOMEM; - } - - status = dmub_srv_calc_mem_info(dmub_srv, &memory_params, fb_info); - if (status != DMUB_STATUS_OK) { - drm_err(adev_to_drm(adev), "Error calculating DMUB FB info: %d\n", status); - return -EINVAL; - } - - adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev); - adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size; - - return 0; -} - static int dm_sw_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -2852,41 +1200,6 @@ static int dm_sw_fini(struct amdgpu_ip_block *ip_block) return 0; } -static int detect_mst_link_for_all_connectors(struct drm_device *dev) -{ - struct amdgpu_dm_connector *aconnector; - struct drm_connector *connector; - struct drm_connector_list_iter iter; - int ret = 0; - - drm_connector_list_iter_begin(dev, &iter); - drm_for_each_connector_iter(connector, &iter) { - - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - aconnector = to_amdgpu_dm_connector(connector); - if (aconnector->dc_link->type == dc_connection_mst_branch && - aconnector->mst_mgr.aux) { - drm_dbg_kms(dev, "DM_MST: starting TM on aconnector: %p [id: %d]\n", - aconnector, - aconnector->base.base.id); - - ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true); - if (ret < 0) { - drm_err(dev, "DM_MST: Failed to start MST\n"); - aconnector->dc_link->type = - dc_connection_single; - ret = dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx, - aconnector->dc_link); - break; - } - } - } - drm_connector_list_iter_end(&iter); - - return ret; -} static void amdgpu_dm_boot_time_crc_init(struct amdgpu_device *adev) { @@ -2984,7 +1297,7 @@ static int dm_late_init(struct amdgpu_ip_block *ip_block) } } - return detect_mst_link_for_all_connectors(adev_to_drm(adev)); + return amdgpu_dm_detect_mst_link_for_all_connectors(adev_to_drm(adev)); } static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr) @@ -3038,48 +1351,6 @@ out_fail: mutex_unlock(&mgr->lock); } -void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector) -{ - struct cec_notifier *n = aconnector->notifier; - - if (!n) - return; - - cec_notifier_phys_addr_invalidate(n); -} - -void hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector) -{ - struct drm_connector *connector = &aconnector->base; - struct cec_notifier *n = aconnector->notifier; - - if (!n) - return; - - cec_notifier_set_phys_addr(n, - connector->display_info.source_physical_address); -} - -static void s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend) -{ - struct amdgpu_dm_connector *aconnector; - struct drm_connector *connector; - struct drm_connector_list_iter conn_iter; - - drm_connector_list_iter_begin(ddev, &conn_iter); - drm_for_each_connector_iter(connector, &conn_iter) { - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - aconnector = to_amdgpu_dm_connector(connector); - if (suspend) - hdmi_cec_unset_edid(aconnector); - else - hdmi_cec_set_edid(aconnector); - } - drm_connector_list_iter_end(&conn_iter); -} - static void s3_handle_mst(struct drm_device *dev, bool suspend) { struct amdgpu_dm_connector *aconnector; @@ -3182,7 +1453,7 @@ static int dm_oem_i2c_hw_init(struct amdgpu_device *adev) oem_ddc_service = dc_get_oem_i2c_device(adev->dm.dc); if (oem_ddc_service) { - oem_i2c = create_i2c(oem_ddc_service, true); + oem_i2c = amdgpu_dm_create_i2c(oem_ddc_service, true); if (!oem_i2c) { drm_info(adev_to_drm(adev), "Failed to create oem i2c adapter data\n"); return -ENOMEM; @@ -3267,7 +1538,7 @@ static void dm_gpureset_toggle_interrupts(struct amdgpu_device *adev, int i = 0; for (i = 0; i < state->stream_count; i++) { - acrtc = get_crtc_by_otg_inst( + acrtc = amdgpu_dm_get_crtc_by_otg_inst( adev, state->stream_status[i].primary_otg_inst); if (acrtc && state->stream_status[i].plane_count != 0) { @@ -3344,16 +1615,6 @@ static enum dc_status amdgpu_dm_commit_zero_streams(struct dc *dc) return dc_commit_streams(dc, ¶ms); } -static void hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm) -{ - int i; - - if (dm->hpd_rx_offload_wq) { - for (i = 0; i < dm->dc->caps.max_links; i++) - flush_workqueue(dm->hpd_rx_offload_wq[i].wq); - } -} - static int dm_cache_state(struct amdgpu_device *adev) { int r; @@ -3449,7 +1710,7 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block) amdgpu_dm_irq_suspend(adev); - hpd_rx_irq_work_suspend(dm); + amdgpu_dm_hpd_rx_irq_work_suspend(dm); return 0; } @@ -3461,7 +1722,7 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block) return r; } - s3_handle_hdmi_cec(adev_to_drm(adev), true); + amdgpu_dm_s3_handle_hdmi_cec(adev_to_drm(adev), true); s3_handle_mst(adev_to_drm(adev), true); @@ -3475,7 +1736,7 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block) scoped_guard(mutex, &dm->dc_lock) amdgpu_dm_ism_force_full_power(dm); - hpd_rx_irq_work_suspend(dm); + amdgpu_dm_hpd_rx_irq_work_suspend(dm); dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D3); @@ -3487,26 +1748,7 @@ static int dm_suspend(struct amdgpu_ip_block *ip_block) return 0; } -struct drm_connector * -amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state, - struct drm_crtc *crtc) -{ - u32 i; - struct drm_connector_state *new_con_state; - struct drm_connector *connector; - struct drm_crtc *crtc_from_state; - - for_each_new_connector_in_state(state, connector, new_con_state, i) { - crtc_from_state = new_con_state->crtc; - - if (crtc_from_state == crtc) - return connector; - } - - return NULL; -} - -static void emulated_link_detect(struct dc_link *link) +void amdgpu_dm_emulated_link_detect(struct dc_link *link) { struct dc_sink_init_data sink_init_data = { 0 }; struct display_sink_capability sink_caps = { 0 }; @@ -3627,8 +1869,8 @@ static void dm_gpureset_commit_state(struct dc_state *dc_state, } } -static void apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev, - struct dc_sink *sink) +void amdgpu_dm_apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev, + struct dc_sink *sink) { struct dc_panel_patch *ppatch = NULL; @@ -3771,8 +2013,7 @@ static int dm_resume(struct amdgpu_ip_block *ip_block) for (i = 0; i < dc_state->stream_count; i++) { dc_state->streams[i]->mode_changed = true; for (j = 0; j < dc_state->stream_status[i].plane_count; j++) { - dc_state->stream_status[i].plane_states[j]->update_flags.raw - = 0xffffffff; + dc_pipe_update_bits_set_full(&dc_state->stream_status[i].plane_states[j]->update_bits); } } @@ -3835,7 +2076,7 @@ static int dm_resume(struct amdgpu_ip_block *ip_block) */ amdgpu_dm_irq_resume_early(adev); - s3_handle_hdmi_cec(ddev, false); + amdgpu_dm_s3_handle_hdmi_cec(ddev, false); /* On resume we need to rewrite the MSTM control bits to enable MST*/ s3_handle_mst(ddev, false); @@ -3870,14 +2111,14 @@ static int dm_resume(struct amdgpu_ip_block *ip_block) drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n"); if (aconnector->base.force && new_connection_type == dc_connection_none) { - emulated_link_detect(aconnector->dc_link); + amdgpu_dm_emulated_link_detect(aconnector->dc_link); } else { guard(mutex)(&dm->dc_lock); dc_exit_ips_for_hw_access(dm->dc); ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_RESUMEFROMS3S4); if (ret) { /* w/a delay for certain panels */ - apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink); + amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink); } } @@ -3955,7 +2196,6 @@ static const struct amd_ip_funcs amdgpu_dm_funcs = { .resume = dm_resume, .is_idle = dm_is_idle, .wait_for_idle = dm_wait_for_idle, - .check_soft_reset = dm_check_soft_reset, .soft_reset = dm_soft_reset, .set_clockgating_state = dm_set_clockgating_state, .set_powergating_state = dm_set_powergating_state, @@ -3988,1070 +2228,6 @@ static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = { .atomic_commit_setup = amdgpu_dm_atomic_setup_commit, }; -#define DDC_MANUFACTURERNAME_SAMSUNG 0x2D4C - -static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector) -{ - struct drm_connector *connector = &aconnector->base; - struct drm_display_info *display_info = &connector->display_info; - struct dc_link *link = aconnector->dc_link; - struct amdgpu_device *adev; - - adev = drm_to_adev(connector->dev); - - link->panel_type = PANEL_TYPE_NONE; - - switch (display_info->amd_vsdb.panel_type) { - case AMD_VSDB_PANEL_TYPE_OLED: - link->panel_type = PANEL_TYPE_OLED; - break; - case AMD_VSDB_PANEL_TYPE_MINILED: - link->panel_type = PANEL_TYPE_MINILED; - break; - } - - /* If VSDB didn't determine panel type, check DPCD ext caps */ - if (link->panel_type == PANEL_TYPE_NONE) { - if (link->dpcd_sink_ext_caps.bits.miniled == 1) - link->panel_type = PANEL_TYPE_MINILED; - if (link->dpcd_sink_ext_caps.bits.oled == 1) - link->panel_type = PANEL_TYPE_OLED; - } - - /* If VSDB and DPCD didn't determine panel type, check DID */ - if (link->panel_type == PANEL_TYPE_NONE) { - if (display_info->panel_type == DRM_MODE_PANEL_TYPE_LCD) - link->panel_type = PANEL_TYPE_LCD; - else if (display_info->panel_type == DRM_MODE_PANEL_TYPE_OLED) - link->panel_type = PANEL_TYPE_OLED; - } - - if (link->panel_type == PANEL_TYPE_NONE) { - struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb; - u32 lum1_max = vsdb->luminance_range1.max_luminance; - u32 lum2_max = vsdb->luminance_range2.max_luminance; - - if (vsdb->version && link->local_sink && - link->local_sink->edid_caps.manufacturer_id == - DDC_MANUFACTURERNAME_SAMSUNG && - lum1_max >= ((lum2_max * 3) / 2)) - link->panel_type = PANEL_TYPE_MINILED; - } - - if (link->panel_type == PANEL_TYPE_OLED) - drm_object_property_set_value(&connector->base, - adev_to_drm(adev)->mode_config.panel_type_property, - DRM_MODE_PANEL_TYPE_OLED); - else if (link->panel_type == PANEL_TYPE_LCD) - drm_object_property_set_value(&connector->base, - adev_to_drm(adev)->mode_config.panel_type_property, - DRM_MODE_PANEL_TYPE_LCD); - else - drm_object_property_set_value(&connector->base, - adev_to_drm(adev)->mode_config.panel_type_property, - DRM_MODE_PANEL_TYPE_UNKNOWN); - - drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type); -} - -static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) -{ - const struct drm_panel_backlight_quirk *panel_backlight_quirk; - struct amdgpu_dm_backlight_caps *caps; - struct drm_connector *conn_base; - struct amdgpu_device *adev; - struct drm_luminance_range_info *luminance_range; - struct drm_device *drm; - - if (aconnector->bl_idx == -1 || - aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP) - return; - - conn_base = &aconnector->base; - drm = conn_base->dev; - adev = drm_to_adev(drm); - - caps = &adev->dm.backlight_caps[aconnector->bl_idx]; - caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps; - caps->aux_support = false; - - if (caps->ext_caps->bits.oled == 1 - /* - * || - * caps->ext_caps->bits.sdr_aux_backlight_control == 1 || - * caps->ext_caps->bits.hdr_aux_backlight_control == 1 - */) - caps->aux_support = true; - - if (amdgpu_backlight == 0) - caps->aux_support = false; - else if (amdgpu_backlight == 1) - caps->aux_support = true; - if (caps->aux_support) - aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX; - - luminance_range = &conn_base->display_info.luminance_range; - - if (luminance_range->max_luminance) - caps->aux_max_input_signal = luminance_range->max_luminance; - else - caps->aux_max_input_signal = 512; - - if (luminance_range->min_luminance) - caps->aux_min_input_signal = luminance_range->min_luminance; - else - caps->aux_min_input_signal = 1; - - panel_backlight_quirk = - drm_get_panel_backlight_quirk(aconnector->drm_edid); - if (!IS_ERR_OR_NULL(panel_backlight_quirk)) { - if (panel_backlight_quirk->min_brightness) { - caps->min_input_signal = - panel_backlight_quirk->min_brightness - 1; - drm_info(drm, - "Applying panel backlight quirk, min_brightness: %d\n", - caps->min_input_signal); - } - if (panel_backlight_quirk->brightness_mask) { - drm_info(drm, - "Applying panel backlight quirk, brightness_mask: 0x%X\n", - panel_backlight_quirk->brightness_mask); - caps->brightness_mask = - panel_backlight_quirk->brightness_mask; - } - } -} - -DEFINE_FREE(sink_release, struct dc_sink *, if (_T) dc_sink_release(_T)) - -void amdgpu_dm_update_connector_after_detect( - struct amdgpu_dm_connector *aconnector) -{ - struct drm_connector *connector = &aconnector->base; - struct dc_sink *sink __free(sink_release) = NULL; - struct drm_device *dev = connector->dev; - - /* MST handled by drm_mst framework */ - if (aconnector->mst_mgr.mst_state == true) - return; - - sink = aconnector->dc_link->local_sink; - if (sink) - dc_sink_retain(sink); - - /* - * Edid mgmt connector gets first update only in mode_valid hook and then - * the connector sink is set to either fake or physical sink depends on link status. - * Skip if already done during boot. - */ - if (aconnector->base.force != DRM_FORCE_UNSPECIFIED - && aconnector->dc_em_sink) { - - /* - * For S3 resume with headless use eml_sink to fake stream - * because on resume connector->sink is set to NULL - */ - guard(mutex)(&dev->mode_config.mutex); - - if (sink) { - if (aconnector->dc_sink) { - amdgpu_dm_update_freesync_caps(connector, NULL, true); - /* - * retain and release below are used to - * bump up refcount for sink because the link doesn't point - * to it anymore after disconnect, so on next crtc to connector - * reshuffle by UMD we will get into unwanted dc_sink release - */ - dc_sink_release(aconnector->dc_sink); - } - aconnector->dc_sink = sink; - dc_sink_retain(aconnector->dc_sink); - amdgpu_dm_update_freesync_caps(connector, - aconnector->drm_edid, true); - } else { - amdgpu_dm_update_freesync_caps(connector, NULL, true); - if (!aconnector->dc_sink) { - aconnector->dc_sink = aconnector->dc_em_sink; - dc_sink_retain(aconnector->dc_sink); - } - } - - return; - } - - /* - * TODO: temporary guard to look for proper fix - * if this sink is MST sink, we should not do anything - */ - if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) - return; - - if (aconnector->dc_sink == sink) { - /* - * We got a DP short pulse (Link Loss, DP CTS, etc...). - * Do nothing!! - */ - drm_dbg_kms(dev, "DCHPD: connector_id=%d: dc_sink didn't change.\n", - aconnector->connector_id); - return; - } - - drm_dbg_kms(dev, "DCHPD: connector_id=%d: Old sink=%p New sink=%p\n", - aconnector->connector_id, aconnector->dc_sink, sink); - - /* When polling, DRM has already locked the mutex for us. */ - if (!drm_kms_helper_is_poll_worker()) - mutex_lock(&dev->mode_config.mutex); - - /* - * 1. Update status of the drm connector - * 2. Send an event and let userspace tell us what to do - */ - if (sink) { - /* - * TODO: check if we still need the S3 mode update workaround. - * If yes, put it here. - */ - if (aconnector->dc_sink) { - amdgpu_dm_update_freesync_caps(connector, NULL, true); - dc_sink_release(aconnector->dc_sink); - } - - aconnector->dc_sink = sink; - dc_sink_retain(aconnector->dc_sink); - drm_edid_free(aconnector->drm_edid); - aconnector->drm_edid = NULL; - if (sink->dc_edid.length == 0) { - hdmi_cec_unset_edid(aconnector); - if (aconnector->dc_link->aux_mode) { - drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux); - } - } else { - const struct edid *edid = (const struct edid *)sink->dc_edid.raw_edid; - - aconnector->drm_edid = drm_edid_alloc(edid, sink->dc_edid.length); - drm_edid_connector_update(connector, aconnector->drm_edid); - - hdmi_cec_set_edid(aconnector); - if (aconnector->dc_link->aux_mode) - drm_dp_cec_attach(&aconnector->dm_dp_aux.aux, - connector->display_info.source_physical_address); - } - - if (!aconnector->timing_requested) { - aconnector->timing_requested = - kzalloc_obj(struct dc_crtc_timing); - if (!aconnector->timing_requested) - drm_err(dev, - "failed to create aconnector->requested_timing\n"); - } - - amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid, true); - update_connector_ext_caps(aconnector); - dm_set_panel_type(aconnector); - } else { - hdmi_cec_unset_edid(aconnector); - drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux); - amdgpu_dm_update_freesync_caps(connector, NULL, true); - aconnector->num_modes = 0; - dc_sink_release(aconnector->dc_sink); - aconnector->dc_sink = NULL; - drm_edid_free(aconnector->drm_edid); - aconnector->drm_edid = NULL; - kfree(aconnector->timing_requested); - aconnector->timing_requested = NULL; - /* Set CP to DESIRED if it was ENABLED, so we can re-enable it again on hotplug */ - if (connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED) - connector->state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; - } - - update_subconnector_property(aconnector); - - /* When polling, the mutex will be unlocked for us by DRM. */ - if (!drm_kms_helper_is_poll_worker()) - mutex_unlock(&dev->mode_config.mutex); -} - -static bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2) -{ - if (!sink1 || !sink2) - return false; - if (sink1->sink_signal != sink2->sink_signal) - return false; - - if (sink1->dc_edid.length != sink2->dc_edid.length) - return false; - - if (memcmp(sink1->dc_edid.raw_edid, sink2->dc_edid.raw_edid, - sink1->dc_edid.length) != 0) - return false; - return true; -} - - -/** - * DOC: hdmi_hpd_debounce_work - * - * HDMI HPD debounce delay in milliseconds. When an HDMI display toggles HPD - * (such as during power save transitions), this delay determines how long to - * wait before processing the HPD event. This allows distinguishing between a - * physical unplug (>hdmi_hpd_debounce_delay) - * and a spontaneous RX HPD toggle (<hdmi_hpd_debounce_delay). - * - * If the toggle is less than this delay, the driver compares sink capabilities - * and permits a hotplug event if they changed. - * - * The default value of 1500ms was chosen based on experimental testing with - * various monitors that exhibit spontaneous HPD toggling behavior. - */ -static void hdmi_hpd_debounce_work(struct work_struct *work) -{ - struct amdgpu_dm_connector *aconnector = - container_of(to_delayed_work(work), struct amdgpu_dm_connector, - hdmi_hpd_debounce_work); - struct drm_connector *connector = &aconnector->base; - struct drm_device *dev = connector->dev; - struct amdgpu_device *adev = drm_to_adev(dev); - struct dc *dc = aconnector->dc_link->ctx->dc; - bool fake_reconnect = false; - bool reallow_idle = false; - bool ret = false; - guard(mutex)(&aconnector->hpd_lock); - - /* Re-detect the display */ - scoped_guard(mutex, &adev->dm.dc_lock) { - if (dc->caps.ips_support && dc->ctx->dmub_srv->idle_allowed) { - dc_allow_idle_optimizations(dc, false); - reallow_idle = true; - } - ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD); - } - - if (ret) { - /* Apply workaround delay for certain panels */ - apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink); - /* Compare sinks to determine if this was a spontaneous HPD toggle */ - if (are_sinks_equal(aconnector->dc_link->local_sink, aconnector->hdmi_prev_sink)) { - /* - * Sinks match - this was a spontaneous HDMI HPD toggle. - */ - drm_dbg_kms(dev, "HDMI HPD: Sink unchanged after debounce, internal re-enable\n"); - fake_reconnect = true; - } - - /* Update connector state */ - amdgpu_dm_update_connector_after_detect(aconnector); - - drm_modeset_lock_all(dev); - dm_restore_drm_connector_state(dev, connector); - drm_modeset_unlock_all(dev); - - /* Only notify OS if sink actually changed */ - if (!fake_reconnect && aconnector->base.force == DRM_FORCE_UNSPECIFIED) - drm_kms_helper_hotplug_event(dev); - } - - /* Release the cached sink reference */ - if (aconnector->hdmi_prev_sink) { - dc_sink_release(aconnector->hdmi_prev_sink); - aconnector->hdmi_prev_sink = NULL; - } - - scoped_guard(mutex, &adev->dm.dc_lock) { - if (reallow_idle && dc->caps.ips_support) - dc_allow_idle_optimizations(dc, true); - } -} - -static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector) -{ - struct drm_connector *connector = &aconnector->base; - struct drm_device *dev = connector->dev; - enum dc_connection_type new_connection_type = dc_connection_none; - struct amdgpu_device *adev = drm_to_adev(dev); - struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state); - struct dc *dc = aconnector->dc_link->ctx->dc; - bool ret = false; - bool debounce_required = false; - - if (adev->dm.disable_hpd_irq) - return; - - /* - * In case of failure or MST no need to update connector status or notify the OS - * since (for MST case) MST does this in its own context. - */ - guard(mutex)(&aconnector->hpd_lock); - - if (adev->dm.hdcp_workqueue) { - hdcp_reset_display(adev->dm.hdcp_workqueue, aconnector->dc_link->link_index); - dm_con_state->update_hdcp = true; - } - if (aconnector->fake_enable) - aconnector->fake_enable = false; - - aconnector->timing_changed = false; - - if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type)) - drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n"); - - /* - * Check for HDMI disconnect with debounce enabled. - */ - debounce_required = (aconnector->hdmi_hpd_debounce_delay_ms > 0 && - dc_is_hdmi_signal(aconnector->dc_link->connector_signal) && - new_connection_type == dc_connection_none && - aconnector->dc_link->local_sink != NULL); - - if (aconnector->base.force && new_connection_type == dc_connection_none) { - emulated_link_detect(aconnector->dc_link); - - drm_modeset_lock_all(dev); - dm_restore_drm_connector_state(dev, connector); - drm_modeset_unlock_all(dev); - - if (aconnector->base.force == DRM_FORCE_UNSPECIFIED) - drm_kms_helper_connector_hotplug_event(connector); - } else if (debounce_required) { - /* - * HDMI disconnect detected - schedule delayed work instead of - * processing immediately. This allows us to coalesce spurious - * HDMI signals from physical unplugs. - */ - drm_dbg_kms(dev, "HDMI HPD: Disconnect detected, scheduling debounce work (%u ms)\n", - aconnector->hdmi_hpd_debounce_delay_ms); - - /* Cache the current sink for later comparison */ - if (aconnector->hdmi_prev_sink) - dc_sink_release(aconnector->hdmi_prev_sink); - aconnector->hdmi_prev_sink = aconnector->dc_link->local_sink; - if (aconnector->hdmi_prev_sink) - dc_sink_retain(aconnector->hdmi_prev_sink); - - /* Schedule delayed detection. */ - if (mod_delayed_work(system_percpu_wq, - &aconnector->hdmi_hpd_debounce_work, - msecs_to_jiffies(aconnector->hdmi_hpd_debounce_delay_ms))) - drm_dbg_kms(dev, "HDMI HPD: Re-scheduled debounce work\n"); - - } else { - - /* If the aconnector->hdmi_hpd_debounce_work is scheduled, exit early */ - if (delayed_work_pending(&aconnector->hdmi_hpd_debounce_work)) - return; - - scoped_guard(mutex, &adev->dm.dc_lock) { - dc_exit_ips_for_hw_access(dc); - ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD); - } - if (ret) { - /* w/a delay for certain panels */ - apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink); - amdgpu_dm_update_connector_after_detect(aconnector); - - drm_modeset_lock_all(dev); - dm_restore_drm_connector_state(dev, connector); - drm_modeset_unlock_all(dev); - - if (aconnector->base.force == DRM_FORCE_UNSPECIFIED) - drm_kms_helper_connector_hotplug_event(connector); - } - } -} - -static void handle_hpd_irq(void *param) -{ - struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param; - - handle_hpd_irq_helper(aconnector); - -} - -static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq, - union hpd_irq_data hpd_irq_data) -{ - struct hpd_rx_irq_offload_work *offload_work = kzalloc_obj(*offload_work); - - if (!offload_work) { - drm_err(adev_to_drm(adev), "Failed to allocate hpd_rx_irq_offload_work.\n"); - return; - } - - INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work); - offload_work->data = hpd_irq_data; - offload_work->offload_wq = offload_wq; - offload_work->adev = adev; - - queue_work(offload_wq->wq, &offload_work->work); - drm_dbg_kms(adev_to_drm(adev), "queue work to handle hpd_rx offload work"); -} - -static void handle_hpd_rx_irq(void *param) -{ - struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param; - struct drm_connector *connector = &aconnector->base; - struct drm_device *dev = connector->dev; - struct dc_link *dc_link = aconnector->dc_link; - bool is_mst_root_connector = aconnector->mst_mgr.mst_state; - bool result = false; - enum dc_connection_type new_connection_type = dc_connection_none; - struct amdgpu_device *adev = drm_to_adev(dev); - union hpd_irq_data hpd_irq_data; - bool link_loss = false; - bool has_left_work = false; - int idx = dc_link->link_index; - struct hpd_rx_irq_offload_work_queue *offload_wq = &adev->dm.hpd_rx_offload_wq[idx]; - struct dc *dc = aconnector->dc_link->ctx->dc; - - memset(&hpd_irq_data, 0, sizeof(hpd_irq_data)); - - if (adev->dm.disable_hpd_irq) - return; - - /* - * TODO:Temporary add mutex to protect hpd interrupt not have a gpio - * conflict, after implement i2c helper, this mutex should be - * retired. - */ - mutex_lock(&aconnector->hpd_lock); - - result = dc_link_handle_hpd_rx_irq(dc_link, &hpd_irq_data, - &link_loss, true, &has_left_work); - - if (!has_left_work) - goto out; - - if (hpd_irq_data.bytes.device_service_irq.bits.AUTOMATED_TEST) { - schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data); - goto out; - } - - if (dc_link_dp_allow_hpd_rx_irq(dc_link)) { - if (hpd_irq_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY || - hpd_irq_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) { - bool skip = false; - - /* - * DOWN_REP_MSG_RDY is also handled by polling method - * mgr->cbs->poll_hpd_irq() - */ - spin_lock(&offload_wq->offload_lock); - skip = offload_wq->is_handling_mst_msg_rdy_event; - - if (!skip) - offload_wq->is_handling_mst_msg_rdy_event = true; - - spin_unlock(&offload_wq->offload_lock); - - if (!skip) - schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data); - - goto out; - } - - if (link_loss) { - bool skip = false; - - spin_lock(&offload_wq->offload_lock); - skip = offload_wq->is_handling_link_loss; - - if (!skip) - offload_wq->is_handling_link_loss = true; - - spin_unlock(&offload_wq->offload_lock); - - if (!skip) - schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data); - - goto out; - } - } - -out: - if (result && !is_mst_root_connector) { - /* Downstream Port status changed. */ - if (!dc_link_detect_connection_type(dc_link, &new_connection_type)) - drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n"); - - if (aconnector->base.force && new_connection_type == dc_connection_none) { - emulated_link_detect(dc_link); - - if (aconnector->fake_enable) - aconnector->fake_enable = false; - - amdgpu_dm_update_connector_after_detect(aconnector); - - - drm_modeset_lock_all(dev); - dm_restore_drm_connector_state(dev, connector); - drm_modeset_unlock_all(dev); - - drm_kms_helper_connector_hotplug_event(connector); - } else { - bool ret = false; - - mutex_lock(&adev->dm.dc_lock); - dc_exit_ips_for_hw_access(dc); - ret = dc_link_detect(dc_link, DETECT_REASON_HPDRX); - mutex_unlock(&adev->dm.dc_lock); - - if (ret) { - if (aconnector->fake_enable) - aconnector->fake_enable = false; - - amdgpu_dm_update_connector_after_detect(aconnector); - - drm_modeset_lock_all(dev); - dm_restore_drm_connector_state(dev, connector); - drm_modeset_unlock_all(dev); - - drm_kms_helper_connector_hotplug_event(connector); - } - } - } - if (hpd_irq_data.bytes.device_service_irq.bits.CP_IRQ) { - if (adev->dm.hdcp_workqueue) - hdcp_handle_cpirq(adev->dm.hdcp_workqueue, aconnector->base.index); - } - - if (dc_link->type != dc_connection_mst_branch) - drm_dp_cec_irq(&aconnector->dm_dp_aux.aux); - - mutex_unlock(&aconnector->hpd_lock); -} - -static int register_hpd_handlers(struct amdgpu_device *adev) -{ - struct drm_device *dev = adev_to_drm(adev); - struct drm_connector *connector; - struct amdgpu_dm_connector *aconnector; - const struct dc_link *dc_link; - struct dc_interrupt_params int_params = {0}; - - int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; - int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; - - if (dc_is_dmub_outbox_supported(adev->dm.dc)) { - if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD, - dmub_hpd_callback, true)) { - drm_err(adev_to_drm(adev), "fail to register dmub hpd callback"); - return -EINVAL; - } - - if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ, - dmub_hpd_callback, true)) { - drm_err(adev_to_drm(adev), "fail to register dmub hpd callback"); - return -EINVAL; - } - - if (!register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_SENSE_NOTIFY, - dmub_hpd_sense_callback, true)) { - drm_err(adev_to_drm(adev), "fail to register dmub hpd sense callback"); - return -EINVAL; - } - } - - list_for_each_entry(connector, - &dev->mode_config.connector_list, head) { - - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - aconnector = to_amdgpu_dm_connector(connector); - dc_link = aconnector->dc_link; - - if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) { - int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; - int_params.irq_source = dc_link->irq_source_hpd; - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_HPD1 || - int_params.irq_source > DC_IRQ_SOURCE_HPD6) { - drm_err(adev_to_drm(adev), "Failed to register hpd irq!\n"); - return -EINVAL; - } - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - handle_hpd_irq, (void *) aconnector)) - return -ENOMEM; - } - - if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) { - - /* Also register for DP short pulse (hpd_rx). */ - int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; - int_params.irq_source = dc_link->irq_source_hpd_rx; - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_HPD1RX || - int_params.irq_source > DC_IRQ_SOURCE_HPD6RX) { - drm_err(adev_to_drm(adev), "Failed to register hpd rx irq!\n"); - return -EINVAL; - } - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - handle_hpd_rx_irq, (void *) aconnector)) - return -ENOMEM; - } - } - return 0; -} - -/* Register IRQ sources and initialize IRQ callbacks */ -static int dce110_register_irq_handlers(struct amdgpu_device *adev) -{ - struct dc *dc = adev->dm.dc; - struct common_irq_params *c_irq_params; - struct dc_interrupt_params int_params = {0}; - int r; - int i; - unsigned int src_id; - unsigned int client_id = AMDGPU_IRQ_CLIENTID_LEGACY; - /* Use different interrupts for VBLANK on DCE 6 vs. newer. */ - const unsigned int vblank_d1 = - adev->dm.dc->ctx->dce_version >= DCE_VERSION_8_0 - ? VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 : 1; - - if (adev->family >= AMDGPU_FAMILY_AI) - client_id = SOC15_IH_CLIENTID_DCE; - - int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; - int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; - - /* - * Actions of amdgpu_irq_add_id(): - * 1. Register a set() function with base driver. - * Base driver will call set() function to enable/disable an - * interrupt in DC hardware. - * 2. Register amdgpu_dm_irq_handler(). - * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts - * coming from DC hardware. - * amdgpu_dm_irq_handler() will re-direct the interrupt to DC - * for acknowledging and handling. - */ - - /* Use VBLANK interrupt */ - for (i = 0; i < adev->mode_info.num_crtc; i++) { - src_id = vblank_d1 + i; - r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->crtc_irq); - if (r) { - drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, src_id, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 || - int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) { - drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_crtc_high_irq, c_irq_params)) - return -ENOMEM; - } - - if (dc_supports_vrr(adev->dm.dc->ctx->dce_version)) { - /* Use VUPDATE interrupt */ - for (i = 0; i < adev->mode_info.num_crtc; i++) { - src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + i * 2; - r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->vupdate_irq); - if (r) { - drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, src_id, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 || - int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) { - drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.vupdate_params[ - int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1]; - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_vupdate_high_irq, c_irq_params)) - return -ENOMEM; - } - } - - /* Use GRPH_PFLIP interrupt */ - for (i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP; - i <= VISLANDS30_IV_SRCID_D6_GRPH_PFLIP; i += 2) { - r = amdgpu_irq_add_id(adev, client_id, i, &adev->pageflip_irq); - if (r) { - drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, i, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST || - int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) { - drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_pflip_high_irq, c_irq_params)) - return -ENOMEM; - } - - /* HPD */ - r = amdgpu_irq_add_id(adev, client_id, - VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A, &adev->hpd_irq); - if (r) { - drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n"); - return r; - } - - r = register_hpd_handlers(adev); - - return r; -} - -/* Register IRQ sources and initialize IRQ callbacks */ -static int dcn10_register_irq_handlers(struct amdgpu_device *adev) -{ - struct dc *dc = adev->dm.dc; - struct common_irq_params *c_irq_params; - struct dc_interrupt_params int_params = {0}; - int r; - int i; -#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) - static const unsigned int vrtl_int_srcid[] = { - DCN_1_0__SRCID__OTG1_VERTICAL_INTERRUPT0_CONTROL, - DCN_1_0__SRCID__OTG2_VERTICAL_INTERRUPT0_CONTROL, - DCN_1_0__SRCID__OTG3_VERTICAL_INTERRUPT0_CONTROL, - DCN_1_0__SRCID__OTG4_VERTICAL_INTERRUPT0_CONTROL, - DCN_1_0__SRCID__OTG5_VERTICAL_INTERRUPT0_CONTROL, - DCN_1_0__SRCID__OTG6_VERTICAL_INTERRUPT0_CONTROL - }; -#endif - - int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; - int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; - - /* - * Actions of amdgpu_irq_add_id(): - * 1. Register a set() function with base driver. - * Base driver will call set() function to enable/disable an - * interrupt in DC hardware. - * 2. Register amdgpu_dm_irq_handler(). - * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts - * coming from DC hardware. - * amdgpu_dm_irq_handler() will re-direct the interrupt to DC - * for acknowledging and handling. - */ - - /* Use VSTARTUP interrupt */ - for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP; - i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1; - i++) { - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq); - - if (r) { - drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, i, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 || - int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) { - drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_crtc_high_irq, c_irq_params)) - return -ENOMEM; - } - - /* Use otg vertical line interrupt */ -#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) - for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) { - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, - vrtl_int_srcid[i], &adev->vline0_irq); - - if (r) { - drm_err(adev_to_drm(adev), "Failed to add vline0 irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, vrtl_int_srcid[i], 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_DC1_VLINE0 || - int_params.irq_source > DC_IRQ_SOURCE_DC6_VLINE0) { - drm_err(adev_to_drm(adev), "Failed to register vline0 irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.vline0_params[int_params.irq_source - - DC_IRQ_SOURCE_DC1_VLINE0]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_dcn_vertical_interrupt0_high_irq, - c_irq_params)) - return -ENOMEM; - } -#endif - - /* Use VUPDATE_NO_LOCK interrupt on DCN, which seems to correspond to - * the regular VUPDATE interrupt on DCE. We want DC_IRQ_SOURCE_VUPDATEx - * to trigger at end of each vblank, regardless of state of the lock, - * matching DCE behaviour. - */ - for (i = DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT; - i <= DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT + adev->mode_info.num_crtc - 1; - i++) { - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->vupdate_irq); - - if (r) { - drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, i, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 || - int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) { - drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.vupdate_params[int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_vupdate_high_irq, c_irq_params)) - return -ENOMEM; - } - - /* Use GRPH_PFLIP interrupt */ - for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT; - i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1; - i++) { - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq); - if (r) { - drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n"); - return r; - } - - int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, i, 0); - - if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || - int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST || - int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) { - drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n"); - return -EINVAL; - } - - c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_pflip_high_irq, c_irq_params)) - return -ENOMEM; - } - - /* HPD */ - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT, - &adev->hpd_irq); - if (r) { - drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n"); - return r; - } - - r = register_hpd_handlers(adev); - - return r; -} -/* Register Outbox IRQ sources and initialize IRQ callbacks */ -static int register_outbox_irq_handlers(struct amdgpu_device *adev) -{ - struct dc *dc = adev->dm.dc; - struct common_irq_params *c_irq_params; - struct dc_interrupt_params int_params = {0}; - int r, i; - - int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; - int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; - - r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT, - &adev->dmub_outbox_irq); - if (r) { - drm_err(adev_to_drm(adev), "Failed to add outbox irq id!\n"); - return r; - } - - if (dc->ctx->dmub_srv) { - i = DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT; - int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; - int_params.irq_source = - dc_interrupt_to_irq_source(dc, i, 0); - - c_irq_params = &adev->dm.dmub_outbox_params[0]; - - c_irq_params->adev = adev; - c_irq_params->irq_src = int_params.irq_source; - - if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, - dm_dmub_outbox1_low_irq, c_irq_params)) - return -ENOMEM; - } - - return 0; -} - /* * Acquires the lock for the atomic state object and returns * the new atomic state. @@ -5201,420 +2377,6 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev) return 0; } -#define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12 -#define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255 -#define AMDGPU_DM_MIN_SPREAD ((AMDGPU_DM_DEFAULT_MAX_BACKLIGHT - AMDGPU_DM_DEFAULT_MIN_BACKLIGHT) / 2) -#define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50 - -void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm, - int bl_idx) -{ - struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[bl_idx]; - - if (caps->caps_valid) - return; - -#if defined(CONFIG_ACPI) - amdgpu_acpi_get_backlight_caps(caps); - - /* validate the firmware value is sane */ - if (caps->caps_valid) { - int spread = caps->max_input_signal - caps->min_input_signal; - - if (caps->max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT || - caps->min_input_signal < 0 || - spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT || - spread < AMDGPU_DM_MIN_SPREAD) { - drm_dbg_kms(adev_to_drm(dm->adev), "DM: Invalid backlight caps: min=%d, max=%d\n", - caps->min_input_signal, caps->max_input_signal); - caps->caps_valid = false; - } - } - - if (!caps->caps_valid) { - caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; - caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; - caps->caps_valid = true; - } -#else - if (caps->aux_support) - return; - - caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; - caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; - caps->caps_valid = true; -#endif -} - -static int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps, - unsigned int *min, unsigned int *max) -{ - if (!caps) - return 0; - - if (caps->aux_support) { - // Firmware limits are in nits, DC API wants millinits. - *max = 1000 * caps->aux_max_input_signal; - *min = 1000 * caps->aux_min_input_signal; - } else { - // Firmware limits are 8-bit, PWM control is 16-bit. - *max = 0x101 * caps->max_input_signal; - *min = 0x101 * caps->min_input_signal; - } - return 1; -} - -/* Rescale from [min..max] to [0..AMDGPU_MAX_BL_LEVEL] */ -static inline u32 scale_input_to_fw(int min, int max, u64 input) -{ - return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max - min); -} - -/* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */ -static inline u32 scale_fw_to_input(int min, int max, u64 input) -{ - return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL); -} - -static void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps, - unsigned int min, unsigned int max, - uint32_t *user_brightness) -{ - u32 brightness = scale_input_to_fw(min, max, *user_brightness); - u8 lower_signal, upper_signal, upper_lum, lower_lum, lum; - int left, right; - - if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) - return; - - if (!caps->data_points) - return; - - /* - * Handle the case where brightness is below the first data point - * Interpolate between (0,0) and (first_signal, first_lum) - */ - if (brightness < caps->luminance_data[0].input_signal) { - lum = DIV_ROUND_CLOSEST(caps->luminance_data[0].luminance * brightness, - caps->luminance_data[0].input_signal); - goto scale; - } - - left = 0; - right = caps->data_points - 1; - while (left <= right) { - int mid = left + (right - left) / 2; - u8 signal = caps->luminance_data[mid].input_signal; - - /* Exact match found */ - if (signal == brightness) { - lum = caps->luminance_data[mid].luminance; - goto scale; - } - - if (signal < brightness) - left = mid + 1; - else - right = mid - 1; - } - - /* verify bound */ - if (left >= caps->data_points) - left = caps->data_points - 1; - - /* At this point, left > right */ - lower_signal = caps->luminance_data[right].input_signal; - upper_signal = caps->luminance_data[left].input_signal; - lower_lum = caps->luminance_data[right].luminance; - upper_lum = caps->luminance_data[left].luminance; - - /* interpolate */ - if (right == left || !lower_lum) - lum = upper_lum; - else - lum = lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) * - (brightness - lower_signal), - upper_signal - lower_signal); -scale: - *user_brightness = scale_fw_to_input(min, max, - DIV_ROUND_CLOSEST(lum * brightness, 101)); -} - -static u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps, - uint32_t brightness) -{ - unsigned int min, max; - - if (!get_brightness_range(caps, &min, &max)) - return brightness; - - convert_custom_brightness(caps, min, max, &brightness); - - // Rescale 0..max to min..max - return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max); -} - -static u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps, - uint32_t brightness) -{ - unsigned int min, max; - - if (!get_brightness_range(caps, &min, &max)) - return brightness; - - if (brightness < min) - return 0; - // Rescale min..max to 0..max - return DIV_ROUND_CLOSEST_ULL((u64)max * (brightness - min), - max - min); -} - -static struct dc_stream_state *dm_find_stream_with_link( - struct amdgpu_display_manager *dm, - struct dc_link *link) -{ - struct dc_state *cur_dc_state = dm->dc->current_state; - struct dc_stream_state *stream = NULL; - int i; - - for (i = 0; i < cur_dc_state->stream_count; i++) { - stream = cur_dc_state->streams[i]; - if (stream->link == link) - return stream; - } - - return NULL; -} - -static void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, - int bl_idx, - u32 user_brightness) -{ - struct amdgpu_dm_backlight_caps *caps; - struct dc_link *link; - u32 brightness = 0; - bool rc = false, reallow_idle = false; - struct drm_connector *connector; - struct dc_stream_state *stream; - unsigned int min, max; - - list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) { - struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); - - if (aconnector->bl_idx != bl_idx) - continue; - - /* if connector is off, save the brightness for next time it's on */ - if (!aconnector->base.encoder) { - dm->brightness[bl_idx] = user_brightness; - dm->actual_brightness[bl_idx] = 0; - return; - } - } - - amdgpu_dm_update_backlight_caps(dm, bl_idx); - caps = &dm->backlight_caps[bl_idx]; - - dm->brightness[bl_idx] = user_brightness; - /* update scratch register */ - if (bl_idx == 0) - amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]); - brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]); - link = (struct dc_link *)dm->backlight_link[bl_idx]; - - /* Apply brightness quirk */ - if (caps->brightness_mask) - brightness |= caps->brightness_mask; - - if (trace_amdgpu_dm_brightness_enabled()) { - trace_amdgpu_dm_brightness(__builtin_return_address(0), - user_brightness, - brightness, - caps->aux_support, - power_supply_is_system_supplied() > 0); - } - - stream = dm_find_stream_with_link(dm, link); - if (!stream) - return; - - mutex_lock(&dm->dc_lock); - if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) { - dc_allow_idle_optimizations(dm->dc, false); - reallow_idle = true; - } - - if (caps->aux_support) { - rc = mod_power_set_backlight_nits(dm->power_module, stream, brightness, - AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true); - } else { - /* power module uses millipercent */ - get_brightness_range(caps, &min, &max); - brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000; - rc = mod_power_set_backlight_percent(dm->power_module, stream, - brightness, 0, false); - } - - /* - * Some kms clients create a ramped backlight transition effect - * by rapidly changing the backlight. Yet we must wait on dmcub - * fw to exit psr/replay before programming backlight. To - * prevent lag, keep disable psr/replay and let the next atomic - * flip clear the event. - * - * ToDo: use ISM to handle rapidly backlight change - * - * Rapidly backlight change is similar to rapidly cursor events, - * which is now handled by ISM. ISM can delay the event until system - * is really idle, so we may use ISM to handle backlight change as well. - */ - amdgpu_dm_psr_set_event(dm, stream, true, - psr_event_hw_programming, true); - amdgpu_dm_replay_set_event(dm, stream, true, - replay_event_hw_programming, true); - - if (dm->dc->caps.ips_support && reallow_idle) - dc_allow_idle_optimizations(dm->dc, true); - - mutex_unlock(&dm->dc_lock); - - if (rc) - dm->actual_brightness[bl_idx] = user_brightness; -} - -static int amdgpu_dm_backlight_update_status(struct backlight_device *bd) -{ - struct amdgpu_display_manager *dm = bl_get_data(bd); - int i; - - for (i = 0; i < dm->num_of_edps; i++) { - if (bd == dm->backlight_dev[i]) - break; - } - if (i >= AMDGPU_DM_MAX_NUM_EDP) - i = 0; - amdgpu_dm_backlight_set_level(dm, i, bd->props.brightness); - - return 0; -} - -static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm, - int bl_idx) -{ - int ret; - struct amdgpu_dm_backlight_caps caps; - struct dc_link *link = (struct dc_link *)dm->backlight_link[bl_idx]; - - amdgpu_dm_update_backlight_caps(dm, bl_idx); - caps = dm->backlight_caps[bl_idx]; - - if (caps.aux_support) { - u32 avg, peak; - - if (!dc_link_get_backlight_level_nits(link, &avg, &peak)) - return dm->brightness[bl_idx]; - return convert_brightness_to_user(&caps, avg); - } - - ret = dc_link_get_backlight_level(link); - - if (ret == DC_ERROR_UNEXPECTED) - return dm->brightness[bl_idx]; - - return convert_brightness_to_user(&caps, ret); -} - -static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd) -{ - struct amdgpu_display_manager *dm = bl_get_data(bd); - int i; - - for (i = 0; i < dm->num_of_edps; i++) { - if (bd == dm->backlight_dev[i]) - break; - } - if (i >= AMDGPU_DM_MAX_NUM_EDP) - i = 0; - return amdgpu_dm_backlight_get_level(dm, i); -} - -static const struct backlight_ops amdgpu_dm_backlight_ops = { - .options = BL_CORE_SUSPENDRESUME, - .get_brightness = amdgpu_dm_backlight_get_brightness, - .update_status = amdgpu_dm_backlight_update_status, -}; - -static void -amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector) -{ - struct drm_device *drm = aconnector->base.dev; - struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm; - struct backlight_properties props = { 0 }; - struct amdgpu_dm_backlight_caps *caps; - char bl_name[16]; - int min, max; - int real_brightness; - int init_brightness; - - if (aconnector->bl_idx == -1) - return; - - if (!acpi_video_backlight_use_native()) { - drm_info(drm, "Skipping amdgpu DM backlight registration\n"); - /* Try registering an ACPI video backlight device instead. */ - acpi_video_register_backlight(); - return; - } - - caps = &dm->backlight_caps[aconnector->bl_idx]; - if (get_brightness_range(caps, &min, &max)) { - if (power_supply_is_system_supplied() > 0) - props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, 100); - else - props.brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, 100); - /* min is zero, so max needs to be adjusted */ - props.max_brightness = max - min; - drm_dbg(drm, "Backlight caps: min: %d, max: %d, ac %d, dc %d\n", min, max, - caps->ac_level, caps->dc_level); - } else - props.brightness = props.max_brightness = MAX_BACKLIGHT_LEVEL; - - init_brightness = props.brightness; - - if (caps->data_points && !(amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE)) { - drm_info(drm, "Using custom brightness curve\n"); - props.scale = BACKLIGHT_SCALE_NON_LINEAR; - } else - props.scale = BACKLIGHT_SCALE_LINEAR; - props.type = BACKLIGHT_RAW; - - snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d", - drm->primary->index + aconnector->bl_idx); - - dm->backlight_dev[aconnector->bl_idx] = - backlight_device_register(bl_name, aconnector->base.kdev, dm, - &amdgpu_dm_backlight_ops, &props); - dm->brightness[aconnector->bl_idx] = props.brightness; - - if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) { - drm_err(drm, "DM: Backlight registration failed!\n"); - dm->backlight_dev[aconnector->bl_idx] = NULL; - } else { - /* - * dm->brightness[x] can be inconsistent just after startup until - * ops.get_brightness is called. - */ - real_brightness = - amdgpu_dm_backlight_ops.get_brightness(dm->backlight_dev[aconnector->bl_idx]); - - if (real_brightness != init_brightness) { - dm->actual_brightness[aconnector->bl_idx] = real_brightness; - dm->brightness[aconnector->bl_idx] = real_brightness; - } - drm_dbg_driver(drm, "DM: Registered Backlight device: %s\n", bl_name); - } -} - static int initialize_plane(struct amdgpu_display_manager *dm, struct amdgpu_mode_info *mode_info, int plane_id, enum drm_plane_type plane_type, @@ -5656,42 +2418,6 @@ static int initialize_plane(struct amdgpu_display_manager *dm, } -static void setup_backlight_device(struct amdgpu_display_manager *dm, - struct amdgpu_dm_connector *aconnector) -{ - struct amdgpu_dm_backlight_caps *caps; - struct dc_link *link = aconnector->dc_link; - int bl_idx = dm->num_of_edps; - - if (!(link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) || - link->type == dc_connection_none) - return; - - if (dm->num_of_edps >= AMDGPU_DM_MAX_NUM_EDP) { - drm_warn(adev_to_drm(dm->adev), "Too much eDP connections, skipping backlight setup for additional eDPs\n"); - return; - } - - aconnector->bl_idx = bl_idx; - - amdgpu_dm_update_backlight_caps(dm, bl_idx); - dm->backlight_link[bl_idx] = link; - dm->num_of_edps++; - - update_connector_ext_caps(aconnector); - caps = &dm->backlight_caps[aconnector->bl_idx]; - - /* Only offer ABM property when non-OLED and user didn't turn off by module parameter */ - if (caps->ext_caps && !caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0) - drm_object_attach_property(&aconnector->base.base, - dm->adev->mode_info.abm_level_property, - ABM_SYSFS_CONTROL); -} - -static void amdgpu_set_panel_orientation(struct drm_connector *connector); - - - /* * In this architecture, the association * connector -> encoder -> crtc @@ -5803,7 +2529,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) case IP_VERSION(4, 0, 1): case IP_VERSION(4, 2, 0): case IP_VERSION(4, 2, 1): - if (register_outbox_irq_handlers(dm->adev)) { + if (amdgpu_dm_register_outbox_irq_handlers(dm->adev)) { drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n"); goto fail; } @@ -5916,7 +2642,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n"); if (aconnector->base.force && new_connection_type == dc_connection_none) { - emulated_link_detect(link); + amdgpu_dm_emulated_link_detect(link); amdgpu_dm_update_connector_after_detect(aconnector); } else { bool ret = false; @@ -5928,7 +2654,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) if (ret) { amdgpu_dm_update_connector_after_detect(aconnector); - setup_backlight_device(dm, aconnector); + amdgpu_dm_setup_backlight_device(dm, aconnector); /* Disable PSR if Replay can be enabled */ if (replay_feature_enabled) @@ -5980,7 +2706,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) case CHIP_VEGA10: case CHIP_VEGA12: case CHIP_VEGA20: - if (dce110_register_irq_handlers(dm->adev)) { + if (amdgpu_dm_dce110_register_irq_handlers(dm->adev)) { drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n"); goto fail; } @@ -6010,7 +2736,7 @@ static int amdgpu_dm_initialize_drm_device(struct amdgpu_device *adev) case IP_VERSION(4, 0, 1): case IP_VERSION(4, 2, 0): case IP_VERSION(4, 2, 1): - if (dcn10_register_irq_handlers(dm->adev)) { + if (amdgpu_dm_dcn10_register_irq_handlers(dm->adev)) { drm_err(adev_to_drm(adev), "DM: Failed to initialize IRQ\n"); goto fail; } @@ -6101,78 +2827,6 @@ DEVICE_ATTR_WO(s3_debug); #endif -static int dm_init_microcode(struct amdgpu_device *adev) -{ - char *fw_name_dmub; - int r; - - switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { - case IP_VERSION(2, 1, 0): - fw_name_dmub = FIRMWARE_RENOIR_DMUB; - if (ASICREV_IS_GREEN_SARDINE(adev->external_rev_id)) - fw_name_dmub = FIRMWARE_GREEN_SARDINE_DMUB; - break; - case IP_VERSION(3, 0, 0): - if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(10, 3, 0)) - fw_name_dmub = FIRMWARE_SIENNA_CICHLID_DMUB; - else - fw_name_dmub = FIRMWARE_NAVY_FLOUNDER_DMUB; - break; - case IP_VERSION(3, 0, 1): - fw_name_dmub = FIRMWARE_VANGOGH_DMUB; - break; - case IP_VERSION(3, 0, 2): - fw_name_dmub = FIRMWARE_DIMGREY_CAVEFISH_DMUB; - break; - case IP_VERSION(3, 0, 3): - fw_name_dmub = FIRMWARE_BEIGE_GOBY_DMUB; - break; - case IP_VERSION(3, 1, 2): - case IP_VERSION(3, 1, 3): - fw_name_dmub = FIRMWARE_YELLOW_CARP_DMUB; - break; - case IP_VERSION(3, 1, 4): - fw_name_dmub = FIRMWARE_DCN_314_DMUB; - break; - case IP_VERSION(3, 1, 5): - fw_name_dmub = FIRMWARE_DCN_315_DMUB; - break; - case IP_VERSION(3, 1, 6): - fw_name_dmub = FIRMWARE_DCN316_DMUB; - break; - case IP_VERSION(3, 2, 0): - fw_name_dmub = FIRMWARE_DCN_V3_2_0_DMCUB; - break; - case IP_VERSION(3, 2, 1): - fw_name_dmub = FIRMWARE_DCN_V3_2_1_DMCUB; - break; - case IP_VERSION(3, 5, 0): - fw_name_dmub = FIRMWARE_DCN_35_DMUB; - break; - case IP_VERSION(3, 5, 1): - fw_name_dmub = FIRMWARE_DCN_351_DMUB; - break; - case IP_VERSION(3, 6, 0): - fw_name_dmub = FIRMWARE_DCN_36_DMUB; - break; - case IP_VERSION(4, 0, 1): - fw_name_dmub = FIRMWARE_DCN_401_DMUB; - break; - case IP_VERSION(4, 2, 0): - fw_name_dmub = FIRMWARE_DCN_42_DMUB; - break; - case IP_VERSION(4, 2, 1): - fw_name_dmub = FIRMWARE_DCN_42B_DMUB; - break; - default: - /* ASIC doesn't support DMUB. */ - return 0; - } - r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED, - "%s", fw_name_dmub); - return r; -} - static int dm_early_init(struct amdgpu_ip_block *ip_block) { struct amdgpu_device *adev = ip_block->adev; @@ -6323,22 +2977,13 @@ static int dm_early_init(struct amdgpu_ip_block *ip_block) return dm_init_microcode(adev); } -static bool modereset_required(struct drm_crtc_state *crtc_state) +STATIC_IFN_KUNIT bool modereset_required(struct drm_crtc_state *crtc_state) { return !crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state); } +EXPORT_IF_KUNIT(modereset_required); -static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); - kfree(encoder); -} - -static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = { - .destroy = amdgpu_dm_encoder_destroy, -}; - -static int +STATIC_IFN_KUNIT int fill_plane_color_attributes(const struct drm_plane_state *plane_state, const enum surface_pixel_format format, enum dc_color_space *color_space) @@ -6385,6 +3030,7 @@ fill_plane_color_attributes(const struct drm_plane_state *plane_state, return 0; } +EXPORT_IF_KUNIT(fill_plane_color_attributes); static int fill_dc_plane_info_and_addr(struct amdgpu_device *adev, @@ -6620,8 +3266,8 @@ static void fill_dc_dirty_rects(struct drm_plane *plane, { struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state); struct rect *dirty_rects = flip_addrs->dirty_rects; - u32 num_clips; - struct drm_mode_rect *clips; + u32 num_clips = 0; + struct drm_mode_rect *clips = NULL; bool bb_changed; bool fb_changed; u32 i = 0; @@ -6637,8 +3283,10 @@ static void fill_dc_dirty_rects(struct drm_plane *plane, if (new_plane_state->rotation != DRM_MODE_ROTATE_0) goto ffu; - num_clips = drm_plane_get_damage_clips_count(new_plane_state); - clips = drm_plane_get_damage_clips(new_plane_state); + if (!new_plane_state->ignore_damage_clips) { + num_clips = drm_plane_get_damage_clips_count(new_plane_state); + clips = drm_plane_get_damage_clips(new_plane_state); + } if (num_clips && (!amdgpu_damage_clips || (amdgpu_damage_clips < 0 && is_psr_su))) @@ -6721,7 +3369,7 @@ ffu: &flip_addrs->dirty_rect_count, true); } -static void update_stream_scaling_settings(struct drm_device *dev, +void amdgpu_dm_update_stream_scaling_settings(struct drm_device *dev, const struct drm_display_mode *mode, const struct dm_connector_state *dm_state, struct dc_stream_state *stream) @@ -6777,2111 +3425,6 @@ static void update_stream_scaling_settings(struct drm_device *dev, } -static enum dc_color_depth -convert_color_depth_from_display_info(const struct drm_connector *connector, - bool is_y420, int requested_bpc) -{ - u8 bpc; - - if (is_y420) { - bpc = 8; - - /* Cap display bpc based on HDMI 2.0 HF-VSDB */ - if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) - bpc = 16; - else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) - bpc = 12; - else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) - bpc = 10; - } else { - bpc = (uint8_t)connector->display_info.bpc; - /* Assume 8 bpc by default if no bpc is specified. */ - bpc = bpc ? bpc : 8; - } - - if (requested_bpc > 0) { - /* - * Cap display bpc based on the user requested value. - * - * The value for state->max_bpc may not correctly updated - * depending on when the connector gets added to the state - * or if this was called outside of atomic check, so it - * can't be used directly. - */ - bpc = min_t(u8, bpc, requested_bpc); - - /* Round down to the nearest even number. */ - bpc = bpc - (bpc & 1); - } - - switch (bpc) { - case 0: - /* - * Temporary Work around, DRM doesn't parse color depth for - * EDID revision before 1.4 - * TODO: Fix edid parsing - */ - return COLOR_DEPTH_888; - case 6: - return COLOR_DEPTH_666; - case 8: - return COLOR_DEPTH_888; - case 10: - return COLOR_DEPTH_101010; - case 12: - return COLOR_DEPTH_121212; - case 14: - return COLOR_DEPTH_141414; - case 16: - return COLOR_DEPTH_161616; - default: - return COLOR_DEPTH_UNDEFINED; - } -} - -static enum dc_aspect_ratio -get_aspect_ratio(const struct drm_display_mode *mode_in) -{ - /* 1-1 mapping, since both enums follow the HDMI spec. */ - return (enum dc_aspect_ratio) mode_in->picture_aspect_ratio; -} - -static enum dc_color_space -get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing, - const struct drm_connector_state *connector_state) -{ - enum dc_color_space color_space = COLOR_SPACE_SRGB; - - switch (connector_state->colorspace) { - case DRM_MODE_COLORIMETRY_BT601_YCC: - if (dc_crtc_timing->flags.Y_ONLY) - color_space = COLOR_SPACE_YCBCR601_LIMITED; - else - color_space = COLOR_SPACE_YCBCR601; - break; - case DRM_MODE_COLORIMETRY_BT709_YCC: - if (dc_crtc_timing->flags.Y_ONLY) - color_space = COLOR_SPACE_YCBCR709_LIMITED; - else - color_space = COLOR_SPACE_YCBCR709; - break; - case DRM_MODE_COLORIMETRY_OPRGB: - color_space = COLOR_SPACE_ADOBERGB; - break; - case DRM_MODE_COLORIMETRY_BT2020_RGB: - case DRM_MODE_COLORIMETRY_BT2020_YCC: - if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) - color_space = COLOR_SPACE_2020_RGB_FULLRANGE; - else - color_space = COLOR_SPACE_2020_YCBCR_LIMITED; - break; - case DRM_MODE_COLORIMETRY_DEFAULT: // ITU601 - default: - if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) { - color_space = COLOR_SPACE_SRGB; - if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED) - color_space = COLOR_SPACE_SRGB_LIMITED; - /* - * 27030khz is the separation point between HDTV and SDTV - * according to HDMI spec, we use YCbCr709 and YCbCr601 - * respectively - */ - } else if (dc_crtc_timing->pix_clk_100hz > 270300) { - if (dc_crtc_timing->flags.Y_ONLY) - color_space = - COLOR_SPACE_YCBCR709_LIMITED; - else - color_space = COLOR_SPACE_YCBCR709; - } else { - if (dc_crtc_timing->flags.Y_ONLY) - color_space = - COLOR_SPACE_YCBCR601_LIMITED; - else - color_space = COLOR_SPACE_YCBCR601; - } - break; - } - - return color_space; -} - -static enum display_content_type -get_output_content_type(const struct drm_connector_state *connector_state) -{ - switch (connector_state->content_type) { - default: - case DRM_MODE_CONTENT_TYPE_NO_DATA: - return DISPLAY_CONTENT_TYPE_NO_DATA; - case DRM_MODE_CONTENT_TYPE_GRAPHICS: - return DISPLAY_CONTENT_TYPE_GRAPHICS; - case DRM_MODE_CONTENT_TYPE_PHOTO: - return DISPLAY_CONTENT_TYPE_PHOTO; - case DRM_MODE_CONTENT_TYPE_CINEMA: - return DISPLAY_CONTENT_TYPE_CINEMA; - case DRM_MODE_CONTENT_TYPE_GAME: - return DISPLAY_CONTENT_TYPE_GAME; - } -} - -static bool adjust_colour_depth_from_display_info( - struct dc_crtc_timing *timing_out, - const struct drm_display_info *info) -{ - enum dc_color_depth depth = timing_out->display_color_depth; - int normalized_clk; - - do { - normalized_clk = timing_out->pix_clk_100hz / 10; - /* YCbCr 4:2:0 requires additional adjustment of 1/2 */ - if (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420) - normalized_clk /= 2; - /* Adjusting pix clock following on HDMI spec based on colour depth */ - switch (depth) { - case COLOR_DEPTH_888: - break; - case COLOR_DEPTH_101010: - normalized_clk = (normalized_clk * 30) / 24; - break; - case COLOR_DEPTH_121212: - normalized_clk = (normalized_clk * 36) / 24; - break; - case COLOR_DEPTH_161616: - normalized_clk = (normalized_clk * 48) / 24; - break; - default: - /* The above depths are the only ones valid for HDMI. */ - return false; - } - if (normalized_clk <= info->max_tmds_clock) { - timing_out->display_color_depth = depth; - return true; - } - } while (--depth > COLOR_DEPTH_666); - return false; -} - -static void fill_stream_properties_from_drm_display_mode( - struct dc_stream_state *stream, - const struct drm_display_mode *mode_in, - const struct drm_connector *connector, - const struct drm_connector_state *connector_state, - const struct dc_stream_state *old_stream, - int requested_bpc) -{ - bool is_dp_or_hdmi = dc_is_hdmi_signal(stream->signal) || dc_is_dp_signal(stream->signal); - struct dc_crtc_timing *timing_out = &stream->timing; - const struct drm_display_info *info = &connector->display_info; - struct amdgpu_dm_connector *aconnector = NULL; - struct hdmi_vendor_infoframe hv_frame; - struct hdmi_avi_infoframe avi_frame; - bool want_420; - bool want_422; - ssize_t err; - - if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) - aconnector = to_amdgpu_dm_connector(connector); - - memset(&hv_frame, 0, sizeof(hv_frame)); - memset(&avi_frame, 0, sizeof(avi_frame)); - - timing_out->h_border_left = 0; - timing_out->h_border_right = 0; - timing_out->v_border_top = 0; - timing_out->v_border_bottom = 0; - - want_420 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) || - (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420); - want_422 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422) || - (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422); - - if (drm_mode_is_420_only(info, mode_in) && - (want_420 || connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO)) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - } else if (drm_mode_is_420_also(info, mode_in) && want_420) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - } else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) && - want_422 && is_dp_or_hdmi) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422; - } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 && - (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) && - is_dp_or_hdmi) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444; - } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444 || - connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO) { - timing_out->pixel_encoding = PIXEL_ENCODING_RGB; - } else { - /* - * If a format was explicitly requested but the requested format - * can't be satisfied, set it to an invalid value so that an - * error bubbles up to userspace. This way, userspace knows it - * needs to make a better choice. - */ - if (connector_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO) - timing_out->pixel_encoding = PIXEL_ENCODING_UNDEFINED; - else if (drm_mode_is_420_only(info, mode_in)) - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - else - timing_out->pixel_encoding = PIXEL_ENCODING_RGB; - } - - timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE; - timing_out->display_color_depth = convert_color_depth_from_display_info( - connector, - (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420), - requested_bpc); - timing_out->scan_type = SCANNING_TYPE_NODATA; - timing_out->hdmi_vic = 0; - - if (old_stream) { - timing_out->vic = old_stream->timing.vic; - timing_out->flags.HSYNC_POSITIVE_POLARITY = old_stream->timing.flags.HSYNC_POSITIVE_POLARITY; - timing_out->flags.VSYNC_POSITIVE_POLARITY = old_stream->timing.flags.VSYNC_POSITIVE_POLARITY; - } else { - timing_out->vic = drm_match_cea_mode(mode_in); - if (mode_in->flags & DRM_MODE_FLAG_PHSYNC) - timing_out->flags.HSYNC_POSITIVE_POLARITY = 1; - if (mode_in->flags & DRM_MODE_FLAG_PVSYNC) - timing_out->flags.VSYNC_POSITIVE_POLARITY = 1; - } - - if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || - stream->signal == SIGNAL_TYPE_HDMI_FRL) { - err = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, - (struct drm_connector *)connector, - mode_in); - if (err < 0) - drm_warn_once(connector->dev, "Failed to setup avi infoframe on connector %s: %zd\n", - connector->name, err); - timing_out->vic = avi_frame.video_code; - err = drm_hdmi_vendor_infoframe_from_display_mode(&hv_frame, - (struct drm_connector *)connector, - mode_in); - if (err < 0) - drm_warn_once(connector->dev, "Failed to setup vendor infoframe on connector %s: %zd\n", - connector->name, err); - timing_out->hdmi_vic = hv_frame.vic; - } - - if (aconnector && is_freesync_video_mode(mode_in, aconnector)) { - timing_out->h_addressable = mode_in->hdisplay; - timing_out->h_total = mode_in->htotal; - timing_out->h_sync_width = mode_in->hsync_end - mode_in->hsync_start; - timing_out->h_front_porch = mode_in->hsync_start - mode_in->hdisplay; - timing_out->v_total = mode_in->vtotal; - timing_out->v_addressable = mode_in->vdisplay; - timing_out->v_front_porch = mode_in->vsync_start - mode_in->vdisplay; - timing_out->v_sync_width = mode_in->vsync_end - mode_in->vsync_start; - timing_out->pix_clk_100hz = mode_in->clock * 10; - } else { - timing_out->h_addressable = mode_in->crtc_hdisplay; - timing_out->h_total = mode_in->crtc_htotal; - timing_out->h_sync_width = mode_in->crtc_hsync_end - mode_in->crtc_hsync_start; - timing_out->h_front_porch = mode_in->crtc_hsync_start - mode_in->crtc_hdisplay; - timing_out->v_total = mode_in->crtc_vtotal; - timing_out->v_addressable = mode_in->crtc_vdisplay; - timing_out->v_front_porch = mode_in->crtc_vsync_start - mode_in->crtc_vdisplay; - timing_out->v_sync_width = mode_in->crtc_vsync_end - mode_in->crtc_vsync_start; - timing_out->pix_clk_100hz = mode_in->crtc_clock * 10; - } - - timing_out->aspect_ratio = get_aspect_ratio(mode_in); - - stream->out_transfer_func.type = TF_TYPE_PREDEFINED; - stream->out_transfer_func.tf = TRANSFER_FUNCTION_SRGB; - if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) { - if (!adjust_colour_depth_from_display_info(timing_out, info) && - drm_mode_is_420_also(info, mode_in) && - timing_out->pixel_encoding != PIXEL_ENCODING_YCBCR420) { - timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; - adjust_colour_depth_from_display_info(timing_out, info); - } - } - - stream->output_color_space = get_output_color_space(timing_out, connector_state); - stream->content_type = get_output_content_type(connector_state); -} - -static void fill_audio_info(struct audio_info *audio_info, - const struct drm_connector *drm_connector, - const struct dc_sink *dc_sink) -{ - int i = 0; - int cea_revision = 0; - const struct dc_edid_caps *edid_caps = &dc_sink->edid_caps; - - audio_info->manufacture_id = edid_caps->manufacturer_id; - audio_info->product_id = edid_caps->product_id; - - cea_revision = drm_connector->display_info.cea_rev; - - strscpy(audio_info->display_name, - edid_caps->display_name, - AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS); - - if (cea_revision >= 3) { - audio_info->mode_count = edid_caps->audio_mode_count; - - for (i = 0; i < audio_info->mode_count; ++i) { - audio_info->modes[i].format_code = - (enum audio_format_code) - (edid_caps->audio_modes[i].format_code); - audio_info->modes[i].channel_count = - edid_caps->audio_modes[i].channel_count; - audio_info->modes[i].sample_rates.all = - edid_caps->audio_modes[i].sample_rate; - audio_info->modes[i].sample_size = - edid_caps->audio_modes[i].sample_size; - } - } - - audio_info->flags.all = edid_caps->speaker_flags; - - /* TODO: We only check for the progressive mode, check for interlace mode too */ - if (drm_connector->latency_present[0]) { - audio_info->video_latency = drm_connector->video_latency[0]; - audio_info->audio_latency = drm_connector->audio_latency[0]; - } - - /* TODO: For DP, video and audio latency should be calculated from DPCD caps */ - -} - -static void -copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode, - struct drm_display_mode *dst_mode) -{ - dst_mode->crtc_hdisplay = src_mode->crtc_hdisplay; - dst_mode->crtc_vdisplay = src_mode->crtc_vdisplay; - dst_mode->crtc_clock = src_mode->crtc_clock; - dst_mode->crtc_hblank_start = src_mode->crtc_hblank_start; - dst_mode->crtc_hblank_end = src_mode->crtc_hblank_end; - dst_mode->crtc_hsync_start = src_mode->crtc_hsync_start; - dst_mode->crtc_hsync_end = src_mode->crtc_hsync_end; - dst_mode->crtc_htotal = src_mode->crtc_htotal; - dst_mode->crtc_hskew = src_mode->crtc_hskew; - dst_mode->crtc_vblank_start = src_mode->crtc_vblank_start; - dst_mode->crtc_vblank_end = src_mode->crtc_vblank_end; - dst_mode->crtc_vsync_start = src_mode->crtc_vsync_start; - dst_mode->crtc_vsync_end = src_mode->crtc_vsync_end; - dst_mode->crtc_vtotal = src_mode->crtc_vtotal; -} - -static void -decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode, - const struct drm_display_mode *native_mode, - bool scale_enabled) -{ - if (scale_enabled || ( - native_mode->clock == drm_mode->clock && - native_mode->htotal == drm_mode->htotal && - native_mode->vtotal == drm_mode->vtotal)) { - if (native_mode->crtc_clock) - copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode); - } else { - /* no scaling nor amdgpu inserted, no need to patch */ - } -} - -static struct dc_sink * -create_fake_sink(struct drm_device *dev, struct dc_link *link) -{ - struct dc_sink_init_data sink_init_data = { 0 }; - struct dc_sink *sink = NULL; - - sink_init_data.link = link; - sink_init_data.sink_signal = link->connector_signal; - - sink = dc_sink_create(&sink_init_data); - if (!sink) { - drm_err(dev, "Failed to create sink!\n"); - return NULL; - } - sink->sink_signal = SIGNAL_TYPE_VIRTUAL; - - return sink; -} - -static void set_multisync_trigger_params( - struct dc_stream_state *stream) -{ - struct dc_stream_state *master = NULL; - - if (stream->triggered_crtc_reset.enabled) { - master = stream->triggered_crtc_reset.event_source; - stream->triggered_crtc_reset.event = - master->timing.flags.VSYNC_POSITIVE_POLARITY ? - CRTC_EVENT_VSYNC_RISING : CRTC_EVENT_VSYNC_FALLING; - stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL; - } -} - -static void set_master_stream(struct dc_stream_state *stream_set[], - int stream_count) -{ - int j, highest_rfr = 0, master_stream = 0; - - for (j = 0; j < stream_count; j++) { - if (stream_set[j] && stream_set[j]->triggered_crtc_reset.enabled) { - int refresh_rate = 0; - - refresh_rate = (stream_set[j]->timing.pix_clk_100hz*100)/ - (stream_set[j]->timing.h_total*stream_set[j]->timing.v_total); - if (refresh_rate > highest_rfr) { - highest_rfr = refresh_rate; - master_stream = j; - } - } - } - for (j = 0; j < stream_count; j++) { - if (stream_set[j]) - stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream]; - } -} - -static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context) -{ - int i = 0; - struct dc_stream_state *stream; - - if (context->stream_count < 2) - return; - for (i = 0; i < context->stream_count ; i++) { - if (!context->streams[i]) - continue; - /* - * TODO: add a function to read AMD VSDB bits and set - * crtc_sync_master.multi_sync_enabled flag - * For now it's set to false - */ - } - - set_master_stream(context->streams, context->stream_count); - - for (i = 0; i < context->stream_count ; i++) { - stream = context->streams[i]; - - if (!stream) - continue; - - set_multisync_trigger_params(stream); - } -} - -/** - * DOC: FreeSync Video - * - * When a userspace application wants to play a video, the content follows a - * standard format definition that usually specifies the FPS for that format. - * The below list illustrates some video format and the expected FPS, - * respectively: - * - * - TV/NTSC (23.976 FPS) - * - Cinema (24 FPS) - * - TV/PAL (25 FPS) - * - TV/NTSC (29.97 FPS) - * - TV/NTSC (30 FPS) - * - Cinema HFR (48 FPS) - * - TV/PAL (50 FPS) - * - Commonly used (60 FPS) - * - Multiples of 24 (48,72,96 FPS) - * - * The list of standards video format is not huge and can be added to the - * connector modeset list beforehand. With that, userspace can leverage - * FreeSync to extends the front porch in order to attain the target refresh - * rate. Such a switch will happen seamlessly, without screen blanking or - * reprogramming of the output in any other way. If the userspace requests a - * modesetting change compatible with FreeSync modes that only differ in the - * refresh rate, DC will skip the full update and avoid blink during the - * transition. For example, the video player can change the modesetting from - * 60Hz to 30Hz for playing TV/NTSC content when it goes full screen without - * causing any display blink. This same concept can be applied to a mode - * setting change. - */ -static struct drm_display_mode * -get_highest_refresh_rate_mode(struct amdgpu_dm_connector *aconnector, - bool use_probed_modes) -{ - struct drm_display_mode *m, *m_pref = NULL; - u16 current_refresh, highest_refresh; - struct list_head *list_head = use_probed_modes ? - &aconnector->base.probed_modes : - &aconnector->base.modes; - - if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - return NULL; - - if (aconnector->freesync_vid_base.clock != 0) - return &aconnector->freesync_vid_base; - - /* Find the preferred mode */ - list_for_each_entry(m, list_head, head) { - if (m->type & DRM_MODE_TYPE_PREFERRED) { - m_pref = m; - break; - } - } - - if (!m_pref) { - /* Probably an EDID with no preferred mode. Fallback to first entry */ - m_pref = list_first_entry_or_null( - &aconnector->base.modes, struct drm_display_mode, head); - if (!m_pref) { - drm_dbg_driver(aconnector->base.dev, "No preferred mode found in EDID\n"); - return NULL; - } - } - - highest_refresh = drm_mode_vrefresh(m_pref); - - /* - * Find the mode with highest refresh rate with same resolution. - * For some monitors, preferred mode is not the mode with highest - * supported refresh rate. - */ - list_for_each_entry(m, list_head, head) { - current_refresh = drm_mode_vrefresh(m); - - if (m->hdisplay == m_pref->hdisplay && - m->vdisplay == m_pref->vdisplay && - highest_refresh < current_refresh) { - highest_refresh = current_refresh; - m_pref = m; - } - } - - drm_mode_copy(&aconnector->freesync_vid_base, m_pref); - return m_pref; -} - -static bool is_freesync_video_mode(const struct drm_display_mode *mode, - struct amdgpu_dm_connector *aconnector) -{ - struct drm_display_mode *high_mode; - int timing_diff; - - high_mode = get_highest_refresh_rate_mode(aconnector, false); - if (!high_mode || !mode) - return false; - - timing_diff = high_mode->vtotal - mode->vtotal; - - if (high_mode->clock == 0 || high_mode->clock != mode->clock || - high_mode->hdisplay != mode->hdisplay || - high_mode->vdisplay != mode->vdisplay || - high_mode->hsync_start != mode->hsync_start || - high_mode->hsync_end != mode->hsync_end || - high_mode->htotal != mode->htotal || - high_mode->hskew != mode->hskew || - high_mode->vscan != mode->vscan || - high_mode->vsync_start - mode->vsync_start != timing_diff || - high_mode->vsync_end - mode->vsync_end != timing_diff) - return false; - else - return true; -} - -#if defined(CONFIG_DRM_AMD_DC_FP) -static void update_dsc_caps(struct amdgpu_dm_connector *aconnector, - struct dc_sink *sink, struct dc_stream_state *stream, - struct dsc_dec_dpcd_caps *dsc_caps) -{ - stream->timing.flags.DSC = 0; - dsc_caps->is_dsc_supported = false; - - if (aconnector->dc_link && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || - sink->sink_signal == SIGNAL_TYPE_EDP)) { - if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE) - dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc, - aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw, - aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw, - dsc_caps); - else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) { - if (aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT && - !aconnector->dsc_settings.dsc_force_disable_passthrough && - aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps > 0 && - sink->edid_caps.frl_dsc_support && - sink->edid_caps.max_frl_rate > 0 && - sink->edid_caps.frl_dsc_max_frl_rate > 0) - dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps); - else - dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc, - aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw, - aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw, - dsc_caps); - } - } else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) { - if (sink->edid_caps.frl_dsc_support && - sink->edid_caps.max_frl_rate > 0 && - sink->edid_caps.frl_dsc_max_frl_rate > 0) - dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps); - } -} - -static void apply_dsc_policy_for_edp(struct amdgpu_dm_connector *aconnector, - struct dc_sink *sink, struct dc_stream_state *stream, - struct dsc_dec_dpcd_caps *dsc_caps, - uint32_t max_dsc_target_bpp_limit_override) -{ - const struct dc_link_settings *verified_link_cap = NULL; - u32 link_bw_in_kbps; - u32 edp_min_bpp_x16, edp_max_bpp_x16; - struct dc *dc = sink->ctx->dc; - struct dc_dsc_bw_range bw_range = {0}; - struct dc_dsc_config dsc_cfg = {0}; - struct dc_dsc_config_options dsc_options = {0}; - - dc_dsc_get_default_config_option(dc, &dsc_options); - dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16; - - verified_link_cap = dc_link_get_link_cap(stream->link); - link_bw_in_kbps = dc_link_bandwidth_kbps(stream->link, verified_link_cap); - edp_min_bpp_x16 = 8 * 16; - edp_max_bpp_x16 = 8 * 16; - - if (edp_max_bpp_x16 > dsc_caps->edp_max_bits_per_pixel) - edp_max_bpp_x16 = dsc_caps->edp_max_bits_per_pixel; - - if (edp_max_bpp_x16 < edp_min_bpp_x16) - edp_min_bpp_x16 = edp_max_bpp_x16; - - if (dc_dsc_compute_bandwidth_range(dc->res_pool->dscs[0], - dc->debug.dsc_min_slice_height_override, - edp_min_bpp_x16, edp_max_bpp_x16, - dsc_caps, - &stream->timing, - dc_link_get_highest_encoding_format(aconnector->dc_link), - &bw_range)) { - - if (bw_range.max_kbps < link_bw_in_kbps) { - if (dc_dsc_compute_config(dc->res_pool->dscs[0], - dsc_caps, - &dsc_options, - 0, - &stream->timing, - dc_link_get_highest_encoding_format(aconnector->dc_link), - &dsc_cfg)) { - stream->timing.dsc_cfg = dsc_cfg; - stream->timing.flags.DSC = 1; - stream->timing.dsc_cfg.bits_per_pixel = edp_max_bpp_x16; - } - return; - } - } - - if (dc_dsc_compute_config(dc->res_pool->dscs[0], - dsc_caps, - &dsc_options, - link_bw_in_kbps, - &stream->timing, - dc_link_get_highest_encoding_format(aconnector->dc_link), - &dsc_cfg)) { - stream->timing.dsc_cfg = dsc_cfg; - stream->timing.flags.DSC = 1; - } -} - -static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector, - struct dc_sink *sink, struct dc_stream_state *stream, - struct dsc_dec_dpcd_caps *dsc_caps) -{ - struct drm_connector *drm_connector = &aconnector->base; - u32 link_bandwidth_kbps; - struct dc *dc = sink->ctx->dc; - const struct dc_hdmi_frl_link_settings *frl_verified_link_cap = NULL; - u32 converter_bw_in_kbps; - u32 sink_bw_in_kbps; - u32 dsc_sink_bw_in_kbps; - u32 max_supported_bw_in_kbps, timing_bw_in_kbps; - u32 dsc_max_supported_bw_in_kbps; - u32 max_dsc_target_bpp_limit_override = - drm_connector->display_info.max_dsc_bpp; - struct dc_dsc_config_options dsc_options = {0}; - - dc_dsc_get_default_config_option(dc, &dsc_options); - dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16; - - link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link, - dc_link_get_link_cap(aconnector->dc_link)); - - /* Set DSC policy according to dsc_clock_en */ - dc_dsc_policy_set_enable_dsc_when_not_needed( - aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE); - - if (sink->sink_signal == SIGNAL_TYPE_EDP && - !aconnector->dc_link->panel_config.dsc.disable_dsc_edp && - dc->caps.edp_dsc_support && aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE) { - - apply_dsc_policy_for_edp(aconnector, sink, stream, dsc_caps, max_dsc_target_bpp_limit_override); - - } else if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) { - if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE) { - if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0], - dsc_caps, - &dsc_options, - link_bandwidth_kbps, - &stream->timing, - dc_link_get_highest_encoding_format(aconnector->dc_link), - &stream->timing.dsc_cfg)) { - stream->timing.flags.DSC = 1; - drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from SST RX\n", - __func__, drm_connector->name); - } - } else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) { - timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing, - dc_link_get_highest_encoding_format(aconnector->dc_link)); - converter_bw_in_kbps = aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps; - sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.max_frl_rate); - dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate); - - if (dsc_caps->is_frl) { - max_supported_bw_in_kbps = min(link_bandwidth_kbps, converter_bw_in_kbps); - max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, sink_bw_in_kbps); - dsc_max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, dsc_sink_bw_in_kbps); - } else { - max_supported_bw_in_kbps = link_bandwidth_kbps; - dsc_max_supported_bw_in_kbps = link_bandwidth_kbps; - } - - if (timing_bw_in_kbps > max_supported_bw_in_kbps && - max_supported_bw_in_kbps > 0 && - dsc_max_supported_bw_in_kbps > 0) - if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0], - dsc_caps, - &dsc_options, - dsc_max_supported_bw_in_kbps, - &stream->timing, - dc_link_get_highest_encoding_format(aconnector->dc_link), - &stream->timing.dsc_cfg)) { - stream->timing.flags.DSC = 1; - drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from %s\n", - __func__, drm_connector->name, - (dsc_caps->is_frl == 1) ? "HDMI FRL RX" : "DP-HDMI PCON"); - } - } - } - else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) { - struct dc_dsc_policy dsc_policy = {0}; - - frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link); - if (frl_verified_link_cap->frl_link_rate != HDMI_FRL_LINK_RATE_DISABLE && - aconnector->dc_link->frl_flags.force_frl_dsc) { - dc_dsc_policy_set_enable_dsc_when_not_needed(true); - dc_dsc_get_policy_for_timing(&stream->timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link)); - } - - timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing, DC_LINK_ENCODING_HDMI_FRL); - link_bandwidth_kbps = dc_link_frl_bandwidth_kbps(stream->link, frl_verified_link_cap->frl_link_rate); - dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate); - - if ((timing_bw_in_kbps > link_bandwidth_kbps && dsc_sink_bw_in_kbps > 0) || - (dsc_policy.enable_dsc_when_not_needed || dsc_options.force_dsc_when_not_needed)) { - if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0], - dsc_caps, - &dsc_options, - dsc_sink_bw_in_kbps, - &stream->timing, - dc_link_get_highest_encoding_format(aconnector->dc_link), - &stream->timing.dsc_cfg)) { - stream->timing.flags.DSC = 1; - drm_dbg_driver(drm_connector->dev, "%s: HDMI_FRL_DSC [%s] DSC is selected from HDMI FRL RX\n", - __func__, drm_connector->name); - } - } - } - - /* Overwrite the stream flag if DSC is enabled through debugfs */ - if (aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE) - stream->timing.flags.DSC = 1; - - if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_h) - stream->timing.dsc_cfg.num_slices_h = aconnector->dsc_settings.dsc_num_slices_h; - - if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_v) - stream->timing.dsc_cfg.num_slices_v = aconnector->dsc_settings.dsc_num_slices_v; - - if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_bits_per_pixel) - stream->timing.dsc_cfg.bits_per_pixel = aconnector->dsc_settings.dsc_bits_per_pixel; -} -#endif - -static struct dc_stream_state * -create_stream_for_sink(struct drm_connector *connector, - const struct drm_display_mode *drm_mode, - const struct dm_connector_state *dm_state, - const struct dc_stream_state *old_stream, - int requested_bpc) -{ - struct drm_device *dev = connector->dev; - struct amdgpu_dm_connector *aconnector = NULL; - struct drm_display_mode *preferred_mode = NULL; - const struct drm_connector_state *con_state = &dm_state->base; - struct dc_stream_state *stream = NULL; - struct drm_display_mode mode; - struct drm_display_mode saved_mode; - struct drm_display_mode *freesync_mode = NULL; - bool native_mode_found = false; - bool recalculate_timing = false; - bool scale = dm_state->scaling != RMX_OFF; - int mode_refresh; - 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 = {0}; -#endif - struct dc_link *link = NULL; - struct dc_sink *sink = NULL; - - drm_mode_init(&mode, drm_mode); - memset(&saved_mode, 0, sizeof(saved_mode)); - - if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) { - aconnector = NULL; - aconnector = to_amdgpu_dm_connector(connector); - link = aconnector->dc_link; - } else { - struct drm_writeback_connector *wbcon = NULL; - struct amdgpu_dm_wb_connector *dm_wbcon = NULL; - - wbcon = drm_connector_to_writeback(connector); - dm_wbcon = to_amdgpu_dm_wb_connector(wbcon); - link = dm_wbcon->link; - } - - if (!aconnector || !aconnector->dc_sink) { - sink = create_fake_sink(dev, link); - if (!sink) - return stream; - - } else { - sink = aconnector->dc_sink; - dc_sink_retain(sink); - } - - stream = dc_create_stream_for_sink(sink); - - if (stream == NULL) { - drm_err(dev, "Failed to create stream for sink!\n"); - goto finish; - } - - /* We leave this NULL for writeback connectors */ - stream->dm_stream_context = aconnector; - - stream->timing.flags.LTE_340MCSC_SCRAMBLE = - connector->display_info.hdmi.scdc.scrambling.low_rates; - - list_for_each_entry(preferred_mode, &connector->modes, head) { - /* Search for preferred mode */ - if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) { - native_mode_found = true; - break; - } - } - if (!native_mode_found) - preferred_mode = list_first_entry_or_null( - &connector->modes, - struct drm_display_mode, - head); - - mode_refresh = drm_mode_vrefresh(&mode); - - if (preferred_mode == NULL) { - /* - * This may not be an error, the use case is when we have no - * usermode calls to reset and set mode upon hotplug. In this - * case, we call set mode ourselves to restore the previous mode - * and the modelist may not be filled in time. - */ - drm_dbg_driver(dev, "No preferred mode found\n"); - } else if (aconnector) { - recalculate_timing = amdgpu_freesync_vid_mode && - is_freesync_video_mode(&mode, aconnector); - if (recalculate_timing) { - freesync_mode = get_highest_refresh_rate_mode(aconnector, false); - drm_mode_copy(&saved_mode, &mode); - saved_mode.picture_aspect_ratio = mode.picture_aspect_ratio; - drm_mode_copy(&mode, freesync_mode); - mode.picture_aspect_ratio = saved_mode.picture_aspect_ratio; - } else { - decide_crtc_timing_for_drm_display_mode( - &mode, preferred_mode, scale); - - preferred_refresh = drm_mode_vrefresh(preferred_mode); - } - } - - if (recalculate_timing) - drm_mode_set_crtcinfo(&saved_mode, 0); - - /* - * If scaling is enabled and refresh rate didn't change - * we copy the vic and polarities of the old timings - */ - if (!scale || mode_refresh != preferred_refresh) - fill_stream_properties_from_drm_display_mode( - stream, &mode, connector, con_state, NULL, - requested_bpc); - else - fill_stream_properties_from_drm_display_mode( - stream, &mode, connector, con_state, old_stream, - requested_bpc); - - /* The rest isn't needed for writeback connectors */ - if (!aconnector) - goto finish; - - if (aconnector->timing_changed) { - drm_dbg(aconnector->base.dev, - "overriding timing for automated test, bpc %d, changing to %d\n", - stream->timing.display_color_depth, - aconnector->timing_requested->display_color_depth); - stream->timing = *aconnector->timing_requested; - } - -#if defined(CONFIG_DRM_AMD_DC_FP) - /* SST DSC determination policy */ - update_dsc_caps(aconnector, sink, stream, &dsc_caps); - if (aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE && dsc_caps.is_dsc_supported) - apply_dsc_policy_for_stream(aconnector, sink, stream, &dsc_caps); -#endif - - update_stream_scaling_settings(dev, &mode, dm_state, stream); - - fill_audio_info( - &stream->audio_info, - connector, - sink); - - update_stream_signal(stream, sink); - - if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || - stream->signal == SIGNAL_TYPE_HDMI_FRL) - mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket, false, false); - - if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT || - stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST || - stream->signal == SIGNAL_TYPE_EDP) { - const struct dc_edid_caps *edid_caps; - unsigned int disable_colorimetry = 0; - - if (aconnector->dc_sink) { - edid_caps = &aconnector->dc_sink->edid_caps; - disable_colorimetry = edid_caps->panel_patch.disable_colorimetry; - } - - // - // should decide stream support vsc sdp colorimetry capability - // before building vsc info packet - // - stream->use_vsc_sdp_for_colorimetry = stream->link->dpcd_caps.dpcd_rev.raw >= 0x14 && - stream->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED && - !disable_colorimetry; - - if (stream->out_transfer_func.tf == TRANSFER_FUNCTION_GAMMA22) - tf = TRANSFER_FUNC_GAMMA_22; - mod_build_vsc_infopacket(stream, &stream->vsc_infopacket, stream->output_color_space, tf); - aconnector->sr_skip_count = AMDGPU_DM_PSR_ENTRY_DELAY; - - } -finish: - dc_sink_release(sink); - - return stream; -} - -/** - * amdgpu_dm_connector_poll - Poll a connector to see if it's connected to a display - * @aconnector: DM connector to poll (owns @base drm_connector and @dc_link) - * @force: if true, force polling even when DAC load detection was used - * - * Used for connectors that don't support HPD (hotplug detection) to - * periodically check whether the connector is connected to a display. - * - * When connection was determined via DAC load detection, we avoid - * re-running it on normal polls to prevent visible glitches, unless - * @force is set. - * - * Return: The probed connector status (connected/disconnected/unknown). - */ -static enum drm_connector_status -amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force) -{ - struct drm_connector *connector = &aconnector->base; - struct drm_device *dev = connector->dev; - struct amdgpu_device *adev = drm_to_adev(dev); - struct dc_link *link = aconnector->dc_link; - enum dc_connection_type conn_type = dc_connection_none; - enum drm_connector_status status = connector_status_disconnected; - - /* When we determined the connection using DAC load detection, - * do NOT poll the connector do detect disconnect because - * that would run DAC load detection again which can cause - * visible visual glitches. - * - * Only allow to poll such a connector again when forcing. - */ - if (!force && link->local_sink && link->type == dc_connection_analog_load) - return connector->status; - - mutex_lock(&aconnector->hpd_lock); - - if (dc_link_detect_connection_type(aconnector->dc_link, &conn_type) && - conn_type != dc_connection_none) { - mutex_lock(&adev->dm.dc_lock); - - /* Only call full link detection when a sink isn't created yet, - * ie. just when the display is plugged in, otherwise we risk flickering. - */ - if (link->local_sink || - dc_link_detect(link, DETECT_REASON_HPD)) - status = connector_status_connected; - - mutex_unlock(&adev->dm.dc_lock); - } - - if (connector->status != status) { - if (status == connector_status_disconnected) { - if (link->local_sink) - dc_sink_release(link->local_sink); - - link->local_sink = NULL; - link->dpcd_sink_count = 0; - link->type = dc_connection_none; - } - - amdgpu_dm_update_connector_after_detect(aconnector); - } - - mutex_unlock(&aconnector->hpd_lock); - return status; -} - -/** - * amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display - * - * A connector is considered connected when it has a sink that is not NULL. - * For connectors that support HPD (hotplug detection), the connection is - * handled in the HPD interrupt. - * For connectors that may not support HPD, such as analog connectors, - * DRM will call this function repeatedly to poll them. - * - * Notes: - * 1. This interface is NOT called in context of HPD irq. - * 2. This interface *is called* in context of user-mode ioctl. Which - * makes it a bad place for *any* MST-related activity. - * - * @connector: The DRM connector we are checking. We convert it to - * amdgpu_dm_connector so we can read the DC link and state. - * @force: If true, do a full detect again. This is used even when - * a lighter check would normally be used to avoid flicker. - * - * Return: The connector status (connected, disconnected, or unknown). - * - */ -static enum drm_connector_status -amdgpu_dm_connector_detect(struct drm_connector *connector, bool force) -{ - struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); - - update_subconnector_property(aconnector); - - if (aconnector->base.force == DRM_FORCE_ON || - aconnector->base.force == DRM_FORCE_ON_DIGITAL) - return connector_status_connected; - else if (aconnector->base.force == DRM_FORCE_OFF) - return connector_status_disconnected; - - /* Poll analog connectors and only when either - * disconnected or connected to an analog display. - */ - if (drm_kms_helper_is_poll_worker() && - dc_connector_supports_analog(aconnector->dc_link->link_id.id) && - (!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog)) - return amdgpu_dm_connector_poll(aconnector, force); - - return (aconnector->dc_sink ? connector_status_connected : - connector_status_disconnected); -} - -int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector, - struct drm_connector_state *connector_state, - struct drm_property *property, - uint64_t val) -{ - struct drm_device *dev = connector->dev; - struct amdgpu_device *adev = drm_to_adev(dev); - struct dm_connector_state *dm_old_state = - to_dm_connector_state(connector->state); - struct dm_connector_state *dm_new_state = - to_dm_connector_state(connector_state); - - int ret = -EINVAL; - - if (property == dev->mode_config.scaling_mode_property) { - enum amdgpu_rmx_type rmx_type; - - switch (val) { - case DRM_MODE_SCALE_CENTER: - rmx_type = RMX_CENTER; - break; - case DRM_MODE_SCALE_ASPECT: - rmx_type = RMX_ASPECT; - break; - case DRM_MODE_SCALE_FULLSCREEN: - rmx_type = RMX_FULL; - break; - case DRM_MODE_SCALE_NONE: - default: - rmx_type = RMX_OFF; - break; - } - - if (dm_old_state->scaling == rmx_type) - return 0; - - dm_new_state->scaling = rmx_type; - ret = 0; - } else if (property == adev->mode_info.underscan_hborder_property) { - dm_new_state->underscan_hborder = val; - ret = 0; - } else if (property == adev->mode_info.underscan_vborder_property) { - dm_new_state->underscan_vborder = val; - ret = 0; - } else if (property == adev->mode_info.underscan_property) { - dm_new_state->underscan_enable = val; - ret = 0; - } else if (property == adev->mode_info.abm_level_property) { - switch (val) { - case ABM_SYSFS_CONTROL: - dm_new_state->abm_sysfs_forbidden = false; - break; - case ABM_LEVEL_OFF: - dm_new_state->abm_sysfs_forbidden = true; - dm_new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE; - break; - default: - dm_new_state->abm_sysfs_forbidden = true; - dm_new_state->abm_level = val; - } - ret = 0; - } - - return ret; -} - -int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector, - const struct drm_connector_state *state, - struct drm_property *property, - uint64_t *val) -{ - struct drm_device *dev = connector->dev; - struct amdgpu_device *adev = drm_to_adev(dev); - struct dm_connector_state *dm_state = - to_dm_connector_state(state); - int ret = -EINVAL; - - if (property == dev->mode_config.scaling_mode_property) { - switch (dm_state->scaling) { - case RMX_CENTER: - *val = DRM_MODE_SCALE_CENTER; - break; - case RMX_ASPECT: - *val = DRM_MODE_SCALE_ASPECT; - break; - case RMX_FULL: - *val = DRM_MODE_SCALE_FULLSCREEN; - break; - case RMX_OFF: - default: - *val = DRM_MODE_SCALE_NONE; - break; - } - ret = 0; - } else if (property == adev->mode_info.underscan_hborder_property) { - *val = dm_state->underscan_hborder; - ret = 0; - } else if (property == adev->mode_info.underscan_vborder_property) { - *val = dm_state->underscan_vborder; - ret = 0; - } else if (property == adev->mode_info.underscan_property) { - *val = dm_state->underscan_enable; - ret = 0; - } else if (property == adev->mode_info.abm_level_property) { - if (!dm_state->abm_sysfs_forbidden) - *val = ABM_SYSFS_CONTROL; - else - *val = (dm_state->abm_level != ABM_LEVEL_IMMEDIATE_DISABLE) ? - dm_state->abm_level : 0; - ret = 0; - } - - return ret; -} - -/** - * DOC: panel power savings - * - * The display manager allows you to set your desired **panel power savings** - * level (between 0-4, with 0 representing off), e.g. using the following:: - * - * # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings - * - * Modifying this value can have implications on color accuracy, so tread - * carefully. - */ - -static ssize_t panel_power_savings_show(struct device *device, - struct device_attribute *attr, - char *buf) -{ - struct drm_connector *connector = dev_get_drvdata(device); - struct drm_device *dev = connector->dev; - u8 val; - - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - val = to_dm_connector_state(connector->state)->abm_level == - ABM_LEVEL_IMMEDIATE_DISABLE ? 0 : - to_dm_connector_state(connector->state)->abm_level; - drm_modeset_unlock(&dev->mode_config.connection_mutex); - - return sysfs_emit(buf, "%u\n", val); -} - -static ssize_t panel_power_savings_store(struct device *device, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct drm_connector *connector = dev_get_drvdata(device); - struct drm_device *dev = connector->dev; - long val; - int ret; - - ret = kstrtol(buf, 0, &val); - - if (ret) - return ret; - - if (val < 0 || val > 4) - return -EINVAL; - - drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); - if (to_dm_connector_state(connector->state)->abm_sysfs_forbidden) - ret = -EBUSY; - else - to_dm_connector_state(connector->state)->abm_level = val ?: - ABM_LEVEL_IMMEDIATE_DISABLE; - drm_modeset_unlock(&dev->mode_config.connection_mutex); - - if (ret) - return ret; - - drm_kms_helper_hotplug_event(dev); - - return count; -} - -static DEVICE_ATTR_RW(panel_power_savings); - -static struct attribute *amdgpu_attrs[] = { - &dev_attr_panel_power_savings.attr, - NULL -}; - -static const struct attribute_group amdgpu_group = { - .name = "amdgpu", - .attrs = amdgpu_attrs -}; - -static bool -amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector) -{ - if (amdgpu_dm_abm_level >= 0) - return false; - - if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) - return false; - - /* check for OLED panels */ - if (amdgpu_dm_connector->bl_idx >= 0) { - struct drm_device *drm = amdgpu_dm_connector->base.dev; - struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm; - struct amdgpu_dm_backlight_caps *caps; - - caps = &dm->backlight_caps[amdgpu_dm_connector->bl_idx]; - if (caps->aux_support) - return false; - } - - return true; -} - -static void amdgpu_dm_connector_unregister(struct drm_connector *connector) -{ - struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); - - if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector)) - sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group); - - cec_notifier_conn_unregister(amdgpu_dm_connector->notifier); - drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux); -} - -static void amdgpu_dm_connector_destroy(struct drm_connector *connector) -{ - struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); - struct amdgpu_device *adev = drm_to_adev(connector->dev); - struct amdgpu_display_manager *dm = &adev->dm; - - /* - * Call only if mst_mgr was initialized before since it's not done - * for all connector types. - */ - if (aconnector->mst_mgr.dev) - drm_dp_mst_topology_mgr_destroy(&aconnector->mst_mgr); - - /* Cancel and flush any pending HDMI HPD debounce work */ - if (aconnector->hdmi_hpd_debounce_delay_ms) { - cancel_delayed_work_sync(&aconnector->hdmi_hpd_debounce_work); - if (aconnector->hdmi_prev_sink) { - dc_sink_release(aconnector->hdmi_prev_sink); - aconnector->hdmi_prev_sink = NULL; - } - } - - if (aconnector->bl_idx != -1) { - backlight_device_unregister(dm->backlight_dev[aconnector->bl_idx]); - dm->backlight_dev[aconnector->bl_idx] = NULL; - } - - if (aconnector->dc_em_sink) - dc_sink_release(aconnector->dc_em_sink); - aconnector->dc_em_sink = NULL; - if (aconnector->dc_sink) - dc_sink_release(aconnector->dc_sink); - aconnector->dc_sink = NULL; - - drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux); - drm_connector_unregister(connector); - drm_connector_cleanup(connector); - kfree(aconnector->dm_dp_aux.aux.name); - - kfree(connector); -} - -void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector) -{ - struct dm_connector_state *state = - to_dm_connector_state(connector->state); - - if (connector->state) - __drm_atomic_helper_connector_destroy_state(connector->state); - - kfree(state); - - state = kzalloc_obj(*state); - - if (state) { - state->scaling = RMX_OFF; - state->underscan_enable = false; - state->underscan_hborder = 0; - state->underscan_vborder = 0; - state->base.max_requested_bpc = 8; - state->vcpi_slots = 0; - state->pbn = 0; - - if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { - if (amdgpu_dm_abm_level <= 0) - state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE; - else - state->abm_level = amdgpu_dm_abm_level; - } - - __drm_atomic_helper_connector_reset(connector, &state->base); - } -} - -struct drm_connector_state * -amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector) -{ - struct dm_connector_state *state = - to_dm_connector_state(connector->state); - - struct dm_connector_state *new_state = - kmemdup(state, sizeof(*state), GFP_KERNEL); - - if (!new_state) - return NULL; - - __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base); - - new_state->freesync_capable = state->freesync_capable; - new_state->abm_level = state->abm_level; - new_state->scaling = state->scaling; - new_state->underscan_enable = state->underscan_enable; - new_state->underscan_hborder = state->underscan_hborder; - new_state->underscan_vborder = state->underscan_vborder; - new_state->vcpi_slots = state->vcpi_slots; - new_state->pbn = state->pbn; - return &new_state->base; -} - -static int -amdgpu_dm_connector_late_register(struct drm_connector *connector) -{ - struct amdgpu_dm_connector *amdgpu_dm_connector = - to_amdgpu_dm_connector(connector); - int r; - - if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector)) { - r = sysfs_create_group(&connector->kdev->kobj, - &amdgpu_group); - if (r) - return r; - } - - amdgpu_dm_register_backlight_device(amdgpu_dm_connector); - - if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) || - (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) { - amdgpu_dm_connector->dm_dp_aux.aux.dev = connector->kdev; - r = drm_dp_aux_register(&amdgpu_dm_connector->dm_dp_aux.aux); - if (r) - return r; - } - -#if defined(CONFIG_DEBUG_FS) - connector_debugfs_init(amdgpu_dm_connector); -#endif - - return 0; -} - -static void amdgpu_dm_connector_funcs_force(struct drm_connector *connector) -{ - struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); - struct dc_link *dc_link = aconnector->dc_link; - struct dc_sink *dc_em_sink = aconnector->dc_em_sink; - const struct drm_edid *drm_edid; - struct i2c_adapter *ddc; - struct drm_device *dev = connector->dev; - - if (dc_link && dc_link->aux_mode) - ddc = &aconnector->dm_dp_aux.aux.ddc; - else - ddc = &aconnector->i2c->base; - - drm_edid = drm_edid_read_ddc(connector, ddc); - drm_edid_connector_update(connector, drm_edid); - if (!drm_edid) { - drm_err(dev, "No EDID found on connector: %s.\n", connector->name); - return; - } - - aconnector->drm_edid = drm_edid; - /* Update emulated (virtual) sink's EDID */ - if (dc_em_sink && dc_link) { - // FIXME: Get rid of drm_edid_raw() - const struct edid *edid = drm_edid_raw(drm_edid); - - memset(&dc_em_sink->edid_caps, 0, sizeof(struct dc_edid_caps)); - memmove(dc_em_sink->dc_edid.raw_edid, edid, - (edid->extensions + 1) * EDID_LENGTH); - dm_helpers_parse_edid_caps( - dc_link, - &dc_em_sink->dc_edid, - &dc_em_sink->edid_caps); - } -} - -static const struct drm_connector_funcs amdgpu_dm_connector_funcs = { - .reset = amdgpu_dm_connector_funcs_reset, - .detect = amdgpu_dm_connector_detect, - .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = amdgpu_dm_connector_destroy, - .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, - .atomic_set_property = amdgpu_dm_connector_atomic_set_property, - .atomic_get_property = amdgpu_dm_connector_atomic_get_property, - .late_register = amdgpu_dm_connector_late_register, - .early_unregister = amdgpu_dm_connector_unregister, - .force = amdgpu_dm_connector_funcs_force -}; - -static int get_modes(struct drm_connector *connector) -{ - return amdgpu_dm_connector_get_modes(connector); -} - -static void create_eml_sink(struct amdgpu_dm_connector *aconnector) -{ - struct drm_connector *connector = &aconnector->base; - struct dc_link *dc_link = aconnector->dc_link; - struct dc_sink_init_data init_params = { - .link = aconnector->dc_link, - .sink_signal = SIGNAL_TYPE_VIRTUAL - }; - const struct drm_edid *drm_edid; - const struct edid *edid; - struct i2c_adapter *ddc; - - if (dc_link && dc_link->aux_mode) - ddc = &aconnector->dm_dp_aux.aux.ddc; - else - ddc = &aconnector->i2c->base; - - drm_edid = drm_edid_read_ddc(connector, ddc); - drm_edid_connector_update(connector, drm_edid); - if (!drm_edid) { - drm_err(connector->dev, "No EDID found on connector: %s.\n", connector->name); - return; - } - - if (connector->display_info.is_hdmi) - init_params.sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; - - aconnector->drm_edid = drm_edid; - - edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw() - aconnector->dc_em_sink = dc_link_add_remote_sink( - aconnector->dc_link, - (uint8_t *)edid, - (edid->extensions + 1) * EDID_LENGTH, - &init_params); - - if (aconnector->base.force == DRM_FORCE_ON) { - aconnector->dc_sink = aconnector->dc_link->local_sink ? - aconnector->dc_link->local_sink : - aconnector->dc_em_sink; - if (aconnector->dc_sink) - dc_sink_retain(aconnector->dc_sink); - } -} - -static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector) -{ - struct dc_link *link = (struct dc_link *)aconnector->dc_link; - - /* - * In case of headless boot with force on for DP managed connector - * Those settings have to be != 0 to get initial modeset - */ - if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) { - link->verified_link_cap.lane_count = LANE_COUNT_FOUR; - link->verified_link_cap.link_rate = LINK_RATE_HIGH2; - } - - create_eml_sink(aconnector); -} - -static enum dc_status dm_validate_stream_and_context(struct dc *dc, - struct dc_stream_state *stream) -{ - enum dc_status dc_result = DC_ERROR_UNEXPECTED; - struct dc_plane_state *dc_plane_state = NULL; - struct dc_state *dc_state = NULL; - - if (!stream) - goto cleanup; - - dc_plane_state = dc_create_plane_state(dc); - if (!dc_plane_state) - goto cleanup; - - dc_state = dc_state_create(dc, NULL); - if (!dc_state) - goto cleanup; - - /* populate stream to plane */ - dc_plane_state->src_rect.height = stream->src.height; - dc_plane_state->src_rect.width = stream->src.width; - dc_plane_state->dst_rect.height = stream->src.height; - dc_plane_state->dst_rect.width = stream->src.width; - dc_plane_state->clip_rect.height = stream->src.height; - dc_plane_state->clip_rect.width = stream->src.width; - dc_plane_state->plane_size.surface_pitch = ((stream->src.width + 255) / 256) * 256; - dc_plane_state->plane_size.surface_size.height = stream->src.height; - dc_plane_state->plane_size.surface_size.width = stream->src.width; - dc_plane_state->plane_size.chroma_size.height = stream->src.height; - dc_plane_state->plane_size.chroma_size.width = stream->src.width; - dc_plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888; - dc_plane_state->tiling_info.gfx9.swizzle = DC_SW_UNKNOWN; - dc_plane_state->rotation = ROTATION_ANGLE_0; - dc_plane_state->is_tiling_rotated = false; - dc_plane_state->tiling_info.gfx8.array_mode = DC_ARRAY_LINEAR_GENERAL; - - dc_result = dc_validate_stream(dc, stream); - if (dc_result == DC_OK) - dc_result = dc_validate_plane(dc, dc_plane_state); - - if (dc_result == DC_OK) - dc_result = dc_state_add_stream(dc, dc_state, stream); - - if (dc_result == DC_OK && !dc_state_add_plane( - dc, - stream, - dc_plane_state, - dc_state)) - dc_result = DC_FAIL_ATTACH_SURFACES; - - if (dc_result == DC_OK) - dc_result = dc_validate_global_state(dc, dc_state, DC_VALIDATE_MODE_ONLY); - -cleanup: - if (dc_state) - dc_state_release(dc_state); - - if (dc_plane_state) - dc_plane_state_release(dc_plane_state); - - return dc_result; -} - -static enum dc_status -dm_validate_stream_color_format(const struct drm_connector_state *drm_state, - const struct dc_stream_state *stream) -{ - enum dc_pixel_encoding encoding; - - if (!drm_state->color_format) - return DC_OK; - - switch (drm_state->color_format) { - case DRM_CONNECTOR_COLOR_FORMAT_AUTO: - case DRM_CONNECTOR_COLOR_FORMAT_RGB444: - encoding = PIXEL_ENCODING_RGB; - break; - case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: - encoding = PIXEL_ENCODING_YCBCR444; - break; - case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: - encoding = PIXEL_ENCODING_YCBCR422; - break; - case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: - encoding = PIXEL_ENCODING_YCBCR420; - break; - default: - encoding = PIXEL_ENCODING_UNDEFINED; - break; - } - - return encoding == stream->timing.pixel_encoding ? - DC_OK : DC_UNSUPPORTED_VALUE; -} - -struct dc_stream_state * -create_validate_stream_for_sink(struct drm_connector *connector, - const struct drm_display_mode *drm_mode, - const struct dm_connector_state *dm_state, - const struct dc_stream_state *old_stream) -{ - struct amdgpu_dm_connector *aconnector = NULL; - struct amdgpu_device *adev = drm_to_adev(connector->dev); - struct dc_stream_state *stream; - const struct drm_connector_state *drm_state = dm_state ? &dm_state->base : NULL; - int requested_bpc = drm_state ? drm_state->max_requested_bpc : 8; - enum dc_status dc_result = DC_OK; - uint8_t bpc_limit = 6; - - if (!dm_state) - return NULL; - - if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) - aconnector = to_amdgpu_dm_connector(connector); - - if (aconnector && - (aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_TYPE_A || - aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_FRL || - aconnector->dc_link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER)) - bpc_limit = 8; - - do { - drm_dbg_kms(connector->dev, "Trying with %d bpc\n", requested_bpc); - stream = create_stream_for_sink(connector, drm_mode, - dm_state, old_stream, - requested_bpc); - if (stream == NULL) { - drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n"); - break; - } - - dc_result = dc_validate_stream(adev->dm.dc, stream); - - if (!aconnector) /* writeback connector */ - return stream; - - if (dc_result == DC_OK && stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) - dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream); - - if (dc_result == DC_OK) - dc_result = dm_validate_stream_and_context(adev->dm.dc, stream); - - if (dc_result == DC_OK) - dc_result = dm_validate_stream_color_format(drm_state, stream); - - if (dc_result != DC_OK) { - drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n", - drm_mode->hdisplay, - drm_mode->vdisplay, - drm_mode->clock, - dc_pixel_encoding_to_str(stream->timing.pixel_encoding), - dc_color_depth_to_str(stream->timing.display_color_depth), - dc_status_to_str(dc_result)); - - dc_stream_release(stream); - stream = NULL; - requested_bpc -= 2; /* lower bpc to retry validation */ - } - - } while (stream == NULL && requested_bpc >= bpc_limit); - - switch (dc_result) { - /* - * If we failed to validate DP bandwidth stream with the requested RGB color depth, - * we try to fallback and configure in order: - * YUV422 (8bpc, 6bpc) - * YUV420 (8bpc, 6bpc) - */ - case DC_FAIL_ENC_VALIDATE: - case DC_EXCEED_DONGLE_CAP: - case DC_NO_DP_LINK_BANDWIDTH: - /* recursively entered twice and already tried both YUV422 and YUV420 */ - if (aconnector->force_yuv422_output && aconnector->force_yuv420_output) - break; - /* first failure; try YUV422 */ - if (!aconnector->force_yuv422_output) { - drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV422\n", - __func__, __LINE__, dc_result); - aconnector->force_yuv422_output = true; - /* recursively entered and YUV422 failed, try YUV420 */ - } else if (!aconnector->force_yuv420_output) { - drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV420\n", - __func__, __LINE__, dc_result); - aconnector->force_yuv420_output = true; - } - stream = create_validate_stream_for_sink(connector, drm_mode, - dm_state, old_stream); - aconnector->force_yuv422_output = false; - aconnector->force_yuv420_output = false; - break; - case DC_OK: - break; - default: - drm_dbg_kms(connector->dev, "%s:%d Unhandled validation failure %d\n", - __func__, __LINE__, dc_result); - break; - } - - return stream; -} - -enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector, - const struct drm_display_mode *mode) -{ - int result = MODE_ERROR; - struct dc_sink *dc_sink; - struct drm_display_mode *test_mode; - /* TODO: Unhardcode stream count */ - struct dc_stream_state *stream; - /* we always have an amdgpu_dm_connector here since we got - * here via the amdgpu_dm_connector_helper_funcs - */ - struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); - - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) || - (mode->flags & DRM_MODE_FLAG_DBLSCAN)) - return result; - - /* - * Only run this the first time mode_valid is called to initilialize - * EDID mgmt - */ - if (aconnector->base.force != DRM_FORCE_UNSPECIFIED && - !aconnector->dc_em_sink) - handle_edid_mgmt(aconnector); - - dc_sink = to_amdgpu_dm_connector(connector)->dc_sink; - - if (dc_sink == NULL && aconnector->base.force != DRM_FORCE_ON_DIGITAL && - aconnector->base.force != DRM_FORCE_ON) { - drm_err(connector->dev, "dc_sink is NULL!\n"); - goto fail; - } - - test_mode = drm_mode_duplicate(connector->dev, mode); - if (!test_mode) - goto fail; - - drm_mode_set_crtcinfo(test_mode, 0); - - stream = create_validate_stream_for_sink(connector, test_mode, - to_dm_connector_state(connector->state), - NULL); - drm_mode_destroy(connector->dev, test_mode); - if (stream) { - dc_stream_release(stream); - result = MODE_OK; - } - -fail: - /* TODO: error handling*/ - return result; -} - -static int fill_hdr_info_packet(const struct drm_connector_state *state, - struct dc_info_packet *out) -{ - struct hdmi_drm_infoframe frame; - unsigned char buf[30]; /* 26 + 4 */ - ssize_t len; - int ret, i; - - memset(out, 0, sizeof(*out)); - - if (!state->hdr_output_metadata) - return 0; - - ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, state); - if (ret) - return ret; - - len = hdmi_drm_infoframe_pack_only(&frame, buf, sizeof(buf)); - if (len < 0) - return (int)len; - - /* Static metadata is a fixed 26 bytes + 4 byte header. */ - if (len != 30) - return -EINVAL; - - /* Prepare the infopacket for DC. */ - switch (state->connector->connector_type) { - case DRM_MODE_CONNECTOR_HDMIA: - out->hb0 = 0x87; /* type */ - out->hb1 = 0x01; /* version */ - out->hb2 = 0x1A; /* length */ - out->sb[0] = buf[3]; /* checksum */ - i = 1; - break; - - case DRM_MODE_CONNECTOR_DisplayPort: - case DRM_MODE_CONNECTOR_eDP: - out->hb0 = 0x00; /* sdp id, zero */ - out->hb1 = 0x87; /* type */ - out->hb2 = 0x1D; /* payload len - 1 */ - out->hb3 = (0x13 << 2); /* sdp version */ - out->sb[0] = 0x01; /* version */ - out->sb[1] = 0x1A; /* length */ - i = 2; - break; - - default: - return -EINVAL; - } - - memcpy(&out->sb[i], &buf[4], 26); - out->valid = true; - - print_hex_dump(KERN_DEBUG, "HDR SB:", DUMP_PREFIX_NONE, 16, 1, out->sb, - sizeof(out->sb), false); - - return 0; -} - -static int -amdgpu_dm_connector_atomic_check(struct drm_connector *conn, - struct drm_atomic_commit *state) -{ - struct drm_connector_state *new_con_state = - drm_atomic_get_new_connector_state(state, conn); - struct drm_connector_state *old_con_state = - drm_atomic_get_old_connector_state(state, conn); - struct drm_crtc *crtc = new_con_state->crtc; - struct drm_crtc_state *new_crtc_state; - struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn); - int ret; - - if (WARN_ON(unlikely(!old_con_state || !new_con_state))) - return -EINVAL; - - trace_amdgpu_dm_connector_atomic_check(new_con_state); - - if (conn->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { - ret = drm_dp_mst_root_conn_atomic_check(new_con_state, &aconn->mst_mgr); - if (ret < 0) - return ret; - } - - if (!crtc) - return 0; - - if (new_con_state->privacy_screen_sw_state != old_con_state->privacy_screen_sw_state) { - new_crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(new_crtc_state)) - return PTR_ERR(new_crtc_state); - - new_crtc_state->mode_changed = true; - } - - if (new_con_state->colorspace != old_con_state->colorspace) { - new_crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(new_crtc_state)) - return PTR_ERR(new_crtc_state); - - new_crtc_state->mode_changed = true; - } - - if (new_con_state->content_type != old_con_state->content_type) { - new_crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(new_crtc_state)) - return PTR_ERR(new_crtc_state); - - new_crtc_state->mode_changed = true; - } - - if (!drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state)) { - struct dc_info_packet hdr_infopacket; - - ret = fill_hdr_info_packet(new_con_state, &hdr_infopacket); - if (ret) - return ret; - - new_crtc_state = drm_atomic_get_crtc_state(state, crtc); - if (IS_ERR(new_crtc_state)) - return PTR_ERR(new_crtc_state); - - /* - * DC considers the stream backends changed if the - * static metadata changes. Forcing the modeset also - * gives a simple way for userspace to switch from - * 8bpc to 10bpc when setting the metadata to enter - * or exit HDR. - * - * Changing the static metadata after it's been - * set is permissible, however. So only force a - * modeset if we're entering or exiting HDR. - */ - new_crtc_state->mode_changed = new_crtc_state->mode_changed || - !old_con_state->hdr_output_metadata || - !new_con_state->hdr_output_metadata; - } - - return 0; -} - -static const struct drm_connector_helper_funcs -amdgpu_dm_connector_helper_funcs = { - /* - * If hotplugging a second bigger display in FB Con mode, bigger resolution - * modes will be filtered by drm_mode_validate_size(), and those modes - * are missing after user start lightdm. So we need to renew modes list. - * in get_modes call back, not just return the modes count - */ - .get_modes = get_modes, - .mode_valid = amdgpu_dm_connector_mode_valid, - .atomic_check = amdgpu_dm_connector_atomic_check, -}; - -static void dm_encoder_helper_disable(struct drm_encoder *encoder) -{ - -} - -int convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth) -{ - switch (display_color_depth) { - case COLOR_DEPTH_666: - return 6; - case COLOR_DEPTH_888: - return 8; - case COLOR_DEPTH_101010: - return 10; - case COLOR_DEPTH_121212: - return 12; - case COLOR_DEPTH_141414: - return 14; - case COLOR_DEPTH_161616: - return 16; - default: - break; - } - return 0; -} - -static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, - struct drm_crtc_state *crtc_state, - struct drm_connector_state *conn_state) -{ - struct drm_atomic_commit *state = crtc_state->state; - struct drm_connector *connector = conn_state->connector; - struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); - struct dm_connector_state *dm_new_connector_state = to_dm_connector_state(conn_state); - const struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; - struct drm_dp_mst_topology_mgr *mst_mgr; - struct drm_dp_mst_port *mst_port; - struct drm_dp_mst_topology_state *mst_state; - enum dc_color_depth color_depth; - int clock, bpp = 0; - bool is_y420 = false; - - if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || - (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { - struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); - struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; - enum drm_mode_status result; - - result = drm_crtc_helper_mode_valid_fixed(encoder->crtc, adjusted_mode, native_mode); - if (result != MODE_OK && dm_new_connector_state->scaling == RMX_OFF) { - drm_dbg_driver(encoder->dev, - "mode %dx%d@%dHz is not native, enabling scaling\n", - adjusted_mode->hdisplay, adjusted_mode->vdisplay, - drm_mode_vrefresh(adjusted_mode)); - dm_new_connector_state->scaling = RMX_ASPECT; - } - return 0; - } - - if (!aconnector->mst_output_port) - return 0; - - mst_port = aconnector->mst_output_port; - mst_mgr = &aconnector->mst_root->mst_mgr; - - if (!crtc_state->connectors_changed && !crtc_state->mode_changed) - return 0; - - mst_state = drm_atomic_get_mst_topology_state(state, mst_mgr); - if (IS_ERR(mst_state)) - return PTR_ERR(mst_state); - - mst_state->pbn_div.full = dm_mst_get_pbn_divider(aconnector->mst_root->dc_link); - - if (!state->duplicated) { - int max_bpc = conn_state->max_requested_bpc; - - is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) && - aconnector->force_yuv420_output; - color_depth = convert_color_depth_from_display_info(connector, - is_y420, - max_bpc); - bpp = convert_dc_color_depth_into_bpc(color_depth) * 3; - clock = adjusted_mode->clock; - dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp << 4); - } - - dm_new_connector_state->vcpi_slots = - drm_dp_atomic_find_time_slots(state, mst_mgr, mst_port, - dm_new_connector_state->pbn); - if (dm_new_connector_state->vcpi_slots < 0) { - drm_dbg_atomic(connector->dev, "failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots); - return dm_new_connector_state->vcpi_slots; - } - return 0; -} - -const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = { - .disable = dm_encoder_helper_disable, - .atomic_check = dm_encoder_helper_atomic_check -}; - static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_commit *state, struct dc_state *dc_state, struct dsc_mst_fairness_vars *vars) @@ -8959,762 +3502,6 @@ static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_commit *state, return 0; } -static int to_drm_connector_type(enum signal_type st, uint32_t connector_id) -{ - switch (st) { - case SIGNAL_TYPE_HDMI_TYPE_A: - return DRM_MODE_CONNECTOR_HDMIA; - case SIGNAL_TYPE_EDP: - return DRM_MODE_CONNECTOR_eDP; - case SIGNAL_TYPE_LVDS: - return DRM_MODE_CONNECTOR_LVDS; - case SIGNAL_TYPE_RGB: - return DRM_MODE_CONNECTOR_VGA; - case SIGNAL_TYPE_DISPLAY_PORT: - case SIGNAL_TYPE_DISPLAY_PORT_MST: - /* External DP bridges have a different connector type. */ - if (connector_id == CONNECTOR_ID_VGA) - return DRM_MODE_CONNECTOR_VGA; - else if (connector_id == CONNECTOR_ID_LVDS) - return DRM_MODE_CONNECTOR_LVDS; - - return DRM_MODE_CONNECTOR_DisplayPort; - case SIGNAL_TYPE_DVI_DUAL_LINK: - case SIGNAL_TYPE_DVI_SINGLE_LINK: - if (connector_id == CONNECTOR_ID_SINGLE_LINK_DVII || - connector_id == CONNECTOR_ID_DUAL_LINK_DVII) - return DRM_MODE_CONNECTOR_DVII; - - return DRM_MODE_CONNECTOR_DVID; - case SIGNAL_TYPE_VIRTUAL: - return DRM_MODE_CONNECTOR_VIRTUAL; - - default: - return DRM_MODE_CONNECTOR_Unknown; - } -} - -static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector) -{ - struct drm_encoder *encoder; - - /* There is only one encoder per connector */ - drm_connector_for_each_possible_encoder(connector, encoder) - return encoder; - - return NULL; -} - -static void amdgpu_dm_get_native_mode(struct drm_connector *connector) -{ - struct drm_encoder *encoder; - struct amdgpu_encoder *amdgpu_encoder; - - encoder = amdgpu_dm_connector_to_encoder(connector); - - if (encoder == NULL) - return; - - amdgpu_encoder = to_amdgpu_encoder(encoder); - - amdgpu_encoder->native_mode.clock = 0; - - if (!list_empty(&connector->probed_modes)) { - struct drm_display_mode *preferred_mode = NULL; - - list_for_each_entry(preferred_mode, - &connector->probed_modes, - head) { - if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) - amdgpu_encoder->native_mode = *preferred_mode; - - break; - } - - } -} - -static struct drm_display_mode * -amdgpu_dm_create_common_mode(struct drm_encoder *encoder, - const char *name, - int hdisplay, int vdisplay) -{ - struct drm_device *dev = encoder->dev; - struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); - struct drm_display_mode *mode = NULL; - struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; - - mode = drm_mode_duplicate(dev, native_mode); - - if (mode == NULL) - return NULL; - - mode->hdisplay = hdisplay; - mode->vdisplay = vdisplay; - mode->type &= ~DRM_MODE_TYPE_PREFERRED; - strscpy(mode->name, name, DRM_DISPLAY_MODE_LEN); - - return mode; - -} - -static const struct amdgpu_dm_mode_size { - char name[DRM_DISPLAY_MODE_LEN]; - int w; - int h; -} common_modes[] = { - { "640x480", 640, 480}, - { "800x600", 800, 600}, - { "1024x768", 1024, 768}, - { "1280x720", 1280, 720}, - { "1280x800", 1280, 800}, - {"1280x1024", 1280, 1024}, - { "1440x900", 1440, 900}, - {"1680x1050", 1680, 1050}, - {"1600x1200", 1600, 1200}, - {"1920x1080", 1920, 1080}, - {"1920x1200", 1920, 1200} -}; - -static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder, - struct drm_connector *connector) -{ - struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); - struct drm_display_mode *mode = NULL; - struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; - struct amdgpu_dm_connector *amdgpu_dm_connector = - to_amdgpu_dm_connector(connector); - int i; - int n; - - if ((connector->connector_type != DRM_MODE_CONNECTOR_eDP) && - (connector->connector_type != DRM_MODE_CONNECTOR_LVDS)) - return; - - n = ARRAY_SIZE(common_modes); - - for (i = 0; i < n; i++) { - struct drm_display_mode *curmode = NULL; - bool mode_existed = false; - - if (common_modes[i].w > native_mode->hdisplay || - common_modes[i].h > native_mode->vdisplay || - (common_modes[i].w == native_mode->hdisplay && - common_modes[i].h == native_mode->vdisplay)) - continue; - - list_for_each_entry(curmode, &connector->probed_modes, head) { - if (common_modes[i].w == curmode->hdisplay && - common_modes[i].h == curmode->vdisplay) { - mode_existed = true; - break; - } - } - - if (mode_existed) - continue; - - mode = amdgpu_dm_create_common_mode(encoder, - common_modes[i].name, common_modes[i].w, - common_modes[i].h); - if (!mode) - continue; - - drm_mode_probed_add(connector, mode); - amdgpu_dm_connector->num_modes++; - } -} - -static void amdgpu_set_panel_orientation(struct drm_connector *connector) -{ - struct drm_encoder *encoder; - struct amdgpu_encoder *amdgpu_encoder; - const struct drm_display_mode *native_mode; - - if (connector->connector_type != DRM_MODE_CONNECTOR_eDP && - connector->connector_type != DRM_MODE_CONNECTOR_LVDS) - return; - - mutex_lock(&connector->dev->mode_config.mutex); - amdgpu_dm_connector_get_modes(connector); - mutex_unlock(&connector->dev->mode_config.mutex); - - encoder = amdgpu_dm_connector_to_encoder(connector); - if (!encoder) - return; - - amdgpu_encoder = to_amdgpu_encoder(encoder); - - native_mode = &amdgpu_encoder->native_mode; - if (native_mode->hdisplay == 0 || native_mode->vdisplay == 0) - return; - - drm_connector_set_panel_orientation_with_quirk(connector, - DRM_MODE_PANEL_ORIENTATION_UNKNOWN, - native_mode->hdisplay, - native_mode->vdisplay); -} - -static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, - const struct drm_edid *drm_edid) -{ - struct amdgpu_dm_connector *amdgpu_dm_connector = - to_amdgpu_dm_connector(connector); - - if (drm_edid) { - /* empty probed_modes */ - INIT_LIST_HEAD(&connector->probed_modes); - amdgpu_dm_connector->num_modes = - drm_edid_connector_add_modes(connector); - - /* sorting the probed modes before calling function - * amdgpu_dm_get_native_mode() since EDID can have - * more than one preferred mode. The modes that are - * later in the probed mode list could be of higher - * and preferred resolution. For example, 3840x2160 - * resolution in base EDID preferred timing and 4096x2160 - * preferred resolution in DID extension block later. - */ - drm_mode_sort(&connector->probed_modes); - amdgpu_dm_get_native_mode(connector); - - /* Freesync capabilities are reset by calling - * drm_edid_connector_add_modes() and need to be - * restored here. - */ - amdgpu_dm_update_freesync_caps(connector, drm_edid, false); - } else { - amdgpu_dm_connector->num_modes = 0; - } -} - -static bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector, - struct drm_display_mode *mode) -{ - struct drm_display_mode *m; - - list_for_each_entry(m, &aconnector->base.probed_modes, head) { - if (drm_mode_equal(m, mode)) - return true; - } - - return false; -} - -static uint add_fs_modes(struct amdgpu_dm_connector *aconnector) -{ - const struct drm_display_mode *m; - struct drm_display_mode *new_mode; - uint i; - u32 new_modes_count = 0; - - /* Standard FPS values - * - * 23.976 - TV/NTSC - * 24 - Cinema - * 25 - TV/PAL - * 29.97 - TV/NTSC - * 30 - TV/NTSC - * 48 - Cinema HFR - * 50 - TV/PAL - * 60 - Commonly used - * 48,72,96,120 - Multiples of 24 - */ - static const u32 common_rates[] = { - 23976, 24000, 25000, 29970, 30000, - 48000, 50000, 60000, 72000, 96000, 120000 - }; - - /* - * Find mode with highest refresh rate with the same resolution - * as the preferred mode. Some monitors report a preferred mode - * with lower resolution than the highest refresh rate supported. - */ - - m = get_highest_refresh_rate_mode(aconnector, true); - if (!m) - return 0; - - for (i = 0; i < ARRAY_SIZE(common_rates); i++) { - u64 target_vtotal, target_vtotal_diff; - u64 num, den; - - if (drm_mode_vrefresh(m) * 1000 < common_rates[i]) - continue; - - if (common_rates[i] < aconnector->min_vfreq * 1000 || - common_rates[i] > aconnector->max_vfreq * 1000) - continue; - - num = (unsigned long long)m->clock * 1000 * 1000; - den = common_rates[i] * (unsigned long long)m->htotal; - target_vtotal = div_u64(num, den); - target_vtotal_diff = target_vtotal - m->vtotal; - - /* Check for illegal modes */ - if (m->vsync_start + target_vtotal_diff < m->vdisplay || - m->vsync_end + target_vtotal_diff < m->vsync_start || - m->vtotal + target_vtotal_diff < m->vsync_end) - continue; - - new_mode = drm_mode_duplicate(aconnector->base.dev, m); - if (!new_mode) - goto out; - - new_mode->vtotal += (u16)target_vtotal_diff; - new_mode->vsync_start += (u16)target_vtotal_diff; - new_mode->vsync_end += (u16)target_vtotal_diff; - new_mode->type &= ~DRM_MODE_TYPE_PREFERRED; - new_mode->type |= DRM_MODE_TYPE_DRIVER; - - if (!is_duplicate_mode(aconnector, new_mode)) { - drm_mode_probed_add(&aconnector->base, new_mode); - new_modes_count += 1; - } else - drm_mode_destroy(aconnector->base.dev, new_mode); - } - out: - return new_modes_count; -} - -static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector, - const struct drm_edid *drm_edid) -{ - struct amdgpu_dm_connector *amdgpu_dm_connector = - to_amdgpu_dm_connector(connector); - - if (!(amdgpu_freesync_vid_mode && drm_edid)) - return; - - if (!amdgpu_dm_connector->dc_sink || !amdgpu_dm_connector->dc_link) - return; - - if (!dc_supports_vrr(amdgpu_dm_connector->dc_sink->ctx->dce_version)) - return; - - if (dc_connector_supports_analog(amdgpu_dm_connector->dc_link->link_id.id) && - amdgpu_dm_connector->dc_sink->edid_caps.analog) - return; - - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - amdgpu_dm_connector->num_modes += - add_fs_modes(amdgpu_dm_connector); -} - -static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) -{ - struct amdgpu_dm_connector *amdgpu_dm_connector = - to_amdgpu_dm_connector(connector); - struct dc_link *dc_link = amdgpu_dm_connector->dc_link; - struct drm_encoder *encoder; - const struct drm_edid *drm_edid = amdgpu_dm_connector->drm_edid; - struct dc_link_settings *verified_link_cap = &dc_link->verified_link_cap; - const struct dc *dc = dc_link->dc; - - encoder = amdgpu_dm_connector_to_encoder(connector); - - if (!drm_edid) { - amdgpu_dm_connector->num_modes = - drm_add_modes_noedid(connector, 640, 480); - if (dc->link_srv->dp_get_encoding_format(verified_link_cap) == DP_128b_132b_ENCODING) - amdgpu_dm_connector->num_modes += - drm_add_modes_noedid(connector, 1920, 1080); - - if (amdgpu_dm_connector->dc_sink && - amdgpu_dm_connector->dc_sink->edid_caps.analog && - dc_connector_supports_analog(dc_link->link_id.id)) { - /* Analog monitor connected by DAC load detection. - * Add common modes. It will be up to the user to select one that works. - */ - for (int i = 0; i < ARRAY_SIZE(common_modes); i++) - amdgpu_dm_connector->num_modes += drm_add_modes_noedid( - connector, common_modes[i].w, common_modes[i].h); - } - } else { - amdgpu_dm_connector_ddc_get_modes(connector, drm_edid); - if (encoder) - amdgpu_dm_connector_add_common_modes(encoder, connector); - amdgpu_dm_connector_add_freesync_modes(connector, drm_edid); - } - amdgpu_dm_fbc_init(connector); - - return amdgpu_dm_connector->num_modes; -} - -static const u32 supported_colorspaces = - BIT(DRM_MODE_COLORIMETRY_BT709_YCC) | - BIT(DRM_MODE_COLORIMETRY_OPRGB) | - BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) | - BIT(DRM_MODE_COLORIMETRY_BT2020_YCC); - -static const u32 supported_colorformats = - BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | - BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | - BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | - BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); - -void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, - struct amdgpu_dm_connector *aconnector, - int connector_type, - struct dc_link *link, - int link_index) -{ - struct amdgpu_device *adev = drm_to_adev(dm->ddev); - - /* - * Some of the properties below require access to state, like bpc. - * Allocate some default initial connector state with our reset helper. - */ - if (aconnector->base.funcs->reset) - aconnector->base.funcs->reset(&aconnector->base); - - aconnector->connector_id = link_index; - aconnector->bl_idx = -1; - aconnector->dc_link = link; - aconnector->base.interlace_allowed = false; - aconnector->base.doublescan_allowed = false; - aconnector->base.stereo_allowed = false; - aconnector->base.dpms = DRM_MODE_DPMS_OFF; - aconnector->hpd.hpd = AMDGPU_HPD_NONE; /* not used */ - aconnector->audio_inst = -1; - aconnector->pack_sdp_v1_3 = false; - aconnector->as_type = ADAPTIVE_SYNC_TYPE_NONE; - memset(&aconnector->vsdb_info, 0, sizeof(aconnector->vsdb_info)); - mutex_init(&aconnector->hpd_lock); - mutex_init(&aconnector->handle_mst_msg_ready); - - /* - * If HDMI HPD debounce delay is set, use the minimum between selected - * value and AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS - */ - if (amdgpu_hdmi_hpd_debounce_delay_ms) { - aconnector->hdmi_hpd_debounce_delay_ms = min(amdgpu_hdmi_hpd_debounce_delay_ms, - AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS); - INIT_DELAYED_WORK(&aconnector->hdmi_hpd_debounce_work, hdmi_hpd_debounce_work); - aconnector->hdmi_prev_sink = NULL; - } else { - aconnector->hdmi_hpd_debounce_delay_ms = 0; - } - - /* - * configure support HPD hot plug connector_>polled default value is 0 - * which means HPD hot plug not supported - */ - switch (connector_type) { - case DRM_MODE_CONNECTOR_HDMIA: - aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; - aconnector->base.ycbcr_420_allowed = - link->link_enc->features.hdmi_ycbcr420_supported ? true : false; - break; - case DRM_MODE_CONNECTOR_DisplayPort: - aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; - link->link_enc = link_enc_cfg_get_link_enc(link); - ASSERT(link->link_enc); - if (link->link_enc) - aconnector->base.ycbcr_420_allowed = - link->link_enc->features.dp_ycbcr420_supported ? true : false; - break; - case DRM_MODE_CONNECTOR_DVID: - aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; - break; - case DRM_MODE_CONNECTOR_DVII: - case DRM_MODE_CONNECTOR_VGA: - aconnector->base.polled = - DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; - break; - default: - break; - } - - drm_object_attach_property(&aconnector->base.base, - dm->ddev->mode_config.scaling_mode_property, - DRM_MODE_SCALE_NONE); - - if (connector_type == DRM_MODE_CONNECTOR_HDMIA - || (connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root)) - drm_connector_attach_broadcast_rgb_property(&aconnector->base); - - drm_object_attach_property(&aconnector->base.base, - adev->mode_info.underscan_property, - UNDERSCAN_OFF); - drm_object_attach_property(&aconnector->base.base, - adev->mode_info.underscan_hborder_property, - 0); - drm_object_attach_property(&aconnector->base.base, - adev->mode_info.underscan_vborder_property, - 0); - - if (!aconnector->mst_root) - drm_connector_attach_max_bpc_property(&aconnector->base, 8, 16); - - aconnector->base.state->max_bpc = 16; - aconnector->base.state->max_requested_bpc = aconnector->base.state->max_bpc; - - if (connector_type == DRM_MODE_CONNECTOR_HDMIA) { - /* Content Type is currently only implemented for HDMI. */ - drm_connector_attach_content_type_property(&aconnector->base); - } - - if (connector_type == DRM_MODE_CONNECTOR_HDMIA) { - if (!drm_mode_create_hdmi_colorspace_property(&aconnector->base, supported_colorspaces)) - drm_connector_attach_colorspace_property(&aconnector->base); - } else if ((connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root) || - connector_type == DRM_MODE_CONNECTOR_eDP) { - if (!drm_mode_create_dp_colorspace_property(&aconnector->base, supported_colorspaces)) - drm_connector_attach_colorspace_property(&aconnector->base); - } - - if (connector_type == DRM_MODE_CONNECTOR_HDMIA || - connector_type == DRM_MODE_CONNECTOR_DisplayPort || - connector_type == DRM_MODE_CONNECTOR_eDP) { - drm_connector_attach_hdr_output_metadata_property(&aconnector->base); - - if (!aconnector->mst_root) { - drm_connector_attach_vrr_capable_property(&aconnector->base); - drm_connector_attach_color_format_property(&aconnector->base, - supported_colorformats); - } - - if (adev->dm.hdcp_workqueue) - drm_connector_attach_content_protection_property(&aconnector->base, true); - } - - if (connector_type == DRM_MODE_CONNECTOR_eDP) { - struct drm_privacy_screen *privacy_screen; - - drm_connector_attach_panel_type_property(&aconnector->base); - - privacy_screen = drm_privacy_screen_get(adev_to_drm(adev)->dev, NULL); - if (!IS_ERR(privacy_screen)) { - drm_connector_attach_privacy_screen_provider(&aconnector->base, - privacy_screen); - } else if (PTR_ERR(privacy_screen) != -ENODEV) { - drm_warn(adev_to_drm(adev), "Error getting privacy-screen\n"); - } - } -} - -static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, - struct i2c_msg *msgs, int num) -{ - struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap); - struct ddc_service *ddc_service = i2c->ddc_service; - struct i2c_command cmd; - int i; - int result = -EIO; - - if (!ddc_service->ddc_pin) - return result; - - cmd.payloads = kzalloc_objs(struct i2c_payload, num); - - if (!cmd.payloads) - return result; - - cmd.number_of_payloads = num; - cmd.engine = I2C_COMMAND_ENGINE_DEFAULT; - cmd.speed = 100; - - for (i = 0; i < num; i++) { - cmd.payloads[i].write = !(msgs[i].flags & I2C_M_RD); - cmd.payloads[i].address = msgs[i].addr; - cmd.payloads[i].length = msgs[i].len; - cmd.payloads[i].data = msgs[i].buf; - } - - if (i2c->oem) { - if (dc_submit_i2c_oem( - ddc_service->ctx->dc, - &cmd)) - result = num; - } else { - if (dc_submit_i2c( - ddc_service->ctx->dc, - ddc_service->link->link_index, - &cmd)) - result = num; - } - - kfree(cmd.payloads); - return result; -} - -static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; -} - -static const struct i2c_algorithm amdgpu_dm_i2c_algo = { - .master_xfer = amdgpu_dm_i2c_xfer, - .functionality = amdgpu_dm_i2c_func, -}; - -static struct amdgpu_i2c_adapter * -create_i2c(struct ddc_service *ddc_service, bool oem) -{ - struct amdgpu_device *adev = ddc_service->ctx->driver_context; - struct amdgpu_i2c_adapter *i2c; - - i2c = kzalloc_obj(struct amdgpu_i2c_adapter); - if (!i2c) - return NULL; - i2c->base.owner = THIS_MODULE; - i2c->base.dev.parent = &adev->pdev->dev; - i2c->base.algo = &amdgpu_dm_i2c_algo; - if (oem) - snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c OEM bus"); - else - snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d", - ddc_service->link->link_index); - i2c_set_adapdata(&i2c->base, i2c); - i2c->ddc_service = ddc_service; - i2c->oem = oem; - - return i2c; -} - -int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector) -{ - struct cec_connector_info conn_info; - struct drm_device *ddev = aconnector->base.dev; - struct device *hdmi_dev = ddev->dev; - - if (amdgpu_dc_debug_mask & DC_DISABLE_HDMI_CEC) { - drm_info(ddev, "HDMI-CEC feature masked\n"); - return -EINVAL; - } - - cec_fill_conn_info_from_drm(&conn_info, &aconnector->base); - aconnector->notifier = - cec_notifier_conn_register(hdmi_dev, NULL, &conn_info); - if (!aconnector->notifier) { - drm_err(ddev, "Failed to create cec notifier\n"); - return -ENOMEM; - } - - return 0; -} - -/* - * Note: this function assumes that dc_link_detect() was called for the - * dc_link which will be represented by this aconnector. - */ -static int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm, - struct amdgpu_dm_connector *aconnector, - u32 link_index, - struct amdgpu_encoder *aencoder) -{ - int res = 0; - int connector_type; - struct dc *dc = dm->dc; - struct dc_link *link = dc_get_link_at_index(dc, link_index); - struct amdgpu_i2c_adapter *i2c; - - /* Not needed for writeback connector */ - link->priv = aconnector; - - - i2c = create_i2c(link->ddc, false); - if (!i2c) { - drm_err(adev_to_drm(dm->adev), "Failed to create i2c adapter data\n"); - return -ENOMEM; - } - - aconnector->i2c = i2c; - res = devm_i2c_add_adapter(dm->adev->dev, &i2c->base); - - if (res) { - drm_err(adev_to_drm(dm->adev), "Failed to register hw i2c %d\n", link->link_index); - goto out_free; - } - - connector_type = to_drm_connector_type(link->connector_signal, link->link_id.id); - - res = drm_connector_init_with_ddc( - dm->ddev, - &aconnector->base, - &amdgpu_dm_connector_funcs, - connector_type, - &i2c->base); - - if (res) { - drm_err(adev_to_drm(dm->adev), "connector_init failed\n"); - aconnector->connector_id = -1; - goto out_free; - } - - drm_connector_helper_add( - &aconnector->base, - &amdgpu_dm_connector_helper_funcs); - - amdgpu_dm_connector_init_helper( - dm, - aconnector, - connector_type, - link, - link_index); - - drm_connector_attach_encoder( - &aconnector->base, &aencoder->base); - - if (connector_type == DRM_MODE_CONNECTOR_HDMIA || - connector_type == DRM_MODE_CONNECTOR_HDMIB) - amdgpu_dm_initialize_hdmi_connector(aconnector); - - if (dc_is_dp_signal(link->connector_signal)) - amdgpu_dm_initialize_dp_connector(dm, aconnector, link->link_index); - -out_free: - if (res) { - kfree(i2c); - aconnector->i2c = NULL; - } - return res; -} - -int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev) -{ - switch (adev->mode_info.num_crtc) { - case 1: - return 0x1; - case 2: - return 0x3; - case 3: - return 0x7; - case 4: - return 0xf; - case 5: - return 0x1f; - case 6: - default: - return 0x3f; - } -} - -static int amdgpu_dm_encoder_init(struct drm_device *dev, - struct amdgpu_encoder *aencoder, - uint32_t link_index) -{ - struct amdgpu_device *adev = drm_to_adev(dev); - - int res = drm_encoder_init(dev, - &aencoder->base, - &amdgpu_dm_encoder_funcs, - DRM_MODE_ENCODER_TMDS, - NULL); - - aencoder->base.possible_crtcs = amdgpu_dm_get_encoder_crtc_mask(adev); - - if (!res) - aencoder->encoder_id = link_index; - else - aencoder->encoder_id = -1; - - drm_encoder_helper_add(&aencoder->base, &amdgpu_dm_encoder_helper_funcs); - - return res; -} - static void manage_dm_interrupts(struct amdgpu_device *adev, struct amdgpu_crtc *acrtc, struct dm_crtc_state *acrtc_state) @@ -9814,7 +3601,7 @@ static void dm_update_pflip_irq_state(struct amdgpu_device *adev, amdgpu_irq_update(adev, &adev->pageflip_irq, irq_type); } -static bool +STATIC_IFN_KUNIT bool is_scaling_state_different(const struct dm_connector_state *dm_state, const struct dm_connector_state *old_dm_state) { @@ -9831,6 +3618,7 @@ is_scaling_state_different(const struct dm_connector_state *dm_state, return true; return false; } +EXPORT_IF_KUNIT(is_scaling_state_different); static bool is_content_protection_different(struct drm_crtc_state *new_crtc_state, struct drm_crtc_state *old_crtc_state, @@ -10414,14 +4202,12 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, continue; bundle->surface_updates[planes_count].surface = dc_plane; - if (new_pcrtc_state->color_mgmt_changed || new_plane_state->color_mgmt_changed) { + if (new_pcrtc_state->color_mgmt_changed) { bundle->surface_updates[planes_count].gamma = &dc_plane->gamma_correction; bundle->surface_updates[planes_count].in_transfer_func = &dc_plane->in_transfer_func; bundle->surface_updates[planes_count].gamut_remap_matrix = &dc_plane->gamut_remap_matrix; bundle->surface_updates[planes_count].hdr_mult = dc_plane->hdr_mult; - bundle->surface_updates[planes_count].func_shaper = &dc_plane->in_shaper_func; - bundle->surface_updates[planes_count].lut3d_func = &dc_plane->lut3d_func; - bundle->surface_updates[planes_count].blend_tf = &dc_plane->blend_tf; + bundle->surface_updates[planes_count].cm = &dc_plane->cm; } amdgpu_dm_plane_fill_dc_scaling_info(dm->adev, new_plane_state, @@ -10698,87 +4484,6 @@ cleanup: kfree(bundle); } -static void amdgpu_dm_commit_audio(struct drm_device *dev, - struct drm_atomic_commit *state) -{ - struct amdgpu_device *adev = drm_to_adev(dev); - struct amdgpu_dm_connector *aconnector; - struct drm_connector *connector; - struct drm_connector_state *old_con_state, *new_con_state; - struct drm_crtc_state *new_crtc_state; - struct dm_crtc_state *new_dm_crtc_state; - const struct dc_stream_status *status; - int i, inst; - - /* Notify device removals. */ - for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { - if (old_con_state->crtc != new_con_state->crtc) { - /* CRTC changes require notification. */ - goto notify; - } - - if (!new_con_state->crtc) - continue; - - new_crtc_state = drm_atomic_get_new_crtc_state( - state, new_con_state->crtc); - - if (!new_crtc_state) - continue; - - if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) - continue; - -notify: - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - aconnector = to_amdgpu_dm_connector(connector); - - mutex_lock(&adev->dm.audio_lock); - inst = aconnector->audio_inst; - aconnector->audio_inst = -1; - mutex_unlock(&adev->dm.audio_lock); - - amdgpu_dm_audio_eld_notify(adev, inst); - } - - /* Notify audio device additions. */ - for_each_new_connector_in_state(state, connector, new_con_state, i) { - if (!new_con_state->crtc) - continue; - - new_crtc_state = drm_atomic_get_new_crtc_state( - state, new_con_state->crtc); - - if (!new_crtc_state) - continue; - - if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) - continue; - - new_dm_crtc_state = to_dm_crtc_state(new_crtc_state); - if (!new_dm_crtc_state->stream) - continue; - - status = dc_stream_get_status(new_dm_crtc_state->stream); - if (!status) - continue; - - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - continue; - - aconnector = to_amdgpu_dm_connector(connector); - - mutex_lock(&adev->dm.audio_lock); - inst = status->audio_inst; - aconnector->audio_inst = inst; - mutex_unlock(&adev->dm.audio_lock); - - amdgpu_dm_audio_eld_notify(adev, inst); - } -} - /* * amdgpu_dm_crtc_copy_transient_flags - copy mirrored flags from DRM to DC * @crtc_state: the DRM CRTC state @@ -10793,10 +4498,55 @@ static void amdgpu_dm_crtc_copy_transient_flags(struct drm_crtc_state *crtc_stat stream_state->mode_changed = drm_atomic_crtc_needs_modeset(crtc_state); } +/** + * amdgpu_dm_crtc_complete_writeback - finish a pending writeback job + * @acrtc: the CRTC whose pending writeback should be completed + * + * Clears the pending state, signals the writeback out fence and releases the + * vblank reference taken in dm_set_writeback() while the writeback was armed. + * The pending flag is tested and cleared under the writeback job lock, so this + * is safe to call concurrently from the completion vblank IRQ + * (dm_crtc_high_irq()) and from the writeback teardown path + * (dm_clear_writeback()); only the caller that observes the pending job + * performs the completion. + * + * Return: true if a pending writeback job was completed by this call. + */ +bool amdgpu_dm_crtc_complete_writeback(struct amdgpu_crtc *acrtc) +{ + unsigned long flags; + bool pending; + + if (!acrtc->wb_conn) + return false; + + spin_lock_irqsave(&acrtc->wb_conn->job_lock, flags); + pending = acrtc->wb_pending; + acrtc->wb_pending = false; + spin_unlock_irqrestore(&acrtc->wb_conn->job_lock, flags); + + if (!pending) + return false; + + drm_writeback_signal_completion(acrtc->wb_conn, 0); + drm_crtc_vblank_put(&acrtc->base); + + return true; +} + static void dm_clear_writeback(struct amdgpu_display_manager *dm, + struct amdgpu_crtc *acrtc, struct dm_crtc_state *crtc_state) { dc_stream_remove_writeback(dm->dc, crtc_state->stream, 0); + + /* + * If the writeback is still pending when it is torn down (its + * completion vblank IRQ never fired), signal the out fence so a + * waiting client does not stall and release the vblank reference + * taken in dm_set_writeback(). + */ + amdgpu_dm_crtc_complete_writeback(acrtc); } /** @@ -10949,7 +4699,7 @@ static void amdgpu_dm_commit_streams(struct drm_atomic_commit *state, dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); - dm_clear_writeback(dm, dm_old_crtc_state); + dm_clear_writeback(dm, acrtc, dm_old_crtc_state); acrtc->wb_enabled = false; } @@ -11223,9 +4973,24 @@ static void dm_set_writeback(struct amdgpu_display_manager *dm, dc_stream_add_writeback(dm->dc, crtc_state->stream, wb_info); - acrtc->wb_pending = true; acrtc->wb_conn = wb_conn; drm_writeback_queue_job(wb_conn, new_con_state); + + /* + * Writeback completion is detected in the CRTC vblank IRQ + * (dm_crtc_high_irq()). Take a vblank reference so the vblank interrupt + * stays enabled while the writeback is pending; otherwise a + * writeback-only commit right after drm_crtc_vblank_on() (e.g. + * re-enabling a CRTC that was disabled) has no other vblank reference, + * the IRQ never fires and the out fence times out. The matching put + * happens once completion is signalled in dm_crtc_high_irq(), or when + * the writeback is torn down in dm_clear_writeback(). + * + * Arm wb_pending only after the reference is held so the completion IRQ + * cannot run its matching vblank_put before this get. + */ + WARN_ON(drm_crtc_vblank_get(&acrtc->base)); + acrtc->wb_pending = true; } static void amdgpu_dm_update_hdcp(struct drm_atomic_commit *state) @@ -11376,6 +5141,74 @@ static int amdgpu_dm_atomic_setup_commit(struct drm_atomic_commit *state) return 0; } +STATIC_IFN_KUNIT void set_multisync_trigger_params( + struct dc_stream_state *stream) +{ + struct dc_stream_state *master = NULL; + + if (stream->triggered_crtc_reset.enabled) { + master = stream->triggered_crtc_reset.event_source; + stream->triggered_crtc_reset.event = + master->timing.flags.VSYNC_POSITIVE_POLARITY ? + CRTC_EVENT_VSYNC_RISING : CRTC_EVENT_VSYNC_FALLING; + stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_PIXEL; + } +} +EXPORT_IF_KUNIT(set_multisync_trigger_params); + +STATIC_IFN_KUNIT void set_master_stream(struct dc_stream_state *stream_set[], + int stream_count) +{ + int j, highest_rfr = 0, master_stream = 0; + + for (j = 0; j < stream_count; j++) { + if (stream_set[j] && stream_set[j]->triggered_crtc_reset.enabled) { + int refresh_rate = 0; + + refresh_rate = (stream_set[j]->timing.pix_clk_100hz*100)/ + (stream_set[j]->timing.h_total*stream_set[j]->timing.v_total); + if (refresh_rate > highest_rfr) { + highest_rfr = refresh_rate; + master_stream = j; + } + } + } + for (j = 0; j < stream_count; j++) { + if (stream_set[j]) + stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream]; + } +} +EXPORT_IF_KUNIT(set_master_stream); + +static void dm_enable_per_frame_crtc_master_sync(struct dc_state *context) +{ + int i = 0; + struct dc_stream_state *stream; + + if (context->stream_count < 2) + return; + for (i = 0; i < context->stream_count ; i++) { + if (!context->streams[i]) + continue; + /* + * TODO: add a function to read AMD VSDB bits and set + * crtc_sync_master.multi_sync_enabled flag + * For now it's set to false + */ + } + + set_master_stream(context->streams, context->stream_count); + + for (i = 0; i < context->stream_count ; i++) { + stream = context->streams[i]; + + if (!stream) + continue; + + set_multisync_trigger_params(stream); + } +} + /** * amdgpu_dm_atomic_commit_tail() - AMDgpu DM's commit tail implementation. * @state: The atomic state to commit @@ -11444,7 +5277,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state) if ((new_con_state->hdmi.broadcast_rgb != old_con_state->hdmi.broadcast_rgb) && (dm_old_crtc_state->stream->output_color_space != - get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state))) + amdgpu_dm_get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state))) output_color_space_changed = true; abm_changed = dm_new_crtc_state->abm_level != @@ -11458,7 +5291,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state) stream_update.stream = dm_new_crtc_state->stream; if (scaling_changed) { - update_stream_scaling_settings(dev, &dm_new_con_state->base.crtc->mode, + amdgpu_dm_update_stream_scaling_settings(dev, &dm_new_con_state->base.crtc->mode, dm_new_con_state, dm_new_crtc_state->stream); stream_update.src = dm_new_crtc_state->stream->src; @@ -11467,7 +5300,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state) if (output_color_space_changed) { dm_new_crtc_state->stream->output_color_space - = get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state); + = amdgpu_dm_get_output_color_space(&dm_new_crtc_state->stream->timing, new_con_state); stream_update.output_color_space = &dm_new_crtc_state->stream->output_color_space; } @@ -11479,7 +5312,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state) } if (hdr_changed) { - fill_hdr_info_packet(new_con_state, &hdr_packet); + amdgpu_dm_fill_hdr_info_packet(new_con_state, &hdr_packet); stream_update.hdr_static_metadata = &hdr_packet; } @@ -11687,104 +5520,6 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_commit *state) trace_amdgpu_dm_atomic_commit_tail_finish(state); } -static int dm_force_atomic_commit(struct drm_connector *connector) -{ - int ret = 0; - struct drm_device *ddev = connector->dev; - struct drm_atomic_commit *state = drm_atomic_commit_alloc(ddev); - struct amdgpu_crtc *disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc); - struct drm_plane *plane = disconnected_acrtc->base.primary; - struct drm_connector_state *conn_state; - struct drm_crtc_state *crtc_state; - struct drm_plane_state *plane_state; - - if (!state) - return -ENOMEM; - - state->acquire_ctx = ddev->mode_config.acquire_ctx; - - /* Construct an atomic state to restore previous display setting */ - - /* - * Attach connectors to drm_atomic_commit - */ - conn_state = drm_atomic_get_connector_state(state, connector); - - /* Check for error in getting connector state */ - if (IS_ERR(conn_state)) { - ret = PTR_ERR(conn_state); - goto out; - } - - /* Attach crtc to drm_atomic_commit*/ - crtc_state = drm_atomic_get_crtc_state(state, &disconnected_acrtc->base); - - /* Check for error in getting crtc state */ - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto out; - } - - /* force a restore */ - crtc_state->mode_changed = true; - - /* Attach plane to drm_atomic_commit */ - plane_state = drm_atomic_get_plane_state(state, plane); - - /* Check for error in getting plane state */ - if (IS_ERR(plane_state)) { - ret = PTR_ERR(plane_state); - goto out; - } - - /* Call commit internally with the state we just constructed */ - ret = drm_atomic_commit(state); - -out: - drm_atomic_commit_put(state); - if (ret) - drm_err(ddev, "Restoring old state failed with %i\n", ret); - - return ret; -} - -/* - * This function handles all cases when set mode does not come upon hotplug. - * This includes when a display is unplugged then plugged back into the - * same port and when running without usermode desktop manager supprot - */ -void dm_restore_drm_connector_state(struct drm_device *dev, - struct drm_connector *connector) -{ - struct amdgpu_dm_connector *aconnector; - struct amdgpu_crtc *disconnected_acrtc; - struct dm_crtc_state *acrtc_state; - - if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) - return; - - aconnector = to_amdgpu_dm_connector(connector); - - if (!aconnector->dc_sink || !connector->state || !connector->encoder) - return; - - disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc); - if (!disconnected_acrtc) - return; - - acrtc_state = to_dm_crtc_state(disconnected_acrtc->base.state); - if (!acrtc_state->stream) - return; - - /* - * If the previous sink is not released and different from the current, - * we deduce we are in a state where we can not rely on usermode call - * to turn on the display, so we do it here - */ - if (acrtc_state->stream->sink != aconnector->dc_sink) - dm_force_atomic_commit(&aconnector->base); -} - /* * Grabs all modesetting locks to serialize against any blocking commits, * Waits for completion of all non blocking commits. @@ -11889,7 +5624,7 @@ static void reset_freesync_config_for_crtc( sizeof(new_crtc_state->vrr_infopacket)); } -static bool +STATIC_IFN_KUNIT bool is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, struct drm_crtc_state *new_crtc_state) { @@ -11918,8 +5653,9 @@ is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, return false; } +EXPORT_IF_KUNIT(is_timing_unchanged_for_freesync); -static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state) +STATIC_IFN_KUNIT void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state) { u64 num, den, res; struct drm_crtc_state *new_crtc_state = &dm_new_crtc_state->base; @@ -11933,6 +5669,7 @@ static void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state) res = div_u64(num, den); dm_new_crtc_state->freesync_config.fixed_refresh_in_uhz = res; } +EXPORT_IF_KUNIT(set_freesync_fixed_config); static int dm_update_crtc_state(struct amdgpu_display_manager *dm, struct drm_atomic_commit *state, @@ -11986,7 +5723,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) goto skip_modeset; - new_stream = create_validate_stream_for_sink(connector, + new_stream = amdgpu_dm_create_validate_stream_for_sink(connector, &new_crtc_state->mode, dm_new_conn_state, dm_old_crtc_state->stream); @@ -12014,7 +5751,7 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, dm_new_crtc_state->abm_level = dm_new_conn_state->abm_level; - ret = fill_hdr_info_packet(drm_new_conn_state, + ret = amdgpu_dm_fill_hdr_info_packet(drm_new_conn_state, &new_stream->hdr_static_metadata); if (ret) goto fail; @@ -12083,11 +5820,11 @@ static int dm_update_crtc_state(struct amdgpu_display_manager *dm, goto skip_modeset; } else if (amdgpu_freesync_vid_mode && aconnector && - is_freesync_video_mode(&new_crtc_state->mode, + amdgpu_dm_is_freesync_video_mode(&new_crtc_state->mode, aconnector)) { struct drm_display_mode *high_mode; - high_mode = get_highest_refresh_rate_mode(aconnector, false); + high_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false); if (!drm_mode_equal(&new_crtc_state->mode, high_mode)) set_freesync_fixed_config(dm_new_crtc_state); } @@ -12179,7 +5916,7 @@ skip_modeset: /* Scaling or underscan settings */ if (is_scaling_state_different(dm_old_conn_state, dm_new_conn_state) || drm_atomic_crtc_needs_modeset(new_crtc_state)) - update_stream_scaling_settings(adev_to_drm(adev), + amdgpu_dm_update_stream_scaling_settings(adev_to_drm(adev), &new_crtc_state->mode, dm_new_conn_state, dm_new_crtc_state->stream); /* ABM settings */ @@ -12275,10 +6012,6 @@ static bool should_reset_plane(struct drm_atomic_commit *state, if (new_crtc_state->color_mgmt_changed) return true; - /* Plane color pipeline or its colorop changes. */ - if (new_plane_state->color_mgmt_changed) - return true; - /* * On zpos change, planes need to be reordered by removing and re-adding * them one by one to the dc state, in order of descending zpos. @@ -12653,7 +6386,7 @@ static int dm_update_plane_state(struct dc *dc, /* Tell DC to do a full surface update every time there * is a plane change. Inefficient, but works for now. */ - dm_new_plane_state->dc_state->update_flags.bits.full_update = 1; + dm_new_plane_state->dc_state->update_bits.full_update = 1; *lock_and_validation_needed = true; } @@ -12672,8 +6405,8 @@ out: return ret; } -static void dm_get_oriented_plane_size(struct drm_plane_state *plane_state, - int *src_w, int *src_h) +STATIC_IFN_KUNIT void dm_get_oriented_plane_size(struct drm_plane_state *plane_state, + int *src_w, int *src_h) { switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) { case DRM_MODE_ROTATE_90: @@ -12689,8 +6422,9 @@ static void dm_get_oriented_plane_size(struct drm_plane_state *plane_state, break; } } +EXPORT_IF_KUNIT(dm_get_oriented_plane_size); -static void +STATIC_IFN_KUNIT void dm_get_plane_scale(struct drm_plane_state *plane_state, int *out_plane_scale_w, int *out_plane_scale_h) { @@ -12700,6 +6434,7 @@ dm_get_plane_scale(struct drm_plane_state *plane_state, *out_plane_scale_w = plane_src_w ? plane_state->crtc_w * 1000 / plane_src_w : 0; *out_plane_scale_h = plane_src_h ? plane_state->crtc_h * 1000 / plane_src_h : 0; } +EXPORT_IF_KUNIT(dm_get_plane_scale); /* * The normalized_zpos value cannot be used by this iterator directly. It's only @@ -13560,373 +7295,6 @@ fail: return ret; } -static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm, - unsigned int offset, - unsigned int total_length, - u8 *data, - unsigned int length, - struct amdgpu_hdmi_vsdb_info *vsdb) -{ - bool res; - union dmub_rb_cmd cmd; - struct dmub_cmd_send_edid_cea *input; - struct dmub_cmd_edid_cea_output *output; - - if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES) - return false; - - memset(&cmd, 0, sizeof(cmd)); - - input = &cmd.edid_cea.data.input; - - cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA; - cmd.edid_cea.header.sub_type = 0; - cmd.edid_cea.header.payload_bytes = - sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header); - input->offset = offset; - input->length = length; - input->cea_total_length = total_length; - memcpy(input->payload, data, length); - - res = dc_wake_and_execute_dmub_cmd(dm->dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY); - if (!res) { - drm_err(adev_to_drm(dm->adev), "EDID CEA parser failed\n"); - return false; - } - - output = &cmd.edid_cea.data.output; - - if (output->type == DMUB_CMD__EDID_CEA_ACK) { - if (!output->ack.success) { - drm_err(adev_to_drm(dm->adev), "EDID CEA ack failed at offset %d\n", - output->ack.offset); - } - } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) { - if (!output->amd_vsdb.vsdb_found) - return false; - - vsdb->freesync_supported = output->amd_vsdb.freesync_supported; - vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version; - vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate; - vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate; - vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code; - } else { - drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n"); - return false; - } - - return true; -} - -static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm, - u8 *edid_ext, int len, - struct amdgpu_hdmi_vsdb_info *vsdb_info) -{ - int i; - - /* send extension block to DMCU for parsing */ - for (i = 0; i < len; i += 8) { - bool res; - int offset; - - /* send 8 bytes a time */ - if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8)) - return false; - - if (i+8 == len) { - /* EDID block sent completed, expect result */ - int version, min_rate, max_rate; - - res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate); - if (res) { - /* amd vsdb found */ - vsdb_info->freesync_supported = 1; - vsdb_info->amd_vsdb_version = version; - vsdb_info->min_refresh_rate_hz = min_rate; - vsdb_info->max_refresh_rate_hz = max_rate; - /* Not enabled on DMCU*/ - vsdb_info->freesync_mccs_vcp_code = 0; - return true; - } - /* not amd vsdb */ - return false; - } - - /* check for ack*/ - res = dc_edid_parser_recv_cea_ack(dm->dc, &offset); - if (!res) - return false; - } - - return false; -} - -static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm, - u8 *edid_ext, int len, - struct amdgpu_hdmi_vsdb_info *vsdb_info) -{ - int i; - - /* send extension block to DMCU for parsing */ - for (i = 0; i < len; i += 8) { - /* send 8 bytes a time */ - if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info)) - return false; - } - - return vsdb_info->freesync_supported; -} - -static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector, - u8 *edid_ext, int len, - struct amdgpu_hdmi_vsdb_info *vsdb_info) -{ - struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev); - bool ret; - - mutex_lock(&adev->dm.dc_lock); - if (adev->dm.dmub_srv) - ret = parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info); - else - ret = parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info); - mutex_unlock(&adev->dm.dc_lock); - return ret; -} - -static void parse_edid_displayid_vrr(struct drm_connector *connector, - const struct edid *edid) -{ - u8 *edid_ext = NULL; - int i; - int j = 0; - u16 min_vfreq; - u16 max_vfreq; - - if (!edid || !edid->extensions) - return; - - /* Find DisplayID extension */ - for (i = 0; i < edid->extensions; i++) { - edid_ext = (void *)(edid + (i + 1)); - if (edid_ext[0] == DISPLAYID_EXT) - break; - } - - if (i == edid->extensions) - return; - - while (j < EDID_LENGTH) { - /* Get dynamic video timing range from DisplayID if available */ - if (EDID_LENGTH - j > 13 && edid_ext[j] == 0x25 && - (edid_ext[j+1] & 0xFE) == 0 && (edid_ext[j+2] == 9)) { - min_vfreq = edid_ext[j+9]; - if (edid_ext[j+1] & 7) - max_vfreq = edid_ext[j+10] + ((edid_ext[j+11] & 3) << 8); - else - max_vfreq = edid_ext[j+10]; - - if (max_vfreq && min_vfreq) { - connector->display_info.monitor_range.max_vfreq = max_vfreq; - connector->display_info.monitor_range.min_vfreq = min_vfreq; - - return; - } - } - j++; - } -} - -static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector, - struct amdgpu_hdmi_vsdb_info *vsdb_info) -{ - struct drm_connector *connector = &aconnector->base; - - vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode; - vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version; - - return connector->display_info.amd_vsdb.version != 0; -} - -static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, - const struct edid *edid, - struct amdgpu_hdmi_vsdb_info *vsdb_info) -{ - u8 *edid_ext = NULL; - int i; - bool valid_vsdb_found = false; - - /*----- drm_find_cea_extension() -----*/ - /* No EDID or EDID extensions */ - if (edid == NULL || edid->extensions == 0) - return -ENODEV; - - /* Find CEA extension */ - for (i = 0; i < edid->extensions; i++) { - edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1); - if (edid_ext[0] == CEA_EXT) - break; - } - - if (i == edid->extensions) - return -ENODEV; - - /*----- cea_db_offsets() -----*/ - if (edid_ext[0] != CEA_EXT) - return -ENODEV; - - valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info); - - return valid_vsdb_found ? i : -ENODEV; -} - -/** - * amdgpu_dm_update_freesync_caps - Update Freesync capabilities - * - * @connector: Connector to query. - * @drm_edid: DRM EDID from monitor - * @do_mccs: Controls whether MCCS (Monitor Control Command Set) over - * DDC (Display Data Channel) transactions are performed. When true, - * the driver queries the monitor to get or update additional FreeSync - * capability information. When false, these transactions are skipped. - * - * Amdgpu supports Freesync in DP and HDMI displays, and it is required to keep - * track of some of the display information in the internal data struct used by - * amdgpu_dm. This function checks which type of connector we need to set the - * FreeSync parameters. - */ -void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, - const struct drm_edid *drm_edid, bool do_mccs) -{ - int i = 0; - struct amdgpu_dm_connector *amdgpu_dm_connector = - to_amdgpu_dm_connector(connector); - struct dm_connector_state *dm_con_state = NULL; - struct dc_sink *sink; - struct amdgpu_device *adev = drm_to_adev(connector->dev); - struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; - const struct edid *edid; - bool freesync_capable = false; - enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE; - - if (!connector->state) { - drm_err(adev_to_drm(adev), "%s - Connector has no state", __func__); - goto update; - } - - sink = amdgpu_dm_connector->dc_sink ? - amdgpu_dm_connector->dc_sink : - amdgpu_dm_connector->dc_em_sink; - - drm_edid_connector_update(connector, drm_edid); - - if (!drm_edid || !sink) { - dm_con_state = to_dm_connector_state(connector->state); - - amdgpu_dm_connector->min_vfreq = 0; - amdgpu_dm_connector->max_vfreq = 0; - freesync_capable = false; - - goto update; - } - - dm_con_state = to_dm_connector_state(connector->state); - - if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version)) - goto update; - - edid = drm_edid_raw(drm_edid); // FIXME: Get rid of drm_edid_raw() - - /* Some eDP panels only have the refresh rate range info in DisplayID */ - if ((connector->display_info.monitor_range.min_vfreq == 0 || - connector->display_info.monitor_range.max_vfreq == 0)) - parse_edid_displayid_vrr(connector, edid); - - if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || - sink->sink_signal == SIGNAL_TYPE_EDP)) { - if (amdgpu_dm_connector->dc_link && - amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) { - amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq; - amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; - } - - get_amd_vsdb(amdgpu_dm_connector, &vsdb_info); - - if (vsdb_info.replay_mode) { - amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode; - amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version; - amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP; - } - - } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) { - i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); - if (i >= 0) { - amdgpu_dm_connector->vsdb_info = vsdb_info; - sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; - - if (vsdb_info.freesync_supported) { - amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; - amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; - - connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; - connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; - } - } - } - - if (amdgpu_dm_connector->dc_link) - as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link); - - if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) { - i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); - if (i >= 0) { - amdgpu_dm_connector->vsdb_info = vsdb_info; - sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; - - if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) { - amdgpu_dm_connector->pack_sdp_v1_3 = true; - amdgpu_dm_connector->as_type = as_type; - - amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; - amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; - if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) - freesync_capable = true; - - connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; - connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; - } - } - } - - /* Handle MCCS */ - if (do_mccs) { - dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); - - if (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported) - freesync_capable = false; - - if (sink->mccs_caps.freesync_supported && freesync_capable) - dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); - } - -update: - if (dm_con_state) - dm_con_state->freesync_capable = freesync_capable; - - if (connector->state && amdgpu_dm_connector->dc_link && !freesync_capable && - amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported) { - amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported = false; - amdgpu_dm_connector->dc_link->replay_settings.replay_feature_enabled = false; - } - - if (connector->vrr_capable_property) - drm_connector_set_vrr_capable_property(connector, - freesync_capable); -} - void amdgpu_dm_trigger_timing_sync(struct drm_device *dev) { struct amdgpu_device *adev = drm_to_adev(dev); @@ -13946,12 +7314,6 @@ void amdgpu_dm_trigger_timing_sync(struct drm_device *dev) mutex_unlock(&adev->dm.dc_lock); } -static inline void amdgpu_dm_exit_ips_for_hw_access(struct dc *dc) -{ - if (dc->ctx->dmub_srv && !dc->ctx->dmub_srv->idle_exit_counter) - dc_exit_ips_for_hw_access(dc); -} - void dm_write_reg_func(const struct dc_context *ctx, uint32_t address, u32 value, const char *func_name) { @@ -13980,13 +7342,6 @@ uint32_t dm_read_reg_func(const struct dc_context *ctx, uint32_t address, } #endif - if (ctx->dmub_srv && - ctx->dmub_srv->reg_helper_offload.gather_in_progress && - !ctx->dmub_srv->reg_helper_offload.should_burst_write) { - ASSERT(false); - return 0; - } - amdgpu_dm_exit_ips_for_hw_access(ctx->dc); value = cgs_read_register(ctx->cgs_device, address); @@ -13996,179 +7351,6 @@ uint32_t dm_read_reg_func(const struct dc_context *ctx, uint32_t address, return value; } -int amdgpu_dm_process_dmub_aux_transfer_sync( - struct dc_context *ctx, - unsigned int link_index, - struct aux_payload *payload, - enum aux_return_code_type *operation_result) -{ - struct amdgpu_device *adev = ctx->driver_context; - struct dmub_notification *p_notify = adev->dm.dmub_notify; - int ret = -1; - - mutex_lock(&adev->dm.dpia_aux_lock); - if (!dc_process_dmub_aux_transfer_async(ctx->dc, link_index, payload)) { - *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE; - goto out; - } - - if (!wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) { - drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!"); - *operation_result = AUX_RET_ERROR_TIMEOUT; - goto out; - } - - if (p_notify->result != AUX_RET_SUCCESS) { - /* - * Transient states before tunneling is enabled could - * lead to this error. We can ignore this for now. - */ - if (p_notify->result == AUX_RET_ERROR_PROTOCOL_ERROR) { - drm_warn(adev_to_drm(adev), "DPIA AUX failed on 0x%x(%d), error %d\n", - payload->address, payload->length, - p_notify->result); - } - *operation_result = p_notify->result; - goto out; - } - - payload->reply[0] = adev->dm.dmub_notify->aux_reply.command & 0xF; - if (adev->dm.dmub_notify->aux_reply.command & 0xF0) - /* The reply is stored in the top nibble of the command. */ - payload->reply[0] = (adev->dm.dmub_notify->aux_reply.command >> 4) & 0xF; - - /*write req may receive a byte indicating partially written number as well*/ - if (p_notify->aux_reply.length) - memcpy(payload->data, p_notify->aux_reply.data, - p_notify->aux_reply.length); - - /* success */ - ret = p_notify->aux_reply.length; - *operation_result = p_notify->result; -out: - reinit_completion(&adev->dm.dmub_aux_transfer_done); - mutex_unlock(&adev->dm.dpia_aux_lock); - return ret; -} - -static void abort_fused_io( - struct dc_context *ctx, - const struct dmub_cmd_fused_request *request -) -{ - union dmub_rb_cmd command = { 0 }; - struct dmub_rb_cmd_fused_io *io = &command.fused_io; - - io->header.type = DMUB_CMD__FUSED_IO; - io->header.sub_type = DMUB_CMD__FUSED_IO_ABORT; - io->header.payload_bytes = sizeof(*io) - sizeof(io->header); - io->request = *request; - dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT); -} - -static bool execute_fused_io( - struct amdgpu_device *dev, - struct dc_context *ctx, - union dmub_rb_cmd *commands, - uint8_t count, - uint32_t timeout_us -) -{ - const uint8_t ddc_line = commands[0].fused_io.request.u.aux.ddc_line; - - if (ddc_line >= ARRAY_SIZE(dev->dm.fused_io)) - return false; - - struct fused_io_sync *sync = &dev->dm.fused_io[ddc_line]; - struct dmub_rb_cmd_fused_io *first = &commands[0].fused_io; - const bool result = dm_execute_dmub_cmd_list(ctx, count, commands, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) - && first->header.ret_status - && first->request.status == FUSED_REQUEST_STATUS_SUCCESS; - - if (!result) - return false; - - while (wait_for_completion_timeout(&sync->replied, usecs_to_jiffies(timeout_us))) { - reinit_completion(&sync->replied); - - struct dmub_cmd_fused_request *reply = (struct dmub_cmd_fused_request *) sync->reply_data; - - static_assert(sizeof(*reply) <= sizeof(sync->reply_data), "Size mismatch"); - - if (reply->identifier == first->request.identifier) { - first->request = *reply; - return true; - } - } - - reinit_completion(&sync->replied); - first->request.status = FUSED_REQUEST_STATUS_TIMEOUT; - abort_fused_io(ctx, &first->request); - return false; -} - -bool amdgpu_dm_execute_fused_io( - struct amdgpu_device *dev, - struct dc_link *link, - union dmub_rb_cmd *commands, - uint8_t count, - uint32_t timeout_us) -{ - struct amdgpu_display_manager *dm = &dev->dm; - - mutex_lock(&dm->dpia_aux_lock); - - const bool result = execute_fused_io(dev, link->ctx, commands, count, timeout_us); - - mutex_unlock(&dm->dpia_aux_lock); - return result; -} - -int amdgpu_dm_process_dmub_set_config_sync( - struct dc_context *ctx, - unsigned int link_index, - struct set_config_cmd_payload *payload, - enum set_config_status *operation_result) -{ - struct amdgpu_device *adev = ctx->driver_context; - bool is_cmd_complete; - int ret; - - mutex_lock(&adev->dm.dpia_aux_lock); - is_cmd_complete = dc_process_dmub_set_config_async(ctx->dc, - link_index, payload, adev->dm.dmub_notify); - - if (is_cmd_complete || wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) { - ret = 0; - *operation_result = adev->dm.dmub_notify->sc_status; - } else { - drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!"); - ret = -1; - *operation_result = SET_CONFIG_UNKNOWN_ERROR; - } - - if (!is_cmd_complete) - reinit_completion(&adev->dm.dmub_aux_transfer_done); - mutex_unlock(&adev->dm.dpia_aux_lock); - return ret; -} - -bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type) -{ - struct amdgpu_device *adev = ctx->driver_context; - - guard(spinlock_irqsave)(&adev->dm.dmub_lock); - return dc_dmub_srv_cmd_run(ctx->dmub_srv, cmd, wait_type); -} - -bool dm_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type) -{ - struct amdgpu_device *adev = ctx->driver_context; - - guard(spinlock_irqsave)(&adev->dm.dmub_lock); - return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type); -} - void dm_acpi_process_phy_transition_interlock( const struct dc_context *ctx, struct dm_process_phy_transition_init_params process_phy_transition_init_params) 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 dd199e0b7922..91affbdb2d6c 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -877,6 +877,9 @@ struct amdgpu_dm_connector { unsigned int hdmi_hpd_debounce_delay_ms; struct delayed_work hdmi_hpd_debounce_work; struct dc_sink *hdmi_prev_sink; + + /* HDMI compliance automation */ + bool hdmi_comp_auto; }; static inline void amdgpu_dm_set_mst_status(uint8_t *status, @@ -1061,35 +1064,7 @@ struct dm_connector_state { #define to_dm_connector_state(x)\ container_of((x), struct dm_connector_state, base) -void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector); -struct drm_connector_state * -amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector); -int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector, - struct drm_connector_state *state, - struct drm_property *property, - uint64_t val); - -int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector, - const struct drm_connector_state *state, - struct drm_property *property, - uint64_t *val); - -int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev); - -void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, - struct amdgpu_dm_connector *aconnector, - int connector_type, - struct dc_link *link, - int link_index); - -enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector, - const struct drm_display_mode *mode); - -void dm_restore_drm_connector_state(struct drm_device *dev, - struct drm_connector *connector); - -void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, - const struct drm_edid *drm_edid, bool do_mccs); +#include "amdgpu_dm_connector.h" void amdgpu_dm_trigger_timing_sync(struct drm_device *dev); @@ -1113,14 +1088,9 @@ int amdgpu_dm_update_plane_color_mgmt(struct dm_crtc_state *crtc, struct drm_plane_state *plane_state, struct dc_plane_state *dc_plane_state); -void amdgpu_dm_update_connector_after_detect( - struct amdgpu_dm_connector *aconnector); - void populate_hdmi_info_from_connector(bool enable_frl, struct drm_hdmi_info *info, struct dc_edid_caps *edid_caps); -extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs; - int amdgpu_dm_process_dmub_aux_transfer_sync(struct dc_context *ctx, unsigned int link_index, struct aux_payload *payload, enum aux_return_code_type *operation_result); @@ -1135,20 +1105,9 @@ bool amdgpu_dm_execute_fused_io( int amdgpu_dm_process_dmub_set_config_sync(struct dc_context *ctx, unsigned int link_index, struct set_config_cmd_payload *payload, enum set_config_status *operation_result); -struct dc_stream_state * - create_validate_stream_for_sink(struct drm_connector *connector, - const struct drm_display_mode *drm_mode, - const struct dm_connector_state *dm_state, - const struct dc_stream_state *old_stream); - int dm_atomic_get_state(struct drm_atomic_commit *state, struct dm_atomic_state **dm_state); -struct drm_connector * -amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state, - struct drm_crtc *crtc); - -int convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth); struct idle_workqueue *idle_create_workqueue(struct amdgpu_device *adev); void *dm_allocate_gpu_mem(struct amdgpu_device *adev, @@ -1161,11 +1120,33 @@ void dm_free_gpu_mem(struct amdgpu_device *adev, bool amdgpu_dm_is_headless(struct amdgpu_device *adev); -void hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector); -void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector); -int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector); +bool amdgpu_dm_crtc_complete_writeback(struct amdgpu_crtc *acrtc); void retrieve_dmi_info(struct amdgpu_display_manager *dm); -void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm, int bl_idx); +void amdgpu_dm_emulated_link_detect(struct dc_link *link); +void amdgpu_dm_apply_delay_after_dpcd_poweroff(struct amdgpu_device *adev, + struct dc_sink *sink); + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +int dm_plane_layer_index_cmp(const void *a, const void *b); +int fill_plane_color_attributes(const struct drm_plane_state *plane_state, + const enum surface_pixel_format format, + enum dc_color_space *color_space); +bool modereset_required(struct drm_crtc_state *crtc_state); +void dm_get_oriented_plane_size(struct drm_plane_state *plane_state, + int *src_w, int *src_h); +void dm_get_plane_scale(struct drm_plane_state *plane_state, + int *out_plane_scale_w, int *out_plane_scale_h); +bool is_scaling_state_different(const struct dm_connector_state *dm_state, + const struct dm_connector_state *old_dm_state); +bool is_timing_unchanged_for_freesync(struct drm_crtc_state *old_crtc_state, + struct drm_crtc_state *new_crtc_state); +void set_freesync_fixed_config(struct dm_crtc_state *dm_new_crtc_state); +bool is_dc_timing_adjust_needed(struct dm_crtc_state *old_state, + struct dm_crtc_state *new_state); +void set_multisync_trigger_params(struct dc_stream_state *stream); +void set_master_stream(struct dc_stream_state *stream_set[], int stream_count); +#endif + #endif /* __AMDGPU_DM_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c new file mode 100644 index 000000000000..13c9a9d145ba --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.c @@ -0,0 +1,323 @@ +// 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 + */ + +#include "amdgpu.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_audio.h" +#include "amdgpu_dm_kunit_helpers.h" +#include "dc.h" + +#include <linux/component.h> +#include <drm/drm_atomic.h> +#include <drm/drm_audio_component.h> +#include <drm/drm_connector.h> +#include <drm/drm_edid.h> +#include <drm/drm_eld.h> + +#include "dc/inc/core_types.h" + +static int amdgpu_dm_audio_component_get_eld(struct device *kdev, int port, + int pipe, bool *enabled, + unsigned char *buf, int max_bytes) +{ + struct drm_device *dev = dev_get_drvdata(kdev); + struct amdgpu_device *adev = drm_to_adev(dev); + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; + struct amdgpu_dm_connector *aconnector; + int ret = 0; + + *enabled = false; + + mutex_lock(&adev->dm.audio_lock); + + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + continue; + + aconnector = to_amdgpu_dm_connector(connector); + if (aconnector->audio_inst != port) + continue; + + *enabled = true; + mutex_lock(&connector->eld_mutex); + ret = drm_eld_size(connector->eld); + memcpy(buf, connector->eld, min(max_bytes, ret)); + mutex_unlock(&connector->eld_mutex); + + break; + } + drm_connector_list_iter_end(&conn_iter); + + mutex_unlock(&adev->dm.audio_lock); + + drm_dbg_kms(adev_to_drm(adev), "Get ELD : idx=%d ret=%d en=%d\n", port, ret, *enabled); + + return ret; +} + +static const struct drm_audio_component_ops amdgpu_dm_audio_component_ops = { + .get_eld = amdgpu_dm_audio_component_get_eld, +}; + +STATIC_IFN_KUNIT int amdgpu_dm_audio_component_bind(struct device *kdev, + struct device *hda_kdev, void *data) +{ + struct drm_device *dev = dev_get_drvdata(kdev); + struct amdgpu_device *adev = drm_to_adev(dev); + struct drm_audio_component *acomp = data; + + acomp->ops = &amdgpu_dm_audio_component_ops; + acomp->dev = kdev; + adev->dm.audio_component = acomp; + + return 0; +} +EXPORT_IF_KUNIT(amdgpu_dm_audio_component_bind); + +STATIC_IFN_KUNIT void amdgpu_dm_audio_component_unbind(struct device *kdev, + struct device *hda_kdev, void *data) +{ + struct amdgpu_device *adev = drm_to_adev(dev_get_drvdata(kdev)); + struct drm_audio_component *acomp = data; + + acomp->ops = NULL; + acomp->dev = NULL; + adev->dm.audio_component = NULL; +} +EXPORT_IF_KUNIT(amdgpu_dm_audio_component_unbind); + +static const struct component_ops amdgpu_dm_audio_component_bind_ops = { + .bind = amdgpu_dm_audio_component_bind, + .unbind = amdgpu_dm_audio_component_unbind, +}; + +int amdgpu_dm_audio_init(struct amdgpu_device *adev) +{ + int i, ret; + + if (!amdgpu_audio) + return 0; + + adev->mode_info.audio.enabled = true; + + adev->mode_info.audio.num_pins = adev->dm.dc->res_pool->audio_count; + + for (i = 0; i < adev->mode_info.audio.num_pins; i++) { + adev->mode_info.audio.pin[i].channels = -1; + adev->mode_info.audio.pin[i].rate = -1; + adev->mode_info.audio.pin[i].bits_per_sample = -1; + adev->mode_info.audio.pin[i].status_bits = 0; + adev->mode_info.audio.pin[i].category_code = 0; + adev->mode_info.audio.pin[i].connected = false; + adev->mode_info.audio.pin[i].id = + adev->dm.dc->res_pool->audios[i]->inst; + adev->mode_info.audio.pin[i].offset = 0; + } + + ret = component_add(adev->dev, &amdgpu_dm_audio_component_bind_ops); + if (ret < 0) + return ret; + + adev->dm.audio_registered = true; + + return 0; +} +EXPORT_IF_KUNIT(amdgpu_dm_audio_init); + +void amdgpu_dm_audio_fini(struct amdgpu_device *adev) +{ + if (!amdgpu_audio) + return; + + if (!adev->mode_info.audio.enabled) + return; + + if (adev->dm.audio_registered) { + component_del(adev->dev, &amdgpu_dm_audio_component_bind_ops); + adev->dm.audio_registered = false; + } + + /* TODO: Disable audio? */ + + adev->mode_info.audio.enabled = false; +} +EXPORT_IF_KUNIT(amdgpu_dm_audio_fini); + +STATIC_IFN_KUNIT void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin) +{ + struct drm_audio_component *acomp = adev->dm.audio_component; + + if (acomp && acomp->audio_ops && acomp->audio_ops->pin_eld_notify) { + drm_dbg_kms(adev_to_drm(adev), "Notify ELD: %d\n", pin); + + acomp->audio_ops->pin_eld_notify(acomp->audio_ops->audio_ptr, + pin, -1); + } +} +EXPORT_IF_KUNIT(amdgpu_dm_audio_eld_notify); + +void amdgpu_dm_fill_audio_info(struct audio_info *audio_info, + const struct drm_connector *drm_connector, + const struct dc_sink *dc_sink) +{ + int i = 0; + int cea_revision = 0; + const struct dc_edid_caps *edid_caps = &dc_sink->edid_caps; + + audio_info->manufacture_id = edid_caps->manufacturer_id; + audio_info->product_id = edid_caps->product_id; + + cea_revision = drm_connector->display_info.cea_rev; + + strscpy(audio_info->display_name, + edid_caps->display_name, + AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS); + + if (cea_revision >= 3) { + audio_info->mode_count = edid_caps->audio_mode_count; + + for (i = 0; i < audio_info->mode_count; ++i) { + audio_info->modes[i].format_code = + (enum audio_format_code) + (edid_caps->audio_modes[i].format_code); + audio_info->modes[i].channel_count = + edid_caps->audio_modes[i].channel_count; + audio_info->modes[i].sample_rates.all = + edid_caps->audio_modes[i].sample_rate; + audio_info->modes[i].sample_size = + edid_caps->audio_modes[i].sample_size; + } + } + + audio_info->flags.all = edid_caps->speaker_flags; + + /* TODO: We only check for the progressive mode, check for interlace mode too */ + if (drm_connector->latency_present[0]) { + audio_info->video_latency = drm_connector->video_latency[0]; + audio_info->audio_latency = drm_connector->audio_latency[0]; + } + + /* TODO: For DP, video and audio latency should be calculated from DPCD caps */ + +} +EXPORT_IF_KUNIT(amdgpu_dm_fill_audio_info); + +void amdgpu_dm_commit_audio(struct drm_device *dev, + struct drm_atomic_commit *state) +{ + struct amdgpu_device *adev = drm_to_adev(dev); + struct amdgpu_dm_connector *aconnector; + struct drm_connector *connector; + struct drm_connector_state *old_con_state, *new_con_state; + struct drm_crtc_state *new_crtc_state; + struct dm_crtc_state *new_dm_crtc_state; + const struct dc_stream_status *status; + int i, inst; + + /* Notify device removals. */ + for_each_oldnew_connector_in_state(state, connector, old_con_state, new_con_state, i) { + if (old_con_state->crtc != new_con_state->crtc) { + /* CRTC changes require notification. */ + goto notify; + } + + if (!new_con_state->crtc) + continue; + + new_crtc_state = drm_atomic_get_new_crtc_state( + state, new_con_state->crtc); + + if (!new_crtc_state) + continue; + + if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) + continue; + +notify: + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + continue; + + aconnector = to_amdgpu_dm_connector(connector); + + mutex_lock(&adev->dm.audio_lock); + inst = aconnector->audio_inst; + aconnector->audio_inst = -1; + mutex_unlock(&adev->dm.audio_lock); + + amdgpu_dm_audio_eld_notify(adev, inst); + } + + /* Notify audio device additions. */ + for_each_new_connector_in_state(state, connector, new_con_state, i) { + if (!new_con_state->crtc) + continue; + + new_crtc_state = drm_atomic_get_new_crtc_state( + state, new_con_state->crtc); + + if (!new_crtc_state) + continue; + + if (!drm_atomic_crtc_needs_modeset(new_crtc_state)) + continue; + + new_dm_crtc_state = to_dm_crtc_state(new_crtc_state); + if (!new_dm_crtc_state->stream) + continue; + + status = dc_stream_get_status(new_dm_crtc_state->stream); + if (!status) + continue; + + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + continue; + + aconnector = to_amdgpu_dm_connector(connector); + + mutex_lock(&adev->dm.audio_lock); + inst = status->audio_inst; + aconnector->audio_inst = inst; + mutex_unlock(&adev->dm.audio_lock); + + amdgpu_dm_audio_eld_notify(adev, inst); + } +} + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +int amdgpu_dm_audio_get_param(void) +{ + return amdgpu_audio; +} +EXPORT_IF_KUNIT(amdgpu_dm_audio_get_param); + +void amdgpu_dm_audio_set_param(int val) +{ + amdgpu_audio = val; +} +EXPORT_IF_KUNIT(amdgpu_dm_audio_set_param); +#endif diff --git a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.h index bf0c5901b4ee..7acfc5ef69b3 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_audio.h @@ -1,4 +1,6 @@ -/* Copyright 2018 Advanced Micro Devices, Inc. +/* 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"), @@ -19,21 +21,36 @@ * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD - * */ -#include "power_helpers.h" -#include "dc/inc/hw/dmcu.h" -#include "dc/inc/hw/abm.h" -#include "dc.h" -#include "core_types.h" -#include "dmub_cmd.h" +#ifndef __AMDGPU_DM_AUDIO_H__ +#define __AMDGPU_DM_AUDIO_H__ + +struct amdgpu_device; +struct drm_device; +struct drm_atomic_state; +struct drm_connector; +struct audio_info; +struct dc_sink; + +int amdgpu_dm_audio_init(struct amdgpu_device *adev); +void amdgpu_dm_audio_fini(struct amdgpu_device *adev); +void amdgpu_dm_commit_audio(struct drm_device *dev, + struct drm_atomic_commit *state); +void amdgpu_dm_fill_audio_info(struct audio_info *audio_info, + const struct drm_connector *drm_connector, + const struct dc_sink *dc_sink); + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +struct device; -#define DIV_ROUNDUP(a, b) (((a)+((b)/2))/(b)) -#define bswap16_based_on_endian(big_endian, value) \ - ((big_endian) ? cpu_to_be16(value) : cpu_to_le16(value)) +int amdgpu_dm_audio_component_bind(struct device *kdev, + struct device *hda_kdev, void *data); +void amdgpu_dm_audio_component_unbind(struct device *kdev, + struct device *hda_kdev, void *data); +void amdgpu_dm_audio_eld_notify(struct amdgpu_device *adev, int pin); +int amdgpu_dm_audio_get_param(void); +void amdgpu_dm_audio_set_param(int val); +#endif -bool mod_power_only_edp(const struct dc_state *context, const struct dc_stream_state *stream) -{ - return context && context->stream_count == 1 && dc_is_embedded_signal(stream->signal); -} +#endif /* __AMDGPU_DM_AUDIO_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c new file mode 100644 index 000000000000..33f4be403a65 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.c @@ -0,0 +1,726 @@ +// 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 + */ + +#include "dc.h" +#include "dc/dc_dmub_srv.h" +#include "dc/dc_state.h" +#include "dc/dc_stat.h" + +#include "amdgpu.h" +#include "amdgpu_display.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_backlight.h" +#include "amdgpu_dm_psr.h" +#include "amdgpu_dm_replay.h" +#include "amdgpu_atombios.h" + +#include "modules/inc/mod_power.h" + +#include <linux/backlight.h> +#include <linux/power_supply.h> +#include <drm/drm_edid.h> +#include <drm/drm_utils.h> + +#include <acpi/video.h> + +#include "amdgpu_dm_trace.h" +#include "amd_shared.h" +#include "amdgpu_dm_kunit_helpers.h" + +void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm, + int bl_idx) +{ + struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[bl_idx]; + + if (caps->caps_valid) + return; + +#if defined(CONFIG_ACPI) + amdgpu_acpi_get_backlight_caps(caps); + + /* validate the firmware value is sane */ + if (caps->caps_valid) { + int spread = caps->max_input_signal - caps->min_input_signal; + + if (caps->max_input_signal > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT || + caps->min_input_signal < 0 || + spread > AMDGPU_DM_DEFAULT_MAX_BACKLIGHT || + spread < AMDGPU_DM_MIN_SPREAD) { + drm_dbg_kms(adev_to_drm(dm->adev), "DM: Invalid backlight caps: min=%d, max=%d\n", + caps->min_input_signal, caps->max_input_signal); + caps->caps_valid = false; + } + } +#else + if (caps->aux_support) + return; +#endif + if (!caps->caps_valid) { + caps->min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps->max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps->ac_level = caps->dc_level = 50; + caps->caps_valid = true; + } +} +EXPORT_IF_KUNIT(amdgpu_dm_update_backlight_caps); + +STATIC_IFN_KUNIT +int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps, + unsigned int *min, unsigned int *max) +{ + if (!caps) + return 0; + + if (caps->aux_support) { + /* Firmware limits are in nits, DC API wants millinits. */ + *max = 1000 * caps->aux_max_input_signal; + *min = 1000 * caps->aux_min_input_signal; + } else { + /* Firmware limits are 8-bit, PWM control is 16-bit. */ + *max = 0x101 * caps->max_input_signal; + *min = 0x101 * caps->min_input_signal; + } + return 1; +} +EXPORT_IF_KUNIT(get_brightness_range); + +/* Rescale from [min..max] to [0..AMDGPU_MAX_BL_LEVEL] */ +static inline u32 scale_input_to_fw(int min, int max, u64 input) +{ + return DIV_ROUND_CLOSEST_ULL(input * AMDGPU_MAX_BL_LEVEL, max - min); +} + +/* Rescale from [0..AMDGPU_MAX_BL_LEVEL] to [min..max] */ +static inline u32 scale_fw_to_input(int min, int max, u64 input) +{ + return min + DIV_ROUND_CLOSEST_ULL(input * (max - min), AMDGPU_MAX_BL_LEVEL); +} + +STATIC_IFN_KUNIT +void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps, + unsigned int min, unsigned int max, + uint32_t *user_brightness) +{ + u32 brightness = scale_input_to_fw(min, max, *user_brightness); + u8 lower_signal, upper_signal, upper_lum, lower_lum, lum; + int left, right; + + if (amdgpu_dc_debug_mask & DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE) + return; + + if (!caps->data_points) + return; + + /* + * Handle the case where brightness is below the first data point + * Interpolate between (0,0) and (first_signal, first_lum) + */ + if (brightness < caps->luminance_data[0].input_signal) { + lum = DIV_ROUND_CLOSEST(caps->luminance_data[0].luminance * brightness, + caps->luminance_data[0].input_signal); + goto scale; + } + + left = 0; + right = caps->data_points - 1; + while (left <= right) { + int mid = left + (right - left) / 2; + u8 signal = caps->luminance_data[mid].input_signal; + + /* Exact match found */ + if (signal == brightness) { + lum = caps->luminance_data[mid].luminance; + goto scale; + } + + if (signal < brightness) + left = mid + 1; + else + right = mid - 1; + } + + /* verify bound */ + if (left >= caps->data_points) + left = caps->data_points - 1; + + /* At this point, left > right */ + lower_signal = caps->luminance_data[right].input_signal; + upper_signal = caps->luminance_data[left].input_signal; + lower_lum = caps->luminance_data[right].luminance; + upper_lum = caps->luminance_data[left].luminance; + + /* interpolate */ + if (right == left || !lower_lum) + lum = upper_lum; + else + lum = lower_lum + DIV_ROUND_CLOSEST((upper_lum - lower_lum) * + (brightness - lower_signal), + upper_signal - lower_signal); +scale: + *user_brightness = scale_fw_to_input(min, max, + DIV_ROUND_CLOSEST(lum * brightness, 101)); +} + +EXPORT_IF_KUNIT(convert_custom_brightness); + +STATIC_IFN_KUNIT +u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps, + uint32_t brightness) +{ + unsigned int min, max; + + if (!get_brightness_range(caps, &min, &max)) + return brightness; + + convert_custom_brightness(caps, min, max, &brightness); + + /* Rescale 0..max to min..max */ + return min + DIV_ROUND_CLOSEST_ULL((u64)(max - min) * brightness, max); +} + +EXPORT_IF_KUNIT(convert_brightness_from_user); + +STATIC_IFN_KUNIT +u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps, + uint32_t brightness) +{ + unsigned int min, max; + + if (!get_brightness_range(caps, &min, &max)) + return brightness; + + if (brightness < min) + return 0; + /* Rescale min..max to 0..max */ + return DIV_ROUND_CLOSEST_ULL((u64)max * (brightness - min), + max - min); +} +EXPORT_IF_KUNIT(convert_brightness_to_user); + +static struct dc_stream_state *dm_find_stream_with_link( + struct amdgpu_display_manager *dm, + struct dc_link *link) +{ + struct dc_state *cur_dc_state = dm->dc->current_state; + struct dc_stream_state *stream = NULL; + int i; + + for (i = 0; i < cur_dc_state->stream_count; i++) { + stream = cur_dc_state->streams[i]; + if (stream->link == link) + return stream; + } + + return NULL; +} + +STATIC_IFN_KUNIT +int amdgpu_dm_backlight_get_device_index(struct amdgpu_display_manager *dm, + struct backlight_device *bd) +{ + int i; + + for (i = 0; i < dm->num_of_edps; i++) { + if (bd == dm->backlight_dev[i]) + return i; + } + + return 0; +} +EXPORT_IF_KUNIT(amdgpu_dm_backlight_get_device_index); + +void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, + int bl_idx, + u32 user_brightness) +{ + struct amdgpu_dm_backlight_caps *caps; + struct dc_link *link; + u32 brightness = 0; + bool rc = false, reallow_idle = false; + struct drm_connector *connector; + struct dc_stream_state *stream; + unsigned int min, max; + + list_for_each_entry(connector, &dm->ddev->mode_config.connector_list, head) { + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + + if (aconnector->bl_idx != bl_idx) + continue; + + /* if connector is off, save the brightness for next time it's on */ + if (!aconnector->base.encoder) { + dm->brightness[bl_idx] = user_brightness; + dm->actual_brightness[bl_idx] = 0; + return; + } + } + + amdgpu_dm_update_backlight_caps(dm, bl_idx); + caps = &dm->backlight_caps[bl_idx]; + + dm->brightness[bl_idx] = user_brightness; + /* update scratch register */ + if (bl_idx == 0) + amdgpu_atombios_scratch_regs_set_backlight_level(dm->adev, dm->brightness[bl_idx]); + brightness = convert_brightness_from_user(caps, dm->brightness[bl_idx]); + link = (struct dc_link *)dm->backlight_link[bl_idx]; + + /* Apply brightness quirk */ + if (caps->brightness_mask) + brightness |= caps->brightness_mask; + + if (trace_amdgpu_dm_brightness_enabled()) { + trace_amdgpu_dm_brightness(__builtin_return_address(0), + user_brightness, + brightness, + caps->aux_support, + power_supply_is_system_supplied() > 0); + } + + stream = dm_find_stream_with_link(dm, link); + if (!stream) + return; + + mutex_lock(&dm->dc_lock); + if (dm->dc->caps.ips_support && dm->dc->ctx->dmub_srv->idle_allowed) { + dc_allow_idle_optimizations(dm->dc, false); + reallow_idle = true; + } + + if (caps->aux_support) { + rc = mod_power_set_backlight_nits(dm->power_module, stream, brightness, + AUX_BL_DEFAULT_TRANSITION_TIME_MS, false, true); + } else { + /* power module uses millipercent */ + get_brightness_range(caps, &min, &max); + brightness = DIV_ROUND_CLOSEST(brightness * 100, (max - min)) * 1000; + rc = mod_power_set_backlight_percent(dm->power_module, stream, + brightness, 0, false); + } + + /* + * Some kms clients create a ramped backlight transition effect + * by rapidly changing the backlight. Yet we must wait on dmcub + * fw to exit psr/replay before programming backlight. To + * prevent lag, keep disable psr/replay and let the next atomic + * flip clear the event. + * + * ToDo: use ISM to handle rapidly backlight change + * + * Rapidly backlight change is similar to rapidly cursor events, + * which is now handled by ISM. ISM can delay the event until system + * is really idle, so we may use ISM to handle backlight change as well. + */ + amdgpu_dm_psr_set_event(dm, stream, true, + psr_event_hw_programming, true); + amdgpu_dm_replay_set_event(dm, stream, true, + replay_event_hw_programming, true); + + if (dm->dc->caps.ips_support && reallow_idle) + dc_allow_idle_optimizations(dm->dc, true); + + mutex_unlock(&dm->dc_lock); + + if (rc) + dm->actual_brightness[bl_idx] = user_brightness; +} + +static int amdgpu_dm_backlight_update_status(struct backlight_device *bd) +{ + struct amdgpu_display_manager *dm = bl_get_data(bd); + int i = amdgpu_dm_backlight_get_device_index(dm, bd); + + amdgpu_dm_backlight_set_level(dm, i, bd->props.brightness); + + return 0; +} + +static u32 amdgpu_dm_backlight_get_level(struct amdgpu_display_manager *dm, + int bl_idx) +{ + int ret; + struct amdgpu_dm_backlight_caps caps; + struct dc_link *link = (struct dc_link *)dm->backlight_link[bl_idx]; + + amdgpu_dm_update_backlight_caps(dm, bl_idx); + caps = dm->backlight_caps[bl_idx]; + + if (caps.aux_support) { + u32 avg, peak; + + if (!dc_link_get_backlight_level_nits(link, &avg, &peak)) + return dm->brightness[bl_idx]; + return convert_brightness_to_user(&caps, avg); + } + + ret = dc_link_get_backlight_level(link); + + if (ret == DC_ERROR_UNEXPECTED) + return dm->brightness[bl_idx]; + + return convert_brightness_to_user(&caps, ret); +} + +static int amdgpu_dm_backlight_get_brightness(struct backlight_device *bd) +{ + struct amdgpu_display_manager *dm = bl_get_data(bd); + int i = amdgpu_dm_backlight_get_device_index(dm, bd); + + return amdgpu_dm_backlight_get_level(dm, i); +} + +static const struct backlight_ops amdgpu_dm_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = amdgpu_dm_backlight_get_brightness, + .update_status = amdgpu_dm_backlight_update_status, +}; + +STATIC_IFN_KUNIT +void amdgpu_dm_backlight_fill_props(const struct amdgpu_dm_backlight_caps *caps, + bool is_system_supplied, + bool custom_curve_enabled, + struct backlight_properties *props) +{ + unsigned int min, max; + + if (get_brightness_range(caps, &min, &max)) { + if (is_system_supplied) + props->brightness = DIV_ROUND_CLOSEST((max - min) * caps->ac_level, + 100); + else + props->brightness = DIV_ROUND_CLOSEST((max - min) * caps->dc_level, + 100); + props->max_brightness = max - min; + } else { + props->brightness = MAX_BACKLIGHT_LEVEL; + props->max_brightness = MAX_BACKLIGHT_LEVEL; + } + + if (caps && caps->data_points && custom_curve_enabled) + props->scale = BACKLIGHT_SCALE_NON_LINEAR; + else + props->scale = BACKLIGHT_SCALE_LINEAR; + props->type = BACKLIGHT_RAW; +} +EXPORT_IF_KUNIT(amdgpu_dm_backlight_fill_props); + +void +amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector) +{ + struct drm_device *drm = aconnector->base.dev; + struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm; + struct backlight_properties props = { 0 }; + struct amdgpu_dm_backlight_caps *caps; + char bl_name[16]; + int real_brightness; + int init_brightness; + + if (aconnector->bl_idx == -1) + return; + + if (!acpi_video_backlight_use_native()) { + drm_info(drm, "Skipping amdgpu DM backlight registration\n"); + /* Try registering an ACPI video backlight device instead. */ + acpi_video_register_backlight(); + return; + } + + caps = &dm->backlight_caps[aconnector->bl_idx]; + amdgpu_dm_backlight_fill_props(caps, power_supply_is_system_supplied() > 0, + !(amdgpu_dc_debug_mask & + DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE), + &props); + drm_dbg(drm, "Backlight caps: max_brightness: %d, ac %d, dc %d\n", + props.max_brightness, caps->ac_level, caps->dc_level); + + init_brightness = props.brightness; + + if (props.scale == BACKLIGHT_SCALE_NON_LINEAR) + drm_info(drm, "Using custom brightness curve\n"); + + snprintf(bl_name, sizeof(bl_name), "amdgpu_bl%d", + drm->primary->index + aconnector->bl_idx); + + dm->backlight_dev[aconnector->bl_idx] = + backlight_device_register(bl_name, aconnector->base.kdev, dm, + &amdgpu_dm_backlight_ops, &props); + dm->brightness[aconnector->bl_idx] = props.brightness; + + if (IS_ERR(dm->backlight_dev[aconnector->bl_idx])) { + drm_err(drm, "DM: Backlight registration failed!\n"); + dm->backlight_dev[aconnector->bl_idx] = NULL; + } else { + /* + * dm->brightness[x] can be inconsistent just after startup until + * ops.get_brightness is called. + */ + real_brightness = + amdgpu_dm_backlight_ops.get_brightness(dm->backlight_dev[aconnector->bl_idx]); + + if (real_brightness != init_brightness) { + dm->actual_brightness[aconnector->bl_idx] = real_brightness; + dm->brightness[aconnector->bl_idx] = real_brightness; + } + drm_dbg_driver(drm, "DM: Registered Backlight device: %s\n", bl_name); + } +} + +void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector) +{ + const struct drm_panel_backlight_quirk *panel_backlight_quirk; + struct amdgpu_dm_backlight_caps *caps; + struct drm_connector *conn_base; + struct amdgpu_device *adev; + struct drm_luminance_range_info *luminance_range; + struct drm_device *drm; + + if (aconnector->bl_idx == -1 || + aconnector->dc_link->connector_signal != SIGNAL_TYPE_EDP) + return; + + conn_base = &aconnector->base; + drm = conn_base->dev; + adev = drm_to_adev(drm); + + caps = &adev->dm.backlight_caps[aconnector->bl_idx]; + caps->ext_caps = &aconnector->dc_link->dpcd_sink_ext_caps; + caps->aux_support = false; + + if (caps->ext_caps->bits.oled == 1 + /* + * || + * caps->ext_caps->bits.sdr_aux_backlight_control == 1 || + * caps->ext_caps->bits.hdr_aux_backlight_control == 1 + */) + caps->aux_support = true; + + if (amdgpu_backlight == 0) + caps->aux_support = false; + else if (amdgpu_backlight == 1) + caps->aux_support = true; + if (caps->aux_support) + aconnector->dc_link->backlight_control_type = BACKLIGHT_CONTROL_AMD_AUX; + + luminance_range = &conn_base->display_info.luminance_range; + + if (luminance_range->max_luminance) + caps->aux_max_input_signal = luminance_range->max_luminance; + else + caps->aux_max_input_signal = 512; + + if (luminance_range->min_luminance) + caps->aux_min_input_signal = luminance_range->min_luminance; + else + caps->aux_min_input_signal = 1; + + panel_backlight_quirk = + drm_get_panel_backlight_quirk(aconnector->drm_edid); + if (!IS_ERR_OR_NULL(panel_backlight_quirk)) { + if (panel_backlight_quirk->min_brightness) { + caps->min_input_signal = + panel_backlight_quirk->min_brightness - 1; + drm_info(drm, + "Applying panel backlight quirk, min_brightness: %d\n", + caps->min_input_signal); + } + if (panel_backlight_quirk->brightness_mask) { + drm_info(drm, + "Applying panel backlight quirk, brightness_mask: 0x%X\n", + panel_backlight_quirk->brightness_mask); + caps->brightness_mask = + panel_backlight_quirk->brightness_mask; + } + } +} +EXPORT_IF_KUNIT(amdgpu_dm_update_connector_ext_caps); + +void amdgpu_dm_setup_backlight_device(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector) +{ + struct amdgpu_dm_backlight_caps *caps; + struct dc_link *link = aconnector->dc_link; + int bl_idx = dm->num_of_edps; + + if (!(link->connector_signal & (SIGNAL_TYPE_EDP | SIGNAL_TYPE_LVDS)) || + link->type == dc_connection_none) + return; + + if (dm->num_of_edps >= AMDGPU_DM_MAX_NUM_EDP) { + drm_warn(adev_to_drm(dm->adev), "Too much eDP connections, skipping backlight setup for additional eDPs\n"); + return; + } + + aconnector->bl_idx = bl_idx; + + amdgpu_dm_update_backlight_caps(dm, bl_idx); + dm->backlight_link[bl_idx] = link; + dm->num_of_edps++; + + amdgpu_dm_update_connector_ext_caps(aconnector); + caps = &dm->backlight_caps[aconnector->bl_idx]; + + /* Only offer ABM property when non-OLED and user didn't turn off by module parameter */ + if (caps->ext_caps && !caps->ext_caps->bits.oled && amdgpu_dm_abm_level < 0) + drm_object_attach_property(&aconnector->base.base, + dm->adev->mode_info.abm_level_property, + ABM_SYSFS_CONTROL); +} +EXPORT_IF_KUNIT(amdgpu_dm_setup_backlight_device); + +/** + * DOC: panel power savings + * + * The display manager allows you to set your desired **panel power savings** + * level (between 0-4, with 0 representing off), e.g. using the following:: + * + * # echo 3 > /sys/class/drm/card0-eDP-1/amdgpu/panel_power_savings + * + * Modifying this value can have implications on color accuracy, so tread + * carefully. + */ + +static ssize_t panel_power_savings_show(struct device *device, + struct device_attribute *attr, + char *buf) +{ + struct drm_connector *connector = dev_get_drvdata(device); + struct drm_device *dev = connector->dev; + u8 val; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + val = to_dm_connector_state(connector->state)->abm_level == + ABM_LEVEL_IMMEDIATE_DISABLE ? 0 : + to_dm_connector_state(connector->state)->abm_level; + drm_modeset_unlock(&dev->mode_config.connection_mutex); + + return sysfs_emit(buf, "%u\n", val); +} + +static ssize_t panel_power_savings_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct drm_connector *connector = dev_get_drvdata(device); + struct drm_device *dev = connector->dev; + long val; + int ret; + + ret = kstrtol(buf, 0, &val); + + if (ret) + return ret; + + if (val < 0 || val > 4) + return -EINVAL; + + drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); + if (to_dm_connector_state(connector->state)->abm_sysfs_forbidden) + ret = -EBUSY; + else + to_dm_connector_state(connector->state)->abm_level = val ?: + ABM_LEVEL_IMMEDIATE_DISABLE; + drm_modeset_unlock(&dev->mode_config.connection_mutex); + + if (ret) + return ret; + + drm_kms_helper_hotplug_event(dev); + + return count; +} + +static DEVICE_ATTR_RW(panel_power_savings); + +static struct attribute *amdgpu_attrs[] = { + &dev_attr_panel_power_savings.attr, + NULL +}; + +const struct attribute_group amdgpu_group = { + .name = "amdgpu", + .attrs = amdgpu_attrs +}; + +bool +amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *amdgpu_dm_connector) +{ + if (amdgpu_dm_abm_level >= 0) + return false; + + if (amdgpu_dm_connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) + return false; + + /* check for OLED panels */ + if (amdgpu_dm_connector->bl_idx >= 0) { + struct drm_device *drm = amdgpu_dm_connector->base.dev; + struct amdgpu_display_manager *dm = &drm_to_adev(drm)->dm; + struct amdgpu_dm_backlight_caps *caps; + + caps = &dm->backlight_caps[amdgpu_dm_connector->bl_idx]; + if (caps->aux_support) + return false; + } + + return true; +} +EXPORT_IF_KUNIT(amdgpu_dm_should_create_sysfs); + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +uint amdgpu_dm_get_dc_debug_mask(void) +{ + return amdgpu_dc_debug_mask; +} +EXPORT_IF_KUNIT(amdgpu_dm_get_dc_debug_mask); + +void amdgpu_dm_set_dc_debug_mask(uint val) +{ + amdgpu_dc_debug_mask = val; +} +EXPORT_IF_KUNIT(amdgpu_dm_set_dc_debug_mask); + +int amdgpu_dm_get_abm_level_param(void) +{ + return amdgpu_dm_abm_level; +} +EXPORT_IF_KUNIT(amdgpu_dm_get_abm_level_param); + +void amdgpu_dm_set_abm_level_param(int val) +{ + amdgpu_dm_abm_level = val; +} +EXPORT_IF_KUNIT(amdgpu_dm_set_abm_level_param); + +int amdgpu_dm_get_backlight_param(void) +{ + return amdgpu_backlight; +} +EXPORT_IF_KUNIT(amdgpu_dm_get_backlight_param); + +void amdgpu_dm_set_backlight_param(int val) +{ + amdgpu_backlight = val; +} +EXPORT_IF_KUNIT(amdgpu_dm_set_backlight_param); +#endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h new file mode 100644 index 000000000000..98d612c60ae9 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_backlight.h @@ -0,0 +1,75 @@ +/* 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. + */ + +#ifndef __AMDGPU_DM_BACKLIGHT_H__ +#define __AMDGPU_DM_BACKLIGHT_H__ + +struct amdgpu_display_manager; +struct amdgpu_dm_connector; +struct backlight_device; +struct backlight_properties; +struct drm_connector; +struct attribute_group; + +#define AMDGPU_DM_DEFAULT_MIN_BACKLIGHT 12 +#define AMDGPU_DM_DEFAULT_MAX_BACKLIGHT 255 +#define AMDGPU_DM_MIN_SPREAD ((AMDGPU_DM_DEFAULT_MAX_BACKLIGHT - AMDGPU_DM_DEFAULT_MIN_BACKLIGHT) / 2) +#define AUX_BL_DEFAULT_TRANSITION_TIME_MS 50 + +void amdgpu_dm_update_backlight_caps(struct amdgpu_display_manager *dm, + int bl_idx); +void amdgpu_dm_backlight_set_level(struct amdgpu_display_manager *dm, + int bl_idx, u32 user_brightness); +void amdgpu_dm_register_backlight_device(struct amdgpu_dm_connector *aconnector); +void amdgpu_dm_setup_backlight_device(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector); +void amdgpu_dm_update_connector_ext_caps(struct amdgpu_dm_connector *aconnector); +bool amdgpu_dm_should_create_sysfs(struct amdgpu_dm_connector *aconnector); + +extern const struct attribute_group amdgpu_group; + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +int get_brightness_range(const struct amdgpu_dm_backlight_caps *caps, + unsigned int *min, unsigned int *max); +void convert_custom_brightness(const struct amdgpu_dm_backlight_caps *caps, + unsigned int min, unsigned int max, + uint32_t *user_brightness); +u32 convert_brightness_from_user(const struct amdgpu_dm_backlight_caps *caps, + uint32_t brightness); +u32 convert_brightness_to_user(const struct amdgpu_dm_backlight_caps *caps, + uint32_t brightness); +int amdgpu_dm_backlight_get_device_index(struct amdgpu_display_manager *dm, + struct backlight_device *bd); +void amdgpu_dm_backlight_fill_props(const struct amdgpu_dm_backlight_caps *caps, + bool is_system_supplied, + bool custom_curve_enabled, + struct backlight_properties *props); +uint amdgpu_dm_get_dc_debug_mask(void); +void amdgpu_dm_set_dc_debug_mask(uint val); +int amdgpu_dm_get_abm_level_param(void); +void amdgpu_dm_set_abm_level_param(int val); +int amdgpu_dm_get_backlight_param(void); +void amdgpu_dm_set_backlight_param(int val); +#endif + +#endif /* __AMDGPU_DM_BACKLIGHT_H__ */ 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 86086d10c543..357c7c5c85cf 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 @@ -1051,26 +1051,28 @@ EXPORT_IF_KUNIT(__drm_3dlut32_to_dc_3dlut); /* amdgpu_dm_atomic_lut3d - set DRM 3D LUT to DC stream * @drm_lut3d: user 3D LUT * @drm_lut3d_size: size of 3D LUT - * @lut3d: DC 3D LUT + * @cm: DC Color Manager (includes 3D LUT) * * Map user 3D LUT data to DC 3D LUT and all necessary bits to program it * on DCN accordingly. */ STATIC_IFN_KUNIT void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d, uint32_t drm_lut3d_size, - struct dc_3dlut *lut) + struct dc_plane_cm *cm) { if (!drm_lut3d_size) { - lut->state.bits.initialized = 0; + cm->lut3d_func.state.bits.initialized = 0; + cm->flags.bits.lut3d_enable = 0; } else { /* Stride and bit depth are not programmable by API yet. * Therefore, only supports 17x17x17 3D LUT (12-bit). */ - lut->lut_3d.use_tetrahedral_9 = false; - lut->lut_3d.use_12bits = true; - lut->state.bits.initialized = 1; - __drm_3dlut_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &lut->lut_3d, - lut->lut_3d.use_tetrahedral_9, + cm->lut3d_func.lut_3d.use_tetrahedral_9 = false; + cm->lut3d_func.lut_3d.use_12bits = true; + cm->lut3d_func.state.bits.initialized = 1; + cm->flags.bits.lut3d_enable = 1; + __drm_3dlut_to_dc_3dlut(drm_lut3d, drm_lut3d_size, &cm->lut3d_func.lut_3d, + cm->lut3d_func.lut_3d.use_tetrahedral_9, MAX_COLOR_3DLUT_BITDEPTH); } } @@ -1080,7 +1082,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *sha bool has_rom, enum dc_transfer_func_predefined tf, uint32_t shaper_size, - struct dc_transfer_func *func_shaper) + struct dc_plane_cm *cm) { int ret = 0; @@ -1089,10 +1091,13 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *sha * If user shaper LUT is set, we assume a linear color space * (linearized by degamma 1D LUT or not). */ - __set_tf_distributed_points(func_shaper, tf); - ret = __set_output_tf(func_shaper, shaper_lut, shaper_size, has_rom); + __set_tf_distributed_points(&cm->shaper_func, tf); + cm->flags.bits.shaper_enable = 1; + + ret = __set_output_tf(&cm->shaper_func, shaper_lut, shaper_size, has_rom); } else { - __set_tf_bypass(func_shaper); + __set_tf_bypass(&cm->shaper_func); + cm->flags.bits.shaper_enable = 0; } return ret; @@ -1103,7 +1108,7 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blen bool has_rom, enum dc_transfer_func_predefined tf, uint32_t blend_size, - struct dc_transfer_func *func_blend) + struct dc_plane_cm *cm) { int ret = 0; @@ -1115,10 +1120,13 @@ STATIC_IFN_KUNIT int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blen * module to fill the parameters that will be translated to HW * points. */ - __set_tf_distributed_points(func_blend, tf); - ret = __set_input_tf(NULL, func_blend, blend_lut, blend_size); + __set_tf_distributed_points(&cm->blend_func, tf); + cm->flags.bits.blend_enable = 1; + + ret = __set_input_tf(NULL, &cm->blend_func, blend_lut, blend_size); } else { - __set_tf_bypass(func_blend); + __set_tf_bypass(&cm->blend_func); + cm->flags.bits.blend_enable = 0; } return ret; @@ -1461,7 +1469,7 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state, const struct drm_color_lut *degamma_lut; enum amdgpu_transfer_function tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT; uint32_t degamma_size; - bool has_degamma_lut; + bool has_degamma_lut, is_subsampled_format; int ret; degamma_lut = __extract_blob_lut(dm_plane_state->degamma_lut, @@ -1491,12 +1499,20 @@ __set_dm_plane_degamma(struct drm_plane_state *plane_state, if (ret) return ret; } else { - dc_plane_state->in_transfer_func.type = - TF_TYPE_PREDEFINED; + /* Check if format requires post-scale color processing (subsampled formats) */ + is_subsampled_format = (dc_plane_state->format >= SURFACE_PIXEL_FORMAT_VIDEO_BEGIN && + dc_plane_state->format < SURFACE_PIXEL_FORMAT_SUBSAMPLE_END); + + dc_plane_state->in_transfer_func.type = TF_TYPE_PREDEFINED; if (!mod_color_calculate_degamma_params(color_caps, - &dc_plane_state->in_transfer_func, NULL, false)) + &dc_plane_state->in_transfer_func, + NULL, + is_subsampled_format)) { + drm_err(plane_state->state->dev, + "Failed to calculate degamma params.\n"); return -ENOMEM; + } } return 0; } @@ -1632,63 +1648,64 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, struct drm_colorop *colorop) { struct drm_colorop *old_colorop; - struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; + struct drm_colorop_state *new_colorop_state; + struct drm_colorop_state *tf_state = NULL, *lut_state = NULL; struct drm_atomic_commit *state = plane_state->state; + struct drm_colorop *lut_colorop; enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR; - struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; + struct dc_transfer_func *tf = &dc_plane_state->cm.shaper_func; const struct drm_color_lut32 *shaper_lut; struct drm_device *dev = colorop->dev; bool enabled = false; u32 shaper_size; int i = 0, ret = 0; - /* 1D Curve - SHAPER TF */ + /* 1D Curve - SHAPER TF: find state */ old_colorop = colorop; for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_shaper_tfs)) { - colorop_state = new_colorop_state; + tf_state = new_colorop_state; break; } } - if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE) { - drm_dbg(dev, "Shaper TF colorop with ID: %d\n", colorop->base.id); - tf->type = TF_TYPE_DISTRIBUTED_POINTS; - tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); - tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; - ret = __set_output_tf(tf, 0, 0, false); - if (ret) - return ret; - enabled = true; - } - - /* 1D LUT - SHAPER LUT */ - colorop = old_colorop->next; - if (!colorop) { + /* 1D LUT - SHAPER LUT: find state */ + lut_colorop = old_colorop->next; + if (!lut_colorop) { drm_dbg(dev, "no Shaper LUT colorop found\n"); return -EINVAL; } - old_colorop = colorop; for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { - if (new_colorop_state->colorop == old_colorop && + if (new_colorop_state->colorop == lut_colorop && new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) { - colorop_state = new_colorop_state; + lut_state = new_colorop_state; break; } } - if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_LUT) { - drm_dbg(dev, "Shaper LUT colorop with ID: %d\n", colorop->base.id); + if (tf_state && !tf_state->bypass) { + drm_dbg(dev, "Shaper TF colorop with ID: %d\n", old_colorop->base.id); + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(tf_state->curve_1d_type); + tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; + ret = __set_output_tf(tf, 0, 0, false); + if (ret) + return ret; + enabled = true; + } + + if (lut_state && !lut_state->bypass) { + drm_dbg(dev, "Shaper LUT colorop with ID: %d\n", lut_colorop->base.id); tf->type = TF_TYPE_DISTRIBUTED_POINTS; tf->tf = default_tf; tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; - shaper_lut = __extract_blob_lut32(colorop_state->data, &shaper_size); + shaper_lut = __extract_blob_lut32(lut_state->data, &shaper_size); shaper_size = shaper_lut != NULL ? shaper_size : 0; /* Custom LUT size must be the same as supported size */ - if (shaper_size == colorop->size) { + if (shaper_size == lut_colorop->size) { ret = __set_output_tf_32(tf, shaper_lut, shaper_size, false); if (ret) return ret; @@ -1696,8 +1713,12 @@ __set_dm_plane_colorop_shaper(struct drm_plane_state *plane_state, } } - if (!enabled) + if (!enabled) { tf->type = TF_TYPE_BYPASS; + dc_plane_state->cm.flags.bits.shaper_enable = 0; + } else { + dc_plane_state->cm.flags.bits.shaper_enable = 1; + } return 0; } @@ -1741,7 +1762,7 @@ __set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state, { struct drm_colorop *old_colorop; struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; - struct dc_transfer_func *tf = &dc_plane_state->in_shaper_func; + struct dc_transfer_func *tf = &dc_plane_state->cm.shaper_func; struct drm_atomic_commit *state = plane_state->state; const struct amdgpu_device *adev = drm_to_adev(colorop->dev); bool has_3dlut = adev->dm.dc->caps.color.dpp.hw_3d_lut || adev->dm.dc->caps.color.mpc.preblend; @@ -1769,13 +1790,15 @@ __set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state, drm_dbg(dev, "3D LUT colorop with ID: %d\n", colorop->base.id); lut3d = __extract_blob_lut32(colorop_state->data, &lut3d_size); lut3d_size = lut3d != NULL ? lut3d_size : 0; - ret = __set_colorop_3dlut(lut3d, lut3d_size, &dc_plane_state->lut3d_func); + ret = __set_colorop_3dlut(lut3d, lut3d_size, &dc_plane_state->cm.lut3d_func); if (ret) { drm_dbg(dev, "3D LUT colorop with ID: %d has LUT size = %d\n", colorop->base.id, lut3d_size); return ret; } + dc_plane_state->cm.flags.bits.lut3d_enable = 1; + /* 3D LUT requires shaper. If shaper colorop is bypassed, enable shaper curve * with TRANSFER_FUNCTION_LINEAR */ @@ -1785,6 +1808,8 @@ __set_dm_plane_colorop_3dlut(struct drm_plane_state *plane_state, tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; ret = __set_output_tf_32(tf, NULL, 0, false); } + } else { + dc_plane_state->cm.flags.bits.lut3d_enable = 0; } return ret; @@ -1796,61 +1821,64 @@ __set_dm_plane_colorop_blend(struct drm_plane_state *plane_state, struct drm_colorop *colorop) { struct drm_colorop *old_colorop; - struct drm_colorop_state *colorop_state = NULL, *new_colorop_state; + struct drm_colorop_state *new_colorop_state; + struct drm_colorop_state *tf_state = NULL, *lut_state = NULL; struct drm_atomic_commit *state = plane_state->state; + struct drm_colorop *lut_colorop; enum dc_transfer_func_predefined default_tf = TRANSFER_FUNCTION_LINEAR; - struct dc_transfer_func *tf = &dc_plane_state->blend_tf; + struct dc_transfer_func *tf = &dc_plane_state->cm.blend_func; const struct drm_color_lut32 *blend_lut = NULL; struct drm_device *dev = colorop->dev; uint32_t blend_size = 0; int i = 0; - /* 1D Curve - BLND TF */ + dc_plane_state->cm.flags.bits.blend_enable = 0; + + /* 1D Curve - BLND TF: find state */ old_colorop = colorop; for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == old_colorop && (BIT(new_colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) { - colorop_state = new_colorop_state; + tf_state = new_colorop_state; break; } } - if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_CURVE && - (BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) { - drm_dbg(dev, "Blend TF colorop with ID: %d\n", colorop->base.id); - tf->type = TF_TYPE_DISTRIBUTED_POINTS; - tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(colorop_state->curve_1d_type); - tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; - __set_input_tf_32(NULL, tf, blend_lut, blend_size); - } - - /* 1D Curve - BLND LUT */ - colorop = old_colorop->next; - if (!colorop) { + /* 1D LUT - BLND LUT: find state */ + lut_colorop = old_colorop->next; + if (!lut_colorop) { drm_dbg(dev, "no Blend LUT colorop found\n"); return -EINVAL; } - old_colorop = colorop; for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { - if (new_colorop_state->colorop == old_colorop && + if (new_colorop_state->colorop == lut_colorop && new_colorop_state->colorop->type == DRM_COLOROP_1D_LUT) { - colorop_state = new_colorop_state; + lut_state = new_colorop_state; break; } } - if (colorop_state && !colorop_state->bypass && colorop->type == DRM_COLOROP_1D_LUT && - (BIT(colorop_state->curve_1d_type) & amdgpu_dm_supported_blnd_tfs)) { - drm_dbg(dev, "Blend LUT colorop with ID: %d\n", colorop->base.id); + if (tf_state && !tf_state->bypass) { + drm_dbg(dev, "Blend TF colorop with ID: %d\n", old_colorop->base.id); + tf->type = TF_TYPE_DISTRIBUTED_POINTS; + tf->tf = default_tf = amdgpu_colorop_tf_to_dc_tf(tf_state->curve_1d_type); + tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; + dc_plane_state->cm.flags.bits.blend_enable = 1; + __set_input_tf_32(NULL, tf, blend_lut, blend_size); + } + + if (lut_state && !lut_state->bypass) { + drm_dbg(dev, "Blend LUT colorop with ID: %d\n", lut_colorop->base.id); tf->type = TF_TYPE_DISTRIBUTED_POINTS; tf->tf = default_tf; tf->sdr_ref_white_level = SDR_WHITE_LEVEL_INIT_VALUE; - blend_lut = __extract_blob_lut32(colorop_state->data, &blend_size); + dc_plane_state->cm.flags.bits.blend_enable = 1; + blend_lut = __extract_blob_lut32(lut_state->data, &blend_size); blend_size = blend_lut != NULL ? blend_size : 0; /* Custom LUT size must be the same as supported size */ - if (blend_size == colorop->size) + if (blend_size == lut_colorop->size) __set_input_tf_32(NULL, tf, blend_lut, blend_size); } @@ -1876,11 +1904,11 @@ amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, lut3d = __extract_blob_lut(dm_plane_state->lut3d, &lut3d_size); lut3d_size = lut3d != NULL ? lut3d_size : 0; - amdgpu_dm_atomic_lut3d(lut3d, lut3d_size, &dc_plane_state->lut3d_func); + amdgpu_dm_atomic_lut3d(lut3d, lut3d_size, &dc_plane_state->cm); ret = amdgpu_dm_atomic_shaper_lut(shaper_lut, false, amdgpu_tf_to_dc_tf(shaper_tf), shaper_size, - &dc_plane_state->in_shaper_func); + &dc_plane_state->cm); if (ret) { drm_dbg_kms(plane_state->plane->dev, "setting plane %d shaper LUT failed.\n", @@ -1895,7 +1923,8 @@ amdgpu_dm_plane_set_color_properties(struct drm_plane_state *plane_state, ret = amdgpu_dm_atomic_blend_lut(blend_lut, false, amdgpu_tf_to_dc_tf(blend_tf), - blend_size, &dc_plane_state->blend_tf); + blend_size, &dc_plane_state->cm); + if (ret) { drm_dbg_kms(plane_state->plane->dev, "setting plane %d gamma lut failed.\n", 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 index e4f53b7bc753..8dbbcb3ab156 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_color.h @@ -87,10 +87,10 @@ void __drm_3dlut32_to_dc_3dlut(const struct drm_color_lut32 *lut, struct tetrahedral_params *params, bool use_tetrahedral_9, int bit_depth); -struct dc_3dlut; +struct dc_plane_cm; void amdgpu_dm_atomic_lut3d(const struct drm_color_lut *drm_lut3d, uint32_t drm_lut3d_size, - struct dc_3dlut *lut); + struct dc_plane_cm *cm); int __set_colorop_3dlut(const struct drm_color_lut32 *drm_lut3d, uint32_t drm_lut3d_size, struct dc_3dlut *lut); @@ -105,12 +105,12 @@ int amdgpu_dm_atomic_shaper_lut(const struct drm_color_lut *shaper_lut, bool has_rom, enum dc_transfer_func_predefined tf, uint32_t shaper_size, - struct dc_transfer_func *func_shaper); + struct dc_plane_cm *cm); int amdgpu_dm_atomic_blend_lut(const struct drm_color_lut *blend_lut, bool has_rom, enum dc_transfer_func_predefined tf, uint32_t blend_size, - struct dc_transfer_func *func_blend); + struct dc_plane_cm *cm); int __set_colorop_in_tf_1d_curve(struct dc_plane_state *dc_plane_state, struct drm_colorop_state *colorop_state); #endif 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 48f5c431eaf9..056a76b88f43 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 @@ -235,3 +235,4 @@ int amdgpu_dm_initialize_default_pipeline(struct drm_plane *plane, struct drm_pr return amdgpu_dm_build_default_pipeline(dev, plane, hw_3d_lut, list); } +EXPORT_IF_KUNIT(amdgpu_dm_initialize_default_pipeline); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c new file mode 100644 index 000000000000..88f938a8f24f --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.c @@ -0,0 +1,3663 @@ +// 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 + * + */ + +#include "dm_services_types.h" +#include "dc.h" +#include "dc/dc_dmub_srv.h" +#include "dc/dc_edid_parser.h" +#include "dc/dc_stat.h" +#include "dc/dc_state.h" +#include "dc/dc_stream.h" +#include "dc/inc/core_types.h" +#include "link_enc_cfg.h" +#include "link/protocols/link_dpcd.h" +#include "link_service_types.h" +#include "link/protocols/link_dp_capability.h" +#include "link/protocols/link_ddc.h" + +#include "amdgpu.h" +#include "amdgpu_display.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_connector.h" +#include "amdgpu_dm_kunit_helpers.h" +#include "amdgpu_dm_plane.h" +#include "amdgpu_dm_crtc.h" +#include "amdgpu_dm_wb.h" +#include "amdgpu_dm_mst_types.h" +#if defined(CONFIG_DEBUG_FS) +#include "amdgpu_dm_debugfs.h" +#endif +#include "amdgpu_dm_backlight.h" +#include "amdgpu_dm_audio.h" +#include "amdgpu_dm_irq.h" +#include "amdgpu_dm_psr.h" +#include "dm_helpers.h" + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_uapi.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_eld.h> +#include <drm/drm_fixed.h> +#include <drm/drm_mode.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_utils.h> +#include <drm/display/drm_dp_mst_helper.h> +#include <drm/display/drm_hdmi_helper.h> +#include <drm/drm_privacy_screen_consumer.h> +#include <drm/display/drm_hdcp_helper.h> + +#include <linux/backlight.h> + +#include <media/cec-notifier.h> + +#include "modules/inc/mod_freesync.h" +#include "modules/inc/mod_power.h" + +#include "amdgpu_dm_trace.h" + +/* Encoder functions */ + +static void amdgpu_dm_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static const struct drm_encoder_funcs amdgpu_dm_encoder_funcs = { + .destroy = amdgpu_dm_encoder_destroy, +}; + +static void dm_encoder_helper_disable(struct drm_encoder *encoder) +{ +} + +static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct drm_atomic_commit *state = crtc_state->state; + struct drm_connector *connector = conn_state->connector; + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + struct dm_connector_state *dm_new_connector_state = to_dm_connector_state(conn_state); + const struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; + struct drm_dp_mst_topology_mgr *mst_mgr; + struct drm_dp_mst_port *mst_port; + struct drm_dp_mst_topology_state *mst_state; + enum dc_color_depth color_depth; + int clock, bpp = 0; + bool is_y420 = false; + + if ((connector->connector_type == DRM_MODE_CONNECTOR_eDP) || + (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) { + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; + enum drm_mode_status result; + + result = drm_crtc_helper_mode_valid_fixed(encoder->crtc, adjusted_mode, native_mode); + if (result != MODE_OK && dm_new_connector_state->scaling == RMX_OFF) { + drm_dbg_driver(encoder->dev, + "mode %dx%d@%dHz is not native, enabling scaling\n", + adjusted_mode->hdisplay, adjusted_mode->vdisplay, + drm_mode_vrefresh(adjusted_mode)); + dm_new_connector_state->scaling = RMX_ASPECT; + } + return 0; + } + + if (!aconnector->mst_output_port) + return 0; + + mst_port = aconnector->mst_output_port; + mst_mgr = &aconnector->mst_root->mst_mgr; + + if (!crtc_state->connectors_changed && !crtc_state->mode_changed) + return 0; + + mst_state = drm_atomic_get_mst_topology_state(state, mst_mgr); + if (IS_ERR(mst_state)) + return PTR_ERR(mst_state); + + mst_state->pbn_div.full = dm_mst_get_pbn_divider(aconnector->mst_root->dc_link); + + if (!state->duplicated) { + int max_bpc = conn_state->max_requested_bpc; + + is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) && + aconnector->force_yuv420_output; + color_depth = amdgpu_dm_convert_color_depth_from_display_info(connector, + is_y420, + max_bpc); + bpp = amdgpu_dm_convert_dc_color_depth_into_bpc(color_depth) * 3; + clock = adjusted_mode->clock; + dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp << 4); + } + + dm_new_connector_state->vcpi_slots = + drm_dp_atomic_find_time_slots(state, mst_mgr, mst_port, + dm_new_connector_state->pbn); + if (dm_new_connector_state->vcpi_slots < 0) { + drm_dbg_atomic(connector->dev, "failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots); + return dm_new_connector_state->vcpi_slots; + } + return 0; +} + +const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs = { + .disable = dm_encoder_helper_disable, + .atomic_check = dm_encoder_helper_atomic_check +}; + +int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev) +{ + switch (adev->mode_info.num_crtc) { + case 1: + return 0x1; + case 2: + return 0x3; + case 3: + return 0x7; + case 4: + return 0xf; + case 5: + return 0x1f; + case 6: + default: + return 0x3f; + } +} +EXPORT_IF_KUNIT(amdgpu_dm_get_encoder_crtc_mask); + +int amdgpu_dm_encoder_init(struct drm_device *dev, + struct amdgpu_encoder *aencoder, + uint32_t link_index) +{ + struct amdgpu_device *adev = drm_to_adev(dev); + + int res = drm_encoder_init(dev, + &aencoder->base, + &amdgpu_dm_encoder_funcs, + DRM_MODE_ENCODER_TMDS, + NULL); + + aencoder->base.possible_crtcs = amdgpu_dm_get_encoder_crtc_mask(adev); + + if (!res) + aencoder->encoder_id = link_index; + else + aencoder->encoder_id = -1; + + drm_encoder_helper_add(&aencoder->base, &amdgpu_dm_encoder_helper_funcs); + + return res; +} + +STATIC_IFN_KUNIT enum drm_mode_subconnector get_subconnector_type(struct dc_link *link) +{ + switch (link->dpcd_caps.dongle_type) { + case DISPLAY_DONGLE_NONE: + return DRM_MODE_SUBCONNECTOR_Native; + case DISPLAY_DONGLE_DP_VGA_CONVERTER: + return DRM_MODE_SUBCONNECTOR_VGA; + case DISPLAY_DONGLE_DP_DVI_CONVERTER: + case DISPLAY_DONGLE_DP_DVI_DONGLE: + return DRM_MODE_SUBCONNECTOR_DVID; + case DISPLAY_DONGLE_DP_HDMI_CONVERTER: + case DISPLAY_DONGLE_DP_HDMI_DONGLE: + return DRM_MODE_SUBCONNECTOR_HDMIA; + case DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE: + default: + return DRM_MODE_SUBCONNECTOR_Unknown; + } +} +EXPORT_IF_KUNIT(get_subconnector_type); + +static void update_subconnector_property(struct amdgpu_dm_connector *aconnector) +{ + struct dc_link *link = aconnector->dc_link; + struct drm_connector *connector = &aconnector->base; + enum drm_mode_subconnector subconnector = DRM_MODE_SUBCONNECTOR_Unknown; + + if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) + return; + + if (aconnector->dc_sink) + subconnector = get_subconnector_type(link); + + drm_object_property_set_value(&connector->base, + connector->dev->mode_config.dp_subconnector_property, + subconnector); +} + +static int amdgpu_dm_connector_get_modes(struct drm_connector *connector); + +static void amdgpu_dm_fbc_init(struct drm_connector *connector) +{ + struct amdgpu_device *adev = drm_to_adev(connector->dev); + struct dm_compressor_info *compressor = &adev->dm.compressor; + struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(connector); + struct drm_display_mode *mode; + unsigned long max_size = 0; + + if (adev->dm.dc->fbc_compressor == NULL) + return; + + if (aconn->dc_link->connector_signal != SIGNAL_TYPE_EDP) + return; + + if (compressor->bo_ptr) + return; + + + list_for_each_entry(mode, &connector->modes, head) { + if (max_size < (unsigned long) mode->htotal * mode->vtotal) + max_size = (unsigned long) mode->htotal * mode->vtotal; + } + + if (max_size) { + int r = amdgpu_bo_create_kernel(adev, max_size * 4, PAGE_SIZE, + AMDGPU_GEM_DOMAIN_GTT, &compressor->bo_ptr, + &compressor->gpu_addr, &compressor->cpu_addr); + + if (r) + drm_err(adev_to_drm(adev), "DM: Failed to initialize FBC\n"); + else { + adev->dm.dc->ctx->fbc_gpu_addr = compressor->gpu_addr; + drm_info(adev_to_drm(adev), "DM: FBC alloc %lu\n", max_size*4); + } + + } + +} + + +int amdgpu_dm_detect_mst_link_for_all_connectors(struct drm_device *dev) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_connector *connector; + struct drm_connector_list_iter iter; + int ret = 0; + + drm_connector_list_iter_begin(dev, &iter); + drm_for_each_connector_iter(connector, &iter) { + + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + continue; + + aconnector = to_amdgpu_dm_connector(connector); + if (aconnector->dc_link->type == dc_connection_mst_branch && + aconnector->mst_mgr.aux) { + drm_dbg_kms(dev, "DM_MST: starting TM on aconnector: %p [id: %d]\n", + aconnector, + aconnector->base.base.id); + + ret = drm_dp_mst_topology_mgr_set_mst(&aconnector->mst_mgr, true); + if (ret < 0) { + drm_err(dev, "DM_MST: Failed to start MST\n"); + aconnector->dc_link->type = + dc_connection_single; + ret = dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx, + aconnector->dc_link); + break; + } + } + } + drm_connector_list_iter_end(&iter); + + return ret; +} + +static void hdmi_cec_unset_edid(struct amdgpu_dm_connector *aconnector) +{ + struct cec_notifier *n = aconnector->notifier; + + if (!n) + return; + + cec_notifier_phys_addr_invalidate(n); +} + +void amdgpu_dm_hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector) +{ + struct drm_connector *connector = &aconnector->base; + struct cec_notifier *n = aconnector->notifier; + + if (!n) + return; + + cec_notifier_set_phys_addr(n, + connector->display_info.source_physical_address); +} + +void amdgpu_dm_s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; + + drm_connector_list_iter_begin(ddev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + continue; + + aconnector = to_amdgpu_dm_connector(connector); + if (suspend) + hdmi_cec_unset_edid(aconnector); + else + amdgpu_dm_hdmi_cec_set_edid(aconnector); + } + drm_connector_list_iter_end(&conn_iter); +} + + +struct drm_connector * +amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state, + struct drm_crtc *crtc) +{ + u32 i; + struct drm_connector_state *new_con_state; + struct drm_connector *connector; + struct drm_crtc *crtc_from_state; + + for_each_new_connector_in_state(state, connector, new_con_state, i) { + crtc_from_state = new_con_state->crtc; + + if (crtc_from_state == crtc) + return connector; + } + + return NULL; +} + +static void dm_set_panel_type(struct amdgpu_dm_connector *aconnector) +{ + struct drm_connector *connector = &aconnector->base; + struct drm_display_info *display_info = &connector->display_info; + struct dc_link *link = aconnector->dc_link; + struct amdgpu_device *adev; + + adev = drm_to_adev(connector->dev); + + link->panel_type = PANEL_TYPE_NONE; + + switch (display_info->amd_vsdb.panel_type) { + case AMD_VSDB_PANEL_TYPE_OLED: + link->panel_type = PANEL_TYPE_OLED; + break; + case AMD_VSDB_PANEL_TYPE_MINILED: + link->panel_type = PANEL_TYPE_MINILED; + break; + } + + /* If VSDB didn't determine panel type, check DPCD ext caps */ + if (link->panel_type == PANEL_TYPE_NONE) { + if (link->dpcd_sink_ext_caps.bits.miniled == 1) + link->panel_type = PANEL_TYPE_MINILED; + if (link->dpcd_sink_ext_caps.bits.oled == 1) + link->panel_type = PANEL_TYPE_OLED; + } + + /* If VSDB and DPCD didn't determine panel type, check DID */ + if (link->panel_type == PANEL_TYPE_NONE) { + if (display_info->panel_type == DRM_MODE_PANEL_TYPE_LCD) + link->panel_type = PANEL_TYPE_LCD; + else if (display_info->panel_type == DRM_MODE_PANEL_TYPE_OLED) + link->panel_type = PANEL_TYPE_OLED; + } + + if (link->panel_type == PANEL_TYPE_NONE) { + struct drm_amd_vsdb_info *vsdb = &display_info->amd_vsdb; + u32 lum1_max = vsdb->luminance_range1.max_luminance; + u32 lum2_max = vsdb->luminance_range2.max_luminance; + + if (vsdb->version && link->local_sink && + link->local_sink->edid_caps.manufacturer_id == + DDC_MANUFACTURERNAME_SAMSUNG && + lum1_max >= ((lum2_max * 3) / 2)) + link->panel_type = PANEL_TYPE_MINILED; + } + + if (link->panel_type == PANEL_TYPE_OLED) + drm_object_property_set_value(&connector->base, + adev_to_drm(adev)->mode_config.panel_type_property, + DRM_MODE_PANEL_TYPE_OLED); + else if (link->panel_type == PANEL_TYPE_LCD) + drm_object_property_set_value(&connector->base, + adev_to_drm(adev)->mode_config.panel_type_property, + DRM_MODE_PANEL_TYPE_LCD); + else + drm_object_property_set_value(&connector->base, + adev_to_drm(adev)->mode_config.panel_type_property, + DRM_MODE_PANEL_TYPE_UNKNOWN); + + drm_dbg_kms(aconnector->base.dev, "Panel type: %d\n", link->panel_type); +} + +DEFINE_FREE(sink_release, struct dc_sink *, if (_T) dc_sink_release(_T)) + +void amdgpu_dm_update_connector_after_detect( + struct amdgpu_dm_connector *aconnector) +{ + struct drm_connector *connector = &aconnector->base; + struct dc_sink *sink __free(sink_release) = NULL; + struct drm_device *dev = connector->dev; + + /* MST handled by drm_mst framework */ + if (aconnector->mst_mgr.mst_state) + return; + + sink = aconnector->dc_link->local_sink; + if (sink) + dc_sink_retain(sink); + + /* + * Edid mgmt connector gets first update only in mode_valid hook and then + * the connector sink is set to either fake or physical sink depends on link status. + * Skip if already done during boot. + */ + if (aconnector->base.force != DRM_FORCE_UNSPECIFIED + && aconnector->dc_em_sink) { + + /* + * For S3 resume with headless use eml_sink to fake stream + * because on resume connector->sink is set to NULL + */ + guard(mutex)(&dev->mode_config.mutex); + + if (sink) { + if (aconnector->dc_sink) { + amdgpu_dm_update_freesync_caps(connector, NULL, true); + /* + * retain and release below are used to + * bump up refcount for sink because the link doesn't point + * to it anymore after disconnect, so on next crtc to connector + * reshuffle by UMD we will get into unwanted dc_sink release + */ + dc_sink_release(aconnector->dc_sink); + } + aconnector->dc_sink = sink; + dc_sink_retain(aconnector->dc_sink); + amdgpu_dm_update_freesync_caps(connector, + aconnector->drm_edid, true); + } else { + amdgpu_dm_update_freesync_caps(connector, NULL, true); + if (!aconnector->dc_sink) { + aconnector->dc_sink = aconnector->dc_em_sink; + dc_sink_retain(aconnector->dc_sink); + } + } + + return; + } + + /* + * TODO: temporary guard to look for proper fix + * if this sink is MST sink, we should not do anything + */ + if (sink && sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT_MST) + return; + + if (aconnector->dc_sink == sink) { + /* + * We got a DP short pulse (Link Loss, DP CTS, etc...). + * Do nothing!! + */ + drm_dbg_kms(dev, "DCHPD: connector_id=%d: dc_sink didn't change.\n", + aconnector->connector_id); + return; + } + + drm_dbg_kms(dev, "DCHPD: connector_id=%d: Old sink=%p New sink=%p\n", + aconnector->connector_id, aconnector->dc_sink, sink); + + /* When polling, DRM has already locked the mutex for us. */ + if (!drm_kms_helper_is_poll_worker()) + mutex_lock(&dev->mode_config.mutex); + + /* + * 1. Update status of the drm connector + * 2. Send an event and let userspace tell us what to do + */ + if (sink) { + /* + * TODO: check if we still need the S3 mode update workaround. + * If yes, put it here. + */ + if (aconnector->dc_sink) { + amdgpu_dm_update_freesync_caps(connector, NULL, true); + dc_sink_release(aconnector->dc_sink); + } + + aconnector->dc_sink = sink; + dc_sink_retain(aconnector->dc_sink); + drm_edid_free(aconnector->drm_edid); + aconnector->drm_edid = NULL; + if (sink->dc_edid.length == 0) { + hdmi_cec_unset_edid(aconnector); + if (aconnector->dc_link->aux_mode) + drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux); + } else { + const struct edid *edid = (const struct edid *)sink->dc_edid.raw_edid; + + aconnector->drm_edid = drm_edid_alloc(edid, sink->dc_edid.length); + drm_edid_connector_update(connector, aconnector->drm_edid); + + amdgpu_dm_hdmi_cec_set_edid(aconnector); + if (aconnector->dc_link->aux_mode) + drm_dp_cec_attach(&aconnector->dm_dp_aux.aux, + connector->display_info.source_physical_address); + } + + if (!aconnector->timing_requested) { + aconnector->timing_requested = + kzalloc_obj(struct dc_crtc_timing); + if (!aconnector->timing_requested) + drm_err(dev, + "failed to create aconnector->requested_timing\n"); + } + + amdgpu_dm_update_freesync_caps(connector, aconnector->drm_edid, true); + amdgpu_dm_update_connector_ext_caps(aconnector); + dm_set_panel_type(aconnector); + + if (aconnector->hdmi_comp_auto) { + if (sink->sink_signal != SIGNAL_TYPE_HDMI_FRL) + sink->sink_signal = SIGNAL_TYPE_HDMI_FRL; + } + } else { + hdmi_cec_unset_edid(aconnector); + drm_dp_cec_unset_edid(&aconnector->dm_dp_aux.aux); + amdgpu_dm_update_freesync_caps(connector, NULL, true); + aconnector->num_modes = 0; + dc_sink_release(aconnector->dc_sink); + aconnector->dc_sink = NULL; + drm_edid_free(aconnector->drm_edid); + aconnector->drm_edid = NULL; + kfree(aconnector->timing_requested); + aconnector->timing_requested = NULL; + /* Set CP to DESIRED if it was ENABLED, so we can re-enable it again on hotplug */ + if (connector->state->content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED) + connector->state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + } + + update_subconnector_property(aconnector); + + /* When polling, the mutex will be unlocked for us by DRM. */ + if (!drm_kms_helper_is_poll_worker()) + mutex_unlock(&dev->mode_config.mutex); +} + +enum dc_color_depth +amdgpu_dm_convert_color_depth_from_display_info(const struct drm_connector *connector, + bool is_y420, int requested_bpc) +{ + u8 bpc; + + if (is_y420) { + bpc = 8; + + /* Cap display bpc based on HDMI 2.0 HF-VSDB */ + if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_48) + bpc = 16; + else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_36) + bpc = 12; + else if (connector->display_info.hdmi.y420_dc_modes & DRM_EDID_YCBCR420_DC_30) + bpc = 10; + } else { + bpc = (uint8_t)connector->display_info.bpc; + /* Assume 8 bpc by default if no bpc is specified. */ + bpc = bpc ? bpc : 8; + } + + if (requested_bpc > 0) { + /* + * Cap display bpc based on the user requested value. + * + * The value for state->max_bpc may not correctly updated + * depending on when the connector gets added to the state + * or if this was called outside of atomic check, so it + * can't be used directly. + */ + bpc = min_t(u8, bpc, requested_bpc); + + /* Round down to the nearest even number. */ + bpc = bpc - (bpc & 1); + } + + switch (bpc) { + case 0: + /* + * Temporary Work around, DRM doesn't parse color depth for + * EDID revision before 1.4 + * TODO: Fix edid parsing + */ + return COLOR_DEPTH_888; + case 6: + return COLOR_DEPTH_666; + case 8: + return COLOR_DEPTH_888; + case 10: + return COLOR_DEPTH_101010; + case 12: + return COLOR_DEPTH_121212; + case 14: + return COLOR_DEPTH_141414; + case 16: + return COLOR_DEPTH_161616; + default: + return COLOR_DEPTH_UNDEFINED; + } +} +EXPORT_IF_KUNIT(amdgpu_dm_convert_color_depth_from_display_info); + +STATIC_IFN_KUNIT enum dc_aspect_ratio +get_aspect_ratio(const struct drm_display_mode *mode_in) +{ + /* 1-1 mapping, since both enums follow the HDMI spec. */ + return (enum dc_aspect_ratio) mode_in->picture_aspect_ratio; +} +EXPORT_IF_KUNIT(get_aspect_ratio); + +enum dc_color_space +amdgpu_dm_get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing, + const struct drm_connector_state *connector_state) +{ + enum dc_color_space color_space = COLOR_SPACE_SRGB; + + switch (connector_state->colorspace) { + case DRM_MODE_COLORIMETRY_BT601_YCC: + if (dc_crtc_timing->flags.Y_ONLY) + color_space = COLOR_SPACE_YCBCR601_LIMITED; + else + color_space = COLOR_SPACE_YCBCR601; + break; + case DRM_MODE_COLORIMETRY_BT709_YCC: + if (dc_crtc_timing->flags.Y_ONLY) + color_space = COLOR_SPACE_YCBCR709_LIMITED; + else + color_space = COLOR_SPACE_YCBCR709; + break; + case DRM_MODE_COLORIMETRY_OPRGB: + color_space = COLOR_SPACE_ADOBERGB; + break; + case DRM_MODE_COLORIMETRY_BT2020_RGB: + case DRM_MODE_COLORIMETRY_BT2020_YCC: + if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) + color_space = COLOR_SPACE_2020_RGB_FULLRANGE; + else + color_space = COLOR_SPACE_2020_YCBCR_LIMITED; + break; + case DRM_MODE_COLORIMETRY_DEFAULT: /* ITU601 */ + default: + if (dc_crtc_timing->pixel_encoding == PIXEL_ENCODING_RGB) { + color_space = COLOR_SPACE_SRGB; + if (connector_state->hdmi.broadcast_rgb == DRM_HDMI_BROADCAST_RGB_LIMITED) + color_space = COLOR_SPACE_SRGB_LIMITED; + /* + * 27030khz is the separation point between HDTV and SDTV + * according to HDMI spec, we use YCbCr709 and YCbCr601 + * respectively + */ + } else if (dc_crtc_timing->pix_clk_100hz > 270300) { + if (dc_crtc_timing->flags.Y_ONLY) + color_space = + COLOR_SPACE_YCBCR709_LIMITED; + else + color_space = COLOR_SPACE_YCBCR709; + } else { + if (dc_crtc_timing->flags.Y_ONLY) + color_space = + COLOR_SPACE_YCBCR601_LIMITED; + else + color_space = COLOR_SPACE_YCBCR601; + } + break; + } + + return color_space; +} +EXPORT_IF_KUNIT(amdgpu_dm_get_output_color_space); + +STATIC_IFN_KUNIT enum display_content_type +get_output_content_type(const struct drm_connector_state *connector_state) +{ + switch (connector_state->content_type) { + default: + case DRM_MODE_CONTENT_TYPE_NO_DATA: + return DISPLAY_CONTENT_TYPE_NO_DATA; + case DRM_MODE_CONTENT_TYPE_GRAPHICS: + return DISPLAY_CONTENT_TYPE_GRAPHICS; + case DRM_MODE_CONTENT_TYPE_PHOTO: + return DISPLAY_CONTENT_TYPE_PHOTO; + case DRM_MODE_CONTENT_TYPE_CINEMA: + return DISPLAY_CONTENT_TYPE_CINEMA; + case DRM_MODE_CONTENT_TYPE_GAME: + return DISPLAY_CONTENT_TYPE_GAME; + } +} +EXPORT_IF_KUNIT(get_output_content_type); + +STATIC_IFN_KUNIT bool adjust_colour_depth_from_display_info( + struct dc_crtc_timing *timing_out, + const struct drm_display_info *info) +{ + enum dc_color_depth depth = timing_out->display_color_depth; + int normalized_clk; + + do { + normalized_clk = timing_out->pix_clk_100hz / 10; + /* YCbCr 4:2:0 requires additional adjustment of 1/2 */ + if (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420) + normalized_clk /= 2; + /* Adjusting pix clock following on HDMI spec based on colour depth */ + switch (depth) { + case COLOR_DEPTH_888: + break; + case COLOR_DEPTH_101010: + normalized_clk = (normalized_clk * 30) / 24; + break; + case COLOR_DEPTH_121212: + normalized_clk = (normalized_clk * 36) / 24; + break; + case COLOR_DEPTH_161616: + normalized_clk = (normalized_clk * 48) / 24; + break; + default: + /* The above depths are the only ones valid for HDMI. */ + return false; + } + if (normalized_clk <= info->max_tmds_clock) { + timing_out->display_color_depth = depth; + return true; + } + } while (--depth > COLOR_DEPTH_666); + return false; +} +EXPORT_IF_KUNIT(adjust_colour_depth_from_display_info); + +static void fill_stream_properties_from_drm_display_mode( + struct dc_stream_state *stream, + const struct drm_display_mode *mode_in, + const struct drm_connector *connector, + const struct drm_connector_state *connector_state, + const struct dc_stream_state *old_stream, + int requested_bpc) +{ + bool is_dp_or_hdmi = dc_is_hdmi_signal(stream->signal) || dc_is_dp_signal(stream->signal); + struct dc_crtc_timing *timing_out = &stream->timing; + const struct drm_display_info *info = &connector->display_info; + struct amdgpu_dm_connector *aconnector = NULL; + struct hdmi_vendor_infoframe hv_frame; + struct hdmi_avi_infoframe avi_frame; + bool want_420; + bool want_422; + ssize_t err; + + if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) + aconnector = to_amdgpu_dm_connector(connector); + + memset(&hv_frame, 0, sizeof(hv_frame)); + memset(&avi_frame, 0, sizeof(avi_frame)); + + timing_out->h_border_left = 0; + timing_out->h_border_right = 0; + timing_out->v_border_top = 0; + timing_out->v_border_bottom = 0; + + want_420 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR420) || + (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR420); + want_422 = (aconnector && aconnector->force_yuv_pixel_format == PIXEL_ENCODING_YCBCR422) || + (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR422); + + if (drm_mode_is_420_only(info, mode_in) && + (want_420 || connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO)) { + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; + } else if (drm_mode_is_420_also(info, mode_in) && want_420) { + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; + } else if ((info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422)) && + want_422 && is_dp_or_hdmi) { + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR422; + } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_YCBCR444 && + (info->color_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444)) && + is_dp_or_hdmi) { + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR444; + } else if (connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_RGB444 || + connector_state->color_format == DRM_CONNECTOR_COLOR_FORMAT_AUTO) { + timing_out->pixel_encoding = PIXEL_ENCODING_RGB; + } else { + /* + * If a format was explicitly requested but the requested format + * can't be satisfied, set it to an invalid value so that an + * error bubbles up to userspace. This way, userspace knows it + * needs to make a better choice. + */ + if (connector_state->color_format != DRM_CONNECTOR_COLOR_FORMAT_AUTO) + timing_out->pixel_encoding = PIXEL_ENCODING_UNDEFINED; + else if (drm_mode_is_420_only(info, mode_in)) + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; + else + timing_out->pixel_encoding = PIXEL_ENCODING_RGB; + } + + timing_out->timing_3d_format = TIMING_3D_FORMAT_NONE; + timing_out->display_color_depth = amdgpu_dm_convert_color_depth_from_display_info( + connector, + (timing_out->pixel_encoding == PIXEL_ENCODING_YCBCR420), + requested_bpc); + timing_out->scan_type = SCANNING_TYPE_NODATA; + timing_out->hdmi_vic = 0; + + if (old_stream) { + timing_out->vic = old_stream->timing.vic; + timing_out->flags.HSYNC_POSITIVE_POLARITY = old_stream->timing.flags.HSYNC_POSITIVE_POLARITY; + timing_out->flags.VSYNC_POSITIVE_POLARITY = old_stream->timing.flags.VSYNC_POSITIVE_POLARITY; + } else { + timing_out->vic = drm_match_cea_mode(mode_in); + if (mode_in->flags & DRM_MODE_FLAG_PHSYNC) + timing_out->flags.HSYNC_POSITIVE_POLARITY = 1; + if (mode_in->flags & DRM_MODE_FLAG_PVSYNC) + timing_out->flags.VSYNC_POSITIVE_POLARITY = 1; + } + + if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || + stream->signal == SIGNAL_TYPE_HDMI_FRL) { + err = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame, + (struct drm_connector *)connector, + mode_in); + if (err < 0) + drm_warn_once(connector->dev, "Failed to setup avi infoframe on connector %s: %zd\n", + connector->name, err); + timing_out->vic = avi_frame.video_code; + err = drm_hdmi_vendor_infoframe_from_display_mode(&hv_frame, + (struct drm_connector *)connector, + mode_in); + if (err < 0) + drm_warn_once(connector->dev, "Failed to setup vendor infoframe on connector %s: %zd\n", + connector->name, err); + timing_out->hdmi_vic = hv_frame.vic; + } + + if (aconnector && amdgpu_dm_is_freesync_video_mode(mode_in, aconnector)) { + timing_out->h_addressable = mode_in->hdisplay; + timing_out->h_total = mode_in->htotal; + timing_out->h_sync_width = mode_in->hsync_end - mode_in->hsync_start; + timing_out->h_front_porch = mode_in->hsync_start - mode_in->hdisplay; + timing_out->v_total = mode_in->vtotal; + timing_out->v_addressable = mode_in->vdisplay; + timing_out->v_front_porch = mode_in->vsync_start - mode_in->vdisplay; + timing_out->v_sync_width = mode_in->vsync_end - mode_in->vsync_start; + timing_out->pix_clk_100hz = mode_in->clock * 10; + } else { + timing_out->h_addressable = mode_in->crtc_hdisplay; + timing_out->h_total = mode_in->crtc_htotal; + timing_out->h_sync_width = mode_in->crtc_hsync_end - mode_in->crtc_hsync_start; + timing_out->h_front_porch = mode_in->crtc_hsync_start - mode_in->crtc_hdisplay; + timing_out->v_total = mode_in->crtc_vtotal; + timing_out->v_addressable = mode_in->crtc_vdisplay; + timing_out->v_front_porch = mode_in->crtc_vsync_start - mode_in->crtc_vdisplay; + timing_out->v_sync_width = mode_in->crtc_vsync_end - mode_in->crtc_vsync_start; + timing_out->pix_clk_100hz = mode_in->crtc_clock * 10; + } + + timing_out->aspect_ratio = get_aspect_ratio(mode_in); + + stream->out_transfer_func.type = TF_TYPE_PREDEFINED; + stream->out_transfer_func.tf = TRANSFER_FUNCTION_SRGB; + if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A) { + if (!adjust_colour_depth_from_display_info(timing_out, info) && + drm_mode_is_420_also(info, mode_in) && + timing_out->pixel_encoding != PIXEL_ENCODING_YCBCR420) { + timing_out->pixel_encoding = PIXEL_ENCODING_YCBCR420; + adjust_colour_depth_from_display_info(timing_out, info); + } + } + + stream->output_color_space = amdgpu_dm_get_output_color_space(timing_out, connector_state); + stream->content_type = get_output_content_type(connector_state); +} + +static void +copy_crtc_timing_for_drm_display_mode(const struct drm_display_mode *src_mode, + struct drm_display_mode *dst_mode) +{ + dst_mode->crtc_hdisplay = src_mode->crtc_hdisplay; + dst_mode->crtc_vdisplay = src_mode->crtc_vdisplay; + dst_mode->crtc_clock = src_mode->crtc_clock; + dst_mode->crtc_hblank_start = src_mode->crtc_hblank_start; + dst_mode->crtc_hblank_end = src_mode->crtc_hblank_end; + dst_mode->crtc_hsync_start = src_mode->crtc_hsync_start; + dst_mode->crtc_hsync_end = src_mode->crtc_hsync_end; + dst_mode->crtc_htotal = src_mode->crtc_htotal; + dst_mode->crtc_hskew = src_mode->crtc_hskew; + dst_mode->crtc_vblank_start = src_mode->crtc_vblank_start; + dst_mode->crtc_vblank_end = src_mode->crtc_vblank_end; + dst_mode->crtc_vsync_start = src_mode->crtc_vsync_start; + dst_mode->crtc_vsync_end = src_mode->crtc_vsync_end; + dst_mode->crtc_vtotal = src_mode->crtc_vtotal; +} + +STATIC_IFN_KUNIT void +decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode, + const struct drm_display_mode *native_mode, + bool scale_enabled) +{ + if (scale_enabled || ( + native_mode->clock == drm_mode->clock && + native_mode->htotal == drm_mode->htotal && + native_mode->vtotal == drm_mode->vtotal)) { + if (native_mode->crtc_clock) + copy_crtc_timing_for_drm_display_mode(native_mode, drm_mode); + } else { + /* no scaling nor amdgpu inserted, no need to patch */ + } +} +EXPORT_IF_KUNIT(decide_crtc_timing_for_drm_display_mode); + +static struct dc_sink * +create_fake_sink(struct drm_device *dev, struct dc_link *link) +{ + struct dc_sink_init_data sink_init_data = { 0 }; + struct dc_sink *sink = NULL; + + sink_init_data.link = link; + sink_init_data.sink_signal = link->connector_signal; + + sink = dc_sink_create(&sink_init_data); + if (!sink) { + drm_err(dev, "Failed to create sink!\n"); + return NULL; + } + sink->sink_signal = SIGNAL_TYPE_VIRTUAL; + + return sink; +} + +/** + * DOC: FreeSync Video + * + * When a userspace application wants to play a video, the content follows a + * standard format definition that usually specifies the FPS for that format. + * The below list illustrates some video format and the expected FPS, + * respectively: + * + * - TV/NTSC (23.976 FPS) + * - Cinema (24 FPS) + * - TV/PAL (25 FPS) + * - TV/NTSC (29.97 FPS) + * - TV/NTSC (30 FPS) + * - Cinema HFR (48 FPS) + * - TV/PAL (50 FPS) + * - Commonly used (60 FPS) + * - Multiples of 24 (48,72,96 FPS) + * + * The list of standards video format is not huge and can be added to the + * connector modeset list beforehand. With that, userspace can leverage + * FreeSync to extends the front porch in order to attain the target refresh + * rate. Such a switch will happen seamlessly, without screen blanking or + * reprogramming of the output in any other way. If the userspace requests a + * modesetting change compatible with FreeSync modes that only differ in the + * refresh rate, DC will skip the full update and avoid blink during the + * transition. For example, the video player can change the modesetting from + * 60Hz to 30Hz for playing TV/NTSC content when it goes full screen without + * causing any display blink. This same concept can be applied to a mode + * setting change. + */ +struct drm_display_mode * +amdgpu_dm_get_highest_refresh_rate_mode(struct amdgpu_dm_connector *aconnector, + bool use_probed_modes) +{ + struct drm_display_mode *m, *m_pref = NULL; + u16 current_refresh, highest_refresh; + struct list_head *list_head = use_probed_modes ? + &aconnector->base.probed_modes : + &aconnector->base.modes; + + if (aconnector->base.connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + return NULL; + + if (aconnector->freesync_vid_base.clock != 0) + return &aconnector->freesync_vid_base; + + /* Find the preferred mode */ + list_for_each_entry(m, list_head, head) { + if (m->type & DRM_MODE_TYPE_PREFERRED) { + m_pref = m; + break; + } + } + + if (!m_pref) { + /* Probably an EDID with no preferred mode. Fallback to first entry */ + m_pref = list_first_entry_or_null( + &aconnector->base.modes, struct drm_display_mode, head); + if (!m_pref) { + drm_dbg_driver(aconnector->base.dev, "No preferred mode found in EDID\n"); + return NULL; + } + } + + highest_refresh = drm_mode_vrefresh(m_pref); + + /* + * Find the mode with highest refresh rate with same resolution. + * For some monitors, preferred mode is not the mode with highest + * supported refresh rate. + */ + list_for_each_entry(m, list_head, head) { + current_refresh = drm_mode_vrefresh(m); + + if (m->hdisplay == m_pref->hdisplay && + m->vdisplay == m_pref->vdisplay && + highest_refresh < current_refresh) { + highest_refresh = current_refresh; + m_pref = m; + } + } + + drm_mode_copy(&aconnector->freesync_vid_base, m_pref); + return m_pref; +} +EXPORT_IF_KUNIT(amdgpu_dm_get_highest_refresh_rate_mode); + +bool amdgpu_dm_is_freesync_video_mode(const struct drm_display_mode *mode, + struct amdgpu_dm_connector *aconnector) +{ + struct drm_display_mode *high_mode; + int timing_diff; + + high_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false); + if (!high_mode || !mode) + return false; + + timing_diff = high_mode->vtotal - mode->vtotal; + + if (high_mode->clock == 0 || high_mode->clock != mode->clock || + high_mode->hdisplay != mode->hdisplay || + high_mode->vdisplay != mode->vdisplay || + high_mode->hsync_start != mode->hsync_start || + high_mode->hsync_end != mode->hsync_end || + high_mode->htotal != mode->htotal || + high_mode->hskew != mode->hskew || + high_mode->vscan != mode->vscan || + high_mode->vsync_start - mode->vsync_start != timing_diff || + high_mode->vsync_end - mode->vsync_end != timing_diff) + return false; + else + return true; +} +EXPORT_IF_KUNIT(amdgpu_dm_is_freesync_video_mode); + +#if defined(CONFIG_DRM_AMD_DC_FP) +static void update_dsc_caps(struct amdgpu_dm_connector *aconnector, + struct dc_sink *sink, struct dc_stream_state *stream, + struct dsc_dec_dpcd_caps *dsc_caps) +{ + stream->timing.flags.DSC = 0; + dsc_caps->is_dsc_supported = false; + + if (aconnector->dc_link && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || + sink->sink_signal == SIGNAL_TYPE_EDP)) { + if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE) + dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc, + aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw, + aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw, + dsc_caps); + else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) { + if (aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.fields.dsc_support.DSC_PASSTHROUGH_SUPPORT && + !aconnector->dsc_settings.dsc_force_disable_passthrough && + aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps > 0 && + sink->edid_caps.frl_dsc_support && + sink->edid_caps.max_frl_rate > 0 && + sink->edid_caps.frl_dsc_max_frl_rate > 0) + dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps); + else + dc_dsc_parse_dsc_dpcd(aconnector->dc_link->ctx->dc, + aconnector->dc_link->dpcd_caps.dsc_caps.dsc_basic_caps.raw, + aconnector->dc_link->dpcd_caps.dsc_caps.dsc_branch_decoder_caps.raw, + dsc_caps); + } + } else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) { + if (sink->edid_caps.frl_dsc_support && + sink->edid_caps.max_frl_rate > 0 && + sink->edid_caps.frl_dsc_max_frl_rate > 0) + dc_dsc_parse_dsc_edid(aconnector->dc_link->ctx->dc, &sink->edid_caps, dsc_caps); + } +} + +static void apply_dsc_policy_for_edp(struct amdgpu_dm_connector *aconnector, + struct dc_sink *sink, struct dc_stream_state *stream, + struct dsc_dec_dpcd_caps *dsc_caps, + uint32_t max_dsc_target_bpp_limit_override) +{ + const struct dc_link_settings *verified_link_cap = NULL; + u32 link_bw_in_kbps; + u32 edp_min_bpp_x16, edp_max_bpp_x16; + struct dc *dc = sink->ctx->dc; + struct dc_dsc_bw_range bw_range = {0}; + struct dc_dsc_config dsc_cfg = {0}; + struct dc_dsc_config_options dsc_options = {0}; + + dc_dsc_get_default_config_option(dc, &dsc_options); + dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16; + + verified_link_cap = dc_link_get_link_cap(stream->link); + link_bw_in_kbps = dc_link_bandwidth_kbps(stream->link, verified_link_cap); + edp_min_bpp_x16 = 8 * 16; + edp_max_bpp_x16 = 8 * 16; + + if (edp_max_bpp_x16 > dsc_caps->edp_max_bits_per_pixel) + edp_max_bpp_x16 = dsc_caps->edp_max_bits_per_pixel; + + if (edp_max_bpp_x16 < edp_min_bpp_x16) + edp_min_bpp_x16 = edp_max_bpp_x16; + + if (dc_dsc_compute_bandwidth_range(dc->res_pool->dscs[0], + dc->debug.dsc_min_slice_height_override, + edp_min_bpp_x16, edp_max_bpp_x16, + dsc_caps, + &stream->timing, + dc_link_get_highest_encoding_format(aconnector->dc_link), + &bw_range)) { + + if (bw_range.max_kbps < link_bw_in_kbps) { + if (dc_dsc_compute_config(dc->res_pool->dscs[0], + dsc_caps, + &dsc_options, + 0, + &stream->timing, + dc_link_get_highest_encoding_format(aconnector->dc_link), + &dsc_cfg)) { + stream->timing.dsc_cfg = dsc_cfg; + stream->timing.flags.DSC = 1; + stream->timing.dsc_cfg.bits_per_pixel = edp_max_bpp_x16; + } + return; + } + } + + if (dc_dsc_compute_config(dc->res_pool->dscs[0], + dsc_caps, + &dsc_options, + link_bw_in_kbps, + &stream->timing, + dc_link_get_highest_encoding_format(aconnector->dc_link), + &dsc_cfg)) { + stream->timing.dsc_cfg = dsc_cfg; + stream->timing.flags.DSC = 1; + } +} + +static void apply_dsc_policy_for_stream(struct amdgpu_dm_connector *aconnector, + struct dc_sink *sink, struct dc_stream_state *stream, + struct dsc_dec_dpcd_caps *dsc_caps) +{ + struct drm_connector *drm_connector = &aconnector->base; + u32 link_bandwidth_kbps; + struct dc *dc = sink->ctx->dc; + const struct dc_hdmi_frl_link_settings *frl_verified_link_cap = NULL; + u32 converter_bw_in_kbps; + u32 sink_bw_in_kbps; + u32 dsc_sink_bw_in_kbps; + u32 max_supported_bw_in_kbps, timing_bw_in_kbps; + u32 dsc_max_supported_bw_in_kbps; + u32 max_dsc_target_bpp_limit_override = + drm_connector->display_info.max_dsc_bpp; + struct dc_dsc_config_options dsc_options = {0}; + + dc_dsc_get_default_config_option(dc, &dsc_options); + dsc_options.max_target_bpp_limit_override_x16 = max_dsc_target_bpp_limit_override * 16; + + link_bandwidth_kbps = dc_link_bandwidth_kbps(aconnector->dc_link, + dc_link_get_link_cap(aconnector->dc_link)); + + /* Set DSC policy according to dsc_clock_en */ + dc_dsc_policy_set_enable_dsc_when_not_needed( + aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE); + + if (sink->sink_signal == SIGNAL_TYPE_EDP && + !aconnector->dc_link->panel_config.dsc.disable_dsc_edp && + dc->caps.edp_dsc_support && aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE) { + + apply_dsc_policy_for_edp(aconnector, sink, stream, dsc_caps, max_dsc_target_bpp_limit_override); + + } else if (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT) { + if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_NONE) { + if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0], + dsc_caps, + &dsc_options, + link_bandwidth_kbps, + &stream->timing, + dc_link_get_highest_encoding_format(aconnector->dc_link), + &stream->timing.dsc_cfg)) { + stream->timing.flags.DSC = 1; + drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from SST RX\n", + __func__, drm_connector->name); + } + } else if (sink->link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER) { + timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing, + dc_link_get_highest_encoding_format(aconnector->dc_link)); + converter_bw_in_kbps = aconnector->dc_link->dpcd_caps.dongle_caps.dp_hdmi_frl_max_link_bw_in_kbps; + sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.max_frl_rate); + dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate); + + if (dsc_caps->is_frl) { + max_supported_bw_in_kbps = min(link_bandwidth_kbps, converter_bw_in_kbps); + max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, sink_bw_in_kbps); + dsc_max_supported_bw_in_kbps = min(max_supported_bw_in_kbps, dsc_sink_bw_in_kbps); + } else { + max_supported_bw_in_kbps = link_bandwidth_kbps; + dsc_max_supported_bw_in_kbps = link_bandwidth_kbps; + } + + if (timing_bw_in_kbps > max_supported_bw_in_kbps && + max_supported_bw_in_kbps > 0 && + dsc_max_supported_bw_in_kbps > 0) + if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0], + dsc_caps, + &dsc_options, + dsc_max_supported_bw_in_kbps, + &stream->timing, + dc_link_get_highest_encoding_format(aconnector->dc_link), + &stream->timing.dsc_cfg)) { + stream->timing.flags.DSC = 1; + drm_dbg_driver(drm_connector->dev, "%s: SST_DSC [%s] DSC is selected from %s\n", + __func__, drm_connector->name, + (dsc_caps->is_frl == 1) ? "HDMI FRL RX" : "DP-HDMI PCON"); + } + } + } else if (aconnector->dc_link && sink->sink_signal == SIGNAL_TYPE_HDMI_FRL) { + struct dc_dsc_policy dsc_policy = {0}; + + frl_verified_link_cap = dc_link_get_frl_link_cap(stream->link); + if (frl_verified_link_cap->frl_link_rate != HDMI_FRL_LINK_RATE_DISABLE && + aconnector->dc_link->frl_flags.force_frl_dsc) { + dc_dsc_policy_set_enable_dsc_when_not_needed(true); + dc_dsc_get_policy_for_timing(&stream->timing, 0, &dsc_policy, dc_link_get_highest_encoding_format(stream->link)); + } + + timing_bw_in_kbps = dc_bandwidth_in_kbps_from_timing(&stream->timing, DC_LINK_ENCODING_HDMI_FRL); + link_bandwidth_kbps = dc_link_frl_bandwidth_kbps(stream->link, frl_verified_link_cap->frl_link_rate); + dsc_sink_bw_in_kbps = dc_link_bw_kbps_from_raw_frl_link_rate_data(dc, sink->edid_caps.frl_dsc_max_frl_rate); + + if ((timing_bw_in_kbps > link_bandwidth_kbps && dsc_sink_bw_in_kbps > 0) || + (dsc_policy.enable_dsc_when_not_needed || dsc_options.force_dsc_when_not_needed)) { + if (dc_dsc_compute_config(aconnector->dc_link->ctx->dc->res_pool->dscs[0], + dsc_caps, + &dsc_options, + dsc_sink_bw_in_kbps, + &stream->timing, + dc_link_get_highest_encoding_format(aconnector->dc_link), + &stream->timing.dsc_cfg)) { + stream->timing.flags.DSC = 1; + drm_dbg_driver(drm_connector->dev, "%s: HDMI_FRL_DSC [%s] DSC is selected from HDMI FRL RX\n", + __func__, drm_connector->name); + } + } + } + + /* Overwrite the stream flag if DSC is enabled through debugfs */ + if (aconnector->dsc_settings.dsc_force_enable == DSC_CLK_FORCE_ENABLE) + stream->timing.flags.DSC = 1; + + if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_h) + stream->timing.dsc_cfg.num_slices_h = aconnector->dsc_settings.dsc_num_slices_h; + + if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_num_slices_v) + stream->timing.dsc_cfg.num_slices_v = aconnector->dsc_settings.dsc_num_slices_v; + + if (stream->timing.flags.DSC && aconnector->dsc_settings.dsc_bits_per_pixel) + stream->timing.dsc_cfg.bits_per_pixel = aconnector->dsc_settings.dsc_bits_per_pixel; +} +#endif + +static struct dc_stream_state * +create_stream_for_sink(struct drm_connector *connector, + const struct drm_display_mode *drm_mode, + const struct dm_connector_state *dm_state, + const struct dc_stream_state *old_stream, + int requested_bpc) +{ + struct drm_device *dev = connector->dev; + struct amdgpu_dm_connector *aconnector = NULL; + struct drm_display_mode *preferred_mode = NULL; + const struct drm_connector_state *con_state = &dm_state->base; + struct dc_stream_state *stream = NULL; + struct drm_display_mode mode; + struct drm_display_mode saved_mode; + struct drm_display_mode *freesync_mode = NULL; + bool native_mode_found = false; + bool recalculate_timing = false; + bool scale = dm_state->scaling != RMX_OFF; + int mode_refresh; + 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 = {0}; +#endif + struct dc_link *link = NULL; + struct dc_sink *sink = NULL; + + drm_mode_init(&mode, drm_mode); + memset(&saved_mode, 0, sizeof(saved_mode)); + + if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) { + aconnector = NULL; + aconnector = to_amdgpu_dm_connector(connector); + link = aconnector->dc_link; + } else { + struct drm_writeback_connector *wbcon = NULL; + struct amdgpu_dm_wb_connector *dm_wbcon = NULL; + + wbcon = drm_connector_to_writeback(connector); + dm_wbcon = to_amdgpu_dm_wb_connector(wbcon); + link = dm_wbcon->link; + } + + if (!aconnector || !aconnector->dc_sink) { + sink = create_fake_sink(dev, link); + if (!sink) + return stream; + + } else { + sink = aconnector->dc_sink; + dc_sink_retain(sink); + } + + stream = dc_create_stream_for_sink(sink); + + if (stream == NULL) { + drm_err(dev, "Failed to create stream for sink!\n"); + goto finish; + } + + /* We leave this NULL for writeback connectors */ + stream->dm_stream_context = aconnector; + + stream->timing.flags.LTE_340MCSC_SCRAMBLE = + connector->display_info.hdmi.scdc.scrambling.low_rates; + + list_for_each_entry(preferred_mode, &connector->modes, head) { + /* Search for preferred mode */ + if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) { + native_mode_found = true; + break; + } + } + if (!native_mode_found) + preferred_mode = list_first_entry_or_null( + &connector->modes, + struct drm_display_mode, + head); + + mode_refresh = drm_mode_vrefresh(&mode); + + if (preferred_mode == NULL) { + /* + * This may not be an error, the use case is when we have no + * usermode calls to reset and set mode upon hotplug. In this + * case, we call set mode ourselves to restore the previous mode + * and the modelist may not be filled in time. + */ + drm_dbg_driver(dev, "No preferred mode found\n"); + } else if (aconnector) { + recalculate_timing = amdgpu_freesync_vid_mode && + amdgpu_dm_is_freesync_video_mode(&mode, aconnector); + if (recalculate_timing) { + freesync_mode = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false); + drm_mode_copy(&saved_mode, &mode); + saved_mode.picture_aspect_ratio = mode.picture_aspect_ratio; + drm_mode_copy(&mode, freesync_mode); + mode.picture_aspect_ratio = saved_mode.picture_aspect_ratio; + } else { + decide_crtc_timing_for_drm_display_mode( + &mode, preferred_mode, scale); + + preferred_refresh = drm_mode_vrefresh(preferred_mode); + } + } + + if (recalculate_timing) + drm_mode_set_crtcinfo(&saved_mode, 0); + + /* + * If scaling is enabled and refresh rate didn't change + * we copy the vic and polarities of the old timings + */ + if (!scale || mode_refresh != preferred_refresh) + fill_stream_properties_from_drm_display_mode( + stream, &mode, connector, con_state, NULL, + requested_bpc); + else + fill_stream_properties_from_drm_display_mode( + stream, &mode, connector, con_state, old_stream, + requested_bpc); + + /* The rest isn't needed for writeback connectors */ + if (!aconnector) + goto finish; + + if (aconnector->timing_changed) { + drm_dbg(aconnector->base.dev, + "overriding timing for automated test, bpc %d, changing to %d\n", + stream->timing.display_color_depth, + aconnector->timing_requested->display_color_depth); + stream->timing = *aconnector->timing_requested; + } + +#if defined(CONFIG_DRM_AMD_DC_FP) + /* SST DSC determination policy */ + update_dsc_caps(aconnector, sink, stream, &dsc_caps); + if (aconnector->dsc_settings.dsc_force_enable != DSC_CLK_FORCE_DISABLE && dsc_caps.is_dsc_supported) + apply_dsc_policy_for_stream(aconnector, sink, stream, &dsc_caps); +#endif + + amdgpu_dm_update_stream_scaling_settings(dev, &mode, dm_state, stream); + + amdgpu_dm_fill_audio_info( + &stream->audio_info, + connector, + sink); + + update_stream_signal(stream, sink); + + if (stream->signal == SIGNAL_TYPE_HDMI_TYPE_A || + stream->signal == SIGNAL_TYPE_HDMI_FRL) + mod_build_hf_vsif_infopacket(stream, &stream->vsp_infopacket, false, false); + + if (stream->signal == SIGNAL_TYPE_DISPLAY_PORT || + stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST || + stream->signal == SIGNAL_TYPE_EDP) { + const struct dc_edid_caps *edid_caps; + unsigned int disable_colorimetry = 0; + + if (aconnector->dc_sink) { + edid_caps = &aconnector->dc_sink->edid_caps; + disable_colorimetry = edid_caps->panel_patch.disable_colorimetry; + } + + /* + * should decide stream support vsc sdp colorimetry capability + * before building vsc info packet + */ + stream->use_vsc_sdp_for_colorimetry = stream->link->dpcd_caps.dpcd_rev.raw >= 0x14 && + stream->link->dpcd_caps.dprx_feature.bits.VSC_SDP_COLORIMETRY_SUPPORTED && + !disable_colorimetry; + + if (stream->out_transfer_func.tf == TRANSFER_FUNCTION_GAMMA22) + tf = TRANSFER_FUNC_GAMMA_22; + mod_build_vsc_infopacket(stream, &stream->vsc_infopacket, stream->output_color_space, tf); + aconnector->sr_skip_count = AMDGPU_DM_PSR_ENTRY_DELAY; + + } +finish: + dc_sink_release(sink); + + return stream; +} + +/** + * amdgpu_dm_connector_poll - Poll a connector to see if it's connected to a display + * @aconnector: DM connector to poll (owns @base drm_connector and @dc_link) + * @force: if true, force polling even when DAC load detection was used + * + * Used for connectors that don't support HPD (hotplug detection) to + * periodically check whether the connector is connected to a display. + * + * When connection was determined via DAC load detection, we avoid + * re-running it on normal polls to prevent visible glitches, unless + * @force is set. + * + * Return: The probed connector status (connected/disconnected/unknown). + */ +static enum drm_connector_status +amdgpu_dm_connector_poll(struct amdgpu_dm_connector *aconnector, bool force) +{ + struct drm_connector *connector = &aconnector->base; + struct drm_device *dev = connector->dev; + struct amdgpu_device *adev = drm_to_adev(dev); + struct dc_link *link = aconnector->dc_link; + enum dc_connection_type conn_type = dc_connection_none; + enum drm_connector_status status = connector_status_disconnected; + + /* When we determined the connection using DAC load detection, + * do NOT poll the connector do detect disconnect because + * that would run DAC load detection again which can cause + * visible visual glitches. + * + * Only allow to poll such a connector again when forcing. + */ + if (!force && link->local_sink && link->type == dc_connection_analog_load) + return connector->status; + + mutex_lock(&aconnector->hpd_lock); + + if (dc_link_detect_connection_type(aconnector->dc_link, &conn_type) && + conn_type != dc_connection_none) { + mutex_lock(&adev->dm.dc_lock); + + /* Only call full link detection when a sink isn't created yet, + * ie. just when the display is plugged in, otherwise we risk flickering. + */ + if (link->local_sink || + dc_link_detect(link, DETECT_REASON_HPD)) + status = connector_status_connected; + + mutex_unlock(&adev->dm.dc_lock); + } + + if (connector->status != status) { + if (status == connector_status_disconnected) { + if (link->local_sink) + dc_sink_release(link->local_sink); + + link->local_sink = NULL; + link->dpcd_sink_count = 0; + link->type = dc_connection_none; + } + + amdgpu_dm_update_connector_after_detect(aconnector); + } + + mutex_unlock(&aconnector->hpd_lock); + return status; +} + +/** + * amdgpu_dm_connector_detect() - Detect whether a DRM connector is connected to a display + * + * A connector is considered connected when it has a sink that is not NULL. + * For connectors that support HPD (hotplug detection), the connection is + * handled in the HPD interrupt. + * For connectors that may not support HPD, such as analog connectors, + * DRM will call this function repeatedly to poll them. + * + * Notes: + * 1. This interface is NOT called in context of HPD irq. + * 2. This interface *is called* in context of user-mode ioctl. Which + * makes it a bad place for *any* MST-related activity. + * + * @connector: The DRM connector we are checking. We convert it to + * amdgpu_dm_connector so we can read the DC link and state. + * @force: If true, do a full detect again. This is used even when + * a lighter check would normally be used to avoid flicker. + * + * Return: The connector status (connected, disconnected, or unknown). + * + */ +static enum drm_connector_status +amdgpu_dm_connector_detect(struct drm_connector *connector, bool force) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + + update_subconnector_property(aconnector); + + if (aconnector->base.force == DRM_FORCE_ON || + aconnector->base.force == DRM_FORCE_ON_DIGITAL) + return connector_status_connected; + else if (aconnector->base.force == DRM_FORCE_OFF) + return connector_status_disconnected; + + /* Poll analog connectors and only when either + * disconnected or connected to an analog display. + */ + if (drm_kms_helper_is_poll_worker() && + dc_connector_supports_analog(aconnector->dc_link->link_id.id) && + (!aconnector->dc_sink || aconnector->dc_sink->edid_caps.analog)) + return amdgpu_dm_connector_poll(aconnector, force); + + return (aconnector->dc_sink ? connector_status_connected : + connector_status_disconnected); +} + +int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *connector_state, + struct drm_property *property, + uint64_t val) +{ + struct drm_device *dev = connector->dev; + struct amdgpu_device *adev = drm_to_adev(dev); + struct dm_connector_state *dm_old_state = + to_dm_connector_state(connector->state); + struct dm_connector_state *dm_new_state = + to_dm_connector_state(connector_state); + + int ret = -EINVAL; + + if (property == dev->mode_config.scaling_mode_property) { + enum amdgpu_rmx_type rmx_type; + + switch (val) { + case DRM_MODE_SCALE_CENTER: + rmx_type = RMX_CENTER; + break; + case DRM_MODE_SCALE_ASPECT: + rmx_type = RMX_ASPECT; + break; + case DRM_MODE_SCALE_FULLSCREEN: + rmx_type = RMX_FULL; + break; + case DRM_MODE_SCALE_NONE: + default: + rmx_type = RMX_OFF; + break; + } + + if (dm_old_state->scaling == rmx_type) + return 0; + + dm_new_state->scaling = rmx_type; + ret = 0; + } else if (property == adev->mode_info.underscan_hborder_property) { + dm_new_state->underscan_hborder = val; + ret = 0; + } else if (property == adev->mode_info.underscan_vborder_property) { + dm_new_state->underscan_vborder = val; + ret = 0; + } else if (property == adev->mode_info.underscan_property) { + dm_new_state->underscan_enable = val; + ret = 0; + } else if (property == adev->mode_info.abm_level_property) { + switch (val) { + case ABM_SYSFS_CONTROL: + dm_new_state->abm_sysfs_forbidden = false; + break; + case ABM_LEVEL_OFF: + dm_new_state->abm_sysfs_forbidden = true; + dm_new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE; + break; + default: + dm_new_state->abm_sysfs_forbidden = true; + dm_new_state->abm_level = val; + } + ret = 0; + } + + return ret; +} +EXPORT_IF_KUNIT(amdgpu_dm_connector_atomic_set_property); + +int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val) +{ + struct drm_device *dev = connector->dev; + struct amdgpu_device *adev = drm_to_adev(dev); + struct dm_connector_state *dm_state = + to_dm_connector_state(state); + int ret = -EINVAL; + + if (property == dev->mode_config.scaling_mode_property) { + switch (dm_state->scaling) { + case RMX_CENTER: + *val = DRM_MODE_SCALE_CENTER; + break; + case RMX_ASPECT: + *val = DRM_MODE_SCALE_ASPECT; + break; + case RMX_FULL: + *val = DRM_MODE_SCALE_FULLSCREEN; + break; + case RMX_OFF: + default: + *val = DRM_MODE_SCALE_NONE; + break; + } + ret = 0; + } else if (property == adev->mode_info.underscan_hborder_property) { + *val = dm_state->underscan_hborder; + ret = 0; + } else if (property == adev->mode_info.underscan_vborder_property) { + *val = dm_state->underscan_vborder; + ret = 0; + } else if (property == adev->mode_info.underscan_property) { + *val = dm_state->underscan_enable; + ret = 0; + } else if (property == adev->mode_info.abm_level_property) { + if (!dm_state->abm_sysfs_forbidden) + *val = ABM_SYSFS_CONTROL; + else + *val = (dm_state->abm_level != ABM_LEVEL_IMMEDIATE_DISABLE) ? + dm_state->abm_level : 0; + ret = 0; + } + + return ret; +} +EXPORT_IF_KUNIT(amdgpu_dm_connector_atomic_get_property); + +static void amdgpu_dm_connector_unregister(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = to_amdgpu_dm_connector(connector); + + if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector)) + sysfs_remove_group(&connector->kdev->kobj, &amdgpu_group); + + cec_notifier_conn_unregister(amdgpu_dm_connector->notifier); + drm_dp_aux_unregister(&amdgpu_dm_connector->dm_dp_aux.aux); +} + +static void amdgpu_dm_connector_destroy(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + struct amdgpu_device *adev = drm_to_adev(connector->dev); + struct amdgpu_display_manager *dm = &adev->dm; + + /* + * Call only if mst_mgr was initialized before since it's not done + * for all connector types. + */ + if (aconnector->mst_mgr.dev) + drm_dp_mst_topology_mgr_destroy(&aconnector->mst_mgr); + + /* Cancel and flush any pending HDMI HPD debounce work */ + if (aconnector->hdmi_hpd_debounce_delay_ms) { + cancel_delayed_work_sync(&aconnector->hdmi_hpd_debounce_work); + if (aconnector->hdmi_prev_sink) { + dc_sink_release(aconnector->hdmi_prev_sink); + aconnector->hdmi_prev_sink = NULL; + } + } + + if (aconnector->bl_idx != -1) { + backlight_device_unregister(dm->backlight_dev[aconnector->bl_idx]); + dm->backlight_dev[aconnector->bl_idx] = NULL; + } + + if (aconnector->dc_em_sink) + dc_sink_release(aconnector->dc_em_sink); + aconnector->dc_em_sink = NULL; + if (aconnector->dc_sink) + dc_sink_release(aconnector->dc_sink); + aconnector->dc_sink = NULL; + + drm_dp_cec_unregister_connector(&aconnector->dm_dp_aux.aux); + drm_connector_unregister(connector); + drm_connector_cleanup(connector); + kfree(aconnector->dm_dp_aux.aux.name); + + kfree(connector); +} + +void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector) +{ + struct dm_connector_state *old_state = + to_dm_connector_state(connector->state); + struct dm_connector_state *state; + + state = kzalloc_obj(*state); + if (!state) + return; + + if (connector->state) + __drm_atomic_helper_connector_destroy_state(connector->state); + + kfree(old_state); + + __drm_atomic_helper_connector_reset(connector, &state->base); + + state->scaling = RMX_OFF; + state->underscan_enable = false; + state->underscan_hborder = 0; + state->underscan_vborder = 0; + state->base.max_requested_bpc = 8; + state->vcpi_slots = 0; + state->pbn = 0; + + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { + if (amdgpu_dm_abm_level <= 0) + state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE; + else + state->abm_level = amdgpu_dm_abm_level; + } +} +EXPORT_IF_KUNIT(amdgpu_dm_connector_funcs_reset); + +struct drm_connector_state * +amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector) +{ + struct dm_connector_state *state = + to_dm_connector_state(connector->state); + + struct dm_connector_state *new_state = + kmemdup(state, sizeof(*state), GFP_KERNEL); + + if (!new_state) + return NULL; + + __drm_atomic_helper_connector_duplicate_state(connector, &new_state->base); + + new_state->freesync_capable = state->freesync_capable; + new_state->abm_level = state->abm_level; + new_state->scaling = state->scaling; + new_state->underscan_enable = state->underscan_enable; + new_state->underscan_hborder = state->underscan_hborder; + new_state->underscan_vborder = state->underscan_vborder; + new_state->vcpi_slots = state->vcpi_slots; + new_state->pbn = state->pbn; + return &new_state->base; +} +EXPORT_IF_KUNIT(amdgpu_dm_connector_atomic_duplicate_state); + +static int +amdgpu_dm_connector_late_register(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + int r; + + if (amdgpu_dm_should_create_sysfs(amdgpu_dm_connector)) { + r = sysfs_create_group(&connector->kdev->kobj, + &amdgpu_group); + if (r) + return r; + } + + amdgpu_dm_register_backlight_device(amdgpu_dm_connector); + + if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) || + (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) { + amdgpu_dm_connector->dm_dp_aux.aux.dev = connector->kdev; + r = drm_dp_aux_register(&amdgpu_dm_connector->dm_dp_aux.aux); + if (r) + return r; + } + +#if defined(CONFIG_DEBUG_FS) + connector_debugfs_init(amdgpu_dm_connector); +#endif + + return 0; +} + +static void amdgpu_dm_connector_funcs_force(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + struct dc_link *dc_link = aconnector->dc_link; + struct dc_sink *dc_em_sink = aconnector->dc_em_sink; + const struct drm_edid *drm_edid; + struct i2c_adapter *ddc; + struct drm_device *dev = connector->dev; + + if (dc_link && dc_link->aux_mode) + ddc = &aconnector->dm_dp_aux.aux.ddc; + else + ddc = &aconnector->i2c->base; + + drm_edid = drm_edid_read_ddc(connector, ddc); + drm_edid_connector_update(connector, drm_edid); + if (!drm_edid) { + drm_err(dev, "No EDID found on connector: %s.\n", connector->name); + return; + } + + aconnector->drm_edid = drm_edid; + /* Update emulated (virtual) sink's EDID */ + if (dc_em_sink && dc_link) { + /* FIXME: Get rid of drm_edid_raw() */ + const struct edid *edid = drm_edid_raw(drm_edid); + + memset(&dc_em_sink->edid_caps, 0, sizeof(struct dc_edid_caps)); + memmove(dc_em_sink->dc_edid.raw_edid, edid, + (edid->extensions + 1) * EDID_LENGTH); + dm_helpers_parse_edid_caps( + dc_link, + &dc_em_sink->dc_edid, + &dc_em_sink->edid_caps); + } +} + +static const struct drm_connector_funcs amdgpu_dm_connector_funcs = { + .reset = amdgpu_dm_connector_funcs_reset, + .detect = amdgpu_dm_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = amdgpu_dm_connector_destroy, + .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_set_property = amdgpu_dm_connector_atomic_set_property, + .atomic_get_property = amdgpu_dm_connector_atomic_get_property, + .late_register = amdgpu_dm_connector_late_register, + .early_unregister = amdgpu_dm_connector_unregister, + .force = amdgpu_dm_connector_funcs_force +}; + +static int get_modes(struct drm_connector *connector) +{ + return amdgpu_dm_connector_get_modes(connector); +} + +static void create_eml_sink(struct amdgpu_dm_connector *aconnector) +{ + struct drm_connector *connector = &aconnector->base; + struct dc_link *dc_link = aconnector->dc_link; + struct dc_sink_init_data init_params = { + .link = aconnector->dc_link, + .sink_signal = SIGNAL_TYPE_VIRTUAL + }; + const struct drm_edid *drm_edid; + const struct edid *edid; + struct i2c_adapter *ddc; + + if (dc_link && dc_link->aux_mode) + ddc = &aconnector->dm_dp_aux.aux.ddc; + else + ddc = &aconnector->i2c->base; + + drm_edid = drm_edid_read_ddc(connector, ddc); + drm_edid_connector_update(connector, drm_edid); + if (!drm_edid) { + drm_err(connector->dev, "No EDID found on connector: %s.\n", connector->name); + return; + } + + if (connector->display_info.is_hdmi) + init_params.sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + + aconnector->drm_edid = drm_edid; + + /* FIXME: Get rid of drm_edid_raw() */ + edid = drm_edid_raw(drm_edid); + aconnector->dc_em_sink = dc_link_add_remote_sink( + aconnector->dc_link, + (uint8_t *)edid, + (edid->extensions + 1) * EDID_LENGTH, + &init_params); + + if (aconnector->base.force == DRM_FORCE_ON) { + aconnector->dc_sink = aconnector->dc_link->local_sink ? + aconnector->dc_link->local_sink : + aconnector->dc_em_sink; + if (aconnector->dc_sink) + dc_sink_retain(aconnector->dc_sink); + } +} + +static void handle_edid_mgmt(struct amdgpu_dm_connector *aconnector) +{ + struct dc_link *link = (struct dc_link *)aconnector->dc_link; + + /* + * In case of headless boot with force on for DP managed connector + * Those settings have to be != 0 to get initial modeset + */ + if (link->connector_signal == SIGNAL_TYPE_DISPLAY_PORT) { + link->verified_link_cap.lane_count = LANE_COUNT_FOUR; + link->verified_link_cap.link_rate = LINK_RATE_HIGH2; + } + + create_eml_sink(aconnector); +} + +static enum dc_status dm_validate_stream_and_context(struct dc *dc, + struct dc_stream_state *stream) +{ + enum dc_status dc_result = DC_ERROR_UNEXPECTED; + struct dc_plane_state *dc_plane_state = NULL; + struct dc_state *dc_state = NULL; + + if (!stream) + goto cleanup; + + dc_plane_state = dc_create_plane_state(dc); + if (!dc_plane_state) + goto cleanup; + + dc_state = dc_state_create(dc, NULL); + if (!dc_state) + goto cleanup; + + /* populate stream to plane */ + dc_plane_state->src_rect.height = stream->src.height; + dc_plane_state->src_rect.width = stream->src.width; + dc_plane_state->dst_rect.height = stream->src.height; + dc_plane_state->dst_rect.width = stream->src.width; + dc_plane_state->clip_rect.height = stream->src.height; + dc_plane_state->clip_rect.width = stream->src.width; + dc_plane_state->plane_size.surface_pitch = ((stream->src.width + 255) / 256) * 256; + dc_plane_state->plane_size.surface_size.height = stream->src.height; + dc_plane_state->plane_size.surface_size.width = stream->src.width; + dc_plane_state->plane_size.chroma_size.height = stream->src.height; + dc_plane_state->plane_size.chroma_size.width = stream->src.width; + dc_plane_state->format = SURFACE_PIXEL_FORMAT_GRPH_ARGB8888; + dc_plane_state->tiling_info.gfx9.swizzle = DC_SW_UNKNOWN; + dc_plane_state->rotation = ROTATION_ANGLE_0; + dc_plane_state->is_tiling_rotated = false; + dc_plane_state->tiling_info.gfx8.array_mode = DC_ARRAY_LINEAR_GENERAL; + + dc_result = dc_validate_stream(dc, stream); + if (dc_result == DC_OK) + dc_result = dc_validate_plane(dc, dc_plane_state); + + if (dc_result == DC_OK) + dc_result = dc_state_add_stream(dc, dc_state, stream); + + if (dc_result == DC_OK && !dc_state_add_plane( + dc, + stream, + dc_plane_state, + dc_state)) + dc_result = DC_FAIL_ATTACH_SURFACES; + + if (dc_result == DC_OK) + dc_result = dc_validate_global_state(dc, dc_state, DC_VALIDATE_MODE_ONLY); + +cleanup: + if (dc_state) + dc_state_release(dc_state); + + if (dc_plane_state) + dc_plane_state_release(dc_plane_state); + + return dc_result; +} + +static enum dc_status +dm_validate_stream_color_format(const struct drm_connector_state *drm_state, + const struct dc_stream_state *stream) +{ + enum dc_pixel_encoding encoding; + + if (!drm_state->color_format) + return DC_OK; + + switch (drm_state->color_format) { + case DRM_CONNECTOR_COLOR_FORMAT_AUTO: + case DRM_CONNECTOR_COLOR_FORMAT_RGB444: + encoding = PIXEL_ENCODING_RGB; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR444: + encoding = PIXEL_ENCODING_YCBCR444; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR422: + encoding = PIXEL_ENCODING_YCBCR422; + break; + case DRM_CONNECTOR_COLOR_FORMAT_YCBCR420: + encoding = PIXEL_ENCODING_YCBCR420; + break; + default: + encoding = PIXEL_ENCODING_UNDEFINED; + break; + } + + return encoding == stream->timing.pixel_encoding ? + DC_OK : DC_UNSUPPORTED_VALUE; +} + +struct dc_stream_state * +amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector, + const struct drm_display_mode *drm_mode, + const struct dm_connector_state *dm_state, + const struct dc_stream_state *old_stream) +{ + struct amdgpu_dm_connector *aconnector = NULL; + struct amdgpu_device *adev = drm_to_adev(connector->dev); + struct dc_stream_state *stream; + const struct drm_connector_state *drm_state = dm_state ? &dm_state->base : NULL; + int requested_bpc = drm_state ? drm_state->max_requested_bpc : 8; + enum dc_status dc_result = DC_OK; + uint8_t bpc_limit = 6; + + if (!dm_state) + return NULL; + + if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) + aconnector = to_amdgpu_dm_connector(connector); + + if (aconnector && + (aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_TYPE_A || + aconnector->dc_link->connector_signal == SIGNAL_TYPE_HDMI_FRL || + aconnector->dc_link->dpcd_caps.dongle_type == DISPLAY_DONGLE_DP_HDMI_CONVERTER)) + bpc_limit = 8; + + do { + drm_dbg_kms(connector->dev, "Trying with %d bpc\n", requested_bpc); + stream = create_stream_for_sink(connector, drm_mode, + dm_state, old_stream, + requested_bpc); + if (stream == NULL) { + drm_err(adev_to_drm(adev), "Failed to create stream for sink!\n"); + break; + } + + dc_result = dc_validate_stream(adev->dm.dc, stream); + + if (!aconnector) /* writeback connector */ + return stream; + + if (dc_result == DC_OK && stream->signal == SIGNAL_TYPE_DISPLAY_PORT_MST) + dc_result = dm_dp_mst_is_port_support_mode(aconnector, stream); + + if (dc_result == DC_OK) + dc_result = dm_validate_stream_and_context(adev->dm.dc, stream); + + if (dc_result == DC_OK) + dc_result = dm_validate_stream_color_format(drm_state, stream); + + if (dc_result != DC_OK) { + drm_dbg_kms(connector->dev, "Pruned mode %d x %d (clk %d) %s %s -- %s\n", + drm_mode->hdisplay, + drm_mode->vdisplay, + drm_mode->clock, + dc_pixel_encoding_to_str(stream->timing.pixel_encoding), + dc_color_depth_to_str(stream->timing.display_color_depth), + dc_status_to_str(dc_result)); + + dc_stream_release(stream); + stream = NULL; + requested_bpc -= 2; /* lower bpc to retry validation */ + } + + } while (stream == NULL && requested_bpc >= bpc_limit); + + switch (dc_result) { + /* + * If we failed to validate DP bandwidth stream with the requested RGB color depth, + * we try to fallback and configure in order: + * YUV422 (8bpc, 6bpc) + * YUV420 (8bpc, 6bpc) + */ + case DC_FAIL_ENC_VALIDATE: + case DC_EXCEED_DONGLE_CAP: + case DC_NO_DP_LINK_BANDWIDTH: + /* recursively entered twice and already tried both YUV422 and YUV420 */ + if (aconnector->force_yuv422_output && aconnector->force_yuv420_output) + break; + /* first failure; try YUV422 */ + if (!aconnector->force_yuv422_output) { + drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV422\n", + __func__, __LINE__, dc_result); + aconnector->force_yuv422_output = true; + /* recursively entered and YUV422 failed, try YUV420 */ + } else if (!aconnector->force_yuv420_output) { + drm_dbg_kms(connector->dev, "%s:%d Validation failed with %d, retrying w/ YUV420\n", + __func__, __LINE__, dc_result); + aconnector->force_yuv420_output = true; + } + stream = amdgpu_dm_create_validate_stream_for_sink(connector, drm_mode, + dm_state, old_stream); + aconnector->force_yuv422_output = false; + aconnector->force_yuv420_output = false; + break; + case DC_OK: + break; + default: + drm_dbg_kms(connector->dev, "%s:%d Unhandled validation failure %d\n", + __func__, __LINE__, dc_result); + break; + } + + return stream; +} + +enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector, + const struct drm_display_mode *mode) +{ + int result = MODE_ERROR; + struct dc_sink *dc_sink; + struct drm_display_mode *test_mode; + /* TODO: Unhardcode stream count */ + struct dc_stream_state *stream; + /* we always have an amdgpu_dm_connector here since we got + * here via the amdgpu_dm_connector_helper_funcs + */ + struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) || + (mode->flags & DRM_MODE_FLAG_DBLSCAN)) + return result; + + /* + * Only run this the first time mode_valid is called to initilialize + * EDID mgmt + */ + if (aconnector->base.force != DRM_FORCE_UNSPECIFIED && + !aconnector->dc_em_sink) + handle_edid_mgmt(aconnector); + + dc_sink = to_amdgpu_dm_connector(connector)->dc_sink; + + if (dc_sink == NULL && aconnector->base.force != DRM_FORCE_ON_DIGITAL && + aconnector->base.force != DRM_FORCE_ON) { + drm_err(connector->dev, "dc_sink is NULL!\n"); + goto fail; + } + + test_mode = drm_mode_duplicate(connector->dev, mode); + if (!test_mode) + goto fail; + + drm_mode_set_crtcinfo(test_mode, 0); + + stream = amdgpu_dm_create_validate_stream_for_sink(connector, test_mode, + to_dm_connector_state(connector->state), + NULL); + drm_mode_destroy(connector->dev, test_mode); + if (stream) { + dc_stream_release(stream); + result = MODE_OK; + } + +fail: + /* TODO: error handling*/ + return result; +} + +int amdgpu_dm_fill_hdr_info_packet(const struct drm_connector_state *state, + struct dc_info_packet *out) +{ + struct hdmi_drm_infoframe frame; + unsigned char buf[30]; /* 26 + 4 */ + ssize_t len; + int ret, i; + + memset(out, 0, sizeof(*out)); + + if (!state->hdr_output_metadata) + return 0; + + ret = drm_hdmi_infoframe_set_hdr_metadata(&frame, state); + if (ret) + return ret; + + len = hdmi_drm_infoframe_pack_only(&frame, buf, sizeof(buf)); + if (len < 0) + return (int)len; + + /* Static metadata is a fixed 26 bytes + 4 byte header. */ + if (len != 30) + return -EINVAL; + + /* Prepare the infopacket for DC. */ + switch (state->connector->connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + out->hb0 = 0x87; /* type */ + out->hb1 = 0x01; /* version */ + out->hb2 = 0x1A; /* length */ + out->sb[0] = buf[3]; /* checksum */ + i = 1; + break; + + case DRM_MODE_CONNECTOR_DisplayPort: + case DRM_MODE_CONNECTOR_eDP: + out->hb0 = 0x00; /* sdp id, zero */ + out->hb1 = 0x87; /* type */ + out->hb2 = 0x1D; /* payload len - 1 */ + out->hb3 = (0x13 << 2); /* sdp version */ + out->sb[0] = 0x01; /* version */ + out->sb[1] = 0x1A; /* length */ + i = 2; + break; + + default: + return -EINVAL; + } + + memcpy(&out->sb[i], &buf[4], 26); + out->valid = true; + + print_hex_dump(KERN_DEBUG, "HDR SB:", DUMP_PREFIX_NONE, 16, 1, out->sb, + sizeof(out->sb), false); + + return 0; +} +EXPORT_IF_KUNIT(amdgpu_dm_fill_hdr_info_packet); + +static int +amdgpu_dm_connector_atomic_check(struct drm_connector *conn, + struct drm_atomic_commit *state) +{ + struct drm_connector_state *new_con_state = + drm_atomic_get_new_connector_state(state, conn); + struct drm_connector_state *old_con_state = + drm_atomic_get_old_connector_state(state, conn); + struct drm_crtc *crtc = new_con_state->crtc; + struct drm_crtc_state *new_crtc_state; + struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn); + int ret; + + if (WARN_ON(unlikely(!old_con_state || !new_con_state))) + return -EINVAL; + + trace_amdgpu_dm_connector_atomic_check(new_con_state); + + if (conn->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { + ret = drm_dp_mst_root_conn_atomic_check(new_con_state, &aconn->mst_mgr); + if (ret < 0) + return ret; + } + + if (!crtc) + return 0; + + if (new_con_state->privacy_screen_sw_state != old_con_state->privacy_screen_sw_state) { + new_crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(new_crtc_state)) + return PTR_ERR(new_crtc_state); + + new_crtc_state->mode_changed = true; + } + + if (new_con_state->colorspace != old_con_state->colorspace) { + new_crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(new_crtc_state)) + return PTR_ERR(new_crtc_state); + + new_crtc_state->mode_changed = true; + } + + if (new_con_state->content_type != old_con_state->content_type) { + new_crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(new_crtc_state)) + return PTR_ERR(new_crtc_state); + + new_crtc_state->mode_changed = true; + } + + if (!drm_connector_atomic_hdr_metadata_equal(old_con_state, new_con_state)) { + struct dc_info_packet hdr_infopacket; + + ret = amdgpu_dm_fill_hdr_info_packet(new_con_state, &hdr_infopacket); + if (ret) + return ret; + + new_crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(new_crtc_state)) + return PTR_ERR(new_crtc_state); + + /* + * DC considers the stream backends changed if the + * static metadata changes. Forcing the modeset also + * gives a simple way for userspace to switch from + * 8bpc to 10bpc when setting the metadata to enter + * or exit HDR. + * + * Changing the static metadata after it's been + * set is permissible, however. So only force a + * modeset if we're entering or exiting HDR. + */ + new_crtc_state->mode_changed = new_crtc_state->mode_changed || + !old_con_state->hdr_output_metadata || + !new_con_state->hdr_output_metadata; + } + + return 0; +} + +static const struct drm_connector_helper_funcs +amdgpu_dm_connector_helper_funcs = { + /* + * If hotplugging a second bigger display in FB Con mode, bigger resolution + * modes will be filtered by drm_mode_validate_size(), and those modes + * are missing after user start lightdm. So we need to renew modes list. + * in get_modes call back, not just return the modes count + */ + .get_modes = get_modes, + .mode_valid = amdgpu_dm_connector_mode_valid, + .atomic_check = amdgpu_dm_connector_atomic_check, +}; + +int amdgpu_dm_convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth) +{ + switch (display_color_depth) { + case COLOR_DEPTH_666: + return 6; + case COLOR_DEPTH_888: + return 8; + case COLOR_DEPTH_101010: + return 10; + case COLOR_DEPTH_121212: + return 12; + case COLOR_DEPTH_141414: + return 14; + case COLOR_DEPTH_161616: + return 16; + default: + break; + } + return 0; +} +EXPORT_IF_KUNIT(amdgpu_dm_convert_dc_color_depth_into_bpc); + +STATIC_IFN_KUNIT int to_drm_connector_type(enum signal_type st, uint32_t connector_id) +{ + switch (st) { + case SIGNAL_TYPE_HDMI_TYPE_A: + return DRM_MODE_CONNECTOR_HDMIA; + case SIGNAL_TYPE_EDP: + return DRM_MODE_CONNECTOR_eDP; + case SIGNAL_TYPE_LVDS: + return DRM_MODE_CONNECTOR_LVDS; + case SIGNAL_TYPE_RGB: + return DRM_MODE_CONNECTOR_VGA; + case SIGNAL_TYPE_DISPLAY_PORT: + case SIGNAL_TYPE_DISPLAY_PORT_MST: + /* External DP bridges have a different connector type. */ + if (connector_id == CONNECTOR_ID_VGA) + return DRM_MODE_CONNECTOR_VGA; + else if (connector_id == CONNECTOR_ID_LVDS) + return DRM_MODE_CONNECTOR_LVDS; + + return DRM_MODE_CONNECTOR_DisplayPort; + case SIGNAL_TYPE_DVI_DUAL_LINK: + case SIGNAL_TYPE_DVI_SINGLE_LINK: + if (connector_id == CONNECTOR_ID_SINGLE_LINK_DVII || + connector_id == CONNECTOR_ID_DUAL_LINK_DVII) + return DRM_MODE_CONNECTOR_DVII; + + return DRM_MODE_CONNECTOR_DVID; + case SIGNAL_TYPE_VIRTUAL: + return DRM_MODE_CONNECTOR_VIRTUAL; + + default: + return DRM_MODE_CONNECTOR_Unknown; + } +} +EXPORT_IF_KUNIT(to_drm_connector_type); + +static struct drm_encoder *amdgpu_dm_connector_to_encoder(struct drm_connector *connector) +{ + struct drm_encoder *encoder; + + /* There is only one encoder per connector */ + drm_connector_for_each_possible_encoder(connector, encoder) + return encoder; + + return NULL; +} + +static void amdgpu_dm_get_native_mode(struct drm_connector *connector) +{ + struct drm_encoder *encoder; + struct amdgpu_encoder *amdgpu_encoder; + + encoder = amdgpu_dm_connector_to_encoder(connector); + + if (encoder == NULL) + return; + + amdgpu_encoder = to_amdgpu_encoder(encoder); + + amdgpu_encoder->native_mode.clock = 0; + + if (!list_empty(&connector->probed_modes)) { + struct drm_display_mode *preferred_mode = NULL; + + list_for_each_entry(preferred_mode, + &connector->probed_modes, + head) { + if (preferred_mode->type & DRM_MODE_TYPE_PREFERRED) + amdgpu_encoder->native_mode = *preferred_mode; + + break; + } + + } +} + +static struct drm_display_mode * +amdgpu_dm_create_common_mode(struct drm_encoder *encoder, + const char *name, + int hdisplay, int vdisplay) +{ + struct drm_device *dev = encoder->dev; + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); + struct drm_display_mode *mode = NULL; + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; + + mode = drm_mode_duplicate(dev, native_mode); + + if (mode == NULL) + return NULL; + + mode->hdisplay = hdisplay; + mode->vdisplay = vdisplay; + mode->type &= ~DRM_MODE_TYPE_PREFERRED; + strscpy(mode->name, name, DRM_DISPLAY_MODE_LEN); + + return mode; + +} + +static const struct amdgpu_dm_mode_size { + char name[DRM_DISPLAY_MODE_LEN]; + int w; + int h; +} common_modes[] = { + { "640x480", 640, 480}, + { "800x600", 800, 600}, + { "1024x768", 1024, 768}, + { "1280x720", 1280, 720}, + { "1280x800", 1280, 800}, + {"1280x1024", 1280, 1024}, + { "1440x900", 1440, 900}, + {"1680x1050", 1680, 1050}, + {"1600x1200", 1600, 1200}, + {"1920x1080", 1920, 1080}, + {"1920x1200", 1920, 1200} +}; + +static void amdgpu_dm_connector_add_common_modes(struct drm_encoder *encoder, + struct drm_connector *connector) +{ + struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder); + struct drm_display_mode *mode = NULL; + struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + int i; + int n; + + if ((connector->connector_type != DRM_MODE_CONNECTOR_eDP) && + (connector->connector_type != DRM_MODE_CONNECTOR_LVDS)) + return; + + n = ARRAY_SIZE(common_modes); + + for (i = 0; i < n; i++) { + struct drm_display_mode *curmode = NULL; + bool mode_existed = false; + + if (common_modes[i].w > native_mode->hdisplay || + common_modes[i].h > native_mode->vdisplay || + (common_modes[i].w == native_mode->hdisplay && + common_modes[i].h == native_mode->vdisplay)) + continue; + + list_for_each_entry(curmode, &connector->probed_modes, head) { + if (common_modes[i].w == curmode->hdisplay && + common_modes[i].h == curmode->vdisplay) { + mode_existed = true; + break; + } + } + + if (mode_existed) + continue; + + mode = amdgpu_dm_create_common_mode(encoder, + common_modes[i].name, common_modes[i].w, + common_modes[i].h); + if (!mode) + continue; + + drm_mode_probed_add(connector, mode); + amdgpu_dm_connector->num_modes++; + } +} + +void amdgpu_set_panel_orientation(struct drm_connector *connector) +{ + struct drm_encoder *encoder; + struct amdgpu_encoder *amdgpu_encoder; + const struct drm_display_mode *native_mode; + + if (connector->connector_type != DRM_MODE_CONNECTOR_eDP && + connector->connector_type != DRM_MODE_CONNECTOR_LVDS) + return; + + mutex_lock(&connector->dev->mode_config.mutex); + amdgpu_dm_connector_get_modes(connector); + mutex_unlock(&connector->dev->mode_config.mutex); + + encoder = amdgpu_dm_connector_to_encoder(connector); + if (!encoder) + return; + + amdgpu_encoder = to_amdgpu_encoder(encoder); + + native_mode = &amdgpu_encoder->native_mode; + if (native_mode->hdisplay == 0 || native_mode->vdisplay == 0) + return; + + drm_connector_set_panel_orientation_with_quirk(connector, + DRM_MODE_PANEL_ORIENTATION_UNKNOWN, + native_mode->hdisplay, + native_mode->vdisplay); +} + +static void amdgpu_dm_connector_ddc_get_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + + if (drm_edid) { + /* empty probed_modes */ + INIT_LIST_HEAD(&connector->probed_modes); + amdgpu_dm_connector->num_modes = + drm_edid_connector_add_modes(connector); + + /* sorting the probed modes before calling function + * amdgpu_dm_get_native_mode() since EDID can have + * more than one preferred mode. The modes that are + * later in the probed mode list could be of higher + * and preferred resolution. For example, 3840x2160 + * resolution in base EDID preferred timing and 4096x2160 + * preferred resolution in DID extension block later. + */ + drm_mode_sort(&connector->probed_modes); + amdgpu_dm_get_native_mode(connector); + + /* Freesync capabilities are reset by calling + * drm_edid_connector_add_modes() and need to be + * restored here. + */ + amdgpu_dm_update_freesync_caps(connector, drm_edid, false); + } else { + amdgpu_dm_connector->num_modes = 0; + } +} + +STATIC_IFN_KUNIT bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector, + struct drm_display_mode *mode) +{ + struct drm_display_mode *m; + + list_for_each_entry(m, &aconnector->base.probed_modes, head) { + if (drm_mode_equal(m, mode)) + return true; + } + + return false; +} +EXPORT_IF_KUNIT(is_duplicate_mode); + +static uint add_fs_modes(struct amdgpu_dm_connector *aconnector) +{ + const struct drm_display_mode *m; + struct drm_display_mode *new_mode; + uint i; + u32 new_modes_count = 0; + + /* Standard FPS values + * + * 23.976 - TV/NTSC + * 24 - Cinema + * 25 - TV/PAL + * 29.97 - TV/NTSC + * 30 - TV/NTSC + * 48 - Cinema HFR + * 50 - TV/PAL + * 60 - Commonly used + * 48,72,96,120 - Multiples of 24 + */ + static const u32 common_rates[] = { + 23976, 24000, 25000, 29970, 30000, + 48000, 50000, 60000, 72000, 96000, 120000 + }; + + /* + * Find mode with highest refresh rate with the same resolution + * as the preferred mode. Some monitors report a preferred mode + * with lower resolution than the highest refresh rate supported. + */ + + m = amdgpu_dm_get_highest_refresh_rate_mode(aconnector, true); + if (!m) + return 0; + + for (i = 0; i < ARRAY_SIZE(common_rates); i++) { + u64 target_vtotal, target_vtotal_diff; + u64 num, den; + + if (drm_mode_vrefresh(m) * 1000 < common_rates[i]) + continue; + + if (common_rates[i] < aconnector->min_vfreq * 1000 || + common_rates[i] > aconnector->max_vfreq * 1000) + continue; + + num = (unsigned long long)m->clock * 1000 * 1000; + den = common_rates[i] * (unsigned long long)m->htotal; + target_vtotal = div_u64(num, den); + target_vtotal_diff = target_vtotal - m->vtotal; + + /* Check for illegal modes */ + if (m->vsync_start + target_vtotal_diff < m->vdisplay || + m->vsync_end + target_vtotal_diff < m->vsync_start || + m->vtotal + target_vtotal_diff < m->vsync_end) + continue; + + new_mode = drm_mode_duplicate(aconnector->base.dev, m); + if (!new_mode) + goto out; + + new_mode->vtotal += (u16)target_vtotal_diff; + new_mode->vsync_start += (u16)target_vtotal_diff; + new_mode->vsync_end += (u16)target_vtotal_diff; + new_mode->type &= ~DRM_MODE_TYPE_PREFERRED; + new_mode->type |= DRM_MODE_TYPE_DRIVER; + + if (!is_duplicate_mode(aconnector, new_mode)) { + drm_mode_probed_add(&aconnector->base, new_mode); + new_modes_count += 1; + } else + drm_mode_destroy(aconnector->base.dev, new_mode); + } + out: + return new_modes_count; +} + +static void amdgpu_dm_connector_add_freesync_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + + if (!(amdgpu_freesync_vid_mode && drm_edid)) + return; + + if (!amdgpu_dm_connector->dc_sink || !amdgpu_dm_connector->dc_link) + return; + + if (!dc_supports_vrr(amdgpu_dm_connector->dc_sink->ctx->dce_version)) + return; + + if (dc_connector_supports_analog(amdgpu_dm_connector->dc_link->link_id.id) && + amdgpu_dm_connector->dc_sink->edid_caps.analog) + return; + + if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + amdgpu_dm_connector->num_modes += + add_fs_modes(amdgpu_dm_connector); +} + +static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) +{ + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + struct dc_link *dc_link = amdgpu_dm_connector->dc_link; + struct drm_encoder *encoder; + const struct drm_edid *drm_edid = amdgpu_dm_connector->drm_edid; + struct dc_link_settings *verified_link_cap = &dc_link->verified_link_cap; + const struct dc *dc = dc_link->dc; + + encoder = amdgpu_dm_connector_to_encoder(connector); + + if (!drm_edid) { + amdgpu_dm_connector->num_modes = + drm_add_modes_noedid(connector, 640, 480); + if (dc->link_srv->dp_get_encoding_format(verified_link_cap) == DP_128b_132b_ENCODING) + amdgpu_dm_connector->num_modes += + drm_add_modes_noedid(connector, 1920, 1080); + + if (amdgpu_dm_connector->dc_sink && + amdgpu_dm_connector->dc_sink->edid_caps.analog && + dc_connector_supports_analog(dc_link->link_id.id)) { + /* Analog monitor connected by DAC load detection. + * Add common modes. It will be up to the user to select one that works. + */ + for (int i = 0; i < ARRAY_SIZE(common_modes); i++) + amdgpu_dm_connector->num_modes += drm_add_modes_noedid( + connector, common_modes[i].w, common_modes[i].h); + } + } else { + amdgpu_dm_connector_ddc_get_modes(connector, drm_edid); + if (encoder) + amdgpu_dm_connector_add_common_modes(encoder, connector); + amdgpu_dm_connector_add_freesync_modes(connector, drm_edid); + } + amdgpu_dm_fbc_init(connector); + + return amdgpu_dm_connector->num_modes; +} + +static const u32 supported_colorspaces = + BIT(DRM_MODE_COLORIMETRY_BT709_YCC) | + BIT(DRM_MODE_COLORIMETRY_OPRGB) | + BIT(DRM_MODE_COLORIMETRY_BT2020_RGB) | + BIT(DRM_MODE_COLORIMETRY_BT2020_YCC); + +static const u32 supported_colorformats = + BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR444) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR422) | + BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + +static void hdmi_frl_status_polling_work(struct work_struct *work) +{ + struct amdgpu_display_manager *dm = + container_of(to_delayed_work(work), struct amdgpu_display_manager, + hdmi_frl_status_polling_work); + struct dc *dc = dm->dc; + struct dc_link *dc_link; + bool link_update = false; + + for (int i = 0; i < MAX_LINKS; i++) { + dc_link = dc->links[i]; + + + if (!dc_link || !dc_link->local_sink) + continue; + + if (!dc_is_hdmi_signal(dc_link->connector_signal)) + continue; + + if (dc_link->connector_signal != SIGNAL_TYPE_HDMI_FRL) + continue; + + link_update = dc_link_frl_poll_status_flag(dc_link); + if (link_update) { + mutex_lock(&dm->dc_lock); + dc_link_detect(dc_link, DETECT_REASON_RETRAIN); + mutex_unlock(&dm->dc_lock); + } + } + + queue_delayed_work(dm->hdmi_frl_status_polling_wq, + &dm->hdmi_frl_status_polling_work, + msecs_to_jiffies(dm->hdmi_frl_status_polling_delay_ms)); +} + +void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector, + int connector_type, + struct dc_link *link, + int link_index) +{ + struct amdgpu_device *adev = drm_to_adev(dm->ddev); + + /* + * Some of the properties below require access to state, like bpc. + * Allocate some default initial connector state with our reset helper. + */ + if (aconnector->base.funcs->reset) + aconnector->base.funcs->reset(&aconnector->base); + + aconnector->connector_id = link_index; + aconnector->bl_idx = -1; + aconnector->dc_link = link; + aconnector->base.interlace_allowed = false; + aconnector->base.doublescan_allowed = false; + aconnector->base.stereo_allowed = false; + aconnector->base.dpms = DRM_MODE_DPMS_OFF; + aconnector->hpd.hpd = AMDGPU_HPD_NONE; /* not used */ + aconnector->audio_inst = -1; + aconnector->pack_sdp_v1_3 = false; + aconnector->as_type = ADAPTIVE_SYNC_TYPE_NONE; + memset(&aconnector->vsdb_info, 0, sizeof(aconnector->vsdb_info)); + mutex_init(&aconnector->hpd_lock); + mutex_init(&aconnector->handle_mst_msg_ready); + + /* + * If HDMI HPD debounce delay is set, use the minimum between selected + * value and AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS + */ + if (amdgpu_hdmi_hpd_debounce_delay_ms) { + aconnector->hdmi_hpd_debounce_delay_ms = min(amdgpu_hdmi_hpd_debounce_delay_ms, + AMDGPU_DM_MAX_HDMI_HPD_DEBOUNCE_MS); + INIT_DELAYED_WORK(&aconnector->hdmi_hpd_debounce_work, amdgpu_dm_hdmi_hpd_debounce_work); + aconnector->hdmi_prev_sink = NULL; + } else { + aconnector->hdmi_hpd_debounce_delay_ms = 0; + } + + dm->hdmi_frl_status_polling_delay_ms = 200; + INIT_DELAYED_WORK(&dm->hdmi_frl_status_polling_work, hdmi_frl_status_polling_work); + /* + * configure support HPD hot plug connector_>polled default value is 0 + * which means HPD hot plug not supported + */ + switch (connector_type) { + case DRM_MODE_CONNECTOR_HDMIA: + aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; + aconnector->base.ycbcr_420_allowed = + link->link_enc->features.hdmi_ycbcr420_supported ? true : false; + break; + case DRM_MODE_CONNECTOR_DisplayPort: + aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; + link->link_enc = link_enc_cfg_get_link_enc(link); + ASSERT(link->link_enc); + if (link->link_enc) + aconnector->base.ycbcr_420_allowed = + link->link_enc->features.dp_ycbcr420_supported ? true : false; + break; + case DRM_MODE_CONNECTOR_DVID: + aconnector->base.polled = DRM_CONNECTOR_POLL_HPD; + break; + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_VGA: + aconnector->base.polled = + DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + break; + default: + break; + } + + drm_object_attach_property(&aconnector->base.base, + dm->ddev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_NONE); + + if (connector_type == DRM_MODE_CONNECTOR_HDMIA + || (connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root)) + drm_connector_attach_broadcast_rgb_property(&aconnector->base); + + drm_object_attach_property(&aconnector->base.base, + adev->mode_info.underscan_property, + UNDERSCAN_OFF); + drm_object_attach_property(&aconnector->base.base, + adev->mode_info.underscan_hborder_property, + 0); + drm_object_attach_property(&aconnector->base.base, + adev->mode_info.underscan_vborder_property, + 0); + + if (!aconnector->mst_root) + drm_connector_attach_max_bpc_property(&aconnector->base, 8, 16); + + aconnector->base.state->max_bpc = 16; + aconnector->base.state->max_requested_bpc = aconnector->base.state->max_bpc; + + if (connector_type == DRM_MODE_CONNECTOR_HDMIA) { + /* Content Type is currently only implemented for HDMI. */ + drm_connector_attach_content_type_property(&aconnector->base); + } + + if (connector_type == DRM_MODE_CONNECTOR_HDMIA) { + if (!drm_mode_create_hdmi_colorspace_property(&aconnector->base, supported_colorspaces)) + drm_connector_attach_colorspace_property(&aconnector->base); + } else if ((connector_type == DRM_MODE_CONNECTOR_DisplayPort && !aconnector->mst_root) || + connector_type == DRM_MODE_CONNECTOR_eDP) { + if (!drm_mode_create_dp_colorspace_property(&aconnector->base, supported_colorspaces)) + drm_connector_attach_colorspace_property(&aconnector->base); + } + + if (connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector_type == DRM_MODE_CONNECTOR_DisplayPort || + connector_type == DRM_MODE_CONNECTOR_eDP) { + drm_connector_attach_hdr_output_metadata_property(&aconnector->base); + + if (!aconnector->mst_root) { + drm_connector_attach_vrr_capable_property(&aconnector->base); + drm_connector_attach_color_format_property(&aconnector->base, + supported_colorformats); + } + + + if (adev->dm.hdcp_workqueue) + drm_connector_attach_content_protection_property(&aconnector->base, true); + } + + if (connector_type == DRM_MODE_CONNECTOR_eDP) { + struct drm_privacy_screen *privacy_screen; + + drm_connector_attach_panel_type_property(&aconnector->base); + + privacy_screen = drm_privacy_screen_get(adev_to_drm(adev)->dev, NULL); + if (!IS_ERR(privacy_screen)) { + drm_connector_attach_privacy_screen_provider(&aconnector->base, + privacy_screen); + } else if (PTR_ERR(privacy_screen) != -ENODEV) { + drm_warn(adev_to_drm(adev), "Error getting privacy-screen\n"); + } + } +} + +static int amdgpu_dm_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg *msgs, int num) +{ + struct amdgpu_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap); + struct ddc_service *ddc_service = i2c->ddc_service; + struct i2c_command cmd; + int i; + int result = -EIO; + + if (!ddc_service->ddc_pin) + return result; + + cmd.payloads = kzalloc_objs(struct i2c_payload, num); + + if (!cmd.payloads) + return result; + + cmd.number_of_payloads = num; + cmd.engine = I2C_COMMAND_ENGINE_DEFAULT; + cmd.speed = 100; + + for (i = 0; i < num; i++) { + cmd.payloads[i].write = !(msgs[i].flags & I2C_M_RD); + cmd.payloads[i].address = msgs[i].addr; + cmd.payloads[i].length = msgs[i].len; + cmd.payloads[i].data = msgs[i].buf; + } + + if (i2c->oem) { + if (dc_submit_i2c_oem( + ddc_service->ctx->dc, + &cmd)) + result = num; + } else { + if (dc_submit_i2c( + ddc_service->ctx->dc, + ddc_service->link->link_index, + &cmd)) + result = num; + } + + kfree(cmd.payloads); + return result; +} + +static u32 amdgpu_dm_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static const struct i2c_algorithm amdgpu_dm_i2c_algo = { + .master_xfer = amdgpu_dm_i2c_xfer, + .functionality = amdgpu_dm_i2c_func, +}; + +struct amdgpu_i2c_adapter * +amdgpu_dm_create_i2c(struct ddc_service *ddc_service, bool oem) +{ + struct amdgpu_device *adev = ddc_service->ctx->driver_context; + struct amdgpu_i2c_adapter *i2c; + + i2c = kzalloc_obj(struct amdgpu_i2c_adapter); + if (!i2c) + return NULL; + i2c->base.owner = THIS_MODULE; + i2c->base.dev.parent = &adev->pdev->dev; + i2c->base.algo = &amdgpu_dm_i2c_algo; + if (oem) + snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c OEM bus"); + else + snprintf(i2c->base.name, sizeof(i2c->base.name), "AMDGPU DM i2c hw bus %d", + ddc_service->link->link_index); + i2c_set_adapdata(&i2c->base, i2c); + i2c->ddc_service = ddc_service; + i2c->oem = oem; + + return i2c; +} + +int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector) +{ + struct cec_connector_info conn_info; + struct drm_device *ddev = aconnector->base.dev; + struct device *hdmi_dev = ddev->dev; + + if (amdgpu_dc_debug_mask & DC_DISABLE_HDMI_CEC) { + drm_info(ddev, "HDMI-CEC feature masked\n"); + return -EINVAL; + } + + cec_fill_conn_info_from_drm(&conn_info, &aconnector->base); + aconnector->notifier = + cec_notifier_conn_register(hdmi_dev, NULL, &conn_info); + if (!aconnector->notifier) { + drm_err(ddev, "Failed to create cec notifier\n"); + return -ENOMEM; + } + + return 0; +} + +/* + * Note: this function assumes that dc_link_detect() was called for the + * dc_link which will be represented by this aconnector. + */ +int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector, + u32 link_index, + struct amdgpu_encoder *aencoder) +{ + int res = 0; + int connector_type; + struct dc *dc = dm->dc; + struct dc_link *link = dc_get_link_at_index(dc, link_index); + struct amdgpu_i2c_adapter *i2c; + + /* Not needed for writeback connector */ + link->priv = aconnector; + + + i2c = amdgpu_dm_create_i2c(link->ddc, false); + if (!i2c) { + drm_err(adev_to_drm(dm->adev), "Failed to create i2c adapter data\n"); + return -ENOMEM; + } + + aconnector->i2c = i2c; + res = devm_i2c_add_adapter(dm->adev->dev, &i2c->base); + + if (res) { + drm_err(adev_to_drm(dm->adev), "Failed to register hw i2c %d\n", link->link_index); + goto out_free; + } + + connector_type = to_drm_connector_type(link->connector_signal, link->link_id.id); + + res = drm_connector_init_with_ddc( + dm->ddev, + &aconnector->base, + &amdgpu_dm_connector_funcs, + connector_type, + &i2c->base); + + if (res) { + drm_err(adev_to_drm(dm->adev), "connector_init failed\n"); + aconnector->connector_id = -1; + goto out_free; + } + + drm_connector_helper_add( + &aconnector->base, + &amdgpu_dm_connector_helper_funcs); + + amdgpu_dm_connector_init_helper( + dm, + aconnector, + connector_type, + link, + link_index); + + drm_connector_attach_encoder( + &aconnector->base, &aencoder->base); + + if (connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector_type == DRM_MODE_CONNECTOR_HDMIB) + amdgpu_dm_initialize_hdmi_connector(aconnector); + + if (dc_is_dp_signal(link->connector_signal)) + amdgpu_dm_initialize_dp_connector(dm, aconnector, link->link_index); + +out_free: + if (res) { + kfree(i2c); + aconnector->i2c = NULL; + } + return res; +} + +static int dm_force_atomic_commit(struct drm_connector *connector) +{ + int ret = 0; + struct drm_device *ddev = connector->dev; + struct drm_atomic_commit *state = drm_atomic_commit_alloc(ddev); + struct amdgpu_crtc *disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc); + struct drm_plane *plane = disconnected_acrtc->base.primary; + struct drm_connector_state *conn_state; + struct drm_crtc_state *crtc_state; + struct drm_plane_state *plane_state; + + if (!state) + return -ENOMEM; + + state->acquire_ctx = ddev->mode_config.acquire_ctx; + + /* Construct an atomic state to restore previous display setting */ + + /* + * Attach connectors to drm_atomic_commit + */ + conn_state = drm_atomic_get_connector_state(state, connector); + + /* Check for error in getting connector state */ + if (IS_ERR(conn_state)) { + ret = PTR_ERR(conn_state); + goto out; + } + + /* Attach crtc to drm_atomic_commit*/ + crtc_state = drm_atomic_get_crtc_state(state, &disconnected_acrtc->base); + + /* Check for error in getting crtc state */ + if (IS_ERR(crtc_state)) { + ret = PTR_ERR(crtc_state); + goto out; + } + + /* force a restore */ + crtc_state->mode_changed = true; + + /* Attach plane to drm_atomic_commit */ + plane_state = drm_atomic_get_plane_state(state, plane); + + /* Check for error in getting plane state */ + if (IS_ERR(plane_state)) { + ret = PTR_ERR(plane_state); + goto out; + } + + /* Call commit internally with the state we just constructed */ + ret = drm_atomic_commit(state); + +out: + drm_atomic_commit_put(state); + if (ret) + drm_err(ddev, "Restoring old state failed with %i\n", ret); + + return ret; +} + +/* + * This function handles all cases when set mode does not come upon hotplug. + * This includes when a display is unplugged then plugged back into the + * same port and when running without usermode desktop manager support + */ +void dm_restore_drm_connector_state(struct drm_device *dev, + struct drm_connector *connector) +{ + struct amdgpu_dm_connector *aconnector; + struct amdgpu_crtc *disconnected_acrtc; + struct dm_crtc_state *acrtc_state; + + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + return; + + aconnector = to_amdgpu_dm_connector(connector); + + if (!aconnector->dc_sink || !connector->state || !connector->encoder) + return; + + disconnected_acrtc = to_amdgpu_crtc(connector->encoder->crtc); + if (!disconnected_acrtc) + return; + + acrtc_state = to_dm_crtc_state(disconnected_acrtc->base.state); + if (!acrtc_state->stream) + return; + + /* + * If the previous sink is not released and different from the current, + * we deduce we are in a state where we can not rely on usermode call + * to turn on the display, so we do it here + */ + if (acrtc_state->stream->sink != aconnector->dc_sink) + dm_force_atomic_commit(&aconnector->base); +} + +static bool dm_edid_parser_send_cea(struct amdgpu_display_manager *dm, + unsigned int offset, + unsigned int total_length, + u8 *data, + unsigned int length, + struct amdgpu_hdmi_vsdb_info *vsdb) +{ + bool res; + union dmub_rb_cmd cmd; + struct dmub_cmd_send_edid_cea *input; + struct dmub_cmd_edid_cea_output *output; + + if (length > DMUB_EDID_CEA_DATA_CHUNK_BYTES) + return false; + + memset(&cmd, 0, sizeof(cmd)); + + input = &cmd.edid_cea.data.input; + + cmd.edid_cea.header.type = DMUB_CMD__EDID_CEA; + cmd.edid_cea.header.sub_type = 0; + cmd.edid_cea.header.payload_bytes = + sizeof(cmd.edid_cea) - sizeof(cmd.edid_cea.header); + input->offset = offset; + input->length = length; + input->cea_total_length = total_length; + memcpy(input->payload, data, length); + + res = dc_wake_and_execute_dmub_cmd(dm->dc->ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY); + if (!res) { + drm_err(adev_to_drm(dm->adev), "EDID CEA parser failed\n"); + return false; + } + + output = &cmd.edid_cea.data.output; + + if (output->type == DMUB_CMD__EDID_CEA_ACK) { + if (!output->ack.success) { + drm_err(adev_to_drm(dm->adev), "EDID CEA ack failed at offset %d\n", + output->ack.offset); + } + } else if (output->type == DMUB_CMD__EDID_CEA_AMD_VSDB) { + if (!output->amd_vsdb.vsdb_found) + return false; + + vsdb->freesync_supported = output->amd_vsdb.freesync_supported; + vsdb->amd_vsdb_version = output->amd_vsdb.amd_vsdb_version; + vsdb->min_refresh_rate_hz = output->amd_vsdb.min_frame_rate; + vsdb->max_refresh_rate_hz = output->amd_vsdb.max_frame_rate; + vsdb->freesync_mccs_vcp_code = output->amd_vsdb.freesync_mccs_vcp_code; + } else { + drm_warn(adev_to_drm(dm->adev), "Unknown EDID CEA parser results\n"); + return false; + } + + return true; +} + +static bool parse_edid_cea_dmcu(struct amdgpu_display_manager *dm, + u8 *edid_ext, int len, + struct amdgpu_hdmi_vsdb_info *vsdb_info) +{ + int i; + + /* send extension block to DMCU for parsing */ + for (i = 0; i < len; i += 8) { + bool res; + int offset; + + /* send 8 bytes a time */ + if (!dc_edid_parser_send_cea(dm->dc, i, len, &edid_ext[i], 8)) + return false; + + if (i+8 == len) { + /* EDID block sent completed, expect result */ + int version, min_rate, max_rate; + + res = dc_edid_parser_recv_amd_vsdb(dm->dc, &version, &min_rate, &max_rate); + if (res) { + /* amd vsdb found */ + vsdb_info->freesync_supported = 1; + vsdb_info->amd_vsdb_version = version; + vsdb_info->min_refresh_rate_hz = min_rate; + vsdb_info->max_refresh_rate_hz = max_rate; + /* Not enabled on DMCU*/ + vsdb_info->freesync_mccs_vcp_code = 0; + return true; + } + /* not amd vsdb */ + return false; + } + + /* check for ack*/ + res = dc_edid_parser_recv_cea_ack(dm->dc, &offset); + if (!res) + return false; + } + + return false; +} + +static bool parse_edid_cea_dmub(struct amdgpu_display_manager *dm, + u8 *edid_ext, int len, + struct amdgpu_hdmi_vsdb_info *vsdb_info) +{ + int i; + + /* send extension block to DMCU for parsing */ + for (i = 0; i < len; i += 8) { + /* send 8 bytes a time */ + if (!dm_edid_parser_send_cea(dm, i, len, &edid_ext[i], 8, vsdb_info)) + return false; + } + + return vsdb_info->freesync_supported; +} + +static bool parse_edid_cea(struct amdgpu_dm_connector *aconnector, + u8 *edid_ext, int len, + struct amdgpu_hdmi_vsdb_info *vsdb_info) +{ + struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev); + bool ret; + + mutex_lock(&adev->dm.dc_lock); + if (adev->dm.dmub_srv) + ret = parse_edid_cea_dmub(&adev->dm, edid_ext, len, vsdb_info); + else + ret = parse_edid_cea_dmcu(&adev->dm, edid_ext, len, vsdb_info); + mutex_unlock(&adev->dm.dc_lock); + return ret; +} + +static void parse_edid_displayid_vrr(struct drm_connector *connector, + const struct edid *edid) +{ + u8 *edid_ext = NULL; + int i; + int j = 0; + u16 min_vfreq; + u16 max_vfreq; + + if (!edid || !edid->extensions) + return; + + /* Find DisplayID extension */ + for (i = 0; i < edid->extensions; i++) { + edid_ext = (void *)(edid + (i + 1)); + if (edid_ext[0] == DISPLAYID_EXT) + break; + } + + if (i == edid->extensions) + return; + + while (j < EDID_LENGTH) { + /* Get dynamic video timing range from DisplayID if available */ + if (EDID_LENGTH - j > 13 && edid_ext[j] == 0x25 && + (edid_ext[j+1] & 0xFE) == 0 && (edid_ext[j+2] == 9)) { + min_vfreq = edid_ext[j+9]; + if (edid_ext[j+1] & 7) + max_vfreq = edid_ext[j+10] + ((edid_ext[j+11] & 3) << 8); + else + max_vfreq = edid_ext[j+10]; + + if (max_vfreq && min_vfreq) { + connector->display_info.monitor_range.max_vfreq = max_vfreq; + connector->display_info.monitor_range.min_vfreq = min_vfreq; + + return; + } + } + j++; + } +} + +static int get_amd_vsdb(struct amdgpu_dm_connector *aconnector, + struct amdgpu_hdmi_vsdb_info *vsdb_info) +{ + struct drm_connector *connector = &aconnector->base; + + vsdb_info->replay_mode = connector->display_info.amd_vsdb.replay_mode; + vsdb_info->amd_vsdb_version = connector->display_info.amd_vsdb.version; + + return connector->display_info.amd_vsdb.version != 0; +} + +static int parse_hdmi_amd_vsdb(struct amdgpu_dm_connector *aconnector, + const struct edid *edid, + struct amdgpu_hdmi_vsdb_info *vsdb_info) +{ + u8 *edid_ext = NULL; + int i; + bool valid_vsdb_found = false; + + /*----- drm_find_cea_extension() -----*/ + /* No EDID or EDID extensions */ + if (edid == NULL || edid->extensions == 0) + return -ENODEV; + + /* Find CEA extension */ + for (i = 0; i < edid->extensions; i++) { + edid_ext = (uint8_t *)edid + EDID_LENGTH * (i + 1); + if (edid_ext[0] == CEA_EXT) + break; + } + + if (i == edid->extensions) + return -ENODEV; + + /*----- cea_db_offsets() -----*/ + if (edid_ext[0] != CEA_EXT) + return -ENODEV; + + valid_vsdb_found = parse_edid_cea(aconnector, edid_ext, EDID_LENGTH, vsdb_info); + + return valid_vsdb_found ? i : -ENODEV; +} + +/** + * amdgpu_dm_update_freesync_caps - Update Freesync capabilities + * + * @connector: Connector to query. + * @drm_edid: DRM EDID from monitor + * @do_mccs: Controls whether MCCS (Monitor Control Command Set) over + * DDC (Display Data Channel) transactions are performed. When true, + * the driver queries the monitor to get or update additional FreeSync + * capability information. When false, these transactions are skipped. + * + * Amdgpu supports Freesync in DP and HDMI displays, and it is required to keep + * track of some of the display information in the internal data struct used by + * amdgpu_dm. This function checks which type of connector we need to set the + * FreeSync parameters. + */ +void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, + const struct drm_edid *drm_edid, bool do_mccs) +{ + int i = 0; + struct amdgpu_dm_connector *amdgpu_dm_connector = + to_amdgpu_dm_connector(connector); + struct dm_connector_state *dm_con_state = NULL; + struct dc_sink *sink; + struct amdgpu_device *adev = drm_to_adev(connector->dev); + struct amdgpu_hdmi_vsdb_info vsdb_info = {0}; + const struct edid *edid; + bool freesync_capable = false; + enum adaptive_sync_type as_type = ADAPTIVE_SYNC_TYPE_NONE; + + if (!connector->state) { + drm_err(adev_to_drm(adev), "%s - Connector has no state", __func__); + goto update; + } + + sink = amdgpu_dm_connector->dc_sink ? + amdgpu_dm_connector->dc_sink : + amdgpu_dm_connector->dc_em_sink; + + drm_edid_connector_update(connector, drm_edid); + + if (!drm_edid || !sink) { + dm_con_state = to_dm_connector_state(connector->state); + + amdgpu_dm_connector->min_vfreq = 0; + amdgpu_dm_connector->max_vfreq = 0; + freesync_capable = false; + + goto update; + } + + dm_con_state = to_dm_connector_state(connector->state); + + if (!adev->dm.freesync_module || !dc_supports_vrr(sink->ctx->dce_version)) + goto update; + + /* FIXME: Get rid of drm_edid_raw() */ + edid = drm_edid_raw(drm_edid); + + /* Some eDP panels only have the refresh rate range info in DisplayID */ + if ((connector->display_info.monitor_range.min_vfreq == 0 || + connector->display_info.monitor_range.max_vfreq == 0)) + parse_edid_displayid_vrr(connector, edid); + + if (edid && (sink->sink_signal == SIGNAL_TYPE_DISPLAY_PORT || + sink->sink_signal == SIGNAL_TYPE_EDP)) { + if (amdgpu_dm_connector->dc_link && + amdgpu_dm_connector->dc_link->dpcd_caps.allow_invalid_MSA_timing_param) { + amdgpu_dm_connector->min_vfreq = connector->display_info.monitor_range.min_vfreq; + amdgpu_dm_connector->max_vfreq = connector->display_info.monitor_range.max_vfreq; + if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + freesync_capable = true; + } + + get_amd_vsdb(amdgpu_dm_connector, &vsdb_info); + + if (vsdb_info.replay_mode) { + amdgpu_dm_connector->vsdb_info.replay_mode = vsdb_info.replay_mode; + amdgpu_dm_connector->vsdb_info.amd_vsdb_version = vsdb_info.amd_vsdb_version; + amdgpu_dm_connector->as_type = ADAPTIVE_SYNC_TYPE_EDP; + } + + } else if (drm_edid && sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A) { + i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); + if (i >= 0) { + amdgpu_dm_connector->vsdb_info = vsdb_info; + sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; + + if (vsdb_info.freesync_supported) { + amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; + amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; + if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + freesync_capable = true; + + connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; + connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; + } + } + } + + if (amdgpu_dm_connector->dc_link) + as_type = dm_get_adaptive_sync_support_type(amdgpu_dm_connector->dc_link); + + if (as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) { + i = parse_hdmi_amd_vsdb(amdgpu_dm_connector, edid, &vsdb_info); + if (i >= 0) { + amdgpu_dm_connector->vsdb_info = vsdb_info; + sink->edid_caps.freesync_vcp_code = vsdb_info.freesync_mccs_vcp_code; + + if (vsdb_info.freesync_supported && vsdb_info.amd_vsdb_version > 0) { + amdgpu_dm_connector->pack_sdp_v1_3 = true; + amdgpu_dm_connector->as_type = as_type; + + amdgpu_dm_connector->min_vfreq = vsdb_info.min_refresh_rate_hz; + amdgpu_dm_connector->max_vfreq = vsdb_info.max_refresh_rate_hz; + if (amdgpu_dm_connector->max_vfreq - amdgpu_dm_connector->min_vfreq > 10) + freesync_capable = true; + + connector->display_info.monitor_range.min_vfreq = vsdb_info.min_refresh_rate_hz; + connector->display_info.monitor_range.max_vfreq = vsdb_info.max_refresh_rate_hz; + } + } + } + + /* Handle MCCS */ + if (do_mccs) + dm_helpers_read_mccs_caps(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); + + if ((sink->sink_signal == SIGNAL_TYPE_HDMI_TYPE_A || + as_type == FREESYNC_TYPE_PCON_IN_WHITELIST) && + (!sink->edid_caps.freesync_vcp_code || + (sink->edid_caps.freesync_vcp_code && !sink->mccs_caps.freesync_supported))) + freesync_capable = false; + + if (do_mccs && sink->mccs_caps.freesync_supported && freesync_capable) + dm_helpers_mccs_vcp_set(adev->dm.dc->ctx, amdgpu_dm_connector->dc_link, sink); + +update: + if (dm_con_state) + dm_con_state->freesync_capable = freesync_capable; + + if (connector->state && amdgpu_dm_connector->dc_link && !freesync_capable && + amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported) { + amdgpu_dm_connector->dc_link->replay_settings.config.replay_supported = false; + amdgpu_dm_connector->dc_link->replay_settings.replay_feature_enabled = false; + } + + if (connector->vrr_capable_property) + drm_connector_set_vrr_capable_property(connector, + freesync_capable); +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h new file mode 100644 index 000000000000..c5b8b13f8f06 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_connector.h @@ -0,0 +1,162 @@ +/* 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_CONNECTOR_H__ +#define __AMDGPU_DM_CONNECTOR_H__ + +struct amdgpu_device; +struct amdgpu_dm_connector; +struct amdgpu_display_manager; +struct amdgpu_encoder; +struct amdgpu_i2c_adapter; +struct dc_crtc_timing; +struct dc_link; +enum signal_type; +struct dc_state; +struct dc_stream_state; +struct ddc_service; +struct dm_connector_state; +struct drm_atomic_commit; +struct drm_device; +struct drm_encoder_helper_funcs; +struct drm_connector; +struct drm_connector_state; +struct drm_crtc; +struct drm_device; +struct drm_display_mode; +struct drm_edid; +struct drm_property; + +void amdgpu_dm_connector_funcs_reset(struct drm_connector *connector); + +struct drm_connector_state * +amdgpu_dm_connector_atomic_duplicate_state(struct drm_connector *connector); + +int amdgpu_dm_connector_atomic_set_property(struct drm_connector *connector, + struct drm_connector_state *connector_state, + struct drm_property *property, + uint64_t val); + +int amdgpu_dm_connector_atomic_get_property(struct drm_connector *connector, + const struct drm_connector_state *state, + struct drm_property *property, + uint64_t *val); + +void amdgpu_dm_connector_init_helper(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *aconnector, + int connector_type, + struct dc_link *link, + int link_index); + +enum drm_mode_status amdgpu_dm_connector_mode_valid(struct drm_connector *connector, + const struct drm_display_mode *mode); + +void dm_restore_drm_connector_state(struct drm_device *dev, + struct drm_connector *connector); + +void amdgpu_dm_update_freesync_caps(struct drm_connector *connector, + const struct drm_edid *drm_edid, + bool do_mccs); + +void amdgpu_dm_update_connector_after_detect( + struct amdgpu_dm_connector *aconnector); + +void amdgpu_dm_hdmi_cec_set_edid(struct amdgpu_dm_connector *aconnector); +int amdgpu_dm_initialize_hdmi_connector(struct amdgpu_dm_connector *aconnector); + +struct drm_connector * +amdgpu_dm_find_first_crtc_matching_connector(struct drm_atomic_commit *state, + struct drm_crtc *crtc); + +int amdgpu_dm_convert_dc_color_depth_into_bpc(enum dc_color_depth display_color_depth); + +struct dc_stream_state * +amdgpu_dm_create_validate_stream_for_sink(struct drm_connector *connector, + const struct drm_display_mode *drm_mode, + const struct dm_connector_state *dm_state, + const struct dc_stream_state *old_stream); + +int amdgpu_dm_connector_init(struct amdgpu_display_manager *dm, + struct amdgpu_dm_connector *amdgpu_dm_connector, + u32 link_index, + struct amdgpu_encoder *amdgpu_encoder); + +void amdgpu_dm_s3_handle_hdmi_cec(struct drm_device *ddev, bool suspend); + +int amdgpu_dm_detect_mst_link_for_all_connectors(struct drm_device *dev); + +void amdgpu_set_panel_orientation(struct drm_connector *connector); + +enum dc_color_depth +amdgpu_dm_convert_color_depth_from_display_info(const struct drm_connector *connector, + bool is_y420, int requested_bpc); + +void amdgpu_dm_update_stream_scaling_settings(struct drm_device *dev, + const struct drm_display_mode *mode, + const struct dm_connector_state *dm_state, + struct dc_stream_state *stream); + +bool amdgpu_dm_is_freesync_video_mode(const struct drm_display_mode *mode, + struct amdgpu_dm_connector *aconnector); + +int amdgpu_dm_fill_hdr_info_packet(const struct drm_connector_state *state, + struct dc_info_packet *out); + +enum dc_color_space +amdgpu_dm_get_output_color_space(const struct dc_crtc_timing *dc_crtc_timing, + const struct drm_connector_state *connector_state); + +struct drm_display_mode * +amdgpu_dm_get_highest_refresh_rate_mode(struct amdgpu_dm_connector *aconnector, + bool use_probed_modes); + +struct amdgpu_i2c_adapter * +amdgpu_dm_create_i2c(struct ddc_service *ddc_service, bool oem); + +#define DDC_MANUFACTURERNAME_SAMSUNG 0x2D4C + +/* Encoder functions */ +extern const struct drm_encoder_helper_funcs amdgpu_dm_encoder_helper_funcs; +int amdgpu_dm_get_encoder_crtc_mask(struct amdgpu_device *adev); +int amdgpu_dm_encoder_init(struct drm_device *dev, + struct amdgpu_encoder *aencoder, + uint32_t link_index); + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +enum drm_mode_subconnector get_subconnector_type(struct dc_link *link); +enum display_content_type +get_output_content_type(const struct drm_connector_state *connector_state); +bool adjust_colour_depth_from_display_info(struct dc_crtc_timing *timing_out, + const struct drm_display_info *info); + +int to_drm_connector_type(enum signal_type st, uint32_t connector_id); +bool is_duplicate_mode(struct amdgpu_dm_connector *aconnector, struct drm_display_mode *mode); +enum dc_aspect_ratio get_aspect_ratio(const struct drm_display_mode *mode_in); +void decide_crtc_timing_for_drm_display_mode(struct drm_display_mode *drm_mode, + const struct drm_display_mode *native_mode, + bool scale_enabled); +#endif +#endif /* __AMDGPU_DM_CONNECTOR_H__ */ 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 88f7cfea5624..970490c401e9 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 @@ -87,6 +87,65 @@ bool dm_need_crc_dither(enum amdgpu_dm_pipe_crc_source src) } EXPORT_IF_KUNIT(dm_need_crc_dither); +/** + * dm_need_dp_aux() - Does this source transition require the DP AUX handle? + * @source: Requested CRC source. + * @cur_crc_src: Current CRC source. + * + * Returns true when either the new source is DPRX-based (starting DPRX CRC), + * or the current source is DPRX-based and the new source is NONE (stopping it). + * + * Return: true if the DP AUX handle is needed, false otherwise. + */ +STATIC_IFN_KUNIT +bool dm_need_dp_aux(enum amdgpu_dm_pipe_crc_source source, + enum amdgpu_dm_pipe_crc_source cur_crc_src) +{ + return dm_is_crc_source_dprx(source) || + (source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE && dm_is_crc_source_dprx(cur_crc_src)); +} +EXPORT_IF_KUNIT(dm_need_dp_aux); + +/** + * dm_crc_source_should_start_dprx() - Should drm_dp_start_crc() be called? + * @source: Requested CRC source. + * @cur_crc_src: Current CRC source. + * + * True when CRC is transitioning from off to a DPRX source + * (!enabled && enable && is_dprx(@source)). + * + * Return: true if drm_dp_start_crc() should be called, false otherwise. + */ +STATIC_IFN_KUNIT +bool dm_crc_source_should_start_dprx(enum amdgpu_dm_pipe_crc_source source, + enum amdgpu_dm_pipe_crc_source cur_crc_src) +{ + return !amdgpu_dm_is_valid_crc_source(cur_crc_src) && + amdgpu_dm_is_valid_crc_source(source) && + dm_is_crc_source_dprx(source); +} +EXPORT_IF_KUNIT(dm_crc_source_should_start_dprx); + +/** + * dm_crc_source_should_stop_dprx() - Should drm_dp_stop_crc() be called? + * @source: Requested CRC source. + * @cur_crc_src: Current CRC source. + * + * True when CRC is transitioning from a DPRX source to off + * (enabled && !enable && is_dprx(@cur_crc_src)). + * + * Return: true if drm_dp_stop_crc() should be called, false otherwise. + */ +STATIC_IFN_KUNIT +bool dm_crc_source_should_stop_dprx(enum amdgpu_dm_pipe_crc_source source, + enum amdgpu_dm_pipe_crc_source cur_crc_src) +{ + return amdgpu_dm_is_valid_crc_source(cur_crc_src) && + !amdgpu_dm_is_valid_crc_source(source) && + dm_is_crc_source_dprx(cur_crc_src); +} +EXPORT_IF_KUNIT(dm_crc_source_should_stop_dprx); + const char *const *amdgpu_dm_crtc_get_crc_sources(struct drm_crtc *crtc, size_t *count) { @@ -496,7 +555,7 @@ amdgpu_dm_crtc_verify_crc_source(struct drm_crtc *crtc, const char *src_name, { enum amdgpu_dm_pipe_crc_source source = dm_parse_crc_source(src_name); - if (source < 0) { + if (source == AMDGPU_DM_PIPE_CRC_SOURCE_INVALID) { DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n", src_name, crtc->index); return -EINVAL; @@ -595,7 +654,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) bool enabled = false; int ret = 0; - if (source < 0) { + if (source == AMDGPU_DM_PIPE_CRC_SOURCE_INVALID) { DRM_DEBUG_DRIVER("Unknown CRC source %s for CRTC%d\n", src_name, crtc->index); return -EINVAL; @@ -622,7 +681,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) */ ret = wait_for_completion_interruptible_timeout( &commit->hw_done, 10 * HZ); - if (ret < 0) + if (ret < 0 && ret != -ERESTARTSYS) goto cleanup; if (ret == 0) { @@ -650,9 +709,7 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) * CRTC DITHER | XXXX | Enable CRTC CRC, set dither * DPRX DITHER | XXXX | Enable DPRX CRC, need 'aux', set dither */ - if (dm_is_crc_source_dprx(source) || - (source == AMDGPU_DM_PIPE_CRC_SOURCE_NONE && - dm_is_crc_source_dprx(cur_crc_src))) { + if (dm_need_dp_aux(source, cur_crc_src)) { struct amdgpu_dm_connector *aconn = NULL; struct drm_connector *connector; struct drm_connector_list_iter conn_iter; @@ -714,23 +771,24 @@ int amdgpu_dm_crtc_set_crc_source(struct drm_crtc *crtc, const char *src_name) goto cleanup; } - if (!enabled && enable) { - if (dm_is_crc_source_dprx(source)) { - if (drm_dp_start_crc(aux, crtc)) { - DRM_DEBUG_DRIVER("dp start crc failed\n"); - ret = -EINVAL; - goto cleanup; - } + if (dm_crc_source_should_start_dprx(source, cur_crc_src)) { + /* !enabled && enable && is_dprx(source): CRC off → DPRX on */ + if (drm_dp_start_crc(aux, crtc)) { + DRM_DEBUG_DRIVER("dp start crc failed\n"); + ret = -EINVAL; + goto cleanup; } - } else if (enabled && !enable) { + } else if (dm_crc_source_should_stop_dprx(source, cur_crc_src)) { + /* enabled && !enable && is_dprx(cur_crc_src): DPRX on → CRC off */ drm_crtc_vblank_put(crtc); - if (dm_is_crc_source_dprx(source)) { - if (drm_dp_stop_crc(aux)) { - DRM_DEBUG_DRIVER("dp stop crc failed\n"); - ret = -EINVAL; - goto cleanup; - } + if (drm_dp_stop_crc(aux)) { + DRM_DEBUG_DRIVER("dp stop crc failed\n"); + ret = -EINVAL; + goto cleanup; } + } else if (enabled && !enable) { + /* Non-DPRX source (e.g. CRTC) turning off: release vblank ref */ + drm_crtc_vblank_put(crtc); } spin_lock_irq(&drm_dev->event_lock); @@ -767,9 +825,9 @@ void amdgpu_dm_crtc_handle_crc_irq(struct drm_crtc *crtc) { struct dm_crtc_state *crtc_state; struct dc_stream_state *stream_state; - struct drm_device *drm_dev = NULL; + struct drm_device *drm_dev; enum amdgpu_dm_pipe_crc_source cur_crc_src; - struct amdgpu_crtc *acrtc = NULL; + struct amdgpu_crtc *acrtc; uint32_t crcs[3]; unsigned long flags; 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 c9aa0c82038f..8bb8a6f6c148 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 @@ -156,6 +156,12 @@ 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); +bool dm_need_dp_aux(enum amdgpu_dm_pipe_crc_source source, + enum amdgpu_dm_pipe_crc_source cur_crc_src); +bool dm_crc_source_should_start_dprx(enum amdgpu_dm_pipe_crc_source source, + enum amdgpu_dm_pipe_crc_source cur_crc_src); +bool dm_crc_source_should_stop_dprx(enum amdgpu_dm_pipe_crc_source source, + enum amdgpu_dm_pipe_crc_source cur_crc_src); #endif #endif /* AMD_DAL_DEV_AMDGPU_DM_AMDGPU_DM_CRC_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c index 3dcedaa67ed8..0ad7704800d9 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.c @@ -34,6 +34,7 @@ #include "amdgpu_dm_plane.h" #include "amdgpu_dm_trace.h" #include "amdgpu_dm_debugfs.h" +#include "amdgpu_dm_kunit_helpers.h" #include "modules/inc/mod_power.h" #define HPD_DETECTION_PERIOD_uS 2000000 @@ -65,6 +66,7 @@ bool amdgpu_dm_crtc_modeset_required(struct drm_crtc_state *crtc_state, { return crtc_state->active && drm_atomic_crtc_needs_modeset(crtc_state); } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_modeset_required); bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc) @@ -74,6 +76,7 @@ bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc) acrtc->dm_irq_params.freesync_config.state == VRR_STATE_ACTIVE_FIXED; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_vrr_active_irq); int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable) { @@ -93,12 +96,14 @@ int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable) acrtc->crtc_id, enable ? "en" : "dis", rc); return rc; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_set_vupdate_irq); bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state) { return dm_state->freesync_config.state == VRR_STATE_ACTIVE_VARIABLE || dm_state->freesync_config.state == VRR_STATE_ACTIVE_FIXED; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_vrr_active); /** * amdgpu_dm_crtc_set_static_screen_optimze() - Toggle static screen optimizations. @@ -156,6 +161,7 @@ bool amdgpu_dm_is_headless(struct amdgpu_device *adev) drm_connector_list_iter_end(&iter); return is_headless; } +EXPORT_IF_KUNIT(amdgpu_dm_is_headless); static void amdgpu_dm_idle_worker(struct work_struct *work) { @@ -207,6 +213,7 @@ struct idle_workqueue *idle_create_workqueue(struct amdgpu_device *adev) return idle_work; } +EXPORT_IF_KUNIT(idle_create_workqueue); static void amdgpu_dm_crtc_vblank_control_worker(struct work_struct *work) { @@ -437,13 +444,13 @@ static void amdgpu_dm_crtc_reset_state(struct drm_crtc *crtc) { struct dm_crtc_state *state; - if (crtc->state) - amdgpu_dm_crtc_destroy_state(crtc, crtc->state); - state = kzalloc_obj(*state); - if (WARN_ON(!state)) + if (!state) return; + if (crtc->state) + amdgpu_dm_crtc_destroy_state(crtc, crtc->state); + __drm_atomic_helper_crtc_reset(crtc, &state->base); } @@ -595,12 +602,13 @@ static void amdgpu_dm_crtc_update_crtc_active_planes(struct drm_crtc *crtc, amdgpu_dm_crtc_count_crtc_active_planes(new_crtc_state); } -static bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +STATIC_IFN_KUNIT bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { return true; } +EXPORT_IF_KUNIT(amdgpu_dm_crtc_helper_mode_fixup); static int amdgpu_dm_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_commit *state) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h index e9fb52f0e66d..d8b004f613ab 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_crtc.h @@ -42,6 +42,12 @@ int amdgpu_dm_crtc_set_vupdate_irq(struct drm_crtc *crtc, bool enable); bool amdgpu_dm_crtc_vrr_active_irq(struct amdgpu_crtc *acrtc); +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +bool amdgpu_dm_crtc_helper_mode_fixup(struct drm_crtc *crtc, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode); +#endif + bool amdgpu_dm_crtc_vrr_active(const struct dm_crtc_state *dm_state); int amdgpu_dm_crtc_enable_vblank(struct drm_crtc *crtc); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index 7db38ad3f848..830cf8da06b4 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -606,7 +606,7 @@ static int dp_lttpr_status_show(struct seq_file *m, void *unused) break; } - seq_puts(m, "\n"); + seq_putc(m, '\n'); return 0; } @@ -1081,7 +1081,7 @@ static int psr_capability_show(struct seq_file *m, void *data) seq_printf(m, "Driver support: %s", str_yes_no(link->psr_settings.psr_feature_enabled)); if (link->psr_settings.psr_version) seq_printf(m, " [0x%02x]", link->psr_settings.psr_version); - seq_puts(m, "\n"); + seq_putc(m, '\n'); return 0; } @@ -1266,7 +1266,7 @@ static int hdcp_sink_capability_show(struct seq_file *m, void *data) if (!hdcp_cap && !hdcp2_cap) seq_printf(m, "%s ", "None"); - seq_puts(m, "\n"); + seq_putc(m, '\n'); return 0; } @@ -2710,11 +2710,10 @@ static int ips_status_show(struct seq_file *m, void *unused) rcg_count = ips_fw->rcg_exit_count; ips1_count = ips_fw->ips1_exit_count; ips2_count = ips_fw->ips2_exit_count; - seq_printf(m, "exit counts: rcg=%u ips1=%u ips2=%u", + seq_printf(m, "exit counts: rcg=%u ips1=%u ips2=%u\n", rcg_count, ips1_count, ips2_count); - seq_puts(m, "\n"); } return 0; } @@ -2971,7 +2970,7 @@ static ssize_t hdmi_cec_state_write(struct file *f, const char __user *buf, ret = amdgpu_dm_initialize_hdmi_connector(aconnector); if (ret) return ret; - hdmi_cec_set_edid(aconnector); + amdgpu_dm_hdmi_cec_set_edid(aconnector); } else { if (!aconnector->notifier) return -EINVAL; @@ -2982,6 +2981,64 @@ static ssize_t hdmi_cec_state_write(struct file *f, const char __user *buf, return size; } +/** + * hdmi_automation_enable - Enable/Disable HDMI automation feature + * @f: file structure. + * @buf: userspace buffer. set to '1' to enable; '0' to disable automation feature. + * @size: size of buffer from userpsace. + * @pos: unused. + * + * Return size on success, error code on failure + */ +static ssize_t hdmi_automation_enable(struct file *f, const char __user *buf, + size_t size, loff_t *pos) +{ + struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; + char *wr_buf = NULL; + const uint32_t wr_buf_size = 40; + int max_param_num = 1; + uint8_t param_nums = 0; + long param[2]; + bool hdmi_comp_auto; + + if (size == 0) + return -EINVAL; + + wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL); + if (!wr_buf) + return -ENOSPC; + + if (parse_write_buffer_into_params(wr_buf, wr_buf_size, + (long *)param, buf, + max_param_num, + ¶m_nums)) { + kfree(wr_buf); + return -EINVAL; + } + + if (param_nums <= 0) { + kfree(wr_buf); + DRM_DEBUG_DRIVER("user data not be read\n"); + return -EINVAL; + } + + switch (param[0]) { + case 0: + hdmi_comp_auto = false; + break; + case 1: + default: + hdmi_comp_auto = true; + break; + } + + /* Persist setting across sink re-detection/hotplug. */ + aconnector->hdmi_comp_auto = hdmi_comp_auto; + + kfree(wr_buf); + return size; +} + DEFINE_SHOW_ATTRIBUTE(dp_dsc_fec_support); DEFINE_SHOW_ATTRIBUTE(dmub_fw_state); DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer); @@ -3099,6 +3156,12 @@ static const struct file_operations dp_mst_link_settings_debugfs_fops = { .llseek = default_llseek }; +static const struct file_operations hdmi_automation_debugfs_fops = { + .owner = THIS_MODULE, + .write = hdmi_automation_enable, + .llseek = default_llseek +}; + static const struct { char *name; const struct file_operations *fops; @@ -3131,7 +3194,8 @@ static const struct { const struct file_operations *fops; } hdmi_debugfs_entries[] = { {"hdcp_sink_capability", &hdcp_sink_capability_fops}, - {"hdmi_cec_state", &hdmi_cec_state_fops} + {"hdmi_cec_state", &hdmi_cec_state_fops}, + {"hdmi_automation", &hdmi_automation_debugfs_fops} }; /* diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c new file mode 100644 index 000000000000..0aa99d1a542f --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.c @@ -0,0 +1,943 @@ +// 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 + * + */ + +#include "dm_services_types.h" +#include "dc.h" +#include "dc/inc/core_types.h" +#include "dc/dc_dmub_srv.h" +#include "dmub/dmub_srv.h" +#include "dc/inc/hw/dmcu.h" +#include "dc/inc/hw/abm.h" +#include "dal_asic_id.h" + +#include "amdgpu.h" +#include "amdgpu_display.h" +#include "amdgpu_ucode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_dmub.h" +#include "amdgpu_dm_kunit_helpers.h" +#include <linux/component.h> +#include <linux/firmware.h> + +static_assert(AMDGPU_DMUB_NOTIFICATION_MAX == DMUB_NOTIFICATION_MAX, "AMDGPU_DMUB_NOTIFICATION_MAX mismatch"); + +MODULE_FIRMWARE(FIRMWARE_RENOIR_DMUB); +MODULE_FIRMWARE(FIRMWARE_SIENNA_CICHLID_DMUB); +MODULE_FIRMWARE(FIRMWARE_NAVY_FLOUNDER_DMUB); +MODULE_FIRMWARE(FIRMWARE_GREEN_SARDINE_DMUB); +MODULE_FIRMWARE(FIRMWARE_VANGOGH_DMUB); +MODULE_FIRMWARE(FIRMWARE_DIMGREY_CAVEFISH_DMUB); +MODULE_FIRMWARE(FIRMWARE_BEIGE_GOBY_DMUB); +MODULE_FIRMWARE(FIRMWARE_YELLOW_CARP_DMUB); +MODULE_FIRMWARE(FIRMWARE_DCN_314_DMUB); +MODULE_FIRMWARE(FIRMWARE_DCN_315_DMUB); +MODULE_FIRMWARE(FIRMWARE_DCN316_DMUB); +MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_0_DMCUB); +MODULE_FIRMWARE(FIRMWARE_DCN_V3_2_1_DMCUB); +MODULE_FIRMWARE(FIRMWARE_DCN_35_DMUB); +MODULE_FIRMWARE(FIRMWARE_DCN_351_DMUB); +MODULE_FIRMWARE(FIRMWARE_DCN_36_DMUB); +MODULE_FIRMWARE(FIRMWARE_DCN_401_DMUB); +MODULE_FIRMWARE(FIRMWARE_DCN_42_DMUB); +MODULE_FIRMWARE(FIRMWARE_DCN_42B_DMUB); + +/** + * dm_dmub_aux_setconfig_callback - Callback for AUX or SET_CONFIG command. + * @adev: amdgpu_device pointer + * @notify: dmub notification structure + * + * Dmub AUX or SET_CONFIG command completion processing callback + * Copies dmub notification to DM which is to be read by AUX command. + * issuing thread and also signals the event to wake up the thread. + */ +void dm_dmub_aux_setconfig_callback(struct amdgpu_device *adev, + struct dmub_notification *notify) +{ + if (adev->dm.dmub_notify) + memcpy(adev->dm.dmub_notify, notify, sizeof(struct dmub_notification)); + if (notify->type == DMUB_NOTIFICATION_AUX_REPLY) + complete(&adev->dm.dmub_aux_transfer_done); +} +EXPORT_IF_KUNIT(dm_dmub_aux_setconfig_callback); + +void dm_dmub_aux_fused_io_callback(struct amdgpu_device *adev, + struct dmub_notification *notify) +{ + if (!adev || !notify) { + ASSERT(false); + return; + } + + const struct dmub_cmd_fused_request *req = ¬ify->fused_request; + const uint8_t ddc_line = req->u.aux.ddc_line; + + if (ddc_line >= ARRAY_SIZE(adev->dm.fused_io)) { + ASSERT(false); + return; + } + + struct fused_io_sync *sync = &adev->dm.fused_io[ddc_line]; + + static_assert(sizeof(*req) <= sizeof(sync->reply_data), "Size mismatch"); + memcpy(sync->reply_data, req, sizeof(*req)); + complete(&sync->replied); +} +EXPORT_IF_KUNIT(dm_dmub_aux_fused_io_callback); + +/** + * dm_register_dmub_notify_callback - Sets callback for DMUB notify + * @adev: amdgpu_device pointer + * @type: Type of dmub notification + * @callback: Dmub interrupt callback function + * @dmub_int_thread_offload: offload indicator + * + * API to register a dmub callback handler for a dmub notification + * Also sets indicator whether callback processing to be offloaded. + * to dmub interrupt handling thread + * Return: true if successfully registered, false if there is existing registration + */ +bool dm_register_dmub_notify_callback(struct amdgpu_device *adev, + enum dmub_notification_type type, + dmub_notify_interrupt_callback_t callback, + bool dmub_int_thread_offload) +{ + if (!callback || type >= ARRAY_SIZE(adev->dm.dmub_thread_offload)) + return false; + + adev->dm.dmub_callback[type] = callback; + adev->dm.dmub_thread_offload[type] = dmub_int_thread_offload; + + return true; +} +EXPORT_IF_KUNIT(dm_register_dmub_notify_callback); + +int dm_dmub_hw_init(struct amdgpu_device *adev) +{ + const struct dmcub_firmware_header_v1_0 *hdr; + struct dmub_srv *dmub_srv = adev->dm.dmub_srv; + struct dmub_srv_fb_info *fb_info = adev->dm.dmub_fb_info; + const struct firmware *dmub_fw = adev->dm.dmub_fw; + struct dc *dc = adev->dm.dc; + struct dmcu *dmcu = adev->dm.dc->res_pool->dmcu; + struct abm *abm = adev->dm.dc->res_pool->abm; + struct dc_context *ctx = adev->dm.dc->ctx; + struct dmub_srv_hw_params hw_params; + enum dmub_status status; + const unsigned char *fw_inst_const, *fw_bss_data; + u32 i, fw_inst_const_size, fw_bss_data_size; + bool has_hw_support; + + if (!dmub_srv) + /* DMUB isn't supported on the ASIC. */ + return 0; + + if (!fb_info) { + drm_err(adev_to_drm(adev), "No framebuffer info for DMUB service.\n"); + return -EINVAL; + } + + if (!dmub_fw) { + /* Firmware required for DMUB support. */ + drm_err(adev_to_drm(adev), "No firmware provided for DMUB.\n"); + return -EINVAL; + } + + /* initialize register offsets for ASICs with runtime initialization available */ + if (dmub_srv->hw_funcs.init_reg_offsets) + dmub_srv->hw_funcs.init_reg_offsets(dmub_srv, ctx); + + status = dmub_srv_has_hw_support(dmub_srv, &has_hw_support); + if (status != DMUB_STATUS_OK) { + drm_err(adev_to_drm(adev), "Error checking HW support for DMUB: %d\n", status); + return -EINVAL; + } + + if (!has_hw_support) { + drm_info(adev_to_drm(adev), "DMUB unsupported on ASIC\n"); + return 0; + } + + /* Reset DMCUB if it was previously running - before we overwrite its memory. */ + status = dmub_srv_hw_reset(dmub_srv); + if (status != DMUB_STATUS_OK) + drm_warn(adev_to_drm(adev), "Error resetting DMUB HW: %d\n", status); + + hdr = (const struct dmcub_firmware_header_v1_0 *)dmub_fw->data; + + fw_inst_const = dmub_fw->data + + le32_to_cpu(hdr->header.ucode_array_offset_bytes) + + PSP_HEADER_BYTES_256; + + fw_bss_data = dmub_fw->data + + le32_to_cpu(hdr->header.ucode_array_offset_bytes) + + le32_to_cpu(hdr->inst_const_bytes); + + /* Copy firmware and bios info into FB memory. */ + fw_inst_const_size = adev->dm.fw_inst_size; + + fw_bss_data_size = le32_to_cpu(hdr->bss_data_bytes); + + /* if adev->firmware.load_type == AMDGPU_FW_LOAD_PSP, + * amdgpu_ucode_init_single_fw will load dmub firmware + * fw_inst_const part to cw0; otherwise, the firmware back door load + * will be done by dm_dmub_hw_init + */ + if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) { + memcpy(fb_info->fb[DMUB_WINDOW_0_INST_CONST].cpu_addr, fw_inst_const, + fw_inst_const_size); + } + + if (fw_bss_data_size) + memcpy(fb_info->fb[DMUB_WINDOW_2_BSS_DATA].cpu_addr, + fw_bss_data, fw_bss_data_size); + + /* Copy firmware bios info into FB memory. */ + memcpy(fb_info->fb[DMUB_WINDOW_3_VBIOS].cpu_addr, adev->bios, + adev->bios_size); + + /* Reset regions that need to be reset. */ + memset(fb_info->fb[DMUB_WINDOW_4_MAILBOX].cpu_addr, 0, + fb_info->fb[DMUB_WINDOW_4_MAILBOX].size); + + memset(fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].cpu_addr, 0, + fb_info->fb[DMUB_WINDOW_5_TRACEBUFF].size); + + memset(fb_info->fb[DMUB_WINDOW_6_FW_STATE].cpu_addr, 0, + fb_info->fb[DMUB_WINDOW_6_FW_STATE].size); + + memset(fb_info->fb[DMUB_WINDOW_SHARED_STATE].cpu_addr, 0, + fb_info->fb[DMUB_WINDOW_SHARED_STATE].size); + + /* Initialize hardware. */ + memset(&hw_params, 0, sizeof(hw_params)); + hw_params.soc_fb_info.fb_base = adev->gmc.fb_start; + hw_params.soc_fb_info.fb_offset = adev->vm_manager.vram_base_offset; + + /* backdoor load firmware and trigger dmub running */ + if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) + hw_params.load_inst_const = true; + + if (dmcu) + hw_params.psp_version = dmcu->psp_version; + + for (i = 0; i < fb_info->num_fb; ++i) + hw_params.fb[i] = &fb_info->fb[i]; + + /* Enable usb4 dpia in the FW APU */ + if (dc->caps.is_apu && + dc->res_pool->usb4_dpia_count != 0 && + !dc->debug.dpia_debug.bits.disable_dpia) { + hw_params.dpia_supported = true; + hw_params.disable_dpia = dc->debug.dpia_debug.bits.disable_dpia; + hw_params.dpia_hpd_int_enable_supported = false; + hw_params.enable_non_transparent_setconfig = dc->config.consolidated_dpia_dp_lt; + hw_params.disable_dpia_bw_allocation = !dc->config.usb4_bw_alloc_support; + } + + switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { + case IP_VERSION(3, 5, 0): + case IP_VERSION(3, 5, 1): + case IP_VERSION(3, 6, 0): + case IP_VERSION(4, 2, 0): + case IP_VERSION(4, 2, 1): + hw_params.ips_sequential_ono = adev->external_rev_id > 0x10; + hw_params.lower_hbr3_phy_ssc = true; + break; + default: + break; + } + + status = dmub_srv_hw_init(dmub_srv, &hw_params); + if (status != DMUB_STATUS_OK) { + drm_err(adev_to_drm(adev), "Error initializing DMUB HW: %d\n", status); + return -EINVAL; + } + + /* Wait for firmware load to finish. */ + status = dmub_srv_wait_for_auto_load(dmub_srv, 100000); + if (status != DMUB_STATUS_OK) + drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status); + + /* Init DMCU and ABM if available. */ + if (dmcu && abm) { + dmcu->funcs->dmcu_init(dmcu); + abm->dmcu_is_running = dmcu->funcs->is_dmcu_initialized(dmcu); + } + + if (!adev->dm.dc->ctx->dmub_srv) + adev->dm.dc->ctx->dmub_srv = dc_dmub_srv_create(adev->dm.dc, dmub_srv); + if (!adev->dm.dc->ctx->dmub_srv) { + drm_err(adev_to_drm(adev), "Couldn't allocate DC DMUB server!\n"); + return -ENOMEM; + } + + drm_info(adev_to_drm(adev), "DMUB hardware initialized: version=0x%08X\n", + adev->dm.dmcub_fw_version); + + /* Keeping sanity checks off if + * DCN31 >= 4.0.59.0 + * DCN314 >= 8.0.16.0 + * Otherwise, turn on sanity checks + */ + switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { + case IP_VERSION(3, 1, 2): + case IP_VERSION(3, 1, 3): + if (adev->dm.dmcub_fw_version && + adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) && + adev->dm.dmcub_fw_version < DMUB_FW_VERSION(4, 0, 59)) + adev->dm.dc->debug.sanity_checks = true; + break; + case IP_VERSION(3, 1, 4): + if (adev->dm.dmcub_fw_version && + adev->dm.dmcub_fw_version >= DMUB_FW_VERSION(4, 0, 0) && + adev->dm.dmcub_fw_version < DMUB_FW_VERSION(8, 0, 16)) + adev->dm.dc->debug.sanity_checks = true; + break; + default: + break; + } + + return 0; +} +EXPORT_IF_KUNIT(dm_dmub_hw_init); + +void dm_dmub_hw_resume(struct amdgpu_device *adev) +{ + struct dmub_srv *dmub_srv = adev->dm.dmub_srv; + enum dmub_status status; + bool init; + int r; + + if (!dmub_srv) { + /* DMUB isn't supported on the ASIC. */ + return; + } + + status = dmub_srv_is_hw_init(dmub_srv, &init); + if (status != DMUB_STATUS_OK) + drm_warn(adev_to_drm(adev), "DMUB hardware init check failed: %d\n", status); + + if (status == DMUB_STATUS_OK && init) { + /* Wait for firmware load to finish. */ + status = dmub_srv_wait_for_auto_load(dmub_srv, 100000); + if (status != DMUB_STATUS_OK) + drm_warn(adev_to_drm(adev), "Wait for DMUB auto-load failed: %d\n", status); + } else { + /* Perform the full hardware initialization. */ + r = dm_dmub_hw_init(adev); + if (r) + drm_err(adev_to_drm(adev), "DMUB interface failed to initialize: status=%d\n", r); + } +} +EXPORT_IF_KUNIT(dm_dmub_hw_resume); + +static enum dmub_status +dm_dmub_send_vbios_gpint_command(struct amdgpu_device *adev, + enum dmub_gpint_command command_code, + uint16_t param, + uint32_t timeout_us) +{ + union dmub_gpint_data_register reg, test; + uint32_t i; + + /* Assume that VBIOS DMUB is ready to take commands */ + + reg.bits.status = 1; + reg.bits.command_code = command_code; + reg.bits.param = param; + + cgs_write_register(adev->dm.cgs_device, 0x34c0 + 0x01f8, reg.all); + + for (i = 0; i < timeout_us; ++i) { + udelay(1); + + /* Check if our GPINT got acked */ + reg.bits.status = 0; + test = (union dmub_gpint_data_register) + cgs_read_register(adev->dm.cgs_device, 0x34c0 + 0x01f8); + + if (test.all == reg.all) + return DMUB_STATUS_OK; + } + + return DMUB_STATUS_TIMEOUT; +} + +static void *dm_dmub_get_vbios_bounding_box(struct amdgpu_device *adev) +{ + void *bb; + long long addr; + unsigned int bb_size; + int i = 0; + uint16_t chunk; + enum dmub_gpint_command send_addrs[] = { + DMUB_GPINT__SET_BB_ADDR_WORD0, + DMUB_GPINT__SET_BB_ADDR_WORD1, + DMUB_GPINT__SET_BB_ADDR_WORD2, + DMUB_GPINT__SET_BB_ADDR_WORD3, + }; + enum dmub_status ret; + + switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { + case IP_VERSION(4, 0, 1): + bb_size = sizeof(struct dml2_soc_bb); + break; + case IP_VERSION(4, 2, 0): + case IP_VERSION(4, 2, 1): + bb_size = sizeof(struct dml2_soc_bb); + break; + default: + return NULL; + } + + bb = dm_allocate_gpu_mem(adev, + DC_MEM_ALLOC_TYPE_GART, + bb_size, + &addr); + if (!bb) + return NULL; + + for (i = 0; i < 4; i++) { + /* Extract 16-bit chunk */ + chunk = ((uint64_t) addr >> (i * 16)) & 0xFFFF; + /* Send the chunk */ + ret = dm_dmub_send_vbios_gpint_command(adev, send_addrs[i], chunk, 30000); + if (ret != DMUB_STATUS_OK) + goto free_bb; + } + + /* Now ask DMUB to copy the bb */ + ret = dm_dmub_send_vbios_gpint_command(adev, DMUB_GPINT__BB_COPY, 1, 200000); + if (ret != DMUB_STATUS_OK) + goto free_bb; + + return bb; + +free_bb: + dm_free_gpu_mem(adev, DC_MEM_ALLOC_TYPE_GART, (void *) bb); + return NULL; + +} + +enum dmub_ips_disable_type dm_get_default_ips_mode( + struct amdgpu_device *adev) +{ + enum dmub_ips_disable_type ret = DMUB_IPS_ENABLE; + + switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { + case IP_VERSION(3, 5, 0): + case IP_VERSION(3, 6, 0): + case IP_VERSION(3, 5, 1): + ret = DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF; + break; + default: + /* ASICs older than DCN35 do not have IPSs */ + if (amdgpu_ip_version(adev, DCE_HWIP, 0) < IP_VERSION(3, 5, 0)) + ret = DMUB_IPS_DISABLE_ALL; + break; + } + + return ret; +} +EXPORT_IF_KUNIT(dm_get_default_ips_mode); + +static uint32_t amdgpu_dm_dmub_reg_read(void *ctx, uint32_t address) +{ + struct amdgpu_device *adev = ctx; + + return dm_read_reg(adev->dm.dc->ctx, address); +} + +static void amdgpu_dm_dmub_reg_write(void *ctx, uint32_t address, + uint32_t value) +{ + struct amdgpu_device *adev = ctx; + + return dm_write_reg(adev->dm.dc->ctx, address, value); +} + +int dm_dmub_sw_init(struct amdgpu_device *adev) +{ + struct dmub_srv_create_params create_params; + struct dmub_srv_fw_meta_info_params fw_meta_info_params; + struct dmub_srv_region_params region_params; + struct dmub_srv_region_info region_info; + struct dmub_srv_memory_params memory_params; + struct dmub_fw_meta_info fw_info; + struct dmub_srv_fb_info *fb_info; + struct dmub_srv *dmub_srv; + const struct dmcub_firmware_header_v1_0 *hdr; + enum dmub_asic dmub_asic; + enum dmub_status status; + static enum dmub_window_memory_type window_memory_type[DMUB_WINDOW_TOTAL] = { + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_0_INST_CONST */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_1_STACK */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_2_BSS_DATA */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_3_VBIOS */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_4_MAILBOX */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_5_TRACEBUFF */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_6_FW_STATE */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_7_SCRATCH_MEM */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_IB_MEM */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_SHARED_STATE */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_LSDMA_BUFFER */ + DMUB_WINDOW_MEMORY_TYPE_FB, /* DMUB_WINDOW_CURSOR_OFFLOAD */ + }; + int r; + + switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { + case IP_VERSION(2, 1, 0): + dmub_asic = DMUB_ASIC_DCN21; + break; + case IP_VERSION(3, 0, 0): + dmub_asic = DMUB_ASIC_DCN30; + break; + case IP_VERSION(3, 0, 1): + dmub_asic = DMUB_ASIC_DCN301; + break; + case IP_VERSION(3, 0, 2): + dmub_asic = DMUB_ASIC_DCN302; + break; + case IP_VERSION(3, 0, 3): + dmub_asic = DMUB_ASIC_DCN303; + break; + case IP_VERSION(3, 1, 2): + case IP_VERSION(3, 1, 3): + dmub_asic = (adev->external_rev_id == YELLOW_CARP_B0) ? DMUB_ASIC_DCN31B : DMUB_ASIC_DCN31; + break; + case IP_VERSION(3, 1, 4): + dmub_asic = DMUB_ASIC_DCN314; + break; + case IP_VERSION(3, 1, 5): + dmub_asic = DMUB_ASIC_DCN315; + break; + case IP_VERSION(3, 1, 6): + dmub_asic = DMUB_ASIC_DCN316; + break; + case IP_VERSION(3, 2, 0): + dmub_asic = DMUB_ASIC_DCN32; + break; + case IP_VERSION(3, 2, 1): + dmub_asic = DMUB_ASIC_DCN321; + break; + case IP_VERSION(3, 5, 0): + case IP_VERSION(3, 5, 1): + dmub_asic = DMUB_ASIC_DCN35; + break; + case IP_VERSION(3, 6, 0): + dmub_asic = DMUB_ASIC_DCN36; + break; + case IP_VERSION(4, 0, 1): + dmub_asic = DMUB_ASIC_DCN401; + break; + case IP_VERSION(4, 2, 0): + dmub_asic = DMUB_ASIC_DCN42; + break; + case IP_VERSION(4, 2, 1): + dmub_asic = DMUB_ASIC_DCN42B; + break; + default: + /* ASIC doesn't support DMUB. */ + return 0; + } + + hdr = (const struct dmcub_firmware_header_v1_0 *)adev->dm.dmub_fw->data; + adev->dm.dmcub_fw_version = le32_to_cpu(hdr->header.ucode_version); + + if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { + adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].ucode_id = + AMDGPU_UCODE_ID_DMCUB; + adev->firmware.ucode[AMDGPU_UCODE_ID_DMCUB].fw = + adev->dm.dmub_fw; + adev->firmware.fw_size += + ALIGN(le32_to_cpu(hdr->inst_const_bytes), PAGE_SIZE); + + drm_info(adev_to_drm(adev), "Loading DMUB firmware via PSP: version=0x%08X\n", + adev->dm.dmcub_fw_version); + } + + + adev->dm.dmub_srv = kzalloc_obj(*adev->dm.dmub_srv); + dmub_srv = adev->dm.dmub_srv; + + if (!dmub_srv) { + drm_err(adev_to_drm(adev), "Failed to allocate DMUB service!\n"); + return -ENOMEM; + } + + memset(&create_params, 0, sizeof(create_params)); + create_params.user_ctx = adev; + create_params.funcs.reg_read = amdgpu_dm_dmub_reg_read; + create_params.funcs.reg_write = amdgpu_dm_dmub_reg_write; + create_params.asic = dmub_asic; + + /* Create the DMUB service. */ + status = dmub_srv_create(dmub_srv, &create_params); + if (status != DMUB_STATUS_OK) { + drm_err(adev_to_drm(adev), "Error creating DMUB service: %d\n", status); + return -EINVAL; + } + + /* Extract the FW meta info. */ + memset(&fw_meta_info_params, 0, sizeof(fw_meta_info_params)); + + fw_meta_info_params.inst_const_size = le32_to_cpu(hdr->inst_const_bytes) - + PSP_HEADER_BYTES_256; + fw_meta_info_params.bss_data_size = le32_to_cpu(hdr->bss_data_bytes); + fw_meta_info_params.fw_inst_const = adev->dm.dmub_fw->data + + le32_to_cpu(hdr->header.ucode_array_offset_bytes) + + PSP_HEADER_BYTES_256; + fw_meta_info_params.fw_bss_data = fw_meta_info_params.bss_data_size ? adev->dm.dmub_fw->data + + le32_to_cpu(hdr->header.ucode_array_offset_bytes) + + le32_to_cpu(hdr->inst_const_bytes) : NULL; + fw_meta_info_params.custom_psp_footer_size = 0; + + status = dmub_srv_get_fw_meta_info_from_raw_fw(&fw_meta_info_params, &fw_info); + if (status != DMUB_STATUS_OK) { + /* Skip returning early, just log the error. */ + drm_err(adev_to_drm(adev), "Error getting DMUB FW meta info: %d\n", status); + } + + /* Calculate the size of all the regions for the DMUB service. */ + memset(®ion_params, 0, sizeof(region_params)); + + region_params.inst_const_size = fw_meta_info_params.inst_const_size; + region_params.bss_data_size = fw_meta_info_params.bss_data_size; + region_params.vbios_size = adev->bios_size; + region_params.fw_bss_data = fw_meta_info_params.fw_bss_data; + region_params.fw_inst_const = fw_meta_info_params.fw_inst_const; + region_params.window_memory_type = window_memory_type; + region_params.fw_info = (status == DMUB_STATUS_OK) ? &fw_info : NULL; + + status = dmub_srv_calc_region_info(dmub_srv, ®ion_params, + ®ion_info); + + if (status != DMUB_STATUS_OK) { + drm_err(adev_to_drm(adev), "Error calculating DMUB region info: %d\n", status); + return -EINVAL; + } + + /* + * Allocate a framebuffer based on the total size of all the regions. + * TODO: Move this into GART. + */ + r = amdgpu_bo_create_kernel(adev, region_info.fb_size, PAGE_SIZE, + AMDGPU_GEM_DOMAIN_VRAM | + AMDGPU_GEM_DOMAIN_GTT, + &adev->dm.dmub_bo, + &adev->dm.dmub_bo_gpu_addr, + &adev->dm.dmub_bo_cpu_addr); + if (r) + return r; + + /* Rebase the regions on the framebuffer address. */ + memset(&memory_params, 0, sizeof(memory_params)); + memory_params.cpu_fb_addr = adev->dm.dmub_bo_cpu_addr; + memory_params.gpu_fb_addr = adev->dm.dmub_bo_gpu_addr; + memory_params.region_info = ®ion_info; + memory_params.window_memory_type = window_memory_type; + + adev->dm.dmub_fb_info = kzalloc_obj(*adev->dm.dmub_fb_info); + fb_info = adev->dm.dmub_fb_info; + + if (!fb_info) { + drm_err(adev_to_drm(adev), + "Failed to allocate framebuffer info for DMUB service!\n"); + return -ENOMEM; + } + + status = dmub_srv_calc_mem_info(dmub_srv, &memory_params, fb_info); + if (status != DMUB_STATUS_OK) { + drm_err(adev_to_drm(adev), "Error calculating DMUB FB info: %d\n", status); + return -EINVAL; + } + + adev->dm.bb_from_dmub = dm_dmub_get_vbios_bounding_box(adev); + adev->dm.fw_inst_size = fw_meta_info_params.inst_const_size; + + return 0; +} +EXPORT_IF_KUNIT(dm_dmub_sw_init); + +int dm_init_microcode(struct amdgpu_device *adev) +{ + char *fw_name_dmub; + int r; + + switch (amdgpu_ip_version(adev, DCE_HWIP, 0)) { + case IP_VERSION(2, 1, 0): + fw_name_dmub = FIRMWARE_RENOIR_DMUB; + if (ASICREV_IS_GREEN_SARDINE(adev->external_rev_id)) + fw_name_dmub = FIRMWARE_GREEN_SARDINE_DMUB; + break; + case IP_VERSION(3, 0, 0): + if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(10, 3, 0)) + fw_name_dmub = FIRMWARE_SIENNA_CICHLID_DMUB; + else + fw_name_dmub = FIRMWARE_NAVY_FLOUNDER_DMUB; + break; + case IP_VERSION(3, 0, 1): + fw_name_dmub = FIRMWARE_VANGOGH_DMUB; + break; + case IP_VERSION(3, 0, 2): + fw_name_dmub = FIRMWARE_DIMGREY_CAVEFISH_DMUB; + break; + case IP_VERSION(3, 0, 3): + fw_name_dmub = FIRMWARE_BEIGE_GOBY_DMUB; + break; + case IP_VERSION(3, 1, 2): + case IP_VERSION(3, 1, 3): + fw_name_dmub = FIRMWARE_YELLOW_CARP_DMUB; + break; + case IP_VERSION(3, 1, 4): + fw_name_dmub = FIRMWARE_DCN_314_DMUB; + break; + case IP_VERSION(3, 1, 5): + fw_name_dmub = FIRMWARE_DCN_315_DMUB; + break; + case IP_VERSION(3, 1, 6): + fw_name_dmub = FIRMWARE_DCN316_DMUB; + break; + case IP_VERSION(3, 2, 0): + fw_name_dmub = FIRMWARE_DCN_V3_2_0_DMCUB; + break; + case IP_VERSION(3, 2, 1): + fw_name_dmub = FIRMWARE_DCN_V3_2_1_DMCUB; + break; + case IP_VERSION(3, 5, 0): + fw_name_dmub = FIRMWARE_DCN_35_DMUB; + break; + case IP_VERSION(3, 5, 1): + fw_name_dmub = FIRMWARE_DCN_351_DMUB; + break; + case IP_VERSION(3, 6, 0): + fw_name_dmub = FIRMWARE_DCN_36_DMUB; + break; + case IP_VERSION(4, 0, 1): + fw_name_dmub = FIRMWARE_DCN_401_DMUB; + break; + case IP_VERSION(4, 2, 0): + fw_name_dmub = FIRMWARE_DCN_42_DMUB; + break; + case IP_VERSION(4, 2, 1): + fw_name_dmub = FIRMWARE_DCN_42B_DMUB; + break; + default: + /* ASIC doesn't support DMUB. */ + return 0; + } + r = amdgpu_ucode_request(adev, &adev->dm.dmub_fw, AMDGPU_UCODE_REQUIRED, + "%s", fw_name_dmub); + return r; +} +EXPORT_IF_KUNIT(dm_init_microcode); + +int amdgpu_dm_process_dmub_aux_transfer_sync( + struct dc_context *ctx, + unsigned int link_index, + struct aux_payload *payload, + enum aux_return_code_type *operation_result) +{ + struct amdgpu_device *adev = ctx->driver_context; + struct dmub_notification *p_notify = adev->dm.dmub_notify; + int ret = -1; + + mutex_lock(&adev->dm.dpia_aux_lock); + if (!dc_process_dmub_aux_transfer_async(ctx->dc, link_index, payload)) { + *operation_result = AUX_RET_ERROR_ENGINE_ACQUIRE; + goto out; + } + + if (!wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) { + drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!"); + *operation_result = AUX_RET_ERROR_TIMEOUT; + goto out; + } + + if (p_notify->result != AUX_RET_SUCCESS) { + /* + * Transient states before tunneling is enabled could + * lead to this error. We can ignore this for now. + */ + if (p_notify->result == AUX_RET_ERROR_PROTOCOL_ERROR) { + drm_warn(adev_to_drm(adev), "DPIA AUX failed on 0x%x(%d), error %d\n", + payload->address, payload->length, + p_notify->result); + } + *operation_result = p_notify->result; + goto out; + } + + payload->reply[0] = adev->dm.dmub_notify->aux_reply.command & 0xF; + if (adev->dm.dmub_notify->aux_reply.command & 0xF0) + /* The reply is stored in the top nibble of the command. */ + payload->reply[0] = (adev->dm.dmub_notify->aux_reply.command >> 4) & 0xF; + + /*write req may receive a byte indicating partially written number as well*/ + if (p_notify->aux_reply.length && payload->data) { + /* Bound the reply to the scratch buffer it was read into. */ + ret = min((uint32_t)p_notify->aux_reply.length, + (uint32_t)sizeof(p_notify->aux_reply.data)); + + /* + * During a write-status-update retry the caller zeroes + * payload->length while still expecting the partial-write + * status byte in payload->data (see dce_aux_transfer_with_retries), + * so only clamp to payload->length for regular transfers. + */ + if (!payload->write_status_update) + ret = min(ret, payload->length); + + memcpy(payload->data, p_notify->aux_reply.data, ret); + } else { + /* success */ + ret = p_notify->aux_reply.length; + } + + *operation_result = p_notify->result; +out: + reinit_completion(&adev->dm.dmub_aux_transfer_done); + mutex_unlock(&adev->dm.dpia_aux_lock); + return ret; +} + +static void abort_fused_io( + struct dc_context *ctx, + const struct dmub_cmd_fused_request *request +) +{ + union dmub_rb_cmd command = { 0 }; + struct dmub_rb_cmd_fused_io *io = &command.fused_io; + + io->header.type = DMUB_CMD__FUSED_IO; + io->header.sub_type = DMUB_CMD__FUSED_IO_ABORT; + io->header.payload_bytes = sizeof(*io) - sizeof(io->header); + io->request = *request; + dm_execute_dmub_cmd(ctx, &command, DM_DMUB_WAIT_TYPE_NO_WAIT); +} + +static bool execute_fused_io( + struct amdgpu_device *dev, + struct dc_context *ctx, + union dmub_rb_cmd *commands, + uint8_t count, + uint32_t timeout_us +) +{ + const uint8_t ddc_line = commands[0].fused_io.request.u.aux.ddc_line; + + if (ddc_line >= ARRAY_SIZE(dev->dm.fused_io)) + return false; + + struct fused_io_sync *sync = &dev->dm.fused_io[ddc_line]; + struct dmub_rb_cmd_fused_io *first = &commands[0].fused_io; + const bool result = dm_execute_dmub_cmd_list(ctx, count, commands, DM_DMUB_WAIT_TYPE_WAIT_WITH_REPLY) + && first->header.ret_status + && first->request.status == FUSED_REQUEST_STATUS_SUCCESS; + + if (!result) + return false; + + while (wait_for_completion_timeout(&sync->replied, usecs_to_jiffies(timeout_us))) { + reinit_completion(&sync->replied); + + struct dmub_cmd_fused_request *reply = (struct dmub_cmd_fused_request *) sync->reply_data; + + static_assert(sizeof(*reply) <= sizeof(sync->reply_data), "Size mismatch"); + + if (reply->identifier == first->request.identifier) { + first->request = *reply; + return true; + } + } + + reinit_completion(&sync->replied); + first->request.status = FUSED_REQUEST_STATUS_TIMEOUT; + abort_fused_io(ctx, &first->request); + return false; +} + +bool amdgpu_dm_execute_fused_io( + struct amdgpu_device *dev, + struct dc_link *link, + union dmub_rb_cmd *commands, + uint8_t count, + uint32_t timeout_us) +{ + struct amdgpu_display_manager *dm = &dev->dm; + + mutex_lock(&dm->dpia_aux_lock); + + const bool result = execute_fused_io(dev, link->ctx, commands, count, timeout_us); + + mutex_unlock(&dm->dpia_aux_lock); + return result; +} + +int amdgpu_dm_process_dmub_set_config_sync( + struct dc_context *ctx, + unsigned int link_index, + struct set_config_cmd_payload *payload, + enum set_config_status *operation_result) +{ + struct amdgpu_device *adev = ctx->driver_context; + bool is_cmd_complete; + int ret; + + mutex_lock(&adev->dm.dpia_aux_lock); + is_cmd_complete = dc_process_dmub_set_config_async(ctx->dc, + link_index, payload, adev->dm.dmub_notify); + + if (is_cmd_complete || wait_for_completion_timeout(&adev->dm.dmub_aux_transfer_done, 10 * HZ)) { + ret = 0; + *operation_result = adev->dm.dmub_notify->sc_status; + } else { + drm_err(adev_to_drm(adev), "wait_for_completion_timeout timeout!"); + ret = -1; + *operation_result = SET_CONFIG_UNKNOWN_ERROR; + } + + if (!is_cmd_complete) + reinit_completion(&adev->dm.dmub_aux_transfer_done); + mutex_unlock(&adev->dm.dpia_aux_lock); + return ret; +} + +bool dm_execute_dmub_cmd(const struct dc_context *ctx, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type) +{ + struct amdgpu_device *adev = ctx->driver_context; + + guard(spinlock_irqsave)(&adev->dm.dmub_lock); + return dc_dmub_srv_cmd_run(ctx->dmub_srv, cmd, wait_type); +} + +bool dm_execute_dmub_cmd_list(const struct dc_context *ctx, unsigned int count, union dmub_rb_cmd *cmd, enum dm_dmub_wait_type wait_type) +{ + struct amdgpu_device *adev = ctx->driver_context; + + guard(spinlock_irqsave)(&adev->dm.dmub_lock); + return dc_dmub_srv_cmd_run_list(ctx->dmub_srv, count, cmd, wait_type); +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h new file mode 100644 index 000000000000..a4a03e40ec37 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_dmub.h @@ -0,0 +1,68 @@ +/* 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_AMDGPU_DM_DMUB_H_ +#define AMDGPU_DM_AMDGPU_DM_DMUB_H_ + +#include "amdgpu.h" + +void dm_dmub_aux_setconfig_callback(struct amdgpu_device *adev, + struct dmub_notification *notify); +void dm_dmub_aux_fused_io_callback(struct amdgpu_device *adev, + struct dmub_notification *notify); +bool dm_register_dmub_notify_callback(struct amdgpu_device *adev, + enum dmub_notification_type type, + dmub_notify_interrupt_callback_t callback, + bool dmub_int_thread_offload); +int dm_dmub_hw_init(struct amdgpu_device *adev); +void dm_dmub_hw_resume(struct amdgpu_device *adev); +enum dmub_ips_disable_type dm_get_default_ips_mode(struct amdgpu_device *adev); +int dm_dmub_sw_init(struct amdgpu_device *adev); +int dm_init_microcode(struct amdgpu_device *adev); + +#define FIRMWARE_RENOIR_DMUB "amdgpu/renoir_dmcub.bin" +#define FIRMWARE_SIENNA_CICHLID_DMUB "amdgpu/sienna_cichlid_dmcub.bin" +#define FIRMWARE_NAVY_FLOUNDER_DMUB "amdgpu/navy_flounder_dmcub.bin" +#define FIRMWARE_GREEN_SARDINE_DMUB "amdgpu/green_sardine_dmcub.bin" +#define FIRMWARE_VANGOGH_DMUB "amdgpu/vangogh_dmcub.bin" +#define FIRMWARE_DIMGREY_CAVEFISH_DMUB "amdgpu/dimgrey_cavefish_dmcub.bin" +#define FIRMWARE_BEIGE_GOBY_DMUB "amdgpu/beige_goby_dmcub.bin" +#define FIRMWARE_YELLOW_CARP_DMUB "amdgpu/yellow_carp_dmcub.bin" +#define FIRMWARE_DCN_314_DMUB "amdgpu/dcn_3_1_4_dmcub.bin" +#define FIRMWARE_DCN_315_DMUB "amdgpu/dcn_3_1_5_dmcub.bin" +#define FIRMWARE_DCN316_DMUB "amdgpu/dcn_3_1_6_dmcub.bin" +#define FIRMWARE_DCN_V3_2_0_DMCUB "amdgpu/dcn_3_2_0_dmcub.bin" +#define FIRMWARE_DCN_V3_2_1_DMCUB "amdgpu/dcn_3_2_1_dmcub.bin" +#define FIRMWARE_DCN_35_DMUB "amdgpu/dcn_3_5_dmcub.bin" +#define FIRMWARE_DCN_351_DMUB "amdgpu/dcn_3_5_1_dmcub.bin" +#define FIRMWARE_DCN_36_DMUB "amdgpu/dcn_3_6_dmcub.bin" +#define FIRMWARE_DCN_401_DMUB "amdgpu/dcn_4_0_1_dmcub.bin" +#define FIRMWARE_DCN_42_DMUB "amdgpu/dcn_4_2_dmcub.bin" +#define FIRMWARE_DCN_42B_DMUB "amdgpu/dcn_4_2_1_dmcub.bin" +#define FIRMWARE_RAVEN_DMCU "amdgpu/raven_dmcu.bin" +#define FIRMWARE_NAVI12_DMCU "amdgpu/navi12_dmcu.bin" + +#endif /* AMDGPU_DM_AMDGPU_DM_DMUB_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 4c164ae4a4f9..5dbeb1e017d4 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 @@ -182,6 +182,70 @@ void process_output(struct hdcp_workqueue *hdcp_work) } EXPORT_IF_KUNIT(process_output); +STATIC_IFN_KUNIT +bool hdcp_get_content_protection_from_status( + unsigned int hdcp_content_type, + enum mod_hdcp_encryption_status encryption_status, + unsigned int *content_protection) +{ + if (encryption_status == MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) { + *content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED; + return true; + } + + if (hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE0 && + encryption_status <= MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) { + *content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; + return true; + } + + if (hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE1 && + encryption_status == MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) { + *content_protection = DRM_MODE_CONTENT_PROTECTION_ENABLED; + return true; + } + + return false; +} +EXPORT_IF_KUNIT(hdcp_get_content_protection_from_status); + +STATIC_IFN_KUNIT +void hdcp_get_link_display_adjustments( + bool enable_encryption, + u8 content_type, + bool fused_io_supported, + bool hdcp_lc_force_fw_enable, + bool hdcp_lc_enable_sw_fallback, + struct mod_hdcp_link_adjustment *link_adjust, + struct mod_hdcp_display_adjustment *display_adjust) +{ + memset(link_adjust, 0, sizeof(*link_adjust)); + memset(display_adjust, 0, sizeof(*display_adjust)); + + if (!enable_encryption) { + display_adjust->disable = + MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; + return; + } + + display_adjust->disable = MOD_HDCP_DISPLAY_NOT_DISABLE; + link_adjust->auth_delay = 2; + link_adjust->retry_limit = MAX_NUM_OF_ATTEMPTS; + + if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) { + link_adjust->hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; + } else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) { + link_adjust->hdcp1.disable = 1; + link_adjust->hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1; + } + + link_adjust->hdcp2.use_fw_locality_check = + fused_io_supported || hdcp_lc_force_fw_enable; + link_adjust->hdcp2.use_sw_locality_fallback = + hdcp_lc_enable_sw_fallback; +} +EXPORT_IF_KUNIT(hdcp_get_link_display_adjustments); + static void link_lock(struct hdcp_workqueue *work, bool lock) { int i = 0; @@ -212,8 +276,11 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, drm_connector_put(&hdcp_w->aconnector[conn_index]->base); hdcp_w->aconnector[conn_index] = aconnector; - memset(&link_adjust, 0, sizeof(link_adjust)); - memset(&display_adjust, 0, sizeof(display_adjust)); + hdcp_get_link_display_adjustments(enable_encryption, content_type, + dc->caps.fused_io_supported, + dc->debug.hdcp_lc_force_fw_enable, + dc->debug.hdcp_lc_enable_sw_fallback, + &link_adjust, &display_adjust); if (enable_encryption) { /* Explicitly set the saved SRM as sysfs call will be after we already enabled hdcp @@ -224,25 +291,9 @@ void hdcp_update_display(struct hdcp_workqueue *hdcp_work, hdcp_work->srm_size, &hdcp_work->srm_version); - display_adjust.disable = MOD_HDCP_DISPLAY_NOT_DISABLE; - - link_adjust.auth_delay = 2; - link_adjust.retry_limit = MAX_NUM_OF_ATTEMPTS; - - if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) { - link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_0; - } else if (content_type == DRM_MODE_HDCP_CONTENT_TYPE1) { - link_adjust.hdcp1.disable = 1; - link_adjust.hdcp2.force_type = MOD_HDCP_FORCE_TYPE_1; - } - link_adjust.hdcp2.use_fw_locality_check = - (dc->caps.fused_io_supported || dc->debug.hdcp_lc_force_fw_enable); - link_adjust.hdcp2.use_sw_locality_fallback = dc->debug.hdcp_lc_enable_sw_fallback; - schedule_delayed_work(&hdcp_w->property_validate_dwork, msecs_to_jiffies(DRM_HDCP_CHECK_PERIOD_MS)); } else { - display_adjust.disable = MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION; hdcp_w->encryption_status[conn_index] = MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; cancel_delayed_work(&hdcp_w->property_validate_dwork); } @@ -336,6 +387,7 @@ static void event_property_update(struct work_struct *work) property_update_work); struct amdgpu_dm_connector *aconnector = NULL; struct drm_device *dev; + unsigned int content_protection; long ret; unsigned int conn_index; struct drm_connector *connector; @@ -375,26 +427,15 @@ static void event_property_update(struct work_struct *work) MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF; } } - if (hdcp_work->encryption_status[conn_index] != - MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF) { - if (conn_state->hdcp_content_type == - DRM_MODE_HDCP_CONTENT_TYPE0 && - hdcp_work->encryption_status[conn_index] <= - MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON) { + if (hdcp_get_content_protection_from_status(conn_state->hdcp_content_type, + hdcp_work->encryption_status[conn_index], + &content_protection)) { + if (content_protection == DRM_MODE_CONTENT_PROTECTION_ENABLED) DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_ENABLED\n"); - drm_hdcp_update_content_protection(connector, - DRM_MODE_CONTENT_PROTECTION_ENABLED); - } else if (conn_state->hdcp_content_type == - DRM_MODE_HDCP_CONTENT_TYPE1 && - hdcp_work->encryption_status[conn_index] == - MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON) { - drm_hdcp_update_content_protection(connector, - DRM_MODE_CONTENT_PROTECTION_ENABLED); - } - } else { - DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n"); - drm_hdcp_update_content_protection(connector, - DRM_MODE_CONTENT_PROTECTION_DESIRED); + else + DRM_DEBUG_DRIVER("[HDCP_DM] DRM_MODE_CONTENT_PROTECTION_DESIRED\n"); + + drm_hdcp_update_content_protection(connector, content_protection); } drm_modeset_unlock(&dev->mode_config.connection_mutex); } 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 90b18c450ca6..3ba5823aed9f 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 @@ -96,6 +96,18 @@ struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) void process_output(struct hdcp_workqueue *hdcp_work); +bool hdcp_get_content_protection_from_status( + unsigned int hdcp_content_type, + enum mod_hdcp_encryption_status encryption_status, + unsigned int *content_protection); +void hdcp_get_link_display_adjustments( + bool enable_encryption, + u8 content_type, + bool fused_io_supported, + bool hdcp_lc_force_fw_enable, + bool hdcp_lc_enable_sw_fallback, + struct mod_hdcp_link_adjustment *link_adjust, + struct mod_hdcp_display_adjustment *display_adjust); #endif #endif /* AMDGPU_DM_AMDGPU_DM_HDCP_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index c6f94eb71ffa..71e2627f9a9d 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -48,6 +48,8 @@ #include "dm_helpers.h" #include "ddc_service_types.h" #include "clk_mgr.h" +#include "amdgpu_dm_kunit_helpers.h" +#include "amdgpu_dm_helpers.h" #define MCCS_DEST_ADDR (0x6E >> 1) #define MCCS_SRC_ADDR 0x51 @@ -88,12 +90,13 @@ union vcp_reply { unsigned char raw[11]; }; -static u32 edid_extract_panel_id(struct edid *edid) +STATIC_IFN_KUNIT u32 edid_extract_panel_id(struct edid *edid) { return (u32)edid->mfg_id[0] << 24 | (u32)edid->mfg_id[1] << 16 | (u32)EDID_PRODUCT_ID(edid); } +EXPORT_IF_KUNIT(edid_extract_panel_id); static void apply_edid_quirks(struct dc_link *link, struct edid *edid, struct dc_edid_caps *edid_caps) @@ -193,6 +196,12 @@ enum dc_edid_status dm_helpers_parse_edid_caps( __func__, connector->name, edid_caps->frl_dsc_10bpc, edid_caps->frl_dsc_12bpc, \ edid_caps->frl_dsc_all_bpp, edid_caps->frl_dsc_native_420, edid_caps->frl_dsc_max_slices, \ edid_caps->frl_dsc_max_frl_rate, edid_caps->frl_dsc_total_chunk_kbytes); + if (aconnector->hdmi_comp_auto) { + edid_caps->panel_patch.hdmi_comp_auto = true; + link->ctx->dc->debug.force_frl_max = true; + link->ctx->dc->debug.force_frl_dsc = true; + drm_dbg_driver(connector->dev, "%s: HDMI_FRL [%s] hdmi_comp_auto --> enabled\n", __func__, connector->name); + } } apply_edid_quirks(link, edid_buf, edid_caps); @@ -489,6 +498,7 @@ void dm_dtn_log_begin(struct dc_context *ctx, dm_dtn_log_append_v(ctx, log_ctx, "%s", msg); } +EXPORT_IF_KUNIT(dm_dtn_log_begin); __printf(3, 4) void dm_dtn_log_append_v(struct dc_context *ctx, @@ -551,6 +561,7 @@ void dm_dtn_log_append_v(struct dc_context *ctx, if (n > 0) log_ctx->pos += n; } +EXPORT_IF_KUNIT(dm_dtn_log_append_v); void dm_dtn_log_end(struct dc_context *ctx, struct dc_log_buffer_ctx *log_ctx) @@ -564,6 +575,7 @@ void dm_dtn_log_end(struct dc_context *ctx, dm_dtn_log_append_v(ctx, log_ctx, "%s", msg); } +EXPORT_IF_KUNIT(dm_dtn_log_end); bool dm_helpers_dp_mst_start_top_mgr( struct dc_context *ctx, @@ -598,6 +610,7 @@ bool dm_helpers_dp_mst_start_top_mgr( return true; } +EXPORT_IF_KUNIT(dm_helpers_dp_mst_start_top_mgr); bool dm_helpers_dp_mst_stop_top_mgr( struct dc_context *ctx, @@ -620,6 +633,7 @@ bool dm_helpers_dp_mst_stop_top_mgr( return false; } +EXPORT_IF_KUNIT(dm_helpers_dp_mst_stop_top_mgr); bool dm_helpers_dp_read_dpcd( struct dc_context *ctx, @@ -637,6 +651,7 @@ bool dm_helpers_dp_read_dpcd( return drm_dp_dpcd_read(&aconnector->dm_dp_aux.aux, address, data, size) == size; } +EXPORT_IF_KUNIT(dm_helpers_dp_read_dpcd); bool dm_helpers_dp_write_dpcd( struct dc_context *ctx, @@ -653,6 +668,7 @@ bool dm_helpers_dp_write_dpcd( return drm_dp_dpcd_write(&aconnector->dm_dp_aux.aux, address, (uint8_t *)data, size) > 0; } +EXPORT_IF_KUNIT(dm_helpers_dp_write_dpcd); bool dm_helpers_submit_i2c( struct dc_context *ctx, @@ -968,6 +984,7 @@ bool dm_helpers_dp_write_hblank_reduction(struct dc_context *ctx, const struct d // TODO return false; } +EXPORT_IF_KUNIT(dm_helpers_dp_write_hblank_reduction); bool dm_helpers_is_dp_sink_present(struct dc_link *link) { @@ -1085,7 +1102,7 @@ dm_helpers_read_vbios_hardcoded_edid(struct dc_link *link, struct amdgpu_dm_conn return edid; } -static uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane) +STATIC_IFN_KUNIT uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane) { uint8_t max_frl_rate; @@ -1106,6 +1123,7 @@ static uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane) return max_frl_rate; } +EXPORT_IF_KUNIT(get_max_frl_rate); static uint8_t get_dsc_max_slices(uint8_t max_slices, int clk_per_slice) { @@ -1150,6 +1168,7 @@ void populate_hdmi_info_from_connector(bool enable_frl, struct drm_hdmi_info *hd } } } +EXPORT_IF_KUNIT(populate_hdmi_info_from_connector); enum dc_edid_status dm_helpers_read_local_edid( struct dc_context *ctx, @@ -1550,24 +1569,32 @@ void dm_helpers_dp_mst_update_branch_bandwidth( // TODO } -static bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id) +STATIC_IFN_KUNIT const uint32_t dm_freesync_pcon_whitelist[] = { + DP_BRANCH_DEVICE_ID_0060AD, + DP_BRANCH_DEVICE_ID_00E04C, + DP_BRANCH_DEVICE_ID_90CC24, + DP_BRANCH_DEVICE_ID_001CF8, + DP_BRANCH_DEVICE_ID_001FF2, +}; +EXPORT_IF_KUNIT(dm_freesync_pcon_whitelist); + +STATIC_IFN_KUNIT uint32_t dm_freesync_pcon_whitelist_count(void) { - bool ret_val = false; - - switch (branch_dev_id) { - case DP_BRANCH_DEVICE_ID_0060AD: - case DP_BRANCH_DEVICE_ID_00E04C: - case DP_BRANCH_DEVICE_ID_90CC24: - case DP_BRANCH_DEVICE_ID_001CF8: - case DP_BRANCH_DEVICE_ID_001FF2: - ret_val = true; - break; - default: - break; - } + return ARRAY_SIZE(dm_freesync_pcon_whitelist); +} +EXPORT_IF_KUNIT(dm_freesync_pcon_whitelist_count); + +STATIC_IFN_KUNIT bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id) +{ + u32 i; + + for (i = 0; i < dm_freesync_pcon_whitelist_count(); i++) + if (dm_freesync_pcon_whitelist[i] == branch_dev_id) + return true; - return ret_val; + return false; } +EXPORT_IF_KUNIT(dm_is_freesync_pcon_whitelist); enum adaptive_sync_type dm_get_adaptive_sync_support_type(struct dc_link *link) { @@ -1587,18 +1614,21 @@ enum adaptive_sync_type dm_get_adaptive_sync_support_type(struct dc_link *link) return as_type; } +EXPORT_IF_KUNIT(dm_get_adaptive_sync_support_type); bool dm_helpers_is_fullscreen(struct dc_context *ctx, struct dc_stream_state *stream) { // TODO return false; } +EXPORT_IF_KUNIT(dm_helpers_is_fullscreen); bool dm_helpers_is_hdr_on(struct dc_context *ctx, struct dc_stream_state *stream) { // TODO return false; } +EXPORT_IF_KUNIT(dm_helpers_is_hdr_on); static int mccs_operation_vcp_request(unsigned int vcp_code, struct dc_link *link, union vcp_reply *reply) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h new file mode 100644 index 000000000000..2ac9762895ec --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#ifndef __AMDGPU_DM_HELPERS_H__ +#define __AMDGPU_DM_HELPERS_H__ + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +#include <drm/drm_edid.h> + +/* Exported for KUnit testing */ +u32 edid_extract_panel_id(struct edid *edid); +uint8_t get_max_frl_rate(uint8_t max_lanes, uint8_t max_rate_per_lane); +bool dm_is_freesync_pcon_whitelist(const uint32_t branch_dev_id); +extern const uint32_t dm_freesync_pcon_whitelist[]; +uint32_t dm_freesync_pcon_whitelist_count(void); +#endif /* CONFIG_DRM_AMD_DC_KUNIT_TEST */ + +#endif /* __AMDGPU_DM_HELPERS_H__ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c index e49803a90eda..551901c7598a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c @@ -26,10 +26,25 @@ #include "dm_services_types.h" #include "dc.h" +#include "dc/dc_dmub_srv.h" +#include "dc/dc_stat.h" #include "amdgpu.h" +#include "amdgpu_display.h" #include "amdgpu_dm.h" #include "amdgpu_dm_irq.h" +#include "amdgpu_dm_kunit_helpers.h" +#include "amdgpu_dm_crtc.h" +#include "amdgpu_dm_hdcp.h" +#include "amdgpu_dm_mst_types.h" +#include "amdgpu_dm_dmub.h" +#include "amdgpu_dm_trace.h" +#include "link/protocols/link_dpcd.h" +#include "link_service_types.h" +#include "ivsrcid/ivsrcid_vislands30.h" +#include "ivsrcid/dcn/irqsrcs_dcn_1_0.h" +#include "modules/inc/mod_freesync.h" +#include <drm/drm_vblank.h> /** * DOC: overview @@ -55,7 +70,8 @@ * are all set to the DM generic handler amdgpu_dm_irq_handler(), which looks up * DM's IRQ tables. However, in order for base driver to recognize this hook, DM * still needs to register the IRQ with the base driver. See - * dce110_register_irq_handlers() and dcn10_register_irq_handlers(). + * amdgpu_dm_dce110_register_irq_handlers() and + * amdgpu_dm_dcn10_register_irq_handlers(). * * To expose DC's hardware interrupt toggle to the base driver, DM implements * &amdgpu_irq_src_funcs.set hooks. Base driver calls it through @@ -172,7 +188,7 @@ static struct list_head *remove_irq_handler(struct amdgpu_device *adev, DM_IRQ_TABLE_UNLOCK(adev, irq_table_flags); - if (handler_removed == false) { + if (!handler_removed) { /* Not necessarily an error - caller may not * know the context. */ @@ -310,7 +326,7 @@ void *amdgpu_dm_irq_register_interrupt(struct amdgpu_device *adev, unsigned long irq_table_flags; enum dc_irq_source irq_source; - if (false == validate_irq_registration_params(int_params, ih)) + if (!validate_irq_registration_params(int_params, ih)) return DAL_INVALID_IRQ_HANDLER_IDX; handler_data = kzalloc_obj(*handler_data); @@ -357,6 +373,7 @@ void *amdgpu_dm_irq_register_interrupt(struct amdgpu_device *adev, return handler_data; } +EXPORT_IF_KUNIT(amdgpu_dm_irq_register_interrupt); /** * amdgpu_dm_irq_unregister_interrupt() - Remove a handler from the DM IRQ table @@ -375,7 +392,7 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev, struct dc_interrupt_params int_params; int i; - if (false == validate_irq_unregistration_params(irq_source, ih)) + if (!validate_irq_unregistration_params(irq_source, ih)) return; memset(&int_params, 0, sizeof(int_params)); @@ -401,6 +418,7 @@ void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device *adev, ih, irq_source); } } +EXPORT_IF_KUNIT(amdgpu_dm_irq_unregister_interrupt); /** * amdgpu_dm_irq_init() - Initialize DM IRQ management @@ -435,6 +453,7 @@ int amdgpu_dm_irq_init(struct amdgpu_device *adev) return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_irq_init); /** * amdgpu_dm_irq_fini() - Tear down DM IRQ management @@ -473,6 +492,7 @@ void amdgpu_dm_irq_fini(struct amdgpu_device *adev) /* Deallocate handlers from the table. */ unregister_all_irq_handlers(adev); } +EXPORT_IF_KUNIT(amdgpu_dm_irq_fini); void amdgpu_dm_irq_suspend(struct amdgpu_device *adev) { @@ -675,7 +695,7 @@ static int amdgpu_dm_irq_handler(struct amdgpu_device *adev, return 0; } -static enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type) +STATIC_IFN_KUNIT enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type) { switch (type) { case AMDGPU_HPD_1: @@ -694,6 +714,7 @@ static enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type) return DC_IRQ_SOURCE_INVALID; } } +EXPORT_IF_KUNIT(amdgpu_dm_hpd_to_dal_irq_source); static int amdgpu_dm_set_hpd_irq_state(struct amdgpu_device *adev, struct amdgpu_irq_src *source, @@ -1020,3 +1041,1520 @@ void amdgpu_dm_hpd_fini(struct amdgpu_device *adev) if (dev->mode_config.poll_enabled) drm_kms_helper_poll_fini(dev); } + +/* ========== HPD handling ========== */ +static void force_connector_state( + struct amdgpu_dm_connector *aconnector, + enum drm_connector_force force_state) +{ + struct drm_connector *connector = &aconnector->base; + + mutex_lock(&connector->dev->mode_config.mutex); + aconnector->base.force = force_state; + mutex_unlock(&connector->dev->mode_config.mutex); + + mutex_lock(&aconnector->hpd_lock); + drm_kms_helper_connector_hotplug_event(connector); + mutex_unlock(&aconnector->hpd_lock); +} + +static void dm_handle_hpd_rx_offload_work(struct work_struct *work) +{ + struct hpd_rx_irq_offload_work *offload_work; + struct amdgpu_dm_connector *aconnector; + struct dc_link *dc_link; + struct amdgpu_device *adev; + enum dc_connection_type new_connection_type = dc_connection_none; + unsigned long flags; + union test_response test_response; + + memset(&test_response, 0, sizeof(test_response)); + + offload_work = container_of(work, struct hpd_rx_irq_offload_work, work); + aconnector = offload_work->offload_wq->aconnector; + adev = offload_work->adev; + + if (!aconnector) { + drm_err(adev_to_drm(adev), "Can't retrieve aconnector in hpd_rx_irq_offload_work"); + goto skip; + } + + dc_link = aconnector->dc_link; + + mutex_lock(&aconnector->hpd_lock); + if (!dc_link_detect_connection_type(dc_link, &new_connection_type)) + drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n"); + mutex_unlock(&aconnector->hpd_lock); + + if (new_connection_type == dc_connection_none) + goto skip; + + if (amdgpu_in_reset(adev)) + goto skip; + + if (offload_work->data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY || + offload_work->data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) { + dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr, DOWN_OR_UP_MSG_RDY_EVENT); + spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags); + offload_work->offload_wq->is_handling_mst_msg_rdy_event = false; + spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags); + goto skip; + } + + mutex_lock(&adev->dm.dc_lock); + if (offload_work->data.bytes.device_service_irq.bits.AUTOMATED_TEST) { + dc_link_dp_handle_automated_test(dc_link); + + if (aconnector->timing_changed) { + /* force connector disconnect and reconnect */ + force_connector_state(aconnector, DRM_FORCE_OFF); + msleep(100); + force_connector_state(aconnector, DRM_FORCE_UNSPECIFIED); + } + + test_response.bits.ACK = 1; + + core_link_write_dpcd( + dc_link, + DP_TEST_RESPONSE, + &test_response.raw, + sizeof(test_response)); + } else if ((dc_link->connector_signal != SIGNAL_TYPE_EDP) && + dc_link_check_link_loss_status(dc_link, &offload_work->data) && + dc_link_dp_allow_hpd_rx_irq(dc_link)) { + /* offload_work->data is from handle_hpd_rx_irq-> + * schedule_hpd_rx_offload_work.this is defer handle + * for hpd short pulse. upon here, link status may be + * changed, need get latest link status from dpcd + * registers. if link status is good, skip run link + * training again. + */ + union hpd_irq_data irq_data; + + memset(&irq_data, 0, sizeof(irq_data)); + + /* before dc_link_dp_handle_link_loss, allow new link lost handle + * request be added to work queue if link lost at end of dc_link_ + * dp_handle_link_loss + */ + spin_lock_irqsave(&offload_work->offload_wq->offload_lock, flags); + offload_work->offload_wq->is_handling_link_loss = false; + spin_unlock_irqrestore(&offload_work->offload_wq->offload_lock, flags); + + if ((dc_link_dp_read_hpd_rx_irq_data(dc_link, &irq_data) == DC_OK) && + dc_link_check_link_loss_status(dc_link, &irq_data)) + dc_link_dp_handle_link_loss(dc_link); + } + mutex_unlock(&adev->dm.dc_lock); + +skip: + kfree(offload_work); + +} + +struct hpd_rx_irq_offload_work_queue *amdgpu_dm_hpd_rx_irq_create_workqueue(struct amdgpu_device *adev) +{ + struct dc *dc = adev->dm.dc; + int max_caps = dc->caps.max_links; + int i = 0; + struct hpd_rx_irq_offload_work_queue *hpd_rx_offload_wq = NULL; + + hpd_rx_offload_wq = kzalloc_objs(*hpd_rx_offload_wq, max_caps); + + if (!hpd_rx_offload_wq) + return NULL; + + + for (i = 0; i < max_caps; i++) { + hpd_rx_offload_wq[i].wq = + create_singlethread_workqueue("amdgpu_dm_hpd_rx_offload_wq"); + + if (hpd_rx_offload_wq[i].wq == NULL) { + drm_err(adev_to_drm(adev), "create amdgpu_dm_hpd_rx_offload_wq fail!"); + goto out_err; + } + + spin_lock_init(&hpd_rx_offload_wq[i].offload_lock); + } + + return hpd_rx_offload_wq; + +out_err: + for (i = 0; i < max_caps; i++) { + if (hpd_rx_offload_wq[i].wq) + destroy_workqueue(hpd_rx_offload_wq[i].wq); + } + kfree(hpd_rx_offload_wq); + return NULL; +} + +void amdgpu_dm_hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm) +{ + int i; + + if (dm->hpd_rx_offload_wq) { + for (i = 0; i < dm->dc->caps.max_links; i++) + flush_workqueue(dm->hpd_rx_offload_wq[i].wq); + } +} + +STATIC_IFN_KUNIT bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2) +{ + if (!sink1 || !sink2) + return false; + if (sink1->sink_signal != sink2->sink_signal) + return false; + + if (sink1->dc_edid.length != sink2->dc_edid.length) + return false; + + if (memcmp(sink1->dc_edid.raw_edid, sink2->dc_edid.raw_edid, + sink1->dc_edid.length) != 0) + return false; + return true; +} +EXPORT_IF_KUNIT(are_sinks_equal); + + +/** + * DOC: amdgpu_dm_hdmi_hpd_debounce_work + * + * HDMI HPD debounce delay in milliseconds. When an HDMI display toggles HPD + * (such as during power save transitions), this delay determines how long to + * wait before processing the HPD event. This allows distinguishing between a + * physical unplug (>hdmi_hpd_debounce_delay) + * and a spontaneous RX HPD toggle (<hdmi_hpd_debounce_delay). + * + * If the toggle is less than this delay, the driver compares sink capabilities + * and permits a hotplug event if they changed. + * + * The default value of 1500ms was chosen based on experimental testing with + * various monitors that exhibit spontaneous HPD toggling behavior. + */ +void amdgpu_dm_hdmi_hpd_debounce_work(struct work_struct *work) +{ + struct amdgpu_dm_connector *aconnector = + container_of(to_delayed_work(work), struct amdgpu_dm_connector, + hdmi_hpd_debounce_work); + struct drm_connector *connector = &aconnector->base; + struct drm_device *dev = connector->dev; + struct amdgpu_device *adev = drm_to_adev(dev); + struct dc *dc = aconnector->dc_link->ctx->dc; + bool fake_reconnect = false; + bool reallow_idle = false; + bool ret = false; + + guard(mutex)(&aconnector->hpd_lock); + + /* Re-detect the display */ + scoped_guard(mutex, &adev->dm.dc_lock) { + if (dc->caps.ips_support && dc->ctx->dmub_srv->idle_allowed) { + dc_allow_idle_optimizations(dc, false); + reallow_idle = true; + } + ret = dc_link_detect(aconnector->dc_link, DETECT_REASON_HPD); + } + + if (ret) { + /* Apply workaround delay for certain panels */ + amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink); + /* Compare sinks to determine if this was a spontaneous HPD toggle */ + if (are_sinks_equal(aconnector->dc_link->local_sink, aconnector->hdmi_prev_sink)) { + /* + * Sinks match - this was a spontaneous HDMI HPD toggle. + */ + drm_dbg_kms(dev, "HDMI HPD: Sink unchanged after debounce, internal re-enable\n"); + fake_reconnect = true; + } + + /* Update connector state */ + amdgpu_dm_update_connector_after_detect(aconnector); + + drm_modeset_lock_all(dev); + dm_restore_drm_connector_state(dev, connector); + drm_modeset_unlock_all(dev); + + /* Only notify OS if sink actually changed */ + if (!fake_reconnect && aconnector->base.force == DRM_FORCE_UNSPECIFIED) + drm_kms_helper_hotplug_event(dev); + } + + /* Release the cached sink reference */ + if (aconnector->hdmi_prev_sink) { + dc_sink_release(aconnector->hdmi_prev_sink); + aconnector->hdmi_prev_sink = NULL; + } + + scoped_guard(mutex, &adev->dm.dc_lock) { + if (reallow_idle && dc->caps.ips_support) + dc_allow_idle_optimizations(dc, true); + } +} + +static void handle_hpd_irq_helper(struct amdgpu_dm_connector *aconnector, + enum dc_detect_reason reason) +{ + struct drm_connector *connector = &aconnector->base; + struct drm_device *dev = connector->dev; + enum dc_connection_type new_connection_type = dc_connection_none; + struct amdgpu_device *adev = drm_to_adev(dev); + struct dm_connector_state *dm_con_state = to_dm_connector_state(connector->state); + struct dc *dc = aconnector->dc_link->ctx->dc; + bool ret = false; + bool debounce_required = false; + + if (adev->dm.disable_hpd_irq) + return; + + /* + * In case of failure or MST no need to update connector status or notify the OS + * since (for MST case) MST does this in its own context. + */ + guard(mutex)(&aconnector->hpd_lock); + + if (adev->dm.hdcp_workqueue) { + hdcp_reset_display(adev->dm.hdcp_workqueue, aconnector->dc_link->link_index); + dm_con_state->update_hdcp = true; + } + if (aconnector->fake_enable) + aconnector->fake_enable = false; + + aconnector->timing_changed = false; + + if (!dc_link_detect_connection_type(aconnector->dc_link, &new_connection_type)) + drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n"); + + /* + * Check for HDMI disconnect with debounce enabled. + */ + debounce_required = (aconnector->hdmi_hpd_debounce_delay_ms > 0 && + dc_is_hdmi_signal(aconnector->dc_link->connector_signal) && + new_connection_type == dc_connection_none && + aconnector->dc_link->local_sink != NULL); + + if (aconnector->base.force && new_connection_type == dc_connection_none) { + amdgpu_dm_emulated_link_detect(aconnector->dc_link); + + drm_modeset_lock_all(dev); + dm_restore_drm_connector_state(dev, connector); + drm_modeset_unlock_all(dev); + + if (aconnector->base.force == DRM_FORCE_UNSPECIFIED || + reason == DETECT_REASON_HPDRX) + drm_kms_helper_connector_hotplug_event(connector); + } else if (debounce_required) { + /* + * HDMI disconnect detected - schedule delayed work instead of + * processing immediately. This allows us to coalesce spurious + * HDMI signals from physical unplugs. + */ + drm_dbg_kms(dev, "HDMI HPD: Disconnect detected, scheduling debounce work (%u ms)\n", + aconnector->hdmi_hpd_debounce_delay_ms); + + /* Cache the current sink for later comparison */ + if (aconnector->hdmi_prev_sink) + dc_sink_release(aconnector->hdmi_prev_sink); + aconnector->hdmi_prev_sink = aconnector->dc_link->local_sink; + if (aconnector->hdmi_prev_sink) + dc_sink_retain(aconnector->hdmi_prev_sink); + + /* Schedule delayed detection. */ + if (mod_delayed_work(system_percpu_wq, + &aconnector->hdmi_hpd_debounce_work, + msecs_to_jiffies(aconnector->hdmi_hpd_debounce_delay_ms))) + drm_dbg_kms(dev, "HDMI HPD: Re-scheduled debounce work\n"); + + } else { + + /* If the aconnector->hdmi_hpd_debounce_work is scheduled, exit early */ + if (delayed_work_pending(&aconnector->hdmi_hpd_debounce_work)) + return; + + scoped_guard(mutex, &adev->dm.dc_lock) { + dc_exit_ips_for_hw_access(dc); + ret = dc_link_detect(aconnector->dc_link, reason); + } + if (ret) { + /* w/a delay for certain panels */ + amdgpu_dm_apply_delay_after_dpcd_poweroff(adev, aconnector->dc_sink); + amdgpu_dm_update_connector_after_detect(aconnector); + + drm_modeset_lock_all(dev); + dm_restore_drm_connector_state(dev, connector); + drm_modeset_unlock_all(dev); + + if (aconnector->base.force == DRM_FORCE_UNSPECIFIED || + reason == DETECT_REASON_HPDRX) + drm_kms_helper_connector_hotplug_event(connector); + } + } +} + +static void handle_hpd_irq(void *param) +{ + struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param; + + handle_hpd_irq_helper(aconnector, DETECT_REASON_HPD); + +} + +static void schedule_hpd_rx_offload_work(struct amdgpu_device *adev, struct hpd_rx_irq_offload_work_queue *offload_wq, + union hpd_irq_data hpd_irq_data) +{ + struct hpd_rx_irq_offload_work *offload_work = kzalloc_obj(*offload_work); + + if (!offload_work) { + drm_err(adev_to_drm(adev), "Failed to allocate hpd_rx_irq_offload_work.\n"); + return; + } + + INIT_WORK(&offload_work->work, dm_handle_hpd_rx_offload_work); + offload_work->data = hpd_irq_data; + offload_work->offload_wq = offload_wq; + offload_work->adev = adev; + + queue_work(offload_wq->wq, &offload_work->work); + drm_dbg_kms(adev_to_drm(adev), "queue work to handle hpd_rx offload work"); +} + +static void handle_hpd_rx_irq(void *param) +{ + struct amdgpu_dm_connector *aconnector = (struct amdgpu_dm_connector *)param; + struct drm_connector *connector = &aconnector->base; + struct drm_device *dev = connector->dev; + struct dc_link *dc_link = aconnector->dc_link; + bool is_mst_root_connector = aconnector->mst_mgr.mst_state; + bool result = false; + enum dc_connection_type new_connection_type = dc_connection_none; + struct amdgpu_device *adev = drm_to_adev(dev); + union hpd_irq_data hpd_irq_data; + bool link_loss = false; + bool has_left_work = false; + int idx = dc_link->link_index; + struct hpd_rx_irq_offload_work_queue *offload_wq = &adev->dm.hpd_rx_offload_wq[idx]; + struct dc *dc = aconnector->dc_link->ctx->dc; + + memset(&hpd_irq_data, 0, sizeof(hpd_irq_data)); + + if (adev->dm.disable_hpd_irq) + return; + + /* + * TODO:Temporary add mutex to protect hpd interrupt not have a gpio + * conflict, after implement i2c helper, this mutex should be + * retired. + */ + mutex_lock(&aconnector->hpd_lock); + + result = dc_link_handle_hpd_rx_irq(dc_link, &hpd_irq_data, + &link_loss, true, &has_left_work); + + if (!has_left_work) + goto out; + + if (hpd_irq_data.bytes.device_service_irq.bits.AUTOMATED_TEST) { + schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data); + goto out; + } + + if (dc_link_dp_allow_hpd_rx_irq(dc_link)) { + if (hpd_irq_data.bytes.device_service_irq.bits.UP_REQ_MSG_RDY || + hpd_irq_data.bytes.device_service_irq.bits.DOWN_REP_MSG_RDY) { + bool skip = false; + + /* + * DOWN_REP_MSG_RDY is also handled by polling method + * mgr->cbs->poll_hpd_irq() + */ + spin_lock(&offload_wq->offload_lock); + skip = offload_wq->is_handling_mst_msg_rdy_event; + + if (!skip) + offload_wq->is_handling_mst_msg_rdy_event = true; + + spin_unlock(&offload_wq->offload_lock); + + if (!skip) + schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data); + + goto out; + } + + if (link_loss) { + bool skip = false; + + spin_lock(&offload_wq->offload_lock); + skip = offload_wq->is_handling_link_loss; + + if (!skip) + offload_wq->is_handling_link_loss = true; + + spin_unlock(&offload_wq->offload_lock); + + if (!skip) + schedule_hpd_rx_offload_work(adev, offload_wq, hpd_irq_data); + + goto out; + } + } + +out: + if (result && !is_mst_root_connector) { + /* Downstream Port status changed. */ + if (!dc_link_detect_connection_type(dc_link, &new_connection_type)) + drm_err(adev_to_drm(adev), "KMS: Failed to detect connector\n"); + + if (aconnector->base.force && new_connection_type == dc_connection_none) { + amdgpu_dm_emulated_link_detect(dc_link); + + if (aconnector->fake_enable) + aconnector->fake_enable = false; + + amdgpu_dm_update_connector_after_detect(aconnector); + + + drm_modeset_lock_all(dev); + dm_restore_drm_connector_state(dev, connector); + drm_modeset_unlock_all(dev); + + drm_kms_helper_connector_hotplug_event(connector); + } else { + bool ret = false; + + mutex_lock(&adev->dm.dc_lock); + dc_exit_ips_for_hw_access(dc); + ret = dc_link_detect(dc_link, DETECT_REASON_HPDRX); + mutex_unlock(&adev->dm.dc_lock); + + if (ret) { + if (aconnector->fake_enable) + aconnector->fake_enable = false; + + amdgpu_dm_update_connector_after_detect(aconnector); + + drm_modeset_lock_all(dev); + dm_restore_drm_connector_state(dev, connector); + drm_modeset_unlock_all(dev); + + drm_kms_helper_connector_hotplug_event(connector); + } + } + } + if (hpd_irq_data.bytes.device_service_irq.bits.CP_IRQ) { + if (adev->dm.hdcp_workqueue) + hdcp_handle_cpirq(adev->dm.hdcp_workqueue, aconnector->base.index); + } + + if (dc_link->type != dc_connection_mst_branch) + drm_dp_cec_irq(&aconnector->dm_dp_aux.aux); + + mutex_unlock(&aconnector->hpd_lock); +} + +/** + * dmub_hpd_callback - DMUB HPD interrupt processing callback. + * @adev: amdgpu_device pointer + * @notify: dmub notification structure + * + * Dmub Hpd interrupt processing callback. Gets displayindex through the + * ink index and calls helper to do the processing. + */ +static void dmub_hpd_callback(struct amdgpu_device *adev, + struct dmub_notification *notify) +{ + struct amdgpu_dm_connector *aconnector; + struct amdgpu_dm_connector *hpd_aconnector = NULL; + struct drm_connector *connector; + struct drm_connector_list_iter iter; + struct dc_link *link; + u8 link_index = 0; + struct drm_device *dev; + + if (adev == NULL) + return; + + if (notify == NULL) { + drm_err(adev_to_drm(adev), "DMUB HPD callback notification was NULL"); + return; + } + + if (notify->link_index > adev->dm.dc->link_count) { + drm_err(adev_to_drm(adev), "DMUB HPD index (%u)is abnormal", notify->link_index); + return; + } + + /* Skip DMUB HPD IRQ in suspend/resume. We will probe them later. */ + if (notify->type == DMUB_NOTIFICATION_HPD && adev->in_suspend) { + drm_info(adev_to_drm(adev), "Skip DMUB HPD IRQ callback in suspend/resume\n"); + return; + } + + link_index = notify->link_index; + link = adev->dm.dc->links[link_index]; + dev = adev->dm.ddev; + + drm_connector_list_iter_begin(dev, &iter); + drm_for_each_connector_iter(connector, &iter) { + + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + continue; + + aconnector = to_amdgpu_dm_connector(connector); + if (link && aconnector->dc_link == link) { + if (notify->type == DMUB_NOTIFICATION_HPD) + drm_info(adev_to_drm(adev), "DMUB HPD IRQ callback: link_index=%u\n", link_index); + else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ) + drm_info(adev_to_drm(adev), "DMUB HPD RX IRQ callback: link_index=%u\n", link_index); + else + drm_warn(adev_to_drm(adev), "DMUB Unknown HPD callback type %d, link_index=%u\n", + notify->type, link_index); + + hpd_aconnector = aconnector; + break; + } + } + drm_connector_list_iter_end(&iter); + + if (hpd_aconnector) { + if (notify->type == DMUB_NOTIFICATION_HPD) { + if (hpd_aconnector->dc_link->hpd_status == (notify->hpd_status == DP_HPD_PLUG)) + drm_warn(adev_to_drm(adev), "DMUB reported hpd status unchanged. link_index=%u\n", link_index); + handle_hpd_irq_helper(hpd_aconnector, DETECT_REASON_HPD); + } else if (notify->type == DMUB_NOTIFICATION_HPD_IRQ) { + handle_hpd_rx_irq(hpd_aconnector); + } + } +} + +/** + * dmub_hpd_sense_callback - DMUB HPD sense processing callback. + * @adev: amdgpu_device pointer + * @notify: dmub notification structure + * + * HPD sense changes can occur during low power states and need to be + * notified from firmware to driver. + */ +static void dmub_hpd_sense_callback(struct amdgpu_device *adev, + struct dmub_notification *notify) +{ + drm_dbg_driver(adev_to_drm(adev), "DMUB HPD SENSE callback.\n"); +} + +int amdgpu_dm_register_hpd_handlers(struct amdgpu_device *adev) +{ + struct drm_device *dev = adev_to_drm(adev); + struct drm_connector *connector; + struct amdgpu_dm_connector *aconnector; + const struct dc_link *dc_link; + struct dc_interrupt_params int_params = {0}; + + int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; + int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; + + if (dc_is_dmub_outbox_supported(adev->dm.dc)) { + if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD, + dmub_hpd_callback, true)) { + drm_err(adev_to_drm(adev), "fail to register dmub hpd callback"); + return -EINVAL; + } + + if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_IRQ, + dmub_hpd_callback, true)) { + drm_err(adev_to_drm(adev), "fail to register dmub hpd callback"); + return -EINVAL; + } + + if (!dm_register_dmub_notify_callback(adev, DMUB_NOTIFICATION_HPD_SENSE_NOTIFY, + dmub_hpd_sense_callback, true)) { + drm_err(adev_to_drm(adev), "fail to register dmub hpd sense callback"); + return -EINVAL; + } + } + + list_for_each_entry(connector, + &dev->mode_config.connector_list, head) { + + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + continue; + + aconnector = to_amdgpu_dm_connector(connector); + dc_link = aconnector->dc_link; + + if (dc_link->irq_source_hpd != DC_IRQ_SOURCE_INVALID) { + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = dc_link->irq_source_hpd; + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || + int_params.irq_source < DC_IRQ_SOURCE_HPD1 || + int_params.irq_source > DC_IRQ_SOURCE_HPD6) { + drm_err(adev_to_drm(adev), "Failed to register hpd irq!\n"); + return -EINVAL; + } + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + handle_hpd_irq, (void *) aconnector)) + return -ENOMEM; + } + + if (dc_link->irq_source_hpd_rx != DC_IRQ_SOURCE_INVALID) { + + /* Also register for DP short pulse (hpd_rx). */ + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = dc_link->irq_source_hpd_rx; + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || + int_params.irq_source < DC_IRQ_SOURCE_HPD1RX || + int_params.irq_source > DC_IRQ_SOURCE_HPD6RX) { + drm_err(adev_to_drm(adev), "Failed to register hpd rx irq!\n"); + return -EINVAL; + } + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + handle_hpd_rx_irq, (void *) aconnector)) + return -ENOMEM; + } + } + return 0; +} + +/* ========== IRQ handlers ========== */ +struct amdgpu_crtc * +amdgpu_dm_get_crtc_by_otg_inst(struct amdgpu_device *adev, + int otg_inst) +{ + struct drm_device *dev = adev_to_drm(adev); + struct drm_crtc *crtc; + struct amdgpu_crtc *amdgpu_crtc; + + if (WARN_ON(otg_inst == -1)) + return adev->mode_info.crtcs[0]; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + amdgpu_crtc = to_amdgpu_crtc(crtc); + + if (amdgpu_crtc->otg_inst == otg_inst) + return amdgpu_crtc; + } + + return NULL; +} +EXPORT_IF_KUNIT(amdgpu_dm_get_crtc_by_otg_inst); + +/** + * dm_pflip_high_irq() - Handle pageflip interrupt + * @interrupt_params: ignored + * + * Handles the pageflip interrupt by notifying all interested parties + * that the pageflip has been completed. + */ +static void dm_pflip_high_irq(void *interrupt_params) +{ + struct amdgpu_crtc *amdgpu_crtc; + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct drm_device *dev = adev_to_drm(adev); + unsigned long flags; + struct drm_pending_vblank_event *e; + u32 vpos, hpos, v_blank_start, v_blank_end; + bool vrr_active; + + amdgpu_crtc = amdgpu_dm_get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_PFLIP); + + /* IRQ could occur when in initial stage */ + /* TODO work and BO cleanup */ + if (amdgpu_crtc == NULL) { + drm_dbg_state(dev, "CRTC is null, returning.\n"); + return; + } + + spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); + + if (amdgpu_crtc->pflip_status != AMDGPU_FLIP_SUBMITTED) { + drm_dbg_state(dev, + "amdgpu_crtc->pflip_status = %d != AMDGPU_FLIP_SUBMITTED(%d) on crtc:%d[%p]\n", + amdgpu_crtc->pflip_status, AMDGPU_FLIP_SUBMITTED, + amdgpu_crtc->crtc_id, amdgpu_crtc); + spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); + return; + } + + /* page flip completed. */ + e = amdgpu_crtc->event; + amdgpu_crtc->event = NULL; + + WARN_ON(!e); + + vrr_active = amdgpu_dm_crtc_vrr_active_irq(amdgpu_crtc); + + /* Fixed refresh rate, or VRR scanout position outside front-porch? */ + if (!vrr_active || + !dc_stream_get_scanoutpos(amdgpu_crtc->dm_irq_params.stream, &v_blank_start, + &v_blank_end, &hpos, &vpos) || + (vpos < v_blank_start)) { + /* Update to correct count and vblank timestamp if racing with + * vblank irq. This also updates to the correct vblank timestamp + * even in VRR mode, as scanout is past the front-porch atm. + */ + drm_crtc_accurate_vblank_count(&amdgpu_crtc->base); + + /* Wake up userspace by sending the pageflip event with proper + * count and timestamp of vblank of flip completion. + */ + if (e) { + drm_crtc_send_vblank_event(&amdgpu_crtc->base, e); + + /* Event sent, so done with vblank for this flip */ + drm_crtc_vblank_put(&amdgpu_crtc->base); + } + } else if (e) { + /* VRR active and inside front-porch: vblank count and + * timestamp for pageflip event will only be up to date after + * drm_crtc_handle_vblank() has been executed from late vblank + * irq handler after start of back-porch (vline 0). We queue the + * pageflip event for send-out by drm_crtc_handle_vblank() with + * updated timestamp and count, once it runs after us. + * + * We need to open-code this instead of using the helper + * drm_crtc_arm_vblank_event(), as that helper would + * call drm_crtc_accurate_vblank_count(), which we must + * not call in VRR mode while we are in front-porch! + */ + + /* sequence will be replaced by real count during send-out. */ + e->sequence = drm_crtc_vblank_count(&amdgpu_crtc->base); + e->pipe = amdgpu_crtc->crtc_id; + + list_add_tail(&e->base.link, &adev_to_drm(adev)->vblank_event_list); + e = NULL; + } + + /* Keep track of vblank of this flip for flip throttling. We use the + * cooked hw counter, as that one incremented at start of this vblank + * of pageflip completion, so last_flip_vblank is the forbidden count + * for queueing new pageflips if vsync + VRR is enabled. + */ + amdgpu_crtc->dm_irq_params.last_flip_vblank = + amdgpu_get_vblank_counter_kms(&amdgpu_crtc->base); + + amdgpu_crtc->pflip_status = AMDGPU_FLIP_NONE; + spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); + + drm_dbg_state(dev, + "crtc:%d[%p], pflip_stat:AMDGPU_FLIP_NONE, vrr[%d]-fp %d\n", + amdgpu_crtc->crtc_id, amdgpu_crtc, vrr_active, (int)!e); +} + +static void dm_handle_vmin_vmax_update(struct work_struct *offload_work) +{ + struct vupdate_offload_work *work = container_of(offload_work, struct vupdate_offload_work, work); + struct amdgpu_device *adev = work->adev; + struct dc_stream_state *stream = work->stream; + struct dc_crtc_timing_adjust *adjust = work->adjust; + + mutex_lock(&adev->dm.dc_lock); + dc_stream_adjust_vmin_vmax(adev->dm.dc, stream, adjust); + mutex_unlock(&adev->dm.dc_lock); + + dc_stream_release(stream); + kfree(work->adjust); + kfree(work); +} + +static void schedule_dc_vmin_vmax(struct amdgpu_device *adev, + struct dc_stream_state *stream, + struct dc_crtc_timing_adjust *adjust) +{ + struct vupdate_offload_work *offload_work = kzalloc_obj(*offload_work, + GFP_NOWAIT); + if (!offload_work) { + drm_dbg_driver(adev_to_drm(adev), "Failed to allocate vupdate_offload_work\n"); + return; + } + + struct dc_crtc_timing_adjust *adjust_copy = kzalloc_obj(*adjust_copy, + GFP_NOWAIT); + if (!adjust_copy) { + drm_dbg_driver(adev_to_drm(adev), "Failed to allocate adjust_copy\n"); + kfree(offload_work); + return; + } + + dc_stream_retain(stream); + memcpy(adjust_copy, adjust, sizeof(*adjust_copy)); + + INIT_WORK(&offload_work->work, dm_handle_vmin_vmax_update); + offload_work->adev = adev; + offload_work->stream = stream; + offload_work->adjust = adjust_copy; + + queue_work(system_percpu_wq, &offload_work->work); +} + +static void dm_vupdate_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_crtc *acrtc; + struct drm_device *drm_dev; + struct drm_vblank_crtc *vblank; + ktime_t frame_duration_ns, previous_timestamp; + unsigned long flags; + int vrr_active; + + acrtc = amdgpu_dm_get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VUPDATE); + + if (acrtc) { + vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); + drm_dev = acrtc->base.dev; + vblank = drm_crtc_vblank_crtc(&acrtc->base); + previous_timestamp = atomic64_read(&irq_params->previous_timestamp); + frame_duration_ns = vblank->time - previous_timestamp; + + if (frame_duration_ns > 0) { + trace_amdgpu_refresh_rate_track(acrtc->base.index, + frame_duration_ns, + ktime_divns(NSEC_PER_SEC, frame_duration_ns)); + atomic64_set(&irq_params->previous_timestamp, vblank->time); + } + + drm_dbg_vbl(drm_dev, + "crtc:%d, vupdate-vrr:%d\n", acrtc->crtc_id, + vrr_active); + + /* Core vblank handling is done here after end of front-porch in + * vrr mode, as vblank timestamping will give valid results + * while now done after front-porch. This will also deliver + * page-flip completion events that have been queued to us + * if a pageflip happened inside front-porch. + */ + if (vrr_active && acrtc->dm_irq_params.stream) { + bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; + bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; + bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state + == VRR_STATE_ACTIVE_VARIABLE; + + amdgpu_dm_crtc_handle_vblank(acrtc); + + /* BTR processing for pre-DCE12 ASICs */ + if (adev->family < AMDGPU_FAMILY_AI) { + spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); + mod_freesync_handle_v_update( + adev->dm.freesync_module, + acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params); + + if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { + schedule_dc_vmin_vmax(adev, + acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params.adjust); + } + spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); + } + } + } +} + +/** + * dm_crtc_high_irq() - Handles CRTC interrupt + * @interrupt_params: used for determining the CRTC instance + * + * Handles the CRTC/VSYNC interrupt by notfying DRM's VBLANK + * event handler. + */ +static void dm_crtc_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_crtc *acrtc; + unsigned long flags; + int vrr_active; + + acrtc = amdgpu_dm_get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VBLANK); + if (!acrtc) + return; + + if (acrtc->wb_conn && acrtc->wb_pending) { + struct dc_stream_state *stream = acrtc->dm_irq_params.stream; + unsigned int v_total, refresh_hz; + + v_total = stream->adjust.v_total_max ? + stream->adjust.v_total_max : stream->timing.v_total; + refresh_hz = div_u64((uint64_t) stream->timing.pix_clk_100hz * + 100LL, (v_total * stream->timing.h_total)); + mdelay(1000 / refresh_hz); + + /* + * Completion (signalling the out fence and releasing the vblank + * reference taken in dm_set_writeback()) is handled by the shared + * helper, which is also used by the teardown path. + */ + if (amdgpu_dm_crtc_complete_writeback(acrtc)) + dc_stream_fc_disable_writeback(adev->dm.dc, + acrtc->dm_irq_params.stream, 0); + } + + vrr_active = amdgpu_dm_crtc_vrr_active_irq(acrtc); + + drm_dbg_vbl(adev_to_drm(adev), + "crtc:%d, vupdate-vrr:%d, planes:%d\n", acrtc->crtc_id, + vrr_active, acrtc->dm_irq_params.active_planes); + + /** + * Core vblank handling at start of front-porch is only possible + * in non-vrr mode, as only there vblank timestamping will give + * valid results while done in front-porch. Otherwise defer it + * to dm_vupdate_high_irq after end of front-porch. + */ + if (!vrr_active) + amdgpu_dm_crtc_handle_vblank(acrtc); + + /** + * Following stuff must happen at start of vblank, for crc + * computation and below-the-range btr support in vrr mode. + */ + amdgpu_dm_crtc_handle_crc_irq(&acrtc->base); + + /* BTR updates need to happen before VUPDATE on Vega and above. */ + if (adev->family < AMDGPU_FAMILY_AI) + return; + + spin_lock_irqsave(&adev_to_drm(adev)->event_lock, flags); + + if (acrtc->dm_irq_params.stream && + acrtc->dm_irq_params.vrr_params.supported) { + bool replay_en = acrtc->dm_irq_params.stream->link->replay_settings.replay_feature_enabled; + bool psr_en = acrtc->dm_irq_params.stream->link->psr_settings.psr_feature_enabled; + bool fs_active_var_en = acrtc->dm_irq_params.freesync_config.state == VRR_STATE_ACTIVE_VARIABLE; + + mod_freesync_handle_v_update(adev->dm.freesync_module, + acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params); + + /* update vmin_vmax only if freesync is enabled, or only if PSR and REPLAY are disabled */ + if (fs_active_var_en || (!fs_active_var_en && !replay_en && !psr_en)) { + schedule_dc_vmin_vmax(adev, acrtc->dm_irq_params.stream, + &acrtc->dm_irq_params.vrr_params.adjust); + } + } + + /* + * If there aren't any active_planes then DCH HUBP may be clock-gated. + * In that case, pageflip completion interrupts won't fire and pageflip + * completion events won't get delivered. Prevent this by sending + * pending pageflip events from here if a flip is still pending. + * + * If any planes are enabled, use dm_pflip_high_irq() instead, to + * avoid race conditions between flip programming and completion, + * which could cause too early flip completion events. + */ + if (adev->family >= AMDGPU_FAMILY_RV && + acrtc->pflip_status == AMDGPU_FLIP_SUBMITTED && + acrtc->dm_irq_params.active_planes == 0) { + if (acrtc->event) { + drm_crtc_send_vblank_event(&acrtc->base, acrtc->event); + acrtc->event = NULL; + drm_crtc_vblank_put(&acrtc->base); + } + acrtc->pflip_status = AMDGPU_FLIP_NONE; + } + + spin_unlock_irqrestore(&adev_to_drm(adev)->event_lock, flags); +} + +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) +/** + * dm_dcn_vertical_interrupt0_high_irq() - Handles OTG Vertical interrupt0 for + * DCN generation ASICs + * @interrupt_params: interrupt parameters + * + * Used to set crc window/read out crc value at vertical line 0 position + */ +static void dm_dcn_vertical_interrupt0_high_irq(void *interrupt_params) +{ + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_crtc *acrtc; + + acrtc = amdgpu_dm_get_crtc_by_otg_inst(adev, irq_params->irq_src - IRQ_TYPE_VLINE0); + + if (!acrtc) + return; + + amdgpu_dm_crtc_handle_crc_window_irq(&acrtc->base); +} +#endif /* CONFIG_DRM_AMD_SECURE_DISPLAY */ + +static void dm_handle_hpd_work(struct work_struct *work) +{ + struct dmub_hpd_work *dmub_hpd_wrk; + + dmub_hpd_wrk = container_of(work, struct dmub_hpd_work, handle_hpd_work); + + if (!dmub_hpd_wrk->dmub_notify) { + drm_err(adev_to_drm(dmub_hpd_wrk->adev), "dmub_hpd_wrk dmub_notify is NULL"); + return; + } + + if (dmub_hpd_wrk->dmub_notify->type < ARRAY_SIZE(dmub_hpd_wrk->adev->dm.dmub_callback)) { + dmub_hpd_wrk->adev->dm.dmub_callback[dmub_hpd_wrk->dmub_notify->type](dmub_hpd_wrk->adev, + dmub_hpd_wrk->dmub_notify); + } + + kfree(dmub_hpd_wrk->dmub_notify); + kfree(dmub_hpd_wrk); + +} + +STATIC_IFN_KUNIT const char *dmub_notification_type_str(enum dmub_notification_type e) +{ + switch (e) { + case DMUB_NOTIFICATION_NO_DATA: + return "NO_DATA"; + case DMUB_NOTIFICATION_AUX_REPLY: + return "AUX_REPLY"; + case DMUB_NOTIFICATION_HPD: + return "HPD"; + case DMUB_NOTIFICATION_HPD_IRQ: + return "HPD_IRQ"; + case DMUB_NOTIFICATION_SET_CONFIG_REPLY: + return "SET_CONFIG_REPLY"; + case DMUB_NOTIFICATION_DPIA_NOTIFICATION: + return "DPIA_NOTIFICATION"; + case DMUB_NOTIFICATION_HPD_SENSE_NOTIFY: + return "HPD_SENSE_NOTIFY"; + case DMUB_NOTIFICATION_FUSED_IO: + return "FUSED_IO"; + default: + return "<unknown>"; + } +} +EXPORT_IF_KUNIT(dmub_notification_type_str); + +#define DMUB_TRACE_MAX_READ 64 +/** + * dm_dmub_outbox1_low_irq() - Handles Outbox interrupt + * @interrupt_params: used for determining the Outbox instance + * + * Handles the Outbox Interrupt + * event handler. + */ +static void dm_dmub_outbox1_low_irq(void *interrupt_params) +{ + struct dmub_notification notify = {0}; + struct common_irq_params *irq_params = interrupt_params; + struct amdgpu_device *adev = irq_params->adev; + struct amdgpu_display_manager *dm = &adev->dm; + struct dmcub_trace_buf_entry entry = { 0 }; + u32 count = 0; + struct dmub_hpd_work *dmub_hpd_wrk; + + do { + if (dc_dmub_srv_get_dmub_outbox0_msg(dm->dc, &entry)) { + trace_amdgpu_dmub_trace_high_irq(entry.trace_code, entry.tick_count, + entry.param0, entry.param1); + + drm_dbg_driver(adev_to_drm(adev), "trace_code:%u, tick_count:%u, param0:%u, param1:%u\n", + entry.trace_code, entry.tick_count, entry.param0, entry.param1); + } else + break; + + count++; + + } while (count <= DMUB_TRACE_MAX_READ); + + if (count > DMUB_TRACE_MAX_READ) + drm_dbg_driver(adev_to_drm(adev), "Warning : count > DMUB_TRACE_MAX_READ"); + + if (dc_enable_dmub_notifications(adev->dm.dc) && + irq_params->irq_src == DC_IRQ_SOURCE_DMCUB_OUTBOX) { + + do { + dc_stat_get_dmub_notification(adev->dm.dc, ¬ify); + if (notify.type >= ARRAY_SIZE(dm->dmub_thread_offload)) { + drm_err(adev_to_drm(adev), "DM: notify type %d invalid!", notify.type); + continue; + } + if (!dm->dmub_callback[notify.type]) { + drm_warn(adev_to_drm(adev), "DMUB notification skipped due to no handler: type=%s\n", + dmub_notification_type_str(notify.type)); + continue; + } + if (dm->dmub_thread_offload[notify.type]) { + dmub_hpd_wrk = kzalloc_obj(*dmub_hpd_wrk, + GFP_ATOMIC); + if (!dmub_hpd_wrk) { + drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk"); + return; + } + dmub_hpd_wrk->dmub_notify = kmemdup(¬ify, sizeof(struct dmub_notification), + GFP_ATOMIC); + if (!dmub_hpd_wrk->dmub_notify) { + kfree(dmub_hpd_wrk); + drm_err(adev_to_drm(adev), "Failed to allocate dmub_hpd_wrk->dmub_notify"); + return; + } + INIT_WORK(&dmub_hpd_wrk->handle_hpd_work, dm_handle_hpd_work); + dmub_hpd_wrk->adev = adev; + queue_work(adev->dm.delayed_hpd_wq, &dmub_hpd_wrk->handle_hpd_work); + } else { + dm->dmub_callback[notify.type](adev, ¬ify); + } + } while (notify.pending_notification); + } +} + +/* Register IRQ sources and initialize IRQ callbacks */ +int amdgpu_dm_dce110_register_irq_handlers(struct amdgpu_device *adev) +{ + struct dc *dc = adev->dm.dc; + struct common_irq_params *c_irq_params; + struct dc_interrupt_params int_params = {0}; + int r; + int i; + unsigned int src_id; + unsigned int client_id = AMDGPU_IRQ_CLIENTID_LEGACY; + /* Use different interrupts for VBLANK on DCE 6 vs. newer. */ + const unsigned int vblank_d1 = + adev->dm.dc->ctx->dce_version >= DCE_VERSION_8_0 + ? VISLANDS30_IV_SRCID_D1_VERTICAL_INTERRUPT0 : 1; + + if (adev->family >= AMDGPU_FAMILY_AI) + client_id = SOC15_IH_CLIENTID_DCE; + + int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; + int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; + + /* + * Actions of amdgpu_irq_add_id(): + * 1. Register a set() function with base driver. + * Base driver will call set() function to enable/disable an + * interrupt in DC hardware. + * 2. Register amdgpu_dm_irq_handler(). + * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts + * coming from DC hardware. + * amdgpu_dm_irq_handler() will re-direct the interrupt to DC + * for acknowledging and handling. + */ + + /* Use VBLANK interrupt */ + for (i = 0; i < adev->mode_info.num_crtc; i++) { + src_id = vblank_d1 + i; + r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->crtc_irq); + if (r) { + drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, src_id, 0); + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || + int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 || + int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) { + drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n"); + return -EINVAL; + } + + c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_crtc_high_irq, c_irq_params)) + return -ENOMEM; + } + + if (dc_supports_vrr(adev->dm.dc->ctx->dce_version)) { + /* Use VUPDATE interrupt */ + for (i = 0; i < adev->mode_info.num_crtc; i++) { + src_id = VISLANDS30_IV_SRCID_D1_V_UPDATE_INT + i * 2; + r = amdgpu_irq_add_id(adev, client_id, src_id, &adev->vupdate_irq); + if (r) { + drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, src_id, 0); + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || + int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 || + int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) { + drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n"); + return -EINVAL; + } + + c_irq_params = &adev->dm.vupdate_params[ + int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1]; + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_vupdate_high_irq, c_irq_params)) + return -ENOMEM; + } + } + + /* Use GRPH_PFLIP interrupt */ + for (i = VISLANDS30_IV_SRCID_D1_GRPH_PFLIP; + i <= VISLANDS30_IV_SRCID_D6_GRPH_PFLIP; i += 2) { + r = amdgpu_irq_add_id(adev, client_id, i, &adev->pageflip_irq); + if (r) { + drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || + int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST || + int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) { + drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n"); + return -EINVAL; + } + + c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_pflip_high_irq, c_irq_params)) + return -ENOMEM; + } + + /* HPD */ + r = amdgpu_irq_add_id(adev, client_id, + VISLANDS30_IV_SRCID_HOTPLUG_DETECT_A, &adev->hpd_irq); + if (r) { + drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n"); + return r; + } + + r = amdgpu_dm_register_hpd_handlers(adev); + + return r; +} + +/* Register IRQ sources and initialize IRQ callbacks */ +int amdgpu_dm_dcn10_register_irq_handlers(struct amdgpu_device *adev) +{ + struct dc *dc = adev->dm.dc; + struct common_irq_params *c_irq_params; + struct dc_interrupt_params int_params = {0}; + int r; + int i; +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + static const unsigned int vrtl_int_srcid[] = { + DCN_1_0__SRCID__OTG1_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG2_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG3_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG4_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG5_VERTICAL_INTERRUPT0_CONTROL, + DCN_1_0__SRCID__OTG6_VERTICAL_INTERRUPT0_CONTROL + }; +#endif + + int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; + int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; + + /* + * Actions of amdgpu_irq_add_id(): + * 1. Register a set() function with base driver. + * Base driver will call set() function to enable/disable an + * interrupt in DC hardware. + * 2. Register amdgpu_dm_irq_handler(). + * Base driver will call amdgpu_dm_irq_handler() for ALL interrupts + * coming from DC hardware. + * amdgpu_dm_irq_handler() will re-direct the interrupt to DC + * for acknowledging and handling. + */ + + /* Use VSTARTUP interrupt */ + for (i = DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP; + i <= DCN_1_0__SRCID__DC_D1_OTG_VSTARTUP + adev->mode_info.num_crtc - 1; + i++) { + r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->crtc_irq); + + if (r) { + drm_err(adev_to_drm(adev), "Failed to add crtc irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || + int_params.irq_source < DC_IRQ_SOURCE_VBLANK1 || + int_params.irq_source > DC_IRQ_SOURCE_VBLANK6) { + drm_err(adev_to_drm(adev), "Failed to register vblank irq!\n"); + return -EINVAL; + } + + c_irq_params = &adev->dm.vblank_params[int_params.irq_source - DC_IRQ_SOURCE_VBLANK1]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_crtc_high_irq, c_irq_params)) + return -ENOMEM; + } + + /* Use otg vertical line interrupt */ +#if defined(CONFIG_DRM_AMD_SECURE_DISPLAY) + for (i = 0; i <= adev->mode_info.num_crtc - 1; i++) { + r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, + vrtl_int_srcid[i], &adev->vline0_irq); + + if (r) { + drm_err(adev_to_drm(adev), "Failed to add vline0 irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, vrtl_int_srcid[i], 0); + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || + int_params.irq_source < DC_IRQ_SOURCE_DC1_VLINE0 || + int_params.irq_source > DC_IRQ_SOURCE_DC6_VLINE0) { + drm_err(adev_to_drm(adev), "Failed to register vline0 irq!\n"); + return -EINVAL; + } + + c_irq_params = &adev->dm.vline0_params[int_params.irq_source + - DC_IRQ_SOURCE_DC1_VLINE0]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_dcn_vertical_interrupt0_high_irq, + c_irq_params)) + return -ENOMEM; + } +#endif + + /* Use VUPDATE_NO_LOCK interrupt on DCN, which seems to correspond to + * the regular VUPDATE interrupt on DCE. We want DC_IRQ_SOURCE_VUPDATEx + * to trigger at end of each vblank, regardless of state of the lock, + * matching DCE behaviour. + */ + for (i = DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT; + i <= DCN_1_0__SRCID__OTG0_IHC_V_UPDATE_NO_LOCK_INTERRUPT + adev->mode_info.num_crtc - 1; + i++) { + r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->vupdate_irq); + + if (r) { + drm_err(adev_to_drm(adev), "Failed to add vupdate irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || + int_params.irq_source < DC_IRQ_SOURCE_VUPDATE1 || + int_params.irq_source > DC_IRQ_SOURCE_VUPDATE6) { + drm_err(adev_to_drm(adev), "Failed to register vupdate irq!\n"); + return -EINVAL; + } + + c_irq_params = &adev->dm.vupdate_params[int_params.irq_source - DC_IRQ_SOURCE_VUPDATE1]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_vupdate_high_irq, c_irq_params)) + return -ENOMEM; + } + + /* Use GRPH_PFLIP interrupt */ + for (i = DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT; + i <= DCN_1_0__SRCID__HUBP0_FLIP_INTERRUPT + dc->caps.max_otg_num - 1; + i++) { + r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, i, &adev->pageflip_irq); + if (r) { + drm_err(adev_to_drm(adev), "Failed to add page flip irq id!\n"); + return r; + } + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + if (int_params.irq_source == DC_IRQ_SOURCE_INVALID || + int_params.irq_source < DC_IRQ_SOURCE_PFLIP_FIRST || + int_params.irq_source > DC_IRQ_SOURCE_PFLIP_LAST) { + drm_err(adev_to_drm(adev), "Failed to register pflip irq!\n"); + return -EINVAL; + } + + c_irq_params = &adev->dm.pflip_params[int_params.irq_source - DC_IRQ_SOURCE_PFLIP_FIRST]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_pflip_high_irq, c_irq_params)) + return -ENOMEM; + } + + /* HPD */ + r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DC_HPD1_INT, + &adev->hpd_irq); + if (r) { + drm_err(adev_to_drm(adev), "Failed to add hpd irq id!\n"); + return r; + } + + r = amdgpu_dm_register_hpd_handlers(adev); + + return r; +} + +/* Register Outbox IRQ sources and initialize IRQ callbacks */ +int amdgpu_dm_register_outbox_irq_handlers(struct amdgpu_device *adev) +{ + struct dc *dc = adev->dm.dc; + struct common_irq_params *c_irq_params; + struct dc_interrupt_params int_params = {0}; + int r, i; + + int_params.requested_polarity = INTERRUPT_POLARITY_DEFAULT; + int_params.current_polarity = INTERRUPT_POLARITY_DEFAULT; + + r = amdgpu_irq_add_id(adev, SOC15_IH_CLIENTID_DCE, DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT, + &adev->dmub_outbox_irq); + if (r) { + drm_err(adev_to_drm(adev), "Failed to add outbox irq id!\n"); + return r; + } + + if (dc->ctx->dmub_srv) { + i = DCN_1_0__SRCID__DMCUB_OUTBOX_LOW_PRIORITY_READY_INT; + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = + dc_interrupt_to_irq_source(dc, i, 0); + + c_irq_params = &adev->dm.dmub_outbox_params[0]; + + c_irq_params->adev = adev; + c_irq_params->irq_src = int_params.irq_source; + + if (!amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_dmub_outbox1_low_irq, c_irq_params)) + return -ENOMEM; + } + + return 0; +} diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h index 4f6b58f4f90d..bccb5d354a9f 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.h @@ -27,6 +27,14 @@ #include "irq_types.h" /* DAL irq definitions */ +struct amdgpu_device; +struct amdgpu_crtc; +struct amdgpu_display_manager; +struct dc_sink; +struct hpd_rx_irq_offload_work_queue; +struct work_struct; +enum dmub_notification_type; + /* * Display Manager IRQ-related interfaces (for use by DAL). */ @@ -101,4 +109,23 @@ void amdgpu_dm_irq_suspend(struct amdgpu_device *adev); void amdgpu_dm_irq_resume_early(struct amdgpu_device *adev); void amdgpu_dm_irq_resume_late(struct amdgpu_device *adev); +/* HPD handling */ +struct hpd_rx_irq_offload_work_queue *amdgpu_dm_hpd_rx_irq_create_workqueue(struct amdgpu_device *adev); +void amdgpu_dm_hpd_rx_irq_work_suspend(struct amdgpu_display_manager *dm); +int amdgpu_dm_register_hpd_handlers(struct amdgpu_device *adev); +void amdgpu_dm_hdmi_hpd_debounce_work(struct work_struct *work); + +/* IRQ handlers */ +struct amdgpu_crtc *amdgpu_dm_get_crtc_by_otg_inst(struct amdgpu_device *adev, + int otg_inst); +int amdgpu_dm_dce110_register_irq_handlers(struct amdgpu_device *adev); +int amdgpu_dm_dcn10_register_irq_handlers(struct amdgpu_device *adev); +int amdgpu_dm_register_outbox_irq_handlers(struct amdgpu_device *adev); + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +enum dc_irq_source amdgpu_dm_hpd_to_dal_irq_source(unsigned int type); +bool are_sinks_equal(const struct dc_sink *sink1, const struct dc_sink *sink2); +const char *dmub_notification_type_str(enum dmub_notification_type e); +#endif + #endif /* __AMDGPU_DM_IRQ_H__ */ 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 index 4b2864375105..1f910a6a00c0 100644 --- 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 @@ -10,6 +10,7 @@ #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 diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c index b3af7445b457..0546efea5de1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c @@ -34,16 +34,17 @@ #include "dm_services.h" #include "amdgpu.h" #include "amdgpu_dm.h" +#include "dmub_cmd.h" #include "amdgpu_dm_mst_types.h" #include "amdgpu_dm_hdcp.h" #include "dc.h" #include "dm_helpers.h" +#include "amdgpu_dm_kunit_helpers.h" #include "ddc_service_types.h" #include "dpcd_defs.h" -#include "dmub_cmd.h" #if defined(CONFIG_DEBUG_FS) #include "amdgpu_dm_debugfs.h" #endif @@ -53,10 +54,53 @@ #define PEAK_FACTOR_X1000 1006 /* + * Translate a failed AUX transaction's operation result into an errno-style + * return value. @result is returned unchanged for AUX_RET_SUCCESS. + */ +STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer_result(ssize_t result, + enum aux_return_code_type operation_result) +{ + switch (operation_result) { + case AUX_RET_SUCCESS: + break; + case AUX_RET_ERROR_HPD_DISCON: + case AUX_RET_ERROR_UNKNOWN: + case AUX_RET_ERROR_INVALID_OPERATION: + case AUX_RET_ERROR_PROTOCOL_ERROR: + result = -EIO; + break; + case AUX_RET_ERROR_INVALID_REPLY: + case AUX_RET_ERROR_ENGINE_ACQUIRE: + result = -EBUSY; + break; + case AUX_RET_ERROR_TIMEOUT: + result = -ETIMEDOUT; + break; + } + + return result; +} +EXPORT_IF_KUNIT(dm_dp_aux_transfer_result); + +/* + * Derive the AUX payload transaction flags from a DP AUX request field. + */ +STATIC_IFN_KUNIT void dm_dp_aux_fill_payload_flags(u8 request, + struct aux_payload *payload) +{ + payload->i2c_over_aux = (request & DP_AUX_NATIVE_WRITE) == 0; + payload->write = (request & DP_AUX_I2C_READ) == 0; + payload->mot = (request & DP_AUX_I2C_MOT) != 0; + payload->write_status_update = + (request & DP_AUX_I2C_WRITE_STATUS_UPDATE) != 0; +} +EXPORT_IF_KUNIT(dm_dp_aux_fill_payload_flags); + +/* * This function handles both native AUX and I2C-Over-AUX transactions. */ -static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, - struct drm_dp_aux_msg *msg) +STATIC_IFN_KUNIT ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) { ssize_t result = 0; struct aux_payload payload; @@ -72,11 +116,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, payload.data = msg->buffer; payload.length = msg->size; payload.reply = &msg->reply; - payload.i2c_over_aux = (msg->request & DP_AUX_NATIVE_WRITE) == 0; - payload.write = (msg->request & DP_AUX_I2C_READ) == 0; - payload.mot = (msg->request & DP_AUX_I2C_MOT) != 0; - payload.write_status_update = - (msg->request & DP_AUX_I2C_WRITE_STATUS_UPDATE) != 0; + dm_dp_aux_fill_payload_flags(msg->request, &payload); payload.defer_delay = 0; if (payload.write) { @@ -116,23 +156,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, } if (result < 0) { - switch (operation_result) { - case AUX_RET_SUCCESS: - break; - case AUX_RET_ERROR_HPD_DISCON: - case AUX_RET_ERROR_UNKNOWN: - case AUX_RET_ERROR_INVALID_OPERATION: - case AUX_RET_ERROR_PROTOCOL_ERROR: - result = -EIO; - break; - case AUX_RET_ERROR_INVALID_REPLY: - case AUX_RET_ERROR_ENGINE_ACQUIRE: - result = -EBUSY; - break; - case AUX_RET_ERROR_TIMEOUT: - result = -ETIMEDOUT; - break; - } + result = dm_dp_aux_transfer_result(result, operation_result); drm_dbg_dp(adev_to_drm(adev), "DP AUX transfer fail:%d\n", operation_result); } @@ -143,6 +167,7 @@ static ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, return result; } +EXPORT_IF_KUNIT(dm_dp_aux_transfer); static void dm_dp_mst_connector_destroy(struct drm_connector *connector) @@ -183,7 +208,7 @@ amdgpu_dm_mst_connector_late_register(struct drm_connector *connector) } -static inline void +STATIC_IFN_KUNIT void amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector) { aconnector->drm_edid = NULL; @@ -192,6 +217,7 @@ amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector aconnector->mst_local_bw = 0; aconnector->vc_full_pbn = 0; } +EXPORT_IF_KUNIT(amdgpu_dm_mst_reset_mst_connector_setting); static void amdgpu_dm_mst_connector_early_unregister(struct drm_connector *connector) @@ -248,6 +274,7 @@ bool needs_dsc_aux_workaround(struct dc_link *link) return false; } +EXPORT_IF_KUNIT(needs_dsc_aux_workaround); #if defined(CONFIG_DRM_AMD_DC_FP) static bool is_synaptics_cascaded_panamera(struct dc_link *link, struct drm_dp_mst_port *port) @@ -311,7 +338,7 @@ static bool validate_dsc_caps_on_connector(struct amdgpu_dm_connector *aconnecto } #endif -static bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector) +STATIC_IFN_KUNIT bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector) { union dp_downstream_port_present ds_port_present; @@ -329,8 +356,9 @@ static bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnect return true; } +EXPORT_IF_KUNIT(retrieve_downstream_port_device); -static bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector) +STATIC_IFN_KUNIT bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector) { struct drm_connector *connector = &aconnector->base; struct drm_dp_mst_port *port = aconnector->mst_output_port; @@ -357,15 +385,13 @@ static bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector return true; } +EXPORT_IF_KUNIT(retrieve_branch_specific_data); static int dm_dp_mst_get_modes(struct drm_connector *connector) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); int ret = 0; - if (!aconnector) - return drm_add_edid_modes(connector, NULL); - if (!aconnector->drm_edid) { const struct drm_edid *drm_edid; @@ -456,7 +482,7 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector) * plugged back with same display index, its hdcp properties * will be retrieved from hdcp_work within dm_dp_mst_get_modes */ - if (aconnector->dc_sink && connector->state) { + if (connector->state) { struct drm_device *dev = connector->dev; struct amdgpu_device *adev = drm_to_adev(dev); @@ -472,20 +498,18 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector) } } - if (aconnector->dc_sink) { - amdgpu_dm_update_freesync_caps( - connector, aconnector->drm_edid, true); + amdgpu_dm_update_freesync_caps( + connector, aconnector->drm_edid, true); #if defined(CONFIG_DRM_AMD_DC_FP) - if (!validate_dsc_caps_on_connector(aconnector)) - memset(&aconnector->dc_sink->dsc_caps, - 0, sizeof(aconnector->dc_sink->dsc_caps)); + if (!validate_dsc_caps_on_connector(aconnector)) + memset(&aconnector->dc_sink->dsc_caps, + 0, sizeof(aconnector->dc_sink->dsc_caps)); #endif - if (!retrieve_downstream_port_device(aconnector)) - memset(&aconnector->mst_downstream_port_present, - 0, sizeof(aconnector->mst_downstream_port_present)); - } + if (!retrieve_downstream_port_device(aconnector)) + memset(&aconnector->mst_downstream_port_present, + 0, sizeof(aconnector->mst_downstream_port_present)); } drm_edid_connector_update(&aconnector->base, aconnector->drm_edid); @@ -495,7 +519,7 @@ static int dm_dp_mst_get_modes(struct drm_connector *connector) return ret; } -static struct drm_encoder * +STATIC_IFN_KUNIT struct drm_encoder * dm_mst_atomic_best_encoder(struct drm_connector *connector, struct drm_atomic_commit *state) { @@ -506,8 +530,9 @@ dm_mst_atomic_best_encoder(struct drm_connector *connector, return &adev->dm.mst_encoders[acrtc->crtc_id].base; } +EXPORT_IF_KUNIT(dm_mst_atomic_best_encoder); -static int +STATIC_IFN_KUNIT int dm_dp_mst_detect(struct drm_connector *connector, struct drm_modeset_acquire_ctx *ctx, bool force) { @@ -577,9 +602,10 @@ dm_dp_mst_detect(struct drm_connector *connector, return connection_status; } +EXPORT_IF_KUNIT(dm_dp_mst_detect); -static int dm_dp_mst_atomic_check(struct drm_connector *connector, - struct drm_atomic_commit *state) +STATIC_IFN_KUNIT int dm_dp_mst_atomic_check(struct drm_connector *connector, + struct drm_atomic_commit *state) { struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector); struct drm_dp_mst_topology_mgr *mst_mgr = &aconnector->mst_root->mst_mgr; @@ -587,6 +613,7 @@ static int dm_dp_mst_atomic_check(struct drm_connector *connector, return drm_dp_atomic_release_time_slots(state, mst_mgr, mst_port); } +EXPORT_IF_KUNIT(dm_dp_mst_atomic_check); static const struct drm_connector_helper_funcs dm_dp_mst_connector_helper_funcs = { .get_modes = dm_dp_mst_get_modes, @@ -627,6 +654,7 @@ dm_dp_create_fake_mst_encoders(struct amdgpu_device *adev) drm_encoder_helper_add(encoder, &amdgpu_dm_encoder_helper_funcs); } } +EXPORT_IF_KUNIT(dm_dp_create_fake_mst_encoders); static struct drm_connector * dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, @@ -711,6 +739,44 @@ dm_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, return connector; } +/* + * Select the ESI[1] mask used to filter the MST sideband ready bits for a + * given message-ready event type. + */ +STATIC_IFN_KUNIT u8 dm_mst_msg_ready_mask(enum mst_msg_ready_type msg_rdy_type) +{ + switch (msg_rdy_type) { + case DOWN_REP_MSG_RDY_EVENT: + /* Only handle DOWN_REP_MSG_RDY case*/ + return DP_DOWN_REP_MSG_RDY; + case UP_REQ_MSG_RDY_EVENT: + /* Only handle UP_REQ_MSG_RDY case*/ + return DP_UP_REQ_MSG_RDY; + default: + /* Handle both cases*/ + return DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY; + } +} +EXPORT_IF_KUNIT(dm_mst_msg_ready_mask); + +/* + * Select the DPCD ESI address and read length based on the DPCD revision. + */ +STATIC_IFN_KUNIT void dm_mst_select_esi_dpcd(u8 dpcd_rev, int *dpcd_addr, + u8 *dpcd_bytes_to_read) +{ + if (dpcd_rev < 0x12) { + *dpcd_bytes_to_read = DP_LANE0_1_STATUS - DP_SINK_COUNT; + /* DPCD 0x200 - 0x201 for downstream IRQ */ + *dpcd_addr = DP_SINK_COUNT; + } else { + *dpcd_bytes_to_read = DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI; + /* DPCD 0x2002 - 0x2005 for downstream IRQ */ + *dpcd_addr = DP_SINK_COUNT_ESI; + } +} +EXPORT_IF_KUNIT(dm_mst_select_esi_dpcd); + void dm_handle_mst_sideband_msg_ready_event( struct drm_dp_mst_topology_mgr *mgr, enum mst_msg_ready_type msg_rdy_type) @@ -729,15 +795,8 @@ void dm_handle_mst_sideband_msg_ready_event( const struct dc_link_status *link_status = dc_link_get_status(aconnector->dc_link); - if (link_status->dpcd_caps->dpcd_rev.raw < 0x12) { - dpcd_bytes_to_read = DP_LANE0_1_STATUS - DP_SINK_COUNT; - /* DPCD 0x200 - 0x201 for downstream IRQ */ - dpcd_addr = DP_SINK_COUNT; - } else { - dpcd_bytes_to_read = DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI; - /* DPCD 0x2002 - 0x2005 for downstream IRQ */ - dpcd_addr = DP_SINK_COUNT_ESI; - } + dm_mst_select_esi_dpcd(link_status->dpcd_caps->dpcd_rev.raw, &dpcd_addr, + &dpcd_bytes_to_read); mutex_lock(&aconnector->handle_mst_msg_ready); @@ -759,20 +818,7 @@ void dm_handle_mst_sideband_msg_ready_event( DRM_DEBUG_DRIVER("ESI %02x %02x %02x\n", esi[0], esi[1], esi[2]); - switch (msg_rdy_type) { - case DOWN_REP_MSG_RDY_EVENT: - /* Only handle DOWN_REP_MSG_RDY case*/ - esi[1] &= DP_DOWN_REP_MSG_RDY; - break; - case UP_REQ_MSG_RDY_EVENT: - /* Only handle UP_REQ_MSG_RDY case*/ - esi[1] &= DP_UP_REQ_MSG_RDY; - break; - default: - /* Handle both cases*/ - esi[1] &= (DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY); - break; - } + esi[1] &= dm_mst_msg_ready_mask(msg_rdy_type); if (!esi[1]) break; @@ -814,6 +860,7 @@ void dm_handle_mst_sideband_msg_ready_event( if (process_count == max_process_count) DRM_DEBUG_DRIVER("Loop exceeded max iterations\n"); } +EXPORT_IF_KUNIT(dm_handle_mst_sideband_msg_ready_event); static void dm_handle_mst_down_rep_msg_ready(struct drm_dp_mst_topology_mgr *mgr) { @@ -869,6 +916,7 @@ uint32_t dm_mst_get_pbn_divider(struct dc_link *link) return dfixed_const(pbn_div_x100) / 100; } +EXPORT_IF_KUNIT(dm_mst_get_pbn_divider); struct dsc_mst_fairness_params { struct dc_crtc_timing *timing; @@ -1787,7 +1835,7 @@ int pre_validate_dsc(struct drm_atomic_commit *state, dm_old_crtc_state = to_dm_crtc_state(state->crtcs[ind].old_state); local_dc_state->streams[i] = - create_validate_stream_for_sink(connector, + amdgpu_dm_create_validate_stream_for_sink(connector, &state->crtcs[ind].new_state->mode, dm_new_conn_state, dm_old_crtc_state->stream); @@ -2066,3 +2114,4 @@ enum dc_status dm_dp_mst_is_port_support_mode( #endif return DC_OK; } +EXPORT_IF_KUNIT(dm_dp_mst_is_port_support_mode); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h index 0e8eef5bdb74..fecf108a9216 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.h @@ -57,8 +57,15 @@ enum mst_msg_ready_type { DOWN_OR_UP_MSG_RDY_EVENT = 3 }; +struct amdgpu_device; struct amdgpu_display_manager; struct amdgpu_dm_connector; +struct aux_payload; +struct dc_state; +struct dc_stream_state; +struct dm_atomic_state; +struct drm_atomic_commit; +struct drm_dp_mst_topology_mgr; uint32_t dm_mst_get_pbn_divider(struct dc_link *link); @@ -94,4 +101,22 @@ enum dc_status dm_dp_mst_is_port_support_mode( struct amdgpu_dm_connector *aconnector, struct dc_stream_state *stream); +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +void amdgpu_dm_mst_reset_mst_connector_setting(struct amdgpu_dm_connector *aconnector); +bool retrieve_downstream_port_device(struct amdgpu_dm_connector *aconnector); +bool retrieve_branch_specific_data(struct amdgpu_dm_connector *aconnector); +ssize_t dm_dp_aux_transfer_result(ssize_t result, + enum aux_return_code_type operation_result); +void dm_dp_aux_fill_payload_flags(u8 request, struct aux_payload *payload); +ssize_t dm_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg); +u8 dm_mst_msg_ready_mask(enum mst_msg_ready_type msg_rdy_type); +void dm_mst_select_esi_dpcd(u8 dpcd_rev, int *dpcd_addr, u8 *dpcd_bytes_to_read); +struct drm_encoder *dm_mst_atomic_best_encoder(struct drm_connector *connector, + struct drm_atomic_commit *state); +int dm_dp_mst_atomic_check(struct drm_connector *connector, + struct drm_atomic_commit *state); +int dm_dp_mst_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, bool force); +#endif + #endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c index c7f8e08feaf4..1b564cfe2120 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.c @@ -37,6 +37,7 @@ #include "amdgpu_display.h" #include "amdgpu_dm_trace.h" #include "amdgpu_dm_plane.h" +#include "amdgpu_dm_kunit_helpers.h" #include "amdgpu_dm_colorop.h" #include "gc/gc_11_0_0_offset.h" #include "gc/gc_11_0_0_sh_mask.h" @@ -97,6 +98,7 @@ const struct drm_format_info *amdgpu_dm_plane_get_format_info(u32 pixel_format, { return amdgpu_lookup_format_info(pixel_format, modifier); } +EXPORT_IF_KUNIT(amdgpu_dm_plane_get_format_info); void amdgpu_dm_plane_fill_blending_from_plane_state(const struct drm_plane_state *plane_state, bool *per_pixel_alpha, bool *pre_multiplied_alpha, @@ -135,12 +137,24 @@ void amdgpu_dm_plane_fill_blending_from_plane_state(const struct drm_plane_state } if (plane_state->alpha < 0xffff) { + struct amdgpu_device *adev = drm_to_adev(plane_state->plane->dev); *global_alpha = true; - *global_alpha_value = plane_state->alpha >> 8; + /* + * DCN 4.2 uses a 12-bit MPCC_GLOBAL_ALPHA field, while + * other ASICs use an 8-bit field. The DRM plane alpha is + * 16-bit, so scale it down to the width the hardware expects. + */ + if (amdgpu_ip_version(adev, DCE_HWIP, 0) == IP_VERSION(4, 2, 0)) + *global_alpha_value = plane_state->alpha >> 4; + else + *global_alpha_value = plane_state->alpha >> 8; + } } +EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_blending_from_plane_state); -static void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *size, uint64_t *cap, uint64_t mod) +STATIC_IFN_KUNIT void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *size, + uint64_t *cap, uint64_t mod) { if (!*mods) return; @@ -164,27 +178,29 @@ static void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *size, uint64 (*mods)[*size] = mod; *size += 1; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_add_modifier); -static bool amdgpu_dm_plane_modifier_has_dcc(uint64_t modifier) +STATIC_IFN_KUNIT bool amdgpu_dm_plane_modifier_has_dcc(uint64_t modifier) { return IS_AMD_FMT_MOD(modifier) && AMD_FMT_MOD_GET(DCC, modifier); } +EXPORT_IF_KUNIT(amdgpu_dm_plane_modifier_has_dcc); -static unsigned int amdgpu_dm_plane_modifier_gfx9_swizzle_mode(uint64_t modifier) +STATIC_IFN_KUNIT unsigned int amdgpu_dm_plane_modifier_gfx9_swizzle_mode(uint64_t modifier) { if (modifier == DRM_FORMAT_MOD_LINEAR) return 0; return AMD_FMT_MOD_GET(TILE, modifier); } +EXPORT_IF_KUNIT(amdgpu_dm_plane_modifier_gfx9_swizzle_mode); -static void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_info *tiling_info, - uint64_t tiling_flags) +STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_info *tiling_info, + uint64_t tiling_flags) { /* Fill GFX8 params */ if (AMDGPU_TILING_GET(tiling_flags, ARRAY_MODE) == DC_ARRAY_2D_TILED_THIN1) { unsigned int bankw, bankh, mtaspect, tile_split, num_banks; - bankw = AMDGPU_TILING_GET(tiling_flags, BANK_WIDTH); bankh = AMDGPU_TILING_GET(tiling_flags, BANK_HEIGHT); mtaspect = AMDGPU_TILING_GET(tiling_flags, MACRO_TILE_ASPECT); @@ -210,9 +226,10 @@ static void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_in tiling_info->gfx8.pipe_config = AMDGPU_TILING_GET(tiling_flags, PIPE_CONFIG); } +EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags); -static void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev, - struct dc_tiling_info *tiling_info) +STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev, + struct dc_tiling_info *tiling_info) { /* Fill GFX9 params */ tiling_info->gfx9.num_pipes = @@ -231,10 +248,11 @@ static void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgp if (amdgpu_ip_version(adev, GC_HWIP, 0) >= IP_VERSION(10, 3, 0)) tiling_info->gfx9.num_pkrs = adev->gfx.config.gb_addr_config_fields.num_pkrs; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_tiling_info_from_device); -static void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amdgpu_device *adev, - struct dc_tiling_info *tiling_info, - uint64_t modifier) +STATIC_IFN_KUNIT void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amdgpu_device *adev, + struct dc_tiling_info *tiling_info, + uint64_t modifier) { unsigned int mod_bank_xor_bits = AMD_FMT_MOD_GET(BANK_XOR_BITS, modifier); unsigned int mod_pipe_xor_bits = AMD_FMT_MOD_GET(PIPE_XOR_BITS, modifier); @@ -259,14 +277,15 @@ static void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amd /* for DCC we know it isn't rb aligned, so rb_per_se doesn't matter. */ } } - -static int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev, - const enum surface_pixel_format format, - const enum dc_rotation_angle rotation, - const struct dc_tiling_info *tiling_info, - const struct dc_plane_dcc_param *dcc, - const struct dc_plane_address *address, - const struct plane_size *plane_size) +EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier); + +STATIC_IFN_KUNIT int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev, + const enum surface_pixel_format format, + const enum dc_rotation_angle rotation, + const struct dc_tiling_info *tiling_info, + const struct dc_plane_dcc_param *dcc, + const struct dc_plane_address *address, + const struct plane_size *plane_size) { struct dc *dc = adev->dm.dc; struct dc_dcc_surface_param input; @@ -307,15 +326,16 @@ static int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev, return 0; } - -static int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev, - const struct amdgpu_framebuffer *afb, - const enum surface_pixel_format format, - const enum dc_rotation_angle rotation, - const struct plane_size *plane_size, - struct dc_tiling_info *tiling_info, - struct dc_plane_dcc_param *dcc, - struct dc_plane_address *address) +EXPORT_IF_KUNIT(amdgpu_dm_plane_validate_dcc); + +STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev, + const struct amdgpu_framebuffer *afb, + const enum surface_pixel_format format, + const enum dc_rotation_angle rotation, + const struct plane_size *plane_size, + struct dc_tiling_info *tiling_info, + struct dc_plane_dcc_param *dcc, + struct dc_plane_address *address) { const uint64_t modifier = afb->base.modifier; int ret = 0; @@ -358,15 +378,16 @@ static int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdg return ret; } - -static int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amdgpu_device *adev, - const struct amdgpu_framebuffer *afb, - const enum surface_pixel_format format, - const enum dc_rotation_angle rotation, - const struct plane_size *plane_size, - struct dc_tiling_info *tiling_info, - struct dc_plane_dcc_param *dcc, - struct dc_plane_address *address) +EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers); + +STATIC_IFN_KUNIT int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amdgpu_device *adev, + const struct amdgpu_framebuffer *afb, + const enum surface_pixel_format format, + const enum dc_rotation_angle rotation, + const struct plane_size *plane_size, + struct dc_tiling_info *tiling_info, + struct dc_plane_dcc_param *dcc, + struct dc_plane_address *address) { const uint64_t modifier = afb->base.modifier; int ret = 0; @@ -398,6 +419,7 @@ static int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amd return ret; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers); static void amdgpu_dm_plane_add_gfx10_1_modifiers(const struct amdgpu_device *adev, uint64_t **mods, @@ -724,7 +746,7 @@ static void amdgpu_dm_plane_add_gfx12_modifiers(struct amdgpu_device *adev, } -static int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev, unsigned int plane_type, uint64_t **mods) +STATIC_IFN_KUNIT int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev, unsigned int plane_type, uint64_t **mods) { uint64_t size = 0, capacity = 128; *mods = NULL; @@ -777,10 +799,11 @@ static int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev, unsig return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_get_plane_modifiers); -static int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane, - const struct dc_plane_cap *plane_cap, - uint32_t *formats, int max_formats) +STATIC_IFN_KUNIT int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane, + const struct dc_plane_cap *plane_cap, + uint32_t *formats, int max_formats) { int i, num_formats = 0; @@ -836,6 +859,7 @@ static int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane, return num_formats; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_get_plane_formats); int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev, const struct amdgpu_framebuffer *afb, @@ -922,6 +946,7 @@ int amdgpu_dm_plane_fill_plane_buffer_attributes(struct amdgpu_device *adev, return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_plane_buffer_attributes); static int amdgpu_dm_plane_helper_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) @@ -1042,9 +1067,9 @@ static void amdgpu_dm_plane_helper_cleanup_fb(struct drm_plane *plane, amdgpu_bo_unref(&rbo); } -static void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev, - struct drm_framebuffer *fb, - int *min_downscale, int *max_upscale) +STATIC_IFN_KUNIT void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev, + struct drm_framebuffer *fb, + int *min_downscale, int *max_upscale) { struct amdgpu_device *adev = drm_to_adev(dev); struct dc *dc = adev->dm.dc; @@ -1088,6 +1113,7 @@ static void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev, if (*min_downscale == 1) *min_downscale = 1000; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_get_min_max_dc_plane_scaling); int amdgpu_dm_plane_helper_check_state(struct drm_plane_state *state, struct drm_crtc_state *new_crtc_state) @@ -1142,6 +1168,7 @@ int amdgpu_dm_plane_helper_check_state(struct drm_plane_state *state, return drm_atomic_helper_check_plane_state( state, new_crtc_state, min_scale, max_scale, true, true); } +EXPORT_IF_KUNIT(amdgpu_dm_plane_helper_check_state); int amdgpu_dm_plane_fill_dc_scaling_info(struct amdgpu_device *adev, const struct drm_plane_state *state, @@ -1225,6 +1252,7 @@ int amdgpu_dm_plane_fill_dc_scaling_info(struct amdgpu_device *adev, return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_fill_dc_scaling_info); static int amdgpu_dm_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_commit *state) @@ -1343,6 +1371,7 @@ int amdgpu_dm_plane_get_cursor_position(struct drm_plane *plane, struct drm_crtc return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_get_cursor_position); void amdgpu_dm_plane_handle_cursor_update(struct drm_plane *plane, struct drm_plane_state *old_plane_state) @@ -1488,17 +1517,15 @@ static const struct drm_plane_helper_funcs dm_primary_plane_helper_funcs = { static void amdgpu_dm_plane_drm_plane_reset(struct drm_plane *plane) { - struct dm_plane_state *amdgpu_state = NULL; - - if (plane->state) - plane->funcs->atomic_destroy_state(plane, plane->state); + struct dm_plane_state *amdgpu_state; amdgpu_state = kzalloc_obj(*amdgpu_state); - WARN_ON(amdgpu_state == NULL); - if (!amdgpu_state) return; + if (plane->state) + plane->funcs->atomic_destroy_state(plane, plane->state); + __drm_atomic_helper_plane_reset(plane, &amdgpu_state->base); amdgpu_state->degamma_tf = AMDGPU_TRANSFER_FUNCTION_DEFAULT; amdgpu_state->hdr_mult = AMDGPU_HDR_MULT_DEFAULT; @@ -1546,9 +1573,9 @@ static struct drm_plane_state *amdgpu_dm_plane_drm_plane_duplicate_state(struct return &dm_plane_state->base; } -static bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane, - uint32_t format, - uint64_t modifier) +STATIC_IFN_KUNIT bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane, + uint32_t format, + uint64_t modifier) { struct amdgpu_device *adev = drm_to_adev(plane->dev); const struct drm_format_info *info = drm_format_info(format); @@ -1607,6 +1634,7 @@ static bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane, return true; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_format_mod_supported); static void amdgpu_dm_plane_drm_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state) @@ -1982,4 +2010,5 @@ bool amdgpu_dm_plane_is_video_format(uint32_t format) return false; } +EXPORT_IF_KUNIT(amdgpu_dm_plane_is_video_format); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h index ea2619b507db..911fb2d73e22 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_plane.h @@ -28,6 +28,8 @@ #define __AMDGPU_DM_PLANE_H__ #include "dc.h" +#include <drm/drm_plane.h> +#include "amdgpu.h" int amdgpu_dm_plane_get_cursor_position(struct drm_plane *plane, struct drm_crtc *crtc, struct dc_cursor_position *position); @@ -65,4 +67,53 @@ void amdgpu_dm_plane_fill_blending_from_plane_state(const struct drm_plane_state bool *global_alpha, int *global_alpha_value); bool amdgpu_dm_plane_is_video_format(uint32_t format); + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +void amdgpu_dm_plane_add_modifier(uint64_t **mods, uint64_t *size, + uint64_t *cap, uint64_t mod); +void amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(struct dc_tiling_info *tiling_info, + uint64_t tiling_flags); +void amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(const struct amdgpu_device *adev, + struct dc_tiling_info *tiling_info); +void amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(const struct amdgpu_device *adev, + struct dc_tiling_info *tiling_info, + uint64_t modifier); +int amdgpu_dm_plane_validate_dcc(struct amdgpu_device *adev, + const enum surface_pixel_format format, + const enum dc_rotation_angle rotation, + const struct dc_tiling_info *tiling_info, + const struct dc_plane_dcc_param *dcc, + const struct dc_plane_address *address, + const struct plane_size *plane_size); +bool amdgpu_dm_plane_modifier_has_dcc(uint64_t modifier); +unsigned int amdgpu_dm_plane_modifier_gfx9_swizzle_mode(uint64_t modifier); +int amdgpu_dm_plane_get_plane_modifiers(struct amdgpu_device *adev, + unsigned int plane_type, uint64_t **mods); +int amdgpu_dm_plane_get_plane_formats(const struct drm_plane *plane, + const struct dc_plane_cap *plane_cap, + uint32_t *formats, int max_formats); +int amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers(struct amdgpu_device *adev, + const struct amdgpu_framebuffer *afb, + const enum surface_pixel_format format, + const enum dc_rotation_angle rotation, + const struct plane_size *plane_size, + struct dc_tiling_info *tiling_info, + struct dc_plane_dcc_param *dcc, + struct dc_plane_address *address); +int amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers(struct amdgpu_device *adev, + const struct amdgpu_framebuffer *afb, + const enum surface_pixel_format format, + const enum dc_rotation_angle rotation, + const struct plane_size *plane_size, + struct dc_tiling_info *tiling_info, + struct dc_plane_dcc_param *dcc, + struct dc_plane_address *address); +bool amdgpu_dm_plane_format_mod_supported(struct drm_plane *plane, + uint32_t format, + uint64_t modifier); +void amdgpu_dm_plane_get_min_max_dc_plane_scaling(struct drm_device *dev, + struct drm_framebuffer *fb, + int *min_downscale, + int *max_upscale); +#endif #endif 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 2cdb8fea504a..0d2e5294d062 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 @@ -33,73 +33,67 @@ #include "amdgpu_dm_irq.h" #include "amdgpu_pm.h" #include "dm_pp_smu.h" +#include "amdgpu_dm_kunit_helpers.h" +#include "amdgpu_dm_pp_smu.h" -bool dm_pp_apply_display_requirements( - const struct dc_context *ctx, +STATIC_IFN_KUNIT void build_pm_display_cfg( + struct amd_pp_display_configuration *pm_display_cfg, const struct dm_pp_display_configuration *pp_display_cfg) { - struct amdgpu_device *adev = ctx->driver_context; int i; - if (adev->pm.dpm_enabled) { + memset(pm_display_cfg, 0, sizeof(*pm_display_cfg)); - memset(&adev->pm.pm_display_cfg, 0, - sizeof(adev->pm.pm_display_cfg)); + pm_display_cfg->cpu_cc6_disable = pp_display_cfg->cpu_cc6_disable; + pm_display_cfg->cpu_pstate_disable = pp_display_cfg->cpu_pstate_disable; + pm_display_cfg->cpu_pstate_separation_time = pp_display_cfg->cpu_pstate_separation_time; + pm_display_cfg->nb_pstate_switch_disable = pp_display_cfg->nb_pstate_switch_disable; - adev->pm.pm_display_cfg.cpu_cc6_disable = - pp_display_cfg->cpu_cc6_disable; + pm_display_cfg->num_display = pp_display_cfg->display_count; + pm_display_cfg->num_path_including_non_display = pp_display_cfg->display_count; - adev->pm.pm_display_cfg.cpu_pstate_disable = - pp_display_cfg->cpu_pstate_disable; + pm_display_cfg->min_core_set_clock = pp_display_cfg->min_engine_clock_khz/10; + pm_display_cfg->min_core_set_clock_in_sr = + pp_display_cfg->min_engine_clock_deep_sleep_khz/10; + pm_display_cfg->min_mem_set_clock = pp_display_cfg->min_memory_clock_khz/10; - adev->pm.pm_display_cfg.cpu_pstate_separation_time = - pp_display_cfg->cpu_pstate_separation_time; + pm_display_cfg->min_dcef_deep_sleep_set_clk = + pp_display_cfg->min_engine_clock_deep_sleep_khz/10; + pm_display_cfg->min_dcef_set_clk = pp_display_cfg->min_dcfclock_khz/10; - adev->pm.pm_display_cfg.nb_pstate_switch_disable = - pp_display_cfg->nb_pstate_switch_disable; + pm_display_cfg->multi_monitor_in_sync = pp_display_cfg->all_displays_in_sync; + pm_display_cfg->min_vblank_time = pp_display_cfg->avail_mclk_switch_time_us; - adev->pm.pm_display_cfg.num_display = - pp_display_cfg->display_count; - adev->pm.pm_display_cfg.num_path_including_non_display = - pp_display_cfg->display_count; + pm_display_cfg->display_clk = pp_display_cfg->disp_clk_khz/10; - adev->pm.pm_display_cfg.min_core_set_clock = - pp_display_cfg->min_engine_clock_khz/10; - adev->pm.pm_display_cfg.min_core_set_clock_in_sr = - pp_display_cfg->min_engine_clock_deep_sleep_khz/10; - adev->pm.pm_display_cfg.min_mem_set_clock = - pp_display_cfg->min_memory_clock_khz/10; + pm_display_cfg->dce_tolerable_mclk_in_active_latency = + pp_display_cfg->avail_mclk_switch_time_in_disp_active_us; - adev->pm.pm_display_cfg.min_dcef_deep_sleep_set_clk = - pp_display_cfg->min_engine_clock_deep_sleep_khz/10; - adev->pm.pm_display_cfg.min_dcef_set_clk = - pp_display_cfg->min_dcfclock_khz/10; + pm_display_cfg->crtc_index = pp_display_cfg->crtc_index; + pm_display_cfg->line_time_in_us = pp_display_cfg->line_time_in_us; - adev->pm.pm_display_cfg.multi_monitor_in_sync = - pp_display_cfg->all_displays_in_sync; - adev->pm.pm_display_cfg.min_vblank_time = - pp_display_cfg->avail_mclk_switch_time_us; + pm_display_cfg->vrefresh = pp_display_cfg->disp_configs[0].v_refresh; + pm_display_cfg->crossfire_display_index = -1; + pm_display_cfg->min_bus_bandwidth = 0; - adev->pm.pm_display_cfg.display_clk = - pp_display_cfg->disp_clk_khz/10; - - adev->pm.pm_display_cfg.dce_tolerable_mclk_in_active_latency = - pp_display_cfg->avail_mclk_switch_time_in_disp_active_us; + for (i = 0; i < pp_display_cfg->display_count; i++) { + const struct dm_pp_single_disp_config *dc_cfg = + &pp_display_cfg->disp_configs[i]; + pm_display_cfg->displays[i].controller_id = dc_cfg->pipe_idx + 1; + pm_display_cfg->displays[i].pixel_clock = dc_cfg->pixel_clock; + } +} +EXPORT_IF_KUNIT(build_pm_display_cfg); - adev->pm.pm_display_cfg.crtc_index = pp_display_cfg->crtc_index; - adev->pm.pm_display_cfg.line_time_in_us = - pp_display_cfg->line_time_in_us; +bool dm_pp_apply_display_requirements( + const struct dc_context *ctx, + const struct dm_pp_display_configuration *pp_display_cfg) +{ + struct amdgpu_device *adev = ctx->driver_context; - adev->pm.pm_display_cfg.vrefresh = pp_display_cfg->disp_configs[0].v_refresh; - adev->pm.pm_display_cfg.crossfire_display_index = -1; - adev->pm.pm_display_cfg.min_bus_bandwidth = 0; + if (adev->pm.dpm_enabled) { - for (i = 0; i < pp_display_cfg->display_count; i++) { - const struct dm_pp_single_disp_config *dc_cfg = - &pp_display_cfg->disp_configs[i]; - adev->pm.pm_display_cfg.displays[i].controller_id = dc_cfg->pipe_idx + 1; - adev->pm.pm_display_cfg.displays[i].pixel_clock = dc_cfg->pixel_clock; - } + build_pm_display_cfg(&adev->pm.pm_display_cfg, pp_display_cfg); amdgpu_dpm_display_configuration_change(adev, &adev->pm.pm_display_cfg); @@ -108,8 +102,9 @@ bool dm_pp_apply_display_requirements( return true; } +EXPORT_IF_KUNIT(dm_pp_apply_display_requirements); -static void get_default_clock_levels( +STATIC_IFN_KUNIT void get_default_clock_levels( enum dm_pp_clock_type clk_type, struct dm_pp_clock_levels *clks) { @@ -140,8 +135,9 @@ static void get_default_clock_levels( break; } } +EXPORT_IF_KUNIT(get_default_clock_levels); -static enum amd_pp_clock_type dc_to_pp_clock_type( +STATIC_IFN_KUNIT enum amd_pp_clock_type dc_to_pp_clock_type( enum dm_pp_clock_type dm_pp_clk_type) { enum amd_pp_clock_type amd_pp_clk_type = 0; @@ -182,8 +178,9 @@ static enum amd_pp_clock_type dc_to_pp_clock_type( return amd_pp_clk_type; } +EXPORT_IF_KUNIT(dc_to_pp_clock_type); -static void pp_to_dc_clock_levels( +STATIC_IFN_KUNIT void pp_to_dc_clock_levels( const struct amd_pp_clocks *pp_clks, struct dm_pp_clock_levels *dc_clks, enum dm_pp_clock_type dc_clk_type) @@ -208,8 +205,9 @@ static void pp_to_dc_clock_levels( dc_clks->clocks_in_khz[i] = pp_clks->clock[i]; } } +EXPORT_IF_KUNIT(pp_to_dc_clock_levels); -static void pp_to_dc_clock_levels_with_latency( +STATIC_IFN_KUNIT void pp_to_dc_clock_levels_with_latency( const struct pp_clock_levels_with_latency *pp_clks, struct dm_pp_clock_levels_with_latency *clk_level_info, enum dm_pp_clock_type dc_clk_type) @@ -235,8 +233,9 @@ static void pp_to_dc_clock_levels_with_latency( clk_level_info->data[i].latency_in_us = pp_clks->data[i].latency_in_us; } } +EXPORT_IF_KUNIT(pp_to_dc_clock_levels_with_latency); -static void pp_to_dc_clock_levels_with_voltage( +STATIC_IFN_KUNIT void pp_to_dc_clock_levels_with_voltage( const struct pp_clock_levels_with_voltage *pp_clks, struct dm_pp_clock_levels_with_voltage *clk_level_info, enum dm_pp_clock_type dc_clk_type) @@ -263,6 +262,41 @@ static void pp_to_dc_clock_levels_with_voltage( clk_level_info->data[i].voltage_in_mv = pp_clks->data[i].voltage_in_mv; } } +EXPORT_IF_KUNIT(pp_to_dc_clock_levels_with_voltage); + +STATIC_IFN_KUNIT void cap_clock_levels_to_validation( + struct dm_pp_clock_levels *dc_clks, + enum dm_pp_clock_type clk_type, + const struct amd_pp_simple_clock_info *validation_clks) +{ + uint32_t i; + + /* Determine the highest non-boosted level from the Validation Clocks */ + if (clk_type == DM_PP_CLOCK_TYPE_ENGINE_CLK) { + for (i = 0; i < dc_clks->num_levels; i++) { + if (dc_clks->clocks_in_khz[i] > validation_clks->engine_max_clock) { + /* This clock is higher the validation clock. + * Than means the previous one is the highest + * non-boosted one. + */ + DRM_INFO("DM_PPLIB: reducing engine clock level from %d to %d\n", + dc_clks->num_levels, i); + dc_clks->num_levels = i > 0 ? i : 1; + break; + } + } + } else if (clk_type == DM_PP_CLOCK_TYPE_MEMORY_CLK) { + for (i = 0; i < dc_clks->num_levels; i++) { + if (dc_clks->clocks_in_khz[i] > validation_clks->memory_max_clock) { + DRM_INFO("DM_PPLIB: reducing memory clock level from %d to %d\n", + dc_clks->num_levels, i); + dc_clks->num_levels = i > 0 ? i : 1; + break; + } + } + } +} +EXPORT_IF_KUNIT(cap_clock_levels_to_validation); bool dm_pp_get_clock_levels_by_type( const struct dc_context *ctx, @@ -272,7 +306,6 @@ bool dm_pp_get_clock_levels_by_type( struct amdgpu_device *adev = ctx->driver_context; struct amd_pp_clocks pp_clks = { 0 }; struct amd_pp_simple_clock_info validation_clks = { 0 }; - uint32_t i; if (amdgpu_dpm_get_clock_by_type(adev, dc_to_pp_clock_type(clk_type), &pp_clks)) { @@ -300,33 +333,11 @@ bool dm_pp_get_clock_levels_by_type( validation_clks.engine_max_clock *= 10; validation_clks.memory_max_clock *= 10; - /* Determine the highest non-boosted level from the Validation Clocks */ - if (clk_type == DM_PP_CLOCK_TYPE_ENGINE_CLK) { - for (i = 0; i < dc_clks->num_levels; i++) { - if (dc_clks->clocks_in_khz[i] > validation_clks.engine_max_clock) { - /* This clock is higher the validation clock. - * Than means the previous one is the highest - * non-boosted one. - */ - DRM_INFO("DM_PPLIB: reducing engine clock level from %d to %d\n", - dc_clks->num_levels, i); - dc_clks->num_levels = i > 0 ? i : 1; - break; - } - } - } else if (clk_type == DM_PP_CLOCK_TYPE_MEMORY_CLK) { - for (i = 0; i < dc_clks->num_levels; i++) { - if (dc_clks->clocks_in_khz[i] > validation_clks.memory_max_clock) { - DRM_INFO("DM_PPLIB: reducing memory clock level from %d to %d\n", - dc_clks->num_levels, i); - dc_clks->num_levels = i > 0 ? i : 1; - break; - } - } - } + cap_clock_levels_to_validation(dc_clks, clk_type, &validation_clks); return true; } +EXPORT_IF_KUNIT(dm_pp_get_clock_levels_by_type); bool dm_pp_get_clock_levels_by_type_with_latency( const struct dc_context *ctx, @@ -347,6 +358,7 @@ bool dm_pp_get_clock_levels_by_type_with_latency( return true; } +EXPORT_IF_KUNIT(dm_pp_get_clock_levels_by_type_with_latency); bool dm_pp_get_clock_levels_by_type_with_voltage( const struct dc_context *ctx, @@ -367,6 +379,7 @@ bool dm_pp_get_clock_levels_by_type_with_voltage( return true; } +EXPORT_IF_KUNIT(dm_pp_get_clock_levels_by_type_with_voltage); bool dm_pp_notify_wm_clock_changes( const struct dc_context *ctx, @@ -386,6 +399,7 @@ bool dm_pp_notify_wm_clock_changes( return false; } +EXPORT_IF_KUNIT(dm_pp_notify_wm_clock_changes); bool dm_pp_apply_clock_for_voltage_request( const struct dc_context *ctx, @@ -407,26 +421,26 @@ bool dm_pp_apply_clock_for_voltage_request( return true; } +EXPORT_IF_KUNIT(dm_pp_apply_clock_for_voltage_request); -static void pp_rv_set_wm_ranges(struct pp_smu *pp, - struct pp_smu_wm_range_sets *ranges) +STATIC_IFN_KUNIT void build_wm_clock_ranges_soc15( + const struct pp_smu_wm_range_sets *ranges, + struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges) { - const struct dc_context *ctx = pp->dm; - struct amdgpu_device *adev = ctx->driver_context; - struct dm_pp_wm_sets_with_clock_ranges_soc15 wm_with_clock_ranges; - struct dm_pp_clock_range_for_dmif_wm_set_soc15 *wm_dce_clocks = wm_with_clock_ranges.wm_dmif_clocks_ranges; - struct dm_pp_clock_range_for_mcif_wm_set_soc15 *wm_soc_clocks = wm_with_clock_ranges.wm_mcif_clocks_ranges; + struct dm_pp_clock_range_for_dmif_wm_set_soc15 *wm_dce_clocks = + wm_with_clock_ranges->wm_dmif_clocks_ranges; + struct dm_pp_clock_range_for_mcif_wm_set_soc15 *wm_soc_clocks = + wm_with_clock_ranges->wm_mcif_clocks_ranges; int32_t i; - wm_with_clock_ranges.num_wm_dmif_sets = ranges->num_reader_wm_sets; - wm_with_clock_ranges.num_wm_mcif_sets = ranges->num_writer_wm_sets; + wm_with_clock_ranges->num_wm_dmif_sets = ranges->num_reader_wm_sets; + wm_with_clock_ranges->num_wm_mcif_sets = ranges->num_writer_wm_sets; - for (i = 0; i < wm_with_clock_ranges.num_wm_dmif_sets; i++) { + for (i = 0; i < wm_with_clock_ranges->num_wm_dmif_sets; i++) { if (ranges->reader_wm_sets[i].wm_inst > 3) wm_dce_clocks[i].wm_set_id = WM_SET_A; else - wm_dce_clocks[i].wm_set_id = - ranges->reader_wm_sets[i].wm_inst; + wm_dce_clocks[i].wm_set_id = ranges->reader_wm_sets[i].wm_inst; wm_dce_clocks[i].wm_max_dcfclk_clk_in_khz = ranges->reader_wm_sets[i].max_drain_clk_mhz * 1000; wm_dce_clocks[i].wm_min_dcfclk_clk_in_khz = @@ -437,12 +451,11 @@ static void pp_rv_set_wm_ranges(struct pp_smu *pp, ranges->reader_wm_sets[i].min_fill_clk_mhz * 1000; } - for (i = 0; i < wm_with_clock_ranges.num_wm_mcif_sets; i++) { + for (i = 0; i < wm_with_clock_ranges->num_wm_mcif_sets; i++) { if (ranges->writer_wm_sets[i].wm_inst > 3) wm_soc_clocks[i].wm_set_id = WM_SET_A; else - wm_soc_clocks[i].wm_set_id = - ranges->writer_wm_sets[i].wm_inst; + wm_soc_clocks[i].wm_set_id = ranges->writer_wm_sets[i].wm_inst; wm_soc_clocks[i].wm_max_socclk_clk_in_khz = ranges->writer_wm_sets[i].max_fill_clk_mhz * 1000; wm_soc_clocks[i].wm_min_socclk_clk_in_khz = @@ -452,52 +465,69 @@ static void pp_rv_set_wm_ranges(struct pp_smu *pp, wm_soc_clocks[i].wm_min_mem_clk_in_khz = ranges->writer_wm_sets[i].min_drain_clk_mhz * 1000; } +} +EXPORT_IF_KUNIT(build_wm_clock_ranges_soc15); + +STATIC_IFN_KUNIT void pp_rv_set_wm_ranges(struct pp_smu *pp, + struct pp_smu_wm_range_sets *ranges) +{ + const struct dc_context *ctx = pp->dm; + struct amdgpu_device *adev = ctx->driver_context; + struct dm_pp_wm_sets_with_clock_ranges_soc15 wm_with_clock_ranges; + + build_wm_clock_ranges_soc15(ranges, &wm_with_clock_ranges); amdgpu_dpm_set_watermarks_for_clocks_ranges(adev, &wm_with_clock_ranges); } +EXPORT_IF_KUNIT(pp_rv_set_wm_ranges); -static void pp_rv_set_pme_wa_enable(struct pp_smu *pp) +STATIC_IFN_KUNIT void pp_rv_set_pme_wa_enable(struct pp_smu *pp) { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; amdgpu_dpm_notify_smu_enable_pwe(adev); } +EXPORT_IF_KUNIT(pp_rv_set_pme_wa_enable); -static void pp_rv_set_active_display_count(struct pp_smu *pp, int count) +STATIC_IFN_KUNIT void pp_rv_set_active_display_count(struct pp_smu *pp, int count) { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; amdgpu_dpm_set_active_display_count(adev, count); } +EXPORT_IF_KUNIT(pp_rv_set_active_display_count); -static void pp_rv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int clock) +STATIC_IFN_KUNIT void pp_rv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int clock) { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; amdgpu_dpm_set_min_deep_sleep_dcefclk(adev, clock); } +EXPORT_IF_KUNIT(pp_rv_set_min_deep_sleep_dcfclk); -static void pp_rv_set_hard_min_dcefclk_by_freq(struct pp_smu *pp, int clock) +STATIC_IFN_KUNIT void pp_rv_set_hard_min_dcefclk_by_freq(struct pp_smu *pp, int clock) { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; amdgpu_dpm_set_hard_min_dcefclk_by_freq(adev, clock); } +EXPORT_IF_KUNIT(pp_rv_set_hard_min_dcefclk_by_freq); -static void pp_rv_set_hard_min_fclk_by_freq(struct pp_smu *pp, int mhz) +STATIC_IFN_KUNIT void pp_rv_set_hard_min_fclk_by_freq(struct pp_smu *pp, int mhz) { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; amdgpu_dpm_set_hard_min_fclk_by_freq(adev, mhz); } +EXPORT_IF_KUNIT(pp_rv_set_hard_min_fclk_by_freq); -static enum pp_smu_status pp_nv_set_wm_ranges(struct pp_smu *pp, +STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_wm_ranges(struct pp_smu *pp, struct pp_smu_wm_range_sets *ranges) { const struct dc_context *ctx = pp->dm; @@ -507,8 +537,9 @@ static enum pp_smu_status pp_nv_set_wm_ranges(struct pp_smu *pp, return PP_SMU_RESULT_OK; } +EXPORT_IF_KUNIT(pp_nv_set_wm_ranges); -static enum pp_smu_status pp_nv_set_display_count(struct pp_smu *pp, int count) +STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_display_count(struct pp_smu *pp, int count) { const struct dc_context *ctx = pp->dm; struct amdgpu_device *adev = ctx->driver_context; @@ -523,8 +554,9 @@ static enum pp_smu_status pp_nv_set_display_count(struct pp_smu *pp, int count) return PP_SMU_RESULT_OK; } +EXPORT_IF_KUNIT(pp_nv_set_display_count); -static enum pp_smu_status +STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int mhz) { const struct dc_context *ctx = pp->dm; @@ -540,8 +572,9 @@ pp_nv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int mhz) return PP_SMU_RESULT_OK; } +EXPORT_IF_KUNIT(pp_nv_set_min_deep_sleep_dcfclk); -static enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq( +STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq( struct pp_smu *pp, int mhz) { const struct dc_context *ctx = pp->dm; @@ -563,8 +596,9 @@ static enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq( return PP_SMU_RESULT_OK; } +EXPORT_IF_KUNIT(pp_nv_set_hard_min_dcefclk_by_freq); -static enum pp_smu_status +STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_hard_min_uclk_by_freq(struct pp_smu *pp, int mhz) { const struct dc_context *ctx = pp->dm; @@ -586,8 +620,9 @@ pp_nv_set_hard_min_uclk_by_freq(struct pp_smu *pp, int mhz) return PP_SMU_RESULT_OK; } +EXPORT_IF_KUNIT(pp_nv_set_hard_min_uclk_by_freq); -static enum pp_smu_status pp_nv_set_pstate_handshake_support( +STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_pstate_handshake_support( struct pp_smu *pp, bool pstate_handshake_supported) { const struct dc_context *ctx = pp->dm; @@ -599,28 +634,40 @@ static enum pp_smu_status pp_nv_set_pstate_handshake_support( return PP_SMU_RESULT_OK; } +EXPORT_IF_KUNIT(pp_nv_set_pstate_handshake_support); -static enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp, - enum pp_smu_nv_clock_id clock_id, int mhz) +STATIC_IFN_KUNIT bool pp_smu_nv_clock_id_to_pp(enum pp_smu_nv_clock_id clock_id, + enum amd_pp_clock_type *clock_type) { - const struct dc_context *ctx = pp->dm; - struct amdgpu_device *adev = ctx->driver_context; - struct pp_display_clock_request clock_req; - int ret = 0; - switch (clock_id) { case PP_SMU_NV_DISPCLK: - clock_req.clock_type = amd_pp_disp_clock; + *clock_type = amd_pp_disp_clock; break; case PP_SMU_NV_PHYCLK: - clock_req.clock_type = amd_pp_phy_clock; + *clock_type = amd_pp_phy_clock; break; case PP_SMU_NV_PIXELCLK: - clock_req.clock_type = amd_pp_pixel_clock; + *clock_type = amd_pp_pixel_clock; break; default: - break; + return false; } + + return true; +} +EXPORT_IF_KUNIT(pp_smu_nv_clock_id_to_pp); + +STATIC_IFN_KUNIT enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp, + enum pp_smu_nv_clock_id clock_id, int mhz) +{ + const struct dc_context *ctx = pp->dm; + struct amdgpu_device *adev = ctx->driver_context; + struct pp_display_clock_request clock_req; + int ret = 0; + + if (!pp_smu_nv_clock_id_to_pp(clock_id, &clock_req.clock_type)) + return PP_SMU_RESULT_FAIL; + clock_req.clock_freq_in_khz = mhz * 1000; /* 0: successful or smu.ppt_funcs->display_clock_voltage_request = NULL @@ -634,8 +681,9 @@ static enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp, return PP_SMU_RESULT_OK; } +EXPORT_IF_KUNIT(pp_nv_set_voltage_by_freq); -static enum pp_smu_status pp_nv_get_maximum_sustainable_clocks( +STATIC_IFN_KUNIT enum pp_smu_status pp_nv_get_maximum_sustainable_clocks( struct pp_smu *pp, struct pp_smu_nv_clock_table *max_clocks) { const struct dc_context *ctx = pp->dm; @@ -651,8 +699,9 @@ static enum pp_smu_status pp_nv_get_maximum_sustainable_clocks( return PP_SMU_RESULT_OK; } +EXPORT_IF_KUNIT(pp_nv_get_maximum_sustainable_clocks); -static enum pp_smu_status pp_nv_get_uclk_dpm_states(struct pp_smu *pp, +STATIC_IFN_KUNIT enum pp_smu_status pp_nv_get_uclk_dpm_states(struct pp_smu *pp, unsigned int *clock_values_in_khz, unsigned int *num_states) { const struct dc_context *ctx = pp->dm; @@ -669,8 +718,9 @@ static enum pp_smu_status pp_nv_get_uclk_dpm_states(struct pp_smu *pp, return PP_SMU_RESULT_OK; } +EXPORT_IF_KUNIT(pp_nv_get_uclk_dpm_states); -static enum pp_smu_status pp_rn_get_dpm_clock_table( +STATIC_IFN_KUNIT enum pp_smu_status pp_rn_get_dpm_clock_table( struct pp_smu *pp, struct dpm_clocks *clock_table) { const struct dc_context *ctx = pp->dm; @@ -685,17 +735,7 @@ static enum pp_smu_status pp_rn_get_dpm_clock_table( return PP_SMU_RESULT_OK; } - -static enum pp_smu_status pp_rn_set_wm_ranges(struct pp_smu *pp, - struct pp_smu_wm_range_sets *ranges) -{ - const struct dc_context *ctx = pp->dm; - struct amdgpu_device *adev = ctx->driver_context; - - amdgpu_dpm_set_watermarks_for_clocks_ranges(adev, ranges); - - return PP_SMU_RESULT_OK; -} +EXPORT_IF_KUNIT(pp_rn_get_dpm_clock_table); void dm_pp_get_funcs( struct dc_context *ctx, @@ -743,7 +783,7 @@ void dm_pp_get_funcs( case DCN_VERSION_2_1: funcs->ctx.ver = PP_SMU_VER_RN; funcs->rn_funcs.pp_smu.dm = ctx; - funcs->rn_funcs.set_wm_ranges = pp_rn_set_wm_ranges; + funcs->rn_funcs.set_wm_ranges = pp_nv_set_wm_ranges; funcs->rn_funcs.get_dpm_clock_table = pp_rn_get_dpm_clock_table; break; default: @@ -751,3 +791,4 @@ void dm_pp_get_funcs( break; } } +EXPORT_IF_KUNIT(dm_pp_get_funcs); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h new file mode 100644 index 000000000000..f918eb71f0d1 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_pp_smu.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#ifndef __AMDGPU_DM_PP_SMU_H__ +#define __AMDGPU_DM_PP_SMU_H__ + +#include "dm_pp_interface.h" + +struct amd_pp_display_configuration; +struct pp_smu_wm_range_sets; +struct dm_pp_wm_sets_with_clock_ranges_soc15; + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +void build_pm_display_cfg(struct amd_pp_display_configuration *pm_display_cfg, + const struct dm_pp_display_configuration *pp_display_cfg); +void build_wm_clock_ranges_soc15(const struct pp_smu_wm_range_sets *ranges, + struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm_with_clock_ranges); +void get_default_clock_levels(enum dm_pp_clock_type clk_type, struct dm_pp_clock_levels *clks); +enum amd_pp_clock_type dc_to_pp_clock_type(enum dm_pp_clock_type dm_pp_clk_type); +void pp_to_dc_clock_levels(const struct amd_pp_clocks *pp_clks, + struct dm_pp_clock_levels *dc_clks, + enum dm_pp_clock_type dc_clk_type); +void pp_to_dc_clock_levels_with_latency(const struct pp_clock_levels_with_latency *pp_clks, + struct dm_pp_clock_levels_with_latency *clk_level_info, + enum dm_pp_clock_type dc_clk_type); +void pp_to_dc_clock_levels_with_voltage(const struct pp_clock_levels_with_voltage *pp_clks, + struct dm_pp_clock_levels_with_voltage *clk_level_info, + enum dm_pp_clock_type dc_clk_type); +void cap_clock_levels_to_validation(struct dm_pp_clock_levels *dc_clks, + enum dm_pp_clock_type clk_type, + const struct amd_pp_simple_clock_info *validation_clks); +bool pp_smu_nv_clock_id_to_pp(enum pp_smu_nv_clock_id clock_id, + enum amd_pp_clock_type *clock_type); +void pp_rv_set_wm_ranges(struct pp_smu *pp, struct pp_smu_wm_range_sets *ranges); +void pp_rv_set_pme_wa_enable(struct pp_smu *pp); +void pp_rv_set_active_display_count(struct pp_smu *pp, int count); +void pp_rv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int clock); +void pp_rv_set_hard_min_dcefclk_by_freq(struct pp_smu *pp, int clock); +void pp_rv_set_hard_min_fclk_by_freq(struct pp_smu *pp, int mhz); +enum pp_smu_status pp_nv_set_wm_ranges(struct pp_smu *pp, + struct pp_smu_wm_range_sets *ranges); +enum pp_smu_status pp_nv_set_display_count(struct pp_smu *pp, int count); +enum pp_smu_status pp_nv_set_min_deep_sleep_dcfclk(struct pp_smu *pp, int mhz); +enum pp_smu_status pp_nv_set_hard_min_dcefclk_by_freq(struct pp_smu *pp, int mhz); +enum pp_smu_status pp_nv_set_hard_min_uclk_by_freq(struct pp_smu *pp, int mhz); +enum pp_smu_status pp_nv_set_pstate_handshake_support(struct pp_smu *pp, + bool pstate_handshake_supported); +enum pp_smu_status pp_nv_set_voltage_by_freq(struct pp_smu *pp, + enum pp_smu_nv_clock_id clock_id, int mhz); +enum pp_smu_status pp_nv_get_maximum_sustainable_clocks(struct pp_smu *pp, + struct pp_smu_nv_clock_table *max_clocks); +enum pp_smu_status pp_nv_get_uclk_dpm_states(struct pp_smu *pp, + unsigned int *clock_values_in_khz, + unsigned int *num_states); +enum pp_smu_status pp_rn_get_dpm_clock_table(struct pp_smu *pp, + struct dpm_clocks *clock_table); +#endif + +#endif /* __AMDGPU_DM_PP_SMU_H__ */ 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 0dadc0bb214f..f87de3d18ac0 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 @@ -32,8 +32,8 @@ #include "modules/power/power_helpers.h" #include "amdgpu_dm_kunit_helpers.h" - -static bool link_supports_psrsu(struct dc_link *link) +STATIC_IFN_KUNIT +bool link_supports_psrsu(struct dc_link *link) { struct dc *dc = link->ctx->dc; @@ -60,6 +60,7 @@ static bool link_supports_psrsu(struct dc_link *link) /* Temporarily disable PSR-SU to avoid glitches */ return false; } +EXPORT_IF_KUNIT(link_supports_psrsu); STATIC_IFN_KUNIT void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps) @@ -134,6 +135,7 @@ bool amdgpu_dm_set_psr_caps(struct dc_link *link, struct amdgpu_dm_connector *ac amdgpu_dm_psr_fill_caps(link, &aconnector->psr_caps); return true; } +EXPORT_IF_KUNIT(amdgpu_dm_set_psr_caps); /* * amdgpu_dm_psr_is_active_allowed() - check if psr is allowed on any stream @@ -157,6 +159,7 @@ bool amdgpu_dm_psr_is_active_allowed(struct amdgpu_display_manager *dm) } return false; } +EXPORT_IF_KUNIT(amdgpu_dm_psr_is_active_allowed); /* * amdgpu_dm_psr_set_event() - set or clear PSR event for stream @@ -190,3 +193,47 @@ bool amdgpu_dm_psr_set_event(struct amdgpu_display_manager *dm, struct dc_stream set_event, event, wait_for_disable); } EXPORT_IF_KUNIT(amdgpu_dm_psr_set_event); + +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +/** + * amdgpu_dm_psr_get_dc_feature_mask() - Get DC feature mask for KUnit tests. + * + * Return: Current value of amdgpu_dc_feature_mask. + */ +unsigned int amdgpu_dm_psr_get_dc_feature_mask(void) +{ + return amdgpu_dc_feature_mask; +} +EXPORT_IF_KUNIT(amdgpu_dm_psr_get_dc_feature_mask); + +/** + * amdgpu_dm_psr_set_dc_feature_mask() - Set DC feature mask for KUnit tests. + * @feature_mask: DC feature mask to set while testing amdgpu_dm_psr_fill_caps(). + */ +void amdgpu_dm_psr_set_dc_feature_mask(unsigned int feature_mask) +{ + amdgpu_dc_feature_mask = feature_mask; +} +EXPORT_IF_KUNIT(amdgpu_dm_psr_set_dc_feature_mask); + +/** + * amdgpu_dm_psr_get_dc_debug_mask() - Get DC debug mask for KUnit tests. + * + * Return: Current value of amdgpu_dc_debug_mask. + */ +unsigned int amdgpu_dm_psr_get_dc_debug_mask(void) +{ + return amdgpu_dc_debug_mask; +} +EXPORT_IF_KUNIT(amdgpu_dm_psr_get_dc_debug_mask); + +/** + * amdgpu_dm_psr_set_dc_debug_mask() - Set DC debug mask for KUnit tests. + * @debug_mask: DC debug mask to set while testing link_supports_psrsu(). + */ +void amdgpu_dm_psr_set_dc_debug_mask(unsigned int debug_mask) +{ + amdgpu_dc_debug_mask = debug_mask; +} +EXPORT_IF_KUNIT(amdgpu_dm_psr_set_dc_debug_mask); +#endif 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 40a09b5dc606..e442e7ed82ec 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 @@ -43,7 +43,12 @@ bool amdgpu_dm_psr_set_event(struct amdgpu_display_manager *dm, bool wait_for_disable); #if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +bool link_supports_psrsu(struct dc_link *link); void amdgpu_dm_psr_fill_caps(struct dc_link *link, struct psr_caps *caps); +unsigned int amdgpu_dm_psr_get_dc_feature_mask(void); +void amdgpu_dm_psr_set_dc_feature_mask(unsigned int feature_mask); +unsigned int amdgpu_dm_psr_get_dc_debug_mask(void); +void amdgpu_dm_psr_set_dc_debug_mask(unsigned int debug_mask); #endif #endif /* AMDGPU_DM_AMDGPU_DM_PSR_H_ */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c index 1da07ebf9217..cf28d50c3b5e 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_quirks.c @@ -28,6 +28,7 @@ #include "amdgpu.h" #include "amdgpu_dm.h" +#include "amdgpu_dm_kunit_helpers.h" struct amdgpu_dm_quirks { bool aux_hpd_discon; @@ -176,3 +177,4 @@ void retrieve_dmi_info(struct amdgpu_display_manager *dm) drm_info(dev, "support_edp0_on_dp1 attached\n"); } } +EXPORT_IF_KUNIT(retrieve_dmi_info); 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 22aa4305d2af..42e17119461d 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 @@ -121,14 +121,14 @@ bool amdgpu_dm_set_replay_caps(struct dc_link *link, struct amdgpu_dm_connector debug_flags = (union replay_debug_flags *)&pr_config.debug_flags; debug_flags->u32All = 0; - debug_flags->bitfields.visual_confirm = - link->ctx->dc->debug.visual_confirm == VISUAL_CONFIRM_REPLAY; + debug_flags->bitfields.visual_confirm = dc->debug.visual_confirm == VISUAL_CONFIRM_REPLAY; debug_flags->bitfields.skip_crtc_disabled = dc->debug.replay_skip_crtc_disabled; init_replay_config(link, &pr_config); return true; } +EXPORT_IF_KUNIT(amdgpu_dm_set_replay_caps); /* * amdgpu_dm_link_setup_replay() - config replay settings @@ -144,7 +144,6 @@ bool amdgpu_dm_link_setup_replay(struct dc_stream_state *stream, { struct dc_link *link; unsigned int static_coasting_vtotal; - unsigned int nom_coasting_vtotal; if (!stream || !stream->link || !vrr_params) return false; @@ -159,16 +158,16 @@ bool amdgpu_dm_link_setup_replay(struct dc_stream_state *stream, calculate_replay_link_off_frame_count(link, stream->timing.v_total, stream->timing.h_total); - nom_coasting_vtotal = stream->timing.v_total; static_coasting_vtotal = mod_freesync_calc_v_total_from_refresh(stream, vrr_params->min_refresh_in_uhz); set_replay_coasting_vtotal(link, PR_COASTING_TYPE_NOM, - nom_coasting_vtotal); + stream->timing.v_total); set_replay_coasting_vtotal(link, PR_COASTING_TYPE_STATIC, static_coasting_vtotal); return true; } +EXPORT_IF_KUNIT(amdgpu_dm_link_setup_replay); /* * amdgpu_dm_replay_set_event() - set or clear replay event for a stream @@ -208,3 +207,4 @@ bool amdgpu_dm_replay_set_event(struct amdgpu_display_manager *dm, return mod_power_set_replay_event(dm->power_module, stream, set_event, event, wait_for_disable); } +EXPORT_IF_KUNIT(amdgpu_dm_replay_set_event); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c index 84dcb573d98f..6c0464754ed8 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_services.c @@ -32,9 +32,11 @@ #include "dm_services.h" #include "amdgpu.h" #include "amdgpu_dm.h" +#include "amdgpu_dm_backlight.h" #include "amdgpu_dm_irq.h" #include "amdgpu_pm.h" #include "amdgpu_dm_trace.h" +#include "amdgpu_dm_kunit_helpers.h" unsigned long long dm_get_elapse_time_in_ns(struct dc_context *ctx, @@ -43,6 +45,7 @@ { return current_time_stamp - last_time_stamp; } +EXPORT_IF_KUNIT(dm_get_elapse_time_in_ns); void dm_perf_trace_timestamp(const char *func_name, unsigned int line, struct dc_context *ctx) { @@ -52,14 +55,17 @@ void dm_perf_trace_timestamp(const char *func_name, unsigned int line, struct dc &ctx->perf_trace->last_entry_write, func_name, line); } +EXPORT_IF_KUNIT(dm_perf_trace_timestamp); void dm_trace_smu_enter(uint32_t msg_id, uint32_t param_in, unsigned int delay, struct dc_context *ctx) { } +EXPORT_IF_KUNIT(dm_trace_smu_enter); void dm_trace_smu_exit(bool success, uint32_t response, struct dc_context *ctx) { } +EXPORT_IF_KUNIT(dm_trace_smu_exit); /**** power component interfaces ****/ @@ -89,3 +95,4 @@ bool dm_query_extended_brightness_caps(struct dc_context *ctx, sizeof(struct dm_bl_data_point) * pCaps->num_data_points); return true; } +EXPORT_IF_KUNIT(dm_query_extended_brightness_caps); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c index 110f0173eee6..0bf82e46f773 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.c @@ -29,6 +29,7 @@ #include "amdgpu.h" #include "amdgpu_dm.h" #include "amdgpu_dm_wb.h" +#include "amdgpu_dm_kunit_helpers.h" #include "amdgpu_display.h" #include "dc.h" @@ -40,7 +41,7 @@ static const u32 amdgpu_dm_wb_formats[] = { DRM_FORMAT_XRGB2101010, }; -static int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder, +STATIC_IFN_KUNIT int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder, struct drm_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -59,9 +60,11 @@ static int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder, return -EINVAL; } - for (i = 0; i < sizeof(amdgpu_dm_wb_formats) / sizeof(u32); i++) { - if (fb->format->format == amdgpu_dm_wb_formats[i]) + for (i = 0; i < ARRAY_SIZE(amdgpu_dm_wb_formats); i++) { + if (fb->format->format == amdgpu_dm_wb_formats[i]) { found = true; + break; + } } if (!found) { @@ -72,13 +75,15 @@ static int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder, return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_wb_encoder_atomic_check); -static int amdgpu_dm_wb_connector_get_modes(struct drm_connector *connector) +STATIC_IFN_KUNIT int amdgpu_dm_wb_connector_get_modes(struct drm_connector *connector) { /* Maximum resolution supported by DWB */ return drm_add_modes_noedid(connector, 3840, 2160); } +EXPORT_IF_KUNIT(amdgpu_dm_wb_connector_get_modes); static int amdgpu_dm_wb_prepare_job(struct drm_writeback_connector *wb_connector, struct drm_writeback_job *job) @@ -187,7 +192,7 @@ int amdgpu_dm_wb_connector_init(struct amdgpu_display_manager *dm, { struct dc *dc = dm->dc; struct dc_link *link = dc_get_link_at_index(dc, link_index); - int res = 0; + int res; wbcon->link = link; @@ -211,3 +216,4 @@ int amdgpu_dm_wb_connector_init(struct amdgpu_display_manager *dm, return 0; } +EXPORT_IF_KUNIT(amdgpu_dm_wb_connector_init); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h index 13d31c857dee..7e9fd7a036fa 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_wb.h @@ -29,8 +29,21 @@ #include <drm/drm_writeback.h> +struct amdgpu_display_manager; +struct amdgpu_dm_wb_connector; + int amdgpu_dm_wb_connector_init(struct amdgpu_display_manager *dm, struct amdgpu_dm_wb_connector *dm_wbcon, uint32_t link_index); +#if IS_ENABLED(CONFIG_DRM_AMD_DC_KUNIT_TEST) +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> + +int amdgpu_dm_wb_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state); +int amdgpu_dm_wb_connector_get_modes(struct drm_connector *connector); +#endif + #endif diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig index bd1bf8d959f9..c7c8527dbb10 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/.kunitconfig @@ -15,6 +15,12 @@ CONFIG_I2C=y CONFIG_POWER_SUPPLY=y CONFIG_CRC16=y +# Limit stack size to 1280 +CONFIG_FRAME_WARN=1280 + +# Treat warnings as errors +CONFIG_WERROR=y + # GCOV Coverage - see tools/testing/kunit/configs/coverage_uml.config CONFIG_DEBUG_KERNEL=y CONFIG_DEBUG_INFO=y diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile index 768f9bbc50e1..1592e8dae1a9 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/Makefile @@ -8,11 +8,29 @@ ccflags-y += -I$(src)/../../include ccflags-y += -I$(src)/../../modules/inc ccflags-y += -I$(src)/../../dc ccflags-y += -I$(src)/../../../amdgpu +ccflags-y += -I$(src)/../../../amdkfd +ccflags-y += -I$(src)/../../../include +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_kunit_helpers.o 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_audio_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_connector_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_backlight_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_dmub_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 +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_irq_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_wb_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_plane_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_mst_types_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_pp_smu_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_crtc_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_services_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_helpers_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_quirks_test.o +obj-$(CONFIG_DRM_AMD_DC_KUNIT_TEST) += amdgpu_dm_plane_test.o diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c new file mode 100644 index 000000000000..79ff5d9b3fa5 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_audio_test.c @@ -0,0 +1,490 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_audio.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include <drm/drm_audio_component.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_audio.h" + +/* Tests for amdgpu_dm_audio_init() */ + +/** + * dm_test_audio_init_disabled - Test audio init exits when audio is disabled + * @test: The KUnit test context + */ +static void dm_test_audio_init_disabled(struct kunit *test) +{ + struct amdgpu_device *adev; + int saved_audio = amdgpu_dm_audio_get_param(); + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + amdgpu_dm_audio_set_param(0); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_audio_init(adev), 0); + KUNIT_EXPECT_FALSE(test, adev->mode_info.audio.enabled); + KUNIT_EXPECT_FALSE(test, adev->dm.audio_registered); + + amdgpu_dm_audio_set_param(saved_audio); +} + +/* Tests for amdgpu_dm_audio_fini() */ + +/** + * dm_test_audio_fini_without_enabled_audio - Test fini exits when audio is not enabled + * @test: The KUnit test context + */ +static void dm_test_audio_fini_without_enabled_audio(struct kunit *test) +{ + struct amdgpu_device *adev; + int saved_audio = amdgpu_dm_audio_get_param(); + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + amdgpu_dm_audio_set_param(1); + adev->mode_info.audio.enabled = false; + adev->dm.audio_registered = true; + + amdgpu_dm_audio_fini(adev); + + KUNIT_EXPECT_FALSE(test, adev->mode_info.audio.enabled); + KUNIT_EXPECT_TRUE(test, adev->dm.audio_registered); + + amdgpu_dm_audio_set_param(saved_audio); +} + +/* Tests for amdgpu_dm_fill_audio_info() */ + +/** + * dm_test_fill_audio_info_ids_name_flags - Test Fill audio info ids name flags + * @test: The KUnit test context + */ +static void dm_test_fill_audio_info_ids_name_flags(struct kunit *test) +{ + struct audio_info *audio_info; + struct drm_connector *connector; + struct dc_sink *dc_sink; + const char *name = "DM-AUDIO-PANEL"; + + audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL); + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink); + + dc_sink->edid_caps.manufacturer_id = 0x1234; + dc_sink->edid_caps.product_id = 0xABCD; + dc_sink->edid_caps.speaker_flags = 0x5; + strscpy(dc_sink->edid_caps.display_name, name, + AUDIO_INFO_DISPLAY_NAME_SIZE_IN_CHARS); + + connector->display_info.cea_rev = 1; + + amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink); + + KUNIT_EXPECT_EQ(test, audio_info->manufacture_id, 0x1234U); + KUNIT_EXPECT_EQ(test, audio_info->product_id, 0xABCDU); + KUNIT_EXPECT_EQ(test, audio_info->flags.all, 0x5U); + KUNIT_EXPECT_STREQ(test, audio_info->display_name, name); +} + +/** + * dm_test_fill_audio_info_cea_lt_3_skips_modes - Test Fill audio info cea lt 3 skips modes + * @test: The KUnit test context + */ +static void dm_test_fill_audio_info_cea_lt_3_skips_modes(struct kunit *test) +{ + struct audio_info *audio_info; + struct drm_connector *connector; + struct dc_sink *dc_sink; + + audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL); + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink); + + connector->display_info.cea_rev = 2; + dc_sink->edid_caps.audio_mode_count = 2; + dc_sink->edid_caps.audio_modes[0].format_code = 1; + dc_sink->edid_caps.audio_modes[0].channel_count = 2; + dc_sink->edid_caps.audio_modes[0].sample_rate = 0x07; + dc_sink->edid_caps.audio_modes[0].sample_size = 16; + + amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink); + + KUNIT_EXPECT_EQ(test, audio_info->mode_count, 0U); +} + +/** + * dm_test_fill_audio_info_cea_ge_3_copies_modes - Test Fill audio info cea ge 3 copies modes + * @test: The KUnit test context + */ +static void dm_test_fill_audio_info_cea_ge_3_copies_modes(struct kunit *test) +{ + struct audio_info *audio_info; + struct drm_connector *connector; + struct dc_sink *dc_sink; + + audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL); + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink); + + connector->display_info.cea_rev = 3; + dc_sink->edid_caps.audio_mode_count = 2; + + dc_sink->edid_caps.audio_modes[0].format_code = 1; + dc_sink->edid_caps.audio_modes[0].channel_count = 2; + dc_sink->edid_caps.audio_modes[0].sample_rate = 0x07; + dc_sink->edid_caps.audio_modes[0].sample_size = 16; + + dc_sink->edid_caps.audio_modes[1].format_code = 11; + dc_sink->edid_caps.audio_modes[1].channel_count = 6; + dc_sink->edid_caps.audio_modes[1].sample_rate = 0x1F; + dc_sink->edid_caps.audio_modes[1].sample_size = 24; + + amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink); + + KUNIT_EXPECT_EQ(test, audio_info->mode_count, 2U); + + KUNIT_EXPECT_EQ(test, (int)audio_info->modes[0].format_code, 1); + KUNIT_EXPECT_EQ(test, audio_info->modes[0].channel_count, 2); + KUNIT_EXPECT_EQ(test, audio_info->modes[0].sample_rates.all, 0x07U); + KUNIT_EXPECT_EQ(test, audio_info->modes[0].sample_size, 16); + + KUNIT_EXPECT_EQ(test, (int)audio_info->modes[1].format_code, 11); + KUNIT_EXPECT_EQ(test, audio_info->modes[1].channel_count, 6); + KUNIT_EXPECT_EQ(test, audio_info->modes[1].sample_rates.all, 0x1FU); + KUNIT_EXPECT_EQ(test, audio_info->modes[1].sample_size, 24); +} + +/** + * dm_test_fill_audio_info_latency_present - Test Fill audio info latency present + * @test: The KUnit test context + */ +static void dm_test_fill_audio_info_latency_present(struct kunit *test) +{ + struct audio_info *audio_info; + struct drm_connector *connector; + struct dc_sink *dc_sink; + + audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL); + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink); + + connector->display_info.cea_rev = 3; + connector->latency_present[0] = true; + connector->video_latency[0] = 11; + connector->audio_latency[0] = 22; + + amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink); + + KUNIT_EXPECT_EQ(test, audio_info->video_latency, 11U); + KUNIT_EXPECT_EQ(test, audio_info->audio_latency, 22U); +} + +/** + * dm_test_fill_audio_info_latency_absent_keeps_zero - Test Fill audio info latency absent keeps zero + * @test: The KUnit test context + */ +static void dm_test_fill_audio_info_latency_absent_keeps_zero(struct kunit *test) +{ + struct audio_info *audio_info; + struct drm_connector *connector; + struct dc_sink *dc_sink; + + audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL); + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink); + + connector->display_info.cea_rev = 3; + connector->latency_present[0] = false; + connector->video_latency[0] = 99; + connector->audio_latency[0] = 88; + + amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink); + + KUNIT_EXPECT_EQ(test, audio_info->video_latency, 0U); + KUNIT_EXPECT_EQ(test, audio_info->audio_latency, 0U); +} + +/** + * dm_test_fill_audio_info_cea_ge_3_zero_modes - Test cea >= 3 with zero modes + * @test: The KUnit test context + * + * When cea_rev >= 3 but the sink reports no audio modes, mode_count must be + * copied as 0 and no mode entries should be populated. + */ +static void dm_test_fill_audio_info_cea_ge_3_zero_modes(struct kunit *test) +{ + struct audio_info *audio_info; + struct drm_connector *connector; + struct dc_sink *dc_sink; + + audio_info = kunit_kzalloc(test, sizeof(*audio_info), GFP_KERNEL); + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + dc_sink = kunit_kzalloc(test, sizeof(*dc_sink), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_info); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc_sink); + + connector->display_info.cea_rev = 3; + dc_sink->edid_caps.audio_mode_count = 0; + + amdgpu_dm_fill_audio_info(audio_info, connector, dc_sink); + + KUNIT_EXPECT_EQ(test, audio_info->mode_count, 0U); + KUNIT_EXPECT_EQ(test, (int)audio_info->modes[0].format_code, 0); +} + +/* Tests for amdgpu_dm_audio_component_bind()/unbind() */ + +/** + * dm_test_audio_component_bind_sets_fields - Test bind wires up audio component + * @test: The KUnit test context + * + * Binding must publish the DRM audio component ops, record the kernel device, + * and store the component pointer in the display manager. + */ +static void dm_test_audio_component_bind_sets_fields(struct kunit *test) +{ + struct amdgpu_device *adev; + struct device *kdev; + struct drm_audio_component *acomp; + int ret; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + kdev = kunit_kzalloc(test, sizeof(*kdev), GFP_KERNEL); + acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, kdev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp); + + dev_set_drvdata(kdev, &adev->ddev); + + ret = amdgpu_dm_audio_component_bind(kdev, NULL, acomp); + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_NOT_NULL(test, acomp->ops); + KUNIT_EXPECT_PTR_EQ(test, acomp->dev, kdev); + KUNIT_EXPECT_PTR_EQ(test, adev->dm.audio_component, acomp); +} + +/** + * dm_test_audio_component_unbind_clears_fields - Test unbind tears down component + * @test: The KUnit test context + * + * Unbinding must clear the component ops, the kernel device, and the display + * manager's stored component pointer. + */ +static void dm_test_audio_component_unbind_clears_fields(struct kunit *test) +{ + struct amdgpu_device *adev; + struct device *kdev; + struct drm_audio_component *acomp; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + kdev = kunit_kzalloc(test, sizeof(*kdev), GFP_KERNEL); + acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, kdev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp); + + dev_set_drvdata(kdev, &adev->ddev); + + /* Pretend a prior bind already happened. */ + acomp->dev = kdev; + adev->dm.audio_component = acomp; + + amdgpu_dm_audio_component_unbind(kdev, NULL, acomp); + + KUNIT_EXPECT_NULL(test, acomp->ops); + KUNIT_EXPECT_NULL(test, acomp->dev); + KUNIT_EXPECT_NULL(test, adev->dm.audio_component); +} + +/* Tests for amdgpu_dm_audio_eld_notify() */ + +static int dm_test_eld_notify_count; +static int dm_test_eld_notify_port; +static void *dm_test_eld_notify_ptr; + +static void dm_test_pin_eld_notify(void *audio_ptr, int port, int pipe) +{ + dm_test_eld_notify_count++; + dm_test_eld_notify_port = port; + dm_test_eld_notify_ptr = audio_ptr; +} + +/** + * dm_test_eld_notify_invokes_callback - Test ELD notify forwards to hda driver + * @test: The KUnit test context + * + * When a component with a pin_eld_notify callback is registered, the notify + * helper must invoke it with the audio pointer and the requested pin. + */ +static void dm_test_eld_notify_invokes_callback(struct kunit *test) +{ + struct amdgpu_device *adev; + struct drm_audio_component *acomp; + struct drm_audio_component_audio_ops *audio_ops; + int marker = 0; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL); + audio_ops = kunit_kzalloc(test, sizeof(*audio_ops), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_ops); + + audio_ops->audio_ptr = ▮ + audio_ops->pin_eld_notify = dm_test_pin_eld_notify; + acomp->audio_ops = audio_ops; + adev->dm.audio_component = acomp; + + dm_test_eld_notify_count = 0; + dm_test_eld_notify_port = -100; + dm_test_eld_notify_ptr = NULL; + + amdgpu_dm_audio_eld_notify(adev, 7); + + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 1); + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_port, 7); + KUNIT_EXPECT_PTR_EQ(test, dm_test_eld_notify_ptr, (void *)&marker); +} + +/** + * dm_test_eld_notify_no_component - Test ELD notify is a no-op without component + * @test: The KUnit test context + * + * With no registered audio component, the notify helper must return without + * invoking any callback. + */ +static void dm_test_eld_notify_no_component(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->dm.audio_component = NULL; + + dm_test_eld_notify_count = 0; + + amdgpu_dm_audio_eld_notify(adev, 3); + + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0); +} + +/** + * dm_test_eld_notify_null_audio_ops - Test ELD notify is a no-op without audio_ops + * @test: The KUnit test context + * + * A component without audio_ops must not trigger any callback. + */ +static void dm_test_eld_notify_null_audio_ops(struct kunit *test) +{ + struct amdgpu_device *adev; + struct drm_audio_component *acomp; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp); + + acomp->audio_ops = NULL; + adev->dm.audio_component = acomp; + + dm_test_eld_notify_count = 0; + + amdgpu_dm_audio_eld_notify(adev, 3); + + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0); +} + +/** + * dm_test_eld_notify_null_callback - Test ELD notify is a no-op without callback + * @test: The KUnit test context + * + * audio_ops present but with a NULL pin_eld_notify must not crash or call + * anything. + */ +static void dm_test_eld_notify_null_callback(struct kunit *test) +{ + struct amdgpu_device *adev; + struct drm_audio_component *acomp; + struct drm_audio_component_audio_ops *audio_ops; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + acomp = kunit_kzalloc(test, sizeof(*acomp), GFP_KERNEL); + audio_ops = kunit_kzalloc(test, sizeof(*audio_ops), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acomp); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, audio_ops); + + audio_ops->pin_eld_notify = NULL; + acomp->audio_ops = audio_ops; + adev->dm.audio_component = acomp; + + dm_test_eld_notify_count = 0; + + amdgpu_dm_audio_eld_notify(adev, 3); + + KUNIT_EXPECT_EQ(test, dm_test_eld_notify_count, 0); +} + +static struct kunit_case dm_audio_test_cases[] = { + /* amdgpu_dm_audio_init */ + KUNIT_CASE(dm_test_audio_init_disabled), + /* amdgpu_dm_audio_fini */ + KUNIT_CASE(dm_test_audio_fini_without_enabled_audio), + /* amdgpu_dm_fill_audio_info */ + KUNIT_CASE(dm_test_fill_audio_info_ids_name_flags), + KUNIT_CASE(dm_test_fill_audio_info_cea_lt_3_skips_modes), + KUNIT_CASE(dm_test_fill_audio_info_cea_ge_3_copies_modes), + KUNIT_CASE(dm_test_fill_audio_info_cea_ge_3_zero_modes), + KUNIT_CASE(dm_test_fill_audio_info_latency_present), + KUNIT_CASE(dm_test_fill_audio_info_latency_absent_keeps_zero), + /* amdgpu_dm_audio_component_bind/unbind */ + KUNIT_CASE(dm_test_audio_component_bind_sets_fields), + KUNIT_CASE(dm_test_audio_component_unbind_clears_fields), + /* amdgpu_dm_audio_eld_notify */ + KUNIT_CASE(dm_test_eld_notify_invokes_callback), + KUNIT_CASE(dm_test_eld_notify_no_component), + KUNIT_CASE(dm_test_eld_notify_null_audio_ops), + KUNIT_CASE(dm_test_eld_notify_null_callback), + {} +}; + +static struct kunit_suite dm_audio_test_suite = { + .name = "amdgpu_dm_audio", + .test_cases = dm_audio_test_cases, +}; + +kunit_test_suite(dm_audio_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_audio"); +MODULE_AUTHOR("AMD"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c new file mode 100644 index 000000000000..fff50c1325c6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_backlight_test.c @@ -0,0 +1,1242 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_backlight.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <linux/backlight.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_backlight.h" +#include "amdgpu_dm_kunit_test_helpers.h" +#include "amd_shared.h" +#include "dc/inc/hw/panel_cntl.h" + +struct dm_backlight_connector_fixture { + struct amdgpu_device *adev; + struct amdgpu_dm_connector *aconnector; + struct dc_link *link; +}; + +static void setup_test_connector(struct kunit *test, + struct dm_backlight_connector_fixture *fixture, + int bl_idx, enum signal_type signal) +{ + fixture->adev = kunit_kzalloc(test, sizeof(*fixture->adev), GFP_KERNEL); + fixture->aconnector = kunit_kzalloc(test, sizeof(*fixture->aconnector), GFP_KERNEL); + fixture->link = kunit_kzalloc(test, sizeof(*fixture->link), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->aconnector); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fixture->link); + + fixture->aconnector->bl_idx = bl_idx; + fixture->aconnector->dc_link = fixture->link; + fixture->aconnector->base.dev = &fixture->adev->ddev; + fixture->link->connector_signal = signal; +} + +/* Tests for amdgpu_dm_backlight_get_device_index() */ + +/** + * dm_test_backlight_device_index_matches_second - Test matching second backlight device + * @test: The KUnit test context + */ +static void dm_test_backlight_device_index_matches_second(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct backlight_device *bd0; + struct backlight_device *bd1; + + bd0 = kunit_kzalloc(test, sizeof(*bd0), GFP_KERNEL); + bd1 = kunit_kzalloc(test, sizeof(*bd1), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, bd0); + KUNIT_ASSERT_NOT_NULL(test, bd1); + + dm->num_of_edps = 2; + dm->backlight_dev[0] = bd0; + dm->backlight_dev[1] = bd1; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_get_device_index(dm, bd1), 1); +} + +/** + * dm_test_backlight_device_index_missing_fallback - Test missing backlight device fallback + * @test: The KUnit test context + */ +static void dm_test_backlight_device_index_missing_fallback(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct backlight_device *known_bd; + struct backlight_device *unknown_bd; + + known_bd = kunit_kzalloc(test, sizeof(*known_bd), GFP_KERNEL); + unknown_bd = kunit_kzalloc(test, sizeof(*unknown_bd), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, known_bd); + KUNIT_ASSERT_NOT_NULL(test, unknown_bd); + + dm->num_of_edps = 1; + dm->backlight_dev[0] = known_bd; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_backlight_get_device_index(dm, unknown_bd), 0); +} + +/* Tests for amdgpu_dm_update_backlight_caps() */ + +/** + * dm_test_backlight_caps_valid_short_circuit - Test Backlight caps valid short circuit + * @test: The KUnit test context + */ +static void dm_test_backlight_caps_valid_short_circuit(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0]; + + caps->caps_valid = true; + caps->aux_support = false; + caps->min_input_signal = 42; + caps->max_input_signal = 199; + + amdgpu_dm_update_backlight_caps(dm, 0); + + KUNIT_EXPECT_TRUE(test, caps->caps_valid); + KUNIT_EXPECT_EQ(test, caps->min_input_signal, 42); + KUNIT_EXPECT_EQ(test, caps->max_input_signal, 199); +} + +#if !defined(CONFIG_ACPI) + +/** + * dm_test_backlight_caps_aux_support_noop - Test Backlight caps aux support noop + * @test: The KUnit test context + */ +static void dm_test_backlight_caps_aux_support_noop(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0]; + + caps->caps_valid = false; + caps->aux_support = true; + caps->min_input_signal = 11; + caps->max_input_signal = 222; + + amdgpu_dm_update_backlight_caps(dm, 0); + + KUNIT_EXPECT_FALSE(test, caps->caps_valid); + KUNIT_EXPECT_EQ(test, caps->min_input_signal, 11); + KUNIT_EXPECT_EQ(test, caps->max_input_signal, 222); +} + +/** + * dm_test_backlight_caps_non_aux_sets_defaults - Test Backlight caps non aux sets defaults + * @test: The KUnit test context + */ +static void dm_test_backlight_caps_non_aux_sets_defaults(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct amdgpu_dm_backlight_caps *caps = &dm->backlight_caps[0]; + + caps->caps_valid = false; + caps->aux_support = false; + caps->min_input_signal = 0; + caps->max_input_signal = 0; + + amdgpu_dm_update_backlight_caps(dm, 0); + + KUNIT_EXPECT_TRUE(test, caps->caps_valid); + KUNIT_EXPECT_EQ(test, caps->min_input_signal, AMDGPU_DM_DEFAULT_MIN_BACKLIGHT); + KUNIT_EXPECT_EQ(test, caps->max_input_signal, AMDGPU_DM_DEFAULT_MAX_BACKLIGHT); +} +#endif + +/* Tests for get_brightness_range() */ + +/** + * dm_test_brightness_range_null_caps - Test Brightness range null caps + * @test: The KUnit test context + */ +static void dm_test_brightness_range_null_caps(struct kunit *test) +{ + unsigned int min = 99, max = 99; + + KUNIT_EXPECT_EQ(test, get_brightness_range(NULL, &min, &max), 0); + /* min/max should remain untouched */ + KUNIT_EXPECT_EQ(test, min, 99U); + KUNIT_EXPECT_EQ(test, max, 99U); +} + +/** + * dm_test_brightness_range_pwm - Test Brightness range pwm + * @test: The KUnit test context + */ +static void dm_test_brightness_range_pwm(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + + caps.aux_support = false; + caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + + KUNIT_EXPECT_EQ(test, get_brightness_range(&caps, &min, &max), 1); + /* 0x101 * AMDGPU_DM_DEFAULT_MIN_BACKLIGHT, 0x101 * AMDGPU_DM_DEFAULT_MAX_BACKLIGHT */ + KUNIT_EXPECT_EQ(test, min, 0x101U * AMDGPU_DM_DEFAULT_MIN_BACKLIGHT); + KUNIT_EXPECT_EQ(test, max, 0x101U * AMDGPU_DM_DEFAULT_MAX_BACKLIGHT); +} + +/** + * dm_test_brightness_range_aux - Test Brightness range aux + * @test: The KUnit test context + */ +static void dm_test_brightness_range_aux(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + + caps.aux_support = true; + caps.aux_min_input_signal = 1; + caps.aux_max_input_signal = 512; + + KUNIT_EXPECT_EQ(test, get_brightness_range(&caps, &min, &max), 1); + /* millinits: 1000 * value */ + KUNIT_EXPECT_EQ(test, min, 1000U); + KUNIT_EXPECT_EQ(test, max, 512000U); +} + +/* Tests for convert_brightness_to_user() */ + +/** + * dm_test_brightness_to_user_null_caps - Test Brightness to user null caps + * @test: The KUnit test context + */ +static void dm_test_brightness_to_user_null_caps(struct kunit *test) +{ + /* + * With NULL caps, get_brightness_range fails → passthrough. + * We simulate this by passing a zeroed caps struct where + * max_input_signal=0 makes max=0 and the function hits + * get_brightness_range returning 0 since caps is NULL. + */ + KUNIT_EXPECT_EQ(test, convert_brightness_to_user(NULL, 42), 42U); +} + +/** + * dm_test_brightness_to_user_below_min - Test Brightness to user below min + * @test: The KUnit test context + */ +static void dm_test_brightness_to_user_below_min(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + + caps.aux_support = false; + caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + + /* brightness < min (0x101*AMDGPU_DM_DEFAULT_MIN_BACKLIGHT), should return 0 */ + KUNIT_EXPECT_EQ(test, convert_brightness_to_user(&caps, 100), 0U); +} + +/** + * dm_test_brightness_to_user_at_max - Test Brightness to user at max + * @test: The KUnit test context + */ +static void dm_test_brightness_to_user_at_max(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + + caps.aux_support = false; + caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + + get_brightness_range(&caps, &min, &max); + + /* At max → should return max */ + KUNIT_EXPECT_EQ(test, convert_brightness_to_user(&caps, max), max); +} + +/** + * dm_test_brightness_to_user_at_min - Test Brightness to user at min + * @test: The KUnit test context + */ +static void dm_test_brightness_to_user_at_min(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + + caps.aux_support = false; + caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + + get_brightness_range(&caps, &min, &max); + + /* At min → should return 0 */ + KUNIT_EXPECT_EQ(test, convert_brightness_to_user(&caps, min), 0U); +} + +/** + * dm_test_brightness_to_user_midpoint_pwm - Test Brightness to user midpoint pwm + * @test: The KUnit test context + */ +static void dm_test_brightness_to_user_midpoint_pwm(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max, mid_hw, result; + u64 expected; + + caps.aux_support = false; + caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + + get_brightness_range(&caps, &min, &max); + + /* midpoint of hw range */ + mid_hw = min + (max - min) / 2; + /* expected = DIV_ROUND_CLOSEST_ULL((u64)max * (mid_hw - min), max - min) */ + expected = DIV_ROUND_CLOSEST_ULL((u64)max * (mid_hw - min), max - min); + result = convert_brightness_to_user(&caps, mid_hw); + + KUNIT_EXPECT_EQ(test, result, (u32)expected); +} + +/* Tests for convert_brightness_from_user() — no custom curve */ + +/** + * dm_test_brightness_from_user_null_caps - Test Brightness from user null caps + * @test: The KUnit test context + */ +static void dm_test_brightness_from_user_null_caps(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, convert_brightness_from_user(NULL, 100), 100U); +} + +/** + * dm_test_brightness_from_user_zero - Test Brightness from user zero + * @test: The KUnit test context + */ +static void dm_test_brightness_from_user_zero(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + + caps.aux_support = false; + caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + /* no custom curve */ + caps.data_points = 0; + + get_brightness_range(&caps, &min, &max); + + /* brightness=0 → min + 0 = min */ + KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 0), (u32)min); +} + +/** + * dm_test_brightness_from_user_max - Test Brightness from user max + * @test: The KUnit test context + */ +static void dm_test_brightness_from_user_max(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + + caps.aux_support = false; + caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps.data_points = 0; + + get_brightness_range(&caps, &min, &max); + + /* + * brightness=max → min + DIV_ROUND_CLOSEST((max-min)*max, max) + * = min + (max - min) = max + */ + KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, max), (u32)max); +} + +/** + * dm_test_brightness_from_user_aux - Test Brightness from user aux + * @test: The KUnit test context + */ +static void dm_test_brightness_from_user_aux(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + + caps.aux_support = true; + caps.aux_min_input_signal = 1; + caps.aux_max_input_signal = 512; + caps.data_points = 0; + + get_brightness_range(&caps, &min, &max); + + /* brightness=0 → min */ + KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, 0), (u32)min); + /* brightness=max → max */ + KUNIT_EXPECT_EQ(test, convert_brightness_from_user(&caps, max), (u32)max); +} + +/* Tests for convert_custom_brightness() */ + +/** + * dm_test_custom_brightness_no_data_points - Test Custom brightness no data points + * @test: The KUnit test context + */ +static void dm_test_custom_brightness_no_data_points(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + uint32_t brightness = 128; + uint32_t saved = brightness; + + caps.data_points = 0; + + convert_custom_brightness(&caps, 3084, 65535, &brightness); + + /* No data points → no-op */ + KUNIT_EXPECT_EQ(test, brightness, saved); +} + +/** + * dm_test_custom_brightness_debug_mask_disables - Test Custom brightness debug mask disables + * @test: The KUnit test context + */ +static void dm_test_custom_brightness_debug_mask_disables(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + uint32_t brightness = 128; + uint32_t saved = brightness; + uint saved_mask = amdgpu_dm_get_dc_debug_mask(); + + caps.data_points = 3; + caps.luminance_data[0].input_signal = 50; + caps.luminance_data[0].luminance = 10; + + /* Set the disable flag */ + amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() | DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE); + + convert_custom_brightness(&caps, 3084, 65535, &brightness); + + /* Should be no-op due to debug mask */ + KUNIT_EXPECT_EQ(test, brightness, saved); + + amdgpu_dm_set_dc_debug_mask(saved_mask); +} + +/** + * dm_test_custom_brightness_exact_match - Test Custom brightness exact match + * @test: The KUnit test context + */ +static void dm_test_custom_brightness_exact_match(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + uint32_t brightness; + unsigned int min, max; + uint saved_mask = amdgpu_dm_get_dc_debug_mask(); + + amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE); + + caps.aux_support = false; + caps.min_input_signal = 0; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps.data_points = 3; + caps.luminance_data[0].input_signal = 50; + caps.luminance_data[0].luminance = 20; + caps.luminance_data[1].input_signal = 128; + caps.luminance_data[1].luminance = 50; + caps.luminance_data[2].input_signal = 200; + caps.luminance_data[2].luminance = 90; + + get_brightness_range(&caps, &min, &max); + + /* + * Set brightness so that scale_input_to_fw yields exactly 128. + * scale_input_to_fw(min, max, x) = DIV_ROUND_CLOSEST(x * 255, max - min) + * With min=0, max=0x101*255=65535: + * We need x such that DIV_ROUND_CLOSEST(x * 255, 65535) = 128 + * → x = 128 * 65535 / 255 = 32896 + */ + brightness = 32896; + + convert_custom_brightness(&caps, min, max, &brightness); + + /* + * Exact match: lum=50, brightness_scaled=128 + * result = scale_fw_to_input(min, max, DIV_ROUND_CLOSEST(50*128, 101)) + * = scale_fw_to_input(0, 65535, DIV_ROUND_CLOSEST(6400, 101)) + * = scale_fw_to_input(0, 65535, 63) + * = 0 + DIV_ROUND_CLOSEST(63 * 65535, 255) = 16191 (approx) + */ + KUNIT_EXPECT_TRUE(test, brightness != 32896); + KUNIT_EXPECT_TRUE(test, brightness < 32896); + + amdgpu_dm_set_dc_debug_mask(saved_mask); +} + +/** + * dm_test_custom_brightness_below_first - Test Custom brightness below first + * @test: The KUnit test context + */ +static void dm_test_custom_brightness_below_first(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + uint32_t brightness; + unsigned int min, max; + uint saved_mask = amdgpu_dm_get_dc_debug_mask(); + + amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE); + + caps.aux_support = false; + caps.min_input_signal = 0; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps.data_points = 2; + caps.luminance_data[0].input_signal = 100; + caps.luminance_data[0].luminance = 40; + caps.luminance_data[1].input_signal = 200; + caps.luminance_data[1].luminance = 80; + + get_brightness_range(&caps, &min, &max); + + /* + * Set brightness low enough that scaled value < 100. + * scale_input_to_fw(0, 65535, x) = DIV_ROUND_CLOSEST(x*255, 65535) + * For result=50: x = 50*65535/255 = 12850 + */ + brightness = 12850; + + convert_custom_brightness(&caps, min, max, &brightness); + + /* + * Below first data point: lum = DIV_ROUND_CLOSEST(40 * 50, 100) = 20 + * Then: scale_fw_to_input(0, 65535, DIV_ROUND_CLOSEST(20 * 50, 101)) + * = scale_fw_to_input(0, 65535, DIV_ROUND_CLOSEST(1000, 101)) + * = scale_fw_to_input(0, 65535, 10) + * The output should be significantly less than input. + */ + KUNIT_EXPECT_TRUE(test, brightness < 12850); + + amdgpu_dm_set_dc_debug_mask(saved_mask); +} + +/** + * dm_test_custom_brightness_interpolation - Test Custom brightness interpolation + * @test: The KUnit test context + */ +static void dm_test_custom_brightness_interpolation(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + uint32_t brightness; + unsigned int min, max; + uint saved_mask = amdgpu_dm_get_dc_debug_mask(); + + amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE); + + caps.aux_support = false; + caps.min_input_signal = 0; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps.data_points = 2; + caps.luminance_data[0].input_signal = 50; + caps.luminance_data[0].luminance = 20; + caps.luminance_data[1].input_signal = 200; + caps.luminance_data[1].luminance = 80; + + get_brightness_range(&caps, &min, &max); + + /* + * Choose a value between data points 50 and 200. + * scale_input_to_fw(0, 65535, x) = 125 when x = 125*65535/255 = 32125 + */ + brightness = 32125; + + convert_custom_brightness(&caps, min, max, &brightness); + + /* + * The function should interpolate between data points and produce + * a remapped value different from the input. + */ + KUNIT_EXPECT_TRUE(test, brightness != 32125); + + amdgpu_dm_set_dc_debug_mask(saved_mask); +} + +/** + * dm_test_custom_brightness_above_last - Test Custom brightness above last data point + * @test: The KUnit test context + */ +static void dm_test_custom_brightness_above_last(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + uint32_t brightness; + unsigned int min, max; + uint saved_mask = amdgpu_dm_get_dc_debug_mask(); + + amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE); + + caps.aux_support = false; + caps.min_input_signal = 0; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps.data_points = 2; + caps.luminance_data[0].input_signal = 50; + caps.luminance_data[0].luminance = 20; + caps.luminance_data[1].input_signal = 150; + caps.luminance_data[1].luminance = 60; + + get_brightness_range(&caps, &min, &max); + + /* + * Choose brightness above the last data point (150). + * scale_input_to_fw(0, 65535, x) = 220 when x = 220*65535/255 = 56533 + * After binary search, left >= data_points, clamped → right==left, + * so lum = upper_lum = 60. + */ + brightness = 56533; + + convert_custom_brightness(&caps, min, max, &brightness); + + /* Output should differ from input (remapped via curve) */ + KUNIT_EXPECT_TRUE(test, brightness != 56533); + KUNIT_EXPECT_TRUE(test, brightness < 56533); + + amdgpu_dm_set_dc_debug_mask(saved_mask); +} + +/** + * dm_test_custom_brightness_single_data_point - Test Custom brightness with single data point + * @test: The KUnit test context + */ +static void dm_test_custom_brightness_single_data_point(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + uint32_t brightness; + unsigned int min, max; + uint saved_mask = amdgpu_dm_get_dc_debug_mask(); + + amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE); + + caps.aux_support = false; + caps.min_input_signal = 0; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps.data_points = 1; + caps.luminance_data[0].input_signal = 128; + caps.luminance_data[0].luminance = 50; + + get_brightness_range(&caps, &min, &max); + + /* + * Brightness below the single data point triggers the + * "below first" path: lum = DIV_ROUND_CLOSEST(50 * scaled, 128). + * scale_input_to_fw(0, 65535, x) = 64 when x = 64*65535/255 = 16448 + */ + brightness = 16448; + + convert_custom_brightness(&caps, min, max, &brightness); + + KUNIT_EXPECT_TRUE(test, brightness < 16448); + + amdgpu_dm_set_dc_debug_mask(saved_mask); +} + +/** + * dm_test_custom_brightness_lower_lum_zero - Test Custom brightness with zero lower luminance + * @test: The KUnit test context + */ +static void dm_test_custom_brightness_lower_lum_zero(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + uint32_t brightness; + unsigned int min, max; + uint saved_mask = amdgpu_dm_get_dc_debug_mask(); + + amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE); + + caps.aux_support = false; + caps.min_input_signal = 0; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps.data_points = 2; + caps.luminance_data[0].input_signal = 50; + caps.luminance_data[0].luminance = 0; /* zero lower luminance */ + caps.luminance_data[1].input_signal = 200; + caps.luminance_data[1].luminance = 80; + + get_brightness_range(&caps, &min, &max); + + /* + * Choose brightness between data points to trigger interpolation. + * scale_input_to_fw(0, 65535, x) = 125 when x = 125*65535/255 = 32125 + * With lower_lum == 0, code takes shortcut: lum = upper_lum = 80. + */ + brightness = 32125; + + convert_custom_brightness(&caps, min, max, &brightness); + + /* Should remap; result should differ from input */ + KUNIT_EXPECT_TRUE(test, brightness != 32125); + + amdgpu_dm_set_dc_debug_mask(saved_mask); +} + +/** + * dm_test_brightness_to_user_above_max - Test Brightness to user above max + * @test: The KUnit test context + */ +static void dm_test_brightness_to_user_above_max(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max, result; + + caps.aux_support = false; + caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + + get_brightness_range(&caps, &min, &max); + + /* brightness above max → result > max (linear extrapolation) */ + result = convert_brightness_to_user(&caps, max + 1000); + + KUNIT_EXPECT_GT(test, result, max); +} + +/** + * dm_test_brightness_from_user_midrange - Test Brightness from user mid-range value + * @test: The KUnit test context + */ +static void dm_test_brightness_from_user_midrange(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + u32 result; + + caps.aux_support = false; + caps.min_input_signal = AMDGPU_DM_DEFAULT_MIN_BACKLIGHT; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps.data_points = 0; + + get_brightness_range(&caps, &min, &max); + + /* Mid-range brightness should map to between min and max */ + result = convert_brightness_from_user(&caps, max / 2); + + KUNIT_EXPECT_GE(test, result, min); + KUNIT_EXPECT_LE(test, result, max); +} + +/** + * dm_test_brightness_from_user_with_curve - Test Brightness from user with custom curve active + * @test: The KUnit test context + */ +static void dm_test_brightness_from_user_with_curve(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + u32 with_curve, without_curve; + uint saved_mask = amdgpu_dm_get_dc_debug_mask(); + + amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() & ~DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE); + + caps.aux_support = false; + caps.min_input_signal = 0; + caps.max_input_signal = AMDGPU_DM_DEFAULT_MAX_BACKLIGHT; + caps.data_points = 2; + caps.luminance_data[0].input_signal = 50; + caps.luminance_data[0].luminance = 20; + caps.luminance_data[1].input_signal = 200; + caps.luminance_data[1].luminance = 80; + + get_brightness_range(&caps, &min, &max); + + with_curve = convert_brightness_from_user(&caps, max / 2); + + /* Now disable the curve and compare */ + amdgpu_dm_set_dc_debug_mask(amdgpu_dm_get_dc_debug_mask() | DC_DISABLE_CUSTOM_BRIGHTNESS_CURVE); + without_curve = convert_brightness_from_user(&caps, max / 2); + + /* Custom curve should produce a different mapping */ + KUNIT_EXPECT_NE(test, with_curve, without_curve); + + amdgpu_dm_set_dc_debug_mask(saved_mask); +} + +/** + * dm_test_brightness_range_zero_signals - Test Brightness range with zero min and max signals + * @test: The KUnit test context + */ +static void dm_test_brightness_range_zero_signals(struct kunit *test) +{ + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min = 99, max = 99; + + caps.aux_support = false; + caps.min_input_signal = 0; + caps.max_input_signal = 0; + + /* Both signals zero → min=max=0 */ + KUNIT_EXPECT_EQ(test, get_brightness_range(&caps, &min, &max), 1); + KUNIT_EXPECT_EQ(test, min, 0U); + KUNIT_EXPECT_EQ(test, max, 0U); +} + +/* Tests for amdgpu_dm_backlight_fill_props() */ + +/** + * dm_test_backlight_fill_props_ac_linear - Test AC brightness and linear scale + * @test: The KUnit test context + */ +static void dm_test_backlight_fill_props_ac_linear(struct kunit *test) +{ + struct backlight_properties props = {}; + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + + caps.min_input_signal = 12; + caps.max_input_signal = 255; + caps.ac_level = 40; + caps.dc_level = 20; + + get_brightness_range(&caps, &min, &max); + amdgpu_dm_backlight_fill_props(&caps, true, false, &props); + + KUNIT_EXPECT_EQ(test, props.brightness, + DIV_ROUND_CLOSEST((max - min) * caps.ac_level, 100)); + KUNIT_EXPECT_EQ(test, props.max_brightness, max - min); + KUNIT_EXPECT_EQ(test, props.scale, BACKLIGHT_SCALE_LINEAR); + KUNIT_EXPECT_EQ(test, props.type, BACKLIGHT_RAW); +} + +/** + * dm_test_backlight_fill_props_dc_nonlinear - Test DC brightness and non-linear scale + * @test: The KUnit test context + */ +static void dm_test_backlight_fill_props_dc_nonlinear(struct kunit *test) +{ + struct backlight_properties props = {}; + struct amdgpu_dm_backlight_caps caps = {}; + unsigned int min, max; + + caps.min_input_signal = 12; + caps.max_input_signal = 255; + caps.ac_level = 40; + caps.dc_level = 20; + caps.data_points = 2; + + get_brightness_range(&caps, &min, &max); + amdgpu_dm_backlight_fill_props(&caps, false, true, &props); + + KUNIT_EXPECT_EQ(test, props.brightness, + DIV_ROUND_CLOSEST((max - min) * caps.dc_level, 100)); + KUNIT_EXPECT_EQ(test, props.max_brightness, max - min); + KUNIT_EXPECT_EQ(test, props.scale, BACKLIGHT_SCALE_NON_LINEAR); + KUNIT_EXPECT_EQ(test, props.type, BACKLIGHT_RAW); +} + +/** + * dm_test_backlight_fill_props_default_range - Test default properties without caps + * @test: The KUnit test context + */ +static void dm_test_backlight_fill_props_default_range(struct kunit *test) +{ + struct backlight_properties props = {}; + + amdgpu_dm_backlight_fill_props(NULL, false, true, &props); + + KUNIT_EXPECT_EQ(test, props.brightness, MAX_BACKLIGHT_LEVEL); + KUNIT_EXPECT_EQ(test, props.max_brightness, MAX_BACKLIGHT_LEVEL); + KUNIT_EXPECT_EQ(test, props.scale, BACKLIGHT_SCALE_LINEAR); + KUNIT_EXPECT_EQ(test, props.type, BACKLIGHT_RAW); +} + +/* Tests for amdgpu_dm_update_connector_ext_caps() */ + +/** + * dm_test_update_connector_ext_caps_negative_bl_idx - Test negative backlight index early return + * @test: The KUnit test context + */ +static void dm_test_update_connector_ext_caps_negative_bl_idx(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector); + + aconnector->bl_idx = -1; + + amdgpu_dm_update_connector_ext_caps(aconnector); + + KUNIT_SUCCEED(test); +} + +/** + * dm_test_update_connector_ext_caps_non_edp - Test non-eDP connector early return + * @test: The KUnit test context + */ +static void dm_test_update_connector_ext_caps_non_edp(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + + setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_HDMI_TYPE_A); + fixture.adev->dm.backlight_caps[0].aux_support = true; + + amdgpu_dm_update_connector_ext_caps(fixture.aconnector); + + KUNIT_EXPECT_TRUE(test, fixture.adev->dm.backlight_caps[0].aux_support); + KUNIT_EXPECT_PTR_EQ(test, fixture.adev->dm.backlight_caps[0].ext_caps, NULL); +} + +/** + * dm_test_update_connector_ext_caps_oled_defaults - Test OLED eDP defaults to AUX backlight + * @test: The KUnit test context + */ +static void dm_test_update_connector_ext_caps_oled_defaults(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + int saved_backlight = amdgpu_dm_get_backlight_param(); + + amdgpu_dm_set_backlight_param(-1); + setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP); + fixture.link->dpcd_sink_ext_caps.bits.oled = 1; + + amdgpu_dm_update_connector_ext_caps(fixture.aconnector); + + KUNIT_EXPECT_PTR_EQ(test, fixture.adev->dm.backlight_caps[0].ext_caps, + &fixture.link->dpcd_sink_ext_caps); + KUNIT_EXPECT_TRUE(test, fixture.adev->dm.backlight_caps[0].aux_support); + KUNIT_EXPECT_EQ(test, fixture.link->backlight_control_type, + BACKLIGHT_CONTROL_AMD_AUX); + KUNIT_EXPECT_EQ(test, fixture.adev->dm.backlight_caps[0].aux_max_input_signal, 512); + KUNIT_EXPECT_EQ(test, fixture.adev->dm.backlight_caps[0].aux_min_input_signal, 1); + + amdgpu_dm_set_backlight_param(saved_backlight); +} + +/** + * dm_test_update_connector_ext_caps_luminance_values - Test luminance range copy + * @test: The KUnit test context + */ +static void dm_test_update_connector_ext_caps_luminance_values(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + int saved_backlight = amdgpu_dm_get_backlight_param(); + + amdgpu_dm_set_backlight_param(-1); + setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP); + fixture.aconnector->base.display_info.luminance_range.min_luminance = 2; + fixture.aconnector->base.display_info.luminance_range.max_luminance = 400; + + amdgpu_dm_update_connector_ext_caps(fixture.aconnector); + + KUNIT_EXPECT_FALSE(test, fixture.adev->dm.backlight_caps[0].aux_support); + KUNIT_EXPECT_EQ(test, fixture.adev->dm.backlight_caps[0].aux_max_input_signal, 400); + KUNIT_EXPECT_EQ(test, fixture.adev->dm.backlight_caps[0].aux_min_input_signal, 2); + + amdgpu_dm_set_backlight_param(saved_backlight); +} + +/** + * dm_test_update_connector_ext_caps_force_aux - Test module parameter forces AUX backlight + * @test: The KUnit test context + */ +static void dm_test_update_connector_ext_caps_force_aux(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + int saved_backlight = amdgpu_dm_get_backlight_param(); + + amdgpu_dm_set_backlight_param(1); + setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP); + + amdgpu_dm_update_connector_ext_caps(fixture.aconnector); + + KUNIT_EXPECT_TRUE(test, fixture.adev->dm.backlight_caps[0].aux_support); + KUNIT_EXPECT_EQ(test, fixture.link->backlight_control_type, + BACKLIGHT_CONTROL_AMD_AUX); + + amdgpu_dm_set_backlight_param(saved_backlight); +} + +/** + * dm_test_update_connector_ext_caps_force_pwm - Test module parameter forces PWM backlight + * @test: The KUnit test context + */ +static void dm_test_update_connector_ext_caps_force_pwm(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + int saved_backlight = amdgpu_dm_get_backlight_param(); + + amdgpu_dm_set_backlight_param(0); + setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP); + fixture.link->dpcd_sink_ext_caps.bits.oled = 1; + + amdgpu_dm_update_connector_ext_caps(fixture.aconnector); + + KUNIT_EXPECT_FALSE(test, fixture.adev->dm.backlight_caps[0].aux_support); + KUNIT_EXPECT_NE(test, fixture.link->backlight_control_type, + BACKLIGHT_CONTROL_AMD_AUX); + + amdgpu_dm_set_backlight_param(saved_backlight); +} + +/* Tests for amdgpu_dm_should_create_sysfs() */ + +/** + * dm_test_should_create_sysfs_abm_forced - Test forced ABM disables sysfs + * @test: The KUnit test context + */ +static void dm_test_should_create_sysfs_abm_forced(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + int saved_abm_level = amdgpu_dm_get_abm_level_param(); + + amdgpu_dm_set_abm_level_param(1); + setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP); + fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector)); + + amdgpu_dm_set_abm_level_param(saved_abm_level); +} + +/** + * dm_test_should_create_sysfs_non_edp - Test non-eDP connector disables sysfs + * @test: The KUnit test context + */ +static void dm_test_should_create_sysfs_non_edp(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + int saved_abm_level = amdgpu_dm_get_abm_level_param(); + + amdgpu_dm_set_abm_level_param(-1); + setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_HDMI_TYPE_A); + fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector)); + + amdgpu_dm_set_abm_level_param(saved_abm_level); +} + +/** + * dm_test_should_create_sysfs_no_backlight_index - Test eDP without backlight index enables sysfs + * @test: The KUnit test context + */ +static void dm_test_should_create_sysfs_no_backlight_index(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + int saved_abm_level = amdgpu_dm_get_abm_level_param(); + + amdgpu_dm_set_abm_level_param(-1); + setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP); + fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector)); + + amdgpu_dm_set_abm_level_param(saved_abm_level); +} + +/** + * dm_test_should_create_sysfs_aux_backlight - Test AUX backlight disables sysfs + * @test: The KUnit test context + */ +static void dm_test_should_create_sysfs_aux_backlight(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + int saved_abm_level = amdgpu_dm_get_abm_level_param(); + + amdgpu_dm_set_abm_level_param(-1); + setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP); + fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP; + fixture.adev->dm.backlight_caps[0].aux_support = true; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector)); + + amdgpu_dm_set_abm_level_param(saved_abm_level); +} + +/** + * dm_test_should_create_sysfs_pwm_backlight - Test PWM backlight enables sysfs + * @test: The KUnit test context + */ +static void dm_test_should_create_sysfs_pwm_backlight(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + int saved_abm_level = amdgpu_dm_get_abm_level_param(); + + amdgpu_dm_set_abm_level_param(-1); + setup_test_connector(test, &fixture, 0, SIGNAL_TYPE_EDP); + fixture.aconnector->base.connector_type = DRM_MODE_CONNECTOR_eDP; + fixture.adev->dm.backlight_caps[0].aux_support = false; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_should_create_sysfs(fixture.aconnector)); + + amdgpu_dm_set_abm_level_param(saved_abm_level); +} + +/* Tests for amdgpu_dm_setup_backlight_device() */ + +/** + * dm_test_setup_backlight_device_non_edp - Test non-eDP/LVDS link is skipped + * @test: The KUnit test context + */ +static void dm_test_setup_backlight_device_non_edp(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + struct amdgpu_display_manager *dm; + + setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_HDMI_TYPE_A); + fixture.link->type = dc_connection_single; + dm = &fixture.adev->dm; + dm->adev = fixture.adev; + dm->num_of_edps = 0; + + amdgpu_dm_setup_backlight_device(dm, fixture.aconnector); + + /* Non-eDP/LVDS signal → no backlight setup */ + KUNIT_EXPECT_EQ(test, dm->num_of_edps, 0); + KUNIT_EXPECT_EQ(test, fixture.aconnector->bl_idx, -1); +} + +/** + * dm_test_setup_backlight_device_connection_none - Test disconnected link is skipped + * @test: The KUnit test context + */ +static void dm_test_setup_backlight_device_connection_none(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + struct amdgpu_display_manager *dm; + + setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP); + fixture.link->type = dc_connection_none; + dm = &fixture.adev->dm; + dm->adev = fixture.adev; + dm->num_of_edps = 0; + + amdgpu_dm_setup_backlight_device(dm, fixture.aconnector); + + /* Disconnected link → no backlight setup */ + KUNIT_EXPECT_EQ(test, dm->num_of_edps, 0); + KUNIT_EXPECT_EQ(test, fixture.aconnector->bl_idx, -1); +} + +/** + * dm_test_setup_backlight_device_max_edps - Test setup is skipped when at eDP limit + * @test: The KUnit test context + */ +static void dm_test_setup_backlight_device_max_edps(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + struct amdgpu_display_manager *dm; + + setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP); + fixture.link->type = dc_connection_single; + dm = &fixture.adev->dm; + dm->adev = fixture.adev; + dm->num_of_edps = AMDGPU_DM_MAX_NUM_EDP; + + amdgpu_dm_setup_backlight_device(dm, fixture.aconnector); + + /* Already at the eDP limit → no additional setup */ + KUNIT_EXPECT_EQ(test, dm->num_of_edps, AMDGPU_DM_MAX_NUM_EDP); + KUNIT_EXPECT_EQ(test, fixture.aconnector->bl_idx, -1); +} + +/** + * dm_test_setup_backlight_device_oled_success - Test successful eDP backlight setup + * @test: The KUnit test context + */ +static void dm_test_setup_backlight_device_oled_success(struct kunit *test) +{ + struct dm_backlight_connector_fixture fixture = {}; + struct amdgpu_display_manager *dm; + int saved_backlight = amdgpu_dm_get_backlight_param(); + + amdgpu_dm_set_backlight_param(-1); + setup_test_connector(test, &fixture, -1, SIGNAL_TYPE_EDP); + fixture.link->type = dc_connection_single; + /* OLED panel avoids the ABM property attach path */ + fixture.link->dpcd_sink_ext_caps.bits.oled = 1; + dm = &fixture.adev->dm; + dm->adev = fixture.adev; + dm->num_of_edps = 0; + + amdgpu_dm_setup_backlight_device(dm, fixture.aconnector); + + KUNIT_EXPECT_EQ(test, dm->num_of_edps, 1); + KUNIT_EXPECT_EQ(test, fixture.aconnector->bl_idx, 0); + KUNIT_EXPECT_PTR_EQ(test, (void *)dm->backlight_link[0], + (void *)fixture.link); + KUNIT_EXPECT_TRUE(test, dm->backlight_caps[0].aux_support); + + amdgpu_dm_set_backlight_param(saved_backlight); +} + +static struct kunit_case dm_backlight_test_cases[] = { + /* amdgpu_dm_backlight_get_device_index */ + KUNIT_CASE(dm_test_backlight_device_index_matches_second), + KUNIT_CASE(dm_test_backlight_device_index_missing_fallback), + KUNIT_CASE(dm_test_backlight_caps_valid_short_circuit), +#if !defined(CONFIG_ACPI) + KUNIT_CASE(dm_test_backlight_caps_aux_support_noop), + KUNIT_CASE(dm_test_backlight_caps_non_aux_sets_defaults), +#endif + /* get_brightness_range */ + KUNIT_CASE(dm_test_brightness_range_null_caps), + KUNIT_CASE(dm_test_brightness_range_pwm), + KUNIT_CASE(dm_test_brightness_range_aux), + /* convert_brightness_to_user */ + KUNIT_CASE(dm_test_brightness_to_user_null_caps), + KUNIT_CASE(dm_test_brightness_to_user_below_min), + KUNIT_CASE(dm_test_brightness_to_user_at_max), + KUNIT_CASE(dm_test_brightness_to_user_at_min), + KUNIT_CASE(dm_test_brightness_to_user_midpoint_pwm), + /* convert_brightness_from_user */ + KUNIT_CASE(dm_test_brightness_from_user_null_caps), + KUNIT_CASE(dm_test_brightness_from_user_zero), + KUNIT_CASE(dm_test_brightness_from_user_max), + KUNIT_CASE(dm_test_brightness_from_user_aux), + /* convert_custom_brightness */ + KUNIT_CASE(dm_test_custom_brightness_no_data_points), + KUNIT_CASE(dm_test_custom_brightness_debug_mask_disables), + KUNIT_CASE(dm_test_custom_brightness_exact_match), + KUNIT_CASE(dm_test_custom_brightness_below_first), + KUNIT_CASE(dm_test_custom_brightness_interpolation), + KUNIT_CASE(dm_test_custom_brightness_above_last), + KUNIT_CASE(dm_test_custom_brightness_single_data_point), + KUNIT_CASE(dm_test_custom_brightness_lower_lum_zero), + KUNIT_CASE(dm_test_brightness_to_user_above_max), + KUNIT_CASE(dm_test_brightness_from_user_midrange), + KUNIT_CASE(dm_test_brightness_from_user_with_curve), + KUNIT_CASE(dm_test_brightness_range_zero_signals), + /* amdgpu_dm_backlight_fill_props */ + KUNIT_CASE(dm_test_backlight_fill_props_ac_linear), + KUNIT_CASE(dm_test_backlight_fill_props_dc_nonlinear), + KUNIT_CASE(dm_test_backlight_fill_props_default_range), + /* amdgpu_dm_update_connector_ext_caps */ + KUNIT_CASE(dm_test_update_connector_ext_caps_negative_bl_idx), + KUNIT_CASE(dm_test_update_connector_ext_caps_non_edp), + KUNIT_CASE(dm_test_update_connector_ext_caps_oled_defaults), + KUNIT_CASE(dm_test_update_connector_ext_caps_luminance_values), + KUNIT_CASE(dm_test_update_connector_ext_caps_force_aux), + KUNIT_CASE(dm_test_update_connector_ext_caps_force_pwm), + /* amdgpu_dm_should_create_sysfs */ + KUNIT_CASE(dm_test_should_create_sysfs_abm_forced), + KUNIT_CASE(dm_test_should_create_sysfs_non_edp), + KUNIT_CASE(dm_test_should_create_sysfs_no_backlight_index), + KUNIT_CASE(dm_test_should_create_sysfs_aux_backlight), + KUNIT_CASE(dm_test_should_create_sysfs_pwm_backlight), + /* amdgpu_dm_setup_backlight_device */ + KUNIT_CASE(dm_test_setup_backlight_device_non_edp), + KUNIT_CASE(dm_test_setup_backlight_device_connection_none), + KUNIT_CASE(dm_test_setup_backlight_device_max_edps), + KUNIT_CASE(dm_test_setup_backlight_device_oled_success), + {} +}; + +static struct kunit_suite dm_backlight_test_suite = { + .name = "amdgpu_dm_backlight", + .test_cases = dm_backlight_test_cases, +}; + +kunit_test_suite(dm_backlight_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_backlight"); +MODULE_AUTHOR("AMD"); 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 index f943361b70e8..d64c7da20f2c 100644 --- 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 @@ -1159,19 +1159,19 @@ static void dm_test_verify_lut_sizes_invalid_degamma_valid_gamma(struct kunit *t */ static void dm_test_atomic_lut3d_zero_size(struct kunit *test) { - struct dc_3dlut *lut; + struct dc_plane_cm *cm; u32 initialized; - lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, lut); + cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, cm); /* Pre-set initialized so we can confirm it is cleared */ - lut->state.bits.initialized = 1; + cm->lut3d_func.state.bits.initialized = 1; - amdgpu_dm_atomic_lut3d(NULL, 0, lut); + amdgpu_dm_atomic_lut3d(NULL, 0, cm); /* Copy bit-field: typeof cannot be applied to a bit-field */ - initialized = lut->state.bits.initialized; + initialized = cm->lut3d_func.state.bits.initialized; KUNIT_EXPECT_EQ(test, initialized, 0U); } @@ -1183,22 +1183,22 @@ static void dm_test_atomic_lut3d_nonzero_state_bits(struct kunit *test) { const uint32_t lut3d_size = 5; struct drm_color_lut *lut_data; - struct dc_3dlut *lut; + struct dc_plane_cm *cm; u32 initialized; lut_data = kunit_kcalloc(test, lut3d_size, sizeof(*lut_data), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, lut_data); - lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, lut); + cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, cm); - amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, lut); + amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, cm); /* Copy bit-field: typeof cannot be applied to a bit-field */ - initialized = lut->state.bits.initialized; + initialized = cm->lut3d_func.state.bits.initialized; KUNIT_EXPECT_EQ(test, initialized, 1U); - KUNIT_EXPECT_FALSE(test, lut->lut_3d.use_tetrahedral_9); - KUNIT_EXPECT_TRUE(test, lut->lut_3d.use_12bits); + KUNIT_EXPECT_FALSE(test, cm->lut3d_func.lut_3d.use_tetrahedral_9); + KUNIT_EXPECT_TRUE(test, cm->lut3d_func.lut_3d.use_12bits); } /** @@ -1209,29 +1209,29 @@ static void dm_test_atomic_lut3d_data_forwarded(struct kunit *test) { const uint32_t lut3d_size = 5; struct drm_color_lut *lut_data; - struct dc_3dlut *lut; + struct dc_plane_cm *cm; lut_data = kunit_kcalloc(test, lut3d_size, sizeof(*lut_data), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, lut_data); - lut = kunit_kzalloc(test, sizeof(*lut), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, lut); + cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, cm); lut_data[0].red = 0xFFFF; lut_data[0].green = 0x8000; lut_data[0].blue = 0x4000; - amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, lut); + amdgpu_dm_atomic_lut3d(lut_data, lut3d_size, cm); /* * use_tetrahedral_9 == false → data goes into tetrahedral_17. * lut[0] maps to lut0[0] (first element of the first group). */ - KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].red, + KUNIT_EXPECT_EQ(test, cm->lut3d_func.lut_3d.tetrahedral_17.lut0[0].red, drm_color_lut_extract(0xFFFF, MAX_COLOR_3DLUT_BITDEPTH)); - KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].green, + KUNIT_EXPECT_EQ(test, cm->lut3d_func.lut_3d.tetrahedral_17.lut0[0].green, drm_color_lut_extract(0x8000, MAX_COLOR_3DLUT_BITDEPTH)); - KUNIT_EXPECT_EQ(test, lut->lut_3d.tetrahedral_17.lut0[0].blue, + KUNIT_EXPECT_EQ(test, cm->lut3d_func.lut_3d.tetrahedral_17.lut0[0].blue, drm_color_lut_extract(0x4000, MAX_COLOR_3DLUT_BITDEPTH)); } @@ -1398,19 +1398,19 @@ static void dm_test_set_atomic_regamma_bypass(struct kunit *test) */ static void dm_test_atomic_shaper_lut_bypass(struct kunit *test) { - struct dc_transfer_func *func_shaper; + struct dc_plane_cm *cm; - func_shaper = kunit_kzalloc(test, sizeof(*func_shaper), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, func_shaper); + cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, cm); /* size=0 and tf=LINEAR: must take the bypass branch */ KUNIT_EXPECT_EQ(test, amdgpu_dm_atomic_shaper_lut(NULL, false, TRANSFER_FUNCTION_LINEAR, - 0, func_shaper), + 0, cm), 0); - KUNIT_EXPECT_EQ(test, (int)func_shaper->type, (int)TF_TYPE_BYPASS); - KUNIT_EXPECT_EQ(test, (int)func_shaper->tf, (int)TRANSFER_FUNCTION_LINEAR); + KUNIT_EXPECT_EQ(test, (int)cm->shaper_func.type, (int)TF_TYPE_BYPASS); + KUNIT_EXPECT_EQ(test, (int)cm->shaper_func.tf, (int)TRANSFER_FUNCTION_LINEAR); } /** @@ -1419,19 +1419,19 @@ static void dm_test_atomic_shaper_lut_bypass(struct kunit *test) */ static void dm_test_atomic_blend_lut_bypass(struct kunit *test) { - struct dc_transfer_func *func_blend; + struct dc_plane_cm *cm; - func_blend = kunit_kzalloc(test, sizeof(*func_blend), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, func_blend); + cm = kunit_kzalloc(test, sizeof(*cm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, cm); /* size=0 and tf=LINEAR: must take the bypass branch */ KUNIT_EXPECT_EQ(test, amdgpu_dm_atomic_blend_lut(NULL, false, TRANSFER_FUNCTION_LINEAR, - 0, func_blend), + 0, cm), 0); - KUNIT_EXPECT_EQ(test, (int)func_blend->type, (int)TF_TYPE_BYPASS); - KUNIT_EXPECT_EQ(test, (int)func_blend->tf, (int)TRANSFER_FUNCTION_LINEAR); + KUNIT_EXPECT_EQ(test, (int)cm->blend_func.type, (int)TF_TYPE_BYPASS); + KUNIT_EXPECT_EQ(test, (int)cm->blend_func.tf, (int)TRANSFER_FUNCTION_LINEAR); } /* ---- Tests for __set_colorop_in_tf_1d_curve ---- */ 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 index fa270ff28c6a..2e557ff66818 100644 --- 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 @@ -9,7 +9,10 @@ #include <drm/drm_colorop.h> #include <drm/drm_kunit_helpers.h> +#include "dc.h" +#include "amdgpu.h" #include "amdgpu_dm_colorop.h" +#include "amdgpu_dm_kunit_test_helpers.h" /* Tests for amdgpu_dm_supported_degam_tfs */ @@ -133,6 +136,30 @@ static void kunit_colorop_pipeline_destroy(void *drm) drm_colorop_pipeline_destroy((struct drm_device *)drm); } +static void dm_expect_colorop_pipeline(struct kunit *test, struct drm_device *drm, + const struct drm_prop_enum_list *list, + const enum drm_colorop_type *expected, + int expected_count) +{ + struct drm_colorop *op, *first = NULL; + int i = 0; + + drm_for_each_colorop(op, drm) { + if (op->base.id == (uint32_t)list->type) { + first = op; + break; + } + } + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, first); + + for (op = first; op; op = op->next, i++) { + KUNIT_ASSERT_LT(test, i, expected_count); + KUNIT_EXPECT_EQ(test, op->type, expected[i]); + KUNIT_EXPECT_NOT_NULL(test, op->bypass_property); + } + KUNIT_EXPECT_EQ(test, i, expected_count); +} + /** * dm_test_initialize_default_pipeline() - Verify amdgpu_dm_build_default_pipeline() * produces the expected colorop chain with all ops bypassable. @@ -154,8 +181,6 @@ static void dm_test_initialize_default_pipeline(struct kunit *test) struct drm_device *drm; struct drm_plane *plane; struct drm_prop_enum_list list = {}; - struct drm_colorop *op, *first = NULL; - int i = 0; int ret; dev = drm_kunit_helper_alloc_device(test); @@ -185,20 +210,102 @@ static void dm_test_initialize_default_pipeline(struct kunit *test) KUNIT_ASSERT_EQ(test, ret, 0); kfree(list.name); - drm_for_each_colorop(op, drm) { - if (op->base.id == (uint32_t)list.type) { - first = op; - break; - } - } - KUNIT_ASSERT_NOT_ERR_OR_NULL(test, first); + dm_expect_colorop_pipeline(test, drm, &list, expected, ARRAY_SIZE(expected)); +} - for (op = first; op; op = op->next, i++) { - KUNIT_ASSERT_LT(test, i, (int)ARRAY_SIZE(expected)); - KUNIT_EXPECT_EQ(test, op->type, expected[i]); - KUNIT_EXPECT_NOT_NULL(test, op->bypass_property); - } - KUNIT_EXPECT_EQ(test, i, (int)ARRAY_SIZE(expected)); +static void dm_test_initialize_default_pipeline_caps(struct kunit *test, + bool dpp_hw_3d_lut, + bool mpc_preblend, + const enum drm_colorop_type *expected, + int expected_count) +{ + struct drm_prop_enum_list list = {}; + struct amdgpu_device *adev; + struct drm_device *drm; + struct drm_plane *plane; + struct dc *dc; + int ret; + + adev = dm_kunit_alloc_adev(test); + drm = &adev->ddev; + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc); + adev->dm.dc = dc; + adev->dm.dc->caps.color.dpp.hw_3d_lut = dpp_hw_3d_lut; + adev->dm.dc->caps.color.mpc.preblend = mpc_preblend; + + plane = drm_kunit_helper_create_primary_plane(test, drm, + NULL, NULL, NULL, 0, NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane); + + kunit_add_action(test, kunit_colorop_pipeline_destroy, drm); + + ret = amdgpu_dm_initialize_default_pipeline(plane, &list); + KUNIT_ASSERT_EQ(test, ret, 0); + kfree(list.name); + + dm_expect_colorop_pipeline(test, drm, &list, expected, expected_count); +} + +/** + * dm_test_initialize_default_pipeline_dpp_3d_lut() - Test DPP 3D LUT cap. + * @test: KUnit test context. + */ +static void dm_test_initialize_default_pipeline_dpp_3d_lut(struct kunit *test) +{ + static const enum drm_colorop_type expected[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_3D_LUT, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + }; + + dm_test_initialize_default_pipeline_caps(test, true, false, + expected, ARRAY_SIZE(expected)); +} + +/** + * dm_test_initialize_default_pipeline_mpc_preblend() - Test MPC preblend cap. + * @test: KUnit test context. + */ +static void dm_test_initialize_default_pipeline_mpc_preblend(struct kunit *test) +{ + static const enum drm_colorop_type expected[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + DRM_COLOROP_3D_LUT, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + }; + + dm_test_initialize_default_pipeline_caps(test, false, true, + expected, ARRAY_SIZE(expected)); +} + +/** + * dm_test_initialize_default_pipeline_no_3d_lut() - Test no 3D LUT caps. + * @test: KUnit test context. + */ +static void dm_test_initialize_default_pipeline_no_3d_lut(struct kunit *test) +{ + static const enum drm_colorop_type expected[] = { + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_MULTIPLIER, + DRM_COLOROP_CTM_3X4, + DRM_COLOROP_1D_CURVE, + DRM_COLOROP_1D_LUT, + }; + + dm_test_initialize_default_pipeline_caps(test, false, false, + expected, ARRAY_SIZE(expected)); } static struct kunit_case dm_colorop_test_cases[] = { @@ -224,6 +331,9 @@ static struct kunit_case dm_colorop_test_cases[] = { KUNIT_CASE(dm_test_degam_and_blnd_tfs_match), /* amdgpu_dm_initialize_default_pipeline */ KUNIT_CASE(dm_test_initialize_default_pipeline), + KUNIT_CASE(dm_test_initialize_default_pipeline_dpp_3d_lut), + KUNIT_CASE(dm_test_initialize_default_pipeline_mpc_preblend), + KUNIT_CASE(dm_test_initialize_default_pipeline_no_3d_lut), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c new file mode 100644 index 000000000000..aa451064b30c --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_connector_test.c @@ -0,0 +1,2158 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_connector.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_edid.h> +#include <drm/drm_kunit_helpers.h> +#include <linux/hdmi.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_display.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_connector.h" +#include "amdgpu_dm_backlight.h" +#include "include/grph_object_id.h" + +/* Tests for get_subconnector_type() */ + +/** + * dm_test_subconnector_type_none - Test Subconnector type none + * @test: The KUnit test context + */ +static void dm_test_subconnector_type_none(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_NONE; + KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_Native); +} + +/** + * dm_test_subconnector_type_vga - Test Subconnector type vga + * @test: The KUnit test context + */ +static void dm_test_subconnector_type_vga(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_VGA_CONVERTER; + KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_VGA); +} + +/** + * dm_test_subconnector_type_dvi_converter - Test Subconnector type dvi converter + * @test: The KUnit test context + */ +static void dm_test_subconnector_type_dvi_converter(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_DVI_CONVERTER; + KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_DVID); +} + +/** + * dm_test_subconnector_type_dvi_dongle - Test Subconnector type dvi dongle + * @test: The KUnit test context + */ +static void dm_test_subconnector_type_dvi_dongle(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_DVI_DONGLE; + KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_DVID); +} + +/** + * dm_test_subconnector_type_hdmi_converter - Test Subconnector type hdmi converter + * @test: The KUnit test context + */ +static void dm_test_subconnector_type_hdmi_converter(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER; + KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_HDMIA); +} + +/** + * dm_test_subconnector_type_hdmi_dongle - Test Subconnector type hdmi dongle + * @test: The KUnit test context + */ +static void dm_test_subconnector_type_hdmi_dongle(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_DONGLE; + KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_HDMIA); +} + +/** + * dm_test_subconnector_type_mismatched - Test Subconnector type mismatched + * @test: The KUnit test context + */ +static void dm_test_subconnector_type_mismatched(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_MISMATCHED_DONGLE; + KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_Unknown); +} + +/** + * dm_test_subconnector_type_default_unknown - Test Subconnector type default unknown + * @test: The KUnit test context + */ +static void dm_test_subconnector_type_default_unknown(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.dongle_type = (typeof(link->dpcd_caps.dongle_type))0x7f; + KUNIT_EXPECT_EQ(test, (int)get_subconnector_type(link), (int)DRM_MODE_SUBCONNECTOR_Unknown); +} + +/* Tests for get_output_content_type() */ + +/** + * dm_test_content_type_no_data - Test Content type no data + * @test: The KUnit test context + */ +static void dm_test_content_type_no_data(struct kunit *test) +{ + struct drm_connector_state state = {}; + + state.content_type = DRM_MODE_CONTENT_TYPE_NO_DATA; + KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_NO_DATA); +} + +/** + * dm_test_content_type_graphics - Test Content type graphics + * @test: The KUnit test context + */ +static void dm_test_content_type_graphics(struct kunit *test) +{ + struct drm_connector_state state = {}; + + state.content_type = DRM_MODE_CONTENT_TYPE_GRAPHICS; + KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_GRAPHICS); +} + +/** + * dm_test_content_type_photo - Test Content type photo + * @test: The KUnit test context + */ +static void dm_test_content_type_photo(struct kunit *test) +{ + struct drm_connector_state state = {}; + + state.content_type = DRM_MODE_CONTENT_TYPE_PHOTO; + KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_PHOTO); +} + +/** + * dm_test_content_type_cinema - Test Content type cinema + * @test: The KUnit test context + */ +static void dm_test_content_type_cinema(struct kunit *test) +{ + struct drm_connector_state state = {}; + + state.content_type = DRM_MODE_CONTENT_TYPE_CINEMA; + KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_CINEMA); +} + +/** + * dm_test_content_type_game - Test Content type game + * @test: The KUnit test context + */ +static void dm_test_content_type_game(struct kunit *test) +{ + struct drm_connector_state state = {}; + + state.content_type = DRM_MODE_CONTENT_TYPE_GAME; + KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), (int)DISPLAY_CONTENT_TYPE_GAME); +} + +/** + * dm_test_content_type_unknown_defaults_no_data - Test unknown content type defaults to no data + * @test: The KUnit test context + */ +static void dm_test_content_type_unknown_defaults_no_data(struct kunit *test) +{ + struct drm_connector_state state = {}; + + state.content_type = 0x7f; + KUNIT_EXPECT_EQ(test, (int)get_output_content_type(&state), + (int)DISPLAY_CONTENT_TYPE_NO_DATA); +} + +/* Tests for adjust_colour_depth_from_display_info() */ + +/** + * dm_test_adjust_colour_depth_fits_at_888 - Test Adjust colour depth fits at 888 + * @test: The KUnit test context + */ +static void dm_test_adjust_colour_depth_fits_at_888(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_display_info info = {}; + + /* 1080p @ 148500 KHz = 1485000 in 100Hz units */ + timing.pix_clk_100hz = 1485000; + timing.display_color_depth = COLOR_DEPTH_888; + timing.pixel_encoding = PIXEL_ENCODING_RGB; + info.max_tmds_clock = 150000; /* 150 MHz */ + + KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info)); + KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_888); +} + +/** + * dm_test_adjust_colour_depth_reduces_to_888 - Test Adjust colour depth reduces to 888 + * @test: The KUnit test context + */ +static void dm_test_adjust_colour_depth_reduces_to_888(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_display_info info = {}; + + /* Request 10bpc but TMDS limit only allows 8bpc */ + timing.pix_clk_100hz = 1485000; + timing.display_color_depth = COLOR_DEPTH_101010; + timing.pixel_encoding = PIXEL_ENCODING_RGB; + /* 10bpc would need 148500*30/24 = 185625 KHz, exceeds limit */ + info.max_tmds_clock = 160000; + + KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info)); + KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_888); +} + +/** + * dm_test_adjust_colour_depth_10bpc_passes - Test Adjust colour depth 10bpc passes + * @test: The KUnit test context + */ +static void dm_test_adjust_colour_depth_10bpc_passes(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_display_info info = {}; + + timing.pix_clk_100hz = 1485000; + timing.display_color_depth = COLOR_DEPTH_101010; + timing.pixel_encoding = PIXEL_ENCODING_RGB; + /* 10bpc needs 185625 KHz, allow it */ + info.max_tmds_clock = 200000; + + KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info)); + KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_101010); +} + +/** + * dm_test_adjust_colour_depth_420_halves_clk - Test Adjust colour depth 420 halves clk + * @test: The KUnit test context + */ +static void dm_test_adjust_colour_depth_420_halves_clk(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_display_info info = {}; + + /* 4K @ 594000 KHz = 5940000 in 100Hz units */ + timing.pix_clk_100hz = 5940000; + timing.display_color_depth = COLOR_DEPTH_101010; + timing.pixel_encoding = PIXEL_ENCODING_YCBCR420; + /* With 420: effective = 594000/2 = 297000, 10bpc = 297000*30/24 = 371250 */ + info.max_tmds_clock = 400000; + + KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info)); + KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_101010); +} + +/** + * dm_test_adjust_colour_depth_reduces_12bpc_to_10bpc - Test Adjust colour + * depth reduces 12bpc to 10bpc + * @test: The KUnit test context + */ +static void dm_test_adjust_colour_depth_reduces_12bpc_to_10bpc(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_display_info info = {}; + + timing.pix_clk_100hz = 1485000; + timing.display_color_depth = COLOR_DEPTH_121212; + timing.pixel_encoding = PIXEL_ENCODING_RGB; + info.max_tmds_clock = 190000; + + KUNIT_EXPECT_TRUE(test, adjust_colour_depth_from_display_info(&timing, &info)); + KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_101010); +} + +/** + * dm_test_adjust_colour_depth_16bpc_no_fallback - Test Adjust colour depth + * 16bpc cannot fall back + * @test: The KUnit test context + */ +static void dm_test_adjust_colour_depth_16bpc_no_fallback(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_display_info info = {}; + + /* 16bpc that exceeds limit cannot reduce because the next enum + * value (COLOR_DEPTH_141414) is not a valid HDMI depth. + */ + timing.pix_clk_100hz = 1485000; + timing.display_color_depth = COLOR_DEPTH_161616; + timing.pixel_encoding = PIXEL_ENCODING_RGB; + info.max_tmds_clock = 230000; + + KUNIT_EXPECT_FALSE(test, adjust_colour_depth_from_display_info(&timing, &info)); +} + +/** + * dm_test_adjust_colour_depth_none_fits - Test Adjust colour depth none fits + * @test: The KUnit test context + */ +static void dm_test_adjust_colour_depth_none_fits(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_display_info info = {}; + + /* Even 8bpc doesn't fit */ + timing.pix_clk_100hz = 1485000; + timing.display_color_depth = COLOR_DEPTH_888; + timing.pixel_encoding = PIXEL_ENCODING_RGB; + info.max_tmds_clock = 100000; /* Too low */ + + KUNIT_EXPECT_FALSE(test, adjust_colour_depth_from_display_info(&timing, &info)); +} + +/** + * dm_test_adjust_colour_depth_invalid_depth - Test Adjust colour depth invalid depth + * @test: The KUnit test context + */ +static void dm_test_adjust_colour_depth_invalid_depth(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_display_info info = {}; + + timing.pix_clk_100hz = 1485000; + timing.display_color_depth = COLOR_DEPTH_141414; + timing.pixel_encoding = PIXEL_ENCODING_RGB; + info.max_tmds_clock = 400000; + + KUNIT_EXPECT_FALSE(test, adjust_colour_depth_from_display_info(&timing, &info)); + KUNIT_EXPECT_EQ(test, (int)timing.display_color_depth, (int)COLOR_DEPTH_141414); +} + +/* Tests for amdgpu_dm_get_output_color_space() */ + +/** + * dm_test_output_color_space_default_rgb_full - Test Output color space default rgb full + * @test: The KUnit test context + */ +static void dm_test_output_color_space_default_rgb_full(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.pixel_encoding = PIXEL_ENCODING_RGB; + state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT; + state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_AUTO; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_SRGB); +} + +/** + * dm_test_output_color_space_default_rgb_limited - Test Output color space default rgb limited + * @test: The KUnit test context + */ +static void dm_test_output_color_space_default_rgb_limited(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.pixel_encoding = PIXEL_ENCODING_RGB; + state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT; + state.hdmi.broadcast_rgb = DRM_HDMI_BROADCAST_RGB_LIMITED; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_SRGB_LIMITED); +} + +/** + * dm_test_output_color_space_default_ycbcr709 - Test Output color space default ycbcr709 + * @test: The KUnit test context + */ +static void dm_test_output_color_space_default_ycbcr709(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.pixel_encoding = PIXEL_ENCODING_YCBCR444; + timing.pix_clk_100hz = 300000; + timing.flags.Y_ONLY = 0; + state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_YCBCR709); +} + +/** + * dm_test_output_color_space_default_ycbcr601_limited - Test Output color space + * default ycbcr601 limited + * @test: The KUnit test context + */ +static void dm_test_output_color_space_default_ycbcr601_limited(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.pixel_encoding = PIXEL_ENCODING_YCBCR444; + timing.pix_clk_100hz = 270300; + timing.flags.Y_ONLY = 1; + state.colorspace = DRM_MODE_COLORIMETRY_DEFAULT; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_YCBCR601_LIMITED); +} + +/** + * dm_test_output_color_space_bt601_y_only - Test Output color space bt601 y only + * @test: The KUnit test context + */ +static void dm_test_output_color_space_bt601_y_only(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.flags.Y_ONLY = 1; + state.colorspace = DRM_MODE_COLORIMETRY_BT601_YCC; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_YCBCR601_LIMITED); +} + +/** + * dm_test_output_color_space_bt601 - Test Output color space bt601 + * @test: The KUnit test context + */ +static void dm_test_output_color_space_bt601(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.flags.Y_ONLY = 0; + state.colorspace = DRM_MODE_COLORIMETRY_BT601_YCC; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_YCBCR601); +} + +/** + * dm_test_output_color_space_bt709 - Test Output color space bt709 + * @test: The KUnit test context + */ +static void dm_test_output_color_space_bt709(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.flags.Y_ONLY = 0; + state.colorspace = DRM_MODE_COLORIMETRY_BT709_YCC; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_YCBCR709); +} + +/** + * dm_test_output_color_space_bt709_y_only - Test Output color space bt709 y only + * @test: The KUnit test context + */ +static void dm_test_output_color_space_bt709_y_only(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.flags.Y_ONLY = 1; + state.colorspace = DRM_MODE_COLORIMETRY_BT709_YCC; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_YCBCR709_LIMITED); +} + +/** + * dm_test_output_color_space_oprgb - Test Output color space oprgb + * @test: The KUnit test context + */ +static void dm_test_output_color_space_oprgb(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + state.colorspace = DRM_MODE_COLORIMETRY_OPRGB; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_ADOBERGB); +} + +/** + * dm_test_output_color_space_bt2020_rgb - Test Output color space bt2020 rgb + * @test: The KUnit test context + */ +static void dm_test_output_color_space_bt2020_rgb(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.pixel_encoding = PIXEL_ENCODING_RGB; + state.colorspace = DRM_MODE_COLORIMETRY_BT2020_RGB; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_2020_RGB_FULLRANGE); +} + +/** + * dm_test_output_color_space_bt2020_ycc - Test Output color space bt2020 ycc + * @test: The KUnit test context + */ +static void dm_test_output_color_space_bt2020_ycc(struct kunit *test) +{ + struct dc_crtc_timing timing = {}; + struct drm_connector_state state = {}; + + timing.pixel_encoding = PIXEL_ENCODING_YCBCR422; + state.colorspace = DRM_MODE_COLORIMETRY_BT2020_YCC; + + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_get_output_color_space(&timing, &state), + (int)COLOR_SPACE_2020_YCBCR_LIMITED); +} + +/* Tests for amdgpu_dm_convert_dc_color_depth_into_bpc() */ + +/** + * dm_test_convert_color_depth_bpc_mappings - Test Convert color depth bpc mappings + * @test: The KUnit test context + */ +static void dm_test_convert_color_depth_bpc_mappings(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_666), 6); + KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_888), 8); + KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_101010), 10); + KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_121212), 12); + KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_141414), 14); + KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_161616), 16); +} + +/** + * dm_test_convert_color_depth_bpc_unknown - Test Convert color depth bpc unknown + * @test: The KUnit test context + */ +static void dm_test_convert_color_depth_bpc_unknown(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, amdgpu_dm_convert_dc_color_depth_into_bpc(COLOR_DEPTH_UNDEFINED), 0); +} + +/* Tests for amdgpu_dm_convert_color_depth_from_display_info() */ + +/** + * dm_test_color_depth_from_info_bpc8 - Test Color depth from info bpc8 + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_bpc8(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + connector->display_info.bpc = 8; + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 0), + (int)COLOR_DEPTH_888); +} + +/** + * dm_test_color_depth_from_info_bpc10 - Test Color depth from info bpc10 + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_bpc10(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + connector->display_info.bpc = 10; + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 0), + (int)COLOR_DEPTH_101010); +} + +/** + * dm_test_color_depth_from_info_zero_bpc_defaults_888 - Test Color depth from + * info zero bpc defaults 888 + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_zero_bpc_defaults_888(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + connector->display_info.bpc = 0; + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 0), + (int)COLOR_DEPTH_888); +} + +/** + * dm_test_color_depth_from_info_requested_bpc_caps - Test Color depth from info requested bpc caps + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_requested_bpc_caps(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + /* Display supports 12bpc but user requests max 10 */ + connector->display_info.bpc = 12; + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 10), + (int)COLOR_DEPTH_101010); +} + +/** + * dm_test_color_depth_from_info_y420_default - Test Color depth from info y420 default + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_y420_default(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + /* No Y420 DC modes set → 8bpc */ + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, true, 0), + (int)COLOR_DEPTH_888); +} + +/** + * dm_test_color_depth_from_info_y420_10bpc - Test Color depth from info y420 10bpc + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_y420_10bpc(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + connector->display_info.hdmi.y420_dc_modes = DRM_EDID_YCBCR420_DC_30; + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, true, 0), + (int)COLOR_DEPTH_101010); +} + +/** + * dm_test_color_depth_from_info_y420_12bpc - Test Color depth from info y420 12bpc + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_y420_12bpc(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + connector->display_info.hdmi.y420_dc_modes = DRM_EDID_YCBCR420_DC_36; + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, true, 0), + (int)COLOR_DEPTH_121212); +} + +/** + * dm_test_color_depth_from_info_y420_16bpc - Test Color depth from info y420 16bpc + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_y420_16bpc(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + connector->display_info.hdmi.y420_dc_modes = DRM_EDID_YCBCR420_DC_48; + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, true, 0), + (int)COLOR_DEPTH_161616); +} + +/** + * dm_test_color_depth_from_info_requested_odd_bpc - Test Color depth from info requested odd bpc + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_requested_odd_bpc(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + connector->display_info.bpc = 12; + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 11), + (int)COLOR_DEPTH_101010); +} + +/** + * dm_test_color_depth_from_info_unsupported_bpc - Test Color depth from info unsupported bpc + * @test: The KUnit test context + */ +static void dm_test_color_depth_from_info_unsupported_bpc(struct kunit *test) +{ + struct drm_connector *connector; + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, connector); + + connector->display_info.bpc = 9; + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_convert_color_depth_from_display_info(connector, false, 0), + (int)COLOR_DEPTH_UNDEFINED); +} + +/* Tests for to_drm_connector_type() */ + +/** + * dm_test_to_connector_type_hdmi - Test To connector type hdmi + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_hdmi(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_HDMI_TYPE_A, 0), + DRM_MODE_CONNECTOR_HDMIA); +} + +/** + * dm_test_to_connector_type_edp - Test To connector type edp + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_edp(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_EDP, 0), + DRM_MODE_CONNECTOR_eDP); +} + +/** + * dm_test_to_connector_type_lvds - Test To connector type lvds + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_lvds(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_LVDS, 0), + DRM_MODE_CONNECTOR_LVDS); +} + +/** + * dm_test_to_connector_type_rgb - Test To connector type rgb + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_rgb(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_RGB, 0), + DRM_MODE_CONNECTOR_VGA); +} + +/** + * dm_test_to_connector_type_dp - Test To connector type dp + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_dp(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_DISPLAY_PORT, 0), + DRM_MODE_CONNECTOR_DisplayPort); +} + +/** + * dm_test_to_connector_type_dp_mst - Test To connector type dp mst + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_dp_mst(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_DISPLAY_PORT_MST, 0), + DRM_MODE_CONNECTOR_DisplayPort); +} + +/** + * dm_test_to_connector_type_dvi_dvii - Test To connector type dvi dvii + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_dvi_dvii(struct kunit *test) +{ + int type = to_drm_connector_type(SIGNAL_TYPE_DVI_SINGLE_LINK, CONNECTOR_ID_SINGLE_LINK_DVII); + + KUNIT_EXPECT_EQ(test, type, DRM_MODE_CONNECTOR_DVII); +} + +/** + * dm_test_to_connector_type_dual_link_dvii - Test To connector type dual link dvii + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_dual_link_dvii(struct kunit *test) +{ + int type = to_drm_connector_type(SIGNAL_TYPE_DVI_DUAL_LINK, CONNECTOR_ID_DUAL_LINK_DVII); + + KUNIT_EXPECT_EQ(test, type, DRM_MODE_CONNECTOR_DVII); +} + +/** + * dm_test_to_connector_type_dvi_dvid - Test To connector type dvi dvid + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_dvi_dvid(struct kunit *test) +{ + int type = to_drm_connector_type(SIGNAL_TYPE_DVI_SINGLE_LINK, CONNECTOR_ID_SINGLE_LINK_DVID); + + KUNIT_EXPECT_EQ(test, type, DRM_MODE_CONNECTOR_DVID); +} + +/** + * dm_test_to_connector_type_virtual - Test To connector type virtual + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_virtual(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_VIRTUAL, 0), + DRM_MODE_CONNECTOR_VIRTUAL); +} + +/** + * dm_test_to_connector_type_unknown - Test To connector type unknown + * @test: The KUnit test context + */ +static void dm_test_to_connector_type_unknown(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, to_drm_connector_type(SIGNAL_TYPE_NONE, 0), + DRM_MODE_CONNECTOR_Unknown); +} + +/* Tests for is_duplicate_mode() */ + +/** + * dm_test_is_duplicate_mode_empty_list - Test Is duplicate mode empty list + * @test: The KUnit test context + */ +static void dm_test_is_duplicate_mode_empty_list(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode mode = {}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector); + + INIT_LIST_HEAD(&aconnector->base.probed_modes); + mode.hdisplay = 1920; + mode.vdisplay = 1080; + + KUNIT_EXPECT_FALSE(test, is_duplicate_mode(aconnector, &mode)); +} + +/** + * dm_test_is_duplicate_mode_match - Test Is duplicate mode match + * @test: The KUnit test context + */ +static void dm_test_is_duplicate_mode_match(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode existing = {}; + struct drm_display_mode candidate = {}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector); + + INIT_LIST_HEAD(&aconnector->base.probed_modes); + existing.hdisplay = 1920; + existing.vdisplay = 1080; + existing.clock = 148500; + list_add_tail(&existing.head, &aconnector->base.probed_modes); + + candidate.hdisplay = 1920; + candidate.vdisplay = 1080; + candidate.clock = 148500; + + KUNIT_EXPECT_TRUE(test, is_duplicate_mode(aconnector, &candidate)); +} + +/** + * dm_test_is_duplicate_mode_no_match - Test Is duplicate mode no match + * @test: The KUnit test context + */ +static void dm_test_is_duplicate_mode_no_match(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode existing = {}; + struct drm_display_mode candidate = {}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector); + + INIT_LIST_HEAD(&aconnector->base.probed_modes); + existing.hdisplay = 1920; + existing.vdisplay = 1080; + existing.clock = 148500; + list_add_tail(&existing.head, &aconnector->base.probed_modes); + + candidate.hdisplay = 2560; + candidate.vdisplay = 1440; + candidate.clock = 241500; + + KUNIT_EXPECT_FALSE(test, is_duplicate_mode(aconnector, &candidate)); +} + +/** + * dm_test_is_duplicate_mode_same_size_different_clock - Test Is duplicate mode + * same size different clock + * @test: The KUnit test context + */ +static void dm_test_is_duplicate_mode_same_size_different_clock(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode existing = {}; + struct drm_display_mode candidate = {}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, aconnector); + + INIT_LIST_HEAD(&aconnector->base.probed_modes); + existing.hdisplay = 1920; + existing.vdisplay = 1080; + existing.clock = 148500; + list_add_tail(&existing.head, &aconnector->base.probed_modes); + + candidate.hdisplay = 1920; + candidate.vdisplay = 1080; + candidate.clock = 74250; + + KUNIT_EXPECT_FALSE(test, is_duplicate_mode(aconnector, &candidate)); +} + +/* Tests for amdgpu_dm_get_encoder_crtc_mask() */ + +/** + * dm_test_encoder_crtc_mask_1 - Test Encoder crtc mask 1 + * @test: The KUnit test context + */ +static void dm_test_encoder_crtc_mask_1(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->mode_info.num_crtc = 1; + KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x1); +} + +/** + * dm_test_encoder_crtc_mask_2 - Test Encoder crtc mask 2 + * @test: The KUnit test context + */ +static void dm_test_encoder_crtc_mask_2(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->mode_info.num_crtc = 2; + KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x3); +} + +/** + * dm_test_encoder_crtc_mask_3 - Test Encoder crtc mask 3 + * @test: The KUnit test context + */ +static void dm_test_encoder_crtc_mask_3(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->mode_info.num_crtc = 3; + KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x7); +} + +/** + * dm_test_encoder_crtc_mask_4 - Test Encoder crtc mask 4 + * @test: The KUnit test context + */ +static void dm_test_encoder_crtc_mask_4(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->mode_info.num_crtc = 4; + KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0xf); +} + +/** + * dm_test_encoder_crtc_mask_5 - Test Encoder crtc mask 5 + * @test: The KUnit test context + */ +static void dm_test_encoder_crtc_mask_5(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->mode_info.num_crtc = 5; + KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x1f); +} + +/** + * dm_test_encoder_crtc_mask_6 - Test Encoder crtc mask 6 + * @test: The KUnit test context + */ +static void dm_test_encoder_crtc_mask_6(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->mode_info.num_crtc = 6; + KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x3f); +} + +/** + * dm_test_encoder_crtc_mask_default - Test Encoder crtc mask default + * @test: The KUnit test context + */ +static void dm_test_encoder_crtc_mask_default(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + /* Values > 6 use the default case */ + adev->mode_info.num_crtc = 8; + KUNIT_EXPECT_EQ(test, amdgpu_dm_get_encoder_crtc_mask(adev), 0x3f); +} + +/* Tests for get_aspect_ratio() */ + +/** + * dm_test_aspect_ratio_no_data - Test Aspect ratio no data + * @test: The KUnit test context + */ +static void dm_test_aspect_ratio_no_data(struct kunit *test) +{ + struct drm_display_mode mode = {}; + + mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_NONE; + KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_NO_DATA); +} + +/** + * dm_test_aspect_ratio_4_3 - Test Aspect ratio 4 3 + * @test: The KUnit test context + */ +static void dm_test_aspect_ratio_4_3(struct kunit *test) +{ + struct drm_display_mode mode = {}; + + mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3; + KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_4_3); +} + +/** + * dm_test_aspect_ratio_16_9 - Test Aspect ratio 16 9 + * @test: The KUnit test context + */ +static void dm_test_aspect_ratio_16_9(struct kunit *test) +{ + struct drm_display_mode mode = {}; + + mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9; + KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_16_9); +} + +/** + * dm_test_aspect_ratio_64_27 - Test Aspect ratio 64 27 + * @test: The KUnit test context + */ +static void dm_test_aspect_ratio_64_27(struct kunit *test) +{ + struct drm_display_mode mode = {}; + + mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_64_27; + KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_64_27); +} + +/** + * dm_test_aspect_ratio_256_135 - Test Aspect ratio 256 135 + * @test: The KUnit test context + */ +static void dm_test_aspect_ratio_256_135(struct kunit *test) +{ + struct drm_display_mode mode = {}; + + mode.picture_aspect_ratio = HDMI_PICTURE_ASPECT_256_135; + KUNIT_EXPECT_EQ(test, (int)get_aspect_ratio(&mode), (int)ASPECT_RATIO_256_135); +} + +/* Tests for decide_crtc_timing_for_drm_display_mode() */ + +/** + * dm_test_decide_crtc_timing_scale_enabled - Test Decide crtc timing scale enabled + * @test: The KUnit test context + */ +static void dm_test_decide_crtc_timing_scale_enabled(struct kunit *test) +{ + struct drm_display_mode drm_mode = {}; + struct drm_display_mode native_mode = {}; + + native_mode.crtc_clock = 148500; + native_mode.crtc_hdisplay = 1920; + native_mode.crtc_vdisplay = 1080; + native_mode.crtc_htotal = 2200; + native_mode.crtc_vtotal = 1125; + native_mode.crtc_hsync_start = 2008; + native_mode.crtc_hsync_end = 2052; + native_mode.crtc_vsync_start = 1084; + native_mode.crtc_vsync_end = 1089; + + /* Different clock/htotal/vtotal, but scale_enabled forces copy */ + drm_mode.clock = 74250; + drm_mode.htotal = 1650; + drm_mode.vtotal = 750; + + decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, true); + + KUNIT_EXPECT_EQ(test, drm_mode.crtc_clock, 148500); + KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 1920); + KUNIT_EXPECT_EQ(test, drm_mode.crtc_vdisplay, 1080); + KUNIT_EXPECT_EQ(test, drm_mode.crtc_htotal, 2200); + KUNIT_EXPECT_EQ(test, drm_mode.crtc_vtotal, 1125); +} + +/** + * dm_test_decide_crtc_timing_matching_mode - Test Decide crtc timing matching mode + * @test: The KUnit test context + */ +static void dm_test_decide_crtc_timing_matching_mode(struct kunit *test) +{ + struct drm_display_mode drm_mode = {}; + struct drm_display_mode native_mode = {}; + + native_mode.clock = 148500; + native_mode.htotal = 2200; + native_mode.vtotal = 1125; + native_mode.crtc_clock = 148500; + native_mode.crtc_hdisplay = 1920; + native_mode.crtc_vdisplay = 1080; + native_mode.crtc_htotal = 2200; + native_mode.crtc_vtotal = 1125; + + /* Matching clock/htotal/vtotal triggers copy */ + drm_mode.clock = 148500; + drm_mode.htotal = 2200; + drm_mode.vtotal = 1125; + + decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, false); + + KUNIT_EXPECT_EQ(test, drm_mode.crtc_clock, 148500); + KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 1920); + KUNIT_EXPECT_EQ(test, drm_mode.crtc_vtotal, 1125); +} + +/** + * dm_test_decide_crtc_timing_no_copy - Test Decide crtc timing no copy + * @test: The KUnit test context + */ +static void dm_test_decide_crtc_timing_no_copy(struct kunit *test) +{ + struct drm_display_mode drm_mode = {}; + struct drm_display_mode native_mode = {}; + + native_mode.clock = 148500; + native_mode.htotal = 2200; + native_mode.vtotal = 1125; + native_mode.crtc_clock = 148500; + native_mode.crtc_hdisplay = 1920; + + /* Different timings, no scaling → no copy */ + drm_mode.clock = 74250; + drm_mode.htotal = 1650; + drm_mode.vtotal = 750; + + decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, false); + + KUNIT_EXPECT_EQ(test, drm_mode.crtc_clock, 0); + KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 0); +} + +/** + * dm_test_decide_crtc_timing_no_crtc_clock - Test Decide crtc timing no crtc clock + * @test: The KUnit test context + */ +static void dm_test_decide_crtc_timing_no_crtc_clock(struct kunit *test) +{ + struct drm_display_mode drm_mode = {}; + struct drm_display_mode native_mode = {}; + + /* Matching timings but native crtc_clock is 0 → no copy */ + native_mode.clock = 148500; + native_mode.htotal = 2200; + native_mode.vtotal = 1125; + native_mode.crtc_clock = 0; + native_mode.crtc_hdisplay = 1920; + + drm_mode.clock = 148500; + drm_mode.htotal = 2200; + drm_mode.vtotal = 1125; + + decide_crtc_timing_for_drm_display_mode(&drm_mode, &native_mode, false); + + KUNIT_EXPECT_EQ(test, drm_mode.crtc_clock, 0); + KUNIT_EXPECT_EQ(test, drm_mode.crtc_hdisplay, 0); +} + +/* Tests for amdgpu_dm_connector_funcs_reset() */ + +static const struct drm_connector_funcs dm_test_connector_funcs = { + .reset = amdgpu_dm_connector_funcs_reset, + .atomic_duplicate_state = amdgpu_dm_connector_atomic_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +/** + * dm_test_funcs_reset_sets_defaults - Test funcs_reset sets defaults + * @test: The KUnit test context + */ +static void dm_test_funcs_reset_sets_defaults(struct kunit *test) +{ + struct device *dev; + struct drm_device *drm; + struct drm_connector *connector; + struct dm_connector_state *dm_state; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, + sizeof(*drm), 0, + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, connector); + + drmm_connector_init(drm, connector, &dm_test_connector_funcs, + DRM_MODE_CONNECTOR_DisplayPort, NULL); + + amdgpu_dm_connector_funcs_reset(connector); + + KUNIT_ASSERT_NOT_NULL(test, connector->state); + dm_state = to_dm_connector_state(connector->state); + KUNIT_EXPECT_EQ(test, (int)dm_state->scaling, (int)RMX_OFF); + KUNIT_EXPECT_FALSE(test, dm_state->underscan_enable); + KUNIT_EXPECT_EQ(test, (int)dm_state->underscan_hborder, 0); + KUNIT_EXPECT_EQ(test, (int)dm_state->underscan_vborder, 0); + KUNIT_EXPECT_EQ(test, (int)dm_state->base.max_requested_bpc, 8); + KUNIT_EXPECT_EQ(test, dm_state->vcpi_slots, 0); + KUNIT_EXPECT_EQ(test, (int)dm_state->pbn, 0); +} + +/** + * dm_test_funcs_reset_edp_abm_level - Test funcs_reset eDP sets ABM + * @test: The KUnit test context + */ +static void dm_test_funcs_reset_edp_abm_level(struct kunit *test) +{ + struct device *dev; + struct drm_device *drm; + struct drm_connector *connector; + struct dm_connector_state *dm_state; + int saved_abm_level = amdgpu_dm_get_abm_level_param(); + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, + sizeof(*drm), 0, + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, connector); + + drmm_connector_init(drm, connector, &dm_test_connector_funcs, + DRM_MODE_CONNECTOR_eDP, NULL); + + /* Test with abm_level > 0 */ + amdgpu_dm_set_abm_level_param(3); + amdgpu_dm_connector_funcs_reset(connector); + + KUNIT_ASSERT_NOT_NULL(test, connector->state); + dm_state = to_dm_connector_state(connector->state); + KUNIT_EXPECT_EQ(test, (int)dm_state->abm_level, 3); + + amdgpu_dm_set_abm_level_param(saved_abm_level); +} + +/** + * dm_test_funcs_reset_edp_abm_disabled - Test funcs_reset eDP ABM + * disabled + * @test: The KUnit test context + */ +static void dm_test_funcs_reset_edp_abm_disabled(struct kunit *test) +{ + struct device *dev; + struct drm_device *drm; + struct drm_connector *connector; + struct dm_connector_state *dm_state; + int saved_abm_level = amdgpu_dm_get_abm_level_param(); + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, + sizeof(*drm), 0, + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, connector); + + drmm_connector_init(drm, connector, &dm_test_connector_funcs, + DRM_MODE_CONNECTOR_eDP, NULL); + + /* Test with abm_level <= 0 → immediate disable */ + amdgpu_dm_set_abm_level_param(-1); + amdgpu_dm_connector_funcs_reset(connector); + + KUNIT_ASSERT_NOT_NULL(test, connector->state); + dm_state = to_dm_connector_state(connector->state); + KUNIT_EXPECT_EQ(test, (int)dm_state->abm_level, + (int)ABM_LEVEL_IMMEDIATE_DISABLE); + + amdgpu_dm_set_abm_level_param(saved_abm_level); +} + +/* Tests for amdgpu_dm_connector_atomic_duplicate_state() */ + +/** + * dm_test_atomic_dup_state_copies_fields - Test atomic_duplicate copies + * fields + * @test: The KUnit test context + */ +static void dm_test_atomic_dup_state_copies_fields(struct kunit *test) +{ + struct device *dev; + struct drm_device *drm; + struct drm_connector *connector; + struct dm_connector_state *dm_state; + struct dm_connector_state *new_dm_state; + struct drm_connector_state *new_state; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, + sizeof(*drm), 0, + DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, connector); + + drmm_connector_init(drm, connector, &dm_test_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA, NULL); + + amdgpu_dm_connector_funcs_reset(connector); + KUNIT_ASSERT_NOT_NULL(test, connector->state); + + /* Modify original state fields */ + dm_state = to_dm_connector_state(connector->state); + dm_state->scaling = RMX_CENTER; + dm_state->underscan_enable = true; + dm_state->underscan_hborder = 10; + dm_state->underscan_vborder = 20; + dm_state->freesync_capable = true; + dm_state->abm_level = 2; + dm_state->vcpi_slots = 4; + dm_state->pbn = 1234; + + /* Duplicate */ + new_state = amdgpu_dm_connector_atomic_duplicate_state(connector); + KUNIT_ASSERT_NOT_NULL(test, new_state); + new_dm_state = to_dm_connector_state(new_state); + + /* Verify all fields copied */ + KUNIT_EXPECT_EQ(test, (int)new_dm_state->scaling, (int)RMX_CENTER); + KUNIT_EXPECT_TRUE(test, new_dm_state->underscan_enable); + KUNIT_EXPECT_EQ(test, (int)new_dm_state->underscan_hborder, 10); + KUNIT_EXPECT_EQ(test, (int)new_dm_state->underscan_vborder, 20); + KUNIT_EXPECT_TRUE(test, new_dm_state->freesync_capable); + KUNIT_EXPECT_EQ(test, (int)new_dm_state->abm_level, 2); + KUNIT_EXPECT_EQ(test, new_dm_state->vcpi_slots, 4); + KUNIT_EXPECT_EQ(test, (int)new_dm_state->pbn, 1234); + + kfree(new_dm_state); +} + +/* Tests for amdgpu_dm_fill_hdr_info_packet() */ + +/** + * dm_test_fill_hdr_null_metadata - Test fill_hdr returns 0 with no + * metadata + * @test: The KUnit test context + */ +static void dm_test_fill_hdr_null_metadata(struct kunit *test) +{ + struct drm_connector_state state = {}; + struct dc_info_packet out = {}; + + /* No hdr_output_metadata → early return 0, out stays zeroed */ + state.hdr_output_metadata = NULL; + KUNIT_EXPECT_EQ(test, amdgpu_dm_fill_hdr_info_packet(&state, &out), 0); + KUNIT_EXPECT_FALSE(test, out.valid); +} + +/** + * dm_test_fill_hdr_zeroes_output - Test fill_hdr zeroes output with no + * metadata + * @test: The KUnit test context + */ +static void dm_test_fill_hdr_zeroes_output(struct kunit *test) +{ + struct drm_connector_state state = {}; + struct dc_info_packet out; + + /* Pre-fill out with nonzero to verify memset(0) */ + memset(&out, 0xAA, sizeof(out)); + + state.hdr_output_metadata = NULL; + KUNIT_EXPECT_EQ(test, amdgpu_dm_fill_hdr_info_packet(&state, &out), 0); + KUNIT_EXPECT_FALSE(test, out.valid); + KUNIT_EXPECT_EQ(test, (int)out.hb0, 0); + KUNIT_EXPECT_EQ(test, (int)out.hb1, 0); + KUNIT_EXPECT_EQ(test, (int)out.hb2, 0); + KUNIT_EXPECT_EQ(test, (int)out.hb3, 0); +} + +/* Tests for amdgpu_dm_connector_atomic_set_property() */ + +/* + * Build a connector wired to a kunit-allocated amdgpu_device so that + * drm_to_adev() resolves correctly, together with old/new dm states and + * the set of properties used by the get/set property handlers. + */ +struct dm_test_prop_ctx { + struct amdgpu_device *adev; + struct drm_connector *connector; + struct dm_connector_state *old_state; + struct dm_connector_state *new_state; + struct drm_property *scaling_prop; + struct drm_property *hborder_prop; + struct drm_property *vborder_prop; + struct drm_property *underscan_prop; + struct drm_property *abm_prop; +}; + +static struct dm_test_prop_ctx *dm_test_prop_ctx_alloc(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + ctx->adev = kunit_kzalloc(test, sizeof(*ctx->adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->adev); + ctx->connector = kunit_kzalloc(test, sizeof(*ctx->connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->connector); + ctx->old_state = kunit_kzalloc(test, sizeof(*ctx->old_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->old_state); + ctx->new_state = kunit_kzalloc(test, sizeof(*ctx->new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->new_state); + ctx->scaling_prop = kunit_kzalloc(test, sizeof(*ctx->scaling_prop), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->scaling_prop); + ctx->hborder_prop = kunit_kzalloc(test, sizeof(*ctx->hborder_prop), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->hborder_prop); + ctx->vborder_prop = kunit_kzalloc(test, sizeof(*ctx->vborder_prop), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->vborder_prop); + ctx->underscan_prop = kunit_kzalloc(test, sizeof(*ctx->underscan_prop), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->underscan_prop); + ctx->abm_prop = kunit_kzalloc(test, sizeof(*ctx->abm_prop), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->abm_prop); + + ctx->connector->dev = &ctx->adev->ddev; + ctx->connector->state = &ctx->old_state->base; + + ctx->adev->ddev.mode_config.scaling_mode_property = ctx->scaling_prop; + ctx->adev->mode_info.underscan_hborder_property = ctx->hborder_prop; + ctx->adev->mode_info.underscan_vborder_property = ctx->vborder_prop; + ctx->adev->mode_info.underscan_property = ctx->underscan_prop; + ctx->adev->mode_info.abm_level_property = ctx->abm_prop; + + return ctx; +} + +/** + * dm_test_set_property_scaling_center - Test set scaling property to center + * @test: The KUnit test context + */ +static void dm_test_set_property_scaling_center(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->scaling_prop, DRM_MODE_SCALE_CENTER), 0); + KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_CENTER); +} + +/** + * dm_test_set_property_scaling_aspect - Test set scaling property to aspect + * @test: The KUnit test context + */ +static void dm_test_set_property_scaling_aspect(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->scaling_prop, DRM_MODE_SCALE_ASPECT), 0); + KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_ASPECT); +} + +/** + * dm_test_set_property_scaling_fullscreen - Test set scaling property to full + * @test: The KUnit test context + */ +static void dm_test_set_property_scaling_fullscreen(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->scaling_prop, DRM_MODE_SCALE_FULLSCREEN), 0); + KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_FULL); +} + +/** + * dm_test_set_property_scaling_none - Test set scaling property to none + * @test: The KUnit test context + */ +static void dm_test_set_property_scaling_none(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + /* old scaling is RMX_CENTER so RMX_OFF is a real change */ + ctx->old_state->scaling = RMX_CENTER; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->scaling_prop, DRM_MODE_SCALE_NONE), 0); + KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_OFF); +} + +/** + * dm_test_set_property_scaling_unchanged - Test set scaling property unchanged + * @test: The KUnit test context + */ +static void dm_test_set_property_scaling_unchanged(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + /* old already RMX_OFF, requesting NONE/OFF returns 0 without write */ + ctx->old_state->scaling = RMX_OFF; + ctx->new_state->scaling = RMX_CENTER; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->scaling_prop, DRM_MODE_SCALE_NONE), 0); + /* new_state untouched because of early return */ + KUNIT_EXPECT_EQ(test, (int)ctx->new_state->scaling, (int)RMX_CENTER); +} + +/** + * dm_test_set_property_underscan_hborder - Test set underscan hborder + * @test: The KUnit test context + */ +static void dm_test_set_property_underscan_hborder(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->hborder_prop, 42), 0); + KUNIT_EXPECT_EQ(test, (int)ctx->new_state->underscan_hborder, 42); +} + +/** + * dm_test_set_property_underscan_vborder - Test set underscan vborder + * @test: The KUnit test context + */ +static void dm_test_set_property_underscan_vborder(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->vborder_prop, 24), 0); + KUNIT_EXPECT_EQ(test, (int)ctx->new_state->underscan_vborder, 24); +} + +/** + * dm_test_set_property_underscan_enable - Test set underscan enable + * @test: The KUnit test context + */ +static void dm_test_set_property_underscan_enable(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->underscan_prop, 1), 0); + KUNIT_EXPECT_TRUE(test, ctx->new_state->underscan_enable); +} + +/** + * dm_test_set_property_abm_sysfs_control - Test set abm sysfs control + * @test: The KUnit test context + */ +static void dm_test_set_property_abm_sysfs_control(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + ctx->new_state->abm_sysfs_forbidden = true; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->abm_prop, ABM_SYSFS_CONTROL), 0); + KUNIT_EXPECT_FALSE(test, ctx->new_state->abm_sysfs_forbidden); +} + +/** + * dm_test_set_property_abm_level_off - Test set abm level off + * @test: The KUnit test context + */ +static void dm_test_set_property_abm_level_off(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->abm_prop, ABM_LEVEL_OFF), 0); + KUNIT_EXPECT_TRUE(test, ctx->new_state->abm_sysfs_forbidden); + KUNIT_EXPECT_EQ(test, (int)ctx->new_state->abm_level, + (int)ABM_LEVEL_IMMEDIATE_DISABLE); +} + +/** + * dm_test_set_property_abm_level_value - Test set abm level to a value + * @test: The KUnit test context + */ +static void dm_test_set_property_abm_level_value(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + ctx->abm_prop, 3), 0); + KUNIT_EXPECT_TRUE(test, ctx->new_state->abm_sysfs_forbidden); + KUNIT_EXPECT_EQ(test, (int)ctx->new_state->abm_level, 3); +} + +/** + * dm_test_set_property_unknown - Test set unknown property returns -EINVAL + * @test: The KUnit test context + */ +static void dm_test_set_property_unknown(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + struct drm_property *other; + + other = kunit_kzalloc(test, sizeof(*other), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, other); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_set_property( + ctx->connector, &ctx->new_state->base, + other, 0), -EINVAL); +} + +/* Tests for amdgpu_dm_connector_atomic_get_property() */ + +/** + * dm_test_get_property_scaling_center - Test get scaling property center + * @test: The KUnit test context + */ +static void dm_test_get_property_scaling_center(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + uint64_t val = 0; + + ctx->new_state->scaling = RMX_CENTER; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->scaling_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, (int)DRM_MODE_SCALE_CENTER); +} + +/** + * dm_test_get_property_scaling_aspect - Test get scaling property aspect + * @test: The KUnit test context + */ +static void dm_test_get_property_scaling_aspect(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + uint64_t val = 0; + + ctx->new_state->scaling = RMX_ASPECT; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->scaling_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, (int)DRM_MODE_SCALE_ASPECT); +} + +/** + * dm_test_get_property_scaling_full - Test get scaling property fullscreen + * @test: The KUnit test context + */ +static void dm_test_get_property_scaling_full(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + uint64_t val = 0; + + ctx->new_state->scaling = RMX_FULL; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->scaling_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, (int)DRM_MODE_SCALE_FULLSCREEN); +} + +/** + * dm_test_get_property_scaling_off - Test get scaling property off/none + * @test: The KUnit test context + */ +static void dm_test_get_property_scaling_off(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + uint64_t val = 0; + + ctx->new_state->scaling = RMX_OFF; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->scaling_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, (int)DRM_MODE_SCALE_NONE); +} + +/** + * dm_test_get_property_underscan_borders - Test get underscan borders/enable + * @test: The KUnit test context + */ +static void dm_test_get_property_underscan_borders(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + uint64_t val = 0; + + ctx->new_state->underscan_hborder = 12; + ctx->new_state->underscan_vborder = 34; + ctx->new_state->underscan_enable = true; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->hborder_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, 12); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->vborder_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, 34); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->underscan_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, 1); +} + +/** + * dm_test_get_property_abm_sysfs_allowed - Test get abm returns sysfs control + * @test: The KUnit test context + */ +static void dm_test_get_property_abm_sysfs_allowed(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + uint64_t val = 0; + + ctx->new_state->abm_sysfs_forbidden = false; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->abm_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, (int)ABM_SYSFS_CONTROL); +} + +/** + * dm_test_get_property_abm_level - Test get abm returns level when forbidden + * @test: The KUnit test context + */ +static void dm_test_get_property_abm_level(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + uint64_t val = 0; + + ctx->new_state->abm_sysfs_forbidden = true; + ctx->new_state->abm_level = 2; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->abm_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, 2); +} + +/** + * dm_test_get_property_abm_disabled_zero - Test get abm returns 0 when disabled + * @test: The KUnit test context + */ +static void dm_test_get_property_abm_disabled_zero(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + uint64_t val = 0xdead; + + ctx->new_state->abm_sysfs_forbidden = true; + ctx->new_state->abm_level = ABM_LEVEL_IMMEDIATE_DISABLE; + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + ctx->abm_prop, &val), 0); + KUNIT_EXPECT_EQ(test, (int)val, 0); +} + +/** + * dm_test_get_property_unknown - Test get unknown property returns -EINVAL + * @test: The KUnit test context + */ +static void dm_test_get_property_unknown(struct kunit *test) +{ + struct dm_test_prop_ctx *ctx = dm_test_prop_ctx_alloc(test); + struct drm_property *other; + uint64_t val = 0; + + other = kunit_kzalloc(test, sizeof(*other), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, other); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_connector_atomic_get_property( + ctx->connector, &ctx->new_state->base, + other, &val), -EINVAL); +} + +/* Tests for amdgpu_dm_get_highest_refresh_rate_mode() */ + +/** + * dm_test_highest_refresh_writeback_null - Test writeback connector returns NULL + * @test: The KUnit test context + */ +static void dm_test_highest_refresh_writeback_null(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.connector_type = DRM_MODE_CONNECTOR_WRITEBACK; + KUNIT_EXPECT_NULL(test, amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false)); +} + +/** + * dm_test_highest_refresh_cached_base - Test cached freesync_vid_base is returned + * @test: The KUnit test context + */ +static void dm_test_highest_refresh_cached_base(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + aconnector->freesync_vid_base.clock = 148500; + + KUNIT_EXPECT_PTR_EQ(test, amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false), + &aconnector->freesync_vid_base); +} + +/** + * dm_test_highest_refresh_preferred_mode - Test preferred mode is selected + * @test: The KUnit test context + */ +static void dm_test_highest_refresh_preferred_mode(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode *mode; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + mode = kunit_kzalloc(test, sizeof(*mode), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, mode); + + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + INIT_LIST_HEAD(&aconnector->base.modes); + + mode->type = DRM_MODE_TYPE_PREFERRED; + mode->clock = 148500; + mode->hdisplay = 1920; + mode->vdisplay = 1080; + mode->htotal = 2200; + mode->vtotal = 1125; + list_add_tail(&mode->head, &aconnector->base.modes); + + KUNIT_EXPECT_PTR_EQ(test, amdgpu_dm_get_highest_refresh_rate_mode(aconnector, false), + mode); +} + +/* Tests for amdgpu_dm_is_freesync_video_mode() */ + +/** + * dm_test_is_freesync_video_mode_null_mode - Test NULL mode returns false + * @test: The KUnit test context + */ +static void dm_test_is_freesync_video_mode_null_mode(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + aconnector->freesync_vid_base.clock = 148500; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_freesync_video_mode(NULL, aconnector)); +} + +/** + * dm_test_is_freesync_video_mode_match - Test matching mode returns true + * @test: The KUnit test context + */ +static void dm_test_is_freesync_video_mode_match(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode candidate = {}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + /* Cached high mode acts as reference */ + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + aconnector->freesync_vid_base.clock = 148500; + aconnector->freesync_vid_base.hdisplay = 1920; + aconnector->freesync_vid_base.vdisplay = 1080; + aconnector->freesync_vid_base.hsync_start = 2008; + aconnector->freesync_vid_base.hsync_end = 2052; + aconnector->freesync_vid_base.htotal = 2200; + aconnector->freesync_vid_base.vsync_start = 1084; + aconnector->freesync_vid_base.vsync_end = 1089; + aconnector->freesync_vid_base.vtotal = 1125; + + candidate.clock = 148500; + candidate.hdisplay = 1920; + candidate.vdisplay = 1080; + candidate.hsync_start = 2008; + candidate.hsync_end = 2052; + candidate.htotal = 2200; + candidate.vsync_start = 1084; + candidate.vsync_end = 1089; + candidate.vtotal = 1125; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_freesync_video_mode(&candidate, aconnector)); +} + +/** + * dm_test_is_freesync_video_mode_no_match - Test mismatched mode returns false + * @test: The KUnit test context + */ +static void dm_test_is_freesync_video_mode_no_match(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_display_mode candidate = {}; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.connector_type = DRM_MODE_CONNECTOR_HDMIA; + aconnector->freesync_vid_base.clock = 148500; + aconnector->freesync_vid_base.hdisplay = 1920; + aconnector->freesync_vid_base.vdisplay = 1080; + aconnector->freesync_vid_base.htotal = 2200; + aconnector->freesync_vid_base.vtotal = 1125; + + /* Different resolution → not a freesync video mode */ + candidate.clock = 148500; + candidate.hdisplay = 1280; + candidate.vdisplay = 720; + candidate.htotal = 1650; + candidate.vtotal = 750; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_freesync_video_mode(&candidate, aconnector)); +} + +static struct kunit_case amdgpu_dm_connector_tests[] = { + /* get_subconnector_type */ + KUNIT_CASE(dm_test_subconnector_type_none), + KUNIT_CASE(dm_test_subconnector_type_vga), + KUNIT_CASE(dm_test_subconnector_type_dvi_converter), + KUNIT_CASE(dm_test_subconnector_type_dvi_dongle), + KUNIT_CASE(dm_test_subconnector_type_hdmi_converter), + KUNIT_CASE(dm_test_subconnector_type_hdmi_dongle), + KUNIT_CASE(dm_test_subconnector_type_mismatched), + KUNIT_CASE(dm_test_subconnector_type_default_unknown), + /* get_output_content_type */ + KUNIT_CASE(dm_test_content_type_no_data), + KUNIT_CASE(dm_test_content_type_graphics), + KUNIT_CASE(dm_test_content_type_photo), + KUNIT_CASE(dm_test_content_type_cinema), + KUNIT_CASE(dm_test_content_type_game), + KUNIT_CASE(dm_test_content_type_unknown_defaults_no_data), + /* adjust_colour_depth_from_display_info */ + KUNIT_CASE(dm_test_adjust_colour_depth_fits_at_888), + KUNIT_CASE(dm_test_adjust_colour_depth_reduces_to_888), + KUNIT_CASE(dm_test_adjust_colour_depth_10bpc_passes), + KUNIT_CASE(dm_test_adjust_colour_depth_420_halves_clk), + KUNIT_CASE(dm_test_adjust_colour_depth_reduces_12bpc_to_10bpc), + KUNIT_CASE(dm_test_adjust_colour_depth_16bpc_no_fallback), + KUNIT_CASE(dm_test_adjust_colour_depth_none_fits), + KUNIT_CASE(dm_test_adjust_colour_depth_invalid_depth), + /* amdgpu_dm_get_output_color_space */ + KUNIT_CASE(dm_test_output_color_space_default_rgb_full), + KUNIT_CASE(dm_test_output_color_space_default_rgb_limited), + KUNIT_CASE(dm_test_output_color_space_default_ycbcr709), + KUNIT_CASE(dm_test_output_color_space_default_ycbcr601_limited), + KUNIT_CASE(dm_test_output_color_space_bt601_y_only), + KUNIT_CASE(dm_test_output_color_space_bt601), + KUNIT_CASE(dm_test_output_color_space_bt709), + KUNIT_CASE(dm_test_output_color_space_bt709_y_only), + KUNIT_CASE(dm_test_output_color_space_oprgb), + KUNIT_CASE(dm_test_output_color_space_bt2020_rgb), + KUNIT_CASE(dm_test_output_color_space_bt2020_ycc), + /* Tests for amdgpu_dm_convert_dc_color_depth_into_bpc */ + KUNIT_CASE(dm_test_convert_color_depth_bpc_mappings), + KUNIT_CASE(dm_test_convert_color_depth_bpc_unknown), + /* amdgpu_dm_convert_color_depth_from_display_info */ + KUNIT_CASE(dm_test_color_depth_from_info_bpc8), + KUNIT_CASE(dm_test_color_depth_from_info_bpc10), + KUNIT_CASE(dm_test_color_depth_from_info_zero_bpc_defaults_888), + KUNIT_CASE(dm_test_color_depth_from_info_requested_bpc_caps), + KUNIT_CASE(dm_test_color_depth_from_info_y420_default), + KUNIT_CASE(dm_test_color_depth_from_info_y420_10bpc), + KUNIT_CASE(dm_test_color_depth_from_info_y420_12bpc), + KUNIT_CASE(dm_test_color_depth_from_info_y420_16bpc), + KUNIT_CASE(dm_test_color_depth_from_info_requested_odd_bpc), + KUNIT_CASE(dm_test_color_depth_from_info_unsupported_bpc), + /* to_drm_connector_type */ + KUNIT_CASE(dm_test_to_connector_type_hdmi), + KUNIT_CASE(dm_test_to_connector_type_edp), + KUNIT_CASE(dm_test_to_connector_type_lvds), + KUNIT_CASE(dm_test_to_connector_type_rgb), + KUNIT_CASE(dm_test_to_connector_type_dp), + KUNIT_CASE(dm_test_to_connector_type_dp_mst), + KUNIT_CASE(dm_test_to_connector_type_dvi_dvii), + KUNIT_CASE(dm_test_to_connector_type_dual_link_dvii), + KUNIT_CASE(dm_test_to_connector_type_dvi_dvid), + KUNIT_CASE(dm_test_to_connector_type_virtual), + KUNIT_CASE(dm_test_to_connector_type_unknown), + /* is_duplicate_mode */ + KUNIT_CASE(dm_test_is_duplicate_mode_empty_list), + KUNIT_CASE(dm_test_is_duplicate_mode_match), + KUNIT_CASE(dm_test_is_duplicate_mode_no_match), + KUNIT_CASE(dm_test_is_duplicate_mode_same_size_different_clock), + /* amdgpu_dm_get_encoder_crtc_mask */ + KUNIT_CASE(dm_test_encoder_crtc_mask_1), + KUNIT_CASE(dm_test_encoder_crtc_mask_2), + KUNIT_CASE(dm_test_encoder_crtc_mask_3), + KUNIT_CASE(dm_test_encoder_crtc_mask_4), + KUNIT_CASE(dm_test_encoder_crtc_mask_5), + KUNIT_CASE(dm_test_encoder_crtc_mask_6), + KUNIT_CASE(dm_test_encoder_crtc_mask_default), + /* get_aspect_ratio */ + KUNIT_CASE(dm_test_aspect_ratio_no_data), + KUNIT_CASE(dm_test_aspect_ratio_4_3), + KUNIT_CASE(dm_test_aspect_ratio_16_9), + KUNIT_CASE(dm_test_aspect_ratio_64_27), + KUNIT_CASE(dm_test_aspect_ratio_256_135), + /* decide_crtc_timing_for_drm_display_mode */ + KUNIT_CASE(dm_test_decide_crtc_timing_scale_enabled), + KUNIT_CASE(dm_test_decide_crtc_timing_matching_mode), + KUNIT_CASE(dm_test_decide_crtc_timing_no_copy), + KUNIT_CASE(dm_test_decide_crtc_timing_no_crtc_clock), + /* amdgpu_dm_connector_funcs_reset */ + KUNIT_CASE(dm_test_funcs_reset_sets_defaults), + KUNIT_CASE(dm_test_funcs_reset_edp_abm_level), + KUNIT_CASE(dm_test_funcs_reset_edp_abm_disabled), + /* amdgpu_dm_connector_atomic_duplicate_state */ + KUNIT_CASE(dm_test_atomic_dup_state_copies_fields), + /* amdgpu_dm_fill_hdr_info_packet */ + KUNIT_CASE(dm_test_fill_hdr_null_metadata), + KUNIT_CASE(dm_test_fill_hdr_zeroes_output), + /* amdgpu_dm_connector_atomic_set_property */ + KUNIT_CASE(dm_test_set_property_scaling_center), + KUNIT_CASE(dm_test_set_property_scaling_aspect), + KUNIT_CASE(dm_test_set_property_scaling_fullscreen), + KUNIT_CASE(dm_test_set_property_scaling_none), + KUNIT_CASE(dm_test_set_property_scaling_unchanged), + KUNIT_CASE(dm_test_set_property_underscan_hborder), + KUNIT_CASE(dm_test_set_property_underscan_vborder), + KUNIT_CASE(dm_test_set_property_underscan_enable), + KUNIT_CASE(dm_test_set_property_abm_sysfs_control), + KUNIT_CASE(dm_test_set_property_abm_level_off), + KUNIT_CASE(dm_test_set_property_abm_level_value), + KUNIT_CASE(dm_test_set_property_unknown), + /* amdgpu_dm_connector_atomic_get_property */ + KUNIT_CASE(dm_test_get_property_scaling_center), + KUNIT_CASE(dm_test_get_property_scaling_aspect), + KUNIT_CASE(dm_test_get_property_scaling_full), + KUNIT_CASE(dm_test_get_property_scaling_off), + KUNIT_CASE(dm_test_get_property_underscan_borders), + KUNIT_CASE(dm_test_get_property_abm_sysfs_allowed), + KUNIT_CASE(dm_test_get_property_abm_level), + KUNIT_CASE(dm_test_get_property_abm_disabled_zero), + KUNIT_CASE(dm_test_get_property_unknown), + /* amdgpu_dm_get_highest_refresh_rate_mode */ + KUNIT_CASE(dm_test_highest_refresh_writeback_null), + KUNIT_CASE(dm_test_highest_refresh_cached_base), + KUNIT_CASE(dm_test_highest_refresh_preferred_mode), + /* amdgpu_dm_is_freesync_video_mode */ + KUNIT_CASE(dm_test_is_freesync_video_mode_null_mode), + KUNIT_CASE(dm_test_is_freesync_video_mode_match), + KUNIT_CASE(dm_test_is_freesync_video_mode_no_match), + {} +}; + +static struct kunit_suite amdgpu_dm_connector_test_suite = { + .name = "amdgpu_dm_connector", + .test_cases = amdgpu_dm_connector_tests, +}; + +kunit_test_suite(amdgpu_dm_connector_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_connector"); +MODULE_LICENSE("Dual MIT/GPL"); 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 index bba8b1a8fa1c..a6fd3a6fd803 100644 --- 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 @@ -95,17 +95,139 @@ static void dm_test_is_valid_crc_source(struct kunit *test) KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_valid_crc_source(AMDGPU_DM_PIPE_CRC_SOURCE_INVALID)); } +/** + * dm_test_need_dp_aux() - Test dm_need_dp_aux(). + * @test: KUnit test context. + * + * Verifies that dm_need_dp_aux() returns true when the transition starts or + * stops a DPRX CRC source (requiring the DP AUX handle), and false for + * non-DPRX transitions such as CRTC or NONE→NONE. + */ +static void dm_test_need_dp_aux(struct kunit *test) +{ + /* Starting a DPRX source always needs AUX, regardless of current source */ + KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, + AMDGPU_DM_PIPE_CRC_SOURCE_CRTC)); + KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + + /* Stopping a DPRX source (NONE requested, DPRX was active) needs AUX */ + KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_NONE, + AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)); + KUNIT_EXPECT_TRUE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_NONE, + AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER)); + + /* CRTC transitions do not need AUX */ + KUNIT_EXPECT_FALSE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + KUNIT_EXPECT_FALSE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_NONE, + AMDGPU_DM_PIPE_CRC_SOURCE_CRTC)); + KUNIT_EXPECT_FALSE(test, dm_need_dp_aux(AMDGPU_DM_PIPE_CRC_SOURCE_NONE, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); +} + +/** + * dm_test_crc_source_should_start_dprx() - Test dm_crc_source_should_start_dprx(). + * @test: KUnit test context. + * + * Verifies that dm_crc_source_should_start_dprx() returns true only when CRC + * is transitioning from off (!enabled) to a DPRX source (enable && + * is_dprx(source)), and false for all other combinations including + * already-enabled or non-DPRX targets. + */ +static void dm_test_crc_source_should_start_dprx(struct kunit *test) +{ + /* CRC off → DPRX: should start */ + KUNIT_EXPECT_TRUE(test, + dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + KUNIT_EXPECT_TRUE(test, + dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + + /* CRC already on (any source) → DPRX: should NOT start (already enabled) */ + KUNIT_EXPECT_FALSE(test, + dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, + AMDGPU_DM_PIPE_CRC_SOURCE_CRTC)); + KUNIT_EXPECT_FALSE(test, + dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, + AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)); + + /* CRC off → CRTC: not a DPRX start */ + KUNIT_EXPECT_FALSE(test, + dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_CRTC, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + + /* Disabling: should not start */ + KUNIT_EXPECT_FALSE(test, + dm_crc_source_should_start_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE, + AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)); +} + +/** + * dm_test_crc_source_should_stop_dprx() - Test dm_crc_source_should_stop_dprx(). + * @test: KUnit test context. + * + * Verifies that dm_crc_source_should_stop_dprx() returns true only when CRC + * is transitioning from a DPRX source (enabled && is_dprx(cur_crc_src)) to + * off (!enable), and false for non-DPRX disables, DPRX starts, and no-op + * transitions. + */ +static void dm_test_crc_source_should_stop_dprx(struct kunit *test) +{ + /* DPRX → off: should stop */ + KUNIT_EXPECT_TRUE(test, + dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE, + AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)); + KUNIT_EXPECT_TRUE(test, + dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE, + AMDGPU_DM_PIPE_CRC_SOURCE_DPRX_DITHER)); + + /* CRTC → off: not a DPRX stop */ + KUNIT_EXPECT_FALSE(test, + dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE, + AMDGPU_DM_PIPE_CRC_SOURCE_CRTC)); + + /* off → DPRX: not a stop */ + KUNIT_EXPECT_FALSE(test, + dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); + + /* DPRX → DPRX: no transition, not a stop */ + KUNIT_EXPECT_FALSE(test, + dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_DPRX, + AMDGPU_DM_PIPE_CRC_SOURCE_DPRX)); + + /* off → off: not a stop */ + KUNIT_EXPECT_FALSE(test, + dm_crc_source_should_stop_dprx(AMDGPU_DM_PIPE_CRC_SOURCE_NONE, + AMDGPU_DM_PIPE_CRC_SOURCE_NONE)); +} + static struct kunit_case dm_crc_test_cases[] = { + /* dm_parse_crc_source() */ 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), + /* dm_is_crc_source_crtc() */ KUNIT_CASE(dm_test_is_crc_source_crtc), + /* dm_is_crc_source_dprx() */ KUNIT_CASE(dm_test_is_crc_source_dprx), + /* dm_need_crc_dither() */ KUNIT_CASE(dm_test_need_crc_dither), + /* amdgpu_dm_is_valid_crc_source() */ KUNIT_CASE(dm_test_is_valid_crc_source), + /* dm_need_dp_aux() */ + KUNIT_CASE(dm_test_need_dp_aux), + /* dm_crc_source_should_start_dprx() */ + KUNIT_CASE(dm_test_crc_source_should_start_dprx), + /* dm_crc_source_should_stop_dprx() */ + KUNIT_CASE(dm_test_crc_source_should_stop_dprx), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c new file mode 100644 index 000000000000..0edaf969f16b --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_crtc_test.c @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_crtc.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <drm/drm_atomic.h> +#include <drm/drm_connector.h> +#include <drm/drm_kunit_helpers.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_crtc.h" +#include "amdgpu_dm_kunit_test_helpers.h" +#include "amdgpu_dm_irq_params.h" + +/* Tests for amdgpu_dm_crtc_modeset_required() */ + +/** + * dm_test_crtc_modeset_required_active_mode_changed - Test Crtc modeset required active mode changed + * @test: The KUnit test context + */ +static void dm_test_crtc_modeset_required_active_mode_changed(struct kunit *test) +{ + struct drm_crtc_state state = {}; + + state.active = true; + state.mode_changed = true; + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_crtc_modeset_required(&state, NULL, NULL)); +} + +/** + * dm_test_crtc_modeset_required_active_active_changed - Test Crtc modeset required active active changed + * @test: The KUnit test context + */ +static void dm_test_crtc_modeset_required_active_active_changed(struct kunit *test) +{ + struct drm_crtc_state state = {}; + + state.active = true; + state.active_changed = true; + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_crtc_modeset_required(&state, NULL, NULL)); +} + +/** + * dm_test_crtc_modeset_required_active_connectors_changed - Test Crtc modeset required active connectors changed + * @test: The KUnit test context + */ +static void dm_test_crtc_modeset_required_active_connectors_changed(struct kunit *test) +{ + struct drm_crtc_state state = {}; + + state.active = true; + state.connectors_changed = true; + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_crtc_modeset_required(&state, NULL, NULL)); +} + +/** + * dm_test_crtc_modeset_required_inactive - Test Crtc modeset required inactive + * @test: The KUnit test context + */ +static void dm_test_crtc_modeset_required_inactive(struct kunit *test) +{ + struct drm_crtc_state state = {}; + + state.active = false; + state.mode_changed = true; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_crtc_modeset_required(&state, NULL, NULL)); +} + +/** + * dm_test_crtc_modeset_required_no_changes - Test Crtc modeset required no changes + * @test: The KUnit test context + */ +static void dm_test_crtc_modeset_required_no_changes(struct kunit *test) +{ + struct drm_crtc_state state = {}; + + state.active = true; + state.mode_changed = false; + state.active_changed = false; + state.connectors_changed = false; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_crtc_modeset_required(&state, NULL, NULL)); +} + +/* Tests for amdgpu_dm_crtc_vrr_active_irq() */ + +/** + * dm_test_crtc_vrr_active_irq_variable - Test Crtc vrr active irq variable + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_irq_variable(struct kunit *test) +{ + struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + acrtc->dm_irq_params.freesync_config.state = VRR_STATE_ACTIVE_VARIABLE; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc)); +} + +/** + * dm_test_crtc_vrr_active_irq_fixed - Test Crtc vrr active irq fixed + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_irq_fixed(struct kunit *test) +{ + struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + acrtc->dm_irq_params.freesync_config.state = VRR_STATE_ACTIVE_FIXED; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc)); +} + +/** + * dm_test_crtc_vrr_active_irq_inactive - Test Crtc vrr active irq inactive + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_irq_inactive(struct kunit *test) +{ + struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + acrtc->dm_irq_params.freesync_config.state = VRR_STATE_INACTIVE; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc)); +} + +/** + * dm_test_crtc_vrr_active_irq_disabled - Test Crtc vrr active irq disabled + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_irq_disabled(struct kunit *test) +{ + struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + acrtc->dm_irq_params.freesync_config.state = VRR_STATE_DISABLED; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc)); +} + +/** + * dm_test_crtc_vrr_active_irq_unsupported - Test Crtc vrr active irq unsupported + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_irq_unsupported(struct kunit *test) +{ + struct amdgpu_crtc *acrtc = kunit_kzalloc(test, sizeof(*acrtc), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + acrtc->dm_irq_params.freesync_config.state = VRR_STATE_UNSUPPORTED; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active_irq(acrtc)); +} + +/* Tests for amdgpu_dm_crtc_vrr_active() */ + +/** + * dm_test_crtc_vrr_active_variable - Test Crtc vrr active variable + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_variable(struct kunit *test) +{ + struct dm_crtc_state *dm_state = kunit_kzalloc(test, + sizeof(*dm_state), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state); + + dm_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_crtc_vrr_active(dm_state)); +} + +/** + * dm_test_crtc_vrr_active_fixed - Test Crtc vrr active fixed + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_fixed(struct kunit *test) +{ + struct dm_crtc_state *dm_state = kunit_kzalloc(test, + sizeof(*dm_state), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state); + + dm_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_crtc_vrr_active(dm_state)); +} + +/** + * dm_test_crtc_vrr_active_inactive - Test Crtc vrr active inactive + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_inactive(struct kunit *test) +{ + struct dm_crtc_state *dm_state = kunit_kzalloc(test, + sizeof(*dm_state), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state); + + dm_state->freesync_config.state = VRR_STATE_INACTIVE; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active(dm_state)); +} + +/** + * dm_test_crtc_vrr_active_disabled - Test Crtc vrr active disabled + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_disabled(struct kunit *test) +{ + struct dm_crtc_state *dm_state = kunit_kzalloc(test, + sizeof(*dm_state), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state); + + dm_state->freesync_config.state = VRR_STATE_DISABLED; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active(dm_state)); +} + +/** + * dm_test_crtc_vrr_active_unsupported - Test Crtc vrr active unsupported + * @test: The KUnit test context + */ +static void dm_test_crtc_vrr_active_unsupported(struct kunit *test) +{ + struct dm_crtc_state *dm_state = kunit_kzalloc(test, + sizeof(*dm_state), + GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_state); + + dm_state->freesync_config.state = VRR_STATE_UNSUPPORTED; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_crtc_vrr_active(dm_state)); +} + +/* Tests for amdgpu_dm_is_headless() */ + +static void dm_test_add_connector(struct drm_device *dev, + struct drm_connector *connector, + int connector_type, + enum drm_connector_status status) +{ + INIT_LIST_HEAD(&connector->head); + kref_init(&connector->base.refcount); + connector->connector_type = connector_type; + connector->status = status; + list_add_tail(&connector->head, &dev->mode_config.connector_list); +} + +/** + * dm_test_crtc_is_headless_null_adev - Test Crtc is headless null adev + * @test: The KUnit test context + */ +static void dm_test_crtc_is_headless_null_adev(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_headless(NULL)); +} + +/** + * dm_test_crtc_is_headless_no_connectors - Test Crtc is headless no connectors + * @test: The KUnit test context + */ +static void dm_test_crtc_is_headless_no_connectors(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + INIT_LIST_HEAD(&dev->mode_config.connector_list); + spin_lock_init(&dev->mode_config.connector_list_lock); + adev->dm.ddev = dev; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_headless(adev)); +} + +/** + * dm_test_crtc_is_headless_writeback_only - Test Crtc is headless writeback only + * @test: The KUnit test context + */ +static void dm_test_crtc_is_headless_writeback_only(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + struct drm_connector *wb = kunit_kzalloc(test, sizeof(*wb), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wb); + + INIT_LIST_HEAD(&dev->mode_config.connector_list); + spin_lock_init(&dev->mode_config.connector_list_lock); + adev->dm.ddev = dev; + + dm_test_add_connector(dev, wb, DRM_MODE_CONNECTOR_WRITEBACK, + connector_status_connected); + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_headless(adev)); +} + +/** + * dm_test_crtc_is_headless_disconnected_display - Test Crtc is headless disconnected display + * @test: The KUnit test context + */ +static void dm_test_crtc_is_headless_disconnected_display(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + struct drm_connector *display = kunit_kzalloc(test, sizeof(*display), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, display); + + INIT_LIST_HEAD(&dev->mode_config.connector_list); + spin_lock_init(&dev->mode_config.connector_list_lock); + adev->dm.ddev = dev; + + dm_test_add_connector(dev, display, DRM_MODE_CONNECTOR_HDMIA, + connector_status_disconnected); + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_is_headless(adev)); +} + +/** + * dm_test_crtc_is_headless_connected_display - Test Crtc is headless connected display + * @test: The KUnit test context + */ +static void dm_test_crtc_is_headless_connected_display(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + struct drm_connector *display = kunit_kzalloc(test, sizeof(*display), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, display); + + INIT_LIST_HEAD(&dev->mode_config.connector_list); + spin_lock_init(&dev->mode_config.connector_list_lock); + adev->dm.ddev = dev; + + dm_test_add_connector(dev, display, DRM_MODE_CONNECTOR_HDMIA, + connector_status_connected); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_headless(adev)); +} + +/** + * dm_test_crtc_is_headless_mixed_connectors - Test headless skips WB and finds display + * @test: The KUnit test context + */ +static void dm_test_crtc_is_headless_mixed_connectors(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct drm_device *dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + struct drm_connector *wb = kunit_kzalloc(test, sizeof(*wb), GFP_KERNEL); + struct drm_connector *display = kunit_kzalloc(test, sizeof(*display), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wb); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, display); + + INIT_LIST_HEAD(&dev->mode_config.connector_list); + spin_lock_init(&dev->mode_config.connector_list_lock); + adev->dm.ddev = dev; + + dm_test_add_connector(dev, wb, DRM_MODE_CONNECTOR_WRITEBACK, + connector_status_connected); + dm_test_add_connector(dev, display, DRM_MODE_CONNECTOR_DisplayPort, + connector_status_connected); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_is_headless(adev)); +} + +/* Tests for amdgpu_dm_crtc_helper_mode_fixup() */ + +/** + * dm_test_crtc_helper_mode_fixup_returns_true - Test mode_fixup accepts mode + * @test: The KUnit test context + */ +static void dm_test_crtc_helper_mode_fixup_returns_true(struct kunit *test) +{ + struct drm_display_mode mode = { 0 }; + struct drm_display_mode adjusted_mode = { 0 }; + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_crtc_helper_mode_fixup(NULL, &mode, &adjusted_mode)); +} + +/* Tests for amdgpu_dm_crtc_set_vupdate_irq() */ + +/** + * dm_test_crtc_set_vupdate_irq_no_otg - Test vupdate irq with unassigned OTG + * @test: The KUnit test context + * + * When the CRTC has no OTG instance assigned (otg_inst == -1) the function + * must return 0 immediately without touching the DC interrupt state. + */ +static void dm_test_crtc_set_vupdate_irq_no_otg(struct kunit *test) +{ + struct amdgpu_crtc *acrtc; + struct amdgpu_device *adev; + + adev = dm_kunit_alloc_adev(test); + + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + acrtc->base.dev = &adev->ddev; + acrtc->otg_inst = -1; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, true), 0); + KUNIT_EXPECT_EQ(test, amdgpu_dm_crtc_set_vupdate_irq(&acrtc->base, false), 0); +} + +/* Tests for idle_create_workqueue() */ + +/** + * dm_test_idle_create_workqueue - Test idle workqueue creation + * @test: The KUnit test context + * + * Verify that idle_create_workqueue() allocates an idle workqueue tied to the + * device's display manager and initializes it in a disabled, non-running state. + */ +static void dm_test_idle_create_workqueue(struct kunit *test) +{ + struct amdgpu_device *adev; + struct idle_workqueue *idle_work; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + idle_work = idle_create_workqueue(adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, idle_work); + + KUNIT_EXPECT_PTR_EQ(test, idle_work->dm, &adev->dm); + KUNIT_EXPECT_FALSE(test, idle_work->enable); + KUNIT_EXPECT_FALSE(test, idle_work->running); + + kfree(idle_work); +} + +static struct kunit_case amdgpu_dm_crtc_tests[] = { + /* amdgpu_dm_crtc_modeset_required */ + KUNIT_CASE(dm_test_crtc_modeset_required_active_mode_changed), + KUNIT_CASE(dm_test_crtc_modeset_required_active_active_changed), + KUNIT_CASE(dm_test_crtc_modeset_required_active_connectors_changed), + KUNIT_CASE(dm_test_crtc_modeset_required_inactive), + KUNIT_CASE(dm_test_crtc_modeset_required_no_changes), + /* amdgpu_dm_crtc_vrr_active_irq */ + KUNIT_CASE(dm_test_crtc_vrr_active_irq_variable), + KUNIT_CASE(dm_test_crtc_vrr_active_irq_fixed), + KUNIT_CASE(dm_test_crtc_vrr_active_irq_inactive), + KUNIT_CASE(dm_test_crtc_vrr_active_irq_disabled), + KUNIT_CASE(dm_test_crtc_vrr_active_irq_unsupported), + /* amdgpu_dm_crtc_vrr_active */ + KUNIT_CASE(dm_test_crtc_vrr_active_variable), + KUNIT_CASE(dm_test_crtc_vrr_active_fixed), + KUNIT_CASE(dm_test_crtc_vrr_active_inactive), + KUNIT_CASE(dm_test_crtc_vrr_active_disabled), + KUNIT_CASE(dm_test_crtc_vrr_active_unsupported), + /* amdgpu_dm_is_headless */ + KUNIT_CASE(dm_test_crtc_is_headless_null_adev), + KUNIT_CASE(dm_test_crtc_is_headless_no_connectors), + KUNIT_CASE(dm_test_crtc_is_headless_writeback_only), + KUNIT_CASE(dm_test_crtc_is_headless_disconnected_display), + KUNIT_CASE(dm_test_crtc_is_headless_connected_display), + KUNIT_CASE(dm_test_crtc_is_headless_mixed_connectors), + /* amdgpu_dm_crtc_helper_mode_fixup */ + KUNIT_CASE(dm_test_crtc_helper_mode_fixup_returns_true), + /* amdgpu_dm_crtc_set_vupdate_irq */ + KUNIT_CASE(dm_test_crtc_set_vupdate_irq_no_otg), + /* idle_create_workqueue */ + KUNIT_CASE(dm_test_idle_create_workqueue), + {} +}; + +static struct kunit_suite amdgpu_dm_crtc_test_suite = { + .name = "amdgpu_dm_crtc", + .test_cases = amdgpu_dm_crtc_tests, +}; + +kunit_test_suite(amdgpu_dm_crtc_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_crtc"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c new file mode 100644 index 000000000000..bf90ccfbf431 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_dmub_test.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_dmub.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include "dc.h" +#include "dc/inc/core_types.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "dmub/dmub_srv.h" +#include "amdgpu_dm_dmub.h" + +/* Tests for dm_register_dmub_notify_callback() */ + +static void dummy_callback(struct amdgpu_device *adev, + struct dmub_notification *notify) +{ +} + +/** + * dm_test_register_dmub_notify_callback_null_callback - Test null callback is rejected + * @test: The KUnit test context + */ +static void dm_test_register_dmub_notify_callback_null_callback(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + KUNIT_EXPECT_FALSE(test, dm_register_dmub_notify_callback(adev, + DMUB_NOTIFICATION_AUX_REPLY, NULL, false)); +} + +/** + * dm_test_register_dmub_notify_callback_type_out_of_range - Test out-of-range type is rejected + * @test: The KUnit test context + */ +static void dm_test_register_dmub_notify_callback_type_out_of_range(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + KUNIT_EXPECT_FALSE(test, dm_register_dmub_notify_callback(adev, + AMDGPU_DMUB_NOTIFICATION_MAX, dummy_callback, false)); +} + +/** + * dm_test_register_dmub_notify_callback_valid - Test Register dmub notify callback valid + * @test: The KUnit test context + */ +static void dm_test_register_dmub_notify_callback_valid(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + KUNIT_EXPECT_TRUE(test, dm_register_dmub_notify_callback(adev, + DMUB_NOTIFICATION_AUX_REPLY, dummy_callback, true)); + + KUNIT_EXPECT_TRUE(test, + adev->dm.dmub_callback[DMUB_NOTIFICATION_AUX_REPLY] == dummy_callback); + KUNIT_EXPECT_TRUE(test, + adev->dm.dmub_thread_offload[DMUB_NOTIFICATION_AUX_REPLY]); +} + +/** + * dm_test_register_dmub_notify_callback_offload_false - Test registration with offload disabled + * @test: The KUnit test context + */ +static void dm_test_register_dmub_notify_callback_offload_false(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + KUNIT_EXPECT_TRUE(test, dm_register_dmub_notify_callback(adev, + DMUB_NOTIFICATION_HPD, dummy_callback, false)); + + KUNIT_EXPECT_TRUE(test, + adev->dm.dmub_callback[DMUB_NOTIFICATION_HPD] == dummy_callback); + KUNIT_EXPECT_FALSE(test, + adev->dm.dmub_thread_offload[DMUB_NOTIFICATION_HPD]); +} + +/* Tests for dm_dmub_aux_setconfig_callback() */ + +/** + * dm_test_dmub_aux_setconfig_callback_copies_and_completes - Test copy and complete on AUX reply + * @test: The KUnit test context + */ +static void dm_test_dmub_aux_setconfig_callback_copies_and_completes(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dmub_notification *dm_notify; + struct dmub_notification notify = {}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + dm_notify = kunit_kzalloc(test, sizeof(*dm_notify), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_notify); + + init_completion(&adev->dm.dmub_aux_transfer_done); + adev->dm.dmub_notify = dm_notify; + + notify.type = DMUB_NOTIFICATION_AUX_REPLY; + notify.result = AUX_RET_SUCCESS; + notify.aux_reply.command = 0xA5; + notify.aux_reply.length = 3; + notify.aux_reply.data[0] = 0x11; + notify.aux_reply.data[1] = 0x22; + notify.aux_reply.data[2] = 0x33; + + dm_dmub_aux_setconfig_callback(adev, ¬ify); + + KUNIT_EXPECT_EQ(test, dm_notify->type, notify.type); + KUNIT_EXPECT_EQ(test, dm_notify->result, notify.result); + KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.command, notify.aux_reply.command); + KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.length, notify.aux_reply.length); + KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.data[0], notify.aux_reply.data[0]); + KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.data[1], notify.aux_reply.data[1]); + KUNIT_EXPECT_EQ(test, dm_notify->aux_reply.data[2], notify.aux_reply.data[2]); + KUNIT_EXPECT_TRUE(test, completion_done(&adev->dm.dmub_aux_transfer_done)); +} + +/** + * dm_test_dmub_aux_setconfig_callback_non_aux_no_complete - Test non-AUX type skips completion + * @test: The KUnit test context + */ +static void dm_test_dmub_aux_setconfig_callback_non_aux_no_complete(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dmub_notification *dm_notify; + struct dmub_notification notify = {}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + dm_notify = kunit_kzalloc(test, sizeof(*dm_notify), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_notify); + + init_completion(&adev->dm.dmub_aux_transfer_done); + adev->dm.dmub_notify = dm_notify; + + notify.type = DMUB_NOTIFICATION_HPD; + notify.result = AUX_RET_ERROR_TIMEOUT; + + dm_dmub_aux_setconfig_callback(adev, ¬ify); + + KUNIT_EXPECT_EQ(test, dm_notify->type, notify.type); + KUNIT_EXPECT_FALSE(test, completion_done(&adev->dm.dmub_aux_transfer_done)); +} + +/** + * dm_test_dmub_aux_setconfig_callback_aux_with_null_dm_notify - Test AUX with NULL dm_notify + * @test: The KUnit test context + */ +static void dm_test_dmub_aux_setconfig_callback_aux_with_null_dm_notify(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dmub_notification notify = {}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + init_completion(&adev->dm.dmub_aux_transfer_done); + adev->dm.dmub_notify = NULL; + + notify.type = DMUB_NOTIFICATION_AUX_REPLY; + + dm_dmub_aux_setconfig_callback(adev, ¬ify); + + KUNIT_EXPECT_TRUE(test, completion_done(&adev->dm.dmub_aux_transfer_done)); +} + +/** + * dm_test_dmub_aux_setconfig_callback_set_config_reply - Test SET_CONFIG reply copies status + * @test: The KUnit test context + */ +static void dm_test_dmub_aux_setconfig_callback_set_config_reply(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dmub_notification *dm_notify; + struct dmub_notification notify = {}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + dm_notify = kunit_kzalloc(test, sizeof(*dm_notify), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm_notify); + + init_completion(&adev->dm.dmub_aux_transfer_done); + adev->dm.dmub_notify = dm_notify; + + notify.type = DMUB_NOTIFICATION_SET_CONFIG_REPLY; + notify.sc_status = SET_CONFIG_RX_TIMEOUT; + + dm_dmub_aux_setconfig_callback(adev, ¬ify); + + KUNIT_EXPECT_EQ(test, dm_notify->type, notify.type); + KUNIT_EXPECT_EQ(test, dm_notify->sc_status, notify.sc_status); + KUNIT_EXPECT_FALSE(test, completion_done(&adev->dm.dmub_aux_transfer_done)); +} + +/* Tests for dm_dmub_aux_fused_io_callback() */ + +/** + * dm_test_dmub_aux_fused_io_callback_copies_reply_and_completes - Test copy and complete + * @test: The KUnit test context + */ +static void dm_test_dmub_aux_fused_io_callback_copies_reply_and_completes(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dmub_notification notify = {}; + struct dmub_cmd_fused_request *reply; + u32 reply_ddc_line; + u32 notify_ddc_line; + u32 reply_address; + u32 notify_address; + u32 reply_length; + u32 notify_length; + uint8_t ddc_line = 2; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + init_completion(&adev->dm.fused_io[ddc_line].replied); + + notify.fused_request.identifier = 0x34; + notify.fused_request.status = FUSED_REQUEST_STATUS_SUCCESS; + notify.fused_request.u.aux.ddc_line = ddc_line; + notify.fused_request.u.aux.address = 0x50; + notify.fused_request.u.aux.length = 4; + + dm_dmub_aux_fused_io_callback(adev, ¬ify); + + KUNIT_EXPECT_TRUE(test, completion_done(&adev->dm.fused_io[ddc_line].replied)); + + reply = (struct dmub_cmd_fused_request *)adev->dm.fused_io[ddc_line].reply_data; + reply_ddc_line = reply->u.aux.ddc_line; + notify_ddc_line = notify.fused_request.u.aux.ddc_line; + reply_address = reply->u.aux.address; + notify_address = notify.fused_request.u.aux.address; + reply_length = reply->u.aux.length; + notify_length = notify.fused_request.u.aux.length; + + KUNIT_EXPECT_EQ(test, reply->identifier, notify.fused_request.identifier); + KUNIT_EXPECT_EQ(test, reply->status, notify.fused_request.status); + KUNIT_EXPECT_EQ(test, reply_ddc_line, notify_ddc_line); + KUNIT_EXPECT_EQ(test, reply_address, notify_address); + KUNIT_EXPECT_EQ(test, reply_length, notify_length); +} + +/** + * dm_test_dmub_aux_fused_io_callback_max_ddc_line - Test Dmub aux fused io callback max ddc line + * @test: The KUnit test context + */ +static void dm_test_dmub_aux_fused_io_callback_max_ddc_line(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dmub_notification notify = {}; + struct dmub_cmd_fused_request *reply; + u32 reply_ddc_line; + u32 notify_ddc_line; + uint8_t ddc_line; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + ddc_line = ARRAY_SIZE(adev->dm.fused_io) - 1; + init_completion(&adev->dm.fused_io[ddc_line].replied); + + notify.fused_request.identifier = 0x56; + notify.fused_request.status = FUSED_REQUEST_STATUS_SUCCESS; + notify.fused_request.u.aux.ddc_line = ddc_line; + notify.fused_request.u.aux.address = 0x50; + notify.fused_request.u.aux.length = 1; + + dm_dmub_aux_fused_io_callback(adev, ¬ify); + + KUNIT_EXPECT_TRUE(test, completion_done(&adev->dm.fused_io[ddc_line].replied)); + + reply = (struct dmub_cmd_fused_request *)adev->dm.fused_io[ddc_line].reply_data; + reply_ddc_line = reply->u.aux.ddc_line; + notify_ddc_line = notify.fused_request.u.aux.ddc_line; + + KUNIT_EXPECT_EQ(test, reply->identifier, notify.fused_request.identifier); + KUNIT_EXPECT_EQ(test, reply_ddc_line, notify_ddc_line); +} + +/* Tests for dm_get_default_ips_mode() */ + +/** + * dm_test_get_default_ips_mode_dcn35 - Test Get default ips mode dcn35 + * @test: The KUnit test context + */ +static void dm_test_get_default_ips_mode_dcn35(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 5, 0); + + KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev), + DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF); +} + +/** + * dm_test_get_default_ips_mode_dcn351 - Test Get default ips mode dcn351 + * @test: The KUnit test context + */ +static void dm_test_get_default_ips_mode_dcn351(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 5, 1); + + KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev), + DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF); +} + +/** + * dm_test_get_default_ips_mode_dcn36 - Test Get default ips mode dcn36 + * @test: The KUnit test context + */ +static void dm_test_get_default_ips_mode_dcn36(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 6, 0); + + KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev), + DMUB_IPS_RCG_IN_ACTIVE_IPS2_IN_OFF); +} + +/** + * dm_test_get_default_ips_mode_older_than_dcn35 - Test Get default ips mode older than dcn35 + * @test: The KUnit test context + */ +static void dm_test_get_default_ips_mode_older_than_dcn35(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(3, 2, 0); + + KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev), + DMUB_IPS_DISABLE_ALL); +} + +/** + * dm_test_get_default_ips_mode_newer_default - Test Get default ips mode newer default + * @test: The KUnit test context + */ +static void dm_test_get_default_ips_mode_newer_default(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + /* DCN 4.0.1 is >= 3.5 but has no explicit case, returns ENABLE */ + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(4, 0, 1); + + KUNIT_EXPECT_EQ(test, dm_get_default_ips_mode(adev), + DMUB_IPS_ENABLE); +} + +/* Tests for dm_dmub_hw_init() */ + +/* + * Build an amdgpu_device with the minimal dc/res_pool pointers that + * dm_dmub_hw_init() and dm_dmub_hw_resume() dereference before their + * early-return checks. + */ +static struct amdgpu_device *dm_test_alloc_adev_with_dc(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + struct resource_pool *res_pool; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dc); + + res_pool = kunit_kzalloc(test, sizeof(*res_pool), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res_pool); + + dc->res_pool = res_pool; + adev->dm.dc = dc; + + return adev; +} + +/** + * dm_test_dmub_hw_init_no_dmub_srv - Test hw init returns 0 when DMUB unsupported + * @test: The KUnit test context + * + * When adev->dm.dmub_srv is NULL the ASIC does not support DMUB and + * dm_dmub_hw_init() should return 0 without touching the hardware. + */ +static void dm_test_dmub_hw_init_no_dmub_srv(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test); + + adev->dm.dmub_srv = NULL; + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), 0); +} + +/** + * dm_test_dmub_hw_init_no_fb_info - Test hw init fails without framebuffer info + * @test: The KUnit test context + * + * With a DMUB service present but no framebuffer info, dm_dmub_hw_init() + * should return -EINVAL. + */ +static void dm_test_dmub_hw_init_no_fb_info(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test); + struct dmub_srv *dmub_srv; + + dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub_srv); + + adev->dm.dmub_srv = dmub_srv; + adev->dm.dmub_fb_info = NULL; + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), -EINVAL); +} + +/** + * dm_test_dmub_hw_init_no_firmware - Test hw init fails without firmware + * @test: The KUnit test context + * + * With a DMUB service and framebuffer info present but no firmware, + * dm_dmub_hw_init() should return -EINVAL. + */ +static void dm_test_dmub_hw_init_no_firmware(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test); + struct dmub_srv *dmub_srv; + struct dmub_srv_fb_info *fb_info; + + dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dmub_srv); + + fb_info = kunit_kzalloc(test, sizeof(*fb_info), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fb_info); + + adev->dm.dmub_srv = dmub_srv; + adev->dm.dmub_fb_info = fb_info; + adev->dm.dmub_fw = NULL; + + KUNIT_EXPECT_EQ(test, dm_dmub_hw_init(adev), -EINVAL); +} + +/* Tests for dm_dmub_hw_resume() */ + +/** + * dm_test_dmub_hw_resume_no_dmub_srv - Test hw resume is a no-op when DMUB unsupported + * @test: The KUnit test context + * + * When adev->dm.dmub_srv is NULL, dm_dmub_hw_resume() should return early + * without dereferencing the (absent) DMUB service. + */ +static void dm_test_dmub_hw_resume_no_dmub_srv(struct kunit *test) +{ + struct amdgpu_device *adev = dm_test_alloc_adev_with_dc(test); + + adev->dm.dmub_srv = NULL; + + /* Must not crash. */ + dm_dmub_hw_resume(adev); +} + +/* Tests for dm_dmub_sw_init() */ + +/** + * dm_test_dmub_sw_init_unsupported_asic - Test sw init returns 0 for unsupported ASIC + * @test: The KUnit test context + * + * For an IP version with no DMUB support, dm_dmub_sw_init() should return 0 + * before attempting to access the firmware. + */ +static void dm_test_dmub_sw_init_unsupported_asic(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(1, 0, 0); + + KUNIT_EXPECT_EQ(test, dm_dmub_sw_init(adev), 0); +} + +/* Tests for dm_init_microcode() */ + +/** + * dm_test_init_microcode_unsupported_asic - Test microcode init returns 0 for unsupported ASIC + * @test: The KUnit test context + * + * For an IP version with no DMUB support, dm_init_microcode() should return 0 + * without requesting any firmware. + */ +static void dm_test_init_microcode_unsupported_asic(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(1, 0, 0); + + KUNIT_EXPECT_EQ(test, dm_init_microcode(adev), 0); +} + +static struct kunit_case amdgpu_dm_dmub_tests[] = { + /* dm_register_dmub_notify_callback() */ + KUNIT_CASE(dm_test_register_dmub_notify_callback_null_callback), + KUNIT_CASE(dm_test_register_dmub_notify_callback_type_out_of_range), + KUNIT_CASE(dm_test_register_dmub_notify_callback_valid), + KUNIT_CASE(dm_test_register_dmub_notify_callback_offload_false), + /* dm_dmub_aux_setconfig_callback() */ + KUNIT_CASE(dm_test_dmub_aux_setconfig_callback_copies_and_completes), + KUNIT_CASE(dm_test_dmub_aux_setconfig_callback_non_aux_no_complete), + KUNIT_CASE(dm_test_dmub_aux_setconfig_callback_aux_with_null_dm_notify), + KUNIT_CASE(dm_test_dmub_aux_setconfig_callback_set_config_reply), + /* dm_dmub_aux_fused_io_callback() */ + KUNIT_CASE(dm_test_dmub_aux_fused_io_callback_copies_reply_and_completes), + KUNIT_CASE(dm_test_dmub_aux_fused_io_callback_max_ddc_line), + /* dm_get_default_ips_mode() */ + KUNIT_CASE(dm_test_get_default_ips_mode_dcn35), + KUNIT_CASE(dm_test_get_default_ips_mode_dcn351), + KUNIT_CASE(dm_test_get_default_ips_mode_dcn36), + KUNIT_CASE(dm_test_get_default_ips_mode_older_than_dcn35), + KUNIT_CASE(dm_test_get_default_ips_mode_newer_default), + /* dm_dmub_hw_init() */ + KUNIT_CASE(dm_test_dmub_hw_init_no_dmub_srv), + KUNIT_CASE(dm_test_dmub_hw_init_no_fb_info), + KUNIT_CASE(dm_test_dmub_hw_init_no_firmware), + /* dm_dmub_hw_resume() */ + KUNIT_CASE(dm_test_dmub_hw_resume_no_dmub_srv), + /* dm_dmub_sw_init() */ + KUNIT_CASE(dm_test_dmub_sw_init_unsupported_asic), + /* dm_init_microcode() */ + KUNIT_CASE(dm_test_init_microcode_unsupported_asic), + {} +}; + +static struct kunit_suite amdgpu_dm_dmub_test_suite = { + .name = "amdgpu_dm_dmub", + .test_cases = amdgpu_dm_dmub_tests, +}; + +kunit_test_suite(amdgpu_dm_dmub_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_dmub"); +MODULE_LICENSE("Dual MIT/GPL"); 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 index d03b606d27bc..619b4a80c82b 100644 --- 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 @@ -12,11 +12,241 @@ static void dummy_work_fn(struct work_struct *work) {} +/* Tests for hdcp_get_content_protection_from_status() */ + +/** + * dm_test_hdcp_get_cp_disabled_returns_desired - HDCP off maps to DESIRED + * @test: KUnit test context + * + * When encryption status is HDCP_OFF, content_protection should be set + * to DESIRED and the function should return true to indicate an update. + */ +static void dm_test_hdcp_get_cp_disabled_returns_desired(struct kunit *test) +{ + unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + bool update; + + update = hdcp_get_content_protection_from_status( + DRM_MODE_HDCP_CONTENT_TYPE0, + MOD_HDCP_ENCRYPTION_STATUS_HDCP_OFF, + &content_protection); + + KUNIT_EXPECT_TRUE(test, update); + KUNIT_EXPECT_EQ(test, content_protection, + DRM_MODE_CONTENT_PROTECTION_DESIRED); +} + +/** + * dm_test_hdcp_get_cp_type0_returns_enabled - TYPE0 with TYPE0_ON maps to ENABLED + * @test: KUnit test context + * + * When content type is TYPE0 and encryption status is at or below + * HDCP2_TYPE0_ON, content_protection should be set to ENABLED. + */ +static void dm_test_hdcp_get_cp_type0_returns_enabled(struct kunit *test) +{ + unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + bool update; + + update = hdcp_get_content_protection_from_status( + DRM_MODE_HDCP_CONTENT_TYPE0, + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON, + &content_protection); + + KUNIT_EXPECT_TRUE(test, update); + KUNIT_EXPECT_EQ(test, content_protection, + DRM_MODE_CONTENT_PROTECTION_ENABLED); +} + +/** + * dm_test_hdcp_get_cp_type1_returns_enabled - TYPE1 with TYPE1_ON maps to ENABLED + * @test: KUnit test context + * + * When content type is TYPE1 and encryption status is exactly + * HDCP2_TYPE1_ON, content_protection should be set to ENABLED. + */ +static void dm_test_hdcp_get_cp_type1_returns_enabled(struct kunit *test) +{ + unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + bool update; + + update = hdcp_get_content_protection_from_status( + DRM_MODE_HDCP_CONTENT_TYPE1, + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON, + &content_protection); + + KUNIT_EXPECT_TRUE(test, update); + KUNIT_EXPECT_EQ(test, content_protection, + DRM_MODE_CONTENT_PROTECTION_ENABLED); +} + +/** + * dm_test_hdcp_get_cp_type1_rejects_type0_status - TYPE1 rejects TYPE0_ON + * @test: KUnit test context + * + * When content type is TYPE1 but encryption status is only TYPE0_ON, + * the function should return false and leave content_protection unchanged. + */ +static void dm_test_hdcp_get_cp_type1_rejects_type0_status(struct kunit *test) +{ + unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + bool update; + + update = hdcp_get_content_protection_from_status( + DRM_MODE_HDCP_CONTENT_TYPE1, + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE0_ON, + &content_protection); + + KUNIT_EXPECT_FALSE(test, update); + KUNIT_EXPECT_EQ(test, content_protection, + DRM_MODE_CONTENT_PROTECTION_UNDESIRED); +} + +/** + * dm_test_hdcp_get_cp_type0_rejects_type1_status - TYPE0 rejects TYPE1_ON + * @test: KUnit test context + * + * When content type is TYPE0 but encryption status exceeds the TYPE0_ON + * boundary (TYPE1_ON), the function should return false. + */ +static void dm_test_hdcp_get_cp_type0_rejects_type1_status(struct kunit *test) +{ + unsigned int content_protection = DRM_MODE_CONTENT_PROTECTION_UNDESIRED; + bool update; + + update = hdcp_get_content_protection_from_status( + DRM_MODE_HDCP_CONTENT_TYPE0, + MOD_HDCP_ENCRYPTION_STATUS_HDCP2_TYPE1_ON, + &content_protection); + + KUNIT_EXPECT_FALSE(test, update); + KUNIT_EXPECT_EQ(test, content_protection, + DRM_MODE_CONTENT_PROTECTION_UNDESIRED); +} + +/* Tests for hdcp_get_link_display_adjustments() */ + +/** + * dm_test_hdcp_get_adjustments_disable_authentication - disable path zeroes adjustments + * @test: KUnit test context + * + * When enable_encryption is false, display_adjust should disable + * authentication and all link_adjust fields should remain zeroed. + */ +static void dm_test_hdcp_get_adjustments_disable_authentication(struct kunit *test) +{ + struct mod_hdcp_link_adjustment link_adjust; + struct mod_hdcp_display_adjustment display_adjust; + unsigned int disable; + unsigned int hdcp1_disable; + unsigned int force_type; + + hdcp_get_link_display_adjustments(false, DRM_MODE_HDCP_CONTENT_TYPE0, + false, false, false, &link_adjust, &display_adjust); + disable = display_adjust.disable; + hdcp1_disable = link_adjust.hdcp1.disable; + force_type = link_adjust.hdcp2.force_type; + + KUNIT_EXPECT_EQ(test, disable, + MOD_HDCP_DISPLAY_DISABLE_AUTHENTICATION); + KUNIT_EXPECT_EQ(test, link_adjust.auth_delay, 0); + KUNIT_EXPECT_EQ(test, link_adjust.retry_limit, 0); + KUNIT_EXPECT_EQ(test, hdcp1_disable, 0); + KUNIT_EXPECT_EQ(test, force_type, 0); +} + +/** + * dm_test_hdcp_get_adjustments_type0_policy - TYPE0 enables HDCP1 and forces TYPE0 + * @test: KUnit test context + * + * When encryption is enabled with content TYPE0, hdcp1 should remain + * enabled, force_type should be TYPE_0, and sw_locality_fallback should + * be propagated from the input parameter. + */ +static void dm_test_hdcp_get_adjustments_type0_policy(struct kunit *test) +{ + struct mod_hdcp_link_adjustment link_adjust; + struct mod_hdcp_display_adjustment display_adjust; + unsigned int disable; + unsigned int hdcp1_disable; + unsigned int force_type; + + hdcp_get_link_display_adjustments(true, DRM_MODE_HDCP_CONTENT_TYPE0, + false, false, true, &link_adjust, &display_adjust); + disable = display_adjust.disable; + hdcp1_disable = link_adjust.hdcp1.disable; + force_type = link_adjust.hdcp2.force_type; + + KUNIT_EXPECT_EQ(test, disable, + MOD_HDCP_DISPLAY_NOT_DISABLE); + KUNIT_EXPECT_EQ(test, link_adjust.auth_delay, 2); + KUNIT_EXPECT_EQ(test, link_adjust.retry_limit, MAX_NUM_OF_ATTEMPTS); + KUNIT_EXPECT_EQ(test, hdcp1_disable, 0); + KUNIT_EXPECT_EQ(test, force_type, + MOD_HDCP_FORCE_TYPE_0); + KUNIT_EXPECT_FALSE(test, link_adjust.hdcp2.use_fw_locality_check); + KUNIT_EXPECT_TRUE(test, link_adjust.hdcp2.use_sw_locality_fallback); +} + +/** + * dm_test_hdcp_get_adjustments_type1_policy - TYPE1 disables HDCP1 and forces TYPE1 + * @test: KUnit test context + * + * When encryption is enabled with content TYPE1, hdcp1 should be + * disabled, force_type should be TYPE_1, and fw_locality_check should + * be enabled when hdcp_lc_force_fw_enable is set. + */ +static void dm_test_hdcp_get_adjustments_type1_policy(struct kunit *test) +{ + struct mod_hdcp_link_adjustment link_adjust; + struct mod_hdcp_display_adjustment display_adjust; + unsigned int disable; + unsigned int hdcp1_disable; + unsigned int force_type; + + hdcp_get_link_display_adjustments(true, DRM_MODE_HDCP_CONTENT_TYPE1, + false, true, false, &link_adjust, &display_adjust); + disable = display_adjust.disable; + hdcp1_disable = link_adjust.hdcp1.disable; + force_type = link_adjust.hdcp2.force_type; + + KUNIT_EXPECT_EQ(test, disable, + MOD_HDCP_DISPLAY_NOT_DISABLE); + KUNIT_EXPECT_EQ(test, link_adjust.auth_delay, 2); + KUNIT_EXPECT_EQ(test, link_adjust.retry_limit, MAX_NUM_OF_ATTEMPTS); + KUNIT_EXPECT_EQ(test, hdcp1_disable, 1); + KUNIT_EXPECT_EQ(test, force_type, + MOD_HDCP_FORCE_TYPE_1); + KUNIT_EXPECT_TRUE(test, link_adjust.hdcp2.use_fw_locality_check); + KUNIT_EXPECT_FALSE(test, link_adjust.hdcp2.use_sw_locality_fallback); +} + +/** + * dm_test_hdcp_get_adjustments_fused_io_enables_fw_check - fused_io enables FW locality check + * @test: KUnit test context + * + * When fused_io_supported is true, use_fw_locality_check should be + * enabled regardless of hdcp_lc_force_fw_enable. + */ +static void dm_test_hdcp_get_adjustments_fused_io_enables_fw_check(struct kunit *test) +{ + struct mod_hdcp_link_adjustment link_adjust; + struct mod_hdcp_display_adjustment display_adjust; + + hdcp_get_link_display_adjustments(true, DRM_MODE_HDCP_CONTENT_TYPE0, + true, false, false, &link_adjust, &display_adjust); + + KUNIT_EXPECT_TRUE(test, link_adjust.hdcp2.use_fw_locality_check); +} + /* Tests for process_output() */ -/* - * Helper: allocate and initialise a minimal hdcp_workqueue sufficient for - * process_output() testing. Only the three delayed works accessed by +/** + * alloc_test_workqueue - allocate a minimal hdcp_workqueue for testing + * @test: KUnit test context for managed allocation + * + * Allocates and initialises 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) @@ -33,9 +263,12 @@ static struct hdcp_workqueue *alloc_test_workqueue(struct kunit *test) return work; } -/* +/** + * dm_test_process_output_property_validate_always_scheduled - validate_dwork always queued + * @test: KUnit test context + * * process_output() always schedules property_validate_dwork with delay=0, - * which queues the work item directly (bypassing the timer). Use + * which queues the work item directly (bypassing the timer). Uses * work_pending() rather than delayed_work_pending() to detect this. */ static void dm_test_process_output_property_validate_always_scheduled(struct kunit *test) @@ -52,8 +285,12 @@ static void dm_test_process_output_property_validate_always_scheduled(struct kun cancel_delayed_work_sync(&work->property_validate_dwork); } -/* - * output.callback_needed=true must schedule callback_dwork. +/** + * dm_test_process_output_callback_needed - callback_needed schedules callback_dwork + * @test: KUnit test context + * + * When output.callback_needed is true, process_output() must schedule + * callback_dwork with the specified delay. */ static void dm_test_process_output_callback_needed(struct kunit *test) { @@ -70,8 +307,12 @@ static void dm_test_process_output_callback_needed(struct kunit *test) cancel_delayed_work_sync(&work->property_validate_dwork); } -/* - * output.callback_stop=true must cancel a previously scheduled callback_dwork. +/** + * dm_test_process_output_callback_stop - callback_stop cancels callback_dwork + * @test: KUnit test context + * + * When output.callback_stop is true, process_output() must cancel a + * previously scheduled callback_dwork. */ static void dm_test_process_output_callback_stop(struct kunit *test) { @@ -90,8 +331,12 @@ static void dm_test_process_output_callback_stop(struct kunit *test) cancel_delayed_work_sync(&work->property_validate_dwork); } -/* - * output.watchdog_timer_needed=true must schedule watchdog_timer_dwork. +/** + * dm_test_process_output_watchdog_needed - watchdog_needed schedules watchdog_dwork + * @test: KUnit test context + * + * When output.watchdog_timer_needed is true, process_output() must + * schedule watchdog_timer_dwork with the specified delay. */ static void dm_test_process_output_watchdog_needed(struct kunit *test) { @@ -108,9 +353,12 @@ static void dm_test_process_output_watchdog_needed(struct kunit *test) cancel_delayed_work_sync(&work->property_validate_dwork); } -/* - * output.watchdog_timer_stop=true must cancel a previously scheduled - * watchdog_timer_dwork. +/** + * dm_test_process_output_watchdog_stop - watchdog_stop cancels watchdog_dwork + * @test: KUnit test context + * + * When output.watchdog_timer_stop is true, process_output() must cancel + * a previously scheduled watchdog_timer_dwork. */ static void dm_test_process_output_watchdog_stop(struct kunit *test) { @@ -129,9 +377,12 @@ static void dm_test_process_output_watchdog_stop(struct kunit *test) cancel_delayed_work_sync(&work->property_validate_dwork); } -/* - * Both callback_needed and watchdog_timer_needed set: both dworks are - * scheduled independently. +/** + * dm_test_process_output_callback_and_watchdog_needed - both dworks scheduled independently + * @test: KUnit test context + * + * When both callback_needed and watchdog_timer_needed are set, + * process_output() must schedule both dworks independently. */ static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *test) { @@ -154,6 +405,18 @@ static void dm_test_process_output_callback_and_watchdog_needed(struct kunit *te /* End of tests for process_output() */ static struct kunit_case dm_hdcp_test_cases[] = { + /* hdcp_get_content_protection_from_status() */ + KUNIT_CASE(dm_test_hdcp_get_cp_disabled_returns_desired), + KUNIT_CASE(dm_test_hdcp_get_cp_type0_returns_enabled), + KUNIT_CASE(dm_test_hdcp_get_cp_type1_returns_enabled), + KUNIT_CASE(dm_test_hdcp_get_cp_type1_rejects_type0_status), + KUNIT_CASE(dm_test_hdcp_get_cp_type0_rejects_type1_status), + /* hdcp_get_link_display_adjustments() */ + KUNIT_CASE(dm_test_hdcp_get_adjustments_disable_authentication), + KUNIT_CASE(dm_test_hdcp_get_adjustments_type0_policy), + KUNIT_CASE(dm_test_hdcp_get_adjustments_type1_policy), + KUNIT_CASE(dm_test_hdcp_get_adjustments_fused_io_enables_fw_check), + /* process_output() */ 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), diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c new file mode 100644 index 000000000000..33014a2d2222 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_helpers_test.c @@ -0,0 +1,634 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_helpers.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <drm/drm_edid.h> +#include <drm/drm_kunit_helpers.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "dm_helpers.h" +#include "ddc_service_types.h" +#include "amdgpu_dm_helpers.h" +#include "amdgpu_dm_kunit_test_helpers.h" + +/* Tests for edid_extract_panel_id() */ + +/** + * dm_test_edid_extract_panel_id_basic - Test Edid extract panel id basic + * @test: The KUnit test context + */ +static void dm_test_edid_extract_panel_id_basic(struct kunit *test) +{ + struct edid *edid; + u32 panel_id; + + edid = kunit_kzalloc(test, sizeof(*edid), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, edid); + + edid->mfg_id[0] = 0x12; + edid->mfg_id[1] = 0x34; + edid->prod_code[0] = 0xAB; + edid->prod_code[1] = 0xCD; + + panel_id = edid_extract_panel_id(edid); + + /* + * Expected: (0x12 << 24) | (0x34 << 16) | EDID_PRODUCT_ID(edid) + * EDID_PRODUCT_ID = prod_code[0] | (prod_code[1] << 8) = 0xAB | 0xCD00 = 0xCDAB + * Result: 0x12340000 | 0x0000CDAB = 0x1234CDAB + */ + KUNIT_EXPECT_EQ(test, panel_id, (u32)0x1234CDAB); +} + +/** + * dm_test_edid_extract_panel_id_zeros - Test Edid extract panel id zeros + * @test: The KUnit test context + */ +static void dm_test_edid_extract_panel_id_zeros(struct kunit *test) +{ + struct edid *edid; + + edid = kunit_kzalloc(test, sizeof(*edid), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, edid); + + KUNIT_EXPECT_EQ(test, edid_extract_panel_id(edid), 0U); +} + +/* Tests for dm_is_freesync_pcon_whitelist() */ + +/** + * dm_test_freesync_pcon_whitelist_all_known - Test all known Freesync Pcon whitelist entries + * @test: The KUnit test context + * + * Iterates over the driver's whitelist table directly so that any ID added + * to dm_freesync_pcon_whitelist[] is automatically covered by this test. + */ +static void dm_test_freesync_pcon_whitelist_all_known(struct kunit *test) +{ + u32 i; + + for (i = 0; i < dm_freesync_pcon_whitelist_count(); i++) + KUNIT_EXPECT_TRUE(test, + dm_is_freesync_pcon_whitelist(dm_freesync_pcon_whitelist[i])); +} + +/** + * dm_test_freesync_pcon_whitelist_not_in_list - Test Freesync pcon whitelist not in list + * @test: The KUnit test context + */ +static void dm_test_freesync_pcon_whitelist_not_in_list(struct kunit *test) +{ + /* 0xFFFFFF is not a known whitelist device */ + KUNIT_EXPECT_FALSE(test, dm_is_freesync_pcon_whitelist(0xFFFFFF)); +} + +/** + * dm_test_freesync_pcon_whitelist_zero - Test Freesync pcon whitelist zero + * @test: The KUnit test context + */ +static void dm_test_freesync_pcon_whitelist_zero(struct kunit *test) +{ + KUNIT_EXPECT_FALSE(test, dm_is_freesync_pcon_whitelist(0)); +} + +/* Tests for populate_hdmi_info_from_connector() */ + +/** + * dm_test_populate_hdmi_scdc_present_true - Test Populate hdmi scdc present true + * @test: The KUnit test context + */ +static void dm_test_populate_hdmi_scdc_present_true(struct kunit *test) +{ + struct drm_hdmi_info *hdmi; + struct dc_edid_caps *caps; + + hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, hdmi); + caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, caps); + + hdmi->scdc.supported = true; + + populate_hdmi_info_from_connector(true, hdmi, caps); + + KUNIT_EXPECT_TRUE(test, caps->scdc_present); +} + +/** + * dm_test_populate_hdmi_scdc_present_false - Test Populate hdmi scdc present false + * @test: The KUnit test context + */ +static void dm_test_populate_hdmi_scdc_present_false(struct kunit *test) +{ + struct drm_hdmi_info *hdmi; + struct dc_edid_caps *caps; + + hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, hdmi); + caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, caps); + + hdmi->scdc.supported = false; + caps->scdc_present = true; /* pre-set to confirm it gets cleared */ + + populate_hdmi_info_from_connector(true, hdmi, caps); + + KUNIT_EXPECT_FALSE(test, caps->scdc_present); +} + +/** + * dm_test_populate_hdmi_frl_dsc_10bpc - Test HDMI FRL DSC 10 bpc caps + * @test: The KUnit test context + */ +static void dm_test_populate_hdmi_frl_dsc_10bpc(struct kunit *test) +{ + struct drm_hdmi_info *hdmi; + struct dc_edid_caps *caps; + + hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, hdmi); + caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, caps); + + hdmi->max_lanes = 4; + hdmi->max_frl_rate_per_lane = 12; + hdmi->dsc_cap.v_1p2 = true; + hdmi->dsc_cap.bpc_supported = 10; + hdmi->dsc_cap.all_bpp = true; + hdmi->dsc_cap.native_420 = true; + hdmi->dsc_cap.max_slices = 8; + hdmi->dsc_cap.clk_per_slice = 400; + hdmi->dsc_cap.max_lanes = 4; + hdmi->dsc_cap.max_frl_rate_per_lane = 10; + hdmi->dsc_cap.total_chunk_kbytes = 7; + + populate_hdmi_info_from_connector(true, hdmi, caps); + + KUNIT_EXPECT_EQ(test, caps->max_frl_rate, 6); + KUNIT_EXPECT_TRUE(test, caps->frl_dsc_support); + KUNIT_EXPECT_TRUE(test, caps->frl_dsc_10bpc); + KUNIT_EXPECT_FALSE(test, caps->frl_dsc_12bpc); + KUNIT_EXPECT_TRUE(test, caps->frl_dsc_all_bpp); + KUNIT_EXPECT_TRUE(test, caps->frl_dsc_native_420); + KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_slices, 5); + KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_frl_rate, 5); + KUNIT_EXPECT_EQ(test, caps->frl_dsc_total_chunk_kbytes, 7); +} + +/** + * dm_test_populate_hdmi_frl_dsc_12bpc - Test HDMI FRL DSC 12 bpc caps + * @test: The KUnit test context + */ +static void dm_test_populate_hdmi_frl_dsc_12bpc(struct kunit *test) +{ + struct drm_hdmi_info *hdmi; + struct dc_edid_caps *caps; + + hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, hdmi); + caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, caps); + + hdmi->max_lanes = 3; + hdmi->max_frl_rate_per_lane = 6; + hdmi->dsc_cap.v_1p2 = true; + hdmi->dsc_cap.bpc_supported = 12; + hdmi->dsc_cap.max_slices = 16; + hdmi->dsc_cap.clk_per_slice = 400; + hdmi->dsc_cap.max_lanes = 3; + hdmi->dsc_cap.max_frl_rate_per_lane = 3; + + populate_hdmi_info_from_connector(true, hdmi, caps); + + KUNIT_EXPECT_EQ(test, caps->max_frl_rate, 2); + KUNIT_EXPECT_TRUE(test, caps->frl_dsc_support); + KUNIT_EXPECT_FALSE(test, caps->frl_dsc_10bpc); + KUNIT_EXPECT_TRUE(test, caps->frl_dsc_12bpc); + KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_slices, 7); + KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_frl_rate, 1); +} + +/** + * dm_test_populate_hdmi_frl_dsc_unknown_values - Test HDMI FRL DSC unknown values + * @test: The KUnit test context + */ +static void dm_test_populate_hdmi_frl_dsc_unknown_values(struct kunit *test) +{ + struct drm_hdmi_info *hdmi; + struct dc_edid_caps *caps; + + hdmi = kunit_kzalloc(test, sizeof(*hdmi), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, hdmi); + caps = kunit_kzalloc(test, sizeof(*caps), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, caps); + + hdmi->max_lanes = 2; + hdmi->max_frl_rate_per_lane = 3; + hdmi->dsc_cap.v_1p2 = true; + hdmi->dsc_cap.bpc_supported = 8; + hdmi->dsc_cap.max_slices = 3; + hdmi->dsc_cap.clk_per_slice = 340; + hdmi->dsc_cap.max_lanes = 2; + hdmi->dsc_cap.max_frl_rate_per_lane = 12; + + populate_hdmi_info_from_connector(true, hdmi, caps); + + KUNIT_EXPECT_EQ(test, caps->max_frl_rate, 0); + KUNIT_EXPECT_TRUE(test, caps->frl_dsc_support); + KUNIT_EXPECT_FALSE(test, caps->frl_dsc_10bpc); + KUNIT_EXPECT_FALSE(test, caps->frl_dsc_12bpc); + KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_slices, 0); + KUNIT_EXPECT_EQ(test, caps->frl_dsc_max_frl_rate, 0); +} + +/* Tests for dm_get_adaptive_sync_support_type() */ + +/** + * dm_test_adaptive_sync_type_none_default - Test Adaptive sync type none default + * @test: The KUnit test context + */ +static void dm_test_adaptive_sync_type_none_default(struct kunit *test) +{ + struct dc_link *link; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + /* dongle_type = 0 (DISPLAY_DONGLE_NONE) → default case → TYPE_NONE */ + KUNIT_EXPECT_EQ(test, + (int)dm_get_adaptive_sync_support_type(link), + (int)ADAPTIVE_SYNC_TYPE_NONE); +} + +/** + * dm_test_adaptive_sync_type_converter_no_conditions - Converter without caps + * @test: The KUnit test context + */ +static void dm_test_adaptive_sync_type_converter_no_conditions(struct kunit *test) +{ + struct dc_link *link; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + /* HDMI converter but no adaptive sync cap → still NONE */ + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER; + + KUNIT_EXPECT_EQ(test, + (int)dm_get_adaptive_sync_support_type(link), + (int)ADAPTIVE_SYNC_TYPE_NONE); +} + +/** + * dm_test_adaptive_sync_type_converter_partial_conditions - Partial caps + * @test: The KUnit test context + */ +static void dm_test_adaptive_sync_type_converter_partial_conditions(struct kunit *test) +{ + struct dc_link *link; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + /* Cap set and whitelist ID, but allow_invalid_MSA_timing_param = false */ + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER; + link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1; + link->dpcd_caps.allow_invalid_MSA_timing_param = false; + link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_0060AD; + + KUNIT_EXPECT_EQ(test, + (int)dm_get_adaptive_sync_support_type(link), + (int)ADAPTIVE_SYNC_TYPE_NONE); +} + +/** + * dm_test_adaptive_sync_type_pcon_whitelist - Test Adaptive sync type pcon whitelist + * @test: The KUnit test context + */ +static void dm_test_adaptive_sync_type_pcon_whitelist(struct kunit *test) +{ + struct dc_link *link; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + /* All conditions met → FREESYNC_TYPE_PCON_IN_WHITELIST */ + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER; + link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1; + link->dpcd_caps.allow_invalid_MSA_timing_param = true; + link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_0060AD; + + KUNIT_EXPECT_EQ(test, + (int)dm_get_adaptive_sync_support_type(link), + (int)FREESYNC_TYPE_PCON_IN_WHITELIST); +} + +/** + * dm_test_adaptive_sync_type_converter_nonwhitelist - Converter not whitelisted + * @test: The KUnit test context + */ +static void dm_test_adaptive_sync_type_converter_nonwhitelist(struct kunit *test) +{ + struct dc_link *link; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + /* All conditions met but branch_dev_id not in whitelist → NONE */ + link->dpcd_caps.dongle_type = DISPLAY_DONGLE_DP_HDMI_CONVERTER; + link->dpcd_caps.adaptive_sync_caps.dp_adap_sync_caps.bits.ADAPTIVE_SYNC_SDP_SUPPORT = 1; + link->dpcd_caps.allow_invalid_MSA_timing_param = true; + link->dpcd_caps.branch_dev_id = 0xFFFFFF; + + KUNIT_EXPECT_EQ(test, + (int)dm_get_adaptive_sync_support_type(link), + (int)ADAPTIVE_SYNC_TYPE_NONE); +} + +/* Tests for dm_helpers_is_fullscreen() and dm_helpers_is_hdr_on() */ + +/** + * dm_test_helpers_is_fullscreen_returns_false - Test Helpers is fullscreen returns false + * @test: The KUnit test context + */ +static void dm_test_helpers_is_fullscreen_returns_false(struct kunit *test) +{ + /* Stub — always returns false */ + KUNIT_EXPECT_FALSE(test, dm_helpers_is_fullscreen(NULL, NULL)); +} + +/** + * dm_test_helpers_is_hdr_on_returns_false - Test Helpers is hdr on returns false + * @test: The KUnit test context + */ +static void dm_test_helpers_is_hdr_on_returns_false(struct kunit *test) +{ + /* Stub — always returns false */ + KUNIT_EXPECT_FALSE(test, dm_helpers_is_hdr_on(NULL, NULL)); +} + +/* Tests for get_max_frl_rate() */ + +/** + * dm_test_get_max_frl_rate_3lanes_3gbps - Test Get max frl rate 3lanes 3gbps + * @test: The KUnit test context + */ +static void dm_test_get_max_frl_rate_3lanes_3gbps(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, get_max_frl_rate(3, 3), 1); +} + +/** + * dm_test_get_max_frl_rate_3lanes_6gbps - Test Get max frl rate 3lanes 6gbps + * @test: The KUnit test context + */ +static void dm_test_get_max_frl_rate_3lanes_6gbps(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, get_max_frl_rate(3, 6), 2); +} + +/** + * dm_test_get_max_frl_rate_4lanes_6gbps - Test Get max frl rate 4lanes 6gbps + * @test: The KUnit test context + */ +static void dm_test_get_max_frl_rate_4lanes_6gbps(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, get_max_frl_rate(4, 6), 3); +} + +/** + * dm_test_get_max_frl_rate_4lanes_8gbps - Test Get max frl rate 4lanes 8gbps + * @test: The KUnit test context + */ +static void dm_test_get_max_frl_rate_4lanes_8gbps(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, get_max_frl_rate(4, 8), 4); +} + +/** + * dm_test_get_max_frl_rate_4lanes_10gbps - Test Get max frl rate 4lanes 10gbps + * @test: The KUnit test context + */ +static void dm_test_get_max_frl_rate_4lanes_10gbps(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, get_max_frl_rate(4, 10), 5); +} + +/** + * dm_test_get_max_frl_rate_4lanes_12gbps - Test Get max frl rate 4lanes 12gbps + * @test: The KUnit test context + */ +static void dm_test_get_max_frl_rate_4lanes_12gbps(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, get_max_frl_rate(4, 12), 6); +} + +/** + * dm_test_get_max_frl_rate_unknown - Test Get max frl rate unknown + * @test: The KUnit test context + */ +static void dm_test_get_max_frl_rate_unknown(struct kunit *test) +{ + /* Unknown lane/rate combination → 0 */ + KUNIT_EXPECT_EQ(test, get_max_frl_rate(2, 3), 0); +} + +/* Tests for dm_dtn_log_begin() / dm_dtn_log_append_v() / dm_dtn_log_end() */ + +/** + * dm_test_dtn_log_buffer_accumulates - Test DTN log buffer accumulation + * @test: The KUnit test context + */ +static void dm_test_dtn_log_buffer_accumulates(struct kunit *test) +{ + struct dc_log_buffer_ctx log_ctx = {0}; + + dm_dtn_log_begin(NULL, &log_ctx); + dm_dtn_log_append_v(NULL, &log_ctx, "x=%d\n", 7); + dm_dtn_log_end(NULL, &log_ctx); + + KUNIT_ASSERT_NOT_NULL(test, log_ctx.buf); + KUNIT_EXPECT_STREQ(test, log_ctx.buf, "[dtn begin]\nx=7\n[dtn end]\n"); + KUNIT_EXPECT_EQ(test, log_ctx.pos, strlen("[dtn begin]\nx=7\n[dtn end]\n")); + + kvfree(log_ctx.buf); +} + +/** + * dm_test_dtn_log_null_ctx_no_crash - Test DTN log helpers with NULL log buffer + * @test: The KUnit test context + */ +static void dm_test_dtn_log_null_ctx_no_crash(struct kunit *test) +{ + /* NULL log_ctx redirects to dmesg and must not dereference a buffer */ + dm_dtn_log_begin(NULL, NULL); + dm_dtn_log_append_v(NULL, NULL, "value %d\n", 1); + dm_dtn_log_end(NULL, NULL); + + KUNIT_EXPECT_TRUE(test, true); +} + +/* Tests for dm_helpers_dp_read_dpcd() / dm_helpers_dp_write_dpcd() */ + +/** + * dm_test_dp_read_dpcd_null_priv - Test DPCD read returns false without connector + * @test: The KUnit test context + */ +static void dm_test_dp_read_dpcd_null_priv(struct kunit *test) +{ + struct dc_link *link; + uint8_t data = 0; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + /* link->priv (aconnector) is NULL → early return false */ + KUNIT_EXPECT_FALSE(test, + dm_helpers_dp_read_dpcd(NULL, link, 0, &data, sizeof(data))); +} + +/** + * dm_test_dp_write_dpcd_null_priv - Test DPCD write returns false without connector + * @test: The KUnit test context + */ +static void dm_test_dp_write_dpcd_null_priv(struct kunit *test) +{ + struct dc_link *link; + uint8_t data = 0; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + /* link->priv (aconnector) is NULL → early return false */ + KUNIT_EXPECT_FALSE(test, + dm_helpers_dp_write_dpcd(NULL, link, 0, &data, sizeof(data))); +} + +/* Tests for dm_helpers_dp_mst_start_top_mgr() / dm_helpers_dp_mst_stop_top_mgr() */ + +/** + * dm_test_mst_start_top_mgr_null_priv - Test MST start returns false without connector + * @test: The KUnit test context + */ +static void dm_test_mst_start_top_mgr_null_priv(struct kunit *test) +{ + struct dc_link *link; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + KUNIT_EXPECT_FALSE(test, dm_helpers_dp_mst_start_top_mgr(NULL, link, false)); +} + +/** + * dm_test_mst_stop_top_mgr_null_priv - Test MST stop returns false without connector + * @test: The KUnit test context + */ +static void dm_test_mst_stop_top_mgr_null_priv(struct kunit *test) +{ + struct dc_link *link; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + KUNIT_EXPECT_FALSE(test, dm_helpers_dp_mst_stop_top_mgr(NULL, link)); +} + +/** + * dm_test_mst_start_top_mgr_boot - Test MST start boot path on a connector-backed link + * @test: The KUnit test context + * + * Uses the DRM KUnit mock device to back the connector so the link is a + * realistic connector-backed link. The boot path short-circuits and returns + * true without touching the MST topology manager. + */ +static void dm_test_mst_start_top_mgr_boot(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct amdgpu_device *adev; + struct dc_link *link; + + adev = dm_kunit_alloc_adev(test); + + link = dm_kunit_alloc_link(test); + + aconnector = dm_kunit_alloc_connector(test, adev, NULL); + + link->priv = aconnector; + + KUNIT_EXPECT_TRUE(test, dm_helpers_dp_mst_start_top_mgr(NULL, link, true)); +} + +/* Tests for dm_helpers_dp_write_hblank_reduction() */ + +/** + * dm_test_dp_write_hblank_reduction_false - Test hblank reduction stub returns false + * @test: The KUnit test context + */ +static void dm_test_dp_write_hblank_reduction_false(struct kunit *test) +{ + KUNIT_EXPECT_FALSE(test, dm_helpers_dp_write_hblank_reduction(NULL, NULL)); +} + +static struct kunit_case amdgpu_dm_helpers_test_cases[] = { + /* edid_extract_panel_id */ + KUNIT_CASE(dm_test_edid_extract_panel_id_basic), + KUNIT_CASE(dm_test_edid_extract_panel_id_zeros), + /* dm_is_freesync_pcon_whitelist */ + KUNIT_CASE(dm_test_freesync_pcon_whitelist_all_known), + KUNIT_CASE(dm_test_freesync_pcon_whitelist_not_in_list), + KUNIT_CASE(dm_test_freesync_pcon_whitelist_zero), + /* populate_hdmi_info_from_connector */ + KUNIT_CASE(dm_test_populate_hdmi_scdc_present_true), + KUNIT_CASE(dm_test_populate_hdmi_scdc_present_false), + KUNIT_CASE(dm_test_populate_hdmi_frl_dsc_10bpc), + KUNIT_CASE(dm_test_populate_hdmi_frl_dsc_12bpc), + KUNIT_CASE(dm_test_populate_hdmi_frl_dsc_unknown_values), + /* dm_get_adaptive_sync_support_type */ + KUNIT_CASE(dm_test_adaptive_sync_type_none_default), + KUNIT_CASE(dm_test_adaptive_sync_type_converter_no_conditions), + KUNIT_CASE(dm_test_adaptive_sync_type_converter_partial_conditions), + KUNIT_CASE(dm_test_adaptive_sync_type_pcon_whitelist), + KUNIT_CASE(dm_test_adaptive_sync_type_converter_nonwhitelist), + /* dm_helpers_is_fullscreen / dm_helpers_is_hdr_on */ + KUNIT_CASE(dm_test_helpers_is_fullscreen_returns_false), + KUNIT_CASE(dm_test_helpers_is_hdr_on_returns_false), + /* get_max_frl_rate */ + KUNIT_CASE(dm_test_get_max_frl_rate_3lanes_3gbps), + KUNIT_CASE(dm_test_get_max_frl_rate_3lanes_6gbps), + KUNIT_CASE(dm_test_get_max_frl_rate_4lanes_6gbps), + KUNIT_CASE(dm_test_get_max_frl_rate_4lanes_8gbps), + KUNIT_CASE(dm_test_get_max_frl_rate_4lanes_10gbps), + KUNIT_CASE(dm_test_get_max_frl_rate_4lanes_12gbps), + KUNIT_CASE(dm_test_get_max_frl_rate_unknown), + /* dm_dtn_log_begin / dm_dtn_log_append_v / dm_dtn_log_end */ + KUNIT_CASE(dm_test_dtn_log_buffer_accumulates), + KUNIT_CASE(dm_test_dtn_log_null_ctx_no_crash), + /* dm_helpers_dp_read_dpcd / dm_helpers_dp_write_dpcd */ + KUNIT_CASE(dm_test_dp_read_dpcd_null_priv), + KUNIT_CASE(dm_test_dp_write_dpcd_null_priv), + /* dm_helpers_dp_mst_start_top_mgr / dm_helpers_dp_mst_stop_top_mgr */ + KUNIT_CASE(dm_test_mst_start_top_mgr_null_priv), + KUNIT_CASE(dm_test_mst_stop_top_mgr_null_priv), + KUNIT_CASE(dm_test_mst_start_top_mgr_boot), + /* dm_helpers_dp_write_hblank_reduction */ + KUNIT_CASE(dm_test_dp_write_hblank_reduction_false), + {} +}; + +static struct kunit_suite amdgpu_dm_helpers_test_suite = { + .name = "amdgpu_dm_helpers", + .test_cases = amdgpu_dm_helpers_test_cases, +}; + +kunit_test_suite(amdgpu_dm_helpers_test_suite); + +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_helpers"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c new file mode 100644 index 000000000000..a73a6dd146d6 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_irq_test.c @@ -0,0 +1,909 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_irq.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <drm/drm_kunit_helpers.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_irq.h" +#include "amdgpu_dm_kunit_test_helpers.h" +#include "dmub/dmub_srv.h" + +static void dm_test_irq_handler(void *arg) +{ +} + +static void dm_test_irq_handler_alt(void *arg) +{ +} + +static void dm_test_crtc_list_del(void *data) +{ + struct amdgpu_crtc *acrtc = data; + + list_del_init(&acrtc->base.head); +} + +/* Tests for amdgpu_dm_hpd_to_dal_irq_source() */ + +/** + * dm_test_hpd_to_dal_irq_source_hpd1 - Test Hpd to dal irq source hpd1 + * @test: The KUnit test context + */ +static void dm_test_hpd_to_dal_irq_source_hpd1(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_1), + (int)DC_IRQ_SOURCE_HPD1); +} + +/** + * dm_test_hpd_to_dal_irq_source_hpd2 - Test Hpd to dal irq source hpd2 + * @test: The KUnit test context + */ +static void dm_test_hpd_to_dal_irq_source_hpd2(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_2), + (int)DC_IRQ_SOURCE_HPD2); +} + +/** + * dm_test_hpd_to_dal_irq_source_hpd3 - Test Hpd to dal irq source hpd3 + * @test: The KUnit test context + */ +static void dm_test_hpd_to_dal_irq_source_hpd3(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_3), + (int)DC_IRQ_SOURCE_HPD3); +} + +/** + * dm_test_hpd_to_dal_irq_source_hpd4 - Test Hpd to dal irq source hpd4 + * @test: The KUnit test context + */ +static void dm_test_hpd_to_dal_irq_source_hpd4(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_4), + (int)DC_IRQ_SOURCE_HPD4); +} + +/** + * dm_test_hpd_to_dal_irq_source_hpd5 - Test Hpd to dal irq source hpd5 + * @test: The KUnit test context + */ +static void dm_test_hpd_to_dal_irq_source_hpd5(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_5), + (int)DC_IRQ_SOURCE_HPD5); +} + +/** + * dm_test_hpd_to_dal_irq_source_hpd6 - Test Hpd to dal irq source hpd6 + * @test: The KUnit test context + */ +static void dm_test_hpd_to_dal_irq_source_hpd6(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_6), + (int)DC_IRQ_SOURCE_HPD6); +} + +/** + * dm_test_hpd_to_dal_irq_source_invalid - Test Hpd to dal irq source invalid + * @test: The KUnit test context + */ +static void dm_test_hpd_to_dal_irq_source_invalid(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(AMDGPU_HPD_NONE), + (int)DC_IRQ_SOURCE_INVALID); +} + +/** + * dm_test_hpd_to_dal_irq_source_out_of_range - Test Hpd to dal irq source out of range + * @test: The KUnit test context + */ +static void dm_test_hpd_to_dal_irq_source_out_of_range(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)amdgpu_dm_hpd_to_dal_irq_source(99), + (int)DC_IRQ_SOURCE_INVALID); +} + +/* Tests for are_sinks_equal() */ + +/** + * dm_test_are_sinks_equal_both_null - Test Are sinks equal both null + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_both_null(struct kunit *test) +{ + KUNIT_EXPECT_FALSE(test, are_sinks_equal(NULL, NULL)); +} + +/** + * dm_test_are_sinks_equal_first_null - Test Are sinks equal first null + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_first_null(struct kunit *test) +{ + struct dc_sink *sink2; + + sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2); + + KUNIT_EXPECT_FALSE(test, are_sinks_equal(NULL, sink2)); +} + +/** + * dm_test_are_sinks_equal_second_null - Test Are sinks equal second null + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_second_null(struct kunit *test) +{ + struct dc_sink *sink1; + + sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1); + + KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, NULL)); +} + +/** + * dm_test_are_sinks_equal_different_signal - Test Are sinks equal different signal + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_different_signal(struct kunit *test) +{ + struct dc_sink *sink1, *sink2; + + sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1); + sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2); + + sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink2->sink_signal = SIGNAL_TYPE_DISPLAY_PORT; + + KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, sink2)); +} + +/** + * dm_test_are_sinks_equal_different_edid_length - Test Are sinks equal different edid length + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_different_edid_length(struct kunit *test) +{ + struct dc_sink *sink1, *sink2; + + sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1); + sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2); + + sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink1->dc_edid.length = 128; + sink2->dc_edid.length = 256; + + KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, sink2)); +} + +/** + * dm_test_are_sinks_equal_different_edid_data - Test Are sinks equal different edid data + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_different_edid_data(struct kunit *test) +{ + struct dc_sink *sink1, *sink2; + + sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1); + sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2); + + sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink1->dc_edid.length = 4; + sink2->dc_edid.length = 4; + memset(sink1->dc_edid.raw_edid, 0xAA, 4); + memset(sink2->dc_edid.raw_edid, 0xBB, 4); + + KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, sink2)); +} + +/** + * dm_test_are_sinks_equal_identical - Test Are sinks equal identical + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_identical(struct kunit *test) +{ + struct dc_sink *sink1, *sink2; + + sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1); + sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2); + + sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink1->dc_edid.length = 4; + sink2->dc_edid.length = 4; + memset(sink1->dc_edid.raw_edid, 0xAA, 4); + memset(sink2->dc_edid.raw_edid, 0xAA, 4); + + KUNIT_EXPECT_TRUE(test, are_sinks_equal(sink1, sink2)); +} + +/** + * dm_test_are_sinks_equal_zero_length - Test Are sinks equal zero length + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_zero_length(struct kunit *test) +{ + struct dc_sink *sink1, *sink2; + + sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1); + sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2); + + sink1->sink_signal = SIGNAL_TYPE_DISPLAY_PORT; + sink2->sink_signal = SIGNAL_TYPE_DISPLAY_PORT; + sink1->dc_edid.length = 0; + sink2->dc_edid.length = 0; + + KUNIT_EXPECT_TRUE(test, are_sinks_equal(sink1, sink2)); +} + +/** + * dm_test_are_sinks_equal_full_edid_identical - Test Are sinks equal full edid identical + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_full_edid_identical(struct kunit *test) +{ + struct dc_sink *sink1, *sink2; + + sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1); + sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2); + + sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink1->dc_edid.length = 128; + sink2->dc_edid.length = 128; + memset(sink1->dc_edid.raw_edid, 0x5A, 128); + memset(sink2->dc_edid.raw_edid, 0x5A, 128); + + KUNIT_EXPECT_TRUE(test, are_sinks_equal(sink1, sink2)); +} + +/** + * dm_test_are_sinks_equal_full_edid_last_byte_differs - Test Are sinks equal last byte differs + * @test: The KUnit test context + */ +static void dm_test_are_sinks_equal_full_edid_last_byte_differs(struct kunit *test) +{ + struct dc_sink *sink1, *sink2; + + sink1 = kunit_kzalloc(test, sizeof(*sink1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink1); + sink2 = kunit_kzalloc(test, sizeof(*sink2), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, sink2); + + sink1->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink2->sink_signal = SIGNAL_TYPE_HDMI_TYPE_A; + sink1->dc_edid.length = 128; + sink2->dc_edid.length = 128; + memset(sink1->dc_edid.raw_edid, 0x5A, 128); + memset(sink2->dc_edid.raw_edid, 0x5A, 128); + sink2->dc_edid.raw_edid[127] = 0x5B; + + KUNIT_EXPECT_FALSE(test, are_sinks_equal(sink1, sink2)); +} + +/* Tests for dmub_notification_type_str() */ + +/** + * dm_test_notification_str_no_data - Test Notification str no data + * @test: The KUnit test context + */ +static void dm_test_notification_str_no_data(struct kunit *test) +{ + KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_NO_DATA), "NO_DATA"); +} + +/** + * dm_test_notification_str_aux_reply - Test Notification str aux reply + * @test: The KUnit test context + */ +static void dm_test_notification_str_aux_reply(struct kunit *test) +{ + KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_AUX_REPLY), "AUX_REPLY"); +} + +/** + * dm_test_notification_str_hpd - Test Notification str hpd + * @test: The KUnit test context + */ +static void dm_test_notification_str_hpd(struct kunit *test) +{ + KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_HPD), "HPD"); +} + +/** + * dm_test_notification_str_hpd_irq - Test Notification str hpd irq + * @test: The KUnit test context + */ +static void dm_test_notification_str_hpd_irq(struct kunit *test) +{ + KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_HPD_IRQ), "HPD_IRQ"); +} + +/** + * dm_test_notification_str_set_config - Test Notification str set config + * @test: The KUnit test context + */ +static void dm_test_notification_str_set_config(struct kunit *test) +{ + KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_SET_CONFIG_REPLY), + "SET_CONFIG_REPLY"); +} + +/** + * dm_test_notification_str_dpia - Test Notification str dpia + * @test: The KUnit test context + */ +static void dm_test_notification_str_dpia(struct kunit *test) +{ + KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_DPIA_NOTIFICATION), + "DPIA_NOTIFICATION"); +} + +/** + * dm_test_notification_str_hpd_sense - Test Notification str hpd sense + * @test: The KUnit test context + */ +static void dm_test_notification_str_hpd_sense(struct kunit *test) +{ + KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_HPD_SENSE_NOTIFY), + "HPD_SENSE_NOTIFY"); +} + +/** + * dm_test_notification_str_fused_io - Test Notification str fused io + * @test: The KUnit test context + */ +static void dm_test_notification_str_fused_io(struct kunit *test) +{ + KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_FUSED_IO), + "FUSED_IO"); +} + +/** + * dm_test_notification_str_unknown - Test Notification str unknown + * @test: The KUnit test context + */ +static void dm_test_notification_str_unknown(struct kunit *test) +{ + KUNIT_EXPECT_STREQ(test, dmub_notification_type_str(DMUB_NOTIFICATION_MAX), "<unknown>"); +} + +/* Tests for amdgpu_dm_irq_init() */ + +/** + * dm_test_irq_init_initializes_lists - Test irq init initializes list heads + * @test: The KUnit test context + */ +static void dm_test_irq_init_initializes_lists(struct kunit *test) +{ + struct amdgpu_device *adev; + int src; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) { + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[src])); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[src])); + } +} + +/* Tests for amdgpu_dm_irq_register_interrupt() */ + +/** + * dm_test_irq_register_rejects_null_params - Test register rejects null params + * @test: The KUnit test context + */ +static void dm_test_irq_register_rejects_null_params(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_interrupt_params int_params = { 0 }; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = DC_IRQ_SOURCE_HPD1; + + KUNIT_EXPECT_NULL(test, + amdgpu_dm_irq_register_interrupt(adev, NULL, + dm_test_irq_handler, NULL)); + KUNIT_EXPECT_NULL(test, + amdgpu_dm_irq_register_interrupt(adev, &int_params, NULL, NULL)); +} + +/** + * dm_test_irq_register_rejects_invalid_context - Test register rejects context + * @test: The KUnit test context + */ +static void dm_test_irq_register_rejects_invalid_context(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_interrupt_params int_params = { 0 }; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + int_params.int_context = INTERRUPT_CONTEXT_NUMBER; + int_params.irq_source = DC_IRQ_SOURCE_HPD1; + + KUNIT_EXPECT_NULL(test, + amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, NULL)); +} + +/** + * dm_test_irq_register_rejects_invalid_source - Test register rejects source + * @test: The KUnit test context + */ +static void dm_test_irq_register_rejects_invalid_source(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_interrupt_params int_params = { 0 }; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = DC_IRQ_SOURCE_INVALID; + + KUNIT_EXPECT_NULL(test, + amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, NULL)); +} + +/** + * dm_test_irq_register_adds_low_context_handler - Test register adds low handler + * @test: The KUnit test context + */ +static void dm_test_irq_register_adds_low_context_handler(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_interrupt_params int_params = { 0 }; + void *handler; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = DC_IRQ_SOURCE_HPD1; + + handler = amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, adev); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler); + KUNIT_EXPECT_FALSE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1])); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD1])); + + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1, + dm_test_irq_handler); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1])); +} + +/** + * dm_test_irq_register_adds_high_context_handler - Test register adds high handler + * @test: The KUnit test context + */ +static void dm_test_irq_register_adds_high_context_handler(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_interrupt_params int_params = { 0 }; + void *handler; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = DC_IRQ_SOURCE_HPD2; + + handler = amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, adev); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler); + KUNIT_EXPECT_FALSE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD2])); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD2])); + + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD2, + dm_test_irq_handler); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD2])); +} + +/** + * dm_test_irq_register_multiple_handlers - Test register keeps multiple handlers + * @test: The KUnit test context + */ +static void dm_test_irq_register_multiple_handlers(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_interrupt_params int_params = { 0 }; + struct list_head *hnd_list; + void *handler1, *handler2; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = DC_IRQ_SOURCE_HPD1; + + handler1 = amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler1); + handler2 = amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler_alt, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler2); + + hnd_list = &adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1]; + KUNIT_EXPECT_EQ(test, list_count_nodes(hnd_list), 2); + + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1, + dm_test_irq_handler); + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1, + dm_test_irq_handler_alt); + KUNIT_EXPECT_TRUE(test, list_empty(hnd_list)); +} + +/** + * dm_test_irq_register_separate_contexts - Test register same source in two contexts + * @test: The KUnit test context + */ +static void dm_test_irq_register_separate_contexts(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_interrupt_params int_params = { 0 }; + void *handler; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + int_params.irq_source = DC_IRQ_SOURCE_HPD5; + + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + handler = amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler); + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + handler = amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler); + + KUNIT_EXPECT_FALSE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD5])); + KUNIT_EXPECT_FALSE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD5])); + + /* + * A single unregister call stops at the first context where the handler + * is found (low context), leaving the high context handler in place. + */ + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD5, + dm_test_irq_handler); + + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD5])); + KUNIT_EXPECT_FALSE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD5])); + + /* A second call removes the remaining high context handler. */ + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD5, + dm_test_irq_handler); + + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD5])); +} + +/* Tests for amdgpu_dm_irq_unregister_interrupt() */ + +/** + * dm_test_irq_unregister_rejects_invalid_source - Test unregister rejects source + * @test: The KUnit test context + */ +static void dm_test_irq_unregister_rejects_invalid_source(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_INVALID, + dm_test_irq_handler); + + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1])); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD1])); +} + +/** + * dm_test_irq_unregister_rejects_null_handler - Test unregister rejects handler + * @test: The KUnit test context + */ +static void dm_test_irq_unregister_rejects_null_handler(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1, + DAL_INVALID_IRQ_HANDLER_IDX); + + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1])); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD1])); +} + +/** + * dm_test_irq_unregister_handler_not_found - Test unregister keeps unmatched handler + * @test: The KUnit test context + */ +static void dm_test_irq_unregister_handler_not_found(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_interrupt_params int_params = { 0 }; + void *handler; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = DC_IRQ_SOURCE_HPD1; + handler = amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler); + + /* Unregister a handler that was never registered for this source. */ + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1, + dm_test_irq_handler_alt); + + /* The originally registered handler must still be present. */ + KUNIT_EXPECT_FALSE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1])); + + amdgpu_dm_irq_unregister_interrupt(adev, DC_IRQ_SOURCE_HPD1, + dm_test_irq_handler); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD1])); +} + +/* Tests for amdgpu_dm_irq_fini() */ + +/** + * dm_test_irq_fini_removes_registered_handlers - Test fini removes handlers + * @test: The KUnit test context + */ +static void dm_test_irq_fini_removes_registered_handlers(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_interrupt_params int_params = { 0 }; + void *handler; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + int_params.int_context = INTERRUPT_LOW_IRQ_CONTEXT; + int_params.irq_source = DC_IRQ_SOURCE_HPD3; + handler = amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler); + + int_params.int_context = INTERRUPT_HIGH_IRQ_CONTEXT; + int_params.irq_source = DC_IRQ_SOURCE_HPD4; + handler = amdgpu_dm_irq_register_interrupt(adev, &int_params, + dm_test_irq_handler, adev); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, handler); + + amdgpu_dm_irq_fini(adev); + + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[DC_IRQ_SOURCE_HPD3])); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[DC_IRQ_SOURCE_HPD4])); +} + +/** + * dm_test_irq_fini_on_empty_tables - Test fini on tables with no handlers + * @test: The KUnit test context + */ +static void dm_test_irq_fini_on_empty_tables(struct kunit *test) +{ + struct amdgpu_device *adev; + int src; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, adev); + KUNIT_ASSERT_EQ(test, amdgpu_dm_irq_init(adev), 0); + + amdgpu_dm_irq_fini(adev); + + for (src = 0; src < DAL_IRQ_SOURCES_NUMBER; src++) { + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_low_tab[src])); + KUNIT_EXPECT_TRUE(test, + list_empty(&adev->dm.irq_handler_list_high_tab[src])); + } +} + +/* Tests for amdgpu_dm_get_crtc_by_otg_inst() */ + +/** + * dm_test_get_crtc_by_otg_inst_returns_match - Test CRTC lookup by OTG instance + * @test: The KUnit test context + */ +static void dm_test_get_crtc_by_otg_inst_returns_match(struct kunit *test) +{ + struct amdgpu_crtc *acrtc_a, *acrtc_b; + struct amdgpu_device *adev; + struct drm_device *drm; + + adev = dm_kunit_alloc_adev(test); + drm = &adev->ddev; + + acrtc_a = kunit_kzalloc(test, sizeof(*acrtc_a), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc_a); + acrtc_b = kunit_kzalloc(test, sizeof(*acrtc_b), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc_b); + + INIT_LIST_HEAD(&acrtc_a->base.head); + INIT_LIST_HEAD(&acrtc_b->base.head); + acrtc_a->otg_inst = 1; + acrtc_b->otg_inst = 3; + + list_add_tail(&acrtc_a->base.head, &drm->mode_config.crtc_list); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_crtc_list_del, + acrtc_a), 0); + list_add_tail(&acrtc_b->base.head, &drm->mode_config.crtc_list); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_crtc_list_del, + acrtc_b), 0); + + KUNIT_EXPECT_PTR_EQ(test, amdgpu_dm_get_crtc_by_otg_inst(adev, 3), acrtc_b); +} + +/** + * dm_test_get_crtc_by_otg_inst_returns_null - Test CRTC lookup misses unknown OTG + * @test: The KUnit test context + */ +static void dm_test_get_crtc_by_otg_inst_returns_null(struct kunit *test) +{ + struct amdgpu_crtc *acrtc; + struct amdgpu_device *adev; + struct drm_device *drm; + + adev = dm_kunit_alloc_adev(test); + drm = &adev->ddev; + + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, acrtc); + + INIT_LIST_HEAD(&acrtc->base.head); + acrtc->otg_inst = 2; + + list_add_tail(&acrtc->base.head, &drm->mode_config.crtc_list); + KUNIT_ASSERT_EQ(test, kunit_add_action_or_reset(test, dm_test_crtc_list_del, + acrtc), 0); + + KUNIT_EXPECT_NULL(test, amdgpu_dm_get_crtc_by_otg_inst(adev, 5)); +} + +/** + * dm_test_get_crtc_by_otg_inst_empty_list - Test CRTC lookup on empty CRTC list + * @test: The KUnit test context + */ +static void dm_test_get_crtc_by_otg_inst_empty_list(struct kunit *test) +{ + struct amdgpu_device *adev; + + adev = dm_kunit_alloc_adev(test); + + KUNIT_EXPECT_NULL(test, amdgpu_dm_get_crtc_by_otg_inst(adev, 0)); +} + +static struct kunit_case amdgpu_dm_irq_tests[] = { + /* amdgpu_dm_hpd_to_dal_irq_source */ + KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd1), + KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd2), + KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd3), + KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd4), + KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd5), + KUNIT_CASE(dm_test_hpd_to_dal_irq_source_hpd6), + KUNIT_CASE(dm_test_hpd_to_dal_irq_source_invalid), + KUNIT_CASE(dm_test_hpd_to_dal_irq_source_out_of_range), + /* are_sinks_equal */ + KUNIT_CASE(dm_test_are_sinks_equal_both_null), + KUNIT_CASE(dm_test_are_sinks_equal_first_null), + KUNIT_CASE(dm_test_are_sinks_equal_second_null), + KUNIT_CASE(dm_test_are_sinks_equal_different_signal), + KUNIT_CASE(dm_test_are_sinks_equal_different_edid_length), + KUNIT_CASE(dm_test_are_sinks_equal_different_edid_data), + KUNIT_CASE(dm_test_are_sinks_equal_identical), + KUNIT_CASE(dm_test_are_sinks_equal_zero_length), + KUNIT_CASE(dm_test_are_sinks_equal_full_edid_identical), + KUNIT_CASE(dm_test_are_sinks_equal_full_edid_last_byte_differs), + /* dmub_notification_type_str */ + KUNIT_CASE(dm_test_notification_str_no_data), + KUNIT_CASE(dm_test_notification_str_aux_reply), + KUNIT_CASE(dm_test_notification_str_hpd), + KUNIT_CASE(dm_test_notification_str_hpd_irq), + KUNIT_CASE(dm_test_notification_str_set_config), + KUNIT_CASE(dm_test_notification_str_dpia), + KUNIT_CASE(dm_test_notification_str_hpd_sense), + KUNIT_CASE(dm_test_notification_str_fused_io), + KUNIT_CASE(dm_test_notification_str_unknown), + /* amdgpu_dm_irq_init */ + KUNIT_CASE(dm_test_irq_init_initializes_lists), + /* amdgpu_dm_irq_register_interrupt */ + KUNIT_CASE(dm_test_irq_register_rejects_null_params), + KUNIT_CASE(dm_test_irq_register_rejects_invalid_context), + KUNIT_CASE(dm_test_irq_register_rejects_invalid_source), + KUNIT_CASE(dm_test_irq_register_adds_low_context_handler), + KUNIT_CASE(dm_test_irq_register_adds_high_context_handler), + KUNIT_CASE(dm_test_irq_register_multiple_handlers), + KUNIT_CASE(dm_test_irq_register_separate_contexts), + /* amdgpu_dm_irq_unregister_interrupt */ + KUNIT_CASE(dm_test_irq_unregister_rejects_invalid_source), + KUNIT_CASE(dm_test_irq_unregister_rejects_null_handler), + KUNIT_CASE(dm_test_irq_unregister_handler_not_found), + /* amdgpu_dm_irq_fini */ + KUNIT_CASE(dm_test_irq_fini_removes_registered_handlers), + KUNIT_CASE(dm_test_irq_fini_on_empty_tables), + /* amdgpu_dm_get_crtc_by_otg_inst */ + KUNIT_CASE(dm_test_get_crtc_by_otg_inst_returns_match), + KUNIT_CASE(dm_test_get_crtc_by_otg_inst_returns_null), + KUNIT_CASE(dm_test_get_crtc_by_otg_inst_empty_list), + {} +}; + +static struct kunit_suite amdgpu_dm_irq_test_suite = { + .name = "amdgpu_dm_irq", + .test_cases = amdgpu_dm_irq_tests, +}; + +kunit_test_suite(amdgpu_dm_irq_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_irq"); +MODULE_LICENSE("Dual MIT/GPL"); 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 index f3b3f77aafd5..7dfb3b351d20 100644 --- 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 @@ -9,20 +9,7 @@ #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; -} +#include "amdgpu_dm_kunit_test_helpers.h" /* * Helper: allocate and zero-initialise an ISM instance. @@ -275,7 +262,7 @@ static void dm_test_ism_sso_delay_null_stream(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); stream->timing.v_total = 1125; stream->timing.h_total = 2200; @@ -288,7 +275,7 @@ static void dm_test_ism_sso_delay_zero_frames(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); uint64_t expected_one_frame_ns, expected; /* @@ -311,7 +298,7 @@ static void dm_test_ism_sso_delay_1080p60_3frames(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); uint64_t expected_one_frame_ns; /* @@ -347,7 +334,7 @@ static void dm_test_ism_idle_delay_null_stream(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); stream->timing.v_total = 1125; stream->timing.h_total = 2200; @@ -361,7 +348,7 @@ static void dm_test_ism_idle_delay_zero_filter_frames(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); stream->timing.v_total = 1125; stream->timing.h_total = 2200; @@ -376,7 +363,7 @@ static void dm_test_ism_idle_delay_zero_entry_count(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); stream->timing.v_total = 1125; stream->timing.h_total = 2200; @@ -392,7 +379,7 @@ static void dm_test_ism_idle_delay_zero_delay_frames(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); uint64_t one_frame_ns; /* @@ -426,7 +413,7 @@ static void dm_test_ism_idle_delay_no_short_idles(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); uint64_t one_frame_ns, expected; /* @@ -461,7 +448,7 @@ static void dm_test_ism_idle_delay_enough_short_idles(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); uint64_t one_frame_ns, expected; /* @@ -497,7 +484,7 @@ static void dm_test_ism_idle_delay_wraps_around_buffer(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); uint64_t one_frame_ns; /* @@ -545,7 +532,7 @@ static void dm_test_ism_idle_delay_old_history_cutoff(struct kunit *test) 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); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); uint64_t one_frame_ns; /* @@ -586,7 +573,7 @@ static void dm_test_ism_idle_delay_mixed_durations(struct kunit *test) static void dm_test_ism_idle_delay_entry_count_exceeds_history_size(struct kunit *test) { struct amdgpu_dm_ism *ism = alloc_test_ism(test); - struct dc_stream_state *stream = alloc_test_stream(test); + struct dc_stream_state *stream = dm_kunit_alloc_stream(test, NULL); uint64_t one_frame_ns, expected; /* diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c new file mode 100644 index 000000000000..58615cdbe854 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_helpers.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit test helpers for amdgpu_dm tests. + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <linux/module.h> +#include <drm/drm_kunit_helpers.h> + +#include "dc.h" +#include "core_types.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_kunit_test_helpers.h" + +struct amdgpu_device *dm_kunit_alloc_adev(struct kunit *test) +{ + struct drm_device *drm; + struct device *dev; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, + sizeof(struct amdgpu_device), + offsetof(struct amdgpu_device, ddev), + DRIVER_MODESET | DRIVER_ATOMIC); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + return drm_to_adev(drm); +} +EXPORT_SYMBOL(dm_kunit_alloc_adev); + +struct dc_link *dm_kunit_alloc_link(struct kunit *test) +{ + struct dc_link *link; + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + return link; +} +EXPORT_SYMBOL(dm_kunit_alloc_link); + +struct dc_link *dm_kunit_alloc_link_with_ctx(struct kunit *test) +{ + struct dc_link *link; + struct dc_context *ctx; + struct dc *dc; + + link = dm_kunit_alloc_link(test); + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc); + + link->ctx = ctx; + ctx->dc = dc; + dc->ctx = ctx; + + return link; +} +EXPORT_SYMBOL(dm_kunit_alloc_link_with_ctx); + +struct amdgpu_display_manager *dm_kunit_alloc_dm(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct dc *dc; + struct dc_state *state; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc); + + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state); + + dm->dc = dc; + dc->current_state = state; + + return dm; +} +EXPORT_SYMBOL(dm_kunit_alloc_dm); + +struct dc_stream_state *dm_kunit_alloc_stream(struct kunit *test, + struct dc_link *link) +{ + struct dc_stream_state *stream; + + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, stream); + + stream->link = link; + kref_init(&stream->refcount); + + return stream; +} +EXPORT_SYMBOL(dm_kunit_alloc_stream); + +void dm_kunit_add_stream_to_state(struct kunit *test, struct dc_state *state, + unsigned int index, struct dc_link *link) +{ + struct dc_stream_state *stream; + + KUNIT_ASSERT_LT(test, index, (unsigned int)MAX_PIPES); + + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, stream); + + stream->link = link; + state->streams[index] = stream; + if (state->stream_count <= index) + state->stream_count = index + 1; +} +EXPORT_SYMBOL(dm_kunit_add_stream_to_state); + +struct amdgpu_dm_connector *dm_kunit_alloc_connector(struct kunit *test, + struct amdgpu_device *adev, + struct dc_link *link) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + if (adev) + aconnector->base.dev = &adev->ddev; + aconnector->dc_link = link; + + return aconnector; +} +EXPORT_SYMBOL(dm_kunit_alloc_connector); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit test helpers for amdgpu_dm tests"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h new file mode 100644 index 000000000000..0f1c48fa2128 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_kunit_test_helpers.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0 OR MIT */ +/* + * KUnit test helpers for amdgpu_dm tests. + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#ifndef AMDGPU_DM_KUNIT_TEST_HELPERS_H +#define AMDGPU_DM_KUNIT_TEST_HELPERS_H + +#include <kunit/test.h> + +struct amdgpu_device; +struct amdgpu_display_manager; +struct amdgpu_dm_connector; +struct dc_link; +struct dc_state; +struct dc_stream_state; + +struct amdgpu_device *dm_kunit_alloc_adev(struct kunit *test); +struct dc_link *dm_kunit_alloc_link(struct kunit *test); +struct dc_link *dm_kunit_alloc_link_with_ctx(struct kunit *test); +struct amdgpu_display_manager *dm_kunit_alloc_dm(struct kunit *test); +struct dc_stream_state *dm_kunit_alloc_stream(struct kunit *test, + struct dc_link *link); +void dm_kunit_add_stream_to_state(struct kunit *test, struct dc_state *state, + unsigned int index, struct dc_link *link); +struct amdgpu_dm_connector *dm_kunit_alloc_connector(struct kunit *test, + struct amdgpu_device *adev, + struct dc_link *link); + +#endif /* AMDGPU_DM_KUNIT_TEST_HELPERS_H */ diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c new file mode 100644 index 000000000000..3a663ee0ca2b --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_mst_types_test.c @@ -0,0 +1,1092 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_mst_types.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include <drm/drm_drv.h> +#include <drm/drm_kunit_helpers.h> +#include <drm/display/drm_dp.h> +#include <drm/display/drm_dp_helper.h> +#include <drm/display/drm_dp_mst_helper.h> + +#include "dc.h" +#include "dpcd_defs.h" +#include "dmub_cmd.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_mst_types.h" +#include "amdgpu_dm_kunit_test_helpers.h" +#include "inc/link_service.h" + +/* + * Minimal mock DPCD backing store and AUX transfer callback used to exercise + * the DPCD read paths without real hardware. + */ +static u8 dm_mst_test_dpcd[0x10]; +static u8 dm_mst_test_desc_dpcd[0x10]; +static struct aux_payload dm_mst_test_last_payload; +static int dm_mst_test_aux_transfer_raw_result; +static enum aux_return_code_type dm_mst_test_aux_transfer_raw_operation_result; + +static int dm_mst_test_aux_transfer_raw(struct ddc_service *ddc, + struct aux_payload *payload, + enum aux_return_code_type *operation_result) +{ + size_t i; + + dm_mst_test_last_payload = *payload; + *operation_result = dm_mst_test_aux_transfer_raw_operation_result; + + if (dm_mst_test_aux_transfer_raw_result) + return dm_mst_test_aux_transfer_raw_result; + + if (payload->write) + return 0; + + for (i = 0; i < payload->length; i++) + payload->data[i] = dm_mst_test_dpcd[(payload->address + i) & 0xf]; + + return payload->length; +} + +static void dm_mst_test_setup_dm_aux(struct amdgpu_dm_dp_aux *dm_aux, + struct ddc_service *ddc, + struct dc_link *link, + struct dc *dc, + struct link_service *link_srv, + struct dc_context *ctx, + struct amdgpu_device *adev) +{ + memset(&dm_mst_test_last_payload, 0, sizeof(dm_mst_test_last_payload)); + dm_mst_test_aux_transfer_raw_result = 0; + dm_mst_test_aux_transfer_raw_operation_result = AUX_RET_SUCCESS; + link_srv->aux_transfer_raw = dm_mst_test_aux_transfer_raw; + dc->link_srv = link_srv; + link->dc = dc; + ctx->driver_context = adev; + ddc->link = link; + ddc->ctx = ctx; + dm_aux->ddc_service = ddc; + dm_aux->aux.name = "dm_mst_test_dm_aux"; + dm_aux->aux.transfer = dm_dp_aux_transfer; + drm_dp_aux_init(&dm_aux->aux); + drm_dp_dpcd_set_probe(&dm_aux->aux, false); +} + +static const struct dc_link_status *dm_mst_test_get_status(const struct dc_link *link) +{ + return &link->link_status; +} + +static ssize_t dm_mst_test_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + size_t i; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_READ: + for (i = 0; i < msg->size; i++) + ((u8 *)msg->buffer)[i] = + dm_mst_test_dpcd[(msg->address + i) & 0xf]; + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + return msg->size; + case DP_AUX_NATIVE_WRITE: + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + return msg->size; + default: + return -EINVAL; + } +} + +static ssize_t dm_mst_test_desc_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + size_t i; + + if ((msg->request & ~DP_AUX_I2C_MOT) != DP_AUX_NATIVE_READ) + return -EINVAL; + + for (i = 0; i < msg->size; i++) + ((u8 *)msg->buffer)[i] = dm_mst_test_desc_dpcd[msg->address + i - DP_BRANCH_OUI]; + + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + return msg->size; +} + +/* Tests for needs_dsc_aux_workaround */ + +/** + * dm_mst_test_needs_dsc_aux_workaround_match - Test workaround triggers for matching device + * @test: KUnit test context + * + * Verify that needs_dsc_aux_workaround() returns true when the link has + * the specific branch device ID, DPCD rev 1.4, and sink count >= 2. + */ +static void dm_mst_test_needs_dsc_aux_workaround_match(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24; + link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14; + link->dpcd_caps.sink_count.bits.SINK_COUNT = 2; + + KUNIT_EXPECT_TRUE(test, needs_dsc_aux_workaround(link)); +} + +/** + * dm_mst_test_needs_dsc_aux_workaround_rev12 - Test workaround triggers for DPCD rev 1.2 + * @test: KUnit test context + * + * Verify that needs_dsc_aux_workaround() returns true when the link has + * the specific branch device ID, DPCD rev 1.2, and sink count >= 2. + */ +static void dm_mst_test_needs_dsc_aux_workaround_rev12(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24; + link->dpcd_caps.dpcd_rev.raw = DPCD_REV_12; + link->dpcd_caps.sink_count.bits.SINK_COUNT = 3; + + KUNIT_EXPECT_TRUE(test, needs_dsc_aux_workaround(link)); +} + +/** + * dm_mst_test_needs_dsc_aux_workaround_wrong_dev_id - Test workaround skipped for wrong device + * @test: KUnit test context + * + * Verify that needs_dsc_aux_workaround() returns false when the branch + * device ID does not match DP_BRANCH_DEVICE_ID_90CC24. + */ +static void dm_mst_test_needs_dsc_aux_workaround_wrong_dev_id(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.branch_dev_id = 0x123456; + link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14; + link->dpcd_caps.sink_count.bits.SINK_COUNT = 2; + + KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(link)); +} + +/** + * dm_mst_test_needs_dsc_aux_workaround_wrong_rev - Test workaround skipped for unsupported rev + * @test: KUnit test context + * + * Verify that needs_dsc_aux_workaround() returns false when the DPCD + * revision is neither 1.2 nor 1.4. + */ +static void dm_mst_test_needs_dsc_aux_workaround_wrong_rev(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24; + link->dpcd_caps.dpcd_rev.raw = 0x11; /* DPCD 1.1 */ + link->dpcd_caps.sink_count.bits.SINK_COUNT = 2; + + KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(link)); +} + +/** + * dm_mst_test_needs_dsc_aux_workaround_low_sink_count - Test workaround skipped for single sink + * @test: KUnit test context + * + * Verify that needs_dsc_aux_workaround() returns false when the sink + * count is less than 2, even if device ID and DPCD rev match. + */ +static void dm_mst_test_needs_dsc_aux_workaround_low_sink_count(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24; + link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14; + link->dpcd_caps.sink_count.bits.SINK_COUNT = 1; + + KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(link)); +} + +/** + * dm_mst_test_needs_dsc_aux_workaround_zero_sink_count - Test workaround skipped for zero sinks + * @test: KUnit test context + * + * Verify that needs_dsc_aux_workaround() returns false when the sink + * count is zero, even if device ID and DPCD rev match. + */ +static void dm_mst_test_needs_dsc_aux_workaround_zero_sink_count(struct kunit *test) +{ + struct dc_link *link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, link); + + link->dpcd_caps.branch_dev_id = DP_BRANCH_DEVICE_ID_90CC24; + link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14; + link->dpcd_caps.sink_count.bits.SINK_COUNT = 0; + + KUNIT_EXPECT_FALSE(test, needs_dsc_aux_workaround(link)); +} + +/* Tests for dm_mst_get_pbn_divider */ + +/** + * dm_mst_test_pbn_divider_null_link - Test pbn_divider with NULL link + * @test: KUnit test context + * + * Verify that dm_mst_get_pbn_divider() returns 0 when passed a NULL + * link pointer without crashing. + */ +static void dm_mst_test_pbn_divider_null_link(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_mst_get_pbn_divider(NULL), 0U); +} + +/* Tests for amdgpu_dm_mst_reset_mst_connector_setting */ + +/** + * dm_mst_test_reset_connector_setting - Test MST connector setting reset + * @test: KUnit test context + * + * Verify that amdgpu_dm_mst_reset_mst_connector_setting() clears the cached + * EDID, DSC AUX, passthrough AUX, local bandwidth, and VC PBN state. + */ +static void dm_mst_test_reset_connector_setting(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_dp_mst_port *port; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, port); + + aconnector->drm_edid = (const struct drm_edid *)test; + aconnector->dsc_aux = (struct drm_dp_aux *)test; + aconnector->mst_output_port = port; + aconnector->mst_output_port->passthrough_aux = (struct drm_dp_aux *)test; + aconnector->mst_local_bw = 12345; + aconnector->vc_full_pbn = 678; + + amdgpu_dm_mst_reset_mst_connector_setting(aconnector); + + KUNIT_EXPECT_TRUE(test, aconnector->drm_edid == NULL); + KUNIT_EXPECT_TRUE(test, aconnector->dsc_aux == NULL); + KUNIT_EXPECT_TRUE(test, aconnector->mst_output_port->passthrough_aux == NULL); + KUNIT_EXPECT_EQ(test, aconnector->mst_local_bw, 0U); + KUNIT_EXPECT_EQ(test, aconnector->vc_full_pbn, 0U); +} + +/* Tests for retrieve_downstream_port_device */ + +/** + * dm_mst_test_retrieve_downstream_no_aux - Test retrieval bails out without AUX + * @test: KUnit test context + * + * Verify that retrieve_downstream_port_device() returns false when the + * connector has no DSC AUX channel and therefore cannot read DPCD. + */ +static void dm_mst_test_retrieve_downstream_no_aux(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->dsc_aux = NULL; + + KUNIT_EXPECT_FALSE(test, retrieve_downstream_port_device(aconnector)); +} + +/** + * dm_mst_test_retrieve_downstream_present - Test retrieval parses DPCD 0x05 + * @test: KUnit test context + * + * Verify that retrieve_downstream_port_device() reads DP_DOWNSTREAMPORT_PRESENT + * over a mock AUX channel and caches the parsed downstream port fields. + */ +static void dm_mst_test_retrieve_downstream_present(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_dp_aux *aux; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, aux); + + memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd)); + /* PORT_PRESENT = 1, PORT_TYPE = 2 (0b101) */ + dm_mst_test_dpcd[DP_DOWNSTREAMPORT_PRESENT] = 0x05; + + aux->name = "dm_mst_test_aux"; + aux->transfer = dm_mst_test_aux_transfer; + drm_dp_aux_init(aux); + drm_dp_dpcd_set_probe(aux, false); + aconnector->dsc_aux = aux; + + KUNIT_EXPECT_TRUE(test, retrieve_downstream_port_device(aconnector)); + KUNIT_EXPECT_EQ(test, + (int)aconnector->mst_downstream_port_present.fields.PORT_PRESENT, 1); + KUNIT_EXPECT_EQ(test, + (int)aconnector->mst_downstream_port_present.fields.PORT_TYPE, 2); +} + +/* Tests for retrieve_branch_specific_data */ + +/** + * dm_mst_test_retrieve_branch_no_parent - Test branch lookup needs a parent port + * @test: KUnit test context + * + * Verify that retrieve_branch_specific_data() returns false when the MST + * output port has no parent branch device to query. + */ +static void dm_mst_test_retrieve_branch_no_parent(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_dp_mst_port *port; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, port); + + port->parent = NULL; + aconnector->mst_output_port = port; + + KUNIT_EXPECT_FALSE(test, retrieve_branch_specific_data(aconnector)); +} + +/** + * dm_mst_test_retrieve_branch_reads_oui - Test branch OUI parsing + * @test: KUnit test context + * + * Verify that retrieve_branch_specific_data() reads the immediate upstream + * branch descriptor and caches its IEEE OUI value on the connector. + */ +static void dm_mst_test_retrieve_branch_reads_oui(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct drm_dp_mst_topology_mgr *mgr; + struct drm_dp_mst_branch *branch; + struct drm_dp_mst_port *port; + struct drm_dp_aux *aux; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + mgr = kunit_kzalloc(test, sizeof(*mgr), GFP_KERNEL); + branch = kunit_kzalloc(test, sizeof(*branch), GFP_KERNEL); + port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL); + aux = kunit_kzalloc(test, sizeof(*aux), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, mgr); + KUNIT_ASSERT_NOT_NULL(test, branch); + KUNIT_ASSERT_NOT_NULL(test, port); + KUNIT_ASSERT_NOT_NULL(test, aux); + + memset(dm_mst_test_desc_dpcd, 0, sizeof(dm_mst_test_desc_dpcd)); + dm_mst_test_desc_dpcd[0] = 0x12; + dm_mst_test_desc_dpcd[1] = 0x34; + dm_mst_test_desc_dpcd[2] = 0x56; + + aux->name = "dm_mst_test_desc_aux"; + aux->transfer = dm_mst_test_desc_aux_transfer; + drm_dp_aux_init(aux); + drm_dp_dpcd_set_probe(aux, false); + mgr->aux = aux; + port->parent = branch; + port->mgr = mgr; + port->aux.drm_dev = NULL; + aconnector->mst_output_port = port; + + KUNIT_EXPECT_TRUE(test, retrieve_branch_specific_data(aconnector)); + KUNIT_EXPECT_EQ(test, aconnector->branch_ieee_oui, 0x123456U); +} + +/** + * dm_mst_test_aux_result_success - AUX_RET_SUCCESS preserves the input result. + * @test: KUnit test context. + * + * On success the original (negative) transfer result must be returned unchanged. + */ +static void dm_mst_test_aux_result_success(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-5, AUX_RET_SUCCESS), (ssize_t)-5); + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(3, AUX_RET_SUCCESS), (ssize_t)3); +} + +/** + * dm_mst_test_aux_result_eio - HPD/unknown/protocol errors map to -EIO. + * @test: KUnit test context. + * + * AUX_RET_ERROR_HPD_DISCON, AUX_RET_ERROR_UNKNOWN, + * AUX_RET_ERROR_INVALID_OPERATION and AUX_RET_ERROR_PROTOCOL_ERROR all map to -EIO. + */ +static void dm_mst_test_aux_result_eio(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_HPD_DISCON), + (ssize_t)-EIO); + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_UNKNOWN), + (ssize_t)-EIO); + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_INVALID_OPERATION), + (ssize_t)-EIO); + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_PROTOCOL_ERROR), + (ssize_t)-EIO); +} + +/** + * dm_mst_test_aux_result_ebusy - invalid reply / engine acquire map to -EBUSY. + * @test: KUnit test context. + * + * AUX_RET_ERROR_INVALID_REPLY and AUX_RET_ERROR_ENGINE_ACQUIRE map to -EBUSY. + */ +static void dm_mst_test_aux_result_ebusy(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_INVALID_REPLY), + (ssize_t)-EBUSY); + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_ENGINE_ACQUIRE), + (ssize_t)-EBUSY); +} + +/** + * dm_mst_test_aux_result_timeout - AUX_RET_ERROR_TIMEOUT maps to -ETIMEDOUT. + * @test: KUnit test context. + */ +static void dm_mst_test_aux_result_timeout(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_dp_aux_transfer_result(-1, AUX_RET_ERROR_TIMEOUT), + (ssize_t)-ETIMEDOUT); +} + +/** + * dm_mst_test_aux_transfer_native_read - native AUX read through DM callback. + * @test: KUnit test context. + * + * The DM AUX transfer callback should build a read payload, call the DC link + * service, and return the number of bytes provided by the fake backend. + */ +static void dm_mst_test_aux_transfer_native_read(struct kunit *test) +{ + struct amdgpu_dm_dp_aux *dm_aux; + struct amdgpu_device *adev; + struct ddc_service *ddc; + struct dc_link *link; + struct dc *dc; + struct link_service *link_srv; + struct dc_context *ctx; + u8 buffer[3] = { 0 }; + ssize_t ret; + + dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL); + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_aux); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ddc); + KUNIT_ASSERT_NOT_NULL(test, link); + KUNIT_ASSERT_NOT_NULL(test, dc); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd)); + dm_mst_test_dpcd[4] = 0xaa; + dm_mst_test_dpcd[5] = 0xbb; + dm_mst_test_dpcd[6] = 0xcc; + dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev); + + ret = drm_dp_dpcd_read(&dm_aux->aux, 4, buffer, sizeof(buffer)); + + KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer)); + KUNIT_EXPECT_EQ(test, buffer[0], (u8)0xaa); + KUNIT_EXPECT_EQ(test, buffer[1], (u8)0xbb); + KUNIT_EXPECT_EQ(test, buffer[2], (u8)0xcc); + KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.write); + KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.i2c_over_aux); + KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 4U); +} + +/** + * dm_mst_test_aux_transfer_native_write - native AUX write through DM callback. + * @test: KUnit test context. + * + * A successful write with an ACK reply should report the requested write size + * and pass a write payload into the fake DC link service. + */ +static void dm_mst_test_aux_transfer_native_write(struct kunit *test) +{ + struct amdgpu_dm_dp_aux *dm_aux; + struct amdgpu_device *adev; + struct ddc_service *ddc; + struct dc_link *link; + struct dc *dc; + struct link_service *link_srv; + struct dc_context *ctx; + u8 buffer[2] = { 0x11, 0x22 }; + ssize_t ret; + + dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL); + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_aux); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ddc); + KUNIT_ASSERT_NOT_NULL(test, link); + KUNIT_ASSERT_NOT_NULL(test, dc); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev); + + ret = drm_dp_dpcd_write(&dm_aux->aux, 7, buffer, sizeof(buffer)); + + KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer)); + KUNIT_EXPECT_TRUE(test, dm_mst_test_last_payload.write); + KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.i2c_over_aux); + KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 7U); + KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.length, + (u32)sizeof(buffer)); +} + +/** + * dm_mst_test_aux_transfer_partial_write - partial write reports byte count. + * @test: KUnit test context. + * + * A positive write result from the DC link service should be interpreted as a + * partial write and replaced with the first payload byte. + */ +static void dm_mst_test_aux_transfer_partial_write(struct kunit *test) +{ + struct amdgpu_dm_dp_aux *dm_aux; + struct amdgpu_device *adev; + struct ddc_service *ddc; + struct dc_link *link; + struct dc *dc; + struct link_service *link_srv; + struct dc_context *ctx; + u8 buffer[2] = { 1, 0xaa }; + struct drm_dp_aux_msg msg = { + .address = 7, + .request = DP_AUX_NATIVE_WRITE, + .buffer = buffer, + .size = sizeof(buffer), + }; + ssize_t ret; + + dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL); + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_aux); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ddc); + KUNIT_ASSERT_NOT_NULL(test, link); + KUNIT_ASSERT_NOT_NULL(test, dc); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev); + dm_mst_test_aux_transfer_raw_result = 1; + + ret = dm_dp_aux_transfer(&dm_aux->aux, &msg); + + KUNIT_EXPECT_EQ(test, ret, (ssize_t)buffer[0]); + KUNIT_EXPECT_TRUE(test, dm_mst_test_last_payload.write); + KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 7U); +} + +/** + * dm_mst_test_aux_transfer_error_result - transfer errors are remapped. + * @test: KUnit test context. + * + * A negative DC link service result should be converted through + * dm_dp_aux_transfer_result() using the returned AUX operation result. + */ +static void dm_mst_test_aux_transfer_error_result(struct kunit *test) +{ + struct amdgpu_dm_dp_aux *dm_aux; + struct amdgpu_device *adev; + struct ddc_service *ddc; + struct dc_link *link; + struct dc *dc; + struct link_service *link_srv; + struct dc_context *ctx; + u8 buffer[2] = { 0 }; + ssize_t ret; + + dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL); + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_aux); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ddc); + KUNIT_ASSERT_NOT_NULL(test, link); + KUNIT_ASSERT_NOT_NULL(test, dc); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev); + dm_mst_test_aux_transfer_raw_result = -EIO; + dm_mst_test_aux_transfer_raw_operation_result = AUX_RET_ERROR_TIMEOUT; + + ret = drm_dp_dpcd_read(&dm_aux->aux, 4, buffer, sizeof(buffer)); + + KUNIT_EXPECT_EQ(test, ret, (ssize_t)-ETIMEDOUT); + KUNIT_EXPECT_FALSE(test, dm_mst_test_last_payload.write); + KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, 4U); +} + +/** + * dm_mst_test_aux_transfer_hpd_discon_quirk - HPD disconnect quirk succeeds. + * @test: KUnit test context. + * + * AUX_RET_ERROR_HPD_DISCON on the sideband down request address should be + * treated as a successful transfer when the platform quirk is enabled. + */ +static void dm_mst_test_aux_transfer_hpd_discon_quirk(struct kunit *test) +{ + struct amdgpu_dm_dp_aux *dm_aux; + struct amdgpu_device *adev; + struct ddc_service *ddc; + struct dc_link *link; + struct dc *dc; + struct link_service *link_srv; + struct dc_context *ctx; + u8 buffer[2] = { 2, 0 }; + ssize_t ret; + + dm_aux = kunit_kzalloc(test, sizeof(*dm_aux), GFP_KERNEL); + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + ddc = kunit_kzalloc(test, sizeof(*ddc), GFP_KERNEL); + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm_aux); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ddc); + KUNIT_ASSERT_NOT_NULL(test, link); + KUNIT_ASSERT_NOT_NULL(test, dc); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + dm_mst_test_setup_dm_aux(dm_aux, ddc, link, dc, link_srv, ctx, adev); + adev->dm.aux_hpd_discon_quirk = true; + dm_mst_test_aux_transfer_raw_result = -EIO; + dm_mst_test_aux_transfer_raw_operation_result = AUX_RET_ERROR_HPD_DISCON; + + ret = drm_dp_dpcd_write(&dm_aux->aux, DP_SIDEBAND_MSG_DOWN_REQ_BASE, + buffer, sizeof(buffer)); + + KUNIT_EXPECT_EQ(test, ret, (ssize_t)sizeof(buffer)); + KUNIT_EXPECT_TRUE(test, dm_mst_test_last_payload.write); + KUNIT_EXPECT_EQ(test, dm_mst_test_last_payload.address, + DP_SIDEBAND_MSG_DOWN_REQ_BASE); +} + +/** + * dm_mst_test_fill_payload_flags_native_write - native write request decode. + * @test: KUnit test context. + * + * DP_AUX_NATIVE_WRITE clears i2c_over_aux and sets write; no I2C bits set. + */ +static void dm_mst_test_fill_payload_flags_native_write(struct kunit *test) +{ + struct aux_payload payload = { 0 }; + + dm_dp_aux_fill_payload_flags(DP_AUX_NATIVE_WRITE, &payload); + + KUNIT_EXPECT_FALSE(test, payload.i2c_over_aux); + KUNIT_EXPECT_TRUE(test, payload.write); + KUNIT_EXPECT_FALSE(test, payload.mot); + KUNIT_EXPECT_FALSE(test, payload.write_status_update); +} + +/** + * dm_mst_test_fill_payload_flags_native_read - native read request decode. + * @test: KUnit test context. + * + * DP_AUX_NATIVE_READ keeps i2c_over_aux clear; the I2C_READ bit clears write. + */ +static void dm_mst_test_fill_payload_flags_native_read(struct kunit *test) +{ + struct aux_payload payload = { 0 }; + + dm_dp_aux_fill_payload_flags(DP_AUX_NATIVE_READ, &payload); + + KUNIT_EXPECT_FALSE(test, payload.i2c_over_aux); + KUNIT_EXPECT_FALSE(test, payload.write); + KUNIT_EXPECT_FALSE(test, payload.mot); +} + +/** + * dm_mst_test_fill_payload_flags_i2c_read_mot - I2C read with MOT request decode. + * @test: KUnit test context. + * + * DP_AUX_I2C_READ sets i2c_over_aux and clears write; DP_AUX_I2C_MOT sets mot. + */ +static void dm_mst_test_fill_payload_flags_i2c_read_mot(struct kunit *test) +{ + struct aux_payload payload = { 0 }; + + dm_dp_aux_fill_payload_flags(DP_AUX_I2C_READ | DP_AUX_I2C_MOT, &payload); + + KUNIT_EXPECT_TRUE(test, payload.i2c_over_aux); + KUNIT_EXPECT_FALSE(test, payload.write); + KUNIT_EXPECT_TRUE(test, payload.mot); +} + +/** + * dm_mst_test_fill_payload_flags_write_status - write status update decode. + * @test: KUnit test context. + * + * DP_AUX_I2C_WRITE_STATUS_UPDATE sets write_status_update. + */ +static void dm_mst_test_fill_payload_flags_write_status(struct kunit *test) +{ + struct aux_payload payload = { 0 }; + + dm_dp_aux_fill_payload_flags(DP_AUX_I2C_WRITE | DP_AUX_I2C_WRITE_STATUS_UPDATE, + &payload); + + KUNIT_EXPECT_TRUE(test, payload.i2c_over_aux); + KUNIT_EXPECT_TRUE(test, payload.write_status_update); +} + +/** + * dm_mst_test_msg_ready_mask - ESI mask selection per message-ready type. + * @test: KUnit test context. + * + * DOWN_REP and UP_REQ each select their single bit; other types select both. + */ +static void dm_mst_test_msg_ready_mask(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(DOWN_REP_MSG_RDY_EVENT), + (u8)DP_DOWN_REP_MSG_RDY); + KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(UP_REQ_MSG_RDY_EVENT), + (u8)DP_UP_REQ_MSG_RDY); + KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(DOWN_OR_UP_MSG_RDY_EVENT), + (u8)(DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY)); + KUNIT_EXPECT_EQ(test, dm_mst_msg_ready_mask(NONE_MSG_RDY_EVENT), + (u8)(DP_DOWN_REP_MSG_RDY | DP_UP_REQ_MSG_RDY)); +} + +/** + * dm_mst_test_select_esi_dpcd_legacy - pre-1.2 DPCD ESI address/length. + * @test: KUnit test context. + * + * For DPCD rev < 0x12 the legacy DP_SINK_COUNT address/length pair is selected. + */ +static void dm_mst_test_select_esi_dpcd_legacy(struct kunit *test) +{ + int dpcd_addr = -1; + u8 dpcd_bytes_to_read = 0; + + dm_mst_select_esi_dpcd(0x11, &dpcd_addr, &dpcd_bytes_to_read); + + KUNIT_EXPECT_EQ(test, dpcd_addr, DP_SINK_COUNT); + KUNIT_EXPECT_EQ(test, (int)dpcd_bytes_to_read, + (int)(DP_LANE0_1_STATUS - DP_SINK_COUNT)); +} + +/** + * dm_mst_test_select_esi_dpcd_esi - 1.2+ DPCD ESI address/length. + * @test: KUnit test context. + * + * For DPCD rev >= 0x12 the ESI DP_SINK_COUNT_ESI address/length pair is selected. + */ +static void dm_mst_test_select_esi_dpcd_esi(struct kunit *test) +{ + int dpcd_addr = -1; + u8 dpcd_bytes_to_read = 0; + + dm_mst_select_esi_dpcd(0x14, &dpcd_addr, &dpcd_bytes_to_read); + + KUNIT_EXPECT_EQ(test, dpcd_addr, DP_SINK_COUNT_ESI); + KUNIT_EXPECT_EQ(test, (int)dpcd_bytes_to_read, + (int)(DP_PSR_ERROR_STATUS - DP_SINK_COUNT_ESI)); +} + +/** + * dm_mst_test_sideband_msg_ready_no_ready_bits - Test idle sideband event + * @test: KUnit test context + * + * Verify that dm_handle_mst_sideband_msg_ready_event() returns cleanly when + * the ESI read succeeds but no DOWN_REP/UP_REQ ready bits are set. + */ +static void dm_mst_test_sideband_msg_ready_no_ready_bits(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + struct link_service *link_srv; + struct dc_link *link; + struct dc *dc; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + link_srv = kunit_kzalloc(test, sizeof(*link_srv), GFP_KERNEL); + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, link_srv); + KUNIT_ASSERT_NOT_NULL(test, link); + KUNIT_ASSERT_NOT_NULL(test, dc); + + mutex_init(&aconnector->handle_mst_msg_ready); + link_srv->get_status = dm_mst_test_get_status; + dc->link_srv = link_srv; + link->dc = dc; + link->dpcd_caps.dpcd_rev.raw = DPCD_REV_14; + link->link_status.dpcd_caps = &link->dpcd_caps; + aconnector->dc_link = link; + aconnector->dm_dp_aux.aux.name = "dm_mst_test_sideband_aux"; + aconnector->dm_dp_aux.aux.transfer = dm_mst_test_aux_transfer; + drm_dp_aux_init(&aconnector->dm_dp_aux.aux); + drm_dp_dpcd_set_probe(&aconnector->dm_dp_aux.aux, false); + memset(dm_mst_test_dpcd, 0, sizeof(dm_mst_test_dpcd)); + + dm_handle_mst_sideband_msg_ready_event(&aconnector->mst_mgr, + DOWN_REP_MSG_RDY_EVENT); + + KUNIT_EXPECT_EQ(test, dm_mst_test_dpcd[1], (u8)0); +} + +/** + * dm_mst_test_atomic_best_encoder - Test MST encoder selection + * @test: KUnit test context + * + * Verify that dm_mst_atomic_best_encoder() selects the MST encoder indexed by + * the CRTC ID in the connector's new atomic state. This uses structural DRM + * mocks only; registering connector/CRTC objects is unnecessary for this helper. + */ +static void dm_mst_test_atomic_best_encoder(struct kunit *test) +{ + struct drm_connector_state connector_state = { 0 }; + struct drm_atomic_commit state = { 0 }; + struct amdgpu_dm_connector *aconnector; + struct amdgpu_device *adev; + struct amdgpu_crtc *acrtc; + unsigned int connector_index = 3; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + acrtc = kunit_kzalloc(test, sizeof(*acrtc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, acrtc); + + aconnector->base.dev = &adev->ddev; + aconnector->base.index = connector_index; + acrtc->crtc_id = 2; + connector_state.connector = &aconnector->base; + connector_state.crtc = &acrtc->base; + state.num_connector = connector_index + 1; + state.connectors = kunit_kzalloc(test, + sizeof(*state.connectors) * state.num_connector, + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state.connectors); + state.connectors[connector_index].ptr = &aconnector->base; + state.connectors[connector_index].new_state = &connector_state; + + KUNIT_EXPECT_PTR_EQ(test, dm_mst_atomic_best_encoder(&aconnector->base, &state), + &adev->dm.mst_encoders[2].base); +} + +/** + * dm_mst_test_create_fake_mst_encoders - Test fake MST encoder setup + * @test: KUnit test context + * + * Verify that dm_dp_create_fake_mst_encoders() initializes the requested MST + * encoders as DPMST encoders with the CRTC mask derived from the device state. + */ +static void dm_mst_test_create_fake_mst_encoders(struct kunit *test) +{ + struct amdgpu_device *adev; + struct drm_device *drm; + int i; + + adev = dm_kunit_alloc_adev(test); + drm = &adev->ddev; + adev->dm.display_indexes_num = 3; + adev->mode_info.num_crtc = 3; + + dm_dp_create_fake_mst_encoders(adev); + + for (i = 0; i < adev->dm.display_indexes_num; i++) { + struct drm_encoder *encoder = &adev->dm.mst_encoders[i].base; + + KUNIT_EXPECT_PTR_EQ(test, encoder->dev, drm); + KUNIT_EXPECT_EQ(test, encoder->encoder_type, DRM_MODE_ENCODER_DPMST); + KUNIT_EXPECT_EQ(test, encoder->possible_crtcs, 0x7U); + KUNIT_EXPECT_TRUE(test, encoder->helper_private != NULL); + } +} + +/** + * dm_mst_test_atomic_check_no_old_crtc - Test atomic check no-op path + * @test: KUnit test context + * + * Verify that dm_dp_mst_atomic_check() returns success when the MST port's old + * connector state has no CRTC, before MST topology state is required. + */ +static void dm_mst_test_atomic_check_no_old_crtc(struct kunit *test) +{ + struct drm_connector_state *old_conn_state; + struct drm_connector_state *new_conn_state; + struct drm_atomic_commit *state; + struct amdgpu_dm_connector *aconnector; + struct amdgpu_dm_connector *root; + struct drm_dp_mst_port *port; + unsigned int connector_index = 2; + + old_conn_state = kunit_kzalloc(test, sizeof(*old_conn_state), GFP_KERNEL); + new_conn_state = kunit_kzalloc(test, sizeof(*new_conn_state), GFP_KERNEL); + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + root = kunit_kzalloc(test, sizeof(*root), GFP_KERNEL); + port = kunit_kzalloc(test, sizeof(*port), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, old_conn_state); + KUNIT_ASSERT_NOT_NULL(test, new_conn_state); + KUNIT_ASSERT_NOT_NULL(test, state); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + KUNIT_ASSERT_NOT_NULL(test, root); + KUNIT_ASSERT_NOT_NULL(test, port); + + aconnector->base.index = connector_index; + aconnector->mst_root = root; + aconnector->mst_output_port = port; + port->connector = &aconnector->base; + old_conn_state->connector = &aconnector->base; + new_conn_state->connector = &aconnector->base; + state->num_connector = connector_index + 1; + state->connectors = kunit_kzalloc(test, + sizeof(*state->connectors) * state->num_connector, + GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state->connectors); + state->connectors[connector_index].ptr = &aconnector->base; + state->connectors[connector_index].old_state = old_conn_state; + state->connectors[connector_index].new_state = new_conn_state; + + KUNIT_EXPECT_EQ(test, dm_dp_mst_atomic_check(&aconnector->base, state), 0); +} + +/** + * dm_mst_test_detect_unregistered - Test detect skips unregistered connector + * @test: KUnit test context + * + * Verify that dm_dp_mst_detect() returns disconnected for an unregistered + * connector before calling into the MST topology helper. + */ +static void dm_mst_test_detect_unregistered(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + aconnector->base.registration_state = DRM_CONNECTOR_UNREGISTERED; + + KUNIT_EXPECT_EQ(test, + dm_dp_mst_detect(&aconnector->base, NULL, false), + (int)connector_status_disconnected); +} + +/** + * dm_mst_test_fp_guarded_public_stubs - Test FP-off public fallbacks + * @test: KUnit test context + * + * When CONFIG_DRM_AMD_DC_FP is disabled, the public DSC validation helper + * has no FP body and must return DC_OK without touching its arguments. + */ +static void dm_mst_test_fp_guarded_public_stubs(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, dm_dp_mst_is_port_support_mode(NULL, NULL), + (enum dc_status)DC_OK); +} + +static struct kunit_case dm_mst_types_test_cases[] = { + /* needs_dsc_aux_workaround tests */ + KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_match), + KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_rev12), + KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_wrong_dev_id), + KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_wrong_rev), + KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_low_sink_count), + KUNIT_CASE(dm_mst_test_needs_dsc_aux_workaround_zero_sink_count), + /* dm_mst_get_pbn_divider tests */ + KUNIT_CASE(dm_mst_test_pbn_divider_null_link), + /* amdgpu_dm_mst_reset_mst_connector_setting tests */ + KUNIT_CASE(dm_mst_test_reset_connector_setting), + /* retrieve_downstream_port_device tests */ + KUNIT_CASE(dm_mst_test_retrieve_downstream_no_aux), + KUNIT_CASE(dm_mst_test_retrieve_downstream_present), + /* retrieve_branch_specific_data tests */ + KUNIT_CASE(dm_mst_test_retrieve_branch_no_parent), + KUNIT_CASE(dm_mst_test_retrieve_branch_reads_oui), + /* dm_dp_aux_transfer_result tests */ + KUNIT_CASE(dm_mst_test_aux_result_success), + KUNIT_CASE(dm_mst_test_aux_result_eio), + KUNIT_CASE(dm_mst_test_aux_result_ebusy), + KUNIT_CASE(dm_mst_test_aux_result_timeout), + KUNIT_CASE(dm_mst_test_aux_transfer_native_read), + KUNIT_CASE(dm_mst_test_aux_transfer_native_write), + KUNIT_CASE(dm_mst_test_aux_transfer_partial_write), + KUNIT_CASE(dm_mst_test_aux_transfer_error_result), + KUNIT_CASE(dm_mst_test_aux_transfer_hpd_discon_quirk), + /* dm_dp_aux_fill_payload_flags tests */ + KUNIT_CASE(dm_mst_test_fill_payload_flags_native_write), + KUNIT_CASE(dm_mst_test_fill_payload_flags_native_read), + KUNIT_CASE(dm_mst_test_fill_payload_flags_i2c_read_mot), + KUNIT_CASE(dm_mst_test_fill_payload_flags_write_status), + /* dm_mst_msg_ready_mask tests */ + KUNIT_CASE(dm_mst_test_msg_ready_mask), + /* dm_mst_select_esi_dpcd tests */ + KUNIT_CASE(dm_mst_test_select_esi_dpcd_legacy), + KUNIT_CASE(dm_mst_test_select_esi_dpcd_esi), + /* dm_handle_mst_sideband_msg_ready_event tests */ + KUNIT_CASE(dm_mst_test_sideband_msg_ready_no_ready_bits), + /* dm_mst_atomic_best_encoder tests */ + KUNIT_CASE(dm_mst_test_atomic_best_encoder), + /* dm_dp_create_fake_mst_encoders tests */ + KUNIT_CASE(dm_mst_test_create_fake_mst_encoders), + /* dm_dp_mst_atomic_check tests */ + KUNIT_CASE(dm_mst_test_atomic_check_no_old_crtc), + /* dm_dp_mst_detect tests */ + KUNIT_CASE(dm_mst_test_detect_unregistered), + /* CONFIG_DRM_AMD_DC_FP disabled public paths */ + KUNIT_CASE(dm_mst_test_fp_guarded_public_stubs), + {} +}; + +static struct kunit_suite dm_mst_types_test_suite = { + .name = "amdgpu_dm_mst_types", + .test_cases = dm_mst_types_test_cases, +}; + +kunit_test_suite(dm_mst_types_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_mst_types"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c new file mode 100644 index 000000000000..46c9af432e37 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_plane_test.c @@ -0,0 +1,1228 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_plane.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + + #include <kunit/test.h> + #include <drm/drm_blend.h> + #include "link_enc_cfg.h" + #include "amdgpu_dm_plane.h" + #include <drm/amdgpu_drm.h> + #include <drm/drm_plane.h> + + +struct dm_test_dcc_cap_ctx { + bool callback_ret; + bool capable; + bool output_independent_64b_blks; + bool called; + struct dc_dcc_surface_param captured_input; +}; + +static struct dm_test_dcc_cap_ctx *dm_test_dcc_ctx; + +static bool dm_test_get_dcc_compression_cap(const struct dc *dc, + const struct dc_dcc_surface_param *input, + struct dc_surface_dcc_cap *output) +{ + if (!dm_test_dcc_ctx) + return false; + + dm_test_dcc_ctx->called = true; + dm_test_dcc_ctx->captured_input = *input; + output->capable = dm_test_dcc_ctx->capable; + output->grph.rgb.independent_64b_blks = dm_test_dcc_ctx->output_independent_64b_blks; + + return dm_test_dcc_ctx->callback_ret; +} + +static void dm_test_init_validate_dcc_inputs(struct amdgpu_device **adev, + struct dc **dc, + struct dc_tiling_info *tiling_info, + struct dc_plane_dcc_param *dcc, + struct dc_plane_address *address, + struct plane_size *plane_size, + struct kunit *test) +{ + *adev = kunit_kzalloc(test, sizeof(**adev), GFP_KERNEL); + *dc = kunit_kzalloc(test, sizeof(**dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, *adev); + KUNIT_ASSERT_NOT_NULL(test, *dc); + + (*adev)->dm.dc = *dc; + (*adev)->family = AMDGPU_FAMILY_NV; + + tiling_info->gfx9.swizzle = 9; + dcc->enable = 1; + dcc->independent_64b_blks = 1; + plane_size->surface_size.width = 1920; + plane_size->surface_size.height = 1080; + + (void)address; +} + + +/** + * dm_test_plane_is_video_format_known_video() - Verify known video formats. + * @test: KUnit test context. + * + * Verify if NV12, NV21, and P010 are treated as video formats. + */ +static void dm_test_plane_is_video_format_known_video(struct kunit *test) +{ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_plane_is_video_format(DRM_FORMAT_NV12)); + KUNIT_EXPECT_TRUE(test, amdgpu_dm_plane_is_video_format(DRM_FORMAT_NV21)); + KUNIT_EXPECT_TRUE(test, amdgpu_dm_plane_is_video_format(DRM_FORMAT_P010)); +} + +/** + * dm_test_fill_blending_defaults() - Verify default blending output values. + * @test: KUnit test context. + * + * Verify if default blending output values are used for opaque alpha and no + * per-pixel blending. + */ +static void dm_test_fill_blending_defaults(struct kunit *test) +{ + struct drm_plane_state state = { 0 }; + bool per_pixel_alpha; + bool pre_multiplied_alpha; + bool global_alpha; + int global_alpha_value; + + state.pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE; + state.alpha = 0xffff; + + amdgpu_dm_plane_fill_blending_from_plane_state(&state, + &per_pixel_alpha, + &pre_multiplied_alpha, + &global_alpha, + &global_alpha_value); + + KUNIT_EXPECT_FALSE(test, per_pixel_alpha); + KUNIT_EXPECT_TRUE(test, pre_multiplied_alpha); + KUNIT_EXPECT_FALSE(test, global_alpha); + KUNIT_EXPECT_EQ(test, global_alpha_value, 0xff); +} + +/** + * dm_test_fill_blending_premulti_alpha_format() - Verify premultiplied alpha path. + * @test: KUnit test context. + * + * Verify if premultiplied mode enables per-pixel alpha for ARGB8888. + */ +static void dm_test_fill_blending_premulti_alpha_format(struct kunit *test) +{ + struct drm_plane_state state = { 0 }; + struct drm_framebuffer fb = { 0 }; + bool per_pixel_alpha; + bool pre_multiplied_alpha; + bool global_alpha; + int global_alpha_value; + + fb.format = drm_format_info(DRM_FORMAT_ARGB8888); + KUNIT_ASSERT_NOT_NULL(test, fb.format); + + state.fb = &fb; + state.pixel_blend_mode = DRM_MODE_BLEND_PREMULTI; + state.alpha = 0xffff; + + amdgpu_dm_plane_fill_blending_from_plane_state(&state, + &per_pixel_alpha, + &pre_multiplied_alpha, + &global_alpha, + &global_alpha_value); + + KUNIT_EXPECT_TRUE(test, per_pixel_alpha); + KUNIT_EXPECT_TRUE(test, pre_multiplied_alpha); + KUNIT_EXPECT_FALSE(test, global_alpha); + KUNIT_EXPECT_EQ(test, global_alpha_value, 0xff); +} + +/** + * dm_test_fill_blending_coverage_alpha_format() - Verify coverage mode behavior. + * @test: KUnit test context. + * + * Verify if coverage mode sets per-pixel alpha and disables + * pre_multiplied_alpha for ARGB8888. + */ +static void dm_test_fill_blending_coverage_alpha_format(struct kunit *test) +{ + struct drm_plane_state state = { 0 }; + struct drm_framebuffer fb = { 0 }; + bool per_pixel_alpha; + bool pre_multiplied_alpha; + bool global_alpha; + int global_alpha_value; + + fb.format = drm_format_info(DRM_FORMAT_ARGB8888); + KUNIT_ASSERT_NOT_NULL(test, fb.format); + + state.fb = &fb; + state.pixel_blend_mode = DRM_MODE_BLEND_COVERAGE; + state.alpha = 0xffff; + + amdgpu_dm_plane_fill_blending_from_plane_state(&state, + &per_pixel_alpha, + &pre_multiplied_alpha, + &global_alpha, + &global_alpha_value); + + KUNIT_EXPECT_TRUE(test, per_pixel_alpha); + KUNIT_EXPECT_FALSE(test, pre_multiplied_alpha); + KUNIT_EXPECT_FALSE(test, global_alpha); + KUNIT_EXPECT_EQ(test, global_alpha_value, 0xff); +} + +/** + * dm_test_fill_blending_global_alpha() - Verify global alpha conversion to 8 bits. + * @test: KUnit test context. + * + * Verify if global alpha is enabled and converted from 16-bit to 8-bit. + */ +static void dm_test_fill_blending_global_alpha(struct kunit *test) +{ + struct amdgpu_device *adev; + struct drm_plane *plane; + struct drm_plane_state *state; + bool per_pixel_alpha; + bool pre_multiplied_alpha; + bool global_alpha; + int global_alpha_value; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL); + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, plane); + KUNIT_ASSERT_NOT_NULL(test, state); + + plane->dev = &adev->ddev; + state->plane = plane; + state->pixel_blend_mode = DRM_MODE_BLEND_PIXEL_NONE; + state->alpha = 0x8000; + + amdgpu_dm_plane_fill_blending_from_plane_state(state, + &per_pixel_alpha, + &pre_multiplied_alpha, + &global_alpha, + &global_alpha_value); + + KUNIT_EXPECT_FALSE(test, per_pixel_alpha); + KUNIT_EXPECT_TRUE(test, pre_multiplied_alpha); + KUNIT_EXPECT_TRUE(test, global_alpha); + KUNIT_EXPECT_EQ(test, global_alpha_value, 0x80); +} + +/** + * dm_test_modifier_has_dcc() - Verify helper detects AMD DCC modifiers. + * @test: KUnit test context. + * + * Verify if DCC detection works for linear and AMD DCC modifiers. + */ +static void dm_test_modifier_has_dcc(struct kunit *test) +{ + uint64_t dcc_mod = AMD_FMT_MOD | AMD_FMT_MOD_SET(DCC, 1); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_plane_modifier_has_dcc(DRM_FORMAT_MOD_LINEAR)); + KUNIT_EXPECT_TRUE(test, amdgpu_dm_plane_modifier_has_dcc(dcc_mod)); +} + +/** + * dm_test_modifier_gfx9_swizzle_mode() - Verify swizzle helper for linear and AMD modifiers. + * @test: KUnit test context. + * + * Verify if swizzle mode decoding works for linear and AMD tiled modifiers. + */ +static void dm_test_modifier_gfx9_swizzle_mode(struct kunit *test) +{ + uint64_t mod = AMD_FMT_MOD | AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X); + + KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_modifier_gfx9_swizzle_mode(DRM_FORMAT_MOD_LINEAR), 0U); + KUNIT_EXPECT_EQ(test, + amdgpu_dm_plane_modifier_gfx9_swizzle_mode(mod), + (unsigned int)AMD_FMT_MOD_TILE_GFX9_64K_S_X); +} + +/** + * dm_test_get_plane_formats() - Verify plane format counts for key plane types. + * @test: KUnit test context. + * + * Verify if returned format counts match primary, overlay, and cursor planes. + */ +static void dm_test_get_plane_formats(struct kunit *test) +{ + struct drm_plane *plane; + struct dc_plane_cap *cap; + uint32_t formats[32] = {0}; + + plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL); + cap = kunit_kzalloc(test, sizeof(*cap), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, plane); + KUNIT_ASSERT_NOT_NULL(test, cap); + + plane->type = DRM_PLANE_TYPE_PRIMARY; + KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_get_plane_formats(plane, NULL, formats, 32), 14); + + cap->pixel_format_support.nv12 = true; + cap->pixel_format_support.p010 = true; + cap->pixel_format_support.fp16 = true; + KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_get_plane_formats(plane, cap, formats, 32), 20); + + plane->type = DRM_PLANE_TYPE_OVERLAY; + KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_get_plane_formats(plane, NULL, formats, 32), 9); + + plane->type = DRM_PLANE_TYPE_CURSOR; + KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_get_plane_formats(plane, NULL, formats, 32), 1); +} + +/** + * dm_test_get_plane_modifiers() - Verify early-return and cursor modifier list. + * @test: KUnit test context. + * + * Verify if modifier list handling works for unsupported families and cursor planes. + */ +static void dm_test_get_plane_modifiers(struct kunit *test) +{ + struct amdgpu_device *adev; + uint64_t *mods = NULL; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + + adev->family = AMDGPU_FAMILY_SI; + KUNIT_EXPECT_EQ(test, + amdgpu_dm_plane_get_plane_modifiers(adev, DRM_PLANE_TYPE_PRIMARY, &mods), + 0); + KUNIT_EXPECT_PTR_EQ(test, mods, NULL); + + adev->family = AMDGPU_FAMILY_NV; + KUNIT_ASSERT_EQ(test, + amdgpu_dm_plane_get_plane_modifiers(adev, DRM_PLANE_TYPE_CURSOR, &mods), + 0); + KUNIT_ASSERT_NOT_NULL(test, mods); + KUNIT_EXPECT_EQ(test, mods[0], DRM_FORMAT_MOD_LINEAR); + KUNIT_EXPECT_EQ(test, mods[1], DRM_FORMAT_MOD_INVALID); + kfree(mods); +} + +/** + * dm_test_fill_dc_scaling_info() - Verify basic error and success paths. + * @test: KUnit test context. + * + * Verify if scaling info rejects invalid sizes and accepts valid sizes. + */ +static void dm_test_fill_dc_scaling_info(struct kunit *test) +{ + struct amdgpu_device *adev; + struct drm_plane_state state = {0}; + struct dc_scaling_info info = {0}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + + state.src_w = 0; + state.src_h = 100 << 16; + state.crtc_w = 100; + state.crtc_h = 100; + KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_fill_dc_scaling_info(adev, &state, &info), -EINVAL); + + state.src_w = 100 << 16; + state.src_h = 100 << 16; + state.crtc_w = 100; + state.crtc_h = 100; + KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_fill_dc_scaling_info(adev, &state, &info), 0); +} + +/** + * dm_test_get_min_max_dc_plane_scaling() - Verify format-specific cap selection and 1->1000 conversion. + * @test: KUnit test context. + * + * Verify if min/max scaling values are correct for NV12 and XRGB8888 formats. + */ +static void dm_test_get_min_max_dc_plane_scaling(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + struct drm_framebuffer *fb; + int min_downscale = 0; + int max_upscale = 0; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, dc); + KUNIT_ASSERT_NOT_NULL(test, fb); + + adev->dm.dc = dc; + dc->caps.planes[0].max_upscale_factor.nv12 = 1; + dc->caps.planes[0].max_downscale_factor.nv12 = 1; + dc->caps.planes[0].max_upscale_factor.argb8888 = 1600; + dc->caps.planes[0].max_downscale_factor.argb8888 = 250; + + fb->format = drm_format_info(DRM_FORMAT_NV12); + KUNIT_ASSERT_NOT_NULL(test, fb->format); + amdgpu_dm_plane_get_min_max_dc_plane_scaling(&adev->ddev, fb, &min_downscale, &max_upscale); + KUNIT_EXPECT_EQ(test, min_downscale, 1000); + KUNIT_EXPECT_EQ(test, max_upscale, 1000); + + fb->format = drm_format_info(DRM_FORMAT_XRGB8888); + KUNIT_ASSERT_NOT_NULL(test, fb->format); + amdgpu_dm_plane_get_min_max_dc_plane_scaling(&adev->ddev, fb, &min_downscale, &max_upscale); + KUNIT_EXPECT_EQ(test, min_downscale, 250); + KUNIT_EXPECT_EQ(test, max_upscale, 1600); +} + +/** + * dm_test_fill_plane_buffer_attributes_gfx8() - Verify graphics path and GFX8 tiling fill. + * @test: KUnit test context. + * + * Verify if GFX8 plane buffer attributes and tiling fields are filled correctly. + */ +static void dm_test_fill_plane_buffer_attributes_gfx8(struct kunit *test) +{ + struct amdgpu_device *adev; + struct amdgpu_framebuffer *afb; + struct dc_tiling_info *tiling_info; + struct plane_size *plane_size; + struct dc_plane_dcc_param *dcc; + struct dc_plane_address *address; + uint64_t tiling_flags = 0; + int ret; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + afb = kunit_kzalloc(test, sizeof(*afb), GFP_KERNEL); + tiling_info = kunit_kzalloc(test, sizeof(*tiling_info), GFP_KERNEL); + plane_size = kunit_kzalloc(test, sizeof(*plane_size), GFP_KERNEL); + dcc = kunit_kzalloc(test, sizeof(*dcc), GFP_KERNEL); + address = kunit_kzalloc(test, sizeof(*address), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, afb); + KUNIT_ASSERT_NOT_NULL(test, tiling_info); + KUNIT_ASSERT_NOT_NULL(test, plane_size); + KUNIT_ASSERT_NOT_NULL(test, dcc); + KUNIT_ASSERT_NOT_NULL(test, address); + + adev->family = AMDGPU_FAMILY_SI; + afb->address = 0x12345000ULL; + afb->base.width = 1920; + afb->base.height = 1080; + afb->base.offsets[0] = 0x1000; + afb->base.pitches[0] = 7680; + afb->base.format = drm_format_info(DRM_FORMAT_XRGB8888); + KUNIT_ASSERT_NOT_NULL(test, afb->base.format); + + tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, DC_ARRAY_1D_TILED_THIN1); + tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 5); + + ret = amdgpu_dm_plane_fill_plane_buffer_attributes(adev, afb, + SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, ROTATION_ANGLE_0, + tiling_flags, tiling_info, plane_size, dcc, address, true); + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, plane_size->surface_size.width, 1920); + KUNIT_EXPECT_EQ(test, plane_size->surface_size.height, 1080); + KUNIT_EXPECT_EQ(test, plane_size->surface_pitch, 1920); + KUNIT_EXPECT_EQ(test, address->type, (int)PLN_ADDR_TYPE_GRAPHICS); + KUNIT_EXPECT_TRUE(test, address->tmz_surface); + KUNIT_EXPECT_EQ(test, (int)tiling_info->gfx8.array_mode, (int)DC_ARRAY_1D_TILED_THIN1); + KUNIT_EXPECT_EQ(test, tiling_info->gfx8.pipe_config, 5U); +} + +/** + * dm_test_get_cursor_position() - Verify cursor clipping and off-screen handling. + * @test: KUnit test context. + * + * Verify if cursor clipping, hotspot adjustment, and off-screen disable behavior work. + */ +static void dm_test_get_cursor_position(struct kunit *test) +{ + struct amdgpu_device *adev; + struct amdgpu_crtc *amdgpu_crtc; + struct drm_plane *plane; + struct drm_plane_state *state; + struct drm_framebuffer *fb; + struct dc_cursor_position position = {0}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + amdgpu_crtc = kunit_kzalloc(test, sizeof(*amdgpu_crtc), GFP_KERNEL); + plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL); + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, amdgpu_crtc); + KUNIT_ASSERT_NOT_NULL(test, plane); + KUNIT_ASSERT_NOT_NULL(test, state); + KUNIT_ASSERT_NOT_NULL(test, fb); + + adev->ip_versions[DCE_HWIP][0] = IP_VERSION(4, 0, 0); + amdgpu_crtc->max_cursor_width = 64; + amdgpu_crtc->max_cursor_height = 64; + + plane->dev = &adev->ddev; + plane->state = state; + state->fb = fb; + state->crtc_x = -5; + state->crtc_y = -7; + state->crtc_w = 32; + state->crtc_h = 32; + + KUNIT_ASSERT_EQ(test, + amdgpu_dm_plane_get_cursor_position(plane, &amdgpu_crtc->base, &position), + 0); + KUNIT_EXPECT_TRUE(test, position.enable); + KUNIT_EXPECT_EQ(test, position.x, 0); + KUNIT_EXPECT_EQ(test, position.y, 0); + KUNIT_EXPECT_EQ(test, position.x_hotspot, 5); + KUNIT_EXPECT_EQ(test, position.y_hotspot, 7); + KUNIT_EXPECT_TRUE(test, position.translate_by_source); + + memset(&position, 0, sizeof(position)); + state->crtc_x = -64; + state->crtc_y = 0; + KUNIT_ASSERT_EQ(test, + amdgpu_dm_plane_get_cursor_position(plane, &amdgpu_crtc->base, &position), + 0); + KUNIT_EXPECT_FALSE(test, position.enable); +} + +/** + * dm_test_format_mod_supported() - Verify key format/modifier acceptance and rejection paths. + * @test: KUnit test context. + * + * Verify if format-modifier support checks match accepted and rejected cases. + */ +static void dm_test_format_mod_supported(struct kunit *test) +{ + struct amdgpu_device *adev; + struct drm_plane *plane; + uint64_t listed_mod; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, plane); + + adev->family = AMDGPU_FAMILY_NV; + plane->dev = &adev->ddev; + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_XRGB8888, + DRM_FORMAT_MOD_LINEAR)); + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_XRGB8888, + DRM_FORMAT_MOD_INVALID)); + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_XRGB8888, + DRM_FORMAT_MOD_VENDOR_AMD)); + + listed_mod = AMD_FMT_MOD | + AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | + AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX9) | + AMD_FMT_MOD_SET(DCC, 1); + plane->modifiers = &listed_mod; + plane->modifier_count = 1; + + KUNIT_EXPECT_FALSE(test, + amdgpu_dm_plane_format_mod_supported(plane, DRM_FORMAT_NV12, listed_mod)); +} + +/** + * dm_test_fill_gfx12_plane_attributes_from_modifiers() - Verify GFX12 DCC mapping path. + * @test: KUnit test context. + * + * Verify if GFX12 modifier parsing enables DCC and sets expected DCC block mode. + */ +static void dm_test_fill_gfx12_plane_attributes_from_modifiers(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + struct amdgpu_framebuffer *afb; + struct plane_size plane_size = {0}; + struct dc_tiling_info tiling_info = {0}; + struct dc_plane_dcc_param dcc = {0}; + struct dc_plane_address address = {0}; + struct dm_test_dcc_cap_ctx ctx = { + .callback_ret = true, + .capable = true, + .output_independent_64b_blks = false, + }; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + afb = kunit_kzalloc(test, sizeof(*afb), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, dc); + KUNIT_ASSERT_NOT_NULL(test, afb); + + adev->family = AMDGPU_FAMILY_GC_12_0_0; + adev->dm.dc = dc; + adev->gfx.config.gb_addr_config_fields.num_pipes = 2; + adev->gfx.config.gb_addr_config_fields.num_banks = 4; + adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256; + adev->gfx.config.gb_addr_config_fields.num_se = 1; + adev->gfx.config.gb_addr_config_fields.max_compress_frags = 2; + adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 1; + dc->cap_funcs.get_dcc_compression_cap = dm_test_get_dcc_compression_cap; + dm_test_dcc_ctx = &ctx; + + afb->base.modifier = AMD_FMT_MOD | + AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX12_64K_2D) | + AMD_FMT_MOD_SET(TILE_VERSION, AMD_FMT_MOD_TILE_VER_GFX12) | + AMD_FMT_MOD_SET(DCC, 1) | + AMD_FMT_MOD_SET(DCC_MAX_COMPRESSED_BLOCK, 1); + plane_size.surface_size.width = 1920; + plane_size.surface_size.height = 1080; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers( + adev, afb, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, + ROTATION_ANGLE_0, &plane_size, &tiling_info, &dcc, &address), + 0); + KUNIT_EXPECT_EQ(test, (int)tiling_info.gfxversion, (int)DcGfxAddr3); + KUNIT_EXPECT_TRUE(test, dcc.enable); + KUNIT_EXPECT_EQ(test, (int)dcc.dcc_ind_blk, (int)hubp_ind_block_128b); + + dm_test_dcc_ctx = NULL; +} + +/** + * dm_test_fill_gfx9_plane_attributes_from_modifiers() - Verify basic GFX9 linear modifier path. + * @test: KUnit test context. + * + * Verify if GFX9 linear modifier handling keeps DCC disabled. + */ +static void dm_test_fill_gfx9_plane_attributes_from_modifiers(struct kunit *test) +{ + struct amdgpu_device *adev; + struct amdgpu_framebuffer *afb; + struct plane_size plane_size = {0}; + struct dc_tiling_info tiling_info = {0}; + struct dc_plane_dcc_param dcc = {0}; + struct dc_plane_address address = {0}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + afb = kunit_kzalloc(test, sizeof(*afb), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, afb); + + adev->family = AMDGPU_FAMILY_NV; + adev->gfx.config.gb_addr_config_fields.num_pipes = 2; + adev->gfx.config.gb_addr_config_fields.num_banks = 4; + adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256; + adev->gfx.config.gb_addr_config_fields.num_se = 1; + adev->gfx.config.gb_addr_config_fields.max_compress_frags = 2; + adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 1; + adev->gfx.config.gb_addr_config_fields.num_pkrs = 2; + adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 3, 0); + + afb->base.modifier = DRM_FORMAT_MOD_LINEAR; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers( + adev, afb, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, + ROTATION_ANGLE_0, &plane_size, &tiling_info, &dcc, &address), + 0); + KUNIT_EXPECT_EQ(test, (int)tiling_info.gfxversion, (int)DcGfxVersion9); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.swizzle, 0U); + KUNIT_EXPECT_FALSE(test, dcc.enable); +} + +/** + * dm_test_helper_check_state_viewport_reject() - Verify viewport outside screen rejects state. + * @test: KUnit test context. + * + * Verify if plane state is rejected when the viewport is outside display bounds. + */ +static void dm_test_helper_check_state_viewport_reject(struct kunit *test) +{ + struct drm_plane *plane; + struct drm_plane_state *state; + struct drm_crtc *crtc; + struct drm_crtc_state *new_crtc_state; + struct drm_framebuffer *fb; + + plane = kunit_kzalloc(test, sizeof(*plane), GFP_KERNEL); + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + crtc = kunit_kzalloc(test, sizeof(*crtc), GFP_KERNEL); + new_crtc_state = kunit_kzalloc(test, sizeof(*new_crtc_state), GFP_KERNEL); + fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, plane); + KUNIT_ASSERT_NOT_NULL(test, state); + KUNIT_ASSERT_NOT_NULL(test, crtc); + KUNIT_ASSERT_NOT_NULL(test, new_crtc_state); + KUNIT_ASSERT_NOT_NULL(test, fb); + + plane->type = DRM_PLANE_TYPE_OVERLAY; + state->plane = plane; + state->fb = fb; + state->crtc = crtc; + state->crtc_x = 200; + state->crtc_y = 0; + state->crtc_w = 100; + state->crtc_h = 100; + new_crtc_state->mode.crtc_hdisplay = 100; + new_crtc_state->mode.crtc_vdisplay = 100; + + KUNIT_EXPECT_EQ(test, amdgpu_dm_plane_helper_check_state(state, new_crtc_state), -EINVAL); +} + +/** + * dm_test_validate_dcc_disabled_returns_success() - Verify disabled DCC is accepted. + * @test: KUnit test context. + * + * Verify if DCC validation succeeds when DCC is disabled. + */ +static void dm_test_validate_dcc_disabled_returns_success(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + struct dc_tiling_info tiling_info = {0}; + struct dc_plane_dcc_param dcc = {0}; + struct dc_plane_address address = {0}; + struct plane_size plane_size = {0}; + + dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address, + &plane_size, test); + dcc.enable = 0; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, + ROTATION_ANGLE_0, &tiling_info, &dcc, + &address, &plane_size), + 0); +} + +/** + * dm_test_validate_dcc_video_non_gfx12_fails() - Verify video format restriction on pre-GFX12. + * @test: KUnit test context. + * + * Verify if video format DCC validation fails on non-GFX12 devices. + */ +static void dm_test_validate_dcc_video_non_gfx12_fails(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + struct dc_tiling_info tiling_info = {0}; + struct dc_plane_dcc_param dcc = {0}; + struct dc_plane_address address = {0}; + struct plane_size plane_size = {0}; + + dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address, + &plane_size, test); + adev->family = AMDGPU_FAMILY_NV; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, + ROTATION_ANGLE_0, &tiling_info, &dcc, + &address, &plane_size), + -EINVAL); +} + +/** + * dm_test_validate_dcc_missing_cap_func_fails() - Verify missing capability callback fails. + * @test: KUnit test context. + * + * Verify if validation fails when DCC capability callback is not provided. + */ +static void dm_test_validate_dcc_missing_cap_func_fails(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + struct dc_tiling_info tiling_info = {0}; + struct dc_plane_dcc_param dcc = {0}; + struct dc_plane_address address = {0}; + struct plane_size plane_size = {0}; + + dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address, + &plane_size, test); + dc->cap_funcs.get_dcc_compression_cap = NULL; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, + ROTATION_ANGLE_0, &tiling_info, &dcc, + &address, &plane_size), + -EINVAL); +} + +/** + * dm_test_validate_dcc_success_and_scan_mapping() - Verify success path and rotation-to-scan mapping. + * @test: KUnit test context. + * + * Verify if DCC validation succeeds and rotation-to-scan mapping is correct. + */ +static void dm_test_validate_dcc_success_and_scan_mapping(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + struct dc_tiling_info tiling_info = {0}; + struct dc_plane_dcc_param dcc = {0}; + struct dc_plane_address address = {0}; + struct plane_size plane_size = {0}; + struct dm_test_dcc_cap_ctx ctx = { + .callback_ret = true, + .capable = true, + .output_independent_64b_blks = true, + }; + + dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address, + &plane_size, test); + dc->cap_funcs.get_dcc_compression_cap = dm_test_get_dcc_compression_cap; + dm_test_dcc_ctx = &ctx; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, + ROTATION_ANGLE_90, &tiling_info, &dcc, + &address, &plane_size), + 0); + KUNIT_EXPECT_TRUE(test, ctx.called); + KUNIT_EXPECT_EQ(test, (int)ctx.captured_input.scan, (int)SCAN_DIRECTION_VERTICAL); + KUNIT_EXPECT_EQ(test, (int)ctx.captured_input.format, + (int)SURFACE_PIXEL_FORMAT_GRPH_ARGB8888); + + dm_test_dcc_ctx = NULL; +} + +/** + * dm_test_validate_dcc_independent_64b_mismatch_fails() - Verify 64B compatibility check. + * @test: KUnit test context. + * + * Verify if validation fails when independent_64b_blks values do not match. + */ +static void dm_test_validate_dcc_independent_64b_mismatch_fails(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc *dc; + struct dc_tiling_info tiling_info = {0}; + struct dc_plane_dcc_param dcc = {0}; + struct dc_plane_address address = {0}; + struct plane_size plane_size = {0}; + struct dm_test_dcc_cap_ctx ctx = { + .callback_ret = true, + .capable = true, + .output_independent_64b_blks = true, + }; + + dm_test_init_validate_dcc_inputs(&adev, &dc, &tiling_info, &dcc, &address, + &plane_size, test); + dcc.independent_64b_blks = 0; + dc->cap_funcs.get_dcc_compression_cap = dm_test_get_dcc_compression_cap; + dm_test_dcc_ctx = &ctx; + + KUNIT_EXPECT_EQ(test, + amdgpu_dm_plane_validate_dcc(adev, SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, + ROTATION_ANGLE_0, &tiling_info, &dcc, + &address, &plane_size), + -EINVAL); + + dm_test_dcc_ctx = NULL; +} + +/** + * dm_test_add_modifier_appends_value() - Verify one modifier append. + * @test: KUnit test context. + * + * Verify if a modifier is appended and size is updated. + */ +static void dm_test_add_modifier_appends_value(struct kunit *test) +{ + uint64_t size = 0; + uint64_t cap = 2; + uint64_t *mods = kmalloc_array(cap, sizeof(*mods), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, mods); + + amdgpu_dm_plane_add_modifier(&mods, &size, &cap, 0x1234ULL); + + KUNIT_ASSERT_NOT_NULL(test, mods); + KUNIT_EXPECT_EQ(test, size, 1ULL); + KUNIT_EXPECT_EQ(test, cap, 2ULL); + KUNIT_EXPECT_EQ(test, mods[0], 0x1234ULL); + + kfree(mods); +} + +/** + * dm_test_add_modifier_grows_capacity() - Verify add triggers growth and preserves old data. + * @test: KUnit test context. + * + * Verify if modifier array growth keeps old data and appends new data. + */ +static void dm_test_add_modifier_grows_capacity(struct kunit *test) +{ + uint64_t size = 1; + uint64_t cap = 1; + uint64_t *mods = kmalloc_array(cap, sizeof(*mods), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, mods); + mods[0] = 0xAAULL; + + amdgpu_dm_plane_add_modifier(&mods, &size, &cap, 0xBBULL); + + KUNIT_ASSERT_NOT_NULL(test, mods); + KUNIT_EXPECT_EQ(test, cap, 2ULL); + KUNIT_EXPECT_EQ(test, size, 2ULL); + KUNIT_EXPECT_EQ(test, mods[0], 0xAAULL); + KUNIT_EXPECT_EQ(test, mods[1], 0xBBULL); + + kfree(mods); +} + +/** + * dm_test_add_modifier_noop_when_mods_null() - Verify helper is a no-op on NULL mods list. + * @test: KUnit test context. + * + * Verify if add_modifier does nothing when the modifier list is NULL. + */ +static void dm_test_add_modifier_noop_when_mods_null(struct kunit *test) +{ + uint64_t size = 3; + uint64_t cap = 7; + uint64_t *mods = NULL; + + amdgpu_dm_plane_add_modifier(&mods, &size, &cap, 0x55ULL); + + KUNIT_EXPECT_PTR_EQ(test, mods, NULL); + KUNIT_EXPECT_EQ(test, size, 3ULL); + KUNIT_EXPECT_EQ(test, cap, 7ULL); +} + +/** + * dm_test_fill_gfx8_tiling_info_2d_tiled() - Verify GFX8 2D tiled flag parsing. + * @test: KUnit test context. + * + * Verify if 2D tiled GFX8 flags populate expected tiling fields. + */ +static void dm_test_fill_gfx8_tiling_info_2d_tiled(struct kunit *test) +{ + struct dc_tiling_info tiling_info = {0}; + uint64_t tiling_flags = 0; + + tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, DC_ARRAY_2D_TILED_THIN1); + tiling_flags |= AMDGPU_TILING_SET(BANK_WIDTH, 2); + tiling_flags |= AMDGPU_TILING_SET(BANK_HEIGHT, 1); + tiling_flags |= AMDGPU_TILING_SET(MACRO_TILE_ASPECT, 3); + tiling_flags |= AMDGPU_TILING_SET(TILE_SPLIT, 4); + tiling_flags |= AMDGPU_TILING_SET(NUM_BANKS, 2); + tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 7); + + amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(&tiling_info, tiling_flags); + + KUNIT_EXPECT_EQ(test, (int)tiling_info.gfxversion, (int)DcGfxVersion8); + KUNIT_EXPECT_EQ(test, (int)tiling_info.gfx8.array_mode, (int)DC_ARRAY_2D_TILED_THIN1); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.bank_width, 2U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.bank_height, 1U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.tile_aspect, 3U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.tile_split, 4U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.num_banks, 2U); + KUNIT_EXPECT_EQ(test, (int)tiling_info.gfx8.tile_mode, + (int)DC_ADDR_SURF_MICRO_TILING_DISPLAY); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.pipe_config, 7U); +} + +/** + * dm_test_fill_gfx8_tiling_info_1d_tiled() - Verify GFX8 1D tiled flag parsing. + * @test: KUnit test context. + * + * Verify if 1D tiled GFX8 flags populate array mode and pipe config. + */ +static void dm_test_fill_gfx8_tiling_info_1d_tiled(struct kunit *test) +{ + struct dc_tiling_info tiling_info = {0}; + uint64_t tiling_flags = 0; + + tiling_flags |= AMDGPU_TILING_SET(ARRAY_MODE, DC_ARRAY_1D_TILED_THIN1); + tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 5); + + amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(&tiling_info, tiling_flags); + + KUNIT_EXPECT_EQ(test, (int)tiling_info.gfx8.array_mode, (int)DC_ARRAY_1D_TILED_THIN1); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.pipe_config, 5U); +} + +/** + * dm_test_fill_gfx8_tiling_info_other_mode() - Verify non-1D/non-2D mode handling. + * @test: KUnit test context. + * + * Verify if unsupported array mode keeps preset fields and updates pipe config. + */ +static void dm_test_fill_gfx8_tiling_info_other_mode(struct kunit *test) +{ + struct dc_tiling_info tiling_info = {0}; + uint64_t tiling_flags = 0; + + tiling_info.gfxversion = 0x7f; + tiling_info.gfx8.array_mode = 0x7f; + tiling_info.gfx8.tile_mode = 0x7f; + tiling_info.gfx8.num_banks = 0x7f; + + tiling_flags |= AMDGPU_TILING_SET(PIPE_CONFIG, 6); + + amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags(&tiling_info, tiling_flags); + + KUNIT_EXPECT_EQ(test, tiling_info.gfxversion, 0x7f); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.array_mode, 0x7f); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.tile_mode, 0x7f); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.num_banks, 0x7f); + KUNIT_EXPECT_EQ(test, tiling_info.gfx8.pipe_config, 6U); +} + +/** + * dm_test_fill_gfx9_tiling_info_from_device_pre_10_3() - Verify GFX9 field copy before 10.3. + * @test: KUnit test context. + * + * Verify if pre-10.3 device fields are copied and existing num_pkrs is kept. + */ +static void dm_test_fill_gfx9_tiling_info_from_device_pre_10_3(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_tiling_info tiling_info = {0}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + + adev->gfx.config.gb_addr_config_fields.num_pipes = 4; + adev->gfx.config.gb_addr_config_fields.num_banks = 8; + adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256; + adev->gfx.config.gb_addr_config_fields.num_se = 2; + adev->gfx.config.gb_addr_config_fields.max_compress_frags = 1; + adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 2; + adev->gfx.config.gb_addr_config_fields.num_pkrs = 3; + adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 2, 9); + + tiling_info.gfx9.num_pkrs = 0x5a; + + amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(adev, &tiling_info); + + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 4U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 8U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.pipe_interleave, 256U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 2U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.max_compressed_frags, 1U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_rb_per_se, 2U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 0x5aU); +} + +/** + * dm_test_fill_gfx9_tiling_info_from_device_10_3_plus() - Verify num_pkrs update on 10.3+. + * @test: KUnit test context. + * + * Verify if 10.3+ device fields are copied and num_pkrs is updated. + */ +static void dm_test_fill_gfx9_tiling_info_from_device_10_3_plus(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_tiling_info tiling_info = {0}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + + adev->gfx.config.gb_addr_config_fields.num_pipes = 2; + adev->gfx.config.gb_addr_config_fields.num_banks = 4; + adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 128; + adev->gfx.config.gb_addr_config_fields.num_se = 1; + adev->gfx.config.gb_addr_config_fields.max_compress_frags = 2; + adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 1; + adev->gfx.config.gb_addr_config_fields.num_pkrs = 6; + adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 3, 0); + + amdgpu_dm_plane_fill_gfx9_tiling_info_from_device(adev, &tiling_info); + + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 2U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 4U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.pipe_interleave, 128U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 1U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.max_compressed_frags, 2U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_rb_per_se, 1U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 6U); +} + +/** + * dm_test_fill_gfx9_tiling_info_from_modifier_linear() - Verify non-AMD modifier keeps device values. + * @test: KUnit test context. + * + * Verify if linear modifier path keeps values from device configuration. + */ +static void dm_test_fill_gfx9_tiling_info_from_modifier_linear(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_tiling_info tiling_info = {0}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + + adev->family = AMDGPU_FAMILY_NV; + adev->gfx.config.gb_addr_config_fields.num_pipes = 4; + adev->gfx.config.gb_addr_config_fields.num_banks = 8; + adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256; + adev->gfx.config.gb_addr_config_fields.num_se = 2; + adev->gfx.config.gb_addr_config_fields.max_compress_frags = 1; + adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 2; + adev->gfx.config.gb_addr_config_fields.num_pkrs = 3; + adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 3, 0); + + amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(adev, &tiling_info, + DRM_FORMAT_MOD_LINEAR); + + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 4U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 8U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.pipe_interleave, 256U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 2U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.max_compressed_frags, 1U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_rb_per_se, 2U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 3U); +} + +/** + * dm_test_fill_gfx9_tiling_info_from_modifier_pre_nv() - Verify AMD modifier updates banks on pre-NV. + * @test: KUnit test context. + * + * Verify if AMD modifier updates pre-NV pipe, engine, and bank fields. + */ +static void dm_test_fill_gfx9_tiling_info_from_modifier_pre_nv(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_tiling_info tiling_info = {0}; + uint64_t modifier; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + + adev->family = AMDGPU_FAMILY_RV; + adev->gfx.config.gb_addr_config_fields.num_pipes = 4; + adev->gfx.config.gb_addr_config_fields.num_banks = 16; + adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 256; + adev->gfx.config.gb_addr_config_fields.num_se = 2; + adev->gfx.config.gb_addr_config_fields.max_compress_frags = 1; + adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 2; + adev->gfx.config.gb_addr_config_fields.num_pkrs = 7; + adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 2, 9); + + tiling_info.gfx9.num_pkrs = 0x5a; + + modifier = AMD_FMT_MOD | + AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | + AMD_FMT_MOD_SET(PIPE_XOR_BITS, 7) | + AMD_FMT_MOD_SET(BANK_XOR_BITS, 3) | + AMD_FMT_MOD_SET(PACKERS, 2); + + amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(adev, &tiling_info, modifier); + + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 32U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 4U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 8U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 0x5aU); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U); +} + +/** + * dm_test_fill_gfx9_tiling_info_from_modifier_nv() - Verify AMD modifier updates packers on NV+. + * @test: KUnit test context. + * + * Verify if AMD modifier updates NV+ pipe, engine, and packer fields. + */ +static void dm_test_fill_gfx9_tiling_info_from_modifier_nv(struct kunit *test) +{ + struct amdgpu_device *adev; + struct dc_tiling_info tiling_info = {0}; + uint64_t modifier; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + + adev->family = AMDGPU_FAMILY_NV; + adev->gfx.config.gb_addr_config_fields.num_pipes = 2; + adev->gfx.config.gb_addr_config_fields.num_banks = 9; + adev->gfx.config.gb_addr_config_fields.pipe_interleave_size = 128; + adev->gfx.config.gb_addr_config_fields.num_se = 1; + adev->gfx.config.gb_addr_config_fields.max_compress_frags = 2; + adev->gfx.config.gb_addr_config_fields.num_rb_per_se = 1; + adev->gfx.config.gb_addr_config_fields.num_pkrs = 2; + adev->ip_versions[GC_HWIP][0] = IP_VERSION(10, 3, 0); + + modifier = AMD_FMT_MOD | + AMD_FMT_MOD_SET(TILE, AMD_FMT_MOD_TILE_GFX9_64K_S_X) | + AMD_FMT_MOD_SET(PIPE_XOR_BITS, 6) | + AMD_FMT_MOD_SET(BANK_XOR_BITS, 2) | + AMD_FMT_MOD_SET(PACKERS, 3); + + amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier(adev, &tiling_info, modifier); + + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pipes, 32U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_shader_engines, 2U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_banks, 9U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.num_pkrs, 8U); + KUNIT_EXPECT_EQ(test, tiling_info.gfx9.shaderEnable, 1U); +} + +static struct kunit_case amdgpu_dm_plane_test_cases[] = { + /* amdgpu_dm_plane_is_video_format() */ + KUNIT_CASE(dm_test_plane_is_video_format_known_video), + /* amdgpu_dm_plane_fill_blending_from_plane_state() */ + KUNIT_CASE(dm_test_fill_blending_defaults), + KUNIT_CASE(dm_test_fill_blending_premulti_alpha_format), + KUNIT_CASE(dm_test_fill_blending_coverage_alpha_format), + KUNIT_CASE(dm_test_fill_blending_global_alpha), + /* amdgpu_dm_plane_modifier_* helpers() */ + KUNIT_CASE(dm_test_modifier_has_dcc), + KUNIT_CASE(dm_test_modifier_gfx9_swizzle_mode), + /* amdgpu_dm_plane_get_plane_formats() */ + KUNIT_CASE(dm_test_get_plane_formats), + /* amdgpu_dm_plane_get_plane_modifiers() */ + KUNIT_CASE(dm_test_get_plane_modifiers), + /* amdgpu_dm_plane_fill_dc_scaling_info() */ + KUNIT_CASE(dm_test_fill_dc_scaling_info), + /* amdgpu_dm_plane_get_min_max_dc_plane_scaling() */ + KUNIT_CASE(dm_test_get_min_max_dc_plane_scaling), + /* amdgpu_dm_plane_fill_plane_buffer_attributes() */ + KUNIT_CASE(dm_test_fill_plane_buffer_attributes_gfx8), + /* amdgpu_dm_plane_get_cursor_position() */ + KUNIT_CASE(dm_test_get_cursor_position), + /* amdgpu_dm_plane_format_mod_supported() */ + KUNIT_CASE(dm_test_format_mod_supported), + /* amdgpu_dm_plane_fill_gfx12_plane_attributes_from_modifiers() */ + KUNIT_CASE(dm_test_fill_gfx12_plane_attributes_from_modifiers), + /* amdgpu_dm_plane_fill_gfx9_plane_attributes_from_modifiers() */ + KUNIT_CASE(dm_test_fill_gfx9_plane_attributes_from_modifiers), + /* amdgpu_dm_plane_helper_check_state() */ + KUNIT_CASE(dm_test_helper_check_state_viewport_reject), + /* amdgpu_dm_plane_add_modifier() */ + KUNIT_CASE(dm_test_add_modifier_appends_value), + KUNIT_CASE(dm_test_add_modifier_grows_capacity), + KUNIT_CASE(dm_test_add_modifier_noop_when_mods_null), + /* amdgpu_dm_plane_fill_gfx8_tiling_info_from_flags() */ + KUNIT_CASE(dm_test_fill_gfx8_tiling_info_2d_tiled), + KUNIT_CASE(dm_test_fill_gfx8_tiling_info_1d_tiled), + KUNIT_CASE(dm_test_fill_gfx8_tiling_info_other_mode), + /* amdgpu_dm_plane_fill_gfx9_tiling_info_from_device() */ + KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_device_pre_10_3), + KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_device_10_3_plus), + /* amdgpu_dm_plane_fill_gfx9_tiling_info_from_modifier() */ + KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_modifier_linear), + KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_modifier_pre_nv), + KUNIT_CASE(dm_test_fill_gfx9_tiling_info_from_modifier_nv), + /* amdgpu_dm_plane_validate_dcc() */ + KUNIT_CASE(dm_test_validate_dcc_disabled_returns_success), + KUNIT_CASE(dm_test_validate_dcc_video_non_gfx12_fails), + KUNIT_CASE(dm_test_validate_dcc_missing_cap_func_fails), + KUNIT_CASE(dm_test_validate_dcc_success_and_scan_mapping), + KUNIT_CASE(dm_test_validate_dcc_independent_64b_mismatch_fails), + {} +}; + +static struct kunit_suite amdgpu_dm_plane_test_suite = { + .name = "amdgpu_dm_plane", + .test_cases = amdgpu_dm_plane_test_cases, +}; + +kunit_test_suite(amdgpu_dm_plane_test_suite); + +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_plane"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c new file mode 100644 index 000000000000..8d1d26bfcc16 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_pp_smu_test.c @@ -0,0 +1,2454 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_pp_smu.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> +#include <linux/types.h> +#include <linux/mutex.h> + +#include "dc.h" +#include "dm_services.h" +#include "dm_pp_smu.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_pp_smu.h" + +/* ---- Stub DPM layer ---- */ + +/** + * struct stub_dpm_context - Tracks stub DPM callback invocations + * @ret_val: Return value for the next DPM callback + * @get_current_clocks_info: Clock info returned by stub get_current_clocks + * @get_clock_by_type_clocks: Clocks returned by stub get_clock_by_type + * @get_validation_clks: Validation clocks returned by stub + * @get_clock_by_type_with_latency_clks: Returned by stub with_latency + * @get_clock_by_type_with_voltage_clks: Returned by stub with_voltage + * @set_watermarks_ret: Return value for set_watermarks + * @display_clock_voltage_ret: Return value for display_clock_voltage_request + * @display_disable_memory_clock_switch_ret: Return for disable_memory_clock + * @get_max_sustainable_ret: Return for get_max_sustainable_clocks_by_dc + * @get_uclk_dpm_ret: Return for get_uclk_dpm_states + * @get_dpm_clock_table_ret: Return for get_dpm_clock_table + * @set_active_display_count_ret: Return for set_active_display_count + * @set_min_deep_sleep_dcefclk_ret: Return for set_min_deep_sleep_dcefclk + * @get_validation_clks_ret: Return for get_display_mode_validation_clocks + */ +struct stub_dpm_context { + int ret_val; + struct amd_pp_clock_info get_current_clocks_info; + struct amd_pp_clocks get_clock_by_type_clocks; + struct amd_pp_simple_clock_info get_validation_clks; + int get_validation_clks_ret; + struct pp_clock_levels_with_latency get_clock_by_type_with_latency_clks; + struct pp_clock_levels_with_voltage get_clock_by_type_with_voltage_clks; + int set_watermarks_ret; + int display_clock_voltage_ret; + int display_disable_memory_clock_switch_ret; + int get_max_sustainable_ret; + int get_uclk_dpm_ret; + int get_dpm_clock_table_ret; + int set_active_display_count_ret; + int set_min_deep_sleep_dcefclk_ret; +}; + +static struct stub_dpm_context *stub_dpm_ctx; + +static int stub_get_current_clocks(void *handle, struct amd_pp_clock_info *clocks) +{ + if (stub_dpm_ctx->ret_val) + return stub_dpm_ctx->ret_val; + *clocks = stub_dpm_ctx->get_current_clocks_info; + return 0; +} + +static int stub_get_clock_by_type(void *handle, enum amd_pp_clock_type type, + struct amd_pp_clocks *clocks) +{ + if (stub_dpm_ctx->ret_val) + return stub_dpm_ctx->ret_val; + *clocks = stub_dpm_ctx->get_clock_by_type_clocks; + return 0; +} + +static int stub_get_display_mode_validation_clocks(void *handle, + struct amd_pp_simple_clock_info *clocks) +{ + if (stub_dpm_ctx->get_validation_clks_ret) + return stub_dpm_ctx->get_validation_clks_ret; + *clocks = stub_dpm_ctx->get_validation_clks; + return 0; +} + +static int stub_get_clock_by_type_with_latency(void *handle, + enum amd_pp_clock_type type, + struct pp_clock_levels_with_latency *clocks) +{ + if (stub_dpm_ctx->ret_val) + return stub_dpm_ctx->ret_val; + *clocks = stub_dpm_ctx->get_clock_by_type_with_latency_clks; + return 0; +} + +static int stub_get_clock_by_type_with_voltage(void *handle, + enum amd_pp_clock_type type, + struct pp_clock_levels_with_voltage *clocks) +{ + if (stub_dpm_ctx->ret_val) + return stub_dpm_ctx->ret_val; + *clocks = stub_dpm_ctx->get_clock_by_type_with_voltage_clks; + return 0; +} + +static void stub_display_configuration_change(void *handle) +{ + /* No-op: satisfies display_configuration_changed callback */ +} + +static void stub_pm_compute_clocks(void *handle) +{ + /* No-op: satisfies pm_compute_clocks callback */ +} + +static int stub_set_watermarks_for_clocks_ranges(void *handle, void *clock_ranges) +{ + return stub_dpm_ctx->set_watermarks_ret; +} + +static int stub_display_clock_voltage_request(void *handle, + struct pp_display_clock_request *clock) +{ + return stub_dpm_ctx->display_clock_voltage_ret; +} + +static int stub_set_active_display_count(void *handle, uint32_t count) +{ + return stub_dpm_ctx->set_active_display_count_ret; +} + +static int stub_set_min_deep_sleep_dcefclk(void *handle, uint32_t clock) +{ + return stub_dpm_ctx->set_min_deep_sleep_dcefclk_ret; +} + +static int stub_set_hard_min_dcefclk_by_freq(void *handle, uint32_t clock) +{ + return 0; +} + +static int stub_set_hard_min_fclk_by_freq(void *handle, uint32_t clock) +{ + return 0; +} + +static int stub_notify_smu_enable_pwe(void *handle) +{ + return 0; +} + +static int stub_display_disable_memory_clock_switch(void *handle, + bool disable_memory_clock_switch) +{ + return stub_dpm_ctx->display_disable_memory_clock_switch_ret; +} + +static int stub_get_max_sustainable_clocks_by_dc(void *handle, + struct pp_smu_nv_clock_table *max_clocks) +{ + return stub_dpm_ctx->get_max_sustainable_ret; +} + +static int stub_get_uclk_dpm_states(void *handle, + unsigned int *clock_values_in_khz, + unsigned int *num_states) +{ + return stub_dpm_ctx->get_uclk_dpm_ret; +} + +static int stub_get_dpm_clock_table(void *handle, struct dpm_clocks *clock_table) +{ + return stub_dpm_ctx->get_dpm_clock_table_ret; +} + +static const struct amd_pm_funcs stub_pp_funcs = { + .get_current_clocks = stub_get_current_clocks, + .get_clock_by_type = stub_get_clock_by_type, + .get_display_mode_validation_clocks = stub_get_display_mode_validation_clocks, + .get_clock_by_type_with_latency = stub_get_clock_by_type_with_latency, + .get_clock_by_type_with_voltage = stub_get_clock_by_type_with_voltage, + .display_configuration_changed = stub_display_configuration_change, + .pm_compute_clocks = stub_pm_compute_clocks, + .set_watermarks_for_clocks_ranges = stub_set_watermarks_for_clocks_ranges, + .display_clock_voltage_request = stub_display_clock_voltage_request, + .set_active_display_count = stub_set_active_display_count, + .set_min_deep_sleep_dcefclk = stub_set_min_deep_sleep_dcefclk, + .set_hard_min_dcefclk_by_freq = stub_set_hard_min_dcefclk_by_freq, + .set_hard_min_fclk_by_freq = stub_set_hard_min_fclk_by_freq, + .notify_smu_enable_pwe = stub_notify_smu_enable_pwe, + .display_disable_memory_clock_switch = stub_display_disable_memory_clock_switch, + .get_max_sustainable_clocks_by_dc = stub_get_max_sustainable_clocks_by_dc, + .get_uclk_dpm_states = stub_get_uclk_dpm_states, + .get_dpm_clock_table = stub_get_dpm_clock_table, +}; + +/** + * setup_stub_dpm - Initialize a stub DPM environment for testing + * @test: KUnit test context + * @adev: Pointer to amdgpu_device to configure + * + * Sets up adev->powerplay.pp_funcs and initializes adev->pm.mutex so that + * amdgpu_dpm_* functions can be safely called with stub callbacks. + */ +static void setup_stub_dpm(struct kunit *test, struct amdgpu_device *adev) +{ + stub_dpm_ctx = kunit_kzalloc(test, sizeof(*stub_dpm_ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, stub_dpm_ctx); + + adev->powerplay.pp_funcs = &stub_pp_funcs; + adev->powerplay.pp_handle = adev; + mutex_init(&adev->pm.mutex); +} + +/* ---- Tests for get_default_clock_levels ---- */ + +/** + * dm_test_default_clock_levels_display - Test display clock default levels + * @test: KUnit test context + * + * Verify that get_default_clock_levels populates 6 display clock levels + * with the expected frequencies in kHz. + */ +static void dm_test_default_clock_levels_display(struct kunit *test) +{ + struct dm_pp_clock_levels clks = { 0 }; + uint32_t expected[] = { 300000, 400000, 496560, 626090, 685720, 757900 }; + int i; + + get_default_clock_levels(DM_PP_CLOCK_TYPE_DISPLAY_CLK, &clks); + + KUNIT_EXPECT_EQ(test, clks.num_levels, 6U); + for (i = 0; i < 6; i++) + KUNIT_EXPECT_EQ(test, clks.clocks_in_khz[i], expected[i]); +} + +/** + * dm_test_default_clock_levels_engine - Test engine clock default levels + * @test: KUnit test context + * + * Verify that get_default_clock_levels populates 6 engine clock levels + * with the expected frequencies in kHz. + */ +static void dm_test_default_clock_levels_engine(struct kunit *test) +{ + struct dm_pp_clock_levels clks = { 0 }; + uint32_t expected[] = { 300000, 360000, 423530, 514290, 626090, 720000 }; + int i; + + get_default_clock_levels(DM_PP_CLOCK_TYPE_ENGINE_CLK, &clks); + + KUNIT_EXPECT_EQ(test, clks.num_levels, 6U); + for (i = 0; i < 6; i++) + KUNIT_EXPECT_EQ(test, clks.clocks_in_khz[i], expected[i]); +} + +/** + * dm_test_default_clock_levels_memory - Test memory clock default levels + * @test: KUnit test context + * + * Verify that get_default_clock_levels populates 2 memory clock levels + * with the expected frequencies in kHz. + */ +static void dm_test_default_clock_levels_memory(struct kunit *test) +{ + struct dm_pp_clock_levels clks = { 0 }; + + get_default_clock_levels(DM_PP_CLOCK_TYPE_MEMORY_CLK, &clks); + + KUNIT_EXPECT_EQ(test, clks.num_levels, 2U); + KUNIT_EXPECT_EQ(test, clks.clocks_in_khz[0], 333000U); + KUNIT_EXPECT_EQ(test, clks.clocks_in_khz[1], 800000U); +} + +/** + * dm_test_default_clock_levels_unknown - Test unknown clock type default + * @test: KUnit test context + * + * Verify that get_default_clock_levels sets num_levels to 0 for an + * unrecognized clock type. + */ +static void dm_test_default_clock_levels_unknown(struct kunit *test) +{ + struct dm_pp_clock_levels clks = { 0 }; + + get_default_clock_levels(DM_PP_CLOCK_TYPE_FCLK, &clks); + + KUNIT_EXPECT_EQ(test, clks.num_levels, 0U); +} + +/* ---- Tests for dc_to_pp_clock_type ---- */ + +/** + * dm_test_dc_to_pp_clock_type_display - Test display clock type mapping + * @test: KUnit test context + * + * Verify DM_PP_CLOCK_TYPE_DISPLAY_CLK maps to amd_pp_disp_clock. + */ +static void dm_test_dc_to_pp_clock_type_display(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DISPLAY_CLK), + (int)amd_pp_disp_clock); +} + +/** + * dm_test_dc_to_pp_clock_type_engine - Test engine clock type mapping + * @test: KUnit test context + * + * Verify DM_PP_CLOCK_TYPE_ENGINE_CLK maps to amd_pp_sys_clock. + */ +static void dm_test_dc_to_pp_clock_type_engine(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_ENGINE_CLK), + (int)amd_pp_sys_clock); +} + +/** + * dm_test_dc_to_pp_clock_type_memory - Test memory clock type mapping + * @test: KUnit test context + * + * Verify DM_PP_CLOCK_TYPE_MEMORY_CLK maps to amd_pp_mem_clock. + */ +static void dm_test_dc_to_pp_clock_type_memory(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_MEMORY_CLK), + (int)amd_pp_mem_clock); +} + +/** + * dm_test_dc_to_pp_clock_type_dcefclk - Test DCEF clock type mapping + * @test: KUnit test context + * + * Verify DM_PP_CLOCK_TYPE_DCEFCLK maps to amd_pp_dcef_clock. + */ +static void dm_test_dc_to_pp_clock_type_dcefclk(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DCEFCLK), + (int)amd_pp_dcef_clock); +} + +/** + * dm_test_dc_to_pp_clock_type_dcfclk - Test DCF clock type mapping + * @test: KUnit test context + * + * Verify DM_PP_CLOCK_TYPE_DCFCLK maps to amd_pp_dcf_clock. + */ +static void dm_test_dc_to_pp_clock_type_dcfclk(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DCFCLK), + (int)amd_pp_dcf_clock); +} + +/** + * dm_test_dc_to_pp_clock_type_pixelclk - Test pixel clock type mapping + * @test: KUnit test context + * + * Verify DM_PP_CLOCK_TYPE_PIXELCLK maps to amd_pp_pixel_clock. + */ +static void dm_test_dc_to_pp_clock_type_pixelclk(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_PIXELCLK), + (int)amd_pp_pixel_clock); +} + +/** + * dm_test_dc_to_pp_clock_type_fclk - Test FCLK type mapping + * @test: KUnit test context + * + * Verify DM_PP_CLOCK_TYPE_FCLK maps to amd_pp_f_clock. + */ +static void dm_test_dc_to_pp_clock_type_fclk(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_FCLK), + (int)amd_pp_f_clock); +} + +/** + * dm_test_dc_to_pp_clock_type_phyclk - Test display PHY clock type mapping + * @test: KUnit test context + * + * Verify DM_PP_CLOCK_TYPE_DISPLAYPHYCLK maps to amd_pp_phy_clock. + */ +static void dm_test_dc_to_pp_clock_type_phyclk(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DISPLAYPHYCLK), + (int)amd_pp_phy_clock); +} + +/** + * dm_test_dc_to_pp_clock_type_dppclk - Test DPP clock type mapping + * @test: KUnit test context + * + * Verify DM_PP_CLOCK_TYPE_DPPCLK maps to amd_pp_dpp_clock. + */ +static void dm_test_dc_to_pp_clock_type_dppclk(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(DM_PP_CLOCK_TYPE_DPPCLK), + (int)amd_pp_dpp_clock); +} + +/** + * dm_test_dc_to_pp_clock_type_invalid - Test invalid clock type mapping + * @test: KUnit test context + * + * Verify that an invalid clock type value maps to 0. + */ +static void dm_test_dc_to_pp_clock_type_invalid(struct kunit *test) +{ + KUNIT_EXPECT_EQ(test, (int)dc_to_pp_clock_type(0), 0); +} + +/* ---- Tests for pp_to_dc_clock_levels ---- */ + +/** + * dm_test_pp_to_dc_clock_levels_within_limit - Test normal copy within limit + * @test: KUnit test context + * + * Verify that pp_to_dc_clock_levels correctly copies clock values when the + * count is within DM_PP_MAX_CLOCK_LEVELS. + */ +static void dm_test_pp_to_dc_clock_levels_within_limit(struct kunit *test) +{ + struct amd_pp_clocks pp_clks = {}; + struct dm_pp_clock_levels dc_clks = {}; + + pp_clks.count = 3; + pp_clks.clock[0] = 300000; + pp_clks.clock[1] = 500000; + pp_clks.clock[2] = 700000; + + pp_to_dc_clock_levels(&pp_clks, &dc_clks, DM_PP_CLOCK_TYPE_ENGINE_CLK); + + KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 3U); + KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[0], 300000U); + KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[1], 500000U); + KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[2], 700000U); +} + +/** + * dm_test_pp_to_dc_clock_levels_caps_at_max - Test count capping at max + * @test: KUnit test context + * + * Verify that pp_to_dc_clock_levels caps num_levels at DM_PP_MAX_CLOCK_LEVELS + * when the input count exceeds the maximum. + */ +static void dm_test_pp_to_dc_clock_levels_caps_at_max(struct kunit *test) +{ + struct amd_pp_clocks pp_clks = {}; + struct dm_pp_clock_levels dc_clks = {}; + uint32_t i; + + pp_clks.count = DM_PP_MAX_CLOCK_LEVELS + 1; + for (i = 0; i < DM_PP_MAX_CLOCK_LEVELS; i++) + pp_clks.clock[i] = (i + 1) * 100000; + + pp_to_dc_clock_levels(&pp_clks, &dc_clks, DM_PP_CLOCK_TYPE_ENGINE_CLK); + + KUNIT_EXPECT_EQ(test, dc_clks.num_levels, (uint32_t)DM_PP_MAX_CLOCK_LEVELS); +} + +/* ---- Tests for pp_to_dc_clock_levels_with_latency ---- */ + +/** + * dm_test_pp_to_dc_clock_levels_latency_within_limit - Test normal copy + * @test: KUnit test context + * + * Verify that pp_to_dc_clock_levels_with_latency correctly copies clock + * and latency values when count is within limits. + */ +static void dm_test_pp_to_dc_clock_levels_latency_within_limit(struct kunit *test) +{ + struct pp_clock_levels_with_latency pp_clks = {}; + struct dm_pp_clock_levels_with_latency dc_clks = {}; + + pp_clks.num_levels = 2; + pp_clks.data[0].clocks_in_khz = 400000; + pp_clks.data[0].latency_in_us = 10; + pp_clks.data[1].clocks_in_khz = 800000; + pp_clks.data[1].latency_in_us = 20; + + pp_to_dc_clock_levels_with_latency(&pp_clks, &dc_clks, + DM_PP_CLOCK_TYPE_ENGINE_CLK); + + KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 2U); + KUNIT_EXPECT_EQ(test, dc_clks.data[0].clocks_in_khz, 400000U); + KUNIT_EXPECT_EQ(test, dc_clks.data[0].latency_in_us, 10U); + KUNIT_EXPECT_EQ(test, dc_clks.data[1].clocks_in_khz, 800000U); + KUNIT_EXPECT_EQ(test, dc_clks.data[1].latency_in_us, 20U); +} + +/** + * dm_test_pp_to_dc_clock_levels_latency_caps_at_max - Test count capping + * @test: KUnit test context + * + * Verify that pp_to_dc_clock_levels_with_latency caps num_levels at + * DM_PP_MAX_CLOCK_LEVELS when input exceeds the maximum. + */ +static void dm_test_pp_to_dc_clock_levels_latency_caps_at_max(struct kunit *test) +{ + struct pp_clock_levels_with_latency pp_clks = {}; + struct dm_pp_clock_levels_with_latency dc_clks = {}; + + pp_clks.num_levels = DM_PP_MAX_CLOCK_LEVELS + 1; + + pp_to_dc_clock_levels_with_latency(&pp_clks, &dc_clks, + DM_PP_CLOCK_TYPE_ENGINE_CLK); + + KUNIT_EXPECT_EQ(test, dc_clks.num_levels, (uint32_t)DM_PP_MAX_CLOCK_LEVELS); +} + +/* ---- Tests for pp_to_dc_clock_levels_with_voltage ---- */ + +/** + * dm_test_pp_to_dc_clock_levels_voltage_within_limit - Test normal copy + * @test: KUnit test context + * + * Verify that pp_to_dc_clock_levels_with_voltage correctly copies clock + * and voltage values when count is within limits. + */ +static void dm_test_pp_to_dc_clock_levels_voltage_within_limit(struct kunit *test) +{ + struct pp_clock_levels_with_voltage pp_clks = {}; + struct dm_pp_clock_levels_with_voltage dc_clks = {}; + + pp_clks.num_levels = 2; + pp_clks.data[0].clocks_in_khz = 300000; + pp_clks.data[0].voltage_in_mv = 800; + pp_clks.data[1].clocks_in_khz = 600000; + pp_clks.data[1].voltage_in_mv = 950; + + pp_to_dc_clock_levels_with_voltage(&pp_clks, &dc_clks, + DM_PP_CLOCK_TYPE_MEMORY_CLK); + + KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 2U); + KUNIT_EXPECT_EQ(test, dc_clks.data[0].clocks_in_khz, 300000U); + KUNIT_EXPECT_EQ(test, dc_clks.data[0].voltage_in_mv, 800U); + KUNIT_EXPECT_EQ(test, dc_clks.data[1].clocks_in_khz, 600000U); + KUNIT_EXPECT_EQ(test, dc_clks.data[1].voltage_in_mv, 950U); +} + +/** + * dm_test_pp_to_dc_clock_levels_voltage_caps_at_max - Test count capping + * @test: KUnit test context + * + * Verify that pp_to_dc_clock_levels_with_voltage caps num_levels at + * DM_PP_MAX_CLOCK_LEVELS when input exceeds the maximum. + */ +static void dm_test_pp_to_dc_clock_levels_voltage_caps_at_max(struct kunit *test) +{ + struct pp_clock_levels_with_voltage pp_clks = {}; + struct dm_pp_clock_levels_with_voltage dc_clks = {}; + + pp_clks.num_levels = DM_PP_MAX_CLOCK_LEVELS + 1; + + pp_to_dc_clock_levels_with_voltage(&pp_clks, &dc_clks, + DM_PP_CLOCK_TYPE_MEMORY_CLK); + + KUNIT_EXPECT_EQ(test, dc_clks.num_levels, (uint32_t)DM_PP_MAX_CLOCK_LEVELS); +} + +/* ---- Tests for dm_pp_get_funcs ---- */ + +/** + * dm_test_get_funcs_rv - Test Raven PP SMU function table setup + * @test: KUnit test context + * + * Verify that DCN 1.0 initializes the Raven SMU function table and stores + * the DC context in the PP SMU handle. + */ +static void dm_test_get_funcs_rv(struct kunit *test) +{ + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_NOT_NULL(test, funcs); + + ctx->dce_version = DCN_VERSION_1_0; + + dm_pp_get_funcs(ctx, funcs); + + KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_VER_RV); + KUNIT_EXPECT_PTR_EQ(test, funcs->rv_funcs.pp_smu.dm, ctx); + KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_wm_ranges != NULL); + KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_pme_wa_enable != NULL); + KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_display_count != NULL); + KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_min_deep_sleep_dcfclk != NULL); + KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_hard_min_dcfclk_by_freq != NULL); + KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_hard_min_fclk_by_freq != NULL); + KUNIT_EXPECT_FALSE(test, funcs->rv_funcs.set_hard_min_socclk_by_freq != NULL); +} + +/** + * dm_test_get_funcs_rv_101 - Test DCN 1.01 Raven PP SMU setup + * @test: KUnit test context + * + * Verify that DCN 1.01 uses the same Raven SMU function table as DCN 1.0. + */ +static void dm_test_get_funcs_rv_101(struct kunit *test) +{ + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_NOT_NULL(test, funcs); + + ctx->dce_version = DCN_VERSION_1_01; + + dm_pp_get_funcs(ctx, funcs); + + KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_VER_RV); + KUNIT_EXPECT_PTR_EQ(test, funcs->rv_funcs.pp_smu.dm, ctx); + KUNIT_EXPECT_TRUE(test, funcs->rv_funcs.set_display_count != NULL); +} + +/** + * dm_test_get_funcs_nv - Test Navi PP SMU function table setup + * @test: KUnit test context + * + * Verify that DCN 2.0 initializes the Navi SMU function table and leaves the + * unsupported PME workaround callback unset. + */ +static void dm_test_get_funcs_nv(struct kunit *test) +{ + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_NOT_NULL(test, funcs); + + ctx->dce_version = DCN_VERSION_2_0; + + dm_pp_get_funcs(ctx, funcs); + + KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_VER_NV); + KUNIT_EXPECT_PTR_EQ(test, funcs->nv_funcs.pp_smu.dm, ctx); + KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_display_count != NULL); + KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_hard_min_dcfclk_by_freq != NULL); + KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_min_deep_sleep_dcfclk != NULL); + KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_voltage_by_freq != NULL); + KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_wm_ranges != NULL); + KUNIT_EXPECT_FALSE(test, funcs->nv_funcs.set_pme_wa_enable != NULL); + KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_hard_min_uclk_by_freq != NULL); + KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.get_maximum_sustainable_clocks != NULL); + KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.get_uclk_dpm_states != NULL); + KUNIT_EXPECT_TRUE(test, funcs->nv_funcs.set_pstate_handshake_support != NULL); +} + +/** + * dm_test_get_funcs_rn - Test Renoir PP SMU function table setup + * @test: KUnit test context + * + * Verify that DCN 2.1 initializes the Renoir SMU function table. + */ +static void dm_test_get_funcs_rn(struct kunit *test) +{ + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_NOT_NULL(test, funcs); + + ctx->dce_version = DCN_VERSION_2_1; + + dm_pp_get_funcs(ctx, funcs); + + KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_VER_RN); + KUNIT_EXPECT_PTR_EQ(test, funcs->rn_funcs.pp_smu.dm, ctx); + KUNIT_EXPECT_TRUE(test, funcs->rn_funcs.set_wm_ranges != NULL); + KUNIT_EXPECT_TRUE(test, funcs->rn_funcs.get_dpm_clock_table != NULL); +} + +/** + * dm_test_get_funcs_unsupported - Test unsupported DCE version handling + * @test: KUnit test context + * + * Verify that unsupported DCE versions do not initialize a PP SMU version or + * function table callbacks. + */ +static void dm_test_get_funcs_unsupported(struct kunit *test) +{ + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu_funcs *funcs = kunit_kzalloc(test, sizeof(*funcs), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, ctx); + KUNIT_ASSERT_NOT_NULL(test, funcs); + + ctx->dce_version = DCE_VERSION_MAX; + + dm_pp_get_funcs(ctx, funcs); + + KUNIT_EXPECT_EQ(test, funcs->ctx.ver, PP_SMU_UNSUPPORTED); + KUNIT_EXPECT_FALSE(test, funcs->rv_funcs.set_wm_ranges != NULL); +} + +/* ---- Tests for amdgpu_device-backed entry points ---- */ + +/** + * dm_test_apply_display_requirements_dpm_disabled - Test DPM-disabled path + * @test: KUnit test context + * + * Verify that dm_pp_apply_display_requirements returns true without touching + * the display configuration when DPM is disabled. + */ +static void dm_test_apply_display_requirements_dpm_disabled(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_display_configuration cfg = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + adev->pm.dpm_enabled = false; + ctx->driver_context = adev; + + KUNIT_EXPECT_TRUE(test, dm_pp_apply_display_requirements(ctx, &cfg)); +} + +/** + * dm_test_apply_clock_for_voltage_invalid_type - Test invalid clock type path + * @test: KUnit test context + * + * Verify that dm_pp_apply_clock_for_voltage_request returns false for a clock + * type that does not map to a valid PP clock type, taking the early-return + * path before any SMU request is issued. + */ +static void dm_test_apply_clock_for_voltage_invalid_type(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_for_voltage_req req = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + ctx->driver_context = adev; + req.clk_type = (enum dm_pp_clock_type)0xffff; + req.clocks_in_khz = 500000; + + KUNIT_EXPECT_FALSE(test, dm_pp_apply_clock_for_voltage_request(ctx, &req)); +} + +/* ---- Tests for build_pm_display_cfg ---- */ + +/** + * dm_test_build_pm_display_cfg_scalar_fields - Test scalar field translation + * @test: KUnit test context + * + * Verify that build_pm_display_cfg copies the pass-through fields and applies + * the /10 (10 kHz) scaling, and sets the fixed constants. + */ +static void dm_test_build_pm_display_cfg_scalar_fields(struct kunit *test) +{ + struct amd_pp_display_configuration *pm = + kunit_kzalloc(test, sizeof(*pm), GFP_KERNEL); + struct dm_pp_display_configuration *pp = + kunit_kzalloc(test, sizeof(*pp), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, pm); + KUNIT_ASSERT_NOT_NULL(test, pp); + + pp->cpu_cc6_disable = true; + pp->cpu_pstate_disable = true; + pp->cpu_pstate_separation_time = 7; + pp->nb_pstate_switch_disable = true; + pp->display_count = 2; + pp->min_engine_clock_khz = 300000; + pp->min_engine_clock_deep_sleep_khz = 50000; + pp->min_memory_clock_khz = 800000; + pp->min_dcfclock_khz = 600000; + pp->all_displays_in_sync = true; + pp->avail_mclk_switch_time_us = 11; + pp->disp_clk_khz = 400000; + pp->avail_mclk_switch_time_in_disp_active_us = 13; + pp->crtc_index = 3; + pp->line_time_in_us = 17; + pp->disp_configs[0].v_refresh = 60; + + build_pm_display_cfg(pm, pp); + + KUNIT_EXPECT_TRUE(test, pm->cpu_cc6_disable); + KUNIT_EXPECT_TRUE(test, pm->cpu_pstate_disable); + KUNIT_EXPECT_EQ(test, pm->cpu_pstate_separation_time, 7); + KUNIT_EXPECT_TRUE(test, pm->nb_pstate_switch_disable); + KUNIT_EXPECT_EQ(test, pm->num_display, 2); + KUNIT_EXPECT_EQ(test, pm->num_path_including_non_display, 2); + KUNIT_EXPECT_EQ(test, pm->min_core_set_clock, 30000); + KUNIT_EXPECT_EQ(test, pm->min_core_set_clock_in_sr, 5000); + KUNIT_EXPECT_EQ(test, pm->min_mem_set_clock, 80000); + KUNIT_EXPECT_EQ(test, pm->min_dcef_deep_sleep_set_clk, 5000); + KUNIT_EXPECT_EQ(test, pm->min_dcef_set_clk, 60000); + KUNIT_EXPECT_TRUE(test, pm->multi_monitor_in_sync); + KUNIT_EXPECT_EQ(test, pm->min_vblank_time, 11); + KUNIT_EXPECT_EQ(test, pm->display_clk, 40000); + KUNIT_EXPECT_EQ(test, pm->dce_tolerable_mclk_in_active_latency, 13); + KUNIT_EXPECT_EQ(test, pm->crtc_index, 3); + KUNIT_EXPECT_EQ(test, pm->line_time_in_us, 17); + KUNIT_EXPECT_EQ(test, pm->vrefresh, 60); + KUNIT_EXPECT_EQ(test, pm->crossfire_display_index, -1); + KUNIT_EXPECT_EQ(test, pm->min_bus_bandwidth, 0); +} + +/** + * dm_test_build_pm_display_cfg_per_display - Test per-display translation + * @test: KUnit test context + * + * Verify that build_pm_display_cfg maps each display config, applying the + * controller_id = pipe_idx + 1 offset and copying the pixel clock. + */ +static void dm_test_build_pm_display_cfg_per_display(struct kunit *test) +{ + struct amd_pp_display_configuration *pm = + kunit_kzalloc(test, sizeof(*pm), GFP_KERNEL); + struct dm_pp_display_configuration *pp = + kunit_kzalloc(test, sizeof(*pp), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, pm); + KUNIT_ASSERT_NOT_NULL(test, pp); + + pp->display_count = 2; + pp->disp_configs[0].pipe_idx = 0; + pp->disp_configs[0].pixel_clock = 148500; + pp->disp_configs[1].pipe_idx = 4; + pp->disp_configs[1].pixel_clock = 297000; + + build_pm_display_cfg(pm, pp); + + KUNIT_EXPECT_EQ(test, pm->displays[0].controller_id, 1); + KUNIT_EXPECT_EQ(test, pm->displays[0].pixel_clock, 148500); + KUNIT_EXPECT_EQ(test, pm->displays[1].controller_id, 5); + KUNIT_EXPECT_EQ(test, pm->displays[1].pixel_clock, 297000); +} + +/* ---- Tests for build_wm_clock_ranges_soc15 ---- */ + +/** + * dm_test_build_wm_clock_ranges_dmif - Test reader (DMIF) watermark sets + * @test: KUnit test context + * + * Verify that build_wm_clock_ranges_soc15 copies the reader set count, + * maps wm_inst to wm_set_id (clamping instances > 3 to WM_SET_A), and + * converts every clock from MHz to kHz (x1000) into the DMIF clock ranges. + */ +static void dm_test_build_wm_clock_ranges_dmif(struct kunit *test) +{ + struct pp_smu_wm_range_sets *ranges = + kunit_kzalloc(test, sizeof(*ranges), GFP_KERNEL); + struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm = + kunit_kzalloc(test, sizeof(*wm), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, ranges); + KUNIT_ASSERT_NOT_NULL(test, wm); + + ranges->num_reader_wm_sets = 2; + /* set 0: wm_inst within range -> preserved */ + ranges->reader_wm_sets[0].wm_inst = 2; + ranges->reader_wm_sets[0].max_drain_clk_mhz = 600; + ranges->reader_wm_sets[0].min_drain_clk_mhz = 300; + ranges->reader_wm_sets[0].max_fill_clk_mhz = 800; + ranges->reader_wm_sets[0].min_fill_clk_mhz = 400; + /* set 1: wm_inst > 3 -> clamped to WM_SET_A */ + ranges->reader_wm_sets[1].wm_inst = 5; + ranges->reader_wm_sets[1].max_drain_clk_mhz = 700; + ranges->reader_wm_sets[1].min_drain_clk_mhz = 350; + ranges->reader_wm_sets[1].max_fill_clk_mhz = 900; + ranges->reader_wm_sets[1].min_fill_clk_mhz = 450; + + build_wm_clock_ranges_soc15(ranges, wm); + + KUNIT_EXPECT_EQ(test, wm->num_wm_dmif_sets, 2U); + KUNIT_EXPECT_EQ(test, wm->num_wm_mcif_sets, 0U); + + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_set_id, WM_SET_C); + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_max_dcfclk_clk_in_khz, 600000U); + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_min_dcfclk_clk_in_khz, 300000U); + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_max_mem_clk_in_khz, 800000U); + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[0].wm_min_mem_clk_in_khz, 400000U); + + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_set_id, WM_SET_A); + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_max_dcfclk_clk_in_khz, 700000U); + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_min_dcfclk_clk_in_khz, 350000U); + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_max_mem_clk_in_khz, 900000U); + KUNIT_EXPECT_EQ(test, wm->wm_dmif_clocks_ranges[1].wm_min_mem_clk_in_khz, 450000U); +} + +/** + * dm_test_build_wm_clock_ranges_mcif - Test writer (MCIF) watermark sets + * @test: KUnit test context + * + * Verify that build_wm_clock_ranges_soc15 copies the writer set count and + * maps the writer clocks into the MCIF ranges: fill clocks become socclk + * and drain clocks become mem clk, each converted from MHz to kHz. + */ +static void dm_test_build_wm_clock_ranges_mcif(struct kunit *test) +{ + struct pp_smu_wm_range_sets *ranges = + kunit_kzalloc(test, sizeof(*ranges), GFP_KERNEL); + struct dm_pp_wm_sets_with_clock_ranges_soc15 *wm = + kunit_kzalloc(test, sizeof(*wm), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, ranges); + KUNIT_ASSERT_NOT_NULL(test, wm); + + ranges->num_writer_wm_sets = 2; + ranges->writer_wm_sets[0].wm_inst = 1; + ranges->writer_wm_sets[0].max_fill_clk_mhz = 1200; + ranges->writer_wm_sets[0].min_fill_clk_mhz = 600; + ranges->writer_wm_sets[0].max_drain_clk_mhz = 1000; + ranges->writer_wm_sets[0].min_drain_clk_mhz = 500; + /* set 1: wm_inst > 3 -> clamped to WM_SET_A */ + ranges->writer_wm_sets[1].wm_inst = 5; + ranges->writer_wm_sets[1].max_fill_clk_mhz = 1400; + ranges->writer_wm_sets[1].min_fill_clk_mhz = 700; + ranges->writer_wm_sets[1].max_drain_clk_mhz = 1100; + ranges->writer_wm_sets[1].min_drain_clk_mhz = 550; + + build_wm_clock_ranges_soc15(ranges, wm); + + KUNIT_EXPECT_EQ(test, wm->num_wm_dmif_sets, 0U); + KUNIT_EXPECT_EQ(test, wm->num_wm_mcif_sets, 2U); + + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_set_id, WM_SET_B); + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_max_socclk_clk_in_khz, 1200000U); + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_min_socclk_clk_in_khz, 600000U); + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_max_mem_clk_in_khz, 1000000U); + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[0].wm_min_mem_clk_in_khz, 500000U); + + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_set_id, WM_SET_A); + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_max_socclk_clk_in_khz, 1400000U); + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_min_socclk_clk_in_khz, 700000U); + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_max_mem_clk_in_khz, 1100000U); + KUNIT_EXPECT_EQ(test, wm->wm_mcif_clocks_ranges[1].wm_min_mem_clk_in_khz, 550000U); +} + +/* ---- Tests for cap_clock_levels_to_validation ---- */ + +/** + * dm_test_cap_clock_levels_engine_caps - Test engine clock level capping + * @test: KUnit test context + * + * Verify that for engine clocks, num_levels is reduced to the index of the + * first level whose frequency exceeds the engine validation clock. + */ +static void dm_test_cap_clock_levels_engine_caps(struct kunit *test) +{ + struct dm_pp_clock_levels clks = { 0 }; + struct amd_pp_simple_clock_info validation = { + .engine_max_clock = 450000, + .memory_max_clock = 800000, + }; + + clks.num_levels = 3; + clks.clocks_in_khz[0] = 300000; + clks.clocks_in_khz[1] = 400000; + clks.clocks_in_khz[2] = 500000; + + cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_ENGINE_CLK, &validation); + + KUNIT_EXPECT_EQ(test, clks.num_levels, 2U); +} + +/** + * dm_test_cap_clock_levels_engine_first_exceeds - Test floor of one level + * @test: KUnit test context + * + * Verify that when the very first engine clock level already exceeds the + * validation clock, num_levels is clamped to 1 rather than 0. + */ +static void dm_test_cap_clock_levels_engine_first_exceeds(struct kunit *test) +{ + struct dm_pp_clock_levels clks = { 0 }; + struct amd_pp_simple_clock_info validation = { + .engine_max_clock = 100000, + .memory_max_clock = 800000, + }; + + clks.num_levels = 3; + clks.clocks_in_khz[0] = 300000; + clks.clocks_in_khz[1] = 400000; + clks.clocks_in_khz[2] = 500000; + + cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_ENGINE_CLK, &validation); + + KUNIT_EXPECT_EQ(test, clks.num_levels, 1U); +} + +/** + * dm_test_cap_clock_levels_memory_caps - Test memory clock level capping + * @test: KUnit test context + * + * Verify that for memory clocks, num_levels is reduced based on the memory + * validation clock (and is unaffected by the engine validation clock). + */ +static void dm_test_cap_clock_levels_memory_caps(struct kunit *test) +{ + struct dm_pp_clock_levels clks = { 0 }; + struct amd_pp_simple_clock_info validation = { + .engine_max_clock = 100000, + .memory_max_clock = 700000, + }; + + clks.num_levels = 2; + clks.clocks_in_khz[0] = 333000; + clks.clocks_in_khz[1] = 800000; + + cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_MEMORY_CLK, &validation); + + KUNIT_EXPECT_EQ(test, clks.num_levels, 1U); +} + +/** + * dm_test_cap_clock_levels_within_limit - Test no capping when within limit + * @test: KUnit test context + * + * Verify that num_levels is left unchanged when no level exceeds the + * validation clock. + */ +static void dm_test_cap_clock_levels_within_limit(struct kunit *test) +{ + struct dm_pp_clock_levels clks = { 0 }; + struct amd_pp_simple_clock_info validation = { + .engine_max_clock = 999000, + .memory_max_clock = 999000, + }; + + clks.num_levels = 3; + clks.clocks_in_khz[0] = 300000; + clks.clocks_in_khz[1] = 400000; + clks.clocks_in_khz[2] = 500000; + + cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_ENGINE_CLK, &validation); + + KUNIT_EXPECT_EQ(test, clks.num_levels, 3U); +} + +/** + * dm_test_cap_clock_levels_other_type - Test non-engine/memory types ignored + * @test: KUnit test context + * + * Verify that for clock types other than engine or memory, num_levels is + * left unchanged regardless of the validation clocks. + */ +static void dm_test_cap_clock_levels_other_type(struct kunit *test) +{ + struct dm_pp_clock_levels clks = { 0 }; + struct amd_pp_simple_clock_info validation = { + .engine_max_clock = 1, + .memory_max_clock = 1, + }; + + clks.num_levels = 3; + clks.clocks_in_khz[0] = 300000; + clks.clocks_in_khz[1] = 400000; + clks.clocks_in_khz[2] = 500000; + + cap_clock_levels_to_validation(&clks, DM_PP_CLOCK_TYPE_DISPLAY_CLK, &validation); + + KUNIT_EXPECT_EQ(test, clks.num_levels, 3U); +} + +/* ---- Tests for pp_smu_nv_clock_id_to_pp ---- */ + +/** + * dm_test_nv_clock_id_dispclk - Test DISPCLK id mapping + * @test: KUnit test context + * + * Verify that PP_SMU_NV_DISPCLK maps to amd_pp_disp_clock and returns true. + */ +static void dm_test_nv_clock_id_dispclk(struct kunit *test) +{ + enum amd_pp_clock_type clock_type = amd_pp_mem_clock; + + KUNIT_EXPECT_TRUE(test, pp_smu_nv_clock_id_to_pp(PP_SMU_NV_DISPCLK, &clock_type)); + KUNIT_EXPECT_EQ(test, clock_type, amd_pp_disp_clock); +} + +/** + * dm_test_nv_clock_id_phyclk - Test PHYCLK id mapping + * @test: KUnit test context + * + * Verify that PP_SMU_NV_PHYCLK maps to amd_pp_phy_clock and returns true. + */ +static void dm_test_nv_clock_id_phyclk(struct kunit *test) +{ + enum amd_pp_clock_type clock_type = amd_pp_mem_clock; + + KUNIT_EXPECT_TRUE(test, pp_smu_nv_clock_id_to_pp(PP_SMU_NV_PHYCLK, &clock_type)); + KUNIT_EXPECT_EQ(test, clock_type, amd_pp_phy_clock); +} + +/** + * dm_test_nv_clock_id_pixelclk - Test PIXELCLK id mapping + * @test: KUnit test context + * + * Verify that PP_SMU_NV_PIXELCLK maps to amd_pp_pixel_clock and returns true. + */ +static void dm_test_nv_clock_id_pixelclk(struct kunit *test) +{ + enum amd_pp_clock_type clock_type = amd_pp_mem_clock; + + KUNIT_EXPECT_TRUE(test, pp_smu_nv_clock_id_to_pp(PP_SMU_NV_PIXELCLK, &clock_type)); + KUNIT_EXPECT_EQ(test, clock_type, amd_pp_pixel_clock); +} + +/** + * dm_test_nv_clock_id_invalid - Test unknown id is rejected + * @test: KUnit test context + * + * Verify that an unknown clock id returns false and leaves the output + * clock_type untouched, guarding against the previously uninitialized path. + */ +static void dm_test_nv_clock_id_invalid(struct kunit *test) +{ + enum amd_pp_clock_type clock_type = amd_pp_dcef_clock; + + KUNIT_EXPECT_FALSE(test, pp_smu_nv_clock_id_to_pp((enum pp_smu_nv_clock_id)0xff, + &clock_type)); + KUNIT_EXPECT_EQ(test, clock_type, amd_pp_dcef_clock); +} + +/* ---- Tests using stub DPM layer ---- */ + +/** + * dm_test_apply_display_requirements_dpm_enabled - Test DPM-enabled path + * @test: KUnit test context + * + * Verify that dm_pp_apply_display_requirements calls build_pm_display_cfg + * and the DPM callbacks when DPM is enabled, and returns true. + */ +static void dm_test_apply_display_requirements_dpm_enabled(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_display_configuration cfg = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + adev->pm.dpm_enabled = true; + + cfg.display_count = 1; + cfg.min_engine_clock_khz = 300000; + cfg.disp_configs[0].v_refresh = 60; + + KUNIT_EXPECT_TRUE(test, dm_pp_apply_display_requirements(ctx, &cfg)); + KUNIT_EXPECT_EQ(test, adev->pm.pm_display_cfg.min_core_set_clock, 30000); + KUNIT_EXPECT_EQ(test, adev->pm.pm_display_cfg.vrefresh, 60); +} + +/** + * dm_test_get_clock_levels_by_type_dpm_error - Test DPM error fallback + * @test: KUnit test context + * + * Verify that dm_pp_get_clock_levels_by_type falls back to default clock + * levels when amdgpu_dpm_get_clock_by_type returns an error. + */ +static void dm_test_get_clock_levels_by_type_dpm_error(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_levels dc_clks = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + stub_dpm_ctx->ret_val = -EINVAL; + + KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type(ctx, + DM_PP_CLOCK_TYPE_DISPLAY_CLK, &dc_clks)); + KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 6U); + KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[0], 300000U); +} + +/** + * dm_test_get_clock_levels_by_type_success - Test successful clock query + * @test: KUnit test context + * + * Verify that dm_pp_get_clock_levels_by_type returns the queried clocks + * capped by validation clocks. + */ +static void dm_test_get_clock_levels_by_type_success(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_levels dc_clks = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + + stub_dpm_ctx->get_clock_by_type_clocks.count = 3; + stub_dpm_ctx->get_clock_by_type_clocks.clock[0] = 300000; + stub_dpm_ctx->get_clock_by_type_clocks.clock[1] = 500000; + stub_dpm_ctx->get_clock_by_type_clocks.clock[2] = 700000; + + /* validation at 60000 * 10 = 600000 kHz → caps to 2 levels */ + stub_dpm_ctx->get_validation_clks.engine_max_clock = 60000; + stub_dpm_ctx->get_validation_clks.memory_max_clock = 80000; + + KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type(ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, &dc_clks)); + KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 2U); + KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[0], 300000U); + KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[1], 500000U); +} + +/** + * dm_test_get_clock_levels_by_type_validation_fallback - Test validation error + * @test: KUnit test context + * + * Verify that dm_pp_get_clock_levels_by_type uses default validation clocks + * (engine=720000, memory=800000 kHz) when get_display_mode_validation_clocks + * returns an error, capping levels accordingly. + */ +static void dm_test_get_clock_levels_by_type_validation_fallback(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_levels dc_clks = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + + /* get_clock_by_type succeeds with 3 engine clock levels */ + stub_dpm_ctx->get_clock_by_type_clocks.count = 3; + stub_dpm_ctx->get_clock_by_type_clocks.clock[0] = 300000; + stub_dpm_ctx->get_clock_by_type_clocks.clock[1] = 500000; + stub_dpm_ctx->get_clock_by_type_clocks.clock[2] = 800000; + + /* Force validation clocks to fail → triggers default path */ + stub_dpm_ctx->get_validation_clks_ret = -EINVAL; + + KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type(ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, &dc_clks)); + /* + * Default validation: engine_max_clock = 72000 * 10 = 720000 kHz. + * Clocks 300000 and 500000 are within limit, 800000 exceeds it, + * so num_levels is capped to 2. + */ + KUNIT_EXPECT_EQ(test, dc_clks.num_levels, 2U); + KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[0], 300000U); + KUNIT_EXPECT_EQ(test, dc_clks.clocks_in_khz[1], 500000U); +} + +/** + * dm_test_get_clock_levels_with_latency_success - Test latency clock query + * @test: KUnit test context + * + * Verify dm_pp_get_clock_levels_by_type_with_latency returns true and + * copies the clock/latency data from the DPM backend. + */ +static void dm_test_get_clock_levels_with_latency_success(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_levels_with_latency info = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + + stub_dpm_ctx->get_clock_by_type_with_latency_clks.num_levels = 1; + stub_dpm_ctx->get_clock_by_type_with_latency_clks.data[0].clocks_in_khz = 600000; + stub_dpm_ctx->get_clock_by_type_with_latency_clks.data[0].latency_in_us = 15; + + KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type_with_latency(ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, &info)); + KUNIT_EXPECT_EQ(test, info.num_levels, 1U); + KUNIT_EXPECT_EQ(test, info.data[0].clocks_in_khz, 600000U); + KUNIT_EXPECT_EQ(test, info.data[0].latency_in_us, 15U); +} + +/** + * dm_test_get_clock_levels_with_latency_failure - Test latency query error + * @test: KUnit test context + * + * Verify dm_pp_get_clock_levels_by_type_with_latency returns false on DPM error. + */ +static void dm_test_get_clock_levels_with_latency_failure(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_levels_with_latency info = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + stub_dpm_ctx->ret_val = -EINVAL; + + KUNIT_EXPECT_FALSE(test, dm_pp_get_clock_levels_by_type_with_latency(ctx, + DM_PP_CLOCK_TYPE_ENGINE_CLK, &info)); +} + +/** + * dm_test_get_clock_levels_with_voltage_success - Test voltage clock query + * @test: KUnit test context + * + * Verify dm_pp_get_clock_levels_by_type_with_voltage returns true and + * copies the clock/voltage data from the DPM backend. + */ +static void dm_test_get_clock_levels_with_voltage_success(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_levels_with_voltage info = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + + stub_dpm_ctx->get_clock_by_type_with_voltage_clks.num_levels = 1; + stub_dpm_ctx->get_clock_by_type_with_voltage_clks.data[0].clocks_in_khz = 400000; + stub_dpm_ctx->get_clock_by_type_with_voltage_clks.data[0].voltage_in_mv = 900; + + KUNIT_EXPECT_TRUE(test, dm_pp_get_clock_levels_by_type_with_voltage(ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, &info)); + KUNIT_EXPECT_EQ(test, info.num_levels, 1U); + KUNIT_EXPECT_EQ(test, info.data[0].clocks_in_khz, 400000U); + KUNIT_EXPECT_EQ(test, info.data[0].voltage_in_mv, 900U); +} + +/** + * dm_test_get_clock_levels_with_voltage_failure - Test voltage query error + * @test: KUnit test context + * + * Verify dm_pp_get_clock_levels_by_type_with_voltage returns false on DPM error. + */ +static void dm_test_get_clock_levels_with_voltage_failure(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_levels_with_voltage info = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + stub_dpm_ctx->ret_val = -EINVAL; + + KUNIT_EXPECT_FALSE(test, dm_pp_get_clock_levels_by_type_with_voltage(ctx, + DM_PP_CLOCK_TYPE_MEMORY_CLK, &info)); +} + +/** + * dm_test_notify_wm_clock_changes_polaris - Test Polaris watermark path + * @test: KUnit test context + * + * Verify dm_pp_notify_wm_clock_changes returns true for Polaris ASICs + * when the DPM set_watermarks call succeeds. + */ +static void dm_test_notify_wm_clock_changes_polaris(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_wm_sets_with_clock_ranges wm = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + adev->asic_type = CHIP_POLARIS10; + stub_dpm_ctx->set_watermarks_ret = 0; + + KUNIT_EXPECT_TRUE(test, dm_pp_notify_wm_clock_changes(ctx, &wm)); +} + +/** + * dm_test_notify_wm_clock_changes_non_polaris - Test non-Polaris path + * @test: KUnit test context + * + * Verify dm_pp_notify_wm_clock_changes returns false for non-Polaris ASICs. + */ +static void dm_test_notify_wm_clock_changes_non_polaris(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_wm_sets_with_clock_ranges wm = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + adev->asic_type = CHIP_NAVI10; + + KUNIT_EXPECT_FALSE(test, dm_pp_notify_wm_clock_changes(ctx, &wm)); +} + +/** + * dm_test_apply_clock_for_voltage_success - Test successful voltage request + * @test: KUnit test context + * + * Verify dm_pp_apply_clock_for_voltage_request returns true when the DPM + * callback succeeds for a valid clock type. + */ +static void dm_test_apply_clock_for_voltage_success(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_for_voltage_req req = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + stub_dpm_ctx->display_clock_voltage_ret = 0; + + req.clk_type = DM_PP_CLOCK_TYPE_ENGINE_CLK; + req.clocks_in_khz = 500000; + + KUNIT_EXPECT_TRUE(test, dm_pp_apply_clock_for_voltage_request(ctx, &req)); +} + +/** + * dm_test_apply_clock_for_voltage_eopnotsupp - Test EOPNOTSUPP treated as success + * @test: KUnit test context + * + * Verify dm_pp_apply_clock_for_voltage_request returns true when the DPM + * callback returns -EOPNOTSUPP (not supported is non-fatal). + */ +static void dm_test_apply_clock_for_voltage_eopnotsupp(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_for_voltage_req req = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + stub_dpm_ctx->display_clock_voltage_ret = -EOPNOTSUPP; + + req.clk_type = DM_PP_CLOCK_TYPE_ENGINE_CLK; + req.clocks_in_khz = 500000; + + KUNIT_EXPECT_TRUE(test, dm_pp_apply_clock_for_voltage_request(ctx, &req)); +} + +/** + * dm_test_apply_clock_for_voltage_fail - Test DPM error returns false + * @test: KUnit test context + * + * Verify dm_pp_apply_clock_for_voltage_request returns false when the DPM + * callback fails with an error other than -EOPNOTSUPP. + */ +static void dm_test_apply_clock_for_voltage_fail(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct dm_pp_clock_for_voltage_req req = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + stub_dpm_ctx->display_clock_voltage_ret = -EIO; + + req.clk_type = DM_PP_CLOCK_TYPE_ENGINE_CLK; + req.clocks_in_khz = 500000; + + KUNIT_EXPECT_FALSE(test, dm_pp_apply_clock_for_voltage_request(ctx, &req)); +} + +/* ---- Tests for pp_nv_set_display_count ---- */ + +/** + * dm_test_nv_set_display_count_ok - Test successful display count set + * @test: KUnit test context + * + * Verify pp_nv_set_display_count returns PP_SMU_RESULT_OK on success. + */ +static void dm_test_nv_set_display_count_ok(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->set_active_display_count_ret = 0; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_display_count(&pp_smu, 2), + (int)PP_SMU_RESULT_OK); +} + +/** + * dm_test_nv_set_display_count_unsupported - Test EOPNOTSUPP mapping + * @test: KUnit test context + * + * Verify pp_nv_set_display_count returns PP_SMU_RESULT_UNSUPPORTED when + * the DPM callback returns -EOPNOTSUPP. + */ +static void dm_test_nv_set_display_count_unsupported(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->set_active_display_count_ret = -EOPNOTSUPP; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_display_count(&pp_smu, 2), + (int)PP_SMU_RESULT_UNSUPPORTED); +} + +/** + * dm_test_nv_set_display_count_fail - Test generic error mapping + * @test: KUnit test context + * + * Verify pp_nv_set_display_count returns PP_SMU_RESULT_FAIL on a generic + * DPM error. + */ +static void dm_test_nv_set_display_count_fail(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->set_active_display_count_ret = -EIO; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_display_count(&pp_smu, 2), + (int)PP_SMU_RESULT_FAIL); +} + +/* ---- Tests for pp_nv_set_voltage_by_freq ---- */ + +/** + * dm_test_nv_set_voltage_by_freq_ok - Test successful voltage-by-freq + * @test: KUnit test context + * + * Verify pp_nv_set_voltage_by_freq returns PP_SMU_RESULT_OK on success. + */ +static void dm_test_nv_set_voltage_by_freq_ok(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->display_clock_voltage_ret = 0; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_voltage_by_freq(&pp_smu, PP_SMU_NV_DISPCLK, 600), + (int)PP_SMU_RESULT_OK); +} + +/** + * dm_test_nv_set_voltage_by_freq_invalid_id - Test invalid clock id + * @test: KUnit test context + * + * Verify pp_nv_set_voltage_by_freq returns PP_SMU_RESULT_FAIL for an + * unrecognized clock id without calling DPM. + */ +static void dm_test_nv_set_voltage_by_freq_invalid_id(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + + KUNIT_EXPECT_EQ(test, + (int)pp_nv_set_voltage_by_freq(&pp_smu, (enum pp_smu_nv_clock_id)0xff, 600), + (int)PP_SMU_RESULT_FAIL); +} + +/* ---- Tests for pp_nv_set_pstate_handshake_support ---- */ + +/** + * dm_test_nv_pstate_handshake_ok - Test successful pstate handshake + * @test: KUnit test context + * + * Verify pp_nv_set_pstate_handshake_support returns PP_SMU_RESULT_OK + * when the DPM callback succeeds. + */ +static void dm_test_nv_pstate_handshake_ok(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->display_disable_memory_clock_switch_ret = 0; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_pstate_handshake_support(&pp_smu, true), + (int)PP_SMU_RESULT_OK); +} + +/** + * dm_test_nv_pstate_handshake_fail - Test failed pstate handshake + * @test: KUnit test context + * + * Verify pp_nv_set_pstate_handshake_support returns PP_SMU_RESULT_FAIL + * when the DPM callback returns non-zero. + */ +static void dm_test_nv_pstate_handshake_fail(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->display_disable_memory_clock_switch_ret = -EIO; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_pstate_handshake_support(&pp_smu, true), + (int)PP_SMU_RESULT_FAIL); +} + +/* ---- Tests for pp_rn_get_dpm_clock_table ---- */ + +/** + * dm_test_rn_get_dpm_clock_table_ok - Test successful DPM clock table + * @test: KUnit test context + * + * Verify pp_rn_get_dpm_clock_table returns PP_SMU_RESULT_OK on success. + */ +static void dm_test_rn_get_dpm_clock_table_ok(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + struct dpm_clocks clock_table = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->get_dpm_clock_table_ret = 0; + + KUNIT_EXPECT_EQ(test, (int)pp_rn_get_dpm_clock_table(&pp_smu, &clock_table), + (int)PP_SMU_RESULT_OK); +} + +/** + * dm_test_rn_get_dpm_clock_table_unsupported - Test EOPNOTSUPP mapping + * @test: KUnit test context + * + * Verify pp_rn_get_dpm_clock_table returns PP_SMU_RESULT_UNSUPPORTED. + */ +static void dm_test_rn_get_dpm_clock_table_unsupported(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + struct dpm_clocks clock_table = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->get_dpm_clock_table_ret = -EOPNOTSUPP; + + KUNIT_EXPECT_EQ(test, (int)pp_rn_get_dpm_clock_table(&pp_smu, &clock_table), + (int)PP_SMU_RESULT_UNSUPPORTED); +} + +/** + * dm_test_rn_get_dpm_clock_table_fail - Test generic error mapping + * @test: KUnit test context + * + * Verify pp_rn_get_dpm_clock_table returns PP_SMU_RESULT_FAIL. + */ +static void dm_test_rn_get_dpm_clock_table_fail(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + struct dpm_clocks clock_table = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->get_dpm_clock_table_ret = -EIO; + + KUNIT_EXPECT_EQ(test, (int)pp_rn_get_dpm_clock_table(&pp_smu, &clock_table), + (int)PP_SMU_RESULT_FAIL); +} + +/* ---- Tests for pp_rv_set_wm_ranges ---- */ + +/** + * dm_test_rv_set_wm_ranges - Test Raven watermark range forwarding + * @test: KUnit test context + * + * Verify pp_rv_set_wm_ranges converts watermark ranges via + * build_wm_clock_ranges_soc15 and forwards them to DPM without crashing. + */ +static void dm_test_rv_set_wm_ranges(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + struct pp_smu_wm_range_sets ranges = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + + ranges.num_reader_wm_sets = 1; + ranges.reader_wm_sets[0].wm_inst = 0; + ranges.reader_wm_sets[0].max_drain_clk_mhz = 600; + ranges.reader_wm_sets[0].min_drain_clk_mhz = 300; + + pp_rv_set_wm_ranges(&pp_smu, &ranges); + + /* Reaching here without crash confirms coverage */ + KUNIT_SUCCEED(test); +} + +/* ---- Tests for pp_rv_set_pme_wa_enable ---- */ + +/** + * dm_test_rv_set_pme_wa_enable - Test Raven PME workaround enable + * @test: KUnit test context + * + * Verify pp_rv_set_pme_wa_enable forwards the call to DPM without crashing. + */ +static void dm_test_rv_set_pme_wa_enable(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + + pp_rv_set_pme_wa_enable(&pp_smu); + + KUNIT_SUCCEED(test); +} + +/* ---- Tests for pp_rv_set_active_display_count ---- */ + +/** + * dm_test_rv_set_active_display_count - Test Raven display count forwarding + * @test: KUnit test context + * + * Verify pp_rv_set_active_display_count forwards the count to DPM without + * crashing. + */ +static void dm_test_rv_set_active_display_count(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + + pp_rv_set_active_display_count(&pp_smu, 2); + + KUNIT_SUCCEED(test); +} + +/* ---- Tests for pp_rv_set_min_deep_sleep_dcfclk ---- */ + +/** + * dm_test_rv_set_min_deep_sleep_dcfclk - Test Raven deep sleep clock + * @test: KUnit test context + * + * Verify pp_rv_set_min_deep_sleep_dcfclk forwards the clock value to DPM + * without crashing. + */ +static void dm_test_rv_set_min_deep_sleep_dcfclk(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + + pp_rv_set_min_deep_sleep_dcfclk(&pp_smu, 300); + + KUNIT_SUCCEED(test); +} + +/* ---- Tests for pp_rv_set_hard_min_dcefclk_by_freq ---- */ + +/** + * dm_test_rv_set_hard_min_dcefclk_by_freq - Test Raven hard min DCEFCLK + * @test: KUnit test context + * + * Verify pp_rv_set_hard_min_dcefclk_by_freq forwards the frequency to DPM + * without crashing. + */ +static void dm_test_rv_set_hard_min_dcefclk_by_freq(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + + pp_rv_set_hard_min_dcefclk_by_freq(&pp_smu, 600); + + KUNIT_SUCCEED(test); +} + +/* ---- Tests for pp_rv_set_hard_min_fclk_by_freq ---- */ + +/** + * dm_test_rv_set_hard_min_fclk_by_freq - Test Raven hard min FCLK + * @test: KUnit test context + * + * Verify pp_rv_set_hard_min_fclk_by_freq forwards the frequency to DPM + * without crashing. + */ +static void dm_test_rv_set_hard_min_fclk_by_freq(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + + pp_rv_set_hard_min_fclk_by_freq(&pp_smu, 800); + + KUNIT_SUCCEED(test); +} + +/* ---- Tests for pp_nv_set_wm_ranges ---- */ + +/** + * dm_test_nv_set_wm_ranges - Test Navi watermark range forwarding + * @test: KUnit test context + * + * Verify pp_nv_set_wm_ranges forwards ranges to DPM and unconditionally + * returns PP_SMU_RESULT_OK. + */ +static void dm_test_nv_set_wm_ranges(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + struct pp_smu_wm_range_sets ranges = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + + ranges.num_reader_wm_sets = 1; + ranges.reader_wm_sets[0].wm_inst = 0; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_wm_ranges(&pp_smu, &ranges), + (int)PP_SMU_RESULT_OK); +} + +/* ---- Tests for pp_nv_set_min_deep_sleep_dcfclk ---- */ + +/** + * dm_test_nv_set_min_deep_sleep_dcfclk_ok - Test successful deep sleep set + * @test: KUnit test context + * + * Verify pp_nv_set_min_deep_sleep_dcfclk returns PP_SMU_RESULT_OK on success. + */ +static void dm_test_nv_set_min_deep_sleep_dcfclk_ok(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->set_min_deep_sleep_dcefclk_ret = 0; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_min_deep_sleep_dcfclk(&pp_smu, 300), + (int)PP_SMU_RESULT_OK); +} + +/** + * dm_test_nv_set_min_deep_sleep_dcfclk_unsupported - Test EOPNOTSUPP mapping + * @test: KUnit test context + * + * Verify pp_nv_set_min_deep_sleep_dcfclk returns PP_SMU_RESULT_UNSUPPORTED. + */ +static void dm_test_nv_set_min_deep_sleep_dcfclk_unsupported(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->set_min_deep_sleep_dcefclk_ret = -EOPNOTSUPP; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_min_deep_sleep_dcfclk(&pp_smu, 300), + (int)PP_SMU_RESULT_UNSUPPORTED); +} + +/** + * dm_test_nv_set_min_deep_sleep_dcfclk_fail - Test generic error mapping + * @test: KUnit test context + * + * Verify pp_nv_set_min_deep_sleep_dcfclk returns PP_SMU_RESULT_FAIL. + */ +static void dm_test_nv_set_min_deep_sleep_dcfclk_fail(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->set_min_deep_sleep_dcefclk_ret = -EIO; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_min_deep_sleep_dcfclk(&pp_smu, 300), + (int)PP_SMU_RESULT_FAIL); +} + +/* ---- Tests for pp_nv_set_hard_min_dcefclk_by_freq ---- */ + +/** + * dm_test_nv_set_hard_min_dcefclk_ok - Test successful hard min DCEFCLK + * @test: KUnit test context + * + * Verify pp_nv_set_hard_min_dcefclk_by_freq returns PP_SMU_RESULT_OK. + */ +static void dm_test_nv_set_hard_min_dcefclk_ok(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->display_clock_voltage_ret = 0; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_dcefclk_by_freq(&pp_smu, 600), + (int)PP_SMU_RESULT_OK); +} + +/** + * dm_test_nv_set_hard_min_dcefclk_unsupported - Test EOPNOTSUPP mapping + * @test: KUnit test context + * + * Verify pp_nv_set_hard_min_dcefclk_by_freq returns PP_SMU_RESULT_UNSUPPORTED. + */ +static void dm_test_nv_set_hard_min_dcefclk_unsupported(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->display_clock_voltage_ret = -EOPNOTSUPP; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_dcefclk_by_freq(&pp_smu, 600), + (int)PP_SMU_RESULT_UNSUPPORTED); +} + +/** + * dm_test_nv_set_hard_min_dcefclk_fail - Test generic error mapping + * @test: KUnit test context + * + * Verify pp_nv_set_hard_min_dcefclk_by_freq returns PP_SMU_RESULT_FAIL. + */ +static void dm_test_nv_set_hard_min_dcefclk_fail(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->display_clock_voltage_ret = -EIO; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_dcefclk_by_freq(&pp_smu, 600), + (int)PP_SMU_RESULT_FAIL); +} + +/* ---- Tests for pp_nv_set_hard_min_uclk_by_freq ---- */ + +/** + * dm_test_nv_set_hard_min_uclk_ok - Test successful hard min UCLK + * @test: KUnit test context + * + * Verify pp_nv_set_hard_min_uclk_by_freq returns PP_SMU_RESULT_OK. + */ +static void dm_test_nv_set_hard_min_uclk_ok(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->display_clock_voltage_ret = 0; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_uclk_by_freq(&pp_smu, 800), + (int)PP_SMU_RESULT_OK); +} + +/** + * dm_test_nv_set_hard_min_uclk_unsupported - Test EOPNOTSUPP mapping + * @test: KUnit test context + * + * Verify pp_nv_set_hard_min_uclk_by_freq returns PP_SMU_RESULT_UNSUPPORTED. + */ +static void dm_test_nv_set_hard_min_uclk_unsupported(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->display_clock_voltage_ret = -EOPNOTSUPP; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_uclk_by_freq(&pp_smu, 800), + (int)PP_SMU_RESULT_UNSUPPORTED); +} + +/** + * dm_test_nv_set_hard_min_uclk_fail - Test generic error mapping + * @test: KUnit test context + * + * Verify pp_nv_set_hard_min_uclk_by_freq returns PP_SMU_RESULT_FAIL. + */ +static void dm_test_nv_set_hard_min_uclk_fail(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->display_clock_voltage_ret = -EIO; + + KUNIT_EXPECT_EQ(test, (int)pp_nv_set_hard_min_uclk_by_freq(&pp_smu, 800), + (int)PP_SMU_RESULT_FAIL); +} + +/* ---- Tests for pp_nv_get_maximum_sustainable_clocks ---- */ + +/** + * dm_test_nv_get_max_sustainable_clocks_ok - Test successful query + * @test: KUnit test context + * + * Verify pp_nv_get_maximum_sustainable_clocks returns PP_SMU_RESULT_OK. + */ +static void dm_test_nv_get_max_sustainable_clocks_ok(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + struct pp_smu_nv_clock_table max_clocks = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->get_max_sustainable_ret = 0; + + KUNIT_EXPECT_EQ(test, + (int)pp_nv_get_maximum_sustainable_clocks(&pp_smu, &max_clocks), + (int)PP_SMU_RESULT_OK); +} + +/** + * dm_test_nv_get_max_sustainable_clocks_unsupported - Test EOPNOTSUPP + * @test: KUnit test context + * + * Verify pp_nv_get_maximum_sustainable_clocks returns PP_SMU_RESULT_UNSUPPORTED. + */ +static void dm_test_nv_get_max_sustainable_clocks_unsupported(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + struct pp_smu_nv_clock_table max_clocks = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->get_max_sustainable_ret = -EOPNOTSUPP; + + KUNIT_EXPECT_EQ(test, + (int)pp_nv_get_maximum_sustainable_clocks(&pp_smu, &max_clocks), + (int)PP_SMU_RESULT_UNSUPPORTED); +} + +/** + * dm_test_nv_get_max_sustainable_clocks_fail - Test generic error + * @test: KUnit test context + * + * Verify pp_nv_get_maximum_sustainable_clocks returns PP_SMU_RESULT_FAIL. + */ +static void dm_test_nv_get_max_sustainable_clocks_fail(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + struct pp_smu_nv_clock_table max_clocks = {}; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->get_max_sustainable_ret = -EIO; + + KUNIT_EXPECT_EQ(test, + (int)pp_nv_get_maximum_sustainable_clocks(&pp_smu, &max_clocks), + (int)PP_SMU_RESULT_FAIL); +} + +/* ---- Tests for pp_nv_get_uclk_dpm_states ---- */ + +/** + * dm_test_nv_get_uclk_dpm_states_ok - Test successful DPM states query + * @test: KUnit test context + * + * Verify pp_nv_get_uclk_dpm_states returns PP_SMU_RESULT_OK. + */ +static void dm_test_nv_get_uclk_dpm_states_ok(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + unsigned int clock_values[4] = {}; + unsigned int num_states = 0; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->get_uclk_dpm_ret = 0; + + KUNIT_EXPECT_EQ(test, + (int)pp_nv_get_uclk_dpm_states(&pp_smu, clock_values, &num_states), + (int)PP_SMU_RESULT_OK); +} + +/** + * dm_test_nv_get_uclk_dpm_states_unsupported - Test EOPNOTSUPP mapping + * @test: KUnit test context + * + * Verify pp_nv_get_uclk_dpm_states returns PP_SMU_RESULT_UNSUPPORTED. + */ +static void dm_test_nv_get_uclk_dpm_states_unsupported(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + unsigned int clock_values[4] = {}; + unsigned int num_states = 0; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->get_uclk_dpm_ret = -EOPNOTSUPP; + + KUNIT_EXPECT_EQ(test, + (int)pp_nv_get_uclk_dpm_states(&pp_smu, clock_values, &num_states), + (int)PP_SMU_RESULT_UNSUPPORTED); +} + +/** + * dm_test_nv_get_uclk_dpm_states_fail - Test generic error mapping + * @test: KUnit test context + * + * Verify pp_nv_get_uclk_dpm_states returns PP_SMU_RESULT_FAIL. + */ +static void dm_test_nv_get_uclk_dpm_states_fail(struct kunit *test) +{ + struct amdgpu_device *adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + struct dc_context *ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + struct pp_smu pp_smu = {}; + unsigned int clock_values[4] = {}; + unsigned int num_states = 0; + + KUNIT_ASSERT_NOT_NULL(test, adev); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + setup_stub_dpm(test, adev); + ctx->driver_context = adev; + pp_smu.dm = ctx; + stub_dpm_ctx->get_uclk_dpm_ret = -EIO; + + KUNIT_EXPECT_EQ(test, + (int)pp_nv_get_uclk_dpm_states(&pp_smu, clock_values, &num_states), + (int)PP_SMU_RESULT_FAIL); +} + +static struct kunit_case dm_pp_smu_test_cases[] = { + /* get_default_clock_levels */ + KUNIT_CASE(dm_test_default_clock_levels_display), + KUNIT_CASE(dm_test_default_clock_levels_engine), + KUNIT_CASE(dm_test_default_clock_levels_memory), + KUNIT_CASE(dm_test_default_clock_levels_unknown), + /* dc_to_pp_clock_type */ + KUNIT_CASE(dm_test_dc_to_pp_clock_type_display), + KUNIT_CASE(dm_test_dc_to_pp_clock_type_engine), + KUNIT_CASE(dm_test_dc_to_pp_clock_type_memory), + KUNIT_CASE(dm_test_dc_to_pp_clock_type_dcefclk), + KUNIT_CASE(dm_test_dc_to_pp_clock_type_dcfclk), + KUNIT_CASE(dm_test_dc_to_pp_clock_type_pixelclk), + KUNIT_CASE(dm_test_dc_to_pp_clock_type_fclk), + KUNIT_CASE(dm_test_dc_to_pp_clock_type_phyclk), + KUNIT_CASE(dm_test_dc_to_pp_clock_type_dppclk), + KUNIT_CASE(dm_test_dc_to_pp_clock_type_invalid), + /* pp_to_dc_clock_levels */ + KUNIT_CASE(dm_test_pp_to_dc_clock_levels_within_limit), + KUNIT_CASE(dm_test_pp_to_dc_clock_levels_caps_at_max), + /* pp_to_dc_clock_levels_with_latency */ + KUNIT_CASE(dm_test_pp_to_dc_clock_levels_latency_within_limit), + KUNIT_CASE(dm_test_pp_to_dc_clock_levels_latency_caps_at_max), + /* pp_to_dc_clock_levels_with_voltage */ + KUNIT_CASE(dm_test_pp_to_dc_clock_levels_voltage_within_limit), + KUNIT_CASE(dm_test_pp_to_dc_clock_levels_voltage_caps_at_max), + /* dm_pp_get_funcs */ + KUNIT_CASE(dm_test_get_funcs_rv), + KUNIT_CASE(dm_test_get_funcs_rv_101), + KUNIT_CASE(dm_test_get_funcs_nv), + KUNIT_CASE(dm_test_get_funcs_rn), + KUNIT_CASE(dm_test_get_funcs_unsupported), + /* amdgpu_device-backed entry points */ + KUNIT_CASE(dm_test_apply_display_requirements_dpm_disabled), + KUNIT_CASE(dm_test_apply_clock_for_voltage_invalid_type), + /* build_pm_display_cfg */ + KUNIT_CASE(dm_test_build_pm_display_cfg_scalar_fields), + KUNIT_CASE(dm_test_build_pm_display_cfg_per_display), + /* build_wm_clock_ranges_soc15 */ + KUNIT_CASE(dm_test_build_wm_clock_ranges_dmif), + KUNIT_CASE(dm_test_build_wm_clock_ranges_mcif), + /* cap_clock_levels_to_validation */ + KUNIT_CASE(dm_test_cap_clock_levels_engine_caps), + KUNIT_CASE(dm_test_cap_clock_levels_engine_first_exceeds), + KUNIT_CASE(dm_test_cap_clock_levels_memory_caps), + KUNIT_CASE(dm_test_cap_clock_levels_within_limit), + KUNIT_CASE(dm_test_cap_clock_levels_other_type), + /* pp_smu_nv_clock_id_to_pp */ + KUNIT_CASE(dm_test_nv_clock_id_dispclk), + KUNIT_CASE(dm_test_nv_clock_id_phyclk), + KUNIT_CASE(dm_test_nv_clock_id_pixelclk), + KUNIT_CASE(dm_test_nv_clock_id_invalid), + /* dm_pp_apply_display_requirements (DPM enabled) */ + KUNIT_CASE(dm_test_apply_display_requirements_dpm_enabled), + /* dm_pp_get_clock_levels_by_type */ + KUNIT_CASE(dm_test_get_clock_levels_by_type_dpm_error), + KUNIT_CASE(dm_test_get_clock_levels_by_type_success), + KUNIT_CASE(dm_test_get_clock_levels_by_type_validation_fallback), + /* dm_pp_get_clock_levels_by_type_with_latency */ + KUNIT_CASE(dm_test_get_clock_levels_with_latency_success), + KUNIT_CASE(dm_test_get_clock_levels_with_latency_failure), + /* dm_pp_get_clock_levels_by_type_with_voltage */ + KUNIT_CASE(dm_test_get_clock_levels_with_voltage_success), + KUNIT_CASE(dm_test_get_clock_levels_with_voltage_failure), + /* dm_pp_notify_wm_clock_changes */ + KUNIT_CASE(dm_test_notify_wm_clock_changes_polaris), + KUNIT_CASE(dm_test_notify_wm_clock_changes_non_polaris), + /* dm_pp_apply_clock_for_voltage_request (with DPM) */ + KUNIT_CASE(dm_test_apply_clock_for_voltage_success), + KUNIT_CASE(dm_test_apply_clock_for_voltage_eopnotsupp), + KUNIT_CASE(dm_test_apply_clock_for_voltage_fail), + /* pp_nv_set_display_count */ + KUNIT_CASE(dm_test_nv_set_display_count_ok), + KUNIT_CASE(dm_test_nv_set_display_count_unsupported), + KUNIT_CASE(dm_test_nv_set_display_count_fail), + /* pp_nv_set_voltage_by_freq */ + KUNIT_CASE(dm_test_nv_set_voltage_by_freq_ok), + KUNIT_CASE(dm_test_nv_set_voltage_by_freq_invalid_id), + /* pp_nv_set_pstate_handshake_support */ + KUNIT_CASE(dm_test_nv_pstate_handshake_ok), + KUNIT_CASE(dm_test_nv_pstate_handshake_fail), + /* pp_rn_get_dpm_clock_table */ + KUNIT_CASE(dm_test_rn_get_dpm_clock_table_ok), + KUNIT_CASE(dm_test_rn_get_dpm_clock_table_unsupported), + KUNIT_CASE(dm_test_rn_get_dpm_clock_table_fail), + /* pp_rv_set_wm_ranges */ + KUNIT_CASE(dm_test_rv_set_wm_ranges), + /* pp_rv_set_pme_wa_enable */ + KUNIT_CASE(dm_test_rv_set_pme_wa_enable), + /* pp_rv_set_active_display_count */ + KUNIT_CASE(dm_test_rv_set_active_display_count), + /* pp_rv_set_min_deep_sleep_dcfclk */ + KUNIT_CASE(dm_test_rv_set_min_deep_sleep_dcfclk), + /* pp_rv_set_hard_min_dcefclk_by_freq */ + KUNIT_CASE(dm_test_rv_set_hard_min_dcefclk_by_freq), + /* pp_rv_set_hard_min_fclk_by_freq */ + KUNIT_CASE(dm_test_rv_set_hard_min_fclk_by_freq), + /* pp_nv_set_wm_ranges */ + KUNIT_CASE(dm_test_nv_set_wm_ranges), + /* pp_nv_set_min_deep_sleep_dcfclk */ + KUNIT_CASE(dm_test_nv_set_min_deep_sleep_dcfclk_ok), + KUNIT_CASE(dm_test_nv_set_min_deep_sleep_dcfclk_unsupported), + KUNIT_CASE(dm_test_nv_set_min_deep_sleep_dcfclk_fail), + /* pp_nv_set_hard_min_dcefclk_by_freq */ + KUNIT_CASE(dm_test_nv_set_hard_min_dcefclk_ok), + KUNIT_CASE(dm_test_nv_set_hard_min_dcefclk_unsupported), + KUNIT_CASE(dm_test_nv_set_hard_min_dcefclk_fail), + /* pp_nv_set_hard_min_uclk_by_freq */ + KUNIT_CASE(dm_test_nv_set_hard_min_uclk_ok), + KUNIT_CASE(dm_test_nv_set_hard_min_uclk_unsupported), + KUNIT_CASE(dm_test_nv_set_hard_min_uclk_fail), + /* pp_nv_get_maximum_sustainable_clocks */ + KUNIT_CASE(dm_test_nv_get_max_sustainable_clocks_ok), + KUNIT_CASE(dm_test_nv_get_max_sustainable_clocks_unsupported), + KUNIT_CASE(dm_test_nv_get_max_sustainable_clocks_fail), + /* pp_nv_get_uclk_dpm_states */ + KUNIT_CASE(dm_test_nv_get_uclk_dpm_states_ok), + KUNIT_CASE(dm_test_nv_get_uclk_dpm_states_unsupported), + KUNIT_CASE(dm_test_nv_get_uclk_dpm_states_fail), + {} +}; + +static struct kunit_suite dm_pp_smu_test_suite = { + .name = "amdgpu_dm_pp_smu", + .test_cases = dm_pp_smu_test_cases, +}; + +kunit_test_suite(dm_pp_smu_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_pp_smu"); 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 index 09084f70a405..09bd98e93047 100644 --- 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 @@ -7,29 +7,331 @@ #include <kunit/test.h> +#include "dc.h" +#include "core_types.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" #include "amdgpu_dm_psr.h" +#include "amdgpu_dm_kunit_test_helpers.h" +#include "power_helpers.h" -/* - * 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) +static struct dc_stream_state *alloc_test_psr_stream(struct kunit *test) { struct dc_link *link; - link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); - KUNIT_ASSERT_NOT_NULL(test, link); + link = dm_kunit_alloc_link(test); + link->psr_settings.psr_feature_enabled = true; + + return dm_kunit_alloc_stream(test, link); +} + +static struct core_power *create_test_power_module(struct kunit *test, + struct dc_stream_state *stream, struct psr_caps *caps) +{ + struct core_power *core_power; + + core_power = kunit_kzalloc(test, sizeof(*core_power), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, core_power); + + core_power->map = kunit_kzalloc(test, sizeof(*core_power->map), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, core_power->map); + + core_power->map[0].stream = stream; + core_power->map[0].caps = caps; + core_power->map[0].psr_events = psr_event_vsync; + core_power->num_entities = 1; + + return core_power; +} + +static struct dc_link *alloc_test_psrsu_link(struct kunit *test) +{ + struct dc_link *link = dm_kunit_alloc_link(test); + struct dc_context *ctx; + struct dc *dc; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc); + + link->ctx = ctx; + ctx->dc = dc; + dc->ctx = ctx; + dc->caps.dmcub_support = true; + ctx->dce_version = DCN_VERSION_3_1; + link->dpcd_caps.edp_rev = DP_EDP_14; + link->dpcd_caps.psr_info.psr_version = DP_PSR2_WITH_Y_COORD_ET_SUPPORTED; + link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 1; + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.Y_COORDINATE_REQUIRED = 1; + + return link; +} + +static struct dc_link *alloc_test_psr_caps_link(struct kunit *test) +{ + struct dc_link *link = alloc_test_psrsu_link(test); + + link->ctx->dc->caps.dmub_caps.psr = true; + link->connector_signal = SIGNAL_TYPE_EDP; + link->type = dc_connection_single; return link; } +static struct amdgpu_dm_connector *alloc_test_aconnector(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector; + + aconnector = kunit_kzalloc(test, sizeof(*aconnector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, aconnector); + + return aconnector; +} + +/* Tests for link_supports_psrsu() */ + +/** + * dm_test_link_supports_psrsu_no_dmcub() - DMCUB support is required. + * @test: KUnit test context. + */ +static void dm_test_link_supports_psrsu_no_dmcub(struct kunit *test) +{ + struct dc_link *link = alloc_test_psrsu_link(test); + + link->ctx->dc->caps.dmcub_support = false; + + KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link)); +} + +/** + * dm_test_link_supports_psrsu_old_dcn() - DCN version 3.1 or newer is required. + * @test: KUnit test context. + */ +static void dm_test_link_supports_psrsu_old_dcn(struct kunit *test) +{ + struct dc_link *link = alloc_test_psrsu_link(test); + + link->ctx->dce_version = DCN_VERSION_3_0; + + KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link)); +} + +/** + * dm_test_link_supports_psrsu_panel_unsupported() - Panel PSR-SU caps are required. + * @test: KUnit test context. + */ +static void dm_test_link_supports_psrsu_panel_unsupported(struct kunit *test) +{ + struct dc_link *link = alloc_test_psrsu_link(test); + + link->dpcd_caps.psr_info.psr_version = 0; + + KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link)); +} + +/** + * dm_test_link_supports_psrsu_missing_alpm() - AUX wake ALPM is required. + * @test: KUnit test context. + */ +static void dm_test_link_supports_psrsu_missing_alpm(struct kunit *test) +{ + struct dc_link *link = alloc_test_psrsu_link(test); + + link->dpcd_caps.alpm_caps.bits.AUX_WAKE_ALPM_CAP = 0; + + KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link)); +} + +/** + * dm_test_link_supports_psrsu_missing_y_coordinate() - Y coordinate support is required. + * @test: KUnit test context. + */ +static void dm_test_link_supports_psrsu_missing_y_coordinate(struct kunit *test) +{ + struct dc_link *link = alloc_test_psrsu_link(test); + + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.Y_COORDINATE_REQUIRED = 0; + + KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link)); +} + +/** + * dm_test_link_supports_psrsu_missing_granularity() - Required granularity must + * be reported by the panel. + * @test: KUnit test context. + */ +static void dm_test_link_supports_psrsu_missing_granularity(struct kunit *test) +{ + struct dc_link *link = alloc_test_psrsu_link(test); + + link->dpcd_caps.psr_info.psr_dpcd_caps.bits.SU_GRANULARITY_REQUIRED = 1; + link->dpcd_caps.psr_info.psr2_su_y_granularity_cap = 0; + + KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link)); +} + +/** + * dm_test_link_supports_psrsu_debug_mask_disabled() - Debug mask disables PSR-SU. + * @test: KUnit test context. + */ +static void dm_test_link_supports_psrsu_debug_mask_disabled(struct kunit *test) +{ + struct dc_link *link = alloc_test_psrsu_link(test); + unsigned int old_debug_mask; + + old_debug_mask = amdgpu_dm_psr_get_dc_debug_mask(); + amdgpu_dm_psr_set_dc_debug_mask(old_debug_mask | DC_DISABLE_PSR_SU); + + KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link)); + amdgpu_dm_psr_set_dc_debug_mask(old_debug_mask); +} + +/** + * dm_test_link_supports_psrsu_temporarily_disabled() - Supported panels still + * return false while PSR-SU is temporarily disabled. + * @test: KUnit test context. + */ +static void dm_test_link_supports_psrsu_temporarily_disabled(struct kunit *test) +{ + struct dc_link *link = alloc_test_psrsu_link(test); + unsigned int old_debug_mask; + + old_debug_mask = amdgpu_dm_psr_get_dc_debug_mask(); + amdgpu_dm_psr_set_dc_debug_mask(old_debug_mask & ~DC_DISABLE_PSR_SU); + + KUNIT_EXPECT_FALSE(test, link_supports_psrsu(link)); + amdgpu_dm_psr_set_dc_debug_mask(old_debug_mask); +} + +/* End of tests for link_supports_psrsu() */ + +/* Tests for amdgpu_dm_set_psr_caps() */ + +/** + * dm_test_set_psr_caps_null_link() - NULL link is rejected. + * @test: KUnit test context. + */ +static void dm_test_set_psr_caps_null_link(struct kunit *test) +{ + struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(NULL, aconnector)); +} + +/** + * dm_test_set_psr_caps_null_connector() - NULL connector is rejected. + * @test: KUnit test context. + */ +static void dm_test_set_psr_caps_null_connector(struct kunit *test) +{ + struct dc_link *link = alloc_test_psr_caps_link(test); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, NULL)); +} + +/** + * dm_test_set_psr_caps_no_dmub_psr() - DMUB PSR capability is required. + * @test: KUnit test context. + */ +static void dm_test_set_psr_caps_no_dmub_psr(struct kunit *test) +{ + struct dc_link *link = alloc_test_psr_caps_link(test); + struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test); + + link->psr_settings.psr_version = DC_PSR_VERSION_1; + link->ctx->dc->caps.dmub_caps.psr = false; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector)); + KUNIT_EXPECT_EQ(test, link->psr_settings.psr_version, + DC_PSR_VERSION_UNSUPPORTED); +} + +/** + * dm_test_set_psr_caps_non_edp() - Only eDP links can enable PSR. + * @test: KUnit test context. + */ +static void dm_test_set_psr_caps_non_edp(struct kunit *test) +{ + struct dc_link *link = alloc_test_psr_caps_link(test); + struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test); + + link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector)); +} + +/** + * dm_test_set_psr_caps_disconnected() - Disconnected links cannot enable PSR. + * @test: KUnit test context. + */ +static void dm_test_set_psr_caps_disconnected(struct kunit *test) +{ + struct dc_link *link = alloc_test_psr_caps_link(test); + struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test); + + link->type = dc_connection_none; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector)); +} + +/** + * dm_test_set_psr_caps_no_dpcd_psr() - DPCD PSR version is required. + * @test: KUnit test context. + */ +static void dm_test_set_psr_caps_no_dpcd_psr(struct kunit *test) +{ + struct dc_link *link = alloc_test_psr_caps_link(test); + struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test); + + link->dpcd_caps.psr_info.psr_version = 0; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector)); +} + +/** + * dm_test_set_psr_caps_edp1_disabled() - eDP panel instance 1 is blocked. + * @test: KUnit test context. + */ +static void dm_test_set_psr_caps_edp1_disabled(struct kunit *test) +{ + struct dc_link *link = alloc_test_psr_caps_link(test); + struct dc_link *edp0 = dm_kunit_alloc_link(test); + struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test); + struct dc *dc = link->ctx->dc; + + edp0->connector_signal = SIGNAL_TYPE_EDP; + dc->links[0] = edp0; + dc->links[1] = link; + dc->link_count = 2; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_psr_caps(link, aconnector)); +} + +/** + * dm_test_set_psr_caps_success_psr1() - Valid eDP link enables PSR1 caps. + * @test: KUnit test context. + */ +static void dm_test_set_psr_caps_success_psr1(struct kunit *test) +{ + struct dc_link *link = alloc_test_psr_caps_link(test); + struct amdgpu_dm_connector *aconnector = alloc_test_aconnector(test); + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_set_psr_caps(link, aconnector)); + KUNIT_EXPECT_EQ(test, link->psr_settings.psr_version, DC_PSR_VERSION_1); + KUNIT_EXPECT_EQ(test, (int)aconnector->psr_caps.psr_version, 1); + KUNIT_EXPECT_EQ(test, (int)aconnector->psr_caps.support_ver, + DP_PSR2_WITH_Y_COORD_ET_SUPPORTED); +} + +/* End of tests for amdgpu_dm_set_psr_caps() */ + /* 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 dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -42,7 +344,7 @@ static void dm_test_psr_fill_caps_version_1(struct kunit *test) static void dm_test_psr_fill_caps_version_su1(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -55,7 +357,7 @@ static void dm_test_psr_fill_caps_version_su1(struct kunit *test) static void dm_test_psr_fill_caps_version_unsupported(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -74,7 +376,7 @@ static void dm_test_psr_fill_caps_version_unsupported(struct kunit *test) static void dm_test_psr_fill_caps_setup_time_zero(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -88,7 +390,7 @@ static void dm_test_psr_fill_caps_setup_time_zero(struct kunit *test) static void dm_test_psr_fill_caps_setup_time_mid(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -102,7 +404,7 @@ static void dm_test_psr_fill_caps_setup_time_mid(struct kunit *test) static void dm_test_psr_fill_caps_setup_time_max(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -118,7 +420,7 @@ static void dm_test_psr_fill_caps_setup_time_max(struct kunit *test) static void dm_test_psr_fill_caps_link_training_required(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -131,7 +433,7 @@ static void dm_test_psr_fill_caps_link_training_required(struct kunit *test) static void dm_test_psr_fill_caps_link_training_not_required(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -146,7 +448,7 @@ static void dm_test_psr_fill_caps_link_training_not_required(struct kunit *test) static void dm_test_psr_fill_caps_dpcd_fields(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -172,7 +474,7 @@ static void dm_test_psr_fill_caps_dpcd_fields(struct kunit *test) static void dm_test_psr_fill_caps_dpcd_fields_unset(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0xFF, sizeof(caps)); @@ -193,7 +495,7 @@ static void dm_test_psr_fill_caps_dpcd_fields_unset(struct kunit *test) static void dm_test_psr_fill_caps_rate_control_always_zero(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; /* Pre-fill caps with non-zero to verify overwrite */ @@ -206,7 +508,7 @@ static void dm_test_psr_fill_caps_rate_control_always_zero(struct kunit *test) static void dm_test_psr_fill_caps_power_opts_z10_always_set(struct kunit *test) { - struct dc_link *link = alloc_test_link(test); + struct dc_link *link = dm_kunit_alloc_link(test); struct psr_caps caps; memset(&caps, 0, sizeof(caps)); @@ -221,6 +523,24 @@ static void dm_test_psr_fill_caps_power_opts_z10_always_set(struct kunit *test) (caps.psr_power_opt_flag & psr_power_opt_z10_static_screen) != 0); } + +static void dm_test_psr_fill_caps_power_opts_smu_opt_set(struct kunit *test) +{ + struct dc_link *link = dm_kunit_alloc_link(test); + struct psr_caps caps; + unsigned int old_feature_mask; + + memset(&caps, 0, sizeof(caps)); + old_feature_mask = amdgpu_dm_psr_get_dc_feature_mask(); + amdgpu_dm_psr_set_dc_feature_mask(old_feature_mask | DC_PSR_ALLOW_SMU_OPT); + + amdgpu_dm_psr_fill_caps(link, &caps); + amdgpu_dm_psr_set_dc_feature_mask(old_feature_mask); + + KUNIT_EXPECT_TRUE(test, + (caps.psr_power_opt_flag & + psr_power_opt_smu_opt_static_screen) != 0); +} /* End of tests for amdgpu_dm_psr_fill_caps() */ /* Tests for amdgpu_dm_psr_set_event() — early-exit validation guards */ @@ -258,9 +578,155 @@ static void dm_test_psr_set_event_psr_not_enabled(struct kunit *test) KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_set_event(NULL, stream, true, psr_event_vsync, false)); } + +/** + * dm_test_psr_set_event_get_event_fails() - Failed power event read returns false. + * @test: KUnit test context. + */ +static void dm_test_psr_set_event_get_event_fails(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct dc_stream_state *stream = alloc_test_psr_stream(test); + + dm->power_module = NULL; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_set_event(dm, stream, true, psr_event_vsync, false)); +} + +/** + * dm_test_psr_set_event_already_set() - Already set event returns true. + * @test: KUnit test context. + */ +static void dm_test_psr_set_event_already_set(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct dc_stream_state *stream = alloc_test_psr_stream(test); + struct psr_caps caps = {0}; + struct core_power *core_power; + + caps.psr_version = 1; + core_power = create_test_power_module(test, stream, &caps); + dm->power_module = &core_power->mod_public; + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_psr_set_event(dm, stream, true, psr_event_vsync, false)); + KUNIT_EXPECT_EQ(test, core_power->map[0].psr_events, + (unsigned int)psr_event_vsync); +} + +/** + * dm_test_psr_set_event_updates_event() - Changed event delegates to mod_power. + * @test: KUnit test context. + */ +static void dm_test_psr_set_event_updates_event(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct dc_stream_state *stream = alloc_test_psr_stream(test); + struct psr_caps caps = {0}; + struct core_power *core_power; + + caps.psr_version = 1; + core_power = create_test_power_module(test, stream, &caps); + dm->power_module = &core_power->mod_public; + + KUNIT_EXPECT_TRUE(test, + amdgpu_dm_psr_set_event(dm, stream, true, psr_event_full_screen, false)); + KUNIT_EXPECT_EQ(test, core_power->map[0].psr_events, + (unsigned int)(psr_event_vsync | psr_event_full_screen)); +} /* End of tests for amdgpu_dm_psr_set_event() */ +/* Tests for amdgpu_dm_psr_is_active_allowed() */ + +/** + * dm_test_psr_is_active_allowed_no_streams() - Empty DC state disallows PSR. + * @test: KUnit test context. + */ +static void dm_test_psr_is_active_allowed_no_streams(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_is_active_allowed(dm)); +} + +/** + * dm_test_psr_is_active_allowed_null_link() - Streams without links are skipped. + * @test: KUnit test context. + */ +static void dm_test_psr_is_active_allowed_null_link(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct dc_state *state = dm->dc->current_state; + + dm_kunit_add_stream_to_state(test, state, 0, NULL); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_is_active_allowed(dm)); +} + +/** + * dm_test_psr_is_active_allowed_requires_enabled_and_allowed() - Both link flags + * must be set before PSR active is allowed. + * @test: KUnit test context. + */ +static void dm_test_psr_is_active_allowed_requires_enabled_and_allowed(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct dc_state *state = dm->dc->current_state; + struct dc_link *link = dm_kunit_alloc_link(test); + + dm_kunit_add_stream_to_state(test, state, 0, link); + link->psr_settings.psr_allow_active = true; + KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_is_active_allowed(dm)); + + link->psr_settings.psr_allow_active = false; + link->psr_settings.psr_feature_enabled = true; + KUNIT_EXPECT_FALSE(test, amdgpu_dm_psr_is_active_allowed(dm)); +} + +/** + * dm_test_psr_is_active_allowed_any_stream() - Any enabled and allowed stream + * permits active PSR. + * @test: KUnit test context. + */ +static void dm_test_psr_is_active_allowed_any_stream(struct kunit *test) +{ + struct amdgpu_display_manager *dm = dm_kunit_alloc_dm(test); + struct dc_state *state = dm->dc->current_state; + struct dc_link *disabled_link = dm_kunit_alloc_link(test); + struct dc_link *allowed_link = dm_kunit_alloc_link(test); + + disabled_link->psr_settings.psr_allow_active = true; + allowed_link->psr_settings.psr_feature_enabled = true; + allowed_link->psr_settings.psr_allow_active = true; + + dm_kunit_add_stream_to_state(test, state, 0, disabled_link); + dm_kunit_add_stream_to_state(test, state, 1, allowed_link); + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_psr_is_active_allowed(dm)); +} + +/* End of tests for amdgpu_dm_psr_is_active_allowed() */ + static struct kunit_case dm_psr_test_cases[] = { + /* link_supports_psrsu */ + KUNIT_CASE(dm_test_link_supports_psrsu_no_dmcub), + KUNIT_CASE(dm_test_link_supports_psrsu_old_dcn), + KUNIT_CASE(dm_test_link_supports_psrsu_panel_unsupported), + KUNIT_CASE(dm_test_link_supports_psrsu_missing_alpm), + KUNIT_CASE(dm_test_link_supports_psrsu_missing_y_coordinate), + KUNIT_CASE(dm_test_link_supports_psrsu_missing_granularity), + KUNIT_CASE(dm_test_link_supports_psrsu_debug_mask_disabled), + KUNIT_CASE(dm_test_link_supports_psrsu_temporarily_disabled), + /* amdgpu_dm_set_psr_caps */ + KUNIT_CASE(dm_test_set_psr_caps_null_link), + KUNIT_CASE(dm_test_set_psr_caps_null_connector), + KUNIT_CASE(dm_test_set_psr_caps_no_dmub_psr), + KUNIT_CASE(dm_test_set_psr_caps_non_edp), + KUNIT_CASE(dm_test_set_psr_caps_disconnected), + KUNIT_CASE(dm_test_set_psr_caps_no_dpcd_psr), + KUNIT_CASE(dm_test_set_psr_caps_edp1_disabled), + KUNIT_CASE(dm_test_set_psr_caps_success_psr1), + /* amdgpu_dm_psr_fill_caps */ 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), @@ -273,9 +739,19 @@ static struct kunit_case dm_psr_test_cases[] = { 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), + KUNIT_CASE(dm_test_psr_fill_caps_power_opts_smu_opt_set), + /* amdgpu_dm_psr_set_event */ KUNIT_CASE(dm_test_psr_set_event_null_stream), KUNIT_CASE(dm_test_psr_set_event_null_link), KUNIT_CASE(dm_test_psr_set_event_psr_not_enabled), + KUNIT_CASE(dm_test_psr_set_event_get_event_fails), + KUNIT_CASE(dm_test_psr_set_event_already_set), + KUNIT_CASE(dm_test_psr_set_event_updates_event), + /* amdgpu_dm_psr_is_active_allowed */ + KUNIT_CASE(dm_test_psr_is_active_allowed_no_streams), + KUNIT_CASE(dm_test_psr_is_active_allowed_null_link), + KUNIT_CASE(dm_test_psr_is_active_allowed_requires_enabled_and_allowed), + KUNIT_CASE(dm_test_psr_is_active_allowed_any_stream), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c new file mode 100644 index 000000000000..a09f31ee0a2a --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_quirks_test.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_quirks.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include "dc.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" + +/* Tests for retrieve_dmi_info() */ + +/* + * Verify that retrieve_dmi_info() always initialises aux_hpd_discon_quirk to + * false, even when the caller had previously set it to true. + */ +/** + * dm_test_quirks_aux_hpd_discon_reset - Test Quirks aux hpd discon reset + * @test: The KUnit test context + */ +static void dm_test_quirks_aux_hpd_discon_reset(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm); + + dm->aux_hpd_discon_quirk = true; + + retrieve_dmi_info(dm); + + /* + * In a KUnit / UML environment no real DMI table is present, so + * dmi_check_system() returns 0 and retrieve_dmi_info() leaves the + * quirk at its initialised-to-false value. + */ + KUNIT_EXPECT_FALSE(test, dm->aux_hpd_discon_quirk); +} + +/* + * Verify that retrieve_dmi_info() always initialises edp0_on_dp1_quirk to + * false, even when the caller had previously set it to true. + */ +/** + * dm_test_quirks_edp0_on_dp1_reset - Test Quirks edp0 on dp1 reset + * @test: The KUnit test context + */ +static void dm_test_quirks_edp0_on_dp1_reset(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm); + + dm->edp0_on_dp1_quirk = true; + + retrieve_dmi_info(dm); + + KUNIT_EXPECT_FALSE(test, dm->edp0_on_dp1_quirk); +} + +/* + * Verify that when no DMI match is found both quirks remain false after a + * fresh (zero-initialised) dm is passed to retrieve_dmi_info(). + */ +/** + * dm_test_quirks_no_dmi_match_both_false - Test Quirks no dmi match both false + * @test: The KUnit test context + */ +static void dm_test_quirks_no_dmi_match_both_false(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dm); + + retrieve_dmi_info(dm); + + KUNIT_EXPECT_FALSE(test, dm->aux_hpd_discon_quirk); + KUNIT_EXPECT_FALSE(test, dm->edp0_on_dp1_quirk); +} + +static struct kunit_case amdgpu_dm_quirks_tests[] = { + /* retrieve_dmi_info */ + KUNIT_CASE(dm_test_quirks_aux_hpd_discon_reset), + KUNIT_CASE(dm_test_quirks_edp0_on_dp1_reset), + KUNIT_CASE(dm_test_quirks_no_dmi_match_both_false), + {} +}; + +static struct kunit_suite amdgpu_dm_quirks_test_suite = { + .name = "amdgpu_dm_quirks", + .test_cases = amdgpu_dm_quirks_tests, +}; + +kunit_test_suite(amdgpu_dm_quirks_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_quirks"); +MODULE_LICENSE("Dual MIT/GPL"); 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 index 28ff8bbcc0f7..6f633b1bbaca 100644 --- 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 @@ -8,12 +8,13 @@ #include <kunit/test.h> #include "dc.h" +#include "dc_dmub_srv.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); +#include "amdgpu_dm_replay.h" +#include "amdgpu_dm_kunit_test_helpers.h" +#include "modules/power/power_helpers.h" +#include "dmub/dmub_srv.h" /* * Helper: allocate a dc_link, amdgpu_dm_connector, and dm_connector_state @@ -23,6 +24,9 @@ struct replay_test_ctx { struct dc_link *link; struct amdgpu_dm_connector *aconnector; struct dm_connector_state *dm_state; + struct dc *dc; + struct dc_context *dc_ctx; + struct dc_stream_state *stream; }; static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test) @@ -32,8 +36,9 @@ static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test) 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->link = dm_kunit_alloc_link_with_ctx(test); + ctx->dc_ctx = ctx->link->ctx; + ctx->dc = ctx->dc_ctx->dc; ctx->aconnector = kunit_kzalloc(test, sizeof(*ctx->aconnector), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->aconnector); @@ -41,6 +46,8 @@ static struct replay_test_ctx *alloc_replay_ctx(struct kunit *test) ctx->dm_state = kunit_kzalloc(test, sizeof(*ctx->dm_state), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, ctx->dm_state); + ctx->stream = dm_kunit_alloc_stream(test, ctx->link); + /* Wire connector state so to_dm_connector_state() works */ ctx->aconnector->base.state = &ctx->dm_state->base; @@ -55,6 +62,7 @@ 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->connector_signal = SIGNAL_TYPE_EDP; 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; @@ -181,7 +189,398 @@ static void dm_test_replay_both_deviations_zero(struct kunit *test) /* End of tests for amdgpu_dm_link_supports_replay() */ +/* Tests for amdgpu_dm_set_replay_caps() */ + +/** + * dm_test_replay_set_caps_already_supported - Verify cached Replay support + * @test: KUnit test context + * + * When replay_supported is already set, amdgpu_dm_set_replay_caps() should + * return true without revalidating the link capabilities. + */ +static void dm_test_replay_set_caps_already_supported(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + ctx->link->replay_settings.config.replay_supported = true; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_non_embedded_signal - Verify non-eDP rejection + * @test: KUnit test context + * + * When the link signal is not embedded, amdgpu_dm_set_replay_caps() should + * reject Replay even if the sink capability fields are otherwise valid. + */ +static void dm_test_replay_set_caps_non_embedded_signal(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->connector_signal = SIGNAL_TYPE_DISPLAY_PORT; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_disallowed_by_panel - Verify panel policy rejection + * @test: KUnit test context + * + * When the panel configuration disallows Replay, amdgpu_dm_set_replay_caps() + * should return false before accepting the capability set. + */ +static void dm_test_replay_set_caps_disallowed_by_panel(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + ctx->link->panel_config.psr.disallow_replay = true; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_link_not_supported - Verify capability rejection + * @test: KUnit test context + * + * When amdgpu_dm_link_supports_replay() rejects the link, the higher-level + * Replay setup helper should also return false. + */ +static void dm_test_replay_set_caps_link_not_supported(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_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_missing_dmub_srv - Verify missing DMUB rejection + * @test: KUnit test context + * + * When the link and connector support Replay but no DMUB service is available, + * amdgpu_dm_set_replay_caps() should return false. + */ +static void dm_test_replay_set_caps_missing_dmub_srv(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + set_all_replay_caps(ctx); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); +} + +/** + * dm_test_replay_set_caps_success - Verify successful Replay configuration + * @test: KUnit test context + * + * When all prerequisites are met (embedded signal, panel allows replay, link + * supports replay, DMUB present with replay support), amdgpu_dm_set_replay_caps() + * should configure the link replay settings and return true. + */ +static void dm_test_replay_set_caps_success(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct dc_dmub_srv *dmub_srv; + struct dmub_srv *dmub; + + set_all_replay_caps(ctx); + + dmub_srv = kunit_kzalloc(test, sizeof(*dmub_srv), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dmub_srv); + + dmub = kunit_kzalloc(test, sizeof(*dmub), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dmub); + + dmub->feature_caps.replay_supported = 1; + dmub_srv->dmub = dmub; + ctx->dc_ctx->dmub_srv = dmub_srv; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_set_replay_caps(ctx->link, ctx->aconnector)); + KUNIT_EXPECT_TRUE(test, ctx->link->replay_settings.config.replay_supported); +} + +/* Tests for amdgpu_dm_link_setup_replay() */ + +/** + * dm_test_replay_link_setup_null_stream - Verify NULL stream rejection + * @test: KUnit test context + * + * amdgpu_dm_link_setup_replay() should return false when no stream is provided. + */ +static void dm_test_replay_link_setup_null_stream(struct kunit *test) +{ + struct mod_vrr_params vrr_params = { 0 }; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(NULL, &vrr_params)); +} + +/** + * dm_test_replay_link_setup_null_link - Verify NULL stream link rejection + * @test: KUnit test context + * + * amdgpu_dm_link_setup_replay() should return false when the stream has no + * associated link. + */ +static void dm_test_replay_link_setup_null_link(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct mod_vrr_params vrr_params = { 0 }; + + ctx->stream->link = NULL; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params)); +} + +/** + * dm_test_replay_link_setup_null_vrr_params - Verify NULL VRR params rejection + * @test: KUnit test context + * + * amdgpu_dm_link_setup_replay() should return false when VRR parameters are + * not supplied. + */ +static void dm_test_replay_link_setup_null_vrr_params(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(ctx->stream, NULL)); +} + +/** + * dm_test_replay_link_setup_not_supported - Verify unsupported Replay rejection + * @test: KUnit test context + * + * amdgpu_dm_link_setup_replay() should return false when Replay is not marked + * supported on the link configuration. + */ +static void dm_test_replay_link_setup_not_supported(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct mod_vrr_params vrr_params = { 0 }; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params)); +} + +/** + * dm_test_replay_link_setup_already_enabled - Verify enabled Replay success + * @test: KUnit test context + * + * When Replay is already enabled, amdgpu_dm_link_setup_replay() should return + * true without recalculating coasting vtotal state. + */ +static void dm_test_replay_link_setup_already_enabled(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct mod_vrr_params vrr_params = { 0 }; + + ctx->link->replay_settings.config.replay_supported = true; + ctx->link->replay_settings.replay_feature_enabled = true; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params)); +} + +/** + * dm_test_replay_link_setup_success - Verify coasting vtotal configuration + * @test: KUnit test context + * + * When Replay is supported but not yet enabled, amdgpu_dm_link_setup_replay() + * should calculate the link-off frame count and set the coasting vtotal values, + * then return true. + */ +static void dm_test_replay_link_setup_success(struct kunit *test) +{ + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct mod_vrr_params vrr_params = { 0 }; + + ctx->link->replay_settings.config.replay_supported = true; + ctx->link->replay_settings.config.replay_version = DC_FREESYNC_REPLAY; + + /* Set timing so calculate_replay_link_off_frame_count computes */ + ctx->stream->timing.v_total = 1125; + ctx->stream->timing.h_total = 2200; + ctx->stream->timing.pix_clk_100hz = 1485000; + ctx->link->dpcd_caps.pr_info.pixel_deviation_per_line = 4; + ctx->link->dpcd_caps.pr_info.max_deviation_line = 10; + + /* min_refresh_in_uhz = 0 makes calc return v_total directly */ + vrr_params.min_refresh_in_uhz = 0; + + KUNIT_EXPECT_TRUE(test, amdgpu_dm_link_setup_replay(ctx->stream, &vrr_params)); + + /* Verify coasting vtotal was set */ + KUNIT_EXPECT_EQ(test, + ctx->link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_NOM], + (uint32_t)1125); + KUNIT_EXPECT_EQ(test, + ctx->link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_STATIC], + (uint32_t)1125); + + /* Verify link_off_frame_count was calculated: 2200*10/(4*1125) = 4 */ + KUNIT_EXPECT_EQ(test, + ctx->link->replay_settings.link_off_frame_count, + (uint32_t)4); +} + +/* Tests for amdgpu_dm_replay_set_event() */ + +/** + * dm_test_replay_set_event_null_stream - Verify NULL stream rejection + * @test: KUnit test context + * + * amdgpu_dm_replay_set_event() should return false when no stream is provided. + */ +static void dm_test_replay_set_event_null_stream(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, NULL, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_null_link - Verify NULL stream link rejection + * @test: KUnit test context + * + * amdgpu_dm_replay_set_event() should return false when the stream has no + * associated link. + */ +static void dm_test_replay_set_event_null_link(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + ctx->stream->link = NULL; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_feature_disabled - Verify disabled Replay rejection + * @test: KUnit test context + * + * amdgpu_dm_replay_set_event() should return false when Replay is not enabled + * on the stream link. + */ +static void dm_test_replay_set_event_feature_disabled(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_missing_power_module - Verify missing power rejection + * @test: KUnit test context + * + * When Replay is enabled but no power module is available, the event helper + * should return false after failing to read the current Replay events. + */ +static void dm_test_replay_set_event_missing_power_module(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + ctx->link->replay_settings.replay_feature_enabled = true; + + KUNIT_EXPECT_FALSE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_already_set - Verify no-op when event already active + * @test: KUnit test context + * + * When the requested event is already in the desired state, the function should + * return true without calling mod_power_set_replay_event(). + */ +static void dm_test_replay_set_event_already_set(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct core_power *core_power; + struct power_entity *map; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + core_power = kunit_kzalloc(test, sizeof(*core_power), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, core_power); + + map = kunit_kzalloc(test, sizeof(*map), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, map); + + /* Wire the power module so mod_power_get_replay_event() succeeds */ + map->stream = ctx->stream; + map->replay_events = replay_event_vsync; + core_power->map = map; + core_power->num_entities = 1; + dm->power_module = &core_power->mod_public; + + ctx->link->replay_settings.replay_feature_enabled = true; + + /* Event already set — should return true without calling set */ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, true, + replay_event_vsync, false)); +} + +/** + * dm_test_replay_set_event_already_clear - Verify no-op when event already cleared + * @test: KUnit test context + * + * When clearing an event that is not currently active, the function should + * return true without calling mod_power_set_replay_event(). + */ +static void dm_test_replay_set_event_already_clear(struct kunit *test) +{ + struct amdgpu_display_manager *dm; + struct replay_test_ctx *ctx = alloc_replay_ctx(test); + struct core_power *core_power; + struct power_entity *map; + + dm = kunit_kzalloc(test, sizeof(*dm), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dm); + + core_power = kunit_kzalloc(test, sizeof(*core_power), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, core_power); + + map = kunit_kzalloc(test, sizeof(*map), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, map); + + /* Wire the power module — replay_events has NO vsync bit */ + map->stream = ctx->stream; + map->replay_events = 0; + core_power->map = map; + core_power->num_entities = 1; + dm->power_module = &core_power->mod_public; + + ctx->link->replay_settings.replay_feature_enabled = true; + + /* Clearing an event that's already clear — should return true */ + KUNIT_EXPECT_TRUE(test, amdgpu_dm_replay_set_event(dm, ctx->stream, false, + replay_event_vsync, false)); +} + static struct kunit_case dm_replay_test_cases[] = { + /* amdgpu_dm_link_supports_replay */ KUNIT_CASE(dm_test_replay_supports_all_caps), KUNIT_CASE(dm_test_replay_no_freesync), KUNIT_CASE(dm_test_replay_no_vsdb_replay_mode), @@ -191,6 +590,27 @@ static struct kunit_case dm_replay_test_cases[] = { 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), + /* amdgpu_dm_set_replay_caps */ + KUNIT_CASE(dm_test_replay_set_caps_already_supported), + KUNIT_CASE(dm_test_replay_set_caps_non_embedded_signal), + KUNIT_CASE(dm_test_replay_set_caps_disallowed_by_panel), + KUNIT_CASE(dm_test_replay_set_caps_link_not_supported), + KUNIT_CASE(dm_test_replay_set_caps_missing_dmub_srv), + KUNIT_CASE(dm_test_replay_set_caps_success), + /* amdgpu_dm_link_setup_replay */ + KUNIT_CASE(dm_test_replay_link_setup_null_stream), + KUNIT_CASE(dm_test_replay_link_setup_null_link), + KUNIT_CASE(dm_test_replay_link_setup_null_vrr_params), + KUNIT_CASE(dm_test_replay_link_setup_not_supported), + KUNIT_CASE(dm_test_replay_link_setup_already_enabled), + KUNIT_CASE(dm_test_replay_link_setup_success), + /* amdgpu_dm_replay_set_event */ + KUNIT_CASE(dm_test_replay_set_event_null_stream), + KUNIT_CASE(dm_test_replay_set_event_null_link), + KUNIT_CASE(dm_test_replay_set_event_feature_disabled), + KUNIT_CASE(dm_test_replay_set_event_missing_power_module), + KUNIT_CASE(dm_test_replay_set_event_already_set), + KUNIT_CASE(dm_test_replay_set_event_already_clear), {} }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_services_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_services_test.c new file mode 100644 index 000000000000..e48bac7fb024 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_services_test.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_services.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" +#include "dm_services.h" +#include "dm_services_types.h" + +/* Tests for dm_get_elapse_time_in_ns() */ + +/** + * dm_test_get_elapse_time_zero_delta - Test Get elapse time zero delta + * @test: The KUnit test context + */ +static void dm_test_get_elapse_time_zero_delta(struct kunit *test) +{ + unsigned long long ts = 1000000ULL; + + KUNIT_EXPECT_EQ(test, dm_get_elapse_time_in_ns(NULL, ts, ts), 0ULL); +} + +/** + * dm_test_get_elapse_time_positive_delta - Test Get elapse time positive delta + * @test: The KUnit test context + */ +static void dm_test_get_elapse_time_positive_delta(struct kunit *test) +{ + unsigned long long current_ts = 5000000ULL; + unsigned long long last_ts = 1000000ULL; + + KUNIT_EXPECT_EQ(test, dm_get_elapse_time_in_ns(NULL, current_ts, last_ts), + 4000000ULL); +} + +/** + * dm_test_get_elapse_time_large_delta - Test Get elapse time large delta + * @test: The KUnit test context + */ +static void dm_test_get_elapse_time_large_delta(struct kunit *test) +{ + unsigned long long current_ts = ULLONG_MAX; + unsigned long long last_ts = 0ULL; + + KUNIT_EXPECT_EQ(test, dm_get_elapse_time_in_ns(NULL, current_ts, last_ts), + ULLONG_MAX); +} + +/** + * dm_test_get_elapse_time_wraparound - Test Get elapse time wraparound + * @test: The KUnit test context + */ +static void dm_test_get_elapse_time_wraparound(struct kunit *test) +{ + /* Unsigned wraparound: result = ULLONG_MAX - last + current + 1 */ + unsigned long long current_ts = 5ULL; + unsigned long long last_ts = ULLONG_MAX - 4ULL; + + KUNIT_EXPECT_EQ(test, dm_get_elapse_time_in_ns(NULL, current_ts, last_ts), + 10ULL); +} + +/* Tests for dm_perf_trace_timestamp() */ + +/** + * dm_test_perf_trace_timestamp_basic - Test Perf trace timestamp basic + * @test: The KUnit test context + * + * The tracepoint is a no-op without an attached probe, so this verifies the + * function dereferences ctx->perf_trace safely and does not crash. + */ +static void dm_test_perf_trace_timestamp_basic(struct kunit *test) +{ + struct dc_context *ctx; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + ctx->perf_trace = kunit_kzalloc(test, sizeof(*ctx->perf_trace), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx->perf_trace); + + ctx->perf_trace->read_count = 10; + ctx->perf_trace->write_count = 20; + + dm_perf_trace_timestamp(__func__, __LINE__, ctx); +} + +/* Tests for dm_trace_smu_enter() */ + +/** + * dm_test_trace_smu_enter_null_ctx - Test Trace smu enter null ctx + * @test: The KUnit test context + */ +static void dm_test_trace_smu_enter_null_ctx(struct kunit *test) +{ + /* Empty stub — must not crash with NULL ctx */ + dm_trace_smu_enter(0, 0, 0, NULL); +} + +/** + * dm_test_trace_smu_enter_with_params - Test Trace smu enter with params + * @test: The KUnit test context + */ +static void dm_test_trace_smu_enter_with_params(struct kunit *test) +{ + /* Exercise non-zero msg_id, param_in, and delay */ + dm_trace_smu_enter(0xFF, 0x12345678, 1000, NULL); +} + +/* Tests for dm_trace_smu_exit() */ + +/** + * dm_test_trace_smu_exit_success_null_ctx - Test Trace smu exit success null ctx + * @test: The KUnit test context + */ +static void dm_test_trace_smu_exit_success_null_ctx(struct kunit *test) +{ + /* Empty stub — must not crash on success path with NULL ctx */ + dm_trace_smu_exit(true, 0x0, NULL); +} + +/** + * dm_test_trace_smu_exit_failure_null_ctx - Test Trace smu exit failure null ctx + * @test: The KUnit test context + */ +static void dm_test_trace_smu_exit_failure_null_ctx(struct kunit *test) +{ + /* Empty stub — must not crash on failure path with NULL ctx */ + dm_trace_smu_exit(false, 0x0, NULL); +} + +/** + * dm_test_trace_smu_exit_with_response - Test Trace smu exit with response + * @test: The KUnit test context + */ +static void dm_test_trace_smu_exit_with_response(struct kunit *test) +{ + /* Exercise non-zero response value */ + dm_trace_smu_exit(true, 0xDEADBEEF, NULL); +} + +/* Tests for dm_query_extended_brightness_caps() */ + +/** + * dm_test_query_brightness_caps_null_ctx - Test Query brightness caps null ctx + * @test: The KUnit test context + */ +static void dm_test_query_brightness_caps_null_ctx(struct kunit *test) +{ + struct dm_acpi_atif_backlight_caps caps = {}; + + KUNIT_EXPECT_FALSE(test, + dm_query_extended_brightness_caps(NULL, AcpiDisplayType_LCD1, &caps)); +} + +/** + * dm_test_query_brightness_caps_null_caps - Test Query brightness caps null caps + * @test: The KUnit test context + */ +static void dm_test_query_brightness_caps_null_caps(struct kunit *test) +{ + struct dc_context ctx = {}; + + ctx.driver_context = (void *)0x1; /* non-NULL sentinel */ + + KUNIT_EXPECT_FALSE(test, + dm_query_extended_brightness_caps(&ctx, AcpiDisplayType_LCD1, NULL)); +} + +/** + * dm_test_query_brightness_caps_null_driver_ctx - Test Query brightness caps null driver ctx + * @test: The KUnit test context + */ +static void dm_test_query_brightness_caps_null_driver_ctx(struct kunit *test) +{ + struct dc_context ctx = {}; + struct dm_acpi_atif_backlight_caps caps = {}; + + ctx.driver_context = NULL; + + KUNIT_EXPECT_FALSE(test, + dm_query_extended_brightness_caps(&ctx, AcpiDisplayType_LCD1, &caps)); +} + +/** + * dm_test_query_brightness_caps_lcd2_null_ctx - Test Query brightness caps lcd2 null ctx + * @test: The KUnit test context + */ +static void dm_test_query_brightness_caps_lcd2_null_ctx(struct kunit *test) +{ + struct dm_acpi_atif_backlight_caps caps = {}; + + KUNIT_EXPECT_FALSE(test, + dm_query_extended_brightness_caps(NULL, AcpiDisplayType_LCD2, &caps)); +} + +/** + * dm_test_query_brightness_caps_lcd1_success - Test Query brightness caps lcd1 success + * @test: The KUnit test context + */ +static void dm_test_query_brightness_caps_lcd1_success(struct kunit *test) +{ + struct amdgpu_device *adev; + struct amdgpu_dm_backlight_caps *source_caps; + struct dc_context ctx = {}; + struct dm_acpi_atif_backlight_caps caps = {}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + + source_caps = &adev->dm.backlight_caps[0]; + source_caps->caps_valid = true; + source_caps->min_input_signal = 12; + source_caps->max_input_signal = 240; + source_caps->ac_level = 80; + source_caps->dc_level = 40; + source_caps->data_points = 2; + source_caps->luminance_data[0].luminance = 10; + source_caps->luminance_data[0].input_signal = 22; + source_caps->luminance_data[1].luminance = 90; + source_caps->luminance_data[1].input_signal = 200; + ctx.driver_context = adev; + + KUNIT_EXPECT_TRUE(test, + dm_query_extended_brightness_caps(&ctx, AcpiDisplayType_LCD1, &caps)); + KUNIT_EXPECT_EQ(test, caps.num_data_points, 2); + KUNIT_EXPECT_EQ(test, caps.max_input_signal, 240); + KUNIT_EXPECT_EQ(test, caps.min_input_signal, 12); + KUNIT_EXPECT_EQ(test, caps.ac_level_percentage, 80); + KUNIT_EXPECT_EQ(test, caps.dc_level_percentage, 40); + KUNIT_EXPECT_EQ(test, caps.data_points[0].luminance, 10); + KUNIT_EXPECT_EQ(test, caps.data_points[0].signal_level, 22); + KUNIT_EXPECT_EQ(test, caps.data_points[1].luminance, 90); + KUNIT_EXPECT_EQ(test, caps.data_points[1].signal_level, 200); +} + +/** + * dm_test_query_brightness_caps_non_lcd1_uses_second_slot - Test Query brightness caps non lcd1 uses second slot + * @test: The KUnit test context + */ +static void dm_test_query_brightness_caps_non_lcd1_uses_second_slot(struct kunit *test) +{ + struct amdgpu_device *adev; + struct amdgpu_dm_backlight_caps *source_caps; + struct dc_context ctx = {}; + struct dm_acpi_atif_backlight_caps caps = {}; + + adev = kunit_kzalloc(test, sizeof(*adev), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, adev); + + adev->dm.backlight_caps[0].caps_valid = true; + adev->dm.backlight_caps[0].min_input_signal = 1; + adev->dm.backlight_caps[0].max_input_signal = 2; + source_caps = &adev->dm.backlight_caps[1]; + source_caps->caps_valid = true; + source_caps->min_input_signal = 33; + source_caps->max_input_signal = 199; + source_caps->ac_level = 70; + source_caps->dc_level = 30; + source_caps->data_points = 0; + ctx.driver_context = adev; + + KUNIT_EXPECT_TRUE(test, + dm_query_extended_brightness_caps(&ctx, AcpiDisplayType_DFP1, &caps)); + KUNIT_EXPECT_EQ(test, caps.num_data_points, 0); + KUNIT_EXPECT_EQ(test, caps.max_input_signal, 199); + KUNIT_EXPECT_EQ(test, caps.min_input_signal, 33); + KUNIT_EXPECT_EQ(test, caps.ac_level_percentage, 70); + KUNIT_EXPECT_EQ(test, caps.dc_level_percentage, 30); + KUNIT_EXPECT_EQ(test, caps.data_points[0].luminance, 0); + KUNIT_EXPECT_EQ(test, caps.data_points[0].signal_level, 0); +} + +static struct kunit_case amdgpu_dm_services_test_cases[] = { + /* dm_get_elapse_time_in_ns */ + KUNIT_CASE(dm_test_get_elapse_time_zero_delta), + KUNIT_CASE(dm_test_get_elapse_time_positive_delta), + KUNIT_CASE(dm_test_get_elapse_time_large_delta), + KUNIT_CASE(dm_test_get_elapse_time_wraparound), + /* dm_perf_trace_timestamp */ + KUNIT_CASE(dm_test_perf_trace_timestamp_basic), + /* dm_trace_smu_enter */ + KUNIT_CASE(dm_test_trace_smu_enter_null_ctx), + KUNIT_CASE(dm_test_trace_smu_enter_with_params), + /* dm_trace_smu_exit */ + KUNIT_CASE(dm_test_trace_smu_exit_success_null_ctx), + KUNIT_CASE(dm_test_trace_smu_exit_failure_null_ctx), + KUNIT_CASE(dm_test_trace_smu_exit_with_response), + /* dm_query_extended_brightness_caps */ + KUNIT_CASE(dm_test_query_brightness_caps_null_ctx), + KUNIT_CASE(dm_test_query_brightness_caps_null_caps), + KUNIT_CASE(dm_test_query_brightness_caps_null_driver_ctx), + KUNIT_CASE(dm_test_query_brightness_caps_lcd2_null_ctx), + KUNIT_CASE(dm_test_query_brightness_caps_lcd1_success), + KUNIT_CASE(dm_test_query_brightness_caps_non_lcd1_uses_second_slot), + {} +}; + +static struct kunit_suite amdgpu_dm_services_test_suite = { + .name = "amdgpu_dm_services", + .test_cases = amdgpu_dm_services_test_cases, +}; + +kunit_test_suite(amdgpu_dm_services_test_suite); + +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_services"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c new file mode 100644 index 000000000000..0b29bf0a7d04 --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_test.c @@ -0,0 +1,949 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include "dc.h" +#include "amdgpu_mode.h" +#include "amdgpu_dm.h" + +/* Tests for dm_plane_layer_index_cmp() */ + +/** + * dm_test_plane_layer_index_cmp_equal - Test Plane layer index cmp equal + * @test: The KUnit test context + */ +static void dm_test_plane_layer_index_cmp_equal(struct kunit *test) +{ + struct dc_plane_state *plane_a; + struct dc_plane_state *plane_b; + struct dc_surface_update sa, sb; + + plane_a = kunit_kzalloc(test, sizeof(*plane_a), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_a); + plane_b = kunit_kzalloc(test, sizeof(*plane_b), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_b); + + plane_a->layer_index = 5; + plane_b->layer_index = 5; + sa.surface = plane_a; + sb.surface = plane_b; + + KUNIT_EXPECT_EQ(test, dm_plane_layer_index_cmp(&sa, &sb), 0); +} + +/** + * dm_test_plane_layer_index_cmp_descending - Test Plane layer index cmp descending + * @test: The KUnit test context + */ +static void dm_test_plane_layer_index_cmp_descending(struct kunit *test) +{ + struct dc_plane_state *plane_a; + struct dc_plane_state *plane_b; + struct dc_surface_update sa, sb; + + plane_a = kunit_kzalloc(test, sizeof(*plane_a), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_a); + plane_b = kunit_kzalloc(test, sizeof(*plane_b), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_b); + + plane_a->layer_index = 3; + plane_b->layer_index = 7; + sa.surface = plane_a; + sb.surface = plane_b; + + /* b has higher index, so cmp(a,b) = b - a > 0 (b sorts first) */ + KUNIT_EXPECT_GT(test, dm_plane_layer_index_cmp(&sa, &sb), 0); +} + +/** + * dm_test_plane_layer_index_cmp_ascending - Test Plane layer index cmp ascending + * @test: The KUnit test context + */ +static void dm_test_plane_layer_index_cmp_ascending(struct kunit *test) +{ + struct dc_plane_state *plane_a; + struct dc_plane_state *plane_b; + struct dc_surface_update sa, sb; + + plane_a = kunit_kzalloc(test, sizeof(*plane_a), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_a); + plane_b = kunit_kzalloc(test, sizeof(*plane_b), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_b); + + plane_a->layer_index = 9; + plane_b->layer_index = 2; + sa.surface = plane_a; + sb.surface = plane_b; + + /* a has higher index, so cmp(a,b) = b - a < 0 (a sorts first) */ + KUNIT_EXPECT_LT(test, dm_plane_layer_index_cmp(&sa, &sb), 0); +} + +/* Tests for fill_plane_color_attributes() */ + +/** + * dm_test_fill_color_attr_rgb_format - Test Fill color attr rgb format + * @test: The KUnit test context + */ +static void dm_test_fill_color_attr_rgb_format(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; + int ret; + + /* RGB format: should return 0 and set SRGB regardless of encoding */ + plane_state.color_encoding = DRM_COLOR_YCBCR_BT709; + plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; + + ret = fill_plane_color_attributes(&plane_state, + SURFACE_PIXEL_FORMAT_GRPH_ARGB8888, + &color_space); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)color_space, (int)COLOR_SPACE_SRGB); +} + +/** + * dm_test_fill_color_attr_bt601_full - Test Fill color attr bt601 full + * @test: The KUnit test context + */ +static void dm_test_fill_color_attr_bt601_full(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; + int ret; + + plane_state.color_encoding = DRM_COLOR_YCBCR_BT601; + plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; + + ret = fill_plane_color_attributes(&plane_state, + SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, + &color_space); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)color_space, (int)COLOR_SPACE_YCBCR601); +} + +/** + * dm_test_fill_color_attr_bt601_limited - Test Fill color attr bt601 limited + * @test: The KUnit test context + */ +static void dm_test_fill_color_attr_bt601_limited(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; + int ret; + + plane_state.color_encoding = DRM_COLOR_YCBCR_BT601; + plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; + + ret = fill_plane_color_attributes(&plane_state, + SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, + &color_space); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)color_space, + (int)COLOR_SPACE_YCBCR601_LIMITED); +} + +/** + * dm_test_fill_color_attr_bt709_full - Test Fill color attr bt709 full + * @test: The KUnit test context + */ +static void dm_test_fill_color_attr_bt709_full(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; + int ret; + + plane_state.color_encoding = DRM_COLOR_YCBCR_BT709; + plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; + + ret = fill_plane_color_attributes(&plane_state, + SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, + &color_space); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)color_space, (int)COLOR_SPACE_YCBCR709); +} + +/** + * dm_test_fill_color_attr_bt709_limited - Test Fill color attr bt709 limited + * @test: The KUnit test context + */ +static void dm_test_fill_color_attr_bt709_limited(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; + int ret; + + plane_state.color_encoding = DRM_COLOR_YCBCR_BT709; + plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; + + ret = fill_plane_color_attributes(&plane_state, + SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, + &color_space); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)color_space, + (int)COLOR_SPACE_YCBCR709_LIMITED); +} + +/** + * dm_test_fill_color_attr_bt2020_full - Test Fill color attr bt2020 full + * @test: The KUnit test context + */ +static void dm_test_fill_color_attr_bt2020_full(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; + int ret; + + plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020; + plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; + + ret = fill_plane_color_attributes(&plane_state, + SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, + &color_space); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)color_space, + (int)COLOR_SPACE_2020_YCBCR_FULL); +} + +/** + * dm_test_fill_color_attr_bt2020_limited - Test Fill color attr bt2020 limited + * @test: The KUnit test context + */ +static void dm_test_fill_color_attr_bt2020_limited(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; + int ret; + + plane_state.color_encoding = DRM_COLOR_YCBCR_BT2020; + plane_state.color_range = DRM_COLOR_YCBCR_LIMITED_RANGE; + + ret = fill_plane_color_attributes(&plane_state, + SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, + &color_space); + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_EQ(test, (int)color_space, + (int)COLOR_SPACE_2020_YCBCR_LIMITED); +} + +/** + * dm_test_fill_color_attr_invalid_encoding - Test Fill color attr invalid encoding + * @test: The KUnit test context + */ +static void dm_test_fill_color_attr_invalid_encoding(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + enum dc_color_space color_space = COLOR_SPACE_UNKNOWN; + int ret; + + plane_state.color_encoding = 99; + plane_state.color_range = DRM_COLOR_YCBCR_FULL_RANGE; + + ret = fill_plane_color_attributes(&plane_state, + SURFACE_PIXEL_FORMAT_VIDEO_420_YCbCr, + &color_space); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +/* Tests for modereset_required() */ + +/** + * dm_test_modereset_required_when_inactive_and_modeset - Test Modereset required when inactive and modeset + * @test: The KUnit test context + */ +static void dm_test_modereset_required_when_inactive_and_modeset(struct kunit *test) +{ + struct drm_crtc_state crtc_state = { 0 }; + + crtc_state.active = false; + crtc_state.mode_changed = true; + + KUNIT_EXPECT_TRUE(test, modereset_required(&crtc_state)); +} + +/** + * dm_test_modereset_not_required_when_active_and_modeset - Test Modereset not required when active and modeset + * @test: The KUnit test context + */ +static void dm_test_modereset_not_required_when_active_and_modeset(struct kunit *test) +{ + struct drm_crtc_state crtc_state = { 0 }; + + crtc_state.active = true; + crtc_state.mode_changed = true; + + KUNIT_EXPECT_FALSE(test, modereset_required(&crtc_state)); +} + +/** + * dm_test_modereset_not_required_when_inactive_without_modeset - Test Modereset not required when inactive without modeset + * @test: The KUnit test context + */ +static void dm_test_modereset_not_required_when_inactive_without_modeset(struct kunit *test) +{ + struct drm_crtc_state crtc_state = { 0 }; + + crtc_state.active = false; + crtc_state.mode_changed = false; + + KUNIT_EXPECT_FALSE(test, modereset_required(&crtc_state)); +} + +/* Tests for dm_get_oriented_plane_size() */ + +/** + * dm_test_oriented_plane_size_rotate_0 - Test Oriented plane size rotate 0 + * @test: The KUnit test context + */ +static void dm_test_oriented_plane_size_rotate_0(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int src_w = 0; + int src_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_0; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + + dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); + + KUNIT_EXPECT_EQ(test, src_w, 1920); + KUNIT_EXPECT_EQ(test, src_h, 1080); +} + +/** + * dm_test_oriented_plane_size_rotate_90 - Test Oriented plane size rotate 90 + * @test: The KUnit test context + */ +static void dm_test_oriented_plane_size_rotate_90(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int src_w = 0; + int src_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_90; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + + dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); + + KUNIT_EXPECT_EQ(test, src_w, 1080); + KUNIT_EXPECT_EQ(test, src_h, 1920); +} + +/** + * dm_test_oriented_plane_size_rotate_180 - Test Oriented plane size rotate 180 + * @test: The KUnit test context + */ +static void dm_test_oriented_plane_size_rotate_180(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int src_w = 0; + int src_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_180; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + + dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); + + KUNIT_EXPECT_EQ(test, src_w, 1920); + KUNIT_EXPECT_EQ(test, src_h, 1080); +} + +/** + * dm_test_oriented_plane_size_rotate_270 - Test Oriented plane size rotate 270 + * @test: The KUnit test context + */ +static void dm_test_oriented_plane_size_rotate_270(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int src_w = 0; + int src_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_270; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + + dm_get_oriented_plane_size(&plane_state, &src_w, &src_h); + + KUNIT_EXPECT_EQ(test, src_w, 1080); + KUNIT_EXPECT_EQ(test, src_h, 1920); +} + +/* Tests for dm_get_plane_scale() */ + +/** + * dm_test_get_plane_scale_identity - Test Get plane scale identity + * @test: The KUnit test context + */ +static void dm_test_get_plane_scale_identity(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int scale_w = 0; + int scale_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_0; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + plane_state.crtc_w = 1920; + plane_state.crtc_h = 1080; + + dm_get_plane_scale(&plane_state, &scale_w, &scale_h); + + KUNIT_EXPECT_EQ(test, scale_w, 1000); + KUNIT_EXPECT_EQ(test, scale_h, 1000); +} + +/** + * dm_test_get_plane_scale_rotate_90_identity - Test Get plane scale rotate 90 identity + * @test: The KUnit test context + */ +static void dm_test_get_plane_scale_rotate_90_identity(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int scale_w = 0; + int scale_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_90; + plane_state.src_w = 1920 << 16; + plane_state.src_h = 1080 << 16; + plane_state.crtc_w = 1080; + plane_state.crtc_h = 1920; + + dm_get_plane_scale(&plane_state, &scale_w, &scale_h); + + KUNIT_EXPECT_EQ(test, scale_w, 1000); + KUNIT_EXPECT_EQ(test, scale_h, 1000); +} + +/** + * dm_test_get_plane_scale_zero_src_width - Test Get plane scale zero src width + * @test: The KUnit test context + */ +static void dm_test_get_plane_scale_zero_src_width(struct kunit *test) +{ + struct drm_plane_state plane_state = { 0 }; + int scale_w = 0; + int scale_h = 0; + + plane_state.rotation = DRM_MODE_ROTATE_0; + plane_state.src_w = 0; + plane_state.src_h = 1080 << 16; + plane_state.crtc_w = 100; + plane_state.crtc_h = 200; + + dm_get_plane_scale(&plane_state, &scale_w, &scale_h); + + KUNIT_EXPECT_EQ(test, scale_w, 0); + KUNIT_EXPECT_EQ(test, scale_h, 185); +} + +/* Tests for is_scaling_state_different() */ + +/** + * dm_test_scaling_state_same - Test identical scaling states compare equal + * @test: The KUnit test context + */ +static void dm_test_scaling_state_same(struct kunit *test) +{ + struct dm_connector_state *a; + struct dm_connector_state *b; + + a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL); + b = kunit_kzalloc(test, sizeof(*b), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, a); + KUNIT_ASSERT_NOT_NULL(test, b); + + a->scaling = RMX_FULL; + a->underscan_enable = false; + *b = *a; + + KUNIT_EXPECT_FALSE(test, is_scaling_state_different(a, b)); +} + +/** + * dm_test_scaling_state_scaling_changed - Test differing scaling mode is detected + * @test: The KUnit test context + */ +static void dm_test_scaling_state_scaling_changed(struct kunit *test) +{ + struct dm_connector_state *a; + struct dm_connector_state *b; + + a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL); + b = kunit_kzalloc(test, sizeof(*b), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, a); + KUNIT_ASSERT_NOT_NULL(test, b); + + a->scaling = RMX_FULL; + b->scaling = RMX_CENTER; + + KUNIT_EXPECT_TRUE(test, is_scaling_state_different(a, b)); +} + +/** + * dm_test_scaling_state_underscan_enabled - Test enabling underscan with borders differs + * @test: The KUnit test context + */ +static void dm_test_scaling_state_underscan_enabled(struct kunit *test) +{ + struct dm_connector_state *old_state; + struct dm_connector_state *new_state; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, old_state); + KUNIT_ASSERT_NOT_NULL(test, new_state); + + /* new enables underscan with non-zero borders, old has it disabled */ + new_state->underscan_enable = true; + new_state->underscan_hborder = 16; + new_state->underscan_vborder = 16; + old_state->underscan_enable = false; + + KUNIT_EXPECT_TRUE(test, is_scaling_state_different(new_state, old_state)); +} + +/** + * dm_test_scaling_state_underscan_border_changed - Test changed underscan borders differ + * @test: The KUnit test context + */ +static void dm_test_scaling_state_underscan_border_changed(struct kunit *test) +{ + struct dm_connector_state *a; + struct dm_connector_state *b; + + a = kunit_kzalloc(test, sizeof(*a), GFP_KERNEL); + b = kunit_kzalloc(test, sizeof(*b), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, a); + KUNIT_ASSERT_NOT_NULL(test, b); + + a->underscan_enable = true; + a->underscan_hborder = 16; + a->underscan_vborder = 16; + *b = *a; + b->underscan_hborder = 32; + + KUNIT_EXPECT_TRUE(test, is_scaling_state_different(a, b)); +} + +/* Tests for is_timing_unchanged_for_freesync() */ + +/** + * dm_test_timing_unchanged_null_args - Test NULL crtc states return false + * @test: The KUnit test context + */ +static void dm_test_timing_unchanged_null_args(struct kunit *test) +{ + struct drm_crtc_state crtc_state = { 0 }; + + KUNIT_EXPECT_FALSE(test, + is_timing_unchanged_for_freesync(NULL, &crtc_state)); + KUNIT_EXPECT_FALSE(test, + is_timing_unchanged_for_freesync(&crtc_state, NULL)); +} + +/** + * dm_test_timing_unchanged_identical_modes - Test identical modes are not "unchanged" + * @test: The KUnit test context + * + * The helper only returns true when vtotal/vsync shift (vrr) while the rest + * of the timing stays fixed, so identical modes must return false. + */ +static void dm_test_timing_unchanged_identical_modes(struct kunit *test) +{ + struct drm_crtc_state old_state = { 0 }; + struct drm_crtc_state new_state = { 0 }; + + old_state.mode.clock = 148500; + old_state.mode.hdisplay = 1920; + old_state.mode.vdisplay = 1080; + old_state.mode.htotal = 2200; + old_state.mode.vtotal = 1125; + new_state.mode = old_state.mode; + + KUNIT_EXPECT_FALSE(test, + is_timing_unchanged_for_freesync(&old_state, &new_state)); +} + +/** + * dm_test_timing_unchanged_vrr_shift - Test vrr-style vtotal/vsync shift is detected + * @test: The KUnit test context + */ +static void dm_test_timing_unchanged_vrr_shift(struct kunit *test) +{ + struct drm_crtc_state old_state = { 0 }; + struct drm_crtc_state new_state = { 0 }; + + old_state.mode.clock = 148500; + old_state.mode.hdisplay = 1920; + old_state.mode.vdisplay = 1080; + old_state.mode.htotal = 2200; + old_state.mode.vtotal = 1125; + old_state.mode.hsync_start = 2008; + old_state.mode.vsync_start = 1084; + old_state.mode.hsync_end = 2052; + old_state.mode.vsync_end = 1089; + + /* Same horizontal timing, vertical totals/sync shifted by 125 lines */ + new_state.mode = old_state.mode; + new_state.mode.vtotal = 1250; + new_state.mode.vsync_start = 1209; + new_state.mode.vsync_end = 1214; + + KUNIT_EXPECT_TRUE(test, + is_timing_unchanged_for_freesync(&old_state, &new_state)); +} + +/** + * dm_test_timing_unchanged_clock_changed - Test pixel clock change returns false + * @test: The KUnit test context + */ +static void dm_test_timing_unchanged_clock_changed(struct kunit *test) +{ + struct drm_crtc_state old_state = { 0 }; + struct drm_crtc_state new_state = { 0 }; + + old_state.mode.clock = 148500; + old_state.mode.htotal = 2200; + old_state.mode.vtotal = 1125; + old_state.mode.vsync_start = 1084; + old_state.mode.vsync_end = 1089; + + new_state.mode = old_state.mode; + new_state.mode.clock = 297000; + new_state.mode.vtotal = 1250; + new_state.mode.vsync_start = 1209; + new_state.mode.vsync_end = 1214; + + KUNIT_EXPECT_FALSE(test, + is_timing_unchanged_for_freesync(&old_state, &new_state)); +} + +/* Tests for set_freesync_fixed_config() */ + +/** + * dm_test_set_freesync_fixed_config_60hz - Test fixed refresh computed for 1080p60 + * @test: The KUnit test context + */ +static void dm_test_set_freesync_fixed_config_60hz(struct kunit *test) +{ + struct dm_crtc_state dm_crtc_state = { 0 }; + + dm_crtc_state.base.mode.clock = 148500; + dm_crtc_state.base.mode.htotal = 2200; + dm_crtc_state.base.mode.vtotal = 1125; + + set_freesync_fixed_config(&dm_crtc_state); + + KUNIT_EXPECT_EQ(test, (int)dm_crtc_state.freesync_config.state, + (int)VRR_STATE_ACTIVE_FIXED); + /* 148500 kHz / (2200 * 1125) = 60 Hz = 60000000 uHz */ + KUNIT_EXPECT_EQ(test, dm_crtc_state.freesync_config.fixed_refresh_in_uhz, + 60000000U); +} + +/* Tests for is_dc_timing_adjust_needed() */ + +/** + * dm_test_dc_timing_adjust_pending - Test a pending hw timing adjust forces true + * @test: The KUnit test context + */ +static void dm_test_dc_timing_adjust_pending(struct kunit *test) +{ + struct dm_crtc_state *old_state, *new_state; + struct dc_stream_state *stream; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + new_state->stream = stream; + stream->adjust.timing_adjust_pending = 1; + + KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state)); +} + +/** + * dm_test_dc_timing_adjust_active_fixed - Test VRR active-fixed forces true + * @test: The KUnit test context + */ +static void dm_test_dc_timing_adjust_active_fixed(struct kunit *test) +{ + struct dm_crtc_state *old_state, *new_state; + struct dc_stream_state *stream; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + new_state->stream = stream; + new_state->freesync_config.state = VRR_STATE_ACTIVE_FIXED; + + KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state)); +} + +/** + * dm_test_dc_timing_adjust_vrr_toggle - Test a change in vrr active state forces true + * @test: The KUnit test context + */ +static void dm_test_dc_timing_adjust_vrr_toggle(struct kunit *test) +{ + struct dm_crtc_state *old_state, *new_state; + struct dc_stream_state *stream; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + new_state->stream = stream; + old_state->freesync_config.state = VRR_STATE_ACTIVE_VARIABLE; + new_state->freesync_config.state = VRR_STATE_INACTIVE; + + KUNIT_EXPECT_TRUE(test, is_dc_timing_adjust_needed(old_state, new_state)); +} + +/** + * dm_test_dc_timing_adjust_not_needed - Test steady-state timing needs no adjust + * @test: The KUnit test context + */ +static void dm_test_dc_timing_adjust_not_needed(struct kunit *test) +{ + struct dm_crtc_state *old_state, *new_state; + struct dc_stream_state *stream; + + old_state = kunit_kzalloc(test, sizeof(*old_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, old_state); + new_state = kunit_kzalloc(test, sizeof(*new_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, new_state); + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + new_state->stream = stream; + old_state->freesync_config.state = VRR_STATE_INACTIVE; + new_state->freesync_config.state = VRR_STATE_INACTIVE; + + KUNIT_EXPECT_FALSE(test, is_dc_timing_adjust_needed(old_state, new_state)); +} + +/* Tests for set_multisync_trigger_params() */ + +/** + * dm_test_multisync_trigger_disabled - Test disabled reset leaves params untouched + * @test: The KUnit test context + */ +static void dm_test_multisync_trigger_disabled(struct kunit *test) +{ + struct dc_stream_state *stream; + + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + + stream->triggered_crtc_reset.enabled = false; + stream->triggered_crtc_reset.event = CRTC_EVENT_VSYNC_FALLING; + stream->triggered_crtc_reset.delay = TRIGGER_DELAY_NEXT_LINE; + + set_multisync_trigger_params(stream); + + /* Nothing should change when the reset trigger is disabled */ + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event, + (int)CRTC_EVENT_VSYNC_FALLING); + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay, + (int)TRIGGER_DELAY_NEXT_LINE); +} + +/** + * dm_test_multisync_trigger_rising - Test positive vsync polarity selects rising edge + * @test: The KUnit test context + */ +static void dm_test_multisync_trigger_rising(struct kunit *test) +{ + struct dc_stream_state *stream; + struct dc_stream_state *master; + + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master); + + master->timing.flags.VSYNC_POSITIVE_POLARITY = 1; + stream->triggered_crtc_reset.enabled = true; + stream->triggered_crtc_reset.event_source = master; + + set_multisync_trigger_params(stream); + + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event, + (int)CRTC_EVENT_VSYNC_RISING); + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay, + (int)TRIGGER_DELAY_NEXT_PIXEL); +} + +/** + * dm_test_multisync_trigger_falling - Test negative vsync polarity selects falling edge + * @test: The KUnit test context + */ +static void dm_test_multisync_trigger_falling(struct kunit *test) +{ + struct dc_stream_state *stream; + struct dc_stream_state *master; + + stream = kunit_kzalloc(test, sizeof(*stream), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream); + master = kunit_kzalloc(test, sizeof(*master), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, master); + + master->timing.flags.VSYNC_POSITIVE_POLARITY = 0; + stream->triggered_crtc_reset.enabled = true; + stream->triggered_crtc_reset.event_source = master; + + set_multisync_trigger_params(stream); + + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.event, + (int)CRTC_EVENT_VSYNC_FALLING); + KUNIT_EXPECT_EQ(test, (int)stream->triggered_crtc_reset.delay, + (int)TRIGGER_DELAY_NEXT_PIXEL); +} + +/* Tests for set_master_stream() */ + +/** + * dm_test_master_stream_highest_refresh - Test highest refresh-rate stream becomes master + * @test: The KUnit test context + */ +static void dm_test_master_stream_highest_refresh(struct kunit *test) +{ + struct dc_stream_state *stream0, *stream1; + struct dc_stream_state *stream_set[2]; + + stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0); + stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1); + stream_set[0] = stream0; + stream_set[1] = stream1; + + /* stream0: 60Hz, stream1: 120Hz -> stream1 is master */ + stream0->triggered_crtc_reset.enabled = true; + stream0->timing.pix_clk_100hz = 1485000; + stream0->timing.h_total = 2200; + stream0->timing.v_total = 1125; + + stream1->triggered_crtc_reset.enabled = true; + stream1->timing.pix_clk_100hz = 2970000; + stream1->timing.h_total = 2200; + stream1->timing.v_total = 1125; + + set_master_stream(stream_set, 2); + + KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source, + stream1); + KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source, + stream1); +} + +/** + * dm_test_master_stream_defaults_to_first - Test default master when none triggered + * @test: The KUnit test context + * + * When no stream has the reset trigger enabled, master_stream stays 0 and all + * streams point at the first stream as their event source. + */ +static void dm_test_master_stream_defaults_to_first(struct kunit *test) +{ + struct dc_stream_state *stream0, *stream1; + struct dc_stream_state *stream_set[2]; + + stream0 = kunit_kzalloc(test, sizeof(*stream0), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream0); + stream1 = kunit_kzalloc(test, sizeof(*stream1), GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, stream1); + stream_set[0] = stream0; + stream_set[1] = stream1; + + set_master_stream(stream_set, 2); + + KUNIT_EXPECT_PTR_EQ(test, stream0->triggered_crtc_reset.event_source, + stream0); + KUNIT_EXPECT_PTR_EQ(test, stream1->triggered_crtc_reset.event_source, + stream0); +} + +static struct kunit_case amdgpu_dm_tests[] = { + /* dm_plane_layer_index_cmp */ + KUNIT_CASE(dm_test_plane_layer_index_cmp_equal), + KUNIT_CASE(dm_test_plane_layer_index_cmp_descending), + KUNIT_CASE(dm_test_plane_layer_index_cmp_ascending), + /* fill_plane_color_attributes */ + KUNIT_CASE(dm_test_fill_color_attr_rgb_format), + KUNIT_CASE(dm_test_fill_color_attr_bt601_full), + KUNIT_CASE(dm_test_fill_color_attr_bt601_limited), + KUNIT_CASE(dm_test_fill_color_attr_bt709_full), + KUNIT_CASE(dm_test_fill_color_attr_bt709_limited), + KUNIT_CASE(dm_test_fill_color_attr_bt2020_full), + KUNIT_CASE(dm_test_fill_color_attr_bt2020_limited), + KUNIT_CASE(dm_test_fill_color_attr_invalid_encoding), + /* modereset_required */ + KUNIT_CASE(dm_test_modereset_required_when_inactive_and_modeset), + KUNIT_CASE(dm_test_modereset_not_required_when_active_and_modeset), + KUNIT_CASE(dm_test_modereset_not_required_when_inactive_without_modeset), + /* dm_get_oriented_plane_size */ + KUNIT_CASE(dm_test_oriented_plane_size_rotate_0), + KUNIT_CASE(dm_test_oriented_plane_size_rotate_90), + KUNIT_CASE(dm_test_oriented_plane_size_rotate_180), + KUNIT_CASE(dm_test_oriented_plane_size_rotate_270), + /* dm_get_plane_scale */ + KUNIT_CASE(dm_test_get_plane_scale_identity), + KUNIT_CASE(dm_test_get_plane_scale_rotate_90_identity), + KUNIT_CASE(dm_test_get_plane_scale_zero_src_width), + /* is_scaling_state_different */ + KUNIT_CASE(dm_test_scaling_state_same), + KUNIT_CASE(dm_test_scaling_state_scaling_changed), + KUNIT_CASE(dm_test_scaling_state_underscan_enabled), + KUNIT_CASE(dm_test_scaling_state_underscan_border_changed), + /* is_timing_unchanged_for_freesync */ + KUNIT_CASE(dm_test_timing_unchanged_null_args), + KUNIT_CASE(dm_test_timing_unchanged_identical_modes), + KUNIT_CASE(dm_test_timing_unchanged_vrr_shift), + KUNIT_CASE(dm_test_timing_unchanged_clock_changed), + /* set_freesync_fixed_config */ + KUNIT_CASE(dm_test_set_freesync_fixed_config_60hz), + /* is_dc_timing_adjust_needed */ + KUNIT_CASE(dm_test_dc_timing_adjust_pending), + KUNIT_CASE(dm_test_dc_timing_adjust_active_fixed), + KUNIT_CASE(dm_test_dc_timing_adjust_vrr_toggle), + KUNIT_CASE(dm_test_dc_timing_adjust_not_needed), + /* set_multisync_trigger_params */ + KUNIT_CASE(dm_test_multisync_trigger_disabled), + KUNIT_CASE(dm_test_multisync_trigger_rising), + KUNIT_CASE(dm_test_multisync_trigger_falling), + /* set_master_stream */ + KUNIT_CASE(dm_test_master_stream_highest_refresh), + KUNIT_CASE(dm_test_master_stream_defaults_to_first), + {} +}; + +static struct kunit_suite amdgpu_dm_test_suite = { + .name = "amdgpu_dm", + .test_cases = amdgpu_dm_tests, +}; + +kunit_test_suite(amdgpu_dm_test_suite); + +MODULE_AUTHOR("AMD"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c new file mode 100644 index 000000000000..c71f61a2438d --- /dev/null +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/tests/amdgpu_dm_wb_test.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * KUnit tests for amdgpu_dm_wb.c + * + * Copyright 2026 Advanced Micro Devices, Inc. + */ + +#include <kunit/test.h> + +#include <drm/drm_atomic_state_helper.h> +#include <drm/drm_connector.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_kunit_helpers.h> +#include <drm/drm_mode.h> +#include <drm/drm_modes.h> +#include <drm/drm_writeback.h> + +#include "dc.h" +#include "amdgpu.h" +#include "amdgpu_dm.h" +#include "amdgpu_dm_wb.h" +#include "amdgpu_dm_kunit_test_helpers.h" + + +/* Helper functions */ + +static struct drm_crtc_state *alloc_test_crtc_state(struct kunit *test, + int hdisplay, int vdisplay) +{ + struct drm_crtc_state *crtc_state; + + crtc_state = kunit_kzalloc(test, sizeof(*crtc_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, crtc_state); + + crtc_state->mode.hdisplay = hdisplay; + crtc_state->mode.vdisplay = vdisplay; + + return crtc_state; +} + +static struct drm_connector_state *alloc_test_conn_state(struct kunit *test, + int fb_width, + int fb_height, + u32 format) +{ + struct drm_connector_state *conn_state; + struct drm_writeback_job *job; + struct drm_framebuffer *fb; + struct drm_format_info *fmt_info; + + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + job = kunit_kzalloc(test, sizeof(*job), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, job); + + fb = kunit_kzalloc(test, sizeof(*fb), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fb); + + fmt_info = kunit_kzalloc(test, sizeof(*fmt_info), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, fmt_info); + + fb->width = fb_width; + fb->height = fb_height; + fmt_info->format = format; + fb->format = fmt_info; + + job->fb = fb; + conn_state->writeback_job = job; + + return conn_state; +} + + + +/* Tests for amdgpu_dm_wb_encoder_atomic_check */ + +/** + * dm_test_wb_atomic_check_no_job - Verify early return when no writeback job + * @test: KUnit test context + * + * When conn_state->writeback_job is NULL, no writeback is requested and the + * function should return 0 without further validation. + */ +static void dm_test_wb_atomic_check_no_job(struct kunit *test) +{ + struct drm_crtc_state *crtc_state; + struct drm_connector_state *conn_state; + int ret; + + crtc_state = alloc_test_crtc_state(test, 1920, 1080); + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + /* No writeback_job — should return 0 */ + conn_state->writeback_job = NULL; + ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +/** + * dm_test_wb_atomic_check_no_fb - Verify early return when job has no framebuffer + * @test: KUnit test context + * + * When a writeback job exists but job->fb is NULL, the function should return 0 + * without validating dimensions or pixel format. + */ +static void dm_test_wb_atomic_check_no_fb(struct kunit *test) +{ + struct drm_crtc_state *crtc_state; + struct drm_connector_state *conn_state; + struct drm_writeback_job *job; + int ret; + + crtc_state = alloc_test_crtc_state(test, 1920, 1080); + conn_state = kunit_kzalloc(test, sizeof(*conn_state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, conn_state); + + job = kunit_kzalloc(test, sizeof(*job), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, job); + + /* writeback_job exists but no fb — should return 0 */ + job->fb = NULL; + conn_state->writeback_job = job; + ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +/** + * dm_test_wb_atomic_check_valid - Verify success with matching size and supported format + * @test: KUnit test context + * + * When the framebuffer dimensions match the CRTC mode and the pixel format is + * in the supported formats list, the function should return 0. + */ +static void dm_test_wb_atomic_check_valid(struct kunit *test) +{ + struct drm_crtc_state *crtc_state; + struct drm_connector_state *conn_state; + int ret; + + crtc_state = alloc_test_crtc_state(test, 1920, 1080); + conn_state = alloc_test_conn_state(test, 1920, 1080, + DRM_FORMAT_XRGB2101010); + + ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +/** + * dm_test_wb_atomic_check_size_mismatch - Verify rejection when both dimensions differ + * @test: KUnit test context + * + * When both framebuffer width and height differ from the CRTC mode, the + * function should return -EINVAL. + */ +static void dm_test_wb_atomic_check_size_mismatch(struct kunit *test) +{ + struct drm_crtc_state *crtc_state; + struct drm_connector_state *conn_state; + int ret; + + /* FB is 3840x2160 but mode is 1920x1080 */ + crtc_state = alloc_test_crtc_state(test, 1920, 1080); + conn_state = alloc_test_conn_state(test, 3840, 2160, + DRM_FORMAT_XRGB2101010); + + ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +/** + * dm_test_wb_atomic_check_width_mismatch - Verify rejection when width alone differs + * @test: KUnit test context + * + * When only the framebuffer width differs from the CRTC mode hdisplay, the + * function should return -EINVAL. + */ +static void dm_test_wb_atomic_check_width_mismatch(struct kunit *test) +{ + struct drm_crtc_state *crtc_state; + struct drm_connector_state *conn_state; + int ret; + + /* Width doesn't match */ + crtc_state = alloc_test_crtc_state(test, 1920, 1080); + conn_state = alloc_test_conn_state(test, 1280, 1080, + DRM_FORMAT_XRGB2101010); + + ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +/** + * dm_test_wb_atomic_check_height_mismatch - Verify rejection when height alone differs + * @test: KUnit test context + * + * When only the framebuffer height differs from the CRTC mode vdisplay, the + * function should return -EINVAL. + */ +static void dm_test_wb_atomic_check_height_mismatch(struct kunit *test) +{ + struct drm_crtc_state *crtc_state; + struct drm_connector_state *conn_state; + int ret; + + /* Height doesn't match */ + crtc_state = alloc_test_crtc_state(test, 1920, 1080); + conn_state = alloc_test_conn_state(test, 1920, 720, + DRM_FORMAT_XRGB2101010); + + ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +/** + * dm_test_wb_atomic_check_invalid_format - Verify rejection of unsupported pixel format + * @test: KUnit test context + * + * When the framebuffer dimensions match but the pixel format is not in + * amdgpu_dm_wb_formats[], the function should return -EINVAL. + */ +static void dm_test_wb_atomic_check_invalid_format(struct kunit *test) +{ + struct drm_crtc_state *crtc_state; + struct drm_connector_state *conn_state; + int ret; + + /* Correct size but unsupported format */ + crtc_state = alloc_test_crtc_state(test, 1920, 1080); + conn_state = alloc_test_conn_state(test, 1920, 1080, + DRM_FORMAT_XRGB8888); + + ret = amdgpu_dm_wb_encoder_atomic_check(NULL, crtc_state, conn_state); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +/* Tests for amdgpu_dm_wb_connector_get_modes using DRM mock */ + +static const struct drm_connector_funcs dm_wb_test_connector_funcs = { + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .reset = drm_atomic_helper_connector_reset, +}; + +/** + * dm_test_wb_get_modes_returns_modes - Verify at least one mode is returned + * @test: KUnit test context + * + * Uses a DRM mock connector to verify that amdgpu_dm_wb_connector_get_modes() + * populates the connector with at least one display mode. + */ +static void dm_test_wb_get_modes_returns_modes(struct kunit *test) +{ + struct device *dev; + struct drm_device *drm; + struct drm_connector *connector; + int count; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, + sizeof(*drm), 0, + DRIVER_MODESET | DRIVER_ATOMIC); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, connector); + + drmm_connector_init(drm, connector, &dm_wb_test_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL, NULL); + + count = amdgpu_dm_wb_connector_get_modes(connector); + + /* drm_add_modes_noedid should return at least one mode */ + KUNIT_EXPECT_GT(test, count, 0); +} + +/** + * dm_test_wb_get_modes_bounded_by_max - Verify all modes are within max resolution + * @test: KUnit test context + * + * Uses a DRM mock connector to verify that all modes returned by + * amdgpu_dm_wb_connector_get_modes() have hdisplay <= 3840 and + * vdisplay <= 2160, matching the DWB hardware maximum. + */ +static void dm_test_wb_get_modes_bounded_by_max(struct kunit *test) +{ + struct device *dev; + struct drm_device *drm; + struct drm_connector *connector; + struct drm_display_mode *mode; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, + sizeof(*drm), 0, + DRIVER_MODESET | DRIVER_ATOMIC); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + connector = kunit_kzalloc(test, sizeof(*connector), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, connector); + + drmm_connector_init(drm, connector, &dm_wb_test_connector_funcs, + DRM_MODE_CONNECTOR_VIRTUAL, NULL); + + amdgpu_dm_wb_connector_get_modes(connector); + + /* All modes must fit within 3840x2160 */ + list_for_each_entry(mode, &connector->probed_modes, head) { + KUNIT_EXPECT_LE(test, mode->hdisplay, 3840); + KUNIT_EXPECT_LE(test, mode->vdisplay, 2160); + } +} + +/* Tests for amdgpu_dm_wb_connector_init using DRM mock */ + +/** + * dm_test_wb_connector_init_success - Verify writeback connector initialization + * @test: KUnit test context + * + * Uses a DRM mock device embedded in struct amdgpu_device to verify that + * amdgpu_dm_wb_connector_init() initializes the writeback connector, stores + * the DC link, installs connector state through reset, and wires the expected + * DRM callbacks. + */ +static void dm_test_wb_connector_init_success(struct kunit *test) +{ + struct amdgpu_dm_wb_connector *wbcon; + struct amdgpu_display_manager *dm; + struct amdgpu_device *adev; + struct dc_link *link; + struct dc *dc; + int ret; + + adev = dm_kunit_alloc_adev(test); + adev->mode_info.num_crtc = 1; + dm = &adev->dm; + dm->adev = adev; + + dc = kunit_kzalloc(test, sizeof(*dc), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dc); + + link = kunit_kzalloc(test, sizeof(*link), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, link); + + dc->links[0] = link; + dm->dc = dc; + + wbcon = kunit_kzalloc(test, sizeof(*wbcon), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, wbcon); + + ret = amdgpu_dm_wb_connector_init(dm, wbcon, 0); + + KUNIT_EXPECT_EQ(test, ret, 0); + KUNIT_EXPECT_PTR_EQ(test, wbcon->link, link); + KUNIT_EXPECT_TRUE(test, wbcon->base.base.funcs != NULL); + KUNIT_EXPECT_TRUE(test, wbcon->base.base.helper_private != NULL); + KUNIT_EXPECT_TRUE(test, wbcon->base.base.state != NULL); + KUNIT_EXPECT_TRUE(test, wbcon->base.encoder.funcs != NULL); + KUNIT_EXPECT_EQ(test, wbcon->base.encoder.possible_crtcs, 0x1); +} + +static struct kunit_case dm_wb_test_cases[] = { + /* amdgpu_dm_wb_encoder_atomic_check */ + KUNIT_CASE(dm_test_wb_atomic_check_no_job), + KUNIT_CASE(dm_test_wb_atomic_check_no_fb), + KUNIT_CASE(dm_test_wb_atomic_check_valid), + KUNIT_CASE(dm_test_wb_atomic_check_size_mismatch), + KUNIT_CASE(dm_test_wb_atomic_check_width_mismatch), + KUNIT_CASE(dm_test_wb_atomic_check_height_mismatch), + KUNIT_CASE(dm_test_wb_atomic_check_invalid_format), + /* amdgpu_dm_wb_connector_get_modes */ + KUNIT_CASE(dm_test_wb_get_modes_returns_modes), + KUNIT_CASE(dm_test_wb_get_modes_bounded_by_max), + /* amdgpu_dm_wb_connector_init */ + KUNIT_CASE(dm_test_wb_connector_init_success), + {} +}; + +static struct kunit_suite dm_wb_test_suite = { + .name = "amdgpu_dm_wb", + .test_cases = dm_wb_test_cases, +}; + +kunit_test_suite(dm_wb_test_suite); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("KUnit tests for amdgpu_dm_wb"); diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c index 4a60c5f54a04..6ee3da89d058 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.c @@ -10,6 +10,7 @@ #include "dcn31/dcn31_clk_mgr.h" #include "dcn32/dcn32_clk_mgr.h" #include "dcn401/dcn401_clk_mgr.h" +#include "hw_sequencer.h" #include "reg_helper.h" #include "core_types.h" #include "dm_helpers.h" @@ -1085,7 +1086,8 @@ static unsigned int dcn401_build_update_display_clocks_sequence( struct clk_mgr *clk_mgr_base, struct dc_state *context, struct dc_clocks *new_clocks, - bool safe_to_lower) + bool safe_to_lower, + unsigned int num_steps_start) { struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); struct dcn401_clk_mgr *clk_mgr401 = TO_DCN401_CLK_MGR(clk_mgr_internal); @@ -1100,7 +1102,7 @@ static unsigned int dcn401_build_update_display_clocks_sequence( bool frl_present = false; unsigned int i; - unsigned int num_steps = 0; + unsigned int num_steps = num_steps_start; /* CLK_MGR401_READ_CLOCKS_FROM_DENTIST */ if (clk_mgr_base->clks.dispclk_khz == 0 || @@ -1239,6 +1241,44 @@ static unsigned int dcn401_build_update_display_clocks_sequence( return num_steps; } +/* + * Build-for-BLS functions. + * These build both bandwidth and display clock sequences into the clk_mgr's + * internal block sequence array, then add a single CLK_MGR_UPDATE_CLOCKS step + * to the HWSS block sequence whose executor will call + * execute_clk_mgr_block_sequence to dispatch all accumulated steps. + */ +void dcn401_build_clock_update_for_bls( + struct clk_mgr *clk_mgr_base, + struct dc_state *context, + bool safe_to_lower, + struct block_sequence_state *seq_state) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dcn401_clk_mgr *clk_mgr401 = TO_DCN401_CLK_MGR(clk_mgr_internal); + unsigned int num_bw_steps; + unsigned int total_steps; + + /* Build bandwidth clocks sequence starting at index 0 */ + num_bw_steps = dcn401_build_update_bandwidth_clocks_sequence(clk_mgr_base, + context, + &context->bw_ctx.bw.dcn.clk, + safe_to_lower); + + /* Build display clocks sequence appended after bandwidth steps */ + total_steps = dcn401_build_update_display_clocks_sequence(clk_mgr_base, + context, + &context->bw_ctx.bw.dcn.clk, + safe_to_lower, + num_bw_steps); + + /* Store total step count for the executor */ + clk_mgr401->num_block_sequence_steps = total_steps; + + /* Add single HWSS step that will execute all clk_mgr block sequence steps */ + hwss_add_clk_mgr_update_clocks(seq_state, clk_mgr_base); +} + static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base, struct dc_state *context, bool safe_to_lower) @@ -1260,7 +1300,8 @@ static void dcn401_update_clocks(struct clk_mgr *clk_mgr_base, num_steps = dcn401_build_update_display_clocks_sequence(clk_mgr_base, context, &context->bw_ctx.bw.dcn.clk, - safe_to_lower); + safe_to_lower, + 0); /* execute sequence */ dcn401_execute_block_sequence(clk_mgr_base, num_steps); @@ -1549,6 +1590,14 @@ unsigned int dcn401_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_typ return 0; } +static void dcn401_execute_clk_mgr_block_sequence_bls(struct clk_mgr *clk_mgr_base) +{ + struct clk_mgr_internal *clk_mgr_internal = TO_CLK_MGR_INTERNAL(clk_mgr_base); + struct dcn401_clk_mgr *clk_mgr401 = TO_DCN401_CLK_MGR(clk_mgr_internal); + + dcn401_execute_block_sequence(clk_mgr_base, clk_mgr401->num_block_sequence_steps); +} + static struct clk_mgr_funcs dcn401_funcs = { .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, .get_dtb_ref_clk_frequency = dcn401_get_dtb_ref_freq_khz, @@ -1566,6 +1615,8 @@ static struct clk_mgr_funcs dcn401_funcs = { .get_hard_min_fclk = dcn401_get_hard_min_fclk, .is_dc_mode_present = dcn401_is_dc_mode_present, .get_max_clock_khz = dcn401_get_max_clock_khz, + .build_clock_update_for_bls = dcn401_build_clock_update_for_bls, + .execute_clk_mgr_block_sequence = dcn401_execute_clk_mgr_block_sequence_bls, }; struct clk_mgr_internal *dcn401_clk_mgr_construct( diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h index 370d2ddd6064..d4cd69a5a8dd 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn401/dcn401_clk_mgr.h @@ -102,6 +102,7 @@ struct dcn401_clk_mgr { struct clk_mgr_internal base; struct dcn401_clk_mgr_block_sequence block_sequence[DCN401_CLK_MGR_MAX_SEQUENCE_SIZE]; + unsigned int num_block_sequence_steps; }; void dcn401_init_clocks(struct clk_mgr *clk_mgr_base); @@ -114,4 +115,12 @@ void dcn401_clk_mgr_destroy(struct clk_mgr_internal *clk_mgr); unsigned int dcn401_get_max_clock_khz(struct clk_mgr *clk_mgr_base, enum clk_type clk_type); +struct block_sequence_state; + +void dcn401_build_clock_update_for_bls( + struct clk_mgr *clk_mgr_base, + struct dc_state *context, + bool safe_to_lower, + struct block_sequence_state *seq_state); + #endif /* __DCN401_CLK_MGR_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index bcdbf3471039..28339e4b6d67 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1509,7 +1509,7 @@ static void disable_vbios_mode_if_required( struct dc *dc_create(const struct dc_init_data *init_params) { - struct dc *dc = kzalloc_obj(*dc); + struct dc *dc = kvzalloc_obj(*dc); unsigned int full_pipe_count; if (!dc) @@ -1557,7 +1557,7 @@ struct dc *dc_create(const struct dc_init_data *init_params) destruct_dc: dc_destruct(dc); - kfree(dc); + kvfree(dc); return NULL; } @@ -1606,7 +1606,7 @@ void dc_deinit_callbacks(struct dc *dc) void dc_destroy(struct dc **dc) { dc_destruct(*dc); - kfree(*dc); + kvfree(*dc); *dc = NULL; } @@ -2310,7 +2310,7 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c for (i = 0; i < context->stream_count; i++) { uint32_t prev_dsc_changed = context->streams[i]->update_flags.bits.dsc_changed; - context->streams[i]->update_flags.raw = 0xFFFFFFFF; + stream_update_flags_set_full(&context->streams[i]->update_flags); context->streams[i]->update_flags.bits.dsc_changed = prev_dsc_changed; } @@ -2416,7 +2416,7 @@ static enum dc_status dc_commit_state_no_check(struct dc *dc, struct dc_state *c /* Clear update flags that were set earlier to avoid redundant programming */ for (i = 0; i < context->stream_count; i++) { - context->streams[i]->update_flags.raw = 0x0; + stream_update_flags_clear(&context->streams[i]->update_flags); } old_state = dc->current_state; @@ -2764,7 +2764,7 @@ static bool is_surface_in_context( static struct surface_update_descriptor get_plane_info_update_type(const struct dc_surface_update *u) { - union surface_update_flags *update_flags = &u->surface->update_flags; + struct pipe_update_bits *update_bits = &u->surface->update_bits; struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; if (!u->plane_info) @@ -2774,37 +2774,37 @@ static struct surface_update_descriptor get_plane_info_update_type(const struct elevate_update_type(&update_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); if (u->plane_info->color_space != u->surface->color_space) { - update_flags->bits.color_space_change = 1; + update_bits->color_space_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); } if (u->plane_info->horizontal_mirror != u->surface->horizontal_mirror) { - update_flags->bits.horizontal_mirror_change = 1; + update_bits->horizontal_mirror_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); } if (u->plane_info->rotation != u->surface->rotation) { - update_flags->bits.rotation_change = 1; + update_bits->rotation_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } if (u->plane_info->format != u->surface->format) { - update_flags->bits.pixel_format_change = 1; + update_bits->pixel_format_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } if (u->plane_info->stereo_format != u->surface->stereo_format) { - update_flags->bits.stereo_format_change = 1; + update_bits->stereo_format_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } if (u->plane_info->per_pixel_alpha != u->surface->per_pixel_alpha) { - update_flags->bits.per_pixel_alpha_change = 1; + update_bits->per_pixel_alpha_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); } if (u->plane_info->global_alpha_value != u->surface->global_alpha_value) { - update_flags->bits.global_alpha_change = 1; + update_bits->global_alpha_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); } @@ -2816,7 +2816,7 @@ static struct surface_update_descriptor get_plane_info_update_type(const struct * stutter period calculation. Triggering a full update will * recalculate stutter period. */ - update_flags->bits.dcc_change = 1; + update_bits->dcc_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } @@ -2825,25 +2825,25 @@ static struct surface_update_descriptor get_plane_info_update_type(const struct /* different bytes per element will require full bandwidth * and DML calculation */ - update_flags->bits.bpp_change = 1; + update_bits->bpp_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } if (u->plane_info->plane_size.surface_pitch != u->surface->plane_size.surface_pitch || u->plane_info->plane_size.chroma_pitch != u->surface->plane_size.chroma_pitch) { - update_flags->bits.plane_size_change = 1; + update_bits->plane_size_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); } const struct dc_tiling_info *tiling = &u->plane_info->tiling_info; if (memcmp(tiling, &u->surface->tiling_info, sizeof(*tiling)) != 0) { - update_flags->bits.swizzle_change = 1; + update_bits->swizzle_change = 1; if (tiling->flags.avoid_full_update_on_tiling_change) { elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); } else { - update_flags->bits.bandwidth_change = 1; + update_bits->bandwidth_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } } @@ -2853,10 +2853,10 @@ static struct surface_update_descriptor get_plane_info_update_type(const struct } static struct surface_update_descriptor get_scaling_info_update_type( - const struct dc_check_config *check_config, - const struct dc_surface_update *u) + const struct dc_check_config *check_config, + const struct dc_surface_update *u) { - union surface_update_flags *update_flags = &u->surface->update_flags; + struct pipe_update_bits *update_bits = &u->surface->update_bits; struct surface_update_descriptor update_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; if (!u->scaling_info) @@ -2873,26 +2873,26 @@ static struct surface_update_descriptor get_scaling_info_update_type( || u->scaling_info->clip_rect.height != u->surface->clip_rect.height || u->scaling_info->scaling_quality.integer_scaling != u->surface->scaling_quality.integer_scaling) { - update_flags->bits.scaling_change = 1; + update_bits->scaling_change = 1; elevate_update_type(&update_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); if (u->scaling_info->src_rect.width > u->surface->src_rect.width || u->scaling_info->src_rect.height > u->surface->src_rect.height) /* Making src rect bigger requires a bandwidth change */ - update_flags->bits.clock_change = 1; + update_bits->clock_change = 1; if ((u->scaling_info->dst_rect.width < u->surface->dst_rect.width || u->scaling_info->dst_rect.height < u->surface->dst_rect.height) && (u->scaling_info->dst_rect.width < u->surface->src_rect.width || u->scaling_info->dst_rect.height < u->surface->src_rect.height)) /* Making dst rect smaller requires a bandwidth change */ - update_flags->bits.bandwidth_change = 1; + update_bits->bandwidth_change = 1; if (u->scaling_info->src_rect.width > (int)check_config->max_optimizable_video_width && (u->scaling_info->clip_rect.width > u->surface->clip_rect.width || u->scaling_info->clip_rect.height > u->surface->clip_rect.height)) /* Changing clip size of a large surface may result in MPC slice count change */ - update_flags->bits.bandwidth_change = 1; + update_bits->bandwidth_change = 1; } if (u->scaling_info->src_rect.x != u->surface->src_rect.x @@ -2902,7 +2902,7 @@ static struct surface_update_descriptor get_scaling_info_update_type( || u->scaling_info->dst_rect.x != u->surface->dst_rect.x || u->scaling_info->dst_rect.y != u->surface->dst_rect.y) { elevate_update_type(&update_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); - update_flags->bits.position_change = 1; + update_bits->position_change = 1; } return update_type; @@ -2913,15 +2913,15 @@ static struct surface_update_descriptor det_surface_update( struct dc_surface_update *u) { struct surface_update_descriptor overall_type = { UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_NONE }; - union surface_update_flags *update_flags = &u->surface->update_flags; + struct pipe_update_bits *update_bits = &u->surface->update_bits; if (u->surface->force_full_update) { - update_flags->raw = 0xFFFFFFFF; + dc_pipe_update_bits_set_full(update_bits); elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); return overall_type; } - update_flags->raw = 0; // Reset all flags + dc_pipe_update_bits_clear(update_bits); struct surface_update_descriptor inner_type = get_plane_info_update_type(u); @@ -2931,87 +2931,112 @@ static struct surface_update_descriptor det_surface_update( elevate_update_type(&overall_type, inner_type.update_type, inner_type.lock_descriptor); if (u->flip_addr) { - update_flags->bits.addr_update = 1; + update_bits->addr_update = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); if (u->flip_addr->address.tmz_surface != u->surface->address.tmz_surface) { - update_flags->bits.tmz_changed = 1; + update_bits->tmz_changed = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } } if (u->in_transfer_func) { - update_flags->bits.in_transfer_func_change = 1; + update_bits->in_transfer_func_change = 1; elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); } if (u->input_csc_color_matrix) { - update_flags->bits.input_csc_change = 1; + update_bits->input_csc_change = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } if (u->cursor_csc_color_matrix) { - update_flags->bits.cursor_csc_color_matrix_change = 1; + update_bits->cursor_csc_color_matrix_change = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } if (u->coeff_reduction_factor) { - update_flags->bits.coeff_reduction_change = 1; + update_bits->coeff_reduction_change = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } if (u->gamut_remap_matrix) { - update_flags->bits.gamut_remap_change = 1; + update_bits->gamut_remap_change = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } - if (u->blend_tf || (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) { - update_flags->bits.gamma_change = 1; + if ((u->cm && u->cm->flags.bits.blend_enable) || + (u->gamma && dce_use_lut(u->plane_info ? u->plane_info->format : u->surface->format))) { + update_bits->gamma_change = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } - if (u->lut3d_func || u->func_shaper) { - update_flags->bits.lut_3d = 1; + if (u->cm && (u->cm->flags.bits.lut3d_enable || u->cm->flags.bits.shaper_enable)) { + update_bits->lut_3d = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } + if (u->cm && u->cm->flags.bits.lut3d_dma_enable != u->surface->cm.flags.bits.lut3d_dma_enable && + u->cm->flags.bits.lut3d_enable && u->surface->cm.flags.bits.lut3d_enable) { + /* Toggling 3DLUT loading between DMA and Host is illegal */ + BREAK_TO_DEBUGGER(); + } + + if (u->cm && u->cm->flags.bits.lut3d_enable && !u->cm->flags.bits.lut3d_dma_enable) { + /* Host loading 3DLUT requires full update but only stream lock */ + elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_STREAM); + } + if (u->hdr_mult.value) if (u->hdr_mult.value != u->surface->hdr_mult.value) { // TODO: Should be fast? - update_flags->bits.hdr_mult = 1; + update_bits->hdr_mult = 1; elevate_update_type(&overall_type, UPDATE_TYPE_MED, LOCK_DESCRIPTOR_STREAM); } if (u->sdr_white_level_nits) if (u->sdr_white_level_nits != u->surface->sdr_white_level_nits) { // TODO: Should be fast? - update_flags->bits.sdr_white_level_nits = 1; + update_bits->sdr_white_level_nits = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } if (u->cm_hist_control) { - update_flags->bits.cm_hist_change = 1; + update_bits->cm_hist_change = 1; elevate_update_type(&overall_type, UPDATE_TYPE_FAST, LOCK_DESCRIPTOR_STREAM); } - if (u->cm2_params) { - if (u->cm2_params->component_settings.shaper_3dlut_setting != u->surface->mcm_shaper_3dlut_setting - || u->cm2_params->component_settings.lut1d_enable != u->surface->mcm_lut1d_enable - || u->cm2_params->cm2_luts.lut3d_data.lut3d_src != u->surface->mcm_luts.lut3d_data.lut3d_src) { - update_flags->bits.mcm_transfer_function_enable_change = 1; + + if (u->cm) { + const union dc_plane_cm_flags blend_only_flags = { + .bits = { + .blend_enable = 1, + } + }; + + if (u->cm->flags.bits.shaper_enable != u->surface->cm.flags.bits.shaper_enable + || u->cm->flags.bits.blend_enable != u->surface->cm.flags.bits.blend_enable + || u->cm->flags.bits.lut3d_enable != u->surface->cm.flags.bits.lut3d_enable + || u->cm->flags.bits.lut3d_dma_enable != u->surface->cm.flags.bits.lut3d_dma_enable) { + update_bits->mcm_transfer_function_enable_change = 1; + elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); + } + + if ((u->cm->flags.all != blend_only_flags.all && u->cm->flags.all != 0) || + (u->surface->cm.flags.all != blend_only_flags.all && u->surface->cm.flags.all != 0)) { elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } } - if (update_flags->bits.lut_3d && - u->surface->mcm_luts.lut3d_data.lut3d_src != DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) { + if (update_bits->lut_3d && + !u->surface->cm.flags.bits.lut3d_dma_enable) { elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } if (check_config->enable_legacy_fast_update && - (update_flags->bits.gamma_change || - update_flags->bits.gamut_remap_change || - update_flags->bits.input_csc_change || - update_flags->bits.cm_hist_change || - update_flags->bits.coeff_reduction_change)) { + (update_bits->gamma_change || + update_bits->gamut_remap_change || + update_bits->input_csc_change || + update_bits->cm_hist_change || + update_bits->coeff_reduction_change)) { elevate_update_type(&overall_type, UPDATE_TYPE_FULL, LOCK_DESCRIPTOR_GLOBAL); } return overall_type; @@ -3036,7 +3061,7 @@ static void force_immediate_gsl_plane_flip(struct dc *dc, struct dc_surface_upda if (has_flip_immediate_plane && surface_count > 1) { for (i = 0; i < surface_count; i++) { if (updates[i].surface->flip_immediate) - updates[i].surface->update_flags.bits.addr_update = 1; + updates[i].surface->update_bits.addr_update = 1; } } } @@ -3191,9 +3216,9 @@ struct surface_update_descriptor dc_check_update_surfaces_for_stream( struct dc_stream_update *stream_update) { if (stream_update) - stream_update->stream->update_flags.raw = 0; + stream_update_flags_clear(&stream_update->stream->update_flags); for (int i = 0; i < surface_count; i++) - updates[i].surface->update_flags.raw = 0; + dc_pipe_update_bits_clear(&updates[i].surface->update_bits); return check_update_surfaces_for_stream(check_config, updates, surface_count, stream_update); } @@ -3304,24 +3329,55 @@ static void copy_surface_update_to_plane( sizeof(struct dc_transfer_func_distributed_points)); } - if (srf_update->cm2_params) { - surface->mcm_shaper_3dlut_setting = srf_update->cm2_params->component_settings.shaper_3dlut_setting; - surface->mcm_lut1d_enable = srf_update->cm2_params->component_settings.lut1d_enable; - surface->mcm_luts = srf_update->cm2_params->cm2_luts; - } - - if (srf_update->func_shaper) { - memcpy(&surface->in_shaper_func, srf_update->func_shaper, - sizeof(surface->in_shaper_func)); + /* Shaper, 3DLUT, 1DLUT */ + if (srf_update->cm) { + struct kref refcount = surface->cm.refcount; + + memcpy(&surface->cm, srf_update->cm, sizeof(surface->cm)); + surface->cm.refcount = refcount; + +#ifndef TRIM_CM2 + /* Populate mcm_luts from cm for legacy consumers (dml2, hwseq) */ + surface->mcm_luts.lut1d_func = &surface->cm.blend_func; + surface->mcm_luts.shaper = &surface->cm.shaper_func; + if (srf_update->cm->flags.bits.lut3d_dma_enable) { + surface->mcm_luts.lut3d_data.lut3d_src = DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM; + surface->mcm_luts.lut3d_data.gpu_mem_params.addr = surface->cm.lut3d_dma.addr; + surface->mcm_luts.lut3d_data.gpu_mem_params.layout = + (surface->cm.lut3d_dma.swizzle == CM_LUT_3D_SWIZZLE_LINEAR_RGB) ? + DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB : + (surface->cm.lut3d_dma.swizzle == CM_LUT_3D_SWIZZLE_LINEAR_BGR) ? + DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR : + DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR; + surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.format = + (surface->cm.lut3d_dma.format == CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB) ? + DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB : + (surface->cm.lut3d_dma.format == CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB) ? + DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB : + DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10; + surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias = + surface->cm.lut3d_dma.bias; + surface->mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale = + surface->cm.lut3d_dma.scale; + surface->mcm_luts.lut3d_data.gpu_mem_params.component_order = + DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA; + surface->mcm_luts.lut3d_data.gpu_mem_params.size = DC_CM2_GPU_MEM_SIZE_TRANSFORMED; + surface->mcm_luts.lut3d_data.mpc_3dlut_enable = (srf_update->cm->flags.bits.lut3d_enable != 0); + } else { + surface->mcm_luts.lut3d_data.lut3d_src = DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM; + surface->mcm_luts.lut3d_data.lut3d_func = &surface->cm.lut3d_func; + } - if (surface->mcm_shaper_3dlut_setting >= DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER) - surface->mcm_luts.shaper = &surface->in_shaper_func; + if (srf_update->cm->flags.bits.shaper_enable && + srf_update->cm->flags.bits.lut3d_enable) + surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT; + else if (srf_update->cm->flags.bits.shaper_enable) + surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER; + else + surface->mcm_shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL; +#endif /* TRIM_CM2 */ } - if (srf_update->lut3d_func) - memcpy(&surface->lut3d_func, srf_update->lut3d_func, - sizeof(surface->lut3d_func)); - if (srf_update->hdr_mult.value) surface->hdr_mult = srf_update->hdr_mult; @@ -3330,15 +3386,10 @@ static void copy_surface_update_to_plane( surface->sdr_white_level_nits = srf_update->sdr_white_level_nits; - if (srf_update->blend_tf) { - memcpy(&surface->blend_tf, srf_update->blend_tf, - sizeof(surface->blend_tf)); - - if (surface->mcm_lut1d_enable) - surface->mcm_luts.lut1d_func = &surface->blend_tf; - } - - if (srf_update->cm2_params || srf_update->blend_tf) + if (srf_update->cm && + (srf_update->cm->flags.bits.blend_enable || + srf_update->cm->flags.bits.shaper_enable || + srf_update->cm->flags.bits.lut3d_enable)) surface->lut_bank_a = !surface->lut_bank_a; if (srf_update->input_csc_color_matrix) @@ -3714,11 +3765,11 @@ static bool update_planes_and_stream_state(struct dc *dc, if (update_type == UPDATE_TYPE_FULL) { if (stream_update) { uint32_t dsc_changed = stream_update->stream->update_flags.bits.dsc_changed; - stream_update->stream->update_flags.raw = 0xFFFFFFFF; + stream_update_flags_set_full(&stream_update->stream->update_flags); stream_update->stream->update_flags.bits.dsc_changed = dsc_changed; } for (i = 0; i < surface_count; i++) - srf_updates[i].surface->update_flags.raw = 0xFFFFFFFF; + dc_pipe_update_bits_set_full(&srf_updates[i].surface->update_bits); } if (update_type >= update_surface_trace_level) @@ -3767,7 +3818,7 @@ static bool update_planes_and_stream_state(struct dc *dc, if (update_type != UPDATE_TYPE_MED) continue; - if (surface->update_flags.bits.position_change) { + if (surface->update_bits.position_change) { for (j = 0; j < dc->res_pool->pipe_count; j++) { struct pipe_ctx *pipe_ctx = &context->res_ctx.pipe_ctx[j]; @@ -4077,8 +4128,6 @@ static void commit_planes_do_stream_update_sequence(struct dc *dc, { int j; struct block_sequence_state seq_state = { .steps = block_sequence, .num_steps = num_steps }; - struct dsc_config dsc_cfgs[MAX_PIPES]; - struct dsc_optc_config dsc_optc_cfgs[MAX_PIPES]; unsigned int dsc_cfg_index = 0; *num_steps = 0; // Initialize to 0 @@ -4150,11 +4199,13 @@ static void commit_planes_do_stream_update_sequence(struct dc *dc, if (stream_update->dsc_config) if (dsc_cfg_index < MAX_PIPES) { + struct dsc_config dsc_cfg; + struct dsc_optc_config dsc_optc_cfg; + add_link_update_dsc_config_sequence(&seq_state, pipe_ctx, - &dsc_cfgs[dsc_cfg_index], - &dsc_optc_cfgs[dsc_cfg_index]); - dsc_cfg_index++; + &dsc_cfg, + &dsc_optc_cfg); } if (stream_update->mst_bw_update) { @@ -4205,8 +4256,8 @@ static void commit_planes_do_stream_update_sequence(struct dc *dc, hwss_add_dc_set_optimized_required(&seq_state, dc, true); } else { - if (get_seamless_boot_stream_count(context) == 0) - hwss_add_prepare_bandwidth(&seq_state, dc, dc->current_state); + if (get_seamless_boot_stream_count(context) == 0 && dc->hwss.prepare_bandwidth_sequence) + dc->hwss.prepare_bandwidth_sequence(dc, dc->current_state, &seq_state); hwss_add_link_set_dpms_on(&seq_state, dc->current_state, dpms_pipe_ctx); } } else if (pipe_ctx->stream->link->wa_flags.blank_stream_on_ocs_change && stream_update->output_color_space @@ -4550,14 +4601,23 @@ static void build_dmub_update_dirty_rect( } } -static bool check_address_only_update(union surface_update_flags update_flags) +/** + * dc_check_address_only_update - Check if addr_update is the sole flag set + * + * @update_bits: The pipe update bits to check + * + * Determines whether an update contains only an address change with no other + * pending updates. + * + * Return: %true if addr_update is the sole bit set, %false otherwise. + */ +bool dc_check_address_only_update(struct pipe_update_bits update_bits) { - union surface_update_flags addr_only_update_flags; - addr_only_update_flags.raw = 0; - addr_only_update_flags.bits.addr_update = 1; + struct pipe_update_bits check = update_bits; /* 1. Copy all flags from input */ - return update_flags.bits.addr_update && - !(update_flags.raw & ~addr_only_update_flags.raw); + check.addr_update = 0; /* 2. Zero the addr_update bit in the copy */ + return update_bits.addr_update && /* 3. Check addr_update was set in original */ + !dc_pipe_update_bits_is_any_set(&check); /* 4. Check no other bits remain in the copy */ } /** @@ -4617,7 +4677,7 @@ static void commit_plane_for_stream_offload_fams2_flip(struct dc *dc, continue; /* update pipe context for plane */ - if (pipe_ctx->plane_state->update_flags.bits.addr_update) + if (pipe_ctx->plane_state->update_bits.addr_update) dc->hwss.update_plane_addr(dc, pipe_ctx); } } @@ -4655,8 +4715,8 @@ static void commit_planes_for_stream_fast(struct dc *dc, should_offload_fams2_flip = true; for (i = 0; i < surface_count; i++) { if (srf_updates[i].surface && - srf_updates[i].surface->update_flags.raw && - !check_address_only_update(srf_updates[i].surface->update_flags)) { + dc_pipe_update_bits_is_any_set(&srf_updates[i].surface->update_bits) && + !dc_check_address_only_update(srf_updates[i].surface->update_bits)) { /* more than address update, need to acquire FAMS2 lock */ should_offload_fams2_flip = false; break; @@ -4747,7 +4807,7 @@ static void commit_planes_for_stream_fast(struct dc *dc, * so no need to clear here. */ if (top_pipe_to_program->stream) - top_pipe_to_program->stream->update_flags.raw = 0; + stream_update_flags_clear(&top_pipe_to_program->stream->update_flags); } static void commit_planes_for_stream(struct dc *dc, @@ -5073,11 +5133,9 @@ static void commit_planes_for_stream(struct dc *dc, if (!should_update_pipe_for_plane(context, pipe_ctx, plane_state)) continue; - if (srf_updates[i].cm2_params && - srf_updates[i].cm2_params->cm2_luts.lut3d_data.lut3d_src == - DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM && - srf_updates[i].cm2_params->component_settings.shaper_3dlut_setting == - DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT && + if (srf_updates[i].cm && + srf_updates[i].cm->flags.bits.lut3d_enable && + srf_updates[i].cm->flags.bits.lut3d_dma_enable && dc->hwss.trigger_3dlut_dma_load) dc->hwss.trigger_3dlut_dma_load(dc, pipe_ctx); @@ -5087,7 +5145,7 @@ static void commit_planes_for_stream(struct dc *dc, dc->hwss.program_triplebuffer( dc, pipe_ctx, pipe_ctx->plane_state->triplebuffer_flips); } - if (pipe_ctx->plane_state->update_flags.bits.addr_update) + if (pipe_ctx->plane_state->update_bits.addr_update) dc->hwss.update_plane_addr(dc, pipe_ctx); } } @@ -5178,7 +5236,7 @@ static void commit_planes_for_stream(struct dc *dc, if (pipe_ctx->bottom_pipe || pipe_ctx->next_odm_pipe || !pipe_ctx->stream || !should_update_pipe_for_stream(context, pipe_ctx, stream) || - !pipe_ctx->plane_state->update_flags.bits.addr_update || + !pipe_ctx->plane_state->update_bits.addr_update || pipe_ctx->plane_state->skip_manual_trigger) continue; @@ -5617,7 +5675,7 @@ static bool commit_minimal_transition_state(struct dc *dc, /* force full surface update */ for (i = 0; i < dc->current_state->stream_count; i++) { for (j = 0; j < (unsigned int)dc->current_state->stream_status[i].plane_count; j++) { - dc->current_state->stream_status[i].plane_states[j]->update_flags.raw = 0xFFFFFFFF; + dc_pipe_update_bits_set_full(&dc->current_state->stream_status[i].plane_states[j]->update_bits); } } @@ -5792,14 +5850,9 @@ static bool full_update_required( (srf_updates[i].sdr_white_level_nits && srf_updates[i].sdr_white_level_nits != srf_updates->surface->sdr_white_level_nits) || srf_updates[i].in_transfer_func || - srf_updates[i].func_shaper || - srf_updates[i].lut3d_func || srf_updates[i].surface->force_full_update || (srf_updates[i].flip_addr && - srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface) || - (srf_updates[i].cm2_params && - (srf_updates[i].cm2_params->component_settings.shaper_3dlut_setting != srf_updates[i].surface->mcm_shaper_3dlut_setting || - srf_updates[i].cm2_params->component_settings.lut1d_enable != srf_updates[i].surface->mcm_lut1d_enable)))) + srf_updates[i].flip_addr->address.tmz_surface != srf_updates[i].surface->address.tmz_surface))) return true; } @@ -6063,17 +6116,17 @@ static bool update_planes_and_stream_v3(struct dc *dc, return true; } -static void clear_update_flags(struct dc_surface_update *srf_updates, +static void clear_update_bits(struct dc_surface_update *srf_updates, int surface_count, struct dc_stream_state *stream) { int i; if (stream) - stream->update_flags.raw = 0; + stream_update_flags_clear(&stream->update_flags); for (i = 0; i < surface_count; i++) if (srf_updates[i].surface) - srf_updates[i].surface->update_flags.raw = 0; + dc_pipe_update_bits_clear(&srf_updates[i].surface->update_bits); } bool dc_update_planes_and_stream(struct dc *dc, @@ -6125,7 +6178,7 @@ void dc_commit_updates_for_stream(struct dc *dc, } if (ret && dc->ctx->dce_version >= DCN_VERSION_3_2) - clear_update_flags(srf_updates, surface_count, stream); + clear_update_bits(srf_updates, surface_count, stream); } uint8_t dc_get_current_stream_count(struct dc *dc) @@ -7542,7 +7595,7 @@ bool dc_capture_register_software_state(struct dc *dc, struct dc_register_softwa struct dc_plane_state *plane_state = pipe_ctx->plane_state; /* MPCC blending tree and mode control - capture actual blend configuration */ - state->mpc.mpcc_mode[i] = (plane_state->blend_tf.type != TF_TYPE_BYPASS) ? 1 : 0; + state->mpc.mpcc_mode[i] = (plane_state->cm.blend_func.type != TF_TYPE_BYPASS) ? 1 : 0; state->mpc.mpcc_alpha_blend_mode[i] = plane_state->per_pixel_alpha ? 1 : 0; state->mpc.mpcc_alpha_multiplied_mode[i] = plane_state->pre_multiplied_alpha ? 1 : 0; state->mpc.mpcc_blnd_active_overlap_only[i] = 0; /* Default - no overlap restriction */ @@ -7875,7 +7928,7 @@ struct dc_update_scratch_space { struct dc_stream_state *stream; struct dc_stream_update *stream_update; bool update_v3; - bool do_clear_update_flags; + bool do_clear_update_bits; enum surface_update_type update_type; struct dc_state *new_context; enum update_v3_flow flow; @@ -7918,8 +7971,8 @@ static bool update_planes_and_stream_cleanup_v2( const struct dc_update_scratch_space *scratch ) { - if (scratch->do_clear_update_flags) - clear_update_flags(scratch->surface_updates, scratch->surface_count, scratch->stream); + if (scratch->do_clear_update_bits) + clear_update_bits(scratch->surface_updates, scratch->surface_count, scratch->stream); return false; } @@ -8173,8 +8226,8 @@ static bool update_planes_and_stream_cleanup_v3( ASSERT(false); } - if (scratch->do_clear_update_flags) - clear_update_flags(scratch->surface_updates, scratch->surface_count, scratch->stream); + if (scratch->do_clear_update_bits) + clear_update_bits(scratch->surface_updates, scratch->surface_count, scratch->stream); return false; } @@ -8197,7 +8250,7 @@ struct dc_update_scratch_space *dc_update_planes_and_stream_init( .stream = stream, .stream_update = stream_update, .update_v3 = version >= DCN_VERSION_4_01 || version == DCN_VERSION_3_2 || version == DCN_VERSION_3_21, - .do_clear_update_flags = version >= DCN_VERSION_1_0, + .do_clear_update_bits = version >= DCN_VERSION_1_0, }; return scratch; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index 88446817a71f..e47c8cf5d036 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -37,6 +37,7 @@ #include "dchubbub.h" #include "dccg.h" #include "abm.h" +#include "clk_mgr.h" #include "dcn10/dcn10_hubbub.h" #include "dce/dmub_hw_lock_mgr.h" #include "link_service.h" @@ -1028,20 +1029,20 @@ void hwss_build_fast_sequence(struct dc *dc, current_mpc_pipe = current_pipe; while (current_mpc_pipe) { if (current_mpc_pipe->plane_state) { - if (dc->hwss.set_flip_control_gsl && current_mpc_pipe->plane_state->update_flags.raw) { + if (dc->hwss.set_flip_control_gsl && dc_pipe_update_bits_is_any_set(¤t_mpc_pipe->plane_state->update_bits)) { block_sequence[*num_steps].params.set_flip_control_gsl_params.hubp = current_mpc_pipe->plane_res.hubp; block_sequence[*num_steps].params.set_flip_control_gsl_params.flip_immediate = current_mpc_pipe->plane_state->flip_immediate; block_sequence[*num_steps].func = HUBP_SET_FLIP_CONTROL_GSL; (*num_steps)++; } - if (dc->hwss.program_triplebuffer && dc->debug.enable_tri_buf && current_mpc_pipe->plane_state->update_flags.raw) { + if (dc->hwss.program_triplebuffer && dc->debug.enable_tri_buf && dc_pipe_update_bits_is_any_set(¤t_mpc_pipe->plane_state->update_bits)) { block_sequence[*num_steps].params.program_triplebuffer_params.dc = dc; block_sequence[*num_steps].params.program_triplebuffer_params.pipe_ctx = current_mpc_pipe; block_sequence[*num_steps].params.program_triplebuffer_params.enableTripleBuffer = current_mpc_pipe->plane_state->triplebuffer_flips; block_sequence[*num_steps].func = HUBP_PROGRAM_TRIPLEBUFFER; (*num_steps)++; } - if (dc->hwss.update_plane_addr && current_mpc_pipe->plane_state->update_flags.bits.addr_update) { + if (dc->hwss.update_plane_addr && current_mpc_pipe->plane_state->update_bits.addr_update) { if (resource_is_pipe_type(current_mpc_pipe, OTG_MASTER) && stream_status->mall_stream_config.type == SUBVP_MAIN) { block_sequence[*num_steps].params.subvp_save_surf_addr.dc_dmub_srv = dc->ctx->dmub_srv; @@ -1057,7 +1058,7 @@ void hwss_build_fast_sequence(struct dc *dc, (*num_steps)++; } - if (hws->funcs.set_input_transfer_func && current_mpc_pipe->plane_state->update_flags.bits.gamma_change) { + if (hws->funcs.set_input_transfer_func && current_mpc_pipe->plane_state->update_bits.gamma_change) { block_sequence[*num_steps].params.set_input_transfer_func_params.dc = dc; block_sequence[*num_steps].params.set_input_transfer_func_params.pipe_ctx = current_mpc_pipe; block_sequence[*num_steps].params.set_input_transfer_func_params.plane_state = current_mpc_pipe->plane_state; @@ -1066,23 +1067,23 @@ void hwss_build_fast_sequence(struct dc *dc, } if (dc->hwss.program_gamut_remap && - (current_mpc_pipe->plane_state->update_flags.bits.gamut_remap_change || + (current_mpc_pipe->plane_state->update_bits.gamut_remap_change || current_mpc_pipe->stream->update_flags.bits.gamut_remap)) { block_sequence[*num_steps].params.program_gamut_remap_params.pipe_ctx = current_mpc_pipe; block_sequence[*num_steps].func = DPP_PROGRAM_GAMUT_REMAP; (*num_steps)++; } - if (current_mpc_pipe->plane_state->update_flags.bits.input_csc_change) { + if (current_mpc_pipe->plane_state->update_bits.input_csc_change) { block_sequence[*num_steps].params.setup_dpp_params.pipe_ctx = current_mpc_pipe; block_sequence[*num_steps].func = DPP_SETUP_DPP; (*num_steps)++; } - if (current_mpc_pipe->plane_state->update_flags.bits.coeff_reduction_change) { + if (current_mpc_pipe->plane_state->update_bits.coeff_reduction_change) { block_sequence[*num_steps].params.program_bias_and_scale_params.pipe_ctx = current_mpc_pipe; block_sequence[*num_steps].func = DPP_PROGRAM_BIAS_AND_SCALE; (*num_steps)++; } - if (current_mpc_pipe->plane_state->update_flags.bits.cm_hist_change) { + if (current_mpc_pipe->plane_state->update_bits.cm_hist_change) { block_sequence[*num_steps].params.control_cm_hist_params.dpp = current_mpc_pipe->plane_res.dpp; block_sequence[*num_steps].params.control_cm_hist_params.cm_hist_control @@ -1095,7 +1096,7 @@ void hwss_build_fast_sequence(struct dc *dc, if (current_mpc_pipe->plane_res.dpp && current_mpc_pipe->plane_res.dpp->funcs->set_cursor_matrix && - current_mpc_pipe->plane_state->update_flags.bits.cursor_csc_color_matrix_change) { + current_mpc_pipe->plane_state->update_bits.cursor_csc_color_matrix_change) { block_sequence[*num_steps].params.dpp_set_cursor_matrix_params.dpp = current_mpc_pipe->plane_res.dpp; block_sequence[*num_steps].params.dpp_set_cursor_matrix_params.color_space = current_mpc_pipe->plane_state->color_space; block_sequence[*num_steps].params.dpp_set_cursor_matrix_params.cursor_csc_color_matrix = ¤t_mpc_pipe->plane_state->cursor_csc_color_matrix; @@ -1176,7 +1177,7 @@ void hwss_build_fast_sequence(struct dc *dc, while (current_mpc_pipe) { if (!current_mpc_pipe->bottom_pipe && !current_mpc_pipe->next_odm_pipe && current_mpc_pipe->stream && current_mpc_pipe->plane_state && - current_mpc_pipe->plane_state->update_flags.bits.addr_update && + current_mpc_pipe->plane_state->update_bits.addr_update && !current_mpc_pipe->plane_state->skip_manual_trigger) { if (dc->hwss.program_cursor_offload_now) { block_sequence[*num_steps].params.program_cursor_update_now_params.dc = dc; @@ -1668,6 +1669,21 @@ void hwss_execute_sequence(struct dc *dc, case LINK_SET_DPMS_ON: hwss_link_set_dpms_on(params); break; + case CLK_MGR_SET_MAX_MEMCLK: + hwss_clk_mgr_set_max_memclk(params); + break; + case CLK_MGR_UPDATE_CLOCKS: + hwss_clk_mgr_update_clocks(params); + break; + case HUBBUB_PROGRAM_WATERMARKS: + hwss_hubbub_program_watermarks(params); + break; + case HUBBUB_PROGRAM_ARBITER: + hwss_hubbub_program_arbiter(params); + break; + case HUBBUB_PROGRAM_COMPBUF_SEGMENTS: + hwss_hubbub_program_compbuf_segments(params); + break; default: ASSERT(false); break; @@ -3849,6 +3865,70 @@ void hwss_dsc_set_config_simple(union block_sequence_params *params) dsc->funcs->dsc_set_config(dsc, dsc_cfg, dsc_optc_cfg); } +/* + * Clock manager executor functions + */ +void hwss_clk_mgr_set_max_memclk(union block_sequence_params *params) +{ + struct clk_mgr *clk_mgr = params->clk_mgr_set_max_memclk_params.clk_mgr; + unsigned int memclk_mhz = params->clk_mgr_set_max_memclk_params.memclk_mhz; + + if (clk_mgr && clk_mgr->funcs && clk_mgr->funcs->set_max_memclk) + clk_mgr->funcs->set_max_memclk(clk_mgr, memclk_mhz); +} + +void hwss_clk_mgr_update_clocks(union block_sequence_params *params) +{ + struct clk_mgr *clk_mgr = params->clk_mgr_update_clocks_params.clk_mgr; + + if (clk_mgr && clk_mgr->funcs && clk_mgr->funcs->execute_clk_mgr_block_sequence) + clk_mgr->funcs->execute_clk_mgr_block_sequence(clk_mgr); +} + +/* + * Hubbub executor functions + */ +void hwss_hubbub_program_watermarks(union block_sequence_params *params) +{ + struct dc *dc = params->hubbub_program_watermarks_params.dc; + struct hubbub *hubbub = params->hubbub_program_watermarks_params.hubbub; + union dcn_watermark_set *watermarks = params->hubbub_program_watermarks_params.watermarks; + unsigned int refclk_mhz = params->hubbub_program_watermarks_params.refclk_mhz; + bool safe_to_lower = params->hubbub_program_watermarks_params.safe_to_lower; + + if (hubbub && hubbub->funcs && hubbub->funcs->program_watermarks) { + bool wm_changed = hubbub->funcs->program_watermarks(hubbub, watermarks, refclk_mhz, safe_to_lower); + + if (dc && !safe_to_lower) + dc->optimized_required |= wm_changed; + } +} + +void hwss_hubbub_program_arbiter(union block_sequence_params *params) +{ + struct dc *dc = params->hubbub_program_arbiter_params.dc; + struct hubbub *hubbub = params->hubbub_program_arbiter_params.hubbub; + struct dml2_display_arb_regs *arb_regs = params->hubbub_program_arbiter_params.arb_regs; + bool safe_to_lower = params->hubbub_program_arbiter_params.safe_to_lower; + + if (hubbub && hubbub->funcs && hubbub->funcs->program_arbiter) { + bool arb_changed = hubbub->funcs->program_arbiter(hubbub, arb_regs, safe_to_lower); + + if (dc && !safe_to_lower) + dc->optimized_required |= arb_changed; + } +} + +void hwss_hubbub_program_compbuf_segments(union block_sequence_params *params) +{ + struct hubbub *hubbub = params->hubbub_program_compbuf_segments_params.hubbub; + unsigned int compbuf_size = params->hubbub_program_compbuf_segments_params.compbuf_size; + bool safe_to_lower = params->hubbub_program_compbuf_segments_params.safe_to_lower; + + if (hubbub && hubbub->funcs && hubbub->funcs->program_compbuf_segments) + hubbub->funcs->program_compbuf_segments(hubbub, compbuf_size, safe_to_lower); +} + void hwss_add_dccg_set_dto_dscclk(struct block_sequence_state *seq_state, struct dccg *dccg, int inst, int num_slices_h) { @@ -4909,6 +4989,9 @@ void hwss_add_hpo_dp_stream_enc_update_dp_info_packets_sdp_line_num(struct block } } +/* + * Clock manager helper functions + */ void hwss_add_hpo_dp_stream_enc_update_dp_info_packets(struct block_sequence_state *seq_state, struct pipe_ctx *pipe_ctx) { @@ -4919,6 +5002,28 @@ void hwss_add_hpo_dp_stream_enc_update_dp_info_packets(struct block_sequence_sta } } +void hwss_add_clk_mgr_set_max_memclk(struct block_sequence_state *seq_state, + struct clk_mgr *clk_mgr, + unsigned int memclk_mhz) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = CLK_MGR_SET_MAX_MEMCLK; + seq_state->steps[*seq_state->num_steps].params.clk_mgr_set_max_memclk_params.clk_mgr = clk_mgr; + seq_state->steps[*seq_state->num_steps].params.clk_mgr_set_max_memclk_params.memclk_mhz = memclk_mhz; + (*seq_state->num_steps)++; + } +} + +void hwss_add_clk_mgr_update_clocks(struct block_sequence_state *seq_state, + struct clk_mgr *clk_mgr) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = CLK_MGR_UPDATE_CLOCKS; + seq_state->steps[*seq_state->num_steps].params.clk_mgr_update_clocks_params.clk_mgr = clk_mgr; + (*seq_state->num_steps)++; + } +} + void hwss_add_stream_enc_update_dp_info_packets_sdp_line_num(struct block_sequence_state *seq_state, struct pipe_ctx *pipe_ctx) { @@ -5022,6 +5127,26 @@ void hwss_add_setup_periodic_interrupt(struct block_sequence_state *seq_state, (*seq_state->num_steps)++; } } +/* + * Hubbub helper functions + */ +void hwss_add_hubbub_program_watermarks(struct block_sequence_state *seq_state, + struct dc *dc, + struct hubbub *hubbub, + union dcn_watermark_set *watermarks, + unsigned int refclk_mhz, + bool safe_to_lower) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PROGRAM_WATERMARKS; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.dc = dc; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.watermarks = watermarks; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.refclk_mhz = refclk_mhz; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_watermarks_params.safe_to_lower = safe_to_lower; + (*seq_state->num_steps)++; + } +} void hwss_add_dp_trace_source_sequence(struct block_sequence_state *seq_state, struct dc_link *link, @@ -5035,6 +5160,22 @@ void hwss_add_dp_trace_source_sequence(struct block_sequence_state *seq_state, } } +void hwss_add_hubbub_program_arbiter(struct block_sequence_state *seq_state, + struct dc *dc, + struct hubbub *hubbub, + struct dml2_display_arb_regs *arb_regs, + bool safe_to_lower) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PROGRAM_ARBITER; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_arbiter_params.dc = dc; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_arbiter_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_arbiter_params.arb_regs = arb_regs; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_arbiter_params.safe_to_lower = safe_to_lower; + (*seq_state->num_steps)++; + } +} + void hwss_add_set_dmdata_attributes(struct block_sequence_state *seq_state, struct pipe_ctx *pipe_ctx) { @@ -5119,6 +5260,19 @@ void hwss_add_disable_audio_stream(struct block_sequence_state *seq_state, (*seq_state->num_steps)++; } } +void hwss_add_hubbub_program_compbuf_segments(struct block_sequence_state *seq_state, + struct hubbub *hubbub, + unsigned int compbuf_size, + bool safe_to_lower) +{ + if (*seq_state->num_steps < MAX_HWSS_BLOCK_SEQUENCE_SIZE) { + seq_state->steps[*seq_state->num_steps].func = HUBBUB_PROGRAM_COMPBUF_SEGMENTS; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_compbuf_segments_params.hubbub = hubbub; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_compbuf_segments_params.compbuf_size = compbuf_size; + seq_state->steps[*seq_state->num_steps].params.hubbub_program_compbuf_segments_params.safe_to_lower = safe_to_lower; + (*seq_state->num_steps)++; + } +} void hwss_add_prepare_bandwidth(struct block_sequence_state *seq_state, struct dc *dc, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c index 5f6cc1b1f788..b21d41df0fab 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_resource.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_resource.c @@ -4412,14 +4412,13 @@ enum dc_status dc_validate_with_context(struct dc *dc, struct dc_stream_state *unchanged_streams[MAX_PIPES] = { 0 }; struct dc_stream_state *del_streams[MAX_PIPES] = { 0 }; struct dc_stream_state *add_streams[MAX_PIPES] = { 0 }; - int old_stream_count = context->stream_count; + unsigned int old_stream_count = context->stream_count; enum dc_status res = DC_ERROR_UNEXPECTED; - int unchanged_streams_count = 0; - int del_streams_count = 0; - int add_streams_count = 0; + unsigned int unchanged_streams_count = 0; + unsigned int del_streams_count = 0; + unsigned int add_streams_count = 0; bool found = false; - int i, j; - unsigned int k; + unsigned int i, j, k; DC_LOGGER_INIT(dc->ctx->logger); diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c index 72845fc788f3..88e825a6582c 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_surface.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_surface.c @@ -45,14 +45,13 @@ void dc_plane_construct(struct dc_context *ctx, struct dc_plane_state *plane_sta plane_state->in_transfer_func.type = TF_TYPE_BYPASS; - plane_state->in_shaper_func.type = TF_TYPE_BYPASS; - - plane_state->lut3d_func.state.raw = 0; - - plane_state->blend_tf.type = TF_TYPE_BYPASS; - plane_state->pre_multiplied_alpha = true; + /* CM */ + plane_state->cm.shaper_func.type = TF_TYPE_BYPASS; + plane_state->cm.blend_func.type = TF_TYPE_BYPASS; + plane_state->cm.lut3d_func.state.raw = 0; + plane_state->cm.flags.all = 0; } void dc_plane_destruct(struct dc_plane_state *plane_state) @@ -282,6 +281,39 @@ void dc_3dlut_func_retain(struct dc_3dlut *lut) kref_get(&lut->refcount); } +static void dc_plane_cm_free(struct kref *kref) +{ + struct dc_plane_cm *cm = container_of(kref, struct dc_plane_cm, refcount); + + kvfree(cm); +} + +struct dc_plane_cm *dc_plane_cm_create(void) +{ + struct dc_plane_cm *cm = kvzalloc(sizeof(*cm), GFP_KERNEL); + + if (cm == NULL) + goto alloc_fail; + + kref_init(&cm->refcount); + + return cm; + +alloc_fail: + return NULL; + +} + +void dc_plane_cm_release(struct dc_plane_cm *cm) +{ + kref_put(&cm->refcount, dc_plane_cm_free); +} + +void dc_plane_cm_retain(struct dc_plane_cm *cm) +{ + kref_get(&cm->refcount); +} + void dc_plane_force_dcc_and_tiling_disable(struct dc_plane_state *plane_state, bool clear_tiling) { diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 82d02ebbd829..13c1f7cd9d7d 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -65,7 +65,7 @@ struct dcn_dsc_reg_state; struct dcn_optc_reg_state; struct dcn_dccg_reg_state; -#define DC_VER "3.2.384" +#define DC_VER "3.2.388" /** * MAX_SURFACES - representative of the upper bound of surfaces that can be piped to a single CRTC @@ -591,7 +591,6 @@ struct dc_config { bool enable_mipi_converter_optimization; bool enable_frl; bool force_hdmi21_frl_enc_enable; - bool skip_frl_pretraining; bool use_default_clock_table; bool force_bios_enable_lttpr; uint8_t force_bios_fixed_vs; @@ -1129,8 +1128,6 @@ struct dc_debug_options { unsigned int force_fclk_khz; bool enable_tri_buf; bool ips_disallow_entry; - bool dmub_offload_enabled; - bool dmcub_emulation; bool disable_idle_power_optimizations; unsigned int mall_size_override; unsigned int mall_additional_timer_percent; @@ -1152,7 +1149,6 @@ struct dc_debug_options { bool validate_dml_output; bool enable_dmcub_surface_flip; bool usbc_combo_phy_reset_wa; - bool force_vrr; bool force_fva; int max_frl_rate; unsigned int force_frl_rate; @@ -1290,6 +1286,8 @@ struct dc_debug_options { unsigned int min_deep_sleep_dcfclk_khz; unsigned int force_odm2to1_for_edp_pixclk_mhz; bool enable_replay_esd_recovery; + uint8_t iommu_mismatch_temp_wka; + bool disable_dynamic_expansion_for_test_pattern; }; @@ -1332,7 +1330,6 @@ struct dc_init_data { enum dce_environment dce_environment; struct dmub_offload_funcs *dmub_if; - struct dc_reg_helper_state *dmub_offload; struct dc_config flags; uint64_t log_mask; @@ -1488,6 +1485,47 @@ struct dc_3dlut { struct fixed31_32 hdr_multiplier; union dc_3dlut_state state; }; + +/* 3DLUT DMA (Fast Load) params */ +struct dc_3dlut_dma { + struct dc_plane_address addr; + enum dc_cm_lut_swizzle swizzle; + enum dc_cm_lut_pixel_format format; + uint16_t bias; /* FP1.5.10 */ + uint16_t scale; /* FP1.5.10 */ + enum dc_cm_lut_size size; +}; + +/* color manager */ +union dc_plane_cm_flags { + unsigned int all; + struct { + unsigned int shaper_enable : 1; + unsigned int lut3d_enable : 1; + unsigned int blend_enable : 1; + /* whether legacy (lut3d_func) or DMA is valid */ + unsigned int lut3d_dma_enable : 1; +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + /* RMCM lut to be used instead of MCM */ + unsigned int rmcm_enable : 1; + unsigned int reserved: 27; +#else + unsigned int reserved: 28; +#endif + } bits; +}; + +struct dc_plane_cm { + struct kref refcount; + struct dc_transfer_func shaper_func; + union { + struct dc_3dlut lut3d_func; + struct dc_3dlut_dma lut3d_dma; + }; + struct dc_transfer_func blend_func; + union dc_plane_cm_flags flags; +}; + /* * This structure is filled in by dc_surface_get_status and contains * the last requested address and the currently active address so the called @@ -1501,47 +1539,120 @@ struct dc_plane_status { struct cm_hist cm_hist; }; -union surface_update_flags { - - struct { - uint32_t addr_update:1; - /* Medium updates */ - uint32_t dcc_change:1; - uint32_t color_space_change:1; - uint32_t horizontal_mirror_change:1; - uint32_t per_pixel_alpha_change:1; - uint32_t global_alpha_change:1; - uint32_t hdr_mult:1; - uint32_t rotation_change:1; - uint32_t swizzle_change:1; - uint32_t scaling_change:1; - uint32_t position_change:1; - uint32_t in_transfer_func_change:1; - uint32_t input_csc_change:1; - uint32_t coeff_reduction_change:1; - uint32_t pixel_format_change:1; - uint32_t plane_size_change:1; - uint32_t gamut_remap_change:1; - uint32_t cursor_csc_color_matrix_change:1; - - /* Full updates */ - uint32_t new_plane:1; - uint32_t bpp_change:1; - uint32_t gamma_change:1; - uint32_t bandwidth_change:1; - uint32_t clock_change:1; - uint32_t stereo_format_change:1; - uint32_t lut_3d:1; - uint32_t tmz_changed:1; - uint32_t mcm_transfer_function_enable_change:1; /* disable or enable MCM transfer func */ - uint32_t full_update:1; - uint32_t sdr_white_level_nits:1; - uint32_t cm_hist_change:1; - } bits; - - uint32_t raw; +struct pipe_update_bits { + uint32_t addr_update:1; + uint32_t dcc_change:1; + uint32_t color_space_change:1; + uint32_t horizontal_mirror_change:1; + uint32_t per_pixel_alpha_change:1; + uint32_t global_alpha_change:1; + uint32_t hdr_mult:1; + uint32_t rotation_change:1; + uint32_t swizzle_change:1; + uint32_t scaling_change:1; + uint32_t position_change:1; + uint32_t in_transfer_func_change:1; + uint32_t input_csc_change:1; + uint32_t coeff_reduction_change:1; + uint32_t pixel_format_change:1; + uint32_t plane_size_change:1; + uint32_t gamut_remap_change:1; + uint32_t cursor_csc_color_matrix_change:1; + uint32_t new_plane:1; + uint32_t bpp_change:1; + uint32_t gamma_change:1; + uint32_t bandwidth_change:1; + uint32_t clock_change:1; + uint32_t stereo_format_change:1; + uint32_t lut_3d:1; + uint32_t tmz_changed:1; + uint32_t mcm_transfer_function_enable_change:1; /* disable or enable MCM transfer func */ + uint32_t full_update:1; + uint32_t sdr_white_level_nits:1; + uint32_t cm_hist_change:1; + /* NOTE: When adding a new field, also update: + * - dc_pipe_update_bits_set_full() + * - dc_pipe_update_bits_is_any_set() + */ }; +static inline void dc_pipe_update_bits_clear(struct pipe_update_bits *flags) +{ + /* memset ensures padding bits are zeroed */ + memset(flags, 0, sizeof(*flags)); +} + +static inline void dc_pipe_update_bits_set_full(struct pipe_update_bits *flags) +{ + dc_pipe_update_bits_clear(flags); + flags->addr_update = 1; + flags->dcc_change = 1; + flags->color_space_change = 1; + flags->horizontal_mirror_change = 1; + flags->per_pixel_alpha_change = 1; + flags->global_alpha_change = 1; + flags->hdr_mult = 1; + flags->rotation_change = 1; + flags->swizzle_change = 1; + flags->scaling_change = 1; + flags->position_change = 1; + flags->in_transfer_func_change = 1; + flags->input_csc_change = 1; + flags->coeff_reduction_change = 1; + flags->pixel_format_change = 1; + flags->plane_size_change = 1; + flags->gamut_remap_change = 1; + flags->cursor_csc_color_matrix_change = 1; + flags->new_plane = 1; + flags->bpp_change = 1; + flags->gamma_change = 1; + flags->bandwidth_change = 1; + flags->clock_change = 1; + flags->stereo_format_change = 1; + flags->lut_3d = 1; + flags->tmz_changed = 1; + flags->mcm_transfer_function_enable_change = 1; + flags->full_update = 1; + flags->sdr_white_level_nits = 1; + flags->cm_hist_change = 1; +} + +static inline bool dc_pipe_update_bits_is_any_set(const struct pipe_update_bits *flags) +{ + return flags->addr_update || + flags->dcc_change || + flags->color_space_change || + flags->horizontal_mirror_change || + flags->per_pixel_alpha_change || + flags->global_alpha_change || + flags->hdr_mult || + flags->rotation_change || + flags->swizzle_change || + flags->scaling_change || + flags->position_change || + flags->in_transfer_func_change || + flags->input_csc_change || + flags->coeff_reduction_change || + flags->pixel_format_change || + flags->plane_size_change || + flags->gamut_remap_change || + flags->cursor_csc_color_matrix_change || + flags->new_plane || + flags->bpp_change || + flags->gamma_change || + flags->bandwidth_change || + flags->clock_change || + flags->stereo_format_change || + flags->lut_3d || + flags->tmz_changed || + flags->mcm_transfer_function_enable_change || + flags->full_update || + flags->sdr_white_level_nits || + flags->cm_hist_change; +} + +bool dc_check_address_only_update(struct pipe_update_bits update_bits); + #define DC_REMOVE_PLANE_POINTERS 1 struct dc_plane_state { @@ -1566,14 +1677,22 @@ struct dc_plane_state { struct fixed31_32 hdr_mult; struct colorspace_transform gamut_remap_matrix; + enum dc_color_space color_space; + +#ifndef TRIM_CM2 // TODO: No longer used, remove struct dc_hdr_static_metadata hdr_static_ctx; - enum dc_color_space color_space; - struct dc_3dlut lut3d_func; struct dc_transfer_func in_shaper_func; struct dc_transfer_func blend_tf; + enum dc_cm2_shaper_3dlut_setting mcm_shaper_3dlut_setting; + bool mcm_lut1d_enable; + struct dc_cm2_func_luts mcm_luts; +#endif /* TRIM_CM2 */ + bool lut_bank_a; + enum mpcc_movable_cm_location mcm_location; + struct dc_plane_cm cm; struct dc_transfer_func *gamcor_tf; enum surface_pixel_format format; @@ -1590,7 +1709,7 @@ struct dc_plane_state { bool horizontal_mirror; unsigned int layer_index; - union surface_update_flags update_flags; + struct pipe_update_bits update_bits; bool flip_int_enabled; bool skip_manual_trigger; @@ -1610,11 +1729,6 @@ struct dc_plane_state { bool is_statically_allocated; enum chroma_cositing cositing; - enum dc_cm2_shaper_3dlut_setting mcm_shaper_3dlut_setting; - bool mcm_lut1d_enable; - struct dc_cm2_func_luts mcm_luts; - bool lut_bank_a; - enum mpcc_movable_cm_location mcm_location; struct dc_csc_transform cursor_csc_color_matrix; bool adaptive_sharpness_en; int adaptive_sharpness_policy; @@ -1728,6 +1842,10 @@ struct dc_scratch_space { * of ddc_pin to know which aux instance is associated with link. */ bool no_ddc_pin; + /** When set, forces all native I2C communication on this DP connector + * to use the I2C-over-AUX protocol instead of native I2C signaling. + */ + bool force_to_use_aux; enum gpio_ddc_line aux_hw_inst; enum gpio_ddc_line ddc_hw_inst; @@ -1978,17 +2096,7 @@ struct dc_surface_update { const struct dc_csc_transform *input_csc_color_matrix; const struct fixed31_32 *coeff_reduction_factor; - const struct dc_transfer_func *func_shaper; - const struct dc_3dlut *lut3d_func; - const struct dc_transfer_func *blend_tf; const struct colorspace_transform *gamut_remap_matrix; - /* - * Color Transformations for pre-blend MCM (Shaper, 3DLUT, 1DLUT) - * - * change cm2_params.component_settings: Full update - * change cm2_params.cm2_luts: Fast update - */ - const struct dc_cm2_parameters *cm2_params; const struct dc_plane_cm *cm; const struct dc_csc_transform *cursor_csc_color_matrix; unsigned int sdr_white_level_nits; @@ -2034,6 +2142,10 @@ struct dc_3dlut *dc_create_3dlut_func(void); void dc_3dlut_func_release(struct dc_3dlut *lut); void dc_3dlut_func_retain(struct dc_3dlut *lut); +struct dc_plane_cm *dc_plane_cm_create(void); +void dc_plane_cm_release(struct dc_plane_cm *cm); +void dc_plane_cm_retain(struct dc_plane_cm *cm); + void dc_post_update_surfaces_to_stream( struct dc *dc); diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index 0ee5c0c5545c..68ed0e16639d 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -518,9 +518,6 @@ void dc_dmub_srv_query_caps_cmd(struct dc_dmub_srv *dc_dmub_srv) { union dmub_rb_cmd cmd = { 0 }; - if (dc_dmub_srv->ctx->dc->debug.dmcub_emulation) - return; - memset(&cmd, 0, sizeof(cmd)); /* Prepare fw command */ @@ -1302,9 +1299,6 @@ bool dc_dmub_srv_is_hw_pwr_up(struct dc_dmub_srv *dc_dmub_srv, bool wait) if (!dc_dmub_srv || !dc_dmub_srv->dmub) return true; - if (dc_dmub_srv->ctx->dc->debug.dmcub_emulation) - return true; - dc_ctx = dc_dmub_srv->ctx; if (wait) { @@ -1345,9 +1339,6 @@ static void dc_dmub_srv_notify_idle(const struct dc *dc, bool allow_idle) struct dc_dmub_srv *dc_dmub_srv; union dmub_rb_cmd cmd = {0}; - if (dc->debug.dmcub_emulation) - return; - if (!dc->ctx->dmub_srv || !dc->ctx->dmub_srv->dmub) return; @@ -1466,9 +1457,6 @@ static void dc_dmub_srv_exit_low_power_state(const struct dc *dc) struct dc_dmub_srv *dc_dmub_srv; uint32_t rcg_exit_count = 0, ips1_exit_count = 0, ips2_exit_count = 0, ips1z8_exit_count = 0; - if (dc->debug.dmcub_emulation) - return; - if (!dc->ctx->dmub_srv || !dc->ctx->dmub_srv->dmub) return; @@ -2137,9 +2125,7 @@ bool dmub_lsdma_init(struct dc_dmub_srv *dc_dmub_srv) bool dmub_lsdma_send_linear_copy_command( struct dc_dmub_srv *dc_dmub_srv, - uint64_t src_addr, - uint64_t dst_addr, - uint32_t count + struct lsdma_linear_copy_params copy_data ) { struct dc_context *dc_ctx = dc_dmub_srv->ctx; @@ -2154,11 +2140,20 @@ bool dmub_lsdma_send_linear_copy_command( cmd.cmd_common.header.sub_type = DMUB_CMD__LSDMA_LINEAR_COPY; wait_type = DM_DMUB_WAIT_TYPE_NO_WAIT; - lsdma_data->u.linear_copy_data.count = count - 1; // LSDMA controller expects bytes to copy -1 - lsdma_data->u.linear_copy_data.src_lo = src_addr & 0xFFFFFFFF; - lsdma_data->u.linear_copy_data.src_hi = (src_addr >> 32) & 0xFFFFFFFF; - lsdma_data->u.linear_copy_data.dst_lo = dst_addr & 0xFFFFFFFF; - lsdma_data->u.linear_copy_data.dst_hi = (dst_addr >> 32) & 0xFFFFFFFF; + lsdma_data->u.linear_copy_data.count = copy_data.count; + lsdma_data->u.linear_copy_data.src_lo = copy_data.src_lo; + lsdma_data->u.linear_copy_data.src_hi = copy_data.src_hi; + lsdma_data->u.linear_copy_data.dst_lo = copy_data.dst_lo; + lsdma_data->u.linear_copy_data.dst_hi = copy_data.dst_hi; + lsdma_data->u.linear_copy_data.tmz = copy_data.tmz; + lsdma_data->u.linear_copy_data.data_format = copy_data.data_format; + lsdma_data->u.linear_copy_data.num_type = copy_data.num_type; + lsdma_data->u.linear_copy_data.read_compress = copy_data.read_compress; + lsdma_data->u.linear_copy_data.write_compress = copy_data.write_compress; + lsdma_data->u.linear_copy_data.max_com = copy_data.max_com; + lsdma_data->u.linear_copy_data.max_uncom = copy_data.max_uncom; + lsdma_data->u.linear_copy_data.cache_policy_src = copy_data.cache_policy_src; + lsdma_data->u.linear_copy_data.cache_policy_dst = copy_data.cache_policy_dst; result = dc_wake_and_execute_dmub_cmd(dc_ctx, &cmd, wait_type); @@ -2203,6 +2198,12 @@ bool dmub_lsdma_send_linear_sub_window_copy_command( lsdma_data->u.linear_sub_window_copy_data.rect_y = copy_data.rect_y; lsdma_data->u.linear_sub_window_copy_data.src_cache_policy = copy_data.src_cache_policy; lsdma_data->u.linear_sub_window_copy_data.dst_cache_policy = copy_data.dst_cache_policy; + lsdma_data->u.linear_sub_window_copy_data.data_format = copy_data.data_format; + lsdma_data->u.linear_sub_window_copy_data.num_type = copy_data.num_type; + lsdma_data->u.linear_sub_window_copy_data.read_compress = copy_data.read_compress; + lsdma_data->u.linear_sub_window_copy_data.write_compress = copy_data.write_compress; + lsdma_data->u.linear_sub_window_copy_data.max_com = copy_data.max_com; + lsdma_data->u.linear_sub_window_copy_data.max_uncom = copy_data.max_uncom; result = dc_wake_and_execute_dmub_cmd(dc_ctx, &cmd, wait_type); diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h index ebcaf49e5961..8bdaac0b0f98 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h @@ -37,17 +37,8 @@ struct dc_crtc_timing; struct dc_state; struct dc_surface_update; -struct dc_reg_helper_state { - bool gather_in_progress; - uint32_t same_addr_count; - bool should_burst_write; - union dmub_rb_cmd cmd_data; - unsigned int reg_seq_count; -}; - struct dc_dmub_srv { struct dmub_srv *dmub; - struct dc_reg_helper_state reg_helper_offload; struct dc_context *ctx; void *dm; @@ -212,11 +203,31 @@ void dc_dmub_srv_fams2_passthrough_flip( int surface_count); bool dmub_lsdma_init(struct dc_dmub_srv *dc_dmub_srv); + +struct lsdma_linear_copy_params { + uint32_t src_lo; + uint32_t src_hi; + + uint32_t dst_lo; + uint32_t dst_hi; + + uint32_t count : 30; + uint32_t read_compress : 2; + + uint32_t tmz : 4; + uint32_t cache_policy_src : 3; + uint32_t cache_policy_dst : 3; + uint32_t data_format : 6; + uint32_t num_type : 3; + uint32_t write_compress : 2; + uint32_t max_com : 2; + uint32_t max_uncom : 1; + uint32_t reserved0 : 8; +}; + bool dmub_lsdma_send_linear_copy_command( struct dc_dmub_srv *dc_dmub_srv, - uint64_t src_addr, - uint64_t dst_addr, - uint32_t count); + struct lsdma_linear_copy_params copy_data); struct lsdma_linear_sub_window_copy_params { uint32_t src_lo; @@ -244,7 +255,13 @@ struct lsdma_linear_sub_window_copy_params { uint32_t element_size : 3; uint32_t src_cache_policy : 3; uint32_t dst_cache_policy : 3; - uint32_t padding : 19; + uint32_t data_format : 6; + uint32_t num_type : 3; + uint32_t read_compress : 2; + uint32_t write_compress : 2; + uint32_t max_com : 2; + uint32_t max_uncom : 1; + uint32_t reserved0 : 3; }; bool dmub_lsdma_send_linear_sub_window_copy_command( diff --git a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h index fbef0dc743ff..d0ba9ad67a3e 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dp_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dp_types.h @@ -1146,6 +1146,7 @@ struct edp_psr_info { union edp_psr_dpcd_caps psr_dpcd_caps; uint8_t psr2_su_y_granularity_cap; uint8_t force_psrsu_cap; + uint8_t psr_active_vtotal_control_cap; }; struct replay_info { diff --git a/drivers/gpu/drm/amd/display/dc/dc_helper.c b/drivers/gpu/drm/amd/display/dc/dc_helper.c index 0e0165764a57..cc7fea613d9e 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_helper.c +++ b/drivers/gpu/drm/amd/display/dc/dc_helper.c @@ -39,53 +39,6 @@ #define DC_LOGGER \ ctx->logger -static inline void submit_dmub_read_modify_write( - struct dc_reg_helper_state *offload, - const struct dc_context *ctx) -{ - struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write; - - offload->should_burst_write = - (offload->same_addr_count == (DMUB_READ_MODIFY_WRITE_SEQ__MAX - 1)); - cmd_buf->header.payload_bytes = - sizeof(struct dmub_cmd_read_modify_write_sequence) * offload->reg_seq_count; - - dc_wake_and_execute_dmub_cmd(ctx, &offload->cmd_data, DM_DMUB_WAIT_TYPE_NO_WAIT); - - memset(cmd_buf, 0, sizeof(*cmd_buf)); - - offload->reg_seq_count = 0; - offload->same_addr_count = 0; -} - -static inline void submit_dmub_burst_write( - struct dc_reg_helper_state *offload, - const struct dc_context *ctx) -{ - struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write; - - cmd_buf->header.payload_bytes = - sizeof(uint32_t) * offload->reg_seq_count; - - dc_wake_and_execute_dmub_cmd(ctx, &offload->cmd_data, DM_DMUB_WAIT_TYPE_NO_WAIT); - - memset(cmd_buf, 0, sizeof(*cmd_buf)); - - offload->reg_seq_count = 0; -} - -static inline void submit_dmub_reg_wait( - struct dc_reg_helper_state *offload, - const struct dc_context *ctx) -{ - struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait; - - dc_wake_and_execute_dmub_cmd(ctx, &offload->cmd_data, DM_DMUB_WAIT_TYPE_NO_WAIT); - - memset(cmd_buf, 0, sizeof(*cmd_buf)); - offload->reg_seq_count = 0; -} - struct dc_reg_value_masks { uint32_t value; uint32_t mask; @@ -127,98 +80,6 @@ static void set_reg_field_values(struct dc_reg_value_masks *field_value_mask, } } -static void dmub_flush_buffer_execute( - struct dc_reg_helper_state *offload, - const struct dc_context *ctx) -{ - submit_dmub_read_modify_write(offload, ctx); -} - -static void dmub_flush_burst_write_buffer_execute( - struct dc_reg_helper_state *offload, - const struct dc_context *ctx) -{ - submit_dmub_burst_write(offload, ctx); -} - -static bool dmub_reg_value_burst_set_pack(const struct dc_context *ctx, uint32_t addr, - uint32_t reg_val) -{ - struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload; - struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write; - - /* flush command if buffer is full */ - if (offload->reg_seq_count == DMUB_BURST_WRITE_VALUES__MAX) - dmub_flush_burst_write_buffer_execute(offload, ctx); - - if (offload->cmd_data.cmd_common.header.type == DMUB_CMD__REG_SEQ_BURST_WRITE && - addr != cmd_buf->addr) { - dmub_flush_burst_write_buffer_execute(offload, ctx); - return false; - } - - cmd_buf->header.type = DMUB_CMD__REG_SEQ_BURST_WRITE; - cmd_buf->header.sub_type = 0; - cmd_buf->addr = addr; - cmd_buf->write_values[offload->reg_seq_count] = reg_val; - offload->reg_seq_count++; - - return true; -} - -static uint32_t dmub_reg_value_pack(const struct dc_context *ctx, uint32_t addr, - struct dc_reg_value_masks *field_value_mask) -{ - struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload; - struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write; - struct dmub_cmd_read_modify_write_sequence *seq; - - /* flush command if buffer is full */ - if (offload->cmd_data.cmd_common.header.type != DMUB_CMD__REG_SEQ_BURST_WRITE && - offload->reg_seq_count == DMUB_READ_MODIFY_WRITE_SEQ__MAX) - dmub_flush_buffer_execute(offload, ctx); - - if (offload->should_burst_write) { - if (dmub_reg_value_burst_set_pack(ctx, addr, field_value_mask->value)) - return field_value_mask->value; - else - offload->should_burst_write = false; - } - - /* pack commands */ - cmd_buf->header.type = DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE; - cmd_buf->header.sub_type = 0; - seq = &cmd_buf->seq[offload->reg_seq_count]; - - if (offload->reg_seq_count) { - if (cmd_buf->seq[offload->reg_seq_count - 1].addr == addr) - offload->same_addr_count++; - else - offload->same_addr_count = 0; - } - - seq->addr = addr; - seq->modify_mask = field_value_mask->mask; - seq->modify_value = field_value_mask->value; - offload->reg_seq_count++; - - return field_value_mask->value; -} - -static void dmub_reg_wait_done_pack(const struct dc_context *ctx, uint32_t addr, - uint32_t mask, uint32_t shift, uint32_t condition_value, uint32_t time_out_us) -{ - struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload; - struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait; - - cmd_buf->header.type = DMUB_CMD__REG_REG_WAIT; - cmd_buf->header.sub_type = 0; - cmd_buf->reg_wait.addr = addr; - cmd_buf->reg_wait.condition_field_value = mask & (condition_value << shift); - cmd_buf->reg_wait.mask = mask; - cmd_buf->reg_wait.time_out_us = time_out_us; -} - uint32_t generic_reg_update_ex(const struct dc_context *ctx, uint32_t addr, int n, uint8_t shift1, uint32_t mask1, uint32_t field_value1, @@ -235,11 +96,6 @@ uint32_t generic_reg_update_ex(const struct dc_context *ctx, va_end(ap); - if (ctx->dmub_srv && - ctx->dmub_srv->reg_helper_offload.gather_in_progress) - return dmub_reg_value_pack(ctx, addr, &field_value_mask); - /* todo: return void so we can decouple code running in driver from register states */ - /* mmio write directly */ reg_val = dm_read_reg(ctx, addr); reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value; @@ -265,12 +121,6 @@ uint32_t generic_reg_set_ex(const struct dc_context *ctx, /* mmio write directly */ reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value; - if (ctx->dmub_srv && - ctx->dmub_srv->reg_helper_offload.gather_in_progress) { - return dmub_reg_value_burst_set_pack(ctx, addr, reg_val); - /* todo: return void so we can decouple code running in driver from register states */ - } - dm_write_reg(ctx, addr, reg_val); return reg_val; } @@ -434,13 +284,6 @@ void generic_reg_wait(const struct dc_context *ctx, uint32_t reg_val; unsigned int i; - if (ctx->dmub_srv && - ctx->dmub_srv->reg_helper_offload.gather_in_progress) { - dmub_reg_wait_done_pack(ctx, addr, mask, shift, condition_value, - delay_between_poll_us * time_out_num_tries); - return; - } - /* * Something is terribly wrong if time out is > 3000ms. * 3000ms is the maximum time needed for SMU to pass values back. @@ -491,12 +334,6 @@ uint32_t generic_read_indirect_reg(const struct dc_context *ctx, { uint32_t value = 0; - // when reg read, there should not be any offload. - if (ctx->dmub_srv && - ctx->dmub_srv->reg_helper_offload.gather_in_progress) { - ASSERT(false); - } - dm_write_reg(ctx, addr_index, index); value = dm_read_reg(ctx, addr_data); @@ -624,69 +461,6 @@ uint32_t generic_indirect_reg_get_sync(const struct dc_context *ctx, return value; } -void reg_sequence_start_gather(const struct dc_context *ctx) -{ - /* if reg sequence is supported and enabled, set flag to - * indicate we want to have REG_SET, REG_UPDATE macro build - * reg sequence command buffer rather than MMIO directly. - */ - - if (ctx->dmub_srv && ctx->dc->debug.dmub_offload_enabled) { - struct dc_reg_helper_state *offload = - &ctx->dmub_srv->reg_helper_offload; - - /* caller sequence mismatch. need to debug caller. offload will not work!!! */ - ASSERT(!offload->gather_in_progress); - - offload->gather_in_progress = true; - } -} - -void reg_sequence_start_execute(const struct dc_context *ctx) -{ - struct dc_reg_helper_state *offload; - - if (!ctx->dmub_srv) - return; - - offload = &ctx->dmub_srv->reg_helper_offload; - - if (offload && offload->gather_in_progress) { - offload->gather_in_progress = false; - offload->should_burst_write = false; - switch (offload->cmd_data.cmd_common.header.type) { - case DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE: - submit_dmub_read_modify_write(offload, ctx); - break; - case DMUB_CMD__REG_REG_WAIT: - submit_dmub_reg_wait(offload, ctx); - break; - case DMUB_CMD__REG_SEQ_BURST_WRITE: - submit_dmub_burst_write(offload, ctx); - break; - default: - return; - } - } -} - -void reg_sequence_wait_done(const struct dc_context *ctx) -{ - /* callback to DM to poll for last submission done*/ - struct dc_reg_helper_state *offload; - - if (!ctx->dmub_srv) - return; - - offload = &ctx->dmub_srv->reg_helper_offload; - - if (offload && - ctx->dc->debug.dmub_offload_enabled && - !ctx->dc->debug.dmcub_emulation) { - dc_dmub_srv_wait_for_idle(ctx->dmub_srv, DM_DMUB_WAIT_TYPE_WAIT, NULL); - } -} - char *dce_version_to_string(const int version) { switch (version) { diff --git a/drivers/gpu/drm/amd/display/dc/dc_stream.h b/drivers/gpu/drm/amd/display/dc/dc_stream.h index 4154cd059562..8b164edc9c51 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_stream.h +++ b/drivers/gpu/drm/amd/display/dc/dc_stream.h @@ -128,6 +128,35 @@ union stream_update_flags { uint32_t raw; }; +static inline void stream_update_flags_clear(union stream_update_flags *flags) +{ + flags->raw = 0; +} + +static inline void stream_update_flags_set_full(union stream_update_flags *flags) +{ + stream_update_flags_clear(flags); + flags->bits.scaling = 1; + flags->bits.out_tf = 1; + flags->bits.out_csc = 1; + flags->bits.abm_level = 1; + flags->bits.dpms_off = 1; + flags->bits.gamut_remap = 1; + flags->bits.wb_update = 1; + flags->bits.dsc_changed = 1; + flags->bits.mst_bw = 1; + flags->bits.crtc_timing_adjust = 1; + flags->bits.fams_changed = 1; + flags->bits.scaler_sharpener = 1; + flags->bits.sharpening_required = 1; + flags->bits.cursor_attr = 1; + flags->bits.cursor_pos = 1; + flags->bits.periodic_interrupt = 1; + flags->bits.info_frame = 1; + flags->bits.dmdata = 1; + flags->bits.dither = 1; +} + struct test_pattern { enum dp_test_pattern type; enum dp_test_pattern_color_space color_space; diff --git a/drivers/gpu/drm/amd/display/dc/dc_types.h b/drivers/gpu/drm/amd/display/dc/dc_types.h index 4ed1efa17270..90dd1ae7e953 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_types.h +++ b/drivers/gpu/drm/amd/display/dc/dc_types.h @@ -183,6 +183,7 @@ struct dc_panel_patch { unsigned int force_frl; unsigned int vsdb_rcc_wa; unsigned int delay_hdmi_link_training; + unsigned int skip_frl_pre_training; unsigned int skip_avmute; unsigned int skip_audio_sab_check; unsigned int mst_start_top_delay; @@ -1397,6 +1398,39 @@ enum dc_hpd_enable_select { HPD_EN_FOR_SECONDARY_EDP_ONLY, }; +enum dc_cm_lut_swizzle { + CM_LUT_3D_SWIZZLE_LINEAR_RGB, + CM_LUT_3D_SWIZZLE_LINEAR_BGR, + CM_LUT_1D_PACKED_LINEAR +}; + +enum dc_cm_lut_pixel_format { + CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB, +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12MSB, +#endif + CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB, +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + CM_LUT_PIXEL_FORMAT_BGRA16161616_UNORM_12LSB, +#endif + CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10, +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + CM_LUT_PIXEL_FORMAT_BGRA16161616_FLOAT_FP1_5_10 +#endif +}; + +enum dc_cm_lut_size { + CM_LUT_SIZE_NONE, + CM_LUT_SIZE_999, + CM_LUT_SIZE_171717, +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + CM_LUT_SIZE_333333, + CM_LUT_SIZE_454545, + CM_LUT_SIZE_656565, +#endif +}; + +#ifndef TRIM_CM2 enum dc_cm2_shaper_3dlut_setting { DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL, DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER, @@ -1421,6 +1455,16 @@ enum dc_cm2_gpu_mem_format { DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10 }; +enum dc_cm2_gpu_mem_size { + DC_CM2_GPU_MEM_SIZE_171717, + DC_CM2_GPU_MEM_SIZE_333333, + DC_CM2_GPU_MEM_SIZE_454545, + DC_CM2_GPU_MEM_SIZE_656565, + DC_CM2_GPU_MEM_SIZE_TRANSFORMED, +}; +#endif /* TRIM_CM2 */ + +#ifndef TRIM_CM2 struct dc_cm2_gpu_mem_format_parameters { enum dc_cm2_gpu_mem_format format; union { @@ -1432,14 +1476,6 @@ struct dc_cm2_gpu_mem_format_parameters { }; }; -enum dc_cm2_gpu_mem_size { - DC_CM2_GPU_MEM_SIZE_171717, - DC_CM2_GPU_MEM_SIZE_333333, - DC_CM2_GPU_MEM_SIZE_454545, - DC_CM2_GPU_MEM_SIZE_656565, - DC_CM2_GPU_MEM_SIZE_TRANSFORMED, -}; - struct dc_cm2_gpu_mem_parameters { struct dc_plane_address addr; enum dc_cm2_gpu_mem_layout layout; @@ -1448,17 +1484,16 @@ struct dc_cm2_gpu_mem_parameters { enum dc_cm2_gpu_mem_size size; uint16_t bit_depth; }; +#endif /* TRIM_CM2 */ +#ifndef TRIM_CM2 enum dc_cm2_transfer_func_source { DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM, DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM }; +#endif /* TRIM_CM2 */ -struct dc_cm2_component_settings { - enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting; - bool lut1d_enable; -}; - +#ifndef TRIM_CM2 /* * All pointers in this struct must remain valid for as long as the 3DLUTs are used */ @@ -1478,11 +1513,7 @@ struct dc_cm2_func_luts { } lut3d_data; const struct dc_transfer_func *lut1d_func; }; - -struct dc_cm2_parameters { - struct dc_cm2_component_settings component_settings; - struct dc_cm2_func_luts cm2_luts; -}; +#endif /* TRIM_CM2 */ enum mall_stream_type { SUBVP_NONE, // subvp not in use diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c index e57242f8bc12..616a896f0782 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.c @@ -77,13 +77,26 @@ void dccg42_otg_drop_pixel(struct dccg *dccg, } } -void dccg42_enable_global_fgcg(struct dccg *dccg, bool value) +void dccg42_enable_global_fgcg(struct dccg *dccg, bool enable) { struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); - if (dccg->ctx->dc->debug.disable_clock_gate) - value = false; - REG_UPDATE(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, !value); + /* Temporary workaround for IOMMU mismatch issue. + * Fine grain control via bit2 of debug flag. + */ + if (dccg->ctx->dc->debug.disable_clock_gate || (dccg->ctx->dc->debug.iommu_mismatch_temp_wka & 0x4)) + enable = false; + + REG_UPDATE(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, !enable); +} + +bool dccg42_get_global_fgcg_status(struct dccg *dccg) +{ + struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + uint32_t disabled = 0; + + REG_GET(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, &disabled); + return disabled & 0x1; } void dccg42_set_physymclk( @@ -265,40 +278,35 @@ void dccg42_trigger_dio_fifo_resync(struct dccg *dccg) static void dccg42_init(struct dccg *dccg) { - int otg_inst; - struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); + unsigned int i; + struct resource_pool *res_pool = dccg->ctx->dc->res_pool; /* Set HPO stream encoder to use refclk to avoid case where PHY is * disabled and SYMCLK32 for HPO SE is sourced from PHYD32CLK which * will cause DCN to hang. */ - for (otg_inst = 0; otg_inst < 4; otg_inst++) - dccg35_disable_symclk32_se(dccg, otg_inst); + for (i = 0; i < res_pool->hpo_dp_stream_enc_count; i++) + dccg35_disable_symclk32_se(dccg, i); if (dccg->ctx->dc->debug.root_clock_optimization.bits.symclk32_le) { - dccg401_disable_symclk32_le(dccg, 0); - dccg401_disable_symclk32_le(dccg, 1); - dccg401_disable_symclk32_le(dccg, 2); - dccg401_disable_symclk32_le(dccg, 3); + for (i = 0; i < res_pool->hpo_dp_link_enc_count; i++) + dccg401_disable_symclk32_le(dccg, i); } if (dccg->ctx->dc->debug.root_clock_optimization.bits.dpstream) { - dccg401_disable_dpstreamclk(dccg, 0); - dccg401_disable_dpstreamclk(dccg, 1); - dccg401_disable_dpstreamclk(dccg, 2); - dccg401_disable_dpstreamclk(dccg, 3); - } - if (!dccg->ctx->dc->debug.root_clock_optimization.bits.physymclk) { - REG_UPDATE_5(DCCG_GATE_DISABLE_CNTL2, - PHYASYMCLK_ROOT_GATE_DISABLE, 1, - PHYBSYMCLK_ROOT_GATE_DISABLE, 1, - PHYCSYMCLK_ROOT_GATE_DISABLE, 1, - PHYDSYMCLK_ROOT_GATE_DISABLE, 1, - PHYESYMCLK_ROOT_GATE_DISABLE, 1); + for (i = 0; i < res_pool->hpo_dp_stream_enc_count; i++) + dccg401_disable_dpstreamclk(dccg, i); } + dccg42_disable_hdmistreamclk(dccg); if (dccg->ctx->dc->debug.root_clock_optimization.bits.hdmichar) dccg42_disable_hdmicharclk(dccg, 0); + + if (dccg->ctx->dc->debug.root_clock_optimization.bits.dpp) { + for (i = 0; i < res_pool->pipe_count; i++) { + dccg35_dpp_root_clock_control(dccg, i, true); + } + } } @@ -340,7 +348,8 @@ static const struct dccg_funcs dccg42_funcs = { .dccg_root_gate_disable_control = dccg35_root_gate_disable_control, .dccg_read_reg_state = dccg31_read_reg_state, .dccg_enable_global_fgcg = dccg42_enable_global_fgcg, - .allow_clock_gating = dccg2_allow_clock_gating + .allow_clock_gating = dccg2_allow_clock_gating, + .dccg_get_global_fgcg_status = dccg42_get_global_fgcg_status, }; struct dccg *dccg42_create( diff --git a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h index 2076565b1caa..ebd3cec1a977 100644 --- a/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h +++ b/drivers/gpu/drm/amd/display/dc/dccg/dcn42/dcn42_dccg.h @@ -46,6 +46,7 @@ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DCCG_FIFO_ERRDET_STATE, mask_sh),\ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DCCG_FIFO_ERRDET_OVR_EN, mask_sh),\ DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, DISPCLK_CHG_FWD_CORR_DISABLE, mask_sh),\ + DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, RESYNC_FIFO_LEVEL_ADJUST_EN, mask_sh),\ DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_PHASE, mask_sh),\ DCCG_SF(DPPCLK0_DTO_PARAM, DPPCLK0_DTO_MODULO, mask_sh),\ DCCG_SF(HDMICHARCLK0_CLOCK_CNTL, HDMICHARCLK0_EN, mask_sh),\ @@ -56,34 +57,24 @@ DCCG_SF(PHYBSYMCLK_CLOCK_CNTL, PHYBSYMCLK_SRC_SEL, mask_sh),\ DCCG_SF(PHYCSYMCLK_CLOCK_CNTL, PHYCSYMCLK_EN, mask_sh),\ DCCG_SF(PHYCSYMCLK_CLOCK_CNTL, PHYCSYMCLK_SRC_SEL, mask_sh),\ - DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_EN, mask_sh),\ - DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_SRC_SEL, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK0_EN, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK1_EN, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK2_EN, mask_sh),\ - DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_EN, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK0_SRC_SEL, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK1_SRC_SEL, mask_sh),\ DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK2_SRC_SEL, mask_sh),\ - DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_SRC_SEL, mask_sh),\ DCCG_SF(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_EN, mask_sh),\ DCCG_SF(HDMISTREAMCLK_CNTL, HDMISTREAMCLK0_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE0_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE1_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE2_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE0_EN, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE1_EN, mask_sh),\ DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE2_EN, mask_sh),\ - DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_EN, mask_sh),\ DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE0_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE1_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE2_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE3_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE0_EN, mask_sh),\ DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE1_EN, mask_sh),\ - DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE2_EN, mask_sh),\ - DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE3_EN, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 0, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 1, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, PIPE, DTO_SRC_SEL, 2, mask_sh),\ @@ -121,7 +112,6 @@ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYASYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYBSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYCSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYDSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GLOBAL_FGCG_REP_CNTL, DCCG_GLOBAL_FGCG_REP_DIS, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 0, mask_sh),\ DCCG_SFII(OTG, PIXEL_RATE_CNTL, DP_DTO, ENABLE, 1, mask_sh),\ @@ -134,7 +124,6 @@ DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK0_EN, mask_sh),\ DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK1_EN, mask_sh),\ DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK2_EN, mask_sh),\ - DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK3_EN, mask_sh),\ DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_PHASE, mask_sh),\ DCCG_SF(DSCCLK0_DTO_PARAM, DSCCLK0_DTO_MODULO, mask_sh),\ DCCG_SF(DSCCLK1_DTO_PARAM, DSCCLK1_DTO_PHASE, mask_sh),\ @@ -147,36 +136,26 @@ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKA_FE_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKB_FE_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKC_FE_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKD_FE_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKA_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKB_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKC_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKD_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYASYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYBSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYCSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYDSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE1_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE1_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE1_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE1_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, HDMICHARCLK0_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYA_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYB_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYC_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYD_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P1_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DTBCLK_P2_GATE_DISABLE, mask_sh),\ @@ -184,19 +163,15 @@ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_FE_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_FE_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_FE_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_FE_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKA_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKB_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKC_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK0_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK1_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK2_ROOT_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK0_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK1_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK2_GATE_DISABLE, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK0_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK1_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL6, DSCCLK2_ROOT_GATE_DISABLE, mask_sh),\ @@ -208,26 +183,38 @@ DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_CLOCK_ENABLE, mask_sh),\ DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_CLOCK_ENABLE, mask_sh),\ DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_CLOCK_ENABLE, mask_sh),\ - DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_CLOCK_ENABLE, mask_sh),\ DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_FE_EN, mask_sh),\ DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_FE_EN, mask_sh),\ DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_EN, mask_sh),\ - DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_EN, mask_sh),\ DCCG_SF(SYMCLKA_CLOCK_ENABLE, SYMCLKA_FE_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_FE_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_SRC_SEL, mask_sh),\ - DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_SRC_SEL, mask_sh) + DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_FE_SRC_SEL, mask_sh) #define DCCG_MASK_SH_LIST_DCN42(mask_sh) \ DCCG_MASK_SH_LIST_DCN42_COMMON(mask_sh),\ + DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_EN, mask_sh),\ + DCCG_SF(PHYDSYMCLK_CLOCK_CNTL, PHYDSYMCLK_SRC_SEL, mask_sh),\ DCCG_SF(PHYESYMCLK_CLOCK_CNTL, PHYESYMCLK_EN, mask_sh),\ DCCG_SF(PHYESYMCLK_CLOCK_CNTL, PHYESYMCLK_SRC_SEL, mask_sh),\ DCCG_SF(HDMISTREAMCLK0_DTO_PARAM, HDMISTREAMCLK0_DTO_PHASE, mask_sh),\ DCCG_SF(HDMISTREAMCLK0_DTO_PARAM, HDMISTREAMCLK0_DTO_MODULO, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYDSYMCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYESYMCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKD_FE_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL2, SYMCLKD_GATE_DISABLE, mask_sh),\ DCCG_SF(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_PHASE, mask_sh),\ DCCG_SF(DSCCLK3_DTO_PARAM, DSCCLK3_DTO_MODULO, mask_sh),\ - DCCG_SF(DCCG_GATE_DISABLE_CNTL2, PHYESYMCLK_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_SE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_ROOT_LE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_SE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE2_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL3, SYMCLK32_LE3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_FE_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKD_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_ROOT_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL5, DPSTREAMCLK3_GATE_DISABLE, mask_sh),\ + DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYD_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL4, PHYE_REFCLK_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKE_FE_ROOT_GATE_DISABLE, mask_sh),\ DCCG_SF(DCCG_GATE_DISABLE_CNTL5, SYMCLKE_ROOT_GATE_DISABLE, mask_sh),\ @@ -236,11 +223,22 @@ DCCG_SF(SYMCLKB_CLOCK_ENABLE, SYMCLKB_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKC_CLOCK_ENABLE, SYMCLKC_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_CLOCK_ENABLE, mask_sh),\ + DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_EN, mask_sh),\ + DCCG_SF(SYMCLKD_CLOCK_ENABLE, SYMCLKD_FE_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_SRC_SEL, mask_sh),\ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_CLOCK_ENABLE, mask_sh),\ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_FE_EN, mask_sh),\ DCCG_SF(SYMCLKE_CLOCK_ENABLE, SYMCLKE_FE_SRC_SEL, mask_sh),\ - DCCG_SF(DISPCLK_FREQ_CHANGE_CNTL, RESYNC_FIFO_LEVEL_ADJUST_EN, mask_sh) + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_SE_CNTL, SYMCLK32_SE3_EN, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE2_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE3_SRC_SEL, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE2_EN, mask_sh),\ + DCCG_SF(SYMCLK32_LE_CNTL, SYMCLK32_LE3_EN, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_EN, mask_sh),\ + DCCG_SF(DPSTREAMCLK_CNTL, DPSTREAMCLK3_SRC_SEL, mask_sh),\ + DCCG_SF(DSCCLK_DTO_CTRL, DSCCLK3_EN, mask_sh) void dccg42_otg_add_pixel(struct dccg *dccg, @@ -249,6 +247,7 @@ void dccg42_otg_add_pixel(struct dccg *dccg, void dccg42_otg_drop_pixel(struct dccg *dccg, uint32_t otg_inst); void dccg42_enable_global_fgcg(struct dccg *dccg, bool value); +bool dccg42_get_global_fgcg_status(struct dccg *dccg); void dccg42_set_physymclk( struct dccg *dccg, diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c index 72ad3ee3d6a5..6cb5e8152cf1 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_aux.c @@ -26,6 +26,7 @@ #include "dm_services.h" #include "core_types.h" #include "dce_aux.h" +#include "dce/dce_11_0_d.h" #include "dce/dce_11_0_sh_mask.h" #include "dm_event_log.h" #include "dm_helpers.h" @@ -529,7 +530,7 @@ static uint32_t dce_aux_configure_timeout(struct ddc_service *ddc, uint32_t prev_timeout_val = 0; struct ddc *ddc_pin = ddc->ddc_pin; - if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) + if (ddc->link->force_to_use_aux) return dce_aux_configure_timeout_without_ddc_pin(ddc, timeout_in_us); struct dce_aux *aux_engine = ddc->ctx->dc->res_pool->engines[ddc_pin->pin_data->en]; @@ -652,7 +653,7 @@ int dce_aux_transfer_raw(struct ddc_service *ddc, struct aux_payload *payload, enum aux_return_code_type *operation_result) { - if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) { + if (ddc->link->force_to_use_aux) { /* Check whether aux to be processed via dmub or dcn directly */ if (ddc->ctx->dc->debug.enable_dmub_aux_for_legacy_ddc) { return dce_aux_transfer_dmub_raw(ddc, payload, operation_result); @@ -795,7 +796,7 @@ int dce_aux_transfer_dmub_raw(struct ddc_service *ddc, release_engine(aux_engine); } - if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) { + if (ddc->link->force_to_use_aux) { struct dce_aux *aux_engine = ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst]; if (!acquire_aux_engine_without_ddc_pin(aux_engine, ddc_pin)) { @@ -893,7 +894,7 @@ bool dce_aux_transfer_with_retries(struct ddc_service *ddc, aux110 = FROM_AUX_ENGINE(aux_engine); } - if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) { + if (ddc->link->force_to_use_aux) { aux_engine = ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst]; aux110 = FROM_AUX_ENGINE(aux_engine); } diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c index ed407e779c12..2c3a20d35fe9 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.c @@ -271,7 +271,6 @@ static void dce110_stream_encoder_dp_set_stream_attribute( bool use_vsc_sdp_for_colorimetry, uint32_t enable_sdp_splitting) { - (void)use_vsc_sdp_for_colorimetry; (void)enable_sdp_splitting; uint32_t h_active_start; uint32_t v_active_start; @@ -334,6 +333,16 @@ static void dce110_stream_encoder_dp_set_stream_attribute( if (REG(DP_MSA_MISC)) misc1 = REG_READ(DP_MSA_MISC); + /* For YCbCr420 and BT2020 Colorimetry Formats, VSC SDP shall be used. + * When MISC1, bit 6, is Set to 1, a Source device uses a VSC SDP to indicate the + * Pixel Encoding/Colorimetry Format and that a Sink device shall ignore MISC1, bit 7, + * and MISC0, bits 7:1 (MISC1, bit 7, and MISC0, bits 7:1, become "don't care"). + */ + if (use_vsc_sdp_for_colorimetry) + misc1 = misc1 | 0x40; + else + misc1 = misc1 & ~0x40; + /* set color depth */ switch (hw_crtc_timing.display_color_depth) { @@ -499,6 +508,10 @@ static void dce110_stream_encoder_dp_set_stream_attribute( hw_crtc_timing.h_addressable + hw_crtc_timing.h_border_right, DP_MSA_VHEIGHT, hw_crtc_timing.v_border_top + hw_crtc_timing.v_addressable + hw_crtc_timing.v_border_bottom); + } else { + /* DCE-only path */ + if (REG(DP_MSA_MISC)) + REG_WRITE(DP_MSA_MISC, misc1); /* MSA_MISC1 */ } } diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h index 342c0afe6a94..88d6044904d1 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_stream_encoder.h @@ -96,7 +96,8 @@ #define SE_COMMON_REG_LIST(id)\ SE_COMMON_REG_LIST_DCE_BASE(id), \ - SRI(AFMT_CNTL, DIG, id) + SRI(AFMT_CNTL, DIG, id), \ + SRI(DP_MSA_MISC, DP, id) #define SE_DCN_REG_LIST(id)\ SE_COMMON_REG_LIST_BASE(id),\ diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c index 9be578ff8c88..140c66081492 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_compressor.c @@ -27,8 +27,6 @@ #include "dce/dce_11_0_d.h" #include "dce/dce_11_0_sh_mask.h" -#include "gmc/gmc_8_2_sh_mask.h" -#include "gmc/gmc_8_2_d.h" #include "include/logger_interface.h" diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c index b265a72eeb70..095869912c09 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_mem_input_v.c @@ -27,8 +27,6 @@ #include "dce/dce_11_0_d.h" #include "dce/dce_11_0_sh_mask.h" /* TODO: this needs to be looked at, used by Stella's workaround*/ -#include "gmc/gmc_8_2_d.h" -#include "gmc/gmc_8_2_sh_mask.h" #include "include/logger_interface.h" #include "inc/dce_calcs.h" diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c index fe97d3946cab..4b273762f07a 100644 --- a/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_compressor.c @@ -27,8 +27,12 @@ #include "dce/dce_11_2_d.h" #include "dce/dce_11_2_sh_mask.h" -#include "gmc/gmc_8_1_sh_mask.h" -#include "gmc/gmc_8_1_d.h" + +#ifndef mmGMCON_LPT_TARGET +#define mmGMCON_LPT_TARGET 0x0D53 +#define GMCON_LPT_TARGET__STCTRL_LPT_TARGET__SHIFT 0x00000000 +#define GMCON_LPT_TARGET__STCTRL_LPT_TARGET_MASK 0xffffffffL +#endif #include "include/logger_interface.h" diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c index bfd5515c2f4f..66fe7f313ea3 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_cm_common.c @@ -303,6 +303,190 @@ bool cm3_helper_translate_curve_to_hw_format(struct dc_context *ctx, return true; } +/* Linear interpolation of tf_pts entries, where (i >> 4) is the integer tf_pts + * index, (i & 0xf) is the 1/16 sub-position. + */ +static struct fixed31_32 interp_tf_pts(const struct fixed31_32 *output_tf_channel, int i) +{ + struct fixed31_32 in_plus_one, in, value; + uint32_t t = i & 0xf; + + in_plus_one = output_tf_channel[(i >> 4) + 1]; + in = output_tf_channel[i >> 4]; + value = dc_fixpt_sub(in_plus_one, in); + value = dc_fixpt_shr(dc_fixpt_mul_int(value, t), 4); + value = dc_fixpt_add(in, value); + + return value; +} + +bool cm3_helper_translate_curve_to_degamma_hw_format( + const struct dc_transfer_func *output_tf, + struct pwl_params *lut_params) +{ + struct curve_points3 *corner_points; + struct pwl_result_data *rgb_resulted; + struct pwl_result_data *rgb; + struct pwl_result_data *rgb_plus_1; + + int32_t region_start, region_end; + int32_t i; + uint32_t j, k, seg_distr[MAX_REGIONS_NUMBER], increment, start_index, hw_points; + + if (output_tf == NULL || lut_params == NULL || output_tf->type == TF_TYPE_BYPASS) + return false; + + corner_points = lut_params->corner_points; + rgb_resulted = lut_params->rgb_resulted; + hw_points = 0; + + memset(lut_params, 0, sizeof(struct pwl_params)); + memset(seg_distr, 0, sizeof(seg_distr)); + + if (output_tf->tf == TRANSFER_FUNCTION_PQ || + output_tf->tf == TRANSFER_FUNCTION_SRGB) { + /* 9 segments + * segments are from 2^-9 to 0 + */ + const uint8_t SEG_COUNT = 9; + seg_distr[0] = 0; // Since we only have one point in darkest region + for (k = 1; k < SEG_COUNT; k++) + seg_distr[k] = k - 1; // 2^(k-1) points per region; halves as k decreases + + region_start = -SEG_COUNT; + region_end = 0; + } else { + /* 12 segments + * segments are from 2^-12 to 2^0 + * There are less than 256 points, for optimization + */ + const uint8_t SEG_COUNT = 12; + + for (i = 0; i < SEG_COUNT; i++) + seg_distr[i] = 4; + + region_start = -SEG_COUNT; + region_end = 0; + } + + for (i = region_end - region_start; i < MAX_REGIONS_NUMBER ; i++) + seg_distr[i] = -1; + + for (k = 0; k < MAX_REGIONS_NUMBER; k++) { + if (seg_distr[k] != -1) + hw_points += (1 << seg_distr[k]); + } + + j = 0; + for (k = 0; k < (region_end - region_start); k++) { + increment = (NUMBER_SW_SEGMENTS << 4) / (1 << seg_distr[k]); + start_index = (region_start + k + MAX_LOW_POINT) * + NUMBER_SW_SEGMENTS; + for (i = (start_index << 4); + i < (start_index << 4) + (NUMBER_SW_SEGMENTS << 4); + i += increment) { + if (j == hw_points - 1) + break; + if ((i >> 4) + 1 >= TRANSFER_FUNC_POINTS) + return false; + + rgb_resulted[j].red = interp_tf_pts(output_tf->tf_pts.red, i); + rgb_resulted[j].green = interp_tf_pts(output_tf->tf_pts.green, i); + rgb_resulted[j].blue = interp_tf_pts(output_tf->tf_pts.blue, i); + j++; + } + } + + /* last point */ + start_index = (region_end + MAX_LOW_POINT) * NUMBER_SW_SEGMENTS; + rgb_resulted[hw_points - 1].red = output_tf->tf_pts.red[start_index]; + rgb_resulted[hw_points - 1].green = output_tf->tf_pts.green[start_index]; + rgb_resulted[hw_points - 1].blue = output_tf->tf_pts.blue[start_index]; + + corner_points[0].red.x = dc_fixpt_pow(dc_fixpt_from_int(2), + dc_fixpt_from_int(region_start)); + corner_points[0].green.x = corner_points[0].red.x; + corner_points[0].blue.x = corner_points[0].red.x; + corner_points[1].red.x = dc_fixpt_pow(dc_fixpt_from_int(2), + dc_fixpt_from_int(region_end)); + corner_points[1].green.x = corner_points[1].red.x; + corner_points[1].blue.x = corner_points[1].red.x; + + corner_points[0].red.y = rgb_resulted[0].red; + corner_points[0].green.y = rgb_resulted[0].green; + corner_points[0].blue.y = rgb_resulted[0].blue; + + /* see comment above, m_arrPoints[1].y should be the Y value for the + * region end (m_numOfHwPoints), not last HW point(m_numOfHwPoints - 1) + */ + corner_points[1].red.y = rgb_resulted[hw_points - 1].red; + corner_points[1].green.y = rgb_resulted[hw_points - 1].green; + corner_points[1].blue.y = rgb_resulted[hw_points - 1].blue; + corner_points[1].red.slope = dc_fixpt_zero; + corner_points[1].green.slope = dc_fixpt_zero; + corner_points[1].blue.slope = dc_fixpt_zero; + + if (output_tf->tf == TRANSFER_FUNCTION_PQ) { + /* for PQ, we want to have a straight line from last HW X point, + * and the slope to be such that we hit 1.0 at 10000 nits. + */ + const struct fixed31_32 end_value = + dc_fixpt_from_int(125); + + corner_points[1].red.slope = dc_fixpt_div( + dc_fixpt_sub(dc_fixpt_one, corner_points[1].red.y), + dc_fixpt_sub(end_value, corner_points[1].red.x)); + corner_points[1].green.slope = dc_fixpt_div( + dc_fixpt_sub(dc_fixpt_one, corner_points[1].green.y), + dc_fixpt_sub(end_value, corner_points[1].green.x)); + corner_points[1].blue.slope = dc_fixpt_div( + dc_fixpt_sub(dc_fixpt_one, corner_points[1].blue.y), + dc_fixpt_sub(end_value, corner_points[1].blue.x)); + } + + lut_params->hw_points_num = hw_points; + + k = 0; + for (i = 1; i < MAX_REGIONS_NUMBER; i++) { + if (seg_distr[k] != -1) { + lut_params->arr_curve_points[k].segments_num = + seg_distr[k]; + lut_params->arr_curve_points[i].offset = + lut_params->arr_curve_points[k].offset + (1 << seg_distr[k]); + } + k++; + } + + if (seg_distr[k] != -1) + lut_params->arr_curve_points[k].segments_num = seg_distr[k]; + + rgb = rgb_resulted; + rgb_plus_1 = rgb_resulted + 1; + + i = 1; + while (i != hw_points + 1) { + if (dc_fixpt_lt(rgb_plus_1->red, rgb->red)) + rgb_plus_1->red = rgb->red; + if (dc_fixpt_lt(rgb_plus_1->green, rgb->green)) + rgb_plus_1->green = rgb->green; + if (dc_fixpt_lt(rgb_plus_1->blue, rgb->blue)) + rgb_plus_1->blue = rgb->blue; + + rgb->delta_red = dc_fixpt_sub(rgb_plus_1->red, rgb->red); + rgb->delta_green = dc_fixpt_sub(rgb_plus_1->green, rgb->green); + rgb->delta_blue = dc_fixpt_sub(rgb_plus_1->blue, rgb->blue); + + ++rgb_plus_1; + ++rgb; + ++i; + } + cm3_helper_convert_to_custom_float(rgb_resulted, + lut_params->corner_points, + hw_points, false); + + return true; +} + bool cm3_helper_convert_to_custom_float( struct pwl_result_data *rgb_resulted, struct curve_points3 *corner_points, diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c index bcb791d74189..f57f3ba68a02 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.c @@ -516,6 +516,8 @@ void dcn31_link_encoder_construct_minimal( struct dc_context *ctx, const struct encoder_feature_support *enc_features, const struct dcn10_link_enc_registers *link_regs, + const struct dcn10_link_enc_shift *link_shift, + const struct dcn10_link_enc_mask *link_mask, enum engine_id eng_id) { struct dcn10_link_encoder *enc10 = &enc20->enc10; @@ -529,6 +531,8 @@ void dcn31_link_encoder_construct_minimal( enc10->base.features = *enc_features; enc10->base.transmitter = TRANSMITTER_UNKNOWN; enc10->link_regs = link_regs; + enc10->link_shift = link_shift; + enc10->link_mask = link_mask; enc10->base.output_signals = SIGNAL_TYPE_DISPLAY_PORT | diff --git a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h index 3cf587527991..88ab9684e207 100644 --- a/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h +++ b/drivers/gpu/drm/amd/display/dc/dio/dcn31/dcn31_dio_link_encoder.h @@ -246,6 +246,8 @@ void dcn31_link_encoder_construct_minimal( struct dc_context *ctx, const struct encoder_feature_support *enc_features, const struct dcn10_link_enc_registers *link_regs, + const struct dcn10_link_enc_shift *link_shift, + const struct dcn10_link_enc_mask *link_mask, enum engine_id eng_id); void dcn31_link_encoder_set_dio_phy_mux( diff --git a/drivers/gpu/drm/amd/display/dc/dm_services.h b/drivers/gpu/drm/amd/display/dc/dm_services.h index 8b062b011fc6..2cf4bcb03cb0 100644 --- a/drivers/gpu/drm/amd/display/dc/dm_services.h +++ b/drivers/gpu/drm/amd/display/dc/dm_services.h @@ -127,10 +127,6 @@ uint32_t generic_reg_update_ex(const struct dc_context *ctx, struct dc_dmub_srv *dc_dmub_srv_create(struct dc *dc, struct dmub_srv *dmub); void dc_dmub_srv_destroy(struct dc_dmub_srv **dmub_srv); -void reg_sequence_start_gather(const struct dc_context *ctx); -void reg_sequence_start_execute(const struct dc_context *ctx); -void reg_sequence_wait_done(const struct dc_context *ctx); - #define FD(reg_field) reg_field ## __SHIFT, \ reg_field ## _MASK diff --git a/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c b/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c index dcca23d53261..2ad4a2635683 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml/calcs/dcn_calcs.c @@ -1237,7 +1237,7 @@ bool dcn_validate_bandwidth( if (pipe->plane_state) { struct pipe_ctx *hsplit_pipe = pipe->bottom_pipe; - pipe->plane_state->update_flags.bits.full_update = 1; + pipe->plane_state->update_bits.full_update = 1; if (v->dpp_per_plane[input_idx] == 2 || ((pipe->stream->view_format == diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c index bd14ebea1111..8064c4b3fd25 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn31/display_mode_vba_31.c @@ -5337,7 +5337,7 @@ void dml31_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l for (j = 0; j <= 1; ++j) { double VMDataOnlyReturnBWPerState; double HostVMInefficiencyFactor = 1; - int NextPrefetchModeState = MinPrefetchMode; + unsigned int NextPrefetchModeState = MinPrefetchMode; bool UnboundedRequestEnabledThisState = false; unsigned int CompressedBufferSizeInkByteThisState = 0; double dummy; diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c index 2ea5cf37f273..bf2dde26b98b 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/display_mode_vba_314.c @@ -5421,7 +5421,7 @@ void dml314_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_ for (j = 0; j <= 1; ++j) { double VMDataOnlyReturnBWPerState; double HostVMInefficiencyFactor = 1; - int NextPrefetchModeState = MinPrefetchMode; + unsigned int NextPrefetchModeState = MinPrefetchMode; bool UnboundedRequestEnabledThisState = false; unsigned int CompressedBufferSizeInkByteThisState = 0; double dummy; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c index de40d7bae252..11fc0b1cd152 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_wrapper_fpu.c @@ -118,16 +118,16 @@ static void dml21_calculate_rq_and_dlg_params(const struct dc *dc, struct dc_sta context->bw_ctx.bw.dcn.clk.bw_dispclk_khz = context->bw_ctx.bw.dcn.clk.dispclk_khz; if (in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values > 1) { context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = - in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values] * 1000; + in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.num_clk_values - 1]; } else { - context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[0] * 1000; + context->bw_ctx.bw.dcn.clk.max_supported_dispclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dispclk.clk_values_khz[0]; } if (in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values > 1) { context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = - in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values] * 1000; + in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.num_clk_values - 1]; } else { - context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[0] * 1000; + context->bw_ctx.bw.dcn.clk.max_supported_dppclk_khz = in_ctx->v21.dml_init.soc_bb.clk_table.dppclk.clk_values_khz[0]; } /* get global mall allocation */ diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h index ce4025591b87..60ef56419846 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/bounding_boxes/dcn42b_soc_bb.h @@ -75,7 +75,7 @@ static const struct dml2_soc_bb dml2_socbb_dcn42b = { .clk_values_khz = {2}, }, .uclk = { - .clk_values_khz = {400000}, + .clk_values_khz = {2400000}, .num_clk_values = 1, }, .fclk = { @@ -224,4 +224,42 @@ static const struct dml2_soc_bb dml2_socbb_dcn42b = { .max_fclk_for_uclk_dpm_khz = 2200 * 1000, }; +static const struct dml2_ip_capabilities dml2_dcn42b_max_ip_caps = { + .pipe_count = 4, + .otg_count = 3, + .num_dsc = 3, + .max_num_dp2p0_streams = 3, + .max_num_hdmi_frl_outputs = 0, + .max_num_dp2p0_outputs = 2, + .rob_buffer_size_kbytes = 64, + .config_return_buffer_size_in_kbytes = 1792, + .config_return_buffer_segment_size_in_kbytes = 64, + .meta_fifo_size_in_kentries = 32, + .compressed_buffer_segment_size_in_kbytes = 64, + .cursor_buffer_size = 24, + .max_flip_time_us = 110, + .max_flip_time_lines = 50, + .hostvm_mode = 0, + .subvp_drr_scheduling_margin_us = 100, + .subvp_prefetch_end_to_mall_start_us = 15, + .subvp_fw_processing_delay = 15, + .max_vactive_det_fill_delay_us = 400, + + .fams2 = { + .max_allow_delay_us = 100 * 1000, + .scheduling_delay_us = 550, + .vertical_interrupt_ack_delay_us = 40, + .allow_programming_delay_us = 18, + .min_allow_width_us = 20, + .subvp_df_throttle_delay_us = 100, + .subvp_programming_delay_us = 200, + .subvp_prefetch_to_mall_delay_us = 18, + .drr_programming_delay_us = 35, + + .lock_timeout_us = 5000, + .recovery_timeout_us = 5000, + .flip_programming_delay_us = 300, + }, +}; + #endif diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h index 6152155d6073..672b96a3da74 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/inc/dml_top_soc_parameter_types.h @@ -71,8 +71,21 @@ enum dml2_qos_param_type { dml2_qos_param_type_dcn4x }; +//Indicies mapped to DPM level +// Unpopulated indicies should fallback to the global derate value. +struct dml2_soc_derate_values_per_dpm { + unsigned int dram_derate_percent_pixel[DML_MAX_CLK_TABLE_SIZE]; + unsigned int fclk_derate_percent[DML_MAX_CLK_TABLE_SIZE]; + unsigned int dcfclk_derate_percent[DML_MAX_CLK_TABLE_SIZE]; +}; + +struct dml2_soc_derates_per_dpm { + struct dml2_soc_derate_values_per_dpm system_active_derates_per_dpm; +}; + struct dml2_soc_qos_parameters { struct dml2_soc_derates derate_table; + struct dml2_soc_derates_per_dpm derate_table_per_dpm; struct { unsigned int base_latency_us; unsigned int scaling_factor_us; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c index f338e733318e..51a66e1be7a1 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c @@ -2701,7 +2701,8 @@ static double dml_get_return_bandwidth_available( bool is_hvm_only, double dcfclk_mhz, double fclk_mhz, - double dram_bw_mbps) + double dram_bw_mbps, + unsigned int uclk_dpm_level) { double return_bw_mbps = 0.; double ideal_sdp_bandwidth = (double)soc->return_bus_width_bytes * dcfclk_mhz; @@ -2722,9 +2723,16 @@ static double dml_get_return_bandwidth_available( derate_fabric_factor = soc->qos_parameters.derate_table.dcn_mall_prefetch_average.fclk_derate_percent / 100.0; derate_dram_factor = soc->qos_parameters.derate_table.dcn_mall_prefetch_average.dram_derate_percent_pixel / 100.0; } else { // just assume sys_active - derate_sdp_factor = soc->qos_parameters.derate_table.system_active_average.dcfclk_derate_percent / 100.0; - derate_fabric_factor = soc->qos_parameters.derate_table.system_active_average.fclk_derate_percent / 100.0; - derate_dram_factor = soc->qos_parameters.derate_table.system_active_average.dram_derate_percent_pixel / 100.0; + // use per dpm derates if the values are populated. Otherwise use global derates + derate_sdp_factor = soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.dcfclk_derate_percent[uclk_dpm_level] != 0 ? + soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.dcfclk_derate_percent[uclk_dpm_level] / 100.0 : + soc->qos_parameters.derate_table.system_active_average.dcfclk_derate_percent / 100.0; + derate_fabric_factor = soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.fclk_derate_percent[uclk_dpm_level] != 0 ? + soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.fclk_derate_percent[uclk_dpm_level] / 100.0 : + soc->qos_parameters.derate_table.system_active_average.fclk_derate_percent / 100.0; + derate_dram_factor = soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.dram_derate_percent_pixel[uclk_dpm_level] != 0 ? + soc->qos_parameters.derate_table_per_dpm.system_active_derates_per_dpm.dram_derate_percent_pixel[uclk_dpm_level] / 100.0 : + soc->qos_parameters.derate_table.system_active_average.dram_derate_percent_pixel / 100.0; } } else { // urgent bw if (state_type == dml2_core_internal_soc_state_svp_prefetch) { @@ -2778,6 +2786,7 @@ static double dml_get_return_bandwidth_available( DML_LOG_VERBOSE("DML::%s: derate_fabric_bandwidth = %f (derate %f)\n", __func__, derate_fabric_bandwidth, derate_fabric_factor); DML_LOG_VERBOSE("DML::%s: derate_dram_bandwidth = %f (derate %f)\n", __func__, derate_dram_bandwidth, derate_dram_factor); DML_LOG_VERBOSE("DML::%s: return_bw_mbps = %f\n", __func__, return_bw_mbps); + DML_LOG_VERBOSE("DML::%s: uclk_dpm_level = %u\n", __func__, uclk_dpm_level); return return_bw_mbps; } @@ -2793,7 +2802,8 @@ static noinline_for_stack void calculate_bandwidth_available( bool HostVMEnable, double dcfclk_mhz, double fclk_mhz, - double dram_bw_mbps) + double dram_bw_mbps, + unsigned int uclk_dpm_level) { unsigned int n, m; @@ -2812,9 +2822,10 @@ static noinline_for_stack void calculate_bandwidth_available( 0, // hvm_only dcfclk_mhz, fclk_mhz, - dram_bw_mbps); + dram_bw_mbps, + uclk_dpm_level); - urg_bandwidth_available[m][n] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 0, dcfclk_mhz, fclk_mhz, dram_bw_mbps); + urg_bandwidth_available[m][n] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 0, dcfclk_mhz, fclk_mhz, dram_bw_mbps, uclk_dpm_level); #ifdef __DML_VBA_DEBUG__ @@ -2824,8 +2835,8 @@ static noinline_for_stack void calculate_bandwidth_available( // urg_bandwidth_available_vm_only is indexed by soc_state if (n == dml2_core_internal_bw_dram) { - urg_bandwidth_available_vm_only[m] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 1, dcfclk_mhz, fclk_mhz, dram_bw_mbps); - urg_bandwidth_available_pixel_and_vm[m] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 0, dcfclk_mhz, fclk_mhz, dram_bw_mbps); + urg_bandwidth_available_vm_only[m] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 1, dcfclk_mhz, fclk_mhz, dram_bw_mbps, uclk_dpm_level); + urg_bandwidth_available_pixel_and_vm[m] = dml_get_return_bandwidth_available(soc, m, n, 0, HostVMEnable, 0, dcfclk_mhz, fclk_mhz, dram_bw_mbps, uclk_dpm_level); } } @@ -9483,7 +9494,8 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out display_cfg->hostvm_enable, mode_lib->ms.DCFCLK, mode_lib->ms.FabricClock, - mode_lib->ms.dram_bw_mbps); + mode_lib->ms.dram_bw_mbps, + mode_lib->ms.active_min_uclk_dpm_index); calculate_bandwidth_available( mode_lib->ms.support.avg_bandwidth_available_min, @@ -9498,10 +9510,12 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out mode_lib->ms.MaxDCFCLK, mode_lib->ms.MaxFabricClock, #ifdef DML_MODE_SUPPORT_USE_DPM_DRAM_BW - mode_lib->ms.dram_bw_mbps); + mode_lib->ms.dram_bw_mbps, #else - mode_lib->ms.max_dram_bw_mbps); + mode_lib->ms.max_dram_bw_mbps, #endif + mode_lib->ms.active_min_uclk_dpm_index); + // Average BW support check calculate_avg_bandwidth_required( @@ -10958,7 +10972,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex display_cfg->hostvm_enable, mode_lib->mp.Dcfclk, mode_lib->mp.FabricClock, - mode_lib->mp.dram_bw_mbps); + mode_lib->mp.dram_bw_mbps, + mode_lib->mp.active_min_uclk_dpm_index); calculate_hostvm_inefficiency_factor( diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c index 67e307fa4310..9f1222f5a835 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_factory.c @@ -15,8 +15,6 @@ bool dml2_core_create(enum dml2_project_id project_id, struct dml2_core_instance memset(out, 0, sizeof(struct dml2_core_instance)); - out->project_id = project_id; - switch (project_id) { case dml2_project_dcn4x_stage1: result = false; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h index 11e295253f72..e9f970794488 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_shared_types.h @@ -2329,7 +2329,6 @@ struct dml2_core_calcs_mode_support_ex { const struct dml2_display_cfg *in_display_cfg; const struct dml2_mcg_min_clock_table *min_clk_table; int min_clk_index; - enum dml2_project_id project_id; //unsigned int in_state_index; struct dml2_core_internal_mode_support_info *out_evaluation_info; }; @@ -2342,7 +2341,6 @@ struct dml2_core_calcs_mode_programming_ex { const struct dml2_mcg_min_clock_table *min_clk_table; const struct core_display_cfg_support_info *cfg_support_info; int min_clk_index; - enum dml2_project_id project_id; struct dml2_display_cfg_programming *programming; }; diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h index d328d92240b4..3ae817ea2aad 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/inc/dml2_internal_shared_types.h @@ -489,7 +489,6 @@ struct dml2_core_scratch { }; struct dml2_core_instance { - enum dml2_project_id project_id; struct dml2_mcg_min_clock_table *minimum_clock_table; struct dml2_core_internal_state_inputs inputs; struct dml2_core_internal_state_intermediates intermediates; diff --git a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c index 53b21adc6267..9788628cf0ad 100644 --- a/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dpp/dcn10/dcn10_dpp_cm.c @@ -397,8 +397,6 @@ void dpp1_cm_program_regamma_lut(struct dpp *dpp_base, uint32_t i; struct dcn10_dpp *dpp = TO_DCN10_DPP(dpp_base); - REG_SEQ_START(); - for (i = 0 ; i < num; i++) { REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].red_reg); REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].green_reg); @@ -408,9 +406,6 @@ void dpp1_cm_program_regamma_lut(struct dpp *dpp_base, REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].delta_green_reg); REG_SET(CM_RGAM_LUT_DATA, 0, CM_RGAM_LUT_DATA, rgb[i].delta_blue_reg); } - - REG_SEQ_SUBMIT(); - REG_SEQ_WAIT_DONE(); } void dpp1_cm_configure_regamma_lut( diff --git a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h index 95f9318a54ef..c23dc1bb29bf 100644 --- a/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h +++ b/drivers/gpu/drm/amd/display/dc/dwb/dcn30/dcn30_cm_common.h @@ -63,6 +63,10 @@ bool cm3_helper_translate_curve_to_hw_format(struct dc_context *ctx, const struct dc_transfer_func *output_tf, struct pwl_params *lut_params, bool fixpoint); +bool cm3_helper_translate_curve_to_degamma_hw_format( + const struct dc_transfer_func *output_tf, + struct pwl_params *lut_params); + bool cm3_helper_convert_to_custom_float( struct pwl_result_data *rgb_resulted, struct curve_points3 *corner_points, diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c index fabb9da504be..19d148a85f12 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dce80/hw_translate_dce80.c @@ -35,7 +35,10 @@ #include "dce/dce_8_0_d.h" #include "dce/dce_8_0_sh_mask.h" -#include "smu/smu_7_0_1_d.h" + +#ifndef mmGPIOPAD_A +#define mmGPIOPAD_A 0x0183 +#endif /* * @brief diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c index fecc8688048d..000f603def58 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn10/hw_translate_dcn10.c @@ -58,146 +58,180 @@ /* macros to expend register list macro defined in HW object header file * end *********************/ +static const struct gpio_id_offset_entry gpio_offsets[] = { + /* GENERIC */ + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_A), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_B), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_C), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_D), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_E), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_F), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_G), + /* HPD */ + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK, + GPIO_ID_HPD, GPIO_HPD_1), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK, + GPIO_ID_HPD, GPIO_HPD_2), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK, + GPIO_ID_HPD, GPIO_HPD_3), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK, + GPIO_ID_HPD, GPIO_HPD_4), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK, + GPIO_ID_HPD, GPIO_HPD_5), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK, + GPIO_ID_HPD, GPIO_HPD_6), + /* SYNCA */ + GPIO_MASK_ENTRY(DC_GPIO_SYNCA_A, + DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK, + GPIO_ID_SYNC, GPIO_SYNC_HSYNC_A), + GPIO_MASK_ENTRY(DC_GPIO_SYNCA_A, + DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK, + GPIO_ID_SYNC, GPIO_SYNC_VSYNC_A), + /* GSL */ + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B), +}; + +/* DDC */ +static const struct gpio_ddc_offset_entry ddc_offset_map[] = { + { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 }, + { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 }, + { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 }, + { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 }, + { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 }, + { REG(DC_GPIO_DDC6_A), GPIO_DDC_LINE_DDC6 }, + { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA }, + { REG(DC_GPIO_I2CPAD_A), GPIO_DDC_LINE_I2C_PAD }, +}; + +static const struct gpio_pin_entry gpio_pins[] = { + /* DDC */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC6, + DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_I2C_PAD, + DC_GPIO_I2CPAD_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC6, + DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_I2C_PAD, + DC_GPIO_I2CPAD_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + /* GENERIC */ + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK), + /* HPD */ + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK), + /* SYNCA */ + GPIO_PIN_ENTRY(GPIO_ID_SYNC, GPIO_SYNC_HSYNC_A, + DC_GPIO_SYNCA_A, DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_SYNC, GPIO_SYNC_VSYNC_A, + DC_GPIO_SYNCA_A, DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK), + /* GSL */ + GPIO_PIN_ENTRY(GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK, + DC_GPIO_GENLK_A, DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC, + DC_GPIO_GENLK_A, DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A, + DC_GPIO_GENLK_A, DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B, + DC_GPIO_GENLK_A, DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK), +}; + static bool offset_to_id( uint32_t offset, uint32_t mask, enum gpio_id *id, uint32_t *en) { - switch (offset) { - /* GENERIC */ - case REG(DC_GPIO_GENERIC_A): - *id = GPIO_ID_GENERIC; - switch (mask) { - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: - *en = GPIO_GENERIC_A; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: - *en = GPIO_GENERIC_B; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: - *en = GPIO_GENERIC_C; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: - *en = GPIO_GENERIC_D; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: - *en = GPIO_GENERIC_E; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: - *en = GPIO_GENERIC_F; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: - *en = GPIO_GENERIC_G; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* HPD */ - case REG(DC_GPIO_HPD_A): - *id = GPIO_ID_HPD; - switch (mask) { - case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: - *en = GPIO_HPD_1; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: - *en = GPIO_HPD_2; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: - *en = GPIO_HPD_3; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: - *en = GPIO_HPD_4; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: - *en = GPIO_HPD_5; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: - *en = GPIO_HPD_6; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* SYNCA */ - case REG(DC_GPIO_SYNCA_A): - *id = GPIO_ID_SYNC; - switch (mask) { - case DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK: - *en = GPIO_SYNC_HSYNC_A; - return true; - case DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK: - *en = GPIO_SYNC_VSYNC_A; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* REG(DC_GPIO_GENLK_MASK */ - case REG(DC_GPIO_GENLK_A): - *id = GPIO_ID_GSL; - switch (mask) { - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: - *en = GPIO_GSL_GENLOCK_CLOCK; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: - *en = GPIO_GSL_GENLOCK_VSYNC; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: - *en = GPIO_GSL_SWAPLOCK_A; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: - *en = GPIO_GSL_SWAPLOCK_B; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* DDC */ - /* we don't care about the GPIO_ID for DDC - * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK - * directly in the create method */ - case REG(DC_GPIO_DDC1_A): - *en = GPIO_DDC_LINE_DDC1; - return true; - case REG(DC_GPIO_DDC2_A): - *en = GPIO_DDC_LINE_DDC2; - return true; - case REG(DC_GPIO_DDC3_A): - *en = GPIO_DDC_LINE_DDC3; - return true; - case REG(DC_GPIO_DDC4_A): - *en = GPIO_DDC_LINE_DDC4; - return true; - case REG(DC_GPIO_DDC5_A): - *en = GPIO_DDC_LINE_DDC5; - return true; - case REG(DC_GPIO_DDC6_A): - *en = GPIO_DDC_LINE_DDC6; + if (dal_hw_translate_gpio_ddc_offset_to_id( + ddc_offset_map, + ARRAY_SIZE(ddc_offset_map), + offset, en)) return true; - case REG(DC_GPIO_DDCVGA_A): - *en = GPIO_DDC_LINE_DDC_VGA; - return true; - /* GPIO_I2CPAD */ - case REG(DC_GPIO_I2CPAD_A): - *en = GPIO_DDC_LINE_I2C_PAD; + + if (dal_hw_translate_gpio_offset_to_id( + gpio_offsets, + ARRAY_SIZE(gpio_offsets), + offset, mask, id, en)) return true; - /* Not implemented */ - case REG(DC_GPIO_PWRSEQ_A): - case REG(DC_GPIO_PAD_STRENGTH_1): - case REG(DC_GPIO_PAD_STRENGTH_2): - case REG(DC_GPIO_DEBUG): - return false; - /* UNEXPECTED */ - default: - ASSERT_CRITICAL(false); - return false; - } + + ASSERT_CRITICAL(false); + return false; } static bool id_to_offset( @@ -205,186 +239,14 @@ static bool id_to_offset( uint32_t en, struct gpio_pin_info *info) { - bool result = true; - - switch (id) { - case GPIO_ID_DDC_DATA: - info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC6: - info->offset = REG(DC_GPIO_DDC6_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - info->offset = REG(DC_GPIO_I2CPAD_A); - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_DDC_CLOCK: - info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC6: - info->offset = REG(DC_GPIO_DDC6_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - info->offset = REG(DC_GPIO_I2CPAD_A); - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GENERIC: - info->offset = REG(DC_GPIO_GENERIC_A); - switch (en) { - case GPIO_GENERIC_A: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; - break; - case GPIO_GENERIC_B: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; - break; - case GPIO_GENERIC_C: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; - break; - case GPIO_GENERIC_D: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; - break; - case GPIO_GENERIC_E: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; - break; - case GPIO_GENERIC_F: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; - break; - case GPIO_GENERIC_G: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_HPD: - info->offset = REG(DC_GPIO_HPD_A); - switch (en) { - case GPIO_HPD_1: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; - break; - case GPIO_HPD_2: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; - break; - case GPIO_HPD_3: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; - break; - case GPIO_HPD_4: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; - break; - case GPIO_HPD_5: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; - break; - case GPIO_HPD_6: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_SYNC: - switch (en) { - case GPIO_SYNC_HSYNC_A: - info->offset = REG(DC_GPIO_SYNCA_A); - info->mask = DC_GPIO_SYNCA_A__DC_GPIO_HSYNCA_A_MASK; - break; - case GPIO_SYNC_VSYNC_A: - info->offset = REG(DC_GPIO_SYNCA_A); - info->mask = DC_GPIO_SYNCA_A__DC_GPIO_VSYNCA_A_MASK; - break; - case GPIO_SYNC_HSYNC_B: - case GPIO_SYNC_VSYNC_B: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GSL: - switch (en) { - case GPIO_GSL_GENLOCK_CLOCK: - info->offset = REG(DC_GPIO_GENLK_A); - info->mask = DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK; - break; - case GPIO_GSL_GENLOCK_VSYNC: - info->offset = REG(DC_GPIO_GENLK_A); - info->mask = - DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK; - break; - case GPIO_GSL_SWAPLOCK_A: - info->offset = REG(DC_GPIO_GENLK_A); - info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK; - break; - case GPIO_GSL_SWAPLOCK_B: - info->offset = REG(DC_GPIO_GENLK_A); - info->mask = DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_VIP_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - - if (result) { - info->offset_y = info->offset + 2; - info->offset_en = info->offset + 1; - info->offset_mask = info->offset - 1; - - info->mask_y = info->mask; - info->mask_en = info->mask; - info->mask_mask = info->mask; - } + if (dal_hw_translate_id_to_offset( + gpio_pins, + ARRAY_SIZE(gpio_pins), + id, en, info)) + return true; - return result; + ASSERT_CRITICAL(false); + return false; } /* function table */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_translate_dcn20.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_translate_dcn20.c index 3005ee7751a0..a21df8668266 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_translate_dcn20.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn20/hw_translate_dcn20.c @@ -62,131 +62,161 @@ * end *********************/ +static const struct gpio_id_offset_entry gpio_offsets[] = { + /* GENERIC */ + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_A), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_B), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_C), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_D), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_E), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_F), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_G), + /* HPD */ + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK, + GPIO_ID_HPD, GPIO_HPD_1), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK, + GPIO_ID_HPD, GPIO_HPD_2), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK, + GPIO_ID_HPD, GPIO_HPD_3), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK, + GPIO_ID_HPD, GPIO_HPD_4), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK, + GPIO_ID_HPD, GPIO_HPD_5), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK, + GPIO_ID_HPD, GPIO_HPD_6), + /* GSL */ + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B), +}; + + +/* DDC */ +static const struct gpio_ddc_offset_entry ddc_offset_map[] = { + { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 }, + { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 }, + { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 }, + { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 }, + { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 }, + { REG(DC_GPIO_DDC6_A), GPIO_DDC_LINE_DDC6 }, + { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA }, +}; + + +/* + * GSL is intentionally omitted here. + * id_to_offset() for GSL is not implemented on this ASIC. + */ +static const struct gpio_pin_entry gpio_pins[] = { + /* DDC */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC6, + DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC6, + DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + /* GENERIC */ + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK), + /* HPD */ + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK), +}; + + static bool offset_to_id( uint32_t offset, uint32_t mask, enum gpio_id *id, uint32_t *en) { - switch (offset) { - /* GENERIC */ - case REG(DC_GPIO_GENERIC_A): - *id = GPIO_ID_GENERIC; - switch (mask) { - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: - *en = GPIO_GENERIC_A; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: - *en = GPIO_GENERIC_B; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: - *en = GPIO_GENERIC_C; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: - *en = GPIO_GENERIC_D; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: - *en = GPIO_GENERIC_E; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: - *en = GPIO_GENERIC_F; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: - *en = GPIO_GENERIC_G; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* HPD */ - case REG(DC_GPIO_HPD_A): - *id = GPIO_ID_HPD; - switch (mask) { - case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: - *en = GPIO_HPD_1; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: - *en = GPIO_HPD_2; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: - *en = GPIO_HPD_3; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: - *en = GPIO_HPD_4; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: - *en = GPIO_HPD_5; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: - *en = GPIO_HPD_6; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* REG(DC_GPIO_GENLK_MASK */ - case REG(DC_GPIO_GENLK_A): - *id = GPIO_ID_GSL; - switch (mask) { - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: - *en = GPIO_GSL_GENLOCK_CLOCK; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: - *en = GPIO_GSL_GENLOCK_VSYNC; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: - *en = GPIO_GSL_SWAPLOCK_A; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: - *en = GPIO_GSL_SWAPLOCK_B; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* DDC */ - /* we don't care about the GPIO_ID for DDC - * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK - * directly in the create method - */ - case REG(DC_GPIO_DDC1_A): - *en = GPIO_DDC_LINE_DDC1; - return true; - case REG(DC_GPIO_DDC2_A): - *en = GPIO_DDC_LINE_DDC2; - return true; - case REG(DC_GPIO_DDC3_A): - *en = GPIO_DDC_LINE_DDC3; - return true; - case REG(DC_GPIO_DDC4_A): - *en = GPIO_DDC_LINE_DDC4; - return true; - case REG(DC_GPIO_DDC5_A): - *en = GPIO_DDC_LINE_DDC5; - return true; - case REG(DC_GPIO_DDC6_A): - *en = GPIO_DDC_LINE_DDC6; + if (dal_hw_translate_gpio_ddc_offset_to_id( + ddc_offset_map, + ARRAY_SIZE(ddc_offset_map), + offset, en)) return true; - case REG(DC_GPIO_DDCVGA_A): - *en = GPIO_DDC_LINE_DDC_VGA; + + if (dal_hw_translate_gpio_offset_to_id( + gpio_offsets, + ARRAY_SIZE(gpio_offsets), + offset, mask, id, en)) return true; -/* - * case REG(DC_GPIO_I2CPAD_A): not exit - * case REG(DC_GPIO_PWRSEQ_A): - * case REG(DC_GPIO_PAD_STRENGTH_1): - * case REG(DC_GPIO_PAD_STRENGTH_2): - * case REG(DC_GPIO_DEBUG): - */ - /* UNEXPECTED */ - default: -/* case REG(DC_GPIO_SYNCA_A): not exist */ - ASSERT_CRITICAL(false); - return false; - } + ASSERT_CRITICAL(false); + return false; } static bool id_to_offset( @@ -194,170 +224,14 @@ static bool id_to_offset( uint32_t en, struct gpio_pin_info *info) { - bool result = true; - - switch (id) { - case GPIO_ID_DDC_DATA: - info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC6: - info->offset = REG(DC_GPIO_DDC6_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_DDC_CLOCK: - info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC6: - info->offset = REG(DC_GPIO_DDC6_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GENERIC: - info->offset = REG(DC_GPIO_GENERIC_A); - switch (en) { - case GPIO_GENERIC_A: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; - break; - case GPIO_GENERIC_B: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; - break; - case GPIO_GENERIC_C: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; - break; - case GPIO_GENERIC_D: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; - break; - case GPIO_GENERIC_E: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; - break; - case GPIO_GENERIC_F: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; - break; - case GPIO_GENERIC_G: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_HPD: - info->offset = REG(DC_GPIO_HPD_A); - switch (en) { - case GPIO_HPD_1: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; - break; - case GPIO_HPD_2: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; - break; - case GPIO_HPD_3: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; - break; - case GPIO_HPD_4: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; - break; - case GPIO_HPD_5: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; - break; - case GPIO_HPD_6: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GSL: - switch (en) { - case GPIO_GSL_GENLOCK_CLOCK: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_GENLOCK_VSYNC: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_A: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_B: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_SYNC: - case GPIO_ID_VIP_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - - if (result) { - info->offset_y = info->offset + 2; - info->offset_en = info->offset + 1; - info->offset_mask = info->offset - 1; - - info->mask_y = info->mask; - info->mask_en = info->mask; - info->mask_mask = info->mask; - } + if (dal_hw_translate_id_to_offset( + gpio_pins, + ARRAY_SIZE(gpio_pins), + id, en, info)) + return true; - return result; + ASSERT_CRITICAL(false); + return false; } /* function table */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c index e3b11b3c1daa..18bd4d4e32d0 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn21/hw_translate_dcn21.c @@ -60,6 +60,135 @@ /* macros to expend register list macro defined in HW object header file * end *********************/ +static const struct gpio_id_offset_entry gpio_offsets[] = { + /* GENERIC */ + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_A), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_B), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_C), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_D), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_E), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_F), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_G), + /* HPD */ + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK, + GPIO_ID_HPD, GPIO_HPD_1), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK, + GPIO_ID_HPD, GPIO_HPD_2), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK, + GPIO_ID_HPD, GPIO_HPD_3), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK, + GPIO_ID_HPD, GPIO_HPD_4), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK, + GPIO_ID_HPD, GPIO_HPD_5), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK, + GPIO_ID_HPD, GPIO_HPD_6), + /* GSL */ + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B), +}; + + +/* DDC */ +static const struct gpio_ddc_offset_entry ddc_offset_map[] = { + { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 }, + { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 }, + { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 }, + { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 }, + { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 }, + { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA }, +}; + + +/* + * GSL is intentionally omitted here. + * id_to_offset() for GSL is not implemented on this ASIC. + */ +static const struct gpio_pin_entry gpio_pins[] = { + /* DDC */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK), + /* GENERIC */ + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK), + /* HPD */ + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK), +}; + static bool offset_to_id( uint32_t offset, @@ -67,122 +196,20 @@ static bool offset_to_id( enum gpio_id *id, uint32_t *en) { - switch (offset) { - /* GENERIC */ - case REG(DC_GPIO_GENERIC_A): - *id = GPIO_ID_GENERIC; - switch (mask) { - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: - *en = GPIO_GENERIC_A; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: - *en = GPIO_GENERIC_B; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: - *en = GPIO_GENERIC_C; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: - *en = GPIO_GENERIC_D; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: - *en = GPIO_GENERIC_E; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: - *en = GPIO_GENERIC_F; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: - *en = GPIO_GENERIC_G; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* HPD */ - case REG(DC_GPIO_HPD_A): - *id = GPIO_ID_HPD; - switch (mask) { - case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: - *en = GPIO_HPD_1; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: - *en = GPIO_HPD_2; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: - *en = GPIO_HPD_3; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: - *en = GPIO_HPD_4; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: - *en = GPIO_HPD_5; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: - *en = GPIO_HPD_6; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* REG(DC_GPIO_GENLK_MASK */ - case REG(DC_GPIO_GENLK_A): - *id = GPIO_ID_GSL; - switch (mask) { - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: - *en = GPIO_GSL_GENLOCK_CLOCK; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: - *en = GPIO_GSL_GENLOCK_VSYNC; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: - *en = GPIO_GSL_SWAPLOCK_A; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: - *en = GPIO_GSL_SWAPLOCK_B; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* DDC */ - /* we don't care about the GPIO_ID for DDC - * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK - * directly in the create method - */ - case REG(DC_GPIO_DDC1_A): - *en = GPIO_DDC_LINE_DDC1; - return true; - case REG(DC_GPIO_DDC2_A): - *en = GPIO_DDC_LINE_DDC2; + if (dal_hw_translate_gpio_ddc_offset_to_id( + ddc_offset_map, + ARRAY_SIZE(ddc_offset_map), + offset, en)) return true; - case REG(DC_GPIO_DDC3_A): - *en = GPIO_DDC_LINE_DDC3; - return true; - case REG(DC_GPIO_DDC4_A): - *en = GPIO_DDC_LINE_DDC4; - return true; - case REG(DC_GPIO_DDC5_A): - *en = GPIO_DDC_LINE_DDC5; - return true; - case REG(DC_GPIO_DDCVGA_A): - *en = GPIO_DDC_LINE_DDC_VGA; + + if (dal_hw_translate_gpio_offset_to_id( + gpio_offsets, + ARRAY_SIZE(gpio_offsets), + offset, mask, id, en)) return true; -/* - * case REG(DC_GPIO_I2CPAD_A): not exit - * case REG(DC_GPIO_PWRSEQ_A): - * case REG(DC_GPIO_PAD_STRENGTH_1): - * case REG(DC_GPIO_PAD_STRENGTH_2): - * case REG(DC_GPIO_DEBUG): - */ - /* UNEXPECTED */ - default: -/* case REG(DC_GPIO_SYNCA_A): not exist */ - ASSERT_CRITICAL(false); - return false; - } + ASSERT_CRITICAL(false); + return false; } static bool id_to_offset( @@ -190,164 +217,14 @@ static bool id_to_offset( uint32_t en, struct gpio_pin_info *info) { - bool result = true; - - switch (id) { - case GPIO_ID_DDC_DATA: - info->mask = DC_GPIO_DDC5_A__DC_GPIO_DDC5DATA_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_DDC_CLOCK: - info->mask = DC_GPIO_DDC5_A__DC_GPIO_DDC5CLK_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GENERIC: - info->offset = REG(DC_GPIO_GENERIC_A); - switch (en) { - case GPIO_GENERIC_A: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; - break; - case GPIO_GENERIC_B: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; - break; - case GPIO_GENERIC_C: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; - break; - case GPIO_GENERIC_D: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; - break; - case GPIO_GENERIC_E: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; - break; - case GPIO_GENERIC_F: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; - break; - case GPIO_GENERIC_G: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_HPD: - info->offset = REG(DC_GPIO_HPD_A); - switch (en) { - case GPIO_HPD_1: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; - break; - case GPIO_HPD_2: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; - break; - case GPIO_HPD_3: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; - break; - case GPIO_HPD_4: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; - break; - case GPIO_HPD_5: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; - break; - case GPIO_HPD_6: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GSL: - switch (en) { - case GPIO_GSL_GENLOCK_CLOCK: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_GENLOCK_VSYNC: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_A: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_B: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_SYNC: - case GPIO_ID_VIP_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - - if (result) { - info->offset_y = info->offset + 2; - info->offset_en = info->offset + 1; - info->offset_mask = info->offset - 1; - - info->mask_y = info->mask; - info->mask_en = info->mask; - info->mask_mask = info->mask; - } + if (dal_hw_translate_id_to_offset( + gpio_pins, + ARRAY_SIZE(gpio_pins), + id, en, info)) + return true; - return result; + ASSERT_CRITICAL(false); + return false; } /* function table */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_translate_dcn30.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_translate_dcn30.c index 49d6250037a9..c4225231f725 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_translate_dcn30.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn30/hw_translate_dcn30.c @@ -67,131 +67,161 @@ * end *********************/ +static const struct gpio_id_offset_entry gpio_offsets[] = { + /* GENERIC */ + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_A), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_B), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_C), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_D), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_E), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_F), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_G), + /* HPD */ + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK, + GPIO_ID_HPD, GPIO_HPD_1), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK, + GPIO_ID_HPD, GPIO_HPD_2), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK, + GPIO_ID_HPD, GPIO_HPD_3), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK, + GPIO_ID_HPD, GPIO_HPD_4), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK, + GPIO_ID_HPD, GPIO_HPD_5), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK, + GPIO_ID_HPD, GPIO_HPD_6), + /* GSL */ + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B), +}; + + +/* DDC */ +static const struct gpio_ddc_offset_entry ddc_offset_map[] = { + { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 }, + { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 }, + { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 }, + { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 }, + { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 }, + { REG(DC_GPIO_DDC6_A), GPIO_DDC_LINE_DDC6 }, + { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA }, +}; + + +/* + * GSL is intentionally omitted here. + * id_to_offset() for GSL is not implemented on this ASIC. + */ +static const struct gpio_pin_entry gpio_pins[] = { + /* DDC */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC6, + DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC6, + DC_GPIO_DDC6_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK), + /* GENERIC */ + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK), + /* HPD */ + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK), +}; + + static bool offset_to_id( uint32_t offset, uint32_t mask, enum gpio_id *id, uint32_t *en) { - switch (offset) { - /* GENERIC */ - case REG(DC_GPIO_GENERIC_A): - *id = GPIO_ID_GENERIC; - switch (mask) { - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: - *en = GPIO_GENERIC_A; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: - *en = GPIO_GENERIC_B; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: - *en = GPIO_GENERIC_C; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: - *en = GPIO_GENERIC_D; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: - *en = GPIO_GENERIC_E; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: - *en = GPIO_GENERIC_F; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: - *en = GPIO_GENERIC_G; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* HPD */ - case REG(DC_GPIO_HPD_A): - *id = GPIO_ID_HPD; - switch (mask) { - case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: - *en = GPIO_HPD_1; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: - *en = GPIO_HPD_2; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: - *en = GPIO_HPD_3; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: - *en = GPIO_HPD_4; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: - *en = GPIO_HPD_5; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: - *en = GPIO_HPD_6; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* REG(DC_GPIO_GENLK_MASK */ - case REG(DC_GPIO_GENLK_A): - *id = GPIO_ID_GSL; - switch (mask) { - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: - *en = GPIO_GSL_GENLOCK_CLOCK; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: - *en = GPIO_GSL_GENLOCK_VSYNC; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: - *en = GPIO_GSL_SWAPLOCK_A; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: - *en = GPIO_GSL_SWAPLOCK_B; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* DDC */ - /* we don't care about the GPIO_ID for DDC - * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK - * directly in the create method - */ - case REG(DC_GPIO_DDC1_A): - *en = GPIO_DDC_LINE_DDC1; - return true; - case REG(DC_GPIO_DDC2_A): - *en = GPIO_DDC_LINE_DDC2; - return true; - case REG(DC_GPIO_DDC3_A): - *en = GPIO_DDC_LINE_DDC3; - return true; - case REG(DC_GPIO_DDC4_A): - *en = GPIO_DDC_LINE_DDC4; - return true; - case REG(DC_GPIO_DDC5_A): - *en = GPIO_DDC_LINE_DDC5; - return true; - case REG(DC_GPIO_DDC6_A): - *en = GPIO_DDC_LINE_DDC6; + if (dal_hw_translate_gpio_ddc_offset_to_id( + ddc_offset_map, + ARRAY_SIZE(ddc_offset_map), + offset, en)) return true; - case REG(DC_GPIO_DDCVGA_A): - *en = GPIO_DDC_LINE_DDC_VGA; + + if (dal_hw_translate_gpio_offset_to_id( + gpio_offsets, + ARRAY_SIZE(gpio_offsets), + offset, mask, id, en)) return true; -/* - * case REG(DC_GPIO_I2CPAD_A): not exit - * case REG(DC_GPIO_PWRSEQ_A): - * case REG(DC_GPIO_PAD_STRENGTH_1): - * case REG(DC_GPIO_PAD_STRENGTH_2): - * case REG(DC_GPIO_DEBUG): - */ - /* UNEXPECTED */ - default: -/* case REG(DC_GPIO_SYNCA_A): not exist */ - ASSERT_CRITICAL(false); - return false; - } + ASSERT_CRITICAL(false); + return false; } static bool id_to_offset( @@ -199,170 +229,14 @@ static bool id_to_offset( uint32_t en, struct gpio_pin_info *info) { - bool result = true; - - switch (id) { - case GPIO_ID_DDC_DATA: - info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6DATA_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC6: - info->offset = REG(DC_GPIO_DDC6_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_DDC_CLOCK: - info->mask = DC_GPIO_DDC6_A__DC_GPIO_DDC6CLK_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC6: - info->offset = REG(DC_GPIO_DDC6_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GENERIC: - info->offset = REG(DC_GPIO_GENERIC_A); - switch (en) { - case GPIO_GENERIC_A: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; - break; - case GPIO_GENERIC_B: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; - break; - case GPIO_GENERIC_C: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; - break; - case GPIO_GENERIC_D: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; - break; - case GPIO_GENERIC_E: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; - break; - case GPIO_GENERIC_F: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; - break; - case GPIO_GENERIC_G: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_HPD: - info->offset = REG(DC_GPIO_HPD_A); - switch (en) { - case GPIO_HPD_1: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; - break; - case GPIO_HPD_2: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; - break; - case GPIO_HPD_3: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; - break; - case GPIO_HPD_4: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; - break; - case GPIO_HPD_5: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; - break; - case GPIO_HPD_6: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GSL: - switch (en) { - case GPIO_GSL_GENLOCK_CLOCK: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_GENLOCK_VSYNC: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_A: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_B: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_SYNC: - case GPIO_ID_VIP_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - - if (result) { - info->offset_y = info->offset + 2; - info->offset_en = info->offset + 1; - info->offset_mask = info->offset - 1; - - info->mask_y = info->mask; - info->mask_en = info->mask; - info->mask_mask = info->mask; - } + if (dal_hw_translate_id_to_offset( + gpio_pins, + ARRAY_SIZE(gpio_pins), + id, en, info)) + return true; - return result; + ASSERT_CRITICAL(false); + return false; } /* function table */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn315/hw_translate_dcn315.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn315/hw_translate_dcn315.c index fbdaba57f718..aa507f7f4ef9 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn315/hw_translate_dcn315.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn315/hw_translate_dcn315.c @@ -62,128 +62,156 @@ * end *********************/ +static const struct gpio_id_offset_entry gpio_offsets[] = { + /* GENERIC */ + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_A), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_B), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_C), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_D), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_E), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_F), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_G), + /* HPD */ + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK, + GPIO_ID_HPD, GPIO_HPD_1), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK, + GPIO_ID_HPD, GPIO_HPD_2), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK, + GPIO_ID_HPD, GPIO_HPD_3), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK, + GPIO_ID_HPD, GPIO_HPD_4), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK, + GPIO_ID_HPD, GPIO_HPD_5), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK, + GPIO_ID_HPD, GPIO_HPD_6), + /* GSL */ + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B), +}; + + +/* DDC */ +static const struct gpio_ddc_offset_entry ddc_offset_map[] = { + { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 }, + { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 }, + { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 }, + { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 }, + { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 }, + { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA }, +}; + + +/* + * GSL is intentionally omitted here. + * id_to_offset() for GSL is not implemented on this ASIC. + */ +static const struct gpio_pin_entry gpio_pins[] = { + /* DDC */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + /* GENERIC */ + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_G, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK), + /* HPD */ + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_6, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK), +}; + + static bool offset_to_id( uint32_t offset, uint32_t mask, enum gpio_id *id, uint32_t *en) { - switch (offset) { - /* GENERIC */ - case REG(DC_GPIO_GENERIC_A): - *id = GPIO_ID_GENERIC; - switch (mask) { - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: - *en = GPIO_GENERIC_A; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: - *en = GPIO_GENERIC_B; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: - *en = GPIO_GENERIC_C; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: - *en = GPIO_GENERIC_D; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: - *en = GPIO_GENERIC_E; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: - *en = GPIO_GENERIC_F; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK: - *en = GPIO_GENERIC_G; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* HPD */ - case REG(DC_GPIO_HPD_A): - *id = GPIO_ID_HPD; - switch (mask) { - case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: - *en = GPIO_HPD_1; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: - *en = GPIO_HPD_2; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: - *en = GPIO_HPD_3; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: - *en = GPIO_HPD_4; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: - *en = GPIO_HPD_5; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK: - *en = GPIO_HPD_6; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* REG(DC_GPIO_GENLK_MASK */ - case REG(DC_GPIO_GENLK_A): - *id = GPIO_ID_GSL; - switch (mask) { - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: - *en = GPIO_GSL_GENLOCK_CLOCK; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: - *en = GPIO_GSL_GENLOCK_VSYNC; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: - *en = GPIO_GSL_SWAPLOCK_A; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: - *en = GPIO_GSL_SWAPLOCK_B; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* DDC */ - /* we don't care about the GPIO_ID for DDC - * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK - * directly in the create method - */ - case REG(DC_GPIO_DDC1_A): - *en = GPIO_DDC_LINE_DDC1; - return true; - case REG(DC_GPIO_DDC2_A): - *en = GPIO_DDC_LINE_DDC2; + if (dal_hw_translate_gpio_ddc_offset_to_id( + ddc_offset_map, + ARRAY_SIZE(ddc_offset_map), + offset, en)) return true; - case REG(DC_GPIO_DDC3_A): - *en = GPIO_DDC_LINE_DDC3; - return true; - case REG(DC_GPIO_DDC4_A): - *en = GPIO_DDC_LINE_DDC4; - return true; - case REG(DC_GPIO_DDC5_A): - *en = GPIO_DDC_LINE_DDC5; - return true; - case REG(DC_GPIO_DDCVGA_A): - *en = GPIO_DDC_LINE_DDC_VGA; + + if (dal_hw_translate_gpio_offset_to_id( + gpio_offsets, + ARRAY_SIZE(gpio_offsets), + offset, mask, id, en)) return true; -/* - * case REG(DC_GPIO_I2CPAD_A): not exit - * case REG(DC_GPIO_PWRSEQ_A): - * case REG(DC_GPIO_PAD_STRENGTH_1): - * case REG(DC_GPIO_PAD_STRENGTH_2): - * case REG(DC_GPIO_DEBUG): - */ - /* UNEXPECTED */ - default: -/* case REG(DC_GPIO_SYNCA_A): not exist */ - ASSERT_CRITICAL(false); - return false; - } + ASSERT_CRITICAL(false); + return false; } static bool id_to_offset( @@ -191,164 +219,14 @@ static bool id_to_offset( uint32_t en, struct gpio_pin_info *info) { - bool result = true; - - switch (id) { - case GPIO_ID_DDC_DATA: - info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_DDC_CLOCK: - info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GENERIC: - info->offset = REG(DC_GPIO_GENERIC_A); - switch (en) { - case GPIO_GENERIC_A: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; - break; - case GPIO_GENERIC_B: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; - break; - case GPIO_GENERIC_C: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; - break; - case GPIO_GENERIC_D: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; - break; - case GPIO_GENERIC_E: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; - break; - case GPIO_GENERIC_F: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; - break; - case GPIO_GENERIC_G: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICG_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_HPD: - info->offset = REG(DC_GPIO_HPD_A); - switch (en) { - case GPIO_HPD_1: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; - break; - case GPIO_HPD_2: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; - break; - case GPIO_HPD_3: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; - break; - case GPIO_HPD_4: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; - break; - case GPIO_HPD_5: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; - break; - case GPIO_HPD_6: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD6_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GSL: - switch (en) { - case GPIO_GSL_GENLOCK_CLOCK: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_GENLOCK_VSYNC: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_A: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_B: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_SYNC: - case GPIO_ID_VIP_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - - if (result) { - info->offset_y = info->offset + 2; - info->offset_en = info->offset + 1; - info->offset_mask = info->offset - 1; - - info->mask_y = info->mask; - info->mask_en = info->mask; - info->mask_mask = info->mask; - } + if (dal_hw_translate_id_to_offset( + gpio_pins, + ARRAY_SIZE(gpio_pins), + id, en, info)) + return true; - return result; + ASSERT_CRITICAL(false); + return false; } /* function table */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_translate_dcn32.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_translate_dcn32.c index 8493b9981f9e..71067a8da121 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_translate_dcn32.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn32/hw_translate_dcn32.c @@ -60,111 +60,145 @@ * end *********************/ +static const struct gpio_id_offset_entry gpio_offsets[] = { + /* GENERIC */ + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_A), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_B), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_C), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_D), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_E), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_F), + /* HPD */ + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK, + GPIO_ID_HPD, GPIO_HPD_1), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK, + GPIO_ID_HPD, GPIO_HPD_2), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK, + GPIO_ID_HPD, GPIO_HPD_3), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK, + GPIO_ID_HPD, GPIO_HPD_4), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK, + GPIO_ID_HPD, GPIO_HPD_5), + /* GSL */ + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B), +}; + + +/* DDC */ +static const struct gpio_ddc_offset_entry ddc_offset_map[] = { + { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 }, + { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 }, + { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 }, + { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 }, + { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 }, + { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA }, +}; + +/* + * GSL is intentionally omitted here. + * id_to_offset() for GSL is not implemented on this ASIC. + */ +static const struct gpio_pin_entry gpio_pins[] = { + /* DDC */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + /* GENERIC */ + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK), + /* HPD */ + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK), +}; + + static bool offset_to_id( uint32_t offset, uint32_t mask, enum gpio_id *id, uint32_t *en) { - switch (offset) { - /* GENERIC */ - case REG(DC_GPIO_GENERIC_A): - *id = GPIO_ID_GENERIC; - switch (mask) { - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: - *en = GPIO_GENERIC_A; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: - *en = GPIO_GENERIC_B; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: - *en = GPIO_GENERIC_C; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: - *en = GPIO_GENERIC_D; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: - *en = GPIO_GENERIC_E; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: - *en = GPIO_GENERIC_F; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* HPD */ - case REG(DC_GPIO_HPD_A): - *id = GPIO_ID_HPD; - switch (mask) { - case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: - *en = GPIO_HPD_1; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: - *en = GPIO_HPD_2; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: - *en = GPIO_HPD_3; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: - *en = GPIO_HPD_4; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: - *en = GPIO_HPD_5; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* REG(DC_GPIO_GENLK_MASK */ - case REG(DC_GPIO_GENLK_A): - *id = GPIO_ID_GSL; - switch (mask) { - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: - *en = GPIO_GSL_GENLOCK_CLOCK; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: - *en = GPIO_GSL_GENLOCK_VSYNC; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: - *en = GPIO_GSL_SWAPLOCK_A; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: - *en = GPIO_GSL_SWAPLOCK_B; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* DDC */ - /* we don't care about the GPIO_ID for DDC - * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK - * directly in the create method */ - case REG(DC_GPIO_DDC1_A): - *en = GPIO_DDC_LINE_DDC1; - return true; - case REG(DC_GPIO_DDC2_A): - *en = GPIO_DDC_LINE_DDC2; - return true; - case REG(DC_GPIO_DDC3_A): - *en = GPIO_DDC_LINE_DDC3; - return true; - case REG(DC_GPIO_DDC4_A): - *en = GPIO_DDC_LINE_DDC4; - return true; - case REG(DC_GPIO_DDC5_A): - *en = GPIO_DDC_LINE_DDC5; + if (dal_hw_translate_gpio_ddc_offset_to_id( + ddc_offset_map, + ARRAY_SIZE(ddc_offset_map), + offset, en)) return true; - case REG(DC_GPIO_DDCVGA_A): - *en = GPIO_DDC_LINE_DDC_VGA; + + if (dal_hw_translate_gpio_offset_to_id( + gpio_offsets, + ARRAY_SIZE(gpio_offsets), + offset, mask, id, en)) return true; - default: - ASSERT_CRITICAL(false); - return false; - } + + ASSERT_CRITICAL(false); + return false; } static bool id_to_offset( @@ -172,158 +206,14 @@ static bool id_to_offset( uint32_t en, struct gpio_pin_info *info) { - bool result = true; - - switch (id) { - case GPIO_ID_DDC_DATA: - info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_DDC_CLOCK: - info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GENERIC: - info->offset = REG(DC_GPIO_GENERIC_A); - switch (en) { - case GPIO_GENERIC_A: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; - break; - case GPIO_GENERIC_B: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; - break; - case GPIO_GENERIC_C: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; - break; - case GPIO_GENERIC_D: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; - break; - case GPIO_GENERIC_E: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; - break; - case GPIO_GENERIC_F: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_HPD: - info->offset = REG(DC_GPIO_HPD_A); - switch (en) { - case GPIO_HPD_1: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; - break; - case GPIO_HPD_2: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; - break; - case GPIO_HPD_3: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; - break; - case GPIO_HPD_4: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; - break; - case GPIO_HPD_5: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GSL: - switch (en) { - case GPIO_GSL_GENLOCK_CLOCK: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_GENLOCK_VSYNC: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_A: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_B: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_SYNC: - case GPIO_ID_VIP_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - - if (result) { - info->offset_y = info->offset + 2; - info->offset_en = info->offset + 1; - info->offset_mask = info->offset - 1; - - info->mask_y = info->mask; - info->mask_en = info->mask; - info->mask_mask = info->mask; - } + if (dal_hw_translate_id_to_offset( + gpio_pins, + ARRAY_SIZE(gpio_pins), + id, en, info)) + return true; - return result; + ASSERT_CRITICAL(false); + return false; } /* function table */ diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c index ea416f01f888..7aa97f09955c 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn401/hw_translate_dcn401.c @@ -35,119 +35,145 @@ * end *********************/ +static const struct gpio_id_offset_entry gpio_offsets[] = { + /* GENERIC */ + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_A), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_B), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_C), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_D), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_E), + GPIO_MASK_ENTRY(DC_GPIO_GENERIC_A, + DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK, + GPIO_ID_GENERIC, GPIO_GENERIC_F), + /* HPD */ + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK, + GPIO_ID_HPD, GPIO_HPD_1), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK, + GPIO_ID_HPD, GPIO_HPD_2), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK, + GPIO_ID_HPD, GPIO_HPD_3), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK, + GPIO_ID_HPD, GPIO_HPD_4), + GPIO_MASK_ENTRY(DC_GPIO_HPD_A, + DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK, + GPIO_ID_HPD, GPIO_HPD_5), + /* GSL */ + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_CLOCK), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK, + GPIO_ID_GSL, GPIO_GSL_GENLOCK_VSYNC), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_A), + GPIO_MASK_ENTRY(DC_GPIO_GENLK_A, + DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK, + GPIO_ID_GSL, GPIO_GSL_SWAPLOCK_B), +}; + + +/* DDC */ +static const struct gpio_ddc_offset_entry ddc_offset_map[] = { + { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 }, + { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 }, + { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 }, + { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 }, + { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA }, +}; + + +/* + * GSL is intentionally omitted here. + * id_to_offset() for GSL is not implemented on this ASIC. + */ +static const struct gpio_pin_entry gpio_pins[] = { + /* DDC */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + /* GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + /* GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + /* GENERIC */ + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_A, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_B, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_C, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_D, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_E, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_GENERIC, GPIO_GENERIC_F, + DC_GPIO_GENERIC_A, DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK), + /* HPD */ + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_1, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_2, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_3, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_4, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_HPD, GPIO_HPD_5, + DC_GPIO_HPD_A, DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK), +}; + + static bool offset_to_id( uint32_t offset, uint32_t mask, enum gpio_id *id, uint32_t *en) { - switch (offset) { - /* GENERIC */ - case REG(DC_GPIO_GENERIC_A): - *id = GPIO_ID_GENERIC; - switch (mask) { - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK: - *en = GPIO_GENERIC_A; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK: - *en = GPIO_GENERIC_B; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK: - *en = GPIO_GENERIC_C; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK: - *en = GPIO_GENERIC_D; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK: - *en = GPIO_GENERIC_E; - return true; - case DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK: - *en = GPIO_GENERIC_F; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* HPD */ - case REG(DC_GPIO_HPD_A): - *id = GPIO_ID_HPD; - switch (mask) { - case DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK: - *en = GPIO_HPD_1; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK: - *en = GPIO_HPD_2; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK: - *en = GPIO_HPD_3; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK: - *en = GPIO_HPD_4; - return true; - case DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK: - *en = GPIO_HPD_5; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* REG(DC_GPIO_GENLK_MASK */ - case REG(DC_GPIO_GENLK_A): - *id = GPIO_ID_GSL; - switch (mask) { - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_CLK_A_MASK: - *en = GPIO_GSL_GENLOCK_CLOCK; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_GENLK_VSYNC_A_MASK: - *en = GPIO_GSL_GENLOCK_VSYNC; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_A_A_MASK: - *en = GPIO_GSL_SWAPLOCK_A; - return true; - case DC_GPIO_GENLK_A__DC_GPIO_SWAPLOCK_B_A_MASK: - *en = GPIO_GSL_SWAPLOCK_B; - return true; - default: - ASSERT_CRITICAL(false); - return false; - } - break; - /* DDC */ - /* we don't care about the GPIO_ID for DDC - * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK - * directly in the create method - */ - case REG(DC_GPIO_DDC1_A): - *en = GPIO_DDC_LINE_DDC1; + if (dal_hw_translate_gpio_ddc_offset_to_id( + ddc_offset_map, + ARRAY_SIZE(ddc_offset_map), + offset, en)) return true; - case REG(DC_GPIO_DDC2_A): - *en = GPIO_DDC_LINE_DDC2; - return true; - case REG(DC_GPIO_DDC3_A): - *en = GPIO_DDC_LINE_DDC3; - return true; - case REG(DC_GPIO_DDC4_A): - *en = GPIO_DDC_LINE_DDC4; - return true; - case REG(DC_GPIO_DDCVGA_A): - *en = GPIO_DDC_LINE_DDC_VGA; + + if (dal_hw_translate_gpio_offset_to_id( + gpio_offsets, + ARRAY_SIZE(gpio_offsets), + offset, mask, id, en)) return true; -/* - * case REG(DC_GPIO_I2CPAD_A): not exit - * case REG(DC_GPIO_PWRSEQ_A): - * case REG(DC_GPIO_PAD_STRENGTH_1): - * case REG(DC_GPIO_PAD_STRENGTH_2): - * case REG(DC_GPIO_DEBUG): - */ - /* UNEXPECTED */ - default: -/* case REG(DC_GPIO_SYNCA_A): not exist */ - ASSERT_CRITICAL(false); - return false; - } + ASSERT_CRITICAL(false); + return false; } @@ -156,158 +182,14 @@ static bool id_to_offset( uint32_t en, struct gpio_pin_info *info) { - bool result = true; - - switch (id) { - case GPIO_ID_DDC_DATA: - info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; -/* case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; */ - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_DDC_CLOCK: - info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; -/* case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; */ - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GENERIC: - info->offset = REG(DC_GPIO_GENERIC_A); - switch (en) { - case GPIO_GENERIC_A: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICA_A_MASK; - break; - case GPIO_GENERIC_B: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICB_A_MASK; - break; - case GPIO_GENERIC_C: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICC_A_MASK; - break; - case GPIO_GENERIC_D: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICD_A_MASK; - break; - case GPIO_GENERIC_E: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICE_A_MASK; - break; - case GPIO_GENERIC_F: - info->mask = DC_GPIO_GENERIC_A__DC_GPIO_GENERICF_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_HPD: - info->offset = REG(DC_GPIO_HPD_A); - switch (en) { - case GPIO_HPD_1: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD1_A_MASK; - break; - case GPIO_HPD_2: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD2_A_MASK; - break; - case GPIO_HPD_3: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD3_A_MASK; - break; - case GPIO_HPD_4: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD4_A_MASK; - break; - case GPIO_HPD_5: - info->mask = DC_GPIO_HPD_A__DC_GPIO_HPD5_A_MASK; - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_GSL: - switch (en) { - case GPIO_GSL_GENLOCK_CLOCK: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_GENLOCK_VSYNC: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_A: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - break; - case GPIO_GSL_SWAPLOCK_B: - /*not implmented*/ - ASSERT_CRITICAL(false); - result = false; - - break; - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_SYNC: - case GPIO_ID_VIP_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - - if (result) { - info->offset_y = info->offset + 2; - info->offset_en = info->offset + 1; - info->offset_mask = info->offset - 1; - - info->mask_y = info->mask; - info->mask_en = info->mask; - info->mask_mask = info->mask; - } + if (dal_hw_translate_id_to_offset( + gpio_pins, + ARRAY_SIZE(gpio_pins), + id, en, info)) + return true; - return result; + ASSERT_CRITICAL(false); + return false; } diff --git a/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c b/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c index e7e1d9979876..7b2c4cd42450 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/dcn42/hw_translate_dcn42.c @@ -39,62 +39,76 @@ * end *********************/ +static const struct gpio_id_offset_entry gpio_offsets[] = { + /* HPD */ + GPIO_ENTRY(HPD0_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_1), + GPIO_ENTRY(HPD1_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_2), + GPIO_ENTRY(HPD2_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_3), + GPIO_ENTRY(HPD3_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_4), + GPIO_ENTRY(HPD4_DC_HPD_INT_STATUS, GPIO_ID_HPD, GPIO_HPD_5), +}; + + +/* DDC */ +static const struct gpio_ddc_offset_entry ddc_offset_map[] = { + { REG(DC_GPIO_DDC1_A), GPIO_DDC_LINE_DDC1 }, + { REG(DC_GPIO_DDC2_A), GPIO_DDC_LINE_DDC2 }, + { REG(DC_GPIO_DDC3_A), GPIO_DDC_LINE_DDC3 }, + { REG(DC_GPIO_DDC4_A), GPIO_DDC_LINE_DDC4 }, + { REG(DC_GPIO_DDC5_A), GPIO_DDC_LINE_DDC5 }, + { REG(DC_GPIO_DDCVGA_A), GPIO_DDC_LINE_DDC_VGA }, +}; + + +static const struct gpio_pin_entry gpio_pins[] = { + /* DDC */ + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_DATA, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC1, + DC_GPIO_DDC1_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC2, + DC_GPIO_DDC2_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC3, + DC_GPIO_DDC3_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC4, + DC_GPIO_DDC4_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC5, + DC_GPIO_DDC5_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), + GPIO_PIN_ENTRY(GPIO_ID_DDC_CLOCK, GPIO_DDC_LINE_DDC_VGA, + DC_GPIO_DDCVGA_A, DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK), +}; + + static bool offset_to_id( uint32_t offset, uint32_t mask, enum gpio_id *id, uint32_t *en) { - (void)mask; - switch (offset) { - /* HPD */ - case REG(HPD0_DC_HPD_INT_STATUS): - *id = GPIO_ID_HPD; - *en = GPIO_HPD_1; - return true; - case REG(HPD1_DC_HPD_INT_STATUS): - *id = GPIO_ID_HPD; - *en = GPIO_HPD_2; + if (dal_hw_translate_gpio_ddc_offset_to_id( + ddc_offset_map, + ARRAY_SIZE(ddc_offset_map), + offset, en)) return true; - case REG(HPD2_DC_HPD_INT_STATUS): - *id = GPIO_ID_HPD; - *en = GPIO_HPD_3; - return true; - case REG(HPD3_DC_HPD_INT_STATUS): - *id = GPIO_ID_HPD; - *en = GPIO_HPD_4; - return true; - case REG(HPD4_DC_HPD_INT_STATUS): - *id = GPIO_ID_HPD; - *en = GPIO_HPD_5; - return true; - /* DDC */ - /* we don't care about the GPIO_ID for DDC - * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK - * directly in the create method - */ - case REG(DC_GPIO_DDC1_A): - *en = GPIO_DDC_LINE_DDC1; - return true; - case REG(DC_GPIO_DDC2_A): - *en = GPIO_DDC_LINE_DDC2; - return true; - case REG(DC_GPIO_DDC3_A): - *en = GPIO_DDC_LINE_DDC3; - return true; - case REG(DC_GPIO_DDC4_A): - *en = GPIO_DDC_LINE_DDC4; - return true; - case REG(DC_GPIO_DDC5_A): - *en = GPIO_DDC_LINE_DDC5; - return true; - case REG(DC_GPIO_DDCVGA_A): - *en = GPIO_DDC_LINE_DDC_VGA; + + if (dal_hw_translate_gpio_offset_to_id( + gpio_offsets, + ARRAY_SIZE(gpio_offsets), + offset, mask, id, en)) return true; - default: - ASSERT_CRITICAL(false); - return false; - } + + ASSERT_CRITICAL(false); + return false; } @@ -103,81 +117,14 @@ static bool id_to_offset( uint32_t en, struct gpio_pin_info *info) { - bool result = true; - - switch (id) { - case GPIO_ID_DDC_DATA: - info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1DATA_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_DDC_CLOCK: - info->mask = DC_GPIO_DDC1_A__DC_GPIO_DDC1CLK_A_MASK; - switch (en) { - case GPIO_DDC_LINE_DDC1: - info->offset = REG(DC_GPIO_DDC1_A); - break; - case GPIO_DDC_LINE_DDC2: - info->offset = REG(DC_GPIO_DDC2_A); - break; - case GPIO_DDC_LINE_DDC3: - info->offset = REG(DC_GPIO_DDC3_A); - break; - case GPIO_DDC_LINE_DDC4: - info->offset = REG(DC_GPIO_DDC4_A); - break; - case GPIO_DDC_LINE_DDC5: - info->offset = REG(DC_GPIO_DDC5_A); - break; - case GPIO_DDC_LINE_DDC_VGA: - info->offset = REG(DC_GPIO_DDCVGA_A); - break; - case GPIO_DDC_LINE_I2C_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - break; - case GPIO_ID_SYNC: - case GPIO_ID_VIP_PAD: - default: - ASSERT_CRITICAL(false); - result = false; - } - - if (result) { - info->offset_y = info->offset + 2; - info->offset_en = info->offset + 1; - info->offset_mask = info->offset - 1; - - info->mask_y = info->mask; - info->mask_en = info->mask; - info->mask_mask = info->mask; - } - - return result; + if (dal_hw_translate_id_to_offset( + gpio_pins, + ARRAY_SIZE(gpio_pins), + id, en, info)) + return true; + + ASSERT_CRITICAL(false); + return false; } diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c index 64a5e11fce5c..b58af86dee10 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.c @@ -133,3 +133,89 @@ bool dal_hw_translate_init( return false; } } + +bool dal_hw_translate_gpio_offset_to_id( + const struct gpio_id_offset_entry *table, + uint32_t table_size, + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en) +{ + uint32_t i; + + for (i = 0; i < table_size; i++) { + const struct gpio_id_offset_entry *entry = &table[i]; + + if (entry->offset != offset) + continue; + + if (entry->check_mask && entry->mask != mask) + continue; + + *id = entry->id; + *en = entry->en; + + return true; + } + + return false; +} + +/* we don't care about the GPIO_ID for DDC + * in DdcHandle it will use GPIO_ID_DDC_DATA/GPIO_ID_DDC_CLOCK + * directly in the create method + */ +bool dal_hw_translate_gpio_ddc_offset_to_id( + const struct gpio_ddc_offset_entry *table, + uint32_t table_size, + uint32_t offset, + uint32_t *en) +{ + uint32_t i; + + for (i = 0; i < table_size; i++) { + const struct gpio_ddc_offset_entry *entry = &table[i]; + + if (entry->offset != offset) + continue; + + *en = entry->en; + + return true; + } + + return false; +} + +bool dal_hw_translate_id_to_offset( + const struct gpio_pin_entry *table, + uint32_t table_size, + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info) +{ + uint32_t i; + + for (i = 0; i < table_size; i++) { + const struct gpio_pin_entry *entry = &table[i]; + + if (entry->id != id || entry->en != en) + continue; + + info->offset = entry->offset; + info->mask = entry->mask; + + info->offset_y = info->offset + 2; + info->offset_en = info->offset + 1; + info->offset_mask = info->offset - 1; + + info->mask_y = info->mask; + info->mask_en = info->mask; + info->mask_mask = info->mask; + + return true; + } + + return false; +} diff --git a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h index 3a7d89ca1605..339e381f8fde 100644 --- a/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h +++ b/drivers/gpu/drm/amd/display/dc/gpio/hw_translate.h @@ -47,4 +47,25 @@ bool dal_hw_translate_init( enum dce_version dce_version, enum dce_environment dce_environment); +bool dal_hw_translate_gpio_offset_to_id( + const struct gpio_id_offset_entry *table, + uint32_t table_size, + uint32_t offset, + uint32_t mask, + enum gpio_id *id, + uint32_t *en); + +bool dal_hw_translate_gpio_ddc_offset_to_id( + const struct gpio_ddc_offset_entry *table, + uint32_t table_size, + uint32_t offset, + uint32_t *en); + +bool dal_hw_translate_id_to_offset( + const struct gpio_pin_entry *table, + uint32_t table_size, + enum gpio_id id, + uint32_t en, + struct gpio_pin_info *info); + #endif diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c index 79cb506be5cb..cbcd22789013 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn31/dcn31_hubbub.c @@ -138,10 +138,10 @@ static void dcn31_program_compbuf_size(struct hubbub *hubbub, unsigned int compb if (safe_to_increase || compbuf_size_segments <= hubbub2->compbuf_size_segments) { if (compbuf_size_segments > hubbub2->compbuf_size_segments) { - REG_WAIT(DCHUBBUB_DET0_CTRL, DET0_SIZE_CURRENT, hubbub2->det0_size, 1, 100); - REG_WAIT(DCHUBBUB_DET1_CTRL, DET1_SIZE_CURRENT, hubbub2->det1_size, 1, 100); - REG_WAIT(DCHUBBUB_DET2_CTRL, DET2_SIZE_CURRENT, hubbub2->det2_size, 1, 100); - REG_WAIT(DCHUBBUB_DET3_CTRL, DET3_SIZE_CURRENT, hubbub2->det3_size, 1, 100); + dcn31_wait_for_det_apply(hubbub, 0); + dcn31_wait_for_det_apply(hubbub, 1); + dcn31_wait_for_det_apply(hubbub, 2); + dcn31_wait_for_det_apply(hubbub, 3); } /* Should never be hit, if it is we have an erroneous hw config*/ ASSERT(hubbub2->det0_size + hubbub2->det1_size + hubbub2->det2_size diff --git a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c index 82d4e3e0e5e8..5e5a7a74346d 100644 --- a/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/hubbub/dcn35/dcn35_hubbub.c @@ -571,7 +571,7 @@ void dcn35_dchvm_init(struct hubbub *hubbub) if (riommu_active) { // Disable gating and memory power requests - REG_UPDATE(DCHVM_MEM_CTRL, HVM_GPUVMRET_PWR_REQ_DIS, 1); + REG_UPDATE_2(DCHVM_MEM_CTRL, HVM_GPUVMRET_PWR_REQ_DIS, 1, HVM_GPUVMRET_FORCE_REQ, 0); REG_UPDATE_4(DCHVM_CLK_CTRL, HVM_DISPCLK_R_GATE_DIS, 1, HVM_DISPCLK_G_GATE_DIS, 1, diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c index 302515128358..9965cf572354 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn401/dcn401_hubp.c @@ -136,7 +136,7 @@ void hubp401_program_3dlut_fl_config( uint32_t mpc_width = {(cfg->width == 17) ? 0 : 1}; uint32_t width = {cfg->width}; - if (cfg->layout == DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR) + if (cfg->layout == CM_LUT_1D_PACKED_LINEAR) width = (cfg->width == 17) ? 4916 : 35940; REG_UPDATE_2(_3DLUT_FL_CONFIG, diff --git a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c index e4602c3ddc66..57de98444f6c 100644 --- a/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/hubp/dcn42/dcn42_hubp.c @@ -20,6 +20,12 @@ static void hubp42_set_fgcg(struct hubp *hubp, bool enable) { struct dcn20_hubp *hubp2 = TO_DCN20_HUBP(hubp); + /* Temporary workaround for IOMMU mismatch issue. + * Fine grain control via bit1 of debug flag. + */ + if (hubp->ctx->dc->debug.iommu_mismatch_temp_wka & 0x2) + enable = false; + REG_UPDATE(HUBP_CLK_CNTL, HUBP_FGCG_REP_DIS, !enable); } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index 042602c50e35..c9691974bf72 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -3166,12 +3166,12 @@ static void dce110_program_front_end_for_pipe( plane_state->rotation); /* Moved programming gamma from dc to hwss */ - if (pipe_ctx->plane_state->update_flags.bits.full_update || - pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || - pipe_ctx->plane_state->update_flags.bits.gamma_change) + if (pipe_ctx->plane_state->update_bits.full_update || + pipe_ctx->plane_state->update_bits.in_transfer_func_change || + pipe_ctx->plane_state->update_bits.gamma_change) hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); - if (pipe_ctx->plane_state->update_flags.bits.full_update) + if (pipe_ctx->plane_state->update_bits.full_update) hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); DC_LOG_SURFACE( diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c index a08e9f9eec17..26aa303b8237 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce60/dce60_hwseq.c @@ -332,12 +332,12 @@ dce60_program_front_end_for_pipe( plane_state->rotation); /* Moved programming gamma from dc to hwss */ - if (pipe_ctx->plane_state->update_flags.bits.full_update || - pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || - pipe_ctx->plane_state->update_flags.bits.gamma_change) + if (pipe_ctx->plane_state->update_bits.full_update || + pipe_ctx->plane_state->update_bits.in_transfer_func_change || + pipe_ctx->plane_state->update_bits.gamma_change) hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); - if (pipe_ctx->plane_state->update_flags.bits.full_update) + if (pipe_ctx->plane_state->update_bits.full_update) hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); DC_LOG_SURFACE( diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c index 7112b71af977..541cd908b341 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn10/dcn10_hwseq.c @@ -2981,7 +2981,7 @@ void dcn10_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) mpcc_id = hubp->inst; /* If there is no full update, don't need to touch MPC tree*/ - if (!pipe_ctx->plane_state->update_flags.bits.full_update) { + if (!pipe_ctx->plane_state->update_bits.full_update) { mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id); dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id); return; @@ -3041,7 +3041,7 @@ static void dcn10_update_dchubp_dpp( /* If request max dpp clk is lower than current dispclk, no need to * divided by 2 */ - if (plane_state->update_flags.bits.full_update) { + if (plane_state->update_bits.full_update) { /* new calculated dispclk, dppclk are stored in * context->bw_ctx.bw.dcn.clk.dispclk_khz / dppclk_khz. current @@ -3096,7 +3096,7 @@ static void dcn10_update_dchubp_dpp( * VTG is within DCHUBBUB which is commond block share by each pipe HUBP. * VTG is 1:1 mapping with OTG. Each pipe HUBP will select which VTG */ - if (plane_state->update_flags.bits.full_update) { + if (plane_state->update_bits.full_update) { hubp->funcs->hubp_vtg_sel(hubp, pipe_ctx->stream_res.tg->inst); hubp->funcs->hubp_setup( @@ -3113,26 +3113,26 @@ static void dcn10_update_dchubp_dpp( size.surface_size = pipe_ctx->plane_res.scl_data.viewport; - if (plane_state->update_flags.bits.full_update || - plane_state->update_flags.bits.bpp_change) + if (plane_state->update_bits.full_update || + plane_state->update_bits.bpp_change) dcn10_update_dpp(dpp, plane_state); - if (plane_state->update_flags.bits.full_update || - plane_state->update_flags.bits.per_pixel_alpha_change || - plane_state->update_flags.bits.global_alpha_change) + if (plane_state->update_bits.full_update || + plane_state->update_bits.per_pixel_alpha_change || + plane_state->update_bits.global_alpha_change) hws->funcs.update_mpcc(dc, pipe_ctx); - if (plane_state->update_flags.bits.full_update || - plane_state->update_flags.bits.per_pixel_alpha_change || - plane_state->update_flags.bits.global_alpha_change || - plane_state->update_flags.bits.scaling_change || - plane_state->update_flags.bits.position_change) { + if (plane_state->update_bits.full_update || + plane_state->update_bits.per_pixel_alpha_change || + plane_state->update_bits.global_alpha_change || + plane_state->update_bits.scaling_change || + plane_state->update_bits.position_change) { update_scaler(pipe_ctx); } - if (plane_state->update_flags.bits.full_update || - plane_state->update_flags.bits.scaling_change || - plane_state->update_flags.bits.position_change) { + if (plane_state->update_bits.full_update || + plane_state->update_bits.scaling_change || + plane_state->update_bits.position_change) { hubp->funcs->mem_program_viewport( hubp, &pipe_ctx->plane_res.scl_data.viewport, @@ -3150,7 +3150,7 @@ static void dcn10_update_dchubp_dpp( dc->hwss.set_cursor_sdr_white_level(pipe_ctx); } - if (plane_state->update_flags.bits.full_update) { + if (plane_state->update_bits.full_update) { /*gamut remap*/ dc->hwss.program_gamut_remap(pipe_ctx); @@ -3161,15 +3161,15 @@ static void dcn10_update_dchubp_dpp( pipe_ctx->stream_res.opp->inst); } - if (plane_state->update_flags.bits.full_update || - plane_state->update_flags.bits.pixel_format_change || - plane_state->update_flags.bits.horizontal_mirror_change || - plane_state->update_flags.bits.rotation_change || - plane_state->update_flags.bits.swizzle_change || - plane_state->update_flags.bits.dcc_change || - plane_state->update_flags.bits.bpp_change || - plane_state->update_flags.bits.scaling_change || - plane_state->update_flags.bits.plane_size_change) { + if (plane_state->update_bits.full_update || + plane_state->update_bits.pixel_format_change || + plane_state->update_bits.horizontal_mirror_change || + plane_state->update_bits.rotation_change || + plane_state->update_bits.swizzle_change || + plane_state->update_bits.dcc_change || + plane_state->update_bits.bpp_change || + plane_state->update_bits.scaling_change || + plane_state->update_bits.plane_size_change) { hubp->funcs->hubp_program_surface_config( hubp, plane_state->format, @@ -3278,16 +3278,16 @@ void dcn10_program_pipe( hws->funcs.blank_pixel_data(dc, pipe_ctx, blank); } - if (pipe_ctx->plane_state->update_flags.bits.full_update) + if (pipe_ctx->plane_state->update_bits.full_update) dcn10_enable_plane(dc, pipe_ctx, context); dcn10_update_dchubp_dpp(dc, pipe_ctx, context); hws->funcs.set_hdr_multiplier(pipe_ctx); - if (pipe_ctx->plane_state->update_flags.bits.full_update || - pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || - pipe_ctx->plane_state->update_flags.bits.gamma_change) + if (pipe_ctx->plane_state->update_bits.full_update || + pipe_ctx->plane_state->update_bits.in_transfer_func_change || + pipe_ctx->plane_state->update_bits.gamma_change) hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); /* dcn10_translate_regamma_to_hw_format takes 750us to finish @@ -3296,7 +3296,7 @@ void dcn10_program_pipe( * Always call this for now since it does memcmp inside before * doing heavy calculation and programming */ - if (pipe_ctx->plane_state->update_flags.bits.full_update) + if (pipe_ctx->plane_state->update_bits.full_update) hws->funcs.set_output_transfer_func(dc, pipe_ctx, pipe_ctx->stream); } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c index e6a8206f8ce0..95e5b6a6ba0f 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn20/dcn20_hwseq.c @@ -1066,11 +1066,11 @@ bool dcn20_set_blend_lut( bool result = true; const struct pwl_params *blend_lut = NULL; - if (plane_state->blend_tf.type == TF_TYPE_HWPWL) - blend_lut = &plane_state->blend_tf.pwl; - else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL) + blend_lut = &plane_state->cm.blend_func.pwl; + else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) { cm_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->blend_tf, + &plane_state->cm.blend_func, &dpp_base->regamma_params, false); blend_lut = &dpp_base->regamma_params; } @@ -1086,19 +1086,19 @@ bool dcn20_set_shaper_3dlut( bool result = true; const struct pwl_params *shaper_lut = NULL; - if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL) - shaper_lut = &plane_state->in_shaper_func.pwl; - else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (plane_state->cm.shaper_func.type == TF_TYPE_HWPWL) + shaper_lut = &plane_state->cm.shaper_func.pwl; + else if (plane_state->cm.shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { cm_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->in_shaper_func, + &plane_state->cm.shaper_func, &dpp_base->shaper_params, true); shaper_lut = &dpp_base->shaper_params; } result = dpp_base->funcs->dpp_program_shaper_lut(dpp_base, shaper_lut); - if (plane_state->lut3d_func.state.bits.initialized == 1) + if (plane_state->cm.lut3d_func.state.bits.initialized == 1) result = dpp_base->funcs->dpp_program_3dlut(dpp_base, - &plane_state->lut3d_func.lut_3d); + &plane_state->cm.lut3d_func.lut_3d); else result = dpp_base->funcs->dpp_program_3dlut(dpp_base, NULL); @@ -1733,10 +1733,10 @@ void dcn20_update_dchubp_dpp( if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.plane_changed || - plane_state->update_flags.bits.bpp_change || - plane_state->update_flags.bits.input_csc_change || - plane_state->update_flags.bits.color_space_change || - plane_state->update_flags.bits.coeff_reduction_change) { + plane_state->update_bits.bpp_change || + plane_state->update_bits.input_csc_change || + plane_state->update_bits.color_space_change || + plane_state->update_bits.coeff_reduction_change) { struct dc_bias_and_scale bns_params = plane_state->bias_and_scale; // program the input csc @@ -1760,16 +1760,16 @@ void dcn20_update_dchubp_dpp( if (pipe_ctx->update_flags.bits.mpcc || pipe_ctx->update_flags.bits.plane_changed - || plane_state->update_flags.bits.global_alpha_change - || plane_state->update_flags.bits.per_pixel_alpha_change) { + || plane_state->update_bits.global_alpha_change + || plane_state->update_bits.per_pixel_alpha_change) { // MPCC inst is equal to pipe index in practice hws->funcs.update_mpcc(dc, pipe_ctx); } if (pipe_ctx->update_flags.bits.scaler || - plane_state->update_flags.bits.scaling_change || - plane_state->update_flags.bits.position_change || - plane_state->update_flags.bits.per_pixel_alpha_change || + plane_state->update_bits.scaling_change || + plane_state->update_bits.position_change || + plane_state->update_bits.per_pixel_alpha_change || pipe_ctx->stream->update_flags.bits.scaling) { pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->plane_state->per_pixel_alpha; ASSERT(pipe_ctx->plane_res.scl_data.lb_params.depth == LB_PIXEL_DEPTH_36BPP); @@ -1779,8 +1779,8 @@ void dcn20_update_dchubp_dpp( } if (pipe_ctx->update_flags.bits.viewport || - (context == dc->current_state && plane_state->update_flags.bits.position_change) || - (context == dc->current_state && plane_state->update_flags.bits.scaling_change) || + (context == dc->current_state && plane_state->update_bits.position_change) || + (context == dc->current_state && plane_state->update_bits.scaling_change) || (context == dc->current_state && pipe_ctx->stream->update_flags.bits.scaling)) { hubp->funcs->mem_program_viewport( @@ -1812,7 +1812,7 @@ void dcn20_update_dchubp_dpp( if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed || pipe_ctx->update_flags.bits.plane_changed || pipe_ctx->stream->update_flags.bits.gamut_remap - || plane_state->update_flags.bits.gamut_remap_change + || plane_state->update_bits.gamut_remap_change || pipe_ctx->stream->update_flags.bits.out_csc) { /* dpp/cm gamut remap*/ dc->hwss.program_gamut_remap(pipe_ctx); @@ -1828,14 +1828,14 @@ void dcn20_update_dchubp_dpp( if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.plane_changed || pipe_ctx->update_flags.bits.opp_changed || - plane_state->update_flags.bits.pixel_format_change || - plane_state->update_flags.bits.horizontal_mirror_change || - plane_state->update_flags.bits.rotation_change || - plane_state->update_flags.bits.swizzle_change || - plane_state->update_flags.bits.dcc_change || - plane_state->update_flags.bits.bpp_change || - plane_state->update_flags.bits.scaling_change || - plane_state->update_flags.bits.plane_size_change) { + plane_state->update_bits.pixel_format_change || + plane_state->update_bits.horizontal_mirror_change || + plane_state->update_bits.rotation_change || + plane_state->update_bits.swizzle_change || + plane_state->update_bits.dcc_change || + plane_state->update_bits.bpp_change || + plane_state->update_bits.scaling_change || + plane_state->update_bits.plane_size_change) { struct plane_size size = plane_state->plane_size; size.surface_size = pipe_ctx->plane_res.scl_data.viewport; @@ -1853,7 +1853,7 @@ void dcn20_update_dchubp_dpp( if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.plane_changed || - plane_state->update_flags.bits.addr_update) { + plane_state->update_bits.addr_update) { if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) && pipe_mall_type == SUBVP_MAIN) { union block_sequence_params params; @@ -1969,18 +1969,18 @@ static void dcn20_program_pipe( } if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw || - pipe_ctx->plane_state->update_flags.raw || + dc_pipe_update_bits_is_any_set(&pipe_ctx->plane_state->update_bits) || pipe_ctx->stream->update_flags.raw)) dcn20_update_dchubp_dpp(dc, pipe_ctx, context); if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable || - pipe_ctx->plane_state->update_flags.bits.hdr_mult)) + pipe_ctx->plane_state->update_bits.hdr_mult)) hws->funcs.set_hdr_multiplier(pipe_ctx); if (pipe_ctx->plane_state && - (pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || - pipe_ctx->plane_state->update_flags.bits.gamma_change || - pipe_ctx->plane_state->update_flags.bits.lut_3d || + (pipe_ctx->plane_state->update_bits.in_transfer_func_change || + pipe_ctx->plane_state->update_bits.gamma_change || + pipe_ctx->plane_state->update_bits.lut_3d || pipe_ctx->update_flags.bits.enable)) hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); @@ -2186,7 +2186,7 @@ void dcn20_program_front_end_for_ctx( pipe = &context->res_ctx.pipe_ctx[i]; if (!pipe->top_pipe && !pipe->prev_odm_pipe && pipe->stream && pipe->stream->num_wb_info > 0 - && (pipe->update_flags.raw || (pipe->plane_state && pipe->plane_state->update_flags.raw) + && (pipe->update_flags.raw || (pipe->plane_state && dc_pipe_update_bits_is_any_set(&pipe->plane_state->update_bits)) || pipe->stream->update_flags.raw) && hws->funcs.program_all_writeback_pipes_in_tree) hws->funcs.program_all_writeback_pipes_in_tree(dc, pipe->stream, context); @@ -2998,7 +2998,7 @@ void dcn20_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) mpcc_id = hubp->inst; /* If there is no full update, don't need to touch MPC tree*/ - if (!pipe_ctx->plane_state->update_flags.bits.full_update && + if (!pipe_ctx->plane_state->update_bits.full_update && !pipe_ctx->update_flags.bits.mpcc) { mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id); dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c index ce18d75fd991..7b820bdae55b 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn201/dcn201_hwseq.c @@ -485,7 +485,7 @@ void dcn201_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) mpcc_id = dpp_id; /* If there is no full update, don't need to touch MPC tree*/ - if (!pipe_ctx->plane_state->update_flags.bits.full_update) { + if (!pipe_ctx->plane_state->update_bits.full_update) { dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id); mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id); return; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c index a7c85a2302ab..aed9d06ec538 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c @@ -239,11 +239,13 @@ bool dcn30_set_blend_lut( bool result = true; const struct pwl_params *blend_lut = NULL; - if (plane_state->blend_tf.type == TF_TYPE_HWPWL) - blend_lut = &plane_state->blend_tf.pwl; - else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL) + blend_lut = &plane_state->cm.blend_func.pwl; + else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) { result = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->blend_tf, &dpp_base->regamma_params, false); + &plane_state->cm.blend_func, + &dpp_base->regamma_params, + false); if (!result) return result; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c index a3242e7521a4..c2ea25927765 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn32/dcn32_hwseq.c @@ -490,12 +490,12 @@ bool dcn32_set_mcm_luts( const struct pwl_params *lut_params = NULL; // 1D LUT - if (plane_state->blend_tf.type == TF_TYPE_HWPWL) - lut_params = &plane_state->blend_tf.pwl; - else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { - result = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->blend_tf, - &dpp_base->regamma_params, false); + if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL) + lut_params = &plane_state->cm.blend_func.pwl; + else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + result = cm3_helper_translate_curve_to_degamma_hw_format( + &plane_state->cm.blend_func, + &dpp_base->regamma_params); if (!result) return result; @@ -505,21 +505,22 @@ bool dcn32_set_mcm_luts( lut_params = NULL; // Shaper - if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL) - lut_params = &plane_state->in_shaper_func.pwl; - else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (plane_state->cm.shaper_func.type == TF_TYPE_HWPWL) + lut_params = &plane_state->cm.shaper_func.pwl; + else if (plane_state->cm.shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { // TODO: dpp_base replace rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->in_shaper_func, - &dpp_base->shaper_params, true); + &plane_state->cm.shaper_func, + &dpp_base->shaper_params, + true); lut_params = rval ? &dpp_base->shaper_params : NULL; } mpc->funcs->program_shaper(mpc, lut_params, mpcc_id); // 3D - if (plane_state->lut3d_func.state.bits.initialized == 1) - result = mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id); + if (plane_state->cm.lut3d_func.state.bits.initialized == 1) + result = mpc->funcs->program_3dlut(mpc, &plane_state->cm.lut3d_func.lut_3d, mpcc_id); else result = mpc->funcs->program_3dlut(mpc, NULL, mpcc_id); @@ -551,9 +552,8 @@ bool dcn32_set_input_transfer_func(struct dc *dc, if (plane_state->in_transfer_func.type == TF_TYPE_HWPWL) params = &plane_state->in_transfer_func.pwl; else if (plane_state->in_transfer_func.type == TF_TYPE_DISTRIBUTED_POINTS && - cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->in_transfer_func, - &dpp_base->degamma_params, false)) + cm3_helper_translate_curve_to_degamma_hw_format(&plane_state->in_transfer_func, + &dpp_base->degamma_params)) params = &dpp_base->degamma_params; dpp_base->funcs->dpp_program_gamcor_lut(dpp_base, params); @@ -1463,7 +1463,7 @@ void dcn32_update_phantom_vp_position(struct dc *dc, if (pipe->stream && dc_state_get_pipe_subvp_type(context, pipe) == SUBVP_MAIN && dc_state_get_paired_subvp_stream(context, pipe->stream) == phantom_pipe->stream) { - if (pipe->plane_state && pipe->plane_state->update_flags.bits.position_change) { + if (pipe->plane_state && pipe->plane_state->update_bits.position_change) { phantom_plane->src_rect.x = pipe->plane_state->src_rect.x; phantom_plane->src_rect.y = pipe->plane_state->src_rect.y; @@ -1471,7 +1471,7 @@ void dcn32_update_phantom_vp_position(struct dc *dc, phantom_plane->dst_rect.x = pipe->plane_state->dst_rect.x; phantom_plane->dst_rect.y = pipe->plane_state->dst_rect.y; - phantom_pipe->plane_state->update_flags.bits.position_change = 1; + phantom_pipe->plane_state->update_bits.position_change = 1; resource_build_scaling_params(phantom_pipe); return; } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c index 8f9038fec0f7..01027d120cb0 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn35/dcn35_hwseq.c @@ -581,9 +581,6 @@ void dcn35_power_down_on_boot(struct dc *dc) bool dcn35_apply_idle_power_optimizations(struct dc *dc, bool enable) { - if (dc->debug.dmcub_emulation) - return true; - if (enable) { uint32_t num_active_edp = 0; int i; diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c index 96815a92a629..0336d118e77e 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.c @@ -321,7 +321,7 @@ void dcn401_init_hw(struct dc *dc) user_level = link->panel_cntl->stored_backlight_registers.USER_LEVEL; } - if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) { + if (link->force_to_use_aux) { struct graphics_object_i2c_info i2c_info; struct ddc *ddc_pin; struct gpio_ddc_hw_info hw_info; @@ -410,37 +410,27 @@ static void dcn401_get_mcm_lut_xable_from_pipe_ctx(struct dc *dc, struct pipe_ct enum MCM_LUT_XABLE *lut3d_xable, enum MCM_LUT_XABLE *lut1d_xable) { - enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL; - bool lut1d_enable = false; struct mpc *mpc = dc->res_pool->mpc; int mpcc_id = pipe_ctx->plane_res.hubp->inst; if (!pipe_ctx->plane_state) return; - shaper_3dlut_setting = pipe_ctx->plane_state->mcm_shaper_3dlut_setting; - lut1d_enable = pipe_ctx->plane_state->mcm_lut1d_enable; + mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE; - *lut1d_xable = lut1d_enable ? MCM_LUT_ENABLE : MCM_LUT_DISABLE; - - switch (shaper_3dlut_setting) { - case DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL: - *lut3d_xable = *shaper_xable = MCM_LUT_DISABLE; - break; - case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER: - *lut3d_xable = MCM_LUT_DISABLE; - *shaper_xable = MCM_LUT_ENABLE; - break; - case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT: - *lut3d_xable = *shaper_xable = MCM_LUT_ENABLE; - break; - } + *lut1d_xable = pipe_ctx->plane_state->cm.flags.bits.blend_enable ? + MCM_LUT_ENABLE : MCM_LUT_DISABLE; + *shaper_xable = pipe_ctx->plane_state->cm.flags.bits.shaper_enable ? + MCM_LUT_ENABLE : MCM_LUT_DISABLE; + *lut3d_xable = (pipe_ctx->plane_state->cm.flags.bits.shaper_enable && + pipe_ctx->plane_state->cm.flags.bits.lut3d_enable) ? + MCM_LUT_ENABLE : MCM_LUT_DISABLE; } void dcn401_populate_mcm_luts(struct dc *dc, struct pipe_ctx *pipe_ctx, - struct dc_cm2_func_luts mcm_luts, + const struct dc_plane_cm *cm, bool lut_bank_a) { struct dpp *dpp_base = pipe_ctx->plane_res.dpp; @@ -448,14 +438,17 @@ void dcn401_populate_mcm_luts(struct dc *dc, int mpcc_id = hubp->inst; struct mpc *mpc = dc->res_pool->mpc; union mcm_lut_params m_lut_params; - enum dc_cm2_transfer_func_source lut3d_src = mcm_luts.lut3d_data.lut3d_src; + const bool lut3d_dma = !!cm->flags.bits.lut3d_dma_enable; enum hubp_3dlut_fl_format format = 0; enum hubp_3dlut_fl_mode mode; - enum hubp_3dlut_fl_width width = 0; + /* Width was previously hard-coded to TRANSFORMED via local_mcm build, + * preserve identical behavior. + */ + enum hubp_3dlut_fl_width width = hubp_3dlut_fl_width_transformed; enum hubp_3dlut_fl_addressing_mode addr_mode; - enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g = 0; - enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b = 0; - enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r = 0; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r; enum MCM_LUT_XABLE shaper_xable = MCM_LUT_DISABLE; enum MCM_LUT_XABLE lut3d_xable = MCM_LUT_DISABLE; enum MCM_LUT_XABLE lut1d_xable = MCM_LUT_DISABLE; @@ -464,13 +457,13 @@ void dcn401_populate_mcm_luts(struct dc *dc, dcn401_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable); /* 1D LUT */ - if (mcm_luts.lut1d_func) { + { memset(&m_lut_params, 0, sizeof(m_lut_params)); - if (mcm_luts.lut1d_func->type == TF_TYPE_HWPWL) - m_lut_params.pwl = &mcm_luts.lut1d_func->pwl; - else if (mcm_luts.lut1d_func->type == TF_TYPE_DISTRIBUTED_POINTS) { + if (cm->blend_func.type == TF_TYPE_HWPWL) + m_lut_params.pwl = &cm->blend_func.pwl; + else if (cm->blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) { rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, - mcm_luts.lut1d_func, + &cm->blend_func, &dpp_base->regamma_params, false); m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; } @@ -483,14 +476,14 @@ void dcn401_populate_mcm_luts(struct dc *dc, } /* Shaper */ - if (mcm_luts.shaper && mcm_luts.lut3d_data.mpc_3dlut_enable) { + if (cm->flags.bits.lut3d_enable) { memset(&m_lut_params, 0, sizeof(m_lut_params)); - if (mcm_luts.shaper->type == TF_TYPE_HWPWL) - m_lut_params.pwl = &mcm_luts.shaper->pwl; - else if (mcm_luts.shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { + if (cm->shaper_func.type == TF_TYPE_HWPWL) + m_lut_params.pwl = &cm->shaper_func.pwl; + else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { ASSERT(false); rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, - mcm_luts.shaper, + &cm->shaper_func, &dpp_base->regamma_params, true); m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; } @@ -503,42 +496,43 @@ void dcn401_populate_mcm_luts(struct dc *dc, } /* 3DLUT */ - switch (lut3d_src) { - case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM: + if (!lut3d_dma) { + /* SYSMEM (legacy lut3d_func) */ memset(&m_lut_params, 0, sizeof(m_lut_params)); if (hubp->funcs->hubp_enable_3dlut_fl) hubp->funcs->hubp_enable_3dlut_fl(hubp, false); - if (mcm_luts.lut3d_data.lut3d_func && mcm_luts.lut3d_data.lut3d_func->state.bits.initialized) { - m_lut_params.lut3d = &mcm_luts.lut3d_data.lut3d_func->lut_3d; + if (cm->lut3d_func.state.bits.initialized) { + m_lut_params.lut3d = &cm->lut3d_func.lut_3d; if (mpc->funcs->populate_lut) mpc->funcs->populate_lut(mpc, MCM_LUT_3DLUT, m_lut_params, lut_bank_a, mpcc_id); if (mpc->funcs->program_lut_mode) mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id); } - break; - case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM: - switch (mcm_luts.lut3d_data.gpu_mem_params.size) { - case DC_CM2_GPU_MEM_SIZE_333333: + } else { + /* VIDMEM (3DLUT DMA Fast Load) */ + + /* Select width based on the requested LUT size */ + switch (cm->lut3d_dma.size) { +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + case CM_LUT_SIZE_333333: if (dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_33) width = hubp_3dlut_fl_width_33; break; - case DC_CM2_GPU_MEM_SIZE_171717: +#endif // CONFIG_DRM_AMD_DC_DCN4_2 + case CM_LUT_SIZE_171717: width = hubp_3dlut_fl_width_17; break; - case DC_CM2_GPU_MEM_SIZE_TRANSFORMED: - width = hubp_3dlut_fl_width_transformed; - break; default: - //TODO: handle default case + /* keep default hubp_3dlut_fl_width_transformed */ break; } //check for support if (mpc->funcs->mcm.is_config_supported && !mpc->funcs->mcm.is_config_supported(width)) - break; + return; if (mpc->funcs->program_lut_read_write_control) mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, mpcc_id); @@ -546,21 +540,24 @@ void dcn401_populate_mcm_luts(struct dc *dc, mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id); if (hubp->funcs->hubp_program_3dlut_fl_addr) - hubp->funcs->hubp_program_3dlut_fl_addr(hubp, mcm_luts.lut3d_data.gpu_mem_params.addr); + hubp->funcs->hubp_program_3dlut_fl_addr(hubp, cm->lut3d_dma.addr); + /* bit_depth was previously zero-initialized in local_mcm, + * preserve identical behavior. + */ if (mpc->funcs->mcm.program_bit_depth) - mpc->funcs->mcm.program_bit_depth(mpc, mcm_luts.lut3d_data.gpu_mem_params.bit_depth, mpcc_id); + mpc->funcs->mcm.program_bit_depth(mpc, 0, mpcc_id); - switch (mcm_luts.lut3d_data.gpu_mem_params.layout) { - case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB: + switch (cm->lut3d_dma.swizzle) { + case CM_LUT_3D_SWIZZLE_LINEAR_RGB: mode = hubp_3dlut_fl_mode_native_1; addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; break; - case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR: + case CM_LUT_3D_SWIZZLE_LINEAR_BGR: mode = hubp_3dlut_fl_mode_native_2; addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; break; - case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR: + case CM_LUT_1D_PACKED_LINEAR: mode = hubp_3dlut_fl_mode_transform; addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear; break; @@ -575,40 +572,38 @@ void dcn401_populate_mcm_luts(struct dc *dc, if (hubp->funcs->hubp_program_3dlut_fl_addressing_mode) hubp->funcs->hubp_program_3dlut_fl_addressing_mode(hubp, addr_mode); - switch (mcm_luts.lut3d_data.gpu_mem_params.format_params.format) { - case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB: + switch (cm->lut3d_dma.format) { + case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB: format = hubp_3dlut_fl_format_unorm_12msb_bitslice; break; - case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB: + case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB: format = hubp_3dlut_fl_format_unorm_12lsb_bitslice; break; - case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10: + case CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10: format = hubp_3dlut_fl_format_float_fp1_5_10; break; + default: + break; } if (hubp->funcs->hubp_program_3dlut_fl_format) hubp->funcs->hubp_program_3dlut_fl_format(hubp, format); if (hubp->funcs->hubp_update_3dlut_fl_bias_scale && mpc->funcs->mcm.program_bias_scale) { mpc->funcs->mcm.program_bias_scale(mpc, - mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias, - mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale, + cm->lut3d_dma.bias, + cm->lut3d_dma.scale, mpcc_id); hubp->funcs->hubp_update_3dlut_fl_bias_scale(hubp, - mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias, - mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale); + cm->lut3d_dma.bias, + cm->lut3d_dma.scale); } - //navi 4x has a bug and r and blue are swapped and need to be worked around here in - //TODO: need to make a method for get_xbar per asic OR do the workaround in program_crossbar for 4x - switch (mcm_luts.lut3d_data.gpu_mem_params.component_order) { - case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA: - default: - crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15; - crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; - crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47; - break; - } + /* component_order was previously hard-coded to RGBA in local_mcm, + * preserve identical behavior. + */ + crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15; + crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; + crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47; if (hubp->funcs->hubp_program_3dlut_fl_crossbar) hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp, @@ -634,8 +629,6 @@ void dcn401_populate_mcm_luts(struct dc *dc, mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id); } } - break; - } } @@ -660,19 +653,19 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, const struct pwl_params *lut_params = NULL; bool rval; - if (plane_state->mcm_luts.lut3d_data.lut3d_src == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) { - dcn401_populate_mcm_luts(dc, pipe_ctx, plane_state->mcm_luts, plane_state->lut_bank_a); + if (plane_state->cm.flags.bits.lut3d_dma_enable) { + dcn401_populate_mcm_luts(dc, pipe_ctx, &plane_state->cm, plane_state->lut_bank_a); return true; } mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE; // 1D LUT - if (plane_state->blend_tf.type == TF_TYPE_HWPWL) - lut_params = &plane_state->blend_tf.pwl; - else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL) + lut_params = &plane_state->cm.blend_func.pwl; + else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) { rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->blend_tf, + &plane_state->cm.blend_func, &dpp_base->regamma_params, false); lut_params = rval ? &dpp_base->regamma_params : NULL; } @@ -680,12 +673,12 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, lut_params = NULL; // Shaper - if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL) - lut_params = &plane_state->in_shaper_func.pwl; - else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (plane_state->cm.shaper_func.type == TF_TYPE_HWPWL) + lut_params = &plane_state->cm.shaper_func.pwl; + else if (plane_state->cm.shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { // TODO: dpp_base replace rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->in_shaper_func, + &plane_state->cm.shaper_func, &dpp_base->shaper_params, true); lut_params = rval ? &dpp_base->shaper_params : NULL; } @@ -693,8 +686,8 @@ bool dcn401_set_mcm_luts(struct pipe_ctx *pipe_ctx, // 3D if (mpc->funcs->program_3dlut) { - if (plane_state->lut3d_func.state.bits.initialized == 1) - result &= mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id); + if (plane_state->cm.lut3d_func.state.bits.initialized == 1) + result &= mpc->funcs->program_3dlut(mpc, &plane_state->cm.lut3d_func.lut_3d, mpcc_id); else result &= mpc->funcs->program_3dlut(mpc, NULL, mpcc_id); } @@ -1428,7 +1421,7 @@ void dcn401_wait_for_dcc_meta_propagation(const struct dc *dc, if (pipe_ctx->plane_state && pipe_ctx->plane_state->dcc.enable && pipe_ctx->plane_state->flip_immediate && - pipe_ctx->plane_state->update_flags.bits.addr_update) { + pipe_ctx->plane_state->update_bits.addr_update) { is_wait_needed = true; break; } @@ -1503,6 +1496,57 @@ void dcn401_prepare_bandwidth(struct dc *dc, } } +void dcn401_prepare_bandwidth_sequence(struct dc *dc, + struct dc_state *context, + struct block_sequence_state *seq_state) +{ + struct hubbub *hubbub = dc->res_pool->hubbub; + bool p_state_change_support = context->bw_ctx.bw.dcn.clk.p_state_change_support; + unsigned int compbuf_size = 0; + + /* Any transition into P-State support should disable MCLK switching first to avoid hangs */ + if (p_state_change_support) { + dc->optimized_required = true; + context->bw_ctx.bw.dcn.clk.p_state_change_support = false; + } + + if (dc->clk_mgr->dc_mode_softmax_enabled) + if (dc->clk_mgr->clks.dramclk_khz <= (int)dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 && + context->bw_ctx.bw.dcn.clk.dramclk_khz > (int)dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000) + hwss_add_clk_mgr_set_max_memclk(seq_state, dc->clk_mgr, + dc->clk_mgr->bw_params->clk_table.entries[dc->clk_mgr->bw_params->clk_table.num_entries - 1].memclk_mhz); + + /* Build bandwidth and display clocks back-to-back (SW calc + append BLS steps) */ + if (dc->clk_mgr->funcs->build_clock_update_for_bls) + dc->clk_mgr->funcs->build_clock_update_for_bls( + dc->clk_mgr, context, false, seq_state); + + hwss_add_hubbub_program_watermarks(seq_state, dc, hubbub, + &context->bw_ctx.bw.dcn.watermarks, + dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, + false); + + if (hubbub->funcs->program_arbiter) + hwss_add_hubbub_program_arbiter(seq_state, dc, hubbub, + &context->bw_ctx.bw.dcn.arb_regs, false); + + if (hubbub->funcs->program_compbuf_segments) { + compbuf_size = context->bw_ctx.bw.dcn.arb_regs.compbuf_size; + dc->optimized_required |= (compbuf_size != dc->current_state->bw_ctx.bw.dcn.arb_regs.compbuf_size); + + hwss_add_hubbub_program_compbuf_segments(seq_state, hubbub, compbuf_size, false); + } + + if (dc->debug.fams2_config.bits.enable) { + dcn401_dmub_hw_control_lock(dc, context, true); + dcn401_fams2_update_config(dc, context, false); + dcn401_dmub_hw_control_lock(dc, context, false); + } + + if (p_state_change_support != context->bw_ctx.bw.dcn.clk.p_state_change_support) + context->bw_ctx.bw.dcn.clk.p_state_change_support = p_state_change_support; +} + void dcn401_optimize_bandwidth( struct dc *dc, struct dc_state *context) @@ -1556,6 +1600,50 @@ void dcn401_optimize_bandwidth( } } +/* + * optimize_bandwidth_sequence is unused for now. It will be used when + * dc_commit_state_no_check is moved into block sequence pattern, similar + * to how commit_planes_do_stream_update_sequence replaces + * commit_planes_do_stream_update. + */ +void dcn401_optimize_bandwidth_sequence(struct dc *dc, + struct dc_state *context, + struct block_sequence_state *seq_state) +{ + struct hubbub *hubbub = dc->res_pool->hubbub; + + /* enable fams2 if needed */ + if (dc->debug.fams2_config.bits.enable) { + dcn401_dmub_hw_control_lock(dc, context, true); + dcn401_fams2_update_config(dc, context, true); + dcn401_dmub_hw_control_lock(dc, context, false); + } + + hwss_add_hubbub_program_watermarks(seq_state, dc, hubbub, + &context->bw_ctx.bw.dcn.watermarks, + dc->res_pool->ref_clocks.dchub_ref_clock_inKhz / 1000, + true); + + if (hubbub->funcs->program_arbiter) + hwss_add_hubbub_program_arbiter(seq_state, dc, hubbub, + &context->bw_ctx.bw.dcn.arb_regs, true); + + if (dc->clk_mgr->dc_mode_softmax_enabled) + if (dc->clk_mgr->clks.dramclk_khz > (int)dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000 && + context->bw_ctx.bw.dcn.clk.dramclk_khz <= (int)dc->clk_mgr->bw_params->dc_mode_softmax_memclk * 1000) + hwss_add_clk_mgr_set_max_memclk(seq_state, dc->clk_mgr, + dc->clk_mgr->bw_params->dc_mode_softmax_memclk); + + if (hubbub->funcs->program_compbuf_segments) + hwss_add_hubbub_program_compbuf_segments(seq_state, hubbub, + context->bw_ctx.bw.dcn.arb_regs.compbuf_size, true); + + /* Build bandwidth and display clocks (SW calc + append BLS steps) */ + if (dc->clk_mgr->funcs->build_clock_update_for_bls) + dc->clk_mgr->funcs->build_clock_update_for_bls( + dc->clk_mgr, context, true, seq_state); +} + void dcn401_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock) @@ -1999,10 +2087,9 @@ void dcn401_perform_3dlut_wa_unlock(struct pipe_ctx *pipe_ctx) for (odm_pipe = pipe_ctx; odm_pipe != NULL; odm_pipe = odm_pipe->next_odm_pipe) { for (mpc_pipe = odm_pipe; mpc_pipe != NULL; mpc_pipe = mpc_pipe->bottom_pipe) { - if (mpc_pipe->plane_state && mpc_pipe->plane_state->mcm_luts.lut3d_data.lut3d_src - == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM - && mpc_pipe->plane_state->mcm_shaper_3dlut_setting - == DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT) { + if (mpc_pipe->plane_state && + mpc_pipe->plane_state->cm.flags.bits.lut3d_enable && + mpc_pipe->plane_state->cm.flags.bits.lut3d_dma_enable) { wa_pipes[wa_pipe_ct++] = mpc_pipe; } } @@ -2276,18 +2363,18 @@ void dcn401_program_pipe( } if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw || - pipe_ctx->plane_state->update_flags.raw || + dc_pipe_update_bits_is_any_set(&pipe_ctx->plane_state->update_bits) || pipe_ctx->stream->update_flags.raw)) dc->hwss.update_dchubp_dpp(dc, pipe_ctx, context); if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable || - pipe_ctx->plane_state->update_flags.bits.hdr_mult)) + pipe_ctx->plane_state->update_bits.hdr_mult)) hws->funcs.set_hdr_multiplier(pipe_ctx); if (pipe_ctx->plane_state && - (pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || - pipe_ctx->plane_state->update_flags.bits.gamma_change || - pipe_ctx->plane_state->update_flags.bits.lut_3d || + (pipe_ctx->plane_state->update_bits.in_transfer_func_change || + pipe_ctx->plane_state->update_bits.gamma_change || + pipe_ctx->plane_state->update_bits.lut_3d || pipe_ctx->update_flags.bits.enable)) hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); @@ -2346,7 +2433,7 @@ void dcn401_program_pipe( pipe_ctx->stream_res.test_pattern_params.offset); } if (pipe_ctx->plane_state - && pipe_ctx->plane_state->update_flags.bits.cm_hist_change + && pipe_ctx->plane_state->update_bits.cm_hist_change && hws->funcs.program_cm_hist) hws->funcs.program_cm_hist(dc, pipe_ctx, pipe_ctx->plane_state); } @@ -2427,7 +2514,7 @@ void dcn401_program_pipe_sequence( } if (pipe_ctx->plane_state && (pipe_ctx->update_flags.raw || - pipe_ctx->plane_state->update_flags.raw || + dc_pipe_update_bits_is_any_set(&pipe_ctx->plane_state->update_bits) || pipe_ctx->stream->update_flags.raw)) { if (dc->hwss.update_dchubp_dpp_sequence) @@ -2435,15 +2522,15 @@ void dcn401_program_pipe_sequence( } if (pipe_ctx->plane_state && (pipe_ctx->update_flags.bits.enable || - pipe_ctx->plane_state->update_flags.bits.hdr_mult)) { + pipe_ctx->plane_state->update_bits.hdr_mult)) { hws->funcs.set_hdr_multiplier_sequence(pipe_ctx, seq_state); } if (pipe_ctx->plane_state && - (pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || - pipe_ctx->plane_state->update_flags.bits.gamma_change || - pipe_ctx->plane_state->update_flags.bits.lut_3d || + (pipe_ctx->plane_state->update_bits.in_transfer_func_change || + pipe_ctx->plane_state->update_bits.gamma_change || + pipe_ctx->plane_state->update_bits.lut_3d || pipe_ctx->update_flags.bits.enable)) { hwss_add_dpp_set_input_transfer_func(seq_state, dc, pipe_ctx, pipe_ctx->plane_state); @@ -2501,7 +2588,7 @@ void dcn401_program_pipe_sequence( } if (pipe_ctx->plane_state - && pipe_ctx->plane_state->update_flags.bits.cm_hist_change + && pipe_ctx->plane_state->update_bits.cm_hist_change && hws->funcs.program_cm_hist) { hwss_add_dpp_program_cm_hist(seq_state, pipe_ctx->plane_res.dpp, @@ -2655,7 +2742,7 @@ void dcn401_program_front_end_for_ctx( pipe = &context->res_ctx.pipe_ctx[i]; if (!pipe->top_pipe && !pipe->prev_odm_pipe && pipe->stream && pipe->stream->num_wb_info > 0 - && (pipe->update_flags.raw || (pipe->plane_state && pipe->plane_state->update_flags.raw) + && (pipe->update_flags.raw || (pipe->plane_state && dc_pipe_update_bits_is_any_set(&pipe->plane_state->update_bits)) || pipe->stream->update_flags.raw) && hws->funcs.program_all_writeback_pipes_in_tree) hws->funcs.program_all_writeback_pipes_in_tree(dc, pipe->stream, context); @@ -3741,10 +3828,10 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc, /* Step 7: DPP setup - input CSC and format setup */ if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.plane_changed || - plane_state->update_flags.bits.bpp_change || - plane_state->update_flags.bits.input_csc_change || - plane_state->update_flags.bits.color_space_change || - plane_state->update_flags.bits.coeff_reduction_change) { + plane_state->update_bits.bpp_change || + plane_state->update_bits.input_csc_change || + plane_state->update_bits.color_space_change || + plane_state->update_bits.coeff_reduction_change) { hwss_add_dpp_setup_dpp(seq_state, pipe_ctx); /* Step 8: DPP cursor matrix setup */ @@ -3761,8 +3848,8 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc, /* Step 10: MPCC updates */ if (pipe_ctx->update_flags.bits.mpcc || pipe_ctx->update_flags.bits.plane_changed || - plane_state->update_flags.bits.global_alpha_change || - plane_state->update_flags.bits.per_pixel_alpha_change) { + plane_state->update_bits.global_alpha_change || + plane_state->update_bits.per_pixel_alpha_change) { /* Check if update_mpcc_sequence is implemented and prefer it over single MPC_UPDATE_MPCC step */ if (hws->funcs.update_mpcc_sequence) @@ -3771,9 +3858,9 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc, /* Step 11: DPP scaler setup */ if (pipe_ctx->update_flags.bits.scaler || - plane_state->update_flags.bits.scaling_change || - plane_state->update_flags.bits.position_change || - plane_state->update_flags.bits.per_pixel_alpha_change || + plane_state->update_bits.scaling_change || + plane_state->update_bits.position_change || + plane_state->update_bits.per_pixel_alpha_change || pipe_ctx->stream->update_flags.bits.scaling) { pipe_ctx->plane_res.scl_data.lb_params.alpha_en = pipe_ctx->plane_state->per_pixel_alpha; ASSERT(pipe_ctx->plane_res.scl_data.lb_params.depth == LB_PIXEL_DEPTH_36BPP); @@ -3782,8 +3869,8 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc, /* Step 12: HUBP viewport programming */ if (pipe_ctx->update_flags.bits.viewport || - (context == dc->current_state && plane_state->update_flags.bits.position_change) || - (context == dc->current_state && plane_state->update_flags.bits.scaling_change) || + (context == dc->current_state && plane_state->update_bits.position_change) || + (context == dc->current_state && plane_state->update_bits.scaling_change) || (context == dc->current_state && pipe_ctx->stream->update_flags.bits.scaling)) { hwss_add_hubp_mem_program_viewport(seq_state, hubp, &pipe_ctx->plane_res.scl_data.viewport, &pipe_ctx->plane_res.scl_data.viewport_c); @@ -3815,7 +3902,7 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc, if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.opp_changed || pipe_ctx->update_flags.bits.plane_changed || pipe_ctx->stream->update_flags.bits.gamut_remap || - plane_state->update_flags.bits.gamut_remap_change || + plane_state->update_bits.gamut_remap_change || pipe_ctx->stream->update_flags.bits.out_csc) { /* Gamut remap */ @@ -3830,14 +3917,14 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc, if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.plane_changed || pipe_ctx->update_flags.bits.opp_changed || - plane_state->update_flags.bits.pixel_format_change || - plane_state->update_flags.bits.horizontal_mirror_change || - plane_state->update_flags.bits.rotation_change || - plane_state->update_flags.bits.swizzle_change || - plane_state->update_flags.bits.dcc_change || - plane_state->update_flags.bits.bpp_change || - plane_state->update_flags.bits.scaling_change || - plane_state->update_flags.bits.plane_size_change) { + plane_state->update_bits.pixel_format_change || + plane_state->update_bits.horizontal_mirror_change || + plane_state->update_bits.rotation_change || + plane_state->update_bits.swizzle_change || + plane_state->update_bits.dcc_change || + plane_state->update_bits.bpp_change || + plane_state->update_bits.scaling_change || + plane_state->update_bits.plane_size_change) { struct plane_size size = plane_state->plane_size; size.surface_size = pipe_ctx->plane_res.scl_data.viewport; @@ -3851,7 +3938,7 @@ void dcn401_update_dchubp_dpp_sequence(struct dc *dc, /* Step 19: Update plane address (with SubVP support) */ if (pipe_ctx->update_flags.bits.enable || pipe_ctx->update_flags.bits.plane_changed || - plane_state->update_flags.bits.addr_update) { + plane_state->update_bits.addr_update) { /* SubVP save surface address if needed */ if (resource_is_pipe_type(pipe_ctx, OTG_MASTER) && pipe_mall_type == SUBVP_MAIN) { @@ -3924,7 +4011,7 @@ void dcn401_update_mpcc_sequence(struct dc *dc, mpcc_id = hubp->inst; /* Step 1: Update blending if no full update needed */ - if (!pipe_ctx->plane_state->update_flags.bits.full_update && + if (!pipe_ctx->plane_state->update_bits.full_update && !pipe_ctx->update_flags.bits.mpcc) { /* Update blending configuration */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h index f78162ab859b..a760050eea8c 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_hwseq.h @@ -52,7 +52,7 @@ enum dc_status dcn401_enable_stream_timing( void dcn401_enable_stream(struct pipe_ctx *pipe_ctx); void dcn401_populate_mcm_luts(struct dc *dc, struct pipe_ctx *pipe_ctx, - struct dc_cm2_func_luts mcm_luts, + const struct dc_plane_cm *cm, bool lut_bank_a); void dcn401_setup_hpo_hw_control(const struct dce_hwseq *hws, bool enable); @@ -70,10 +70,20 @@ void dcn401_wait_for_dcc_meta_propagation(const struct dc *dc, void dcn401_prepare_bandwidth(struct dc *dc, struct dc_state *context); +struct block_sequence_state; + +void dcn401_prepare_bandwidth_sequence(struct dc *dc, + struct dc_state *context, + struct block_sequence_state *seq_state); + void dcn401_optimize_bandwidth( struct dc *dc, struct dc_state *context); +void dcn401_optimize_bandwidth_sequence(struct dc *dc, + struct dc_state *context, + struct block_sequence_state *seq_state); + void dcn401_dmub_hw_control_lock(struct dc *dc, struct dc_state *context, bool lock); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c index 33b2cf344f1e..f206e221f926 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn401/dcn401_init.c @@ -44,7 +44,9 @@ static const struct hw_sequencer_funcs dcn401_funcs = { .interdependent_update_lock = dcn401_interdependent_update_lock, .cursor_lock = dcn10_cursor_lock, .prepare_bandwidth = dcn401_prepare_bandwidth, + .prepare_bandwidth_sequence = dcn401_prepare_bandwidth_sequence, .optimize_bandwidth = dcn401_optimize_bandwidth, + .optimize_bandwidth_sequence = dcn401_optimize_bandwidth_sequence, .update_bandwidth = dcn401_update_bandwidth, .set_drr = dcn10_set_drr, .get_position = dcn10_get_position, diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c index 664004cadf10..f415473517d4 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.c @@ -70,6 +70,7 @@ void dcn42_init_hw(struct dc *dc) uint32_t user_level = MAX_BACKLIGHT_LEVEL; bool dchub_ref_freq_changed; int current_dchub_ref_freq = 0; + uint8_t dcfclk_gate_dis_value = 0; if (dc->clk_mgr && dc->clk_mgr->funcs && dc->clk_mgr->funcs->init_clocks) { dc->clk_mgr->funcs->init_clocks(dc->clk_mgr); @@ -243,7 +244,13 @@ void dcn42_init_hw(struct dc *dc) /* enable all DCN clock gating */ REG_WRITE(DCCG_GATE_DISABLE_CNTL, 0); - REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, 0); + /* Temporary workaround for IOMMU mismatch issue. + * Fine grain control via bit0 of debug flag. + */ + if (dc->debug.iommu_mismatch_temp_wka & 0x1) + dcfclk_gate_dis_value = 1; + + REG_UPDATE(DCFCLK_CNTL, DCFCLK_GATE_DIS, dcfclk_gate_dis_value); } dcn401_setup_hpo_hw_control(hws, true); @@ -348,7 +355,7 @@ void dcn42_update_mpcc(struct dc *dc, struct pipe_ctx *pipe_ctx) mpcc_id = hubp->inst; /* If there is no full update, don't need to touch MPC tree*/ - if (!pipe_ctx->plane_state->update_flags.bits.full_update && + if (!pipe_ctx->plane_state->update_bits.full_update && !pipe_ctx->update_flags.bits.mpcc) { mpc->funcs->update_blending(mpc, &blnd_cfg, mpcc_id); dc->hwss.update_visual_confirm_color(dc, pipe_ctx, mpcc_id); @@ -394,40 +401,33 @@ void dcn42_program_cm_hist( } static void dc_get_lut_xbar( - enum dc_cm2_gpu_mem_pixel_component_order order, enum hubp_3dlut_fl_crossbar_bit_slice *cr_r, enum hubp_3dlut_fl_crossbar_bit_slice *y_g, enum hubp_3dlut_fl_crossbar_bit_slice *cb_b) { - switch (order) { - case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_RGBA: - *cr_r = hubp_3dlut_fl_crossbar_bit_slice_32_47; - *y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; - *cb_b = hubp_3dlut_fl_crossbar_bit_slice_0_15; - break; - case DC_CM2_GPU_MEM_PIXEL_COMPONENT_ORDER_BGRA: - *cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15; - *y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; - *cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47; - break; - } + /* component_order was previously hard-coded to RGBA in local_mcm, + * preserve identical behavior. + */ + *cr_r = hubp_3dlut_fl_crossbar_bit_slice_32_47; + *y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; + *cb_b = hubp_3dlut_fl_crossbar_bit_slice_0_15; } static void dc_get_lut_mode( - enum dc_cm2_gpu_mem_layout layout, + enum dc_cm_lut_swizzle swizzle, enum hubp_3dlut_fl_mode *mode, enum hubp_3dlut_fl_addressing_mode *addr_mode) { - switch (layout) { - case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB: + switch (swizzle) { + case CM_LUT_3D_SWIZZLE_LINEAR_RGB: *mode = hubp_3dlut_fl_mode_native_1; *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; break; - case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR: + case CM_LUT_3D_SWIZZLE_LINEAR_BGR: *mode = hubp_3dlut_fl_mode_native_2; *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; break; - case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR: + case CM_LUT_1D_PACKED_LINEAR: *mode = hubp_3dlut_fl_mode_transform; *addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear; break; @@ -439,19 +439,22 @@ static void dc_get_lut_mode( } static void dc_get_lut_format( - enum dc_cm2_gpu_mem_format dc_format, + enum dc_cm_lut_pixel_format dc_format, enum hubp_3dlut_fl_format *format) { switch (dc_format) { - case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB: + case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB: *format = hubp_3dlut_fl_format_unorm_12msb_bitslice; break; - case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB: + case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB: *format = hubp_3dlut_fl_format_unorm_12lsb_bitslice; break; - case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10: + case CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10: *format = hubp_3dlut_fl_format_float_fp1_5_10; break; + default: + *format = hubp_3dlut_fl_format_unorm_12msb_bitslice; + break; } } @@ -465,16 +468,17 @@ static bool dc_is_rmcm_3dlut_supported(struct hubp *hubp, struct mpc *mpc) return false; } -static bool is_rmcm_3dlut_fl_supported(struct dc *dc, enum dc_cm2_gpu_mem_size size) +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) +static bool is_rmcm_3dlut_fl_supported(struct dc *dc) { + /* size was previously hard-coded to TRANSFORMED in local_mcm, + * which mapped to dim_17. Preserve identical behavior. + */ if (!dc->caps.color.mpc.rmcm_3d_lut_caps.dma_3d_lut) return false; - if (size == DC_CM2_GPU_MEM_SIZE_171717) - return dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_17 != 0u; - else if (size == DC_CM2_GPU_MEM_SIZE_333333) - return dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_33 != 0u; - return false; + return dc->caps.color.mpc.rmcm_3d_lut_caps.lut_dim_caps.dim_17 != 0u; } +#endif static void dcn42_set_mcm_location_post_blend(struct dc *dc, struct pipe_ctx *pipe_ctx, bool bPostBlend) { @@ -495,56 +499,45 @@ static void dcn42_get_mcm_lut_xable_from_pipe_ctx(struct dc *dc, struct pipe_ctx enum MCM_LUT_XABLE *lut3d_xable, enum MCM_LUT_XABLE *lut1d_xable) { - enum dc_cm2_shaper_3dlut_setting shaper_3dlut_setting = DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL; - bool lut1d_enable = false; struct mpc *mpc = dc->res_pool->mpc; int mpcc_id = pipe_ctx->plane_res.hubp->inst; if (!pipe_ctx->plane_state) return; - shaper_3dlut_setting = pipe_ctx->plane_state->mcm_shaper_3dlut_setting; - lut1d_enable = pipe_ctx->plane_state->mcm_lut1d_enable; + mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE; - *lut1d_xable = lut1d_enable ? MCM_LUT_ENABLE : MCM_LUT_DISABLE; - - switch (shaper_3dlut_setting) { - case DC_CM2_SHAPER_3DLUT_SETTING_BYPASS_ALL: - *lut3d_xable = *shaper_xable = MCM_LUT_DISABLE; - break; - case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER: - *lut3d_xable = MCM_LUT_DISABLE; - *shaper_xable = MCM_LUT_ENABLE; - break; - case DC_CM2_SHAPER_3DLUT_SETTING_ENABLE_SHAPER_3DLUT: - *lut3d_xable = *shaper_xable = MCM_LUT_ENABLE; - break; - } + *lut1d_xable = pipe_ctx->plane_state->cm.flags.bits.blend_enable ? + MCM_LUT_ENABLE : MCM_LUT_DISABLE; + *shaper_xable = pipe_ctx->plane_state->cm.flags.bits.shaper_enable ? + MCM_LUT_ENABLE : MCM_LUT_DISABLE; + *lut3d_xable = (pipe_ctx->plane_state->cm.flags.bits.shaper_enable && + pipe_ctx->plane_state->cm.flags.bits.lut3d_enable) ? + MCM_LUT_ENABLE : MCM_LUT_DISABLE; } static void fl_get_lut_mode( - enum dc_cm2_gpu_mem_layout layout, - enum dc_cm2_gpu_mem_size size, + enum dc_cm_lut_swizzle swizzle, enum hubp_3dlut_fl_mode *mode, enum hubp_3dlut_fl_addressing_mode *addr_mode, enum hubp_3dlut_fl_width *width) { + /* size was previously hard-coded to TRANSFORMED in local_mcm, + * preserve identical behavior (transformed width). + */ *width = hubp_3dlut_fl_width_17; - if (size == DC_CM2_GPU_MEM_SIZE_333333) - *width = hubp_3dlut_fl_width_33; - - switch (layout) { - case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_RGB: + switch (swizzle) { + case CM_LUT_3D_SWIZZLE_LINEAR_RGB: *mode = hubp_3dlut_fl_mode_native_1; *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; break; - case DC_CM2_GPU_MEM_LAYOUT_3D_SWIZZLE_LINEAR_BGR: + case CM_LUT_3D_SWIZZLE_LINEAR_BGR: *mode = hubp_3dlut_fl_mode_native_2; *addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; break; - case DC_CM2_GPU_MEM_LAYOUT_1D_PACKED_LINEAR: + case CM_LUT_1D_PACKED_LINEAR: *mode = hubp_3dlut_fl_mode_transform; *addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear; break; @@ -558,8 +551,7 @@ static void fl_get_lut_mode( bool dcn42_program_rmcm_luts( struct hubp *hubp, struct pipe_ctx *pipe_ctx, - enum dc_cm2_transfer_func_source lut3d_src, - struct dc_cm2_func_luts *mcm_luts, + const struct dc_plane_cm *cm, struct mpc *mpc, bool lut_bank_a, int mpcc_id) @@ -589,21 +581,24 @@ bool dcn42_program_rmcm_luts( if (!rmcm_3dlut) return false; - rmcm_3dlut->protection_bits = mcm_luts->lut3d_data.rmcm_tmz; + /* rmcm_tmz was previously zero-initialized in local_mcm, + * preserve identical behavior. + */ + rmcm_3dlut->protection_bits = 0; dcn42_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable); /* Shaper */ - if (mcm_luts->shaper) { + { memset(&m_lut_params, 0, sizeof(m_lut_params)); - if (mcm_luts->shaper->type == TF_TYPE_HWPWL) { - m_lut_params.pwl = &mcm_luts->shaper->pwl; - } else if (mcm_luts->shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { + if (cm->shaper_func.type == TF_TYPE_HWPWL) { + m_lut_params.pwl = &cm->shaper_func.pwl; + } else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { ASSERT(false); cm_helper_translate_curve_to_hw_format( dc->ctx, - mcm_luts->shaper, + &cm->shaper_func, &dpp_base->shaper_params, true); m_lut_params.pwl = &dpp_base->shaper_params; } @@ -619,15 +614,16 @@ bool dcn42_program_rmcm_luts( } /* 3DLUT */ - switch (lut3d_src) { - case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM: + if (!cm->flags.bits.lut3d_dma_enable) { + /* SYSMEM path — no DMA 3DLUT available. + * Previously this was treated as a no-op for the DMA/VIDMEM + * programming, preserve identical behavior. + */ memset(&m_lut_params, 0, sizeof(m_lut_params)); - // Don't know what to do in this case. - //case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM: - break; - case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM: - fl_get_lut_mode(mcm_luts->lut3d_data.gpu_mem_params.layout, - mcm_luts->lut3d_data.gpu_mem_params.size, + } else { + /* VIDMEM (3DLUT DMA Fast Load) */ + + fl_get_lut_mode(cm->lut3d_dma.swizzle, &mode, &addr_mode, &width); @@ -639,20 +635,19 @@ bool dcn42_program_rmcm_luts( return false; // setting native or transformed mode, - dc_get_lut_mode(mcm_luts->lut3d_data.gpu_mem_params.layout, &mode, &addr_mode); + dc_get_lut_mode(cm->lut3d_dma.swizzle, &mode, &addr_mode); //seems to be only for the MCM - dc_get_lut_format(mcm_luts->lut3d_data.gpu_mem_params.format_params.format, &format); + dc_get_lut_format(cm->lut3d_dma.format, &format); dc_get_lut_xbar( - mcm_luts->lut3d_data.gpu_mem_params.component_order, &crossbar_bit_slice_cr_r, &crossbar_bit_slice_y_g, &crossbar_bit_slice_cb_b); fl_config.mode = mode; fl_config.enabled = lut3d_xable != MCM_LUT_DISABLE; - fl_config.address = mcm_luts->lut3d_data.gpu_mem_params.addr; + fl_config.address = cm->lut3d_dma.addr; fl_config.format = format; fl_config.crossbar_bit_slice_y_g = crossbar_bit_slice_y_g; fl_config.crossbar_bit_slice_cb_b = crossbar_bit_slice_cb_b; @@ -660,17 +655,20 @@ bool dcn42_program_rmcm_luts( fl_config.width = width; fl_config.protection_bits = rmcm_3dlut->protection_bits; fl_config.addr_mode = addr_mode; - fl_config.layout = mcm_luts->lut3d_data.gpu_mem_params.layout; - fl_config.bias = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.bias; - fl_config.scale = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.scale; + fl_config.layout = cm->lut3d_dma.swizzle; + fl_config.bias = cm->lut3d_dma.bias; + fl_config.scale = cm->lut3d_dma.scale; mpc_fl_config.enabled = fl_config.enabled; mpc_fl_config.width = width; mpc_fl_config.select_lut_bank_a = lut_bank_a; - mpc_fl_config.bit_depth = mcm_luts->lut3d_data.gpu_mem_params.bit_depth; + /* bit_depth was previously zero-initialized in local_mcm, + * preserve identical behavior. + */ + mpc_fl_config.bit_depth = 0; mpc_fl_config.hubp_index = hubp->inst; - mpc_fl_config.bias = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.bias; - mpc_fl_config.scale = mcm_luts->lut3d_data.gpu_mem_params.format_params.float_params.scale; + mpc_fl_config.bias = cm->lut3d_dma.bias; + mpc_fl_config.scale = cm->lut3d_dma.scale; //1. power down the block mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, false); @@ -682,10 +680,6 @@ bool dcn42_program_rmcm_luts( //3. power on the block mpc->funcs->rmcm.power_on_shaper_3dlut(mpc, mpcc_id, true); - - break; - default: - return false; } return true; @@ -693,7 +687,7 @@ bool dcn42_program_rmcm_luts( void dcn42_populate_mcm_luts(struct dc *dc, struct pipe_ctx *pipe_ctx, - struct dc_cm2_func_luts mcm_luts, + const struct dc_plane_cm *cm, bool lut_bank_a) { struct dpp *dpp_base = pipe_ctx->plane_res.dpp; @@ -701,14 +695,17 @@ void dcn42_populate_mcm_luts(struct dc *dc, int mpcc_id = hubp->inst; struct mpc *mpc = dc->res_pool->mpc; union mcm_lut_params m_lut_params; - enum dc_cm2_transfer_func_source lut3d_src = mcm_luts.lut3d_data.lut3d_src; + const bool lut3d_dma = !!cm->flags.bits.lut3d_dma_enable; enum hubp_3dlut_fl_format format = 0; enum hubp_3dlut_fl_mode mode; - enum hubp_3dlut_fl_width width = 0; + /* Width was previously hard-coded to TRANSFORMED via local_mcm build, + * preserve identical behavior. + */ + enum hubp_3dlut_fl_width width = hubp_3dlut_fl_width_transformed; enum hubp_3dlut_fl_addressing_mode addr_mode; - enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g = 0; - enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b = 0; - enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r = 0; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b; + enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cr_r; enum MCM_LUT_XABLE shaper_xable = MCM_LUT_DISABLE; enum MCM_LUT_XABLE lut3d_xable = MCM_LUT_DISABLE; enum MCM_LUT_XABLE lut1d_xable = MCM_LUT_DISABLE; @@ -717,33 +714,35 @@ void dcn42_populate_mcm_luts(struct dc *dc, dcn42_get_mcm_lut_xable_from_pipe_ctx(dc, pipe_ctx, &shaper_xable, &lut3d_xable, &lut1d_xable); //MCM - setting its location (Before/After) blender - //set to post blend (true) + //mpc_mcm_post_blend was previously zero-initialized in local_mcm, + //preserve identical behavior. dcn42_set_mcm_location_post_blend( dc, pipe_ctx, - mcm_luts.lut3d_data.mpc_mcm_post_blend); + false); //RMCM - 3dLUT+Shaper - if (mcm_luts.lut3d_data.rmcm_3dlut_enable && - is_rmcm_3dlut_fl_supported(dc, mcm_luts.lut3d_data.gpu_mem_params.size)) { +#if defined(CONFIG_DRM_AMD_DC_DCN4_2) + if (cm->flags.bits.rmcm_enable && + is_rmcm_3dlut_fl_supported(dc)) { dcn42_program_rmcm_luts( hubp, pipe_ctx, - lut3d_src, - &mcm_luts, + cm, mpc, lut_bank_a, mpcc_id); } +#endif /* CONFIG_DRM_AMD_DC_DCN4_2 */ /* 1D LUT */ - if (mcm_luts.lut1d_func) { + { memset(&m_lut_params, 0, sizeof(m_lut_params)); - if (mcm_luts.lut1d_func->type == TF_TYPE_HWPWL) - m_lut_params.pwl = &mcm_luts.lut1d_func->pwl; - else if (mcm_luts.lut1d_func->type == TF_TYPE_DISTRIBUTED_POINTS) { + if (cm->blend_func.type == TF_TYPE_HWPWL) + m_lut_params.pwl = &cm->blend_func.pwl; + else if (cm->blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) { rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, - mcm_luts.lut1d_func, + &cm->blend_func, &dpp_base->regamma_params, false); m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; } @@ -756,14 +755,14 @@ void dcn42_populate_mcm_luts(struct dc *dc, } /* Shaper */ - if (mcm_luts.shaper && mcm_luts.lut3d_data.mpc_3dlut_enable) { + if (cm->flags.bits.lut3d_enable) { memset(&m_lut_params, 0, sizeof(m_lut_params)); - if (mcm_luts.shaper->type == TF_TYPE_HWPWL) - m_lut_params.pwl = &mcm_luts.shaper->pwl; - else if (mcm_luts.shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { + if (cm->shaper_func.type == TF_TYPE_HWPWL) + m_lut_params.pwl = &cm->shaper_func.pwl; + else if (cm->shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { ASSERT(false); rval = cm3_helper_translate_curve_to_hw_format(mpc->ctx, - mcm_luts.shaper, + &cm->shaper_func, &dpp_base->regamma_params, true); m_lut_params.pwl = rval ? &dpp_base->regamma_params : NULL; } @@ -776,41 +775,27 @@ void dcn42_populate_mcm_luts(struct dc *dc, } /* 3DLUT */ - switch (lut3d_src) { - case DC_CM2_TRANSFER_FUNC_SOURCE_SYSMEM: + if (!lut3d_dma) { + /* SYSMEM (legacy lut3d_func) */ memset(&m_lut_params, 0, sizeof(m_lut_params)); if (hubp->funcs->hubp_enable_3dlut_fl) hubp->funcs->hubp_enable_3dlut_fl(hubp, false); - if (mcm_luts.lut3d_data.lut3d_func && mcm_luts.lut3d_data.lut3d_func->state.bits.initialized) { - m_lut_params.lut3d = &mcm_luts.lut3d_data.lut3d_func->lut_3d; + if (cm->lut3d_func.state.bits.initialized) { + m_lut_params.lut3d = &cm->lut3d_func.lut_3d; if (mpc->funcs->populate_lut) mpc->funcs->populate_lut(mpc, MCM_LUT_3DLUT, m_lut_params, lut_bank_a, mpcc_id); if (mpc->funcs->program_lut_mode) mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id); } - break; - case DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM: - switch (mcm_luts.lut3d_data.gpu_mem_params.size) { - case DC_CM2_GPU_MEM_SIZE_333333: - width = hubp_3dlut_fl_width_33; - break; - case DC_CM2_GPU_MEM_SIZE_171717: - width = hubp_3dlut_fl_width_17; - break; - case DC_CM2_GPU_MEM_SIZE_TRANSFORMED: - width = hubp_3dlut_fl_width_transformed; - break; - default: - //TODO: Handle default case - break; - } + } else { + /* VIDMEM (3DLUT DMA Fast Load) */ //check for support if (mpc->funcs->mcm.is_config_supported && !mpc->funcs->mcm.is_config_supported(width)) - break; + return; if (mpc->funcs->program_lut_read_write_control) mpc->funcs->program_lut_read_write_control(mpc, MCM_LUT_3DLUT, lut_bank_a, mpcc_id); @@ -818,49 +803,70 @@ void dcn42_populate_mcm_luts(struct dc *dc, mpc->funcs->program_lut_mode(mpc, MCM_LUT_3DLUT, lut3d_xable, lut_bank_a, mpcc_id); if (hubp->funcs->hubp_program_3dlut_fl_addr) - hubp->funcs->hubp_program_3dlut_fl_addr(hubp, mcm_luts.lut3d_data.gpu_mem_params.addr); + hubp->funcs->hubp_program_3dlut_fl_addr(hubp, cm->lut3d_dma.addr); + /* bit_depth was previously zero-initialized in local_mcm, + * preserve identical behavior. + */ if (mpc->funcs->mcm.program_bit_depth) - mpc->funcs->mcm.program_bit_depth(mpc, mcm_luts.lut3d_data.gpu_mem_params.bit_depth, mpcc_id); + mpc->funcs->mcm.program_bit_depth(mpc, 0, mpcc_id); - dc_get_lut_mode(mcm_luts.lut3d_data.gpu_mem_params.layout, &mode, &addr_mode); + switch (cm->lut3d_dma.swizzle) { + case CM_LUT_3D_SWIZZLE_LINEAR_RGB: + mode = hubp_3dlut_fl_mode_native_1; + addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + case CM_LUT_3D_SWIZZLE_LINEAR_BGR: + mode = hubp_3dlut_fl_mode_native_2; + addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + case CM_LUT_1D_PACKED_LINEAR: + mode = hubp_3dlut_fl_mode_transform; + addr_mode = hubp_3dlut_fl_addressing_mode_simple_linear; + break; + default: + mode = hubp_3dlut_fl_mode_disable; + addr_mode = hubp_3dlut_fl_addressing_mode_sw_linear; + break; + } if (hubp->funcs->hubp_program_3dlut_fl_mode) hubp->funcs->hubp_program_3dlut_fl_mode(hubp, mode); if (hubp->funcs->hubp_program_3dlut_fl_addressing_mode) hubp->funcs->hubp_program_3dlut_fl_addressing_mode(hubp, addr_mode); - switch (mcm_luts.lut3d_data.gpu_mem_params.format_params.format) { - case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12MSB: + switch (cm->lut3d_dma.format) { + case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12MSB: format = hubp_3dlut_fl_format_unorm_12msb_bitslice; break; - case DC_CM2_GPU_MEM_FORMAT_16161616_UNORM_12LSB: + case CM_LUT_PIXEL_FORMAT_RGBA16161616_UNORM_12LSB: format = hubp_3dlut_fl_format_unorm_12lsb_bitslice; break; - case DC_CM2_GPU_MEM_FORMAT_16161616_FLOAT_FP1_5_10: + case CM_LUT_PIXEL_FORMAT_RGBA16161616_FLOAT_FP1_5_10: format = hubp_3dlut_fl_format_float_fp1_5_10; break; + default: + break; } if (hubp->funcs->hubp_program_3dlut_fl_format) hubp->funcs->hubp_program_3dlut_fl_format(hubp, format); if (hubp->funcs->hubp_update_3dlut_fl_bias_scale && mpc->funcs->mcm.program_bias_scale) { mpc->funcs->mcm.program_bias_scale(mpc, - mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias, - mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale, + cm->lut3d_dma.bias, + cm->lut3d_dma.scale, mpcc_id); hubp->funcs->hubp_update_3dlut_fl_bias_scale(hubp, - mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.bias, - mcm_luts.lut3d_data.gpu_mem_params.format_params.float_params.scale); + cm->lut3d_dma.bias, + cm->lut3d_dma.scale); } - //navi 4x has a bug and r and blue are swapped and need to be worked around here in - //TODO: need to make a method for get_xbar per asic OR do the workaround in program_crossbar for 4x - dc_get_lut_xbar( - mcm_luts.lut3d_data.gpu_mem_params.component_order, - &crossbar_bit_slice_cr_r, - &crossbar_bit_slice_y_g, - &crossbar_bit_slice_cb_b); + /* component_order was previously hard-coded to RGBA in local_mcm, + * preserve identical behavior. + */ + crossbar_bit_slice_cr_r = hubp_3dlut_fl_crossbar_bit_slice_0_15; + crossbar_bit_slice_y_g = hubp_3dlut_fl_crossbar_bit_slice_16_31; + crossbar_bit_slice_cb_b = hubp_3dlut_fl_crossbar_bit_slice_32_47; if (hubp->funcs->hubp_program_3dlut_fl_crossbar) hubp->funcs->hubp_program_3dlut_fl_crossbar(hubp, @@ -886,7 +892,6 @@ void dcn42_populate_mcm_luts(struct dc *dc, mpc->funcs->program_lut_mode(mpc, MCM_LUT_1DLUT, MCM_LUT_DISABLE, lut_bank_a, mpcc_id); } } - break; } } @@ -901,19 +906,19 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, const struct pwl_params *lut_params = NULL; bool rval; - if (plane_state->mcm_luts.lut3d_data.lut3d_src == DC_CM2_TRANSFER_FUNC_SOURCE_VIDMEM) { - dcn42_populate_mcm_luts(dc, pipe_ctx, plane_state->mcm_luts, plane_state->lut_bank_a); + if (plane_state->cm.flags.bits.lut3d_dma_enable) { + dcn42_populate_mcm_luts(dc, pipe_ctx, &plane_state->cm, plane_state->lut_bank_a); return true; } mpc->funcs->set_movable_cm_location(mpc, MPCC_MOVABLE_CM_LOCATION_BEFORE, mpcc_id); pipe_ctx->plane_state->mcm_location = MPCC_MOVABLE_CM_LOCATION_BEFORE; // 1D LUT - if (plane_state->blend_tf.type == TF_TYPE_HWPWL) - lut_params = &plane_state->blend_tf.pwl; - else if (plane_state->blend_tf.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (plane_state->cm.blend_func.type == TF_TYPE_HWPWL) + lut_params = &plane_state->cm.blend_func.pwl; + else if (plane_state->cm.blend_func.type == TF_TYPE_DISTRIBUTED_POINTS) { rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->blend_tf, + &plane_state->cm.blend_func, &dpp_base->regamma_params, false); lut_params = rval ? &dpp_base->regamma_params : NULL; } @@ -921,12 +926,12 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, lut_params = NULL; // Shaper - if (plane_state->in_shaper_func.type == TF_TYPE_HWPWL) - lut_params = &plane_state->in_shaper_func.pwl; - else if (plane_state->in_shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { + if (plane_state->cm.shaper_func.type == TF_TYPE_HWPWL) + lut_params = &plane_state->cm.shaper_func.pwl; + else if (plane_state->cm.shaper_func.type == TF_TYPE_DISTRIBUTED_POINTS) { // TODO: dpp_base replace rval = cm3_helper_translate_curve_to_hw_format(plane_state->ctx, - &plane_state->in_shaper_func, + &plane_state->cm.shaper_func, &dpp_base->shaper_params, true); lut_params = rval ? &dpp_base->shaper_params : NULL; } @@ -934,8 +939,8 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, // 3D if (mpc->funcs->program_3dlut) { - if (plane_state->lut3d_func.state.bits.initialized == 1) - result &= mpc->funcs->program_3dlut(mpc, &plane_state->lut3d_func.lut_3d, mpcc_id); + if (plane_state->cm.lut3d_func.state.bits.initialized == 1) + result &= mpc->funcs->program_3dlut(mpc, &plane_state->cm.lut3d_func.lut_3d, mpcc_id); else result &= mpc->funcs->program_3dlut(mpc, NULL, mpcc_id); } diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h index 0539ee0ffaee..c469e7535114 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn42/dcn42_hwseq.h @@ -20,14 +20,13 @@ bool dcn42_set_mcm_luts(struct pipe_ctx *pipe_ctx, void dcn42_populate_mcm_luts(struct dc *dc, struct pipe_ctx *pipe_ctx, - struct dc_cm2_func_luts mcm_luts, + const struct dc_plane_cm *cm, bool lut_bank_a); bool dcn42_program_rmcm_luts( struct hubp *hubp, struct pipe_ctx *pipe_ctx, - enum dc_cm2_transfer_func_source lut3d_src, - struct dc_cm2_func_luts *mcm_luts, + const struct dc_plane_cm *cm, struct mpc *mpc, bool lut_bank_a, int mpcc_id); diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h index dfb278a9fc3e..65df8002d3d7 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer.h @@ -894,6 +894,36 @@ struct disable_audio_stream_params { struct pipe_ctx *pipe_ctx; }; +struct clk_mgr_set_max_memclk_params { + struct clk_mgr *clk_mgr; + unsigned int memclk_mhz; +}; + +struct clk_mgr_update_clocks_params { + struct clk_mgr *clk_mgr; +}; + +struct hubbub_program_watermarks_params { + struct dc *dc; + struct hubbub *hubbub; + union dcn_watermark_set *watermarks; + unsigned int refclk_mhz; + bool safe_to_lower; +}; + +struct hubbub_program_arbiter_params { + struct dc *dc; + struct hubbub *hubbub; + struct dml2_display_arb_regs *arb_regs; + bool safe_to_lower; +}; + +struct hubbub_program_compbuf_segments_params { + struct hubbub *hubbub; + unsigned int compbuf_size; + bool safe_to_lower; +}; + struct prepare_bandwidth_params { struct dc *dc; struct dc_state *context; @@ -1057,6 +1087,11 @@ union block_sequence_params { struct disable_audio_stream_params disable_audio_stream_params; struct prepare_bandwidth_params prepare_bandwidth_params; struct link_set_dpms_on_params link_set_dpms_on_params; + struct clk_mgr_set_max_memclk_params clk_mgr_set_max_memclk_params; + struct clk_mgr_update_clocks_params clk_mgr_update_clocks_params; + struct hubbub_program_watermarks_params hubbub_program_watermarks_params; + struct hubbub_program_arbiter_params hubbub_program_arbiter_params; + struct hubbub_program_compbuf_segments_params hubbub_program_compbuf_segments_params; }; enum block_sequence_func { @@ -1209,6 +1244,11 @@ enum block_sequence_func { DISABLE_AUDIO_STREAM, PREPARE_BANDWIDTH, LINK_SET_DPMS_ON, + CLK_MGR_SET_MAX_MEMCLK, + CLK_MGR_UPDATE_CLOCKS, + HUBBUB_PROGRAM_WATERMARKS, + HUBBUB_PROGRAM_ARBITER, + HUBBUB_PROGRAM_COMPBUF_SEGMENTS, /* This must be the last value in this enum, add new ones above */ HWSS_BLOCK_SEQUENCE_FUNC_COUNT }; @@ -1316,8 +1356,14 @@ struct hw_sequencer_funcs { /* Bandwidth Related */ void (*prepare_bandwidth)(struct dc *dc, struct dc_state *context); + void (*prepare_bandwidth_sequence)(struct dc *dc, + struct dc_state *context, + struct block_sequence_state *seq_state); bool (*update_bandwidth)(struct dc *dc, struct dc_state *context); void (*optimize_bandwidth)(struct dc *dc, struct dc_state *context); + void (*optimize_bandwidth_sequence)(struct dc *dc, + struct dc_state *context, + struct block_sequence_state *seq_state); /* Infopacket Related */ void (*set_avmute)(struct pipe_ctx *pipe_ctx, bool enable); @@ -2475,4 +2521,40 @@ void hwss_add_link_set_dpms_on(struct block_sequence_state *seq_state, struct dc_state *state, struct pipe_ctx *pipe_ctx); +/* Clock manager BLS executor functions */ +void hwss_clk_mgr_set_max_memclk(union block_sequence_params *params); +void hwss_clk_mgr_update_clocks(union block_sequence_params *params); + +void hwss_hubbub_program_watermarks(union block_sequence_params *params); + +void hwss_hubbub_program_arbiter(union block_sequence_params *params); + +void hwss_hubbub_program_compbuf_segments(union block_sequence_params *params); + +/* Clock manager BLS add-helper functions */ +void hwss_add_clk_mgr_set_max_memclk(struct block_sequence_state *seq_state, + struct clk_mgr *clk_mgr, + unsigned int memclk_mhz); + +void hwss_add_clk_mgr_update_clocks(struct block_sequence_state *seq_state, + struct clk_mgr *clk_mgr); + +void hwss_add_hubbub_program_watermarks(struct block_sequence_state *seq_state, + struct dc *dc, + struct hubbub *hubbub, + union dcn_watermark_set *watermarks, + unsigned int refclk_mhz, + bool safe_to_lower); + +void hwss_add_hubbub_program_arbiter(struct block_sequence_state *seq_state, + struct dc *dc, + struct hubbub *hubbub, + struct dml2_display_arb_regs *arb_regs, + bool safe_to_lower); + +void hwss_add_hubbub_program_compbuf_segments(struct block_sequence_state *seq_state, + struct hubbub *hubbub, + unsigned int compbuf_size, + bool safe_to_lower); + #endif /* __DC_HW_SEQUENCER_H__ */ diff --git a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h index 63c6c841c681..b4956893ae9a 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h +++ b/drivers/gpu/drm/amd/display/dc/hwss/hw_sequencer_private.h @@ -58,6 +58,7 @@ struct dc_state; struct dc_stream_status; struct dc_writeback_info; struct dchub_init_data; +struct dc_plane_cm; struct dc_static_screen_params; struct resource_pool; struct resource_context; @@ -219,7 +220,7 @@ struct hwseq_private_funcs { struct dc_state *context); void (*populate_mcm_luts)(struct dc *dc, struct pipe_ctx *pipe_ctx, - struct dc_cm2_func_luts mcm_luts, + const struct dc_plane_cm *cm, bool lut_bank_a); void (*perform_3dlut_wa_unlock)(struct pipe_ctx *pipe_ctx); void (*wait_for_pipe_update_if_needed)(struct dc *dc, struct pipe_ctx *pipe_ctx, bool is_surface_update_only); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h index f829ce3f70e5..68dc2d4ba7ca 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h @@ -30,6 +30,7 @@ #include "dc.h" #include "core_types.h" #include "dm_pp_smu.h" +struct utm_qos_model; /* Constants */ #define DDR4_DRAM_WIDTH 64 @@ -311,6 +312,7 @@ struct clk_bw_params { struct wm_table wm_table; struct dummy_pstate_entry dummy_pstate_table[4]; struct clk_limit_table_entry dc_mode_limit; + const struct utm_qos_model *utm_qos_model; }; /* Public interfaces */ @@ -318,6 +320,8 @@ struct clk_states { uint32_t dprefclk_khz; }; +struct block_sequence_state; + struct clk_mgr_funcs { /* * This function should set new clocks based on the input "safe_to_lower". @@ -337,6 +341,8 @@ struct clk_mgr_funcs { void (*exit_low_power_state)(struct clk_mgr *clk_mgr); bool (*is_ips_supported)(struct clk_mgr *clk_mgr); + void (*set_idle_power_optimizations)(struct clk_mgr *clk_mgr, bool enable); + void (*init_clocks)(struct clk_mgr *clk_mgr); void (*dump_clk_registers)(struct clk_state_registers_and_bypass *regs_and_bypass, @@ -405,6 +411,12 @@ struct clk_mgr_funcs { void (*get_requested_memory_qos)( struct clk_mgr *clk_mgr, struct dc_requested_memory_qos *qos); + + void (*build_clock_update_for_bls)(struct clk_mgr *clk_mgr, + struct dc_state *context, bool safe_to_lower, + struct block_sequence_state *seq_state); + + void (*execute_clk_mgr_block_sequence)(struct clk_mgr *clk_mgr); }; struct clk_mgr { diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h index 6db7c8753081..e756719308ab 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dccg.h @@ -348,6 +348,7 @@ struct dccg_funcs { void (*dccg_root_gate_disable_control)(struct dccg *dccg, uint32_t pipe_idx, uint32_t disable_clock_gating); void (*dccg_read_reg_state)(struct dccg *dccg, struct dcn_dccg_reg_state *dccg_reg_state); void (*dccg_enable_global_fgcg)(struct dccg *dccg, bool enable); + bool (*dccg_get_global_fgcg_status)(struct dccg *dccg); }; #endif //__DAL_DCCG_H__ diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h index 1c18898aa475..6d6eda0e7e9d 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/hubp.h @@ -108,7 +108,7 @@ struct hubp_fl_3dlut_config { uint16_t scale; struct dc_plane_address address; enum hubp_3dlut_fl_addressing_mode addr_mode; - enum dc_cm2_gpu_mem_layout layout; + enum dc_cm_lut_swizzle layout; uint8_t protection_bits; enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_y_g; enum hubp_3dlut_fl_crossbar_bit_slice crossbar_bit_slice_cb_b; diff --git a/drivers/gpu/drm/amd/display/dc/inc/link_service.h b/drivers/gpu/drm/amd/display/dc/inc/link_service.h index 23202c2114bb..addeb3e3b25a 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/link_service.h +++ b/drivers/gpu/drm/amd/display/dc/inc/link_service.h @@ -193,6 +193,7 @@ struct link_service { struct aux_payload *payload); bool (*is_in_aux_transaction_mode)(struct ddc_service *ddc); uint32_t (*get_aux_defer_delay)(struct ddc_service *ddc); + uint8_t (*get_ddc_aux_inst)(const struct dc_link *link); /*************************** DP Capability ****************************/ diff --git a/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h b/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h index 7a1ecb8d986f..6d15ccdc7f87 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h +++ b/drivers/gpu/drm/amd/display/dc/inc/reg_helper.h @@ -536,23 +536,4 @@ uint32_t generic_indirect_reg_update_ex_sync(const struct dc_context *ctx, uint8_t shift1, uint32_t mask1, uint32_t field_value1, ...); -/* register offload macros - * - * instead of MMIO to register directly, in some cases we want - * to gather register sequence and execute the register sequence - * from another thread so we optimize time required for lengthy ops - */ - -/* start gathering register sequence */ -#define REG_SEQ_START() \ - reg_sequence_start_gather(CTX) - -/* start execution of register sequence gathered since REG_SEQ_START */ -#define REG_SEQ_SUBMIT() \ - reg_sequence_start_execute(CTX) - -/* wait for the last REG_SEQ_SUBMIT to finish */ -#define REG_SEQ_WAIT_DONE() \ - reg_sequence_wait_done(CTX) - #endif /* DRIVERS_GPU_DRM_AMD_DC_DEV_DC_INC_REG_HELPER_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h index 6a97a3e28bd2..5dcb9f8f4daf 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h +++ b/drivers/gpu/drm/amd/display/dc/inc/soc_and_ip_translator.h @@ -8,26 +8,12 @@ #include "dc.h" #include "dml_top_soc_parameter_types.h" -/* Forward declarations — callers that dereference these structs must include - * the full UTM model headers themselves. */ -struct utm_qos_model; -struct utm_qos_model_dchub_v2; - struct soc_and_ip_translator_funcs { void (*get_soc_bb)( struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config); void (*get_ip_caps)(struct dml2_ip_capabilities *dml_ip_caps); - /** - * get_utm_qos_model - Return the static UTM QoS model for this DCN - * generation. Caller provides storage for @qos_model and @dchub. - * @qos_model: output — populated with SoC bounding box and SOP table - * @dchub: output — populated with DCHUB client extension data - */ - void (*get_utm_qos_model)( - struct utm_qos_model *qos_model, - struct utm_qos_model_dchub_v2 *dchub); }; struct soc_and_ip_translator { diff --git a/drivers/gpu/drm/amd/display/dc/link/link_detection.c b/drivers/gpu/drm/amd/display/dc/link/link_detection.c index a3212fd151d1..281a7c5acaca 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_detection.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_detection.c @@ -933,7 +933,7 @@ static bool should_verify_link_capability_destructively(struct dc_link *link, destrictive = true; if (is_hdmi_frl_in_use(link)) { destrictive = false; - } else if (link->dc->config.skip_frl_pretraining) { + } else if (link->local_sink->edid_caps.panel_patch.skip_frl_pre_training) { for (i = 0; i < MAX_PIPES; i++) { if (pipes[i].stream != NULL && pipes[i].stream->link == link) { @@ -1164,8 +1164,11 @@ static bool detect_link_and_local_sink(struct dc_link *link, link->link_enc->features.flags.bits.DP_IS_USB_C == 1) { /* if alt mode times out, return false */ - if (!wait_for_entering_dp_alt_mode(link)) + if (!wait_for_entering_dp_alt_mode(link)) { + if (prev_sink) + dc_sink_release(prev_sink); return false; + } } if (!detect_dp(link, &sink_caps, reason)) { diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index b6262e43ca02..67ce8d95bbd6 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -143,6 +143,7 @@ static void construct_link_service_ddc(struct link_service *link_srv) link_aux_transfer_with_retries_no_mutex; link_srv->is_in_aux_transaction_mode = link_is_in_aux_transaction_mode; link_srv->get_aux_defer_delay = link_get_aux_defer_delay; + link_srv->get_ddc_aux_inst = link_get_ddc_aux_inst; } /* link dp capability implements dp specific link capability retrieval sequence. @@ -441,7 +442,7 @@ static enum channel_id get_ddc_line(struct dc_link *link) channel = CHANNEL_ID_UNKNOWN; - if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) { + if (link->force_to_use_aux) { channel = link->aux_hw_inst + 1; } else { ddc = get_ddc_pin(link->ddc); @@ -576,6 +577,8 @@ static bool construct_phy(struct dc_link *link, link->is_internal_display = (disp_connect_caps_info.INTERNAL_DISPLAY != 0); DC_LOG_DC("BIOS object table - is_internal_display: %d", link->is_internal_display); link->no_ddc_pin = disp_connect_caps_info.NO_DDC_PIN != 0; + link->force_to_use_aux = link->dc->config.dp_connector_no_native_i2c + && link->no_ddc_pin; } if (link->link_id.type != OBJECT_TYPE_CONNECTOR) { @@ -598,7 +601,7 @@ static bool construct_phy(struct dc_link *link, goto ddc_create_fail; } - if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) { + if (link->force_to_use_aux) { link->ddc_hw_inst = link->aux_hw_inst; } else { /* Embedded display connectors such as LVDS may not have DDC. */ diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c index ead71f6d116d..f9d5a2441e38 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.c @@ -120,8 +120,7 @@ static void ddc_service_construct( ddc_service->link = init_data->link; ddc_service->ctx = init_data->ctx; - if (ddc_service->link && ddc_service->ctx->dc->config.dp_connector_no_native_i2c && - ddc_service->link->no_ddc_pin) { + if (ddc_service->link && ddc_service->link->force_to_use_aux) { // Obtain aux instance info from i2c_info without GPIO DDC pin info if (dcb->funcs->get_connector_aux_info(dcb, init_data->id, &i2c_info) == BP_RESULT_OK) ddc_service->link->aux_hw_inst = (uint8_t)i2c_info.i2c_line; @@ -252,6 +251,21 @@ static uint32_t defer_delay_converter_wa( #define DP_TRANSLATOR_DELAY 5 +/** + * link_get_ddc_aux_inst - Return the AUX/DDC hardware instance for a link. + * @link: the link to query + * + * Return: aux_hw_inst when I2C is forced over AUX, otherwise the DDC pin + * channel index. + */ +uint8_t link_get_ddc_aux_inst(const struct dc_link *link) +{ + if (link->force_to_use_aux) + return link->aux_hw_inst; + ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); + return (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; +} + uint32_t link_get_aux_defer_delay(struct ddc_service *ddc) { uint32_t defer_delay = 0; @@ -526,7 +540,7 @@ bool try_to_configure_aux_timeout(struct ddc_service *ddc, if (ddc->link->ep_type != DISPLAY_ENDPOINT_PHY) return true; - if (ddc->ctx->dc->config.dp_connector_no_native_i2c && ddc->link->no_ddc_pin) { + if (ddc->link->force_to_use_aux) { if (ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst]->funcs->configure_timeout) { ddc->ctx->dc->res_pool->engines[ddc->link->aux_hw_inst]->funcs->configure_timeout(ddc, timeout); result = true; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h index f2a80e12494b..fdd8a3dce97f 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_ddc.h @@ -46,6 +46,8 @@ void set_ddc_transaction_type( struct ddc_service *ddc, enum ddc_transaction_type type); +uint8_t link_get_ddc_aux_inst(const struct dc_link *link); + uint32_t link_get_aux_defer_delay(struct ddc_service *ddc); bool link_is_in_aux_transaction_mode(struct ddc_service *ddc); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c index 47abb4066709..d2329714408a 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_capability.c @@ -181,6 +181,12 @@ uint32_t link_bw_kbps_from_raw_frl_link_rate_data(uint8_t bw) return 40000000; case 0b110: return 48000000; + case 0b111: + return 64000000; + case 0b1000: + return 80000000; + case 0b1001: + return 96000000; } return 0; @@ -2242,10 +2248,14 @@ void detect_edp_sink_caps(struct dc_link *link) /* * ALPM is only valid for eDP v1.4 or higher. */ - if (link->dpcd_caps.dpcd_rev.raw >= DP_EDP_14) + if (link->dpcd_caps.dpcd_rev.raw >= DP_EDP_14) { core_link_read_dpcd(link, DP_RECEIVER_ALPM_CAP, &link->dpcd_caps.alpm_caps.raw, sizeof(link->dpcd_caps.alpm_caps.raw)); + core_link_read_dpcd(link, DP_SINK_PSR_ACTIVE_VTOTAL_CONTROL_CAP, + &link->dpcd_caps.psr_info.psr_active_vtotal_control_cap, + sizeof(link->dpcd_caps.psr_info.psr_active_vtotal_control_cap)); + } /* * Read REPLAY info @@ -2573,7 +2583,7 @@ bool dp_is_sink_present(struct dc_link *link) /* We can't perform the step below for ASICs with no Native * I2C signaling support on DP connectors, so skip it. */ - if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) + if (link->force_to_use_aux) return present; ddc = get_ddc_pin(link->ddc); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c index d87f87a02d63..0d4f88ff844d 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_panel_replay.c @@ -25,6 +25,7 @@ #include "link_dp_panel_replay.h" #include "link_edp_panel_control.h" +#include "link_ddc.h" #include "link_dpcd.h" #include "dm_helpers.h" #include "dc/dc_dmub_srv.h" @@ -119,7 +120,7 @@ static bool dp_setup_panel_replay(struct dc_link *link, const struct dc_stream_s if (!dp_pr_get_panel_inst(dc, link, &panel_inst)) return false; - replay_context.aux_inst = link->ddc->ddc_pin->hw_info.ddc_channel; + replay_context.aux_inst = (enum channel_id) link_get_ddc_aux_inst(link); replay_context.digbe_inst = link->link_enc->transmitter; replay_context.digfe_inst = link->link_enc->preferred_engine; diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c index 80a372ceaa51..baf57692bbb5 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c @@ -29,6 +29,7 @@ */ #include "link_edp_panel_control.h" +#include "link_ddc.h" #include "link_dpcd.h" #include "link_dp_capability.h" #include "dm_helpers.h" @@ -788,10 +789,7 @@ bool edp_setup_psr(struct dc_link *link, } } - if (dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) - psr_context->channel = (enum channel_id)link->aux_hw_inst; - else - psr_context->channel = link->ddc->ddc_pin->hw_info.ddc_channel; + psr_context->channel = link_get_ddc_aux_inst(link); psr_context->transmitterId = link->link_enc->transmitter; psr_context->engineId = link->link_enc->preferred_engine; @@ -1024,7 +1022,7 @@ bool edp_setup_freesync_replay(struct dc_link *link, const struct dc_stream_stat if (!dp_pr_get_panel_inst(dc, link, &panel_inst)) return false; - replay_context.aux_inst = link->ddc->ddc_pin->hw_info.ddc_channel; + replay_context.aux_inst = link_get_ddc_aux_inst(link); replay_context.digbe_inst = link->link_enc->transmitter; replay_context.digfe_inst = link->link_enc->preferred_engine; diff --git a/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c b/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c index fa600593f4c1..0e09d073ab29 100644 --- a/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c +++ b/drivers/gpu/drm/amd/display/dc/mpc/dcn20/dcn20_mpc.c @@ -380,7 +380,6 @@ static void mpc20_program_ogam_pwl( struct dcn20_mpc *mpc20 = TO_DCN20_MPC(mpc); PERF_TRACE(); - REG_SEQ_START(); for (i = 0 ; i < num; i++) { REG_SET(MPCC_OGAM_LUT_DATA[mpcc_id], 0, MPCC_OGAM_LUT_DATA, rgb[i].red_reg); @@ -395,9 +394,6 @@ static void mpc20_program_ogam_pwl( MPCC_OGAM_LUT_DATA, rgb[i].delta_blue_reg); } - REG_SEQ_SUBMIT(); - PERF_TRACE(); - REG_SEQ_WAIT_DONE(); PERF_TRACE(); } diff --git a/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c b/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c index 83730bbe26a8..881b8da656b2 100644 --- a/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c +++ b/drivers/gpu/drm/amd/display/dc/opp/dcn20/dcn20_opp.c @@ -89,6 +89,24 @@ void opp2_set_disp_pattern_generator( break; } + if (opp->ctx->dc->debug.disable_dynamic_expansion_for_test_pattern) { + switch (test_pattern) { + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES: + case CONTROLLER_DP_TEST_PATTERN_COLORSQUARES_CEA: + case CONTROLLER_DP_TEST_PATTERN_VERTICALBARS: + case CONTROLLER_DP_TEST_PATTERN_HORIZONTALBARS: + case CONTROLLER_DP_TEST_PATTERN_COLORRAMP: + if (color_depth == COLOR_DEPTH_121212) + REG_UPDATE(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, 0); + break; + case CONTROLLER_DP_TEST_PATTERN_VIDEOMODE: + REG_UPDATE(FMT_DYNAMIC_EXP_CNTL, FMT_DYNAMIC_EXP_EN, 1); + break; + default: + break; + } + } + /* set DPG dimentions */ REG_SET_2(DPG_DIMENSIONS, 0, DPG_ACTIVE_WIDTH, width, @@ -149,6 +167,9 @@ void opp2_set_disp_pattern_generator( case TEST_PATTERN_COLOR_FORMAT_BPC_10: dst_bpc = 10; break; + case TEST_PATTERN_COLOR_FORMAT_BPC_12: + dst_bpc = 12; + break; default: dst_bpc = 8; break; @@ -192,22 +213,25 @@ void opp2_set_disp_pattern_generator( case CONTROLLER_DP_TEST_PATTERN_COLORRAMP: { - mode = (bit_depth == - TEST_PATTERN_COLOR_FORMAT_BPC_10 ? - TEST_PATTERN_MODE_DUALRAMP_RGB : - TEST_PATTERN_MODE_SINGLERAMP_RGB); - switch (bit_depth) { case TEST_PATTERN_COLOR_FORMAT_BPC_6: + mode = TEST_PATTERN_MODE_SINGLERAMP_RGB; dst_bpc = 6; break; case TEST_PATTERN_COLOR_FORMAT_BPC_8: + mode = TEST_PATTERN_MODE_SINGLERAMP_RGB; dst_bpc = 8; break; case TEST_PATTERN_COLOR_FORMAT_BPC_10: + mode = TEST_PATTERN_MODE_DUALRAMP_RGB; dst_bpc = 10; break; + case TEST_PATTERN_COLOR_FORMAT_BPC_12: + mode = TEST_PATTERN_MODE_DUALRAMP_RGB; + dst_bpc = 12; + break; default: + mode = TEST_PATTERN_MODE_SINGLERAMP_RGB; dst_bpc = 8; break; } @@ -244,9 +268,20 @@ void opp2_set_disp_pattern_generator( case TEST_PATTERN_COLOR_FORMAT_BPC_10: { REG_SET_3(DPG_RAMP_CONTROL, 0, - DPG_RAMP0_OFFSET, 384 << 6, - DPG_INC0, inc_base, - DPG_INC1, inc_base + 2); + DPG_RAMP0_OFFSET, 384 << inc_base, // 384 start point + DPG_INC0, inc_base, // step size of 1 + DPG_INC1, inc_base + 2); // step size of 4 (1 << 2) + REG_UPDATE_2(DPG_CONTROL, + DPG_VRES, 5, + DPG_HRES, 8); + } + break; + case TEST_PATTERN_COLOR_FORMAT_BPC_12: + { + REG_SET_3(DPG_RAMP_CONTROL, 0, + DPG_RAMP0_OFFSET, 1920 << inc_base, // 1920 start point + DPG_INC0, inc_base, // step size of 1 + DPG_INC1, inc_base + 4); // step size of 16 (1 << 4) REG_UPDATE_2(DPG_CONTROL, DPG_VRES, 5, DPG_HRES, 8); diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c index e6426ccee2d8..cf8e22289d6a 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn10/dcn10_optc.c @@ -539,16 +539,11 @@ static bool optc1_enable_crtc(struct timing_generator *optc) REG_UPDATE(CONTROL, VTG0_ENABLE, 1); - REG_SEQ_START(); - /* Enable CRTC */ REG_UPDATE_2(OTG_CONTROL, OTG_DISABLE_POINT_CNTL, 3, OTG_MASTER_EN, 1); - REG_SEQ_SUBMIT(); - REG_SEQ_WAIT_DONE(); - return true; } diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c index c558b1d633f3..73cc8a713556 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn20/dcn20_optc.c @@ -63,16 +63,11 @@ bool optc2_enable_crtc(struct timing_generator *optc) REG_UPDATE(CONTROL, VTG0_ENABLE, 1); - REG_SEQ_START(); - /* Enable CRTC */ REG_UPDATE_2(OTG_CONTROL, OTG_DISABLE_POINT_CNTL, 3, OTG_MASTER_EN, 1); - REG_SEQ_SUBMIT(); - REG_SEQ_WAIT_DONE(); - return true; } diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c index 98aaa22ce81c..3ace83e1b50f 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn31/dcn31_optc.c @@ -105,16 +105,11 @@ static bool optc31_enable_crtc(struct timing_generator *optc) REG_UPDATE(CONTROL, VTG0_ENABLE, 1); - REG_SEQ_START(); - /* Enable CRTC */ REG_UPDATE_2(OTG_CONTROL, OTG_DISABLE_POINT_CNTL, 2, OTG_MASTER_EN, 1); - REG_SEQ_SUBMIT(); - REG_SEQ_WAIT_DONE(); - return true; } diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c index a7cf34937b2f..7250478a5092 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn314/dcn314_optc.c @@ -115,16 +115,11 @@ static bool optc314_enable_crtc(struct timing_generator *optc) REG_UPDATE(CONTROL, VTG0_ENABLE, 1); - REG_SEQ_START(); - /* Enable CRTC */ REG_UPDATE_2(OTG_CONTROL, OTG_DISABLE_POINT_CNTL, 2, OTG_MASTER_EN, 1); - REG_SEQ_SUBMIT(); - REG_SEQ_WAIT_DONE(); - return true; } diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c index 07895d5f4dfa..f9e05efcad98 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn32/dcn32_optc.c @@ -155,16 +155,11 @@ static bool optc32_enable_crtc(struct timing_generator *optc) REG_UPDATE(CONTROL, VTG0_ENABLE, 1); - REG_SEQ_START(); - /* Enable CRTC */ REG_UPDATE_2(OTG_CONTROL, OTG_DISABLE_POINT_CNTL, 2, OTG_MASTER_EN, 1); - REG_SEQ_SUBMIT(); - REG_SEQ_WAIT_DONE(); - return true; } diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c index 62f45c156c32..9b7f9d5bbfb3 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn35/dcn35_optc.c @@ -122,16 +122,11 @@ static bool optc35_enable_crtc(struct timing_generator *optc) REG_UPDATE(CONTROL, VTG0_ENABLE, 1); - REG_SEQ_START(); - /* Enable CRTC */ REG_UPDATE_2(OTG_CONTROL, OTG_DISABLE_POINT_CNTL, 2, OTG_MASTER_EN, 1); - REG_SEQ_SUBMIT(); - REG_SEQ_WAIT_DONE(); - return true; } diff --git a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c index a6d76f451cf8..5fcdd74eb4a0 100644 --- a/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c +++ b/drivers/gpu/drm/amd/display/dc/optc/dcn401/dcn401_optc.c @@ -189,16 +189,11 @@ bool optc401_enable_crtc(struct timing_generator *optc) REG_UPDATE(CONTROL, VTG0_ENABLE, 1); - REG_SEQ_START(); - /* Enable CRTC */ REG_UPDATE_2(OTG_CONTROL, OTG_DISABLE_POINT_CNTL, 2, OTG_MASTER_EN, 1); - REG_SEQ_SUBMIT(); - REG_SEQ_WAIT_DONE(); - return true; } diff --git a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c index 729c2b653161..78b33b2dbae8 100644 --- a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c +++ b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.c @@ -22,6 +22,60 @@ #define DC_LOGGER \ pg_cntl->ctx->logger +/* + * ONO PG Workoaround: Saved FGCG repeaters states captured before powering up an ONO + * domain so it can be restored once the domain is powered up. + */ +struct dcn42_global_fgcg_rep_state { + uint32_t dmu_rep_fgcg; + uint32_t dccg_global_ono_rep_fgcg; + uint32_t az_rep_fgcg; +}; + +/* Save and disable FGCG repeaters before powering up the ONO domain. */ +static void pg_cntl42_save_and_disable_global_fgcg_rep(struct pg_cntl *pg_cntl, + struct dcn42_global_fgcg_rep_state *state) +{ + struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl); + + REG_GET(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, &state->dmu_rep_fgcg); + if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_get_global_fgcg_status) + state->dccg_global_ono_rep_fgcg = pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_get_global_fgcg_status(pg_cntl->ctx->dc->res_pool->dccg); + REG_GET(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, &state->az_rep_fgcg); + + REG_UPDATE(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, 1); + if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) + pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false); + REG_UPDATE(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, 1); +} + +/* Restore FGCG repeaters after the ONO domains are powered up. */ +static void pg_cntl42_restore_global_fgcg_rep(struct pg_cntl *pg_cntl, + struct dcn42_global_fgcg_rep_state *state) +{ + struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl); + + REG_UPDATE(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, state->dmu_rep_fgcg); + if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) + pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, state->dccg_global_ono_rep_fgcg); + REG_UPDATE(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, state->az_rep_fgcg); +} + +static bool should_skip_pg_control(bool dc_in_idle_opt, bool power_on, bool block_enabled) +{ + if (dc_in_idle_opt) + return true; + + if (power_on && block_enabled) + return true; + + if (!power_on && !block_enabled) + return true; + + return false; +} + + static bool pg_cntl42_dsc_pg_status(struct pg_cntl *pg_cntl, unsigned int dsc_inst) { struct dcn_pg_cntl *pg_cntl_dcn = TO_DCN_PG_CNTL(pg_cntl); @@ -54,37 +108,23 @@ void pg_cntl42_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo uint32_t power_gate = power_on ? 0 : 1; uint32_t pwr_status = power_on ? 0 : 2; uint32_t org_ip_request_cntl = 0; - bool block_enabled; - - /*need to enable dscclk regardless DSC_PG*/ - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->enable_dsc && power_on) - pg_cntl->ctx->dc->res_pool->dccg->funcs->enable_dsc( - pg_cntl->ctx->dc->res_pool->dccg, dsc_inst); + struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0}; + bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg || pg_cntl->ctx->dc->debug.disable_dsc_power_gate; - bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg || - pg_cntl->ctx->dc->debug.disable_dsc_power_gate || - pg_cntl->ctx->dc->idle_optimizations_allowed; - - if (skip_pg && !power_on) + if (block_pg_disabled && !power_on) return; - block_enabled = pg_cntl42_dsc_pg_status(pg_cntl, dsc_inst); - if (power_on) { - if (block_enabled) - return; - } else { - if (!block_enabled) - return; - } + bool block_enabled = pg_cntl42_dsc_pg_status(pg_cntl, dsc_inst); + if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled)) + return; REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl); if (org_ip_request_cntl == 0) REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - if (power_on) { - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) - pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false); - } + if (power_on) + pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state); + switch (dsc_inst) { case 0: /* DSC0 */ REG_UPDATE(DOMAIN16_PG_CONFIG, @@ -123,19 +163,11 @@ void pg_cntl42_dsc_pg_control(struct pg_cntl *pg_cntl, unsigned int dsc_inst, bo break; } - if (power_on) { - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) - pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, true); - } + if (power_on) + pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state); if (dsc_inst < MAX_PIPES) pg_cntl->pg_pipe_res_enable[PG_DSC][dsc_inst] = power_on; - - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->disable_dsc && !power_on) { - /*this is to disable dscclk*/ - pg_cntl->ctx->dc->res_pool->dccg->funcs->disable_dsc( - pg_cntl->ctx->dc->res_pool->dccg, dsc_inst); - } } static bool pg_cntl42_hubp_dpp_pg_status(struct pg_cntl *pg_cntl, unsigned int hubp_dpp_inst) @@ -174,32 +206,24 @@ void pg_cntl42_hubp_dpp_pg_control(struct pg_cntl *pg_cntl, unsigned int hubp_dp uint32_t power_gate = power_on ? 0 : 1; uint32_t pwr_status = power_on ? 0 : 2; uint32_t org_ip_request_cntl; - bool block_enabled; - bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg || - pg_cntl->ctx->dc->debug.disable_hubp_power_gate || - pg_cntl->ctx->dc->debug.disable_dpp_power_gate || - pg_cntl->ctx->dc->idle_optimizations_allowed; + struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0}; + bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg || + pg_cntl->ctx->dc->debug.disable_hubp_power_gate || + pg_cntl->ctx->dc->debug.disable_dpp_power_gate; - if (skip_pg && !power_on) + if (block_pg_disabled && !power_on) return; - block_enabled = pg_cntl42_hubp_dpp_pg_status(pg_cntl, hubp_dpp_inst); - if (power_on) { - if (block_enabled) - return; - } else { - if (!block_enabled) - return; - } + bool block_enabled = pg_cntl42_hubp_dpp_pg_status(pg_cntl, hubp_dpp_inst); + if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled)) + return; REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl); if (org_ip_request_cntl == 0) REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - if (power_on) { - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) - pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false); - } + if (power_on) + pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state); switch (hubp_dpp_inst) { case 0: @@ -227,10 +251,9 @@ void pg_cntl42_hubp_dpp_pg_control(struct pg_cntl *pg_cntl, unsigned int hubp_dp break; } - if (power_on) { - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) - pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, true); - } + if (power_on) + pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state); + DC_LOG_DEBUG("HUBP DPP instance %d, power %s", hubp_dpp_inst, power_on ? "ON" : "OFF"); @@ -258,22 +281,18 @@ void pg_cntl42_hpo_pg_control(struct pg_cntl *pg_cntl, bool power_on) uint32_t pwr_status = power_on ? 0 : 2; uint32_t org_ip_request_cntl; uint32_t power_forceon; - bool block_enabled; + struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0}; - bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg || - pg_cntl->ctx->dc->debug.disable_hpo_power_gate || - pg_cntl->ctx->dc->idle_optimizations_allowed; + bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg || + pg_cntl->ctx->dc->debug.disable_hpo_power_gate; - if (skip_pg && !power_on) + if (block_pg_disabled && !power_on) + return; + + bool block_enabled = pg_cntl42_hpo_pg_status(pg_cntl); + + if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled)) return; - block_enabled = pg_cntl42_hpo_pg_status(pg_cntl); - if (power_on) { - if (block_enabled) - return; - } else { - if (!block_enabled) - return; - } REG_GET(DOMAIN25_PG_CONFIG, DOMAIN_POWER_FORCEON, &power_forceon); if (power_forceon) @@ -282,17 +301,15 @@ void pg_cntl42_hpo_pg_control(struct pg_cntl *pg_cntl, bool power_on) REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl); if (org_ip_request_cntl == 0) REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - if (power_on) { - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) - pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false); - } + if (power_on) + pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state); + REG_UPDATE(DOMAIN25_PG_CONFIG, DOMAIN_POWER_GATE, power_gate); REG_WAIT(DOMAIN25_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000); - if (power_on) { - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) - pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, true); - } + if (power_on) + pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state); + pg_cntl->pg_res_enable[PG_HPO] = power_on; } @@ -314,23 +331,17 @@ void pg_cntl42_io_clk_pg_control(struct pg_cntl *pg_cntl, bool power_on) uint32_t pwr_status = power_on ? 0 : 2; uint32_t org_ip_request_cntl; uint32_t power_forceon; - bool block_enabled; - bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg || - pg_cntl->ctx->dc->idle_optimizations_allowed || + bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg || pg_cntl->ctx->dc->debug.disable_io_clk_power_gate; - if (skip_pg && !power_on) + if (block_pg_disabled && !power_on) return; - block_enabled = pg_cntl42_io_clk_status(pg_cntl); - if (power_on) { - if (block_enabled) - return; - } else { - if (!block_enabled) - return; - } + bool block_enabled = pg_cntl42_io_clk_status(pg_cntl); + + if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled)) + return; REG_GET(DOMAIN22_PG_CONFIG, DOMAIN_POWER_FORCEON, &power_forceon); if (power_forceon) @@ -412,24 +423,16 @@ void pg_cntl42_mem_pg_control(struct pg_cntl *pg_cntl, bool power_on) uint32_t pwr_status = power_on ? 0 : 2; uint32_t org_ip_request_cntl; uint32_t power_forceon; - bool block_enabled; - bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg || - pg_cntl->ctx->dc->idle_optimizations_allowed || + bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg || pg_cntl->ctx->dc->debug.disable_mem_power_gate; - if (skip_pg && !power_on) + if (block_pg_disabled && !power_on) return; - block_enabled = pg_cntl42_mem_status(pg_cntl); - if (power_on) { - if (block_enabled) - return; - } else { - if (!block_enabled) - return; - } - + bool block_enabled = pg_cntl42_mem_status(pg_cntl); + if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled)) + return; REG_GET(DOMAIN23_PG_CONFIG, DOMAIN_POWER_FORCEON, &power_forceon); if (power_forceon) return; @@ -466,38 +469,31 @@ void pg_cntl42_dio_pg_control(struct pg_cntl *pg_cntl, bool power_on) uint32_t power_gate = power_on ? 0 : 1; uint32_t pwr_status = power_on ? 0 : 2; uint32_t org_ip_request_cntl; - bool block_enabled; + struct dcn42_global_fgcg_rep_state fgcg_rep_state = {0}; - bool skip_pg = pg_cntl->ctx->dc->debug.ignore_pg || - pg_cntl->ctx->dc->idle_optimizations_allowed || + bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg || pg_cntl->ctx->dc->debug.disable_dio_power_gate; - if (skip_pg && !power_on) + + if (block_pg_disabled && !power_on) return; - block_enabled = pg_cntl42_dio_pg_status(pg_cntl); - if (power_on) { - if (block_enabled) - return; - } else { - if (!block_enabled) - return; - } + bool block_enabled = pg_cntl42_dio_pg_status(pg_cntl); + if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled)) + return; REG_GET(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, &org_ip_request_cntl); if (org_ip_request_cntl == 0) REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 1); - if (power_on) { - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) - pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, false); - } + if (power_on) + pg_cntl42_save_and_disable_global_fgcg_rep(pg_cntl, &fgcg_rep_state); + /* DIO */ REG_UPDATE(DOMAIN26_PG_CONFIG, DOMAIN_POWER_GATE, power_gate); REG_WAIT(DOMAIN26_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, pwr_status, 1, 1000); - if (power_on) { - if (pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg) - pg_cntl->ctx->dc->res_pool->dccg->funcs->dccg_enable_global_fgcg(pg_cntl->ctx->dc->res_pool->dccg, true); - } + if (power_on) + pg_cntl42_restore_global_fgcg_rep(pg_cntl, &fgcg_rep_state); + pg_cntl->pg_res_enable[PG_DIO] = power_on; } @@ -509,23 +505,19 @@ void pg_cntl42_plane_otg_pg_control(struct pg_cntl *pg_cntl, bool power_on) uint32_t pwr_status = power_on ? 0 : 2; uint32_t org_ip_request_cntl; unsigned int i; - bool block_enabled; bool all_mpcc_disabled = true, all_opp_disabled = true; bool all_optc_disabled = true, all_stream_disabled = true; - if (pg_cntl->ctx->dc->debug.ignore_pg || - pg_cntl->ctx->dc->debug.disable_optc_power_gate || - pg_cntl->ctx->dc->idle_optimizations_allowed) + bool block_pg_disabled = pg_cntl->ctx->dc->debug.ignore_pg || + pg_cntl->ctx->dc->debug.disable_optc_power_gate; + + if (block_pg_disabled && !power_on) return; - block_enabled = pg_cntl42_plane_otg_status(pg_cntl); - if (power_on) { - if (block_enabled) - return; - } else { - if (!block_enabled) - return; - } + bool block_enabled = pg_cntl42_plane_otg_status(pg_cntl); + + if (should_skip_pg_control(pg_cntl->ctx->dc->idle_optimizations_allowed, power_on, block_enabled)) + return; for (i = 0; i < pg_cntl->ctx->dc->res_pool->pipe_count; i++) { struct pipe_ctx *pipe_ctx = &pg_cntl->ctx->dc->current_state->res_ctx.pipe_ctx[i]; diff --git a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.h b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.h index 7e8f4f03ae0e..813fa5c81172 100644 --- a/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.h +++ b/drivers/gpu/drm/amd/display/dc/pg/dcn42/dcn42_pg_cntl.h @@ -34,7 +34,9 @@ SR(DOMAIN24_PG_STATUS), \ SR(DOMAIN25_PG_STATUS), \ SR(DOMAIN26_PG_STATUS), \ - SR(DC_IP_REQUEST_CNTL) + SR(DC_IP_REQUEST_CNTL), \ + SR(DMU_CLK_CNTL), \ + SR(AZ_CLOCK_CNTL) #define PG_CNTL_REG_LIST_DCN42B()\ SR(DOMAIN0_PG_CONFIG), \ @@ -63,7 +65,9 @@ SR(DOMAIN24_PG_STATUS), \ SR(DOMAIN25_PG_STATUS), \ SR(DOMAIN26_PG_STATUS), \ - SR(DC_IP_REQUEST_CNTL) + SR(DC_IP_REQUEST_CNTL), \ + SR(DMU_CLK_CNTL), \ + SR(AZ_CLOCK_CNTL) #define PG_CNTL_SF(reg_name, field_name, post_fix)\ .field_name = reg_name ## __ ## field_name ## post_fix @@ -121,7 +125,9 @@ PG_CNTL_SF(DOMAIN25_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, mask_sh), \ PG_CNTL_SF(DOMAIN26_PG_STATUS, DOMAIN_DESIRED_PWR_STATE, mask_sh), \ PG_CNTL_SF(DOMAIN26_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, mask_sh), \ - PG_CNTL_SF(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh) + PG_CNTL_SF(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh), \ + PG_CNTL_SF(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, mask_sh), \ + PG_CNTL_SF(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, mask_sh) /* Not in DCN42B: * PG_CNTL_SF(DOMAIN19_PG_CONFIG, DOMAIN_POWER_FORCEON, mask_sh), @@ -178,7 +184,9 @@ PG_CNTL_SF(DOMAIN25_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, mask_sh), \ PG_CNTL_SF(DOMAIN26_PG_STATUS, DOMAIN_DESIRED_PWR_STATE, mask_sh), \ PG_CNTL_SF(DOMAIN26_PG_STATUS, DOMAIN_PGFSM_PWR_STATUS, mask_sh), \ - PG_CNTL_SF(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh) + PG_CNTL_SF(DC_IP_REQUEST_CNTL, IP_REQUEST_EN, mask_sh), \ + PG_CNTL_SF(DMU_CLK_CNTL, LONO_FGCG_REP_DIS, mask_sh), \ + PG_CNTL_SF(AZ_CLOCK_CNTL, AZ_GLOBAL_FGCG_REP_DIS, mask_sh) struct pg_cntl_shift { uint8_t IP_REQUEST_EN; @@ -186,6 +194,8 @@ struct pg_cntl_shift { uint8_t DOMAIN_POWER_GATE; uint8_t DOMAIN_DESIRED_PWR_STATE; uint8_t DOMAIN_PGFSM_PWR_STATUS; + uint8_t LONO_FGCG_REP_DIS; + uint8_t AZ_GLOBAL_FGCG_REP_DIS; }; struct pg_cntl_mask { uint32_t IP_REQUEST_EN; @@ -193,6 +203,8 @@ struct pg_cntl_mask { uint32_t DOMAIN_POWER_GATE; uint32_t DOMAIN_DESIRED_PWR_STATE; uint32_t DOMAIN_PGFSM_PWR_STATUS; + uint32_t LONO_FGCG_REP_DIS; + uint32_t AZ_GLOBAL_FGCG_REP_DIS; }; struct pg_cntl_registers { @@ -224,6 +236,8 @@ struct pg_cntl_registers { uint32_t DOMAIN24_PG_STATUS; uint32_t DOMAIN25_PG_STATUS; uint32_t DOMAIN26_PG_STATUS; + uint32_t DMU_CLK_CNTL; + uint32_t AZ_CLOCK_CNTL; }; struct dcn_pg_cntl { diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c index 01770df63d0e..d11ab57afcdd 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn30/dcn30_resource.c @@ -2479,7 +2479,6 @@ static bool dcn30_resource_construct( dc->caps.post_blend_color_processing = true; dc->caps.force_dp_tps4_for_cp2520 = true; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.extended_aux_timeout_support = true; dc->caps.dmcub_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c index 58add1071bc1..ae8918a4ad3e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn302/dcn302_resource.c @@ -1378,7 +1378,6 @@ static bool dcn302_resource_construct( dc->caps.post_blend_color_processing = true; dc->caps.force_dp_tps4_for_cp2520 = true; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.extended_aux_timeout_support = true; dc->caps.dmcub_support = true; dc->caps.max_v_total = (1 << 15) - 1; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c index 6cb297cd08fd..75e6f4e46f60 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn303/dcn303_resource.c @@ -1322,7 +1322,6 @@ static bool dcn303_resource_construct( dc->caps.post_blend_color_processing = true; dc->caps.force_dp_tps4_for_cp2520 = true; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.extended_aux_timeout_support = true; dc->caps.dmcub_support = true; dc->caps.max_v_total = (1 << 15) - 1; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c index 7c6a6872688b..e29efa452c87 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn31/dcn31_resource.c @@ -1189,7 +1189,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal( { struct dcn20_link_encoder *enc20; - if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc) + if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; enc20 = kzalloc_obj(struct dcn20_link_encoder); @@ -1201,6 +1201,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal( ctx, &link_enc_feature, &link_enc_regs[eng_id - ENGINE_ID_DIGA], + &le_shift, + &le_mask, eng_id); return &enc20->enc10.base; @@ -2077,7 +2079,6 @@ static bool dcn31_resource_construct( if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c index d8096f11fb77..f50b3250dcba 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn314/dcn314_resource.c @@ -1246,7 +1246,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal( { struct dcn20_link_encoder *enc20; - if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc) + if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; enc20 = kzalloc_obj(struct dcn20_link_encoder); @@ -1258,6 +1258,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal( ctx, &link_enc_feature, &link_enc_regs[eng_id - ENGINE_ID_DIGA], + &le_shift, + &le_mask, eng_id); return &enc20->enc10.base; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c index 3b2e57c6970f..8297f2f04c16 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn315/dcn315_resource.c @@ -1188,7 +1188,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal( { struct dcn20_link_encoder *enc20; - if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc) + if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; enc20 = kzalloc_obj(struct dcn20_link_encoder); @@ -1200,6 +1200,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal( ctx, &link_enc_feature, &link_enc_regs[eng_id - ENGINE_ID_DIGA], + &le_shift, + &le_mask, eng_id); return &enc20->enc10.base; @@ -2053,7 +2055,6 @@ static bool dcn315_resource_construct( if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c index 924b167bcd74..046566ad1afe 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn316/dcn316_resource.c @@ -1181,7 +1181,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal( { struct dcn20_link_encoder *enc20; - if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc) + if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; enc20 = kzalloc_obj(struct dcn20_link_encoder); @@ -1193,6 +1193,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal( ctx, &link_enc_feature, &link_enc_regs[eng_id - ENGINE_ID_DIGA], + &le_shift, + &le_mask, eng_id); return &enc20->enc10.base; @@ -1927,7 +1929,6 @@ static bool dcn316_resource_construct( if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c index a11110e304fc..004c5690f876 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn32/dcn32_resource.c @@ -2405,7 +2405,6 @@ static bool dcn32_resource_construct( if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c index d1dbcc8ddb71..53fd32249310 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn321/dcn321_resource.c @@ -1897,7 +1897,6 @@ static bool dcn321_resource_construct( dc->caps.post_blend_color_processing = true; dc->caps.force_dp_tps4_for_cp2520 = true; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; dc->caps.edp_dsc_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c index 53596e790eb4..5541b89b1350 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn35/dcn35_resource.c @@ -819,7 +819,6 @@ static const struct dc_debug_options debug_defaults_drv = { .enable_hpo_pg_support = false, .enable_single_display_2to1_odm_policy = true, .disable_idle_power_optimizations = false, - .dmcub_emulation = false, .disable_boot_optimizations = false, .disable_unbounded_requesting = false, .disable_mem_low_power = false, @@ -1189,7 +1188,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal( { struct dcn20_link_encoder *enc20; - if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc) + if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; enc20 = kzalloc_obj(struct dcn20_link_encoder); @@ -1201,6 +1200,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal( ctx, &link_enc_feature, &link_enc_regs[eng_id - ENGINE_ID_DIGA], + &le_shift, + &le_mask, eng_id); return &enc20->enc10.base; @@ -2029,7 +2030,6 @@ static bool dcn35_resource_construct( if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c index 3e2c9cfd555d..053b4380f57e 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn351/dcn351_resource.c @@ -799,7 +799,6 @@ static const struct dc_debug_options debug_defaults_drv = { .enable_hpo_pg_support = false, .enable_single_display_2to1_odm_policy = true, .disable_idle_power_optimizations = false, - .dmcub_emulation = false, .disable_boot_optimizations = false, .disable_unbounded_requesting = false, .disable_mem_low_power = false, @@ -1169,7 +1168,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal( { struct dcn20_link_encoder *enc20; - if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc) + if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; enc20 = kzalloc_obj(struct dcn20_link_encoder); @@ -1181,6 +1180,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal( ctx, &link_enc_feature, &link_enc_regs[eng_id - ENGINE_ID_DIGA], + &le_shift, + &le_mask, eng_id); return &enc20->enc10.base; @@ -2002,7 +2003,6 @@ static bool dcn351_resource_construct( if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c index 9e795130eb89..592000cf9250 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn36/dcn36_resource.c @@ -806,7 +806,6 @@ static const struct dc_debug_options debug_defaults_drv = { .enable_hpo_pg_support = false, .enable_single_display_2to1_odm_policy = true, .disable_idle_power_optimizations = false, - .dmcub_emulation = false, .disable_boot_optimizations = false, .disable_unbounded_requesting = false, .disable_mem_low_power = false, @@ -1176,7 +1175,7 @@ static struct link_encoder *dcn31_link_enc_create_minimal( { struct dcn20_link_encoder *enc20; - if (((unsigned int)eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc) + if (((unsigned int)eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; enc20 = kzalloc_obj(struct dcn20_link_encoder); @@ -1188,6 +1187,8 @@ static struct link_encoder *dcn31_link_enc_create_minimal( ctx, &link_enc_feature, &link_enc_regs[eng_id - ENGINE_ID_DIGA], + &le_shift, + &le_mask, eng_id); return &enc20->enc10.base; @@ -1999,7 +2000,6 @@ static bool dcn36_resource_construct( if (dc->config.forceHBR2CP2520) dc->caps.force_dp_tps4_for_cp2520 = false; dc->caps.hdmi_hpo = true; - dc->config.skip_frl_pretraining = true; dc->caps.dp_hpo = true; dc->caps.dp_hdmi21_pcon_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c index 7de12b16d7ad..7620da96ffc1 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42/dcn42_resource.c @@ -801,6 +801,7 @@ static const struct dc_debug_options debug_defaults_drv = { .replay_skip_crtc_disabled = true, .psr_skip_crtc_disable = true, .force_odm2to1_for_edp_pixclk_mhz = 0, // disable the policy for now + .iommu_mismatch_temp_wka = 0x7, }; static const struct dc_check_config config_defaults = { @@ -1881,7 +1882,7 @@ static struct link_encoder *dcn42_link_enc_create_minimal( { struct dcn20_link_encoder *enc20; - if ((unsigned int)(eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc) + if ((unsigned int)(eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; enc20 = kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL); @@ -1893,6 +1894,8 @@ static struct link_encoder *dcn42_link_enc_create_minimal( ctx, &link_enc_feature, &link_enc_regs[eng_id - ENGINE_ID_DIGA], + &le_shift, + &le_mask, eng_id); return &enc20->enc10.base; diff --git a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c index 527d17f29f3b..60cbaf4f6fdf 100644 --- a/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c +++ b/drivers/gpu/drm/amd/display/dc/resource/dcn42b/dcn42b_resource.c @@ -757,7 +757,7 @@ static const struct dc_debug_options debug_defaults_drv = { .underflow_assert_delay_us = 0xFFFFFFFF, .dwb_fi_phase = -1, // -1 = disable, .dmub_command_table = true, - .pstate_enabled = false, + .pstate_enabled = true, .enable_mem_low_power = { .bits = { .vga = false, @@ -1824,7 +1824,7 @@ static struct link_encoder *dcn42b_link_enc_create_minimal( { struct dcn20_link_encoder *enc20; - if ((unsigned int)(eng_id - ENGINE_ID_DIGA) > ctx->dc->res_pool->res_cap->num_dig_link_enc) + if ((unsigned int)(eng_id - ENGINE_ID_DIGA) >= ctx->dc->res_pool->res_cap->num_dig_link_enc) return NULL; enc20 = kzalloc(sizeof(struct dcn20_link_encoder), GFP_KERNEL); @@ -1836,6 +1836,8 @@ static struct link_encoder *dcn42b_link_enc_create_minimal( ctx, &link_enc_feature, &link_enc_regs[eng_id - ENGINE_ID_DIGA], + &le_shift, + &le_mask, eng_id); return &enc20->enc10.base; diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile index d168fb1eacf7..8a9bb0aef9b7 100644 --- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/Makefile @@ -9,13 +9,16 @@ soc_and_ip_translator_rcflags := $(CC_FLAGS_NO_FPU) CFLAGS_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.o := $(soc_and_ip_translator_ccflags) CFLAGS_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.o := $(soc_and_ip_translator_ccflags) +CFLAGS_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.o := $(soc_and_ip_translator_ccflags) CFLAGS_REMOVE_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.o := $(soc_and_ip_translator_rcflags) CFLAGS_REMOVE_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.o := $(soc_and_ip_translator_rcflags) +CFLAGS_REMOVE_$(AMDDALPATH)/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.o := $(soc_and_ip_translator_rcflags) soc_and_ip_translator := soc_and_ip_translator.o soc_and_ip_translator += dcn401/dcn401_soc_and_ip_translator.o soc_and_ip_translator += dcn42/dcn42_soc_and_ip_translator.o +soc_and_ip_translator += dcn42b/dcn42b_soc_and_ip_translator.o AMD_DAL_soc_and_ip_translator := $(addprefix $(AMDDALPATH)/dc/soc_and_ip_translator/, $(soc_and_ip_translator)) diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c index ae2c6a2f3f75..c6c1b19b7370 100644 --- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.c @@ -5,22 +5,16 @@ #include "dcn42_soc_and_ip_translator.h" #include "../dcn401/dcn401_soc_and_ip_translator.h" #include "bounding_boxes/dcn42_soc_bb.h" -#include "bounding_boxes/dcn42b_soc_bb.h" /* soc_and_ip_translator component used to get up-to-date values for bounding box. * Bounding box values are stored in several locations and locations can vary with DCN revision. * This component provides an interface to get DCN-specific bounding box values. */ -static void get_default_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc) +static void get_default_soc_bb(struct dml2_soc_bb *soc_bb) { - if (dc->ctx->dce_version == DCN_VERSION_4_2B) { - memcpy(soc_bb, &dml2_socbb_dcn42b, sizeof(struct dml2_soc_bb)); - memcpy(&soc_bb->qos_parameters, &dml_dcn42b_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters)); - } else { - memcpy(soc_bb, &dml2_socbb_dcn42, sizeof(struct dml2_soc_bb)); - memcpy(&soc_bb->qos_parameters, &dml_dcn42_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters)); - } + memcpy(soc_bb, &dml2_socbb_dcn42, sizeof(struct dml2_soc_bb)); + memcpy(&soc_bb->qos_parameters, &dml_dcn42_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters)); } /* @@ -165,7 +159,7 @@ static void dcn42_update_soc_bb_with_values_from_clk_mgr(struct dml2_soc_bb *soc } } -static void apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) +void dcn42_apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) { (void)config; /* Individual modification can be overwritten even if it was obtained by a previous function. @@ -181,9 +175,9 @@ static void apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc void dcn42_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) { //get default soc_bb with static values - get_default_soc_bb(soc_bb, dc); + get_default_soc_bb(soc_bb); //update soc_bb values with more accurate values - apply_soc_bb_updates(soc_bb, dc, config); + dcn42_apply_soc_bb_updates(soc_bb, dc, config); } static void dcn42_get_ip_caps(struct dml2_ip_capabilities *ip_caps) diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h index 1dded5426152..8ac90655f276 100644 --- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h @@ -13,5 +13,6 @@ void dcn42_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator); void dcn42_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config); +void dcn42_apply_soc_bb_updates(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config); #endif /* _DCN42_SOC_AND_IP_TRANSLATOR_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.c new file mode 100644 index 000000000000..50669f458e23 --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#include "../dcn42/dcn42_soc_and_ip_translator.h" +#include "dcn42b_soc_and_ip_translator.h" +#include "../dcn401/dcn401_soc_and_ip_translator.h" +#include "bounding_boxes/dcn42b_soc_bb.h" + +/* soc_and_ip_translator component used to get up-to-date values for bounding box. + * Bounding box values are stored in several locations and locations can vary with DCN revision. + * This component provides an interface to get DCN-specific bounding box values. + */ + +static void get_default_soc_bb(struct dml2_soc_bb *soc_bb) +{ + memcpy(soc_bb, &dml2_socbb_dcn42b, sizeof(struct dml2_soc_bb)); + memcpy(&soc_bb->qos_parameters, &dml_dcn42b_variant_a_soc_qos_params, sizeof(struct dml2_soc_qos_parameters)); +} + +void dcn42b_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config) +{ + //get default soc_bb with static values + get_default_soc_bb(soc_bb); + //update soc_bb values with more accurate values + dcn42_apply_soc_bb_updates(soc_bb, dc, config); +} + +static void dcn42b_get_ip_caps(struct dml2_ip_capabilities *ip_caps) +{ + *ip_caps = dml2_dcn42b_max_ip_caps; +} + +static struct soc_and_ip_translator_funcs dcn42b_translator_funcs = { + .get_soc_bb = dcn42b_get_soc_bb, + .get_ip_caps = dcn42b_get_ip_caps, +}; + +void dcn42b_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator) +{ + soc_and_ip_translator->translator_funcs = &dcn42b_translator_funcs; +} diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.h b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.h new file mode 100644 index 000000000000..0d4ea613431a --- /dev/null +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.h @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +// +// Copyright 2026 Advanced Micro Devices, Inc. + +#ifndef _DCN42B_SOC_AND_IP_TRANSLATOR_H_ +#define _DCN42B_SOC_AND_IP_TRANSLATOR_H_ + +#include "core_types.h" +#include "dc.h" +#include "clk_mgr.h" +#include "dml_top_soc_parameter_types.h" +#include "soc_and_ip_translator.h" + +void dcn42b_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator); +void dcn42b_get_soc_bb(struct dml2_soc_bb *soc_bb, const struct dc *dc, const struct dml2_configuration_options *config); + +#endif /* _DCN42B_SOC_AND_IP_TRANSLATOR_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c index f99afb22d7da..1e3ee25732fa 100644 --- a/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c +++ b/drivers/gpu/drm/amd/display/dc/soc_and_ip_translator/soc_and_ip_translator.c @@ -5,6 +5,7 @@ #include "soc_and_ip_translator.h" #include "soc_and_ip_translator/dcn401/dcn401_soc_and_ip_translator.h" #include "soc_and_ip_translator/dcn42/dcn42_soc_and_ip_translator.h" +#include "soc_and_ip_translator/dcn42b/dcn42b_soc_and_ip_translator.h" static void dc_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc_and_ip_translator, enum dce_version dc_version) @@ -14,9 +15,11 @@ static void dc_construct_soc_and_ip_translator(struct soc_and_ip_translator *soc dcn401_construct_soc_and_ip_translator(soc_and_ip_translator); break; case DCN_VERSION_4_2: - case DCN_VERSION_4_2B: dcn42_construct_soc_and_ip_translator(soc_and_ip_translator); break; + case DCN_VERSION_4_2B: + dcn42b_construct_soc_and_ip_translator(soc_and_ip_translator); + break; default: break; } diff --git a/drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h b/drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h index a6f6132df241..a0e9df382582 100644 --- a/drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h +++ b/drivers/gpu/drm/amd/display/dc/sspl/spl_debug.h @@ -5,7 +5,7 @@ #ifndef SPL_DEBUG_H #define SPL_DEBUG_H -#if defined(CONFIG_HAVE_KGDB) || defined(CONFIG_KGDB) +#ifdef CONFIG_KGDB #define SPL_ASSERT_CRITICAL(expr) do { \ if (WARN_ON(!(expr))) { \ kgdb_breakpoint(); \ @@ -17,7 +17,7 @@ ; \ } \ } while (0) -#endif /* CONFIG_HAVE_KGDB || CONFIG_KGDB */ +#endif /* CONFIG_KGDB */ #if defined(CONFIG_DEBUG_KERNEL_DC) #define SPL_ASSERT(expr) SPL_ASSERT_CRITICAL(expr) diff --git a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h index 6f6a59a23495..e7879bd81f83 100644 --- a/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h +++ b/drivers/gpu/drm/amd/display/dmub/inc/dmub_cmd.h @@ -246,14 +246,14 @@ * OS/FW agnostic memcpy */ #ifndef dmub_memcpy -#define dmub_memcpy(dest, source, bytes) memcpy((dest), (source), (bytes)) +#define dmub_memcpy(dest, source, bytes) ((void)memcpy((dest), (source), (bytes))) #endif /** * OS/FW agnostic memset */ #ifndef dmub_memset -#define dmub_memset(dest, val, bytes) memset((dest), (val), (bytes)) +#define dmub_memset(dest, val, bytes) ((void)memset((dest), (val), (bytes))) #endif /** @@ -1702,6 +1702,17 @@ enum dmub_gpint_command { * ARGS: 1 - Power off */ DMUB_GPINT__PANEL_POWER_OFF_SEQ = 138, + /** + * DESC: Gets panel polarity bias. + * ARGS: 0 - Get panel polarity bias + */ + DMUB_GPINT__PANEL_POLARITY_GET_BIAS = 139, + /** + * DESC: Enables panel polarity. + * ARGS: 0 - Disable panel polarity + * 1 - Enable panel polarity + */ + DMUB_GPINT__PANEL_POLARITY_DEBUG_ENABLE = 140, }; /** @@ -1800,6 +1811,7 @@ enum dmub_inbox0_command { * * Command IDs should be treated as stable ABI. * Do not reuse or modify IDs. + * Note that command IDs 1-4 have been deprecated. */ enum dmub_cmd_type { /** @@ -1807,22 +1819,6 @@ enum dmub_cmd_type { */ DMUB_CMD__NULL = 0, /** - * Read modify write register sequence offload. - */ - DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE = 1, - /** - * Field update register sequence offload. - */ - DMUB_CMD__REG_SEQ_FIELD_UPDATE_SEQ = 2, - /** - * Burst write sequence offload. - */ - DMUB_CMD__REG_SEQ_BURST_WRITE = 3, - /** - * Reg wait sequence offload. - */ - DMUB_CMD__REG_REG_WAIT = 4, - /** * Workaround to avoid HUBP underflow during NV12 playback. */ DMUB_CMD__PLAT_54186_WA = 5, @@ -1972,6 +1968,11 @@ enum dmub_cmd_type { DMUB_CMD__BOOT_TIME_CRC = 96, /** + * Command type use for all Panel Polarity commands. + */ + DMUB_CMD__PANEL_POLARITY = 97, + + /** * Command type use for VBIOS shared commands. */ DMUB_CMD__VBIOS = 128, @@ -2041,98 +2042,6 @@ struct dmub_cmd_header { unsigned int reserved1 : 2; /**< reserved bits */ }; -/* - * struct dmub_cmd_read_modify_write_sequence - Read modify write - * - * 60 payload bytes can hold up to 5 sets of read modify writes, - * each take 3 dwords. - * - * number of sequences = header.payload_bytes / sizeof(struct dmub_cmd_read_modify_write_sequence) - * - * modify_mask = 0xffff'ffff means all fields are going to be updated. in this case - * command parser will skip the read and we can use modify_mask = 0xffff'ffff as reg write - */ -struct dmub_cmd_read_modify_write_sequence { - uint32_t addr; /**< register address */ - uint32_t modify_mask; /**< modify mask */ - uint32_t modify_value; /**< modify value */ -}; - -/** - * Maximum number of ops in read modify write sequence. - */ -#define DMUB_READ_MODIFY_WRITE_SEQ__MAX 5 - -/** - * struct dmub_cmd_read_modify_write_sequence - Read modify write command. - */ -struct dmub_rb_cmd_read_modify_write { - struct dmub_cmd_header header; /**< command header */ - /** - * Read modify write sequence. - */ - struct dmub_cmd_read_modify_write_sequence seq[DMUB_READ_MODIFY_WRITE_SEQ__MAX]; -}; - -/* - * Update a register with specified masks and values sequeunce - * - * 60 payload bytes can hold address + up to 7 sets of mask/value combo, each take 2 dword - * - * number of field update sequence = (header.payload_bytes - sizeof(addr)) / sizeof(struct read_modify_write_sequence) - * - * - * USE CASE: - * 1. auto-increment register where additional read would update pointer and produce wrong result - * 2. toggle a bit without read in the middle - */ - -struct dmub_cmd_reg_field_update_sequence { - uint32_t modify_mask; /**< 0xffff'ffff to skip initial read */ - uint32_t modify_value; /**< value to update with */ -}; - -/** - * Maximum number of ops in field update sequence. - */ -#define DMUB_REG_FIELD_UPDATE_SEQ__MAX 7 - -/** - * struct dmub_rb_cmd_reg_field_update_sequence - Field update command. - */ -struct dmub_rb_cmd_reg_field_update_sequence { - struct dmub_cmd_header header; /**< command header */ - uint32_t addr; /**< register address */ - /** - * Field update sequence. - */ - struct dmub_cmd_reg_field_update_sequence seq[DMUB_REG_FIELD_UPDATE_SEQ__MAX]; -}; - - -/** - * Maximum number of burst write values. - */ -#define DMUB_BURST_WRITE_VALUES__MAX 14 - -/* - * struct dmub_rb_cmd_burst_write - Burst write - * - * support use case such as writing out LUTs. - * - * 60 payload bytes can hold up to 14 values to write to given address - * - * number of payload = header.payload_bytes / sizeof(struct read_modify_write_sequence) - */ -struct dmub_rb_cmd_burst_write { - struct dmub_cmd_header header; /**< command header */ - uint32_t addr; /**< register start address */ - /** - * Burst write register values. - */ - uint32_t write_values[DMUB_BURST_WRITE_VALUES__MAX]; -}; - /** * struct dmub_rb_cmd_common - Common command header */ @@ -2145,24 +2054,6 @@ struct dmub_rb_cmd_common { }; /** - * struct dmub_cmd_reg_wait_data - Register wait data - */ -struct dmub_cmd_reg_wait_data { - uint32_t addr; /**< Register address */ - uint32_t mask; /**< Mask for register bits */ - uint32_t condition_field_value; /**< Value to wait for */ - uint32_t time_out_us; /**< Time out for reg wait in microseconds */ -}; - -/** - * struct dmub_rb_cmd_reg_wait - Register wait command - */ -struct dmub_rb_cmd_reg_wait { - struct dmub_cmd_header header; /**< Command header */ - struct dmub_cmd_reg_wait_data reg_wait; /**< Register wait data */ -}; - -/** * struct dmub_cmd_PLAT_54186_wa - Underflow workaround * * Reprograms surface parameters to avoid underflow. @@ -4491,6 +4382,15 @@ enum dmub_cmd_replay_type { }; /* + * Panel Polarity sub-types + */ +enum dmub_cmd_panel_polarity_type { + DMUB_CMD__PANEL_POLARITY_ENABLE = 0, + DMUB_CMD__PANEL_POLARITY_GET_BIAS = 1, + DMUB_CMD__PANEL_POLARITY_RESET = 2, +}; + +/* * Panel Replay sub-types */ enum dmub_cmd_panel_replay_type { @@ -6440,9 +6340,42 @@ struct dmub_cmd_cacp_set_backlight_data { uint8_t panel_mask; /** + * AUX HW Instance. + */ + uint8_t aux_inst; + + /** * Explicit padding to 4 byte boundary. */ - uint8_t pad[2]; + uint8_t pad[1]; + + /** + * Backlight control type. + * Value 0 is PWM backlight control. + * Value 1 is VAUX backlight control. + * Value 2 is AMD DPCD AUX backlight control. + */ + enum dmub_backlight_control_type backlight_control_type; + + /** + * Minimum luminance in nits. + */ + uint32_t min_luminance; + + /** + * Maximum luminance in nits. + */ + uint32_t max_luminance; + + /** + * Minimum backlight in pwm. + */ + uint32_t min_backlight_pwm; + + /** + * Maximum backlight in pwm. + */ + uint32_t max_backlight_pwm; }; /** @@ -7123,6 +7056,80 @@ struct dmub_cmd_pr_enable_data { uint8_t pad[2]; }; +struct dmub_cmd_panel_polarity_enable_data { + /** + * Panel Polarity enable or disable. + */ + uint8_t enable; + /** + * OTG instance + */ + uint8_t otg_inst; + /** + * @pad: Align structure to 4 byte boundary. + */ + uint8_t pad[2]; +}; + +struct dmub_cmd_panel_polarity_reset_data { + /** + * OTG instance + */ + uint8_t otg_inst; + /** + * @pad: Align structure to 4 byte boundary. + */ + uint8_t pad[3]; +}; + +struct dmub_cmd_panel_polarity_get_bias_input { + /** + * OTG instance + */ + uint8_t otg_inst; + uint8_t pad[3]; +}; + +struct dmub_cmd_panel_polarity_get_bias_output { + /** + * Accumulated Polarity Bias + */ + int32_t accumulated_bias; +}; + +struct dmub_rb_cmd_panel_polarity_enable { + /** + * Command header. + */ + struct dmub_cmd_header header; + + struct dmub_cmd_panel_polarity_enable_data data; +}; + + +struct dmub_rb_cmd_panel_polarity_get_bias { + /** + * Command header. + */ + struct dmub_cmd_header header; + + union dmub_cmd_panel_polarity_get_bias_data { + struct dmub_cmd_panel_polarity_get_bias_input input; /**< Input */ + struct dmub_cmd_panel_polarity_get_bias_output output; /**< Output */ + uint32_t output_raw; /**< Raw data output */ + } data; +}; + +struct dmub_rb_cmd_panel_polarity_reset { + /** + * Command header. + */ + struct dmub_cmd_header header; + + struct dmub_cmd_panel_polarity_reset_data data; +}; + + /** * Definition of a DMUB_CMD__PR_ENABLE command. * Panel Replay enable/disable is controlled using action in data. @@ -7363,22 +7370,6 @@ union dmub_rb_cmd { */ struct dmub_rb_cmd_common cmd_common; /** - * Definition of a DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE command. - */ - struct dmub_rb_cmd_read_modify_write read_modify_write; - /** - * Definition of a DMUB_CMD__REG_SEQ_FIELD_UPDATE_SEQ command. - */ - struct dmub_rb_cmd_reg_field_update_sequence reg_field_update_seq; - /** - * Definition of a DMUB_CMD__REG_SEQ_BURST_WRITE command. - */ - struct dmub_rb_cmd_burst_write burst_write; - /** - * Definition of a DMUB_CMD__REG_REG_WAIT command. - */ - struct dmub_rb_cmd_reg_wait reg_wait; - /** * Definition of a DMUB_CMD__VBIOS_DIGX_ENCODER_CONTROL command. */ struct dmub_rb_cmd_digx_encoder_control digx_encoder_control; @@ -7754,6 +7745,7 @@ union dmub_rb_cmd { struct dmub_rb_cmd_pr_update_state pr_update_state; struct dmub_rb_cmd_pr_general_cmd pr_general_cmd; + /** * Definition of a DMUB_CMD__IHC command. */ @@ -7762,6 +7754,13 @@ union dmub_rb_cmd { * Definition of a DMUB_CMD__BOOT_TIME_CRC_INIT command. */ struct dmub_rb_cmd_boot_time_crc_init boot_time_crc_init; + + /** + * Definition of a DMUB_CMD__PANEL_POLARITY_ENABLE command. + */ + struct dmub_rb_cmd_panel_polarity_enable panel_polarity_enable; + struct dmub_rb_cmd_panel_polarity_get_bias panel_polarity_get_bias; + struct dmub_rb_cmd_panel_polarity_reset panel_polarity_reset; }; /** diff --git a/drivers/gpu/drm/amd/display/include/ddc_service_types.h b/drivers/gpu/drm/amd/display/include/ddc_service_types.h index 53210e3aa0e0..827e9bd7c5cf 100644 --- a/drivers/gpu/drm/amd/display/include/ddc_service_types.h +++ b/drivers/gpu/drm/amd/display/include/ddc_service_types.h @@ -45,6 +45,7 @@ #define DP_DEVICE_ID_BA4159 0xBA4159 #define DP_FORCE_PSRSU_CAPABILITY 0x40F +#define DP_SINK_PSR_ACTIVE_VTOTAL_CONTROL_CAP 0x370 #define DP_SINK_PSR_ACTIVE_VTOTAL 0x373 #define DP_SINK_PSR_ACTIVE_VTOTAL_CONTROL_MODE 0x375 #define DP_SOURCE_PSR_ACTIVE_VTOTAL 0x376 diff --git a/drivers/gpu/drm/amd/display/include/gpio_types.h b/drivers/gpu/drm/amd/display/include/gpio_types.h index 8dd46ed799e5..afd3fc73a911 100644 --- a/drivers/gpu/drm/amd/display/include/gpio_types.h +++ b/drivers/gpu/drm/amd/display/include/gpio_types.h @@ -277,6 +277,49 @@ enum gpio_config_type { GPIO_CONFIG_TYPE_I2C_AUX_DUAL_MODE }; +struct gpio_id_offset_entry { + uint32_t offset; + uint32_t mask; + + bool check_mask; + + enum gpio_id id; + uint32_t en; +}; + +#define GPIO_ENTRY(_offset, _id, _en) \ + { \ + .offset = REG(_offset), \ + .check_mask = false, \ + .id = (_id), \ + .en = (_en), \ + } + +#define GPIO_MASK_ENTRY(_offset, _mask, _id, _en) \ + { \ + .offset = REG(_offset), \ + .mask = (_mask), \ + .check_mask = true, \ + .id = (_id), \ + .en = (_en), \ + } + +struct gpio_pin_entry { + enum gpio_id id; + uint32_t en; + + uint32_t offset; + uint32_t mask; +}; + +#define GPIO_PIN_ENTRY(_id, _en, _offset, _mask) \ + { \ + .id = (_id), \ + .en = (_en), \ + .offset = REG(_offset), \ + .mask = (_mask), \ + } + /* DDC configuration */ enum gpio_ddc_config_type { @@ -293,6 +336,11 @@ struct gpio_ddc_config { bool clock_en_bit_present; }; +struct gpio_ddc_offset_entry { + uint32_t offset; + uint32_t en; +}; + /* HPD configuration */ struct gpio_hpd_config { diff --git a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c index 1164fd96b714..f0f8e280ed30 100644 --- a/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c +++ b/drivers/gpu/drm/amd/display/modules/hdcp/hdcp_log.c @@ -33,22 +33,28 @@ void mod_hdcp_dump_binary_message(uint8_t *msg, uint32_t msg_size, byte_size = 3, newline_size = 1, terminator_size = 1; - uint32_t line_count = msg_size / bytes_per_line, - trailing_bytes = msg_size % bytes_per_line; - uint32_t target_size = (byte_size * bytes_per_line + newline_size) * line_count + - byte_size * trailing_bytes + newline_size + terminator_size; uint32_t buf_pos = 0; uint32_t i = 0; - if (buf_size >= target_size) { - for (i = 0; i < msg_size; i++) { - if (i % bytes_per_line == 0) - buf[buf_pos++] = '\n'; - sprintf((char *)&buf[buf_pos], "%02X ", msg[i]); - buf_pos += byte_size; - } - buf[buf_pos++] = '\0'; + /* Need room for at least the terminator. */ + if (buf_size < terminator_size) + return; + + for (i = 0; i < msg_size; i++) { + uint32_t needed = byte_size + terminator_size; + + if (i % bytes_per_line == 0) + needed += newline_size; + + if (buf_pos + needed > buf_size) + break; + + if (i % bytes_per_line == 0) + buf[buf_pos++] = '\n'; + sprintf((char *)&buf[buf_pos], "%02X ", msg[i]); + buf_pos += byte_size; } + buf[buf_pos++] = '\0'; } void mod_hdcp_log_ddc_trace(struct mod_hdcp *hdcp) diff --git a/drivers/gpu/drm/amd/display/modules/power/Makefile b/drivers/gpu/drm/amd/display/modules/power/Makefile index 3000f392bdbc..0746f671eb4d 100644 --- a/drivers/gpu/drm/amd/display/modules/power/Makefile +++ b/drivers/gpu/drm/amd/display/modules/power/Makefile @@ -23,7 +23,7 @@ # Makefile for the 'power' sub-module of DAL. # -MOD_POWER = power_helpers.o power.o power_abm.o power_psr.o power_replay.o +MOD_POWER = power.o power_abm.o power_psr.o power_replay.o AMD_DAL_MOD_POWER = $(addprefix $(AMDDALPATH)/modules/power/,$(MOD_POWER)) #$(info ************ DAL POWER MODULE MAKEFILE ************) diff --git a/drivers/gpu/drm/amd/display/modules/power/power.c b/drivers/gpu/drm/amd/display/modules/power/power.c index 5659a38b3366..db101fdb11f0 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power.c +++ b/drivers/gpu/drm/amd/display/modules/power/power.c @@ -483,12 +483,7 @@ bool mod_power_notify_mode_change(struct mod_power *mod_power, link = dc_stream_get_link(stream); if (link != NULL && dc_get_edp_link_panel_inst(dc, link, &panel_inst)) { - if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) { - aux_inst = (uint8_t)link->aux_hw_inst; - } else { - ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); - aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; - } + aux_inst = link->dc->link_srv->get_ddc_aux_inst(link); mod_power_update_backlight_on_mode_change(core_power, link, panel_inst, aux_inst, is_hdr); @@ -501,3 +496,8 @@ bool mod_power_notify_mode_change(struct mod_power *mod_power, return true; } + +bool mod_power_only_edp(const struct dc_state *context, const struct dc_stream_state *stream) +{ + return context && context->stream_count == 1 && dc_is_embedded_signal(stream->signal); +} diff --git a/drivers/gpu/drm/amd/display/modules/power/power_abm.c b/drivers/gpu/drm/amd/display/modules/power/power_abm.c index a1a0563598b5..b9447cb7485b 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_abm.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_abm.c @@ -849,12 +849,7 @@ bool mod_power_set_backlight_nits(struct mod_power *mod_power, core_power = MOD_POWER_TO_CORE(mod_power); link = dc_stream_get_link(stream); - if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) { - aux_inst = (uint8_t)link->aux_hw_inst; - } else { - ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); - aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; - } + aux_inst = link->dc->link_srv->get_ddc_aux_inst(link); if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &panel_inst)) return false; @@ -941,12 +936,7 @@ bool mod_power_set_backlight_percent(struct mod_power *mod_power, core_power = MOD_POWER_TO_CORE(mod_power); link = dc_stream_get_link(stream); - if (link->ctx->dc->config.dp_connector_no_native_i2c && link->no_ddc_pin) { - aux_inst = (uint8_t)link->aux_hw_inst; - } else { - ASSERT(link->ddc->ddc_pin->hw_info.ddc_channel <= 0xFF); - aux_inst = (uint8_t)link->ddc->ddc_pin->hw_info.ddc_channel; - } + aux_inst = link->dc->link_srv->get_ddc_aux_inst(link); if (!dc_get_edp_link_panel_inst(core_power->dc, stream->link, &panel_inst)) return false; diff --git a/drivers/gpu/drm/amd/display/modules/power/power_replay.c b/drivers/gpu/drm/amd/display/modules/power/power_replay.c index 983be9759e74..e782501442c4 100644 --- a/drivers/gpu/drm/amd/display/modules/power/power_replay.c +++ b/drivers/gpu/drm/amd/display/modules/power/power_replay.c @@ -175,11 +175,10 @@ static bool mod_power_update_replay_active_status(unsigned int active_replay_eve if (link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]) *coasting_vtotal = link->replay_settings.coasting_vtotal_table[PR_COASTING_TYPE_TEST_HARNESS]; - if (link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]) { - ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS] <= 0xFFFF); - *frame_skip_number = - (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]; - } + + ASSERT(link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS] <= 0xFFFF); + *frame_skip_number = + (uint16_t)link->replay_settings.frame_skip_number_table[PR_COASTING_TYPE_TEST_HARNESS]; /* During the ultra sleep mode testing, disable the timing sync in short vblank mode */ if (active_replay_events & (replay_event_test_harness_enable_replay)) { diff --git a/drivers/gpu/drm/amd/include/amd_shared.h b/drivers/gpu/drm/amd/include/amd_shared.h index 3fd38323a88b..10396018afb3 100644 --- a/drivers/gpu/drm/amd/include/amd_shared.h +++ b/drivers/gpu/drm/amd/include/amd_shared.h @@ -471,10 +471,7 @@ struct amd_ip_funcs { void (*complete)(struct amdgpu_ip_block *ip_block); bool (*is_idle)(struct amdgpu_ip_block *ip_block); int (*wait_for_idle)(struct amdgpu_ip_block *ip_block); - bool (*check_soft_reset)(struct amdgpu_ip_block *ip_block); - int (*pre_soft_reset)(struct amdgpu_ip_block *ip_block); int (*soft_reset)(struct amdgpu_ip_block *ip_block); - int (*post_soft_reset)(struct amdgpu_ip_block *ip_block); int (*set_clockgating_state)(struct amdgpu_ip_block *ip_block, enum amd_clockgating_state state); int (*set_powergating_state)(struct amdgpu_ip_block *ip_block, diff --git a/drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h b/drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h index ead81aeffd67..11c32e4274fa 100644 --- a/drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h +++ b/drivers/gpu/drm/amd/include/asic_reg/sdma/sdma_4_4_2_offset.h @@ -493,6 +493,10 @@ #define regSDMA_RLC0_MIDCMD_DATA10_BASE_IDX 0 #define regSDMA_RLC0_MIDCMD_CNTL 0x017b #define regSDMA_RLC0_MIDCMD_CNTL_BASE_IDX 0 +#define regSDMA_RLC0_UTILIZATION_LO 0x017c +#define regSDMA_RLC0_UTILIZATION_LO_BASE_IDX 0 +#define regSDMA_RLC0_UTILIZATION_HI 0x017d +#define regSDMA_RLC0_UTILIZATION_HI_BASE_IDX 0 #define regSDMA_RLC1_RB_CNTL 0x0188 #define regSDMA_RLC1_RB_CNTL_BASE_IDX 0 #define regSDMA_RLC1_RB_BASE 0x0189 diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 44e225e097d0..965b50c8ca30 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -339,6 +339,9 @@ struct kfd2kgd_calls { uint32_t *ptl_state, enum amdgpu_ptl_fmt *fmt1, enum amdgpu_ptl_fmt *fmt2); + int (*hqd_sdma_get_counter)(struct amdgpu_device *adev, + void *mqd, uint32_t num_sdma_queues_per_eng, + uint64_t *val); }; #endif /* KGD_KFD_INTERFACE_H_INCLUDED */ diff --git a/drivers/gpu/drm/amd/include/mes_v11_api_def.h b/drivers/gpu/drm/amd/include/mes_v11_api_def.h index f9629d42ada2..b06412ac8583 100644 --- a/drivers/gpu/drm/amd/include/mes_v11_api_def.h +++ b/drivers/gpu/drm/amd/include/mes_v11_api_def.h @@ -238,8 +238,9 @@ union MESAPI_SET_HW_RESOURCES { uint32_t enable_mes_sch_stb_log : 1; uint32_t limit_single_process : 1; uint32_t is_strix_tmz_wa_enabled :1; - uint32_t enable_lr_compute_wa : 1; - uint32_t reserved : 12; + uint32_t enable_lr_compute_wa : 2; + uint32_t enable_compute_pipe_reset : 1; + uint32_t reserved : 10; }; uint32_t uint32_t_all; }; @@ -427,6 +428,7 @@ union MESAPI__SUSPEND { uint32_t suspend_fence_value; struct MES_API_STATUS api_status; + uint32_t doorbell_offset; }; uint32_t max_dwords_in_api[API_FRAME_SIZE_IN_DWORDS]; @@ -444,6 +446,7 @@ union MESAPI__RESUME { uint64_t gang_context_addr; struct MES_API_STATUS api_status; + uint32_t doorbell_offset; }; uint32_t max_dwords_in_api[API_FRAME_SIZE_IN_DWORDS]; diff --git a/drivers/gpu/drm/amd/include/mes_v12_api_def.h b/drivers/gpu/drm/amd/include/mes_v12_api_def.h index e541a43714a1..cb7ebdfffeeb 100644 --- a/drivers/gpu/drm/amd/include/mes_v12_api_def.h +++ b/drivers/gpu/drm/amd/include/mes_v12_api_def.h @@ -294,8 +294,9 @@ union MESAPI_SET_HW_RESOURCES { uint32_t limit_single_process : 1; uint32_t unmapped_doorbell_handling: 2; uint32_t enable_mes_fence_int: 1; - uint32_t enable_lr_compute_wa : 1; - uint32_t reserved : 9; + uint32_t enable_lr_compute_wa : 2; + uint32_t enable_compute_pipe_reset : 1; + uint32_t reserved : 7; }; uint32_t uint32_all; }; diff --git a/drivers/gpu/drm/amd/include/soc15_hw_ip.h b/drivers/gpu/drm/amd/include/soc15_hw_ip.h index a20e59584dde..60f588dd0130 100644 --- a/drivers/gpu/drm/amd/include/soc15_hw_ip.h +++ b/drivers/gpu/drm/amd/include/soc15_hw_ip.h @@ -44,6 +44,7 @@ #define SDPMUX_HWID 19 #define NTB_HWID 20 #define VPE_HWID 21 +#define UMSCH_HWID 22 #define IOHC_HWID 24 #define L2IMU_HWID 28 #define VCE_HWID 32 diff --git a/drivers/gpu/drm/amd/include/v9_structs.h b/drivers/gpu/drm/amd/include/v9_structs.h index a2f81b9c38af..e0d387f08576 100644 --- a/drivers/gpu/drm/amd/include/v9_structs.h +++ b/drivers/gpu/drm/amd/include/v9_structs.h @@ -69,8 +69,8 @@ struct v9_sdma_mqd { uint32_t sdmax_rlcx_midcmd_cntl; uint32_t reserved_42; uint32_t reserved_43; - uint32_t reserved_44; - uint32_t reserved_45; + uint32_t sdmax_rlcx_utilization_lo; + uint32_t sdmax_rlcx_utilization_hi; uint32_t reserved_46; uint32_t reserved_47; uint32_t reserved_48; diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index f43d09769320..f5a5d72b4108 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -41,6 +41,8 @@ #define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name) +#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff)) + struct od_attribute { struct kobj_attribute attribute; struct list_head entry; @@ -98,6 +100,37 @@ const char * const amdgpu_pp_profile_name[] = { "UNCAPPED", }; +static int amdgpu_pm_parse_long_params(char *str, long *params, + uint32_t max_params, + uint32_t *num_params) +{ + const char delimiter[] = { ' ', '\n', '\0' }; + uint32_t count = 0; + char *sub_str; + int ret; + + if (!params || !num_params) + return -EINVAL; + + while ((sub_str = strsep(&str, delimiter)) != NULL) { + if (strlen(sub_str) == 0) + continue; + if (count >= max_params) + return -EINVAL; + ret = kstrtol(sub_str, 0, ¶ms[count]); + if (ret) + return -EINVAL; + count++; + if (!str) + break; + while (isspace(*str)) + str++; + } + *num_params = count; + + return 0; +} + /** * amdgpu_pm_dev_state_check - Check if device can be accessed. * @adev: Target device. @@ -767,8 +800,6 @@ static ssize_t amdgpu_set_pp_od_clk_voltage(struct device *dev, long parameter[64]; char buf_cpy[128]; char *tmp_str; - char *sub_str; - const char delimiter[3] = {' ', '\n', '\0'}; uint32_t type; if (count > 127 || count == 0) @@ -803,22 +834,10 @@ static ssize_t amdgpu_set_pp_od_clk_voltage(struct device *dev, tmp_str++; while (isspace(*++tmp_str)); - while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) { - if (strlen(sub_str) == 0) - continue; - if (parameter_size >= ARRAY_SIZE(parameter)) - return -EINVAL; - ret = kstrtol(sub_str, 0, ¶meter[parameter_size]); - if (ret) - return -EINVAL; - parameter_size++; - - if (!tmp_str) - break; - - while (isspace(*tmp_str)) - tmp_str++; - } + ret = amdgpu_pm_parse_long_params( + tmp_str, parameter, ARRAY_SIZE(parameter), ¶meter_size); + if (ret) + return ret; ret = amdgpu_pm_get_access(adev); if (ret < 0) @@ -1391,11 +1410,9 @@ static ssize_t amdgpu_set_pp_power_profile_mode(struct device *dev, struct amdgpu_device *adev = drm_to_adev(ddev); uint32_t parameter_size = 0; long parameter[64]; - char *sub_str, buf_cpy[128]; - char *tmp_str; + char buf_cpy[128]; char tmp[2]; long int profile_mode = 0; - const char delimiter[3] = {' ', '\n', '\0'}; /* Reject empty/whitespace strings - fuzzing found this is not validated */ if (count == 0 || sysfs_streq(buf, "")) @@ -1413,19 +1430,11 @@ static ssize_t amdgpu_set_pp_power_profile_mode(struct device *dev, while (isspace(*buf)) buf++; strscpy(buf_cpy, buf, sizeof(buf_cpy)); - tmp_str = buf_cpy; - while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) { - if (strlen(sub_str) == 0) - continue; - ret = kstrtol(sub_str, 0, ¶meter[parameter_size]); - if (ret) - return -EINVAL; - parameter_size++; - if (!tmp_str) - break; - while (isspace(*tmp_str)) - tmp_str++; - } + ret = amdgpu_pm_parse_long_params(buf_cpy, parameter, + ARRAY_SIZE(parameter) - 1, + ¶meter_size); + if (ret) + return ret; } parameter[parameter_size] = profile_mode; @@ -2696,6 +2705,11 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ gc_ver != IP_VERSION(9, 4, 3)) || gc_ver < IP_VERSION(9, 0, 0)) *states = ATTR_STATE_UNSUPPORTED; + + if (adev->scpm_enabled) { + dev_attr->attr.mode &= ~S_IWUGO; + dev_attr->store = NULL; + } } else if (DEVICE_ATTR_IS(gpu_metrics)) { if (gc_ver < IP_VERSION(9, 1, 0)) *states = ATTR_STATE_UNSUPPORTED; @@ -3349,7 +3363,6 @@ static int amdgpu_hwmon_get_power(struct device *dev, enum amd_pp_sensors sensor) { struct amdgpu_device *adev = dev_get_drvdata(dev); - unsigned int uw; u32 query = 0; int r; @@ -3358,9 +3371,7 @@ static int amdgpu_hwmon_get_power(struct device *dev, return r; /* convert to microwatts */ - uw = (query >> 8) * 1000000 + (query & 0xff) * 1000; - - return uw; + return power_2_mwatt(query) * 1000; } static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev, @@ -3954,18 +3965,14 @@ out_pm_put: return size; } -static int parse_input_od_command_lines(const char *buf, - size_t count, - u32 *type, - long *params, - size_t params_max, +static int parse_input_od_command_lines(const char *buf, size_t count, + u32 *type, long *params, + uint32_t max_params, uint32_t *num_of_params) { - const char delimiter[3] = {' ', '\n', '\0'}; uint32_t parameter_size = 0; char buf_cpy[128] = {0}; - char *tmp_str, *sub_str; - int ret; + char *tmp_str; if (count > sizeof(buf_cpy) - 1) return -EINVAL; @@ -3990,28 +3997,8 @@ static int parse_input_od_command_lines(const char *buf, break; } - while ((sub_str = strsep(&tmp_str, delimiter)) != NULL) { - if (strlen(sub_str) == 0) - continue; - - if (parameter_size >= params_max) - return -EINVAL; - - ret = kstrtol(sub_str, 0, ¶ms[parameter_size]); - if (ret) - return -EINVAL; - parameter_size++; - - if (!tmp_str) - break; - - while (isspace(*tmp_str)) - tmp_str++; - } - - *num_of_params = parameter_size; - - return 0; + return amdgpu_pm_parse_long_params(tmp_str, params, max_params, + num_of_params); } static int @@ -4024,10 +4011,7 @@ amdgpu_distribute_custom_od_settings(struct amdgpu_device *adev, long parameter[64]; int ret; - ret = parse_input_od_command_lines(in_buf, - count, - &cmd_type, - parameter, + ret = parse_input_od_command_lines(in_buf, count, &cmd_type, parameter, ARRAY_SIZE(parameter), ¶meter_size); if (ret) @@ -4903,7 +4887,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a { uint32_t mp1_ver = amdgpu_ip_version(adev, MP1_HWIP, 0); uint32_t gc_ver = amdgpu_ip_version(adev, GC_HWIP, 0); - uint32_t value; + uint32_t value, mwatt, centiwatt; uint64_t value64 = 0; uint32_t query = 0; int size; @@ -4928,17 +4912,21 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a seq_printf(m, "\t%u mV (VDDNB)\n", value); size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) { + mwatt = power_2_mwatt(query); + centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) - seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100); else - seq_printf(m, "\t%u.%02u W (average SoC)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (average SoC)\n", centiwatt / 100, centiwatt % 100); } size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) { + mwatt = power_2_mwatt(query); + centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) - seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100); else - seq_printf(m, "\t%u.%02u W (current SoC)\n", query >> 8, query & 0xff); + seq_printf(m, "\t%u.%02u W (current SoC)\n", centiwatt / 100, centiwatt % 100); } size = sizeof(value); seq_printf(m, "\n"); diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c index 6f5c27bdc1e9..7c70e228a5ba 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c +++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c @@ -660,25 +660,20 @@ static int amd_powerplay_reset(void *handle) static int pp_dpm_set_pp_table(void *handle, const char *buf, size_t size) { struct pp_hwmgr *hwmgr = handle; + void *hardcode_pp_table; int ret = -ENOMEM; - if (!hwmgr || !hwmgr->pm_en) - return -EINVAL; - - if (size > hwmgr->soft_pp_table_size) + if (!hwmgr || !hwmgr->pm_en || !buf || !size || size > U32_MAX) return -EINVAL; - if (!hwmgr->hardcode_pp_table) { - hwmgr->hardcode_pp_table = kmemdup(hwmgr->soft_pp_table, - hwmgr->soft_pp_table_size, - GFP_KERNEL); - if (!hwmgr->hardcode_pp_table) - return ret; - } - - memcpy(hwmgr->hardcode_pp_table, buf, size); + hardcode_pp_table = kmemdup(buf, size, GFP_KERNEL); + if (!hardcode_pp_table) + return ret; + kfree(hwmgr->hardcode_pp_table); + hwmgr->hardcode_pp_table = hardcode_pp_table; hwmgr->soft_pp_table = hwmgr->hardcode_pp_table; + hwmgr->soft_pp_table_size = size; ret = amd_powerplay_reset(handle); if (ret) diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c index ce166a7f8e42..4b796d60b03d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomctrl.c @@ -46,16 +46,22 @@ union voltage_object_info { static int atomctrl_retrieve_ac_timing( uint8_t index, ATOM_INIT_REG_BLOCK *reg_block, + u8 *table_end, pp_atomctrl_mc_reg_table *table) { uint32_t i, j; + u16 stride = le16_to_cpu(reg_block->usRegDataBlkSize); uint8_t tmem_id; ATOM_MEMORY_SETTING_DATA_BLOCK *reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) ((uint8_t *)reg_block + (2 * sizeof(uint16_t)) + le16_to_cpu(reg_block->usRegIndexTblSize)); uint8_t num_ranges = 0; - while (*(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK && + if (stride < sizeof(uint32_t)) + return -EINVAL; + + while ((uint8_t *)reg_data + sizeof(uint32_t) <= table_end && + *(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK && num_ranges < VBIOS_MAX_AC_TIMING_ENTRIES) { tmem_id = (uint8_t)((*(uint32_t *)reg_data & MEM_ID_MASK) >> MEM_ID_SHIFT); @@ -67,6 +73,10 @@ static int atomctrl_retrieve_ac_timing( for (i = 0, j = 1; i < table->last; i++) { if ((table->mc_reg_address[i].uc_pre_reg_data & LOW_NIBBLE_MASK) == DATA_FROM_TABLE) { + if ((uint8_t *)reg_data + + (j + 1) * sizeof(uint32_t) > table_end) + return -EINVAL; + table->mc_reg_table_entry[num_ranges].mc_data[i] = (uint32_t)*((uint32_t *)reg_data + j); j++; @@ -81,11 +91,13 @@ static int atomctrl_retrieve_ac_timing( } reg_data = (ATOM_MEMORY_SETTING_DATA_BLOCK *) - ((uint8_t *)reg_data + le16_to_cpu(reg_block->usRegDataBlkSize)) ; + ((uint8_t *)reg_data + stride); } - PP_ASSERT_WITH_CODE((*(uint32_t *)reg_data == END_OF_REG_DATA_BLOCK), - "Invalid VramInfo table.", return -1); + if ((uint8_t *)reg_data + sizeof(uint32_t) > table_end || + *(uint32_t *)reg_data != END_OF_REG_DATA_BLOCK) + return -EINVAL; + table->num_entries = num_ranges; return 0; @@ -136,6 +148,7 @@ int atomctrl_initialize_mc_reg_table( { ATOM_VRAM_INFO_HEADER_V2_1 *vram_info; ATOM_INIT_REG_BLOCK *reg_block; + u8 *table_end; int result = 0; u8 frev, crev; u16 size; @@ -157,6 +170,7 @@ int atomctrl_initialize_mc_reg_table( } if (0 == result) { + table_end = (uint8_t *)vram_info + size; reg_block = (ATOM_INIT_REG_BLOCK *) ((uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset)); result = atomctrl_set_mc_reg_address_table(reg_block, table); @@ -164,7 +178,7 @@ int atomctrl_initialize_mc_reg_table( if (0 == result) { result = atomctrl_retrieve_ac_timing(module_index, - reg_block, table); + reg_block, table_end, table); } return result; @@ -177,6 +191,7 @@ int atomctrl_initialize_mc_reg_table_v2_2( { ATOM_VRAM_INFO_HEADER_V2_2 *vram_info; ATOM_INIT_REG_BLOCK *reg_block; + u8 *table_end; int result = 0; u8 frev, crev; u16 size; @@ -198,6 +213,7 @@ int atomctrl_initialize_mc_reg_table_v2_2( } if (0 == result) { + table_end = (uint8_t *)vram_info + size; reg_block = (ATOM_INIT_REG_BLOCK *) ((uint8_t *)vram_info + le16_to_cpu(vram_info->usMemClkPatchTblOffset)); result = atomctrl_set_mc_reg_address_table(reg_block, table); @@ -205,7 +221,7 @@ int atomctrl_initialize_mc_reg_table_v2_2( if (0 == result) { result = atomctrl_retrieve_ac_timing(module_index, - reg_block, table); + reg_block, table_end, table); } return result; @@ -268,15 +284,21 @@ static const ATOM_VOLTAGE_OBJECT_V3 *atomctrl_lookup_voltage_type_v3( unsigned int offset = offsetof(ATOM_VOLTAGE_OBJECT_INFO_V3_1, asVoltageObj[0]); uint8_t *start = (uint8_t *)voltage_object_info_table; - while (offset < size) { + while (offset + sizeof(ATOM_VOLTAGE_OBJECT_HEADER_V3) <= size) { const ATOM_VOLTAGE_OBJECT_V3 *voltage_object = (const ATOM_VOLTAGE_OBJECT_V3 *)(start + offset); + u16 obj_size; + + obj_size = le16_to_cpu(voltage_object->asGpioVoltageObj.sHeader.usSize); + if (obj_size < sizeof(voltage_object->asGpioVoltageObj.sHeader) || + offset + obj_size > size) + break; if (voltage_type == voltage_object->asGpioVoltageObj.sHeader.ucVoltageType && voltage_mode == voltage_object->asGpioVoltageObj.sHeader.ucVoltageMode) return voltage_object; - offset += le16_to_cpu(voltage_object->asGpioVoltageObj.sHeader.usSize); + offset += obj_size; } return NULL; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c index 6120f14caab0..69aee8661d1e 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/ppatomfwctrl.c @@ -36,16 +36,21 @@ static const union atom_voltage_object_v4 *pp_atomfwctrl_lookup_voltage_type_v4( offsetof(struct atom_voltage_objects_info_v4_1, voltage_object[0]); unsigned long start = (unsigned long)voltage_object_info_table; - while (offset < size) { + while (offset + sizeof(struct atom_voltage_object_header_v4) <= size) { const union atom_voltage_object_v4 *voltage_object = (const union atom_voltage_object_v4 *)(start + offset); + u16 obj_size; + + obj_size = le16_to_cpu(voltage_object->gpio_voltage_obj.header.object_size); + if (obj_size < sizeof(voltage_object->gpio_voltage_obj.header) || + offset + obj_size > size) + break; if (voltage_type == voltage_object->gpio_voltage_obj.header.voltage_type && voltage_mode == voltage_object->gpio_voltage_obj.header.voltage_mode) return voltage_object; - offset += le16_to_cpu(voltage_object->gpio_voltage_obj.header.object_size); - + offset += obj_size; } return NULL; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c index 6fcca65bd7d4..c5673077c895 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/process_pptables_v1_0.c @@ -150,6 +150,368 @@ static const void *get_powerplay_table(struct pp_hwmgr *hwmgr) return table_address; } +static bool tonga_pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset, + size_t size) +{ + size_t table_size = hwmgr->soft_pp_table_size; + + return offset <= table_size && size <= table_size - offset; +} + +static int get_tonga_subtable(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + u16 table_offset, size_t table_size, const void **table) +{ + PP_ASSERT_WITH_CODE((table_offset != 0), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *table = (const void *)(((unsigned long)powerplay_table) + table_offset); + + return 0; +} + +static int validate_tonga_table_entries(struct pp_hwmgr *hwmgr, + u16 table_offset, size_t entries_offset, + u8 num_entries, size_t entry_size) +{ + size_t table_size; + + PP_ASSERT_WITH_CODE((num_entries != 0), + "Invalid PowerPlay Table!", return -1); + + table_size = entries_offset + num_entries * entry_size; + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + return 0; +} + +static int get_tonga_voltage_lookup_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + u16 table_offset, uint32_t max_levels, + const ATOM_Tonga_Voltage_Lookup_Table **lookup_table) +{ + const ATOM_Tonga_Voltage_Lookup_Table *table; + size_t table_size; + int ret; + + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + PP_ASSERT_WITH_CODE((table->ucNumEntries != 0 && + table->ucNumEntries <= max_levels), + "Invalid PowerPlay Table!", return -1); + + table_size = offsetof(ATOM_Tonga_Voltage_Lookup_Table, entries) + + table->ucNumEntries * sizeof(ATOM_Tonga_Voltage_Lookup_Record); + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *lookup_table = table; + + return 0; +} + +static int get_tonga_mclk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_MCLK_Dependency_Table **mclk_dep_table) +{ + const ATOM_Tonga_MCLK_Dependency_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usMclkDependencyTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_tonga_table_entries(hwmgr, table_offset, + offsetof(ATOM_Tonga_MCLK_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Tonga_MCLK_Dependency_Record)); + if (ret) + return ret; + + *mclk_dep_table = table; + + return 0; +} + +static int get_tonga_mm_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_MM_Dependency_Table **mm_dep_table) +{ + const ATOM_Tonga_MM_Dependency_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usMMDependencyTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_tonga_table_entries(hwmgr, table_offset, + offsetof(ATOM_Tonga_MM_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Tonga_MM_Dependency_Record)); + if (ret) + return ret; + + *mm_dep_table = table; + + return 0; +} + +static int get_tonga_sclk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const PPTable_Generic_SubTable_Header **sclk_dep_table) +{ + const PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t entries_offset; + size_t entry_size; + u8 num_entries; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usSclkDependencyTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId < 1) { + const ATOM_Tonga_SCLK_Dependency_Table *table = + (const ATOM_Tonga_SCLK_Dependency_Table *)header; + + entries_offset = offsetof(ATOM_Tonga_SCLK_Dependency_Table, entries); + entry_size = sizeof(ATOM_Tonga_SCLK_Dependency_Record); + num_entries = table->ucNumEntries; + } else { + const ATOM_Polaris_SCLK_Dependency_Table *table = + (const ATOM_Polaris_SCLK_Dependency_Table *)header; + + entries_offset = offsetof(ATOM_Polaris_SCLK_Dependency_Table, entries); + entry_size = sizeof(ATOM_Polaris_SCLK_Dependency_Record); + num_entries = table->ucNumEntries; + } + + ret = validate_tonga_table_entries(hwmgr, table_offset, entries_offset, + num_entries, entry_size); + if (ret) + return ret; + + *sclk_dep_table = header; + + return 0; +} + +static int get_tonga_pcie_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const PPTable_Generic_SubTable_Header **pcie_table) +{ + const PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t entries_offset; + size_t entry_size; + u8 num_entries; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usPCIETableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId < 1) { + const ATOM_Tonga_PCIE_Table *table = + (const ATOM_Tonga_PCIE_Table *)header; + + entries_offset = offsetof(ATOM_Tonga_PCIE_Table, entries); + entry_size = sizeof(ATOM_Tonga_PCIE_Record); + num_entries = table->ucNumEntries; + } else { + const ATOM_Polaris10_PCIE_Table *table = + (const ATOM_Polaris10_PCIE_Table *)header; + + entries_offset = offsetof(ATOM_Polaris10_PCIE_Table, entries); + entry_size = sizeof(ATOM_Polaris10_PCIE_Record); + num_entries = table->ucNumEntries; + } + + ret = validate_tonga_table_entries(hwmgr, table_offset, entries_offset, + num_entries, entry_size); + if (ret) + return ret; + + *pcie_table = header; + + return 0; +} + +static int get_tonga_hard_limit_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_Hard_Limit_Table **hard_limit_table) +{ + const ATOM_Tonga_Hard_Limit_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usHardLimitTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_tonga_table_entries(hwmgr, table_offset, + offsetof(ATOM_Tonga_Hard_Limit_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Tonga_Hard_Limit_Record)); + if (ret) + return ret; + + *hard_limit_table = table; + + return 0; +} + +static int get_tonga_thermal_controller_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_Thermal_Controller **thermal_controller) +{ + u16 table_offset; + + table_offset = le16_to_cpu(powerplay_table->usThermalControllerOffset); + + return get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(**thermal_controller), + (const void **)thermal_controller); +} + +static int get_tonga_fan_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const PPTable_Generic_SubTable_Header **fan_table) +{ + const PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t table_size; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usFanTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId < 8) + table_size = sizeof(ATOM_Tonga_Fan_Table); + else if (header->ucRevId == 8) + table_size = sizeof(ATOM_Fiji_Fan_Table); + else + table_size = sizeof(ATOM_Polaris_Fan_Table); + + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *fan_table = header; + + return 0; +} + +static int get_tonga_power_tune_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const PPTable_Generic_SubTable_Header **power_tune_table) +{ + const PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t table_size; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usPowerTuneTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId < 3) + table_size = sizeof(ATOM_Tonga_PowerTune_Table); + else if (header->ucRevId < 4) + table_size = sizeof(ATOM_Fiji_PowerTune_Table); + else + table_size = sizeof(ATOM_Polaris_PowerTune_Table); + + PP_ASSERT_WITH_CODE((tonga_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *power_tune_table = header; + + return 0; +} + +static int get_tonga_ppm_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_PPM_Table **ppm_table) +{ + u16 table_offset; + + table_offset = le16_to_cpu(powerplay_table->usPPMTableOffset); + + return get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(**ppm_table), (const void **)ppm_table); +} + +static int get_tonga_gpio_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_GPIO_Table **gpio_table) +{ + u16 table_offset; + + table_offset = le16_to_cpu(powerplay_table->usGPIOTableOffset); + + return get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(**gpio_table), (const void **)gpio_table); +} + +static int get_tonga_vce_state_table(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_VCE_State_Table **vce_state_table) +{ + const ATOM_Tonga_VCE_State_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usVCEStateTableOffset); + ret = get_tonga_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_tonga_table_entries(hwmgr, table_offset, + offsetof(ATOM_Tonga_VCE_State_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Tonga_VCE_State_Record)); + if (ret) + return ret; + + *vce_state_table = table; + + return 0; +} + static int get_vddc_lookup_table( struct pp_hwmgr *hwmgr, phm_ppt_v1_voltage_lookup_table **lookup_table, @@ -158,6 +520,7 @@ static int get_vddc_lookup_table( ) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_voltage_lookup_table *table; phm_ppt_v1_voltage_lookup_record *record; ATOM_Tonga_Voltage_Lookup_Record *atom_record; @@ -165,13 +528,22 @@ static int get_vddc_lookup_table( PP_ASSERT_WITH_CODE((0 != vddc_lookup_pp_tables->ucNumEntries), "Invalid CAC Leakage PowerPlay Table!", return 1); - table = kzalloc_flex(*table, entries, max_levels); + num_entries = min_t(uint32_t, vddc_lookup_pp_tables->ucNumEntries, + min_t(uint32_t, max_levels, + pp_entries_max(hwmgr, vddc_lookup_pp_tables, + sizeof(*vddc_lookup_pp_tables), + sizeof(ATOM_Tonga_Voltage_Lookup_Record)))); + if (num_entries < vddc_lookup_pp_tables->ucNumEntries) + pr_warn("amdgpu: VddcLookup table: clamping ucNumEntries %u -> %u\n", + vddc_lookup_pp_tables->ucNumEntries, num_entries); + + table = kzalloc_flex(*table, entries, num_entries); if (!table) return -ENOMEM; - table->count = vddc_lookup_pp_tables->ucNumEntries; + table->count = num_entries; - for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( phm_ppt_v1_voltage_lookup_record, entries, table, i); @@ -198,7 +570,7 @@ static int get_vddc_lookup_table( */ static int get_platform_power_management_table( struct pp_hwmgr *hwmgr, - ATOM_Tonga_PPM_Table *atom_ppm_table) + const ATOM_Tonga_PPM_Table *atom_ppm_table) { struct phm_ppm_table *ptr = kzalloc_obj(*ptr); struct phm_ppt_v1_information *pp_table_information = @@ -246,7 +618,7 @@ static int init_dpm_2_parameters( { int result = 0; struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable); - ATOM_Tonga_PPM_Table *atom_ppm_table; + const ATOM_Tonga_PPM_Table *atom_ppm_table; uint32_t disable_ppm = 0; uint32_t disable_power_control = 0; @@ -275,30 +647,39 @@ static int init_dpm_2_parameters( } if (0 != powerplay_table->usVddcLookupTableOffset) { - const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable = - (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddcLookupTableOffset)); - - result = get_vddc_lookup_table(hwmgr, - &pp_table_information->vddc_lookup_table, pVddcCACTable, 16); + const ATOM_Tonga_Voltage_Lookup_Table *pVddcCACTable; + + result = get_tonga_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddcLookupTableOffset), + 16, &pVddcCACTable); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_information->vddc_lookup_table, + pVddcCACTable, 16); } - if (0 != powerplay_table->usVddgfxLookupTableOffset) { - const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable = - (ATOM_Tonga_Voltage_Lookup_Table *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset)); + if (!result && 0 != powerplay_table->usVddgfxLookupTableOffset) { + const ATOM_Tonga_Voltage_Lookup_Table *pVddgfxCACTable; - result = get_vddc_lookup_table(hwmgr, - &pp_table_information->vddgfx_lookup_table, pVddgfxCACTable, 16); + result = get_tonga_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddgfxLookupTableOffset), + 16, &pVddgfxCACTable); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_information->vddgfx_lookup_table, + pVddgfxCACTable, 16); } disable_ppm = 0; if (0 == disable_ppm) { - atom_ppm_table = (ATOM_Tonga_PPM_Table *) - (((unsigned long)powerplay_table) + le16_to_cpu(powerplay_table->usPPMTableOffset)); - if (0 != powerplay_table->usPPMTableOffset) { - if (get_platform_power_management_table(hwmgr, atom_ppm_table) == 0) { + int ret; + + ret = get_tonga_ppm_table(hwmgr, powerplay_table, + &atom_ppm_table); + if (!ret && + get_platform_power_management_table(hwmgr, + atom_ppm_table) == 0) { phm_cap_set(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_EnablePlatformPowerManagement); } @@ -363,6 +744,7 @@ static int get_mclk_voltage_dependency_table( ) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_clock_voltage_dependency_table *mclk_table; phm_ppt_v1_clock_voltage_dependency_record *mclk_table_record; ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record; @@ -370,14 +752,21 @@ static int get_mclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((0 != mclk_dep_table->ucNumEntries), "Invalid PowerPlay Table!", return -1); - mclk_table = kzalloc_flex(*mclk_table, entries, - mclk_dep_table->ucNumEntries); + num_entries = min_t(uint32_t, mclk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, mclk_dep_table, + sizeof(*mclk_dep_table), + sizeof(ATOM_Tonga_MCLK_Dependency_Record))); + if (num_entries < mclk_dep_table->ucNumEntries) + pr_warn("amdgpu: MCLK dependency table: clamping ucNumEntries %u -> %u\n", + mclk_dep_table->ucNumEntries, num_entries); + + mclk_table = kzalloc_flex(*mclk_table, entries, num_entries); if (!mclk_table) return -ENOMEM; - mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries; + mclk_table->count = num_entries; - for (i = 0; i < mclk_dep_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { mclk_table_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( phm_ppt_v1_clock_voltage_dependency_record, entries, mclk_table, i); @@ -403,6 +792,7 @@ static int get_sclk_voltage_dependency_table( ) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_clock_voltage_dependency_table *sclk_table; phm_ppt_v1_clock_voltage_dependency_record *sclk_table_record; @@ -414,14 +804,21 @@ static int get_sclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((0 != tonga_table->ucNumEntries), "Invalid PowerPlay Table!", return -1); - sclk_table = kzalloc_flex(*sclk_table, entries, - tonga_table->ucNumEntries); + num_entries = min_t(uint32_t, tonga_table->ucNumEntries, + pp_entries_max(hwmgr, tonga_table, + sizeof(*tonga_table), + sizeof(ATOM_Tonga_SCLK_Dependency_Record))); + if (num_entries < tonga_table->ucNumEntries) + pr_warn("amdgpu: Tonga SCLK dependency table: clamping ucNumEntries %u -> %u\n", + tonga_table->ucNumEntries, num_entries); + + sclk_table = kzalloc_flex(*sclk_table, entries, num_entries); if (!sclk_table) return -ENOMEM; - sclk_table->count = (uint32_t)tonga_table->ucNumEntries; + sclk_table->count = num_entries; - for (i = 0; i < tonga_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_SCLK_Dependency_Record, entries, tonga_table, i); @@ -443,14 +840,21 @@ static int get_sclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((0 != polaris_table->ucNumEntries), "Invalid PowerPlay Table!", return -1); - sclk_table = kzalloc_flex(*sclk_table, entries, - polaris_table->ucNumEntries); + num_entries = min_t(uint32_t, polaris_table->ucNumEntries, + pp_entries_max(hwmgr, polaris_table, + sizeof(*polaris_table), + sizeof(ATOM_Polaris_SCLK_Dependency_Record))); + if (num_entries < polaris_table->ucNumEntries) + pr_warn("amdgpu: Polaris SCLK dependency table: clamping ucNumEntries %u -> %u\n", + polaris_table->ucNumEntries, num_entries); + + sclk_table = kzalloc_flex(*sclk_table, entries, num_entries); if (!sclk_table) return -ENOMEM; - sclk_table->count = (uint32_t)polaris_table->ucNumEntries; + sclk_table->count = num_entries; - for (i = 0; i < polaris_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Polaris_SCLK_Dependency_Record, entries, polaris_table, i); @@ -715,20 +1119,29 @@ static int get_mm_clock_voltage_table( ) { uint32_t i; + uint32_t num_entries; const ATOM_Tonga_MM_Dependency_Record *mm_dependency_record; phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table; phm_ppt_v1_mm_clock_voltage_dependency_record *mm_table_record; PP_ASSERT_WITH_CODE((0 != mm_dependency_table->ucNumEntries), "Invalid PowerPlay Table!", return -1); - mm_table = kzalloc_flex(*mm_table, entries, - mm_dependency_table->ucNumEntries); + + num_entries = min_t(uint32_t, mm_dependency_table->ucNumEntries, + pp_entries_max(hwmgr, mm_dependency_table, + sizeof(*mm_dependency_table), + sizeof(ATOM_Tonga_MM_Dependency_Record))); + if (num_entries < mm_dependency_table->ucNumEntries) + pr_warn("amdgpu: MM dependency table: clamping ucNumEntries %u -> %u\n", + mm_dependency_table->ucNumEntries, num_entries); + + mm_table = kzalloc_flex(*mm_table, entries, num_entries); if (!mm_table) return -ENOMEM; - mm_table->count = mm_dependency_table->ucNumEntries; + mm_table->count = num_entries; - for (i = 0; i < mm_dependency_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { mm_dependency_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_MM_Dependency_Record, entries, mm_dependency_table, i); @@ -789,28 +1202,13 @@ static int init_clock_voltage_dependency( int result = 0; struct phm_ppt_v1_information *pp_table_information = (struct phm_ppt_v1_information *)(hwmgr->pptable); - - const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table = - (const ATOM_Tonga_MM_Dependency_Table *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usMMDependencyTableOffset)); - const PPTable_Generic_SubTable_Header *pPowerTuneTable = - (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPowerTuneTableOffset)); - const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table = - (const ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usMclkDependencyTableOffset)); - const PPTable_Generic_SubTable_Header *sclk_dep_table = - (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usSclkDependencyTableOffset)); - const ATOM_Tonga_Hard_Limit_Table *pHardLimits = - (const ATOM_Tonga_Hard_Limit_Table *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usHardLimitTableOffset)); - const PPTable_Generic_SubTable_Header *pcie_table = - (const PPTable_Generic_SubTable_Header *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPCIETableOffset)); - const ATOM_Tonga_GPIO_Table *gpio_table = - (const ATOM_Tonga_GPIO_Table *)(((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usGPIOTableOffset)); + const ATOM_Tonga_MM_Dependency_Table *mm_dependency_table; + const PPTable_Generic_SubTable_Header *pPowerTuneTable; + const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table; + const PPTable_Generic_SubTable_Header *sclk_dep_table; + const ATOM_Tonga_Hard_Limit_Table *pHardLimits; + const PPTable_Generic_SubTable_Header *pcie_table; + const ATOM_Tonga_GPIO_Table *gpio_table; pp_table_information->vdd_dep_on_sclk = NULL; pp_table_information->vdd_dep_on_mclk = NULL; @@ -818,29 +1216,58 @@ static int init_clock_voltage_dependency( pp_table_information->pcie_table = NULL; pp_table_information->gpio_table = NULL; - if (powerplay_table->usMMDependencyTableOffset != 0) - result = get_mm_clock_voltage_table(hwmgr, - &pp_table_information->mm_dep_table, mm_dependency_table); + if (powerplay_table->usMMDependencyTableOffset != 0) { + result = get_tonga_mm_dependency_table(hwmgr, powerplay_table, + &mm_dependency_table); + if (!result) + result = get_mm_clock_voltage_table(hwmgr, + &pp_table_information->mm_dep_table, + mm_dependency_table); + } - if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0) - result = get_cac_tdp_table(hwmgr, - &pp_table_information->cac_dtp_table, pPowerTuneTable); + if (result == 0 && powerplay_table->usPowerTuneTableOffset != 0) { + result = get_tonga_power_tune_table(hwmgr, powerplay_table, + &pPowerTuneTable); + if (!result) + result = get_cac_tdp_table(hwmgr, + &pp_table_information->cac_dtp_table, + pPowerTuneTable); + } - if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0) - result = get_sclk_voltage_dependency_table(hwmgr, - &pp_table_information->vdd_dep_on_sclk, sclk_dep_table); + if (result == 0 && powerplay_table->usSclkDependencyTableOffset != 0) { + result = get_tonga_sclk_dependency_table(hwmgr, powerplay_table, + &sclk_dep_table); + if (!result) + result = get_sclk_voltage_dependency_table(hwmgr, + &pp_table_information->vdd_dep_on_sclk, + sclk_dep_table); + } - if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0) - result = get_mclk_voltage_dependency_table(hwmgr, - &pp_table_information->vdd_dep_on_mclk, mclk_dep_table); + if (result == 0 && powerplay_table->usMclkDependencyTableOffset != 0) { + result = get_tonga_mclk_dependency_table(hwmgr, powerplay_table, + &mclk_dep_table); + if (!result) + result = get_mclk_voltage_dependency_table(hwmgr, + &pp_table_information->vdd_dep_on_mclk, + mclk_dep_table); + } - if (result == 0 && powerplay_table->usPCIETableOffset != 0) - result = get_pcie_table(hwmgr, - &pp_table_information->pcie_table, pcie_table); + if (result == 0 && powerplay_table->usPCIETableOffset != 0) { + result = get_tonga_pcie_table(hwmgr, powerplay_table, + &pcie_table); + if (!result) + result = get_pcie_table(hwmgr, + &pp_table_information->pcie_table, pcie_table); + } - if (result == 0 && powerplay_table->usHardLimitTableOffset != 0) - result = get_hard_limits(hwmgr, - &pp_table_information->max_clock_voltage_on_dc, pHardLimits); + if (result == 0 && powerplay_table->usHardLimitTableOffset != 0) { + result = get_tonga_hard_limit_table(hwmgr, powerplay_table, + &pHardLimits); + if (!result) + result = get_hard_limits(hwmgr, + &pp_table_information->max_clock_voltage_on_dc, + pHardLimits); + } hwmgr->dyn_state.max_clock_voltage_on_dc.sclk = pp_table_information->max_clock_voltage_on_dc.sclk; @@ -861,9 +1288,13 @@ static int init_clock_voltage_dependency( result = get_valid_clk(hwmgr, &pp_table_information->valid_sclk_values, pp_table_information->vdd_dep_on_sclk); - if (!result && gpio_table) - result = get_gpio_table(hwmgr, &pp_table_information->gpio_table, - gpio_table); + if (!result && powerplay_table->usGPIOTableOffset) { + result = get_tonga_gpio_table(hwmgr, powerplay_table, + &gpio_table); + if (!result) + result = get_gpio_table(hwmgr, + &pp_table_information->gpio_table, gpio_table); + } return result; } @@ -908,14 +1339,17 @@ static int init_thermal_controller( ) { const PPTable_Generic_SubTable_Header *fan_table; - ATOM_Tonga_Thermal_Controller *thermal_controller; + const ATOM_Tonga_Thermal_Controller *thermal_controller; + int ret; - thermal_controller = (ATOM_Tonga_Thermal_Controller *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usThermalControllerOffset)); PP_ASSERT_WITH_CODE((0 != powerplay_table->usThermalControllerOffset), "Thermal controller table not set!", return -1); + ret = get_tonga_thermal_controller_table(hwmgr, powerplay_table, + &thermal_controller); + if (ret) + return ret; + hwmgr->thermal_controller.ucType = thermal_controller->ucType; hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine; hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress; @@ -943,12 +1377,13 @@ static int init_thermal_controller( return 0; } - fan_table = (const PPTable_Generic_SubTable_Header *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usFanTableOffset)); - PP_ASSERT_WITH_CODE((0 != powerplay_table->usFanTableOffset), "Fan table not set!", return -1); + + ret = get_tonga_fan_table(hwmgr, powerplay_table, &fan_table); + if (ret) + return ret; + PP_ASSERT_WITH_CODE((0 < fan_table->ucRevId), "Unsupported fan table format!", return -1); @@ -1104,21 +1539,24 @@ static int init_thermal_controller( } /** - * check_powerplay_tables - Private Function used during initialization. - * Inspect the PowerPlay table for obvious signs of corruption. + * get_tonga_state_array - Get the Tonga state array from the PowerPlay table. * @hwmgr: Pointer to the hardware manager. * @powerplay_table: Pointer to the PowerPlay Table. - * Exception: 2 if the powerplay table is incorrect. + * @state_array: Pointer to the returned Tonga state array. + * + * Return: 0 on success, negative error code on failure. */ -static int check_powerplay_tables( - struct pp_hwmgr *hwmgr, - const ATOM_Tonga_POWERPLAYTABLE *powerplay_table - ) +static int get_tonga_state_array(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table, + const ATOM_Tonga_State_Array **state_array) { const ATOM_Tonga_State_Array *state_arrays; + u16 state_array_offset; + size_t state_array_size; + size_t table_size = hwmgr->soft_pp_table_size; - state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + PP_ASSERT_WITH_CODE((table_size >= sizeof(*powerplay_table)), + "Invalid PowerPlay Table!", return -1); PP_ASSERT_WITH_CODE((ATOM_Tonga_TABLE_REVISION_TONGA <= powerplay_table->sHeader.ucTableFormatRevision), @@ -1127,12 +1565,34 @@ static int check_powerplay_tables( "State table is not set!", return -1); PP_ASSERT_WITH_CODE((0 < powerplay_table->sHeader.usStructureSize), "Invalid PowerPlay Table!", return -1); + + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + PP_ASSERT_WITH_CODE((state_array_offset <= + table_size - sizeof(*state_arrays)), + "Invalid PowerPlay Table!", return -1); + + state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)powerplay_table) + + state_array_offset); PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries), "Invalid PowerPlay Table!", return -1); + state_array_size = struct_size(state_arrays, entries, state_arrays->ucNumEntries); + PP_ASSERT_WITH_CODE((state_array_size <= table_size - state_array_offset), + "Invalid PowerPlay Table!", return -1); + + *state_array = state_arrays; + return 0; } +static int check_powerplay_tables(struct pp_hwmgr *hwmgr, + const ATOM_Tonga_POWERPLAYTABLE *powerplay_table) +{ + const ATOM_Tonga_State_Array *state_arrays; + + return get_tonga_state_array(hwmgr, powerplay_table, &state_arrays); +} + static int pp_tables_v1_0_initialize(struct pp_hwmgr *hwmgr) { int result = 0; @@ -1236,17 +1696,16 @@ const struct pp_table_func pptable_v1_0_funcs = { int get_number_of_powerplay_table_entries_v1_0(struct pp_hwmgr *hwmgr) { - ATOM_Tonga_State_Array const *state_arrays; + const ATOM_Tonga_State_Array *state_arrays; const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr); + int result; PP_ASSERT_WITH_CODE((NULL != pp_table), "Missing PowerPlay Table!", return -1); - PP_ASSERT_WITH_CODE((pp_table->sHeader.ucTableFormatRevision >= - ATOM_Tonga_TABLE_REVISION_TONGA), - "Incorrect PowerPlay table revision!", return -1); - state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); + result = get_tonga_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), + "Invalid PowerPlay Table State Array.", return result); return (uint32_t)(state_arrays->ucNumEntries); } @@ -1287,13 +1746,15 @@ static int ppt_get_num_of_vce_state_table_entries_v1_0(struct pp_hwmgr *hwmgr) { const ATOM_Tonga_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr); const ATOM_Tonga_VCE_State_Table *vce_state_table; + int ret; if (pp_table == NULL) return 0; - vce_state_table = (void *)pp_table + - le16_to_cpu(pp_table->usVCEStateTableOffset); + ret = get_tonga_vce_state_table(hwmgr, pp_table, &vce_state_table); + if (ret) + return 0; return vce_state_table->ucNumEntries; } @@ -1302,18 +1763,39 @@ static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i struct amd_vce_state *vce_state, void **clock_info, uint32_t *flag) { const ATOM_Tonga_VCE_State_Record *vce_state_record; - ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record; + ATOM_Tonga_SCLK_Dependency_Record *sclk_dep_record = NULL; + ATOM_Polaris_SCLK_Dependency_Record *polaris_sclk_dep_record = NULL; ATOM_Tonga_MCLK_Dependency_Record *mclk_dep_record; ATOM_Tonga_MM_Dependency_Record *mm_dep_record; const ATOM_Tonga_POWERPLAYTABLE *pptable = get_powerplay_table(hwmgr); - const ATOM_Tonga_VCE_State_Table *vce_state_table = (ATOM_Tonga_VCE_State_Table *)(((unsigned long)pptable) - + le16_to_cpu(pptable->usVCEStateTableOffset)); - const ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table = (ATOM_Tonga_SCLK_Dependency_Table *)(((unsigned long)pptable) - + le16_to_cpu(pptable->usSclkDependencyTableOffset)); - const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table = (ATOM_Tonga_MCLK_Dependency_Table *)(((unsigned long)pptable) - + le16_to_cpu(pptable->usMclkDependencyTableOffset)); - const ATOM_Tonga_MM_Dependency_Table *mm_dep_table = (ATOM_Tonga_MM_Dependency_Table *)(((unsigned long)pptable) - + le16_to_cpu(pptable->usMMDependencyTableOffset)); + const ATOM_Tonga_VCE_State_Table *vce_state_table; + const PPTable_Generic_SubTable_Header *sclk_dep_table_header; + const ATOM_Tonga_SCLK_Dependency_Table *sclk_dep_table; + const ATOM_Tonga_MCLK_Dependency_Table *mclk_dep_table; + const ATOM_Tonga_MM_Dependency_Table *mm_dep_table; + int ret; + + if (!pptable) + return -EINVAL; + + ret = get_tonga_vce_state_table(hwmgr, pptable, &vce_state_table); + if (ret) + return ret; + + ret = get_tonga_sclk_dependency_table(hwmgr, pptable, + &sclk_dep_table_header); + if (ret) + return ret; + sclk_dep_table = (const ATOM_Tonga_SCLK_Dependency_Table *) + sclk_dep_table_header; + + ret = get_tonga_mclk_dependency_table(hwmgr, pptable, &mclk_dep_table); + if (ret) + return ret; + + ret = get_tonga_mm_dependency_table(hwmgr, pptable, &mm_dep_table); + if (ret) + return ret; PP_ASSERT_WITH_CODE((i < vce_state_table->ucNumEntries), "Requested state entry ID is out of range!", @@ -1322,10 +1804,27 @@ static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i vce_state_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_VCE_State_Record, entries, vce_state_table, i); - sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( - ATOM_Tonga_SCLK_Dependency_Record, - entries, sclk_dep_table, - vce_state_record->ucSCLKIndex); + PP_ASSERT_WITH_CODE((vce_state_record->ucSCLKIndex < + sclk_dep_table->ucNumEntries), + "Invalid PowerPlay Table!", return -EINVAL); + PP_ASSERT_WITH_CODE((vce_state_record->ucVCEClockIndex < + mm_dep_table->ucNumEntries), + "Invalid PowerPlay Table!", return -EINVAL); + PP_ASSERT_WITH_CODE((mclk_dep_table->ucNumEntries != 0), + "Invalid PowerPlay Table!", return -EINVAL); + + if (sclk_dep_table_header->ucRevId < 1) + sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( + ATOM_Tonga_SCLK_Dependency_Record, + entries, sclk_dep_table, + vce_state_record->ucSCLKIndex); + else + polaris_sclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( + ATOM_Polaris_SCLK_Dependency_Record, + entries, + (ATOM_Polaris_SCLK_Dependency_Table *) + sclk_dep_table_header, + vce_state_record->ucSCLKIndex); mm_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_MM_Dependency_Record, entries, mm_dep_table, @@ -1334,7 +1833,10 @@ static int ppt_get_vce_state_table_entry_v1_0(struct pp_hwmgr *hwmgr, uint32_t i vce_state->evclk = le32_to_cpu(mm_dep_record->ulEClk); vce_state->ecclk = le32_to_cpu(mm_dep_record->ulEClk); - vce_state->sclk = le32_to_cpu(sclk_dep_record->ulSclk); + if (sclk_dep_record) + vce_state->sclk = le32_to_cpu(sclk_dep_record->ulSclk); + else + vce_state->sclk = le32_to_cpu(polaris_sclk_dep_record->ulSclk); if (vce_state_record->ucMCLKIndex >= mclk_dep_table->ucNumEntries) mclk_dep_record = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( @@ -1377,15 +1879,11 @@ int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr, if (pp_table->sHeader.ucTableFormatRevision >= ATOM_Tonga_TABLE_REVISION_TONGA) { - state_arrays = (ATOM_Tonga_State_Array *)(((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); - - PP_ASSERT_WITH_CODE((0 < pp_table->usStateArrayOffset), - "Invalid PowerPlay Table State Array Offset.", return -1); - PP_ASSERT_WITH_CODE((0 < state_arrays->ucNumEntries), - "Invalid PowerPlay Table State Array.", return -1); - PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries), - "Invalid PowerPlay Table State Array Entry.", return -1); + result = get_tonga_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), + "Invalid PowerPlay Table State Array.", return result); + PP_ASSERT_WITH_CODE((entry_index < state_arrays->ucNumEntries), + "Invalid PowerPlay Table State Array Entry.", return -1); state_entry = GET_FLEXIBLE_ARRAY_MEMBER_ADDR( ATOM_Tonga_State, entries, @@ -1411,4 +1909,3 @@ int get_powerplay_table_entry_v1_0(struct pp_hwmgr *hwmgr, return result; } - diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c index bfd8fbb0b49d..56926eec6820 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/processpptables.c @@ -47,26 +47,53 @@ #define NUM_BITS_CLOCK_INFO_ARRAY_INDEX 6 +static bool pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset, + size_t size) +{ + size_t table_size = hwmgr->soft_pp_table_size; + + return offset <= table_size && size <= table_size - offset; +} + +static const ATOM_PPLIB_EXTENDEDHEADER * +get_extended_header(struct pp_hwmgr *hwmgr, + const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table, + size_t min_size) +{ + const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; + u16 offset; + + if (le16_to_cpu(powerplay_table->usTableSize) < + sizeof(ATOM_PPLIB_POWERPLAYTABLE3) || + !pp_table_has_space(hwmgr, 0, sizeof(ATOM_PPLIB_POWERPLAYTABLE3))) + return NULL; + + powerplay_table3 = (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; + offset = le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset); + if (!offset || !pp_table_has_space(hwmgr, offset, + sizeof(extended_header->usSize))) + return NULL; + + extended_header = (const ATOM_PPLIB_EXTENDEDHEADER *) + (((unsigned long)powerplay_table) + offset); + if (le16_to_cpu(extended_header->usSize) < min_size || + !pp_table_has_space(hwmgr, offset, min_size)) + return NULL; + + return extended_header; +} + static uint16_t get_vce_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t vce_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *extended_header = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2) - vce_table_offset = le16_to_cpu(extended_header->usVCETableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V2); + if (extended_header) + vce_table_offset = le16_to_cpu(extended_header->usVCETableOffset); return vce_table_offset; } @@ -93,7 +120,14 @@ static uint16_t get_vce_clock_info_array_size(struct pp_hwmgr *hwmgr, if (table_offset > 0) { const VCEClockInfoArray *p = (const VCEClockInfoArray *) (((unsigned long) powerplay_table) + table_offset); - table_size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo); + size_t size; + + if (!pp_table_has_space(hwmgr, table_offset, sizeof(p->ucNumEntries))) + return 0; + + size = sizeof(uint8_t) + p->ucNumEntries * sizeof(VCEClockInfo); + if (pp_table_has_space(hwmgr, table_offset, size)) + table_size = size; } return table_size; @@ -104,10 +138,13 @@ static uint16_t get_vce_clock_voltage_limit_table_offset(struct pp_hwmgr *hwmgr, { uint16_t table_offset = get_vce_clock_info_array_offset(hwmgr, powerplay_table); + u16 table_size; - if (table_offset > 0) - return table_offset + get_vce_clock_info_array_size(hwmgr, - powerplay_table); + if (table_offset > 0) { + table_size = get_vce_clock_info_array_size(hwmgr, powerplay_table); + if (table_size) + return table_offset + table_size; + } return 0; } @@ -121,8 +158,15 @@ static uint16_t get_vce_clock_voltage_limit_table_size(struct pp_hwmgr *hwmgr, if (table_offset > 0) { const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *ptable = (const ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table *)(((unsigned long) powerplay_table) + table_offset); + size_t size; - table_size = sizeof(uint8_t) + ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record); + if (!pp_table_has_space(hwmgr, table_offset, sizeof(ptable->numEntries))) + return 0; + + size = sizeof(uint8_t) + + ptable->numEntries * sizeof(ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record); + if (pp_table_has_space(hwmgr, table_offset, size)) + table_size = size; } return table_size; } @@ -130,9 +174,13 @@ static uint16_t get_vce_clock_voltage_limit_table_size(struct pp_hwmgr *hwmgr, static uint16_t get_vce_state_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t table_offset = get_vce_clock_voltage_limit_table_offset(hwmgr, powerplay_table); + u16 table_size; - if (table_offset > 0) - return table_offset + get_vce_clock_voltage_limit_table_size(hwmgr, powerplay_table); + if (table_offset > 0) { + table_size = get_vce_clock_voltage_limit_table_size(hwmgr, powerplay_table); + if (table_size) + return table_offset + table_size; + } return 0; } @@ -143,8 +191,12 @@ static const ATOM_PPLIB_VCE_State_Table *get_vce_state_table( { uint16_t table_offset = get_vce_state_table_offset(hwmgr, powerplay_table); - if (table_offset > 0) - return (const ATOM_PPLIB_VCE_State_Table *)(((unsigned long) powerplay_table) + table_offset); + if (table_offset > 0) { + if (pp_table_has_space(hwmgr, table_offset, + sizeof(((ATOM_PPLIB_VCE_State_Table *)0)->numEntries))) + return (const ATOM_PPLIB_VCE_State_Table *) + (((unsigned long)powerplay_table) + table_offset); + } return NULL; } @@ -153,21 +205,13 @@ static uint16_t get_uvd_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t uvd_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; + + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3); + if (extended_header) + uvd_table_offset = le16_to_cpu(extended_header->usUVDTableOffset); - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *extended_header = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V3) - uvd_table_offset = le16_to_cpu(extended_header->usUVDTableOffset); - } - } return uvd_table_offset; } @@ -193,8 +237,14 @@ static uint16_t get_uvd_clock_info_array_size(struct pp_hwmgr *hwmgr, const UVDClockInfoArray *p = (const UVDClockInfoArray *) (((unsigned long) powerplay_table) + table_offset); - table_size = sizeof(UCHAR) + - p->ucNumEntries * sizeof(UVDClockInfo); + size_t size; + + if (!pp_table_has_space(hwmgr, table_offset, sizeof(p->ucNumEntries))) + return 0; + + size = sizeof(UCHAR) + p->ucNumEntries * sizeof(UVDClockInfo); + if (pp_table_has_space(hwmgr, table_offset, size)) + table_size = size; } return table_size; @@ -206,10 +256,13 @@ static uint16_t get_uvd_clock_voltage_limit_table_offset( { uint16_t table_offset = get_uvd_clock_info_array_offset(hwmgr, powerplay_table); + u16 table_size; - if (table_offset > 0) - return table_offset + - get_uvd_clock_info_array_size(hwmgr, powerplay_table); + if (table_offset > 0) { + table_size = get_uvd_clock_info_array_size(hwmgr, powerplay_table); + if (table_size) + return table_offset + table_size; + } return 0; } @@ -218,21 +271,12 @@ static uint16_t get_samu_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t samu_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *extended_header = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(extended_header->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4) - samu_table_offset = le16_to_cpu(extended_header->usSAMUTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V4); + if (extended_header) + samu_table_offset = le16_to_cpu(extended_header->usSAMUTableOffset); return samu_table_offset; } @@ -254,21 +298,12 @@ static uint16_t get_acp_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t acp_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(pExtendedHeader->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6) - acp_table_offset = le16_to_cpu(pExtendedHeader->usACPTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V6); + if (extended_header) + acp_table_offset = le16_to_cpu(extended_header->usACPTableOffset); return acp_table_offset; } @@ -290,21 +325,12 @@ static uint16_t get_cacp_tdp_table_offset( const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t cacTdpTableOffset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(pExtendedHeader->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7) - cacTdpTableOffset = le16_to_cpu(pExtendedHeader->usPowerTuneTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V7); + if (extended_header) + cacTdpTableOffset = le16_to_cpu(extended_header->usPowerTuneTableOffset); return cacTdpTableOffset; } @@ -341,22 +367,13 @@ static uint16_t get_sclk_vdd_gfx_table_offset(struct pp_hwmgr *hwmgr, const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table) { uint16_t sclk_vdd_gfx_table_offset = 0; + const ATOM_PPLIB_EXTENDEDHEADER *extended_header; - if (le16_to_cpu(powerplay_table->usTableSize) >= - sizeof(ATOM_PPLIB_POWERPLAYTABLE3)) { - const ATOM_PPLIB_POWERPLAYTABLE3 *powerplay_table3 = - (const ATOM_PPLIB_POWERPLAYTABLE3 *)powerplay_table; - if (powerplay_table3->usExtendendedHeaderOffset > 0) { - const ATOM_PPLIB_EXTENDEDHEADER *pExtendedHeader = - (const ATOM_PPLIB_EXTENDEDHEADER *) - (((unsigned long)powerplay_table3) + - le16_to_cpu(powerplay_table3->usExtendendedHeaderOffset)); - if (le16_to_cpu(pExtendedHeader->usSize) >= - SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8) - sclk_vdd_gfx_table_offset = - le16_to_cpu(pExtendedHeader->usSclkVddgfxTableOffset); - } - } + extended_header = get_extended_header(hwmgr, powerplay_table, + SIZE_OF_ATOM_PPLIB_EXTENDEDHEADER_V8); + if (extended_header) + sclk_vdd_gfx_table_offset = + le16_to_cpu(extended_header->usSclkVddgfxTableOffset); return sclk_vdd_gfx_table_offset; } @@ -769,20 +786,37 @@ static ULONG size_of_entry_v2(ULONG num_dpm_levels) } static const ATOM_PPLIB_STATE_V2 *get_state_entry_v2( + struct pp_hwmgr *hwmgr, const StateArray * pstate_arrays, + u16 state_array_offset, ULONG entry_index) { ULONG i; const ATOM_PPLIB_STATE_V2 *pstate; + size_t entry_offset; + size_t entry_size; + + if (entry_index >= pstate_arrays->ucNumEntries) + return NULL; + entry_offset = state_array_offset + sizeof(pstate_arrays->ucNumEntries); pstate = pstate_arrays->states; - if (entry_index <= pstate_arrays->ucNumEntries) { - for (i = 0; i < entry_index; i++) - pstate = (ATOM_PPLIB_STATE_V2 *)( - (unsigned long)pstate + - size_of_entry_v2(pstate->ucNumDPMLevels)); + for (i = 0; i <= entry_index; i++) { + if (!pp_table_has_space(hwmgr, entry_offset, sizeof(*pstate))) + return NULL; + + entry_size = size_of_entry_v2(pstate->ucNumDPMLevels); + if (!pp_table_has_space(hwmgr, entry_offset, entry_size)) + return NULL; + + if (i == entry_index) + return pstate; + + entry_offset += entry_size; + pstate = (ATOM_PPLIB_STATE_V2 *)((unsigned long)pstate + entry_size); } - return pstate; + + return NULL; } static const unsigned char soft_dummy_pp_table[] = { @@ -850,6 +884,8 @@ int pp_tables_get_response_times(struct pp_hwmgr *hwmgr, PP_ASSERT_WITH_CODE(NULL != powerplay_tab, "Missing PowerPlay Table!", return -EINVAL); + PP_ASSERT_WITH_CODE(pp_table_has_space(hwmgr, 0, sizeof(*powerplay_tab)), + "Invalid PowerPlay Table!", return -EINVAL); *vol_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usVoltageTime); *bb_rep_time = (uint32_t)le16_to_cpu(powerplay_tab->usBackbiasTime); @@ -862,13 +898,21 @@ int pp_tables_get_num_of_entries(struct pp_hwmgr *hwmgr, { const StateArray *pstate_arrays; const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr); + u16 state_array_offset; if (powerplay_table == NULL) return -1; + if (!pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table))) + return -1; if (powerplay_table->sHeader.ucTableFormatRevision >= 6) { + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + if (!pp_table_has_space(hwmgr, state_array_offset, + sizeof(pstate_arrays->ucNumEntries))) + return -1; + pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + state_array_offset); *num_of_entries = (unsigned long)(pstate_arrays->ucNumEntries); } else @@ -895,49 +939,128 @@ int pp_tables_get_entry(struct pp_hwmgr *hwmgr, const NonClockInfoArray *pnon_clock_arrays; const ATOM_PPLIB_STATE *pstate_entry; + u16 state_array_offset; + u16 clock_info_array_offset; + u16 non_clock_info_array_offset; + size_t clock_info_offset; + size_t non_clock_info_offset; + size_t state_entry_offset; if (powerplay_table == NULL) return -1; + if (!pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table))) + return -1; ps->classification.bios_index = entry_index; if (powerplay_table->sHeader.ucTableFormatRevision >= 6) { + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + if (!pp_table_has_space(hwmgr, state_array_offset, + sizeof(pstate_arrays->ucNumEntries))) + return -1; + pstate_arrays = (StateArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + state_array_offset); + + if (entry_index >= pstate_arrays->ucNumEntries) + return -1; - if (entry_index > pstate_arrays->ucNumEntries) + pstate_entry_v2 = get_state_entry_v2(hwmgr, pstate_arrays, + state_array_offset, + entry_index); + if (!pstate_entry_v2) + return -1; + + clock_info_array_offset = + le16_to_cpu(powerplay_table->usClockInfoArrayOffset); + if (!pp_table_has_space(hwmgr, clock_info_array_offset, + sizeof(*pclock_arrays))) return -1; - pstate_entry_v2 = get_state_entry_v2(pstate_arrays, entry_index); pclock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset)); + clock_info_array_offset); + if (!pclock_arrays->ucEntrySize) + return -1; + + non_clock_info_array_offset = + le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset); + if (!pp_table_has_space(hwmgr, non_clock_info_array_offset, + sizeof(*pnon_clock_arrays))) + return -1; pnon_clock_arrays = (NonClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset)); + non_clock_info_array_offset); + if (!pnon_clock_arrays->ucEntrySize || + pnon_clock_arrays->ucEntrySize < ATOM_PPLIB_NONCLOCKINFO_VER1 || + (pnon_clock_arrays->ucEntrySize > ATOM_PPLIB_NONCLOCKINFO_VER1 && + pnon_clock_arrays->ucEntrySize < ATOM_PPLIB_NONCLOCKINFO_VER2) || + pstate_entry_v2->nonClockInfoIndex >= pnon_clock_arrays->ucNumEntries) + return -1; + non_clock_info_offset = non_clock_info_array_offset + + offsetof(NonClockInfoArray, nonClockInfo) + + pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize; + if (!pp_table_has_space(hwmgr, non_clock_info_offset, + pnon_clock_arrays->ucEntrySize)) + return -1; pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)(pnon_clock_arrays->nonClockInfo) + (pstate_entry_v2->nonClockInfoIndex * pnon_clock_arrays->ucEntrySize)); result = init_non_clock_fields(hwmgr, ps, pnon_clock_arrays->ucEntrySize, pnon_clock_info); for (i = 0; i < pstate_entry_v2->ucNumDPMLevels; i++) { - const void *pclock_info = (const void *)( - (unsigned long)(pclock_arrays->clockInfo) + - (pstate_entry_v2->clockInfoIndex[i] * pclock_arrays->ucEntrySize)); + const void *pclock_info; + + if (pstate_entry_v2->clockInfoIndex[i] >= + pclock_arrays->ucNumEntries) + return -1; + + clock_info_offset = clock_info_array_offset + + offsetof(ClockInfoArray, clockInfo) + + pstate_entry_v2->clockInfoIndex[i] * pclock_arrays->ucEntrySize; + if (!pp_table_has_space(hwmgr, clock_info_offset, + pclock_arrays->ucEntrySize)) + return -1; + + pclock_info = (const void *) + ((unsigned long)(pclock_arrays->clockInfo) + + (pstate_entry_v2->clockInfoIndex[i] * + pclock_arrays->ucEntrySize)); res = func(hwmgr, &ps->hardware, i, pclock_info); if ((0 == result) && (0 != res)) result = res; } } else { - if (entry_index > powerplay_table->ucNumStates) + if (entry_index >= powerplay_table->ucNumStates || + !powerplay_table->ucStateEntrySize || + !powerplay_table->ucNonClockSize || + powerplay_table->ucNonClockSize < ATOM_PPLIB_NONCLOCKINFO_VER1 || + (powerplay_table->ucNonClockSize > ATOM_PPLIB_NONCLOCKINFO_VER1 && + powerplay_table->ucNonClockSize < ATOM_PPLIB_NONCLOCKINFO_VER2) || + !powerplay_table->ucClockInfoSize) + return -1; + + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + state_entry_offset = state_array_offset + + entry_index * powerplay_table->ucStateEntrySize; + if (!pp_table_has_space(hwmgr, state_entry_offset, + powerplay_table->ucStateEntrySize)) return -1; pstate_entry = (ATOM_PPLIB_STATE *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usStateArrayOffset) + + state_array_offset + entry_index * powerplay_table->ucStateEntrySize); + non_clock_info_array_offset = + le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset); + non_clock_info_offset = non_clock_info_array_offset + + pstate_entry->ucNonClockStateIndex * powerplay_table->ucNonClockSize; + if (!pp_table_has_space(hwmgr, non_clock_info_offset, + powerplay_table->ucNonClockSize)) + return -1; + pnon_clock_info = (ATOM_PPLIB_NONCLOCK_INFO *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usNonClockInfoArrayOffset) + + non_clock_info_array_offset + pstate_entry->ucNonClockStateIndex * powerplay_table->ucNonClockSize); @@ -946,12 +1069,23 @@ int pp_tables_get_entry(struct pp_hwmgr *hwmgr, pnon_clock_info); for (i = 0; i < powerplay_table->ucStateEntrySize-1; i++) { - const void *pclock_info = (const void *)((unsigned long)powerplay_table + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset) + + const void *pclock_info; + + clock_info_array_offset = + le16_to_cpu(powerplay_table->usClockInfoArrayOffset); + clock_info_offset = clock_info_array_offset + + pstate_entry->ucClockStateIndices[i] * + powerplay_table->ucClockInfoSize; + if (!pp_table_has_space(hwmgr, clock_info_offset, + powerplay_table->ucClockInfoSize)) + return -1; + + pclock_info = (const void *)((unsigned long)powerplay_table + + clock_info_array_offset + pstate_entry->ucClockStateIndices[i] * powerplay_table->ucClockInfoSize); - int res = func(hwmgr, &ps->hardware, i, pclock_info); + res = func(hwmgr, &ps->hardware, i, pclock_info); if ((0 == result) && (0 != res)) result = res; @@ -1661,21 +1795,70 @@ static int get_vce_state_table_entry(struct pp_hwmgr *hwmgr, unsigned long *flag) { const ATOM_PPLIB_POWERPLAYTABLE *powerplay_table = get_powerplay_table(hwmgr); + const ATOM_PPLIB_VCE_State_Table *vce_state_table; + const ATOM_PPLIB_VCE_State_Record *record; + const VCEClockInfoArray *vce_clock_info_array; + const VCEClockInfo *vce_clock_info; + const ClockInfoArray *clock_arrays; + u16 vce_state_table_offset; + u16 vce_clock_info_array_offset; + u16 clock_info_array_offset; + unsigned long clockInfoIndex; + size_t record_offset; + size_t vce_clock_info_offset; + size_t clock_info_offset; + + if (!powerplay_table || !pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table))) + return -1; - const ATOM_PPLIB_VCE_State_Table *vce_state_table = get_vce_state_table(hwmgr, powerplay_table); + vce_state_table_offset = get_vce_state_table_offset(hwmgr, powerplay_table); + vce_state_table = get_vce_state_table(hwmgr, powerplay_table); + if (!vce_state_table || i >= vce_state_table->numEntries) + return -1; + + record_offset = vce_state_table_offset + + offsetof(ATOM_PPLIB_VCE_State_Table, entries) + + i * sizeof(*record); + if (!pp_table_has_space(hwmgr, record_offset, sizeof(*record))) + return -1; + + record = &vce_state_table->entries[i]; + + vce_clock_info_array_offset = + get_vce_clock_info_array_offset(hwmgr, powerplay_table); + if (!pp_table_has_space(hwmgr, vce_clock_info_array_offset, + sizeof(vce_clock_info_array->ucNumEntries))) + return -1; - unsigned short vce_clock_info_array_offset = get_vce_clock_info_array_offset(hwmgr, powerplay_table); + vce_clock_info_array = (const VCEClockInfoArray *) + (((unsigned long)powerplay_table) + vce_clock_info_array_offset); + if (record->ucVCEClockInfoIndex >= vce_clock_info_array->ucNumEntries) + return -1; - const VCEClockInfoArray *vce_clock_info_array = (const VCEClockInfoArray *)(((unsigned long) powerplay_table) + vce_clock_info_array_offset); + vce_clock_info_offset = vce_clock_info_array_offset + + offsetof(VCEClockInfoArray, entries) + + record->ucVCEClockInfoIndex * sizeof(*vce_clock_info); + if (!pp_table_has_space(hwmgr, vce_clock_info_offset, sizeof(*vce_clock_info))) + return -1; - const ClockInfoArray *clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usClockInfoArrayOffset)); + vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex]; - const ATOM_PPLIB_VCE_State_Record *record = &vce_state_table->entries[i]; + clock_info_array_offset = le16_to_cpu(powerplay_table->usClockInfoArrayOffset); + if (!pp_table_has_space(hwmgr, clock_info_array_offset, + sizeof(*clock_arrays))) + return -1; - const VCEClockInfo *vce_clock_info = &vce_clock_info_array->entries[record->ucVCEClockInfoIndex]; + clock_arrays = (ClockInfoArray *)(((unsigned long)powerplay_table) + + clock_info_array_offset); + clockInfoIndex = record->ucClockInfoIndex & 0x3F; + if (!clock_arrays->ucEntrySize || clockInfoIndex >= clock_arrays->ucNumEntries) + return -1; - unsigned long clockInfoIndex = record->ucClockInfoIndex & 0x3F; + clock_info_offset = clock_info_array_offset + + offsetof(ClockInfoArray, clockInfo) + + clockInfoIndex * clock_arrays->ucEntrySize; + if (!pp_table_has_space(hwmgr, clock_info_offset, clock_arrays->ucEntrySize)) + return -1; *flag = (record->ucClockInfoIndex >> NUM_BITS_CLOCK_INFO_ARRAY_INDEX); @@ -1699,6 +1882,10 @@ static int pp_tables_initialize(struct pp_hwmgr *hwmgr) hwmgr->need_pp_table_upload = true; powerplay_table = get_powerplay_table(hwmgr); + PP_ASSERT_WITH_CODE((powerplay_table), + "Missing PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE(pp_table_has_space(hwmgr, 0, sizeof(*powerplay_table)), + "Invalid PowerPlay Table!", return -1); result = init_powerplay_tables(hwmgr, powerplay_table); @@ -1801,4 +1988,3 @@ const struct pp_table_func pptable_funcs = { .pptable_get_vce_state_table_entry = get_vce_state_table_entry, }; - 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 95bf187f02a5..1f1bb274685a 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -2216,12 +2216,24 @@ static int smu7_patch_voltage_dependency_tables_with_lookup_table( if (data->vdd_gfx_control == SMU7_VOLTAGE_CONTROL_BY_SVID2) { for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) { voltage_id = sclk_table->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddgfx_lookup_table->count) { + pr_err("amdgpu: sclk[%u] vddgfx index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddgfx_lookup_table->count); + return -EINVAL; + } sclk_table->entries[entry_id].vddgfx = table_info->vddgfx_lookup_table->entries[voltage_id].us_vdd; } } else { for (entry_id = 0; entry_id < sclk_table->count; ++entry_id) { voltage_id = sclk_table->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: sclk[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } sclk_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } @@ -2229,12 +2241,24 @@ static int smu7_patch_voltage_dependency_tables_with_lookup_table( for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) { voltage_id = mclk_table->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: mclk[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } mclk_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } for (entry_id = 0; entry_id < mm_table->count; ++entry_id) { voltage_id = mm_table->entries[entry_id].vddcInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: mm[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } mm_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } @@ -5648,23 +5672,29 @@ static int smu7_odn_edit_dpm_table(struct pp_hwmgr *hwmgr, } for (i = 0; i < size; i += 3) { - if (i + 3 > size || input[i] >= podn_dpm_table_in_backend->num_of_pl) { - pr_info("invalid clock voltage input \n"); - return 0; + if (i + 3 > size) { + pr_info("truncated clock/voltage input\n"); + return -EINVAL; } - input_level = input[i]; - input_clk = input[i+1] * 100; - input_vol = input[i+2]; - - if (smu7_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) { - podn_dpm_table_in_backend->entries[input_level].clock = input_clk; - podn_vdd_dep_in_backend->entries[input_level].clk = input_clk; - podn_dpm_table_in_backend->entries[input_level].vddc = input_vol; - podn_vdd_dep_in_backend->entries[input_level].vddc = input_vol; - podn_vdd_dep_in_backend->entries[input_level].vddgfx = input_vol; - } else { + if (input[i] < 0 || input[i] >= podn_dpm_table_in_backend->num_of_pl) { + pr_info("invalid clock/voltage level\n"); return -EINVAL; } + input_clk = input[i + 1] * 100; + input_vol = input[i + 2]; + if (!smu7_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) + return -EINVAL; + } + + for (i = 0; i < size; i += 3) { + input_level = input[i]; + input_clk = input[i + 1] * 100; + input_vol = input[i + 2]; + podn_dpm_table_in_backend->entries[input_level].clock = input_clk; + podn_vdd_dep_in_backend->entries[input_level].clk = input_clk; + podn_dpm_table_in_backend->entries[input_level].vddc = input_vol; + podn_vdd_dep_in_backend->entries[input_level].vddc = input_vol; + podn_vdd_dep_in_backend->entries[input_level].vddgfx = input_vol; } return 0; 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 4b92b52aba2b..0e237feb1629 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c @@ -685,10 +685,18 @@ static int vega10_patch_voltage_dependency_tables_with_lookup_table( case 3: vdt = table_info->vdd_dep_on_pixclk; break; case 4: vdt = table_info->vdd_dep_on_dispclk; break; case 5: vdt = table_info->vdd_dep_on_phyclk; break; + default: + continue; } for (entry_id = 0; entry_id < vdt->count; entry_id++) { voltage_id = vdt->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: clk_dep[%u][%u] vddc index %u out of bounds (%u)\n", + i, entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } vdt->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } @@ -696,23 +704,48 @@ static int vega10_patch_voltage_dependency_tables_with_lookup_table( for (entry_id = 0; entry_id < mm_table->count; ++entry_id) { voltage_id = mm_table->entries[entry_id].vddcInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: mm[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } mm_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; } for (entry_id = 0; entry_id < mclk_table->count; ++entry_id) { voltage_id = mclk_table->entries[entry_id].vddInd; + if (voltage_id >= table_info->vddc_lookup_table->count) { + pr_err("amdgpu: mclk[%u] vddc index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddc_lookup_table->count); + return -EINVAL; + } mclk_table->entries[entry_id].vddc = table_info->vddc_lookup_table->entries[voltage_id].us_vdd; + voltage_id = mclk_table->entries[entry_id].vddciInd; + if (voltage_id >= table_info->vddci_lookup_table->count) { + pr_err("amdgpu: mclk[%u] vddci index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddci_lookup_table->count); + return -EINVAL; + } mclk_table->entries[entry_id].vddci = table_info->vddci_lookup_table->entries[voltage_id].us_vdd; + voltage_id = mclk_table->entries[entry_id].mvddInd; + if (voltage_id >= table_info->vddmem_lookup_table->count) { + pr_err("amdgpu: mclk[%u] vddmem index %u out of bounds (%u)\n", + entry_id, voltage_id, + table_info->vddmem_lookup_table->count); + return -EINVAL; + } mclk_table->entries[entry_id].mvdd = table_info->vddmem_lookup_table->entries[voltage_id].us_vdd; } - return 0; } @@ -5215,6 +5248,11 @@ static int vega10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui uint8_t min_active_level; uint32_t power_profile_mode = input[size]; + if (power_profile_mode > PP_SMC_POWER_PROFILE_CUSTOM) { + pr_err("Invalid power profile mode %u\n", power_profile_mode); + return -EINVAL; + } + if (power_profile_mode == PP_SMC_POWER_PROFILE_CUSTOM) { if (size != 0 && size != 4) return -EINVAL; @@ -5230,6 +5268,10 @@ static int vega10_set_power_profile_mode(struct pp_hwmgr *hwmgr, long *input, ui return -EINVAL; } + if ((input[0] & ~0xFF) || (input[1] & ~0xFF) || + (input[2] & ~0xFF) || (input[3] & ~0xFF)) + return -EINVAL; + data->custom_profile_mode[0] = busy_set_point = input[0]; data->custom_profile_mode[1] = FPS = input[1]; data->custom_profile_mode[2] = use_rlc_busy = input[2]; @@ -5454,11 +5496,9 @@ static int vega10_odn_edit_dpm_table(struct pp_hwmgr *hwmgr, if (PP_OD_EDIT_SCLK_VDDC_TABLE == type) { dpm_table = &data->dpm_table.gfx_table; podn_vdd_dep_table = &data->odn_dpm_table.vdd_dep_on_sclk; - data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK; } else if (PP_OD_EDIT_MCLK_VDDC_TABLE == type) { dpm_table = &data->dpm_table.mem_table; podn_vdd_dep_table = &data->odn_dpm_table.vdd_dep_on_mclk; - data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK; } else if (PP_OD_RESTORE_DEFAULT_TABLE == type) { memcpy(&(data->dpm_table), &(data->golden_dpm_table), sizeof(struct vega10_dpm_table)); vega10_odn_initial_default_setting(hwmgr); @@ -5476,21 +5516,32 @@ static int vega10_odn_edit_dpm_table(struct pp_hwmgr *hwmgr, } for (i = 0; i < size; i += 3) { - if (i + 3 > size || input[i] >= podn_vdd_dep_table->count) { - pr_info("invalid clock voltage input\n"); - return 0; + if (i + 3 > size) { + pr_info("truncated clock/voltage input\n"); + return -EINVAL; } - input_level = input[i]; - input_clk = input[i+1] * 100; - input_vol = input[i+2]; - - if (vega10_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) { - dpm_table->dpm_levels[input_level].value = input_clk; - podn_vdd_dep_table->entries[input_level].clk = input_clk; - podn_vdd_dep_table->entries[input_level].vddc = input_vol; - } else { + if (input[i] < 0 || input[i] >= podn_vdd_dep_table->count) { + pr_info("invalid clock/voltage level\n"); return -EINVAL; } + input_clk = input[i + 1] * 100; + input_vol = input[i + 2]; + if (!vega10_check_clk_voltage_valid(hwmgr, type, input_clk, input_vol)) + return -EINVAL; + } + + if (type == PP_OD_EDIT_SCLK_VDDC_TABLE) + data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_SCLK; + else + data->need_update_dpm_table |= DPMTABLE_OD_UPDATE_MCLK; + + for (i = 0; i < size; i += 3) { + input_level = input[i]; + input_clk = input[i + 1] * 100; + input_vol = input[i + 2]; + dpm_table->dpm_levels[input_level].value = input_clk; + podn_vdd_dep_table->entries[input_level].clk = input_clk; + podn_vdd_dep_table->entries[input_level].vddc = input_vol; } vega10_odn_update_soc_table(hwmgr, type); return 0; diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c index 052d139584fd..62b1e068f90d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_processpptables.c @@ -63,14 +63,57 @@ static const void *get_powerplay_table(struct pp_hwmgr *hwmgr) return table_address; } -static int check_powerplay_tables( - struct pp_hwmgr *hwmgr, - const ATOM_Vega10_POWERPLAYTABLE *powerplay_table) +static bool vega10_pp_table_has_space(struct pp_hwmgr *hwmgr, size_t offset, + size_t size) +{ + size_t table_size = hwmgr->soft_pp_table_size; + + return offset <= table_size && size <= table_size - offset; +} + +static int get_vega10_subtable(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + u16 table_offset, size_t table_size, const void **table) +{ + PP_ASSERT_WITH_CODE((table_offset != 0), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *table = (const void *)(((unsigned long)powerplay_table) + table_offset); + + return 0; +} + +static int validate_vega10_table_entries(struct pp_hwmgr *hwmgr, + u16 table_offset, size_t entries_offset, + u8 num_entries, size_t entry_size) +{ + size_t table_size; + + PP_ASSERT_WITH_CODE((num_entries != 0), + "Invalid PowerPlay Table!", return -1); + + table_size = entries_offset + num_entries * entry_size; + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + return 0; +} + +static int get_vega10_state_array(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_State_Array **state_array) { const ATOM_Vega10_State_Array *state_arrays; + u16 state_array_offset; + size_t state_array_size; + size_t table_size = hwmgr->soft_pp_table_size; - state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usStateArrayOffset)); + PP_ASSERT_WITH_CODE((table_size >= sizeof(*powerplay_table)), + "Invalid PowerPlay Table!", return -1); PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >= ATOM_Vega10_TABLE_REVISION_VEGA10), @@ -79,12 +122,321 @@ static int check_powerplay_tables( "State table is not set!", return -1); PP_ASSERT_WITH_CODE(powerplay_table->sHeader.structuresize > 0, "Invalid PowerPlay Table!", return -1); + + state_array_offset = le16_to_cpu(powerplay_table->usStateArrayOffset); + PP_ASSERT_WITH_CODE((state_array_offset <= + table_size - sizeof(*state_arrays)), + "Invalid PowerPlay Table!", return -1); + + state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)powerplay_table) + + state_array_offset); PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0, "Invalid PowerPlay Table!", return -1); + state_array_size = struct_size(state_arrays, states, state_arrays->ucNumEntries); + PP_ASSERT_WITH_CODE((state_array_size <= table_size - state_array_offset), + "Invalid PowerPlay Table!", return -1); + + *state_array = state_arrays; + return 0; } +static int get_vega10_gfxclk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_GFXCLK_Dependency_Table **gfxclk_dep_table) +{ + const ATOM_Vega10_GFXCLK_Dependency_Table *table; + u16 table_offset; + size_t table_size; + size_t entry_size; + + table_offset = le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset); + if (!table_offset) + return -EINVAL; + + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + sizeof(*table))), + "Invalid PowerPlay Table!", return -1); + + table = (const ATOM_Vega10_GFXCLK_Dependency_Table *) + (((unsigned long)powerplay_table) + table_offset); + PP_ASSERT_WITH_CODE((table->ucNumEntries != 0), + "Invalid PowerPlay Table!", return -1); + + if (table->ucRevId == 0) + entry_size = sizeof(ATOM_Vega10_GFXCLK_Dependency_Record); + else if (table->ucRevId == 1) + entry_size = sizeof(ATOM_Vega10_GFXCLK_Dependency_Record_V2); + else + PP_ASSERT_WITH_CODE(false, + "Unsupported GFXClockDependencyTable Revision!", + return -EINVAL); + + table_size = offsetof(ATOM_Vega10_GFXCLK_Dependency_Table, entries) + + table->ucNumEntries * entry_size; + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *gfxclk_dep_table = table; + + return 0; +} + +static int get_vega10_clk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + u16 table_offset, + const ATOM_Vega10_SOCCLK_Dependency_Table **clk_dep_table) +{ + const ATOM_Vega10_SOCCLK_Dependency_Table *table; + int ret; + + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_SOCCLK_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_CLK_Dependency_Record)); + if (ret) + return ret; + + *clk_dep_table = table; + + return 0; +} + +static int get_vega10_mclk_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_MCLK_Dependency_Table **mclk_dep_table) +{ + const ATOM_Vega10_MCLK_Dependency_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usMclkDependencyTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_MCLK_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_MCLK_Dependency_Record)); + if (ret) + return ret; + + *mclk_dep_table = table; + + return 0; +} + +static int get_vega10_mm_dependency_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_MM_Dependency_Table **mm_dep_table) +{ + const ATOM_Vega10_MM_Dependency_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usMMDependencyTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_MM_Dependency_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_MM_Dependency_Record)); + if (ret) + return ret; + + *mm_dep_table = table; + + return 0; +} + +static int get_vega10_pcie_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const Vega10_PPTable_Generic_SubTable_Header **pcie_table) +{ + const ATOM_Vega10_PCIE_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usPCIETableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + if (!table->ucNumEntries) { + *pcie_table = (const Vega10_PPTable_Generic_SubTable_Header *)table; + return 0; + } + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_PCIE_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_PCIE_Record)); + if (ret) + return ret; + + *pcie_table = (const Vega10_PPTable_Generic_SubTable_Header *)table; + + return 0; +} + +static int get_vega10_hard_limit_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_Hard_Limit_Table **hard_limit_table) +{ + const ATOM_Vega10_Hard_Limit_Table *table; + u16 table_offset; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usHardLimitTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + ret = validate_vega10_table_entries(hwmgr, table_offset, + offsetof(ATOM_Vega10_Hard_Limit_Table, + entries), + table->ucNumEntries, + sizeof(ATOM_Vega10_Hard_Limit_Record)); + if (ret) + return ret; + + *hard_limit_table = table; + + return 0; +} + +static int get_vega10_thermal_controller_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const ATOM_Vega10_Thermal_Controller **thermal_controller) +{ + u16 table_offset; + + table_offset = le16_to_cpu(powerplay_table->usThermalControllerOffset); + + return get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(**thermal_controller), + (const void **)thermal_controller); +} + +static int get_vega10_fan_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const Vega10_PPTable_Generic_SubTable_Header **fan_table) +{ + const Vega10_PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t table_size; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usFanTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId == 10) + table_size = sizeof(ATOM_Vega10_Fan_Table); + else if (header->ucRevId == 0xb) + table_size = sizeof(ATOM_Vega10_Fan_Table_V2); + else if (header->ucRevId > 0xb) + table_size = sizeof(ATOM_Vega10_Fan_Table_V3); + else + table_size = sizeof(*header); + + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *fan_table = header; + + return 0; +} + +static int get_vega10_power_tune_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + const Vega10_PPTable_Generic_SubTable_Header **power_tune_table) +{ + const Vega10_PPTable_Generic_SubTable_Header *header; + u16 table_offset; + size_t table_size; + int ret; + + table_offset = le16_to_cpu(powerplay_table->usPowerTuneTableOffset); + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*header), (const void **)&header); + if (ret) + return ret; + + if (header->ucRevId == 5) + table_size = sizeof(ATOM_Vega10_PowerTune_Table); + else if (header->ucRevId == 6) + table_size = sizeof(ATOM_Vega10_PowerTune_Table_V2); + else + table_size = sizeof(ATOM_Vega10_PowerTune_Table_V3); + + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *power_tune_table = header; + + return 0; +} + +static int get_vega10_voltage_lookup_table(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table, + u16 table_offset, uint32_t max_levels, + const ATOM_Vega10_Voltage_Lookup_Table **lookup_table) +{ + const ATOM_Vega10_Voltage_Lookup_Table *table; + size_t table_size; + int ret; + + ret = get_vega10_subtable(hwmgr, powerplay_table, table_offset, + sizeof(*table), (const void **)&table); + if (ret) + return ret; + + PP_ASSERT_WITH_CODE((table->ucNumEntries != 0 && + table->ucNumEntries <= max_levels), + "Invalid PowerPlay Table!", return -1); + + table_size = offsetof(ATOM_Vega10_Voltage_Lookup_Table, entries) + + table->ucNumEntries * sizeof(ATOM_Vega10_Voltage_Lookup_Record); + PP_ASSERT_WITH_CODE((vega10_pp_table_has_space(hwmgr, table_offset, + table_size)), + "Invalid PowerPlay Table!", return -1); + + *lookup_table = table; + + return 0; +} + +static int check_powerplay_tables(struct pp_hwmgr *hwmgr, + const ATOM_Vega10_POWERPLAYTABLE *powerplay_table) +{ + const ATOM_Vega10_State_Array *state_arrays; + + return get_vega10_state_array(hwmgr, powerplay_table, &state_arrays); +} + static int set_platform_caps(struct pp_hwmgr *hwmgr, uint32_t powerplay_caps) { set_hw_cap( @@ -124,14 +476,16 @@ static int init_thermal_controller( const ATOM_Vega10_Fan_Table *fan_table_v1; const ATOM_Vega10_Fan_Table_V2 *fan_table_v2; const ATOM_Vega10_Fan_Table_V3 *fan_table_v3; - - thermal_controller = (ATOM_Vega10_Thermal_Controller *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usThermalControllerOffset)); + int ret; PP_ASSERT_WITH_CODE((powerplay_table->usThermalControllerOffset != 0), "Thermal controller table not set!", return -EINVAL); + ret = get_vega10_thermal_controller_table(hwmgr, powerplay_table, + &thermal_controller); + if (ret) + return ret; + hwmgr->thermal_controller.ucType = thermal_controller->ucType; hwmgr->thermal_controller.ucI2cLine = thermal_controller->ucI2cLine; hwmgr->thermal_controller.ucI2cAddress = thermal_controller->ucI2cAddress; @@ -160,9 +514,9 @@ static int init_thermal_controller( if (!powerplay_table->usFanTableOffset) return 0; - header = (const Vega10_PPTable_Generic_SubTable_Header *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usFanTableOffset)); + ret = get_vega10_fan_table(hwmgr, powerplay_table, &header); + if (ret) + return ret; if (header->ucRevId == 10) { fan_table_v1 = (ATOM_Vega10_Fan_Table *)header; @@ -307,12 +661,15 @@ static int init_over_drive_limits( struct pp_hwmgr *hwmgr, const ATOM_Vega10_POWERPLAYTABLE *powerplay_table) { - const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table = - (const ATOM_Vega10_GFXCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset)); + const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table; bool is_acg_enabled = false; ATOM_Vega10_GFXCLK_Dependency_Record_V2 *patom_record_v2; + int ret; + + ret = get_vega10_gfxclk_dependency_table(hwmgr, powerplay_table, + &gfxclk_dep_table); + if (ret) + return ret; if (gfxclk_dep_table->ucRevId == 1) { patom_record_v2 = @@ -344,20 +701,28 @@ static int get_mm_clock_voltage_table( const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table) { uint32_t i; + uint32_t num_entries; const ATOM_Vega10_MM_Dependency_Record *mm_dependency_record; phm_ppt_v1_mm_clock_voltage_dependency_table *mm_table; PP_ASSERT_WITH_CODE((mm_dependency_table->ucNumEntries != 0), "Invalid PowerPlay Table!", return -1); - mm_table = kzalloc_flex(*mm_table, entries, - mm_dependency_table->ucNumEntries); + num_entries = min_t(uint32_t, mm_dependency_table->ucNumEntries, + pp_entries_max(hwmgr, mm_dependency_table, + sizeof(*mm_dependency_table), + sizeof(ATOM_Vega10_MM_Dependency_Record))); + if (num_entries < mm_dependency_table->ucNumEntries) + pr_warn("amdgpu: Vega10 MM dependency table: clamping ucNumEntries %u -> %u\n", + mm_dependency_table->ucNumEntries, num_entries); + + mm_table = kzalloc_flex(*mm_table, entries, num_entries); if (!mm_table) return -ENOMEM; - mm_table->count = mm_dependency_table->ucNumEntries; + mm_table->count = num_entries; - for (i = 0; i < mm_dependency_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { mm_dependency_record = &mm_dependency_table->entries[i]; mm_table->entries[i].vddcInd = mm_dependency_record->ucVddcInd; mm_table->entries[i].samclock = @@ -568,19 +933,27 @@ static int get_socclk_voltage_dependency_table( const ATOM_Vega10_SOCCLK_Dependency_Table *clk_dep_table) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_clock_voltage_dependency_table *clk_table; PP_ASSERT_WITH_CODE(clk_dep_table->ucNumEntries, "Invalid PowerPlay Table!", return -1); - clk_table = kzalloc_flex(*clk_table, entries, - clk_dep_table->ucNumEntries); + num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_CLK_Dependency_Record))); + if (num_entries < clk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 SOCCLK dependency table: clamping ucNumEntries %u -> %u\n", + clk_dep_table->ucNumEntries, num_entries); + + clk_table = kzalloc_flex(*clk_table, entries, num_entries); if (!clk_table) return -ENOMEM; - clk_table->count = (uint32_t)clk_dep_table->ucNumEntries; + clk_table->count = num_entries; - for (i = 0; i < clk_dep_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { clk_table->entries[i].vddInd = clk_dep_table->entries[i].ucVddInd; clk_table->entries[i].clk = @@ -598,19 +971,27 @@ static int get_mclk_voltage_dependency_table( const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_clock_voltage_dependency_table *mclk_table; PP_ASSERT_WITH_CODE(mclk_dep_table->ucNumEntries, "Invalid PowerPlay Table!", return -1); - mclk_table = kzalloc_flex(*mclk_table, entries, - mclk_dep_table->ucNumEntries); + num_entries = min_t(uint32_t, mclk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, mclk_dep_table, + sizeof(*mclk_dep_table), + sizeof(ATOM_Vega10_MCLK_Dependency_Record))); + if (num_entries < mclk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 MCLK dependency table: clamping ucNumEntries %u -> %u\n", + mclk_dep_table->ucNumEntries, num_entries); + + mclk_table = kzalloc_flex(*mclk_table, entries, num_entries); if (!mclk_table) return -ENOMEM; - mclk_table->count = (uint32_t)mclk_dep_table->ucNumEntries; + mclk_table->count = num_entries; - for (i = 0; i < mclk_dep_table->ucNumEntries; i++) { + for (i = 0; i < num_entries; i++) { mclk_table->entries[i].vddInd = mclk_dep_table->entries[i].ucVddInd; mclk_table->entries[i].vddciInd = @@ -633,6 +1014,7 @@ static int get_gfxclk_voltage_dependency_table( const ATOM_Vega10_GFXCLK_Dependency_Table *clk_dep_table) { uint32_t i; + uint32_t num_entries; struct phm_ppt_v1_clock_voltage_dependency_table *clk_table; ATOM_Vega10_GFXCLK_Dependency_Record_V2 *patom_record_v2; @@ -640,15 +1022,34 @@ static int get_gfxclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0), "Invalid PowerPlay Table!", return -1); - clk_table = kzalloc_flex(*clk_table, entries, - clk_dep_table->ucNumEntries); + if (clk_dep_table->ucRevId == 0) { + num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_GFXCLK_Dependency_Record))); + } else if (clk_dep_table->ucRevId == 1) { + num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_GFXCLK_Dependency_Record_V2))); + } else { + PP_ASSERT_WITH_CODE(false, + "Unsupported GFXClockDependencyTable Revision!", + return -EINVAL); + } + + if (num_entries < clk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 GFXCLK dependency table: clamping ucNumEntries %u -> %u\n", + clk_dep_table->ucNumEntries, num_entries); + + clk_table = kzalloc_flex(*clk_table, entries, num_entries); if (!clk_table) return -ENOMEM; - clk_table->count = clk_dep_table->ucNumEntries; + clk_table->count = num_entries; if (clk_dep_table->ucRevId == 0) { - for (i = 0; i < clk_table->count; i++) { + for (i = 0; i < num_entries; i++) { clk_table->entries[i].vddInd = clk_dep_table->entries[i].ucVddInd; clk_table->entries[i].clk = @@ -661,9 +1062,9 @@ static int get_gfxclk_voltage_dependency_table( clk_table->entries[i].sclk_offset = le16_to_cpu(clk_dep_table->entries[i].usAVFSOffset); } - } else if (clk_dep_table->ucRevId == 1) { + } else { patom_record_v2 = (ATOM_Vega10_GFXCLK_Dependency_Record_V2 *)clk_dep_table->entries; - for (i = 0; i < clk_table->count; i++) { + for (i = 0; i < num_entries; i++) { clk_table->entries[i].vddInd = patom_record_v2->ucVddInd; clk_table->entries[i].clk = @@ -677,11 +1078,6 @@ static int get_gfxclk_voltage_dependency_table( le16_to_cpu(patom_record_v2->usAVFSOffset); patom_record_v2++; } - } else { - kfree(clk_table); - PP_ASSERT_WITH_CODE(false, - "Unsupported GFXClockDependencyTable Revision!", - return -EINVAL); } *pp_vega10_clk_dep_table = clk_table; @@ -696,20 +1092,28 @@ static int get_pix_clk_voltage_dependency_table( const ATOM_Vega10_PIXCLK_Dependency_Table *clk_dep_table) { uint32_t i; + uint32_t num_entries; struct phm_ppt_v1_clock_voltage_dependency_table *clk_table; PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0), "Invalid PowerPlay Table!", return -1); - clk_table = kzalloc_flex(*clk_table, entries, - clk_dep_table->ucNumEntries); + num_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_CLK_Dependency_Record))); + if (num_entries < clk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 PIXCLK dependency table: clamping ucNumEntries %u -> %u\n", + clk_dep_table->ucNumEntries, num_entries); + + clk_table = kzalloc_flex(*clk_table, entries, num_entries); if (!clk_table) return -ENOMEM; - clk_table->count = clk_dep_table->ucNumEntries; + clk_table->count = num_entries; - for (i = 0; i < clk_table->count; i++) { + for (i = 0; i < num_entries; i++) { clk_table->entries[i].vddInd = clk_dep_table->entries[i].ucVddInd; clk_table->entries[i].clk = @@ -728,6 +1132,7 @@ static int get_dcefclk_voltage_dependency_table( const ATOM_Vega10_DCEFCLK_Dependency_Table *clk_dep_table) { uint32_t i; + uint32_t safe_entries; uint8_t num_entries; struct phm_ppt_v1_clock_voltage_dependency_table *clk_table; @@ -738,6 +1143,14 @@ static int get_dcefclk_voltage_dependency_table( PP_ASSERT_WITH_CODE((clk_dep_table->ucNumEntries != 0), "Invalid PowerPlay Table!", return -1); + safe_entries = min_t(uint32_t, clk_dep_table->ucNumEntries, + pp_entries_max(hwmgr, clk_dep_table, + sizeof(*clk_dep_table), + sizeof(ATOM_Vega10_CLK_Dependency_Record))); + if (safe_entries < clk_dep_table->ucNumEntries) + pr_warn("amdgpu: Vega10 DCEFCLK dependency table: clamping ucNumEntries %u -> %u\n", + clk_dep_table->ucNumEntries, safe_entries); + /* * workaround needed to add another DPM level for pioneer cards * as VBIOS is locked down. @@ -747,12 +1160,12 @@ static int get_dcefclk_voltage_dependency_table( dev_id = adev->pdev->device; rev_id = adev->pdev->revision; - if (dev_id == 0x6863 && rev_id == 0 && - clk_dep_table->entries[clk_dep_table->ucNumEntries - 1].ulClk < 90000) - num_entries = clk_dep_table->ucNumEntries + 1 > NUM_DSPCLK_LEVELS ? - NUM_DSPCLK_LEVELS : clk_dep_table->ucNumEntries + 1; + if (dev_id == 0x6863 && rev_id == 0 && safe_entries > 0 && + clk_dep_table->entries[safe_entries - 1].ulClk < 90000) + num_entries = safe_entries + 1 > NUM_DSPCLK_LEVELS ? + NUM_DSPCLK_LEVELS : safe_entries + 1; else - num_entries = clk_dep_table->ucNumEntries; + num_entries = safe_entries; clk_table = kzalloc_flex(*clk_table, entries, num_entries); @@ -761,7 +1174,7 @@ static int get_dcefclk_voltage_dependency_table( clk_table->count = (uint32_t)num_entries; - for (i = 0; i < clk_dep_table->ucNumEntries; i++) { + for (i = 0; i < safe_entries; i++) { clk_table->entries[i].vddInd = clk_dep_table->entries[i].ucVddInd; clk_table->entries[i].clk = @@ -873,51 +1286,13 @@ static int init_powerplay_extended_tables( int result = 0; struct phm_ppt_v2_information *pp_table_info = (struct phm_ppt_v2_information *)(hwmgr->pptable); - - const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table = - (const ATOM_Vega10_MM_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usMMDependencyTableOffset)); - const Vega10_PPTable_Generic_SubTable_Header *power_tune_table = - (const Vega10_PPTable_Generic_SubTable_Header *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPowerTuneTableOffset)); - const ATOM_Vega10_SOCCLK_Dependency_Table *socclk_dep_table = - (const ATOM_Vega10_SOCCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usSocclkDependencyTableOffset)); - const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table = - (const ATOM_Vega10_GFXCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usGfxclkDependencyTableOffset)); - const ATOM_Vega10_DCEFCLK_Dependency_Table *dcefclk_dep_table = - (const ATOM_Vega10_DCEFCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usDcefclkDependencyTableOffset)); - const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table = - (const ATOM_Vega10_MCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usMclkDependencyTableOffset)); - const ATOM_Vega10_Hard_Limit_Table *hard_limits = - (const ATOM_Vega10_Hard_Limit_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usHardLimitTableOffset)); - const Vega10_PPTable_Generic_SubTable_Header *pcie_table = - (const Vega10_PPTable_Generic_SubTable_Header *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPCIETableOffset)); - const ATOM_Vega10_PIXCLK_Dependency_Table *pixclk_dep_table = - (const ATOM_Vega10_PIXCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPixclkDependencyTableOffset)); - const ATOM_Vega10_PHYCLK_Dependency_Table *phyclk_dep_table = - (const ATOM_Vega10_PHYCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usPhyClkDependencyTableOffset)); - const ATOM_Vega10_DISPCLK_Dependency_Table *dispclk_dep_table = - (const ATOM_Vega10_DISPCLK_Dependency_Table *) - (((unsigned long) powerplay_table) + - le16_to_cpu(powerplay_table->usDispClkDependencyTableOffset)); + const ATOM_Vega10_MM_Dependency_Table *mm_dependency_table; + const Vega10_PPTable_Generic_SubTable_Header *power_tune_table; + const ATOM_Vega10_GFXCLK_Dependency_Table *gfxclk_dep_table; + const ATOM_Vega10_MCLK_Dependency_Table *mclk_dep_table; + const ATOM_Vega10_Hard_Limit_Table *hard_limits; + const Vega10_PPTable_Generic_SubTable_Header *pcie_table; + const ATOM_Vega10_SOCCLK_Dependency_Table *clk_dep_table; pp_table_info->vdd_dep_on_socclk = NULL; pp_table_info->vdd_dep_on_sclk = NULL; @@ -929,63 +1304,114 @@ static int init_powerplay_extended_tables( pp_table_info->vdd_dep_on_phyclk = NULL; pp_table_info->vdd_dep_on_dispclk = NULL; - if (powerplay_table->usMMDependencyTableOffset) - result = get_mm_clock_voltage_table(hwmgr, + if (powerplay_table->usMMDependencyTableOffset) { + result = get_vega10_mm_dependency_table(hwmgr, powerplay_table, + &mm_dependency_table); + if (!result) + result = get_mm_clock_voltage_table(hwmgr, &pp_table_info->mm_dep_table, mm_dependency_table); + } - if (!result && powerplay_table->usPowerTuneTableOffset) - result = get_tdp_table(hwmgr, + if (!result && powerplay_table->usPowerTuneTableOffset) { + result = get_vega10_power_tune_table(hwmgr, powerplay_table, + &power_tune_table); + if (!result) + result = get_tdp_table(hwmgr, &pp_table_info->tdp_table, power_tune_table); + } - if (!result && powerplay_table->usSocclkDependencyTableOffset) - result = get_socclk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usSocclkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usSocclkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_socclk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_socclk, - socclk_dep_table); + (const ATOM_Vega10_SOCCLK_Dependency_Table *) + clk_dep_table); + } - if (!result && powerplay_table->usGfxclkDependencyTableOffset) - result = get_gfxclk_voltage_dependency_table(hwmgr, - &pp_table_info->vdd_dep_on_sclk, - gfxclk_dep_table); + if (!result && powerplay_table->usGfxclkDependencyTableOffset) { + result = get_vega10_gfxclk_dependency_table(hwmgr, + powerplay_table, &gfxclk_dep_table); + if (!result) + result = get_gfxclk_voltage_dependency_table(hwmgr, + &pp_table_info->vdd_dep_on_sclk, + gfxclk_dep_table); + } - if (!result && powerplay_table->usPixclkDependencyTableOffset) - result = get_pix_clk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usPixclkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usPixclkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_pix_clk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_pixclk, (const ATOM_Vega10_PIXCLK_Dependency_Table *) - pixclk_dep_table); + clk_dep_table); + } - if (!result && powerplay_table->usPhyClkDependencyTableOffset) - result = get_pix_clk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usPhyClkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usPhyClkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_pix_clk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_phyclk, (const ATOM_Vega10_PIXCLK_Dependency_Table *) - phyclk_dep_table); + clk_dep_table); + } - if (!result && powerplay_table->usDispClkDependencyTableOffset) - result = get_pix_clk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usDispClkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usDispClkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_pix_clk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_dispclk, (const ATOM_Vega10_PIXCLK_Dependency_Table *) - dispclk_dep_table); + clk_dep_table); + } - if (!result && powerplay_table->usDcefclkDependencyTableOffset) - result = get_dcefclk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usDcefclkDependencyTableOffset) { + result = get_vega10_clk_dependency_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usDcefclkDependencyTableOffset), + &clk_dep_table); + if (!result) + result = get_dcefclk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_dcefclk, - dcefclk_dep_table); + (const ATOM_Vega10_DCEFCLK_Dependency_Table *) + clk_dep_table); + } - if (!result && powerplay_table->usMclkDependencyTableOffset) - result = get_mclk_voltage_dependency_table(hwmgr, + if (!result && powerplay_table->usMclkDependencyTableOffset) { + result = get_vega10_mclk_dependency_table(hwmgr, powerplay_table, + &mclk_dep_table); + if (!result) + result = get_mclk_voltage_dependency_table(hwmgr, &pp_table_info->vdd_dep_on_mclk, mclk_dep_table); + } - if (!result && powerplay_table->usPCIETableOffset) - result = get_pcie_table(hwmgr, + if (!result && powerplay_table->usPCIETableOffset) { + result = get_vega10_pcie_table(hwmgr, powerplay_table, + &pcie_table); + if (!result) + result = get_pcie_table(hwmgr, &pp_table_info->pcie_table, pcie_table); + } - if (!result && powerplay_table->usHardLimitTableOffset) - result = get_hard_limits(hwmgr, + if (!result && powerplay_table->usHardLimitTableOffset) { + result = get_vega10_hard_limit_table(hwmgr, powerplay_table, + &hard_limits); + if (!result) + result = get_hard_limits(hwmgr, &pp_table_info->max_clock_voltage_on_dc, hard_limits); + } hwmgr->dyn_state.max_clock_voltage_on_dc.sclk = pp_table_info->max_clock_voltage_on_dc.sclk; @@ -1034,18 +1460,28 @@ static int get_vddc_lookup_table( uint32_t max_levels) { uint32_t i; + uint32_t num_entries; phm_ppt_v1_voltage_lookup_table *table; PP_ASSERT_WITH_CODE((vddc_lookup_pp_tables->ucNumEntries != 0), "Invalid SOC_VDDD Lookup Table!", return 1); - table = kzalloc_flex(*table, entries, max_levels); + num_entries = min_t(uint32_t, vddc_lookup_pp_tables->ucNumEntries, + min_t(uint32_t, max_levels, + pp_entries_max(hwmgr, vddc_lookup_pp_tables, + sizeof(*vddc_lookup_pp_tables), + sizeof(ATOM_Vega10_Voltage_Lookup_Record)))); + if (num_entries < vddc_lookup_pp_tables->ucNumEntries) + pr_warn("amdgpu: Vega10 VddcLookup table: clamping ucNumEntries %u -> %u\n", + vddc_lookup_pp_tables->ucNumEntries, num_entries); + + table = kzalloc_flex(*table, entries, num_entries); if (!table) return -ENOMEM; - table->count = vddc_lookup_pp_tables->ucNumEntries; + table->count = num_entries; - for (i = 0; i < vddc_lookup_pp_tables->ucNumEntries; i++) + for (i = 0; i < num_entries; i++) table->entries[i].us_vdd = le16_to_cpu(vddc_lookup_pp_tables->entries[i].usVdd); @@ -1113,30 +1549,39 @@ static int init_dpm_2_parameters( } if (powerplay_table->usVddcLookupTableOffset) { - const ATOM_Vega10_Voltage_Lookup_Table *vddc_table = - (ATOM_Vega10_Voltage_Lookup_Table *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddcLookupTableOffset)); - result = get_vddc_lookup_table(hwmgr, - &pp_table_info->vddc_lookup_table, vddc_table, 8); + const ATOM_Vega10_Voltage_Lookup_Table *vddc_table; + + result = get_vega10_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddcLookupTableOffset), + 8, &vddc_table); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_info->vddc_lookup_table, + vddc_table, 8); } - if (powerplay_table->usVddmemLookupTableOffset) { - const ATOM_Vega10_Voltage_Lookup_Table *vdd_mem_table = - (ATOM_Vega10_Voltage_Lookup_Table *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddmemLookupTableOffset)); - result = get_vddc_lookup_table(hwmgr, - &pp_table_info->vddmem_lookup_table, vdd_mem_table, 4); + if (!result && powerplay_table->usVddmemLookupTableOffset) { + const ATOM_Vega10_Voltage_Lookup_Table *vdd_mem_table; + + result = get_vega10_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddmemLookupTableOffset), + 4, &vdd_mem_table); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_info->vddmem_lookup_table, + vdd_mem_table, 4); } - if (powerplay_table->usVddciLookupTableOffset) { - const ATOM_Vega10_Voltage_Lookup_Table *vddci_table = - (ATOM_Vega10_Voltage_Lookup_Table *) - (((unsigned long)powerplay_table) + - le16_to_cpu(powerplay_table->usVddciLookupTableOffset)); - result = get_vddc_lookup_table(hwmgr, - &pp_table_info->vddci_lookup_table, vddci_table, 4); + if (!result && powerplay_table->usVddciLookupTableOffset) { + const ATOM_Vega10_Voltage_Lookup_Table *vddci_table; + + result = get_vega10_voltage_lookup_table(hwmgr, powerplay_table, + le16_to_cpu(powerplay_table->usVddciLookupTableOffset), + 4, &vddci_table); + if (!result) + result = get_vddc_lookup_table(hwmgr, + &pp_table_info->vddci_lookup_table, + vddci_table, 4); } return result; @@ -1247,15 +1692,14 @@ int vega10_get_number_of_powerplay_table_entries(struct pp_hwmgr *hwmgr) { const ATOM_Vega10_State_Array *state_arrays; const ATOM_Vega10_POWERPLAYTABLE *pp_table = get_powerplay_table(hwmgr); + int result; PP_ASSERT_WITH_CODE((pp_table != NULL), "Missing PowerPlay Table!", return -1); - PP_ASSERT_WITH_CODE((pp_table->sHeader.format_revision >= - ATOM_Vega10_TABLE_REVISION_VEGA10), - "Incorrect PowerPlay table revision!", return -1); - state_arrays = (ATOM_Vega10_State_Array *)(((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); + result = get_vega10_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), + "Invalid PowerPlay Table State Array.", return result); return (uint32_t)(state_arrays->ucNumEntries); } @@ -1306,17 +1750,11 @@ int vega10_get_powerplay_table_entry(struct pp_hwmgr *hwmgr, if (pp_table->sHeader.format_revision >= ATOM_Vega10_TABLE_REVISION_VEGA10) { - state_arrays = (ATOM_Vega10_State_Array *) - (((unsigned long)pp_table) + - le16_to_cpu(pp_table->usStateArrayOffset)); - - PP_ASSERT_WITH_CODE(pp_table->usStateArrayOffset > 0, - "Invalid PowerPlay Table State Array Offset.", - return -1); - PP_ASSERT_WITH_CODE(state_arrays->ucNumEntries > 0, + result = get_vega10_state_array(hwmgr, pp_table, &state_arrays); + PP_ASSERT_WITH_CODE((result == 0), "Invalid PowerPlay Table State Array.", - return -1); - PP_ASSERT_WITH_CODE((entry_index <= state_arrays->ucNumEntries), + return result); + PP_ASSERT_WITH_CODE((entry_index < state_arrays->ucNumEntries), "Invalid PowerPlay Table State Array Entry.", return -1); @@ -1358,4 +1796,3 @@ int vega10_baco_set_cap(struct pp_hwmgr *hwmgr) PHM_PlatformCaps_BACO); return result; } - diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c index 55e13f376039..dcb9c749eba3 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_processpptables.c @@ -64,6 +64,13 @@ static int check_powerplay_tables( struct pp_hwmgr *hwmgr, const ATOM_Vega12_POWERPLAYTABLE *powerplay_table) { + size_t smc_pptable_size = + offsetofend(ATOM_Vega12_POWERPLAYTABLE, smcPPTable); + size_t table_size = hwmgr->soft_pp_table_size; + + PP_ASSERT_WITH_CODE((table_size >= smc_pptable_size), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >= ATOM_VEGA12_TABLE_REVISION_VEGA12), "Unsupported PPTable format!", return -1); diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c index 36cb7aa80d07..a0c884c2341d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_processpptables.c @@ -66,6 +66,13 @@ static int check_powerplay_tables( struct pp_hwmgr *hwmgr, const ATOM_Vega20_POWERPLAYTABLE *powerplay_table) { + size_t smc_pptable_size = + offsetofend(ATOM_Vega20_POWERPLAYTABLE, smcPPTable); + size_t table_size = hwmgr->soft_pp_table_size; + + PP_ASSERT_WITH_CODE((table_size >= smc_pptable_size), + "Invalid PowerPlay Table!", return -1); + PP_ASSERT_WITH_CODE((powerplay_table->sHeader.format_revision >= ATOM_VEGA20_TABLE_REVISION_VEGA20), "Unsupported PPTable format!", return -1); diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h index ca71efaa1656..7ebc1344023f 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h @@ -829,4 +829,21 @@ int smu8_init_function_pointers(struct pp_hwmgr *hwmgr); int vega12_hwmgr_init(struct pp_hwmgr *hwmgr); int vega20_hwmgr_init(struct pp_hwmgr *hwmgr); +static inline uint32_t pp_entries_max(const struct pp_hwmgr *hwmgr, + const void *sub_table, + size_t hdr_size, size_t rec_size) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)hwmgr->adev; + const char *bios_end = (const char *)adev->bios + adev->bios_size; + const char *pp_end = (const char *)hwmgr->soft_pp_table + + hwmgr->soft_pp_table_size; + const char *entries = (const char *)sub_table + hdr_size; + + if (pp_end > bios_end) + return 0; + if (!rec_size || entries >= pp_end) + return 0; + return (uint32_t)((pp_end - entries) / rec_size); +} + #endif /* _HWMGR_H_ */ diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index 208a2fba6d40..541cf0a985eb 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -591,17 +591,13 @@ static int smu_get_power_num_states(void *handle, return 0; } -bool is_support_sw_smu(struct amdgpu_device *adev) +void amdgpu_smu_early_init(struct amdgpu_device *adev) { /* vega20 is 11.0.2, but it's supported via the powerplay code */ - if (adev->asic_type == CHIP_VEGA20) - return false; - - if ((amdgpu_ip_version(adev, MP1_HWIP, 0) >= IP_VERSION(11, 0, 0)) && - amdgpu_device_ip_is_valid(adev, AMD_IP_BLOCK_TYPE_SMC)) - return true; - - return false; + adev->is_sw_smu = adev->asic_type != CHIP_VEGA20 && + (amdgpu_ip_version(adev, MP1_HWIP, 0) >= + IP_VERSION(11, 0, 0) && + amdgpu_device_ip_is_valid(adev, AMD_IP_BLOCK_TYPE_SMC)); } bool is_support_cclk_dpm(struct amdgpu_device *adev) @@ -667,25 +663,28 @@ static int smu_sys_set_pp_table(void *handle, { struct smu_context *smu = handle; struct smu_table_context *smu_table = &smu->smu_table; - ATOM_COMMON_TABLE_HEADER *header = (ATOM_COMMON_TABLE_HEADER *)buf; + ATOM_COMMON_TABLE_HEADER *header; + void *hardcode_pptable; int ret = 0; if (!smu->pm_enabled || !smu->adev->pm.dpm_enabled) return -EOPNOTSUPP; + if (!buf || size < sizeof(*header)) + return -EINVAL; + + header = (ATOM_COMMON_TABLE_HEADER *)buf; if (header->usStructureSize != size) { dev_err(smu->adev->dev, "pp table size not matched !\n"); return -EIO; } - if (!smu_table->hardcode_pptable || smu_table->power_play_table_size < size) { - kfree(smu_table->hardcode_pptable); - smu_table->hardcode_pptable = kzalloc(size, GFP_KERNEL); - if (!smu_table->hardcode_pptable) - return -ENOMEM; - } + hardcode_pptable = kmemdup(buf, size, GFP_KERNEL); + if (!hardcode_pptable) + return -ENOMEM; - memcpy(smu_table->hardcode_pptable, buf, size); + kfree(smu_table->hardcode_pptable); + smu_table->hardcode_pptable = hardcode_pptable; smu_table->power_play_table = smu_table->hardcode_pptable; smu_table->power_play_table_size = size; @@ -2772,7 +2771,6 @@ const struct amd_ip_funcs smu_ip_funcs = { .suspend = smu_suspend, .resume = smu_resume, .is_idle = NULL, - .check_soft_reset = NULL, .wait_for_idle = NULL, .soft_reset = NULL, .set_clockgating_state = smu_set_clockgating_state, diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index d76e0b005308..378781c05bea 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -849,8 +849,6 @@ struct pptable_funcs { */ int (*set_default_dpm_table)(struct smu_context *smu); - int (*set_power_state)(struct smu_context *smu); - /** * @populate_umd_state_clk: Populate the UMD power state table with * defaults. @@ -904,16 +902,6 @@ struct pptable_funcs { pp_clock_levels_with_latency *clocks); /** - * @get_clock_by_type_with_voltage: Get the speed and voltage of a clock - * domain. - */ - int (*get_clock_by_type_with_voltage)(struct smu_context *smu, - enum amd_pp_clock_type type, - struct - pp_clock_levels_with_voltage - *clocks); - - /** * @get_power_profile_mode: Print all power profile modes to * buffer. Star current mode. */ @@ -1355,11 +1343,6 @@ struct pptable_funcs { int (*register_irq_handler)(struct smu_context *smu); /** - * @set_azalia_d3_pme: Wake the audio decode engine from d3 sleep. - */ - int (*set_azalia_d3_pme)(struct smu_context *smu); - - /** * @get_max_sustainable_clocks_by_dc: Get a copy of the max sustainable * clock speeds table. * @@ -1376,18 +1359,6 @@ struct pptable_funcs { int (*get_bamaco_support)(struct smu_context *smu); /** - * @baco_get_state: Get the current BACO state. - * - * Return: Current BACO state. - */ - enum smu_baco_state (*baco_get_state)(struct smu_context *smu); - - /** - * @baco_set_state: Enter/exit BACO. - */ - int (*baco_set_state)(struct smu_context *smu, enum smu_baco_state state); - - /** * @baco_enter: Enter BACO. */ int (*baco_enter)(struct smu_context *smu); @@ -1952,7 +1923,13 @@ int smu_link_reset(struct smu_context *smu); extern const struct amd_ip_funcs smu_ip_funcs; -bool is_support_sw_smu(struct amdgpu_device *adev); +void amdgpu_smu_early_init(struct amdgpu_device *adev); + +static inline bool is_support_sw_smu(struct amdgpu_device *adev) +{ + return adev->is_sw_smu; +} + bool is_support_cclk_dpm(struct amdgpu_device *adev); int smu_write_watermarks_table(struct smu_context *smu); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h index dd94e8a9e218..c0accee9a9c8 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v11_0.h @@ -199,8 +199,6 @@ int smu_v11_0_gfx_off_control(struct smu_context *smu, bool enable); int smu_v11_0_register_irq_handler(struct smu_context *smu); -int smu_v11_0_set_azalia_d3_pme(struct smu_context *smu); - int smu_v11_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu, struct pp_smu_nv_clock_table *max_clocks); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h index 89bbda0670ef..7f21f867d73c 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h @@ -180,8 +180,6 @@ int smu_v13_0_gfx_off_control(struct smu_context *smu, bool enable); int smu_v13_0_register_irq_handler(struct smu_context *smu); -int smu_v13_0_set_azalia_d3_pme(struct smu_context *smu); - int smu_v13_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu, struct pp_smu_nv_clock_table *max_clocks); @@ -255,10 +253,6 @@ void smu_v13_0_init_msg_ctl(struct smu_context *smu, int smu_v13_0_mode1_reset(struct smu_context *smu); -int smu_v13_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id); int smu_v13_0_update_pcie_parameters(struct smu_context *smu, uint8_t pcie_gen_cap, diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h index 4eb40ff8aff2..dc8e13a7c879 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h @@ -203,10 +203,6 @@ int smu_v14_0_set_gfx_power_up_by_imu(struct smu_context *smu); int smu_v14_0_set_default_dpm_tables(struct smu_context *smu); -int smu_v14_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id); int smu_v14_0_od_edit_dpm_table(struct smu_context *smu, enum PP_OD_DPM_TABLE_COMMAND type, diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h index e6fd8be2cc4a..13723d45a7de 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h @@ -211,10 +211,6 @@ int smu_v15_0_deep_sleep_control(struct smu_context *smu, int smu_v15_0_set_gfx_power_up_by_imu(struct smu_context *smu); -int smu_v15_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id); int smu_v15_0_od_edit_dpm_table(struct smu_context *smu, enum PP_OD_DPM_TABLE_COMMAND type, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c index 54d3dba7d354..db5db2c9c8e8 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c @@ -1466,9 +1466,10 @@ static int arcturus_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != ARCTURUS_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= ARCTURUS_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + ARCTURUS_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + ARCTURUS_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * ARCTURUS_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -1932,7 +1933,6 @@ static const struct pptable_funcs arcturus_ppt_funcs = { .set_xgmi_pstate = smu_v11_0_set_xgmi_pstate, .gfx_off_control = smu_v11_0_gfx_off_control, .register_irq_handler = smu_v11_0_register_irq_handler, - .set_azalia_d3_pme = smu_v11_0_set_azalia_d3_pme, .get_max_sustainable_clocks_by_dc = smu_v11_0_get_max_sustainable_clocks_by_dc, .get_bamaco_support = smu_v11_0_get_bamaco_support, .baco_enter = smu_v11_0_baco_enter, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index cd0457e13f54..8feea44f3ca0 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -1843,9 +1843,10 @@ static int navi10_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != NAVI10_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= NAVI10_CUSTOM_PARAMS_CLOCKS_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + NAVI10_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + NAVI10_CUSTOM_PARAMS_CLOCKS_COUNT)) return -EINVAL; idx = custom_params[0] * NAVI10_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -3337,7 +3338,6 @@ static const struct pptable_funcs navi10_ppt_funcs = { .set_xgmi_pstate = smu_v11_0_set_xgmi_pstate, .gfx_off_control = smu_v11_0_gfx_off_control, .register_irq_handler = smu_v11_0_register_irq_handler, - .set_azalia_d3_pme = smu_v11_0_set_azalia_d3_pme, .get_max_sustainable_clocks_by_dc = smu_v11_0_get_max_sustainable_clocks_by_dc, .get_bamaco_support = smu_v11_0_get_bamaco_support, .baco_enter = navi10_baco_enter, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index f799e489b481..c0de73b85353 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -1755,9 +1755,10 @@ static int sienna_cichlid_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != SIENNA_CICHLID_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= SIENNA_CICHLID_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + SIENNA_CICHLID_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + SIENNA_CICHLID_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * SIENNA_CICHLID_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -3144,7 +3145,6 @@ static const struct pptable_funcs sienna_cichlid_ppt_funcs = { .set_xgmi_pstate = smu_v11_0_set_xgmi_pstate, .gfx_off_control = smu_v11_0_gfx_off_control, .register_irq_handler = smu_v11_0_register_irq_handler, - .set_azalia_d3_pme = smu_v11_0_set_azalia_d3_pme, .get_max_sustainable_clocks_by_dc = smu_v11_0_get_max_sustainable_clocks_by_dc, .get_bamaco_support = smu_v11_0_get_bamaco_support, .baco_enter = sienna_cichlid_baco_enter, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c index d68ceee16d8f..a889d846e9c5 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c @@ -192,81 +192,22 @@ int smu_v11_0_check_fw_status(struct smu_context *smu) return -EIO; } -static int smu_v11_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t ppt_offset_bytes; - const struct smc_firmware_header_v2_0 *v2; - - v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data; - - ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); - *size = le32_to_cpu(v2->ppt_size_bytes); - *table = (uint8_t *)v2 + ppt_offset_bytes; - - return 0; -} - -static int smu_v11_0_set_pptable_v2_1(struct smu_context *smu, void **table, - uint32_t *size, uint32_t pptable_id) -{ - struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v2_1 *v2_1; - struct smc_soft_pptable_entry *entries; - uint32_t pptable_count = 0; - int i = 0; - - v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data; - entries = (struct smc_soft_pptable_entry *) - ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset)); - pptable_count = le32_to_cpu(v2_1->pptable_count); - for (i = 0; i < pptable_count; i++) { - if (le32_to_cpu(entries[i].id) == pptable_id) { - *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes)); - *size = le32_to_cpu(entries[i].ppt_size_bytes); - break; - } - } - - if (i == pptable_count) - return -EINVAL; - - return 0; -} - int smu_v11_0_setup_pptable(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v1_0 *hdr; - int ret, index; - uint32_t size = 0; uint16_t atom_table_size; uint8_t frev, crev; + uint32_t size = 0; + int ret, index; void *table; - uint16_t version_major, version_minor; - - if (!amdgpu_sriov_vf(adev)) { - hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; - version_major = le16_to_cpu(hdr->header.header_version_major); - version_minor = le16_to_cpu(hdr->header.header_version_minor); - if (version_major == 2 && smu->smu_table.boot_values.pp_table_id > 0) { - dev_info(adev->dev, "use driver provided pptable %d\n", smu->smu_table.boot_values.pp_table_id); - switch (version_minor) { - case 0: - ret = smu_v11_0_set_pptable_v2_0(smu, &table, &size); - break; - case 1: - ret = smu_v11_0_set_pptable_v2_1(smu, &table, &size, - smu->smu_table.boot_values.pp_table_id); - break; - default: - ret = -EINVAL; - break; - } - if (ret) - return ret; - goto out; - } + + if (!amdgpu_sriov_vf(adev) && + smu->smu_table.boot_values.pp_table_id > 0) { + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, + smu->smu_table.boot_values.pp_table_id); + if (ret) + return ret; + goto out; } dev_info(adev->dev, "use vbios provided pptable\n"); @@ -1476,11 +1417,6 @@ int smu_v11_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu, return 0; } -int smu_v11_0_set_azalia_d3_pme(struct smu_context *smu) -{ - return smu_cmn_send_smc_msg(smu, SMU_MSG_BacoAudioD3PME, NULL); -} - int smu_v11_0_baco_set_armd3_sequence(struct smu_context *smu, enum smu_baco_seq baco_seq) { diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c index 75335da224c7..2b011610e3c7 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c @@ -1444,7 +1444,6 @@ static int renoir_get_enabled_mask(struct smu_context *smu, } static const struct pptable_funcs renoir_ppt_funcs = { - .set_power_state = NULL, .emit_clk_levels = renoir_emit_clk_levels, .get_current_power_state = renoir_get_current_power_state, .dpm_set_vcn_enable = renoir_dpm_set_vcn_enable, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c index 9d8b1227388f..cd7bf36673cb 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c @@ -2004,7 +2004,6 @@ static const struct pptable_funcs aldebaran_ppt_funcs = { .disable_thermal_alert = smu_v13_0_disable_thermal_alert, .set_xgmi_pstate = smu_v13_0_set_xgmi_pstate, .register_irq_handler = smu_v13_0_register_irq_handler, - .set_azalia_d3_pme = smu_v13_0_set_azalia_d3_pme, .get_max_sustainable_clocks_by_dc = smu_v13_0_get_max_sustainable_clocks_by_dc, .get_bamaco_support = aldebaran_get_bamaco_support, .get_dpm_ultimate_freq = aldebaran_get_dpm_ultimate_freq, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c index be9a7a32de99..4f10bce36756 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c @@ -218,7 +218,7 @@ int smu_v13_0_init_pptable_microcode(struct smu_context *smu) if (!pptable_id) return 0; - ret = smu_v13_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -258,48 +258,6 @@ int smu_v13_0_check_fw_status(struct smu_context *smu) return -EIO; } -static int smu_v13_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t ppt_offset_bytes; - const struct smc_firmware_header_v2_0 *v2; - - v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data; - - ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); - *size = le32_to_cpu(v2->ppt_size_bytes); - *table = (uint8_t *)v2 + ppt_offset_bytes; - - return 0; -} - -static int smu_v13_0_set_pptable_v2_1(struct smu_context *smu, void **table, - uint32_t *size, uint32_t pptable_id) -{ - struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v2_1 *v2_1; - struct smc_soft_pptable_entry *entries; - uint32_t pptable_count = 0; - int i = 0; - - v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data; - entries = (struct smc_soft_pptable_entry *) - ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset)); - pptable_count = le32_to_cpu(v2_1->pptable_count); - for (i = 0; i < pptable_count; i++) { - if (le32_to_cpu(entries[i].id) == pptable_id) { - *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes)); - *size = le32_to_cpu(entries[i].ppt_size_bytes); - break; - } - } - - if (i == pptable_count) - return -EINVAL; - - return 0; -} - static int smu_v13_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size) { struct amdgpu_device *adev = smu->adev; @@ -322,45 +280,6 @@ static int smu_v13_0_get_pptable_from_vbios(struct smu_context *smu, void **tabl return 0; } -int smu_v13_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id) -{ - const struct smc_firmware_header_v1_0 *hdr; - struct amdgpu_device *adev = smu->adev; - uint16_t version_major, version_minor; - int ret; - - hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; - if (!hdr) - return -EINVAL; - - dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id); - - version_major = le16_to_cpu(hdr->header.header_version_major); - version_minor = le16_to_cpu(hdr->header.header_version_minor); - if (version_major != 2) { - dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n", - version_major, version_minor); - return -EINVAL; - } - - switch (version_minor) { - case 0: - ret = smu_v13_0_set_pptable_v2_0(smu, table, size); - break; - case 1: - ret = smu_v13_0_set_pptable_v2_1(smu, table, size, pptable_id); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - int smu_v13_0_setup_pptable(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; @@ -380,7 +299,7 @@ int smu_v13_0_setup_pptable(struct smu_context *smu) if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1)) ret = smu_v13_0_get_pptable_from_vbios(smu, &table, &size); else - ret = smu_v13_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -1401,15 +1320,6 @@ int smu_v13_0_get_max_sustainable_clocks_by_dc(struct smu_context *smu, return 0; } -int smu_v13_0_set_azalia_d3_pme(struct smu_context *smu) -{ - int ret = 0; - - ret = smu_cmn_send_smc_msg(smu, SMU_MSG_BacoAudioD3PME, NULL); - - return ret; -} - static int smu_v13_0_wait_for_reset_complete(struct smu_context *smu, uint64_t event_arg) { diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 7f8d4bb47d02..4ce1429cf57b 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -2403,11 +2403,14 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; - uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); + uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); + uint32_t od_percent_upper = 0, od_percent_lower = 0; int ret; if (current_power_limit) { - ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + ret = smu_v13_0_get_current_power_limit(smu, current_power_limit); if (ret) *current_power_limit = pp_limit; } @@ -2430,12 +2433,12 @@ static int smu_v13_0_0_get_power_limit(struct smu_context *smu, od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = pp_limit * (100 + od_percent_upper); + *max_power_limit = max_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = pp_limit * (100 - od_percent_lower); + *min_power_limit = min_limit * (100 - od_percent_lower); *min_power_limit /= 100; } @@ -2616,9 +2619,10 @@ static int smu_v13_0_0_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != SMU_13_0_0_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= SMU_13_0_0_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + SMU_13_0_0_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + SMU_13_0_0_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * SMU_13_0_0_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c index fe929bd89058..dea27fcb2b20 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c @@ -1042,7 +1042,7 @@ static int smu_v13_0_12_get_badpage_count(struct amdgpu_device *adev, uint32_t * /* eeprom is not ready */ if (ret != -EBUSY) return ret; - mdelay(10); + usleep_range(10000, 15000); now = (uint64_t)ktime_to_ms(ktime_get()); } while (now < end); @@ -1137,16 +1137,10 @@ static const struct ras_eeprom_smu_funcs smu_v13_0_12_eeprom_smu_funcs = { static void smu_v13_0_12_ras_smu_feature_flags(struct amdgpu_device *adev, uint64_t *flags) { - struct smu_context *smu = adev->powerplay.pp_handle; - if (!flags) return; *flags = 0ULL; - - if (smu_v13_0_6_cap_supported(smu, SMU_CAP(RAS_EEPROM))) - *flags |= RAS_SMU_FEATURE_BIT__RAS_EEPROM; - } const struct ras_smu_drv smu_v13_0_12_ras_smu_drv = { diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index b12388134489..334c92a28994 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -44,8 +44,6 @@ #include "amdgpu_xgmi.h" #include <linux/pci.h> #include "amdgpu_ras.h" -#include "amdgpu_mca.h" -#include "amdgpu_aca.h" #include "smu_cmn.h" #include "mp/mp_13_0_6_offset.h" #include "mp/mp_13_0_6_sh_mask.h" @@ -99,25 +97,6 @@ static const struct smu_feature_bits smu_v13_0_6_dpm_features = { #define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE_MASK 0xE0 #define PCIE_LC_SPEED_CNTL__LC_CURRENT_DATA_RATE__SHIFT 0x5 #define LINK_SPEED_MAX 4 -#define MCA_BANK_IPID(_ip, _hwid, _type) \ - [AMDGPU_MCA_IP_##_ip] = { .hwid = _hwid, .mcatype = _type, } - -struct mca_bank_ipid { - enum amdgpu_mca_ip ip; - uint16_t hwid; - uint16_t mcatype; -}; - -struct mca_ras_info { - enum amdgpu_ras_block blkid; - enum amdgpu_mca_ip ip; - int *err_code_array; - int err_code_count; - int (*get_err_count)(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count); - bool (*bank_is_valid)(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry); -}; #define P2S_TABLE_ID_A 0x50325341 #define P2S_TABLE_ID_X 0x50325358 @@ -1943,17 +1922,6 @@ static int smu_v13_0_6_notify_unload(struct smu_context *smu) return 0; } -static int smu_v13_0_6_mca_set_debug_mode(struct smu_context *smu, bool enable) -{ - /* NOTE: this ClearMcaOnRead message is only supported for smu version 85.72.0 or higher */ - if (!smu_v13_0_6_cap_supported(smu, SMU_CAP(MCA_DEBUG_MODE))) - return 0; - - return smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_ClearMcaOnRead, - enable ? 0 : ClearMcaOnRead_UE_FLAG_MASK | ClearMcaOnRead_CE_POLL_MASK, - NULL); -} - static int smu_v13_0_6_system_features_control(struct smu_context *smu, bool enable) { @@ -3299,622 +3267,6 @@ static int smu_v13_0_6_post_init(struct smu_context *smu) return 0; } -static int mca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - - return smu_v13_0_6_mca_set_debug_mode(smu, enable); -} - -static int smu_v13_0_6_get_valid_mca_count(struct smu_context *smu, enum amdgpu_mca_error_type type, uint32_t *count) -{ - uint32_t msg; - int ret; - - if (!count) - return -EINVAL; - - switch (type) { - case AMDGPU_MCA_ERROR_TYPE_UE: - msg = SMU_MSG_QueryValidMcaCount; - break; - case AMDGPU_MCA_ERROR_TYPE_CE: - msg = SMU_MSG_QueryValidMcaCeCount; - break; - default: - return -EINVAL; - } - - ret = smu_cmn_send_smc_msg(smu, msg, count); - if (ret) { - *count = 0; - return ret; - } - - return 0; -} - -static int __smu_v13_0_6_mca_dump_bank(struct smu_context *smu, enum amdgpu_mca_error_type type, - int idx, int offset, uint32_t *val) -{ - uint32_t msg, param; - - switch (type) { - case AMDGPU_MCA_ERROR_TYPE_UE: - msg = SMU_MSG_McaBankDumpDW; - break; - case AMDGPU_MCA_ERROR_TYPE_CE: - msg = SMU_MSG_McaBankCeDumpDW; - break; - default: - return -EINVAL; - } - - param = ((idx & 0xffff) << 16) | (offset & 0xfffc); - - return smu_cmn_send_smc_msg_with_param(smu, msg, param, val); -} - -static int smu_v13_0_6_mca_dump_bank(struct smu_context *smu, enum amdgpu_mca_error_type type, - int idx, int offset, uint32_t *val, int count) -{ - int ret, i; - - if (!val) - return -EINVAL; - - for (i = 0; i < count; i++) { - ret = __smu_v13_0_6_mca_dump_bank(smu, type, idx, offset + (i << 2), &val[i]); - if (ret) - return ret; - } - - return 0; -} - -static const struct mca_bank_ipid smu_v13_0_6_mca_ipid_table[AMDGPU_MCA_IP_COUNT] = { - MCA_BANK_IPID(UMC, 0x96, 0x0), - MCA_BANK_IPID(SMU, 0x01, 0x1), - MCA_BANK_IPID(MP5, 0x01, 0x2), - MCA_BANK_IPID(PCS_XGMI, 0x50, 0x0), -}; - -static void mca_bank_entry_info_decode(struct mca_bank_entry *entry, struct mca_bank_info *info) -{ - u64 ipid = entry->regs[MCA_REG_IDX_IPID]; - u32 instidhi, instid; - - /* NOTE: All MCA IPID register share the same format, - * so the driver can share the MCMP1 register header file. - * */ - - info->hwid = REG_GET_FIELD(ipid, MCMP1_IPIDT0, HardwareID); - info->mcatype = REG_GET_FIELD(ipid, MCMP1_IPIDT0, McaType); - - /* - * Unfied DieID Format: SAASS. A:AID, S:Socket. - * Unfied DieID[4] = InstanceId[0] - * Unfied DieID[0:3] = InstanceIdHi[0:3] - */ - instidhi = REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdHi); - instid = REG_GET_FIELD(ipid, MCMP1_IPIDT0, InstanceIdLo); - info->aid = ((instidhi >> 2) & 0x03); - info->socket_id = ((instid & 0x1) << 2) | (instidhi & 0x03); -} - -static int mca_bank_read_reg(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, - int idx, int reg_idx, uint64_t *val) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - uint32_t data[2] = {0, 0}; - int ret; - - if (!val || reg_idx >= MCA_REG_IDX_COUNT) - return -EINVAL; - - ret = smu_v13_0_6_mca_dump_bank(smu, type, idx, reg_idx * 8, data, ARRAY_SIZE(data)); - if (ret) - return ret; - - *val = (uint64_t)data[1] << 32 | data[0]; - - dev_dbg(adev->dev, "mca read bank reg: type:%s, index: %d, reg_idx: %d, val: 0x%016llx\n", - type == AMDGPU_MCA_ERROR_TYPE_UE ? "UE" : "CE", idx, reg_idx, *val); - - return 0; -} - -static int mca_get_mca_entry(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, - int idx, struct mca_bank_entry *entry) -{ - int i, ret; - - /* NOTE: populated all mca register by default */ - for (i = 0; i < ARRAY_SIZE(entry->regs); i++) { - ret = mca_bank_read_reg(adev, type, idx, i, &entry->regs[i]); - if (ret) - return ret; - } - - entry->idx = idx; - entry->type = type; - - mca_bank_entry_info_decode(entry, &entry->info); - - return 0; -} - -static int mca_decode_ipid_to_hwip(uint64_t val) -{ - const struct mca_bank_ipid *ipid; - uint16_t hwid, mcatype; - int i; - - hwid = REG_GET_FIELD(val, MCMP1_IPIDT0, HardwareID); - mcatype = REG_GET_FIELD(val, MCMP1_IPIDT0, McaType); - - for (i = 0; i < ARRAY_SIZE(smu_v13_0_6_mca_ipid_table); i++) { - ipid = &smu_v13_0_6_mca_ipid_table[i]; - - if (!ipid->hwid) - continue; - - if (ipid->hwid == hwid && ipid->mcatype == mcatype) - return i; - } - - return AMDGPU_MCA_IP_UNKNOW; -} - -static int mca_umc_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count) -{ - uint64_t status0; - uint32_t ext_error_code; - uint32_t odecc_err_cnt; - - status0 = entry->regs[MCA_REG_IDX_STATUS]; - ext_error_code = MCA_REG__STATUS__ERRORCODEEXT(status0); - odecc_err_cnt = MCA_REG__MISC0__ERRCNT(entry->regs[MCA_REG_IDX_MISC0]); - - if (!REG_GET_FIELD(status0, MCMP1_STATUST0, Val)) { - *count = 0; - return 0; - } - - if (umc_v12_0_is_deferred_error(adev, status0) || - umc_v12_0_is_uncorrectable_error(adev, status0) || - umc_v12_0_is_correctable_error(adev, status0)) - *count = (ext_error_code == 0) ? odecc_err_cnt : 1; - - amdgpu_umc_update_ecc_status(adev, - entry->regs[MCA_REG_IDX_STATUS], - entry->regs[MCA_REG_IDX_IPID], - entry->regs[MCA_REG_IDX_ADDR]); - - return 0; -} - -static int mca_pcs_xgmi_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, - uint32_t *count) -{ - u32 ext_error_code; - u32 err_cnt; - - ext_error_code = MCA_REG__STATUS__ERRORCODEEXT(entry->regs[MCA_REG_IDX_STATUS]); - err_cnt = MCA_REG__MISC0__ERRCNT(entry->regs[MCA_REG_IDX_MISC0]); - - if (type == AMDGPU_MCA_ERROR_TYPE_UE && - (ext_error_code == 0 || ext_error_code == 9)) - *count = err_cnt; - else if (type == AMDGPU_MCA_ERROR_TYPE_CE && ext_error_code == 6) - *count = err_cnt; - - return 0; -} - -static bool mca_smu_check_error_code(struct amdgpu_device *adev, const struct mca_ras_info *mca_ras, - uint32_t errcode) -{ - int i; - - if (!mca_ras->err_code_count || !mca_ras->err_code_array) - return true; - - for (i = 0; i < mca_ras->err_code_count; i++) { - if (errcode == mca_ras->err_code_array[i]) - return true; - } - - return false; -} - -static int mca_gfx_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count) -{ - uint64_t status0, misc0; - - status0 = entry->regs[MCA_REG_IDX_STATUS]; - if (!REG_GET_FIELD(status0, MCMP1_STATUST0, Val)) { - *count = 0; - return 0; - } - - if (type == AMDGPU_MCA_ERROR_TYPE_UE && - REG_GET_FIELD(status0, MCMP1_STATUST0, UC) == 1 && - REG_GET_FIELD(status0, MCMP1_STATUST0, PCC) == 1) { - *count = 1; - return 0; - } else { - misc0 = entry->regs[MCA_REG_IDX_MISC0]; - *count = REG_GET_FIELD(misc0, MCMP1_MISC0T0, ErrCnt); - } - - return 0; -} - -static int mca_smu_mca_get_err_count(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry, uint32_t *count) -{ - uint64_t status0, misc0; - - status0 = entry->regs[MCA_REG_IDX_STATUS]; - if (!REG_GET_FIELD(status0, MCMP1_STATUST0, Val)) { - *count = 0; - return 0; - } - - if (type == AMDGPU_MCA_ERROR_TYPE_UE && - REG_GET_FIELD(status0, MCMP1_STATUST0, UC) == 1 && - REG_GET_FIELD(status0, MCMP1_STATUST0, PCC) == 1) { - if (count) - *count = 1; - return 0; - } - - misc0 = entry->regs[MCA_REG_IDX_MISC0]; - *count = REG_GET_FIELD(misc0, MCMP1_MISC0T0, ErrCnt); - - return 0; -} - -static bool mca_gfx_smu_bank_is_valid(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry) -{ - uint32_t instlo; - - instlo = REG_GET_FIELD(entry->regs[MCA_REG_IDX_IPID], MCMP1_IPIDT0, InstanceIdLo); - instlo &= GENMASK(31, 1); - switch (instlo) { - case 0x36430400: /* SMNAID XCD 0 */ - case 0x38430400: /* SMNAID XCD 1 */ - case 0x40430400: /* SMNXCD XCD 0, NOTE: FIXME: fix this error later */ - return true; - default: - return false; - } - - return false; -}; - -static bool mca_smu_bank_is_valid(const struct mca_ras_info *mca_ras, struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - uint32_t errcode, instlo; - - instlo = REG_GET_FIELD(entry->regs[MCA_REG_IDX_IPID], MCMP1_IPIDT0, InstanceIdLo); - instlo &= GENMASK(31, 1); - if (instlo != 0x03b30400) - return false; - - if (smu_v13_0_6_cap_supported(smu, SMU_CAP(ACA_SYND))) { - errcode = MCA_REG__SYND__ERRORINFORMATION(entry->regs[MCA_REG_IDX_SYND]); - errcode &= 0xff; - } else { - errcode = REG_GET_FIELD(entry->regs[MCA_REG_IDX_STATUS], MCMP1_STATUST0, ErrorCode); - } - - return mca_smu_check_error_code(adev, mca_ras, errcode); -} - -static int sdma_err_codes[] = { CODE_SDMA0, CODE_SDMA1, CODE_SDMA2, CODE_SDMA3 }; -static int mmhub_err_codes[] = { - CODE_DAGB0, CODE_DAGB0 + 1, CODE_DAGB0 + 2, CODE_DAGB0 + 3, CODE_DAGB0 + 4, /* DAGB0-4 */ - CODE_EA0, CODE_EA0 + 1, CODE_EA0 + 2, CODE_EA0 + 3, CODE_EA0 + 4, /* MMEA0-4*/ - CODE_VML2, CODE_VML2_WALKER, CODE_MMCANE, -}; - -static int vcn_err_codes[] = { - CODE_VIDD, CODE_VIDV, -}; -static int jpeg_err_codes[] = { - CODE_JPEG0S, CODE_JPEG0D, CODE_JPEG1S, CODE_JPEG1D, - CODE_JPEG2S, CODE_JPEG2D, CODE_JPEG3S, CODE_JPEG3D, - CODE_JPEG4S, CODE_JPEG4D, CODE_JPEG5S, CODE_JPEG5D, - CODE_JPEG6S, CODE_JPEG6D, CODE_JPEG7S, CODE_JPEG7D, -}; - -static const struct mca_ras_info mca_ras_table[] = { - { - .blkid = AMDGPU_RAS_BLOCK__UMC, - .ip = AMDGPU_MCA_IP_UMC, - .get_err_count = mca_umc_mca_get_err_count, - }, { - .blkid = AMDGPU_RAS_BLOCK__GFX, - .ip = AMDGPU_MCA_IP_SMU, - .get_err_count = mca_gfx_mca_get_err_count, - .bank_is_valid = mca_gfx_smu_bank_is_valid, - }, { - .blkid = AMDGPU_RAS_BLOCK__SDMA, - .ip = AMDGPU_MCA_IP_SMU, - .err_code_array = sdma_err_codes, - .err_code_count = ARRAY_SIZE(sdma_err_codes), - .get_err_count = mca_smu_mca_get_err_count, - .bank_is_valid = mca_smu_bank_is_valid, - }, { - .blkid = AMDGPU_RAS_BLOCK__MMHUB, - .ip = AMDGPU_MCA_IP_SMU, - .err_code_array = mmhub_err_codes, - .err_code_count = ARRAY_SIZE(mmhub_err_codes), - .get_err_count = mca_smu_mca_get_err_count, - .bank_is_valid = mca_smu_bank_is_valid, - }, { - .blkid = AMDGPU_RAS_BLOCK__XGMI_WAFL, - .ip = AMDGPU_MCA_IP_PCS_XGMI, - .get_err_count = mca_pcs_xgmi_mca_get_err_count, - }, { - .blkid = AMDGPU_RAS_BLOCK__VCN, - .ip = AMDGPU_MCA_IP_SMU, - .err_code_array = vcn_err_codes, - .err_code_count = ARRAY_SIZE(vcn_err_codes), - .get_err_count = mca_smu_mca_get_err_count, - .bank_is_valid = mca_smu_bank_is_valid, - }, { - .blkid = AMDGPU_RAS_BLOCK__JPEG, - .ip = AMDGPU_MCA_IP_SMU, - .err_code_array = jpeg_err_codes, - .err_code_count = ARRAY_SIZE(jpeg_err_codes), - .get_err_count = mca_smu_mca_get_err_count, - .bank_is_valid = mca_smu_bank_is_valid, - }, -}; - -static const struct mca_ras_info *mca_get_mca_ras_info(struct amdgpu_device *adev, enum amdgpu_ras_block blkid) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(mca_ras_table); i++) { - if (mca_ras_table[i].blkid == blkid) - return &mca_ras_table[i]; - } - - return NULL; -} - -static int mca_get_valid_mca_count(struct amdgpu_device *adev, enum amdgpu_mca_error_type type, uint32_t *count) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - int ret; - - switch (type) { - case AMDGPU_MCA_ERROR_TYPE_UE: - case AMDGPU_MCA_ERROR_TYPE_CE: - ret = smu_v13_0_6_get_valid_mca_count(smu, type, count); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static bool mca_bank_is_valid(struct amdgpu_device *adev, const struct mca_ras_info *mca_ras, - enum amdgpu_mca_error_type type, struct mca_bank_entry *entry) -{ - if (mca_decode_ipid_to_hwip(entry->regs[MCA_REG_IDX_IPID]) != mca_ras->ip) - return false; - - if (mca_ras->bank_is_valid) - return mca_ras->bank_is_valid(mca_ras, adev, type, entry); - - return true; -} - -static int mca_smu_parse_mca_error_count(struct amdgpu_device *adev, enum amdgpu_ras_block blk, enum amdgpu_mca_error_type type, - struct mca_bank_entry *entry, uint32_t *count) -{ - const struct mca_ras_info *mca_ras; - - if (!entry || !count) - return -EINVAL; - - mca_ras = mca_get_mca_ras_info(adev, blk); - if (!mca_ras) - return -EOPNOTSUPP; - - if (!mca_bank_is_valid(adev, mca_ras, type, entry)) { - *count = 0; - return 0; - } - - return mca_ras->get_err_count(mca_ras, adev, type, entry, count); -} - -static int mca_smu_get_mca_entry(struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, int idx, struct mca_bank_entry *entry) -{ - return mca_get_mca_entry(adev, type, idx, entry); -} - -static int mca_smu_get_valid_mca_count(struct amdgpu_device *adev, - enum amdgpu_mca_error_type type, uint32_t *count) -{ - return mca_get_valid_mca_count(adev, type, count); -} - -static const struct amdgpu_mca_smu_funcs smu_v13_0_6_mca_smu_funcs = { - .max_ue_count = 12, - .max_ce_count = 12, - .mca_set_debug_mode = mca_smu_set_debug_mode, - .mca_parse_mca_error_count = mca_smu_parse_mca_error_count, - .mca_get_mca_entry = mca_smu_get_mca_entry, - .mca_get_valid_mca_count = mca_smu_get_valid_mca_count, -}; - -static int aca_smu_set_debug_mode(struct amdgpu_device *adev, bool enable) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - - return smu_v13_0_6_mca_set_debug_mode(smu, enable); -} - -static int smu_v13_0_6_get_valid_aca_count(struct smu_context *smu, enum aca_smu_type type, u32 *count) -{ - uint32_t msg; - int ret; - - if (!count) - return -EINVAL; - - switch (type) { - case ACA_SMU_TYPE_UE: - msg = SMU_MSG_QueryValidMcaCount; - break; - case ACA_SMU_TYPE_CE: - msg = SMU_MSG_QueryValidMcaCeCount; - break; - default: - return -EINVAL; - } - - ret = smu_cmn_send_smc_msg(smu, msg, count); - if (ret) { - *count = 0; - return ret; - } - - return 0; -} - -static int aca_smu_get_valid_aca_count(struct amdgpu_device *adev, - enum aca_smu_type type, u32 *count) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - int ret; - - switch (type) { - case ACA_SMU_TYPE_UE: - case ACA_SMU_TYPE_CE: - ret = smu_v13_0_6_get_valid_aca_count(smu, type, count); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - -static int __smu_v13_0_6_aca_bank_dump(struct smu_context *smu, enum aca_smu_type type, - int idx, int offset, u32 *val) -{ - uint32_t msg, param; - - switch (type) { - case ACA_SMU_TYPE_UE: - msg = SMU_MSG_McaBankDumpDW; - break; - case ACA_SMU_TYPE_CE: - msg = SMU_MSG_McaBankCeDumpDW; - break; - default: - return -EINVAL; - } - - param = ((idx & 0xffff) << 16) | (offset & 0xfffc); - - return smu_cmn_send_smc_msg_with_param(smu, msg, param, (uint32_t *)val); -} - -static int smu_v13_0_6_aca_bank_dump(struct smu_context *smu, enum aca_smu_type type, - int idx, int offset, u32 *val, int count) -{ - int ret, i; - - if (!val) - return -EINVAL; - - for (i = 0; i < count; i++) { - ret = __smu_v13_0_6_aca_bank_dump(smu, type, idx, offset + (i << 2), &val[i]); - if (ret) - return ret; - } - - return 0; -} - -static int aca_bank_read_reg(struct amdgpu_device *adev, enum aca_smu_type type, - int idx, int reg_idx, u64 *val) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - u32 data[2] = {0, 0}; - int ret; - - if (!val || reg_idx >= ACA_REG_IDX_COUNT) - return -EINVAL; - - ret = smu_v13_0_6_aca_bank_dump(smu, type, idx, reg_idx * 8, data, ARRAY_SIZE(data)); - if (ret) - return ret; - - *val = (u64)data[1] << 32 | data[0]; - - dev_dbg(adev->dev, "mca read bank reg: type:%s, index: %d, reg_idx: %d, val: 0x%016llx\n", - type == ACA_SMU_TYPE_UE ? "UE" : "CE", idx, reg_idx, *val); - - return 0; -} - -static int aca_smu_get_valid_aca_bank(struct amdgpu_device *adev, - enum aca_smu_type type, int idx, struct aca_bank *bank) -{ - int i, ret, count; - - count = min_t(int, 16, ARRAY_SIZE(bank->regs)); - for (i = 0; i < count; i++) { - ret = aca_bank_read_reg(adev, type, idx, i, &bank->regs[i]); - if (ret) - return ret; - } - - return 0; -} - -static int aca_smu_parse_error_code(struct amdgpu_device *adev, struct aca_bank *bank) -{ - struct smu_context *smu = adev->powerplay.pp_handle; - int error_code; - - if (smu_v13_0_6_cap_supported(smu, SMU_CAP(ACA_SYND))) - error_code = ACA_REG__SYND__ERRORINFORMATION(bank->regs[ACA_REG_IDX_SYND]); - else - error_code = ACA_REG__STATUS__ERRORCODE(bank->regs[ACA_REG_IDX_STATUS]); - - return error_code & 0xff; -} - -static const struct aca_smu_funcs smu_v13_0_6_aca_smu_funcs = { - .max_ue_bank_count = 12, - .max_ce_bank_count = 12, - .set_debug_mode = aca_smu_set_debug_mode, - .get_valid_aca_count = aca_smu_get_valid_aca_count, - .get_valid_aca_bank = aca_smu_get_valid_aca_bank, - .parse_error_code = aca_smu_parse_error_code, -}; - static void smu_v13_0_6_set_temp_funcs(struct smu_context *smu) { smu->smu_temp.temp_funcs = (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) @@ -3929,9 +3281,6 @@ static int smu_v13_0_6_get_ras_smu_drv(struct smu_context *smu, const struct ras if (amdgpu_sriov_vf(smu->adev)) return -EOPNOTSUPP; - if (smu_cmn_feature_is_enabled(smu, SMU_FEATURE_HROM_EN_BIT)) - smu_v13_0_6_cap_set(smu, SMU_CAP(RAS_EEPROM)); - switch (amdgpu_ip_version(smu->adev, MP1_HWIP, 0)) { case IP_VERSION(13, 0, 12): *ras_smu_drv = &smu_v13_0_12_ras_smu_drv; @@ -4020,7 +3369,5 @@ void smu_v13_0_6_set_ppt_funcs(struct smu_context *smu) smu->smc_fw_caps |= SMU_FW_CAP_RAS_PRI; smu_v13_0_init_msg_ctl(smu, message_map); smu_v13_0_6_set_temp_funcs(smu); - amdgpu_mca_smu_init_funcs(smu->adev, &smu_v13_0_6_mca_smu_funcs); - amdgpu_aca_set_smu_funcs(smu->adev, &smu_v13_0_6_aca_smu_funcs); } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 0f774b0920ce..5f23f2e7f401 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -2385,15 +2385,16 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, uint32_t pp_limit = smu->adev->pm.ac_power ? skutable->SocketPowerLimitAc[PPT_THROTTLER_PPT0] : skutable->SocketPowerLimitDc[PPT_THROTTLER_PPT0]; - uint32_t power_limit = 0, od_percent_upper = 0, od_percent_lower = 0; + uint32_t msg_limit = skutable->MsgLimits.Power[PPT_THROTTLER_PPT0][POWER_SOURCE_AC]; + uint32_t min_limit = min_t(uint32_t, pp_limit, msg_limit); + uint32_t max_limit = max_t(uint32_t, pp_limit, msg_limit); + uint32_t od_percent_upper = 0, od_percent_lower = 0; int ret; if (current_power_limit) { - ret = smu_v13_0_get_current_power_limit(smu, &power_limit); + ret = smu_v13_0_get_current_power_limit(smu, current_power_limit); if (ret) - power_limit = pp_limit; - - *current_power_limit = power_limit; + *current_power_limit = pp_limit; } if (default_power_limit) @@ -2414,12 +2415,12 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, od_percent_upper, od_percent_lower, pp_limit); if (max_power_limit) { - *max_power_limit = pp_limit * (100 + od_percent_upper); + *max_power_limit = max_limit * (100 + od_percent_upper); *max_power_limit /= 100; } if (min_power_limit) { - *min_power_limit = pp_limit * (100 - od_percent_lower); + *min_power_limit = min_limit * (100 - od_percent_lower); *min_power_limit /= 100; } @@ -2573,9 +2574,10 @@ static int smu_v13_0_7_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != SMU_13_0_7_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= SMU_13_0_7_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + SMU_13_0_7_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + SMU_13_0_7_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * SMU_13_0_7_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c index d0a8df1aa6b6..2a0c7cde938d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c @@ -194,7 +194,7 @@ int smu_v14_0_init_pptable_microcode(struct smu_context *smu) if (!pptable_id) return 0; - ret = smu_v14_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -229,48 +229,6 @@ int smu_v14_0_check_fw_status(struct smu_context *smu) return -EIO; } -static int smu_v14_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t ppt_offset_bytes; - const struct smc_firmware_header_v2_0 *v2; - - v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data; - - ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); - *size = le32_to_cpu(v2->ppt_size_bytes); - *table = (uint8_t *)v2 + ppt_offset_bytes; - - return 0; -} - -static int smu_v14_0_set_pptable_v2_1(struct smu_context *smu, void **table, - uint32_t *size, uint32_t pptable_id) -{ - struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v2_1 *v2_1; - struct smc_soft_pptable_entry *entries; - uint32_t pptable_count = 0; - int i = 0; - - v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data; - entries = (struct smc_soft_pptable_entry *) - ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset)); - pptable_count = le32_to_cpu(v2_1->pptable_count); - for (i = 0; i < pptable_count; i++) { - if (le32_to_cpu(entries[i].id) == pptable_id) { - *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes)); - *size = le32_to_cpu(entries[i].ppt_size_bytes); - break; - } - } - - if (i == pptable_count) - return -EINVAL; - - return 0; -} - static int smu_v14_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size) { struct amdgpu_device *adev = smu->adev; @@ -293,45 +251,6 @@ static int smu_v14_0_get_pptable_from_vbios(struct smu_context *smu, void **tabl return 0; } -int smu_v14_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id) -{ - const struct smc_firmware_header_v1_0 *hdr; - struct amdgpu_device *adev = smu->adev; - uint16_t version_major, version_minor; - int ret; - - hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; - if (!hdr) - return -EINVAL; - - dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id); - - version_major = le16_to_cpu(hdr->header.header_version_major); - version_minor = le16_to_cpu(hdr->header.header_version_minor); - if (version_major != 2) { - dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n", - version_major, version_minor); - return -EINVAL; - } - - switch (version_minor) { - case 0: - ret = smu_v14_0_set_pptable_v2_0(smu, table, size); - break; - case 1: - ret = smu_v14_0_set_pptable_v2_1(smu, table, size, pptable_id); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - int smu_v14_0_setup_pptable(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; @@ -351,7 +270,7 @@ int smu_v14_0_setup_pptable(struct smu_context *smu) if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1)) ret = smu_v14_0_get_pptable_from_vbios(smu, &table, &size); else - ret = smu_v14_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index fdc1456b885c..d6cf643205ab 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -1828,9 +1828,10 @@ static int smu_v14_0_2_set_power_profile_mode(struct smu_context *smu, return -ENOMEM; } if (custom_params && custom_params_max_idx) { - if (custom_params_max_idx != SMU_14_0_2_CUSTOM_PARAMS_COUNT) - return -EINVAL; - if (custom_params[0] >= SMU_14_0_2_CUSTOM_PARAMS_CLOCK_COUNT) + if (!smu_cmn_custom_params_count_valid(custom_params_max_idx, + SMU_14_0_2_CUSTOM_PARAMS_COUNT) || + !smu_cmn_custom_params_clock_valid(custom_params[0], + SMU_14_0_2_CUSTOM_PARAMS_CLOCK_COUNT)) return -EINVAL; idx = custom_params[0] * SMU_14_0_2_CUSTOM_PARAMS_COUNT; smu->custom_profile_params[idx] = 1; @@ -2889,8 +2890,6 @@ static const struct pptable_funcs smu_v14_0_2_ppt_funcs = { .deep_sleep_control = smu_v14_0_deep_sleep_control, .gfx_ulv_control = smu_v14_0_gfx_ulv_control, .get_bamaco_support = smu_v14_0_get_bamaco_support, - .baco_get_state = smu_v14_0_baco_get_state, - .baco_set_state = smu_v14_0_baco_set_state, .baco_enter = smu_v14_0_2_baco_enter, .baco_exit = smu_v14_0_2_baco_exit, .mode1_reset_is_support = smu_v14_0_2_is_mode1_reset_supported, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c index a1318409e4b5..f3fb6ed4bc95 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0.c @@ -174,7 +174,7 @@ int smu_v15_0_init_pptable_microcode(struct smu_context *smu) if (!pptable_id) return 0; - ret = smu_v15_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; @@ -207,48 +207,6 @@ int smu_v15_0_check_fw_status(struct smu_context *smu) return -EIO; } -static int smu_v15_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) -{ - struct amdgpu_device *adev = smu->adev; - uint32_t ppt_offset_bytes; - const struct smc_firmware_header_v2_0 *v2; - - v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data; - - ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); - *size = le32_to_cpu(v2->ppt_size_bytes); - *table = (uint8_t *)v2 + ppt_offset_bytes; - - return 0; -} - -static int smu_v15_0_set_pptable_v2_1(struct smu_context *smu, void **table, - uint32_t *size, uint32_t pptable_id) -{ - struct amdgpu_device *adev = smu->adev; - const struct smc_firmware_header_v2_1 *v2_1; - struct smc_soft_pptable_entry *entries; - uint32_t pptable_count = 0; - int i = 0; - - v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data; - entries = (struct smc_soft_pptable_entry *) - ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset)); - pptable_count = le32_to_cpu(v2_1->pptable_count); - for (i = 0; i < pptable_count; i++) { - if (le32_to_cpu(entries[i].id) == pptable_id) { - *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes)); - *size = le32_to_cpu(entries[i].ppt_size_bytes); - break; - } - } - - if (i == pptable_count) - return -EINVAL; - - return 0; -} - static int smu_v15_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size) { struct amdgpu_device *adev = smu->adev; @@ -271,45 +229,6 @@ static int smu_v15_0_get_pptable_from_vbios(struct smu_context *smu, void **tabl return 0; } -int smu_v15_0_get_pptable_from_firmware(struct smu_context *smu, - void **table, - uint32_t *size, - uint32_t pptable_id) -{ - const struct smc_firmware_header_v1_0 *hdr; - struct amdgpu_device *adev = smu->adev; - uint16_t version_major, version_minor; - int ret; - - hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; - if (!hdr) - return -EINVAL; - - dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id); - - version_major = le16_to_cpu(hdr->header.header_version_major); - version_minor = le16_to_cpu(hdr->header.header_version_minor); - if (version_major != 2) { - dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n", - version_major, version_minor); - return -EINVAL; - } - - switch (version_minor) { - case 0: - ret = smu_v15_0_set_pptable_v2_0(smu, table, size); - break; - case 1: - ret = smu_v15_0_set_pptable_v2_1(smu, table, size, pptable_id); - break; - default: - ret = -EINVAL; - break; - } - - return ret; -} - int smu_v15_0_setup_pptable(struct smu_context *smu) { struct amdgpu_device *adev = smu->adev; @@ -329,7 +248,7 @@ int smu_v15_0_setup_pptable(struct smu_context *smu) if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1)) ret = smu_v15_0_get_pptable_from_vbios(smu, &table, &size); else - ret = smu_v15_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + ret = smu_cmn_get_pptable_from_firmware(smu, &table, &size, pptable_id); if (ret) return ret; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c index d365f06ac1ac..2bd3ea17e789 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.c @@ -1544,3 +1544,143 @@ int smu_cmn_dpm_pcie_width_idx(int width) return ret; } + +static int smu_cmn_get_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) +{ + const struct smc_firmware_header_v2_0 *v2; + struct amdgpu_device *adev = smu->adev; + size_t fw_size = adev->pm.fw->size; + uint32_t ppt_offset_bytes; + uint32_t ppt_size_bytes; + + if (fw_size < sizeof(*v2)) { + dev_err(adev->dev, + "SMC firmware too small for v2.0 header: %zu < %zu\n", + fw_size, sizeof(*v2)); + return -EINVAL; + } + + v2 = (const struct smc_firmware_header_v2_0 *)adev->pm.fw->data; + + ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); + ppt_size_bytes = le32_to_cpu(v2->ppt_size_bytes); + + if (ppt_offset_bytes > fw_size || + ppt_size_bytes > fw_size - ppt_offset_bytes) { + dev_err(adev->dev, + "pptable v2.0 exceeds firmware binary: offset %u + size %u > %zu\n", + ppt_offset_bytes, ppt_size_bytes, fw_size); + return -EINVAL; + } + + *size = ppt_size_bytes; + *table = (uint8_t *)v2 + ppt_offset_bytes; + + return 0; +} + +static int smu_cmn_get_pptable_v2_1(struct smu_context *smu, void **table, + uint32_t *size, uint32_t pptable_id) +{ + const struct smc_firmware_header_v2_1 *v2_1; + struct amdgpu_device *adev = smu->adev; + struct smc_soft_pptable_entry *entries; + size_t fw_size = adev->pm.fw->size; + uint32_t pptable_entry_offset; + uint32_t ppt_offset_bytes; + uint32_t ppt_size_bytes; + uint32_t pptable_count; + int i; + + if (fw_size < sizeof(*v2_1)) { + dev_err(adev->dev, + "SMC firmware too small for v2.1 header: %zu < %zu\n", + fw_size, sizeof(*v2_1)); + return -EINVAL; + } + + v2_1 = (const struct smc_firmware_header_v2_1 *)adev->pm.fw->data; + + pptable_entry_offset = le32_to_cpu(v2_1->pptable_entry_offset); + pptable_count = le32_to_cpu(v2_1->pptable_count); + + if (pptable_entry_offset > fw_size || + pptable_count > (fw_size - pptable_entry_offset) / sizeof(*entries)) { + dev_err(adev->dev, + "pptable v2.1 entry array exceeds firmware binary: offset %u, count %u\n", + pptable_entry_offset, pptable_count); + return -EINVAL; + } + + entries = (struct smc_soft_pptable_entry *) + ((uint8_t *)v2_1 + pptable_entry_offset); + + for (i = 0; i < pptable_count; i++) { + if (le32_to_cpu(entries[i].id) != pptable_id) + continue; + + ppt_offset_bytes = le32_to_cpu(entries[i].ppt_offset_bytes); + ppt_size_bytes = le32_to_cpu(entries[i].ppt_size_bytes); + + if (ppt_offset_bytes > fw_size || + ppt_size_bytes > fw_size - ppt_offset_bytes) { + dev_err(adev->dev, + "pptable entry %d exceeds firmware binary: offset %u + size %u > %zu\n", + i, ppt_offset_bytes, ppt_size_bytes, fw_size); + return -EINVAL; + } + + *table = (uint8_t *)v2_1 + ppt_offset_bytes; + *size = ppt_size_bytes; + return 0; + } + + return -EINVAL; +} + +/** + * smu_cmn_get_pptable_from_firmware - locate the soft pptable embedded in the + * SMC firmware binary. + * @smu: SMU context + * @table: on success, set to the start of the pptable within the firmware + * blob + * @size: on success, set to the pptable size in bytes + * @pptable_id: the entry ID to search for (used only for v2.1 binaries) + * + * Reads the firmware header version and dispatches to the appropriate v2.x + * parser. Only major version 2 is supported; minor version selects between + * the single-entry (v2.0) and multi-entry directory (v2.1) layouts. + * + * Return: 0 on success, -EINVAL for an unsupported version or if the + * requested pptable cannot be found or exceeds the binary bounds. + */ +int smu_cmn_get_pptable_from_firmware(struct smu_context *smu, void **table, + uint32_t *size, uint32_t pptable_id) +{ + const struct smc_firmware_header_v1_0 *hdr; + struct amdgpu_device *adev = smu->adev; + uint16_t version_major, version_minor; + + hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data; + if (!hdr) + return -EINVAL; + + dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id); + + version_major = le16_to_cpu(hdr->header.header_version_major); + version_minor = le16_to_cpu(hdr->header.header_version_minor); + if (version_major != 2) { + dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n", + version_major, version_minor); + return -EINVAL; + } + + switch (version_minor) { + case 0: + return smu_cmn_get_pptable_v2_0(smu, table, size); + case 1: + return smu_cmn_get_pptable_v2_1(smu, table, size, pptable_id); + default: + return -EINVAL; + } +} diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h index 0e119965ce13..ae6742f5298f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h +++ b/drivers/gpu/drm/amd/pm/swsmu/smu_cmn.h @@ -113,6 +113,16 @@ static inline int pcie_gen_to_speed(uint32_t gen) return ((gen == 0) ? link_speed[0] : link_speed[gen - 1]); } +static inline bool smu_cmn_custom_params_count_valid(u32 max_idx, u32 params_count) +{ + return max_idx == params_count; +} + +static inline bool smu_cmn_custom_params_clock_valid(long clock_idx, long clock_count) +{ + return clock_idx >= 0 && clock_idx < clock_count; +} + int smu_cmn_send_smc_msg_with_param(struct smu_context *smu, enum smu_message_type msg, uint32_t param, @@ -239,6 +249,9 @@ int smu_cmn_dpm_pcie_gen_idx(int gen); int smu_cmn_dpm_pcie_width_idx(int width); int smu_cmn_check_fw_version(struct smu_context *smu); +int smu_cmn_get_pptable_from_firmware(struct smu_context *smu, void **table, + uint32_t *size, uint32_t pptable_id); + /*SMU gpu metrics */ /* Attribute ID mapping */ diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c index 658bf3fdb66b..bfbfdffbfbe6 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_cmd.c @@ -30,9 +30,6 @@ #include "amdgpu_ras_mgr.h" #include "amdgpu_virt_ras_cmd.h" -/* inject address is 52 bits */ -#define RAS_UMC_INJECT_ADDR_LIMIT (0x1ULL << 52) - #define AMDGPU_RAS_TYPE_RASCORE 0x1 #define AMDGPU_RAS_TYPE_AMDGPU 0x2 #define AMDGPU_RAS_TYPE_VF 0x3 diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c index 3ed3ff42b7e1..9c6d0024210d 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_eeprom_i2c.c @@ -67,7 +67,7 @@ static int ras_eeprom_i2c_config(struct ras_core_context *ras_core) struct ras_eeprom_control *control = &ras_core->ras_eeprom; u8 i2c_addr; - if (amdgpu_atomfirmware_ras_rom_addr(adev, &i2c_addr)) { + if (adev->bios && amdgpu_atomfirmware_ras_rom_addr(adev, &i2c_addr)) { /* The address given by VBIOS is an 8-bit, wire-format * address, i.e. the most significant byte. * diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c index a22d1aebbeb9..b62bbb5ea292 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.c @@ -95,11 +95,35 @@ static int amdgpu_ras_mgr_init_aca_config(struct amdgpu_device *adev, return 0; } +static uint64_t amdgpu_ras_mgr_reserved_vram_size(struct amdgpu_device *adev) +{ + struct amdgpu_ras *con = amdgpu_ras_get_context(adev); + uint64_t reserved_pages_in_bytes = 0; + + if (!con || (adev->flags & AMD_IS_APU)) + return 0; + + switch (amdgpu_ip_version(adev, MP0_HWIP, 0)) { + case IP_VERSION(13, 0, 6): + case IP_VERSION(13, 0, 12): + reserved_pages_in_bytes = RAS_RESERVED_VRAM_SIZE_DEFAULT; + break; + case IP_VERSION(13, 0, 14): + reserved_pages_in_bytes = (RAS_RESERVED_VRAM_SIZE_DEFAULT << 1); + break; + default: + break; + } + return reserved_pages_in_bytes; +} + static int amdgpu_ras_mgr_init_eeprom_config(struct amdgpu_device *adev, struct ras_core_config *config) { struct ras_eeprom_config *eeprom_cfg = &config->eeprom_cfg; + uint64_t ras_reserved_vram_size; + ras_reserved_vram_size = amdgpu_ras_mgr_reserved_vram_size(adev); eeprom_cfg->eeprom_sys_fn = &amdgpu_ras_eeprom_i2c_sys_func; eeprom_cfg->eeprom_i2c_adapter = adev->pm.ras_eeprom_i2c_bus; if (eeprom_cfg->eeprom_i2c_adapter) { @@ -133,7 +157,7 @@ static int amdgpu_ras_mgr_init_eeprom_config(struct amdgpu_device *adev, div64_u64(adev->gmc.mc_vram_size, TYPICAL_ECC_BAD_PAGE_RATE); else if (amdgpu_bad_page_threshold == WARN_NONSTOP_OVER_THRESHOLD) eeprom_cfg->eeprom_record_threshold_count = - COUNT_BAD_PAGE_THRESHOLD(RAS_RESERVED_VRAM_SIZE_DEFAULT); + COUNT_BAD_PAGE_THRESHOLD(ras_reserved_vram_size); else eeprom_cfg->eeprom_record_threshold_count = amdgpu_bad_page_threshold; @@ -142,6 +166,21 @@ static int amdgpu_ras_mgr_init_eeprom_config(struct amdgpu_device *adev, return 0; } +static bool amdgpu_ras_mgr_eeprom_is_supported(struct amdgpu_device *adev) +{ + if (amdgpu_sriov_vf(adev)) + return false; + + switch (amdgpu_ip_version(adev, MP1_HWIP, 0)) { + case IP_VERSION(13, 0, 6): + case IP_VERSION(13, 0, 12): + case IP_VERSION(13, 0, 14): + return (adev->gmc.is_app_apu) ? false : true; + default: + return false; + } +} + static int amdgpu_ras_mgr_init_mp1_config(struct amdgpu_device *adev, struct ras_core_config *config) { @@ -266,7 +305,8 @@ static struct ras_core_context *amdgpu_ras_mgr_create_ras_core(struct amdgpu_dev init_config.aca_ip_version = IP_VERSION(1, 0, 0); init_config.sys_fn = &amdgpu_ras_sys_fn; - init_config.ras_eeprom_supported = true; + init_config.ras_eeprom_supported = + amdgpu_ras_mgr_eeprom_is_supported(adev); init_config.poison_supported = amdgpu_ras_is_poison_mode_supported(adev); @@ -425,6 +465,28 @@ static int amdgpu_ras_mgr_hw_fini(struct amdgpu_ip_block *ip_block) return 0; } +int amdgpu_ras_mgr_resume_after_reset(struct amdgpu_device *adev) +{ + struct amdgpu_ras *con = amdgpu_ras_get_context(adev); + struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev); + struct amdgpu_ip_block *ip_block; + + if (!con || !con->uniras_enabled) + return 0; + + if (!ras_mgr || !ras_mgr->ras_core) + return -EINVAL; + + if (ras_mgr->ras_is_ready) + return 0; + + ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_RAS); + if (!ip_block) + return -EINVAL; + + return amdgpu_ras_mgr_hw_init(ip_block); +} + struct amdgpu_ras_mgr *amdgpu_ras_mgr_get_context(struct amdgpu_device *adev) { if (!adev || !adev->psp.ras_context.ras) @@ -733,3 +795,13 @@ int amdgpu_ras_mgr_lookup_bad_pages_in_a_row(struct amdgpu_device *adev, return ras_core_convert_soc_pa_to_cur_nps_pages(ras_mgr->ras_core, addr, nps_page_addr, max_page_count); } + +int amdgpu_ras_mgr_set_debug_mode(struct amdgpu_device *adev, bool enable) +{ + struct amdgpu_ras_mgr *ras_mgr = amdgpu_ras_mgr_get_context(adev); + + if (!ras_mgr || !ras_mgr->ras_core || !ras_mgr->ras_is_ready) + return false; + + return ras_core_set_debug_mode(ras_mgr->ras_core, enable); +} diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h index 4f44a917d48b..a20bb8fdce87 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mgr.h @@ -82,6 +82,8 @@ int amdgpu_ras_mgr_handle_ras_cmd(struct amdgpu_device *adev, void *output, uint32_t out_size); int amdgpu_ras_mgr_pre_reset(struct amdgpu_device *adev); int amdgpu_ras_mgr_post_reset(struct amdgpu_device *adev); +int amdgpu_ras_mgr_resume_after_reset(struct amdgpu_device *adev); int amdgpu_ras_mgr_lookup_bad_pages_in_a_row(struct amdgpu_device *adev, uint64_t addr, uint64_t *nps_page_addr, uint32_t max_page_count); +int amdgpu_ras_mgr_set_debug_mode(struct amdgpu_device *adev, bool enable); #endif diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c index 2098f24d4940..3c4575a5d902 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_mp1_v13_0.c @@ -24,6 +24,7 @@ #include "amdgpu_smu.h" #include "amdgpu_reset.h" #include "amdgpu_ras_mp1_v13_0.h" +#include "smu13_driver_if_v13_0_6.h" #define RAS_MP1_MSG_QueryValidMcaCeCount 0x3A #define RAS_MP1_MSG_McaBankCeDumpDW 0x3B @@ -131,10 +132,23 @@ static int mp1_v13_0_get_ras_enabled_mask(struct ras_core_context *ras_core, return ret; } +static int mp1_v13_0_set_debug_mode(struct ras_core_context *ras_core, bool enable) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev; + int ret; + u32 smu_msg = SMU_MSG_ClearMcaOnRead; + + ret = amdgpu_smu_ras_send_msg(adev, smu_msg, + enable ? 0 : ClearMcaOnRead_UE_FLAG_MASK | + ClearMcaOnRead_CE_POLL_MASK, NULL); + return ret; +} + const struct ras_mp1_sys_func amdgpu_ras_mp1_sys_func_v13_0 = { .mp1_get_valid_bank_count = mp1_v13_0_get_valid_bank_count, .mp1_dump_valid_bank = mp1_v13_0_dump_valid_bank, .mp1_send_eeprom_msg = mp1_v13_0_eeprom_send_msg, .mp1_get_ras_enabled_mask = mp1_v13_0_get_ras_enabled_mask, + .mp1_set_debug_mode = mp1_v13_0_set_debug_mode, }; diff --git a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c index 7d728e523604..e4444798bc73 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c +++ b/drivers/gpu/drm/amd/ras/ras_mgr/amdgpu_ras_sys.c @@ -267,6 +267,24 @@ static int amdgpu_ras_sys_put_gpu_mem(struct ras_core_context *ras_core, return 0; } +static int amdgpu_ras_sys_check_address_sanity(struct ras_core_context *ras_core, + uint64_t addr) +{ + struct amdgpu_device *adev = (struct amdgpu_device *)ras_core->dev; + + if ((addr >= adev->gmc.mc_vram_size && + adev->gmc.mc_vram_size) || + (addr >= RAS_UMC_INJECT_ADDR_LIMIT)) + return -EINVAL; + + if (addr >= adev->gmc.real_vram_size) { + RAS_DEV_WARN(ras_core->dev, "Recorded address out of range: 0x%llx!\n", addr); + return -EINVAL; + } + + return 0; +} + const struct ras_sys_func amdgpu_ras_sys_fn = { .ras_notifier = amdgpu_ras_sys_event_notifier, .get_utc_second_timestamp = amdgpu_ras_sys_get_utc_second_timestamp, @@ -277,4 +295,5 @@ const struct ras_sys_func amdgpu_ras_sys_fn = { .detect_ras_interrupt = amdgpu_ras_sys_detect_ras_interrupt, .get_gpu_mem = amdgpu_ras_sys_get_gpu_mem, .put_gpu_mem = amdgpu_ras_sys_put_gpu_mem, + .check_address_sanity = amdgpu_ras_sys_check_address_sanity, }; 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 f34dda7ce87b..2775c7bf41b7 100644 --- a/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h +++ b/drivers/gpu/drm/amd/ras/ras_mgr/ras_sys.h @@ -30,6 +30,9 @@ #include <linux/mempool.h> #include "amdgpu.h" +/* inject address is 52 bits */ +#define RAS_UMC_INJECT_ADDR_LIMIT (0x1ULL << 52) + #define RAS_DEV_ERR(device, fmt, ...) \ do { \ if (device) \ diff --git a/drivers/gpu/drm/amd/ras/rascore/ras.h b/drivers/gpu/drm/amd/ras/rascore/ras.h index c059fcebaf00..878dfdfcb18a 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras.h +++ b/drivers/gpu/drm/amd/ras/rascore/ras.h @@ -167,6 +167,7 @@ struct ras_mp1_sys_func { enum ras_fw_eeprom_cmd index, uint32_t param, uint32_t *read_arg); int (*mp1_get_ras_enabled_mask)(struct ras_core_context *ras_core, uint64_t *enabled_mask); + int (*mp1_set_debug_mode)(struct ras_core_context *ras_core, bool enable); }; struct ras_eeprom_sys_func { @@ -231,6 +232,7 @@ struct ras_sys_func { enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem); int (*put_gpu_mem)(struct ras_core_context *ras_core, enum gpu_mem_type mem_type, struct gpu_mem_block *gpu_mem); + int (*check_address_sanity)(struct ras_core_context *ras_core, uint64_t addr); }; struct ras_ecc_count { @@ -398,4 +400,7 @@ int ras_core_get_device_system_info(struct ras_core_context *ras_core, struct device_system_info *dev_info); int ras_core_convert_soc_pa_to_cur_nps_pages(struct ras_core_context *ras_core, uint64_t soc_pa, uint64_t *page_pfn, uint32_t max_pages); +int ras_core_check_address_sanity(struct ras_core_context *ras_core, uint64_t addr); + +int ras_core_set_debug_mode(struct ras_core_context *ras_core, bool enable); #endif diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c index 210fbd8851a6..840610538c1f 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_aca_v1_0.c @@ -213,7 +213,7 @@ static int aca_parse_umc_bank(struct ras_core_context *ras_core, struct aca_bank_reg *bank = (struct aca_bank_reg *)data; struct aca_bank_ecc *ecc = (struct aca_bank_ecc *)buf; struct aca_ecc_info bank_info; - uint32_t ext_error_code; + uint32_t ext_error_code, misc0_errcnt; uint64_t status0; status0 = bank->regs[ACA_REG_IDX__STATUS]; @@ -228,15 +228,14 @@ static int aca_parse_umc_bank(struct ras_core_context *ras_core, ecc->bank_info.addr = bank->regs[ACA_REG_IDX__ADDR]; ext_error_code = ACA_REG_STATUS_ERRORCODEEXT(status0); + misc0_errcnt = ACA_REG_MISC0_ERRCNT(bank->regs[ACA_REG_IDX__MISC0]); if (aca_check_umc_de(ras_core, status0)) - ecc->de_count = 1; + ecc->de_count = misc0_errcnt ? misc0_errcnt : 1; else if (aca_check_umc_ue(ras_core, status0)) - ecc->ue_count = ext_error_code ? - 1 : ACA_REG_MISC0_ERRCNT(bank->regs[ACA_REG_IDX__MISC0]); + ecc->ue_count = ext_error_code ? 1 : misc0_errcnt; else if (aca_check_umc_ce(ras_core, status0)) - ecc->ce_count = ext_error_code ? - 1 : ACA_REG_MISC0_ERRCNT(bank->regs[ACA_REG_IDX__MISC0]); + ecc->ce_count = ext_error_code ? 1 : misc0_errcnt; return 0; } @@ -266,7 +265,7 @@ static int aca_parse_bank_default(struct ras_core_context *ras_core, ecc->bank_info.addr = bank->regs[ACA_REG_IDX__ADDR]; if (aca_check_bank_is_de(ras_core, status)) { - ecc->de_count = 1; + ecc->de_count = 0; } else { if (bank->ecc_type == RAS_ERR_TYPE__UE) ecc->ue_count = 1; diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_core.c b/drivers/gpu/drm/amd/ras/rascore/ras_core.c index 62d124a3eeac..c63a358b7e57 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_core.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_core.c @@ -151,6 +151,11 @@ bool ras_core_gpu_is_rma(struct ras_core_context *ras_core) return ras_core->is_rma; } +int ras_core_set_debug_mode(struct ras_core_context *ras_core, bool enable) +{ + return ras_mp1_set_debug_mode(ras_core, enable); +} + static int ras_core_seqno_fifo_write(struct ras_core_context *ras_core, enum ras_seqno_fifo fifo_type, uint64_t seqno) { @@ -676,3 +681,13 @@ int ras_core_convert_soc_pa_to_cur_nps_pages(struct ras_core_context *ras_core, return count; } + +int ras_core_check_address_sanity(struct ras_core_context *ras_core, + uint64_t addr) +{ + if (ras_core && ras_core->sys_fn && + ras_core->sys_fn->check_address_sanity) + return ras_core->sys_fn->check_address_sanity(ras_core, addr); + + return 0; +} diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c index 3a0ea036c9be..62d1a319c08c 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom.c @@ -746,6 +746,9 @@ static int ras_eeprom_update_header(struct ras_eeprom_control *control) int res; bad_page_count = ras_umc_get_badpage_count(ras_core); + ras_core_event_notify(ras_core, RAS_EVENT_ID__UPDATE_BAD_PAGE_NUM, + &bad_page_count); + /* Modify the header if it exceeds. */ if (threshold_config != 0 && diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom_fw.c b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom_fw.c index f5fa80db91fb..59e195652e42 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_eeprom_fw.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_eeprom_fw.c @@ -72,7 +72,7 @@ int ras_fw_get_badpage_count(struct ras_core_context *ras_core, if (ret != -EBUSY) return ret; - mdelay(10); + usleep_range(10000, 15000); now = (uint64_t)ktime_to_ms(ktime_get()); } while (now < end); diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c index f3321df85021..26af09f3574a 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.c @@ -59,9 +59,20 @@ int ras_mp1_dump_bank(struct ras_core_context *ras_core, return mp1->ip_func->dump_valid_bank(ras_core, type, idx, reg_idx, val); } +int ras_mp1_set_debug_mode(struct ras_core_context *ras_core, bool enable) +{ + struct ras_mp1 *mp1 = &ras_core->ras_mp1; + + if (!mp1->ip_func || !mp1->ip_func->set_debug_mode) + return -EOPNOTSUPP; + + return mp1->ip_func->set_debug_mode(ras_core, enable); +} + int ras_mp1_hw_init(struct ras_core_context *ras_core) { struct ras_mp1 *mp1 = &ras_core->ras_mp1; + int ret = 0; mp1->mp1_ip_version = ras_core->config->mp1_ip_version; mp1->sys_func = ras_core->config->mp1_cfg.mp1_sys_fn; @@ -71,8 +82,14 @@ int ras_mp1_hw_init(struct ras_core_context *ras_core) } mp1->ip_func = ras_mp1_get_ip_funcs(ras_core, mp1->mp1_ip_version); + if (!mp1->ip_func) + return -EINVAL; + + ret = ras_mp1_set_debug_mode(ras_core, false); + if (ret) + return -EINVAL; - return mp1->ip_func ? RAS_CORE_OK : -EINVAL; + return ret; } int ras_mp1_hw_fini(struct ras_core_context *ras_core) diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h index de1d08286f41..5bc7c1b7fdab 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h +++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1.h @@ -31,6 +31,7 @@ struct ras_mp1_ip_func { enum ras_err_type type, u32 *count); int (*dump_valid_bank)(struct ras_core_context *ras_core, enum ras_err_type type, u32 idx, u32 reg_idx, u64 *val); + int (*set_debug_mode)(struct ras_core_context *ras_core, bool enable); }; struct ras_mp1 { @@ -47,4 +48,6 @@ int ras_mp1_get_bank_count(struct ras_core_context *ras_core, int ras_mp1_dump_bank(struct ras_core_context *ras_core, u32 ecc_type, u32 idx, u32 reg_idx, u64 *val); + +int ras_mp1_set_debug_mode(struct ras_core_context *ras_core, bool enable); #endif diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c b/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c index 310d39fc816b..1fcfc1995ad3 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_mp1_v13_0.c @@ -99,7 +99,20 @@ static int mp1_v13_0_dump_bank(struct ras_core_context *ras_core, return sys_func->mp1_dump_valid_bank(ras_core, msg, idx, reg_idx, val); } +static int mp1_v13_0_set_debug_mode(struct ras_core_context *ras_core, bool enable) +{ + struct ras_mp1 *mp1 = &ras_core->ras_mp1; + const struct ras_mp1_sys_func *sys_func = mp1->sys_func; + + if (!sys_func || !sys_func->mp1_set_debug_mode) + return -RAS_CORE_NOT_SUPPORTED; + + return sys_func->mp1_set_debug_mode(ras_core, enable); +} + + const struct ras_mp1_ip_func mp1_ras_func_v13_0 = { .get_valid_bank_count = mp1_v13_0_get_bank_count, .dump_valid_bank = mp1_v13_0_dump_bank, + .set_debug_mode = mp1_v13_0_set_debug_mode, }; diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_process.c b/drivers/gpu/drm/amd/ras/rascore/ras_process.c index 3267dcdb169c..c001074c8c56 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_process.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_process.c @@ -248,9 +248,10 @@ int ras_process_init(struct ras_core_context *ras_core) ras_proc->ras_process_thread = kthread_run(ras_process_thread, (void *)ras_core, "ras_process_thread"); - if (!ras_proc->ras_process_thread) { + if (IS_ERR(ras_proc->ras_process_thread)) { RAS_DEV_ERR(ras_core->dev, "Failed to create ras_process_thread.\n"); - ret = -ENOMEM; + ret = PTR_ERR(ras_proc->ras_process_thread); + ras_proc->ras_process_thread = NULL; goto err; } diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c index f32ee2fecf53..e366fb97293e 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.c +++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.c @@ -406,7 +406,7 @@ static int ras_umc_update_eeprom_ram_data(struct ras_core_context *ras_core, struct ras_umc *ras_umc = &ras_core->ras_umc; struct eeprom_store_record *data = &ras_umc->umc_err_data.ram_data; uint64_t page_pfn[16]; - int count = 0, j; + int count = 0, i, j; if (!data->space_left && ras_umc_realloc_err_data_space(ras_core, data, 256)) { @@ -418,10 +418,23 @@ static int ras_umc_update_eeprom_ram_data(struct ras_core_context *ras_core, bps, bps->cur_nps, page_pfn, ARRAY_SIZE(page_pfn)); if (count > 0) { for (j = 0; j < count; j++) { + if (ras_core_check_address_sanity(ras_core, + page_pfn[j] << AMDGPU_GPU_PAGE_SHIFT)) { + + for (i = 0; i < data->count; i++) + if (page_pfn[j] == data->bps[i].cur_nps_retired_row_pfn) + break; + data->bps[data->count].cur_nps_retired_row_pfn = U64_MAX; + data->count++; + data->space_left--; + continue; + } + bps->cur_nps_retired_row_pfn = page_pfn[j]; memcpy(&data->bps[data->count], bps, sizeof(*data->bps)); data->count++; data->space_left--; + data->bad_page_num++; } } else { RAS_DEV_ERR(ras_core->dev, "Failed to convert record to nps pages!"); @@ -431,6 +444,14 @@ static int ras_umc_update_eeprom_ram_data(struct ras_core_context *ras_core, return 0; } +static void ras_umc_update_bad_pages(struct ras_core_context *ras_core) +{ + struct ras_umc *ras_umc = &ras_core->ras_umc; + struct eeprom_store_record *data = &ras_umc->umc_err_data.ram_data; + + data->bad_page_num_old = data->bad_page_num; +} + /* it deal with vram only. */ static int ras_umc_add_bad_pages(struct ras_core_context *ras_core, struct eeprom_umc_record *bps, @@ -506,6 +527,7 @@ int ras_umc_load_bad_pages(struct ras_core_context *ras_core) } else { ras_core->ras_umc.umc_err_data.last_retired_pfn = UMC_INV_MEM_PFN; ret = ras_umc_add_bad_pages(ras_core, bps, ras_num_recs, true); + ras_umc_update_bad_pages(ras_core); } kfree(bps); @@ -521,7 +543,8 @@ static int ras_umc_save_bad_pages(struct ras_core_context *ras_core) { struct ras_umc *ras_umc = &ras_core->ras_umc; struct eeprom_store_record *data = &ras_umc->umc_err_data.rom_data; - uint32_t eeprom_record_num; + struct eeprom_store_record *ram_data = &ras_umc->umc_err_data.ram_data; + uint32_t eeprom_record_num, logical_count = 0; int save_count; int ret = 0; @@ -534,6 +557,7 @@ static int ras_umc_save_bad_pages(struct ras_core_context *ras_core) eeprom_record_num = ras_eeprom_get_record_count(ras_core); mutex_lock(&ras_umc->umc_lock); save_count = data->count - eeprom_record_num; + logical_count = ram_data->bad_page_num - ram_data->bad_page_num_old; /* only new entries are saved */ if (save_count > 0) { if (ras_fw_eeprom_supported(ras_core)) @@ -547,8 +571,8 @@ static int ras_umc_save_bad_pages(struct ras_core_context *ras_core) ret = -EIO; goto exit; } - - RAS_DEV_INFO(ras_core->dev, "Saved %d pages to EEPROM table.\n", save_count); + ras_umc_update_bad_pages(ras_core); + RAS_DEV_INFO(ras_core->dev, "Saved %d pages to EEPROM table.\n", logical_count); } exit: diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h index 237525b46b9b..ee7100f25f51 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_umc.h +++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc.h @@ -119,6 +119,12 @@ struct eeprom_store_record { int count; /* the space can place new entries */ int space_left; + /* logical bad page number */ + int bad_page_num; + /* the bad page number is ras_num_recs or + * ras_num_recs * retire_unit + */ + int bad_page_num_old; }; struct ras_umc_err_data { diff --git a/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h index 8a35ad856165..650b5f1f22f7 100644 --- a/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h +++ b/drivers/gpu/drm/amd/ras/rascore/ras_umc_v12_0.h @@ -290,6 +290,8 @@ /* R13 bit shift should be considered, double the number */ #define UMC_V12_0_BAD_PAGE_NUM_PER_CHANNEL (UMC_V12_0_NA_MAP_PA_NUM * 2) +/* UMC register per channel offset */ +#define UMC_V12_0_PER_CHANNEL_OFFSET 0x400 /* C2, C3, C4, R13, four MCA bits are looped in page retirement */ #define UMC_V12_0_RETIRE_LOOP_BITS 4 diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c index 5ba62e637a61..9aad1d1d28ec 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_dev.c @@ -313,7 +313,11 @@ void komeda_dev_destroy(struct komeda_dev *mdev) int komeda_dev_resume(struct komeda_dev *mdev) { - clk_prepare_enable(mdev->aclk); + int err; + + err = clk_prepare_enable(mdev->aclk); + if (err) + return err; mdev->funcs->enable_irq(mdev); diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c index 4bb5f250e95e..67fffab018ae 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c @@ -74,8 +74,11 @@ static int komeda_platform_probe(struct platform_device *pdev) } pm_runtime_enable(dev); - if (!pm_runtime_enabled(dev)) - komeda_dev_resume(mdrv->mdev); + if (!pm_runtime_enabled(dev)) { + err = komeda_dev_resume(mdrv->mdev); + if (err) + goto err_destroy_mdev; + } mdrv->kms = komeda_kms_attach(mdrv->mdev); if (IS_ERR(mdrv->kms)) { @@ -93,7 +96,7 @@ destroy_mdev: pm_runtime_disable(dev); else komeda_dev_suspend(mdrv->mdev); - +err_destroy_mdev: komeda_dev_destroy(mdrv->mdev); free_mdrv: @@ -140,11 +143,12 @@ static int __maybe_unused komeda_pm_suspend(struct device *dev) static int __maybe_unused komeda_pm_resume(struct device *dev) { struct komeda_drv *mdrv = dev_get_drvdata(dev); + int err = 0; if (!pm_runtime_status_suspended(dev)) - komeda_dev_resume(mdrv->mdev); + err = komeda_dev_resume(mdrv->mdev); - return drm_mode_config_helper_resume(&mdrv->kms->base); + return err ? err : drm_mode_config_helper_resume(&mdrv->kms->base); } static const struct dev_pm_ops komeda_pm_ops = { diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 9abe800f598a..23fa942ae4bb 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -670,6 +670,11 @@ static int malidp_runtime_pm_suspend(struct device *dev) struct drm_device *drm = dev_get_drvdata(dev); struct malidp_drm *malidp = drm_to_malidp(drm); struct malidp_hw_device *hwdev = malidp->dev; + struct clk_bulk_data clks[] = { + { .clk = hwdev->pclk }, + { .clk = hwdev->aclk }, + { .clk = hwdev->mclk }, + }; /* we can only suspend if the hardware is in config mode */ WARN_ON(!hwdev->hw->in_config_mode(hwdev)); @@ -677,9 +682,7 @@ static int malidp_runtime_pm_suspend(struct device *dev) malidp_se_irq_fini(hwdev); malidp_de_irq_fini(hwdev); hwdev->pm_suspended = true; - clk_disable_unprepare(hwdev->mclk); - clk_disable_unprepare(hwdev->aclk); - clk_disable_unprepare(hwdev->pclk); + clk_bulk_disable_unprepare(ARRAY_SIZE(clks), clks); return 0; } @@ -689,10 +692,17 @@ static int malidp_runtime_pm_resume(struct device *dev) struct drm_device *drm = dev_get_drvdata(dev); struct malidp_drm *malidp = drm_to_malidp(drm); struct malidp_hw_device *hwdev = malidp->dev; + struct clk_bulk_data clks[] = { + { .clk = hwdev->pclk }, + { .clk = hwdev->aclk }, + { .clk = hwdev->mclk }, + }; + int err; + + err = clk_bulk_prepare_enable(ARRAY_SIZE(clks), clks); + if (err) + return err; - clk_prepare_enable(hwdev->pclk); - clk_prepare_enable(hwdev->aclk); - clk_prepare_enable(hwdev->mclk); hwdev->pm_suspended = false; malidp_de_irq_hw_init(hwdev); malidp_se_irq_hw_init(hwdev); diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c index 46094cca2974..d4577663a1f0 100644 --- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c @@ -6,7 +6,6 @@ #include <linux/irq.h> #include <linux/mfd/syscon.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/property.h> diff --git a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c index 8e8cfd66f23b..ea43f59f06b2 100644 --- a/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c +++ b/drivers/gpu/drm/bridge/imx/imx8mp-hdmi-tx.c @@ -6,7 +6,6 @@ #include <linux/clk.h> #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <drm/bridge/dw_hdmi.h> diff --git a/drivers/gpu/drm/bridge/inno-hdmi.c b/drivers/gpu/drm/bridge/inno-hdmi.c index 1e2a039cf9e8..dd35f5a875d3 100644 --- a/drivers/gpu/drm/bridge/inno-hdmi.c +++ b/drivers/gpu/drm/bridge/inno-hdmi.c @@ -13,7 +13,6 @@ #include <linux/i2c.h> #include <linux/hdmi.h> #include <linux/mfd/syscon.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/bridge/ssd2825.c b/drivers/gpu/drm/bridge/ssd2825.c index e345f3a652c2..00a4ed4a0700 100644 --- a/drivers/gpu/drm/bridge/ssd2825.c +++ b/drivers/gpu/drm/bridge/ssd2825.c @@ -6,7 +6,6 @@ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/mutex.h> #include <linux/of.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/bridge/tc358762.c b/drivers/gpu/drm/bridge/tc358762.c index 4f9943b0223e..6124d3123f57 100644 --- a/drivers/gpu/drm/bridge/tc358762.c +++ b/drivers/gpu/drm/bridge/tc358762.c @@ -12,7 +12,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of_graph.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/bridge/tc358764.c b/drivers/gpu/drm/bridge/tc358764.c index 084e9d898e22..12b43245bb8f 100644 --- a/drivers/gpu/drm/bridge/tc358764.c +++ b/drivers/gpu/drm/bridge/tc358764.c @@ -9,7 +9,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of_graph.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/bridge/th1520-dw-hdmi.c b/drivers/gpu/drm/bridge/th1520-dw-hdmi.c index 389eead5f1c4..cbea8b14cd4b 100644 --- a/drivers/gpu/drm/bridge/th1520-dw-hdmi.c +++ b/drivers/gpu/drm/bridge/th1520-dw-hdmi.c @@ -9,7 +9,6 @@ */ #include <linux/clk.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/reset.h> diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index 4de36fda0544..7ce9e212770a 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -3740,8 +3740,10 @@ void drm_dp_mst_topology_queue_probe(struct drm_dp_mst_topology_mgr *mgr) { mutex_lock(&mgr->lock); - if (drm_WARN_ON(mgr->dev, !mgr->mst_state || !mgr->mst_primary)) + if (!mgr->mst_state || !mgr->mst_primary) { + drm_dbg_kms(mgr->dev, "queue_probe skipped: topology torn down\n"); goto out_unlock; + } drm_dp_mst_topology_mgr_invalidate_mstb(mgr->mst_primary); drm_dp_mst_queue_probe_work(mgr); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 3f8e025fd6d9..e1eed13a8e94 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1469,6 +1469,25 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, } EXPORT_SYMBOL(drm_mode_duplicate); +static bool drm_mode_match_timings_vrr(const struct drm_display_mode *mode1, + const struct drm_display_mode *mode2) +{ + int mode1_vsync_start_offset = mode1->vtotal - mode1->vsync_start; + int mode1_vsync_end_offset = mode1->vtotal - mode1->vsync_end; + int mode2_vsync_start_offset = mode2->vtotal - mode2->vsync_start; + int mode2_vsync_end_offset = mode2->vtotal - mode2->vsync_end; + + return mode1->hdisplay == mode2->hdisplay && + mode1->hsync_start == mode2->hsync_start && + mode1->hsync_end == mode2->hsync_end && + mode1->htotal == mode2->htotal && + mode1->hskew == mode2->hskew && + mode1->vdisplay == mode2->vdisplay && + mode1_vsync_start_offset == mode2_vsync_start_offset && + mode1_vsync_end_offset == mode2_vsync_end_offset && + mode1->vscan == mode2->vscan; +} + static bool drm_mode_match_timings(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2) { @@ -1538,6 +1557,10 @@ bool drm_mode_match(const struct drm_display_mode *mode1, if (!mode1 || !mode2) return false; + if (match_flags & DRM_MODE_MATCH_TIMINGS_VRR && + !drm_mode_match_timings_vrr(mode1, mode2)) + return false; + if (match_flags & DRM_MODE_MATCH_TIMINGS && !drm_mode_match_timings(mode1, mode2)) return false; diff --git a/drivers/gpu/drm/drm_panel_backlight_quirks.c b/drivers/gpu/drm/drm_panel_backlight_quirks.c index 537dc6dd0534..f85cb293a3db 100644 --- a/drivers/gpu/drm/drm_panel_backlight_quirks.c +++ b/drivers/gpu/drm/drm_panel_backlight_quirks.c @@ -3,7 +3,6 @@ #include <linux/array_size.h> #include <linux/dmi.h> #include <linux/export.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <drm/drm_edid.h> #include <drm/drm_utils.h> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c index 552631c3554a..c314b3cb5e70 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_gpu.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_gpu.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/dma-fence.h> #include <linux/dma-mapping.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index e6d516e1976d..d9637ddfcfc4 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -11,7 +11,6 @@ #include <linux/component.h> #include <linux/kernel.h> #include <linux/mfd/syscon.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/property.h> diff --git a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c index ab3cd309505a..15042365dec0 100644 --- a/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c +++ b/drivers/gpu/drm/hisilicon/kirin/dw_drm_dsi.c @@ -14,7 +14,6 @@ #include <linux/clk.h> #include <linux/component.h> #include <linux/delay.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 07802a7f4ce5..c4de717505d7 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -83,8 +83,7 @@ i915-y += \ i915_fb_pin.o \ i915_hdcp_gsc.o \ i915_initial_plane.o \ - i915_overlay.o \ - i915_panic.o + i915_overlay.o # "Graphics Technology" (aka we talk to the gpu) gt-y += \ @@ -167,6 +166,7 @@ gem-y += \ gem/i915_gem_object.o \ gem/i915_gem_object_frontbuffer.o \ gem/i915_gem_pages.o \ + gem/i915_gem_panic.o \ gem/i915_gem_phys.o \ gem/i915_gem_pm.o \ gem/i915_gem_region.o \ @@ -356,6 +356,7 @@ i915-y += \ display/intel_dp_aux.o \ display/intel_dp_aux_backlight.o \ display/intel_dp_hdcp.o \ + display/intel_dp_link_caps.o \ display/intel_dp_link_training.o \ display/intel_dp_mst.o \ display/intel_dp_test.o \ diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c index 5ff1cdf4581a..b867443ff227 100644 --- a/drivers/gpu/drm/i915/display/g4x_dp.c +++ b/drivers/gpu/drm/i915/display/g4x_dp.c @@ -1222,7 +1222,8 @@ static bool ilk_digital_port_connected(struct intel_encoder *encoder) return intel_de_read(display, DEISR) & bit; } -static int g4x_dp_compute_config(struct intel_encoder *encoder, +static int g4x_dp_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -1232,7 +1233,7 @@ static int g4x_dp_compute_config(struct intel_encoder *encoder, if (HAS_PCH_SPLIT(display) && encoder->port != PORT_A) crtc_state->has_pch_encoder = true; - ret = intel_dp_compute_config(encoder, crtc_state, conn_state); + ret = intel_dp_compute_config(state, encoder, crtc_state, conn_state); if (ret) return ret; @@ -1252,10 +1253,13 @@ static void g4x_dp_suspend_complete(struct intel_encoder *encoder) static void intel_dp_encoder_destroy(struct drm_encoder *encoder) { + struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder)); + intel_dp_encoder_flush_work(encoder); drm_encoder_cleanup(encoder); - kfree(enc_to_dig_port(to_intel_encoder(encoder))); + intel_dp_link_cleanup(&dig_port->dp); + kfree(dig_port); } static void intel_dp_encoder_reset(struct drm_encoder *encoder) @@ -1350,6 +1354,9 @@ bool g4x_dp_init(struct intel_display *display, intel_encoder->audio_enable = g4x_dp_audio_enable; intel_encoder->audio_disable = g4x_dp_audio_disable; + if (intel_dp_link_init(&dig_port->dp) != 0) + goto err_dp_init; + if ((display->platform.ivybridge && port == PORT_A) || (HAS_PCH_CPT(display) && port != PORT_A)) { dig_port->dp.set_link_train = cpt_set_link_train; @@ -1419,6 +1426,8 @@ bool g4x_dp_init(struct intel_display *display, return true; err_init_connector: + intel_dp_link_cleanup(&dig_port->dp); +err_dp_init: drm_encoder_cleanup(encoder); err_encoder_init: kfree(intel_connector); diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c index acb36cab999c..4c33aa1d1d32 100644 --- a/drivers/gpu/drm/i915/display/g4x_hdmi.c +++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c @@ -126,12 +126,12 @@ static bool g4x_compute_has_hdmi_sink(struct intel_atomic_state *state, return false; } -static int g4x_hdmi_compute_config(struct intel_encoder *encoder, +static int g4x_hdmi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->uapi.state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); if (HAS_PCH_SPLIT(display)) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index a549f1fac810..ea0cdb7822f3 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1657,7 +1657,8 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder, return 0; } -static int gen11_dsi_compute_config(struct intel_encoder *encoder, +static int gen11_dsi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -1671,7 +1672,7 @@ static int gen11_dsi_compute_config(struct intel_encoder *encoder, pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB; pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB; - ret = intel_panel_compute_config(intel_connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, intel_connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_alpm.c b/drivers/gpu/drm/i915/display/intel_alpm.c index c6963ea420cc..f1383764b702 100644 --- a/drivers/gpu/drm/i915/display/intel_alpm.c +++ b/drivers/gpu/drm/i915/display/intel_alpm.c @@ -291,7 +291,9 @@ void intel_alpm_lobf_compute_config_late(struct intel_dp *intel_dp, if (!crtc_state->has_lobf) return; - if (!intel_alpm_lobf_is_window1_sufficient(crtc_state)) { + if (crtc_state->has_psr || + !intel_vrr_is_fixed_rr(crtc_state) || + !intel_alpm_lobf_is_window1_sufficient(crtc_state)) { crtc_state->has_lobf = false; return; } @@ -343,11 +345,7 @@ void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp, if (!intel_dp->as_sdp_supported) return; - if (crtc_state->has_psr) - return; - - if (!intel_vrr_always_use_vrr_tg(display) || - !intel_vrr_is_fixed_rr(crtc_state)) + if (!intel_vrr_always_use_vrr_tg(display)) return; if (!(intel_alpm_aux_wake_supported(intel_dp) || @@ -407,6 +405,11 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp, if (crtc_state->disable_as_sdp_when_pr_active) pr_alpm_ctl |= PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE; + if (intel_display_power_dc3co_allowed(display)) + pr_alpm_ctl |= PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL; + else + pr_alpm_ctl &= ~PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL; + intel_de_write(display, PR_ALPM_CTL(display, cpu_transcoder), pr_alpm_ctl); } diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index b6fe87c29aa7..97cbae2e547e 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -623,6 +623,21 @@ get_lfp_data_tail(const struct bdb_lfp_data *data, return NULL; } +static bool is_panel_type_valid(int panel_type) +{ + return panel_type >= 0 && panel_type < 16; +} + +static bool is_panel_type_pnp(int panel_type) +{ + return panel_type == 0xff; +} + +static bool is_panel_type_valid_or_pnp(int panel_type) +{ + return is_panel_type_valid(panel_type) || is_panel_type_pnp(panel_type); +} + static int opregion_get_panel_type(struct intel_display *display, const struct intel_bios_encoder_data *devdata, const struct drm_edid *drm_edid, bool use_fallback) @@ -640,15 +655,21 @@ static int vbt_get_panel_type(struct intel_display *display, if (!lfp_options) return -1; - if (lfp_options->panel_type > 0xf && - lfp_options->panel_type != 0xff) { + if (!is_panel_type_valid_or_pnp(lfp_options->panel_type)) { drm_dbg_kms(display->drm, "Invalid VBT panel type 0x%x\n", lfp_options->panel_type); return -1; } - if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) + if (devdata && devdata->child.handle == DEVICE_HANDLE_LFP2) { + if (!is_panel_type_valid_or_pnp(lfp_options->panel_type2)) { + drm_dbg_kms(display->drm, "Invalid VBT panel type 2 0x%x\n", + lfp_options->panel_type2); + return -1; + } + return lfp_options->panel_type2; + } drm_WARN_ON(display->drm, devdata && devdata->child.handle != DEVICE_HANDLE_LFP1); @@ -762,13 +783,12 @@ static int get_panel_type(struct intel_display *display, panel_types[i].name, panel_types[i].panel_type); } - if (panel_types[PANEL_TYPE_OPREGION].panel_type >= 0) + if (is_panel_type_valid(panel_types[PANEL_TYPE_OPREGION].panel_type)) i = PANEL_TYPE_OPREGION; - else if (panel_types[PANEL_TYPE_VBT].panel_type == 0xff && - panel_types[PANEL_TYPE_PNPID].panel_type >= 0) + else if (is_panel_type_pnp(panel_types[PANEL_TYPE_VBT].panel_type) && + is_panel_type_valid(panel_types[PANEL_TYPE_PNPID].panel_type)) i = PANEL_TYPE_PNPID; - else if (panel_types[PANEL_TYPE_VBT].panel_type != 0xff && - panel_types[PANEL_TYPE_VBT].panel_type >= 0) + else if (is_panel_type_valid(panel_types[PANEL_TYPE_VBT].panel_type)) i = PANEL_TYPE_VBT; else i = PANEL_TYPE_FALLBACK; @@ -2197,52 +2217,52 @@ static u8 translate_iboost(struct intel_display *display, u8 val) static const u8 cnp_ddc_pin_map[] = { [0] = 0, /* N/A */ - [GMBUS_PIN_1_BXT] = DDC_BUS_DDI_B, - [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_C, - [GMBUS_PIN_4_CNP] = DDC_BUS_DDI_D, /* sic */ - [GMBUS_PIN_3_BXT] = DDC_BUS_DDI_F, /* sic */ + [GMBUS_PIN_1] = DDC_BUS_DDI_B, + [GMBUS_PIN_2] = DDC_BUS_DDI_C, + [GMBUS_PIN_4] = DDC_BUS_DDI_D, /* sic */ + [GMBUS_PIN_3] = DDC_BUS_DDI_F, /* sic */ }; static const u8 icp_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, - [GMBUS_PIN_3_BXT] = TGL_DDC_BUS_DDI_C, - [GMBUS_PIN_9_TC1_ICP] = ICL_DDC_BUS_PORT_1, - [GMBUS_PIN_10_TC2_ICP] = ICL_DDC_BUS_PORT_2, - [GMBUS_PIN_11_TC3_ICP] = ICL_DDC_BUS_PORT_3, - [GMBUS_PIN_12_TC4_ICP] = ICL_DDC_BUS_PORT_4, - [GMBUS_PIN_13_TC5_TGP] = TGL_DDC_BUS_PORT_5, - [GMBUS_PIN_14_TC6_TGP] = TGL_DDC_BUS_PORT_6, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_3] = TGL_DDC_BUS_DDI_C, + [GMBUS_PIN_9_TC1] = ICL_DDC_BUS_PORT_1, + [GMBUS_PIN_10_TC2] = ICL_DDC_BUS_PORT_2, + [GMBUS_PIN_11_TC3] = ICL_DDC_BUS_PORT_3, + [GMBUS_PIN_12_TC4] = ICL_DDC_BUS_PORT_4, + [GMBUS_PIN_13_TC5] = TGL_DDC_BUS_PORT_5, + [GMBUS_PIN_14_TC6] = TGL_DDC_BUS_PORT_6, }; static const u8 rkl_pch_tgp_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, - [GMBUS_PIN_9_TC1_ICP] = RKL_DDC_BUS_DDI_D, - [GMBUS_PIN_10_TC2_ICP] = RKL_DDC_BUS_DDI_E, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1] = RKL_DDC_BUS_DDI_D, + [GMBUS_PIN_10_TC2] = RKL_DDC_BUS_DDI_E, }; static const u8 adls_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_9_TC1_ICP] = ADLS_DDC_BUS_PORT_TC1, - [GMBUS_PIN_10_TC2_ICP] = ADLS_DDC_BUS_PORT_TC2, - [GMBUS_PIN_11_TC3_ICP] = ADLS_DDC_BUS_PORT_TC3, - [GMBUS_PIN_12_TC4_ICP] = ADLS_DDC_BUS_PORT_TC4, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_9_TC1] = ADLS_DDC_BUS_PORT_TC1, + [GMBUS_PIN_10_TC2] = ADLS_DDC_BUS_PORT_TC2, + [GMBUS_PIN_11_TC3] = ADLS_DDC_BUS_PORT_TC3, + [GMBUS_PIN_12_TC4] = ADLS_DDC_BUS_PORT_TC4, }; static const u8 gen9bc_tgp_ddc_pin_map[] = { - [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_B, - [GMBUS_PIN_9_TC1_ICP] = DDC_BUS_DDI_C, - [GMBUS_PIN_10_TC2_ICP] = DDC_BUS_DDI_D, + [GMBUS_PIN_2] = DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1] = DDC_BUS_DDI_C, + [GMBUS_PIN_10_TC2] = DDC_BUS_DDI_D, }; static const u8 adlp_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, - [GMBUS_PIN_9_TC1_ICP] = ADLP_DDC_BUS_PORT_TC1, - [GMBUS_PIN_10_TC2_ICP] = ADLP_DDC_BUS_PORT_TC2, - [GMBUS_PIN_11_TC3_ICP] = ADLP_DDC_BUS_PORT_TC3, - [GMBUS_PIN_12_TC4_ICP] = ADLP_DDC_BUS_PORT_TC4, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1] = ADLP_DDC_BUS_PORT_TC1, + [GMBUS_PIN_10_TC2] = ADLP_DDC_BUS_PORT_TC2, + [GMBUS_PIN_11_TC3] = ADLP_DDC_BUS_PORT_TC3, + [GMBUS_PIN_12_TC4] = ADLP_DDC_BUS_PORT_TC4, }; static u8 map_ddc_pin(struct intel_display *display, u8 vbt_pin) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index dc5a5b639d87..41539fdfeac5 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -59,7 +59,7 @@ struct intel_psf_gv_point { struct intel_qgv_info { struct intel_qgv_point points[I915_NUM_QGV_POINTS]; struct intel_psf_gv_point psf_points[I915_NUM_PSF_GV_POINTS]; - u8 num_points; + u8 num_qgv_points; u8 num_psf_points; u8 t_bl; u8 max_numchannels; @@ -155,8 +155,8 @@ static int adls_pcode_read_psf_gv_point_info(struct intel_display *display, static u16 icl_qgv_points_mask(struct intel_display *display) { - unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points; - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_psf_gv_points = display->bw.num_psf_gv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; u16 qgv_points = 0, psf_points = 0; /* @@ -252,7 +252,7 @@ static int icl_get_qgv_points(struct intel_display *display, { int i, ret; - qi->num_points = dram_info->num_qgv_points; + qi->num_qgv_points = dram_info->num_qgv_points; qi->num_psf_points = dram_info->num_psf_gv_points; if (DISPLAY_VER(display) >= 14) { @@ -324,10 +324,10 @@ static int icl_get_qgv_points(struct intel_display *display, } if (drm_WARN_ON(display->drm, - qi->num_points > ARRAY_SIZE(qi->points))) - qi->num_points = ARRAY_SIZE(qi->points); + qi->num_qgv_points > ARRAY_SIZE(qi->points))) + qi->num_qgv_points = ARRAY_SIZE(qi->points); - for (i = 0; i < qi->num_points; i++) { + for (i = 0; i < qi->num_qgv_points; i++) { struct intel_qgv_point *sp = &qi->points[i]; ret = intel_read_qgv_point_info(display, sp, i); @@ -373,7 +373,7 @@ static int icl_sagv_max_dclk(const struct intel_qgv_info *qi) u16 dclk = 0; int i; - for (i = 0; i < qi->num_points; i++) + for (i = 0; i < qi->num_qgv_points; i++) dclk = max(dclk, qi->points[i].dclk); return dclk; @@ -536,6 +536,9 @@ static int icl_get_bw_info(struct intel_display *display, ipqdepth = min(ipqdepthpch, display_bw_params->displayrtids / num_channels); qi.deinterleave = DIV_ROUND_UP(num_channels, is_y_tile(display) ? 4 : 2); + display->bw.num_qgv_points = qi.num_qgv_points; + display->bw.num_psf_gv_points = qi.num_psf_points; + for (i = 0; i < num_groups; i++) { struct intel_bw_info *bi = &display->bw.max[i]; int clpchgroup; @@ -544,10 +547,7 @@ static int icl_get_bw_info(struct intel_display *display, clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i; bi->num_planes = (ipqdepth - clpchgroup) / clpchgroup + 1; - bi->num_qgv_points = qi.num_points; - bi->num_psf_gv_points = qi.num_psf_points; - - for (j = 0; j < qi.num_points; j++) { + for (j = 0; j < qi.num_qgv_points; j++) { const struct intel_qgv_point *sp = &qi.points[j]; int ct, bw; @@ -574,7 +574,7 @@ static int icl_get_bw_info(struct intel_display *display, * SAGV point, but we can't send PCode commands to restrict it * as it will fail and pointless anyway. */ - if (qi.num_points == 1) + if (qi.num_qgv_points == 1) display->sagv.status = I915_SAGV_NOT_CONTROLLED; else display->sagv.status = I915_SAGV_ENABLED; @@ -629,16 +629,20 @@ static int tgl_get_bw_info(struct intel_display *display, */ clperchgroup = 4 * (8 / num_channels) * qi.deinterleave; + display->bw.num_qgv_points = qi.num_qgv_points; + display->bw.num_psf_gv_points = qi.num_psf_points; + + display->bw.max[0].num_planes = U8_MAX; + for (i = 0; i < num_groups; i++) { struct intel_bw_info *bi = &display->bw.max[i]; - struct intel_bw_info *bi_next; int clpchgroup; int j; clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i; if (i < num_groups - 1) { - bi_next = &display->bw.max[i + 1]; + struct intel_bw_info *bi_next = &display->bw.max[i + 1]; if (clpchgroup < clperchgroup) bi_next->num_planes = (ipqdepth - clpchgroup) / clpchgroup; @@ -646,10 +650,7 @@ static int tgl_get_bw_info(struct intel_display *display, bi_next->num_planes = 0; } - bi->num_qgv_points = qi.num_points; - bi->num_psf_gv_points = qi.num_psf_points; - - for (j = 0; j < qi.num_points; j++) { + for (j = 0; j < qi.num_qgv_points; j++) { const struct intel_qgv_point *sp = &qi.points[j]; int ct, bw; @@ -665,23 +666,27 @@ static int tgl_get_bw_info(struct intel_display *display, bi->deratedbw[j] = min(maxdebw, bw * (100 - soc_bw_params->derating) / 100); - bi->peakbw[j] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); drm_dbg_kms(display->drm, - "BW%d / QGV %d: num_planes=%d deratedbw=%u peakbw: %u\n", - i, j, bi->num_planes, bi->deratedbw[j], - bi->peakbw[j]); + "BW%d / QGV %d: num_planes=%d deratedbw=%u\n", + i, j, bi->num_planes, bi->deratedbw[j]); } + } - for (j = 0; j < qi.num_psf_points; j++) { - const struct intel_psf_gv_point *sp = &qi.psf_points[j]; + for (i = 0; i < qi.num_qgv_points; i++) { + const struct intel_qgv_point *sp = &qi.points[i]; - bi->psf_bw[j] = adl_calc_psf_bw(sp->clk); + display->bw.peakbw[i] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); - drm_dbg_kms(display->drm, - "BW%d / PSF GV %d: num_planes=%d bw=%u\n", - i, j, bi->num_planes, bi->psf_bw[j]); - } + drm_dbg_kms(display->drm, "QGV %d: peakbw=%u\n", i, display->bw.peakbw[i]); + } + + for (i = 0; i < qi.num_psf_points; i++) { + const struct intel_psf_gv_point *sp = &qi.psf_points[i]; + + display->bw.psf_bw[i] = adl_calc_psf_bw(sp->clk); + + drm_dbg_kms(display->drm, "PSF GV %d: bw=%u\n", i, display->bw.psf_bw[i]); } /* @@ -689,7 +694,7 @@ static int tgl_get_bw_info(struct intel_display *display, * SAGV point, but we can't send PCode commands to restrict it * as it will fail and pointless anyway. */ - if (qi.num_points == 1) + if (qi.num_qgv_points == 1) display->sagv.status = I915_SAGV_NOT_CONTROLLED; else display->sagv.status = I915_SAGV_ENABLED; @@ -699,25 +704,20 @@ static int tgl_get_bw_info(struct intel_display *display, static void dg2_get_bw_info(struct intel_display *display) { - unsigned int deratedbw = display->platform.dg2_g11 ? 38000 : 50000; - int num_groups = ARRAY_SIZE(display->bw.max); int i; - /* - * DG2 doesn't have SAGV or QGV points, just a constant max bandwidth - * that doesn't depend on the number of planes enabled. So fill all the - * plane group with constant bw information for uniformity with other - * platforms. DG2-G10 platforms have a constant 50 GB/s bandwidth, - * whereas DG2-G11 platforms have 38 GB/s. - */ - for (i = 0; i < num_groups; i++) { - struct intel_bw_info *bi = &display->bw.max[i]; + display->bw.num_qgv_points = 1; - bi->num_planes = 1; - /* Need only one dummy QGV point per group */ - bi->num_qgv_points = 1; - bi->deratedbw[0] = deratedbw; - } + display->bw.max[0].num_planes = U8_MAX; + display->bw.max[0].deratedbw[0] = display->platform.dg2_g11 ? 38000 : 50000; + + drm_dbg_kms(display->drm, + "QGV 0: deratedbw=%u\n", + display->bw.max[0].deratedbw[0]); + + /* Bandwidth does not depend on # of planes; set all groups the same */ + for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) + display->bw.max[i] = display->bw.max[0]; display->sagv.status = I915_SAGV_NOT_CONTROLLED; } @@ -741,31 +741,32 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, peakbw = tgl_peakbw(num_channels, qi.channel_width, icl_sagv_max_dclk(&qi)); maxdebw = min(soc_bw_params->deprogbwlimit * 1000, peakbw * DEPROGBWPCLIMIT / 100); - for (i = 0; i < qi.num_points; i++) { + display->bw.num_qgv_points = qi.num_qgv_points; + + display->bw.max[0].num_planes = U8_MAX; + + for (i = 0; i < qi.num_qgv_points; i++) { const struct intel_qgv_point *sp = &qi.points[i]; int bw = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); display->bw.max[0].deratedbw[i] = min(maxdebw, (100 - soc_bw_params->derating) * bw / 100); - display->bw.max[0].peakbw[i] = bw; - drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw: %u\n", - i, display->bw.max[0].deratedbw[i], - display->bw.max[0].peakbw[i]); + display->bw.peakbw[i] = bw; + + drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw=%u\n", + i, display->bw.max[0].deratedbw[i], display->bw.peakbw[i]); } /* Bandwidth does not depend on # of planes; set all groups the same */ - display->bw.max[0].num_planes = 1; - display->bw.max[0].num_qgv_points = qi.num_points; for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) - memcpy(&display->bw.max[i], &display->bw.max[0], - sizeof(display->bw.max[0])); + display->bw.max[i] = display->bw.max[0]; /* * Xe2_HPD should always have exactly two QGV points representing * battery and plugged-in operation. */ - drm_WARN_ON(display->drm, qi.num_points != 2); + drm_WARN_ON(display->drm, qi.num_qgv_points != 2); display->sagv.status = I915_SAGV_ENABLED; return 0; @@ -776,6 +777,9 @@ static unsigned int icl_max_bw_index(struct intel_display *display, { int i; + if (qgv_point >= display->bw.num_qgv_points) + return UINT_MAX; + /* * Let's return max bw for 0 planes */ @@ -785,13 +789,6 @@ static unsigned int icl_max_bw_index(struct intel_display *display, const struct intel_bw_info *bi = &display->bw.max[i]; - /* - * Pcode will not expose all QGV points when - * SAGV is forced to off/min/med/max. - */ - if (qgv_point >= bi->num_qgv_points) - return UINT_MAX; - if (num_planes >= bi->num_planes) return i; } @@ -804,31 +801,24 @@ static unsigned int tgl_max_bw_index(struct intel_display *display, { int i; + if (qgv_point >= display->bw.num_qgv_points) + return UINT_MAX; + for (i = ARRAY_SIZE(display->bw.max) - 1; i >= 0; i--) { const struct intel_bw_info *bi = &display->bw.max[i]; - /* - * Pcode will not expose all QGV points when - * SAGV is forced to off/min/med/max. - */ - if (qgv_point >= bi->num_qgv_points) - return UINT_MAX; - if (num_planes <= bi->num_planes) return i; } - return 0; + return UINT_MAX; } static unsigned int adl_psf_bw(struct intel_display *display, int psf_gv_point) { - const struct intel_bw_info *bi = - &display->bw.max[0]; - - return bi->psf_bw[psf_gv_point]; + return display->bw.psf_bw[psf_gv_point]; } static unsigned int icl_qgv_bw(struct intel_display *display, @@ -950,7 +940,7 @@ intel_atomic_get_bw_state(struct intel_atomic_state *state) static unsigned int icl_max_bw_qgv_point_mask(struct intel_display *display, int num_active_planes) { - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; unsigned int max_bw_point = 0; unsigned int max_bw = 0; int i; @@ -986,7 +976,7 @@ static u16 icl_prepare_qgv_points_mask(struct intel_display *display, static unsigned int icl_max_bw_psf_gv_point_mask(struct intel_display *display) { - unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points; + unsigned int num_psf_gv_points = display->bw.num_psf_gv_points; unsigned int max_bw_point_mask = 0; unsigned int max_bw = 0; int i; @@ -1091,7 +1081,7 @@ static int mtl_find_qgv_points(struct intel_display *display, struct intel_bw_state *new_bw_state) { unsigned int best_rate = UINT_MAX; - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; unsigned int qgv_peak_bw = 0; int i; int ret; @@ -1116,21 +1106,15 @@ static int mtl_find_qgv_points(struct intel_display *display, * offered per plane group */ for (i = 0; i < num_qgv_points; i++) { - unsigned int bw_index = - tgl_max_bw_index(display, num_active_planes, i); - unsigned int max_data_rate; - - if (bw_index >= ARRAY_SIZE(display->bw.max)) - continue; - - max_data_rate = display->bw.max[bw_index].deratedbw[i]; + unsigned int max_data_rate = + icl_qgv_bw(display, num_active_planes, i); if (max_data_rate < data_rate) continue; - if (max_data_rate - data_rate < best_rate) { - best_rate = max_data_rate - data_rate; - qgv_peak_bw = display->bw.max[bw_index].peakbw[i]; + if (max_data_rate < best_rate) { + best_rate = max_data_rate; + qgv_peak_bw = display->bw.peakbw[i]; } drm_dbg_kms(display->drm, "QGV point %d: max bw %d required %d qgv_peak_bw: %d\n", @@ -1162,8 +1146,8 @@ static int icl_find_qgv_points(struct intel_display *display, const struct intel_bw_state *old_bw_state, struct intel_bw_state *new_bw_state) { - unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points; - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_psf_gv_points = display->bw.num_psf_gv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; u16 psf_points = 0; u16 qgv_points = 0; int i; diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 7bc9b956554b..d3c5e3438d19 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1229,23 +1229,28 @@ static void skl_set_cdclk(struct intel_display *display, static void skl_sanitize_cdclk(struct intel_display *display) { - u32 cdctl, expected; + u32 cdctl, expected, swf18; /* * check if the pre-os initialized the display * There is SWF18 scratchpad register defined which is set by the * pre-os which can be used by the OS drivers to check the status */ - if ((intel_de_read(display, SWF_ILK(0x18)) & 0x00FFFFFF) == 0) + swf18 = intel_de_read(display, SWF_ILK(0x18)); + if ((swf18 & 0x00FFFFFF) == 0) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to SWF18 0x%x\n", swf18); goto sanitize; + } intel_update_cdclk(display); intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK"); /* Is PLL enabled and locked ? */ if (display->cdclk.hw.vco == 0 || - display->cdclk.hw.cdclk == display->cdclk.hw.bypass) + display->cdclk.hw.cdclk == display->cdclk.hw.bypass) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n"); goto sanitize; + } /* DPLL okay; verify the cdclock * @@ -1261,8 +1266,11 @@ static void skl_sanitize_cdclk(struct intel_display *display) cdctl &= ~CDCLK_FREQ_DECIMAL_MASK; cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; - if (cdctl != expected) + if (cdctl != expected) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x)\n", + intel_de_read(display, CDCLK_CTL), expected); goto sanitize; + } drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", intel_de_read(display, CDCLK_CTL), expected); @@ -1274,8 +1282,6 @@ static void skl_sanitize_cdclk(struct intel_display *display) return; sanitize: - drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); - /* force cdclk programming */ display->cdclk.hw.cdclk = 0; /* force full PLL disable + enable */ @@ -1938,14 +1944,17 @@ static void adlp_cdclk_pll_crawl(struct intel_display *display, int vco) display->cdclk.hw.vco = vco; } +static u32 bxt_cdclk_cd2x_pipe_mask(struct intel_display *display) +{ + if (DISPLAY_VER(display) >= 11) + return ICL_CDCLK_CD2X_PIPE_MASK; + else + return BXT_CDCLK_CD2X_PIPE_MASK; +} + static u32 bxt_cdclk_cd2x_pipe(struct intel_display *display, enum pipe pipe) { - if (DISPLAY_VER(display) >= 12) { - if (pipe == INVALID_PIPE) - return TGL_CDCLK_CD2X_PIPE_NONE; - else - return TGL_CDCLK_CD2X_PIPE(pipe); - } else if (DISPLAY_VER(display) >= 11) { + if (DISPLAY_VER(display) >= 11) { if (pipe == INVALID_PIPE) return ICL_CDCLK_CD2X_PIPE_NONE; else @@ -2340,18 +2349,24 @@ static void bxt_sanitize_cdclk(struct intel_display *display) intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK"); if (display->cdclk.hw.vco == 0 || - display->cdclk.hw.cdclk == display->cdclk.hw.bypass) + display->cdclk.hw.cdclk == display->cdclk.hw.bypass) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n"); goto sanitize; + } /* Make sure this is a legal cdclk value for the platform */ cdclk = bxt_calc_cdclk(display, display->cdclk.hw.cdclk); - if (cdclk != display->cdclk.hw.cdclk) + if (cdclk != display->cdclk.hw.cdclk) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad CDCLK frequency\n"); goto sanitize; + } /* Make sure the VCO is correct for the cdclk */ vco = bxt_calc_cdclk_pll_vco(display, cdclk); - if (vco != display->cdclk.hw.vco) + if (vco != display->cdclk.hw.vco) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad VCO frequency\n"); goto sanitize; + } /* * Some BIOS versions leave an incorrect decimal frequency value and @@ -2366,7 +2381,7 @@ static void bxt_sanitize_cdclk(struct intel_display *display) * dividers both syncing to an active pipe, or asynchronously * (PIPE_NONE). */ - cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); + cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display); cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); if (cdctl != expected) { @@ -2375,8 +2390,11 @@ static void bxt_sanitize_cdclk(struct intel_display *display) cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; } - if (cdctl != expected) + if (cdctl != expected) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x\n", + intel_de_read(display, CDCLK_CTL), expected); goto sanitize; + } drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", intel_de_read(display, CDCLK_CTL), expected); @@ -2388,8 +2406,6 @@ static void bxt_sanitize_cdclk(struct intel_display *display) return; sanitize: - drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); - /* force cdclk programming */ display->cdclk.hw.cdclk = 0; diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index e1fdc6fe9762..c8e0f90af910 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -4,7 +4,6 @@ */ #include <linux/string_choices.h> -#include <linux/types.h> #include <drm/drm_device.h> #include <drm/drm_print.h> @@ -13,9 +12,14 @@ #include "intel_cmtg_regs.h" #include "intel_crtc.h" #include "intel_de.h" +#include "intel_display.h" #include "intel_display_device.h" +#include "intel_display_irq.h" #include "intel_display_power.h" #include "intel_display_regs.h" +#include "intel_display_types.h" +#include "intel_vrr.h" +#include "intel_vrr_regs.h" /** * DOC: Common Primary Timing Generator (CMTG) @@ -81,6 +85,18 @@ static void intel_cmtg_dump_config(struct intel_display *display, str_yes_no(cmtg_config->trans_b_secondary)); } +static inline enum transcoder to_cmtg_transcoder(enum transcoder cpu_transcoder) +{ + switch (cpu_transcoder) { + case TRANSCODER_A: + return TRANSCODER_CMTG0; + case TRANSCODER_B: + return TRANSCODER_CMTG1; + default: + return INVALID_TRANSCODER; + } +} + static bool intel_cmtg_transcoder_is_secondary(struct intel_display *display, enum transcoder trans) { @@ -103,11 +119,11 @@ static void intel_cmtg_get_config(struct intel_display *display, { u32 val; - val = intel_de_read(display, TRANS_CMTG_CTL_A); + val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_A)); cmtg_config->cmtg_a_enable = val & CMTG_ENABLE; if (intel_cmtg_has_cmtg_b(display)) { - val = intel_de_read(display, TRANS_CMTG_CTL_B); + val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_B)); cmtg_config->cmtg_b_enable = val & CMTG_ENABLE; } @@ -124,8 +140,8 @@ static bool intel_cmtg_disable_requires_modeset(struct intel_display *display, return cmtg_config->trans_a_secondary || cmtg_config->trans_b_secondary; } -static void intel_cmtg_disable(struct intel_display *display, - struct intel_cmtg_config *cmtg_config) +static void intel_cmtg_disable_all(struct intel_display *display, + struct intel_cmtg_config *cmtg_config) { u32 clk_sel_clr = 0; u32 clk_sel_set = 0; @@ -140,14 +156,14 @@ static void intel_cmtg_disable(struct intel_display *display, if (cmtg_config->cmtg_a_enable) { drm_dbg_kms(display->drm, "Disabling CMTG A\n"); - intel_de_rmw(display, TRANS_CMTG_CTL_A, CMTG_ENABLE, 0); + intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_A), CMTG_ENABLE, 0); clk_sel_clr |= CMTG_CLK_SEL_A_MASK; clk_sel_set |= CMTG_CLK_SEL_A_DISABLED; } if (cmtg_config->cmtg_b_enable) { drm_dbg_kms(display->drm, "Disabling CMTG B\n"); - intel_de_rmw(display, TRANS_CMTG_CTL_B, CMTG_ENABLE, 0); + intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_B), CMTG_ENABLE, 0); clk_sel_clr |= CMTG_CLK_SEL_B_MASK; clk_sel_set |= CMTG_CLK_SEL_B_DISABLED; } @@ -156,6 +172,57 @@ static void intel_cmtg_disable(struct intel_display *display, intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set); } +void intel_cmtg_disable(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + u32 clk_sel_clr = 0, interrupt_mask = 0; + + if (!crtc->cmtg.enabled) + return; + + if (drm_WARN_ON(display->drm, cmtg_transcoder == INVALID_TRANSCODER)) + return; + + crtc->cmtg.enabled = false; + intel_de_rmw(display, TRANS_VRR_CTL(display, cmtg_transcoder), + VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN, 0); + + /* + * Use cpu_transcoder for: + * 1. Exclusive CMTG registers that do not use the standard transcoder offset + * (e.g., TRANS_CMTG_CTL, CMTG_CLK_SEL). + * 2. Registers shared between the eDP and CMTG transcoders. + * (e.g., TRANS_DDI_FUNC_CTL2). + */ + + intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), + CMTG_SECONDARY_MODE, 0); + intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), CMTG_HW_GB_ENABLE, 0); + + intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_ENABLE, 0); + + if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_STATE, 50)) { + drm_WARN(display->drm, 1, "CMTG: %s disable timeout\n", + transcoder_name(cpu_transcoder)); + return; + } + + clk_sel_clr = cpu_transcoder == TRANSCODER_A ? CMTG_CLK_SEL_A_MASK : CMTG_CLK_SEL_B_MASK; + intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, 0); + + drm_dbg_kms(display->drm, "CMTG: %s disabled\n", transcoder_name(cpu_transcoder)); + + if (cpu_transcoder == TRANSCODER_A) + interrupt_mask = CMTG_VBLANK_A; + else if (cpu_transcoder == TRANSCODER_B) + interrupt_mask = CMTG_VBLANK_B; + + intel_display_irq_port_interrupt_mask(display, interrupt_mask, true); +} + /* * Read out CMTG configuration and, on platforms that allow disabling it without * a modeset, do it. @@ -183,5 +250,202 @@ void intel_cmtg_sanitize(struct intel_display *display) if (intel_cmtg_disable_requires_modeset(display, &cmtg_config)) return; - intel_cmtg_disable(display, &cmtg_config); + intel_cmtg_disable_all(display, &cmtg_config); +} + +bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + + if ((cpu_transcoder == TRANSCODER_A || cpu_transcoder == TRANSCODER_B) && + DISPLAY_VER(display) == 35 && intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) + return true; + + return false; +} + +void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 clk_sel_clr = 0; + u32 clk_sel_set = 0; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + if (cpu_transcoder == TRANSCODER_A) { + clk_sel_clr = CMTG_CLK_SEL_A_MASK; + clk_sel_set = CMTG_CLK_SELECT_PHYA_ENABLE; + } else if (cpu_transcoder == TRANSCODER_B) { + clk_sel_clr = CMTG_CLK_SEL_B_MASK; + clk_sel_set = CMTG_CLK_SELECT_PHYB_ENABLE; + } + + if (clk_sel_set) + intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set); +} + +void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type) +{ + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + + if (cmtg_transcoder == INVALID_TRANSCODER) + return; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + if (type == LRR) + intel_set_transcoder_timings_lrr(crtc_state, cmtg_transcoder); + else + intel_set_transcoder_timings(crtc_state, cmtg_transcoder); +} + +void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state) +{ + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + intel_vrr_set_fixed_rr_timings(crtc_state, cmtg_transcoder); +} + +void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + u32 vrr_ctl; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + vrr_ctl = VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN | + XELPD_VRR_CTL_VRR_GUARDBAND(crtc_state->vrr.guardband); + + /* TODO: The code below may need to be revisited once CMRR is enabled */ + if (crtc_state->cmrr.enable) + vrr_ctl |= VRR_CTL_CMRR_ENABLE; + + intel_de_write(display, TRANS_VRR_CTL(display, cmtg_transcoder), vrr_ctl); +} + +void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + const struct intel_link_m_n *m_n = &crtc_state->dp_m_n; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + intel_de_write(display, PIPE_LINK_M1(display, cmtg_transcoder), m_n->link_m); + intel_de_write(display, PIPE_LINK_N1(display, cmtg_transcoder), m_n->link_n); +} + +static void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 cmtg_ctl; + + cmtg_ctl = CMTG_SYNC_TO_PORT | CMTG_ENABLE; + + intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), 0, cmtg_ctl); + if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder), + CMTG_SYNC_TO_PORT, 50)) { + drm_WARN(display->drm, 1, "CMTG: %s enable timeout\n", + transcoder_name(cpu_transcoder)); + } +} + +static void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 interrupt_mask = 0; + + intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), 0, CMTG_SECONDARY_MODE); + intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), 0, CMTG_HW_GB_ENABLE); + crtc->cmtg.enabled = true; + drm_dbg_kms(display->drm, "CMTG: %s enabled\n", transcoder_name(cpu_transcoder)); + + /* + * TODO: Currently cmtg is enabled along with eDP transcoder so cmtg + * interrupt is not enabled through IER, need to do some fine + * tuning in future. + */ + + if (cpu_transcoder == TRANSCODER_A) + interrupt_mask = CMTG_VBLANK_A; + else if (cpu_transcoder == TRANSCODER_B) + interrupt_mask = CMTG_VBLANK_B; + + intel_display_irq_port_interrupt_mask(display, interrupt_mask, false); +} + +/* Bspec: 75253 */ +#define DC3CO_ENTRY_LATENCY_US 55 +#define DC3CO_EXIT_LATENCY_US 40 + +static void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 breakeven_gb; + u32 dc5_exit_latency; + u32 line_time_us = 75; /* Max default initialization value */ + u32 val; + + if (crtc_state->linetime) + line_time_us = DIV_ROUND_UP(crtc_state->linetime, 8); + + /* Break Even Guardband - DC3co Entry Latency / linetime */ + breakeven_gb = DIV_ROUND_UP(DC3CO_ENTRY_LATENCY_US, line_time_us); + + /* DC5 Exit Latency - DC3co Exit Latency / linetime */ + dc5_exit_latency = DIV_ROUND_UP(DC3CO_EXIT_LATENCY_US, line_time_us); + + val = REG_FIELD_PREP(CMTG_HW_GB_BREAKEVEN_MASK, breakeven_gb) | + REG_FIELD_PREP(CMTG_HW_GB_DC5_EXIT_LATENCY_MASK, dc5_exit_latency) | + REG_FIELD_PREP(CMTG_HW_GB_UP_LW_BG_DIFF_MASK, 1); + + intel_de_write(display, CMTG_HW_GB(cpu_transcoder), val); +} + +static void intel_cmtg_restore(const struct intel_crtc_state *crtc_state) +{ + intel_cmtg_set_clk_select(crtc_state); + intel_cmtg_set_timings(crtc_state, MODESET); + intel_cmtg_set_vrr_timings(crtc_state); + intel_cmtg_set_vrr_ctl(crtc_state); + intel_cmtg_set_m_n(crtc_state); +} + +void intel_cmtg_program(struct intel_atomic_state *state) +{ + struct intel_display *display = to_intel_display(state); + struct intel_crtc *crtc; + struct intel_crtc_state *new_crtc_state; + bool dc3co_to_dc6 = intel_display_power_get_and_reset_dc3co_to_dc6(display); + + for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state) { + bool modeset = intel_crtc_needs_modeset(new_crtc_state); + + if (!intel_cmtg_is_allowed(new_crtc_state)) + continue; + + if ((modeset || dc3co_to_dc6) && + new_crtc_state->hw.active && !crtc->cmtg.enabled) { + if (dc3co_to_dc6) + intel_cmtg_restore(new_crtc_state); + + intel_cmtg_enable_sync(new_crtc_state); + intel_cmtg_set_hwgb(new_crtc_state); + intel_cmtg_enable_ddi(new_crtc_state); + } + } } diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index ba62199adaa2..a08cb2dcee67 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -6,8 +6,25 @@ #ifndef __INTEL_CMTG_H__ #define __INTEL_CMTG_H__ +#include <linux/types.h> + +struct intel_atomic_state; struct intel_display; +struct intel_crtc_state; + +enum set_timing_type { + MODESET = 0, + LRR +}; +void intel_cmtg_disable(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type); +void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state); void intel_cmtg_sanitize(struct intel_display *display); +bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state); +void intel_cmtg_program(struct intel_atomic_state *state); #endif /* __INTEL_CMTG_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h index 945a35578284..18dcb665df04 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h @@ -10,12 +10,32 @@ #define CMTG_CLK_SEL _MMIO(0x46160) #define CMTG_CLK_SEL_A_MASK REG_GENMASK(31, 29) +#define CMTG_CLK_SELECT_PHYA_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0x4) #define CMTG_CLK_SEL_A_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0) #define CMTG_CLK_SEL_B_MASK REG_GENMASK(15, 13) +#define CMTG_CLK_SELECT_PHYB_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0x6) #define CMTG_CLK_SEL_B_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0) -#define TRANS_CMTG_CTL_A _MMIO(0x6fa88) -#define TRANS_CMTG_CTL_B _MMIO(0x6fb88) +#define _TRANS_CMTG_CTL_A 0x6fa88 +#define _TRANS_CMTG_CTL_B 0x6fb88 +#define TRANS_CMTG_CTL(trans) _MMIO_TRANS((trans), \ + _TRANS_CMTG_CTL_A, _TRANS_CMTG_CTL_B) #define CMTG_ENABLE REG_BIT(31) +#define CMTG_SYNC_TO_PORT REG_BIT(29) +#define CMTG_STATE REG_BIT(23) + +#define _CMTG_HW_GB_A 0x6fa8c +#define _CMTG_HW_GB_B 0x6fb8c +#define CMTG_HW_GB(trans) _MMIO_TRANS((trans), \ + _CMTG_HW_GB_A, _CMTG_HW_GB_B) +#define CMTG_HW_GB_BREAKEVEN_MASK REG_GENMASK(11, 0) +#define CMTG_HW_GB_DC5_EXIT_LATENCY_MASK REG_GENMASK(27, 16) +#define CMTG_HW_GB_UP_LW_BG_DIFF_MASK REG_GENMASK(31, 28) + +#define _CMTG_SCANLINE_GB1_A 0x456A0 +#define _CMTG_SCANLINE_GB1_B 0x456C0 +#define CMTG_SCANLINE_GB1(trans) _MMIO_TRANS((trans), \ + _CMTG_SCANLINE_GB1_A, _CMTG_SCANLINE_GB1_B) +#define CMTG_HW_GB_ENABLE REG_BIT(31) #endif /* __INTEL_CMTG_REGS_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 7ef870cd9a16..87ced9f6ff40 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -3967,48 +3967,34 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, enum pipe pipe = to_intel_plane(state->plane)->pipe; enum plane_id plane = to_intel_plane(state->plane)->id; const struct drm_color_lut32 *pre_csc_lut = plane_state->hw.degamma_lut->data; - u32 i, lut_size; + int i, lut_size = 128; + u32 lut_val; - if (icl_is_hdr_plane(display, plane)) { - lut_size = 128; + if (!icl_is_hdr_plane(display, plane)) + return; - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), - PLANE_PAL_PREC_AUTO_INCREMENT); + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), + PLANE_PAL_PREC_AUTO_INCREMENT); + for (i = 0; i < lut_size + 3; i++) { if (pre_csc_lut) { - for (i = 0; i < lut_size; i++) { - u32 lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); - - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } - - /* Program the max register to clamp values > 1.0. */ - /* TODO: Restrict to 0x7ffffff */ - do { - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - (1 << 24)); - } while (i++ < 130); + if (i < lut_size) + lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); + /* else duplicate last lut_val */ } else { - for (i = 0; i < lut_size; i++) { - u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1); - - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), v); - } - - do { - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - 1 << 24); - } while (i++ < 130); + if (i < lut_size) + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + else + lut_val = 1 << 24; } - intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } + + intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); } static void @@ -4020,51 +4006,38 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb, enum pipe pipe = to_intel_plane(state->plane)->pipe; enum plane_id plane = to_intel_plane(state->plane)->id; const struct drm_color_lut32 *post_csc_lut = plane_state->hw.gamma_lut->data; - u32 i, lut_size, lut_val; - - if (icl_is_hdr_plane(display, plane)) { - intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), - PLANE_PAL_PREC_AUTO_INCREMENT); - /* TODO: Add macro */ - intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), - PLANE_PAL_PREC_AUTO_INCREMENT); + int i, lut_size = 32; + u32 lut_val; + + if (!icl_is_hdr_plane(display, plane)) + return; + + intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), + PLANE_PAL_PREC_AUTO_INCREMENT); + /* TODO: Add macro */ + intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), + PLANE_PAL_PREC_AUTO_INCREMENT); + + for (i = 0; i < lut_size + 3; i++) { if (post_csc_lut) { - lut_size = 32; - for (i = 0; i < lut_size; i++) { + if (i < lut_size) lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24); - - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } - - /* Segment 2 */ - do { - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - (1 << 24)); - } while (i++ < 34); + /* else clamp to the last LUT value to prevent step discontinuity */ } else { - /*TODO: Add for segment 0 */ - lut_size = 32; - for (i = 0; i < lut_size; i++) { - u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1); - - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), v); - } - - do { - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - 1 << 24); - } while (i++ < 34); + if (i < lut_size) + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + else + lut_val = 1 << 24; } - intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0); + PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } + + intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); + intel_de_write_dsb(display, dsb, + PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0); } static void diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c index 243e332bef57..5b8968197fbc 100644 --- a/drivers/gpu/drm/i915/display/intel_crt.c +++ b/drivers/gpu/drm/i915/display/intel_crt.c @@ -397,7 +397,8 @@ intel_crt_mode_valid(struct drm_connector *connector, return MODE_OK; } -static int intel_crt_compute_config(struct intel_encoder *encoder, +static int intel_crt_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -413,7 +414,8 @@ static int intel_crt_compute_config(struct intel_encoder *encoder, return 0; } -static int pch_crt_compute_config(struct intel_encoder *encoder, +static int pch_crt_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -432,7 +434,8 @@ static int pch_crt_compute_config(struct intel_encoder *encoder, return 0; } -static int hsw_crt_compute_config(struct intel_encoder *encoder, +static int hsw_crt_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 52347668f27d..88384dea868b 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -876,7 +876,7 @@ intel_legacy_cursor_update(struct drm_plane *_plane, new_plane_state->uapi.crtc_w = crtc_w; new_plane_state->uapi.crtc_h = crtc_h; - intel_plane_copy_uapi_to_hw_state(new_plane_state, new_plane_state, crtc); + intel_plane_copy_uapi_to_hw_state(NULL, new_plane_state, new_plane_state, crtc); ret = intel_plane_atomic_check_with_state(crtc_state, new_crtc_state, old_plane_state, new_plane_state); diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 24a51ab21b55..452062417ce9 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -9,6 +9,7 @@ #include <drm/drm_print.h> #include "intel_alpm.h" +#include "intel_cmtg.h" #include "intel_cx0_phy.h" #include "intel_cx0_phy_regs.h" #include "intel_display_regs.h" @@ -3418,10 +3419,20 @@ void intel_mtl_pll_enable(struct intel_encoder *encoder, void intel_mtl_pll_enable_clock(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) { + struct intel_display *display = to_intel_display(encoder); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); if (intel_tc_port_in_tbt_alt_mode(dig_port)) intel_mtl_tbt_pll_enable_clock(encoder, crtc_state->port_clock); + + /* + * CMTG can be enabled only when the transcoder and port are compatible + * (transcoder A with port A, transcoder B with port B). + */ + if (HAS_LT_PHY(display) && + ((crtc_state->cpu_transcoder == TRANSCODER_A && encoder->port == PORT_A) || + (crtc_state->cpu_transcoder == TRANSCODER_B && encoder->port == PORT_B))) + intel_cmtg_set_clk_select(crtc_state); } /* diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 1428e7a5a318..95c20eb4b4b7 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -60,7 +60,6 @@ void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder, u8 lane_mask, u8 state); -int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock); void intel_cx0_setup_powerdown(struct intel_encoder *encoder); bool intel_cx0_is_hdmi_frl(u32 clock); u8 intel_cx0_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 6296635c4e79..2b7eb010511b 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3501,6 +3501,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state, } intel_ddi_buf_enable(encoder, buf_ctl); + + intel_hdmi_poll_for_scrambling_enable(crtc_state, connector); } static void intel_ddi_enable(struct intel_atomic_state *state, @@ -4483,7 +4485,8 @@ intel_ddi_compute_output_type(struct intel_encoder *encoder, } } -static int intel_ddi_compute_config(struct intel_encoder *encoder, +static int intel_ddi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -4501,7 +4504,7 @@ static int intel_ddi_compute_config(struct intel_encoder *encoder, ret = intel_hdmi_compute_config(encoder, pipe_config, conn_state); } else { - ret = intel_dp_compute_config(encoder, pipe_config, conn_state); + ret = intel_dp_compute_config(state, encoder, pipe_config, conn_state); } if (ret) @@ -4606,7 +4609,8 @@ intel_ddi_port_sync_transcoders(const struct intel_crtc_state *ref_crtc_state, return transcoders; } -static int intel_ddi_compute_config_late(struct intel_encoder *encoder, +static int intel_ddi_compute_config_late(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -4659,6 +4663,7 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder) drm_encoder_cleanup(encoder); kfree(dig_port->hdcp.port_data.streams); + intel_dp_link_cleanup(&dig_port->dp); kfree(dig_port); } @@ -4696,11 +4701,16 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) struct intel_display *display = to_intel_display(dig_port); struct intel_connector *connector; enum port port = dig_port->base.port; + int err; connector = intel_connector_alloc(); if (!connector) return -ENOMEM; + err = intel_dp_link_init(&dig_port->dp); + if (err) + goto err_dp_init; + dig_port->dp.output_reg = DDI_BUF_CTL(port); if (DISPLAY_VER(display) >= 14) dig_port->dp.prepare_link_retrain = mtl_ddi_prepare_link_retrain; @@ -4713,8 +4723,9 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) dig_port->dp.preemph_max = intel_ddi_dp_preemph_max; if (!intel_dp_init_connector(dig_port, connector)) { - kfree(connector); - return -EINVAL; + err = -EINVAL; + + goto err_init_connector; } if (dig_port->base.type == INTEL_OUTPUT_EDP) { @@ -4730,6 +4741,13 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) } return 0; + +err_init_connector: + intel_dp_link_cleanup(&dig_port->dp); +err_dp_init: + kfree(connector); + + return err; } static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port) @@ -4738,6 +4756,7 @@ static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port) struct intel_connector *connector = intel_dp->attached_connector; intel_dp_cleanup_connector(dig_port, connector); + intel_dp_link_cleanup(intel_dp); kfree(connector); } diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c index 6daee9e82503..a7417905192d 100644 --- a/drivers/gpu/drm/i915/display/intel_de.c +++ b/drivers/gpu/drm/i915/display/intel_de.c @@ -9,11 +9,11 @@ #include "intel_de.h" -static int __intel_de_wait_for_register(struct intel_display *display, - intel_reg_t reg, u32 mask, u32 value, - unsigned int timeout_us, - u32 (*read)(struct intel_display *display, intel_reg_t reg), - u32 *out_val, bool is_atomic) +static int intel_de_wait_for_register(struct intel_display *display, + intel_reg_t reg, u32 mask, u32 value, + unsigned int timeout_us, + u32 (*read)(struct intel_display *display, intel_reg_t reg), + u32 *out_val, bool is_atomic) { const ktime_t end = ktime_add_us(ktime_get_raw(), timeout_us); int wait_max = 1000; @@ -60,28 +60,6 @@ static int __intel_de_wait_for_register(struct intel_display *display, return ret; } -static int intel_de_wait_for_register(struct intel_display *display, - intel_reg_t reg, u32 mask, u32 value, - unsigned int fast_timeout_us, - unsigned int slow_timeout_us, - u32 (*read)(struct intel_display *display, intel_reg_t reg), - u32 *out_value, bool is_atomic) -{ - int ret = -EINVAL; - - if (fast_timeout_us) - ret = __intel_de_wait_for_register(display, reg, mask, value, - fast_timeout_us, read, - out_value, is_atomic); - - if (ret && slow_timeout_us) - ret = __intel_de_wait_for_register(display, reg, mask, value, - slow_timeout_us, read, - out_value, is_atomic); - - return ret; -} - int intel_de_wait_us(struct intel_display *display, intel_reg_t reg, u32 mask, u32 value, unsigned int timeout_us, u32 *out_value) @@ -91,7 +69,7 @@ int intel_de_wait_us(struct intel_display *display, intel_reg_t reg, intel_dmc_wl_get(display, reg); ret = intel_de_wait_for_register(display, reg, mask, value, - timeout_us, 0, + timeout_us, intel_de_read, out_value, false); @@ -109,7 +87,7 @@ int intel_de_wait_ms(struct intel_display *display, intel_reg_t reg, intel_dmc_wl_get(display, reg); ret = intel_de_wait_for_register(display, reg, mask, value, - 2, timeout_ms * 1000, + timeout_ms * 1000, intel_de_read, out_value, false); @@ -123,7 +101,7 @@ int intel_de_wait_fw_ms(struct intel_display *display, intel_reg_t reg, u32 *out_value) { return intel_de_wait_for_register(display, reg, mask, value, - 2, timeout_ms * 1000, + timeout_ms * 1000, intel_de_read_fw, out_value, false); } @@ -133,7 +111,7 @@ int intel_de_wait_fw_us_atomic(struct intel_display *display, intel_reg_t reg, u32 *out_value) { return intel_de_wait_for_register(display, reg, mask, value, - timeout_us, 0, + timeout_us, intel_de_read_fw, out_value, true); } diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 8e269b71f18e..90c05ad08f86 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -60,6 +60,7 @@ #include "intel_bw.h" #include "intel_cdclk.h" #include "intel_clock_gating.h" +#include "intel_cmtg.h" #include "intel_color.h" #include "intel_crt.h" #include "intel_crtc.h" @@ -132,7 +133,6 @@ #include "vlv_dsi_pll.h" #include "vlv_dsi_regs.h" -static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state); static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state); static void hsw_set_transconf(const struct intel_crtc_state *crtc_state); static void bdw_set_pipe_misc(struct intel_dsb *dsb, @@ -1504,7 +1504,7 @@ static void ilk_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta &crtc_state->dp_m2_n2); } - intel_set_transcoder_timings(crtc_state); + intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); ilk_set_pipeconf(crtc_state); } @@ -1635,7 +1635,9 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta &crtc_state->dp_m2_n2); } - intel_set_transcoder_timings(crtc_state); + intel_cmtg_set_m_n(crtc_state); + intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); + intel_cmtg_set_timings(crtc_state, MODESET); if (cpu_transcoder != TRANSCODER_EDP) intel_de_write(display, TRANS_MULT(display, cpu_transcoder), @@ -1788,6 +1790,10 @@ static void hsw_crtc_disable(struct intel_atomic_state *state, intel_atomic_get_old_crtc_state(state, crtc); struct intel_crtc *pipe_crtc; + if (crtc->cmtg.enabled && intel_cmtg_is_allowed(old_crtc_state)) { + intel_cmtg_set_clk_select(old_crtc_state); + intel_cmtg_disable(old_crtc_state); + } /* * FIXME collapse everything to one hook. * Need care with mst->ddi interactions. @@ -2048,7 +2054,7 @@ static void i9xx_configure_cpu_transcoder(const struct intel_crtc_state *crtc_st &crtc_state->dp_m2_n2); } - intel_set_transcoder_timings(crtc_state); + intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); i9xx_set_pipeconf(crtc_state); } @@ -2664,17 +2670,17 @@ transcoder_has_vrr(const struct intel_crtc_state *crtc_state) return HAS_VRR(display) && !transcoder_is_dsi(cpu_transcoder); } -static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state) +void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum pipe pipe = crtc->pipe; - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end; int vsyncshift = 0; - drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder)); + drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder)); /* We need to be careful not to changed the adjusted mode, for otherwise * the hw state checker will get angry at the mismatch. */ @@ -2703,7 +2709,7 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta */ if (DISPLAY_VER(display) >= 13) { intel_de_write(display, - TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder), + TRANS_SET_CONTEXT_LATENCY(display, transcoder), crtc_state->set_context_latency); /* @@ -2718,35 +2724,26 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta if (DISPLAY_VER(display) >= 4 && DISPLAY_VER(display) < 35) intel_de_write(display, - TRANS_VSYNCSHIFT(display, cpu_transcoder), + TRANS_VSYNCSHIFT(display, transcoder), vsyncshift); - intel_de_write(display, TRANS_HTOTAL(display, cpu_transcoder), + intel_de_write(display, TRANS_HTOTAL(display, transcoder), HACTIVE(adjusted_mode->crtc_hdisplay - 1) | HTOTAL(adjusted_mode->crtc_htotal - 1)); - intel_de_write(display, TRANS_HBLANK(display, cpu_transcoder), + intel_de_write(display, TRANS_HBLANK(display, transcoder), HBLANK_START(adjusted_mode->crtc_hblank_start - 1) | HBLANK_END(adjusted_mode->crtc_hblank_end - 1)); - intel_de_write(display, TRANS_HSYNC(display, cpu_transcoder), + intel_de_write(display, TRANS_HSYNC(display, transcoder), HSYNC_START(adjusted_mode->crtc_hsync_start - 1) | HSYNC_END(adjusted_mode->crtc_hsync_end - 1)); - /* - * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal - * bits are not required. Since the support for these bits is going to - * be deprecated in upcoming platforms, avoid writing these bits for the - * platforms that do not use legacy Timing Generator. - */ - if (intel_vrr_always_use_vrr_tg(display)) - crtc_vtotal = 1; - - intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder), + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); - intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder), + intel_de_write(display, TRANS_VBLANK(display, transcoder), VBLANK_START(crtc_vblank_start - 1) | VBLANK_END(crtc_vblank_end - 1)); - intel_de_write(display, TRANS_VSYNC(display, cpu_transcoder), + intel_de_write(display, TRANS_VSYNC(display, transcoder), VSYNC_START(adjusted_mode->crtc_vsync_start - 1) | VSYNC_END(adjusted_mode->crtc_vsync_end - 1)); @@ -2754,13 +2751,15 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta * programmed with the VTOTAL_EDP value. Same for VTOTAL_C. This is * documented on the DDI_FUNC_CTL register description, EDP Input Select * bits. */ - if (display->platform.haswell && cpu_transcoder == TRANSCODER_EDP && + if (display->platform.haswell && transcoder == TRANSCODER_EDP && (pipe == PIPE_B || pipe == PIPE_C)) intel_de_write(display, TRANS_VTOTAL(display, pipe), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); - if (DISPLAY_VER(display) >= 30) { + if (DISPLAY_VER(display) >= 30 && + transcoder != TRANSCODER_CMTG0 && + transcoder != TRANSCODER_CMTG1) { /* * Address issues for resolutions with high refresh rate that * have small Hblank, specifically where Hblank is smaller than @@ -2769,19 +2768,19 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta * followed by BE which DPRX devices are unable to handle. * https://groups.vesa.org/wg/DP/document/20494 */ - intel_de_write(display, DP_MIN_HBLANK_CTL(cpu_transcoder), + intel_de_write(display, DP_MIN_HBLANK_CTL(transcoder), crtc_state->min_hblank); } } -static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state) +void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end; - drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder)); + drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder)); crtc_vdisplay = adjusted_mode->crtc_vdisplay; crtc_vtotal = adjusted_mode->crtc_vtotal; @@ -2796,7 +2795,7 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc if (DISPLAY_VER(display) >= 13) { intel_de_write(display, - TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder), + TRANS_SET_CONTEXT_LATENCY(display, transcoder), crtc_state->set_context_latency); /* @@ -2813,28 +2812,27 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc * The hardware actually ignores TRANS_VBLANK.VBLANK_END in DP mode. * But let's write it anyway to keep the state checker happy. */ - intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder), + intel_de_write(display, TRANS_VBLANK(display, transcoder), VBLANK_START(crtc_vblank_start - 1) | VBLANK_END(crtc_vblank_end - 1)); + /* - * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal - * bits are not required. Since the support for these bits is going to - * be deprecated in upcoming platforms, avoid writing these bits for the - * platforms that do not use legacy Timing Generator. + * DP doesn't have vertical sync, so TRANS_VSYNC only affects + * the position of the vsync interrupt (and does so even when + * using the VRR timing generator!). Thus updating TRANS_VSYNC + * here seems fine even if it isn't double buffered. */ - if (intel_vrr_always_use_vrr_tg(display)) - crtc_vtotal = 1; + intel_de_write(display, TRANS_VSYNC(display, transcoder), + VSYNC_START(adjusted_mode->crtc_vsync_start - 1) | + VSYNC_END(adjusted_mode->crtc_vsync_end - 1)); /* * The double buffer latch point for TRANS_VTOTAL * is the transcoder's undelayed vblank. */ - intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder), + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); - - intel_vrr_set_fixed_rr_timings(crtc_state); - intel_vrr_transcoder_enable(crtc_state); } static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state) @@ -4783,7 +4781,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state, if (connector_state->crtc != &crtc->base) continue; - ret = encoder->compute_config(encoder, crtc_state, + ret = encoder->compute_config(state, encoder, crtc_state, connector_state); if (ret == -EDEADLK) return ret; @@ -4843,7 +4841,7 @@ intel_modeset_pipe_config_late(struct intel_atomic_state *state, !encoder->compute_config_late) continue; - ret = encoder->compute_config_late(encoder, crtc_state, + ret = encoder->compute_config_late(state, encoder, crtc_state, conn_state); if (ret) return ret; @@ -5189,9 +5187,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_I(name.crtc_vdisplay); \ if (!fastset || !allow_vblank_delay_fastset(current_config)) \ PIPE_CONF_CHECK_I(name.crtc_vblank_start); \ - PIPE_CONF_CHECK_I(name.crtc_vsync_start); \ - PIPE_CONF_CHECK_I(name.crtc_vsync_end); \ if (!fastset || !pipe_config->update_lrr) { \ + PIPE_CONF_CHECK_I(name.crtc_vsync_start); \ + PIPE_CONF_CHECK_I(name.crtc_vsync_end); \ PIPE_CONF_CHECK_I(name.crtc_vtotal); \ PIPE_CONF_CHECK_I(name.crtc_vblank_end); \ } \ @@ -5658,35 +5656,43 @@ int intel_modeset_all_pipes_late(struct intel_atomic_state *state, return 0; } +int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state, + u8 pipe_mask, + struct drm_modeset_acquire_ctx *ctx) +{ + struct intel_display *display = to_intel_display(state); + struct intel_crtc *crtc; + + state->base.acquire_ctx = ctx; + state->internal = true; + + for_each_intel_crtc_in_pipe_mask(display, crtc, pipe_mask) { + struct intel_crtc_state *crtc_state = + intel_atomic_get_crtc_state(&state->base, crtc); + + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + crtc_state->uapi.connectors_changed = true; + } + + return drm_atomic_commit(&state->base); +} + int intel_modeset_commit_pipes(struct intel_display *display, u8 pipe_mask, struct drm_modeset_acquire_ctx *ctx) { struct drm_atomic_commit *state; - struct intel_crtc *crtc; int ret; state = drm_atomic_commit_alloc(display->drm); if (!state) return -ENOMEM; - state->acquire_ctx = ctx; - to_intel_atomic_state(state)->internal = true; - - for_each_intel_crtc_in_pipe_mask(display, crtc, pipe_mask) { - struct intel_crtc_state *crtc_state = - intel_atomic_get_crtc_state(state, crtc); - - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto out; - } + ret = intel_modeset_commit_pipes_for_atomic_state(to_intel_atomic_state(state), + pipe_mask, ctx); - crtc_state->uapi.connectors_changed = true; - } - - ret = drm_atomic_commit(state); -out: drm_atomic_commit_put(state); return ret; @@ -5805,6 +5811,8 @@ static bool lrr_params_changed(const struct intel_crtc_state *old_crtc_state, return old_adjusted_mode->crtc_vblank_start != new_adjusted_mode->crtc_vblank_start || old_adjusted_mode->crtc_vblank_end != new_adjusted_mode->crtc_vblank_end || + old_adjusted_mode->crtc_vsync_start != new_adjusted_mode->crtc_vsync_start || + old_adjusted_mode->crtc_vsync_end != new_adjusted_mode->crtc_vsync_end || old_adjusted_mode->crtc_vtotal != new_adjusted_mode->crtc_vtotal || old_crtc_state->set_context_latency != new_crtc_state->set_context_latency; } @@ -6667,12 +6675,19 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state, display->platform.broadwell || display->platform.haswell) hsw_set_linetime_wm(new_crtc_state); - if (new_crtc_state->update_m_n) + if (new_crtc_state->update_m_n) { intel_cpu_transcoder_set_m1_n1(crtc, new_crtc_state->cpu_transcoder, &new_crtc_state->dp_m_n); + intel_cmtg_set_m_n(new_crtc_state); + } - if (new_crtc_state->update_lrr) - intel_set_transcoder_timings_lrr(new_crtc_state); + if (new_crtc_state->update_lrr) { + intel_set_transcoder_timings_lrr(new_crtc_state, new_crtc_state->cpu_transcoder); + intel_cmtg_set_timings(new_crtc_state, LRR); + intel_vrr_set_fixed_rr_timings(new_crtc_state, new_crtc_state->cpu_transcoder); + intel_cmtg_set_vrr_timings(new_crtc_state); + intel_vrr_transcoder_enable(new_crtc_state); + } } static void commit_pipe_pre_planes(struct intel_atomic_state *state, @@ -6870,6 +6885,12 @@ static void intel_update_crtc(struct intel_atomic_state *state, if (intel_crtc_needs_fastset(new_crtc_state) && old_crtc_state->inherited) intel_crtc_arm_fifo_underrun(crtc, new_crtc_state); + + if (crtc->cmtg.enabled && intel_crtc_vrr_enabling(state, crtc) && + intel_cmtg_is_allowed(new_crtc_state)) { + intel_cmtg_set_clk_select(new_crtc_state); + intel_cmtg_disable(new_crtc_state); + } } static void intel_old_crtc_state_disables(struct intel_atomic_state *state, @@ -7428,6 +7449,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) struct intel_crtc *crtc; struct intel_power_domain_mask put_domains[I915_MAX_PIPES] = {}; struct ref_tracker *wakeref = NULL; + int power_async_delay; for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state) intel_atomic_dsb_prepare(state, crtc); @@ -7536,9 +7558,14 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) /* Now enable the clocks, plane, pipe, and connectors that we set up. */ display->modeset.funcs->commit_modeset_enables(state); + intel_display_power_dc3co_compute(state); + /* FIXME probably need to sequence this properly */ intel_program_dpkgc_latency(state); + if (intel_display_power_dc3co_allowed(display)) + intel_cmtg_program(state); + intel_wait_for_vblank_workers(state); /* FIXME: We should call drm_atomic_helper_commit_hw_done() here @@ -7632,11 +7659,12 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) */ intel_uncore_arm_unclaimed_mmio_detection(uncore); } - /* - * Delay re-enabling DC states by 17 ms to avoid the off->on->off - * toggling overhead at and above 60 FPS. - */ - intel_display_power_put_async_delay(display, POWER_DOMAIN_DC_OFF, wakeref, 17); + + power_async_delay = intel_display_power_select_target_dc_state(state); + + intel_display_power_put_async_delay(display, + POWER_DOMAIN_DC_OFF, wakeref, power_async_delay); + intel_display_rpm_put(display, state->wakeref); /* diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 1963dbc80221..57ea4f2edf2a 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -424,6 +424,10 @@ void intel_set_m_n(struct intel_display *display, const struct intel_link_m_n *m_n, intel_reg_t data_m_reg, intel_reg_t data_n_reg, intel_reg_t link_m_reg, intel_reg_t link_n_reg); +void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder); +void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder); void intel_get_m_n(struct intel_display *display, struct intel_link_m_n *m_n, intel_reg_t data_m_reg, intel_reg_t data_n_reg, @@ -474,6 +478,9 @@ int intel_modeset_pipes_in_mask_early(struct intel_atomic_state *state, const char *reason, u8 pipe_mask); int intel_modeset_all_pipes_late(struct intel_atomic_state *state, const char *reason); +int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state, + u8 pipe_mask, + struct drm_modeset_acquire_ctx *ctx); int intel_modeset_commit_pipes(struct intel_display *display, u8 pipe_mask, struct drm_modeset_acquire_ctx *ctx); diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index 09ce25a6d4b1..17f7d3abdb9c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -325,14 +325,14 @@ struct intel_display { struct intel_bw_info { /* for each QGV point */ unsigned int deratedbw[I915_NUM_QGV_POINTS]; - /* for each PSF GV point */ - unsigned int psf_bw[I915_NUM_PSF_GV_POINTS]; - /* Peak BW for each QGV point */ - unsigned int peakbw[I915_NUM_QGV_POINTS]; - u8 num_qgv_points; - u8 num_psf_gv_points; u8 num_planes; } max[6]; + /* for each PSF GV point */ + unsigned int psf_bw[I915_NUM_PSF_GV_POINTS]; + /* Peak BW for each QGV point */ + unsigned int peakbw[I915_NUM_QGV_POINTS]; + u8 num_qgv_points; + u8 num_psf_gv_points; } bw; struct { @@ -538,6 +538,8 @@ struct intel_display { struct { struct i915_power_domains domains; + /* DC3CO state */ + struct intel_dc3co_state dc3co; /* Shadow for DISPLAY_PHY_CONTROL which can't be safely read */ u32 chv_phy_control; diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 08004c1ba03f..3f02868ef105 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -32,6 +32,7 @@ #include "intel_display_types.h" #include "intel_dmc.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -1342,6 +1343,7 @@ void intel_connector_debugfs_add(struct intel_connector *connector) intel_psr_connector_debugfs_add(connector); intel_alpm_lobf_debugfs_add(connector); intel_dp_link_training_debugfs_add(connector); + intel_dp_link_caps_debugfs_add(connector); intel_link_bw_connector_debugfs_add(connector); if (DISPLAY_VER(display) >= 11 && diff --git a/drivers/gpu/drm/i915/display/intel_display_device.c b/drivers/gpu/drm/i915/display/intel_display_device.c index 69a9f782935c..f17fc2c68472 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.c +++ b/drivers/gpu/drm/i915/display/intel_display_device.c @@ -101,6 +101,8 @@ static const struct intel_display_device_info no_display = {}; #define TRANSCODER_EDP_OFFSET 0x6f000 #define TRANSCODER_DSI0_OFFSET 0x6b000 #define TRANSCODER_DSI1_OFFSET 0x6b800 +#define TRANSCODER_CMTG0_OFFSET 0x6F000 +#define TRANSCODER_CMTG1_OFFSET 0x6F100 #define CURSOR_A_OFFSET 0x70080 #define CURSOR_B_OFFSET 0x700c0 @@ -1352,6 +1354,18 @@ static const struct intel_display_device_info xe2_lpd_display = { BIT(INTEL_FBC_A) | BIT(INTEL_FBC_B) | BIT(INTEL_FBC_C) | BIT(INTEL_FBC_D), .__runtime_defaults.has_dbuf_overlap_detection = true, + .trans_offsets = { + [TRANSCODER_A] = TRANSCODER_A_OFFSET, + [TRANSCODER_B] = TRANSCODER_B_OFFSET, + [TRANSCODER_C] = TRANSCODER_C_OFFSET, + [TRANSCODER_D] = TRANSCODER_D_OFFSET, + [TRANSCODER_CMTG0] = TRANSCODER_CMTG0_OFFSET, + [TRANSCODER_CMTG1] = TRANSCODER_CMTG1_OFFSET, + }, + .__runtime_defaults.cpu_transcoder_mask = + BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | + BIT(TRANSCODER_C) | BIT(TRANSCODER_D) | + BIT(TRANSCODER_CMTG0) | BIT(TRANSCODER_CMTG1), }; static const struct intel_display_device_info wcl_display = { diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h index 12e5a522a299..f77b3da2cff5 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.h +++ b/drivers/gpu/drm/i915/display/intel_display_device.h @@ -159,6 +159,7 @@ struct intel_display_platforms { #define HAS_CUR_FBC(__display) (!HAS_GMCH(__display) && IS_DISPLAY_VER(__display, 7, 13)) #define HAS_D12_PLANE_MINIMIZATION(__display) ((__display)->platform.rocketlake || (__display)->platform.alderlake_s) #define HAS_DBUF_OVERLAP_DETECTION(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dbuf_overlap_detection) +#define HAS_DC3CO(__display) (DISPLAY_VER(__display) >= 35) #define HAS_DDI(__display) (DISPLAY_INFO(__display)->has_ddi) #define HAS_DISPLAY(__display) (DISPLAY_RUNTIME_INFO(__display)->pipe_mask != 0) #define HAS_DMC(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dmc) @@ -292,7 +293,7 @@ struct intel_display_runtime_info { u32 rawclk_freq; u8 pipe_mask; - u8 cpu_transcoder_mask; + u16 cpu_transcoder_mask; u16 port_mask; u8 num_sprites[I915_MAX_PIPES]; diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index d0729936f681..bb5301b90231 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -43,6 +43,8 @@ #include "intel_dp_tunnel.h" #include "intel_dpll.h" #include "intel_dpll_mgr.h" +#include "intel_dram.h" +#include "intel_encoder.h" #include "intel_fb.h" #include "intel_fbc.h" #include "intel_fbdev.h" @@ -65,6 +67,8 @@ #include "intel_wm.h" #include "skl_watermark.h" +static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown); + bool intel_display_driver_probe_defer(struct pci_dev *pdev) { struct drm_privacy_screen *privacy_screen; @@ -200,11 +204,23 @@ int intel_display_driver_probe_noirq(struct intel_display *display) { int ret; + intel_opregion_setup(display); + + /* + * Fill the dram structure to get the system dram info. This will be + * used for memory latency calculation. + */ + ret = intel_dram_detect(display); + if (ret) + goto cleanup_opregion; + + intel_bw_init_hw(display); + if (HAS_DISPLAY(display)) { ret = drm_vblank_init(display->drm, INTEL_NUM_PIPES(display)); if (ret) - return ret; + goto cleanup_opregion; } intel_bios_init(display); @@ -303,6 +319,8 @@ cleanup_pw_domain_dmc: intel_display_power_driver_remove(display); cleanup_bios: intel_bios_driver_remove(display); +cleanup_opregion: + intel_opregion_cleanup(display); return ret; } @@ -604,6 +622,8 @@ void intel_display_driver_remove_noirq(struct intel_display *display) if (!HAS_DISPLAY(display)) return; + intel_hpd_cancel_work(display); + intel_display_driver_suspend_access(display); /* @@ -644,6 +664,8 @@ void intel_display_driver_remove_nogem(struct intel_display *display) intel_display_power_driver_remove(display); intel_bios_driver_remove(display); + + intel_opregion_cleanup(display); } void intel_display_driver_unregister(struct intel_display *display) @@ -674,34 +696,102 @@ void intel_display_driver_unregister(struct intel_display *display) intel_vga_unregister(display); } +void intel_display_driver_shutdown(struct intel_display *display) +{ + if (!HAS_DISPLAY(display)) + return; + + __intel_display_driver_pm_suspend(display, true); + + intel_encoder_shutdown_all(display); +} + +void intel_display_driver_shutdown_late(struct intel_display *display) +{ + if (!HAS_DISPLAY(display)) + return; + + /* + * The only requirement is to reboot with display DC states disabled, + * for now leaving all display power wells in the INIT power domain + * enabled. + */ + intel_display_power_driver_remove(display); +} + /* * turn all crtc's off, but do not adjust state * This has to be paired with a call to intel_modeset_setup_hw_state. */ -int intel_display_driver_suspend(struct intel_display *display) +static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown) { - struct drm_atomic_commit *state; - int ret; + int ret = 0; if (!HAS_DISPLAY(display)) return 0; - state = drm_atomic_helper_suspend(display->drm); - ret = PTR_ERR_OR_ZERO(state); - if (ret) - drm_err(display->drm, "Suspending crtc's failed with %i\n", - ret); - else - display->restore.modeset_state = state; + /* + * We do a lot of poking in a lot of registers, make sure they work + * properly. + */ + intel_display_power_disable(display); + + drm_client_dev_suspend(display->drm); + + drm_kms_helper_poll_disable(display->drm); + intel_display_driver_disable_user_access(display); + + if (shutdown) { + drm_atomic_helper_shutdown(display->drm); + } else { + struct drm_atomic_commit *state; + + state = drm_atomic_helper_suspend(display->drm); + ret = PTR_ERR_OR_ZERO(state); + if (ret) + drm_err(display->drm, "Suspending crtc's failed with %i\n", + ret); + else + display->restore.modeset_state = state; + } /* ensure all DPT VMAs have been unpinned for intel_dpt_suspend() */ flush_workqueue(display->wq.cleanup); intel_dp_mst_suspend(display); + intel_encoder_block_all_hpds(display); + + intel_hpd_cancel_work(display); + + intel_display_driver_suspend_access(display); + + intel_encoder_suspend_all(display); + return ret; } +int intel_display_driver_pm_suspend(struct intel_display *display) +{ + return __intel_display_driver_pm_suspend(display, false); +} + +void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle) +{ + if (!HAS_DISPLAY(display)) + return; + + intel_display_power_suspend_late(display, s2idle); +} + +void intel_display_driver_pm_resume_early(struct intel_display *display) +{ + if (!HAS_DISPLAY(display)) + return; + + intel_display_power_resume_early(display); +} + int __intel_display_driver_resume(struct intel_display *display, struct drm_atomic_commit *state, @@ -741,7 +831,7 @@ __intel_display_driver_resume(struct intel_display *display, return ret; } -void intel_display_driver_resume(struct intel_display *display) +void intel_display_driver_pm_resume(struct intel_display *display) { struct drm_atomic_commit *state = display->restore.modeset_state; struct drm_modeset_acquire_ctx ctx; @@ -750,6 +840,12 @@ void intel_display_driver_resume(struct intel_display *display) if (!HAS_DISPLAY(display)) return; + intel_display_driver_resume_access(display); + + intel_hpd_init(display); + + intel_encoder_unblock_all_hpds(display); + /* MST sideband requires HPD interrupts enabled */ intel_dp_mst_resume(display); @@ -779,4 +875,15 @@ void intel_display_driver_resume(struct intel_display *display) "Restoring old state failed with %i\n", ret); if (state) drm_atomic_commit_put(state); + + intel_display_driver_enable_user_access(display); + drm_kms_helper_poll_enable(display->drm); + + intel_hpd_poll_disable(display); + + intel_opregion_resume(display); + + drm_client_dev_resume(display->drm); + + intel_display_power_enable(display); } diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.h b/drivers/gpu/drm/i915/display/intel_display_driver.h index 5270c26a32e0..7eca3d17dd82 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.h +++ b/drivers/gpu/drm/i915/display/intel_display_driver.h @@ -24,8 +24,13 @@ void intel_display_driver_remove(struct intel_display *display); void intel_display_driver_remove_noirq(struct intel_display *display); void intel_display_driver_remove_nogem(struct intel_display *display); void intel_display_driver_unregister(struct intel_display *display); -int intel_display_driver_suspend(struct intel_display *display); -void intel_display_driver_resume(struct intel_display *display); +void intel_display_driver_shutdown(struct intel_display *display); +void intel_display_driver_shutdown_late(struct intel_display *display); + +int intel_display_driver_pm_suspend(struct intel_display *display); +void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle); +void intel_display_driver_pm_resume_early(struct intel_display *display); +void intel_display_driver_pm_resume(struct intel_display *display); /* interface for intel_display_reset.c */ int __intel_display_driver_resume(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c index 4a821b0674fd..bcb0ee22fb56 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.c +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c @@ -1469,6 +1469,18 @@ static void gen8_de_irq_handler(struct intel_display *display, u32 master_ctl) found = true; } + if (DISPLAY_VER(display) == 35) { + if (iir & CMTG_VBLANK_A) { + intel_handle_vblank(display, PIPE_A); + found = true; + } + + if (iir & CMTG_VBLANK_B) { + intel_handle_vblank(display, PIPE_B); + found = true; + } + } + if (DISPLAY_VER(display) >= 11) { u32 te_trigger = iir & (DSI0_TE | DSI1_TE); @@ -2666,3 +2678,10 @@ void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *s drm_printf(p, "DERRMR: 0x%08x\n", snapshot->derrmr); drm_printf(p, "ERR_INT: 0x%08x\n", snapshot->err_int); } + +void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask) +{ + spin_lock_irq(&display->irq.lock); + bdw_update_port_irq(display, bits, mask ? 0 : bits); + spin_unlock_irq(&display->irq.lock); +} diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.h b/drivers/gpu/drm/i915/display/intel_display_irq.h index a1227cee885a..84446bf53401 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.h +++ b/drivers/gpu/drm/i915/display/intel_display_irq.h @@ -82,4 +82,6 @@ void i915gm_irq_cstate_wa(struct intel_display *display, bool enable); struct intel_display_irq_snapshot *intel_display_irq_snapshot_capture(struct intel_display *display); void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *snapshot, struct drm_printer *p); +void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask); + #endif /* __INTEL_DISPLAY_IRQ_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_display_limits.h b/drivers/gpu/drm/i915/display/intel_display_limits.h index 453f7b720815..ea89473c177f 100644 --- a/drivers/gpu/drm/i915/display/intel_display_limits.h +++ b/drivers/gpu/drm/i915/display/intel_display_limits.h @@ -45,6 +45,8 @@ enum transcoder { TRANSCODER_DSI_1, TRANSCODER_DSI_A = TRANSCODER_DSI_0, /* legacy DSI */ TRANSCODER_DSI_C = TRANSCODER_DSI_1, /* legacy DSI */ + TRANSCODER_CMTG0, + TRANSCODER_CMTG1, I915_MAX_TRANSCODERS }; diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 2e51dfcd5dce..8e312f5b9f6d 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -14,7 +14,9 @@ #include "intel_cdclk.h" #include "intel_clock_gating.h" #include "intel_combo_phy.h" +#include "intel_crtc.h" #include "intel_de.h" +#include "intel_display.h" #include "intel_display_power.h" #include "intel_display_power_map.h" #include "intel_display_power_well.h" @@ -30,6 +32,8 @@ #include "intel_pch_refclk.h" #include "intel_pmdemand.h" #include "intel_pps_regs.h" +#include "intel_psr.h" +#include "intel_psr_regs.h" #include "intel_snps_phy.h" #include "skl_watermark.h" #include "skl_watermark_regs.h" @@ -267,7 +271,7 @@ sanitize_target_dc_state(struct intel_display *display, static const u32 states[] = { DC_STATE_EN_UPTO_DC6, DC_STATE_EN_UPTO_DC5, - DC_STATE_EN_DC3CO, + DC_STATE_EN_UPTO_DC3CO, DC_STATE_DISABLE, }; int i; @@ -285,6 +289,19 @@ sanitize_target_dc_state(struct intel_display *display, return target_dc_state; } +bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display) +{ + struct i915_power_domains *power_domains = &display->power.domains; + bool ret; + + mutex_lock(&power_domains->lock); + ret = power_domains->dc3co_to_dc6; + power_domains->dc3co_to_dc6 = false; + mutex_unlock(&power_domains->lock); + + return ret; +} + /** * intel_display_power_set_target_dc_state - Set target dc state. * @display: display device @@ -300,6 +317,7 @@ void intel_display_power_set_target_dc_state(struct intel_display *display, struct i915_power_well *power_well; bool dc_off_enabled; struct i915_power_domains *power_domains = &display->power.domains; + u32 old_target_dc_state; mutex_lock(&power_domains->lock); power_well = lookup_power_well(display, SKL_DISP_DC_OFF); @@ -320,8 +338,17 @@ void intel_display_power_set_target_dc_state(struct intel_display *display, if (!dc_off_enabled) intel_power_well_enable(display, power_well); + old_target_dc_state = power_domains->target_dc_state; power_domains->target_dc_state = state; + /* + * CMTG must be restored explicitly after DC6 exit. The dc3co_to_dc6 + * flag helps CMTG determine whether restoration is required. + */ + if (old_target_dc_state == DC_STATE_EN_UPTO_DC3CO && + power_domains->target_dc_state == DC_STATE_EN_UPTO_DC6) + power_domains->dc3co_to_dc6 = true; + if (!dc_off_enabled) intel_power_well_disable(display, power_well); @@ -358,6 +385,144 @@ unlock: return current_dc_state; } +bool intel_display_power_dc3co_supported(struct intel_display *display) +{ + struct i915_power_domains *power_domains = &display->power.domains; + + if (!HAS_DC3CO(display)) + return false; + + return (power_domains->allowed_dc_mask & DC_STATE_EN_UPTO_DC3CO) == DC_STATE_EN_UPTO_DC3CO; +} + +bool intel_display_power_dc3co_allowed(struct intel_display *display) +{ + struct intel_dc3co_state *dc3co = &display->power.dc3co; + bool allowed; + + if (!intel_display_power_dc3co_supported(display)) + return false; + + mutex_lock(&dc3co->lock); + allowed = dc3co->allowed; + mutex_unlock(&dc3co->lock); + + return allowed; +} + +void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger) +{ + struct intel_dc3co_state *dc3co = &display->power.dc3co; + + if (!intel_display_power_dc3co_supported(display)) + return; + + mutex_lock(&dc3co->lock); + dc3co->trigger = trigger; + dc3co->allowed = !!trigger; + mutex_unlock(&dc3co->lock); +} + +static bool intel_dc3co_port_pipe_compatible(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe; + enum port port = dig_port->base.port; + int num_pipes = intel_crtc_num_joined_pipes(crtc_state); + + /* Need to follow 1:1 mapping because of CMTG restriction */ + if (DISPLAY_VER(to_intel_display(crtc_state)) == 35) + return num_pipes == 1 && + ((pipe == PIPE_A && port == PORT_A) || + (pipe == PIPE_B && port == PORT_B)); + else + return num_pipes == 1 && pipe <= PIPE_B && port <= PORT_B; +} + +void intel_display_power_dc3co_compute(struct intel_atomic_state *state) +{ + struct intel_display *display = to_intel_display(state); + struct intel_crtc *crtc; + struct intel_crtc_state *crtc_state; + struct intel_encoder *encoder; + struct intel_dp *intel_dp; + u8 active_pipes = 0; + enum pipe pipe; + u32 trigger = DC3CO_TRIGGER_NONE; + + if (!intel_display_power_dc3co_supported(display)) + return; + + for_each_intel_crtc(display, crtc) + active_pipes |= crtc->active ? BIT(crtc->pipe) : 0; + + active_pipes = intel_calc_active_pipes(state, active_pipes); + + if (hweight8(active_pipes) != 1) + goto done; + + pipe = ffs(active_pipes) - 1; + crtc = intel_crtc_for_pipe(display, pipe); + + crtc_state = to_intel_crtc_state(crtc->base.state); + + for_each_intel_encoder_mask(display->drm, encoder, + crtc_state->uapi.encoder_mask) { + if (encoder->type != INTEL_OUTPUT_EDP) + goto done; + + intel_dp = enc_to_intel_dp(encoder); + + if (!intel_dc3co_port_pipe_compatible(intel_dp, crtc_state)) + goto done; + + if (intel_psr2_in_deep_sleep(intel_dp)) + goto done; + } + + if (crtc_state->has_lobf) + trigger |= DC3CO_TRIGGER_LOBF; + if (crtc_state->has_panel_replay && intel_dp->as_sdp_supported) + trigger |= DC3CO_TRIGGER_PANEL_REPLAY; + if (crtc_state->has_sel_update) + trigger |= DC3CO_TRIGGER_PSR2; + +done: + intel_display_power_dc3co_update(display, trigger); +} + +/* + * Select the target DC state for this commit and return the async-put delay + * to use when releasing the DC_OFF reference. + * + * Picks DC_STATE_EN_UPTO_DC3CO when DC3CO can be enabled + * otherwise falls back to default DC state of DC_STATE_EN_UPTO_DC6. + * The chosen target is programmed via intel_display_power_set_target_dc_state(). + * + * Returns the async-put delay (in ms) to use when releasing the DC_OFF + * reference: DC3CO_PUT_ASYNC_DELAY_MS when DC3CO was selected, otherwise + * DC6_PUT_ASYNC_DELAY_MS. + */ +int intel_display_power_select_target_dc_state(struct intel_atomic_state *state) +{ + struct intel_display *display = to_intel_display(state); + u32 target_dc_state; + + if (!intel_display_power_dc3co_supported(display)) + return DC6_PUT_ASYNC_DELAY_MS; + + if (intel_display_power_dc3co_allowed(display)) + target_dc_state = DC_STATE_EN_UPTO_DC3CO; + else + target_dc_state = DC_STATE_EN_UPTO_DC6; + + intel_display_power_set_target_dc_state(display, target_dc_state); + + return target_dc_state == DC_STATE_EN_UPTO_DC3CO ? + DC3CO_PUT_ASYNC_DELAY_MS : DC6_PUT_ASYNC_DELAY_MS; +} + static void __async_put_domains_mask(struct i915_power_domains *power_domains, struct intel_power_domain_mask *mask) { @@ -956,7 +1121,9 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc) if (!HAS_DISPLAY(display)) return 0; - if (DISPLAY_VER(display) >= 20) + if (DISPLAY_VER(display) >= 35) + max_dc = 4; + else if (DISPLAY_VER(display) >= 20) max_dc = 2; else if (display->platform.dg2) max_dc = 1; @@ -999,10 +1166,10 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc) switch (requested_dc) { case 4: - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6; + mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6; break; case 3: - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC5; + mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC5; break; case 2: mask |= DC_STATE_EN_UPTO_DC6; @@ -1037,6 +1204,7 @@ int intel_display_power_init(struct intel_display *display) sanitize_target_dc_state(display, DC_STATE_EN_UPTO_DC6); mutex_init(&power_domains->lock); + mutex_init(&display->power.dc3co.lock); INIT_DELAYED_WORK(&power_domains->async_put_work, intel_display_power_put_async_work); @@ -1634,9 +1802,12 @@ static void tgl_bw_buddy_init(struct intel_display *display) if (table[config].page_mask == 0) { drm_dbg_kms(display->drm, "Unknown memory configuration; disabling address buddy logic.\n"); - for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) - intel_de_write(display, BW_BUDDY_CTL(i), - BW_BUDDY_DISABLE); + + if (DISPLAY_VER(display) < 20) { + for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) + intel_de_write(display, BW_BUDDY_CTL(i), + BW_BUDDY_DISABLE); + } } else { for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) { intel_de_write(display, BW_BUDDY_PAGE_MASK(i), diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h index 56dc89eed3f8..546af67b680b 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.h +++ b/drivers/gpu/drm/i915/display/intel_display_power.h @@ -9,9 +9,12 @@ #include <linux/mutex.h> #include <linux/workqueue.h> +#include "intel_display_limits.h" + enum aux_ch; enum port; struct i915_power_well; +struct intel_atomic_state; struct intel_display; struct intel_encoder; struct ref_tracker; @@ -131,6 +134,36 @@ struct intel_power_domain_mask { DECLARE_BITMAP(bits, POWER_DOMAIN_NUM); }; +/* + * DC3CO enabling triggers (bitmask). + * DC3CO may be enabled when at least one of these triggers is active. + * Additional constraints may still apply. + */ +#define DC3CO_TRIGGER_NONE (0) +#define DC3CO_TRIGGER_PSR2 BIT(0) +#define DC3CO_TRIGGER_LOBF BIT(1) +#define DC3CO_TRIGGER_PANEL_REPLAY BIT(2) +#define DC3CO_TRIGGER_ALL (DC3CO_TRIGGER_PSR2 | \ + DC3CO_TRIGGER_LOBF | \ + DC3CO_TRIGGER_PANEL_REPLAY) + +/* + * Delay to re-enable DC5/DC6 states by 17 ms to avoid the off->on->off + * toggling overhead at and above 60 FPS. + */ +#define DC6_PUT_ASYNC_DELAY_MS 17 +/* + * Use minimal re-enable delay to allow DC3CO entry on + * the next idle frame. + */ +#define DC3CO_PUT_ASYNC_DELAY_MS 1 + +struct intel_dc3co_state { + struct mutex lock; /* protects allowed and trigger fields */ + bool allowed; /* DC3CO compute result */ + u32 trigger; /* Bitmask of active DC3CO triggers */ +}; + struct i915_power_domains { /* * Power wells needed for initialization at driver init and suspend @@ -138,6 +171,7 @@ struct i915_power_domains { */ bool initializing; bool display_core_suspended; + bool dc3co_to_dc6; int power_well_count; u32 dc_state; @@ -179,9 +213,15 @@ void intel_display_power_sanitize_state(struct intel_display *display); void intel_display_power_suspend_late(struct intel_display *display, bool s2idle); void intel_display_power_resume_early(struct intel_display *display); +bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display); void intel_display_power_set_target_dc_state(struct intel_display *display, u32 state); u32 intel_display_power_get_current_dc_state(struct intel_display *display); +bool intel_display_power_dc3co_supported(struct intel_display *display); +void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger); +bool intel_display_power_dc3co_allowed(struct intel_display *display); +void intel_display_power_dc3co_compute(struct intel_atomic_state *state); +int intel_display_power_select_target_dc_state(struct intel_atomic_state *state); void intel_display_power_runtime_suspend(struct intel_display *display); void intel_display_power_runtime_resume(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c index 04bd0dde5bed..02cb4d800e23 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -726,12 +726,28 @@ static void assert_can_disable_dc9(struct intel_display *display) */ } +static u32 dc_state_ro_mask(struct intel_display *display) +{ + if (DISPLAY_VER(display) >= 20) + return DC_STATE_EN_CSR_MASK_CMTG_1 | DC_STATE_EN_CSR_MASK_CMTG_0; + else if (DISPLAY_VER(display) >= 13 && !display->platform.dg2) + return DC_STATE_EN_CSR_MASK_CMTG_0; + + return 0; +} + static void gen9_write_dc_state(struct intel_display *display, u32 state) { int rewrites = 0; int rereads = 0; u32 v; + /* + * Mask out RO status bits from read-back comparison. + * HW may set these bits independently, so exclude them + * to prevent the verify loop from retrying due to RO bits mismatch. + */ + u32 ro_mask = dc_state_ro_mask(display); intel_de_write(display, DC_STATE_EN, state); @@ -743,7 +759,7 @@ static void gen9_write_dc_state(struct intel_display *display, do { v = intel_de_read(display, DC_STATE_EN); - if (v != state) { + if ((v & ~ro_mask) != (state & ~ro_mask)) { intel_de_write(display, DC_STATE_EN, state); rewrites++; rereads = 0; @@ -753,10 +769,10 @@ static void gen9_write_dc_state(struct intel_display *display, } while (rewrites < 100); - if (v != state) + if ((v & ~ro_mask) != (state & ~ro_mask)) drm_err(display->drm, - "Writing dc state to 0x%x failed, now 0x%x\n", - state, v); + "Writing dc state to 0x%x failed, now 0x%x (ro_mask=0x%x)\n", + state, v, ro_mask); /* Most of the times we need one retry, avoid spam */ if (rewrites > 1) @@ -772,7 +788,7 @@ static u32 gen9_dc_mask(struct intel_display *display) mask = DC_STATE_EN_UPTO_DC5; if (DISPLAY_VER(display) >= 12) - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6 + mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9; else if (DISPLAY_VER(display) == 11) mask |= DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9; @@ -866,21 +882,30 @@ void gen9_set_dc_state(struct intel_display *display, u32 state) power_domains->dc_state = val & mask; } -static void tgl_enable_dc3co(struct intel_display *display) +void xe3lpd_enable_dc_count(struct intel_display *display) { - drm_dbg_kms(display->drm, "Enabling DC3CO\n"); - gen9_set_dc_state(display, DC_STATE_EN_DC3CO); + if (DISPLAY_VER(display) < 35) + return; + + intel_de_write(display, DC_COUNT_EN, DC_COUNT_EN_COUNTER_ENABLE); } -static void tgl_disable_dc3co(struct intel_display *display) +static void assert_can_enable_dc3co(struct intel_display *display) { - drm_dbg_kms(display->drm, "Disabling DC3CO\n"); - intel_de_rmw(display, DC_STATE_EN, DC_STATE_DC3CO_STATUS, 0); - gen9_set_dc_state(display, DC_STATE_DISABLE); - /* - * Delay of 200us DC3CO Exit time B.Spec 49196 - */ - usleep_range(200, 210); + drm_WARN_ONCE(display->drm, + (intel_de_read(display, DC_STATE_EN) & + DC_STATE_EN_UPTO_DC3CO), + "DC3CO already programmed to be enabled.\n"); + + assert_main_dmc_loaded(display); +} + +static void xe3lpd_enable_dc3co(struct intel_display *display) +{ + assert_can_enable_dc3co(display); + drm_dbg_kms(display->drm, "Enabling DC3CO\n"); + intel_dmc_wl_enable(display, DC_STATE_EN_UPTO_DC3CO); + gen9_set_dc_state(display, DC_STATE_EN_UPTO_DC3CO); } static void assert_can_enable_dc5(struct intel_display *display) @@ -1039,8 +1064,8 @@ static void bxt_verify_dpio_phy_power_wells(struct intel_display *display) static bool gen9_dc_off_power_well_enabled(struct intel_display *display, struct i915_power_well *power_well) { - return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_DC3CO) == 0 && - (intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC5_DC6_MASK) == 0); + return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO) == 0 && + (intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK) == 0); } static void gen9_assert_dbuf_enabled(struct intel_display *display) @@ -1061,11 +1086,6 @@ void gen9_disable_dc_states(struct intel_display *display) struct intel_cdclk_config cdclk_config = {}; u32 old_state = power_domains->dc_state; - if (power_domains->target_dc_state == DC_STATE_EN_DC3CO) { - tgl_disable_dc3co(display); - return; - } - if (HAS_DISPLAY(display)) { intel_dmc_wl_get_noreg(display); gen9_set_dc_state(display, DC_STATE_DISABLE); @@ -1076,9 +1096,13 @@ void gen9_disable_dc_states(struct intel_display *display) } if (old_state == DC_STATE_EN_UPTO_DC5 || - old_state == DC_STATE_EN_UPTO_DC6) + old_state == DC_STATE_EN_UPTO_DC6 || + old_state == DC_STATE_EN_UPTO_DC3CO) intel_dmc_wl_disable(display); + if (old_state == DC_STATE_EN_UPTO_DC3CO) + return; + intel_cdclk_get_cdclk(display, &cdclk_config); /* Can't read out voltage_level so can't use intel_cdclk_changed() */ drm_WARN_ON(display->drm, @@ -1114,8 +1138,8 @@ static void gen9_dc_off_power_well_disable(struct intel_display *display, return; switch (power_domains->target_dc_state) { - case DC_STATE_EN_DC3CO: - tgl_enable_dc3co(display); + case DC_STATE_EN_UPTO_DC3CO: + xe3lpd_enable_dc3co(display); break; case DC_STATE_EN_UPTO_DC6: skl_enable_dc6(display); diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.h b/drivers/gpu/drm/i915/display/intel_display_power_well.h index 8f5524da2d06..0ce64b894436 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.h +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.h @@ -159,6 +159,7 @@ void gen9_set_dc_state(struct intel_display *display, u32 state); void gen9_disable_dc_states(struct intel_display *display); void bxt_enable_dc9(struct intel_display *display); void bxt_disable_dc9(struct intel_display *display); +void xe3lpd_enable_dc_count(struct intel_display *display); extern const struct i915_power_well_ops i9xx_always_on_power_well_ops; extern const struct i915_power_well_ops chv_pipe_power_well_ops; diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 4321f8b529da..39e50423132f 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -1458,6 +1458,8 @@ #define GEN9_AUX_CHANNEL_B (1 << 25) #define DSI1_TE (1 << 24) #define DSI0_TE (1 << 23) +#define CMTG_VBLANK_B (1 << 17) +#define CMTG_VBLANK_A (1 << 14) #define GEN8_DE_PORT_HOTPLUG(hpd_pin) REG_BIT(3 + _HPD_PIN_DDI(hpd_pin)) #define BXT_DE_PORT_HOTPLUG_MASK (GEN8_DE_PORT_HOTPLUG(HPD_PORT_A) | \ GEN8_DE_PORT_HOTPLUG(HPD_PORT_B) | \ @@ -2767,28 +2769,28 @@ enum skl_power_gate { */ /* CDCLK_CTL */ #define CDCLK_CTL _MMIO(0x46000) -#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26) +#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26) /* skl */ #define CDCLK_FREQ_450_432 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 0) -#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1) +#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1) #define CDCLK_FREQ_337_308 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 2) #define CDCLK_FREQ_675_617 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 3) -#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25) +#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25) /* lnl+ */ #define MDCLK_SOURCE_SEL_CD2XCLK REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 0) #define MDCLK_SOURCE_SEL_CDCLK_PLL REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 1) -#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22) +#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22) /* bxt+ */ #define BXT_CDCLK_CD2X_DIV_SEL_1 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 0) #define BXT_CDCLK_CD2X_DIV_SEL_1_5 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 1) #define BXT_CDCLK_CD2X_DIV_SEL_2 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 2) #define BXT_CDCLK_CD2X_DIV_SEL_4 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 3) -#define BXT_CDCLK_CD2X_PIPE(pipe) ((pipe) << 20) -#define CDCLK_DIVMUX_CD_OVERRIDE (1 << 19) -#define BXT_CDCLK_CD2X_PIPE_NONE BXT_CDCLK_CD2X_PIPE(3) -#define ICL_CDCLK_CD2X_PIPE(pipe) (_PICK(pipe, 0, 2, 6) << 19) -#define ICL_CDCLK_CD2X_PIPE_NONE (7 << 19) -#define TGL_CDCLK_CD2X_PIPE(pipe) BXT_CDCLK_CD2X_PIPE(pipe) -#define TGL_CDCLK_CD2X_PIPE_NONE ICL_CDCLK_CD2X_PIPE_NONE -#define BXT_CDCLK_SSA_PRECHARGE_ENABLE (1 << 16) -#define CDCLK_FREQ_DECIMAL_MASK (0x7ff) +#define BXT_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 20) /* bxt/glk */ +#define BXT_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, (pipe)) +#define BXT_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, 3) +#define ICL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* icl+ */ +#define ICL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, (pipe) << 1) +#define ICL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, 7) +#define CDCLK_DIVMUX_CD_OVERRIDE REG_BIT(19) /* pre-icl */ +#define BXT_CDCLK_SSA_PRECHARGE_ENABLE REG_BIT(16) /* bxt/glk */ +#define CDCLK_FREQ_DECIMAL_MASK REG_GENMASK(10, 0) /* pre-lnl */ /* CDCLK_SQUASH_CTL */ #define CDCLK_SQUASH_CTL _MMIO(0x46008) @@ -3070,19 +3072,29 @@ enum skl_power_gate { /* GEN9 DC */ #define DC_STATE_EN _MMIO(0x45504) #define DC_STATE_DISABLE 0 -#define DC_STATE_EN_DC3CO REG_BIT(30) #define DC_STATE_DC3CO_STATUS REG_BIT(29) #define HOLD_PHY_CLKREQ_PG1_LATCH REG_BIT(21) #define HOLD_PHY_PG1_LATCH REG_BIT(20) -#define DC_STATE_EN_UPTO_DC5 (1 << 0) #define DC_STATE_EN_DC9 (1 << 3) -#define DC_STATE_EN_UPTO_DC6 (2 << 0) -#define DC_STATE_EN_UPTO_DC5_DC6_MASK 0x3 +#define DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK REG_GENMASK(1, 0) +#define DC_STATE_EN_DISABLE REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 0) +#define DC_STATE_EN_UPTO_DC5 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 1) +#define DC_STATE_EN_UPTO_DC6 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 2) +#define DC_STATE_EN_UPTO_DC3CO REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 3) +/* display version 20+ */ +#define DC_STATE_EN_CSR_MASK_CMTG_1 REG_BIT(11) +/* display version 13+, except dg2 */ +#define DC_STATE_EN_CSR_MASK_CMTG_0 REG_BIT(10) #define DC_STATE_DEBUG _MMIO(0x45520) #define DC_STATE_DEBUG_MASK_CORES (1 << 0) #define DC_STATE_DEBUG_MASK_MEMORY_UP (1 << 1) +#define DC_COUNT_EN _MMIO(0x457B4) +#define DC_COUNT_EN_COUNTER_ENABLE REG_BIT(31) + +#define DC_STATE_DC3CO_RESIDENCY _MMIO(0x457B8) + #define D_COMP_BDW _MMIO(0x138144) /* Pipe WM_LINETIME - watermark line time */ diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index c21e0c0ef0b1..c048da7d6fea 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -58,6 +58,8 @@ struct cec_notifier; struct drm_printer; struct intel_connector; struct intel_ddi_buf_trans; +struct intel_dp_link_caps; +struct intel_dp_link_training; struct intel_fbc; struct intel_global_objs_state; struct intel_hdcp_shim; @@ -150,7 +152,6 @@ struct intel_framebuffer { unsigned int min_alignment; unsigned int vtd_guard; - unsigned int (*panic_tiling)(unsigned int x, unsigned int y, unsigned int width); struct intel_panic *panic; }; @@ -177,10 +178,12 @@ struct intel_encoder { enum intel_output_type (*compute_output_type)(struct intel_encoder *, struct intel_crtc_state *, struct drm_connector_state *); - int (*compute_config)(struct intel_encoder *, + int (*compute_config)(struct intel_atomic_state *, + struct intel_encoder *, struct intel_crtc_state *, struct drm_connector_state *); - int (*compute_config_late)(struct intel_encoder *, + int (*compute_config_late)(struct intel_atomic_state *, + struct intel_encoder *, struct intel_crtc_state *, struct drm_connector_state *); void (*pre_pll_enable)(struct intel_atomic_state *, @@ -1185,7 +1188,6 @@ struct intel_crtc_state { bool pkg_c_latency_used; /* Only used for state verification. */ enum intel_panel_replay_dsc_support panel_replay_dsc_support; - u32 dc3co_exitline; u16 su_y_granularity; u8 active_non_psr_pipes; u8 entry_setup_frames; @@ -1573,6 +1575,10 @@ struct intel_crtc { #endif bool vblank_psr_notify; + + struct { + bool enabled; + } cmtg; }; struct intel_plane_error { @@ -1772,14 +1778,16 @@ struct intel_psr { ktime_t last_exit; bool sink_not_reliable; bool irq_aux_error; + /* DC3CO allowed used to control PSR configuration */ + bool dc3co_allowed; + /* DC3CO disable work */ + struct delayed_work dc3co_work; u16 su_w_granularity; u16 su_y_granularity; bool source_panel_replay_support; bool sink_panel_replay_support; bool panel_replay_enabled; - u32 dc3co_exitline; u32 dc3co_exit_delay; - struct delayed_work dc3co_work; u8 entry_setup_frames; u8 io_wake_lines; @@ -1795,6 +1803,14 @@ struct intel_psr { struct ref_tracker *vblank_wakeref; }; +struct intel_dp_link_config { + int rate; + int lane_count; +}; + +#define INTEL_DP_LINK_CONFIG_NULL \ + ((struct intel_dp_link_config){}) + struct intel_dp { intel_reg_t output_reg; u32 DP; @@ -1820,29 +1836,9 @@ struct intel_dp { bool use_rate_select; /* Max sink lane count as reported by DP_MAX_LANE_COUNT */ int max_sink_lane_count; - /* intersection of source and sink rates */ - int num_common_rates; - int common_rates[DP_MAX_SUPPORTED_RATES]; - int max_common_lane_count; struct { /* TODO: move the rest of link specific fields to here */ bool active; - /* common rate,lane_count configs in bw order */ - int num_configs; -#define INTEL_DP_MAX_LANE_COUNT 4 -#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1) -#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) -#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS) -#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \ - INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) - struct intel_dp_link_config { - u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS; - u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS; - } configs[INTEL_DP_MAX_LINK_CONFIGS]; - /* Max lane count for the current link */ - int max_lane_count; - /* Max rate for the current link */ - int max_rate; /* * Link parameters for which the MST topology was probed. * Tracking these ensures that the MST path resources are @@ -1851,13 +1847,8 @@ struct intel_dp { */ int mst_probed_lane_count; int mst_probed_rate; - int force_lane_count; - int force_rate; - bool retrain_disabled; - /* Sequential link training failures after a passing LT */ - int seq_train_failures; - int force_train_failure; - bool force_retrain; + struct intel_dp_link_training *training; + struct intel_dp_link_caps *caps; } link; bool reset_link_params; int mso_link_count; diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 481fb65b7110..11f5dbf91e68 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -941,6 +941,8 @@ void intel_dmc_load_program(struct intel_display *display) gen9_set_dc_state_debugmask(display); + xe3lpd_enable_dc_count(display); + pipedmc_clock_gating_wa(display, false); } @@ -1647,19 +1649,20 @@ static int intel_dmc_debugfs_status_show(struct seq_file *m, void *unused) DMC_VERSION_MINOR(dmc->version)); if (DISPLAY_VER(display) >= 12) { - intel_reg_t dc3co_reg; + if (DISPLAY_VER(display) >= 35) { + dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; + seq_printf(m, "DC3CO count: %d\n", + intel_de_read(display, XE3P_DMC_DC3CO_COUNT)); - if (display->platform.dgfx || DISPLAY_VER(display) >= 14) { - dc3co_reg = DG1_DMC_DEBUG3; + seq_printf(m, "DC3CO residency: %d\n", + intel_de_read(display, DC_STATE_DC3CO_RESIDENCY)); + } else if (display->platform.dgfx || DISPLAY_VER(display) >= 14) { dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; } else { - dc3co_reg = TGL_DMC_DEBUG3; dc5_reg = TGL_DMC_DEBUG_DC5_COUNT; dc6_reg = TGL_DMC_DEBUG_DC6_COUNT; } - seq_printf(m, "DC3CO count: %d\n", - intel_de_read(display, dc3co_reg)); } else { dc5_reg = display->platform.broxton ? BXT_DMC_DC3_DC5_COUNT : SKL_DMC_DC3_DC5_COUNT; diff --git a/drivers/gpu/drm/i915/display/intel_dmc_regs.h b/drivers/gpu/drm/i915/display/intel_dmc_regs.h index 38e342b45af0..6b7978fb8986 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_regs.h +++ b/drivers/gpu/drm/i915/display/intel_dmc_regs.h @@ -531,6 +531,8 @@ enum pipedmc_event_id { #define TGL_DMC_DEBUG3 _MMIO(0x101090) #define DG1_DMC_DEBUG3 _MMIO(0x13415c) +#define XE3P_DMC_DC3CO_COUNT _MMIO(0x8f05c) + #define DMC_WAKELOCK_CFG _MMIO(0x8F1B0) #define DMC_WAKELOCK_CFG_ENABLE REG_BIT(31) #define DMC_WAKELOCK1_CTL _MMIO(0x8F140) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index b007343721e1..ab4e0e9573df 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -267,7 +267,7 @@ static bool intel_dmc_wl_check_range(struct intel_display *display, * the DMC and requires a DC exit for proper access. */ switch (dc_state) { - case DC_STATE_EN_DC3CO: + case DC_STATE_EN_UPTO_DC3CO: ranges = xe3lpd_dc3co_dmc_ranges; break; case DC_STATE_EN_UPTO_DC5: diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index c81fc00a334c..6e3fa6662cbe 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -33,7 +33,6 @@ #include <linux/notifier.h> #include <linux/seq_buf.h> #include <linux/slab.h> -#include <linux/sort.h> #include <linux/string_helpers.h> #include <linux/timekeeping.h> #include <linux/types.h> @@ -71,6 +70,7 @@ #include "intel_dp.h" #include "intel_dp_aux.h" #include "intel_dp_hdcp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -313,7 +313,7 @@ static void intel_dp_set_max_sink_lane_count(struct intel_dp *intel_dp) } /* Get length of rates array potentially limited by max_rate. */ -static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate) +int intel_dp_rate_limit_len(const int *rates, int len, int max_rate) { int i; @@ -326,31 +326,6 @@ static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate) return 0; } -/* Get length of common rates array potentially limited by max_rate. */ -static int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, - int max_rate) -{ - return intel_dp_rate_limit_len(intel_dp->common_rates, - intel_dp->num_common_rates, max_rate); -} - -int intel_dp_common_rate(struct intel_dp *intel_dp, int index) -{ - struct intel_display *display = to_intel_display(intel_dp); - - if (drm_WARN_ON(display->drm, - index < 0 || index >= intel_dp->num_common_rates)) - return 162000; - - return intel_dp->common_rates[index]; -} - -/* Theoretical max between source and sink */ -int intel_dp_max_common_rate(struct intel_dp *intel_dp) -{ - return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1); -} - int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port) { int vbt_max_lanes = intel_bios_dp_max_lane_count(dig_port->base.devdata); @@ -364,43 +339,35 @@ int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port) /* * Theoretical max between source and sink. - * Return %true if the max common lane count changed. */ -static bool intel_dp_set_max_common_lane_count(struct intel_dp *intel_dp) +static int intel_dp_get_max_common_lane_count(struct intel_dp *intel_dp) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); int source_max = intel_dp_max_source_lane_count(dig_port); int sink_max = intel_dp->max_sink_lane_count; int lane_max = intel_tc_port_max_lane_count(dig_port); int lttpr_max = drm_dp_lttpr_max_lane_count(intel_dp->lttpr_common_caps); - int old_max_common_lane_count = intel_dp->max_common_lane_count; if (lttpr_max) sink_max = min(sink_max, lttpr_max); - intel_dp->max_common_lane_count = min3(source_max, sink_max, lane_max); - - return intel_dp->max_common_lane_count != old_max_common_lane_count; -} - -int intel_dp_max_common_lane_count(struct intel_dp *intel_dp) -{ - return intel_dp->max_common_lane_count; -} - -static int forced_lane_count(struct intel_dp *intel_dp) -{ - return clamp(intel_dp->link.force_lane_count, 1, intel_dp_max_common_lane_count(intel_dp)); + return min3(source_max, sink_max, lane_max); } int intel_dp_max_lane_count(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_limits; + struct intel_dp_link_config forced_params; int lane_count; - if (intel_dp->link.force_lane_count) - lane_count = forced_lane_count(intel_dp); + intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + + if (forced_params.lane_count) + lane_count = forced_params.lane_count; else - lane_count = intel_dp->link.max_lane_count; + lane_count = max_link_limits.lane_count; switch (lane_count) { case 1: @@ -415,8 +382,12 @@ int intel_dp_max_lane_count(struct intel_dp *intel_dp) static int intel_dp_min_lane_count(struct intel_dp *intel_dp) { - if (intel_dp->link.force_lane_count) - return forced_lane_count(intel_dp); + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); + + if (forced_params.lane_count) + return forced_params.lane_count; return 1; } @@ -698,174 +669,44 @@ int intel_dp_rate_index(const int *rates, int len, int rate) return -1; } -static int intel_dp_link_config_rate(struct intel_dp *intel_dp, - const struct intel_dp_link_config *lc) -{ - return intel_dp_common_rate(intel_dp, lc->link_rate_idx); -} - -static int intel_dp_link_config_lane_count(const struct intel_dp_link_config *lc) -{ - return 1 << lc->lane_count_exp; -} - -static int intel_dp_link_config_bw(struct intel_dp *intel_dp, - const struct intel_dp_link_config *lc) -{ - return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc), - intel_dp_link_config_lane_count(lc)); -} - -static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) -{ - struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */ - const struct intel_dp_link_config *lc_a = a; - const struct intel_dp_link_config *lc_b = b; - int bw_a = intel_dp_link_config_bw(intel_dp, lc_a); - int bw_b = intel_dp_link_config_bw(intel_dp, lc_b); - - if (bw_a != bw_b) - return bw_a - bw_b; - - return intel_dp_link_config_rate(intel_dp, lc_a) - - intel_dp_link_config_rate(intel_dp, lc_b); -} - -static void intel_dp_link_config_init(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - struct intel_dp_link_config *lc; - int num_common_lane_configs; - int i; - int j; - - if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp)))) - return; - - num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1; - - if (drm_WARN_ON(display->drm, intel_dp->num_common_rates * num_common_lane_configs > - ARRAY_SIZE(intel_dp->link.configs))) - return; - - intel_dp->link.num_configs = intel_dp->num_common_rates * num_common_lane_configs; - - lc = &intel_dp->link.configs[0]; - for (i = 0; i < intel_dp->num_common_rates; i++) { - for (j = 0; j < num_common_lane_configs; j++) { - lc->lane_count_exp = j; - lc->link_rate_idx = i; - - lc++; - } - } - - sort_r(intel_dp->link.configs, intel_dp->link.num_configs, - sizeof(intel_dp->link.configs[0]), - link_config_cmp_by_bw, NULL, - intel_dp); -} - -void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count) -{ - struct intel_display *display = to_intel_display(intel_dp); - const struct intel_dp_link_config *lc; - - if (drm_WARN_ON(display->drm, idx < 0 || idx >= intel_dp->link.num_configs)) - idx = 0; - - lc = &intel_dp->link.configs[idx]; - - *link_rate = intel_dp_link_config_rate(intel_dp, lc); - *lane_count = intel_dp_link_config_lane_count(lc); -} - -int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count) -{ - int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates, - link_rate); - int lane_count_exp = ilog2(lane_count); - int i; - - for (i = 0; i < intel_dp->link.num_configs; i++) { - const struct intel_dp_link_config *lc = &intel_dp->link.configs[i]; - - if (lc->lane_count_exp == lane_count_exp && - lc->link_rate_idx == link_rate_idx) - return i; - } - - return -1; -} - -/* Return %true if the common rates changed. */ -static bool intel_dp_set_common_rates(struct intel_dp *intel_dp) +static void intel_dp_get_common_rates(struct intel_dp *intel_dp, + int common_rates[DP_MAX_SUPPORTED_RATES], + int *num_common_rates) { struct intel_display *display = to_intel_display(intel_dp); - int num_old_common_rates = intel_dp->num_common_rates; - int old_common_rates[DP_MAX_SUPPORTED_RATES]; drm_WARN_ON(display->drm, !intel_dp->num_source_rates || !intel_dp->num_sink_rates); - /* TODO: Add a struct containing both rates and number of rates. */ - static_assert(__same_type(old_common_rates[0], intel_dp->common_rates[0]) && - sizeof(old_common_rates) == sizeof(intel_dp->common_rates)); - memcpy(old_common_rates, intel_dp->common_rates, - num_old_common_rates * sizeof(old_common_rates[0])); - - intel_dp->num_common_rates = intersect_rates(intel_dp->source_rates, - intel_dp->num_source_rates, - intel_dp->sink_rates, - intel_dp->num_sink_rates, - intel_dp->common_rates); + *num_common_rates = intersect_rates(intel_dp->source_rates, + intel_dp->num_source_rates, + intel_dp->sink_rates, + intel_dp->num_sink_rates, + common_rates); /* Paranoia, there should always be something in common. */ - if (drm_WARN_ON(display->drm, intel_dp->num_common_rates == 0)) { - intel_dp->common_rates[0] = 162000; - intel_dp->num_common_rates = 1; + if (drm_WARN_ON(display->drm, *num_common_rates == 0)) { + common_rates[0] = 162000; + *num_common_rates = 1; } - - return num_old_common_rates != intel_dp->num_common_rates || - memcmp(old_common_rates, intel_dp->common_rates, - num_old_common_rates * sizeof(old_common_rates[0])); } /* Return %true if any common link param changed. */ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) { + int num_common_rates; + int common_rates[DP_MAX_SUPPORTED_RATES]; bool params_changed = false; - if (intel_dp_set_common_rates(intel_dp)) + intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates); + if (intel_dp_link_caps_update(intel_dp->link.caps, + common_rates, num_common_rates, + intel_dp_get_max_common_lane_count(intel_dp))) params_changed = true; - if (intel_dp_set_max_common_lane_count(intel_dp)) - params_changed = true; - - intel_dp_link_config_init(intel_dp); - return params_changed; } -bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, - u8 lane_count) -{ - /* - * FIXME: we need to synchronize the current link parameters with - * hardware readout. Currently fast link training doesn't work on - * boot-up. - */ - if (link_rate == 0 || - link_rate > intel_dp->link.max_rate) - return false; - - if (lane_count == 0 || - lane_count > intel_dp_max_lane_count(intel_dp)) - return false; - - return true; -} - u32 intel_dp_mode_to_fec_clock(u32 mode_clock) { return div_u64(mul_u32_u32(mode_clock, DP_DSC_FEC_OVERHEAD_FACTOR), @@ -1693,41 +1534,40 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp) seq_buf_print_array(&s, intel_dp->sink_rates, intel_dp->num_sink_rates); drm_dbg_kms(display->drm, "sink rates: %s\n", seq_buf_str(&s)); - seq_buf_clear(&s); - seq_buf_print_array(&s, intel_dp->common_rates, intel_dp->num_common_rates); - drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); -} - -static int forced_link_rate(struct intel_dp *intel_dp) -{ - int len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate); - - if (len == 0) - return intel_dp_common_rate(intel_dp, 0); - - return intel_dp_common_rate(intel_dp, len - 1); + intel_dp_link_caps_print_common_rates(intel_dp->link.caps); } int intel_dp_max_link_rate(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_limits; + struct intel_dp_link_config forced_params; int len; - if (intel_dp->link.force_rate) - return forced_link_rate(intel_dp); + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.max_rate); + if (forced_params.rate) + return forced_params.rate; - return intel_dp_common_rate(intel_dp, len - 1); + intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); + len = intel_dp_common_len_rate_limit(link_caps, max_link_limits.rate); + + return intel_dp_common_rate(link_caps, len - 1); } static int intel_dp_min_link_rate(struct intel_dp *intel_dp) { - if (intel_dp->link.force_rate) - return forced_link_rate(intel_dp); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - return intel_dp_common_rate(intel_dp, 0); + if (forced_params.rate) + return forced_params.rate; + + return intel_dp_common_rate(link_caps, 0); } int intel_dp_rate_select(struct intel_dp *intel_dp, int rate) @@ -1911,6 +1751,7 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state, const struct link_config_limits *limits) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int bpp, i, lane_count, clock = intel_dp_mode_clock(pipe_config, conn_state); int link_rate, link_avail; @@ -1920,8 +1761,8 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, int link_bpp_x16 = intel_dp_output_format_link_bpp_x16(pipe_config->output_format, bpp); - for (i = 0; i < intel_dp->num_common_rates; i++) { - link_rate = intel_dp_common_rate(intel_dp, i); + for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { + link_rate = intel_dp_common_rate(link_caps, i); if (link_rate < limits->min_rate || link_rate > limits->max_rate) continue; @@ -2145,12 +1986,13 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp, const struct link_config_limits *limits, int dsc_bpp_x16) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; int link_rate, lane_count; int i; - for (i = 0; i < intel_dp->num_common_rates; i++) { - link_rate = intel_dp_common_rate(intel_dp, i); + for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { + link_rate = intel_dp_common_rate(link_caps, i); if (link_rate < limits->min_rate || link_rate > limits->max_rate) continue; @@ -3685,19 +3527,19 @@ int intel_dp_compute_min_hblank(struct intel_crtc_state *crtc_state, } int -intel_dp_compute_config(struct intel_encoder *encoder, +intel_dp_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state); struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_connector *connector = intel_dp->attached_connector; int ret = 0, link_bpp_x16; if (intel_dp_is_edp(intel_dp)) { - ret = intel_panel_compute_config(connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, connector); if (ret) return ret; } @@ -3810,12 +3652,10 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp, void intel_dp_reset_link_params(struct intel_dp *intel_dp) { - intel_dp->link.max_lane_count = intel_dp_max_common_lane_count(intel_dp); - intel_dp->link.max_rate = intel_dp_max_common_rate(intel_dp); + intel_dp_link_caps_reset(intel_dp->link.caps); intel_dp->link.mst_probed_lane_count = 0; intel_dp->link.mst_probed_rate = 0; - intel_dp->link.retrain_disabled = false; - intel_dp->link.seq_train_failures = 0; + intel_dp_link_training_reset(intel_dp->link.training); } /* Enable backlight PWM and backlight PP control. */ @@ -5715,32 +5555,6 @@ void intel_read_dp_sdp(struct intel_encoder *encoder, } } -static bool intel_dp_link_ok(struct intel_dp *intel_dp, - u8 link_status[DP_LINK_STATUS_SIZE]) -{ - struct intel_display *display = to_intel_display(intel_dp); - struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; - bool uhbr = intel_dp->link_rate >= 1000000; - bool ok; - - if (uhbr) - ok = drm_dp_128b132b_lane_channel_eq_done(link_status, - intel_dp->lane_count); - else - ok = drm_dp_channel_eq_ok(link_status, intel_dp->lane_count); - - if (ok) - return true; - - intel_dp_dump_link_status(intel_dp, DP_PHY_DPRX, link_status); - drm_dbg_kms(display->drm, - "[ENCODER:%d:%s] %s link not ok, retraining\n", - encoder->base.base.id, encoder->base.name, - uhbr ? "128b/132b" : "8b/10b"); - - return false; -} - static void intel_dp_mst_hpd_irq(struct intel_dp *intel_dp, u8 *esi, u8 *ack) { @@ -5774,7 +5588,7 @@ static bool intel_dp_check_mst_status(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - bool force_retrain = intel_dp->link.force_retrain; + bool force_retrain = intel_dp_link_training_get_force_retrain(intel_dp->link.training); bool reprobe_needed = false; for (;;) { @@ -5847,78 +5661,6 @@ intel_dp_handle_hdmi_link_status_change(struct intel_dp *intel_dp) } } -static int -intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATUS_SIZE]) -{ - int err; - - memset(link_status, 0, DP_LINK_STATUS_SIZE); - - if (intel_dp_mst_active_streams(intel_dp) > 0) - err = drm_dp_dpcd_read_data(&intel_dp->aux, DP_LANE0_1_STATUS_ESI, - link_status, DP_LINK_STATUS_SIZE - 2); - else - err = drm_dp_dpcd_read_phy_link_status(&intel_dp->aux, DP_PHY_DPRX, - link_status); - - if (err) - return err; - - if (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] & - DP_DOWNSTREAM_PORT_STATUS_CHANGED) - WRITE_ONCE(intel_dp->downstream_port_changed, true); - - return 0; -} - -static bool -intel_dp_needs_link_retrain(struct intel_dp *intel_dp) -{ - u8 link_status[DP_LINK_STATUS_SIZE]; - - if (!intel_dp->link.active) - return false; - - /* - * While PSR source HW is enabled, it will control main-link sending - * frames, enabling and disabling it so trying to do a retrain will fail - * as the link would or not be on or it could mix training patterns - * and frame data at the same time causing retrain to fail. - * Also when exiting PSR, HW will retrain the link anyways fixing - * any link status error. - */ - if (intel_psr_enabled(intel_dp)) - return false; - - if (intel_dp->link.force_retrain) - return true; - - if (intel_dp_read_link_status(intel_dp, link_status) < 0) - return false; - - /* - * Validate the cached values of intel_dp->link_rate and - * intel_dp->lane_count before attempting to retrain. - * - * FIXME would be nice to user the crtc state here, but since - * we need to call this from the short HPD handler that seems - * a bit hard. - */ - if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate, - intel_dp->lane_count)) - return false; - - if (intel_dp->link.retrain_disabled) - return false; - - if (intel_dp->link.seq_train_failures) - return true; - - /* Retrain if link not ok */ - return !intel_dp_link_ok(intel_dp, link_status) && - !intel_psr_link_ok(intel_dp); -} - bool intel_dp_has_connector(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state) { @@ -6010,86 +5752,6 @@ void intel_dp_flush_connector_commits(struct intel_connector *connector) wait_for_connector_hw_done(connector->base.state); } -static bool intel_dp_is_connected(struct intel_dp *intel_dp) -{ - struct intel_connector *connector = intel_dp->attached_connector; - - return connector->base.status == connector_status_connected || - intel_dp->is_mst; -} - -static int intel_dp_retrain_link(struct intel_encoder *encoder, - struct drm_modeset_acquire_ctx *ctx) -{ - struct intel_display *display = to_intel_display(encoder); - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - u8 pipe_mask; - int ret; - - if (!intel_dp_is_connected(intel_dp)) - return 0; - - ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex, - ctx); - if (ret) - return ret; - - if (!intel_dp_needs_link_retrain(intel_dp)) - return 0; - - ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask); - if (ret) - return ret; - - if (pipe_mask == 0) - return 0; - - if (!intel_dp_needs_link_retrain(intel_dp)) - return 0; - - drm_dbg_kms(display->drm, - "[ENCODER:%d:%s] retraining link (forced %s)\n", - encoder->base.base.id, encoder->base.name, - str_yes_no(intel_dp->link.force_retrain)); - - ret = intel_modeset_commit_pipes(display, pipe_mask, ctx); - if (ret == -EDEADLK) - return ret; - - intel_dp->link.force_retrain = false; - - if (ret) - drm_dbg_kms(display->drm, - "[ENCODER:%d:%s] link retraining failed: %pe\n", - encoder->base.base.id, encoder->base.name, - ERR_PTR(ret)); - - return ret; -} - -void intel_dp_link_check(struct intel_encoder *encoder) -{ - struct drm_modeset_acquire_ctx ctx; - int ret; - - intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret) - ret = intel_dp_retrain_link(encoder, &ctx); -} - -void intel_dp_check_link_state(struct intel_dp *intel_dp) -{ - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct intel_encoder *encoder = &dig_port->base; - - if (!intel_dp_is_connected(intel_dp)) - return; - - if (!intel_dp_needs_link_retrain(intel_dp)) - return; - - intel_encoder_link_check_queue_work(encoder, 0); -} - static void intel_dp_handle_device_service_irq(struct intel_dp *intel_dp, u8 irq_mask) { struct intel_display *display = to_intel_display(intel_dp); @@ -6185,7 +5847,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) /* * Force checking the link status for DPCD_REV < 1.2 * TODO: let the link status check depend on LINK_STATUS_CHANGED - * or intel_dp->link.force_retrain for DPCD_REV >= 1.2 + * or intel_dp->link.training.force_retrain for DPCD_REV >= 1.2 */ esi[3] |= LINK_STATUS_CHANGED; if (intel_dp_handle_link_service_irq(intel_dp, esi[3])) @@ -7688,3 +7350,25 @@ u8 intel_dp_as_sdp_transmission_time(void) return DP_PR_AS_SDP_SETUP_TIME_T1; } + +int intel_dp_link_init(struct intel_dp *intel_dp) +{ + intel_dp->link.training = intel_dp_link_training_init(intel_dp); + if (!intel_dp->link.training) + return -ENOMEM; + + intel_dp->link.caps = intel_dp_link_caps_init(intel_dp); + if (!intel_dp->link.caps) { + intel_dp_link_training_cleanup(intel_dp->link.training); + + return -ENOMEM; + } + + return 0; +} + +void intel_dp_link_cleanup(struct intel_dp *intel_dp) +{ + intel_dp_link_caps_cleanup(intel_dp->link.caps); + intel_dp_link_training_cleanup(intel_dp->link.training); +} diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 46a7f5c70981..02b691df6755 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -59,8 +59,6 @@ int intel_dp_get_active_pipes(struct intel_dp *intel_dp, struct drm_modeset_acquire_ctx *ctx, u8 *pipe_mask); void intel_dp_flush_connector_commits(struct intel_connector *connector); -void intel_dp_link_check(struct intel_encoder *encoder); -void intel_dp_check_link_state(struct intel_dp *intel_dp); void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode); void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); @@ -73,7 +71,8 @@ void intel_dp_sink_disable_decompression(struct intel_atomic_state *state, void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder); void intel_dp_encoder_shutdown(struct intel_encoder *intel_encoder); void intel_dp_encoder_flush_work(struct drm_encoder *encoder); -int intel_dp_compute_config(struct intel_encoder *encoder, +int intel_dp_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state); bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state, @@ -102,17 +101,13 @@ void intel_edp_backlight_off(const struct drm_connector_state *conn_state); void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp); void intel_dp_mst_suspend(struct intel_display *display); void intel_dp_mst_resume(struct intel_display *display); +int intel_dp_rate_limit_len(const int *rates, int len, int max_rate); int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port); int intel_dp_max_link_rate(struct intel_dp *intel_dp); int intel_dp_max_lane_count(struct intel_dp *intel_dp); int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); -int intel_dp_max_common_rate(struct intel_dp *intel_dp); -int intel_dp_max_common_lane_count(struct intel_dp *intel_dp); -int intel_dp_common_rate(struct intel_dp *intel_dp, int index); int intel_dp_rate_index(const int *rates, int len, int rate); -int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count); -void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); void intel_dp_update_sink_caps(struct intel_dp *intel_dp); void intel_dp_reset_link_params(struct intel_dp *intel_dp); @@ -209,8 +204,6 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector); bool intel_dp_has_gamut_metadata_dip(struct intel_encoder *encoder); -bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, - u8 lane_count); bool intel_dp_has_connector(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state); int intel_dp_dsc_max_src_input_bpc(struct intel_display *display); @@ -243,4 +236,7 @@ bool intel_dp_joiner_candidate_valid(struct intel_connector *connector, u8 intel_dp_as_sdp_transmission_time(void); +int intel_dp_link_init(struct intel_dp *intel_dp); +void intel_dp_link_cleanup(struct intel_dp *intel_dp); + #endif /* __INTEL_DP_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c new file mode 100644 index 000000000000..1c34ba6c49c3 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -0,0 +1,675 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include <linux/bitops.h> +#include <linux/debugfs.h> +#include <linux/log2.h> +#include <linux/seq_buf.h> +#include <linux/slab.h> +#include <linux/sort.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <drm/drm_print.h> + +#include "intel_display_core.h" +#include "intel_display_types.h" +#include "intel_dp.h" +#include "intel_dp_link_caps.h" + +struct intel_dp_link_caps { + struct intel_dp *dp; + + /* Rate, lane count caps common to source and sink. */ + int num_rates; + int rates[DP_MAX_SUPPORTED_RATES]; + int max_lane_count; + + /* common rate,lane_count configs in bw order */ + int num_configs; +#define INTEL_DP_MAX_LANE_COUNT 4 +#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1) +#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) +#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS) +#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \ + INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) + struct intel_dp_link_config_entry { + /* index into rates[] */ + u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS; + u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS; + } configs[INTEL_DP_MAX_LINK_CONFIGS]; + + /* + * Forced parameters requested via debugfs. Remains set across sink + * disconnects. + */ + struct intel_dp_link_config forced_params; + + /* + * User set maximum limits. These limits constrain the currently + * allowed set of configurations and are not adjusted when sink + * capabilities change. + * + * max_limits.rate/lane_count may come from different allowed + * configurations, i.e. the (max_limits.rate, max_limits.lane_count) + * tuple itself may not be an allowed configuration. + */ + struct intel_dp_link_config max_limits; +}; + +/* Get length of common rates array potentially limited by max_rate. */ +int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, + int max_rate) +{ + return intel_dp_rate_limit_len(link_caps->rates, + link_caps->num_rates, max_rate); +} + +int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) +{ + struct intel_display *display = to_intel_display(link_caps->dp); + + if (drm_WARN_ON(display->drm, + index < 0 || index >= link_caps->num_rates)) + return 162000; + + return link_caps->rates[index]; +} + +int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate) +{ + return intel_dp_rate_index(link_caps->rates, + link_caps->num_rates, + rate); +} + +/* Theoretical max between source and sink */ +int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps) +{ + return intel_dp_common_rate(link_caps, link_caps->num_rates - 1); +} + +int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps) +{ + return link_caps->num_rates; +} + +void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) +{ + struct intel_display *display = to_intel_display(link_caps->dp); + DECLARE_SEQ_BUF(s, 128); + int i; + + for (i = 0; i < link_caps->num_rates; i++) + seq_buf_printf(&s, "%s%d", i ? ", " : "", link_caps->rates[i]); + + drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); +} + +int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps) +{ + return link_caps->max_lane_count; +} + +static int forced_lane_count(struct intel_dp_link_caps *link_caps) +{ + if (!link_caps->forced_params.lane_count) + return 0; + + return clamp(link_caps->forced_params.lane_count, + 1, intel_dp_link_caps_max_common_lane_count(link_caps)); +} + +static int forced_link_rate(struct intel_dp_link_caps *link_caps) +{ + int len; + + if (!link_caps->forced_params.rate) + return 0; + + len = intel_dp_common_len_rate_limit(link_caps, link_caps->forced_params.rate); + if (len == 0) + return intel_dp_common_rate(link_caps, 0); + + return intel_dp_common_rate(link_caps, len - 1); +} + +void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *forced_params) +{ + forced_params->rate = forced_link_rate(link_caps); + forced_params->lane_count = forced_lane_count(link_caps); +} + +static int intel_dp_link_config_rate(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config_entry *lce) +{ + return intel_dp_common_rate(link_caps, lce->link_rate_idx); +} + +static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lce) +{ + return 1 << lce->lane_count_exp; +} + +static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) +{ + link_caps->max_limits = *max_link_limits; +} + +static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps) +{ + struct intel_dp_link_config max_link_limits = { + .rate = intel_dp_max_common_rate(link_caps), + .lane_count = intel_dp_link_caps_max_common_lane_count(link_caps), + }; + + set_max_link_limits_no_update(link_caps, &max_link_limits); +} + +/** + * intel_dp_link_caps_get_max_limits - get the current maximum link limits + * @link_caps: link capabilities state + * @max_link_limits: returned maximum link limits + * + * Return the current maximum rate and lane count limits in + * @max_link_limits. + * + * These limits constrain the set of allowed configurations. + * + * The limits are set to the maximum common supported values after + * intel_dp_link_caps_reset() is called, and can later be modified by + * intel_dp_link_caps_set_max_limits(). The max rate and lane count + * parameters are independent limits, so the pair does not necessarily + * define a valid configuration. + * + * This function may be called without serializing against updates to + * @link_caps. However, without such serialization the returned value may be + * an out-of-sync (link rate, lane count) tuple, i.e. the parameters may + * belong to different update snapshots in time. + */ +void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_link_limits) +{ + *max_link_limits = link_caps->max_limits; +} + +/** + * intel_dp_link_caps_set_max_limits - set the current maximum link limits + * @link_caps: link capabilities state + * @max_link_limits: new maximum link limits + * + * Set the current maximum rate and lane count limits to @max_link_limits, + * constraining the set of allowed configurations. + * + * Unlike intel_dp_link_caps_get_max_limits(), the caller must serialize + * this call against concurrent queries and updates to @link_caps, in line + * with the rest of the API. + * + * Return: + * - %true if the @link_caps cached max limits value got updated with + * @max_link_limits. + * - %false if @max_link_limits is invalid. + */ +bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) +{ + set_max_link_limits_no_update(link_caps, max_link_limits); + + /* TODO: validate max_link_limits */ + return true; +} + +/** + * intel_dp_link_caps_reset_max_limits - reset the current maximum link limits + * @link_caps: link capabilities state + * + * Reset the current maximum link limits to the maximum supported common link + * rate and lane count. + */ +void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps) +{ + reset_max_link_limits_no_update(link_caps); +} + +static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config_entry *lce) +{ + return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(link_caps, lce), + intel_dp_link_config_lane_count(lce)); +} + +static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) +{ + struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */ + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + + const struct intel_dp_link_config_entry *lce_a = a; + const struct intel_dp_link_config_entry *lce_b = b; + int bw_a = intel_dp_link_config_bw(link_caps, lce_a); + int bw_b = intel_dp_link_config_bw(link_caps, lce_b); + + if (bw_a != bw_b) + return bw_a - bw_b; + + return intel_dp_link_config_rate(link_caps, lce_a) - + intel_dp_link_config_rate(link_caps, lce_b); +} + +/* Return %true if the supported link parameters have changed. */ +bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, + const int *rates, int num_rates, int max_lane_count) +{ + struct intel_dp *intel_dp = link_caps->dp; + struct intel_display *display = to_intel_display(intel_dp); + struct intel_dp_link_config_entry *lce; + bool link_params_changed = false; + int num_common_lane_configs; + int i; + int j; + + if (drm_WARN_ON(display->drm, !is_power_of_2(max_lane_count))) + return false; + + if (drm_WARN_ON(display->drm, num_rates > ARRAY_SIZE(link_caps->rates))) + return false; + + num_common_lane_configs = ilog2(max_lane_count) + 1; + + if (drm_WARN_ON(display->drm, num_rates * num_common_lane_configs > + ARRAY_SIZE(link_caps->configs))) + return false; + + /* TODO: Add a struct containing both rates and number of rates. */ + static_assert(__same_type(rates[0], link_caps->rates[0])); + if (num_rates != link_caps->num_rates || + memcmp(rates, link_caps->rates, num_rates * sizeof(rates[0]))) + link_params_changed = true; + + if (max_lane_count != link_caps->max_lane_count) + link_params_changed = true; + + memcpy(link_caps->rates, rates, num_rates * sizeof(rates[0])); + link_caps->num_rates = num_rates; + link_caps->max_lane_count = max_lane_count; + + link_caps->num_configs = num_rates * num_common_lane_configs; + + lce = &link_caps->configs[0]; + for (i = 0; i < link_caps->num_rates; i++) { + for (j = 0; j < num_common_lane_configs; j++) { + lce->lane_count_exp = j; + lce->link_rate_idx = i; + + lce++; + } + } + + sort_r(link_caps->configs, link_caps->num_configs, + sizeof(link_caps->configs[0]), + link_config_cmp_by_bw, NULL, + intel_dp); + + return link_params_changed; +} + +void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, + int idx, int *link_rate, int *lane_count) +{ + struct intel_display *display = to_intel_display(link_caps->dp); + const struct intel_dp_link_config_entry *lce; + + if (drm_WARN_ON(display->drm, idx < 0 || idx >= link_caps->num_configs)) + idx = 0; + + lce = &link_caps->configs[idx]; + + *link_rate = intel_dp_link_config_rate(link_caps, lce); + *lane_count = intel_dp_link_config_lane_count(lce); +} + +int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, + int link_rate, int lane_count) +{ + int link_rate_idx = intel_dp_rate_index(link_caps->rates, link_caps->num_rates, + link_rate); + int lane_count_exp = ilog2(lane_count); + int i; + + for (i = 0; i < link_caps->num_configs; i++) { + const struct intel_dp_link_config_entry *lce = &link_caps->configs[i]; + + if (lce->lane_count_exp == lane_count_exp && + lce->link_rate_idx == link_rate_idx) + return i; + } + + return -1; +} + +/** + * intel_dp_link_caps_reset - reset link capability restrictions + * @link_caps: link capabilities state + * + * Reset all current restrictions except for the user requested forced + * parameters, thus updating the set of allowed configurations and the + * derived maximum link information accordingly. + * + * This function is regularly called after a sink is connected, either + * for the first time to the connector or after a previous sink was + * disconnected from it, and intel_dp_link_caps_update() was called. + */ +void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps) +{ + /* TODO: Update the maximum link information. */ + reset_max_link_limits_no_update(link_caps); +} + +static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) +{ + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int current_rate = -1; + int force_rate; + int err; + int i; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + if (intel_dp->link.active) + current_rate = intel_dp->link_rate; + + force_rate = link_caps->forced_params.rate; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + seq_printf(m, "%sauto%s", + force_rate == 0 ? "[" : "", + force_rate == 0 ? "]" : ""); + + for (i = 0; i < intel_dp->num_source_rates; i++) + seq_printf(m, " %s%d%s%s", + intel_dp->source_rates[i] == force_rate ? "[" : "", + intel_dp->source_rates[i], + intel_dp->source_rates[i] == current_rate ? "*" : "", + intel_dp->source_rates[i] == force_rate ? "]" : ""); + + seq_putc(m, '\n'); + + return 0; +} + +static int parse_link_rate(struct intel_dp_link_caps *link_caps, const char __user *ubuf, size_t len) +{ + struct intel_dp *intel_dp = link_caps->dp; + char *kbuf; + const char *p; + int rate; + int ret = 0; + + kbuf = memdup_user_nul(ubuf, len); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + + p = strim(kbuf); + + if (!strcmp(p, "auto")) { + rate = 0; + } else { + ret = kstrtoint(p, 0, &rate); + if (ret < 0) + goto out_free; + + if (intel_dp_rate_index(intel_dp->source_rates, + intel_dp->num_source_rates, + rate) < 0) + ret = -EINVAL; + } + +out_free: + kfree(kbuf); + + return ret < 0 ? ret : rate; +} + +static ssize_t i915_dp_force_link_rate_write(struct file *file, + const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int rate; + int err; + + rate = parse_link_rate(link_caps, ubuf, len); + if (rate < 0) + return rate; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_reset_link_params(intel_dp); + link_caps->forced_params.rate = rate; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + *offp += len; + + return len; +} +DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate); + +static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) +{ + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int current_lane_count = -1; + int force_lane_count; + int err; + int i; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + if (intel_dp->link.active) + current_lane_count = intel_dp->lane_count; + force_lane_count = link_caps->forced_params.lane_count; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + seq_printf(m, "%sauto%s", + force_lane_count == 0 ? "[" : "", + force_lane_count == 0 ? "]" : ""); + + for (i = 1; i <= 4; i <<= 1) + seq_printf(m, " %s%d%s%s", + i == force_lane_count ? "[" : "", + i, + i == current_lane_count ? "*" : "", + i == force_lane_count ? "]" : ""); + + seq_putc(m, '\n'); + + return 0; +} + +static int parse_lane_count(const char __user *ubuf, size_t len) +{ + char *kbuf; + const char *p; + int lane_count; + int ret = 0; + + kbuf = memdup_user_nul(ubuf, len); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + + p = strim(kbuf); + + if (!strcmp(p, "auto")) { + lane_count = 0; + } else { + ret = kstrtoint(p, 0, &lane_count); + if (ret < 0) + goto out_free; + + switch (lane_count) { + case 1: + case 2: + case 4: + break; + default: + ret = -EINVAL; + } + } + +out_free: + kfree(kbuf); + + return ret < 0 ? ret : lane_count; +} + +static ssize_t i915_dp_force_lane_count_write(struct file *file, + const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int lane_count; + int err; + + lane_count = parse_lane_count(ubuf, len); + if (lane_count < 0) + return lane_count; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_reset_link_params(intel_dp); + link_caps->forced_params.lane_count = lane_count; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + *offp += len; + + return len; +} +DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count); + +static int i915_dp_max_link_rate_show(void *data, u64 *val) +{ + struct intel_connector *connector = to_intel_connector(data); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_config max_link_limits; + int err; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); + *val = max_link_limits.rate; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n"); + +static int i915_dp_max_lane_count_show(void *data, u64 *val) +{ + struct intel_connector *connector = to_intel_connector(data); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_config max_link_limits; + int err; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); + *val = max_link_limits.lane_count; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n"); + + +/** + * intel_dp_link_caps_debugfs_add - add link caps debugfs files for a connector + * @connector: connector to add the debugfs files for + * + * Add the link-capability debugfs files for a DP @connector. + */ +void intel_dp_link_caps_debugfs_add(struct intel_connector *connector) +{ + struct dentry *root = connector->base.debugfs_entry; + + if (connector->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort && + connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) + return; + + debugfs_create_file("i915_dp_force_link_rate", 0644, root, + connector, &i915_dp_force_link_rate_fops); + + debugfs_create_file("i915_dp_force_lane_count", 0644, root, + connector, &i915_dp_force_lane_count_fops); + + debugfs_create_file("i915_dp_max_link_rate", 0444, root, + connector, &i915_dp_max_link_rate_fops); + + debugfs_create_file("i915_dp_max_lane_count", 0444, root, + connector, &i915_dp_max_lane_count_fops); +} + +struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) +{ + struct intel_dp_link_caps *link_caps; + + link_caps = kzalloc_obj(*link_caps); + if (!link_caps) + return NULL; + + link_caps->dp = intel_dp; + + return link_caps; +} + +void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps) +{ + kfree(link_caps); +} diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h new file mode 100644 index 000000000000..af9028e7cb98 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __INTEL_DP_LINK_CAPS_H__ +#define __INTEL_DP_LINK_CAPS_H__ + +#include <linux/types.h> + +struct intel_connector; +struct intel_dp; +struct intel_dp_link_caps; +struct intel_dp_link_config; + +int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, + int max_rate); +int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index); +int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate); +int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps); +int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps); +int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps); + +void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps); + +void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *forced_params); + +int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, + int link_rate, int lane_count); +void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, + int idx, int *link_rate, int *lane_count); + +void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_link_limits); +bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits); +void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps); + +bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, + const int *rates, int num_rates, int max_lane_count); +void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps); + +void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); + +struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp); +void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps); + +#endif /* __INTEL_DP_LINK_CAPS_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index e566f2b49594..b521dd11b62a 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -27,18 +27,344 @@ #include <drm/display/drm_dp_helper.h> #include <drm/drm_print.h> +#include "intel_display.h" #include "intel_display_core.h" #include "intel_display_jiffies.h" #include "intel_display_types.h" #include "intel_display_utils.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" +#include "intel_dp_mst.h" #include "intel_encoder.h" #include "intel_hdmi.h" #include "intel_hotplug.h" +#include "intel_modeset_lock.h" #include "intel_panel.h" #include "intel_psr.h" +/** + * DOC: DisplayPort link training + * + * This documents the Intel DisplayPort link training implementation and + * its internal interfaces, with a current focus on link recovery. + * + * Documentation of the full link training procedure is not yet included. + * + * The Intel DP link recovery logic governs how the driver reacts to + * link training failures and to links that degrade asynchronously + * after a previously successful training. Recovery is first attempted + * via automatic retraining (``autoretrain``) and, when that is no + * longer possible, by selecting fallback link configurations and + * notifying userspace to recover the link via a modeset. + * + * Recovery sequence and userspace notification + * -------------------------------------------- + * + * After the first link training failure following initialization or a + * previously successful training, recovery is first attempted by the + * driver via automatic retraining, without userspace involvement. + * During this phase, a given link configuration is attempted twice + * before being abandoned: after the initial link training failure, an + * automatic retraining modeset is performed with the same link + * parameters, constituting the second attempt. + * + * Once automatic retraining is no longer possible, recovery is delegated + * to userspace, which must select a new modeset configuration, as the + * kernel must not do so. From this point onwards, each link configuration + * may be attempted only once as userspace iterates through alternative + * configurations. A successful link training restores the automatic + * retraining model for subsequent failures. + * + * The failure of the last automatic retraining attempt is reported to + * userspace, and from that point onward the driver notifies userspace of + * each subsequent failure. This allows userspace to both initiate + * recovery via modesets and observe the outcome of those recovery + * attempts, even when no further fallback configurations remain. + * + * Link training failures are always reported to userspace, even when they + * result from a kernel-internal modeset. Such modesets only re-apply the + * existing userspace-provided state and must not modify it. A failure + * triggered by such a modeset is therefore treated the same as a link + * degradation after a previously successful training, and recovery is + * handled by userspace in place of the kernel caller. + * + * Contexts + * -------- + * + * The following execution contexts (A/B/C) describe how the different + * recovery states are reached but are not themselves implementation + * states. The actual state machine is defined by &enum + * intel_dp_link_training_recovery_state. + * + * A. Modeset context: + * + * Triggered by: + * - link training during a modeset, or + * - via the "i915_dp_force_link_training_failure" debugfs entry, + * forcing this path by emulating a link training failure. + * + * Transitions: + * - A1 Link training succeeds. + * + * A link check work to recover any degraded link is scheduled + * (and handled if needed in context B). + * + * State -> %INTEL_DP_LINK_RECOVERY_IDLE. + * + * - A2 First link training fails after initialization or a previously + * successful link training. + * + * An automatic retraining work is scheduled (and handled in + * context B) with the same link parameters with which the link + * training failed. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING. + * + * - A3 Link training fails again after A2 or A3. + * + * Through fallback selection, the driver attempts to restrict the + * allowed link configurations for subsequent modesets. This may + * be done either by lowering global limits (rate/lane caps), or by + * disabling only the currently failing configuration while leaving + * all other configurations allowed, even if they use higher rate or + * lane count. + * + * (The current implementation may still apply parameter capping as + * a coarse fallback selection mechanism. This is transitional and is + * expected to be replaced by a scheme that disables only the failing + * configuration, rather than removing configurations that have not + * been observed to fail and may still train successfully.) + * + * This case may repeat in a loop: + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED -> + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * via repeated A3a -> A3a transitions until the configuration fallback + * space is exhausted, reaching the A3b terminal case. + * + * - A3a Fallback selection succeeds. + * + * Userspace is notified to retry the modeset. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED. + * + * - A3b Fallback selection fails. + * + * Userspace is notified of the failure and may continue recovery + * by retrying the modeset with the remaining allowed link + * configuration. + * + * State -> %INTEL_DP_LINK_RECOVERY_NO_FALLBACK. + * + * B. Automatic retraining context: + * + * Triggered by: + * - after a successful link training in context A1 followed by + * asynchronous link degradation, or + * - after the first failed link training attempt in context A2, or + * - via the "i915_dp_force_link_retrain" debugfs entry, which may + * bypass normal gating and force this path. + * + * Transitions: + * - B1 ``Autoretrain`` modeset check and link training succeeds. + * + * The case is handled as in A1, scheduling a link check work to + * recover any degraded link. + * + * State -> %INTEL_DP_LINK_RECOVERY_IDLE. + * + * - B2 ``Autoretrain`` modeset check succeeds but link training fails. + * + * - B2a Previously the link degraded asynchronously (current state + * is %INTEL_DP_LINK_RECOVERY_IDLE). + * + * This corresponds to a first failure in a new failure + * sequence and is handled as in A2: an automatic retraining + * attempt is scheduled with the same link parameters. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING. + * + * - B2b Previously a link training failed (current state is + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING). + * + * In non-regular (debug-forced) scenarios this may also be + * reached from + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED or + * %INTEL_DP_LINK_RECOVERY_NO_FALLBACK, effectively behaving + * like a userspace-driven recovery attempt. + * + * The failure is handled as in A3, performing a fallback selection: + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED (via A3a). + * + * or + * + * State -> %INTEL_DP_LINK_RECOVERY_NO_FALLBACK (via A3b). + * + * - B3 ``Autoretrain`` modeset check fails (and hence the link training + * cannot be started). + * + * The modeset check may fail, for example, due to external conditions + * such as changed shared link bandwidth, which can make previously + * valid modeset parameters no longer acceptable. + * + * In this case, automatic retraining is disabled without selecting + * a fallback configuration. The driver hands recovery over to + * userspace without modifying the allowed configuration set, so a + * subsequent userspace modeset will retry with the current link + * configuration. Userspace is in a better position to select new + * modeset parameters (e.g. video mode or enabled outputs) that + * satisfy the updated constraints, as the driver is only allowed + * to retry the modeset with the existing userspace-provided modeset + * configuration. + * + * This policy preserves the normal retry model, where a given link + * configuration is attempted twice in the automatic retraining + * flow before being abandoned: after a first link training failure, + * an automatic retraining modeset is performed with the same link + * parameters, and if its atomic check passes, the link training + * itself may either succeed or fail, constituting the second + * attempt. In this case, however, the retry modeset's atomic check + * failed, so no second link training attempt with those parameters + * was performed, and selecting a fallback would cause that + * configuration to be tried only once rather than twice. + * + * The userspace-driven link recovery continues with subsequent + * userspace modesets handled in A3. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED. + * + * C. State reset context: + * + * Triggered by: + * - sink capability changes, or + * - sink disconnect/reconnect, or + * - system suspend/resume or power transitions where HPD + * handling may have been suppressed, or + * - successful link training. + * + * Transitions: + * - The recovery state is reset from any of the recovery states + * + * State -> %INTEL_DP_LINK_RECOVERY_IDLE. + * + * After reset, the driver may re-check link status and schedule + * retraining if the link is found to remain degraded. + * + * State transition summary + * ------------------------ + * + * - From %INTEL_DP_LINK_RECOVERY_IDLE + * + * - To %INTEL_DP_LINK_RECOVERY_IDLE + * + * - | In context: B1 + * | Action: no action + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING + * + * - | In contexts: A2, B2a + * | Action: queue ``autoretrain`` work + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - | In context: B3 + * | Action: notify userspace + * + * - From %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - | In contexts: A3a, B2b + * | Action: select fallback configurations, notify userspace + * + * - | In context: B3 + * | Action: notify userspace + * + * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - | In contexts: A3b, B2b + * | Action: notify userspace + * + * - From %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - | In contexts: A3a, B2b + * | Action: select fallback configurations, notify userspace + * + * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - | In contexts: A3b, B2b + * | Action: notify userspace + * + * - From %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - | In contexts: A3b + * | Action: notify userspace + * + * - From any state + * + * - To %INTEL_DP_LINK_RECOVERY_IDLE + * + * - | In contexts: C + * | Action: no action + * + * Recovery flows + * -------------- + * + * Userspace modeset link recovery:: + * + * [IDLE] + * | + * | userspace modeset link training fails + * | (autoretrain link recovery work scheduled) + * v + * [AUTORETRAIN_PENDING]-- autoretrain link recovery succeeds -> [IDLE] + * | + * | autoretrain link recovery modeset check or link training fails + * | + * +--o--+ + * modeset check fails | | link training fails + * (userspace notified) | | + * | o-------- no fallback (userspace notified) ---> [NO_FALLBACK] + * | | + * +-------------+ | | fallback selected (userspace notified) + * | | | | + * | v v v + * | [AUTORETRAIN_DISABLED]--- userspace link recovery succeeds ----> [IDLE] + * | | + * | | userspace link recovery fails + * | | + * +-------------------o------------- no fallback (userspace notified) ----> [NO_FALLBACK] + * fallback selected + * (userspace notified) + * + * Asynchronous link degradation recovery:: + * + * [IDLE] + * | + * | link degrades + * | (autoretrain link recovery performed) + * | + * o--- autoretrain link recovery succeeds ---> [IDLE] + * | + * | autoretrain link recovery modeset check or link training fails + * | + * +--o--+ + * modeset check fails | | link training fails + * (userspace notified) | | (autoretrain work scheduled) + * v v + * [AUTORETRAIN_DISABLED*] [AUTORETRAIN_PENDING*] + * + * ``*`` marks states where the sequence continues from the corresponding state + * in the Userspace modeset link recovery flow above. + * + */ + #define LT_MSG_PREFIX "[CONNECTOR:%d:%s][ENCODER:%d:%s][%s] " #define LT_MSG_ARGS(_intel_dp, _dp_phy) (_intel_dp)->attached_connector->base.base.id, \ (_intel_dp)->attached_connector->base.name, \ @@ -60,7 +386,72 @@ lt_dbg(_intel_dp, _dp_phy, "Sink disconnected: " _format, ## __VA_ARGS__); \ } while (0) -#define MAX_SEQ_TRAIN_FAILURES 2 +/* + * enum intel_dp_link_recovery_state - LT recovery state + * @INTEL_DP_LINK_RECOVERY_IDLE: + * No link training failure is currently tracked and no recovery is + * in progress. This is the initial state after driver initialization, + * power state transitions, sink (re-)connection, or after a successful + * link training. + * + * @INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING: + * A first link training failure has been observed and an automatic + * retraining attempt with the same link parameters is pending. Exactly + * one such attempt is allowed before switching to userspace-driven + * recovery. + * + * @INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED: + * Automatic retraining is no longer possible. At this point, a + * fallback selection is made and userspace is notified to take over + * recovery, performing modesets with parameters it determines are + * required. The driver then selects a link configuration from the + * remaining fallback configuration set. Subsequent link training + * failures trigger further fallback selections and userspace + * notifications. + * + * @INTEL_DP_LINK_RECOVERY_NO_FALLBACK: + * Fallback selection is no longer possible, as no usable fallback link + * configurations remain. Recovery must proceed via userspace modesets + * using the remaining allowed link configuration. Userspace continues + * to be notified of subsequent link training failures. + * + * Describes the link recovery state used by the Intel DP link recovery + * logic. + * + * See also: + * - DOC: DisplayPort link training + * - link_recovery_autoretrain_pending() + * - link_recovery_autoretrain_allowed() + * - link_recovery_has_no_fallback() + * - link_recovery_mark_train_failure() + * - link_recovery_mark_autoretrain_modeset_failure() + * - link_recovery_mark_no_fallback() + * - link_recovery_reset() + */ +enum intel_dp_link_recovery_state { + /* + * Keep the enum values ordered from least to most severe + * recovery state; helper logic relies on that ordering. + */ + INTEL_DP_LINK_RECOVERY_IDLE, + INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING, + INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED, + INTEL_DP_LINK_RECOVERY_NO_FALLBACK, +}; + +struct intel_dp_link_training { + struct intel_dp *dp; + + enum intel_dp_link_recovery_state recovery_state; + + int force_train_failure; + bool force_retrain; +}; + +static struct intel_dp_link_training *connector_to_link_training(struct intel_connector *connector) +{ + return intel_attached_dp(connector)->link.training; +} static void intel_dp_reset_lttpr_common_caps(struct intel_dp *intel_dp) { @@ -245,12 +636,12 @@ int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_S * transparent mode link training mode. * * Returns: - * >0 if LTTPRs were detected and the non-transparent LT mode was set. The + * - >0 if LTTPRs were detected and the non-transparent LT mode was + * set. The DPRX capabilities are read out. + * - 0 if no LTTPRs or more than 8 LTTPRs were detected or in case of + * a detection failure and the transparent LT mode was set. The * DPRX capabilities are read out. - * 0 if no LTTPRs or more than 8 LTTPRs were detected or in case of a - * detection failure and the transparent LT mode was set. The DPRX - * capabilities are read out. - * <0 Reading out the DPRX capabilities failed. + * - <0 Reading out the DPRX capabilities failed. */ int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp) { @@ -1247,6 +1638,124 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp, return sink_status & DP_INTRA_HOP_AUX_REPLY_INDICATION ? 1 : 0; } +static bool +link_recovery_autoretrain_pending(struct intel_dp_link_training *link_training) +{ + return link_training->recovery_state == INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING; +} + +/* + * Automatic retraining is a driver-driven link recovery mechanism that + * retrains the link with the current userspace provided modeset + * configuration and link parameters. + * + * Autoretrain is allowed while the link configurations available for + * retraining, i.e. those not disabled yet via fallback selection, still + * make it possible to retrain the link for the current userspace provided + * modeset configuration. + * + * Once automatic retraining is no longer allowed, userspace driven link + * recovery via userspace notifications and userspace modesets takes over. + * + * See also: + * - DOC: DisplayPort link training + */ +static bool +link_recovery_autoretrain_allowed(struct intel_dp_link_training *link_training) +{ + switch (link_training->recovery_state) { + case INTEL_DP_LINK_RECOVERY_IDLE: + case INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING: + return true; + default: + return false; + } +} + +static bool +link_recovery_has_no_fallback(struct intel_dp_link_training *link_training) +{ + return link_training->recovery_state == INTEL_DP_LINK_RECOVERY_NO_FALLBACK; +} + +/* + * Record a link training failure and advance the recovery state to + * indicate the next required recovery step. + * + * The caller must proceed with recovery as instructed by the return + * value, either via automatic retraining or, once automatic retraining + * is no longer possible, via userspace modesets after fallback + * selection. + * + * Note that the error reported via this function is the error seen by + * the link training failure handler proper after an actual link + * training failure indicated by the sink device, and so the error and + * corresponding actions required are distinct from an autoretrain + * modeset failure. See link_recovery_mark_autoretrain_modeset_failure() to + * report a modeset failure. + * + * See also: + * - DOC: DisplayPort link training + */ +static bool +link_recovery_mark_train_failure(struct intel_dp_link_training *link_training) +{ + switch (link_training->recovery_state) { + case INTEL_DP_LINK_RECOVERY_IDLE: + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING; + break; + case INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING: + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED; + break; + default: + break; + } + + return link_recovery_autoretrain_allowed(link_training); +} + +/* + * Record a failure of the autoretrain modeset before link training + * itself could run. + * + * Note that the error reported via this function and the corresponding + * expected actions are distinct from an actual link training failure: + * the modeset failed before a link training attempt could be performed. + * See link_recovery_mark_train_failure() to report an actual link + * training failure. + * + * Update the state to indicate that further recovery is to be delegated to + * userspace via a regular modeset. + * + * See also: + * - DOC: DisplayPort link training + */ +static void +link_recovery_mark_autoretrain_modeset_failure(struct intel_dp_link_training *link_training) +{ + if (link_recovery_autoretrain_allowed(link_training)) + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED; +} + +/* Record that no more link fallback configuration is available. */ +static void +link_recovery_mark_no_fallback(struct intel_dp_link_training *link_training) +{ + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_NO_FALLBACK; +} + +/** + * link_recovery_reset - reset the link recovery state + * @link_training: link training state + * + * Reset the link recovery state to indicate that no link recovery is + * required. + */ +static void link_recovery_reset(struct intel_dp_link_training *link_training) +{ + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_IDLE; +} + /** * intel_dp_stop_link_train - stop link training * @intel_dp: DP struct @@ -1266,6 +1775,7 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp, void intel_dp_stop_link_train(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { + struct intel_dp_link_training *link_training = intel_dp->link.training; struct intel_display *display = to_intel_display(intel_dp); struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; int ret; @@ -1286,8 +1796,8 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp, intel_hpd_unblock(encoder); if (!display->hotplug.ignore_long_hpd && - intel_dp->link.seq_train_failures < MAX_SEQ_TRAIN_FAILURES) { - int delay_ms = intel_dp->link.seq_train_failures ? 0 : 2000; + link_recovery_autoretrain_allowed(link_training)) { + int delay_ms = link_recovery_autoretrain_pending(link_training) ? 0 : 2000; intel_encoder_link_check_queue_work(encoder, delay_ms); } @@ -1340,18 +1850,23 @@ static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, int *new_link_rate, int *new_lane_count) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; int link_rate; int lane_count; int i; - i = intel_dp_link_config_index(intel_dp, crtc_state->port_clock, crtc_state->lane_count); + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + + i = intel_dp_link_config_index(intel_dp->link.caps, + crtc_state->port_clock, crtc_state->lane_count); for (i--; i >= 0; i--) { - intel_dp_link_config_get(intel_dp, i, &link_rate, &lane_count); + intel_dp_link_config_get(intel_dp->link.caps, i, &link_rate, &lane_count); - if ((intel_dp->link.force_rate && - intel_dp->link.force_rate != link_rate) || - (intel_dp->link.force_lane_count && - intel_dp->link.force_lane_count != lane_count)) + if ((forced_params.rate && + forced_params.rate != link_rate) || + (forced_params.lane_count && + forced_params.lane_count != lane_count)) continue; break; @@ -1368,20 +1883,22 @@ static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp, static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; int rate_index; int new_rate; - if (intel_dp->link.force_rate) + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + if (forced_params.rate) return -1; - rate_index = intel_dp_rate_index(intel_dp->common_rates, - intel_dp->num_common_rates, - current_rate); + rate_index = intel_dp_link_caps_common_rate_idx(link_caps, + current_rate); if (rate_index <= 0) return -1; - new_rate = intel_dp_common_rate(intel_dp, rate_index - 1); + new_rate = intel_dp_common_rate(link_caps, rate_index - 1); /* TODO: Make switching from UHBR to non-UHBR rates work. */ if (drm_dp_is_uhbr_rate(current_rate) != drm_dp_is_uhbr_rate(new_rate)) @@ -1392,7 +1909,10 @@ static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) static int reduce_lane_count(struct intel_dp *intel_dp, int current_lane_count) { - if (intel_dp->link.force_lane_count) + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); + if (forced_params.lane_count) return -1; if (current_lane_count == 1) @@ -1405,6 +1925,7 @@ static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, int *new_link_rate, int *new_lane_count) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int link_rate; int lane_count; @@ -1412,7 +1933,7 @@ static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp, link_rate = reduce_link_rate(intel_dp, crtc_state->port_clock); if (link_rate < 0) { lane_count = reduce_lane_count(intel_dp, crtc_state->lane_count); - link_rate = intel_dp_max_common_rate(intel_dp); + link_rate = intel_dp_max_common_rate(link_caps); } if (lane_count < 0) @@ -1439,6 +1960,8 @@ static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crt static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_limits; int new_link_rate; int new_lane_count; @@ -1464,8 +1987,11 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, crtc_state->lane_count, crtc_state->port_clock, new_lane_count, new_link_rate); - intel_dp->link.max_rate = new_link_rate; - intel_dp->link.max_lane_count = new_lane_count; + max_link_limits.rate = new_link_rate; + max_link_limits.lane_count = new_lane_count; + + /* TODO: handle an update failure */ + intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits); return 0; } @@ -1780,6 +2306,9 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, struct intel_display *display = to_intel_display(state); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct intel_encoder *encoder = &dig_port->base; + struct intel_dp_link_training *link_training = + intel_dp->link.training; + bool autoretrain_allowed; bool passed; /* * Reinit the LTTPRs here to ensure that they are switched to @@ -1803,15 +2332,15 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, else passed = intel_dp_link_train_all_phys(intel_dp, crtc_state, lttpr_count); - if (intel_dp->link.force_train_failure) { - intel_dp->link.force_train_failure--; + if (link_training->force_train_failure) { + link_training->force_train_failure--; lt_dbg(intel_dp, DP_PHY_DPRX, "Forcing link training failure\n"); } else if (passed) { - intel_dp->link.seq_train_failures = 0; + link_recovery_reset(link_training); return; } - intel_dp->link.seq_train_failures++; + autoretrain_allowed = link_recovery_mark_train_failure(link_training); /* * Ignore the link failure in CI @@ -1830,13 +2359,13 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, return; } - if (intel_dp->link.seq_train_failures < MAX_SEQ_TRAIN_FAILURES) + if (autoretrain_allowed) return; if (intel_dp_schedule_fallback_link_training(state, intel_dp, crtc_state)) return; - intel_dp->link.retrain_disabled = true; + link_recovery_mark_no_fallback(link_training); if (!passed) lt_err(intel_dp, DP_PHY_DPRX, "Can't reduce link training parameters after failure\n"); @@ -1864,256 +2393,307 @@ void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, lt_dbg(intel_dp, DP_PHY_DPRX, "DP2.0 SDP CRC16 for 128b/132b enabled\n"); } -static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) +bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, + u8 lane_count) { - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int current_rate = -1; - int force_rate; - int err; - int i; - - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; - - if (intel_dp->link.active) - current_rate = intel_dp->link_rate; - force_rate = intel_dp->link.force_rate; - - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + struct intel_dp_link_config max_link_limits; - seq_printf(m, "%sauto%s", - force_rate == 0 ? "[" : "", - force_rate == 0 ? "]" : ""); + /* + * FIXME: we need to synchronize the current link parameters with + * hardware readout. Currently fast link training doesn't work on + * boot-up. + * + * NOTE: + * This may be called from both serialized (locked and synced against + * async commit tails) and unserialized (e.g. HPD IRQ) contexts. It + * uses the current max link limits as upper bounds to reject + * obviously bogus values, even if those bounds may be observed in a + * transient or slightly stale state. + * + * This is not a full validation of the link configuration. Even in + * serialized contexts, additional constraints (e.g. source limitations, + * bandwidth checks, and other atomic state dependencies) are only + * verified during the atomic check of the subsequent commit. + * + * max_link_limits only provides independent upper bounds for rate and + * lane count. Callers must not assume it is itself an allowed link + * configuration. Although that happens to be true for now, it will + * stop being guaranteed once fallback depends only on disabled configs. + */ + intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); - for (i = 0; i < intel_dp->num_source_rates; i++) - seq_printf(m, " %s%d%s%s", - intel_dp->source_rates[i] == force_rate ? "[" : "", - intel_dp->source_rates[i], - intel_dp->source_rates[i] == current_rate ? "*" : "", - intel_dp->source_rates[i] == force_rate ? "]" : ""); + if (link_rate == 0 || + link_rate > max_link_limits.rate) + return false; - seq_putc(m, '\n'); + if (lane_count == 0 || + lane_count > max_link_limits.lane_count) + return false; - return 0; + return true; } -static int parse_link_rate(struct intel_dp *intel_dp, const char __user *ubuf, size_t len) +static bool intel_dp_link_ok(struct intel_dp *intel_dp, + u8 link_status[DP_LINK_STATUS_SIZE]) { - char *kbuf; - const char *p; - int rate; - int ret = 0; - - kbuf = memdup_user_nul(ubuf, len); - if (IS_ERR(kbuf)) - return PTR_ERR(kbuf); + struct intel_display *display = to_intel_display(intel_dp); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + bool uhbr = intel_dp->link_rate >= 1000000; + bool ok; - p = strim(kbuf); + if (uhbr) + ok = drm_dp_128b132b_lane_channel_eq_done(link_status, + intel_dp->lane_count); + else + ok = drm_dp_channel_eq_ok(link_status, intel_dp->lane_count); - if (!strcmp(p, "auto")) { - rate = 0; - } else { - ret = kstrtoint(p, 0, &rate); - if (ret < 0) - goto out_free; - - if (intel_dp_rate_index(intel_dp->source_rates, - intel_dp->num_source_rates, - rate) < 0) - ret = -EINVAL; - } + if (ok) + return true; -out_free: - kfree(kbuf); + intel_dp_dump_link_status(intel_dp, DP_PHY_DPRX, link_status); + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] %s link not ok, retraining\n", + encoder->base.base.id, encoder->base.name, + uhbr ? "128b/132b" : "8b/10b"); - return ret < 0 ? ret : rate; + return false; } -static ssize_t i915_dp_force_link_rate_write(struct file *file, - const char __user *ubuf, - size_t len, loff_t *offp) +static int +intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATUS_SIZE]) { - struct seq_file *m = file->private_data; - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int rate; int err; - rate = parse_link_rate(intel_dp, ubuf, len); - if (rate < 0) - return rate; + memset(link_status, 0, DP_LINK_STATUS_SIZE); + + if (intel_dp_mst_active_streams(intel_dp) > 0) + err = drm_dp_dpcd_read_data(&intel_dp->aux, DP_LANE0_1_STATUS_ESI, + link_status, DP_LINK_STATUS_SIZE - 2); + else + err = drm_dp_dpcd_read_phy_link_status(&intel_dp->aux, DP_PHY_DPRX, + link_status); - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - intel_dp_reset_link_params(intel_dp); - intel_dp->link.force_rate = rate; + if (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] & + DP_DOWNSTREAM_PORT_STATUS_CHANGED) + WRITE_ONCE(intel_dp->downstream_port_changed, true); - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + return 0; +} - *offp += len; +bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training) +{ + return link_training->force_retrain; +} - return len; +static void intel_dp_link_training_set_force_retrain(struct intel_dp_link_training *link_training, + bool forced) +{ + link_training->force_retrain = forced; } -DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate); -static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) +static bool +intel_dp_needs_link_retrain(struct intel_dp *intel_dp) { - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int current_lane_count = -1; - int force_lane_count; - int err; - int i; + struct intel_dp_link_training *link_training = intel_dp->link.training; + u8 link_status[DP_LINK_STATUS_SIZE]; - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; + if (!intel_dp->link.active) + return false; - if (intel_dp->link.active) - current_lane_count = intel_dp->lane_count; - force_lane_count = intel_dp->link.force_lane_count; + /* + * While PSR source HW is enabled, it will control main-link sending + * frames, enabling and disabling it so trying to do a retrain will fail + * as the link would or not be on or it could mix training patterns + * and frame data at the same time causing retrain to fail. + * Also when exiting PSR, HW will retrain the link anyways fixing + * any link status error. + */ + if (intel_psr_enabled(intel_dp)) + return false; - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + if (intel_dp_link_training_get_force_retrain(link_training)) + return true; - seq_printf(m, "%sauto%s", - force_lane_count == 0 ? "[" : "", - force_lane_count == 0 ? "]" : ""); + if (intel_dp_read_link_status(intel_dp, link_status) < 0) + return false; + + /* + * Validate the cached values of intel_dp->link_rate and + * intel_dp->lane_count before attempting to retrain. + * + * FIXME would be nice to user the crtc state here, but since + * we need to call this from the short HPD handler that seems + * a bit hard. + */ + if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate, + intel_dp->lane_count)) + return false; - for (i = 1; i <= 4; i <<= 1) - seq_printf(m, " %s%d%s%s", - i == force_lane_count ? "[" : "", - i, - i == current_lane_count ? "*" : "", - i == force_lane_count ? "]" : ""); + if (!link_recovery_autoretrain_allowed(link_training)) + return false; - seq_putc(m, '\n'); + if (link_recovery_autoretrain_pending(link_training)) + return true; - return 0; + /* Retrain if link not ok */ + return !intel_dp_link_ok(intel_dp, link_status) && + !intel_psr_link_ok(intel_dp); } -static int parse_lane_count(const char __user *ubuf, size_t len) +static bool intel_dp_is_connected(struct intel_dp *intel_dp) { - char *kbuf; - const char *p; - int lane_count; - int ret = 0; + struct intel_connector *connector = intel_dp->attached_connector; - kbuf = memdup_user_nul(ubuf, len); - if (IS_ERR(kbuf)) - return PTR_ERR(kbuf); - - p = strim(kbuf); + return connector->base.status == connector_status_connected || + intel_dp->is_mst; +} - if (!strcmp(p, "auto")) { - lane_count = 0; - } else { - ret = kstrtoint(p, 0, &lane_count); - if (ret < 0) - goto out_free; - - switch (lane_count) { - case 1: - case 2: - case 4: - break; - default: - ret = -EINVAL; - } - } +static void queue_modeset_retry_for_links_in_state(struct intel_atomic_state *state, + struct intel_encoder *encoder, + u8 pipe_mask) +{ + const struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; -out_free: - kfree(kbuf); + for_each_new_intel_crtc_in_state(state, crtc, crtc_state) { + if (!(BIT(crtc->pipe) & pipe_mask)) + continue; - return ret < 0 ? ret : lane_count; + intel_dp_queue_modeset_retry_for_link(state, encoder, crtc_state); + } } -static ssize_t i915_dp_force_lane_count_write(struct file *file, - const char __user *ubuf, - size_t len, loff_t *offp) +static int intel_dp_retrain_link(struct intel_encoder *encoder, + struct drm_modeset_acquire_ctx *ctx) { - struct seq_file *m = file->private_data; - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int lane_count; - int err; + struct intel_display *display = to_intel_display(encoder); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_dp_link_training *link_training = + intel_dp->link.training; + struct intel_atomic_state *state; + struct drm_atomic_commit *_state; + u8 pipe_mask; + int ret; - lane_count = parse_lane_count(ubuf, len); - if (lane_count < 0) - return lane_count; + if (!intel_dp_is_connected(intel_dp)) + return 0; - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; + ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex, + ctx); + if (ret) + return ret; - intel_dp_reset_link_params(intel_dp); - intel_dp->link.force_lane_count = lane_count; + if (!intel_dp_needs_link_retrain(intel_dp)) + return 0; - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask); + if (ret) + return ret; - *offp += len; + if (pipe_mask == 0) + return 0; - return len; -} -DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count); + if (!intel_dp_needs_link_retrain(intel_dp)) + return 0; -static int i915_dp_max_link_rate_show(void *data, u64 *val) -{ - struct intel_connector *connector = to_intel_connector(data); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int err; + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] retraining link (forced %s)\n", + encoder->base.base.id, encoder->base.name, + str_yes_no(intel_dp_link_training_get_force_retrain(link_training))); - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; + _state = drm_atomic_commit_alloc(display->drm); + if (!_state) + return -ENOMEM; - *val = intel_dp->link.max_rate; + state = to_intel_atomic_state(_state); - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + ret = intel_modeset_commit_pipes_for_atomic_state(state, pipe_mask, ctx); + if (ret == -EDEADLK) + goto out; - return 0; + intel_dp_link_training_set_force_retrain(link_training, false); + + if (ret) { + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] link retraining failed: %pe\n", + encoder->base.base.id, encoder->base.name, + ERR_PTR(ret)); + /* + * intel_dp_needs_link_retrain() only performs a coarse check of + * retrainability, so the modeset commit may still fail. Disable + * further auto-retrain attempts in that case. + * + * A sink capability change may restore the retrainable state (see + * intel_dp_update_sink_caps(), intel_dp_reset_link_params()), + * allowing retraining to be attempted again. + */ + link_recovery_mark_autoretrain_modeset_failure(link_training); + queue_modeset_retry_for_links_in_state(state, encoder, pipe_mask); + } +out: + drm_atomic_commit_put(&state->base); + + return ret; } -DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n"); -static int i915_dp_max_lane_count_show(void *data, u64 *val) +void intel_dp_link_check(struct intel_encoder *encoder) { - struct intel_connector *connector = to_intel_connector(data); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int err; + struct drm_modeset_acquire_ctx ctx; + int ret; - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; + intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret) + ret = intel_dp_retrain_link(encoder, &ctx); +} - *val = intel_dp->link.max_lane_count; +void intel_dp_check_link_state(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &dig_port->base; - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + if (!intel_dp_is_connected(intel_dp)) + return; - return 0; + /* + * NOTE: + * This may race with an ongoing modeset updating the max link limits + * and, with that, the link's retrainability, so + * intel_dp_needs_link_retrain() may observe stale state. + * + * This is harmless: stale params captured as valid may spuriously + * allow retraining here, but the decision is rechecked later in a + * properly serialized context. + * + * Conversely, stale params captured as invalid may skip retraining, + * but that can only happen before the modeset has completed its own + * link training for the new, valid configuration, after which the + * link state is rechecked. + * + * See intel_dp_link_params_valid() for capturing and validating the + * params. + */ + if (!intel_dp_needs_link_retrain(intel_dp)) + return; + + intel_encoder_link_check_queue_work(encoder, 0); } -DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n"); static int i915_dp_force_link_training_failure_show(void *data, u64 *val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - *val = intel_dp->link.force_train_failure; + intel_dp_flush_connector_commits(connector); + + *val = link_training->force_train_failure; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2124,7 +2704,7 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); int err; if (val > 2) @@ -2134,7 +2714,9 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val) if (err) return err; - intel_dp->link.force_train_failure = val; + intel_dp_flush_connector_commits(connector); + + link_training->force_train_failure = val; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2148,14 +2730,16 @@ static int i915_dp_force_link_retrain_show(void *data, u64 *val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - *val = intel_dp->link.force_retrain; + intel_dp_flush_connector_commits(connector); + + *val = intel_dp_link_training_get_force_retrain(link_training); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2166,14 +2750,17 @@ static int i915_dp_force_link_retrain_write(void *data, u64 val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); + struct intel_dp *intel_dp = link_training->dp; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - intel_dp->link.force_retrain = val; + intel_dp_flush_connector_commits(connector); + + intel_dp_link_training_set_force_retrain(link_training, val); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2189,14 +2776,17 @@ static int i915_dp_link_retrain_disabled_show(struct seq_file *m, void *data) { struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - seq_printf(m, "%s\n", str_yes_no(intel_dp->link.retrain_disabled)); + intel_dp_flush_connector_commits(connector); + + /* TODO: Expose this via a debugfs entry reflecting what the state represents. */ + seq_printf(m, "%s\n", str_yes_no(link_recovery_has_no_fallback(link_training))); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2212,18 +2802,6 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) return; - debugfs_create_file("i915_dp_force_link_rate", 0644, root, - connector, &i915_dp_force_link_rate_fops); - - debugfs_create_file("i915_dp_force_lane_count", 0644, root, - connector, &i915_dp_force_lane_count_fops); - - debugfs_create_file("i915_dp_max_link_rate", 0444, root, - connector, &i915_dp_max_link_rate_fops); - - debugfs_create_file("i915_dp_max_lane_count", 0444, root, - connector, &i915_dp_max_lane_count_fops); - debugfs_create_file("i915_dp_force_link_training_failure", 0644, root, connector, &i915_dp_force_link_training_failure_fops); @@ -2233,3 +2811,26 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) debugfs_create_file("i915_dp_link_retrain_disabled", 0444, root, connector, &i915_dp_link_retrain_disabled_fops); } + +void intel_dp_link_training_reset(struct intel_dp_link_training *link_training) +{ + link_recovery_reset(link_training); +} + +struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp) +{ + struct intel_dp_link_training *link_training; + + link_training = kzalloc_obj(*link_training); + if (!link_training) + return NULL; + + link_training->dp = intel_dp; + + return link_training; +} + +void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training) +{ + kfree(link_training); +} diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h index 18c34c1a472f..ef16fcabd6da 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -12,6 +12,8 @@ struct intel_atomic_state; struct intel_connector; struct intel_crtc_state; struct intel_dp; +struct intel_dp_link_training; +struct intel_encoder; int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_SIZE]); int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp); @@ -54,6 +56,19 @@ static inline u8 intel_dp_training_pattern_symbol(u8 pattern) void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); +bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, + u8 lane_count); + +bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training); + +void intel_dp_link_check(struct intel_encoder *encoder); +void intel_dp_check_link_state(struct intel_dp *intel_dp); + void intel_dp_link_training_debugfs_add(struct intel_connector *connector); +void intel_dp_link_training_reset(struct intel_dp_link_training *link_training); + +struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp); +void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training); + #endif /* __INTEL_DP_LINK_TRAINING_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 0aa3e6b4c781..ecc90e8faee1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -697,12 +697,12 @@ static int mst_stream_compute_link_for_joined_pipes(struct intel_encoder *encode return 0; } -static int mst_stream_compute_config(struct intel_encoder *encoder, +static int mst_stream_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state); struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); struct intel_dp *intel_dp = to_primary_dp(encoder); struct intel_connector *connector = @@ -925,11 +925,11 @@ int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state, return 0; } -static int mst_stream_compute_config_late(struct intel_encoder *encoder, +static int mst_stream_compute_config_late(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state); struct intel_dp *intel_dp = to_primary_dp(encoder); /* lowest numbered transcoder will be designated master */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_test.c b/drivers/gpu/drm/i915/display/intel_dp_test.c index ba44769c9cfb..da7632536dac 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_test.c +++ b/drivers/gpu/drm/i915/display/intel_dp_test.c @@ -14,6 +14,7 @@ #include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -32,6 +33,7 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp, struct intel_crtc_state *pipe_config, struct link_config_limits *limits) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_display *display = to_intel_display(intel_dp); /* For DP Compliance we override the computed bpp for the pipe */ @@ -54,9 +56,8 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp, */ if (intel_dp_link_params_valid(intel_dp, intel_dp->compliance.test_link_rate, intel_dp->compliance.test_lane_count)) { - index = intel_dp_rate_index(intel_dp->common_rates, - intel_dp->num_common_rates, - intel_dp->compliance.test_link_rate); + index = intel_dp_link_caps_common_rate_idx(link_caps, + intel_dp->compliance.test_link_rate); if (index >= 0) { limits->min_rate = intel_dp->compliance.test_link_rate; limits->max_rate = intel_dp->compliance.test_link_rate; diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c index d6bd1f7e01e1..76e9753766b9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -11,6 +11,7 @@ #include "intel_display_limits.h" #include "intel_display_types.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_tunnel.h" @@ -56,8 +57,9 @@ static int kbytes_to_mbits(int kbytes) static int get_current_link_bw(struct intel_dp *intel_dp) { - int rate = intel_dp_max_common_rate(intel_dp); - int lane_count = intel_dp_max_common_lane_count(intel_dp); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int rate = intel_dp_max_common_rate(link_caps); + int lane_count = intel_dp_link_caps_max_common_lane_count(link_caps); return intel_dp_max_link_data_rate(intel_dp, rate, lane_count); } diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c index fec8a56e21ea..d9a270362a82 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.c +++ b/drivers/gpu/drm/i915/display/intel_dsb.c @@ -210,6 +210,18 @@ static int dsb_scanline_to_hw(struct intel_atomic_state *state, return (scanline + vtotal - intel_crtc_scanline_offset(crtc_state)) % vtotal; } +static +bool pre_commit_use_safe_window(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + struct intel_display *display = to_intel_display(crtc->base.dev); + + if (intel_vrr_always_use_vrr_tg(display)) + return true; + + return pre_commit_is_vrr_active(state, crtc); +} + /* * Bspec suggests that we should always set DSB_SKIP_WAITS_EN. We have approach * different from what is explained in Bspec on how flip is considered being @@ -229,7 +241,7 @@ static u32 dsb_chicken(struct intel_atomic_state *state, u32 chicken = intel_psr_use_trans_push(new_crtc_state) ? DSB_SKIP_WAITS_EN : 0; - if (pre_commit_is_vrr_active(state, crtc)) + if (pre_commit_use_safe_window(state, crtc)) chicken |= DSB_CTRL_WAIT_SAFE_WINDOW | DSB_CTRL_NO_WAIT_VBLANK | DSB_INST_WAIT_SAFE_WINDOW | @@ -798,6 +810,12 @@ void intel_dsb_vblank_evade(struct intel_atomic_state *state, end = intel_vrr_vmax_vblank_start(crtc_state); start = end - vblank_delay - latency; intel_dsb_wait_scanline_out(state, dsb, start, end); + } else if (pre_commit_use_safe_window(state, crtc)) { + int vblank_delay = crtc_state->set_context_latency; + + end = intel_mode_vblank_start(&crtc_state->hw.adjusted_mode); + start = end - vblank_delay - latency; + intel_dsb_wait_scanline_out(state, dsb, start, end); } else { int vblank_delay = intel_mode_vblank_delay(&crtc_state->hw.adjusted_mode); @@ -891,7 +909,7 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state, &crtc_state->hw.adjusted_mode; int wait_scanlines; - if (pre_commit_is_vrr_active(state, crtc)) { + if (pre_commit_use_safe_window(state, crtc)) { /* * If the push happened before the vmin decision boundary * we don't know how far we are from the undelayed vblank. @@ -902,9 +920,17 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state, * the hardware itself guarantees that we're SCL lines * away from the delayed vblank, and we won't be inside * the vmin safe window so this extra wait does nothing. + * + * Experimentally, DSB may observe a slightly stale + * PIPEDSL value. When the actual scanline has just reached + * safe_window_start, WAIT_DSL_OUT may complete immediately + * due to the stale value. + * + * Shift the start back by one scanline to ensure the wait + * window is entered reliably. */ intel_dsb_wait_scanline_out(state, dsb, - intel_vrr_safe_window_start(crtc_state), + intel_vrr_safe_window_start(crtc_state) - 1, intel_vrr_vmin_safe_window_end(crtc_state)); /* * When the push is sent during vblank it will trigger diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c index dd1a995c2979..f157699a7c4c 100644 --- a/drivers/gpu/drm/i915/display/intel_dvo.c +++ b/drivers/gpu/drm/i915/display/intel_dvo.c @@ -242,7 +242,8 @@ intel_dvo_mode_valid(struct drm_connector *_connector, return intel_dvo->dev.dev_ops->mode_valid(&intel_dvo->dev, mode); } -static int intel_dvo_compute_config(struct intel_encoder *encoder, +static int intel_dvo_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -256,7 +257,7 @@ static int intel_dvo_compute_config(struct intel_encoder *encoder, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - ret = intel_panel_compute_config(connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index 049157c41fe2..60a70dea5d85 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -54,21 +54,21 @@ struct intel_gmbus { }; enum gmbus_gpio { - GPIOA, - GPIOB, - GPIOC, - GPIOD, - GPIOE, - GPIOF, - GPIOG, - GPIOH, - __GPIOI_UNUSED, - GPIOJ, - GPIOK, - GPIOL, - GPIOM, - GPION, - GPIOO, + GPIO_0, + GPIO_1, + GPIO_2, + GPIO_3, + GPIO_4, + GPIO_5, + GPIO_6, + GPIO_7, + GPIO_8, + GPIO_9, + GPIO_10, + GPIO_11, + GPIO_12, + GPIO_13, + GPIO_14, }; struct gmbus_pin { @@ -78,77 +78,82 @@ struct gmbus_pin { /* Map gmbus pin pairs to names and registers. */ static const struct gmbus_pin gmbus_pins[] = { - [GMBUS_PIN_SSC] = { "ssc", GPIOB }, - [GMBUS_PIN_VGADDC] = { "vga", GPIOA }, - [GMBUS_PIN_PANEL] = { "panel", GPIOC }, - [GMBUS_PIN_DPC] = { "dpc", GPIOD }, - [GMBUS_PIN_DPB] = { "dpb", GPIOE }, - [GMBUS_PIN_DPD] = { "dpd", GPIOF }, + [GMBUS_PIN_SSC] = { "ssc", GPIO_1 }, + [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 }, + [GMBUS_PIN_PANEL] = { "panel", GPIO_2 }, + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, + [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; -static const struct gmbus_pin gmbus_pins_bdw[] = { - [GMBUS_PIN_VGADDC] = { "vga", GPIOA }, - [GMBUS_PIN_DPC] = { "dpc", GPIOD }, - [GMBUS_PIN_DPB] = { "dpb", GPIOE }, - [GMBUS_PIN_DPD] = { "dpd", GPIOF }, +static const struct gmbus_pin gmbus_pins_lpt_h[] = { + [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 }, + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, + [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; -static const struct gmbus_pin gmbus_pins_skl[] = { - [GMBUS_PIN_DPC] = { "dpc", GPIOD }, - [GMBUS_PIN_DPB] = { "dpb", GPIOE }, - [GMBUS_PIN_DPD] = { "dpd", GPIOF }, +static const struct gmbus_pin gmbus_pins_lpt_lp[] = { + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, +}; + +static const struct gmbus_pin gmbus_pins_spt[] = { + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, + [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; static const struct gmbus_pin gmbus_pins_bxt[] = { - [GMBUS_PIN_1_BXT] = { "dpb", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpc", GPIOC }, - [GMBUS_PIN_3_BXT] = { "misc", GPIOD }, + [GMBUS_PIN_1] = { "dpb", GPIO_1 }, + [GMBUS_PIN_2] = { "dpc", GPIO_2 }, + [GMBUS_PIN_3] = { "misc", GPIO_3 }, }; static const struct gmbus_pin gmbus_pins_cnp[] = { - [GMBUS_PIN_1_BXT] = { "dpb", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpc", GPIOC }, - [GMBUS_PIN_3_BXT] = { "misc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, + [GMBUS_PIN_1] = { "dpb", GPIO_1 }, + [GMBUS_PIN_2] = { "dpc", GPIO_2 }, + [GMBUS_PIN_3] = { "misc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, }; static const struct gmbus_pin gmbus_pins_icp[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, - [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK }, - [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL }, - [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM }, - [GMBUS_PIN_13_TC5_TGP] = { "tc5", GPION }, - [GMBUS_PIN_14_TC6_TGP] = { "tc6", GPIOO }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 }, + [GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 }, + [GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 }, + [GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 }, + [GMBUS_PIN_13_TC5] = { "tc5", GPIO_13 }, + [GMBUS_PIN_14_TC6] = { "tc6", GPIO_14 }, }; static const struct gmbus_pin gmbus_pins_dg1[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, }; static const struct gmbus_pin gmbus_pins_dg2[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, + [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 }, }; static const struct gmbus_pin gmbus_pins_mtp[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, - [GMBUS_PIN_5_MTP] = { "dpe", GPIOF }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, - [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK }, - [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL }, - [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, + [GMBUS_PIN_5] = { "dpe", GPIO_5 }, + [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 }, + [GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 }, + [GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 }, + [GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 }, }; static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display, @@ -175,12 +180,15 @@ static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display, } else if (display->platform.geminilake || display->platform.broxton) { pins = gmbus_pins_bxt; size = ARRAY_SIZE(gmbus_pins_bxt); - } else if (DISPLAY_VER(display) == 9) { - pins = gmbus_pins_skl; - size = ARRAY_SIZE(gmbus_pins_skl); - } else if (display->platform.broadwell) { - pins = gmbus_pins_bdw; - size = ARRAY_SIZE(gmbus_pins_bdw); + } else if (HAS_PCH_SPT(display)) { + pins = gmbus_pins_spt; + size = ARRAY_SIZE(gmbus_pins_spt); + } else if (HAS_PCH_LPT_LP(display)) { + pins = gmbus_pins_lpt_lp; + size = ARRAY_SIZE(gmbus_pins_lpt_lp); + } else if (HAS_PCH_LPT(display)) { + pins = gmbus_pins_lpt_h; + size = ARRAY_SIZE(gmbus_pins_lpt_h); } else { pins = gmbus_pins; size = ARRAY_SIZE(gmbus_pins); diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.h b/drivers/gpu/drm/i915/display/intel_gmbus.h index 35a200a9efc0..5fdeab1aa794 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.h +++ b/drivers/gpu/drm/i915/display/intel_gmbus.h @@ -20,17 +20,17 @@ struct intel_display; #define GMBUS_PIN_DPB 5 /* SDVO, HDMIB */ #define GMBUS_PIN_DPD 6 /* HDMID */ #define GMBUS_PIN_RESERVED 7 /* 7 reserved */ -#define GMBUS_PIN_1_BXT 1 /* BXT+ (atom) and CNP+ (big core) */ -#define GMBUS_PIN_2_BXT 2 -#define GMBUS_PIN_3_BXT 3 -#define GMBUS_PIN_4_CNP 4 -#define GMBUS_PIN_5_MTP 5 -#define GMBUS_PIN_9_TC1_ICP 9 -#define GMBUS_PIN_10_TC2_ICP 10 -#define GMBUS_PIN_11_TC3_ICP 11 -#define GMBUS_PIN_12_TC4_ICP 12 -#define GMBUS_PIN_13_TC5_TGP 13 -#define GMBUS_PIN_14_TC6_TGP 14 +#define GMBUS_PIN_1 1 /* BXT+ (atom) and CNP+ (big core) */ +#define GMBUS_PIN_2 2 +#define GMBUS_PIN_3 3 +#define GMBUS_PIN_4 4 +#define GMBUS_PIN_5 5 +#define GMBUS_PIN_9_TC1 9 /* ICP+ */ +#define GMBUS_PIN_10_TC2 10 +#define GMBUS_PIN_11_TC3 11 +#define GMBUS_PIN_12_TC4 12 +#define GMBUS_PIN_13_TC5 13 +#define GMBUS_PIN_14_TC6 14 #define GMBUS_NUM_PINS 15 /* including 0 */ diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index e88fec24af49..0a076d2ed70a 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -145,6 +145,9 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state, if (!new_conn_state || !new_conn_state->crtc) continue; + if (drm_WARN_ON(display->drm, data->k >= INTEL_NUM_PIPES(display))) + return -EINVAL; + data->streams[data->k].stream_id = intel_conn_to_vcpi(state, connector); data->k++; @@ -155,7 +158,7 @@ intel_hdcp_required_content_stream(struct intel_atomic_state *state, } drm_connector_list_iter_end(&conn_iter); - if (drm_WARN_ON(display->drm, data->k > INTEL_NUM_PIPES(display) || data->k == 0)) + if (drm_WARN_ON(display->drm, !data->k)) return -EINVAL; /* @@ -1798,9 +1801,10 @@ int hdcp2_authenticate_repeater_topology(struct intel_connector *connector) return -EINVAL; } - if (seq_num_v < hdcp->seq_num_v) { - /* Roll over of the seq_num_v from repeater. Reauthenticate. */ - drm_dbg_kms(display->drm, "Seq_num_v roll over.\n"); + if (hdcp->hdcp2_encrypted && seq_num_v <= hdcp->seq_num_v) { + /* Reauthenticate on Seq_num_v repeat or rollover */ + drm_dbg_kms(display->drm, "Seq_num_v %s\n", + seq_num_v == hdcp->seq_num_v ? "repeat" : "rollover"); return -EINVAL; } diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 9076c2b176ec..8a019d3574df 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -71,7 +71,7 @@ bool intel_hdmi_is_frl(u32 clock) { - u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 }; + static const u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 }; int i; for (i = 0; i < ARRAY_SIZE(rates); i++) @@ -2729,6 +2729,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_ } /* + * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after + * enabling Scrambling_Enable and starting scrambled video + * transmission, poll Scrambling_Status for up to 200 ms. + */ +void +intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector) +{ + struct intel_connector *connector = to_intel_connector(_connector); + struct intel_display *display = to_intel_display(crtc_state); + bool scrambling_enabled = false; + int ret; + + if (!crtc_state->hdmi_scrambling) + return; + + /* Poll for a max of 200 msec as per HDMI spec */ + ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base), + scrambling_enabled, 1000, 200 * 1000, false); + if (ret) + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n", + connector->base.base.id, connector->base.name); +} + +/* * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup * @encoder: intel_encoder * @connector: drm_connector @@ -2799,14 +2825,14 @@ static u8 bxt_encoder_to_ddc_pin(struct intel_encoder *encoder) switch (port) { case PORT_B: - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; case PORT_C: - ddc_pin = GMBUS_PIN_2_BXT; + ddc_pin = GMBUS_PIN_2; break; default: MISSING_CASE(port); - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; } return ddc_pin; @@ -2819,20 +2845,17 @@ static u8 cnp_encoder_to_ddc_pin(struct intel_encoder *encoder) switch (port) { case PORT_B: - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; case PORT_C: - ddc_pin = GMBUS_PIN_2_BXT; + ddc_pin = GMBUS_PIN_2; break; case PORT_D: - ddc_pin = GMBUS_PIN_4_CNP; - break; - case PORT_F: - ddc_pin = GMBUS_PIN_3_BXT; + ddc_pin = GMBUS_PIN_4; break; default: MISSING_CASE(port); - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; } return ddc_pin; @@ -2844,12 +2867,12 @@ static u8 icl_encoder_to_ddc_pin(struct intel_encoder *encoder) enum port port = encoder->port; if (intel_encoder_is_combo(encoder)) - return GMBUS_PIN_1_BXT + port; + return GMBUS_PIN_1 + port; else if (intel_encoder_is_tc(encoder)) - return GMBUS_PIN_9_TC1_ICP + intel_encoder_to_tc(encoder); + return GMBUS_PIN_9_TC1 + intel_encoder_to_tc(encoder); drm_WARN(display->drm, 1, "Unknown port:%c\n", port_name(port)); - return GMBUS_PIN_2_BXT; + return GMBUS_PIN_2; } static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder) @@ -2859,17 +2882,17 @@ static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder) switch (phy) { case PHY_A: - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; case PHY_B: - ddc_pin = GMBUS_PIN_2_BXT; + ddc_pin = GMBUS_PIN_2; break; case PHY_C: - ddc_pin = GMBUS_PIN_9_TC1_ICP; + ddc_pin = GMBUS_PIN_9_TC1; break; default: MISSING_CASE(phy); - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; } return ddc_pin; @@ -2889,9 +2912,9 @@ static u8 rkl_encoder_to_ddc_pin(struct intel_encoder *encoder) * all outputs. */ if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C) - return GMBUS_PIN_9_TC1_ICP + phy - PHY_C; + return GMBUS_PIN_9_TC1 + phy - PHY_C; - return GMBUS_PIN_1_BXT + phy; + return GMBUS_PIN_1 + phy; } static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder) @@ -2908,9 +2931,9 @@ static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder) * all outputs. */ if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C) - return GMBUS_PIN_9_TC1_ICP + phy - PHY_C; + return GMBUS_PIN_9_TC1 + phy - PHY_C; - return GMBUS_PIN_1_BXT + phy; + return GMBUS_PIN_1 + phy; } static u8 dg1_encoder_to_ddc_pin(struct intel_encoder *encoder) @@ -2929,9 +2952,9 @@ static u8 adls_encoder_to_ddc_pin(struct intel_encoder *encoder) * except first combo output. */ if (phy == PHY_A) - return GMBUS_PIN_1_BXT; + return GMBUS_PIN_1; - return GMBUS_PIN_9_TC1_ICP + phy - PHY_B; + return GMBUS_PIN_9_TC1 + phy - PHY_B; } static u8 g4x_encoder_to_ddc_pin(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index be2fad57e4ad..0fa3661568e8 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, unsigned int type, void *frame, ssize_t len); +void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector); #endif /* __INTEL_HDMI_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_initial_plane.c b/drivers/gpu/drm/i915/display/intel_initial_plane.c index 6aa253678ec9..e414b5d1085c 100644 --- a/drivers/gpu/drm/i915/display/intel_initial_plane.c +++ b/drivers/gpu/drm/i915/display/intel_initial_plane.c @@ -170,7 +170,7 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc, drm_framebuffer_get(fb); plane_state->uapi.crtc = &crtc->base; - intel_plane_copy_uapi_to_hw_state(plane_state, plane_state, crtc); + intel_plane_copy_uapi_to_hw_state(NULL, plane_state, plane_state, crtc); atomic_or(plane->frontbuffer_bit, &to_intel_frontbuffer(fb)->bits); diff --git a/drivers/gpu/drm/i915/display/intel_lpe_audio.c b/drivers/gpu/drm/i915/display/intel_lpe_audio.c index 775493306a83..ff2cf479d8e1 100644 --- a/drivers/gpu/drm/i915/display/intel_lpe_audio.c +++ b/drivers/gpu/drm/i915/display/intel_lpe_audio.c @@ -262,10 +262,10 @@ void intel_lpe_audio_irq_handler(struct intel_display *display) if (!HAS_LPE_AUDIO(display)) return; - ret = generic_handle_irq(display->audio.lpe.irq); + ret = generic_handle_irq_safe(display->audio.lpe.irq); if (ret) drm_err_ratelimited(display->drm, - "error handling LPE audio irq: %d\n", ret); + "LPE audio: irq handling failed (%pe)\n", ERR_PTR(ret)); } /** diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 615ee980470e..8fc6d230493f 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1223,11 +1223,7 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder, else val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK); - /* DP2.0 10G and 20G rates enable MPLLA*/ - if (port_clock == 1000000 || port_clock == 2000000) - val |= XELPDP_SSC_ENABLE_PLLA; - else - val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; + val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLA : 0; intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port), XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE | @@ -2178,8 +2174,9 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p, { int i, j; - drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %d, tbt mode: %d\n", - hw_state->lane_count, hw_state->ssc_enabled, hw_state->tbt_mode); + drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %s, tbt mode: %s\n", + hw_state->lane_count, str_yes_no(hw_state->ssc_enabled), + str_yes_no(hw_state->tbt_mode)); for (i = 0; i < 3; i++) { drm_printf(p, "config[%d] = 0x%.4x,\n", @@ -2221,6 +2218,14 @@ static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder) XELPDP_LANE_PCLK_PLL_ACK(0); } +static bool readout_ssc_state(struct intel_encoder *encoder) +{ + struct intel_display *display = to_intel_display(encoder); + + return intel_de_read(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port)) & + XELPDP_SSC_ENABLE_PLLA; +} + bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display, struct intel_dpll *pll, struct intel_dpll_hw_state *hw_state) @@ -2250,6 +2255,7 @@ bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder); lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1; wakeref = intel_lt_phy_transaction_begin(encoder); + pll_state->ssc_enabled = readout_ssc_state(encoder); pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_LT_PHY_LANE0, INTEL_LT_PHY_LANE1); diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c index c8098104d853..872753478cf2 100644 --- a/drivers/gpu/drm/i915/display/intel_lvds.c +++ b/drivers/gpu/drm/i915/display/intel_lvds.c @@ -413,7 +413,8 @@ intel_lvds_mode_valid(struct drm_connector *_connector, return MODE_OK; } -static int intel_lvds_compute_config(struct intel_encoder *encoder, +static int intel_lvds_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -459,7 +460,7 @@ static int intel_lvds_compute_config(struct intel_encoder *encoder, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - ret = intel_panel_compute_config(connector, adjusted_mode); + ret = intel_panel_compute_config(state, crtc_state, connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 20c548eea6da..81e638d0c7b3 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -67,21 +67,62 @@ static bool is_best_fixed_mode(struct intel_connector *connector, if (!best_mode) return true; - /* - * With VRR always pick a mode with equal/higher than requested - * vrefresh, which we can then reduce to match the requested - * vrefresh by extending the vblank length. - */ - if (intel_vrr_is_in_range(connector, vrefresh) && - intel_vrr_is_in_range(connector, fixed_mode_vrefresh) && - fixed_mode_vrefresh < vrefresh) - return false; - /* pick the fixed_mode that is closest in terms of vrefresh */ return abs(fixed_mode_vrefresh - vrefresh) < abs(drm_mode_vrefresh(best_mode) - vrefresh); } +static bool is_vrr_compatible(const struct drm_display_mode *mode1, + const struct drm_display_mode *mode2) +{ + return drm_mode_match(mode1, mode2, + DRM_MODE_MATCH_CLOCK | + DRM_MODE_MATCH_TIMINGS_VRR | + DRM_MODE_MATCH_FLAGS | + DRM_MODE_MATCH_3D_FLAGS); +} + +static const struct drm_display_mode * +intel_panel_fixed_mode_vrr(struct intel_connector *connector, + const struct drm_display_mode *mode, + const struct drm_display_mode *vrr_ref_mode) +{ + const struct drm_display_mode *fixed_mode, *best_mode = NULL; + int vrefresh = drm_mode_vrefresh(mode); + + if (!intel_vrr_is_in_range(connector, vrefresh)) + return NULL; + + if (vrr_ref_mode && + !intel_vrr_is_in_range(connector, drm_mode_vrefresh(vrr_ref_mode))) + return NULL; + + list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { + int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); + + if (!intel_vrr_is_in_range(connector, fixed_mode_vrefresh)) + continue; + + /* + * With VRR always pick a mode with equal/higher than requested + * vrefresh, which we can then reduce to match the requested + * vrefresh by extending the vblank length. + */ + if (fixed_mode_vrefresh < vrefresh) + continue; + + if (vrr_ref_mode && + !is_vrr_compatible(fixed_mode, vrr_ref_mode)) + continue; + + if (is_best_fixed_mode(connector, vrefresh, + fixed_mode_vrefresh, best_mode)) + best_mode = fixed_mode; + } + + return best_mode; +} + const struct drm_display_mode * intel_panel_fixed_mode(struct intel_connector *connector, const struct drm_display_mode *mode) @@ -197,56 +238,107 @@ enum drrs_type intel_panel_drrs_type(struct intel_connector *connector) return connector->panel.vbt.drrs_type; } -int intel_panel_compute_config(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode) +static int intel_panel_compute_config_vrr(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector) { - const struct drm_display_mode *fixed_mode = - intel_panel_fixed_mode(connector, adjusted_mode); + struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + const struct drm_display_mode *fixed_mode = NULL; int vrefresh, fixed_mode_vrefresh; - bool is_vrr; + + /* + * Attempt a VRR based refresh rate change if possible + * when userspace has forbidden a full modeset. + */ + if (!state->base.allow_modeset) { + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + const struct intel_crtc_state *old_crtc_state = + intel_atomic_get_old_crtc_state(state, crtc); + + if (old_crtc_state->hw.enable && + old_crtc_state->uapi.encoder_mask == crtc_state->uapi.encoder_mask) + fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode, + &old_crtc_state->hw.adjusted_mode); + } if (!fixed_mode) + fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode, NULL); + + if (!fixed_mode) + return -EINVAL; + + vrefresh = drm_mode_vrefresh(adjusted_mode); + fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); + + drm_mode_copy(adjusted_mode, fixed_mode); + + if (fixed_mode_vrefresh != vrefresh) { + int vsync_start_offset = adjusted_mode->vtotal - adjusted_mode->vsync_start; + int vsync_end_offset = adjusted_mode->vtotal - adjusted_mode->vsync_end; + + adjusted_mode->vtotal = + DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000, + adjusted_mode->htotal * vrefresh); + + adjusted_mode->vsync_start = adjusted_mode->vtotal - vsync_start_offset; + adjusted_mode->vsync_end = adjusted_mode->vtotal - vsync_end_offset; + } + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + return 0; +} + +static int intel_panel_compute_config_fixed_rr(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector) +{ + struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + const struct drm_display_mode *fixed_mode; + int vrefresh, fixed_mode_vrefresh; + + fixed_mode = intel_panel_fixed_mode(connector, adjusted_mode); + if (!fixed_mode) return 0; vrefresh = drm_mode_vrefresh(adjusted_mode); fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); /* - * Assume that we shouldn't muck about with the - * timings if they don't land in the VRR range. + * We don't want to lie too much to the user about the refresh + * rate they're going to get. But we have to allow a bit of latitude + * for Xorg since it likes to automagically cook up modes with slightly + * off refresh rates. */ - is_vrr = intel_vrr_is_in_range(connector, vrefresh) && - intel_vrr_is_in_range(connector, fixed_mode_vrefresh); - - if (!is_vrr) { - /* - * We don't want to lie too much to the user about the refresh - * rate they're going to get. But we have to allow a bit of latitude - * for Xorg since it likes to automagically cook up modes with slightly - * off refresh rates. - */ - if (abs(vrefresh - fixed_mode_vrefresh) > 1) { - drm_dbg_kms(connector->base.dev, - "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n", - connector->base.base.id, connector->base.name, - vrefresh, fixed_mode_vrefresh); + if (abs(vrefresh - fixed_mode_vrefresh) > 1) { + drm_dbg_kms(connector->base.dev, + "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n", + connector->base.base.id, connector->base.name, + vrefresh, fixed_mode_vrefresh); - return -EINVAL; - } + return -EINVAL; } drm_mode_copy(adjusted_mode, fixed_mode); - if (is_vrr && fixed_mode_vrefresh != vrefresh) - adjusted_mode->vtotal = - DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000, - adjusted_mode->htotal * vrefresh); - drm_mode_set_crtcinfo(adjusted_mode, 0); return 0; } +int intel_panel_compute_config(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector) +{ + int ret; + + ret = intel_panel_compute_config_vrr(state, crtc_state, connector); + if (ret) + ret = intel_panel_compute_config_fixed_rr(state, crtc_state, connector); + + return ret; +} + static void intel_panel_add_edid_alt_fixed_modes(struct intel_connector *connector) { struct intel_display *display = to_intel_display(connector); diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h index 23bd227826c9..30c6078ecb1b 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.h +++ b/drivers/gpu/drm/i915/display/intel_panel.h @@ -14,6 +14,7 @@ struct drm_connector; struct drm_connector_state; struct drm_display_mode; struct drm_edid; +struct intel_atomic_state; struct intel_connector; struct intel_crtc_state; struct intel_display; @@ -45,8 +46,9 @@ enum drm_mode_status intel_panel_mode_valid(struct intel_connector *connector, const struct drm_display_mode *mode, int *target_clock); -int intel_panel_compute_config(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode); +int intel_panel_compute_config(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector); void intel_panel_add_edid_fixed_modes(struct intel_connector *connector, bool use_alt_fixed_modes); void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector); diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c index a5816561be40..a5e41ea66921 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.c +++ b/drivers/gpu/drm/i915/display/intel_parent.c @@ -251,9 +251,11 @@ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display) return display->parent->panic->alloc(); } -int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb) +int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, + struct drm_scanout_buffer *sb, struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { - return display->parent->panic->setup(panic, sb); + return display->parent->panic->setup(panic, sb, obj, tiling); } void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic) diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h index 27e35f891a6b..595d4148b8eb 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.h +++ b/drivers/gpu/drm/i915/display/intel_parent.h @@ -105,7 +105,9 @@ void intel_parent_overlay_cleanup(struct intel_display *display); /* panic */ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display); -int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb); +int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, + struct drm_scanout_buffer *sb, struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)); void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic); /* pc8 */ diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 7017ed6cb9d1..a440f92ff00c 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -408,25 +408,27 @@ intel_plane_colorop_replace_blob(struct intel_plane_state *plane_state, } static void -intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, +intel_plane_color_copy_uapi_to_hw_state(struct intel_atomic_state *state, + struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc) { struct drm_colorop *iter_colorop, *colorop; struct drm_colorop_state *new_colorop_state; - struct drm_atomic_commit *state = plane_state->uapi.state; struct intel_colorop *intel_colorop; struct drm_property_blob *blob; - struct intel_atomic_state *intel_atomic_state = to_intel_atomic_state(state); - struct intel_crtc_state *new_crtc_state = intel_atomic_state ? - intel_atomic_get_new_crtc_state(intel_atomic_state, crtc) : NULL; + struct intel_crtc_state *new_crtc_state = state ? + intel_atomic_get_new_crtc_state(state, crtc) : NULL; bool changed = false; int i = 0; + if (!state) + return; + iter_colorop = from_plane_state->uapi.color_pipeline; while (iter_colorop) { - for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { + for_each_new_colorop_in_state(&state->base, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == iter_colorop) { blob = new_colorop_state->bypass ? NULL : new_colorop_state->data; intel_colorop = to_intel_colorop(colorop); @@ -442,7 +444,8 @@ intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, new_crtc_state->plane_color_changed = true; } -void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, +void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state, + struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc) { @@ -471,19 +474,25 @@ void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, plane_state->uapi.src = drm_plane_state_src(&from_plane_state->uapi); plane_state->uapi.dst = drm_plane_state_dest(&from_plane_state->uapi); - intel_plane_color_copy_uapi_to_hw_state(plane_state, from_plane_state, crtc); + intel_plane_color_copy_uapi_to_hw_state(state, plane_state, from_plane_state, crtc); } -void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, - const struct intel_plane_state *from_plane_state) +static void intel_plane_y_copy_hw_state(struct intel_plane_state *y_plane_state, + const struct intel_plane_state *uv_plane_state) { - intel_plane_clear_hw_state(plane_state); + intel_plane_clear_hw_state(y_plane_state); - memcpy(&plane_state->hw, &from_plane_state->hw, - sizeof(plane_state->hw)); + y_plane_state->hw.crtc = uv_plane_state->hw.crtc; + y_plane_state->hw.fb = uv_plane_state->hw.fb; + if (y_plane_state->hw.fb) + drm_framebuffer_get(y_plane_state->hw.fb); - if (plane_state->hw.fb) - drm_framebuffer_get(plane_state->hw.fb); + y_plane_state->hw.alpha = uv_plane_state->hw.alpha; + y_plane_state->hw.pixel_blend_mode = uv_plane_state->hw.pixel_blend_mode; + y_plane_state->hw.rotation = uv_plane_state->hw.rotation; + y_plane_state->hw.color_encoding = uv_plane_state->hw.color_encoding; + y_plane_state->hw.color_range = uv_plane_state->hw.color_range; + y_plane_state->hw.scaling_filter = uv_plane_state->hw.scaling_filter; } static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, @@ -868,7 +877,8 @@ static int plane_atomic_check(struct intel_atomic_state *state, old_primary_crtc_plane_state, new_primary_crtc_plane_state); - intel_plane_copy_uapi_to_hw_state(new_plane_state, + intel_plane_copy_uapi_to_hw_state(state, + new_plane_state, new_primary_crtc_plane_state, crtc); @@ -1607,17 +1617,17 @@ static int intel_get_scanout_buffer(struct drm_plane *plane, if (fb == intel_fbdev_framebuffer(display->fbdev.fbdev)) { intel_fbdev_get_map(display, &sb->map[0]); } else { + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width) = NULL; int ret; /* Can't disable tiling if DPT is in use */ if (intel_fb_uses_dpt(&fb->base)) { if (fb->base.format->cpp[0] != 4) return -EOPNOTSUPP; - fb->panic_tiling = intel_get_tiling_func(fb->base.modifier); - if (!fb->panic_tiling) + tiling = intel_get_tiling_func(fb->base.modifier); + if (!tiling) return -EOPNOTSUPP; } - sb->private = fb; - ret = intel_parent_panic_setup(display, fb->panic, sb); + ret = intel_parent_panic_setup(display, fb->panic, sb, obj, tiling); if (ret) return ret; } @@ -1688,7 +1698,7 @@ static void link_nv12_planes(struct intel_crtc_state *crtc_state, crtc_state->rel_data_rate[y_plane->id] = crtc_state->rel_data_rate_y[uv_plane->id]; /* Copy parameters to Y plane */ - intel_plane_copy_hw_state(y_plane_state, uv_plane_state); + intel_plane_y_copy_hw_state(y_plane_state, uv_plane_state); y_plane_state->uapi.src = uv_plane_state->uapi.src; y_plane_state->uapi.dst = uv_plane_state->uapi.dst; diff --git a/drivers/gpu/drm/i915/display/intel_plane.h b/drivers/gpu/drm/i915/display/intel_plane.h index a6338bba72d9..31a6229aea73 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.h +++ b/drivers/gpu/drm/i915/display/intel_plane.h @@ -35,11 +35,10 @@ unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state, unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, int color_plane); -void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, +void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state, + struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc); -void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, - const struct intel_plane_state *from_plane_state); void intel_plane_async_flip(struct intel_dsb *dsb, struct intel_plane *plane, const struct intel_crtc_state *crtc_state, diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index e138982dc91f..92af21d823a3 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -88,22 +88,6 @@ * issues the self-refresh re-enable code is done from a work queue, which * must be correctly synchronized/cancelled when shutting down the pipe." * - * DC3CO (DC3 clock off) - * - * On top of PSR2, GEN12 adds a intermediate power savings state that turns - * clock off automatically during PSR2 idle state. - * The smaller overhead of DC3co entry/exit vs. the overhead of PSR2 deep sleep - * entry/exit allows the HW to enter a low-power state even when page flipping - * periodically (for instance a 30fps video playback scenario). - * - * Every time a flips occurs PSR2 will get out of deep sleep state(if it was), - * so DC3CO is enabled and tgl_dc3co_disable_work is schedule to run after 6 - * frames, if no other flip occurs and the function above is executed, DC3CO is - * disabled and PSR2 is configured to enter deep sleep, resetting again in case - * of another flip. - * Front buffer modifications do not trigger DC3CO activation on purpose as it - * would bring a lot of complexity and most of the moderns systems will only - * use page flips. */ /* @@ -1098,10 +1082,11 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) u32 psr_val = 0; u8 idle_frames; - /* Wa_16025596647 */ - if ((DISPLAY_VER(display) == 20 || - IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && - is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used) + /* DC3CO / Wa_16025596647 */ + if (intel_dp->psr.dc3co_allowed || + ((DISPLAY_VER(display) == 20 || + IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && + is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used)) idle_frames = 0; else idle_frames = psr_compute_idle_frames(intel_dp); @@ -1220,108 +1205,6 @@ static void psr2_program_idle_frames(struct intel_dp *intel_dp, EDP_PSR2_IDLE_FRAMES(idle_frames)); } -static void tgl_psr2_enable_dc3co(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - - psr2_program_idle_frames(intel_dp, 0); - intel_display_power_set_target_dc_state(display, DC_STATE_EN_DC3CO); -} - -static void tgl_psr2_disable_dc3co(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - - intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6); - psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp)); -} - -static void tgl_dc3co_disable_work(struct work_struct *work) -{ - struct intel_dp *intel_dp = - container_of(work, typeof(*intel_dp), psr.dc3co_work.work); - - mutex_lock(&intel_dp->psr.lock); - /* If delayed work is pending, it is not idle */ - if (delayed_work_pending(&intel_dp->psr.dc3co_work)) - goto unlock; - - tgl_psr2_disable_dc3co(intel_dp); -unlock: - mutex_unlock(&intel_dp->psr.lock); -} - -static void tgl_disallow_dc3co_on_psr2_exit(struct intel_dp *intel_dp) -{ - if (!intel_dp->psr.dc3co_exitline) - return; - - cancel_delayed_work(&intel_dp->psr.dc3co_work); - /* Before PSR2 exit disallow dc3co*/ - tgl_psr2_disable_dc3co(intel_dp); -} - -static bool -dc3co_is_pipe_port_compatible(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) -{ - struct intel_display *display = to_intel_display(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe; - enum port port = dig_port->base.port; - - if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14) - return pipe <= PIPE_B && port <= PORT_B; - else - return pipe == PIPE_A && port == PORT_A; -} - -static void -tgl_dc3co_exitline_compute_config(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) -{ - struct intel_display *display = to_intel_display(intel_dp); - const u32 crtc_vdisplay = crtc_state->uapi.adjusted_mode.crtc_vdisplay; - struct i915_power_domains *power_domains = &display->power.domains; - u32 exit_scanlines; - - /* - * FIXME: Due to the changed sequence of activating/deactivating DC3CO, - * disable DC3CO until the changed dc3co activating/deactivating sequence - * is applied. B.Specs:49196 - */ - return; - - /* - * DMC's DC3CO exit mechanism has an issue with Selective Fecth - * TODO: when the issue is addressed, this restriction should be removed. - */ - if (crtc_state->enable_psr2_sel_fetch) - return; - - if (!(power_domains->allowed_dc_mask & DC_STATE_EN_DC3CO)) - return; - - if (!dc3co_is_pipe_port_compatible(intel_dp, crtc_state)) - return; - - /* Wa_16011303918:adl-p */ - if (intel_display_wa(display, INTEL_DISPLAY_WA_16011303918)) - return; - - /* - * DC3CO Exit time 200us B.Spec 49196 - * PSR2 transcoder Early Exit scanlines = ROUNDUP(200 / line time) + 1 - */ - exit_scanlines = - intel_usecs_to_scanlines(&crtc_state->uapi.adjusted_mode, 200) + 1; - - if (drm_WARN_ON(display->drm, exit_scanlines > crtc_vdisplay)) - return; - - crtc_state->dc3co_exitline = crtc_vdisplay - exit_scanlines; -} - static bool intel_psr2_sel_fetch_config_valid(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state) { @@ -1522,9 +1405,6 @@ int _intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state needs_panel_replay) return 0; - if (intel_vrr_always_use_vrr_tg(display)) - return 0; - return 1; } @@ -1697,8 +1577,6 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, return false; } - tgl_dc3co_exitline_compute_config(intel_dp, crtc_state); - return true; } @@ -1893,6 +1771,50 @@ static bool intel_psr_needs_wa_18037818876(struct intel_dp *intel_dp, !crtc_state->has_sel_update); } +static void psr2_dc3co_disable_locked(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + + if (intel_dp->psr.dc3co_allowed) { + intel_dp->psr.dc3co_allowed = false; + intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6); + psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp)); + } +} + +static void psr2_dc3co_disable_work(struct work_struct *work) +{ + struct intel_dp *intel_dp = + container_of(work, typeof(*intel_dp), psr.dc3co_work.work); + + mutex_lock(&intel_dp->psr.lock); + psr2_dc3co_disable_locked(intel_dp); + mutex_unlock(&intel_dp->psr.lock); +} + +static void +psr2_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits) +{ + struct intel_display *display = to_intel_display(intel_dp); + + if (!intel_dp->psr.dc3co_allowed) + return; + + if (!intel_dp->psr.sel_update_enabled || + !intel_dp->psr.active) + return; + /* + * At every frontbuffer flush flip event modified delay of delayed work, + * when delayed work schedules that means display has been idle. + */ + if (!(frontbuffer_bits & + INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe))) + return; + + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, + intel_dp->psr.dc3co_exit_delay); +} + static void intel_psr_set_non_psr_pipes(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state) @@ -2013,12 +1935,6 @@ void intel_psr_get_config(struct intel_encoder *encoder, } pipe_config->enable_psr2_su_region_et = intel_dp->psr.su_region_et_enabled; - - if (DISPLAY_VER(display) >= 12) { - val = intel_de_read(display, - TRANS_EXITLINE(display, cpu_transcoder)); - pipe_config->dc3co_exitline = REG_FIELD_GET(EXITLINE_MASK, val); - } unlock: mutex_unlock(&intel_dp->psr.lock); } @@ -2146,16 +2062,6 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, psr_irq_control(intel_dp); - /* - * TODO: if future platforms supports DC3CO in more than one - * transcoder, EXITLINE will need to be unset when disabling PSR - */ - if (intel_dp->psr.dc3co_exitline) - intel_de_rmw(display, - TRANS_EXITLINE(display, cpu_transcoder), - EXITLINE_MASK, - intel_dp->psr.dc3co_exitline << EXITLINE_SHIFT | EXITLINE_ENABLE); - if (HAS_PSR_HW_TRACKING(display) && HAS_PSR2_SEL_FETCH(display)) intel_de_rmw(display, CHICKEN_PAR1_1, IGNORE_PSR2_HW_TRACKING, intel_dp->psr.psr2_sel_fetch_enabled ? @@ -2255,7 +2161,6 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp, /* DC5/DC6 requires at least 6 idle frames */ val = usecs_to_jiffies(intel_get_frame_time_us(crtc_state) * 6); intel_dp->psr.dc3co_exit_delay = val; - intel_dp->psr.dc3co_exitline = crtc_state->dc3co_exitline; intel_dp->psr.psr2_sel_fetch_enabled = crtc_state->enable_psr2_sel_fetch; intel_dp->psr.su_region_et_enabled = crtc_state->enable_psr2_su_region_et; intel_dp->psr.psr2_sel_fetch_cff_enabled = false; @@ -2334,8 +2239,6 @@ static void intel_psr_exit(struct intel_dp *intel_dp) intel_de_rmw(display, TRANS_DP2_CTL(intel_dp->psr.transcoder), TRANS_DP2_PANEL_REPLAY_ENABLE, 0); } else if (intel_dp->psr.sel_update_enabled) { - tgl_disallow_dc3co_on_psr2_exit(intel_dp); - val = intel_de_rmw(display, EDP_PSR2_CTL(display, cpu_transcoder), EDP_PSR2_ENABLE, 0); @@ -2358,6 +2261,27 @@ static void intel_psr_exit(struct intel_dp *intel_dp) intel_dp->psr.active = false; } +bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + enum transcoder cpu_transcoder; + bool in_deep_sleep = false; + u32 val; + + mutex_lock(&intel_dp->psr.lock); + + if (!intel_dp->psr.enabled || !intel_dp->psr.sel_update_enabled) + goto out; + + cpu_transcoder = intel_dp->psr.transcoder; + val = intel_de_read(display, EDP_PSR2_STATUS(display, cpu_transcoder)); + in_deep_sleep = (val & EDP_PSR2_STATUS_STATE_MASK) == + EDP_PSR2_STATUS_STATE_DEEP_SLEEP; +out: + mutex_unlock(&intel_dp->psr.lock); + return in_deep_sleep; +} + static void intel_psr_wait_exit_locked(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); @@ -2448,6 +2372,8 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) intel_dp->psr.psr2_sel_fetch_cff_enabled = false; intel_dp->psr.active_non_psr_pipes = 0; intel_dp->psr.pkg_c_latency_used = 0; + cancel_delayed_work(&intel_dp->psr.dc3co_work); + intel_dp->psr.dc3co_allowed = false; } /** @@ -2535,8 +2461,13 @@ void intel_psr_resume(struct intel_dp *intel_dp) goto out; } - if (--intel_dp->psr.pause_counter == 0) + if (--intel_dp->psr.pause_counter == 0) { intel_psr_activate(intel_dp); + /* re-arm cancelled dc3co work from pause */ + if (intel_dp->psr.dc3co_allowed) + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, + intel_dp->psr.dc3co_exit_delay); + } out: mutex_unlock(&psr->lock); @@ -3240,10 +3171,13 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_encoder *encoder; + bool dc3co_allowed; if (!crtc_state->has_psr) return; + dc3co_allowed = intel_display_power_dc3co_allowed(display); + verify_panel_replay_dsc_state(crtc_state); for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, @@ -3271,6 +3205,8 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, keep_disabled = true; } + intel_dp->psr.dc3co_allowed = dc3co_allowed; + if (!psr->enabled && !keep_disabled) intel_psr_enable_locked(intel_dp, crtc_state); else if (psr->enabled && !crtc_state->wm_level_disabled) @@ -3287,6 +3223,11 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, */ intel_dp->psr.busy_frontbuffer_bits = 0; + if (intel_dp->psr.dc3co_allowed) { + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, + intel_dp->psr.dc3co_exit_delay); + } + mutex_unlock(&psr->lock); } } @@ -3659,34 +3600,6 @@ void intel_psr_invalidate(struct intel_display *display, mutex_unlock(&intel_dp->psr.lock); } } -/* - * When we will be completely rely on PSR2 S/W tracking in future, - * intel_psr_flush() will invalidate and flush the PSR for ORIGIN_FLIP - * event also therefore tgl_dc3co_flush_locked() require to be changed - * accordingly in future. - */ -static void -tgl_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits, - enum fb_op_origin origin) -{ - struct intel_display *display = to_intel_display(intel_dp); - - if (!intel_dp->psr.dc3co_exitline || !intel_dp->psr.sel_update_enabled || - !intel_dp->psr.active) - return; - - /* - * At every frontbuffer flush flip event modified delay of delayed work, - * when delayed work schedules that means display has been idle. - */ - if (!(frontbuffer_bits & - INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe))) - return; - - tgl_psr2_enable_dc3co(intel_dp); - mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, - intel_dp->psr.dc3co_exit_delay); -} static void _psr_flush_handle(struct intel_dp *intel_dp) { @@ -3773,7 +3686,7 @@ void intel_psr_flush(struct intel_display *display, if (origin == ORIGIN_FLIP || (origin == ORIGIN_CURSOR_UPDATE && !intel_dp->psr.psr2_sel_fetch_enabled)) { - tgl_dc3co_flush_locked(intel_dp, frontbuffer_bits, origin); + psr2_dc3co_flush_locked(intel_dp, frontbuffer_bits); goto unlock; } @@ -3832,7 +3745,7 @@ void intel_psr_init(struct intel_dp *intel_dp) intel_dp->psr.link_standby = connector->panel.vbt.psr.full_link; INIT_WORK(&intel_dp->psr.work, intel_psr_work); - INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work); + INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, psr2_dc3co_disable_work); mutex_init(&intel_dp->psr.lock); } diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h index 29723e63888f..d545fdaa0de7 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.h +++ b/drivers/gpu/drm/i915/display/intel_psr.h @@ -87,5 +87,6 @@ void intel_psr_compute_config_late(struct intel_dp *intel_dp, int intel_psr_min_guardband(struct intel_crtc_state *crtc_state); bool intel_psr_use_trans_push(const struct intel_crtc_state *crtc_state); bool intel_psr_pr_async_video_timing_supported(struct intel_dp *intel_dp); +bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp); #endif /* __INTEL_PSR_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_psr_regs.h b/drivers/gpu/drm/i915/display/intel_psr_regs.h index 8afbf5a38335..16a9e3af198d 100644 --- a/drivers/gpu/drm/i915/display/intel_psr_regs.h +++ b/drivers/gpu/drm/i915/display/intel_psr_regs.h @@ -268,6 +268,7 @@ #define _PR_ALPM_CTL_A 0x60948 #define PR_ALPM_CTL(dev_priv, tran) _MMIO_TRANS2(dev_priv, tran, _PR_ALPM_CTL_A) +#define PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL BIT(7) #define PR_ALPM_CTL_ALLOW_LINK_OFF_BETWEEN_AS_SDP_AND_SU BIT(6) #define PR_ALPM_CTL_RFB_UPDATE_CONTROL BIT(5) #define PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE BIT(4) diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index d83d350959d8..3075ef04df56 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -1354,7 +1354,8 @@ static bool intel_sdvo_has_audio(struct intel_encoder *encoder, return intel_conn_state->force_audio == HDMI_AUDIO_ON; } -static int intel_sdvo_compute_config(struct intel_encoder *encoder, +static int intel_sdvo_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -1398,8 +1399,8 @@ static int intel_sdvo_compute_config(struct intel_encoder *encoder, const struct drm_display_mode *fixed_mode; int ret; - ret = intel_panel_compute_config(&intel_sdvo_connector->base, - adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, + &intel_sdvo_connector->base); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c index 0a926c6f25f4..840e1dcdc2d0 100644 --- a/drivers/gpu/drm/i915/display/intel_tv.c +++ b/drivers/gpu/drm/i915/display/intel_tv.c @@ -1187,13 +1187,12 @@ static bool intel_tv_vert_scaling(const struct drm_display_mode *tv_mode, } static int -intel_tv_compute_config(struct intel_encoder *encoder, +intel_tv_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = - to_intel_atomic_state(pipe_config->uapi.state); struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); struct intel_tv_connector_state *tv_conn_state = to_intel_tv_connector_state(conn_state); diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index e03b5daac5be..bffbdee76ee1 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -7,8 +7,10 @@ #include <drm/drm_print.h> #include "intel_alpm.h" +#include "intel_cmtg.h" #include "intel_crtc.h" #include "intel_de.h" +#include "intel_display_limits.h" #include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_dmc.h" @@ -74,6 +76,10 @@ bool intel_vrr_is_capable(struct intel_connector *connector) return false; } + if (!info->monitor_range.min_vfreq || !info->monitor_range.max_vfreq || + info->monitor_range.min_vfreq > info->monitor_range.max_vfreq) + return false; + return info->monitor_range.max_vfreq - info->monitor_range.min_vfreq > 10; } @@ -318,19 +324,19 @@ int intel_vrr_fixed_rr_hw_flipline(const struct intel_crtc_state *crtc_state) return intel_vrr_fixed_rr_hw_vtotal(crtc_state); } -void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state) +void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; if (!intel_vrr_possible(crtc_state)) return; - intel_de_write(display, TRANS_VRR_VMIN(display, cpu_transcoder), + intel_de_write(display, TRANS_VRR_VMIN(display, transcoder), intel_vrr_fixed_rr_hw_vmin(crtc_state) - 1); - intel_de_write(display, TRANS_VRR_VMAX(display, cpu_transcoder), + intel_de_write(display, TRANS_VRR_VMAX(display, transcoder), intel_vrr_fixed_rr_hw_vmax(crtc_state) - 1); - intel_de_write(display, TRANS_VRR_FLIPLINE(display, cpu_transcoder), + intel_de_write(display, TRANS_VRR_FLIPLINE(display, transcoder), intel_vrr_fixed_rr_hw_flipline(crtc_state) - 1); } @@ -645,7 +651,8 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state) lower_32_bits(crtc_state->cmrr.cmrr_n)); } - intel_vrr_set_fixed_rr_timings(crtc_state); + intel_vrr_set_fixed_rr_timings(crtc_state, cpu_transcoder); + intel_cmtg_set_vrr_timings(crtc_state); if (!intel_vrr_always_use_vrr_tg(display)) intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), @@ -930,6 +937,8 @@ static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state, vrr_ctl |= VRR_CTL_CMRR_ENABLE; intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl); + + intel_cmtg_set_vrr_ctl(crtc_state); } static void intel_vrr_tg_disable(const struct intel_crtc_state *old_crtc_state) @@ -974,7 +983,7 @@ void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state) intel_vrr_tg_disable(old_crtc_state); intel_vrr_disable_dc_balancing(old_crtc_state); - intel_vrr_set_fixed_rr_timings(old_crtc_state); + intel_vrr_set_fixed_rr_timings(old_crtc_state, old_crtc_state->cpu_transcoder); } void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state) @@ -1097,16 +1106,6 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) crtc_state->vrr.vmin += intel_vrr_vmin_flipline_offset(display); } - /* - * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal - * bits are not filled. Since for these platforms TRAN_VMIN is always - * filled with crtc_vtotal, use TRAN_VRR_VMIN to get the vtotal for - * adjusted_mode. - */ - if (intel_vrr_always_use_vrr_tg(display)) - crtc_state->hw.adjusted_mode.crtc_vtotal = - intel_vrr_vmin_vtotal(crtc_state); - if (HAS_AS_SDP(display)) { trans_vrr_vsync = intel_de_read(display, diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h index 4f16ca4af91f..55e9c429f579 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.h +++ b/drivers/gpu/drm/i915/display/intel_vrr.h @@ -15,6 +15,7 @@ struct intel_crtc; struct intel_crtc_state; struct intel_dsb; struct intel_display; +enum transcoder; bool intel_vrr_is_capable(struct intel_connector *connector); bool intel_vrr_is_in_range(struct intel_connector *connector, int vrefresh); @@ -42,7 +43,8 @@ int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state); bool intel_vrr_is_fixed_rr(const struct intel_crtc_state *crtc_state); void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state); void intel_vrr_transcoder_disable(const struct intel_crtc_state *crtc_state); -void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state); +void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder); void intel_vrr_dcb_reset(const struct intel_crtc_state *old_crtc_state, struct intel_crtc *crtc); bool intel_vrr_always_use_vrr_tg(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index 877eab75f19a..8829f365592e 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -266,7 +266,8 @@ static void band_gap_reset(struct intel_display *display) vlv_flisdsi_put(display); } -static int intel_dsi_compute_config(struct intel_encoder *encoder, +static int intel_dsi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -280,7 +281,7 @@ static int intel_dsi_compute_config(struct intel_encoder *encoder, pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB; pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB; - ret = intel_panel_compute_config(intel_connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, intel_connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h index 8878539c10ed..2c5d20e4dbaf 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h @@ -17,8 +17,6 @@ #include "i915_vma_types.h" enum intel_region_id; -struct drm_scanout_buffer; -struct intel_panic; #define obj_to_i915(obj__) to_i915((obj__)->base.dev) @@ -693,11 +691,6 @@ i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj) int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj); int i915_gem_object_truncate(struct drm_i915_gem_object *obj); -struct intel_panic *i915_gem_object_alloc_panic(void); -int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj, bool panic_tiling); -void i915_gem_object_panic_finish(struct intel_panic *panic); - /** * i915_gem_object_pin_map - return a contiguous mapping of the entire object * @obj: the object to map into kernel address space diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c index df35bdb755e4..59e3d4de7d3c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c @@ -6,11 +6,8 @@ #include <linux/vmalloc.h> #include <drm/drm_cache.h> -#include <drm/drm_panic.h> #include <drm/drm_print.h> -#include "display/intel_fb.h" -#include "display/intel_display_types.h" #include "gt/intel_gt.h" #include "gt/intel_tlb.h" @@ -359,131 +356,6 @@ static void *i915_gem_object_map_pfn(struct drm_i915_gem_object *obj, return vaddr ?: ERR_PTR(-ENOMEM); } -struct intel_panic { - struct page **pages; - int page; - void *vaddr; -}; - -static void i915_panic_kunmap(struct intel_panic *panic) -{ - if (panic->vaddr) { - drm_clflush_virt_range(panic->vaddr, PAGE_SIZE); - kunmap_local(panic->vaddr); - panic->vaddr = NULL; - } -} - -static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj) -{ - unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i; - struct page *page; - struct page **pages; - struct sgt_iter iter; - - /* For a 3840x2160 32 bits Framebuffer, this should require ~64K */ - pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC); - if (!pages) - return NULL; - - i = 0; - for_each_sgt_page(page, iter, obj->mm.pages) - pages[i++] = page; - return pages; -} - -static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, - unsigned int y, u32 color) -{ - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - unsigned int offset = fb->panic_tiling(sb->width, x, y); - - iosys_map_wr(&sb->map[0], offset, u32, color); -} - -/* - * The scanout buffer pages are not mapped, so for each pixel, - * use kmap_local_page_try_from_panic() to map the page, and write the pixel. - * Try to keep the map from the previous pixel, to avoid too much map/unmap. - */ -static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, - unsigned int y, u32 color) -{ - unsigned int new_page; - unsigned int offset; - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct intel_panic *panic = fb->panic; - - if (fb->panic_tiling) - offset = fb->panic_tiling(sb->width, x, y); - else - offset = y * sb->pitch[0] + x * sb->format->cpp[0]; - - new_page = offset >> PAGE_SHIFT; - offset = offset % PAGE_SIZE; - if (new_page != panic->page) { - i915_panic_kunmap(panic); - panic->page = new_page; - panic->vaddr = - kmap_local_page_try_from_panic(panic->pages[panic->page]); - } - if (panic->vaddr) { - u32 *pix = panic->vaddr + offset; - *pix = color; - } -} - -struct intel_panic *i915_gem_object_alloc_panic(void) -{ - struct intel_panic *panic; - - panic = kzalloc_obj(*panic); - - return panic; -} - -/* - * Setup the gem framebuffer for drm_panic access. - * Use current vaddr if it exists, or setup a list of pages. - * pfn is not supported yet. - */ -int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj, bool panic_tiling) -{ - enum i915_map_type has_type; - struct drm_i915_gem_object *obj = to_intel_bo(_obj); - void *ptr; - - ptr = page_unpack_bits(obj->mm.mapping, &has_type); - if (ptr) { - if (i915_gem_object_has_iomem(obj)) - iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr); - else - iosys_map_set_vaddr(&sb->map[0], ptr); - - if (panic_tiling) - sb->set_pixel = i915_gem_object_panic_map_set_pixel; - return 0; - } - if (i915_gem_object_has_struct_page(obj)) { - panic->pages = i915_gem_object_panic_pages(obj); - if (!panic->pages) - return -ENOMEM; - panic->page = -1; - sb->set_pixel = i915_gem_object_panic_page_set_pixel; - return 0; - } - return -EOPNOTSUPP; -} - -void i915_gem_object_panic_finish(struct intel_panic *panic) -{ - i915_panic_kunmap(panic); - panic->page = -1; - kfree(panic->pages); - panic->pages = NULL; -} - /* get, pin, and map the pages of the object into kernel space */ void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj, enum i915_map_type type) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c new file mode 100644 index 000000000000..91389d36f101 --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: MIT +/* Copyright © 2026 Intel Corporation */ + +#include <drm/drm_cache.h> +#include <drm/drm_panic.h> +#include <drm/intel/display_parent_interface.h> + +#include "i915_gem_object.h" +#include "i915_gem_panic.h" + +struct intel_panic { + struct page **pages; + int page; + void *vaddr; + + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width); +}; + +static void i915_panic_kunmap(struct intel_panic *panic) +{ + if (panic->vaddr) { + drm_clflush_virt_range(panic->vaddr, PAGE_SIZE); + kunmap_local(panic->vaddr); + panic->vaddr = NULL; + } +} + +static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj) +{ + unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i; + struct page *page; + struct page **pages; + struct sgt_iter iter; + + /* For a 3840x2160 32 bits Framebuffer, this should require ~64K */ + pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC); + if (!pages) + return NULL; + + i = 0; + for_each_sgt_page(page, iter, obj->mm.pages) + pages[i++] = page; + return pages; +} + +static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, + unsigned int y, u32 color) +{ + struct intel_panic *panic = sb->private; + unsigned int offset = panic->tiling(sb->width, x, y); + + iosys_map_wr(&sb->map[0], offset, u32, color); +} + +/* + * The scanout buffer pages are not mapped, so for each pixel, + * use kmap_local_page_try_from_panic() to map the page, and write the pixel. + * Try to keep the map from the previous pixel, to avoid too much map/unmap. + */ +static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, + unsigned int y, u32 color) +{ + struct intel_panic *panic = sb->private; + unsigned int new_page; + unsigned int offset; + + if (panic->tiling) + offset = panic->tiling(sb->width, x, y); + else + offset = y * sb->pitch[0] + x * sb->format->cpp[0]; + + new_page = offset >> PAGE_SHIFT; + offset = offset % PAGE_SIZE; + if (new_page != panic->page) { + i915_panic_kunmap(panic); + panic->page = new_page; + panic->vaddr = + kmap_local_page_try_from_panic(panic->pages[panic->page]); + } + if (panic->vaddr) { + u32 *pix = panic->vaddr + offset; + *pix = color; + } +} + +static struct intel_panic *i915_gem_object_alloc_panic(void) +{ + struct intel_panic *panic; + + panic = kzalloc_obj(*panic); + + return panic; +} + +/* + * Setup the gem framebuffer for drm_panic access. + * Use current vaddr if it exists, or setup a list of pages. + * pfn is not supported yet. + */ +static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *_obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) +{ + enum i915_map_type has_type; + struct drm_i915_gem_object *obj = to_intel_bo(_obj); + void *ptr; + + sb->private = panic; + + ptr = page_unpack_bits(obj->mm.mapping, &has_type); + if (ptr) { + if (i915_gem_object_has_iomem(obj)) + iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr); + else + iosys_map_set_vaddr(&sb->map[0], ptr); + + if (tiling) { + panic->tiling = tiling; + sb->set_pixel = i915_gem_object_panic_map_set_pixel; + } + return 0; + } + if (i915_gem_object_has_struct_page(obj)) { + panic->pages = i915_gem_object_panic_pages(obj); + if (!panic->pages) + return -ENOMEM; + panic->page = -1; + panic->tiling = tiling; + sb->set_pixel = i915_gem_object_panic_page_set_pixel; + return 0; + } + return -EOPNOTSUPP; +} + +static void i915_gem_object_panic_finish(struct intel_panic *panic) +{ + i915_panic_kunmap(panic); + panic->page = -1; + kfree(panic->pages); + panic->pages = NULL; +} + +const struct intel_display_panic_interface i915_display_panic_interface = { + .alloc = i915_gem_object_alloc_panic, + .setup = i915_gem_object_panic_setup, + .finish = i915_gem_object_panic_finish, +}; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.h b/drivers/gpu/drm/i915/gem/i915_gem_panic.h new file mode 100644 index 000000000000..82c3aca6f1f3 --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __I915_GEM_PANIC_H__ +#define __I915_GEM_PANIC_H__ + +#include <linux/types.h> + +extern const struct intel_display_panic_interface i915_display_panic_interface; + +#endif /* __I915_GEM_PANIC_H__ */ diff --git a/drivers/gpu/drm/i915/gt/intel_gsc.c b/drivers/gpu/drm/i915/gt/intel_gsc.c index 050d909fb4f8..1c06bf76568a 100644 --- a/drivers/gpu/drm/i915/gt/intel_gsc.c +++ b/drivers/gpu/drm/i915/gt/intel_gsc.c @@ -286,7 +286,7 @@ static void gsc_irq_handler(struct intel_gt *gt, unsigned int intf_id) ret = generic_handle_irq_safe(gt->gsc.intf[intf_id].irq); if (ret) - gt_err_ratelimited(gt, "error handling GSC irq: %d\n", ret); + gt_err_ratelimited(gt, "GSC: irq handling failed (%pe)\n", ERR_PTR(ret)); } void intel_gsc_irq_handler(struct intel_gt *gt, u32 iir) diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index ca5b54466a65..dc9ef98ff51b 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -90,13 +90,13 @@ static inline int cnp_get_port_from_gmbus0(u32 gmbus0) int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; int port = -EINVAL; - if (port_select == GMBUS_PIN_1_BXT) + if (port_select == GMBUS_PIN_1) port = PORT_B; - else if (port_select == GMBUS_PIN_2_BXT) + else if (port_select == GMBUS_PIN_2) port = PORT_C; - else if (port_select == GMBUS_PIN_3_BXT) + else if (port_select == GMBUS_PIN_3) port = PORT_D; - else if (port_select == GMBUS_PIN_4_CNP) + else if (port_select == GMBUS_PIN_4) port = PORT_E; return port; } @@ -106,11 +106,11 @@ static inline int bxt_get_port_from_gmbus0(u32 gmbus0) int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; int port = -EINVAL; - if (port_select == GMBUS_PIN_1_BXT) + if (port_select == GMBUS_PIN_1) port = PORT_B; - else if (port_select == GMBUS_PIN_2_BXT) + else if (port_select == GMBUS_PIN_2) port = PORT_C; - else if (port_select == GMBUS_PIN_3_BXT) + else if (port_select == GMBUS_PIN_3) port = PORT_D; return port; } diff --git a/drivers/gpu/drm/i915/i915_active.c b/drivers/gpu/drm/i915/i915_active.c index e7632c1ff4be..b2442f05825e 100644 --- a/drivers/gpu/drm/i915/i915_active.c +++ b/drivers/gpu/drm/i915/i915_active.c @@ -318,7 +318,7 @@ active_instance(struct i915_active *ref, u64 idx) */ node = kmem_cache_alloc(slab_cache, GFP_ATOMIC); if (!node) - goto out; + goto err; __i915_active_fence_init(&node->base, NULL, node_retire); node->ref = ref; @@ -332,6 +332,11 @@ out: spin_unlock_irq(&ref->tree_lock); return &node->base; + +err: + spin_unlock_irq(&ref->tree_lock); + + return NULL; } void __i915_active_init(struct i915_active *ref, diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 58081b52461a..45f2dcc5130e 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -51,7 +51,6 @@ #include <drm/intel/intel_pcode_regs.h> #include "display/i9xx_display_sr.h" -#include "display/intel_bw.h" #include "display/intel_cdclk.h" #include "display/intel_crtc.h" #include "display/intel_display_device.h" @@ -60,8 +59,6 @@ #include "display/intel_dmc.h" #include "display/intel_dp.h" #include "display/intel_dpt.h" -#include "display/intel_dram.h" -#include "display/intel_encoder.h" #include "display/intel_fbdev.h" #include "display/intel_gmbus.h" #include "display/intel_hotplug.h" @@ -79,6 +76,7 @@ #include "gem/i915_gem_ioctls.h" #include "gem/i915_gem_mman.h" #include "gem/i915_gem_object_frontbuffer.h" +#include "gem/i915_gem_panic.h" #include "gem/i915_gem_pm.h" #include "gt/intel_gt.h" #include "gt/intel_gt_pm.h" @@ -111,7 +109,6 @@ #include "i915_irq.h" #include "i915_memcpy.h" #include "i915_overlay.h" -#include "i915_panic.h" #include "i915_perf.h" #include "i915_query.h" #include "i915_reg.h" @@ -470,7 +467,6 @@ static int i915_pcode_init(struct drm_i915_private *i915) */ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv) { - struct intel_display *display = dev_priv->display; struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); int ret; @@ -564,28 +560,14 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv) drm_dbg(&dev_priv->drm, "can't enable MSI"); } - intel_opregion_setup(display); - ret = i915_pcode_init(dev_priv); if (ret) - goto err_opregion; - - /* - * Fill the dram structure to get the system dram info. This will be - * used for memory latency calculation. - */ - ret = intel_dram_detect(display); - if (ret) - goto err_opregion; - - intel_bw_init_hw(display); + goto err_msi; return 0; -err_opregion: - intel_opregion_cleanup(display); - if (pdev->msi_enabled) - pci_disable_msi(pdev); +err_msi: + pci_disable_msi(pdev); err_mem_regions: intel_memory_regions_driver_release(dev_priv); err_ggtt: @@ -604,15 +586,11 @@ ALLOW_ERROR_INJECTION(i915_driver_hw_probe, ERRNO); */ static void i915_driver_hw_remove(struct drm_i915_private *dev_priv) { - struct intel_display *display = dev_priv->display; struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); i915_perf_fini(dev_priv); - intel_opregion_cleanup(display); - - if (pdev->msi_enabled) - pci_disable_msi(pdev); + pci_disable_msi(pdev); } /** @@ -803,7 +781,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent) const struct intel_device_info *match_info = (struct intel_device_info *)ent->driver_data; struct drm_i915_private *i915; - struct intel_display *display; i915 = devm_drm_dev_alloc(&pdev->dev, &i915_drm_driver, struct drm_i915_private, drm); @@ -818,12 +795,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent) /* Set up device info and initial runtime info. */ intel_device_info_driver_create(i915, pdev->device, match_info); - display = intel_display_device_probe(pdev, &parent); - if (IS_ERR(display)) - return ERR_CAST(display); - - i915->display = display; - return i915; } @@ -856,7 +827,13 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return PTR_ERR(i915); } - display = i915->display; + display = intel_display_device_probe(pdev, &parent); + if (IS_ERR(display)) { + ret = PTR_ERR(display); + goto out_pci_disable; + } + + i915->display = display; ret = i915_driver_early_probe(i915); if (ret < 0) @@ -971,7 +948,6 @@ void i915_driver_remove(struct drm_i915_private *i915) intel_display_driver_remove(display); intel_irq_uninstall(i915); - intel_hpd_cancel_work(display); intel_display_driver_remove_noirq(display); @@ -1043,43 +1019,24 @@ void i915_driver_shutdown(struct drm_i915_private *i915) disable_rpm_wakeref_asserts(&i915->runtime_pm); intel_runtime_pm_disable(&i915->runtime_pm); - intel_display_power_disable(display); - drm_client_dev_suspend(&i915->drm); - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&i915->drm); - intel_display_driver_disable_user_access(display); - - drm_atomic_helper_shutdown(&i915->drm); - } - - intel_dp_mst_suspend(display); + intel_display_driver_shutdown(display); intel_irq_suspend(i915); - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); - - intel_encoder_suspend_all(display); - intel_encoder_shutdown_all(display); intel_dmc_suspend(display); i915_gem_suspend(i915); /* - * The only requirement is to reboot with display DC states disabled, - * for now leaving all display power wells in the INIT power domain - * enabled. - * * TODO: * - unify the pci_driver::shutdown sequence here with the * pci_driver.driver.pm.poweroff,poweroff_late sequence. * - unify the driver remove and system/runtime suspend sequences with * the above unified shutdown/poweroff sequence. */ - intel_display_power_driver_remove(display); + + intel_display_driver_shutdown_late(display); enable_rpm_wakeref_asserts(&i915->runtime_pm); intel_runtime_pm_driver_last_release(&i915->runtime_pm); @@ -1108,10 +1065,10 @@ static int i915_drm_prepare(struct drm_device *dev) intel_pxp_suspend_prepare(i915->pxp); /* - * NB intel_display_driver_suspend() may issue new requests after we've - * ostensibly marked the GPU as ready-to-sleep here. We need to - * split out that work and pull it forward so that after point, - * the GPU is not woken again. + * NB intel_display_driver_pm_suspend() may issue new requests after + * we've ostensibly marked the GPU as ready-to-sleep here. We need to + * split out that work and pull it forward so that after point, the GPU + * is not woken again. */ return i915_gem_backup_suspend(i915); } @@ -1124,24 +1081,9 @@ static int i915_drm_suspend(struct drm_device *dev) disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); - /* We do a lot of poking in a lot of registers, make sure they work - * properly. */ - intel_display_power_disable(display); - drm_client_dev_suspend(dev); - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(dev); - intel_display_driver_disable_user_access(display); - } - - intel_display_driver_suspend(display); + intel_display_driver_pm_suspend(display); intel_irq_suspend(dev_priv); - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); - - intel_encoder_suspend_all(display); /* Must be called before GGTT is suspended. */ intel_dpt_suspend(display); @@ -1181,12 +1123,12 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation) for_each_gt(gt, dev_priv, i) intel_uncore_suspend(gt->uncore); - intel_display_power_suspend_late(display, s2idle); + intel_display_driver_pm_suspend_late(display, s2idle); ret = vlv_suspend_complete(dev_priv); if (ret) { drm_err(&dev_priv->drm, "Suspend complete failed: %d\n", ret); - intel_display_power_resume_early(display); + intel_display_driver_pm_resume_early(display); } enable_rpm_wakeref_asserts(rpm); @@ -1309,24 +1251,7 @@ static int i915_drm_resume(struct drm_device *dev) intel_clock_gating_init(&dev_priv->drm); - if (intel_display_device_present(display)) - intel_display_driver_resume_access(display); - - intel_hpd_init(display); - - intel_display_driver_resume(display); - - if (intel_display_device_present(display)) { - intel_display_driver_enable_user_access(display); - drm_kms_helper_poll_enable(dev); - } - intel_hpd_poll_disable(display); - - intel_opregion_resume(display); - - drm_client_dev_resume(dev); - - intel_display_power_enable(display); + intel_display_driver_pm_resume(display); intel_gvt_resume(dev_priv); @@ -1362,7 +1287,7 @@ static int i915_drm_resume_early(struct drm_device *dev) for_each_gt(gt, dev_priv, i) intel_gt_resume_early(gt); - intel_display_power_resume_early(display); + intel_display_driver_pm_resume_early(display); enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); diff --git a/drivers/gpu/drm/i915/i915_panic.c b/drivers/gpu/drm/i915/i915_panic.c deleted file mode 100644 index 728be077e8e8..000000000000 --- a/drivers/gpu/drm/i915/i915_panic.c +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -/* Copyright © 2025 Intel Corporation */ - -#include <drm/drm_panic.h> -#include <drm/intel/display_parent_interface.h> - -#include "display/intel_display_types.h" -#include "display/intel_fb.h" -#include "gem/i915_gem_object.h" - -#include "i915_panic.h" - -static struct intel_panic *intel_panic_alloc(void) -{ - return i915_gem_object_alloc_panic(); -} - -static int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) -{ - struct intel_framebuffer *fb = sb->private; - struct drm_gem_object *obj = intel_fb_bo(&fb->base); - - return i915_gem_object_panic_setup(panic, sb, obj, fb->panic_tiling); -} - -static void intel_panic_finish(struct intel_panic *panic) -{ - return i915_gem_object_panic_finish(panic); -} - -const struct intel_display_panic_interface i915_display_panic_interface = { - .alloc = intel_panic_alloc, - .setup = intel_panic_setup, - .finish = intel_panic_finish, -}; diff --git a/drivers/gpu/drm/i915/i915_panic.h b/drivers/gpu/drm/i915/i915_panic.h deleted file mode 100644 index 743d8c861c42..000000000000 --- a/drivers/gpu/drm/i915/i915_panic.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* Copyright © 2025 Intel Corporation */ - -#ifndef __I915_PANIC_H__ -#define __I915_PANIC_H__ - -extern const struct intel_display_panic_interface i915_display_panic_interface; - -#endif /* __I915_PANIC_H__ */ diff --git a/drivers/gpu/drm/imagination/pvr_context.c b/drivers/gpu/drm/imagination/pvr_context.c index eba4694400b5..52e16c1e7af0 100644 --- a/drivers/gpu/drm/imagination/pvr_context.c +++ b/drivers/gpu/drm/imagination/pvr_context.c @@ -161,22 +161,24 @@ ctx_fw_data_init(void *cpu_ptr, void *priv) /** * pvr_context_destroy_queues() - Destroy all queues attached to a context. * @ctx: Context to destroy queues on. + * @cleanup_queue_entity: Whether to cleanup the queue entity e.g. context + * creation failure path. * * Should be called when the last reference to a context object is dropped. * It releases all resources attached to the queues bound to this context. */ -static void pvr_context_destroy_queues(struct pvr_context *ctx) +static void pvr_context_destroy_queues(struct pvr_context *ctx, bool cleanup_queue_entity) { switch (ctx->type) { case DRM_PVR_CTX_TYPE_RENDER: - pvr_queue_destroy(ctx->queues.fragment); - pvr_queue_destroy(ctx->queues.geometry); + pvr_queue_destroy(ctx->queues.fragment, cleanup_queue_entity); + pvr_queue_destroy(ctx->queues.geometry, cleanup_queue_entity); break; case DRM_PVR_CTX_TYPE_COMPUTE: - pvr_queue_destroy(ctx->queues.compute); + pvr_queue_destroy(ctx->queues.compute, cleanup_queue_entity); break; case DRM_PVR_CTX_TYPE_TRANSFER_FRAG: - pvr_queue_destroy(ctx->queues.transfer); + pvr_queue_destroy(ctx->queues.transfer, cleanup_queue_entity); break; } } @@ -240,7 +242,7 @@ static int pvr_context_create_queues(struct pvr_context *ctx, return -EINVAL; err_destroy_queues: - pvr_context_destroy_queues(ctx); + pvr_context_destroy_queues(ctx, true); return err; } @@ -349,7 +351,7 @@ err_destroy_fw_obj: pvr_fw_object_destroy(ctx->fw_obj); err_destroy_queues: - pvr_context_destroy_queues(ctx); + pvr_context_destroy_queues(ctx, true); err_free_ctx_id: /* @@ -384,7 +386,7 @@ pvr_context_release(struct kref *ref_count) spin_unlock(&pvr_dev->ctx_list_lock); xa_erase(&pvr_dev->ctx_ids, ctx->ctx_id); - pvr_context_destroy_queues(ctx); + pvr_context_destroy_queues(ctx, false); pvr_fw_object_destroy(ctx->fw_obj); kfree(ctx->data); pvr_vm_context_put(ctx->vm_ctx); diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c index ca3042d14253..8897b3bdeb4a 100644 --- a/drivers/gpu/drm/imagination/pvr_drv.c +++ b/drivers/gpu/drm/imagination/pvr_drv.c @@ -30,7 +30,6 @@ #include <linux/fs.h> #include <linux/kernel.h> #include <linux/list.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/of_device.h> @@ -515,7 +514,8 @@ copy_out: if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } @@ -596,7 +596,8 @@ copy_out: if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } @@ -1255,14 +1256,13 @@ pvr_set_uobj_array(const struct drm_pvr_obj_array *out, u32 min_stride, u32 obj_ if (copy_to_user(out_ptr, in_ptr, cpy_elem_size)) return -EFAULT; - out_ptr += obj_size; - in_ptr += out->stride; - } + if (out->stride > obj_size && + clear_user(out_ptr + cpy_elem_size, out->stride - obj_size)) { + return -EFAULT; + } - if (out->stride > obj_size && - clear_user(u64_to_user_ptr(out->array + obj_size), - out->stride - obj_size)) { - return -EFAULT; + out_ptr += out->stride; + in_ptr += obj_size; } } diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 7ed60e1c1a86..941c017399fc 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -1439,11 +1439,12 @@ void pvr_queue_kill(struct pvr_queue *queue) /** * pvr_queue_destroy() - Destroy a queue. * @queue: The queue to destroy. + * @cleanup_queue_entity: Whether to cleanup the queue entity. * * Cleanup the queue and free the resources attached to it. Should be * called from the context release function. */ -void pvr_queue_destroy(struct pvr_queue *queue) +void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity) { if (!queue) return; @@ -1453,7 +1454,8 @@ void pvr_queue_destroy(struct pvr_queue *queue) mutex_unlock(&queue->ctx->pvr_dev->queues.lock); drm_sched_fini(&queue->scheduler); - drm_sched_entity_fini(&queue->entity); + if (cleanup_queue_entity) + drm_sched_entity_fini(&queue->entity); if (WARN_ON(queue->last_queued_job_scheduled_fence)) dma_fence_put(queue->last_queued_job_scheduled_fence); diff --git a/drivers/gpu/drm/imagination/pvr_queue.h b/drivers/gpu/drm/imagination/pvr_queue.h index 4aa72665ce25..149cc6d124bf 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.h +++ b/drivers/gpu/drm/imagination/pvr_queue.h @@ -158,7 +158,7 @@ struct pvr_queue *pvr_queue_create(struct pvr_context *ctx, void pvr_queue_kill(struct pvr_queue *queue); -void pvr_queue_destroy(struct pvr_queue *queue); +void pvr_queue_destroy(struct pvr_queue *queue, bool cleanup_queue_entity); void pvr_queue_process(struct pvr_queue *queue); diff --git a/drivers/gpu/drm/imagination/pvr_vm.c b/drivers/gpu/drm/imagination/pvr_vm.c index e1ec60f34b6e..396d349fb6ce 100644 --- a/drivers/gpu/drm/imagination/pvr_vm.c +++ b/drivers/gpu/drm/imagination/pvr_vm.c @@ -1019,7 +1019,8 @@ copy_out: if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } @@ -1069,7 +1070,8 @@ copy_out: if (err < 0) return err; - args->size = sizeof(query); + if (args->size > sizeof(query)) + args->size = sizeof(query); return 0; } diff --git a/drivers/gpu/drm/imx/dc/dc-cf.c b/drivers/gpu/drm/imx/dc/dc-cf.c index 2f077161e912..7c2f7abc5099 100644 --- a/drivers/gpu/drm/imx/dc/dc-cf.c +++ b/drivers/gpu/drm/imx/dc/dc-cf.c @@ -7,7 +7,6 @@ #include <linux/bits.h> #include <linux/component.h> #include <linux/ioport.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpu/drm/imx/dc/dc-de.c b/drivers/gpu/drm/imx/dc/dc-de.c index 5a3125596fdf..15056590b04d 100644 --- a/drivers/gpu/drm/imx/dc/dc-de.c +++ b/drivers/gpu/drm/imx/dc/dc-de.c @@ -4,7 +4,6 @@ */ #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of_platform.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/imx/dc/dc-drv.c b/drivers/gpu/drm/imx/dc/dc-drv.c index 04f021d2d6cf..13795a2ad735 100644 --- a/drivers/gpu/drm/imx/dc/dc-drv.c +++ b/drivers/gpu/drm/imx/dc/dc-drv.c @@ -7,7 +7,6 @@ #include <linux/component.h> #include <linux/device.h> #include <linux/dma-mapping.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> diff --git a/drivers/gpu/drm/imx/dc/dc-ed.c b/drivers/gpu/drm/imx/dc/dc-ed.c index d42f33d6f3fc..de1b71315eab 100644 --- a/drivers/gpu/drm/imx/dc/dc-ed.c +++ b/drivers/gpu/drm/imx/dc/dc-ed.c @@ -6,7 +6,6 @@ #include <linux/bitfield.h> #include <linux/bits.h> #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpu/drm/imx/dc/dc-fg.c b/drivers/gpu/drm/imx/dc/dc-fg.c index 28f372be9247..3741dc66c0d9 100644 --- a/drivers/gpu/drm/imx/dc/dc-fg.c +++ b/drivers/gpu/drm/imx/dc/dc-fg.c @@ -9,7 +9,6 @@ #include <linux/component.h> #include <linux/device.h> #include <linux/jiffies.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpu/drm/imx/dc/dc-fl.c b/drivers/gpu/drm/imx/dc/dc-fl.c index 3ce24c72aa13..9f03df44a63a 100644 --- a/drivers/gpu/drm/imx/dc/dc-fl.c +++ b/drivers/gpu/drm/imx/dc/dc-fl.c @@ -5,7 +5,6 @@ #include <linux/component.h> #include <linux/ioport.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpu/drm/imx/dc/dc-fw.c b/drivers/gpu/drm/imx/dc/dc-fw.c index acb2d4d9e2ec..14512c01ea78 100644 --- a/drivers/gpu/drm/imx/dc/dc-fw.c +++ b/drivers/gpu/drm/imx/dc/dc-fw.c @@ -4,7 +4,6 @@ */ #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpu/drm/imx/dc/dc-lb.c b/drivers/gpu/drm/imx/dc/dc-lb.c index ca1d714c8d6e..cb614f3c2f69 100644 --- a/drivers/gpu/drm/imx/dc/dc-lb.c +++ b/drivers/gpu/drm/imx/dc/dc-lb.c @@ -6,7 +6,6 @@ #include <linux/bitfield.h> #include <linux/bits.h> #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpu/drm/imx/dc/dc-pe.c b/drivers/gpu/drm/imx/dc/dc-pe.c index 6676c22f3f45..4eb8c11de99c 100644 --- a/drivers/gpu/drm/imx/dc/dc-pe.c +++ b/drivers/gpu/drm/imx/dc/dc-pe.c @@ -5,7 +5,6 @@ #include <linux/clk.h> #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> diff --git a/drivers/gpu/drm/imx/dc/dc-tc.c b/drivers/gpu/drm/imx/dc/dc-tc.c index 0bfd381b2cea..d0d4faba790e 100644 --- a/drivers/gpu/drm/imx/dc/dc-tc.c +++ b/drivers/gpu/drm/imx/dc/dc-tc.c @@ -4,7 +4,6 @@ */ #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c index f52832b43aca..c67fe80b8115 100644 --- a/drivers/gpu/drm/imx/lcdc/imx-lcdc.c +++ b/drivers/gpu/drm/imx/lcdc/imx-lcdc.c @@ -21,7 +21,6 @@ #include <linux/bitfield.h> #include <linux/clk.h> #include <linux/dma-mapping.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c index b8ccd6e55bed..4a40e510e7db 100644 --- a/drivers/gpu/drm/mediatek/mtk_cec.c +++ b/drivers/gpu/drm/mediatek/mtk_cec.c @@ -8,7 +8,6 @@ #include <linux/io.h> #include <linux/interrupt.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include "mtk_cec.h" diff --git a/drivers/gpu/drm/mediatek/mtk_mdp_rdma.c b/drivers/gpu/drm/mediatek/mtk_mdp_rdma.c index 7982788ae9df..f903656ea0ef 100644 --- a/drivers/gpu/drm/mediatek/mtk_mdp_rdma.c +++ b/drivers/gpu/drm/mediatek/mtk_mdp_rdma.c @@ -6,7 +6,6 @@ #include <drm/drm_fourcc.h> #include <linux/clk.h> #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/soc/mediatek/mtk-cmdq.h> diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c index 4412bd678108..867918e9f498 100644 --- a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c +++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c @@ -7,7 +7,6 @@ #include <linux/clk.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of_graph.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c index 0b756da2fec2..9b8fbda85d28 100644 --- a/drivers/gpu/drm/mxsfb/mxsfb_drv.c +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -12,7 +12,6 @@ #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/io.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/property.h> diff --git a/drivers/gpu/drm/panel/panel-arm-versatile.c b/drivers/gpu/drm/panel/panel-arm-versatile.c index ea5119018df4..cffee9838324 100644 --- a/drivers/gpu/drm/panel/panel-arm-versatile.c +++ b/drivers/gpu/drm/panel/panel-arm-versatile.c @@ -30,7 +30,6 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/mfd/syscon.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpu/drm/panel/panel-auo-a030jtn01.c b/drivers/gpu/drm/panel/panel-auo-a030jtn01.c index 6e52bf6830e1..d71850b24ffa 100644 --- a/drivers/gpu/drm/panel/panel-auo-a030jtn01.c +++ b/drivers/gpu/drm/panel/panel-auo-a030jtn01.c @@ -11,7 +11,6 @@ #include <linux/device.h> #include <linux/gpio/consumer.h> #include <linux/media-bus-format.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-boe-td4320.c b/drivers/gpu/drm/panel/panel-boe-td4320.c index 1956daa2c71b..23558a76dd72 100644 --- a/drivers/gpu/drm/panel/panel-boe-td4320.c +++ b/drivers/gpu/drm/panel/panel-boe-td4320.c @@ -5,7 +5,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c index 6225501cb174..8c3a231c147d 100644 --- a/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c +++ b/drivers/gpu/drm/panel/panel-feixin-k101-im2ba02.c @@ -5,7 +5,6 @@ #include <linux/gpio/consumer.h> #include <linux/delay.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c index dbdb7e3cb7b6..c1d8ca5ca6e1 100644 --- a/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c +++ b/drivers/gpu/drm/panel/panel-feiyang-fy07024di26a30d.c @@ -11,7 +11,6 @@ #include <linux/gpio/consumer.h> #include <linux/delay.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/regulator/consumer.h> #define FEIYANG_INIT_CMD_LEN 2 diff --git a/drivers/gpu/drm/panel/panel-himax-hx83112b.c b/drivers/gpu/drm/panel/panel-himax-hx83112b.c index 263f79a967de..41f21f8c1373 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83112b.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83112b.c @@ -7,7 +7,6 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-himax-hx83121a.c b/drivers/gpu/drm/panel/panel-himax-hx83121a.c index e31e2fba0a78..3b6a562c4d11 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx83121a.c +++ b/drivers/gpu/drm/panel/panel-himax-hx83121a.c @@ -9,7 +9,6 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_graph.h> diff --git a/drivers/gpu/drm/panel/panel-himax-hx8394.c b/drivers/gpu/drm/panel/panel-himax-hx8394.c index bf80354567df..416203da2f45 100644 --- a/drivers/gpu/drm/panel/panel-himax-hx8394.c +++ b/drivers/gpu/drm/panel/panel-himax-hx8394.c @@ -13,7 +13,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/media-bus-format.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-hydis-hv101hd1.c b/drivers/gpu/drm/panel/panel-hydis-hv101hd1.c index 46426c388932..0a96eb0fae1e 100644 --- a/drivers/gpu/drm/panel/panel-hydis-hv101hd1.c +++ b/drivers/gpu/drm/panel/panel-hydis-hv101hd1.c @@ -4,7 +4,6 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c index f7425dfaa50d..8115a3158492 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9341.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9341.c @@ -19,7 +19,6 @@ #include <linux/bitops.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c index ecdbed8d4a3a..ad33414719fc 100644 --- a/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c +++ b/drivers/gpu/drm/panel/panel-ilitek-ili9806e-dsi.c @@ -4,7 +4,6 @@ #include <linux/err.h> #include <linux/errno.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> diff --git a/drivers/gpu/drm/panel/panel-lg-ld070wx3.c b/drivers/gpu/drm/panel/panel-lg-ld070wx3.c index 00cbfc5518a5..0280addb6500 100644 --- a/drivers/gpu/drm/panel/panel-lg-ld070wx3.c +++ b/drivers/gpu/drm/panel/panel-lg-ld070wx3.c @@ -4,7 +4,6 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-motorola-mot.c b/drivers/gpu/drm/panel/panel-motorola-mot.c index eb1f86c3d704..d5b1a6b72ebc 100644 --- a/drivers/gpu/drm/panel/panel-motorola-mot.c +++ b/drivers/gpu/drm/panel/panel-motorola-mot.c @@ -4,7 +4,6 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35532.c b/drivers/gpu/drm/panel/panel-novatek-nt35532.c index 184f61bca7ca..edea766a3c44 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35532.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35532.c @@ -6,7 +6,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-novatek-nt37801.c b/drivers/gpu/drm/panel/panel-novatek-nt37801.c index d6a37d7e0cc6..861e999250f9 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt37801.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt37801.c @@ -5,7 +5,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <drm/display/drm_dsc.h> diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c index 60701521c3b1..130520558a81 100644 --- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c @@ -9,7 +9,6 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-raydium-rm67200.c b/drivers/gpu/drm/panel/panel-raydium-rm67200.c index 333faed62da7..b2ba006c06f6 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm67200.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm67200.c @@ -4,7 +4,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-raydium-rm68200.c b/drivers/gpu/drm/panel/panel-raydium-rm68200.c index 669b5f5c1ad9..c535dc931903 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm68200.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm68200.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-renesas-r61307.c b/drivers/gpu/drm/panel/panel-renesas-r61307.c index d8185cc1b5d6..53556452e746 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r61307.c +++ b/drivers/gpu/drm/panel/panel-renesas-r61307.c @@ -4,7 +4,6 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-renesas-r69328.c b/drivers/gpu/drm/panel/panel-renesas-r69328.c index bfe2787f8f53..81b77141b4e4 100644 --- a/drivers/gpu/drm/panel/panel-renesas-r69328.c +++ b/drivers/gpu/drm/panel/panel-renesas-r69328.c @@ -4,7 +4,6 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-samsung-ltl106hl02.c b/drivers/gpu/drm/panel/panel-samsung-ltl106hl02.c index 1618841b7caa..2f8fa95dd7fb 100644 --- a/drivers/gpu/drm/panel/panel-samsung-ltl106hl02.c +++ b/drivers/gpu/drm/panel/panel-samsung-ltl106hl02.c @@ -4,7 +4,6 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c index ba1a02000bb9..1b14aa4efe35 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6d16d0.c @@ -11,7 +11,6 @@ #include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> #include <linux/delay.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> struct s6d16d0 { diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c index 6f3d39556f92..e05199ce14ee 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63j0x03.c @@ -11,7 +11,6 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c index a89d925fdfb2..2630975c111b 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0-dsi.c @@ -6,7 +6,6 @@ #include <linux/module.h> #include <linux/delay.h> -#include <linux/mod_devicetable.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_print.h> diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c index 7e2f4e043d62..77fee36dbb55 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams427ap24.c @@ -7,7 +7,6 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/property.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c b/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c index 199ff99efd78..2fae0dc6c424 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e8fc0-m1906f9.c @@ -7,7 +7,6 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-sitronix-st7703.c b/drivers/gpu/drm/panel/panel-sitronix-st7703.c index 6c348fe28955..f1641c9c7d13 100644 --- a/drivers/gpu/drm/panel/panel-sitronix-st7703.c +++ b/drivers/gpu/drm/panel/panel-sitronix-st7703.c @@ -11,7 +11,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/media-bus-format.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-summit.c b/drivers/gpu/drm/panel/panel-summit.c index 6d40b9ddfe02..84435be52424 100644 --- a/drivers/gpu/drm/panel/panel-summit.c +++ b/drivers/gpu/drm/panel/panel-summit.c @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only #include <linux/backlight.h> -#include <linux/mod_devicetable.h> #include <linux/property.h> #include <drm/drm_device.h> #include <drm/drm_mipi_dsi.h> diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c index f1430370ff94..50f8a84537ca 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c @@ -7,7 +7,6 @@ #include <linux/delay.h> #include <linux/module.h> #include <linux/property.h> -#include <linux/mod_devicetable.h> #include <linux/gpio/consumer.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panel/panel-visionox-rm692e5.c b/drivers/gpu/drm/panel/panel-visionox-rm692e5.c index e53645d59413..9567a6125565 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm692e5.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm692e5.c @@ -9,7 +9,6 @@ #include <linux/backlight.h> #include <linux/delay.h> #include <linux/gpio/consumer.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regulator/consumer.h> diff --git a/drivers/gpu/drm/panthor/panthor_device.c b/drivers/gpu/drm/panthor/panthor_device.c index bd417d6ae8c0..0b25abebb803 100644 --- a/drivers/gpu/drm/panthor/panthor_device.c +++ b/drivers/gpu/drm/panthor/panthor_device.c @@ -207,6 +207,7 @@ int panthor_device_init(struct panthor_device *ptdev) *dummy_page_virt = 1; INIT_WORK(&ptdev->reset.work, panthor_device_reset_work); + disable_work(&ptdev->reset.work); ptdev->reset.wq = alloc_ordered_workqueue("panthor-reset-wq", 0); if (!ptdev->reset.wq) return -ENOMEM; @@ -285,6 +286,9 @@ int panthor_device_init(struct panthor_device *ptdev) panthor_gem_init(ptdev); + /* Now that everything is initialized, we can enable the reset work. */ + enable_work(&ptdev->reset.work); + /* ~3 frames */ pm_runtime_set_autosuspend_delay(ptdev->base.dev, 50); pm_runtime_use_autosuspend(ptdev->base.dev); diff --git a/drivers/gpu/drm/panthor/panthor_device.h b/drivers/gpu/drm/panthor/panthor_device.h index 35679bfa1f3a..0fda64fbe5f2 100644 --- a/drivers/gpu/drm/panthor/panthor_device.h +++ b/drivers/gpu/drm/panthor/panthor_device.h @@ -512,9 +512,6 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) struct panthor_irq *pirq = data; \ enum panthor_irq_state old_state; \ \ - if (!gpu_read(pirq->iomem, INT_STAT)) \ - return IRQ_NONE; \ - \ guard(spinlock_irqsave)(&pirq->mask_lock); \ old_state = atomic_cmpxchg(&pirq->state, \ PANTHOR_IRQ_STATE_ACTIVE, \ @@ -522,6 +519,13 @@ static irqreturn_t panthor_ ## __name ## _irq_raw_handler(int irq, void *data) if (old_state != PANTHOR_IRQ_STATE_ACTIVE) \ return IRQ_NONE; \ \ + if (!gpu_read(pirq->iomem, INT_STAT)) { \ + atomic_cmpxchg(&pirq->state, \ + PANTHOR_IRQ_STATE_PROCESSING, \ + PANTHOR_IRQ_STATE_ACTIVE); \ + return IRQ_NONE; \ + } \ + \ gpu_write(pirq->iomem, INT_MASK, 0); \ return IRQ_WAKE_THREAD; \ } \ @@ -584,14 +588,15 @@ static inline void panthor_ ## __name ## _irq_resume(struct panthor_irq *pirq) \ static int panthor_request_ ## __name ## _irq(struct panthor_device *ptdev, \ struct panthor_irq *pirq, \ - int irq, u32 mask, void __iomem *iomem) \ + int irq, void __iomem *iomem) \ { \ pirq->ptdev = ptdev; \ pirq->irq = irq; \ - pirq->mask = mask; \ + pirq->mask = 0; \ pirq->iomem = iomem; \ spin_lock_init(&pirq->mask_lock); \ - panthor_ ## __name ## _irq_resume(pirq); \ + atomic_set(&pirq->state, PANTHOR_IRQ_STATE_SUSPENDED); \ + gpu_write(pirq->iomem, INT_MASK, 0); \ \ return devm_request_threaded_irq(ptdev->base.dev, irq, \ panthor_ ## __name ## _irq_raw_handler, \ diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 986151681b24..de8e6689a869 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -1279,9 +1279,7 @@ void panthor_fw_unplug(struct panthor_device *ptdev) if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) { /* Make sure the IRQ handler cannot be called after that point. */ - if (ptdev->fw->irq.irq) - panthor_job_irq_suspend(&ptdev->fw->irq); - + panthor_job_irq_suspend(&ptdev->fw->irq); panthor_fw_stop(ptdev); } @@ -1476,7 +1474,7 @@ int panthor_fw_init(struct panthor_device *ptdev) if (irq <= 0) return -ENODEV; - ret = panthor_request_job_irq(ptdev, &fw->irq, irq, 0, + ret = panthor_request_job_irq(ptdev, &fw->irq, irq, ptdev->iomem + JOB_INT_BASE); if (ret) { drm_err(&ptdev->base, "failed to request job irq"); diff --git a/drivers/gpu/drm/panthor/panthor_gpu.c b/drivers/gpu/drm/panthor/panthor_gpu.c index e52c5675981f..c013d6bf9a59 100644 --- a/drivers/gpu/drm/panthor/panthor_gpu.c +++ b/drivers/gpu/drm/panthor/panthor_gpu.c @@ -170,11 +170,12 @@ int panthor_gpu_init(struct panthor_device *ptdev) return irq; ret = panthor_request_gpu_irq(ptdev, &ptdev->gpu->irq, irq, - GPU_INTERRUPTS_MASK, ptdev->iomem + GPU_INT_BASE); if (ret) return ret; + panthor_gpu_irq_enable_events(&ptdev->gpu->irq, GPU_INTERRUPTS_MASK); + panthor_gpu_irq_resume(&ptdev->gpu->irq); return 0; } diff --git a/drivers/gpu/drm/panthor/panthor_mmu.c b/drivers/gpu/drm/panthor/panthor_mmu.c index 1f6b9242279c..f45ef5824ff2 100644 --- a/drivers/gpu/drm/panthor/panthor_mmu.c +++ b/drivers/gpu/drm/panthor/panthor_mmu.c @@ -3405,7 +3405,6 @@ int panthor_mmu_init(struct panthor_device *ptdev) return -ENODEV; ret = panthor_request_mmu_irq(ptdev, &mmu->irq, irq, - panthor_mmu_fault_mask(ptdev, ~0), ptdev->iomem + MMU_INT_BASE); if (ret) return ret; @@ -3423,7 +3422,13 @@ int panthor_mmu_init(struct panthor_device *ptdev) ptdev->gpu_info.mmu_features |= BITS_PER_LONG; } - return drmm_add_action_or_reset(&ptdev->base, panthor_mmu_release_wq, mmu->vm.wq); + ret = drmm_add_action_or_reset(&ptdev->base, panthor_mmu_release_wq, mmu->vm.wq); + if (ret) + return ret; + + panthor_mmu_irq_enable_events(&mmu->irq, panthor_mmu_fault_mask(ptdev, ~0)); + panthor_mmu_irq_resume(&mmu->irq); + return 0; } #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpu/drm/panthor/panthor_pwr.c b/drivers/gpu/drm/panthor/panthor_pwr.c index 7c7f424a1436..f2c2c3000590 100644 --- a/drivers/gpu/drm/panthor/panthor_pwr.c +++ b/drivers/gpu/drm/panthor/panthor_pwr.c @@ -453,7 +453,8 @@ void panthor_pwr_unplug(struct panthor_device *ptdev) return; /* Make sure the IRQ handler is not running after that point. */ - panthor_pwr_irq_suspend(&ptdev->pwr->irq); + if (!IS_ENABLED(CONFIG_PM) || pm_runtime_active(ptdev->base.dev)) + panthor_pwr_irq_suspend(&ptdev->pwr->irq); /* Wake-up all waiters. */ spin_lock_irqsave(&ptdev->pwr->reqs_lock, flags); @@ -483,12 +484,13 @@ int panthor_pwr_init(struct panthor_device *ptdev) if (irq < 0) return irq; - err = panthor_request_pwr_irq( - ptdev, &pwr->irq, irq, PWR_INTERRUPTS_MASK, - pwr->iomem + PWR_INT_BASE); + err = panthor_request_pwr_irq(ptdev, &pwr->irq, irq, + pwr->iomem + PWR_INT_BASE); if (err) return err; + panthor_pwr_irq_enable_events(&pwr->irq, PWR_INTERRUPTS_MASK); + panthor_pwr_irq_resume(&pwr->irq); return 0; } diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 5b34032deff8..298b046c95ed 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1057,7 +1057,8 @@ group_unbind_locked(struct panthor_group *group) /* Tiler OOM events will be re-issued next time the group is scheduled. */ atomic_set(&group->tiler_oom, 0); - cancel_work(&group->tiler_oom_work); + if (cancel_work(&group->tiler_oom_work)) + group_put(group); for (u32 i = 0; i < group->queue_count; i++) group->queues[i]->doorbell_id = -1; @@ -1151,15 +1152,14 @@ queue_suspend_timeout_locked(struct panthor_queue *queue) static void queue_suspend_timeout(struct panthor_queue *queue) { - spin_lock(&queue->fence_ctx.lock); + guard(spinlock_irqsave)(&queue->fence_ctx.lock); queue_suspend_timeout_locked(queue); - spin_unlock(&queue->fence_ctx.lock); } static void queue_resume_timeout(struct panthor_queue *queue) { - spin_lock(&queue->fence_ctx.lock); + guard(spinlock_irqsave)(&queue->fence_ctx.lock); if (queue_timeout_is_suspended(queue)) { mod_delayed_work(queue->scheduler.timeout_wq, @@ -1168,8 +1168,6 @@ queue_resume_timeout(struct panthor_queue *queue) queue->timeout.remaining = MAX_SCHEDULE_TIMEOUT; } - - spin_unlock(&queue->fence_ctx.lock); } /** @@ -1542,7 +1540,7 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev, u64 cs_extract = queue->iface.output->extract; struct panthor_job *job; - spin_lock(&queue->fence_ctx.lock); + guard(spinlock_irqsave)(&queue->fence_ctx.lock); list_for_each_entry(job, &queue->fence_ctx.in_flight_jobs, node) { if (cs_extract >= job->ringbuf.end) continue; @@ -1552,7 +1550,6 @@ cs_slot_process_fault_event_locked(struct panthor_device *ptdev, dma_fence_set_error(job->done_fence, -EINVAL); } - spin_unlock(&queue->fence_ctx.lock); } if (group) { @@ -1604,7 +1601,10 @@ static int group_process_tiler_oom(struct panthor_group *group, u32 cs_id) if (unlikely(csg_id < 0)) return 0; - if (IS_ERR(heaps) || frag_end > vt_end || vt_end >= vt_start) { + if (IS_ERR(heaps)) { + ret = -EINVAL; + heaps = NULL; + } else if (frag_end > vt_end || vt_end >= vt_start) { ret = -EINVAL; } else { /* We do the allocation without holding the scheduler lock to avoid @@ -2183,13 +2183,13 @@ group_term_post_processing(struct panthor_group *group) if (!queue) continue; - spin_lock(&queue->fence_ctx.lock); - list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) { - list_move_tail(&job->node, &faulty_jobs); - dma_fence_set_error(job->done_fence, err); - dma_fence_signal_locked(job->done_fence); + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) { + list_for_each_entry_safe(job, tmp, &queue->fence_ctx.in_flight_jobs, node) { + list_move_tail(&job->node, &faulty_jobs); + dma_fence_set_error(job->done_fence, err); + dma_fence_signal_locked(job->done_fence); + } } - spin_unlock(&queue->fence_ctx.lock); /* Manually update the syncobj seqno to unblock waiters. */ syncobj = group->syncobjs->kmap + (i * sizeof(*syncobj)); @@ -2368,7 +2368,13 @@ tick_ctx_apply(struct panthor_scheduler *sched, struct panthor_sched_tick_ctx *c csg_iface = panthor_fw_get_csg_iface(ptdev, csg_id); csg_slot = &sched->csg_slots[csg_id]; - group_bind_locked(group, csg_id); + ret = group_bind_locked(group, csg_id); + if (ret) { + panthor_device_schedule_reset(ptdev); + ctx->csg_upd_failed_mask |= BIT(csg_id); + return; + } + csg_slot_prog_locked(ptdev, csg_id, new_csg_prio--); csgs_upd_ctx_queue_reqs(ptdev, &upd_ctx, csg_id, group->state == PANTHOR_CS_GROUP_SUSPENDED ? @@ -2668,7 +2674,14 @@ static void sched_resume_tick(struct panthor_device *ptdev) else delay_jiffies = 0; - sched_queue_delayed_work(sched, tick, delay_jiffies); + /* We schedule immediate ticks when we need to process events on CSGs, + * but those don't change the resched_target because we want the other + * groups to stay scheduled for the remaining of the GPU timeslot they + * were given. Make sure those immediate ticks don't get overruled by + * a sched_queue_delayed_work() that would delay the tick execution. + */ + if (!delayed_work_pending(&sched->tick_work)) + sched_queue_delayed_work(sched, tick, delay_jiffies); } static void group_schedule_locked(struct panthor_group *group, u32 queue_mask) @@ -3049,39 +3062,39 @@ static bool queue_check_job_completion(struct panthor_queue *queue) LIST_HEAD(done_jobs); cookie = dma_fence_begin_signalling(); - spin_lock(&queue->fence_ctx.lock); - list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) { - if (!syncobj) { - struct panthor_group *group = job->group; + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) { + list_for_each_entry_safe(job, job_tmp, &queue->fence_ctx.in_flight_jobs, node) { + if (!syncobj) { + struct panthor_group *group = job->group; - syncobj = group->syncobjs->kmap + - (job->queue_idx * sizeof(*syncobj)); - } + syncobj = group->syncobjs->kmap + + (job->queue_idx * sizeof(*syncobj)); + } - if (syncobj->seqno < job->done_fence->seqno) - break; + if (syncobj->seqno < job->done_fence->seqno) + break; - list_move_tail(&job->node, &done_jobs); - dma_fence_signal_locked(job->done_fence); - } + list_move_tail(&job->node, &done_jobs); + dma_fence_signal_locked(job->done_fence); + } - if (list_empty(&queue->fence_ctx.in_flight_jobs)) { - /* If we have no job left, we cancel the timer, and reset remaining - * time to its default so it can be restarted next time - * queue_resume_timeout() is called. - */ - queue_suspend_timeout_locked(queue); + if (list_empty(&queue->fence_ctx.in_flight_jobs)) { + /* If we have no job left, we cancel the timer, and reset remaining + * time to its default so it can be restarted next time + * queue_resume_timeout() is called. + */ + queue_suspend_timeout_locked(queue); - /* If there's no job pending, we consider it progress to avoid a - * spurious timeout if the timeout handler and the sync update - * handler raced. - */ - progress = true; - } else if (!list_empty(&done_jobs)) { - queue_reset_timeout_locked(queue); - progress = true; + /* If there's no job pending, we consider it progress to avoid a + * spurious timeout if the timeout handler and the sync update + * handler raced. + */ + progress = true; + } else if (!list_empty(&done_jobs)) { + queue_reset_timeout_locked(queue); + progress = true; + } } - spin_unlock(&queue->fence_ctx.lock); dma_fence_end_signalling(cookie); list_for_each_entry_safe(job, job_tmp, &done_jobs, node) { @@ -3346,9 +3359,8 @@ queue_run_job(struct drm_sched_job *sched_job) job->ringbuf.end = job->ringbuf.start + (instrs.count * sizeof(u64)); panthor_job_get(&job->base); - spin_lock(&queue->fence_ctx.lock); - list_add_tail(&job->node, &queue->fence_ctx.in_flight_jobs); - spin_unlock(&queue->fence_ctx.lock); + scoped_guard(spinlock_irqsave, &queue->fence_ctx.lock) + list_add_tail(&job->node, &queue->fence_ctx.in_flight_jobs); /* Make sure the ring buffer is updated before the INSERT * register. diff --git a/drivers/gpu/drm/radeon/r600_dpm.c b/drivers/gpu/drm/radeon/r600_dpm.c index 83f1ae31cbdb..9755e717ca8b 100644 --- a/drivers/gpu/drm/radeon/r600_dpm.c +++ b/drivers/gpu/drm/radeon/r600_dpm.c @@ -932,7 +932,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev) ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk, dep_table); if (ret) { - kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); + r600_free_extended_power_table(rdev); return ret; } } @@ -943,8 +943,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev) ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk, dep_table); if (ret) { - kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); - kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries); + r600_free_extended_power_table(rdev); return ret; } } @@ -955,9 +954,7 @@ int r600_parse_extended_power_table(struct radeon_device *rdev) ret = r600_parse_clk_voltage_dep_table(&rdev->pm.dpm.dyn_state.mvdd_dependency_on_mclk, dep_table); if (ret) { - kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_sclk.entries); - kfree(rdev->pm.dpm.dyn_state.vddci_dependency_on_mclk.entries); - kfree(rdev->pm.dpm.dyn_state.vddc_dependency_on_mclk.entries); + r600_free_extended_power_table(rdev); return ret; } } @@ -1296,17 +1293,29 @@ void r600_free_extended_power_table(struct radeon_device *rdev) struct radeon_dpm_dynamic_state *dyn_state = &rdev->pm.dpm.dyn_state; kfree(dyn_state->vddc_dependency_on_sclk.entries); + dyn_state->vddc_dependency_on_sclk.entries = NULL; kfree(dyn_state->vddci_dependency_on_mclk.entries); + dyn_state->vddci_dependency_on_mclk.entries = NULL; kfree(dyn_state->vddc_dependency_on_mclk.entries); + dyn_state->vddc_dependency_on_mclk.entries = NULL; kfree(dyn_state->mvdd_dependency_on_mclk.entries); + dyn_state->mvdd_dependency_on_mclk.entries = NULL; kfree(dyn_state->cac_leakage_table.entries); + dyn_state->cac_leakage_table.entries = NULL; kfree(dyn_state->phase_shedding_limits_table.entries); + dyn_state->phase_shedding_limits_table.entries = NULL; kfree(dyn_state->ppm_table); + dyn_state->ppm_table = NULL; kfree(dyn_state->cac_tdp_table); + dyn_state->cac_tdp_table = NULL; kfree(dyn_state->vce_clock_voltage_dependency_table.entries); + dyn_state->vce_clock_voltage_dependency_table.entries = NULL; kfree(dyn_state->uvd_clock_voltage_dependency_table.entries); + dyn_state->uvd_clock_voltage_dependency_table.entries = NULL; kfree(dyn_state->samu_clock_voltage_dependency_table.entries); + dyn_state->samu_clock_voltage_dependency_table.entries = NULL; kfree(dyn_state->acp_clock_voltage_dependency_table.entries); + dyn_state->acp_clock_voltage_dependency_table.entries = NULL; } enum radeon_pcie_gen r600_get_pcie_gen_support(struct radeon_device *rdev, diff --git a/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c index c0176e5de9a8..8e7fac7a893b 100644 --- a/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c +++ b/drivers/gpu/drm/renesas/rcar-du/rcar_dw_hdmi.c @@ -7,7 +7,6 @@ * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c index 9dc207c2d20d..6db88665060d 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c @@ -10,7 +10,6 @@ #include <linux/clk.h> #include <linux/component.h> #include <linux/media-bus-format.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/pm_runtime.h> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c index 45a6dae4de31..33ff195bd2e6 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi-rockchip.c @@ -7,7 +7,6 @@ #include <linux/err.h> #include <linux/hw_bitfield.h> #include <linux/mfd/syscon.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/regmap.h> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c index 1d8473a6dfd1..c44648e1bc0d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c @@ -8,7 +8,6 @@ #include <linux/kernel.h> #include <linux/component.h> #include <linux/hw_bitfield.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/of.h> #include <drm/drm_blend.h> diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index b2f8ebf90968..72301e0a3e0a 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -5,7 +5,6 @@ */ #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> diff --git a/drivers/gpu/drm/sprd/sprd_drm.c b/drivers/gpu/drm/sprd/sprd_drm.c index ceacdcb7c566..c2fd5380a834 100644 --- a/drivers/gpu/drm/sprd/sprd_drm.c +++ b/drivers/gpu/drm/sprd/sprd_drm.c @@ -5,7 +5,6 @@ #include <linux/component.h> #include <linux/dma-mapping.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/of_graph.h> diff --git a/drivers/gpu/drm/sti/sti_hda.c b/drivers/gpu/drm/sti/sti_hda.c index 360a88ca8f0c..b9f8f68f01f9 100644 --- a/drivers/gpu/drm/sti/sti_hda.c +++ b/drivers/gpu/drm/sti/sti_hda.c @@ -7,7 +7,6 @@ #include <linux/clk.h> #include <linux/component.h> #include <linux/io.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/seq_file.h> diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index 144b7cda989a..0967b9b3451d 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -11,7 +11,6 @@ #include <linux/aperture.h> #include <linux/component.h> #include <linux/dma-mapping.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c index 58eae6804cc8..a3eae5b2c26e 100644 --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -10,7 +10,6 @@ #include <linux/clk-provider.h> #include <linux/iopoll.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> diff --git a/drivers/gpu/drm/sun4i/sun6i_drc.c b/drivers/gpu/drm/sun4i/sun6i_drc.c index 310c7e0daede..0050c1b46ea8 100644 --- a/drivers/gpu/drm/sun4i/sun6i_drc.c +++ b/drivers/gpu/drm/sun4i/sun6i_drc.c @@ -8,7 +8,6 @@ #include <linux/clk.h> #include <linux/component.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/regmap.h> #include <linux/reset.h> diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index e7f675c15c20..1d6c9a423a41 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -6,7 +6,6 @@ /* LCDC DRM driver, based on da8xx-fb */ -#include <linux/mod_devicetable.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> diff --git a/drivers/gpu/drm/tiny/sharp-memory.c b/drivers/gpu/drm/tiny/sharp-memory.c index 506e6432e70d..7efd7b567f3b 100644 --- a/drivers/gpu/drm/tiny/sharp-memory.c +++ b/drivers/gpu/drm/tiny/sharp-memory.c @@ -21,7 +21,6 @@ #include <linux/delay.h> #include <linux/gpio/consumer.h> #include <linux/kthread.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/pwm.h> diff --git a/drivers/gpu/drm/vc4/vc4_dpi.c b/drivers/gpu/drm/vc4/vc4_dpi.c index 2afc88394d64..53f36626a50d 100644 --- a/drivers/gpu/drm/vc4/vc4_dpi.c +++ b/drivers/gpu/drm/vc4/vc4_dpi.c @@ -23,7 +23,6 @@ #include <linux/clk.h> #include <linux/component.h> #include <linux/media-bus-format.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include "vc4_drv.h" #include "vc4_regs.h" diff --git a/drivers/gpu/drm/vc4/vc4_txp.c b/drivers/gpu/drm/vc4/vc4_txp.c index 66b6f2acf862..bc3f366fc3e6 100644 --- a/drivers/gpu/drm/vc4/vc4_txp.c +++ b/drivers/gpu/drm/vc4/vc4_txp.c @@ -9,7 +9,6 @@ #include <linux/clk.h> #include <linux/component.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> diff --git a/drivers/gpu/drm/virtio/virtgpu_vq.c b/drivers/gpu/drm/virtio/virtgpu_vq.c index 68d097ad9d1d..905b1f42cd98 100644 --- a/drivers/gpu/drm/virtio/virtgpu_vq.c +++ b/drivers/gpu/drm/virtio/virtgpu_vq.c @@ -937,7 +937,8 @@ static int virtio_get_edid_block(void *data, u8 *buf, struct virtio_gpu_resp_edid *resp = data; size_t start = block * EDID_LENGTH; - if (start + len > le32_to_cpu(resp->size)) + if (start + len > le32_to_cpu(resp->size) || + start + len > sizeof(resp->edid)) return -EINVAL; memcpy(buf, resp->edid + start, len); return 0; diff --git a/drivers/gpu/drm/xe/Kconfig.profile b/drivers/gpu/drm/xe/Kconfig.profile index 7530df998148..e07517d120e0 100644 --- a/drivers/gpu/drm/xe/Kconfig.profile +++ b/drivers/gpu/drm/xe/Kconfig.profile @@ -1,50 +1,71 @@ # SPDX-License-Identifier: GPL-2.0-only config DRM_XE_JOB_TIMEOUT_MAX - int "Default max job timeout (ms)" + int "Hard upper limit for job timeout (ms)" default 10000 # milliseconds help - Configures the default max job timeout after which job will - be forcefully taken away from scheduler. + Absolute upper bound (in milliseconds) for the per-engine-class job + timeout. This is the maximum value that can be written to the sysfs + job_timeout_ms knob, regardless of privileges. To raise this ceiling, + increase this value and rebuild the kernel. config DRM_XE_JOB_TIMEOUT_MIN - int "Default min job timeout (ms)" + int "Hard lower limit for job timeout (ms)" default 1 # milliseconds help - Configures the default min job timeout after which job will - be forcefully taken away from scheduler. + Absolute lower bound (in milliseconds) for the per-engine-class job + timeout. This is the minimum value that can be written to the sysfs + job_timeout_ms knob, regardless of privileges. + + Note: the job timeout default (5000 ms) is hardcoded in the driver + and is not configurable here. Use the sysfs job_timeout_ms knob at + runtime to change the engine-class default. config DRM_XE_TIMESLICE_MAX - int "Default max timeslice duration (us)" + int "Hard upper limit for timeslice duration (us)" default 10000000 # microseconds help - Configures the default max timeslice duration between multiple - contexts by guc scheduling. + Absolute upper bound (in microseconds) for the timeslice duration. + This caps both the sysfs timeslice_duration_us knob and the value + accepted via the DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE UAPI for + processes with CAP_SYS_NICE when DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT + is enabled. config DRM_XE_TIMESLICE_MIN - int "Default min timeslice duration (us)" + int "Hard lower limit for timeslice duration (us)" default 1 # microseconds help - Configures the default min timeslice duration between multiple - contexts by guc scheduling. + Absolute lower bound (in microseconds) for the timeslice duration. + This caps both the sysfs timeslice_duration_us knob and the value + accepted via the DRM_XE_EXEC_QUEUE_SET_PROPERTY_TIMESLICE UAPI for + processes with CAP_SYS_NICE when DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT + is enabled. config DRM_XE_PREEMPT_TIMEOUT - int "Preempt timeout (us, jiffy granularity)" + int "Default preempt timeout (us, jiffy granularity)" default 640000 # microseconds help - How long to wait (in microseconds) for a preemption event to occur - when submitting a new context. If the current context does not hit - an arbitration point and yield to HW before the timer expires, the - HW will be reset to allow the more important context to execute. + Initial per-engine-class preemption timeout (in microseconds). This + is the value the driver programs at boot; it can be changed at + runtime via the sysfs preempt_timeout_us knob. + + This is how long the driver waits for the current context to reach + an arbitration point and yield the GPU voluntarily when a + higher-priority context becomes runnable. If the context does not + yield before the timer expires, the HW is reset to allow the + higher-priority context to execute. + + The range userspace may write via sysfs is bounded by + DRM_XE_PREEMPT_TIMEOUT_MIN and DRM_XE_PREEMPT_TIMEOUT_MAX. config DRM_XE_PREEMPT_TIMEOUT_MAX - int "Default max preempt timeout (us)" + int "Hard upper limit for preempt timeout (us)" default 10000000 # microseconds help - Configures the default max preempt timeout after which context - will be forcefully taken away and higher priority context will - run. + Absolute upper bound (in microseconds) for the per-engine-class + preemption timeout. This is the maximum value that can be written to + the sysfs preempt_timeout_us knob, regardless of privileges. config DRM_XE_PREEMPT_TIMEOUT_MIN - int "Default min preempt timeout (us)" + int "Hard lower limit for preempt timeout (us)" default 1 # microseconds help - Configures the default min preempt timeout after which context - will be forcefully taken away and higher priority context will - run. + Absolute lower bound (in microseconds) for the per-engine-class + preemption timeout. This is the minimum value that can be written to + the sysfs preempt_timeout_us knob, regardless of privileges. config DRM_XE_ENABLE_SCHEDTIMEOUT_LIMIT bool "Default configuration of limitation on scheduler timeout" default y diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 8e7b146880f4..6d728f8c4c39 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -101,6 +101,7 @@ xe-y += xe_bb.o \ xe_page_reclaim.o \ xe_pat.o \ xe_pci.o \ + xe_pci_error.o \ xe_pci_rebar.o \ xe_pcode.o \ xe_pm.o \ @@ -278,6 +279,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/intel_dp_aux.o \ i915-display/intel_dp_aux_backlight.o \ i915-display/intel_dp_hdcp.o \ + i915-display/intel_dp_link_caps.o \ i915-display/intel_dp_link_training.o \ i915-display/intel_dp_mst.o \ i915-display/intel_dp_test.o \ diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 810d93fefcbc..92a4573db28a 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -19,7 +19,6 @@ #include "intel_acpi.h" #include "intel_audio.h" -#include "intel_bw.h" #include "intel_display.h" #include "intel_display_core.h" #include "intel_display_device.h" @@ -29,8 +28,6 @@ #include "intel_dmc.h" #include "intel_dmc_wl.h" #include "intel_dp.h" -#include "intel_dram.h" -#include "intel_encoder.h" #include "intel_fbdev.h" #include "intel_hdcp.h" #include "intel_hotplug.h" @@ -84,10 +81,8 @@ static void xe_display_fini_early(void *arg) if (!xe->info.probe_display) return; - intel_hpd_cancel_work(display); - intel_display_driver_remove_nogem(display); intel_display_driver_remove_noirq(display); - intel_opregion_cleanup(display); + intel_display_driver_remove_nogem(display); intel_display_power_cleanup(display); } @@ -113,22 +108,9 @@ int xe_display_init_early(struct xe_device *xe) return 0; } - /* Early display init.. */ - intel_opregion_setup(display); - - /* - * Fill the dram structure to get the system dram info. This will be - * used for memory latency calculation. - */ - err = intel_dram_detect(display); - if (err) - goto err_opregion; - - intel_bw_init_hw(display); - err = intel_display_driver_probe_noirq(display); if (err) - goto err_opregion; + return err; err = intel_display_driver_probe_nogem(display); if (err) @@ -138,8 +120,7 @@ int xe_display_init_early(struct xe_device *xe) err_noirq: intel_display_driver_remove_noirq(display); intel_display_power_cleanup(display); -err_opregion: - intel_opregion_cleanup(display); + return err; } @@ -191,6 +172,30 @@ void xe_display_unregister(struct xe_device *xe) intel_display_driver_unregister(display); } +void xe_display_shutdown(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + intel_display_driver_shutdown(display); + + intel_opregion_suspend(display, PCI_D3cold); + + intel_dmc_suspend(display); +} + +void xe_display_shutdown_late(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + intel_display_driver_shutdown_late(display); +} + /* IRQ-related functions */ void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) @@ -244,57 +249,62 @@ static bool suspend_to_idle(void) return false; } -static void xe_display_enable_d3cold(struct xe_device *xe) +void xe_display_pm_suspend(struct xe_device *xe) { struct intel_display *display = xe->display; + bool s2idle = suspend_to_idle(); if (!xe->info.probe_display) return; - /* - * We do a lot of poking in a lot of registers, make sure they work - * properly. - */ - intel_display_power_disable(display); - - intel_display_flush_cleanup_work(display); + intel_display_driver_pm_suspend(display); - intel_opregion_suspend(display, PCI_D3cold); + intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold); intel_dmc_suspend(display); +} - if (intel_display_device_present(display)) - intel_hpd_poll_enable(display); +void xe_display_pm_suspend_late(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + bool s2idle = suspend_to_idle(); + + if (!xe->info.probe_display) + return; + + intel_display_driver_pm_suspend_late(display, s2idle); } -static void xe_display_disable_d3cold(struct xe_device *xe) +void xe_display_pm_resume_early(struct xe_device *xe) { struct intel_display *display = xe->display; if (!xe->info.probe_display) return; - intel_dmc_resume(display); + intel_display_driver_pm_resume_early(display); +} - if (intel_display_device_present(display)) - drm_mode_config_reset(&xe->drm); +void xe_display_pm_resume(struct xe_device *xe) +{ + struct intel_display *display = xe->display; - intel_display_driver_init_hw(display); + if (!xe->info.probe_display) + return; - intel_hpd_init(display); + intel_dmc_resume(display); if (intel_display_device_present(display)) - intel_hpd_poll_disable(display); + drm_mode_config_reset(&xe->drm); - intel_opregion_resume(display); + intel_display_driver_init_hw(display); - intel_display_power_enable(display); + intel_display_driver_pm_resume(display); } -void xe_display_pm_suspend(struct xe_device *xe) +static void xe_display_enable_d3cold(struct xe_device *xe) { struct intel_display *display = xe->display; - bool s2idle = suspend_to_idle(); if (!xe->info.probe_display) return; @@ -304,60 +314,39 @@ void xe_display_pm_suspend(struct xe_device *xe) * properly. */ intel_display_power_disable(display); - drm_client_dev_suspend(&xe->drm); - - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&xe->drm); - intel_display_driver_disable_user_access(display); - intel_display_driver_suspend(display); - } intel_display_flush_cleanup_work(display); - intel_encoder_block_all_hpds(display); - - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) { - intel_display_driver_suspend_access(display); - intel_encoder_suspend_all(display); - } - - intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold); + intel_opregion_suspend(display, PCI_D3cold); intel_dmc_suspend(display); + + if (intel_display_device_present(display)) + intel_hpd_poll_enable(display); } -void xe_display_pm_shutdown(struct xe_device *xe) +static void xe_display_disable_d3cold(struct xe_device *xe) { struct intel_display *display = xe->display; if (!xe->info.probe_display) return; - intel_display_power_disable(display); - drm_client_dev_suspend(&xe->drm); + intel_dmc_resume(display); - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&xe->drm); - intel_display_driver_disable_user_access(display); - intel_display_driver_suspend(display); - } + if (intel_display_device_present(display)) + drm_mode_config_reset(&xe->drm); - intel_display_flush_cleanup_work(display); - intel_dp_mst_suspend(display); - intel_encoder_block_all_hpds(display); - intel_hpd_cancel_work(display); + intel_display_driver_init_hw(display); - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); + intel_hpd_init(display); - intel_encoder_suspend_all(display); - intel_encoder_shutdown_all(display); + if (intel_display_device_present(display)) + intel_hpd_poll_disable(display); - intel_opregion_suspend(display, PCI_D3cold); + intel_opregion_resume(display); - intel_dmc_suspend(display); + intel_display_power_enable(display); } void xe_display_pm_runtime_suspend(struct xe_device *xe) @@ -375,17 +364,6 @@ void xe_display_pm_runtime_suspend(struct xe_device *xe) intel_hpd_poll_enable(display); } -void xe_display_pm_suspend_late(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - bool s2idle = suspend_to_idle(); - - if (!xe->info.probe_display) - return; - - intel_display_power_suspend_late(display, s2idle); -} - void xe_display_pm_runtime_suspend_late(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -404,68 +382,6 @@ void xe_display_pm_runtime_suspend_late(struct xe_device *xe) intel_dmc_wl_flush_release_work(display); } -void xe_display_pm_shutdown_late(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - /* - * The only requirement is to reboot with display DC states disabled, - * for now leaving all display power wells in the INIT power domain - * enabled. - */ - intel_display_power_driver_remove(display); -} - -void xe_display_pm_resume_early(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - intel_display_power_resume_early(display); -} - -void xe_display_pm_resume(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - intel_dmc_resume(display); - - if (intel_display_device_present(display)) - drm_mode_config_reset(&xe->drm); - - intel_display_driver_init_hw(display); - - if (intel_display_device_present(display)) - intel_display_driver_resume_access(display); - - intel_hpd_init(display); - - intel_encoder_unblock_all_hpds(display); - - if (intel_display_device_present(display)) { - intel_display_driver_resume(display); - drm_kms_helper_poll_enable(&xe->drm); - intel_display_driver_enable_user_access(display); - } - - if (intel_display_device_present(display)) - intel_hpd_poll_disable(display); - - intel_opregion_resume(display); - - drm_client_dev_resume(&xe->drm); - - intel_display_power_enable(display); -} - void xe_display_pm_runtime_resume(struct xe_device *xe) { struct intel_display *display = xe->display; diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h index 60291cb154df..e5f9aed93206 100644 --- a/drivers/gpu/drm/xe/display/xe_display.h +++ b/drivers/gpu/drm/xe/display/xe_display.h @@ -29,15 +29,16 @@ int xe_display_init(struct xe_device *xe); void xe_display_register(struct xe_device *xe); void xe_display_unregister(struct xe_device *xe); +void xe_display_shutdown(struct xe_device *xe); +void xe_display_shutdown_late(struct xe_device *xe); + void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl); void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir); void xe_display_irq_reset(struct xe_device *xe); void xe_display_irq_postinstall(struct xe_device *xe); void xe_display_pm_suspend(struct xe_device *xe); -void xe_display_pm_shutdown(struct xe_device *xe); void xe_display_pm_suspend_late(struct xe_device *xe); -void xe_display_pm_shutdown_late(struct xe_device *xe); void xe_display_pm_resume_early(struct xe_device *xe); void xe_display_pm_resume(struct xe_device *xe); void xe_display_pm_runtime_suspend(struct xe_device *xe); @@ -65,15 +66,16 @@ static inline int xe_display_init(struct xe_device *xe) { return 0; } static inline void xe_display_register(struct xe_device *xe) {} static inline void xe_display_unregister(struct xe_device *xe) {} +static inline void xe_display_shutdown(struct xe_device *xe) {} +static inline void xe_display_shutdown_late(struct xe_device *xe) {} + static inline void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) {} static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) {} static inline void xe_display_irq_reset(struct xe_device *xe) {} static inline void xe_display_irq_postinstall(struct xe_device *xe) {} static inline void xe_display_pm_suspend(struct xe_device *xe) {} -static inline void xe_display_pm_shutdown(struct xe_device *xe) {} static inline void xe_display_pm_suspend_late(struct xe_device *xe) {} -static inline void xe_display_pm_shutdown_late(struct xe_device *xe) {} static inline void xe_display_pm_resume_early(struct xe_device *xe) {} static inline void xe_display_pm_resume(struct xe_device *xe) {} static inline void xe_display_pm_runtime_suspend(struct xe_device *xe) {} diff --git a/drivers/gpu/drm/xe/display/xe_display_bo.c b/drivers/gpu/drm/xe/display/xe_display_bo.c index 7fbac223b097..8953da0136dc 100644 --- a/drivers/gpu/drm/xe/display/xe_display_bo.c +++ b/drivers/gpu/drm/xe/display/xe_display_bo.c @@ -48,7 +48,8 @@ static int xe_display_bo_framebuffer_init(struct drm_gem_object *obj, if (ret) goto err; - if (!(bo->flags & XE_BO_FLAG_FORCE_WC)) { + if (!(bo->flags & XE_BO_FLAG_FORCE_WC) && + bo->ttm.type != ttm_bo_type_sg) { /* * XE_BO_FLAG_FORCE_WC should ideally be set at creation, or is * automatically set when creating FB. We cannot change caching diff --git a/drivers/gpu/drm/xe/display/xe_fb_pin.c b/drivers/gpu/drm/xe/display/xe_fb_pin.c index f93c98bec5b5..5f4a0cd8deca 100644 --- a/drivers/gpu/drm/xe/display/xe_fb_pin.c +++ b/drivers/gpu/drm/xe/display/xe_fb_pin.c @@ -331,7 +331,8 @@ static struct i915_vma *__xe_pin_fb_vma(struct drm_gem_object *obj, bool is_dpt, int ret = 0; /* We reject creating !SCANOUT fb's, so this is weird.. */ - drm_WARN_ON(bo->ttm.base.dev, !(bo->flags & XE_BO_FLAG_FORCE_WC)); + drm_WARN_ON(bo->ttm.base.dev, !(bo->flags & XE_BO_FLAG_FORCE_WC) && + bo->ttm.type != ttm_bo_type_sg); if (!vma) return ERR_PTR(-ENODEV); diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c index bebb21d617f0..12c6fb99015d 100644 --- a/drivers/gpu/drm/xe/display/xe_panic.c +++ b/drivers/gpu/drm/xe/display/xe_panic.c @@ -5,8 +5,6 @@ #include <drm/drm_panic.h> #include <drm/intel/display_parent_interface.h> -#include "intel_display_types.h" -#include "intel_fb.h" #include "xe_bo.h" #include "xe_panic.h" #include "xe_res_cursor.h" @@ -16,6 +14,9 @@ struct intel_panic { struct iosys_map vmap; int page; + + struct xe_bo *bo; + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width); }; static void xe_panic_kunmap(struct intel_panic *panic) @@ -36,14 +37,13 @@ static void xe_panic_kunmap(struct intel_panic *panic) static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct intel_panic *panic = fb->panic; - struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base)); + struct intel_panic *panic = sb->private; + struct xe_bo *bo = panic->bo; unsigned int new_page; unsigned int offset; - if (fb->panic_tiling) - offset = fb->panic_tiling(sb->width, x, y); + if (panic->tiling) + offset = panic->tiling(sb->width, x, y); else offset = y * sb->pitch[0] + x * sb->format->cpp[0]; @@ -84,16 +84,22 @@ static struct intel_panic *xe_panic_alloc(void) return panic; } -static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) +static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base)); + struct xe_bo *bo = gem_to_xe_bo(obj); if (xe_bo_is_vram(bo) && !xe_bo_is_visible_vram(bo)) return -ENODEV; panic->page = -1; + panic->bo = bo; + panic->tiling = tiling; + + sb->private = panic; sb->set_pixel = xe_panic_page_set_pixel; + return 0; } diff --git a/drivers/gpu/drm/xe/tests/Makefile b/drivers/gpu/drm/xe/tests/Makefile index 0e3408f4952c..f7aa47f11a36 100644 --- a/drivers/gpu/drm/xe/tests/Makefile +++ b/drivers/gpu/drm/xe/tests/Makefile @@ -9,5 +9,6 @@ obj-$(CONFIG_DRM_XE_KUNIT_TEST) += xe_test.o xe_test-y = xe_test_mod.o \ xe_args_test.o \ xe_pci_test.o \ + xe_rtp_tables_test.o \ xe_rtp_test.o \ xe_wa_test.o diff --git a/drivers/gpu/drm/xe/tests/xe_pci.c b/drivers/gpu/drm/xe/tests/xe_pci.c index 9240aff779da..8df9029afcd3 100644 --- a/drivers/gpu/drm/xe/tests/xe_pci.c +++ b/drivers/gpu/drm/xe/tests/xe_pci.c @@ -311,40 +311,44 @@ const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc } EXPORT_SYMBOL_IF_KUNIT(xe_pci_id_gen_param); -static void fake_init_devid(struct xe_device *xe) +static int fake_probe_info(struct xe_device *xe, + const struct xe_device_desc *desc, + struct xe_pci_fake_data *data, + struct xe_probed_info *probed_info) { - /* Nothing to do, just keep zero. */ -} - -static int fake_read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, - u32 *ver, u32 *revid) -{ - struct kunit *test = kunit_get_current_test(); - struct xe_pci_fake_data *data = test->priv; + probed_info->tile_count = 1 + desc->max_remote_tiles; - if (type == GMDID_MEDIA) { - *ver = data->media_verx100; - *revid = xe_step_to_gmdid(data->step.media); + if (!data || desc->pre_gmdid_graphics_ip) { + probed_info->graphics_ip = desc->pre_gmdid_graphics_ip; + probed_info->media_ip = desc->pre_gmdid_media_ip; } else { - *ver = data->graphics_verx100; - *revid = xe_step_to_gmdid(data->step.graphics); + probed_info->graphics_ip = find_graphics_ip(data->graphics_verx100); + + if (data->media_verx100) { + probed_info->media_ip = find_media_ip(data->media_verx100); + xe_assert(xe, probed_info->media_ip); + } } - return 0; -} + xe_assert(xe, probed_info->graphics_ip); + if (!probed_info->graphics_ip) + return -ENODEV; -static void fake_xe_info_probe_tile_count(struct xe_device *xe) -{ - /* Nothing to do, just use the statically defined value. */ + if (data) + probed_info->step = data->step; + + return 0; } int xe_pci_fake_device_init(struct xe_device *xe) { struct kunit *test = kunit_get_current_test(); struct xe_pci_fake_data *data = test->priv; + struct xe_probed_info probed_info = {}; const struct pci_device_id *ent = pciidlist; const struct xe_device_desc *desc; const struct xe_subplatform_desc *subplatform_desc; + int err; if (!data) { desc = (const void *)ent->driver_data; @@ -374,13 +378,12 @@ done: xe->sriov.__mode = data && data->sriov_mode ? data->sriov_mode : XE_SRIOV_MODE_NONE; - kunit_activate_static_stub(test, init_devid, fake_init_devid); - kunit_activate_static_stub(test, read_gmdid, fake_read_gmdid); - kunit_activate_static_stub(test, xe_info_probe_tile_count, - fake_xe_info_probe_tile_count); + err = fake_probe_info(xe, desc, data, &probed_info); + if (err) + return err; - xe_info_init_early(xe, desc, subplatform_desc); - xe_info_init(xe, desc); + xe_info_init_early(xe, desc, subplatform_desc, &probed_info); + xe_info_init(xe, desc, &probed_info); return 0; } diff --git a/drivers/gpu/drm/xe/tests/xe_rtp_tables_test.c b/drivers/gpu/drm/xe/tests/xe_rtp_tables_test.c new file mode 100644 index 000000000000..7e2fc39ac62c --- /dev/null +++ b/drivers/gpu/drm/xe/tests/xe_rtp_tables_test.c @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright © 2026 Intel Corporation + */ + +#include <kunit/test.h> + +#include "xe_reg_whitelist.h" +#include "xe_rtp_types.h" +#include "xe_tuning.h" +#include "xe_wa.h" + +#define RTP_TABLE_PARAM(table) \ + static const void *table##_gen_params(struct kunit *test, \ + const void *prev, char *desc) \ + { \ + typeof((table.entries)[0]) *__next = prev ? \ + ((typeof(__next))prev) + 1 : (table.entries); \ + if (__next - table.entries < table.n_entries) { \ + scnprintf(desc, KUNIT_PARAM_DESC_SIZE, #table "/%s", __next->name); \ + return __next; \ + } \ + return NULL; \ + } + +static void xe_rtp_table_gt_test(struct kunit *test) +{ + const struct xe_rtp_entry_sr *entry = test->param_value; + + for (int i = 0; i < entry->n_rules; i++) { + KUNIT_EXPECT_TRUE(test, + entry->rules[i].match_type != XE_RTP_MATCH_ENGINE_CLASS || + entry->flags & XE_RTP_ENTRY_FLAG_FOREACH_ENGINE); + KUNIT_EXPECT_TRUE(test, + entry->rules[i].match_type != XE_RTP_MATCH_NOT_ENGINE_CLASS || + entry->flags & XE_RTP_ENTRY_FLAG_FOREACH_ENGINE); + } +} + +RTP_TABLE_PARAM(gt_was); +RTP_TABLE_PARAM(gt_tunings); + +static void xe_rtp_table_oob_test(struct kunit *test) +{ + const struct xe_rtp_entry *entry = test->param_value; + + for (int i = 0; i < entry->n_rules; i++) { + u8 match_type = entry->rules[i].match_type; + + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_ENGINE_CLASS); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_NOT_ENGINE_CLASS); + } +} + +RTP_TABLE_PARAM(oob_was); + +static void xe_rtp_table_dev_oob_test(struct kunit *test) +{ + const struct xe_rtp_entry *entry = test->param_value; + + for (int i = 0; i < entry->n_rules; i++) { + u8 match_type = entry->rules[i].match_type; + + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_ENGINE_CLASS); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_NOT_ENGINE_CLASS); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_GRAPHICS_VERSION); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_GRAPHICS_VERSION_RANGE); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_GRAPHICS_VERSION_ANY_GT); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_GRAPHICS_STEP); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_MEDIA_VERSION); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_MEDIA_VERSION_RANGE); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_MEDIA_VERSION_ANY_GT); + KUNIT_EXPECT_NE(test, match_type, XE_RTP_MATCH_MEDIA_STEP); + } +} + +RTP_TABLE_PARAM(device_oob_was); + +static void xe_rtp_table_missing_upper_bound_test(struct kunit *test) +{ + const struct xe_rtp_entry_sr *entry = test->param_value; + + for (int i = 0; i < entry->n_rules; i++) { + u8 match_type = entry->rules[i].match_type; + + KUNIT_EXPECT_FALSE(test, + match_type == XE_RTP_MATCH_GRAPHICS_VERSION_RANGE && + entry->rules[i].ver_end == XE_RTP_END_VERSION_UNDEFINED); + KUNIT_EXPECT_FALSE(test, + match_type == XE_RTP_MATCH_MEDIA_VERSION_RANGE && + entry->rules[i].ver_end == XE_RTP_END_VERSION_UNDEFINED); + } +} + +RTP_TABLE_PARAM(register_whitelist); + +static struct kunit_case xe_rtp_table_tests[] = { + KUNIT_CASE_PARAM(xe_rtp_table_gt_test, gt_was_gen_params), + KUNIT_CASE_PARAM(xe_rtp_table_gt_test, gt_tunings_gen_params), + KUNIT_CASE_PARAM(xe_rtp_table_oob_test, oob_was_gen_params), + KUNIT_CASE_PARAM(xe_rtp_table_dev_oob_test, device_oob_was_gen_params), + KUNIT_CASE_PARAM(xe_rtp_table_missing_upper_bound_test, + register_whitelist_gen_params), + {} +}; + +static struct kunit_suite xe_rtp_tables_test_suite = { + .name = "xe_rtp_tables_test", + .test_cases = xe_rtp_table_tests, +}; + +kunit_test_suite(xe_rtp_tables_test_suite); diff --git a/drivers/gpu/drm/xe/tests/xe_rtp_test.c b/drivers/gpu/drm/xe/tests/xe_rtp_test.c index 642f6e090ad0..367811621880 100644 --- a/drivers/gpu/drm/xe/tests/xe_rtp_test.c +++ b/drivers/gpu/drm/xe/tests/xe_rtp_test.c @@ -54,13 +54,13 @@ struct rtp_to_sr_test_case { unsigned long expected_count_sr_entries; unsigned int expected_sr_errors; unsigned long expected_active; - const struct xe_rtp_entry_sr *entries; + const struct xe_rtp_table_sr table; }; struct rtp_test_case { const char *name; unsigned long expected_active; - const struct xe_rtp_entry *entries; + const struct xe_rtp_table table; }; static bool fake_xe_gt_mcr_check_reg(struct xe_gt *gt, struct xe_reg reg) @@ -280,6 +280,11 @@ static void xe_rtp_rules_tests(struct kunit *test) KUNIT_EXPECT_EQ(test, err, param->expected_err); } +static u32 bits_2_3_set(struct xe_gt *gt, struct xe_hw_engine *hwe) +{ + return REG_BIT(2) | REG_BIT(3); +} + static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { { .name = "coalesce-same-reg", @@ -289,7 +294,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 1, /* Different bits on the same register: create a single entry */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -298,8 +303,30 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(1))) }, - {} - }, + ), + }, + { + .name = "coalesce-same-reg-literal-and-func", + .expected_reg = REGULAR_REG1, + .expected_set_bits = REG_BIT(0) | REG_BIT(1) | REG_BIT(2) | REG_BIT(3), + .expected_clr_bits = REG_BIT(0) | REG_BIT(1) | REG_BIT(2) | REG_BIT(3), + .expected_active = BIT(0) | BIT(1), + .expected_count_sr_entries = 1, + /* Different bits on the same register: create a single entry */ + .table = XE_RTP_TABLE_SR( + { XE_RTP_NAME("basic-1"), + XE_RTP_RULES(FUNC(match_yes)), + XE_RTP_ACTIONS(FIELD_SET(REGULAR_REG1, + REG_BIT(0) | REG_BIT(1), + REG_BIT(0) | REG_BIT(1))) + }, + { XE_RTP_NAME("basic-2"), + XE_RTP_RULES(FUNC(match_yes)), + XE_RTP_ACTIONS(FIELD_SET_FUNC(REGULAR_REG1, + REG_BIT(2) | REG_BIT(3), + bits_2_3_set)) + }, + ), }, { .name = "no-match-no-add", @@ -309,7 +336,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0), .expected_count_sr_entries = 1, /* Don't coalesce second entry since rules don't match */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -318,8 +345,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_no)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(1))) }, - {} - }, + ), }, { .name = "two-regs-two-entries", @@ -329,7 +355,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 2, /* Same bits on different registers are not coalesced */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -338,8 +364,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG2, REG_BIT(0))) }, - {} - }, + ), }, { .name = "clr-one-set-other", @@ -349,7 +374,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 1, /* Check clr vs set actions on different bits */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -358,8 +383,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(CLR(REGULAR_REG1, REG_BIT(1))) }, - {} - }, + ), }, { #define TEMP_MASK REG_GENMASK(10, 8) @@ -371,14 +395,13 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0), .expected_count_sr_entries = 1, /* Check FIELD_SET works */ - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(FIELD_SET(REGULAR_REG1, TEMP_MASK, TEMP_FIELD)) }, - {} - }, + ), #undef TEMP_MASK #undef TEMP_FIELD }, @@ -390,7 +413,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 1, .expected_sr_errors = 1, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -400,8 +423,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) }, - {} - }, + ), }, { .name = "conflict-not-disjoint", @@ -411,7 +433,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1), .expected_count_sr_entries = 1, .expected_sr_errors = 1, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -421,8 +443,31 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(CLR(REGULAR_REG1, REG_GENMASK(1, 0))) }, - {} - }, + ), + }, + { + .name = "conflict-not-disjoint-literal-and-func", + .expected_reg = REGULAR_REG1, + .expected_set_bits = REG_BIT(1) | REG_BIT(2), + .expected_clr_bits = REG_BIT(1) | REG_BIT(2), + .expected_active = BIT(0) | BIT(1), + .expected_count_sr_entries = 1, + .expected_sr_errors = 1, + .table = XE_RTP_TABLE_SR( + { XE_RTP_NAME("basic-1"), + XE_RTP_RULES(FUNC(match_yes)), + XE_RTP_ACTIONS(FIELD_SET(REGULAR_REG1, + REG_BIT(1) | REG_BIT(2), + REG_BIT(1) | REG_BIT(2))) + }, + /* drop: bits are not disjoint with previous entries */ + { XE_RTP_NAME("basic-2"), + XE_RTP_RULES(FUNC(match_yes)), + XE_RTP_ACTIONS(FIELD_SET_FUNC(REGULAR_REG1, + REG_BIT(2) | REG_BIT(3), + bits_2_3_set)) + }, + ), }, { .name = "conflict-reg-type", @@ -432,7 +477,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0) | BIT(1) | BIT(2), .expected_count_sr_entries = 1, .expected_sr_errors = 2, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("basic-1"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(REGULAR_REG1, REG_BIT(0))) @@ -447,8 +492,7 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(MASKED_REG1, REG_BIT(0))) }, - {} - }, + ), }, { .name = "bad-mcr-reg-forced-to-regular", @@ -458,13 +502,12 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0), .expected_count_sr_entries = 1, .expected_sr_errors = 1, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("bad-mcr-regular-reg"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(BAD_MCR_REG4, REG_BIT(0))) }, - {} - }, + ), }, { .name = "bad-regular-reg-forced-to-mcr", @@ -474,13 +517,12 @@ static const struct rtp_to_sr_test_case rtp_to_sr_cases[] = { .expected_active = BIT(0), .expected_count_sr_entries = 1, .expected_sr_errors = 1, - .entries = (const struct xe_rtp_entry_sr[]) { + .table = XE_RTP_TABLE_SR( { XE_RTP_NAME("bad-regular-reg"), XE_RTP_RULES(FUNC(match_yes)), XE_RTP_ACTIONS(SET(BAD_REGULAR_REG5, REG_BIT(0))) }, - {} - }, + ), }, }; @@ -492,16 +534,12 @@ static void xe_rtp_process_to_sr_tests(struct kunit *test) struct xe_reg_sr *reg_sr = >->reg_sr; const struct xe_reg_sr_entry *sre, *sr_entry = NULL; struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(gt); - unsigned long idx, count_sr_entries = 0, count_rtp_entries = 0, active = 0; + unsigned long idx, count_sr_entries = 0, active = 0; xe_reg_sr_init(reg_sr, "xe_rtp_to_sr_tests", xe); - while (param->entries[count_rtp_entries].rules) - count_rtp_entries++; - - xe_rtp_process_ctx_enable_active_tracking(&ctx, &active, count_rtp_entries); - xe_rtp_process_to_sr(&ctx, param->entries, count_rtp_entries, - reg_sr, false); + xe_rtp_process_ctx_enable_active_tracking(&ctx, &active, param->table.n_entries); + xe_rtp_process_to_sr(&ctx, ¶m->table, reg_sr, false); xa_for_each(®_sr->xa, idx, sre) { if (idx == param->expected_reg.addr) @@ -534,56 +572,52 @@ static const struct rtp_test_case rtp_cases[] = { { .name = "active1", .expected_active = BIT(0), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_yes)), }, - {} - }, + ), }, { .name = "active2", .expected_active = BIT(0) | BIT(1), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_yes)), }, { XE_RTP_NAME("r2"), XE_RTP_RULES(FUNC(match_yes)), }, - {} - }, + ), }, { .name = "active-inactive", .expected_active = BIT(0), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_yes)), }, { XE_RTP_NAME("r2"), XE_RTP_RULES(FUNC(match_no)), }, - {} - }, + ), }, { .name = "inactive-active", .expected_active = BIT(1), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_no)), }, { XE_RTP_NAME("r2"), XE_RTP_RULES(FUNC(match_yes)), }, - {} - }, + ), }, { .name = "inactive-active-inactive", .expected_active = BIT(1), - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_no)), }, @@ -593,13 +627,12 @@ static const struct rtp_test_case rtp_cases[] = { { XE_RTP_NAME("r3"), XE_RTP_RULES(FUNC(match_no)), }, - {} - }, + ), }, { .name = "inactive-inactive-inactive", .expected_active = 0, - .entries = (const struct xe_rtp_entry[]) { + .table = XE_RTP_TABLE( { XE_RTP_NAME("r1"), XE_RTP_RULES(FUNC(match_no)), }, @@ -609,8 +642,7 @@ static const struct rtp_test_case rtp_cases[] = { { XE_RTP_NAME("r3"), XE_RTP_RULES(FUNC(match_no)), }, - {} - }, + ), }, }; @@ -620,13 +652,10 @@ static void xe_rtp_process_tests(struct kunit *test) struct xe_device *xe = test->priv; struct xe_gt *gt = xe_device_get_root_tile(xe)->primary_gt; struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(gt); - unsigned long count_rtp_entries = 0, active = 0; - - while (param->entries[count_rtp_entries].rules) - count_rtp_entries++; + unsigned long active = 0; - xe_rtp_process_ctx_enable_active_tracking(&ctx, &active, count_rtp_entries); - xe_rtp_process(&ctx, param->entries); + xe_rtp_process_ctx_enable_active_tracking(&ctx, &active, param->table.n_entries); + xe_rtp_process(&ctx, ¶m->table); KUNIT_EXPECT_EQ(test, active, param->expected_active); } diff --git a/drivers/gpu/drm/xe/tests/xe_wa_test.c b/drivers/gpu/drm/xe/tests/xe_wa_test.c index ff0e2502b39f..21601e9df353 100644 --- a/drivers/gpu/drm/xe/tests/xe_wa_test.c +++ b/drivers/gpu/drm/xe/tests/xe_wa_test.c @@ -43,9 +43,6 @@ static int xe_wa_test_init(struct kunit *test) xe_gt_mmio_init(gt); } - if (!param->graphics_verx100) - xe->info.step = param->step; - /* TODO: init hw engines for engine/LRC WAs */ xe->drm.dev = dev; test->priv = xe; diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c index 22b471303984..3c018dbccc07 100644 --- a/drivers/gpu/drm/xe/xe_debugfs.c +++ b/drivers/gpu/drm/xe/xe_debugfs.c @@ -117,7 +117,6 @@ static int info(struct seq_file *m, void *data) drm_printf(&p, "revid %d\n", xe->info.revid); drm_printf(&p, "tile_count %d\n", xe->info.tile_count); drm_printf(&p, "vm_max_level %d\n", xe->info.vm_max_level); - drm_printf(&p, "force_execlist %s\n", str_yes_no(xe->info.force_execlist)); drm_printf(&p, "has_flat_ccs %s\n", str_yes_no(xe->info.has_flat_ccs)); drm_printf(&p, "has_usm %s\n", str_yes_no(xe->info.has_usm)); drm_printf(&p, "skip_guc_pc %s\n", str_yes_no(xe->info.skip_guc_pc)); diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 4efd2bc8c02e..ad7f3e61d457 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -61,6 +61,7 @@ #include "xe_psmi.h" #include "xe_pxp.h" #include "xe_query.h" +#include "xe_ras.h" #include "xe_shrinker.h" #include "xe_soc_remapper.h" #include "xe_survivability_mode.h" @@ -738,9 +739,11 @@ static void vf_update_device_info(struct xe_device *xe) xe->info.probe_display = 0; xe->info.has_heci_cscfi = 0; xe->info.has_heci_gscfi = 0; + xe->info.has_i2c = 0; xe->info.has_late_bind = 0; xe->info.skip_guc_pc = 1; xe->info.skip_pcode = 1; + xe->info.has_drm_ras = false; } static int xe_device_vram_alloc(struct xe_device *xe) @@ -949,6 +952,15 @@ int xe_device_probe(struct xe_device *xe) return err; } + /* + * Wa_16029380221: The affected GT will always use non-coherent + * access to page tables, so we must do uncached writes from the + * CPU. + */ + for_each_gt(gt, xe, id) + if (XE_GT_WA(gt, 16029380221)) + xe->info.has_cached_pt = false; + for_each_tile(tile, xe, id) { err = xe_ggtt_init_early(tile->mem.ggtt); if (err) @@ -989,6 +1001,16 @@ int xe_device_probe(struct xe_device *xe) if (err) return err; + err = xe_soc_remapper_init(xe); + if (err) + return err; + + err = xe_sysctrl_init(xe); + if (err) + return err; + + xe_ras_init(xe); + /* * Now that GT is initialized (TTM in particular), * we can try to init display, and inherit the initial fb. @@ -1029,10 +1051,6 @@ int xe_device_probe(struct xe_device *xe) xe_nvm_init(xe); - err = xe_soc_remapper_init(xe); - if (err) - return err; - err = xe_heci_gsc_init(xe); if (err) return err; @@ -1071,10 +1089,6 @@ int xe_device_probe(struct xe_device *xe) if (err) goto err_unregister_display; - err = xe_sysctrl_init(xe); - if (err) - goto err_unregister_display; - err = xe_device_sysfs_init(xe); if (err) goto err_unregister_display; @@ -1129,14 +1143,14 @@ void xe_device_shutdown(struct xe_device *xe) drm_dbg(&xe->drm, "Shutting down device\n"); - xe_display_pm_shutdown(xe); + xe_display_shutdown(xe); xe_irq_suspend(xe); for_each_gt(gt, xe, id) xe_gt_shutdown(gt); - xe_display_pm_shutdown_late(xe); + xe_display_shutdown_late(xe); if (!xe_driver_flr_disabled(xe)) { /* BOOM! */ diff --git a/drivers/gpu/drm/xe/xe_device.h b/drivers/gpu/drm/xe/xe_device.h index 975768a6a9c8..a03760d0ce38 100644 --- a/drivers/gpu/drm/xe/xe_device.h +++ b/drivers/gpu/drm/xe/xe_device.h @@ -116,7 +116,7 @@ static inline struct xe_mmio *xe_root_tile_mmio(struct xe_device *xe) static inline bool xe_device_uc_enabled(struct xe_device *xe) { - return !xe->info.force_execlist; + return true; } #define for_each_tile(tile__, xe__, id__) \ @@ -181,6 +181,21 @@ static inline bool xe_device_has_mert(const struct xe_device *xe) return xe->info.has_mert; } +static inline bool xe_device_is_in_reset(struct xe_device *xe) +{ + return atomic_read(&xe->in_reset); +} + +static inline void xe_device_set_in_reset(struct xe_device *xe) +{ + atomic_set(&xe->in_reset, 1); +} + +static inline void xe_device_clear_in_reset(struct xe_device *xe) +{ + atomic_set(&xe->in_reset, 0); +} + u32 xe_device_ccs_bytes(struct xe_device *xe, u64 size); void xe_device_snapshot_print(struct xe_device *xe, struct drm_printer *p); diff --git a/drivers/gpu/drm/xe/xe_device_types.h b/drivers/gpu/drm/xe/xe_device_types.h index 32dd2ffbc796..022e08205897 100644 --- a/drivers/gpu/drm/xe/xe_device_types.h +++ b/drivers/gpu/drm/xe/xe_device_types.h @@ -144,8 +144,6 @@ struct xe_device { * Keep all flags below alphabetically sorted */ - /** @info.force_execlist: Forced execlist submission */ - u8 force_execlist:1; /** @info.has_access_counter: Device supports access counter */ u8 has_access_counter:1; /** @info.has_asid: Has address space ID */ @@ -156,6 +154,8 @@ struct xe_device { u8 has_cached_pt:1; /** @info.has_device_atomics_on_smem: Supports device atomics on SMEM */ u8 has_device_atomics_on_smem:1; + /** @info.has_drm_ras: Device supports drm_ras (Reliability, Availability, Serviceability) */ + u8 has_drm_ras:1; /** @info.has_fan_control: Device supports fan control */ u8 has_fan_control:1; /** @info.has_flat_ccs: Whether flat CCS metadata is used */ @@ -483,6 +483,9 @@ struct xe_device { /** @needs_flr_on_fini: requests function-reset on fini */ bool needs_flr_on_fini; + /** @in_reset: Indicates if device is in reset */ + atomic_t in_reset; + /** @wedged: Struct to control Wedged States and mode */ struct { /** @wedged.flag: Xe device faced a critical error and is now blocked. */ @@ -495,6 +498,9 @@ struct xe_device { bool inconsistent_reset; } wedged; + /** @devres_group: devres group */ + void *devres_group; + /** @bo_device: Struct to control async free of BOs */ struct xe_bo_dev { /** @bo_device.async_free: Free worker */ diff --git a/drivers/gpu/drm/xe/xe_drm_client.c b/drivers/gpu/drm/xe/xe_drm_client.c index 84b66147bf49..e116fb562c4c 100644 --- a/drivers/gpu/drm/xe/xe_drm_client.c +++ b/drivers/gpu/drm/xe/xe_drm_client.c @@ -168,10 +168,20 @@ static void bo_meminfo(struct xe_bo *bo, struct drm_memory_stats stats[TTM_NUM_MEM_TYPES]) { u64 sz = xe_bo_size(bo); - u32 mem_type = bo->ttm.resource->mem_type; + u32 mem_type; xe_bo_assert_held(bo); + /* + * The resource can be NULL if the BO has been purged, plus maybe some + * other cases. Either way there shouldn't be any memory to account for, + * or a current resource to account this against, so skip for now. + */ + if (!bo->ttm.resource) + return; + + mem_type = bo->ttm.resource->mem_type; + if (drm_gem_object_is_shared_for_memory_stats(&bo->ttm.base)) stats[mem_type].shared += sz; else @@ -183,7 +193,7 @@ static void bo_meminfo(struct xe_bo *bo, if (!dma_resv_test_signaled(bo->ttm.base.resv, DMA_RESV_USAGE_BOOKKEEP)) stats[mem_type].active += sz; - else if (mem_type == XE_PL_SYSTEM) + else if (mem_type == XE_PL_SYSTEM || xe_bo_madv_is_dontneed(bo)) stats[mem_type].purgeable += sz; } } @@ -263,8 +273,7 @@ static void show_meminfo(struct drm_printer *p, struct drm_file *file) &stats[mem_type], DRM_GEM_OBJECT_ACTIVE | DRM_GEM_OBJECT_RESIDENT | - (mem_type != XE_PL_SYSTEM ? 0 : - DRM_GEM_OBJECT_PURGEABLE), + DRM_GEM_OBJECT_PURGEABLE, xe_mem_type_to_name[mem_type]); } } diff --git a/drivers/gpu/drm/xe/xe_drm_ras.c b/drivers/gpu/drm/xe/xe_drm_ras.c index cd236f53699e..7937d8ba0ed9 100644 --- a/drivers/gpu/drm/xe/xe_drm_ras.c +++ b/drivers/gpu/drm/xe/xe_drm_ras.c @@ -11,27 +11,46 @@ #include "xe_device_types.h" #include "xe_drm_ras.h" +#include "xe_ras.h" static const char * const error_components[] = DRM_XE_RAS_ERROR_COMPONENT_NAMES; static const char * const error_severity[] = DRM_XE_RAS_ERROR_SEVERITY_NAMES; -static int hw_query_error_counter(struct xe_drm_ras_counter *info, - u32 error_id, const char **name, u32 *val) +static int query_error_counter(struct xe_device *xe, + enum drm_xe_ras_error_severity severity, + u32 error_id, const char **name, u32 *val) { + struct xe_drm_ras *ras = &xe->ras; + struct xe_drm_ras_counter *info = ras->info[severity]; + if (!info || !info[error_id].name) return -ENOENT; *name = info[error_id].name; + + /* Fetch counter from system controller if supported */ + if (xe->info.has_sysctrl) + return xe_ras_get_counter(xe, severity, error_id, val); + *val = atomic_read(&info[error_id].counter); return 0; } -static int hw_clear_error_counter(struct xe_drm_ras_counter *info, u32 error_id) +static int clear_error_counter(struct xe_device *xe, + enum drm_xe_ras_error_severity severity, + u32 error_id) { + struct xe_drm_ras *ras = &xe->ras; + struct xe_drm_ras_counter *info = ras->info[severity]; + if (!info || !info[error_id].name) return -ENOENT; + /* Clear counter from system controller if supported */ + if (xe->info.has_sysctrl) + return xe_ras_clear_counter(xe, severity, error_id); + atomic_set(&info[error_id].counter, 0); return 0; @@ -41,38 +60,30 @@ static int query_uncorrectable_error_counter(struct drm_ras_node *ep, u32 error_ const char **name, u32 *val) { struct xe_device *xe = ep->priv; - struct xe_drm_ras *ras = &xe->ras; - struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_UNCORRECTABLE]; - return hw_query_error_counter(info, error_id, name, val); + return query_error_counter(xe, DRM_XE_RAS_ERR_SEV_UNCORRECTABLE, error_id, name, val); } static int clear_uncorrectable_error_counter(struct drm_ras_node *node, u32 error_id) { struct xe_device *xe = node->priv; - struct xe_drm_ras *ras = &xe->ras; - struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_UNCORRECTABLE]; - return hw_clear_error_counter(info, error_id); + return clear_error_counter(xe, DRM_XE_RAS_ERR_SEV_UNCORRECTABLE, error_id); } static int query_correctable_error_counter(struct drm_ras_node *ep, u32 error_id, const char **name, u32 *val) { struct xe_device *xe = ep->priv; - struct xe_drm_ras *ras = &xe->ras; - struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_CORRECTABLE]; - return hw_query_error_counter(info, error_id, name, val); + return query_error_counter(xe, DRM_XE_RAS_ERR_SEV_CORRECTABLE, error_id, name, val); } static int clear_correctable_error_counter(struct drm_ras_node *node, u32 error_id) { struct xe_device *xe = node->priv; - struct xe_drm_ras *ras = &xe->ras; - struct xe_drm_ras_counter *info = ras->info[DRM_XE_RAS_ERR_SEV_CORRECTABLE]; - return hw_clear_error_counter(info, error_id); + return clear_error_counter(xe, DRM_XE_RAS_ERR_SEV_CORRECTABLE, error_id); } static struct xe_drm_ras_counter *allocate_and_copy_counters(struct xe_device *xe) diff --git a/drivers/gpu/drm/xe/xe_eu_stall.c b/drivers/gpu/drm/xe/xe_eu_stall.c index 297be3c42b20..d37770c58c5d 100644 --- a/drivers/gpu/drm/xe/xe_eu_stall.c +++ b/drivers/gpu/drm/xe/xe_eu_stall.c @@ -985,9 +985,10 @@ int xe_eu_stall_stream_open(struct drm_device *dev, u64 data, struct drm_file *f return -ENODEV; } - if (xe_observation_paranoid && !perfmon_capable()) { + ret = xe_observation_paranoid_check(); + if (ret) { drm_dbg(&xe->drm, "Insufficient privileges for EU stall monitoring\n"); - return -EACCES; + return ret; } /* Initialize and set default values */ diff --git a/drivers/gpu/drm/xe/xe_exec_queue_types.h b/drivers/gpu/drm/xe/xe_exec_queue_types.h index 2f5ccf294675..d27ce24daae5 100644 --- a/drivers/gpu/drm/xe/xe_exec_queue_types.h +++ b/drivers/gpu/drm/xe/xe_exec_queue_types.h @@ -318,8 +318,6 @@ struct xe_exec_queue_ops { void (*resume)(struct xe_exec_queue *q); /** @reset_status: check exec queue reset status */ bool (*reset_status)(struct xe_exec_queue *q); - /** @active: check exec queue is active */ - bool (*active)(struct xe_exec_queue *q); }; #endif diff --git a/drivers/gpu/drm/xe/xe_execlist.c b/drivers/gpu/drm/xe/xe_execlist.c index 9fb99c038ea8..6b86b4f9cc1c 100644 --- a/drivers/gpu/drm/xe/xe_execlist.c +++ b/drivers/gpu/drm/xe/xe_execlist.c @@ -458,12 +458,6 @@ static bool execlist_exec_queue_reset_status(struct xe_exec_queue *q) return false; } -static bool execlist_exec_queue_active(struct xe_exec_queue *q) -{ - /* NIY */ - return false; -} - static const struct xe_exec_queue_ops execlist_exec_queue_ops = { .init = execlist_exec_queue_init, .kill = execlist_exec_queue_kill, @@ -476,7 +470,6 @@ static const struct xe_exec_queue_ops execlist_exec_queue_ops = { .suspend_wait = execlist_exec_queue_suspend_wait, .resume = execlist_exec_queue_resume, .reset_status = execlist_exec_queue_reset_status, - .active = execlist_exec_queue_active, }; int xe_execlist_init(struct xe_gt *gt) diff --git a/drivers/gpu/drm/xe/xe_ggtt.c b/drivers/gpu/drm/xe/xe_ggtt.c index a351c578b170..8ec23862477f 100644 --- a/drivers/gpu/drm/xe/xe_ggtt.c +++ b/drivers/gpu/drm/xe/xe_ggtt.c @@ -111,14 +111,14 @@ struct xe_ggtt_pt_ops { struct xe_ggtt { /** @tile: Back pointer to tile where this GGTT belongs */ struct xe_tile *tile; - /** @start: Start offset of GGTT */ + /** @start: Start offset of GGTT */ u64 start; /** @size: Total usable size of this GGTT */ u64 size; - /** - * @flags: Flags for this GGTT + * @flags: Flags for this GGTT. * Acceptable flags: + * * - %XE_GGTT_FLAGS_64K - if PTE size is 64K. Otherwise, regular is 4K. * - %XE_GGTT_FLAGS_ONLINE - is GGTT online, protected by ggtt->lock * after init @@ -129,7 +129,7 @@ struct xe_ggtt { /** @lock: Mutex lock to protect GGTT data */ struct mutex lock; /** - * @gsm: The iomem pointer to the actual location of the translation + * @gsm: The iomem pointer to the actual location of the translation * table located in the GSM for easy PTE manipulation */ u64 __iomem *gsm; diff --git a/drivers/gpu/drm/xe/xe_gt.c b/drivers/gpu/drm/xe/xe_gt.c index 783eb6d631b5..d904527a8898 100644 --- a/drivers/gpu/drm/xe/xe_gt.c +++ b/drivers/gpu/drm/xe/xe_gt.c @@ -917,6 +917,9 @@ static void gt_reset_worker(struct work_struct *w) if (xe_device_wedged(gt_to_xe(gt))) goto err_pm_put; + if (xe_device_is_in_reset(gt_to_xe(gt))) + goto err_pm_put; + /* We only support GT resets with GuC submission */ if (!xe_device_uc_enabled(gt_to_xe(gt))) goto err_pm_put; @@ -977,18 +980,21 @@ err_pm_put: void xe_gt_reset_async(struct xe_gt *gt) { - xe_gt_info(gt, "trying reset from %ps\n", __builtin_return_address(0)); + struct xe_device *xe = gt_to_xe(gt); + + if (xe_device_is_in_reset(xe)) + return; /* Don't do a reset while one is already in flight */ if (!xe_fault_inject_gt_reset() && xe_uc_reset_prepare(>->uc)) return; - xe_gt_info(gt, "reset queued\n"); + xe_gt_info(gt, "reset queued from %ps\n", __builtin_return_address(0)); /* Pair with put in gt_reset_worker() if work is enqueued */ - xe_pm_runtime_get_noresume(gt_to_xe(gt)); + xe_pm_runtime_get_noresume(xe); if (!queue_work(gt->ordered_wq, >->reset.worker)) - xe_pm_runtime_put(gt_to_xe(gt)); + xe_pm_runtime_put(xe); } void xe_gt_suspend_prepare(struct xe_gt *gt) diff --git a/drivers/gpu/drm/xe/xe_gt_debugfs.c b/drivers/gpu/drm/xe/xe_gt_debugfs.c index f45306308cd6..c38bcacb27e4 100644 --- a/drivers/gpu/drm/xe/xe_gt_debugfs.c +++ b/drivers/gpu/drm/xe/xe_gt_debugfs.c @@ -149,8 +149,10 @@ static int register_save_restore(struct xe_gt *gt, struct drm_printer *p) drm_printf(p, "\n"); drm_printf(p, "Whitelist\n"); - for_each_hw_engine(hwe, gt, id) + for_each_hw_engine(hwe, gt, id) { xe_reg_whitelist_dump(&hwe->reg_whitelist, p); + xe_reg_whitelist_dump(&hwe->oa_whitelist, p); + } return 0; } diff --git a/drivers/gpu/drm/xe/xe_gt_mcr.c b/drivers/gpu/drm/xe/xe_gt_mcr.c index 04f0098070a4..a97b236dab7c 100644 --- a/drivers/gpu/drm/xe/xe_gt_mcr.c +++ b/drivers/gpu/drm/xe/xe_gt_mcr.c @@ -404,8 +404,7 @@ fallback: * Some older platforms don't have tables or don't have complete tables. * Newer platforms should always have the required info. */ - if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 2000 && - !gt_to_xe(gt)->info.force_execlist) + if (GRAPHICS_VERx100(gt_to_xe(gt)) >= 2000) xe_gt_err(gt, "Slice/Subslice counts missing from hwconfig table; using typical fallback values\n"); if (gt_to_xe(gt)->info.platform == XE_PVC) @@ -507,7 +506,7 @@ void xe_gt_mcr_init_early(struct xe_gt *gt) spin_lock_init(>->mcr_lock); if (gt->info.type == XE_GT_TYPE_MEDIA) { - drm_WARN_ON(&xe->drm, MEDIA_VER(xe) < 13); + xe_gt_WARN_ON(gt, MEDIA_VER(xe) < 13); if (MEDIA_VER(xe) >= 30) { gt->steering[OADDRM].ranges = xe2lpm_gpmxmt_steering_table; @@ -662,9 +661,9 @@ bool xe_gt_mcr_get_nonterminated_steering(struct xe_gt *gt, for (int type = 0; type < IMPLICIT_STEERING; type++) { if (reg_in_steering_type_ranges(gt, reg, type)) { - drm_WARN(>_to_xe(gt)->drm, !gt->steering[type].initialized, - "Uninitialized usage of MCR register %s/%#x\n", - xe_steering_types[type].name, reg.addr); + xe_gt_WARN(gt, !gt->steering[type].initialized, + "Uninitialized usage of MCR register %s/%#x\n", + xe_steering_types[type].name, reg.addr); *group = gt->steering[type].group_target; *instance = gt->steering[type].instance_target; @@ -679,9 +678,9 @@ bool xe_gt_mcr_get_nonterminated_steering(struct xe_gt *gt, * Not found in a steering table and not a register with implicit * steering. Just steer to 0/0 as a guess and raise a warning. */ - drm_WARN(>_to_xe(gt)->drm, true, - "Did not find MCR register %#x in any MCR steering table\n", - reg.addr); + xe_gt_WARN(gt, true, + "Did not find MCR register %#x in any MCR steering table\n", + reg.addr); *group = 0; *instance = 0; @@ -710,7 +709,7 @@ static void mcr_lock(struct xe_gt *gt) __acquires(>->mcr_lock) ret = xe_mmio_wait32(>->mmio, STEER_SEMAPHORE, 0x1, 0x1, 10, NULL, true); - drm_WARN_ON_ONCE(&xe->drm, ret == -ETIMEDOUT); + xe_gt_WARN_ON_ONCE(gt, ret == -ETIMEDOUT); } static void mcr_unlock(struct xe_gt *gt) __releases(>->mcr_lock) diff --git a/drivers/gpu/drm/xe/xe_guc_relay.c b/drivers/gpu/drm/xe/xe_guc_relay.c index 577a315854af..eed0a750d2eb 100644 --- a/drivers/gpu/drm/xe/xe_guc_relay.c +++ b/drivers/gpu/drm/xe/xe_guc_relay.c @@ -689,12 +689,17 @@ static int relay_action_handler(struct xe_guc_relay *relay, u32 origin, return relay_testloop_action_handler(relay, origin, msg, len, response, size); type = FIELD_GET(GUC_HXG_MSG_0_TYPE, msg[0]); + relay_assert(relay, guc_hxg_type_is_action(type)); - if (IS_SRIOV_PF(relay_to_xe(relay))) - ret = xe_gt_sriov_pf_service_process_request(gt, origin, msg, len, response, size); - else + if (IS_SRIOV_PF(relay_to_xe(relay))) { + if (type == GUC_HXG_TYPE_REQUEST) + ret = xe_gt_sriov_pf_service_process_request(gt, origin, msg, len, + response, size); + else + ret = -EOPNOTSUPP; + } else { ret = -EOPNOTSUPP; - + } if (type == GUC_HXG_TYPE_EVENT) relay_assert(relay, ret <= 0); diff --git a/drivers/gpu/drm/xe/xe_guc_submit.c b/drivers/gpu/drm/xe/xe_guc_submit.c index 12a410458df6..12416bfa3255 100644 --- a/drivers/gpu/drm/xe/xe_guc_submit.c +++ b/drivers/gpu/drm/xe/xe_guc_submit.c @@ -1163,7 +1163,7 @@ static void submit_exec_queue(struct xe_exec_queue *q, struct xe_sched_job *job) if (exec_queue_suspended(q)) return; - if (!exec_queue_enabled(q) && !exec_queue_suspended(q)) { + if (!exec_queue_enabled(q)) { action[len++] = XE_GUC_ACTION_SCHED_CONTEXT_MODE_SET; action[len++] = q->guc->id; action[len++] = GUC_CONTEXT_ENABLE; @@ -1493,7 +1493,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) struct xe_device *xe = guc_to_xe(guc); int err = -ETIME; pid_t pid = -1; - bool wedged = false, skip_timeout_check; + bool wedged = false, wedge_device = false, skip_timeout_check; xe_gt_assert(guc_to_gt(guc), !exec_queue_destroyed(q)); @@ -1532,7 +1532,7 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) * If devcoredump not captured and GuC capture for the job is not ready * do manual capture first and decide later if we need to use it */ - if (!exec_queue_killed(q) && !xe->devcoredump.captured && + if (!xe_device_is_in_reset(xe) && !exec_queue_killed(q) && !xe->devcoredump.captured && !xe_guc_capture_get_matching_and_lock(q)) { /* take force wake before engine register manual capture */ CLASS(xe_force_wake, fw_ref)(gt_to_fw(q->gt), XE_FORCEWAKE_ALL); @@ -1554,8 +1554,8 @@ guc_exec_queue_timedout_job(struct drm_sched_job *drm_job) set_exec_queue_banned(q); /* Kick job / queue off hardware */ - if (!wedged && (exec_queue_enabled(primary) || - exec_queue_pending_disable(primary))) { + if (!xe_device_is_in_reset(xe) && !wedged && + (exec_queue_enabled(primary) || exec_queue_pending_disable(primary))) { int ret; if (exec_queue_reset(primary)) @@ -1623,7 +1623,8 @@ trigger_reset: trace_xe_sched_job_timedout(job); - if (!exec_queue_killed(q)) + /* Do not access device if in reset */ + if (!xe_device_is_in_reset(xe) && !exec_queue_killed(q)) xe_devcoredump(q, job, "Timedout job - seqno=%u, lrc_seqno=%u, guc_id=%d, flags=0x%lx", xe_sched_job_seqno(job), xe_sched_job_lrc_seqno(job), @@ -1638,7 +1639,7 @@ trigger_reset: } if (q->flags & EXEC_QUEUE_FLAG_KERNEL) { xe_gt_WARN(q->gt, true, "Kernel-submitted job timed out\n"); - xe_device_declare_wedged(gt_to_xe(q->gt)); + wedge_device = true; } } else if (q->flags & EXEC_QUEUE_FLAG_VM && !exec_queue_killed(q)) { xe_gt_WARN(q->gt, true, "VM job timed out on non-killed execqueue\n"); @@ -1658,6 +1659,9 @@ trigger_reset: xe_guc_exec_queue_trigger_cleanup(q); } + if (wedge_device) + xe_device_declare_wedged(gt_to_xe(q->gt)); + /* * We want the job added back to the pending list so it gets freed; this * is what DRM_GPU_SCHED_STAT_NO_HANG does. @@ -2241,14 +2245,6 @@ static bool guc_exec_queue_reset_status(struct xe_exec_queue *q) return exec_queue_reset(q) || exec_queue_killed_or_banned_or_wedged(q); } -static bool guc_exec_queue_active(struct xe_exec_queue *q) -{ - struct xe_exec_queue *primary = xe_exec_queue_multi_queue_primary(q); - - return exec_queue_enabled(primary) && - !exec_queue_pending_disable(primary); -} - /* * All of these functions are an abstraction layer which other parts of Xe can * use to trap into the GuC backend. All of these functions, aside from init, @@ -2268,7 +2264,6 @@ static const struct xe_exec_queue_ops guc_exec_queue_ops = { .suspend_wait = guc_exec_queue_suspend_wait, .resume = guc_exec_queue_resume, .reset_status = guc_exec_queue_reset_status, - .active = guc_exec_queue_active, }; static void guc_exec_queue_stop(struct xe_guc *guc, struct xe_exec_queue *q) diff --git a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c index cf6d106e6036..046d0655122f 100644 --- a/drivers/gpu/drm/xe/xe_guc_tlb_inval.c +++ b/drivers/gpu/drm/xe/xe_guc_tlb_inval.c @@ -208,9 +208,6 @@ static int send_tlb_inval_asid_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, lockdep_assert_held(&tlb_inval->seqno_lock); - if (guc_to_xe(guc)->info.force_execlist) - return -ECANCELED; - return send_tlb_inval_ppgtt(guc, seqno, start, end, asid, XE_GUC_TLB_INVAL_PAGE_SELECTIVE, prl_sa); } @@ -228,9 +225,6 @@ static int send_tlb_inval_ctx_ppgtt(struct xe_tlb_inval *tlb_inval, u32 seqno, lockdep_assert_held(&tlb_inval->seqno_lock); - if (xe->info.force_execlist) - return -ECANCELED; - vm = xe_device_asid_to_vm(xe, asid); if (IS_ERR(vm)) return PTR_ERR(vm); diff --git a/drivers/gpu/drm/xe/xe_hw_engine.c b/drivers/gpu/drm/xe/xe_hw_engine.c index 8c66ff6f3d3c..87d60c4117bd 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine.c +++ b/drivers/gpu/drm/xe/xe_hw_engine.c @@ -337,42 +337,43 @@ static bool xe_rtp_cfeg_wmtp_disabled(const struct xe_device *xe, return xe_mmio_read32(&hwe->gt->mmio, XEHP_FUSE4) & CFEG_WMTP_DISABLE; } +static u32 blit_cctl_val(struct xe_gt *gt, struct xe_hw_engine *hwe) +{ + return REG_FIELD_PREP(BLIT_CCTL_DST_MOCS_MASK, gt->mocs.uc_index) | + REG_FIELD_PREP(BLIT_CCTL_SRC_MOCS_MASK, gt->mocs.uc_index); +} + +static const struct xe_rtp_table_sr lrc_setup = XE_RTP_TABLE_SR( + /* + * Some blitter commands do not have a field for MOCS, those + * commands will use MOCS index pointed by BLIT_CCTL. + * BLIT_CCTL registers are needed to be programmed to un-cached. + */ + { XE_RTP_NAME("BLIT_CCTL_default_MOCS"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1274), + ENGINE_CLASS(COPY)), + XE_RTP_ACTIONS(FIELD_SET_FUNC(BLIT_CCTL(0), + BLIT_CCTL_DST_MOCS_MASK | + BLIT_CCTL_SRC_MOCS_MASK, + blit_cctl_val, + XE_RTP_ACTION_FLAG(ENGINE_BASE))) + }, + /* Disable WMTP if HW doesn't support it */ + { XE_RTP_NAME("DISABLE_WMTP_ON_UNSUPPORTED_HW"), + XE_RTP_RULES(FUNC(xe_rtp_cfeg_wmtp_disabled)), + XE_RTP_ACTIONS(FIELD_SET(CS_CHICKEN1(0), + PREEMPT_GPGPU_LEVEL_MASK, + PREEMPT_GPGPU_THREAD_GROUP_LEVEL)), + XE_RTP_ENTRY_FLAG(FOREACH_ENGINE) + }, +); + static void hw_engine_setup_default_lrc_state(struct xe_hw_engine *hwe) { - struct xe_gt *gt = hwe->gt; - const u8 mocs_write_idx = gt->mocs.uc_index; - const u8 mocs_read_idx = gt->mocs.uc_index; - u32 blit_cctl_val = REG_FIELD_PREP(BLIT_CCTL_DST_MOCS_MASK, mocs_write_idx) | - REG_FIELD_PREP(BLIT_CCTL_SRC_MOCS_MASK, mocs_read_idx); struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); - const struct xe_rtp_entry_sr lrc_setup[] = { - /* - * Some blitter commands do not have a field for MOCS, those - * commands will use MOCS index pointed by BLIT_CCTL. - * BLIT_CCTL registers are needed to be programmed to un-cached. - */ - { XE_RTP_NAME("BLIT_CCTL_default_MOCS"), - XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1274), - ENGINE_CLASS(COPY)), - XE_RTP_ACTIONS(FIELD_SET(BLIT_CCTL(0), - BLIT_CCTL_DST_MOCS_MASK | - BLIT_CCTL_SRC_MOCS_MASK, - blit_cctl_val, - XE_RTP_ACTION_FLAG(ENGINE_BASE))) - }, - /* Disable WMTP if HW doesn't support it */ - { XE_RTP_NAME("DISABLE_WMTP_ON_UNSUPPORTED_HW"), - XE_RTP_RULES(FUNC(xe_rtp_cfeg_wmtp_disabled)), - XE_RTP_ACTIONS(FIELD_SET(CS_CHICKEN1(0), - PREEMPT_GPGPU_LEVEL_MASK, - PREEMPT_GPGPU_THREAD_GROUP_LEVEL)), - XE_RTP_ENTRY_FLAG(FOREACH_ENGINE) - }, - }; - - xe_rtp_process_to_sr(&ctx, lrc_setup, ARRAY_SIZE(lrc_setup), - &hwe->reg_lrc, true); + + xe_rtp_process_to_sr(&ctx, &lrc_setup, &hwe->reg_lrc, true); } void xe_hw_engine_setup_reg_lrc(struct xe_hw_engine *hwe) @@ -386,89 +387,94 @@ void xe_hw_engine_setup_reg_lrc(struct xe_hw_engine *hwe) xe_tuning_process_lrc(hwe); } -static void -hw_engine_setup_default_state(struct xe_hw_engine *hwe) +/* + * RING_CMD_CCTL specifies the default MOCS entry that will be + * used by the command streamer when executing commands that + * don't have a way to explicitly specify a MOCS setting. + * The default should usually reference whichever MOCS entry + * corresponds to uncached behavior, although use of a WB cached + * entry is recommended by the spec in certain circumstances on + * specific platforms. + * Bspec: 72161 + */ +static u32 ring_cmd_cctl_val(struct xe_gt *gt, struct xe_hw_engine *hwe) { - struct xe_gt *gt = hwe->gt; struct xe_device *xe = gt_to_xe(gt); + u8 mocs_read_idx = gt->mocs.uc_index; + + if (hwe->class == XE_ENGINE_CLASS_COMPUTE && IS_DGFX(xe) && + (GRAPHICS_VER(xe) >= 20 || xe->info.platform == XE_PVC)) + mocs_read_idx = gt->mocs.wb_index; + + return REG_FIELD_PREP(CMD_CCTL_WRITE_OVERRIDE_MASK, gt->mocs.uc_index) | + REG_FIELD_PREP(CMD_CCTL_READ_OVERRIDE_MASK, mocs_read_idx); +} + +static const struct xe_rtp_table_sr engine_sr = XE_RTP_TABLE_SR( + { XE_RTP_NAME("RING_CMD_CCTL_default_MOCS"), + XE_RTP_RULES(FUNC(xe_rtp_match_always)), + XE_RTP_ACTIONS(FIELD_SET_FUNC(RING_CMD_CCTL(0), + CMD_CCTL_WRITE_OVERRIDE_MASK | + CMD_CCTL_READ_OVERRIDE_MASK, + ring_cmd_cctl_val, + XE_RTP_ACTION_FLAG(ENGINE_BASE))) + }, + { XE_RTP_NAME("Disable HW status page updates for interrupts"), + XE_RTP_RULES(FUNC(xe_rtp_match_always)), + XE_RTP_ACTIONS(SET(RING_HWSTAM(0), ~0x0, + XE_RTP_ACTION_FLAG(ENGINE_BASE))) + }, + { XE_RTP_NAME("Disable engine 'legacy' mode"), + XE_RTP_RULES(FUNC(xe_rtp_match_always)), + XE_RTP_ACTIONS(SET(GFX_MODE(0), GFX_DISABLE_LEGACY_MODE, + XE_RTP_ACTION_FLAG(ENGINE_BASE))) + }, /* - * RING_CMD_CCTL specifies the default MOCS entry that will be - * used by the command streamer when executing commands that - * don't have a way to explicitly specify a MOCS setting. - * The default should usually reference whichever MOCS entry - * corresponds to uncached behavior, although use of a WB cached - * entry is recommended by the spec in certain circumstances on - * specific platforms. - * Bspec: 72161 + * To allow the GSC engine to go idle on MTL we need to enable + * idle messaging and set the hysteresis value (we use 0xA=5us + * as recommended in spec). On platforms after MTL this is + * enabled by default. */ - const u8 mocs_write_idx = gt->mocs.uc_index; - const u8 mocs_read_idx = hwe->class == XE_ENGINE_CLASS_COMPUTE && IS_DGFX(xe) && - (GRAPHICS_VER(xe) >= 20 || xe->info.platform == XE_PVC) ? - gt->mocs.wb_index : gt->mocs.uc_index; - u32 ring_cmd_cctl_val = REG_FIELD_PREP(CMD_CCTL_WRITE_OVERRIDE_MASK, mocs_write_idx) | - REG_FIELD_PREP(CMD_CCTL_READ_OVERRIDE_MASK, mocs_read_idx); + { XE_RTP_NAME("MTL GSCCS IDLE MSG enable"), + XE_RTP_RULES(MEDIA_VERSION(1300), ENGINE_CLASS(OTHER)), + XE_RTP_ACTIONS(CLR(RING_PSMI_CTL(0), + IDLE_MSG_DISABLE, + XE_RTP_ACTION_FLAG(ENGINE_BASE)), + FIELD_SET(RING_PWRCTX_MAXCNT(0), + IDLE_WAIT_TIME, + 0xA, + XE_RTP_ACTION_FLAG(ENGINE_BASE))) + }, + /* Enable Priority Mem Read */ + { XE_RTP_NAME("Priority_Mem_Read"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)), + XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0), CS_PRIORITY_MEM_READ, + XE_RTP_ACTION_FLAG(ENGINE_BASE))) + }, + { XE_RTP_NAME("Enable CCS Engine(s)"), + XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1255, XE_RTP_END_VERSION_UNDEFINED), + FUNC(xe_rtp_match_first_render_or_compute)), + XE_RTP_ACTIONS(SET(RCU_MODE, RCU_MODE_CCS_ENABLE)) + }, + /* Use Fixed slice CCS mode */ + { XE_RTP_NAME("RCU_MODE_FIXED_SLICE_CCS_MODE"), + XE_RTP_RULES(FUNC(xe_hw_engine_match_fixed_cslice_mode)), + XE_RTP_ACTIONS(FIELD_SET(RCU_MODE, RCU_MODE_FIXED_SLICE_CCS_MODE, + RCU_MODE_FIXED_SLICE_CCS_MODE)) + }, + { XE_RTP_NAME("Enable MSI-X interrupt support"), + XE_RTP_RULES(FUNC(xe_rtp_match_has_msix)), + XE_RTP_ACTIONS(SET(GFX_MODE(0), GFX_MSIX_INTERRUPT_ENABLE, + XE_RTP_ACTION_FLAG(ENGINE_BASE))) + }, +); + +static void +hw_engine_setup_default_state(struct xe_hw_engine *hwe) +{ struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); - const struct xe_rtp_entry_sr engine_entries[] = { - { XE_RTP_NAME("RING_CMD_CCTL_default_MOCS"), - XE_RTP_RULES(FUNC(xe_rtp_match_always)), - XE_RTP_ACTIONS(FIELD_SET(RING_CMD_CCTL(0), - CMD_CCTL_WRITE_OVERRIDE_MASK | - CMD_CCTL_READ_OVERRIDE_MASK, - ring_cmd_cctl_val, - XE_RTP_ACTION_FLAG(ENGINE_BASE))) - }, - { XE_RTP_NAME("Disable HW status page updates for interrupts"), - XE_RTP_RULES(FUNC(xe_rtp_match_always)), - XE_RTP_ACTIONS(SET(RING_HWSTAM(0), ~0x0, - XE_RTP_ACTION_FLAG(ENGINE_BASE))) - }, - { XE_RTP_NAME("Disable engine 'legacy' mode"), - XE_RTP_RULES(FUNC(xe_rtp_match_always)), - XE_RTP_ACTIONS(SET(GFX_MODE(0), GFX_DISABLE_LEGACY_MODE, - XE_RTP_ACTION_FLAG(ENGINE_BASE))) - }, - /* - * To allow the GSC engine to go idle on MTL we need to enable - * idle messaging and set the hysteresis value (we use 0xA=5us - * as recommended in spec). On platforms after MTL this is - * enabled by default. - */ - { XE_RTP_NAME("MTL GSCCS IDLE MSG enable"), - XE_RTP_RULES(MEDIA_VERSION(1300), ENGINE_CLASS(OTHER)), - XE_RTP_ACTIONS(CLR(RING_PSMI_CTL(0), - IDLE_MSG_DISABLE, - XE_RTP_ACTION_FLAG(ENGINE_BASE)), - FIELD_SET(RING_PWRCTX_MAXCNT(0), - IDLE_WAIT_TIME, - 0xA, - XE_RTP_ACTION_FLAG(ENGINE_BASE))) - }, - /* Enable Priority Mem Read */ - { XE_RTP_NAME("Priority_Mem_Read"), - XE_RTP_RULES(GRAPHICS_VERSION_RANGE(2001, XE_RTP_END_VERSION_UNDEFINED)), - XE_RTP_ACTIONS(SET(CSFE_CHICKEN1(0), CS_PRIORITY_MEM_READ, - XE_RTP_ACTION_FLAG(ENGINE_BASE))) - }, - { XE_RTP_NAME("Enable CCS Engine(s)"), - XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1255, XE_RTP_END_VERSION_UNDEFINED), - FUNC(xe_rtp_match_first_render_or_compute)), - XE_RTP_ACTIONS(SET(RCU_MODE, RCU_MODE_CCS_ENABLE)) - }, - /* Use Fixed slice CCS mode */ - { XE_RTP_NAME("RCU_MODE_FIXED_SLICE_CCS_MODE"), - XE_RTP_RULES(FUNC(xe_hw_engine_match_fixed_cslice_mode)), - XE_RTP_ACTIONS(FIELD_SET(RCU_MODE, RCU_MODE_FIXED_SLICE_CCS_MODE, - RCU_MODE_FIXED_SLICE_CCS_MODE)) - }, - { XE_RTP_NAME("Enable MSI-X interrupt support"), - XE_RTP_RULES(FUNC(xe_rtp_match_has_msix)), - XE_RTP_ACTIONS(SET(GFX_MODE(0), GFX_MSIX_INTERRUPT_ENABLE, - XE_RTP_ACTION_FLAG(ENGINE_BASE))) - }, - }; - - xe_rtp_process_to_sr(&ctx, engine_entries, ARRAY_SIZE(engine_entries), - &hwe->reg_sr, false); + + xe_rtp_process_to_sr(&ctx, &engine_sr, &hwe->reg_sr, false); } static const struct engine_info *find_engine_info(enum xe_engine_class class, int instance) @@ -574,6 +580,8 @@ static void hw_engine_init_early(struct xe_gt *gt, struct xe_hw_engine *hwe, hw_engine_setup_default_state(hwe); xe_reg_sr_init(&hwe->reg_whitelist, hwe->name, gt_to_xe(gt)); + xe_reg_sr_init(&hwe->oa_whitelist, hwe->name, gt_to_xe(gt)); + xe_reg_sr_init(&hwe->oa_sr, hwe->name, gt_to_xe(gt)); xe_reg_whitelist_process_engine(hwe); } @@ -628,7 +636,7 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe, hwe->exl_port = xe_execlist_port_create(xe, hwe); if (IS_ERR(hwe->exl_port)) { err = PTR_ERR(hwe->exl_port); - goto err_hwsp; + goto err_name; } } else { /* GSCCS has a special interrupt for reset */ @@ -648,8 +656,6 @@ static int hw_engine_init(struct xe_gt *gt, struct xe_hw_engine *hwe, return devm_add_action_or_reset(xe->drm.dev, hw_engine_fini, hwe); -err_hwsp: - xe_bo_unpin_map_no_vm(hwe->hwsp); err_name: hwe->name = NULL; diff --git a/drivers/gpu/drm/xe/xe_hw_engine_types.h b/drivers/gpu/drm/xe/xe_hw_engine_types.h index 2cf898e682f5..84c097da9b6f 100644 --- a/drivers/gpu/drm/xe/xe_hw_engine_types.h +++ b/drivers/gpu/drm/xe/xe_hw_engine_types.h @@ -131,6 +131,14 @@ struct xe_hw_engine { */ struct xe_reg_sr reg_whitelist; /** + * @oa_whitelist: oa registers to be whitelisted + */ + struct xe_reg_sr oa_whitelist; + /** + * @oa_sr: oa nonpriv whitelist registers, changed on oa stream open/close + */ + struct xe_reg_sr oa_sr; + /** * @reg_lrc: LRC workaround registers */ struct xe_reg_sr reg_lrc; diff --git a/drivers/gpu/drm/xe/xe_hw_error.c b/drivers/gpu/drm/xe/xe_hw_error.c index 4b72959b2276..4a4b363fc844 100644 --- a/drivers/gpu/drm/xe/xe_hw_error.c +++ b/drivers/gpu/drm/xe/xe_hw_error.c @@ -437,6 +437,16 @@ static void hw_error_source_handler(struct xe_tile *tile, const enum hardware_er if (!IS_DGFX(xe)) return; + /* + * Hardware errors are reported through System Controller on the platforms that + * support it, and never routed as direct IRQ to SGUnit. So we should never be + * here for those platforms. + */ + if (xe->info.has_sysctrl) { + drm_err_ratelimited(&xe->drm, HW_ERR "Invalid error routing\n"); + return; + } + spin_lock_irqsave(&xe->irq.lock, flags); err_src = xe_mmio_read32(&tile->mmio, DEV_ERR_STAT_REG(hw_err)); if (!err_src) { @@ -516,14 +526,6 @@ void xe_hw_error_irq_handler(struct xe_tile *tile, const u32 master_ctl) } } -static int hw_error_info_init(struct xe_device *xe) -{ - if (xe->info.platform != XE_PVC) - return 0; - - return xe_drm_ras_init(xe); -} - /* * Process hardware errors during boot */ @@ -550,16 +552,11 @@ static void process_hw_errors(struct xe_device *xe) void xe_hw_error_init(struct xe_device *xe) { struct xe_tile *tile = xe_device_get_root_tile(xe); - int ret; if (!IS_DGFX(xe) || IS_SRIOV_VF(xe)) return; INIT_WORK(&tile->csc_hw_error_work, csc_hw_error_work); - ret = hw_error_info_init(xe); - if (ret) - drm_err(&xe->drm, "Failed to initialize XE DRM RAS (%pe)\n", ERR_PTR(ret)); - process_hw_errors(xe); } diff --git a/drivers/gpu/drm/xe/xe_i2c.c b/drivers/gpu/drm/xe/xe_i2c.c index 706783863d07..bd956776b10b 100644 --- a/drivers/gpu/drm/xe/xe_i2c.c +++ b/drivers/gpu/drm/xe/xe_i2c.c @@ -334,9 +334,6 @@ int xe_i2c_probe(struct xe_device *xe) if (!xe->info.has_i2c) return 0; - if (IS_SRIOV_VF(xe)) - return 0; - xe_i2c_read_endpoint(xe_root_tile_mmio(xe), &ep); if (ep.cookie != XE_I2C_EP_COOKIE_DEVICE) return 0; diff --git a/drivers/gpu/drm/xe/xe_lrc.c b/drivers/gpu/drm/xe/xe_lrc.c index a4292a11391d..3e7c995085d0 100644 --- a/drivers/gpu/drm/xe/xe_lrc.c +++ b/drivers/gpu/drm/xe/xe_lrc.c @@ -2618,13 +2618,19 @@ void xe_lrc_snapshot_free(struct xe_lrc_snapshot *snapshot) kfree(snapshot); } +static bool engine_valid_for_utilization(struct xe_gt *gt, struct xe_hw_engine *hwe) +{ + /* The USM-reserved copy engine runs kernel migrate contexts queried here */ + return hwe && (!xe_hw_engine_is_reserved(hwe) || xe_gt_is_usm_hwe(gt, hwe)); +} + static struct xe_hw_engine *engine_id_to_hwe(struct xe_gt *gt, u32 engine_id) { u16 class = REG_FIELD_GET(ENGINE_CLASS_ID, engine_id); u16 instance = REG_FIELD_GET(ENGINE_INSTANCE_ID, engine_id); struct xe_hw_engine *hwe = xe_gt_hw_engine(gt, class, instance, false); - if (xe_gt_WARN_ONCE(gt, !hwe || xe_hw_engine_is_reserved(hwe), + if (xe_gt_WARN_ONCE(gt, !engine_valid_for_utilization(gt, hwe), "Unexpected engine class:instance %d:%d for utilization\n", class, instance)) return NULL; diff --git a/drivers/gpu/drm/xe/xe_mmio.c b/drivers/gpu/drm/xe/xe_mmio.c index 78adb303b663..7fa18dfcb5a2 100644 --- a/drivers/gpu/drm/xe/xe_mmio.c +++ b/drivers/gpu/drm/xe/xe_mmio.c @@ -11,27 +11,18 @@ #include <linux/pci.h> #include <drm/drm_managed.h> -#include <drm/drm_print.h> #include "regs/xe_bars.h" #include "xe_device.h" #include "xe_gt_sriov_vf.h" +#include "xe_printk.h" #include "xe_sriov.h" +#include "xe_tile_printk.h" #include "xe_trace.h" #include "xe_wa.h" #include "generated/xe_device_wa_oob.h" -static void tiles_fini(void *arg) -{ - struct xe_device *xe = arg; - struct xe_tile *tile; - int id; - - for_each_remote_tile(tile, xe, id) - tile->mmio.regs = NULL; -} - /* * On multi-tile devices, partition the BAR space for MMIO on each tile, * possibly accounting for register override on the number of tiles available. @@ -56,50 +47,71 @@ static void mmio_multi_tile_setup(struct xe_device *xe, size_t tile_mmio_size) struct xe_tile *tile; u8 id; - /* - * Nothing to be done as tile 0 has already been setup earlier with the - * entire BAR mapped - see xe_mmio_probe_early() - */ - if (xe->info.tile_count == 1) - return; - for_each_remote_tile(tile, xe, id) xe_mmio_init(&tile->mmio, tile, xe->mmio.regs + id * tile_mmio_size, SZ_4M); } +/** + * xe_mmio_probe_tiles() - Initialize all tiles' MMIO + * @xe: the &xe_device + * + * Initialize the remaining tiles' MMIO instances. + * + * Return: 0 on success or a negative error code on failure. + */ int xe_mmio_probe_tiles(struct xe_device *xe) { size_t tile_mmio_size = SZ_16M; - mmio_multi_tile_setup(xe, tile_mmio_size); + /* + * Nothing to be done as tile 0 has already been setup earlier with the + * entire BAR mapped - see xe_mmio_probe_early() + */ + if (xe->info.tile_count == 1) + return 0; - return devm_add_action_or_reset(xe->drm.dev, tiles_fini, xe); + if (xe->mmio.size < xe->info.tile_count * tile_mmio_size) { + xe_err(xe, "GTTMMADR_BAR is too small for %d tiles: %zu\n", + xe->info.tile_count, xe->mmio.size); + return -EIO; + } + + mmio_multi_tile_setup(xe, tile_mmio_size); + return 0; } static void mmio_fini(void *arg) { struct xe_device *xe = arg; - struct xe_tile *root_tile = xe_device_get_root_tile(xe); - pci_iounmap(to_pci_dev(xe->drm.dev), xe->mmio.regs); xe->mmio.regs = NULL; - root_tile->mmio.regs = NULL; } +/** + * xe_mmio_probe_early() - Probe and initialize device's MMIO + * @xe: the &xe_device + * + * Map the entire GTTMMADR_BAR and initialize the first tile's MMIO instance. + * + * The first 16MB of the GTTMMADR_BAR always belongs to the root tile, and + * includes: registers (0-4MB), reserved space (4MB-8MB) and GGTT (8MB-16MB). + * + * Return: 0 on success or a negative error code on failure. + */ int xe_mmio_probe_early(struct xe_device *xe) { struct xe_tile *root_tile = xe_device_get_root_tile(xe); struct pci_dev *pdev = to_pci_dev(xe->drm.dev); - /* - * Map the entire BAR. - * The first 16MB of the BAR, belong to the root tile, and include: - * registers (0-4MB), reserved space (4MB-8MB) and GGTT (8MB-16MB). - */ - xe->mmio.size = pci_resource_len(pdev, GTTMMADR_BAR); - xe->mmio.regs = pci_iomap(pdev, GTTMMADR_BAR, 0); + xe->mmio.regs = pcim_iomap(pdev, GTTMMADR_BAR, 0); if (!xe->mmio.regs) { - drm_err(&xe->drm, "failed to map registers\n"); + xe_err(xe, "Failed to map GTTMMADR_BAR\n"); + return -EIO; + } + + xe->mmio.size = pci_resource_len(pdev, GTTMMADR_BAR); + if (xe->mmio.size < SZ_16M) { + xe_err(xe, "GTTMMADR_BAR is too small: %zu\n", xe->mmio.size); return -EIO; } @@ -128,6 +140,11 @@ void xe_mmio_init(struct xe_mmio *mmio, struct xe_tile *tile, void __iomem *ptr, mmio->tile = tile; } +static bool mmio_available(struct xe_mmio *mmio) +{ + return !xe_tile_WARN_ON_ONCE(mmio->tile, !mmio->tile->xe->mmio.regs); +} + static void mmio_flush_pending_writes(struct xe_mmio *mmio) { #define DUMMY_REG_OFFSET 0x130030 @@ -146,6 +163,9 @@ u8 xe_mmio_read8(struct xe_mmio *mmio, struct xe_reg reg) u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); u8 val; + if (!mmio_available(mmio)) + return 0; + mmio_flush_pending_writes(mmio); val = readb(mmio->regs + addr); @@ -158,6 +178,9 @@ void xe_mmio_write8(struct xe_mmio *mmio, struct xe_reg reg, u8 val) { u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); + if (!mmio_available(mmio)) + return; + trace_xe_reg_rw(mmio, true, addr, val, sizeof(val)); writeb(val, mmio->regs + addr); @@ -168,6 +191,9 @@ u16 xe_mmio_read16(struct xe_mmio *mmio, struct xe_reg reg) u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); u16 val; + if (!mmio_available(mmio)) + return 0; + mmio_flush_pending_writes(mmio); val = readw(mmio->regs + addr); @@ -180,6 +206,9 @@ void xe_mmio_write32(struct xe_mmio *mmio, struct xe_reg reg, u32 val) { u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); + if (!mmio_available(mmio)) + return; + trace_xe_reg_rw(mmio, true, addr, val, sizeof(val)); if (!reg.vf && IS_SRIOV_VF(mmio->tile->xe)) @@ -194,6 +223,9 @@ u32 xe_mmio_read32(struct xe_mmio *mmio, struct xe_reg reg) u32 addr = xe_mmio_adjusted_addr(mmio, reg.addr); u32 val; + if (!mmio_available(mmio)) + return 0; + mmio_flush_pending_writes(mmio); if (!reg.vf && IS_SRIOV_VF(mmio->tile->xe)) @@ -282,8 +314,8 @@ u64 xe_mmio_read64_2x32(struct xe_mmio *mmio, struct xe_reg reg) oldudw = udw; } - drm_WARN(&mmio->tile->xe->drm, retries == 0, - "64-bit read of %#x did not stabilize\n", reg.addr); + xe_tile_WARN(mmio->tile, retries == 0, + "MMIO: 64-bit read of %#x did not stabilize\n", reg.addr); return (u64)udw << 32 | ldw; } diff --git a/drivers/gpu/drm/xe/xe_module.c b/drivers/gpu/drm/xe/xe_module.c index 4cb578182912..39e4fc85f019 100644 --- a/drivers/gpu/drm/xe/xe_module.c +++ b/drivers/gpu/drm/xe/xe_module.c @@ -36,9 +36,6 @@ module_param_named(svm_notifier_size, xe_modparam.svm_notifier_size, uint, 0600) MODULE_PARM_DESC(svm_notifier_size, "Set the svm notifier size in MiB, must be power of 2 " "[default=" __stringify(XE_DEFAULT_SVM_NOTIFIER_SIZE) "]"); -module_param_named_unsafe(force_execlist, xe_modparam.force_execlist, bool, 0444); -MODULE_PARM_DESC(force_execlist, "Force Execlist submission"); - #if IS_ENABLED(CONFIG_DRM_XE_DISPLAY) module_param_named(probe_display, xe_modparam.probe_display, bool, 0444); MODULE_PARM_DESC(probe_display, "Probe display HW, otherwise it's left untouched " diff --git a/drivers/gpu/drm/xe/xe_module.h b/drivers/gpu/drm/xe/xe_module.h index 79cb9639c0f3..c75153471248 100644 --- a/drivers/gpu/drm/xe/xe_module.h +++ b/drivers/gpu/drm/xe/xe_module.h @@ -10,7 +10,6 @@ /* Module modprobe variables */ struct xe_modparam { - bool force_execlist; bool probe_display; int force_vram_bar_size; int guc_log_level; diff --git a/drivers/gpu/drm/xe/xe_oa.c b/drivers/gpu/drm/xe/xe_oa.c index 4bf4b1f65929..b3acbcd678b7 100644 --- a/drivers/gpu/drm/xe/xe_oa.c +++ b/drivers/gpu/drm/xe/xe_oa.c @@ -37,6 +37,7 @@ #include "xe_oa.h" #include "xe_observation.h" #include "xe_pm.h" +#include "xe_reg_whitelist.h" #include "xe_sched_job.h" #include "xe_sriov.h" #include "xe_sync.h" @@ -885,6 +886,9 @@ static void xe_oa_stream_destroy(struct xe_oa_stream *stream) mutex_destroy(&stream->stream_lock); + if (stream->sample) + xe_reg_dewhitelist_oa_regs(stream->gt); + xe_oa_disable_metric_set(stream); xe_exec_queue_put(stream->k_exec_q); @@ -1698,11 +1702,12 @@ static int xe_oa_release(struct inode *inode, struct file *file) static int xe_oa_mmap(struct file *file, struct vm_area_struct *vma) { struct xe_oa_stream *stream = file->private_data; + int ret = xe_observation_paranoid_check(); struct xe_bo *bo = stream->oa_buffer.bo; - if (xe_observation_paranoid && !perfmon_capable()) { + if (ret) { drm_dbg(&stream->oa->xe->drm, "Insufficient privilege to map OA buffer\n"); - return -EACCES; + return ret; } /* Can mmap the entire OA buffer or nothing (no partial OA buffer mmaps) */ @@ -1885,6 +1890,9 @@ static int xe_oa_stream_open_ioctl_locked(struct xe_oa *oa, goto err_disable; } + if (stream->sample) + xe_reg_whitelist_oa_regs(stream->gt); + /* Hold a reference on the drm device till stream_fd is released */ drm_dev_get(&stream->oa->xe->drm); @@ -2073,10 +2081,12 @@ int xe_oa_stream_open_ioctl(struct drm_device *dev, u64 data, struct drm_file *f privileged_op = true; } - if (privileged_op && xe_observation_paranoid && !perfmon_capable()) { - drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n"); - ret = -EACCES; - goto err_exec_q; + if (privileged_op) { + ret = xe_observation_paranoid_check(); + if (ret) { + drm_dbg(&oa->xe->drm, "Insufficient privileges to open xe OA stream\n"); + goto err_exec_q; + } } if (!param.exec_q && !param.sample) { @@ -2358,9 +2368,10 @@ int xe_oa_add_config_ioctl(struct drm_device *dev, u64 data, struct drm_file *fi return -ENODEV; } - if (xe_observation_paranoid && !perfmon_capable()) { + err = xe_observation_paranoid_check(); + if (err) { drm_dbg(&oa->xe->drm, "Insufficient privileges to add xe OA config\n"); - return -EACCES; + return err; } err = copy_from_user(¶m, u64_to_user_ptr(data), sizeof(param)); @@ -2460,9 +2471,10 @@ int xe_oa_remove_config_ioctl(struct drm_device *dev, u64 data, struct drm_file return -ENODEV; } - if (xe_observation_paranoid && !perfmon_capable()) { + ret = xe_observation_paranoid_check(); + if (ret) { drm_dbg(&oa->xe->drm, "Insufficient privileges to remove xe OA config\n"); - return -EACCES; + return ret; } ret = get_user(arg, ptr); diff --git a/drivers/gpu/drm/xe/xe_oa_types.h b/drivers/gpu/drm/xe/xe_oa_types.h index 3d9ec8490899..e876e9be92ba 100644 --- a/drivers/gpu/drm/xe/xe_oa_types.h +++ b/drivers/gpu/drm/xe/xe_oa_types.h @@ -126,6 +126,9 @@ struct xe_oa_gt { /** @oa_unit: array of oa_units */ struct xe_oa_unit *oa_unit; + + /** @whitelist_count: number of open streams for which oa registers are whitelisted */ + u32 whitelist_count; }; /** diff --git a/drivers/gpu/drm/xe/xe_observation.c b/drivers/gpu/drm/xe/xe_observation.c index e3f9b546207e..39e05b9131a7 100644 --- a/drivers/gpu/drm/xe/xe_observation.c +++ b/drivers/gpu/drm/xe/xe_observation.c @@ -4,6 +4,7 @@ */ #include <linux/errno.h> +#include <linux/perf_event.h> #include <linux/sysctl.h> #include <uapi/drm/xe_drm.h> @@ -12,9 +13,28 @@ #include "xe_oa.h" #include "xe_observation.h" -u32 xe_observation_paranoid = true; +static u32 xe_observation_paranoid = true; static struct ctl_table_header *sysctl_header; +/** + * xe_observation_paranoid_check - Gate access to xe observation streams. + * + * When the xe-specific observation_paranoid sysctl is enabled (the + * default), defer to perf_allow_cpu() so that access is governed by the + * same policy as system-wide perf CPU events: kernel.perf_event_paranoid + * plus the security_perf_event_open() LSM hook. When the sysctl has been + * cleared by a privileged user, observation is open to all callers. + * + * Return: 0 if access is permitted, a negative errno otherwise. + */ +int xe_observation_paranoid_check(void) +{ + if (!xe_observation_paranoid) + return 0; + + return perf_allow_cpu(); +} + static int xe_oa_ioctl(struct drm_device *dev, struct drm_xe_observation_param *arg, struct drm_file *file) { @@ -83,11 +103,13 @@ static const struct ctl_table observation_ctl_table[] = { }; /** - * xe_observation_sysctl_register - Register xe_observation_paranoid sysctl + * xe_observation_sysctl_register - Register the observation_paranoid sysctl * - * Normally only superuser/root can access observation stream - * data. However, superuser can set xe_observation_paranoid sysctl to 0 to - * allow non-privileged users to also access observation data. + * When dev.xe.observation_paranoid is set (the default), access to + * observation streams follows the system-wide perf_allow_cpu() policy: + * kernel.perf_event_paranoid plus the security_perf_event_open() LSM + * hook. A privileged user can clear the sysctl to bypass that gate and + * allow unprivileged access to observation data. * * Return: always returns 0 */ diff --git a/drivers/gpu/drm/xe/xe_observation.h b/drivers/gpu/drm/xe/xe_observation.h index 17816998e966..73a03e03c96a 100644 --- a/drivers/gpu/drm/xe/xe_observation.h +++ b/drivers/gpu/drm/xe/xe_observation.h @@ -11,8 +11,7 @@ struct drm_device; struct drm_file; -extern u32 xe_observation_paranoid; - +int xe_observation_paranoid_check(void); int xe_observation_ioctl(struct drm_device *dev, void *data, struct drm_file *file); int xe_observation_sysctl_register(void); void xe_observation_sysctl_unregister(void); diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c index 3165686e3e04..91af603e9431 100644 --- a/drivers/gpu/drm/xe/xe_pci.c +++ b/drivers/gpu/drm/xe/xe_pci.c @@ -26,6 +26,7 @@ #include "xe_guc.h" #include "xe_mmio.h" #include "xe_module.h" +#include "xe_pci_error.h" #include "xe_pci_rebar.h" #include "xe_pci_sriov.h" #include "xe_pci_types.h" @@ -355,6 +356,7 @@ static const __maybe_unused struct xe_device_desc pvc_desc = { PLATFORM(PVC), .dma_mask_size = 52, .has_display = false, + .has_drm_ras = true, .has_gsc_nvm = 1, .has_heci_gscfi = 1, .max_gt_per_tile = 1, @@ -457,6 +459,7 @@ static const struct xe_device_desc cri_desc = { PLATFORM(CRESCENTISLAND), .dma_mask_size = 52, .has_display = false, + .has_drm_ras = true, .has_flat_ccs = false, .has_gsc_nvm = 1, .has_i2c = true, @@ -599,8 +602,6 @@ static int read_gmdid(struct xe_device *xe, enum xe_gmdid_type type, u32 *ver, u struct xe_reg gmdid_reg = GMD_ID; u32 val; - KUNIT_STATIC_STUB_REDIRECT(read_gmdid, xe, type, ver, revid); - if (IS_SRIOV_VF(xe)) { /* * To get the value of the GMDID register, VFs must obtain it @@ -726,14 +727,30 @@ static int handle_gmdid(struct xe_device *xe, return 0; } -static void init_devid(struct xe_device *xe) +struct xe_probed_info { + u16 devid; + u8 revid; + u8 tile_count; + struct xe_step_info step; + const struct xe_ip *graphics_ip; + const struct xe_ip *media_ip; +}; + +/* + * Probe from the hardware the info required by xe_info_init_early(). + */ +static int xe_probe_info_early(struct xe_device *xe, + const struct xe_device_desc *desc, + struct xe_probed_info *probed_info) { struct pci_dev *pdev = to_pci_dev(xe->drm.dev); - KUNIT_STATIC_STUB_REDIRECT(init_devid, xe); + probed_info->devid = pdev->device; + probed_info->revid = pdev->revision; - xe->info.devid = pdev->device; - xe->info.revid = pdev->revision; + xe_step_platform_get(desc->platform, probed_info->revid, &probed_info->step); + + return 0; } /* @@ -742,17 +759,20 @@ static void init_devid(struct xe_device *xe) */ static int xe_info_init_early(struct xe_device *xe, const struct xe_device_desc *desc, - const struct xe_subplatform_desc *subplatform_desc) + const struct xe_subplatform_desc *subplatform_desc, + struct xe_probed_info *probed_info) { int err; + xe->info.devid = probed_info->devid; + xe->info.revid = probed_info->revid; + xe->info.step.platform = probed_info->step.platform; + xe->info.platform_name = desc->platform_name; xe->info.platform = desc->platform; xe->info.subplatform = subplatform_desc ? subplatform_desc->subplatform : XE_SUBPLATFORM_NONE; - init_devid(xe); - xe->info.dma_mask_size = desc->dma_mask_size; xe->info.va_bits = desc->va_bits; xe->info.vm_max_level = desc->vm_max_level; @@ -760,6 +780,7 @@ static int xe_info_init_early(struct xe_device *xe, xe->info.is_dgfx = desc->is_dgfx; xe->info.has_cached_pt = desc->has_cached_pt; + xe->info.has_drm_ras = desc->has_drm_ras; xe->info.has_fan_control = desc->has_fan_control; /* runtime fusing may force flat_ccs to disabled later */ xe->info.has_flat_ccs = desc->has_flat_ccs; @@ -789,14 +810,10 @@ static int xe_info_init_early(struct xe_device *xe, xe->info.probe_display = IS_ENABLED(CONFIG_DRM_XE_DISPLAY) && xe_modparam.probe_display && desc->has_display; - xe->info.force_execlist = xe_modparam.force_execlist; xe_assert(xe, desc->max_gt_per_tile > 0); xe_assert(xe, desc->max_gt_per_tile <= XE_MAX_GT_PER_TILE); xe->info.max_gt_per_tile = desc->max_gt_per_tile; - xe->info.tile_count = 1 + desc->max_remote_tiles; - - xe_step_platform_get(xe); err = xe_tile_init_early(xe_device_get_root_tile(xe), xe, 0); if (err) @@ -805,22 +822,21 @@ static int xe_info_init_early(struct xe_device *xe, return 0; } -/* - * Possibly override number of tile based on configuration register. - */ -static void xe_info_probe_tile_count(struct xe_device *xe) +static void xe_probe_tile_count(struct xe_device *xe, + const struct xe_device_desc *desc, + struct xe_probed_info *probed_info) { struct xe_mmio *mmio; u8 tile_count; u32 mtcfg; - KUNIT_STATIC_STUB_REDIRECT(xe_info_probe_tile_count, xe); + probed_info->tile_count = 1 + desc->max_remote_tiles; /* * Probe for tile count only for platforms that support multiple * tiles. */ - if (xe->info.tile_count == 1) + if (probed_info->tile_count == 1) return; mmio = xe_root_tile_mmio(xe); @@ -833,10 +849,10 @@ static void xe_info_probe_tile_count(struct xe_device *xe) mtcfg = xe_mmio_read32(mmio, XEHP_MTCFG_ADDR); tile_count = REG_FIELD_GET(TILE_COUNT, mtcfg) + 1; - if (tile_count < xe->info.tile_count) { + if (tile_count < probed_info->tile_count) { drm_info(&xe->drm, "tile_count: %d, reduced_tile_count %d\n", - xe->info.tile_count, tile_count); - xe->info.tile_count = tile_count; + probed_info->tile_count, tile_count); + probed_info->tile_count = tile_count; } } @@ -909,25 +925,10 @@ static struct xe_gt *alloc_media_gt(struct xe_tile *tile, return gt; } -/* - * Initialize device info content that does require knowledge about - * graphics / media IP version. - * Make sure that GT / tile structures allocated by the driver match the data - * present in device info. - */ -static int xe_info_init(struct xe_device *xe, - const struct xe_device_desc *desc) +static int xe_probe_ips(struct xe_device *xe, + const struct xe_device_desc *desc, + struct xe_probed_info *probed_info) { - u32 graphics_gmdid_revid = 0, media_gmdid_revid = 0; - const struct xe_ip *graphics_ip; - const struct xe_ip *media_ip; - const struct xe_graphics_desc *graphics_desc; - const struct xe_media_desc *media_desc; - struct xe_tile *tile; - struct xe_gt *gt; - int ret; - u8 id; - /* * If this platform supports GMD_ID, we'll detect the proper IP * descriptor to use from hardware registers. @@ -936,17 +937,21 @@ static int xe_info_init(struct xe_device *xe, * versions are simply derived from that. */ if (desc->pre_gmdid_graphics_ip) { - graphics_ip = desc->pre_gmdid_graphics_ip; - media_ip = desc->pre_gmdid_media_ip; - xe_step_pre_gmdid_get(xe); + probed_info->graphics_ip = desc->pre_gmdid_graphics_ip; + probed_info->media_ip = desc->pre_gmdid_media_ip; + xe_step_pre_gmdid_get(xe, &probed_info->step); } else { + int err; + u32 graphics_revid, media_revid; + xe_assert(xe, !desc->pre_gmdid_media_ip); - ret = handle_gmdid(xe, &graphics_ip, &media_ip, - &graphics_gmdid_revid, &media_gmdid_revid); - if (ret) - return ret; - xe_step_gmdid_get(xe, graphics_gmdid_revid, media_gmdid_revid); + err = handle_gmdid(xe, &probed_info->graphics_ip, &probed_info->media_ip, + &graphics_revid, &media_revid); + if (err) + return err; + + xe_step_gmdid_get(xe, graphics_revid, media_revid, &probed_info->step); } /* @@ -954,9 +959,56 @@ static int xe_info_init(struct xe_device *xe, * error and we should abort driver load. Failing to detect media * IP is non-fatal; we'll just proceed without enabling media support. */ - if (!graphics_ip) + if (!probed_info->graphics_ip) return -ENODEV; + return 0; +} + +/* + * Probe from the hardware the info required by xe_info_init(). + */ +static int xe_probe_info(struct xe_device *xe, + const struct xe_device_desc *desc, + struct xe_probed_info *probed_info) +{ + int err; + + xe_probe_tile_count(xe, desc, probed_info); + + err = xe_probe_ips(xe, desc, probed_info); + if (err) + return err; + + return 0; +} + +/* + * Initialize device info content that does require knowledge about + * graphics / media IP version. + * Make sure that GT / tile structures allocated by the driver match the data + * present in device info. + */ +static int xe_info_init(struct xe_device *xe, + const struct xe_device_desc *desc, + struct xe_probed_info *probed_info) +{ + const struct xe_ip *graphics_ip; + const struct xe_ip *media_ip; + const struct xe_graphics_desc *graphics_desc; + const struct xe_media_desc *media_desc; + struct xe_tile *tile; + struct xe_gt *gt; + u8 id; + + graphics_ip = probed_info->graphics_ip; + media_ip = probed_info->media_ip; + + xe->info.tile_count = probed_info->tile_count; + xe->info.step.basedie = probed_info->step.basedie; + xe->info.step.graphics = probed_info->step.graphics; + xe->info.step.media = probed_info->step.media; + xe->info.graphics_verx100 = graphics_ip->verx100; xe->info.graphics_name = graphics_ip->name; graphics_desc = graphics_ip->desc; @@ -988,8 +1040,6 @@ static int xe_info_init(struct xe_device *xe, xe->info.has_soc_remapper_telem = 0; } - xe_info_probe_tile_count(xe); - for_each_remote_tile(tile, xe, id) { int err; @@ -1072,9 +1122,11 @@ static void xe_pci_remove(struct pci_dev *pdev) */ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { + struct xe_probed_info probed_info = {}; const struct xe_device_desc *desc = (const void *)ent->driver_data; const struct xe_subplatform_desc *subplatform_desc; struct xe_device *xe; + void *group; int err; subplatform_desc = find_subplatform(desc, pdev->device); @@ -1102,6 +1154,11 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (xe_display_driver_probe_defer(pdev)) return -EPROBE_DEFER; + /* Group all devres so xe_pci_error_slot_reset() can release them as a unit. */ + group = devres_open_group(&pdev->dev, NULL, GFP_KERNEL); + if (!group) + return -ENOMEM; + err = pcim_enable_device(pdev); if (err) return err; @@ -1110,13 +1167,19 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (IS_ERR(xe)) return PTR_ERR(xe); + xe->devres_group = group; + pci_set_drvdata(pdev, &xe->drm); xe_pm_assert_unbounded_bridge(xe); pci_set_master(pdev); - err = xe_info_init_early(xe, desc, subplatform_desc); + err = xe_probe_info_early(xe, desc, &probed_info); + if (err) + return err; + + err = xe_info_init_early(xe, desc, subplatform_desc, &probed_info); if (err) return err; @@ -1135,7 +1198,11 @@ static int xe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) return err; - err = xe_info_init(xe, desc); + err = xe_probe_info(xe, desc, &probed_info); + if (err) + return err; + + err = xe_info_init(xe, desc, &probed_info); if (err) return err; @@ -1348,6 +1415,7 @@ static struct pci_driver xe_pci_driver = { .remove = xe_pci_remove, .shutdown = xe_pci_shutdown, .sriov_configure = xe_pci_sriov_configure, + .err_handler = &xe_pci_error_handlers, #ifdef CONFIG_PM_SLEEP .driver.pm = &xe_pm_ops, #endif diff --git a/drivers/gpu/drm/xe/xe_pci_error.c b/drivers/gpu/drm/xe/xe_pci_error.c new file mode 100644 index 000000000000..9b78cc0d3293 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_pci_error.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include <linux/pci.h> + +#include "xe_device.h" +#include "xe_gt.h" +#include "xe_pci.h" +#include "xe_pm.h" +#include "xe_printk.h" +#include "xe_survivability_mode.h" + +static void prepare_device_for_reset(struct pci_dev *pdev) +{ + struct xe_device *xe = pdev_to_xe_device(pdev); + struct xe_gt *gt; + u8 id; + + /* + * Wedge the device to prevent userspace access but do not send the uevent. + * xe_device_wedged_fini() releases runtime pm if wedged flag is set, so acquire a runtime + * pm reference to avoid underflow. + */ + if (!atomic_xchg(&xe->wedged.flag, 1)) + xe_pm_runtime_get_noresume(xe); + + xe_device_set_in_reset(xe); + + for_each_gt(gt, xe, id) + xe_gt_declare_wedged(gt); + + pci_disable_device(pdev); +} + +static pci_ers_result_t xe_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) +{ + struct xe_device *xe = pdev_to_xe_device(pdev); + + xe_info(xe, "PCI error: detected state = %d\n", state); + + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + + /* If the device is already wedged or in survivability mode, do not attempt recovery */ + if (xe_survivability_mode_is_boot_enabled(xe) || xe_device_wedged(xe)) + return PCI_ERS_RESULT_DISCONNECT; + + switch (state) { + case pci_channel_io_normal: + return PCI_ERS_RESULT_CAN_RECOVER; + case pci_channel_io_frozen: + prepare_device_for_reset(pdev); + return PCI_ERS_RESULT_NEED_RESET; + default: + xe_info(xe, "PCI error: unknown state %d\n", state); + return PCI_ERS_RESULT_DISCONNECT; + } +} + +static pci_ers_result_t xe_pci_error_mmio_enabled(struct pci_dev *pdev) +{ + struct xe_device *xe = pdev_to_xe_device(pdev); + + xe_info(xe, "PCI error: MMIO enabled\n"); + + /* TODO: Query system controller for the type of error and take appropriate action */ + return PCI_ERS_RESULT_RECOVERED; +} + +static pci_ers_result_t xe_pci_error_slot_reset(struct pci_dev *pdev) +{ + const struct pci_device_id *ent = pci_match_id(pdev->driver->id_table, pdev); + struct xe_device *xe = pdev_to_xe_device(pdev); + + xe_info(xe, "PCI error: slot reset\n"); + + pci_restore_state(pdev); + + if (pci_enable_device(pdev)) { + xe_err(xe, "Cannot re-enable PCI device after reset\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + /* + * Secondary Bus Reset causes all VRAM state to be lost along with + * hardware state. As an initial step, re-probe the device to + * re-initialize the driver and hardware. + * TODO: optimize by re-initializing only the hardware state and re-creating + * kernel BOs. + */ + xe_device_clear_in_reset(xe); + pdev->driver->remove(pdev); + devres_release_group(&pdev->dev, xe->devres_group); + + if (pdev->driver->probe(pdev, ent)) + return PCI_ERS_RESULT_DISCONNECT; + + xe = pdev_to_xe_device(pdev); + + /* Wedge the device to prevent I/O operations till the resume callback */ + atomic_set(&xe->wedged.flag, 1); + + return PCI_ERS_RESULT_RECOVERED; +} + +static void xe_pci_error_resume(struct pci_dev *pdev) +{ + struct xe_device *xe = pdev_to_xe_device(pdev); + + xe_info(xe, "PCI error: resume\n"); + + atomic_set(&xe->wedged.flag, 0); +} + +const struct pci_error_handlers xe_pci_error_handlers = { + .error_detected = xe_pci_error_detected, + .mmio_enabled = xe_pci_error_mmio_enabled, + .slot_reset = xe_pci_error_slot_reset, + .resume = xe_pci_error_resume, +}; diff --git a/drivers/gpu/drm/xe/xe_pci_error.h b/drivers/gpu/drm/xe/xe_pci_error.h new file mode 100644 index 000000000000..725ad0214e62 --- /dev/null +++ b/drivers/gpu/drm/xe/xe_pci_error.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2026 Intel Corporation + */ + +#ifndef _XE_PCI_ERROR_H_ +#define _XE_PCI_ERROR_H_ + +struct pci_error_handlers; + +extern const struct pci_error_handlers xe_pci_error_handlers; + +#endif diff --git a/drivers/gpu/drm/xe/xe_pci_types.h b/drivers/gpu/drm/xe/xe_pci_types.h index 5b85e2c24b7b..24d4a3d00517 100644 --- a/drivers/gpu/drm/xe/xe_pci_types.h +++ b/drivers/gpu/drm/xe/xe_pci_types.h @@ -40,6 +40,7 @@ struct xe_device_desc { u8 has_cached_pt:1; u8 has_display:1; + u8 has_drm_ras:1; u8 has_fan_control:1; u8 has_flat_ccs:1; u8 has_gsc_nvm:1; diff --git a/drivers/gpu/drm/xe/xe_pcode.c b/drivers/gpu/drm/xe/xe_pcode.c index dc66d0c7ee06..866986694d9c 100644 --- a/drivers/gpu/drm/xe/xe_pcode.c +++ b/drivers/gpu/drm/xe/xe_pcode.c @@ -323,15 +323,17 @@ int xe_pcode_ready(struct xe_device *xe, bool locked) } /** - * xe_pcode_init: initialize components of PCODE + * xe_pcode_init_early() - Initialize components of PCODE * @tile: tile instance * * This function initializes the xe_pcode component. * To be called once only during probe. + * + * Return: 0 on success or a negative error code on failure. */ -void xe_pcode_init(struct xe_tile *tile) +int xe_pcode_init_early(struct xe_tile *tile) { - drmm_mutex_init(&tile_to_xe(tile)->drm, &tile->pcode.lock); + return drmm_mutex_init(&tile_to_xe(tile)->drm, &tile->pcode.lock); } /** diff --git a/drivers/gpu/drm/xe/xe_pcode.h b/drivers/gpu/drm/xe/xe_pcode.h index 490e4f269607..18260c29e620 100644 --- a/drivers/gpu/drm/xe/xe_pcode.h +++ b/drivers/gpu/drm/xe/xe_pcode.h @@ -12,7 +12,7 @@ struct drm_device; struct xe_device; struct xe_tile; -void xe_pcode_init(struct xe_tile *tile); +int xe_pcode_init_early(struct xe_tile *tile); int xe_pcode_probe_early(struct xe_device *xe); int xe_pcode_ready(struct xe_device *xe, bool locked); int xe_pcode_init_min_freq_table(struct xe_tile *tile, u32 min_gt_freq, diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 18a98667c0e6..5fdad444009f 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -433,6 +433,7 @@ xe_pt_insert_entry(struct xe_pt_stage_bind_walk *xe_walk, struct xe_pt *parent, static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, struct xe_pt_stage_bind_walk *xe_walk) { + struct xe_bo *bo = xe_vma_bo(xe_walk->vma); u64 size, dma; if (level > MAX_HUGEPTE_LEVEL) @@ -446,8 +447,8 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, if (next - xe_walk->va_curs_start > xe_walk->curs->size) return false; - /* null VMA's do not have dma addresses */ - if (xe_vma_is_null(xe_walk->vma)) + /* null VMA's and purged BO's do not have dma addresses */ + if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo))) return true; /* if we are clearing page table, no dma addresses*/ @@ -468,6 +469,7 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, static bool xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk) { + struct xe_bo *bo = xe_vma_bo(xe_walk->vma); struct xe_res_cursor curs = *xe_walk->curs; if (!IS_ALIGNED(addr, SZ_64K)) @@ -476,8 +478,8 @@ xe_pt_scan_64K(u64 addr, u64 next, struct xe_pt_stage_bind_walk *xe_walk) if (next > xe_walk->l0_end_addr) return false; - /* null VMA's do not have dma addresses */ - if (xe_vma_is_null(xe_walk->vma)) + /* null VMA's and purged BO's do not have dma addresses */ + if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo))) return true; xe_res_next(&curs, addr - xe_walk->va_curs_start); @@ -708,7 +710,7 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, { struct xe_device *xe = tile_to_xe(tile); struct xe_bo *bo = xe_vma_bo(vma); - struct xe_res_cursor curs; + struct xe_res_cursor curs = {}; struct xe_vm *vm = xe_vma_vm(vma); struct xe_pt_stage_bind_walk xe_walk = { .base = { @@ -885,13 +887,21 @@ static int xe_pt_zap_ptes_entry(struct xe_ptw *parent, pgoff_t offset, { struct xe_pt_zap_ptes_walk *xe_walk = container_of(walk, typeof(*xe_walk), base); - struct xe_pt *xe_child = container_of(*child, typeof(*xe_child), base); + struct xe_pt *xe_child; pgoff_t end_offset; - XE_WARN_ON(!*child); XE_WARN_ON(!level); /* + * Below would be unexpected behavior that needs to be root caused + * but better warn and bail than crash the driver. + */ + if (XE_WARN_ON(!*child)) + return 0; + + xe_child = container_of(*child, typeof(*xe_child), base); + + /* * Note that we're called from an entry callback, and we're dealing * with the child of that entry rather than the parent, so need to * adjust level down. @@ -1016,12 +1026,22 @@ xe_vm_populate_pgtable(struct xe_migrate_pt_update *pt_update, struct xe_tile *t u64 *ptr = data; u32 i; + /* + * @qword_ofs is the absolute entry offset within the page table, while + * @ptes is indexed relative to @update->ofs (its first entry). The GPU + * path (write_pgtable) splits a single update into MAX_PTE_PER_SDI-sized + * chunks, calling this with an advancing @qword_ofs but a fresh @data + * pointer per chunk, so translate back into a @ptes index rather than + * assuming the chunk starts at ptes[0]. + */ for (i = 0; i < num_qwords; i++) { + u32 idx = qword_ofs - update->ofs + i; + if (map) xe_map_wr(tile_to_xe(tile), map, (qword_ofs + i) * - sizeof(u64), u64, ptes[i].pte); + sizeof(u64), u64, ptes[idx].pte); else - ptr[i] = ptes[i].pte; + ptr[i] = ptes[idx].pte; } } @@ -1078,7 +1098,7 @@ static void xe_pt_commit_locks_assert(struct xe_vma *vma) xe_pt_commit_prepare_locks_assert(vma); if (xe_vma_is_userptr(vma)) - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); } static void xe_pt_commit(struct xe_vma *vma, @@ -1398,6 +1418,33 @@ static int xe_pt_pre_commit(struct xe_migrate_pt_update *pt_update) pt_update_ops, rftree); } +/* + * Acquire/release the svm notifier_lock around xe_pt_svm_userptr_pre_commit() + * and the matching late release in xe_pt_update_ops_run(). Read mode by + * default; write mode when CONFIG_DRM_XE_USERPTR_INVAL_INJECT is on, + * because a userptr op in this critical section may invoke the injected + * xe_vma_userptr_force_invalidate() path that calls + * drm_gpusvm_unmap_pages() with ctx->in_notifier=true, which requires the + * lock held for write. + */ +static void xe_pt_svm_userptr_notifier_lock(struct xe_vm *vm) +{ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) + down_write(&vm->svm.gpusvm.notifier_lock); +#else + xe_svm_notifier_lock(vm); +#endif +} + +static void xe_pt_svm_userptr_notifier_unlock(struct xe_vm *vm) +{ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) + up_write(&vm->svm.gpusvm.notifier_lock); +#else + xe_svm_notifier_unlock(vm); +#endif +} + #if IS_ENABLED(CONFIG_DRM_GPUSVM) #ifdef CONFIG_DRM_XE_USERPTR_INVAL_INJECT @@ -1429,7 +1476,7 @@ static int vma_check_userptr(struct xe_vm *vm, struct xe_vma *vma, struct xe_userptr_vma *uvma; unsigned long notifier_seq; - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); if (!xe_vma_is_userptr(vma)) return 0; @@ -1459,7 +1506,7 @@ static int op_check_svm_userptr(struct xe_vm *vm, struct xe_vma_op *op, { int err = 0; - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); switch (op->base.op) { case DRM_GPUVA_OP_MAP: @@ -1531,12 +1578,12 @@ static int xe_pt_svm_userptr_pre_commit(struct xe_migrate_pt_update *pt_update) if (err) return err; - xe_svm_notifier_lock(vm); + xe_pt_svm_userptr_notifier_lock(vm); list_for_each_entry(op, &vops->list, link) { err = op_check_svm_userptr(vm, op, pt_update_ops); if (err) { - xe_svm_notifier_unlock(vm); + xe_pt_svm_userptr_notifier_unlock(vm); break; } } @@ -2033,6 +2080,9 @@ static int bind_op_prepare(struct xe_vm *vm, struct xe_tile *tile, * automatically when the context is re-enabled by the rebind worker, * or in fault mode it was invalidated on PTE zapping. * + * If rebind, we have to invalidate TLB on context based TLB invalidation + * LR vms, as they cannot be relied on context re-enable. + * * If !rebind, and scratch enabled VMs, there is a chance the scratch * PTE is already cached in the TLB so it needs to be invalidated. * On !LR VMs this is done in the ring ops preceding a batch, but on @@ -2042,6 +2092,9 @@ static int bind_op_prepare(struct xe_vm *vm, struct xe_tile *tile, if ((!pt_op->rebind && xe_vm_has_scratch(vm) && xe_vm_in_lr_mode(vm))) pt_update_ops->needs_invalidation = true; + else if (pt_op->rebind && xe_vm_in_preempt_fence_mode(vm) && + vm->xe->info.has_ctx_tlb_inval) + pt_update_ops->needs_invalidation = true; else if (pt_op->rebind && !xe_vm_in_lr_mode(vm)) /* We bump also if batch_invalidate_tlb is true */ vm->tlb_flush_seqno++; @@ -2395,7 +2448,7 @@ static void bind_op_commit(struct xe_vm *vm, struct xe_tile *tile, vma->tile_invalidated & ~BIT(tile->id)); vma->tile_staged &= ~BIT(tile->id); if (xe_vma_is_userptr(vma)) { - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); to_userptr_vma(vma)->userptr.initial_bind = true; } @@ -2431,7 +2484,7 @@ static void unbind_op_commit(struct xe_vm *vm, struct xe_tile *tile, if (!vma->tile_present) { list_del_init(&vma->combined_links.rebind); if (xe_vma_is_userptr(vma)) { - xe_svm_assert_held_read(vm); + xe_svm_assert_held_read_or_inject_write(vm); spin_lock(&vm->userptr.invalidated_lock); list_del_init(&to_userptr_vma(vma)->userptr.invalidate_link); @@ -2707,7 +2760,7 @@ xe_pt_update_ops_run(struct xe_tile *tile, struct xe_vma_ops *vops) } if (pt_update_ops->needs_svm_lock) - xe_svm_notifier_unlock(vm); + xe_pt_svm_userptr_notifier_unlock(vm); /* * The last fence is only used for zero bind queue idling; migrate diff --git a/drivers/gpu/drm/xe/xe_pxp.c b/drivers/gpu/drm/xe/xe_pxp.c index 968b7e70b3f9..fea3d8ceeddb 100644 --- a/drivers/gpu/drm/xe/xe_pxp.c +++ b/drivers/gpu/drm/xe/xe_pxp.c @@ -59,6 +59,7 @@ bool xe_pxp_is_enabled(const struct xe_pxp *pxp) static bool pxp_prerequisites_done(const struct xe_pxp *pxp) { struct xe_gt *gt = pxp->gt; + bool huc_ok; bool ready; CLASS(xe_force_wake, fw_ref)(gt_to_fw(gt), XE_FORCEWAKE_ALL); @@ -73,9 +74,14 @@ static bool pxp_prerequisites_done(const struct xe_pxp *pxp) */ XE_WARN_ON(!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)); - /* PXP requires both HuC authentication via GSC and GSC proxy initialized */ - ready = xe_huc_is_authenticated(>->uc.huc, XE_HUC_AUTH_VIA_GSC) && - xe_gsc_proxy_init_done(>->uc.gsc); + /* + * PXP requires GSC proxy to be initialized. On platforms where the HuC + * is loaded by the kernel driver (i.e., pre media 35) PXP also requires + * the HuC to be authenticated by GSC. + */ + huc_ok = MEDIA_VER(gt_to_xe(gt)) >= 35 || + xe_huc_is_authenticated(>->uc.huc, XE_HUC_AUTH_VIA_GSC); + ready = huc_ok && xe_gsc_proxy_init_done(>->uc.gsc); return ready; } @@ -97,9 +103,13 @@ int xe_pxp_get_readiness_status(struct xe_pxp *pxp) if (!xe_pxp_is_enabled(pxp)) return -ENODEV; - /* if the GSC or HuC FW are in an error state, PXP will never work */ - if (xe_uc_fw_status_to_error(pxp->gt->uc.huc.fw.status) || - xe_uc_fw_status_to_error(pxp->gt->uc.gsc.fw.status)) + /* If the GSC FW is in an error state, PXP will never work */ + if (xe_uc_fw_status_to_error(pxp->gt->uc.gsc.fw.status)) + return -EIO; + + /* Same for HuC FW, but only if the kernel owns HuC-loading (i.e. pre-NVL) */ + if (MEDIA_VER(gt_to_xe(pxp->gt)) < 35 && + xe_uc_fw_status_to_error(pxp->gt->uc.huc.fw.status)) return -EIO; guard(xe_pm_runtime)(pxp->xe); @@ -361,6 +371,7 @@ static void pxp_fini(void *arg) int xe_pxp_init(struct xe_device *xe) { struct xe_gt *gt = xe->tiles[0].media_gt; + bool gsc_ok, huc_ok; struct xe_pxp *pxp; int err; @@ -375,10 +386,14 @@ int xe_pxp_init(struct xe_device *xe) if (!(gt->info.engine_mask & BIT(XE_HW_ENGINE_GSCCS0))) return 0; - /* PXP requires both GSC and HuC firmwares to be available */ - if (!xe_uc_fw_is_loadable(>->uc.gsc.fw) || - !xe_uc_fw_is_loadable(>->uc.huc.fw)) { - drm_info(&xe->drm, "skipping PXP init due to missing FW dependencies"); + /* PXP requires GSC FW to be available. Pre-NVL it also requires HuC FW */ + gsc_ok = xe_uc_fw_is_loadable(>->uc.gsc.fw); + huc_ok = MEDIA_VER(xe) >= 35 || xe_uc_fw_is_loadable(>->uc.huc.fw); + + if (!gsc_ok || !huc_ok) { + drm_info(&xe->drm, "Skipping PXP due to unsatisfied FW deps - GSC=%s, HuC=%s\n", + str_yes_no(gsc_ok), + MEDIA_VER(xe) >= 35 ? "not needed" : str_yes_no(huc_ok)); return 0; } diff --git a/drivers/gpu/drm/xe/xe_query.c b/drivers/gpu/drm/xe/xe_query.c index 8c7d54498f38..dc975f595368 100644 --- a/drivers/gpu/drm/xe/xe_query.c +++ b/drivers/gpu/drm/xe/xe_query.c @@ -119,6 +119,7 @@ query_engine_cycles(struct xe_device *xe, struct drm_xe_engine_class_instance *eci; struct drm_xe_query_engine_cycles resp; size_t size = sizeof(resp); + enum xe_force_wake_domains fw_domain; __ktime_func_t cpu_clock; struct xe_hw_engine *hwe; struct xe_gt *gt; @@ -154,8 +155,10 @@ query_engine_cycles(struct xe_device *xe, if (!hwe) return -EINVAL; - xe_with_force_wake(fw_ref, gt_to_fw(gt), XE_FORCEWAKE_ALL) { - if (!xe_force_wake_ref_has_domain(fw_ref.domains, XE_FORCEWAKE_ALL)) + fw_domain = xe_hw_engine_to_fw_domain(hwe); + + xe_with_force_wake(fw_ref, gt_to_fw(gt), fw_domain) { + if (!xe_force_wake_ref_has_domain(fw_ref.domains, fw_domain)) return -EIO; hwe_read_timestamp(hwe, &resp.engine_cycles, &resp.cpu_timestamp, diff --git a/drivers/gpu/drm/xe/xe_ras.c b/drivers/gpu/drm/xe/xe_ras.c index 4cb16b419b0c..74d5016d9ffe 100644 --- a/drivers/gpu/drm/xe/xe_ras.c +++ b/drivers/gpu/drm/xe/xe_ras.c @@ -4,11 +4,15 @@ */ #include "xe_device.h" +#include "xe_drm_ras.h" +#include "xe_pm.h" #include "xe_printk.h" #include "xe_ras.h" #include "xe_ras_types.h" #include "xe_sysctrl.h" #include "xe_sysctrl_event_types.h" +#include "xe_sysctrl_mailbox.h" +#include "xe_sysctrl_mailbox_types.h" /* Severity of detected errors */ enum xe_ras_severity { @@ -31,6 +35,17 @@ enum xe_ras_component { XE_RAS_COMP_MAX }; +/* RAS response status codes */ +enum xe_ras_response_status { + XE_RAS_STATUS_SUCCESS = 0, + XE_RAS_STATUS_INVALID_PARAM, + XE_RAS_STATUS_OP_NOT_SUPPORTED, + XE_RAS_STATUS_TIMEOUT, + XE_RAS_STATUS_HARDWARE_FAILURE, + XE_RAS_STATUS_INSUFFICIENT_RESOURCES, + XE_RAS_STATUS_MAX +}; + static const char *const xe_ras_severities[] = { [XE_RAS_SEV_NOT_SUPPORTED] = "Not Supported", [XE_RAS_SEV_CORRECTABLE] = "Correctable Error", @@ -50,6 +65,56 @@ static const char *const xe_ras_components[] = { }; static_assert(ARRAY_SIZE(xe_ras_components) == XE_RAS_COMP_MAX); +static u8 drm_to_xe_ras_severity(u8 severity) +{ + switch (severity) { + case DRM_XE_RAS_ERR_SEV_CORRECTABLE: + return XE_RAS_SEV_CORRECTABLE; + case DRM_XE_RAS_ERR_SEV_UNCORRECTABLE: + return XE_RAS_SEV_UNCORRECTABLE; + default: + return XE_RAS_SEV_NOT_SUPPORTED; + } +} + +static u8 drm_to_xe_ras_component(u8 component) +{ + switch (component) { + case DRM_XE_RAS_ERR_COMP_CORE_COMPUTE: + return XE_RAS_COMP_CORE_COMPUTE; + case DRM_XE_RAS_ERR_COMP_SOC_INTERNAL: + return XE_RAS_COMP_SOC_INTERNAL; + case DRM_XE_RAS_ERR_COMP_DEVICE_MEMORY: + return XE_RAS_COMP_DEVICE_MEMORY; + case DRM_XE_RAS_ERR_COMP_PCIE: + return XE_RAS_COMP_PCIE; + case DRM_XE_RAS_ERR_COMP_FABRIC: + return XE_RAS_COMP_FABRIC; + default: + return XE_RAS_COMP_NOT_SUPPORTED; + } +} + +static int ras_status_to_errno(u32 status) +{ + switch (status) { + case XE_RAS_STATUS_SUCCESS: + return 0; + case XE_RAS_STATUS_INVALID_PARAM: + return -EINVAL; + case XE_RAS_STATUS_OP_NOT_SUPPORTED: + return -EOPNOTSUPP; + case XE_RAS_STATUS_TIMEOUT: + return -ETIMEDOUT; + case XE_RAS_STATUS_HARDWARE_FAILURE: + return -EIO; + case XE_RAS_STATUS_INSUFFICIENT_RESOURCES: + return -ENOSPC; + default: + return -EPROTO; + } +} + static inline const char *sev_to_str(u8 severity) { if (severity >= XE_RAS_SEV_MAX) @@ -66,6 +131,68 @@ static inline const char *comp_to_str(u8 component) return xe_ras_components[component]; } +static struct pci_dev *find_usp_dev(struct pci_dev *pdev) +{ + struct pci_dev *vsp; + + /* + * Device Hierarchy: + * + * Upstream Switch Port (USP) --> Virtual Switch Port (VSP) --> SGunit (GPU endpoint) + */ + vsp = pci_upstream_bridge(pdev); + if (!vsp) + return NULL; + + return pci_upstream_bridge(vsp); +} + +static void ras_usp_aer_init(struct xe_device *xe) +{ + struct pci_dev *pdev = to_pci_dev(xe->drm.dev); + struct pci_dev *usp; + u16 aer_cap; + u32 status; + + usp = find_usp_dev(pdev); + if (!usp) + return; + + aer_cap = pci_find_ext_capability(usp, PCI_EXT_CAP_ID_ERR); + if (!aer_cap) { + dev_warn(&usp->dev, "AER capability unavailable\n"); + return; + } + + /* + * Clear any stale Uncorrectable Internal Error Status event in Uncorrectable Error + * Status Register. + */ + pci_read_config_dword(usp, aer_cap + PCI_ERR_UNCOR_STATUS, &status); + if (status & PCI_ERR_UNC_INTN) + pci_write_config_dword(usp, aer_cap + PCI_ERR_UNCOR_STATUS, PCI_ERR_UNC_INTN); + + /* + * All errors are steered to USP which is a PCIe AER Compliant device. + * Downgrade all the errors to non-fatal to prevent PCIe bus driver + * from triggering a Secondary Bus Reset (SBR). This allows error + * detection, containment and recovery in the driver. + * + * The Uncorrectable Error Severity Register has the 'Uncorrectable + * Internal Error Severity' set to fatal by default. Set this to + * non-fatal and unmask the error. + */ + + /* Downgrade Uncorrectable Internal Error to non-fatal */ + pci_clear_and_set_config_dword(usp, aer_cap + PCI_ERR_UNCOR_SEVER, PCI_ERR_UNC_INTN, 0); + + /* Unmask Uncorrectable Internal Error */ + pci_clear_and_set_config_dword(usp, aer_cap + PCI_ERR_UNCOR_MASK, PCI_ERR_UNC_INTN, 0); + + pci_save_state(usp); + dev_dbg(&usp->dev, "Uncorrectable Internal Errors downgraded and unmasked\n"); +} + void xe_ras_counter_threshold_crossed(struct xe_device *xe, struct xe_sysctrl_event_response *response) { @@ -91,3 +218,136 @@ void xe_ras_counter_threshold_crossed(struct xe_device *xe, comp_to_str(component), sev_to_str(severity)); } } + +static int get_counter(struct xe_device *xe, struct xe_ras_error_class *counter, u32 *value) +{ + struct xe_ras_get_counter_response response = {0}; + struct xe_ras_get_counter_request request = {0}; + struct xe_sysctrl_mailbox_command command = {0}; + struct xe_ras_error_common *common; + size_t rlen; + int ret; + + request.counter = *counter; + + xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, XE_SYSCTRL_CMD_GET_COUNTER, + &request, sizeof(request), &response, sizeof(response)); + + ret = xe_sysctrl_send_command(&xe->sc, &command, &rlen); + if (ret) { + xe_err(xe, "sysctrl: failed to get counter %d\n", ret); + return ret; + } + + if (rlen != sizeof(response)) { + xe_err(xe, "sysctrl: unexpected get counter response length %zu (expected %zu)\n", + rlen, sizeof(response)); + return -EIO; + } + + common = &response.counter.common; + *value = response.value; + + xe_dbg(xe, "[RAS]: get counter %u for %s %s\n", *value, comp_to_str(common->component), + sev_to_str(common->severity)); + + return 0; +} + +/** + * xe_ras_get_counter() - Get error counter value + * @xe: Xe device instance + * @severity: Error severity to be queried (&enum drm_xe_ras_error_severity) + * @component: Error component to be queried (&enum drm_xe_ras_error_component) + * @value: Counter value + * + * This function retrieves the value of a specific error counter based on + * the error severity and component. + * + * Return: 0 on success, negative error code on failure. + */ +int xe_ras_get_counter(struct xe_device *xe, u8 severity, u8 component, u32 *value) +{ + struct xe_ras_error_class counter = {0}; + + counter.common.severity = drm_to_xe_ras_severity(severity); + counter.common.component = drm_to_xe_ras_component(component); + + guard(xe_pm_runtime)(xe); + return get_counter(xe, &counter, value); +} + +/** + * xe_ras_clear_counter() - Clear error counter value + * @xe: Xe device instance + * @severity: Error severity to be cleared (&enum drm_xe_ras_error_severity) + * @component: Error component to be cleared (&enum drm_xe_ras_error_component) + * + * This function clears the value of a specific error counter based on + * the error severity and component. + * + * Return: 0 on success, negative error code on failure. + */ +int xe_ras_clear_counter(struct xe_device *xe, u8 severity, u8 component) +{ + struct xe_ras_clear_counter_response response = {0}; + struct xe_ras_clear_counter_request request = {0}; + struct xe_sysctrl_mailbox_command command = {0}; + struct xe_ras_error_class *counter; + size_t rlen; + int ret; + + counter = &request.counter; + counter->common.severity = drm_to_xe_ras_severity(severity); + counter->common.component = drm_to_xe_ras_component(component); + + xe_sysctrl_create_command(&command, XE_SYSCTRL_GROUP_GFSP, XE_SYSCTRL_CMD_CLEAR_COUNTER, + &request, sizeof(request), &response, sizeof(response)); + + guard(xe_pm_runtime)(xe); + ret = xe_sysctrl_send_command(&xe->sc, &command, &rlen); + if (ret) { + xe_err(xe, "sysctrl: failed to clear counter %d\n", ret); + return ret; + } + + if (rlen != sizeof(response)) { + xe_err(xe, "sysctrl: unexpected clear counter response length %zu (expected %zu)\n", + rlen, sizeof(response)); + return -EIO; + } + + ret = ras_status_to_errno(response.status); + if (ret) { + xe_err(xe, "sysctrl: clear counter command failed with status %#x\n", + response.status); + return ret; + } + + counter = &response.counter; + + xe_dbg(xe, "[RAS]: clear counter for %s %s\n", comp_to_str(counter->common.component), + sev_to_str(counter->common.severity)); + + return 0; +} + +/** + * xe_ras_init - Initialize Xe RAS + * @xe: xe device instance + * + * Initialize Xe RAS + */ +void xe_ras_init(struct xe_device *xe) +{ + if (!xe->info.has_drm_ras) + return; + + xe_drm_ras_init(xe); + + if (!xe->info.has_sysctrl) + return; + + if (IS_ENABLED(CONFIG_PCIEAER)) + ras_usp_aer_init(xe); +} diff --git a/drivers/gpu/drm/xe/xe_ras.h b/drivers/gpu/drm/xe/xe_ras.h index ea90593b62dc..ba0b0224df23 100644 --- a/drivers/gpu/drm/xe/xe_ras.h +++ b/drivers/gpu/drm/xe/xe_ras.h @@ -6,10 +6,15 @@ #ifndef _XE_RAS_H_ #define _XE_RAS_H_ +#include <linux/types.h> + struct xe_device; struct xe_sysctrl_event_response; void xe_ras_counter_threshold_crossed(struct xe_device *xe, struct xe_sysctrl_event_response *response); +int xe_ras_get_counter(struct xe_device *xe, u8 severity, u8 component, u32 *value); +int xe_ras_clear_counter(struct xe_device *xe, u8 severity, u8 component); +void xe_ras_init(struct xe_device *xe); #endif diff --git a/drivers/gpu/drm/xe/xe_ras_types.h b/drivers/gpu/drm/xe/xe_ras_types.h index 4e63c67f806a..6688e11f57a8 100644 --- a/drivers/gpu/drm/xe/xe_ras_types.h +++ b/drivers/gpu/drm/xe/xe_ras_types.h @@ -70,4 +70,55 @@ struct xe_ras_threshold_crossed { struct xe_ras_error_class counters[XE_RAS_NUM_COUNTERS]; } __packed; +/** + * struct xe_ras_get_counter_request - Request structure for get counter + */ +struct xe_ras_get_counter_request { + /** @counter: Error counter to be queried */ + struct xe_ras_error_class counter; + /** @reserved: Reserved for future use */ + u32 reserved; +} __packed; + +/** + * struct xe_ras_get_counter_response - Response structure for get counter + */ +struct xe_ras_get_counter_response { + /** @counter: Error counter that was queried */ + struct xe_ras_error_class counter; + /** @value: Current counter value */ + u32 value; + /** @timestamp: Timestamp when counter was last updated */ + u64 timestamp; + /** @threshold: Threshold value for the counter */ + u32 threshold; + /** @reserved: Reserved */ + u32 reserved[57]; +} __packed; + +/** + * struct xe_ras_clear_counter_request - Request structure for clear counter + */ +struct xe_ras_clear_counter_request { + /** @counter: Counter class to be cleared */ + struct xe_ras_error_class counter; + /** @reserved: Reserved for future use */ + u32 reserved; +} __packed; + +/** + * struct xe_ras_clear_counter_response - Response structure for clear counter + */ +struct xe_ras_clear_counter_response { + /** @counter: Counter class that was cleared */ + struct xe_ras_error_class counter; + /** @reserved: Reserved */ + u32 reserved; + /** @timestamp: Timestamp when the counter was cleared */ + u64 timestamp; + /** @status: Status of the clear operation */ + u32 status; + /** @reserved1: Reserved for future use */ + u32 reserved1[3]; +} __packed; #endif diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.c b/drivers/gpu/drm/xe/xe_reg_whitelist.c index fb65940848d7..cab1b578ca0e 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.c +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.c @@ -5,6 +5,8 @@ #include "xe_reg_whitelist.h" +#include <kunit/visibility.h> + #include "regs/xe_engine_regs.h" #include "regs/xe_gt_regs.h" #include "regs/xe_oa_regs.h" @@ -41,7 +43,7 @@ static bool match_multi_queue_class(const struct xe_device *xe, return xe_gt_supports_multi_queue(gt, hwe->class); } -static const struct xe_rtp_entry_sr register_whitelist[] = { +VISIBLE_IF_KUNIT const struct xe_rtp_table_sr register_whitelist = XE_RTP_TABLE_SR( { XE_RTP_NAME("WaAllowPMDepthAndInvocationCountAccessFromUMD, 1408556865"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1210), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(WHITELIST(PS_INVOCATION_COUNT, @@ -103,11 +105,17 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { WHITELIST(VFLSKPD, RING_FORCE_TO_NONPRIV_ACCESS_RW)) }, +); +EXPORT_SYMBOL_IF_KUNIT(register_whitelist); + +static const struct xe_rtp_table_sr oa_whitelist = XE_RTP_TABLE_SR( + +#define WHITELIST_DENY(r, f) WHITELIST(r, (f) | RING_FORCE_TO_NONPRIV_DENY) #define WHITELIST_OA_MMIO_TRG(trg, status, head) \ - WHITELIST(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ - WHITELIST(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ - WHITELIST(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) + WHITELIST_DENY(trg, RING_FORCE_TO_NONPRIV_ACCESS_RW), \ + WHITELIST_DENY(status, RING_FORCE_TO_NONPRIV_ACCESS_RD), \ + WHITELIST_DENY(head, RING_FORCE_TO_NONPRIV_ACCESS_RD | RING_FORCE_TO_NONPRIV_RANGE_4) #define WHITELIST_OAG_MMIO_TRG \ WHITELIST_OA_MMIO_TRG(OAG_MMIOTRIGGER, OAG_OASTATUS, OAG_OAHEADPTR) @@ -124,7 +132,7 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { OAM_HEAD_POINTER(XE_OAM_SCMI_1_BASE_ADJ)) #define WHITELIST_OA_MERT_MMIO_TRG \ - WHITELIST_OA_MMIO_TRG(OAMERT_MMIO_TRG, OAMERT_STATUS, OAMERT_HEAD_POINTER) + WHITELIST_OA_MMIO_TRG(OAMERT_MMIO_TRG, OAMERT_STATUS, OAMERT_TAIL_POINTER) { XE_RTP_NAME("oag_mmio_trg_rcs"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, XE_RTP_END_VERSION_UNDEFINED), @@ -154,11 +162,12 @@ static const struct xe_rtp_entry_sr register_whitelist[] = { XE_RTP_RULES(FUNC(match_has_mert), ENGINE_CLASS(COPY)), XE_RTP_ACTIONS(WHITELIST_OA_MERT_MMIO_TRG) }, -}; +); -static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) +static int whitelist_apply_to_hwe(struct xe_hw_engine *hwe, struct xe_reg_sr *in, + struct xe_reg_sr *out, int first_slot) { - struct xe_reg_sr *sr = &hwe->reg_whitelist; + struct xe_reg_sr *sr = in; struct xe_reg_sr_entry *entry; struct drm_printer p; unsigned long reg; @@ -167,7 +176,7 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) xe_gt_dbg(hwe->gt, "Add %s whitelist to engine\n", sr->name); p = xe_gt_dbg_printer(hwe->gt); - slot = 0; + slot = first_slot; xa_for_each(&sr->xa, reg, entry) { struct xe_reg_sr_entry hwe_entry = { .reg = RING_FORCE_TO_NONPRIV(hwe->mmio_base, slot), @@ -184,10 +193,12 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) } xe_reg_whitelist_print_entry(&p, 0, reg, entry); - xe_reg_sr_add(&hwe->reg_sr, &hwe_entry, hwe->gt); + xe_reg_sr_add(out, &hwe_entry, hwe->gt); slot++; } + + return slot; } /** @@ -201,10 +212,78 @@ static void whitelist_apply_to_hwe(struct xe_hw_engine *hwe) void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe) { struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); + int first_oa_slot; + + xe_rtp_process_to_sr(&ctx, ®ister_whitelist, &hwe->reg_whitelist, false); + first_oa_slot = whitelist_apply_to_hwe(hwe, &hwe->reg_whitelist, &hwe->reg_sr, 0); + + xe_rtp_process_to_sr(&ctx, &oa_whitelist, &hwe->oa_whitelist, false); + + /* + * Save oa nonpriv registers to hwe->oa_sr, from which oa registers are whitelisted + * or de-whitelisted, by toggling the 'deny' bit on oa stream open/close + */ + whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->oa_sr, first_oa_slot); + + /* + * Also save oa nonpriv registers to hwe->reg_sr, to ensure oa registers are not + * whitelisted by default after probe, gt reset, resume and engine reset + */ + whitelist_apply_to_hwe(hwe, &hwe->oa_whitelist, &hwe->reg_sr, first_oa_slot); +} + +static void __whitelist_oa_regs(struct xe_hw_engine *hwe, bool whitelist) +{ + struct xe_reg_sr_entry *entry; + unsigned long reg; + + xa_for_each(&hwe->oa_sr.xa, reg, entry) { + if (whitelist) + entry->set_bits &= ~RING_FORCE_TO_NONPRIV_DENY; + else + entry->set_bits |= RING_FORCE_TO_NONPRIV_DENY; + } + + xe_reg_sr_apply_mmio(&hwe->oa_sr, hwe->gt); +} + +/** + * xe_reg_whitelist_oa_regs - whitelist oa registers for gt + * @gt: gt to whitelist oa registers for + * + * Whitelist OA registers by resetting RING_FORCE_TO_NONPRIV_DENY + */ +void xe_reg_whitelist_oa_regs(struct xe_gt *gt) +{ + struct xe_hw_engine *hwe; + enum xe_hw_engine_id id; + + lockdep_assert_held(>->oa.gt_lock); + if (gt->oa.whitelist_count++) + return; + + for_each_hw_engine(hwe, gt, id) + __whitelist_oa_regs(hwe, true); +} + +/** + * xe_reg_dewhitelist_oa_regs - dewhitelist oa registers for gt + * @gt: gt to dewhitelist oa registers for + * + * Dewhitelist OA registers by setting RING_FORCE_TO_NONPRIV_DENY + */ +void xe_reg_dewhitelist_oa_regs(struct xe_gt *gt) +{ + struct xe_hw_engine *hwe; + enum xe_hw_engine_id id; + + lockdep_assert_held(>->oa.gt_lock); + xe_assert(gt_to_xe(gt), gt->oa.whitelist_count); + if (--gt->oa.whitelist_count) + return; - xe_rtp_process_to_sr(&ctx, register_whitelist, ARRAY_SIZE(register_whitelist), - &hwe->reg_whitelist, false); - whitelist_apply_to_hwe(hwe); + for_each_hw_engine(hwe, gt, id) + __whitelist_oa_regs(hwe, false); } /** diff --git a/drivers/gpu/drm/xe/xe_reg_whitelist.h b/drivers/gpu/drm/xe/xe_reg_whitelist.h index 3b64b42fe96e..c0248063d515 100644 --- a/drivers/gpu/drm/xe/xe_reg_whitelist.h +++ b/drivers/gpu/drm/xe/xe_reg_whitelist.h @@ -9,12 +9,20 @@ #include <linux/types.h> struct drm_printer; +struct xe_gt; struct xe_hw_engine; struct xe_reg_sr; struct xe_reg_sr_entry; +#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST) +extern const struct xe_rtp_table_sr register_whitelist; +#endif + void xe_reg_whitelist_process_engine(struct xe_hw_engine *hwe); +void xe_reg_whitelist_oa_regs(struct xe_gt *gt); +void xe_reg_dewhitelist_oa_regs(struct xe_gt *gt); + void xe_reg_whitelist_print_entry(struct drm_printer *p, unsigned int indent, u32 reg, struct xe_reg_sr_entry *entry); diff --git a/drivers/gpu/drm/xe/xe_rtp.c b/drivers/gpu/drm/xe/xe_rtp.c index dec9d94e6fb0..6a8d6ea68f25 100644 --- a/drivers/gpu/drm/xe/xe_rtp.c +++ b/drivers/gpu/drm/xe/xe_rtp.c @@ -227,17 +227,23 @@ static bool rule_matches(const struct xe_device *xe, static void rtp_add_sr_entry(const struct xe_rtp_action *action, struct xe_gt *gt, + struct xe_hw_engine *hwe, u32 mmio_base, struct xe_reg_sr *sr) { struct xe_reg_sr_entry sr_entry = { .reg = action->reg, .clr_bits = action->clr_bits, - .set_bits = action->set_bits, .read_mask = action->read_mask, }; + if (action->use_func) + sr_entry.set_bits = action->set_func(gt, hwe); + else + sr_entry.set_bits = action->set_bits; + sr_entry.reg.addr += mmio_base; + xe_reg_sr_add(sr, &sr_entry, gt); } @@ -259,7 +265,7 @@ static bool rtp_process_one_sr(const struct xe_rtp_entry_sr *entry, else mmio_base = 0; - rtp_add_sr_entry(action, gt, mmio_base, sr); + rtp_add_sr_entry(action, gt, hwe, mmio_base, sr); } return true; @@ -326,8 +332,7 @@ static void rtp_mark_active(struct xe_device *xe, * xe_rtp_process_to_sr - Process all rtp @entries, adding the matching ones to * the save-restore argument. * @ctx: The context for processing the table, with one of device, gt or hwe - * @entries: Table with RTP definitions - * @n_entries: Number of entries to process, usually ARRAY_SIZE(entries) + * @table: Table with RTP definitions * @sr: Save-restore struct where matching rules execute the action. This can be * viewed as the "coalesced view" of multiple the tables. The bits for each * register set are expected not to collide with previously added entries @@ -339,12 +344,10 @@ static void rtp_mark_active(struct xe_device *xe, * used to calculate the right register offset */ void xe_rtp_process_to_sr(struct xe_rtp_process_ctx *ctx, - const struct xe_rtp_entry_sr *entries, - size_t n_entries, + const struct xe_rtp_table_sr *table, struct xe_reg_sr *sr, bool process_in_vf) { - const struct xe_rtp_entry_sr *entry; struct xe_hw_engine *hwe = NULL; struct xe_gt *gt = NULL; struct xe_device *xe = NULL; @@ -354,9 +357,10 @@ void xe_rtp_process_to_sr(struct xe_rtp_process_ctx *ctx, if (!process_in_vf && IS_SRIOV_VF(xe)) return; - xe_assert(xe, entries); + xe_assert(xe, table->entries); - for (entry = entries; entry - entries < n_entries; entry++) { + for (size_t i = 0; i < table->n_entries; i++) { + const struct xe_rtp_entry_sr *entry = &table->entries[i]; bool match = false; if (entry->flags & XE_RTP_ENTRY_FLAG_FOREACH_ENGINE) { @@ -371,37 +375,40 @@ void xe_rtp_process_to_sr(struct xe_rtp_process_ctx *ctx, } if (match) - rtp_mark_active(xe, ctx, entry - entries); + rtp_mark_active(xe, ctx, i); } } EXPORT_SYMBOL_IF_KUNIT(xe_rtp_process_to_sr); /** - * xe_rtp_process - Process all rtp @entries, without running any action + * xe_rtp_process - Process all entries in rtp @table, without running any action * @ctx: The context for processing the table, with one of device, gt or hwe - * @entries: Table with RTP definitions + * @table: Table with RTP definitions * - * Walk the table pointed by @entries (with an empty sentinel), executing the + * Walk the table pointed by @table, executing the * rules. One difference from xe_rtp_process_to_sr(): there is no action * associated with each entry since this uses struct xe_rtp_entry. Its main use * is for marking active workarounds via * xe_rtp_process_ctx_enable_active_tracking(). */ void xe_rtp_process(struct xe_rtp_process_ctx *ctx, - const struct xe_rtp_entry *entries) + const struct xe_rtp_table *table) { - const struct xe_rtp_entry *entry; struct xe_hw_engine *hwe; struct xe_gt *gt; struct xe_device *xe; rtp_get_context(ctx, &hwe, >, &xe); - for (entry = entries; entry && entry->rules; entry++) { + xe_assert(xe, table->entries); + + for (size_t i = 0; i < table->n_entries; i++) { + const struct xe_rtp_entry *entry = &table->entries[i]; + if (!rule_matches(xe, gt, hwe, entry->rules, entry->n_rules)) continue; - rtp_mark_active(xe, ctx, entry - entries); + rtp_mark_active(xe, ctx, i); } } EXPORT_SYMBOL_IF_KUNIT(xe_rtp_process); diff --git a/drivers/gpu/drm/xe/xe_rtp.h b/drivers/gpu/drm/xe/xe_rtp.h index e4f1930ca1c3..0032f68ea187 100644 --- a/drivers/gpu/drm/xe/xe_rtp.h +++ b/drivers/gpu/drm/xe/xe_rtp.h @@ -323,6 +323,25 @@ struct xe_reg_sr; .read_mask = 0, ##__VA_ARGS__ } /** + * XE_RTP_ACTION_FIELD_SET_FUNC: Set a bit range to the value returned by a function + * @reg_: Register + * @mask_bits_: Mask of bits to be changed in the register, forming a field + * @func_: Function that returns value to set in the field denoted by @mask_bits_ + * @...: Additional fields to override in the struct xe_rtp_action entry + * + * This macro works like XE_RTP_ACTION_FIELD_SET(), except that the + * field value is evaluated at the time the RTP table is processed. + * + * @func_ will only be called a single time, when the RTP table is being + * processed. After processing, the value in the reg_sr entry is fixed and + * will not be re-evaluated. + */ +#define XE_RTP_ACTION_FIELD_SET_FUNC(reg_, mask_bits_, func_, ...) \ + { .reg = XE_RTP_DROP_CAST(reg_), \ + .clr_bits = mask_bits_, .set_func = func_, .use_func = 1, \ + .read_mask = mask_bits_, ##__VA_ARGS__ } + +/** * XE_RTP_ACTION_WHITELIST - Add register to userspace whitelist * @reg_: Register * @val_: Whitelist-specific flags to set @@ -461,6 +480,24 @@ struct xe_reg_sr; XE_RTP_PASTE_FOREACH(ACTION_, COMMA, (__VA_ARGS__)) \ } +/* + * Note: ARRAY_SIZE() cannot be used here because it expands through + * __must_be_array() -> __BUILD_BUG_ON_ZERO_MSG() -> _Static_assert inside + * sizeof(struct{}), which clang < 21 rejects when the compound literal + * contains non-compile-time-constant initializers. + */ +#define XE_RTP_TABLE_SR(...) { \ + .entries = (const struct xe_rtp_entry_sr[]){__VA_ARGS__}, \ + .n_entries = sizeof((const struct xe_rtp_entry_sr[]){__VA_ARGS__}) / \ + sizeof(struct xe_rtp_entry_sr), \ +} + +#define XE_RTP_TABLE(...) { \ + .entries = (const struct xe_rtp_entry[]){__VA_ARGS__}, \ + .n_entries = sizeof((const struct xe_rtp_entry[]){__VA_ARGS__}) / \ + sizeof(struct xe_rtp_entry), \ +} + #define XE_RTP_PROCESS_CTX_INITIALIZER(arg__) _Generic((arg__), \ struct xe_hw_engine * : (struct xe_rtp_process_ctx){ { (void *)(arg__) }, XE_RTP_PROCESS_TYPE_ENGINE }, \ struct xe_gt * : (struct xe_rtp_process_ctx){ { (void *)(arg__) }, XE_RTP_PROCESS_TYPE_GT }, \ @@ -471,12 +508,12 @@ void xe_rtp_process_ctx_enable_active_tracking(struct xe_rtp_process_ctx *ctx, size_t n_entries); void xe_rtp_process_to_sr(struct xe_rtp_process_ctx *ctx, - const struct xe_rtp_entry_sr *entries, - size_t n_entries, struct xe_reg_sr *sr, + const struct xe_rtp_table_sr *table, + struct xe_reg_sr *sr, bool process_in_vf); void xe_rtp_process(struct xe_rtp_process_ctx *ctx, - const struct xe_rtp_entry *entries); + const struct xe_rtp_table *table); /* Match functions to be used with XE_RTP_MATCH_FUNC */ diff --git a/drivers/gpu/drm/xe/xe_rtp_types.h b/drivers/gpu/drm/xe/xe_rtp_types.h index 0265c16d2762..b78092fa06e0 100644 --- a/drivers/gpu/drm/xe/xe_rtp_types.h +++ b/drivers/gpu/drm/xe/xe_rtp_types.h @@ -22,20 +22,37 @@ struct xe_gt; */ struct xe_rtp_action { /** @reg: Register */ - struct xe_reg reg; + struct xe_reg reg; + /** * @clr_bits: bits to clear when updating register. It's always a * superset of bits being modified */ - u32 clr_bits; - /** @set_bits: bits to set when updating register */ - u32 set_bits; + u32 clr_bits; + + union { + /** @set_bits: bits to set when updating register */ + u32 set_bits; + + /** @set_func: function to provide bits to set when updating register */ + u32 (*set_func)(struct xe_gt *gt, + struct xe_hw_engine *hwe); + }; + #define XE_RTP_NOCHECK .read_mask = 0 /** @read_mask: mask for bits to consider when reading value back */ - u32 read_mask; + u32 read_mask; + #define XE_RTP_ACTION_FLAG_ENGINE_BASE BIT(0) /** @flags: flags to apply on rule evaluation or action */ - u8 flags; + u8 flags; + + /** + * @use_func: + * Internal flag indicating @set_func should be called instead of + * using @set_bits. + */ + u8 use_func:1; }; enum { @@ -69,6 +86,7 @@ struct xe_rtp_rule { u8 platform; u8 subplatform; }; + /* * MATCH_GRAPHICS_VERSION / XE_RTP_MATCH_GRAPHICS_VERSION_RANGE / * MATCH_MEDIA_VERSION / XE_RTP_MATCH_MEDIA_VERSION_RANGE @@ -78,15 +96,18 @@ struct xe_rtp_rule { #define XE_RTP_END_VERSION_UNDEFINED U32_MAX u32 ver_end; }; + /* MATCH_STEP */ struct { u8 step_start; u8 step_end; }; + /* MATCH_ENGINE_CLASS / MATCH_NOT_ENGINE_CLASS */ struct { u8 engine_class; }; + /* MATCH_FUNC */ bool (*match_func)(const struct xe_device *xe, const struct xe_gt *gt, @@ -112,6 +133,16 @@ struct xe_rtp_entry { u8 n_rules; }; +struct xe_rtp_table_sr { + const struct xe_rtp_entry_sr *entries; + size_t n_entries; +}; + +struct xe_rtp_table { + const struct xe_rtp_entry *entries; + size_t n_entries; +}; + enum xe_rtp_process_type { XE_RTP_PROCESS_TYPE_DEVICE, XE_RTP_PROCESS_TYPE_GT, diff --git a/drivers/gpu/drm/xe/xe_step.c b/drivers/gpu/drm/xe/xe_step.c index fb9c31613ca7..55c1996f689e 100644 --- a/drivers/gpu/drm/xe/xe_step.c +++ b/drivers/gpu/drm/xe/xe_step.c @@ -110,12 +110,14 @@ __diag_pop(); /** * xe_step_platform_get - Determine platform-level stepping from PCI revid - * @xe: Xe device + * @platform: The Xe platform + * @revid: The PCI revid + * @step: Pointer to the step struct to update * * Convert the PCI revid into a platform-level stepping value and store that - * in the device info. + * in @step->platform. */ -void xe_step_platform_get(struct xe_device *xe) +void xe_step_platform_get(enum xe_platform platform, u8 revid, struct xe_step_info *step) { /* * Not all platforms map PCI revid directly into our symbolic stepping @@ -126,18 +128,21 @@ void xe_step_platform_get(struct xe_device *xe) * checks. */ - if (xe->info.platform == XE_NOVALAKE_P) - xe->info.step.platform = STEP_A0 + xe->info.revid; + if (platform == XE_NOVALAKE_P) + step->platform = STEP_A0 + revid; } /** * xe_step_pre_gmdid_get - Determine IP steppings from PCI revid * @xe: Xe device + * @step: Pointer to the step struct to update + * + * Convert the PCI revid into proper IP steppings and update @step->basedie, + * @step->graphics and @step->media accordingly. * - * Convert the PCI revid into proper IP steppings. This should only be - * used on platforms that do not have GMD_ID support. + * This should only be used on platforms that do not have GMD_ID support. */ -void xe_step_pre_gmdid_get(struct xe_device *xe) +void xe_step_pre_gmdid_get(struct xe_device *xe, struct xe_step_info *step) { const struct xe_step_info *revids = NULL; u16 revid = xe->info.revid; @@ -234,9 +239,9 @@ void xe_step_pre_gmdid_get(struct xe_device *xe) } done: - xe->info.step.graphics = graphics; - xe->info.step.media = media; - xe->info.step.basedie = basedie; + step->graphics = graphics; + step->media = media; + step->basedie = basedie; } /** @@ -244,8 +249,10 @@ done: * @xe: Xe device * @graphics_gmdid_revid: value of graphics GMD_ID register's revid field * @media_gmdid_revid: value of media GMD_ID register's revid field + * @step: Poninter to the step struct to update. * - * Convert the revid fields of the GMD_ID registers into proper IP steppings. + * Convert the revid fields of the GMD_ID registers into proper IP steppings + * and update @step->graphics and @step->media accordingly. * * GMD_ID revid values are currently expected to have consistent meanings on * all platforms: major steppings (A0, B0, etc.) are 4 apart, with minor @@ -253,7 +260,8 @@ done: */ void xe_step_gmdid_get(struct xe_device *xe, u32 graphics_gmdid_revid, - u32 media_gmdid_revid) + u32 media_gmdid_revid, + struct xe_step_info *step) { u8 graphics = STEP_A0 + graphics_gmdid_revid; u8 media = STEP_A0 + media_gmdid_revid; @@ -270,8 +278,8 @@ void xe_step_gmdid_get(struct xe_device *xe, media_gmdid_revid); } - xe->info.step.graphics = graphics; - xe->info.step.media = media; + step->graphics = graphics; + step->media = media; } #define STEP_NAME_CASE(name) \ diff --git a/drivers/gpu/drm/xe/xe_step.h b/drivers/gpu/drm/xe/xe_step.h index ea36b22cc297..5a5845335740 100644 --- a/drivers/gpu/drm/xe/xe_step.h +++ b/drivers/gpu/drm/xe/xe_step.h @@ -10,14 +10,16 @@ #include "xe_step_types.h" +enum xe_platform; struct xe_device; -void xe_step_platform_get(struct xe_device *xe); +void xe_step_platform_get(enum xe_platform platform, u8 revid, struct xe_step_info *step); -void xe_step_pre_gmdid_get(struct xe_device *xe); +void xe_step_pre_gmdid_get(struct xe_device *xe, struct xe_step_info *step); void xe_step_gmdid_get(struct xe_device *xe, u32 graphics_gmdid_revid, - u32 media_gmdid_revid); + u32 media_gmdid_revid, + struct xe_step_info *step); static inline u32 xe_step_to_gmdid(enum intel_step step) { return step - STEP_A0; } const char *xe_step_name(enum intel_step step); diff --git a/drivers/gpu/drm/xe/xe_svm.c b/drivers/gpu/drm/xe/xe_svm.c index dba73786d82a..e0f0c23d172d 100644 --- a/drivers/gpu/drm/xe/xe_svm.c +++ b/drivers/gpu/drm/xe/xe_svm.c @@ -1248,10 +1248,8 @@ retry: xe_svm_range_fault_count_stats_incr(gt, range); - if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) { - err = -EACCES; - goto out; - } + if (ctx.devmem_only && !range->base.pages.flags.migrate_devmem) + return -EACCES; if (xe_svm_range_is_valid(range, tile, ctx.devmem_only, dpagemap)) { xe_svm_range_valid_fault_count_stats_incr(gt, range); diff --git a/drivers/gpu/drm/xe/xe_svm.h b/drivers/gpu/drm/xe/xe_svm.h index b7b8eeacf196..3ca46a6f98c7 100644 --- a/drivers/gpu/drm/xe/xe_svm.h +++ b/drivers/gpu/drm/xe/xe_svm.h @@ -394,8 +394,19 @@ static inline struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_inst #define xe_svm_assert_in_notifier(vm__) \ lockdep_assert_held_write(&(vm__)->svm.gpusvm.notifier_lock) -#define xe_svm_assert_held_read(vm__) \ +/* + * Assert the svm notifier_lock is held. Read mode by default; write mode + * when CONFIG_DRM_XE_USERPTR_INVAL_INJECT is on, because that path forces + * a userptr invalidation that ends in drm_gpusvm_unmap_pages() with + * ctx->in_notifier=true, which requires the lock held for write. + */ +#if IS_ENABLED(CONFIG_DRM_XE_USERPTR_INVAL_INJECT) +#define xe_svm_assert_held_read_or_inject_write(vm__) \ + lockdep_assert_held_write(&(vm__)->svm.gpusvm.notifier_lock) +#else +#define xe_svm_assert_held_read_or_inject_write(vm__) \ lockdep_assert_held_read(&(vm__)->svm.gpusvm.notifier_lock) +#endif #define xe_svm_notifier_lock(vm__) \ drm_gpusvm_notifier_lock(&(vm__)->svm.gpusvm) @@ -409,7 +420,7 @@ static inline struct drm_pagemap *xe_drm_pagemap_from_fd(int fd, u32 region_inst #else #define xe_svm_assert_in_notifier(...) do {} while (0) -static inline void xe_svm_assert_held_read(struct xe_vm *vm) +static inline void xe_svm_assert_held_read_or_inject_write(struct xe_vm *vm) { } diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c index 3caa9f15875f..e13eebaac1d0 100644 --- a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c +++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.c @@ -294,6 +294,34 @@ static int sysctrl_send_command(struct xe_sysctrl *sc, } /** + * xe_sysctrl_create_command() - Create system controller command + * @command: Sysctrl command structure + * @group_id: Command group ID + * @cmd_id: Command ID + * @request: Pointer to request buffer (can be NULL) + * @request_len: Size of request buffer + * @response: Pointer to response buffer + * @response_len: Size of response buffer + * + * Helper function to create sysctrl command to be sent via %xe_sysctrl_send_command() + */ +void xe_sysctrl_create_command(struct xe_sysctrl_mailbox_command *command, u8 group_id, u8 cmd_id, + void *request, size_t request_len, void *response, + size_t response_len) +{ + struct xe_sysctrl_app_msg_hdr header = {0}; + + header.data = FIELD_PREP(APP_HDR_GROUP_ID_MASK, group_id) | + FIELD_PREP(APP_HDR_COMMAND_MASK, cmd_id); + + command->header = header; + command->data_in = request; + command->data_in_len = request_len; + command->data_out = response; + command->data_out_len = response_len; +} + +/** * xe_sysctrl_mailbox_init - Initialize System Controller mailbox interface * @sc: System controller structure * diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h index f67e9234de48..fb434cc165b2 100644 --- a/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h +++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox.h @@ -23,6 +23,9 @@ struct xe_sysctrl_mailbox_command; #define XE_SYSCTRL_APP_HDR_VERSION(hdr) \ FIELD_GET(APP_HDR_VERSION_MASK, (hdr)->data) +void xe_sysctrl_create_command(struct xe_sysctrl_mailbox_command *command, u8 group_id, u8 cmd_id, + void *request, size_t request_len, void *response, + size_t response_len); void xe_sysctrl_mailbox_init(struct xe_sysctrl *sc); int xe_sysctrl_send_command(struct xe_sysctrl *sc, struct xe_sysctrl_mailbox_command *cmd, diff --git a/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h b/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h index 84d7c647e743..6e3753554510 100644 --- a/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h +++ b/drivers/gpu/drm/xe/xe_sysctrl_mailbox_types.h @@ -22,9 +22,13 @@ enum xe_sysctrl_group { /** * enum xe_sysctrl_gfsp_cmd - Commands supported by GFSP group * + * @XE_SYSCTRL_CMD_GET_COUNTER: Get error counter value + * @XE_SYSCTRL_CMD_CLEAR_COUNTER: Clear error counter value * @XE_SYSCTRL_CMD_GET_PENDING_EVENT: Retrieve pending event */ enum xe_sysctrl_gfsp_cmd { + XE_SYSCTRL_CMD_GET_COUNTER = 0x03, + XE_SYSCTRL_CMD_CLEAR_COUNTER = 0x04, XE_SYSCTRL_CMD_GET_PENDING_EVENT = 0x07, }; diff --git a/drivers/gpu/drm/xe/xe_tile.c b/drivers/gpu/drm/xe/xe_tile.c index c465aae7883c..74d925a337b7 100644 --- a/drivers/gpu/drm/xe/xe_tile.c +++ b/drivers/gpu/drm/xe/xe_tile.c @@ -157,7 +157,9 @@ int xe_tile_init_early(struct xe_tile *tile, struct xe_device *xe, u8 id) if (err) return err; - xe_pcode_init(tile); + err = xe_pcode_init_early(tile); + if (err) + return err; return 0; } diff --git a/drivers/gpu/drm/xe/xe_tuning.c b/drivers/gpu/drm/xe/xe_tuning.c index 9a1b3862e192..bcec40ca2d35 100644 --- a/drivers/gpu/drm/xe/xe_tuning.c +++ b/drivers/gpu/drm/xe/xe_tuning.c @@ -20,7 +20,7 @@ #undef XE_REG_MCR #define XE_REG_MCR(...) XE_REG(__VA_ARGS__, .mcr = 1) -static const struct xe_rtp_entry_sr gt_tunings[] = { +VISIBLE_IF_KUNIT const struct xe_rtp_table_sr gt_tunings = XE_RTP_TABLE_SR( { XE_RTP_NAME("Tuning: Blend Fill Caching Optimization Disable"), XE_RTP_RULES(PLATFORM(DG2)), XE_RTP_ACTIONS(SET(XEHP_L3SCQREG7, BLEND_FILL_CACHING_OPT_DIS)) @@ -100,9 +100,10 @@ static const struct xe_rtp_entry_sr gt_tunings[] = { XE_RTP_ACTIONS(FIELD_SET(GAMSTLB_CTRL, BANK_HASH_MODE, BANK_HASH_4KB_MODE)) }, -}; +); +EXPORT_SYMBOL_IF_KUNIT(gt_tunings); -static const struct xe_rtp_entry_sr engine_tunings[] = { +static const struct xe_rtp_table_sr engine_tunings = XE_RTP_TABLE_SR( { XE_RTP_NAME("Tuning: L3 Hashing Mask"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1210), FUNC(xe_rtp_match_first_render_or_compute)), @@ -129,9 +130,9 @@ static const struct xe_rtp_entry_sr engine_tunings[] = { FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(TDL_TSL_CHICKEN2, TILEY_LOCALID)) }, -}; +); -static const struct xe_rtp_entry_sr lrc_tunings[] = { +static const struct xe_rtp_table_sr lrc_tunings = XE_RTP_TABLE_SR( { XE_RTP_NAME("Tuning: Windower HW Filtering"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(3000, 3599), ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(XEHP_COMMON_SLICE_CHICKEN4, HW_FILTERING)) @@ -171,7 +172,7 @@ static const struct xe_rtp_entry_sr lrc_tunings[] = { XE_RTP_ACTIONS(FIELD_SET(FF_MODE, VS_HIT_MAX_VALUE_MASK, REG_FIELD_PREP(VS_HIT_MAX_VALUE_MASK, 0x3f))) }, -}; +); /** * xe_tuning_init - initialize gt with tunings bookkeeping @@ -185,9 +186,9 @@ int xe_tuning_init(struct xe_gt *gt) size_t n_lrc, n_engine, n_gt, total; unsigned long *p; - n_gt = BITS_TO_LONGS(ARRAY_SIZE(gt_tunings)); - n_engine = BITS_TO_LONGS(ARRAY_SIZE(engine_tunings)); - n_lrc = BITS_TO_LONGS(ARRAY_SIZE(lrc_tunings)); + n_gt = BITS_TO_LONGS(gt_tunings.n_entries); + n_engine = BITS_TO_LONGS(engine_tunings.n_entries); + n_lrc = BITS_TO_LONGS(lrc_tunings.n_entries); total = n_gt + n_engine + n_lrc; p = drmm_kzalloc(&xe->drm, sizeof(*p) * total, GFP_KERNEL); @@ -210,9 +211,8 @@ void xe_tuning_process_gt(struct xe_gt *gt) xe_rtp_process_ctx_enable_active_tracking(&ctx, gt->tuning_active.gt, - ARRAY_SIZE(gt_tunings)); - xe_rtp_process_to_sr(&ctx, gt_tunings, ARRAY_SIZE(gt_tunings), - >->reg_sr, false); + gt_tunings.n_entries); + xe_rtp_process_to_sr(&ctx, >_tunings, >->reg_sr, false); } EXPORT_SYMBOL_IF_KUNIT(xe_tuning_process_gt); @@ -222,9 +222,8 @@ void xe_tuning_process_engine(struct xe_hw_engine *hwe) xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->tuning_active.engine, - ARRAY_SIZE(engine_tunings)); - xe_rtp_process_to_sr(&ctx, engine_tunings, ARRAY_SIZE(engine_tunings), - &hwe->reg_sr, false); + engine_tunings.n_entries); + xe_rtp_process_to_sr(&ctx, &engine_tunings, &hwe->reg_sr, false); } EXPORT_SYMBOL_IF_KUNIT(xe_tuning_process_engine); @@ -242,9 +241,8 @@ void xe_tuning_process_lrc(struct xe_hw_engine *hwe) xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->tuning_active.lrc, - ARRAY_SIZE(lrc_tunings)); - xe_rtp_process_to_sr(&ctx, lrc_tunings, ARRAY_SIZE(lrc_tunings), - &hwe->reg_lrc, true); + lrc_tunings.n_entries); + xe_rtp_process_to_sr(&ctx, &lrc_tunings, &hwe->reg_lrc, true); } /** @@ -259,18 +257,18 @@ int xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p) size_t idx; drm_printf(p, "GT Tunings\n"); - for_each_set_bit(idx, gt->tuning_active.gt, ARRAY_SIZE(gt_tunings)) - drm_printf_indent(p, 1, "%s\n", gt_tunings[idx].name); + for_each_set_bit(idx, gt->tuning_active.gt, gt_tunings.n_entries) + drm_printf_indent(p, 1, "%s\n", gt_tunings.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "Engine Tunings\n"); - for_each_set_bit(idx, gt->tuning_active.engine, ARRAY_SIZE(engine_tunings)) - drm_printf_indent(p, 1, "%s\n", engine_tunings[idx].name); + for_each_set_bit(idx, gt->tuning_active.engine, engine_tunings.n_entries) + drm_printf_indent(p, 1, "%s\n", engine_tunings.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "LRC Tunings\n"); - for_each_set_bit(idx, gt->tuning_active.lrc, ARRAY_SIZE(lrc_tunings)) - drm_printf_indent(p, 1, "%s\n", lrc_tunings[idx].name); + for_each_set_bit(idx, gt->tuning_active.lrc, lrc_tunings.n_entries) + drm_printf_indent(p, 1, "%s\n", lrc_tunings.entries[idx].name); return 0; } diff --git a/drivers/gpu/drm/xe/xe_tuning.h b/drivers/gpu/drm/xe/xe_tuning.h index d18e187debf6..869564e3e992 100644 --- a/drivers/gpu/drm/xe/xe_tuning.h +++ b/drivers/gpu/drm/xe/xe_tuning.h @@ -6,6 +6,8 @@ #ifndef _XE_TUNING_H_ #define _XE_TUNING_H_ +#include <kunit/visibility.h> + struct drm_printer; struct xe_gt; struct xe_hw_engine; @@ -16,4 +18,8 @@ void xe_tuning_process_engine(struct xe_hw_engine *hwe); void xe_tuning_process_lrc(struct xe_hw_engine *hwe); int xe_tuning_dump(struct xe_gt *gt, struct drm_printer *p); +#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST) +extern const struct xe_rtp_table_sr gt_tunings; +#endif + #endif diff --git a/drivers/gpu/drm/xe/xe_userptr.c b/drivers/gpu/drm/xe/xe_userptr.c index 2e45e42c648f..1d2ab678faf5 100644 --- a/drivers/gpu/drm/xe/xe_userptr.c +++ b/drivers/gpu/drm/xe/xe_userptr.c @@ -269,7 +269,7 @@ static const struct mmu_interval_notifier_ops vma_userptr_notifier_ops = { */ void xe_vma_userptr_force_invalidate(struct xe_userptr_vma *uvma) { - static struct mmu_interval_notifier_finish *finish; + struct mmu_interval_notifier_finish *finish; struct xe_vm *vm = xe_vma_vm(&uvma->vma); /* Protect against concurrent userptr pinning */ diff --git a/drivers/gpu/drm/xe/xe_vm_madvise.c b/drivers/gpu/drm/xe/xe_vm_madvise.c index c4fb29004195..9e343f9aa44d 100644 --- a/drivers/gpu/drm/xe/xe_vm_madvise.c +++ b/drivers/gpu/drm/xe/xe_vm_madvise.c @@ -332,6 +332,20 @@ static int xe_vm_invalidate_madvise_range(struct xe_vm *vm, u64 start, u64 end) return err; } +/** + * madvise_range_needs_invalidation() - Check whether madvise needs invalidation + * @args: madvise ioctl arguments + * + * Purgeable state updates only touch VMA/BO metadata. PTEs stay valid and are + * zapped only if the BO is later purged. + * + * Return: true when the update needs PTE invalidation. + */ +static bool madvise_range_needs_invalidation(const struct drm_xe_madvise *args) +{ + return args->type != DRM_XE_VMA_ATTR_PURGEABLE_STATE; +} + static bool madvise_args_are_sane(struct xe_device *xe, const struct drm_xe_madvise *args) { if (XE_IOCTL_DBG(xe, !args)) @@ -708,8 +722,9 @@ int xe_vm_madvise_ioctl(struct drm_device *dev, void *data, struct drm_file *fil madvise_funcs[attr_type](xe, vm, madvise_range.vmas, madvise_range.num_vmas, args, &details); - err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, - madvise_range.addr + args->range); + if (madvise_range_needs_invalidation(args)) + err = xe_vm_invalidate_madvise_range(vm, madvise_range.addr, + madvise_range.addr + args->range); if (madvise_range.has_svm_userptr_vmas) xe_svm_notifier_unlock(vm); diff --git a/drivers/gpu/drm/xe/xe_wa.c b/drivers/gpu/drm/xe/xe_wa.c index cb811f8a7781..139434946f8f 100644 --- a/drivers/gpu/drm/xe/xe_wa.c +++ b/drivers/gpu/drm/xe/xe_wa.c @@ -130,7 +130,7 @@ __diag_push(); __diag_ignore_all("-Woverride-init", "Allow field overrides in table"); -static const struct xe_rtp_entry_sr gt_was[] = { +VISIBLE_IF_KUNIT const struct xe_rtp_table_sr gt_was = XE_RTP_TABLE_SR( /* Workarounds applying over a range of IPs */ { XE_RTP_NAME("14011060649"), @@ -293,7 +293,7 @@ static const struct xe_rtp_entry_sr gt_was[] = { XE_RTP_ACTIONS(SET(MMIOATSREQLIMIT_GAM_WALK_3D, DIS_ATS_WRONLY_PG)) }, - { XE_RTP_NAME("14026144927, 16029437861"), + { XE_RTP_NAME("14026144927, 16029437861, 14026127056"), XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0)), XE_RTP_ACTIONS(SET(L3SQCREG2, L3_SQ_DISABLE_COAMA_2WAY_COH | L3_SQ_DISABLE_COAMA)) @@ -306,9 +306,10 @@ static const struct xe_rtp_entry_sr gt_was[] = { XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0)), XE_RTP_ACTIONS(SET(GUC_INTR_CHICKEN, DISABLE_SIGNALING_ENGINES)) }, -}; +); +EXPORT_SYMBOL_IF_KUNIT(gt_was); -static const struct xe_rtp_entry_sr engine_was[] = { +static const struct xe_rtp_table_sr engine_was = XE_RTP_TABLE_SR( /* Workarounds applying over a range of IPs */ { XE_RTP_NAME("22010931296, 18011464164, 14010919138"), @@ -586,12 +587,12 @@ static const struct xe_rtp_entry_sr engine_was[] = { /* Xe3p_LPG*/ - { XE_RTP_NAME("22021149932"), + { XE_RTP_NAME("22021149932, 14026290593"), XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0), FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, SAMPLER_LD_LSC_DISABLE)) }, - { XE_RTP_NAME("14025676848"), + { XE_RTP_NAME("14025676848, 14026270459"), XE_RTP_RULES(GRAPHICS_VERSION(3510), GRAPHICS_STEP(A0, B0), FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(LSC_CHICKEN_BIT_0_UDW, LSCFE_SAME_ADDRESS_ATOMICS_COALESCING_DISABLE)) @@ -614,9 +615,9 @@ static const struct xe_rtp_entry_sr engine_was[] = { FUNC(xe_rtp_match_first_render_or_compute)), XE_RTP_ACTIONS(SET(TDL_CHICKEN, BIT_APQ_OPT_DIS)) }, -}; +); -static const struct xe_rtp_entry_sr lrc_was[] = { +static const struct xe_rtp_table_sr lrc_was = XE_RTP_TABLE_SR( { XE_RTP_NAME("16011163337"), XE_RTP_RULES(GRAPHICS_VERSION_RANGE(1200, 1210), ENGINE_CLASS(RENDER)), /* read verification is ignored due to 1608008084. */ @@ -794,21 +795,31 @@ static const struct xe_rtp_entry_sr lrc_was[] = { ENGINE_CLASS(RENDER)), XE_RTP_ACTIONS(SET(CHICKEN_RASTER_1, DIS_CLIP_NEGATIVE_BOUNDING_BOX)) }, -}; +); -static __maybe_unused const struct xe_rtp_entry oob_was[] = { +static const struct xe_rtp_entry oob_was_entries[] = { #include <generated/xe_wa_oob.c> - {} }; -static_assert(ARRAY_SIZE(oob_was) - 1 == _XE_WA_OOB_COUNT); +static_assert(ARRAY_SIZE(oob_was_entries) == _XE_WA_OOB_COUNT); -static __maybe_unused const struct xe_rtp_entry device_oob_was[] = { +VISIBLE_IF_KUNIT __maybe_unused const struct xe_rtp_table oob_was = { + .entries = oob_was_entries, + .n_entries = ARRAY_SIZE(oob_was_entries), +}; +EXPORT_SYMBOL_IF_KUNIT(oob_was); + +static const struct xe_rtp_entry device_oob_was_entries[] = { #include <generated/xe_device_wa_oob.c> - {} }; -static_assert(ARRAY_SIZE(device_oob_was) - 1 == _XE_DEVICE_WA_OOB_COUNT); +static_assert(ARRAY_SIZE(device_oob_was_entries) == _XE_DEVICE_WA_OOB_COUNT); + +VISIBLE_IF_KUNIT __maybe_unused const struct xe_rtp_table device_oob_was = { + .entries = device_oob_was_entries, + .n_entries = ARRAY_SIZE(device_oob_was_entries), +}; +EXPORT_SYMBOL_IF_KUNIT(device_oob_was); __diag_pop(); @@ -824,10 +835,10 @@ void xe_wa_process_device_oob(struct xe_device *xe) { struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(xe); - xe_rtp_process_ctx_enable_active_tracking(&ctx, xe->wa_active.oob, ARRAY_SIZE(device_oob_was)); + xe_rtp_process_ctx_enable_active_tracking(&ctx, xe->wa_active.oob, device_oob_was.n_entries); xe->wa_active.oob_initialized = true; - xe_rtp_process(&ctx, device_oob_was); + xe_rtp_process(&ctx, &device_oob_was); } /** @@ -842,9 +853,9 @@ void xe_wa_process_gt_oob(struct xe_gt *gt) struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(gt); xe_rtp_process_ctx_enable_active_tracking(&ctx, gt->wa_active.oob, - ARRAY_SIZE(oob_was)); + oob_was.n_entries); gt->wa_active.oob_initialized = true; - xe_rtp_process(&ctx, oob_was); + xe_rtp_process(&ctx, &oob_was); } /** @@ -859,9 +870,8 @@ void xe_wa_process_gt(struct xe_gt *gt) struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(gt); xe_rtp_process_ctx_enable_active_tracking(&ctx, gt->wa_active.gt, - ARRAY_SIZE(gt_was)); - xe_rtp_process_to_sr(&ctx, gt_was, ARRAY_SIZE(gt_was), - >->reg_sr, false); + gt_was.n_entries); + xe_rtp_process_to_sr(&ctx, >_was, >->reg_sr, false); } EXPORT_SYMBOL_IF_KUNIT(xe_wa_process_gt); @@ -878,9 +888,8 @@ void xe_wa_process_engine(struct xe_hw_engine *hwe) struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->wa_active.engine, - ARRAY_SIZE(engine_was)); - xe_rtp_process_to_sr(&ctx, engine_was, ARRAY_SIZE(engine_was), - &hwe->reg_sr, false); + engine_was.n_entries); + xe_rtp_process_to_sr(&ctx, &engine_was, &hwe->reg_sr, false); } /** @@ -896,9 +905,8 @@ void xe_wa_process_lrc(struct xe_hw_engine *hwe) struct xe_rtp_process_ctx ctx = XE_RTP_PROCESS_CTX_INITIALIZER(hwe); xe_rtp_process_ctx_enable_active_tracking(&ctx, hwe->gt->wa_active.lrc, - ARRAY_SIZE(lrc_was)); - xe_rtp_process_to_sr(&ctx, lrc_was, ARRAY_SIZE(lrc_was), - &hwe->reg_lrc, true); + lrc_was.n_entries); + xe_rtp_process_to_sr(&ctx, &lrc_was, &hwe->reg_lrc, true); } /** @@ -912,7 +920,7 @@ int xe_wa_device_init(struct xe_device *xe) unsigned long *p; p = drmm_kzalloc(&xe->drm, - sizeof(*p) * BITS_TO_LONGS(ARRAY_SIZE(device_oob_was)), + sizeof(*p) * BITS_TO_LONGS(device_oob_was.n_entries), GFP_KERNEL); if (!p) @@ -935,10 +943,10 @@ int xe_wa_gt_init(struct xe_gt *gt) size_t n_oob, n_lrc, n_engine, n_gt, total; unsigned long *p; - n_gt = BITS_TO_LONGS(ARRAY_SIZE(gt_was)); - n_engine = BITS_TO_LONGS(ARRAY_SIZE(engine_was)); - n_lrc = BITS_TO_LONGS(ARRAY_SIZE(lrc_was)); - n_oob = BITS_TO_LONGS(ARRAY_SIZE(oob_was)); + n_gt = BITS_TO_LONGS(gt_was.n_entries); + n_engine = BITS_TO_LONGS(engine_was.n_entries); + n_lrc = BITS_TO_LONGS(lrc_was.n_entries); + n_oob = BITS_TO_LONGS(oob_was.n_entries); total = n_gt + n_engine + n_lrc + n_oob; p = drmm_kzalloc(&xe->drm, sizeof(*p) * total, GFP_KERNEL); @@ -962,9 +970,9 @@ void xe_wa_device_dump(struct xe_device *xe, struct drm_printer *p) size_t idx; drm_printf(p, "Device OOB Workarounds\n"); - for_each_set_bit(idx, xe->wa_active.oob, ARRAY_SIZE(device_oob_was)) - if (device_oob_was[idx].name) - drm_printf_indent(p, 1, "%s\n", device_oob_was[idx].name); + for_each_set_bit(idx, xe->wa_active.oob, device_oob_was.n_entries) + if (device_oob_was.entries[idx].name) + drm_printf_indent(p, 1, "%s\n", device_oob_was.entries[idx].name); } /** @@ -979,24 +987,24 @@ int xe_wa_gt_dump(struct xe_gt *gt, struct drm_printer *p) size_t idx; drm_printf(p, "GT Workarounds\n"); - for_each_set_bit(idx, gt->wa_active.gt, ARRAY_SIZE(gt_was)) - drm_printf_indent(p, 1, "%s\n", gt_was[idx].name); + for_each_set_bit(idx, gt->wa_active.gt, gt_was.n_entries) + drm_printf_indent(p, 1, "%s\n", gt_was.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "Engine Workarounds\n"); - for_each_set_bit(idx, gt->wa_active.engine, ARRAY_SIZE(engine_was)) - drm_printf_indent(p, 1, "%s\n", engine_was[idx].name); + for_each_set_bit(idx, gt->wa_active.engine, engine_was.n_entries) + drm_printf_indent(p, 1, "%s\n", engine_was.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "LRC Workarounds\n"); - for_each_set_bit(idx, gt->wa_active.lrc, ARRAY_SIZE(lrc_was)) - drm_printf_indent(p, 1, "%s\n", lrc_was[idx].name); + for_each_set_bit(idx, gt->wa_active.lrc, lrc_was.n_entries) + drm_printf_indent(p, 1, "%s\n", lrc_was.entries[idx].name); drm_puts(p, "\n"); drm_printf(p, "OOB Workarounds\n"); - for_each_set_bit(idx, gt->wa_active.oob, ARRAY_SIZE(oob_was)) - if (oob_was[idx].name) - drm_printf_indent(p, 1, "%s\n", oob_was[idx].name); + for_each_set_bit(idx, gt->wa_active.oob, oob_was.n_entries) + if (oob_was.entries[idx].name) + drm_printf_indent(p, 1, "%s\n", oob_was.entries[idx].name); return 0; } diff --git a/drivers/gpu/drm/xe/xe_wa.h b/drivers/gpu/drm/xe/xe_wa.h index a5f7d33c1b32..f4da2b271396 100644 --- a/drivers/gpu/drm/xe/xe_wa.h +++ b/drivers/gpu/drm/xe/xe_wa.h @@ -6,6 +6,7 @@ #ifndef _XE_WA_H_ #define _XE_WA_H_ +#include <kunit/visibility.h> #include "xe_assert.h" struct drm_printer; @@ -24,6 +25,12 @@ void xe_wa_apply_tile_workarounds(struct xe_tile *tile); void xe_wa_device_dump(struct xe_device *xe, struct drm_printer *p); int xe_wa_gt_dump(struct xe_gt *gt, struct drm_printer *p); +#if IS_ENABLED(CONFIG_DRM_XE_KUNIT_TEST) +extern const struct xe_rtp_table_sr gt_was; +extern __maybe_unused const struct xe_rtp_table oob_was; +extern __maybe_unused const struct xe_rtp_table device_oob_was; +#endif + /** * XE_GT_WA - Out-of-band GT workarounds, to be queried and called as needed. * @gt__: gt instance diff --git a/drivers/gpu/drm/xe/xe_wa_oob.rules b/drivers/gpu/drm/xe/xe_wa_oob.rules index f8a185103b80..9027365f0043 100644 --- a/drivers/gpu/drm/xe/xe_wa_oob.rules +++ b/drivers/gpu/drm/xe/xe_wa_oob.rules @@ -65,3 +65,4 @@ 14025883347 MEDIA_VERSION_RANGE(1301, 3503) GRAPHICS_VERSION_RANGE(2004, 3005) +16029380221 MEDIA_VERSION(3500) |
